Skip to content
Snippets Groups Projects
spirv.c 334 KiB
Newer Older
    {
        storage_class = SpvStorageClassPrivate;
        var_id = vkd3d_dxbc_compiler_emit_array_variable(compiler, &builder->global_stream,
                storage_class, VKD3D_TYPE_FLOAT, component_count, array_size);
        vkd3d_symbol_set_register_info(&reg_symbol, var_id, storage_class,
                use_private_var ? VKD3D_TYPE_FLOAT : component_type, write_mask);
        vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
        vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
        type_id = vkd3d_spirv_get_type_id(builder, component_type, input_component_count);
        for (i = 0; i < max(array_size, 1); ++i)
        {
            struct vkd3d_shader_register dst_reg = *reg;
            dst_reg.data_type = VKD3D_DATA_FLOAT;
            val_id = input_id;
            if (array_size)
            {
                ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassInput, type_id);
                index = vkd3d_dxbc_compiler_get_constant_uint(compiler, i);
                val_id = vkd3d_spirv_build_op_in_bounds_access_chain1(builder, ptr_type_id, input_id, index);
                dst_reg.idx[0].offset = i;
            }
            val_id = vkd3d_spirv_build_op_load(builder, type_id, val_id, SpvMemoryAccessMaskNone);
            if (builtin && builtin->fixup_pfn)
                val_id = builtin->fixup_pfn(compiler, val_id);
            if (component_type != VKD3D_TYPE_FLOAT)
            {
                float_type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, input_component_count);
                val_id = vkd3d_spirv_build_op_bitcast(builder, float_type_id, val_id);
            }
            val_id = vkd3d_dxbc_compiler_emit_swizzle(compiler, val_id,
                    vkd3d_write_mask_from_component_count(input_component_count) << component_idx,
                    VKD3D_TYPE_FLOAT, VKD3D_NO_SWIZZLE, dst->write_mask);

            vkd3d_dxbc_compiler_emit_store_reg(compiler, &dst_reg, dst->write_mask, val_id);
        }
static void vkd3d_dxbc_compiler_emit_input_register(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_register *reg = &dst->reg;
    const struct vkd3d_spirv_builtin *builtin;
    struct vkd3d_symbol reg_symbol;
    uint32_t input_id;

    assert(!reg->idx[0].rel_addr);
    assert(!reg->idx[1].rel_addr);
    assert(reg->idx[1].offset == ~0u);

    if (!(builtin = get_spirv_builtin_for_register(reg->type)))
    {
        FIXME("Unhandled register %#x.\n", reg->type);
        return;
    }

    /* vPrim may be declared in multiple hull shader phases. */
    vkd3d_symbol_make_register(&reg_symbol, reg);
    if ((entry = rb_get(&compiler->symbol_table, &reg_symbol)))
        return;

    input_id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassInput, 0);
    vkd3d_symbol_set_register_info(&reg_symbol, input_id, SpvStorageClassInput,
            builtin->component_type, vkd3d_write_mask_from_component_count(builtin->component_count));
    reg_symbol.info.reg.is_aggregate = builtin->spirv_array_size;
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, input_id, reg);
}

static void vkd3d_dxbc_compiler_emit_shader_phase_input(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_phase *phase, const struct vkd3d_shader_dst_param *dst)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_register *reg = &dst->reg;
    struct vkd3d_symbol reg_symbol;
    uint32_t val_id;

    switch (reg->type)
    {
            vkd3d_dxbc_compiler_emit_input(compiler, dst, VKD3D_SIV_NONE, VKD3DSIM_NONE);
        case VKD3DSPR_PRIMID:
            vkd3d_dxbc_compiler_emit_input_register(compiler, dst);
            return;
        case VKD3DSPR_FORKINSTID:
        case VKD3DSPR_JOININSTID:
            val_id = phase->instance_id;
            break;
        case VKD3DSPR_OUTPOINTID: /* Emitted in vkd3d_dxbc_compiler_emit_initial_declarations(). */
        case VKD3DSPR_OUTCONTROLPOINT: /* See vkd3d_dxbc_compiler_leave_shader_phase(). */
        default:
            FIXME("Unhandled shader phase input register %#x.\n", reg->type);
            return;
    }

    vkd3d_symbol_make_register(&reg_symbol, reg);
    vkd3d_symbol_set_register_info(&reg_symbol, val_id,
            SpvStorageClassMax /* Intermediate value */,
            VKD3D_TYPE_UINT, VKD3DSP_WRITEMASK_0);
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, val_id, reg);
}

static unsigned int vkd3d_dxbc_compiler_get_output_variable_index(
        struct vkd3d_dxbc_compiler *compiler, unsigned int register_idx)
{
    if (register_idx == ~0u) /* oDepth */
        return ARRAY_SIZE(compiler->private_output_variable) - 1;
    assert(register_idx < ARRAY_SIZE(compiler->private_output_variable) - 1);
    return register_idx;
}

static unsigned int get_shader_output_swizzle(const struct vkd3d_dxbc_compiler *compiler,
        unsigned int register_idx)
{
    const struct vkd3d_shader_compile_arguments *compile_args;

    if (!(compile_args = compiler->compile_args))
        return VKD3D_NO_SWIZZLE;
    if (register_idx >= compile_args->output_swizzle_count)
        return VKD3D_NO_SWIZZLE;
    return compile_args->output_swizzles[register_idx];
}

static bool is_dual_source_blending(const struct vkd3d_dxbc_compiler *compiler)
{
    const struct vkd3d_shader_compile_arguments *compile_args = compiler->compile_args;

    return compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL
            && compile_args && compile_args->dual_source_blending;
}

static void calculate_clip_or_cull_distance_mask(const struct vkd3d_shader_signature_element *e,
        uint32_t *mask)
{
    if (e->semantic_index >= sizeof(*mask) * CHAR_BIT / VKD3D_VEC4_SIZE)
    {
        FIXME("Invalid semantic index %u for clip/cull distance.\n", e->semantic_index);
        return;
    }

    *mask |= (e->mask & VKD3DSP_WRITEMASK_ALL) << (VKD3D_VEC4_SIZE * e->semantic_index);
}

/* Emits arrayed SPIR-V built-in variables. */
static void vkd3d_dxbc_compiler_emit_shader_signature_outputs(struct vkd3d_dxbc_compiler *compiler)
{
    const struct vkd3d_shader_signature *output_signature = compiler->output_signature;
    uint32_t clip_distance_mask = 0, clip_distance_id = 0;
    uint32_t cull_distance_mask = 0, cull_distance_id = 0;
    const struct vkd3d_spirv_builtin *builtin;
    unsigned int i, count;

    for (i = 0; i < output_signature->element_count; ++i)
    {
        const struct vkd3d_shader_signature_element *e = &output_signature->elements[i];

        switch (e->sysval_semantic)
        {
            case VKD3D_SV_CLIP_DISTANCE:
                calculate_clip_or_cull_distance_mask(e, &clip_distance_mask);
                break;
            case VKD3D_SV_CULL_DISTANCE:
                calculate_clip_or_cull_distance_mask(e, &cull_distance_mask);
    if (clip_distance_mask)
    {
        count = vkd3d_popcount(clip_distance_mask);
        builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SIV_CLIP_DISTANCE);
        clip_distance_id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler,
                builtin, SpvStorageClassOutput, count);
    if (cull_distance_mask)
    {
        count = vkd3d_popcount(cull_distance_mask);
        builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SIV_CULL_DISTANCE);
        cull_distance_id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler,
                builtin, SpvStorageClassOutput, count);

    for (i = 0; i < output_signature->element_count; ++i)
    {
        const struct vkd3d_shader_signature_element *e = &output_signature->elements[i];

        switch (e->sysval_semantic)
        {
            case VKD3D_SV_CLIP_DISTANCE:
                compiler->output_info[i].id = clip_distance_id;
                compiler->output_info[i].array_element_mask = clip_distance_mask;
            case VKD3D_SV_CULL_DISTANCE:
                compiler->output_info[i].id = cull_distance_id;
                compiler->output_info[i].array_element_mask = cull_distance_mask;
static void vkd3d_dxbc_compiler_emit_output_register(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_register *reg = &dst->reg;
    const struct vkd3d_spirv_builtin *builtin;
    struct vkd3d_symbol reg_symbol;
    uint32_t output_id;

    assert(!reg->idx[0].rel_addr);
    assert(!reg->idx[1].rel_addr);
    assert(reg->idx[1].offset == ~0u);

    if (!(builtin = get_spirv_builtin_for_register(reg->type)))
    {
        FIXME("Unhandled register %#x.\n", reg->type);
        return;
    }

    output_id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0);

    vkd3d_symbol_make_register(&reg_symbol, reg);
    vkd3d_symbol_set_register_info(&reg_symbol, output_id, SpvStorageClassOutput,
            builtin->component_type, vkd3d_write_mask_from_component_count(builtin->component_count));
    reg_symbol.info.reg.is_aggregate = builtin->spirv_array_size;
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, output_id, reg);
}

static void 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_signature_element *signature_element;
    const struct vkd3d_shader_signature *shader_signature;
    const struct vkd3d_shader_register *reg = &dst->reg;
    unsigned int component_idx, output_component_count;
    const struct vkd3d_spirv_builtin *builtin;
    const struct vkd3d_shader_phase *phase;
    struct vkd3d_symbol reg_symbol;
    SpvStorageClass storage_class;
    struct rb_entry *entry = NULL;
    unsigned int signature_idx;
    bool use_private_variable;
    phase = vkd3d_dxbc_compiler_get_current_shader_phase(compiler);
    is_patch_constant = phase && (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE);

    shader_signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature;

    array_size = is_control_point_phase(phase) ? compiler->output_control_point_count : 0;
    if (!(signature_element = vkd3d_find_signature_element_for_reg(shader_signature,
            &signature_idx, reg->idx[0].offset, dst->write_mask)))
    {
        FIXME("No signature element for shader output, ignoring shader output.\n");
        return;
    }

    builtin = vkd3d_get_spirv_builtin(compiler, dst->reg.type, sysval);
    write_mask = signature_element->mask & 0xff;

    component_idx = vkd3d_write_mask_get_component_idx(dst->write_mask);
    output_component_count = vkd3d_write_mask_component_count(signature_element->mask & 0xff);
    if (builtin)
    {
        component_type = builtin->component_type;
        if (!builtin->spirv_array_size)
            output_component_count = builtin->component_count;
        component_type = signature_element->component_type;
    if ((use_private_variable = builtin && builtin->spirv_array_size))
        write_mask = dst->write_mask;
    else if (get_shader_output_swizzle(compiler, signature_element->register_index) != VKD3D_NO_SWIZZLE
            || needs_private_io_variable(shader_signature, signature_element->register_index,
            builtin, &output_component_count, &write_mask))
    {
        use_private_variable = true;
        write_mask = VKD3DSP_WRITEMASK_ALL;
    }
    else
    {
        component_idx = vkd3d_write_mask_get_component_idx(write_mask);
    }
    vkd3d_symbol_make_register(&reg_symbol, reg);

    if (compiler->output_info[signature_idx].id)
    {
        id = compiler->output_info[signature_idx].id;
        if (compiler->output_info[signature_idx].array_element_mask)
        {
            use_private_variable = true;
            write_mask = VKD3DSP_WRITEMASK_ALL;
            entry = rb_get(&compiler->symbol_table, &reg_symbol);
        }
    }
    else if ((entry = rb_get(&compiler->symbol_table, &reg_symbol)))
    {
        id = RB_ENTRY_VALUE(entry, const struct vkd3d_symbol, entry)->id;
            id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size);
            vkd3d_dxbc_compiler_emit_register_execution_mode(compiler, &dst->reg);
            if (component_idx)
                FIXME("Unhandled component index %u.\n", component_idx);
        }
        else
        {
            unsigned int location = reg->idx[0].offset;

            if (is_patch_constant)
                location += compiler->output_signature->element_count;

            id = vkd3d_dxbc_compiler_emit_array_variable(compiler, &builder->global_stream,
                    storage_class, component_type, output_component_count, array_size);
            vkd3d_spirv_add_iface_variable(builder, id);

            if (is_dual_source_blending(compiler) && reg->idx[0].offset < 2)
            {
                vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, 0);
                vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationIndex, reg->idx[0].offset);
            }
            else
            {
                vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, location);
            if (component_idx)
                vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationComponent, component_idx);
        }
        if (is_patch_constant)
            vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0);

        vkd3d_dxbc_compiler_decorate_xfb_output(compiler, id, output_component_count, signature_element);
    compiler->output_info[signature_idx].id = id;
    compiler->output_info[signature_idx].component_type = component_type;
        var_id = RB_ENTRY_VALUE(entry, const struct vkd3d_symbol, entry)->id;
    else if (!use_private_variable)
        var_id = id;
    else
        var_id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
                storage_class, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    if (!entry)
    {
        vkd3d_symbol_set_register_info(&reg_symbol, var_id, storage_class,
                use_private_variable ? VKD3D_TYPE_FLOAT : component_type, write_mask);
        reg_symbol.info.reg.is_aggregate = use_private_variable ? false : array_size;
        if (!use_private_variable && is_control_point_phase(phase))
            reg_symbol.info.reg.member_idx = vkd3d_dxbc_compiler_get_invocation_id(compiler);
            reg_symbol.info.reg.is_dynamically_indexed = true;
        }
        vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);

        vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    }

        unsigned int idx = vkd3d_dxbc_compiler_get_output_variable_index(compiler, reg->idx[0].offset);
        compiler->private_output_variable[idx] = var_id;
        compiler->private_output_variable_write_mask[idx] |= dst->write_mask;
        if (!compiler->epilogue_function_id)
            compiler->epilogue_function_id = vkd3d_spirv_alloc_id(builder);
static void vkd3d_dxbc_compiler_emit_shader_phase_output(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_phase *phase, 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;
    const struct vkd3d_spirv_builtin *builtin;
    struct vkd3d_symbol reg_symbol;
    uint32_t *variable_id;

    variable_id = NULL;
    if ((builtin = get_spirv_builtin_for_sysval(compiler, sysval)))
    {
        if (builtin->spirv_builtin == SpvBuiltInTessLevelOuter)
            variable_id = &compiler->hs.tess_level_outer_id;
        else if (builtin->spirv_builtin == SpvBuiltInTessLevelInner)
            variable_id = &compiler->hs.tess_level_inner_id;
    }

    if (!variable_id)
    {
        FIXME("Unhandled shader phase output register %#x, sysval %#x.\n", reg->type, sysval);
        return;
    }

    if (!*variable_id)
    {
        *variable_id = vkd3d_dxbc_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0);
        if (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE)
            vkd3d_spirv_build_op_decorate(builder, *variable_id, SpvDecorationPatch, NULL, 0);
    }

    vkd3d_symbol_make_register(&reg_symbol, reg);
    vkd3d_symbol_set_register_info(&reg_symbol, *variable_id, SpvStorageClassOutput,
            builtin->component_type, vkd3d_write_mask_from_component_count(builtin->component_count));
    reg_symbol.info.reg.member_idx = builtin->member_idx;
    reg_symbol.info.reg.is_aggregate = builtin->spirv_array_size;
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static void vkd3d_dxbc_compiler_emit_store_shader_output(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_signature_element *output,
        const struct vkd3d_shader_output_info *output_info,
        uint32_t output_index_id, uint32_t val_id, unsigned int write_mask)
    unsigned int dst_write_mask, use_mask, uninit_mask, swizzle, mask;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, zero_id, ptr_type_id, chain_id, object_id;
    unsigned int i, index;
    dst_write_mask = output->mask & 0xff;
    write_mask &= dst_write_mask;
    use_mask = (output->mask >> 8) & 0xff;

    if (output_info->component_type != VKD3D_TYPE_FLOAT)
    {
        type_id = vkd3d_spirv_get_type_id(builder, output_info->component_type, VKD3D_VEC4_SIZE);
        val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
    }

    swizzle = get_shader_output_swizzle(compiler, output->register_index);
    uninit_mask = dst_write_mask & use_mask;
    if (uninit_mask)
    {
        /* Set values to 0 for not initialized shader output components. */
        zero_id = vkd3d_dxbc_compiler_get_constant_vector(compiler,
                output_info->component_type, VKD3D_VEC4_SIZE, 0);
        val_id = vkd3d_dxbc_compiler_emit_vector_shuffle(compiler,
                zero_id, val_id, swizzle, uninit_mask, output_info->component_type,
                vkd3d_write_mask_component_count(write_mask));
    }
    else
    {
        val_id = vkd3d_dxbc_compiler_emit_swizzle(compiler,
                val_id, VKD3DSP_WRITEMASK_ALL, output_info->component_type, swizzle, write_mask);
    if (output_index_id)
    {
        type_id = vkd3d_spirv_get_type_id(builder,
                output_info->component_type, vkd3d_write_mask_component_count(dst_write_mask));
        ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassOutput, type_id);
        output_id = vkd3d_spirv_build_op_access_chain1(builder, ptr_type_id, output_id, output_index_id);
    }
        vkd3d_dxbc_compiler_emit_store(compiler,
                output_id, dst_write_mask, output_info->component_type, SpvStorageClassOutput, write_mask, val_id);
    type_id = vkd3d_spirv_get_type_id(builder, output_info->component_type, 1);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassOutput, type_id);
    mask = output_info->array_element_mask;
    mask &= (1u << (output->semantic_index * VKD3D_VEC4_SIZE)) - 1;
    for (i = 0, index = vkd3d_popcount(mask); i < VKD3D_VEC4_SIZE; ++i)
    {
        if (!(write_mask & (VKD3DSP_WRITEMASK_0 << i)))
            continue;

        chain_id = vkd3d_spirv_build_op_access_chain1(builder,
                ptr_type_id, output_id, vkd3d_dxbc_compiler_get_constant_uint(compiler, index));
        object_id = vkd3d_dxbc_compiler_emit_swizzle(compiler, val_id,
                write_mask, output_info->component_type, VKD3D_NO_SWIZZLE, VKD3DSP_WRITEMASK_0 << i);
        vkd3d_dxbc_compiler_emit_store(compiler, chain_id, VKD3DSP_WRITEMASK_0 << index,
                output_info->component_type, SpvStorageClassOutput, VKD3DSP_WRITEMASK_0 << i, object_id);
        ++index;
    }
}

static void vkd3d_dxbc_compiler_emit_shader_epilogue_function(struct vkd3d_dxbc_compiler *compiler)
{
    uint32_t param_type_id[MAX_REG_OUTPUT + 1], param_id[MAX_REG_OUTPUT + 1] = {0};
    uint32_t void_id, type_id, ptr_type_id, function_type_id, function_id;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_signature *signature;
    const struct vkd3d_shader_phase *phase;
    uint32_t output_index_id = 0;
    unsigned int i, count;
    DWORD variable_idx;

    STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(param_id));
    STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(param_type_id));
    STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(compiler->private_output_variable_write_mask));
    phase = vkd3d_dxbc_compiler_get_current_shader_phase(compiler);
    is_patch_constant = phase && (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE);

    signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature;
    function_id = compiler->epilogue_function_id;

    void_id = vkd3d_spirv_get_op_type_void(builder);
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, 4);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPrivate, type_id);
    for (i = 0, count = 0; i < ARRAY_SIZE(compiler->private_output_variable); ++i)
    {
        if (compiler->private_output_variable[i])
            param_type_id[count++] = ptr_type_id;
    }
    function_type_id = vkd3d_spirv_get_op_type_function(builder, void_id, param_type_id, count);

    vkd3d_spirv_build_op_function(builder, void_id, function_id,
            SpvFunctionControlMaskNone, function_type_id);

    for (i = 0; i < ARRAY_SIZE(compiler->private_output_variable); ++i)
    {
        if (compiler->private_output_variable[i])
            param_id[i] = vkd3d_spirv_build_op_function_parameter(builder, ptr_type_id);
    }

    vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder));

    for (i = 0; i < ARRAY_SIZE(compiler->private_output_variable); ++i)
    {
        if (compiler->private_output_variable[i])
            param_id[i] = vkd3d_spirv_build_op_load(builder, type_id, param_id[i], SpvMemoryAccessMaskNone);
    }

    if (is_control_point_phase(phase))
        output_index_id = vkd3d_dxbc_compiler_emit_load_invocation_id(compiler);
    for (i = 0; i < signature->element_count; ++i)
    {
        variable_idx = vkd3d_dxbc_compiler_get_output_variable_index(compiler,
                signature->elements[i].register_index);

        if (!param_id[variable_idx])
            continue;

        vkd3d_dxbc_compiler_emit_store_shader_output(compiler,
                &signature->elements[i], &compiler->output_info[i], output_index_id,
                param_id[variable_idx], compiler->private_output_variable_write_mask[variable_idx]);
    }

    vkd3d_spirv_build_op_return(&compiler->spirv_builder);
    vkd3d_spirv_build_op_function_end(builder);

    memset(compiler->private_output_variable, 0, sizeof(compiler->private_output_variable));
    memset(compiler->private_output_variable_write_mask, 0, sizeof(compiler->private_output_variable_write_mask));
static void vkd3d_dxbc_compiler_emit_hull_shader_builtins(struct vkd3d_dxbc_compiler *compiler)
{
    struct vkd3d_shader_dst_param dst;

    memset(&dst, 0, sizeof(dst));
    dst.reg.type = VKD3DSPR_OUTPOINTID;
    dst.reg.idx[0].offset = ~0u;
    dst.reg.idx[1].offset = ~0u;
    dst.write_mask = VKD3DSP_WRITEMASK_0;
    vkd3d_dxbc_compiler_emit_input_register(compiler, &dst);
}

static void vkd3d_dxbc_compiler_emit_initial_declarations(struct vkd3d_dxbc_compiler *compiler)
{
    const struct vkd3d_shader_transform_feedback_info *xfb_info = compiler->xfb_info;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    switch (compiler->shader_type)
    {
        case VKD3D_SHADER_TYPE_VERTEX:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelVertex);
            break;
        case VKD3D_SHADER_TYPE_HULL:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelTessellationControl);
            vkd3d_dxbc_compiler_emit_hull_shader_builtins(compiler);
            break;
        case VKD3D_SHADER_TYPE_DOMAIN:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelTessellationEvaluation);
            break;
        case VKD3D_SHADER_TYPE_GEOMETRY:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelGeometry);
            break;
        case VKD3D_SHADER_TYPE_PIXEL:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelFragment);
            vkd3d_dxbc_compiler_emit_execution_mode(compiler, SpvExecutionModeOriginUpperLeft, NULL, 0);
            break;
        case VKD3D_SHADER_TYPE_COMPUTE:
            vkd3d_spirv_set_execution_model(builder, SpvExecutionModelGLCompute);
            break;
        default:
            ERR("Invalid shader type %#x.\n", compiler->shader_type);
    }

    if (xfb_info && xfb_info->element_count)
    {
        vkd3d_spirv_enable_capability(builder, SpvCapabilityTransformFeedback);
        vkd3d_dxbc_compiler_emit_execution_mode(compiler, SpvExecutionModeXfb, NULL, 0);
    }

    if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL)
        vkd3d_spirv_builder_begin_main_function(builder);

    vkd3d_dxbc_compiler_emit_shader_signature_outputs(compiler);
}

static size_t vkd3d_dxbc_compiler_get_current_function_location(struct vkd3d_dxbc_compiler *compiler)
{
    const struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_phase *phase;

    if ((phase = vkd3d_dxbc_compiler_get_current_shader_phase(compiler)))
        return phase->function_location;

    return builder->main_function_location;
}

static void vkd3d_dxbc_compiler_emit_dcl_global_flags(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    unsigned int flags = instruction->flags;

    if (flags & VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL)
    {
        vkd3d_dxbc_compiler_emit_execution_mode(compiler, SpvExecutionModeEarlyFragmentTests, NULL, 0);
        flags &= ~VKD3DSGF_FORCE_EARLY_DEPTH_STENCIL;
    }

    if (flags & ~(VKD3DSGF_REFACTORING_ALLOWED | VKD3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS))
        FIXME("Unhandled global flags %#x.\n", flags);
        WARN("Unhandled global flags %#x.\n", flags);
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;
    function_location = vkd3d_dxbc_compiler_get_current_function_location(compiler);
    vkd3d_spirv_begin_function_stream_insertion(builder, function_location);
    compiler->temp_count = instruction->declaration.count;
    for (i = 0; i < compiler->temp_count; ++i)
    {
        id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->function_stream,
                SpvStorageClassFunction, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
        if (!i)
            compiler->temp_id = id;
        assert(id == compiler->temp_id + i);

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

    vkd3d_spirv_end_function_stream_insertion(builder);
static void vkd3d_dxbc_compiler_emit_dcl_indexable_temp(struct vkd3d_dxbc_compiler *compiler,
          const struct vkd3d_shader_instruction *instruction)
{
    const struct vkd3d_shader_indexable_temp *temp = &instruction->declaration.indexable_temp;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_shader_register reg;
    struct vkd3d_symbol reg_symbol;
    uint32_t id;

    if (temp->component_count != 4)
        FIXME("Unhandled component count %u.\n", temp->component_count);

    memset(&reg, 0, sizeof(reg));
    reg.type = VKD3DSPR_IDXTEMP;
    reg.idx[0].offset = temp->register_idx;
    reg.idx[1].offset = ~0u;

    function_location = vkd3d_dxbc_compiler_get_current_function_location(compiler);
    vkd3d_spirv_begin_function_stream_insertion(builder, function_location);

    id = vkd3d_dxbc_compiler_emit_array_variable(compiler, &builder->function_stream,
            SpvStorageClassFunction, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE, temp->register_size);

    vkd3d_dxbc_compiler_emit_register_debug_name(builder, id, &reg);

    vkd3d_spirv_end_function_stream_insertion(builder);

    vkd3d_symbol_make_register(&reg_symbol, &reg);
    vkd3d_symbol_set_register_info(&reg_symbol, id,
            SpvStorageClassFunction, VKD3D_TYPE_FLOAT, VKD3DSP_WRITEMASK_ALL);
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static void vkd3d_dxbc_compiler_emit_push_constant_buffers(struct vkd3d_dxbc_compiler *compiler)
{
    const SpvStorageClass storage_class = SpvStorageClassPushConstant;
    uint32_t vec4_id, length_id, struct_id, pointer_type_id, var_id;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int i, j, count, reg_idx, cb_size;
    struct vkd3d_symbol reg_symbol;
    uint32_t *member_ids;

    count = 0;
    for (i = 0; i < compiler->shader_interface.push_constant_buffer_count; ++i)
        const struct vkd3d_push_constant_buffer_binding *cb = &compiler->push_constants[i];

        if (cb->reg.type)
            ++count;
    }
    if (!count)
        return;

    if (!(member_ids = vkd3d_calloc(count, sizeof(*member_ids))))
        return;

    vec4_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);

    for (i = 0, j = 0; i < compiler->shader_interface.push_constant_buffer_count; ++i)
        const struct vkd3d_push_constant_buffer_binding *cb = &compiler->push_constants[i];
        if (!cb->reg.type)
            continue;

        cb_size = cb->reg.idx[1].offset;
        length_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, cb_size);
        member_ids[j]  = vkd3d_spirv_build_op_type_array(builder, vec4_id, length_id);
        vkd3d_spirv_build_op_decorate1(builder, member_ids[j], SpvDecorationArrayStride, 16);

        ++j;
    }

    struct_id = vkd3d_spirv_build_op_type_struct(builder, member_ids, count);
    vkd3d_spirv_build_op_decorate(builder, struct_id, SpvDecorationBlock, NULL, 0);
    vkd3d_spirv_build_op_name(builder, struct_id, "push_cb");
    vkd3d_free(member_ids);

    pointer_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, struct_id);
    var_id = vkd3d_spirv_build_op_variable(builder, &builder->global_stream,
            pointer_type_id, storage_class, 0);

    for (i = 0, j = 0; i < compiler->shader_interface.push_constant_buffer_count; ++i)
        const struct vkd3d_push_constant_buffer_binding *cb = &compiler->push_constants[i];
        if (!cb->reg.type)
            continue;

        reg_idx = cb->reg.idx[0].offset;
        vkd3d_spirv_build_op_member_decorate1(builder, struct_id, j,
        vkd3d_spirv_build_op_member_name(builder, struct_id, j, "cb%u", reg_idx);

        vkd3d_symbol_make_register(&reg_symbol, &cb->reg);
        vkd3d_symbol_set_register_info(&reg_symbol, var_id,
                storage_class, VKD3D_TYPE_FLOAT, VKD3DSP_WRITEMASK_ALL);
        reg_symbol.info.reg.member_idx = j;
        vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);

        ++j;
    }
}

static void vkd3d_dxbc_compiler_emit_dcl_constant_buffer(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    uint32_t vec4_id, array_type_id, length_id, struct_id, pointer_type_id, var_id;
    const struct vkd3d_shader_constant_buffer *cb = &instruction->declaration.cb;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const SpvStorageClass storage_class = SpvStorageClassUniform;
    const struct vkd3d_shader_register *reg = &cb->src.reg;
    struct vkd3d_push_constant_buffer_binding *push_cb;
    struct vkd3d_symbol reg_symbol;

    assert(!(instruction->flags & ~VKD3DSI_INDEXED_DYNAMIC));

    if (cb->register_space)
        FIXME("Unhandled register space %u.\n", cb->register_space);
    if ((push_cb = vkd3d_dxbc_compiler_find_push_constant_buffer(compiler, reg)))
        /* Push constant buffers are handled in
         * vkd3d_dxbc_compiler_emit_push_constant_buffers().
         */
        unsigned int cb_size_in_bytes = cb->size * VKD3D_VEC4_SIZE * sizeof(uint32_t);
        if (cb_size_in_bytes > push_cb->pc.size)
            WARN("Constant buffer size %u exceeds push constant size %u.\n",
                    cb_size_in_bytes, push_cb->pc.size);
    vec4_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    length_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, cb->size);
    array_type_id = vkd3d_spirv_build_op_type_array(builder, vec4_id, length_id);
    vkd3d_spirv_build_op_decorate1(builder, array_type_id, SpvDecorationArrayStride, 16);

    struct_id = vkd3d_spirv_build_op_type_struct(builder, &array_type_id, 1);
    vkd3d_spirv_build_op_decorate(builder, struct_id, SpvDecorationBlock, NULL, 0);
    vkd3d_spirv_build_op_member_decorate1(builder, struct_id, 0, SpvDecorationOffset, 0);
    vkd3d_spirv_build_op_name(builder, struct_id, "cb%u_struct", cb->size);
    pointer_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, struct_id);
    var_id = vkd3d_spirv_build_op_variable(builder, &builder->global_stream,
            pointer_type_id, storage_class, 0);
    vkd3d_dxbc_compiler_emit_descriptor_binding_for_reg(compiler,
            var_id, reg, VKD3D_SHADER_RESOURCE_BUFFER, false);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    vkd3d_symbol_set_register_info(&reg_symbol, var_id,
            storage_class, VKD3D_TYPE_FLOAT, VKD3DSP_WRITEMASK_ALL);
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static void vkd3d_dxbc_compiler_emit_dcl_immediate_constant_buffer(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    const struct vkd3d_shader_immediate_constant_buffer *icb = instruction->declaration.icb;
    uint32_t *elements, length_id, type_id, const_id, ptr_type_id, icb_id;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_shader_register reg;
    struct vkd3d_symbol reg_symbol;
    unsigned int i;

    if (!(elements = vkd3d_calloc(icb->vec4_count, sizeof(*elements))))
        return;
    for (i = 0; i < icb->vec4_count; ++i)
        elements[i] = vkd3d_dxbc_compiler_get_constant(compiler,
                VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE, &icb->data[4 * i]);
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    length_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, icb->vec4_count);
    type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id);
    const_id = vkd3d_spirv_build_op_constant_composite(builder, type_id, elements, icb->vec4_count);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPrivate, type_id);
    icb_id = vkd3d_spirv_build_op_variable(builder, &builder->global_stream,
            ptr_type_id, SpvStorageClassPrivate, const_id);
    vkd3d_spirv_build_op_name(builder, icb_id, "icb");
    vkd3d_free(elements);

    memset(&reg, 0, sizeof(reg));
    reg.type = VKD3DSPR_IMMCONSTBUFFER;
    vkd3d_symbol_make_register(&reg_symbol, &reg);
    vkd3d_symbol_set_register_info(&reg_symbol, icb_id,
            SpvStorageClassPrivate, VKD3D_TYPE_FLOAT, VKD3DSP_WRITEMASK_ALL);
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static void vkd3d_dxbc_compiler_emit_dcl_sampler(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    const struct vkd3d_shader_register *reg = &instruction->declaration.sampler.src.reg;
    const SpvStorageClass storage_class = SpvStorageClassUniformConstant;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, ptr_type_id, var_id;
    struct vkd3d_symbol reg_symbol;

    if (instruction->declaration.sampler.register_space)
        FIXME("Unhandled register space %u.\n", instruction->declaration.sampler.register_space);

    if (vkd3d_dxbc_compiler_has_combined_sampler(compiler, NULL, reg))
    type_id = vkd3d_spirv_get_op_type_sampler(builder);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, type_id);
    var_id = vkd3d_spirv_build_op_variable(builder, &builder->global_stream,
            ptr_type_id, storage_class, 0);

    vkd3d_dxbc_compiler_emit_descriptor_binding_for_reg(compiler,
            var_id, reg, VKD3D_SHADER_RESOURCE_NONE, false);

    vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);

    vkd3d_symbol_make_register(&reg_symbol, reg);
    vkd3d_symbol_set_register_info(&reg_symbol, var_id,
            storage_class, VKD3D_TYPE_FLOAT, VKD3DSP_WRITEMASK_ALL);
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static const struct vkd3d_spirv_resource_type *vkd3d_dxbc_compiler_enable_resource_type(
        struct vkd3d_dxbc_compiler *compiler, enum vkd3d_shader_resource_type resource_type, bool is_uav)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_spirv_resource_type *resource_type_info;

    if (!(resource_type_info = vkd3d_get_spirv_resource_type(resource_type)))
        return NULL;

    if (resource_type_info->capability)
        vkd3d_spirv_enable_capability(builder, resource_type_info->capability);
    if (is_uav && resource_type_info->uav_capability)
        vkd3d_spirv_enable_capability(builder, resource_type_info->uav_capability);

    return resource_type_info;
static SpvImageFormat image_format_for_image_read(enum vkd3d_component_type data_type)
{
    /* The following formats are supported by Direct3D 11 hardware for UAV
     * typed loads. A newer hardware may support more formats for UAV typed
     * loads (see StorageImageReadWithoutFormat SPIR-V capability).
     */
    switch (data_type)
    {
        case VKD3D_TYPE_FLOAT:
            return SpvImageFormatR32f;
        case VKD3D_TYPE_INT:
            return SpvImageFormatR32i;
        case VKD3D_TYPE_UINT:
            return SpvImageFormatR32ui;
        default:
            FIXME("Unhandled type %#x.\n", data_type);
            return SpvImageFormatUnknown;
    }
}

static uint32_t vkd3d_dxbc_compiler_get_image_type_id(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, const struct vkd3d_spirv_resource_type *resource_type_info,
        enum vkd3d_component_type data_type, bool raw_structured, uint32_t depth)
    const struct vkd3d_shader_scan_info *scan_info = compiler->scan_info;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t sampled_type_id;
    SpvImageFormat format;

    format = SpvImageFormatUnknown;
    if (reg->type == VKD3DSPR_UAV
            && (raw_structured || (scan_info->uav_read_mask & (1u << reg->idx[0].offset))))
        format = image_format_for_image_read(data_type);

    sampled_type_id = vkd3d_spirv_get_type_id(builder, data_type, 1);
    return vkd3d_spirv_get_op_type_image(builder, sampled_type_id, resource_type_info->dim,