From 11475ef62a47a2e32a4404ea6653dffabdc60a38 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Thu, 20 Jul 2023 21:52:25 -0500 Subject: [PATCH] vkd3d-shader: Implement remapping shader output registers to match the next shader's semantics. --- include/vkd3d_shader.h | 76 +++++++++++++++++++++++- libs/vkd3d-shader/ir.c | 76 +++++++++++++++++++++++- libs/vkd3d-shader/spirv.c | 2 +- libs/vkd3d-shader/vkd3d_shader_private.h | 5 +- 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index d6653d18..1a4ad6f6 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -90,6 +90,11 @@ enum vkd3d_shader_structure_type * \since 1.9 */ VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO, + /** + * The structure is a vkd3d_shader_next_stage_info structure. + * \since 1.9 + */ + VKD3D_SHADER_STRUCTURE_TYPE_NEXT_STAGE_INFO, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_STRUCTURE_TYPE), }; @@ -1676,6 +1681,74 @@ struct vkd3d_shader_scan_signature_info struct vkd3d_shader_signature patch_constant; }; +/** + * Describes the mapping of a output varying register in a shader stage, + * to an input varying register in the following shader stage. + * + * This structure is used in struct vkd3d_shader_next_stage_info. + */ +struct vkd3d_shader_varying_map +{ + /** + * The signature index (in the output signature) of the output varying. + * If greater than or equal to the number of elements in the output + * signature, signifies that the varying is consumed by the next stage but + * not written by this one. + */ + unsigned int output_signature_index; + /** The register index of the input varying to map this register to. */ + unsigned int input_register_index; + /** The mask consumed by the destination register. */ + unsigned int input_mask; +}; + +/** + * A chained structure which describes the next shader in the pipeline. + * + * This structure is optional, and should only be provided if there is in fact + * another shader in the pipeline. + * However, depending on the input and output formats, this structure may be + * necessary in order to generate shaders which correctly match each other. + * If the structure or its individual fields are not provided, vkd3d-shader + * will generate shaders which may be correct in isolation, but are not + * guaranteed to correctly match each other. + * + * This structure is passed to vkd3d_shader_compile() and extends + * vkd3d_shader_compile_info. + * + * This structure contains only input parameters. + * + * \since 1.9 + */ +struct vkd3d_shader_next_stage_info +{ + /** Must be set to VKD3D_SHADER_STRUCTURE_TYPE_NEXT_STAGE_INFO. */ + enum vkd3d_shader_structure_type type; + /** Optional pointer to a structure containing further parameters. */ + const void *next; + + /** + * A mapping of output varyings in this shader stage to input varyings + * in the next shader stage. + * + * This mapping should include exactly one element for each varying + * consumed by the next shader stage. + * If this shader stage outputs a varying that is not consumed by the next + * shader stage, that varying should be absent from this array. + * + * If this field is absent, vkd3d-shader will map varyings from one stage + * to another based on their register index. + * For Direct3D shader model 3.0, such a default mapping will be incorrect + * unless the registers are allocated in the same order, and hence this + * field is necessary to correctly match inter-stage varyings. + * This mapping may also be necessary under other circumstances where the + * varying interface does not match exactly. + */ + const struct vkd3d_shader_varying_map *varying_map; + /** The number of registers provided in \ref varying_map. */ + unsigned int varying_count; +}; + #ifdef LIBVKD3D_SHADER_SOURCE # define VKD3D_SHADER_API VKD3D_EXPORT #else @@ -1748,13 +1821,14 @@ VKD3D_SHADER_API const enum vkd3d_shader_target_type *vkd3d_shader_get_supported * * Depending on the source and target types, this function may support the * following chained structures: + * - vkd3d_shader_hlsl_source_info * - vkd3d_shader_interface_info + * - vkd3d_shader_next_stage_info * - vkd3d_shader_scan_descriptor_info * - vkd3d_shader_scan_signature_info * - vkd3d_shader_spirv_domain_shader_target_info * - vkd3d_shader_spirv_target_info * - vkd3d_shader_transform_feedback_info - * - vkd3d_shader_hlsl_source_info * * \param compile_info A chained structure containing compilation parameters. * diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index d74f81af..0ade5a07 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -85,6 +85,75 @@ static void shader_instruction_eliminate_phase_instance_id(struct vkd3d_shader_i shader_register_eliminate_phase_addressing((struct vkd3d_shader_register *)&ins->dst[i].reg, instance_id); } +static const struct vkd3d_shader_varying_map *find_varying_map( + const struct vkd3d_shader_next_stage_info *next_stage, unsigned int signature_idx) +{ + unsigned int i; + + for (i = 0; i < next_stage->varying_count; ++i) + { + if (next_stage->varying_map[i].output_signature_index == signature_idx) + return &next_stage->varying_map[i]; + } + + return NULL; +} + +static enum vkd3d_result remap_output_signature(struct vkd3d_shader_parser *parser, + const struct vkd3d_shader_compile_info *compile_info) +{ + struct shader_signature *signature = &parser->shader_desc.output_signature; + const struct vkd3d_shader_next_stage_info *next_stage; + unsigned int i; + + if (!(next_stage = vkd3d_find_struct(compile_info->next, NEXT_STAGE_INFO))) + return VKD3D_OK; + + for (i = 0; i < signature->element_count; ++i) + { + const struct vkd3d_shader_varying_map *map = find_varying_map(next_stage, i); + struct signature_element *e = &signature->elements[i]; + + if (map) + { + unsigned int input_mask = map->input_mask; + + e->target_location = map->input_register_index; + + /* It is illegal in Vulkan if the next shader uses the same varying + * location with a different mask. */ + if (input_mask && input_mask != e->mask) + { + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Aborting due to not yet implemented feature: " + "Output mask %#x does not match input mask %#x.", + e->mask, input_mask); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + } + else + { + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Aborting due to not yet implemented feature: " + "This stage outputs varyings not consumed by the next stage."); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + } + + for (i = 0; i < next_stage->varying_count; ++i) + { + if (next_stage->varying_map[i].output_signature_index >= signature->element_count) + { + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Aborting due to not yet implemented feature: " + "The next stage consumes varyings not written by this stage."); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + } + + return VKD3D_OK; +} + struct hull_flattener { struct vkd3d_shader_instruction_array instructions; @@ -1194,7 +1263,8 @@ static enum vkd3d_result instruction_array_normalise_flat_constants(struct vkd3d return VKD3D_OK; } -enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser) +enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, + const struct vkd3d_shader_compile_info *compile_info) { struct vkd3d_shader_instruction_array *instructions = &parser->instructions; enum vkd3d_result result = VKD3D_OK; @@ -1202,6 +1272,10 @@ enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser) if (parser->shader_desc.is_dxil) return result; + if (parser->shader_version.type != VKD3D_SHADER_TYPE_PIXEL + && (result = remap_output_signature(parser, compile_info)) < 0) + return result; + if (parser->shader_version.type == VKD3D_SHADER_TYPE_HULL && (result = instruction_array_flatten_hull_shader_phases(instructions)) >= 0) { diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 27d95912..28825256 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -9542,7 +9542,7 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, compiler->location.column = 0; compiler->location.line = 1; - if ((result = vkd3d_shader_normalise(parser)) < 0) + if ((result = vkd3d_shader_normalise(parser, compile_info)) < 0) return result; instructions = parser->instructions; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index d2a2ffe5..188fe460 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -168,6 +168,8 @@ enum vkd3d_shader_error VKD3D_SHADER_WARNING_DXIL_INVALID_BLOCK_LENGTH = 8302, VKD3D_SHADER_WARNING_DXIL_INVALID_MODULE_LENGTH = 8303, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS = 8304, + + VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED = 9000, }; enum vkd3d_shader_opcode @@ -1409,6 +1411,7 @@ void dxbc_writer_add_section(struct dxbc_writer *dxbc, uint32_t tag, const void void dxbc_writer_init(struct dxbc_writer *dxbc); int dxbc_writer_write(struct dxbc_writer *dxbc, struct vkd3d_shader_code *code); -enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser); +enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, + const struct vkd3d_shader_compile_info *compile_info); #endif /* __VKD3D_SHADER_PRIVATE_H */