// // 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 { enum class ImageLayout; // 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 { … }; constexpr VkPipelineStageFlags kPreFragmentStageFlags = …; constexpr VkPipelineStageFlags kAllShadersPipelineStageFlags = …; constexpr VkPipelineStageFlags kAllDepthStencilPipelineStageFlags = …; constexpr VkPipelineStageFlags kFragmentAndAttachmentPipelineStageFlags = …; // 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 { … }; class PipelineStageAccessHeuristic final { … }; static constexpr PipelineStageAccessHeuristic kPipelineStageAccessFragmentOnly = …; static constexpr PipelineStageAccessHeuristic kPipelineStageAccessComputeOnly = …; static constexpr PipelineStageAccessHeuristic kPipelineStageAccessPreFragmentOnly = …; // 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 { … }; // Initialize EventStage to VkPipelineStageFlags mapping table. void InitializeEventAndPipelineStagesMap( angle::PackedEnumMap<EventStage, VkPipelineStageFlags> *mapping, VkPipelineStageFlags supportedVulkanPipelineStageMask); // 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 ImageLayout for now which gives us more information for debugging. struct EventAndStage { … }; // 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 { … }; RefCountedEventCollector; // Tracks a list of RefCountedEvents per EventStage. struct EventMaps { … }; // 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 { … }; // 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 RefCountedEventRecycler. // 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 // ShareGoupVk 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 { … }; // 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 { … }; // This wraps data and API for vkCmdWaitEvent call class EventBarrier : angle::NonCopyable { … }; class EventBarrierArray final { … }; } // namespace vk } // namespace rx #endif // LIBANGLE_RENDERER_VULKAN_REFCOUNTED_EVENT_H_