• 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 // CommandQueue.cpp:
7 //    Implements the class methods for CommandQueue.
8 //
9 
10 #include "libANGLE/renderer/vulkan/CommandQueue.h"
11 #include <algorithm>
12 #include "common/system_utils.h"
13 #include "libANGLE/renderer/vulkan/SyncVk.h"
14 #include "libANGLE/renderer/vulkan/vk_renderer.h"
15 #include "vulkan/vulkan_core.h"
16 
17 namespace rx
18 {
19 namespace vk
20 {
21 namespace
22 {
23 constexpr bool kOutputVmaStatsString = false;
24 // When suballocation garbages is more than this, we may wait for GPU to finish and free up some
25 // memory for allocation.
26 constexpr VkDeviceSize kMaxBufferSuballocationGarbageSize = 64 * 1024 * 1024;
27 
InitializeSubmitInfo(VkSubmitInfo * submitInfo,const PrimaryCommandBuffer & commandBuffer,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const VkSemaphore & signalSemaphore)28 void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
29                           const PrimaryCommandBuffer &commandBuffer,
30                           const std::vector<VkSemaphore> &waitSemaphores,
31                           const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
32                           const VkSemaphore &signalSemaphore)
33 {
34     // Verify that the submitInfo has been zero'd out.
35     ASSERT(submitInfo->signalSemaphoreCount == 0);
36     ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
37     submitInfo->sType              = VK_STRUCTURE_TYPE_SUBMIT_INFO;
38     submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
39     submitInfo->pCommandBuffers    = commandBuffer.ptr();
40     submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
41     submitInfo->pWaitSemaphores    = waitSemaphores.empty() ? nullptr : waitSemaphores.data();
42     submitInfo->pWaitDstStageMask  = waitSemaphoreStageMasks.data();
43 
44     if (signalSemaphore != VK_NULL_HANDLE)
45     {
46         submitInfo->signalSemaphoreCount = 1;
47         submitInfo->pSignalSemaphores    = &signalSemaphore;
48     }
49 }
50 
GetDeviceQueue(VkDevice device,bool makeProtected,uint32_t queueFamilyIndex,uint32_t queueIndex,VkQueue * queue)51 void GetDeviceQueue(VkDevice device,
52                     bool makeProtected,
53                     uint32_t queueFamilyIndex,
54                     uint32_t queueIndex,
55                     VkQueue *queue)
56 {
57     if (makeProtected)
58     {
59         VkDeviceQueueInfo2 queueInfo2 = {};
60         queueInfo2.sType              = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
61         queueInfo2.flags              = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
62         queueInfo2.queueFamilyIndex   = queueFamilyIndex;
63         queueInfo2.queueIndex         = queueIndex;
64 
65         vkGetDeviceQueue2(device, &queueInfo2, queue);
66     }
67     else
68     {
69         vkGetDeviceQueue(device, queueFamilyIndex, queueIndex, queue);
70     }
71 }
72 }  // namespace
73 
74 // RecyclableFence implementation
RecyclableFence()75 RecyclableFence::RecyclableFence() : mRecycler(nullptr) {}
76 
~RecyclableFence()77 RecyclableFence::~RecyclableFence()
78 {
79     ASSERT(!valid());
80 }
81 
init(VkDevice device,FenceRecycler * recycler)82 VkResult RecyclableFence::init(VkDevice device, FenceRecycler *recycler)
83 {
84     ASSERT(!valid());
85     ASSERT(mRecycler == nullptr);
86 
87     // First try to fetch from recycler. If that failed, try to create a new VkFence
88     recycler->fetch(device, &mFence);
89     if (!valid())
90     {
91         VkFenceCreateInfo fenceCreateInfo = {};
92         fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
93         fenceCreateInfo.flags             = 0;
94         VkResult result                   = mFence.init(device, fenceCreateInfo);
95         if (result != VK_SUCCESS)
96         {
97             ASSERT(!valid());
98             return result;
99         }
100         ASSERT(valid());
101     }
102 
103     mRecycler = recycler;
104 
105     return VK_SUCCESS;
106 }
107 
destroy(VkDevice device)108 void RecyclableFence::destroy(VkDevice device)
109 {
110     if (valid())
111     {
112         if (mRecycler != nullptr)
113         {
114             mRecycler->recycle(std::move(mFence));
115         }
116         else
117         {
118             // Recycler was detached - destroy the fence.
119             mFence.destroy(device);
120         }
121         ASSERT(!valid());
122     }
123 }
124 
125 // FenceRecycler implementation
destroy(ErrorContext * context)126 void FenceRecycler::destroy(ErrorContext *context)
127 {
128     std::lock_guard<angle::SimpleMutex> lock(mMutex);
129     mRecycler.destroy(context->getDevice());
130 }
131 
fetch(VkDevice device,Fence * fenceOut)132 void FenceRecycler::fetch(VkDevice device, Fence *fenceOut)
133 {
134     ASSERT(fenceOut != nullptr && !fenceOut->valid());
135     std::lock_guard<angle::SimpleMutex> lock(mMutex);
136     if (!mRecycler.empty())
137     {
138         mRecycler.fetch(fenceOut);
139         fenceOut->reset(device);
140     }
141 }
142 
recycle(Fence && fence)143 void FenceRecycler::recycle(Fence &&fence)
144 {
145     std::lock_guard<angle::SimpleMutex> lock(mMutex);
146     mRecycler.recycle(std::move(fence));
147 }
148 
149 // CommandBatch implementation.
CommandBatch()150 CommandBatch::CommandBatch()
151     : mProtectionType(ProtectionType::InvalidEnum), mCommandPoolAccess(nullptr)
152 {}
153 
154 CommandBatch::~CommandBatch() = default;
155 
CommandBatch(CommandBatch && other)156 CommandBatch::CommandBatch(CommandBatch &&other) : CommandBatch()
157 {
158     *this = std::move(other);
159 }
160 
operator =(CommandBatch && other)161 CommandBatch &CommandBatch::operator=(CommandBatch &&other)
162 {
163     std::swap(mQueueSerial, other.mQueueSerial);
164     std::swap(mProtectionType, other.mProtectionType);
165     std::swap(mPrimaryCommands, other.mPrimaryCommands);
166     std::swap(mCommandPoolAccess, other.mCommandPoolAccess);
167     std::swap(mSecondaryCommands, other.mSecondaryCommands);
168     std::swap(mFence, other.mFence);
169     std::swap(mExternalFence, other.mExternalFence);
170     return *this;
171 }
172 
destroy(VkDevice device)173 void CommandBatch::destroy(VkDevice device)
174 {
175     if (mPrimaryCommands.valid())
176     {
177         ASSERT(mCommandPoolAccess != nullptr);
178         mCommandPoolAccess->destroyPrimaryCommandBuffer(device, &mPrimaryCommands);
179     }
180     mSecondaryCommands.releaseCommandBuffers();
181     if (mFence)
182     {
183         mFence->detachRecycler();
184         mFence.reset();
185     }
186     mExternalFence.reset();
187     // Do not clean other members to catch invalid reuse attempt with ASSERTs.
188 }
189 
release(ErrorContext * context)190 angle::Result CommandBatch::release(ErrorContext *context)
191 {
192     if (mPrimaryCommands.valid())
193     {
194         ASSERT(mCommandPoolAccess != nullptr);
195         ANGLE_TRY(mCommandPoolAccess->collectPrimaryCommandBuffer(context, mProtectionType,
196                                                                   &mPrimaryCommands));
197     }
198     mSecondaryCommands.releaseCommandBuffers();
199     mFence.reset();
200     mExternalFence.reset();
201     // Do not clean other members to catch invalid reuse attempt with ASSERTs.
202     return angle::Result::Continue;
203 }
204 
setQueueSerial(const QueueSerial & serial)205 void CommandBatch::setQueueSerial(const QueueSerial &serial)
206 {
207     ASSERT(serial.valid());
208     ASSERT(!mQueueSerial.valid());
209     mQueueSerial = serial;
210 }
211 
setProtectionType(ProtectionType protectionType)212 void CommandBatch::setProtectionType(ProtectionType protectionType)
213 {
214     ASSERT(protectionType != ProtectionType::InvalidEnum);
215     ASSERT(mProtectionType == ProtectionType::InvalidEnum);
216     mProtectionType = protectionType;
217 }
218 
setPrimaryCommands(PrimaryCommandBuffer && primaryCommands,CommandPoolAccess * commandPoolAccess)219 void CommandBatch::setPrimaryCommands(PrimaryCommandBuffer &&primaryCommands,
220                                       CommandPoolAccess *commandPoolAccess)
221 {
222     // primaryCommands is optional.
223     ASSERT(!(primaryCommands.valid() && commandPoolAccess == nullptr));
224     ASSERT(!mPrimaryCommands.valid());
225     ASSERT(mCommandPoolAccess == nullptr);
226     mPrimaryCommands   = std::move(primaryCommands);
227     mCommandPoolAccess = commandPoolAccess;
228 }
229 
setSecondaryCommands(SecondaryCommandBufferCollector && secondaryCommands)230 void CommandBatch::setSecondaryCommands(SecondaryCommandBufferCollector &&secondaryCommands)
231 {
232     // secondaryCommands is optional.
233     ASSERT(mSecondaryCommands.empty());
234     mSecondaryCommands = std::move(secondaryCommands);
235 }
236 
initFence(VkDevice device,FenceRecycler * recycler)237 VkResult CommandBatch::initFence(VkDevice device, FenceRecycler *recycler)
238 {
239     ASSERT(!hasFence());
240     auto fence            = SharedFence::MakeShared(device);
241     const VkResult result = fence->init(device, recycler);
242     if (result == VK_SUCCESS)
243     {
244         ASSERT(fence->valid());
245         mFence = std::move(fence);
246     }
247     return result;
248 }
249 
setExternalFence(SharedExternalFence && externalFence)250 void CommandBatch::setExternalFence(SharedExternalFence &&externalFence)
251 {
252     ASSERT(!hasFence());
253     mExternalFence = std::move(externalFence);
254 }
255 
getQueueSerial() const256 const QueueSerial &CommandBatch::getQueueSerial() const
257 {
258     ASSERT(mQueueSerial.valid());
259     return mQueueSerial;
260 }
261 
getPrimaryCommands() const262 const PrimaryCommandBuffer &CommandBatch::getPrimaryCommands() const
263 {
264     return mPrimaryCommands;
265 }
266 
getExternalFence()267 const SharedExternalFence &CommandBatch::getExternalFence()
268 {
269     return mExternalFence;
270 }
271 
hasFence() const272 bool CommandBatch::hasFence() const
273 {
274     ASSERT(!mExternalFence || !mFence);
275     ASSERT(!mFence || mFence->valid());
276     return mFence || mExternalFence;
277 }
278 
getFenceHandle() const279 VkFence CommandBatch::getFenceHandle() const
280 {
281     ASSERT(hasFence());
282     return mFence ? mFence->get().getHandle() : mExternalFence->getHandle();
283 }
284 
getFenceStatus(VkDevice device) const285 VkResult CommandBatch::getFenceStatus(VkDevice device) const
286 {
287     ASSERT(hasFence());
288     return mFence ? mFence->get().getStatus(device) : mExternalFence->getStatus(device);
289 }
290 
waitFence(VkDevice device,uint64_t timeout) const291 VkResult CommandBatch::waitFence(VkDevice device, uint64_t timeout) const
292 {
293     ASSERT(hasFence());
294     return mFence ? mFence->get().wait(device, timeout) : mExternalFence->wait(device, timeout);
295 }
296 
waitFenceUnlocked(VkDevice device,uint64_t timeout,std::unique_lock<angle::SimpleMutex> * lock) const297 VkResult CommandBatch::waitFenceUnlocked(VkDevice device,
298                                          uint64_t timeout,
299                                          std::unique_lock<angle::SimpleMutex> *lock) const
300 {
301     ASSERT(hasFence());
302     VkResult status;
303     // You can only use the local copy of the fence without lock.
304     // Do not access "this" after unlock() because object might be deleted from other thread.
305     if (mFence)
306     {
307         const SharedFence localFenceToWaitOn = mFence;
308         lock->unlock();
309         status = localFenceToWaitOn->get().wait(device, timeout);
310         lock->lock();
311     }
312     else
313     {
314         const SharedExternalFence localFenceToWaitOn = mExternalFence;
315         lock->unlock();
316         status = localFenceToWaitOn->wait(device, timeout);
317         lock->lock();
318     }
319     return status;
320 }
321 
322 // CleanUpThread implementation.
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)323 void CleanUpThread::handleError(VkResult errorCode,
324                                 const char *file,
325                                 const char *function,
326                                 unsigned int line)
327 {
328     ASSERT(errorCode != VK_SUCCESS);
329 
330     std::stringstream errorStream;
331     errorStream << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode)
332                 << ".";
333 
334     if (errorCode == VK_ERROR_DEVICE_LOST)
335     {
336         WARN() << errorStream.str();
337         mCommandQueue->handleDeviceLost(mRenderer);
338     }
339 
340     std::lock_guard<angle::SimpleMutex> queueLock(mErrorMutex);
341     Error error = {errorCode, file, function, line};
342     mErrors.emplace(error);
343 }
344 
CleanUpThread(Renderer * renderer,CommandQueue * commandQueue)345 CleanUpThread::CleanUpThread(Renderer *renderer, CommandQueue *commandQueue)
346     : ErrorContext(renderer),
347       mCommandQueue(commandQueue),
348       mTaskThreadShouldExit(false),
349       mNeedCleanUp(false)
350 {}
351 
352 CleanUpThread::~CleanUpThread() = default;
353 
checkAndPopPendingError(ErrorContext * errorHandlingContext)354 angle::Result CleanUpThread::checkAndPopPendingError(ErrorContext *errorHandlingContext)
355 {
356     std::lock_guard<angle::SimpleMutex> queueLock(mErrorMutex);
357     if (mErrors.empty())
358     {
359         return angle::Result::Continue;
360     }
361 
362     while (!mErrors.empty())
363     {
364         Error err = mErrors.front();
365         mErrors.pop();
366         errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
367     }
368     return angle::Result::Stop;
369 }
370 
requestCleanUp()371 void CleanUpThread::requestCleanUp()
372 {
373     if (!mNeedCleanUp.exchange(true))
374     {
375         // request clean up in async thread
376         std::unique_lock<std::mutex> enqueueLock(mMutex);
377         mWorkAvailableCondition.notify_one();
378     }
379 }
380 
processTasks()381 void CleanUpThread::processTasks()
382 {
383     angle::SetCurrentThreadName("ANGLE-GC");
384 
385     while (true)
386     {
387         bool exitThread = false;
388         (void)processTasksImpl(&exitThread);
389         if (exitThread)
390         {
391             // We are doing a controlled exit of the thread, break out of the while loop.
392             break;
393         }
394     }
395 }
396 
processTasksImpl(bool * exitThread)397 angle::Result CleanUpThread::processTasksImpl(bool *exitThread)
398 {
399     while (true)
400     {
401         std::unique_lock<std::mutex> lock(mMutex);
402         mWorkAvailableCondition.wait(lock,
403                                      [this] { return mTaskThreadShouldExit || mNeedCleanUp; });
404 
405         if (mTaskThreadShouldExit)
406         {
407             break;
408         }
409         lock.unlock();
410 
411         if (mNeedCleanUp.exchange(false))
412         {
413             // Always check completed commands again in case anything new has been finished.
414             ANGLE_TRY(mCommandQueue->checkCompletedCommands(this));
415 
416             // Reset command buffer and clean up garbage
417             if (mRenderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled() &&
418                 mCommandQueue->hasFinishedCommands())
419             {
420                 ANGLE_TRY(mCommandQueue->releaseFinishedCommands(this));
421             }
422             mRenderer->cleanupGarbage(nullptr);
423         }
424     }
425     *exitThread = true;
426     return angle::Result::Continue;
427 }
428 
init()429 angle::Result CleanUpThread::init()
430 {
431     mTaskThread = std::thread(&CleanUpThread::processTasks, this);
432 
433     return angle::Result::Continue;
434 }
435 
destroy(ErrorContext * context)436 void CleanUpThread::destroy(ErrorContext *context)
437 {
438     {
439         // Request to terminate the worker thread
440         std::lock_guard<std::mutex> lock(mMutex);
441         mTaskThreadShouldExit = true;
442         mNeedCleanUp          = false;
443         mWorkAvailableCondition.notify_one();
444     }
445 
446     // Perform any lingering clean up right away.
447     if (mRenderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled())
448     {
449         (void)mCommandQueue->releaseFinishedCommands(context);
450         mRenderer->cleanupGarbage(nullptr);
451     }
452 
453     if (mTaskThread.joinable())
454     {
455         mTaskThread.join();
456     }
457 }
458 
459 CommandPoolAccess::CommandPoolAccess()  = default;
460 CommandPoolAccess::~CommandPoolAccess() = default;
461 
462 // CommandPoolAccess public API implementation. These must be thread safe and never called from
463 // CommandPoolAccess class itself.
initCommandPool(ErrorContext * context,ProtectionType protectionType,const uint32_t queueFamilyIndex)464 angle::Result CommandPoolAccess::initCommandPool(ErrorContext *context,
465                                                  ProtectionType protectionType,
466                                                  const uint32_t queueFamilyIndex)
467 {
468     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
469     PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[protectionType];
470     return commandPool.init(context, protectionType, queueFamilyIndex);
471 }
472 
destroy(VkDevice device)473 void CommandPoolAccess::destroy(VkDevice device)
474 {
475     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
476     for (auto &protectionMap : mCommandsStateMap)
477     {
478         for (CommandsState &state : protectionMap)
479         {
480             state.waitSemaphores.clear();
481             state.waitSemaphoreStageMasks.clear();
482             state.primaryCommands.destroy(device);
483             state.secondaryCommands.releaseCommandBuffers();
484         }
485     }
486 
487     for (PersistentCommandPool &commandPool : mPrimaryCommandPoolMap)
488     {
489         commandPool.destroy(device);
490     }
491 }
492 
destroyPrimaryCommandBuffer(VkDevice device,PrimaryCommandBuffer * primaryCommands) const493 void CommandPoolAccess::destroyPrimaryCommandBuffer(VkDevice device,
494                                                     PrimaryCommandBuffer *primaryCommands) const
495 {
496     ASSERT(primaryCommands->valid());
497 
498     // Does not require a pool mutex lock.
499     primaryCommands->destroy(device);
500 }
501 
collectPrimaryCommandBuffer(ErrorContext * context,const ProtectionType protectionType,PrimaryCommandBuffer * primaryCommands)502 angle::Result CommandPoolAccess::collectPrimaryCommandBuffer(ErrorContext *context,
503                                                              const ProtectionType protectionType,
504                                                              PrimaryCommandBuffer *primaryCommands)
505 {
506     ASSERT(primaryCommands->valid());
507     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
508 
509     PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[protectionType];
510     ANGLE_TRY(commandPool.collect(context, std::move(*primaryCommands)));
511 
512     return angle::Result::Continue;
513 }
514 
flushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)515 angle::Result CommandPoolAccess::flushOutsideRPCommands(
516     Context *context,
517     ProtectionType protectionType,
518     egl::ContextPriority priority,
519     OutsideRenderPassCommandBufferHelper **outsideRPCommands)
520 {
521     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
522     ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
523     CommandsState &state = mCommandsStateMap[priority][protectionType];
524     return (*outsideRPCommands)->flushToPrimary(context, &state);
525 }
526 
flushRenderPassCommands(Context * context,const ProtectionType & protectionType,const egl::ContextPriority & priority,const RenderPass & renderPass,VkFramebuffer framebufferOverride,RenderPassCommandBufferHelper ** renderPassCommands)527 angle::Result CommandPoolAccess::flushRenderPassCommands(
528     Context *context,
529     const ProtectionType &protectionType,
530     const egl::ContextPriority &priority,
531     const RenderPass &renderPass,
532     VkFramebuffer framebufferOverride,
533     RenderPassCommandBufferHelper **renderPassCommands)
534 {
535     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
536     ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
537     CommandsState &state = mCommandsStateMap[priority][protectionType];
538     return (*renderPassCommands)->flushToPrimary(context, &state, renderPass, framebufferOverride);
539 }
540 
flushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)541 void CommandPoolAccess::flushWaitSemaphores(
542     ProtectionType protectionType,
543     egl::ContextPriority priority,
544     std::vector<VkSemaphore> &&waitSemaphores,
545     std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
546 {
547     ASSERT(!waitSemaphores.empty());
548     ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
549     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
550 
551     CommandsState &state = mCommandsStateMap[priority][protectionType];
552 
553     state.waitSemaphores.insert(state.waitSemaphores.end(), waitSemaphores.begin(),
554                                 waitSemaphores.end());
555     state.waitSemaphoreStageMasks.insert(state.waitSemaphoreStageMasks.end(),
556                                          waitSemaphoreStageMasks.begin(),
557                                          waitSemaphoreStageMasks.end());
558 
559     waitSemaphores.clear();
560     waitSemaphoreStageMasks.clear();
561 }
562 
getCommandsAndWaitSemaphores(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority priority,CommandBatch * batchOut,std::vector<VkImageMemoryBarrier> && imagesToTransitionToForeign,std::vector<VkSemaphore> * waitSemaphoresOut,std::vector<VkPipelineStageFlags> * waitSemaphoreStageMasksOut)563 angle::Result CommandPoolAccess::getCommandsAndWaitSemaphores(
564     ErrorContext *context,
565     ProtectionType protectionType,
566     egl::ContextPriority priority,
567     CommandBatch *batchOut,
568     std::vector<VkImageMemoryBarrier> &&imagesToTransitionToForeign,
569     std::vector<VkSemaphore> *waitSemaphoresOut,
570     std::vector<VkPipelineStageFlags> *waitSemaphoreStageMasksOut)
571 {
572     std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
573 
574     CommandsState &state = mCommandsStateMap[priority][protectionType];
575     ASSERT(state.primaryCommands.valid() || state.secondaryCommands.empty());
576 
577     // If there are foreign images to transition, issue the barrier now.
578     if (!imagesToTransitionToForeign.empty())
579     {
580         // It is possible for another thread to have made a submission just now, such that there is
581         // no primary command buffer anymore.  In that case, one has to be allocated to hold the
582         // barriers.
583         ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
584 
585         state.primaryCommands.pipelineBarrier(
586             VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
587             nullptr, static_cast<uint32_t>(imagesToTransitionToForeign.size()),
588             imagesToTransitionToForeign.data());
589         imagesToTransitionToForeign.clear();
590     }
591 
592     // Store the primary CommandBuffer and the reference to CommandPoolAccess in the in-flight list.
593     if (state.primaryCommands.valid())
594     {
595         ANGLE_VK_TRY(context, state.primaryCommands.end());
596     }
597     batchOut->setPrimaryCommands(std::move(state.primaryCommands), this);
598 
599     // Store secondary Command Buffers.
600     batchOut->setSecondaryCommands(std::move(state.secondaryCommands));
601 
602     // Store wait semaphores.
603     *waitSemaphoresOut          = std::move(state.waitSemaphores);
604     *waitSemaphoreStageMasksOut = std::move(state.waitSemaphoreStageMasks);
605 
606     return angle::Result::Continue;
607 }
608 
609 // CommandQueue public API implementation. These must be thread safe and never called from
610 // CommandQueue class itself.
CommandQueue()611 CommandQueue::CommandQueue()
612     : mInFlightCommands(kInFlightCommandsLimit),
613       mFinishedCommandBatches(kMaxFinishedCommandsLimit),
614       mNumAllCommands(0),
615       mPerfCounters{}
616 {}
617 
618 CommandQueue::~CommandQueue() = default;
619 
destroy(ErrorContext * context)620 void CommandQueue::destroy(ErrorContext *context)
621 {
622     std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
623     std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
624     std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
625 
626     mQueueMap.destroy();
627 
628     // Assigns an infinite "last completed" serial to force garbage to delete.
629     mLastCompletedSerials.fill(Serial::Infinite());
630 
631     mCommandPoolAccess.destroy(context->getDevice());
632 
633     mFenceRecycler.destroy(context);
634 
635     ASSERT(mInFlightCommands.empty());
636     ASSERT(mFinishedCommandBatches.empty());
637     ASSERT(mNumAllCommands == 0);
638 }
639 
init(ErrorContext * context,const QueueFamily & queueFamily,bool enableProtectedContent,uint32_t queueCount)640 angle::Result CommandQueue::init(ErrorContext *context,
641                                  const QueueFamily &queueFamily,
642                                  bool enableProtectedContent,
643                                  uint32_t queueCount)
644 {
645     std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
646     std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
647     std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
648 
649     // In case Renderer gets re-initialized, we can't rely on constructor to do initialization.
650     mLastSubmittedSerials.fill(kZeroSerial);
651     mLastCompletedSerials.fill(kZeroSerial);
652 
653     // Assign before initializing the command pools in order to get the queue family index.
654     mQueueMap.initialize(context->getDevice(), queueFamily, enableProtectedContent, 0, queueCount);
655     ANGLE_TRY(mCommandPoolAccess.initCommandPool(context, ProtectionType::Unprotected,
656                                                  mQueueMap.getQueueFamilyIndex()));
657 
658     if (mQueueMap.isProtected())
659     {
660         ANGLE_TRY(mCommandPoolAccess.initCommandPool(context, ProtectionType::Protected,
661                                                      mQueueMap.getQueueFamilyIndex()));
662     }
663     return angle::Result::Continue;
664 }
665 
handleDeviceLost(Renderer * renderer)666 void CommandQueue::handleDeviceLost(Renderer *renderer)
667 {
668     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::handleDeviceLost");
669     VkDevice device = renderer->getDevice();
670     // Hold all locks while clean up mInFlightCommands.
671     std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
672     std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
673     std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
674 
675     // Work around a driver bug where resource clean up would cause a crash without vkQueueWaitIdle.
676     mQueueMap.waitAllQueuesIdle();
677 
678     while (!mInFlightCommands.empty())
679     {
680         CommandBatch &batch = mInFlightCommands.front();
681         // On device loss we need to wait for fence to be signaled before destroying it
682         if (batch.hasFence())
683         {
684             VkResult status = batch.waitFence(device, renderer->getMaxFenceWaitTimeNs());
685             // If the wait times out, it is probably not possible to recover from lost device
686             ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
687         }
688         mLastCompletedSerials.setQueueSerial(batch.getQueueSerial());
689         batch.destroy(device);
690         popInFlightBatchLocked();
691     }
692 }
693 
postSubmitCheck(ErrorContext * context)694 angle::Result CommandQueue::postSubmitCheck(ErrorContext *context)
695 {
696     Renderer *renderer = context->getRenderer();
697 
698     // Update mLastCompletedQueueSerial immediately in case any command has been finished.
699     ANGLE_TRY(checkAndCleanupCompletedCommands(context));
700 
701     VkDeviceSize suballocationGarbageSize = renderer->getSuballocationGarbageSize();
702     while (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize)
703     {
704         // CPU should be throttled to avoid accumulating too much memory garbage waiting to be
705         // destroyed. This is important to keep peak memory usage at check when game launched and a
706         // lot of staging buffers used for textures upload and then gets released. But if there is
707         // only one command buffer in flight, we do not wait here to ensure we keep GPU busy.
708         constexpr size_t kMinInFlightBatchesToKeep = 1;
709         bool anyGarbageCleaned                     = false;
710         ANGLE_TRY(cleanupSomeGarbage(context, kMinInFlightBatchesToKeep, &anyGarbageCleaned));
711         if (!anyGarbageCleaned)
712         {
713             break;
714         }
715         suballocationGarbageSize = renderer->getSuballocationGarbageSize();
716     }
717 
718     if (kOutputVmaStatsString)
719     {
720         renderer->outputVmaStatString();
721     }
722 
723     return angle::Result::Continue;
724 }
725 
finishResourceUse(ErrorContext * context,const ResourceUse & use,uint64_t timeout)726 angle::Result CommandQueue::finishResourceUse(ErrorContext *context,
727                                               const ResourceUse &use,
728                                               uint64_t timeout)
729 {
730     VkDevice device = context->getDevice();
731     {
732         std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
733         while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
734         {
735             bool finished;
736             ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
737             if (!finished)
738             {
739                 ANGLE_VK_TRY(context,
740                              mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock));
741             }
742         }
743         // Check the rest of the commands in case they are also finished.
744         ANGLE_TRY(checkCompletedCommandsLocked(context));
745     }
746     ASSERT(hasResourceUseFinished(use));
747 
748     if (!mFinishedCommandBatches.empty())
749     {
750         ANGLE_TRY(releaseFinishedCommandsAndCleanupGarbage(context));
751     }
752 
753     return angle::Result::Continue;
754 }
755 
finishQueueSerial(ErrorContext * context,const QueueSerial & queueSerial,uint64_t timeout)756 angle::Result CommandQueue::finishQueueSerial(ErrorContext *context,
757                                               const QueueSerial &queueSerial,
758                                               uint64_t timeout)
759 {
760     ResourceUse use(queueSerial);
761     return finishResourceUse(context, use, timeout);
762 }
763 
waitIdle(ErrorContext * context,uint64_t timeout)764 angle::Result CommandQueue::waitIdle(ErrorContext *context, uint64_t timeout)
765 {
766     // Fill the local variable with lock
767     ResourceUse use;
768     {
769         std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
770         if (mInFlightCommands.empty())
771         {
772             return angle::Result::Continue;
773         }
774         use.setQueueSerial(mInFlightCommands.back().getQueueSerial());
775     }
776 
777     return finishResourceUse(context, use, timeout);
778 }
779 
waitForResourceUseToFinishWithUserTimeout(ErrorContext * context,const ResourceUse & use,uint64_t timeout,VkResult * result)780 angle::Result CommandQueue::waitForResourceUseToFinishWithUserTimeout(ErrorContext *context,
781                                                                       const ResourceUse &use,
782                                                                       uint64_t timeout,
783                                                                       VkResult *result)
784 {
785     // Serial is not yet submitted. This is undefined behaviour, so we can do anything.
786     if (!hasResourceUseSubmitted(use))
787     {
788         WARN() << "Waiting on an unsubmitted serial.";
789         *result = VK_TIMEOUT;
790         return angle::Result::Continue;
791     }
792 
793     VkDevice device      = context->getDevice();
794     size_t finishedCount = 0;
795     {
796         std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
797         *result = hasResourceUseFinished(use) ? VK_SUCCESS : VK_NOT_READY;
798         while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
799         {
800             bool finished;
801             ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
802             if (!finished)
803             {
804                 *result = mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock);
805                 // Don't trigger an error on timeout.
806                 if (*result == VK_TIMEOUT)
807                 {
808                     break;
809                 }
810                 else
811                 {
812                     ANGLE_VK_TRY(context, *result);
813                 }
814             }
815             else
816             {
817                 *result = hasResourceUseFinished(use) ? VK_SUCCESS : VK_NOT_READY;
818             }
819         }
820         // Do one more check in case more commands also finished.
821         ANGLE_TRY(checkCompletedCommandsLocked(context));
822         finishedCount = mFinishedCommandBatches.size();
823     }
824 
825     if (finishedCount > 0)
826     {
827         ANGLE_TRY(releaseFinishedCommandsAndCleanupGarbage(context));
828     }
829 
830     return angle::Result::Continue;
831 }
832 
isBusy(Renderer * renderer) const833 bool CommandQueue::isBusy(Renderer *renderer) const
834 {
835     // No lock is needed here since we are accessing atomic variables only.
836     size_t maxIndex = renderer->getLargestQueueSerialIndexEverAllocated();
837     for (SerialIndex i = 0; i <= maxIndex; ++i)
838     {
839         if (mLastSubmittedSerials[i] > mLastCompletedSerials[i])
840         {
841             return true;
842         }
843     }
844     return false;
845 }
846 
submitCommands(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,std::vector<VkImageMemoryBarrier> && imagesToTransitionToForeign,const QueueSerial & submitQueueSerial)847 angle::Result CommandQueue::submitCommands(
848     ErrorContext *context,
849     ProtectionType protectionType,
850     egl::ContextPriority priority,
851     VkSemaphore signalSemaphore,
852     SharedExternalFence &&externalFence,
853     std::vector<VkImageMemoryBarrier> &&imagesToTransitionToForeign,
854     const QueueSerial &submitQueueSerial)
855 {
856     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitCommands");
857     std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
858     Renderer *renderer = context->getRenderer();
859     VkDevice device    = renderer->getDevice();
860 
861     ++mPerfCounters.commandQueueSubmitCallsTotal;
862     ++mPerfCounters.commandQueueSubmitCallsPerFrame;
863 
864     DeviceScoped<CommandBatch> scopedBatch(device);
865     CommandBatch &batch = scopedBatch.get();
866 
867     batch.setQueueSerial(submitQueueSerial);
868     batch.setProtectionType(protectionType);
869 
870     std::vector<VkSemaphore> waitSemaphores;
871     std::vector<VkPipelineStageFlags> waitSemaphoreStageMasks;
872 
873     ANGLE_TRY(mCommandPoolAccess.getCommandsAndWaitSemaphores(
874         context, protectionType, priority, &batch, std::move(imagesToTransitionToForeign),
875         &waitSemaphores, &waitSemaphoreStageMasks));
876 
877     mPerfCounters.commandQueueWaitSemaphoresTotal += waitSemaphores.size();
878 
879     // Don't make a submission if there is nothing to submit.
880     const bool needsQueueSubmit = batch.getPrimaryCommands().valid() ||
881                                   signalSemaphore != VK_NULL_HANDLE || externalFence ||
882                                   !waitSemaphores.empty();
883     VkSubmitInfo submitInfo                   = {};
884     VkProtectedSubmitInfo protectedSubmitInfo = {};
885 
886     if (needsQueueSubmit)
887     {
888         InitializeSubmitInfo(&submitInfo, batch.getPrimaryCommands(), waitSemaphores,
889                              waitSemaphoreStageMasks, signalSemaphore);
890 
891         // No need protected submission if no commands to submit.
892         if (protectionType == ProtectionType::Protected && batch.getPrimaryCommands().valid())
893         {
894             protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
895             protectedSubmitInfo.pNext           = nullptr;
896             protectedSubmitInfo.protectedSubmit = true;
897             submitInfo.pNext                    = &protectedSubmitInfo;
898         }
899 
900         if (!externalFence)
901         {
902             ANGLE_VK_TRY(context, batch.initFence(context->getDevice(), &mFenceRecycler));
903         }
904         else
905         {
906             batch.setExternalFence(std::move(externalFence));
907         }
908 
909         ++mPerfCounters.vkQueueSubmitCallsTotal;
910         ++mPerfCounters.vkQueueSubmitCallsPerFrame;
911     }
912 
913     return queueSubmitLocked(context, priority, submitInfo, scopedBatch, submitQueueSerial);
914 }
915 
queueSubmitOneOff(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const QueueSerial & submitQueueSerial)916 angle::Result CommandQueue::queueSubmitOneOff(ErrorContext *context,
917                                               ProtectionType protectionType,
918                                               egl::ContextPriority contextPriority,
919                                               VkCommandBuffer commandBufferHandle,
920                                               VkSemaphore waitSemaphore,
921                                               VkPipelineStageFlags waitSemaphoreStageMask,
922                                               const QueueSerial &submitQueueSerial)
923 {
924     std::unique_lock<angle::SimpleMutex> lock(mQueueSubmitMutex);
925     DeviceScoped<CommandBatch> scopedBatch(context->getDevice());
926     CommandBatch &batch = scopedBatch.get();
927     batch.setQueueSerial(submitQueueSerial);
928     batch.setProtectionType(protectionType);
929 
930     ANGLE_VK_TRY(context, batch.initFence(context->getDevice(), &mFenceRecycler));
931 
932     VkSubmitInfo submitInfo = {};
933     submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;
934 
935     VkProtectedSubmitInfo protectedSubmitInfo = {};
936     ASSERT(protectionType == ProtectionType::Unprotected ||
937            protectionType == ProtectionType::Protected);
938     if (protectionType == ProtectionType::Protected)
939     {
940         protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
941         protectedSubmitInfo.pNext           = nullptr;
942         protectedSubmitInfo.protectedSubmit = true;
943         submitInfo.pNext                    = &protectedSubmitInfo;
944     }
945 
946     if (commandBufferHandle != VK_NULL_HANDLE)
947     {
948         submitInfo.commandBufferCount = 1;
949         submitInfo.pCommandBuffers    = &commandBufferHandle;
950     }
951 
952     if (waitSemaphore != VK_NULL_HANDLE)
953     {
954         submitInfo.waitSemaphoreCount = 1;
955         submitInfo.pWaitSemaphores    = &waitSemaphore;
956         submitInfo.pWaitDstStageMask  = &waitSemaphoreStageMask;
957     }
958 
959     ++mPerfCounters.vkQueueSubmitCallsTotal;
960     ++mPerfCounters.vkQueueSubmitCallsPerFrame;
961 
962     return queueSubmitLocked(context, contextPriority, submitInfo, scopedBatch, submitQueueSerial);
963 }
964 
queueSubmitLocked(ErrorContext * context,egl::ContextPriority contextPriority,const VkSubmitInfo & submitInfo,DeviceScoped<CommandBatch> & commandBatch,const QueueSerial & submitQueueSerial)965 angle::Result CommandQueue::queueSubmitLocked(ErrorContext *context,
966                                               egl::ContextPriority contextPriority,
967                                               const VkSubmitInfo &submitInfo,
968                                               DeviceScoped<CommandBatch> &commandBatch,
969                                               const QueueSerial &submitQueueSerial)
970 {
971     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::queueSubmitLocked");
972     Renderer *renderer = context->getRenderer();
973 
974     // CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
975     // off-screen scenarios.
976     if (mInFlightCommands.full())
977     {
978         std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
979         // Check once more inside the lock in case other thread already finished some/all commands.
980         if (mInFlightCommands.full())
981         {
982             ANGLE_TRY(finishOneCommandBatch(context, renderer->getMaxFenceWaitTimeNs(), &lock));
983         }
984     }
985     // Assert will succeed since new batch is pushed only in this method below.
986     ASSERT(!mInFlightCommands.full());
987 
988     // Also ensure that all mInFlightCommands may be moved into the mFinishedCommandBatches without
989     // need of the releaseFinishedCommandsLocked() call.
990     ASSERT(mNumAllCommands <= mFinishedCommandBatches.capacity());
991     if (mNumAllCommands == mFinishedCommandBatches.capacity())
992     {
993         std::lock_guard<angle::SimpleMutex> lock(mCmdReleaseMutex);
994         ANGLE_TRY(releaseFinishedCommandsLocked(context));
995     }
996     // Assert will succeed since mNumAllCommands is incremented only in this method below.
997     ASSERT(mNumAllCommands < mFinishedCommandBatches.capacity());
998 
999     if (submitInfo.sType == VK_STRUCTURE_TYPE_SUBMIT_INFO)
1000     {
1001         CommandBatch &batch = commandBatch.get();
1002 
1003         VkQueue queue = getQueue(contextPriority);
1004         VkFence fence = batch.getFenceHandle();
1005         ASSERT(fence != VK_NULL_HANDLE);
1006         ANGLE_VK_TRY(context, vkQueueSubmit(queue, 1, &submitInfo, fence));
1007 
1008         if (batch.getExternalFence())
1009         {
1010             // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
1011             // obeys copy semantics. This means that the fence must already be signaled or the work
1012             // to signal it is in the graphics pipeline at the time we export the fd.
1013             // In other words, must call exportFd() after successful vkQueueSubmit() call.
1014             ExternalFence &externalFence       = *batch.getExternalFence();
1015             VkFenceGetFdInfoKHR fenceGetFdInfo = {};
1016             fenceGetFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
1017             fenceGetFdInfo.fence               = externalFence.getHandle();
1018             fenceGetFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
1019             externalFence.exportFd(renderer->getDevice(), fenceGetFdInfo);
1020         }
1021     }
1022 
1023     pushInFlightBatchLocked(commandBatch.release());
1024 
1025     // This must set last so that when this submission appears submitted, it actually already
1026     // submitted and enqueued to mInFlightCommands.
1027     mLastSubmittedSerials.setQueueSerial(submitQueueSerial);
1028     return angle::Result::Continue;
1029 }
1030 
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo)1031 VkResult CommandQueue::queuePresent(egl::ContextPriority contextPriority,
1032                                     const VkPresentInfoKHR &presentInfo)
1033 {
1034     std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1035     VkQueue queue = getQueue(contextPriority);
1036     return vkQueuePresentKHR(queue, &presentInfo);
1037 }
1038 
getPerfCounters() const1039 const angle::VulkanPerfCounters CommandQueue::getPerfCounters() const
1040 {
1041     std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1042     return mPerfCounters;
1043 }
1044 
resetPerFramePerfCounters()1045 void CommandQueue::resetPerFramePerfCounters()
1046 {
1047     std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1048     mPerfCounters.commandQueueSubmitCallsPerFrame = 0;
1049     mPerfCounters.vkQueueSubmitCallsPerFrame      = 0;
1050 }
1051 
releaseFinishedCommandsAndCleanupGarbage(ErrorContext * context)1052 angle::Result CommandQueue::releaseFinishedCommandsAndCleanupGarbage(ErrorContext *context)
1053 {
1054     Renderer *renderer = context->getRenderer();
1055     if (renderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled())
1056     {
1057         renderer->requestAsyncCommandsAndGarbageCleanup(context);
1058     }
1059     else
1060     {
1061         // Do immediate command buffer reset and garbage cleanup
1062         ANGLE_TRY(releaseFinishedCommands(context));
1063         renderer->cleanupGarbage(nullptr);
1064     }
1065 
1066     return angle::Result::Continue;
1067 }
1068 
cleanupSomeGarbage(ErrorContext * context,size_t minInFlightBatchesToKeep,bool * anyGarbageCleanedOut)1069 angle::Result CommandQueue::cleanupSomeGarbage(ErrorContext *context,
1070                                                size_t minInFlightBatchesToKeep,
1071                                                bool *anyGarbageCleanedOut)
1072 {
1073     Renderer *renderer = context->getRenderer();
1074 
1075     bool anyGarbageCleaned = false;
1076 
1077     renderer->cleanupGarbage(&anyGarbageCleaned);
1078 
1079     while (!anyGarbageCleaned)
1080     {
1081         {
1082             std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
1083             if (mInFlightCommands.size() <= minInFlightBatchesToKeep)
1084             {
1085                 break;
1086             }
1087             ANGLE_TRY(finishOneCommandBatch(context, renderer->getMaxFenceWaitTimeNs(), &lock));
1088         }
1089         renderer->cleanupGarbage(&anyGarbageCleaned);
1090     }
1091 
1092     if (anyGarbageCleanedOut != nullptr)
1093     {
1094         *anyGarbageCleanedOut = anyGarbageCleaned;
1095     }
1096 
1097     return angle::Result::Continue;
1098 }
1099 
1100 // CommandQueue private API implementation. These are called by public API, so lock already held.
checkOneCommandBatchLocked(ErrorContext * context,bool * finished)1101 angle::Result CommandQueue::checkOneCommandBatchLocked(ErrorContext *context, bool *finished)
1102 {
1103     ASSERT(!mInFlightCommands.empty());
1104 
1105     CommandBatch &batch = mInFlightCommands.front();
1106     *finished           = false;
1107     if (batch.hasFence())
1108     {
1109         VkResult status = batch.getFenceStatus(context->getDevice());
1110         if (status == VK_NOT_READY)
1111         {
1112             return angle::Result::Continue;
1113         }
1114         ANGLE_VK_TRY(context, status);
1115     }
1116 
1117     onCommandBatchFinishedLocked(std::move(batch));
1118     *finished = true;
1119 
1120     return angle::Result::Continue;
1121 }
1122 
finishOneCommandBatch(ErrorContext * context,uint64_t timeout,std::unique_lock<angle::SimpleMutex> * lock)1123 angle::Result CommandQueue::finishOneCommandBatch(ErrorContext *context,
1124                                                   uint64_t timeout,
1125                                                   std::unique_lock<angle::SimpleMutex> *lock)
1126 {
1127     ASSERT(!mInFlightCommands.empty());
1128     ASSERT(lock->owns_lock());
1129 
1130     CommandBatch &batch = mInFlightCommands.front();
1131     // Save queue serial since the batch may be destroyed during possible unlocked fence wait.
1132     const QueueSerial batchSerial = batch.getQueueSerial();
1133     if (batch.hasFence())
1134     {
1135         VkResult status = batch.waitFenceUnlocked(context->getDevice(), timeout, lock);
1136         ANGLE_VK_TRY(context, status);
1137     }
1138 
1139     // Other thread might already finish the batch, in that case do not touch the |batch| reference.
1140     if (!hasQueueSerialFinished(batchSerial))
1141     {
1142         onCommandBatchFinishedLocked(std::move(batch));
1143     }
1144 
1145     return angle::Result::Continue;
1146 }
1147 
onCommandBatchFinishedLocked(CommandBatch && batch)1148 void CommandQueue::onCommandBatchFinishedLocked(CommandBatch &&batch)
1149 {
1150     // Finished.
1151     mLastCompletedSerials.setQueueSerial(batch.getQueueSerial());
1152 
1153     // Move command batch to mFinishedCommandBatches.
1154     moveInFlightBatchToFinishedQueueLocked(std::move(batch));
1155 }
1156 
releaseFinishedCommandsLocked(ErrorContext * context)1157 angle::Result CommandQueue::releaseFinishedCommandsLocked(ErrorContext *context)
1158 {
1159     ANGLE_TRACE_EVENT0("gpu.angle", "releaseFinishedCommandsLocked");
1160 
1161     while (!mFinishedCommandBatches.empty())
1162     {
1163         CommandBatch &batch = mFinishedCommandBatches.front();
1164         ASSERT(batch.getQueueSerial() <= mLastCompletedSerials);
1165         ANGLE_TRY(batch.release(context));
1166         popFinishedBatchLocked();
1167     }
1168 
1169     return angle::Result::Continue;
1170 }
1171 
checkCompletedCommandsLocked(ErrorContext * context)1172 angle::Result CommandQueue::checkCompletedCommandsLocked(ErrorContext *context)
1173 {
1174     while (!mInFlightCommands.empty())
1175     {
1176         bool finished;
1177         ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
1178         if (!finished)
1179         {
1180             break;
1181         }
1182     }
1183     return angle::Result::Continue;
1184 }
1185 
pushInFlightBatchLocked(CommandBatch && batch)1186 void CommandQueue::pushInFlightBatchLocked(CommandBatch &&batch)
1187 {
1188     // Need to increment before the push to prevent possible decrement from 0.
1189     ++mNumAllCommands;
1190     mInFlightCommands.push(std::move(batch));
1191 }
1192 
moveInFlightBatchToFinishedQueueLocked(CommandBatch && batch)1193 void CommandQueue::moveInFlightBatchToFinishedQueueLocked(CommandBatch &&batch)
1194 {
1195     // This must not happen, since we always leave space in the queue during queueSubmitLocked.
1196     ASSERT(!mFinishedCommandBatches.full());
1197     ASSERT(&batch == &mInFlightCommands.front());
1198 
1199     mFinishedCommandBatches.push(std::move(batch));
1200     mInFlightCommands.pop();
1201     // No mNumAllCommands update since batch was simply moved to the other queue.
1202 }
1203 
popFinishedBatchLocked()1204 void CommandQueue::popFinishedBatchLocked()
1205 {
1206     mFinishedCommandBatches.pop();
1207     // Need to decrement after the pop to prevent possible push over the limit.
1208     ASSERT(mNumAllCommands > 0);
1209     --mNumAllCommands;
1210 }
1211 
popInFlightBatchLocked()1212 void CommandQueue::popInFlightBatchLocked()
1213 {
1214     mInFlightCommands.pop();
1215     // Need to decrement after the pop to prevent possible push over the limit.
1216     ASSERT(mNumAllCommands > 0);
1217     --mNumAllCommands;
1218 }
1219 
1220 // QueuePriorities:
1221 constexpr float kVulkanQueuePriorityLow    = 0.0;
1222 constexpr float kVulkanQueuePriorityMedium = 0.4;
1223 constexpr float kVulkanQueuePriorityHigh   = 1.0;
1224 
1225 const float QueueFamily::kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] =
1226     {kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
1227 
~DeviceQueueMap()1228 DeviceQueueMap::~DeviceQueueMap() {}
1229 
destroy()1230 void DeviceQueueMap::destroy()
1231 {
1232     waitAllQueuesIdle();
1233 }
1234 
waitAllQueuesIdle()1235 void DeviceQueueMap::waitAllQueuesIdle()
1236 {
1237     // Force all commands to finish by flushing all queues.
1238     for (const QueueAndIndex &queueAndIndex : mQueueAndIndices)
1239     {
1240         if (queueAndIndex.queue != VK_NULL_HANDLE)
1241         {
1242             vkQueueWaitIdle(queueAndIndex.queue);
1243         }
1244     }
1245 }
1246 
initialize(VkDevice device,const QueueFamily & queueFamily,bool makeProtected,uint32_t queueIndex,uint32_t queueCount)1247 void DeviceQueueMap::initialize(VkDevice device,
1248                                 const QueueFamily &queueFamily,
1249                                 bool makeProtected,
1250                                 uint32_t queueIndex,
1251                                 uint32_t queueCount)
1252 {
1253     // QueueIndexing:
1254     constexpr uint32_t kQueueIndexMedium = 0;
1255     constexpr uint32_t kQueueIndexHigh   = 1;
1256     constexpr uint32_t kQueueIndexLow    = 2;
1257 
1258     ASSERT(queueCount);
1259     ASSERT((queueIndex + queueCount) <= queueFamily.getProperties()->queueCount);
1260     mQueueFamilyIndex = queueFamily.getQueueFamilyIndex();
1261     mIsProtected      = makeProtected;
1262 
1263     VkQueue queue = VK_NULL_HANDLE;
1264     GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexMedium,
1265                    &queue);
1266     mQueueAndIndices[egl::ContextPriority::Medium] = {egl::ContextPriority::Medium, queue,
1267                                                       queueIndex + kQueueIndexMedium};
1268 
1269     // If at least 2 queues, High has its own queue
1270     if (queueCount > 1)
1271     {
1272         GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexHigh,
1273                        &queue);
1274         mQueueAndIndices[egl::ContextPriority::High] = {egl::ContextPriority::High, queue,
1275                                                         queueIndex + kQueueIndexHigh};
1276     }
1277     else
1278     {
1279         mQueueAndIndices[egl::ContextPriority::High] =
1280             mQueueAndIndices[egl::ContextPriority::Medium];
1281     }
1282     // If at least 3 queues, Low has its own queue. Adjust Low priority.
1283     if (queueCount > 2)
1284     {
1285         GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexLow,
1286                        &queue);
1287         mQueueAndIndices[egl::ContextPriority::Low] = {egl::ContextPriority::Low, queue,
1288                                                        queueIndex + kQueueIndexLow};
1289     }
1290     else
1291     {
1292         mQueueAndIndices[egl::ContextPriority::Low] =
1293             mQueueAndIndices[egl::ContextPriority::Medium];
1294     }
1295 }
1296 
initialize(const VkQueueFamilyProperties & queueFamilyProperties,uint32_t queueFamilyIndex)1297 void QueueFamily::initialize(const VkQueueFamilyProperties &queueFamilyProperties,
1298                              uint32_t queueFamilyIndex)
1299 {
1300     mProperties       = queueFamilyProperties;
1301     mQueueFamilyIndex = queueFamilyIndex;
1302 }
1303 
FindIndex(const std::vector<VkQueueFamilyProperties> & queueFamilyProperties,VkQueueFlags includeFlags,VkQueueFlags optionalFlags,VkQueueFlags excludeFlags,uint32_t * matchCount)1304 uint32_t QueueFamily::FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties,
1305                                 VkQueueFlags includeFlags,
1306                                 VkQueueFlags optionalFlags,
1307                                 VkQueueFlags excludeFlags,
1308                                 uint32_t *matchCount)
1309 {
1310     // check with both include and optional flags
1311     VkQueueFlags preferredFlags = includeFlags | optionalFlags;
1312     auto findIndexPredicate     = [&preferredFlags,
1313                                &excludeFlags](const VkQueueFamilyProperties &queueInfo) {
1314         return (queueInfo.queueFlags & excludeFlags) == 0 &&
1315                (queueInfo.queueFlags & preferredFlags) == preferredFlags;
1316     };
1317 
1318     auto it = std::find_if(queueFamilyProperties.begin(), queueFamilyProperties.end(),
1319                            findIndexPredicate);
1320     if (it == queueFamilyProperties.end())
1321     {
1322         // didn't find a match, exclude the optional flags from the list
1323         preferredFlags = includeFlags;
1324         it             = std::find_if(queueFamilyProperties.begin(), queueFamilyProperties.end(),
1325                                       findIndexPredicate);
1326     }
1327     if (it == queueFamilyProperties.end())
1328     {
1329         *matchCount = 0;
1330         return QueueFamily::kInvalidIndex;
1331     }
1332 
1333     *matchCount = 1;
1334     return static_cast<uint32_t>(std::distance(queueFamilyProperties.begin(), it));
1335 }
1336 
1337 }  // namespace vk
1338 }  // namespace rx
1339