aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgivethesourceplox <272928635+givethesourceplox@users.noreply.github.com>2026-10-30 09:45:53 -0300
committerkraibse <33869366+kraibse@users.noreply.github.com>2026-10-30 17:48:04 +0100
commitea33ad1865970ee731e5f74dbcc26ca472798afe (patch)
treef73345d1d273bcbab8dfbd708a459b8ba6afee0b
parent6f5e137ec4eb83bbff9ea15f1ad1a8407cfc0cba (diff)
vulkan: write the driver pipeline cache atomically; reject truncated ones
The emulator stopped while saving vulkan_pipelines.bin left it empty, and every later boot terminated: the header read throws ios_base::failure, and macOS's libc++ throws one the handler's type doesn't match. Write the cache to a temporary file and rename it over the old one, and treat a file shorter than its header as invalid without reading it. Assisted-by: Claude (Anthropic)
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp23
1 files changed, 22 insertions, 1 deletions
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index ac878ddfef..2c64f3375b 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -1138,7 +1138,11 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename,
const vk::PipelineCache& pipeline_cache,
u32 cache_version) try {
- std::ofstream file(filename, std::ios::binary);
+ // Written beside the cache and renamed over it: the emulator stopped mid-write left the cache
+ // empty, and every later boot failed to read it.
+ auto temporary = filename;
+ temporary += ".tmp";
+ std::ofstream file(temporary, std::ios::binary);
file.exceptions(std::ifstream::failbit);
if (!file.is_open()) {
LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}",
@@ -1156,6 +1160,15 @@ void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& fi
pipeline_cache.Read(&cache_size, cache_data.data());
}
file.write(cache_data.data(), cache_size);
+ file.close();
+ std::error_code error;
+ std::filesystem::rename(temporary, filename, error);
+ if (error) {
+ LOG_ERROR(Common_Filesystem, "Failed to replace Vulkan driver pipeline cache file {}: {}",
+ Common::FS::PathToUTF8String(filename), error.message());
+ std::filesystem::remove(temporary, error);
+ return;
+ }
LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}",
Common::FS::PathToUTF8String(filename));
@@ -1190,6 +1203,14 @@ vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::
std::array<char, 8> magic_number;
u32 cache_version;
+ // A file cut short (the emulator stopped while writing it) would throw from the reads
+ // below, and macOS's libc++ throws an ios_base::failure the handler doesn't match.
+ if (static_cast<size_t>(end) < magic_number.size() + sizeof(cache_version)) {
+ file.close();
+ LOG_ERROR(Common_Filesystem, "Truncated Vulkan driver pipeline cache file, deleting it");
+ Common::FS::RemoveFile(filename);
+ return create_pipeline_cache(0, nullptr);
+ }
file.read(magic_number.data(), magic_number.size())
.read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version));
if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) {