diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index 90536c7ef4..4bbca1d02f 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -124,6 +124,7 @@ namespace dawn_native { // complete before proceeding with destruction. // Assert that errors are device loss so that we can continue with destruction AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction()); + ASSERT(mCompletedSerial == mLastSubmittedSerial); break; case State::BeingDisconnected: @@ -173,6 +174,7 @@ namespace dawn_native { // Assert that errors are device losses so that we can continue with destruction. AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction()); + ASSERT(mCompletedSerial == mLastSubmittedSerial); mState = State::Disconnected; // Now everything is as if the device was lost. @@ -292,6 +294,45 @@ namespace dawn_native { return mFenceSignalTracker.get(); } + Serial DeviceBase::GetCompletedCommandSerial() const { + return mCompletedSerial; + } + + Serial DeviceBase::GetLastSubmittedCommandSerial() const { + return mLastSubmittedSerial; + } + + void DeviceBase::IncrementLastSubmittedCommandSerial() { + mLastSubmittedSerial++; + } + + void DeviceBase::ArtificiallyIncrementSerials() { + mCompletedSerial++; + mLastSubmittedSerial++; + } + + void DeviceBase::AssumeCommandsComplete() { + mLastSubmittedSerial++; + mCompletedSerial = mLastSubmittedSerial; + } + + Serial DeviceBase::GetPendingCommandSerial() const { + return mLastSubmittedSerial + 1; + } + + void DeviceBase::CheckPassedSerials() { + Serial completedSerial = CheckAndUpdateCompletedSerials(); + + ASSERT(completedSerial <= mLastSubmittedSerial); + // completedSerial should not be less than mCompletedSerial unless it is 0. + // It can be 0 when there's no fences to check. + ASSERT(completedSerial >= mCompletedSerial || completedSerial == 0); + + if (completedSerial > mCompletedSerial) { + mCompletedSerial = completedSerial; + } + } + ResultOrError DeviceBase::GetInternalFormat(wgpu::TextureFormat format) const { size_t index = ComputeFormatIndex(format); if (index >= mFormatTable.size()) { diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h index 5187427246..0a270a54cf 100644 --- a/src/dawn_native/Device.h +++ b/src/dawn_native/Device.h @@ -86,9 +86,9 @@ namespace dawn_native { CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) = 0; - virtual Serial GetCompletedCommandSerial() const = 0; - virtual Serial GetLastSubmittedCommandSerial() const = 0; - virtual Serial GetPendingCommandSerial() const = 0; + Serial GetCompletedCommandSerial() const; + Serial GetLastSubmittedCommandSerial() const; + Serial GetPendingCommandSerial() const; virtual MaybeError TickImpl() = 0; // Many Dawn objects are completely immutable once created which means that if two @@ -220,6 +220,18 @@ namespace dawn_native { MaybeError Initialize(QueueBase* defaultQueue); void ShutDownBase(); + // Incrememt mLastSubmittedSerial when we submit the next serial + void IncrementLastSubmittedCommandSerial(); + // If there's no GPU work in flight we still need to artificially increment the serial + // so that CPU operations waiting on GPU completion can know they don't have to wait. + void ArtificiallyIncrementSerials(); + // During shut down of device, some operations might have been started since the last submit + // and waiting on a serial that doesn't have a corresponding fence enqueued. Fake serials to + // make all commands look completed. + void AssumeCommandsComplete(); + // Check for passed fences and set the new completed serial + void CheckPassedSerials(); + private: virtual ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) = 0; @@ -281,6 +293,18 @@ namespace dawn_native { void ConsumeError(std::unique_ptr error); + // Each backend should implement to check their passed fences if there are any and return a + // completed serial. Return 0 should indicate no fences to check. + virtual Serial CheckAndUpdateCompletedSerials() = 0; + // mCompletedSerial tracks the last completed command serial that the fence has returned. + // mLastSubmittedSerial tracks the last submitted command serial. + // During device removal, the serials could be artificially incremented + // to make it appear as if commands have been compeleted. They can also be artificially + // incremented when no work is being done in the GPU so CPU operations don't have to wait on + // stale serials. + Serial mCompletedSerial = 0; + Serial mLastSubmittedSerial = 0; + // ShutDownImpl is used to clean up and release resources used by device, does not wait for // GPU or check errors. virtual void ShutDownImpl() = 0; diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp index fdd620ffe6..ce935a9813 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -72,9 +72,10 @@ namespace dawn_native { namespace d3d12 { // value. mCommandQueue.As(&mD3d12SharingContract); - DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(&mFence)), - "D3D12 create fence")); + DAWN_TRY( + CheckHRESULT(mD3d12Device->CreateFence(GetLastSubmittedCommandSerial(), + D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)), + "D3D12 create fence")); mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); ASSERT(mFenceEvent != nullptr); @@ -198,44 +199,35 @@ namespace dawn_native { namespace d3d12 { return &mPendingCommands; } - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - MaybeError Device::TickImpl() { - // Perform cleanup operations to free unused objects - mCompletedSerial = mFence->GetCompletedValue(); + CheckPassedSerials(); - mResourceAllocatorManager->Tick(mCompletedSerial); - DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial)); - mViewShaderVisibleDescriptorAllocator->Tick(mCompletedSerial); - mSamplerShaderVisibleDescriptorAllocator->Tick(mCompletedSerial); - mRenderTargetViewAllocator->Tick(mCompletedSerial); - mDepthStencilViewAllocator->Tick(mCompletedSerial); - mMapRequestTracker->Tick(mCompletedSerial); - mUsedComObjectRefs.ClearUpTo(mCompletedSerial); + // Perform cleanup operations to free unused objects + Serial completedSerial = GetCompletedCommandSerial(); + + mResourceAllocatorManager->Tick(completedSerial); + DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial)); + mViewShaderVisibleDescriptorAllocator->Tick(completedSerial); + mSamplerShaderVisibleDescriptorAllocator->Tick(completedSerial); + mRenderTargetViewAllocator->Tick(completedSerial); + mDepthStencilViewAllocator->Tick(completedSerial); + mMapRequestTracker->Tick(completedSerial); + mUsedComObjectRefs.ClearUpTo(completedSerial); DAWN_TRY(ExecutePendingCommandContext()); DAWN_TRY(NextSerial()); return {}; } MaybeError Device::NextSerial() { - mLastSubmittedSerial++; - return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial), + IncrementLastSubmittedCommandSerial(); + + return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), GetLastSubmittedCommandSerial()), "D3D12 command queue signal fence"); } MaybeError Device::WaitForSerial(uint64_t serial) { - mCompletedSerial = mFence->GetCompletedValue(); - if (mCompletedSerial < serial) { + CheckPassedSerials(); + if (GetCompletedCommandSerial() < serial) { DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(serial, mFenceEvent), "D3D12 set event on completion")); WaitForSingleObject(mFenceEvent, INFINITE); @@ -243,6 +235,10 @@ namespace dawn_native { namespace d3d12 { return {}; } + Serial Device::CheckAndUpdateCompletedSerials() { + return mFence->GetCompletedValue(); + } + void Device::ReferenceUntilUnused(ComPtr object) { mUsedComObjectRefs.Enqueue(object, GetPendingCommandSerial()); } @@ -447,11 +443,13 @@ namespace dawn_native { namespace d3d12 { DAWN_TRY(NextSerial()); // Wait for all in-flight commands to finish executing - DAWN_TRY(WaitForSerial(mLastSubmittedSerial)); + DAWN_TRY(WaitForSerial(GetLastSubmittedCommandSerial())); // Call tick one last time so resources are cleaned up. DAWN_TRY(TickImpl()); + // Force all operations to look as if they were completed + AssumeCommandsComplete(); return {}; } @@ -461,16 +459,16 @@ namespace dawn_native { namespace d3d12 { // Immediately forget about all pending commands mPendingCommands.Release(); - // GPU is no longer executing commands. Existing objects do not get freed until the device - // is destroyed. To ensure objects are always released, force the completed serial to be - // MAX. - mCompletedSerial = std::numeric_limits::max(); + // Some operations might have been started since the last submit and waiting + // on a serial that doesn't have a corresponding fence enqueued. Force all + // operations to look as if they were completed (because they were). + AssumeCommandsComplete(); if (mFenceEvent != nullptr) { ::CloseHandle(mFenceEvent); } - mUsedComObjectRefs.ClearUpTo(mCompletedSerial); + mUsedComObjectRefs.ClearUpTo(GetCompletedCommandSerial()); ASSERT(mUsedComObjectRefs.Empty()); ASSERT(!mPendingCommands.IsOpen()); diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h index 569acbf127..450523024a 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.h +++ b/src/dawn_native/d3d12/DeviceD3D12.h @@ -55,8 +55,6 @@ namespace dawn_native { namespace d3d12 { CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; MaybeError TickImpl() override; ID3D12Device* GetD3D12Device() const; @@ -75,7 +73,6 @@ namespace dawn_native { namespace d3d12 { ComPtr GetFactory() const; ResultOrError GetPendingCommandContext(); - Serial GetPendingCommandSerial() const override; const D3D12DeviceInfo& GetDeviceInfo() const; @@ -156,10 +153,9 @@ namespace dawn_native { namespace d3d12 { void ShutDownImpl() override; MaybeError WaitForIdleForDestruction() override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; ComPtr mFence; HANDLE mFenceEvent = nullptr; + Serial CheckAndUpdateCompletedSerials() override; ComPtr mD3d12Device; // Device is owned by adapter and will not be outlived. ComPtr mCommandQueue; diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h index bc755916c3..b09926a857 100644 --- a/src/dawn_native/metal/DeviceMTL.h +++ b/src/dawn_native/metal/DeviceMTL.h @@ -46,15 +46,12 @@ namespace dawn_native { namespace metal { CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; MaybeError TickImpl() override; id GetMTLDevice(); id GetMTLQueue(); CommandRecordingContext* GetPendingCommandContext(); - Serial GetPendingCommandSerial() const override; void SubmitPendingCommandBuffer(); MapRequestTracker* GetMapTracker() const; @@ -103,12 +100,12 @@ namespace dawn_native { namespace metal { void InitTogglesFromDriver(); void ShutDownImpl() override; MaybeError WaitForIdleForDestruction() override; + Serial CheckAndUpdateCompletedSerials() override; id mMtlDevice = nil; id mCommandQueue = nil; std::unique_ptr mMapTracker; - Serial mLastSubmittedSerial = 0; CommandRecordingContext mCommandContext; // The completed serial is updated in a Metal completion handler that can be fired on a diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm index 30521b85c4..ba24d06780 100644 --- a/src/dawn_native/metal/DeviceMTL.mm +++ b/src/dawn_native/metal/DeviceMTL.mm @@ -154,31 +154,30 @@ namespace dawn_native { namespace metal { return new TextureView(texture, descriptor); } - Serial Device::GetCompletedCommandSerial() const { + Serial Device::CheckAndUpdateCompletedSerials() { + if (GetCompletedCommandSerial() > mCompletedSerial) { + // sometimes we artificially increase the serials, in which case the completed serial in + // the device base will surpass the completed serial we have in the metal backend, so we + // must update ours when we see that the completed serial from the frontend has + // increased. + mCompletedSerial = GetCompletedCommandSerial(); + } static_assert(std::is_same::value, ""); return mCompletedSerial.load(); } - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - MaybeError Device::TickImpl() { + CheckPassedSerials(); Serial completedSerial = GetCompletedCommandSerial(); mMapTracker->Tick(completedSerial); if (mCommandContext.GetCommands() != nil) { SubmitPendingCommandBuffer(); - } else if (completedSerial == mLastSubmittedSerial) { + } else if (completedSerial == GetLastSubmittedCommandSerial()) { // If there's no GPU work in flight we still need to artificially increment the serial // so that CPU operations waiting on GPU completion can know they don't have to wait. - mCompletedSerial++; - mLastSubmittedSerial++; + ArtificiallyIncrementSerials(); } return {}; @@ -208,7 +207,7 @@ namespace dawn_native { namespace metal { return; } - mLastSubmittedSerial++; + IncrementLastSubmittedCommandSerial(); // Acquire the pending command buffer, which is retained. It must be released later. id pendingCommands = mCommandContext.AcquireCommands(); @@ -231,7 +230,8 @@ namespace dawn_native { namespace metal { // Update the completed serial once the completed handler is fired. Make a local copy of // mLastSubmittedSerial so it is captured by value. - Serial pendingSerial = mLastSubmittedSerial; + Serial pendingSerial = GetLastSubmittedCommandSerial(); + // this ObjC block runs on a different thread [pendingCommands addCompletedHandler:^(id) { TRACE_EVENT_ASYNC_END0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer", pendingSerial); @@ -299,17 +299,22 @@ namespace dawn_native { namespace metal { MaybeError Device::WaitForIdleForDestruction() { [mCommandContext.AcquireCommands() release]; + CheckPassedSerials(); // Wait for all commands to be finished so we can free resources - while (GetCompletedCommandSerial() != mLastSubmittedSerial) { + while (GetCompletedCommandSerial() != GetLastSubmittedCommandSerial()) { usleep(100); + CheckPassedSerials(); } // Artificially increase the serials so work that was pending knows it can complete. - mCompletedSerial++; - mLastSubmittedSerial++; + ArtificiallyIncrementSerials(); DAWN_TRY(TickImpl()); + + // Force all operations to look as if they were completed + AssumeCommandsComplete(); + return {}; } diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp index 1f75cf9523..0dac43e170 100644 --- a/src/dawn_native/null/DeviceNull.cpp +++ b/src/dawn_native/null/DeviceNull.cpp @@ -186,7 +186,7 @@ namespace dawn_native { namespace null { MaybeError Device::WaitForIdleForDestruction() { // Fake all commands being completed - mCompletedSerial = mLastSubmittedSerial; + AssumeCommandsComplete(); return {}; } @@ -221,23 +221,15 @@ namespace dawn_native { namespace null { mMemoryUsage -= bytes; } - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - MaybeError Device::TickImpl() { SubmitPendingOperations(); return {}; } + Serial Device::CheckAndUpdateCompletedSerials() { + return GetLastSubmittedCommandSerial(); + } + void Device::AddPendingOperation(std::unique_ptr operation) { mPendingOperations.emplace_back(std::move(operation)); } @@ -247,8 +239,8 @@ namespace dawn_native { namespace null { } mPendingOperations.clear(); - mCompletedSerial = mLastSubmittedSerial; - mLastSubmittedSerial++; + CheckPassedSerials(); + IncrementLastSubmittedCommandSerial(); } // BindGroupDataHolder diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h index fc67316708..a78c8a3179 100644 --- a/src/dawn_native/null/DeviceNull.h +++ b/src/dawn_native/null/DeviceNull.h @@ -91,9 +91,6 @@ namespace dawn_native { namespace null { CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - Serial GetPendingCommandSerial() const override; MaybeError TickImpl() override; void AddPendingOperation(std::unique_ptr operation); @@ -138,11 +135,11 @@ namespace dawn_native { namespace null { TextureBase* texture, const TextureViewDescriptor* descriptor) override; + Serial CheckAndUpdateCompletedSerials() override; + void ShutDownImpl() override; MaybeError WaitForIdleForDestruction() override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; std::vector> mPendingOperations; static constexpr size_t kMaxMemoryUsage = 256 * 1024 * 1024; diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp index 47d2eec44c..4508176262 100644 --- a/src/dawn_native/opengl/DeviceGL.cpp +++ b/src/dawn_native/opengl/DeviceGL.cpp @@ -147,46 +147,37 @@ namespace dawn_native { namespace opengl { void Device::SubmitFenceSync() { GLsync sync = gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - mLastSubmittedSerial++; - mFencesInFlight.emplace(sync, mLastSubmittedSerial); - } - - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; + IncrementLastSubmittedCommandSerial(); + mFencesInFlight.emplace(sync, GetLastSubmittedCommandSerial()); } MaybeError Device::TickImpl() { - CheckPassedFences(); + CheckPassedSerials(); return {}; } - void Device::CheckPassedFences() { + Serial Device::CheckAndUpdateCompletedSerials() { + Serial fenceSerial = 0; while (!mFencesInFlight.empty()) { GLsync sync = mFencesInFlight.front().first; - Serial fenceSerial = mFencesInFlight.front().second; + Serial tentativeSerial = mFencesInFlight.front().second; // Fence are added in order, so we can stop searching as soon // as we see one that's not ready. GLenum result = gl.ClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0); if (result == GL_TIMEOUT_EXPIRED) { - continue; + return fenceSerial; } + // Update fenceSerial since fence is ready. + fenceSerial = tentativeSerial; gl.DeleteSync(sync); mFencesInFlight.pop(); - ASSERT(fenceSerial > mCompletedSerial); - mCompletedSerial = fenceSerial; + ASSERT(fenceSerial > GetCompletedCommandSerial()); } + return fenceSerial; } ResultOrError> Device::CreateStagingBuffer(size_t size) { @@ -207,14 +198,17 @@ namespace dawn_native { namespace opengl { // Some operations might have been started since the last submit and waiting // on a serial that doesn't have a corresponding fence enqueued. Force all // operations to look as if they were completed (because they were). - mCompletedSerial = mLastSubmittedSerial + 1; + AssumeCommandsComplete(); } MaybeError Device::WaitForIdleForDestruction() { gl.Finish(); - CheckPassedFences(); + CheckPassedSerials(); ASSERT(mFencesInFlight.empty()); Tick(); + + // Force all operations to look as if they were completed + AssumeCommandsComplete(); return {}; } diff --git a/src/dawn_native/opengl/DeviceGL.h b/src/dawn_native/opengl/DeviceGL.h index a768c89b56..c38fcf67f9 100644 --- a/src/dawn_native/opengl/DeviceGL.h +++ b/src/dawn_native/opengl/DeviceGL.h @@ -52,9 +52,6 @@ namespace dawn_native { namespace opengl { CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - Serial GetPendingCommandSerial() const override; MaybeError TickImpl() override; ResultOrError> CreateStagingBuffer(size_t size) override; @@ -96,12 +93,10 @@ namespace dawn_native { namespace opengl { const TextureViewDescriptor* descriptor) override; void InitTogglesFromDriver(); - void CheckPassedFences(); + Serial CheckAndUpdateCompletedSerials() override; void ShutDownImpl() override; MaybeError WaitForIdleForDestruction() override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; std::queue> mFencesInFlight; GLFormatTable mFormatTable; diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index 4d2040a1b7..a2ba72a183 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -155,39 +155,28 @@ namespace dawn_native { namespace vulkan { return TextureView::Create(texture, descriptor); } - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - MaybeError Device::TickImpl() { - CheckPassedFences(); + CheckPassedSerials(); RecycleCompletedCommands(); - for (Ref& bgl : - mBindGroupLayoutsPendingDeallocation.IterateUpTo(mCompletedSerial)) { - bgl->FinishDeallocation(mCompletedSerial); - } - mBindGroupLayoutsPendingDeallocation.ClearUpTo(mCompletedSerial); + Serial completedSerial = GetCompletedCommandSerial(); - mMapRequestTracker->Tick(mCompletedSerial); - mResourceMemoryAllocator->Tick(mCompletedSerial); - mDeleter->Tick(mCompletedSerial); + for (Ref& bgl : + mBindGroupLayoutsPendingDeallocation.IterateUpTo(completedSerial)) { + bgl->FinishDeallocation(completedSerial); + } + mBindGroupLayoutsPendingDeallocation.ClearUpTo(completedSerial); + + mMapRequestTracker->Tick(completedSerial); + mResourceMemoryAllocator->Tick(completedSerial); + mDeleter->Tick(completedSerial); if (mRecordingContext.used) { DAWN_TRY(SubmitPendingCommands()); - } else if (mCompletedSerial == mLastSubmittedSerial) { + } else if (completedSerial == GetLastSubmittedCommandSerial()) { // If there's no GPU work in flight we still need to artificially increment the serial // so that CPU operations waiting on GPU completion can know they don't have to wait. - mCompletedSerial++; - mLastSubmittedSerial++; + ArtificiallyIncrementSerials(); } return {}; @@ -271,12 +260,13 @@ namespace dawn_native { namespace vulkan { mDeleter->DeleteWhenUnused(semaphore); } - mLastSubmittedSerial++; - mFencesInFlight.emplace(fence, mLastSubmittedSerial); + IncrementLastSubmittedCommandSerial(); + Serial lastSubmittedSerial = GetLastSubmittedCommandSerial(); + mFencesInFlight.emplace(fence, lastSubmittedSerial); CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPool, mRecordingContext.commandBuffer}; - mCommandsInFlight.Enqueue(submittedCommands, mLastSubmittedSerial); + mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial); mRecordingContext = CommandRecordingContext(); DAWN_TRY(PrepareRecordingContext()); @@ -468,11 +458,11 @@ namespace dawn_native { namespace vulkan { return fence; } - void Device::CheckPassedFences() { + Serial Device::CheckAndUpdateCompletedSerials() { + Serial fenceSerial = 0; while (!mFencesInFlight.empty()) { VkFence fence = mFencesInFlight.front().first; - Serial fenceSerial = mFencesInFlight.front().second; - + Serial tentativeSerial = mFencesInFlight.front().second; VkResult result = VkResult::WrapUnsafe( INJECT_ERROR_OR_RUN(fn.GetFenceStatus(mVkDevice, fence), VK_ERROR_DEVICE_LOST)); // TODO: Handle DeviceLost error. @@ -481,15 +471,17 @@ namespace dawn_native { namespace vulkan { // 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; + return fenceSerial; } + // Update fenceSerial since fence is ready. + fenceSerial = tentativeSerial; mUnusedFences.push_back(fence); - mFencesInFlight.pop(); - ASSERT(fenceSerial > mCompletedSerial); - mCompletedSerial = fenceSerial; + ASSERT(fenceSerial > GetCompletedCommandSerial()); + mFencesInFlight.pop(); } + return fenceSerial; } MaybeError Device::PrepareRecordingContext() { @@ -542,10 +534,10 @@ namespace dawn_native { namespace vulkan { } void Device::RecycleCompletedCommands() { - for (auto& commands : mCommandsInFlight.IterateUpTo(mCompletedSerial)) { + for (auto& commands : mCommandsInFlight.IterateUpTo(GetCompletedCommandSerial())) { mUnusedCommands.push_back(commands); } - mCommandsInFlight.ClearUpTo(mCompletedSerial); + mCommandsInFlight.ClearUpTo(GetCompletedCommandSerial()); } ResultOrError> Device::CreateStagingBuffer(size_t size) { @@ -726,13 +718,11 @@ namespace dawn_native { namespace vulkan { // (so they are as good as waited on) or success. DAWN_UNUSED(waitIdleResult); - CheckPassedFences(); - // Make sure all fences are complete by explicitly waiting on them all while (!mFencesInFlight.empty()) { VkFence fence = mFencesInFlight.front().first; Serial fenceSerial = mFencesInFlight.front().second; - ASSERT(fenceSerial > mCompletedSerial); + ASSERT(fenceSerial > GetCompletedCommandSerial()); VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); do { @@ -746,8 +736,10 @@ namespace dawn_native { namespace vulkan { fn.DestroyFence(mVkDevice, fence, nullptr); mFencesInFlight.pop(); - mCompletedSerial = fenceSerial; } + + // Force all operations to look as if they were completed + AssumeCommandsComplete(); return {}; } @@ -790,7 +782,7 @@ namespace dawn_native { namespace vulkan { // Some operations might have been started since the last submit and waiting // on a serial that doesn't have a corresponding fence enqueued. Force all // operations to look as if they were completed (because they were). - mCompletedSerial = mLastSubmittedSerial + 1; + AssumeCommandsComplete(); // Assert that errors are device loss so that we can continue with destruction AssertAndIgnoreDeviceLossError(TickImpl()); @@ -808,7 +800,7 @@ namespace dawn_native { namespace vulkan { // Releasing the uploader enqueues buffers to be released. // Call Tick() again to clear them before releasing the deleter. - mDeleter->Tick(mCompletedSerial); + mDeleter->Tick(GetCompletedCommandSerial()); mMapRequestTracker = nullptr; diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h index cd08aede7f..585d7f076a 100644 --- a/src/dawn_native/vulkan/DeviceVk.h +++ b/src/dawn_native/vulkan/DeviceVk.h @@ -63,7 +63,6 @@ namespace dawn_native { namespace vulkan { RenderPassCache* GetRenderPassCache() const; CommandRecordingContext* GetPendingRecordingContext(); - Serial GetPendingCommandSerial() const override; MaybeError SubmitPendingCommands(); void EnqueueDeferredDeallocation(BindGroupLayout* bindGroupLayout); @@ -82,8 +81,6 @@ namespace dawn_native { namespace vulkan { CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; MaybeError TickImpl() override; ResultOrError> CreateStagingBuffer(size_t size) override; @@ -158,7 +155,7 @@ namespace dawn_native { namespace vulkan { std::unique_ptr mExternalSemaphoreService; ResultOrError GetUnusedFence(); - void CheckPassedFences(); + Serial CheckAndUpdateCompletedSerials() override; // We track which operations are in flight on the GPU with an increasing serial. // This works only because we have a single queue. Each submit to a queue is associated @@ -167,8 +164,6 @@ namespace dawn_native { namespace vulkan { std::queue> mFencesInFlight; // Fences in the unused list aren't reset yet. std::vector mUnusedFences; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; MaybeError PrepareRecordingContext(); void RecycleCompletedCommands();