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 "common/system_utils.h"
12 #include "libANGLE/renderer/vulkan/RendererVk.h"
13 #include "libANGLE/renderer/vulkan/SyncVk.h"
14
15 namespace rx
16 {
17 namespace vk
18 {
19 namespace
20 {
21 constexpr bool kOutputVmaStatsString = false;
22 // When suballocation garbages is more than this, we may wait for GPU to finish and free up some
23 // memory for allocation.
24 constexpr VkDeviceSize kMaxBufferSuballocationGarbageSize = 64 * 1024 * 1024;
25
InitializeSubmitInfo(VkSubmitInfo * submitInfo,const PrimaryCommandBuffer & commandBuffer,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const VkSemaphore & signalSemaphore)26 void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
27 const PrimaryCommandBuffer &commandBuffer,
28 const std::vector<VkSemaphore> &waitSemaphores,
29 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
30 const VkSemaphore &signalSemaphore)
31 {
32 // Verify that the submitInfo has been zero'd out.
33 ASSERT(submitInfo->signalSemaphoreCount == 0);
34 ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
35 submitInfo->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
36 submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
37 submitInfo->pCommandBuffers = commandBuffer.ptr();
38 submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
39 submitInfo->pWaitSemaphores = waitSemaphores.empty() ? nullptr : waitSemaphores.data();
40 submitInfo->pWaitDstStageMask = waitSemaphoreStageMasks.data();
41
42 if (signalSemaphore != VK_NULL_HANDLE)
43 {
44 submitInfo->signalSemaphoreCount = 1;
45 submitInfo->pSignalSemaphores = &signalSemaphore;
46 }
47 }
48 } // namespace
49
50 // SharedFence implementation
SharedFence()51 SharedFence::SharedFence() : mRefCountedFence(nullptr), mRecycler(nullptr) {}
SharedFence(const SharedFence & other)52 SharedFence::SharedFence(const SharedFence &other)
53 : mRefCountedFence(other.mRefCountedFence), mRecycler(other.mRecycler)
54 {
55 if (mRefCountedFence != nullptr)
56 {
57 mRefCountedFence->addRef();
58 }
59 }
SharedFence(SharedFence && other)60 SharedFence::SharedFence(SharedFence &&other)
61 : mRefCountedFence(other.mRefCountedFence), mRecycler(other.mRecycler)
62 {
63 other.mRecycler = nullptr;
64 other.mRefCountedFence = nullptr;
65 }
66
~SharedFence()67 SharedFence::~SharedFence()
68 {
69 release();
70 }
71
init(VkDevice device,FenceRecycler * recycler)72 VkResult SharedFence::init(VkDevice device, FenceRecycler *recycler)
73 {
74 ASSERT(mRecycler == nullptr && mRefCountedFence == nullptr);
75 Fence fence;
76
77 // First try to fetch from recycler. If that failed, try to create a new VkFence
78 recycler->fetch(device, &fence);
79 if (!fence.valid())
80 {
81 VkFenceCreateInfo fenceCreateInfo = {};
82 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
83 fenceCreateInfo.flags = 0;
84 VkResult result = fence.init(device, fenceCreateInfo);
85 if (result != VK_SUCCESS)
86 {
87 return result;
88 }
89 }
90
91 // Create a new refcounted object to hold onto VkFence
92 mRefCountedFence = new RefCounted<Fence>(std::move(fence));
93 mRefCountedFence->addRef();
94 mRecycler = recycler;
95
96 return VK_SUCCESS;
97 }
98
operator =(const SharedFence & other)99 SharedFence &SharedFence::operator=(const SharedFence &other)
100 {
101 release();
102
103 mRecycler = other.mRecycler;
104 if (other.mRefCountedFence != nullptr)
105 {
106 mRefCountedFence = other.mRefCountedFence;
107 mRefCountedFence->addRef();
108 }
109 return *this;
110 }
111
operator =(SharedFence && other)112 SharedFence &SharedFence::operator=(SharedFence &&other)
113 {
114 release();
115 mRecycler = other.mRecycler;
116 mRefCountedFence = other.mRefCountedFence;
117 other.mRecycler = nullptr;
118 other.mRefCountedFence = nullptr;
119 return *this;
120 }
121
destroy(VkDevice device)122 void SharedFence::destroy(VkDevice device)
123 {
124 if (mRefCountedFence != nullptr)
125 {
126 mRefCountedFence->releaseRef();
127 if (!mRefCountedFence->isReferenced())
128 {
129 mRefCountedFence->get().destroy(device);
130 SafeDelete(mRefCountedFence);
131 }
132 else
133 {
134 mRefCountedFence = nullptr;
135 }
136 mRecycler = nullptr;
137 }
138 }
139
release()140 void SharedFence::release()
141 {
142 if (mRefCountedFence != nullptr)
143 {
144 mRefCountedFence->releaseRef();
145 if (!mRefCountedFence->isReferenced())
146 {
147 mRecycler->recycle(std::move(mRefCountedFence->get()));
148 ASSERT(!mRefCountedFence->get().valid());
149 SafeDelete(mRefCountedFence);
150 }
151 else
152 {
153 mRefCountedFence = nullptr;
154 }
155 mRecycler = nullptr;
156 }
157 }
158
operator bool() const159 SharedFence::operator bool() const
160 {
161 ASSERT(mRefCountedFence == nullptr || mRefCountedFence->isReferenced());
162 return mRefCountedFence != nullptr;
163 }
164
getStatus(VkDevice device) const165 VkResult SharedFence::getStatus(VkDevice device) const
166 {
167 if (mRefCountedFence != nullptr)
168 {
169 return mRefCountedFence->get().getStatus(device);
170 }
171 return VK_SUCCESS;
172 }
173
wait(VkDevice device,uint64_t timeout) const174 VkResult SharedFence::wait(VkDevice device, uint64_t timeout) const
175 {
176 if (mRefCountedFence != nullptr)
177 {
178 ANGLE_TRACE_EVENT0("gpu.angle", "SharedFence::wait");
179 return mRefCountedFence->get().wait(device, timeout);
180 }
181 return VK_SUCCESS;
182 }
183
184 // FenceRecycler implementation
destroy(Context * context)185 void FenceRecycler::destroy(Context *context)
186 {
187 std::lock_guard<std::mutex> lock(mMutex);
188 mRecyler.destroy(context->getDevice());
189 }
190
fetch(VkDevice device,Fence * fenceOut)191 void FenceRecycler::fetch(VkDevice device, Fence *fenceOut)
192 {
193 ASSERT(fenceOut != nullptr && !fenceOut->valid());
194 std::lock_guard<std::mutex> lock(mMutex);
195 if (!mRecyler.empty())
196 {
197 mRecyler.fetch(fenceOut);
198 fenceOut->reset(device);
199 }
200 }
201
recycle(Fence && fence)202 void FenceRecycler::recycle(Fence &&fence)
203 {
204 std::lock_guard<std::mutex> lock(mMutex);
205 mRecyler.recycle(std::move(fence));
206 }
207
208 // CommandProcessorTask implementation
initTask()209 void CommandProcessorTask::initTask()
210 {
211 mTask = CustomTask::Invalid;
212 mOutsideRenderPassCommandBuffer = nullptr;
213 mRenderPassCommandBuffer = nullptr;
214 mRenderPass = nullptr;
215 mSemaphore = VK_NULL_HANDLE;
216 mOneOffWaitSemaphore = VK_NULL_HANDLE;
217 mOneOffWaitSemaphoreStageMask = 0;
218 mPresentInfo = {};
219 mPresentInfo.pResults = nullptr;
220 mPresentInfo.pSwapchains = nullptr;
221 mPresentInfo.pImageIndices = nullptr;
222 mPresentInfo.pNext = nullptr;
223 mPresentInfo.pWaitSemaphores = nullptr;
224 mPresentFence = VK_NULL_HANDLE;
225 mSwapchainStatus = nullptr;
226 mOneOffCommandBuffer = VK_NULL_HANDLE;
227 mPriority = egl::ContextPriority::Medium;
228 mProtectionType = ProtectionType::InvalidEnum;
229 }
230
initFlushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)231 void CommandProcessorTask::initFlushWaitSemaphores(
232 ProtectionType protectionType,
233 egl::ContextPriority priority,
234 std::vector<VkSemaphore> &&waitSemaphores,
235 std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
236 {
237 mTask = CustomTask::FlushWaitSemaphores;
238 mPriority = priority;
239 mProtectionType = protectionType;
240 mWaitSemaphores = std::move(waitSemaphores);
241 mWaitSemaphoreStageMasks = std::move(waitSemaphoreStageMasks);
242 }
243
initOutsideRenderPassProcessCommands(ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper * commandBuffer)244 void CommandProcessorTask::initOutsideRenderPassProcessCommands(
245 ProtectionType protectionType,
246 egl::ContextPriority priority,
247 OutsideRenderPassCommandBufferHelper *commandBuffer)
248 {
249 mTask = CustomTask::ProcessOutsideRenderPassCommands;
250 mOutsideRenderPassCommandBuffer = commandBuffer;
251 mPriority = priority;
252 mProtectionType = protectionType;
253 }
254
initRenderPassProcessCommands(ProtectionType protectionType,egl::ContextPriority priority,RenderPassCommandBufferHelper * commandBuffer,const RenderPass * renderPass)255 void CommandProcessorTask::initRenderPassProcessCommands(
256 ProtectionType protectionType,
257 egl::ContextPriority priority,
258 RenderPassCommandBufferHelper *commandBuffer,
259 const RenderPass *renderPass)
260 {
261 mTask = CustomTask::ProcessRenderPassCommands;
262 mRenderPassCommandBuffer = commandBuffer;
263 mRenderPass = renderPass;
264 mPriority = priority;
265 mProtectionType = protectionType;
266 }
267
copyPresentInfo(const VkPresentInfoKHR & other)268 void CommandProcessorTask::copyPresentInfo(const VkPresentInfoKHR &other)
269 {
270 if (other.sType == 0)
271 {
272 return;
273 }
274
275 mPresentInfo.sType = other.sType;
276 mPresentInfo.pNext = nullptr;
277
278 if (other.swapchainCount > 0)
279 {
280 ASSERT(other.swapchainCount == 1);
281 mPresentInfo.swapchainCount = 1;
282 mSwapchain = other.pSwapchains[0];
283 mPresentInfo.pSwapchains = &mSwapchain;
284 mImageIndex = other.pImageIndices[0];
285 mPresentInfo.pImageIndices = &mImageIndex;
286 }
287
288 if (other.waitSemaphoreCount > 0)
289 {
290 ASSERT(other.waitSemaphoreCount == 1);
291 mPresentInfo.waitSemaphoreCount = 1;
292 mWaitSemaphore = other.pWaitSemaphores[0];
293 mPresentInfo.pWaitSemaphores = &mWaitSemaphore;
294 }
295
296 mPresentInfo.pResults = other.pResults;
297
298 void *pNext = const_cast<void *>(other.pNext);
299 while (pNext != nullptr)
300 {
301 VkStructureType sType = *reinterpret_cast<VkStructureType *>(pNext);
302 switch (sType)
303 {
304 case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
305 {
306 const VkPresentRegionsKHR *presentRegions =
307 reinterpret_cast<VkPresentRegionsKHR *>(pNext);
308 mPresentRegion = *presentRegions->pRegions;
309 mRects.resize(mPresentRegion.rectangleCount);
310 for (uint32_t i = 0; i < mPresentRegion.rectangleCount; i++)
311 {
312 mRects[i] = presentRegions->pRegions->pRectangles[i];
313 }
314 mPresentRegion.pRectangles = mRects.data();
315
316 mPresentRegions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
317 mPresentRegions.pNext = nullptr;
318 mPresentRegions.swapchainCount = 1;
319 mPresentRegions.pRegions = &mPresentRegion;
320 AddToPNextChain(&mPresentInfo, &mPresentRegions);
321 pNext = const_cast<void *>(presentRegions->pNext);
322 break;
323 }
324 case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT:
325 {
326 const VkSwapchainPresentFenceInfoEXT *presentFenceInfo =
327 reinterpret_cast<VkSwapchainPresentFenceInfoEXT *>(pNext);
328 ASSERT(presentFenceInfo->swapchainCount == 1);
329 mPresentFence = presentFenceInfo->pFences[0];
330
331 mPresentFenceInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT;
332 mPresentFenceInfo.pNext = nullptr;
333 mPresentFenceInfo.swapchainCount = 1;
334 mPresentFenceInfo.pFences = &mPresentFence;
335 AddToPNextChain(&mPresentInfo, &mPresentFenceInfo);
336 pNext = const_cast<void *>(presentFenceInfo->pNext);
337 break;
338 }
339 case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT:
340 {
341 const VkSwapchainPresentModeInfoEXT *presentModeInfo =
342 reinterpret_cast<VkSwapchainPresentModeInfoEXT *>(pNext);
343 ASSERT(presentModeInfo->swapchainCount == 1);
344 mPresentMode = presentModeInfo->pPresentModes[0];
345
346 mPresentModeInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT;
347 mPresentModeInfo.pNext = nullptr;
348 mPresentModeInfo.swapchainCount = 1;
349 mPresentModeInfo.pPresentModes = &mPresentMode;
350 AddToPNextChain(&mPresentInfo, &mPresentModeInfo);
351 pNext = const_cast<void *>(presentModeInfo->pNext);
352 break;
353 }
354 default:
355 ERR() << "Unknown sType: " << sType << " in VkPresentInfoKHR.pNext chain";
356 UNREACHABLE();
357 break;
358 }
359 }
360 }
361
initPresent(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)362 void CommandProcessorTask::initPresent(egl::ContextPriority priority,
363 const VkPresentInfoKHR &presentInfo,
364 SwapchainStatus *swapchainStatus)
365 {
366 mTask = CustomTask::Present;
367 mPriority = priority;
368 mSwapchainStatus = swapchainStatus;
369 copyPresentInfo(presentInfo);
370 }
371
initFlushAndQueueSubmit(VkSemaphore semaphore,SharedExternalFence && externalFence,ProtectionType protectionType,egl::ContextPriority priority,const QueueSerial & submitQueueSerial)372 void CommandProcessorTask::initFlushAndQueueSubmit(VkSemaphore semaphore,
373 SharedExternalFence &&externalFence,
374 ProtectionType protectionType,
375 egl::ContextPriority priority,
376 const QueueSerial &submitQueueSerial)
377 {
378 mTask = CustomTask::FlushAndQueueSubmit;
379 mSemaphore = semaphore;
380 mExternalFence = std::move(externalFence);
381 mPriority = priority;
382 mProtectionType = protectionType;
383 mSubmitQueueSerial = submitQueueSerial;
384 }
385
initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const QueueSerial & submitQueueSerial)386 void CommandProcessorTask::initOneOffQueueSubmit(VkCommandBuffer commandBufferHandle,
387 ProtectionType protectionType,
388 egl::ContextPriority priority,
389 VkSemaphore waitSemaphore,
390 VkPipelineStageFlags waitSemaphoreStageMask,
391 const QueueSerial &submitQueueSerial)
392 {
393 mTask = CustomTask::OneOffQueueSubmit;
394 mOneOffCommandBuffer = commandBufferHandle;
395 mOneOffWaitSemaphore = waitSemaphore;
396 mOneOffWaitSemaphoreStageMask = waitSemaphoreStageMask;
397 mPriority = priority;
398 mProtectionType = protectionType;
399 mSubmitQueueSerial = submitQueueSerial;
400 }
401
operator =(CommandProcessorTask && rhs)402 CommandProcessorTask &CommandProcessorTask::operator=(CommandProcessorTask &&rhs)
403 {
404 if (this == &rhs)
405 {
406 return *this;
407 }
408
409 std::swap(mRenderPass, rhs.mRenderPass);
410 std::swap(mOutsideRenderPassCommandBuffer, rhs.mOutsideRenderPassCommandBuffer);
411 std::swap(mRenderPassCommandBuffer, rhs.mRenderPassCommandBuffer);
412 std::swap(mTask, rhs.mTask);
413 std::swap(mWaitSemaphores, rhs.mWaitSemaphores);
414 std::swap(mWaitSemaphoreStageMasks, rhs.mWaitSemaphoreStageMasks);
415 std::swap(mSemaphore, rhs.mSemaphore);
416 std::swap(mExternalFence, rhs.mExternalFence);
417 std::swap(mOneOffWaitSemaphore, rhs.mOneOffWaitSemaphore);
418 std::swap(mOneOffWaitSemaphoreStageMask, rhs.mOneOffWaitSemaphoreStageMask);
419 std::swap(mSubmitQueueSerial, rhs.mSubmitQueueSerial);
420 std::swap(mPriority, rhs.mPriority);
421 std::swap(mProtectionType, rhs.mProtectionType);
422 std::swap(mOneOffCommandBuffer, rhs.mOneOffCommandBuffer);
423
424 copyPresentInfo(rhs.mPresentInfo);
425 std::swap(mSwapchainStatus, rhs.mSwapchainStatus);
426
427 // clear rhs now that everything has moved.
428 rhs.initTask();
429
430 return *this;
431 }
432
433 // CommandBatch implementation.
CommandBatch()434 CommandBatch::CommandBatch() : protectionType(ProtectionType::InvalidEnum) {}
435
436 CommandBatch::~CommandBatch() = default;
437
CommandBatch(CommandBatch && other)438 CommandBatch::CommandBatch(CommandBatch &&other) : CommandBatch()
439 {
440 *this = std::move(other);
441 }
442
operator =(CommandBatch && other)443 CommandBatch &CommandBatch::operator=(CommandBatch &&other)
444 {
445 std::swap(primaryCommands, other.primaryCommands);
446 std::swap(secondaryCommands, other.secondaryCommands);
447 std::swap(fence, other.fence);
448 std::swap(externalFence, other.externalFence);
449 std::swap(queueSerial, other.queueSerial);
450 std::swap(protectionType, other.protectionType);
451 return *this;
452 }
453
destroy(VkDevice device)454 void CommandBatch::destroy(VkDevice device)
455 {
456 primaryCommands.destroy(device);
457 secondaryCommands.retireCommandBuffers();
458 destroyFence(device);
459 protectionType = ProtectionType::InvalidEnum;
460 }
461
hasFence() const462 bool CommandBatch::hasFence() const
463 {
464 ASSERT(!externalFence || !fence);
465 return fence || externalFence;
466 }
467
releaseFence()468 void CommandBatch::releaseFence()
469 {
470 fence.release();
471 externalFence.reset();
472 }
473
destroyFence(VkDevice device)474 void CommandBatch::destroyFence(VkDevice device)
475 {
476 fence.destroy(device);
477 externalFence.reset();
478 }
479
getFenceHandle() const480 VkFence CommandBatch::getFenceHandle() const
481 {
482 ASSERT(hasFence());
483 return fence ? fence.get().getHandle() : externalFence->getHandle();
484 }
485
getFenceStatus(VkDevice device) const486 VkResult CommandBatch::getFenceStatus(VkDevice device) const
487 {
488 ASSERT(hasFence());
489 return fence ? fence.getStatus(device) : externalFence->getStatus(device);
490 }
491
waitFence(VkDevice device,uint64_t timeout) const492 VkResult CommandBatch::waitFence(VkDevice device, uint64_t timeout) const
493 {
494 ASSERT(hasFence());
495 return fence ? fence.wait(device, timeout) : externalFence->wait(device, timeout);
496 }
497
waitFenceUnlocked(VkDevice device,uint64_t timeout,std::unique_lock<std::mutex> * lock) const498 VkResult CommandBatch::waitFenceUnlocked(VkDevice device,
499 uint64_t timeout,
500 std::unique_lock<std::mutex> *lock) const
501 {
502 ASSERT(hasFence());
503 VkResult status;
504 // You can only use the local copy of the fence without lock.
505 // Do not access "this" after unlock() because object might be deleted from other thread.
506 if (fence)
507 {
508 const SharedFence localFenceToWaitOn = fence;
509 lock->unlock();
510 status = localFenceToWaitOn.wait(device, timeout);
511 lock->lock();
512 }
513 else
514 {
515 const SharedExternalFence localFenceToWaitOn = externalFence;
516 lock->unlock();
517 status = localFenceToWaitOn->wait(device, timeout);
518 lock->lock();
519 }
520 return status;
521 }
522
523 // CommandProcessor implementation.
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)524 void CommandProcessor::handleError(VkResult errorCode,
525 const char *file,
526 const char *function,
527 unsigned int line)
528 {
529 ASSERT(errorCode != VK_SUCCESS);
530
531 std::stringstream errorStream;
532 errorStream << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode)
533 << ".";
534
535 if (errorCode == VK_ERROR_DEVICE_LOST)
536 {
537 WARN() << errorStream.str();
538 handleDeviceLost(mRenderer);
539 }
540
541 std::lock_guard<std::mutex> queueLock(mErrorMutex);
542 Error error = {errorCode, file, function, line};
543 mErrors.emplace(error);
544 }
545
CommandProcessor(RendererVk * renderer,CommandQueue * commandQueue)546 CommandProcessor::CommandProcessor(RendererVk *renderer, CommandQueue *commandQueue)
547 : Context(renderer),
548 mCommandQueue(commandQueue),
549 mTaskThreadShouldExit(false),
550 mNeedCommandsAndGarbageCleanup(false)
551 {
552 std::lock_guard<std::mutex> queueLock(mErrorMutex);
553 while (!mErrors.empty())
554 {
555 mErrors.pop();
556 }
557 }
558
559 CommandProcessor::~CommandProcessor() = default;
560
checkAndPopPendingError(Context * errorHandlingContext)561 angle::Result CommandProcessor::checkAndPopPendingError(Context *errorHandlingContext)
562 {
563 std::lock_guard<std::mutex> queueLock(mErrorMutex);
564 if (mErrors.empty())
565 {
566 return angle::Result::Continue;
567 }
568
569 while (!mErrors.empty())
570 {
571 Error err = mErrors.front();
572 mErrors.pop();
573 errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
574 }
575 return angle::Result::Stop;
576 }
577
queueCommand(CommandProcessorTask && task)578 angle::Result CommandProcessor::queueCommand(CommandProcessorTask &&task)
579 {
580 ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::queueCommand");
581 // Take mTaskEnqueueMutex lock. If task queue is full, try to drain one.
582 std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
583 if (mTaskQueue.full())
584 {
585 std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
586 // Check mTasks again in case someone just drained the mTasks.
587 if (mTaskQueue.full())
588 {
589 CommandProcessorTask frontTask(std::move(mTaskQueue.front()));
590 mTaskQueue.pop();
591 ANGLE_TRY(processTask(&frontTask));
592 }
593 }
594 mTaskQueue.push(std::move(task));
595 mWorkAvailableCondition.notify_one();
596
597 return angle::Result::Continue;
598 }
599
requestCommandsAndGarbageCleanup()600 void CommandProcessor::requestCommandsAndGarbageCleanup()
601 {
602 if (!mNeedCommandsAndGarbageCleanup.exchange(true))
603 {
604 // request clean up in async thread
605 std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
606 mWorkAvailableCondition.notify_one();
607 }
608 }
609
processTasks()610 void CommandProcessor::processTasks()
611 {
612 while (true)
613 {
614 bool exitThread = false;
615 angle::Result result = processTasksImpl(&exitThread);
616 if (exitThread)
617 {
618 // We are doing a controlled exit of the thread, break out of the while loop.
619 break;
620 }
621 if (result != angle::Result::Continue)
622 {
623 // TODO: https://issuetracker.google.com/issues/170311829 - follow-up on error handling
624 // ContextVk::commandProcessorSyncErrorsAndQueueCommand and WindowSurfaceVk::destroy
625 // do error processing, is anything required here? Don't think so, mostly need to
626 // continue the worker thread until it's been told to exit.
627 UNREACHABLE();
628 }
629 }
630 }
631
processTasksImpl(bool * exitThread)632 angle::Result CommandProcessor::processTasksImpl(bool *exitThread)
633 {
634 while (true)
635 {
636 std::unique_lock<std::mutex> enqueueLock(mTaskEnqueueMutex);
637 if (mTaskQueue.empty())
638 {
639 if (mTaskThreadShouldExit)
640 {
641 break;
642 }
643
644 // Only wake if notified and command queue is not empty
645 mWorkAvailableCondition.wait(enqueueLock, [this] {
646 return !mTaskQueue.empty() || mTaskThreadShouldExit ||
647 mNeedCommandsAndGarbageCleanup;
648 });
649 }
650 // Do submission with mTaskEnqueueMutex unlocked so that we still allow enqueue while we
651 // process work.
652 enqueueLock.unlock();
653
654 // Take submission lock to ensure the submission is in the same order as we received.
655 std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
656 if (!mTaskQueue.empty())
657 {
658 CommandProcessorTask task(std::move(mTaskQueue.front()));
659 mTaskQueue.pop();
660
661 // Artificially make the task take longer to catch threading issues.
662 if (getFeatures().slowAsyncCommandQueueForTesting.enabled)
663 {
664 constexpr double kSlowdownTime = 0.005;
665
666 double startTime = angle::GetCurrentSystemTime();
667 while (angle::GetCurrentSystemTime() - startTime < kSlowdownTime)
668 {
669 // Busy waiting
670 }
671 }
672
673 ANGLE_TRY(processTask(&task));
674 }
675
676 if (mNeedCommandsAndGarbageCleanup.exchange(false))
677 {
678 // Always check completed commands again in case anything new has been finished.
679 ANGLE_TRY(mCommandQueue->checkCompletedCommands(this));
680
681 // Reset command buffer and clean up garbage
682 if (mRenderer->isAsyncCommandBufferResetEnabled() &&
683 mCommandQueue->hasFinishedCommands())
684 {
685 ANGLE_TRY(mCommandQueue->retireFinishedCommands(this));
686 }
687 mRenderer->cleanupGarbage();
688 }
689 }
690 *exitThread = true;
691 return angle::Result::Continue;
692 }
693
processTask(CommandProcessorTask * task)694 angle::Result CommandProcessor::processTask(CommandProcessorTask *task)
695 {
696 switch (task->getTaskCommand())
697 {
698 case CustomTask::FlushAndQueueSubmit:
699 {
700 ANGLE_TRACE_EVENT0("gpu.angle", "processTask::FlushAndQueueSubmit");
701 // End command buffer
702
703 // Call submitCommands()
704 ANGLE_TRY(mCommandQueue->submitCommands(
705 this, task->getProtectionType(), task->getPriority(), task->getSemaphore(),
706 std::move(task->getExternalFence()), task->getSubmitQueueSerial()));
707 mNeedCommandsAndGarbageCleanup = true;
708 break;
709 }
710 case CustomTask::OneOffQueueSubmit:
711 {
712 ANGLE_TRACE_EVENT0("gpu.angle", "processTask::OneOffQueueSubmit");
713
714 ANGLE_TRY(mCommandQueue->queueSubmitOneOff(
715 this, task->getProtectionType(), task->getPriority(),
716 task->getOneOffCommandBuffer(), task->getOneOffWaitSemaphore(),
717 task->getOneOffWaitSemaphoreStageMask(), SubmitPolicy::EnsureSubmitted,
718 task->getSubmitQueueSerial()));
719 mNeedCommandsAndGarbageCleanup = true;
720 break;
721 }
722 case CustomTask::Present:
723 {
724 // Do not access task->getSwapchainStatus() after this call because it is marked as no
725 // longer pending, and so may get deleted or clobbered by another thread.
726 VkResult result =
727 present(task->getPriority(), task->getPresentInfo(), task->getSwapchainStatus());
728
729 // We get to ignore these as they are not fatal
730 if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR &&
731 result != VK_SUCCESS)
732 {
733 // Save the error so that we can handle it.
734 // Don't leave processing loop, don't consider errors from present to be fatal.
735 // TODO: https://issuetracker.google.com/issues/170329600 - This needs to improve to
736 // properly parallelize present
737 handleError(result, __FILE__, __FUNCTION__, __LINE__);
738 }
739 break;
740 }
741 case CustomTask::FlushWaitSemaphores:
742 {
743 mCommandQueue->flushWaitSemaphores(task->getProtectionType(), task->getPriority(),
744 std::move(task->getWaitSemaphores()),
745 std::move(task->getWaitSemaphoreStageMasks()));
746 break;
747 }
748 case CustomTask::ProcessOutsideRenderPassCommands:
749 {
750 OutsideRenderPassCommandBufferHelper *commandBuffer =
751 task->getOutsideRenderPassCommandBuffer();
752 ANGLE_TRY(mCommandQueue->flushOutsideRPCommands(this, task->getProtectionType(),
753 task->getPriority(), &commandBuffer));
754
755 OutsideRenderPassCommandBufferHelper *originalCommandBuffer =
756 task->getOutsideRenderPassCommandBuffer();
757 mRenderer->recycleOutsideRenderPassCommandBufferHelper(&originalCommandBuffer);
758 break;
759 }
760 case CustomTask::ProcessRenderPassCommands:
761 {
762 RenderPassCommandBufferHelper *commandBuffer = task->getRenderPassCommandBuffer();
763 ANGLE_TRY(mCommandQueue->flushRenderPassCommands(
764 this, task->getProtectionType(), task->getPriority(), *task->getRenderPass(),
765 &commandBuffer));
766
767 RenderPassCommandBufferHelper *originalCommandBuffer =
768 task->getRenderPassCommandBuffer();
769 mRenderer->recycleRenderPassCommandBufferHelper(&originalCommandBuffer);
770 break;
771 }
772 default:
773 UNREACHABLE();
774 break;
775 }
776
777 return angle::Result::Continue;
778 }
779
waitForAllWorkToBeSubmitted(Context * context)780 angle::Result CommandProcessor::waitForAllWorkToBeSubmitted(Context *context)
781 {
782 ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::waitForAllWorkToBeSubmitted");
783 // Take mWorkerMutex lock so that no one is able to enqueue more work while we drain it
784 // and handle device lost.
785 std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
786 std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
787 // Sync any errors to the context
788 // Do this inside the mutex to prevent new errors adding to the list.
789 ANGLE_TRY(checkAndPopPendingError(context));
790
791 while (!mTaskQueue.empty())
792 {
793 CommandProcessorTask task(std::move(mTaskQueue.front()));
794 mTaskQueue.pop();
795 ANGLE_TRY(processTask(&task));
796 }
797
798 if (mRenderer->isAsyncCommandBufferResetEnabled())
799 {
800 ANGLE_TRY(mCommandQueue->retireFinishedCommands(context));
801 }
802 context->getRenderer()->cleanupGarbage();
803
804 mNeedCommandsAndGarbageCleanup = false;
805
806 return angle::Result::Continue;
807 }
808
init()809 angle::Result CommandProcessor::init()
810 {
811 mTaskThread = std::thread(&CommandProcessor::processTasks, this);
812
813 return angle::Result::Continue;
814 }
815
destroy(Context * context)816 void CommandProcessor::destroy(Context *context)
817 {
818 {
819 // Request to terminate the worker thread
820 std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
821 mTaskThreadShouldExit = true;
822 mWorkAvailableCondition.notify_one();
823 }
824
825 (void)waitForAllWorkToBeSubmitted(context);
826 if (mTaskThread.joinable())
827 {
828 mTaskThread.join();
829 }
830 }
831
handleDeviceLost(RendererVk * renderer)832 void CommandProcessor::handleDeviceLost(RendererVk *renderer)
833 {
834 ANGLE_TRACE_EVENT0("gpu.angle", "CommandProcessor::handleDeviceLost");
835 // Take mTaskEnqueueMutex lock so that no one is able to add more work to the queue while we
836 // drain it and handle device lost.
837 std::lock_guard<std::mutex> enqueueLock(mTaskEnqueueMutex);
838 (void)waitForAllWorkToBeSubmitted(this);
839 // Worker thread is idle and command queue is empty so good to continue
840 mCommandQueue->handleDeviceLost(renderer);
841 }
842
present(egl::ContextPriority priority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)843 VkResult CommandProcessor::present(egl::ContextPriority priority,
844 const VkPresentInfoKHR &presentInfo,
845 SwapchainStatus *swapchainStatus)
846 {
847 ANGLE_TRACE_EVENT0("gpu.angle", "vkQueuePresentKHR");
848 // Verify that we are presenting one and only one swapchain
849 ASSERT(presentInfo.swapchainCount == 1);
850 ASSERT(presentInfo.pResults == nullptr);
851
852 mCommandQueue->queuePresent(priority, presentInfo, swapchainStatus);
853 const VkResult result = swapchainStatus->lastPresentResult;
854
855 // Always make sure update isPending after status has been updated.
856 // Can't access swapchainStatus after this assignment because it is marked as no longer pending,
857 // and so may get deleted or clobbered by another thread.
858 ASSERT(swapchainStatus->isPending);
859 swapchainStatus->isPending = false;
860
861 return result;
862 }
863
enqueueSubmitCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,const QueueSerial & submitQueueSerial)864 angle::Result CommandProcessor::enqueueSubmitCommands(Context *context,
865 ProtectionType protectionType,
866 egl::ContextPriority priority,
867 VkSemaphore signalSemaphore,
868 SharedExternalFence &&externalFence,
869 const QueueSerial &submitQueueSerial)
870 {
871 ANGLE_TRY(checkAndPopPendingError(context));
872
873 CommandProcessorTask task;
874 task.initFlushAndQueueSubmit(signalSemaphore, std::move(externalFence), protectionType,
875 priority, submitQueueSerial);
876
877 ANGLE_TRY(queueCommand(std::move(task)));
878
879 mLastEnqueuedSerials.setQueueSerial(submitQueueSerial);
880
881 return angle::Result::Continue;
882 }
883
enqueueSubmitOneOffCommands(Context * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,SubmitPolicy submitPolicy,const QueueSerial & submitQueueSerial)884 angle::Result CommandProcessor::enqueueSubmitOneOffCommands(
885 Context *context,
886 ProtectionType protectionType,
887 egl::ContextPriority contextPriority,
888 VkCommandBuffer commandBufferHandle,
889 VkSemaphore waitSemaphore,
890 VkPipelineStageFlags waitSemaphoreStageMask,
891 SubmitPolicy submitPolicy,
892 const QueueSerial &submitQueueSerial)
893 {
894 ANGLE_TRY(checkAndPopPendingError(context));
895
896 CommandProcessorTask task;
897 task.initOneOffQueueSubmit(commandBufferHandle, protectionType, contextPriority, waitSemaphore,
898 waitSemaphoreStageMask, submitQueueSerial);
899 ANGLE_TRY(queueCommand(std::move(task)));
900
901 mLastEnqueuedSerials.setQueueSerial(submitQueueSerial);
902
903 if (submitPolicy == SubmitPolicy::EnsureSubmitted)
904 {
905 // Caller has synchronization requirement to have work in GPU pipe when returning from this
906 // function.
907 ANGLE_TRY(waitForQueueSerialToBeSubmitted(context, submitQueueSerial));
908 }
909
910 return angle::Result::Continue;
911 }
912
enqueuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)913 void CommandProcessor::enqueuePresent(egl::ContextPriority contextPriority,
914 const VkPresentInfoKHR &presentInfo,
915 SwapchainStatus *swapchainStatus)
916 {
917 ASSERT(!swapchainStatus->isPending);
918 swapchainStatus->isPending = true;
919 // Always return with VK_SUCCESS initially. When we call acquireNextImage we'll check the
920 // return code again. This allows the app to continue working until we really need to know
921 // the return code from present.
922 swapchainStatus->lastPresentResult = VK_SUCCESS;
923
924 CommandProcessorTask task;
925 task.initPresent(contextPriority, presentInfo, swapchainStatus);
926 (void)queueCommand(std::move(task));
927 }
928
enqueueFlushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)929 angle::Result CommandProcessor::enqueueFlushWaitSemaphores(
930 ProtectionType protectionType,
931 egl::ContextPriority priority,
932 std::vector<VkSemaphore> &&waitSemaphores,
933 std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
934 {
935 CommandProcessorTask task;
936 task.initFlushWaitSemaphores(protectionType, priority, std::move(waitSemaphores),
937 std::move(waitSemaphoreStageMasks));
938 ANGLE_TRY(queueCommand(std::move(task)));
939
940 return angle::Result::Continue;
941 }
942
enqueueFlushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)943 angle::Result CommandProcessor::enqueueFlushOutsideRPCommands(
944 Context *context,
945 ProtectionType protectionType,
946 egl::ContextPriority priority,
947 OutsideRenderPassCommandBufferHelper **outsideRPCommands)
948 {
949 ANGLE_TRY(checkAndPopPendingError(context));
950
951 (*outsideRPCommands)->markClosed();
952
953 SecondaryCommandPool *commandPool = nullptr;
954 ANGLE_TRY((*outsideRPCommands)->detachCommandPool(context, &commandPool));
955
956 // Detach functions are only used for ring buffer allocators.
957 SecondaryCommandMemoryAllocator *allocator = (*outsideRPCommands)->detachAllocator();
958
959 CommandProcessorTask task;
960 task.initOutsideRenderPassProcessCommands(protectionType, priority, *outsideRPCommands);
961 ANGLE_TRY(queueCommand(std::move(task)));
962
963 ANGLE_TRY(mRenderer->getOutsideRenderPassCommandBufferHelper(context, commandPool, allocator,
964 outsideRPCommands));
965
966 return angle::Result::Continue;
967 }
968
enqueueFlushRenderPassCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,const RenderPass & renderPass,RenderPassCommandBufferHelper ** renderPassCommands)969 angle::Result CommandProcessor::enqueueFlushRenderPassCommands(
970 Context *context,
971 ProtectionType protectionType,
972 egl::ContextPriority priority,
973 const RenderPass &renderPass,
974 RenderPassCommandBufferHelper **renderPassCommands)
975 {
976 ANGLE_TRY(checkAndPopPendingError(context));
977
978 (*renderPassCommands)->markClosed();
979
980 SecondaryCommandPool *commandPool = nullptr;
981 (*renderPassCommands)->detachCommandPool(&commandPool);
982
983 // Detach functions are only used for ring buffer allocators.
984 SecondaryCommandMemoryAllocator *allocator = (*renderPassCommands)->detachAllocator();
985
986 CommandProcessorTask task;
987 task.initRenderPassProcessCommands(protectionType, priority, *renderPassCommands, &renderPass);
988 ANGLE_TRY(queueCommand(std::move(task)));
989
990 ANGLE_TRY(mRenderer->getRenderPassCommandBufferHelper(context, commandPool, allocator,
991 renderPassCommands));
992
993 return angle::Result::Continue;
994 }
995
waitForResourceUseToBeSubmitted(Context * context,const ResourceUse & use)996 angle::Result CommandProcessor::waitForResourceUseToBeSubmitted(Context *context,
997 const ResourceUse &use)
998 {
999 if (mCommandQueue->hasResourceUseSubmitted(use))
1000 {
1001 ANGLE_TRY(checkAndPopPendingError(context));
1002 }
1003 else
1004 {
1005 // We do not hold mTaskEnqueueMutex lock, so that we still allow other context to enqueue
1006 // work while we are processing them.
1007 std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
1008
1009 // Do this inside the mutex to prevent new errors adding to the list.
1010 ANGLE_TRY(checkAndPopPendingError(context));
1011
1012 size_t maxTaskCount = mTaskQueue.size();
1013 size_t taskCount = 0;
1014 while (taskCount < maxTaskCount && !mCommandQueue->hasResourceUseSubmitted(use))
1015 {
1016 CommandProcessorTask task(std::move(mTaskQueue.front()));
1017 mTaskQueue.pop();
1018 ANGLE_TRY(processTask(&task));
1019 taskCount++;
1020 }
1021 }
1022 return angle::Result::Continue;
1023 }
1024
waitForPresentToBeSubmitted(SwapchainStatus * swapchainStatus)1025 angle::Result CommandProcessor::waitForPresentToBeSubmitted(SwapchainStatus *swapchainStatus)
1026 {
1027 if (!swapchainStatus->isPending)
1028 {
1029 return angle::Result::Continue;
1030 }
1031
1032 std::lock_guard<std::mutex> dequeueLock(mTaskDequeueMutex);
1033 size_t maxTaskCount = mTaskQueue.size();
1034 size_t taskCount = 0;
1035 while (taskCount < maxTaskCount && swapchainStatus->isPending)
1036 {
1037 CommandProcessorTask task(std::move(mTaskQueue.front()));
1038 mTaskQueue.pop();
1039 ANGLE_TRY(processTask(&task));
1040 taskCount++;
1041 }
1042 ASSERT(!swapchainStatus->isPending);
1043 return angle::Result::Continue;
1044 }
1045
1046 // CommandQueue public API implementation. These must be thread safe and never called from
1047 // CommandQueue class itself.
CommandQueue()1048 CommandQueue::CommandQueue() : mPerfCounters{} {}
1049
1050 CommandQueue::~CommandQueue() = default;
1051
destroy(Context * context)1052 void CommandQueue::destroy(Context *context)
1053 {
1054 std::lock_guard<std::mutex> lock(mMutex);
1055 std::lock_guard<std::mutex> enqueuelock(mQueueSubmitMutex);
1056 // Force all commands to finish by flushing all queues.
1057 for (VkQueue queue : mQueueMap)
1058 {
1059 if (queue != VK_NULL_HANDLE)
1060 {
1061 vkQueueWaitIdle(queue);
1062 }
1063 }
1064
1065 RendererVk *renderer = context->getRenderer();
1066
1067 // Assigns an infinite "last completed" serial to force garbage to delete.
1068 mLastCompletedSerials.fill(Serial::Infinite());
1069
1070 for (auto &protectionMap : mCommandsStateMap)
1071 {
1072 for (CommandsState &state : protectionMap)
1073 {
1074 state.waitSemaphores.clear();
1075 state.waitSemaphoreStageMasks.clear();
1076 state.primaryCommands.destroy(renderer->getDevice());
1077 state.secondaryCommands.retireCommandBuffers();
1078 }
1079 }
1080
1081 for (PersistentCommandPool &commandPool : mPrimaryCommandPoolMap)
1082 {
1083 commandPool.destroy(renderer->getDevice());
1084 }
1085
1086 mFenceRecycler.destroy(context);
1087
1088 ASSERT(mInFlightCommands.empty());
1089 ASSERT(mFinishedCommandBatches.empty());
1090 }
1091
init(Context * context,const DeviceQueueMap & queueMap)1092 angle::Result CommandQueue::init(Context *context, const DeviceQueueMap &queueMap)
1093 {
1094 std::lock_guard<std::mutex> lock(mMutex);
1095 // In case of RendererVk gets re-initialized, we can't rely on constructor to do initialization
1096 // for us.
1097 mLastSubmittedSerials.fill(kZeroSerial);
1098 mLastCompletedSerials.fill(kZeroSerial);
1099
1100 // Assign before initializing the command pools in order to get the queue family index.
1101 mQueueMap = queueMap;
1102
1103 ANGLE_TRY(initCommandPool(context, ProtectionType::Unprotected));
1104
1105 if (queueMap.isProtected())
1106 {
1107 ANGLE_TRY(initCommandPool(context, ProtectionType::Protected));
1108 }
1109
1110 return angle::Result::Continue;
1111 }
1112
handleDeviceLost(RendererVk * renderer)1113 void CommandQueue::handleDeviceLost(RendererVk *renderer)
1114 {
1115 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::handleDeviceLost");
1116 VkDevice device = renderer->getDevice();
1117 // Hold both locks while clean up mInFlightCommands.
1118 std::lock_guard<std::mutex> dequeuelock(mMutex);
1119 std::lock_guard<std::mutex> enqueuelock(mQueueSubmitMutex);
1120
1121 while (!mInFlightCommands.empty())
1122 {
1123 CommandBatch &batch = mInFlightCommands.front();
1124 // On device loss we need to wait for fence to be signaled before destroying it
1125 if (batch.hasFence())
1126 {
1127 VkResult status = batch.waitFence(device, renderer->getMaxFenceWaitTimeNs());
1128 // If the wait times out, it is probably not possible to recover from lost device
1129 ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
1130
1131 batch.destroyFence(device);
1132 }
1133
1134 // On device lost, here simply destroy the CommandBuffer, it will fully cleared later
1135 // by CommandPool::destroy
1136 if (batch.primaryCommands.valid())
1137 {
1138 batch.primaryCommands.destroy(device);
1139 }
1140
1141 batch.secondaryCommands.retireCommandBuffers();
1142
1143 mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1144 mInFlightCommands.pop();
1145 }
1146 }
1147
postSubmitCheck(Context * context)1148 angle::Result CommandQueue::postSubmitCheck(Context *context)
1149 {
1150 RendererVk *renderer = context->getRenderer();
1151
1152 // Update mLastCompletedQueueSerial immediately in case any command has been finished.
1153 ANGLE_TRY(checkAndCleanupCompletedCommands(context));
1154
1155 VkDeviceSize suballocationGarbageSize = renderer->getSuballocationGarbageSize();
1156 if (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize)
1157 {
1158 // CPU should be throttled to avoid accumulating too much memory garbage waiting to be
1159 // destroyed. This is important to keep peak memory usage at check when game launched and a
1160 // lot of staging buffers used for textures upload and then gets released. But if there is
1161 // only one command buffer in flight, we do not wait here to ensure we keep GPU busy.
1162 std::unique_lock<std::mutex> lock(mMutex);
1163 while (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize &&
1164 mInFlightCommands.size() > 1)
1165 {
1166 ANGLE_TRY(
1167 finishOneCommandBatchAndCleanupImpl(context, renderer->getMaxFenceWaitTimeNs()));
1168 suballocationGarbageSize = renderer->getSuballocationGarbageSize();
1169 }
1170 }
1171
1172 if (kOutputVmaStatsString)
1173 {
1174 renderer->outputVmaStatString();
1175 }
1176
1177 return angle::Result::Continue;
1178 }
1179
finishResourceUse(Context * context,const ResourceUse & use,uint64_t timeout)1180 angle::Result CommandQueue::finishResourceUse(Context *context,
1181 const ResourceUse &use,
1182 uint64_t timeout)
1183 {
1184 VkDevice device = context->getDevice();
1185
1186 {
1187 std::unique_lock<std::mutex> lock(mMutex);
1188 while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
1189 {
1190 bool finished;
1191 ANGLE_TRY(checkOneCommandBatch(context, &finished));
1192 if (!finished)
1193 {
1194 ANGLE_VK_TRY(context,
1195 mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock));
1196 }
1197 }
1198 // Check the rest of the commands in case they are also finished.
1199 ANGLE_TRY(checkCompletedCommandsLocked(context));
1200 }
1201 ASSERT(hasResourceUseFinished(use));
1202
1203 if (!mFinishedCommandBatches.empty())
1204 {
1205 ANGLE_TRY(retireFinishedCommandsAndCleanupGarbage(context));
1206 }
1207
1208 return angle::Result::Continue;
1209 }
1210
finishQueueSerial(Context * context,const QueueSerial & queueSerial,uint64_t timeout)1211 angle::Result CommandQueue::finishQueueSerial(Context *context,
1212 const QueueSerial &queueSerial,
1213 uint64_t timeout)
1214 {
1215 vk::ResourceUse use(queueSerial);
1216 return finishResourceUse(context, use, timeout);
1217 }
1218
waitIdle(Context * context,uint64_t timeout)1219 angle::Result CommandQueue::waitIdle(Context *context, uint64_t timeout)
1220 {
1221 // Fill the local variable with lock
1222 vk::ResourceUse use;
1223 {
1224 std::lock_guard<std::mutex> lock(mMutex);
1225 if (mInFlightCommands.empty())
1226 {
1227 return angle::Result::Continue;
1228 }
1229 use.setQueueSerial(mInFlightCommands.back().queueSerial);
1230 }
1231
1232 return finishResourceUse(context, use, timeout);
1233 }
1234
waitForResourceUseToFinishWithUserTimeout(Context * context,const ResourceUse & use,uint64_t timeout,VkResult * result)1235 angle::Result CommandQueue::waitForResourceUseToFinishWithUserTimeout(Context *context,
1236 const ResourceUse &use,
1237 uint64_t timeout,
1238 VkResult *result)
1239 {
1240 // Serial is not yet submitted. This is undefined behaviour, so we can do anything.
1241 if (!hasResourceUseSubmitted(use))
1242 {
1243 WARN() << "Waiting on an unsubmitted serial.";
1244 *result = VK_TIMEOUT;
1245 return angle::Result::Continue;
1246 }
1247
1248 VkDevice device = context->getDevice();
1249 size_t finishedCount = 0;
1250 {
1251 std::unique_lock<std::mutex> lock(mMutex);
1252 while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
1253 {
1254 bool finished;
1255 ANGLE_TRY(checkOneCommandBatch(context, &finished));
1256 if (!finished)
1257 {
1258 *result = mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock);
1259 // Don't trigger an error on timeout.
1260 if (*result == VK_TIMEOUT)
1261 {
1262 break;
1263 }
1264 else
1265 {
1266 ANGLE_VK_TRY(context, *result);
1267 }
1268 }
1269 }
1270 // Do one more check in case more commands also finished.
1271 ANGLE_TRY(checkCompletedCommandsLocked(context));
1272 finishedCount = mFinishedCommandBatches.size();
1273 }
1274
1275 if (finishedCount > 0)
1276 {
1277 ANGLE_TRY(retireFinishedCommandsAndCleanupGarbage(context));
1278 }
1279
1280 return angle::Result::Continue;
1281 }
1282
isBusy(RendererVk * renderer) const1283 bool CommandQueue::isBusy(RendererVk *renderer) const
1284 {
1285 // No lock is needed here since we are accessing atomic variables only.
1286 size_t maxIndex = renderer->getLargestQueueSerialIndexEverAllocated();
1287 for (SerialIndex i = 0; i <= maxIndex; ++i)
1288 {
1289 if (mLastSubmittedSerials[i] > mLastCompletedSerials[i])
1290 {
1291 return true;
1292 }
1293 }
1294 return false;
1295 }
1296
flushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)1297 void CommandQueue::flushWaitSemaphores(ProtectionType protectionType,
1298 egl::ContextPriority priority,
1299 std::vector<VkSemaphore> &&waitSemaphores,
1300 std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
1301 {
1302 ASSERT(!waitSemaphores.empty());
1303 ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
1304 std::lock_guard<std::mutex> lock(mMutex);
1305
1306 CommandsState &state = mCommandsStateMap[priority][protectionType];
1307
1308 state.waitSemaphores.insert(state.waitSemaphores.end(), waitSemaphores.begin(),
1309 waitSemaphores.end());
1310 state.waitSemaphoreStageMasks.insert(state.waitSemaphoreStageMasks.end(),
1311 waitSemaphoreStageMasks.begin(),
1312 waitSemaphoreStageMasks.end());
1313
1314 waitSemaphores.clear();
1315 waitSemaphoreStageMasks.clear();
1316 }
1317
flushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)1318 angle::Result CommandQueue::flushOutsideRPCommands(
1319 Context *context,
1320 ProtectionType protectionType,
1321 egl::ContextPriority priority,
1322 OutsideRenderPassCommandBufferHelper **outsideRPCommands)
1323 {
1324 std::lock_guard<std::mutex> lock(mMutex);
1325 ANGLE_TRY(ensurePrimaryCommandBufferValid(context, protectionType, priority));
1326 CommandsState &state = mCommandsStateMap[priority][protectionType];
1327 return (*outsideRPCommands)->flushToPrimary(context, &state);
1328 }
1329
flushRenderPassCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,const RenderPass & renderPass,RenderPassCommandBufferHelper ** renderPassCommands)1330 angle::Result CommandQueue::flushRenderPassCommands(
1331 Context *context,
1332 ProtectionType protectionType,
1333 egl::ContextPriority priority,
1334 const RenderPass &renderPass,
1335 RenderPassCommandBufferHelper **renderPassCommands)
1336 {
1337 std::lock_guard<std::mutex> lock(mMutex);
1338 ANGLE_TRY(ensurePrimaryCommandBufferValid(context, protectionType, priority));
1339 CommandsState &state = mCommandsStateMap[priority][protectionType];
1340 return (*renderPassCommands)->flushToPrimary(context, &state, &renderPass);
1341 }
1342
submitCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,const QueueSerial & submitQueueSerial)1343 angle::Result CommandQueue::submitCommands(Context *context,
1344 ProtectionType protectionType,
1345 egl::ContextPriority priority,
1346 VkSemaphore signalSemaphore,
1347 SharedExternalFence &&externalFence,
1348 const QueueSerial &submitQueueSerial)
1349 {
1350 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitCommands");
1351 std::unique_lock<std::mutex> lock(mMutex);
1352 RendererVk *renderer = context->getRenderer();
1353 VkDevice device = renderer->getDevice();
1354
1355 ++mPerfCounters.commandQueueSubmitCallsTotal;
1356 ++mPerfCounters.commandQueueSubmitCallsPerFrame;
1357
1358 DeviceScoped<CommandBatch> scopedBatch(device);
1359 CommandBatch &batch = scopedBatch.get();
1360
1361 batch.queueSerial = submitQueueSerial;
1362 batch.protectionType = protectionType;
1363
1364 CommandsState &state = mCommandsStateMap[priority][protectionType];
1365 // Store the primary CommandBuffer in the in-flight list.
1366 batch.primaryCommands = std::move(state.primaryCommands);
1367
1368 // Store secondary Command Buffers.
1369 batch.secondaryCommands = std::move(state.secondaryCommands);
1370 ASSERT(batch.primaryCommands.valid() || batch.secondaryCommands.empty());
1371
1372 // Move to local copy of vectors since queueSubmit will release the lock.
1373 std::vector<VkSemaphore> waitSemaphores = std::move(state.waitSemaphores);
1374 std::vector<VkPipelineStageFlags> waitSemaphoreStageMasks =
1375 std::move(state.waitSemaphoreStageMasks);
1376
1377 mPerfCounters.commandQueueWaitSemaphoresTotal += waitSemaphores.size();
1378
1379 // Don't make a submission if there is nothing to submit.
1380 const bool needsQueueSubmit = batch.primaryCommands.valid() ||
1381 signalSemaphore != VK_NULL_HANDLE || externalFence ||
1382 !waitSemaphores.empty();
1383 VkSubmitInfo submitInfo = {};
1384 VkProtectedSubmitInfo protectedSubmitInfo = {};
1385
1386 if (needsQueueSubmit)
1387 {
1388 if (batch.primaryCommands.valid())
1389 {
1390 ANGLE_VK_TRY(context, batch.primaryCommands.end());
1391 }
1392
1393 InitializeSubmitInfo(&submitInfo, batch.primaryCommands, waitSemaphores,
1394 waitSemaphoreStageMasks, signalSemaphore);
1395
1396 // No need protected submission if no commands to submit.
1397 if (protectionType == ProtectionType::Protected && batch.primaryCommands.valid())
1398 {
1399 protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1400 protectedSubmitInfo.pNext = nullptr;
1401 protectedSubmitInfo.protectedSubmit = true;
1402 submitInfo.pNext = &protectedSubmitInfo;
1403 }
1404
1405 if (!externalFence)
1406 {
1407 ANGLE_VK_TRY(context, batch.fence.init(context->getDevice(), &mFenceRecycler));
1408 }
1409 else
1410 {
1411 batch.externalFence = std::move(externalFence);
1412 }
1413
1414 ++mPerfCounters.vkQueueSubmitCallsTotal;
1415 ++mPerfCounters.vkQueueSubmitCallsPerFrame;
1416 }
1417
1418 // Note queueSubmit will release the lock.
1419 ANGLE_TRY(queueSubmit(context, std::move(lock), priority, submitInfo, scopedBatch,
1420 submitQueueSerial));
1421
1422 // Clear local vector without lock.
1423 waitSemaphores.clear();
1424 waitSemaphoreStageMasks.clear();
1425
1426 return angle::Result::Continue;
1427 }
1428
queueSubmitOneOff(Context * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,SubmitPolicy submitPolicy,const QueueSerial & submitQueueSerial)1429 angle::Result CommandQueue::queueSubmitOneOff(Context *context,
1430 ProtectionType protectionType,
1431 egl::ContextPriority contextPriority,
1432 VkCommandBuffer commandBufferHandle,
1433 VkSemaphore waitSemaphore,
1434 VkPipelineStageFlags waitSemaphoreStageMask,
1435 SubmitPolicy submitPolicy,
1436 const QueueSerial &submitQueueSerial)
1437 {
1438 std::unique_lock<std::mutex> lock(mMutex);
1439 DeviceScoped<CommandBatch> scopedBatch(context->getDevice());
1440 CommandBatch &batch = scopedBatch.get();
1441 batch.queueSerial = submitQueueSerial;
1442 batch.protectionType = protectionType;
1443
1444 ANGLE_VK_TRY(context, batch.fence.init(context->getDevice(), &mFenceRecycler));
1445
1446 VkSubmitInfo submitInfo = {};
1447 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1448
1449 VkProtectedSubmitInfo protectedSubmitInfo = {};
1450 ASSERT(protectionType == ProtectionType::Unprotected ||
1451 protectionType == ProtectionType::Protected);
1452 if (protectionType == ProtectionType::Protected)
1453 {
1454 protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
1455 protectedSubmitInfo.pNext = nullptr;
1456 protectedSubmitInfo.protectedSubmit = true;
1457 submitInfo.pNext = &protectedSubmitInfo;
1458 }
1459
1460 if (commandBufferHandle != VK_NULL_HANDLE)
1461 {
1462 submitInfo.commandBufferCount = 1;
1463 submitInfo.pCommandBuffers = &commandBufferHandle;
1464 }
1465
1466 if (waitSemaphore != VK_NULL_HANDLE)
1467 {
1468 submitInfo.waitSemaphoreCount = 1;
1469 submitInfo.pWaitSemaphores = &waitSemaphore;
1470 submitInfo.pWaitDstStageMask = &waitSemaphoreStageMask;
1471 }
1472
1473 ++mPerfCounters.vkQueueSubmitCallsTotal;
1474 ++mPerfCounters.vkQueueSubmitCallsPerFrame;
1475
1476 // Note queueSubmit will release the lock.
1477 return queueSubmit(context, std::move(lock), contextPriority, submitInfo, scopedBatch,
1478 submitQueueSerial);
1479 }
1480
queueSubmit(Context * context,std::unique_lock<std::mutex> && dequeueLock,egl::ContextPriority contextPriority,const VkSubmitInfo & submitInfo,DeviceScoped<CommandBatch> & commandBatch,const QueueSerial & submitQueueSerial)1481 angle::Result CommandQueue::queueSubmit(Context *context,
1482 std::unique_lock<std::mutex> &&dequeueLock,
1483 egl::ContextPriority contextPriority,
1484 const VkSubmitInfo &submitInfo,
1485 DeviceScoped<CommandBatch> &commandBatch,
1486 const QueueSerial &submitQueueSerial)
1487 {
1488 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::queueSubmit");
1489 RendererVk *renderer = context->getRenderer();
1490
1491 // Lock relay to ensure the ordering of submission strictly follow the context's submission
1492 // order. This lock relay (first take mMutex and then mQueueSubmitMutex, and then release
1493 // mMutex) ensures we always have a lock covering the entire call which ensures the strict
1494 // submission order.
1495 std::lock_guard<std::mutex> queueSubmitLock(mQueueSubmitMutex);
1496 // CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
1497 // off-screen scenarios.
1498 if (mInFlightCommands.full())
1499 {
1500 ANGLE_TRY(finishOneCommandBatchAndCleanupImpl(context, renderer->getMaxFenceWaitTimeNs()));
1501 }
1502 // Release the dequeue lock while doing potentially lengthy vkQueueSubmit call.
1503 // Note: after this point, you can not reference anything that required mMutex lock.
1504 dequeueLock.unlock();
1505
1506 if (submitInfo.sType == VK_STRUCTURE_TYPE_SUBMIT_INFO)
1507 {
1508 CommandBatch &batch = commandBatch.get();
1509
1510 VkQueue queue = getQueue(contextPriority);
1511 VkFence fence = batch.getFenceHandle();
1512 ASSERT(fence != VK_NULL_HANDLE);
1513 ANGLE_VK_TRY(context, vkQueueSubmit(queue, 1, &submitInfo, fence));
1514
1515 if (batch.externalFence)
1516 {
1517 // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
1518 // obeys copy semantics. This means that the fence must already be signaled or the work
1519 // to signal it is in the graphics pipeline at the time we export the fd.
1520 // In other words, must call exportFd() after successful vkQueueSubmit() call.
1521 ExternalFence &externalFence = *batch.externalFence;
1522 VkFenceGetFdInfoKHR fenceGetFdInfo = {};
1523 fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
1524 fenceGetFdInfo.fence = externalFence.getHandle();
1525 fenceGetFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
1526 externalFence.exportFd(renderer->getDevice(), fenceGetFdInfo);
1527 }
1528 }
1529
1530 mInFlightCommands.push(commandBatch.release());
1531
1532 // This must set last so that when this submission appears submitted, it actually already
1533 // submitted and enqueued to mInFlightCommands.
1534 mLastSubmittedSerials.setQueueSerial(submitQueueSerial);
1535 return angle::Result::Continue;
1536 }
1537
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo,SwapchainStatus * swapchainStatus)1538 void CommandQueue::queuePresent(egl::ContextPriority contextPriority,
1539 const VkPresentInfoKHR &presentInfo,
1540 SwapchainStatus *swapchainStatus)
1541 {
1542 std::lock_guard<std::mutex> queueSubmitLock(mQueueSubmitMutex);
1543 VkQueue queue = getQueue(contextPriority);
1544 swapchainStatus->lastPresentResult = vkQueuePresentKHR(queue, &presentInfo);
1545 }
1546
getPerfCounters() const1547 const angle::VulkanPerfCounters CommandQueue::getPerfCounters() const
1548 {
1549 std::lock_guard<std::mutex> lock(mMutex);
1550 return mPerfCounters;
1551 }
1552
resetPerFramePerfCounters()1553 void CommandQueue::resetPerFramePerfCounters()
1554 {
1555 std::lock_guard<std::mutex> lock(mMutex);
1556 mPerfCounters.commandQueueSubmitCallsPerFrame = 0;
1557 mPerfCounters.vkQueueSubmitCallsPerFrame = 0;
1558 }
1559
retireFinishedCommandsAndCleanupGarbage(Context * context)1560 angle::Result CommandQueue::retireFinishedCommandsAndCleanupGarbage(Context *context)
1561 {
1562 RendererVk *renderer = context->getRenderer();
1563 if (!renderer->isAsyncCommandBufferResetEnabled())
1564 {
1565 // Do immediate command buffer reset
1566 ANGLE_TRY(retireFinishedCommands(context));
1567 }
1568
1569 renderer->requestAsyncCommandsAndGarbageCleanup(context);
1570
1571 return angle::Result::Continue;
1572 }
1573
1574 // CommandQueue private API implementation. These are called by public API, so lock already held.
checkOneCommandBatch(Context * context,bool * finished)1575 angle::Result CommandQueue::checkOneCommandBatch(Context *context, bool *finished)
1576 {
1577 ASSERT(!mInFlightCommands.empty());
1578
1579 CommandBatch &batch = mInFlightCommands.front();
1580 *finished = false;
1581 if (batch.hasFence())
1582 {
1583 VkResult status = batch.getFenceStatus(context->getDevice());
1584 if (status == VK_NOT_READY)
1585 {
1586 return angle::Result::Continue;
1587 }
1588 ANGLE_VK_TRY(context, status);
1589 }
1590
1591 // Finished.
1592 mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1593
1594 // Move command batch to mFinishedCommandBatches.
1595 if (mFinishedCommandBatches.full())
1596 {
1597 ANGLE_TRY(retireFinishedCommandsLocked(context));
1598 }
1599 mFinishedCommandBatches.push(std::move(batch));
1600 mInFlightCommands.pop();
1601 *finished = true;
1602
1603 return angle::Result::Continue;
1604 }
1605
finishOneCommandBatchAndCleanup(Context * context,uint64_t timeout,bool * anyFinished)1606 angle::Result CommandQueue::finishOneCommandBatchAndCleanup(Context *context,
1607 uint64_t timeout,
1608 bool *anyFinished)
1609 {
1610 std::lock_guard<std::mutex> lock(mMutex);
1611
1612 // If there are in-flight submissions in the queue, they can be finished.
1613 *anyFinished = false;
1614 if (!mInFlightCommands.empty())
1615 {
1616 ANGLE_TRY(finishOneCommandBatchAndCleanupImpl(context, timeout));
1617 *anyFinished = true;
1618 }
1619 return angle::Result::Continue;
1620 }
1621
finishOneCommandBatchAndCleanupImpl(Context * context,uint64_t timeout)1622 angle::Result CommandQueue::finishOneCommandBatchAndCleanupImpl(Context *context, uint64_t timeout)
1623 {
1624 ASSERT(!mInFlightCommands.empty());
1625 CommandBatch &batch = mInFlightCommands.front();
1626 if (batch.hasFence())
1627 {
1628 VkResult status = batch.waitFence(context->getDevice(), timeout);
1629 ANGLE_VK_TRY(context, status);
1630 }
1631
1632 mLastCompletedSerials.setQueueSerial(batch.queueSerial);
1633 // Move command batch to mFinishedCommandBatches.
1634 if (mFinishedCommandBatches.full())
1635 {
1636 ANGLE_TRY(retireFinishedCommandsLocked(context));
1637 }
1638 mFinishedCommandBatches.push(std::move(batch));
1639 mInFlightCommands.pop();
1640
1641 // Immediately clean up finished batches.
1642 ANGLE_TRY(retireFinishedCommandsLocked(context));
1643 context->getRenderer()->cleanupGarbage();
1644
1645 return angle::Result::Continue;
1646 }
1647
retireFinishedCommandsLocked(Context * context)1648 angle::Result CommandQueue::retireFinishedCommandsLocked(Context *context)
1649 {
1650 ANGLE_TRACE_EVENT0("gpu.angle", "retireFinishedCommandsLocked");
1651
1652 while (!mFinishedCommandBatches.empty())
1653 {
1654 CommandBatch &batch = mFinishedCommandBatches.front();
1655 ASSERT(batch.queueSerial <= mLastCompletedSerials);
1656
1657 batch.releaseFence();
1658
1659 if (batch.primaryCommands.valid())
1660 {
1661 PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[batch.protectionType];
1662 ANGLE_TRY(commandPool.collect(context, std::move(batch.primaryCommands)));
1663 }
1664
1665 batch.secondaryCommands.retireCommandBuffers();
1666
1667 mFinishedCommandBatches.pop();
1668 }
1669
1670 return angle::Result::Continue;
1671 }
1672
checkCompletedCommandsLocked(Context * context)1673 angle::Result CommandQueue::checkCompletedCommandsLocked(Context *context)
1674 {
1675 while (!mInFlightCommands.empty())
1676 {
1677 bool finished;
1678 ANGLE_TRY(checkOneCommandBatch(context, &finished));
1679 if (!finished)
1680 {
1681 break;
1682 }
1683 }
1684 return angle::Result::Continue;
1685 }
1686
ensurePrimaryCommandBufferValid(Context * context,ProtectionType protectionType,egl::ContextPriority priority)1687 angle::Result CommandQueue::ensurePrimaryCommandBufferValid(Context *context,
1688 ProtectionType protectionType,
1689 egl::ContextPriority priority)
1690 {
1691 CommandsState &state = mCommandsStateMap[priority][protectionType];
1692
1693 if (state.primaryCommands.valid())
1694 {
1695 return angle::Result::Continue;
1696 }
1697
1698 ANGLE_TRY(mPrimaryCommandPoolMap[protectionType].allocate(context, &state.primaryCommands));
1699 VkCommandBufferBeginInfo beginInfo = {};
1700 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1701 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1702 beginInfo.pInheritanceInfo = nullptr;
1703 ANGLE_VK_TRY(context, state.primaryCommands.begin(beginInfo));
1704
1705 return angle::Result::Continue;
1706 }
1707
1708 // QueuePriorities:
1709 constexpr float kVulkanQueuePriorityLow = 0.0;
1710 constexpr float kVulkanQueuePriorityMedium = 0.4;
1711 constexpr float kVulkanQueuePriorityHigh = 1.0;
1712
1713 const float QueueFamily::kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] =
1714 {kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
1715
getDevicePriority(egl::ContextPriority priority) const1716 egl::ContextPriority DeviceQueueMap::getDevicePriority(egl::ContextPriority priority) const
1717 {
1718 return mPriorities[priority];
1719 }
1720
~DeviceQueueMap()1721 DeviceQueueMap::~DeviceQueueMap() {}
1722
operator =(const DeviceQueueMap & other)1723 DeviceQueueMap &DeviceQueueMap::operator=(const DeviceQueueMap &other)
1724 {
1725 ASSERT(this != &other);
1726 if ((this != &other) && other.valid())
1727 {
1728 mIndex = other.mIndex;
1729 mIsProtected = other.mIsProtected;
1730 mPriorities[egl::ContextPriority::Low] = other.mPriorities[egl::ContextPriority::Low];
1731 mPriorities[egl::ContextPriority::Medium] = other.mPriorities[egl::ContextPriority::Medium];
1732 mPriorities[egl::ContextPriority::High] = other.mPriorities[egl::ContextPriority::High];
1733 *static_cast<angle::PackedEnumMap<egl::ContextPriority, VkQueue> *>(this) = other;
1734 }
1735 return *this;
1736 }
1737
getDeviceQueue(VkDevice device,bool makeProtected,uint32_t queueIndex,VkQueue * queue)1738 void QueueFamily::getDeviceQueue(VkDevice device,
1739 bool makeProtected,
1740 uint32_t queueIndex,
1741 VkQueue *queue)
1742 {
1743 if (makeProtected)
1744 {
1745 VkDeviceQueueInfo2 queueInfo2 = {};
1746 queueInfo2.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
1747 queueInfo2.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
1748 queueInfo2.queueFamilyIndex = mIndex;
1749 queueInfo2.queueIndex = queueIndex;
1750
1751 vkGetDeviceQueue2(device, &queueInfo2, queue);
1752 }
1753 else
1754 {
1755 vkGetDeviceQueue(device, mIndex, queueIndex, queue);
1756 }
1757 }
1758
initializeQueueMap(VkDevice device,bool makeProtected,uint32_t queueIndex,uint32_t queueCount)1759 DeviceQueueMap QueueFamily::initializeQueueMap(VkDevice device,
1760 bool makeProtected,
1761 uint32_t queueIndex,
1762 uint32_t queueCount)
1763 {
1764 // QueueIndexing:
1765 constexpr uint32_t kQueueIndexMedium = 0;
1766 constexpr uint32_t kQueueIndexHigh = 1;
1767 constexpr uint32_t kQueueIndexLow = 2;
1768
1769 ASSERT(queueCount);
1770 ASSERT((queueIndex + queueCount) <= mProperties.queueCount);
1771 DeviceQueueMap queueMap(mIndex, makeProtected);
1772
1773 getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexMedium,
1774 &queueMap[egl::ContextPriority::Medium]);
1775 queueMap.mPriorities[egl::ContextPriority::Medium] = egl::ContextPriority::Medium;
1776
1777 // If at least 2 queues, High has its own queue
1778 if (queueCount > 1)
1779 {
1780 getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexHigh,
1781 &queueMap[egl::ContextPriority::High]);
1782 queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::High;
1783 }
1784 else
1785 {
1786 queueMap[egl::ContextPriority::High] = queueMap[egl::ContextPriority::Medium];
1787 queueMap.mPriorities[egl::ContextPriority::High] = egl::ContextPriority::Medium;
1788 }
1789 // If at least 3 queues, Low has its own queue. Adjust Low priority.
1790 if (queueCount > 2)
1791 {
1792 getDeviceQueue(device, makeProtected, queueIndex + kQueueIndexLow,
1793 &queueMap[egl::ContextPriority::Low]);
1794 queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Low;
1795 }
1796 else
1797 {
1798 queueMap[egl::ContextPriority::Low] = queueMap[egl::ContextPriority::Medium];
1799 queueMap.mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Medium;
1800 }
1801 return queueMap;
1802 }
1803
initialize(const VkQueueFamilyProperties & queueFamilyProperties,uint32_t index)1804 void QueueFamily::initialize(const VkQueueFamilyProperties &queueFamilyProperties, uint32_t index)
1805 {
1806 mProperties = queueFamilyProperties;
1807 mIndex = index;
1808 }
1809
FindIndex(const std::vector<VkQueueFamilyProperties> & queueFamilyProperties,VkQueueFlags flags,int32_t matchNumber,uint32_t * matchCount)1810 uint32_t QueueFamily::FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties,
1811 VkQueueFlags flags,
1812 int32_t matchNumber,
1813 uint32_t *matchCount)
1814 {
1815 uint32_t index = QueueFamily::kInvalidIndex;
1816 uint32_t count = 0;
1817
1818 for (uint32_t familyIndex = 0; familyIndex < queueFamilyProperties.size(); ++familyIndex)
1819 {
1820 const auto &queueInfo = queueFamilyProperties[familyIndex];
1821 if ((queueInfo.queueFlags & flags) == flags)
1822 {
1823 ASSERT(queueInfo.queueCount > 0);
1824 count++;
1825 if ((index == QueueFamily::kInvalidIndex) && (matchNumber-- == 0))
1826 {
1827 index = familyIndex;
1828 }
1829 }
1830 }
1831 if (matchCount)
1832 {
1833 *matchCount = count;
1834 }
1835
1836 return index;
1837 }
1838
1839 } // namespace vk
1840 } // namespace rx
1841