There are two parts to this:
- First, a way to retrieve any signature from a DXBC shader. This is, I gather, generally useful for reflection, and it can be used as one source with which to implement d3d10 and d3d11 shader reflection APIs.
The rest of those APIs will need much more data to be exposed from d3d shaders, and while I was originally planning to expose that all in a single "vkd3d_shader_d3d_shader_info" structure, I think that signatures at least are a reasonable enough subset to have a dedicated structure. Moreover, I did not want to block sm1 support on too much API design.
- Second, signatures synthesized from sm1 byte code. This is conceptually a bit weird, because sm1 does not have signatures, but in terms of how these APIs are used by Wine (or other translation layers, as evidenced not least by the Vulkan test shader runner, which I have locally adapted for sm1 but not submitted yet) it fits rather nicely.
Because this is new API, it of course deserves plenty of discussion, especially the sm1 part. Several open questions which occurred to me while writing are:
1. Should we fix the mask (and used mask) for sm1 signatures as 0xf rather than 0? SPIR-V cares about this in order to declare I/O variables, which makes some amount of sense. In fact I have a patch in my local tree to change this, specifically for that purpose. However, we could also normalize it later.
2. If we do fix the mask as nonzero, should single-component semantics (fog, depth, etc...) be declared as 0x1 instead of 0xf?
3. Should BLENDINDICES get a UINT type? It's float in shader instructions (well, kind of, although in practice it's used as an array index of course), but the vertex attribute type is in practice "supposed" to be integer.
Part 1 of a series to implement sm1 -> spirv translation in vkd3d-shader, brought to you by late nights spent coding and rereading The Waste Land.
Ganga was sunken, and the limp leaves Waited for rain, while the black clouds Gathered far distant, over Himavant.
From: Zebediah Figura zfigura@codeweavers.com
--- include/vkd3d_shader.h | 71 ++++++++++ libs/vkd3d-shader/vkd3d_shader.map | 1 + libs/vkd3d-shader/vkd3d_shader_main.c | 33 +++++ tests/vkd3d_shader_api.c | 187 ++++++++++++++++++++++++++ 4 files changed, 292 insertions(+)
diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 859b8c79..5bc87945 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -84,6 +84,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.x + */ + VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO,
VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_STRUCTURE_TYPE), }; @@ -1550,6 +1555,54 @@ 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 signatures scanned from a DXBC shader. + * + * 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. + * + * \since 1.x + */ +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 parsed input signature embedded in a DXBC shader. + * The structure is zeroed for any other type of shader. + * + * The signature may contain pointers into the input shader, and should + * only be accessed while the input shader remains valid. + */ + struct vkd3d_shader_signature input; + + /** + * The parsed output signature embedded in a DXBC shader. + * The structure is zeroed for any other type of shader. + * + * The signature may contain pointers into the input shader, and should + * only be accessed while the input shader remains valid. + */ + struct vkd3d_shader_signature output; + + /** + * The parsed patch constant signature embedded in a DXBC hull shader. + * The structure is zeroed for any other type of shader. + * + * The signature may contain pointers into the input shader, and should + * only be accessed while the input shader remains valid. + */ + struct vkd3d_shader_signature patch_constant; +}; + #ifdef LIBVKD3D_SHADER_SOURCE # define VKD3D_SHADER_API VKD3D_EXPORT #else @@ -1624,6 +1677,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 @@ -1790,6 +1844,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, @@ -2021,6 +2076,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.x + */ +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(). */ @@ -2086,6 +2154,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.x */ +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 409d759c..058b8981 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -24,6 +24,9 @@
VKD3D_DEBUG_ENV_NAME("VKD3D_SHADER_DEBUG");
+static bool vkd3d_shader_signature_from_shader_signature(struct vkd3d_shader_signature *signature, + const struct shader_signature *src); + static inline int char_to_int(char c) { if ('0' <= c && c <= '9') @@ -1070,6 +1073,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 +1084,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 +1104,25 @@ static int scan_with_parser(const struct vkd3d_shader_compile_info *compile_info } }
+ if (!ret && 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)); + + 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; } @@ -1339,6 +1363,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); 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); }
From: Zebediah Figura zfigura@codeweavers.com
--- include/vkd3d_shader.h | 40 ++++- libs/vkd3d-shader/d3dbc.c | 205 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + tests/vkd3d_shader_api.c | 153 ++++++++++++++++- 4 files changed, 392 insertions(+), 7 deletions(-)
diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 5bc87945..01e3475e 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -1556,7 +1556,8 @@ static inline uint32_t vkd3d_shader_create_swizzle(enum vkd3d_shader_swizzle_com }
/** - * A chained structure containing signatures scanned from a DXBC shader. + * A chained structure containing signatures scanned from a DXBC shader, or + * synthesized from legacy Direct3D bytecode instructions. * * All members (except for \ref type and \ref next) are output-only. * @@ -1566,6 +1567,43 @@ static inline uint32_t vkd3d_shader_create_swizzle(enum vkd3d_shader_swizzle_com * Members of this structure are allocated by vkd3d-shader and should be freed * with vkd3d_shader_free_scan_signature_info() when no longer needed. * + * Signature elements are synthesized from legacy Direct3D bytecode as follows: + * - The \ref vkd3d_shader_signature_element.semantic_name field is set to an + * uppercase string corresponding to the HLSL name for the usage, e.g. + * "POSITION", "BLENDWEIGHT", "COLOR", etc. + * - The \ref vkd3d_shader_signature_element.semantic_index field is set to the + * usage index. + * - The \ref vkd3d_shader_signature_element.stream_index is always 0. + * - The \ref vkd3d_shader_signature_element.sysval_semantic field is set as + * follows: + * - For position outputs from vertex shaders, it is set to + * VKD3D_SHADER_SV_POSITION. + * - For shader model 3 pixel position inputs (i.e. VPOS, but not POSITION), + * it is set to VKD3D_SHADER_SV_POSITION. + * - For shader model 3 face inputs (i.e. VFACE), it is set to + * VKD3D_SHADER_SV_IS_FRONT_FACE. + * - For all other elements, it is set to VKD3D_SHADER_SV_NONE. + * - The \ref vkd3d_shader_signature_element.component_type field is always set + * to VKD3D_SHADER_COMPONENT_FLOAT. + * - The \ref vkd3d_shader_signature_element.register_index field is set to the + * bytecode register index. + * Note that for shader model 1 and 2 shaders (excepting vertex shader + * inputs), the register index of colour and texture coordinate registers will + * be equal to the usage index, and hence will not be unique. + * - The \ref vkd3d_shader_signature_element.mask field is always 0. + * - The \ref vkd3d_shader_signature_element.used_mask field is always 0. + * - The \ref vkd3d_shader_signature_element.min_precision field is always + * VKD3D_SHADER_MINIMUM_PRECISION_NONE. + * + * Signature elements are synthesized for any input or output register declared + * or used in a legacy Direct3D bytecode shader, including the following: + * - Shader model 1 and 2 colour and texture coordinate registers. + * - The shader model 1 pixel shader output register. + * - Shader model 1 and 2 vertex shader output registers (position, fog, and + * point size). + * - Shader model 3 pixel shader system value input registers (pixel position + * and face). + * * \since 1.x */ struct vkd3d_shader_scan_signature_info diff --git a/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d-shader/d3dbc.c index 823df6f6..7b72d077 100644 --- a/libs/vkd3d-shader/d3dbc.c +++ b/libs/vkd3d-shader/d3dbc.c @@ -214,6 +214,8 @@ struct vkd3d_shader_sm1_parser bool abort;
struct vkd3d_shader_parser p; + + size_t input_signature_capacity, output_signature_capacity; };
/* This table is not order or position dependent. */ @@ -488,6 +490,201 @@ static void shader_sm1_parse_dst_param(uint32_t param, const struct vkd3d_shader dst->shift = (param & VKD3D_SM1_DSTSHIFT_MASK) >> VKD3D_SM1_DSTSHIFT_SHIFT; }
+static struct signature_element *find_signature_element(const struct shader_signature *signature, + const char *semantic_name, unsigned int semantic_index) +{ + struct signature_element *e; + unsigned int i; + + e = signature->elements; + for (i = 0; i < signature->element_count; ++i) + { + if (!ascii_strcasecmp(e[i].semantic_name, semantic_name) + && e[i].semantic_index == semantic_index) + return &e[i]; + } + + return NULL; +} + +static bool add_signature_element(struct shader_signature *signature, const char *name, + unsigned int index, enum vkd3d_shader_sysval_semantic sysval, unsigned int register_index) +{ + struct signature_element *element; + + if (find_signature_element(signature, name, index)) + return true; + + if (!vkd3d_array_reserve((void **)&signature->elements, &signature->elements_capacity, + signature->element_count + 1, sizeof(*signature->elements))) + return false; + element = &signature->elements[signature->element_count++]; + + element->semantic_name = name; + element->semantic_index = index; + element->stream_index = 0; + element->sysval_semantic = sysval; + element->component_type = VKD3D_SHADER_COMPONENT_FLOAT; + element->register_index = register_index; + element->mask = 0; + element->used_mask = 0; + element->min_precision = VKD3D_SHADER_MINIMUM_PRECISION_NONE; + + return true; +} + +static bool shader_sm1_add_signature_element(struct vkd3d_shader_sm1_parser *sm1, bool output, const char *name, + unsigned int index, enum vkd3d_shader_sysval_semantic sysval, unsigned int register_index) +{ + if (output) + return add_signature_element(&sm1->p.shader_desc.output_signature, name, index, sysval, register_index); + else + return add_signature_element(&sm1->p.shader_desc.input_signature, name, index, sysval, register_index); +} + +static bool shader_sm1_add_signature_element_from_register(struct vkd3d_shader_sm1_parser *sm1, + const struct vkd3d_shader_register *reg) +{ + unsigned int register_index = reg->idx[0].offset; + + switch (reg->type) + { + case VKD3DSPR_CONST: + return true; + + case VKD3DSPR_TEMP: + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_PIXEL + && sm1->p.shader_version.major == 1 && !register_index) + return shader_sm1_add_signature_element(sm1, true, "COLOR", 0, VKD3D_SHADER_SV_NONE, 0); + return true; + + case VKD3DSPR_INPUT: + /* For vertex shaders or sm3 pixel shaders, we should have already + * had a DCL instruction. Otherwise, this is a colour input. */ + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_VERTEX || sm1->p.shader_version.major >= 3) + return true; + return shader_sm1_add_signature_element(sm1, false, "COLOR", + register_index, VKD3D_SHADER_SV_NONE, register_index); + + case VKD3DSPR_TEXTURE: + /* For vertex shaders, this is ADDR. */ + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_VERTEX) + return true; + return shader_sm1_add_signature_element(sm1, false, "TEXCOORD", + register_index, VKD3D_SHADER_SV_NONE, register_index); + + case VKD3DSPR_OUTPUT: + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_VERTEX) + { + /* For sm < 2 vertex shaders, this is TEXCRDOUT. + * + * For sm3 vertex shaders, this is OUTPUT, but we already + * should have had a DCL instruction. */ + if (sm1->p.shader_version.major >= 3) + return true; + return shader_sm1_add_signature_element(sm1, true, "TEXCOORD", + register_index, VKD3D_SHADER_SV_NONE, register_index); + } + /* fall through */ + + case VKD3DSPR_ATTROUT: + case VKD3DSPR_COLOROUT: + return shader_sm1_add_signature_element(sm1, true, "COLOR", + register_index, VKD3D_SHADER_SV_NONE, register_index); + + case VKD3DSPR_DEPTHOUT: + return shader_sm1_add_signature_element(sm1, true, "DEPTH", + 0, VKD3D_SHADER_SV_NONE, register_index); + + case VKD3DSPR_RASTOUT: + switch (register_index) + { + case 0: + return shader_sm1_add_signature_element(sm1, true, "POSITION", + 0, VKD3D_SHADER_SV_POSITION, register_index); + + case 1: + return shader_sm1_add_signature_element(sm1, true, "FOG", + 0, VKD3D_SHADER_SV_NONE, register_index); + + case 2: + return shader_sm1_add_signature_element(sm1, true, "PSIZE", + 0, VKD3D_SHADER_SV_NONE, register_index); + + default: + FIXME("Unexpected rasterizer output %#x.\n", register_index); + return true; + } + + case VKD3DSPR_MISCTYPE: + switch (register_index) + { + case 0: + return shader_sm1_add_signature_element(sm1, false, "VPOS", + 0, VKD3D_SHADER_SV_POSITION, register_index); + + case 1: + return shader_sm1_add_signature_element(sm1, false, "VFACE", + 0, VKD3D_SHADER_SV_IS_FRONT_FACE, register_index); + + default: + FIXME("Unexpected rasterizer output %#x.\n", register_index); + return true; + } + + default: + FIXME("Unhandled register type %#x.\n", reg->type); + return true; + } +} + +static bool shader_sm1_add_signature_element_from_semantic(struct vkd3d_shader_sm1_parser *sm1, + const struct vkd3d_shader_semantic *semantic) +{ + const struct vkd3d_shader_register *reg = &semantic->resource.reg.reg; + enum vkd3d_shader_sysval_semantic sysval = VKD3D_SHADER_SV_NONE; + bool output; + + static const char sm1_semantic_names[][13] = + { + [VKD3D_DECL_USAGE_POSITION ] = "POSITION", + [VKD3D_DECL_USAGE_BLEND_WEIGHT ] = "BLENDWEIGHT", + [VKD3D_DECL_USAGE_BLEND_INDICES] = "BLENDINDICES", + [VKD3D_DECL_USAGE_NORMAL ] = "NORMAL", + [VKD3D_DECL_USAGE_PSIZE ] = "PSIZE", + [VKD3D_DECL_USAGE_TEXCOORD ] = "TEXCOORD", + [VKD3D_DECL_USAGE_TANGENT ] = "TANGENT", + [VKD3D_DECL_USAGE_BINORMAL ] = "BINORMAL", + [VKD3D_DECL_USAGE_TESS_FACTOR ] = "TESSFACTOR", + [VKD3D_DECL_USAGE_POSITIONT ] = "POSITIONT", + [VKD3D_DECL_USAGE_COLOR ] = "COLOR", + [VKD3D_DECL_USAGE_FOG ] = "FOG", + [VKD3D_DECL_USAGE_DEPTH ] = "DEPTH", + [VKD3D_DECL_USAGE_SAMPLE ] = "SAMPLE", + }; + + if (reg->type == VKD3DSPR_OUTPUT) + output = true; + else if (reg->type == VKD3DSPR_INPUT || reg->type == VKD3DSPR_TEXTURE) + output = false; + else /* vpos and vface don't have a semantic. */ + return shader_sm1_add_signature_element_from_register(sm1, reg); + + /* sm2 pixel shaders use DCL but don't provide a semantic. */ + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_PIXEL && sm1->p.shader_version.major == 2) + return shader_sm1_add_signature_element_from_register(sm1, reg); + + /* With the exception of vertex POSITION output, none of these are system + * values. Pixel POSITION input is not equivalent to SV_Position; the closer + * equivalent is VPOS, which is not declared as a semantic. */ + if (sm1->p.shader_version.type == VKD3D_SHADER_TYPE_VERTEX + && output && semantic->usage == VKD3D_DECL_USAGE_POSITION) + sysval = VKD3D_SHADER_SV_POSITION; + + return shader_sm1_add_signature_element(sm1, output, + sm1_semantic_names[semantic->usage], semantic->usage_idx, sysval, reg->idx[0].offset); +} + /* Read a parameter token from the input stream, and possibly a relative * addressing token. */ static void shader_sm1_read_param(struct vkd3d_shader_sm1_parser *sm1, @@ -638,6 +835,8 @@ static void shader_sm1_read_semantic(struct vkd3d_shader_sm1_parser *sm1, range = &semantic->resource.range; range->space = 0; range->first = range->last = semantic->resource.reg.reg.idx[0].offset; + + shader_sm1_add_signature_element_from_semantic(sm1, semantic); }
static void shader_sm1_read_immconst(struct vkd3d_shader_sm1_parser *sm1, const uint32_t **ptr, @@ -829,7 +1028,10 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str { /* Destination token */ if (ins->dst_count) + { shader_sm1_read_dst_param(sm1, &p, dst_param); + shader_sm1_add_signature_element_from_register(sm1, &dst_param->reg); + }
/* Predication token */ if (ins->predicate) @@ -837,7 +1039,10 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str
/* Other source tokens */ for (i = 0; i < ins->src_count; ++i) + { shader_sm1_read_src_param(sm1, &p, &src_params[i]); + shader_sm1_add_signature_element_from_register(sm1, &src_params[i].reg); + } }
if (sm1->abort) diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index f1641c1e..ed603cd3 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -792,6 +792,7 @@ struct signature_element struct shader_signature { struct signature_element *elements; + size_t elements_capacity; unsigned int element_count; };
diff --git a/tests/vkd3d_shader_api.c b/tests/vkd3d_shader_api.c index 318e5068..ac51568b 100644 --- a/tests/vkd3d_shader_api.c +++ b/tests/vkd3d_shader_api.c @@ -476,6 +476,74 @@ static void test_scan_signatures(void) {"position", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0, 0xf, 0xf}, };
+ static const char vs3_source[] = + "void main(\n" + " in float4 c : position,\n" + " out float4 b : position,\n" + " in float4 a : binormal,\n" + " in float4 d : blendindices,\n" + " inout float4 e : texcoord2,\n" + " inout float4 f : color,\n" + " inout float g : fog,\n" + " inout float h : psize)\n" + "{\n" + " b = a + c + d;\n" + "}"; + + static const struct vkd3d_shader_signature_element vs3_inputs[] = + { + {"POSITION", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"BINORMAL", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + {"BLENDINDICES", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 3}, + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 4}, + {"FOG", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 5}, + {"PSIZE", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 6}, + }; + + static const struct vkd3d_shader_signature_element vs3_outputs[] = + { + {"POSITION", 0, 0, VKD3D_SHADER_SV_POSITION, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"FOG", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + {"PSIZE", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + }; + + static const char vs4_source[] = + "void main(\n" + " inout float4 c : position,\n" + " inout float4 a : binormal,\n" + " inout float4 d : blendindices,\n" + " inout float4 e : texcoord2,\n" + " inout float4 f : color,\n" + " inout float4 g : fog,\n" + " inout float h : psize)\n" + "{\n" + "}"; + + static const struct vkd3d_shader_signature_element vs4_inputs[] = + { + {"POSITION", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"BINORMAL", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + {"BLENDINDICES", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 3}, + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 4}, + {"FOG", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 5}, + {"PSIZE", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 6}, + }; + + static const struct vkd3d_shader_signature_element vs4_outputs[] = + { + {"POSITION", 0, 0, VKD3D_SHADER_SV_POSITION, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"BINORMAL", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + {"BLENDINDICES", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 3}, + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 4}, + {"FOG", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 5}, + {"PSIZE", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 6}, + }; + static const char ps1_source[] = "void main(\n" " in float2 a : apple,\n" @@ -501,6 +569,72 @@ static void test_scan_signatures(void) {"sv_depth", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, ~0u, 0x1, 0x1}, };
+ static const char ps2_source[] = + "void main(\n" + "in float4 c : color,\n" + "in float4 a : texcoord2,\n" + "out float4 b : color)\n" + "{\n" + "b = a.x + c;\n" + "}"; + + static const struct vkd3d_shader_signature_element ps2_inputs[] = + { + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + }; + + static const struct vkd3d_shader_signature_element ps2_outputs[] = + { + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + }; + + static const char ps3_source[] = + "void main(\n" + "in float4 c : color,\n" + "in float4 a : texcoord2,\n" + "out float4 b : color,\n" + "out float d : depth)\n" + "{\n" + "b = c;\n" + "d = a;\n" + "}"; + + static const struct vkd3d_shader_signature_element ps3_inputs[] = + { + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + }; + + static const struct vkd3d_shader_signature_element ps3_outputs[] = + { + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"DEPTH", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + }; + + static const char ps4_source[] = + "void main(\n" + " in float4 c : color,\n" + " in float4 a : texcoord2,\n" + " out float4 b : color,\n" + " inout float d : depth,\n" + " in float4 e : blendindices,\n" + " in float4 f : vpos,\n" + " in float g : vface)\n" + "{\n" + " b = c + a + e + f + g;\n" + "}"; + + static const struct vkd3d_shader_signature_element ps4_inputs[] = + { + {"COLOR", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"TEXCOORD", 2, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + {"DEPTH", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 2}, + {"BLENDINDICES", 0, 0, VKD3D_SHADER_SV_NONE, VKD3D_SHADER_COMPONENT_FLOAT, 3}, + {"VPOS", 0, 0, VKD3D_SHADER_SV_POSITION, VKD3D_SHADER_COMPONENT_FLOAT, 0}, + {"VFACE", 0, 0, VKD3D_SHADER_SV_IS_FRONT_FACE, VKD3D_SHADER_COMPONENT_FLOAT, 1}, + }; + static const char cs1_source[] = "[numthreads(1, 1, 1)]\n" "void main(\n" @@ -513,6 +647,7 @@ static void test_scan_signatures(void) static const struct { const char *source; + bool sm4; const char *profile; const struct vkd3d_shader_signature_element *inputs; size_t input_count; @@ -523,10 +658,16 @@ static void test_scan_signatures(void) } 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}, + {vs1_source, true, "vs_4_0", vs1_inputs, ARRAY_SIZE(vs1_inputs), vs1_outputs, ARRAY_SIZE(vs1_outputs)}, + {vs2_source, true, "vs_4_0", vs2_inputs, ARRAY_SIZE(vs2_inputs), vs2_outputs, ARRAY_SIZE(vs2_outputs)}, + {vs3_source, false, "vs_1_1", vs3_inputs, ARRAY_SIZE(vs3_inputs), vs3_outputs, ARRAY_SIZE(vs3_outputs)}, + {vs3_source, false, "vs_2_0", vs3_inputs, ARRAY_SIZE(vs3_inputs), vs3_outputs, ARRAY_SIZE(vs3_outputs)}, + {vs4_source, false, "vs_3_0", vs4_inputs, ARRAY_SIZE(vs4_inputs), vs4_outputs, ARRAY_SIZE(vs4_outputs)}, + {ps1_source, true, "ps_4_0", ps1_inputs, ARRAY_SIZE(ps1_inputs), ps1_outputs, ARRAY_SIZE(ps1_outputs)}, + {ps2_source, false, "ps_1_1", ps2_inputs, ARRAY_SIZE(ps2_inputs), ps2_outputs, ARRAY_SIZE(ps2_outputs)}, + {ps3_source, false, "ps_2_0", ps3_inputs, ARRAY_SIZE(ps3_inputs), ps3_outputs, ARRAY_SIZE(ps3_outputs)}, + {ps4_source, false, "ps_3_0", ps4_inputs, ARRAY_SIZE(ps4_inputs), ps3_outputs, ARRAY_SIZE(ps3_outputs)}, + {cs1_source, true, "cs_5_0", NULL, 0, NULL, 0}, };
for (i = 0; i < ARRAY_SIZE(tests); ++i) @@ -536,7 +677,7 @@ static void test_scan_signatures(void) 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.target_type = tests[i].sm4 ? VKD3D_SHADER_TARGET_DXBC_TPF : VKD3D_SHADER_TARGET_D3D_BYTECODE; compile_info.log_level = VKD3D_SHADER_LOG_INFO;
compile_info.next = &hlsl_info; @@ -545,7 +686,7 @@ static void test_scan_signatures(void) 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_type = tests[i].sm4 ? VKD3D_SHADER_SOURCE_DXBC_TPF : VKD3D_SHADER_SOURCE_D3D_BYTECODE; compile_info.source = dxbc;
compile_info.next = &signature_info;
Second, signatures synthesized from sm1 byte code. This is conceptually a bit weird, because sm1 does not have signatures, but in terms of how these APIs are used by Wine (or other translation layers, as evidenced not least by the Vulkan test shader runner, which I have locally adapted for sm1 but not submitted yet) it fits rather nicely.
It's not that weird. Shaders have interfaces with each other and the rest of the API. For d3dbc shaders those are largely implicit interfaces, while tpf made them much more explicit. We could call this something else if you prefer, but conceptually it's very similar to the existing vkd3d_shader_scan_descriptor_info structure. In principle there's no reason we couldn't return this kind of information for SPIR-V/HLSL/GLSL/MSL shaders either.
Also, it's perhaps worth pointing out that a (fairly rudimentary) version of this exist in d3dx9 as D3DXGetShaderInputSemantics() and D3DXGetShaderOutputSemantics(); it might be worth making sure that we're able to implement those on top of this, although it doesn't look too hard.
Because this is new API, it of course deserves plenty of discussion, especially the sm1 part. Several open questions which occurred to me while writing are:
- Should we fix the mask (and used mask) for sm1 signatures as 0xf rather than 0? SPIR-V cares about this in order to declare I/O variables, which makes some amount of sense. In fact I have a patch in my local tree to change this, specifically for that purpose. However, we could also normalize it later.
- If we do fix the mask as nonzero, should single-component semantics (fog, depth, etc...) be declared as 0x1 instead of 0xf?
- Should BLENDINDICES get a UINT type? It's float in shader instructions (well, kind of, although in practice it's used as an array index of course), but the vertex attribute type is in practice "supposed" to be integer.
Could we just provide accurate information from the point of view of the user of the API? I.e., provide the actual masks and data types the shader expects and uses?
I'm not quite sure what that means in practice for BLENDINDICES; in wined3d we seem to map everything to WINED3D_TYPE_FLOAT, but it's not clear if that's correct because we have very little existing coverage for BLENDINDICES. But if e.g. Vulkan should pass the data for this as VK_FORMAT_R32_UINT, I think it should be UINT, yes.
+static bool vkd3d_shader_signature_from_shader_signature(struct vkd3d_shader_signature *signature, + const struct shader_signature *src);
I imagine we'll just want to move the function in the final version of this series and avoid the forward declaration.
+static bool add_signature_element(struct shader_signature *signature, const char *name, + unsigned int index, enum vkd3d_shader_sysval_semantic sysval, unsigned int register_index) +{ [...] + element->semantic_name = name; + element->semantic_index = index; + element->stream_index = 0; + element->sysval_semantic = sysval; + element->component_type = VKD3D_SHADER_COMPONENT_FLOAT; + element->register_index = register_index; + element->mask = 0; + element->used_mask = 0; + element->min_precision = VKD3D_SHADER_MINIMUM_PRECISION_NONE; + + return true; +}
I think we should set the "register_count" field above. It will be largely unused for the moment, but vkd3d_shader_signature_from_shader_signature() will print a FIXME if it ends up being larger than 1.
Most of the FIXMEs should use vkd3d_parser_error()/vkd3d_parser_warning().
Could we just provide accurate information from the point of view of the user of the API? I.e., provide the actual masks and data types the shader expects and uses?
I think so.
I'm not quite sure what that means in practice for BLENDINDICES; in wined3d we seem to map everything to WINED3D_TYPE_FLOAT, but it's not clear if that's correct because we have very little existing coverage for BLENDINDICES. But if e.g. Vulkan should pass the data for this as VK_FORMAT_R32_UINT, I think it should be UINT, yes.
I'm going off memory, but I think it's legal in d3d9 to use any data type in the vertex declaration. I think it's also legal for d3d11, even; i.e. there's no need to actually match the shader data type. In practice this means we hit Vulkan validation errors, which we'd need yet more interface data to avoid, and I believe these validation errors also matter (i.e. the driver will just bit-cast the data if the type doesn't match.)
So if my memory is correct in this respect, the vertex attribute type doesn't exactly matter. On the other hand, we can (as always) provide reasonable guesses, and UINT is the best reasonable guess for BLENDINDICES, so it does kind of seem like the most sensible thing to me.
Wrt wined3d, I haven't checked, but GL may implicitly do a proper cast?
I imagine we'll just want to move the function in the final version of this series and avoid the forward declaration.
Sure, I'll add an extra patch for that.
I think we should set the "register_count" field above. It will be largely unused for the moment, but vkd3d_shader_signature_from_shader_signature() will print a FIXME if it ends up being larger than 1.
Indeed; that was oversight while rebasing onto Conor's work.
Most of the FIXMEs should use vkd3d_parser_error()/vkd3d_parser_warning().
Sounds right.
I'm going off memory, but I think it's legal in d3d9 to use any data type in the vertex declaration. I think it's also legal for d3d11, even; i.e. there's no need to actually match the shader data type. In practice this means we hit Vulkan validation errors, which we'd need yet more interface data to avoid, and I believe these validation errors also matter (i.e. the driver will just bit-cast the data if the type doesn't match.)
So if my memory is correct in this respect, the vertex attribute type doesn't exactly matter. On the other hand, we can (as always) provide reasonable guesses, and UINT is the best reasonable guess for BLENDINDICES, so it does kind of seem like the most sensible thing to me.
Wrt wined3d, I haven't checked, but GL may implicitly do a proper cast?
Direct3D 9 and the OpenGL of that time didn't have proper integer attributes; you could send integer data to the driver/GPU, but in the shader it would be visible as floating point data. The GPUs of that time generally just didn't have the capability of doing e.g. integer multiplication. Note also that most of the d3d9 vertex declaration data types are either floating-point formats or normalised formats, although e.g. D3DDECLTYPE_UBYTE4 does exist.
The OpenGL interface is perhaps illustrative here; compare the behaviour of e.g. glVertexAttrib1i() with the behaviour of glVertexAttribI1i(). The GL spec calls these "pure integers".
I think that what you're saying implies that FLOAT would be the correct type for BLENDINDICES.
I think that what you're saying implies that FLOAT would be the correct type for BLENDINDICES.
I think so, but that does then force us to add that extra interface, whereas we could otherwise (always? usually?) get away with guessing UINT for blend indices and FLOAT otherwise. Which is annoying :-/
(I did say that I ran into Vulkan validation errors, but on second reflection I'm not sure if I have, *except* for BLENDINDICES.)
I think that what you're saying implies that FLOAT would be the correct type for BLENDINDICES.
I think so, but that does then force us to add that extra interface, whereas we could otherwise (always? usually?) get away with guessing UINT for blend indices and FLOAT otherwise. Which is annoying :-/
We could add a compilation option for "pure integer" BLENDINDICES which would perhaps make the issue go away for most cases in practice, but in principle I don't think it would prevent an application from e.g. using D3DDECLTYPE_UBYTE4 texture coordinates.
Okay, I went and actually double checked, and though I didn't test exhaustively, it looks like d3d9 will always cast to float, but d3d11 will simply bit-cast. At least, R32G32B32A32_UINT seems to behave the same as R32G32B32A32_FLOAT.
I feel like you can make a reasonable argument for always defaulting d3d9 attributes to float, for defaulting BLENDINDICES to UINT and the rest to float, and for not having a default at all and making them some VKD3D_SHADER_COMPONENT_UNKNOWN or VOID type.
I feel like it has to be unlikely that any d3d8/9 application is ever going to deviate from the "blendindices is uint, everything else is float" pattern, but if you think it's more sensible to default everything to float and just add that interface anyway, I can take that approach...
Okay, I went and actually double checked, and though I didn't test exhaustively, it looks like d3d9 will always cast to float, but d3d11 will simply bit-cast. At least, R32G32B32A32_UINT seems to behave the same as R32G32B32A32_FLOAT.
Sound like what we'd expect.
I feel like you can make a reasonable argument for always defaulting d3d9 attributes to float, for defaulting BLENDINDICES to UINT and the rest to float, and for not having a default at all and making them some VKD3D_SHADER_COMPONENT_UNKNOWN or VOID type.
I feel like it has to be unlikely that any d3d8/9 application is ever going to deviate from the "blendindices is uint, everything else is float" pattern, but if you think it's more sensible to default everything to float and just add that interface anyway, I can take that approach...
Actually, I don't think we need to handle anything in the shader for this. Vulkan has the _USCALED/_SSCALED formats (and VK_FORMAT_R8G8B8A8_USCALED in particular) which look like what we want here. That would probably imply introducing WINED3DFMT_R8G8B8A8_USCALED and a few other formats and then distinguishing them from their _UINT/_SINT variant, but that would be a wined3d issue, and probably not a bad thing in any case...
Actually, I don't think we need to handle anything in the shader for this. Vulkan has the _USCALED/_SSCALED formats (and VK_FORMAT_R8G8B8A8_USCALED in particular) which look like what we want here. That would probably imply introducing WINED3DFMT_R8G8B8A8_USCALED and a few other formats and then distinguishing them from their _UINT/_SINT variant, but that would be a wined3d issue, and probably not a bad thing in any case...
Oh, that's very convenient. Float it is, then :-)