Skip to content
Snippets Groups Projects
spirv.c 218 KiB
Newer Older
        return;
    }

    if (resource->reg.type == VKD3DSPR_GROUPSHAREDMEM)
        scope = SpvScopeWorkgroup;
        coordinate_mask = 1u;
        vkd3d_dxbc_compiler_get_register_info(compiler, &resource->reg, &reg_info);
        structure_stride = reg_info.structure_stride;
        raw = !structure_stride;
    }
    else
    {
        scope = SpvScopeDevice;
        vkd3d_dxbc_compiler_prepare_image(compiler, &image, &resource->reg, VKD3D_IMAGE_FLAG_NO_LOAD);
        coordinate_mask = (1u << image.resource_type_info->coordinate_component_count) - 1;
        structure_stride = image.structure_stride;
        raw = image.raw;
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_UINT, 1);
    if (structure_stride || raw)
    {
        assert(!raw != !structure_stride);
        coordinate_id = vkd3d_dxbc_compiler_emit_raw_structured_addressing(compiler,
                type_id, structure_stride, &src[0], VKD3DSP_WRITEMASK_0,
                &src[0], VKD3DSP_WRITEMASK_1);
    }
    else
    {
        assert(resource->reg.type != VKD3DSPR_GROUPSHAREDMEM);
        coordinate_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[0], coordinate_mask);
    }

    if (resource->reg.type == VKD3DSPR_GROUPSHAREDMEM)
    {
        ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, reg_info.storage_class, type_id);
        pointer_id = vkd3d_spirv_build_op_access_chain(builder, ptr_type_id,
                reg_info.id, &coordinate_id, 1);
    }
    else
    {
        type_id = vkd3d_spirv_get_type_id(builder, image.sampled_type, 1);
        ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassImage, type_id);
        sample_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, 0);
        pointer_id = vkd3d_spirv_build_op_image_texel_pointer(builder,
                ptr_type_id, image.id, coordinate_id, sample_id);
    }

    val_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[1], VKD3DSP_WRITEMASK_0);

    operands[i++] = pointer_id;
    operands[i++] = vkd3d_dxbc_compiler_get_constant_uint(compiler, scope);
    operands[i++] = vkd3d_dxbc_compiler_get_constant_uint(compiler, SpvMemorySemanticsMaskNone);
    if (instruction->src_count >= 3)
    {
        operands[i++] = vkd3d_dxbc_compiler_get_constant_uint(compiler, SpvMemorySemanticsMaskNone);
        operands[i++] = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[2], VKD3DSP_WRITEMASK_0);
    }
    result_id = vkd3d_spirv_build_op_trv(builder, &builder->function_stream,
            op, type_id, operands, i);

    if (is_imm_atomic_instruction(instruction->handler_idx))
        vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, result_id);
static void vkd3d_dxbc_compiler_emit_resinfo(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, lod_id, val_id, miplevel_count_id;
    uint32_t constituents[VKD3D_VEC4_SIZE];
    unsigned int i, size_component_count;
    struct vkd3d_shader_image image;

    vkd3d_spirv_enable_capability(builder, SpvCapabilityImageQuery);

    vkd3d_dxbc_compiler_prepare_image(compiler, &image, &src[1].reg, VKD3D_IMAGE_FLAG_NONE);
    size_component_count = image.resource_type_info->coordinate_component_count;
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_UINT, size_component_count);
    if (src[1].reg.type == VKD3DSPR_RESOURCE)
    {
        lod_id = vkd3d_dxbc_compiler_emit_load_src(compiler, &src[0], VKD3DSP_WRITEMASK_0);
        val_id = vkd3d_spirv_build_op_image_query_size_lod(builder, type_id, image.image_id, lod_id);
        type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_UINT, 1);
        miplevel_count_id = vkd3d_spirv_build_op_image_query_levels(builder, type_id, image.image_id);
    }
    else if (src[1].reg.type == VKD3DSPR_UAV)
    {
        val_id = vkd3d_spirv_build_op_image_query_size(builder, type_id, image.image_id);
        /* For UAVs the returned miplevel count is always 1. */
        miplevel_count_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, 1);
    }
    else
        ERR("Unexpected register type %#x.\n", src[1].reg.type);
        return;

    constituents[0] = val_id;
    for (i = 0; i < 3 - size_component_count; ++i)
        constituents[i + 1] = vkd3d_dxbc_compiler_get_constant_uint(compiler, 0);
    constituents[i + 1] = miplevel_count_id;
    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_UINT, VKD3D_VEC4_SIZE);
    val_id = vkd3d_spirv_build_op_composite_construct(builder,
            type_id, constituents, i + 2);

    type_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_FLOAT, VKD3D_VEC4_SIZE);
    if (instruction->flags == VKD3DSI_RESINFO_UINT)
    {
        val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
    }
    else
    {
        if (instruction->flags)
            FIXME("Unhandled flags %#x.\n", instruction->flags);
        val_id = vkd3d_spirv_build_op_convert_utof(builder, type_id, val_id);
    val_id = vkd3d_dxbc_compiler_emit_swizzle(compiler,
            val_id, VKD3D_TYPE_FLOAT, src[1].swizzle, dst->write_mask);

    vkd3d_dxbc_compiler_emit_store_dst(compiler, dst, val_id);
}

/* From the Vulkan spec:
 *
 *   "Scope for execution must be limited to: * Workgroup * Subgroup"
 *
 *   "Scope for memory must be limited to: * Device * Workgroup * Invocation"
 */
static void vkd3d_dxbc_compiler_emit_sync(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    uint32_t execution_id, memory_id, semantics_id;
    unsigned int flags = instruction->flags;
    SpvScope execution_scope = SpvScopeMax;
    SpvScope memory_scope = SpvScopeDevice;
    unsigned int memory_semantics = 0;

    if (flags & VKD3DSSF_GROUP_SHARED_MEMORY)
    {
        memory_scope = SpvScopeWorkgroup;
        memory_semantics |= SpvMemorySemanticsWorkgroupMemoryMask;
        flags &= ~VKD3DSSF_GROUP_SHARED_MEMORY;
    }

    if (flags & VKD3DSSF_THREAD_GROUP)
    {
        execution_scope = SpvScopeWorkgroup;
        flags &= ~VKD3DSSF_THREAD_GROUP;
    }

    if (flags)
    {
        FIXME("Unhandled sync flags %#x.\n", flags);
        memory_scope = SpvScopeDevice;
        execution_scope = SpvScopeWorkgroup;
        memory_semantics |= SpvMemorySemanticsSequentiallyConsistentMask
                | SpvMemorySemanticsUniformMemoryMask
                | SpvMemorySemanticsSubgroupMemoryMask
                | SpvMemorySemanticsWorkgroupMemoryMask
                | SpvMemorySemanticsCrossWorkgroupMemoryMask
                | SpvMemorySemanticsAtomicCounterMemoryMask
                | SpvMemorySemanticsImageMemoryMask;
    }

    memory_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, memory_scope);
    semantics_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, memory_semantics);
    if (execution_scope != SpvScopeMax)
    {
        execution_id = vkd3d_dxbc_compiler_get_constant_uint(compiler, execution_scope);
        vkd3d_spirv_build_op_control_barrier(builder,
                execution_id, memory_id, semantics_id);
    }
    else
    {
        vkd3d_spirv_build_op_memory_barrier(builder, memory_id, semantics_id);
    }
}

/* This function is called after declarations are processed. */
static void vkd3d_dxbc_compiler_emit_main_prolog(struct vkd3d_dxbc_compiler *compiler)
{
    vkd3d_dxbc_compiler_emit_push_constant_buffers(compiler);
}

static bool is_dcl_instruction(enum VKD3D_SHADER_INSTRUCTION_HANDLER handler_idx)
{
    return VKD3DSIH_DCL <= handler_idx && handler_idx <= VKD3DSIH_DCL_VERTICES_OUT;
}

void vkd3d_dxbc_compiler_handle_instruction(struct vkd3d_dxbc_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    if (!is_dcl_instruction(instruction->handler_idx) && !compiler->after_declarations_section)
    {
        compiler->after_declarations_section = true;
        vkd3d_dxbc_compiler_emit_main_prolog(compiler);
    }

    switch (instruction->handler_idx)
    {
        case VKD3DSIH_DCL_GLOBAL_FLAGS:
            vkd3d_dxbc_compiler_emit_dcl_global_flags(compiler, instruction);
            break;
        case VKD3DSIH_DCL_TEMPS:
            vkd3d_dxbc_compiler_emit_dcl_temps(compiler, instruction);
            break;
        case VKD3DSIH_DCL_CONSTANT_BUFFER:
            vkd3d_dxbc_compiler_emit_dcl_constant_buffer(compiler, instruction);
            break;
        case VKD3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER:
            vkd3d_dxbc_compiler_emit_dcl_immediate_constant_buffer(compiler, instruction);
            break;
        case VKD3DSIH_DCL_SAMPLER:
            vkd3d_dxbc_compiler_emit_dcl_sampler(compiler, instruction);
            break;
        case VKD3DSIH_DCL:
            vkd3d_dxbc_compiler_emit_dcl_resource(compiler, instruction);
            break;
        case VKD3DSIH_DCL_RESOURCE_RAW:
            vkd3d_dxbc_compiler_emit_dcl_resource_raw(compiler, instruction);
            break;
        case VKD3DSIH_DCL_UAV_RAW:
            vkd3d_dxbc_compiler_emit_dcl_uav_raw(compiler, instruction);
            break;
        case VKD3DSIH_DCL_RESOURCE_STRUCTURED:
            vkd3d_dxbc_compiler_emit_dcl_resource_structured(compiler, instruction);
            break;
        case VKD3DSIH_DCL_UAV_STRUCTURED:
            vkd3d_dxbc_compiler_emit_dcl_uav_structured(compiler, instruction);
            break;
        case VKD3DSIH_DCL_UAV_TYPED:
            vkd3d_dxbc_compiler_emit_dcl_uav_typed(compiler, instruction);
            break;
        case VKD3DSIH_DCL_TGSM_RAW:
            vkd3d_dxbc_compiler_emit_dcl_tgsm_raw(compiler, instruction);
            break;
        case VKD3DSIH_DCL_TGSM_STRUCTURED:
            vkd3d_dxbc_compiler_emit_dcl_tgsm_structured(compiler, instruction);
            break;
        case VKD3DSIH_DCL_INPUT:
            vkd3d_dxbc_compiler_emit_dcl_input(compiler, instruction);
            break;
        case VKD3DSIH_DCL_INPUT_PS:
            vkd3d_dxbc_compiler_emit_dcl_input_ps(compiler, instruction);
            break;
        case VKD3DSIH_DCL_INPUT_PS_SIV:
            vkd3d_dxbc_compiler_emit_dcl_input_ps_siv(compiler, instruction);
            break;
        case VKD3DSIH_DCL_INPUT_SGV:
            vkd3d_dxbc_compiler_emit_dcl_input_sgv(compiler, instruction);
            break;
        case VKD3DSIH_DCL_OUTPUT:
            vkd3d_dxbc_compiler_emit_dcl_output(compiler, instruction);
            break;
        case VKD3DSIH_DCL_OUTPUT_SIV:
            vkd3d_dxbc_compiler_emit_dcl_output_siv(compiler, instruction);
            break;
        case VKD3DSIH_DCL_THREAD_GROUP:
            vkd3d_dxbc_compiler_emit_dcl_thread_group(compiler, instruction);
            break;
        case VKD3DSIH_MOV:
            vkd3d_dxbc_compiler_emit_mov(compiler, instruction);
            break;
        case VKD3DSIH_MOVC:
            vkd3d_dxbc_compiler_emit_movc(compiler, instruction);
            break;
        case VKD3DSIH_SWAPC:
            vkd3d_dxbc_compiler_emit_swapc(compiler, instruction);
            break;
        case VKD3DSIH_ADD:
        case VKD3DSIH_AND:
        case VKD3DSIH_COUNTBITS:
        case VKD3DSIH_FTOI:
        case VKD3DSIH_FTOU:
        case VKD3DSIH_IADD:
        case VKD3DSIH_ISHL:
        case VKD3DSIH_ISHR:
        case VKD3DSIH_NOT:
        case VKD3DSIH_OR:
        case VKD3DSIH_USHR:
            vkd3d_dxbc_compiler_emit_alu_instruction(compiler, instruction);
            break;
        case VKD3DSIH_EXP:
        case VKD3DSIH_FIRSTBIT_HI:
        case VKD3DSIH_FIRSTBIT_LO:
        case VKD3DSIH_FIRSTBIT_SHI:
        case VKD3DSIH_FRC:
        case VKD3DSIH_IMAX:
        case VKD3DSIH_IMIN:
        case VKD3DSIH_LOG:
        case VKD3DSIH_MAX:
        case VKD3DSIH_MIN:
        case VKD3DSIH_ROUND_NI:
        case VKD3DSIH_ROUND_PI:
        case VKD3DSIH_ROUND_Z:
        case VKD3DSIH_SQRT:
        case VKD3DSIH_UMAX:
        case VKD3DSIH_UMIN:
            vkd3d_dxbc_compiler_emit_ext_glsl_instruction(compiler, instruction);
            break;
        case VKD3DSIH_DP4:
        case VKD3DSIH_DP3:
        case VKD3DSIH_DP2:
            vkd3d_dxbc_compiler_emit_dot(compiler, instruction);
            break;
        case VKD3DSIH_RCP:
            vkd3d_dxbc_compiler_emit_rcp(compiler, instruction);
            break;
        case VKD3DSIH_IMUL:
            vkd3d_dxbc_compiler_emit_imul(compiler, instruction);
            break;
        case VKD3DSIH_IMAD:
            vkd3d_dxbc_compiler_emit_imad(compiler, instruction);
            break;
        case VKD3DSIH_UDIV:
            vkd3d_dxbc_compiler_emit_udiv(compiler, instruction);
            break;
        case VKD3DSIH_IEQ:
        case VKD3DSIH_IGE:
        case VKD3DSIH_ILT:
        case VKD3DSIH_INE:
        case VKD3DSIH_UGE:
        case VKD3DSIH_ULT:
            vkd3d_dxbc_compiler_emit_comparison_instruction(compiler, instruction);
            break;
        case VKD3DSIH_BFI:
        case VKD3DSIH_IBFE:
        case VKD3DSIH_UBFE:
            vkd3d_dxbc_compiler_emit_bitfield_instruction(compiler, instruction);
            break;
        case VKD3DSIH_F16TOF32:
            vkd3d_dxbc_compiler_emit_f16tof32(compiler, instruction);
            break;
        case VKD3DSIH_F32TOF16:
            vkd3d_dxbc_compiler_emit_f32tof16(compiler, instruction);
            break;
        case VKD3DSIH_BREAKP:
        case VKD3DSIH_CASE:
        case VKD3DSIH_DEFAULT:
        case VKD3DSIH_ELSE:
        case VKD3DSIH_ENDIF:
        case VKD3DSIH_ENDLOOP:
        case VKD3DSIH_IF:
        case VKD3DSIH_LOOP:
        case VKD3DSIH_RET:
        case VKD3DSIH_TEXKILL:
            vkd3d_dxbc_compiler_emit_control_flow_instruction(compiler, instruction);
        case VKD3DSIH_LD:
            vkd3d_dxbc_compiler_emit_ld(compiler, instruction);
            break;
        case VKD3DSIH_SAMPLE:
        case VKD3DSIH_SAMPLE_LOD:
            vkd3d_dxbc_compiler_emit_sample(compiler, instruction);
            break;
        case VKD3DSIH_SAMPLE_C:
        case VKD3DSIH_SAMPLE_C_LZ:
            vkd3d_dxbc_compiler_emit_sample_c(compiler, instruction);
            break;
        case VKD3DSIH_GATHER4:
            vkd3d_dxbc_compiler_emit_gather4(compiler, instruction);
            break;
        case VKD3DSIH_LD_RAW:
        case VKD3DSIH_LD_STRUCTURED:
            vkd3d_dxbc_compiler_emit_ld_raw_structured(compiler, instruction);
        case VKD3DSIH_STORE_RAW:
        case VKD3DSIH_STORE_STRUCTURED:
            vkd3d_dxbc_compiler_emit_store_raw_structured(compiler, instruction);
            break;
        case VKD3DSIH_LD_UAV_TYPED:
            vkd3d_dxbc_compiler_emit_ld_uav_typed(compiler, instruction);
        case VKD3DSIH_STORE_UAV_TYPED:
            vkd3d_dxbc_compiler_emit_store_uav_typed(compiler, instruction);
            break;
        case VKD3DSIH_IMM_ATOMIC_ALLOC:
        case VKD3DSIH_IMM_ATOMIC_CONSUME:
            vkd3d_dxbc_compiler_emit_uav_counter_instruction(compiler, instruction);
            break;
        case VKD3DSIH_ATOMIC_IADD:
        case VKD3DSIH_ATOMIC_IMAX:
        case VKD3DSIH_ATOMIC_IMIN:
        case VKD3DSIH_ATOMIC_OR:
        case VKD3DSIH_ATOMIC_UMAX:
        case VKD3DSIH_ATOMIC_UMIN:
        case VKD3DSIH_ATOMIC_XOR:
        case VKD3DSIH_IMM_ATOMIC_AND:
        case VKD3DSIH_IMM_ATOMIC_CMP_EXCH:
        case VKD3DSIH_IMM_ATOMIC_EXCH:
        case VKD3DSIH_IMM_ATOMIC_IADD:
        case VKD3DSIH_IMM_ATOMIC_IMAX:
        case VKD3DSIH_IMM_ATOMIC_IMIN:
        case VKD3DSIH_IMM_ATOMIC_OR:
        case VKD3DSIH_IMM_ATOMIC_UMAX:
        case VKD3DSIH_IMM_ATOMIC_UMIN:
        case VKD3DSIH_IMM_ATOMIC_XOR:
            vkd3d_dxbc_compiler_emit_atomic_instruction(compiler, instruction);
            break;
        case VKD3DSIH_RESINFO:
            vkd3d_dxbc_compiler_emit_resinfo(compiler, instruction);
            break;
        case VKD3DSIH_SYNC:
            vkd3d_dxbc_compiler_emit_sync(compiler, instruction);
            break;
        default:
            FIXME("Unhandled instruction %#x.\n", instruction->handler_idx);
    }
static void vkd3d_dxbc_compiler_emit_output_setup_function(struct vkd3d_dxbc_compiler *compiler)
{
    uint32_t void_id, type_id, ptr_type_id, function_type_id, function_id, val_id;
    const struct vkd3d_shader_signature *signature = compiler->output_signature;
    uint32_t param_type_id[MAX_REG_OUTPUT], param_id[MAX_REG_OUTPUT] = {};
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    DWORD write_mask, variable_idx;
    unsigned int i, count;

    function_id = compiler->output_setup_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_build_op_type_function(builder, void_id, param_type_id, count);

    vkd3d_spirv_build_op_function(builder, void_id, function_id,
            SpvFunctionControlMaskNone, function_type_id);
    vkd3d_spirv_build_op_name(builder, function_id, "setup_output");

    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);
    }

    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])
        write_mask = signature->elements[i].mask & 0xff;
        val_id = vkd3d_dxbc_compiler_emit_swizzle(compiler,
                param_id[variable_idx], VKD3D_TYPE_FLOAT, VKD3DSP_NOSWIZZLE, write_mask);

        if (compiler->output_info[i].component_type != VKD3D_TYPE_FLOAT)
        {
            type_id = vkd3d_spirv_get_type_id(builder, compiler->output_info[i].component_type,
                    vkd3d_write_mask_component_count(write_mask));
            val_id = vkd3d_spirv_build_op_bitcast(builder, type_id, val_id);
        }

        vkd3d_spirv_build_op_store(builder, compiler->output_info[i].id, val_id, SpvMemoryAccessMaskNone);
    }

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

bool vkd3d_dxbc_compiler_generate_spirv(struct vkd3d_dxbc_compiler *compiler,
        struct vkd3d_shader_code *spirv)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;

    if (compiler->options & VKD3D_SHADER_STRIP_DEBUG)
        vkd3d_spirv_stream_clear(&builder->debug_stream);

    vkd3d_spirv_build_op_function_end(builder);

    if (compiler->output_setup_function_id)
        vkd3d_dxbc_compiler_emit_output_setup_function(compiler);

    if (!vkd3d_spirv_compile_module(builder, spirv))
        return false;

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

    return true;
}

void vkd3d_dxbc_compiler_destroy(struct vkd3d_dxbc_compiler *compiler)
{
    vkd3d_free(compiler->control_flow_info);

    vkd3d_spirv_builder_free(&compiler->spirv_builder);

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