From 20190a138875e806c28f7b4bd7346f5c7cf43471 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Thu, 23 Feb 2023 16:21:18 -0600 Subject: [PATCH] vkd3d-shader: Introduce an API to retrieve all signatures from DXBC shaders. --- include/vkd3d_shader.h | 69 +++++++++- libs/vkd3d-shader/vkd3d_shader.map | 1 + libs/vkd3d-shader/vkd3d_shader_main.c | 116 +++++++++++----- tests/vkd3d_shader_api.c | 187 ++++++++++++++++++++++++++ 4 files changed, 335 insertions(+), 38 deletions(-) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 33e51384..ff6101f6 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -85,6 +85,11 @@ enum vkd3d_shader_structure_type * \since 1.3 */ VKD3D_SHADER_STRUCTURE_TYPE_DESCRIPTOR_OFFSET_INFO, + /** + * The structure is a vkd3d_shader_scan_signature_info structure. + * \since 1.9 + */ + VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_STRUCTURE_TYPE), }; @@ -1551,6 +1556,44 @@ static inline uint32_t vkd3d_shader_create_swizzle(enum vkd3d_shader_swizzle_com | ((w & VKD3D_SHADER_SWIZZLE_MASK) << VKD3D_SHADER_SWIZZLE_SHIFT(3)); } +/** + * A chained structure containing descriptions of shader inputs and outputs. + * + * This structure is currently implemented only for DXBC source types. For DXBC + * shaders, the returned information is parsed directly from the signatures + * embedded in the DXBC shader. For all other shader types, the structure is + * zeroed. + * + * All members (except for \ref type and \ref next) are output-only. + * + * This structure is passed to vkd3d_shader_scan() and extends + * vkd3d_shader_compile_info. + * + * Members of this structure are allocated by vkd3d-shader and should be freed + * with vkd3d_shader_free_scan_signature_info() when no longer needed. + * + * All signatures may contain pointers into the input shader, and should only + * be accessed while the input shader remains valid. + * + * \since 1.9 + */ +struct vkd3d_shader_scan_signature_info +{ + /** Must be set to VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO. */ + enum vkd3d_shader_structure_type type; + /** Optional pointer to a structure containing further parameters. */ + const void *next; + + /** The shader input varyings. */ + struct vkd3d_shader_signature input; + + /** The shader output varyings. */ + struct vkd3d_shader_signature output; + + /** The shader patch constant varyings. */ + struct vkd3d_shader_signature patch_constant; +}; + #ifdef LIBVKD3D_SHADER_SOURCE # define VKD3D_SHADER_API VKD3D_EXPORT #else @@ -1625,6 +1668,7 @@ VKD3D_SHADER_API const enum vkd3d_shader_target_type *vkd3d_shader_get_supported * following chained structures: * - vkd3d_shader_interface_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 @@ -1811,6 +1855,7 @@ VKD3D_SHADER_API int vkd3d_shader_convert_root_signature(struct vkd3d_shader_ver * \n * The DXBC_TPF scanner supports the following chained structures: * - vkd3d_shader_scan_descriptor_info + * - vkd3d_shader_scan_signature_info * \n * Although the \a compile_info parameter is read-only, chained structures * passed to this function need not be, and may serve as output parameters, @@ -1847,12 +1892,18 @@ VKD3D_SHADER_API void vkd3d_shader_free_scan_descriptor_info( struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info); /** - * Read the input signature of a compiled shader, returning a structural + * Read the input signature of a compiled DXBC shader, returning a structural * description which can be easily parsed by C code. * * This function parses a compiled shader. To parse a standalone root signature, * use vkd3d_shader_parse_root_signature(). * + * This function only parses DXBC shaders, and only retrieves the input + * signature. To retrieve signatures from other shader types, or other signature + * types, use vkd3d_shader_scan() and struct vkd3d_shader_scan_signature_info. + * This function returns the same input signature that is returned in + * struct vkd3d_shader_scan_signature_info. + * * \param dxbc Compiled byte code, in DXBC format. * * \param signature Output location in which the parsed root signature will be @@ -2042,6 +2093,19 @@ VKD3D_SHADER_API int vkd3d_shader_parse_dxbc(const struct vkd3d_shader_code *dxb VKD3D_SHADER_API int vkd3d_shader_serialize_dxbc(size_t section_count, const struct vkd3d_shader_dxbc_section_desc *sections, struct vkd3d_shader_code *dxbc, char **messages); +/** + * Free members of struct vkd3d_shader_scan_signature_info allocated by + * vkd3d_shader_scan(). + * + * This function may free members of vkd3d_shader_scan_signature_info, but + * does not free the structure itself. + * + * \param info Scan information to free. + * + * \since 1.9 + */ +VKD3D_SHADER_API void vkd3d_shader_free_scan_signature_info(struct vkd3d_shader_scan_signature_info *info); + #endif /* VKD3D_SHADER_NO_PROTOTYPES */ /** Type of vkd3d_shader_get_version(). */ @@ -2107,6 +2171,9 @@ typedef int (*PFN_vkd3d_shader_parse_dxbc)(const struct vkd3d_shader_code *dxbc, typedef int (*PFN_vkd3d_shader_serialize_dxbc)(size_t section_count, const struct vkd3d_shader_dxbc_section_desc *sections, struct vkd3d_shader_code *dxbc, char **messages); +/** Type of vkd3d_shader_free_scan_signature_info(). \since 1.9 */ +typedef void (*PFN_vkd3d_shader_free_scan_signature_info)(struct vkd3d_shader_scan_signature_info *info); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/libs/vkd3d-shader/vkd3d_shader.map b/libs/vkd3d-shader/vkd3d_shader.map index 6afc526a..8a596a80 100644 --- a/libs/vkd3d-shader/vkd3d_shader.map +++ b/libs/vkd3d-shader/vkd3d_shader.map @@ -8,6 +8,7 @@ global: vkd3d_shader_free_messages; vkd3d_shader_free_root_signature; vkd3d_shader_free_scan_descriptor_info; + vkd3d_shader_free_scan_signature_info; vkd3d_shader_free_shader_code; vkd3d_shader_free_shader_signature; vkd3d_shader_get_supported_source_types; diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 1fef3216..8c15f1ca 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -440,6 +440,18 @@ void vkd3d_shader_dump_shader(enum vkd3d_shader_source_type source_type, shader_get_source_type_suffix(source_type), shader->code, shader->size); } +static void init_scan_signature_info(const struct vkd3d_shader_compile_info *info) +{ + struct vkd3d_shader_scan_signature_info *signature_info; + + if ((signature_info = vkd3d_find_struct(info->next, SCAN_SIGNATURE_INFO))) + { + memset(&signature_info->input, 0, sizeof(signature_info->input)); + memset(&signature_info->output, 0, sizeof(signature_info->output)); + memset(&signature_info->patch_constant, 0, sizeof(signature_info->patch_constant)); + } +} + bool vkd3d_shader_parser_init(struct vkd3d_shader_parser *parser, struct vkd3d_shader_message_context *message_context, const char *source_name, const struct vkd3d_shader_version *version, const struct vkd3d_shader_parser_ops *ops, @@ -526,6 +538,43 @@ void vkd3d_shader_free_messages(char *messages) vkd3d_free(messages); } +static bool vkd3d_shader_signature_from_shader_signature(struct vkd3d_shader_signature *signature, + const struct shader_signature *src) +{ + unsigned int i; + + signature->element_count = src->element_count; + if (!src->elements) + { + assert(!signature->element_count); + signature->elements = NULL; + return true; + } + + if (!(signature->elements = vkd3d_calloc(signature->element_count, sizeof(*signature->elements)))) + return false; + + for (i = 0; i < signature->element_count; ++i) + { + struct vkd3d_shader_signature_element *d = &signature->elements[i]; + struct signature_element *e = &src->elements[i]; + + d->semantic_name = e->semantic_name; + d->semantic_index = e->semantic_index; + d->stream_index = e->stream_index; + d->sysval_semantic = e->sysval_semantic; + d->component_type = e->component_type; + d->register_index = e->register_index; + if (e->register_count > 1) + FIXME("Arrayed elements are not supported yet.\n"); + d->mask = e->mask; + d->used_mask = e->used_mask; + d->min_precision = e->min_precision; + } + + return true; +} + struct vkd3d_shader_scan_context { struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info; @@ -1070,6 +1119,7 @@ static int scan_with_parser(const struct vkd3d_shader_compile_info *compile_info struct vkd3d_shader_message_context *message_context, struct vkd3d_shader_parser *parser) { struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info; + struct vkd3d_shader_scan_signature_info *signature_info; struct vkd3d_shader_instruction *instruction; struct vkd3d_shader_scan_context context; int ret = VKD3D_OK; @@ -1080,6 +1130,7 @@ static int scan_with_parser(const struct vkd3d_shader_compile_info *compile_info scan_descriptor_info->descriptors = NULL; scan_descriptor_info->descriptor_count = 0; } + signature_info = vkd3d_find_struct(compile_info->next, SCAN_SIGNATURE_INFO); vkd3d_shader_scan_context_init(&context, compile_info, scan_descriptor_info, message_context); @@ -1099,6 +1150,21 @@ static int scan_with_parser(const struct vkd3d_shader_compile_info *compile_info } } + if (!ret && signature_info) + { + if (!vkd3d_shader_signature_from_shader_signature(&signature_info->input, &parser->shader_desc.input_signature) + || !vkd3d_shader_signature_from_shader_signature(&signature_info->output, + &parser->shader_desc.output_signature) + || !vkd3d_shader_signature_from_shader_signature(&signature_info->patch_constant, + &parser->shader_desc.patch_constant_signature)) + { + vkd3d_shader_free_scan_signature_info(signature_info); + if (scan_descriptor_info) + vkd3d_shader_free_scan_descriptor_info(scan_descriptor_info); + ret = VKD3D_ERROR_OUT_OF_MEMORY; + } + } + vkd3d_shader_scan_context_cleanup(&context); return ret; } @@ -1152,6 +1218,8 @@ int vkd3d_shader_scan(const struct vkd3d_shader_compile_info *compile_info, char if ((ret = vkd3d_shader_validate_compile_info(compile_info, false)) < 0) return ret; + init_scan_signature_info(compile_info); + vkd3d_shader_message_context_init(&message_context, compile_info->log_level); switch (compile_info->source_type) @@ -1305,6 +1373,8 @@ int vkd3d_shader_compile(const struct vkd3d_shader_compile_info *compile_info, if ((ret = vkd3d_shader_validate_compile_info(compile_info, true)) < 0) return ret; + init_scan_signature_info(compile_info); + vkd3d_shader_message_context_init(&message_context, compile_info->log_level); switch (compile_info->source_type) @@ -1339,6 +1409,15 @@ void vkd3d_shader_free_scan_descriptor_info(struct vkd3d_shader_scan_descriptor_ vkd3d_free(scan_descriptor_info->descriptors); } +void vkd3d_shader_free_scan_signature_info(struct vkd3d_shader_scan_signature_info *info) +{ + TRACE("info %p.\n", info); + + vkd3d_shader_free_shader_signature(&info->input); + vkd3d_shader_free_shader_signature(&info->output); + vkd3d_shader_free_shader_signature(&info->patch_constant); +} + void vkd3d_shader_free_shader_code(struct vkd3d_shader_code *shader_code) { TRACE("shader_code %p.\n", shader_code); @@ -1401,43 +1480,6 @@ void vkd3d_shader_free_root_signature(struct vkd3d_shader_versioned_root_signatu desc->version = 0; } -static bool vkd3d_shader_signature_from_shader_signature(struct vkd3d_shader_signature *signature, - const struct shader_signature *src) -{ - unsigned int i; - - signature->element_count = src->element_count; - if (!src->elements) - { - assert(!signature->element_count); - signature->elements = NULL; - return true; - } - - if (!(signature->elements = vkd3d_calloc(signature->element_count, sizeof(*signature->elements)))) - return false; - - for (i = 0; i < signature->element_count; ++i) - { - struct vkd3d_shader_signature_element *d = &signature->elements[i]; - struct signature_element *e = &src->elements[i]; - - d->semantic_name = e->semantic_name; - d->semantic_index = e->semantic_index; - d->stream_index = e->stream_index; - d->sysval_semantic = e->sysval_semantic; - d->component_type = e->component_type; - d->register_index = e->register_index; - if (e->register_count > 1) - FIXME("Arrayed elements are not supported yet.\n"); - d->mask = e->mask; - d->used_mask = e->used_mask; - d->min_precision = e->min_precision; - } - - return true; -} - void shader_signature_cleanup(struct shader_signature *signature) { vkd3d_free(signature->elements); diff --git a/tests/vkd3d_shader_api.c b/tests/vkd3d_shader_api.c index 54e5d5ba..318e5068 100644 --- a/tests/vkd3d_shader_api.c +++ b/tests/vkd3d_shader_api.c @@ -401,6 +401,192 @@ static void test_dxbc(void) vkd3d_shader_free_shader_code(&dxbc); } +static void check_signature_element(const struct vkd3d_shader_signature_element *element, + const struct vkd3d_shader_signature_element *expect) +{ + ok(!strcmp(element->semantic_name, expect->semantic_name), "Got semantic name %s.\n", element->semantic_name); + ok(element->semantic_index == expect->semantic_index, "Got semantic index %u.\n", element->semantic_index); + ok(element->stream_index == expect->stream_index, "Got stream index %u.\n", element->stream_index); + ok(element->sysval_semantic == expect->sysval_semantic, "Got sysval semantic %#x.\n", element->sysval_semantic); + ok(element->component_type == expect->component_type, "Got component type %#x.\n", element->component_type); + ok(element->register_index == expect->register_index, "Got register index %u.\n", element->register_index); + ok(element->mask == expect->mask, "Got mask %#x.\n", element->mask); + todo_if (expect->used_mask != expect->mask) + ok(element->used_mask == expect->used_mask, "Got used mask %#x.\n", element->used_mask); + ok(element->min_precision == expect->min_precision, "Got minimum precision %#x.\n", element->min_precision); +} + +static void test_scan_signatures(void) +{ + struct vkd3d_shader_scan_signature_info signature_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO}; + struct vkd3d_shader_hlsl_source_info hlsl_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_HLSL_SOURCE_INFO}; + struct vkd3d_shader_compile_info compile_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO}; + struct vkd3d_shader_code dxbc; + size_t i, j; + int rc; + + static const char vs1_source[] = + "void main(\n" + " in float4 a : apple,\n" + " out float4 b : banana2,\n" + " inout float4 c : color,\n" + " inout float4 d : depth,\n" + " inout float4 e : sv_position,\n" + " in uint3 f : fruit,\n" + " inout bool2 g : grape,\n" + " in int h : honeydew,\n" + " in uint i : sv_vertexid)\n" + "{\n" + " b.yw = a.xz;\n" + "}"; + + static const struct vkd3d_shader_signature_element vs1_inputs[] = + { + {"apple", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0xf, 0x5}, + {"color", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1, 0xf, 0xf}, + {"depth", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2, 0xf, 0xf}, + {"sv_position", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 3, 0xf, 0xf}, + {"fruit", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_UINT, 4, 0x7}, + {"grape", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_UINT, 5, 0x3, 0x3}, + {"honeydew", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_INT, 6, 0x1}, + {"sv_vertexid", 0, 0, VKD3D_SHADER_SV_VERTEX_ID, VKD3D_SHADER_COMPONENT_UINT, 7, 0x1}, + }; + + static const struct vkd3d_shader_signature_element vs1_outputs[] = + { + {"banana", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0xf, 0xa}, + {"color", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1, 0xf, 0xf}, + {"depth", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2, 0xf, 0xf}, + {"sv_position", 0, 0, VKD3D_SHADER_SV_POSITION, VKD3D_SHADER_COMPONENT_FLOAT, 3, 0xf, 0xf}, + {"grape", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_UINT, 4, 0x3, 0x3}, + }; + + static const char vs2_source[] = + "void main(inout float4 pos : position)\n" + "{\n" + "}"; + + static const struct vkd3d_shader_signature_element vs2_inputs[] = + { + {"position", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0xf, 0xf}, + }; + + static const struct vkd3d_shader_signature_element vs2_outputs[] = + { + {"position", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0xf, 0xf}, + }; + + static const char ps1_source[] = + "void main(\n" + " in float2 a : apple,\n" + " out float4 b : sv_target2,\n" + " out float c : sv_depth,\n" + " in float4 d : position,\n" + " in float4 e : sv_position)\n" + "{\n" + " b = d;\n" + " c = 0;\n" + "}"; + + static const struct vkd3d_shader_signature_element ps1_inputs[] = + { + {"apple", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0x3}, + {"position", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1, 0xf, 0xf}, + {"sv_position", 0, 0, VKD3D_SHADER_SV_POSITION, VKD3D_SHADER_COMPONENT_FLOAT, 2, 0xf}, + }; + + static const struct vkd3d_shader_signature_element ps1_outputs[] = + { + {"sv_target", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2, 0xf, 0xf}, + {"sv_depth", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, ~0u, 0x1, 0x1}, + }; + + static const char cs1_source[] = + "[numthreads(1, 1, 1)]\n" + "void main(\n" + " in uint a : sv_dispatchthreadid,\n" + " in uint b : sv_groupid,\n" + " in uint c : sv_groupthreadid)\n" + "{\n" + "}"; + + static const struct + { + const char *source; + const char *profile; + const struct vkd3d_shader_signature_element *inputs; + size_t input_count; + const struct vkd3d_shader_signature_element *outputs; + size_t output_count; + const struct vkd3d_shader_signature_element *patch_constants; + size_t patch_constant_count; + } + tests[] = + { + {vs1_source, "vs_4_0", vs1_inputs, ARRAY_SIZE(vs1_inputs), vs1_outputs, ARRAY_SIZE(vs1_outputs)}, + {vs2_source, "vs_4_0", vs2_inputs, ARRAY_SIZE(vs2_inputs), vs2_outputs, ARRAY_SIZE(vs2_outputs)}, + {ps1_source, "ps_4_0", ps1_inputs, ARRAY_SIZE(ps1_inputs), ps1_outputs, ARRAY_SIZE(ps1_outputs)}, + {cs1_source, "cs_5_0", NULL, 0, NULL, 0}, + }; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + vkd3d_test_push_context("test %u", i); + + compile_info.source.code = tests[i].source; + compile_info.source.size = strlen(tests[i].source); + compile_info.source_type = VKD3D_SHADER_SOURCE_HLSL; + compile_info.target_type = VKD3D_SHADER_TARGET_DXBC_TPF; + compile_info.log_level = VKD3D_SHADER_LOG_INFO; + + compile_info.next = &hlsl_info; + hlsl_info.profile = tests[i].profile; + + rc = vkd3d_shader_compile(&compile_info, &dxbc, NULL); + ok(rc == VKD3D_OK, "Got unexpected error code %d.\n", rc); + + compile_info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF; + compile_info.source = dxbc; + + compile_info.next = &signature_info; + + rc = vkd3d_shader_scan(&compile_info, NULL); + ok(rc == VKD3D_OK, "Got unexpected error code %d.\n", rc); + + ok(signature_info.input.element_count == tests[i].input_count, + "Got input count %u.\n", signature_info.input.element_count); + for (j = 0; j < signature_info.input.element_count; ++j) + { + vkd3d_test_push_context("input %u", j); + check_signature_element(&signature_info.input.elements[j], &tests[i].inputs[j]); + vkd3d_test_pop_context(); + } + + ok(signature_info.output.element_count == tests[i].output_count, + "Got output count %u.\n", signature_info.output.element_count); + for (j = 0; j < signature_info.output.element_count; ++j) + { + vkd3d_test_push_context("output %u", j); + check_signature_element(&signature_info.output.elements[j], &tests[i].outputs[j]); + vkd3d_test_pop_context(); + } + + ok(signature_info.patch_constant.element_count == tests[i].patch_constant_count, + "Got patch constant count %u.\n", signature_info.patch_constant.element_count); + for (j = 0; j < signature_info.patch_constant.element_count; ++j) + { + vkd3d_test_push_context("patch constant %u", j); + check_signature_element(&signature_info.patch_constant.elements[j], &tests[i].patch_constants[j]); + vkd3d_test_pop_context(); + } + + vkd3d_shader_free_scan_signature_info(&signature_info); + vkd3d_shader_free_shader_code(&dxbc); + + vkd3d_test_pop_context(); + } +} + START_TEST(vkd3d_shader_api) { setlocale(LC_ALL, ""); @@ -410,4 +596,5 @@ START_TEST(vkd3d_shader_api) run_test(test_version); run_test(test_d3dbc); run_test(test_dxbc); + run_test(test_scan_signatures); }