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:
parent
c4c5353820
commit
72ea1419d5
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue