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