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