Repository navigation
fix(agc): merge the pipeline cache file instead of overwriting it with one device's cache - #4915
Conversation
MarcZX10
left a comment
There was a problem hiding this comment.
I compared this save path with #4795 and issue #4781. The pathMutex here serializes file access between devices, but it does not synchronize either vkGetPipelineCacheData call in save() with concurrent vkCreate*Pipelines calls on the same live cache. #4795 adds that device-cache synchronization for Intel and its test exercises snapshot-vs-pipeline use. Could you clarify whether #4915 should depend on #4795 or include equivalent synchronization? Otherwise the file merge can still hit the exact Intel saver race reported in #4781.
|
You are right that I propose #4795 first and have updated the description to The current RTX 3060 tests cover the file-loss bug. They do not reproduce or validate the Intel saver crash; I will keep that distinction in the description. AI-assisted: yes. |
0c07a99 to
2c62442
Compare
|
Thanks for confirming the ordering and updating the dependency. I agree #4795 should land first: its device-cache guard needs to cover the live-cache reads and merge, while the file mutex continues to protect the file transaction between cache objects. I also agree the RTX 3060 validation should stay distinct from the unreproduced Intel saver crash. I’ll review the combined result after the rebase. AI-assisted: yes; reviewed by MarcZX100. |
b0ffde6 to
d495299
Compare
|
Update: #4795 has merged, so the device-cache synchronization you listed as a prerequisite is now on |
d495299 to
f2efd60
Compare
|
Rebased onto main The combined source was independently checked against the complete expected merge tree, with original authors and commit messages preserved. This update is source-only; the earlier RTX 3060 file-loss tests do not validate or reproduce the Intel saver crash. Thank you for reviewing the guard placement. AI-assisted: yes. Co-Authored-By: Codex noreply@openai.com |
f2efd60 to
446356d
Compare
…h one device's cache The driver keeps the replaced headless VulkanDevice alive next to the windowed one, so a process holds two Graphics::PipelineCache objects for the same vk-pipelines-*.bin. Each saved its own VkPipelineCache over the whole file, periodically and at teardown, so the last writer dropped the other object's pipelines and those of earlier runs. A save now reads the file under a per-path mutex, validates it as the load does, creates a temporary cache from it and merges this object's cache into that temporary one, then writes the result atomically. Merging into the temporary cache keeps the live cache free of the external synchronisation vkMergePipelineCaches requires of its destination. A merge that adds nothing leaves the file as it is, an unreadable file is not written over, and a result smaller than the file on disk or the data loaded at start is not written; an invalid file or one the driver refuses is replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
446356d to
8c8cd86
Compare
|
Rebased onto main Conflicts:
Guard:
The path mutex still covers the file read, merge and write. Reads of the private temporary merged cache and the file I/O stay outside the guard. Tests: Windows Server 2025 10.0.26100, NVIDIA GeForce RTX 3060 12 GB with driver 591.86 (from
As before, these tests cover the file-loss bug. They do not reproduce or validate the Intel saver crash from #4781. AI-assisted: yes. |
tools/check_skips.py accepts only device-limitation skip reasons, so the Linux job rejected this one.
|
Pushed a one-line fix: the Linux full build rejected the |
What
A process holds two
Graphics::PipelineCacheobjects for one cache file.Driver::AttachWindowkeeps the replaced headlessVulkanDeviceinreplacedDevicesand creates the windowed one. Each device builds its ownPipelineCachein its constructor, and both resolve to the samevk-pipelines-<vendor>-<device>-<driver>-<uuid>.bin. On main,PipelineCache::savewrote the whole file from its ownVkPipelineCachealone. It ran from the periodicrun()thread every 10 s and once more from the destructor. It wrote whenever the cache size differed fromsavedBytes, which is the size this object last loaded or wrote. Nothing checked what the other object had stored, so the last writer won.Evidence from Demon's Souls (PPSA01342) runs on this machine, from the
[pipeline-cache]log lines:savedBytesand it never wrote again. The process then crashed, so neither teardown save ran.loaded 31289.8 KiBlines at start, then saves of 31651.1 KiB and 32172.1 KiB.The fix keeps one
VkPipelineCacheper device and changes how a save writes the file:vkMergePipelineCacheswith the temporary cache as destination and this object's cache as source. It then writes the merged data with the existing atomic write.dstCacheto be externally synchronised, and worker threads create pipelines with the live cache at any time. So the live cache is only ever a merge source.cannot readinstead of starting empty silently.Behaviour change: a save that has something to write now also reads the file and has the driver parse it. The
saved ... in N msline includes that time and says(merged with the file)when a merge happened. No speed claim is made.Not covered: two AnyPS5 processes sharing one cache directory. The atomic write keeps the file valid, but one process can still drop pipelines the other wrote between its read and its rename.
Related
#4795 (MarcZX100, open) serializes
PipelineCache::save()'svkGetPipelineCacheDatacalls against pipeline creation with a shared/exclusive lock, as a workaround for the Intel crash in #4781. It conflicts with this PR inGraphics/src/PipelineCache.cpp, but the two are complementary: #4795 decides when the live cache may be read, this PR decides what is written to the file. Merge #4795 first. This PR will then be rebased onto its device-cache guard, preserving exclusive access for the initial live-cache size read and the merge that reads the live cache; reads of the temporary merged cache and file I/O stay outside that guard. The file-path mutex still covers the file read/merge/write transaction.Tested
Windows Server 2025, RTX 3060, GCC 15.2,
build-dev(Ninja).New test
agc_pipeline_cache_file(agc_driver_recorder_tests --pipeline-cache-file-only). TwoPipelineCacheobjects on the same path each build a different compute pipeline (SampleLod.comp,SampleArray.comp) and are destroyed in both orders. A third object loads the file, and each pipeline is built into its cache again.vkGetPipelineCacheDatamust report the same size afterwards, which means the pipeline was already in the cache. The test also checks two more cases:It exits with 77 if the driver keeps nothing in a pipeline cache.
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BITis not used, because the test device does not enablepipelineCreationCacheControl.AGC graphics: the cache file lost the pipeline of the object that saved first. The objects saved 6.2 KiB and 6.4 KiB, and the third object loaded 6.4 KiB.(merged with the file)in both orders. It passed 10 of 10 runs with--repeat until-fail:10.Every ctest registration of the touched executables passed, 26 tests in all:
agc_driver_recorder_tests, includingagc_pipeline_cacheagc_shader_disk_cacheVulkanDevicetestsagc_buffer_pool,agc_compute_pipeline_capture,agc_driver_buffer_load_store,agc_driver_cmask_fast_clear,agc_driver_copied_writer_copy,agc_driver_d16_memory,agc_driver_depth_plane_guest_layersandagc_driver_draw_offset_indicespython -I tools/check_test_registration.py build-dev: 741 test executables, none built and not run.Not run: the other
VulkanDevicetest executables of libSceAgcDriver, lavapipe, Linux, and any title with this change. The floor that refuses a smaller result is not reached by any test, because merges only add entries on the tested driver.Open PR fix(agc): serialize pipeline cache snapshots #4795 ("fix(agc): serialize pipeline cache snapshots") implements the separate device-cache synchronization. Its diff was inspected; this PR depends on it and must preserve its guard after rebase.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code