summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/ObjectCache.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/ObjectCache.cpp357
1 files changed, 256 insertions, 101 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
index 1de68b8349..59690118aa 100644
--- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
@@ -19,7 +19,7 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ShaderCompiler.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
-#include "VideoBackends/Vulkan/Util.h"
+#include "VideoBackends/Vulkan/VKTexture.h"
#include "VideoBackends/Vulkan/VertexFormat.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoCommon/Statistics.h"
@@ -28,16 +28,16 @@ namespace Vulkan
{
std::unique_ptr<ObjectCache> g_object_cache;
-ObjectCache::ObjectCache()
-{
-}
+ObjectCache::ObjectCache() = default;
ObjectCache::~ObjectCache()
{
+ DestroyPipelineCache();
DestroySamplers();
DestroyPipelineLayouts();
DestroyDescriptorSetLayouts();
DestroyRenderPassCache();
+ m_dummy_texture.reset();
}
bool ObjectCache::Initialize()
@@ -48,44 +48,37 @@ bool ObjectCache::Initialize()
if (!CreatePipelineLayouts())
return false;
- if (!CreateUtilityShaderVertexFormat())
- return false;
-
if (!CreateStaticSamplers())
return false;
m_texture_upload_buffer =
- StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE,
- MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE);
+ StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, TEXTURE_UPLOAD_BUFFER_SIZE);
if (!m_texture_upload_buffer)
{
PanicAlert("Failed to create texture upload buffer");
return false;
}
- m_utility_shader_vertex_buffer =
- StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1024 * 1024, 4 * 1024 * 1024);
- m_utility_shader_uniform_buffer =
- StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 1024, 4 * 1024 * 1024);
- if (!m_utility_shader_vertex_buffer || !m_utility_shader_uniform_buffer)
- return false;
-
- m_dummy_texture = Texture2D::Create(1, 1, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT,
- VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
- m_dummy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
- VkClearColorValue clear_color = {};
- VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
- vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
- m_dummy_texture->GetImage(), m_dummy_texture->GetLayout(), &clear_color, 1,
- &clear_range);
- m_dummy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ if (g_ActiveConfig.bShaderCache)
+ {
+ if (!LoadPipelineCache())
+ return false;
+ }
+ else
+ {
+ if (!CreatePipelineCache())
+ return false;
+ }
return true;
}
+void ObjectCache::Shutdown()
+{
+ if (g_ActiveConfig.bShaderCache && m_pipeline_cache != VK_NULL_HANDLE)
+ SavePipelineCache();
+}
+
void ObjectCache::ClearSamplerCache()
{
for (const auto& it : m_sampler_cache)
@@ -115,13 +108,9 @@ void ObjectCache::DestroySamplers()
bool ObjectCache::CreateDescriptorSetLayouts()
{
- static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = {
- 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
- VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT};
-
// The geometry shader buffer must be last in this binding set, as we don't include it
// if geometry shaders are not supported by the device. See the decrement below.
- static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = {
+ static const VkDescriptorSetLayoutBinding standard_ubo_bindings[] = {
{UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
VK_SHADER_STAGE_FRAGMENT_BIT},
{UBO_DESCRIPTOR_SET_BINDING_VS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
@@ -129,45 +118,56 @@ bool ObjectCache::CreateDescriptorSetLayouts()
{UBO_DESCRIPTOR_SET_BINDING_GS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
VK_SHADER_STAGE_GEOMETRY_BIT}};
- static const VkDescriptorSetLayoutBinding sampler_set_bindings[] = {
+ static const VkDescriptorSetLayoutBinding standard_sampler_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, static_cast<u32>(NUM_PIXEL_SHADER_SAMPLERS),
VK_SHADER_STAGE_FRAGMENT_BIT}};
- static const VkDescriptorSetLayoutBinding ssbo_set_bindings[] = {
+ static const VkDescriptorSetLayoutBinding standard_ssbo_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}};
- static const VkDescriptorSetLayoutBinding texel_buffer_set_bindings[] = {
- {0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ static const VkDescriptorSetLayoutBinding utility_ubo_bindings[] = {
+ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
+ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT};
+
+ // Utility samplers aren't dynamically indexed.
+ static const VkDescriptorSetLayoutBinding utility_sampler_bindings[] = {
+ {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {6, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {7, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {8, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
};
static const VkDescriptorSetLayoutBinding compute_set_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_COMPUTE_BIT},
{1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
{2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
- {3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
- {4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
- {5, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
- {6, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
- {7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT},
+ {3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
+ {4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT},
+ {5, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT},
};
VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings},
+ static_cast<u32>(ArraySize(standard_ubo_bindings)), standard_ubo_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(per_stage_ubo_set_bindings)), per_stage_ubo_set_bindings},
+ static_cast<u32>(ArraySize(standard_sampler_bindings)), standard_sampler_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(sampler_set_bindings)), sampler_set_bindings},
+ static_cast<u32>(ArraySize(standard_ssbo_bindings)), standard_ssbo_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(ssbo_set_bindings)), ssbo_set_bindings},
+ static_cast<u32>(ArraySize(utility_ubo_bindings)), utility_ubo_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(texel_buffer_set_bindings)), texel_buffer_set_bindings},
+ static_cast<u32>(ArraySize(utility_sampler_bindings)), utility_sampler_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(compute_set_bindings)), compute_set_bindings}};
// Don't set the GS bit if geometry shaders aren't available.
- if (!g_vulkan_context->SupportsGeometryShaders())
- create_infos[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS].bindingCount--;
+ if (!g_ActiveConfig.backend_info.bSupportsGeometryShaders)
+ create_infos[DESCRIPTOR_SET_LAYOUT_STANDARD_UNIFORM_BUFFERS].bindingCount--;
for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++)
{
@@ -199,22 +199,15 @@ bool ObjectCache::CreatePipelineLayouts()
// Descriptor sets for each pipeline layout.
// In the standard set, the SSBO must be the last descriptor, as we do not include it
// when fragment stores and atomics are not supported by the device.
- VkDescriptorSetLayout standard_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]};
- VkDescriptorSetLayout texture_conversion_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS]};
- VkDescriptorSetLayout utility_sets[] = {
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER],
- m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
- VkDescriptorSetLayout compute_sets[] = {m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_COMPUTE]};
- VkPushConstantRange push_constant_range = {
- VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
- VkPushConstantRange compute_push_constant_range = {VK_SHADER_STAGE_COMPUTE_BIT, 0,
- PUSH_CONSTANT_BUFFER_SIZE};
+ const VkDescriptorSetLayout standard_sets[] = {
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_STANDARD_UNIFORM_BUFFERS],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_STANDARD_SAMPLERS],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_STANDARD_SHADER_STORAGE_BUFFERS]};
+ const VkDescriptorSetLayout utility_sets[] = {
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UTILITY_UNIFORM_BUFFER],
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UTILITY_SAMPLERS]};
+ const VkDescriptorSetLayout compute_sets[] = {
+ m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_COMPUTE]};
// Info for each pipeline layout
VkPipelineLayoutCreateInfo pipeline_layout_info[NUM_PIPELINE_LAYOUTS] = {
@@ -222,25 +215,16 @@ bool ObjectCache::CreatePipelineLayouts()
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
- // Push Constant
- {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range},
-
- // Texture Conversion
- {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(texture_conversion_sets)), texture_conversion_sets, 1,
- &push_constant_range},
-
- // Texture Conversion
+ // Utility
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(utility_sets)), utility_sets, 0, nullptr},
// Compute
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
- static_cast<u32>(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}};
+ static_cast<u32>(ArraySize(compute_sets)), compute_sets, 0, nullptr}};
// If bounding box is unsupported, don't bother with the SSBO descriptor set.
- if (!g_vulkan_context->SupportsBoundingBox())
+ if (!g_ActiveConfig.backend_info.bSupportsBBox)
pipeline_layout_info[PIPELINE_LAYOUT_STANDARD].setLayoutCount--;
for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++)
@@ -265,30 +249,6 @@ void ObjectCache::DestroyPipelineLayouts()
}
}
-bool ObjectCache::CreateUtilityShaderVertexFormat()
-{
- PortableVertexDeclaration vtx_decl = {};
- vtx_decl.position.enable = true;
- vtx_decl.position.type = VAR_FLOAT;
- vtx_decl.position.components = 4;
- vtx_decl.position.integer = false;
- vtx_decl.position.offset = offsetof(UtilityShaderVertex, Position);
- vtx_decl.texcoords[0].enable = true;
- vtx_decl.texcoords[0].type = VAR_FLOAT;
- vtx_decl.texcoords[0].components = 4;
- vtx_decl.texcoords[0].integer = false;
- vtx_decl.texcoords[0].offset = offsetof(UtilityShaderVertex, TexCoord);
- vtx_decl.colors[0].enable = true;
- vtx_decl.colors[0].type = VAR_UNSIGNED_BYTE;
- vtx_decl.colors[0].components = 4;
- vtx_decl.colors[0].integer = false;
- vtx_decl.colors[0].offset = offsetof(UtilityShaderVertex, Color);
- vtx_decl.stride = sizeof(UtilityShaderVertex);
-
- m_utility_shader_vertex_format = std::make_unique<VertexFormat>(vtx_decl);
- return true;
-}
-
bool ObjectCache::CreateStaticSamplers()
{
VkSamplerCreateInfo create_info = {
@@ -472,4 +432,199 @@ void ObjectCache::DestroyRenderPassCache()
vkDestroyRenderPass(g_vulkan_context->GetDevice(), it.second, nullptr);
m_render_pass_cache.clear();
}
+
+class PipelineCacheReadCallback : public LinearDiskCacheReader<u32, u8>
+{
+public:
+ PipelineCacheReadCallback(std::vector<u8>* data) : m_data(data) {}
+ void Read(const u32& key, const u8* value, u32 value_size) override
+ {
+ m_data->resize(value_size);
+ if (value_size > 0)
+ memcpy(m_data->data(), value, value_size);
+ }
+
+private:
+ std::vector<u8>* m_data;
+};
+
+class PipelineCacheReadIgnoreCallback : public LinearDiskCacheReader<u32, u8>
+{
+public:
+ void Read(const u32& key, const u8* value, u32 value_size) override {}
+};
+
+bool ObjectCache::CreatePipelineCache()
+{
+ // Vulkan pipeline caches can be shared between games for shader compile time reduction.
+ // This assumes that drivers don't create all pipelines in the cache on load time, only
+ // when a lookup occurs that matches a pipeline (or pipeline data) in the cache.
+ m_pipeline_cache_filename = GetDiskShaderCacheFileName(APIType::Vulkan, "Pipeline", false, true);
+
+ VkPipelineCacheCreateInfo info = {
+ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType
+ nullptr, // const void* pNext
+ 0, // VkPipelineCacheCreateFlags flags
+ 0, // size_t initialDataSize
+ nullptr // const void* pInitialData
+ };
+
+ VkResult res =
+ vkCreatePipelineCache(g_vulkan_context->GetDevice(), &info, nullptr, &m_pipeline_cache);
+ if (res == VK_SUCCESS)
+ return true;
+
+ LOG_VULKAN_ERROR(res, "vkCreatePipelineCache failed: ");
+ return false;
+}
+
+bool ObjectCache::LoadPipelineCache()
+{
+ // We have to keep the pipeline cache file name around since when we save it
+ // we delete the old one, by which time the game's unique ID is already cleared.
+ m_pipeline_cache_filename = GetDiskShaderCacheFileName(APIType::Vulkan, "Pipeline", false, true);
+
+ std::vector<u8> disk_data;
+ LinearDiskCache<u32, u8> disk_cache;
+ PipelineCacheReadCallback read_callback(&disk_data);
+ if (disk_cache.OpenAndRead(m_pipeline_cache_filename, read_callback) != 1)
+ disk_data.clear();
+
+ if (!disk_data.empty() && !ValidatePipelineCache(disk_data.data(), disk_data.size()))
+ {
+ // Don't use this data. In fact, we should delete it to prevent it from being used next time.
+ File::Delete(m_pipeline_cache_filename);
+ return CreatePipelineCache();
+ }
+
+ VkPipelineCacheCreateInfo info = {
+ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType
+ nullptr, // const void* pNext
+ 0, // VkPipelineCacheCreateFlags flags
+ disk_data.size(), // size_t initialDataSize
+ disk_data.data() // const void* pInitialData
+ };
+
+ VkResult res =
+ vkCreatePipelineCache(g_vulkan_context->GetDevice(), &info, nullptr, &m_pipeline_cache);
+ if (res == VK_SUCCESS)
+ return true;
+
+ // Failed to create pipeline cache, try with it empty.
+ LOG_VULKAN_ERROR(res, "vkCreatePipelineCache failed, trying empty cache: ");
+ return CreatePipelineCache();
+}
+
+// Based on Vulkan 1.0 specification,
+// Table 9.1. Layout for pipeline cache header version VK_PIPELINE_CACHE_HEADER_VERSION_ONE
+// NOTE: This data is assumed to be in little-endian format.
+#pragma pack(push, 4)
+struct VK_PIPELINE_CACHE_HEADER
+{
+ u32 header_length;
+ u32 header_version;
+ u32 vendor_id;
+ u32 device_id;
+ u8 uuid[VK_UUID_SIZE];
+};
+#pragma pack(pop)
+static_assert(std::is_trivially_copyable<VK_PIPELINE_CACHE_HEADER>::value,
+ "VK_PIPELINE_CACHE_HEADER must be trivially copyable");
+
+bool ObjectCache::ValidatePipelineCache(const u8* data, size_t data_length)
+{
+ if (data_length < sizeof(VK_PIPELINE_CACHE_HEADER))
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header");
+ return false;
+ }
+
+ VK_PIPELINE_CACHE_HEADER header;
+ std::memcpy(&header, data, sizeof(header));
+ if (header.header_length < sizeof(VK_PIPELINE_CACHE_HEADER))
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header length");
+ return false;
+ }
+
+ if (header.header_version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Invalid header version");
+ return false;
+ }
+
+ if (header.vendor_id != g_vulkan_context->GetDeviceProperties().vendorID)
+ {
+ ERROR_LOG(VIDEO,
+ "Pipeline cache failed validation: Incorrect vendor ID (file: 0x%X, device: 0x%X)",
+ header.vendor_id, g_vulkan_context->GetDeviceProperties().vendorID);
+ return false;
+ }
+
+ if (header.device_id != g_vulkan_context->GetDeviceProperties().deviceID)
+ {
+ ERROR_LOG(VIDEO,
+ "Pipeline cache failed validation: Incorrect device ID (file: 0x%X, device: 0x%X)",
+ header.device_id, g_vulkan_context->GetDeviceProperties().deviceID);
+ return false;
+ }
+
+ if (std::memcmp(header.uuid, g_vulkan_context->GetDeviceProperties().pipelineCacheUUID,
+ VK_UUID_SIZE) != 0)
+ {
+ ERROR_LOG(VIDEO, "Pipeline cache failed validation: Incorrect UUID");
+ return false;
+ }
+
+ return true;
+}
+
+void ObjectCache::DestroyPipelineCache()
+{
+ vkDestroyPipelineCache(g_vulkan_context->GetDevice(), m_pipeline_cache, nullptr);
+ m_pipeline_cache = VK_NULL_HANDLE;
+}
+
+void ObjectCache::SavePipelineCache()
+{
+ size_t data_size;
+ VkResult res =
+ vkGetPipelineCacheData(g_vulkan_context->GetDevice(), m_pipeline_cache, &data_size, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData failed: ");
+ return;
+ }
+
+ std::vector<u8> data(data_size);
+ res = vkGetPipelineCacheData(g_vulkan_context->GetDevice(), m_pipeline_cache, &data_size,
+ data.data());
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData failed: ");
+ return;
+ }
+
+ // Delete the old cache and re-create.
+ File::Delete(m_pipeline_cache_filename);
+
+ // We write a single key of 1, with the entire pipeline cache data.
+ // Not ideal, but our disk cache class does not support just writing a single blob
+ // of data without specifying a key.
+ LinearDiskCache<u32, u8> disk_cache;
+ PipelineCacheReadIgnoreCallback callback;
+ disk_cache.OpenAndRead(m_pipeline_cache_filename, callback);
+ disk_cache.Append(1, data.data(), static_cast<u32>(data.size()));
+ disk_cache.Close();
+}
+
+void ObjectCache::ReloadPipelineCache()
+{
+ SavePipelineCache();
+
+ if (g_ActiveConfig.bShaderCache)
+ LoadPipelineCache();
+ else
+ CreatePipelineCache();
}
+} // namespace Vulkan