Most of the interesting part is the API (first patch); the rest is implementation. The basic idea is that we have predefined fixed numbers for the float, integer, and boolean constant arrays, and treat each of the three like a constant buffer.
Implementation-wise, we lower sm1-style constant registers to sm4-style constant buffers and immediate constants, which are capable of expressing a strict superset of the same functionality.
Note that use of immediate constants is *not* the way that wined3d currently handles DEF instructions, but rather those are uploaded via uniforms. Is there a reason for this?
Relative addressing is not yet implemented. In theory it should be simple enough to either translate it directly, for external constants, or use sm4-style immediate constant buffers, for internal constants. There may be a snag if an application depends on relative addressing to cover a range of both internal and external constants; this surely would require manual assembly or an application bug, but we could implement it by copying to a temporary array using a private TEMP-like register type.
--
Because API is easiest to review when there's a concrete user, I have functional patches hooking this up to wined3d, in the following branches:
https://gitlab.winehq.org/zfigura/vkd3d/-/commits/himavant5
https://gitlab.winehq.org/zfigura/wine/-/commits/himavant_cb
The vkd3d branch contains some extra commits necessary to compile basic shaders; I intend to submit these as soon as possible after the important API parts have been reviewed. I tested this patch series by building a mingw vkd3d tree with that branch, and running the shader runner through Wine, with a test that uses both internal and external constants:
make tests/shader_runner.exe && WINE_D3D_CONFIG=renderer=vulkan wine tests/shader_runner.exe ../vkd3d/tests/hlsl/writemask-assignop-0.shader_test
I actually originally wrote the API without a user in mind, and later hooked up the implementation in Wine, and was surprised to find how straightforward it ended up being, so I think that speaks quite strongly in favour of this API. Granted, I had already written "wined3d: Store push constants in wined3d_buffers in struct wined3d_state." by that point, but that was something I anticipated needing for Wine anyway, without thinking of vkd3d.
--
I actually originally did the implementation all in spirv.c, mostly because this was before we had infrastructure in place to do passes on the middle IR. I much prefer this version, it's quite centralized in one place and I think ends up being simpler than the spirv.c version anyway, but I can retrieve the spirv.c version if anyone wants to see it.
That said, we may not want to lower to VKD3DSPR_CONSTBUFFER for GLSL without UBOs (but then again, we could also just emit VKD3DSPR_CONSTBUFFER registers as plain GLSL arrays).
The actual declaration of flat constants is kept in spirv. I suppose the alternative here is to instead declare buffers from the reflection information and simply ignore dcl_constantbuffer. I submitted the patch as-is since it seemed simple enough and I didn't want to block this work further on rewriting that part, but we may want to rewrite it in the future regardless.
``` After the torchlight red on sweaty faces After the frosty silence in the gardens After the agony in stony places The shouting and the crying Prison and palace and reverberation Of thunder of spring over distant mountains ```
-- v2: vkd3d-shader/spirv: Emit variables for flat constant buffers. vkd3d-shader/ir: Normalise sm1-style constants. vkd3d-shader/ir: Move normalization code from spirv.c to ir.c. vkd3d-shader/d3dbc: Scan descriptors for constant register sets. include: Define an API for d3dbc constants.
From: Zebediah Figura zfigura@codeweavers.com
--- include/vkd3d_shader.h | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 6c17a07b..8133d240 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -332,6 +332,25 @@ struct vkd3d_shader_parameter } u; };
+/** + * Symbolic register indices for mapping uniform constant register sets in + * legacy Direct3D bytecode to constant buffer views in the target environment. + * + * Members of this enumeration are used in + * \ref vkd3d_shader_resource_binding.register_index. + * + * \since 1.9 + */ +enum vkd3d_shader_d3dbc_constant_register +{ + /** The float constant register set, c# in Direct3D assembly. */ + VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER = 0x0, + /** The integer constant register set, i# in Direct3D assembly. */ + VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER = 0x1, + /** The boolean constant register set, b# in Direct3D assembly. */ + VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER = 0x2, +}; + /** * Describes the mapping of a single resource or resource array to its binding * point in the target environment. @@ -356,7 +375,14 @@ struct vkd3d_shader_resource_binding * support multiple register spaces, this parameter must be set to 0. */ unsigned int register_space; - /** Register index of the DXBC resource. */ + /** + * Register index of the Direct3D resource. + * + * For legacy Direct3D shaders, vkd3d-shader maps each constant register + * set to a single constant buffer view. This parameter names the register + * set to map, and must be a member of + * enum vkd3d_shader_d3dbc_constant_register. + */ unsigned int register_index; /** Shader stage(s) to which the resource is visible. */ enum vkd3d_shader_visibility shader_visibility; @@ -1330,6 +1356,20 @@ struct vkd3d_shader_descriptor_info * A chained structure enumerating the descriptors declared by a shader. * * This structure extends vkd3d_shader_compile_info. + * + * When scanning a legacy Direct3D shader, vkd3d-shader enumerates each + * constant register set used by the shader as a single constant buffer + * descriptor, as follows: + * - The \ref vkd3d_shader_descriptor_info.type field is set to + * VKD3D_SHADER_DESCRIPTOR_TYPE_CBV. + * - The \ref vkd3d_shader_descriptor_info.register_space field is set to zero. + * - The \ref vkd3d_shader_descriptor_info.register_index field is set to a + * member of enum vkd3d_shader_d3dbc_constant_register denoting which set + * is used. + * - The \ref vkd3d_shader_descriptor_info.count field is set to one. + * + * In summary, there may be up to three such descriptors, one for each register + * set used by the shader: float, integer, and boolean. */ struct vkd3d_shader_scan_descriptor_info {
From: Zebediah Figura zfigura@codeweavers.com
--- include/private/vkd3d_common.h | 15 ++++ libs/vkd3d-shader/d3dbc.c | 82 ++++++++++++++++++-- libs/vkd3d-shader/vkd3d_shader_main.c | 9 +++ libs/vkd3d-shader/vkd3d_shader_private.h | 5 ++ tests/vkd3d_shader_api.c | 95 ++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 5 deletions(-)
diff --git a/include/private/vkd3d_common.h b/include/private/vkd3d_common.h index 6be3cee8..eec63bcc 100644 --- a/include/private/vkd3d_common.h +++ b/include/private/vkd3d_common.h @@ -188,6 +188,21 @@ static inline int vkd3d_u32_compare(uint32_t x, uint32_t y) return (x > y) - (x < y); }
+static inline bool bitmap_clear(uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] &= ~(1u << (idx & 0x1f)); +} + +static inline bool bitmap_set(uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] |= (1u << (idx & 0x1f)); +} + +static inline bool bitmap_is_set(const uint32_t *map, unsigned int idx) +{ + return map[idx >> 5] & (1u << (idx & 0x1f)); +} + static inline int ascii_isupper(int c) { return 'A' <= c && c <= 'Z'; diff --git a/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d-shader/d3dbc.c index 369112ce..0b1c7bca 100644 --- a/libs/vkd3d-shader/d3dbc.c +++ b/libs/vkd3d-shader/d3dbc.c @@ -214,6 +214,9 @@ struct vkd3d_shader_sm1_parser bool abort;
struct vkd3d_shader_parser p; + +#define MAX_CONSTANT_COUNT 8192 + uint32_t constant_def_mask[3][MAX_CONSTANT_COUNT / 32]; };
/* This table is not order or position dependent. */ @@ -729,12 +732,60 @@ static bool add_signature_element_from_semantic(struct vkd3d_shader_sm1_parser * semantic->usage_idx, sysval, reg->idx[0].offset, true, mask); }
-static void shader_sm1_scan_register(struct vkd3d_shader_sm1_parser *sm1, const struct vkd3d_shader_register *reg, unsigned int mask) +static void record_constant_register(struct vkd3d_shader_sm1_parser *sm1, + enum vkd3d_shader_d3dbc_constant_register set, uint32_t index, bool from_def) { + struct vkd3d_shader_desc *desc = &sm1->p.shader_desc; + + desc->flat_constant_count[set].used = max(desc->flat_constant_count[set].used, index + 1); + if (from_def) + { + /* d3d shaders have a maximum of 8192 constants; we should not overrun + * this array. */ + assert((index / 32) <= ARRAY_SIZE(sm1->constant_def_mask[set])); + bitmap_set(sm1->constant_def_mask[set], index); + } +} + +static void shader_sm1_scan_register(struct vkd3d_shader_sm1_parser *sm1, + const struct vkd3d_shader_register *reg, unsigned int mask, bool from_def) +{ + struct vkd3d_shader_desc *desc = &sm1->p.shader_desc; uint32_t register_index = reg->idx[0].offset;
- if (reg->type == VKD3DSPR_TEMP) - sm1->p.shader_desc.temp_count = max(sm1->p.shader_desc.temp_count, register_index + 1); + switch (reg->type) + { + case VKD3DSPR_TEMP: + desc->temp_count = max(desc->temp_count, register_index + 1); + break; + + case VKD3DSPR_CONST: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, register_index, from_def); + break; + + case VKD3DSPR_CONST2: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 2048 + register_index, from_def); + break; + + case VKD3DSPR_CONST3: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 4096 + register_index, from_def); + break; + + case VKD3DSPR_CONST4: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 6144 + register_index, from_def); + break; + + case VKD3DSPR_CONSTINT: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, register_index, from_def); + break; + + case VKD3DSPR_CONSTBOOL: + record_constant_register(sm1, VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, register_index, from_def); + break; + + default: + break; + }
add_signature_element_from_register(sm1, reg, false, mask); } @@ -1076,16 +1127,19 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str { shader_sm1_read_dst_param(sm1, &p, dst_param); shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_FLOAT); + shader_sm1_scan_register(sm1, &dst_param->reg, dst_param->write_mask, true); } else if (ins->handler_idx == VKD3DSIH_DEFB) { shader_sm1_read_dst_param(sm1, &p, dst_param); shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_SCALAR, VKD3D_DATA_UINT); + shader_sm1_scan_register(sm1, &dst_param->reg, dst_param->write_mask, true); } else if (ins->handler_idx == VKD3DSIH_DEFI) { shader_sm1_read_dst_param(sm1, &p, dst_param); shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_INT); + shader_sm1_scan_register(sm1, &dst_param->reg, dst_param->write_mask, true); } else { @@ -1093,7 +1147,7 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str if (ins->dst_count) { shader_sm1_read_dst_param(sm1, &p, dst_param); - shader_sm1_scan_register(sm1, &dst_param->reg, dst_param->write_mask); + shader_sm1_scan_register(sm1, &dst_param->reg, dst_param->write_mask, false); }
/* Predication token */ @@ -1104,7 +1158,7 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_sm1_parser *sm1, str for (i = 0; i < ins->src_count; ++i) { shader_sm1_read_src_param(sm1, &p, &src_params[i]); - shader_sm1_scan_register(sm1, &src_params[i].reg, mask_from_swizzle(src_params[i].swizzle)); + shader_sm1_scan_register(sm1, &src_params[i].reg, mask_from_swizzle(src_params[i].swizzle), false); } }
@@ -1212,12 +1266,27 @@ static enum vkd3d_result shader_sm1_init(struct vkd3d_shader_sm1_parser *sm1, return VKD3D_OK; }
+static uint32_t get_external_constant_count(struct vkd3d_shader_sm1_parser *sm1, + enum vkd3d_shader_d3dbc_constant_register set) +{ + unsigned int j; + + for (j = sm1->p.shader_desc.flat_constant_count[set].used; j > 0; --j) + { + if (!bitmap_is_set(sm1->constant_def_mask[set], j - 1)) + return j; + } + + return 0; +} + int vkd3d_shader_sm1_parser_create(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context, struct vkd3d_shader_parser **parser) { struct vkd3d_shader_instruction_array *instructions; struct vkd3d_shader_instruction *ins; struct vkd3d_shader_sm1_parser *sm1; + unsigned int i; int ret;
if (!(sm1 = vkd3d_calloc(1, sizeof(*sm1)))) @@ -1257,6 +1326,9 @@ int vkd3d_shader_sm1_parser_create(const struct vkd3d_shader_compile_info *compi
*parser = &sm1->p;
+ for (i = 0; i < ARRAY_SIZE(sm1->p.shader_desc.flat_constant_count); ++i) + sm1->p.shader_desc.flat_constant_count[i].external = get_external_constant_count(sm1, i); + return sm1->p.failed ? VKD3D_ERROR_INVALID_SHADER : VKD3D_OK; }
diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index f26050bb..644fdc6b 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -1152,6 +1152,15 @@ static int scan_with_parser(const struct vkd3d_shader_compile_info *compile_info } }
+ for (i = 0; i < ARRAY_SIZE(parser->shader_desc.flat_constant_count); ++i) + { + struct vkd3d_shader_register_range range = {.space = 0, .first = i, .last = i}; + + if (parser->shader_desc.flat_constant_count[i].external) + vkd3d_shader_scan_add_descriptor(&context, VKD3D_SHADER_DESCRIPTOR_TYPE_CBV, + &range, VKD3D_SHADER_RESOURCE_BUFFER, VKD3D_SHADER_RESOURCE_DATA_FLOAT, 0); + } + if (!ret && signature_info) { if (!vkd3d_shader_signature_from_shader_signature(&signature_info->input, &parser->shader_desc.input_signature) diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 85fca964..9bf585b7 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -831,6 +831,11 @@ struct vkd3d_shader_desc struct shader_signature patch_constant_signature;
uint32_t temp_count; + + struct + { + uint32_t used, external; + } flat_constant_count[3]; };
struct vkd3d_shader_register_semantic diff --git a/tests/vkd3d_shader_api.c b/tests/vkd3d_shader_api.c index f9a3f717..f4d5b910 100644 --- a/tests/vkd3d_shader_api.c +++ b/tests/vkd3d_shader_api.c @@ -729,6 +729,100 @@ static void test_scan_signatures(void) } }
+static void test_scan_descriptors(void) +{ + struct vkd3d_shader_scan_descriptor_info descriptor_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_DESCRIPTOR_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 ps1_source[] = + "float4 main(uniform float4 u, uniform float4 v) : sv_target\n" + "{\n" + " return u * v + 1.0;\n" + "}"; + + static const char ps2_source[] = + "float4 main() : sv_target\n" + "{\n" + " return 1.0;\n" + "}"; + + static const struct vkd3d_shader_descriptor_info ps1_descriptors[] = + { + {VKD3D_SHADER_DESCRIPTOR_TYPE_CBV, 0, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, + VKD3D_SHADER_RESOURCE_BUFFER, VKD3D_SHADER_RESOURCE_DATA_FLOAT, 0, 1}, + }; + + static const struct + { + const char *source; + bool sm4; + const char *profile; + const struct vkd3d_shader_descriptor_info *descriptors; + size_t descriptor_count; + } + tests[] = + { + {ps1_source, false, "ps_2_0", ps1_descriptors, ARRAY_SIZE(ps1_descriptors)}, + {ps2_source, false, "ps_2_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 = 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; + 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 = tests[i].sm4 ? VKD3D_SHADER_SOURCE_DXBC_TPF : VKD3D_SHADER_SOURCE_D3D_BYTECODE; + compile_info.source = dxbc; + + compile_info.next = &descriptor_info; + + rc = vkd3d_shader_scan(&compile_info, NULL); + ok(rc == VKD3D_OK, "Got unexpected error code %d.\n", rc); + + ok(descriptor_info.descriptor_count == tests[i].descriptor_count, + "Got descriptor count %u.\n", descriptor_info.descriptor_count); + for (j = 0; j < descriptor_info.descriptor_count; ++j) + { + const struct vkd3d_shader_descriptor_info *descriptor = &descriptor_info.descriptors[j]; + const struct vkd3d_shader_descriptor_info *expect = &tests[i].descriptors[j]; + + vkd3d_test_push_context("descriptor %u", j); + + ok(descriptor->type == expect->type, "Got type %#x.\n", descriptor->type); + ok(descriptor->register_space == expect->register_space, "Got space %u.\n", descriptor->register_space); + ok(descriptor->register_index == expect->register_index, "Got index %u.\n", descriptor->register_index); + ok(descriptor->resource_type == expect->resource_type, + "Got resource type %#x.\n", descriptor->resource_type); + ok(descriptor->resource_data_type == expect->resource_data_type, + "Got data type %#x.\n", descriptor->resource_data_type); + ok(descriptor->flags == expect->flags, "Got flags %#x.\n", descriptor->flags); + ok(descriptor->count == expect->count, "Got count %u.\n", descriptor->count); + + vkd3d_test_pop_context(); + } + + vkd3d_shader_free_scan_descriptor_info(&descriptor_info); + vkd3d_shader_free_shader_code(&dxbc); + + vkd3d_test_pop_context(); + } +} + START_TEST(vkd3d_shader_api) { setlocale(LC_ALL, ""); @@ -739,4 +833,5 @@ START_TEST(vkd3d_shader_api) run_test(test_d3dbc); run_test(test_dxbc); run_test(test_scan_signatures); + run_test(test_scan_descriptors); }
From: Zebediah Figura zfigura@codeweavers.com
It is not spirv-specific and will (presumably) be used for GLSL as well. --- libs/vkd3d-shader/ir.c | 28 ++++++++++++++++++++--- libs/vkd3d-shader/spirv.c | 29 ++++++++---------------- libs/vkd3d-shader/vkd3d_shader_private.h | 7 +----- 3 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 9eefb82c..0e574e27 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -253,7 +253,7 @@ static void shader_instruction_init(struct vkd3d_shader_instruction *ins, enum v ins->handler_idx = handler_idx; }
-enum vkd3d_result instruction_array_flatten_hull_shader_phases(struct vkd3d_shader_instruction_array *src_instructions) +static enum vkd3d_result instruction_array_flatten_hull_shader_phases(struct vkd3d_shader_instruction_array *src_instructions) { struct hull_flattener flattener = {*src_instructions}; struct vkd3d_shader_instruction_array *instructions; @@ -388,7 +388,7 @@ static enum vkd3d_result control_point_normaliser_emit_hs_input(struct control_p return VKD3D_OK; }
-enum vkd3d_result instruction_array_normalise_hull_shader_control_point_io( +static enum vkd3d_result instruction_array_normalise_hull_shader_control_point_io( struct vkd3d_shader_instruction_array *src_instructions, const struct shader_signature *input_signature) { struct vkd3d_shader_instruction_array *instructions; @@ -999,7 +999,7 @@ static void shader_instruction_normalise_io_params(struct vkd3d_shader_instructi shader_instruction_init(ins, VKD3DSIH_NOP); }
-enum vkd3d_result instruction_array_normalise_io_registers(struct vkd3d_shader_instruction_array *instructions, +static enum vkd3d_result instruction_array_normalise_io_registers(struct vkd3d_shader_instruction_array *instructions, enum vkd3d_shader_type shader_type, struct shader_signature *input_signature, struct shader_signature *output_signature, struct shader_signature *patch_constant_signature) { @@ -1070,3 +1070,25 @@ enum vkd3d_result instruction_array_normalise_io_registers(struct vkd3d_shader_i *instructions = normaliser.instructions; return VKD3D_OK; } + +enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser) +{ + struct vkd3d_shader_instruction_array *instructions = &parser->instructions; + enum vkd3d_result result = VKD3D_OK; + + if (parser->shader_version.type == VKD3D_SHADER_TYPE_HULL + && (result = instruction_array_flatten_hull_shader_phases(instructions)) >= 0) + { + result = instruction_array_normalise_hull_shader_control_point_io(instructions, + &parser->shader_desc.input_signature); + } + if (result >= 0) + result = instruction_array_normalise_io_registers(instructions, parser->shader_version.type, + &parser->shader_desc.input_signature, &parser->shader_desc.output_signature, + &parser->shader_desc.patch_constant_signature); + + if (result >= 0 && TRACE_ON()) + vkd3d_shader_trace(instructions, &parser->shader_version); + + return result; +} diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 5535a650..4506e3ba 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2429,13 +2429,6 @@ static struct spirv_compiler *spirv_compiler_create(const struct vkd3d_shader_ve
compiler->shader_type = shader_version->type;
- compiler->input_signature = shader_desc->input_signature; - compiler->output_signature = shader_desc->output_signature; - compiler->patch_constant_signature = shader_desc->patch_constant_signature; - memset(&shader_desc->input_signature, 0, sizeof(shader_desc->input_signature)); - memset(&shader_desc->output_signature, 0, sizeof(shader_desc->output_signature)); - memset(&shader_desc->patch_constant_signature, 0, sizeof(shader_desc->patch_constant_signature)); - if ((shader_interface = vkd3d_find_struct(compile_info->next, INTERFACE_INFO))) { compiler->xfb_info = vkd3d_find_struct(compile_info->next, TRANSFORM_FEEDBACK_INFO); @@ -9438,6 +9431,7 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info; const struct vkd3d_shader_spirv_domain_shader_target_info *ds_info; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + struct vkd3d_shader_desc *shader_desc = &parser->shader_desc; struct vkd3d_shader_instruction_array instructions; enum vkd3d_result result = VKD3D_OK; unsigned int i; @@ -9448,21 +9442,18 @@ 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) + return result; + instructions = parser->instructions; memset(&parser->instructions, 0, sizeof(parser->instructions));
- if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL - && (result = instruction_array_flatten_hull_shader_phases(&instructions)) >= 0) - { - result = instruction_array_normalise_hull_shader_control_point_io(&instructions, - &compiler->input_signature); - } - if (result >= 0) - result = instruction_array_normalise_io_registers(&instructions, parser->shader_version.type, - &compiler->input_signature, &compiler->output_signature, &compiler->patch_constant_signature); - - if (result >= 0 && TRACE_ON()) - vkd3d_shader_trace(&instructions, &parser->shader_version); + compiler->input_signature = shader_desc->input_signature; + compiler->output_signature = shader_desc->output_signature; + compiler->patch_constant_signature = shader_desc->patch_constant_signature; + memset(&shader_desc->input_signature, 0, sizeof(shader_desc->input_signature)); + memset(&shader_desc->output_signature, 0, sizeof(shader_desc->output_signature)); + memset(&shader_desc->patch_constant_signature, 0, sizeof(shader_desc->patch_constant_signature));
if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) spirv_compiler_emit_shader_signature_outputs(compiler); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 9bf585b7..8a032a74 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1394,11 +1394,6 @@ 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 instruction_array_flatten_hull_shader_phases(struct vkd3d_shader_instruction_array *instructions); -enum vkd3d_result instruction_array_normalise_hull_shader_control_point_io( - struct vkd3d_shader_instruction_array *instructions, const struct shader_signature *input_signature); -enum vkd3d_result instruction_array_normalise_io_registers(struct vkd3d_shader_instruction_array *instructions, - enum vkd3d_shader_type shader_type, struct shader_signature *input_signature, - struct shader_signature *output_signature, struct shader_signature *patch_constant_signature); +enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser);
#endif /* __VKD3D_SHADER_PRIVATE_H */
From: Zebediah Figura zfigura@codeweavers.com
--- libs/vkd3d-shader/ir.c | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 0e574e27..962bb3fd 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1071,6 +1071,125 @@ static enum vkd3d_result instruction_array_normalise_io_registers(struct vkd3d_s return VKD3D_OK; }
+struct flat_constant_def +{ + enum vkd3d_shader_d3dbc_constant_register set; + uint32_t index; + uint32_t value[4]; +}; + +struct flat_constants_normaliser +{ + struct vkd3d_shader_parser *parser; + struct flat_constant_def *defs; + size_t def_count, defs_capacity; +}; + +static bool get_flat_constant_register_type(const struct vkd3d_shader_register *reg, + enum vkd3d_shader_d3dbc_constant_register *set, uint32_t *index) +{ + static const struct + { + enum vkd3d_shader_register_type type; + enum vkd3d_shader_d3dbc_constant_register set; + uint32_t offset; + } + regs[] = + { + {VKD3DSPR_CONST, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 0}, + {VKD3DSPR_CONST2, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 2048}, + {VKD3DSPR_CONST3, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 4096}, + {VKD3DSPR_CONST4, VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, 6144}, + {VKD3DSPR_CONSTINT, VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, 0}, + {VKD3DSPR_CONSTBOOL, VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, 0}, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(regs); ++i) + { + if (reg->type == regs[i].type) + { + if (reg->idx[0].rel_addr) + { + FIXME("Unhandled relative address.\n"); + return false; + } + + *set = regs[i].set; + *index = regs[i].offset + reg->idx[0].offset; + return true; + } + } + + return false; +} + +static void shader_register_normalise_flat_constants(struct vkd3d_shader_src_param *param, + const struct flat_constants_normaliser *normaliser) +{ + enum vkd3d_shader_d3dbc_constant_register set; + uint32_t index; + size_t i, j; + + if (!get_flat_constant_register_type(¶m->reg, &set, &index)) + return; + + for (i = 0; i < normaliser->def_count; ++i) + { + if (normaliser->defs[i].set == set && normaliser->defs[i].index == index) + { + param->reg.type = VKD3DSPR_IMMCONST; + param->reg.idx_count = 0; + param->reg.immconst_type = VKD3D_IMMCONST_VEC4; + for (j = 0; j < 4; ++j) + param->reg.u.immconst_uint[j] = normaliser->defs[i].value[j]; + return; + } + } + + param->reg.type = VKD3DSPR_CONSTBUFFER; + param->reg.idx[0].offset = set; /* register ID */ + param->reg.idx[1].offset = set; /* register index */ + param->reg.idx[2].offset = index; /* buffer index */ + param->reg.idx_count = 3; +} + +static enum vkd3d_result instruction_array_normalise_flat_constants(struct vkd3d_shader_parser *parser) +{ + struct flat_constants_normaliser normaliser = {.parser = parser}; + unsigned int i, j; + + for (i = 0; i < parser->instructions.count; ++i) + { + struct vkd3d_shader_instruction *ins = &parser->instructions.elements[i]; + + if (ins->handler_idx == VKD3DSIH_DEF || ins->handler_idx == VKD3DSIH_DEFI || ins->handler_idx == VKD3DSIH_DEFB) + { + struct flat_constant_def *def; + + if (!vkd3d_array_reserve((void **)&normaliser.defs, &normaliser.defs_capacity, + normaliser.def_count + 1, sizeof(*normaliser.defs))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + def = &normaliser.defs[normaliser.def_count++]; + + get_flat_constant_register_type((struct vkd3d_shader_register *)&ins->dst[0].reg, &def->set, &def->index); + for (j = 0; j < 4; ++j) + def->value[j] = ins->src[0].reg.u.immconst_uint[j]; + + vkd3d_shader_instruction_make_nop(ins); + } + else + { + for (j = 0; j < ins->src_count; ++j) + shader_register_normalise_flat_constants((struct vkd3d_shader_src_param *)&ins->src[j], &normaliser); + } + } + + return VKD3D_OK; +} + enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser) { struct vkd3d_shader_instruction_array *instructions = &parser->instructions; @@ -1087,6 +1206,9 @@ enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser) &parser->shader_desc.input_signature, &parser->shader_desc.output_signature, &parser->shader_desc.patch_constant_signature);
+ if (result >= 0) + result = instruction_array_normalise_flat_constants(parser); + if (result >= 0 && TRACE_ON()) vkd3d_shader_trace(instructions, &parser->shader_version);
From: Zebediah Figura zfigura@codeweavers.com
--- libs/vkd3d-shader/spirv.c | 65 +++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 4506e3ba..ffa9ae11 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2529,13 +2529,13 @@ static bool spirv_compiler_check_shader_visibility(const struct spirv_compiler * }
static struct vkd3d_push_constant_buffer_binding *spirv_compiler_find_push_constant_buffer( - const struct spirv_compiler *compiler, const struct vkd3d_shader_constant_buffer *cb) + const struct spirv_compiler *compiler, const struct vkd3d_shader_register_range *range) { - unsigned int register_space = cb->range.space; - unsigned int reg_idx = cb->range.first; + unsigned int register_space = range->space; + unsigned int reg_idx = range->first; unsigned int i;
- if (cb->range.first != cb->range.last) + if (range->first != range->last) return NULL;
for (i = 0; i < compiler->shader_interface.push_constant_buffer_count; ++i) @@ -5465,28 +5465,24 @@ static uint32_t spirv_compiler_build_descriptor_variable(struct spirv_compiler * return var_id; }
-static void spirv_compiler_emit_dcl_constant_buffer(struct spirv_compiler *compiler, - const struct vkd3d_shader_instruction *instruction) +static void spirv_compiler_emit_constant_buffer(struct spirv_compiler *compiler, unsigned int size, + const struct vkd3d_shader_register_range *range, const struct vkd3d_shader_register *reg) { - const struct vkd3d_shader_constant_buffer *cb = &instruction->declaration.cb; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; uint32_t vec4_id, array_type_id, length_id, struct_id, var_id; const SpvStorageClass storage_class = SpvStorageClassUniform; - const struct vkd3d_shader_register *reg = &cb->src.reg; struct vkd3d_push_constant_buffer_binding *push_cb; struct vkd3d_descriptor_variable_info var_info; struct vkd3d_symbol reg_symbol;
- assert(!(instruction->flags & ~VKD3DSI_INDEXED_DYNAMIC)); - - if ((push_cb = spirv_compiler_find_push_constant_buffer(compiler, cb))) + if ((push_cb = spirv_compiler_find_push_constant_buffer(compiler, range))) { /* Push constant buffers are handled in * spirv_compiler_emit_push_constant_buffers(). */ - unsigned int cb_size_in_bytes = cb->size * VKD3D_VEC4_SIZE * sizeof(uint32_t); + unsigned int cb_size_in_bytes = size * VKD3D_VEC4_SIZE * sizeof(uint32_t); push_cb->reg = *reg; - push_cb->size = cb->size; + push_cb->size = size; if (cb_size_in_bytes > push_cb->pc.size) { WARN("Constant buffer size %u exceeds push constant size %u.\n", @@ -5496,17 +5492,17 @@ static void spirv_compiler_emit_dcl_constant_buffer(struct spirv_compiler *compi }
vec4_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE); - length_id = spirv_compiler_get_constant_uint(compiler, cb->size); + length_id = spirv_compiler_get_constant_uint(compiler, size); array_type_id = vkd3d_spirv_build_op_type_array(builder, vec4_id, length_id); vkd3d_spirv_build_op_decorate1(builder, array_type_id, SpvDecorationArrayStride, 16);
struct_id = vkd3d_spirv_build_op_type_struct(builder, &array_type_id, 1); vkd3d_spirv_build_op_decorate(builder, struct_id, SpvDecorationBlock, NULL, 0); vkd3d_spirv_build_op_member_decorate1(builder, struct_id, 0, SpvDecorationOffset, 0); - vkd3d_spirv_build_op_name(builder, struct_id, "cb%u_struct", cb->size); + vkd3d_spirv_build_op_name(builder, struct_id, "cb%u_struct", size);
var_id = spirv_compiler_build_descriptor_variable(compiler, storage_class, struct_id, - reg, &cb->range, VKD3D_SHADER_RESOURCE_BUFFER, false, &var_info); + reg, range, VKD3D_SHADER_RESOURCE_BUFFER, false, &var_info);
vkd3d_symbol_make_register(®_symbol, reg); vkd3d_symbol_set_register_info(®_symbol, var_id, storage_class, @@ -5516,6 +5512,16 @@ static void spirv_compiler_emit_dcl_constant_buffer(struct spirv_compiler *compi spirv_compiler_put_symbol(compiler, ®_symbol); }
+static void spirv_compiler_emit_dcl_constant_buffer(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) +{ + const struct vkd3d_shader_constant_buffer *cb = &instruction->declaration.cb; + + assert(!(instruction->flags & ~VKD3DSI_INDEXED_DYNAMIC)); + + spirv_compiler_emit_constant_buffer(compiler, cb->size, &cb->range, &cb->src.reg); +} + static void spirv_compiler_emit_dcl_immediate_constant_buffer(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { @@ -9424,6 +9430,26 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, return ret; }
+static void spirv_compiler_emit_sm1_constant_buffer(struct spirv_compiler *compiler, + const struct vkd3d_shader_desc *desc, enum vkd3d_shader_d3dbc_constant_register set, + enum vkd3d_data_type data_type) +{ + struct vkd3d_shader_register_range range = {.space = 0, .first = set, .last = set}; + uint32_t count = desc->flat_constant_count[set].external; + struct vkd3d_shader_register reg = + { + .type = VKD3DSPR_CONSTBUFFER, + .idx[0].offset = set, /* register ID */ + .idx[1].offset = set, /* register index */ + .idx[2].offset = count, /* size */ + .idx_count = 3, + .data_type = data_type, + }; + + if (count) + spirv_compiler_emit_constant_buffer(compiler, count, &range, ®); +} + static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_parser *parser, struct vkd3d_shader_code *spirv) @@ -9439,6 +9465,13 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, if (parser->shader_desc.temp_count) spirv_compiler_emit_temps(compiler, parser->shader_desc.temp_count);
+ spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc, + VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, VKD3D_DATA_FLOAT); + spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc, + VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, VKD3D_DATA_INT); + spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc, + VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, VKD3D_DATA_UINT); + compiler->location.column = 0; compiler->location.line = 1;
There are a couple of cases where we can't use immediates though; relative addressing of constants is one case, and having non-finite values without GL_ARB_shader_bit_encoding is another.
The non-finite values part makes sense to me, but I don't see how relative addressing would require uniforms: is it not possible in GLSL to declare a const array of values and dynamically index that? Or are there actually applications that require dynamically indexing such that we need to return both internal and external constants?
As far as I know nothing prevents an application from mixing these, and it was not uncommon for shader model 1-3 shaders to be written in assembler, so "the HLSL compiler never generates mixed accesses" wouldn't generally be sufficient to stop us from worrying about them.
Makes sense. Although presumably that doesn't mean we have to upload them that way—I was envisioning solving that particular problem by copying both external and internal constants into a temp array in the compiled shader. (But maybe that's more overhead for the lower level compiler?)
It's perhaps also worth pointing out that Direct3D 8 can load shader constants from the vertex declaration using "D3DVSD_CONST"; we have a couple of examples of that in the Wine d3d8 tests.
Is there any reason we can't just implement that on top of wined3d_stateblock_set_vs_consts_f() these days?
It might be possible. The tricky part is probably mostly that D3DVSD_CONST constants override those set by SetVertexShaderConstant(), but we should still need to keep track of constants set by SetVertexShaderConstant() for when we switch vertex shaders.
I actually said wined3d_stateblock_set_vs_consts_f() but meant wined3d_device_set_vs_consts_f(). Point is, I was envisioning handling it in wined3d_device_apply_stateblock().
Right, as I said, the clarification is probably helpful. I'd just prefer phrasing it as something along the lines of "Note that vkd3d-shader maps constant registers in d3dbc sources to CBV registers in the following way: ..., so use enum vkd3d_shader_d3dbc_constant_register here for those.", in order to avoid (seemingly) implying that the "register_index" field works in a fundamentally different way for d3dbc sources.
I think I see what you're trying to get at. I've rewritten this bit in v2; does this seem more reasonable?
Makes sense. Although presumably that doesn't mean we have to upload them that way—I was envisioning solving that particular problem by copying both external and internal constants into a temp array in the compiled shader. (But maybe that's more overhead for the lower level compiler?)
I imagine it would be slower, yes. In part because relative addressing typically means all constants are potentially accessed instead of the more limited set typically used by applications. So we'd introduce a bunch of loads from the UBO/CBV that may never end up actually being used.
I guess you're trying to avoid having to use vkd3d_shader_scan() to determine the values of "local" constants? It would be nice if we could, but there may not be a good solution for that.
I think I see what you're trying to get at. I've rewritten this bit in v2; does this seem more reasonable?
Yeah, that works for me.
+ for (i = 0; i < ARRAY_SIZE(parser->shader_desc.flat_constant_count); ++i) + { + struct vkd3d_shader_register_range range = {.space = 0, .first = i, .last = i}; + + if (parser->shader_desc.flat_constant_count[i].external) + vkd3d_shader_scan_add_descriptor(&context, VKD3D_SHADER_DESCRIPTOR_TYPE_CBV, + &range, VKD3D_SHADER_RESOURCE_BUFFER, VKD3D_SHADER_RESOURCE_DATA_FLOAT, 0); + }
It's fairly minor, but I'd still feel better about this if it used VKD3D_SHADER_RESOURCE_DATA_UINT. Or perhaps we can use a common helper shared with vkd3d_shader_scan_constant_buffer_declaration().