• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // RefCountedEvent:
7 //    Manages reference count of VkEvent and its associated functions.
8 //
9 
10 #include "libANGLE/renderer/vulkan/vk_ref_counted_event.h"
11 #include "libANGLE/renderer/vulkan/vk_helpers.h"
12 #include "libANGLE/renderer/vulkan/vk_renderer.h"
13 
14 namespace rx
15 {
16 namespace vk
17 {
18 namespace
19 {
20 // Predefined VkPipelineStageFlags for RefCountedEvent
21 constexpr angle::PackedEnumMap<EventStage, VkPipelineStageFlags>
22     kEventStageAndPipelineStageFlagsMap = {
23         {EventStage::Transfer, VK_PIPELINE_STAGE_TRANSFER_BIT},
24         {EventStage::VertexShader, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT},
25         {EventStage::FragmentShader, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT},
26         {EventStage::ComputeShader, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT},
27         {EventStage::AllShaders, kAllShadersPipelineStageFlags},
28         {EventStage::PreFragmentShaders, kPreFragmentStageFlags},
29         {EventStage::FragmentShadingRate,
30          VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR},
31         {EventStage::ColorAttachmentOutput, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT},
32         {EventStage::ColorAttachmentOutputAndFragmentShader,
33          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT},
34         {EventStage::ColorAttachmentOutputAndFragmentShaderAndTransfer,
35          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT |
36              VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT},
37         {EventStage::ColorAttachmentOutputAndAllShaders,
38          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | kAllShadersPipelineStageFlags},
39         {EventStage::AllFragmentTest, kAllDepthStencilPipelineStageFlags},
40         {EventStage::AllFragmentTestAndFragmentShader,
41          VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | kAllDepthStencilPipelineStageFlags},
42         {EventStage::AllFragmentTestAndAllShaders,
43          kAllShadersPipelineStageFlags | kAllDepthStencilPipelineStageFlags},
44         {EventStage::TransferAndComputeShader,
45          VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT}};
46 
DestroyRefCountedEvents(VkDevice device,RefCountedEventCollector & events)47 void DestroyRefCountedEvents(VkDevice device, RefCountedEventCollector &events)
48 {
49     while (!events.empty())
50     {
51         events.back().destroy(device);
52         events.pop_back();
53     }
54 }
55 }  // namespace
56 
InitializeEventAndPipelineStagesMap(angle::PackedEnumMap<EventStage,VkPipelineStageFlags> * map,VkPipelineStageFlags supportedVulkanPipelineStageMask)57 void InitializeEventAndPipelineStagesMap(
58     angle::PackedEnumMap<EventStage, VkPipelineStageFlags> *map,
59     VkPipelineStageFlags supportedVulkanPipelineStageMask)
60 {
61     *map = kEventStageAndPipelineStageFlagsMap;
62     for (VkPipelineStageFlags &flag : *map)
63     {
64         flag &= supportedVulkanPipelineStageMask;
65     }
66 }
67 
init(Context * context,EventStage eventStage)68 bool RefCountedEvent::init(Context *context, EventStage eventStage)
69 {
70     ASSERT(mHandle == nullptr);
71     ASSERT(eventStage != EventStage::InvalidEnum);
72 
73     // First try with recycler. We must issue VkCmdResetEvent before VkCmdSetEvent
74     if (context->getRefCountedEventsGarbageRecycler()->fetch(context->getRenderer(), this))
75     {
76         ASSERT(valid());
77         ASSERT(!mHandle->isReferenced());
78     }
79     else
80     {
81         // If failed to fetch from recycler, then create a new event.
82         mHandle                      = new RefCounted<EventAndStage>;
83         VkEventCreateInfo createInfo = {};
84         createInfo.sType             = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
85         // Use device only for performance reasons.
86         createInfo.flags = context->getFeatures().supportsSynchronization2.enabled
87                                ? VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR
88                                : 0;
89         VkResult result  = mHandle->get().event.init(context->getDevice(), createInfo);
90         if (result != VK_SUCCESS)
91         {
92             WARN() << "event.init failed. Clean up garbage and retry again";
93             // Proactively clean up garbage and retry
94             context->getRefCountedEventsGarbageRecycler()->cleanup(context->getRenderer());
95             result = mHandle->get().event.init(context->getDevice(), createInfo);
96             if (result != VK_SUCCESS)
97             {
98                 // Drivers usually can allocate huge amount of VkEvents, and we should never use
99                 // that many VkEvents under normal situation. If we failed to allocate, there is a
100                 // high chance that we may have a leak somewhere. This macro should help us catch
101                 // such potential bugs in the bots if that happens.
102                 UNREACHABLE();
103                 // If still fail to create, we just return. An invalid event will trigger
104                 // pipelineBarrier code path
105                 return false;
106             }
107         }
108     }
109 
110     mHandle->addRef();
111     mHandle->get().eventStage = eventStage;
112     return true;
113 }
114 
release(Context * context)115 void RefCountedEvent::release(Context *context)
116 {
117     if (mHandle != nullptr)
118     {
119         releaseImpl(context->getRenderer(), context->getRefCountedEventsGarbageRecycler());
120     }
121 }
122 
release(Renderer * renderer)123 void RefCountedEvent::release(Renderer *renderer)
124 {
125     if (mHandle != nullptr)
126     {
127         releaseImpl(renderer, renderer->getRefCountedEventRecycler());
128     }
129 }
130 
131 template <typename RecyclerT>
releaseImpl(Renderer * renderer,RecyclerT * recycler)132 void RefCountedEvent::releaseImpl(Renderer *renderer, RecyclerT *recycler)
133 {
134     ASSERT(mHandle != nullptr);
135     // This should never called from async submission thread since the refcount is not atomic. It is
136     // expected only called under context share lock.
137     ASSERT(std::this_thread::get_id() != renderer->getCommandProcessorThreadId());
138 
139     const bool isLastReference = mHandle->getAndReleaseRef() == 1;
140     if (isLastReference)
141     {
142         // When async submission is enabled, recycler will be null when release call comes from
143         // CommandProcessor. But in that case it will not be the last reference since garbage
144         // collector should have one reference count and will never release that reference count
145         // until GPU finished.
146         ASSERT(recycler != nullptr);
147         recycler->recycle(std::move(*this));
148         ASSERT(mHandle == nullptr);
149     }
150     else
151     {
152         mHandle = nullptr;
153     }
154 }
155 
destroy(VkDevice device)156 void RefCountedEvent::destroy(VkDevice device)
157 {
158     ASSERT(mHandle != nullptr);
159     ASSERT(!mHandle->isReferenced());
160     mHandle->get().event.destroy(device);
161     SafeDelete(mHandle);
162 }
163 
164 // RefCountedEventsGarbage implementation.
destroy(Renderer * renderer)165 void RefCountedEventsGarbage::destroy(Renderer *renderer)
166 {
167     ASSERT(renderer->hasQueueSerialFinished(mQueueSerial));
168     while (!mRefCountedEvents.empty())
169     {
170         ASSERT(mRefCountedEvents.back().valid());
171         mRefCountedEvents.back().release(renderer);
172         mRefCountedEvents.pop_back();
173     }
174 }
175 
releaseIfComplete(Renderer * renderer,RefCountedEventsGarbageRecycler * recycler)176 bool RefCountedEventsGarbage::releaseIfComplete(Renderer *renderer,
177                                                 RefCountedEventsGarbageRecycler *recycler)
178 {
179     if (!renderer->hasQueueSerialFinished(mQueueSerial))
180     {
181         return false;
182     }
183 
184     while (!mRefCountedEvents.empty())
185     {
186         ASSERT(mRefCountedEvents.back().valid());
187         mRefCountedEvents.back().releaseImpl(renderer, recycler);
188         ASSERT(!mRefCountedEvents.back().valid());
189         mRefCountedEvents.pop_back();
190     }
191     return true;
192 }
193 
moveIfComplete(Renderer * renderer,std::deque<RefCountedEventCollector> * releasedBucket)194 bool RefCountedEventsGarbage::moveIfComplete(Renderer *renderer,
195                                              std::deque<RefCountedEventCollector> *releasedBucket)
196 {
197     if (!renderer->hasQueueSerialFinished(mQueueSerial))
198     {
199         return false;
200     }
201 
202     releasedBucket->emplace_back(std::move(mRefCountedEvents));
203     return true;
204 }
205 
206 // RefCountedEventRecycler implementation.
destroy(VkDevice device)207 void RefCountedEventRecycler::destroy(VkDevice device)
208 {
209     std::lock_guard<angle::SimpleMutex> lock(mMutex);
210 
211     while (!mEventsToReset.empty())
212     {
213         DestroyRefCountedEvents(device, mEventsToReset.back());
214         mEventsToReset.pop_back();
215     }
216 
217     ASSERT(mResettingQueue.empty());
218 
219     while (!mEventsToReuse.empty())
220     {
221         DestroyRefCountedEvents(device, mEventsToReuse.back());
222         mEventsToReuse.pop_back();
223     }
224 }
225 
resetEvents(Context * context,const QueueSerial queueSerial,PrimaryCommandBuffer * commandbuffer)226 void RefCountedEventRecycler::resetEvents(Context *context,
227                                           const QueueSerial queueSerial,
228                                           PrimaryCommandBuffer *commandbuffer)
229 {
230     std::lock_guard<angle::SimpleMutex> lock(mMutex);
231 
232     if (mEventsToReset.empty())
233     {
234         return;
235     }
236 
237     Renderer *renderer = context->getRenderer();
238     while (!mEventsToReset.empty())
239     {
240         RefCountedEventCollector &events = mEventsToReset.back();
241         ASSERT(!events.empty());
242         for (const RefCountedEvent &refCountedEvent : events)
243         {
244             VkPipelineStageFlags stageMask = renderer->getEventPipelineStageMask(refCountedEvent);
245             commandbuffer->resetEvent(refCountedEvent.getEvent().getHandle(), stageMask);
246         }
247         mResettingQueue.emplace(queueSerial, std::move(events));
248         mEventsToReset.pop_back();
249     }
250 }
251 
cleanupResettingEvents(Renderer * renderer)252 void RefCountedEventRecycler::cleanupResettingEvents(Renderer *renderer)
253 {
254     std::lock_guard<angle::SimpleMutex> lock(mMutex);
255     while (!mResettingQueue.empty())
256     {
257         bool released = mResettingQueue.front().moveIfComplete(renderer, &mEventsToReuse);
258         if (released)
259         {
260             mResettingQueue.pop();
261         }
262         else
263         {
264             break;
265         }
266     }
267 }
268 
fetchEventsToReuse(RefCountedEventCollector * eventsToReuseOut)269 bool RefCountedEventRecycler::fetchEventsToReuse(RefCountedEventCollector *eventsToReuseOut)
270 {
271     ASSERT(eventsToReuseOut != nullptr);
272     ASSERT(eventsToReuseOut->empty());
273     std::lock_guard<angle::SimpleMutex> lock(mMutex);
274     if (mEventsToReuse.empty())
275     {
276         return false;
277     }
278     eventsToReuseOut->swap(mEventsToReuse.back());
279     mEventsToReuse.pop_back();
280     return true;
281 }
282 
283 // RefCountedEventsGarbageRecycler implementation.
~RefCountedEventsGarbageRecycler()284 RefCountedEventsGarbageRecycler::~RefCountedEventsGarbageRecycler()
285 {
286     ASSERT(mEventsToReset.empty());
287     ASSERT(mGarbageQueue.empty());
288     ASSERT(mEventsToReuse.empty());
289     ASSERT(mGarbageCount == 0);
290 }
291 
destroy(Renderer * renderer)292 void RefCountedEventsGarbageRecycler::destroy(Renderer *renderer)
293 {
294     VkDevice device = renderer->getDevice();
295     DestroyRefCountedEvents(device, mEventsToReset);
296     ASSERT(mGarbageQueue.empty());
297     ASSERT(mGarbageCount == 0);
298     mEventsToReuse.destroy(device);
299 }
300 
cleanup(Renderer * renderer)301 void RefCountedEventsGarbageRecycler::cleanup(Renderer *renderer)
302 {
303     // First cleanup already completed events and add to mEventsToReset
304     while (!mGarbageQueue.empty())
305     {
306         size_t count  = mGarbageQueue.front().size();
307         bool released = mGarbageQueue.front().releaseIfComplete(renderer, this);
308         if (released)
309         {
310             mGarbageCount -= count;
311             mGarbageQueue.pop();
312         }
313         else
314         {
315             break;
316         }
317     }
318 
319     // Move mEventsToReset to the renderer so that it can be reset.
320     if (!mEventsToReset.empty())
321     {
322         renderer->getRefCountedEventRecycler()->recycle(std::move(mEventsToReset));
323     }
324 }
325 
fetch(Renderer * renderer,RefCountedEvent * outObject)326 bool RefCountedEventsGarbageRecycler::fetch(Renderer *renderer, RefCountedEvent *outObject)
327 {
328     if (mEventsToReuse.empty())
329     {
330         // Retrieve a list of ready to reuse events from renderer.
331         RefCountedEventCollector events;
332         if (!renderer->getRefCountedEventRecycler()->fetchEventsToReuse(&events))
333         {
334             return false;
335         }
336         mEventsToReuse.refill(std::move(events));
337         ASSERT(!mEventsToReuse.empty());
338     }
339     mEventsToReuse.fetch(outObject);
340     return true;
341 }
342 
343 // EventBarrier implementation.
addDiagnosticsString(std::ostringstream & out) const344 void EventBarrier::addDiagnosticsString(std::ostringstream &out) const
345 {
346     if (mMemoryBarrierSrcAccess != 0 || mMemoryBarrierDstAccess != 0)
347     {
348         out << "Src: 0x" << std::hex << mMemoryBarrierSrcAccess << " &rarr; Dst: 0x" << std::hex
349             << mMemoryBarrierDstAccess << std::endl;
350     }
351 }
352 
execute(PrimaryCommandBuffer * primary)353 void EventBarrier::execute(PrimaryCommandBuffer *primary)
354 {
355     if (isEmpty())
356     {
357         return;
358     }
359     ASSERT(mEvent != VK_NULL_HANDLE);
360     ASSERT(mImageMemoryBarrierCount == 0 ||
361            (mImageMemoryBarrierCount == 1 && mImageMemoryBarrier.image != VK_NULL_HANDLE));
362 
363     // Issue vkCmdWaitEvents call
364     VkMemoryBarrier memoryBarrier = {};
365     memoryBarrier.sType           = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
366     memoryBarrier.srcAccessMask   = mMemoryBarrierSrcAccess;
367     memoryBarrier.dstAccessMask   = mMemoryBarrierDstAccess;
368 
369     primary->waitEvents(1, &mEvent, mSrcStageMask, mDstStageMask, 1, &memoryBarrier, 0, nullptr,
370                         mImageMemoryBarrierCount,
371                         mImageMemoryBarrierCount == 0 ? nullptr : &mImageMemoryBarrier);
372 }
373 
374 // EventBarrierArray implementation.
addAdditionalStageAccess(const RefCountedEvent & waitEvent,VkPipelineStageFlags dstStageMask,VkAccessFlags dstAccess)375 void EventBarrierArray::addAdditionalStageAccess(const RefCountedEvent &waitEvent,
376                                                  VkPipelineStageFlags dstStageMask,
377                                                  VkAccessFlags dstAccess)
378 {
379     for (EventBarrier &barrier : mBarriers)
380     {
381         if (barrier.hasEvent(waitEvent.getEvent().getHandle()))
382         {
383             barrier.addAdditionalStageAccess(dstStageMask, dstAccess);
384             return;
385         }
386     }
387     UNREACHABLE();
388 }
389 
addMemoryEvent(Renderer * renderer,const RefCountedEvent & waitEvent,VkPipelineStageFlags dstStageMask,VkAccessFlags dstAccess)390 void EventBarrierArray::addMemoryEvent(Renderer *renderer,
391                                        const RefCountedEvent &waitEvent,
392                                        VkPipelineStageFlags dstStageMask,
393                                        VkAccessFlags dstAccess)
394 {
395     ASSERT(waitEvent.valid());
396     VkPipelineStageFlags stageFlags = renderer->getEventPipelineStageMask(waitEvent);
397     // This should come down as WAW without layout change, dstStageMask should be the same as
398     // event's stageMask. Otherwise you should get into addImageEvent.
399     ASSERT(stageFlags == dstStageMask);
400     mBarriers.emplace_back(stageFlags, dstStageMask, dstAccess, dstAccess,
401                            waitEvent.getEvent().getHandle());
402 }
403 
addImageEvent(Renderer * renderer,const RefCountedEvent & waitEvent,VkPipelineStageFlags dstStageMask,const VkImageMemoryBarrier & imageMemoryBarrier)404 void EventBarrierArray::addImageEvent(Renderer *renderer,
405                                       const RefCountedEvent &waitEvent,
406                                       VkPipelineStageFlags dstStageMask,
407                                       const VkImageMemoryBarrier &imageMemoryBarrier)
408 {
409     ASSERT(waitEvent.valid());
410     VkPipelineStageFlags srcStageFlags = renderer->getEventPipelineStageMask(waitEvent);
411     mBarriers.emplace_back(srcStageFlags, dstStageMask, waitEvent.getEvent().getHandle(),
412                            imageMemoryBarrier);
413 }
414 
execute(Renderer * renderer,PrimaryCommandBuffer * primary)415 void EventBarrierArray::execute(Renderer *renderer, PrimaryCommandBuffer *primary)
416 {
417     while (!mBarriers.empty())
418     {
419         mBarriers.back().execute(primary);
420         mBarriers.pop_back();
421     }
422     reset();
423 }
424 
addDiagnosticsString(std::ostringstream & out) const425 void EventBarrierArray::addDiagnosticsString(std::ostringstream &out) const
426 {
427     out << "Event Barrier: ";
428     for (const EventBarrier &barrier : mBarriers)
429     {
430         barrier.addDiagnosticsString(out);
431     }
432     out << "\\l";
433 }
434 }  // namespace vk
435 }  // namespace rx
436