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