| // Copyright 2018 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "dawn/native/vulkan/QueueVk.h" |
| |
| #include "dawn/common/Math.h" |
| #include "dawn/native/Buffer.h" |
| #include "dawn/native/CommandValidation.h" |
| #include "dawn/native/Commands.h" |
| #include "dawn/native/DynamicUploader.h" |
| #include "dawn/native/vulkan/CommandBufferVk.h" |
| #include "dawn/native/vulkan/CommandRecordingContext.h" |
| #include "dawn/native/vulkan/DeviceVk.h" |
| #include "dawn/native/vulkan/FencedDeleter.h" |
| #include "dawn/native/vulkan/SharedFenceVk.h" |
| #include "dawn/native/vulkan/TextureVk.h" |
| #include "dawn/native/vulkan/UtilsVulkan.h" |
| #include "dawn/native/vulkan/VulkanError.h" |
| #include "dawn/platform/DawnPlatform.h" |
| #include "dawn/platform/tracing/TraceEvent.h" |
| #include "partition_alloc/pointers/raw_ptr.h" |
| |
| namespace dawn::native::vulkan { |
| |
| namespace { |
| |
| // Destroy the semaphore when out of scope. |
| class ScopedSignalSemaphore : public NonCopyable { |
| public: |
| ScopedSignalSemaphore(Device* device, VkSemaphore semaphore) |
| : mDevice(device), mSemaphore(semaphore) {} |
| ScopedSignalSemaphore(ScopedSignalSemaphore&& other) |
| : mDevice(other.mDevice), mSemaphore(std::exchange(other.mSemaphore, VK_NULL_HANDLE)) {} |
| ~ScopedSignalSemaphore() { |
| if (mSemaphore != VK_NULL_HANDLE) { |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(mSemaphore); |
| } |
| } |
| |
| VkSemaphore Get() { return mSemaphore; } |
| VkSemaphore* InitializeInto() { return &mSemaphore; } |
| |
| private: |
| raw_ptr<Device> mDevice = nullptr; |
| VkSemaphore mSemaphore = VK_NULL_HANDLE; |
| }; |
| |
| // Destroys command pool/buffer. |
| // TODO(dawn:1601) Revisit this and potentially bake into pool/buffer objects instead. |
| void DestroyCommandPoolAndBuffer(const VulkanFunctions& fn, |
| VkDevice device, |
| const CommandPoolAndBuffer& commands) { |
| // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is |
| // destroyed, but that's not the case in some drivers and they leak memory. So we call |
| // FreeCommandBuffers before DestroyCommandPool to be safe. |
| // TODO(enga): Only do this on a known list of bad drivers. |
| if (commands.pool != VK_NULL_HANDLE) { |
| if (commands.commandBuffer != VK_NULL_HANDLE) { |
| fn.FreeCommandBuffers(device, commands.pool, 1, &commands.commandBuffer); |
| } |
| fn.DestroyCommandPool(device, commands.pool, nullptr); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| // static |
| ResultOrError<Ref<Queue>> Queue::Create(Device* device, |
| const QueueDescriptor* descriptor, |
| uint32_t family) { |
| Ref<Queue> queue = AcquireRef(new Queue(device, descriptor, family)); |
| DAWN_TRY(queue->Initialize()); |
| return queue; |
| } |
| |
| Queue::Queue(Device* device, const QueueDescriptor* descriptor, uint32_t family) |
| : QueueBase(device, descriptor), mQueueFamily(family) {} |
| |
| Queue::~Queue() {} |
| |
| MaybeError Queue::Initialize() { |
| Device* device = ToBackend(GetDevice()); |
| device->fn.GetDeviceQueue(device->GetVkDevice(), mQueueFamily, 0, &mQueue); |
| |
| DAWN_TRY(PrepareRecordingContext()); |
| |
| SetLabelImpl(); |
| return {}; |
| } |
| |
| MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { |
| TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferVk::RecordCommands"); |
| CommandRecordingContext* recordingContext = GetPendingRecordingContext(); |
| for (uint32_t i = 0; i < commandCount; ++i) { |
| DAWN_TRY(ToBackend(commands[i])->RecordCommands(recordingContext)); |
| } |
| TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferVk::RecordCommands"); |
| |
| DAWN_TRY(SubmitPendingCommands()); |
| |
| return {}; |
| } |
| |
| void Queue::SetLabelImpl() { |
| Device* device = ToBackend(GetDevice()); |
| // TODO(crbug.com/dawn/1344): When we start using multiple queues this needs to be adjusted |
| // so it doesn't always change the default queue's label. |
| SetDebugName(device, VK_OBJECT_TYPE_QUEUE, mQueue, "Dawn_Queue", GetLabel()); |
| } |
| |
| bool Queue::HasPendingCommands() const { |
| return mRecordingContext.needsSubmit; |
| } |
| |
| VkQueue Queue::GetVkQueue() const { |
| return mQueue; |
| } |
| |
| ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() { |
| Device* device = ToBackend(GetDevice()); |
| return mFencesInFlight.Use([&](auto fencesInFlight) -> ResultOrError<ExecutionSerial> { |
| ExecutionSerial fenceSerial(0); |
| while (!fencesInFlight->empty()) { |
| VkFence fence = fencesInFlight->front().first; |
| ExecutionSerial tentativeSerial = fencesInFlight->front().second; |
| VkResult result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN( |
| device->fn.GetFenceStatus(device->GetVkDevice(), fence), VK_ERROR_DEVICE_LOST)); |
| |
| // Fence are added in order, so we can stop searching as soon |
| // as we see one that's not ready. |
| if (result == VK_NOT_READY) { |
| return fenceSerial; |
| } else { |
| DAWN_TRY(CheckVkSuccess(::VkResult(result), "GetFenceStatus")); |
| } |
| |
| // Update fenceSerial since fence is ready. |
| fenceSerial = tentativeSerial; |
| |
| mUnusedFences.push_back(fence); |
| |
| DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial()); |
| fencesInFlight->pop_front(); |
| } |
| return fenceSerial; |
| }); |
| } |
| |
| void Queue::ForceEventualFlushOfCommands() { |
| mRecordingContext.needsSubmit |= mRecordingContext.used; |
| } |
| |
| MaybeError Queue::WaitForIdleForDestruction() { |
| // Immediately tag the recording context as unused so we don't try to submit it in Tick. |
| // Move the mRecordingContext.used to mUnusedCommands so it can be cleaned up in |
| // ShutDownImpl |
| if (mRecordingContext.used) { |
| CommandPoolAndBuffer commands = {mRecordingContext.commandPool, |
| mRecordingContext.commandBuffer}; |
| mUnusedCommands.push_back(commands); |
| mRecordingContext = CommandRecordingContext(); |
| } |
| |
| Device* device = ToBackend(GetDevice()); |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| // Ignore the result of QueueWaitIdle: it can return OOM which we can't really do anything |
| // about, Device lost, which means workloads running on the GPU are no longer accessible |
| // (so they are as good as waited on) or success. |
| [[maybe_unused]] VkResult waitIdleResult = |
| VkResult::WrapUnsafe(device->fn.QueueWaitIdle(mQueue)); |
| |
| // Make sure all fences are complete by explicitly waiting on them all |
| mFencesInFlight.Use([&](auto fencesInFlight) { |
| while (!fencesInFlight->empty()) { |
| VkFence fence = fencesInFlight->front().first; |
| ExecutionSerial fenceSerial = fencesInFlight->front().second; |
| DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial()); |
| |
| VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); |
| do { |
| // If WaitForIdleForDesctruction is called while we are Disconnected, it means that |
| // the device lost came from the ErrorInjector and we need to wait without allowing |
| // any more error to be injected. This is because the device lost was "fake" and |
| // commands might still be running. |
| if (GetDevice()->GetState() == Device::State::Disconnected) { |
| result = VkResult::WrapUnsafe( |
| device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX)); |
| continue; |
| } |
| |
| result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN( |
| device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX), |
| VK_ERROR_DEVICE_LOST)); |
| } while (result == VK_TIMEOUT); |
| // Ignore errors from vkWaitForFences: it can be either OOM which we can't do anything |
| // about (and we need to keep going with the destruction of all fences), or device |
| // loss, which means the workload on the GPU is no longer accessible and we can |
| // safely destroy the fence. |
| |
| device->fn.DestroyFence(vkDevice, fence, nullptr); |
| fencesInFlight->pop_front(); |
| } |
| }); |
| return {}; |
| } |
| |
| CommandRecordingContext* Queue::GetPendingRecordingContext(SubmitMode submitMode) { |
| DAWN_ASSERT(mRecordingContext.commandBuffer != VK_NULL_HANDLE); |
| mRecordingContext.needsSubmit |= (submitMode == SubmitMode::Normal); |
| mRecordingContext.used = true; |
| return &mRecordingContext; |
| } |
| |
| MaybeError Queue::PrepareRecordingContext() { |
| DAWN_ASSERT(!mRecordingContext.needsSubmit); |
| DAWN_ASSERT(mRecordingContext.commandBuffer == VK_NULL_HANDLE); |
| DAWN_ASSERT(mRecordingContext.commandPool == VK_NULL_HANDLE); |
| |
| CommandPoolAndBuffer commands; |
| DAWN_TRY_ASSIGN(commands, BeginVkCommandBuffer()); |
| |
| mRecordingContext.commandBuffer = commands.commandBuffer; |
| mRecordingContext.commandPool = commands.pool; |
| mRecordingContext.commandBufferList.push_back(commands.commandBuffer); |
| mRecordingContext.commandPoolList.push_back(commands.pool); |
| |
| return {}; |
| } |
| |
| // Splits the recording context, ending the current command buffer and beginning a new one. |
| // This should not be necessary in most cases, and is provided only to work around driver issues |
| // on some hardware. |
| MaybeError Queue::SplitRecordingContext(CommandRecordingContext* recordingContext) { |
| DAWN_ASSERT(recordingContext->used); |
| Device* device = ToBackend(GetDevice()); |
| |
| DAWN_TRY(CheckVkSuccess(device->fn.EndCommandBuffer(recordingContext->commandBuffer), |
| "vkEndCommandBuffer")); |
| |
| CommandPoolAndBuffer commands; |
| DAWN_TRY_ASSIGN(commands, BeginVkCommandBuffer()); |
| |
| recordingContext->commandBuffer = commands.commandBuffer; |
| recordingContext->commandPool = commands.pool; |
| recordingContext->commandBufferList.push_back(commands.commandBuffer); |
| recordingContext->commandPoolList.push_back(commands.pool); |
| recordingContext->hasRecordedRenderPass = false; |
| |
| return {}; |
| } |
| |
| ResultOrError<CommandPoolAndBuffer> Queue::BeginVkCommandBuffer() { |
| Device* device = ToBackend(GetDevice()); |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| CommandPoolAndBuffer commands; |
| |
| // First try to recycle unused command pools. |
| if (!mUnusedCommands.empty()) { |
| commands = mUnusedCommands.back(); |
| mUnusedCommands.pop_back(); |
| DAWN_TRY_WITH_CLEANUP( |
| CheckVkSuccess(device->fn.ResetCommandPool(vkDevice, commands.pool, 0), |
| "vkResetCommandPool"), |
| { DestroyCommandPoolAndBuffer(device->fn, vkDevice, commands); }); |
| } else { |
| // Create a new command pool for our commands and allocate the command buffer. |
| VkCommandPoolCreateInfo createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
| createInfo.pNext = nullptr; |
| createInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; |
| createInfo.queueFamilyIndex = mQueueFamily; |
| |
| DAWN_TRY(CheckVkSuccess( |
| device->fn.CreateCommandPool(vkDevice, &createInfo, nullptr, &*commands.pool), |
| "vkCreateCommandPool")); |
| |
| VkCommandBufferAllocateInfo allocateInfo; |
| allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
| allocateInfo.pNext = nullptr; |
| allocateInfo.commandPool = commands.pool; |
| allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| allocateInfo.commandBufferCount = 1; |
| |
| DAWN_TRY_WITH_CLEANUP(CheckVkSuccess(device->fn.AllocateCommandBuffers( |
| vkDevice, &allocateInfo, &commands.commandBuffer), |
| "vkAllocateCommandBuffers"), |
| { DestroyCommandPoolAndBuffer(device->fn, vkDevice, commands); }); |
| } |
| |
| // Start the recording of commands in the command buffer. |
| VkCommandBufferBeginInfo beginInfo; |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| beginInfo.pNext = nullptr; |
| beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
| beginInfo.pInheritanceInfo = nullptr; |
| |
| DAWN_TRY_WITH_CLEANUP( |
| CheckVkSuccess(device->fn.BeginCommandBuffer(commands.commandBuffer, &beginInfo), |
| "vkBeginCommandBuffer"), |
| { DestroyCommandPoolAndBuffer(device->fn, vkDevice, commands); }); |
| |
| return commands; |
| } |
| |
| void Queue::RecycleCompletedCommands(ExecutionSerial completedSerial) { |
| for (auto& commands : mCommandsInFlight.IterateUpTo(completedSerial)) { |
| mUnusedCommands.push_back(commands); |
| } |
| mCommandsInFlight.ClearUpTo(completedSerial); |
| } |
| |
| MaybeError Queue::SubmitPendingCommands() { |
| if (!mRecordingContext.needsSubmit) { |
| return {}; |
| } |
| |
| Device* device = ToBackend(GetDevice()); |
| |
| if (!mRecordingContext.mappableBuffersForEagerTransition.empty()) { |
| // Transition mappable buffers back to map usages with the submit. |
| Buffer::TransitionMappableBuffersEagerly( |
| device->fn, &mRecordingContext, mRecordingContext.mappableBuffersForEagerTransition); |
| } |
| std::vector<ScopedSignalSemaphore> externalTextureSemaphores; |
| for (size_t i = 0; i < mRecordingContext.externalTexturesForEagerTransition.size(); ++i) { |
| // Create an external semaphore for each external textures that have been used in the |
| // pending submit. |
| auto& externalTextureSemaphore = |
| externalTextureSemaphores.emplace_back(device, VK_NULL_HANDLE); |
| DAWN_TRY_ASSIGN(*externalTextureSemaphore.InitializeInto(), |
| device->GetExternalSemaphoreService()->CreateExportableSemaphore()); |
| } |
| |
| // Transition eagerly all used external textures for export. |
| for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) { |
| texture->TransitionEagerlyForExport(&mRecordingContext); |
| std::vector<VkSemaphore> waitRequirements = texture->AcquireWaitRequirements(); |
| mRecordingContext.waitSemaphores.insert(mRecordingContext.waitSemaphores.end(), |
| waitRequirements.begin(), waitRequirements.end()); |
| |
| SharedResourceMemoryContents* contents = texture->GetSharedResourceMemoryContents(); |
| if (contents != nullptr) { |
| SharedTextureMemoryBase::PendingFenceList fences; |
| contents->AcquirePendingFences(&fences); |
| |
| for (const auto& fence : fences) { |
| // All semaphores are binary semaphores. |
| DAWN_ASSERT(fence.signaledValue == 1u); |
| ExternalSemaphoreHandle semaphoreHandle = [&]() { |
| if constexpr (std::is_same_v<ExternalSemaphoreHandle, SystemHandle::Handle>) { |
| return ToBackend(fence.object)->GetHandle().Get(); |
| } else { |
| // TODO(crbug.com/dawn/1745): Remove this path and make the semaphore |
| // service use SystemHandle. |
| DAWN_UNREACHABLE(); |
| return ExternalSemaphoreHandle{}; |
| } |
| }(); |
| |
| VkSemaphore semaphore; |
| DAWN_TRY_ASSIGN(semaphore, device->GetExternalSemaphoreService()->ImportSemaphore( |
| semaphoreHandle)); |
| mRecordingContext.waitSemaphores.push_back(semaphore); |
| } |
| } |
| } |
| |
| DAWN_TRY(CheckVkSuccess(device->fn.EndCommandBuffer(mRecordingContext.commandBuffer), |
| "vkEndCommandBuffer")); |
| |
| std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(), |
| VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| |
| for (auto& externalTextureSemaphore : externalTextureSemaphores) { |
| mRecordingContext.signalSemaphores.push_back(externalTextureSemaphore.Get()); |
| } |
| |
| VkSubmitInfo submitInfo; |
| submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submitInfo.pNext = nullptr; |
| submitInfo.waitSemaphoreCount = static_cast<uint32_t>(mRecordingContext.waitSemaphores.size()); |
| submitInfo.pWaitSemaphores = AsVkArray(mRecordingContext.waitSemaphores.data()); |
| submitInfo.pWaitDstStageMask = dstStageMasks.data(); |
| submitInfo.commandBufferCount = mRecordingContext.commandBufferList.size(); |
| submitInfo.pCommandBuffers = mRecordingContext.commandBufferList.data(); |
| submitInfo.signalSemaphoreCount = mRecordingContext.signalSemaphores.size(); |
| submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data()); |
| |
| VkFence fence = VK_NULL_HANDLE; |
| DAWN_TRY_ASSIGN(fence, GetUnusedFence()); |
| DAWN_TRY_WITH_CLEANUP( |
| CheckVkSuccess(device->fn.QueueSubmit(mQueue, 1, &submitInfo, fence), "vkQueueSubmit"), { |
| // If submitting to the queue fails, move the fence back into the unused fence |
| // list, as if it were never acquired. Not doing so would leak the fence since |
| // it would be neither in the unused list nor in the in-flight list. |
| mUnusedFences.push_back(fence); |
| }); |
| |
| // Enqueue the semaphores before incrementing the serial, so that they can be deleted as |
| // soon as the current submission is finished. |
| for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { |
| device->GetFencedDeleter()->DeleteWhenUnused(semaphore); |
| } |
| IncrementLastSubmittedCommandSerial(); |
| ExecutionSerial lastSubmittedSerial = GetLastSubmittedCommandSerial(); |
| mFencesInFlight->emplace_back(fence, lastSubmittedSerial); |
| |
| for (size_t i = 0; i < mRecordingContext.commandBufferList.size(); ++i) { |
| CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPoolList[i], |
| mRecordingContext.commandBufferList[i]}; |
| mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial); |
| } |
| |
| auto externalTextureSemaphoreIter = externalTextureSemaphores.begin(); |
| for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) { |
| // Export the signal semaphore. |
| ExternalSemaphoreHandle semaphoreHandle; |
| DAWN_TRY_ASSIGN(semaphoreHandle, device->GetExternalSemaphoreService()->ExportSemaphore( |
| externalTextureSemaphoreIter->Get())); |
| ++externalTextureSemaphoreIter; |
| |
| // Update all external textures, eagerly transitioned in the submit, with the exported |
| // handles. |
| texture->UpdateExternalSemaphoreHandle(semaphoreHandle); |
| } |
| DAWN_ASSERT(externalTextureSemaphoreIter == externalTextureSemaphores.end()); |
| |
| mRecordingContext = CommandRecordingContext(); |
| DAWN_TRY(PrepareRecordingContext()); |
| |
| return {}; |
| } |
| |
| ResultOrError<VkFence> Queue::GetUnusedFence() { |
| Device* device = ToBackend(GetDevice()); |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| if (!mUnusedFences.empty()) { |
| VkFence fence = mUnusedFences.back(); |
| DAWN_TRY(CheckVkSuccess(device->fn.ResetFences(vkDevice, 1, &*fence), "vkResetFences")); |
| |
| mUnusedFences.pop_back(); |
| return fence; |
| } |
| |
| VkFenceCreateInfo createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| |
| VkFence fence = VK_NULL_HANDLE; |
| DAWN_TRY(CheckVkSuccess(device->fn.CreateFence(vkDevice, &createInfo, nullptr, &*fence), |
| "vkCreateFence")); |
| |
| return fence; |
| } |
| |
| void Queue::DestroyImpl() { |
| Device* device = ToBackend(GetDevice()); |
| VkDevice vkDevice = device->GetVkDevice(); |
| |
| // Immediately tag the recording context as unused so we don't try to submit it in Tick. |
| mRecordingContext.needsSubmit = false; |
| if (mRecordingContext.commandPool != VK_NULL_HANDLE) { |
| DestroyCommandPoolAndBuffer( |
| device->fn, vkDevice, {mRecordingContext.commandPool, mRecordingContext.commandBuffer}); |
| } |
| |
| for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { |
| device->fn.DestroySemaphore(vkDevice, semaphore, nullptr); |
| } |
| mRecordingContext.waitSemaphores.clear(); |
| mRecordingContext.signalSemaphores.clear(); |
| |
| // Some commands might still be marked as in-flight if we shut down because of a device |
| // loss. Recycle them as unused so that we free them below. |
| RecycleCompletedCommands(kMaxExecutionSerial); |
| DAWN_ASSERT(mCommandsInFlight.Empty()); |
| |
| for (const CommandPoolAndBuffer& commands : mUnusedCommands) { |
| DestroyCommandPoolAndBuffer(device->fn, vkDevice, commands); |
| } |
| mUnusedCommands.clear(); |
| |
| // Some fences might still be marked as in-flight if we shut down because of a device loss. |
| // Delete them since at this point all commands are complete. |
| mFencesInFlight.Use([&](auto fencesInFlight) { |
| while (!fencesInFlight->empty()) { |
| device->fn.DestroyFence(vkDevice, *fencesInFlight->front().first, nullptr); |
| fencesInFlight->pop_front(); |
| } |
| }); |
| |
| for (VkFence fence : mUnusedFences) { |
| device->fn.DestroyFence(vkDevice, fence, nullptr); |
| } |
| mUnusedFences.clear(); |
| |
| QueueBase::DestroyImpl(); |
| } |
| |
| ResultOrError<bool> Queue::WaitForQueueSerial(ExecutionSerial serial, Nanoseconds timeout) { |
| Device* device = ToBackend(GetDevice()); |
| VkDevice vkDevice = device->GetVkDevice(); |
| VkResult waitResult = mFencesInFlight.Use([&](auto fencesInFlight) { |
| // Search from for the first fence >= serial. |
| VkFence waitFence = VK_NULL_HANDLE; |
| for (auto it = fencesInFlight->begin(); it != fencesInFlight->end(); ++it) { |
| if (it->second >= serial) { |
| waitFence = it->first; |
| break; |
| } |
| } |
| if (waitFence == VK_NULL_HANDLE) { |
| // Fence not found. This serial must have already completed. |
| // Return a VK_SUCCESS status. |
| DAWN_ASSERT(serial <= GetCompletedCommandSerial()); |
| return VkResult::WrapUnsafe(VK_SUCCESS); |
| } |
| // Wait for the fence. |
| return VkResult::WrapUnsafe( |
| INJECT_ERROR_OR_RUN(device->fn.WaitForFences(vkDevice, 1, &*waitFence, true, |
| static_cast<uint64_t>(timeout)), |
| VK_ERROR_DEVICE_LOST)); |
| }); |
| if (waitResult == VK_TIMEOUT) { |
| return false; |
| } |
| DAWN_TRY(CheckVkSuccess(::VkResult(waitResult), "vkWaitForFences")); |
| return true; |
| } |
| |
| } // namespace dawn::native::vulkan |