| // |
| // Copyright 2024 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // RefCountedEvent: |
| // Manages reference count of VkEvent and its associated functions. |
| // |
| |
| #ifndef LIBANGLE_RENDERER_VULKAN_REFCOUNTED_EVENT_H_ |
| #define LIBANGLE_RENDERER_VULKAN_REFCOUNTED_EVENT_H_ |
| |
| #include <atomic> |
| #include <limits> |
| #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" |
| #include "libANGLE/renderer/vulkan/vk_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_wrapper.h" |
| |
| namespace rx |
| { |
| namespace vk |
| { |
| class Context; |
| enum class ImageAccess; |
| |
| // There are two ways to implement a barrier: Using VkCmdPipelineBarrier or VkCmdWaitEvents. The |
| // BarrierType enum will be passed around to indicate which barrier caller want to use. |
| enum class BarrierType |
| { |
| Pipeline, |
| Event, |
| }; |
| |
| constexpr VkPipelineStageFlags kPreFragmentStageFlags = |
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | |
| VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; |
| |
| constexpr VkPipelineStageFlags kAllShadersPipelineStageFlags = |
| kPreFragmentStageFlags | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | |
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; |
| |
| constexpr VkPipelineStageFlags kAllDepthStencilPipelineStageFlags = |
| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; |
| |
| constexpr VkPipelineStageFlags kFragmentAndAttachmentPipelineStageFlags = |
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | |
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| |
| // We group VK_PIPELINE_STAGE_*_BITs into different groups. The expectation is that execution within |
| // Fragment/PreFragment/Compute will not overlap. This information is used to optimize the usage of |
| // VkEvent where we try to not use it when we know that it will not provide benefits over |
| // pipelineBarriers. |
| enum class PipelineStageGroup : uint8_t |
| { |
| Other, |
| PreFragmentOnly, |
| FragmentOnly, |
| ComputeOnly, |
| |
| InvalidEnum, |
| EnumCount = InvalidEnum, |
| }; |
| |
| class PipelineStageAccessHeuristic final |
| { |
| public: |
| constexpr PipelineStageAccessHeuristic() = default; |
| constexpr PipelineStageAccessHeuristic(PipelineStageGroup pipelineStageGroup) |
| { |
| for (size_t i = 0; i < kHeuristicWindowSize; i++) |
| { |
| mHeuristicBits <<= kPipelineStageGroupBitShift; |
| mHeuristicBits |= ToUnderlying(pipelineStageGroup); |
| } |
| } |
| void onAccess(PipelineStageGroup pipelineStageGroup) |
| { |
| mHeuristicBits <<= kPipelineStageGroupBitShift; |
| mHeuristicBits |= ToUnderlying(pipelineStageGroup); |
| } |
| constexpr bool operator==(const PipelineStageAccessHeuristic &other) const |
| { |
| return mHeuristicBits == other.mHeuristicBits; |
| } |
| |
| private: |
| static constexpr size_t kPipelineStageGroupBitShift = 2; |
| static_assert(ToUnderlying(PipelineStageGroup::EnumCount) <= |
| (1 << kPipelineStageGroupBitShift)); |
| static constexpr size_t kHeuristicWindowSize = 8; |
| angle::BitSet16<kHeuristicWindowSize * kPipelineStageGroupBitShift> mHeuristicBits; |
| }; |
| static constexpr PipelineStageAccessHeuristic kPipelineStageAccessFragmentOnly = |
| PipelineStageAccessHeuristic(PipelineStageGroup::FragmentOnly); |
| static constexpr PipelineStageAccessHeuristic kPipelineStageAccessComputeOnly = |
| PipelineStageAccessHeuristic(PipelineStageGroup::ComputeOnly); |
| static constexpr PipelineStageAccessHeuristic kPipelineStageAccessPreFragmentOnly = |
| PipelineStageAccessHeuristic(PipelineStageGroup::PreFragmentOnly); |
| |
| // Enum for predefined VkPipelineStageFlags set that VkEvent will be using. Because VkEvent has |
| // strict rules that waitEvent and setEvent must have matching VkPipelineStageFlags, it is desirable |
| // to keep VkEvent per VkPipelineStageFlags combination. This enum table enumerates all possible |
| // pipeline stage combinations that VkEvent used with. The enum maps to VkPipelineStageFlags via |
| // Renderer::getPipelineStageMask call. |
| enum class EventStage : uint32_t |
| { |
| Transfer = 0, |
| VertexShader = 1, |
| FragmentShader = 2, |
| ComputeShader = 3, |
| AllShaders = 4, |
| PreFragmentShaders = 5, |
| FragmentShadingRate = 6, |
| Attachment = 7, |
| AttachmentAndFragmentShader = 8, |
| AttachmentAndFragmentShaderAndTransfer = 9, |
| AttachmentAndAllShaders = 10, |
| TransferAndComputeShader = 11, |
| // For buffers only |
| VertexInput = 12, |
| TransformFeedbackWrite = 13, |
| InvalidEnum = 14, |
| EnumCount = InvalidEnum, |
| }; |
| using EventStageBitMask = typename angle::PackedEnumBitSet<EventStage, uint64_t>; |
| |
| using EventStageToVkPipelineStageFlagsMap = angle::PackedEnumMap<EventStage, VkPipelineStageFlags>; |
| |
| // VkCmdWaitEvents requires srcStageMask must be the bitwise OR of the stageMask parameter used in |
| // previous calls to vkCmdSetEvent (See VUID-vkCmdWaitEvents-srcStageMask-01158). This mean we must |
| // keep the record of what stageMask each event has been used in VkCmdSetEvent call so that we can |
| // retrieve that information when we need to wait for the event. Instead of keeping just stageMask |
| // here, we keep the ImageAccess for now which gives us more information for debugging. |
| struct EventAndStage |
| { |
| bool valid() const { return event.valid(); } |
| Event event; |
| EventStage eventStage; |
| }; |
| |
| // The VkCmdSetEvent is called after VkCmdEndRenderPass and all images that used at the given |
| // pipeline stage (i.e, they have the same stageMask) will be tracked by the same event. This means |
| // there will be multiple objects pointing to the same event. Events are thus reference counted so |
| // that we do not destroy it while other objects still referencing to it. |
| class RefCountedEvent final |
| { |
| public: |
| RefCountedEvent() { mHandle = nullptr; } |
| ~RefCountedEvent() { ASSERT(mHandle == nullptr); } |
| |
| // Move constructor moves reference of the underline object from other to this. |
| RefCountedEvent(RefCountedEvent &&other) |
| { |
| mHandle = other.mHandle; |
| other.mHandle = nullptr; |
| } |
| |
| // Copy constructor adds reference to the underline object. |
| RefCountedEvent(const RefCountedEvent &other) |
| { |
| ASSERT(other.valid()); |
| mHandle = other.mHandle; |
| mHandle->addRef(); |
| } |
| |
| // Move assignment moves reference of the underline object from other to this. |
| RefCountedEvent &operator=(RefCountedEvent &&other) |
| { |
| ASSERT(!valid()); |
| ASSERT(other.valid()); |
| std::swap(mHandle, other.mHandle); |
| return *this; |
| } |
| |
| // Copy assignment adds reference to the underline object. |
| RefCountedEvent &operator=(const RefCountedEvent &other) |
| { |
| ASSERT(!valid()); |
| ASSERT(other.valid()); |
| mHandle = other.mHandle; |
| mHandle->addRef(); |
| return *this; |
| } |
| |
| // Returns true if both points to the same underline object. |
| bool operator==(const RefCountedEvent &other) const { return mHandle == other.mHandle; } |
| |
| // Create VkEvent and associated it with given layout. Returns true if success and false if |
| // failed. |
| bool init(Context *context, EventStage eventStage); |
| |
| // 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); |
| |
| // Release one reference count to the underline Event object and destroy or recycle the handle |
| // to the context share group's recycler if this is the very last reference. |
| void release(Context *context); |
| |
| // 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; } |
| |
| ANGLE_INLINE void assertValidAndNoReference() const |
| { |
| ASSERT(mHandle != nullptr); |
| mHandle->assertIsRefCountAsExpected(0); |
| } |
| |
| // Returns the underlying Event object |
| const Event &getEvent() const |
| { |
| ASSERT(valid()); |
| return mHandle->get().event; |
| } |
| |
| EventStage getEventStage() const |
| { |
| ASSERT(mHandle != nullptr); |
| return mHandle->get().eventStage; |
| } |
| |
| VkPipelineStageFlags getPipelineStageMask(Renderer *renderer) const; |
| |
| 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 RefCountedEventsGarbage; |
| template <typename RecyclerT> |
| void releaseImpl(Renderer *renderer, RecyclerT *recycler); |
| |
| RefCounted<EventAndStage> *mHandle; |
| }; |
| using RefCountedEventCollector = std::deque<RefCountedEvent>; |
| |
| // Tracks a list of RefCountedEvents per EventStage. |
| class RefCountedEventArray : angle::NonCopyable |
| { |
| public: |
| RefCountedEventArray &operator=(RefCountedEventArray &&other) |
| { |
| for (EventStage stage : other.mBitMask) |
| { |
| mEvents[stage] = std::move(other.mEvents[stage]); |
| } |
| mBitMask = std::move(other.mBitMask); |
| other.mBitMask.reset(); |
| return *this; |
| } |
| |
| void release(Renderer *renderer); |
| void release(Context *context); |
| |
| void releaseToEventCollector(RefCountedEventCollector *eventCollector); |
| |
| const RefCountedEvent &getEvent(EventStage eventStage) const { return mEvents[eventStage]; } |
| |
| bool initEventAtStage(Context *context, EventStage eventStage); |
| |
| bool empty() const { return mBitMask.none(); } |
| const EventStageBitMask getBitMask() const { return mBitMask; } |
| |
| template <typename CommandBufferT> |
| void flushSetEvents(Renderer *renderer, CommandBufferT *commandBuffer) const; |
| |
| protected: |
| angle::PackedEnumMap<EventStage, RefCountedEvent> mEvents; |
| // The mask is used to accelerate the loop of map |
| EventStageBitMask mBitMask; |
| }; |
| |
| class RefCountedEventArrayWithAccessFlags final : public RefCountedEventArray |
| { |
| public: |
| RefCountedEventArrayWithAccessFlags() { mAccessFlags.fill(0); } |
| void replaceEventAtStage(Context *context, |
| EventStage eventStage, |
| const RefCountedEvent &event, |
| VkAccessFlags accessFlags) |
| { |
| if (mBitMask[eventStage]) |
| { |
| mEvents[eventStage].release(context); |
| } |
| mEvents[eventStage] = event; |
| mAccessFlags[eventStage] |= accessFlags; |
| mBitMask.set(eventStage); |
| } |
| VkAccessFlags getAccessFlags(EventStage eventStage) const |
| { |
| ASSERT(mBitMask[eventStage]); |
| return mAccessFlags[eventStage]; |
| } |
| |
| void release(Renderer *renderer) |
| { |
| RefCountedEventArray::release(renderer); |
| mAccessFlags.fill(0); |
| } |
| void release(Context *context) |
| { |
| RefCountedEventArray::release(context); |
| mAccessFlags.fill(0); |
| } |
| void releaseToEventCollector(RefCountedEventCollector *eventCollector) |
| { |
| for (EventStage eventStage : mBitMask) |
| { |
| eventCollector->emplace_back(std::move(mEvents[eventStage])); |
| mAccessFlags[eventStage] = 0; |
| } |
| mBitMask.reset(); |
| } |
| bool hasEventAndAccess(EventStage eventStage, VkAccessFlags accessType) const |
| { |
| return mBitMask.test(eventStage) && (mAccessFlags[eventStage] & accessType) == accessType; |
| } |
| |
| private: |
| angle::PackedEnumMap<EventStage, VkAccessFlags> mAccessFlags; |
| }; |
| |
| class RefCountedEventWithAccessFlags final |
| { |
| public: |
| RefCountedEventWithAccessFlags() : mAccessFlags(0) {} |
| |
| void release(Renderer *renderer) |
| { |
| mEvent.release(renderer); |
| mAccessFlags = 0; |
| } |
| void release(Context *context) |
| { |
| mEvent.release(context); |
| mAccessFlags = 0; |
| } |
| void releaseToEventCollector(RefCountedEventCollector *eventCollector) |
| { |
| eventCollector->emplace_back(std::move(mEvent)); |
| mAccessFlags = 0; |
| } |
| RefCountedEventWithAccessFlags &operator=(RefCountedEventWithAccessFlags &&other) |
| { |
| mEvent = std::move(other.mEvent); |
| mAccessFlags = other.mAccessFlags; |
| other.mAccessFlags = 0; |
| return *this; |
| } |
| |
| void setEventAndAccessFlags(const RefCountedEvent &event, VkAccessFlags accessFlags) |
| { |
| mEvent = event; |
| mAccessFlags = accessFlags; |
| } |
| |
| const RefCountedEvent &getEvent() const { return mEvent; } |
| VkAccessFlags getAccessFlags() const |
| { |
| ASSERT(mEvent.valid()); |
| return mAccessFlags; |
| } |
| |
| bool valid() const { return mEvent.valid(); } |
| |
| EventStage getEventStage() const { return mEvent.getEventStage(); } |
| |
| private: |
| RefCountedEvent mEvent; |
| VkAccessFlags mAccessFlags; |
| }; |
| |
| // Only used by RenderPassCommandBufferHelper |
| class EventArray final : angle::NonCopyable |
| { |
| public: |
| void init(Renderer *renderer, const RefCountedEventArray &refCountedEventArray); |
| |
| bool empty() const { return mBitMask.none(); } |
| void flushSetEvents(PrimaryCommandBuffer *primary); |
| |
| private: |
| // The mask is used to accelerate the loop of map |
| EventStageBitMask mBitMask; |
| angle::PackedEnumMap<EventStage, VkEvent> mEvents; |
| angle::PackedEnumMap<EventStage, VkPipelineStageFlags> mPipelineStageFlags; |
| }; |
| |
| // This class tracks a vector of RefcountedEvent garbage. For performance reason, instead of |
| // individually tracking each VkEvent garbage, we collect all events that are accessed in the |
| // CommandBufferHelper into this class. After we submit the command buffer, we treat this vector of |
| // events as one garbage object and add it to renderer's garbage list. The garbage clean up will |
| // decrement the refCount and destroy event only when last refCount goes away. Basically all GPU |
| // usage will use one refCount and that refCount ensures we never destroy event until GPU is |
| // finished. |
| class RefCountedEventsGarbage final |
| { |
| public: |
| RefCountedEventsGarbage() = default; |
| ~RefCountedEventsGarbage() { ASSERT(mRefCountedEvents.empty()); } |
| |
| RefCountedEventsGarbage(const QueueSerial &queueSerial, |
| RefCountedEventCollector &&refCountedEvents) |
| : mQueueSerial(queueSerial), mRefCountedEvents(std::move(refCountedEvents)) |
| { |
| ASSERT(!mRefCountedEvents.empty()); |
| } |
| |
| void destroy(Renderer *renderer); |
| |
| // Check the queue serial and release the events to recycler if GPU finished. |
| bool releaseIfComplete(Renderer *renderer, RefCountedEventsGarbageRecycler *recycler); |
| |
| // Check the queue serial and move all events to releasedBucket if GPU finished. This is only |
| // used by RefCountedEventRecycler. |
| bool moveIfComplete(Renderer *renderer, std::deque<RefCountedEventCollector> *releasedBucket); |
| |
| bool empty() const { return mRefCountedEvents.empty(); } |
| |
| size_t size() const { return mRefCountedEvents.size(); } |
| |
| private: |
| QueueSerial mQueueSerial; |
| RefCountedEventCollector mRefCountedEvents; |
| }; |
| |
| // Two levels of RefCountedEvents recycle system: For the performance reason, we have two levels of |
| // events recycler system. The first level is per ShareGroupVk, which owns |
| // RefCountedEventsGarbageRecycler. RefCountedEvent garbage is added to it without any lock. Once |
| // GPU complete, the refCount is decremented. When the last refCount goes away, it goes into |
| // mEventsToReset. Note that since ShareGroupVk access is already protected by context share lock at |
| // the API level, so no lock is taken and reference counting is not atomic. At |
| // RefCountedEventsGarbageRecycler::cleanup time, the entire mEventsToReset is added into renderer's |
| // list. The renderer owns RefCountedEventRecycler list, and all access to it is protected with |
| // simple mutex lock. When any context calls OutsideRenderPassCommandBufferHelper::flushToPrimary, |
| // mEventsToReset is retrieved from renderer and the reset commands is added to the command buffer. |
| // The events are then moved to the renderer's garbage list. They are checked and along with |
| // renderer's garbage cleanup and if completed, they get moved to renderer's mEventsToReuse list. |
| // When a RefCountedEvent is needed, we always dip into ShareGroupVk's mEventsToReuse list. If its |
| // empty, it then dip into renderer's mEventsToReuse and grab a collector of events and try to |
| // reuse. That way the traffic into renderer is minimized as most of calls will be contained in |
| // ShareGroupVk. |
| |
| // Thread safe event recycler, protected by its own lock. |
| class RefCountedEventRecycler final |
| { |
| public: |
| RefCountedEventRecycler() {} |
| ~RefCountedEventRecycler() |
| { |
| ASSERT(mEventsToReset.empty()); |
| ASSERT(mResettingQueue.empty()); |
| ASSERT(mEventsToReuse.empty()); |
| } |
| |
| void destroy(VkDevice device); |
| |
| // Add single event to the toReset list |
| void recycle(RefCountedEvent &&garbageObject, VkDevice device) |
| { |
| garbageObject.assertValidAndNoReference(); |
| std::lock_guard<angle::SimpleMutex> lock(mMutex); |
| if (mEventsToReset.size() >= kMaxEventToKeepCount) |
| { |
| garbageObject.destroy(device); |
| } |
| else |
| { |
| if (mEventsToReset.empty()) |
| { |
| mEventsToReset.emplace_back(); |
| } |
| mEventsToReset.back().emplace_back(std::move(garbageObject)); |
| } |
| } |
| |
| // Add a list of events to the toReset list |
| void recycle(RefCountedEventCollector &&garbageObjects, VkDevice device); |
| |
| // Reset all events in the toReset list and move them to the toReuse list |
| void resetEvents(ErrorContext *context, |
| const QueueSerial queueSerial, |
| PrimaryCommandBuffer *commandbuffer); |
| |
| // Clean up the resetting event list and move completed events to the toReuse list. |
| // Number of events released is returned. |
| size_t cleanupResettingEvents(Renderer *renderer); |
| |
| // Fetch a list of events that are ready to be reused. Returns true if eventsToReuseOut is |
| // returned. |
| bool fetchEventsToReuse(RefCountedEventCollector *eventsToReuseOut); |
| |
| private: |
| // Maximum number of RefCountedEventCollector we will keep in mEventsToReset. |
| // When mEventsToReset size reaches kMaxEventToKeepCount, any extra used VkEvent items will be |
| // destroyed immediately. |
| static constexpr size_t kMaxEventToKeepCount = 16; |
| angle::SimpleMutex mMutex; |
| // RefCountedEvent list that has been released, needs to be reset. |
| std::deque<RefCountedEventCollector> mEventsToReset; |
| // RefCountedEvent list that is currently resetting. |
| std::queue<RefCountedEventsGarbage> mResettingQueue; |
| // RefCountedEvent list that already has been reset. Ready to be reused. |
| std::deque<RefCountedEventCollector> mEventsToReuse; |
| }; |
| |
| // Not thread safe event garbage collection and recycler. Caller must ensure the thread safety. It |
| // is intended to use by ShareGroupVk which all access should already protected by share context |
| // lock. |
| class RefCountedEventsGarbageRecycler final |
| { |
| public: |
| RefCountedEventsGarbageRecycler() : mGarbageCount(0) {} |
| ~RefCountedEventsGarbageRecycler(); |
| |
| // Release all garbage and free events. |
| void destroy(Renderer *renderer); |
| |
| // Walk the garbage list and move completed garbage to free list |
| void cleanup(Renderer *renderer); |
| |
| void collectGarbage(const QueueSerial &queueSerial, RefCountedEventCollector &&refCountedEvents) |
| { |
| mGarbageCount += refCountedEvents.size(); |
| mGarbageQueue.emplace(queueSerial, std::move(refCountedEvents)); |
| } |
| |
| void recycle(RefCountedEvent &&garbageObject, VkDevice device) |
| { |
| garbageObject.assertValidAndNoReference(); |
| mEventsToReset.emplace_back(std::move(garbageObject)); |
| } |
| |
| bool fetch(Renderer *renderer, RefCountedEvent *outObject); |
| |
| size_t getGarbageCount() const { return mGarbageCount; } |
| |
| private: |
| RefCountedEventCollector mEventsToReset; |
| std::queue<RefCountedEventsGarbage> mGarbageQueue; |
| Recycler<RefCountedEvent> mEventsToReuse; |
| size_t mGarbageCount; |
| }; |
| |
| // This wraps data and API for vkCmdWaitEvent call |
| class EventBarrier : angle::NonCopyable |
| { |
| public: |
| EventBarrier() |
| : mSrcStageMask(0), |
| mDstStageMask(0), |
| mMemoryBarrierSrcAccess(0), |
| mMemoryBarrierDstAccess(0), |
| mImageMemoryBarrierCount(0), |
| mEvent(VK_NULL_HANDLE) |
| {} |
| |
| EventBarrier(VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| VkAccessFlags srcAccess, |
| VkAccessFlags dstAccess, |
| const VkEvent &event) |
| : mSrcStageMask(srcStageMask), |
| mDstStageMask(dstStageMask), |
| mMemoryBarrierSrcAccess(srcAccess), |
| mMemoryBarrierDstAccess(dstAccess), |
| mImageMemoryBarrierCount(0), |
| mEvent(event) |
| { |
| ASSERT(mEvent != VK_NULL_HANDLE); |
| } |
| |
| EventBarrier(VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| const VkEvent &event, |
| const VkImageMemoryBarrier &imageMemoryBarrier) |
| : mSrcStageMask(srcStageMask), |
| mDstStageMask(dstStageMask), |
| mMemoryBarrierSrcAccess(0), |
| mMemoryBarrierDstAccess(0), |
| mImageMemoryBarrierCount(1), |
| mEvent(event), |
| mImageMemoryBarrier(imageMemoryBarrier) |
| { |
| ASSERT(mEvent != VK_NULL_HANDLE); |
| ASSERT(mImageMemoryBarrier.image != VK_NULL_HANDLE); |
| ASSERT(mImageMemoryBarrier.pNext == nullptr); |
| } |
| |
| EventBarrier(EventBarrier &&other) |
| { |
| mSrcStageMask = other.mSrcStageMask; |
| mDstStageMask = other.mDstStageMask; |
| mMemoryBarrierSrcAccess = other.mMemoryBarrierSrcAccess; |
| mMemoryBarrierDstAccess = other.mMemoryBarrierDstAccess; |
| mImageMemoryBarrierCount = other.mImageMemoryBarrierCount; |
| std::swap(mEvent, other.mEvent); |
| std::swap(mImageMemoryBarrier, other.mImageMemoryBarrier); |
| other.mSrcStageMask = 0; |
| other.mDstStageMask = 0; |
| other.mMemoryBarrierSrcAccess = 0; |
| other.mMemoryBarrierDstAccess = 0; |
| other.mImageMemoryBarrierCount = 0; |
| } |
| |
| ~EventBarrier() {} |
| |
| bool isEmpty() const { return mEvent == VK_NULL_HANDLE; } |
| |
| bool hasEvent(const VkEvent &event) const { return mEvent == event; } |
| |
| void addAdditionalStageAccess(VkPipelineStageFlags dstStageMask, VkAccessFlags dstAccess) |
| { |
| mDstStageMask |= dstStageMask; |
| mMemoryBarrierDstAccess |= dstAccess; |
| } |
| |
| void execute(PrimaryCommandBuffer *primary); |
| |
| void addDiagnosticsString(std::ostringstream &out) const; |
| |
| private: |
| friend class EventBarrierArray; |
| VkPipelineStageFlags mSrcStageMask; |
| VkPipelineStageFlags mDstStageMask; |
| VkAccessFlags mMemoryBarrierSrcAccess; |
| VkAccessFlags mMemoryBarrierDstAccess; |
| uint32_t mImageMemoryBarrierCount; |
| VkEvent mEvent; |
| VkImageMemoryBarrier mImageMemoryBarrier; |
| }; |
| |
| class EventBarrierArray final |
| { |
| public: |
| bool isEmpty() const { return mBarriers.empty(); } |
| |
| void execute(Renderer *renderer, PrimaryCommandBuffer *primary); |
| |
| // Add the additional stageMask to the existing waitEvent. |
| void addAdditionalStageAccess(const RefCountedEvent &waitEvent, |
| VkPipelineStageFlags dstStageMask, |
| VkAccessFlags dstAccess); |
| |
| void addEventMemoryBarrier(Renderer *renderer, |
| const RefCountedEvent &waitEvent, |
| VkAccessFlags srcAccess, |
| VkPipelineStageFlags dstStageMask, |
| VkAccessFlags dstAccess); |
| |
| void addEventImageBarrier(Renderer *renderer, |
| const RefCountedEvent &waitEvent, |
| VkPipelineStageFlags dstStageMask, |
| const VkImageMemoryBarrier &imageMemoryBarrier); |
| |
| void reset() { ASSERT(mBarriers.empty()); } |
| |
| void addDiagnosticsString(std::ostringstream &out) const; |
| |
| private: |
| std::deque<EventBarrier> mBarriers; |
| }; |
| } // namespace vk |
| } // namespace rx |
| #endif // LIBANGLE_RENDERER_VULKAN_REFCOUNTED_EVENT_H_ |