diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index a9793c43c..79c0afaca 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -1254,20 +1254,19 @@ static std::size_t pread(int fd, void* buf, std::size_t count, uint64_t offset) #define pread ::pread #endif -std::size_t IOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) { +std::size_t IOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) { if (!IsOpen()) { m_good = false; return std::numeric_limits::max(); } - if (length == 0) { + if (byte_count == 0) { return 0; } DEBUG_ASSERT(data != nullptr); - return pread(fileno(m_file), data, data_size * length, offset); + return pread(fileno(m_file), data, byte_count, offset); } std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { @@ -1315,19 +1314,19 @@ struct CryptoIOFileImpl { std::size_t res = f.IOFile::ReadImpl(data, length, data_size); if (res != std::numeric_limits::max() && res != 0) { d.ProcessData(reinterpret_cast(data), - reinterpret_cast(data), length * data_size); + reinterpret_cast(data), res * data_size); e.Seek(f.IOFile::Tell()); } return res; } - std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size, + std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t byte_count, std::size_t offset) { - std::size_t res = f.IOFile::ReadAtImpl(data, length, data_size, offset); + std::size_t res = f.IOFile::ReadAtImpl(data, byte_count, offset); if (res != std::numeric_limits::max() && res != 0) { d.Seek(offset); d.ProcessData(reinterpret_cast(data), - reinterpret_cast(data), length * data_size); + reinterpret_cast(data), res); e.Seek(f.IOFile::Tell()); } return res; @@ -1378,9 +1377,8 @@ std::size_t CryptoIOFile::ReadImpl(void* data, std::size_t length, std::size_t d return impl->ReadImpl(*this, data, length, data_size); } -std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) { - return impl->ReadAtImpl(*this, data, length, data_size, offset); +std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) { + return impl->ReadAtImpl(*this, data, byte_count, offset); } std::size_t CryptoIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { diff --git a/src/common/file_util.h b/src/common/file_util.h index 4c7d21349..98d232dcf 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -302,6 +302,7 @@ public: virtual bool Close(); + /// Returns the amount of T items read template std::size_t ReadArray(T* data, std::size_t length) { static_assert(std::is_trivially_copyable_v, @@ -314,16 +315,18 @@ public: return items_read; } + /// Returns the amount of bytes read template std::size_t ReadAtArray(T* data, std::size_t length, std::size_t offset) { static_assert(std::is_trivially_copyable_v, "Given array does not consist of trivially copyable objects"); - std::size_t items_read = ReadAtImpl(data, length, sizeof(T), offset); - if (items_read != length) + const size_t bytes = length * sizeof(T); + std::size_t size_read = ReadAtImpl(data, bytes, offset); + if (size_read != bytes) m_good = false; - return items_read; + return size_read; } template @@ -466,8 +469,7 @@ protected: virtual bool Open(); virtual std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size); - virtual std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset); + virtual std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset); virtual std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size); virtual bool SeekImpl(s64 off, int origin); @@ -520,8 +522,7 @@ private: std::unique_ptr impl; std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override; - std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) override; + std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override; std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override; bool SeekImpl(s64 off, int origin) override; diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index e177f24d9..3257fa2d8 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp @@ -357,8 +357,7 @@ std::size_t Z3DSWriteIOFile::ReadImpl(void* data, std::size_t length, std::size_ return 0; } -std::size_t Z3DSWriteIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) { +std::size_t Z3DSWriteIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) { // Stubbed UNIMPLEMENTED(); return 0; @@ -619,12 +618,12 @@ bool Z3DSReadIOFile::Open() { } std::size_t Z3DSReadIOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) { - return impl->Read(data, length * data_size); + size_t res = impl->Read(data, length * data_size); + return res == std::numeric_limits::max() ? res : (res / data_size); } -std::size_t Z3DSReadIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) { - return impl->ReadAt(data, length * data_size, offset); +std::size_t Z3DSReadIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) { + return impl->ReadAt(data, byte_count, offset); } std::size_t Z3DSReadIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h index bd1d990dd..0d3372bd5 100644 --- a/src/common/zstd_compression.h +++ b/src/common/zstd_compression.h @@ -183,8 +183,7 @@ private: bool Open() override; std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override; - std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) override; + std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override; std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override; bool SeekImpl(s64 off, int origin) override; @@ -250,8 +249,7 @@ private: bool Open() override; std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override; - std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, - std::size_t offset) override; + std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override; std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override; bool SeekImpl(s64 off, int origin) override; diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 29232d4be..1438a766a 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -168,7 +168,6 @@ Loader::ResultStatus NCCHContainer::LoadHeader() { ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic); ASSERT(partition < 8); ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize; - LOG_ERROR(Service_FS, "{}", ncch_offset); file->Seek(ncch_offset, SEEK_SET); file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8c8171495..97b7cc8b0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -144,24 +144,59 @@ void PipelineCache::BuildLayout() { } PipelineCache::~PipelineCache() { - SaveDiskCache(); + workers.WaitForRequests(); + SaveDriverPipelineDiskCache(); } -void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading, - const VideoCore::DiskResourceLoadCallback& callback) { +void PipelineCache::LoadCache(const std::atomic_bool& stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) { + LoadDriverPipelineDiskCache(stop_loading, callback); + LoadDiskCache(stop_loading, callback); +} + +void PipelineCache::SwitchCache(u64 title_id, const std::atomic_bool& stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) { + if (GetProgramID() == title_id) { + LOG_DEBUG(Render_Vulkan, + "Skipping pipeline cache switch - already using cache for title_id={:016X}", + title_id); + return; + } + + // Make sure we have a valid pipeline cache before switching + if (!driver_pipeline_cache) { + vk::PipelineCacheCreateInfo cache_info{}; + try { + driver_pipeline_cache = instance.GetDevice().createPipelineCacheUnique(cache_info); + } catch (const vk::SystemError& err) { + LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what()); + return; + } + } + + LOG_INFO(Render_Vulkan, "Switching pipeline cache to title_id={:016X}", title_id); + + // Save current driver cache, update program ID and load the new driver cache + SaveDriverPipelineDiskCache(); + SetProgramID(title_id); + LoadDriverPipelineDiskCache(stop_loading, nullptr); + + // Switch the disk shader cache after driver cache is switched + SwitchDiskCache(title_id, stop_loading, callback); +} + +void PipelineCache::LoadDriverPipelineDiskCache( + const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { vk::PipelineCacheCreateInfo cache_info{}; - if (callback) { - callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, ""); - } if (callback) { callback(VideoCore::LoadCallbackStage::Build, 0, 1, "Driver Pipeline Cache"); } - auto load_cache = [this, &cache_info](bool allow_fallback) { + auto load_cache = [this, &cache_info, &callback](bool allow_fallback) { const vk::Device device = instance.GetDevice(); try { - pipeline_cache = device.createPipelineCacheUnique(cache_info); + driver_pipeline_cache = device.createPipelineCacheUnique(cache_info); } catch (const vk::SystemError& err) { LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what()); if (allow_fallback) { @@ -169,13 +204,16 @@ void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading, cache_info.initialDataSize = 0; cache_info.pInitialData = nullptr; try { - pipeline_cache = device.createPipelineCacheUnique(cache_info); + driver_pipeline_cache = device.createPipelineCacheUnique(cache_info); } catch (const vk::SystemError& err) { LOG_ERROR(Render_Vulkan, "Failed to create fallback pipeline cache: {}", err.what()); } } } + if (callback) { + callback(VideoCore::LoadCallbackStage::Build, 1, 1, "Driver Pipeline Cache"); + } }; // Try to load existing pipeline cache if disk cache is enabled and directories exist @@ -226,19 +264,9 @@ void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading, load_cache(true); } -void PipelineCache::LoadDiskCache(const std::atomic_bool& stop_loading, - const VideoCore::DiskResourceLoadCallback& callback) { - - disk_caches.clear(); - curr_disk_cache = - disk_caches.emplace_back(std::make_shared(*this, GetProgramID())); - - curr_disk_cache->Init(stop_loading, callback); -} - -void PipelineCache::SaveDiskCache() { +void PipelineCache::SaveDriverPipelineDiskCache() { // Save Vulkan pipeline cache - if (!Settings::values.use_disk_shader_cache || !pipeline_cache) { + if (!Settings::values.use_disk_shader_cache || !driver_pipeline_cache) { return; } @@ -258,13 +286,81 @@ void PipelineCache::SaveDiskCache() { } const vk::Device device = instance.GetDevice(); - const auto cache_data = device.getPipelineCacheData(*pipeline_cache); + const auto cache_data = device.getPipelineCacheData(*driver_pipeline_cache); if (cache_file.WriteBytes(cache_data.data(), cache_data.size()) != cache_data.size()) { LOG_ERROR(Render_Vulkan, "Error during pipeline cache write"); return; } } +void PipelineCache::LoadDiskCache(const std::atomic_bool& stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) { + + disk_caches.clear(); + curr_disk_cache = + disk_caches.emplace_back(std::make_shared(*this, GetProgramID())); + + curr_disk_cache->Init(stop_loading, callback); +} + +void PipelineCache::SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) { + // NOTE: curr_disk_cache can be null if emulation restarted without calling + // LoadDefaultDiskResources + + // Check if the current cache is for the specified TID. + if (curr_disk_cache && curr_disk_cache->GetProgramID() == title_id) { + return; + } + + // Search for an existing manager + size_t new_pos = 0; + for (new_pos = 0; new_pos < disk_caches.size(); new_pos++) { + if (disk_caches[new_pos]->GetProgramID() == title_id) { + break; + } + } + // Manager does not exist, create it and append to the end + if (new_pos >= disk_caches.size()) { + new_pos = disk_caches.size(); + auto& new_manager = + disk_caches.emplace_back(std::make_shared(*this, title_id)); + + new_manager->Init(stop_loading, callback); + } + + auto is_applet = [](u64 tid) { + constexpr u32 APPLET_TID_HIGH = 0x00040030; + return static_cast(tid >> 32) == APPLET_TID_HIGH; + }; + + bool prev_applet = curr_disk_cache ? is_applet(curr_disk_cache->GetProgramID()) : false; + bool new_applet = is_applet(disk_caches[new_pos]->GetProgramID()); + curr_disk_cache = disk_caches[new_pos]; + + if (prev_applet) { + // If we came from an applet, clean up all other applets + for (auto it = disk_caches.begin(); it != disk_caches.end();) { + if (it == disk_caches.begin() || *it == curr_disk_cache || + !is_applet((*it)->GetProgramID())) { + it++; + continue; + } + it = disk_caches.erase(it); + } + } + if (!new_applet) { + // If we are going into a non-applet, clean up everything + for (auto it = disk_caches.begin(); it != disk_caches.end();) { + if (it == disk_caches.begin() || *it == curr_disk_cache) { + it++; + continue; + } + it = disk_caches.erase(it); + } + } +} + bool PipelineCache::BindPipeline(PipelineInfo& info, bool wait_built) { MICROPROFILE_SCOPE(Vulkan_Bind); @@ -551,112 +647,4 @@ std::string PipelineCache::GetTransferableDir() const { return GetVulkanDir() + DIR_SEP + "transferable"; } -void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading, - const VideoCore::DiskResourceLoadCallback& callback) { - if (!Settings::values.use_disk_shader_cache || GetProgramID() == title_id) { - LOG_DEBUG(Render_Vulkan, - "Skipping pipeline cache switch - already using cache for title_id={:016X}", - title_id); - return; - } - - if (callback) { - callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, ""); - } - if (callback) { - callback(VideoCore::LoadCallbackStage::Build, 0, 1, "Driver Pipeline Cache"); - } - - // Make sure we have a valid pipeline cache before switching - if (!pipeline_cache) { - vk::PipelineCacheCreateInfo cache_info{}; - try { - pipeline_cache = instance.GetDevice().createPipelineCacheUnique(cache_info); - } catch (const vk::SystemError& err) { - LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what()); - return; - } - } - - LOG_INFO(Render_Vulkan, "Switching pipeline cache to title_id={:016X}", title_id); - - // Save current cache before switching - SaveDiskCache(); - - // Update program ID and load the new pipeline cache - SetProgramID(title_id); - LoadPipelineDiskCache(stop_loading, nullptr); - SwitchDiskCache(title_id, stop_loading, callback); - - if (callback) { - callback(VideoCore::LoadCallbackStage::Complete, 0, 0, ""); - } -} - -void PipelineCache::SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading, - const VideoCore::DiskResourceLoadCallback& callback) { - // NOTE: curr_disk_cache can be null if emulation restarted without calling - // LoadDefaultDiskResources - - // Check if the current cache is for the specified TID. - if (curr_disk_cache && curr_disk_cache->GetProgramID() == title_id) { - return; - } - - // Search for an existing manager - size_t new_pos = 0; - for (new_pos = 0; new_pos < disk_caches.size(); new_pos++) { - if (disk_caches[new_pos]->GetProgramID() == title_id) { - break; - } - } - // Manager does not exist, create it and append to the end - if (new_pos >= disk_caches.size()) { - new_pos = disk_caches.size(); - auto& new_manager = - disk_caches.emplace_back(std::make_shared(*this, title_id)); - - if (callback) { - callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, ""); - } - - new_manager->Init(stop_loading, callback); - - if (callback) { - callback(VideoCore::LoadCallbackStage::Complete, 0, 0, ""); - } - } - - auto is_applet = [](u64 tid) { - constexpr u32 APPLET_TID_HIGH = 0x00040030; - return static_cast(tid >> 32) == APPLET_TID_HIGH; - }; - - bool prev_applet = curr_disk_cache ? is_applet(curr_disk_cache->GetProgramID()) : false; - bool new_applet = is_applet(disk_caches[new_pos]->GetProgramID()); - curr_disk_cache = disk_caches[new_pos]; - - if (prev_applet) { - // If we came from an applet, clean up all other applets - for (auto it = disk_caches.begin(); it != disk_caches.end();) { - if (it == disk_caches.begin() || *it == curr_disk_cache || - !is_applet((*it)->GetProgramID())) { - it++; - continue; - } - it = disk_caches.erase(it); - } - } - if (!new_applet) { - // If we are going into a non-applet, clean up everything - for (auto it = disk_caches.begin(); it != disk_caches.end();) { - if (it == disk_caches.begin() || *it == curr_disk_cache) { - it++; - continue; - } - it = disk_caches.erase(it); - } - } -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 2bb9c750d..111960073 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -58,15 +58,13 @@ public: offsets[binding] = offset; } - /// Loads the pipeline cache stored to disk - void LoadPipelineDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false}, - const VideoCore::DiskResourceLoadCallback& callback = {}); + /// Loads the driver pipeline cache and the disk shader cache + void LoadCache(const std::atomic_bool& stop_loading = std::atomic_bool{false}, + const VideoCore::DiskResourceLoadCallback& callback = {}); - void LoadDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false}, - const VideoCore::DiskResourceLoadCallback& callback = {}); - - /// Stores the generated pipeline cache to disk - void SaveDiskCache(); + /// Switches the driver pipeline cache and the shader disk cache to the specified title + void SwitchCache(u64 title_id, const std::atomic_bool& stop_loading = std::atomic_bool{false}, + const VideoCore::DiskResourceLoadCallback& callback = {}); /// Binds a pipeline using the provided information bool BindPipeline(PipelineInfo& info, bool wait_built = false); @@ -90,11 +88,6 @@ public: /// Binds a fragment shader generated from PICA state void UseFragmentShader(const Pica::RegsInternal& regs, const Pica::Shader::UserConfig& user); - /// Switches the shader disk cache to the specified title - void SwitchPipelineCache(u64 title_id, - const std::atomic_bool& stop_loading = std::atomic_bool{false}, - const VideoCore::DiskResourceLoadCallback& callback = {}); - /// Gets the current program ID u64 GetProgramID() const { return current_program_id; @@ -111,6 +104,17 @@ public: private: friend ShaderDiskCache; + /// Loads the driver pipeline cache + void LoadDriverPipelineDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false}, + const VideoCore::DiskResourceLoadCallback& callback = {}); + + /// Stores the generated pipeline cache + void SaveDriverPipelineDiskCache(); + + /// Loads the shader disk cache + void LoadDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false}, + const VideoCore::DiskResourceLoadCallback& callback = {}); + /// Switches the disk cache at runtime to use a different title ID void SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback); @@ -140,7 +144,7 @@ private: DescriptorUpdateQueue& update_queue; Pica::Shader::Profile profile{}; - vk::UniquePipelineCache pipeline_cache; + vk::UniquePipelineCache driver_pipeline_cache; vk::UniquePipelineLayout pipeline_layout; std::size_t num_worker_threads; Common::ThreadWorker workers; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b38818f88..7a27032e9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -152,10 +152,13 @@ void RasterizerVulkan::LoadDefaultDiskResources( program_id = 0; } + if (callback) { + callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, ""); + } + pipeline_cache.SetProgramID(program_id); pipeline_cache.SetAccurateMul(accurate_mul); - pipeline_cache.LoadPipelineDiskCache(stop_loading, callback); - pipeline_cache.LoadDiskCache(stop_loading, callback); + pipeline_cache.LoadCache(stop_loading, callback); if (callback) { callback(VideoCore::LoadCallbackStage::Complete, 0, 0, ""); @@ -985,8 +988,17 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) { void RasterizerVulkan::SwitchDiskResources(u64 title_id) { std::atomic_bool stop_loading = false; + + if (switch_disk_resources_callback) { + switch_disk_resources_callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, ""); + } + pipeline_cache.SetAccurateMul(accurate_mul); - pipeline_cache.SwitchPipelineCache(title_id, stop_loading, switch_disk_resources_callback); + pipeline_cache.SwitchCache(title_id, stop_loading, switch_disk_resources_callback); + + if (switch_disk_resources_callback) { + switch_disk_resources_callback(VideoCore::LoadCallbackStage::Complete, 0, 0, ""); + } } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_disk_cache.cpp b/src/video_core/renderer_vulkan/vk_shader_disk_cache.cpp index b3f1f0613..15e45ce81 100644 --- a/src/video_core/renderer_vulkan/vk_shader_disk_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_disk_cache.cpp @@ -59,6 +59,9 @@ static VideoCore::DiskResourceLoadCallback MakeThrottledCallback( void ShaderDiskCache::Init(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { + if (!Settings::values.use_disk_shader_cache) + return; + auto new_callback = MakeThrottledCallback(callback); if (!stop_loading && !InitVSCache(stop_loading, new_callback)) { @@ -248,7 +251,7 @@ GraphicsPipeline* ShaderDiskCache::GetPipeline(const PipelineInfo& info) { parent.UseTrivialGeometryShader(); } it.value() = std::make_unique( - parent.instance, parent.renderpass_cache, info, *parent.pipeline_cache, + parent.instance, parent.renderpass_cache, info, *parent.driver_pipeline_cache, *parent.pipeline_layout, parent.current_shaders, &parent.workers); } @@ -339,86 +342,129 @@ ShaderDiskCache::CacheEntry ShaderDiskCache::CacheFile::ReadAt(size_t position) } size_t ShaderDiskCache::CacheFile::GetTotalEntries() { - if (biggest_entry_id != SIZE_MAX) { - return biggest_entry_id + 1; + if (!file.IsGood()) { + next_entry_id = SIZE_MAX; + return next_entry_id; + } + + if (next_entry_id != SIZE_MAX) { + return next_entry_id; } const size_t file_size = file.GetSize(); + if (file_size == 0) { + next_entry_id = 0; + return next_entry_id; + } + CacheEntry::CacheEntryFooter footer{}; if (file.ReadAtArray(&footer, 1, file_size - sizeof(footer)) == sizeof(footer) && footer.version == CacheEntry::CacheEntryFooter::ENTRY_VERSION) { - biggest_entry_id = footer.entry_id; + next_entry_id = footer.entry_id + 1; + } else { + return SIZE_MAX; } - return biggest_entry_id + 1; + return next_entry_id; } -bool ShaderDiskCache::CacheFile::Append(CacheEntryType type, u64 id, std::span data, +void ShaderDiskCache::CacheFile::Append(CacheEntryType type, u64 id, std::span data, bool compress) { - std::scoped_lock lock(mutex); - - std::span data_final; - std::vector data_compress; - - CacheEntry::CacheEntryHeader header{}; - CacheEntry::CacheEntryFooter footer{}; - - constexpr u32 headers_size = - sizeof(CacheEntry::CacheEntryHeader) + sizeof(CacheEntry::CacheEntryFooter); - - if (compress) { - data_compress = Common::Compression::CompressDataZSTDDefault(data); - data_final = data_compress; - header.zstd_compressed.Assign(true); - } else { - data_final = data; + if (curr_mode != CacheOpMode::APPEND) { + return; } - header.entry_version = CacheEntry::CacheEntryHeader::ENTRY_VERSION; - footer.version.Assign(CacheEntry::CacheEntryFooter::ENTRY_VERSION); - header.entry_size = footer.entry_size = data_final.size() + headers_size; - footer.entry_id.Assign(biggest_entry_id++); - header.type = type; - header.id = id; + std::vector copy_data(data.begin(), data.end()); - std::vector out_data(data_final.size() + headers_size); - memcpy(out_data.data(), &header, sizeof(header)); - memcpy(out_data.data() + sizeof(header), data_final.data(), data_final.size()); - memcpy(out_data.data() + sizeof(header) + data_final.size(), &footer, sizeof(footer)); + append_worker.QueueWork([this, type, id, compress, data = std::move(copy_data)]() { + if (next_entry_id == SIZE_MAX || !file.IsGood()) { + return; + } - return file.WriteBytes(out_data.data(), out_data.size()) == out_data.size(); + std::span data_final; + std::vector data_compress; + + CacheEntry::CacheEntryHeader header{}; + CacheEntry::CacheEntryFooter footer{}; + + constexpr u32 headers_size = + sizeof(CacheEntry::CacheEntryHeader) + sizeof(CacheEntry::CacheEntryFooter); + + if (compress) { + data_compress = Common::Compression::CompressDataZSTDDefault(data); + data_final = data_compress; + header.zstd_compressed.Assign(true); + } else { + data_final = data; + } + header.entry_version = CacheEntry::CacheEntryHeader::ENTRY_VERSION; + footer.version.Assign(CacheEntry::CacheEntryFooter::ENTRY_VERSION); + header.entry_size = footer.entry_size = data_final.size() + headers_size; + footer.entry_id.Assign(next_entry_id++); + + header.type = type; + header.id = id; + + std::vector out_data(data_final.size() + headers_size); + memcpy(out_data.data(), &header, sizeof(header)); + memcpy(out_data.data() + sizeof(header), data_final.data(), data_final.size()); + memcpy(out_data.data() + sizeof(header) + data_final.size(), &footer, sizeof(footer)); + + file.WriteBytes(out_data.data(), out_data.size()); + if (file.IsGood()) { + file.Flush(); + } + }); } bool ShaderDiskCache::CacheFile::SwitchMode(CacheOpMode mode) { + if (curr_mode == mode) { + return true; + } + if (curr_mode == CacheOpMode::APPEND) { + append_worker.WaitForRequests(); + } + switch (mode) { case CacheOpMode::READ: { + next_entry_id = SIZE_MAX; // Force reading entries agains file = FileUtil::IOFile(filepath, "rb"); - bool is_open = file.IsOpen(); + bool is_open = file.IsGood(); if (is_open) { GetTotalEntries(); } + curr_mode = mode; return is_open; } case CacheOpMode::APPEND: { - GetTotalEntries(); + if (!SwitchMode(CacheOpMode::READ)) { + curr_mode = mode; + return false; + } file.Close(); - if (biggest_entry_id == SIZE_MAX) { + curr_mode = mode; + if (next_entry_id == SIZE_MAX) { // Cannot append if getting total items fails return false; } file = FileUtil::IOFile(filepath, "ab"); - return file.IsOpen(); + return file.IsGood(); } case CacheOpMode::DELETE: { - biggest_entry_id = 0; + next_entry_id = SIZE_MAX; file.Close(); - FileUtil::Delete(filepath); - return true; + curr_mode = mode; + return FileUtil::Delete(filepath); } case CacheOpMode::RECREATE: { - SwitchMode(CacheOpMode::DELETE); + if (!SwitchMode(CacheOpMode::DELETE)) { + return false; + } + if (!FileUtil::CreateEmptyFile(filepath)) { + return false; + } return SwitchMode(CacheOpMode::APPEND); } default: @@ -827,8 +873,7 @@ bool ShaderDiskCache::InitVSCache(const std::atomic_bool& stop_loading, } // Switch to append mode to receive new entries. - vs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); - return true; + return vs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); } bool ShaderDiskCache::InitFSCache(const std::atomic_bool& stop_loading, @@ -1045,8 +1090,7 @@ bool ShaderDiskCache::InitFSCache(const std::atomic_bool& stop_loading, } // Switch to append mode to receive new entries. - fs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); - return true; + return fs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); } bool ShaderDiskCache::InitGSCache(const std::atomic_bool& stop_loading, @@ -1272,8 +1316,7 @@ bool ShaderDiskCache::InitGSCache(const std::atomic_bool& stop_loading, } // Switch to append mode to receive new entries. - gs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); - return true; + return gs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); } bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading, @@ -1400,7 +1443,7 @@ bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading, auto [it_pl, _] = graphics_pipelines.try_emplace(pl_hash_opt); it_pl.value() = std::make_unique( - parent.instance, parent.renderpass_cache, info, *parent.pipeline_cache, + parent.instance, parent.renderpass_cache, info, *parent.driver_pipeline_cache, *parent.pipeline_layout, shaders, &parent.workers); it_pl.value()->TryBuild(false); @@ -1413,11 +1456,10 @@ bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading, } // Switch to append mode to receive new entries. - pl_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); - return true; + return pl_cache.SwitchMode(CacheFile::CacheOpMode::APPEND); } -bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file, +void ShaderDiskCache::AppendVSConfigProgram(CacheFile& file, const Pica::Shader::Generator::PicaVSConfig& config, const Pica::ShaderSetup& setup, u64 config_id, u64 spirv_id) { @@ -1430,7 +1472,6 @@ bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file, Common::HashCombine(config.state.program_hash, config.state.swizzle_hash); bool new_entry = known_vertex_programs.emplace(entry.program_entry_id).second; - bool prog_res = true; if (new_entry) { std::unique_ptr prog_entry = std::make_unique(); prog_entry->version = VSProgramEntry::EXPECTED_VERSION; @@ -1439,49 +1480,46 @@ bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file, prog_entry->swizzle_len = setup.GetBiggestSwizzleSize(); prog_entry->swizzle_code = setup.GetSwizzleData(); - prog_res = AppendVSProgram(file, *prog_entry, entry.program_entry_id); + AppendVSProgram(file, *prog_entry, entry.program_entry_id); } - return AppendVSConfig(file, entry, config_id) && prog_res; + AppendVSConfig(file, entry, config_id); } -bool ShaderDiskCache::AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, +void ShaderDiskCache::AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, u64 program_id) { - return file.Append(CacheEntryType::VS_PROGRAM, program_id, entry, true); + file.Append(CacheEntryType::VS_PROGRAM, program_id, entry, true); } -bool ShaderDiskCache::AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id) { - return file.Append(CacheEntryType::VS_CONFIG, config_id, entry, true); +void ShaderDiskCache::AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id) { + file.Append(CacheEntryType::VS_CONFIG, config_id, entry, true); } -bool ShaderDiskCache::AppendVSSPIRV(CacheFile& file, std::span program, u64 program_id) { - return file.Append(CacheEntryType::VS_SPIRV, program_id, - {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, - true); +void ShaderDiskCache::AppendVSSPIRV(CacheFile& file, std::span program, u64 program_id) { + file.Append(CacheEntryType::VS_SPIRV, program_id, + {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, true); } -bool ShaderDiskCache::AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id) { - return file.Append(CacheEntryType::FS_CONFIG, config_id, entry, true); +void ShaderDiskCache::AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id) { + file.Append(CacheEntryType::FS_CONFIG, config_id, entry, true); } -bool ShaderDiskCache::AppendFSSPIRV(CacheFile& file, std::span program, u64 program_id) { - return file.Append(CacheEntryType::FS_SPIRV, program_id, - {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, - true); +void ShaderDiskCache::AppendFSSPIRV(CacheFile& file, std::span program, u64 program_id) { + file.Append(CacheEntryType::FS_SPIRV, program_id, + {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, true); } -bool ShaderDiskCache::AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id) { - return file.Append(CacheEntryType::GS_CONFIG, config_id, entry, true); +void ShaderDiskCache::AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id) { + file.Append(CacheEntryType::GS_CONFIG, config_id, entry, true); } -bool ShaderDiskCache::AppendGSSPIRV(CacheFile& file, std::span program, u64 program_id) { - return file.Append(CacheEntryType::GS_SPIRV, program_id, - {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, - true); +void ShaderDiskCache::AppendGSSPIRV(CacheFile& file, std::span program, u64 program_id) { + file.Append(CacheEntryType::GS_SPIRV, program_id, + {reinterpret_cast(program.data()), program.size() * sizeof(u32)}, true); } -bool ShaderDiskCache::AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id) { - return file.Append(CacheEntryType::PL_CONFIG, config_id, entry, true); +void ShaderDiskCache::AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id) { + file.Append(CacheEntryType::PL_CONFIG, config_id, entry, true); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_disk_cache.h b/src/video_core/renderer_vulkan/vk_shader_disk_cache.h index 451b9e078..d6b487f18 100644 --- a/src/video_core/renderer_vulkan/vk_shader_disk_cache.h +++ b/src/video_core/renderer_vulkan/vk_shader_disk_cache.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/thread_worker.h" #include "video_core/pica/shader_setup.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" @@ -241,6 +242,7 @@ private: class CacheFile { public: enum class CacheOpMode { + NONE = 0, READ, APPEND, DELETE, @@ -249,6 +251,9 @@ private: CacheFile() = default; CacheFile(const std::string& _filepath) : filepath(_filepath) {} + ~CacheFile() { + append_worker.WaitForRequests(); + } void SetFilePath(const std::string& path) { filepath = path; @@ -268,24 +273,25 @@ private: size_t GetTotalEntries(); template - bool Append(CacheEntryType type, u64 id, const T& object, bool compress) { + void Append(CacheEntryType type, u64 id, const T& object, bool compress) { static_assert(std::is_trivially_copyable_v); auto bytes = std::as_bytes(std::span{&object, 1}); auto u8_span = std::span(reinterpret_cast(bytes.data()), bytes.size()); - return Append(type, id, u8_span, compress); + Append(type, id, u8_span, compress); } - bool Append(CacheEntryType type, u64 id, std::span data, bool compress); + void Append(CacheEntryType type, u64 id, std::span data, bool compress); bool SwitchMode(CacheOpMode mode); private: + CacheOpMode curr_mode = CacheOpMode::NONE; std::string filepath; - std::mutex mutex; FileUtil::IOFile file{}; - size_t biggest_entry_id = SIZE_MAX; + std::atomic next_entry_id = SIZE_MAX; + Common::ThreadWorker append_worker{1, "Disk Shader Cache Append Worker"}; }; std::string GetVSFile(u64 title_id, bool is_temp) const; @@ -307,19 +313,19 @@ private: bool InitPLCache(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback); - bool AppendVSConfigProgram(CacheFile& file, const Pica::Shader::Generator::PicaVSConfig& config, + void AppendVSConfigProgram(CacheFile& file, const Pica::Shader::Generator::PicaVSConfig& config, const Pica::ShaderSetup& setup, u64 config_id, u64 program_id); - bool AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, u64 program_id); - bool AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id); - bool AppendVSSPIRV(CacheFile& file, std::span program, u64 program_id); + void AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, u64 program_id); + void AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id); + void AppendVSSPIRV(CacheFile& file, std::span program, u64 program_id); - bool AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id); - bool AppendFSSPIRV(CacheFile& file, std::span program, u64 program_id); + void AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id); + void AppendFSSPIRV(CacheFile& file, std::span program, u64 program_id); - bool AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id); - bool AppendGSSPIRV(CacheFile& file, std::span program, u64 program_id); + void AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id); + void AppendGSSPIRV(CacheFile& file, std::span program, u64 program_id); - bool AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id); + void AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id); CacheFile vs_cache; CacheFile fs_cache;