Vulkan: Add a new garbage type gated by fences.

This allows Vulkan EGL objects such as EGL Syncs and EGL Images to give their
garbage to the renderer before destroying.

BUG=angleproject:2464

Change-Id: I59b8e1080e4292bd0856e59a928750c7e77a372e
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1562522
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Geoff Lang 2019-04-10 09:58:21 -04:00 коммит произвёл Commit Bot
Родитель d7d42395ac
Коммит e755a5374f
13 изменённых файлов: 274 добавлений и 39 удалений

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

@ -32,13 +32,34 @@ void ImageVk::onDestroy(const egl::Display *display)
DisplayVk *displayVk = vk::GetImpl(display);
RendererVk *renderer = displayVk->getRenderer();
std::vector<vk::GarbageObjectBase> garbage;
if (mImage != nullptr && mOwnsImage)
{
mImage->releaseImage(renderer);
mImage->releaseStagingBuffer(renderer);
mImage->releaseImage(displayVk, &garbage);
mImage->releaseStagingBuffer(displayVk, &garbage);
delete mImage;
}
else if (egl::IsExternalImageTarget(mState.target))
{
ASSERT(mState.source != nullptr);
ExternalImageSiblingVk *externalImageSibling =
GetImplAs<ExternalImageSiblingVk>(GetAs<egl::ExternalImageSibling>(mState.source));
externalImageSibling->release(displayVk, &garbage);
}
mImage = nullptr;
if (!garbage.empty())
{
renderer->addGarbage(std::move(mImageLastUseFences), std::move(garbage));
}
else
{
for (vk::Shared<vk::Fence> &fence : mImageLastUseFences)
{
fence.reset(displayVk->getDevice());
}
}
}
egl::Error ImageVk::initialize(const egl::Display *display)
@ -129,6 +150,19 @@ angle::Result ImageVk::orphan(const gl::Context *context, egl::ImageSibling *sib
}
}
// Grab a fence from the releasing context to know when the image is no longer used
ASSERT(mContext != nullptr);
ContextVk *contextVk = vk::GetImpl(mContext);
// Flush the context to make sure the fence has been submitted.
ANGLE_TRY(contextVk->flushImpl());
vk::Shared<vk::Fence> fence = contextVk->getRenderer()->getLastSubmittedFence();
if (fence.isReferenced())
{
mImageLastUseFences.push_back(std::move(fence));
}
return angle::Result::Continue;
}

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

@ -23,6 +23,8 @@ class ExternalImageSiblingVk : public ExternalImageSiblingImpl
~ExternalImageSiblingVk() override {}
virtual vk::ImageHelper *getImage() const = 0;
virtual void release(DisplayVk *display, std::vector<vk::GarbageObjectBase> *garbageQueue) = 0;
};
class ImageVk : public ImageImpl
@ -49,6 +51,8 @@ class ImageVk : public ImageImpl
bool mOwnsImage;
vk::ImageHelper *mImage;
std::vector<vk::Shared<vk::Fence>> mImageLastUseFences;
const gl::Context *mContext;
};

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

@ -486,6 +486,30 @@ void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
}
}
angle::Result WaitFences(vk::Context *context,
std::vector<vk::Shared<vk::Fence>> *fences,
bool block)
{
uint64_t timeout = block ? kMaxFenceWaitTimeNs : 0;
// Iterate backwards over the fences, removing them from the list in constant time when they are
// complete.
while (!fences->empty())
{
VkResult result = fences->back().get().wait(context->getDevice(), timeout);
if (result == VK_TIMEOUT)
{
return angle::Result::Continue;
}
ANGLE_VK_TRY(context, result);
fences->back().reset(context->getDevice());
fences->pop_back();
}
return angle::Result::Continue;
}
// Initially dumping the command graphs is disabled.
constexpr bool kEnableCommandGraphDiagnostics = false;
@ -544,11 +568,15 @@ RendererVk::RendererVk()
mFormatProperties.fill(invalid);
}
RendererVk::~RendererVk() {}
RendererVk::~RendererVk()
{
ASSERT(mGarbage.empty());
ASSERT(mFencedGarbage.empty());
}
void RendererVk::onDestroy(vk::Context *context)
{
if (!mInFlightCommands.empty() || !mGarbage.empty())
if (!mInFlightCommands.empty() || !mGarbage.empty() || !mFencedGarbage.empty())
{
// TODO(jmadill): Not nice to pass nullptr here, but shouldn't be a problem.
(void)finish(context, nullptr, nullptr);
@ -1392,6 +1420,9 @@ angle::Result RendererVk::finish(vk::Context *context,
ANGLE_VK_TRY(context, vkQueueWaitIdle(mQueue));
freeAllInFlightResources();
ANGLE_TRY(cleanupFencedGarbage(context, true));
ASSERT(mFencedGarbage.empty());
if (mGpuEventsEnabled)
{
// This loop should in practice execute once since the queue is already idle.
@ -1473,6 +1504,8 @@ angle::Result RendererVk::checkCompletedCommands(vk::Context *context)
mGarbage.erase(mGarbage.begin(), mGarbage.begin() + freeIndex);
}
ANGLE_TRY(cleanupFencedGarbage(context, false));
return angle::Result::Continue;
}
@ -2204,6 +2237,20 @@ void RendererVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpu
mGpuEvents.clear();
}
void RendererVk::addGarbage(vk::Shared<vk::Fence> &&fence,
std::vector<vk::GarbageObjectBase> &&garbage)
{
std::vector<vk::Shared<vk::Fence>> fences;
fences.push_back(std::move(fence));
addGarbage(std::move(fences), std::move(garbage));
}
void RendererVk::addGarbage(std::vector<vk::Shared<vk::Fence>> &&fences,
std::vector<vk::GarbageObjectBase> &&garbage)
{
mFencedGarbage.emplace_back(std::move(fences), std::move(garbage));
}
template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits)
@ -2234,6 +2281,29 @@ bool RendererVk::hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlag
return IsMaskFlagSet(getFormatFeatureBits<features>(format, featureBits), featureBits);
}
angle::Result RendererVk::cleanupFencedGarbage(vk::Context *context, bool block)
{
auto garbageIter = mFencedGarbage.begin();
while (garbageIter != mFencedGarbage.end())
{
ANGLE_TRY(WaitFences(context, &garbageIter->first, block));
if (garbageIter->first.empty())
{
for (vk::GarbageObjectBase &garbageObject : garbageIter->second)
{
garbageObject.destroy(mDevice);
}
garbageIter = mFencedGarbage.erase(garbageIter);
}
else
{
garbageIter++;
}
}
return angle::Result::Continue;
}
uint32_t GetUniformBufferDescriptorCount()
{
return kUniformBufferDescriptorsPerDescriptorSet;

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

@ -213,6 +213,10 @@ class RendererVk : angle::NonCopyable
void pushDebugMarker(GLenum source, GLuint id, std::string &&marker);
void popDebugMarker();
void addGarbage(vk::Shared<vk::Fence> &&fence, std::vector<vk::GarbageObjectBase> &&garbage);
void addGarbage(std::vector<vk::Shared<vk::Fence>> &&fences,
std::vector<vk::GarbageObjectBase> &&garbage);
static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
@ -254,6 +258,8 @@ class RendererVk : angle::NonCopyable
void nextSerial();
angle::Result cleanupFencedGarbage(vk::Context *context, bool block);
egl::Display *mDisplay;
mutable bool mCapsInitialized;
@ -302,6 +308,11 @@ class RendererVk : angle::NonCopyable
std::vector<CommandBatch> mInFlightCommands;
std::vector<vk::GarbageObject> mGarbage;
using FencedGarbage =
std::pair<std::vector<vk::Shared<vk::Fence>>, std::vector<vk::GarbageObjectBase>>;
std::vector<FencedGarbage> mFencedGarbage;
vk::MemoryProperties mMemoryProperties;
vk::FormatTable mFormatTable;

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

@ -31,6 +31,14 @@ void FenceSyncVk::onDestroy(RendererVk *renderer)
mFence.reset(renderer->getDevice());
}
void FenceSyncVk::onDestroy(DisplayVk *display)
{
std::vector<vk::GarbageObjectBase> garbage;
mEvent.dumpResources(&garbage);
display->getRenderer()->addGarbage(std::move(mFence), std::move(garbage));
}
angle::Result FenceSyncVk::initialize(ContextVk *contextVk)
{
ASSERT(!mEvent.valid());

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

@ -31,6 +31,7 @@ class FenceSyncVk
~FenceSyncVk();
void onDestroy(RendererVk *renderer);
void onDestroy(DisplayVk *display);
angle::Result initialize(ContextVk *contextVk);
angle::Result clientWait(vk::Context *context,

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

@ -164,15 +164,7 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk
void HardwareBufferImageSiblingVkAndroid::onDestroy(const egl::Display *display)
{
DisplayVk *displayVk = vk::GetImpl(display);
RendererVk *renderer = displayVk->getRenderer();
if (mImage != nullptr)
{
mImage->releaseImage(renderer);
mImage->releaseStagingBuffer(renderer);
SafeDelete(mImage);
}
ASSERT(mImage == nullptr);
}
gl::Format HardwareBufferImageSiblingVkAndroid::getFormat() const
@ -205,4 +197,16 @@ vk::ImageHelper *HardwareBufferImageSiblingVkAndroid::getImage() const
{
return mImage;
}
void HardwareBufferImageSiblingVkAndroid::release(DisplayVk *display,
std::vector<vk::GarbageObjectBase> *garbageQueue)
{
if (mImage != nullptr)
{
mImage->releaseImage(display, garbageQueue);
mImage->releaseStagingBuffer(display, garbageQueue);
SafeDelete(mImage);
}
}
} // namespace rx

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

@ -36,6 +36,8 @@ class HardwareBufferImageSiblingVkAndroid : public ExternalImageSiblingVk
// ExternalImageSiblingVk interface
vk::ImageHelper *getImage() const override;
void release(DisplayVk *display, std::vector<vk::GarbageObjectBase> *garbageQueue) override;
private:
angle::Result initImpl(DisplayVk *displayVk);

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

@ -12,6 +12,7 @@
#include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
@ -375,6 +376,21 @@ void DynamicBuffer::release(RendererVk *renderer)
}
}
void DynamicBuffer::release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
{
reset();
releaseRetainedBuffers(display, garbageQueue);
if (mBuffer)
{
mBuffer->unmap(display->getDevice());
mBuffer->release(display, garbageQueue);
delete mBuffer;
mBuffer = nullptr;
}
}
void DynamicBuffer::releaseRetainedBuffers(RendererVk *renderer)
{
for (BufferHelper *toFree : mRetainedBuffers)
@ -388,6 +404,18 @@ void DynamicBuffer::releaseRetainedBuffers(RendererVk *renderer)
mRetainedBuffers.clear();
}
void DynamicBuffer::releaseRetainedBuffers(DisplayVk *display,
std::vector<GarbageObjectBase> *garbageQueue)
{
for (BufferHelper *toFree : mRetainedBuffers)
{
toFree->release(display, garbageQueue);
delete toFree;
}
mRetainedBuffers.clear();
}
void DynamicBuffer::destroy(VkDevice device)
{
reset();
@ -1120,6 +1148,17 @@ void BufferHelper::release(RendererVk *renderer)
renderer->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
}
void BufferHelper::release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
{
unmap(display->getDevice());
mSize = 0;
mViewFormat = nullptr;
mBuffer.dumpResources(garbageQueue);
mBufferView.dumpResources(garbageQueue);
mDeviceMemory.dumpResources(garbageQueue);
}
void BufferHelper::onWrite(VkAccessFlagBits writeAccessType)
{
if (mCurrentReadAccess != 0 || mCurrentWriteAccess != 0)
@ -1351,6 +1390,12 @@ void ImageHelper::releaseImage(RendererVk *renderer)
renderer->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
}
void ImageHelper::releaseImage(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
{
mImage.dumpResources(garbageQueue);
mDeviceMemory.dumpResources(garbageQueue);
}
void ImageHelper::releaseStagingBuffer(RendererVk *renderer)
{
// Remove updates that never made it to the texture.
@ -1362,6 +1407,18 @@ void ImageHelper::releaseStagingBuffer(RendererVk *renderer)
mSubresourceUpdates.clear();
}
void ImageHelper::releaseStagingBuffer(DisplayVk *display,
std::vector<GarbageObjectBase> *garbageQueue)
{
// Remove updates that never made it to the texture.
for (SubresourceUpdate &update : mSubresourceUpdates)
{
update.release(display, garbageQueue);
}
mStagingBuffer.release(display, garbageQueue);
mSubresourceUpdates.clear();
}
void ImageHelper::resetImageWeakReference()
{
mImage.reset();
@ -2269,6 +2326,17 @@ void ImageHelper::SubresourceUpdate::release(RendererVk *renderer)
}
}
void ImageHelper::SubresourceUpdate::release(DisplayVk *display,
std::vector<GarbageObjectBase> *garbageQueue)
{
if (updateSource == UpdateSource::Image)
{
image.image->releaseImage(display, garbageQueue);
image.image->releaseStagingBuffer(display, garbageQueue);
SafeDelete(image.image);
}
}
bool ImageHelper::SubresourceUpdate::isUpdateToLayerLevel(uint32_t layerIndex,
uint32_t levelIndex) const
{

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

@ -58,9 +58,11 @@ class DynamicBuffer : angle::NonCopyable
// This releases resources when they might currently be in use.
void release(RendererVk *renderer);
void release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
// This releases all the buffers that have been allocated since this was last called.
void releaseRetainedBuffers(RendererVk *renderer);
void releaseRetainedBuffers(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
// This frees resources immediately.
void destroy(VkDevice device);
@ -397,7 +399,9 @@ class BufferHelper final : public CommandGraphResource
const VkBufferCreateInfo &createInfo,
VkMemoryPropertyFlags memoryPropertyFlags);
void destroy(VkDevice device);
void release(RendererVk *renderer);
void release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
bool valid() const { return mBuffer.valid(); }
const Buffer &getBuffer() const { return mBuffer; }
@ -585,7 +589,10 @@ class ImageHelper final : public CommandGraphResource
uint32_t layerCount);
void releaseImage(RendererVk *renderer);
void releaseImage(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
void releaseStagingBuffer(RendererVk *renderer);
void releaseStagingBuffer(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
bool valid() const { return mImage.valid(); }
@ -756,6 +763,7 @@ class ImageHelper final : public CommandGraphResource
SubresourceUpdate(const SubresourceUpdate &other);
void release(RendererVk *renderer);
void release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue);
const VkImageSubresourceLayers &dstSubresource() const
{

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

@ -415,27 +415,11 @@ angle::Result InitShaderAndSerial(Context *context,
return angle::Result::Continue;
}
// GarbageObject implementation.
GarbageObject::GarbageObject()
: mSerial(), mHandleType(HandleType::Invalid), mHandle(VK_NULL_HANDLE)
GarbageObjectBase::GarbageObjectBase() : mHandleType(HandleType::Invalid), mHandle(VK_NULL_HANDLE)
{}
GarbageObject::GarbageObject(const GarbageObject &other) = default;
GarbageObject &GarbageObject::operator=(const GarbageObject &other) = default;
bool GarbageObject::destroyIfComplete(VkDevice device, Serial completedSerial)
{
if (completedSerial >= mSerial)
{
destroy(device);
return true;
}
return false;
}
void GarbageObject::destroy(VkDevice device)
// GarbageObjectBase implementation
void GarbageObjectBase::destroy(VkDevice device)
{
switch (mHandleType)
{
@ -503,6 +487,25 @@ void GarbageObject::destroy(VkDevice device)
break;
}
}
// GarbageObject implementation.
GarbageObject::GarbageObject() : mSerial() {}
GarbageObject::GarbageObject(const GarbageObject &other) = default;
GarbageObject &GarbageObject::operator=(const GarbageObject &other) = default;
bool GarbageObject::destroyIfComplete(VkDevice device, Serial completedSerial)
{
if (completedSerial >= mSerial)
{
destroy(device);
return true;
}
return false;
}
} // namespace vk
// VK_EXT_debug_utils

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

@ -163,30 +163,41 @@ GetImplType<T> *GetImpl(const T *glObject)
return GetImplAs<GetImplType<T>>(glObject);
}
class GarbageObject final
class GarbageObjectBase
{
public:
template <typename ObjectT>
GarbageObject(Serial serial, const ObjectT &object)
: mSerial(serial),
mHandleType(HandleTypeHelper<ObjectT>::kHandleType),
GarbageObjectBase(const ObjectT &object)
: mHandleType(HandleTypeHelper<ObjectT>::kHandleType),
mHandle(reinterpret_cast<VkDevice>(object.getHandle()))
{}
GarbageObjectBase();
void destroy(VkDevice device);
private:
HandleType mHandleType;
VkDevice mHandle;
};
class GarbageObject final : public GarbageObjectBase
{
public:
template <typename ObjectT>
GarbageObject(Serial serial, const ObjectT &object) : GarbageObjectBase(object), mSerial(serial)
{}
GarbageObject();
GarbageObject(const GarbageObject &other);
GarbageObject &operator=(const GarbageObject &other);
bool destroyIfComplete(VkDevice device, Serial completedSerial);
void destroy(VkDevice device);
private:
// TODO(jmadill): Since many objects will have the same serial, it might be more efficient to
// store the serial outside of the garbage object itself. We could index ranges of garbage
// objects in the Renderer, using a circular buffer.
Serial mSerial;
HandleType mHandleType;
VkDevice mHandle;
};
class MemoryProperties final : angle::NonCopyable
@ -465,6 +476,7 @@ class Shared final : angle::NonCopyable
private:
RefCounted<T> *mRefCounted;
};
} // namespace vk
// List of function pointers for used extensions.

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

@ -39,6 +39,16 @@ class WrappedObject : angle::NonCopyable
}
}
template <typename ResourceOutType>
void dumpResources(std::vector<ResourceOutType> *outQueue)
{
if (valid())
{
outQueue->emplace_back(*static_cast<DerivedT *>(this));
mHandle = VK_NULL_HANDLE;
}
}
protected:
WrappedObject() : mHandle(VK_NULL_HANDLE) {}
~WrappedObject() { ASSERT(!valid()); }