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>
This commit is contained in:
Shahbaz Youssefi 2018-09-24 16:39:26 -04:00 коммит произвёл Commit Bot
Родитель 5ddbdbf70c
Коммит 996628a4a1
23 изменённых файлов: 344 добавлений и 119 удалений

Просмотреть файл

@ -956,7 +956,7 @@ if (is_android) {
}
android_apk("angle_apk") {
deps = [
":angle_apk_assets"
":angle_apk_assets",
]
if (build_apk_secondary_abi && android_64bit_target_cpu) {
secondary_abi_shared_libraries = [

Просмотреть файл

@ -15,10 +15,6 @@ namespace angle
{
// MemoryBuffer implementation.
MemoryBuffer::MemoryBuffer() : mSize(0), mData(nullptr)
{
}
MemoryBuffer::~MemoryBuffer()
{
free(mData);

Просмотреть файл

@ -20,7 +20,8 @@ namespace angle
class MemoryBuffer final : NonCopyable
{
public:
MemoryBuffer();
MemoryBuffer() = default;
MemoryBuffer(size_t size) { resize(size); }
~MemoryBuffer();
MemoryBuffer(MemoryBuffer &&other);
@ -51,8 +52,8 @@ class MemoryBuffer final : NonCopyable
void fill(uint8_t datum);
private:
size_t mSize;
uint8_t *mData;
size_t mSize = 0;
uint8_t *mData = nullptr;
};
class ScratchBuffer final : NonCopyable

Просмотреть файл

@ -52,6 +52,14 @@ void BlobCache::put(const BlobCache::Key &key, angle::MemoryBuffer &&value)
}
}
void BlobCache::putApplication(const BlobCache::Key &key, const angle::MemoryBuffer &value)
{
if (areBlobCacheFuncsSet())
{
mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
}
}
void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source)
{
CacheEntry newEntry;
@ -66,7 +74,7 @@ void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value,
}
}
bool BlobCache::get(const gl::Context *context,
bool BlobCache::get(angle::ScratchBuffer *scratchBuffer,
const BlobCache::Key &key,
BlobCache::Value *valueOut)
{
@ -79,19 +87,19 @@ bool BlobCache::get(const gl::Context *context,
return false;
}
angle::MemoryBuffer *scratchBuffer;
bool result = context->getScratchBuffer(valueSize, &scratchBuffer);
angle::MemoryBuffer *scratchMemory;
bool result = scratchBuffer->get(valueSize, &scratchMemory);
if (!result)
{
ERR() << "Failed to allocate memory for binary blob";
return false;
}
valueSize = mGetBlobFunc(key.data(), key.size(), scratchBuffer->data(), valueSize);
valueSize = mGetBlobFunc(key.data(), key.size(), scratchMemory->data(), valueSize);
// Make sure the key/value pair still exists/is unchanged after the second call
// (modifications to the application cache by another thread are a possibility)
if (static_cast<size_t>(valueSize) != scratchBuffer->size())
if (static_cast<size_t>(valueSize) != scratchMemory->size())
{
// This warning serves to find issues with the application cache, none of which are
// currently known to be thread-safe. If such a use ever arises, this WARN can be
@ -100,7 +108,7 @@ bool BlobCache::get(const gl::Context *context,
return false;
}
*valueOut = BlobCache::Value(scratchBuffer->data(), scratchBuffer->size());
*valueOut = BlobCache::Value(scratchMemory->data(), scratchMemory->size());
return true;
}

Просмотреть файл

@ -61,7 +61,7 @@ class BlobCache final : angle::NonCopyable
Value(const uint8_t *ptr, size_t sz) : mPtr(ptr), mSize(sz) {}
// A very basic struct to hold the pointer and size together. The objects of this class
// doesn't own the memory.
// don't own the memory.
const uint8_t *data() { return mPtr; }
size_t size() { return mSize; }
@ -88,6 +88,9 @@ class BlobCache final : angle::NonCopyable
// will be used. Otherwise the value is cached in this object.
void put(const BlobCache::Key &key, angle::MemoryBuffer &&value);
// Store a key-blob pair in the application cache, only if application callbacks are set.
void putApplication(const BlobCache::Key &key, const angle::MemoryBuffer &value);
// Store a key-blob pair in the cache without making callbacks to the application. This is used
// to repopulate this object's cache on startup without generating callback calls.
void populate(const BlobCache::Key &key,
@ -96,10 +99,14 @@ class BlobCache final : angle::NonCopyable
// Check if the cache contains the blob corresponding to this key. If application callbacks are
// set, those will be used. Otherwise they key is looked up in this object's cache.
bool get(const gl::Context *context, const BlobCache::Key &key, BlobCache::Value *valueOut);
ANGLE_NO_DISCARD bool get(angle::ScratchBuffer *scratchBuffer,
const BlobCache::Key &key,
BlobCache::Value *valueOut);
// For querying the contents of the cache.
bool getAt(size_t index, const BlobCache::Key **keyOut, BlobCache::Value *valueOut);
ANGLE_NO_DISCARD bool getAt(size_t index,
const BlobCache::Key **keyOut,
BlobCache::Value *valueOut);
// Evict a blob from the binary cache.
void remove(const BlobCache::Key &key);

Просмотреть файл

@ -1543,6 +1543,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
angle::MemoryBuffer **scratchBufferOut) const;
ANGLE_NO_DISCARD bool getZeroFilledBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **zeroBufferOut) const;
angle::ScratchBuffer *getScratchBuffer() const { return &mScratchBuffer; }
Error prepareForDispatch();

Просмотреть файл

@ -447,6 +447,8 @@ void Display::setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap
Error Display::initialize()
{
mImplementation->setBlobCache(&mBlobCache);
// TODO(jmadill): Store Platform in Display and init here.
const angle::PlatformMethods *platformMethods =
reinterpret_cast<const angle::PlatformMethods *>(

Просмотреть файл

@ -113,13 +113,13 @@ namespace angle
{
Result::operator gl::Error() const
{
if (mStop)
{
return gl::Error(GL_INTERNAL_ERROR_ANGLEX);
}
else
if (mResult == ResultValue::kContinue)
{
return gl::NoError();
}
else
{
return gl::Error(GL_INTERNAL_ERROR_ANGLEX);
}
}
} // namespace angle

Просмотреть файл

@ -241,16 +241,15 @@ inline Error NoError()
// TODO(jmadill): Remove this once refactor is complete. http://anglebug.com/2491
#define ANGLE_TRY_HANDLE(CONTEXT, EXPR) \
\
{ \
{ \
auto ANGLE_LOCAL_VAR = (EXPR); \
if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR.isError())) \
{ \
CONTEXT->handleError(ANGLE_LOCAL_VAR); \
return angle::Result::Stop(); \
} \
\
}
} \
ANGLE_EMPTY_STATEMENT
#define ANGLE_TRY_RESULT(EXPR, RESULT) \
{ \
@ -281,15 +280,20 @@ inline Error NoError()
namespace angle
{
// Result signals if calling code should continue running or early exit. A value of Stop() can
// either indicate and Error or a non-Error early exit condition such as a detected no-op.
// either indicate an Error or a non-Error early exit condition such as a detected no-op. A few
// other values exist to signal special cases that are neither success nor failure but require
// special attention.
class ANGLE_NO_DISCARD Result
{
public:
// TODO(jmadill): Rename when refactor is complete. http://anglebug.com/2491
bool isError() const { return mStop; }
bool isError() const { return mResult == ResultValue::kStop; }
Result getError() { return *this; }
Result getResult() { return *this; }
static Result Stop() { return Result(true); }
static Result Continue() { return Result(false); }
static Result Continue() { return Result(ResultValue::kContinue); }
static Result Stop() { return Result(ResultValue::kStop); }
static Result Incomplete() { return Result(ResultValue::kIncomplete); }
// TODO(jmadill): Remove when refactor is complete. http://anglebug.com/2491
operator gl::Error() const;
@ -301,13 +305,20 @@ class ANGLE_NO_DISCARD Result
return operator gl::Error();
}
bool operator==(Result other) const { return mStop == other.mStop; }
bool operator==(Result other) const { return mResult == other.mResult; }
bool operator!=(Result other) const { return mStop != other.mStop; }
bool operator!=(Result other) const { return mResult != other.mResult; }
private:
Result(bool stop) : mStop(stop) {}
bool mStop;
enum class ResultValue
{
kContinue = 0,
kStop,
kIncomplete,
};
Result(ResultValue stop) : mResult(stop) {}
ResultValue mResult;
};
} // namespace angle

Просмотреть файл

@ -681,7 +681,7 @@ bool MemoryProgramCache::get(const Context *context,
const egl::BlobCache::Key &programHash,
egl::BlobCache::Value *programOut)
{
return mBlobCache.get(context, programHash, programOut);
return mBlobCache.get(context->getScratchBuffer(), programHash, programOut);
}
bool MemoryProgramCache::getAt(size_t index,

Просмотреть файл

@ -15,7 +15,7 @@ namespace rx
{
DisplayImpl::DisplayImpl(const egl::DisplayState &state)
: mState(state), mExtensionsInitialized(false), mCapsInitialized(false)
: mState(state), mExtensionsInitialized(false), mCapsInitialized(false), mBlobCache(nullptr)
{
}

Просмотреть файл

@ -23,6 +23,7 @@
namespace egl
{
class AttributeMap;
class BlobCache;
class Display;
struct DisplayState;
struct Config;
@ -79,6 +80,9 @@ class DisplayImpl : public EGLImplFactory
const egl::DisplayExtensions &getExtensions() const;
void setBlobCache(egl::BlobCache *blobCache) { mBlobCache = blobCache; }
egl::BlobCache *getBlobCache() const { return mBlobCache; }
protected:
const egl::DisplayState &mState;
@ -91,6 +95,8 @@ class DisplayImpl : public EGLImplFactory
mutable bool mCapsInitialized;
mutable egl::Caps mCaps;
egl::BlobCache *mBlobCache;
};
}

Просмотреть файл

@ -150,6 +150,8 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
mDirtyBits = mNewCommandBufferDirtyBits;
}
#undef INIT
ContextVk::~ContextVk() = default;
void ContextVk::onDestroy(const gl::Context *context)

Просмотреть файл

@ -20,7 +20,7 @@ namespace rx
{
DisplayVk::DisplayVk(const egl::DisplayState &state)
: DisplayImpl(state), vk::Context(new RendererVk())
: DisplayImpl(state), vk::Context(new RendererVk()), mScratchBuffer(1000u)
{
}
@ -181,6 +181,12 @@ void DisplayVk::generateCaps(egl::Caps *outCaps) const
outCaps->textureNPOT = true;
}
bool DisplayVk::getScratchBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **scratchBufferOut) const
{
return mScratchBuffer.get(requstedSizeBytes, scratchBufferOut);
}
void DisplayVk::handleError(VkResult result, const char *file, unsigned int line)
{
std::stringstream errorStream;

Просмотреть файл

@ -10,6 +10,7 @@
#ifndef LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_
#define LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_
#include "common/MemoryBuffer.h"
#include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
@ -74,6 +75,10 @@ class DisplayVk : public DisplayImpl, public vk::Context
// returning a bool to indicate if the config should be supported.
virtual bool checkConfigSupport(egl::Config *config) = 0;
ANGLE_NO_DISCARD bool getScratchBuffer(size_t requestedSizeBytes,
angle::MemoryBuffer **scratchBufferOut) const;
angle::ScratchBuffer *getScratchBuffer() const { return &mScratchBuffer; }
void handleError(VkResult result, const char *file, unsigned int line) override;
// TODO(jmadill): Remove this once refactor is done. http://anglebug.com/2491
@ -87,6 +92,8 @@ class DisplayVk : public DisplayImpl, public vk::Context
void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
void generateCaps(egl::Caps *outCaps) const override;
mutable angle::ScratchBuffer mScratchBuffer;
std::string mStoredErrorString;
};

Просмотреть файл

@ -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 @@ namespace
// 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 @@ RendererVk::RendererVk()
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 @@ void RendererVk::onDestroy(vk::Context *context)
mRenderPassCache.destroy(mDevice);
mPipelineCache.destroy(mDevice);
mPipelineCacheVk.destroy(mDevice);
mShaderLibrary.destroy(mDevice);
GlslangWrapper::Release();
@ -350,7 +355,7 @@ void RendererVk::onDestroy(vk::Context *context)
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 @@ angle::Result RendererVk::initialize(vk::Context *context,
// 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 @@ angle::Result RendererVk::initialize(vk::Context *context,
}
// 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 @@ angle::Result RendererVk::initialize(vk::Context *context,
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 @@ angle::Result RendererVk::initialize(vk::Context *context,
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 @@ angle::Result RendererVk::initialize(vk::Context *context,
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::Result RendererVk::initialize(vk::Context *context,
}
}
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 @@ angle::Result RendererVk::initialize(vk::Context *context,
// 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 @@ angle::Result RendererVk::initialize(vk::Context *context,
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 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
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 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
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 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
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 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
// 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 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
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::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
}
}
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 @@ void RendererVk::initFeatures()
#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::Result RendererVk::getPipeline(vk::Context *context,
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 @@ angle::Result RendererVk::getPipelineLayout(
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;

Просмотреть файл

@ -14,6 +14,7 @@
#include <memory>
#include "common/angleutils.h"
#include "libANGLE/BlobCache.h"
#include "libANGLE/Caps.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/FeaturesVk.h"
@ -23,10 +24,12 @@
namespace egl
{
class AttributeMap;
class BlobCache;
}
namespace rx
{
class DisplayVk;
class FramebufferVk;
namespace vk
@ -40,7 +43,7 @@ class RendererVk : angle::NonCopyable
RendererVk();
~RendererVk();
angle::Result initialize(vk::Context *context,
angle::Result initialize(DisplayVk *displayVk,
const egl::AttributeMap &attribs,
const char *wsiName);
void onDestroy(vk::Context *context);
@ -57,7 +60,7 @@ class RendererVk : angle::NonCopyable
VkQueue getQueue() const { return mQueue; }
VkDevice getDevice() const { return mDevice; }
angle::Result selectPresentQueueForSurface(vk::Context *context,
angle::Result selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface,
uint32_t *presentQueueOut);
@ -132,6 +135,8 @@ class RendererVk : angle::NonCopyable
const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut);
angle::Result syncPipelineCacheVk(DisplayVk *displayVk);
// This should only be called from ResourceVk.
// TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandGraph *getCommandGraph();
@ -143,7 +148,7 @@ class RendererVk : angle::NonCopyable
const FeaturesVk &getFeatures() const { return mFeatures; }
private:
angle::Result initializeDevice(vk::Context *context, uint32_t queueFamilyIndex);
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const;
angle::Result submitFrame(vk::Context *context,
const VkSubmitInfo &submitInfo,
@ -152,6 +157,8 @@ class RendererVk : angle::NonCopyable
void freeAllInFlightResources();
angle::Result flushCommandGraph(vk::Context *context, vk::CommandBuffer *commandBatch);
void initFeatures();
void initPipelineCacheVkKey();
angle::Result initPipelineCacheVk(DisplayVk *display);
mutable bool mCapsInitialized;
mutable gl::Caps mNativeCaps;
@ -198,6 +205,10 @@ class RendererVk : angle::NonCopyable
RenderPassCache mRenderPassCache;
PipelineCache mPipelineCache;
vk::PipelineCache mPipelineCacheVk;
egl::BlobCache::Key mPipelineCacheVkBlobKey;
uint32_t mPipelineCacheVkUpdateTimeout;
// See CommandGraph.h for a desription of the Command Graph.
vk::CommandGraph mCommandGraph;

Просмотреть файл

@ -588,6 +588,8 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk)
// Get the next available swapchain image.
ANGLE_TRY(nextSwapchainImage(displayVk));
ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk));
return angle::Result::Continue();
}

Просмотреть файл

@ -11,7 +11,7 @@
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
#include "common/aligned_memory.h"
#include "libANGLE/SizedMRUCache.h"
#include "libANGLE/BlobCache.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
@ -436,6 +436,7 @@ void PipelineDesc::initDefaults()
}
angle::Result PipelineDesc::initializePipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,
@ -617,7 +618,7 @@ angle::Result PipelineDesc::initializePipeline(vk::Context *context,
createInfo.basePipelineHandle = VK_NULL_HANDLE;
createInfo.basePipelineIndex = 0;
ANGLE_TRY(pipelineOut->initGraphics(context, createInfo));
ANGLE_TRY(pipelineOut->initGraphics(context, createInfo, pipelineCacheVk));
return angle::Result::Continue();
}
@ -1125,6 +1126,7 @@ void PipelineCache::destroy(VkDevice device)
}
angle::Result PipelineCache::getPipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,
@ -1145,9 +1147,9 @@ angle::Result PipelineCache::getPipeline(vk::Context *context,
// This "if" is left here for the benefit of VulkanPipelineCachePerfTest.
if (context != nullptr)
{
ANGLE_TRY(desc.initializePipeline(context, compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, vertexModule, fragmentModule,
&newPipeline));
ANGLE_TRY(desc.initializePipeline(context, pipelineCacheVk, compatibleRenderPass,
pipelineLayout, activeAttribLocationsMask, vertexModule,
fragmentModule, &newPipeline));
}
// The Serial will be updated outside of this query.

Просмотреть файл

@ -344,6 +344,7 @@ class PipelineDesc final
void initDefaults();
angle::Result initializePipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,
@ -612,6 +613,7 @@ class PipelineCache final : angle::NonCopyable
void populate(const vk::PipelineDesc &desc, vk::Pipeline &&pipeline);
angle::Result getPipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,

Просмотреть файл

@ -826,29 +826,6 @@ angle::Result ShaderModule::init(Context *context, const VkShaderModuleCreateInf
return angle::Result::Continue();
}
// Pipeline implementation.
Pipeline::Pipeline()
{
}
void Pipeline::destroy(VkDevice device)
{
if (valid())
{
vkDestroyPipeline(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
angle::Result Pipeline::initGraphics(Context *context,
const VkGraphicsPipelineCreateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(context, vkCreateGraphicsPipelines(context->getDevice(), VK_NULL_HANDLE, 1,
&createInfo, nullptr, &mHandle));
return angle::Result::Continue();
}
// PipelineLayout implementation.
PipelineLayout::PipelineLayout()
{
@ -871,6 +848,73 @@ angle::Result PipelineLayout::init(Context *context, const VkPipelineLayoutCreat
return angle::Result::Continue();
}
// PipelineCache implementation.
PipelineCache::PipelineCache()
{
}
void PipelineCache::destroy(VkDevice device)
{
if (valid())
{
vkDestroyPipelineCache(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
angle::Result PipelineCache::init(Context *context, const VkPipelineCacheCreateInfo &createInfo)
{
ASSERT(!valid());
// Note: if we are concerned with memory usage of this cache, we should give it custom
// allocators. Also, failure of this function is of little importance.
ANGLE_VK_TRY(context,
vkCreatePipelineCache(context->getDevice(), &createInfo, nullptr, &mHandle));
return angle::Result::Continue();
}
angle::Result PipelineCache::getCacheData(Context *context, size_t *cacheSize, void *cacheData)
{
ASSERT(valid());
// Note: vkGetPipelineCacheData can return VK_INCOMPLETE if cacheSize is smaller than actual
// size. There are two usages of this function. One is with *cacheSize == 0 to query the size
// of the cache, and one is with an appropriate buffer to retrieve the cache contents.
// VK_INCOMPLETE in the first case is an expected output. In the second case, VK_INCOMPLETE is
// also acceptable and the resulting buffer will contain valid value by spec. Angle currently
// ensures *cacheSize to be either 0 or of enough size, therefore VK_INCOMPLETE is not expected.
angle::Result result = angle::Result::Stop();
ANGLE_VK_TRY_ALLOW_INCOMPLETE(
context, vkGetPipelineCacheData(context->getDevice(), mHandle, cacheSize, cacheData),
result);
return result;
}
// Pipeline implementation.
Pipeline::Pipeline()
{
}
void Pipeline::destroy(VkDevice device)
{
if (valid())
{
vkDestroyPipeline(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
angle::Result Pipeline::initGraphics(Context *context,
const VkGraphicsPipelineCreateInfo &createInfo,
const PipelineCache &pipelineCacheVk)
{
ASSERT(!valid());
ANGLE_VK_TRY(context,
vkCreateGraphicsPipelines(context->getDevice(), pipelineCacheVk.getHandle(), 1,
&createInfo, nullptr, &mHandle));
return angle::Result::Continue();
}
// DescriptorSetLayout implementation.
DescriptorSetLayout::DescriptorSetLayout()
{

Просмотреть файл

@ -94,7 +94,7 @@ namespace vk
{
struct Format;
// Abstracts error handling. Implemented by both ContextVk for GL and RendererVk for EGL errors.
// Abstracts error handling. Implemented by both ContextVk for GL and DisplayVk for EGL errors.
class Context : angle::NonCopyable
{
public:
@ -531,15 +531,6 @@ class ShaderModule final : public WrappedObject<ShaderModule, VkShaderModule>
angle::Result init(Context *context, const VkShaderModuleCreateInfo &createInfo);
};
class Pipeline final : public WrappedObject<Pipeline, VkPipeline>
{
public:
Pipeline();
void destroy(VkDevice device);
angle::Result initGraphics(Context *context, const VkGraphicsPipelineCreateInfo &createInfo);
};
class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayout>
{
public:
@ -549,6 +540,27 @@ class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayo
angle::Result init(Context *context, const VkPipelineLayoutCreateInfo &createInfo);
};
class PipelineCache final : public WrappedObject<PipelineCache, VkPipelineCache>
{
public:
PipelineCache();
void destroy(VkDevice device);
angle::Result init(Context *context, const VkPipelineCacheCreateInfo &createInfo);
angle::Result getCacheData(Context *context, size_t *cacheSize, void *cacheData);
};
class Pipeline final : public WrappedObject<Pipeline, VkPipeline>
{
public:
Pipeline();
void destroy(VkDevice device);
angle::Result initGraphics(Context *context,
const VkGraphicsPipelineCreateInfo &createInfo,
const PipelineCache &pipelineCacheVk);
};
class DescriptorSetLayout final : public WrappedObject<DescriptorSetLayout, VkDescriptorSetLayout>
{
public:
@ -757,6 +769,19 @@ VkColorComponentFlags GetColorComponentFlags(bool red, bool green, bool blue, bo
} \
ANGLE_EMPTY_STATEMENT
#define ANGLE_VK_TRY_ALLOW_INCOMPLETE(context, command, result) \
{ \
auto ANGLE_LOCAL_VAR = command; \
if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR != VK_SUCCESS && ANGLE_LOCAL_VAR != VK_INCOMPLETE)) \
{ \
context->handleError(ANGLE_LOCAL_VAR, __FILE__, __LINE__); \
return angle::Result::Stop(); \
} \
result = ANGLE_LOCAL_VAR == VK_INCOMPLETE ? angle::Result::Incomplete() \
: angle::Result::Continue(); \
} \
ANGLE_EMPTY_STATEMENT
#define ANGLE_VK_CHECK(context, test, error) ANGLE_VK_TRY(context, test ? VK_SUCCESS : error)
#define ANGLE_VK_CHECK_MATH(context, result) \

Просмотреть файл

@ -80,6 +80,7 @@ void VulkanPipelineCachePerfTest::step()
{
vk::RenderPass rp;
vk::PipelineLayout pl;
vk::PipelineCache pc;
vk::ShaderModule sm;
vk::PipelineAndSerial *result = nullptr;
gl::AttributesMask am;
@ -88,7 +89,7 @@ void VulkanPipelineCachePerfTest::step()
{
for (const auto &hit : mCacheHits)
{
(void)mCache.getPipeline(VK_NULL_HANDLE, rp, pl, am, sm, sm, hit, &result);
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, sm, sm, hit, &result);
}
}
@ -96,7 +97,7 @@ void VulkanPipelineCachePerfTest::step()
++missCount, ++mMissIndex)
{
const auto &miss = mCacheMisses[mMissIndex];
(void)mCache.getPipeline(VK_NULL_HANDLE, rp, pl, am, sm, sm, miss, &result);
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, sm, sm, miss, &result);
}
}