Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3b34d1f
perf(agc): pipeline queue-0 draw commits behind the packet worker (AP…
MichonGoddijn231849 Oct 15, 2026
a18ab8c
fix(agc): a null storage image in a draw's written set writes nothing
MichonGoddijn231849 Oct 13, 2026
2cd5c75
fix(agc): a pipelined release with INT_SEL 3 raises no interrupt
MichonGoddijn231849 Oct 13, 2026
a39261b
fix(agc): a CB metadata pass drains the draw pipeline before it runs
Raghav314 Oct 14, 2026
7b40c05
fix(agc): entry validation drains queued draws that write its runs
Raghav314 Oct 14, 2026
dc027cc
fix(agc): note an HTILE depth clear after the pipeline drain
Raghav314 Oct 14, 2026
6ad0531
fix(agc): a predicated packet drains queued writes to its predicate b…
eliyarson Oct 14, 2026
084d1fe
fix(agc): a label packet behind a deferred label is not enqueued ahea…
eliyarson Oct 14, 2026
a719fdc
fix(agc): a pipelined fill reads its inputs after queued writes and n…
oneandonlydean Oct 14, 2026
3826c13
fix(agc): a pipelined memory-value or buffer-pattern fill reads its s…
adriadam10 Oct 15, 2026
bacd50a
fix(agc): a failed pipelined commit is rethrown like an inline draw e…
adriadam10 Oct 15, 2026
5d5aeab
fix(agc): a queued draw's writes include its color targets' CMASK
adriadam10 Oct 15, 2026
4051ee1
docs(architecture): describe the queue-0 draw pipeline
adriadam10 Oct 15, 2026
ee0a188
fix(agc): an emptied buffer pool size class is erased again
adriadam10 Oct 15, 2026
5c09de1
fix(agc): a failed pipelined commit stops queue 0 like a failed worker
adriadam10 Oct 15, 2026
62e5bc2
fix(agc): a pipelined draw keeps its patched and freshly materialized…
adriadam10 Oct 15, 2026
1df509b
test(agc): run the failure and draw cache reuse tests with pipelined …
adriadam10 Oct 15, 2026
43d7c11
fix(agc): a pipelined draw owns its ShaderMemory through a unique_ptr…
adriadam10 Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawCapture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawDecode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawLookup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawPipeline.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawPrecheck.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawRegisterKey.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawTiming.cpp
Expand Down Expand Up @@ -1642,8 +1643,10 @@ if(BUILD_TESTING)
add_test(NAME agc_descriptor_pool_reset_failure COMMAND agc_driver_graphics_tests --descriptor-pool-reset-failure)
add_test(NAME agc_driver_address_space_teardown COMMAND agc_driver_graphics_tests --address-space-teardown)
set_tests_properties(agc_driver_address_space_teardown PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_driver_pm4_failure_pipelined COMMAND agc_driver_pm4_tests failure)
set_tests_properties(agc_driver_pm4_failure_pipelined PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=1" SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_pm4_failure agc_descriptor_pool_reset_failure agc_driver_address_space_teardown PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_pm4_failure agc_driver_pm4_failure_pipelined agc_descriptor_pool_reset_failure agc_driver_address_space_teardown PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
if(NOT WIN32)
Expand All @@ -1664,10 +1667,12 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_draw_cache_reuse_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_GPL=1" SKIP_RETURN_CODE 77)
add_test(NAME agc_driver_draw_cache_reuse_verify COMMAND agc_driver_draw_cache_reuse_tests)
set_tests_properties(agc_driver_draw_cache_reuse_verify PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_VERIFY_DRAW_DATA_HITS=1" SKIP_RETURN_CODE 77)
set_tests_properties(agc_driver_draw_cache_reuse agc_driver_draw_cache_reuse_off agc_driver_draw_cache_reuse_verify PROPERTIES FAIL_REGULAR_EXPRESSION "FATAL:")
add_test(NAME agc_driver_draw_cache_reuse_pipelined COMMAND agc_driver_draw_cache_reuse_tests)
set_tests_properties(agc_driver_draw_cache_reuse_pipelined PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=1;APS5_VERIFY_DRAW_RECIPE=1;APS5_VERIFY_REGISTER_KEYS=1" SKIP_RETURN_CODE 77)
set_tests_properties(agc_driver_draw_cache_reuse agc_driver_draw_cache_reuse_off agc_driver_draw_cache_reuse_verify agc_driver_draw_cache_reuse_pipelined PROPERTIES FAIL_REGULAR_EXPRESSION "FATAL:")
set_property(TEST agc_driver_draw_cache_reuse agc_driver_draw_cache_reuse_off APPEND PROPERTY ENVIRONMENT "APS5_VERIFY_DRAW_RECIPE=1" "APS5_VERIFY_REGISTER_KEYS=1")
if(WIN32)
set_tests_properties(agc_driver_draw_cache_reuse_off agc_driver_draw_cache_reuse_verify PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_draw_cache_reuse_off agc_driver_draw_cache_reuse_verify agc_driver_draw_cache_reuse_pipelined PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAW_DRAWPIPELINE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAW_DRAWPIPELINE_HPP

#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <exception>
#include <functional>
#include <mutex>
#include <optional>
#include <thread>
#include <utility>
#include <vector>

namespace AgcDriver::DriverDetail {

class DrawPipeline {
public:
using Commit = std::function<void()>;
using Range = std::pair<std::uint64_t, std::uint64_t>;
enum class DrainReason : std::uint8_t { Packet, Flush, Labels, Capture, Indirect, Submission, Count };

static DrawPipeline& Queue0();
static std::size_t Depth();
static bool& Active();
static std::atomic<std::uint64_t>& EpochToken();
static void FollowEpoch(std::uint64_t token);

void Enqueue(Commit commit, std::vector<Range> writes, std::uint64_t labelAddress = 0, std::vector<std::byte> labelBytes = {});
void Drain(DrainReason reason, std::uint32_t opcode = 0x100);
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);

private:
struct Item {
Commit commit;
std::vector<Range> writes;
std::uint64_t labelAddress = 0;
std::vector<std::byte> labelBytes;
};
DrawPipeline() = default;
void run();
void rethrowFailure();
void report(std::chrono::steady_clock::time_point now);

std::mutex mutex;
std::condition_variable wake;
std::condition_variable idle;
std::deque<Item> items;
std::exception_ptr failure;
std::atomic<std::size_t> outstanding{0};
std::uint32_t idleWaiters = 0;
bool committerWaiting = false;
bool stopped = false;
std::thread thread;
std::uint64_t commits = 0;
std::uint64_t commitNs = 0;
std::uint64_t commitErrors = 0;
std::uint64_t enqueued = 0;
std::uint64_t fullWaitNs = 0;
std::array<std::uint64_t, static_cast<std::size_t>(DrainReason::Count)> drains{};
std::array<std::uint64_t, static_cast<std::size_t>(DrainReason::Count)> drainWaitNs{};
std::array<std::uint64_t, 257> drainOpcodes{};
std::uint64_t depthSum = 0;
std::chrono::steady_clock::time_point lastReport = std::chrono::steady_clock::now();
};

}

#endif
16 changes: 16 additions & 0 deletions core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class Driver {
void enqueue(Submission submission);
void noteHeldAtSubmit(Submission& submission, std::size_t cursor);
static void forgetUnfinishedWrites(QueueWorker& worker, const Submission& submission);
static void forgetUnfinishedWrites(QueueWorker& worker, std::span<const std::uint64_t> labelWrites);
static bool& completionDeferred();
void deferCompletion(const Submission& submission);
void submitOpenWork();
void completeSubmission(std::uint64_t serial, std::uint64_t received, std::span<const std::uint64_t> labelWrites);
static bool waitFree(const Submission& submission);
bool queue0Before(std::uint64_t received) const;
bool orderReleased(std::uint32_t queue, std::uint64_t received) const;
Expand Down Expand Up @@ -207,6 +212,13 @@ class Driver {
static void appendWrittenRanges(const ShaderRecompiler::RecompileResult& compiled, std::vector<std::pair<std::uint64_t, std::uint64_t>>& ranges);
void noteForeignWriter(std::uint64_t begin, std::uint64_t end, std::uint32_t queue);
void noteDrawWriters(std::span<const Graphics::CompiledShader> stages, std::uint32_t queue);
static std::vector<std::pair<std::uint64_t, std::uint64_t>> drawWriteRanges(const Graphics::State& graphics, std::span<const Graphics::CompiledShader> stages);
void commitDraw(std::shared_ptr<VulkanDevice> localDevice, std::uint32_t queue, const Graphics::State& graphics, const Pm4::DrawParameters& drawParameters, std::span<const Graphics::CompiledShader> stages, std::span<const Graphics::GuestMemorySnapshot> snapshots, const std::shared_ptr<const DrawRecipe>& recipe, const std::vector<std::shared_ptr<DispatchVariant>>& recipeStages, std::uint64_t drawKey);
bool enqueueLabelPacket(std::span<const std::uint32_t> packet, std::uint32_t opcode, std::uint32_t queue);
bool enqueueDmaPacket(std::span<const std::uint32_t> packet, std::uint32_t opcode, std::uint32_t queue, const QueueState& state);
void commitDmaStore(std::uint32_t queue, std::uint64_t address, std::span<const std::byte> bytes);
void commitLabel(std::uint32_t queue, std::uint64_t address, std::span<const std::byte> bytes, bool endOfPipeInterrupt);
void submitDueAfterCommit(VulkanDevice* localDevice);
std::optional<WrittenBuffer> newestWriterLocked(std::uint64_t begin, std::uint64_t end) const;
std::optional<WrittenBuffer> newestWriter(std::uint64_t begin, std::uint64_t end);
std::string describeWriters(std::uint64_t begin, std::uint64_t end);
Expand Down Expand Up @@ -264,6 +276,9 @@ class Driver {
static Graphics::Recorder::LateStatistics& lateCountsSeen();
static EpochBumps& epochBumps();
static void bumpEpoch(std::uint64_t EpochBumps::*counter);
static std::chrono::steady_clock::time_point& lastEpochBump();
static std::deque<const std::uint32_t*>& releasedTails();
void noteReleasedTails(const Submission& submission);
static bool packetEpoch();
static bool labelTryEachPacket();
static std::chrono::microseconds labelFlushDeadline();
Expand Down Expand Up @@ -372,6 +387,7 @@ class Driver {

std::uint32_t idleWaiters = 0;
std::uint64_t queue0Executing = 0;
std::deque<std::uint64_t> queue0Uncommitted;
std::atomic<std::uint32_t> orderHolders{0};
std::atomic<std::uint32_t> runningWorkers{0};
std::atomic<std::uint64_t> idleEpoch{0};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAWWRITERANGES_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAWWRITERANGES_HPP

#include "prx/libSceAgcDriver/Graphics/include/Shaders.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include <cstdint>
#include <span>
#include <utility>
#include <vector>

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);
bool ExactDrawWrites();

}

#endif
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "prx/libSceAgcDriver/Execution/include/ProfileOutput.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Memory/BufferCopy.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
Expand Down Expand Up @@ -76,17 +77,18 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
if (!partialLastGroup && matchesFillKernel(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))) return;
} else {
if (const auto htile = Graphics::HtileDepthClearAddress(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads); htile != 0) {
const auto threads = (packet[4] & 0x20u) != 0 ? static_cast<std::uint64_t>(packet[1]) : static_cast<std::uint64_t>(packet[1]) * compute.numThreads[0];
Graphics::NoteHtileDepthClear(htile, static_cast<std::size_t>(threads * 16u));
}
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;
return;
}
if (DrawPipeline::Active()) DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Packet, 0x15);
if (const auto htile = Graphics::HtileDepthClearAddress(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads); htile != 0) {
const auto threads = (packet[4] & 0x20u) != 0 ? static_cast<std::uint64_t>(packet[1]) : static_cast<std::uint64_t>(packet[1]) * compute.numThreads[0];
Graphics::NoteHtileDepthClear(htile, static_cast<std::size_t>(threads * 16u));
}
if (!partialLastGroup && indirectArguments == 0 && copyBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice, address)) {
pendingDispatchPhases().outcome = DispatchOutcome::CopyHle;
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>

Expand Down Expand Up @@ -78,6 +80,10 @@ bool Driver::syncPendingRuns(std::uint64_t program, std::uint32_t queue, const S

EntryOutcome Driver::validateVariant(std::uint64_t program, std::uint32_t queue, const DispatchVariant& variant, std::span<const ShaderRecompiler::MemoryRegion> regions, std::uint64_t& imagesFlushed, std::uint64_t& runsSynced, std::optional<SampledReadScope>& sampling, std::vector<std::pair<std::uint32_t, std::uint32_t>>* live) {
if (live != nullptr) live->clear();
if (DrawPipeline::Active()) {
auto& pipeline = DrawPipeline::Queue0();
if (pipeline.Busy() && std::any_of(variant.runs.begin(), variant.runs.end(), [&](const auto& run) { return pipeline.Overlaps(run.first, static_cast<std::size_t>(run.second - run.first)); })) pipeline.Drain(DrawPipeline::DrainReason::Capture);
}
const bool masked = live != nullptr && !variant.dataPositions.empty();
const DataMask mask{variant.dataPositions, live};
PendingView pending;
Expand Down
Loading
Loading