Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ std::shared_ptr<const ShaderSnapshot> RegisteredProgram(const ShaderRegistry& re
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot);
void SubmitRegistrationPreparation(std::function<void()> task, const PreparedShaders* prepared = nullptr);
void PrioritizeRegistrationPreparation(PreparedShaders& prepared);
void AwaitRegistrationPreparation();
void SubmitLibraryPrebuild(std::function<void()> task);
void CloseRegistrationPreparation();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ void Driver::Submit(const Packet* packet, std::uint32_t queue) {
if (APS5_ENABLE_TIMING_LOG) submission.validatedAt = std::chrono::steady_clock::now();
waitForFlipRoom(submission);
holdFlipBehindWorker(submission);
AwaitRegistrationPreparation();
if (APS5_ENABLE_TIMING_LOG) submission.roomReadyAt = std::chrono::steady_clock::now();
static const bool trace = std::getenv("APS5_TRACE_GPU") != nullptr;
if (trace) std::fprintf(stderr, "[gpu] %.1f submit queue=0x%x dwords=%zu at %p\n", TraceMs(), queue, submission.commands.size(), static_cast<const void*>(descriptor.addr));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <set>
#include <condition_variable>
#include <cstdio>
#include <cstddef>
Expand Down Expand Up @@ -894,8 +895,10 @@ class RegistrationPreparePool {
{
std::unique_lock lock(mutex);
if (threads != 0 && !closed) {
if (prepared != nullptr && prepared->demanded.load()) tasks.push_front({std::move(task), prepared});
else tasks.push_back({std::move(task), prepared});
const auto sequence = ++submitted;
if (prepared != nullptr && prepared->demanded.load()) tasks.push_front({std::move(task), prepared, sequence});
else tasks.push_back({std::move(task), prepared, sequence});
unfinished.insert(sequence);
lock.unlock();
ready.notify_one();
return;
Expand All @@ -912,6 +915,12 @@ class RegistrationPreparePool {
std::rotate(tasks.begin(), found, std::next(found));
}

void AwaitSubmitted() {
std::unique_lock lock(mutex);
const auto ticket = submitted;
idle.wait(lock, [&] { return unfinished.empty() || *unfinished.begin() > ticket; });
}

void Close() {
std::unique_lock lock(mutex);
closed = true;
Expand Down Expand Up @@ -945,14 +954,16 @@ class RegistrationPreparePool {
pool.tasks.pop_front();
++pool.running;
}
const auto sequence = task.sequence;
task.function();
task = {};
bool drained = false;
{
std::lock_guard lock(pool.mutex);
--pool.running;
pool.unfinished.erase(sequence);
drained = pool.tasks.empty() && pool.running == 0;
if (drained) pool.idle.notify_all();
pool.idle.notify_all();
}
#ifdef __GLIBC__
if (drained) trimFreedMemory();
Expand All @@ -964,6 +975,7 @@ class RegistrationPreparePool {
struct Task {
std::function<void()> function;
const PreparedShaders* prepared = nullptr;
std::uint64_t sequence = 0;
};

std::mutex mutex;
Expand All @@ -972,6 +984,8 @@ class RegistrationPreparePool {
std::deque<Task> tasks;
unsigned threads = 0;
unsigned running = 0;
std::uint64_t submitted = 0;
std::set<std::uint64_t> unfinished;
bool closed = false;
};

Expand Down Expand Up @@ -1017,6 +1031,10 @@ void PrioritizeRegistrationPreparation(PreparedShaders& prepared) {
if (auto* pool = RegistrationPreparePool::Existing()) pool->Prioritize(prepared);
}

void AwaitRegistrationPreparation() {
if (auto* pool = RegistrationPreparePool::Existing()) pool->AwaitSubmitted();
}

void SubmitLibraryPrebuild(std::function<void()> task) {
RegistrationPreparePool::Prebuild().Submit(std::move(task));
}
Expand Down
26 changes: 26 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,31 @@ std::exception_ptr InjectedFailure() {
}
}

void AwaitedRegistrationPreparation() {
std::atomic<bool> ran{false};
AgcDriver::DriverDetail::SubmitRegistrationPreparation([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
ran.store(true, std::memory_order_release);
});
AgcDriver::DriverDetail::AwaitRegistrationPreparation();
Require(ran.load(std::memory_order_acquire), "a submission did not wait for the registration preparation queued before it");
std::atomic<bool> slow{false};
std::atomic<bool> fast{false};
AgcDriver::DriverDetail::SubmitRegistrationPreparation([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
slow.store(true, std::memory_order_release);
});
auto waiter = std::async(std::launch::async, [&] {
AgcDriver::DriverDetail::AwaitRegistrationPreparation();
return slow.load(std::memory_order_acquire);
});
std::this_thread::sleep_for(std::chrono::milliseconds(50));
AgcDriver::DriverDetail::SubmitRegistrationPreparation([&] { fast.store(true, std::memory_order_release); });
Require(waiter.get(), "a submission stopped waiting for an earlier preparation once a later one finished");
AgcDriver::DriverDetail::AwaitRegistrationPreparation();
Require(fast.load(std::memory_order_acquire), "the later preparation did not run");
}

void PendingRegistrationPreparation(AgcDriver::VulkanDevice& device) {
alignas(256) const std::array<std::uint32_t, 1> code{0xbf810000u};
std::array<std::uint32_t, 4> users{};
Expand Down Expand Up @@ -673,6 +698,7 @@ int main(int argc, char** argv) {
RegisteredVertexWorkgroup(device->Target());
FailureCapture(*device);
PendingRegistrationPreparation(*device);
AwaitedRegistrationPreparation();
Require(argc != 2 || std::string_view(argv[1]) != "--fail-before-registration", "injected failure before registration");
PrepareMultisampledStorage(*device);
device.reset();
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libSceAgcDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,4 @@
- [16-bit depth with stencil](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver) is stored as `D24_UNORM_S8_UINT`, or `D32_SFLOAT_S8_UINT`, on a device without `D16_UNORM_S8_UINT` as an attachment. Depth comparisons, clears and the depth image see the wider format's precision, so results at the 16-bit quantization boundary can differ from the console; the constant depth bias is scaled by 256 for D24, and a non-zero constant on the D32 float fallback throws. Not measured on console hardware.
- [Flip packets raise the graphics end-of-pipe event](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp) (libSceAgcDriver) - a flip delivers the queue's end-of-pipe event (`sceAgcDriverAddEqEvent`) once the work recorded before it completes, as a `RELEASE_MEM` with `INT_SEL` does. Minecraft (PPSA17221) registers the graphics event, never requests an interrupt in its releases, and waits on the event after every flip while holding its render lock, so without this it never presents a second frame. Whether the console raises the event from the flip itself or from an interrupt its flip sequence contains is not measured; a title that waits only on release interrupts sees one extra event per flip, with `data` counting it.
- [Host import windows and budget](../../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp) (libSceAgcDriver): a registered range larger than 64 MiB is imported one 64 MiB window at a time for buffer and fill accesses; an access across window edges imports the cells it touches, grown to the union of the range's imports it overlaps. An address-based build (`AcquireRegistered`) imports the range whole and replaces its windows. Live imports stay under a budget: `APS5_HOST_IMPORT_MIB`, else the larger of 6 GiB and half the physical memory, at most the largest host heap less 2 GiB (imports and type-3 mirrors share it; #167's rule). A new import beyond it retires the least recently used imports not used in the last 256 lookups; one that would not fit even then is refused before anything is retired, and imports made when a range is mapped never evict. A refused import is tried again at its next use. The 64 MiB window, the 256-lookup protection and the 2 GiB reserve are choices, not measured.
- [Registration preparation at submit](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) (libSceAgcDriver) - `Driver::Submit` waits for every registration-time shader preparation queued before the call, not only those of the shaders the commands use, so a submission behind a long queue of unrelated preparations waits for all of them on the submitting thread. Waiting there instead of in the queue worker keeps the GPU side of a submission short: a title that sees a submission take seconds treats the GPU as hung (Goat Simulator 3, PPSA04159, then stops submitting and its garbage collector fails a 10 s timeout). On the console shaders are compiled ahead of time, so neither wait exists; how long a title tolerates a slow submit call is not known.
Loading