mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
Merge pull request #5808 from Ondra97/codex/feat-configurable-trace-capacity
feat(agc): configure trace event buffers
This commit is contained in:
4 files changed
+42
-1
No files matched your search
@@ -128,8 +128,20 @@ class TraceThread {
|
||||
public:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
static constexpr std::size_t DefaultCapacity = 262144;
|
||||
static constexpr std::size_t MaximumCapacity = 4194304;
|
||||
static constexpr Clock::duration MinimumSlice = std::chrono::microseconds(20);
|
||||
|
||||
static std::size_t ParseCapacity(const char* events) {
|
||||
if (events == nullptr) return DefaultCapacity;
|
||||
const std::string_view text = events;
|
||||
std::size_t capacity = 0;
|
||||
const auto end = text.data() + text.size();
|
||||
const auto [last, error] = std::from_chars(text.data(), end, capacity);
|
||||
if (text.empty() || error != std::errc{} || last != end || capacity == 0 || capacity > MaximumCapacity)
|
||||
throw std::invalid_argument("APS5_TRACE_EVENTS=" + std::string(text) + " is not a whole number from 1 through " + std::to_string(MaximumCapacity));
|
||||
return capacity;
|
||||
}
|
||||
|
||||
struct Slice {
|
||||
const char* scope;
|
||||
const char* stage;
|
||||
|
||||
@@ -138,6 +138,7 @@ double seconds(Clock::duration elapsed) {
|
||||
void PerformanceTrace::BindThread(const char* name, std::size_t capacity) {
|
||||
if (!Configured() || local != nullptr) return;
|
||||
const bool windowed = std::getenv("APS5_TRACE_FRAMES") != nullptr || std::getenv("APS5_TRACE_SECONDS") != nullptr;
|
||||
if (windowed && capacity == TraceThread::DefaultCapacity) capacity = TraceThread::ParseCapacity(std::getenv("APS5_TRACE_EVENTS"));
|
||||
auto bound = std::make_unique<TraceThread>(name, presentCount, windowed ? capacity : 0, buffering);
|
||||
auto& state = session();
|
||||
std::lock_guard lock(state.mutex);
|
||||
|
||||
@@ -60,6 +60,31 @@ void BufferCap() {
|
||||
Check(snapshot.totals.size() == 1 && snapshot.totals[0].count == 3 && snapshot.totals[0].length == microseconds(90), "totals missed a slice past the cap");
|
||||
}
|
||||
|
||||
void CapacitySettings() {
|
||||
Check(TraceThread::ParseCapacity(nullptr) == TraceThread::DefaultCapacity, "the default event capacity changed");
|
||||
Check(TraceThread::ParseCapacity("1") == 1, "the smallest event capacity was rejected");
|
||||
Check(TraceThread::ParseCapacity("3145728") == 3145728, "a larger event capacity was rejected");
|
||||
Check(TraceThread::ParseCapacity("4194304") == TraceThread::MaximumCapacity, "the maximum event capacity was rejected");
|
||||
std::atomic<std::uint64_t> presents{1};
|
||||
const auto requested = std::to_string(TraceThread::DefaultCapacity + 1);
|
||||
TraceThread thread("queue worker 0x0", presents, TraceThread::ParseCapacity(requested.c_str()));
|
||||
const auto origin = Clock::now();
|
||||
for (std::size_t index = 0; index <= TraceThread::DefaultCapacity; ++index)
|
||||
thread.Add("Driver.Packet", nullptr, origin, origin + microseconds(30));
|
||||
auto snapshot = thread.Collect();
|
||||
Check(snapshot.slices.size() == TraceThread::DefaultCapacity + 1 && !snapshot.dropped, "the configured buffer stopped at the default capacity");
|
||||
thread.Add("Driver.Packet", nullptr, origin, origin + microseconds(30));
|
||||
snapshot = thread.Collect();
|
||||
Check(snapshot.slices.size() == TraceThread::DefaultCapacity + 1 && snapshot.dropped, "the configured buffer grew beyond its capacity");
|
||||
Check(snapshot.totals.size() == 1 && snapshot.totals[0].count == TraceThread::DefaultCapacity + 2, "totals lost the event past the configured capacity");
|
||||
for (const auto* text : {"", "0", "-1", "+1", "1.5", " 1", "1 ", "1x", "4194305", "18446744073709551616"}) {
|
||||
bool rejected = false;
|
||||
try { TraceThread::ParseCapacity(text); }
|
||||
catch (const std::invalid_argument& error) { rejected = std::string_view(error.what()).starts_with("APS5_TRACE_EVENTS="); }
|
||||
Check(rejected, "an invalid event capacity did not report its setting");
|
||||
}
|
||||
}
|
||||
|
||||
void FoldShortSlices() {
|
||||
std::atomic<std::uint64_t> presents{1};
|
||||
TraceThread thread("queue worker 0x0", presents, 16);
|
||||
@@ -298,6 +323,7 @@ int main() {
|
||||
WindowBySeconds();
|
||||
WindowRejects();
|
||||
BufferCap();
|
||||
CapacitySettings();
|
||||
FoldShortSlices();
|
||||
EscapeStrings();
|
||||
EventsShape();
|
||||
|
||||
+3
-1
@@ -149,7 +149,9 @@ Set `APS5_SAMPLE_WORKER=<file>` to sample queue worker 0 every millisecond (in p
|
||||
|
||||
Set `APS5_TRACE_FRAMES=<start>:<count>` to record `<count>` presents from present `<start>` (counted from 0), or `APS5_TRACE_SECONDS=<start>:<length>` to record from the first present at least `<start>` seconds after the first present until the first present `<length>` seconds later. Both values are whole numbers and the length is above 0; a malformed value, or both switches together, fails the first present. The window opens and closes at presents.
|
||||
|
||||
Inside the window the queue workers and the presenter record every `PerformanceTimer` scope and stage as a complete event named after its frame timing key: `Driver.WorkerWait`, `Driver.Packet`, `Driver.Packet.draw`, `Graphics.Draw`, `Driver.Present`, `Driver.Present.inflight_wait`, `Vulkan.Retire` and so on. A slice shorter than 20 us is only counted: its time is in the thread's counter `<thread> folded` (milliseconds per present) and in the summary. A thread writes a present's counter sample at its first slice after the next present, so the last present of the window has no sample; its time is still in the summary. Each thread allocates a buffer for 262,144 events when it starts; a full buffer stops that thread's recording and sets `trace_dropped`. When the window closes, the presenter formats the trace once and a `ProfileOutput` writer thread writes `aps5-trace-<commit>-<pid>.json.gz` (Chrome trace-event JSON, opened by ui.perfetto.dev or chrome://tracing) and appends one line to `aps5-summary.jsonl`, both in `APS5_TRACE_DIR` or the working directory. A window that is still open when the title exits writes nothing.
|
||||
Set `APS5_TRACE_EVENTS=<count>` to change the queue-worker and presenter buffers for a frame or seconds window. The count is a whole number from 1 through 4,194,304; the default is 262,144. An invalid value fails when one of these threads first binds to the trace, before recording. Each buffer uses `count * sizeof(TraceThread::Slice)` bytes (currently 32 bytes per event): 8 MiB at the default, 96 MiB at `3145728`, and 128 MiB at the maximum, per thread. The buffers are allocated even while waiting for the window to open, and formatting the trace uses additional memory. Shader preparation keeps its explicitly sized 32,768-event buffers, and the GPU range limit stays 262,144. Summary-only sessions allocate no event buffers and do not read this setting. A larger buffer can still fill; use a shorter window or increase the count within the bound.
|
||||
|
||||
Inside the window the queue workers and the presenter record every `PerformanceTimer` scope and stage as a complete event named after its frame timing key: `Driver.WorkerWait`, `Driver.Packet`, `Driver.Packet.draw`, `Graphics.Draw`, `Driver.Present`, `Driver.Present.inflight_wait`, `Vulkan.Retire` and so on. A slice shorter than 20 us is only counted: its time is in the thread's counter `<thread> folded` (milliseconds per present) and in the summary. A thread writes a present's counter sample at its first slice after the next present, so the last present of the window has no sample; its time is still in the summary. By default, each queue worker and presenter allocates a buffer for 262,144 events when it starts; a full buffer stops that thread's recording and sets `trace_dropped`. When the window closes, the presenter formats the trace once and a `ProfileOutput` writer thread writes `aps5-trace-<commit>-<pid>.json.gz` (Chrome trace-event JSON, opened by ui.perfetto.dev or chrome://tracing) and appends one line to `aps5-summary.jsonl`, both in `APS5_TRACE_DIR` or the working directory. A window that is still open when the title exits writes nothing.
|
||||
|
||||
The summary line has `commit` and `dirty` of the build, `env` (every `APS5_*` variable; a value containing `/` or `\` is written as `<path>`), `gpu`, `driver`, `os`, `resolution` (the video output's), `window`, `presents`, `frame_ms` (p50, p90, p99 and max of the intervals between presents), `gpu_busy_ms_per_present` (the batch timestamps behind `[present]`), `worker_busy_pct` (share of the window each queue worker spends in `Driver.Packet`, `Driver.Suspend`, `Driver.EndSubmission`, `Driver.Rewind` and `Driver.Completion`), `top_phases_ms_per_frame` (the eight largest scopes and stages, without `Driver.WorkerWait`), `trace_dropped` and `trace`. A trace holds timings, scope names, counts and thread names; no guest memory, paths or strings of the title.
|
||||
|
||||
|
||||
Reference in new issue
Block a user