From 93fef6b0f41e2d8f50fdffccfa40c36cf3a55651 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Mon, 21 Sep 2026 06:53:19 +0300 Subject: [PATCH] feat(shader): implement spirv program requirements analysis --- .../SpirvBackend/src/SpirvAnalysis.cpp | 117 +++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvAnalysis.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvAnalysis.cpp index f822c8b82..4647104f4 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvAnalysis.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvAnalysis.cpp @@ -4,7 +4,122 @@ namespace ShaderRecompiler { SpirvRequirements AnalyzeProgramRequirements(const IrProgram& program) { - throw std::runtime_error("AnalyzeProgramRequirements not implemented"); + SpirvRequirements requirements {}; + for (const IrBlock* block : program.BlockOrder()) { + for (const IrValue* inst : block->Instructions()) { + if (BufferAccessOf(inst->Opcode()) == BufferAccess::Atomic && inst->Type() == IrType::U64) { + requirements.bufferInt64Atomics = true; + } + const auto addressAccess = AddressOpcodeInfoOf(inst->Opcode()).access; + if (addressAccess != AddressAccess::None) { + const auto memoryIndex = inst->Flags().index; + if (memoryIndex >= program.Resources().memoryInfo.size()) { + throw std::runtime_error("address operation has invalid memory metadata"); + } + if (program.Resources().memoryInfo.at(memoryIndex).kind == ResourceKind::Scratch) { + if (program.Info().scratchDwords == 0u) { + throw std::runtime_error("scratch operation has no per-thread storage"); + } + requirements.functionScratch = true; + } else if (addressAccess == AddressAccess::Write) { + throw std::runtime_error("writable FLAT/GLOBAL addresses require GPU ownership tracking"); + } + } + if (BufferAccessOf(inst->Opcode()) != BufferAccess::None) { + const auto memoryIndex = inst->Flags().index; + if (memoryIndex >= program.Resources().memoryInfo.size()) { + throw std::runtime_error("buffer operation has invalid memory metadata"); + } + const auto& memory = program.Resources().memoryInfo.at(memoryIndex); + if (memory.kind == ResourceKind::Buffer) { + if (memory.resource >= program.Info().buffers.size()) { + throw std::runtime_error("buffer operation has invalid resource metadata"); + } + if ((program.Info().buffers.at(memory.resource).packedStride & (1u << 20u)) != 0u) { + if (program.Resources().stage != IrShaderStage::Compute) { + throw std::runtime_error("buffer ADD_TID is only valid for compute shaders"); + } + requirements.subgroupLocalInvocationId = true; + } + } + } + const auto sharedAccess = SharedAccessOf(inst->Opcode()); + if (sharedAccess != SharedAccess::None) { + const auto index = inst->Flags().index; + if (index >= program.Resources().memoryInfo.size()) { + throw std::runtime_error("shared operation has invalid memory metadata"); + } + const auto kind = program.Resources().memoryInfo.at(index).kind; + if (kind != ResourceKind::Lds && kind != ResourceKind::Gds) { + throw std::runtime_error("shared operation has invalid resource kind"); + } + if (program.Resources().stage != IrShaderStage::Compute && program.Resources().stage != IrShaderStage::Mesh && kind == ResourceKind::Lds) { + requirements.functionLds = true; + } + if (sharedAccess == SharedAccess::Append || sharedAccess == SharedAccess::Consume) { + requirements.subgroupBallot = true; + requirements.subgroupShuffle = true; + requirements.subgroupLocalInvocationId = true; + } + } + switch (inst->Opcode()) { + case IrOpcode::Ballot: + requirements.subgroupBallot = true; + break; + case IrOpcode::DppMoveU32: + case IrOpcode::ReadFirstLane: + case IrOpcode::ReadLane: { + requirements.subgroupBallot = true; + requirements.subgroupShuffle = true; + if (inst->Opcode() == IrOpcode::DppMoveU32) { + requirements.subgroupLocalInvocationId = true; + } + break; + } + case IrOpcode::DppUpdateU32: + case IrOpcode::WriteLane: { + requirements.subgroupBallot = true; + requirements.subgroupLocalInvocationId = true; + break; + } + case IrOpcode::Permlane16U32: { + requirements.subgroupBallot = true; + requirements.subgroupShuffle = true; + requirements.subgroupLocalInvocationId = true; + break; + } + case IrOpcode::SwizzleU32: + case IrOpcode::BpermuteU32: { + requirements.subgroupBallot = true; + requirements.subgroupShuffle = true; + requirements.subgroupLocalInvocationId = true; + break; + } + case IrOpcode::LaneId: + requirements.subgroupLocalInvocationId = requirements.subgroupLocalInvocationId || program.Resources().stage != IrShaderStage::TessellationControl; + break; + case IrOpcode::ImageQueryLod: + requirements.computeDerivatives = true; + break; + case IrOpcode::ImageGatherRaw: + requirements.imageGatherExtended = true; + break; + case IrOpcode::SetAttribute: { + const auto index = inst->Flags().index; + if (index >= program.Metadata().exportInfo.size()) { + throw std::runtime_error("attribute export has invalid metadata"); + } + if (program.Resources().stage == IrShaderStage::Pixel && program.Metadata().exportInfo.at(index).vm) { + requirements.pixelValidMask = true; + } + break; + } + default: + break; + } + } + } + return requirements; } }