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 // CommandQueue.cpp:
7 // Implements the class methods for CommandQueue.
8 //
9
10 #include "libANGLE/renderer/vulkan/CommandQueue.h"
11 #include <algorithm>
12 #include "common/system_utils.h"
13 #include "libANGLE/renderer/vulkan/SyncVk.h"
14 #include "libANGLE/renderer/vulkan/vk_renderer.h"
15 #include "vulkan/vulkan_core.h"
16
17 namespace rx
18 {
19 namespace vk
20 {
21 namespace
22 {
23 constexpr bool kOutputVmaStatsString = false;
24 // When suballocation garbages is more than this, we may wait for GPU to finish and free up some
25 // memory for allocation.
26 constexpr VkDeviceSize kMaxBufferSuballocationGarbageSize = 64 * 1024 * 1024;
27
InitializeSubmitInfo(VkSubmitInfo * submitInfo,const PrimaryCommandBuffer & commandBuffer,const std::vector<VkSemaphore> & waitSemaphores,const std::vector<VkPipelineStageFlags> & waitSemaphoreStageMasks,const VkSemaphore & signalSemaphore)28 void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
29 const PrimaryCommandBuffer &commandBuffer,
30 const std::vector<VkSemaphore> &waitSemaphores,
31 const std::vector<VkPipelineStageFlags> &waitSemaphoreStageMasks,
32 const VkSemaphore &signalSemaphore)
33 {
34 // Verify that the submitInfo has been zero'd out.
35 ASSERT(submitInfo->signalSemaphoreCount == 0);
36 ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
37 submitInfo->sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
38 submitInfo->commandBufferCount = commandBuffer.valid() ? 1 : 0;
39 submitInfo->pCommandBuffers = commandBuffer.ptr();
40 submitInfo->waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size());
41 submitInfo->pWaitSemaphores = waitSemaphores.empty() ? nullptr : waitSemaphores.data();
42 submitInfo->pWaitDstStageMask = waitSemaphoreStageMasks.data();
43
44 if (signalSemaphore != VK_NULL_HANDLE)
45 {
46 submitInfo->signalSemaphoreCount = 1;
47 submitInfo->pSignalSemaphores = &signalSemaphore;
48 }
49 }
50
GetDeviceQueue(VkDevice device,bool makeProtected,uint32_t queueFamilyIndex,uint32_t queueIndex,VkQueue * queue)51 void GetDeviceQueue(VkDevice device,
52 bool makeProtected,
53 uint32_t queueFamilyIndex,
54 uint32_t queueIndex,
55 VkQueue *queue)
56 {
57 if (makeProtected)
58 {
59 VkDeviceQueueInfo2 queueInfo2 = {};
60 queueInfo2.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
61 queueInfo2.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
62 queueInfo2.queueFamilyIndex = queueFamilyIndex;
63 queueInfo2.queueIndex = queueIndex;
64
65 vkGetDeviceQueue2(device, &queueInfo2, queue);
66 }
67 else
68 {
69 vkGetDeviceQueue(device, queueFamilyIndex, queueIndex, queue);
70 }
71 }
72 } // namespace
73
74 // RecyclableFence implementation
RecyclableFence()75 RecyclableFence::RecyclableFence() : mRecycler(nullptr) {}
76
~RecyclableFence()77 RecyclableFence::~RecyclableFence()
78 {
79 ASSERT(!valid());
80 }
81
init(VkDevice device,FenceRecycler * recycler)82 VkResult RecyclableFence::init(VkDevice device, FenceRecycler *recycler)
83 {
84 ASSERT(!valid());
85 ASSERT(mRecycler == nullptr);
86
87 // First try to fetch from recycler. If that failed, try to create a new VkFence
88 recycler->fetch(device, &mFence);
89 if (!valid())
90 {
91 VkFenceCreateInfo fenceCreateInfo = {};
92 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
93 fenceCreateInfo.flags = 0;
94 VkResult result = mFence.init(device, fenceCreateInfo);
95 if (result != VK_SUCCESS)
96 {
97 ASSERT(!valid());
98 return result;
99 }
100 ASSERT(valid());
101 }
102
103 mRecycler = recycler;
104
105 return VK_SUCCESS;
106 }
107
destroy(VkDevice device)108 void RecyclableFence::destroy(VkDevice device)
109 {
110 if (valid())
111 {
112 if (mRecycler != nullptr)
113 {
114 mRecycler->recycle(std::move(mFence));
115 }
116 else
117 {
118 // Recycler was detached - destroy the fence.
119 mFence.destroy(device);
120 }
121 ASSERT(!valid());
122 }
123 }
124
125 // FenceRecycler implementation
destroy(ErrorContext * context)126 void FenceRecycler::destroy(ErrorContext *context)
127 {
128 std::lock_guard<angle::SimpleMutex> lock(mMutex);
129 mRecycler.destroy(context->getDevice());
130 }
131
fetch(VkDevice device,Fence * fenceOut)132 void FenceRecycler::fetch(VkDevice device, Fence *fenceOut)
133 {
134 ASSERT(fenceOut != nullptr && !fenceOut->valid());
135 std::lock_guard<angle::SimpleMutex> lock(mMutex);
136 if (!mRecycler.empty())
137 {
138 mRecycler.fetch(fenceOut);
139 fenceOut->reset(device);
140 }
141 }
142
recycle(Fence && fence)143 void FenceRecycler::recycle(Fence &&fence)
144 {
145 std::lock_guard<angle::SimpleMutex> lock(mMutex);
146 mRecycler.recycle(std::move(fence));
147 }
148
149 // CommandBatch implementation.
CommandBatch()150 CommandBatch::CommandBatch()
151 : mProtectionType(ProtectionType::InvalidEnum), mCommandPoolAccess(nullptr)
152 {}
153
154 CommandBatch::~CommandBatch() = default;
155
CommandBatch(CommandBatch && other)156 CommandBatch::CommandBatch(CommandBatch &&other) : CommandBatch()
157 {
158 *this = std::move(other);
159 }
160
operator =(CommandBatch && other)161 CommandBatch &CommandBatch::operator=(CommandBatch &&other)
162 {
163 std::swap(mQueueSerial, other.mQueueSerial);
164 std::swap(mProtectionType, other.mProtectionType);
165 std::swap(mPrimaryCommands, other.mPrimaryCommands);
166 std::swap(mCommandPoolAccess, other.mCommandPoolAccess);
167 std::swap(mSecondaryCommands, other.mSecondaryCommands);
168 std::swap(mFence, other.mFence);
169 std::swap(mExternalFence, other.mExternalFence);
170 return *this;
171 }
172
destroy(VkDevice device)173 void CommandBatch::destroy(VkDevice device)
174 {
175 if (mPrimaryCommands.valid())
176 {
177 ASSERT(mCommandPoolAccess != nullptr);
178 mCommandPoolAccess->destroyPrimaryCommandBuffer(device, &mPrimaryCommands);
179 }
180 mSecondaryCommands.releaseCommandBuffers();
181 if (mFence)
182 {
183 mFence->detachRecycler();
184 mFence.reset();
185 }
186 mExternalFence.reset();
187 // Do not clean other members to catch invalid reuse attempt with ASSERTs.
188 }
189
release(ErrorContext * context)190 angle::Result CommandBatch::release(ErrorContext *context)
191 {
192 if (mPrimaryCommands.valid())
193 {
194 ASSERT(mCommandPoolAccess != nullptr);
195 ANGLE_TRY(mCommandPoolAccess->collectPrimaryCommandBuffer(context, mProtectionType,
196 &mPrimaryCommands));
197 }
198 mSecondaryCommands.releaseCommandBuffers();
199 mFence.reset();
200 mExternalFence.reset();
201 // Do not clean other members to catch invalid reuse attempt with ASSERTs.
202 return angle::Result::Continue;
203 }
204
setQueueSerial(const QueueSerial & serial)205 void CommandBatch::setQueueSerial(const QueueSerial &serial)
206 {
207 ASSERT(serial.valid());
208 ASSERT(!mQueueSerial.valid());
209 mQueueSerial = serial;
210 }
211
setProtectionType(ProtectionType protectionType)212 void CommandBatch::setProtectionType(ProtectionType protectionType)
213 {
214 ASSERT(protectionType != ProtectionType::InvalidEnum);
215 ASSERT(mProtectionType == ProtectionType::InvalidEnum);
216 mProtectionType = protectionType;
217 }
218
setPrimaryCommands(PrimaryCommandBuffer && primaryCommands,CommandPoolAccess * commandPoolAccess)219 void CommandBatch::setPrimaryCommands(PrimaryCommandBuffer &&primaryCommands,
220 CommandPoolAccess *commandPoolAccess)
221 {
222 // primaryCommands is optional.
223 ASSERT(!(primaryCommands.valid() && commandPoolAccess == nullptr));
224 ASSERT(!mPrimaryCommands.valid());
225 ASSERT(mCommandPoolAccess == nullptr);
226 mPrimaryCommands = std::move(primaryCommands);
227 mCommandPoolAccess = commandPoolAccess;
228 }
229
setSecondaryCommands(SecondaryCommandBufferCollector && secondaryCommands)230 void CommandBatch::setSecondaryCommands(SecondaryCommandBufferCollector &&secondaryCommands)
231 {
232 // secondaryCommands is optional.
233 ASSERT(mSecondaryCommands.empty());
234 mSecondaryCommands = std::move(secondaryCommands);
235 }
236
initFence(VkDevice device,FenceRecycler * recycler)237 VkResult CommandBatch::initFence(VkDevice device, FenceRecycler *recycler)
238 {
239 ASSERT(!hasFence());
240 auto fence = SharedFence::MakeShared(device);
241 const VkResult result = fence->init(device, recycler);
242 if (result == VK_SUCCESS)
243 {
244 ASSERT(fence->valid());
245 mFence = std::move(fence);
246 }
247 return result;
248 }
249
setExternalFence(SharedExternalFence && externalFence)250 void CommandBatch::setExternalFence(SharedExternalFence &&externalFence)
251 {
252 ASSERT(!hasFence());
253 mExternalFence = std::move(externalFence);
254 }
255
getQueueSerial() const256 const QueueSerial &CommandBatch::getQueueSerial() const
257 {
258 ASSERT(mQueueSerial.valid());
259 return mQueueSerial;
260 }
261
getPrimaryCommands() const262 const PrimaryCommandBuffer &CommandBatch::getPrimaryCommands() const
263 {
264 return mPrimaryCommands;
265 }
266
getExternalFence()267 const SharedExternalFence &CommandBatch::getExternalFence()
268 {
269 return mExternalFence;
270 }
271
hasFence() const272 bool CommandBatch::hasFence() const
273 {
274 ASSERT(!mExternalFence || !mFence);
275 ASSERT(!mFence || mFence->valid());
276 return mFence || mExternalFence;
277 }
278
getFenceHandle() const279 VkFence CommandBatch::getFenceHandle() const
280 {
281 ASSERT(hasFence());
282 return mFence ? mFence->get().getHandle() : mExternalFence->getHandle();
283 }
284
getFenceStatus(VkDevice device) const285 VkResult CommandBatch::getFenceStatus(VkDevice device) const
286 {
287 ASSERT(hasFence());
288 return mFence ? mFence->get().getStatus(device) : mExternalFence->getStatus(device);
289 }
290
waitFence(VkDevice device,uint64_t timeout) const291 VkResult CommandBatch::waitFence(VkDevice device, uint64_t timeout) const
292 {
293 ASSERT(hasFence());
294 return mFence ? mFence->get().wait(device, timeout) : mExternalFence->wait(device, timeout);
295 }
296
waitFenceUnlocked(VkDevice device,uint64_t timeout,std::unique_lock<angle::SimpleMutex> * lock) const297 VkResult CommandBatch::waitFenceUnlocked(VkDevice device,
298 uint64_t timeout,
299 std::unique_lock<angle::SimpleMutex> *lock) const
300 {
301 ASSERT(hasFence());
302 VkResult status;
303 // You can only use the local copy of the fence without lock.
304 // Do not access "this" after unlock() because object might be deleted from other thread.
305 if (mFence)
306 {
307 const SharedFence localFenceToWaitOn = mFence;
308 lock->unlock();
309 status = localFenceToWaitOn->get().wait(device, timeout);
310 lock->lock();
311 }
312 else
313 {
314 const SharedExternalFence localFenceToWaitOn = mExternalFence;
315 lock->unlock();
316 status = localFenceToWaitOn->wait(device, timeout);
317 lock->lock();
318 }
319 return status;
320 }
321
322 // CleanUpThread implementation.
handleError(VkResult errorCode,const char * file,const char * function,unsigned int line)323 void CleanUpThread::handleError(VkResult errorCode,
324 const char *file,
325 const char *function,
326 unsigned int line)
327 {
328 ASSERT(errorCode != VK_SUCCESS);
329
330 std::stringstream errorStream;
331 errorStream << "Internal Vulkan error (" << errorCode << "): " << VulkanResultString(errorCode)
332 << ".";
333
334 if (errorCode == VK_ERROR_DEVICE_LOST)
335 {
336 WARN() << errorStream.str();
337 mCommandQueue->handleDeviceLost(mRenderer);
338 }
339
340 std::lock_guard<angle::SimpleMutex> queueLock(mErrorMutex);
341 Error error = {errorCode, file, function, line};
342 mErrors.emplace(error);
343 }
344
CleanUpThread(Renderer * renderer,CommandQueue * commandQueue)345 CleanUpThread::CleanUpThread(Renderer *renderer, CommandQueue *commandQueue)
346 : ErrorContext(renderer),
347 mCommandQueue(commandQueue),
348 mTaskThreadShouldExit(false),
349 mNeedCleanUp(false)
350 {}
351
352 CleanUpThread::~CleanUpThread() = default;
353
checkAndPopPendingError(ErrorContext * errorHandlingContext)354 angle::Result CleanUpThread::checkAndPopPendingError(ErrorContext *errorHandlingContext)
355 {
356 std::lock_guard<angle::SimpleMutex> queueLock(mErrorMutex);
357 if (mErrors.empty())
358 {
359 return angle::Result::Continue;
360 }
361
362 while (!mErrors.empty())
363 {
364 Error err = mErrors.front();
365 mErrors.pop();
366 errorHandlingContext->handleError(err.errorCode, err.file, err.function, err.line);
367 }
368 return angle::Result::Stop;
369 }
370
requestCleanUp()371 void CleanUpThread::requestCleanUp()
372 {
373 if (!mNeedCleanUp.exchange(true))
374 {
375 // request clean up in async thread
376 std::unique_lock<std::mutex> enqueueLock(mMutex);
377 mWorkAvailableCondition.notify_one();
378 }
379 }
380
processTasks()381 void CleanUpThread::processTasks()
382 {
383 angle::SetCurrentThreadName("ANGLE-GC");
384
385 while (true)
386 {
387 bool exitThread = false;
388 (void)processTasksImpl(&exitThread);
389 if (exitThread)
390 {
391 // We are doing a controlled exit of the thread, break out of the while loop.
392 break;
393 }
394 }
395 }
396
processTasksImpl(bool * exitThread)397 angle::Result CleanUpThread::processTasksImpl(bool *exitThread)
398 {
399 while (true)
400 {
401 std::unique_lock<std::mutex> lock(mMutex);
402 mWorkAvailableCondition.wait(lock,
403 [this] { return mTaskThreadShouldExit || mNeedCleanUp; });
404
405 if (mTaskThreadShouldExit)
406 {
407 break;
408 }
409 lock.unlock();
410
411 if (mNeedCleanUp.exchange(false))
412 {
413 // Always check completed commands again in case anything new has been finished.
414 ANGLE_TRY(mCommandQueue->checkCompletedCommands(this));
415
416 // Reset command buffer and clean up garbage
417 if (mRenderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled() &&
418 mCommandQueue->hasFinishedCommands())
419 {
420 ANGLE_TRY(mCommandQueue->releaseFinishedCommands(this));
421 }
422 mRenderer->cleanupGarbage(nullptr);
423 }
424 }
425 *exitThread = true;
426 return angle::Result::Continue;
427 }
428
init()429 angle::Result CleanUpThread::init()
430 {
431 mTaskThread = std::thread(&CleanUpThread::processTasks, this);
432
433 return angle::Result::Continue;
434 }
435
destroy(ErrorContext * context)436 void CleanUpThread::destroy(ErrorContext *context)
437 {
438 {
439 // Request to terminate the worker thread
440 std::lock_guard<std::mutex> lock(mMutex);
441 mTaskThreadShouldExit = true;
442 mNeedCleanUp = false;
443 mWorkAvailableCondition.notify_one();
444 }
445
446 // Perform any lingering clean up right away.
447 if (mRenderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled())
448 {
449 (void)mCommandQueue->releaseFinishedCommands(context);
450 mRenderer->cleanupGarbage(nullptr);
451 }
452
453 if (mTaskThread.joinable())
454 {
455 mTaskThread.join();
456 }
457 }
458
459 CommandPoolAccess::CommandPoolAccess() = default;
460 CommandPoolAccess::~CommandPoolAccess() = default;
461
462 // CommandPoolAccess public API implementation. These must be thread safe and never called from
463 // CommandPoolAccess class itself.
initCommandPool(ErrorContext * context,ProtectionType protectionType,const uint32_t queueFamilyIndex)464 angle::Result CommandPoolAccess::initCommandPool(ErrorContext *context,
465 ProtectionType protectionType,
466 const uint32_t queueFamilyIndex)
467 {
468 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
469 PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[protectionType];
470 return commandPool.init(context, protectionType, queueFamilyIndex);
471 }
472
destroy(VkDevice device)473 void CommandPoolAccess::destroy(VkDevice device)
474 {
475 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
476 for (auto &protectionMap : mCommandsStateMap)
477 {
478 for (CommandsState &state : protectionMap)
479 {
480 state.waitSemaphores.clear();
481 state.waitSemaphoreStageMasks.clear();
482 state.primaryCommands.destroy(device);
483 state.secondaryCommands.releaseCommandBuffers();
484 }
485 }
486
487 for (PersistentCommandPool &commandPool : mPrimaryCommandPoolMap)
488 {
489 commandPool.destroy(device);
490 }
491 }
492
destroyPrimaryCommandBuffer(VkDevice device,PrimaryCommandBuffer * primaryCommands) const493 void CommandPoolAccess::destroyPrimaryCommandBuffer(VkDevice device,
494 PrimaryCommandBuffer *primaryCommands) const
495 {
496 ASSERT(primaryCommands->valid());
497
498 // Does not require a pool mutex lock.
499 primaryCommands->destroy(device);
500 }
501
collectPrimaryCommandBuffer(ErrorContext * context,const ProtectionType protectionType,PrimaryCommandBuffer * primaryCommands)502 angle::Result CommandPoolAccess::collectPrimaryCommandBuffer(ErrorContext *context,
503 const ProtectionType protectionType,
504 PrimaryCommandBuffer *primaryCommands)
505 {
506 ASSERT(primaryCommands->valid());
507 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
508
509 PersistentCommandPool &commandPool = mPrimaryCommandPoolMap[protectionType];
510 ANGLE_TRY(commandPool.collect(context, std::move(*primaryCommands)));
511
512 return angle::Result::Continue;
513 }
514
flushOutsideRPCommands(Context * context,ProtectionType protectionType,egl::ContextPriority priority,OutsideRenderPassCommandBufferHelper ** outsideRPCommands)515 angle::Result CommandPoolAccess::flushOutsideRPCommands(
516 Context *context,
517 ProtectionType protectionType,
518 egl::ContextPriority priority,
519 OutsideRenderPassCommandBufferHelper **outsideRPCommands)
520 {
521 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
522 ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
523 CommandsState &state = mCommandsStateMap[priority][protectionType];
524 return (*outsideRPCommands)->flushToPrimary(context, &state);
525 }
526
flushRenderPassCommands(Context * context,const ProtectionType & protectionType,const egl::ContextPriority & priority,const RenderPass & renderPass,VkFramebuffer framebufferOverride,RenderPassCommandBufferHelper ** renderPassCommands)527 angle::Result CommandPoolAccess::flushRenderPassCommands(
528 Context *context,
529 const ProtectionType &protectionType,
530 const egl::ContextPriority &priority,
531 const RenderPass &renderPass,
532 VkFramebuffer framebufferOverride,
533 RenderPassCommandBufferHelper **renderPassCommands)
534 {
535 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
536 ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
537 CommandsState &state = mCommandsStateMap[priority][protectionType];
538 return (*renderPassCommands)->flushToPrimary(context, &state, renderPass, framebufferOverride);
539 }
540
flushWaitSemaphores(ProtectionType protectionType,egl::ContextPriority priority,std::vector<VkSemaphore> && waitSemaphores,std::vector<VkPipelineStageFlags> && waitSemaphoreStageMasks)541 void CommandPoolAccess::flushWaitSemaphores(
542 ProtectionType protectionType,
543 egl::ContextPriority priority,
544 std::vector<VkSemaphore> &&waitSemaphores,
545 std::vector<VkPipelineStageFlags> &&waitSemaphoreStageMasks)
546 {
547 ASSERT(!waitSemaphores.empty());
548 ASSERT(waitSemaphores.size() == waitSemaphoreStageMasks.size());
549 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
550
551 CommandsState &state = mCommandsStateMap[priority][protectionType];
552
553 state.waitSemaphores.insert(state.waitSemaphores.end(), waitSemaphores.begin(),
554 waitSemaphores.end());
555 state.waitSemaphoreStageMasks.insert(state.waitSemaphoreStageMasks.end(),
556 waitSemaphoreStageMasks.begin(),
557 waitSemaphoreStageMasks.end());
558
559 waitSemaphores.clear();
560 waitSemaphoreStageMasks.clear();
561 }
562
getCommandsAndWaitSemaphores(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority priority,CommandBatch * batchOut,std::vector<VkImageMemoryBarrier> && imagesToTransitionToForeign,std::vector<VkSemaphore> * waitSemaphoresOut,std::vector<VkPipelineStageFlags> * waitSemaphoreStageMasksOut)563 angle::Result CommandPoolAccess::getCommandsAndWaitSemaphores(
564 ErrorContext *context,
565 ProtectionType protectionType,
566 egl::ContextPriority priority,
567 CommandBatch *batchOut,
568 std::vector<VkImageMemoryBarrier> &&imagesToTransitionToForeign,
569 std::vector<VkSemaphore> *waitSemaphoresOut,
570 std::vector<VkPipelineStageFlags> *waitSemaphoreStageMasksOut)
571 {
572 std::lock_guard<angle::SimpleMutex> lock(mCmdPoolMutex);
573
574 CommandsState &state = mCommandsStateMap[priority][protectionType];
575 ASSERT(state.primaryCommands.valid() || state.secondaryCommands.empty());
576
577 // If there are foreign images to transition, issue the barrier now.
578 if (!imagesToTransitionToForeign.empty())
579 {
580 // It is possible for another thread to have made a submission just now, such that there is
581 // no primary command buffer anymore. In that case, one has to be allocated to hold the
582 // barriers.
583 ANGLE_TRY(ensurePrimaryCommandBufferValidLocked(context, protectionType, priority));
584
585 state.primaryCommands.pipelineBarrier(
586 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
587 nullptr, static_cast<uint32_t>(imagesToTransitionToForeign.size()),
588 imagesToTransitionToForeign.data());
589 imagesToTransitionToForeign.clear();
590 }
591
592 // Store the primary CommandBuffer and the reference to CommandPoolAccess in the in-flight list.
593 if (state.primaryCommands.valid())
594 {
595 ANGLE_VK_TRY(context, state.primaryCommands.end());
596 }
597 batchOut->setPrimaryCommands(std::move(state.primaryCommands), this);
598
599 // Store secondary Command Buffers.
600 batchOut->setSecondaryCommands(std::move(state.secondaryCommands));
601
602 // Store wait semaphores.
603 *waitSemaphoresOut = std::move(state.waitSemaphores);
604 *waitSemaphoreStageMasksOut = std::move(state.waitSemaphoreStageMasks);
605
606 return angle::Result::Continue;
607 }
608
609 // CommandQueue public API implementation. These must be thread safe and never called from
610 // CommandQueue class itself.
CommandQueue()611 CommandQueue::CommandQueue()
612 : mInFlightCommands(kInFlightCommandsLimit),
613 mFinishedCommandBatches(kMaxFinishedCommandsLimit),
614 mNumAllCommands(0),
615 mPerfCounters{}
616 {}
617
618 CommandQueue::~CommandQueue() = default;
619
destroy(ErrorContext * context)620 void CommandQueue::destroy(ErrorContext *context)
621 {
622 std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
623 std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
624 std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
625
626 mQueueMap.destroy();
627
628 // Assigns an infinite "last completed" serial to force garbage to delete.
629 mLastCompletedSerials.fill(Serial::Infinite());
630
631 mCommandPoolAccess.destroy(context->getDevice());
632
633 mFenceRecycler.destroy(context);
634
635 ASSERT(mInFlightCommands.empty());
636 ASSERT(mFinishedCommandBatches.empty());
637 ASSERT(mNumAllCommands == 0);
638 }
639
init(ErrorContext * context,const QueueFamily & queueFamily,bool enableProtectedContent,uint32_t queueCount)640 angle::Result CommandQueue::init(ErrorContext *context,
641 const QueueFamily &queueFamily,
642 bool enableProtectedContent,
643 uint32_t queueCount)
644 {
645 std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
646 std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
647 std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
648
649 // In case Renderer gets re-initialized, we can't rely on constructor to do initialization.
650 mLastSubmittedSerials.fill(kZeroSerial);
651 mLastCompletedSerials.fill(kZeroSerial);
652
653 // Assign before initializing the command pools in order to get the queue family index.
654 mQueueMap.initialize(context->getDevice(), queueFamily, enableProtectedContent, 0, queueCount);
655 ANGLE_TRY(mCommandPoolAccess.initCommandPool(context, ProtectionType::Unprotected,
656 mQueueMap.getQueueFamilyIndex()));
657
658 if (mQueueMap.isProtected())
659 {
660 ANGLE_TRY(mCommandPoolAccess.initCommandPool(context, ProtectionType::Protected,
661 mQueueMap.getQueueFamilyIndex()));
662 }
663 return angle::Result::Continue;
664 }
665
handleDeviceLost(Renderer * renderer)666 void CommandQueue::handleDeviceLost(Renderer *renderer)
667 {
668 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::handleDeviceLost");
669 VkDevice device = renderer->getDevice();
670 // Hold all locks while clean up mInFlightCommands.
671 std::lock_guard<angle::SimpleMutex> queueSubmitLock(mQueueSubmitMutex);
672 std::lock_guard<angle::SimpleMutex> cmdCompleteLock(mCmdCompleteMutex);
673 std::lock_guard<angle::SimpleMutex> cmdReleaseLock(mCmdReleaseMutex);
674
675 // Work around a driver bug where resource clean up would cause a crash without vkQueueWaitIdle.
676 mQueueMap.waitAllQueuesIdle();
677
678 while (!mInFlightCommands.empty())
679 {
680 CommandBatch &batch = mInFlightCommands.front();
681 // On device loss we need to wait for fence to be signaled before destroying it
682 if (batch.hasFence())
683 {
684 VkResult status = batch.waitFence(device, renderer->getMaxFenceWaitTimeNs());
685 // If the wait times out, it is probably not possible to recover from lost device
686 ASSERT(status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST);
687 }
688 mLastCompletedSerials.setQueueSerial(batch.getQueueSerial());
689 batch.destroy(device);
690 popInFlightBatchLocked();
691 }
692 }
693
postSubmitCheck(ErrorContext * context)694 angle::Result CommandQueue::postSubmitCheck(ErrorContext *context)
695 {
696 Renderer *renderer = context->getRenderer();
697
698 // Update mLastCompletedQueueSerial immediately in case any command has been finished.
699 ANGLE_TRY(checkAndCleanupCompletedCommands(context));
700
701 VkDeviceSize suballocationGarbageSize = renderer->getSuballocationGarbageSize();
702 while (suballocationGarbageSize > kMaxBufferSuballocationGarbageSize)
703 {
704 // CPU should be throttled to avoid accumulating too much memory garbage waiting to be
705 // destroyed. This is important to keep peak memory usage at check when game launched and a
706 // lot of staging buffers used for textures upload and then gets released. But if there is
707 // only one command buffer in flight, we do not wait here to ensure we keep GPU busy.
708 constexpr size_t kMinInFlightBatchesToKeep = 1;
709 bool anyGarbageCleaned = false;
710 ANGLE_TRY(cleanupSomeGarbage(context, kMinInFlightBatchesToKeep, &anyGarbageCleaned));
711 if (!anyGarbageCleaned)
712 {
713 break;
714 }
715 suballocationGarbageSize = renderer->getSuballocationGarbageSize();
716 }
717
718 if (kOutputVmaStatsString)
719 {
720 renderer->outputVmaStatString();
721 }
722
723 return angle::Result::Continue;
724 }
725
finishResourceUse(ErrorContext * context,const ResourceUse & use,uint64_t timeout)726 angle::Result CommandQueue::finishResourceUse(ErrorContext *context,
727 const ResourceUse &use,
728 uint64_t timeout)
729 {
730 VkDevice device = context->getDevice();
731 {
732 std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
733 while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
734 {
735 bool finished;
736 ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
737 if (!finished)
738 {
739 ANGLE_VK_TRY(context,
740 mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock));
741 }
742 }
743 // Check the rest of the commands in case they are also finished.
744 ANGLE_TRY(checkCompletedCommandsLocked(context));
745 }
746 ASSERT(hasResourceUseFinished(use));
747
748 if (!mFinishedCommandBatches.empty())
749 {
750 ANGLE_TRY(releaseFinishedCommandsAndCleanupGarbage(context));
751 }
752
753 return angle::Result::Continue;
754 }
755
finishQueueSerial(ErrorContext * context,const QueueSerial & queueSerial,uint64_t timeout)756 angle::Result CommandQueue::finishQueueSerial(ErrorContext *context,
757 const QueueSerial &queueSerial,
758 uint64_t timeout)
759 {
760 ResourceUse use(queueSerial);
761 return finishResourceUse(context, use, timeout);
762 }
763
waitIdle(ErrorContext * context,uint64_t timeout)764 angle::Result CommandQueue::waitIdle(ErrorContext *context, uint64_t timeout)
765 {
766 // Fill the local variable with lock
767 ResourceUse use;
768 {
769 std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
770 if (mInFlightCommands.empty())
771 {
772 return angle::Result::Continue;
773 }
774 use.setQueueSerial(mInFlightCommands.back().getQueueSerial());
775 }
776
777 return finishResourceUse(context, use, timeout);
778 }
779
waitForResourceUseToFinishWithUserTimeout(ErrorContext * context,const ResourceUse & use,uint64_t timeout,VkResult * result)780 angle::Result CommandQueue::waitForResourceUseToFinishWithUserTimeout(ErrorContext *context,
781 const ResourceUse &use,
782 uint64_t timeout,
783 VkResult *result)
784 {
785 // Serial is not yet submitted. This is undefined behaviour, so we can do anything.
786 if (!hasResourceUseSubmitted(use))
787 {
788 WARN() << "Waiting on an unsubmitted serial.";
789 *result = VK_TIMEOUT;
790 return angle::Result::Continue;
791 }
792
793 VkDevice device = context->getDevice();
794 size_t finishedCount = 0;
795 {
796 std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
797 *result = hasResourceUseFinished(use) ? VK_SUCCESS : VK_NOT_READY;
798 while (!mInFlightCommands.empty() && !hasResourceUseFinished(use))
799 {
800 bool finished;
801 ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
802 if (!finished)
803 {
804 *result = mInFlightCommands.front().waitFenceUnlocked(device, timeout, &lock);
805 // Don't trigger an error on timeout.
806 if (*result == VK_TIMEOUT)
807 {
808 break;
809 }
810 else
811 {
812 ANGLE_VK_TRY(context, *result);
813 }
814 }
815 else
816 {
817 *result = hasResourceUseFinished(use) ? VK_SUCCESS : VK_NOT_READY;
818 }
819 }
820 // Do one more check in case more commands also finished.
821 ANGLE_TRY(checkCompletedCommandsLocked(context));
822 finishedCount = mFinishedCommandBatches.size();
823 }
824
825 if (finishedCount > 0)
826 {
827 ANGLE_TRY(releaseFinishedCommandsAndCleanupGarbage(context));
828 }
829
830 return angle::Result::Continue;
831 }
832
isBusy(Renderer * renderer) const833 bool CommandQueue::isBusy(Renderer *renderer) const
834 {
835 // No lock is needed here since we are accessing atomic variables only.
836 size_t maxIndex = renderer->getLargestQueueSerialIndexEverAllocated();
837 for (SerialIndex i = 0; i <= maxIndex; ++i)
838 {
839 if (mLastSubmittedSerials[i] > mLastCompletedSerials[i])
840 {
841 return true;
842 }
843 }
844 return false;
845 }
846
submitCommands(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority priority,VkSemaphore signalSemaphore,SharedExternalFence && externalFence,std::vector<VkImageMemoryBarrier> && imagesToTransitionToForeign,const QueueSerial & submitQueueSerial)847 angle::Result CommandQueue::submitCommands(
848 ErrorContext *context,
849 ProtectionType protectionType,
850 egl::ContextPriority priority,
851 VkSemaphore signalSemaphore,
852 SharedExternalFence &&externalFence,
853 std::vector<VkImageMemoryBarrier> &&imagesToTransitionToForeign,
854 const QueueSerial &submitQueueSerial)
855 {
856 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::submitCommands");
857 std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
858 Renderer *renderer = context->getRenderer();
859 VkDevice device = renderer->getDevice();
860
861 ++mPerfCounters.commandQueueSubmitCallsTotal;
862 ++mPerfCounters.commandQueueSubmitCallsPerFrame;
863
864 DeviceScoped<CommandBatch> scopedBatch(device);
865 CommandBatch &batch = scopedBatch.get();
866
867 batch.setQueueSerial(submitQueueSerial);
868 batch.setProtectionType(protectionType);
869
870 std::vector<VkSemaphore> waitSemaphores;
871 std::vector<VkPipelineStageFlags> waitSemaphoreStageMasks;
872
873 ANGLE_TRY(mCommandPoolAccess.getCommandsAndWaitSemaphores(
874 context, protectionType, priority, &batch, std::move(imagesToTransitionToForeign),
875 &waitSemaphores, &waitSemaphoreStageMasks));
876
877 mPerfCounters.commandQueueWaitSemaphoresTotal += waitSemaphores.size();
878
879 // Don't make a submission if there is nothing to submit.
880 const bool needsQueueSubmit = batch.getPrimaryCommands().valid() ||
881 signalSemaphore != VK_NULL_HANDLE || externalFence ||
882 !waitSemaphores.empty();
883 VkSubmitInfo submitInfo = {};
884 VkProtectedSubmitInfo protectedSubmitInfo = {};
885
886 if (needsQueueSubmit)
887 {
888 InitializeSubmitInfo(&submitInfo, batch.getPrimaryCommands(), waitSemaphores,
889 waitSemaphoreStageMasks, signalSemaphore);
890
891 // No need protected submission if no commands to submit.
892 if (protectionType == ProtectionType::Protected && batch.getPrimaryCommands().valid())
893 {
894 protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
895 protectedSubmitInfo.pNext = nullptr;
896 protectedSubmitInfo.protectedSubmit = true;
897 submitInfo.pNext = &protectedSubmitInfo;
898 }
899
900 if (!externalFence)
901 {
902 ANGLE_VK_TRY(context, batch.initFence(context->getDevice(), &mFenceRecycler));
903 }
904 else
905 {
906 batch.setExternalFence(std::move(externalFence));
907 }
908
909 ++mPerfCounters.vkQueueSubmitCallsTotal;
910 ++mPerfCounters.vkQueueSubmitCallsPerFrame;
911 }
912
913 return queueSubmitLocked(context, priority, submitInfo, scopedBatch, submitQueueSerial);
914 }
915
queueSubmitOneOff(ErrorContext * context,ProtectionType protectionType,egl::ContextPriority contextPriority,VkCommandBuffer commandBufferHandle,VkSemaphore waitSemaphore,VkPipelineStageFlags waitSemaphoreStageMask,const QueueSerial & submitQueueSerial)916 angle::Result CommandQueue::queueSubmitOneOff(ErrorContext *context,
917 ProtectionType protectionType,
918 egl::ContextPriority contextPriority,
919 VkCommandBuffer commandBufferHandle,
920 VkSemaphore waitSemaphore,
921 VkPipelineStageFlags waitSemaphoreStageMask,
922 const QueueSerial &submitQueueSerial)
923 {
924 std::unique_lock<angle::SimpleMutex> lock(mQueueSubmitMutex);
925 DeviceScoped<CommandBatch> scopedBatch(context->getDevice());
926 CommandBatch &batch = scopedBatch.get();
927 batch.setQueueSerial(submitQueueSerial);
928 batch.setProtectionType(protectionType);
929
930 ANGLE_VK_TRY(context, batch.initFence(context->getDevice(), &mFenceRecycler));
931
932 VkSubmitInfo submitInfo = {};
933 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
934
935 VkProtectedSubmitInfo protectedSubmitInfo = {};
936 ASSERT(protectionType == ProtectionType::Unprotected ||
937 protectionType == ProtectionType::Protected);
938 if (protectionType == ProtectionType::Protected)
939 {
940 protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
941 protectedSubmitInfo.pNext = nullptr;
942 protectedSubmitInfo.protectedSubmit = true;
943 submitInfo.pNext = &protectedSubmitInfo;
944 }
945
946 if (commandBufferHandle != VK_NULL_HANDLE)
947 {
948 submitInfo.commandBufferCount = 1;
949 submitInfo.pCommandBuffers = &commandBufferHandle;
950 }
951
952 if (waitSemaphore != VK_NULL_HANDLE)
953 {
954 submitInfo.waitSemaphoreCount = 1;
955 submitInfo.pWaitSemaphores = &waitSemaphore;
956 submitInfo.pWaitDstStageMask = &waitSemaphoreStageMask;
957 }
958
959 ++mPerfCounters.vkQueueSubmitCallsTotal;
960 ++mPerfCounters.vkQueueSubmitCallsPerFrame;
961
962 return queueSubmitLocked(context, contextPriority, submitInfo, scopedBatch, submitQueueSerial);
963 }
964
queueSubmitLocked(ErrorContext * context,egl::ContextPriority contextPriority,const VkSubmitInfo & submitInfo,DeviceScoped<CommandBatch> & commandBatch,const QueueSerial & submitQueueSerial)965 angle::Result CommandQueue::queueSubmitLocked(ErrorContext *context,
966 egl::ContextPriority contextPriority,
967 const VkSubmitInfo &submitInfo,
968 DeviceScoped<CommandBatch> &commandBatch,
969 const QueueSerial &submitQueueSerial)
970 {
971 ANGLE_TRACE_EVENT0("gpu.angle", "CommandQueue::queueSubmitLocked");
972 Renderer *renderer = context->getRenderer();
973
974 // CPU should be throttled to avoid mInFlightCommands from growing too fast. Important for
975 // off-screen scenarios.
976 if (mInFlightCommands.full())
977 {
978 std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
979 // Check once more inside the lock in case other thread already finished some/all commands.
980 if (mInFlightCommands.full())
981 {
982 ANGLE_TRY(finishOneCommandBatch(context, renderer->getMaxFenceWaitTimeNs(), &lock));
983 }
984 }
985 // Assert will succeed since new batch is pushed only in this method below.
986 ASSERT(!mInFlightCommands.full());
987
988 // Also ensure that all mInFlightCommands may be moved into the mFinishedCommandBatches without
989 // need of the releaseFinishedCommandsLocked() call.
990 ASSERT(mNumAllCommands <= mFinishedCommandBatches.capacity());
991 if (mNumAllCommands == mFinishedCommandBatches.capacity())
992 {
993 std::lock_guard<angle::SimpleMutex> lock(mCmdReleaseMutex);
994 ANGLE_TRY(releaseFinishedCommandsLocked(context));
995 }
996 // Assert will succeed since mNumAllCommands is incremented only in this method below.
997 ASSERT(mNumAllCommands < mFinishedCommandBatches.capacity());
998
999 if (submitInfo.sType == VK_STRUCTURE_TYPE_SUBMIT_INFO)
1000 {
1001 CommandBatch &batch = commandBatch.get();
1002
1003 VkQueue queue = getQueue(contextPriority);
1004 VkFence fence = batch.getFenceHandle();
1005 ASSERT(fence != VK_NULL_HANDLE);
1006 ANGLE_VK_TRY(context, vkQueueSubmit(queue, 1, &submitInfo, fence));
1007
1008 if (batch.getExternalFence())
1009 {
1010 // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
1011 // obeys copy semantics. This means that the fence must already be signaled or the work
1012 // to signal it is in the graphics pipeline at the time we export the fd.
1013 // In other words, must call exportFd() after successful vkQueueSubmit() call.
1014 ExternalFence &externalFence = *batch.getExternalFence();
1015 VkFenceGetFdInfoKHR fenceGetFdInfo = {};
1016 fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
1017 fenceGetFdInfo.fence = externalFence.getHandle();
1018 fenceGetFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
1019 externalFence.exportFd(renderer->getDevice(), fenceGetFdInfo);
1020 }
1021 }
1022
1023 pushInFlightBatchLocked(commandBatch.release());
1024
1025 // This must set last so that when this submission appears submitted, it actually already
1026 // submitted and enqueued to mInFlightCommands.
1027 mLastSubmittedSerials.setQueueSerial(submitQueueSerial);
1028 return angle::Result::Continue;
1029 }
1030
queuePresent(egl::ContextPriority contextPriority,const VkPresentInfoKHR & presentInfo)1031 VkResult CommandQueue::queuePresent(egl::ContextPriority contextPriority,
1032 const VkPresentInfoKHR &presentInfo)
1033 {
1034 std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1035 VkQueue queue = getQueue(contextPriority);
1036 return vkQueuePresentKHR(queue, &presentInfo);
1037 }
1038
getPerfCounters() const1039 const angle::VulkanPerfCounters CommandQueue::getPerfCounters() const
1040 {
1041 std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1042 return mPerfCounters;
1043 }
1044
resetPerFramePerfCounters()1045 void CommandQueue::resetPerFramePerfCounters()
1046 {
1047 std::lock_guard<angle::SimpleMutex> lock(mQueueSubmitMutex);
1048 mPerfCounters.commandQueueSubmitCallsPerFrame = 0;
1049 mPerfCounters.vkQueueSubmitCallsPerFrame = 0;
1050 }
1051
releaseFinishedCommandsAndCleanupGarbage(ErrorContext * context)1052 angle::Result CommandQueue::releaseFinishedCommandsAndCleanupGarbage(ErrorContext *context)
1053 {
1054 Renderer *renderer = context->getRenderer();
1055 if (renderer->isAsyncCommandBufferResetAndGarbageCleanupEnabled())
1056 {
1057 renderer->requestAsyncCommandsAndGarbageCleanup(context);
1058 }
1059 else
1060 {
1061 // Do immediate command buffer reset and garbage cleanup
1062 ANGLE_TRY(releaseFinishedCommands(context));
1063 renderer->cleanupGarbage(nullptr);
1064 }
1065
1066 return angle::Result::Continue;
1067 }
1068
cleanupSomeGarbage(ErrorContext * context,size_t minInFlightBatchesToKeep,bool * anyGarbageCleanedOut)1069 angle::Result CommandQueue::cleanupSomeGarbage(ErrorContext *context,
1070 size_t minInFlightBatchesToKeep,
1071 bool *anyGarbageCleanedOut)
1072 {
1073 Renderer *renderer = context->getRenderer();
1074
1075 bool anyGarbageCleaned = false;
1076
1077 renderer->cleanupGarbage(&anyGarbageCleaned);
1078
1079 while (!anyGarbageCleaned)
1080 {
1081 {
1082 std::unique_lock<angle::SimpleMutex> lock(mCmdCompleteMutex);
1083 if (mInFlightCommands.size() <= minInFlightBatchesToKeep)
1084 {
1085 break;
1086 }
1087 ANGLE_TRY(finishOneCommandBatch(context, renderer->getMaxFenceWaitTimeNs(), &lock));
1088 }
1089 renderer->cleanupGarbage(&anyGarbageCleaned);
1090 }
1091
1092 if (anyGarbageCleanedOut != nullptr)
1093 {
1094 *anyGarbageCleanedOut = anyGarbageCleaned;
1095 }
1096
1097 return angle::Result::Continue;
1098 }
1099
1100 // CommandQueue private API implementation. These are called by public API, so lock already held.
checkOneCommandBatchLocked(ErrorContext * context,bool * finished)1101 angle::Result CommandQueue::checkOneCommandBatchLocked(ErrorContext *context, bool *finished)
1102 {
1103 ASSERT(!mInFlightCommands.empty());
1104
1105 CommandBatch &batch = mInFlightCommands.front();
1106 *finished = false;
1107 if (batch.hasFence())
1108 {
1109 VkResult status = batch.getFenceStatus(context->getDevice());
1110 if (status == VK_NOT_READY)
1111 {
1112 return angle::Result::Continue;
1113 }
1114 ANGLE_VK_TRY(context, status);
1115 }
1116
1117 onCommandBatchFinishedLocked(std::move(batch));
1118 *finished = true;
1119
1120 return angle::Result::Continue;
1121 }
1122
finishOneCommandBatch(ErrorContext * context,uint64_t timeout,std::unique_lock<angle::SimpleMutex> * lock)1123 angle::Result CommandQueue::finishOneCommandBatch(ErrorContext *context,
1124 uint64_t timeout,
1125 std::unique_lock<angle::SimpleMutex> *lock)
1126 {
1127 ASSERT(!mInFlightCommands.empty());
1128 ASSERT(lock->owns_lock());
1129
1130 CommandBatch &batch = mInFlightCommands.front();
1131 // Save queue serial since the batch may be destroyed during possible unlocked fence wait.
1132 const QueueSerial batchSerial = batch.getQueueSerial();
1133 if (batch.hasFence())
1134 {
1135 VkResult status = batch.waitFenceUnlocked(context->getDevice(), timeout, lock);
1136 ANGLE_VK_TRY(context, status);
1137 }
1138
1139 // Other thread might already finish the batch, in that case do not touch the |batch| reference.
1140 if (!hasQueueSerialFinished(batchSerial))
1141 {
1142 onCommandBatchFinishedLocked(std::move(batch));
1143 }
1144
1145 return angle::Result::Continue;
1146 }
1147
onCommandBatchFinishedLocked(CommandBatch && batch)1148 void CommandQueue::onCommandBatchFinishedLocked(CommandBatch &&batch)
1149 {
1150 // Finished.
1151 mLastCompletedSerials.setQueueSerial(batch.getQueueSerial());
1152
1153 // Move command batch to mFinishedCommandBatches.
1154 moveInFlightBatchToFinishedQueueLocked(std::move(batch));
1155 }
1156
releaseFinishedCommandsLocked(ErrorContext * context)1157 angle::Result CommandQueue::releaseFinishedCommandsLocked(ErrorContext *context)
1158 {
1159 ANGLE_TRACE_EVENT0("gpu.angle", "releaseFinishedCommandsLocked");
1160
1161 while (!mFinishedCommandBatches.empty())
1162 {
1163 CommandBatch &batch = mFinishedCommandBatches.front();
1164 ASSERT(batch.getQueueSerial() <= mLastCompletedSerials);
1165 ANGLE_TRY(batch.release(context));
1166 popFinishedBatchLocked();
1167 }
1168
1169 return angle::Result::Continue;
1170 }
1171
checkCompletedCommandsLocked(ErrorContext * context)1172 angle::Result CommandQueue::checkCompletedCommandsLocked(ErrorContext *context)
1173 {
1174 while (!mInFlightCommands.empty())
1175 {
1176 bool finished;
1177 ANGLE_TRY(checkOneCommandBatchLocked(context, &finished));
1178 if (!finished)
1179 {
1180 break;
1181 }
1182 }
1183 return angle::Result::Continue;
1184 }
1185
pushInFlightBatchLocked(CommandBatch && batch)1186 void CommandQueue::pushInFlightBatchLocked(CommandBatch &&batch)
1187 {
1188 // Need to increment before the push to prevent possible decrement from 0.
1189 ++mNumAllCommands;
1190 mInFlightCommands.push(std::move(batch));
1191 }
1192
moveInFlightBatchToFinishedQueueLocked(CommandBatch && batch)1193 void CommandQueue::moveInFlightBatchToFinishedQueueLocked(CommandBatch &&batch)
1194 {
1195 // This must not happen, since we always leave space in the queue during queueSubmitLocked.
1196 ASSERT(!mFinishedCommandBatches.full());
1197 ASSERT(&batch == &mInFlightCommands.front());
1198
1199 mFinishedCommandBatches.push(std::move(batch));
1200 mInFlightCommands.pop();
1201 // No mNumAllCommands update since batch was simply moved to the other queue.
1202 }
1203
popFinishedBatchLocked()1204 void CommandQueue::popFinishedBatchLocked()
1205 {
1206 mFinishedCommandBatches.pop();
1207 // Need to decrement after the pop to prevent possible push over the limit.
1208 ASSERT(mNumAllCommands > 0);
1209 --mNumAllCommands;
1210 }
1211
popInFlightBatchLocked()1212 void CommandQueue::popInFlightBatchLocked()
1213 {
1214 mInFlightCommands.pop();
1215 // Need to decrement after the pop to prevent possible push over the limit.
1216 ASSERT(mNumAllCommands > 0);
1217 --mNumAllCommands;
1218 }
1219
1220 // QueuePriorities:
1221 constexpr float kVulkanQueuePriorityLow = 0.0;
1222 constexpr float kVulkanQueuePriorityMedium = 0.4;
1223 constexpr float kVulkanQueuePriorityHigh = 1.0;
1224
1225 const float QueueFamily::kQueuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] =
1226 {kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
1227
~DeviceQueueMap()1228 DeviceQueueMap::~DeviceQueueMap() {}
1229
destroy()1230 void DeviceQueueMap::destroy()
1231 {
1232 waitAllQueuesIdle();
1233 }
1234
waitAllQueuesIdle()1235 void DeviceQueueMap::waitAllQueuesIdle()
1236 {
1237 // Force all commands to finish by flushing all queues.
1238 for (const QueueAndIndex &queueAndIndex : mQueueAndIndices)
1239 {
1240 if (queueAndIndex.queue != VK_NULL_HANDLE)
1241 {
1242 vkQueueWaitIdle(queueAndIndex.queue);
1243 }
1244 }
1245 }
1246
initialize(VkDevice device,const QueueFamily & queueFamily,bool makeProtected,uint32_t queueIndex,uint32_t queueCount)1247 void DeviceQueueMap::initialize(VkDevice device,
1248 const QueueFamily &queueFamily,
1249 bool makeProtected,
1250 uint32_t queueIndex,
1251 uint32_t queueCount)
1252 {
1253 // QueueIndexing:
1254 constexpr uint32_t kQueueIndexMedium = 0;
1255 constexpr uint32_t kQueueIndexHigh = 1;
1256 constexpr uint32_t kQueueIndexLow = 2;
1257
1258 ASSERT(queueCount);
1259 ASSERT((queueIndex + queueCount) <= queueFamily.getProperties()->queueCount);
1260 mQueueFamilyIndex = queueFamily.getQueueFamilyIndex();
1261 mIsProtected = makeProtected;
1262
1263 VkQueue queue = VK_NULL_HANDLE;
1264 GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexMedium,
1265 &queue);
1266 mQueueAndIndices[egl::ContextPriority::Medium] = {egl::ContextPriority::Medium, queue,
1267 queueIndex + kQueueIndexMedium};
1268
1269 // If at least 2 queues, High has its own queue
1270 if (queueCount > 1)
1271 {
1272 GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexHigh,
1273 &queue);
1274 mQueueAndIndices[egl::ContextPriority::High] = {egl::ContextPriority::High, queue,
1275 queueIndex + kQueueIndexHigh};
1276 }
1277 else
1278 {
1279 mQueueAndIndices[egl::ContextPriority::High] =
1280 mQueueAndIndices[egl::ContextPriority::Medium];
1281 }
1282 // If at least 3 queues, Low has its own queue. Adjust Low priority.
1283 if (queueCount > 2)
1284 {
1285 GetDeviceQueue(device, makeProtected, mQueueFamilyIndex, queueIndex + kQueueIndexLow,
1286 &queue);
1287 mQueueAndIndices[egl::ContextPriority::Low] = {egl::ContextPriority::Low, queue,
1288 queueIndex + kQueueIndexLow};
1289 }
1290 else
1291 {
1292 mQueueAndIndices[egl::ContextPriority::Low] =
1293 mQueueAndIndices[egl::ContextPriority::Medium];
1294 }
1295 }
1296
initialize(const VkQueueFamilyProperties & queueFamilyProperties,uint32_t queueFamilyIndex)1297 void QueueFamily::initialize(const VkQueueFamilyProperties &queueFamilyProperties,
1298 uint32_t queueFamilyIndex)
1299 {
1300 mProperties = queueFamilyProperties;
1301 mQueueFamilyIndex = queueFamilyIndex;
1302 }
1303
FindIndex(const std::vector<VkQueueFamilyProperties> & queueFamilyProperties,VkQueueFlags includeFlags,VkQueueFlags optionalFlags,VkQueueFlags excludeFlags,uint32_t * matchCount)1304 uint32_t QueueFamily::FindIndex(const std::vector<VkQueueFamilyProperties> &queueFamilyProperties,
1305 VkQueueFlags includeFlags,
1306 VkQueueFlags optionalFlags,
1307 VkQueueFlags excludeFlags,
1308 uint32_t *matchCount)
1309 {
1310 // check with both include and optional flags
1311 VkQueueFlags preferredFlags = includeFlags | optionalFlags;
1312 auto findIndexPredicate = [&preferredFlags,
1313 &excludeFlags](const VkQueueFamilyProperties &queueInfo) {
1314 return (queueInfo.queueFlags & excludeFlags) == 0 &&
1315 (queueInfo.queueFlags & preferredFlags) == preferredFlags;
1316 };
1317
1318 auto it = std::find_if(queueFamilyProperties.begin(), queueFamilyProperties.end(),
1319 findIndexPredicate);
1320 if (it == queueFamilyProperties.end())
1321 {
1322 // didn't find a match, exclude the optional flags from the list
1323 preferredFlags = includeFlags;
1324 it = std::find_if(queueFamilyProperties.begin(), queueFamilyProperties.end(),
1325 findIndexPredicate);
1326 }
1327 if (it == queueFamilyProperties.end())
1328 {
1329 *matchCount = 0;
1330 return QueueFamily::kInvalidIndex;
1331 }
1332
1333 *matchCount = 1;
1334 return static_cast<uint32_t>(std::distance(queueFamilyProperties.begin(), it));
1335 }
1336
1337 } // namespace vk
1338 } // namespace rx
1339