vkd3d-shader: Implement shader model 1.0-1.3 texture projection.

This commit is contained in:
Elizabeth Figura
2025-08-09 15:33:46 -05:00
committed by Henri Verbeet
parent 44c80c60b8
commit c50210f230
Notes: Henri Verbeet 2025-11-20 18:37:00 +01:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1814
9 changed files with 209 additions and 9 deletions

View File

@@ -221,6 +221,7 @@ vkd3d_shader_tests = \
tests/hlsl/pow.shader_test \
tests/hlsl/precise-modifier.shader_test \
tests/hlsl/primitive-id.shader_test \
tests/hlsl/ps1-projection.shader_test \
tests/hlsl/ps1-sampler.shader_test \
tests/hlsl/rasteriser-ordered-views.shader_test \
tests/hlsl/rcp.shader_test \

View File

@@ -1039,6 +1039,25 @@ enum vkd3d_shader_parameter_name
VKD3D_SHADER_PARAMETER_NAME_BUMP_LUMINANCE_OFFSET_3,
VKD3D_SHADER_PARAMETER_NAME_BUMP_LUMINANCE_OFFSET_4,
VKD3D_SHADER_PARAMETER_NAME_BUMP_LUMINANCE_OFFSET_5,
/**
* A mask of projected textures.
*
* When this parameter is provided to a shader model 1.0-1.3 pixel shader,
* for each nonzero bit of this mask, the corresponding texture will be
* projected. That is, it will have its coordinates divided by their W
* component before sampling.
*
* The default value is zero, i.e. no textures are projected.
*
* 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.19
*/
VKD3D_SHADER_PARAMETER_NAME_PROJECTED_TEXTURE_MASK,
VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_NAME),
};

View File

@@ -1936,8 +1936,36 @@ static enum vkd3d_result vsir_program_lower_texldl(struct vsir_program *program,
return VKD3D_OK;
}
static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program, struct vkd3d_shader_instruction *ins)
static bool is_texture_projected(const struct vsir_program *program,
struct vkd3d_shader_message_context *message_context, unsigned int index)
{
const struct vkd3d_shader_parameter1 *parameter;
if (!(parameter = vsir_program_get_parameter(program, VKD3D_SHADER_PARAMETER_NAME_PROJECTED_TEXTURE_MASK)))
return false;
if (parameter->type != VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT)
{
vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED,
"Unsupported projected texture mask parameter type %#x.", parameter->type);
return false;
}
if (parameter->data_type != VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32)
{
vkd3d_shader_error(message_context, NULL, VKD3D_SHADER_ERROR_VSIR_INVALID_DATA_TYPE,
"Invalid projected texture mask parameter data type %#x.", parameter->data_type);
return false;
}
return parameter->u.immediate_constant.u.u32 & (1u << index);
}
static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program,
struct vsir_program_iterator *it, struct vkd3d_shader_message_context *message_context)
{
struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(it);
const struct vkd3d_shader_location location = ins->location;
const struct vkd3d_shader_descriptor_info1 *sampler;
unsigned int idx = ins->dst[0].reg.idx[0].offset;
struct vkd3d_shader_src_param *srcs;
@@ -1951,10 +1979,40 @@ static enum vkd3d_result vsir_program_lower_tex(struct vsir_program *program, st
if (!(srcs = vsir_program_get_src_params(program, 4)))
return VKD3D_ERROR_OUT_OF_MEMORY;
vsir_src_param_init(&srcs[0], VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
srcs[0].reg.idx[0].offset = idx;
srcs[0].reg.dimension = VSIR_DIMENSION_VEC4;
srcs[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
if (is_texture_projected(program, message_context, idx))
{
struct vkd3d_shader_dst_param *dst = ins->dst;
uint32_t coords = program->ssa_count++;
/* div sr0, t#, t#.w */
if (!vsir_program_iterator_insert_after(it, 1))
return VKD3D_ERROR_OUT_OF_MEMORY;
ins = vsir_program_iterator_current(it);
if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_DIV, 1, 2))
return VKD3D_ERROR_OUT_OF_MEMORY;
dst_param_init_ssa_float4(&ins->dst[0], coords);
vsir_src_param_init(&ins->src[0], VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
ins->src[0].reg.idx[0].offset = idx;
ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
ins->src[1] = ins->src[0];
ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W);
ins = vsir_program_iterator_next(it);
vsir_instruction_init(ins, &location, VSIR_OP_SAMPLE);
ins->dst_count = 1;
ins->dst = dst;
src_param_init_ssa_float4(&srcs[0], coords);
}
else
{
vsir_src_param_init(&srcs[0], VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
srcs[0].reg.idx[0].offset = idx;
srcs[0].reg.dimension = VSIR_DIMENSION_VEC4;
srcs[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
}
vsir_src_param_init_resource(&srcs[1], idx, idx);
vsir_src_param_init_sampler(&srcs[2], idx, idx);
@@ -2082,6 +2140,7 @@ static enum vkd3d_result vsir_program_lower_texbem(struct vsir_program *program,
unsigned int idx = ins->dst[0].reg.idx[0].offset;
uint32_t ssa_coords, ssa_luminance, ssa_sample;
struct vkd3d_shader_src_param orig_coords;
bool projected;
/* texbem t#, SRC
* ->
@@ -2097,6 +2156,11 @@ static enum vkd3d_result vsir_program_lower_texbem(struct vsir_program *program,
* mad srLUM.x, SRC.z, BUMP_LUMINANCE_SCALE#, BUMP_LUMINANCE_OFFSET#
* mul t#, t#, srLUM.xxxx
*
* If projecting, we replace srCOORDS calculation with
*
* div srPROJ, t#, t#.w
* bem srCOORDS.xy, srPROJ.xy, SRC
*
* Note that the t# destination will subsequently be turned into a temp. */
descriptor = vkd3d_shader_find_descriptor(&program->descriptors, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, idx);
@@ -2115,7 +2179,8 @@ static enum vkd3d_result vsir_program_lower_texbem(struct vsir_program *program,
return VKD3D_ERROR_NOT_IMPLEMENTED;
}
if (!vsir_program_iterator_insert_after(it, is_texbeml ? 4 : 2))
projected = is_texture_projected(program, message_context, idx);
if (!vsir_program_iterator_insert_after(it, 2 + (is_texbeml ? 2 : 0) + (projected ? 1 : 0)))
return VKD3D_ERROR_OUT_OF_MEMORY;
vsir_src_param_init(&orig_coords, VKD3DSPR_TEXTURE, VSIR_DATA_F32, 1);
@@ -2123,6 +2188,23 @@ static enum vkd3d_result vsir_program_lower_texbem(struct vsir_program *program,
orig_coords.reg.dimension = VSIR_DIMENSION_VEC4;
orig_coords.swizzle = VKD3D_SHADER_NO_SWIZZLE;
if (projected)
{
uint32_t ssa_proj = program->ssa_count++;
ins = vsir_program_iterator_current(it);
if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_DIV, 1, 2))
return VKD3D_ERROR_OUT_OF_MEMORY;
dst_param_init_ssa_float4(&ins->dst[0], ssa_proj);
ins->src[0] = orig_coords;
ins->src[1] = ins->src[0];
ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W);
src_param_init_ssa_float4(&orig_coords, ssa_proj);
vsir_program_iterator_next(it);
}
if (!(ins = generate_bump_coords(program, it, idx, &orig_coords, &src[0], &location)))
return VKD3D_ERROR_OUT_OF_MEMORY;
ssa_coords = ins->dst[0].reg.idx[0].offset;
@@ -2290,7 +2372,7 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr
break;
case VSIR_OP_TEX:
ret = vsir_program_lower_tex(program, ins);
ret = vsir_program_lower_tex(program, &it, message_context);
break;
case VSIR_OP_TEXLD:

View File

@@ -67,10 +67,15 @@ ffff0101 % ps_1_1
[test]
bump 1 f32(0.1, 0.2, 0.3, 0.5) 2.0 0.3
projected 1 disable
draw quad
% WARP transposes the matrix for TEXBEM/TEXBEML, but not for BEM.
if(!warp) probe (250, 500) f32(0.58, 0.27, 0.73, 0.42) 32
if(!warp) probe (750, 500) f32(0.47, 0.33, 0.67, 0.53) 16
projected 1 enable
draw quad
if(!warp) probe (250, 500) f32(0.28, 0.47, 0.53, 0.72) 32
if(!warp) probe (750, 500) f32(0.17, 0.53, 0.47, 0.83) 16
[pixel shader d3dbc-hex]
% TODO: Convert to assembly.
@@ -82,11 +87,16 @@ ffff0101 % ps_1_1
[test]
bump 1 f32(0.1, 0.2, 0.3, 0.5) 2.0 0.3
projected 1 disable
draw quad
% Besides transposing the matrix, WARP also uses the texcoord t1.z (0.4)
% as a luminance factor, instead of the textured value t0.z (0.3 or 0.2).
if(!warp) probe (250, 500) f32(0.522, 0.243, 0.657, 0.378) 32
if(!warp) probe (750, 500) f32(0.329, 0.231, 0.469, 0.371) 16
projected 1 enable
draw quad
if(!warp) probe (250, 500) f32(0.252, 0.423, 0.477, 0.648) 32
if(!warp) probe (750, 500) f32(0.119, 0.371, 0.329, 0.581) 32
[pixel shader d3dbc-hex]
% TODO: Convert to assembly.

View File

@@ -0,0 +1,62 @@
% Test for the D3DTTFF_* projected texture flags.
[require]
shader model < 3.0
[sampler 0]
filter point point point
address clamp clamp clamp
[srv 0]
size (2d, 5, 5)
.0 .0 .0 .1 .1 .0 .0 .1 .2 .0 .0 .1 .3 .0 .0 .1 .4 .0 .0 .1
.0 .1 .0 .1 .1 .1 .0 .1 .2 .1 .0 .1 .3 .1 .0 .1 .4 .1 .0 .1
.0 .2 .0 .1 .1 .2 .0 .1 .2 .2 .0 .1 .3 .2 .0 .1 .4 .2 .0 .1
.0 .3 .0 .1 .1 .3 .0 .1 .2 .3 .0 .1 .3 .3 .0 .1 .4 .3 .0 .1
.0 .4 .0 .1 .1 .4 .0 .1 .2 .4 .0 .1 .3 .4 .0 .1 .4 .4 .0 .1
[vertex shader]
void main(inout float4 pos : position, out float4 tex : texcoord)
{
tex = float4(1.2, 0.5, 4.0, 2.0);
}
[pixel shader d3dbc-hex]
% TODO: Convert to assembly or HLSL.
ffff0101 % ps_1_1
00000042 b00f0000 % tex t0
00000001 800f0000 b0e40000 % mov r0, t0
0000ffff % end
[test]
projected 0 disable
draw quad
probe (0, 0) f32(0.4, 0.2, 0.0, 0.1)
projected 0 enable
draw quad
probe (0, 0) f32(0.3, 0.1, 0.0, 0.1)
% Doesn't affect 1.4 or 2.0.
[pixel shader d3dbc-hex]
% TODO: Convert to assembly or HLSL.
ffff0104 % ps_1_4
00000042 800f0000 b0e40000 % texld r0, t0
0000ffff % end
[test]
projected 0 enable
draw quad
probe (0, 0) f32(0.4, 0.2, 0.0, 0.1)
[pixel shader]
sampler sam;
float4 main(float2 tex : texcoord) : sv_target
{
return tex2D(sam, tex);
}
[test]
projected 0 enable
draw quad
probe (0, 0) f32(0.4, 0.2, 0.0, 0.1)

View File

@@ -765,8 +765,10 @@ static void parse_resource_directive(struct resource_params *resource, const cha
for (;;)
{
while (isspace(*line))
++line;
u.u = strtoul(line, &rest, 0);
if (rest && *rest == '.')
if (*line == '.' || (rest && *rest == '.'))
u.f = strtof(line, &rest);
if (rest == line)
@@ -1764,6 +1766,20 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
else
fatal_error("Invalid denorm mode '%s'.\n", line);
}
else if (match_string(line, "projected", &line))
{
unsigned int index;
index = strtoul(line, (char **)&rest, 10);
if (rest == line || index >= 6)
fatal_error("Malformed projection directive '%s'.\n", line);
line = rest;
if (match_string(line, "enable", &line))
runner->projected_texture_mask |= (1u << index);
else
runner->projected_texture_mask &= ~(1u << index);
}
else
{
fatal_error("Unknown test directive '%s'.\n", line);

View File

@@ -304,6 +304,7 @@ struct shader_runner
float matrix[2][2];
float luminance_scale, luminance_offset;
} bump[8];
uint8_t projected_texture_mask;
enum denorm_mode denorm_mode;

View File

@@ -651,6 +651,10 @@ static bool d3d9_runner_draw(struct shader_runner *r,
hr = IDirect3DDevice9_SetTextureStageState(device, i, D3DTSS_BUMPENVLOFFSET,
float_to_int(runner->r.bump[i].luminance_offset));
ok(hr == D3D_OK, "Failed to set texture state, hr %#lx.\n", hr);
hr = IDirect3DDevice9_SetTextureStageState(device, i, D3DTSS_TEXTURETRANSFORMFLAGS,
(runner->r.projected_texture_mask & (1u << i)) ? D3DTTFF_PROJECTED : 0);
ok(hr == D3D_OK, "Failed to set texture state, hr %#lx.\n", hr);
}
hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration);

View File

@@ -366,7 +366,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[40];
struct vkd3d_shader_parameter1 parameters[41];
unsigned int i;
char *messages;
int ret;
@@ -609,6 +609,11 @@ static bool compile_d3d_code(struct vulkan_shader_runner *runner,
parameters[34 + i].u.immediate_constant.u.f32 = runner->r.bump[i].luminance_offset;
}
parameters[40].name = VKD3D_SHADER_PARAMETER_NAME_PROJECTED_TEXTURE_MASK;
parameters[40].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT;
parameters[40].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32;
parameters[40].u.immediate_constant.u.u32 = runner->r.projected_texture_mask;
parameter_info.parameter_count = ARRAY_SIZE(parameters);
parameter_info.parameters = parameters;