Skip to content
Snippets Groups Projects
spirv.c 52.4 KiB
Newer Older

struct vkd3d_dxbc_register_info
{
    uint32_t id;
    SpvStorageClass storage_class;
};

static void vkd3d_dxbc_compiler_get_register_info(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, struct vkd3d_dxbc_register_info *register_info)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_symbol reg_symbol, *symbol;
    struct rb_entry *entry;

    assert(reg->type != VKD3DSPR_IMMCONST);

    if (reg->idx[0].rel_addr || reg->idx[1].rel_addr)
        FIXME("Relative indexing not implemented.\n");

    if (reg->type == VKD3DSPR_TEMP)
    {
        assert(reg->idx[0].offset < compiler->temp_count);
        register_info->id = compiler->temp_id + reg->idx[0].offset;
        register_info->storage_class = SpvStorageClassFunction;
        return;
    }

    vkd3d_symbol_make_register(&reg_symbol, reg);
    entry = rb_get(&compiler->symbol_table, &reg_symbol);
    assert(entry);
    symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry);
    register_info->id = symbol->id;
    register_info->storage_class = symbol->info.storage_class;

    if (reg->type == VKD3DSPR_CONSTBUFFER)
    {
        uint32_t type_id, ptr_type_id;
        uint32_t indexes[] =
        {
            vkd3d_dxbc_compiler_get_constant_uint(compiler, 0),
            vkd3d_dxbc_compiler_get_constant_uint(compiler, reg->idx[1].offset),
        };

        type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
        ptr_type_id = vkd3d_dxbc_compiler_get_pointer_type(compiler,
                type_id, register_info->storage_class);
        register_info->id = vkd3d_spirv_build_op_access_chain(builder, ptr_type_id,
                register_info->id, indexes, ARRAY_SIZE(indexes));
    }
}

static uint32_t vkd3d_dxbc_compiler_get_register_id(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_dxbc_register_info register_info;
    unsigned int component_count;

    switch (reg->type)
    {
        case VKD3DSPR_TEMP:
        case VKD3DSPR_INPUT:
        case VKD3DSPR_OUTPUT:
        case VKD3DSPR_COLOROUT:
        case VKD3DSPR_CONSTBUFFER:
            vkd3d_dxbc_compiler_get_register_info(compiler, reg, &register_info);
            return register_info.id;
        case VKD3DSPR_IMMCONST:
            if (reg->immconst_type == VKD3D_IMMCONST_SCALAR)
            {
                component_count = 1;
            }
            else if (reg->immconst_type == VKD3D_IMMCONST_VEC4)
            {
                component_count = VKD3D_VEC4_SIZE;
            }
            else
            {
                FIXME("Unhandled immconst type %#x.\n", reg->immconst_type);
                component_count = 1;
            }
            return vkd3d_dxbc_compiler_get_constant(compiler,
                    vkd3d_component_type_from_data_type(reg->data_type),
                    component_count, reg->u.immconst_data);
        default:
            FIXME("Unhandled register type %#x.\n", reg->type);
            return vkd3d_dxbc_compiler_emit_undef(compiler, &builder->global_stream, reg);
    }
}

static uint32_t vkd3d_dxbc_compiler_emit_load_constant(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD swizzle, DWORD write_mask)
{
    unsigned int component_count = vkd3d_write_mask_component_count(write_mask);
    uint32_t values[VKD3D_VEC4_SIZE];
    unsigned int i, j;

    assert(reg->type == VKD3DSPR_IMMCONST);

    if (component_count == 1 || component_count == VKD3D_VEC4_SIZE)
    {
        assert(component_count != 1 || reg->immconst_type == VKD3D_IMMCONST_SCALAR);
        return vkd3d_dxbc_compiler_get_register_id(compiler, reg);
    }

    assert(reg->immconst_type == VKD3D_IMMCONST_VEC4);

    for (i = 0, j = 0; i < VKD3D_VEC4_SIZE; ++i)
    {
        if (write_mask & (VKD3DSP_WRITEMASK_0 << i))
            values[j++] = reg->u.immconst_data[vkd3d_swizzle_get_component(swizzle, i)];
    }

    return vkd3d_dxbc_compiler_get_constant(compiler,
            vkd3d_component_type_from_data_type(reg->data_type), component_count, values);
}

static uint32_t vkd3d_dxbc_compiler_emit_load_scalar(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD swizzle, DWORD write_mask)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, ptr_type_id, indexes[1], chain_id, val_id;
    struct vkd3d_dxbc_register_info reg_info;
    unsigned int component_idx;

    assert(reg->type != VKD3DSPR_IMMCONST);
    assert(vkd3d_write_mask_component_count(write_mask) == 1);

    component_idx = vkd3d_write_mask_get_component_idx(write_mask);
    component_idx = vkd3d_swizzle_get_component(swizzle, component_idx);

    vkd3d_dxbc_compiler_get_register_info(compiler, reg, &reg_info);

    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, 1);
    ptr_type_id = vkd3d_dxbc_compiler_get_pointer_type(compiler, type_id, reg_info.storage_class);
    indexes[0] = vkd3d_dxbc_compiler_get_constant_uint(compiler, component_idx);
    chain_id = vkd3d_spirv_build_op_in_bounds_access_chain(builder,
            ptr_type_id, reg_info.id, indexes, ARRAY_SIZE(indexes));

    val_id = vkd3d_spirv_build_op_load(builder, type_id, chain_id, SpvMemoryAccessMaskNone);

    if (reg->data_type != VKD3D_DATA_FLOAT)
    {
        type_id = vkd3d_spirv_get_type_id(builder,
                vkd3d_component_type_from_data_type(reg->data_type), 1);
        val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
    }

    return val_id;
}

static uint32_t vkd3d_dxbc_compiler_emit_load_reg(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD swizzle, DWORD write_mask)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t src_id, type_id, val_id, components[VKD3D_VEC4_SIZE];
    unsigned int i, component_idx, component_count;

    if (reg->type == VKD3DSPR_IMMCONST)
        return vkd3d_dxbc_compiler_emit_load_constant(compiler, reg, swizzle, write_mask);

    component_count = vkd3d_write_mask_component_count(write_mask);
    if (component_count == 1)
        return vkd3d_dxbc_compiler_emit_load_scalar(compiler, reg, swizzle, write_mask);

    src_id = vkd3d_dxbc_compiler_get_register_id(compiler, reg);
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    val_id = vkd3d_spirv_build_op_load(builder, type_id, src_id, SpvMemoryAccessMaskNone);

    if (component_count != VKD3D_VEC4_SIZE || swizzle != VKD3DSP_NOSWIZZLE)
    {
        type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, component_count);

        for (i = 0, component_idx = 0; i < ARRAY_SIZE(components); ++i)
        {
            if (write_mask & (VKD3DSP_WRITEMASK_0 << i))
                components[component_idx++] = vkd3d_swizzle_get_component(swizzle, i);
        }

        val_id = vkd3d_spirv_build_op_vector_shuffle(builder,
                type_id, val_id, val_id, components, component_count);
    }

    if (reg->data_type != VKD3D_DATA_FLOAT)
    {
        type_id = vkd3d_spirv_get_type_id(builder,
                vkd3d_component_type_from_data_type(reg->data_type), component_count);
        val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
    }

    return val_id;
}

static void vkd3d_dxbc_compiler_emit_store_scalar(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD write_mask, uint32_t val_id)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, ptr_type_id, chain_id, index[1];
    struct vkd3d_dxbc_register_info reg_info;
    unsigned int component_idx;

    assert(reg->type != VKD3DSPR_IMMCONST);

    vkd3d_dxbc_compiler_get_register_info(compiler, reg, &reg_info);

    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, 1);
    ptr_type_id = vkd3d_dxbc_compiler_get_pointer_type(compiler,
            type_id, reg_info.storage_class);
    component_idx = vkd3d_write_mask_get_component_idx(write_mask);
    index[0] = vkd3d_dxbc_compiler_get_constant_uint(compiler, component_idx);
    chain_id = vkd3d_spirv_build_op_in_bounds_access_chain(builder,
            ptr_type_id, reg_info.id, index, ARRAY_SIZE(index));

    vkd3d_spirv_build_op_store(builder, chain_id, val_id, SpvMemoryAccessMaskNone);
}

static void vkd3d_dxbc_compiler_emit_store_reg(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD write_mask, uint32_t val_id)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int i, component_idx, component_count;
    uint32_t components[VKD3D_VEC4_SIZE];
    uint32_t reg_id;

    assert(reg->type != VKD3DSPR_IMMCONST);

    component_count = vkd3d_write_mask_component_count(write_mask);

    if (reg->data_type != VKD3D_DATA_FLOAT)
    {
        uint32_t type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, component_count);
        val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
    }

    if (component_count == 1)
        return vkd3d_dxbc_compiler_emit_store_scalar(compiler, reg, write_mask, val_id);

    if (component_count != VKD3D_VEC4_SIZE)
    {
        uint32_t type_id, reg_val_id;

        type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);

        reg_val_id = vkd3d_dxbc_compiler_emit_load_reg(compiler,
                reg, VKD3DSP_NOSWIZZLE, VKD3DSP_WRITEMASK_ALL);

        for (i = 0, component_idx = 0; i < ARRAY_SIZE(components); ++i)
        {
            if (write_mask & (VKD3DSP_WRITEMASK_0 << i))
                components[i] = VKD3D_VEC4_SIZE + component_idx++;
            else
                components[i] = i;
        }

        val_id = vkd3d_spirv_build_op_vector_shuffle(builder,
                type_id, reg_val_id, val_id, components, ARRAY_SIZE(components));
    }

    reg_id = vkd3d_dxbc_compiler_get_register_id(compiler, reg);
    vkd3d_spirv_build_op_store(builder, reg_id, val_id, SpvMemoryAccessMaskNone);
}

static void vkd3d_dxbc_compiler_decorate_sysval(struct vkd3d_spirv_builder *builder,
        uint32_t target_id, enum vkd3d_shader_input_sysval_semantic sysval)
{
    SpvBuiltIn builtin;

    switch (sysval)
    {
        case VKD3D_SIV_POSITION:
            builtin = SpvBuiltInPosition;
            break;
        case VKD3D_SIV_VERTEX_ID:
            builtin = SpvBuiltInVertexIndex;
            break;
        default:
            FIXME("Unhandled semantic %#x.\n", sysval);
            return;
    }

    vkd3d_spirv_build_op_decorate1(builder, target_id, SpvDecorationBuiltIn, builtin);
}

static uint32_t vkd3d_dxbc_compiler_emit_output(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst, enum vkd3d_shader_input_sysval_semantic sysval)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_register *reg = &dst->reg;
    struct vkd3d_symbol reg_symbol;
    SpvStorageClass storage_class;
    uint32_t id;

    storage_class = SpvStorageClassOutput;

    id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
            storage_class, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    vkd3d_spirv_add_iface_variable(builder, id);
    if (sysval)
        vkd3d_dxbc_compiler_decorate_sysval(builder, id, sysval);
    else
        vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, reg->idx[0].offset);

    vkd3d_dxbc_compiler_emit_register_debug_name(builder, id, reg);

    vkd3d_symbol_make_register(&reg_symbol, &dst->reg);
    reg_symbol.id = id;
    reg_symbol.info.storage_class = storage_class;
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);

    return id;
}

static void vkd3d_dxbc_compiler_emit_dcl_temps(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, ptr_type_id, id;
    char debug_name[100];
    unsigned int i;

    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    ptr_type_id = vkd3d_dxbc_compiler_get_pointer_type(compiler, type_id, SpvStorageClassFunction);

    assert(!compiler->temp_count);
    compiler->temp_count = instruction->declaration.count;
    for (i = 0; i < compiler->temp_count; ++i)
    {
        id = vkd3d_spirv_build_op_variable(builder, &builder->function_stream,
                ptr_type_id, SpvStorageClassFunction);
        if (!i)
            compiler->temp_id = id;
        assert(id == compiler->temp_id + i);

        sprintf(debug_name, "r%u", i);
        vkd3d_spirv_build_op_name(builder, id, debug_name);
    }
}

static void vkd3d_dxbc_compiler_emit_dcl_output(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_dxbc_compiler_emit_output(compiler, &instruction->declaration.dst, VKD3D_SIV_NONE);
}

static void vkd3d_dxbc_compiler_emit_dcl_output_siv(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_dxbc_compiler_emit_output(compiler, &instruction->declaration.dst,
            instruction->declaration.register_semantic.sysval_semantic);
}

static void vkd3d_dxbc_compiler_emit_dcl_thread_group(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    const struct vkd3d_shader_thread_group_size *group_size = &instruction->declaration.thread_group_size;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    vkd3d_spirv_set_local_size(builder, group_size->x, group_size->y, group_size->z);
}

static SpvOp vkd3d_dxbc_compiler_map_alu_instruction(const struct vkd3d_shader_instruction *instruction)
{
    static const struct
    {
        enum VKD3D_SHADER_INSTRUCTION_HANDLER handler_idx;
        SpvOp spirv_op;
    }
    alu_ops[] =
    {
        {VKD3DSIH_ADD,  SpvOpFAdd},
        {VKD3DSIH_AND,  SpvOpBitwiseAnd},
        {VKD3DSIH_DIV,  SpvOpFDiv},
        {VKD3DSIH_MUL,  SpvOpFMul},
        {VKD3DSIH_UTOF, SpvOpConvertUToF},
    };
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(alu_ops); ++i)
    {
        if (alu_ops[i].handler_idx == instruction->handler_idx)
            return alu_ops[i].spirv_op;
    }

    return SpvOpMax;
}

static void vkd3d_dxbc_compiler_emit_alu_instruction(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_dst_param *dst = instruction->dst;
    const struct vkd3d_shader_src_param *src = instruction->src;
    uint32_t src_ids[VKD3D_DXBC_MAX_SOURCE_COUNT];
    unsigned int component_count;
    uint32_t type_id, val_id;
    unsigned int i;
    SpvOp op;

    op = vkd3d_dxbc_compiler_map_alu_instruction(instruction);
    if (op == SpvOpMax)
    {
        FIXME("Unhandled ALU instruction %#x.\n", instruction->handler_idx);
        return;
    }

    assert(instruction->dst_count == 1);
    assert(instruction->src_count <= VKD3D_DXBC_MAX_SOURCE_COUNT);

    component_count = vkd3d_write_mask_component_count(dst->write_mask);
    type_id = vkd3d_spirv_get_type_id(builder,
            vkd3d_component_type_from_data_type(dst->reg.data_type), component_count);

    for (i = 0; i < instruction->src_count; ++i)
        src_ids[i] = vkd3d_dxbc_compiler_emit_load_reg(compiler,
                &src[i].reg, src[i].swizzle, dst->write_mask);

    val_id = vkd3d_spirv_build_op_trv(builder, &builder->function_stream, op, type_id,
            src_ids, instruction->src_count);

    vkd3d_dxbc_compiler_emit_store_reg(compiler, &dst->reg, dst->write_mask, val_id);
}

static void vkd3d_dxbc_compiler_emit_mov(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    const struct vkd3d_shader_dst_param *dst = &instruction->dst[0];
    const struct vkd3d_shader_src_param *src = &instruction->src[0];
    uint32_t val_id;

    val_id = vkd3d_dxbc_compiler_emit_load_reg(compiler, &src->reg, src->swizzle, dst->write_mask);
    vkd3d_dxbc_compiler_emit_store_reg(compiler, &dst->reg, dst->write_mask, val_id);
}

static void vkd3d_dxbc_compiler_emit_return(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_spirv_build_op_return(&compiler->spirv_builder);
}

void vkd3d_dxbc_compiler_handle_instruction(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    switch (instruction->handler_idx)
    {
        case VKD3DSIH_DCL_TEMPS:
            vkd3d_dxbc_compiler_emit_dcl_temps(compiler, instruction);
            break;
        case VKD3DSIH_DCL_OUTPUT:
            vkd3d_dxbc_compiler_emit_dcl_output(compiler, instruction);
            break;
        case VKD3DSIH_DCL_OUTPUT_SIV:
            vkd3d_dxbc_compiler_emit_dcl_output_siv(compiler, instruction);
            break;
        case VKD3DSIH_DCL_THREAD_GROUP:
            vkd3d_dxbc_compiler_emit_dcl_thread_group(compiler, instruction);
            break;
        case VKD3DSIH_MOV:
            vkd3d_dxbc_compiler_emit_mov(compiler, instruction);
            break;
        case VKD3DSIH_ADD:
        case VKD3DSIH_AND:
        case VKD3DSIH_DIV:
        case VKD3DSIH_MUL:
        case VKD3DSIH_UTOF:
            vkd3d_dxbc_compiler_emit_alu_instruction(compiler, instruction);
            break;
        case VKD3DSIH_RET:
            vkd3d_dxbc_compiler_emit_return(compiler, instruction);
            break;
        default:
            FIXME("Unhandled instruction %#x.\n", instruction->handler_idx);
    }
}

bool vkd3d_dxbc_compiler_generate_spirv(struct vkd3d_dxbc_compiler *compiler,
        struct vkd3d_shader_code *spirv)
{
    if (!vkd3d_spirv_compile_module(&compiler->spirv_builder, spirv))
        return false;

    if (TRACE_ON())
    {
        vkd3d_spirv_dump(spirv);
        vkd3d_spirv_validate(spirv);
    }

    return true;
}

void vkd3d_dxbc_compiler_destroy(struct vkd3d_dxbc_compiler *compiler)
{
    vkd3d_spirv_builder_free(&compiler->spirv_builder);

    rb_destroy(&compiler->symbol_table, vkd3d_symbol_free, NULL);