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 &¤tGarbage,
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 &¤tGarbage,
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 &¤tGarbage,
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