On Mon, Nov 8, 2021 at 1:30 PM Nikolay Sivov <nsivov(a)codeweavers.com> wrote:
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> ---
v3: some rework based on received feedback
For now for simplicity I removed destination index from property update, with additional test for property type when parsing.
I like the changes. Unfortunately I keep finding stuff... Only one important comment, see below.
dlls/d3d10/effect.c | 577 +++++++++++++++++++++++++++++++++++++- dlls/d3d10/tests/effect.c | 114 ++++++++ 2 files changed, 683 insertions(+), 8 deletions(-)
diff --git a/dlls/d3d10/effect.c b/dlls/d3d10/effect.c index 78a3d0c386a..1be16771921 100644 --- a/dlls/d3d10/effect.c +++ b/dlls/d3d10/effect.c
+static float * d3d10_effect_preshader_get_reg_ptr(const struct d3d10_effect_preshader *p, + enum d3d10_reg_table_type regt, unsigned int offset) +{ + switch (regt) + { + case D3D10_REG_TABLE_CONSTANTS: + case D3D10_REG_TABLE_CB: + case D3D10_REG_TABLE_RESULT: + case D3D10_REG_TABLE_TEMP: + return p->reg_tables[regt].f + offset; + default: + return NULL; + } +}
I think we want to validate the offset (for the _CONSTANTS table in particular, since the other 3 are sized to fit all the accesses). Not sure this is the best place for that but, if so, it might look something like: case D3D10_REG_TABLE_CB: case D3D10_REG_TABLE_RESULT: case D3D10_REG_TABLE_TEMP: + if (offset / sizeof(*p->reg_tables[regt].f) >= p->reg_tables[regt].count + || (offset + sizeof(*p->reg_tables[regt].f)) / sizeof(*p->reg_tables[regt].f) + > p->reg_tables[regt].count) + { + WARN("Invalid table offset %u.\n", offset); + offset = 0; + } return p->reg_tables[regt].f + offset; default: + WARN("Unexpected register type %#x.\n", regt); return NULL; } } (I expect it to get wrapped badly but hopefully it'll still be readable)
+ +static HRESULT d3d10_effect_preshader_eval(struct d3d10_effect_preshader *p) +{ + unsigned int i, j, regt, offset, instr_count, input_count; + const DWORD *ip = ID3D10Blob_GetBufferPointer(p->code); + float *dst, *args[4], *retval; + struct preshader_instr ins; + + dst = d3d10_effect_preshader_get_reg_ptr(p, D3D10_REG_TABLE_RESULT, 0); + memset(dst, 0, sizeof(float) * p->reg_tables[D3D10_REG_TABLE_RESULT].count); + + /* Update constant buffer */ + dst = d3d10_effect_preshader_get_reg_ptr(p, D3D10_REG_TABLE_CB, 0); + for (i = 0; i < p->vars_count; ++i) + { + struct d3d10_ctab_var *v = &p->vars[i]; + memcpy(dst + v->offset, v->v->buffer->u.buffer.local_buffer, v->length * sizeof(*dst));
This is the one I mentioned at the top. Does this need to take v->v->buffer_offset into account for the memcpy() source argument?
+static HRESULT parse_fx10_fxlc(void *ctx, const char *data, unsigned int data_size) +{ + struct d3d10_preshader_parse_context *context = ctx; + struct d3d10_effect_preshader *p = context->preshader; + unsigned int i, offset = 4; + uint32_t ins_count; + const char *ptr; + HRESULT hr; + + if (data_size % sizeof(uint32_t)) + { + WARN("FXLC size misaligned %u.\n", data_size); + return E_FAIL; + } + + /* Parse through bytecode copy, so we can patch opcodes. */
This comment is not actual anymore. I think we can just get rid of it.
+static HRESULT parse_fx10_ctab(void *ctx, const char *data, unsigned int data_size) +{ + struct d3d10_preshader_parse_context *context = ctx; + struct d3d10_effect_preshader *p = context->preshader; + struct ctab_header + { + DWORD size; + DWORD creator; + DWORD version; + DWORD constants; + DWORD constantinfo; + DWORD flags; + DWORD target; + } header; + struct ctab_const_info + { + DWORD name; + WORD register_set; + WORD register_index; + WORD register_count; + WORD reserved; + DWORD typeinfo; + DWORD default_value; + } *info; + unsigned int i, cb_reg_count = 0; + const char *ptr = data; + const char *name; + size_t name_len; + HRESULT hr; + + if (data_size < sizeof(header)) + { + WARN("Invalid constant table size %u.\n", data_size); + return E_FAIL; + } + + read_dword(&ptr, &header.size); + read_dword(&ptr, &header.creator); + read_dword(&ptr, &header.version); + read_dword(&ptr, &header.constants); + read_dword(&ptr, &header.constantinfo); + read_dword(&ptr, &header.flags); + read_dword(&ptr, &header.target); + + if (!require_space(header.constantinfo, header.constants, sizeof(*info), data_size)) + { + WARN("Invalid constant info section offset %#x.\n", header.constantinfo); + return E_FAIL; + } + + p->vars_count = header.constants; + + TRACE("Variable count %u.\n", p->vars_count); + + if (!(p->vars = heap_calloc(p->vars_count, sizeof(*p->vars)))) + return E_OUTOFMEMORY; + + /* Collect variables used in expression. */
That's somewhat specific to INDEX_EXPRESSION preshaders. I'd just leave out the comment, the code is pretty self-explanatory anyway.
+ info = (struct ctab_const_info *)(data + header.constantinfo); + for (i = 0; i < p->vars_count; ++i, ++info) + { + if (!fx10_get_string(data, data_size, info->name, &name, &name_len)) + return E_FAIL; + + if (!(p->vars[i].v = d3d10_effect_get_variable_by_name(context->effect, name))) + { + WARN("Couldn't find variable %s.\n", debugstr_a(name)); + return E_FAIL; + } + + /* 4 components per register */ + p->vars[i].offset = info->register_index * 4; + p->vars[i].length = info->register_count * 4; + + cb_reg_count = max(cb_reg_count, info->register_index + info->register_count); + } + + /* Allocate contiguous "constant buffer" for all referenced variables. */ + if (FAILED(hr = d3d10_reg_table_allocate(&p->reg_tables[D3D10_REG_TABLE_CB], cb_reg_count * 4))) + { + WARN("Failed to allocate variables buffer.\n"); + return hr; + } + + return S_OK; +}
@@ -2051,14 +2545,75 @@ static HRESULT parse_fx10_property_assignment(const char *data, size_t data_size dep.id = id; dep.idx = idx; dep.operation = operation; - dep.u.var.v = variable; - dep.u.var.offset = offset; + dep.var.v = variable; + dep.var.offset = offset;
return d3d10_effect_add_prop_dependency(d, &dep); }
break;
+ case D3D10_EOO_INDEX_EXPRESSION: + + /* Variable, and an expression for its index. */ + if (value_offset >= data_size || !require_space(value_offset, 2, sizeof(DWORD), data_size)) + { + WARN("Invalid offset %#x (data size %#lx).\n", value_offset, (long)data_size); + return E_FAIL; + } + + data_ptr = data + value_offset; + read_dword(&data_ptr, &value_offset); + read_dword(&data_ptr, &code_offset); + + if (!fx10_get_string(data, data_size, value_offset, &name, &name_len)) + { + WARN("Failed to get variable name.\n"); + return E_FAIL; + } + + TRACE("Variable name %s[<expr>].\n", debugstr_a(name)); + + if (!(variable = d3d10_effect_get_variable_by_name(effect, name))) + { + WARN("Couldn't find variable %s.\n", debugstr_a(name)); + return E_FAIL; + } + + /* Has to be an array */ + if (!variable->type->element_count) + { + WARN("Expected array variable.\n"); + return E_FAIL; + }
This comment can also go IMO.
+ + if (!is_object_property(property_info)) + { + WARN("Expected object type property used with indexed expression.\n"); + return E_FAIL; + } + + if (code_offset >= data_size || !require_space(code_offset, 1, sizeof(DWORD), data_size)) + { + WARN("Invalid offset %#x (data size %#lx).\n", value_offset, (long)data_size); + return E_FAIL; + } + + data_ptr = data + code_offset; + read_dword(&data_ptr, &blob_size); + + dep.id = id; + dep.idx = idx; + dep.operation = operation; + dep.index_expr.v = variable; + if (FAILED(hr = parse_fx10_preshader(data_ptr, blob_size, effect, &dep.index_expr.index))) + { + WARN("Failed to parse preshader, hr %#x.\n", hr); + return hr; + } + + return d3d10_effect_add_prop_dependency(d, &dep); + case D3D10_EOO_ANONYMOUS_SHADER:
/* Anonymous shader */