Commit 996628a4 by Shahbaz Youssefi Committed by Commit Bot

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/1241373Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 5ddbdbf7
...@@ -956,7 +956,7 @@ if (is_android) { ...@@ -956,7 +956,7 @@ if (is_android) {
} }
android_apk("angle_apk") { android_apk("angle_apk") {
deps = [ deps = [
":angle_apk_assets" ":angle_apk_assets",
] ]
if (build_apk_secondary_abi && android_64bit_target_cpu) { if (build_apk_secondary_abi && android_64bit_target_cpu) {
secondary_abi_shared_libraries = [ secondary_abi_shared_libraries = [
......
...@@ -15,10 +15,6 @@ namespace angle ...@@ -15,10 +15,6 @@ namespace angle
{ {
// MemoryBuffer implementation. // MemoryBuffer implementation.
MemoryBuffer::MemoryBuffer() : mSize(0), mData(nullptr)
{
}
MemoryBuffer::~MemoryBuffer() MemoryBuffer::~MemoryBuffer()
{ {
free(mData); free(mData);
......
...@@ -20,7 +20,8 @@ namespace angle ...@@ -20,7 +20,8 @@ namespace angle
class MemoryBuffer final : NonCopyable class MemoryBuffer final : NonCopyable
{ {
public: public:
MemoryBuffer(); MemoryBuffer() = default;
MemoryBuffer(size_t size) { resize(size); }
~MemoryBuffer(); ~MemoryBuffer();
MemoryBuffer(MemoryBuffer &&other); MemoryBuffer(MemoryBuffer &&other);
...@@ -51,8 +52,8 @@ class MemoryBuffer final : NonCopyable ...@@ -51,8 +52,8 @@ class MemoryBuffer final : NonCopyable
void fill(uint8_t datum); void fill(uint8_t datum);
private: private:
size_t mSize; size_t mSize = 0;
uint8_t *mData; uint8_t *mData = nullptr;
}; };
class ScratchBuffer final : NonCopyable class ScratchBuffer final : NonCopyable
......
...@@ -52,6 +52,14 @@ void BlobCache::put(const BlobCache::Key &key, angle::MemoryBuffer &&value) ...@@ -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) void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source)
{ {
CacheEntry newEntry; CacheEntry newEntry;
...@@ -66,7 +74,7 @@ void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, ...@@ -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, const BlobCache::Key &key,
BlobCache::Value *valueOut) BlobCache::Value *valueOut)
{ {
...@@ -79,19 +87,19 @@ bool BlobCache::get(const gl::Context *context, ...@@ -79,19 +87,19 @@ bool BlobCache::get(const gl::Context *context,
return false; return false;
} }
angle::MemoryBuffer *scratchBuffer; angle::MemoryBuffer *scratchMemory;
bool result = context->getScratchBuffer(valueSize, &scratchBuffer); bool result = scratchBuffer->get(valueSize, &scratchMemory);
if (!result) if (!result)
{ {
ERR() << "Failed to allocate memory for binary blob"; ERR() << "Failed to allocate memory for binary blob";
return false; 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 // 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) // (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 // 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 // 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, ...@@ -100,7 +108,7 @@ bool BlobCache::get(const gl::Context *context,
return false; return false;
} }
*valueOut = BlobCache::Value(scratchBuffer->data(), scratchBuffer->size()); *valueOut = BlobCache::Value(scratchMemory->data(), scratchMemory->size());
return true; return true;
} }
......
...@@ -61,7 +61,7 @@ class BlobCache final : angle::NonCopyable ...@@ -61,7 +61,7 @@ class BlobCache final : angle::NonCopyable
Value(const uint8_t *ptr, size_t sz) : mPtr(ptr), mSize(sz) {} 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 // 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; } const uint8_t *data() { return mPtr; }
size_t size() { return mSize; } size_t size() { return mSize; }
...@@ -88,6 +88,9 @@ class BlobCache final : angle::NonCopyable ...@@ -88,6 +88,9 @@ class BlobCache final : angle::NonCopyable
// will be used. Otherwise the value is cached in this object. // will be used. Otherwise the value is cached in this object.
void put(const BlobCache::Key &key, angle::MemoryBuffer &&value); 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 // 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. // to repopulate this object's cache on startup without generating callback calls.
void populate(const BlobCache::Key &key, void populate(const BlobCache::Key &key,
...@@ -96,10 +99,14 @@ class BlobCache final : angle::NonCopyable ...@@ -96,10 +99,14 @@ class BlobCache final : angle::NonCopyable
// Check if the cache contains the blob corresponding to this key. If application callbacks are // 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. // 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. // 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. // Evict a blob from the binary cache.
void remove(const BlobCache::Key &key); void remove(const BlobCache::Key &key);
......
...@@ -1543,6 +1543,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1543,6 +1543,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
angle::MemoryBuffer **scratchBufferOut) const; angle::MemoryBuffer **scratchBufferOut) const;
ANGLE_NO_DISCARD bool getZeroFilledBuffer(size_t requstedSizeBytes, ANGLE_NO_DISCARD bool getZeroFilledBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **zeroBufferOut) const; angle::MemoryBuffer **zeroBufferOut) const;
angle::ScratchBuffer *getScratchBuffer() const { return &mScratchBuffer; }
Error prepareForDispatch(); Error prepareForDispatch();
......
...@@ -447,6 +447,8 @@ void Display::setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap ...@@ -447,6 +447,8 @@ void Display::setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap
Error Display::initialize() Error Display::initialize()
{ {
mImplementation->setBlobCache(&mBlobCache);
// TODO(jmadill): Store Platform in Display and init here. // TODO(jmadill): Store Platform in Display and init here.
const angle::PlatformMethods *platformMethods = const angle::PlatformMethods *platformMethods =
reinterpret_cast<const angle::PlatformMethods *>( reinterpret_cast<const angle::PlatformMethods *>(
......
...@@ -113,13 +113,13 @@ namespace angle ...@@ -113,13 +113,13 @@ namespace angle
{ {
Result::operator gl::Error() const Result::operator gl::Error() const
{ {
if (mStop) if (mResult == ResultValue::kContinue)
{ {
return gl::Error(GL_INTERNAL_ERROR_ANGLEX); return gl::NoError();
} }
else else
{ {
return gl::NoError(); return gl::Error(GL_INTERNAL_ERROR_ANGLEX);
} }
} }
} // namespace angle } // namespace angle
...@@ -241,16 +241,15 @@ inline Error NoError() ...@@ -241,16 +241,15 @@ inline Error NoError()
// TODO(jmadill): Remove this once refactor is complete. http://anglebug.com/2491 // TODO(jmadill): Remove this once refactor is complete. http://anglebug.com/2491
#define ANGLE_TRY_HANDLE(CONTEXT, EXPR) \ #define ANGLE_TRY_HANDLE(CONTEXT, EXPR) \
\ { \
{ \
auto ANGLE_LOCAL_VAR = (EXPR); \ auto ANGLE_LOCAL_VAR = (EXPR); \
if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR.isError())) \ if (ANGLE_UNLIKELY(ANGLE_LOCAL_VAR.isError())) \
{ \ { \
CONTEXT->handleError(ANGLE_LOCAL_VAR); \ CONTEXT->handleError(ANGLE_LOCAL_VAR); \
return angle::Result::Stop(); \ return angle::Result::Stop(); \
} \ } \
\ } \
} ANGLE_EMPTY_STATEMENT
#define ANGLE_TRY_RESULT(EXPR, RESULT) \ #define ANGLE_TRY_RESULT(EXPR, RESULT) \
{ \ { \
...@@ -281,15 +280,20 @@ inline Error NoError() ...@@ -281,15 +280,20 @@ inline Error NoError()
namespace angle namespace angle
{ {
// Result signals if calling code should continue running or early exit. A value of Stop() can // 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 class ANGLE_NO_DISCARD Result
{ {
public: public:
// TODO(jmadill): Rename when refactor is complete. http://anglebug.com/2491 // 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(ResultValue::kContinue); }
static Result Continue() { return Result(false); } 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 // TODO(jmadill): Remove when refactor is complete. http://anglebug.com/2491
operator gl::Error() const; operator gl::Error() const;
...@@ -301,13 +305,20 @@ class ANGLE_NO_DISCARD Result ...@@ -301,13 +305,20 @@ class ANGLE_NO_DISCARD Result
return operator gl::Error(); 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: private:
Result(bool stop) : mStop(stop) {} enum class ResultValue
bool mStop; {
kContinue = 0,
kStop,
kIncomplete,
};
Result(ResultValue stop) : mResult(stop) {}
ResultValue mResult;
}; };
} // namespace angle } // namespace angle
......
...@@ -681,7 +681,7 @@ bool MemoryProgramCache::get(const Context *context, ...@@ -681,7 +681,7 @@ bool MemoryProgramCache::get(const Context *context,
const egl::BlobCache::Key &programHash, const egl::BlobCache::Key &programHash,
egl::BlobCache::Value *programOut) egl::BlobCache::Value *programOut)
{ {
return mBlobCache.get(context, programHash, programOut); return mBlobCache.get(context->getScratchBuffer(), programHash, programOut);
} }
bool MemoryProgramCache::getAt(size_t index, bool MemoryProgramCache::getAt(size_t index,
......
...@@ -15,7 +15,7 @@ namespace rx ...@@ -15,7 +15,7 @@ namespace rx
{ {
DisplayImpl::DisplayImpl(const egl::DisplayState &state) DisplayImpl::DisplayImpl(const egl::DisplayState &state)
: mState(state), mExtensionsInitialized(false), mCapsInitialized(false) : mState(state), mExtensionsInitialized(false), mCapsInitialized(false), mBlobCache(nullptr)
{ {
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
namespace egl namespace egl
{ {
class AttributeMap; class AttributeMap;
class BlobCache;
class Display; class Display;
struct DisplayState; struct DisplayState;
struct Config; struct Config;
...@@ -79,6 +80,9 @@ class DisplayImpl : public EGLImplFactory ...@@ -79,6 +80,9 @@ class DisplayImpl : public EGLImplFactory
const egl::DisplayExtensions &getExtensions() const; const egl::DisplayExtensions &getExtensions() const;
void setBlobCache(egl::BlobCache *blobCache) { mBlobCache = blobCache; }
egl::BlobCache *getBlobCache() const { return mBlobCache; }
protected: protected:
const egl::DisplayState &mState; const egl::DisplayState &mState;
...@@ -91,6 +95,8 @@ class DisplayImpl : public EGLImplFactory ...@@ -91,6 +95,8 @@ class DisplayImpl : public EGLImplFactory
mutable bool mCapsInitialized; mutable bool mCapsInitialized;
mutable egl::Caps mCaps; mutable egl::Caps mCaps;
egl::BlobCache *mBlobCache;
}; };
} }
......
...@@ -150,6 +150,8 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer) ...@@ -150,6 +150,8 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
mDirtyBits = mNewCommandBufferDirtyBits; mDirtyBits = mNewCommandBufferDirtyBits;
} }
#undef INIT
ContextVk::~ContextVk() = default; ContextVk::~ContextVk() = default;
void ContextVk::onDestroy(const gl::Context *context) void ContextVk::onDestroy(const gl::Context *context)
......
...@@ -20,7 +20,7 @@ namespace rx ...@@ -20,7 +20,7 @@ namespace rx
{ {
DisplayVk::DisplayVk(const egl::DisplayState &state) 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 ...@@ -181,6 +181,12 @@ void DisplayVk::generateCaps(egl::Caps *outCaps) const
outCaps->textureNPOT = true; 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) void DisplayVk::handleError(VkResult result, const char *file, unsigned int line)
{ {
std::stringstream errorStream; std::stringstream errorStream;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#ifndef LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_ #ifndef LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_
#define LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_ #define LIBANGLE_RENDERER_VULKAN_DISPLAYVK_H_
#include "common/MemoryBuffer.h"
#include "libANGLE/renderer/DisplayImpl.h" #include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/vulkan/vk_utils.h" #include "libANGLE/renderer/vulkan/vk_utils.h"
...@@ -74,6 +75,10 @@ class DisplayVk : public DisplayImpl, public vk::Context ...@@ -74,6 +75,10 @@ class DisplayVk : public DisplayImpl, public vk::Context
// returning a bool to indicate if the config should be supported. // returning a bool to indicate if the config should be supported.
virtual bool checkConfigSupport(egl::Config *config) = 0; 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; void handleError(VkResult result, const char *file, unsigned int line) override;
// TODO(jmadill): Remove this once refactor is done. http://anglebug.com/2491 // TODO(jmadill): Remove this once refactor is done. http://anglebug.com/2491
...@@ -87,6 +92,8 @@ class DisplayVk : public DisplayImpl, public vk::Context ...@@ -87,6 +92,8 @@ class DisplayVk : public DisplayImpl, public vk::Context
void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
void generateCaps(egl::Caps *outCaps) const override; void generateCaps(egl::Caps *outCaps) const override;
mutable angle::ScratchBuffer mScratchBuffer;
std::string mStoredErrorString; std::string mStoredErrorString;
}; };
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "libANGLE/renderer/driver_utils.h" #include "libANGLE/renderer/driver_utils.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h" #include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/CompilerVk.h" #include "libANGLE/renderer/vulkan/CompilerVk.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h" #include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h" #include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h"
...@@ -44,6 +45,8 @@ namespace ...@@ -44,6 +45,8 @@ namespace
// We currently only allocate 2 uniform buffer per descriptor set, one for the fragment shader and // We currently only allocate 2 uniform buffer per descriptor set, one for the fragment shader and
// one for the vertex shader. // one for the vertex shader.
constexpr size_t kUniformBufferDescriptorsPerDescriptorSet = 2; 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) bool ShouldEnableMockICD(const egl::AttributeMap &attribs)
{ {
...@@ -295,7 +298,8 @@ RendererVk::RendererVk() ...@@ -295,7 +298,8 @@ RendererVk::RendererVk()
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()), mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mDevice(VK_NULL_HANDLE), mDevice(VK_NULL_HANDLE),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()), mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
mCurrentQueueSerial(mQueueSerialFactory.generate()) mCurrentQueueSerial(mQueueSerialFactory.generate()),
mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod)
{ {
} }
...@@ -316,6 +320,7 @@ void RendererVk::onDestroy(vk::Context *context) ...@@ -316,6 +320,7 @@ void RendererVk::onDestroy(vk::Context *context)
mRenderPassCache.destroy(mDevice); mRenderPassCache.destroy(mDevice);
mPipelineCache.destroy(mDevice); mPipelineCache.destroy(mDevice);
mPipelineCacheVk.destroy(mDevice);
mShaderLibrary.destroy(mDevice); mShaderLibrary.destroy(mDevice);
GlslangWrapper::Release(); GlslangWrapper::Release();
...@@ -350,7 +355,7 @@ void RendererVk::onDestroy(vk::Context *context) ...@@ -350,7 +355,7 @@ void RendererVk::onDestroy(vk::Context *context)
mPhysicalDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE;
} }
angle::Result RendererVk::initialize(vk::Context *context, angle::Result RendererVk::initialize(DisplayVk *displayVk,
const egl::AttributeMap &attribs, const egl::AttributeMap &attribs,
const char *wsiName) const char *wsiName)
{ {
...@@ -361,24 +366,25 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -361,24 +366,25 @@ angle::Result RendererVk::initialize(vk::Context *context,
// Gather global layer properties. // Gather global layer properties.
uint32_t instanceLayerCount = 0; uint32_t instanceLayerCount = 0;
ANGLE_VK_TRY(context, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr)); ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerCount); std::vector<VkLayerProperties> instanceLayerProps(instanceLayerCount);
if (instanceLayerCount > 0) if (instanceLayerCount > 0)
{ {
ANGLE_VK_TRY(context, vkEnumerateInstanceLayerProperties(&instanceLayerCount, ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount,
instanceLayerProps.data())); instanceLayerProps.data()));
} }
uint32_t instanceExtensionCount = 0; uint32_t instanceExtensionCount = 0;
ANGLE_VK_TRY(context, ANGLE_VK_TRY(displayVk,
vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr)); vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr));
std::vector<VkExtensionProperties> instanceExtensionProps(instanceExtensionCount); std::vector<VkExtensionProperties> instanceExtensionProps(instanceExtensionCount);
if (instanceExtensionCount > 0) if (instanceExtensionCount > 0)
{ {
ANGLE_VK_TRY(context, vkEnumerateInstanceExtensionProperties( ANGLE_VK_TRY(displayVk,
nullptr, &instanceExtensionCount, instanceExtensionProps.data())); vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount,
instanceExtensionProps.data()));
} }
const char *const *enabledLayerNames = nullptr; const char *const *enabledLayerNames = nullptr;
...@@ -402,7 +408,7 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -402,7 +408,7 @@ angle::Result RendererVk::initialize(vk::Context *context,
} }
// Verify the required extensions are in the extension names set. Fail if not. // Verify the required extensions are in the extension names set. Fail if not.
ANGLE_VK_TRY(context, ANGLE_VK_TRY(displayVk,
VerifyExtensionsPresent(instanceExtensionProps, enabledInstanceExtensions)); VerifyExtensionsPresent(instanceExtensionProps, enabledInstanceExtensions));
VkApplicationInfo applicationInfo; VkApplicationInfo applicationInfo;
...@@ -427,7 +433,7 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -427,7 +433,7 @@ angle::Result RendererVk::initialize(vk::Context *context,
instanceInfo.enabledLayerCount = enabledLayerCount; instanceInfo.enabledLayerCount = enabledLayerCount;
instanceInfo.ppEnabledLayerNames = enabledLayerNames; instanceInfo.ppEnabledLayerNames = enabledLayerNames;
ANGLE_VK_TRY(context, vkCreateInstance(&instanceInfo, nullptr, &mInstance)); ANGLE_VK_TRY(displayVk, vkCreateInstance(&instanceInfo, nullptr, &mInstance));
if (mEnableValidationLayers) if (mEnableValidationLayers)
{ {
...@@ -444,17 +450,17 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -444,17 +450,17 @@ angle::Result RendererVk::initialize(vk::Context *context,
auto createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>( auto createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
vkGetInstanceProcAddr(mInstance, "vkCreateDebugReportCallbackEXT")); vkGetInstanceProcAddr(mInstance, "vkCreateDebugReportCallbackEXT"));
ASSERT(createDebugReportCallback); ASSERT(createDebugReportCallback);
ANGLE_VK_TRY(context, createDebugReportCallback(mInstance, &debugReportInfo, nullptr, ANGLE_VK_TRY(displayVk, createDebugReportCallback(mInstance, &debugReportInfo, nullptr,
&mDebugReportCallback)); &mDebugReportCallback));
} }
uint32_t physicalDeviceCount = 0; uint32_t physicalDeviceCount = 0;
ANGLE_VK_TRY(context, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr)); ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));
ANGLE_VK_CHECK(context, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED); ANGLE_VK_CHECK(displayVk, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED);
// TODO(jmadill): Handle multiple physical devices. For now, use the first device. // TODO(jmadill): Handle multiple physical devices. For now, use the first device.
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount); std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
ANGLE_VK_TRY(context, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount,
physicalDevices.data())); physicalDevices.data()));
ChoosePhysicalDevice(physicalDevices, enableMockICD, &mPhysicalDevice, ChoosePhysicalDevice(physicalDevices, enableMockICD, &mPhysicalDevice,
&mPhysicalDeviceProperties); &mPhysicalDeviceProperties);
...@@ -465,7 +471,7 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -465,7 +471,7 @@ angle::Result RendererVk::initialize(vk::Context *context,
uint32_t queueCount = 0; uint32_t queueCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr); 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); mQueueFamilyProperties.resize(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount,
...@@ -488,7 +494,7 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -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(); initFeatures();
...@@ -496,7 +502,7 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -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. // queue, we'll have to wait until we see a WindowSurface to know which supports present.
if (graphicsQueueFamilyCount == 1) 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. // Store the physical device memory properties so we can find the right memory pools.
...@@ -511,27 +517,27 @@ angle::Result RendererVk::initialize(vk::Context *context, ...@@ -511,27 +517,27 @@ angle::Result RendererVk::initialize(vk::Context *context,
return angle::Result::Continue(); 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; uint32_t deviceLayerCount = 0;
ANGLE_VK_TRY(context, ANGLE_VK_TRY(displayVk,
vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, nullptr)); vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, nullptr));
std::vector<VkLayerProperties> deviceLayerProps(deviceLayerCount); std::vector<VkLayerProperties> deviceLayerProps(deviceLayerCount);
if (deviceLayerCount > 0) if (deviceLayerCount > 0)
{ {
ANGLE_VK_TRY(context, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, ANGLE_VK_TRY(displayVk, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount,
deviceLayerProps.data())); deviceLayerProps.data()));
} }
uint32_t deviceExtensionCount = 0; uint32_t deviceExtensionCount = 0;
ANGLE_VK_TRY(context, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
&deviceExtensionCount, nullptr)); &deviceExtensionCount, nullptr));
std::vector<VkExtensionProperties> deviceExtensionProps(deviceExtensionCount); std::vector<VkExtensionProperties> deviceExtensionProps(deviceExtensionCount);
if (deviceExtensionCount > 0) if (deviceExtensionCount > 0)
{ {
ANGLE_VK_TRY(context, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
&deviceExtensionCount, &deviceExtensionCount,
deviceExtensionProps.data())); deviceExtensionProps.data()));
} }
...@@ -553,7 +559,7 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF ...@@ -553,7 +559,7 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
} }
ANGLE_VK_TRY(context, VerifyExtensionsPresent(deviceExtensionProps, enabledDeviceExtensions)); ANGLE_VK_TRY(displayVk, VerifyExtensionsPresent(deviceExtensionProps, enabledDeviceExtensions));
VkDeviceQueueCreateInfo queueCreateInfo; VkDeviceQueueCreateInfo queueCreateInfo;
...@@ -581,7 +587,7 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF ...@@ -581,7 +587,7 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data(); enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data();
createInfo.pEnabledFeatures = nullptr; // TODO(jmadill): features 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; mCurrentQueueFamilyIndex = queueFamilyIndex;
...@@ -594,12 +600,15 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF ...@@ -594,12 +600,15 @@ angle::Result RendererVk::initializeDevice(vk::Context *context, uint32_t queueF
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
commandPoolInfo.queueFamilyIndex = mCurrentQueueFamilyIndex; 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(); return angle::Result::Continue();
} }
angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context, angle::Result RendererVk::selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface, VkSurfaceKHR surface,
uint32_t *presentQueueOut) uint32_t *presentQueueOut)
{ {
...@@ -611,7 +620,7 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context, ...@@ -611,7 +620,7 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
// Check if the current device supports present on this surface. // Check if the current device supports present on this surface.
VkBool32 supportsPresent = VK_FALSE; VkBool32 supportsPresent = VK_FALSE;
ANGLE_VK_TRY(context, ANGLE_VK_TRY(displayVk,
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mCurrentQueueFamilyIndex, vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mCurrentQueueFamilyIndex,
surface, &supportsPresent)); surface, &supportsPresent));
...@@ -631,8 +640,8 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context, ...@@ -631,8 +640,8 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
if ((queueInfo.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) if ((queueInfo.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
{ {
VkBool32 supportsPresent = VK_FALSE; VkBool32 supportsPresent = VK_FALSE;
ANGLE_VK_TRY(context, vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, queueIndex, ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceSupportKHR(
surface, &supportsPresent)); mPhysicalDevice, queueIndex, surface, &supportsPresent));
if (supportsPresent == VK_TRUE) if (supportsPresent == VK_TRUE)
{ {
...@@ -642,8 +651,8 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context, ...@@ -642,8 +651,8 @@ angle::Result RendererVk::selectPresentQueueForSurface(vk::Context *context,
} }
} }
ANGLE_VK_CHECK(context, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED); ANGLE_VK_CHECK(displayVk, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED);
ANGLE_TRY(initializeDevice(context, newPresentQueue.value())); ANGLE_TRY(initializeDevice(displayVk, newPresentQueue.value()));
*presentQueueOut = newPresentQueue.value(); *presentQueueOut = newPresentQueue.value();
return angle::Result::Continue(); return angle::Result::Continue();
...@@ -701,6 +710,45 @@ void RendererVk::initFeatures() ...@@ -701,6 +710,45 @@ void RendererVk::initFeatures()
#endif #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 void RendererVk::ensureCapsInitialized() const
{ {
if (!mCapsInitialized) if (!mCapsInitialized)
...@@ -957,8 +1005,8 @@ angle::Result RendererVk::getPipeline(vk::Context *context, ...@@ -957,8 +1005,8 @@ angle::Result RendererVk::getPipeline(vk::Context *context,
ANGLE_TRY( ANGLE_TRY(
getCompatibleRenderPass(context, pipelineDesc.getRenderPassDesc(), &compatibleRenderPass)); getCompatibleRenderPass(context, pipelineDesc.getRenderPassDesc(), &compatibleRenderPass));
return mPipelineCache.getPipeline(context, *compatibleRenderPass, pipelineLayout, return mPipelineCache.getPipeline(context, mPipelineCacheVk, *compatibleRenderPass,
activeAttribLocationsMask, vertexShader.get(), pipelineLayout, activeAttribLocationsMask, vertexShader.get(),
fragmentShader.get(), pipelineDesc, pipelineOut); fragmentShader.get(), pipelineDesc, pipelineOut);
} }
...@@ -980,6 +1028,49 @@ angle::Result RendererVk::getPipelineLayout( ...@@ -980,6 +1028,49 @@ angle::Result RendererVk::getPipelineLayout(
pipelineLayoutOut); 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() vk::ShaderLibrary *RendererVk::getShaderLibrary()
{ {
return &mShaderLibrary; return &mShaderLibrary;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <memory> #include <memory>
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/BlobCache.h"
#include "libANGLE/Caps.h" #include "libANGLE/Caps.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h" #include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/FeaturesVk.h" #include "libANGLE/renderer/vulkan/FeaturesVk.h"
...@@ -23,10 +24,12 @@ ...@@ -23,10 +24,12 @@
namespace egl namespace egl
{ {
class AttributeMap; class AttributeMap;
class BlobCache;
} }
namespace rx namespace rx
{ {
class DisplayVk;
class FramebufferVk; class FramebufferVk;
namespace vk namespace vk
...@@ -40,7 +43,7 @@ class RendererVk : angle::NonCopyable ...@@ -40,7 +43,7 @@ class RendererVk : angle::NonCopyable
RendererVk(); RendererVk();
~RendererVk(); ~RendererVk();
angle::Result initialize(vk::Context *context, angle::Result initialize(DisplayVk *displayVk,
const egl::AttributeMap &attribs, const egl::AttributeMap &attribs,
const char *wsiName); const char *wsiName);
void onDestroy(vk::Context *context); void onDestroy(vk::Context *context);
...@@ -57,7 +60,7 @@ class RendererVk : angle::NonCopyable ...@@ -57,7 +60,7 @@ class RendererVk : angle::NonCopyable
VkQueue getQueue() const { return mQueue; } VkQueue getQueue() const { return mQueue; }
VkDevice getDevice() const { return mDevice; } VkDevice getDevice() const { return mDevice; }
angle::Result selectPresentQueueForSurface(vk::Context *context, angle::Result selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface, VkSurfaceKHR surface,
uint32_t *presentQueueOut); uint32_t *presentQueueOut);
...@@ -132,6 +135,8 @@ class RendererVk : angle::NonCopyable ...@@ -132,6 +135,8 @@ class RendererVk : angle::NonCopyable
const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts, const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut); vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut);
angle::Result syncPipelineCacheVk(DisplayVk *displayVk);
// This should only be called from ResourceVk. // This should only be called from ResourceVk.
// TODO(jmadill): Keep in ContextVk to enable threaded rendering. // TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandGraph *getCommandGraph(); vk::CommandGraph *getCommandGraph();
...@@ -143,7 +148,7 @@ class RendererVk : angle::NonCopyable ...@@ -143,7 +148,7 @@ class RendererVk : angle::NonCopyable
const FeaturesVk &getFeatures() const { return mFeatures; } const FeaturesVk &getFeatures() const { return mFeatures; }
private: private:
angle::Result initializeDevice(vk::Context *context, uint32_t queueFamilyIndex); angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
angle::Result submitFrame(vk::Context *context, angle::Result submitFrame(vk::Context *context,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
...@@ -152,6 +157,8 @@ class RendererVk : angle::NonCopyable ...@@ -152,6 +157,8 @@ class RendererVk : angle::NonCopyable
void freeAllInFlightResources(); void freeAllInFlightResources();
angle::Result flushCommandGraph(vk::Context *context, vk::CommandBuffer *commandBatch); angle::Result flushCommandGraph(vk::Context *context, vk::CommandBuffer *commandBatch);
void initFeatures(); void initFeatures();
void initPipelineCacheVkKey();
angle::Result initPipelineCacheVk(DisplayVk *display);
mutable bool mCapsInitialized; mutable bool mCapsInitialized;
mutable gl::Caps mNativeCaps; mutable gl::Caps mNativeCaps;
...@@ -198,6 +205,10 @@ class RendererVk : angle::NonCopyable ...@@ -198,6 +205,10 @@ class RendererVk : angle::NonCopyable
RenderPassCache mRenderPassCache; RenderPassCache mRenderPassCache;
PipelineCache mPipelineCache; PipelineCache mPipelineCache;
vk::PipelineCache mPipelineCacheVk;
egl::BlobCache::Key mPipelineCacheVkBlobKey;
uint32_t mPipelineCacheVkUpdateTimeout;
// See CommandGraph.h for a desription of the Command Graph. // See CommandGraph.h for a desription of the Command Graph.
vk::CommandGraph mCommandGraph; vk::CommandGraph mCommandGraph;
......
...@@ -588,6 +588,8 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk) ...@@ -588,6 +588,8 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk)
// Get the next available swapchain image. // Get the next available swapchain image.
ANGLE_TRY(nextSwapchainImage(displayVk)); ANGLE_TRY(nextSwapchainImage(displayVk));
ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk));
return angle::Result::Continue(); return angle::Result::Continue();
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "libANGLE/renderer/vulkan/vk_cache_utils.h" #include "libANGLE/renderer/vulkan/vk_cache_utils.h"
#include "common/aligned_memory.h" #include "common/aligned_memory.h"
#include "libANGLE/SizedMRUCache.h" #include "libANGLE/BlobCache.h"
#include "libANGLE/VertexAttribute.h" #include "libANGLE/VertexAttribute.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h" #include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h"
...@@ -436,6 +436,7 @@ void PipelineDesc::initDefaults() ...@@ -436,6 +436,7 @@ void PipelineDesc::initDefaults()
} }
angle::Result PipelineDesc::initializePipeline(vk::Context *context, angle::Result PipelineDesc::initializePipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const RenderPass &compatibleRenderPass, const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout, const PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask, const gl::AttributesMask &activeAttribLocationsMask,
...@@ -617,7 +618,7 @@ angle::Result PipelineDesc::initializePipeline(vk::Context *context, ...@@ -617,7 +618,7 @@ angle::Result PipelineDesc::initializePipeline(vk::Context *context,
createInfo.basePipelineHandle = VK_NULL_HANDLE; createInfo.basePipelineHandle = VK_NULL_HANDLE;
createInfo.basePipelineIndex = 0; createInfo.basePipelineIndex = 0;
ANGLE_TRY(pipelineOut->initGraphics(context, createInfo)); ANGLE_TRY(pipelineOut->initGraphics(context, createInfo, pipelineCacheVk));
return angle::Result::Continue(); return angle::Result::Continue();
} }
...@@ -1125,6 +1126,7 @@ void PipelineCache::destroy(VkDevice device) ...@@ -1125,6 +1126,7 @@ void PipelineCache::destroy(VkDevice device)
} }
angle::Result PipelineCache::getPipeline(vk::Context *context, angle::Result PipelineCache::getPipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const vk::RenderPass &compatibleRenderPass, const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout, const vk::PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask, const gl::AttributesMask &activeAttribLocationsMask,
...@@ -1145,9 +1147,9 @@ angle::Result PipelineCache::getPipeline(vk::Context *context, ...@@ -1145,9 +1147,9 @@ angle::Result PipelineCache::getPipeline(vk::Context *context,
// This "if" is left here for the benefit of VulkanPipelineCachePerfTest. // This "if" is left here for the benefit of VulkanPipelineCachePerfTest.
if (context != nullptr) if (context != nullptr)
{ {
ANGLE_TRY(desc.initializePipeline(context, compatibleRenderPass, pipelineLayout, ANGLE_TRY(desc.initializePipeline(context, pipelineCacheVk, compatibleRenderPass,
activeAttribLocationsMask, vertexModule, fragmentModule, pipelineLayout, activeAttribLocationsMask, vertexModule,
&newPipeline)); fragmentModule, &newPipeline));
} }
// The Serial will be updated outside of this query. // The Serial will be updated outside of this query.
......
...@@ -344,6 +344,7 @@ class PipelineDesc final ...@@ -344,6 +344,7 @@ class PipelineDesc final
void initDefaults(); void initDefaults();
angle::Result initializePipeline(vk::Context *context, angle::Result initializePipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const RenderPass &compatibleRenderPass, const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout, const PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask, const gl::AttributesMask &activeAttribLocationsMask,
...@@ -612,6 +613,7 @@ class PipelineCache final : angle::NonCopyable ...@@ -612,6 +613,7 @@ class PipelineCache final : angle::NonCopyable
void populate(const vk::PipelineDesc &desc, vk::Pipeline &&pipeline); void populate(const vk::PipelineDesc &desc, vk::Pipeline &&pipeline);
angle::Result getPipeline(vk::Context *context, angle::Result getPipeline(vk::Context *context,
const vk::PipelineCache &pipelineCacheVk,
const vk::RenderPass &compatibleRenderPass, const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout, const vk::PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask, const gl::AttributesMask &activeAttribLocationsMask,
......
...@@ -826,48 +826,92 @@ angle::Result ShaderModule::init(Context *context, const VkShaderModuleCreateInf ...@@ -826,48 +826,92 @@ angle::Result ShaderModule::init(Context *context, const VkShaderModuleCreateInf
return angle::Result::Continue(); return angle::Result::Continue();
} }
// Pipeline implementation. // PipelineLayout implementation.
Pipeline::Pipeline() PipelineLayout::PipelineLayout()
{ {
} }
void Pipeline::destroy(VkDevice device) void PipelineLayout::destroy(VkDevice device)
{ {
if (valid()) if (valid())
{ {
vkDestroyPipeline(device, mHandle, nullptr); vkDestroyPipelineLayout(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;
} }
} }
angle::Result Pipeline::initGraphics(Context *context, angle::Result PipelineLayout::init(Context *context, const VkPipelineLayoutCreateInfo &createInfo)
const VkGraphicsPipelineCreateInfo &createInfo)
{ {
ASSERT(!valid()); ASSERT(!valid());
ANGLE_VK_TRY(context, vkCreateGraphicsPipelines(context->getDevice(), VK_NULL_HANDLE, 1, ANGLE_VK_TRY(context,
&createInfo, nullptr, &mHandle)); vkCreatePipelineLayout(context->getDevice(), &createInfo, nullptr, &mHandle));
return angle::Result::Continue(); return angle::Result::Continue();
} }
// PipelineLayout implementation. // PipelineCache implementation.
PipelineLayout::PipelineLayout() PipelineCache::PipelineCache()
{ {
} }
void PipelineLayout::destroy(VkDevice device) void PipelineCache::destroy(VkDevice device)
{ {
if (valid()) if (valid())
{ {
vkDestroyPipelineLayout(device, mHandle, nullptr); vkDestroyPipelineCache(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;
} }
} }
angle::Result PipelineLayout::init(Context *context, const VkPipelineLayoutCreateInfo &createInfo) angle::Result PipelineCache::init(Context *context, const VkPipelineCacheCreateInfo &createInfo)
{ {
ASSERT(!valid()); 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, ANGLE_VK_TRY(context,
vkCreatePipelineLayout(context->getDevice(), &createInfo, nullptr, &mHandle)); 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(); return angle::Result::Continue();
} }
......
...@@ -94,7 +94,7 @@ namespace vk ...@@ -94,7 +94,7 @@ namespace vk
{ {
struct Format; 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 class Context : angle::NonCopyable
{ {
public: public:
...@@ -531,22 +531,34 @@ class ShaderModule final : public WrappedObject<ShaderModule, VkShaderModule> ...@@ -531,22 +531,34 @@ class ShaderModule final : public WrappedObject<ShaderModule, VkShaderModule>
angle::Result init(Context *context, const VkShaderModuleCreateInfo &createInfo); angle::Result init(Context *context, const VkShaderModuleCreateInfo &createInfo);
}; };
class Pipeline final : public WrappedObject<Pipeline, VkPipeline> class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayout>
{ {
public: public:
Pipeline(); PipelineLayout();
void destroy(VkDevice device); void destroy(VkDevice device);
angle::Result initGraphics(Context *context, const VkGraphicsPipelineCreateInfo &createInfo); angle::Result init(Context *context, const VkPipelineLayoutCreateInfo &createInfo);
}; };
class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayout> class PipelineCache final : public WrappedObject<PipelineCache, VkPipelineCache>
{ {
public: public:
PipelineLayout(); PipelineCache();
void destroy(VkDevice device); void destroy(VkDevice device);
angle::Result init(Context *context, const VkPipelineLayoutCreateInfo &createInfo); 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> class DescriptorSetLayout final : public WrappedObject<DescriptorSetLayout, VkDescriptorSetLayout>
...@@ -757,6 +769,19 @@ VkColorComponentFlags GetColorComponentFlags(bool red, bool green, bool blue, bo ...@@ -757,6 +769,19 @@ VkColorComponentFlags GetColorComponentFlags(bool red, bool green, bool blue, bo
} \ } \
ANGLE_EMPTY_STATEMENT 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(context, test, error) ANGLE_VK_TRY(context, test ? VK_SUCCESS : error)
#define ANGLE_VK_CHECK_MATH(context, result) \ #define ANGLE_VK_CHECK_MATH(context, result) \
......
...@@ -80,6 +80,7 @@ void VulkanPipelineCachePerfTest::step() ...@@ -80,6 +80,7 @@ void VulkanPipelineCachePerfTest::step()
{ {
vk::RenderPass rp; vk::RenderPass rp;
vk::PipelineLayout pl; vk::PipelineLayout pl;
vk::PipelineCache pc;
vk::ShaderModule sm; vk::ShaderModule sm;
vk::PipelineAndSerial *result = nullptr; vk::PipelineAndSerial *result = nullptr;
gl::AttributesMask am; gl::AttributesMask am;
...@@ -88,7 +89,7 @@ void VulkanPipelineCachePerfTest::step() ...@@ -88,7 +89,7 @@ void VulkanPipelineCachePerfTest::step()
{ {
for (const auto &hit : mCacheHits) 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() ...@@ -96,7 +97,7 @@ void VulkanPipelineCachePerfTest::step()
++missCount, ++mMissIndex) ++missCount, ++mMissIndex)
{ {
const auto &miss = mCacheMisses[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);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment