Skip to content
Snippets Groups Projects
spirv.c 380 KiB
Newer Older
        flags &= ~VKD3DSSF_GROUP_SHARED_MEMORY;
    }

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

    if (flags & VKD3DSSF_GLOBAL_UAV)
    {
        memory_scope = SpvScopeDevice;
        memory_semantics |= SpvMemorySemanticsImageMemoryMask;
        flags &= ~VKD3DSSF_GLOBAL_UAV;
    }

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

    spirv_compiler_emit_barrier(compiler, execution_scope, memory_scope, memory_semantics);
static void spirv_compiler_emit_emit_stream(struct spirv_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int stream_idx;

    if (instruction->handler_idx == VKD3DSIH_EMIT_STREAM)
        stream_idx = instruction->src[0].reg.idx[0].offset;
    else
        stream_idx = 0;

    if (stream_idx)
    {
        FIXME("Multiple streams are not supported yet.\n");
        return;
    }

    spirv_compiler_emit_shader_epilogue_invocation(compiler);
    vkd3d_spirv_build_op_emit_vertex(builder);
}

static void spirv_compiler_emit_cut_stream(struct spirv_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    unsigned int stream_idx;

    if (instruction->handler_idx == VKD3DSIH_CUT_STREAM)
        stream_idx = instruction->src[0].reg.idx[0].offset;
    else
        stream_idx = 0;

    if (stream_idx)
    {
        FIXME("Multiple streams are not supported yet.\n");
        return;
    }

    vkd3d_spirv_build_op_end_primitive(builder);
}

/* This function is called after declarations are processed. */
static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler)
    spirv_compiler_emit_push_constant_buffers(compiler);

    if (compiler->xfb_info && compiler->xfb_info->element_count
            && compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY)
static bool is_dcl_instruction(enum vkd3d_shader_opcode handler_idx)
    return (VKD3DSIH_DCL <= handler_idx && handler_idx <= VKD3DSIH_DCL_VERTICES_OUT)
            || handler_idx == VKD3DSIH_HS_DECLS;
static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler,
        const struct vkd3d_shader_instruction *instruction)
{
    if (!is_dcl_instruction(instruction->handler_idx) && !compiler->after_declarations_section)
    {
        compiler->after_declarations_section = true;
    switch (instruction->handler_idx)
    {
        case VKD3DSIH_DCL_GLOBAL_FLAGS:
            spirv_compiler_emit_dcl_global_flags(compiler, instruction);
        case VKD3DSIH_DCL_INDEXABLE_TEMP:
            spirv_compiler_emit_dcl_indexable_temp(compiler, instruction);
        case VKD3DSIH_DCL_CONSTANT_BUFFER:
            spirv_compiler_emit_dcl_constant_buffer(compiler, instruction);
        case VKD3DSIH_DCL_IMMEDIATE_CONSTANT_BUFFER:
            spirv_compiler_emit_dcl_immediate_constant_buffer(compiler, instruction);
        case VKD3DSIH_DCL_SAMPLER:
            spirv_compiler_emit_dcl_sampler(compiler, instruction);
            spirv_compiler_emit_dcl_resource(compiler, instruction);
        case VKD3DSIH_DCL_RESOURCE_RAW:
        case VKD3DSIH_DCL_UAV_RAW:
            spirv_compiler_emit_dcl_resource_raw(compiler, instruction);
        case VKD3DSIH_DCL_RESOURCE_STRUCTURED:
        case VKD3DSIH_DCL_UAV_STRUCTURED:
            spirv_compiler_emit_dcl_resource_structured(compiler, instruction);
        case VKD3DSIH_DCL_TGSM_RAW:
            spirv_compiler_emit_dcl_tgsm_raw(compiler, instruction);
        case VKD3DSIH_DCL_TGSM_STRUCTURED:
            spirv_compiler_emit_dcl_tgsm_structured(compiler, instruction);
        case VKD3DSIH_DCL_INPUT:
            spirv_compiler_emit_dcl_input(compiler, instruction);
            break;
        case VKD3DSIH_DCL_INPUT_PS:
            spirv_compiler_emit_dcl_input_ps(compiler, instruction);
        case VKD3DSIH_DCL_INPUT_PS_SGV:
        case VKD3DSIH_DCL_INPUT_PS_SIV:
            spirv_compiler_emit_dcl_input_ps_sysval(compiler, instruction);
        case VKD3DSIH_DCL_INPUT_SGV:
        case VKD3DSIH_DCL_INPUT_SIV:
            spirv_compiler_emit_dcl_input_sysval(compiler, instruction);
        case VKD3DSIH_DCL_OUTPUT:
            spirv_compiler_emit_dcl_output(compiler, instruction);
        case VKD3DSIH_DCL_OUTPUT_SIV:
            spirv_compiler_emit_dcl_output_siv(compiler, instruction);
            spirv_compiler_emit_dcl_stream(compiler, instruction);
        case VKD3DSIH_DCL_VERTICES_OUT:
            spirv_compiler_emit_output_vertex_count(compiler, instruction);
        case VKD3DSIH_DCL_INPUT_PRIMITIVE:
            spirv_compiler_emit_dcl_input_primitive(compiler, instruction);
        case VKD3DSIH_DCL_OUTPUT_TOPOLOGY:
            spirv_compiler_emit_dcl_output_topology(compiler, instruction);
        case VKD3DSIH_DCL_GS_INSTANCES:
            spirv_compiler_emit_dcl_gs_instances(compiler, instruction);
        case VKD3DSIH_DCL_INPUT_CONTROL_POINT_COUNT:
            compiler->input_control_point_count = instruction->declaration.count;
            break;
        case VKD3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT:
            compiler->output_control_point_count = instruction->declaration.count;
            spirv_compiler_emit_output_vertex_count(compiler, instruction);
        case VKD3DSIH_DCL_TESSELLATOR_DOMAIN:
            spirv_compiler_emit_dcl_tessellator_domain(compiler, instruction);
        case VKD3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE:
            spirv_compiler_emit_tessellator_output_primitive(compiler,
                    instruction->declaration.tessellator_output_primitive);
        case VKD3DSIH_DCL_TESSELLATOR_PARTITIONING:
            spirv_compiler_emit_tessellator_partitioning(compiler,
                    instruction->declaration.tessellator_partitioning);
        case VKD3DSIH_DCL_THREAD_GROUP:
            spirv_compiler_emit_dcl_thread_group(compiler, instruction);
        case VKD3DSIH_HS_CONTROL_POINT_PHASE:
        case VKD3DSIH_HS_FORK_PHASE:
        case VKD3DSIH_HS_JOIN_PHASE:
            spirv_compiler_enter_shader_phase(compiler, instruction);
        case VKD3DSIH_DMOV:
        case VKD3DSIH_MOV:
            spirv_compiler_emit_mov(compiler, instruction);
        case VKD3DSIH_DMOVC:
        case VKD3DSIH_MOVC:
            spirv_compiler_emit_movc(compiler, instruction);
        case VKD3DSIH_SWAPC:
            spirv_compiler_emit_swapc(compiler, instruction);
        case VKD3DSIH_ADD:
        case VKD3DSIH_AND:
        case VKD3DSIH_COUNTBITS:
        case VKD3DSIH_DADD:
        case VKD3DSIH_DDIV:
        case VKD3DSIH_DMUL:
        case VKD3DSIH_DTOF:
        case VKD3DSIH_DTOI:
        case VKD3DSIH_DTOU:
        case VKD3DSIH_FTOD:
        case VKD3DSIH_FTOI:
        case VKD3DSIH_FTOU:
        case VKD3DSIH_IADD:
        case VKD3DSIH_INEG:
        case VKD3DSIH_ISHL:
        case VKD3DSIH_ISHR:
        case VKD3DSIH_ITOD:
        case VKD3DSIH_NOT:
        case VKD3DSIH_OR:
        case VKD3DSIH_USHR:
        case VKD3DSIH_UTOD:
            spirv_compiler_emit_alu_instruction(compiler, instruction);
        case VKD3DSIH_DFMA:
        case VKD3DSIH_DMAX:
        case VKD3DSIH_DMIN:
        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_NE:
        case VKD3DSIH_ROUND_NI:
        case VKD3DSIH_ROUND_PI:
        case VKD3DSIH_ROUND_Z:
        case VKD3DSIH_SQRT:
        case VKD3DSIH_UMAX:
        case VKD3DSIH_UMIN:
            spirv_compiler_emit_ext_glsl_instruction(compiler, instruction);
        case VKD3DSIH_DP4:
        case VKD3DSIH_DP3:
        case VKD3DSIH_DP2:
            spirv_compiler_emit_dot(compiler, instruction);
        case VKD3DSIH_DRCP:
        case VKD3DSIH_RCP:
            spirv_compiler_emit_rcp(compiler, instruction);
        case VKD3DSIH_SINCOS:
            spirv_compiler_emit_sincos(compiler, instruction);
        case VKD3DSIH_IMUL:
            spirv_compiler_emit_imul(compiler, instruction);
        case VKD3DSIH_IMAD:
            spirv_compiler_emit_imad(compiler, instruction);
        case VKD3DSIH_UDIV:
            spirv_compiler_emit_udiv(compiler, instruction);
        case VKD3DSIH_DEQ:
        case VKD3DSIH_DGE:
        case VKD3DSIH_DLT:
        case VKD3DSIH_DNE:
        case VKD3DSIH_IEQ:
        case VKD3DSIH_IGE:
        case VKD3DSIH_ILT:
        case VKD3DSIH_INE:
        case VKD3DSIH_UGE:
        case VKD3DSIH_ULT:
            spirv_compiler_emit_comparison_instruction(compiler, instruction);
        case VKD3DSIH_BFI:
        case VKD3DSIH_IBFE:
        case VKD3DSIH_UBFE:
            spirv_compiler_emit_bitfield_instruction(compiler, instruction);
        case VKD3DSIH_F16TOF32:
            spirv_compiler_emit_f16tof32(compiler, instruction);
        case VKD3DSIH_F32TOF16:
            spirv_compiler_emit_f32tof16(compiler, instruction);
        case VKD3DSIH_BREAKP:
        case VKD3DSIH_CONTINUE:
        case VKD3DSIH_CONTINUEP:
        case VKD3DSIH_DISCARD:
        case VKD3DSIH_ELSE:
        case VKD3DSIH_ENDIF:
        case VKD3DSIH_ENDLOOP:
        case VKD3DSIH_IF:
        case VKD3DSIH_LOOP:
        case VKD3DSIH_RET:
        case VKD3DSIH_RETP:
        case VKD3DSIH_TEXKILL:
            ret = spirv_compiler_emit_control_flow_instruction(compiler, instruction);
        case VKD3DSIH_DSX:
        case VKD3DSIH_DSX_COARSE:
        case VKD3DSIH_DSX_FINE:
        case VKD3DSIH_DSY:
        case VKD3DSIH_DSY_COARSE:
        case VKD3DSIH_DSY_FINE:
            spirv_compiler_emit_deriv_instruction(compiler, instruction);
        case VKD3DSIH_LD2DMS:
            spirv_compiler_emit_ld(compiler, instruction);
            spirv_compiler_emit_lod(compiler, instruction);
        case VKD3DSIH_SAMPLE:
        case VKD3DSIH_SAMPLE_B:
        case VKD3DSIH_SAMPLE_GRAD:
        case VKD3DSIH_SAMPLE_LOD:
            spirv_compiler_emit_sample(compiler, instruction);
        case VKD3DSIH_SAMPLE_C:
        case VKD3DSIH_SAMPLE_C_LZ:
            spirv_compiler_emit_sample_c(compiler, instruction);
        case VKD3DSIH_GATHER4:
        case VKD3DSIH_GATHER4_C:
        case VKD3DSIH_GATHER4_PO:
        case VKD3DSIH_GATHER4_PO_C:
            spirv_compiler_emit_gather4(compiler, instruction);
        case VKD3DSIH_LD_RAW:
        case VKD3DSIH_LD_STRUCTURED:
            spirv_compiler_emit_ld_raw_structured(compiler, instruction);
        case VKD3DSIH_STORE_RAW:
        case VKD3DSIH_STORE_STRUCTURED:
            spirv_compiler_emit_store_raw_structured(compiler, instruction);
            break;
        case VKD3DSIH_LD_UAV_TYPED:
            spirv_compiler_emit_ld_uav_typed(compiler, instruction);
        case VKD3DSIH_STORE_UAV_TYPED:
            spirv_compiler_emit_store_uav_typed(compiler, instruction);
        case VKD3DSIH_IMM_ATOMIC_ALLOC:
        case VKD3DSIH_IMM_ATOMIC_CONSUME:
            spirv_compiler_emit_uav_counter_instruction(compiler, instruction);
        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:
            spirv_compiler_emit_atomic_instruction(compiler, instruction);
        case VKD3DSIH_BUFINFO:
            spirv_compiler_emit_bufinfo(compiler, instruction);
        case VKD3DSIH_RESINFO:
            spirv_compiler_emit_resinfo(compiler, instruction);
        case VKD3DSIH_SAMPLE_INFO:
            spirv_compiler_emit_sample_info(compiler, instruction);
        case VKD3DSIH_SAMPLE_POS:
            spirv_compiler_emit_sample_position(compiler, instruction);
        case VKD3DSIH_EVAL_CENTROID:
        case VKD3DSIH_EVAL_SAMPLE_INDEX:
            spirv_compiler_emit_eval_attrib(compiler, instruction);
        case VKD3DSIH_SYNC:
            spirv_compiler_emit_sync(compiler, instruction);
        case VKD3DSIH_EMIT:
        case VKD3DSIH_EMIT_STREAM:
            spirv_compiler_emit_emit_stream(compiler, instruction);
        case VKD3DSIH_CUT:
        case VKD3DSIH_CUT_STREAM:
            spirv_compiler_emit_cut_stream(compiler, instruction);
        case VKD3DSIH_DCL_HS_MAX_TESSFACTOR:
        case VKD3DSIH_NOP:
        default:
            FIXME("Unhandled instruction %#x.\n", instruction->handler_idx);
    }
static void spirv_compiler_emit_sm1_constant_buffer(struct spirv_compiler *compiler,
        const struct vkd3d_shader_desc *desc, enum vkd3d_shader_d3dbc_constant_register set,
        enum vkd3d_data_type data_type)
{
    struct vkd3d_shader_register_range range = {.space = 0, .first = set, .last = set};
    uint32_t count = desc->flat_constant_count[set].external;
    struct vkd3d_shader_register reg =
    {
        .type = VKD3DSPR_CONSTBUFFER,
        .idx[0].offset = set, /* register ID */
        .idx[1].offset = set, /* register index */
        .idx[2].offset = count, /* size */
        .idx_count = 3,
        .data_type = data_type,
    };

    if (count)
        spirv_compiler_emit_constant_buffer(compiler, count, &range, &reg);
}

static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler,
        const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_parser *parser,
        struct vkd3d_shader_code *spirv)
    const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info;
    const struct vkd3d_shader_spirv_domain_shader_target_info *ds_info;
    struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
    struct vkd3d_shader_desc *shader_desc = &parser->shader_desc;
    enum vkd3d_result result = VKD3D_OK;
    unsigned int i;

    if (parser->shader_desc.temp_count)
        spirv_compiler_emit_temps(compiler, parser->shader_desc.temp_count);

    spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc,
            VKD3D_SHADER_D3DBC_FLOAT_CONSTANT_REGISTER, VKD3D_DATA_FLOAT);
    spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc,
            VKD3D_SHADER_D3DBC_INT_CONSTANT_REGISTER, VKD3D_DATA_INT);
    spirv_compiler_emit_sm1_constant_buffer(compiler, &parser->shader_desc,
            VKD3D_SHADER_D3DBC_BOOL_CONSTANT_REGISTER, VKD3D_DATA_UINT);

    if ((result = vkd3d_shader_normalise(parser)) < 0)
        return result;

    instructions = parser->instructions;
    memset(&parser->instructions, 0, sizeof(parser->instructions));
    compiler->input_signature = shader_desc->input_signature;
    compiler->output_signature = shader_desc->output_signature;
    compiler->patch_constant_signature = shader_desc->patch_constant_signature;
    memset(&shader_desc->input_signature, 0, sizeof(shader_desc->input_signature));
    memset(&shader_desc->output_signature, 0, sizeof(shader_desc->output_signature));
    memset(&shader_desc->patch_constant_signature, 0, sizeof(shader_desc->patch_constant_signature));

    if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL)
        spirv_compiler_emit_shader_signature_outputs(compiler);
    for (i = 0; i < instructions.count && result >= 0; ++i)
        result = spirv_compiler_handle_instruction(compiler, &instructions.elements[i]);
    if (compiler->main_block_open)
        vkd3d_spirv_build_op_return(builder);

    if (!is_in_default_phase(compiler))
        spirv_compiler_leave_shader_phase(compiler);
    else
        vkd3d_spirv_build_op_function_end(builder);
    if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL)
        spirv_compiler_emit_hull_shader_main(compiler);
    if (compiler->shader_type == VKD3D_SHADER_TYPE_DOMAIN)
    {
        if (info && (ds_info = vkd3d_find_struct(compile_info->next, SPIRV_DOMAIN_SHADER_TARGET_INFO)))
            spirv_compiler_emit_tessellator_output_primitive(compiler, ds_info->output_primitive);
            spirv_compiler_emit_tessellator_partitioning(compiler, ds_info->partitioning);
        else if (spirv_compiler_is_opengl_target(compiler))
            ERR("vkd3d_shader_spirv_domain_shader_target_info is required for "
                    "OpenGL tessellation evaluation shader.\n");
        }
    }

    if (compiler->epilogue_function_id)
    {
        vkd3d_spirv_build_op_name(builder, compiler->epilogue_function_id, "epilogue");
        spirv_compiler_emit_shader_epilogue_function(compiler);
        vkd3d_spirv_stream_clear(&builder->debug_stream);

    if (!vkd3d_spirv_compile_module(builder, spirv, spirv_compiler_get_entry_point_name(compiler)))
        enum vkd3d_shader_spirv_environment environment = spirv_compiler_get_target_environment(compiler);
        vkd3d_spirv_dump(spirv, environment);
        vkd3d_spirv_validate(spirv, environment);
    if (compiler->failed)
        return VKD3D_ERROR_INVALID_SHADER;

    if (compile_info->target_type == VKD3D_SHADER_TARGET_SPIRV_TEXT)
    {
        struct vkd3d_shader_code text;
        enum vkd3d_shader_spirv_environment environment = spirv_compiler_get_target_environment(compiler);
        if (vkd3d_spirv_binary_to_text(spirv, environment, compiler->formatting, &text) != VKD3D_OK)
int spirv_compile(struct vkd3d_shader_parser *parser,
        const struct vkd3d_shader_scan_descriptor_info *scan_descriptor_info,
        const struct vkd3d_shader_compile_info *compile_info,
        struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context)
{
    struct spirv_compiler *spirv_compiler;
    int ret;

    if (!(spirv_compiler = spirv_compiler_create(&parser->shader_version, &parser->shader_desc,
            compile_info, scan_descriptor_info, message_context, &parser->location)))
    {
        ERR("Failed to create SPIR-V compiler.\n");
        return VKD3D_ERROR;
    }

    ret = spirv_compiler_generate_spirv(spirv_compiler, compile_info, parser, out);

    spirv_compiler_destroy(spirv_compiler);
    return ret;
}