mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
Merge pull request #12616 from Raghav314/perf/capture-pending-branches
perf(agc): leave a capture's branch over a pending GPU write unpruned instead of waiting
This commit is contained in:
9 files changed
+241
-5
No files matched your search
@@ -91,6 +91,7 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> capture(const ShaderRecompiler::RecompileRequest& request, const ShaderRecompiler::SourceHandle* handle, const ShaderRecompiler::PreparedShaderInvocation* invocation);
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> captureWaitingOnFailure(const ShaderRecompiler::RecompileRequest& request, const ShaderRecompiler::SourceHandle* handle, const ShaderRecompiler::PreparedShaderInvocation* invocation);
|
||||
static constexpr std::size_t PageBytes = 4096;
|
||||
static constexpr std::size_t PageWords = PageBytes / sizeof(std::uint32_t);
|
||||
|
||||
@@ -169,6 +170,8 @@ private:
|
||||
std::size_t descriptorSpanBytes = 0;
|
||||
std::uint64_t rawInvalidations = 0;
|
||||
std::uint32_t forceSync = 0;
|
||||
std::uint64_t branchReadsRefused = 0;
|
||||
bool waitBranches = false;
|
||||
};
|
||||
|
||||
// The positions, among a dispatch-cache variant's stored words, of the pure flat-SRT leaves a
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Optimization/RequestMemoryView.hpp"
|
||||
#include "Optimization/ResourceMaterializer.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include "Optimization/SrtWalker.hpp"
|
||||
#include "ThreadOwned.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -39,6 +40,8 @@ struct CaptureProfile {
|
||||
// were read raw on the driver's evidence, or were read both ways (verify) and differed.
|
||||
std::atomic<std::uint64_t> pagesWordwise{0};
|
||||
std::atomic<std::uint64_t> wordWaits{0};
|
||||
std::atomic<std::uint64_t> branchReadsRefused{0};
|
||||
std::atomic<std::uint64_t> branchCapturesRedone{0};
|
||||
std::atomic<std::uint64_t> wordWaitNanoseconds{0};
|
||||
std::atomic<std::uint64_t> wordsRaw{0};
|
||||
std::atomic<std::uint64_t> wordsVerified{0};
|
||||
@@ -147,6 +150,11 @@ CaptureProfile& CaptureTotals() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
bool BranchesSkipPendingWords() {
|
||||
static const bool skip = std::getenv("APS5_WAIT_PENDING_BRANCHES") == nullptr;
|
||||
return skip;
|
||||
}
|
||||
|
||||
std::atomic<ShaderMemory::WaitedMsProvider> waitedMsProvider{nullptr};
|
||||
|
||||
double WaitedMs() {
|
||||
@@ -425,6 +433,10 @@ bool ShaderMemory::read(void* context, std::uint64_t address, std::uint32_t* val
|
||||
waited = page.wordwise && (self.hookWaits != nullptr ? self.hookWaits() != waitsBefore : true);
|
||||
word = verifiedWord;
|
||||
}
|
||||
} else if (page.wordwise && policy == PendingWrite::Sync && !forcedRaw && !self.waitBranches && BranchesSkipPendingWords() && ShaderRecompiler::SrtEvaluatingBranch()) {
|
||||
++self.branchReadsRefused;
|
||||
if (CaptureProfiled()) ++CaptureTotals().branchReadsRefused;
|
||||
return false;
|
||||
} else {
|
||||
// On a word-wise page the bytes before the hook's wait are kept for the observer.
|
||||
const bool observed = page.wordwise && policy == PendingWrite::Sync && self.observe != nullptr && GuestMemory::Accessible(reinterpret_cast<const void*>(address), sizeof(word));
|
||||
@@ -775,11 +787,29 @@ std::vector<std::shared_ptr<const ShaderRecompiler::SrtGuestWords>> ShaderMemory
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> ShaderMemory::Capture(const ShaderRecompiler::RecompileRequest& request, const ShaderRecompiler::SourceHandle* handle) {
|
||||
return capture(request, handle, nullptr);
|
||||
return captureWaitingOnFailure(request, handle, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> ShaderMemory::Capture(const ShaderRecompiler::PreparedShaderInvocation& invocation) {
|
||||
return capture(invocation.Request(), nullptr, &invocation);
|
||||
return captureWaitingOnFailure(invocation.Request(), nullptr, &invocation);
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> ShaderMemory::captureWaitingOnFailure(const ShaderRecompiler::RecompileRequest& request, const ShaderRecompiler::SourceHandle* handle, const ShaderRecompiler::PreparedShaderInvocation* invocation) {
|
||||
branchReadsRefused = 0;
|
||||
try {
|
||||
return capture(request, handle, invocation);
|
||||
} catch (const std::exception&) {
|
||||
if (branchReadsRefused == 0u) throw;
|
||||
}
|
||||
if (CaptureProfiled()) ++CaptureTotals().branchCapturesRedone;
|
||||
struct WaitBranches {
|
||||
bool& wait;
|
||||
explicit WaitBranches(bool& flag) : wait(flag) { wait = true; }
|
||||
~WaitBranches() { wait = false; }
|
||||
WaitBranches(const WaitBranches&) = delete;
|
||||
WaitBranches& operator=(const WaitBranches&) = delete;
|
||||
} waiting(waitBranches);
|
||||
return capture(request, handle, invocation);
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::ResourceCapture> ShaderMemory::capture(const ShaderRecompiler::RecompileRequest& request, const ShaderRecompiler::SourceHandle* handle, const ShaderRecompiler::PreparedShaderInvocation* invocation) {
|
||||
@@ -827,7 +857,7 @@ std::shared_ptr<const ShaderRecompiler::ResourceCapture> ShaderMemory::capture(c
|
||||
const auto materializeNs = ShaderRecompiler::ResourceMaterializer::MaterializeNanoseconds();
|
||||
const auto evaluateNs = ShaderRecompiler::ResourceMaterializer::EvaluateNanoseconds();
|
||||
const auto walkNs = captureNs > resolveNs + pageNs + wordNs + specializationNs ? captureNs - resolveNs - pageNs - wordNs - specializationNs : 0;
|
||||
AgcDriver::ProfilePrint_nid_no_patch( "[capture] %llu captures: %llu word reads, %llu pages fetched (%llu read word by word over pending GPU writes: %llu words waited %.2f s, %llu read raw on evidence, %llu verified with %llu mismatches, %llu served from known values, %llu hook reads not observed: no GPU wait), capture (plan lookup + materialize) %.1f s, %llu KiB registered code this capture; sub-phases (s, us per capture): source resolve %.2f/%.1f, walk %.2f/%.1f, specialization (every materialize) %.2f/%.1f, page reads %.2f/%.1f, word reads %.2f/%.1f, hook GPU waits inside %.2f/%.1f, materialize %.2f/%.1f (SRT evaluate %.2f/%.1f); source handle memo %llu hits, %llu resolves\n", static_cast<unsigned long long>(totals.captures.load()), static_cast<unsigned long long>(totals.reads.load()), static_cast<unsigned long long>(totals.pages.load()), static_cast<unsigned long long>(totals.pagesWordwise.load()), static_cast<unsigned long long>(totals.wordWaits.load()), totals.wordWaitNanoseconds.load() / 1e9, static_cast<unsigned long long>(totals.wordsRaw.load()), static_cast<unsigned long long>(totals.wordsVerified.load()), static_cast<unsigned long long>(totals.wordMismatches.load()), static_cast<unsigned long long>(totals.wordsKnown.load()), static_cast<unsigned long long>(totals.observationsSkipped.load()), captureNs / 1e9, static_cast<unsigned long long>(initialBytes / 1024), resolveNs / 1e9, resolveNs / captures / 1e3, walkNs / 1e9, walkNs / captures / 1e3, specializationNs / 1e9, specializationNs / captures / 1e3, pageNs / 1e9, pageNs / captures / 1e3, wordNs / 1e9, wordNs / captures / 1e3, hookNs / 1e9, hookNs / captures / 1e3, materializeNs / 1e9, materializeNs / captures / 1e3, evaluateNs / 1e9, evaluateNs / captures / 1e3, static_cast<unsigned long long>(totals.handleHits.load()), static_cast<unsigned long long>(totals.handleMisses.load()));
|
||||
AgcDriver::ProfilePrint_nid_no_patch( "[capture] %llu captures: %llu word reads, %llu pages fetched (%llu read word by word over pending GPU writes: %llu words waited %.2f s, %llu read raw on evidence, %llu verified with %llu mismatches, %llu served from known values, %llu hook reads not observed: no GPU wait, %llu branch condition reads over pending GPU writes left the branch unpruned instead of waiting, %llu captures redone waiting after one failed), capture (plan lookup + materialize) %.1f s, %llu KiB registered code this capture; sub-phases (s, us per capture): source resolve %.2f/%.1f, walk %.2f/%.1f, specialization (every materialize) %.2f/%.1f, page reads %.2f/%.1f, word reads %.2f/%.1f, hook GPU waits inside %.2f/%.1f, materialize %.2f/%.1f (SRT evaluate %.2f/%.1f); source handle memo %llu hits, %llu resolves\n", static_cast<unsigned long long>(totals.captures.load()), static_cast<unsigned long long>(totals.reads.load()), static_cast<unsigned long long>(totals.pages.load()), static_cast<unsigned long long>(totals.pagesWordwise.load()), static_cast<unsigned long long>(totals.wordWaits.load()), totals.wordWaitNanoseconds.load() / 1e9, static_cast<unsigned long long>(totals.wordsRaw.load()), static_cast<unsigned long long>(totals.wordsVerified.load()), static_cast<unsigned long long>(totals.wordMismatches.load()), static_cast<unsigned long long>(totals.wordsKnown.load()), static_cast<unsigned long long>(totals.observationsSkipped.load()), static_cast<unsigned long long>(totals.branchReadsRefused.load()), static_cast<unsigned long long>(totals.branchCapturesRedone.load()), captureNs / 1e9, static_cast<unsigned long long>(initialBytes / 1024), resolveNs / 1e9, resolveNs / captures / 1e3, walkNs / 1e9, walkNs / captures / 1e3, specializationNs / 1e9, specializationNs / captures / 1e3, pageNs / 1e9, pageNs / captures / 1e3, wordNs / 1e9, wordNs / captures / 1e3, hookNs / 1e9, hookNs / captures / 1e3, materializeNs / 1e9, materializeNs / captures / 1e3, evaluateNs / 1e9, evaluateNs / captures / 1e3, static_cast<unsigned long long>(totals.handleHits.load()), static_cast<unsigned long long>(totals.handleMisses.load()));
|
||||
}
|
||||
}
|
||||
return capture;
|
||||
|
||||
@@ -916,6 +916,98 @@ void verifyPlanRetention() {
|
||||
require(copiedWalk.active == wholeWalk.active && copiedWalk.results == wholeWalk.results && copiedWalk.flat == wholeWalk.flat, "plan retention: the plan's walk differs from the program's");
|
||||
}
|
||||
|
||||
std::uint32_t branchReadsRefused = 0;
|
||||
|
||||
bool readFiveUnlessBranch(void*, std::uint64_t, std::uint32_t* value) {
|
||||
if (ShaderRecompiler::SrtEvaluatingBranch()) {
|
||||
++branchReadsRefused;
|
||||
return false;
|
||||
}
|
||||
*value = 5u;
|
||||
return true;
|
||||
}
|
||||
|
||||
void verifyPendingBranchLeftUnpruned() {
|
||||
using namespace ShaderRecompiler;
|
||||
std::vector<std::unique_ptr<IrValue>> values;
|
||||
std::uint32_t ids = 0;
|
||||
const auto make = [&](IrOpcode opcode, IrType type) -> IrValue& {
|
||||
values.push_back(std::make_unique<IrValue>(opcode, type, ids++));
|
||||
return *values.back();
|
||||
};
|
||||
const auto constant = [&](std::uint32_t value) -> IrValue& {
|
||||
auto& immediate = make(IrOpcode::Void, IrType::U32);
|
||||
immediate.SetImmediateU32(value);
|
||||
return immediate;
|
||||
};
|
||||
const auto userData = [&](std::uint32_t index) -> IrValue& {
|
||||
auto& reg = make(IrOpcode::Void, IrType::ScalarReg);
|
||||
reg.SetRegister({RegisterBank::Scalar, index});
|
||||
auto& read = make(IrOpcode::GetUserData, IrType::U32);
|
||||
read.AddArgument(®);
|
||||
return read;
|
||||
};
|
||||
const auto binary = [&](IrOpcode opcode, IrType type, IrValue& left, IrValue& right) -> IrValue& {
|
||||
auto& value = make(opcode, type);
|
||||
value.AddArgument(&left);
|
||||
value.AddArgument(&right);
|
||||
return value;
|
||||
};
|
||||
constexpr std::uint32_t Chain = 64;
|
||||
IrValue* address = &userData(4);
|
||||
for (std::uint32_t step = 0; step < Chain; ++step) address = &binary(IrOpcode::IAdd32, IrType::U32, *address, constant(1));
|
||||
auto& load = make(IrOpcode::LoadBufferU32, IrType::U32);
|
||||
load.AddArgument(address);
|
||||
auto& low = binary(IrOpcode::CompositeExtractU32x2, IrType::U32, binary(IrOpcode::CompositeConstructU32x2, IrType::U32x2, userData(1), load), constant(0));
|
||||
auto& mask = binary(IrOpcode::IEqual32, IrType::Bool, userData(5), constant(0));
|
||||
auto& choice = make(IrOpcode::SelectU32, IrType::U32);
|
||||
choice.AddArgument(&mask);
|
||||
choice.AddArgument(&userData(6));
|
||||
choice.AddArgument(&load);
|
||||
auto& offset = binary(IrOpcode::ReadFirstLane, IrType::U32, choice, mask);
|
||||
auto& read = binary(IrOpcode::LoadAddressU32, IrType::U32, binary(IrOpcode::CompositeConstructU64, IrType::U64, userData(2), userData(3)), offset);
|
||||
read.SetFlags(MemoryFlags{0, 0});
|
||||
IrProgram program;
|
||||
auto& resources = program.Resources();
|
||||
resources.srtPlanComplete = true;
|
||||
resources.resourceTrackingComplete = true;
|
||||
MemoryInfo memory;
|
||||
memory.kind = ResourceKind::ScalarAddress;
|
||||
resources.memoryInfo.push_back(memory);
|
||||
for (std::uint32_t source = 0; source < 2u; ++source) {
|
||||
DescriptorSource descriptor;
|
||||
descriptor.dwordCount = 4;
|
||||
for (std::uint32_t dword = 0; dword < 4u; ++dword) descriptor.dwords[dword] = &userData(10u + source * 4u + dword);
|
||||
resources.descriptorSources.push_back(descriptor);
|
||||
BufferResource buffer;
|
||||
buffer.source = source;
|
||||
resources.info.buffers.push_back(buffer);
|
||||
}
|
||||
resources.controlFlow.push_back({&binary(IrOpcode::IEqual32, IrType::Bool, low, constant(7)), {1, 2}, {0}, {}});
|
||||
resources.controlFlow.push_back({&binary(IrOpcode::IEqual32, IrType::Bool, read, constant(5)), {3, 4}, {}, {}});
|
||||
resources.controlFlow.push_back({nullptr, {}, {}, {}});
|
||||
resources.controlFlow.push_back({nullptr, {}, {}, {}});
|
||||
resources.controlFlow.push_back({nullptr, {}, {1}, {}});
|
||||
const auto plan = ResourceMaterializer{}.ExtractPlan(program);
|
||||
std::array<std::uint32_t, 18> userWords{};
|
||||
userWords[1] = 7u;
|
||||
userWords[2] = 0x1000u;
|
||||
userWords[6] = 8u;
|
||||
for (std::uint32_t word = 10; word < userWords.size(); ++word) userWords[word] = 0x100u + word;
|
||||
SrtRuntime runtime{userWords};
|
||||
runtime.readMemory = readFiveUnlessBranch;
|
||||
runtime.readSpecializationMemory = readFiveUnlessBranch;
|
||||
branchReadsRefused = 0;
|
||||
const auto copiedWalk = walkPlan(plan, plan.materializationSources, plan.cleanFlatSlots, runtime);
|
||||
require(branchReadsRefused != 0u && !SrtEvaluatingBranch(), "pending branch: the plan's walk read its branch condition outside a branch scope or left the scope open");
|
||||
branchReadsRefused = 0;
|
||||
const std::array<std::uint32_t, 2> sources{0u, 1u};
|
||||
const auto wholeWalk = walkPlan(program.Resources(), sources, {}, runtime);
|
||||
require(branchReadsRefused != 0u && !SrtEvaluatingBranch(), "pending branch: the program's walk read its branch condition outside a branch scope or left the scope open");
|
||||
require(wholeWalk.active == std::vector<std::uint8_t>{1u, 1u}, "pending branch: a condition whose word could not be read pruned a successor");
|
||||
require(copiedWalk.active == wholeWalk.active && copiedWalk.results == wholeWalk.results && copiedWalk.flat == wholeWalk.flat, "pending branch: the plan's walk differs from the program's");
|
||||
}
|
||||
|
||||
void verifyPlanRetentionChains() {
|
||||
using namespace ShaderRecompiler;
|
||||
std::vector<std::size_t> kept;
|
||||
@@ -2544,6 +2636,89 @@ void verifyExecGuardedDescriptorSources() {
|
||||
}
|
||||
}
|
||||
|
||||
void verifyPendingBranchCapture() {
|
||||
using namespace ShaderRecompiler;
|
||||
constexpr std::uint32_t Format8888UNorm = 56;
|
||||
constexpr std::uint32_t Type2D = 9;
|
||||
struct alignas(256) Texture { std::array<std::uint8_t, 256> bytes{}; };
|
||||
static Texture texture;
|
||||
const auto base = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(texture.bytes.data()));
|
||||
const std::array<std::uint32_t, 8> valid{static_cast<std::uint32_t>(base >> 8u), static_cast<std::uint32_t>((base >> 40u) & 0xffu) | (Format8888UNorm << 20u) | (3u << 30u), 3u << 14u, 0xfacu | (Type2D << 28u), 0u, 0u, 0u, 0u};
|
||||
const std::array<std::uint32_t, 8> invalid{0x12345678u, 0xffffffffu, 0xffffffffu, 2u << 28u, 0u, 0u, 0u, 0u};
|
||||
const std::array<std::uint32_t, 4> sampler{0u, 0u, 0u, 0u};
|
||||
static std::array<std::uint32_t, 4> output{};
|
||||
const auto outputAddress = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(output.data()));
|
||||
const std::array<std::uint32_t, 4> outputV{static_cast<std::uint32_t>(outputAddress), static_cast<std::uint32_t>((outputAddress >> 32u) & 0xffffu), 16u, 0xfacu};
|
||||
alignas(4096) static std::array<std::uint32_t, 1024> srt{};
|
||||
alignas(4096) static std::array<std::uint32_t, 1024> flags{};
|
||||
const auto flagsAddress = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(flags.data()));
|
||||
const std::array<std::uint32_t, 4> flagsV{static_cast<std::uint32_t>(flagsAddress), static_cast<std::uint32_t>((flagsAddress >> 32u) & 0xffffu), 16u, 0xfacu};
|
||||
static std::uint64_t conditionAddress = 0;
|
||||
static std::uint64_t waits = 0;
|
||||
const auto fill = [&](const std::array<std::uint32_t, 8>& image, std::uint32_t mode) {
|
||||
srt.fill(0u);
|
||||
std::copy(image.begin(), image.end(), srt.begin());
|
||||
std::copy(sampler.begin(), sampler.end(), srt.begin() + 8);
|
||||
std::copy(outputV.begin(), outputV.end(), srt.begin() + 16);
|
||||
std::copy(flagsV.begin(), flagsV.end(), srt.begin() + 24);
|
||||
flags[0] = mode;
|
||||
};
|
||||
const auto srtAddress = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(srt.data()));
|
||||
conditionAddress = flagsAddress;
|
||||
const std::array<std::uint32_t, 2> userData{static_cast<std::uint32_t>(srtAddress), static_cast<std::uint32_t>(srtAddress >> 32u)};
|
||||
const std::array<std::uint32_t, 4> capabilities{29u, spv::CapabilityInt64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess};
|
||||
const std::array<std::string_view, 2> extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"};
|
||||
RecompileRequest request{};
|
||||
const std::vector<std::uint32_t> code{0xf4080700u, 0xfa000060u, 0xf4080500u, 0xfa000020u, 0xf4080600u, 0xfa000040u, 0xbf8cc07fu, 0xf420040eu, 0xfa000000u, 0xbf8cc07fu, 0xbf068010u, 0xbf850006u, 0xf40c0200u, 0xfa000000u, 0xbf8cc07fu, 0xf09c0f08u, 0x00a20000u, 0xbf8c3f70u, 0xe0700000u, 0x80060000u, 0xbf810000u};
|
||||
request.shader = {ShaderStage::Compute, 0x2a000u, code, 0, {}};
|
||||
request.context.waveSize = 32;
|
||||
request.context.userDataBaseRegister = 0;
|
||||
request.context.userData = userData;
|
||||
request.context.compute = ShaderComputeStageInfo{{32u, 1u, 1u}, 0u, {false, false, false}, false, 1u};
|
||||
request.target.vulkanVersion = 0x00401000u;
|
||||
request.target.spirvVersion = 0x00010300u;
|
||||
request.target.subgroupSize = 32;
|
||||
request.target.supportedCapabilities = capabilities;
|
||||
request.target.supportedExtensions = extensions;
|
||||
request.target.bdaAbiVersion = BdaAbi::Version;
|
||||
request.target.fragmentShaderBarycentricEnabled = false;
|
||||
request.layout.pushConstantSizeBytes = 128;
|
||||
|
||||
struct FlushReset {
|
||||
~FlushReset() { AgcDriver::GuestMemory::SetFlushHook(nullptr); }
|
||||
} flushReset;
|
||||
AgcDriver::GuestMemory::SetFlushHook([](std::uint64_t address, std::size_t bytes) {
|
||||
if (address < conditionAddress + sizeof(std::uint32_t) && conditionAddress < address + bytes) ++waits;
|
||||
});
|
||||
const auto pending = +[](std::uint64_t address, std::size_t bytes, std::span<std::byte>) -> AgcDriver::ShaderMemory::PendingWrite {
|
||||
using Pending = AgcDriver::ShaderMemory::PendingWrite;
|
||||
return address < conditionAddress + sizeof(std::uint32_t) && conditionAddress < address + bytes ? Pending::Sync : Pending::None;
|
||||
};
|
||||
const auto captured = [&](const std::array<std::uint32_t, 8>& image, std::uint32_t mode) {
|
||||
fill(image, mode);
|
||||
waits = 0;
|
||||
AgcDriver::ShaderMemory memory({}, pending);
|
||||
return memory.Capture(request);
|
||||
};
|
||||
const auto holds = [](const std::shared_ptr<const ResourceCapture>& capture, const std::array<std::uint32_t, 8>& image) {
|
||||
return capture->snapshot.images.size() == 1u && std::equal(image.begin(), image.end(), capture->snapshot.images[0].dwords.begin());
|
||||
};
|
||||
const std::array<std::uint32_t, 8> null{};
|
||||
|
||||
const auto unpruned = captured(valid, 0u);
|
||||
require(waits == 0u, ("pending branch capture: the branch condition over a pending GPU write waited " + std::to_string(waits) + " times").c_str());
|
||||
require(holds(unpruned, valid), "pending branch capture: the branch over a pending GPU write was pruned instead of keeping both successors");
|
||||
const auto redone = captured(invalid, 0u);
|
||||
require(waits != 0u, "pending branch capture: a capture that failed behind an unpruned branch was not redone waiting for the condition");
|
||||
require(holds(redone, null), "pending branch capture: the capture redone waiting did not prune the branch its condition skips");
|
||||
fill(invalid, 1u);
|
||||
{
|
||||
AgcDriver::ShaderMemory memory({}, pending);
|
||||
expectFailure([&] { static_cast<void>(memory.Capture(request)); }, "runtime image descriptor is invalid", "pending branch capture: an invalid T# behind a taken branch was accepted after the capture was redone waiting");
|
||||
}
|
||||
std::cout << "pending branch capture passed\n";
|
||||
}
|
||||
|
||||
void verifyGuardedDescriptorSources() {
|
||||
using namespace ShaderRecompiler;
|
||||
constexpr std::uint32_t Format8888UNorm = 56;
|
||||
@@ -10726,9 +10901,11 @@ int main(int argc, char** argv) {
|
||||
verifyPureFlatSlots();
|
||||
verifyPlanConditionPruning();
|
||||
verifyPlanRetention();
|
||||
verifyPendingBranchLeftUnpruned();
|
||||
verifyPlanRetentionChains();
|
||||
verifyPlanCopyAnswers();
|
||||
verifyGuardedDescriptorSources();
|
||||
verifyPendingBranchCapture();
|
||||
verifyLaneMaskGuardedDescriptorSources();
|
||||
verifyExecGuardedDescriptorSources();
|
||||
verifySrtTape();
|
||||
|
||||
@@ -291,6 +291,7 @@ function(add_shader_recompiler target runtimeTarget)
|
||||
RuntimeAbi::HeapCapacity
|
||||
SampledTextureNumericClass
|
||||
SetDebugProbeActive
|
||||
SrtEvaluatingBranch
|
||||
ShaderCacheDirectory
|
||||
ShaderDiskCache::Shutdown
|
||||
ShaderStage
|
||||
|
||||
@@ -134,6 +134,16 @@ enum class RuntimeValueType {
|
||||
Integer
|
||||
};
|
||||
|
||||
bool& SrtEvaluatingBranch();
|
||||
|
||||
struct SrtBranchScope {
|
||||
bool saved;
|
||||
SrtBranchScope() : saved(SrtEvaluatingBranch()) { SrtEvaluatingBranch() = true; }
|
||||
~SrtBranchScope() { SrtEvaluatingBranch() = saved; }
|
||||
SrtBranchScope(const SrtBranchScope&) = delete;
|
||||
SrtBranchScope& operator=(const SrtBranchScope&) = delete;
|
||||
};
|
||||
|
||||
class SrtWalker {
|
||||
public:
|
||||
void BuildPlan(IrProgram& program) const;
|
||||
|
||||
@@ -104,7 +104,12 @@ bool evaluateRuntimeSources(const IrResourcePlan& program, std::span<const std::
|
||||
std::uint32_t condition = 0;
|
||||
TNode conditionValue{};
|
||||
const auto nullRootsBefore = conditionNullRoots;
|
||||
const bool cleanEvaluable = TNode::Condition(program, index, conditionValue) && runtime.readSpecializationMemory != nullptr && cleanEvaluator.Evaluate(conditionValue, condition) && conditionNullRoots == nullRootsBefore;
|
||||
bool conditionEvaluated = false;
|
||||
{
|
||||
const SrtBranchScope branch;
|
||||
conditionEvaluated = TNode::Condition(program, index, conditionValue) && runtime.readSpecializationMemory != nullptr && cleanEvaluator.Evaluate(conditionValue, condition);
|
||||
}
|
||||
const bool cleanEvaluable = conditionEvaluated && conditionNullRoots == nullRootsBefore;
|
||||
if (cleanEvaluable) {
|
||||
pending.push_back(block.successors[condition != 0u ? 0u : 1u]);
|
||||
} else {
|
||||
|
||||
@@ -640,7 +640,11 @@ bool SrtTape::Run(const IrResourcePlan& plan, const SrtRuntime& runtime, std::ve
|
||||
for (const auto image : block.images) active[imageBase + image] = 1u;
|
||||
for (const auto slot : block.flatSlots) activeFlat[slot] = 1u;
|
||||
std::uint64_t condition = 0;
|
||||
const bool cleanEvaluable = block.condition != nullptr && runtime.readSpecializationMemory != nullptr && conditionRoots[index] != NoNode && evaluate(frame, conditionRoots[index], condition);
|
||||
bool cleanEvaluable = false;
|
||||
{
|
||||
const SrtBranchScope branch;
|
||||
cleanEvaluable = block.condition != nullptr && runtime.readSpecializationMemory != nullptr && conditionRoots[index] != NoNode && evaluate(frame, conditionRoots[index], condition);
|
||||
}
|
||||
if (frame.nullRoot) return refuse();
|
||||
if (cleanEvaluable) {
|
||||
if (block.successors.size() < 2) return refuse();
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
bool& SrtEvaluatingBranch() {
|
||||
thread_local bool evaluating = false;
|
||||
return evaluating;
|
||||
}
|
||||
|
||||
void SrtWalker::BuildPlan(IrProgram& program) const {
|
||||
if (program.Resources().resourceTrackingComplete) {
|
||||
throw std::runtime_error("shader SRT planning failed: cannot rebuild SRT after resource tracking");
|
||||
|
||||
@@ -70,6 +70,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti
|
||||
| `APS5_VERIFY_MASKED_RESULT_MEMO` | presence | `core/shader/recompiler/Recompiler.cpp` | | also materializes every patched result memo hit, prints the first twenty whose patched result differs and counts them in the result memo report of `APS5_PROFILE_DRAW` |
|
||||
| `APS5_VERIFY_SRT_COMPACT` | presence | `core/shader/recompiler/Optimization/src/SrtWalker/SrtDescriptorEvaluation.cpp` | | walks every SRT descriptor a second time through the IR evaluator and throws when the two walks differ |
|
||||
| `APS5_VERIFY_SRT_TAPE` | presence | `core/shader/recompiler/Optimization/src/SrtWalker/SrtTape.cpp` | | walks a plan's runtime sources a second time through the evaluator, adopts its answers where the two differ and prints the first twenty differences |
|
||||
| `APS5_WAIT_PENDING_BRANCHES` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp` | | a resource capture that evaluates a scalar branch condition over a word with a pending GPU write waits for the write to prune the branch, instead of leaving the branch unpruned (both successors' resources captured) |
|
||||
| `APS5_WAVE_LDS_SUBGROUP` | presence | `core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp` | | restores subgroup-scope LDS barriers for single-wave workgroups instead of workgroup scope |
|
||||
|
||||
### libSceAgc
|
||||
|
||||
Reference in new issue
Block a user