aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Lao <cclao@google.com>2024-05-10 19:48:34 -0700
committerAngle LUCI CQ <angle-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-05-15 18:36:08 +0000
commit5332ab8c6260724bb0729c593c6f5a9772c87985 (patch)
tree10228f6bf943a54f261bcc7427c02fae72b6f2ce
parent0ac0603e5f76422279086e0abe22db5d1b866681 (diff)
downloadangle-5332ab8c6260724bb0729c593c6f5a9772c87985.tar.gz
Vulkan: Add RefCountedEventRecycler to vk::Renderer
This CL adds event recycler in vk::Renderer to avoid the constant create and destroy of VkEvents. When RefCountedEvent is destroyed previously, it now goes into per renderer recycler. When RefCountedEvent is created previously, it now dips into this recycler and fetch it. Before we issue VkCmdSetEvent, if this event was from recycler, we also issue VkCmdResetEvent before VkCmdSetEvebt. When glFinish/EGLSwapBuffer is called or context gets destroyed, this recycler is purged to keep the free count under limit. Bug: b/336844257 Change-Id: I92ec1b183f708112a96c3d06fcfa265024f5aa04 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5519174 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Charlie Lao <cclao@google.com> Reviewed-by: Amirali Abdolrashidi <abdolrashidi@google.com>
-rw-r--r--src/libANGLE/renderer/vulkan/ContextVk.cpp2
-rw-r--r--src/libANGLE/renderer/vulkan/vk_helpers.cpp34
-rw-r--r--src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp102
-rw-r--r--src/libANGLE/renderer/vulkan/vk_ref_counted_event.h86
-rw-r--r--src/libANGLE/renderer/vulkan/vk_renderer.cpp2
-rw-r--r--src/libANGLE/renderer/vulkan/vk_renderer.h4
6 files changed, 161 insertions, 69 deletions
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 39ca051602..d176779cc8 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -1255,6 +1255,7 @@ void ContextVk::onDestroy(const gl::Context *context)
VkDevice device = getDevice();
+ mRenderer->getRefCountedEventRecycler()->destroy(getDevice());
mDefaultUniformStorage.release(mRenderer);
mEmptyBuffer.release(mRenderer);
@@ -7693,6 +7694,7 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore,
mShareGroupVk->isDueForBufferPoolPrune(mRenderer))
{
mShareGroupVk->pruneDefaultBufferPools(mRenderer);
+ mRenderer->getRefCountedEventRecycler()->destroy(getDevice());
}
// Since we just flushed, deferred flush is no longer deferred.
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index 06712331b7..b52ab55536 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -1745,8 +1745,12 @@ void CommandBufferHelperCommon::flushSetEventsImpl(Context *context, CommandBuff
ASSERT(refCountedEvent.valid());
const ImageMemoryBarrierData &layoutData =
kImageMemoryBarrierData[refCountedEvent.getImageLayout()];
- commandBuffer->setEvent(refCountedEvent.getEvent().getHandle(),
- GetImageLayoutDstStageMask(context, layoutData));
+ VkPipelineStageFlags stageMask = GetImageLayoutDstStageMask(context, layoutData);
+ if (refCountedEvent.needsReset())
+ {
+ commandBuffer->resetEvent(refCountedEvent.getEvent().getHandle(), stageMask);
+ }
+ commandBuffer->setEvent(refCountedEvent.getEvent().getHandle(), stageMask);
// We no longer need event, so garbage collect it.
mRefCountedEventCollector.emplace_back(std::move(refCountedEvent));
}
@@ -6140,8 +6144,8 @@ void ImageHelper::releaseImage(Renderer *renderer)
renderer->onMemoryDealloc(mMemoryAllocationType, mAllocationSize, mMemoryTypeIndex,
mVmaAllocation.getHandle());
}
- mCurrentEvent.release(renderer->getDevice());
- mLastNonShaderReadOnlyEvent.release(renderer->getDevice());
+ mCurrentEvent.release(renderer);
+ mLastNonShaderReadOnlyEvent.release(renderer);
renderer->collectGarbage(mUse, &mImage, &mDeviceMemory, &mVmaAllocation);
mViewFormats.clear();
mUse.reset();
@@ -6621,8 +6625,8 @@ void ImageHelper::destroy(Renderer *renderer)
mVmaAllocation.getHandle());
}
- mCurrentEvent.release(device);
- mLastNonShaderReadOnlyEvent.release(device);
+ mCurrentEvent.release(renderer);
+ mLastNonShaderReadOnlyEvent.release(renderer);
mImage.destroy(device);
mDeviceMemory.destroy(device);
mVmaAllocation.destroy(renderer->getAllocator());
@@ -7117,7 +7121,7 @@ void ImageHelper::barrierImpl(Context *context,
{
// For now we always use pipelineBarrier for singlebuffer mode. We could use event here in
// future.
- mCurrentEvent.release(context->getDevice());
+ mCurrentEvent.release(context->getRenderer());
const ImageMemoryBarrierData &transition = kImageMemoryBarrierData[mCurrentLayout];
VkMemoryBarrier memoryBarrier = {};
@@ -7174,7 +7178,7 @@ void ImageHelper::barrierImpl(Context *context,
}
commandBuffer->imageBarrier(srcStageMask, dstStageMask, imageMemoryBarrier);
// We use pipelineBarrier here, no needs to wait for events any more.
- mCurrentEvent.release(context->getDevice());
+ mCurrentEvent.release(context->getRenderer());
}
mCurrentLayout = newLayout;
@@ -7368,7 +7372,7 @@ void ImageHelper::updateLayoutAndBarrier(Context *context,
// Release it. No need to garbage collect since we did not use the event here. ALl
// previous use of event should garbage tracked already.
- mCurrentEvent.release(context->getDevice());
+ mCurrentEvent.release(context->getRenderer());
}
mBarrierQueueSerial = queueSerial;
}
@@ -7483,7 +7487,7 @@ void ImageHelper::updateLayoutAndBarrier(Context *context,
mLastNonShaderReadOnlyLayout = ImageLayout::Undefined;
if (mLastNonShaderReadOnlyEvent.valid())
{
- mLastNonShaderReadOnlyEvent.release(context->getDevice());
+ mLastNonShaderReadOnlyEvent.release(context->getRenderer());
}
}
@@ -7492,7 +7496,7 @@ void ImageHelper::updateLayoutAndBarrier(Context *context,
const bool isShaderReadOnly = IsShaderReadOnlyLayout(transitionTo);
if (isShaderReadOnly)
{
- mLastNonShaderReadOnlyEvent.release(context->getDevice());
+ mLastNonShaderReadOnlyEvent.release(context->getRenderer());
mLastNonShaderReadOnlyLayout = mCurrentLayout;
mCurrentShaderReadStageMask = dstStageMask;
}
@@ -7511,7 +7515,7 @@ void ImageHelper::updateLayoutAndBarrier(Context *context,
{
pipelineBarriers->mergeImageBarrier(transitionTo.barrierIndex, srcStageMask,
dstStageMask, imageMemoryBarrier);
- mCurrentEvent.release(context->getDevice());
+ mCurrentEvent.release(context->getRenderer());
}
mBarrierQueueSerial = queueSerial;
@@ -7532,7 +7536,7 @@ void ImageHelper::setCurrentRefCountedEvent(Context *context, ImageLayoutEventMa
ASSERT(context->getRenderer()->getFeatures().useVkEventForImageBarrier.enabled);
// If there is already an event, release it first.
- mCurrentEvent.release(context->getDevice());
+ mCurrentEvent.release(context->getRenderer());
// Create the event if we have not yet so. Otherwise just use the already created event. This
// means all images used in the same render pass that has the same layout will be tracked by the
@@ -10211,7 +10215,7 @@ angle::Result ImageHelper::copySurfaceImageToBuffer(DisplayVk *displayVk,
// We may have a valid event here but we do not have a collector to collect it. Release the
// event here to force pipelineBarrier.
- mCurrentEvent.release(displayVk->getDevice());
+ mCurrentEvent.release(displayVk->getRenderer());
PrimaryCommandBuffer primaryCommandBuffer;
ANGLE_TRY(renderer->getCommandBufferOneOff(displayVk, ProtectionType::Unprotected,
@@ -10263,7 +10267,7 @@ angle::Result ImageHelper::copyBufferToSurfaceImage(DisplayVk *displayVk,
// We may have a valid event here but we do not have a collector to collect it. Release the
// event here to force pipelineBarrier.
- mCurrentEvent.release(displayVk->getDevice());
+ mCurrentEvent.release(displayVk->getRenderer());
PrimaryCommandBuffer commandBuffer;
ANGLE_TRY(
diff --git a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
index 2390c37ba1..6f93aea8bf 100644
--- a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
@@ -20,52 +20,92 @@ bool RefCountedEvent::init(Context *context, ImageLayout layout)
ASSERT(mHandle == nullptr);
ASSERT(layout != ImageLayout::Undefined);
- mHandle = new AtomicRefCounted<EventAndLayout>;
- VkEventCreateInfo createInfo = {};
- createInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
- // Use device only for performance reasons.
- createInfo.flags = context->getFeatures().supportsSynchronization2.enabled
- ? VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR
- : 0;
- VkResult result = mHandle->get().event.init(context->getDevice(), createInfo);
- if (result != VK_SUCCESS)
+ // First try with recycler. We must issue VkCmdResetEvent before VkCmdSetEvent
+ if (context->getRenderer()->getRefCountedEventRecycler()->fetch(this))
{
- WARN() << "event.init failed. Clean up garbage and retry again";
- // Proactively clean up garbage and retry
- context->getRenderer()->cleanupGarbage();
- result = mHandle->get().event.init(context->getDevice(), createInfo);
+ mHandle->get().needsReset = true;
+ }
+ else
+ {
+ // If failed to fetch from recycler, then create a new event.
+ mHandle = new AtomicRefCounted<EventAndLayout>;
+ VkEventCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
+ // Use device only for performance reasons.
+ createInfo.flags = context->getFeatures().supportsSynchronization2.enabled
+ ? VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR
+ : 0;
+ VkResult result = mHandle->get().event.init(context->getDevice(), createInfo);
if (result != VK_SUCCESS)
{
- // Drivers usually can allocate huge amount of VkEvents, and we should never use that
- // many VkEvents under normal situation. If we failed to allocate, there is a high
- // chance that we may have a leak somewhere. This macro should help us catch such
- // potential bugs in the bots if that happens.
- UNREACHABLE();
- // If still fail to create, we just return. An invalid event will trigger
- // pipelineBarrier code path
- return false;
+ WARN() << "event.init failed. Clean up garbage and retry again";
+ // Proactively clean up garbage and retry
+ context->getRenderer()->cleanupGarbage();
+ result = mHandle->get().event.init(context->getDevice(), createInfo);
+ if (result != VK_SUCCESS)
+ {
+ // Drivers usually can allocate huge amount of VkEvents, and we should never use
+ // that many VkEvents under normal situation. If we failed to allocate, there is a
+ // high chance that we may have a leak somewhere. This macro should help us catch
+ // such potential bugs in the bots if that happens.
+ UNREACHABLE();
+ // If still fail to create, we just return. An invalid event will trigger
+ // pipelineBarrier code path
+ return false;
+ }
}
+ mHandle->get().needsReset = false;
}
+
mHandle->addRef();
mHandle->get().imageLayout = layout;
return true;
}
+void RefCountedEvent::release(Renderer *renderer)
+{
+ if (mHandle != nullptr)
+ {
+ releaseImpl(renderer, renderer->getRefCountedEventRecycler());
+ }
+}
+
+template <typename RecyclerT>
+void RefCountedEvent::releaseImpl(Renderer *renderer, RecyclerT *recycler)
+{
+ ASSERT(mHandle != nullptr);
+ const bool isLastReference = mHandle->getAndReleaseRef() == 1;
+ if (isLastReference)
+ {
+ recycler->recycle(std::move(*this));
+ ASSERT(mHandle == nullptr);
+ }
+ else
+ {
+ mHandle = nullptr;
+ }
+}
+
+void RefCountedEvent::destroy(VkDevice device)
+{
+ ASSERT(mHandle != nullptr);
+ ASSERT(!mHandle->isReferenced());
+ mHandle->get().event.destroy(device);
+ SafeDelete(mHandle);
+}
+
// RefCountedEventsGarbage implementation.
bool RefCountedEventsGarbage::destroyIfComplete(Renderer *renderer)
{
- if (renderer->hasResourceUseFinished(mLifetime))
+ if (!renderer->hasResourceUseFinished(mLifetime))
{
- for (RefCountedEvent &event : mRefCountedEvents)
- {
- ASSERT(event.valid());
- event.release(renderer->getDevice());
- ASSERT(!event.valid());
- }
- mRefCountedEvents.clear();
- return true;
+ return false;
}
- return false;
+
+ RefCountedEventRecycler *recycler = renderer->getRefCountedEventRecycler();
+ recycler->releaseOrRecycle(renderer, std::move(mRefCountedEvents));
+
+ return true;
}
bool RefCountedEventsGarbage::hasResourceUseSubmitted(Renderer *renderer) const
diff --git a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.h b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.h
index 10f78c28ec..e657eef0dd 100644
--- a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.h
+++ b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.h
@@ -15,6 +15,7 @@
#include <queue>
#include "common/PackedEnums.h"
+#include "common/SimpleMutex.h"
#include "common/debug.h"
#include "libANGLE/renderer/serial_utils.h"
#include "libANGLE/renderer/vulkan/vk_resource.h"
@@ -45,6 +46,7 @@ struct EventAndLayout
bool valid() const { return event.valid(); }
Event event;
ImageLayout imageLayout;
+ bool needsReset;
};
// The VkCmdSetEvent is called after VkCmdEndRenderPass and all images that used at the given
@@ -98,23 +100,13 @@ class RefCountedEvent final
// failed.
bool init(Context *context, ImageLayout layout);
- // Release one reference count to the underline Event object and destroy if this is the
- // very last reference.
- void release(VkDevice device)
- {
- if (mHandle != nullptr)
- {
- const bool isLastReference = mHandle->getAndReleaseRef() == 1;
- if (isLastReference)
- {
- destroy(device);
- }
- else
- {
- mHandle = nullptr;
- }
- }
- }
+ // Release one reference count to the underline Event object and destroy or recycle the handle
+ // to renderer's recycler if this is the very last reference.
+ void release(Renderer *renderer);
+
+ // Destroy the event and mHandle. Caller must ensure there is no outstanding reference to the
+ // mHandle.
+ void destroy(VkDevice device);
bool valid() const { return mHandle != nullptr; }
@@ -132,15 +124,19 @@ class RefCountedEvent final
return mHandle->get().imageLayout;
}
- private:
- void destroy(VkDevice device)
+ bool needsReset() const
{
- ASSERT(mHandle != nullptr);
- ASSERT(!mHandle->isReferenced());
- mHandle->get().event.destroy(device);
- SafeDelete(mHandle);
+ ASSERT(valid());
+ return mHandle->get().needsReset;
}
+ private:
+ // Release one reference count to the underline Event object and destroy or recycle the handle
+ // to the provided recycler if this is the very last reference.
+ friend class RefCountedEventRecycler;
+ template <typename RecyclerT>
+ void releaseImpl(Renderer *renderer, RecyclerT *recycler);
+
AtomicRefCounted<EventAndLayout> *mHandle;
};
using RefCountedEventCollector = std::vector<RefCountedEvent>;
@@ -213,6 +209,50 @@ class RefCountedEventsGarbage final
RefCountedEventCollector mRefCountedEvents;
};
+// Thread safe event recycler
+class RefCountedEventRecycler final
+{
+ public:
+ void recycle(RefCountedEvent &&garbageObject)
+ {
+ std::lock_guard<angle::SimpleMutex> lock(mMutex);
+ mFreeStack.recycle(std::move(garbageObject));
+ }
+
+ void releaseOrRecycle(Renderer *renderer, RefCountedEventCollector &&eventCollector)
+ {
+ // Take lock once and then use event's releaseImpl function to directly recycle into the
+ // underlying recycling storage.
+ std::lock_guard<angle::SimpleMutex> lock(mMutex);
+ for (RefCountedEvent &event : eventCollector)
+ {
+ event.releaseImpl(renderer, &mFreeStack);
+ }
+ eventCollector.clear();
+ }
+
+ bool fetch(RefCountedEvent *outObject)
+ {
+ std::lock_guard<angle::SimpleMutex> lock(mMutex);
+ if (mFreeStack.empty())
+ {
+ return false;
+ }
+ mFreeStack.fetch(outObject);
+ return true;
+ }
+
+ void destroy(VkDevice device)
+ {
+ std::lock_guard<angle::SimpleMutex> lock(mMutex);
+ mFreeStack.destroy(device);
+ }
+
+ private:
+ angle::SimpleMutex mMutex;
+ Recycler<RefCountedEvent> mFreeStack;
+};
+
// This wraps data and API for vkCmdWaitEvent call
class EventBarrier : angle::NonCopyable
{
diff --git a/src/libANGLE/renderer/vulkan/vk_renderer.cpp b/src/libANGLE/renderer/vulkan/vk_renderer.cpp
index 4c4a49d719..f6486e2fee 100644
--- a/src/libANGLE/renderer/vulkan/vk_renderer.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_renderer.cpp
@@ -1513,6 +1513,8 @@ void Renderer::onDestroy(vk::Context *context)
ASSERT(!hasSharedGarbage());
ASSERT(mOrphanedBufferBlockList.empty());
+ mRefCountedEventRecycler.destroy(mDevice);
+
for (OneOffCommandPool &oneOffCommandPool : mOneOffCommandPoolMap)
{
oneOffCommandPool.destroy(mDevice);
diff --git a/src/libANGLE/renderer/vulkan/vk_renderer.h b/src/libANGLE/renderer/vulkan/vk_renderer.h
index 228da259fd..bc168e1748 100644
--- a/src/libANGLE/renderer/vulkan/vk_renderer.h
+++ b/src/libANGLE/renderer/vulkan/vk_renderer.h
@@ -748,6 +748,8 @@ class Renderer : angle::NonCopyable
return mPipelineCacheGraphDumpPath.c_str();
}
+ vk::RefCountedEventRecycler *getRefCountedEventRecycler() { return &mRefCountedEventRecycler; }
+
private:
angle::Result setupDevice(vk::Context *context,
const angle::FeatureOverrides &featureOverrides,
@@ -959,6 +961,8 @@ class Renderer : angle::NonCopyable
vk::BufferBlockGarbageList mOrphanedBufferBlockList;
// Holds RefCountedEvent garbage
vk::SharedGarbageList<vk::RefCountedEventsGarbage> mRefCountedEventGarbageList;
+ // Holds RefCountedEvent that are free and ready to reuse
+ vk::RefCountedEventRecycler mRefCountedEventRecycler;
VkDeviceSize mPendingGarbageSizeLimit;