feat(bindless): Persist shader cache

This commit is contained in:
PancakeTAS 2026-04-26 00:18:09 +02:00
parent 5438886234
commit 08452e9771
No known key found for this signature in database
3 changed files with 118 additions and 18 deletions

View file

@ -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<vk::UniquePipeline> 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};

View file

@ -18,6 +18,7 @@
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@ -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<uint32_t>(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<vk::UniquePipelineCache, bool> 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<uint8_t> 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<uint8_t> cacheData{
device.getPipelineCacheData(cache, dld)
};
file.write(
reinterpret_cast<const char*>(cacheData.data()), // NOLINT (unsafe cast)
static_cast<std::streamsize>(cacheData.size())
);
file.flush();
file.close();
}
std::pair<vk::UniqueDescriptorSetLayout, vk::UniquePipelineLayout> vkhelper::createLayout(

View file

@ -21,6 +21,7 @@
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@ -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<vk::UniquePipelineCache, bool> 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