diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index fc0c8f363..84736ef41 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -170,8 +170,8 @@ static void create(WrappedVulkan *driver, const char *objName, const int line, V 0, }; - VkResult vkr = driver->vkCreateComputePipelines(driver->GetDev(), VK_NULL_HANDLE, 1, - &compPipeInfo, NULL, pipe); + VkResult vkr = driver->vkCreateComputePipelines( + driver->GetDev(), driver->GetShaderCache()->GetPipeCache(), 1, &compPipeInfo, NULL, pipe); if(vkr != VK_SUCCESS) RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str()); } @@ -213,8 +213,8 @@ static void create(WrappedVulkan *driver, const char *objName, const int line, V 0, }; - vkr = driver->vkCreateComputePipelines(driver->GetDev(), VK_NULL_HANDLE, 1, &compPipeInfo, NULL, - pipe); + vkr = driver->vkCreateComputePipelines(driver->GetDev(), driver->GetShaderCache()->GetPipeCache(), + 1, &compPipeInfo, NULL, pipe); if(vkr != VK_SUCCESS) RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str()); @@ -389,8 +389,8 @@ static void create(WrappedVulkan *driver, const char *objName, const int line, V -1, // base pipeline index }; - VkResult vkr = driver->vkCreateGraphicsPipelines(driver->GetDev(), VK_NULL_HANDLE, 1, - &graphicsPipeInfo, NULL, pipe); + VkResult vkr = driver->vkCreateGraphicsPipelines( + driver->GetDev(), driver->GetShaderCache()->GetPipeCache(), 1, &graphicsPipeInfo, NULL, pipe); if(vkr != VK_SUCCESS) RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str()); } diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index 849223f8d..7f9366880 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -69,6 +69,7 @@ public: void CopyArrayToTex2DMS(VkImage destMS, VkImage srcArray, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt); + VkPipelineCache GetPipelineCache() { return m_PipelineCache; } VkPipeline GetCustomPipeline() { return m_Custom.TexPipeline; } VkImage GetCustomTexture() { return m_Custom.TexImg; } VkFramebuffer GetCustomFramebuffer() { return m_Custom.TexFB; } @@ -132,6 +133,8 @@ private: // one per depth/stencil output format, per sample count VkPipeline m_DepthArray2MSPipe[6][4] = {{VK_NULL_HANDLE}}; + VkPipelineCache m_PipelineCache = VK_NULL_HANDLE; + struct CustomShaderRendering { void Destroy(WrappedVulkan *driver); diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index 9cda16b6a..85f36ddc6 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -120,6 +120,15 @@ struct VulkanBlobShaderCallbacks const byte *GetData(SPIRVBlob blob) const { return (const byte *)blob->data(); } } VulkanShaderCacheCallbacks; +struct VkPipeCacheHeader +{ + uint32_t length; + VkPipelineCacheHeaderVersion version; + uint32_t vendorID; + uint32_t deviceID; + byte uuid[VK_UUID_SIZE]; +}; + VulkanShaderCache::VulkanShaderCache(WrappedVulkan *driver) { // Load shader cache, if present @@ -224,11 +233,79 @@ VulkanShaderCache::VulkanShaderCache(WrappedVulkan *driver) } } + { + VkPipelineCacheCreateInfo createInfo = {VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO}; + + GetPipeCacheBlob(); + + if(!m_PipeCacheBlob.empty()) + { + if(m_PipeCacheBlob.size() < sizeof(VkPipeCacheHeader)) + { + m_PipeCacheBlob.clear(); + } + else + { + VkPipeCacheHeader *header = (VkPipeCacheHeader *)m_PipeCacheBlob.data(); + + // check explicitly for incompatibility + if(header->length != sizeof(VkPipeCacheHeader)) + { + m_PipeCacheBlob.clear(); + RDCLOG("Pipeline cache header length %u is unexpected, not using cache", header->length); + } + else if(header->version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) + { + m_PipeCacheBlob.clear(); + RDCLOG("Pipeline cache header version %u is unexpected, not using cache", header->version); + } + else if(header->vendorID != m_pDriver->GetDeviceProps().vendorID) + { + m_PipeCacheBlob.clear(); + RDCLOG("Pipeline cache header vendorID %u doesn't match %u", header->vendorID, + m_pDriver->GetDeviceProps().vendorID); + } + else if(header->deviceID != m_pDriver->GetDeviceProps().deviceID) + { + m_PipeCacheBlob.clear(); + RDCLOG("Pipeline cache header deviceID %u doesn't match %u", header->deviceID, + m_pDriver->GetDeviceProps().deviceID); + } + else if(memcmp(header->uuid, m_pDriver->GetDeviceProps().pipelineCacheUUID, VK_UUID_SIZE) != 0) + { + m_PipeCacheBlob.clear(); + RDCLOG("Pipeline cache UUID doesn't match"); + } + } + } + + if(!m_PipeCacheBlob.empty()) + { + createInfo.initialDataSize = m_PipeCacheBlob.size(); + createInfo.pInitialData = m_PipeCacheBlob.data(); + } + + VkResult vkr = driver->vkCreatePipelineCache(m_Device, &createInfo, NULL, &m_PipelineCache); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } + SetCaching(false); } VulkanShaderCache::~VulkanShaderCache() { + if(m_PipelineCache != VK_NULL_HANDLE) + { + bytebuf blob; + size_t size = 0; + ObjDisp(m_Device)->GetPipelineCacheData(Unwrap(m_Device), Unwrap(m_PipelineCache), &size, NULL); + blob.resize(size); + ObjDisp(m_Device)->GetPipelineCacheData(Unwrap(m_Device), Unwrap(m_PipelineCache), &size, + blob.data()); + SetPipeCacheBlob(blob); + m_pDriver->vkDestroyPipelineCache(m_Device, m_PipelineCache, NULL); + } + if(m_ShaderCacheDirty) { SaveShaderCache("vkshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion, m_ShaderCache, @@ -289,6 +366,56 @@ rdcstr VulkanShaderCache::GetSPIRVBlob(const rdcspv::CompilationSettings &settin return errors; } +void VulkanShaderCache::GetPipeCacheBlob() +{ + m_PipeCacheBlob.clear(); + + uint32_t hash = strhash(StringFormat::Fmt("PipelineCache%x%x", m_pDriver->GetDeviceProps().vendorID, + m_pDriver->GetDeviceProps().deviceID) + .c_str()); + + auto it = m_ShaderCache.find(hash); + + if(it != m_ShaderCache.end()) + { + SPIRVBlob blob = it->second; + + // first uint32_t is the real byte size, since we rounded up to the nearest uint32 to store in a + // SPIRVBlob + uint32_t size = blob->at(0); + if(size == rdcspv::MagicNumber) + { + RDCLOG("Hash collision - pipeline cache trampled"); + return; + } + m_PipeCacheBlob.resize(size); + memcpy(m_PipeCacheBlob.data(), blob->data() + 1, m_PipeCacheBlob.size()); + } +} + +void VulkanShaderCache::SetPipeCacheBlob(bytebuf &blob) +{ + if(m_PipeCacheBlob == blob) + return; + + VkPipeCacheHeader *header = (VkPipeCacheHeader *)blob.data(); + + uint32_t hash = + strhash(StringFormat::Fmt("PipelineCache%x%x", header->vendorID, header->deviceID).c_str()); + + rdcarray *spirvBlob = new rdcarray(); + + // align the size up to the nearest 4, and add one extra for us to store the real byte size + spirvBlob->resize(AlignUp4(blob.size()) / 4 + 1); + + // store the size, then the real data after that + (*spirvBlob)[0] = (uint32_t)blob.size(); + memcpy(spirvBlob->data() + 1, blob.data(), blob.size()); + + m_ShaderCache[hash] = spirvBlob; + m_ShaderCacheDirty = true; +} + void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo, ResourceId pipeline) { diff --git a/renderdoc/driver/vulkan/vk_shader_cache.h b/renderdoc/driver/vulkan/vk_shader_cache.h index 98b12486f..17c26f431 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.h +++ b/renderdoc/driver/vulkan/vk_shader_cache.h @@ -74,7 +74,7 @@ public: { return m_BuiltinShaderModules[(size_t)builtin]; } - + VkPipelineCache GetPipeCache() { return m_PipelineCache; } void MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo, ResourceId pipeline); void MakeComputePipelineInfo(VkComputePipelineCreateInfo &pipeCreateInfo, ResourceId pipeline); @@ -84,9 +84,15 @@ private: static const uint32_t m_ShaderCacheMagic = 0xf00d00d5; static const uint32_t m_ShaderCacheVersion = 1; + void GetPipeCacheBlob(); + void SetPipeCacheBlob(bytebuf &blob); + WrappedVulkan *m_pDriver = NULL; VkDevice m_Device = VK_NULL_HANDLE; + bytebuf m_PipeCacheBlob; + VkPipelineCache m_PipelineCache = VK_NULL_HANDLE; + rdcstr m_GlobalDefines; bool m_ShaderCacheDirty = false, m_CacheShaders = false; diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index 6bb7b04a6..b6738489c 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -1170,8 +1170,9 @@ private: }; VkPipeline pipe = VK_NULL_HANDLE; - VkResult vkr = m_pDriver->vkCreateGraphicsPipelines(m_pDriver->GetDev(), VK_NULL_HANDLE, 1, - &graphicsPipeInfo, NULL, &pipe); + VkResult vkr = m_pDriver->vkCreateGraphicsPipelines(m_pDriver->GetDev(), + m_pDriver->GetShaderCache()->GetPipeCache(), + 1, &graphicsPipeInfo, NULL, &pipe); if(vkr != VK_SUCCESS) { RDCERR("Failed creating debug pipeline"); @@ -3121,8 +3122,8 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ } VkPipeline inputsPipe; - vkr = - m_pDriver->vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &graphicsInfo, NULL, &inputsPipe); + vkr = m_pDriver->vkCreateGraphicsPipelines(dev, m_pDriver->GetShaderCache()->GetPipeCache(), 1, + &graphicsInfo, NULL, &inputsPipe); RDCASSERTEQUAL(vkr, VK_SUCCESS); // make copy of state to draw from diff --git a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp index 24329b309..8591ec50d 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp @@ -394,7 +394,7 @@ VkResult WrappedVulkan::vkGetPipelineCacheData(VkDevice device, VkPipelineCache uint32_t *ptr = (uint32_t *)pData; - ptr[0] = (uint32_t)totalSize; + ptr[0] = 16 + VK_UUID_SIZE; ptr[1] = VK_PIPELINE_CACHE_HEADER_VERSION_ONE; // just in case the user expects a valid vendorID/deviceID, write the real one // MULTIDEVICE need to get the right physical device for this device