diff --git a/lsfg-vk-backend/src/modules/pipeline.cpp b/lsfg-vk-backend/src/modules/pipeline.cpp index 5532656..b46f7bc 100644 --- a/lsfg-vk-backend/src/modules/pipeline.cpp +++ b/lsfg-vk-backend/src/modules/pipeline.cpp @@ -516,7 +516,15 @@ Pipeline::Pipeline( }); } - this->m_cache = vkhelper::createPipelineCache(dld, device); + const std::string_view cacheTag{perf ? "performance" : "quality"}; + auto [cache, isCacheValid] = vkhelper::createPipelineCache( + dld, + device, + physdev, + cacheTag + ); + this->m_cache = std::move(cache); + std::vector pipelines{ device.createComputePipelinesUnique( *this->m_cache, @@ -526,6 +534,18 @@ Pipeline::Pipeline( ).value }; + if (!isCacheValid) { + LOG_DEBUG(" Pipeline cache is not valid, persisting new cache data") + + vkhelper::persistPipelineCache( + dld, + device, + physdev, + *this->m_cache, + cacheTag + ); + } + this->m_pipelines.reserve(signature.shaders.size()); for (size_t i = 0; i < signature.shaders.size(); i++) { const auto& name{signature.shaders.at(i).first}; diff --git a/lsfg-vk-backend/src/utility/vkhelper.cpp b/lsfg-vk-backend/src/utility/vkhelper.cpp index 0eecdce..85b0e82 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.cpp +++ b/lsfg-vk-backend/src/utility/vkhelper.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -183,24 +184,57 @@ vk::UniqueShaderModule vkhelper::createShaderModule( return device.createShaderModuleUnique(shaderInfo, nullptr, dld); } -vk::UniquePipelineCache vkhelper::createPipelineCache( +namespace { + /// Find the cache file path + std::filesystem::path findPipelineCache( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::PhysicalDevice& physdev, + std::string_view tag + ) { + // First find the base path + std::filesystem::path path{"/tmp/lsfg-vk"}; + + const char* xdgCacheHome{std::getenv("XDG_CACHE_HOME")}; + if (xdgCacheHome && *xdgCacheHome != '\0') + path = std::filesystem::path(xdgCacheHome) / "lsfg-vk"; + + const char* home{std::getenv("HOME")}; + if (home && *home != '\0') + path = std::filesystem::path(home) / ".cache" / "lsfg-vk"; + + // Ensure the directory exists + if (!std::filesystem::exists(path)) + std::filesystem::create_directories(path); + + // Calculate the physical device UUID + vk::PhysicalDeviceProperties2 info{}; + physdev.getProperties2(&info, dld); + + std::ostringstream ss; + ss << std::hex << std::setfill('0'); + for (uint32_t i = 0; i < 16; i++) { + ss << std::setw(2) << static_cast(info.properties.pipelineCacheUUID.at(i)); + if (i == 3 || i == 5 || i == 7 || i == 9) { + ss << "-"; + } + } + + // Return the full path + return path / ("cache_" + std::string(tag) + "_" + ss.str() + ".bin"); + } +} + +std::pair vkhelper::createPipelineCache( const vk::detail::DispatchLoaderDynamic& dld, - const vk::Device& device + const vk::Device& device, + const vk::PhysicalDevice& physdev, + std::string_view tag ) { - // Find the cache file path - std::filesystem::path path{"/tmp/lsfg-vk_pipeline_cache.bin"}; - - const char* xdgCacheHome{std::getenv("XDG_CACHE_HOME")}; - if (xdgCacheHome && *xdgCacheHome != '\0') - path = std::filesystem::path(xdgCacheHome) / "lsfg-vk_pipeline_cache.bin"; - - const char* home{std::getenv("HOME")}; - if (home && *home != '\0') - path = std::filesystem::path(home) / ".cache" / "lsfg-vk_pipeline_cache.bin"; + const std::filesystem::path path{findPipelineCache(dld, physdev, tag)}; + const bool valid{std::filesystem::exists(path) && std::filesystem::file_size(path) > 32}; // Read cache data (if any) std::vector cacheData{}; - if (std::filesystem::exists(path)) { std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file.is_open()) @@ -219,7 +253,32 @@ vk::UniquePipelineCache vkhelper::createPipelineCache( .initialDataSize = cacheData.size(), .pInitialData = cacheData.data() }; - return device.createPipelineCacheUnique(pipelineCacheInfo, nullptr, dld); + return { device.createPipelineCacheUnique(pipelineCacheInfo, nullptr, dld), valid }; +} + +void vkhelper::persistPipelineCache( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const vk::PhysicalDevice& physdev, + const vk::PipelineCache& cache, + std::string_view tag +) { + const std::filesystem::path path{findPipelineCache(dld, physdev, tag)}; + + std::ofstream file(path, std::ios::binary | std::ios::trunc); + if (!file.is_open()) + throw std::runtime_error("Unable to open pipeline cache file for writing"); + + const std::vector cacheData{ + device.getPipelineCacheData(cache, dld) + }; + file.write( + reinterpret_cast(cacheData.data()), // NOLINT (unsafe cast) + static_cast(cacheData.size()) + ); + + file.flush(); + file.close(); } std::pair vkhelper::createLayout( diff --git a/lsfg-vk-backend/src/utility/vkhelper.hpp b/lsfg-vk-backend/src/utility/vkhelper.hpp index 5d3aec3..b20f818 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.hpp +++ b/lsfg-vk-backend/src/utility/vkhelper.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -124,15 +125,35 @@ namespace vkhelper { /// /// @param dld Dynamic dispatch loader /// @param device Vulkan device + /// @param physdev Physical device + /// @param tag Cache tag for different pipelines /// @return RAII-wrapped Vulkan pipeline cache /// @throws std::runtime_error on failure /// - vk::UniquePipelineCache createPipelineCache( + std::pair createPipelineCache( const vk::detail::DispatchLoaderDynamic& dld, - const vk::Device& device + const vk::Device& device, + const vk::PhysicalDevice& physdev, + std::string_view tag ); - // TODO: Persist pipeline cache + /// + /// Persist the Vulkan pipeline cache to disk + /// + /// @param dld Dynamic dispatch loader + /// @param device Vulkan device + /// @param physdev Physical device + /// @param cache Vulkan pipeline cache + /// @param tag Cache tag for different pipelines + /// @throws std::runtime_error on failure + /// + void persistPipelineCache( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const vk::PhysicalDevice& physdev, + const vk::PipelineCache& cache, + std::string_view tag + ); /// /// Create a Vulkan descriptor set layout