From 6801ad9b7a578fbe3157fffc8bc4daeec5eb81a8 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Fri, 4 Oct 2024 23:36:28 +0200 Subject: [PATCH] vkd3d-shader/fx: Introduce a parser/disassembler. Signed-off-by: Nikolay Sivov --- include/vkd3d_shader.h | 5 + libs/vkd3d-shader/fx.c | 267 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_main.c | 14 ++ libs/vkd3d-shader/vkd3d_shader_private.h | 5 + programs/vkd3d-compiler/main.c | 2 + 5 files changed, 293 insertions(+) diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index e22f236e..5c0d13ea 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -1181,6 +1181,11 @@ enum vkd3d_shader_source_type * the format used for Direct3D shader model 6 shaders. \since 1.9 */ VKD3D_SHADER_SOURCE_DXBC_DXIL, + /** + * Binary format used by Direct3D 9/10.x/11 effects. + * Input is a raw FX section without container. \since 1.14 + */ + VKD3D_SHADER_SOURCE_FX, VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_SOURCE_TYPE), }; diff --git a/libs/vkd3d-shader/fx.c b/libs/vkd3d-shader/fx.c index cc18857a..66fa2793 100644 --- a/libs/vkd3d-shader/fx.c +++ b/libs/vkd3d-shader/fx.c @@ -2814,3 +2814,270 @@ int hlsl_emit_effect_binary(struct hlsl_ctx *ctx, struct vkd3d_shader_code *out) vkd3d_unreachable(); } } + +struct fx_parser +{ + const uint8_t *ptr, *start, *end; + struct vkd3d_shader_message_context *message_context; + struct vkd3d_string_buffer buffer; + struct + { + const uint8_t *ptr; + const uint8_t *end; + uint32_t size; + } unstructured; + uint32_t buffer_count; + bool failed; +}; + +static uint32_t fx_parser_read_u32(struct fx_parser *parser) +{ + uint32_t ret; + + if ((parser->end - parser->ptr) < sizeof(uint32_t)) + { + parser->failed = true; + return 0; + } + + ret = *(uint32_t *)parser->ptr; + parser->ptr += sizeof(uint32_t); + + return ret; +} + +static void fx_parser_read_u32s(struct fx_parser *parser, void *dst, size_t size) +{ + uint32_t *ptr = dst; + size_t i; + + for (i = 0; i < size / sizeof(uint32_t); ++i) + ptr[i] = fx_parser_read_u32(parser); +} + +static void fx_parser_skip(struct fx_parser *parser, size_t size) +{ + if ((parser->end - parser->ptr) < size) + { + parser->ptr = parser->end; + parser->failed = true; + return; + } + parser->ptr += size; +} + +static void VKD3D_PRINTF_FUNC(3, 4) fx_parser_error(struct fx_parser *parser, enum vkd3d_shader_error error, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + vkd3d_shader_verror(parser->message_context, NULL, error, format, args); + va_end(args); + + parser->failed = true; +} + +static int fx_2_parse(struct fx_parser *parser) +{ + fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_NOT_IMPLEMENTED, "Parsing fx_2_0 binaries is not implemented.\n"); + + return -1; +} + +static const char *fx_4_get_string(struct fx_parser *parser, uint32_t offset) +{ + const uint8_t *ptr = parser->unstructured.ptr; + const uint8_t *end = parser->unstructured.end; + + if (offset >= parser->unstructured.size) + { + parser->failed = true; + return ""; + } + + ptr += offset; + + while (ptr < end && *ptr) + ++ptr; + + if (*ptr) + { + parser->failed = true; + return ""; + } + + return (const char *)(parser->unstructured.ptr + offset); +} + +static int fx_parse_buffers(struct fx_parser *parser) +{ + struct fx_buffer + { + uint32_t name; + uint32_t size; + uint32_t flags; + uint32_t count; + uint32_t bind_point; + } buffer; + const char *name; + uint32_t i; + + for (i = 0; i < parser->buffer_count; ++i) + { + fx_parser_read_u32s(parser, &buffer, sizeof(buffer)); + + name = fx_4_get_string(parser, buffer.name); + + vkd3d_string_buffer_printf(&parser->buffer, "cbuffer %s\n", name); + vkd3d_string_buffer_printf(&parser->buffer, "{\n"); + + if (fx_parser_read_u32(parser)) + { + fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_NOT_IMPLEMENTED, "Parsing annotations is not implemented.\n"); + return -1; + } + + if (buffer.count) + { + fx_parser_error(parser, VKD3D_SHADER_ERROR_FX_NOT_IMPLEMENTED, + "Parsing constant buffer elements is not implemented.\n"); + return -1; + } + + vkd3d_string_buffer_printf(&parser->buffer, "}\n\n"); + } + + return 0; +} + +static int fx_4_parse(struct fx_parser *parser) +{ + struct fx_4_header + { + uint32_t version; + uint32_t buffer_count; + uint32_t numeric_variable_count; + uint32_t object_variable_count; + uint32_t shared_buffer_count; + uint32_t shared_numeric_variable_count; + uint32_t shared_object_count; + uint32_t technique_count; + uint32_t unstructured_size; + uint32_t string_count; + uint32_t texture_count; + uint32_t depth_stencil_state_count; + uint32_t blend_state_count; + uint32_t rasterizer_state_count; + uint32_t sampler_state_count; + uint32_t rtv_count; + uint32_t dsv_count; + uint32_t shader_count; + uint32_t inline_shader_count; + } header; + + fx_parser_read_u32s(parser, &header, sizeof(header)); + parser->buffer_count = header.buffer_count; + + if (parser->end - parser->ptr < header.unstructured_size) + { + parser->failed = true; + return -1; + } + + parser->unstructured.ptr = parser->ptr; + parser->unstructured.end = parser->ptr + header.unstructured_size; + parser->unstructured.size = header.unstructured_size; + fx_parser_skip(parser, header.unstructured_size); + + return fx_parse_buffers(parser); +} + +static int fx_5_parse(struct fx_parser *parser) +{ + struct fx_5_header + { + uint32_t version; + uint32_t buffer_count; + uint32_t numeric_variable_count; + uint32_t object_variable_count; + uint32_t shared_buffer_count; + uint32_t shared_numeric_variable_count; + uint32_t shared_object_count; + uint32_t technique_count; + uint32_t unstructured_size; + uint32_t string_count; + uint32_t texture_count; + uint32_t depth_stencil_state_count; + uint32_t blend_state_count; + uint32_t rasterizer_state_count; + uint32_t sampler_state_count; + uint32_t rtv_count; + uint32_t dsv_count; + uint32_t shader_count; + uint32_t inline_shader_count; + uint32_t group_count; + uint32_t uav_count; + uint32_t interface_variable_count; + uint32_t interface_variable_element_count; + uint32_t class_instance_element_count; + } header; + + fx_parser_read_u32s(parser, &header, sizeof(header)); + parser->buffer_count = header.buffer_count; + + if (parser->end - parser->ptr < header.unstructured_size) + { + parser->failed = true; + return -1; + } + + parser->unstructured.ptr = parser->ptr; + parser->unstructured.end = parser->ptr + header.unstructured_size; + parser->unstructured.size = header.unstructured_size; + fx_parser_skip(parser, header.unstructured_size); + + return fx_parse_buffers(parser); +} + +int fx_parse(const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) +{ + struct fx_parser parser = + { + .start = compile_info->source.code, + .ptr = compile_info->source.code, + .end = (uint8_t *)compile_info->source.code + compile_info->source.size, + .message_context = message_context, + }; + uint32_t version; + int ret; + + vkd3d_string_buffer_init(&parser.buffer); + + if (parser.end - parser.start < sizeof(version)) + return -1; + version = *(uint32_t *)parser.ptr; + + switch (version) + { + case 0xfeff0901: + ret = fx_2_parse(&parser); + break; + case 0xfeff1001: + case 0xfeff1011: + ret = fx_4_parse(&parser); + break; + case 0xfeff2001: + ret = fx_5_parse(&parser); + break; + default: + fx_parser_error(&parser, VKD3D_SHADER_ERROR_FX_INVALID_VERSION, + "Invalid effect binary version value 0x%08x.", version); + ret = -1; + } + + vkd3d_shader_code_from_string_buffer(out, &parser.buffer); + + return ret; +} diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 885aeb18..53f905ca 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -1710,6 +1710,10 @@ int vkd3d_shader_compile(const struct vkd3d_shader_compile_info *compile_info, { ret = compile_hlsl(compile_info, out, &message_context); } + else if (compile_info->source_type == VKD3D_SHADER_SOURCE_FX) + { + ret = fx_parse(compile_info, out, &message_context); + } else { uint64_t config_flags = vkd3d_shader_init_config_flags(); @@ -1942,6 +1946,7 @@ const enum vkd3d_shader_source_type *vkd3d_shader_get_supported_source_types(uns #ifdef VKD3D_SHADER_UNSUPPORTED_DXIL VKD3D_SHADER_SOURCE_DXBC_DXIL, #endif + VKD3D_SHADER_SOURCE_FX, }; TRACE("count %p.\n", count); @@ -2000,6 +2005,11 @@ const enum vkd3d_shader_target_type *vkd3d_shader_get_supported_target_types( }; #endif + static const enum vkd3d_shader_target_type fx_types[] = + { + VKD3D_SHADER_TARGET_D3D_ASM, + }; + TRACE("source_type %#x, count %p.\n", source_type, count); switch (source_type) @@ -2022,6 +2032,10 @@ const enum vkd3d_shader_target_type *vkd3d_shader_get_supported_target_types( return dxbc_dxil_types; #endif + case VKD3D_SHADER_SOURCE_FX: + *count = ARRAY_SIZE(fx_types); + return fx_types; + default: *count = 0; return NULL; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 7c5f6686..04d3a54e 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -252,6 +252,9 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_MSL_INTERNAL = 10000, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND = 10001, + + VKD3D_SHADER_ERROR_FX_NOT_IMPLEMENTED = 11000, + VKD3D_SHADER_ERROR_FX_INVALID_VERSION = 11001, }; enum vkd3d_shader_opcode @@ -1605,6 +1608,8 @@ int dxil_parse(const struct vkd3d_shader_compile_info *compile_info, uint64_t co struct vkd3d_shader_message_context *message_context, struct vsir_program *program); int tpf_parse(const struct vkd3d_shader_compile_info *compile_info, uint64_t config_flags, struct vkd3d_shader_message_context *message_context, struct vsir_program *program); +int fx_parse(const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context); void free_dxbc_shader_desc(struct dxbc_shader_desc *desc); diff --git a/programs/vkd3d-compiler/main.c b/programs/vkd3d-compiler/main.c index 8a07e2e2..b6eae6c9 100644 --- a/programs/vkd3d-compiler/main.c +++ b/programs/vkd3d-compiler/main.c @@ -813,6 +813,8 @@ int main(int argc, char **argv) } else if ((token & 0xfffe0000) == 0xfffe0000) options.source_type = VKD3D_SHADER_SOURCE_D3D_BYTECODE; + else if ((token & 0xffff0000) == 0xfeff0000) + options.source_type = VKD3D_SHADER_SOURCE_FX; else options.source_type = VKD3D_SHADER_SOURCE_HLSL; }