- Vulkan shader cache from GZDoom.

This commit is contained in:
Christoph Oelckers 2023-01-07 19:31:21 +01:00
parent 905a9bfeae
commit d5b217df2a
5 changed files with 172 additions and 2 deletions

View file

@ -236,6 +236,7 @@ class ComputePipelineBuilder
public:
ComputePipelineBuilder();
ComputePipelineBuilder& Cache(VulkanPipelineCache* cache);
ComputePipelineBuilder& Layout(VulkanPipelineLayout *layout);
ComputePipelineBuilder& ComputeShader(VulkanShader *shader);
ComputePipelineBuilder& DebugName(const char* name) { debugName = name; return *this; }
@ -245,6 +246,7 @@ public:
private:
VkComputePipelineCreateInfo pipelineInfo = {};
VkPipelineShaderStageCreateInfo stageInfo = {};
VulkanPipelineCache* cache = nullptr;
const char* debugName = nullptr;
};
@ -324,6 +326,7 @@ class GraphicsPipelineBuilder
public:
GraphicsPipelineBuilder();
GraphicsPipelineBuilder& Cache(VulkanPipelineCache* cache);
GraphicsPipelineBuilder& Subpass(int subpass);
GraphicsPipelineBuilder& Layout(VulkanPipelineLayout *layout);
GraphicsPipelineBuilder& RenderPass(VulkanRenderPass *renderPass);
@ -377,6 +380,7 @@ private:
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes;
std::vector<VkDynamicState> dynamicStates;
VulkanPipelineCache* cache = nullptr;
const char* debugName = nullptr;
};
@ -399,6 +403,24 @@ private:
const char* debugName = nullptr;
};
class PipelineCacheBuilder
{
public:
PipelineCacheBuilder();
PipelineCacheBuilder& InitialData(const void* data, size_t size);
PipelineCacheBuilder& Flags(VkPipelineCacheCreateFlags flags);
PipelineCacheBuilder& DebugName(const char* name) { debugName = name; return *this; }
std::unique_ptr<VulkanPipelineCache> Create(VulkanDevice* device);
private:
VkPipelineCacheCreateInfo pipelineCacheInfo = {};
std::vector<uint8_t> initData;
const char* debugName = nullptr;
};
class RenderPassBuilder
{
public:

View file

@ -305,6 +305,24 @@ private:
VulkanPipelineLayout &operator=(const VulkanPipelineLayout &) = delete;
};
class VulkanPipelineCache
{
public:
VulkanPipelineCache(VulkanDevice* device, VkPipelineCache cache);
~VulkanPipelineCache();
void SetDebugName(const char* name) { device->SetObjectName(name, (uint64_t)cache, VK_OBJECT_TYPE_PIPELINE_CACHE); }
std::vector<uint8_t> GetCacheData();
VulkanDevice* device;
VkPipelineCache cache;
private:
VulkanPipelineCache(const VulkanPipelineCache&) = delete;
VulkanPipelineCache& operator=(const VulkanPipelineCache&) = delete;
};
class VulkanRenderPass
{
public:
@ -1179,6 +1197,33 @@ inline VulkanPipelineLayout::~VulkanPipelineLayout()
/////////////////////////////////////////////////////////////////////////////
inline VulkanPipelineCache::VulkanPipelineCache(VulkanDevice* device, VkPipelineCache cache) : device(device), cache(cache)
{
}
inline VulkanPipelineCache::~VulkanPipelineCache()
{
vkDestroyPipelineCache(device->device, cache, nullptr);
}
inline std::vector<uint8_t> VulkanPipelineCache::GetCacheData()
{
size_t dataSize = 0;
VkResult result = vkGetPipelineCacheData(device->device, cache, &dataSize, nullptr);
CheckVulkanError(result, "Could not get cache data size");
std::vector<uint8_t> buffer;
buffer.resize(dataSize);
result = vkGetPipelineCacheData(device->device, cache, &dataSize, buffer.data());
if (result == VK_INCOMPLETE)
VulkanError("Could not get cache data (incomplete)");
CheckVulkanError(result, "Could not get cache data");
buffer.resize(dataSize);
return buffer;
}
/////////////////////////////////////////////////////////////////////////////
inline VulkanRenderPass::VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass) : device(device), renderPass(renderPass)
{
}

View file

@ -589,6 +589,12 @@ ComputePipelineBuilder::ComputePipelineBuilder()
stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
}
ComputePipelineBuilder& ComputePipelineBuilder::Cache(VulkanPipelineCache* cache)
{
this->cache = cache;
return *this;
}
ComputePipelineBuilder& ComputePipelineBuilder::Layout(VulkanPipelineLayout* layout)
{
pipelineInfo.layout = layout->layout;
@ -608,7 +614,7 @@ ComputePipelineBuilder& ComputePipelineBuilder::ComputeShader(VulkanShader* shad
std::unique_ptr<VulkanPipeline> ComputePipelineBuilder::Create(VulkanDevice* device)
{
VkPipeline pipeline;
vkCreateComputePipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
vkCreateComputePipelines(device->device, cache ? cache->cache : VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName)
obj->SetDebugName(debugName);
@ -871,6 +877,12 @@ GraphicsPipelineBuilder& GraphicsPipelineBuilder::RasterizationSamples(VkSampleC
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Cache(VulkanPipelineCache* cache)
{
this->cache = cache;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Subpass(int subpass)
{
pipelineInfo.subpass = subpass;
@ -1087,7 +1099,7 @@ GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddDynamicState(VkDynamicState
std::unique_ptr<VulkanPipeline> GraphicsPipelineBuilder::Create(VulkanDevice* device)
{
VkPipeline pipeline = 0;
VkResult result = vkCreateGraphicsPipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
VkResult result = vkCreateGraphicsPipelines(device->device, cache ? cache->cache : VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
CheckVulkanError(result, "Could not create graphics pipeline");
auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName)
@ -1135,6 +1147,54 @@ std::unique_ptr<VulkanPipelineLayout> PipelineLayoutBuilder::Create(VulkanDevice
/////////////////////////////////////////////////////////////////////////////
PipelineCacheBuilder::PipelineCacheBuilder()
{
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
}
PipelineCacheBuilder& PipelineCacheBuilder::InitialData(const void* data, size_t size)
{
initData.resize(size);
memcpy(initData.data(), data, size);
return *this;
}
PipelineCacheBuilder& PipelineCacheBuilder::Flags(VkPipelineCacheCreateFlags flags)
{
pipelineCacheInfo.flags = flags;
return *this;
}
std::unique_ptr<VulkanPipelineCache> PipelineCacheBuilder::Create(VulkanDevice* device)
{
pipelineCacheInfo.pInitialData = nullptr;
pipelineCacheInfo.initialDataSize = 0;
// Check if the saved cache data is compatible with our device:
if (initData.size() >= sizeof(VkPipelineCacheHeaderVersionOne))
{
VkPipelineCacheHeaderVersionOne* header = (VkPipelineCacheHeaderVersionOne*)initData.data();
if (header->headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE &&
header->vendorID == device->PhysicalDevice.Properties.vendorID &&
header->deviceID == device->PhysicalDevice.Properties.deviceID &&
memcmp(header->pipelineCacheUUID, device->PhysicalDevice.Properties.pipelineCacheUUID, VK_UUID_SIZE) == 0)
{
pipelineCacheInfo.pInitialData = initData.data();
pipelineCacheInfo.initialDataSize = initData.size();
}
}
VkPipelineCache pipelineCache;
VkResult result = vkCreatePipelineCache(device->device, &pipelineCacheInfo, nullptr, &pipelineCache);
CheckVulkanError(result, "Could not create pipeline cache");
auto obj = std::make_unique<VulkanPipelineCache>(device, pipelineCache);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
RenderPassBuilder::RenderPassBuilder()
{
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;

View file

@ -34,13 +34,49 @@
#include "flatvertices.h"
#include "hw_viewpointuniforms.h"
#include "v_2ddrawer.h"
#include "i_specialpaths.h"
VkRenderPassManager::VkRenderPassManager(VulkanRenderDevice* fb) : fb(fb)
{
FString path = M_GetCachePath(true);
CreatePath(path);
CacheFilename = path + "/pipelinecache.zdpc";
PipelineCacheBuilder builder;
builder.DebugName("PipelineCache");
try
{
FileReader fr;
if (fr.OpenFile(CacheFilename))
{
std::vector<uint8_t> data;
data.resize(fr.GetLength());
if (fr.Read(data.data(), data.size()) == data.size())
{
builder.InitialData(data.data(), data.size());
}
}
}
catch (...)
{
}
PipelineCache = builder.Create(fb->device.get());
}
VkRenderPassManager::~VkRenderPassManager()
{
try
{
auto data = PipelineCache->GetCacheData();
std::unique_ptr<FileWriter> fw(FileWriter::Open(CacheFilename));
if (fw)
fw->Write(data.data(), data.size());
}
catch (...)
{
}
}
void VkRenderPassManager::RenderBuffersReset()
@ -206,6 +242,7 @@ VulkanPipeline *VkRenderPassSetup::GetPipeline(const VkPipelineKey &key)
std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipelineKey &key)
{
GraphicsPipelineBuilder builder;
builder.Cache(fb->GetRenderPassManager()->GetCache());
VkShaderProgram *program;
if (key.SpecialEffect != EFF_NONE)
@ -332,6 +369,7 @@ void VkPPRenderPassSetup::CreatePipelineLayout(const VkPPRenderPassKey& key)
void VkPPRenderPassSetup::CreatePipeline(const VkPPRenderPassKey& key)
{
GraphicsPipelineBuilder builder;
builder.Cache(fb->GetRenderPassManager()->GetCache());
builder.AddVertexShader(key.Shader->VertexShader.get());
builder.AddFragmentShader(key.Shader->FragmentShader.get());

View file

@ -135,6 +135,8 @@ public:
VkPPRenderPassSetup* GetPPRenderPass(const VkPPRenderPassKey& key);
VulkanPipelineCache* GetCache() { return PipelineCache.get(); }
private:
VulkanRenderDevice* fb = nullptr;
@ -143,4 +145,7 @@ private:
std::vector<VkVertexFormat> VertexFormats;
std::map<VkPPRenderPassKey, std::unique_ptr<VkPPRenderPassSetup>> PPRenderPassSetup;
FString CacheFilename;
std::unique_ptr<VulkanPipelineCache> PipelineCache;
};