Commit 20ae6814 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Decouple EGLSync from renderer serial

To support future work where RendererVk functionality is moved to ContextVk. Given multiple contexts, EGLSync can no longer rely on a single serial as it can be used in multiple contexts. Instead, the fence corresponding to the submission in which the EGLSync object signals is kept so it can be waited on. Introduces a `vk::Shared` class that includes a ref-counted reference to a Vulkan object (vk::Fence in this case). This is specially made to `destroy()` object when reference count reaches zero. Bug: angleproject:2464 Change-Id: I68c8229eea8df77974e28fcc2a9563dae5d204f9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1493131 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill (use @chromium please) <jmadill@google.com> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org>
parent e397949e
...@@ -468,8 +468,9 @@ RendererVk::CommandBatch::CommandBatch() = default; ...@@ -468,8 +468,9 @@ RendererVk::CommandBatch::CommandBatch() = default;
RendererVk::CommandBatch::~CommandBatch() = default; RendererVk::CommandBatch::~CommandBatch() = default;
RendererVk::CommandBatch::CommandBatch(CommandBatch &&other) RendererVk::CommandBatch::CommandBatch(CommandBatch &&other)
: commandPool(std::move(other.commandPool)), fence(std::move(other.fence)), serial(other.serial) {
{} *this = std::move(other);
}
RendererVk::CommandBatch &RendererVk::CommandBatch::operator=(CommandBatch &&other) RendererVk::CommandBatch &RendererVk::CommandBatch::operator=(CommandBatch &&other)
{ {
...@@ -482,7 +483,7 @@ RendererVk::CommandBatch &RendererVk::CommandBatch::operator=(CommandBatch &&oth ...@@ -482,7 +483,7 @@ RendererVk::CommandBatch &RendererVk::CommandBatch::operator=(CommandBatch &&oth
void RendererVk::CommandBatch::destroy(VkDevice device) void RendererVk::CommandBatch::destroy(VkDevice device)
{ {
commandPool.destroy(device); commandPool.destroy(device);
fence.destroy(device); fence.reset(device);
} }
// RendererVk implementation. // RendererVk implementation.
...@@ -1371,12 +1372,12 @@ void RendererVk::freeAllInFlightResources() ...@@ -1371,12 +1372,12 @@ void RendererVk::freeAllInFlightResources()
// On device loss we need to wait for fence to be signaled before destroying it // On device loss we need to wait for fence to be signaled before destroying it
if (mDeviceLost) if (mDeviceLost)
{ {
VkResult status = batch.fence.wait(mDevice, kMaxFenceWaitTimeNs); VkResult status = batch.fence.get().wait(mDevice, kMaxFenceWaitTimeNs);
// If wait times out, it is probably not possible to recover from lost device // If wait times out, it is probably not possible to recover from lost device
ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST); ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
} }
batch.fence.destroy(mDevice);
batch.commandPool.destroy(mDevice); batch.commandPool.destroy(mDevice);
batch.fence.reset(mDevice);
} }
mInFlightCommands.clear(); mInFlightCommands.clear();
...@@ -1395,7 +1396,7 @@ angle::Result RendererVk::checkCompletedCommands(vk::Context *context) ...@@ -1395,7 +1396,7 @@ angle::Result RendererVk::checkCompletedCommands(vk::Context *context)
for (CommandBatch &batch : mInFlightCommands) for (CommandBatch &batch : mInFlightCommands)
{ {
VkResult result = batch.fence.getStatus(mDevice); VkResult result = batch.fence.get().getStatus(mDevice);
if (result == VK_NOT_READY) if (result == VK_NOT_READY)
{ {
break; break;
...@@ -1405,7 +1406,7 @@ angle::Result RendererVk::checkCompletedCommands(vk::Context *context) ...@@ -1405,7 +1406,7 @@ angle::Result RendererVk::checkCompletedCommands(vk::Context *context)
ASSERT(batch.serial > mLastCompletedQueueSerial); ASSERT(batch.serial > mLastCompletedQueueSerial);
mLastCompletedQueueSerial = batch.serial; mLastCompletedQueueSerial = batch.serial;
batch.fence.destroy(mDevice); batch.fence.reset(mDevice);
TRACE_EVENT0("gpu.angle", "commandPool.destroy"); TRACE_EVENT0("gpu.angle", "commandPool.destroy");
batch.commandPool.destroy(mDevice); batch.commandPool.destroy(mDevice);
++finishedCount; ++finishedCount;
...@@ -1434,15 +1435,12 @@ angle::Result RendererVk::submitFrame(vk::Context *context, ...@@ -1434,15 +1435,12 @@ angle::Result RendererVk::submitFrame(vk::Context *context,
vk::CommandBuffer &&commandBuffer) vk::CommandBuffer &&commandBuffer)
{ {
TRACE_EVENT0("gpu.angle", "RendererVk::submitFrame"); TRACE_EVENT0("gpu.angle", "RendererVk::submitFrame");
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = 0;
vk::Scoped<CommandBatch> scopedBatch(mDevice); vk::Scoped<CommandBatch> scopedBatch(mDevice);
CommandBatch &batch = scopedBatch.get(); CommandBatch &batch = scopedBatch.get();
ANGLE_VK_TRY(context, batch.fence.init(mDevice, fenceInfo)); ANGLE_TRY(getSubmitFence(context, &batch.fence));
ANGLE_VK_TRY(context, vkQueueSubmit(mQueue, 1, &submitInfo, batch.fence.getHandle())); ANGLE_VK_TRY(context, vkQueueSubmit(mQueue, 1, &submitInfo, batch.fence.get().getHandle()));
// Store this command buffer in the in-flight list. // Store this command buffer in the in-flight list.
batch.commandPool = std::move(mCommandPool); batch.commandPool = std::move(mCommandPool);
...@@ -1450,10 +1448,12 @@ angle::Result RendererVk::submitFrame(vk::Context *context, ...@@ -1450,10 +1448,12 @@ angle::Result RendererVk::submitFrame(vk::Context *context,
mInFlightCommands.emplace_back(scopedBatch.release()); mInFlightCommands.emplace_back(scopedBatch.release());
// Make sure a new fence is created for the next submission.
mSubmitFence.reset(mDevice);
// CPU should be throttled to avoid mInFlightCommands from growing too fast. That is done on // CPU should be throttled to avoid mInFlightCommands from growing too fast. That is done on
// swap() though, and there could be multiple submissions in between (through glFlush() calls), // swap() though, and there could be multiple submissions in between (through glFlush() calls),
// so the limit is larger than the expected number of images. The // so the limit is larger than the expected number of images.
// InterleavedAttributeDataBenchmark perf test for example issues a large number of flushes.
ASSERT(mInFlightCommands.size() <= kInFlightCommandsLimit); ASSERT(mInFlightCommands.size() <= kInFlightCommandsLimit);
nextSerial(); nextSerial();
...@@ -1505,24 +1505,6 @@ bool RendererVk::isSerialInUse(Serial serial) const ...@@ -1505,24 +1505,6 @@ bool RendererVk::isSerialInUse(Serial serial) const
angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial) angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial)
{ {
bool timedOut = false;
angle::Result result = finishToSerialOrTimeout(context, serial, kMaxFenceWaitTimeNs, &timedOut);
// Don't tolerate timeout. If such a large wait time results in timeout, something's wrong.
if (timedOut)
{
result = angle::Result::Stop;
}
return result;
}
angle::Result RendererVk::finishToSerialOrTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
bool *outTimedOut)
{
*outTimedOut = false;
if (!isSerialInUse(serial) || mInFlightCommands.empty()) if (!isSerialInUse(serial) || mInFlightCommands.empty())
{ {
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1542,15 +1524,9 @@ angle::Result RendererVk::finishToSerialOrTimeout(vk::Context *context, ...@@ -1542,15 +1524,9 @@ angle::Result RendererVk::finishToSerialOrTimeout(vk::Context *context,
const CommandBatch &batch = mInFlightCommands[batchIndex]; const CommandBatch &batch = mInFlightCommands[batchIndex];
// Wait for it finish // Wait for it finish
VkResult status = batch.fence.wait(mDevice, kMaxFenceWaitTimeNs); VkResult status = batch.fence.get().wait(mDevice, kMaxFenceWaitTimeNs);
// If timed out, report it as such.
if (status == VK_TIMEOUT)
{
*outTimedOut = true;
return angle::Result::Continue;
}
// Don't tolerate timeout. If such a large wait time results in timeout, something's wrong.
ANGLE_VK_TRY(context, status); ANGLE_VK_TRY(context, status);
// Clean up finished batches. // Clean up finished batches.
...@@ -1714,6 +1690,26 @@ const vk::Semaphore *RendererVk::getSubmitLastSignaledSemaphore(vk::Context *con ...@@ -1714,6 +1690,26 @@ const vk::Semaphore *RendererVk::getSubmitLastSignaledSemaphore(vk::Context *con
return semaphore; return semaphore;
} }
angle::Result RendererVk::getSubmitFence(vk::Context *context,
vk::Shared<vk::Fence> *sharedFenceOut)
{
if (!mSubmitFence.isReferenced())
{
vk::Fence fence;
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;
ANGLE_VK_TRY(context, fence.init(mDevice, fenceCreateInfo));
mSubmitFence.assign(mDevice, std::move(fence));
}
sharedFenceOut->copy(mDevice, mSubmitFence);
return angle::Result::Continue;
}
angle::Result RendererVk::getTimestamp(vk::Context *context, uint64_t *timestampOut) angle::Result RendererVk::getTimestamp(vk::Context *context, uint64_t *timestampOut)
{ {
// The intent of this function is to query the timestamp without stalling the GPU. Currently, // The intent of this function is to query the timestamp without stalling the GPU. Currently,
......
...@@ -113,11 +113,6 @@ class RendererVk : angle::NonCopyable ...@@ -113,11 +113,6 @@ class RendererVk : angle::NonCopyable
// Wait for completion of batches until (at least) batch with given serial is finished. // Wait for completion of batches until (at least) batch with given serial is finished.
angle::Result finishToSerial(vk::Context *context, Serial serial); angle::Result finishToSerial(vk::Context *context, Serial serial);
// A variant of finishToSerial that can time out. Timeout status returned in outTimedOut.
angle::Result finishToSerialOrTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
bool *outTimedOut);
uint32_t getQueueFamilyIndex() const { return mCurrentQueueFamilyIndex; } uint32_t getQueueFamilyIndex() const { return mCurrentQueueFamilyIndex; }
...@@ -163,6 +158,9 @@ class RendererVk : angle::NonCopyable ...@@ -163,6 +158,9 @@ class RendererVk : angle::NonCopyable
// by next submission. // by next submission.
const vk::Semaphore *getSubmitLastSignaledSemaphore(vk::Context *context); const vk::Semaphore *getSubmitLastSignaledSemaphore(vk::Context *context);
// Get (or allocate) the fence that will be signaled on next submission.
angle::Result getSubmitFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut);
// 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();
...@@ -290,7 +288,7 @@ class RendererVk : angle::NonCopyable ...@@ -290,7 +288,7 @@ class RendererVk : angle::NonCopyable
void destroy(VkDevice device); void destroy(VkDevice device);
vk::CommandPool commandPool; vk::CommandPool commandPool;
vk::Fence fence; vk::Shared<vk::Fence> fence;
Serial serial; Serial serial;
}; };
...@@ -328,6 +326,14 @@ class RendererVk : angle::NonCopyable ...@@ -328,6 +326,14 @@ class RendererVk : angle::NonCopyable
// A pool of semaphores used to support the aforementioned mid-frame submissions. // A pool of semaphores used to support the aforementioned mid-frame submissions.
vk::DynamicSemaphorePool mSubmitSemaphorePool; vk::DynamicSemaphorePool mSubmitSemaphorePool;
// mSubmitFence is the fence that's going to be signaled at the next submission. This is used
// to support SyncVk objects, which may outlive the context (as EGLSync objects).
//
// TODO(geofflang): this is in preparation for moving RendererVk functionality to ContextVk, and
// is otherwise unnecessary as the SyncVk objects don't actually outlive the renderer currently.
// http://anglebug.com/2701
vk::Shared<vk::Fence> mSubmitFence;
// 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;
......
...@@ -27,6 +27,8 @@ void FenceSyncVk::onDestroy(RendererVk *renderer) ...@@ -27,6 +27,8 @@ void FenceSyncVk::onDestroy(RendererVk *renderer)
{ {
renderer->releaseObject(renderer->getCurrentQueueSerial(), &mEvent); renderer->releaseObject(renderer->getCurrentQueueSerial(), &mEvent);
} }
mFence.reset(renderer->getDevice());
} }
angle::Result FenceSyncVk::initialize(vk::Context *context) angle::Result FenceSyncVk::initialize(vk::Context *context)
...@@ -43,8 +45,9 @@ angle::Result FenceSyncVk::initialize(vk::Context *context) ...@@ -43,8 +45,9 @@ angle::Result FenceSyncVk::initialize(vk::Context *context)
vk::Scoped<vk::Event> event(device); vk::Scoped<vk::Event> event(device);
ANGLE_VK_TRY(context, event.get().init(device, eventCreateInfo)); ANGLE_VK_TRY(context, event.get().init(device, eventCreateInfo));
ANGLE_TRY(renderer->getSubmitFence(context, &mFence));
mEvent = event.release(); mEvent = event.release();
mSignalSerial = renderer->getCurrentQueueSerial();
renderer->getCommandGraph()->setFenceSync(mEvent); renderer->getCommandGraph()->setFenceSync(mEvent);
return angle::Result::Continue; return angle::Result::Continue;
...@@ -73,23 +76,22 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context, ...@@ -73,23 +76,22 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
// If asked to flush, only do so if the queue serial hasn't changed (as otherwise the event if (flushCommands)
// signal is already flushed). If not asked to flush, do the flush anyway! This is because
// there's no cpu-side wait on the event and there's no fence yet inserted to wait on. We could
// test the event in a loop with a sleep, which can only ever not timeout if another thread
// performs the flush. Instead, we perform the flush for simplicity.
if (hasPendingWork(renderer))
{ {
ANGLE_TRY(renderer->flush(context)); ANGLE_TRY(renderer->flush(context));
} }
// Wait on the fence that's implicitly inserted at the end of every submission. // Wait on the fence that's expected to be signaled on the first vkQueueSubmit after
bool timedOut = false; // `initialize` was called.
angle::Result result = VkResult status = mFence.get().wait(renderer->getDevice(), timeout);
renderer->finishToSerialOrTimeout(context, mSignalSerial, timeout, &timedOut);
ANGLE_TRY(result); // Check for errors, but don't consider timeout as such.
if (status != VK_TIMEOUT)
{
ANGLE_VK_TRY(context, status);
}
*outResult = timedOut ? VK_TIMEOUT : VK_SUCCESS; *outResult = status;
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -110,11 +112,6 @@ angle::Result FenceSyncVk::getStatus(vk::Context *context, bool *signaled) ...@@ -110,11 +112,6 @@ angle::Result FenceSyncVk::getStatus(vk::Context *context, bool *signaled)
return angle::Result::Continue; return angle::Result::Continue;
} }
bool FenceSyncVk::hasPendingWork(RendererVk *renderer)
{
return mSignalSerial == renderer->getCurrentQueueSerial();
}
SyncVk::SyncVk() : SyncImpl() {} SyncVk::SyncVk() : SyncImpl() {}
SyncVk::~SyncVk() {} SyncVk::~SyncVk() {}
......
...@@ -41,15 +41,12 @@ class FenceSyncVk ...@@ -41,15 +41,12 @@ class FenceSyncVk
angle::Result getStatus(vk::Context *context, bool *signaled); angle::Result getStatus(vk::Context *context, bool *signaled);
private: private:
bool hasPendingWork(RendererVk *renderer);
// The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with // The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with
// `getStatus`. // `getStatus`.
vk::Event mEvent; vk::Event mEvent;
// The serial in which the event was inserted. Used in `clientWait` to know whether flush is // The vkFence that's signaled once the command buffer including the `init` signal is executed.
// necessary, and to be able to wait on the vkFence that's automatically inserted at the end of // `clientWait` waits on this fence.
// each submissions. vk::Shared<vk::Fence> mFence;
Serial mSignalSerial;
}; };
class SyncVk final : public SyncImpl class SyncVk final : public SyncImpl
......
...@@ -319,6 +319,7 @@ class RefCounted : angle::NonCopyable ...@@ -319,6 +319,7 @@ class RefCounted : angle::NonCopyable
RefCounted(RefCounted &&copy) : mRefCount(copy.mRefCount), mObject(std::move(copy.mObject)) RefCounted(RefCounted &&copy) : mRefCount(copy.mRefCount), mObject(std::move(copy.mObject))
{ {
ASSERT(this != &copy);
copy.mRefCount = 0; copy.mRefCount = 0;
} }
...@@ -384,6 +385,76 @@ class BindingPointer final : angle::NonCopyable ...@@ -384,6 +385,76 @@ class BindingPointer final : angle::NonCopyable
private: private:
RefCounted<T> *mRefCounted; RefCounted<T> *mRefCounted;
}; };
// Helper class to share ref-counted Vulkan objects. Requires that T have a destroy method
// that takes a VkDevice and returns void.
template <typename T>
class Shared final : angle::NonCopyable
{
public:
Shared() : mRefCounted(nullptr) {}
~Shared() { ASSERT(mRefCounted == nullptr); }
Shared(Shared &&other) { *this = std::move(other); }
Shared &operator=(Shared &&other)
{
ASSERT(this != &other);
mRefCounted = other.mRefCounted;
other.mRefCounted = nullptr;
return *this;
}
void set(VkDevice device, RefCounted<T> *refCounted)
{
if (mRefCounted)
{
mRefCounted->releaseRef();
if (!mRefCounted->isReferenced())
{
mRefCounted->get().destroy(device);
SafeDelete(mRefCounted);
}
}
mRefCounted = refCounted;
if (mRefCounted)
{
mRefCounted->addRef();
}
}
void assign(VkDevice device, T &&newObject)
{
set(device, new RefCounted<T>(std::move(newObject)));
}
void copy(VkDevice device, const Shared<T> &other) { set(device, other.mRefCounted); }
void reset(VkDevice device) { set(device, nullptr); }
bool isReferenced() const
{
// If reference is zero, the object should have been deleted. I.e. if the object is not
// nullptr, it should have a reference.
ASSERT(!mRefCounted || mRefCounted->isReferenced());
return mRefCounted != nullptr;
}
T &get()
{
ASSERT(mRefCounted && mRefCounted->isReferenced());
return mRefCounted->get();
}
const T &get() const
{
ASSERT(mRefCounted && mRefCounted->isReferenced());
return mRefCounted->get();
}
private:
RefCounted<T> *mRefCounted;
};
} // namespace vk } // namespace vk
// List of function pointers for used extensions. // List of function pointers for used extensions.
......
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