Skip to content
Merged
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,7 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_pipelined_dispatch_item PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_queued_index_read_order PROPERTIES TIMEOUT 60)

foreach(agcTest IN ITEMS agc_spirv_narrow_constant_store_tests agc_spirv13_image_tests agc_spirv_optimizer_tests agc_driver_constant_store_tests)
if(NOT TARGET ${agcTest})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,24 @@ class DrawPipeline {
static void FollowEpoch(std::uint64_t token);
static unsigned Committers();

void Enqueue(Commit commit, std::vector<Range> writes, std::uint64_t labelAddress = 0, std::vector<std::byte> labelBytes = {});
void Enqueue(Commit commit, std::vector<Range> writes, std::uint64_t labelAddress = 0, std::vector<std::byte> labelBytes = {}, std::vector<Range> reads = {});
void Drain(DrainReason reason, std::uint32_t opcode = 0x100);
void Stop();
static std::uint64_t DrainRequestsByOpcode(std::uint32_t opcode);
bool Busy() const { return outstanding.load(std::memory_order_acquire) != 0; }
std::size_t Outstanding() const { return outstanding.load(std::memory_order_acquire); }
bool Overlaps(std::uint64_t address, std::size_t bytes);
std::optional<std::uint64_t> PendingLabel(std::uint64_t address, std::size_t bytes);
static bool HeldReads(std::uint64_t address, std::size_t bytes);
void ReleaseRunningReads();

private:
struct Item {
Commit commit;
std::vector<Range> writes;
std::uint64_t labelAddress = 0;
std::vector<std::byte> labelBytes;
std::vector<Range> reads;
};
DrawPipeline() = default;
void run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAWWRITERANGES_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAWWRITERANGES_HPP

#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Shaders.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include <cstdint>
Expand All @@ -13,6 +14,7 @@ namespace AgcDriver::DriverDetail {
std::vector<std::pair<std::uint64_t, std::uint64_t>> DrawWriteRanges(const Graphics::State& graphics, std::span<const Graphics::CompiledShader> stages, bool exact);
std::vector<std::pair<std::uint64_t, std::uint64_t>> DispatchWriteRanges(const ShaderRecompiler::RecompileResult& compiled, bool& bounded);
bool ExactDrawWrites();
std::vector<std::pair<std::uint64_t, std::uint64_t>> QueuedDrawReadRanges(const Pm4::DrawParameters& draw, std::span<const Graphics::CompiledShader> stages);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Memory/DrawWriteRanges.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
Expand Down Expand Up @@ -602,13 +603,14 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
const auto commitQueue = submission.queue;
const auto commitPacket = (packet[0] >> 8u) & 0xffu;
const auto epoch = DrawPipeline::EpochToken().load(std::memory_order_relaxed);
auto reads = QueuedDrawReadRanges(drawParameters, stages);
auto commit = [this, localDevice = std::move(localDevice), decode = std::move(decode), drawParameters, stages = std::move(stages), snapshots = std::move(snapshots), recipe = std::move(recipe), recipeStages = std::move(recipeStages), drawKey, commitQueue, commitPacket, epoch, programs = std::move(programs), results = std::move(results), memory = std::move(memory), linked = std::move(linked), stageCaptures = std::move(stageCaptures), matched = std::move(matched), matchedRegions = std::move(matchedRegions), fresh = std::move(fresh), decodeReads = std::move(decodeReads), shaderMemory = std::move(shaderMemory), entry = std::move(entry), dataEntry = std::move(dataEntry), compiledStages = std::move(compiledStages), stageData = std::move(stageData)] {
DrawPipeline::FollowEpoch(epoch);
GuestMemory::SetCurrentPacket(commitPacket, commitQueue);
commitDraw(localDevice, commitQueue, decode->state, drawParameters, stages, snapshots, recipe, recipeStages, drawKey);
};
auto held = std::make_shared<decltype(commit)>(std::move(commit));
DrawPipeline::Queue0().Enqueue([held] { (*held)(); }, std::move(writes));
DrawPipeline::Queue0().Enqueue([held] { (*held)(); }, std::move(writes), 0, {}, std::move(reads));
phaseTiming.Phase(DrawRowLockWait);
timing.Mark("draw_enqueued");
return drawn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ std::uint64_t elapsedNs(std::chrono::steady_clock::time_point since) {
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - since).count());
}

std::atomic<std::uint64_t> heldReadRanges{0};
std::atomic<std::uint64_t> heldReadQueries{0};
std::atomic<std::uint64_t> heldReadHits{0};

}

DrawPipeline& DrawPipeline::Queue0() {
Expand Down Expand Up @@ -73,7 +77,7 @@ void DrawPipeline::rethrowFailure() {
std::rethrow_exception(error);
}

void DrawPipeline::Enqueue(Commit commit, std::vector<Range> writes, std::uint64_t labelAddress, std::vector<std::byte> labelBytes) {
void DrawPipeline::Enqueue(Commit commit, std::vector<Range> writes, std::uint64_t labelAddress, std::vector<std::byte> labelBytes, std::vector<Range> reads) {
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
std::unique_lock lock(mutex);
rethrowFailure();
Expand All @@ -93,7 +97,8 @@ void DrawPipeline::Enqueue(Commit commit, std::vector<Range> writes, std::uint64
}
depthSum += items.size();
++enqueued;
items.push_back({std::move(commit), std::move(writes), labelAddress, std::move(labelBytes)});
heldReadRanges.fetch_add(reads.size(), std::memory_order_relaxed);
items.push_back({std::move(commit), std::move(writes), labelAddress, std::move(labelBytes), std::move(reads)});
outstanding.fetch_add(1, std::memory_order_release);
if (!thread.joinable()) {
thread = std::thread([this] { run(); });
Expand Down Expand Up @@ -126,7 +131,10 @@ void DrawPipeline::report(std::chrono::steady_clock::time_point now) {
const auto perCommit = commits == 0 ? 0.0 : static_cast<double>(commitNs) / 1e3 / static_cast<double>(commits);
const auto perDraw = enqueued == 0 ? 0.0 : static_cast<double>(fullWaitNs) / 1e3 / static_cast<double>(enqueued);
const auto queued = enqueued == 0 ? 0.0 : static_cast<double>(depthSum) / static_cast<double>(enqueued);
std::fprintf(stderr, "[draw] pipelined (10 s, depth %zu): %llu commits at %.1f us each on the committer, %llu failed; %llu enqueued behind %.2f on average, waiting %.1f us per draw for room; drains:%s\n", Depth(), static_cast<unsigned long long>(commits), perCommit, static_cast<unsigned long long>(commitErrors), static_cast<unsigned long long>(enqueued), queued, perDraw, drainText.c_str());
char reads[64];
std::snprintf(reads, sizeof(reads), " %llu read ranges, %llu stores held", static_cast<unsigned long long>(heldReadRanges.load(std::memory_order_relaxed)), static_cast<unsigned long long>(heldReadHits.exchange(0, std::memory_order_relaxed)));
drainText += reads;
std::fprintf(stderr, "[draw] pipelined (10 s, depth %zu): %llu commits at %.1f us each on the committer, %llu failed; %llu enqueued behind %.2f on average, waiting %.1f us per draw for room;%s; drains:%s\n", Depth(), static_cast<unsigned long long>(commits), perCommit, static_cast<unsigned long long>(commitErrors), static_cast<unsigned long long>(enqueued), queued, perDraw, reads, drainText.c_str());
commits = commitNs = commitErrors = enqueued = fullWaitNs = depthSum = 0;
drains = {};
drainWaitNs = {};
Expand Down Expand Up @@ -211,6 +219,27 @@ std::uint64_t DrawPipeline::DrainRequestsByOpcode(std::uint32_t opcode) {
return Queue0().drainRequests[std::min<std::uint32_t>(opcode, 0x100)].load(std::memory_order_relaxed);
}

bool DrawPipeline::HeldReads(std::uint64_t address, std::size_t bytes) {
if (bytes == 0 || std::getenv("APS5_NO_QUEUED_DRAW_READ_ORDER") != nullptr) return false;
const auto end = address + bytes;
auto& pipeline = Queue0();
std::lock_guard lock(pipeline.mutex);
heldReadQueries.fetch_add(1, std::memory_order_relaxed);
for (const auto& item : pipeline.items) {
for (const auto& [begin, limit] : item.reads) {
if (address >= limit || begin >= end) continue;
heldReadHits.fetch_add(1, std::memory_order_relaxed);
return true;
}
}
return false;
}

void DrawPipeline::ReleaseRunningReads() {
std::lock_guard lock(mutex);
if (!items.empty()) items.front().reads.clear();
}

std::optional<std::uint64_t> DrawPipeline::PendingLabel(std::uint64_t address, std::size_t bytes) {
if (bytes == 0 || bytes > 8) return std::nullopt;
const auto end = address + bytes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp"
#include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp"
#include "prx/libSceAgcDriver/Graphics/include/VertexInput.hpp"
#include "Optimization/ShaderStageInputInfo.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <stdexcept>

namespace AgcDriver::DriverDetail {

Expand Down Expand Up @@ -156,6 +159,40 @@ std::vector<std::pair<std::uint64_t, std::uint64_t>> Driver::drawWriteRanges(con
return DrawWriteRanges(graphics, stages, ExactDrawWrites());
}

std::vector<std::pair<std::uint64_t, std::uint64_t>> QueuedDrawReadRanges(const Pm4::DrawParameters& draw, std::span<const Graphics::CompiledShader> stages) {
std::vector<std::pair<std::uint64_t, std::uint64_t>> ranges;
const auto add = [&](std::uint64_t begin, std::uint64_t bytes) {
if (begin == 0 || bytes == 0 || bytes > std::numeric_limits<std::uint64_t>::max() - begin) return;
ranges.emplace_back(begin, begin + bytes);
};
if (draw.indexed && draw.indexSize != 0 && draw.indexCount != 0) add(draw.indexAddress, static_cast<std::uint64_t>(draw.indexCount) * draw.indexSize);
if (stages.empty()) return ranges;
for (const auto& stage : stages) {
if (stage.program == nullptr) continue;
for (const auto& binding : stage.program->bindings) {
if (binding.role != ShaderRecompiler::DescriptorRole::GuestBuffers || binding.guestDescriptor.size() < static_cast<std::size_t>(binding.count) * 4u) continue;
for (std::uint32_t element = 0; element < binding.count; ++element) {
const auto* words = binding.guestDescriptor.data() + static_cast<std::size_t>(element) * 4u;
const ShaderRecompiler::ShaderBufferResource descriptor{{words[0], words[1], words[2], words[3]}};
add(descriptor.Base48(), descriptor.GetSize());
}
}
}
if (stages.empty() || stages.front().program == nullptr) return ranges;
for (const auto& attribute : stages.front().program->vertexAttributes) {
if (Graphics::NullVertexDescriptor(attribute) || Graphics::VertexFetchOutOfRange(attribute)) continue;
const auto address = attribute.resource.fields[0] | (static_cast<std::uint64_t>(attribute.resource.fields[1] & 0xffffu) << 32u);
std::uint64_t bytes = 0;
try {
bytes = Graphics::VertexBufferExtent(attribute);
} catch (const std::runtime_error&) {
continue;
}
add(address, bytes);
}
return ranges;
}

std::optional<WrittenBuffer> Driver::newestWriterLocked(std::uint64_t begin, std::uint64_t end) const {
for (auto it = writtenBuffers.rbegin(); it != writtenBuffers.rend(); ++it) {
if (begin < it->end && it->begin < end) return *it;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ bool Driver::enqueueDmaPacket(std::span<const std::uint32_t> packet, std::uint32
const auto copy = Pm4::DecodeMemoryCopy(packet);
if (!copy.has_value() || copy->destination % 4 != 0 || copy->bytes % 4 != 0 || !GuestMemory::Accessible(reinterpret_cast<const void*>(copy->source), copy->bytes) || !GuestMemory::Accessible(reinterpret_cast<const void*>(copy->destination), copy->bytes, true)) return false;
DrawPipeline::Queue0().Enqueue([this, queue, copy = *copy] {
DrawPipeline::Queue0().ReleaseRunningReads();
GuestMemory::SetCurrentPacket(0x50, queue);
constexpr std::size_t gpuStoreLimit = 65536;
if (copy.bytes > gpuStoreLimit) {
Expand All @@ -326,7 +327,7 @@ bool Driver::enqueueDmaPacket(std::span<const std::uint32_t> packet, std::uint32
std::vector<std::byte> bytes(copy.bytes);
GuestMemory::Read(copy.source, bytes, 1);
commitDmaStore(queue, copy.destination, bytes);
}, {{copy->destination, copy->destination + copy->bytes}});
}, {{copy->destination, copy->destination + copy->bytes}}, 0, {}, {{copy->source, copy->source + copy->bytes}});
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ void Driver::waitMemory(std::span<const std::uint32_t> packet, std::uint32_t que
~BlockedWait() { driver.noteWaitBlocked(queue, 0, false); }
} blocked{*this, queue};
static const bool pauseSpin = std::getenv("APS5_NO_PAUSE_SPIN") == nullptr;
static const bool storeHeldResults = std::getenv("APS5_NO_QUEUED_DRAW_READ_ORDER") == nullptr;
const auto spinLimit = queue == 0 ? std::chrono::microseconds(1500) : std::chrono::microseconds(100);
const auto spinStart = std::chrono::steady_clock::now();
bool spinning = pauseSpin;
Expand All @@ -350,6 +351,7 @@ void Driver::waitMemory(std::span<const std::uint32_t> packet, std::uint32_t que
}
CheckFailure();
if (pollService(!spinning)) return;
if (storeHeldResults && !spinning && (polls & 31u) == 0) GuestMemory::FlushGpuWrites(awaited, awaitedBytes);
noteAwaitingTitle(queue, awaited);
if (requireMemory && (polls & 15u) == 0) {
std::unique_lock settleLock(GuestMemory::GpuMutex(), std::try_to_lock);
Expand Down
8 changes: 7 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "prx/libSceAgcDriver/Execution/include/AsyncLog.hpp"
#include "prx/libSceAgcDriver/Execution/include/ProfileOutput.hpp"
#include "prx/libSceAgcDriver/Execution/include/CaptureTrace.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp"
#include "prx/libSceAgcDriver/Graphics/include/SlimMutex.hpp"
#include "prx/libc/include/General.hpp"
Expand Down Expand Up @@ -94,6 +95,7 @@ std::atomic<std::uint64_t> partialUploads{0}, partialUploadBytes{0}, partialWrit
// writeBackLayers), and pending units a DCC clear -> uncompressed key flip kept as the texels
// (Refresh).
std::atomic<std::uint64_t> pretestSkipped{0}, unregisteredDropped{0}, keyFlipKept{0};
std::atomic<std::uint64_t> queuedReadHeld{0};
// Images stored by a FlushPending after a hook skip of theirs (AccessKeptByCpu), of which by the
// hook for the read site of the last skip; images evictStale dropped (the [hooksync] and [storage] lines).
std::atomic<std::uint64_t> flushedAfterSkip{0}, flushedAfterSkipSameSite{0}, staleEvicted{0};
Expand Down Expand Up @@ -140,7 +142,7 @@ void reportStorageTraffic(StorageTraffic& traffic) {
const auto take = [](std::atomic<std::uint64_t>& counter) { return static_cast<unsigned long long>(counter.exchange(0, std::memory_order_relaxed)); };
const auto partialUploadCount = take(partialUploads), partialWriteBackCount = take(partialWriteBacks);
const auto uploadMiB = take(partialUploadBytes) / 1048576.0, writeBackMiB = take(partialWriteBackBytes) / 1048576.0;
AgcDriver::ProfilePrint_nid_no_patch( "[storage] uploads by path (count/MiB, 10 s):%s; %llu write-backs GPU-direct, %llu stored nothing; block units: partial uploads %llu/%.1f, partial write-backs %llu/%.1f, %llu widened to every pending unit, units dropped %llu, superseded %llu, dropped unregistered %llu, kept over a key flip %llu%s, pretest skipped %llu, evicted stale %llu\n", line.c_str(), static_cast<unsigned long long>(traffic.directWriteBacks), take(emptyWriteBacks), partialUploadCount, uploadMiB, partialWriteBackCount, writeBackMiB, take(coalescedWriteBacks), take(unitsDropped), take(unitsSuperseded), take(unregisteredDropped), take(keyFlipKept), ShadowReport().c_str(), take(pretestSkipped), take(staleEvicted));
AgcDriver::ProfilePrint_nid_no_patch( "[storage] uploads by path (count/MiB, 10 s):%s; %llu write-backs GPU-direct, %llu stored nothing; block units: partial uploads %llu/%.1f, partial write-backs %llu/%.1f, %llu widened to every pending unit, units dropped %llu, superseded %llu, dropped unregistered %llu, kept over a key flip %llu%s, pretest skipped %llu, held for a queued draw read %llu, evicted stale %llu\n", line.c_str(), static_cast<unsigned long long>(traffic.directWriteBacks), take(emptyWriteBacks), partialUploadCount, uploadMiB, partialWriteBackCount, writeBackMiB, take(coalescedWriteBacks), take(unitsDropped), take(unitsSuperseded), take(unregisteredDropped), take(keyFlipKept), ShadowReport().c_str(), take(pretestSkipped), take(queuedReadHeld), take(staleEvicted));
traffic.writeBacks.clear();
traffic.uploadReasons.clear();
traffic.directWriteBacks = 0;
Expand Down Expand Up @@ -3640,6 +3642,10 @@ bool StorageTexture::pendingUnitInside(std::uint64_t address, std::size_t bytes)

bool StorageTexture::FlushPending(std::uint64_t address, std::size_t bytes, const StorageTexture* except, const char* reason, PublishScope scope, bool* published) {
if (published != nullptr) *published = false;
if (AgcDriver::DriverDetail::DrawPipeline::HeldReads(address, bytes)) {
queuedReadHeld.fetch_add(1, std::memory_order_relaxed);
return false;
}
// The images stay alive across the scan: their last owner may be a batch's kept list, which
// another thread releases outside the GPU mutex once the batch completed.
std::vector<std::shared_ptr<StorageTexture>> flush;
Expand Down
Loading
Loading