Skip to content

perf(agc): present 8-bit integer resident images without a readback - #6037

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
meh7an:perf/agc-present-integer-resident-images
Oct 15, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
meh7an:perf/agc-present-integer-resident-images

Conversation

@meh7an

@meh7an meh7an commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

ResidentPresentPath sends a resident display image in an 8-bit integer format (R8G8B8A8_UINT, B8G8R8A8_UINT) down ResidentPresent::None. Every present then reads the image back from the GPU (FlushForAccess syncs the recorder, then GuestMemory::Read) and uploads it again (GpuColorTransfer::Upload), all under the GPU mutex.

The presentation pass reads its source as raw words (usampler2D) and decodes them by the display's format. So these images can take ResidentPresent::Convert like the UNORM ones. A 10-bit display still takes the existing path.

Sonic Origins Plus (PPSA05324) renders its final image as R8G8B8A8_UINT. On the S2 page of its main menu, the presenter waited 270 of 5,141 samples (15 s) in Recorder::finish <- Sync <- FlushForAccess <- GuestMemory::Read, and ran 52 more in the re-upload. Meanwhile the queue worker waited on the GPU mutex for 350 samples, 5.3 per frame. With this change both are gone, and the worker's GPU-mutex waits drop to 0.2 per frame.

Tested

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

  • agc_resident_present on main 5010908 with this change. It adds two decisions: an 8-bit integer image is converted, and a 10-bit display still is not.
  • 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 d21f225, this change 910f3bc:
Metric Baseline This PR Change
frame_ms.p50 234.697 219.297 -6.6%
frame_ms.p90 241.277 228.722 -5.2%
mean_frame_ms 232.445 218.581 -6.0%
gpu_busy_ms_per_present 75.015 75.212 +0.3%
Driver.Packet (ms per frame) 229.664 215.842 -6.0%

The FPS shown in the title went from 4.50 to 4.77 (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 d21f225 and this change is 910f3bc.

The comparison tool marks the window's first present (1918 vs 2024) 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 (baseline d21f225, this change 910f3bc):
pr6037-traces.zip

@Dextroid17

Copy link
Copy Markdown
Contributor

Measured on this branch (5b53712, 229 commits behind main), default CMake build (CMAKE_BUILD_TYPE empty, so unoptimized), CachyOS Linux 7.2.9, agc_driver_recorder_tests --resident-present-only, rebuilt:

  • 3 of 3 runs pass.
  • With DisplayFormat.cpp taken from main and the new checks kept: exit 1, an 8-bit integer image is not converted. So the new decision checks fail without the change.

One thing I checked because it could have been a problem: a vkCmdBlitImage between an integer and a UNORM format is not allowed. This change does not use a blit for these images. By reading, for these images Convert takes the drawn branch: an R32_UINT view and vkCmdDraw, reading raw words as the PR body says, with no vkCmdBlitImage. (A half-float display with an undrawn Convert image does blit, but ResidentPresentPath does not return that for these formats.)

Not checked: the speedup, the Sonic Origins Plus run, anything on a real GPU or on Windows, and that an _UINT image actually has the usage flags the Convert path needs. The new checks cover only ResidentPresentPath; the suite's GPU conversion cases use no _UINT format, and this ran on lavapipe.

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