vkd3d-shader/fx: Implement parsing complex value and index expressions.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
This commit is contained in:
Nikolay Sivov
2025-02-21 23:57:50 +01:00
committed by Henri Verbeet
parent 58aed8fd0d
commit 09226543fe
Notes: Henri Verbeet 2025-02-24 16:28:00 +01:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1392
2 changed files with 320 additions and 2 deletions

View File

@ -65,9 +65,12 @@
#define vkd3d_clamp(value, lower, upper) max(min(value, upper), lower)
#define TAG_AON9 VKD3D_MAKE_TAG('A', 'o', 'n', '9')
#define TAG_CLI4 VKD3D_MAKE_TAG('C', 'L', 'I', '4')
#define TAG_CTAB VKD3D_MAKE_TAG('C', 'T', 'A', 'B')
#define TAG_DXBC VKD3D_MAKE_TAG('D', 'X', 'B', 'C')
#define TAG_DXIL VKD3D_MAKE_TAG('D', 'X', 'I', 'L')
#define TAG_FX10 VKD3D_MAKE_TAG('F', 'X', '1', '0')
#define TAG_FXLC VKD3D_MAKE_TAG('F', 'X', 'L', 'C')
#define TAG_ISG1 VKD3D_MAKE_TAG('I', 'S', 'G', '1')
#define TAG_ISGN VKD3D_MAKE_TAG('I', 'S', 'G', 'N')
#define TAG_OSG1 VKD3D_MAKE_TAG('O', 'S', 'G', '1')

View File

@ -560,8 +560,21 @@ enum fx_4_type_constants
FX_4_ASSIGNMENT_VARIABLE = 0x2,
FX_4_ASSIGNMENT_ARRAY_CONSTANT_INDEX = 0x3,
FX_4_ASSIGNMENT_ARRAY_VARIABLE_INDEX = 0x4,
FX_4_ASSIGNMENT_INDEX_EXPRESSION = 0x5,
FX_4_ASSIGNMENT_VALUE_EXPRESSION = 0x6,
FX_4_ASSIGNMENT_INLINE_SHADER = 0x7,
FX_5_ASSIGNMENT_INLINE_SHADER = 0x8,
/* FXLVM constants */
FX_4_FXLC_COMP_COUNT_MASK = 0xffff,
FX_4_FXLC_OPCODE_MASK = 0x7ff,
FX_4_FXLC_OPCODE_SHIFT = 20,
FX_4_FXLC_IS_SCALAR_MASK = 0x80000000,
FX_4_FXLC_REG_LITERAL = 1,
FX_4_FXLC_REG_CB = 2,
FX_4_FXLC_REG_OUTPUT = 4,
FX_4_FXLC_REG_TEMP = 7,
};
static const uint32_t fx_4_numeric_base_types[] =
@ -2986,15 +2999,16 @@ static const void *fx_parser_get_unstructured_ptr(struct fx_parser *parser, uint
return &ptr[offset];
}
static void fx_parser_read_unstructured(struct fx_parser *parser, void *dst, uint32_t offset, size_t size)
static uint32_t fx_parser_read_unstructured(struct fx_parser *parser, void *dst, uint32_t offset, size_t size)
{
const uint8_t *ptr;
memset(dst, 0, size);
if (!(ptr = fx_parser_get_unstructured_ptr(parser, offset, size)))
return;
return offset;
memcpy(dst, ptr, size);
return offset + size;
}
static const char *fx_4_get_string(struct fx_parser *parser, uint32_t offset)
@ -3366,6 +3380,294 @@ static int fx_4_state_id_compare(const void *a, const void *b)
return id - state->id;
}
static const struct
{
uint32_t opcode;
const char *name;
}
fx_4_fxlc_opcodes[] =
{
{ 0x100, "mov" },
{ 0x101, "neg" },
{ 0x103, "rcp" },
{ 0x104, "frc" },
{ 0x105, "exp" },
{ 0x106, "log" },
{ 0x107, "rsq" },
{ 0x108, "sin" },
{ 0x109, "cos" },
{ 0x10a, "asin" },
{ 0x10b, "acos" },
{ 0x10c, "atan" },
{ 0x112, "sqrt" },
{ 0x120, "ineg" },
{ 0x121, "not" },
{ 0x130, "itof" },
{ 0x131, "utof" },
{ 0x133, "ftou" },
{ 0x137, "ftob" },
{ 0x139, "floor" },
{ 0x13a, "ceil" },
{ 0x200, "min" },
{ 0x201, "max" },
{ 0x204, "add" },
{ 0x205, "mul" },
{ 0x206, "atan2" },
{ 0x208, "div" },
{ 0x210, "bilt" },
{ 0x211, "bige" },
{ 0x212, "bieq" },
{ 0x213, "bine" },
{ 0x214, "buge" },
{ 0x215, "bult" },
{ 0x216, "iadd" },
{ 0x219, "imul" },
{ 0x21a, "udiv" },
{ 0x21d, "imin" },
{ 0x21e, "imax" },
{ 0x21f, "umin" },
{ 0x220, "umax" },
{ 0x230, "and" },
{ 0x231, "or" },
{ 0x233, "xor" },
{ 0x234, "ishl" },
{ 0x235, "ishr" },
{ 0x236, "ushr" },
{ 0x301, "movc" },
{ 0x500, "dot" },
{ 0x70e, "d3ds_dotswiz" },
};
static const char *fx_4_get_fxlc_opcode_name(uint32_t opcode)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(fx_4_fxlc_opcodes); ++i)
{
if (fx_4_fxlc_opcodes[i].opcode == opcode)
return fx_4_fxlc_opcodes[i].name;
}
return "<unrecognized>";
}
struct fx_4_fxlc_argument
{
uint32_t flags;
uint32_t reg_type;
uint32_t address;
};
struct fx_4_ctab_entry
{
uint32_t name;
uint16_t register_set;
uint16_t register_index;
uint16_t register_count;
uint16_t reserved;
uint32_t typeinfo;
uint32_t default_value;
};
struct fxlvm_code
{
const float *cli4;
uint32_t cli4_count;
const struct fx_4_ctab_entry *constants;
uint32_t ctab_offset;
uint32_t ctab_count;
const char *ctab;
unsigned int comp_count;
bool scalar;
};
static void fx_4_parse_print_swizzle(struct fx_parser *parser, const struct fxlvm_code *code, unsigned int addr)
{
unsigned int comp_count = code->scalar ? 1 : code->comp_count;
static const char comp[] = "xyzw";
if (comp_count < 4)
vkd3d_string_buffer_printf(&parser->buffer, ".%.*s", comp_count, &comp[addr % 4]);
}
static void fx_4_parse_fxlc_constant_argument(struct fx_parser *parser,
const struct fx_4_fxlc_argument *arg, const struct fxlvm_code *code)
{
uint32_t i, offset, register_index = arg->address / 4; /* Address counts in components. */
for (i = 0; i < code->ctab_count; ++i)
{
const struct fx_4_ctab_entry *c = &code->constants[i];
if (register_index < c->register_index || register_index - c->register_index >= c->register_count)
continue;
vkd3d_string_buffer_printf(&parser->buffer, "%s", &code->ctab[c->name]);
/* Register offset within variable */
offset = arg->address - c->register_index * 4;
if (offset / 4)
vkd3d_string_buffer_printf(&parser->buffer, "[%u]", offset / 4);
fx_4_parse_print_swizzle(parser, code, offset);
return;
}
vkd3d_string_buffer_printf(&parser->buffer, "(var-not-found)");
}
static void fx_4_parse_fxlc_argument(struct fx_parser *parser, uint32_t offset, const struct fxlvm_code *code)
{
struct fx_4_fxlc_argument arg;
uint32_t count;
fx_parser_read_unstructured(parser, &arg, offset, sizeof(arg));
switch (arg.reg_type)
{
case FX_4_FXLC_REG_LITERAL:
count = code->scalar ? 1 : code->comp_count;
if (arg.address >= code->cli4_count || count > code->cli4_count - arg.address)
{
vkd3d_string_buffer_printf(&parser->buffer, "(<out-of-bounds>)");
parser->failed = true;
break;
}
vkd3d_string_buffer_printf(&parser->buffer, "(");
vkd3d_string_buffer_print_f32(&parser->buffer, code->cli4[arg.address]);
for (unsigned int i = 1; i < code->comp_count; ++i)
{
vkd3d_string_buffer_printf(&parser->buffer, ", ");
vkd3d_string_buffer_print_f32(&parser->buffer, code->cli4[arg.address + code->scalar ? 0 : i]);
}
vkd3d_string_buffer_printf(&parser->buffer, ")");
break;
case FX_4_FXLC_REG_CB:
fx_4_parse_fxlc_constant_argument(parser, &arg, code);
break;
case FX_4_FXLC_REG_OUTPUT:
case FX_4_FXLC_REG_TEMP:
if (arg.reg_type == FX_4_FXLC_REG_OUTPUT)
vkd3d_string_buffer_printf(&parser->buffer, "expr");
else
vkd3d_string_buffer_printf(&parser->buffer, "r%u", arg.address / 4);
fx_4_parse_print_swizzle(parser, code, arg.address);
break;
default:
vkd3d_string_buffer_printf(&parser->buffer, "<unknown register %u>", arg.reg_type);
break;
}
}
static void fx_4_parse_fxlvm_expression(struct fx_parser *parser, uint32_t offset)
{
struct vkd3d_shader_dxbc_section_desc *section, fxlc, cli4, ctab;
struct vkd3d_shader_dxbc_desc dxbc_desc;
struct vkd3d_shader_code dxbc;
uint32_t size, ins_count;
struct fxlvm_code code;
size_t i, j;
offset = fx_parser_read_unstructured(parser, &size, offset, sizeof(size));
dxbc.size = size;
dxbc.code = fx_parser_get_unstructured_ptr(parser, offset, size);
if (!dxbc.code)
return;
if (vkd3d_shader_parse_dxbc(&dxbc, 0, &dxbc_desc, NULL) < 0)
{
parser->failed = true;
return;
}
memset(&fxlc, 0, sizeof(fxlc));
memset(&cli4, 0, sizeof(cli4));
memset(&ctab, 0, sizeof(ctab));
for (i = 0; i < dxbc_desc.section_count; ++i)
{
section = &dxbc_desc.sections[i];
if (section->tag == TAG_FXLC)
fxlc = *section;
else if (section->tag == TAG_CLI4)
cli4 = *section;
else if (section->tag == TAG_CTAB)
ctab = *section;
}
vkd3d_shader_free_dxbc(&dxbc_desc);
if (cli4.data.code)
{
uint32_t cli4_offset = offset + (size_t)cli4.data.code - (size_t)dxbc.code;
fx_parser_read_unstructured(parser, &code.cli4_count, cli4_offset, sizeof(code.cli4_count));
code.cli4 = fx_parser_get_unstructured_ptr(parser, cli4_offset + 4, code.cli4_count * sizeof(float));
}
if (ctab.data.code)
{
uint32_t ctab_offset = offset + (size_t)ctab.data.code - (size_t)dxbc.code;
uint32_t consts_offset;
fx_parser_read_unstructured(parser, &code.ctab_count, ctab_offset + 12, sizeof(code.ctab_count));
fx_parser_read_unstructured(parser, &consts_offset, ctab_offset + 16, sizeof(consts_offset));
code.ctab = ctab.data.code;
code.constants = fx_parser_get_unstructured_ptr(parser,
ctab_offset + consts_offset, code.ctab_count * sizeof(*code.constants));
}
offset += (size_t)fxlc.data.code - (size_t)dxbc.code;
offset = fx_parser_read_unstructured(parser, &ins_count, offset, sizeof(ins_count));
parse_fx_start_indent(parser);
for (i = 0; i < ins_count; ++i)
{
uint32_t instr, opcode, src_count;
struct fx_4_fxlc_argument arg;
offset = fx_parser_read_unstructured(parser, &instr, offset, sizeof(instr));
offset = fx_parser_read_unstructured(parser, &src_count, offset, sizeof(src_count));
opcode = (instr >> FX_4_FXLC_OPCODE_SHIFT) & FX_4_FXLC_OPCODE_MASK;
code.comp_count = instr & FX_4_FXLC_COMP_COUNT_MASK;
code.scalar = false;
parse_fx_print_indent(parser);
vkd3d_string_buffer_printf(&parser->buffer, "%s ", fx_4_get_fxlc_opcode_name(opcode));
/* Destination first. */
fx_4_parse_fxlc_argument(parser, offset + sizeof(arg) * src_count, &code);
for (j = 0; j < src_count; ++j)
{
vkd3d_string_buffer_printf(&parser->buffer, ", ");
/* Scalar modifier applies only to first source. */
code.scalar = j == 0 && !!(instr & FX_4_FXLC_IS_SCALAR_MASK);
fx_4_parse_fxlc_argument(parser, offset, &code);
offset += sizeof(arg);
}
/* Destination */
offset += sizeof(arg);
vkd3d_string_buffer_printf(&parser->buffer, "\n");
}
parse_fx_end_indent(parser);
}
static void fx_4_parse_state_object_initializer(struct fx_parser *parser, uint32_t count,
enum hlsl_type_class type_class)
{
@ -3496,6 +3798,19 @@ static void fx_4_parse_state_object_initializer(struct fx_parser *parser, uint32
vkd3d_string_buffer_printf(&parser->buffer, "%s[%s]", fx_4_get_string(parser, index.name),
fx_4_get_string(parser, index.index));
break;
case FX_4_ASSIGNMENT_INDEX_EXPRESSION:
fx_parser_read_unstructured(parser, &index, entry.value, sizeof(index));
vkd3d_string_buffer_printf(&parser->buffer, "%s[eval(\n", fx_4_get_string(parser, index.name));
fx_4_parse_fxlvm_expression(parser, index.index);
parse_fx_print_indent(parser);
vkd3d_string_buffer_printf(&parser->buffer, ")]");
break;
case FX_4_ASSIGNMENT_VALUE_EXPRESSION:
vkd3d_string_buffer_printf(&parser->buffer, "eval(\n");
fx_4_parse_fxlvm_expression(parser, entry.value);
parse_fx_print_indent(parser);
vkd3d_string_buffer_printf(&parser->buffer, ")");
break;
case FX_4_ASSIGNMENT_INLINE_SHADER:
case FX_5_ASSIGNMENT_INLINE_SHADER:
{