summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2016-11-28 22:03:49 +1000
committerGitHub <noreply@github.com>2016-11-28 22:03:49 +1000
commitda87580dc1c8eb63dac783f0e50e9bd3cfe4b3a1 (patch)
tree3875647817c8ef5e0f8843f02bba13fc1f12e91d /Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
parent681294586b3267f12d4c1632d9ff07e1298b58a6 (diff)
parent8d48319414d7e3f348e4ef38e976121e9a1fce6a (diff)
Merge pull request #4449 from stenzek/vulkan-pipeline-cache
Vulkan: Implement pipeline UID cache
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/ObjectCache.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/ObjectCache.cpp114
1 files changed, 98 insertions, 16 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
index 61e685874a..761402e478 100644
--- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
@@ -159,12 +159,8 @@ GetVulkanColorBlendState(const BlendState& state,
return vk_state;
}
-VkPipeline ObjectCache::GetPipeline(const PipelineInfo& info)
+VkPipeline ObjectCache::CreatePipeline(const PipelineInfo& info)
{
- auto iter = m_pipeline_objects.find(info);
- if (iter != m_pipeline_objects.end())
- return iter->second;
-
// Declare descriptors for empty vertex buffers/attributes
static const VkPipelineVertexInputStateCreateInfo empty_vertex_input_state = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType
@@ -278,16 +274,34 @@ VkPipeline ObjectCache::GetPipeline(const PipelineInfo& info)
-1 // int32_t basePipelineIndex
};
- VkPipeline pipeline = VK_NULL_HANDLE;
+ VkPipeline pipeline;
VkResult res = vkCreateGraphicsPipelines(g_vulkan_context->GetDevice(), m_pipeline_cache, 1,
&pipeline_info, nullptr, &pipeline);
if (res != VK_SUCCESS)
+ {
LOG_VULKAN_ERROR(res, "vkCreateGraphicsPipelines failed: ");
+ return VK_NULL_HANDLE;
+ }
- m_pipeline_objects.emplace(info, pipeline);
return pipeline;
}
+VkPipeline ObjectCache::GetPipeline(const PipelineInfo& info)
+{
+ return GetPipelineWithCacheResult(info).first;
+}
+
+std::pair<VkPipeline, bool> ObjectCache::GetPipelineWithCacheResult(const PipelineInfo& info)
+{
+ auto iter = m_pipeline_objects.find(info);
+ if (iter != m_pipeline_objects.end())
+ return {iter->second, true};
+
+ VkPipeline pipeline = CreatePipeline(info);
+ m_pipeline_objects.emplace(info, pipeline);
+ return {pipeline, false};
+}
+
std::string ObjectCache::GetDiskCacheFileName(const char* type)
{
return StringFromFormat("%svulkan-%s-%s.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(),
@@ -330,6 +344,13 @@ bool ObjectCache::CreatePipelineCache(bool load_from_disk)
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);
+ disk_data.clear();
+ }
+
VkPipelineCacheCreateInfo info = {
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
@@ -355,6 +376,76 @@ bool ObjectCache::CreatePipelineCache(bool load_from_disk)
return false;
}
+// 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)
+// TODO: Remove the #if here when GCC 5 is a minimum build requirement.
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
+static_assert(std::has_trivial_copy_constructor<VK_PIPELINE_CACHE_HEADER>::value,
+ "VK_PIPELINE_CACHE_HEADER must be trivially copyable");
+#else
+static_assert(std::is_trivially_copyable<VK_PIPELINE_CACHE_HEADER>::value,
+ "VK_PIPELINE_CACHE_HEADER must be trivially copyable");
+#endif
+
+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()
{
for (const auto& it : m_pipeline_objects)
@@ -368,15 +459,6 @@ void ObjectCache::DestroyPipelineCache()
m_pipeline_cache = VK_NULL_HANDLE;
}
-void ObjectCache::ClearPipelineCache()
-{
- // Reallocate the pipeline cache object, so it starts fresh and we don't
- // save old pipelines to disk. This is for major changes, e.g. MSAA mode change.
- DestroyPipelineCache();
- if (!CreatePipelineCache(false))
- PanicAlert("Failed to re-create pipeline cache");
-}
-
void ObjectCache::SavePipelineCache()
{
size_t data_size;