• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2020 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 // CommandProcessor.cpp:
7 //    Implements the class methods for CommandProcessor.
8 //
9 
10 #include "libANGLE/renderer/vulkan/CommandProcessor.h"
11 #include "common/system_utils.h"
12 #include "libANGLE/renderer/vulkan/RendererVk.h"
13 #include "libANGLE/renderer/vulkan/SyncVk.h"
14 
15 namespace rx
16 {
17 namespace vk
18 {
19 namespace
20 {
21 constexpr bool kOutputVmaStatsString = false;
22 // When suballocation garbages is more than this, we may wait for GPU to finish and free up some
23 // memory for allocation.
24 constexpr VkDeviceSize kMaxBufferSuballocationGarbageSize = 64 * 1024 * 1024;
25 
InitializeSubmitInfo(VkSubmitInfo * submitInfo,const PrimaryCommandBuffer & commandBuffer,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const VkSemaphore & signalSemaphore)26 void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
27                           const PrimaryCommandBuffer &commandBuffer,
28                           const std::vector<VkSemaphore> &waitSemaphores,
29                           const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
30                           const VkSemaphore &signalSemaphore)
31 {
32     // Verify that the submitInfo has been zero'd out.
33     ASSERT(submitInfo->signalSemaphoreCount == 0);
34     ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
35     submitInfo->sType              = VK_STRUCTURE_TYPE_SUBMIT_INFO;
36     submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
37     submitInfo->pCommandBuffers    = commandBuffer.ptr();
38     submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
39     submitInfo->pWaitSemaphores    = waitSemaphores.empty() ? nullptr : waitSemaphores.data();
40     submitInfo->pWaitDstStageMask  = waitSemaphoreStageMasks.data();
41 
42     if (signalSemaphore != VK_NULL_HANDLE)
43     {
44         submitInfo->signalSemaphoreCount = 1;
45         submitInfo->pSignalSemaphores    = &signalSemaphore;
46     }
47 }
48 }  // namespace
49 
50 // SharedFence implementation
SharedFence()51 SharedFence::SharedFence() : mRefCountedFence(nullptr), mRecycler(nullptr) {}
SharedFence(const SharedFence & other)52 SharedFence::SharedFence(const SharedFence &other)
53     : mRefCountedFence(other.mRefCountedFence), mRecycler(other.mRecycler)
54 {
55     if (mRefCountedFence != nullptr)
56     {
57         mRefCountedFence->addRef();
58     }
59 }
SharedFence(SharedFence && other)60 SharedFence::SharedFence(SharedFence &&other)
61     : mRefCountedFence(other.mRefCountedFence), mRecycler(other.mRecycler)
62 {
63     other.mRecycler        = nullptr;
64     other.mRefCountedFence = nullptr;
65 }
66 
~SharedFence()67 SharedFence::~SharedFence()
68 {
69     release();
70 }
71 
init(VkDevice device,FenceRecycler * recycler)72 VkResult SharedFence::init(VkDevice device, FenceRecycler *recycler)
73 {
74     ASSERT(mRecycler == nullptr && mRefCountedFence == nullptr);
75     Fence fence;
76 
77     // First try to fetch from recycler. If that failed, try to create a new VkFence
78     recycler->fetch(device, &fence);
79     if (!fence.valid())
80     {
81         VkFenceCreateInfo fenceCreateInfo = {};
82         fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
83         fenceCreateInfo.flags             = 0;
84         VkResult result                   = fence.init(device, fenceCreateInfo);
85         if (result != VK_SUCCESS)
86         {
87             return result;
88         }
89     }
90 
91     // Create a new refcounted object to hold onto VkFence
92     mRefCountedFence = new RefCounted<Fence>(std::move(fence));
93     mRefCountedFence->addRef();
94     mRecycler = recycler;
95 
96     return VK_SUCCESS;
97 }
98 
operator =(const SharedFence & other)99 SharedFence &SharedFence::operator=(const SharedFence &other)
100 {
101     release();
102 
103     mRecycler = other.mRecycler;
104     if (other.mRefCountedFence != nullptr)
105     {
106         mRefCountedFence = other.mRefCountedFence;
107         mRefCountedFence->addRef();
108     }
109     return *this;
110 }
111 
operator =(SharedFence && other)112 SharedFence &SharedFence::operator=(SharedFence &&other)
113 {
114     release();
115     mRecycler              = other.mRecycler;
116     mRefCountedFence       = other.mRefCountedFence;
117     other.mRecycler        = nullptr;
118     other.mRefCountedFence = nullptr;
119     return *this;
120 }
121 
destroy(VkDevice device)122 void SharedFence::destroy(VkDevice device)
123 {
124     if (mRefCountedFence != nullptr)
125     {
126         mRefCountedFence->releaseRef();
127         if (!mRefCountedFence->isReferenced())
128         {
129             mRefCountedFence->get().destroy(device);
130             SafeDelete(mRefCountedFence);
131         }
132         else
133         {
134             mRefCountedFence = nullptr;
135         }
136         mRecycler = nullptr;
137     }
138 }
139 
release()140 void SharedFence::release()
141 {
142     if (mRefCountedFence != nullptr)
143     {
144         mRefCountedFence->releaseRef();
145         if (!mRefCountedFence->isReferenced())
146         {
147             mRecycler->recycle(std::move(mRefCountedFence->get()));
148             ASSERT(!mRefCountedFence->get().valid());
149             SafeDelete(mRefCountedFence);
150         }
151         else
152         {
153             mRefCountedFence = nullptr;
154         }
155         mRecycler = nullptr;
156     }
157 }
158 
operator bool() const159 SharedFence::operator bool() const
160 {
161     ASSERT(mRefCountedFence == nullptr || mRefCountedFence->isReferenced());
162     return mRefCountedFence != nullptr;
163 }
164 
getStatus(VkDevice device) const165 VkResult SharedFence::getStatus(VkDevice device) const
166 {
167     if (mRefCountedFence != nullptr)
168     {
169         return mRefCountedFence->get().getStatus(device);
170     }
171     return VK_SUCCESS;
172 }
173 
wait(VkDevice device,uint64_t timeout) const174 VkResult SharedFence::wait(VkDevice device, uint64_t timeout) const
175 {
176     if (mRefCountedFence != nullptr)
177     {
178         ANGLE_TRACE_EVENT0("gpu.angle", "SharedFence::wait");
179         return mRefCountedFence->get().wait(device, timeout);
180     }
181     return VK_SUCCESS;
182 }
183 
184 // FenceRecycler implementation
destroy(Context * context)185 void FenceRecycler::destroy(Context *context)
186 {
187     std::lock_guard<std::mutex> lock(mMutex);
188     mRecyler.destroy(context->getDevice());
189 }
190 
fetch(VkDevice device,Fence * fenceOut)191 void FenceRecycler::fetch(VkDevice device, Fence *fenceOut)
192 {
193     ASSERT(fenceOut != nullptr && !fenceOut->valid());
194     std::lock_guard<std::mutex> lock(mMutex);
195     if (!mRecyler.empty())
196     {
197         mRecyler.fetch(fenceOut);
198         fenceOut->reset(device);
199     }
200 }
201 
recycle(Fence && fence)202 void FenceRecycler::recycle(Fence &&fence)
203 {
204     std::lock_guard<std::mutex> lock(mMutex);
205     mRecyler.recycle(std::move(fence));
206 }
207 
208 // CommandProcessorTask implementation
initTask()209 void CommandProcessorTask::initTask()
210 {
211     mTask                           = CustomTask::Invalid;
212     mOutsideRenderPassCommandBuffer = nullptr;
213     mRenderPassCommandBuffer        = nullptr;
214     mRenderPass                     = nullptr;
215     mSemaphore                      = VK_NULL_HANDLE;
216     mOneOffWaitSemaphore            = VK_NULL_HANDLE;
217     mOneOffWaitSemaphoreStageMask   = 0;
218     mPresentInfo                    = {};
219     mPresentInfo.pResults           = nullptr;
220     mPresentInfo.pSwapchains        = nullptr;
221     mPresentInfo.pImageIndices      = nullptr;
222     mPresentInfo.pNext              = nullptr;
223     mPresentInfo.pWaitSemaphores    = nullptr;
224     mPresentFence                   = VK_NULL_HANDLE;
225     mSwapchainStatus                = nullptr;
226     mOneOffCommandBuffer            = VK_NULL_HANDLE;
227     mPriority                       = egl::ContextPriority::Medium;
228     mProtectionType                 = ProtectionType::InvalidEnum;
229 }
230 
initFlushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)231 void CommandProcessorTask::initFlushWaitSemaphores(
232     ProtectionType protectionType,
233     egl::ContextPriority priority,
234     std::vector<VkSemaphore> &&waitSemaphores,
235     std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
236 {
237     mTask                    = CustomTask::FlushWaitSemaphores;
238     mPriority                = priority;
239     mProtectionType          = protectionType;
240     mWaitSemaphores          = std::move(waitSemaphores);
241     mWaitSemaphoreStageMasks = std::move(waitSemaphoreStageMasks);
242 }
243 
initOutsideRenderPassProcessCommands(ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper * commandBuffer)244 void CommandProcessorTask::initOutsideRenderPassProcessCommands(
245     ProtectionType protectionType,
246     egl::ContextPriority priority,
247     OutsideRenderPassCommandBufferHelper *commandBuffer)
248 {
249     mTask                           = CustomTask::ProcessOutsideRenderPassCommands;
250     mOutsideRenderPassCommandBuffer = commandBuffer;
251     mPriority                       = priority;
252     mProtectionType                 = protectionType;
253 }
254 
initRenderPassProcessCommands(ProtectionType protectionType,egl::ContextPriority priority,RenderPassCommandBufferHelper * commandBuffer,const RenderPass * renderPass)255 void CommandProcessorTask::initRenderPassProcessCommands(
256     ProtectionType protectionType,
257     egl::ContextPriority priority,
258     RenderPassCommandBufferHelper *commandBuffer,
259     const RenderPass *renderPass)
260 {
261     mTask                    = CustomTask::ProcessRenderPassCommands;
262     mRenderPassCommandBuffer = commandBuffer;
263     mRenderPass              = renderPass;
264     mPriority                = priority;
265     mProtectionType          = protectionType;
266 }
267 
copyPresentInfo(const VkPresentInfoKHR & other)268 void CommandProcessorTask::copyPresentInfo(const VkPresentInfoKHR &other)
269 {
270     if (other.sType == 0)
271     {
272         return;
273     }
274 
275     mPresentInfo.sType = other.sType;
276     mPresentInfo.pNext = nullptr;
277 
278     if (other.swapchainCount > 0)
279     {
280         ASSERT(other.swapchainCount == 1);
281         mPresentInfo.swapchainCount = 1;
282         mSwapchain                  = other.pSwapchains[0];
283         mPresentInfo.pSwapchains    = &mSwapchain;
284         mImageIndex                 = other.pImageIndices[0];
285         mPresentInfo.pImageIndices  = &mImageIndex;
286     }
287 
288     if (other.waitSemaphoreCount > 0)
289     {
290         ASSERT(other.waitSemaphoreCount == 1);
291         mPresentInfo.waitSemaphoreCount = 1;
292         mWaitSemaphore                  = other.pWaitSemaphores[0];
293         mPresentInfo.pWaitSemaphores    = &mWaitSemaphore;
294     }
295 
296     mPresentInfo.pResults = other.pResults;
297 
298     void *pNext = const_cast<void *>(other.pNext);
299     while (pNext != nullptr)
300     {
301         VkStructureType sType = *reinterpret_cast<VkStructureType *>(pNext);
302         switch (sType)
303         {
304             case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
305             {
306                 const VkPresentRegionsKHR *presentRegions =
307                     reinterpret_cast<VkPresentRegionsKHR *>(pNext);
308                 mPresentRegion = *presentRegions->pRegions;
309                 mRects.resize(mPresentRegion.rectangleCount);
310                 for (uint32_t i = 0; i < mPresentRegion.rectangleCount; i++)
311                 {
312                     mRects[i] = presentRegions->pRegions->pRectangles[i];
313                 }
314                 mPresentRegion.pRectangles = mRects.data();
315 
316                 mPresentRegions.sType          = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
317                 mPresentRegions.pNext          = nullptr;
318                 mPresentRegions.swapchainCount = 1;
319                 mPresentRegions.pRegions       = &mPresentRegion;
320                 AddToPNextChain(&mPresentInfo, &mPresentRegions);
321                 pNext = const_cast<void *>(presentRegions->pNext);
322                 break;
323             }
324             case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT:
325             {
326                 const VkSwapchainPresentFenceInfoEXT *presentFenceInfo =
327                     reinterpret_cast<VkSwapchainPresentFenceInfoEXT *>(pNext);
328                 ASSERT(presentFenceInfo->swapchainCount == 1);
329                 mPresentFence = presentFenceInfo->pFences[0];
330 
331                 mPresentFenceInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT;
332                 mPresentFenceInfo.pNext = nullptr;
333                 mPresentFenceInfo.swapchainCount = 1;
334                 mPresentFenceInfo.pFences        = &mPresentFence;
335                 AddToPNextChain(&mPresentInfo, &mPresentFenceInfo);
336                 pNext = const_cast<void *>(presentFenceInfo->pNext);
337                 break;
338             }
339             case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT:
340             {
341                 const VkSwapchainPresentModeInfoEXT *presentModeInfo =
342                     reinterpret_cast<VkSwapchainPresentModeInfoEXT *>(pNext);
343                 ASSERT(presentModeInfo->swapchainCount == 1);
344                 mPresentMode = presentModeInfo->pPresentModes[0];
345 
346                 mPresentModeInfo.sType          = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT;
347                 mPresentModeInfo.pNext          = nullptr;
348                 mPresentModeInfo.swapchainCount = 1;
349                 mPresentModeInfo.pPresentModes  = &mPresentMode;
350                 AddToPNextChain(&mPresentInfo, &mPresentModeInfo);
351                 pNext = const_cast<void *>(presentModeInfo->pNext);
352                 break;
353             }
354             default:
355                 ERR() << "Unknown sType: " << sType << " in VkPresentInfoKHR.pNext chain";
356                 UNREACHABLE();
357                 break;
358         }
359     }
360 }
361 
initPresent(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)362 void CommandProcessorTask::initPresent(egl::ContextPriority priority,
363                                        const VkPresentInfoKHR &presentInfo,
364                                        SwapchainStatus *swapchainStatus)
365 {
366     mTask            = CustomTask::Present;
367     mPriority        = priority;
368     mSwapchainStatus = swapchainStatus;
369     copyPresentInfo(presentInfo);
370 }
371 
initFlushAndQueueSubmit(VkSemaphore semaphore,SharedExternalFence && externalFence,ProtectionType protectionType,egl::ContextPriority priority,const QueueSerial & submitQueueSerial)372 void CommandProcessorTask::initFlushAndQueueSubmit(VkSemaphore semaphore,
373                                                    SharedExternalFence &&externalFence,
374                                                    ProtectionType protectionType,
375                                                    egl::ContextPriority priority,
376                                                    const QueueSerial &submitQueueSerial)
377 {
378     mTask              = CustomTask::FlushAndQueueSubmit;
379     mSemaphore         = semaphore;
380     mExternalFence     = std::move(externalFence);
381     mPriority          = priority;
382     mProtectionType    = protectionType;
383     mSubmitQueueSerial = submitQueueSerial;
384 }
385 
initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const QueueSerial & submitQueueSerial)386 void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,
387                                                  ProtectionType protectionType,
388                                                  egl::ContextPriority priority,
389                                                  VkSemaphore waitSemaphore,
390                                                  VkPipelineStageFlags waitSemaphoreStageMask,
391                                                  const QueueSerial &submitQueueSerial)
392 {
393     mTask                         = CustomTask::OneOffQueueSubmit;
394     mOneOffCommandBuffer          = commandBufferHandle;
395     mOneOffWaitSemaphore          = waitSemaphore;
396     mOneOffWaitSemaphoreStageMask = waitSemaphoreStageMask;
397     mPriority                     = priority;
398     mProtectionType               = protectionType;
399     mSubmitQueueSerial            = submitQueueSerial;
400 }
401 
operator =(CommandProcessorTask && rhs)402 CommandProcessorTask &CommandProcessorTask::operator=(CommandProcessorTask &&rhs)
403 {
404     if (this == &rhs)
405     {
406         return *this;
407     }
408 
409     std::swap(mRenderPass, rhs.mRenderPass);
410     std::swap(mOutsideRenderPassCommandBuffer, rhs.mOutsideRenderPassCommandBuffer);
411     std::swap(mRenderPassCommandBuffer, rhs.mRenderPassCommandBuffer);
412     std::swap(mTask, rhs.mTask);
413     std::swap(mWaitSemaphores, rhs.mWaitSemaphores);
414     std::swap(mWaitSemaphoreStageMasks, rhs.mWaitSemaphoreStageMasks);
415     std::swap(mSemaphore, rhs.mSemaphore);
416     std::swap(mExternalFence, rhs.mExternalFence);
417     std::swap(mOneOffWaitSemaphore, rhs.mOneOffWaitSemaphore);
418     std::swap(mOneOffWaitSemaphoreStageMask, rhs.mOneOffWaitSemaphoreStageMask);
419     std::swap(mSubmitQueueSerial, rhs.mSubmitQueueSerial);
420     std::swap(mPriority, rhs.mPriority);
421     std::swap(mProtectionType, rhs.mProtectionType);
422     std::swap(mOneOffCommandBuffer, rhs.mOneOffCommandBuffer);
423 
424     copyPresentInfo(rhs.mPresentInfo);
425     std::swap(mSwapchainStatus, rhs.mSwapchainStatus);
426 
427     // clear rhs now that everything has moved.
428     rhs.initTask();
429 
430     return *this;
431 }
432 
433 // CommandBatch implementation.
CommandBatch()434 CommandBatch::CommandBatch() : protectionType(ProtectionType::InvalidEnum) {}
435 
436 CommandBatch::~CommandBatch() = default;
437 
CommandBatch(CommandBatch && other)438 CommandBatch::CommandBatch(CommandBatch &&other) : CommandBatch()
439 {
440     *this = std::move(other);
441 }
442 
operator =(CommandBatch && other)443 CommandBatch &CommandBatch::operator=(CommandBatch &&other)
444 {
445     std::swap(primaryCommands, other.primaryCommands);
446     std::swap(secondaryCommands, other.secondaryCommands);
447     std::swap(fence, other.fence);
448     std::swap(externalFence, other.externalFence);
449     std::swap(queueSerial, other.queueSerial);
450     std::swap(protectionType, other.protectionType);
451     return *this;
452 }
453 
destroy(VkDevice device)454 void CommandBatch::destroy(VkDevice device)
455 {
456     primaryCommands.destroy(device);
457     secondaryCommands.retireCommandBuffers();
458     destroyFence(device);
459     protectionType = ProtectionType::InvalidEnum;
460 }
461 
hasFence() const462 bool CommandBatch::hasFence() const
463 {
464     ASSERT(!externalFence || !fence);
465     return fence || externalFence;
466 }
467 
releaseFence()468 void CommandBatch::releaseFence()
469 {
470     fence.release();
471     externalFence.reset();
472 }
473 
destroyFence(VkDevice device)474 void CommandBatch::destroyFence(VkDevice device)
475 {
476     fence.destroy(device);
477     externalFence.reset();
478 }
479 
getFenceHandle() const480 VkFence CommandBatch::getFenceHandle() const
481 {
482     ASSERT(hasFence());
483     return fence ? fence.get().getHandle() : externalFence->getHandle();
484 }
485 
getFenceStatus(VkDevice device) const486 VkResult CommandBatch::getFenceStatus(VkDevice device) const
487 {
488     ASSERT(hasFence());
489     return fence ? fence.getStatus(device) : externalFence->getStatus(device);
490 }
491 
waitFence(VkDevice device,uint64_t timeout) const492 VkResult CommandBatch::waitFence(VkDevice device, uint64_t timeout) const
493 {
494     ASSERT(hasFence());
495     return fence ? fence.wait(device, timeout) : externalFence->wait(device, timeout);
496 }
497 
waitFenceUnlocked(VkDevice device,uint64_t timeout,std::unique_lock<std::mutex> * lock) const498 VkResult CommandBatch::waitFenceUnlocked(VkDevice device,
499                                          uint64_t timeout,
500                                          std::unique_lock<std::mutex> *lock) const
501 {
502     ASSERT(hasFence());
503     VkResult status;
504     // You can only use the local copy of the fence without lock.
505     // Do not access "this" after unlock() because object might be deleted from other thread.
506     if (fence)
507     {
508         const SharedFence localFenceToWaitOn = fence;
509         lock->unlock();
510         status = localFenceToWaitOn.wait(device, timeout);
511         lock->lock();
512     }
513     else
514     {
515         const SharedExternalFence localFenceToWaitOn = externalFence;
516         lock->unlock();
517         status = localFenceToWaitOn->wait(device, timeout);
518         lock->lock();
519     }
520     return status;
521 }
522 
523 // CommandProcessor implementation.
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)524 void CommandProcessor::handleError(VkResult errorCode,
525                                    const char *file,
526                                    const char *function,
527                                    unsigned int line)
528 {
529     ASSERT(errorCode != VK_SUCCESS);
530 
531     std::stringstream errorStream;
532     errorStream << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode)
533                 << ".";
534 
535     if (errorCode == VK_ERROR_DEVICE_LOST)
536     {
537         WARN() << errorStream.str();
538         handleDeviceLost(mRenderer);
539     }
540 
541     std::lock_guard<std::mutex> queueLock(mErrorMutex);
542     Error error = {errorCode, file, function, line};
543     mErrors.emplace(error);
544 }
545 
CommandProcessor(RendererVk * renderer,CommandQueue * commandQueue)546 CommandProcessor::CommandProcessor(RendererVk *renderer, CommandQueue *commandQueue)
547     : Context(renderer),
548       mCommandQueue(commandQueue),
549       mTaskThreadShouldExit(false),
550       mNeedCommandsAndGarbageCleanup(false)
551 {
552     std::lock_guard<std::mutex> queueLock(mErrorMutex);
553     while (!mErrors.empty())
554     {
555         mErrors.pop();
556     }
557 }
558 
559 CommandProcessor::~CommandProcessor() = default;
560 
checkAndPopPendingError(Context * errorHandlingContext)561 angle::Result CommandProcessor::checkAndPopPendingError(Context *errorHandlingContext)
562 {
563     std::lock_guard<std::mutex> queueLock(mErrorMutex);
564     if (mErrors.empty())
565     {
566         return angle::Result::Continue;
567     }
568 
569     while (!mErrors.empty())
570     {
571         Error err = mErrors.front();
572         mErrors.pop();
573         errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
574     }
575     return angle::Result::Stop;
576 }
577 
queueCommand(CommandProcessorTask && task)578 angle::Result CommandProcessor::queueCommand(CommandProcessorTask &&task)
579 {
580     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queueCommand");
581     // Take mTaskEnqueueMutex lock. If task queue is full, try to drain one.
582     std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
583     if (mTaskQueue.full())
584     {
585         std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
586         // Check mTasks again in case someone just drained the mTasks.
587         if (mTaskQueue.full())
588         {
589             CommandProcessorTask frontTask(std::move(mTaskQueue.front()));
590             mTaskQueue.pop();
591             ANGLE_TRY(processTask(&frontTask));
592         }
593     }
594     mTaskQueue.push(std::move(task));
595     mWorkAvailableCondition.notify_one();
596 
597     return angle::Result::Continue;
598 }
599 
requestCommandsAndGarbageCleanup()600 void CommandProcessor::requestCommandsAndGarbageCleanup()
601 {
602     if (!mNeedCommandsAndGarbageCleanup.exchange(true))
603     {
604         // request clean up in async thread
605         std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
606         mWorkAvailableCondition.notify_one();
607     }
608 }
609 
processTasks()610 void CommandProcessor::processTasks()
611 {
612     while (true)
613     {
614         bool exitThread      = false;
615         angle::Result result = processTasksImpl(&exitThread);
616         if (exitThread)
617         {
618             // We are doing a controlled exit of the thread, break out of the while loop.
619             break;
620         }
621         if (result != angle::Result::Continue)
622         {
623             // TODO: https://issuetracker.google.com/issues/170311829 - follow-up on error handling
624             // ContextVk::commandProcessorSyncErrorsAndQueueCommand and WindowSurfaceVk::destroy
625             // do error processing, is anything required here? Don't think so, mostly need to
626             // continue the worker thread until it's been told to exit.
627             UNREACHABLE();
628         }
629     }
630 }
631 
processTasksImpl(bool * exitThread)632 angle::Result CommandProcessor::processTasksImpl(bool *exitThread)
633 {
634     while (true)
635     {
636         std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
637         if (mTaskQueue.empty())
638         {
639             if (mTaskThreadShouldExit)
640             {
641                 break;
642             }
643 
644             // Only wake if notified and command queue is not empty
645             mWorkAvailableCondition.wait(enqueueLock, [this] {
646                 return !mTaskQueue.empty() || mTaskThreadShouldExit ||
647                        mNeedCommandsAndGarbageCleanup;
648             });
649         }
650         // Do submission with mTaskEnqueueMutex unlocked so that we still allow enqueue while we
651         // process work.
652         enqueueLock.unlock();
653 
654         // Take submission lock to ensure the submission is in the same order as we received.
655         std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
656         if (!mTaskQueue.empty())
657         {
658             CommandProcessorTask task(std::move(mTaskQueue.front()));
659             mTaskQueue.pop();
660 
661             // Artificially make the task take longer to catch threading issues.
662             if (getFeatures().slowAsyncCommandQueueForTesting.enabled)
663             {
664                 constexpr double kSlowdownTime = 0.005;
665 
666                 double startTime = angle::GetCurrentSystemTime();
667                 while (angle::GetCurrentSystemTime() - startTime < kSlowdownTime)
668                 {
669                     // Busy waiting
670                 }
671             }
672 
673             ANGLE_TRY(processTask(&task));
674         }
675 
676         if (mNeedCommandsAndGarbageCleanup.exchange(false))
677         {
678             // Always check completed commands again in case anything new has been finished.
679             ANGLE_TRY(mCommandQueue->checkCompletedCommands(this));
680 
681             // Reset command buffer and clean up garbage
682             if (mRenderer->isAsyncCommandBufferResetEnabled() &&
683                 mCommandQueue->hasFinishedCommands())
684             {
685                 ANGLE_TRY(mCommandQueue->retireFinishedCommands(this));
686             }
687             mRenderer->cleanupGarbage();
688         }
689     }
690     *exitThread = true;
691     return angle::Result::Continue;
692 }
693 
processTask(CommandProcessorTask * task)694 angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
695 {
696     switch (task->getTaskCommand())
697     {
698         case CustomTask::FlushAndQueueSubmit:
699         {
700             ANGLE_TRACE_EVENT0("gpu.angle", "processTask::FlushAndQueueSubmit");
701             // End command buffer
702 
703             // Call submitCommands()
704             ANGLE_TRY(mCommandQueue->submitCommands(
705                 this, task->getProtectionType(), task->getPriority(), task->getSemaphore(),
706                 std::move(task->getExternalFence()), task->getSubmitQueueSerial()));
707             mNeedCommandsAndGarbageCleanup = true;
708             break;
709         }
710         case CustomTask::OneOffQueueSubmit:
711         {
712             ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit");
713 
714             ANGLE_TRY(mCommandQueue->queueSubmitOneOff(
715                 this, task->getProtectionType(), task->getPriority(),
716                 task->getOneOffCommandBuffer(), task->getOneOffWaitSemaphore(),
717                 task->getOneOffWaitSemaphoreStageMask(), SubmitPolicy::EnsureSubmitted,
718                 task->getSubmitQueueSerial()));
719             mNeedCommandsAndGarbageCleanup = true;
720             break;
721         }
722         case CustomTask::Present:
723         {
724             // Do not access task->getSwapchainStatus() after this call because it is marked as no
725             // longer pending, and so may get deleted or clobbered by another thread.
726             VkResult result =
727                 present(task->getPriority(), task->getPresentInfo(), task->getSwapchainStatus());
728 
729             // We get to ignore these as they are not fatal
730             if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR &&
731                 result != VK_SUCCESS)
732             {
733                 // Save the error so that we can handle it.
734                 // Don't leave processing loop, don't consider errors from present to be fatal.
735                 // TODO: https://issuetracker.google.com/issues/170329600 - This needs to improve to
736                 // properly parallelize present
737                 handleError(result, __FILE__, __FUNCTION__, __LINE__);
738             }
739             break;
740         }
741         case CustomTask::FlushWaitSemaphores:
742         {
743             mCommandQueue->flushWaitSemaphores(task->getProtectionType(), task->getPriority(),
744                                                std::move(task->getWaitSemaphores()),
745                                                std::move(task->getWaitSemaphoreStageMasks()));
746             break;
747         }
748         case CustomTask::ProcessOutsideRenderPassCommands:
749         {
750             OutsideRenderPassCommandBufferHelper *commandBuffer =
751                 task->getOutsideRenderPassCommandBuffer();
752             ANGLE_TRY(mCommandQueue->flushOutsideRPCommands(this, task->getProtectionType(),
753                                                             task->getPriority(), &commandBuffer));
754 
755             OutsideRenderPassCommandBufferHelper *originalCommandBuffer =
756                 task->getOutsideRenderPassCommandBuffer();
757             mRenderer->recycleOutsideRenderPassCommandBufferHelper(&originalCommandBuffer);
758             break;
759         }
760         case CustomTask::ProcessRenderPassCommands:
761         {
762             RenderPassCommandBufferHelper *commandBuffer = task->getRenderPassCommandBuffer();
763             ANGLE_TRY(mCommandQueue->flushRenderPassCommands(
764                 this, task->getProtectionType(), task->getPriority(), *task->getRenderPass(),
765                 &commandBuffer));
766 
767             RenderPassCommandBufferHelper *originalCommandBuffer =
768                 task->getRenderPassCommandBuffer();
769             mRenderer->recycleRenderPassCommandBufferHelper(&originalCommandBuffer);
770             break;
771         }
772         default:
773             UNREACHABLE();
774             break;
775     }
776 
777     return angle::Result::Continue;
778 }
779 
waitForAllWorkToBeSubmitted(Context * context)780 angle::Result CommandProcessor::waitForAllWorkToBeSubmitted(Context *context)
781 {
782     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForAllWorkToBeSubmitted");
783     // Take mWorkerMutex lock so that no one is able to enqueue more work while we drain it
784     // and handle device lost.
785     std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
786     std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
787     // Sync any errors to the context
788     // Do this inside the mutex to prevent new errors adding to the list.
789     ANGLE_TRY(checkAndPopPendingError(context));
790 
791     while (!mTaskQueue.empty())
792     {
793         CommandProcessorTask task(std::move(mTaskQueue.front()));
794         mTaskQueue.pop();
795         ANGLE_TRY(processTask(&task));
796     }
797 
798     if (mRenderer->isAsyncCommandBufferResetEnabled())
799     {
800         ANGLE_TRY(mCommandQueue->retireFinishedCommands(context));
801     }
802     context->getRenderer()->cleanupGarbage();
803 
804     mNeedCommandsAndGarbageCleanup = false;
805 
806     return angle::Result::Continue;
807 }
808 
init()809 angle::Result CommandProcessor::init()
810 {
811     mTaskThread = std::thread(&CommandProcessor::processTasks, this);
812 
813     return angle::Result::Continue;
814 }
815 
destroy(Context * context)816 void CommandProcessor::destroy(Context *context)
817 {
818     {
819         // Request to terminate the worker thread
820         std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
821         mTaskThreadShouldExit = true;
822         mWorkAvailableCondition.notify_one();
823     }
824 
825     (void)waitForAllWorkToBeSubmitted(context);
826     if (mTaskThread.joinable())
827     {
828         mTaskThread.join();
829     }
830 }
831 
handleDeviceLost(RendererVk * renderer)832 void CommandProcessor::handleDeviceLost(RendererVk *renderer)
833 {
834     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::handleDeviceLost");
835     // Take mTaskEnqueueMutex lock so that no one is able to add more work to the queue while we
836     // drain it and handle device lost.
837     std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
838     (void)waitForAllWorkToBeSubmitted(this);
839     // Worker thread is idle and command queue is empty so good to continue
840     mCommandQueue->handleDeviceLost(renderer);
841 }
842 
present(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)843 VkResult CommandProcessor::present(egl::ContextPriority priority,
844                                    const VkPresentInfoKHR &presentInfo,
845                                    SwapchainStatus *swapchainStatus)
846 {
847     ANGLE_TRACE_EVENT0("gpu.angle", "vkQueuePresentKHR");
848     // Verify that we are presenting one and only one swapchain
849     ASSERT(presentInfo.swapchainCount == 1);
850     ASSERT(presentInfo.pResults == nullptr);
851 
852     mCommandQueue->queuePresent(priority, presentInfo, swapchainStatus);
853     const VkResult result = swapchainStatus->lastPresentResult;
854 
855     // Always make sure update isPending after status has been updated.
856     // Can't access swapchainStatus after this assignment because it is marked as no longer pending,
857     // and so may get deleted or clobbered by another thread.
858     ASSERT(swapchainStatus->isPending);
859     swapchainStatus->isPending = false;
860 
861     return result;
862 }
863 
enqueueSubmitCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,const QueueSerial & submitQueueSerial)864 angle::Result CommandProcessor::enqueueSubmitCommands(Context *context,
865                                                       ProtectionType protectionType,
866                                                       egl::ContextPriority priority,
867                                                       VkSemaphore signalSemaphore,
868                                                       SharedExternalFence &&externalFence,
869                                                       const QueueSerial &submitQueueSerial)
870 {
871     ANGLE_TRY(checkAndPopPendingError(context));
872 
873     CommandProcessorTask task;
874     task.initFlushAndQueueSubmit(signalSemaphore, std::move(externalFence), protectionType,
875                                  priority, submitQueueSerial);
876 
877     ANGLE_TRY(queueCommand(std::move(task)));
878 
879     mLastEnqueuedSerials.setQueueSerial(submitQueueSerial);
880 
881     return angle::Result::Continue;
882 }
883 
enqueueSubmitOneOffCommands(Context * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,SubmitPolicy submitPolicy,const QueueSerial & submitQueueSerial)884 angle::Result CommandProcessor::enqueueSubmitOneOffCommands(
885     Context *context,
886     ProtectionType protectionType,
887     egl::ContextPriority contextPriority,
888     VkCommandBuffer commandBufferHandle,
889     VkSemaphore waitSemaphore,
890     VkPipelineStageFlags waitSemaphoreStageMask,
891     SubmitPolicy submitPolicy,
892     const QueueSerial &submitQueueSerial)
893 {
894     ANGLE_TRY(checkAndPopPendingError(context));
895 
896     CommandProcessorTask task;
897     task.initOneOffQueueSubmit(commandBufferHandle, protectionType, contextPriority, waitSemaphore,
898                                waitSemaphoreStageMask, submitQueueSerial);
899     ANGLE_TRY(queueCommand(std::move(task)));
900 
901     mLastEnqueuedSerials.setQueueSerial(submitQueueSerial);
902 
903     if (submitPolicy == SubmitPolicy::EnsureSubmitted)
904     {
905         // Caller has synchronization requirement to have work in GPU pipe when returning from this
906         // function.
907         ANGLE_TRY(waitForQueueSerialToBeSubmitted(context, submitQueueSerial));
908     }
909 
910     return angle::Result::Continue;
911 }
912 
enqueuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)913 void CommandProcessor::enqueuePresent(egl::ContextPriority contextPriority,
914                                       const VkPresentInfoKHR &presentInfo,
915                                       SwapchainStatus *swapchainStatus)
916 {
917     ASSERT(!swapchainStatus->isPending);
918     swapchainStatus->isPending = true;
919     // Always return with VK_SUCCESS initially. When we call acquireNextImage we'll check the
920     // return code again. This allows the app to continue working until we really need to know
921     // the return code from present.
922     swapchainStatus->lastPresentResult = VK_SUCCESS;
923 
924     CommandProcessorTask task;
925     task.initPresent(contextPriority, presentInfo, swapchainStatus);
926     (void)queueCommand(std::move(task));
927 }
928 
enqueueFlushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)929 angle::Result CommandProcessor::enqueueFlushWaitSemaphores(
930     ProtectionType protectionType,
931     egl::ContextPriority priority,
932     std::vector<VkSemaphore> &&waitSemaphores,
933     std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
934 {
935     CommandProcessorTask task;
936     task.initFlushWaitSemaphores(protectionType, priority, std::move(waitSemaphores),
937                                  std::move(waitSemaphoreStageMasks));
938     ANGLE_TRY(queueCommand(std::move(task)));
939 
940     return angle::Result::Continue;
941 }
942 
enqueueFlushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)943 angle::Result CommandProcessor::enqueueFlushOutsideRPCommands(
944     Context *context,
945     ProtectionType protectionType,
946     egl::ContextPriority priority,
947     OutsideRenderPassCommandBufferHelper **outsideRPCommands)
948 {
949     ANGLE_TRY(checkAndPopPendingError(context));
950 
951     (*outsideRPCommands)->markClosed();
952 
953     SecondaryCommandPool *commandPool = nullptr;
954     ANGLE_TRY((*outsideRPCommands)->detachCommandPool(context, &commandPool));
955 
956     // Detach functions are only used for ring buffer allocators.
957     SecondaryCommandMemoryAllocator *allocator = (*outsideRPCommands)->detachAllocator();
958 
959     CommandProcessorTask task;
960     task.initOutsideRenderPassProcessCommands(protectionType, priority, *outsideRPCommands);
961     ANGLE_TRY(queueCommand(std::move(task)));
962 
963     ANGLE_TRY(mRenderer->getOutsideRenderPassCommandBufferHelper(context, commandPool, allocator,
964                                                                  outsideRPCommands));
965 
966     return angle::Result::Continue;
967 }
968 
enqueueFlushRenderPassCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,const RenderPass & renderPass,RenderPassCommandBufferHelper ** renderPassCommands)969 angle::Result CommandProcessor::enqueueFlushRenderPassCommands(
970     Context *context,
971     ProtectionType protectionType,
972     egl::ContextPriority priority,
973     const RenderPass &renderPass,
974     RenderPassCommandBufferHelper **renderPassCommands)
975 {
976     ANGLE_TRY(checkAndPopPendingError(context));
977 
978     (*renderPassCommands)->markClosed();
979 
980     SecondaryCommandPool *commandPool = nullptr;
981     (*renderPassCommands)->detachCommandPool(&commandPool);
982 
983     // Detach functions are only used for ring buffer allocators.
984     SecondaryCommandMemoryAllocator *allocator = (*renderPassCommands)->detachAllocator();
985 
986     CommandProcessorTask task;
987     task.initRenderPassProcessCommands(protectionType, priority, *renderPassCommands, &renderPass);
988     ANGLE_TRY(queueCommand(std::move(task)));
989 
990     ANGLE_TRY(mRenderer->getRenderPassCommandBufferHelper(context, commandPool, allocator,
991                                                           renderPassCommands));
992 
993     return angle::Result::Continue;
994 }
995 
waitForResourceUseToBeSubmitted(Context * context,const ResourceUse & use)996 angle::Result CommandProcessor::waitForResourceUseToBeSubmitted(Context *context,
997                                                                 const ResourceUse &use)
998 {
999     if (mCommandQueue->hasResourceUseSubmitted(use))
1000     {
1001         ANGLE_TRY(checkAndPopPendingError(context));
1002     }
1003     else
1004     {
1005         // We do not hold mTaskEnqueueMutex lock, so that we still allow other context to enqueue
1006         // work while we are processing them.
1007         std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
1008 
1009         // Do this inside the mutex to prevent new errors adding to the list.
1010         ANGLE_TRY(checkAndPopPendingError(context));
1011 
1012         size_t maxTaskCount = mTaskQueue.size();
1013         size_t taskCount    = 0;
1014         while (taskCount < maxTaskCount && !mCommandQueue->hasResourceUseSubmitted(use))
1015         {
1016             CommandProcessorTask task(std::move(mTaskQueue.front()));
1017             mTaskQueue.pop();
1018             ANGLE_TRY(processTask(&task));
1019             taskCount++;
1020         }
1021     }
1022     return angle::Result::Continue;
1023 }
1024 
waitForPresentToBeSubmitted(SwapchainStatus * swapchainStatus)1025 angle::Result CommandProcessor::waitForPresentToBeSubmitted(SwapchainStatus *swapchainStatus)
1026 {
1027     if (!swapchainStatus->isPending)
1028     {
1029         return angle::Result::Continue;
1030     }
1031 
1032     std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
1033     size_t maxTaskCount = mTaskQueue.size();
1034     size_t taskCount    = 0;
1035     while (taskCount < maxTaskCount && swapchainStatus->isPending)
1036     {
1037         CommandProcessorTask task(std::move(mTaskQueue.front()));
1038         mTaskQueue.pop();
1039         ANGLE_TRY(processTask(&task));
1040         taskCount++;
1041     }
1042     ASSERT(!swapchainStatus->isPending);
1043     return angle::Result::Continue;
1044 }
1045 
1046 // CommandQueue public API implementation. These must be thread safe and never called from
1047 // CommandQueue class itself.
CommandQueue()1048 CommandQueue::CommandQueue() : mPerfCounters{} {}
1049 
1050 CommandQueue::~CommandQueue() = default;
1051 
destroy(Context * context)1052 void CommandQueue::destroy(Context *context)
1053 {
1054     std::lock_guard<std::mutex> lock(mMutex);
1055     std::lock_guard<std::mutex> enqueuelock(mQueueSubmitMutex);
1056     // Force all commands to finish by flushing all queues.
1057     for (VkQueue queue : mQueueMap)
1058     {
1059         if (queue != VK_NULL_HANDLE)
1060         {
1061             vkQueueWaitIdle(queue);
1062         }
1063     }
1064 
1065     RendererVk *renderer = context->getRenderer();
1066 
1067     // Assigns an infinite "last completed" serial to force garbage to delete.
1068     mLastCompletedSerials.fill(Serial::Infinite());
1069 
1070     for (auto &protectionMap : mCommandsStateMap)
1071     {
1072         for (CommandsState &state : protectionMap)
1073         {
1074             state.waitSemaphores.clear();
1075             state.waitSemaphoreStageMasks.clear();
1076             state.primaryCommands.destroy(renderer->getDevice());
1077             state.secondaryCommands.retireCommandBuffers();
1078         }
1079     }
1080 
1081     for (PersistentCommandPool &commandPool : mPrimaryCommandPoolMap)
1082     {
1083         commandPool.destroy(renderer->getDevice());
1084     }
1085 
1086     mFenceRecycler.destroy(context);
1087 
1088     ASSERT(mInFlightCommands.empty());
1089     ASSERT(mFinishedCommandBatches.empty());
1090 }
1091 
init(Context * context,const DeviceQueueMap & queueMap)1092 angle::Result CommandQueue::init(Context *context, const DeviceQueueMap &queueMap)
1093 {
1094     std::lock_guard<std::mutex> lock(mMutex);
1095     // In case of RendererVk gets re-initialized, we can't rely on constructor to do initialization
1096     // for us.
1097     mLastSubmittedSerials.fill(kZeroSerial);
1098     mLastCompletedSerials.fill(kZeroSerial);
1099 
1100     // Assign before initializing the command pools in order to get the queue family index.
1101     mQueueMap = queueMap;
1102 
1103     ANGLE_TRY(initCommandPool(context, ProtectionType::Unprotected));
1104 
1105     if (queueMap.isProtected())
1106     {
1107         ANGLE_TRY(initCommandPool(context, ProtectionType::Protected));
1108     }
1109 
1110     return angle::Result::Continue;
1111 }
1112 
handleDeviceLost(RendererVk * renderer)1113 void CommandQueue::handleDeviceLost(RendererVk *renderer)
1114 {
1115     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::handleDeviceLost");
1116     VkDevice device = renderer->getDevice();
1117     // Hold both locks while clean up mInFlightCommands.
1118     std::lock_guard<std::mutex> dequeuelock(mMutex);
1119     std::lock_guard<std::mutex> enqueuelock(mQueueSubmitMutex);
1120 
1121     while (!mInFlightCommands.empty())
1122     {
1123         CommandBatch &batch = mInFlightCommands.front();
1124         // On device loss we need to wait for fence to be signaled before destroying it
1125         if (batch.hasFence())
1126         {
1127             VkResult status = batch.waitFence(device, renderer->getMaxFenceWaitTimeNs());
1128             // If the wait times out, it is probably not possible to recover from lost device
1129             ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
1130 
1131             batch.destroyFence(device);
1132         }
1133 
1134         // On device lost, here simply destroy the CommandBuffer, it will fully cleared later
1135         // by CommandPool::destroy
1136         if (batch.primaryCommands.valid())
1137         {
1138             batch.primaryCommands.destroy(device);
1139         }
1140 
1141         batch.secondaryCommands.retireCommandBuffers();
1142 
1143         mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1144         mInFlightCommands.pop();
1145     }
1146 }
1147 
postSubmitCheck(Context * context)1148 angle::Result CommandQueue::postSubmitCheck(Context *context)
1149 {
1150     RendererVk *renderer = context->getRenderer();
1151 
1152     // Update mLastCompletedQueueSerial immediately in case any command has been finished.
1153     ANGLE_TRY(checkAndCleanupCompletedCommands(context));
1154 
1155     VkDeviceSize suballocationGarbageSize = renderer->getSuballocationGarbageSize();
1156     if (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize)
1157     {
1158         // CPU should be throttled to avoid accumulating too much memory garbage waiting to be
1159         // destroyed. This is important to keep peak memory usage at check when game launched and a
1160         // lot of staging buffers used for textures upload and then gets released. But if there is
1161         // only one command buffer in flight, we do not wait here to ensure we keep GPU busy.
1162         std::unique_lock<std::mutex> lock(mMutex);
1163         while (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize &&
1164                mInFlightCommands.size() > 1)
1165         {
1166             ANGLE_TRY(
1167                 finishOneCommandBatchAndCleanupImpl(context, renderer->getMaxFenceWaitTimeNs()));
1168             suballocationGarbageSize = renderer->getSuballocationGarbageSize();
1169         }
1170     }
1171 
1172     if (kOutputVmaStatsString)
1173     {
1174         renderer->outputVmaStatString();
1175     }
1176 
1177     return angle::Result::Continue;
1178 }
1179 
finishResourceUse(Context * context,const ResourceUse & use,uint64_t timeout)1180 angle::Result CommandQueue::finishResourceUse(Context *context,
1181                                               const ResourceUse &use,
1182                                               uint64_t timeout)
1183 {
1184     VkDevice device = context->getDevice();
1185 
1186     {
1187         std::unique_lock<std::mutex> lock(mMutex);
1188         while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
1189         {
1190             bool finished;
1191             ANGLE_TRY(checkOneCommandBatch(context, &finished));
1192             if (!finished)
1193             {
1194                 ANGLE_VK_TRY(context,
1195                              mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock));
1196             }
1197         }
1198         // Check the rest of the commands in case they are also finished.
1199         ANGLE_TRY(checkCompletedCommandsLocked(context));
1200     }
1201     ASSERT(hasResourceUseFinished(use));
1202 
1203     if (!mFinishedCommandBatches.empty())
1204     {
1205         ANGLE_TRY(retireFinishedCommandsAndCleanupGarbage(context));
1206     }
1207 
1208     return angle::Result::Continue;
1209 }
1210 
finishQueueSerial(Context * context,const QueueSerial & queueSerial,uint64_t timeout)1211 angle::Result CommandQueue::finishQueueSerial(Context *context,
1212                                               const QueueSerial &queueSerial,
1213                                               uint64_t timeout)
1214 {
1215     vk::ResourceUse use(queueSerial);
1216     return finishResourceUse(context, use, timeout);
1217 }
1218 
waitIdle(Context * context,uint64_t timeout)1219 angle::Result CommandQueue::waitIdle(Context *context, uint64_t timeout)
1220 {
1221     // Fill the local variable with lock
1222     vk::ResourceUse use;
1223     {
1224         std::lock_guard<std::mutex> lock(mMutex);
1225         if (mInFlightCommands.empty())
1226         {
1227             return angle::Result::Continue;
1228         }
1229         use.setQueueSerial(mInFlightCommands.back().queueSerial);
1230     }
1231 
1232     return finishResourceUse(context, use, timeout);
1233 }
1234 
waitForResourceUseToFinishWithUserTimeout(Context * context,const ResourceUse & use,uint64_t timeout,VkResult * result)1235 angle::Result CommandQueue::waitForResourceUseToFinishWithUserTimeout(Context *context,
1236                                                                       const ResourceUse &use,
1237                                                                       uint64_t timeout,
1238                                                                       VkResult *result)
1239 {
1240     // Serial is not yet submitted. This is undefined behaviour, so we can do anything.
1241     if (!hasResourceUseSubmitted(use))
1242     {
1243         WARN() << "Waiting on an unsubmitted serial.";
1244         *result = VK_TIMEOUT;
1245         return angle::Result::Continue;
1246     }
1247 
1248     VkDevice device      = context->getDevice();
1249     size_t finishedCount = 0;
1250     {
1251         std::unique_lock<std::mutex> lock(mMutex);
1252         while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
1253         {
1254             bool finished;
1255             ANGLE_TRY(checkOneCommandBatch(context, &finished));
1256             if (!finished)
1257             {
1258                 *result = mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock);
1259                 // Don't trigger an error on timeout.
1260                 if (*result == VK_TIMEOUT)
1261                 {
1262                     break;
1263                 }
1264                 else
1265                 {
1266                     ANGLE_VK_TRY(context, *result);
1267                 }
1268             }
1269         }
1270         // Do one more check in case more commands also finished.
1271         ANGLE_TRY(checkCompletedCommandsLocked(context));
1272         finishedCount = mFinishedCommandBatches.size();
1273     }
1274 
1275     if (finishedCount > 0)
1276     {
1277         ANGLE_TRY(retireFinishedCommandsAndCleanupGarbage(context));
1278     }
1279 
1280     return angle::Result::Continue;
1281 }
1282 
isBusy(RendererVk * renderer) const1283 bool CommandQueue::isBusy(RendererVk *renderer) const
1284 {
1285     // No lock is needed here since we are accessing atomic variables only.
1286     size_t maxIndex = renderer->getLargestQueueSerialIndexEverAllocated();
1287     for (SerialIndex i = 0; i <= maxIndex; ++i)
1288     {
1289         if (mLastSubmittedSerials[i] > mLastCompletedSerials[i])
1290         {
1291             return true;
1292         }
1293     }
1294     return false;
1295 }
1296 
flushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)1297 void CommandQueue::flushWaitSemaphores(ProtectionType protectionType,
1298                                        egl::ContextPriority priority,
1299                                        std::vector<VkSemaphore> &&waitSemaphores,
1300                                        std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
1301 {
1302     ASSERT(!waitSemaphores.empty());
1303     ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
1304     std::lock_guard<std::mutex> lock(mMutex);
1305 
1306     CommandsState &state = mCommandsStateMap[priority][protectionType];
1307 
1308     state.waitSemaphores.insert(state.waitSemaphores.end(), waitSemaphores.begin(),
1309                                 waitSemaphores.end());
1310     state.waitSemaphoreStageMasks.insert(state.waitSemaphoreStageMasks.end(),
1311                                          waitSemaphoreStageMasks.begin(),
1312                                          waitSemaphoreStageMasks.end());
1313 
1314     waitSemaphores.clear();
1315     waitSemaphoreStageMasks.clear();
1316 }
1317 
flushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)1318 angle::Result CommandQueue::flushOutsideRPCommands(
1319     Context *context,
1320     ProtectionType protectionType,
1321     egl::ContextPriority priority,
1322     OutsideRenderPassCommandBufferHelper **outsideRPCommands)
1323 {
1324     std::lock_guard<std::mutex> lock(mMutex);
1325     ANGLE_TRY(ensurePrimaryCommandBufferValid(context, protectionType, priority));
1326     CommandsState &state = mCommandsStateMap[priority][protectionType];
1327     return (*outsideRPCommands)->flushToPrimary(context, &state);
1328 }
1329 
flushRenderPassCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,const RenderPass & renderPass,RenderPassCommandBufferHelper ** renderPassCommands)1330 angle::Result CommandQueue::flushRenderPassCommands(
1331     Context *context,
1332     ProtectionType protectionType,
1333     egl::ContextPriority priority,
1334     const RenderPass &renderPass,
1335     RenderPassCommandBufferHelper **renderPassCommands)
1336 {
1337     std::lock_guard<std::mutex> lock(mMutex);
1338     ANGLE_TRY(ensurePrimaryCommandBufferValid(context, protectionType, priority));
1339     CommandsState &state = mCommandsStateMap[priority][protectionType];
1340     return (*renderPassCommands)->flushToPrimary(context, &state, &renderPass);
1341 }
1342 
submitCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,const QueueSerial & submitQueueSerial)1343 angle::Result CommandQueue::submitCommands(Context *context,
1344                                            ProtectionType protectionType,
1345                                            egl::ContextPriority priority,
1346                                            VkSemaphore signalSemaphore,
1347                                            SharedExternalFence &&externalFence,
1348                                            const QueueSerial &submitQueueSerial)
1349 {
1350     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitCommands");
1351     std::unique_lock<std::mutex> lock(mMutex);
1352     RendererVk *renderer = context->getRenderer();
1353     VkDevice device      = renderer->getDevice();
1354 
1355     ++mPerfCounters.commandQueueSubmitCallsTotal;
1356     ++mPerfCounters.commandQueueSubmitCallsPerFrame;
1357 
1358     DeviceScoped<CommandBatch> scopedBatch(device);
1359     CommandBatch &batch = scopedBatch.get();
1360 
1361     batch.queueSerial    = submitQueueSerial;
1362     batch.protectionType = protectionType;
1363 
1364     CommandsState &state = mCommandsStateMap[priority][protectionType];
1365     // Store the primary CommandBuffer in the in-flight list.
1366     batch.primaryCommands = std::move(state.primaryCommands);
1367 
1368     // Store secondary Command Buffers.
1369     batch.secondaryCommands = std::move(state.secondaryCommands);
1370     ASSERT(batch.primaryCommands.valid() || batch.secondaryCommands.empty());
1371 
1372     // Move to local copy of vectors since queueSubmit will release the lock.
1373     std::vector<VkSemaphore> waitSemaphores = std::move(state.waitSemaphores);
1374     std::vector<VkPipelineStageFlags> waitSemaphoreStageMasks =
1375         std::move(state.waitSemaphoreStageMasks);
1376 
1377     mPerfCounters.commandQueueWaitSemaphoresTotal += waitSemaphores.size();
1378 
1379     // Don't make a submission if there is nothing to submit.
1380     const bool needsQueueSubmit = batch.primaryCommands.valid() ||
1381                                   signalSemaphore != VK_NULL_HANDLE || externalFence ||
1382                                   !waitSemaphores.empty();
1383     VkSubmitInfo submitInfo                   = {};
1384     VkProtectedSubmitInfo protectedSubmitInfo = {};
1385 
1386     if (needsQueueSubmit)
1387     {
1388         if (batch.primaryCommands.valid())
1389         {
1390             ANGLE_VK_TRY(context, batch.primaryCommands.end());
1391         }
1392 
1393         InitializeSubmitInfo(&submitInfo, batch.primaryCommands, waitSemaphores,
1394                              waitSemaphoreStageMasks, signalSemaphore);
1395 
1396         // No need protected submission if no commands to submit.
1397         if (protectionType == ProtectionType::Protected && batch.primaryCommands.valid())
1398         {
1399             protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1400             protectedSubmitInfo.pNext           = nullptr;
1401             protectedSubmitInfo.protectedSubmit = true;
1402             submitInfo.pNext                    = &protectedSubmitInfo;
1403         }
1404 
1405         if (!externalFence)
1406         {
1407             ANGLE_VK_TRY(context, batch.fence.init(context->getDevice(), &mFenceRecycler));
1408         }
1409         else
1410         {
1411             batch.externalFence = std::move(externalFence);
1412         }
1413 
1414         ++mPerfCounters.vkQueueSubmitCallsTotal;
1415         ++mPerfCounters.vkQueueSubmitCallsPerFrame;
1416     }
1417 
1418     // Note queueSubmit will release the lock.
1419     ANGLE_TRY(queueSubmit(context, std::move(lock), priority, submitInfo, scopedBatch,
1420                           submitQueueSerial));
1421 
1422     // Clear local vector without lock.
1423     waitSemaphores.clear();
1424     waitSemaphoreStageMasks.clear();
1425 
1426     return angle::Result::Continue;
1427 }
1428 
queueSubmitOneOff(Context * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,SubmitPolicy submitPolicy,const QueueSerial & submitQueueSerial)1429 angle::Result CommandQueue::queueSubmitOneOff(Context *context,
1430                                               ProtectionType protectionType,
1431                                               egl::ContextPriority contextPriority,
1432                                               VkCommandBuffer commandBufferHandle,
1433                                               VkSemaphore waitSemaphore,
1434                                               VkPipelineStageFlags waitSemaphoreStageMask,
1435                                               SubmitPolicy submitPolicy,
1436                                               const QueueSerial &submitQueueSerial)
1437 {
1438     std::unique_lock<std::mutex> lock(mMutex);
1439     DeviceScoped<CommandBatch> scopedBatch(context->getDevice());
1440     CommandBatch &batch  = scopedBatch.get();
1441     batch.queueSerial    = submitQueueSerial;
1442     batch.protectionType = protectionType;
1443 
1444     ANGLE_VK_TRY(context, batch.fence.init(context->getDevice(), &mFenceRecycler));
1445 
1446     VkSubmitInfo submitInfo = {};
1447     submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1448 
1449     VkProtectedSubmitInfo protectedSubmitInfo = {};
1450     ASSERT(protectionType == ProtectionType::Unprotected ||
1451            protectionType == ProtectionType::Protected);
1452     if (protectionType == ProtectionType::Protected)
1453     {
1454         protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1455         protectedSubmitInfo.pNext           = nullptr;
1456         protectedSubmitInfo.protectedSubmit = true;
1457         submitInfo.pNext                    = &protectedSubmitInfo;
1458     }
1459 
1460     if (commandBufferHandle != VK_NULL_HANDLE)
1461     {
1462         submitInfo.commandBufferCount = 1;
1463         submitInfo.pCommandBuffers    = &commandBufferHandle;
1464     }
1465 
1466     if (waitSemaphore != VK_NULL_HANDLE)
1467     {
1468         submitInfo.waitSemaphoreCount = 1;
1469         submitInfo.pWaitSemaphores    = &waitSemaphore;
1470         submitInfo.pWaitDstStageMask  = &waitSemaphoreStageMask;
1471     }
1472 
1473     ++mPerfCounters.vkQueueSubmitCallsTotal;
1474     ++mPerfCounters.vkQueueSubmitCallsPerFrame;
1475 
1476     // Note queueSubmit will release the lock.
1477     return queueSubmit(context, std::move(lock), contextPriority, submitInfo, scopedBatch,
1478                        submitQueueSerial);
1479 }
1480 
queueSubmit(Context * context,std::unique_lock<std::mutex> && dequeueLock,egl::ContextPriority contextPriority,const VkSubmitInfo & submitInfo,DeviceScoped<CommandBatch> & commandBatch,const QueueSerial & submitQueueSerial)1481 angle::Result CommandQueue::queueSubmit(Context *context,
1482                                         std::unique_lock<std::mutex> &&dequeueLock,
1483                                         egl::ContextPriority contextPriority,
1484                                         const VkSubmitInfo &submitInfo,
1485                                         DeviceScoped<CommandBatch> &commandBatch,
1486                                         const QueueSerial &submitQueueSerial)
1487 {
1488     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::queueSubmit");
1489     RendererVk *renderer = context->getRenderer();
1490 
1491     // Lock relay to ensure the ordering of submission strictly follow the context's submission
1492     // order. This lock relay (first take mMutex and then mQueueSubmitMutex, and then release
1493     // mMutex) ensures we always have a lock covering the entire call which ensures the strict
1494     // submission order.
1495     std::lock_guard<std::mutex> queueSubmitLock(mQueueSubmitMutex);
1496     // CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
1497     // off-screen scenarios.
1498     if (mInFlightCommands.full())
1499     {
1500         ANGLE_TRY(finishOneCommandBatchAndCleanupImpl(context, renderer->getMaxFenceWaitTimeNs()));
1501     }
1502     // Release the dequeue lock while doing potentially lengthy vkQueueSubmit call.
1503     // Note: after this point, you can not reference anything that required mMutex lock.
1504     dequeueLock.unlock();
1505 
1506     if (submitInfo.sType == VK_STRUCTURE_TYPE_SUBMIT_INFO)
1507     {
1508         CommandBatch &batch = commandBatch.get();
1509 
1510         VkQueue queue = getQueue(contextPriority);
1511         VkFence fence = batch.getFenceHandle();
1512         ASSERT(fence != VK_NULL_HANDLE);
1513         ANGLE_VK_TRY(context, vkQueueSubmit(queue, 1, &submitInfo, fence));
1514 
1515         if (batch.externalFence)
1516         {
1517             // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
1518             // obeys copy semantics. This means that the fence must already be signaled or the work
1519             // to signal it is in the graphics pipeline at the time we export the fd.
1520             // In other words, must call exportFd() after successful vkQueueSubmit() call.
1521             ExternalFence &externalFence       = *batch.externalFence;
1522             VkFenceGetFdInfoKHR fenceGetFdInfo = {};
1523             fenceGetFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
1524             fenceGetFdInfo.fence               = externalFence.getHandle();
1525             fenceGetFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
1526             externalFence.exportFd(renderer->getDevice(), fenceGetFdInfo);
1527         }
1528     }
1529 
1530     mInFlightCommands.push(commandBatch.release());
1531 
1532     // This must set last so that when this submission appears submitted, it actually already
1533     // submitted and enqueued to mInFlightCommands.
1534     mLastSubmittedSerials.setQueueSerial(submitQueueSerial);
1535     return angle::Result::Continue;
1536 }
1537 
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)1538 void CommandQueue::queuePresent(egl::ContextPriority contextPriority,
1539                                 const VkPresentInfoKHR &presentInfo,
1540                                 SwapchainStatus *swapchainStatus)
1541 {
1542     std::lock_guard<std::mutex> queueSubmitLock(mQueueSubmitMutex);
1543     VkQueue queue                      = getQueue(contextPriority);
1544     swapchainStatus->lastPresentResult = vkQueuePresentKHR(queue, &presentInfo);
1545 }
1546 
getPerfCounters() const1547 const angle::VulkanPerfCounters CommandQueue::getPerfCounters() const
1548 {
1549     std::lock_guard<std::mutex> lock(mMutex);
1550     return mPerfCounters;
1551 }
1552 
resetPerFramePerfCounters()1553 void CommandQueue::resetPerFramePerfCounters()
1554 {
1555     std::lock_guard<std::mutex> lock(mMutex);
1556     mPerfCounters.commandQueueSubmitCallsPerFrame = 0;
1557     mPerfCounters.vkQueueSubmitCallsPerFrame      = 0;
1558 }
1559 
retireFinishedCommandsAndCleanupGarbage(Context * context)1560 angle::Result CommandQueue::retireFinishedCommandsAndCleanupGarbage(Context *context)
1561 {
1562     RendererVk *renderer = context->getRenderer();
1563     if (!renderer->isAsyncCommandBufferResetEnabled())
1564     {
1565         // Do immediate command buffer reset
1566         ANGLE_TRY(retireFinishedCommands(context));
1567     }
1568 
1569     renderer->requestAsyncCommandsAndGarbageCleanup(context);
1570 
1571     return angle::Result::Continue;
1572 }
1573 
1574 // CommandQueue private API implementation. These are called by public API, so lock already held.
checkOneCommandBatch(Context * context,bool * finished)1575 angle::Result CommandQueue::checkOneCommandBatch(Context *context, bool *finished)
1576 {
1577     ASSERT(!mInFlightCommands.empty());
1578 
1579     CommandBatch &batch = mInFlightCommands.front();
1580     *finished           = false;
1581     if (batch.hasFence())
1582     {
1583         VkResult status = batch.getFenceStatus(context->getDevice());
1584         if (status == VK_NOT_READY)
1585         {
1586             return angle::Result::Continue;
1587         }
1588         ANGLE_VK_TRY(context, status);
1589     }
1590 
1591     // Finished.
1592     mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1593 
1594     // Move command batch to mFinishedCommandBatches.
1595     if (mFinishedCommandBatches.full())
1596     {
1597         ANGLE_TRY(retireFinishedCommandsLocked(context));
1598     }
1599     mFinishedCommandBatches.push(std::move(batch));
1600     mInFlightCommands.pop();
1601     *finished = true;
1602 
1603     return angle::Result::Continue;
1604 }
1605 
finishOneCommandBatchAndCleanup(Context * context,uint64_t timeout,bool * anyFinished)1606 angle::Result CommandQueue::finishOneCommandBatchAndCleanup(Context *context,
1607                                                             uint64_t timeout,
1608                                                             bool *anyFinished)
1609 {
1610     std::lock_guard<std::mutex> lock(mMutex);
1611 
1612     // If there are in-flight submissions in the queue, they can be finished.
1613     *anyFinished = false;
1614     if (!mInFlightCommands.empty())
1615     {
1616         ANGLE_TRY(finishOneCommandBatchAndCleanupImpl(context, timeout));
1617         *anyFinished = true;
1618     }
1619     return angle::Result::Continue;
1620 }
1621 
finishOneCommandBatchAndCleanupImpl(Context * context,uint64_t timeout)1622 angle::Result CommandQueue::finishOneCommandBatchAndCleanupImpl(Context *context, uint64_t timeout)
1623 {
1624     ASSERT(!mInFlightCommands.empty());
1625     CommandBatch &batch = mInFlightCommands.front();
1626     if (batch.hasFence())
1627     {
1628         VkResult status = batch.waitFence(context->getDevice(), timeout);
1629         ANGLE_VK_TRY(context, status);
1630     }
1631 
1632     mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1633     // Move command batch to mFinishedCommandBatches.
1634     if (mFinishedCommandBatches.full())
1635     {
1636         ANGLE_TRY(retireFinishedCommandsLocked(context));
1637     }
1638     mFinishedCommandBatches.push(std::move(batch));
1639     mInFlightCommands.pop();
1640 
1641     // Immediately clean up finished batches.
1642     ANGLE_TRY(retireFinishedCommandsLocked(context));
1643     context->getRenderer()->cleanupGarbage();
1644 
1645     return angle::Result::Continue;
1646 }
1647 
retireFinishedCommandsLocked(Context * context)1648 angle::Result CommandQueue::retireFinishedCommandsLocked(Context *context)
1649 {
1650     ANGLE_TRACE_EVENT0("gpu.angle", "retireFinishedCommandsLocked");
1651 
1652     while (!mFinishedCommandBatches.empty())
1653     {
1654         CommandBatch &batch = mFinishedCommandBatches.front();
1655         ASSERT(batch.queueSerial <= mLastCompletedSerials);
1656 
1657         batch.releaseFence();
1658 
1659         if (batch.primaryCommands.valid())
1660         {
1661             PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[batch.protectionType];
1662             ANGLE_TRY(commandPool.collect(context, std::move(batch.primaryCommands)));
1663         }
1664 
1665         batch.secondaryCommands.retireCommandBuffers();
1666 
1667         mFinishedCommandBatches.pop();
1668     }
1669 
1670     return angle::Result::Continue;
1671 }
1672 
checkCompletedCommandsLocked(Context * context)1673 angle::Result CommandQueue::checkCompletedCommandsLocked(Context *context)
1674 {
1675     while (!mInFlightCommands.empty())
1676     {
1677         bool finished;
1678         ANGLE_TRY(checkOneCommandBatch(context, &finished));
1679         if (!finished)
1680         {
1681             break;
1682         }
1683     }
1684     return angle::Result::Continue;
1685 }
1686 
ensurePrimaryCommandBufferValid(Context * context,ProtectionType protectionType,egl::ContextPriority priority)1687 angle::Result CommandQueue::ensurePrimaryCommandBufferValid(Context *context,
1688                                                             ProtectionType protectionType,
1689                                                             egl::ContextPriority priority)
1690 {
1691     CommandsState &state = mCommandsStateMap[priority][protectionType];
1692 
1693     if (state.primaryCommands.valid())
1694     {
1695         return angle::Result::Continue;
1696     }
1697 
1698     ANGLE_TRY(mPrimaryCommandPoolMap[protectionType].allocate(context, &state.primaryCommands));
1699     VkCommandBufferBeginInfo beginInfo = {};
1700     beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1701     beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1702     beginInfo.pInheritanceInfo         = nullptr;
1703     ANGLE_VK_TRY(context, state.primaryCommands.begin(beginInfo));
1704 
1705     return angle::Result::Continue;
1706 }
1707 
1708 // QueuePriorities:
1709 constexpr float kVulkanQueuePriorityLow    = 0.0;
1710 constexpr float kVulkanQueuePriorityMedium = 0.4;
1711 constexpr float kVulkanQueuePriorityHigh   = 1.0;
1712 
1713 const float QueueFamily::kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] =
1714     {kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
1715 
getDevicePriority(egl::ContextPriority priority) const1716 egl::ContextPriority DeviceQueueMap::getDevicePriority(egl::ContextPriority priority) const
1717 {
1718     return mPriorities[priority];
1719 }
1720 
~DeviceQueueMap()1721 DeviceQueueMap::~DeviceQueueMap() {}
1722 
operator =(const DeviceQueueMap & other)1723 DeviceQueueMap &DeviceQueueMap::operator=(const DeviceQueueMap &other)
1724 {
1725     ASSERT(this != &other);
1726     if ((this != &other) && other.valid())
1727     {
1728         mIndex                                    = other.mIndex;
1729         mIsProtected                              = other.mIsProtected;
1730         mPriorities[egl::ContextPriority::Low]    = other.mPriorities[egl::ContextPriority::Low];
1731         mPriorities[egl::ContextPriority::Medium] = other.mPriorities[egl::ContextPriority::Medium];
1732         mPriorities[egl::ContextPriority::High]   = other.mPriorities[egl::ContextPriority::High];
1733         *static_cast<angle::PackedEnumMap<egl::ContextPriority, VkQueue> *>(this) = other;
1734     }
1735     return *this;
1736 }
1737 
getDeviceQueue(VkDevice device,bool makeProtected,uint32_t queueIndex,VkQueue * queue)1738 void QueueFamily::getDeviceQueue(VkDevice device,
1739                                  bool makeProtected,
1740                                  uint32_t queueIndex,
1741                                  VkQueue *queue)
1742 {
1743     if (makeProtected)
1744     {
1745         VkDeviceQueueInfo2 queueInfo2 = {};
1746         queueInfo2.sType              = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
1747         queueInfo2.flags              = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
1748         queueInfo2.queueFamilyIndex   = mIndex;
1749         queueInfo2.queueIndex         = queueIndex;
1750 
1751         vkGetDeviceQueue2(device, &queueInfo2, queue);
1752     }
1753     else
1754     {
1755         vkGetDeviceQueue(device, mIndex, queueIndex, queue);
1756     }
1757 }
1758 
initializeQueueMap(VkDevice device,bool makeProtected,uint32_t queueIndex,uint32_t queueCount)1759 DeviceQueueMap QueueFamily::initializeQueueMap(VkDevice device,
1760                                                bool makeProtected,
1761                                                uint32_t queueIndex,
1762                                                uint32_t queueCount)
1763 {
1764     // QueueIndexing:
1765     constexpr uint32_t kQueueIndexMedium = 0;
1766     constexpr uint32_t kQueueIndexHigh   = 1;
1767     constexpr uint32_t kQueueIndexLow    = 2;
1768 
1769     ASSERT(queueCount);
1770     ASSERT((queueIndex + queueCount) <= mProperties.queueCount);
1771     DeviceQueueMap queueMap(mIndex, makeProtected);
1772 
1773     getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexMedium,
1774                    &queueMap[egl::ContextPriority::Medium]);
1775     queueMap.mPriorities[egl::ContextPriority::Medium] = egl::ContextPriority::Medium;
1776 
1777     // If at least 2 queues, High has its own queue
1778     if (queueCount > 1)
1779     {
1780         getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexHigh,
1781                        &queueMap[egl::ContextPriority::High]);
1782         queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::High;
1783     }
1784     else
1785     {
1786         queueMap[egl::ContextPriority::High]             = queueMap[egl::ContextPriority::Medium];
1787         queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::Medium;
1788     }
1789     // If at least 3 queues, Low has its own queue. Adjust Low priority.
1790     if (queueCount > 2)
1791     {
1792         getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexLow,
1793                        &queueMap[egl::ContextPriority::Low]);
1794         queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Low;
1795     }
1796     else
1797     {
1798         queueMap[egl::ContextPriority::Low]             = queueMap[egl::ContextPriority::Medium];
1799         queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Medium;
1800     }
1801     return queueMap;
1802 }
1803 
initialize(const VkQueueFamilyProperties & queueFamilyProperties,uint32_t index)1804 void QueueFamily::initialize(const VkQueueFamilyProperties &queueFamilyProperties, uint32_t index)
1805 {
1806     mProperties = queueFamilyProperties;
1807     mIndex      = index;
1808 }
1809 
FindIndex(const std::vector<VkQueueFamilyProperties> & queueFamilyProperties,VkQueueFlags flags,int32_t matchNumber,uint32_t * matchCount)1810 uint32_t QueueFamily::FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties,
1811                                 VkQueueFlags flags,
1812                                 int32_t matchNumber,
1813                                 uint32_t *matchCount)
1814 {
1815     uint32_t index = QueueFamily::kInvalidIndex;
1816     uint32_t count = 0;
1817 
1818     for (uint32_t familyIndex = 0; familyIndex < queueFamilyProperties.size(); ++familyIndex)
1819     {
1820         const auto &queueInfo = queueFamilyProperties[familyIndex];
1821         if ((queueInfo.queueFlags & flags) == flags)
1822         {
1823             ASSERT(queueInfo.queueCount > 0);
1824             count++;
1825             if ((index == QueueFamily::kInvalidIndex) && (matchNumber-- == 0))
1826             {
1827                 index = familyIndex;
1828             }
1829         }
1830     }
1831     if (matchCount)
1832     {
1833         *matchCount = count;
1834     }
1835 
1836     return index;
1837 }
1838 
1839 }  // namespace vk
1840 }  // namespace rx
1841