• 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 "libANGLE/renderer/vulkan/RendererVk.h"
12 #include "libANGLE/trace.h"
13 
14 namespace rx
15 {
16 namespace vk
17 {
18 namespace
19 {
20 constexpr size_t kInFlightCommandsLimit = 100u;
21 constexpr bool kOutputVmaStatsString    = false;
22 
InitializeSubmitInfo(VkSubmitInfo * submitInfo,const vk::PrimaryCommandBuffer & commandBuffer,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const vk::Semaphore * signalSemaphore)23 void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
24                           const vk::PrimaryCommandBuffer &commandBuffer,
25                           const std::vector<VkSemaphore> &waitSemaphores,
26                           const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
27                           const vk::Semaphore *signalSemaphore)
28 {
29     // Verify that the submitInfo has been zero'd out.
30     ASSERT(submitInfo->signalSemaphoreCount == 0);
31     ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
32     submitInfo->sType              = VK_STRUCTURE_TYPE_SUBMIT_INFO;
33     submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
34     submitInfo->pCommandBuffers    = commandBuffer.ptr();
35     submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
36     submitInfo->pWaitSemaphores    = waitSemaphores.data();
37     submitInfo->pWaitDstStageMask  = waitSemaphoreStageMasks.data();
38 
39     if (signalSemaphore)
40     {
41         submitInfo->signalSemaphoreCount = 1;
42         submitInfo->pSignalSemaphores    = signalSemaphore->ptr();
43     }
44 }
45 
CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> & commands)46 bool CommandsHaveValidOrdering(const std::vector<vk::CommandBatch> &commands)
47 {
48     Serial currentSerial;
49     for (const vk::CommandBatch &commandBatch : commands)
50     {
51         if (commandBatch.serial <= currentSerial)
52         {
53             return false;
54         }
55         currentSerial = commandBatch.serial;
56     }
57 
58     return true;
59 }
60 }  // namespace
61 
newSharedFence(vk::Context * context,vk::Shared<vk::Fence> * sharedFenceOut)62 angle::Result FenceRecycler::newSharedFence(vk::Context *context,
63                                             vk::Shared<vk::Fence> *sharedFenceOut)
64 {
65     bool gotRecycledFence = false;
66     vk::Fence fence;
67     {
68         std::lock_guard<std::mutex> lock(mMutex);
69         if (!mRecyler.empty())
70         {
71             mRecyler.fetch(&fence);
72             gotRecycledFence = true;
73         }
74     }
75 
76     VkDevice device(context->getDevice());
77     if (gotRecycledFence)
78     {
79         ANGLE_VK_TRY(context, fence.reset(device));
80     }
81     else
82     {
83         VkFenceCreateInfo fenceCreateInfo = {};
84         fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
85         fenceCreateInfo.flags             = 0;
86         ANGLE_VK_TRY(context, fence.init(device, fenceCreateInfo));
87     }
88     sharedFenceOut->assign(device, std::move(fence));
89     return angle::Result::Continue;
90 }
91 
destroy(vk::Context * context)92 void FenceRecycler::destroy(vk::Context *context)
93 {
94     std::lock_guard<std::mutex> lock(mMutex);
95     mRecyler.destroy(context->getDevice());
96 }
97 
98 // CommandProcessorTask implementation
initTask()99 void CommandProcessorTask::initTask()
100 {
101     mTask                         = CustomTask::Invalid;
102     mRenderPass                   = nullptr;
103     mCommandBuffer                = nullptr;
104     mSemaphore                    = nullptr;
105     mCommandPool                  = nullptr;
106     mOneOffWaitSemaphore          = nullptr;
107     mOneOffWaitSemaphoreStageMask = 0;
108     mOneOffFence                  = nullptr;
109     mPresentInfo                  = {};
110     mPresentInfo.pResults         = nullptr;
111     mPresentInfo.pSwapchains      = nullptr;
112     mPresentInfo.pImageIndices    = nullptr;
113     mPresentInfo.pNext            = nullptr;
114     mPresentInfo.pWaitSemaphores  = nullptr;
115     mOneOffCommandBufferVk        = VK_NULL_HANDLE;
116     mPriority                     = egl::ContextPriority::Medium;
117     mHasProtectedContent          = false;
118 }
119 
initProcessCommands(bool hasProtectedContent,CommandBufferHelper * commandBuffer,const RenderPass * renderPass)120 void CommandProcessorTask::initProcessCommands(bool hasProtectedContent,
121                                                CommandBufferHelper *commandBuffer,
122                                                const RenderPass *renderPass)
123 {
124     mTask                = CustomTask::ProcessCommands;
125     mCommandBuffer       = commandBuffer;
126     mRenderPass          = renderPass;
127     mHasProtectedContent = hasProtectedContent;
128 }
129 
copyPresentInfo(const VkPresentInfoKHR & other)130 void CommandProcessorTask::copyPresentInfo(const VkPresentInfoKHR &other)
131 {
132     if (other.sType == 0)
133     {
134         return;
135     }
136 
137     mPresentInfo.sType = other.sType;
138     mPresentInfo.pNext = other.pNext;
139 
140     if (other.swapchainCount > 0)
141     {
142         ASSERT(other.swapchainCount == 1);
143         mPresentInfo.swapchainCount = 1;
144         mSwapchain                  = other.pSwapchains[0];
145         mPresentInfo.pSwapchains    = &mSwapchain;
146         mImageIndex                 = other.pImageIndices[0];
147         mPresentInfo.pImageIndices  = &mImageIndex;
148     }
149 
150     if (other.waitSemaphoreCount > 0)
151     {
152         ASSERT(other.waitSemaphoreCount == 1);
153         mPresentInfo.waitSemaphoreCount = 1;
154         mWaitSemaphore                  = other.pWaitSemaphores[0];
155         mPresentInfo.pWaitSemaphores    = &mWaitSemaphore;
156     }
157 
158     mPresentInfo.pResults = other.pResults;
159 
160     void *pNext = const_cast<void *>(other.pNext);
161     while (pNext != nullptr)
162     {
163         VkStructureType sType = *reinterpret_cast<VkStructureType *>(pNext);
164         switch (sType)
165         {
166             case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
167             {
168                 const VkPresentRegionsKHR *presentRegions =
169                     reinterpret_cast<VkPresentRegionsKHR *>(pNext);
170                 mPresentRegion = *presentRegions->pRegions;
171                 mRects.resize(mPresentRegion.rectangleCount);
172                 for (uint32_t i = 0; i < mPresentRegion.rectangleCount; i++)
173                 {
174                     mRects[i] = presentRegions->pRegions->pRectangles[i];
175                 }
176                 mPresentRegion.pRectangles = mRects.data();
177 
178                 mPresentRegions.sType          = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
179                 mPresentRegions.pNext          = presentRegions->pNext;
180                 mPresentRegions.swapchainCount = 1;
181                 mPresentRegions.pRegions       = &mPresentRegion;
182                 mPresentInfo.pNext             = &mPresentRegions;
183                 pNext                          = const_cast<void *>(presentRegions->pNext);
184                 break;
185             }
186             default:
187                 ERR() << "Unknown sType: " << sType << " in VkPresentInfoKHR.pNext chain";
188                 UNREACHABLE();
189                 break;
190         }
191     }
192 }
193 
initPresent(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo)194 void CommandProcessorTask::initPresent(egl::ContextPriority priority,
195                                        const VkPresentInfoKHR &presentInfo)
196 {
197     mTask     = CustomTask::Present;
198     mPriority = priority;
199     copyPresentInfo(presentInfo);
200 }
201 
initFinishToSerial(Serial serial)202 void CommandProcessorTask::initFinishToSerial(Serial serial)
203 {
204     // Note: sometimes the serial is not valid and that's okay, the finish will early exit in the
205     // TaskProcessor::finishToSerial
206     mTask   = CustomTask::FinishToSerial;
207     mSerial = serial;
208 }
209 
initWaitIdle()210 void CommandProcessorTask::initWaitIdle()
211 {
212     mTask = CustomTask::WaitIdle;
213 }
214 
initFlushAndQueueSubmit(const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const Semaphore * semaphore,bool hasProtectedContent,egl::ContextPriority priority,CommandPool * commandPool,GarbageList && currentGarbage,std::vector<CommandBuffer> && commandBuffersToReset,Serial submitQueueSerial)215 void CommandProcessorTask::initFlushAndQueueSubmit(
216     const std::vector<VkSemaphore> &waitSemaphores,
217     const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
218     const Semaphore *semaphore,
219     bool hasProtectedContent,
220     egl::ContextPriority priority,
221     CommandPool *commandPool,
222     GarbageList &&currentGarbage,
223     std::vector<CommandBuffer> &&commandBuffersToReset,
224     Serial submitQueueSerial)
225 {
226     mTask                    = CustomTask::FlushAndQueueSubmit;
227     mWaitSemaphores          = waitSemaphores;
228     mWaitSemaphoreStageMasks = waitSemaphoreStageMasks;
229     mSemaphore               = semaphore;
230     mCommandPool             = commandPool;
231     mGarbage                 = std::move(currentGarbage);
232     mCommandBuffersToReset   = std::move(commandBuffersToReset);
233     mPriority                = priority;
234     mHasProtectedContent     = hasProtectedContent;
235     mSerial                  = submitQueueSerial;
236 }
237 
initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,bool hasProtectedContent,egl::ContextPriority priority,const Semaphore * waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const Fence * fence,Serial submitQueueSerial)238 void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,
239                                                  bool hasProtectedContent,
240                                                  egl::ContextPriority priority,
241                                                  const Semaphore *waitSemaphore,
242                                                  VkPipelineStageFlags waitSemaphoreStageMask,
243                                                  const Fence *fence,
244                                                  Serial submitQueueSerial)
245 {
246     mTask                         = CustomTask::OneOffQueueSubmit;
247     mOneOffCommandBufferVk        = commandBufferHandle;
248     mOneOffWaitSemaphore          = waitSemaphore;
249     mOneOffWaitSemaphoreStageMask = waitSemaphoreStageMask;
250     mOneOffFence                  = fence;
251     mPriority                     = priority;
252     mHasProtectedContent          = hasProtectedContent;
253     mSerial                       = submitQueueSerial;
254 }
255 
operator =(CommandProcessorTask && rhs)256 CommandProcessorTask &CommandProcessorTask::operator=(CommandProcessorTask &&rhs)
257 {
258     if (this == &rhs)
259     {
260         return *this;
261     }
262 
263     std::swap(mRenderPass, rhs.mRenderPass);
264     std::swap(mCommandBuffer, rhs.mCommandBuffer);
265     std::swap(mTask, rhs.mTask);
266     std::swap(mWaitSemaphores, rhs.mWaitSemaphores);
267     std::swap(mWaitSemaphoreStageMasks, rhs.mWaitSemaphoreStageMasks);
268     std::swap(mSemaphore, rhs.mSemaphore);
269     std::swap(mOneOffWaitSemaphore, rhs.mOneOffWaitSemaphore);
270     std::swap(mOneOffWaitSemaphoreStageMask, rhs.mOneOffWaitSemaphoreStageMask);
271     std::swap(mOneOffFence, rhs.mOneOffFence);
272     std::swap(mCommandPool, rhs.mCommandPool);
273     std::swap(mGarbage, rhs.mGarbage);
274     std::swap(mCommandBuffersToReset, rhs.mCommandBuffersToReset);
275     std::swap(mSerial, rhs.mSerial);
276     std::swap(mPriority, rhs.mPriority);
277     std::swap(mHasProtectedContent, rhs.mHasProtectedContent);
278     std::swap(mOneOffCommandBufferVk, rhs.mOneOffCommandBufferVk);
279 
280     copyPresentInfo(rhs.mPresentInfo);
281 
282     // clear rhs now that everything has moved.
283     rhs.initTask();
284 
285     return *this;
286 }
287 
288 // CommandBatch implementation.
CommandBatch()289 CommandBatch::CommandBatch() : commandPool(nullptr), hasProtectedContent(false) {}
290 
291 CommandBatch::~CommandBatch() = default;
292 
CommandBatch(CommandBatch && other)293 CommandBatch::CommandBatch(CommandBatch &&other) : CommandBatch()
294 {
295     *this = std::move(other);
296 }
297 
operator =(CommandBatch && other)298 CommandBatch &CommandBatch::operator=(CommandBatch &&other)
299 {
300     std::swap(primaryCommands, other.primaryCommands);
301     std::swap(commandPool, other.commandPool);
302     std::swap(commandBuffersToReset, other.commandBuffersToReset);
303     std::swap(fence, other.fence);
304     std::swap(serial, other.serial);
305     std::swap(hasProtectedContent, other.hasProtectedContent);
306     return *this;
307 }
308 
destroy(VkDevice device)309 void CommandBatch::destroy(VkDevice device)
310 {
311     primaryCommands.destroy(device);
312     fence.reset(device);
313     hasProtectedContent = false;
314 }
315 
resetSecondaryCommandBuffers(VkDevice device)316 void CommandBatch::resetSecondaryCommandBuffers(VkDevice device)
317 {
318 #if !ANGLE_USE_CUSTOM_VULKAN_CMD_BUFFERS
319     for (CommandBuffer &secondary : commandBuffersToReset)
320     {
321         // Note: we currently free the command buffers individually, but we could potentially reset
322         // the entire command pool.  https://issuetracker.google.com/issues/166793850
323         commandPool->freeCommandBuffers(device, 1, secondary.ptr());
324         secondary.releaseHandle();
325     }
326     commandBuffersToReset.clear();
327 #endif
328 }
329 
330 // CommandProcessor implementation.
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)331 void CommandProcessor::handleError(VkResult errorCode,
332                                    const char *file,
333                                    const char *function,
334                                    unsigned int line)
335 {
336     ASSERT(errorCode != VK_SUCCESS);
337 
338     std::stringstream errorStream;
339     errorStream << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode)
340                 << ".";
341 
342     if (errorCode == VK_ERROR_DEVICE_LOST)
343     {
344         WARN() << errorStream.str();
345         handleDeviceLost(mRenderer);
346     }
347 
348     std::lock_guard<std::mutex> queueLock(mErrorMutex);
349     Error error = {errorCode, file, function, line};
350     mErrors.emplace(error);
351 }
352 
CommandProcessor(RendererVk * renderer)353 CommandProcessor::CommandProcessor(RendererVk *renderer)
354     : Context(renderer), mWorkerThreadIdle(false)
355 {
356     std::lock_guard<std::mutex> queueLock(mErrorMutex);
357     while (!mErrors.empty())
358     {
359         mErrors.pop();
360     }
361 }
362 
363 CommandProcessor::~CommandProcessor() = default;
364 
checkAndPopPendingError(Context * errorHandlingContext)365 angle::Result CommandProcessor::checkAndPopPendingError(Context *errorHandlingContext)
366 {
367     std::lock_guard<std::mutex> queueLock(mErrorMutex);
368     if (mErrors.empty())
369     {
370         return angle::Result::Continue;
371     }
372     else
373     {
374         Error err = mErrors.front();
375         mErrors.pop();
376         errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
377         return angle::Result::Stop;
378     }
379 }
380 
queueCommand(CommandProcessorTask && task)381 void CommandProcessor::queueCommand(CommandProcessorTask &&task)
382 {
383     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queueCommand");
384     // Grab the worker mutex so that we put things on the queue in the same order as we give out
385     // serials.
386     std::lock_guard<std::mutex> queueLock(mWorkerMutex);
387 
388     mTasks.emplace(std::move(task));
389     mWorkAvailableCondition.notify_one();
390 }
391 
processTasks()392 void CommandProcessor::processTasks()
393 {
394     while (true)
395     {
396         bool exitThread      = false;
397         angle::Result result = processTasksImpl(&exitThread);
398         if (exitThread)
399         {
400             // We are doing a controlled exit of the thread, break out of the while loop.
401             break;
402         }
403         if (result != angle::Result::Continue)
404         {
405             // TODO: https://issuetracker.google.com/issues/170311829 - follow-up on error handling
406             // ContextVk::commandProcessorSyncErrorsAndQueueCommand and WindowSurfaceVk::destroy
407             // do error processing, is anything required here? Don't think so, mostly need to
408             // continue the worker thread until it's been told to exit.
409             UNREACHABLE();
410         }
411     }
412 }
413 
processTasksImpl(bool * exitThread)414 angle::Result CommandProcessor::processTasksImpl(bool *exitThread)
415 {
416     while (true)
417     {
418         std::unique_lock<std::mutex> lock(mWorkerMutex);
419         if (mTasks.empty())
420         {
421             mWorkerThreadIdle = true;
422             mWorkerIdleCondition.notify_all();
423             // Only wake if notified and command queue is not empty
424             mWorkAvailableCondition.wait(lock, [this] { return !mTasks.empty(); });
425         }
426         mWorkerThreadIdle = false;
427         CommandProcessorTask task(std::move(mTasks.front()));
428         mTasks.pop();
429         lock.unlock();
430 
431         ANGLE_TRY(processTask(&task));
432         if (task.getTaskCommand() == CustomTask::Exit)
433         {
434 
435             *exitThread = true;
436             lock.lock();
437             mWorkerThreadIdle = true;
438             mWorkerIdleCondition.notify_one();
439             return angle::Result::Continue;
440         }
441     }
442 
443     UNREACHABLE();
444     return angle::Result::Stop;
445 }
446 
processTask(CommandProcessorTask * task)447 angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
448 {
449     switch (task->getTaskCommand())
450     {
451         case CustomTask::Exit:
452         {
453             ANGLE_TRY(mCommandQueue.finishToSerial(this, Serial::Infinite(),
454                                                    mRenderer->getMaxFenceWaitTimeNs()));
455             // Shutting down so cleanup
456             mCommandQueue.destroy(this);
457             break;
458         }
459         case CustomTask::FlushAndQueueSubmit:
460         {
461             ANGLE_TRACE_EVENT0("gpu.angle", "processTask::FlushAndQueueSubmit");
462             // End command buffer
463 
464             // Call submitFrame()
465             ANGLE_TRY(mCommandQueue.submitFrame(
466                 this, task->hasProtectedContent(), task->getPriority(), task->getWaitSemaphores(),
467                 task->getWaitSemaphoreStageMasks(), task->getSemaphore(),
468                 std::move(task->getGarbage()), std::move(task->getCommandBuffersToReset()),
469                 task->getCommandPool(), task->getQueueSerial()));
470 
471             ASSERT(task->getGarbage().empty());
472             break;
473         }
474         case CustomTask::OneOffQueueSubmit:
475         {
476             ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit");
477 
478             ANGLE_TRY(mCommandQueue.queueSubmitOneOff(
479                 this, task->hasProtectedContent(), task->getPriority(),
480                 task->getOneOffCommandBufferVk(), task->getOneOffWaitSemaphore(),
481                 task->getOneOffWaitSemaphoreStageMask(), task->getOneOffFence(),
482                 SubmitPolicy::EnsureSubmitted, task->getQueueSerial()));
483             ANGLE_TRY(mCommandQueue.checkCompletedCommands(this));
484             break;
485         }
486         case CustomTask::FinishToSerial:
487         {
488             ANGLE_TRY(mCommandQueue.finishToSerial(this, task->getQueueSerial(),
489                                                    mRenderer->getMaxFenceWaitTimeNs()));
490             break;
491         }
492         case CustomTask::WaitIdle:
493         {
494             ANGLE_TRY(mCommandQueue.waitIdle(this, mRenderer->getMaxFenceWaitTimeNs()));
495             break;
496         }
497         case CustomTask::Present:
498         {
499             VkResult result = present(task->getPriority(), task->getPresentInfo());
500             if (ANGLE_UNLIKELY(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR))
501             {
502                 // We get to ignore these as they are not fatal
503             }
504             else if (ANGLE_UNLIKELY(result != VK_SUCCESS))
505             {
506                 // Save the error so that we can handle it.
507                 // Don't leave processing loop, don't consider errors from present to be fatal.
508                 // TODO: https://issuetracker.google.com/issues/170329600 - This needs to improve to
509                 // properly parallelize present
510                 handleError(result, __FILE__, __FUNCTION__, __LINE__);
511             }
512             break;
513         }
514         case CustomTask::ProcessCommands:
515         {
516             ASSERT(!task->getCommandBuffer()->empty());
517 
518             CommandBufferHelper *commandBuffer = task->getCommandBuffer();
519             if (task->getRenderPass())
520             {
521                 ANGLE_TRY(mCommandQueue.flushRenderPassCommands(
522                     this, task->hasProtectedContent(), *task->getRenderPass(), &commandBuffer));
523             }
524             else
525             {
526                 ANGLE_TRY(mCommandQueue.flushOutsideRPCommands(this, task->hasProtectedContent(),
527                                                                &commandBuffer));
528             }
529 
530             CommandBufferHelper *originalCommandBuffer = task->getCommandBuffer();
531             ASSERT(originalCommandBuffer->empty());
532             mRenderer->recycleCommandBufferHelper(mRenderer->getDevice(), &originalCommandBuffer);
533             break;
534         }
535         case CustomTask::CheckCompletedCommands:
536         {
537             ANGLE_TRY(mCommandQueue.checkCompletedCommands(this));
538             break;
539         }
540         default:
541             UNREACHABLE();
542             break;
543     }
544 
545     return angle::Result::Continue;
546 }
547 
checkCompletedCommands(Context * context)548 angle::Result CommandProcessor::checkCompletedCommands(Context *context)
549 {
550     ANGLE_TRY(checkAndPopPendingError(context));
551 
552     CommandProcessorTask checkCompletedTask;
553     checkCompletedTask.initTask(CustomTask::CheckCompletedCommands);
554     queueCommand(std::move(checkCompletedTask));
555 
556     return angle::Result::Continue;
557 }
558 
waitForWorkComplete(Context * context)559 angle::Result CommandProcessor::waitForWorkComplete(Context *context)
560 {
561     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForWorkComplete");
562     std::unique_lock<std::mutex> lock(mWorkerMutex);
563     mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); });
564     // Worker thread is idle and command queue is empty so good to continue
565 
566     // Sync any errors to the context
567     bool shouldStop = hasPendingError();
568     while (hasPendingError())
569     {
570         (void)checkAndPopPendingError(context);
571     }
572     return shouldStop ? angle::Result::Stop : angle::Result::Continue;
573 }
574 
init(Context * context,const DeviceQueueMap & queueMap)575 angle::Result CommandProcessor::init(Context *context, const DeviceQueueMap &queueMap)
576 {
577     ANGLE_TRY(mCommandQueue.init(context, queueMap));
578 
579     mTaskThread = std::thread(&CommandProcessor::processTasks, this);
580 
581     return angle::Result::Continue;
582 }
583 
destroy(Context * context)584 void CommandProcessor::destroy(Context *context)
585 {
586     CommandProcessorTask endTask;
587     endTask.initTask(CustomTask::Exit);
588     queueCommand(std::move(endTask));
589     (void)waitForWorkComplete(context);
590     if (mTaskThread.joinable())
591     {
592         mTaskThread.join();
593     }
594 }
595 
getLastCompletedQueueSerial() const596 Serial CommandProcessor::getLastCompletedQueueSerial() const
597 {
598     std::lock_guard<std::mutex> lock(mQueueSerialMutex);
599     return mCommandQueue.getLastCompletedQueueSerial();
600 }
601 
isBusy() const602 bool CommandProcessor::isBusy() const
603 {
604     std::lock_guard<std::mutex> serialLock(mQueueSerialMutex);
605     std::lock_guard<std::mutex> workerLock(mWorkerMutex);
606     return !mTasks.empty() || mCommandQueue.isBusy();
607 }
608 
reserveSubmitSerial()609 Serial CommandProcessor::reserveSubmitSerial()
610 {
611     std::lock_guard<std::mutex> lock(mQueueSerialMutex);
612     return mCommandQueue.reserveSubmitSerial();
613 }
614 
615 // Wait until all commands up to and including serial have been processed
finishToSerial(Context * context,Serial serial,uint64_t timeout)616 angle::Result CommandProcessor::finishToSerial(Context *context, Serial serial, uint64_t timeout)
617 {
618     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::finishToSerial");
619 
620     ANGLE_TRY(checkAndPopPendingError(context));
621 
622     CommandProcessorTask task;
623     task.initFinishToSerial(serial);
624     queueCommand(std::move(task));
625 
626     // Wait until the worker is idle. At that point we know that the finishToSerial command has
627     // completed executing, including any associated state cleanup.
628     return waitForWorkComplete(context);
629 }
630 
waitIdle(Context * context,uint64_t timeout)631 angle::Result CommandProcessor::waitIdle(Context *context, uint64_t timeout)
632 {
633     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitIdle");
634 
635     CommandProcessorTask task;
636     task.initWaitIdle();
637     queueCommand(std::move(task));
638 
639     return waitForWorkComplete(context);
640 }
641 
handleDeviceLost(RendererVk * renderer)642 void CommandProcessor::handleDeviceLost(RendererVk *renderer)
643 {
644     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::handleDeviceLost");
645     std::unique_lock<std::mutex> lock(mWorkerMutex);
646     mWorkerIdleCondition.wait(lock, [this] { return (mTasks.empty() && mWorkerThreadIdle); });
647 
648     // Worker thread is idle and command queue is empty so good to continue
649     mCommandQueue.handleDeviceLost(renderer);
650 }
651 
getLastAndClearPresentResult(VkSwapchainKHR swapchain)652 VkResult CommandProcessor::getLastAndClearPresentResult(VkSwapchainKHR swapchain)
653 {
654     std::unique_lock<std::mutex> lock(mSwapchainStatusMutex);
655     if (mSwapchainStatus.find(swapchain) == mSwapchainStatus.end())
656     {
657         // Wake when required swapchain status becomes available
658         mSwapchainStatusCondition.wait(lock, [this, swapchain] {
659             return mSwapchainStatus.find(swapchain) != mSwapchainStatus.end();
660         });
661     }
662     VkResult result = mSwapchainStatus[swapchain];
663     mSwapchainStatus.erase(swapchain);
664     return result;
665 }
666 
present(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo)667 VkResult CommandProcessor::present(egl::ContextPriority priority,
668                                    const VkPresentInfoKHR &presentInfo)
669 {
670     std::lock_guard<std::mutex> lock(mSwapchainStatusMutex);
671     ANGLE_TRACE_EVENT0("gpu.angle", "vkQueuePresentKHR");
672     VkResult result = mCommandQueue.queuePresent(priority, presentInfo);
673 
674     // Verify that we are presenting one and only one swapchain
675     ASSERT(presentInfo.swapchainCount == 1);
676     ASSERT(presentInfo.pResults == nullptr);
677     mSwapchainStatus[presentInfo.pSwapchains[0]] = result;
678 
679     mSwapchainStatusCondition.notify_all();
680 
681     return result;
682 }
683 
submitFrame(Context * context,bool hasProtectedContent,egl::ContextPriority priority,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const Semaphore * signalSemaphore,GarbageList && currentGarbage,std::vector<CommandBuffer> && commandBuffersToReset,CommandPool * commandPool,Serial submitQueueSerial)684 angle::Result CommandProcessor::submitFrame(
685     Context *context,
686     bool hasProtectedContent,
687     egl::ContextPriority priority,
688     const std::vector<VkSemaphore> &waitSemaphores,
689     const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
690     const Semaphore *signalSemaphore,
691     GarbageList &&currentGarbage,
692     std::vector<CommandBuffer> &&commandBuffersToReset,
693     CommandPool *commandPool,
694     Serial submitQueueSerial)
695 {
696     ANGLE_TRY(checkAndPopPendingError(context));
697 
698     CommandProcessorTask task;
699     task.initFlushAndQueueSubmit(waitSemaphores, waitSemaphoreStageMasks, signalSemaphore,
700                                  hasProtectedContent, priority, commandPool,
701                                  std::move(currentGarbage), std::move(commandBuffersToReset),
702                                  submitQueueSerial);
703 
704     queueCommand(std::move(task));
705 
706     return angle::Result::Continue;
707 }
708 
queueSubmitOneOff(Context * context,bool hasProtectedContent,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,const Semaphore * waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const Fence * fence,SubmitPolicy submitPolicy,Serial submitQueueSerial)709 angle::Result CommandProcessor::queueSubmitOneOff(Context *context,
710                                                   bool hasProtectedContent,
711                                                   egl::ContextPriority contextPriority,
712                                                   VkCommandBuffer commandBufferHandle,
713                                                   const Semaphore *waitSemaphore,
714                                                   VkPipelineStageFlags waitSemaphoreStageMask,
715                                                   const Fence *fence,
716                                                   SubmitPolicy submitPolicy,
717                                                   Serial submitQueueSerial)
718 {
719     ANGLE_TRY(checkAndPopPendingError(context));
720 
721     CommandProcessorTask task;
722     task.initOneOffQueueSubmit(commandBufferHandle, hasProtectedContent, contextPriority,
723                                waitSemaphore, waitSemaphoreStageMask, fence, submitQueueSerial);
724     queueCommand(std::move(task));
725     if (submitPolicy == SubmitPolicy::EnsureSubmitted)
726     {
727         // Caller has synchronization requirement to have work in GPU pipe when returning from this
728         // function.
729         ANGLE_TRY(waitForWorkComplete(context));
730     }
731 
732     return angle::Result::Continue;
733 }
734 
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo)735 VkResult CommandProcessor::queuePresent(egl::ContextPriority contextPriority,
736                                         const VkPresentInfoKHR &presentInfo)
737 {
738     CommandProcessorTask task;
739     task.initPresent(contextPriority, presentInfo);
740 
741     ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queuePresent");
742     queueCommand(std::move(task));
743 
744     // Always return success, when we call acquireNextImage we'll check the return code. This
745     // allows the app to continue working until we really need to know the return code from
746     // present.
747     return VK_SUCCESS;
748 }
749 
waitForSerialWithUserTimeout(vk::Context * context,Serial serial,uint64_t timeout,VkResult * result)750 angle::Result CommandProcessor::waitForSerialWithUserTimeout(vk::Context *context,
751                                                              Serial serial,
752                                                              uint64_t timeout,
753                                                              VkResult *result)
754 {
755     // If finishToSerial times out we generate an error. Therefore we a large timeout.
756     // TODO: https://issuetracker.google.com/170312581 - Wait with timeout.
757     return finishToSerial(context, serial, mRenderer->getMaxFenceWaitTimeNs());
758 }
759 
flushOutsideRPCommands(Context * context,bool hasProtectedContent,CommandBufferHelper ** outsideRPCommands)760 angle::Result CommandProcessor::flushOutsideRPCommands(Context *context,
761                                                        bool hasProtectedContent,
762                                                        CommandBufferHelper **outsideRPCommands)
763 {
764     ANGLE_TRY(checkAndPopPendingError(context));
765 
766     (*outsideRPCommands)->markClosed();
767     CommandProcessorTask task;
768     task.initProcessCommands(hasProtectedContent, *outsideRPCommands, nullptr);
769     queueCommand(std::move(task));
770     return mRenderer->getCommandBufferHelper(context, false, (*outsideRPCommands)->getCommandPool(),
771                                              outsideRPCommands);
772 }
773 
flushRenderPassCommands(Context * context,bool hasProtectedContent,const RenderPass & renderPass,CommandBufferHelper ** renderPassCommands)774 angle::Result CommandProcessor::flushRenderPassCommands(Context *context,
775                                                         bool hasProtectedContent,
776                                                         const RenderPass &renderPass,
777                                                         CommandBufferHelper **renderPassCommands)
778 {
779     ANGLE_TRY(checkAndPopPendingError(context));
780 
781     (*renderPassCommands)->markClosed();
782     CommandProcessorTask task;
783     task.initProcessCommands(hasProtectedContent, *renderPassCommands, &renderPass);
784     queueCommand(std::move(task));
785     return mRenderer->getCommandBufferHelper(context, true, (*renderPassCommands)->getCommandPool(),
786                                              renderPassCommands);
787 }
788 
ensureNoPendingWork(Context * context)789 angle::Result CommandProcessor::ensureNoPendingWork(Context *context)
790 {
791     return waitForWorkComplete(context);
792 }
793 
794 // CommandQueue implementation.
CommandQueue()795 CommandQueue::CommandQueue() : mCurrentQueueSerial(mQueueSerialFactory.generate()) {}
796 
797 CommandQueue::~CommandQueue() = default;
798 
destroy(Context * context)799 void CommandQueue::destroy(Context *context)
800 {
801     // Force all commands to finish by flushing all queues.
802     for (VkQueue queue : mQueueMap)
803     {
804         if (queue != VK_NULL_HANDLE)
805         {
806             vkQueueWaitIdle(queue);
807         }
808     }
809 
810     RendererVk *renderer = context->getRenderer();
811 
812     mLastCompletedQueueSerial = Serial::Infinite();
813     (void)clearAllGarbage(renderer);
814 
815     mPrimaryCommands.destroy(renderer->getDevice());
816     mPrimaryCommandPool.destroy(renderer->getDevice());
817 
818     if (mProtectedPrimaryCommandPool.valid())
819     {
820         mProtectedPrimaryCommands.destroy(renderer->getDevice());
821         mProtectedPrimaryCommandPool.destroy(renderer->getDevice());
822     }
823 
824     mFenceRecycler.destroy(context);
825 
826     ASSERT(mInFlightCommands.empty() && mGarbageQueue.empty());
827 }
828 
init(Context * context,const vk::DeviceQueueMap & queueMap)829 angle::Result CommandQueue::init(Context *context, const vk::DeviceQueueMap &queueMap)
830 {
831     // Initialize the command pool now that we know the queue family index.
832     ANGLE_TRY(mPrimaryCommandPool.init(context, false, queueMap.getIndex()));
833     mQueueMap = queueMap;
834 
835     if (queueMap.isProtected())
836     {
837         ANGLE_TRY(mProtectedPrimaryCommandPool.init(context, true, queueMap.getIndex()));
838     }
839 
840     return angle::Result::Continue;
841 }
842 
checkCompletedCommands(Context * context)843 angle::Result CommandQueue::checkCompletedCommands(Context *context)
844 {
845     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::checkCompletedCommandsNoLock");
846     RendererVk *renderer = context->getRenderer();
847     VkDevice device      = renderer->getDevice();
848 
849     int finishedCount = 0;
850 
851     for (CommandBatch &batch : mInFlightCommands)
852     {
853         VkResult result = batch.fence.get().getStatus(device);
854         if (result == VK_NOT_READY)
855         {
856             break;
857         }
858         ANGLE_VK_TRY(context, result);
859         ++finishedCount;
860     }
861 
862     if (finishedCount == 0)
863     {
864         return angle::Result::Continue;
865     }
866 
867     return retireFinishedCommands(context, finishedCount);
868 }
869 
retireFinishedCommands(Context * context,size_t finishedCount)870 angle::Result CommandQueue::retireFinishedCommands(Context *context, size_t finishedCount)
871 {
872     ASSERT(finishedCount > 0);
873 
874     RendererVk *renderer = context->getRenderer();
875     VkDevice device      = renderer->getDevice();
876 
877     for (size_t commandIndex = 0; commandIndex < finishedCount; ++commandIndex)
878     {
879         CommandBatch &batch = mInFlightCommands[commandIndex];
880 
881         mLastCompletedQueueSerial = batch.serial;
882         mFenceRecycler.resetSharedFence(&batch.fence);
883         ANGLE_TRACE_EVENT0("gpu.angle", "command buffer recycling");
884         batch.resetSecondaryCommandBuffers(device);
885         PersistentCommandPool &commandPool = getCommandPool(batch.hasProtectedContent);
886         ANGLE_TRY(commandPool.collect(context, std::move(batch.primaryCommands)));
887     }
888 
889     if (finishedCount > 0)
890     {
891         auto beginIter = mInFlightCommands.begin();
892         mInFlightCommands.erase(beginIter, beginIter + finishedCount);
893     }
894 
895     size_t freeIndex = 0;
896     for (; freeIndex < mGarbageQueue.size(); ++freeIndex)
897     {
898         GarbageAndSerial &garbageList = mGarbageQueue[freeIndex];
899         if (garbageList.getSerial() < mLastCompletedQueueSerial)
900         {
901             for (GarbageObject &garbage : garbageList.get())
902             {
903                 garbage.destroy(renderer);
904             }
905         }
906         else
907         {
908             break;
909         }
910     }
911 
912     // Remove the entries from the garbage list - they should be ready to go.
913     if (freeIndex > 0)
914     {
915         mGarbageQueue.erase(mGarbageQueue.begin(), mGarbageQueue.begin() + freeIndex);
916     }
917 
918     return angle::Result::Continue;
919 }
920 
releaseToCommandBatch(bool hasProtectedContent,PrimaryCommandBuffer && commandBuffer,CommandPool * commandPool,CommandBatch * batch)921 void CommandQueue::releaseToCommandBatch(bool hasProtectedContent,
922                                          PrimaryCommandBuffer &&commandBuffer,
923                                          CommandPool *commandPool,
924                                          CommandBatch *batch)
925 {
926     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::releaseToCommandBatch");
927 
928     batch->primaryCommands     = std::move(commandBuffer);
929     batch->commandPool         = commandPool;
930     batch->hasProtectedContent = hasProtectedContent;
931 }
932 
clearAllGarbage(RendererVk * renderer)933 void CommandQueue::clearAllGarbage(RendererVk *renderer)
934 {
935     for (GarbageAndSerial &garbageList : mGarbageQueue)
936     {
937         for (GarbageObject &garbage : garbageList.get())
938         {
939             garbage.destroy(renderer);
940         }
941     }
942     mGarbageQueue.clear();
943 }
944 
handleDeviceLost(RendererVk * renderer)945 void CommandQueue::handleDeviceLost(RendererVk *renderer)
946 {
947     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::handleDeviceLost");
948 
949     VkDevice device = renderer->getDevice();
950 
951     for (CommandBatch &batch : mInFlightCommands)
952     {
953         // On device loss we need to wait for fence to be signaled before destroying it
954         VkResult status = batch.fence.get().wait(device, renderer->getMaxFenceWaitTimeNs());
955         // If the wait times out, it is probably not possible to recover from lost device
956         ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
957 
958         // On device lost, here simply destroy the CommandBuffer, it will fully cleared later
959         // by CommandPool::destroy
960         batch.primaryCommands.destroy(device);
961 
962         batch.resetSecondaryCommandBuffers(device);
963         batch.fence.reset(device);
964     }
965     mInFlightCommands.clear();
966 }
967 
allInFlightCommandsAreAfterSerial(Serial serial)968 bool CommandQueue::allInFlightCommandsAreAfterSerial(Serial serial)
969 {
970     return mInFlightCommands.empty() || mInFlightCommands[0].serial > serial;
971 }
972 
finishToSerial(Context * context,Serial finishSerial,uint64_t timeout)973 angle::Result CommandQueue::finishToSerial(Context *context, Serial finishSerial, uint64_t timeout)
974 {
975     if (mInFlightCommands.empty())
976     {
977         return angle::Result::Continue;
978     }
979 
980     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::finishToSerial");
981 
982     // Find the serial in the the list. The serials should be in order.
983     ASSERT(CommandsHaveValidOrdering(mInFlightCommands));
984 
985     size_t finishedCount = 0;
986     while (finishedCount < mInFlightCommands.size() &&
987            mInFlightCommands[finishedCount].serial <= finishSerial)
988     {
989         finishedCount++;
990     }
991 
992     if (finishedCount == 0)
993     {
994         return angle::Result::Continue;
995     }
996 
997     const CommandBatch &batch = mInFlightCommands[finishedCount - 1];
998 
999     // Wait for it finish
1000     VkDevice device = context->getDevice();
1001     VkResult status = batch.fence.get().wait(device, timeout);
1002 
1003     ANGLE_VK_TRY(context, status);
1004 
1005     // Clean up finished batches.
1006     ANGLE_TRY(retireFinishedCommands(context, finishedCount));
1007     ASSERT(allInFlightCommandsAreAfterSerial(finishSerial));
1008 
1009     return angle::Result::Continue;
1010 }
1011 
waitIdle(Context * context,uint64_t timeout)1012 angle::Result CommandQueue::waitIdle(Context *context, uint64_t timeout)
1013 {
1014     return finishToSerial(context, mLastSubmittedQueueSerial, timeout);
1015 }
1016 
reserveSubmitSerial()1017 Serial CommandQueue::reserveSubmitSerial()
1018 {
1019     Serial returnSerial = mCurrentQueueSerial;
1020     mCurrentQueueSerial = mQueueSerialFactory.generate();
1021     return returnSerial;
1022 }
1023 
submitFrame(Context * context,bool hasProtectedContent,egl::ContextPriority priority,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const Semaphore * signalSemaphore,GarbageList && currentGarbage,std::vector<CommandBuffer> && commandBuffersToReset,CommandPool * commandPool,Serial submitQueueSerial)1024 angle::Result CommandQueue::submitFrame(
1025     Context *context,
1026     bool hasProtectedContent,
1027     egl::ContextPriority priority,
1028     const std::vector<VkSemaphore> &waitSemaphores,
1029     const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
1030     const Semaphore *signalSemaphore,
1031     GarbageList &&currentGarbage,
1032     std::vector<CommandBuffer> &&commandBuffersToReset,
1033     CommandPool *commandPool,
1034     Serial submitQueueSerial)
1035 {
1036     // Start an empty primary buffer if we have an empty submit.
1037     PrimaryCommandBuffer &commandBuffer = getCommandBuffer(hasProtectedContent);
1038     ANGLE_TRY(ensurePrimaryCommandBufferValid(context, hasProtectedContent));
1039     ANGLE_VK_TRY(context, commandBuffer.end());
1040 
1041     VkSubmitInfo submitInfo = {};
1042     InitializeSubmitInfo(&submitInfo, commandBuffer, waitSemaphores, waitSemaphoreStageMasks,
1043                          signalSemaphore);
1044 
1045     VkProtectedSubmitInfo protectedSubmitInfo = {};
1046     if (hasProtectedContent)
1047     {
1048         protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1049         protectedSubmitInfo.pNext           = nullptr;
1050         protectedSubmitInfo.protectedSubmit = true;
1051         submitInfo.pNext                    = &protectedSubmitInfo;
1052     }
1053 
1054     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitFrame");
1055 
1056     RendererVk *renderer = context->getRenderer();
1057     VkDevice device      = renderer->getDevice();
1058 
1059     DeviceScoped<CommandBatch> scopedBatch(device);
1060     CommandBatch &batch = scopedBatch.get();
1061 
1062     ANGLE_TRY(mFenceRecycler.newSharedFence(context, &batch.fence));
1063     batch.serial                = submitQueueSerial;
1064     batch.hasProtectedContent   = hasProtectedContent;
1065     batch.commandBuffersToReset = std::move(commandBuffersToReset);
1066 
1067     ANGLE_TRY(queueSubmit(context, priority, submitInfo, &batch.fence.get(), batch.serial));
1068 
1069     if (!currentGarbage.empty())
1070     {
1071         mGarbageQueue.emplace_back(std::move(currentGarbage), batch.serial);
1072     }
1073 
1074     // Store the primary CommandBuffer and command pool used for secondary CommandBuffers
1075     // in the in-flight list.
1076     if (hasProtectedContent)
1077     {
1078         releaseToCommandBatch(hasProtectedContent, std::move(mProtectedPrimaryCommands),
1079                               commandPool, &batch);
1080     }
1081     else
1082     {
1083         releaseToCommandBatch(hasProtectedContent, std::move(mPrimaryCommands), commandPool,
1084                               &batch);
1085     }
1086     mInFlightCommands.emplace_back(scopedBatch.release());
1087 
1088     ANGLE_TRY(checkCompletedCommands(context));
1089 
1090     // CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
1091     // off-screen scenarios.
1092     if (mInFlightCommands.size() > kInFlightCommandsLimit)
1093     {
1094         size_t numCommandsToFinish = mInFlightCommands.size() - kInFlightCommandsLimit;
1095         Serial finishSerial        = mInFlightCommands[numCommandsToFinish].serial;
1096         ANGLE_TRY(finishToSerial(context, finishSerial, renderer->getMaxFenceWaitTimeNs()));
1097     }
1098 
1099     return angle::Result::Continue;
1100 }
1101 
waitForSerialWithUserTimeout(vk::Context * context,Serial serial,uint64_t timeout,VkResult * result)1102 angle::Result CommandQueue::waitForSerialWithUserTimeout(vk::Context *context,
1103                                                          Serial serial,
1104                                                          uint64_t timeout,
1105                                                          VkResult *result)
1106 {
1107     // No in-flight work. This indicates the serial is already complete.
1108     if (mInFlightCommands.empty())
1109     {
1110         *result = VK_SUCCESS;
1111         return angle::Result::Continue;
1112     }
1113 
1114     // Serial is already complete.
1115     if (serial < mInFlightCommands[0].serial)
1116     {
1117         *result = VK_SUCCESS;
1118         return angle::Result::Continue;
1119     }
1120 
1121     size_t batchIndex = 0;
1122     while (batchIndex != mInFlightCommands.size() && mInFlightCommands[batchIndex].serial < serial)
1123     {
1124         batchIndex++;
1125     }
1126 
1127     // Serial is not yet submitted. This is undefined behaviour, so we can do anything.
1128     if (batchIndex >= mInFlightCommands.size())
1129     {
1130         WARN() << "Waiting on an unsubmitted serial.";
1131         *result = VK_TIMEOUT;
1132         return angle::Result::Continue;
1133     }
1134 
1135     ASSERT(serial == mInFlightCommands[batchIndex].serial);
1136 
1137     vk::Fence &fence = mInFlightCommands[batchIndex].fence.get();
1138     ASSERT(fence.valid());
1139     *result = fence.wait(context->getDevice(), timeout);
1140 
1141     // Don't trigger an error on timeout.
1142     if (*result != VK_TIMEOUT)
1143     {
1144         ANGLE_VK_TRY(context, *result);
1145     }
1146 
1147     return angle::Result::Continue;
1148 }
1149 
ensurePrimaryCommandBufferValid(Context * context,bool hasProtectedContent)1150 angle::Result CommandQueue::ensurePrimaryCommandBufferValid(Context *context,
1151                                                             bool hasProtectedContent)
1152 {
1153     PersistentCommandPool &commandPool  = getCommandPool(hasProtectedContent);
1154     PrimaryCommandBuffer &commandBuffer = getCommandBuffer(hasProtectedContent);
1155 
1156     if (commandBuffer.valid())
1157     {
1158         return angle::Result::Continue;
1159     }
1160 
1161     ANGLE_TRY(commandPool.allocate(context, &commandBuffer));
1162     VkCommandBufferBeginInfo beginInfo = {};
1163     beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1164     beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1165     beginInfo.pInheritanceInfo         = nullptr;
1166     ANGLE_VK_TRY(context, commandBuffer.begin(beginInfo));
1167 
1168     return angle::Result::Continue;
1169 }
1170 
flushOutsideRPCommands(Context * context,bool hasProtectedContent,CommandBufferHelper ** outsideRPCommands)1171 angle::Result CommandQueue::flushOutsideRPCommands(Context *context,
1172                                                    bool hasProtectedContent,
1173                                                    CommandBufferHelper **outsideRPCommands)
1174 {
1175     ANGLE_TRY(ensurePrimaryCommandBufferValid(context, hasProtectedContent));
1176     PrimaryCommandBuffer &commandBuffer = getCommandBuffer(hasProtectedContent);
1177     return (*outsideRPCommands)->flushToPrimary(context, &commandBuffer, nullptr);
1178 }
1179 
flushRenderPassCommands(Context * context,bool hasProtectedContent,const RenderPass & renderPass,CommandBufferHelper ** renderPassCommands)1180 angle::Result CommandQueue::flushRenderPassCommands(Context *context,
1181                                                     bool hasProtectedContent,
1182                                                     const RenderPass &renderPass,
1183                                                     CommandBufferHelper **renderPassCommands)
1184 {
1185     ANGLE_TRY(ensurePrimaryCommandBufferValid(context, hasProtectedContent));
1186     PrimaryCommandBuffer &commandBuffer = getCommandBuffer(hasProtectedContent);
1187     return (*renderPassCommands)->flushToPrimary(context, &commandBuffer, &renderPass);
1188 }
1189 
queueSubmitOneOff(Context * context,bool hasProtectedContent,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,const Semaphore * waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const Fence * fence,SubmitPolicy submitPolicy,Serial submitQueueSerial)1190 angle::Result CommandQueue::queueSubmitOneOff(Context *context,
1191                                               bool hasProtectedContent,
1192                                               egl::ContextPriority contextPriority,
1193                                               VkCommandBuffer commandBufferHandle,
1194                                               const Semaphore *waitSemaphore,
1195                                               VkPipelineStageFlags waitSemaphoreStageMask,
1196                                               const Fence *fence,
1197                                               SubmitPolicy submitPolicy,
1198                                               Serial submitQueueSerial)
1199 {
1200     VkSubmitInfo submitInfo = {};
1201     submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1202 
1203     VkProtectedSubmitInfo protectedSubmitInfo = {};
1204     if (hasProtectedContent)
1205     {
1206         protectedSubmitInfo.sType           = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1207         protectedSubmitInfo.pNext           = nullptr;
1208         protectedSubmitInfo.protectedSubmit = true;
1209         submitInfo.pNext                    = &protectedSubmitInfo;
1210     }
1211 
1212     if (commandBufferHandle != VK_NULL_HANDLE)
1213     {
1214         submitInfo.commandBufferCount = 1;
1215         submitInfo.pCommandBuffers    = &commandBufferHandle;
1216     }
1217 
1218     if (waitSemaphore != nullptr)
1219     {
1220         submitInfo.waitSemaphoreCount = 1;
1221         submitInfo.pWaitSemaphores    = waitSemaphore->ptr();
1222         submitInfo.pWaitDstStageMask  = &waitSemaphoreStageMask;
1223     }
1224 
1225     return queueSubmit(context, contextPriority, submitInfo, fence, submitQueueSerial);
1226 }
1227 
queueSubmit(Context * context,egl::ContextPriority contextPriority,const VkSubmitInfo & submitInfo,const Fence * fence,Serial submitQueueSerial)1228 angle::Result CommandQueue::queueSubmit(Context *context,
1229                                         egl::ContextPriority contextPriority,
1230                                         const VkSubmitInfo &submitInfo,
1231                                         const Fence *fence,
1232                                         Serial submitQueueSerial)
1233 {
1234     ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::queueSubmit");
1235 
1236     RendererVk *renderer = context->getRenderer();
1237 
1238     if (kOutputVmaStatsString)
1239     {
1240         renderer->outputVmaStatString();
1241     }
1242 
1243     VkFence fenceHandle = fence ? fence->getHandle() : VK_NULL_HANDLE;
1244     VkQueue queue       = getQueue(contextPriority);
1245     ANGLE_VK_TRY(context, vkQueueSubmit(queue, 1, &submitInfo, fenceHandle));
1246     mLastSubmittedQueueSerial = submitQueueSerial;
1247 
1248     // Now that we've submitted work, clean up RendererVk garbage
1249     return renderer->cleanupGarbage(mLastCompletedQueueSerial);
1250 }
1251 
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo)1252 VkResult CommandQueue::queuePresent(egl::ContextPriority contextPriority,
1253                                     const VkPresentInfoKHR &presentInfo)
1254 {
1255     VkQueue queue = getQueue(contextPriority);
1256     return vkQueuePresentKHR(queue, &presentInfo);
1257 }
1258 
getLastCompletedQueueSerial() const1259 Serial CommandQueue::getLastCompletedQueueSerial() const
1260 {
1261     return mLastCompletedQueueSerial;
1262 }
1263 
isBusy() const1264 bool CommandQueue::isBusy() const
1265 {
1266     return mLastSubmittedQueueSerial > mLastCompletedQueueSerial;
1267 }
1268 
1269 // QueuePriorities:
1270 constexpr float kVulkanQueuePriorityLow    = 0.0;
1271 constexpr float kVulkanQueuePriorityMedium = 0.4;
1272 constexpr float kVulkanQueuePriorityHigh   = 1.0;
1273 
1274 const float QueueFamily::kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] =
1275     {kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
1276 
getDevicePriority(egl::ContextPriority priority) const1277 egl::ContextPriority DeviceQueueMap::getDevicePriority(egl::ContextPriority priority) const
1278 {
1279     return mPriorities[priority];
1280 }
1281 
~DeviceQueueMap()1282 DeviceQueueMap::~DeviceQueueMap() {}
1283 
operator =(const DeviceQueueMap & other)1284 DeviceQueueMap &DeviceQueueMap::operator=(const DeviceQueueMap &other)
1285 {
1286     ASSERT(this != &other);
1287     if ((this != &other) && other.valid())
1288     {
1289         mIndex                                    = other.mIndex;
1290         mIsProtected                              = other.mIsProtected;
1291         mPriorities[egl::ContextPriority::Low]    = other.mPriorities[egl::ContextPriority::Low];
1292         mPriorities[egl::ContextPriority::Medium] = other.mPriorities[egl::ContextPriority::Medium];
1293         mPriorities[egl::ContextPriority::High]   = other.mPriorities[egl::ContextPriority::High];
1294         *static_cast<angle::PackedEnumMap<egl::ContextPriority, VkQueue> *>(this) = other;
1295     }
1296     return *this;
1297 }
1298 
getDeviceQueue(VkDevice device,bool makeProtected,uint32_t queueIndex,VkQueue * queue)1299 void QueueFamily::getDeviceQueue(VkDevice device,
1300                                  bool makeProtected,
1301                                  uint32_t queueIndex,
1302                                  VkQueue *queue)
1303 {
1304     if (makeProtected)
1305     {
1306         VkDeviceQueueInfo2 queueInfo2 = {};
1307         queueInfo2.sType              = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
1308         queueInfo2.flags              = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
1309         queueInfo2.queueFamilyIndex   = mIndex;
1310         queueInfo2.queueIndex         = queueIndex;
1311 
1312         vkGetDeviceQueue2(device, &queueInfo2, queue);
1313     }
1314     else
1315     {
1316         vkGetDeviceQueue(device, mIndex, queueIndex, queue);
1317     }
1318 }
1319 
initializeQueueMap(VkDevice device,bool makeProtected,uint32_t queueIndex,uint32_t queueCount)1320 DeviceQueueMap QueueFamily::initializeQueueMap(VkDevice device,
1321                                                bool makeProtected,
1322                                                uint32_t queueIndex,
1323                                                uint32_t queueCount)
1324 {
1325     // QueueIndexing:
1326     constexpr uint32_t kQueueIndexMedium = 0;
1327     constexpr uint32_t kQueueIndexHigh   = 1;
1328     constexpr uint32_t kQueueIndexLow    = 2;
1329 
1330     ASSERT(queueCount);
1331     ASSERT((queueIndex + queueCount) <= mProperties.queueCount);
1332     DeviceQueueMap queueMap(mIndex, makeProtected);
1333 
1334     getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexMedium,
1335                    &queueMap[egl::ContextPriority::Medium]);
1336     queueMap.mPriorities[egl::ContextPriority::Medium] = egl::ContextPriority::Medium;
1337 
1338     // If at least 2 queues, High has its own queue
1339     if (queueCount > 1)
1340     {
1341         getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexHigh,
1342                        &queueMap[egl::ContextPriority::High]);
1343         queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::High;
1344     }
1345     else
1346     {
1347         queueMap[egl::ContextPriority::High]             = queueMap[egl::ContextPriority::Medium];
1348         queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::Medium;
1349     }
1350     // If at least 3 queues, Low has its own queue. Adjust Low priority.
1351     if (queueCount > 2)
1352     {
1353         getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexLow,
1354                        &queueMap[egl::ContextPriority::Low]);
1355         queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Low;
1356     }
1357     else
1358     {
1359         queueMap[egl::ContextPriority::Low]             = queueMap[egl::ContextPriority::Medium];
1360         queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Medium;
1361     }
1362     return queueMap;
1363 }
1364 
initialize(const VkQueueFamilyProperties & queueFamilyProperties,uint32_t index)1365 void QueueFamily::initialize(const VkQueueFamilyProperties &queueFamilyProperties, uint32_t index)
1366 {
1367     mProperties = queueFamilyProperties;
1368     mIndex      = index;
1369 }
1370 
FindIndex(const std::vector<VkQueueFamilyProperties> & queueFamilyProperties,VkQueueFlags flags,int32_t matchNumber,uint32_t * matchCount)1371 uint32_t QueueFamily::FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties,
1372                                 VkQueueFlags flags,
1373                                 int32_t matchNumber,
1374                                 uint32_t *matchCount)
1375 {
1376     uint32_t index = QueueFamily::kInvalidIndex;
1377     uint32_t count = 0;
1378 
1379     for (uint32_t familyIndex = 0; familyIndex < queueFamilyProperties.size(); ++familyIndex)
1380     {
1381         const auto &queueInfo = queueFamilyProperties[familyIndex];
1382         if ((queueInfo.queueFlags & flags) == flags)
1383         {
1384             ASSERT(queueInfo.queueCount > 0);
1385             count++;
1386             if ((index == QueueFamily::kInvalidIndex) && (matchNumber-- == 0))
1387             {
1388                 index = familyIndex;
1389             }
1390         }
1391     }
1392     if (matchCount)
1393     {
1394         *matchCount = count;
1395     }
1396 
1397     return index;
1398 }
1399 
1400 }  // namespace vk
1401 }  // namespace rx
1402