In order to make it easier to add shader backends based on external libraries, e.g. vkd3d-shader. Additionally, allows us to easily parse additional DXBC chunks in wined3d.
Signed-off-by: Józef Kucia jkucia@codeweavers.com --- dlls/wined3d/shader.c | 105 ++++++++----- dlls/wined3d/shader_sm4.c | 329 +++++++++++++++++++++++++++++++++++++++++ dlls/wined3d/wined3d_private.h | 9 +- include/wine/wined3d.h | 1 + 4 files changed, 404 insertions(+), 40 deletions(-)
diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c index 9524757470eb..ecff0bc299a6 100644 --- a/dlls/wined3d/shader.c +++ b/dlls/wined3d/shader.c @@ -3153,7 +3153,7 @@ static void shader_cleanup(struct wined3d_shader *shader) heap_free(shader->signature_strings); shader->device->shader_backend->shader_destroy(shader); shader_cleanup_reg_maps(&shader->reg_maps); - heap_free(shader->function); + heap_free(shader->byte_code); shader_delete_constant_list(&shader->constantsF); shader_delete_constant_list(&shader->constantsB); shader_delete_constant_list(&shader->constantsI); @@ -3461,11 +3461,11 @@ HRESULT CDECL wined3d_shader_get_byte_code(const struct wined3d_shader *shader,
if (!byte_code) { - *byte_code_size = shader->functionLength; + *byte_code_size = shader->byte_code_size; return WINED3D_OK; }
- if (*byte_code_size < shader->functionLength) + if (*byte_code_size < shader->byte_code_size) { /* MSDN claims (for d3d8 at least) that if *byte_code_size is smaller * than the required size we should write the required size and @@ -3473,7 +3473,7 @@ HRESULT CDECL wined3d_shader_get_byte_code(const struct wined3d_shader *shader, return WINED3DERR_INVALIDCALL; }
- memcpy(byte_code, shader->function, shader->functionLength); + memcpy(byte_code, shader->byte_code, shader->byte_code_size);
return WINED3D_OK; } @@ -3693,7 +3693,8 @@ static HRESULT shader_copy_signatures_from_shader_desc(struct wined3d_shader *sh static HRESULT shader_init(struct wined3d_shader *shader, struct wined3d_device *device, const struct wined3d_shader_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops) { - size_t byte_code_size; + enum wined3d_shader_byte_code_format format; + unsigned int max_version; HRESULT hr;
TRACE("byte_code %p, byte_code_size %#lx, format %#x.\n", @@ -3702,20 +3703,11 @@ static HRESULT shader_init(struct wined3d_shader *shader, struct wined3d_device if (!desc->byte_code) return WINED3DERR_INVALIDCALL;
- if (!(shader->frontend = shader_select_frontend(desc->format))) - { - FIXME("Unable to find frontend for shader.\n"); - return WINED3DERR_INVALIDCALL; - } - shader->ref = 1; shader->device = device; shader->parent = parent; shader->parent_ops = parent_ops;
- if (FAILED(hr = shader_copy_signatures_from_shader_desc(shader, desc))) - return hr; - list_init(&shader->linked_programs); list_init(&shader->constantsF); list_init(&shader->constantsB); @@ -3724,40 +3716,78 @@ static HRESULT shader_init(struct wined3d_shader *shader, struct wined3d_device list_init(&shader->reg_maps.indexable_temps); list_init(&shader->shader_list_entry);
- byte_code_size = desc->byte_code_size; - if (byte_code_size == ~(size_t)0) + format = desc->format; + if (format == WINED3D_SHADER_BYTE_CODE_FORMAT_DXBC) { - const struct wined3d_shader_frontend *fe = shader->frontend; - struct wined3d_shader_version shader_version; - struct wined3d_shader_instruction ins; - const DWORD *ptr; - void *fe_data; - - if (!(fe_data = fe->shader_init(desc->byte_code, byte_code_size, &shader->output_signature))) + if (!(shader->byte_code = heap_alloc(desc->byte_code_size))) { - WARN("Failed to initialise frontend data.\n"); - shader_cleanup(shader); - return WINED3DERR_INVALIDCALL; + hr = E_OUTOFMEMORY; + goto fail; } + memcpy(shader->byte_code, desc->byte_code, desc->byte_code_size); + shader->byte_code_size = desc->byte_code_size;
- fe->shader_read_header(fe_data, &ptr, &shader_version); - while (!fe->shader_is_end(fe_data, &ptr)) - fe->shader_read_instruction(fe_data, &ptr, &ins); - - fe->shader_free(fe_data); + max_version = shader_max_version_from_feature_level(device->feature_level); + if (FAILED(hr = shader_extract_from_dxbc(shader, max_version, &format))) + goto fail; + } + else if (FAILED(hr = shader_copy_signatures_from_shader_desc(shader, desc))) + { + goto fail; + }
- byte_code_size = (ptr - desc->byte_code) * sizeof(*ptr); + if (!(shader->frontend = shader_select_frontend(format))) + { + FIXME("Unable to find frontend for shader.\n"); + hr = WINED3DERR_INVALIDCALL; + goto fail; }
- if (!(shader->function = heap_alloc(byte_code_size))) + if (!shader->byte_code) { - shader_cleanup(shader); - return E_OUTOFMEMORY; + size_t byte_code_size = desc->byte_code_size; + + if (byte_code_size == ~(size_t)0) + { + const struct wined3d_shader_frontend *fe = shader->frontend; + struct wined3d_shader_version shader_version; + struct wined3d_shader_instruction ins; + const DWORD *ptr; + void *fe_data; + + if (!(fe_data = fe->shader_init(desc->byte_code, byte_code_size, &shader->output_signature))) + { + WARN("Failed to initialise frontend data.\n"); + hr = WINED3DERR_INVALIDCALL; + goto fail; + } + + fe->shader_read_header(fe_data, &ptr, &shader_version); + while (!fe->shader_is_end(fe_data, &ptr)) + fe->shader_read_instruction(fe_data, &ptr, &ins); + + fe->shader_free(fe_data); + + byte_code_size = (ptr - desc->byte_code) * sizeof(*ptr); + } + + if (!(shader->byte_code = heap_alloc(byte_code_size))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + memcpy(shader->byte_code, desc->byte_code, byte_code_size); + shader->byte_code_size = byte_code_size; + + shader->function = shader->byte_code; + shader->functionLength = shader->byte_code_size; } - memcpy(shader->function, desc->byte_code, byte_code_size); - shader->functionLength = byte_code_size;
return hr; + +fail: + shader_cleanup(shader); + return hr; }
static HRESULT vertex_shader_init(struct wined3d_shader *shader, struct wined3d_device *device, @@ -3820,7 +3850,6 @@ static HRESULT geometry_shader_init_stream_output(struct wined3d_shader *shader, { case WINED3D_SHADER_TYPE_VERTEX: case WINED3D_SHADER_TYPE_DOMAIN: - heap_free(shader->function); shader->function = NULL; shader->functionLength = 0; break; diff --git a/dlls/wined3d/shader_sm4.c b/dlls/wined3d/shader_sm4.c index 7841686b8b4b..5f9dbecfebaa 100644 --- a/dlls/wined3d/shader_sm4.c +++ b/dlls/wined3d/shader_sm4.c @@ -1774,3 +1774,332 @@ const struct wined3d_shader_frontend sm4_shader_frontend = shader_sm4_read_instruction, shader_sm4_is_end, }; + +#define TAG_AON9 WINEMAKEFOURCC('A', 'o', 'n', '9') +#define TAG_DXBC WINEMAKEFOURCC('D', 'X', 'B', 'C') +#define TAG_ISGN WINEMAKEFOURCC('I', 'S', 'G', 'N') +#define TAG_OSG5 WINEMAKEFOURCC('O', 'S', 'G', '5') +#define TAG_OSGN WINEMAKEFOURCC('O', 'S', 'G', 'N') +#define TAG_PCSG WINEMAKEFOURCC('P', 'C', 'S', 'G') +#define TAG_SHDR WINEMAKEFOURCC('S', 'H', 'D', 'R') +#define TAG_SHEX WINEMAKEFOURCC('S', 'H', 'E', 'X') + +struct aon9_header +{ + DWORD chunk_size; + DWORD shader_version; + DWORD unknown; + DWORD byte_code_offset; +}; + +struct shader_handler_context +{ + struct wined3d_shader *shader; + enum wined3d_shader_byte_code_format *format; + unsigned int max_version; +}; + +static void read_dword(const char **ptr, DWORD *d) +{ + memcpy(d, *ptr, sizeof(*d)); + *ptr += sizeof(*d); +} + +static BOOL require_space(size_t offset, size_t count, size_t size, size_t data_size) +{ + return !count || (data_size - offset) / count >= size; +} + +static void skip_dword_unknown(const char **ptr, unsigned int count) +{ + unsigned int i; + DWORD d; + + WARN("Skipping %u unknown DWORDs:\n", count); + for (i = 0; i < count; ++i) + { + read_dword(ptr, &d); + WARN("\t0x%08x\n", d); + } +} + +static HRESULT parse_dxbc(const char *data, SIZE_T data_size, + HRESULT (*chunk_handler)(const char *data, DWORD data_size, DWORD tag, void *ctx), void *ctx) +{ + const char *ptr = data; + HRESULT hr = S_OK; + DWORD chunk_count; + DWORD total_size; + unsigned int i; + DWORD version; + DWORD tag; + + read_dword(&ptr, &tag); + TRACE("tag: %s.\n", debugstr_an((const char *)&tag, 4)); + + if (tag != TAG_DXBC) + { + WARN("Wrong tag.\n"); + return E_INVALIDARG; + } + + WARN("Ignoring DXBC checksum.\n"); + skip_dword_unknown(&ptr, 4); + + read_dword(&ptr, &version); + TRACE("version: %#x.\n", version); + if (version != 0x00000001) + { + WARN("Got unexpected DXBC version %#x.\n", version); + return E_INVALIDARG; + } + + read_dword(&ptr, &total_size); + TRACE("total size: %#x\n", total_size); + + read_dword(&ptr, &chunk_count); + TRACE("chunk count: %#x\n", chunk_count); + + for (i = 0; i < chunk_count; ++i) + { + DWORD chunk_tag, chunk_size; + const char *chunk_ptr; + DWORD chunk_offset; + + read_dword(&ptr, &chunk_offset); + TRACE("chunk %u at offset %#x\n", i, chunk_offset); + + if (chunk_offset >= data_size || !require_space(chunk_offset, 2, sizeof(DWORD), data_size)) + { + WARN("Invalid chunk offset %#x (data size %#lx).\n", chunk_offset, data_size); + return E_FAIL; + } + + chunk_ptr = data + chunk_offset; + + read_dword(&chunk_ptr, &chunk_tag); + read_dword(&chunk_ptr, &chunk_size); + + if (!require_space(chunk_ptr - data, 1, chunk_size, data_size)) + { + WARN("Invalid chunk size %#x (data size %#lx, chunk offset %#x).\n", + chunk_size, data_size, chunk_offset); + return E_FAIL; + } + + if (FAILED(hr = chunk_handler(chunk_ptr, chunk_size, chunk_tag, ctx))) + break; + } + + return hr; +} + +static const char *shader_get_string(const char *data, size_t data_size, DWORD offset) +{ + size_t len, max_len; + + if (offset >= data_size) + { + WARN("Invalid offset %#x (data size %#lx).\n", offset, (long)data_size); + return NULL; + } + + max_len = data_size - offset; + len = strnlen(data + offset, max_len); + + if (len == max_len) + return NULL; + + return data + offset; +} + +static HRESULT shader_parse_signature(DWORD tag, const char *data, DWORD data_size, + struct wined3d_shader_signature *s) +{ + struct wined3d_shader_signature_element *e; + const char *ptr = data; + unsigned int i; + DWORD count; + + if (!require_space(0, 2, sizeof(DWORD), data_size)) + { + WARN("Invalid data size %#x.\n", data_size); + return E_INVALIDARG; + } + + read_dword(&ptr, &count); + TRACE("%u elements.\n", count); + + skip_dword_unknown(&ptr, 1); /* It seems to always be 0x00000008. */ + + if (!require_space(ptr - data, count, 6 * sizeof(DWORD), data_size)) + { + WARN("Invalid count %#x (data size %#x).\n", count, data_size); + return E_INVALIDARG; + } + + if (!(e = heap_calloc(count, sizeof(*e)))) + { + ERR("Failed to allocate input signature memory.\n"); + return E_OUTOFMEMORY; + } + + for (i = 0; i < count; ++i) + { + DWORD name_offset; + + if (tag == TAG_OSG5) + read_dword(&ptr, &e[i].stream_idx); + else + e[i].stream_idx = 0; + read_dword(&ptr, &name_offset); + if (!(e[i].semantic_name = shader_get_string(data, data_size, name_offset))) + { + WARN("Invalid name offset %#x (data size %#x).\n", name_offset, data_size); + heap_free(e); + return E_INVALIDARG; + } + read_dword(&ptr, &e[i].semantic_idx); + read_dword(&ptr, &e[i].sysval_semantic); + read_dword(&ptr, &e[i].component_type); + read_dword(&ptr, &e[i].register_idx); + read_dword(&ptr, &e[i].mask); + + TRACE("Stream: %u, semantic: %s, semantic idx: %u, sysval_semantic %#x, " + "type %u, register idx: %u, use_mask %#x, input_mask %#x.\n", + e[i].stream_idx, debugstr_a(e[i].semantic_name), e[i].semantic_idx, e[i].sysval_semantic, + e[i].component_type, e[i].register_idx, (e[i].mask >> 8) & 0xff, e[i].mask & 0xff); + } + + s->elements = e; + s->element_count = count; + + return S_OK; +} + +static HRESULT shader_dxbc_chunk_handler(const char *data, DWORD data_size, DWORD tag, void *context) +{ + struct shader_handler_context *ctx = context; + struct wined3d_shader *shader = ctx->shader; + HRESULT hr; + + switch (tag) + { + case TAG_ISGN: + if (ctx->max_version < 4) + { + TRACE("Skipping shader input signature.\n"); + break; + } + if (shader->input_signature.elements) + { + FIXME("Multiple input signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->input_signature))) + return hr; + break; + + case TAG_OSGN: + case TAG_OSG5: + if (ctx->max_version < 4) + { + TRACE("Skipping shader output signature.\n"); + break; + } + if (shader->output_signature.elements) + { + FIXME("Multiple output signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->output_signature))) + return hr; + break; + + case TAG_PCSG: + if (shader->patch_constant_signature.elements) + { + FIXME("Multiple patch constant signatures.\n"); + break; + } + if (FAILED(hr = shader_parse_signature(tag, data, data_size, &shader->patch_constant_signature))) + return hr; + break; + + case TAG_SHDR: + case TAG_SHEX: + if (ctx->max_version < 4) + { + TRACE("Skipping SM4+ shader.\n"); + break; + } + if (shader->function) + FIXME("Multiple shader code chunks.\n"); + shader->function = (const DWORD *)data; + shader->functionLength = data_size; + *ctx->format = WINED3D_SHADER_BYTE_CODE_FORMAT_SM4; + break; + + case TAG_AON9: + if (ctx->max_version < 4) + { + const struct aon9_header *header = (const struct aon9_header *)data; + unsigned int unknown_dword_count; + const char *byte_code; + + if (data_size < sizeof(*header)) + { + WARN("Invalid Aon9 data size %#x.\n", data_size); + return E_FAIL; + } + byte_code = data + header->byte_code_offset; + unknown_dword_count = (header->byte_code_offset - sizeof(*header)) / sizeof(DWORD); + + if (data_size - 2 * sizeof(DWORD) < header->byte_code_offset) + { + WARN("Invalid byte code offset %#x (size %#x).\n", header->byte_code_offset, data_size); + return E_FAIL; + } + FIXME("Skipping %u unknown DWORDs.\n", unknown_dword_count); + + if (shader->function) + FIXME("Multiple shader code chunks.\n"); + shader->function = (const DWORD *)byte_code; + shader->functionLength = data_size - header->byte_code_offset; + *ctx->format = WINED3D_SHADER_BYTE_CODE_FORMAT_SM1; + TRACE("Feature level 9 shader version 0%08x, 0%08x.\n", + header->shader_version, *shader->function); + } + else + { + TRACE("Skipping feature level 9 shader code.\n"); + } + break; + + default: + TRACE("Skipping chunk %s.\n", debugstr_an((const char *)&tag, 4)); + break; + } + + return S_OK; +} + +HRESULT shader_extract_from_dxbc(struct wined3d_shader *shader, + unsigned int max_shader_version, enum wined3d_shader_byte_code_format *format) +{ + struct shader_handler_context ctx; + HRESULT hr; + + ctx.shader = shader; + ctx.format = format; + ctx.max_version = max_shader_version; + + hr = parse_dxbc(shader->byte_code, shader->byte_code_size, shader_dxbc_chunk_handler, &ctx); + if (!shader->function) + hr = E_INVALIDARG; + + if (FAILED(hr)) + WARN("Failed to parse DXBC, hr %#x.\n", hr); + + return hr; +} diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 9baefa6184dd..221b9472aa67 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -1253,6 +1253,9 @@ struct wined3d_shader_frontend extern const struct wined3d_shader_frontend sm1_shader_frontend DECLSPEC_HIDDEN; extern const struct wined3d_shader_frontend sm4_shader_frontend DECLSPEC_HIDDEN;
+HRESULT shader_extract_from_dxbc(struct wined3d_shader *shader, + unsigned int max_shader_version, enum wined3d_shader_byte_code_format *format); + typedef void (*SHADER_HANDLER)(const struct wined3d_shader_instruction *);
#define WINED3D_SHADER_CAP_VS_CLIPPING 0x00000001 @@ -4080,8 +4083,10 @@ struct wined3d_shader { LONG ref; const struct wined3d_shader_limits *limits; - DWORD *function; - UINT functionLength; + const DWORD *function; + unsigned int functionLength; + void *byte_code; + unsigned int byte_code_size; BOOL load_local_constsF; const struct wined3d_shader_frontend *frontend; void *frontend_data; diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index cf44ca64a409..297192f7c703 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -858,6 +858,7 @@ enum wined3d_shader_byte_code_format { WINED3D_SHADER_BYTE_CODE_FORMAT_SM1 = 0, WINED3D_SHADER_BYTE_CODE_FORMAT_SM4 = 1, + WINED3D_SHADER_BYTE_CODE_FORMAT_DXBC = 2, };
enum wined3d_shader_type