wined3d: Accept full DXBC.

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>
Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Józef Kucia 2018-10-05 14:10:17 +02:00 committed by Alexandre Julliard
parent c4c5353820
commit 72ea1419d5
4 changed files with 406 additions and 42 deletions

View File

@ -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,39 +3716,77 @@ 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;
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;
}
if (!(shader->frontend = shader_select_frontend(format)))
{
FIXME("Unable to find frontend for shader.\n");
hr = WINED3DERR_INVALIDCALL;
goto fail;
}
if (!shader->byte_code)
{
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);
}
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);
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;
fe->shader_free(fe_data);
byte_code_size = (ptr - desc->byte_code) * sizeof(*ptr);
shader->function = shader->byte_code;
shader->functionLength = shader->byte_code_size;
}
if (!(shader->function = heap_alloc(byte_code_size)))
{
shader_cleanup(shader);
return E_OUTOFMEMORY;
}
memcpy(shader->function, desc->byte_code, byte_code_size);
shader->functionLength = byte_code_size;
return hr;
fail:
shader_cleanup(shader);
return hr;
}
@ -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;

View File

@ -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;
}

View File

@ -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;

View File

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