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