Commit 29f7916f by Jamie Madill Committed by Commit Bot

Vulkan: Store current Serial in RendererVk.

This gives a stronger ordering on serials than if they're acquired by the ContextVk. Part of the steps of implementing multithreaded GL on Vulkan. Implements a "globalFinish" method in RendererVk that is triggered on ContextVk destruction. This helped fixed some racy object deletion situations where the ContextVk could have queued work that uses deleted objects. Flush all the Contexts before destruction to avoid these hanging deleted objects. Bug: angleproject:2464 Change-Id: I244e9bbf6cd47b272c7cbca45b0fb1eb46d626fc Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1791268 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent babe59ff
...@@ -252,7 +252,7 @@ CommandGraphResource::~CommandGraphResource() ...@@ -252,7 +252,7 @@ CommandGraphResource::~CommandGraphResource()
bool CommandGraphResource::isResourceInUse(ContextVk *contextVk) const bool CommandGraphResource::isResourceInUse(ContextVk *contextVk) const
{ {
return mUse.getCounter() > 1 || contextVk->isSerialInUse(mUse.getSerial()); return mUse.isCurrentlyInGraph() || contextVk->isSerialInUse(mUse.getSerial());
} }
angle::Result CommandGraphResource::recordCommands(ContextVk *context, angle::Result CommandGraphResource::recordCommands(ContextVk *context,
......
...@@ -339,10 +339,12 @@ class SharedResourceUse final : angle::NonCopyable ...@@ -339,10 +339,12 @@ class SharedResourceUse final : angle::NonCopyable
return mUse->serial; return mUse->serial;
} }
ANGLE_INLINE uint32_t getCounter() const // The base counter value for an live resource is "1". Any value greater than one indicates
// the resource is in use by a vk::CommandGraph.
ANGLE_INLINE bool isCurrentlyInGraph() const
{ {
ASSERT(valid()); ASSERT(valid());
return mUse->counter; return mUse->counter > 1;
} }
private: private:
...@@ -583,8 +585,8 @@ ANGLE_INLINE bool CommandGraphResource::hasStartedRenderPass() const ...@@ -583,8 +585,8 @@ ANGLE_INLINE bool CommandGraphResource::hasStartedRenderPass() const
ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph) ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph)
{ {
// Clear dependencies if this is a new access. The minimum counter value is 1. // Clear dependencies if this is a new access.
if (mUse.getCounter() == 1) if (!mUse.isCurrentlyInGraph())
{ {
mCurrentWritingNode = nullptr; mCurrentWritingNode = nullptr;
mCurrentReadingNodes.clear(); mCurrentReadingNodes.clear();
......
...@@ -262,16 +262,16 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -262,16 +262,16 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
const vk::CommandPool &getCommandPool() const; const vk::CommandPool &getCommandPool() const;
Serial getCurrentQueueSerial() const { return mCurrentQueueSerial; } Serial getCurrentQueueSerial() const { return mRenderer->getCurrentQueueSerial(); }
Serial getLastSubmittedQueueSerial() const { return mLastSubmittedQueueSerial; } Serial getLastSubmittedQueueSerial() const { return mRenderer->getLastSubmittedQueueSerial(); }
Serial getLastCompletedQueueSerial() const { return mLastCompletedQueueSerial; } Serial getLastCompletedQueueSerial() const { return mRenderer->getLastCompletedQueueSerial(); }
bool isSerialInUse(Serial serial) const; bool isSerialInUse(Serial serial) const;
template <typename T> template <typename T>
void addGarbage(T *object) void addGarbage(T *object)
{ {
object->dumpResources(mCurrentQueueSerial, &mGarbage); object->dumpResources(&mCurrentGarbage);
} }
// Check to see which batches have finished completion (forward progress for // Check to see which batches have finished completion (forward progress for
...@@ -498,6 +498,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -498,6 +498,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
bool shouldUseOldRewriteStructSamplers() const; bool shouldUseOldRewriteStructSamplers() const;
uint64_t getMaxFenceWaitTimeNs() const; uint64_t getMaxFenceWaitTimeNs() const;
void clearAllGarbage();
vk::PipelineHelper *mCurrentGraphicsPipeline; vk::PipelineHelper *mCurrentGraphicsPipeline;
vk::PipelineAndSerial *mCurrentComputePipeline; vk::PipelineAndSerial *mCurrentComputePipeline;
...@@ -602,10 +603,6 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -602,10 +603,6 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
// We use a Persistent CommandPool with pre-allocated buffers for primary CommandBuffer // We use a Persistent CommandPool with pre-allocated buffers for primary CommandBuffer
vk::PersistentCommandPool mPrimaryCommandPool; vk::PersistentCommandPool mPrimaryCommandPool;
Serial mLastCompletedQueueSerial;
Serial mLastSubmittedQueueSerial;
Serial mCurrentQueueSerial;
struct CommandBatch final : angle::NonCopyable struct CommandBatch final : angle::NonCopyable
{ {
CommandBatch(); CommandBatch();
...@@ -627,7 +624,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -627,7 +624,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
angle::Result recycleCommandBatch(CommandBatch *batch); angle::Result recycleCommandBatch(CommandBatch *batch);
std::vector<CommandBatch> mInFlightCommands; std::vector<CommandBatch> mInFlightCommands;
std::vector<vk::GarbageObject> mGarbage; vk::GarbageList mCurrentGarbage;
vk::GarbageQueue mGarbageQueue;
RenderPassCache mRenderPassCache; RenderPassCache mRenderPassCache;
......
...@@ -548,6 +548,8 @@ RendererVk::RendererVk() ...@@ -548,6 +548,8 @@ RendererVk::RendererVk()
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()), mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mMaxVertexAttribDivisor(1), mMaxVertexAttribDivisor(1),
mDevice(VK_NULL_HANDLE), mDevice(VK_NULL_HANDLE),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
mCurrentQueueSerial(mQueueSerialFactory.generate()),
mDeviceLost(false), mDeviceLost(false),
mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod), mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod),
mPipelineCacheDirty(false), mPipelineCacheDirty(false),
...@@ -606,7 +608,8 @@ void RendererVk::onDestroy(vk::Context *context) ...@@ -606,7 +608,8 @@ void RendererVk::onDestroy(vk::Context *context)
void RendererVk::notifyDeviceLost() void RendererVk::notifyDeviceLost()
{ {
mDeviceLost = true; mLastCompletedQueueSerial = mLastSubmittedQueueSerial;
mDeviceLost = true;
mDisplay->notifyDeviceLost(); mDisplay->notifyDeviceLost();
} }
...@@ -1552,7 +1555,8 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatu ...@@ -1552,7 +1555,8 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatu
angle::Result RendererVk::queueSubmit(vk::Context *context, angle::Result RendererVk::queueSubmit(vk::Context *context,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence &fence) const vk::Fence &fence,
Serial *serialOut)
{ {
{ {
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
...@@ -1561,6 +1565,10 @@ angle::Result RendererVk::queueSubmit(vk::Context *context, ...@@ -1561,6 +1565,10 @@ angle::Result RendererVk::queueSubmit(vk::Context *context,
ANGLE_TRY(cleanupGarbage(context, false)); ANGLE_TRY(cleanupGarbage(context, false));
*serialOut = mCurrentQueueSerial;
mLastSubmittedQueueSerial = mCurrentQueueSerial;
mCurrentQueueSerial = mQueueSerialFactory.generate();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1588,11 +1596,6 @@ VkResult RendererVk::queuePresent(const VkPresentInfoKHR &presentInfo) ...@@ -1588,11 +1596,6 @@ VkResult RendererVk::queuePresent(const VkPresentInfoKHR &presentInfo)
} }
} }
Serial RendererVk::nextSerial()
{
return mQueueSerialFactory.generate();
}
angle::Result RendererVk::newSharedFence(vk::Context *context, angle::Result RendererVk::newSharedFence(vk::Context *context,
vk::Shared<vk::Fence> *sharedFenceOut) vk::Shared<vk::Fence> *sharedFenceOut)
{ {
...@@ -1716,4 +1719,28 @@ uint64_t RendererVk::getMaxFenceWaitTimeNs() const ...@@ -1716,4 +1719,28 @@ uint64_t RendererVk::getMaxFenceWaitTimeNs() const
#endif #endif
} }
void RendererVk::onCompletedSerial(Serial serial)
{
if (serial > mLastCompletedQueueSerial)
{
mLastCompletedQueueSerial = serial;
}
}
angle::Result RendererVk::globalFinish()
{
for (gl::Context *context : mDisplay->getContextSet())
{
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(contextVk->flushImpl(nullptr));
}
for (gl::Context *context : mDisplay->getContextSet())
{
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(contextVk->finishImpl());
}
return angle::Result::Continue;
}
} // namespace rx } // namespace rx
...@@ -139,12 +139,11 @@ class RendererVk : angle::NonCopyable ...@@ -139,12 +139,11 @@ class RendererVk : angle::NonCopyable
angle::Result queueSubmit(vk::Context *context, angle::Result queueSubmit(vk::Context *context,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence &fence); const vk::Fence &fence,
Serial *serialOut);
angle::Result queueWaitIdle(vk::Context *context); angle::Result queueWaitIdle(vk::Context *context);
VkResult queuePresent(const VkPresentInfoKHR &presentInfo); VkResult queuePresent(const VkPresentInfoKHR &presentInfo);
Serial nextSerial();
angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut); angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut);
inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn) inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn)
{ {
...@@ -165,6 +164,14 @@ class RendererVk : angle::NonCopyable ...@@ -165,6 +164,14 @@ class RendererVk : angle::NonCopyable
std::string getAndClearLastValidationMessage(uint32_t *countSinceLastClear); std::string getAndClearLastValidationMessage(uint32_t *countSinceLastClear);
uint64_t getMaxFenceWaitTimeNs() const; uint64_t getMaxFenceWaitTimeNs() const;
Serial getCurrentQueueSerial() const { return mCurrentQueueSerial; }
Serial getLastSubmittedQueueSerial() const { return mLastSubmittedQueueSerial; }
Serial getLastCompletedQueueSerial() const { return mLastCompletedQueueSerial; }
void onCompletedSerial(Serial serial);
// Calls finish on every Context in the active set.
angle::Result globalFinish();
private: private:
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex); angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
...@@ -212,6 +219,9 @@ class RendererVk : angle::NonCopyable ...@@ -212,6 +219,9 @@ class RendererVk : angle::NonCopyable
VkDevice mDevice; VkDevice mDevice;
AtomicSerialFactory mQueueSerialFactory; AtomicSerialFactory mQueueSerialFactory;
AtomicSerialFactory mShaderSerialFactory; AtomicSerialFactory mShaderSerialFactory;
Serial mLastCompletedQueueSerial;
Serial mLastSubmittedQueueSerial;
Serial mCurrentQueueSerial; Serial mCurrentQueueSerial;
bool mDeviceLost; bool mDeviceLost;
......
...@@ -1755,10 +1755,10 @@ VkImageAspectFlags ImageHelper::getAspectFlags() const ...@@ -1755,10 +1755,10 @@ VkImageAspectFlags ImageHelper::getAspectFlags() const
return GetFormatAspectFlags(mFormat->imageFormat()); return GetFormatAspectFlags(mFormat->imageFormat());
} }
void ImageHelper::dumpResources(Serial serial, std::vector<GarbageObject> *garbageQueue) void ImageHelper::dumpResources(GarbageList *garbageList)
{ {
mImage.dumpResources(serial, garbageQueue); mImage.dumpResources(garbageList);
mDeviceMemory.dumpResources(serial, garbageQueue); mDeviceMemory.dumpResources(garbageList);
} }
VkImageLayout ImageHelper::getCurrentLayout() const VkImageLayout ImageHelper::getCurrentLayout() const
......
...@@ -713,7 +713,7 @@ class ImageHelper final : public CommandGraphResource ...@@ -713,7 +713,7 @@ class ImageHelper final : public CommandGraphResource
VkImageAspectFlags getAspectFlags() const; VkImageAspectFlags getAspectFlags() const;
void destroy(VkDevice device); void destroy(VkDevice device);
void dumpResources(Serial serial, std::vector<GarbageObject> *garbageQueue); void dumpResources(GarbageList *garbageList);
void init2DWeakReference(VkImage handle, void init2DWeakReference(VkImage handle,
const gl::Extents &glExtents, const gl::Extents &glExtents,
......
...@@ -351,10 +351,10 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs ...@@ -351,10 +351,10 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs
return angle::Result::Continue; return angle::Result::Continue;
} }
void StagingBuffer::dumpResources(Serial serial, std::vector<vk::GarbageObject> *garbageQueue) void StagingBuffer::dumpResources(GarbageList *garbageList)
{ {
mBuffer.dumpResources(serial, garbageQueue); mBuffer.dumpResources(garbageList);
mDeviceMemory.dumpResources(serial, garbageQueue); mDeviceMemory.dumpResources(garbageList);
} }
angle::Result AllocateBufferMemory(vk::Context *context, angle::Result AllocateBufferMemory(vk::Context *context,
...@@ -438,6 +438,18 @@ gl::TextureType Get2DTextureType(uint32_t layerCount, GLint samples) ...@@ -438,6 +438,18 @@ gl::TextureType Get2DTextureType(uint32_t layerCount, GLint samples)
GarbageObjectBase::GarbageObjectBase() : mHandleType(HandleType::Invalid), mHandle(VK_NULL_HANDLE) GarbageObjectBase::GarbageObjectBase() : mHandleType(HandleType::Invalid), mHandle(VK_NULL_HANDLE)
{} {}
GarbageObjectBase::GarbageObjectBase(GarbageObjectBase &&other) : GarbageObjectBase()
{
*this = std::move(other);
}
GarbageObjectBase &GarbageObjectBase::operator=(GarbageObjectBase &&rhs)
{
std::swap(mHandle, rhs.mHandle);
std::swap(mHandleType, rhs.mHandleType);
return *this;
}
// GarbageObjectBase implementation // GarbageObjectBase implementation
void GarbageObjectBase::destroy(VkDevice device) void GarbageObjectBase::destroy(VkDevice device)
{ {
...@@ -508,24 +520,6 @@ void GarbageObjectBase::destroy(VkDevice device) ...@@ -508,24 +520,6 @@ void GarbageObjectBase::destroy(VkDevice device)
} }
} }
// 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;
}
bool SamplerNameContainsNonZeroArrayElement(const std::string &name) bool SamplerNameContainsNonZeroArrayElement(const std::string &name)
{ {
constexpr char kZERO_ELEMENT[] = "[0]"; constexpr char kZERO_ELEMENT[] = "[0]";
......
...@@ -187,6 +187,45 @@ inline OverlayVk *GetImpl(const gl::DummyOverlay *glObject) ...@@ -187,6 +187,45 @@ inline OverlayVk *GetImpl(const gl::DummyOverlay *glObject)
return nullptr; return nullptr;
} }
template <typename ObjT>
class ObjectAndSerial final : angle::NonCopyable
{
public:
ObjectAndSerial() {}
ObjectAndSerial(ObjT &&object, Serial serial) : mObject(std::move(object)), mSerial(serial) {}
ObjectAndSerial(ObjectAndSerial &&other)
: mObject(std::move(other.mObject)), mSerial(std::move(other.mSerial))
{}
ObjectAndSerial &operator=(ObjectAndSerial &&other)
{
mObject = std::move(other.mObject);
mSerial = std::move(other.mSerial);
return *this;
}
Serial getSerial() const { return mSerial; }
void updateSerial(Serial newSerial) { mSerial = newSerial; }
const ObjT &get() const { return mObject; }
ObjT &get() { return mObject; }
bool valid() const { return mObject.valid(); }
void destroy(VkDevice device)
{
mObject.destroy(device);
mSerial = Serial();
}
private:
ObjT mObject;
Serial mSerial;
};
// Reference to a deleted object. The object is due to be destroyed at some point in the future.
// |mHandleType| determines the type of the object and which destroy function should be called.
class GarbageObjectBase class GarbageObjectBase
{ {
public: public:
...@@ -196,6 +235,8 @@ class GarbageObjectBase ...@@ -196,6 +235,8 @@ class GarbageObjectBase
mHandle(reinterpret_cast<VkDevice>(object.getHandle())) mHandle(reinterpret_cast<VkDevice>(object.getHandle()))
{} {}
GarbageObjectBase(); GarbageObjectBase();
GarbageObjectBase(GarbageObjectBase &&other);
GarbageObjectBase &operator=(GarbageObjectBase &&rhs);
void destroy(VkDevice device); void destroy(VkDevice device);
...@@ -204,25 +245,15 @@ class GarbageObjectBase ...@@ -204,25 +245,15 @@ class GarbageObjectBase
VkDevice mHandle; VkDevice mHandle;
}; };
class GarbageObject final : public GarbageObjectBase // A list of garbage objects. Has no object lifetime information.
{ using GarbageList = std::vector<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); // A list of garbage objects and the associated serial after which the objects can be destroyed.
using GarbageAndSerial = ObjectAndSerial<GarbageList>;
private: // Houses multiple lists of garbage objects. Each sub-list has a different lifetime. They should
// TODO(jmadill): Since many objects will have the same serial, it might be more efficient to // be sorted such that later-living garbage is ordered later in the list.
// store the serial outside of the garbage object itself. We could index ranges of garbage using GarbageQueue = std::vector<GarbageAndSerial>;
// objects in the Renderer, using a circular buffer.
Serial mSerial;
};
class MemoryProperties final : angle::NonCopyable class MemoryProperties final : angle::NonCopyable
{ {
...@@ -256,7 +287,7 @@ class StagingBuffer final : angle::NonCopyable ...@@ -256,7 +287,7 @@ class StagingBuffer final : angle::NonCopyable
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; } const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
size_t getSize() const { return mSize; } size_t getSize() const { return mSize; }
void dumpResources(Serial serial, std::vector<GarbageObject> *garbageQueue); void dumpResources(GarbageList *garbageList);
private: private:
Buffer mBuffer; Buffer mBuffer;
...@@ -264,43 +295,6 @@ class StagingBuffer final : angle::NonCopyable ...@@ -264,43 +295,6 @@ class StagingBuffer final : angle::NonCopyable
size_t mSize; size_t mSize;
}; };
template <typename ObjT>
class ObjectAndSerial final : angle::NonCopyable
{
public:
ObjectAndSerial() {}
ObjectAndSerial(ObjT &&object, Serial serial) : mObject(std::move(object)), mSerial(serial) {}
ObjectAndSerial(ObjectAndSerial &&other)
: mObject(std::move(other.mObject)), mSerial(std::move(other.mSerial))
{}
ObjectAndSerial &operator=(ObjectAndSerial &&other)
{
mObject = std::move(other.mObject);
mSerial = std::move(other.mSerial);
return *this;
}
Serial getSerial() const { return mSerial; }
void updateSerial(Serial newSerial) { mSerial = newSerial; }
const ObjT &get() const { return mObject; }
ObjT &get() { return mObject; }
bool valid() const { return mObject.valid(); }
void destroy(VkDevice device)
{
mObject.destroy(device);
mSerial = Serial();
}
private:
ObjT mObject;
Serial mSerial;
};
angle::Result AllocateBufferMemory(vk::Context *context, angle::Result AllocateBufferMemory(vk::Context *context,
VkMemoryPropertyFlags requestedMemoryPropertyFlags, VkMemoryPropertyFlags requestedMemoryPropertyFlags,
VkMemoryPropertyFlags *memoryPropertyFlagsOut, VkMemoryPropertyFlags *memoryPropertyFlagsOut,
......
...@@ -30,16 +30,6 @@ class WrappedObject : angle::NonCopyable ...@@ -30,16 +30,6 @@ class WrappedObject : angle::NonCopyable
const HandleT *ptr() const { return &mHandle; } const HandleT *ptr() const { return &mHandle; }
template <typename ResourceOutType> template <typename ResourceOutType>
void dumpResources(Serial serial, std::vector<ResourceOutType> *outQueue)
{
if (valid())
{
outQueue->emplace_back(serial, *static_cast<DerivedT *>(this));
mHandle = VK_NULL_HANDLE;
}
}
template <typename ResourceOutType>
void dumpResources(std::vector<ResourceOutType> *outQueue) void dumpResources(std::vector<ResourceOutType> *outQueue)
{ {
if (valid()) if (valid())
......
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