diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 300792ae..af55d63a 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -500,6 +500,32 @@ enum vkd3d_shader_fog_fragment_mode * Equivalently, the fog interpolation factor is 1. */ VKD3D_SHADER_FOG_FRAGMENT_NONE = 0x0, + /** + * The fog interpolation factor is 2^-(k * c). + * + * In order to implement traditional exponential fog, as present in + * Direct3D and OpenGL, i.e. + * + * e^-(density * c) + * + * set + * + * k = density * log₂(e) + */ + VKD3D_SHADER_FOG_FRAGMENT_EXP = 0x1, + /** + * The fog interpolation factor is 2^-((k * c)²). + * + * In order to implement traditional square-exponential fog, as present in + * Direct3D and OpenGL, i.e. + * + * e^-((density * c)²) + * + * set + * + * k = density * √log₂(e) + */ + VKD3D_SHADER_FOG_FRAGMENT_EXP2 = 0x2, /** * The fog interpolation factor is (E - c) * k. * @@ -871,7 +897,7 @@ enum vkd3d_shader_parameter_name */ VKD3D_SHADER_PARAMETER_NAME_FOG_END, /** - * Scale value for linear fog. + * Scale value for fog. * See VKD3D_SHADER_PARAMETER_NAME_FOG_FRAGMENT_MODE for documentation of * fog. * diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 0bbefa5a..53b26dac 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -6764,7 +6764,7 @@ static enum vkd3d_result insert_fragment_fog_before_ret(struct vsir_program *pro uint32_t ssa_factor = program->ssa_count++; size_t pos = ret - instructions->elements; struct vkd3d_shader_instruction *ins; - uint32_t ssa_temp; + uint32_t ssa_temp, ssa_temp2; switch (mode) { @@ -6798,6 +6798,71 @@ static enum vkd3d_result insert_fragment_fog_before_ret(struct vsir_program *pro src_param_init_parameter(&ins->src[1], VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, VKD3D_DATA_FLOAT); break; + case VKD3D_SHADER_FOG_FRAGMENT_EXP: + /* We generate the following code: + * + * mul sr0, FOG_SCALE, vFOG.x + * exp_sat srFACTOR, -sr0 + */ + if (!shader_instruction_array_insert_at(&program->instructions, pos, 4)) + return VKD3D_ERROR_OUT_OF_MEMORY; + *ret_pos = pos + 4; + + ssa_temp = program->ssa_count++; + + ins = &program->instructions.elements[pos]; + + vsir_instruction_init_with_params(program, ins, &loc, VKD3DSIH_MUL, 1, 2); + dst_param_init_ssa_float(&ins->dst[0], ssa_temp); + src_param_init_parameter(&ins->src[0], VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, VKD3D_DATA_FLOAT); + vsir_src_param_init(&ins->src[1], VKD3DSPR_INPUT, VKD3D_DATA_FLOAT, 1); + ins->src[1].reg.idx[0].offset = fog_signature_idx; + ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_EXP, 1, 1); + dst_param_init_ssa_float(&ins->dst[0], ssa_factor); + ins->dst[0].modifiers = VKD3DSPDM_SATURATE; + src_param_init_ssa_float(&ins->src[0], ssa_temp); + ins->src[0].modifiers = VKD3DSPSM_NEG; + break; + + case VKD3D_SHADER_FOG_FRAGMENT_EXP2: + /* We generate the following code: + * + * mul sr0, FOG_SCALE, vFOG.x + * mul sr1, sr0, sr0 + * exp_sat srFACTOR, -sr1 + */ + if (!shader_instruction_array_insert_at(&program->instructions, pos, 5)) + return VKD3D_ERROR_OUT_OF_MEMORY; + *ret_pos = pos + 5; + + ssa_temp = program->ssa_count++; + ssa_temp2 = program->ssa_count++; + + ins = &program->instructions.elements[pos]; + + vsir_instruction_init_with_params(program, ins, &loc, VKD3DSIH_MUL, 1, 2); + dst_param_init_ssa_float(&ins->dst[0], ssa_temp); + src_param_init_parameter(&ins->src[0], VKD3D_SHADER_PARAMETER_NAME_FOG_SCALE, VKD3D_DATA_FLOAT); + vsir_src_param_init(&ins->src[1], VKD3DSPR_INPUT, VKD3D_DATA_FLOAT, 1); + ins->src[1].reg.idx[0].offset = fog_signature_idx; + ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_MUL, 1, 2); + dst_param_init_ssa_float(&ins->dst[0], ssa_temp2); + src_param_init_ssa_float(&ins->src[0], ssa_temp); + src_param_init_ssa_float(&ins->src[1], ssa_temp); + + vsir_instruction_init_with_params(program, ++ins, &loc, VKD3DSIH_EXP, 1, 1); + dst_param_init_ssa_float(&ins->dst[0], ssa_factor); + ins->dst[0].modifiers = VKD3DSPDM_SATURATE; + src_param_init_ssa_float(&ins->src[0], ssa_temp2); + ins->src[0].modifiers = VKD3DSPSM_NEG; + break; + default: vkd3d_unreachable(); } diff --git a/tests/hlsl/fog.shader_test b/tests/hlsl/fog.shader_test index 5f86a9be..e5ce6eef 100644 --- a/tests/hlsl/fog.shader_test +++ b/tests/hlsl/fog.shader_test @@ -78,6 +78,20 @@ 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 +fog exp ortho 2.0 +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.45118836, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.63212056, 1.0, 1.0) 64 +probe (160, 120) rgba (0.0, 0.45118836, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.63212056, 1.0, 1.0) 64 + +fog exp2 ortho 2.0 +draw triangle strip 4 +probe (160, 120) rgba (0.0, 0.30232367, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.63212056, 1.0, 1.0) 64 +probe (160, 120) rgba (0.0, 0.30232367, 1.0, 1.0) 64 +probe (480, 360) rgba (0.0, 0.63212056, 1.0, 1.0) 64 + % Test a VS that doesn't write fog, but does write specular. diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 71b16645..98c94f60 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -1450,6 +1450,11 @@ static void parse_test_directive(struct shader_runner *runner, const char *line) if (sscanf(line, "%f %f", &runner->fog_start, &runner->fog_end) < 2) fatal_error("Malformed fog constants '%s'.\n", line); } + else if (runner->fog_mode == FOG_MODE_EXP || runner->fog_mode == FOG_MODE_EXP2) + { + if (sscanf(line, "%f", &runner->fog_density) < 1) + fatal_error("Malformed fog constants '%s'.\n", line); + } } else if (match_string(line, "fog-colour", &line)) { @@ -1909,6 +1914,7 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c runner->fog_mode = FOG_MODE_DISABLE; runner->fog_start = 0.0f; runner->fog_end = 1.0f; + runner->fog_density = 1.0f; runner->sample_mask = ~0u; runner->depth_bounds = false; diff --git a/tests/shader_runner.h b/tests/shader_runner.h index 95e96677..b87feba2 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -234,7 +234,7 @@ struct shader_runner bool point_sprite; struct vec4 fog_colour; enum fog_mode fog_mode; - float fog_start, fog_end; + float fog_start, fog_end, fog_density; bool ortho_fog; }; diff --git a/tests/shader_runner_d3d9.c b/tests/shader_runner_d3d9.c index 279d9136..4e148ad0 100644 --- a/tests/shader_runner_d3d9.c +++ b/tests/shader_runner_d3d9.c @@ -522,6 +522,8 @@ static bool d3d9_runner_draw(struct shader_runner *r, 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_SetRenderState(device, D3DRS_FOGDENSITY, float_to_int(runner->r.fog_density)); + 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 dc66d4cd..0b1be984 100644 --- a/tests/shader_runner_vulkan.c +++ b/tests/shader_runner_vulkan.c @@ -30,6 +30,9 @@ #include "vulkan_utils.h" #include "vkd3d_test.h" +#define LOG2_E 1.44269504f +#define SQRT_LOG2_E 1.20112241f + struct vulkan_resource { struct resource r; @@ -274,6 +277,8 @@ 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; + case FOG_MODE_EXP: return VKD3D_SHADER_FOG_FRAGMENT_EXP; + case FOG_MODE_EXP2: return VKD3D_SHADER_FOG_FRAGMENT_EXP2; default: fatal_error("Unhandled fog mode %#x.\n", mode); } } @@ -510,15 +515,28 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner, parameters[20].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; parameters[20].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32; - if (runner->r.fog_mode == FOG_MODE_NONE) + switch (runner->r.fog_mode) { - 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); + case FOG_MODE_NONE: + parameters[19].u.immediate_constant.u.f32 = 0.0f; + parameters[20].u.immediate_constant.u.f32 = -1.0f; + break; + + case FOG_MODE_LINEAR: + 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); + break; + + case FOG_MODE_EXP: + parameters[20].u.immediate_constant.u.f32 = runner->r.fog_density * LOG2_E; + break; + + case FOG_MODE_EXP2: + parameters[20].u.immediate_constant.u.f32 = runner->r.fog_density * SQRT_LOG2_E; + break; + + case FOG_MODE_DISABLE: + break; } parameters[21].name = VKD3D_SHADER_PARAMETER_NAME_FOG_SOURCE;