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(a)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
--
2.16.4