d3d11: add Buffer::FinalizeMap This postpones the actual d3d11 buffer map call from MapAsyncImpl to FinalizeMap, when its mLastUsageSerial has passed, to avoid the potential map stalls. Bug: dawn:2357 Change-Id: I15152cca55c2f0bb52f5d55108e5524747de9d79 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/175486 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Jie A Chen <jie.a.chen@intel.com> Reviewed-by: Peng Huang <penghuang@chromium.org>
diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp index 5f6c915..9f463f4 100644 --- a/src/dawn/native/d3d11/BufferD3D11.cpp +++ b/src/dawn/native/d3d11/BufferD3D11.cpp
@@ -297,23 +297,43 @@ MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { DAWN_ASSERT(mD3d11NonConstantBuffer); - auto commandContext = ToBackend(GetDevice()->GetQueue()) - ->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal); + mMapReadySerial = mLastUsageSerial; + const ExecutionSerial completedSerial = GetDevice()->GetQueue()->GetCompletedCommandSerial(); + // We may run into map stall in case that the buffer is still being used by previous submitted + // commands. To avoid that, instead we ask Queue to do the map later when mLastUsageSerial has + // passed. + if (mMapReadySerial > completedSerial) { + ToBackend(GetDevice()->GetQueue())->TrackPendingMapBuffer({this}, mMapReadySerial); + } else { + auto commandContext = ToBackend(GetDevice()->GetQueue()) + ->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal); + DAWN_TRY(FinalizeMap(&commandContext, completedSerial)); + } - // TODO(dawn:1705): make sure the map call is not blocked by the GPU operations. - DAWN_TRY(MapInternal(&commandContext)); + return {}; +} - DAWN_TRY(EnsureDataInitialized(&commandContext)); +MaybeError Buffer::FinalizeMap(ScopedCommandRecordingContext* commandContext, + ExecutionSerial completedSerial) { + // Needn't map the buffer if this is for a previous mapAsync that was cancelled. + if (completedSerial >= mMapReadySerial) { + // TODO(dawn:1705): make sure the map call is not blocked by the GPU operations. + DAWN_TRY(MapInternal(commandContext)); + + DAWN_TRY(EnsureDataInitialized(commandContext)); + } return {}; } void Buffer::UnmapImpl() { DAWN_ASSERT(mD3d11NonConstantBuffer); - DAWN_ASSERT(mMappedData); - auto commandContext = ToBackend(GetDevice()->GetQueue()) - ->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal); - UnmapInternal(&commandContext); + mMapReadySerial = kMaxExecutionSerial; + if (mMappedData) { + auto commandContext = ToBackend(GetDevice()->GetQueue()) + ->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal); + UnmapInternal(&commandContext); + } } void* Buffer::GetMappedPointer() {
diff --git a/src/dawn/native/d3d11/BufferD3D11.h b/src/dawn/native/d3d11/BufferD3D11.h index 09decf0..7c185fb 100644 --- a/src/dawn/native/d3d11/BufferD3D11.h +++ b/src/dawn/native/d3d11/BufferD3D11.h
@@ -87,6 +87,10 @@ Buffer* destination, uint64_t destinationOffset); + // Actually map the buffer when its last usage serial has passed. + MaybeError FinalizeMap(ScopedCommandRecordingContext* commandContext, + ExecutionSerial completedSerial); + class ScopedMap : public NonCopyable { public: // Map buffer and return a ScopedMap object. If the buffer is not mappable, @@ -156,6 +160,7 @@ ComPtr<ID3D11Buffer> mD3d11NonConstantBuffer; bool mConstantBufferIsUpdated = true; raw_ptr<uint8_t, AllowPtrArithmetic> mMappedData = nullptr; + ExecutionSerial mMapReadySerial = kMaxExecutionSerial; }; } // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp index 5690978..b39c17d 100644 --- a/src/dawn/native/d3d11/QueueD3D11.cpp +++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -156,6 +156,20 @@ return {}; } +MaybeError Queue::CheckAndMapReadyBuffers(ExecutionSerial completedSerial) { + auto commandContext = GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal); + for (auto buffer : mPendingMapBuffers.IterateUpTo(completedSerial)) { + DAWN_TRY(buffer->FinalizeMap(&commandContext, completedSerial)); + } + mPendingMapBuffers.ClearUpTo(completedSerial); + + return {}; +} + +void Queue::TrackPendingMapBuffer(Ref<Buffer>&& buffer, ExecutionSerial readySerial) { + mPendingMapBuffers.Enqueue(buffer, readySerial); +} + MaybeError Queue::WriteBufferImpl(BufferBase* buffer, uint64_t bufferOffset, const void* data, @@ -215,6 +229,8 @@ return ExecutionSerial(0); } + DAWN_TRY(CheckAndMapReadyBuffers(completedSerial)); + return completedSerial; }
diff --git a/src/dawn/native/d3d11/QueueD3D11.h b/src/dawn/native/d3d11/QueueD3D11.h index c029b34..ca265c7 100644 --- a/src/dawn/native/d3d11/QueueD3D11.h +++ b/src/dawn/native/d3d11/QueueD3D11.h
@@ -30,6 +30,7 @@ #include "dawn/common/MutexProtected.h" #include "dawn/common/SerialMap.h" +#include "dawn/common/SerialQueue.h" #include "dawn/native/SystemEvent.h" #include "dawn/native/d3d/QueueD3D.h" @@ -56,6 +57,9 @@ // DeviceBase is fully created. MaybeError InitializePendingContext(); + // Register the pending map buffer to be checked. + void TrackPendingMapBuffer(Ref<Buffer>&& buffer, ExecutionSerial readySerial); + private: using d3d::Queue::Queue; @@ -83,11 +87,15 @@ ResultOrError<Ref<d3d::SharedFence>> GetOrCreateSharedFence() override; void SetEventOnCompletion(ExecutionSerial serial, HANDLE event) override; + // Check all pending map buffers, and actually map the ready ones. + MaybeError CheckAndMapReadyBuffers(ExecutionSerial completedSerial); + ComPtr<ID3D11Fence> mFence; HANDLE mFenceEvent = nullptr; Ref<SharedFence> mSharedFence; MutexProtected<CommandRecordingContext, CommandRecordingContextGuard> mPendingCommands; std::atomic<bool> mPendingCommandsNeedSubmit = false; + SerialQueue<ExecutionSerial, Ref<Buffer>> mPendingMapBuffers; }; } // namespace dawn::native::d3d11