Revert "D3D12: Move ExecutionQueue logic to the Queue." This reverts commit a3dc7f4aff1c0c06046697a3531d175c63040067. Reason for revert: Fails in CI only test suites. Original change's description: > D3D12: Move ExecutionQueue logic to the Queue. > > No functional changes intended but the Queue methods have been made to > do slightly more than the equivalent methods on the device (for example > checking if we actually need to do a wait or a submission). > > Bug: dawn:1413 > Change-Id: I56e1f3b996487e79aceeccb78aa5a2f676902f47 > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/140401 > Kokoro: Kokoro <noreply+kokoro@google.com> > Reviewed-by: Austin Eng <enga@chromium.org> > Commit-Queue: Corentin Wallez <cwallez@chromium.org> > Reviewed-by: Loko Kung <lokokung@google.com> TBR=cwallez@chromium.org,enga@chromium.org,noreply+kokoro@google.com,dawn-scoped@luci-project-accounts.iam.gserviceaccount.com,lokokung@google.com Change-Id: I2c1dde3bb098b2fa710adb0db84554b10ccaa2c6 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: dawn:1413 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/165660 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org> Kokoro: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/d3d12/CommandAllocatorManager.cpp b/src/dawn/native/d3d12/CommandAllocatorManager.cpp index f3cd0ac..09e144b 100644 --- a/src/dawn/native/d3d12/CommandAllocatorManager.cpp +++ b/src/dawn/native/d3d12/CommandAllocatorManager.cpp
@@ -29,14 +29,14 @@ #include "dawn/native/d3d/D3DError.h" #include "dawn/native/d3d12/DeviceD3D12.h" -#include "dawn/native/d3d12/QueueD3D12.h" #include "dawn/common/Assert.h" #include "dawn/common/BitSetIterator.h" namespace dawn::native::d3d12 { -CommandAllocatorManager::CommandAllocatorManager(Queue* queue) : mQueue(queue), mAllocatorCount(0) { +CommandAllocatorManager::CommandAllocatorManager(Device* device) + : device(device), mAllocatorCount(0) { mFreeAllocators.set(); } @@ -44,7 +44,7 @@ // If there are no free allocators, get the oldest serial in flight and wait on it if (mFreeAllocators.none()) { const ExecutionSerial firstSerial = mInFlightCommandAllocators.FirstSerial(); - DAWN_TRY(mQueue->WaitForSerial(firstSerial)); + DAWN_TRY(device->WaitForSerial(firstSerial)); DAWN_TRY(Tick(firstSerial)); } @@ -56,11 +56,9 @@ if (firstFreeIndex >= mAllocatorCount) { DAWN_ASSERT(firstFreeIndex == mAllocatorCount); mAllocatorCount++; - - ID3D12Device* d3d12Device = ToBackend(mQueue->GetDevice())->GetD3D12Device(); DAWN_TRY(CheckHRESULT( - d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex])), + device->GetD3D12Device()->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex])), "D3D12 create command allocator")); } @@ -70,7 +68,7 @@ // Enqueue the command allocator. It will be scheduled for reset after the next // ExecuteCommandLists mInFlightCommandAllocators.Enqueue({mCommandAllocators[firstFreeIndex], firstFreeIndex}, - mQueue->GetPendingCommandSerial()); + device->GetPendingCommandSerial()); return mCommandAllocators[firstFreeIndex].Get(); }
diff --git a/src/dawn/native/d3d12/CommandAllocatorManager.h b/src/dawn/native/d3d12/CommandAllocatorManager.h index b0a29ed..f8e9d0d 100644 --- a/src/dawn/native/d3d12/CommandAllocatorManager.h +++ b/src/dawn/native/d3d12/CommandAllocatorManager.h
@@ -38,11 +38,11 @@ namespace dawn::native::d3d12 { -class Queue; +class Device; class CommandAllocatorManager { public: - explicit CommandAllocatorManager(Queue* queue); + explicit CommandAllocatorManager(Device* device); // A CommandAllocator that is reserved must be used on the next ExecuteCommandLists // otherwise its commands may be reset before execution has completed on the GPU @@ -50,8 +50,7 @@ MaybeError Tick(ExecutionSerial lastCompletedSerial); private: - // The allocator manager is owned by the queue so the queue outlives it. - Queue* mQueue; + Device* device; // This must be at least 2 because the Device and Queue use separate command allocators static constexpr unsigned int kMaxCommandAllocators = 32;
diff --git a/src/dawn/native/d3d12/CommandRecordingContext.cpp b/src/dawn/native/d3d12/CommandRecordingContext.cpp index ba2b37b..6f88e28 100644 --- a/src/dawn/native/d3d12/CommandRecordingContext.cpp +++ b/src/dawn/native/d3d12/CommandRecordingContext.cpp
@@ -78,73 +78,70 @@ return {}; } -MaybeError CommandRecordingContext::ExecuteCommandList(Device* device, - ID3D12CommandQueue* commandQueue) { - if (!IsOpen()) { - return {}; +MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { + if (IsOpen()) { + for (Texture* texture : mSharedTextures) { + DAWN_TRY(texture->SynchronizeImportedTextureBeforeUse()); + } + + MaybeError error = + CheckHRESULT(mD3d12CommandList->Close(), "D3D12 closing pending command list"); + if (error.IsError()) { + Release(); + DAWN_TRY(std::move(error)); + } + DAWN_TRY(device->GetResidencyManager()->EnsureHeapsAreResident(mHeapsPendingUsage.data(), + mHeapsPendingUsage.size())); + + if (device->IsToggleEnabled(Toggle::RecordDetailedTimingInTraceEvents)) { + uint64_t gpuTimestamp; + uint64_t cpuTimestamp; + FILETIME fileTimeNonPrecise; + SYSTEMTIME systemTimeNonPrecise; + + // Both supported since Windows 2000, have a accuracy of 1ms + GetSystemTimeAsFileTime(&fileTimeNonPrecise); + GetSystemTime(&systemTimeNonPrecise); + // Query CPU and GPU timestamps at almost the same time + device->GetCommandQueue()->GetClockCalibration(&gpuTimestamp, &cpuTimestamp); + + uint64_t gpuFrequency; + uint64_t cpuFrequency; + LARGE_INTEGER cpuFrequencyLargeInteger; + device->GetCommandQueue()->GetTimestampFrequency(&gpuFrequency); + QueryPerformanceFrequency(&cpuFrequencyLargeInteger); // Supported since Windows 2000 + cpuFrequency = cpuFrequencyLargeInteger.QuadPart; + + std::string timingInfo = absl::StrFormat( + "UTC Time: %u/%u/%u %02u:%02u:%02u.%03u, File Time: %u, CPU " + "Timestamp: %u, GPU Timestamp: %u, CPU Tick Frequency: %u, GPU Tick Frequency: " + "%u", + systemTimeNonPrecise.wYear, systemTimeNonPrecise.wMonth, systemTimeNonPrecise.wDay, + systemTimeNonPrecise.wHour, systemTimeNonPrecise.wMinute, + systemTimeNonPrecise.wSecond, systemTimeNonPrecise.wMilliseconds, + (static_cast<uint64_t>(fileTimeNonPrecise.dwHighDateTime) << 32) + + fileTimeNonPrecise.dwLowDateTime, + cpuTimestamp, gpuTimestamp, cpuFrequency, gpuFrequency); + + TRACE_EVENT_INSTANT1( + device->GetPlatform(), General, + "d3d12::CommandRecordingContext::ExecuteCommandList Detailed Timing", "Timing", + timingInfo.c_str()); + } + + ID3D12CommandList* d3d12CommandList = GetCommandList(); + device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); + + for (Texture* texture : mSharedTextures) { + DAWN_TRY(texture->SynchronizeImportedTextureAfterUse()); + } + + mIsOpen = false; + mNeedsSubmit = false; + mSharedTextures.clear(); + mHeapsPendingUsage.clear(); + mTempBuffers.clear(); } - - for (Texture* texture : mSharedTextures) { - DAWN_TRY(texture->SynchronizeImportedTextureBeforeUse()); - } - - MaybeError error = - CheckHRESULT(mD3d12CommandList->Close(), "D3D12 closing pending command list"); - if (error.IsError()) { - Release(); - DAWN_TRY(std::move(error)); - } - DAWN_TRY(device->GetResidencyManager()->EnsureHeapsAreResident(mHeapsPendingUsage.data(), - mHeapsPendingUsage.size())); - - if (device->IsToggleEnabled(Toggle::RecordDetailedTimingInTraceEvents)) { - uint64_t gpuTimestamp; - uint64_t cpuTimestamp; - FILETIME fileTimeNonPrecise; - SYSTEMTIME systemTimeNonPrecise; - - // Both supported since Windows 2000, have a accuracy of 1ms - GetSystemTimeAsFileTime(&fileTimeNonPrecise); - GetSystemTime(&systemTimeNonPrecise); - // Query CPU and GPU timestamps at almost the same time - commandQueue->GetClockCalibration(&gpuTimestamp, &cpuTimestamp); - - uint64_t gpuFrequency; - uint64_t cpuFrequency; - LARGE_INTEGER cpuFrequencyLargeInteger; - commandQueue->GetTimestampFrequency(&gpuFrequency); - QueryPerformanceFrequency(&cpuFrequencyLargeInteger); // Supported since Windows 2000 - cpuFrequency = cpuFrequencyLargeInteger.QuadPart; - - std::string timingInfo = absl::StrFormat( - "UTC Time: %u/%u/%u %02u:%02u:%02u.%03u, File Time: %u, CPU " - "Timestamp: %u, GPU Timestamp: %u, CPU Tick Frequency: %u, GPU Tick Frequency: " - "%u", - systemTimeNonPrecise.wYear, systemTimeNonPrecise.wMonth, systemTimeNonPrecise.wDay, - systemTimeNonPrecise.wHour, systemTimeNonPrecise.wMinute, systemTimeNonPrecise.wSecond, - systemTimeNonPrecise.wMilliseconds, - (static_cast<uint64_t>(fileTimeNonPrecise.dwHighDateTime) << 32) + - fileTimeNonPrecise.dwLowDateTime, - cpuTimestamp, gpuTimestamp, cpuFrequency, gpuFrequency); - - TRACE_EVENT_INSTANT1(device->GetPlatform(), General, - "d3d12::CommandRecordingContext::ExecuteCommandList Detailed Timing", - "Timing", timingInfo.c_str()); - } - - ID3D12CommandList* d3d12CommandList = GetCommandList(); - commandQueue->ExecuteCommandLists(1, &d3d12CommandList); - - for (Texture* texture : mSharedTextures) { - DAWN_TRY(texture->SynchronizeImportedTextureAfterUse()); - } - - mIsOpen = false; - mNeedsSubmit = false; - mSharedTextures.clear(); - mHeapsPendingUsage.clear(); - mTempBuffers.clear(); - return {}; }
diff --git a/src/dawn/native/d3d12/CommandRecordingContext.h b/src/dawn/native/d3d12/CommandRecordingContext.h index 1ba4d47..1778813 100644 --- a/src/dawn/native/d3d12/CommandRecordingContext.h +++ b/src/dawn/native/d3d12/CommandRecordingContext.h
@@ -53,7 +53,7 @@ bool NeedsSubmit() const; void SetNeedsSubmit(); - MaybeError ExecuteCommandList(Device* device, ID3D12CommandQueue* commandQueue); + MaybeError ExecuteCommandList(Device* device); void TrackHeapUsage(Heap* heap, ExecutionSerial serial);
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp index ba296c6..90e448f 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -91,8 +91,13 @@ DAWN_ASSERT(mD3d12Device != nullptr); - Ref<Queue> queue; - DAWN_TRY_ASSIGN(queue, Queue::Create(this, &descriptor->defaultQueue)); + // Create device-global objects + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + DAWN_TRY( + CheckHRESULT(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)), + "D3D12 create command queue")); if ((HasFeature(Feature::TimestampQuery) || HasFeature(Feature::ChromiumExperimentalTimestampQueryInsidePasses)) && @@ -102,18 +107,30 @@ // always support timestamps except where there are bugs in Windows container and vGPU // implementations. uint64_t frequency; - DAWN_TRY(CheckHRESULT(queue->GetCommandQueue()->GetTimestampFrequency(&frequency), + DAWN_TRY(CheckHRESULT(mCommandQueue->GetTimestampFrequency(&frequency), "D3D12 get timestamp frequency")); // Calculate the period in nanoseconds by the frequency. mTimestampPeriod = static_cast<float>(1e9) / frequency; } - DAWN_TRY(CheckHRESULT(mD3d12Device->CreateSharedHandle(queue->GetFence(), nullptr, GENERIC_ALL, + // If PIX is not attached, the QueryInterface fails. Hence, no need to check the return + // value. + mCommandQueue.As(&mD3d12SharingContract); + + DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(uint64_t(kBeginningOfGPUTime), + D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&mFence)), + "D3D12 create fence")); + + mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + DAWN_ASSERT(mFenceEvent != nullptr); + + DAWN_TRY(CheckHRESULT(mD3d12Device->CreateSharedHandle(mFence.Get(), nullptr, GENERIC_ALL, nullptr, &mFenceHandle), "D3D12 create fence handle")); DAWN_ASSERT(mFenceHandle != nullptr); // Initialize backend services + mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this); // Zero sized allocator is never requested and does not need to exist. for (uint32_t countIndex = 0; countIndex < kNumViewDescriptorAllocators; countIndex++) { @@ -174,7 +191,10 @@ GetD3D12Device()->CreateCommandSignature(&programDesc, nullptr, IID_PPV_ARGS(&mDrawIndexedIndirectSignature)); - DAWN_TRY(DeviceBase::Initialize(std::move(queue))); + DAWN_TRY(DeviceBase::Initialize(Queue::Create(this, &descriptor->defaultQueue))); + // Device shouldn't be used until after DeviceBase::Initialize so we must wait until after + // device initialization to call NextSerial + DAWN_TRY(NextSerial()); // Ensure DXC if use_dxc toggle is set. DAWN_TRY(EnsureDXCIfRequired()); @@ -197,6 +217,18 @@ return mD3d12Device.Get(); } +ID3D12Fence* Device::GetD3D12Fence() const { + return mFence.Get(); +} + +ComPtr<ID3D12CommandQueue> Device::GetCommandQueue() const { + return mCommandQueue; +} + +ID3D12SharingContract* Device::GetSharingContract() const { + return mD3d12SharingContract.Get(); +} + ComPtr<ID3D12CommandSignature> Device::GetDispatchIndirectSignature() const { return mDispatchIndirectSignature; } @@ -225,14 +257,25 @@ return ToBackend(GetPhysicalDevice())->GetBackend()->GetFunctions(); } +CommandAllocatorManager* Device::GetCommandAllocatorManager() const { + return mCommandAllocatorManager.get(); +} + MutexProtected<ResidencyManager>& Device::GetResidencyManager() const { return *mResidencyManager; } ResultOrError<CommandRecordingContext*> Device::GetPendingCommandContext( Device::SubmitMode submitMode) { - // TODO(dawn:1413): Make callers of this method use the queue directly. - return ToBackend(GetQueue())->GetPendingCommandContext(submitMode); + // Callers of GetPendingCommandList do so to record commands. Only reserve a command + // allocator when it is needed so we don't submit empty command lists + if (!mPendingCommands.IsOpen()) { + DAWN_TRY(mPendingCommands.Open(mD3d12Device.Get(), mCommandAllocatorManager.get())); + } + if (submitMode == Device::SubmitMode::Normal) { + mPendingCommands.SetNeedsSubmit(); + } + return &mPendingCommands; } MaybeError Device::CreateZeroBuffer() { @@ -291,23 +334,83 @@ ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial(); (*mResourceAllocatorManager)->Tick(completedSerial); + DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial)); (*mViewShaderVisibleDescriptorAllocator)->Tick(completedSerial); (*mSamplerShaderVisibleDescriptorAllocator)->Tick(completedSerial); (*mRenderTargetViewAllocator)->Tick(completedSerial); (*mDepthStencilViewAllocator)->Tick(completedSerial); mUsedComObjectRefs->ClearUpTo(completedSerial); - DAWN_TRY(ToBackend(GetQueue())->SubmitPendingCommands()); + if (mPendingCommands.IsOpen() && mPendingCommands.NeedsSubmit()) { + DAWN_TRY(ExecutePendingCommandContext()); + DAWN_TRY(NextSerial()); + } DAWN_TRY(CheckDebugLayerAndGenerateErrors()); return {}; } +MaybeError Device::NextSerial() { + GetQueue()->IncrementLastSubmittedCommandSerial(); + + TRACE_EVENT1(GetPlatform(), General, "D3D12Device::SignalFence", "serial", + uint64_t(GetLastSubmittedCommandSerial())); + + return CheckHRESULT( + mCommandQueue->Signal(mFence.Get(), uint64_t(GetLastSubmittedCommandSerial())), + "D3D12 command queue signal fence"); +} + +MaybeError Device::WaitForSerial(ExecutionSerial serial) { + DAWN_TRY(GetQueue()->CheckPassedSerials()); + if (GetQueue()->GetCompletedCommandSerial() < serial) { + DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(uint64_t(serial), mFenceEvent), + "D3D12 set event on completion")); + WaitForSingleObject(mFenceEvent, INFINITE); + DAWN_TRY(GetQueue()->CheckPassedSerials()); + } + return {}; +} + +ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() { + ExecutionSerial completedSerial = ExecutionSerial(mFence->GetCompletedValue()); + if (DAWN_UNLIKELY(completedSerial == ExecutionSerial(UINT64_MAX))) { + // GetCompletedValue returns UINT64_MAX if the device was removed. + // Try to query the failure reason. + DAWN_TRY(CheckHRESULT(mD3d12Device->GetDeviceRemovedReason(), + "ID3D12Device::GetDeviceRemovedReason")); + // Otherwise, return a generic device lost error. + return DAWN_DEVICE_LOST_ERROR("Device lost"); + } + + if (completedSerial <= GetQueue()->GetCompletedCommandSerial()) { + return ExecutionSerial(0); + } + + return completedSerial; +} + void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) { mUsedComObjectRefs->Enqueue(std::move(object), GetPendingCommandSerial()); } +bool Device::HasPendingCommands() const { + return mPendingCommands.NeedsSubmit(); +} + +void Device::ForceEventualFlushOfCommands() { + if (mPendingCommands.IsOpen()) { + mPendingCommands.SetNeedsSubmit(); + } +} + +MaybeError Device::ExecutePendingCommandContext() { + DAWN_ASSERT(IsLockedByCurrentThreadIfNeeded()); + + return mPendingCommands.ExecuteCommandList(this); +} + ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl( const BindGroupDescriptor* descriptor) { return BindGroup::Create(this, descriptor); @@ -562,6 +665,17 @@ return ToBackend(GetPhysicalDevice())->GetDeviceInfo(); } +MaybeError Device::WaitForIdleForDestruction() { + // Immediately forget about all pending commands + mPendingCommands.Release(); + + DAWN_TRY(NextSerial()); + // Wait for all in-flight commands to finish executing + DAWN_TRY(WaitForSerial(GetLastSubmittedCommandSerial())); + + return {}; +} + void AppendDebugLayerMessagesToError(ID3D12InfoQueue* infoQueue, uint64_t totalErrors, ErrorData* error) { @@ -666,6 +780,14 @@ mZeroBuffer = nullptr; + // Immediately forget about all pending commands for the case where device is lost on its + // own and WaitForIdleForDestruction isn't called. + mPendingCommands.Release(); + + if (mFenceEvent != nullptr) { + ::CloseHandle(mFenceEvent); + } + // Release recycled resource heaps and all other objects waiting for deletion in the resource // allocation manager. mResourceAllocatorManager.reset(); @@ -674,6 +796,11 @@ mUsedComObjectRefs->ClearUpTo(std::numeric_limits<ExecutionSerial>::max()); DAWN_ASSERT(mUsedComObjectRefs->Empty()); + DAWN_ASSERT(!mPendingCommands.IsOpen()); + + // Now that we've cleared out pending work from the queue, we can safely release it and reclaim + // memory. + mCommandQueue.Reset(); } MutexProtected<ShaderVisibleDescriptorAllocator>& Device::GetViewShaderVisibleDescriptorAllocator()
diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h index edf0c8d..810e5ea 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h
@@ -76,11 +76,15 @@ MaybeError TickImpl() override; ID3D12Device* GetD3D12Device() const; + ID3D12Fence* GetD3D12Fence() const; + ComPtr<ID3D12CommandQueue> GetCommandQueue() const; + ID3D12SharingContract* GetSharingContract() const; ComPtr<ID3D12CommandSignature> GetDispatchIndirectSignature() const; ComPtr<ID3D12CommandSignature> GetDrawIndirectSignature() const; ComPtr<ID3D12CommandSignature> GetDrawIndexedIndirectSignature() const; + CommandAllocatorManager* GetCommandAllocatorManager() const; MutexProtected<ResidencyManager>& GetResidencyManager() const; const PlatformFunctions* GetFunctions() const; @@ -95,8 +99,13 @@ const D3D12DeviceInfo& GetDeviceInfo() const; + MaybeError NextSerial(); + MaybeError WaitForSerial(ExecutionSerial serial); + void ReferenceUntilUnused(ComPtr<IUnknown> object); + MaybeError ExecutePendingCommandContext(); + MaybeError CopyFromStagingToBufferImpl(BufferBase* source, uint64_t sourceOffset, BufferBase* destination, @@ -171,6 +180,12 @@ // Dawn APIs void SetLabelImpl() override; + // TODO(dawn:1413) move these methods to the d3d12::Queue. + void ForceEventualFlushOfCommands(); + bool HasPendingCommands() const; + ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials(); + MaybeError WaitForIdleForDestruction(); + // Those DXC methods are needed by d3d12::ShaderModule ComPtr<IDxcLibrary> GetDxcLibrary() const; ComPtr<IDxcCompiler3> GetDxcCompiler() const; @@ -231,14 +246,22 @@ MaybeError CreateZeroBuffer(); + ComPtr<ID3D12Fence> mFence; + HANDLE mFenceEvent = nullptr; + ComPtr<ID3D12Device> mD3d12Device; // Device is owned by adapter and will not be outlived. + ComPtr<ID3D12CommandQueue> mCommandQueue; + ComPtr<ID3D12SharingContract> mD3d12SharingContract; ComPtr<ID3D12CommandSignature> mDispatchIndirectSignature; ComPtr<ID3D12CommandSignature> mDrawIndirectSignature; ComPtr<ID3D12CommandSignature> mDrawIndexedIndirectSignature; + CommandRecordingContext mPendingCommands; + MutexProtected<SerialQueue<ExecutionSerial, ComPtr<IUnknown>>> mUsedComObjectRefs; + std::unique_ptr<CommandAllocatorManager> mCommandAllocatorManager; std::unique_ptr<MutexProtected<ResourceAllocatorManager>> mResourceAllocatorManager; std::unique_ptr<MutexProtected<ResidencyManager>> mResidencyManager;
diff --git a/src/dawn/native/d3d12/QueueD3D12.cpp b/src/dawn/native/d3d12/QueueD3D12.cpp index 18ddf5c..4d2dc9f 100644 --- a/src/dawn/native/d3d12/QueueD3D12.cpp +++ b/src/dawn/native/d3d12/QueueD3D12.cpp
@@ -34,7 +34,6 @@ #include "dawn/native/Commands.h" #include "dawn/native/DynamicUploader.h" #include "dawn/native/d3d/D3DError.h" -#include "dawn/native/d3d12/CommandAllocatorManager.h" #include "dawn/native/d3d12/CommandBufferD3D12.h" #include "dawn/native/d3d12/DeviceD3D12.h" #include "dawn/native/d3d12/UtilsD3D12.h" @@ -44,68 +43,21 @@ namespace dawn::native::d3d12 { // static -ResultOrError<Ref<Queue>> Queue::Create(Device* device, const QueueDescriptor* descriptor) { +Ref<Queue> Queue::Create(Device* device, const QueueDescriptor* descriptor) { Ref<Queue> queue = AcquireRef(new Queue(device, descriptor)); - DAWN_TRY(queue->Initialize()); + queue->Initialize(); return queue; } -Queue::~Queue() {} - -MaybeError Queue::Initialize() { +void Queue::Initialize() { SetLabelImpl(); - - ID3D12Device* d3d12Device = ToBackend(GetDevice())->GetD3D12Device(); - - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - DAWN_TRY(CheckHRESULT(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)), - "D3D12 create command queue")); - - // If PIX is not attached, the QueryInterface fails. Hence, no need to check the return - // value. - mCommandQueue.As(&mD3d12SharingContract); - - DAWN_TRY(CheckHRESULT(d3d12Device->CreateFence(uint64_t(kBeginningOfGPUTime), - D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&mFence)), - "D3D12 create fence")); - - mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - DAWN_ASSERT(mFenceEvent != nullptr); - - // TODO(dawn:1413): Consider folding the command allocator manager in this class. - mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this); - return {}; -} - -void Queue::Destroy() { - // Immediately forget about all pending commands for the case where device is lost on its - // own and WaitForIdleForDestruction isn't called. - DAWN_ASSERT(!mPendingCommands.IsOpen()); - mPendingCommands.Release(); - - if (mFenceEvent != nullptr) { - ::CloseHandle(mFenceEvent); - } - mCommandQueue.Reset(); -} - -ID3D12Fence* Queue::GetFence() const { - return mFence.Get(); -} - -ID3D12CommandQueue* Queue::GetCommandQueue() const { - return mCommandQueue.Get(); -} - -ID3D12SharingContract* Queue::GetSharingContract() const { - return mD3d12SharingContract.Get(); } MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + Device* device = ToBackend(GetDevice()); + CommandRecordingContext* commandContext; - DAWN_TRY_ASSIGN(commandContext, GetPendingCommandContext()); + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); TRACE_EVENT_BEGIN1(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D12::RecordCommands", "serial", uint64_t(GetDevice()->GetPendingCommandSerial())); @@ -115,111 +67,40 @@ TRACE_EVENT_END1(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D12::RecordCommands", "serial", uint64_t(GetDevice()->GetPendingCommandSerial())); - return SubmitPendingCommands(); -} + DAWN_TRY(device->ExecutePendingCommandContext()); -MaybeError Queue::SubmitPendingCommands() { - Device* device = ToBackend(GetDevice()); - DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded()); + DAWN_TRY(device->NextSerial()); - if (!mPendingCommands.IsOpen() || !mPendingCommands.NeedsSubmit()) { - return {}; - } - - DAWN_TRY(mCommandAllocatorManager->Tick(GetCompletedCommandSerial())); - DAWN_TRY(mPendingCommands.ExecuteCommandList(device, mCommandQueue.Get());); - return NextSerial(); -} - -MaybeError Queue::NextSerial() { - ForceEventualFlushOfCommands(); - DAWN_TRY(SubmitPendingCommands()); - - IncrementLastSubmittedCommandSerial(); - - TRACE_EVENT1(GetDevice()->GetPlatform(), General, "D3D12Device::SignalFence", "serial", - uint64_t(GetLastSubmittedCommandSerial())); - - return CheckHRESULT( - mCommandQueue->Signal(mFence.Get(), uint64_t(GetLastSubmittedCommandSerial())), - "D3D12 command queue signal fence"); -} - -MaybeError Queue::WaitForSerial(ExecutionSerial serial) { - if (GetCompletedCommandSerial() >= serial) { - return {}; - } - DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(uint64_t(serial), mFenceEvent), - "D3D12 set event on completion")); - WaitForSingleObject(mFenceEvent, INFINITE); - DAWN_TRY(CheckPassedSerials()); return {}; } bool Queue::HasPendingCommands() const { - return mPendingCommands.NeedsSubmit(); + return ToBackend(GetDevice())->HasPendingCommands(); } ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() { - ExecutionSerial completedSerial = ExecutionSerial(mFence->GetCompletedValue()); - if (DAWN_UNLIKELY(completedSerial == ExecutionSerial(UINT64_MAX))) { - // GetCompletedValue returns UINT64_MAX if the device was removed. - // Try to query the failure reason. - ID3D12Device* d3d12Device = ToBackend(GetDevice())->GetD3D12Device(); - DAWN_TRY(CheckHRESULT(d3d12Device->GetDeviceRemovedReason(), - "ID3D12Device::GetDeviceRemovedReason")); - // Otherwise, return a generic device lost error. - return DAWN_DEVICE_LOST_ERROR("Device lost"); - } - - if (completedSerial <= GetCompletedCommandSerial()) { - return ExecutionSerial(0); - } - - return completedSerial; + return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials(); } void Queue::ForceEventualFlushOfCommands() { - if (mPendingCommands.IsOpen()) { - mPendingCommands.SetNeedsSubmit(); - } + return ToBackend(GetDevice())->ForceEventualFlushOfCommands(); } MaybeError Queue::WaitForIdleForDestruction() { - // Immediately forget about all pending commands - mPendingCommands.Release(); - - DAWN_TRY(NextSerial()); - // Wait for all in-flight commands to finish executing - DAWN_TRY(WaitForSerial(GetLastSubmittedCommandSerial())); - - return {}; -} - -ResultOrError<CommandRecordingContext*> Queue::GetPendingCommandContext(SubmitMode submitMode) { - Device* device = ToBackend(GetDevice()); - ID3D12Device* d3d12Device = device->GetD3D12Device(); - - // Callers of GetPendingCommandList do so to record commands. Only reserve a command - // allocator when it is needed so we don't submit empty command lists - if (!mPendingCommands.IsOpen()) { - DAWN_TRY(mPendingCommands.Open(d3d12Device, mCommandAllocatorManager.get())); - } - if (submitMode == SubmitMode::Normal) { - mPendingCommands.SetNeedsSubmit(); - } - return &mPendingCommands; + return ToBackend(GetDevice())->WaitForIdleForDestruction(); } 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, mCommandQueue.Get(), "Dawn_Queue", GetLabel()); + SetDebugName(device, device->GetCommandQueue().Get(), "Dawn_Queue", GetLabel()); } void Queue::SetEventOnCompletion(ExecutionSerial serial, HANDLE event) { - mFence->SetEventOnCompletion(static_cast<uint64_t>(serial), event); + ToBackend(GetDevice()) + ->GetD3D12Fence() + ->SetEventOnCompletion(static_cast<uint64_t>(serial), event); } } // namespace dawn::native::d3d12
diff --git a/src/dawn/native/d3d12/QueueD3D12.h b/src/dawn/native/d3d12/QueueD3D12.h index f545ee7..2d00586 100644 --- a/src/dawn/native/d3d12/QueueD3D12.h +++ b/src/dawn/native/d3d12/QueueD3D12.h
@@ -28,8 +28,6 @@ #ifndef SRC_DAWN_NATIVE_D3D12_QUEUED3D12_H_ #define SRC_DAWN_NATIVE_D3D12_QUEUED3D12_H_ -#include <memory> - #include "dawn/common/MutexProtected.h" #include "dawn/common/SerialMap.h" #include "dawn/native/SystemEvent.h" @@ -43,24 +41,12 @@ class Queue final : public d3d::Queue { public: - static ResultOrError<Ref<Queue>> Create(Device* device, const QueueDescriptor* descriptor); - - void Destroy(); - - MaybeError NextSerial(); - MaybeError WaitForSerial(ExecutionSerial serial); - ResultOrError<CommandRecordingContext*> GetPendingCommandContext( - SubmitMode submitMode = SubmitMode::Normal); - ID3D12Fence* GetFence() const; - ID3D12CommandQueue* GetCommandQueue() const; - ID3D12SharingContract* GetSharingContract() const; - MaybeError SubmitPendingCommands(); + static Ref<Queue> Create(Device* device, const QueueDescriptor* descriptor); private: using d3d::Queue::Queue; - ~Queue() override; - MaybeError Initialize(); + void Initialize(); MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; bool HasPendingCommands() const override; @@ -72,15 +58,6 @@ // Dawn API void SetLabelImpl() override; - - ComPtr<ID3D12Fence> mFence; - HANDLE mFenceEvent = nullptr; - - CommandRecordingContext mPendingCommands; - ComPtr<ID3D12CommandQueue> mCommandQueue; - ComPtr<ID3D12SharingContract> mD3d12SharingContract; - - std::unique_ptr<CommandAllocatorManager> mCommandAllocatorManager; }; } // namespace dawn::native::d3d12
diff --git a/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp b/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp index b1b848e..c1b83b1 100644 --- a/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp +++ b/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp
@@ -183,7 +183,9 @@ // We must ensure that any previous use of a resource has completed before the resource can // be evicted. - DAWN_TRY(ToBackend(mDevice->GetQueue())->WaitForSerial(lastSubmissionSerial)); + if (lastSubmissionSerial > mDevice->GetQueue()->GetCompletedCommandSerial()) { + DAWN_TRY(mDevice->WaitForSerial(lastSubmissionSerial)); + } pageable->RemoveFromList(); return pageable;
diff --git a/src/dawn/native/d3d12/SwapChainD3D12.cpp b/src/dawn/native/d3d12/SwapChainD3D12.cpp index 65c1d55..e5ae0f8 100644 --- a/src/dawn/native/d3d12/SwapChainD3D12.cpp +++ b/src/dawn/native/d3d12/SwapChainD3D12.cpp
@@ -38,7 +38,6 @@ #include "dawn/native/d3d/D3DError.h" #include "dawn/native/d3d/UtilsD3D.h" #include "dawn/native/d3d12/DeviceD3D12.h" -#include "dawn/native/d3d12/QueueD3D12.h" #include "dawn/native/d3d12/TextureD3D12.h" namespace dawn::native::d3d12 { @@ -55,7 +54,7 @@ SwapChain::~SwapChain() = default; IUnknown* SwapChain::GetD3DDeviceForCreatingSwapChain() { - return ToBackend(GetDevice()->GetQueue())->GetCommandQueue(); + return ToBackend(GetDevice())->GetCommandQueue().Get(); } void SwapChain::ReuseBuffers(SwapChainBase* previousSwapChain) { @@ -83,22 +82,22 @@ } MaybeError SwapChain::PresentImpl() { - Queue* queue = ToBackend(GetDevice()->GetQueue()); + Device* device = ToBackend(GetDevice()); // Transition the texture to the present state as required by IDXGISwapChain1::Present() // TODO(crbug.com/dawn/269): Remove the need for this by eagerly transitioning the // presentable texture to present at the end of submits that use them. CommandRecordingContext* commandContext; - DAWN_TRY_ASSIGN(commandContext, queue->GetPendingCommandContext()); + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); mApiTexture->TrackUsageAndTransitionNow(commandContext, kPresentTextureUsage, mApiTexture->GetAllSubresources()); - DAWN_TRY(queue->SubmitPendingCommands()); + DAWN_TRY(device->ExecutePendingCommandContext()); DAWN_TRY(PresentDXGISwapChain()); // Record that "new" is the last time the buffer has been used. - DAWN_TRY(queue->NextSerial()); - mBufferLastUsedSerials[mCurrentBuffer] = queue->GetPendingCommandSerial(); + DAWN_TRY(device->NextSerial()); + mBufferLastUsedSerials[mCurrentBuffer] = device->GetPendingCommandSerial(); mApiTexture->APIDestroy(); mApiTexture = nullptr; @@ -107,14 +106,14 @@ } ResultOrError<Ref<TextureBase>> SwapChain::GetCurrentTextureImpl() { - Queue* queue = ToBackend(GetDevice()->GetQueue()); + Device* device = ToBackend(GetDevice()); // Synchronously wait until previous operations on the next swapchain buffer are finished. // This is the logic that performs frame pacing. // TODO(crbug.com/dawn/269): Consider whether this should be lifted for Mailbox so that // there is not frame pacing. mCurrentBuffer = GetDXGISwapChain()->GetCurrentBackBufferIndex(); - DAWN_TRY(queue->WaitForSerial(mBufferLastUsedSerials[mCurrentBuffer])); + DAWN_TRY(device->WaitForSerial(mBufferLastUsedSerials[mCurrentBuffer])); // Create the API side objects for this use of the swapchain's buffer. TextureDescriptor descriptor = GetSwapChainBaseTextureDescriptor(this); @@ -130,10 +129,10 @@ // SerialQueue with the current "pending serial" so that we don't destroy the texture // before it is finished being used. Flush the commands and wait for that serial to be // passed, then Tick the device to make sure the reference to the D3D12 texture is removed. - Queue* queue = ToBackend(GetDevice()->GetQueue()); - DAWN_TRY(queue->NextSerial()); - DAWN_TRY(queue->WaitForSerial(queue->GetLastSubmittedCommandSerial())); - return ToBackend(GetDevice())->TickImpl(); + Device* device = ToBackend(GetDevice()); + DAWN_TRY(device->NextSerial()); + DAWN_TRY(device->WaitForSerial(device->GetLastSubmittedCommandSerial())); + return device->TickImpl(); } void SwapChain::DetachFromSurfaceImpl() {
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp index 8d1b810..c0e00ed 100644 --- a/src/dawn/native/d3d12/TextureD3D12.cpp +++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -46,7 +46,6 @@ #include "dawn/native/d3d12/DeviceD3D12.h" #include "dawn/native/d3d12/Forward.h" #include "dawn/native/d3d12/HeapD3D12.h" -#include "dawn/native/d3d12/QueueD3D12.h" #include "dawn/native/d3d12/ResourceAllocatorManagerD3D12.h" #include "dawn/native/d3d12/SharedFenceD3D12.h" #include "dawn/native/d3d12/SharedTextureMemoryD3D12.h" @@ -365,22 +364,23 @@ ResultOrError<ExecutionSerial> Texture::EndAccess() { DAWN_ASSERT(mD3D12ResourceFlags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS); - Queue* queue = ToBackend(GetDevice()->GetQueue()); - + Device* device = ToBackend(GetDevice()); // Synchronize if texture access wasn't synchronized already due to ExecuteCommandLists. if (!mSignalFenceValue.has_value()) { + // Needed to ensure that command allocator doesn't get destroyed before pending commands + // are submitted due to calling NextSerial(). No-op if there are no pending commands. + DAWN_TRY(device->ExecutePendingCommandContext()); // If there were pending commands that used this texture mSignalFenceValue will be set, // but if it's still not set, generate a signal fence after waiting on wait fences. if (!mSignalFenceValue.has_value()) { DAWN_TRY(SynchronizeImportedTextureBeforeUse()); DAWN_TRY(SynchronizeImportedTextureAfterUse()); } - // Make the queue signal the fence in finite time. - DAWN_TRY(queue->NextSerial()); + DAWN_TRY(device->NextSerial()); + DAWN_ASSERT(mSignalFenceValue.has_value()); } - ExecutionSerial ret = mSignalFenceValue.value(); - DAWN_ASSERT(ret <= queue->GetLastSubmittedCommandSerial()); + DAWN_ASSERT(ret <= device->GetLastSubmittedCommandSerial()); // Explicitly call reset() since std::move() on optional doesn't make it std::nullopt. mSignalFenceValue.reset(); return ret; @@ -422,12 +422,11 @@ MaybeError Texture::SynchronizeImportedTextureBeforeUse() { // Perform the wait only on the first call. Device* device = ToBackend(GetDevice()); - ID3D12CommandQueue* commandQueue = ToBackend(device->GetQueue())->GetCommandQueue(); - for (Ref<d3d::Fence>& fence : mWaitFences) { - DAWN_TRY(CheckHRESULT(commandQueue->Wait(static_cast<Fence*>(fence.Get())->GetD3D12Fence(), - fence->GetFenceValue()), - "D3D12 fence wait");); + DAWN_TRY(CheckHRESULT( + device->GetCommandQueue()->Wait( + static_cast<Fence*>(fence.Get())->GetD3D12Fence(), fence->GetFenceValue()), + "D3D12 fence wait");); // Keep D3D12 fence alive since we'll clear the waitFences list below. device->ReferenceUntilUnused(static_cast<Fence*>(fence.Get())->GetD3D12Fence()); } @@ -441,9 +440,9 @@ } for (const auto& fence : fences) { - DAWN_TRY(CheckHRESULT( - commandQueue->Wait(ToBackend(fence.object)->GetD3DFence(), fence.signaledValue), - "D3D12 fence wait")); + DAWN_TRY(CheckHRESULT(device->GetCommandQueue()->Wait( + ToBackend(fence.object)->GetD3DFence(), fence.signaledValue), + "D3D12 fence wait")); // Keep D3D12 fence alive until commands complete. device->ReferenceUntilUnused(ToBackend(fence.object)->GetD3DFence()); } @@ -457,15 +456,15 @@ // If we know we're dealing with a swapbuffer texture, inform PIX we've // "presented" the texture so it can determine frame boundaries and use its // contents for the UI. - Queue* queue = ToBackend(GetDevice()->GetQueue()); + Device* device = ToBackend(GetDevice()); if (mSwapChainTexture) { - ID3D12SharingContract* d3dSharingContract = queue->GetSharingContract(); + ID3D12SharingContract* d3dSharingContract = device->GetSharingContract(); if (d3dSharingContract != nullptr) { d3dSharingContract->Present(mResourceAllocation.GetD3D12Resource(), 0, 0); } } // NextSerial() will be called after this - this is also checked in EndAccess(). - mSignalFenceValue = queue->GetPendingCommandSerial(); + mSignalFenceValue = device->GetPendingCommandSerial(); return {}; }
diff --git a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp index 37eae0c..921a851 100644 --- a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp +++ b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
@@ -257,7 +257,7 @@ // CheckPassedSerials() will update the last internally completed serial. EXPECT_TRUE(mD3DQueue->CheckPassedSerials().IsSuccess()); // NextSerial() will increment the last internally submitted serial. - EXPECT_TRUE(mD3DQueue->NextSerial().IsSuccess()); + EXPECT_TRUE(mD3DDevice->NextSerial().IsSuccess()); } // Repeat up to |kFrameDepth| again but ensure heaps are the same in the expected order @@ -269,7 +269,7 @@ EXPECT_TRUE(heaps.front() == heap); heaps.pop_front(); EXPECT_TRUE(mD3DQueue->CheckPassedSerials().IsSuccess()); - EXPECT_TRUE(mD3DQueue->NextSerial().IsSuccess()); + EXPECT_TRUE(mD3DDevice->NextSerial().IsSuccess()); } EXPECT_TRUE(heaps.empty()); @@ -1064,7 +1064,7 @@ EXPECT_TRUE(gpuAllocator->IsAllocationStillValid(gpuHeapDescAllocation)); - EXPECT_TRUE(mD3DQueue->NextSerial().IsSuccess()); + EXPECT_TRUE(d3dDevice->NextSerial().IsSuccess()); EXPECT_FALSE(gpuAllocator->IsAllocationStillValid(gpuHeapDescAllocation)); }
diff --git a/src/dawn/tests/white_box/GPUTimestampCalibrationTests_D3D12.cpp b/src/dawn/tests/white_box/GPUTimestampCalibrationTests_D3D12.cpp index 07ca216..7df95ed 100644 --- a/src/dawn/tests/white_box/GPUTimestampCalibrationTests_D3D12.cpp +++ b/src/dawn/tests/white_box/GPUTimestampCalibrationTests_D3D12.cpp
@@ -28,7 +28,6 @@ #include <memory> #include "dawn/native/d3d12/DeviceD3D12.h" -#include "dawn/native/d3d12/QueueD3D12.h" #include "dawn/tests/white_box/GPUTimestampCalibrationTests.h" namespace dawn { @@ -38,20 +37,18 @@ public: explicit GPUTimestampCalibrationTestsD3D12(const wgpu::Device& device) { mBackendDevice = native::d3d12::ToBackend(native::FromAPI(device.Get())); - mBackendQueue = native::d3d12::ToBackend(mBackendDevice->GetQueue()); } bool IsSupported() const override { return true; } void GetTimestampCalibration(uint64_t* gpuTimestamp, uint64_t* cpuTimestamp) override { - mBackendQueue->GetCommandQueue()->GetClockCalibration(gpuTimestamp, cpuTimestamp); + mBackendDevice->GetCommandQueue()->GetClockCalibration(gpuTimestamp, cpuTimestamp); } float GetTimestampPeriod() const override { return mBackendDevice->GetTimestampPeriodInNS(); } private: native::d3d12::Device* mBackendDevice; - native::d3d12::Queue* mBackendQueue; }; } // anonymous namespace