From a492d64fef66c20716322d14bac96548f00f499e Mon Sep 17 00:00:00 2001 From: Elizabeth Figura Date: Tue, 15 Oct 2024 19:04:19 -0500 Subject: [PATCH] vkd3d-shader/ir: Allow controlling point sprite through a parameter. --- include/vkd3d_shader.h | 33 ++++ libs/vkd3d-shader/d3d_asm.c | 4 + libs/vkd3d-shader/ir.c | 194 +++++++++++++++++++++++ libs/vkd3d-shader/spirv.c | 13 ++ libs/vkd3d-shader/vkd3d_shader_private.h | 2 + 5 files changed, 246 insertions(+) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 5c0d13ea..1476387c 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -700,6 +700,39 @@ enum vkd3d_shader_parameter_name * \since 1.14 */ VKD3D_SHADER_PARAMETER_NAME_POINT_SIZE_MAX, + /** + * Whether texture coordinate inputs should take their values from the + * point coordinate. + * + * When this parameter is provided to a pixel shader, and the value is + * nonzero, any fragment shader input with the semantic name "TEXCOORD" + * takes its value from the point coordinates instead of from the previous + * shader. The point coordinates here are defined as a four-component vector + * whose X and Y components are the X and Y coordinates of the fragment + * within a point being rasterized, and whose Z and W components are zero. + * + * In GLSL, the X and Y components are drawn from gl_PointCoord; in SPIR-V, + * they are drawn from a variable with the BuiltinPointCoord decoration. + * + * This includes t# fragment shader inputs in shader model 2 shaders, + * as well as texture sampling in shader model 1 shaders. + * + * This parameter can be used to implement fixed function point sprite, as + * present in Direct3D versions 8 and 9, if the target environment does not + * support point sprite as part of its own fixed-function API (as Vulkan and + * core OpenGL). + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. + * + * The default value is zero, i.e. use the original varyings. + * + * Only VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT is supported in this + * version of vkd3d-shader. + * + * \since 1.14 + */ + VKD3D_SHADER_PARAMETER_NAME_POINT_SPRITE, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_NAME), }; diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 38d566d9..c8993a1f 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -1190,6 +1190,10 @@ static void shader_print_register(struct vkd3d_d3d_asm_compiler *compiler, const vkd3d_string_buffer_printf(buffer, "vWaveLaneIndex"); break; + case VKD3DSPR_POINT_COORD: + vkd3d_string_buffer_printf(buffer, "vPointCoord"); + break; + default: vkd3d_string_buffer_printf(buffer, "%s%s", compiler->colours.error, reg->type, compiler->colours.reset); diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 04ec24c1..8cccc05f 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -264,6 +264,13 @@ static void dst_param_init_temp_bool(struct vkd3d_shader_dst_param *dst, unsigne dst->reg.idx[0].offset = idx; } +static void dst_param_init_temp_float4(struct vkd3d_shader_dst_param *dst, unsigned int idx) +{ + vsir_dst_param_init(dst, VKD3DSPR_TEMP, VKD3D_DATA_FLOAT, 1); + dst->reg.idx[0].offset = idx; + dst->reg.dimension = VSIR_DIMENSION_VEC4; +} + static void dst_param_init_temp_uint(struct vkd3d_shader_dst_param *dst, unsigned int idx) { vsir_dst_param_init(dst, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); @@ -6132,6 +6139,192 @@ static enum vkd3d_result vsir_program_insert_point_size_clamp(struct vsir_progra return VKD3D_OK; } +static bool has_texcoord_signature_element(const struct shader_signature *signature) +{ + for (size_t i = 0; i < signature->element_count; ++i) + { + if (!ascii_strcasecmp(signature->elements[i].semantic_name, "TEXCOORD")) + return true; + } + return false; +} + +/* Returns true if replacement was done. */ +static bool replace_texcoord_with_point_coord(struct vsir_program *program, + struct vkd3d_shader_src_param *src, unsigned int coord_temp) +{ + uint32_t prev_swizzle = src->swizzle; + const struct signature_element *e; + + /* The input semantic may have a nontrivial mask, which we need to + * correct for. E.g. if the mask is .yz, and we read from .y, that needs + * to become .x. */ + static const uint32_t inverse_swizzles[16] = + { + /* Use _ for "undefined" components, for clarity. */ +#define VKD3D_SHADER_SWIZZLE__ VKD3D_SHADER_SWIZZLE_X + 0, + /* .x */ VKD3D_SHADER_SWIZZLE(X, _, _, _), + /* .y */ VKD3D_SHADER_SWIZZLE(_, X, _, _), + /* .xy */ VKD3D_SHADER_SWIZZLE(X, Y, _, _), + /* .z */ VKD3D_SHADER_SWIZZLE(_, _, X, _), + /* .xz */ VKD3D_SHADER_SWIZZLE(X, _, Y, _), + /* .yz */ VKD3D_SHADER_SWIZZLE(_, X, Y, _), + /* .xyz */ VKD3D_SHADER_SWIZZLE(X, Y, Z, _), + /* .w */ VKD3D_SHADER_SWIZZLE(_, _, _, X), + /* .xw */ VKD3D_SHADER_SWIZZLE(X, _, _, Y), + /* .yw */ VKD3D_SHADER_SWIZZLE(_, X, _, Y), + /* .xyw */ VKD3D_SHADER_SWIZZLE(X, Y, _, Z), + /* .zw */ VKD3D_SHADER_SWIZZLE(_, _, X, Y), + /* .xzw */ VKD3D_SHADER_SWIZZLE(X, _, Y, Z), + /* .yzw */ VKD3D_SHADER_SWIZZLE(_, X, Y, Z), + /* .xyzw */ VKD3D_SHADER_SWIZZLE(X, Y, Z, W), +#undef VKD3D_SHADER_SWIZZLE__ + }; + + if (src->reg.type != VKD3DSPR_INPUT) + return false; + e = &program->input_signature.elements[src->reg.idx[0].offset]; + + if (ascii_strcasecmp(e->semantic_name, "TEXCOORD")) + return false; + + src->reg.type = VKD3DSPR_TEMP; + src->reg.idx[0].offset = coord_temp; + + /* If the mask is already contiguous and zero-based, no need to remap + * the swizzle. */ + if (!(e->mask & (e->mask + 1))) + return true; + + src->swizzle = 0; + for (unsigned int i = 0; i < 4; ++i) + { + src->swizzle |= vsir_swizzle_get_component(inverse_swizzles[e->mask], + vsir_swizzle_get_component(prev_swizzle, i)) << VKD3D_SHADER_SWIZZLE_SHIFT(i); + } + + return true; +} + +static enum vkd3d_result vsir_program_insert_point_coord(struct vsir_program *program, + struct vsir_transformation_context *ctx) +{ + const struct vkd3d_shader_parameter1 *sprite_parameter = NULL; + static const struct vkd3d_shader_location no_loc; + struct vkd3d_shader_instruction *ins; + bool used_texcoord = false; + unsigned int coord_temp; + size_t i, insert_pos; + + if (program->shader_version.type != VKD3D_SHADER_TYPE_PIXEL) + return VKD3D_OK; + + for (i = 0; i < program->parameter_count; ++i) + { + const struct vkd3d_shader_parameter1 *parameter = &program->parameters[i]; + + if (parameter->name == VKD3D_SHADER_PARAMETER_NAME_POINT_SPRITE) + sprite_parameter = parameter; + } + + if (!sprite_parameter) + return VKD3D_OK; + + if (sprite_parameter->type != VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) + { + vkd3d_shader_error(ctx->message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Unsupported point sprite parameter type %#x.", sprite_parameter->type); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + if (sprite_parameter->data_type != VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32) + { + vkd3d_shader_error(ctx->message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_INVALID_DATA_TYPE, + "Invalid point sprite parameter data type %#x.", sprite_parameter->data_type); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + if (!sprite_parameter->u.immediate_constant.u.u32) + return VKD3D_OK; + + if (!has_texcoord_signature_element(&program->input_signature)) + return VKD3D_OK; + + /* VKD3DSPR_POINTCOORD is a two-component value; fill the remaining two + * components with zeroes. */ + coord_temp = program->temp_count++; + + /* Construct the new temp after all LABEL, DCL, and NOP instructions. + * We need to skip NOP instructions because they might result from removed + * DCLs, and there could still be DCLs after NOPs. */ + for (i = 0; i < program->instructions.count; ++i) + { + ins = &program->instructions.elements[i]; + + if (!vsir_instruction_is_dcl(ins) && ins->opcode != VKD3DSIH_LABEL && ins->opcode != VKD3DSIH_NOP) + break; + } + + insert_pos = i; + + /* Replace each texcoord read with a read from the point coord. */ + for (; i < program->instructions.count; ++i) + { + ins = &program->instructions.elements[i]; + + if (vsir_instruction_is_dcl(ins)) + continue; + + for (unsigned int j = 0; j < ins->src_count; ++j) + { + used_texcoord |= replace_texcoord_with_point_coord(program, &ins->src[j], coord_temp); + + for (unsigned int k = 0; k < ins->src[j].reg.idx_count; ++k) + { + if (ins->src[j].reg.idx[k].rel_addr) + used_texcoord |= replace_texcoord_with_point_coord(program, + ins->src[j].reg.idx[k].rel_addr, coord_temp); + } + } + + for (unsigned int j = 0; j < ins->dst_count; ++j) + { + for (unsigned int k = 0; k < ins->dst[j].reg.idx_count; ++k) + { + if (ins->dst[j].reg.idx[k].rel_addr) + used_texcoord |= replace_texcoord_with_point_coord(program, + ins->dst[j].reg.idx[k].rel_addr, coord_temp); + } + } + } + + if (used_texcoord) + { + if (!shader_instruction_array_insert_at(&program->instructions, insert_pos, 2)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &program->instructions.elements[insert_pos]; + + vsir_instruction_init_with_params(program, ins, &no_loc, VKD3DSIH_MOV, 1, 1); + dst_param_init_temp_float4(&ins->dst[0], coord_temp); + ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0 | VKD3DSP_WRITEMASK_1; + vsir_src_param_init(&ins->src[0], VKD3DSPR_POINT_COORD, VKD3D_DATA_FLOAT, 0); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE; + ++ins; + + vsir_instruction_init_with_params(program, ins, &no_loc, VKD3DSIH_MOV, 1, 1); + dst_param_init_temp_float4(&ins->dst[0], coord_temp); + ins->dst[0].write_mask = VKD3DSP_WRITEMASK_2 | VKD3DSP_WRITEMASK_3; + vsir_src_param_init(&ins->src[0], VKD3DSPR_IMMCONST, VKD3D_DATA_FLOAT, 0); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ++ins; + + program->has_point_coord = true; + } + + return VKD3D_OK; +} + struct validation_context { struct vkd3d_shader_message_context *message_context; @@ -7838,6 +8031,7 @@ enum vkd3d_result vsir_program_transform(struct vsir_program *program, uint64_t vsir_transform(&ctx, vsir_program_insert_clip_planes); vsir_transform(&ctx, vsir_program_insert_point_size); vsir_transform(&ctx, vsir_program_insert_point_size_clamp); + vsir_transform(&ctx, vsir_program_insert_point_coord); if (TRACE_ON()) vsir_program_trace(program); diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index f68561e3..6bbac961 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -3252,6 +3252,9 @@ static bool spirv_compiler_get_register_name(char *buffer, unsigned int buffer_s case VKD3DSPR_WAVELANEINDEX: snprintf(buffer, buffer_size, "vWaveLaneIndex"); break; + case VKD3DSPR_POINT_COORD: + snprintf(buffer, buffer_size, "vPointCoord"); + break; default: FIXME("Unhandled register %#x.\n", reg->type); snprintf(buffer, buffer_size, "unrecognized_%#x", reg->type); @@ -4886,6 +4889,8 @@ vkd3d_register_builtins[] = {VKD3DSPR_TESSCOORD, {VKD3D_SHADER_COMPONENT_FLOAT, 3, SpvBuiltInTessCoord}}, + {VKD3DSPR_POINT_COORD, {VKD3D_SHADER_COMPONENT_FLOAT, 2, SpvBuiltInPointCoord}}, + {VKD3DSPR_COVERAGE, {VKD3D_SHADER_COMPONENT_UINT, 1, SpvBuiltInSampleMask, NULL, 1}}, {VKD3DSPR_SAMPLEMASK, {VKD3D_SHADER_COMPONENT_UINT, 1, SpvBuiltInSampleMask, NULL, 1}}, @@ -10596,6 +10601,14 @@ static void spirv_compiler_emit_io_declarations(struct spirv_compiler *compiler) dst.reg.idx[0].offset = VSIR_RASTOUT_POINT_SIZE; spirv_compiler_emit_output_register(compiler, &dst); } + + if (compiler->program->has_point_coord) + { + struct vkd3d_shader_dst_param dst; + + vsir_dst_param_init(&dst, VKD3DSPR_POINT_COORD, VKD3D_DATA_FLOAT, 0); + spirv_compiler_emit_input_register(compiler, &dst); + } } static void spirv_compiler_emit_descriptor_declarations(struct spirv_compiler *compiler) diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index daee463b..d4094598 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -648,6 +648,7 @@ enum vkd3d_shader_register_type VKD3DSPR_WAVELANECOUNT, VKD3DSPR_WAVELANEINDEX, VKD3DSPR_PARAMETER, + VKD3DSPR_POINT_COORD, VKD3DSPR_COUNT, @@ -1421,6 +1422,7 @@ struct vsir_program unsigned int ssa_count; bool use_vocp; bool has_point_size; + bool has_point_coord; enum vsir_control_flow_type cf_type; enum vsir_normalisation_level normalisation_level;