From 806363b76546639079bcadc8b2083cec36c16c57 Mon Sep 17 00:00:00 2001 From: Elizabeth Figura Date: Mon, 10 Jun 2024 19:25:18 -0500 Subject: [PATCH] vkd3d-shader: Allow controlling alpha test through vkd3d-shader parameters. --- include/vkd3d_shader.h | 47 +++++ libs/vkd3d-shader/ir.c | 212 +++++++++++++++++++++++ libs/vkd3d-shader/spirv.c | 39 +++-- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 4 files changed, 287 insertions(+), 12 deletions(-) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index ceee4b86..4acb6224 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -492,6 +492,8 @@ enum vkd3d_shader_parameter_data_type VKD3D_SHADER_PARAMETER_DATA_TYPE_UNKNOWN, /** The parameter is provided as a 32-bit unsigned integer. */ VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32, + /** The parameter is provided as a 32-bit float. \since 1.13 */ + VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_DATA_TYPE), }; @@ -511,8 +513,46 @@ enum vkd3d_shader_parameter_name * provides no builtin ability to query this information from the shader. * * The default value is 1. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. */ VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT, + /** + * Alpha test comparison function. When this parameter is provided, if the + * alpha component of the pixel shader colour output at location 0 fails the + * test, as defined by this function and the reference value provided by + * VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, the fragment will be + * discarded. + * + * This parameter, along with VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, + * can be used to implement fixed function alpha test, as present in + * Direct3D versions up to 9, if the target environment does not support + * alpha test as part of its own fixed-function API (as Vulkan and core + * OpenGL). + * + * The default value is VKD3D_SHADER_COMPARISON_FUNC_ALWAYS. + * + * The data type for this parameter must be + * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. The value specified must be + * a member of enum vkd3d_shader_comparison_func. + * + * Only VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT is supported in this + * version of vkd3d-shader. + * + * \since 1.13 + */ + VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_FUNC, + /** + * Alpha test reference value. + * See VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_FUNC for documentation of + * alpha test. + * + * The default value is zero. + * + * \since 1.13 + */ + VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_PARAMETER_NAME), }; @@ -530,6 +570,13 @@ struct vkd3d_shader_parameter_immediate_constant * VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32. */ uint32_t u32; + /** + * The value if the parameter's data type is + * VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32. + * + * \since 1.13 + */ + float f32; } u; }; diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index a9cfa49b..be9e4219 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -732,6 +732,12 @@ static void dst_param_init_temp_uint(struct vkd3d_shader_dst_param *dst, unsigne dst->write_mask = VKD3DSP_WRITEMASK_0; } +static void src_param_init_temp_float(struct vkd3d_shader_src_param *src, unsigned int idx) +{ + vsir_src_param_init(src, VKD3DSPR_TEMP, VKD3D_DATA_FLOAT, 1); + src->reg.idx[0].offset = idx; +} + static void src_param_init_temp_uint(struct vkd3d_shader_src_param *src, unsigned int idx) { vsir_src_param_init(src, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); @@ -744,6 +750,12 @@ static void src_param_init_const_uint(struct vkd3d_shader_src_param *src, uint32 src->reg.u.immconst_u32[0] = value; } +static void src_param_init_parameter(struct vkd3d_shader_src_param *src, uint32_t idx, enum vkd3d_data_type type) +{ + vsir_src_param_init(src, VKD3DSPR_PARAMETER, type, 1); + src->reg.idx[0].offset = idx; +} + void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, enum vkd3d_shader_opcode opcode) { @@ -5348,6 +5360,203 @@ static enum vkd3d_result vsir_program_materialize_undominated_ssas_to_temps(stru return VKD3D_OK; } +static bool find_colour_signature_idx(const struct shader_signature *signature, uint32_t *index) +{ + for (unsigned int i = 0; i < signature->element_count; ++i) + { + if (signature->elements[i].sysval_semantic == VKD3D_SHADER_SV_TARGET + && !signature->elements[i].register_index) + { + *index = i; + return true; + } + } + + return false; +} + +static enum vkd3d_result insert_alpha_test_before_ret(struct vsir_program *program, + const struct vkd3d_shader_instruction *ret, enum vkd3d_shader_comparison_func compare_func, + const struct vkd3d_shader_parameter1 *ref, uint32_t colour_signature_idx, uint32_t colour_temp, size_t *ret_pos) +{ + struct vkd3d_shader_instruction_array *instructions = &program->instructions; + size_t pos = ret - instructions->elements; + struct vkd3d_shader_instruction *ins; + + static const struct + { + enum vkd3d_shader_opcode float_opcode; + enum vkd3d_shader_opcode uint_opcode; + bool swap; + } + opcodes[] = + { + [VKD3D_SHADER_COMPARISON_FUNC_EQUAL] = {VKD3DSIH_EQO, VKD3DSIH_IEQ}, + [VKD3D_SHADER_COMPARISON_FUNC_NOT_EQUAL] = {VKD3DSIH_NEO, VKD3DSIH_INE}, + [VKD3D_SHADER_COMPARISON_FUNC_GREATER_EQUAL] = {VKD3DSIH_GEO, VKD3DSIH_UGE}, + [VKD3D_SHADER_COMPARISON_FUNC_LESS] = {VKD3DSIH_LTO, VKD3DSIH_ULT}, + [VKD3D_SHADER_COMPARISON_FUNC_LESS_EQUAL] = {VKD3DSIH_GEO, VKD3DSIH_UGE, true}, + [VKD3D_SHADER_COMPARISON_FUNC_GREATER] = {VKD3DSIH_LTO, VKD3DSIH_ULT, true}, + }; + + if (compare_func == VKD3D_SHADER_COMPARISON_FUNC_NEVER) + { + if (!shader_instruction_array_insert_at(&program->instructions, pos, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + ins = &program->instructions.elements[pos]; + + vsir_instruction_init_with_params(program, ins, &ret->location, VKD3DSIH_DISCARD, 0, 1); + ins->flags = VKD3D_SHADER_CONDITIONAL_OP_Z; + src_param_init_const_uint(&ins->src[0], 0); + + *ret_pos = pos + 1; + return VKD3D_OK; + } + + if (!shader_instruction_array_insert_at(&program->instructions, pos, 3)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &program->instructions.elements[pos]; + + switch (ref->data_type) + { + case VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32: + vsir_instruction_init_with_params(program, ins, &ret->location, opcodes[compare_func].float_opcode, 1, 2); + src_param_init_temp_float(&ins->src[opcodes[compare_func].swap ? 1 : 0], colour_temp); + src_param_init_parameter(&ins->src[opcodes[compare_func].swap ? 0 : 1], + VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, VKD3D_DATA_FLOAT); + break; + + case VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32: + vsir_instruction_init_with_params(program, ins, &ret->location, opcodes[compare_func].uint_opcode, 1, 2); + src_param_init_temp_uint(&ins->src[opcodes[compare_func].swap ? 1 : 0], colour_temp); + src_param_init_parameter(&ins->src[opcodes[compare_func].swap ? 0 : 1], + VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, VKD3D_DATA_UINT); + break; + + default: + FIXME("Unhandled parameter data type %#x.\n", ref->data_type); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + + dst_param_init_ssa_bool(&ins->dst[0], program->ssa_count); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W); + ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[1].swizzle = VKD3D_SHADER_SWIZZLE(W, W, W, W); + + ++ins; + vsir_instruction_init_with_params(program, ins, &ret->location, VKD3DSIH_DISCARD, 0, 1); + ins->flags = VKD3D_SHADER_CONDITIONAL_OP_Z; + src_param_init_ssa_bool(&ins->src[0], program->ssa_count); + + ++program->ssa_count; + + ++ins; + vsir_instruction_init_with_params(program, ins, &ret->location, VKD3DSIH_MOV, 1, 1); + vsir_dst_param_init(&ins->dst[0], VKD3DSPR_OUTPUT, VKD3D_DATA_FLOAT, 1); + ins->dst[0].reg.idx[0].offset = colour_signature_idx; + ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->dst[0].write_mask = program->output_signature.elements[colour_signature_idx].mask; + src_param_init_temp_float(&ins->src[0], colour_temp); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE; + + *ret_pos = pos + 3; + return VKD3D_OK; +} + +static enum vkd3d_result vsir_program_insert_alpha_test(struct vsir_program *program, + struct vkd3d_shader_message_context *message_context) +{ + const struct vkd3d_shader_parameter1 *func = NULL, *ref = NULL; + static const struct vkd3d_shader_location no_loc; + enum vkd3d_shader_comparison_func compare_func; + uint32_t colour_signature_idx, colour_temp; + struct vkd3d_shader_instruction *ins; + size_t new_pos; + int ret; + + if (program->shader_version.type != VKD3D_SHADER_TYPE_PIXEL) + return VKD3D_OK; + + if (!find_colour_signature_idx(&program->output_signature, &colour_signature_idx) + || !(program->output_signature.elements[colour_signature_idx].mask & VKD3DSP_WRITEMASK_3)) + return VKD3D_OK; + + for (unsigned int i = 0; i < program->parameter_count; ++i) + { + const struct vkd3d_shader_parameter1 *parameter = &program->parameters[i]; + + if (parameter->name == VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_FUNC) + func = parameter; + else if (parameter->name == VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF) + ref = parameter; + } + + if (!func || !ref) + return VKD3D_OK; + + if (func->type != VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Unsupported alpha test function parameter type %#x.\n", func->type); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + if (func->data_type != VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32) + { + vkd3d_shader_error(message_context, &no_loc, VKD3D_SHADER_ERROR_VSIR_INVALID_DATA_TYPE, + "Invalid alpha test function parameter data type %#x.\n", func->data_type); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + compare_func = func->u.immediate_constant.u.u32; + + if (compare_func == VKD3D_SHADER_COMPARISON_FUNC_ALWAYS) + return VKD3D_OK; + + /* We're going to be reading from the output, so we need to go + * through the whole shader and convert it to a temp. */ + + if (compare_func != VKD3D_SHADER_COMPARISON_FUNC_NEVER) + colour_temp = program->temp_count++; + + for (size_t i = 0; i < program->instructions.count; ++i) + { + ins = &program->instructions.elements[i]; + + if (vsir_instruction_is_dcl(ins)) + continue; + + if (ins->opcode == VKD3DSIH_RET) + { + if ((ret = insert_alpha_test_before_ret(program, ins, compare_func, + ref, colour_signature_idx, colour_temp, &new_pos)) < 0) + return ret; + i = new_pos; + continue; + } + + /* No need to convert it if the comparison func is NEVER; we don't + * read from the output in that case. */ + if (compare_func == VKD3D_SHADER_COMPARISON_FUNC_NEVER) + 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 == colour_signature_idx) + { + dst->reg.type = VKD3DSPR_TEMP; + dst->reg.idx[0].offset = colour_temp; + } + } + } + + return VKD3D_OK; +} + struct validation_context { struct vkd3d_shader_message_context *message_context; @@ -6340,6 +6549,9 @@ enum vkd3d_result vsir_program_normalise(struct vsir_program *program, uint64_t return result; } + if ((result = vsir_program_insert_alpha_test(program, message_context)) < 0) + return result; + if (TRACE_ON()) vkd3d_shader_trace(program); diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 215a64a9..ef7fa2ba 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -3320,6 +3320,7 @@ static const struct vkd3d_spec_constant_info vkd3d_shader_parameters[] = { {VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT, 1, "sample_count"}, + {VKD3D_SHADER_PARAMETER_NAME_ALPHA_TEST_REF, 0, "alpha_test_ref"}, }; static const struct vkd3d_spec_constant_info *get_spec_constant_info(enum vkd3d_shader_parameter_name name) @@ -3358,7 +3359,7 @@ static uint32_t spirv_compiler_alloc_spec_constant_id(struct spirv_compiler *com } static uint32_t spirv_compiler_emit_spec_constant(struct spirv_compiler *compiler, - enum vkd3d_shader_parameter_name name, uint32_t spec_id) + enum vkd3d_shader_parameter_name name, uint32_t spec_id, enum vkd3d_data_type type) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_spec_constant_info *info; @@ -3367,7 +3368,7 @@ static uint32_t spirv_compiler_emit_spec_constant(struct spirv_compiler *compile info = get_spec_constant_info(name); default_value = info ? info->default_value : 0; - type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_UINT, 1); + type_id = vkd3d_spirv_get_type_id(builder, vkd3d_component_type_from_data_type(type), 1); id = vkd3d_spirv_build_op_spec_constant(builder, type_id, default_value); vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationSpecId, spec_id); @@ -3386,7 +3387,7 @@ static uint32_t spirv_compiler_emit_spec_constant(struct spirv_compiler *compile } static uint32_t spirv_compiler_get_spec_constant(struct spirv_compiler *compiler, - enum vkd3d_shader_parameter_name name, uint32_t spec_id) + enum vkd3d_shader_parameter_name name, uint32_t spec_id, enum vkd3d_data_type type) { unsigned int i; @@ -3396,17 +3397,17 @@ static uint32_t spirv_compiler_get_spec_constant(struct spirv_compiler *compiler return compiler->spec_constants[i].id; } - return spirv_compiler_emit_spec_constant(compiler, name, spec_id); + return spirv_compiler_emit_spec_constant(compiler, name, spec_id, type); } static uint32_t spirv_compiler_get_buffer_parameter(struct spirv_compiler *compiler, - const struct vkd3d_shader_parameter1 *parameter) + const struct vkd3d_shader_parameter1 *parameter, enum vkd3d_data_type type) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; unsigned int index = parameter - compiler->parameters; uint32_t type_id, ptr_id, ptr_type_id; - type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_UINT, 1); + type_id = vkd3d_spirv_get_type_id(builder, vkd3d_component_type_from_data_type(type), 1); ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassUniform, type_id); ptr_id = vkd3d_spirv_build_op_access_chain1(builder, ptr_type_id, compiler->spirv_parameter_info[index].buffer_id, @@ -3414,10 +3415,11 @@ static uint32_t spirv_compiler_get_buffer_parameter(struct spirv_compiler *compi return vkd3d_spirv_build_op_load(builder, type_id, ptr_id, SpvMemoryAccessMaskNone); } -static uint32_t spirv_compiler_emit_uint_shader_parameter(struct spirv_compiler *compiler, +static uint32_t spirv_compiler_emit_shader_parameter(struct spirv_compiler *compiler, enum vkd3d_shader_parameter_name name) { const struct vkd3d_shader_parameter1 *parameter; + enum vkd3d_data_type type = VKD3D_DATA_UINT; if (!(parameter = spirv_compiler_get_shader_parameter(compiler, name))) { @@ -3426,17 +3428,28 @@ static uint32_t spirv_compiler_emit_uint_shader_parameter(struct spirv_compiler } if (parameter->type == VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT) - return spirv_compiler_get_constant_uint(compiler, parameter->u.immediate_constant.u.u32); + { + if (parameter->data_type == VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32) + return spirv_compiler_get_constant_float(compiler, parameter->u.immediate_constant.u.f32); + else + return spirv_compiler_get_constant_uint(compiler, parameter->u.immediate_constant.u.u32); + } + + if (parameter->data_type == VKD3D_SHADER_PARAMETER_DATA_TYPE_FLOAT32) + type = VKD3D_DATA_FLOAT; + else + type = VKD3D_DATA_UINT; + if (parameter->type == VKD3D_SHADER_PARAMETER_TYPE_SPECIALIZATION_CONSTANT) - return spirv_compiler_get_spec_constant(compiler, name, parameter->u.specialization_constant.id); + return spirv_compiler_get_spec_constant(compiler, name, parameter->u.specialization_constant.id, type); if (parameter->type == VKD3D_SHADER_PARAMETER_TYPE_BUFFER) - return spirv_compiler_get_buffer_parameter(compiler, parameter); + return spirv_compiler_get_buffer_parameter(compiler, parameter, type); FIXME("Unhandled parameter type %#x.\n", parameter->type); default_parameter: return spirv_compiler_get_spec_constant(compiler, - name, spirv_compiler_alloc_spec_constant_id(compiler)); + name, spirv_compiler_alloc_spec_constant_id(compiler), type); } static uint32_t spirv_compiler_emit_construct_vector(struct spirv_compiler *compiler, @@ -4211,6 +4224,8 @@ static uint32_t spirv_compiler_emit_load_reg(struct spirv_compiler *compiler, return spirv_compiler_emit_load_constant64(compiler, reg, swizzle, write_mask); else if (reg->type == VKD3DSPR_UNDEF) return spirv_compiler_emit_load_undef(compiler, reg, write_mask); + else if (reg->type == VKD3DSPR_PARAMETER) + return spirv_compiler_emit_shader_parameter(compiler, reg->idx[0].offset); component_count = vsir_write_mask_component_count(write_mask); component_type = vkd3d_component_type_from_data_type(reg->data_type); @@ -9550,7 +9565,7 @@ static uint32_t spirv_compiler_emit_query_sample_count(struct spirv_compiler *co if (src->reg.type == VKD3DSPR_RASTERIZER) { - val_id = spirv_compiler_emit_uint_shader_parameter(compiler, + val_id = spirv_compiler_emit_shader_parameter(compiler, VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT); } else diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 25a9c649..3f618b51 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -619,6 +619,7 @@ enum vkd3d_shader_register_type VKD3DSPR_SSA, VKD3DSPR_WAVELANECOUNT, VKD3DSPR_WAVELANEINDEX, + VKD3DSPR_PARAMETER, VKD3DSPR_COUNT,