diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 2ef87ad8d..7aa6e9a89 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -817,8 +817,12 @@ if(BUILD_TESTING) endif() add_test(NAME agc_shader_bindless COMMAND agc_shader_memory_tests --bindless) set_tests_properties(agc_shader_bindless PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") + add_test(NAME agc_shader_frontend_pair COMMAND agc_shader_memory_tests --frontend-pair) + add_test(NAME agc_shader_frontend_pair_off COMMAND agc_shader_memory_tests --frontend-pair) + set_tests_properties(agc_shader_frontend_pair PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") + set_tests_properties(agc_shader_frontend_pair_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_FRONTEND_PAIR=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") if(WIN32) - set_tests_properties(agc_shader_bindless PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + set_tests_properties(agc_shader_bindless agc_shader_frontend_pair agc_shader_frontend_pair_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() foreach(agcTest IN ITEMS agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests ${agcExecutionTests}) if(agcTest STREQUAL "agc_driver_constant_store_tests") diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index 88b1e51b6..be2666a59 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -406,9 +406,10 @@ std::vector PrepareRegistered(const ShaderSnapshot& snap ShaderRecompiler::RecompileRequest request{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, userData, compute, pixel, vertex, memory, RegisteredFloatMode(snapshot)}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics}; if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes; std::vector entries; + ShaderRecompiler::ShaderPreparationContext preparation; const auto append = [&] { PerformanceTimer timing("Shader.PrepareArtifact"); - entries.push_back({codeOffset, ShaderRecompiler::PrepareShader(request)}); + entries.push_back({codeOffset, ShaderRecompiler::PrepareShader(request, &preparation)}); }; append(); if (compute) { diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index 99f418ed7..8fef7ec53 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,258 @@ void verifyResult(const ShaderRecompiler::RecompileResult& first, const ShaderRe } } +ShaderRecompiler::RecompileRequest FrontendRequest(std::span code, std::span userData) { + using namespace ShaderRecompiler; + RecompileRequest request{}; + request.shader = {ShaderStage::Compute, 0x100000u, code, 0, {}}; + request.context.waveSize = 64; + request.context.userDataBaseRegister = 0; + request.context.userData = userData; + request.context.compute = ShaderComputeStageInfo{{64u, 1u, 1u}, 0u, {false, false, false}, false, 1u}; + request.target = BufferTarget(); + request.target.vulkanVersion = 0x00401000u; + request.target.spirvVersion = 0x00010300u; + request.target.subgroupSize = 32u; + request.target.fragmentShaderBarycentricEnabled = false; + request.layout.pushConstantSizeBytes = 128; + request.useCache = false; + return request; +} + +void verifyFrontendIdentity() { + using namespace ShaderRecompiler; + const bool reuse = std::getenv("APS5_NO_PERF_FRONTEND_PAIR") == nullptr; + const std::vector code{0xbf800000u, 0xbf810000u}; + const std::array userData{}; + const auto request = FrontendRequest(code, userData); + ShaderPreparationContext preparation; + const auto first = preparation.AcquireFrontend(request); + require(first != nullptr, "frontend pair: no frontend was prepared"); + require((preparation.AcquireFrontend(request) == first) == reuse, "frontend pair: repeated acquisition ignored the reuse switch"); + + const auto relocatedCode = code; + auto relocated = request; + relocated.shader.code = relocatedCode; + relocated.shader.codeAddress += 0x1000u; + require((preparation.AcquireFrontend(relocated) == first) == reuse, "frontend pair: identical words at another allocation or address changed reuse"); + auto partial = request; + partial.context.compute->partialThreads = {33u, 1u, 1u}; + require((preparation.AcquireFrontend(partial) == first) == reuse, "frontend pair: partial-thread state changed the frontend identity"); + auto target = request; + target.target.subgroupSize = 64u; + target.context.waveSize = 32u; + target.context.compute->numThreads = {32u, 2u, 1u}; + require((preparation.AcquireFrontend(target) == first) == reuse, "frontend pair: translation-only state changed the frontend identity"); + const std::array changedUserData{1u, 2u}; + auto userValues = request; + userValues.context.userData = changedUserData; + require((preparation.AcquireFrontend(userValues) == first) == reuse, "frontend pair: user-data values changed the frontend identity"); + + const auto verifyMiss = [&](const RecompileRequest& changed, const char* message) { + ShaderPreparationContext isolated; + const auto original = isolated.AcquireFrontend(request); + const auto replacement = isolated.AcquireFrontend(changed); + require(replacement != original, message); + require((isolated.AcquireFrontend(changed) == replacement) == reuse, "frontend pair: a successful miss was not retained"); + require(isolated.AcquireFrontend(request) != original, "frontend pair: replacement retained more than the last frontend"); + }; + auto changed = request; + auto changedCode = code; + changedCode[0] = 0xbf800001u; + changed.shader.code = changedCode; + verifyMiss(changed, "frontend pair: changed code words reused the frontend"); + changedCode = code; + changedCode.push_back(0xbf800000u); + changed.shader.code = changedCode; + verifyMiss(changed, "frontend pair: changed code length reused the frontend"); + changed = request; + changed.context.userDataBaseRegister = 4u; + verifyMiss(changed, "frontend pair: changed user-data base reused the frontend"); + changed = request; + changed.context.userData = std::span(userData).first(1u); + verifyMiss(changed, "frontend pair: changed user-data count reused the frontend"); + changed = request; + changed.shader.stage = ShaderStage::Fragment; + changed.context.compute.reset(); + changed.context.pixel = ShaderPixelStageInfo{}; + verifyMiss(changed, "frontend pair: a different stage reused the frontend"); + changed = request; + changed.shader.stage = ShaderStage::Vertex; + changed.context.compute.reset(); + changed.context.vertex = ShaderVertexStageInfo{}; + verifyMiss(changed, "frontend pair: a fetch-capable stage reused the compute frontend"); + + auto invalid = request; + invalid.shader.code = {}; + bool failed = false; + try { + static_cast(preparation.AcquireFrontend(invalid)); + } catch (const std::out_of_range& error) { + failed = std::string_view(error.what()).find("before s_endpgm") != std::string_view::npos; + } + require(failed, "frontend pair: invalid code did not fail during frontend construction"); + require((preparation.AcquireFrontend(request) == first) == reuse, "frontend pair: a failed miss replaced the prior frontend"); +} + +void verifyFrontendProgram(const ShaderRecompiler::IrProgram& reused, const ShaderRecompiler::IrProgram& fresh) { + using namespace ShaderRecompiler; + require(ProgramToString(reused) == ProgramToString(fresh), "frontend pair: reused frontend changed the IR"); + require(reused.WaveSize() == fresh.WaveSize() && reused.Info() == fresh.Info(), "frontend pair: reused frontend changed shader information"); + const auto& left = reused.Resources(); + const auto& right = fresh.Resources(); + require(left.stage == right.stage && left.shaderHash == right.shaderHash && left.userDataBase == right.userDataBase && left.userDataCount == right.userDataCount && left.srgbDecodeFormats == right.srgbDecodeFormats, "frontend pair: resource identity changed"); + require(left.memoryInfo == right.memoryInfo && left.materializationSources == right.materializationSources && left.guardedSrtSlots == right.guardedSrtSlots && left.srtGuardOffset == right.srtGuardOffset && left.cleanFlatSlots == right.cleanFlatSlots && left.pureFlatSlots == right.pureFlatSlots, "frontend pair: resource planning changed"); + require(left.descriptorSources.size() == right.descriptorSources.size() && left.srtReads.size() == right.srtReads.size(), "frontend pair: resource counts changed"); + for (std::size_t index = 0; index < left.descriptorSources.size(); ++index) { + const auto& a = left.descriptorSources[index]; + const auto& b = right.descriptorSources[index]; + require(a.dwordCount == b.dwordCount && a.indirectImage == b.indirectImage, "frontend pair: descriptor planning changed"); + for (std::size_t word = 0; word < a.dwords.size(); ++word) { + require((a.dwords[word] == nullptr) == (b.dwords[word] == nullptr), "frontend pair: descriptor word presence changed"); + if (a.dwords[word] != nullptr) require(a.dwords[word] != b.dwords[word] && a.dwords[word]->Id() == b.dwords[word]->Id(), "frontend pair: descriptor values were shared or changed"); + } + } + require(reused.BlockOrder().size() == fresh.BlockOrder().size(), "frontend pair: block count changed"); + for (std::size_t block = 0; block < reused.BlockOrder().size(); ++block) { + const auto* a = reused.BlockOrder()[block]; + const auto* b = fresh.BlockOrder()[block]; + require(a != b && a->Instructions().size() == b->Instructions().size(), "frontend pair: mutable IR blocks were shared or changed"); + auto other = b->Instructions().begin(); + for (const auto* x : a->Instructions()) { + const auto* y = *other++; + require(x != y && x->Flags() == y->Flags(), "frontend pair: mutable IR values were shared or their flags changed"); + } + } +} + +void verifyFrontendPair() { + using namespace ShaderRecompiler; + verifyFrontendIdentity(); + const bool reuse = std::getenv("APS5_NO_PERF_FRONTEND_PAIR") == nullptr; + const std::vector code{0xe0700000u, 0x80000000u, 0xbf810000u}; + const std::array buffer{0x10000000u, 0u, 0x40u, 0x00027facu}; + const auto request = FrontendRequest(code, buffer); + auto partial = request; + partial.context.compute->partialThreads = {33u, 1u, 1u}; + const auto freshFull = PrepareResourceProgram(request); + const auto freshPartial = PrepareResourceProgram(partial); + require(ProgramToString(freshFull) != ProgramToString(freshPartial), "frontend pair: fixture does not distinguish full and partial threads"); + const auto storeThreadLimits = [](const IrProgram& program) { + std::vector pending; + for (const auto* block : program.BlockOrder()) { + for (const auto* inst : block->Instructions()) { + if (inst->Opcode() == IrOpcode::StoreBufferU32) pending.push_back(inst->Argument(inst->ArgumentCount() - 1u)); + } + } + require(pending.size() == 1u, "frontend pair: fixture does not retain exactly one buffer store"); + std::vector visited; + std::uint32_t axes = 0u; + while (!pending.empty()) { + const auto* value = pending.back()->Resolve(); + pending.pop_back(); + if (std::find(visited.begin(), visited.end(), value) != visited.end()) continue; + visited.push_back(value); + if (value->Opcode() == IrOpcode::GetBuiltin && value->Argument(0)->Resolve()->ImmediateU32() == static_cast(StageInputKind::DispatchThreadLimit)) { + const auto axis = value->Argument(1)->Resolve()->ImmediateU32(); + require(axis < 3u, "frontend pair: invalid dispatch-limit axis"); + axes |= 1u << axis; + } + for (const auto* argument : value->Arguments()) pending.push_back(argument); + } + return axes; + }; + require(storeThreadLimits(freshFull) == 0u && storeThreadLimits(freshPartial) == 7u, "frontend pair: buffer-store predicate does not retain all partial-thread bounds"); + require(!freshFull.Resources().memoryInfo.empty() && !freshFull.Resources().descriptorSources.empty() && !freshFull.Info().buffers.empty(), "frontend pair: fixture has no buffer resources"); + + for (const bool partialFirst : {false, true}) { + ShaderPreparationContext preparation; + std::shared_ptr retained; + { + auto temporary = code; + auto borrowed = request; + borrowed.shader.code = temporary; + retained = preparation.AcquireFrontend(borrowed); + std::fill(temporary.begin(), temporary.end(), 0xffffffffu); + require((preparation.AcquireFrontend(request) == retained) == reuse, "frontend pair: mutating the caller's code changed the retained frontend"); + } + require((preparation.AcquireFrontend(request) == retained) == reuse, "frontend pair: destroying the caller's code changed the retained frontend"); + const auto first = PrepareResourceProgram(partialFirst ? partial : request, &preparation); + const auto second = PrepareResourceProgram(partialFirst ? request : partial, &preparation); + verifyFrontendProgram(first, partialFirst ? freshPartial : freshFull); + verifyFrontendProgram(second, partialFirst ? freshFull : freshPartial); + require(first.BlockOrder().front() != second.BlockOrder().front(), "frontend pair: full and partial variants share mutable IR"); + const auto repeated = PrepareResourceProgram(partialFirst ? partial : request, &preparation); + verifyFrontendProgram(repeated, first); + auto translated = request; + translated.context.waveSize = 32u; + translated.context.compute->numThreads = {32u, 2u, 1u}; + const auto changed = PrepareResourceProgram(translated, &preparation); + const auto freshChanged = PrepareResourceProgram(translated); + verifyFrontendProgram(changed, freshChanged); + require(changed.WaveSize() != freshFull.WaveSize(), "frontend pair: changed translation inputs were ignored"); + require((preparation.AcquireFrontend(request) == retained) == reuse, "frontend pair: translating a variant replaced the frontend"); + } + + for (const bool partialFirst : {false, true}) { + auto pairCode = code; + pairCode.insert(pairCode.begin(), 0xbf800000u | static_cast(partialFirst)); + auto full = request; + full.shader.code = pairCode; + auto limited = partial; + limited.shader.code = pairCode; + const auto fullReference = Recompile(full); + const auto partialReference = Recompile(limited); + ShaderPreparationContext preparation; + const auto first = PrepareShader(partialFirst ? limited : full, &preparation); + const auto second = PrepareShader(partialFirst ? full : limited, &preparation); + const auto& fullHandle = partialFirst ? second : first; + const auto& partialHandle = partialFirst ? first : second; + const auto& fullArtifact = GetPreparedArtifact(*fullHandle); + const auto& partialArtifact = GetPreparedArtifact(*partialHandle); + require(first->source != second->source, "frontend pair: full and partial fixtures did not exercise separate sources"); + require(fullArtifact.spirv != partialArtifact.spirv, "frontend pair: full and partial threads share the same artifact"); + SrtRuntime runtime{}; + runtime.userData = buffer; + runtime.shaderBase = full.shader.codeAddress; + const auto fullCapture = CaptureResources(full, runtime, *fullHandle); + const auto partialCapture = CaptureResources(limited, runtime, *partialHandle); + const auto fullResult = MaterializeShader(full, *fullCapture, *fullHandle); + const auto partialResult = MaterializeShader(limited, *partialCapture, *partialHandle); + verifyResult(fullReference, *fullResult); + verifyResult(partialReference, *partialResult); + } + + const std::array color{0x3f800000u}; + for (const bool zeroFirst : {false, true}) { + const std::vector pixelCode{0xbf800000u | static_cast(zeroFirst), 0x7e000200u, 0xf800180fu, 0u, 0xbf810000u}; + auto pixel = FrontendRequest(pixelCode, color); + pixel.shader.stage = ShaderStage::Fragment; + pixel.context.compute.reset(); + pixel.context.pixel = ShaderPixelStageInfo{}; + pixel.context.pixel->wave32 = true; + pixel.context.pixel->targetOutputMode[0] = 9u; + pixel.context.waveSize = 32u; + auto zero = pixel; + zero.layout.pushConstantSizeBytes = 0u; + const auto pixelReference = Recompile(pixel); + const auto zeroReference = Recompile(zero); + ShaderPreparationContext preparation; + const auto first = PrepareShader(zeroFirst ? zero : pixel, &preparation); + const auto second = PrepareShader(zeroFirst ? pixel : zero, &preparation); + const auto& pixelHandle = zeroFirst ? second : first; + const auto& zeroHandle = zeroFirst ? first : second; + require(first->source == second->source, "frontend pair: pixel capacities did not exercise the same source"); + require(GetPreparedArtifact(*pixelHandle).spirv != GetPreparedArtifact(*zeroHandle).spirv, "frontend pair: pixel fixture does not distinguish push-constant capacities"); + SrtRuntime runtime{}; + runtime.userData = color; + runtime.shaderBase = pixel.shader.codeAddress; + const auto pixelCapture = CaptureResources(pixel, runtime, *pixelHandle); + const auto zeroCapture = CaptureResources(zero, runtime, *zeroHandle); + verifyResult(pixelReference, *MaterializeShader(pixel, *pixelCapture, *pixelHandle)); + verifyResult(zeroReference, *MaterializeShader(zero, *zeroCapture, *zeroHandle)); + } +} + void verifyRegisterSources() { using namespace ShaderRecompiler; IrResourcePlan plan; @@ -2221,6 +2474,11 @@ void verifyGuardedNullPointers() { int main(int argc, char** argv) { try { using namespace ShaderRecompiler; + if (argc == 2 && std::string_view(argv[1]) == "--frontend-pair") { + verifyFrontendPair(); + std::cout << "Prepared frontend reuse preserves full and partial-thread programs\n"; + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--bindless") { verifyBindlessTable(); std::cout << "Bindless mapping, indexing capabilities and strict validation passed\n"; @@ -2229,6 +2487,7 @@ int main(int argc, char** argv) { require(argc == 1, "unknown shader memory test arguments"); verifyRegisterSources(); verifyEvaluatedValues(); + verifyFrontendPair(); verifyPureFlatSlots(); verifyBindlessTable(); verifyDescriptorPhis(); diff --git a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp index a0c470a7b..f23d1b8f6 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp @@ -9,7 +9,17 @@ namespace ShaderRecompiler { +struct PreparedControlFlow; +class ShaderPreparationContext { +public: + [[nodiscard]] std::shared_ptr AcquireFrontend(const RecompileRequest& request); + +private: + std::shared_ptr frontend; +}; + [[nodiscard]] IrProgram PrepareResourceProgram(const RecompileRequest& request); +[[nodiscard]] IrProgram PrepareResourceProgram(const RecompileRequest& request, ShaderPreparationContext* preparation); [[nodiscard]] std::shared_ptr GetResourcePlan(const RecompileRequest& request); struct SourceEntry; @@ -55,6 +65,7 @@ private: }; [[nodiscard]] std::span GetPreparedCode(const SourceHandle& handle); [[nodiscard]] std::shared_ptr PrepareShader(const RecompileRequest& request); +[[nodiscard]] std::shared_ptr PrepareShader(const RecompileRequest& request, ShaderPreparationContext* preparation); [[nodiscard]] bool MatchesPreparedShader(const RecompileRequest& request, const SourceHandle& handle); void BuildPreparedShaderKey(const RecompileRequest& request, std::vector& key); [[nodiscard]] bool MatchesPreparedShader(const RecompileRequest& request, const SourceHandle& handle, std::span key); diff --git a/core/shader/recompiler/Recompiler.cpp b/core/shader/recompiler/Recompiler.cpp index 5e57d5b4f..0246d07a1 100644 --- a/core/shader/recompiler/Recompiler.cpp +++ b/core/shader/recompiler/Recompiler.cpp @@ -100,22 +100,54 @@ ShaderStageInputInfo RequestInputInfo(const RecompileRequest& request) { } +struct PreparedControlFlow { + PreparedControlFlow(const RecompileRequest& request, const SwappcInfo& swappcInfo) + : stage(request.shader.stage), swappc(swappcInfo), code(request.shader.code.begin(), request.shader.code.end()), + decoded(RdnaInstructionDecoder{}.Decode(code)), cfg(GraphBuilder{}.Build(decoded, &swappc)) { + Structurizer{}.Structurize(cfg); + } + + PreparedControlFlow(const PreparedControlFlow&) = delete; + PreparedControlFlow& operator=(const PreparedControlFlow&) = delete; + + bool Matches(const RecompileRequest& request, const SwappcInfo& swappcInfo) const { + return stage == request.shader.stage && swappc.fetchCallAllowed == swappcInfo.fetchCallAllowed && + swappc.userDataBaseRegister == swappcInfo.userDataBaseRegister && swappc.userDataCount == swappcInfo.userDataCount && + std::ranges::equal(code, request.shader.code); + } + + ShaderStage stage; + SwappcInfo swappc; + std::vector code; + RdnaProgram decoded; + ControlFlowGraph cfg; +}; + +std::shared_ptr ShaderPreparationContext::AcquireFrontend(const RecompileRequest& request) { + const auto inputInfo = RequestInputInfo(request); + const SwappcInfo swappc{inputInfo.vertex != nullptr, request.context.userDataBaseRegister, static_cast(request.context.userData.size())}; + static const bool reuse = std::getenv("APS5_NO_PERF_FRONTEND_PAIR") == nullptr; + if (reuse && frontend != nullptr && frontend->Matches(request, swappc)) { + return frontend; + } + auto prepared = std::make_shared(request, swappc); + if (reuse) { + frontend = prepared; + } + return prepared; +} + IrProgram PrepareResourceProgram(const RecompileRequest& request) { + return PrepareResourceProgram(request, nullptr); +} + +IrProgram PrepareResourceProgram(const RecompileRequest& request, ShaderPreparationContext* preparation) { + ShaderPreparationContext local; + const auto frontend = (preparation != nullptr ? *preparation : local).AcquireFrontend(request); const auto stageKind = toShaderStageKind(request.shader.stage); const auto inputInfo = RequestInputInfo(request); - - constexpr RdnaInstructionDecoder decoder; - const auto decoded = decoder.Decode(request.shader.code); - - constexpr GraphBuilder graphBuilder; - SwappcInfo swappcInfo; - swappcInfo.fetchCallAllowed = inputInfo.vertex != nullptr; - swappcInfo.userDataBaseRegister = request.context.userDataBaseRegister; - swappcInfo.userDataCount = static_cast(request.context.userData.size()); - auto cfg = graphBuilder.Build(decoded, &swappcInfo); - - constexpr Structurizer structurizer; - structurizer.Structurize(cfg); + const auto& decoded = frontend->decoded; + const auto& cfg = frontend->cfg; TranslateOptions translateOptions {}; translateOptions.stage = stageKind; @@ -238,7 +270,7 @@ struct SourceEntry { namespace { struct ResourceProgram { - explicit ResourceProgram(const RecompileRequest& request) : program(std::make_unique(PrepareResourceProgram(request))), plan(std::make_shared(ResourceMaterializer{}.ExtractPlan(*program))) {} + explicit ResourceProgram(const RecompileRequest& request, ShaderPreparationContext* preparation = nullptr) : program(std::make_unique(PrepareResourceProgram(request, preparation))), plan(std::make_shared(ResourceMaterializer{}.ExtractPlan(*program))) {} std::unique_ptr program; std::shared_ptr plan; @@ -264,7 +296,7 @@ bool FailureMemo() { return memo; } -std::shared_ptr getSource(const RecompileRequest& request) { +std::shared_ptr getSource(const RecompileRequest& request, ShaderPreparationContext* preparation = nullptr) { static std::shared_mutex mutex; // Entries whose code hashes alike share a bucket; the code comparison picks the right one. static std::unordered_map, std::vector>, SourceKeyHash> sources; @@ -308,7 +340,7 @@ std::shared_ptr getSource(const RecompileRequest& request) { if (source->plan == nullptr) { if (FailureMemo() && source->planFailure) std::rethrow_exception(source->planFailure); try { - ResourceProgram resource(request); + ResourceProgram resource(request, preparation); source->plan = std::move(resource.plan); source->program = std::move(resource.program); } catch (...) { @@ -735,7 +767,7 @@ bool sameLayout(const BindingLayout& left, const BindingLayout& right) { return left.descriptorSet == right.descriptorSet && left.firstBinding == right.firstBinding && left.pushConstantOffsetBytes == right.pushConstantOffsetBytes && left.pushConstantSizeBytes == right.pushConstantSizeBytes; } -std::shared_ptr findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, bool& cacheHit) { +std::shared_ptr findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, bool& cacheHit, ShaderPreparationContext* preparation = nullptr) { for (const auto& candidate : source.variants) { if (sameLayout(candidate->layout, request.layout)) { cacheHit = true; @@ -759,7 +791,7 @@ std::shared_ptr findOrCompileVariant(SourceEntry& source, } } if (variant == nullptr) { - auto program = source.program != nullptr ? std::move(*source.program) : PrepareResourceProgram(request); + auto program = source.program != nullptr ? std::move(*source.program) : PrepareResourceProgram(request, preparation); source.program.reset(); std::exception_ptr emissionFailure; try { @@ -1031,15 +1063,19 @@ std::shared_ptr ResolveSource(const RecompileRequest& reques } std::shared_ptr PrepareShader(const RecompileRequest& request) { + return PrepareShader(request, nullptr); +} + +std::shared_ptr PrepareShader(const RecompileRequest& request, ShaderPreparationContext* preparation) { return recompileReporting(request, [&]() -> std::shared_ptr { static_cast(RequestInputInfo(request)); auto handle = std::make_shared(); - handle->source = getSource(request); + handle->source = getSource(request, preparation); RecompileCacheKey::BuildInterface(request, handle->staticKey); handle->staticKey.push_back(HostSubgroupSize(request)); bool cacheHit = false; std::lock_guard lock(handle->source->mutex); - handle->artifact = findOrCompileVariant(*handle->source, request, cacheHit); + handle->artifact = findOrCompileVariant(*handle->source, request, cacheHit, preparation); return handle; }); } diff --git a/core/shader/recompiler/ShaderDiskCache.cpp b/core/shader/recompiler/ShaderDiskCache.cpp index a4cad03de..01c16ec61 100644 --- a/core/shader/recompiler/ShaderDiskCache.cpp +++ b/core/shader/recompiler/ShaderDiskCache.cpp @@ -655,6 +655,7 @@ constexpr std::string_view NeutralSwitches[] = { "APS5_DUMP_IR", "APS5_NO_CODE_HASH_KEY", "APS5_NO_FAILURE_MEMO", + "APS5_NO_PERF_FRONTEND_PAIR", "APS5_NO_RESULT_MEMO", };