Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -315,20 +315,21 @@ class Driver {
static void notePendingWait(std::span<const std::uint32_t> packet, std::uint32_t queue, std::uint64_t received);
void landPendingWaits(std::uint32_t queue);
struct ResolvedDispatch {
std::uint64_t address = 0, key = 0, indirectArguments = 0;
std::uint64_t address = 0, key = 0, indirectArguments = 0, forgetAtCapture = 0;
std::array<std::uint32_t, 5> packet{};
std::shared_ptr<const ShaderSnapshot> registeredShader;
std::shared_ptr<const ShaderRecompiler::RecompileResult> compiledResult;
std::shared_ptr<DispatchVariant> keepVariant, attachVariant;
std::shared_ptr<DispatchEntry> missedEntry;
std::shared_ptr<ShaderMemory> shaderMemory;
std::vector<ShaderRecompiler::MemoryRegion> captured;
std::vector<std::uint32_t> liveWords;
std::shared_ptr<const ShaderRecompiler::ResourceCapture> capture;
bool dataHit = false, cached = false, validated = false;
bool dataHit = false, cached = false, validated = false, missedDiffering = false, noDispatchCache = false;
std::chrono::steady_clock::time_point resolvedAt{};
};
struct GroupCaptureStats {
std::uint64_t runs = 0, resolved = 0, resolvedHits = 0, adopted = 0, addressMismatches = 0, keyMismatches = 0, memoryMismatches = 0, failures = 0, stale = 0, labelOverlaps = 0, writtenBefore = 0, deviceRejects = 0, argumentMismatches = 0;
std::uint64_t runs = 0, resolved = 0, resolvedHits = 0, adopted = 0, addressMismatches = 0, keyMismatches = 0, memoryMismatches = 0, failures = 0, stale = 0, labelOverlaps = 0, writtenBefore = 0, unknownWrites = 0, deviceRejects = 0, argumentMismatches = 0;
double resolveMs = 0, resolveMaxMs = 0, ageMs = 0, ageMaxMs = 0;
std::chrono::steady_clock::time_point reported = std::chrono::steady_clock::now();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,20 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
if (resolving) {
if (indirectArguments == 0 && Graphics::MatchMultisampledSingleClear(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads)) return;
if (!partialLastGroup && matchesFillKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute)) return;
if (!partialLastGroup && indirectArguments == 0 && (MatchDwordPatternFill(packet, std::span(snapshot.code).subspan(codeOffset), userData, compute) || MatchConstantDwordFill(packet, std::span(snapshot.code).subspan(codeOffset), userData, compute) || MatchClampedGlobalFill(packet, std::span(snapshot.code).subspan(codeOffset), userData, compute) || MatchMemoryValueBufferFill(packet, std::span(snapshot.code).subspan(codeOffset), userData, compute))) return;
if (!partialLastGroup && indirectArguments == 0 && (matchesCopyKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute) || MatchesStreamCopyKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute) || MatchesIndexedCopyKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute))) return;
} else {
if (indirectArguments == 0 && Graphics::TakeMultisampledSingleClear(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads)) {
pendingDispatchPhases().outcome = DispatchOutcome::FillHle;
if (resolvedAhead().empty()) gateOpened() = true;
return;
}
if (indirectArguments == 0 && (packet[4] & 0x20u) == 0) {
if (const auto fill = Graphics::MatchMemoryValueFill(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads, {packet[1], packet[2], packet[3]})) Graphics::NoteMemoryValueFill(*fill);
}
if (!partialLastGroup && fillBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice)) {
pendingDispatchPhases().outcome = DispatchOutcome::FillHle;
if (resolvedAhead().empty()) gateOpened() = true;
return;
}
if (DrawPipeline::Active() && !pipelineCandidate) DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Packet, 0x15);
Expand All @@ -161,6 +164,7 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
}
if (!partialLastGroup && indirectArguments == 0 && copyBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice, address)) {
pendingDispatchPhases().outcome = DispatchOutcome::CopyHle;
if (resolvedAhead().empty()) gateOpened() = true;
return;
}
}
Expand Down Expand Up @@ -360,12 +364,12 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
}
if (!pipelined && DrawPipeline::Queue0().Busy()) DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Packet, 0x15);
}
std::uint64_t forgetAtCapture = 0;
if (cached) {
require(keepVariant != nullptr && keepVariant->shader == registeredShader, "dispatch cache belongs to another registered shader");
captureMs += phaseTiming.Elapsed();
} else if (!fromAhead) {
shaderMemory = std::make_shared<ShaderMemory>(memory, &queryPendingWrite, &observePendingWrite, hookWaitCounter(), &QueryPendingSpan);
std::uint64_t forgetAtCapture = 0;

static const bool dumpShaders = std::getenv("APS5_DUMP_SHADERS") != nullptr;
try {
Expand Down Expand Up @@ -413,34 +417,33 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
}
recompileMs += phaseTiming.Elapsed();
phaseTiming.Phase(PhaseRecompile);
insertDispatch(address, key, noDispatchCache || !CacheableResult(*compiledResult), profile, registeredShader, forgetAtCapture, memory, shaderMemory, captured, capture, compiledResult, missedEntry, missedDiffering, attachVariant, phaseTiming);
if (!resolving) insertDispatch(address, key, noDispatchCache || !CacheableResult(*compiledResult), profile, registeredShader, forgetAtCapture, memory, shaderMemory, captured, capture, compiledResult, missedEntry, missedDiffering, attachVariant, phaseTiming);
}
if (resolving) {
auto& parked = resolvedAhead()[currentPacketOffset()];
parked = ResolvedDispatch{};
parked.address = address;
parked.key = key;
parked.indirectArguments = indirectArguments;
parked.forgetAtCapture = forgetAtCapture;
std::copy_n(packet.begin(), std::min(packet.size(), parked.packet.size()), parked.packet.begin());
parked.registeredShader = registeredShader;
parked.compiledResult = compiledResult;
parked.keepVariant = keepVariant;
parked.attachVariant = attachVariant;
parked.missedEntry = missedEntry;
parked.shaderMemory = shaderMemory;
parked.captured = std::move(captured);
parked.liveWords = std::move(liveWords);
parked.capture = capture;
parked.dataHit = dataHit;
parked.cached = cached;
parked.validated = validated;
parked.missedDiffering = missedDiffering;
parked.noDispatchCache = noDispatchCache || (compiledResult != nullptr && !CacheableResult(*compiledResult));
parked.resolvedAt = std::chrono::steady_clock::now();
++groupStats.resolved;
if (cached) ++groupStats.resolvedHits;
if (!cached && compiledResult != nullptr && !localDevice->HasComputePipeline(*compiledResult)) {
SubmitLibraryPrebuild([localDevice, compiledResult, address] {
try { localDevice->PrebuildComputePipeline(*compiledResult, address); } catch (const std::exception&) {}
});
}
return;
}
if (verifyDataHits() && dataHit) verifyDataHit(snapshot, codeOffset, request, memory, address, *keepVariant, liveWords, *compiledResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "ThreadOwned.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Memory/DrawWriteRanges.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
Expand Down Expand Up @@ -108,10 +109,6 @@ void NoteLabel(std::vector<RunLabel>& labels, std::uint64_t address, std::span<c
labels.push_back({address, bytes.size(), value});
}

bool WritesThroughPointers(const ShaderRecompiler::RecompileResult& compiled) {
return std::any_of(compiled.bindings.begin(), compiled.bindings.end(), [](const ShaderRecompiler::DescriptorBinding& binding) { return binding.role == ShaderRecompiler::DescriptorRole::BdaPagetable; });
}

bool OverlapsLabel(std::span<const ShaderRecompiler::MemoryRegion> regions, const std::vector<RunLabel>& labels) {
for (const auto& label : labels) {
for (const auto& region : regions) {
Expand All @@ -136,7 +133,6 @@ void Driver::resolveGroupAhead(const Submission& submission, const QueueState& l
QueueState scratch = live;
std::vector<RunLabel> labels;
std::vector<std::pair<std::uint64_t, std::uint64_t>> writes;
bool unknownWrites = false;
for (const auto& label : deferredLabels().labels) NoteLabel(labels, label.address, std::span<const std::byte>(label.bytes).first(label.size));
struct Resolving {
std::size_t offset;
Expand Down Expand Up @@ -277,7 +273,7 @@ void Driver::resolveGroupAhead(const Submission& submission, const QueueState& l
const auto argumentsEnd = arguments + 3u * sizeof(std::uint32_t);
const bool written = std::any_of(writes.begin(), writes.end(), [&](const auto& range) { return range.first < argumentsEnd && arguments < range.second; })
|| std::any_of(labels.begin(), labels.end(), [&](const RunLabel& label) { return label.address < argumentsEnd && arguments < label.address + label.size; });
if ((unknownWrites || written) && indirectArgumentsPath(packet, arguments) != IndirectGpu) {
if (written && indirectArgumentsPath(packet, arguments) != IndirectGpu) {
++stats.writtenBefore;
break;
}
Expand All @@ -302,10 +298,38 @@ void Driver::resolveGroupAhead(const Submission& submission, const QueueState& l
++stats.writtenBefore;
break;
}
if (found->second.compiledResult != nullptr) appendWrittenRanges(*found->second.compiledResult, writes);
if (found->second.compiledResult == nullptr || WritesThroughPointers(*found->second.compiledResult)) unknownWrites = true;
if (found->second.compiledResult == nullptr) {
++stats.unknownWrites;
break;
}
if (!found->second.cached && found->second.registeredShader != nullptr) {
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
const auto& snapshot = *found->second.registeredShader;
std::vector<ShaderRecompiler::MemoryRegion> memory{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}};
if (!snapshot.header.empty()) memory.push_back({snapshot.headerAddress, snapshot.header});
auto lap = std::chrono::steady_clock::now();
std::array<double, DriverPhaseCount> phaseMs{};
auto phaseLap = lap;
DispatchPhaseTiming phaseTiming{profile, lap, phaseMs, phaseLap};
insertDispatch(found->second.address, found->second.key, found->second.noDispatchCache, profile, found->second.registeredShader, found->second.forgetAtCapture, memory, found->second.shaderMemory, found->second.captured, found->second.capture, found->second.compiledResult, found->second.missedEntry, found->second.missedDiffering, found->second.attachVariant, phaseTiming);
if (!localDevice->HasComputePipeline(*found->second.compiledResult)) {
const auto compiledResult = found->second.compiledResult;
const auto address = found->second.address;
SubmitLibraryPrebuild([localDevice, compiledResult, address] {
try { localDevice->PrebuildComputePipeline(*compiledResult, address); } catch (const std::exception&) {}
});
}
}
bool bounded = false;
const auto itemWrites = DispatchWriteRanges(*found->second.compiledResult, bounded);
if (!bounded) {
++stats.unknownWrites;
break;
}
writes.insert(writes.end(), itemWrites.begin(), itemWrites.end());
} else {
unknownWrites = true;
++stats.unknownWrites;
break;
}
} else if (opcode == 0x37 || opcode == 0x49) {
const auto label = Pm4::DecodeLabelWrite(packet);
Expand Down Expand Up @@ -334,10 +358,10 @@ void Driver::reportGroupCapture(std::uint32_t queue) {
auto& stats = groupCaptureStats();
const auto now = std::chrono::steady_clock::now();
if (now - stats.reported < std::chrono::seconds(10)) return;
AgcDriver::ProfilePrint_nid_no_patch("[groupcap] queue 0x%x (10 s): %llu runs resolved %llu dispatches (%llu cache hits) in %.1f ms (max %.2f ms); %llu adopted %.2f ms after the resolve on average, max %.2f ms; mismatches: address %llu key %llu memory %llu; %llu resolve failures, %llu stale, %llu label overlaps, %llu read an earlier dispatch's output, %llu rejected by the device, %llu dropped because the argument re-read differed\n",
AgcDriver::ProfilePrint_nid_no_patch("[groupcap] queue 0x%x (10 s): %llu runs resolved %llu dispatches (%llu cache hits) in %.1f ms (max %.2f ms); %llu adopted %.2f ms after the resolve on average, max %.2f ms; mismatches: address %llu key %llu memory %llu; %llu resolve failures, %llu stale, %llu label overlaps, %llu read an earlier dispatch's output, %llu stopped at unknown writes, %llu rejected by the device, %llu dropped because the argument re-read differed\n",
queue, static_cast<unsigned long long>(stats.runs), static_cast<unsigned long long>(stats.resolved), static_cast<unsigned long long>(stats.resolvedHits), stats.resolveMs, stats.resolveMaxMs,
static_cast<unsigned long long>(stats.adopted), stats.adopted != 0 ? stats.ageMs / static_cast<double>(stats.adopted) : 0.0, stats.ageMaxMs, static_cast<unsigned long long>(stats.addressMismatches), static_cast<unsigned long long>(stats.keyMismatches), static_cast<unsigned long long>(stats.memoryMismatches),
static_cast<unsigned long long>(stats.failures), static_cast<unsigned long long>(stats.stale), static_cast<unsigned long long>(stats.labelOverlaps), static_cast<unsigned long long>(stats.writtenBefore), static_cast<unsigned long long>(stats.deviceRejects), static_cast<unsigned long long>(stats.argumentMismatches));
static_cast<unsigned long long>(stats.failures), static_cast<unsigned long long>(stats.stale), static_cast<unsigned long long>(stats.labelOverlaps), static_cast<unsigned long long>(stats.writtenBefore), static_cast<unsigned long long>(stats.unknownWrites), static_cast<unsigned long long>(stats.deviceRejects), static_cast<unsigned long long>(stats.argumentMismatches));
stats = GroupCaptureStats{};
stats.reported = now;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void Driver::noteCopyWriter(std::uint64_t program, std::uint64_t begin, std::uin
knownValueEntries.fetch_add(1, std::memory_order_relaxed);
}
std::lock_guard lock(writtenBuffersMutex);
writtenBuffers.push_back({program, begin, end, ++writtenBufferSerial, queue, false, std::move(known), generation});
writtenBuffers.push_back({known || !copyKnownValues() ? program : 0, begin, end, ++writtenBufferSerial, queue, false, std::move(known), generation});
while (writtenBuffers.size() > WrittenBufferRing) writtenBuffers.pop_front();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ void Driver::noteWrittenBuffers(std::uint64_t program, std::uint32_t queue, cons
}

std::vector<std::pair<std::uint64_t, std::uint64_t>> DispatchWriteRanges(const ShaderRecompiler::RecompileResult& compiled, bool& bounded) {
bounded = compiled.bdaAbiVersion == 0;
bounded = compiled.bdaAbiVersion == 0 || std::any_of(compiled.bindings.begin(), compiled.bindings.end(), [](const ShaderRecompiler::DescriptorBinding& binding) { return binding.role == ShaderRecompiler::DescriptorRole::FaultBuffer; });
std::vector<std::pair<std::uint64_t, std::uint64_t>> ranges;
constexpr std::uint64_t UnknownImageBytes = 64ull * 1024 * 1024;
for (const auto& binding : compiled.bindings) {
if (binding.role == ShaderRecompiler::DescriptorRole::Gds || binding.role == ShaderRecompiler::DescriptorRole::BdaPagetable || binding.role == ShaderRecompiler::DescriptorRole::FaultBuffer) bounded = false;
if (binding.role == ShaderRecompiler::DescriptorRole::Gds || binding.role == ShaderRecompiler::DescriptorRole::BdaPagetable) bounded = false;
if (binding.role != ShaderRecompiler::DescriptorRole::GuestImages || binding.kind != ShaderRecompiler::DescriptorKind::StorageImage) continue;
for (std::uint32_t element = 0; element < binding.count; ++element) {
const bool written = element >= binding.imageWritten.size() || binding.imageWritten[element];
Expand Down
Loading
Loading