#include "Recompiler.hpp" #include "ControlFlow/include/ControlFlow/GraphBuilder.hpp" #include "ControlFlow/include/ControlFlow/Structurizer.hpp" #include "RdnaDecoder/include/RdnaDecoder/RdnaInstructionDecoder.hpp" #include "IntermediateRepresentation/include/IntermediateRepresentation/IrProgram.hpp" #include "Optimization/include/Optimization/BindingAllocator.hpp" #include "Optimization/include/Optimization/ConstantFolder.hpp" #include "Optimization/include/Optimization/DeadCodeEliminator.hpp" #include "Optimization/include/Optimization/DescriptorBindingBuilder.hpp" #include "Optimization/include/Optimization/ReadLaneEliminator.hpp" #include "Optimization/include/Optimization/RequestMemoryView.hpp" #include "Optimization/include/Optimization/ResourceMaterializer.hpp" #include "Optimization/ResourceProgram.hpp" #include "Optimization/include/Optimization/ResourceTracker.hpp" #include "Optimization/include/Optimization/ShaderInfoCollector.hpp" #include "Optimization/include/Optimization/SrtWalker.hpp" #include "Optimization/include/Optimization/SsaBuilder.hpp" #include "SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp" #include "SpirvBackend/SpirvOptimizer.hpp" #include "Translation/include/Translation/InstructionTranslator.hpp" #include "Translation/include/Translation/ShaderInputInfoBuilder.hpp" #include #include #include #include #include namespace ShaderRecompiler { namespace { ShaderStageKind toShaderStageKind(ShaderStage stage) { switch (stage) { case ShaderStage::Compute: return ShaderStageKind::Compute; case ShaderStage::Vertex: return ShaderStageKind::Vertex; case ShaderStage::TessellationControl: return ShaderStageKind::TessellationControl; case ShaderStage::TessellationEvaluation: return ShaderStageKind::TessellationEvaluation; case ShaderStage::Fragment: return ShaderStageKind::Pixel; case ShaderStage::Local: return ShaderStageKind::Local; case ShaderStage::Mesh: return ShaderStageKind::Mesh; case ShaderStage::Geometry: break; } throw std::runtime_error("ShaderRecompiler::Recompile: unsupported shader stage"); } } IrProgram PrepareResourceProgram(const RecompileRequest& request) { const auto stageKind = toShaderStageKind(request.shader.stage); const auto inputInfo = BuildShaderStageInputInfo(stageKind, request.context); constexpr RdnaInstructionDecoder decoder; const auto decoded = decoder.Decode(request.shader.code); constexpr GraphBuilder graphBuilder; auto cfg = graphBuilder.Build(decoded); constexpr Structurizer structurizer; structurizer.Structurize(cfg); TranslateOptions translateOptions {}; translateOptions.stage = stageKind; translateOptions.waveSize = request.context.waveSize; translateOptions.userDataBaseRegister = request.context.userDataBaseRegister; translateOptions.userDataCount = static_cast(request.context.userData.size()); translateOptions.embeddedFetch = nullptr; translateOptions.fragmentShaderBarycentricEnabled = request.target.fragmentShaderBarycentricEnabled; translateOptions.inputInfo = inputInfo; constexpr InstructionTranslator translator; EmbeddedFetchPlan embeddedFetch; if ((stageKind == ShaderStageKind::Vertex || stageKind == ShaderStageKind::Local) && inputInfo.vertex != nullptr && inputInfo.vertex->fetchEmbedded) { constexpr EmbeddedVertexFetchAnalyzer embeddedFetchAnalyzer; embeddedFetch = embeddedFetchAnalyzer.Analyze(decoded, inputInfo.vertex->fetchAttribReg, inputInfo.vertex->fetchBufferReg, request.context.userDataBaseRegister, static_cast(request.context.userData.size()), request.context.waveSize); } translateOptions.embeddedFetch = embeddedFetch.loads.empty() ? nullptr : &embeddedFetch; auto program = translator.Translate(decoded, cfg, translateOptions); constexpr SsaBuilder ssaBuilder; ssaBuilder.Rewrite(program); constexpr ConstantFolder constantFolder; constexpr DeadCodeEliminator deadCodeEliminator; constantFolder.Fold(program); ResolveControlFlowIdentities(program); deadCodeEliminator.RemoveIdentities(program); deadCodeEliminator.Eliminate(program); constexpr ReadLaneEliminator readLaneEliminator; const auto readLaneStats = readLaneEliminator.Eliminate(program, translateOptions.waveSize); if (readLaneStats.rewrittenReads != 0u) { constantFolder.Fold(program); ResolveControlFlowIdentities(program); deadCodeEliminator.RemoveIdentities(program); deadCodeEliminator.Eliminate(program); } constexpr SrtWalker srtWalker; srtWalker.BuildPlan(program); deadCodeEliminator.Eliminate(program); constexpr ResourceTracker resourceTracker; resourceTracker.Track(program); deadCodeEliminator.Eliminate(program); return program; } namespace { RecompileResult RecompileImpl(const RecompileRequest& request) { auto program = PrepareResourceProgram(request); const auto inputInfo = BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context); constexpr DeadCodeEliminator deadCodeEliminator; constexpr ResourceMaterializer resourceMaterializer; const auto resourcePlan = resourceMaterializer.ExtractPlan(program); RequestMemoryView requestMemoryView(request.context.memory); const auto srtRuntime = requestMemoryView.MakeRuntime(request.context.userData, request.shader.codeAddress); ResourceSnapshot resourceSnapshot; ResourceSpecialization resourceSpecialization; resourceMaterializer.Materialize(resourcePlan, srtRuntime, resourceSnapshot, resourceSpecialization); resourceMaterializer.Apply(program, resourceSpecialization); deadCodeEliminator.RemoveIdentities(program); deadCodeEliminator.Eliminate(program); constexpr ShaderInfoCollector shaderInfoCollector; shaderInfoCollector.Collect(program, inputInfo); constexpr BindingAllocator bindingAllocator; auto bindings = bindingAllocator.Allocate(program, request.layout); constexpr DescriptorBindingBuilder descriptorBindingBuilder; descriptorBindingBuilder.Populate(bindings, program, resourceSnapshot); SpirvTargetOptions targetOptions {}; targetOptions.vulkanVersion = request.target.vulkanVersion; targetOptions.spirvVersion = request.target.spirvVersion; targetOptions.subgroupSize = request.target.subgroupSize; targetOptions.bdaAbiVersion = request.target.bdaAbiVersion; targetOptions.supportedCapabilities = request.target.supportedCapabilities; targetOptions.supportedExtensions = request.target.supportedExtensions; constexpr SpirvEmitter spirvEmitter; const auto spirv = spirvEmitter.Emit(program, inputInfo, bindings, targetOptions); RecompileResult result; result.spirv = ValidateAndOptimizeSpirv(spirv, request.target.vulkanVersion, request.target.spirvVersion); result.bdaAbiVersion = program.Info().usesDma ? request.target.bdaAbiVersion : 0u; result.bindings = bindings.bindings; result.pushConstants = bindings.pushConstants; result.vertexOffsetSgpr = program.Info().vertexOffsetSgpr; result.instanceOffsetSgpr = program.Info().instanceOffsetSgpr; if (request.shader.stage == ShaderStage::Vertex || request.shader.stage == ShaderStage::Local) { if (inputInfo.vertex == nullptr) throw std::runtime_error("vertex input metadata is missing"); for (const auto& input : program.Info().inputs) { if (input.kind != StageInputKind::Parameter) continue; if (input.location >= static_cast(inputInfo.vertex->resourcesNum)) throw std::runtime_error("vertex attribute location exceeds resource count"); result.vertexAttributes.push_back({input.location, input.componentCount, {inputInfo.vertex->resources[input.location].fields}, inputInfo.vertex->resourcesDst[input.location].fetchIndex}); } } std::fprintf(stdout, "[RecompileImpl] shader recompiled\n"); std::fflush(stdout); return result; } } RecompileResult Recompile(const RecompileRequest& request) { try { return RecompileImpl(request); } catch (const std::exception& e) { constexpr auto requestSerializer = RequestSerializer{}; const auto inputInfo = "\nRecompileRequest:\n" + requestSerializer.Serialize(request); throw std::runtime_error(std::string("ShaderRecompiler::Recompile: ") + e.what() + inputInfo); } catch (...) { throw std::runtime_error("ShaderRecompiler::Recompile: unknown exception"); } } }