[PATCH 1/3] wined3d: Accept full DXBC.

Józef Kucia jkucia at codeweavers.com
Fri Oct 5 07:10:17 CDT 2018


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 at 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




More information about the wine-devel mailing list