vkd3d-shader/ir: Implement exponential fog.

This commit is contained in:
Elizabeth Figura 2024-11-13 19:39:35 -06:00 committed by Henri Verbeet
parent 1fbbc82f3a
commit d56601c8d0
Notes: Henri Verbeet 2024-12-02 17:19:05 +01:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1265
7 changed files with 142 additions and 11 deletions

View File

@ -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.
*

View File

@ -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();
}

View File

@ -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.

View File

@ -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;

View File

@ -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;
};

View File

@ -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);

View File

@ -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)
{
case FOG_MODE_NONE:
parameters[19].u.immediate_constant.u.f32 = 0.0f;
parameters[20].u.immediate_constant.u.f32 = -1.0f;
}
else
{
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;