diff --git a/docs/ACTIVE.md b/docs/ACTIVE.md index e21f4f0..0a3ec4d 100644 --- a/docs/ACTIVE.md +++ b/docs/ACTIVE.md @@ -45,14 +45,18 @@ The acceptance goal (2026-09-25): Resident Evil 4, Super Smash Bros. Melee and Wind Waker correct, full speed, with audio, input and states, then stable and measured through the profiles in `tooling/dolphin-profiles/` (docs/PHASE_LOG.md). -1. Profiles 0 and 1 pass on all three games: correct pictures, full speed, - display-paced frames (docs/PHASE_LOG.md, 2026-09-25). Tolerated: a - screenshot's 55 ms read-back leaves its audio window ~0.4% short. -2. The driver's copy throughput: the 55 ms read-back of the 4K output and a - 218 ms copy in RE4's load; Profile 5's EFB copies to RAM depend on it. -3. Profiles 2-11 on all three games: ubershaders, 6x torture, GPU and RAM EFB, - the scaling ladder, MSAA, cold/warm shader cache, reloads, state stress, - long soak. +1. Profiles 0, 1 and 2 (1x and 6x) pass on all three games: correct + pictures, full speed, display-paced frames, five swapchain images at 120 Hz + (docs/PHASE_LOG.md, 2026-09-25). Tolerated: a screenshot's 55 ms read-back + leaves its audio window ~0.4% short. +2. The driver's copy throughput: the 55 ms read-back of the 4K output, a + 218 ms copy in RE4's load, and Wind Waker's 40-90 read-backs a second, each + done by the CPU after a wait for the GPU; Profile 5's EFB copies to RAM + depend on it. +3. Profiles 3-11 on all three games: 6x torture, GPU and RAM EFB, the scaling + ladder, MSAA, cold/warm shader cache (a new build's first pipelines cost + 30-95 ms each), reloads with the menu's three-image swapchain and the game's + five, state stress, long soak. 3. The dual-core FIFO playback stall (single core plays). ## Accepted baselines diff --git a/docs/PHASE_LOG.md b/docs/PHASE_LOG.md index 45c4300..1b1d1a6 100644 --- a/docs/PHASE_LOG.md +++ b/docs/PHASE_LOG.md @@ -3287,3 +3287,40 @@ that long now empties it; before, a screenshot window was ~750 frames short in RE4 and Wind Waker and 2000-3800 in Melee. The driver's copy throughput (the 55 ms read-back, a 218 ms copy in RE4's load) is the next lead, and matters most for Profile 5's EFB copies to RAM. + +## 2026-09-25 — Profile 2: five swapchain images at 120 Hz + +Profile 2 (synchronous ubershaders, 1x and 6x) ran RE4 and Melee at full speed +at both resolutions, and Wind Waker at 89-99% at 1x and 80-84% at 6x with the +GPU busy 0.7 and 2.2 ms a present: slow at 1x, so CPU or presentation, not GPU +throughput. + +- The driver's new `cpu_copies` profile field showed Wind Waker reading back + 40-90 images a second (EFB copies encoded to RAM, and the EFB peeks its game + settings enable), each a wait for the GPU; ubershaders lengthen what each + wait covers. +- The CPU sampler at a 1 ms threshold found neither thread busy: the main + thread waited in vkAcquireNextImageKHR and the CPU thread between frame + steps. +- A one-shot timeline of retro_run (removed since) showed why. RetroArch + emulates a swap interval of 2 at 119.88 Hz by presenting each frame twice, + and three images leave room for two queued flips: one frame. A new frame's + run took 7 ms and queued both presents at once; the repeated field's run + then waited 13-14 ms for two vblanks inside its present, and Dolphin's GPU + thread -- the thread that waits -- cannot drain the FIFO while it does, so + the CPU thread stalled behind it. The pair of runs crossed 33.4 ms often + enough to lose 4-12%. +- A temporary driver with five images and `video_max_swapchain_images = 5` + ran the same state at 100%. + +../PS5_Vulkan R74 gives a swapchain the images it asks for, three to five, and +patch 0091 has RetroArch ask for (video_max_swapchain_images - 1) * interval + 1 +when it emulates an interval. The default of three becomes five at 120 Hz with an +interval of 2: two frames of lookahead, the same frames and the same 33 ms as three +images at 60 Hz, so the latency is what a 60 Hz display has. + +Title 5beca7cc (`evidence/dolphin-p2-swapchain-before`, `-after`): Profile 1 and +Profile 2 at 1x and 6x on all three games, 1,200 presents per 10 s and every +steady window at 100% (99% in one screenshot window). Melee's first run on a new +build still stalls 30-95 ms on each pipeline it compiles for the first time: the +driver's shader cache is per build, and Profile 8 is where that is measured. diff --git a/evidence/dolphin-p2-swapchain-after/capture.json b/evidence/dolphin-p2-swapchain-after/capture.json new file mode 100644 index 0000000..be848d8 --- /dev/null +++ b/evidence/dolphin-p2-swapchain-after/capture.json @@ -0,0 +1,44 @@ +{ + "step": "dolphin-p2-swapchain-after", + "recorded": "2026-09-25", + "console_run": "not read this run", + "revision": "title 5beca7cc: driver R74 (up to five images) and RetroArch patch 0091", + "command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; driver profile armed", + "raw_capture": "klog/ww-p2-r74-trace.txt", + "raw_lines": 106, + "raw_bytes": 16201, + "lines": [ + "build identity: 5beca7cccd834b7c46bcc76900e15956f0f7eacc9eb6d2e7676b36cb3d49d86a", + "[ps5vk] output: 119.88 Hz selected", + "audio ps5: ready requested_rate=48000 rate=48000 channels=2 s16 grain=256 buffer_bytes=12288", + "[ps5vk] hitch period_ms=1099.782 app_ms=1091.192 queue_ms=8.557 copy_ms=4.571", + "[ps5vk] hitch period_ms=156.733 app_ms=137.499 queue_ms=19.202 copy_ms=18.807", + "[ps5vk] hitch period_ms=255.465 app_ms=255.176 queue_ms=0.052 copy_ms=0.000", + "[ps5vk] hitch period_ms=5810.600 app_ms=5804.438 queue_ms=5.908 copy_ms=1.694", + "[ps5vk] profile frames=329", + "[ps5vk] profile2 frame_ms=30.432 frame_max_ms=5810.600 hitches=4 presents=65/66 periods=0:163 4:78 8:2 12:1 16:4 20:3 24:73 32:1 64:4", + "audio ps5: window played=132185 silence=347815", + "[ps5vk] profile frames=1201", + "[ps5vk] profile2 frame_ms=8.328 frame_max_ms=25.019 hitches=0 presents=241/240 periods=0:498 4:309 8:74 12:24 16:50 20:160 24:86", + "audio ps5: window played=480000 silence=0", + "[ps5vk] profile frames=1197", + "[ps5vk] profile2 frame_ms=8.355 frame_max_ms=26.307 hitches=0 presents=239/240 periods=0:584 4:307 8:6 16:6 20:65 24:229", + "audio ps5: window played=480000 silence=0", + "[ps5vk] profile frames=1200", + "[ps5vk] profile2 frame_ms=8.342 frame_max_ms=25.324 hitches=0 presents=240/240 periods=0:491 4:409 24:300", + "audio ps5: window played=480000 silence=0", + "[ps5vk] profile frames=1200", + "[ps5vk] profile2 frame_ms=8.342 frame_max_ms=25.049 hitches=0 presents=240/240 periods=0:599 4:301 20:1 24:299", + "audio ps5: window played=480000 silence=0", + "[ps5vk] profile frames=1200", + "[ps5vk] profile2 frame_ms=8.339 frame_max_ms=25.360 hitches=0 presents=240/240 periods=0:520 4:357 8:18 12:5 16:12 20:99 24:189", + "audio ps5: window played=480000 silence=0", + "[ps5vk] profile frames=1200", + "[ps5vk] profile2 frame_ms=8.334 frame_max_ms=26.859 hitches=0 presents=240/240 periods=0:554 4:281 8:46 12:20 16:36 20:134 24:129", + "audio ps5: window played=480000 silence=0", + "audio ps5: closed accepted=3121669 played=3121669 discarded=0 silence=384251 calls=13695 errors=0 peak=2470 capacity=3072 drain=00000100 close=00000000", + "[ps5vk] output: default mode restored", + "[INFO] [PS5] build identity: 5beca7cccd834b7c46bcc76900e15956f0f7eacc9eb6d2e7676b36cb3d49d86a", + "[INFO] [Vulkan] Got 5 swapchain images." + ] +} diff --git a/evidence/dolphin-p2-swapchain-after/expectation.json b/evidence/dolphin-p2-swapchain-after/expectation.json new file mode 100644 index 0000000..b6c648c --- /dev/null +++ b/evidence/dolphin-p2-swapchain-after/expectation.json @@ -0,0 +1,13 @@ +{ + "step": "dolphin-p2-swapchain-after", + "note": "After: RetroArch asks for (3 - 1) * 2 + 1 = 5 images for its emulated swap interval of 2 and the driver gives them; the same run is 100% in every steady window, 1,200 presents per 10 s.", + "must_contain": [ + "Got 5 swapchain images", + "[ps5vk] output: 119.88 Hz selected", + "audio ps5: window played=480000 silence=0" + ], + "must_not_contain": [ + "Got 3 swapchain images" + ], + "human_check": "" +} diff --git a/evidence/dolphin-p2-swapchain-before/capture.json b/evidence/dolphin-p2-swapchain-before/capture.json new file mode 100644 index 0000000..6e624c5 --- /dev/null +++ b/evidence/dolphin-p2-swapchain-before/capture.json @@ -0,0 +1,47 @@ +{ + "step": "dolphin-p2-swapchain-before", + "recorded": "2026-09-25", + "console_run": "not read this run", + "revision": "title f2585b8f: driver R73, three swapchain images at 119.88 Hz with a swap interval of 2", + "command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; driver profile armed", + "raw_capture": "klog/p2-ubershaders-1x-ww-trace.txt", + "raw_lines": 112, + "raw_bytes": 16467, + "lines": [ + "build identity: f2585b8fcbf26c8f63d4651abdfeb7020167fffaac0868f1c0d04b3dcbaeec24", + "[ps5vk] output: 119.88 Hz selected", + "audio ps5: ready requested_rate=48000 rate=48000 channels=2 s16 grain=256 buffer_bytes=12288", + "[ps5vk] hitch period_ms=1044.742 app_ms=1036.333 queue_ms=8.374 copy_ms=4.556", + "[ps5vk] hitch period_ms=219.937 app_ms=219.651 queue_ms=0.052 copy_ms=0.000", + "[ps5vk] hitch period_ms=102.392 app_ms=96.902 queue_ms=5.245 copy_ms=1.732", + "audio ps5: window played=386656 silence=93344", + "[ps5vk] profile frames=969", + "[ps5vk] profile2 frame_ms=10.343 frame_max_ms=1044.742 hitches=3 presents=323/323 periods=0:356 4:292 8:72 12:3 16:7 20:72 24:162 32:1 36:1 64:3", + "audio ps5: window played=477015 silence=2985", + "[ps5vk] profile frames=1190", + "[ps5vk] profile2 frame_ms=8.413 frame_max_ms=26.668 hitches=0 presents=396/397 periods=0:531 4:260 8:83 12:22 16:49 20:148 24:97", + "[ps5vk] hitch period_ms=63.659 app_ms=8.107 queue_ms=55.492 copy_ms=55.212", + "[ps5vk] hitch period_ms=52.922 app_ms=43.976 queue_ms=8.902 copy_ms=2.070", + "audio ps5: window played=452215 silence=27785", + "[ps5vk] profile frames=1108", + "[ps5vk] profile2 frame_ms=9.027 frame_max_ms=63.659 hitches=2 presents=370/369 periods=0:437 4:317 8:73 16:2 20:1 24:276 52:1 60:1", + "audio ps5: window played=427837 silence=52163", + "[ps5vk] profile frames=1064", + "[ps5vk] profile2 frame_ms=9.400 frame_max_ms=25.412 hitches=0 presents=354/355 periods=0:361 4:302 8:135 24:266", + "[ps5vk] hitch period_ms=55.275 app_ms=0.063 queue_ms=55.155 copy_ms=54.882", + "[ps5vk] hitch period_ms=52.767 app_ms=43.939 queue_ms=8.795 copy_ms=2.039", + "audio ps5: window played=452533 silence=27467", + "[ps5vk] profile frames=1109", + "[ps5vk] profile2 frame_ms=9.019 frame_max_ms=55.275 hitches=2 presents=370/370 periods=0:483 4:272 8:71 12:1 16:1 20:2 24:277 52:2", + "[ps5vk] hitch period_ms=59.729 app_ms=4.392 queue_ms=55.283 copy_ms=55.014", + "[ps5vk] hitch period_ms=47.645 app_ms=38.135 queue_ms=9.477 copy_ms=2.659", + "audio ps5: window played=451832 silence=28168", + "[ps5vk] profile frames=1113", + "[ps5vk] profile2 frame_ms=8.989 frame_max_ms=59.729 hitches=2 presents=371/371 periods=0:398 4:287 8:115 12:31 16:45 20:126 24:109 44:1 56:1", + "audio ps5: window played=473100 silence=6900", + "audio ps5: closed accepted=3123802 played=3123802 discarded=0 silence=281766 calls=13303 errors=0 peak=3072 capacity=3072 drain=00000100 close=00000000", + "[ps5vk] output: default mode restored", + "[INFO] [PS5] build identity: f2585b8fcbf26c8f63d4651abdfeb7020167fffaac0868f1c0d04b3dcbaeec24", + "[INFO] [Vulkan] Got 3 swapchain images." + ] +} diff --git a/evidence/dolphin-p2-swapchain-before/expectation.json b/evidence/dolphin-p2-swapchain-before/expectation.json new file mode 100644 index 0000000..dd6410e --- /dev/null +++ b/evidence/dolphin-p2-swapchain-before/expectation.json @@ -0,0 +1,11 @@ +{ + "step": "dolphin-p2-swapchain-before", + "note": "Before: Wind Waker under synchronous ubershaders ran at 89-99% (a window as short as 427837 of 480000 audio frames) with the GPU busy 0.7 ms a present. RetroArch presented every frame twice into three images, and the second present waited for a vblank on the thread that runs the frame-stepped core.", + "must_contain": [ + "Got 3 swapchain images", + "[ps5vk] output: 119.88 Hz selected", + "audio ps5: window played=427837 silence=52163" + ], + "must_not_contain": [], + "human_check": "" +} diff --git a/patches/series b/patches/series index 8c516fe..86e7137 100644 --- a/patches/series +++ b/patches/series @@ -157,3 +157,9 @@ # viewport, and a save-state thumbnail of Dolphin's frame overran by ~1 MB # gfx/drivers/vulkan.c 0090-readback-within-caller-viewport + +# 0091 an emulated swap interval (each frame presented swap_interval times) +# asks for (video_max_swapchain_images - 1) * interval + 1 images, so +# the setting's frames of lookahead hold at 120 Hz as at 60 Hz +# gfx/common/vulkan_common.c +0091-swap-interval-images diff --git a/tests/test_frontend.py b/tests/test_frontend.py index c019909..d7e49e7 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -342,7 +342,7 @@ class PortPatches(unittest.TestCase): change deliberately, in a commit that says why. """ self.assertEqual( - len(self.blocks()), 199, + len(self.blocks()), 200, "the patch count changed: if a block was added or removed on purpose, " "update this number in the same commit and say why in its message") diff --git a/tools/apply-port-patches.py b/tools/apply-port-patches.py index 05401aa..7b36864 100755 --- a/tools/apply-port-patches.py +++ b/tools/apply-port-patches.py @@ -2856,6 +2856,28 @@ EDITS = [ " vp_height = caller_height;\n", "patches/series, 0090): never more than the", ), + ( + # 0091: a swap interval above 1 is emulated by presenting each frame + # that many times (vulkan.c), so each frame takes that many of the + # queue's images, and video_max_swapchain_images' lookahead in frames + # shrinks by the interval. At 120 Hz with an automatic interval of 2, + # three images held one frame (16.7 ms) where they hold two (33 ms) at + # 60 Hz; the second present of every frame then waited on the thread + # that runs the core, and Dolphin's frame-stepped emulation lost 4-12% + # of Wind Waker's speed under ubershaders (2026-09-25, ../PS5_Vulkan + # R74). (images - 1) * interval + 1 keeps the frames of lookahead the + # setting names, within the surface's maxImageCount (clamped below). + "gfx/common/vulkan_common.c", + " desired_swapchain_images = settings->uints.video_max_swapchain_images;\n", + " desired_swapchain_images = settings->uints.video_max_swapchain_images;\n" + " /* Changed by this port (patches/series, 0091): an emulated swap\n" + " * interval presents each frame swap_interval times, so the frames of\n" + " * lookahead the setting names need that many images each. */\n" + " if (swap_interval > 1 && desired_swapchain_images > 1)\n" + " desired_swapchain_images = (desired_swapchain_images - 1)\n" + " * (uint32_t)swap_interval + 1;\n", + "patches/series, 0091): an emulated swap", + ), ] # Changes that are withdrawn rather than deleted, by marker.