Skip to content
Snippets Groups Projects
spirv.c 400 KiB
Newer Older
struct vkd3d_symbol_descriptor_array_data
{
    SpvStorageClass storage_class;
    uint32_t contained_type_id;
};

struct vkd3d_symbol
{
    struct rb_entry entry;

    enum
    {
        VKD3D_SYMBOL_REGISTER,
        VKD3D_SYMBOL_RESOURCE,
        VKD3D_SYMBOL_COMBINED_SAMPLER,
        struct vkd3d_symbol_register reg;
        struct vkd3d_symbol_resource resource;
        struct vkd3d_symbol_sampler sampler;
        struct vkd3d_symbol_combined_sampler combined_sampler;
        struct vkd3d_symbol_descriptor_array descriptor_array;
    /* The array declaration which this symbol maps to, or NULL. */
    const struct vkd3d_symbol *descriptor_array;

        struct vkd3d_symbol_register_data reg;
        struct vkd3d_symbol_resource_data resource;
        struct vkd3d_symbol_sampler_data sampler;
        struct vkd3d_symbol_descriptor_array_data descriptor_array;
    } info;
};

static int vkd3d_symbol_compare(const void *key, const struct rb_entry *entry)
{
    const struct vkd3d_symbol *a = key;
    const struct vkd3d_symbol *b = RB_ENTRY_VALUE(entry, const struct vkd3d_symbol, entry);

    if (a->type != b->type)
        return a->type - b->type;
    return memcmp(&a->key, &b->key, sizeof(a->key));
}

static void vkd3d_symbol_free(struct rb_entry *entry, void *context)
{
    struct vkd3d_symbol *s = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry);

    vkd3d_free(s);
}

static void vkd3d_symbol_make_register(struct vkd3d_symbol *symbol,
        const struct vkd3d_shader_register *reg)
{
    symbol->type = VKD3D_SYMBOL_REGISTER;
    memset(&symbol->key, 0, sizeof(symbol->key));
    symbol->key.reg.type = reg->type;
    if (vkd3d_shader_register_is_input(reg) && reg->idx[1].offset != ~0u)
        symbol->key.reg.idx = reg->idx[1].offset;
    else if (reg->type != VKD3DSPR_IMMCONSTBUFFER)
        symbol->key.reg.idx = reg->idx[0].offset;
static void vkd3d_symbol_set_register_info(struct vkd3d_symbol *symbol,
        uint32_t val_id, SpvStorageClass storage_class,
        enum vkd3d_shader_component_type component_type, DWORD write_mask)
    symbol->info.reg.storage_class = storage_class;
    symbol->info.reg.member_idx = 0;
    symbol->info.reg.component_type = component_type;
    symbol->info.reg.write_mask = write_mask;
    symbol->info.reg.dcl_mask = 0;
    symbol->info.reg.structure_stride = 0;
    symbol->info.reg.binding_base_idx = 0;
    symbol->info.reg.is_aggregate = false;
    symbol->info.reg.is_dynamically_indexed = false;
static void vkd3d_symbol_make_resource(struct vkd3d_symbol *symbol,
        const struct vkd3d_shader_register *reg)
{
    symbol->type = VKD3D_SYMBOL_RESOURCE;
    memset(&symbol->key, 0, sizeof(symbol->key));
    symbol->key.resource.type = reg->type;
    symbol->key.resource.idx = reg->idx[0].offset;
}

static void vkd3d_symbol_make_sampler(struct vkd3d_symbol *symbol,
        const struct vkd3d_shader_register *reg)
{
    symbol->type = VKD3D_SYMBOL_SAMPLER;
    memset(&symbol->key, 0, sizeof(symbol->key));
    symbol->key.sampler.id = reg->idx[0].offset;
}

static void vkd3d_symbol_make_combined_sampler(struct vkd3d_symbol *symbol,
        const struct vkd3d_shader_register *resource_reg, unsigned int sampler_space, unsigned int sampler_index)
{
    symbol->type = VKD3D_SYMBOL_COMBINED_SAMPLER;
    memset(&symbol->key, 0, sizeof(symbol->key));
    symbol->key.combined_sampler.resource_type = resource_reg->type;
    symbol->key.combined_sampler.resource_id = resource_reg->idx[0].offset;
    symbol->key.combined_sampler.sampler_space = sampler_space;
    symbol->key.combined_sampler.sampler_index = sampler_index;
static struct vkd3d_symbol *vkd3d_symbol_dup(const struct vkd3d_symbol *symbol)
{
    struct vkd3d_symbol *s;

    if (!(s = vkd3d_malloc(sizeof(*s))))
        return NULL;

    return memcpy(s, symbol, sizeof(*s));
}

static const char *debug_vkd3d_symbol(const struct vkd3d_symbol *symbol)
{
    switch (symbol->type)
    {
        case VKD3D_SYMBOL_REGISTER:
            return vkd3d_dbg_sprintf("register %#x, %u",
                    symbol->key.reg.type, symbol->key.reg.idx);
        case VKD3D_SYMBOL_RESOURCE:
            return vkd3d_dbg_sprintf("resource %#x, %u",
                    symbol->key.resource.type, symbol->key.resource.idx);
        case VKD3D_SYMBOL_SAMPLER:
            return vkd3d_dbg_sprintf("sampler %u",
                    symbol->key.sampler.id);
        default:
            return vkd3d_dbg_sprintf("type %#x", symbol->type);
    }
}

struct vkd3d_if_cf_info
    size_t stream_location;
    unsigned int id;
    uint32_t merge_block_id;
    uint32_t else_block_id;
};

struct vkd3d_loop_cf_info
{
    uint32_t header_block_id;
    uint32_t continue_block_id;
    uint32_t merge_block_id;
};

struct vkd3d_switch_cf_info
{
    size_t stream_location;
    uint32_t selector_id;
    uint32_t merge_block_id;
    uint32_t default_block_id;
    uint32_t *case_blocks;
    size_t case_blocks_size;
struct vkd3d_control_flow_info
{
    union
    {
        struct vkd3d_loop_cf_info loop;
        struct vkd3d_switch_cf_info switch_;
        VKD3D_BLOCK_LOOP,
{
    struct vkd3d_shader_register reg;
    unsigned int idx;
    unsigned int instance_count;
struct vkd3d_shader_spec_constant
{
    enum vkd3d_shader_parameter_name name;
    uint32_t id;
};

struct vkd3d_hull_shader_variables
{
    uint32_t tess_level_outer_id;
    uint32_t tess_level_inner_id;
struct vkd3d_dxbc_compiler
{
    struct vkd3d_spirv_builder spirv_builder;

    struct vkd3d_shader_message_context *message_context;
    const struct vkd3d_shader_location *location;

    struct rb_tree symbol_table;
    uint32_t temp_id;
    unsigned int temp_count;
    struct vkd3d_hull_shader_variables hs;
    uint32_t sample_positions_id;

    enum vkd3d_shader_type shader_type;
    unsigned int loop_id;
    unsigned int control_flow_depth;
    struct vkd3d_control_flow_info *control_flow_info;
    size_t control_flow_info_size;
    struct vkd3d_shader_interface_info shader_interface;
    struct vkd3d_shader_descriptor_offset_info offset_info;
    uint32_t descriptor_offsets_member_id;
    uint32_t push_constants_var_id;
    uint32_t *descriptor_offset_ids;
    struct vkd3d_push_constant_buffer_binding *push_constants;
    const struct vkd3d_shader_spirv_target_info *spirv_target_info;
    bool after_declarations_section;
    const struct vkd3d_shader_signature *input_signature;
    const struct vkd3d_shader_signature *output_signature;
    const struct vkd3d_shader_signature *patch_constant_signature;
    const struct vkd3d_shader_transform_feedback_info *xfb_info;
        enum vkd3d_shader_component_type component_type;
    uint32_t private_output_variable[MAX_REG_OUTPUT + 1]; /* 1 entry for oDepth */
    uint32_t private_output_variable_array_idx[MAX_REG_OUTPUT + 1]; /* 1 entry for oDepth */
    uint32_t private_output_variable_write_mask[MAX_REG_OUTPUT + 1]; /* 1 entry for oDepth */
    const struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info;
    unsigned int input_control_point_count;
    unsigned int output_control_point_count;

    unsigned int shader_phase_count;
    struct vkd3d_shader_phase *shader_phases;
    size_t shader_phases_size;

    uint32_t current_spec_constant_id;
    unsigned int spec_constant_count;
    struct vkd3d_shader_spec_constant *spec_constants;
    size_t spec_constants_size;
    enum vkd3d_shader_compile_option_formatting_flags formatting;

    struct vkd3d_string_buffer_cache string_buffers;
static bool is_control_point_phase(const struct vkd3d_shader_phase *phase)
{
    return phase && phase->type == VKD3DSIH_HS_CONTROL_POINT_PHASE;
}

static void vkd3d_dxbc_compiler_emit_initial_declarations(struct vkd3d_dxbc_compiler *compiler);
static const char *vkd3d_dxbc_compiler_get_entry_point_name(const struct vkd3d_dxbc_compiler *compiler)
{
    const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info;

    return info && info->entry_point ? info->entry_point : "main";
}

struct vkd3d_dxbc_compiler *vkd3d_dxbc_compiler_create(const struct vkd3d_shader_version *shader_version,
        const struct vkd3d_shader_desc *shader_desc, const struct vkd3d_shader_compile_info *compile_info,
        const struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info,
        struct vkd3d_shader_message_context *message_context, const struct vkd3d_shader_location *location)
    const struct vkd3d_shader_signature *patch_constant_signature = &shader_desc->patch_constant_signature;
    const struct vkd3d_shader_signature *output_signature = &shader_desc->output_signature;
    const struct vkd3d_shader_interface_info *shader_interface;
    const struct vkd3d_shader_descriptor_offset_info *offset_info;
    const struct vkd3d_shader_spirv_target_info *target_info;
    struct vkd3d_dxbc_compiler *compiler;
    unsigned int max_element_count;

    if (!(compiler = vkd3d_malloc(sizeof(*compiler))))
        return NULL;

    memset(compiler, 0, sizeof(*compiler));
    compiler->message_context = message_context;
    if ((target_info = vkd3d_find_struct(compile_info->next, SPIRV_TARGET_INFO)))
    {
        switch (target_info->environment)
        {
            case VKD3D_SHADER_SPIRV_ENVIRONMENT_OPENGL_4_5:
            case VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0:
                break;
            default:
                WARN("Invalid target environment %#x.\n", target_info->environment);
                vkd3d_free(compiler);
                return NULL;
        }

        compiler->spirv_target_info = target_info;
    }

    max_element_count = max(output_signature->element_count, patch_constant_signature->element_count);
    if (!(compiler->output_info = vkd3d_calloc(max_element_count, sizeof(*compiler->output_info))))
    vkd3d_spirv_builder_init(&compiler->spirv_builder, vkd3d_dxbc_compiler_get_entry_point_name(compiler));
    compiler->formatting = VKD3D_SHADER_COMPILE_OPTION_FORMATTING_INDENT
            | VKD3D_SHADER_COMPILE_OPTION_FORMATTING_HEADER;
    for (i = 0; i < compile_info->option_count; ++i)
    {
        const struct vkd3d_shader_compile_option *option = &compile_info->options[i];

        switch (option->name)
        {
            case VKD3D_SHADER_COMPILE_OPTION_STRIP_DEBUG:
                compiler->strip_debug = !!option->value;
                break;

            case VKD3D_SHADER_COMPILE_OPTION_BUFFER_UAV:
                if (option->value == VKD3D_SHADER_COMPILE_OPTION_BUFFER_UAV_STORAGE_TEXEL_BUFFER)
                    compiler->ssbo_uavs = false;
                else if (option->value == VKD3D_SHADER_COMPILE_OPTION_BUFFER_UAV_STORAGE_BUFFER)
                    compiler->ssbo_uavs = true;
                else
                    WARN("Ignoring unrecognised value %#x for option %#x.\n", option->value, option->name);
                break;

            case VKD3D_SHADER_COMPILE_OPTION_FORMATTING:
                compiler->formatting = option->value;
                break;

            default:
                WARN("Ignoring unrecognised option %#x with value %#x.\n", option->name, option->value);
                break;
        }
    }
    rb_init(&compiler->symbol_table, vkd3d_symbol_compare);

    compiler->shader_type = shader_version->type;

    compiler->input_signature = &shader_desc->input_signature;
    compiler->output_signature = &shader_desc->output_signature;
    compiler->patch_constant_signature = &shader_desc->patch_constant_signature;
    if ((shader_interface = vkd3d_find_struct(compile_info->next, INTERFACE_INFO)))
        compiler->xfb_info = vkd3d_find_struct(compile_info->next, TRANSFORM_FEEDBACK_INFO);
        compiler->shader_interface = *shader_interface;
        if (shader_interface->push_constant_buffer_count)
            if (!(compiler->push_constants = vkd3d_calloc(shader_interface->push_constant_buffer_count,
                    sizeof(*compiler->push_constants))))
            {
                vkd3d_dxbc_compiler_destroy(compiler);
                return NULL;
            }
            for (i = 0; i < shader_interface->push_constant_buffer_count; ++i)
                compiler->push_constants[i].pc = shader_interface->push_constant_buffers[i];

        if ((offset_info = vkd3d_find_struct(shader_interface->next, DESCRIPTOR_OFFSET_INFO)))
        {
            compiler->offset_info = *offset_info;
            if (compiler->offset_info.descriptor_table_count && !(compiler->descriptor_offset_ids = vkd3d_calloc(
                    compiler->offset_info.descriptor_table_count, sizeof(*compiler->descriptor_offset_ids))))
            {
                vkd3d_dxbc_compiler_destroy(compiler);
                return NULL;
            }
    compiler->scan_descriptor_info = scan_descriptor_info;
    vkd3d_string_buffer_cache_init(&compiler->string_buffers);

    vkd3d_dxbc_compiler_emit_initial_declarations(compiler);
static bool vkd3d_dxbc_compiler_use_storage_buffer(const struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_symbol_resource_data *resource)
{
    return compiler->ssbo_uavs && resource->resource_type_info->resource_type == VKD3D_SHADER_RESOURCE_BUFFER;
}

static enum vkd3d_shader_spirv_environment vkd3d_dxbc_compiler_get_target_environment(
        const struct vkd3d_dxbc_compiler *compiler)
    const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info;

    return info ? info->environment : VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0;
static bool vkd3d_dxbc_compiler_is_opengl_target(const struct vkd3d_dxbc_compiler *compiler)
{
    return vkd3d_dxbc_compiler_get_target_environment(compiler) == VKD3D_SHADER_SPIRV_ENVIRONMENT_OPENGL_4_5;
static bool vkd3d_dxbc_compiler_is_target_extension_supported(const struct vkd3d_dxbc_compiler *compiler,
    const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info;
static bool vkd3d_dxbc_compiler_check_shader_visibility(const struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_visibility visibility)
{
    switch (visibility)
    {
        case VKD3D_SHADER_VISIBILITY_ALL:
            return true;
        case VKD3D_SHADER_VISIBILITY_VERTEX:
            return compiler->shader_type == VKD3D_SHADER_TYPE_VERTEX;
        case VKD3D_SHADER_VISIBILITY_HULL:
            return compiler->shader_type == VKD3D_SHADER_TYPE_HULL;
        case VKD3D_SHADER_VISIBILITY_DOMAIN:
            return compiler->shader_type == VKD3D_SHADER_TYPE_DOMAIN;
        case VKD3D_SHADER_VISIBILITY_GEOMETRY:
            return compiler->shader_type == VKD3D_SHADER_TYPE_GEOMETRY;
        case VKD3D_SHADER_VISIBILITY_PIXEL:
            return compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL;
        case VKD3D_SHADER_VISIBILITY_COMPUTE:
            return compiler->shader_type == VKD3D_SHADER_TYPE_COMPUTE;
        default:
            ERR("Invalid shader visibility %#x.\n", visibility);
            return false;
static struct vkd3d_push_constant_buffer_binding *vkd3d_dxbc_compiler_find_push_constant_buffer(
        const struct vkd3d_dxbc_compiler *compiler, const struct vkd3d_shader_constant_buffer *cb)
    unsigned int register_space = cb->range.space;
    unsigned int reg_idx = cb->range.first;
    if (cb->range.first != cb->range.last)
        return NULL;

    for (i = 0; i < compiler->shader_interface.push_constant_buffer_count; ++i)
        struct vkd3d_push_constant_buffer_binding *current = &compiler->push_constants[i];
        if (!vkd3d_dxbc_compiler_check_shader_visibility(compiler, current->pc.shader_visibility))
            continue;

        if (current->pc.register_space == register_space && current->pc.register_index == reg_idx)
static bool vkd3d_dxbc_compiler_has_combined_sampler(const struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_resource *resource, const struct vkd3d_shader_sampler *sampler)
    const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
    const struct vkd3d_shader_combined_resource_sampler *combined_sampler;
    unsigned int i;

    if (!shader_interface->combined_sampler_count)
        return false;

    if (resource && (resource->reg.reg.type == VKD3DSPR_UAV || resource->range.last != resource->range.first))
        return false;

    if (sampler && sampler->range.first != sampler->range.last)
        return false;

    for (i = 0; i < shader_interface->combined_sampler_count; ++i)
    {
        combined_sampler = &shader_interface->combined_samplers[i];

        if (!vkd3d_dxbc_compiler_check_shader_visibility(compiler, combined_sampler->shader_visibility))
            continue;

        if ((!resource || (combined_sampler->resource_space == resource->range.space
                && combined_sampler->resource_index == resource->range.first))
                && (!sampler || (combined_sampler->sampler_space == sampler->range.space
                && combined_sampler->sampler_index == sampler->range.first)))
static void VKD3D_PRINTF_FUNC(3, 4) vkd3d_dxbc_compiler_error(struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_error error, const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vkd3d_shader_verror(compiler->message_context, compiler->location, error, format, args);
static struct vkd3d_string_buffer *vkd3d_shader_register_range_string(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register_range *range)
{
    struct vkd3d_string_buffer *buffer = vkd3d_string_buffer_get(&compiler->string_buffers);

    if (!buffer)
        return NULL;

    if (range->last != ~0u)
        vkd3d_string_buffer_printf(buffer, "[%u:%u]", range->first, range->last);
    else
        vkd3d_string_buffer_printf(buffer, "[%u:*]", range->first);

    return buffer;
}

static struct vkd3d_shader_descriptor_binding vkd3d_dxbc_compiler_get_descriptor_binding(
        struct vkd3d_dxbc_compiler *compiler, const struct vkd3d_shader_register *reg,
        const struct vkd3d_shader_register_range *range, enum vkd3d_shader_resource_type resource_type,
        bool is_uav_counter, struct vkd3d_descriptor_binding_address *binding_address)
    const struct vkd3d_shader_interface_info *shader_interface = &compiler->shader_interface;
    unsigned int register_last = (range->last == ~0u) ? range->first : range->last;
    const struct vkd3d_shader_descriptor_offset *binding_offsets;
    enum vkd3d_shader_descriptor_type descriptor_type;
    enum vkd3d_shader_binding_flag resource_type_flag;
    struct vkd3d_shader_descriptor_binding binding;
        descriptor_type = VKD3D_SHADER_DESCRIPTOR_TYPE_CBV;
        descriptor_type = VKD3D_SHADER_DESCRIPTOR_TYPE_SRV;
        descriptor_type = VKD3D_SHADER_DESCRIPTOR_TYPE_UAV;
        descriptor_type = VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER;
        FIXME("Unhandled register type %#x.\n", reg->type);
        vkd3d_dxbc_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_REGISTER_TYPE,
                "Encountered invalid/unhandled register type %#x.", reg->type);
    resource_type_flag = resource_type == VKD3D_SHADER_RESOURCE_BUFFER
            ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE;

    if (is_uav_counter)
    {
        assert(descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV);
        binding_offsets = compiler->offset_info.uav_counter_offsets;
        for (i = 0; i < shader_interface->uav_counter_count; ++i)
        {
            const struct vkd3d_shader_uav_counter_binding *current = &shader_interface->uav_counters[i];

            if (!vkd3d_dxbc_compiler_check_shader_visibility(compiler, current->shader_visibility))
                continue;

            if (current->register_space != range->space || current->register_index > range->first
                    || current->binding.count <= register_last - current->register_index)
                FIXME("Atomic counter offsets are not supported yet.\n");
                vkd3d_dxbc_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_INVALID_DESCRIPTOR_BINDING,
                        "Descriptor binding for UAV counter %u, space %u has unsupported ‘offset’ %u.",
                        range->first, range->space, current->offset);
            binding_address->binding_base_idx = current->register_index
                    - (binding_offsets ? binding_offsets[i].static_offset : 0);
            binding_address->push_constant_index = binding_offsets ? binding_offsets[i].dynamic_offset_index : ~0u;
        }
        if (shader_interface->uav_counter_count)
            FIXME("Could not find descriptor binding for UAV counter %u, space %u.\n", range->first, range->space);
            vkd3d_dxbc_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_BINDING_NOT_FOUND,
                    "Could not find descriptor binding for UAV counter %u, space %u.", range->first, range->space);
        binding_offsets = compiler->offset_info.binding_offsets;
        for (i = 0; i < shader_interface->binding_count; ++i)
            const struct vkd3d_shader_resource_binding *current = &shader_interface->bindings[i];
            if (!(current->flags & resource_type_flag))
                continue;

            if (!vkd3d_dxbc_compiler_check_shader_visibility(compiler, current->shader_visibility))
                continue;

            if (current->type != descriptor_type || current->register_space != range->space
                    || current->register_index > range->first
                    || current->binding.count <= register_last - current->register_index)
            binding_address->binding_base_idx = current->register_index
                    - (binding_offsets ? binding_offsets[i].static_offset : 0);
            binding_address->push_constant_index = binding_offsets ? binding_offsets[i].dynamic_offset_index : ~0u;
        if (shader_interface->binding_count)
            struct vkd3d_string_buffer *buffer = vkd3d_shader_register_range_string(compiler, range);
            const char *range_str = buffer ? buffer->buffer : "";
            FIXME("Could not find descriptor binding for type %#x, space %u, registers %s, shader type %#x.\n",
                    descriptor_type, range->space, range_str, compiler->shader_type);
            vkd3d_dxbc_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_DESCRIPTOR_BINDING_NOT_FOUND,
                    "Could not find descriptor binding for type %#x, space %u, registers %s, shader type %#x.",
                    descriptor_type, range->space, range_str, compiler->shader_type);
            vkd3d_string_buffer_release(&compiler->string_buffers, buffer);
    binding_address->binding_base_idx = range->first;
    binding_address->push_constant_index = ~0u;
    binding.binding = compiler->binding_idx++;
    return binding;
}

static void vkd3d_dxbc_compiler_emit_descriptor_binding(struct vkd3d_dxbc_compiler *compiler,
        uint32_t variable_id, const struct vkd3d_shader_descriptor_binding *binding)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    vkd3d_spirv_build_op_decorate1(builder, variable_id, SpvDecorationDescriptorSet, binding->set);
    vkd3d_spirv_build_op_decorate1(builder, variable_id, SpvDecorationBinding, binding->binding);
}

static void vkd3d_dxbc_compiler_decorate_nonuniform(struct vkd3d_dxbc_compiler *compiler,
        uint32_t expression_id)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    vkd3d_spirv_enable_capability(builder, SpvCapabilityShaderNonUniformEXT);
    vkd3d_spirv_build_op_decorate(builder, expression_id, SpvDecorationNonUniformEXT, NULL, 0);
}

static const struct vkd3d_symbol *vkd3d_dxbc_compiler_put_symbol(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_symbol *symbol)
{
    struct vkd3d_symbol *s;

    s = vkd3d_symbol_dup(symbol);
    if (rb_put(&compiler->symbol_table, s, &s->entry) == -1)
    {
        ERR("Failed to insert symbol entry (%s).\n", debug_vkd3d_symbol(symbol));
static uint32_t vkd3d_dxbc_compiler_get_constant(struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_component_type component_type, unsigned int component_count, const uint32_t *values)
    uint32_t type_id, scalar_type_id, component_ids[VKD3D_VEC4_SIZE];
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int i;

    assert(0 < component_count && component_count <= VKD3D_VEC4_SIZE);
    type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count);
        case VKD3D_SHADER_COMPONENT_UINT:
        case VKD3D_SHADER_COMPONENT_INT:
        case VKD3D_SHADER_COMPONENT_FLOAT:
            break;
        default:
            FIXME("Unhandled component_type %#x.\n", component_type);
            return vkd3d_spirv_build_op_undef(builder, &builder->global_stream, type_id);
    }

    if (component_count == 1)
    {
        return vkd3d_spirv_get_op_constant(builder, type_id, *values);
        scalar_type_id = vkd3d_spirv_get_type_id(builder, component_type, 1);
        for (i = 0; i < component_count; ++i)
            component_ids[i] = vkd3d_spirv_get_op_constant(builder, scalar_type_id, values[i]);
        return vkd3d_spirv_get_op_constant_composite(builder, type_id, component_ids, component_count);
static uint32_t vkd3d_dxbc_compiler_get_constant64(struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_component_type component_type, unsigned int component_count, const uint64_t *values)
{
    uint32_t type_id, scalar_type_id, component_ids[VKD3D_DVEC2_SIZE];
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int i;

    assert(0 < component_count && component_count <= VKD3D_DVEC2_SIZE);
    type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count);

    if (component_type != VKD3D_SHADER_COMPONENT_DOUBLE)
    {
        FIXME("Unhandled component_type %#x.\n", component_type);
        return vkd3d_spirv_build_op_undef(builder, &builder->global_stream, type_id);
    }

    if (component_count == 1)
    {
        return vkd3d_spirv_get_op_constant64(builder, type_id, *values);
    }
    else
    {
        scalar_type_id = vkd3d_spirv_get_type_id(builder, component_type, 1);
        for (i = 0; i < component_count; ++i)
            component_ids[i] = vkd3d_spirv_get_op_constant64(builder, scalar_type_id, values[i]);
        return vkd3d_spirv_get_op_constant_composite(builder, type_id, component_ids, component_count);
    }
}

static uint32_t vkd3d_dxbc_compiler_get_constant_uint(struct vkd3d_dxbc_compiler *compiler,
        uint32_t value)
{
    return vkd3d_dxbc_compiler_get_constant(compiler, VKD3D_SHADER_COMPONENT_UINT, 1, &value);
static uint32_t vkd3d_dxbc_compiler_get_constant_float(struct vkd3d_dxbc_compiler *compiler,
        float value)
{
    return vkd3d_dxbc_compiler_get_constant(compiler, VKD3D_SHADER_COMPONENT_FLOAT, 1, (uint32_t *)&value);
static uint32_t vkd3d_dxbc_compiler_get_constant_vector(struct vkd3d_dxbc_compiler *compiler,
        enum vkd3d_shader_component_type component_type, unsigned int component_count, uint32_t value)
{
    const uint32_t values[] = {value, value, value, value};
    return vkd3d_dxbc_compiler_get_constant(compiler, component_type, component_count, values);
}

static uint32_t vkd3d_dxbc_compiler_get_constant_uint_vector(struct vkd3d_dxbc_compiler *compiler,
        uint32_t value, unsigned int component_count)
{
    return vkd3d_dxbc_compiler_get_constant_vector(compiler, VKD3D_SHADER_COMPONENT_UINT, component_count, value);
static uint32_t vkd3d_dxbc_compiler_get_constant_float_vector(struct vkd3d_dxbc_compiler *compiler,
        float value, unsigned int component_count)
{
    const float values[] = {value, value, value, value};
    return vkd3d_dxbc_compiler_get_constant(compiler, VKD3D_SHADER_COMPONENT_FLOAT,
            component_count, (const uint32_t *)values);
static uint32_t vkd3d_dxbc_compiler_get_constant_double_vector(struct vkd3d_dxbc_compiler *compiler,
        double value, unsigned int component_count)
{
    const double values[] = {value, value};
    return vkd3d_dxbc_compiler_get_constant64(compiler, VKD3D_SHADER_COMPONENT_DOUBLE,
            component_count, (const uint64_t *)values);
}

static uint32_t vkd3d_dxbc_compiler_get_type_id_for_reg(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_register *reg, DWORD write_mask)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    return vkd3d_spirv_get_type_id(builder,
            vkd3d_component_type_from_data_type(reg->data_type),
            vkd3d_write_mask_component_count(write_mask));
}

static uint32_t vkd3d_dxbc_compiler_get_type_id_for_dst(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_dst_param *dst)
{
    return vkd3d_dxbc_compiler_get_type_id_for_reg(compiler, &dst->reg, dst->write_mask);
}

static bool vkd3d_dxbc_compiler_get_register_name(char *buffer, unsigned int buffer_size,
        const struct vkd3d_shader_register *reg)
    unsigned int idx;

    idx = reg->idx[1].offset != ~0u ? reg->idx[1].offset : reg->idx[0].offset;
        case VKD3DSPR_RESOURCE:
            snprintf(buffer, buffer_size, "t%u", reg->idx[0].offset);
            break;
        case VKD3DSPR_UAV:
            snprintf(buffer, buffer_size, "u%u", reg->idx[0].offset);
            break;
        case VKD3DSPR_SAMPLER:
            snprintf(buffer, buffer_size, "s%u", reg->idx[0].offset);
            break;
        case VKD3DSPR_CONSTBUFFER:
            snprintf(buffer, buffer_size, "cb%u_%u", reg->idx[0].offset, reg->idx[1].offset);
            break;
        case VKD3DSPR_INPUT:
            snprintf(buffer, buffer_size, "v%u", idx);
        case VKD3DSPR_INCONTROLPOINT:
            snprintf(buffer, buffer_size, "vicp%u", idx);
            break;
        case VKD3DSPR_OUTPUT:
        case VKD3DSPR_COLOROUT:
            snprintf(buffer, buffer_size, "o%u", idx);
        case VKD3DSPR_DEPTHOUT:
        case VKD3DSPR_DEPTHOUTGE:
        case VKD3DSPR_DEPTHOUTLE:
            snprintf(buffer, buffer_size, "oDepth");
            break;
        case VKD3DSPR_FORKINSTID:
            snprintf(buffer, buffer_size, "vForkInstanceId");
            break;
        case VKD3DSPR_JOININSTID:
            snprintf(buffer, buffer_size, "vJoinInstanceId");
            break;
        case VKD3DSPR_GSINSTID:
            snprintf(buffer, buffer_size, "vGSInstanceID");
            break;
        case VKD3DSPR_PATCHCONST:
            snprintf(buffer, buffer_size, "vpc%u", idx);
            break;
        case VKD3DSPR_TESSCOORD:
            snprintf(buffer, buffer_size, "vDomainLocation");
            break;
        case VKD3DSPR_THREADID:
            snprintf(buffer, buffer_size, "vThreadID");
        case VKD3DSPR_LOCALTHREADID:
            snprintf(buffer, buffer_size, "vThreadIDInGroup");
            break;
        case VKD3DSPR_LOCALTHREADINDEX:
            snprintf(buffer, buffer_size, "vThreadIDInGroupFlattened");
            break;
        case VKD3DSPR_THREADGROUPID:
            snprintf(buffer, buffer_size, "vThreadGroupID");
            break;
        case VKD3DSPR_GROUPSHAREDMEM:
            snprintf(buffer, buffer_size, "g%u", reg->idx[0].offset);
            break;
        case VKD3DSPR_IDXTEMP:
            snprintf(buffer, buffer_size, "x%u", idx);
            break;
        case VKD3DSPR_COVERAGE:
            snprintf(buffer, buffer_size, "vCoverage");
            break;
        case VKD3DSPR_SAMPLEMASK:
            snprintf(buffer, buffer_size, "oMask");
            break;
        case VKD3DSPR_OUTPOINTID:
        case VKD3DSPR_PRIMID:
            /* SPIRV-Tools disassembler generates names for SPIR-V built-ins. */
            return false;
        case VKD3DSPR_OUTSTENCILREF:
            snprintf(buffer, buffer_size, "oStencilRef");
            break;
        default:
            FIXME("Unhandled register %#x.\n", reg->type);
            snprintf(buffer, buffer_size, "unrecognized_%#x", reg->type);
/* TODO: UAV counters: vkd3d_spirv_build_op_name(builder, counter_var_id, "u%u_counter", reg->idx[0].offset); */
static void vkd3d_dxbc_compiler_emit_register_debug_name(struct vkd3d_spirv_builder *builder,
        uint32_t id, const struct vkd3d_shader_register *reg)
{
    char debug_name[256];
    if (vkd3d_dxbc_compiler_get_register_name(debug_name, ARRAY_SIZE(debug_name), reg))
        vkd3d_spirv_build_op_name(builder, id, "%s", debug_name);
}

static uint32_t vkd3d_dxbc_compiler_emit_variable(struct vkd3d_dxbc_compiler *compiler,
        struct vkd3d_spirv_stream *stream, SpvStorageClass storage_class,
        enum vkd3d_shader_component_type component_type, unsigned int component_count)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, ptr_type_id;

    type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, type_id);
    return vkd3d_spirv_build_op_variable(builder, stream, ptr_type_id, storage_class, 0);
static uint32_t vkd3d_dxbc_compiler_emit_array_variable(struct vkd3d_dxbc_compiler *compiler,
        struct vkd3d_spirv_stream *stream, SpvStorageClass storage_class,
        enum vkd3d_shader_component_type component_type, unsigned int component_count, unsigned int array_length)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t type_id, length_id, ptr_type_id;

    if (!array_length)
        return vkd3d_dxbc_compiler_emit_variable(compiler,
                stream, storage_class, component_type, component_count);

    type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count);
    length_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, array_length);
    type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id);
    ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, type_id);
    return vkd3d_spirv_build_op_variable(builder, stream, ptr_type_id, storage_class, 0);
}

static const struct vkd3d_shader_parameter *vkd3d_dxbc_compiler_get_shader_parameter(
        struct vkd3d_dxbc_compiler *compiler, enum vkd3d_shader_parameter_name name)
{
    const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info;
    for (i = 0; info && i < info->parameter_count; ++i)
        if (info->parameters[i].name == name)
            return &info->parameters[i];
static const struct vkd3d_spec_constant_info
{
    enum vkd3d_shader_parameter_name name;
    uint32_t default_value;
    const char *debug_name;
}
vkd3d_shader_parameters[] =
{
    {VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT, 1, "sample_count"},
};

static const struct vkd3d_spec_constant_info *get_spec_constant_info(enum vkd3d_shader_parameter_name name)
{
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(vkd3d_shader_parameters); ++i)
    {
        if (vkd3d_shader_parameters[i].name == name)
            return &vkd3d_shader_parameters[i];
    }