From: Paul Gofman Subject: [v2 1/2] d3dx9: Support relative addressing in preshader. Message-Id: <20170324221219.10745-1-gofmanp@gmail.com> Date: Sat, 25 Mar 2017 01:12:18 +0300 Signed-off-by: Paul Gofman --- v2: - created struct d3dx_pres_reg to store together table and offset. It is identical to unpatched version of d3dx_pres_operand, while d3dx_pres_operand now has two d3dx_pres_reg structures; - renamed base_reg to index_reg in d3dx_pres_operand; - fixed a bug (mismatch with native implementation) when offset to base register table is nonzero; - removed '(offset & 255)' special case in index wrap to bounds. I now got the test failure with that in extended tests. It seems that it was helping me to fix some of my tests before I figured out the true offset wrap size, though that was wrong; - refactored out of bounds index checking code; - fixed possible crash in parse_pres_arg(); - used lrint() for getting register value; - added some comments to index wrap logic; --- dlls/d3dx9_36/preshader.c | 153 ++++++++++++++++++++++++++++++++++--------- dlls/d3dx9_36/tests/effect.c | 38 +++++------ 2 files changed, 140 insertions(+), 51 deletions(-) diff --git a/dlls/d3dx9_36/preshader.c b/dlls/d3dx9_36/preshader.c index 9aedc99..395bfe1 100644 --- a/dlls/d3dx9_36/preshader.c +++ b/dlls/d3dx9_36/preshader.c @@ -185,7 +185,7 @@ static const enum pres_reg_tables shad_regset2table[] = PRES_REGTAB_COUNT, /* D3DXRS_SAMPLER */ }; -struct d3dx_pres_operand +struct d3dx_pres_reg { enum pres_reg_tables table; /* offset is component index, not register index, e. g. @@ -193,6 +193,12 @@ struct d3dx_pres_operand unsigned int offset; }; +struct d3dx_pres_operand +{ + struct d3dx_pres_reg reg; + struct d3dx_pres_reg index_reg; +}; + #define MAX_INPUTS_COUNT 8 struct d3dx_pres_ins @@ -368,7 +374,7 @@ static unsigned int *find_bytecode_comment(unsigned int *ptr, unsigned int count return NULL; } -static unsigned int *parse_pres_arg(unsigned int *ptr, unsigned int count, struct d3dx_pres_operand *opr) +static unsigned int *parse_pres_reg(unsigned int *ptr, struct d3dx_pres_reg *reg) { static const enum pres_reg_tables reg_table[8] = { @@ -376,29 +382,46 @@ static unsigned int *parse_pres_arg(unsigned int *ptr, unsigned int count, struc PRES_REGTAB_OCONST, PRES_REGTAB_OBCONST, PRES_REGTAB_OICONST, PRES_REGTAB_TEMP }; - if (count < 3) + if (*ptr >= ARRAY_SIZE(reg_table) || reg_table[*ptr] == PRES_REGTAB_COUNT) { - WARN("Byte code buffer ends unexpectedly.\n"); + FIXME("Unsupported register table %#x.\n", *ptr); return NULL; } - if (*ptr) + reg->table = reg_table[*ptr++]; + reg->offset = *ptr++; + return ptr; +} + +static unsigned int *parse_pres_arg(unsigned int *ptr, unsigned int count, struct d3dx_pres_operand *opr) +{ + if (count < 3 || (*ptr && count < 5)) { - FIXME("Relative addressing not supported yet, word %#x.\n", *ptr); + WARN("Byte code buffer ends unexpectedly, count %u.\n", count); return NULL; } - ++ptr; - if (*ptr >= ARRAY_SIZE(reg_table) || reg_table[*ptr] == PRES_REGTAB_COUNT) + if (*ptr) { - FIXME("Unsupported register table %#x.\n", *ptr); - return NULL; + if (*ptr != 1) + { + FIXME("Unknown relative addressing flag, word %#x.\n", *ptr); + return NULL; + } + ptr = parse_pres_reg(ptr + 1, &opr->index_reg); + if (!ptr) + return NULL; } - opr->table = reg_table[*ptr++]; - opr->offset = *ptr++; + else + { + opr->index_reg.table = PRES_REGTAB_COUNT; + ++ptr; + } + + ptr = parse_pres_reg(ptr, &opr->reg); - if (opr->table == PRES_REGTAB_OBCONST) - opr->offset /= 4; + if (opr->reg.table == PRES_REGTAB_OBCONST) + opr->reg.offset /= 4; return ptr; } @@ -452,6 +475,12 @@ static unsigned int *parse_pres_ins(unsigned int *ptr, unsigned int count, struc ptr = p; } ptr = parse_pres_arg(ptr, count, &ins->output); + if (ins->output.index_reg.table != PRES_REGTAB_COUNT) + { + FIXME("Relative addressing in output register not supported.\n"); + return NULL; + } + return ptr; } @@ -566,20 +595,33 @@ static void dump_arg(struct d3dx_regstore *rs, const struct d3dx_pres_operand *a static const char *xyzw_str = "xyzw"; unsigned int i, table; - table = arg->table; - if (table == PRES_REGTAB_IMMED) + table = arg->reg.table; + if (table == PRES_REGTAB_IMMED && arg->index_reg.table == PRES_REGTAB_COUNT) { TRACE("("); for (i = 0; i < component_count; ++i) TRACE(i < component_count - 1 ? "%.16e, " : "%.16e", - ((double *)rs->tables[PRES_REGTAB_IMMED])[arg->offset + i]); + ((double *)rs->tables[PRES_REGTAB_IMMED])[arg->reg.offset + i]); TRACE(")"); } else { - TRACE("%s%u.", table_symbol[table], get_reg_offset(table, arg->offset)); + if (arg->index_reg.table == PRES_REGTAB_COUNT) + { + TRACE("%s%u.", table_symbol[table], get_reg_offset(table, arg->reg.offset)); + } + else + { + unsigned int index_reg; + + index_reg = get_reg_offset(arg->index_reg.table, arg->index_reg.offset); + TRACE("%s[%u + %s%u.%c].", table_symbol[table], get_reg_offset(table, arg->reg.offset), + table_symbol[arg->index_reg.table], index_reg, + xyzw_str[arg->index_reg.offset + - index_reg * table_info[arg->index_reg.table].reg_component_count]); + } for (i = 0; i < component_count; ++i) - TRACE("%c", xyzw_str[(arg->offset + i) % 4]); + TRACE("%c", xyzw_str[(arg->reg.offset + i) % 4]); } } @@ -697,13 +739,13 @@ static HRESULT parse_preshader(struct d3dx_preshader *pres, unsigned int *ptr, u for (i = 0; i < pres->ins_count; ++i) { for (j = 0; j < pres_op_info[pres->ins[i].op].input_count; ++j) - update_table_size(pres->regs.table_sizes, pres->ins[i].inputs[j].table, - get_reg_offset(pres->ins[i].inputs[j].table, - pres->ins[i].inputs[j].offset + pres->ins[i].component_count - 1)); + update_table_size(pres->regs.table_sizes, pres->ins[i].inputs[j].reg.table, + get_reg_offset(pres->ins[i].inputs[j].reg.table, + pres->ins[i].inputs[j].reg.offset + pres->ins[i].component_count - 1)); - update_table_size(pres->regs.table_sizes, pres->ins[i].output.table, - get_reg_offset(pres->ins[i].output.table, - pres->ins[i].output.offset + pres->ins[i].component_count - 1)); + update_table_size(pres->regs.table_sizes, pres->ins[i].output.reg.table, + get_reg_offset(pres->ins[i].output.reg.table, + pres->ins[i].output.reg.offset + pres->ins[i].component_count - 1)); } update_table_sizes_consts(pres->regs.table_sizes, &pres->inputs); if (FAILED(regstore_alloc_table(&pres->regs, PRES_REGTAB_IMMED))) @@ -1088,18 +1130,65 @@ static HRESULT init_set_constants(struct d3dx_const_tab *const_tab, ID3DXConstan return ret; } +static double exec_get_reg_value(struct d3dx_regstore *rs, enum pres_reg_tables table, unsigned int offset) +{ + if (!regstore_is_val_set_reg(rs, table, offset / table_info[table].reg_component_count)) + WARN("Using uninitialized input, table %u, offset %u.\n", table, offset); + + return regstore_get_double(rs, table, offset); +} + static double exec_get_arg(struct d3dx_regstore *rs, const struct d3dx_pres_operand *opr, unsigned int comp) { - if (!regstore_is_val_set_reg(rs, opr->table, (opr->offset + comp) / table_info[opr->table].reg_component_count)) - WARN("Using uninitialized input, table %u, offset %u.\n", opr->table, opr->offset + comp); + unsigned int offset, base_index, reg_index, table; + + table = opr->reg.table; + + if (opr->index_reg.table == PRES_REGTAB_COUNT) + base_index = 0; + else + base_index = lrint(exec_get_reg_value(rs, opr->index_reg.table, opr->index_reg.offset)); + + /* '4' is used instead of reg_component_count, as immediate constants (which have + * reg_component_count of 1) are still indexed as 4 values according to the tests. + */ + offset = base_index * 4 + opr->reg.offset + comp; + reg_index = offset / table_info[table].reg_component_count; + + if (reg_index >= rs->table_sizes[table]) + { + unsigned int wrap_size; + + if (table == PRES_REGTAB_CONST) + { + /* As it can be guessed from tests (by iterating base register value and + * observing the period of result values repeat), offset into floating + * constant table are wrapped to the nearest power of 2 and not to the + * actual table size. + */ + for (wrap_size = 1; wrap_size < rs->table_sizes[table]; wrap_size <<= 1) + ; + } + else + { + wrap_size = rs->table_sizes[table]; + } + reg_index %= wrap_size; + + if (reg_index >= rs->table_sizes[table]) + return 0.0; + + offset = reg_index * table_info[table].reg_component_count + + offset % table_info[table].reg_component_count; + } - return regstore_get_double(rs, opr->table, opr->offset + comp); + return exec_get_reg_value(rs, table, offset); } -static void exec_set_arg(struct d3dx_regstore *rs, const struct d3dx_pres_operand *opr, +static void exec_set_arg(struct d3dx_regstore *rs, const struct d3dx_pres_reg *reg, unsigned int comp, double res) { - regstore_set_double(rs, opr->table, opr->offset + comp, res); + regstore_set_double(rs, reg->table, reg->offset + comp, res); } #define ARGS_ARRAY_SIZE 8 @@ -1130,7 +1219,7 @@ static HRESULT execute_preshader(struct d3dx_preshader *pres) res = oi->func(args, ins->component_count); /* only 'dot' instruction currently falls here */ - exec_set_arg(&pres->regs, &ins->output, 0, res); + exec_set_arg(&pres->regs, &ins->output.reg, 0, res); } else { @@ -1139,7 +1228,7 @@ static HRESULT execute_preshader(struct d3dx_preshader *pres) for (k = 0; k < oi->input_count; ++k) args[k] = exec_get_arg(&pres->regs, &ins->inputs[k], ins->scalar_op && !k ? 0 : j); res = oi->func(args, ins->component_count); - exec_set_arg(&pres->regs, &ins->output, j, res); + exec_set_arg(&pres->regs, &ins->output.reg, j, res); } } } diff --git a/dlls/d3dx9_36/tests/effect.c b/dlls/d3dx9_36/tests/effect.c index 54f5377..622a8d5 100644 --- a/dlls/d3dx9_36/tests/effect.c +++ b/dlls/d3dx9_36/tests/effect.c @@ -3971,7 +3971,7 @@ test_effect_preshader_op_expected[] = {"prec", {FALSE, FALSE, TRUE, FALSE}, {0x2b8cbccc, 0x2c0cbccc, 0xac531800, 0x00000000}}, {"dotswiz", {FALSE, FALSE, FALSE, FALSE}, {0xc00ccccd, 0xc0d33334, 0xc10ccccd, 0}}, - {"reladdr", { TRUE, TRUE, TRUE, TRUE}, {0xc00ccccd, 0x40000000, 0x41a00000, 0x41500000}}, + {"reladdr", {FALSE, FALSE, FALSE, FALSE}, {0xc00ccccd, 0x40000000, 0x41a00000, 0x41500000}}, }; enum expected_state_update @@ -4207,7 +4207,7 @@ static void test_effect_preshader(IDirect3DDevice9 *device) ok(hr == D3D_OK, "SetVector failed, hr %#x.\n", hr); hr = effect->lpVtbl->BeginPass(effect, 0); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->BeginPass(effect, 0); ok(hr == D3DERR_INVALIDCALL, "Got result %#x.\n", hr); @@ -4375,7 +4375,7 @@ static void test_preshader_op(IDirect3DDevice9 *device, const DWORD *sample_effe } hr = effect->lpVtbl->BeginPass(effect, 0); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetLight(device, blob_position[test->args_count].result_index, &light); ok(hr == D3D_OK, "Got result %#x.\n", hr); @@ -4674,7 +4674,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->BeginPass(effect, 0); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); for (i = 0; i < ARRAY_SIZE(check_op_parameters); ++i) { @@ -4692,7 +4692,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, &fvect, sizeof(fvect)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_op_results(device, check_op_parameters[i].state_updated, check_op_parameters[i].param_name); @@ -4710,7 +4710,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, buffer, sizeof(buffer)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[i].const_updated_mask, check_vconsts_parameters[i].param_name); @@ -4723,7 +4723,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetVectorArray(effect, param, &fvect, 1); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[0].const_updated_mask, check_vconsts_parameters[0].param_name); @@ -4735,7 +4735,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetFloat(effect, param, 92.0f); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, const_no_update_mask, check_vconsts_parameters[10].param_name); @@ -4748,7 +4748,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, &fvect.x, sizeof(fvect.x)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[10].const_updated_mask, check_vconsts_parameters[10].param_name); @@ -4759,7 +4759,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetFloatArray(effect, param, &fvect.x, 1); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[10].const_updated_mask, check_vconsts_parameters[10].param_name); @@ -4771,7 +4771,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetInt(effect, param, 93); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, const_no_update_mask, check_vconsts_parameters[10].param_name); @@ -4782,7 +4782,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetVector(effect, param, &fvect); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[1].const_updated_mask, check_vconsts_parameters[1].param_name); @@ -4797,7 +4797,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, &fvect.x, sizeof(float)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, check_vconsts_parameters[7].const_updated_mask, check_vconsts_parameters[7].param_name); @@ -4822,7 +4822,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, &fvect, sizeof(float) * 3); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetRenderState(device, D3DRS_FOGDENSITY, &value); ok(hr == D3D_OK, "Got result %#x.\n", hr); ok(value == 0, "Unexpected fog density %g.\n", *(float *)&value); @@ -4861,7 +4861,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, &fvect.x, sizeof(float)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetRenderState(device, D3DRS_FOGDENSITY, &value); ok(hr == D3D_OK, "Got result %#x.\n", hr); ok(*(float *)&value == 9999.0f, "Unexpected fog density %g.\n", *(float *)&value); @@ -4897,7 +4897,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) test_effect_preshader_clear_vconsts(device); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetVertexShader(device, &vshader); ok(hr == D3D_OK, "Got result %#x.\n", hr); @@ -4918,7 +4918,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, ivect, sizeof(ivect)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetVertexShader(device, &vshader); ok(hr == D3D_OK, "Got result %#x.\n", hr); ok(!vshader, "Got non NULL vshader.\n"); @@ -4928,7 +4928,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, ivect, sizeof(ivect)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = IDirect3DDevice9_GetVertexShader(device, &vshader); ok(hr == D3D_OK, "Got result %#x.\n", hr); ok(!!vshader, "Got NULL vshader.\n"); @@ -4945,7 +4945,7 @@ static void test_effect_commitchanges(IDirect3DDevice9 *device) hr = effect->lpVtbl->SetValue(effect, param, ivect, sizeof(ivect)); ok(hr == D3D_OK, "Got result %#x.\n", hr); hr = effect->lpVtbl->CommitChanges(effect); - todo_wine ok(hr == D3D_OK, "Got result %#x.\n", hr); + ok(hr == D3D_OK, "Got result %#x.\n", hr); test_effect_preshader_compare_vconsts(device, NULL, NULL); hr = effect->lpVtbl->EndPass(effect); -- 2.9.3