From 1fbbc82f3a4a06d394f5063b4ae5036324283fc0 Mon Sep 17 00:00:00 2001 From: Elizabeth Figura Date: Wed, 13 Nov 2024 19:16:39 -0600 Subject: [PATCH] vkd3d-shader/ir: Allow controlling the fog source through a parameter. --- include/vkd3d_shader.h | 62 ++++++++ libs/vkd3d-shader/d3dbc.c | 2 + libs/vkd3d-shader/ir.c | 175 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + tests/hlsl/fog.shader_test | 63 ++++++++ tests/shader_runner.c | 13 ++ tests/shader_runner.h | 2 + tests/shader_runner_d3d9.c | 11 ++ tests/shader_runner_vulkan.c | 23 ++- 9 files changed, 351 insertions(+), 1 deletion(-) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index f71f5aec..300792ae 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -486,6 +486,7 @@ enum vkd3d_shader_binding_flag * * c = The fog coordinate value output from the vertex shader. This is an * inter-stage varying with the semantic name "FOG" and semantic index 0. + * It may be modified by VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE. * E = The value of VKD3D_SHADER_PARAMETER_NAME_FOG_END. * k = The value of VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE. * @@ -515,6 +516,43 @@ enum vkd3d_shader_fog_fragment_mode VKD3D_SHADER_FOG_FRAGMENT_LINEAR = 0x3, }; +/** + * The source of the fog varying output by a pre-rasterization shader. + * The fog varying is defined as the output varying with the semantic name "FOG" + * and semantic index 0. + * + * See VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE for further documentation of this + * parameter. + * + * \since 1.15 + */ +enum vkd3d_shader_fog_source +{ + /** + * The source shader is not modified. That is, the fog varying in the target + * shader is the original fog varying if and only if present. + */ + VKD3D_SHADER_FOG_SOURCE_FOG = 0x0, + /** + * If the source shader has a fog varying, it is not modified. + * Otherwise, if the source shader outputs a varying with semantic name + * "COLOR" and semantic index 1 whose index includes a W component, + * said W component is output as fog varying. + * Otherwise, no fog varying is output. + */ + VKD3D_SHADER_FOG_SOURCE_FOG_OR_SPECULAR_W = 0x1, + /** + * The fog source is the Z component of the position output by the vertex + * shader. + */ + VKD3D_SHADER_FOG_SOURCE_Z = 0x2, + /** + * The fog source is the W component of the position output by the vertex + * shader. + */ + VKD3D_SHADER_FOG_SOURCE_W = 0x3, +}; + /** * The manner in which a parameter value is provided to the shader, used in * struct vkd3d_shader_parameter and struct vkd3d_shader_parameter1. @@ -845,6 +883,30 @@ enum vkd3d_shader_parameter_name * \since 1.15 */ VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, + /** + * Fog source. The value specified by this parameter must be a member of + * enum vkd3d_shader_fog_source. + * + * This parameter replaces or suppletes the fog varying output by a + * pre-rasterization shader. The fog varying is defined as the output + * varying with the semantic name "FOG" and semantic index 0. + * + * Together with other fog parameters, this parameter can be used to + * implement fixed function fog, as present in Direct3D versions up to 9, + * if the target environment does not support fog as part of its own + * fixed-function API (as Vulkan and core OpenGL). + * + * The default value is VKD3D_SHADER_FOG_SOURCE_FOG. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. + * + * Only VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT is supported in this + * version of vkd3d-shader. + * + * \since 1.15 + */ + VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_NAME), }; diff --git a/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d-shader/d3dbc.c index bda9bc72..7db658fb 100644 --- a/libs/vkd3d-shader/d3dbc.c +++ b/libs/vkd3d-shader/d3dbc.c @@ -968,6 +968,8 @@ static void shader_sm1_read_dst_param(struct vkd3d_shader_sm1_parser *sm1, const if (dst_param->reg.type == VKD3DSPR_RASTOUT && dst_param->reg.idx[0].offset == VSIR_RASTOUT_POINT_SIZE) sm1->p.program->has_point_size = true; + if (dst_param->reg.type == VKD3DSPR_RASTOUT && dst_param->reg.idx[0].offset == VSIR_RASTOUT_FOG) + sm1->p.program->has_fog = true; } static void shader_sm1_read_semantic(struct vkd3d_shader_sm1_parser *sm1, diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 46a0a356..0bbefa5a 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -6907,6 +6907,177 @@ static enum vkd3d_result vsir_program_insert_fragment_fog(struct vsir_program *p return VKD3D_OK; } +static enum vkd3d_result vsir_program_add_fog_output(struct vsir_program *program, + struct vsir_transformation_context *ctx) +{ + struct shader_signature *signature = &program->output_signature; + const struct vkd3d_shader_parameter1 *source_parameter; + uint32_t register_idx = 0; + + if (!is_pre_rasterization_shader(program->shader_version.type)) + return VKD3D_OK; + + if (!(source_parameter = vsir_program_get_parameter(program, VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE))) + return VKD3D_OK; + + if (source_parameter->type == VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) + { + enum vkd3d_shader_fog_source source = source_parameter->u.immediate_constant.u.u32; + + if (source == VKD3D_SHADER_FOG_SOURCE_FOG) + return VKD3D_OK; + + if (source == VKD3D_SHADER_FOG_SOURCE_FOG_OR_SPECULAR_W + && !vsir_signature_find_element_by_name(signature, "COLOR", 1)) + return VKD3D_OK; + } + + if (vsir_signature_find_element_by_name(signature, "FOG", 0)) + return VKD3D_OK; + + for (unsigned int i = 0; i < signature->element_count; ++i) + register_idx = max(register_idx, signature->elements[i].register_index + 1); + + if (!add_signature_element(signature, "FOG", 0, VKD3DSP_WRITEMASK_0, register_idx, VKD3DSIM_LINEAR)) + return VKD3D_ERROR_OUT_OF_MEMORY; + return VKD3D_OK; +} + +static enum vkd3d_result insert_vertex_fog_before_ret(struct vsir_program *program, + const struct vkd3d_shader_instruction *ret, enum vkd3d_shader_fog_source source, uint32_t temp, + uint32_t fog_signature_idx, uint32_t source_signature_idx, size_t *ret_pos) +{ + const struct signature_element *e = &program->output_signature.elements[source_signature_idx]; + struct vkd3d_shader_instruction_array *instructions = &program->instructions; + size_t pos = ret - instructions->elements; + struct vkd3d_shader_instruction *ins; + + if (!shader_instruction_array_insert_at(&program->instructions, pos, 2)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &program->instructions.elements[pos]; + + /* Write the fog output. */ + vsir_instruction_init_with_params(program, ins, &ret->location, VKD3DSIH_MOV, 1, 1); + dst_param_init_output(&ins->dst[0], VKD3D_DATA_FLOAT, fog_signature_idx, 0x1); + src_param_init_temp_float4(&ins->src[0], temp); + if (source == VKD3D_SHADER_FOG_SOURCE_Z) + ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(Z, Z, Z, Z); + else /* Position or specular W. */ + ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W); + ++ins; + + /* Write the position or specular output. */ + vsir_instruction_init_with_params(program, ins, &ret->location, VKD3DSIH_MOV, 1, 1); + dst_param_init_output(&ins->dst[0], vkd3d_data_type_from_component_type(e->component_type), + source_signature_idx, e->mask); + src_param_init_temp_float4(&ins->src[0], temp); + ++ins; + + *ret_pos = pos + 2; + return VKD3D_OK; +} + +static enum vkd3d_result vsir_program_insert_vertex_fog(struct vsir_program *program, + struct vsir_transformation_context *ctx) +{ + struct vkd3d_shader_message_context *message_context = ctx->message_context; + const struct vkd3d_shader_parameter1 *source_parameter = NULL; + uint32_t fog_signature_idx, source_signature_idx, temp; + static const struct vkd3d_shader_location no_loc; + enum vkd3d_shader_fog_source source; + const struct signature_element *e; + + if (!is_pre_rasterization_shader(program->shader_version.type)) + return VKD3D_OK; + + if (!(source_parameter = vsir_program_get_parameter(program, VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE))) + return VKD3D_OK; + + if (source_parameter->type != VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Unsupported fog source parameter type %#x.", source_parameter->type); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + if (source_parameter->data_type != VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_INVALID_DATA_TYPE, + "Invalid fog source parameter data type %#x.", source_parameter->data_type); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + source = source_parameter->u.immediate_constant.u.u32; + + TRACE("Fog source %#x.\n", source); + + if (source == VKD3D_SHADER_FOG_SOURCE_FOG) + return VKD3D_OK; + + if (source == VKD3D_SHADER_FOG_SOURCE_FOG_OR_SPECULAR_W) + { + if (program->has_fog || !(e = vsir_signature_find_element_by_name(&program->output_signature, "COLOR", 1))) + return VKD3D_OK; + source_signature_idx = e - program->output_signature.elements; + } + else + { + if (!vsir_signature_find_sysval(&program->output_signature, + VKD3D_SHADER_SV_POSITION, 0, &source_signature_idx)) + { + vkd3d_shader_error(ctx->message_context, &no_loc, + VKD3D_SHADER_ERROR_VSIR_MISSING_SEMANTIC, "Shader does not write position."); + return VKD3D_ERROR_INVALID_SHADER; + } + } + + if (!(e = vsir_signature_find_element_by_name(&program->output_signature, "FOG", 0))) + { + ERR("Fog output not found.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + fog_signature_idx = e - program->output_signature.elements; + + temp = program->temp_count++; + + /* Insert a fog write before each ret, and convert either specular or + * position output to a temp. */ + for (size_t i = 0; i < program->instructions.count; ++i) + { + struct vkd3d_shader_instruction *ins = &program->instructions.elements[i]; + + if (vsir_instruction_is_dcl(ins)) + continue; + + if (ins->opcode == VKD3DSIH_RET) + { + size_t new_pos; + int ret; + + if ((ret = insert_vertex_fog_before_ret(program, ins, source, temp, + fog_signature_idx, source_signature_idx, &new_pos)) < 0) + return ret; + i = new_pos; + continue; + } + + for (size_t j = 0; j < ins->dst_count; ++j) + { + struct vkd3d_shader_dst_param *dst = &ins->dst[j]; + + /* Note we run after I/O normalization. */ + if (dst->reg.type == VKD3DSPR_OUTPUT && dst->reg.idx[0].offset == source_signature_idx) + { + dst->reg.type = VKD3DSPR_TEMP; + dst->reg.idx[0].offset = temp; + } + } + } + + program->has_fog = true; + + return VKD3D_OK; +} + struct validation_context { struct vkd3d_shader_message_context *message_context; @@ -8994,6 +9165,9 @@ enum vkd3d_result vsir_program_transform_early(struct vsir_program *program, uin /* For vsir_program_insert_fragment_fog(). */ vsir_transform(&ctx, vsir_program_add_fog_input); + /* For vsir_program_insert_vertex_fog(). */ + vsir_transform(&ctx, vsir_program_add_fog_output); + return ctx.result; } @@ -9049,6 +9223,7 @@ enum vkd3d_result vsir_program_transform(struct vsir_program *program, uint64_t vsir_transform(&ctx, vsir_program_insert_point_size_clamp); vsir_transform(&ctx, vsir_program_insert_point_coord); vsir_transform(&ctx, vsir_program_insert_fragment_fog); + vsir_transform(&ctx, vsir_program_insert_vertex_fog); if (TRACE_ON()) vsir_program_trace(program); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index b9f2b25c..4c9795e4 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1428,6 +1428,7 @@ struct vsir_program bool use_vocp; bool has_point_size; bool has_point_coord; + bool has_fog; uint8_t diffuse_written_mask; enum vsir_control_flow_type cf_type; enum vsir_normalisation_level normalisation_level; diff --git a/tests/hlsl/fog.shader_test b/tests/hlsl/fog.shader_test index b9455dcb..5f86a9be 100644 --- a/tests/hlsl/fog.shader_test +++ b/tests/hlsl/fog.shader_test @@ -70,3 +70,66 @@ probe (160, 120) rgba (0.0, 0.8, 1.0, 1.0) probe (480, 360) rgba (0.0, 0.8, 1.0, 1.0) probe (160, 120) rgba (0.0, 0.8, 1.0, 1.0) probe (480, 360) rgba (0.0, 0.8, 1.0, 1.0) + +fog linear ortho 0.1 1.1 +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.2, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.4, 1.0, 1.0) 64 +probe (160, 120) rgba (0.0, 0.2, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.4, 1.0, 1.0) 64 + + +% Test a VS that doesn't write fog, but does write specular. + +[vertex shader] +float4 main(float4 pos : position, out float4 specular : color1) : sv_position +{ + specular = float4(0, 0, 0, 0.3); + return pos; +} + +[test] +fog none +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.7, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.7, 1.0, 1.0) +probe (160, 120) rgba (0.0, 0.7, 1.0, 1.0) +probe (480, 360) rgba (0.0, 0.7, 1.0, 1.0) + +fog linear ortho 0.1 1.1 +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.2, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.4, 1.0, 1.0) 64 +probe (160, 120) rgba (0.0, 0.2, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.4, 1.0, 1.0) 64 + + +% Test the non-orthogonal case, where fog comes from W. +% We don't do this using the same VB: drivers disagree on whether Z means vertex +% Z or pixel Z (i.e. whether perspective division is applied, as well as things +% like depth bias) so we want W to be flat 1.0 for the Z tests. + +[vb 0] +-2.0 0.0 0.2 0.5 + 0.0 2.0 0.2 0.5 + 0.0 0.0 0.2 0.5 + + 0.0 0.0 0.2 0.9 + 0.0 2.0 0.2 0.9 + 2.0 0.0 0.2 0.9 + + 0.0 -2.0 0.5 0.5 +-2.0 0.0 0.5 0.5 + 0.0 0.0 0.5 0.5 + + 0.0 -2.0 0.5 0.9 + 0.0 0.0 0.5 0.9 + 2.0 0.0 0.5 0.9 + +[test] +fog linear non-ortho 0.1 1.1 +draw triangle list 12 +probe (300, 220) rgba (0.0, 0.4, 1.0, 1.0) 64 +probe (340, 220) rgba (0.0, 0.8, 1.0, 1.0) 64 +probe (300, 260) rgba (0.0, 0.4, 1.0, 1.0) 64 +probe (340, 260) rgba (0.0, 0.8, 1.0, 1.0) 64 diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 748d89be..71b16645 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -1439,6 +1439,17 @@ static void parse_test_directive(struct shader_runner *runner, const char *line) runner->fog_mode = FOG_MODE_EXP2; else fatal_error("Invalid fog mode '%s'.\n", line); + + if (match_string(line, "ortho", &line)) + runner->ortho_fog = true; + if (match_string(line, "non-ortho", &line)) + runner->ortho_fog = false; + + if (runner->fog_mode == FOG_MODE_LINEAR) + { + if (sscanf(line, "%f %f", &runner->fog_start, &runner->fog_end) < 2) + fatal_error("Malformed fog constants '%s'.\n", line); + } } else if (match_string(line, "fog-colour", &line)) { @@ -1896,6 +1907,8 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c runner->point_size_min = 1.0f; runner->point_size_max = FLT_MAX; runner->fog_mode = FOG_MODE_DISABLE; + runner->fog_start = 0.0f; + runner->fog_end = 1.0f; runner->sample_mask = ~0u; runner->depth_bounds = false; diff --git a/tests/shader_runner.h b/tests/shader_runner.h index 3a7267f3..95e96677 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -234,6 +234,8 @@ struct shader_runner bool point_sprite; struct vec4 fog_colour; enum fog_mode fog_mode; + float fog_start, fog_end; + bool ortho_fog; }; struct shader_runner_ops diff --git a/tests/shader_runner_d3d9.c b/tests/shader_runner_d3d9.c index a7d63fb6..279d9136 100644 --- a/tests/shader_runner_d3d9.c +++ b/tests/shader_runner_d3d9.c @@ -354,6 +354,7 @@ static bool d3d9_runner_draw(struct shader_runner *r, ID3D10Blob *vs_code, *ps_code; IDirect3DVertexShader9 *vs; IDirect3DPixelShader9 *ps; + D3DMATRIX proj_matrix; HRESULT hr; if (instance_count > 1) @@ -509,8 +510,18 @@ static bool d3d9_runner_draw(struct shader_runner *r, hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGTABLEMODE, runner->r.fog_mode); ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); } + memset(&proj_matrix, 0, sizeof(proj_matrix)); + proj_matrix._11 = proj_matrix._22 = proj_matrix._33 = proj_matrix._44 = 1.0f; + if (!runner->r.ortho_fog) + proj_matrix._44 = 1.01f; + hr = IDirect3DDevice9_SetTransform(device, D3DTS_PROJECTION, &proj_matrix); + ok(hr == D3D_OK, "Failed to set projection matrix, hr %#lx.\n", hr); hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGCOLOR, d3d_color_from_vec4(&runner->r.fog_colour)); ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGSTART, float_to_int(runner->r.fog_start)); + ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGEND, float_to_int(runner->r.fog_end)); + ok(hr == D3D_OK, "Failed to set render state, hr %#lx.\n", hr); hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration); ok(hr == D3D_OK, "Failed to create vertex declaration, hr %#lx.\n", hr); diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c index 60312d09..dc66d4cd 100644 --- a/tests/shader_runner_vulkan.c +++ b/tests/shader_runner_vulkan.c @@ -273,10 +273,21 @@ static enum vkd3d_shader_fog_fragment_mode get_fog_fragment_mode(enum fog_mode m { case FOG_MODE_DISABLE: return VKD3D_SHADER_FOG_FRAGMENT_NONE; case FOG_MODE_NONE: return VKD3D_SHADER_FOG_FRAGMENT_LINEAR; + case FOG_MODE_LINEAR: return VKD3D_SHADER_FOG_FRAGMENT_LINEAR; default: fatal_error("Unhandled fog mode %#x.\n", mode); } } +static enum vkd3d_shader_fog_source get_fog_source(const struct shader_runner *runner) +{ + if (runner->fog_mode == FOG_MODE_DISABLE) + return VKD3D_SHADER_FOG_SOURCE_FOG; + else if (runner->fog_mode == FOG_MODE_NONE) + return VKD3D_SHADER_FOG_SOURCE_FOG_OR_SPECULAR_W; + + return runner->ortho_fog ? VKD3D_SHADER_FOG_SOURCE_Z : VKD3D_SHADER_FOG_SOURCE_W; +} + static bool compile_hlsl_and_scan(struct vulkan_shader_runner *runner, enum shader_type type) { struct vkd3d_shader_parameter_info parameter_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_PARAMETER_INFO}; @@ -327,7 +338,7 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner, struct vkd3d_shader_varying_map varying_map[12]; struct vkd3d_shader_resource_binding *binding; struct vkd3d_shader_compile_option options[2]; - struct vkd3d_shader_parameter1 parameters[21]; + struct vkd3d_shader_parameter1 parameters[22]; unsigned int i; char *messages; int ret; @@ -504,6 +515,16 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner, parameters[19].u.immediate_constant.u.f32 = 0.0f; parameters[20].u.immediate_constant.u.f32 = -1.0f; } + else + { + parameters[19].u.immediate_constant.u.f32 = runner->r.fog_end; + parameters[20].u.immediate_constant.u.f32 = 1.0 / (runner->r.fog_end - runner->r.fog_start); + } + + parameters[21].name = VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE; + parameters[21].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; + parameters[21].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; + parameters[21].u.immediate_constant.u.u32 = get_fog_source(&runner->r); parameter_info.parameter_count = ARRAY_SIZE(parameters); parameter_info.parameters = parameters;