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 << " → 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