[v7 2/7] d3dx9: Implement preshader parsing.

Matteo Bruni matteo.mystral at gmail.com
Mon Mar 28 18:16:15 CDT 2016


2016-03-28 9:20 GMT+02:00 Paul Gofman <gofmanp at gmail.com>:
> Signed-off-by: Paul Gofman <gofmanp at gmail.com>
> ---
> Changes
> - Changed opcode function to take double arguments and return double

Good catch.

> +static struct op_info pres_op_info[] =
> +{
> +    {0x000, "nop", 0, 0, NULL    }, /* PRESHADER_OP_NOP */
> +    {0x100, "mov", 1, 0, pres_mov}, /* PRESHADER_OP_MOV */
> +};

I think this can be const.

> +struct d3dx_pres_ins
> +{
> +    enum pres_ops op;
> +    /* first input argument is scalar,
> +       scalar component is propagated */
> +    BOOL scalar_op;
> +    unsigned int component_count;
> +    struct d3dx_pres_operand inputs[3];

The maximum number of input operands could use a #define. Not a huge
deal though.

> +static void regstore_free_tables(struct d3dx_regstore *rs)
> +{
> +    unsigned int i;
> +
> +    for (i = PRES_REGTAB_FIRST; i <= PRES_REGTAB_LAST; ++i)
> +    {
> +        if (rs->tables[i])
> +            HeapFree(GetProcessHeap(), 0, rs->tables[i]);
> +        if (rs->table_value_set[i])
> +            HeapFree(GetProcessHeap(), 0, rs->table_value_set[i]);
> +    }
> +}

You don't need to check for non-NULL, HeapFree() handles NULL
arguments just fine.

> +static unsigned int *find_bytecode_comment(unsigned int *ptr, unsigned int fourcc)
> +{
> +    while ((*ptr & 0xffff) == 0xfffe)
> +    {
> +        if (*(ptr + 1) == fourcc)
> +            return ptr + 2;
> +        ptr += 1 + (*ptr >> 16);
> +    }
> +    return NULL;
> +}

This is probably paranoid but in theory a broken or malicious effect
could send you rummaging outside of the bytecode buffer so all the
offsets should be checked. I don't think it's critical in practice
i.e. I'll probably sleep just fine if you decide to ignore this
issue...

> +static unsigned int *parse_pres_arg(unsigned int *ptr, struct d3dx_pres_operand *opr)
> +{
> +    static const enum pres_reg_tables reg_table[8] =
> +    {
> +        PRES_REGTAB_COUNT, PRES_REGTAB_IMMED, PRES_REGTAB_CONST, PRES_REGTAB_COUNT,
> +        PRES_REGTAB_OCONST, PRES_REGTAB_OBCONST, PRES_REGTAB_OICONST, PRES_REGTAB_REG

Matter of taste, so feel free to ignore this, but PRES_REGTAB_REG
sounds a bit vague to me. I'd go with something like PRES_REGTAB_TEMP.

> +    if (input_count != pres_op_info[i].input_count)
> +    {
> +        FIXME("Actual input args %u, expected %u, instruction %s.\n", input_count,
> +                pres_op_info[i].input_count, pres_op_info[i].mnem);
> +        return NULL;
> +    }
> +    for (i = 0; i < input_count; ++i)
> +    {
> +        ptr = parse_pres_arg(ptr, &ins->inputs[i]);
> +        if (!ptr)
> +            return NULL;
> +    }

Another pretty paranoid potential check, here you could verify that
input_count is < ARRAY_SIZE(ins->inputs). Of course that could only
fail if we encounter some instruction with more than 3 components AND
we update pres_op_info BUT we forget to update struct d3dx_pres_ins,
but I don't think it would hurt to be safe...

> +    for (i = 0; i < pres->ins_count; ++i)
> +    {
> +        unsigned int table;
> +
> +        for (j = 0; j < pres_op_info[pres->ins[i].op].input_count; ++j)
> +        {
> +            table = pres->ins[i].inputs[j].table;
> +            max_offset = get_reg_offset(table, pres->ins[i].inputs[j].offset + pres->ins[i].component_count - 1);
> +            pres->regs.table_sizes[table] = max(pres->regs.table_sizes[table], max_offset + 1);
> +        }
> +        table = pres->ins[i].output.table;
> +        max_offset = get_reg_offset(table, pres->ins[i].output.offset + pres->ins[i].component_count - 1);
> +        pres->regs.table_sizes[table] = max(pres->regs.table_sizes[table], max_offset + 1);
> +    }
> +    update_table_sizes_consts(pres->regs.table_sizes, &pres->inputs);

It probably makes sense to add a small update_table_size() helper (or
similar), the "same" 3 lines are repeated in the loop for the input
operands, for the output operand and in update_table_sizes_consts().

> +    for (i = PRES_REGTAB_IMMED + 1; i <= PRES_REGTAB_LAST; ++i)

It depends on what is coming up next but maybe it would make sense to
add an enum for "PRES_REGTAB_IMMED + 1" (incidentally,
PRES_REGTAB_FIRST is never used in this patch series).

> +    {
> +        TRACE("table_sizes[%u] %u.\n", i, peval->pres.regs.table_sizes[i]);

That trace looks a bit like a debugging leftover.

> +        if (FAILED(regstore_alloc_table(&peval->pres.regs, i)))
> +        {
> +            hr = E_OUTOFMEMORY;
> +            goto err_out;
> +        }
> +    }
> +
> +    if (TRACE_ON(d3dx))
> +        dump_bytecode(byte_code, byte_code_size);
> +
> +    *peval_out = peval;
> +    TRACE("retval %u, *peval_out %p.\n", D3D_OK, *peval_out);

Same with this. This one in particular doesn't seem worth keeping.

> +    return D3D_OK;
> +
> +err_out:
> +    FIXME("Error creating parameter evaluator.\n");
> +    d3dx_free_param_eval(peval);
>      *peval_out = NULL;
> -    return E_NOTIMPL;
> +    TRACE("retval %u, *peval_out %p.\n", hr, *peval_out);
> +    return hr;
> +}

You could print the HRESULT in the FIXME (e.g. FIXME("Error creating
parameter evaluator, returning %#x.\n", hr)). Tracing the value of
*peval_out doesn't seem particularly worthwhile.

> +static void d3dx_free_const_tab(struct d3dx_const_tab *ctab)
> +{
> +    if (ctab->inputs)
> +        HeapFree(GetProcessHeap(), 0, ctab->inputs);
> +    if (ctab->inputs_param)
> +        HeapFree(GetProcessHeap(), 0, ctab->inputs_param);
> +    if (ctab->ctab)
> +        ID3DXConstantTable_Release(ctab->ctab);
> +}
> +
> +static void d3dx_free_preshader(struct d3dx_preshader *pres)
> +{
> +    if (pres->ins)
> +        HeapFree(GetProcessHeap(), 0, pres->ins);
> +
> +    regstore_free_tables(&pres->regs);
> +    d3dx_free_const_tab(&pres->inputs);
>  }

You can drop all those ifs (I think there are a few more of them in
the middle of the patch I haven't specifically mentioned).



More information about the wine-devel mailing list