Skip to content

perf(agc): skip the register read log while no log is open - #6066

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
meh7an:perf/agc-register-read-log-when-open
Oct 15, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
meh7an:perf/agc-register-read-log-when-open

Conversation

@meh7an

@meh7an meh7an commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

Every register read of a draw's decode goes through NoteRegisterRead, which checks the thread's register read log (RegisterReadLog(), thread-local). Only the draw recipe verification and the tests ever open one. Under MinGW a thread-local access is an __emutls_get_address call, and a draw decode reads a few hundred registers.

A process-wide count of open logs (RegisterReadLogsOpen(), kept by the new RegisterReadLogScope) now lets a read skip the thread-local lookup while no log is open. Logs are per thread, so a count of zero means no thread has one. The decode and the tests open their logs through the scope.

On the S2 page of Sonic Origins Plus's main menu, the queue worker's samples per frame (APS5 sampler, 15 s) fell as follows: __emutls_get_address 6.09 to 2.35, pthread_getspecific 2.69 to 1.09, and Driver::decodeDraw 5.61 to 2.88.

Tested

Windows 11 (10.0.26300), WinLibs GCC 15.2.0 (UCRT), Release, NVIDIA GeForce RTX 2080 Ti, driver 617.14.

  • agc_driver_graphics on main fa539dd with this change. Its register read checks open their logs through the new scope.
  • Sonic Origins Plus, main menu, S2 page (the 3D background), 1233x694 window.
    • Captured with APS5_TRACE_SECONDS=200:5 on the same machine in one session, caches warm. Traces attached.
    • Baseline 910f3bc, this change 9b08cb0:
Metric Baseline This PR Change
frame_ms.p50 222.257 204.139 -8.2%
frame_ms.p90 230.192 209.037 -9.2%
mean_frame_ms 220.917 203.179 -8.0%
gpu_busy_ms_per_present 74.687 73.102 -2.1%
Driver.Packet (ms per frame) 218.178 200.483 -8.1%

The FPS shown in the title went from 4.66 to 5.00 (median of six readings).

Note: measuring base. These numbers come from main 0a56aaa, the last main before #5596. On Windows, later mains compare direct imports on every use, which keeps this menu at 3-4 FPS and hides a change of this size. The base also carries these commits from my fork, in both captures:

All of them are on measure/fresh-dev-2026-10-14: the baseline is 910f3bc and this change is 9b08cb0.

The comparison tool marks the window's first present (2148 vs 2229) as a configuration difference: the window opens 200 s into each run. Both summaries set trace_dropped, because the worker filled its event buffer at about 2,700 draws per frame. The phase totals above stay complete.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 2080 Ti, driver 617.14, Windows 11
  • AI-assisted: yes

Traces and summary lines (baseline 910f3bc, this change 9b08cb0):
regread-traces.zip

@Dextroid17

Copy link
Copy Markdown
Contributor

Measured on main 0ae1deb and on this branch (ec615c1), default CMake build (CMAKE_BUILD_TYPE empty, so unoptimized), lavapipe (llvmpipe), CachyOS Linux 7.2.9, agc_driver_graphics_tests, rebuilt for each ref: 5 of 5 runs pass on both.

I read the diff. RegisterReadLogsOpen() counts open scopes, and the register hook returns early when it is 0. Because the log pointer is thread-local and the counter is global, a scope open on another thread only makes the hook fall through to the old thread-local check, so it never records wrongly.

Limits: the added assertion (the register facade recorded without an open log) does not show the speedup; by reading, the same check holds on main's code too (the pointer is null after the scope). I did not measure the performance change, and I ran only this one test target.

AI-assisted: yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants