Vulkan: Add support for VkPipelineCache
The cache is initialized from the application's blob cache and is
occasionally written back to it for disk storage.
Bug: angleproject:2516
Change-Id: I4cba4b00a7b9641c2983ef07159bc62cd10a5519
Reviewed-on: https://chromium-review.googlesource.com/1241373
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 694541f..cd474fd 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -19,6 +19,7 @@
#include "libANGLE/renderer/driver_utils.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/CompilerVk.h"
+#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
@@ -44,6 +45,8 @@
// We currently only allocate 2 uniform buffer per descriptor set, one for the fragment shader and
// one for the vertex shader.
constexpr size_t kUniformBufferDescriptorsPerDescriptorSet = 2;
+// Update the pipeline cache every this many swaps (if 60fps, this means every 10 minutes)
+static constexpr uint32_t kPipelineCacheVkUpdatePeriod = 10 * 60 * 60;
bool ShouldEnableMockICD(const egl::AttributeMap &attribs)
{
@@ -295,7 +298,8 @@
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mDevice(VK_NULL_HANDLE),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
- mCurrentQueueSerial(mQueueSerialFactory.generate())
+ mCurrentQueueSerial(mQueueSerialFactory.generate()),
+ mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod)
{
}
@@ -316,6 +320,7 @@
mRenderPassCache.destroy(mDevice);
mPipelineCache.destroy(mDevice);
+ mPipelineCacheVk.destroy(mDevice);
mShaderLibrary.destroy(mDevice);
GlslangWrapper::Release();
@@ -350,7 +355,7 @@
mPhysicalDevice = VK_NULL_HANDLE;
}
-angle::Result RendererVk::initialize(vk::Context *context,
+angle::Result RendererVk::initialize(DisplayVk *displayVk,
const egl::AttributeMap &attribs,
const char *wsiName)
{
@@ -361,24 +366,25 @@
// Gather global layer properties.
uint32_t instanceLayerCount = 0;
- ANGLE_VK_TRY(context, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
+ ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerCount);
if (instanceLayerCount > 0)
{
- ANGLE_VK_TRY(context, vkEnumerateInstanceLayerProperties(&instanceLayerCount,
- instanceLayerProps.data()));
+ ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount,
+ instanceLayerProps.data()));
}
uint32_t instanceExtensionCount = 0;
- ANGLE_VK_TRY(context,
+ ANGLE_VK_TRY(displayVk,
vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr));
std::vector<VkExtensionProperties> instanceExtensionProps(instanceExtensionCount);
if (instanceExtensionCount > 0)
{
- ANGLE_VK_TRY(context, vkEnumerateInstanceExtensionProperties(
- nullptr, &instanceExtensionCount, instanceExtensionProps.data()));
+ ANGLE_VK_TRY(displayVk,
+ vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount,
+ instanceExtensionProps.data()));
}
const char *const *enabledLayerNames = nullptr;
@@ -402,7 +408,7 @@
}
// Verify the required extensions are in the extension names set. Fail if not.
- ANGLE_VK_TRY(context,
+ ANGLE_VK_TRY(displayVk,
VerifyExtensionsPresent(instanceExtensionProps, enabledInstanceExtensions));
VkApplicationInfo applicationInfo;
@@ -427,7 +433,7 @@
instanceInfo.enabledLayerCount = enabledLayerCount;
instanceInfo.ppEnabledLayerNames = enabledLayerNames;
- ANGLE_VK_TRY(context, vkCreateInstance(&instanceInfo, nullptr, &mInstance));
+ ANGLE_VK_TRY(displayVk, vkCreateInstance(&instanceInfo, nullptr, &mInstance));
if (mEnableValidationLayers)
{
@@ -444,18 +450,18 @@
auto createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
vkGetInstanceProcAddr(mInstance, "vkCreateDebugReportCallbackEXT"));
ASSERT(createDebugReportCallback);
- ANGLE_VK_TRY(context, createDebugReportCallback(mInstance, &debugReportInfo, nullptr,
- &mDebugReportCallback));
+ ANGLE_VK_TRY(displayVk, createDebugReportCallback(mInstance, &debugReportInfo, nullptr,
+ &mDebugReportCallback));
}
uint32_t physicalDeviceCount = 0;
- ANGLE_VK_TRY(context, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));
- ANGLE_VK_CHECK(context, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED);
+ ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));
+ ANGLE_VK_CHECK(displayVk, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED);
// TODO(jmadill): Handle multiple physical devices. For now, use the first device.
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
- ANGLE_VK_TRY(context, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount,
- physicalDevices.data()));
+ ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount,
+ physicalDevices.data()));
ChoosePhysicalDevice(physicalDevices, enableMockICD, &mPhysicalDevice,
&mPhysicalDeviceProperties);
@@ -465,7 +471,7 @@
uint32_t queueCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
- ANGLE_VK_CHECK(context, queueCount > 0, VK_ERROR_INITIALIZATION_FAILED);
+ ANGLE_VK_CHECK(displayVk, queueCount > 0, VK_ERROR_INITIALIZATION_FAILED);
mQueueFamilyProperties.resize(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount,
@@ -488,7 +494,7 @@
}
}
- ANGLE_VK_CHECK(context, graphicsQueueFamilyCount > 0, VK_ERROR_INITIALIZATION_FAILED);
+ ANGLE_VK_CHECK(displayVk, graphicsQueueFamilyCount > 0, VK_ERROR_INITIALIZATION_FAILED);
initFeatures();
@@ -496,7 +502,7 @@
// queue, we'll have to wait until we see a WindowSurface to know which supports present.
if (graphicsQueueFamilyCount == 1)
{
- ANGLE_TRY(initializeDevice(context, firstGraphicsQueueFamily));
+ ANGLE_TRY(initializeDevice(displayVk, firstGraphicsQueueFamily));
}
// Store the physical device memory properties so we can find the right memory pools.
@@ -511,29 +517,29 @@
return angle::Result::Continue();
}
-angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueFamilyIndex)
+angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex)
{
uint32_t deviceLayerCount = 0;
- ANGLE_VK_TRY(context,
+ ANGLE_VK_TRY(displayVk,
vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, nullptr));
std::vector<VkLayerProperties> deviceLayerProps(deviceLayerCount);
if (deviceLayerCount > 0)
{
- ANGLE_VK_TRY(context, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount,
- deviceLayerProps.data()));
+ ANGLE_VK_TRY(displayVk, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount,
+ deviceLayerProps.data()));
}
uint32_t deviceExtensionCount = 0;
- ANGLE_VK_TRY(context, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
- &deviceExtensionCount, nullptr));
+ ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
+ &deviceExtensionCount, nullptr));
std::vector<VkExtensionProperties> deviceExtensionProps(deviceExtensionCount);
if (deviceExtensionCount > 0)
{
- ANGLE_VK_TRY(context, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
- &deviceExtensionCount,
- deviceExtensionProps.data()));
+ ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
+ &deviceExtensionCount,
+ deviceExtensionProps.data()));
}
const char *const *enabledLayerNames = nullptr;
@@ -553,7 +559,7 @@
enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
}
- ANGLE_VK_TRY(context, VerifyExtensionsPresent(deviceExtensionProps, enabledDeviceExtensions));
+ ANGLE_VK_TRY(displayVk, VerifyExtensionsPresent(deviceExtensionProps, enabledDeviceExtensions));
VkDeviceQueueCreateInfo queueCreateInfo;
@@ -581,7 +587,7 @@
enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data();
createInfo.pEnabledFeatures = nullptr; // TODO(jmadill): features
- ANGLE_VK_TRY(context, vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice));
+ ANGLE_VK_TRY(displayVk, vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice));
mCurrentQueueFamilyIndex = queueFamilyIndex;
@@ -594,12 +600,15 @@
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
commandPoolInfo.queueFamilyIndex = mCurrentQueueFamilyIndex;
- ANGLE_TRY(mCommandPool.init(context, commandPoolInfo));
+ ANGLE_TRY(mCommandPool.init(displayVk, commandPoolInfo));
+
+ // Initialize the vulkan pipeline cache
+ ANGLE_TRY(initPipelineCacheVk(displayVk));
return angle::Result::Continue();
}
-angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
+angle::Result RendererVk::selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface,
uint32_t *presentQueueOut)
{
@@ -611,7 +620,7 @@
// Check if the current device supports present on this surface.
VkBool32 supportsPresent = VK_FALSE;
- ANGLE_VK_TRY(context,
+ ANGLE_VK_TRY(displayVk,
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mCurrentQueueFamilyIndex,
surface, &supportsPresent));
@@ -631,8 +640,8 @@
if ((queueInfo.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
{
VkBool32 supportsPresent = VK_FALSE;
- ANGLE_VK_TRY(context, vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, queueIndex,
- surface, &supportsPresent));
+ ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceSupportKHR(
+ mPhysicalDevice, queueIndex, surface, &supportsPresent));
if (supportsPresent == VK_TRUE)
{
@@ -642,8 +651,8 @@
}
}
- ANGLE_VK_CHECK(context, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED);
- ANGLE_TRY(initializeDevice(context, newPresentQueue.value()));
+ ANGLE_VK_CHECK(displayVk, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED);
+ ANGLE_TRY(initializeDevice(displayVk, newPresentQueue.value()));
*presentQueueOut = newPresentQueue.value();
return angle::Result::Continue();
@@ -701,6 +710,45 @@
#endif
}
+void RendererVk::initPipelineCacheVkKey()
+{
+ std::ostringstream hashStream("ANGLE Pipeline Cache: ", std::ios_base::ate);
+ // Add the pipeline cache UUID to make sure the blob cache always gives a compatible pipeline
+ // cache. It's not particularly necessary to write it as a hex number as done here, so long as
+ // there is no '\0' in the result.
+ for (const uint32_t c : mPhysicalDeviceProperties.pipelineCacheUUID)
+ {
+ hashStream << std::hex << c;
+ }
+ // Add the vendor and device id too for good measure.
+ hashStream << std::hex << mPhysicalDeviceProperties.vendorID;
+ hashStream << std::hex << mPhysicalDeviceProperties.deviceID;
+
+ const std::string &hashString = hashStream.str();
+ angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(hashString.c_str()),
+ hashString.length(), mPipelineCacheVkBlobKey.data());
+}
+
+angle::Result RendererVk::initPipelineCacheVk(DisplayVk *display)
+{
+ initPipelineCacheVkKey();
+
+ egl::BlobCache::Value initialData;
+ bool success = display->getBlobCache()->get(display->getScratchBuffer(),
+ mPipelineCacheVkBlobKey, &initialData);
+
+ VkPipelineCacheCreateInfo pipelineCacheCreateInfo;
+
+ pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ pipelineCacheCreateInfo.pNext = nullptr;
+ pipelineCacheCreateInfo.flags = 0;
+ pipelineCacheCreateInfo.initialDataSize = success ? initialData.size() : 0;
+ pipelineCacheCreateInfo.pInitialData = success ? initialData.data() : nullptr;
+
+ ANGLE_TRY(mPipelineCacheVk.init(display, pipelineCacheCreateInfo));
+ return angle::Result::Continue();
+}
+
void RendererVk::ensureCapsInitialized() const
{
if (!mCapsInitialized)
@@ -957,8 +1005,8 @@
ANGLE_TRY(
getCompatibleRenderPass(context, pipelineDesc.getRenderPassDesc(), &compatibleRenderPass));
- return mPipelineCache.getPipeline(context, *compatibleRenderPass, pipelineLayout,
- activeAttribLocationsMask, vertexShader.get(),
+ return mPipelineCache.getPipeline(context, mPipelineCacheVk, *compatibleRenderPass,
+ pipelineLayout, activeAttribLocationsMask, vertexShader.get(),
fragmentShader.get(), pipelineDesc, pipelineOut);
}
@@ -980,6 +1028,49 @@
pipelineLayoutOut);
}
+angle::Result RendererVk::syncPipelineCacheVk(DisplayVk *displayVk)
+{
+ ASSERT(mPipelineCacheVk.valid());
+
+ if (--mPipelineCacheVkUpdateTimeout > 0)
+ {
+ return angle::Result::Continue();
+ }
+
+ mPipelineCacheVkUpdateTimeout = kPipelineCacheVkUpdatePeriod;
+
+ // Get the size of the cache.
+ size_t pipelineCacheSize = 0;
+ ANGLE_TRY(mPipelineCacheVk.getCacheData(displayVk, &pipelineCacheSize, nullptr));
+
+ angle::MemoryBuffer *pipelineCacheData = nullptr;
+ ANGLE_VK_CHECK_ALLOC(displayVk,
+ displayVk->getScratchBuffer(pipelineCacheSize, &pipelineCacheData));
+
+ size_t originalPipelineCacheSize = pipelineCacheSize;
+ angle::Result result =
+ mPipelineCacheVk.getCacheData(displayVk, &pipelineCacheSize, pipelineCacheData->data());
+ ANGLE_TRY(result);
+
+ // Note: currently we don't accept incomplete as we don't expect it (the full size of cache
+ // was determined just above), so receiving it hints at an implementation bug we would want
+ // to know about early.
+ ASSERT(result != angle::Result::Incomplete());
+
+ // If vkGetPipelineCacheData ends up writing fewer bytes than requested, zero out the rest of
+ // the buffer to avoid leaking garbage memory.
+ ASSERT(pipelineCacheSize <= originalPipelineCacheSize);
+ if (pipelineCacheSize < originalPipelineCacheSize)
+ {
+ memset(pipelineCacheData->data() + pipelineCacheSize, 0,
+ originalPipelineCacheSize - pipelineCacheSize);
+ }
+
+ displayVk->getBlobCache()->putApplication(mPipelineCacheVkBlobKey, *pipelineCacheData);
+
+ return angle::Result::Continue();
+}
+
vk::ShaderLibrary *RendererVk::getShaderLibrary()
{
return &mShaderLibrary;