Skip to content
Snippets Groups Projects
spirv.c 146 KiB
Newer Older
static uint32_t vkd3d_dxbc_compiler_emit_sat(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD write_mask, uint32_t val_id)
{
    static const float zero[] = {0.0f, 0.0f, 0.0f, 0.0f};
    static const float one[] = {1.0f, 1.0f, 1.0f, 1.0f};

    unsigned int component_count = vkd3d_write_mask_component_count(write_mask);
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, zero_id, one_id;

    zero_id = vkd3d_dxbc_compiler_get_constant(compiler,
            VKD3D_TYPE_FLOAT, component_count, (const uint32_t *)zero);
    one_id = vkd3d_dxbc_compiler_get_constant(compiler,
            VKD3D_TYPE_FLOAT, component_count, (const uint32_t *)one);

    type_id = vkd3d_spirv_get_type_id(builder,
            vkd3d_component_type_from_data_type(reg->data_type), component_count);
    if (reg->data_type == VKD3D_DATA_FLOAT)
        return vkd3d_spirv_build_op_glsl_std450_nclamp(builder, type_id, val_id, zero_id, one_id);

    FIXME("Unhandled data type %#x.\n", reg->data_type);
    return val_id;
}

static void vkd3d_dxbc_compiler_emit_store_dst(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst, uint32_t val_id)
{
    assert(!(dst->modifiers & ~VKD3DSPDM_SATURATE));
    if (dst->modifiers & VKD3DSPDM_SATURATE)
        val_id = vkd3d_dxbc_compiler_emit_sat(compiler, &dst->reg, dst->write_mask, val_id);

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

/* The Vulkan spec says:
 *
 *   "The variable decorated with GlobalInvocationId must be declared as a
 *   three-component vector of 32-bit integers."
 *
 *   "The variable decorated with LocalInvocationId must be declared as a
 *   three-component vector of 32-bit integers."
 *
 *   "The variable decorated with WorkgroupId must be declared as a
 *   three-component vector of 32-bit integers."
 *
 *   "The variable decorated with FragCoord must be declared as a
 *   four-component vector of 32-bit floating-point values."
 *
 *   "Any variable decorated with Position must be declared as a four-component
 *   vector of 32-bit floating-point values."
 *
 *   "The variable decorated with VertexIndex must be declared as a scalar
 *   32-bit integer."
 */
static const struct vkd3d_spirv_builtin
{
    enum vkd3d_shader_input_sysval_semantic sysval;
    enum vkd3d_shader_register_type reg_type;

    enum vkd3d_component_type component_type;
    unsigned int component_count;
    SpvBuiltIn spirv_builtin;
}
vkd3d_spirv_builtin_table[] =
{
    {VKD3D_SIV_NONE, VKD3DSPR_THREADID,         VKD3D_TYPE_INT, 3, SpvBuiltInGlobalInvocationId},
    {VKD3D_SIV_NONE, VKD3DSPR_LOCALTHREADID,    VKD3D_TYPE_INT, 3, SpvBuiltInLocalInvocationId},
    {VKD3D_SIV_NONE, VKD3DSPR_LOCALTHREADINDEX, VKD3D_TYPE_INT, 1, SpvBuiltInLocalInvocationIndex},
    {VKD3D_SIV_NONE, VKD3DSPR_THREADGROUPID,    VKD3D_TYPE_INT, 3, SpvBuiltInWorkgroupId},

    {VKD3D_SIV_POSITION,  ~0u, VKD3D_TYPE_FLOAT, 4, SpvBuiltInPosition},
    {VKD3D_SIV_VERTEX_ID, ~0u, VKD3D_TYPE_INT,   1, SpvBuiltInVertexIndex},
};

static const struct vkd3d_spirv_builtin *vkd3d_get_spirv_builtin(enum vkd3d_shader_register_type reg_type,
        enum vkd3d_shader_input_sysval_semantic sysval)
{
    for (i = 0; i < ARRAY_SIZE(vkd3d_spirv_builtin_table); ++i)
        const struct vkd3d_spirv_builtin* current = &vkd3d_spirv_builtin_table[i];

        if (current->sysval == VKD3D_SIV_NONE && current->reg_type == reg_type)
            return current;

        if (current->reg_type == ~0u && current->sysval == sysval)
            return current;

    if (sysval != VKD3D_SIV_NONE
            || (reg_type != VKD3DSPR_INPUT && reg_type != VKD3DSPR_OUTPUT && reg_type != VKD3DSPR_COLOROUT))
        FIXME("Unhandled builtin (register type %#x, semantic %#x).\n", reg_type, sysval);
    return NULL;
}

static void vkd3d_dxbc_compiler_decorate_builtin(struct vkd3d_dxbc_compiler *compiler,
        uint32_t target_id, SpvBuiltIn builtin)
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    if (compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL && builtin == SpvBuiltInPosition)
        builtin = SpvBuiltInFragCoord;

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

static const struct vkd3d_shader_signature_element *vkd3d_find_signature_element_for_reg(
        const struct vkd3d_shader_signature *signature, unsigned int *signature_element_index,
        const struct vkd3d_shader_register *reg, DWORD write_mask)
{
    unsigned int signature_idx;

    for (signature_idx = 0; signature_idx < signature->element_count; ++signature_idx)
    {
        if (signature->elements[signature_idx].register_idx == reg->idx[0].offset
                && (signature->elements[signature_idx].mask & 0xff) == write_mask)
        {
            if (signature_element_index)
                *signature_element_index = signature_idx;
            return &signature->elements[signature_idx];
        }
    }

    FIXME("Could not find shader signature element (register %u, write mask %#x).\n",
            reg->idx[0].offset, write_mask);
    if (signature_element_index)
        *signature_element_index = ~0u;
    return NULL;
}

static uint32_t vkd3d_dxbc_compiler_emit_input(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst, enum vkd3d_shader_input_sysval_semantic sysval)
{
    unsigned int component_idx, component_count, input_component_count;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_signature_element *signature_element;
    const struct vkd3d_shader_register *reg = &dst->reg;
    const struct vkd3d_spirv_builtin *builtin;
    enum vkd3d_component_type component_type;
    uint32_t val_id = 0, input_id, var_id;
    uint32_t type_id, float_type_id;
    struct vkd3d_symbol reg_symbol;
    SpvStorageClass storage_class;
    bool use_private_var = false;
    /* vThreadIDInGroupFlattened is declared with no write mask in shader
     * bytecode generated by fxc. */
    if (!write_mask && reg->type == VKD3DSPR_LOCALTHREADINDEX)
    signature_element = vkd3d_find_signature_element_for_reg(compiler->input_signature,
            NULL, reg, write_mask);
    builtin = vkd3d_get_spirv_builtin(reg->type, sysval);

    component_idx = vkd3d_write_mask_get_component_idx(write_mask);
    component_count = vkd3d_write_mask_component_count(write_mask);
    if (builtin)
    {
        component_type = builtin->component_type;
        input_component_count = builtin->component_count;
    }
    else
    {
        component_type = signature_element ? signature_element->component_type : VKD3D_TYPE_FLOAT;
        input_component_count = component_count;
    assert(component_count <= input_component_count);
    storage_class = SpvStorageClassInput;
    input_id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
            storage_class, component_type, input_component_count);
    vkd3d_spirv_add_iface_variable(builder, input_id);
        vkd3d_dxbc_compiler_decorate_builtin(compiler, input_id, builtin->spirv_builtin);
        if (component_idx)
            FIXME("Unhandled component index %u.\n", component_idx);
        vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationLocation, reg->idx[0].offset);
        if (component_idx)
            vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationComponent, component_idx);
    }
    if (component_type != VKD3D_TYPE_FLOAT || component_count != VKD3D_VEC4_SIZE)
        type_id = vkd3d_spirv_get_type_id(builder, component_type, input_component_count);
        val_id = vkd3d_spirv_build_op_load(builder, type_id, input_id, SpvMemoryAccessMaskNone);
        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);
        }

        use_private_var = true;
    if (val_id && input_component_count != component_count)
        val_id = vkd3d_dxbc_compiler_emit_swizzle(compiler, val_id, VKD3DSP_NOSWIZZLE, write_mask);
    vkd3d_symbol_make_register(&reg_symbol, reg);
    if (!use_private_var)
    {
        var_id = input_id;
    }
    else if (!(entry = rb_get(&compiler->symbol_table, &reg_symbol)))
    {
        storage_class = SpvStorageClassPrivate;
        var_id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
                storage_class, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    }
    if (!entry)
    {
        reg_symbol.id = var_id;
        reg_symbol.info.storage_class = storage_class;
        vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
        vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    if (use_private_var)
    {
        assert(val_id);
        vkd3d_dxbc_compiler_emit_store_reg(compiler, reg, write_mask, val_id);
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)
{
    unsigned int component_idx, component_count, output_component_count;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_signature_element *signature_element;
    const struct vkd3d_shader_register *reg = &dst->reg;
    const struct vkd3d_spirv_builtin *builtin;
    struct vkd3d_symbol reg_symbol;
    SpvStorageClass storage_class;
    struct rb_entry *entry = NULL;
    unsigned int signature_idx;
    bool use_private_variable;
    uint32_t id, var_id;
    signature_element = vkd3d_find_signature_element_for_reg(compiler->output_signature,
            &signature_idx, reg, dst->write_mask);
    builtin = vkd3d_get_spirv_builtin(dst->reg.type, sysval);

    component_idx = vkd3d_write_mask_get_component_idx(dst->write_mask);
    component_count = vkd3d_write_mask_component_count(dst->write_mask);
    if (builtin)
    {
        component_type = builtin->component_type;
        output_component_count = builtin->component_count;
    }
    else
    {
        component_type = signature_element ? signature_element->component_type : VKD3D_TYPE_FLOAT;
        output_component_count = component_count;
    }
    assert(component_count <= output_component_count);

    id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
            storage_class, component_type, component_count);
    vkd3d_spirv_add_iface_variable(builder, id);
        vkd3d_dxbc_compiler_decorate_builtin(compiler, id, builtin->spirv_builtin);
        if (component_idx)
            FIXME("Unhandled component index %u.\n", component_idx);
    }
        vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, reg->idx[0].offset);
        if (component_idx)
            vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationComponent, component_idx);
    }
    if (signature_element)
    {
        compiler->output_info[signature_idx].id = id;
        compiler->output_info[signature_idx].component_type = component_type;
    }
    if ((use_private_variable = component_type != VKD3D_TYPE_FLOAT || component_count != VKD3D_VEC4_SIZE))
        storage_class = SpvStorageClassPrivate;
    vkd3d_symbol_make_register(&reg_symbol, reg);

    if (!use_private_variable)
        var_id = id;
    else if ((entry = rb_get(&compiler->symbol_table, &reg_symbol)))
        var_id = RB_ENTRY_VALUE(entry, const struct vkd3d_symbol, entry)->id;
    else
        var_id = vkd3d_dxbc_compiler_emit_variable(compiler, &builder->global_stream,
                storage_class, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    if (!entry)
    {
        reg_symbol.id = var_id;
        reg_symbol.info.storage_class = storage_class;
        vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);

        vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    }

    if (use_private_variable)
    {
        compiler->private_output_variable[reg->idx[0].offset] = var_id;
        if (!compiler->output_setup_function_id)
            compiler->output_setup_function_id = vkd3d_spirv_alloc_id(builder);
    }
static void vkd3d_dxbc_compiler_emit_dcl_global_flags(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    if (instruction->flags & ~(VKD3DSGF_REFACTORING_ALLOWED | VKD3DSGF_ENABLE_RAW_AND_STRUCTURED_BUFFERS))
        FIXME("Unrecognized global flags %#x.\n", instruction->flags);
    else
        WARN("Unhandled global flags %#x.\n", instruction->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;
    unsigned int i;
    /* FIXME: Make sure that function variables are declared at the beginning
     * of the first block in the function. Otherwise, we'll produce invalid
     * SPIR-V code. */
    assert(!compiler->temp_count);
    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);
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_register *reg = &instruction->declaration.src.reg;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_symbol reg_symbol;

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

    if (instruction->flags & VKD3DSI_INDEXED_DYNAMIC)
        vkd3d_spirv_enable_capability(builder, SpvCapabilityUniformBufferArrayDynamicIndexing);


    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, SpvStorageClassUniform, struct_id);
    var_id = vkd3d_spirv_build_op_variable(builder, &builder->global_stream,
            pointer_type_id, SpvStorageClassUniform, 0);
    vkd3d_dxbc_compiler_emit_descriptor_binding(compiler, var_id, reg);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    reg_symbol.id = var_id;
    reg_symbol.info.storage_class = SpvStorageClassUniform;
    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_build_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);
    reg_symbol.id = icb_id;
    reg_symbol.info.storage_class = SpvStorageClassPrivate;
    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.dst.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;

    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(compiler, var_id, reg);

    vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);

    vkd3d_symbol_make_register(&reg_symbol, reg);
    reg_symbol.id = var_id;
    reg_symbol.info.storage_class = storage_class;
    vkd3d_dxbc_compiler_put_symbol(compiler, &reg_symbol);
}

static const struct vkd3d_spirv_resource_type
    enum vkd3d_shader_resource_type resource_type;

    SpvDim dim;
    uint32_t arrayed;
    uint32_t ms;

    unsigned int coordinate_component_count;

    SpvCapability capability;
    SpvCapability uav_capability;
}
vkd3d_spirv_resource_type_table[] =
{
    {VKD3D_SHADER_RESOURCE_BUFFER,            SpvDimBuffer, 0, 0, 1,
            SpvCapabilitySampledBuffer, SpvCapabilityImageBuffer},
    {VKD3D_SHADER_RESOURCE_TEXTURE_1D,        SpvDim1D,     0, 0, 1,
            SpvCapabilitySampled1D, SpvCapabilityImage1D},
    {VKD3D_SHADER_RESOURCE_TEXTURE_2DMS,      SpvDim2D,     0, 1, 2},
    {VKD3D_SHADER_RESOURCE_TEXTURE_2D,        SpvDim2D,     0, 0, 2},
    {VKD3D_SHADER_RESOURCE_TEXTURE_3D,        SpvDim3D,     0, 0, 3},
    {VKD3D_SHADER_RESOURCE_TEXTURE_CUBE,      SpvDimCube,   0, 0, 3},
    {VKD3D_SHADER_RESOURCE_TEXTURE_1DARRAY,   SpvDim1D,     1, 0, 2,
            SpvCapabilitySampled1D, SpvCapabilityImage1D},
    {VKD3D_SHADER_RESOURCE_TEXTURE_2DARRAY,   SpvDim2D,     1, 0, 3},
    {VKD3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY, SpvDimCube,   1, 0, 3,
            SpvCapabilitySampledCubeArray, SpvCapabilityImageCubeArray},
static const struct vkd3d_spirv_resource_type *vkd3d_get_spirv_resource_type(
        enum vkd3d_shader_resource_type resource_type)
{
    unsigned int i;
    for (i = 0; i < ARRAY_SIZE(vkd3d_spirv_resource_type_table); ++i)
        const struct vkd3d_spirv_resource_type* current = &vkd3d_spirv_resource_type_table[i];

        if (current->resource_type == resource_type)
            return current;

    FIXME("Unhandled resource type %#x.\n", resource_type);
    return NULL;
}

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 void vkd3d_dxbc_compiler_emit_resource_declaration(struct vkd3d_dxbc_compiler *compiler,
{
    const SpvStorageClass storage_class = SpvStorageClassUniformConstant;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    const struct vkd3d_shader_register *reg = &semantic->reg.reg;
    const struct vkd3d_spirv_resource_type *resource_type_info;
    uint32_t sampled_type_id, type_id, ptr_type_id, var_id;
    enum vkd3d_component_type sampled_type;
    struct vkd3d_symbol resource_symbol;
    if (!(resource_type_info = vkd3d_dxbc_compiler_enable_resource_type(compiler,
            semantic->resource_type, is_uav)))
    {
        FIXME("Failed to emit resource declaration.\n");
        return;
    }

    sampled_type = vkd3d_component_type_from_data_type(semantic->resource_data_type);
    sampled_type_id = vkd3d_spirv_get_type_id(builder, sampled_type, 1);

    type_id = vkd3d_spirv_get_op_type_image(builder, sampled_type_id, resource_type_info->dim,
            0, resource_type_info->arrayed, resource_type_info->ms, is_uav ? 2 : 1, SpvImageFormatUnknown);
    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(compiler, var_id, reg);
    vkd3d_dxbc_compiler_emit_register_debug_name(builder, var_id, reg);
    resource_symbol.id = var_id;
    resource_symbol.info.resource.sampled_type = sampled_type;
    resource_symbol.info.resource.type_id = type_id;
    resource_symbol.info.resource.coordinate_mask = (1u << resource_type_info->coordinate_component_count) - 1;
    vkd3d_dxbc_compiler_put_symbol(compiler, &resource_symbol);
static void vkd3d_dxbc_compiler_emit_dcl_resource(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_dxbc_compiler_emit_resource_declaration(compiler, &instruction->declaration.semantic);
}

static void vkd3d_dxbc_compiler_emit_dcl_uav_typed(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    if (instruction->flags)
        FIXME("Unhandled flags %#x.\n", instruction->flags);

    vkd3d_dxbc_compiler_emit_resource_declaration(compiler, &instruction->declaration.semantic);
static void vkd3d_dxbc_compiler_emit_dcl_input(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_dxbc_compiler_emit_input(compiler, &instruction->declaration.dst, VKD3D_SIV_NONE);
}

static void vkd3d_dxbc_compiler_emit_interpolation_decorations(struct vkd3d_dxbc_compiler *compiler,
        uint32_t id, enum vkd3d_shader_interpolation_mode mode)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    switch (mode)
    {
        case VKD3DSIM_CONSTANT:
            vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationFlat, NULL, 0);
            break;
        case VKD3DSIM_LINEAR:
            break;
        default:
            FIXME("Unhandled interpolation mode %#x.\n", mode);
            break;
    }
}

static void vkd3d_dxbc_compiler_emit_dcl_input_ps(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    uint32_t input_id;

    input_id = vkd3d_dxbc_compiler_emit_input(compiler, &instruction->declaration.dst, VKD3D_SIV_NONE);
    vkd3d_dxbc_compiler_emit_interpolation_decorations(compiler, input_id, instruction->flags);
static void vkd3d_dxbc_compiler_emit_dcl_input_ps_siv(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    uint32_t input_id;

    input_id = vkd3d_dxbc_compiler_emit_input(compiler, &instruction->declaration.register_semantic.reg,
            instruction->declaration.register_semantic.sysval_semantic);
    if (!instruction->declaration.register_semantic.sysval_semantic)
        vkd3d_dxbc_compiler_emit_interpolation_decorations(compiler, input_id, instruction->flags);
static void vkd3d_dxbc_compiler_emit_dcl_input_sgv(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    vkd3d_dxbc_compiler_emit_input(compiler, &instruction->declaration.register_semantic.reg,
            instruction->declaration.register_semantic.sysval_semantic);
}

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.register_semantic.reg,
            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_BFREV,      SpvOpBitReverse},
        {VKD3DSIH_COUNTBITS,  SpvOpBitCount},
        {VKD3DSIH_DIV,        SpvOpFDiv},
        {VKD3DSIH_FTOI,       SpvOpConvertFToS},
        {VKD3DSIH_FTOU,       SpvOpConvertFToU},
        {VKD3DSIH_IADD,       SpvOpIAdd},
        {VKD3DSIH_ISHL,       SpvOpShiftLeftLogical},
        {VKD3DSIH_ISHR,       SpvOpShiftRightArithmetic},
        {VKD3DSIH_ITOF,       SpvOpConvertSToF},
        {VKD3DSIH_MUL,        SpvOpFMul},
        {VKD3DSIH_NOT,        SpvOpNot},
        {VKD3DSIH_OR,         SpvOpBitwiseOr},
        {VKD3DSIH_USHR,       SpvOpShiftRightLogical},
        {VKD3DSIH_UTOF,       SpvOpConvertUToF},
        {VKD3DSIH_XOR,        SpvOpBitwiseXor},
    };
    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)
    {
        ERR("Unexpected 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_src(compiler, &src[i], 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_dst(compiler, dst, val_id);
static enum GLSLstd450 vkd3d_dxbc_compiler_map_ext_glsl_instruction(
        const struct vkd3d_shader_instruction *instruction)
{
    static const struct
    {
        enum VKD3D_SHADER_INSTRUCTION_HANDLER handler_idx;
        enum GLSLstd450 glsl_inst;
    }
    glsl_insts[] =
    {
        {VKD3DSIH_EXP,             GLSLstd450Exp2},
        {VKD3DSIH_FIRSTBIT_HI,     GLSLstd450FindUMsb},
        {VKD3DSIH_FIRSTBIT_LO,     GLSLstd450FindILsb},
        {VKD3DSIH_FIRSTBIT_SHI,    GLSLstd450FindSMsb},
        {VKD3DSIH_FRC,             GLSLstd450Fract},
        {VKD3DSIH_IMAX,            GLSLstd450SMax},
        {VKD3DSIH_IMIN,            GLSLstd450SMin},
        {VKD3DSIH_LOG,             GLSLstd450Log2},
        {VKD3DSIH_MAD,             GLSLstd450Fma},
        {VKD3DSIH_MAX,             GLSLstd450FMax},
        {VKD3DSIH_MIN,             GLSLstd450FMin},
        {VKD3DSIH_ROUND_NI,        GLSLstd450Floor},
        {VKD3DSIH_ROUND_PI,        GLSLstd450Ceil},
        {VKD3DSIH_RSQ,             GLSLstd450InverseSqrt},
        {VKD3DSIH_SQRT,            GLSLstd450Sqrt},
        {VKD3DSIH_UMAX,            GLSLstd450UMax},
        {VKD3DSIH_UMIN,            GLSLstd450UMin},
    };
    unsigned int i;

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

    return GLSLstd450Bad;
}

static void vkd3d_dxbc_compiler_emit_ext_glsl_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_id[VKD3D_DXBC_MAX_SOURCE_COUNT];
    uint32_t instr_set_id, type_id, val_id;
    unsigned int component_count;
    enum GLSLstd450 glsl_inst;
    unsigned int i;

    glsl_inst = vkd3d_dxbc_compiler_map_ext_glsl_instruction(instruction);
    if (glsl_inst == GLSLstd450Bad)
    {
        ERR("Unexpected instruction %#x.\n", instruction->handler_idx);
    instr_set_id = vkd3d_spirv_get_glsl_std450_instr_set(builder);

    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_id[i] = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[i], dst->write_mask);

    val_id = vkd3d_spirv_build_op_ext_inst(builder, type_id,
            instr_set_id, glsl_inst, src_id, instruction->src_count);
    if (instruction->handler_idx == VKD3DSIH_FIRSTBIT_HI
            || instruction->handler_idx == VKD3DSIH_FIRSTBIT_SHI)
    {
        /* In D3D bits are numbered from the most significant bit. */
        val_id = vkd3d_spirv_build_op_isub(builder, type_id,
                vkd3d_dxbc_compiler_get_constant_uint(compiler, 31), val_id);
    }

    vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, 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;
    const struct vkd3d_shader_src_param *src = instruction->src;
    uint32_t val_id, dst_val_id, type_id, dst_id, src_id;
    uint32_t components[VKD3D_VEC4_SIZE];
    unsigned int i, component_count;
    component_count = vkd3d_write_mask_component_count(dst->write_mask);

    if (component_count == 1 || component_count == VKD3D_VEC4_SIZE
            || dst->modifiers || src->modifiers || src->reg.type == VKD3DSPR_IMMCONST)
    {
        val_id = vkd3d_dxbc_compiler_emit_load_src(compiler, src, dst->write_mask);
        vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, val_id);
    }
    else
    {
        struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

        type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
        dst_id = vkd3d_dxbc_compiler_get_register_id(compiler, &dst->reg);
        src_id = vkd3d_dxbc_compiler_get_register_id(compiler, &src->reg);

        val_id = vkd3d_spirv_build_op_load(builder, type_id, src_id, SpvMemoryAccessMaskNone);
        dst_val_id = vkd3d_spirv_build_op_load(builder, type_id, dst_id, SpvMemoryAccessMaskNone);

        for (i = 0; i < ARRAY_SIZE(components); ++i)
        {
            if (dst->write_mask & (VKD3DSP_WRITEMASK_0 << i))
                components[i] = VKD3D_VEC4_SIZE + vkd3d_swizzle_get_component(src->swizzle, i);
            else
                components[i] = i;
        }

        val_id = vkd3d_spirv_build_op_vector_shuffle(builder,
                type_id, dst_val_id, val_id, components, VKD3D_VEC4_SIZE);

        vkd3d_spirv_build_op_store(builder, dst_id, val_id, SpvMemoryAccessMaskNone);
    }
static uint32_t vkd3d_dxbc_compiler_emit_int_to_bool(struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_conditional_op condition, unsigned int component_count, uint32_t val_id)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    static const uint32_t zero[VKD3D_VEC4_SIZE];
    assert(!(condition & ~(VKD3D_SHADER_CONDITIONAL_OP_NZ | VKD3D_SHADER_CONDITIONAL_OP_Z)));

    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_BOOL, component_count);
    op = condition & VKD3D_SHADER_CONDITIONAL_OP_Z ? SpvOpIEqual : SpvOpINotEqual;
    return vkd3d_spirv_build_op_tr2(builder, &builder->function_stream, op, type_id, val_id,
            vkd3d_dxbc_compiler_get_constant(compiler, VKD3D_TYPE_UINT, component_count, zero));
}

static void vkd3d_dxbc_compiler_emit_movc(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 condition_id, src1_id, src2_id, type_id, val_id;
    unsigned int component_count;

    condition_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[0], dst->write_mask);
    src1_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[1], dst->write_mask);
    src2_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[2], dst->write_mask);

    component_count = vkd3d_write_mask_component_count(dst->write_mask);
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, component_count);

    condition_id = vkd3d_dxbc_compiler_emit_int_to_bool(compiler,
            VKD3D_SHADER_CONDITIONAL_OP_NZ, component_count, condition_id);
    val_id = vkd3d_spirv_build_op_select(builder, type_id, condition_id, src1_id, src2_id);

    vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, val_id);
static void vkd3d_dxbc_compiler_emit_swapc(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 condition_id, src1_id, src2_id, type_id, val_id;
    unsigned int component_count;

    assert(dst[0].write_mask == dst[1].write_mask);

    condition_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[0], dst->write_mask);
    src1_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[1], dst->write_mask);
    src2_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[2], dst->write_mask);

    component_count = vkd3d_write_mask_component_count(dst->write_mask);
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, component_count);

    condition_id = vkd3d_dxbc_compiler_emit_int_to_bool(compiler,
            VKD3D_SHADER_CONDITIONAL_OP_NZ, component_count, condition_id);

    val_id = vkd3d_spirv_build_op_select(builder, type_id, condition_id, src2_id, src1_id);
    vkd3d_dxbc_compiler_emit_store_dst(compiler, &dst[0], val_id);
    val_id = vkd3d_spirv_build_op_select(builder, type_id, condition_id, src1_id, src2_id);
    vkd3d_dxbc_compiler_emit_store_dst(compiler, &dst[1], val_id);
}

static void vkd3d_dxbc_compiler_emit_dot(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 type_id, val_id, src_ids[2];
    DWORD write_mask;
    unsigned int i;

    assert(vkd3d_write_mask_component_count(dst->write_mask) == 1);

    if (instruction->handler_idx == VKD3DSIH_DP4)
        write_mask = VKD3DSP_WRITEMASK_ALL;
    else if (instruction->handler_idx == VKD3DSIH_DP3)
        write_mask = VKD3DSP_WRITEMASK_0 | VKD3DSP_WRITEMASK_1 | VKD3DSP_WRITEMASK_2;
    else
        write_mask = VKD3DSP_WRITEMASK_0 | VKD3DSP_WRITEMASK_1;

    assert(instruction->src_count == ARRAY_SIZE(src_ids));
    for (i = 0; i < instruction->src_count; ++i)
        src_ids[i] = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[i], write_mask);

    type_id = vkd3d_spirv_get_type_id(builder,
            vkd3d_component_type_from_data_type(dst->reg.data_type), 1);

    val_id = vkd3d_spirv_build_op_tr2(builder, &builder->function_stream,
            SpvOpDot, type_id, src_ids[0], src_ids[1]);

    vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, val_id);
static void vkd3d_dxbc_compiler_emit_rcp(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 type_id, src_id, val_id;
    unsigned int component_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);

    src_id = vkd3d_dxbc_compiler_emit_load_src(compiler, src, dst->write_mask);
    val_id = vkd3d_spirv_build_op_fdiv(builder, type_id,
            vkd3d_dxbc_compiler_get_constant_float(compiler, 1.0f), src_id);
    vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, val_id);
}

static void vkd3d_dxbc_compiler_emit_imul(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 type_id, val_id, src0_id, src1_id;
    unsigned int component_count;

    if (dst[0].reg.type != VKD3DSPR_NULL)
        FIXME("Extended multiplies not implemented.\n"); /* SpvOpSMulExtended */

    if (dst[1].reg.type == VKD3DSPR_NULL)
        return;

    component_count = vkd3d_write_mask_component_count(dst[1].write_mask);
    type_id = vkd3d_spirv_get_type_id(builder,
            vkd3d_component_type_from_data_type(dst[1].reg.data_type), component_count);

    src0_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[0], dst[1].write_mask);
    src1_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[1], dst[1].write_mask);

    val_id = vkd3d_spirv_build_op_imul(builder, type_id, src0_id, src1_id);

    vkd3d_dxbc_compiler_emit_store_dst(compiler, &dst[1], val_id);
}

static void vkd3d_dxbc_compiler_emit_imad(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 type_id, val_id, src_ids[3];