Skip to content

perf(agc): back the GDS with anonymous shared memory on integrated GPUs and keep its DMA_DATA on the GPU - #13159

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
Gonic0:perf/agc-gds-on-gpu
Nov 2, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
Gonic0:perf/agc-gds-on-gpu

Conversation

@Gonic0

@Gonic0 Gonic0 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What

Builds on #9385. Until it merges, this branch carries its commits (merged at ec471877d3); only the top commit, 57190a6629, is new here.

The GDS is a 64 KiB private anonymous guest mapping (Pm4::GdsAddress). On MoltenVK the GPU cannot import that mapping: registered for the GPU as an experiment, its host import failed at vkAllocateMemory (-2) (APS5_TRACE_HOST_IMPORT=1). So DMA_DATA through the GDS stays on the CPU and waits for the GPU:

This change, on top of #9385:

  • Backing: on an integrated GPU (VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) with VK_EXT_external_memory_host, the Vulkan device requests an importable GDS when it is created (Pm4::RequestImportableGds). GdsAddress then backs the GDS with anonymous shared memory: shm_open(SHM_ANON), 64 KiB, mapped shared with CPU read and write, zeroed, and the descriptor closed. This is not direct memory, so the title's direct memory pool and sceKernelAvailableDirectMemorySize are unchanged, and the object has no name a title could open. Its host import succeeds on MoltenVK. Pm4::GdsImportable() reports the backing actually obtained. If the shared mapping fails, if the request was not made, or if the GDS was first used before the device made it, the GDS keeps the private mapping and everything behaves as with perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 alone. APS5_NO_SHARED_GDS=1 forces that.
  • Packets, only when the GDS is importable: in preparePacketMemory, a GDS destination first tries the GPU copy behind a pending source (copyBehindPendingSource, with decodeCopy decoding GDS to memory and memory to GDS), then perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's idle-GDS path, then the existing store path (ResolveStore, which perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 extends to GDS destinations, then WriteLabelOnGpu), which records the store on the GPU behind the queued work instead of draining. A GDS to memory copy behind pending GDS writes is recorded on the GPU the same way. With a private GDS, as on any discrete GPU, decodeCopy returns nothing for the GDS and the store path is skipped for it, so those devices take perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's paths unchanged.
  • Pending writes: with an importable GDS, gdsWritesPending also counts the GPU writes this change records into the GDS (VulkanDevice::StoresPendingOver), so perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's idle path does not take a GDS that a recorded copy or store still writes. In the test below, removing that check did not fail: the idle path's CPU write still landed after the recorded copy. The check keeps such a fill on the GPU instead of on that path.

DecodeMemoryCopy and ResolveStore are unchanged by this commit. On an integrated GPU the shaders' GDS bindings (ShaderResources, AddWritable(gds, GdsBytes, true)) now find the import and use it in place.

Devices that are neither integrated nor discrete (CPU, VIRTUAL_GPU, OTHER) keep the private GDS. I ran only an integrated GPU (Apple M5 Pro).

Tested

Apple M5 Pro, macOS 27.2, MoltenVK from Vulkan SDK 1.4.363, x86-64 Release under Rosetta 2. main alone does not build on macOS. I ran everything in a tree with the open macOS port PRs on main 39fc186c97 with #9385 merged, and this commit applied; it applies unchanged there.

agc_driver_gds_dma_fill (#9385's test), extended:

  • The GDS must be importable exactly when it was requested and APS5_NO_SHARED_GDS is not set.
  • A dispatch using the GDS, a GDS fill, a second dispatch and a copy out of the GDS in one submission: perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's value checks are kept. With an importable GDS, the fill must be stored on the GPU, the copy out recorded on the GPU, and nothing may drain; with a private GDS the fill drains, as perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 requires.
  • New: a dispatch that does not use the GDS writes a buffer, a copy of it into the GDS, a fill of that GDS dword and a copy out of it, in one submission. The read-back must be the fill's value. With an importable GDS, the copy in must be recorded on the GPU, the fill stored on the GPU, and nothing may drain.
  • New CTest entry agc_driver_gds_dma_fill_private runs the same test with APS5_NO_SHARED_GDS=1: same values, perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's drain, no GPU copy or store over the GDS.

Negative controls, each of which fails agc_driver_gds_dma_fill:

  • no shared mapping while it is requested: "the GDS kept its private mapping although shared memory was requested";
  • decodeCopy without the GDS cases: the copy out of the shared GDS is not recorded on the GPU;
  • the store path skipped for the GDS: the fill of the shared GDS drains.

gdsWritesPending without StoresPendingOver does not fail the test, as said above.

With VK_LAYER_KHRONOS_validation, ANYPS5_REQUIRE_VULKAN=1 and ANYPS5_NO_SHADER_CACHE=1, these 44 CTest entries pass: agc_driver, agc_driver_gds_dma_fill, agc_driver_gds_dma_fill_private, agc_driver_gds_append_consume, agc_atomic_gds_size, agc_dma_data_tests, agc_driver_pipelined_dma, agc_driver_pipelined_dma_draw_pipeline, agc_driver_pm4, agc_guarded_memory_loads and the 34 agc_storage_, agc_driver_storage_ and agc_driver_linear_target_ entries. The only validation message is VUID-VkDeviceCreateInfo-pProperties-04451, from the tests' own device creation on MoltenVK.

PPSA21564 01.007.000, the logos before the intro. Per frame there is one 4-byte DATA → GDS fill on queue 0x40 behind GDS writes, which drained for 3 to 6 ms on that queue (the graphics queue waits on it), and about 15 four-byte GDS → memory copies read on the CPU. The base is the tree with #9385 and without this commit (99fffc44a1, its revert); the change is c4562034f0. Alternating, no profiling switches, APS5_TRACE_FRAMES=150:240, four pairs:

round build FPS frame p50 p90 p99 GPU busy per present load average
1 base 28.9 32.9 ms 35.6 ms 42.5 ms 20.7 ms 8.4
2 this change 22.9 37.3 ms 40.6 ms 42.2 ms 25.1 ms 6.7
3 base 28.9 32.6 ms 42.7 ms 46.5 ms 20.0 ms 5.0
4 this change 33.0 29.1 ms 31.1 ms 34.4 ms 21.8 ms 4.8
5 base 31.9 28.9 ms 33.0 ms 37.9 ms 18.9 ms 5.7
6 this change 36.3 25.2 ms 27.2 ms 29.6 ms 16.2 ms 5.2
7 base 33.1 28.7 ms 31.1 ms 35.2 ms 17.7 ms 5.0
8 this change 36.3 26.0 ms 28.6 ms 30.9 ms 17.7 ms 5.1

FPS is 480 presents over the time between dumped frames 5 and 13; the load average is the one-minute value after each run. The host was not idle: a browser and the desktop stayed open, and the first pair ran while the load was still falling from earlier runs. Round 2 is the only one where this change was slower. Rounds 3 to 8 give 28.9 to 33.1 FPS for the base against 33.0 to 36.3 with this change, frame p50 28.7 to 32.6 ms against 25.2 to 29.1 ms. An earlier set on a busier host (load 7 to 10) moved the frame p50 from 41.6 to 43.1 ms to 36.4 to 37.2 ms, with flat FPS. No run reported an unexpected copy path, and the dumped frames show the logos intact.

Base (99fffc44a1, round 5):

{"commit":"99fffc44a17d","dirty":null,"env":{"APS5_ASYNC_PIPELINES":"1","APS5_DUMP_FRAMES":"200","APS5_DUMP_FRAMES_EVERY":"60","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"150:240"},"gpu":"Apple M5 Pro","driver":"MoltenVK 1.4.2","os":"Darwin 27.2.0","cpu":"VirtualApple @ 2.50GHz","cpu_threads":18,"memory_mib":49152,"title_id":"PPSA21564","title":"ASTRO BOT","content_version":"01.007.000","resolution":"3840x2160","window":{"spec":"frames 150:240","first_present":150,"ms":7120.992},"presents":240,"frame_ms":{"p50":28.924,"p90":33.006,"p99":37.86,"max":53.888},"gpu_busy_ms_per_present":18.877,"worker_busy_pct":{"queue worker 0x0":98.859,"queue worker 0x40":99.605,"queue worker 0x48":95.681,"queue worker 0x3a":0.031},"top_phases_ms_per_frame":{"Driver.Packet":86.34,"Driver.Packet.memory_wait":53.801,"Driver.Packet.dispatch":10.166,"Driver.Dispatch":8.528,"Driver.Packet.prepare_memory":8.131,"VideoOut.Flip":6.569,"Driver.Present":5.431,"VideoOut.Flip.present":5.417},"trace_dropped":false,"trace":"aps5-trace-99fffc44a17d-76398.json.gz"}

This change (c4562034f0, round 6):

{"commit":"c4562034f0dd","dirty":null,"env":{"APS5_ASYNC_PIPELINES":"1","APS5_DUMP_FRAMES":"200","APS5_DUMP_FRAMES_EVERY":"60","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"150:240"},"gpu":"Apple M5 Pro","driver":"MoltenVK 1.4.2","os":"Darwin 27.2.0","cpu":"VirtualApple @ 2.50GHz","cpu_threads":18,"memory_mib":49152,"title_id":"PPSA21564","title":"ASTRO BOT","content_version":"01.007.000","resolution":"3840x2160","window":{"spec":"frames 150:240","first_present":150,"ms":6130.148},"presents":240,"frame_ms":{"p50":25.211,"p90":27.206,"p99":29.588,"max":51.432},"gpu_busy_ms_per_present":16.241,"worker_busy_pct":{"queue worker 0x0":98.659,"queue worker 0x40":99.667,"queue worker 0x48":96.778,"queue worker 0x3a":0.049},"top_phases_ms_per_frame":{"Driver.Packet":74.486,"Driver.Packet.memory_wait":45.523,"Driver.Packet.dispatch":15.598,"Driver.Dispatch":14.113,"VideoOut.Flip":6.743,"Vulkan.DispatchRecipe":6.181,"Vulkan.DispatchRecipe.command_record":6.177,"Driver.Present":5.653},"trace_dropped":false,"trace":"aps5-trace-c4562034f0dd-77169.json.gz"}

Traces: base, this change

Not tested on Linux, Windows, a discrete GPU or a software Vulkan driver.

Overlap

Checklist

🤖 Generated with Claude Code

@github-actions github-actions Bot added the perf Performance change label Oct 30, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Gonic0 thanks, the design reads well: moving the GDS DMA onto the GPU timeline behind the queued work is the right way to keep its ordering against shader GDS use, and the three negative controls make the test convincing. Reviewed at 5ddf02c. Four points:

  1. The gate is "not a discrete GPU", not "integrated". SetGdsDirectMemory(state->properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) also covers CPU, VIRTUAL_GPU and OTHER devices (a software Vulkan driver that offers VK_EXT_external_memory_host takes the new path, including on a CI runner). Please either test for INTEGRATED_GPU or say in the description that every non-discrete device with host imports is affected, and say which of those you ran. The title and description speak of integrated GPUs only.
  2. The 64 KiB comes out of the title's direct memory. GdsAddress() allocates it with sceKernelAllocateDirectMemory(0, sceKernelGetDirectMemorySize(), ...) the first time the GDS is used and keeps it for the life of the process. A title that later asks for the whole pool (or a fixed physical range) can now fail or get different offsets. Please say how you checked that (for example by what the title allocates before and after, and what sceKernelAvailableDirectMemorySize reports), or take the 64 KiB from a place the title cannot see.
  3. Which baseline was measured, and the overlap with perf(agc): write DMA_DATA fills of an idle GDS without draining #9385. The text says the tree for the measurements had perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 reverted, and also that the drain was measured "in the tree with perf(agc): write DMA_DATA fills of an idle GDS without draining #9385". Please state which tree the base rows come from. perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 puts an early if (opcode == 0x50 && DmaDestination == DmaSelectGds) branch ahead of copyBehindPendingSource and ResolveStore; applied together, a GDS destination never reaches your decodeCopy / resolveGdsFill cases and keeps draining when a pending write overlaps. The two PRs conflict in PacketMemory.cpp, so please say which one is meant to land and rebase on it. fix(agc): avoid host synchronous drain on GDS DMA_DATA packets #13137 (unconditional no-drain) would also undo your "drained as before otherwise" fallback.
  4. The packet-level changes are not integrated-only. decodeCopy and resolveGdsFill run on every device; only the backing is conditional. On a discrete GPU the fill now becomes a CPU write when the recorder is idle and falls back to the drain otherwise. Please confirm that (a short run on a discrete GPU with the drain count per present before and after is enough), since the description says those devices are left alone.
  5. Requested backing vs actual backing. GdsAddress() deliberately falls back to an anonymous mapping when the direct allocation or the map fails, but the flag set by SetGdsDirectMemory stays true, and the new test requires the backing to equal that flag. Please keep "requested" and "obtained" apart (the GPU path should depend on the backing actually obtained), and add a case for the fallback that checks the GDS contents are still correct without the acceleration. A GDS first used before the request is set also keeps the original anonymous mapping; please cover or document that.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 8963 docs/dev/ENVIRONMENT.md L352
PR 9508 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L2262
PR 12568 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L2262-2263
PR 12696 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp L2262-2263
PR 13858 docs/dev/ENVIRONMENT.md L352

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1395 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 1896 docs/dev/ENVIRONMENT.md
PR 2434 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 5021 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6186 docs/dev/ENVIRONMENT.md
PR 6188 core/libs/prx/libSceAgcDriver/Execution/include/Pm4.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6804 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6924 docs/dev/ENVIRONMENT.md
PR 7275 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7449 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 7457 docs/dev/ENVIRONMENT.md
PR 7464 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7514 docs/dev/ENVIRONMENT.md
PR 7604 docs/dev/ENVIRONMENT.md
PR 7642 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 7963 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp
PR 8004 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 8246 docs/dev/ENVIRONMENT.md
PR 8484 docs/dev/ENVIRONMENT.md
PR 9012 docs/dev/ENVIRONMENT.md
PR 9345 docs/dev/ENVIRONMENT.md
PR 9356 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 9387 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 9712 docs/dev/ENVIRONMENT.md
PR 9852 docs/dev/ENVIRONMENT.md
PR 9898 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 10193 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 10249 core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp
PR 10442 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 10757 docs/dev/ENVIRONMENT.md
PR 10867 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10894 docs/dev/ENVIRONMENT.md
PR 11351 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 11435 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 11436 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 11483 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 12120 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 12323 docs/dev/ENVIRONMENT.md
PR 12324 docs/dev/ENVIRONMENT.md
PR 12544 docs/dev/ENVIRONMENT.md
PR 12567 core/libs/prx/libSceAgcDriver/Execution/include/Pm4.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 12746 docs/dev/ENVIRONMENT.md
PR 12828 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 12863 docs/dev/ENVIRONMENT.md
PR 12921 docs/dev/ENVIRONMENT.md
PR 12930 docs/dev/ENVIRONMENT.md
PR 12944 core/libs/prx/libSceAgcDriver/Execution/include/Pm4.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 12970 docs/dev/ENVIRONMENT.md
PR 13148 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 13646 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 13707 docs/dev/ENVIRONMENT.md
PR 13863 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 13920 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 13928 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 13929 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 14217 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 14218 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 14262 docs/dev/ENVIRONMENT.md
PR 14264 docs/dev/ENVIRONMENT.md
PR 14299 docs/dev/ENVIRONMENT.md
PR 14344 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 14943 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 15059 docs/dev/ENVIRONMENT.md

@Gonic0
Gonic0 force-pushed the perf/agc-gds-on-gpu branch from 5ddf02c to 57190a6 Compare October 30, 2026 05:25
@Gonic0 Gonic0 changed the title perf(agc): back the GDS with direct memory on integrated GPUs and keep its DMA_DATA on the GPU perf(agc): back the GDS with anonymous shared memory on integrated GPUs and keep its DMA_DATA on the GPU Oct 30, 2026
@Gonic0

Gonic0 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review. 57190a6629 reworks this on top of #9385; the description is rewritten to match.

  1. Gate. The request is now deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, inside the VK_EXT_external_memory_host block. CPU, VIRTUAL_GPU and OTHER devices keep the private GDS. I ran only an integrated GPU (Apple M5 Pro).
  2. Direct memory. The GDS no longer comes from the title's pool. It is anonymous shared memory (shm_open(SHM_ANON), 64 KiB, mapped shared with CPU read and write, descriptor closed): no direct memory, no name a title can open, and sceKernelAvailableDirectMemorySize is unchanged. Its host import succeeds on MoltenVK, where the private mapping's import failed at vkAllocateMemory.
  3. Baseline and perf(agc): write DMA_DATA fills of an idle GDS without draining #9385. The branch now builds on perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 (Depends on) and rewrites its GDS block. With an importable GDS it tries the GPU copy, then perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's idle path, then the store path on the GPU. With a private GDS it is perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 unchanged. The new measurements compare the tree with perf(agc): write DMA_DATA fills of an idle GDS without draining #9385 (99fffc44a1) against the same plus this commit (c4562034f0). The earlier rows were from a tree without perf(agc): write DMA_DATA fills of an idle GDS without draining #9385, and the 3 to 6 ms drain from a tree with it. fix(agc): avoid host synchronous drain on GDS DMA_DATA packets #13137 conflicts with this block; the overlap section says so.
  4. Discrete GPUs. The packet changes now depend on the GDS actually being importable. decodeCopy returns nothing for the GDS and the store path is skipped for it otherwise, so a discrete GPU takes perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's paths unchanged. That follows from the code; I have not run a discrete GPU.
  5. Requested vs obtained. Pm4::RequestImportableGds records the request and Pm4::GdsImportable() the backing obtained; only the latter gates the packet paths. A failed shared mapping, a missing request, or a GDS first used before the device made it keeps the private mapping, and the description says so. APS5_NO_SHARED_GDS=1 forces that, and the new CTest entry agc_driver_gds_dma_fill_private runs perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's test that way: same values, perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's drain, no GPU copy or store over the GDS.

The tests are now in #9385's agc_driver_gds_dma_fill, which has a dispatch using the GDS: with an importable GDS its fill behind that dispatch is stored on the GPU and its copy out is recorded on the GPU, and a new case orders a GPU copy into the GDS before a fill. Three negative controls fail it. The fourth, gdsWritesPending without the recorded-write check, did not fail (the description says why). The 44 focused entries pass with validation.

AI-assisted: yes

@oneandonlydean oneandonlydean self-assigned this Oct 30, 2026
@bunbeans2-arch

Copy link
Copy Markdown
Contributor

An AMD RDNA 4 run of 57190a6629 on a discrete card, since the description lists an Apple M5 Pro (MoltenVK) only.

Windows 11 Pro 10.0.22631, AMD Radeon RX 9070 XT (1002:7550, driver 32.0.32015.7029, amdvlk64), MSYS2 UCRT64 g++ 16.1.0, Release. ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation; with VK_LOADER_DEBUG=layer the loader logs Insert instance layer "VK_LAYER_KHRONOS_validation" in every run of every test below that creates a Vulkan instance (both GDS tests and agc_driver_recorder do; agc_driver_pm4, agc_driver_graphics and four of the agc_write_tracking* tests create none). This head merged onto main f7f03daf52, 3 runs per side:

  • This head: agc_driver_gds_dma_fill and agc_driver_gds_dma_fill_private (APS5_NO_SHARED_GDS=1) each pass 3 of 3, printing GDS DMA fill tests passed. agc_driver_pm4, agc_driver_recorder, agc_driver_graphics and the agc_write_tracking* tests also pass 3 of 3, as on main.
  • main f7f03daf52: the same existing tests pass 3 of 3.
  • No VUID- line on either side.

I did not check which GDS backing the default run picked on this card; the logs do not print it.

AI-assisted: yes (Claude Code)

@TechLevelZero

TechLevelZero commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 57190a6 merged onto main 73f4f7e · tested 2026-10-31 20:10 UTC · 23 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,430 passed · 5 failed (0 new, 3 also fail on main, 2 unstable on main) · 50 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,485 of 3,072 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (925 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_bda_device 50.7 s, ⚠️ agc_driver_graphics_maps_fallback 30.7 s, ⚠️ agc_driver_graphics 30.5 s
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_depth_clear_hle: failed in 2 of the last 10 main runs
agc_driver_submit_progress: failed in 7 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_submit_progress
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_driver_gds_dma_fill_private
Validation IDs already present on main (5 IDs in 427 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (427 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 419 more
VUID-vkCmdCopyImage-srcImage-01548 (13 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 5 more
VUID-vkCmdCopyImage-srcImage-01551 (13 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 5 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-vkCmdCopyBuffer-commonparent (1 test): agc_driver_depth_clear_hle
Skipped (50)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or pla
Reproduce
git fetch origin 57190a6629a4902fd56c7b4b75f384e3e0b1e411 73f4f7e06e4f210a8c6570e335fd654dc0a86134
git checkout 73f4f7e06e4f210a8c6570e335fd654dc0a86134 && git merge --no-ff 57190a6629a4902fd56c7b4b75f384e3e0b1e411
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@JJenkx

JJenkx commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Windows GPU run of head 57190a6629, merged onto main 1c6b316cf7 (clean merge, local only).

Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, Radeon RX 6700 XT with AMD proprietary driver 26.10.1 (Vulkan 1.4.315), WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON. Khronos validation layer 1.4.309 requested for every test (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation).

  • All 16 CTest entries of , which builds the test files this PR changes: pass, 3 of 3 runs each (--repeat until-fail:3), no failures, no skips.
  • They create Vulkan instances on the RX 6700 XT: the validation layer was inserted 12 times across the runs and reported no VUIDs.
  • No negative control: this PR's test files do not build against main's sources, because they use what the PR adds.

AI-assisted: yes (Claude Code).

@github-actions github-actions Bot added build docs agc libSceAgc* needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 30, 2026
@TechLevelZero

TechLevelZero commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 25d850e merged onto main 93dbd0f · tested 2026-11-02 03:20 UTC · 28 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,497 passed · 4 failed (0 new, 3 also fail on main, 1 unstable on main) · 45 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,546 of 3,165 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (932 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ agc_driver_graphics_maps_fallback 31.5 s, ⚠️ agc_driver_graphics 30.2 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 1 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (2, not in the main baseline's validation pass)
agc_driver_gds_dma_fill
agc_driver_gds_dma_fill_private
Validation IDs already present on main (5 IDs in 483 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (483 tests): agc_driver, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, and 475 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (45)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fps_limit: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_o
Reproduce
git fetch origin 25d850e2bd42573413e19ffc988116b88c8334c8 93dbd0f82aea684e95f15f06e7d42c0064af18cf
git checkout 93dbd0f82aea684e95f15f06e7d42c0064af18cf && git merge --no-ff 25d850e2bd42573413e19ffc988116b88c8334c8
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
@Lander0606

Copy link
Copy Markdown
Contributor

First run of the new path on an integrated GPU outside macOS: a Radeon 860M under Linux/RADV takes it (INTEGRATED_GPU with VK_EXT_external_memory_host), and agc_driver_gds_dma_fill fails there 5 of 5, because RADV refuses the host import of the shared GDS. The data stays correct; only the GPU path is lost. The private mapping does import on RADV.

Setup: Radeon 860M (1002:1114, RADV KRACKAN1), Mesa 26.2.5, Fedora 44 (kernel 7.2.14), GCC 13.3 (Ubuntu 24.04 container, as CI), Release, ANYPS5_REQUIRE_VULKAN=1. Because #9385 merged this morning, I applied only your commit 57190a6629 onto main 88e804b0a7. Its CMakeLists.txt hunk goes into tests/cmake/agc_driver_gds_dma_fill_tests.cmake, as on your head 25d850e2bd.

RADV 860M lavapipe
main: agc_driver_gds_dma_fill 3/3 pass 3/3 pass
this PR: agc_driver_gds_dma_fill 5/5 fail 3/3 pass
this PR: agc_driver_gds_dma_fill_private 3/3 pass 3/3 pass

Why: the shared GDS is created, so GdsImportable() returns true, but its import fails:

[gpu] host import of 0x200000000+0x10000 failed at vkAllocateMemory (-1000072003); falling back to copies, requested by build
GDS DMA fill: a fill of the shared GDS behind a dispatch still using it drained the device

-1000072003 is VK_ERROR_INVALID_EXTERNAL_HANDLE. On Linux, SHM_ANON is a memfd_create mapping (core/libs/prx/libkernel/File/src/SharedMemory.cpp:707), and RADV refuses host-pointer imports of memfd-backed shared mappings. #13407 hit the same refusal with the memfd test fixtures. The other host imports in the same run (anonymous memory) succeed.

What it costs: with the test's check made non-fatal (a local change only), the run goes to the end, and only these five path checks fail:

  • a fill of the shared GDS behind a dispatch still using it drained the device;
  • a fill of the shared GDS behind a dispatch still using it was not stored on the GPU;
  • the copy into the shared GDS behind the dispatch writing its source was not recorded on the GPU;
  • the copy out of the shared GDS behind the dispatches using it was not recorded on the GPU;
  • the fill of the shared GDS behind the GPU copy into it was not stored on the GPU.

Every value check passes, so RADV falls back to #9385's behaviour and nothing reads wrong data.

The private mapping imports on RADV: as a second local experiment, I kept the private GDS (APS5_NO_SHARED_GDS=1) but set gdsImportable for it when the device requests an importable GDS. Then the log shows host import of 0x200000000+0x10000 ok, and every GPU-path check of the test passes. The only failure is the expected "the GDS is in shared memory although that was not requested or is switched off", which comes from the experiment itself.

So on RADV it's the other way round from MoltenVK: the private mapping imports and the shared one doesn't. Two ways that would cover both, if they fit your design:

  1. Count the GDS as importable only once its import has succeeded, so that a refused import keeps perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's path, and have the test expect that path then.
  2. Request the shared GDS only where the private mapping can't be imported, and mark the private mapping importable where it can.

Full ^agc_ on RADV with this PR: 1216 of 1219 pass. Besides agc_driver_gds_dma_fill, the only failures are agc_driver_tessellation (a crash in Mesa 26.2.5's RADV compiler) and agc_idle_publish_wait (#11889). Both fail on main on this machine too.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi Gonic0,

Thanks for the PR. Lander0606's RADV run (Radeon 860M, Mesa 26.2.5) matches what the code does at 25d850e. gdsImportable is set as soon as the shared shm_open/mmap mapping succeeds (Pm4.cpp:53), before any Vulkan import is tried. RADV then refuses the host import of that memfd-backed mapping (VK_ERROR_INVALID_EXTERNAL_HANDLE). The runtime falls back to copies, but agc_driver_gds_dma_fill still expects the GPU path whenever GdsImportable() is true (GdsDmaFill.cpp:150-151 and the path checks after it), so it fails 5/5 on that GPU.

Could you make importability depend on the import actually succeeding? Either set the flag only after the first successful host import, or fall back to the private mapping when the shared one is refused. The test should then expect #9385's copy path whenever the import was refused. A run on a RADV integrated GPU, or Lander0606's results, on the new head would confirm it.

Gonic0 and others added 2 commits November 2, 2026 09:57
…Us and keep its DMA_DATA on the GPU

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared GDS was taken as importable as soon as its shm_open mapping
existed, before any Vulkan import was tried. RADV refuses host-pointer
imports of memfd-backed shared mappings (VK_ERROR_INVALID_EXTERNAL_HANDLE),
so the driver fell back to copies while GdsImportable() still reported the
GPU path. Import the GDS when the device is created and drop the flag when
the import is refused, so such devices keep boykopovar#9385's path. GdsShared() tells
the shared mapping apart from a working import; the test checks the mapping
against the request and expects the copy path when the import was refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Gonic0
Gonic0 force-pushed the perf/agc-gds-on-gpu branch from 25d850e to 9e874bb Compare November 2, 2026 13:01
@Gonic0

Gonic0 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks both, and thanks Lander0606 for the RADV run. The branch is rebased onto main 0278ca9831, which already has #9385, so it is now one commit with no merges. 9e874bbf7d follows the first option.

  • Probe at device creation: when the device asks for an importable GDS, it imports the GDS right away. If that import is refused, RejectGdsImport() clears the flag, so a device like RADV, which refuses memfd-backed shared mappings, keeps perf(agc): write DMA_DATA fills of an idle GDS without draining #9385's copy path.
  • Two separate states: GdsShared() reports the shared mapping, and GdsImportable() now means that the import worked.
  • Test:
    • agc_driver_gds_dma_fill checks the mapping against the request through GdsShared().
    • It expects the GPU path only when GdsImportable() is true, and checks that the GDS is never importable without the shared mapping.
    • When the import was refused, it prints that it is checking the copy path.
  • Test registration: the agc_driver_gds_dma_fill_private registration moved to tests/cmake/agc_driver_gds_dma_fill_tests.cmake.

Tested on macOS 27.2, Apple M5 Pro, MoltenVK 1.4.2 (Vulkan SDK 1.4.363.0). main still does not build libkernel on macOS, so I ran this on a tree with the change applied over local macOS build fixes.

case result
agc_driver_gds_dma_fill (shared GDS imports) pass
private mapping (APS5_NO_SHARED_GDS=1) pass
refusal forced in that tree only (the probe treated as refused) pass: "the device refused the host import of the shared GDS: the copy path is checked"
every gds / pm4 test (18) pass

A run on a RADV integrated GPU would confirm the real refusal.

AI-assisted: yes

🤖 Generated with Claude Code

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
@oneandonlydean
oneandonlydean merged commit 8009be6 into boykopovar:main Nov 2, 2026
11 checks passed
ZSWolt added a commit to ZSWolt/AnyPS5 that referenced this pull request Nov 2, 2026
…d-append

The shared GDS mapping from boykopovar#13159 takes GdsBindingBytes, as its import check does, so the ordered-append turn and timeout count past the 64 KiB GDS stay mapped; disk cache format 52 follows main's 51.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs perf Performance change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants