diff --git a/include/dawn/native/D3D12Backend.h b/include/dawn/native/D3D12Backend.h index 611ac80620..15a7e6f256 100644 --- a/include/dawn/native/D3D12Backend.h +++ b/include/dawn/native/D3D12Backend.h @@ -21,6 +21,7 @@ #include #include +#include #include "dawn/dawn_wsi.h" #include "dawn/native/DawnNative.h" @@ -54,30 +55,30 @@ struct DAWN_NATIVE_EXPORT ExternalImageDescriptorDXGISharedHandle : ExternalImag ExternalImageDescriptorDXGISharedHandle(); // Note: SharedHandle must be a handle to a texture object. - // TODO(dawn:576): Remove after changing Chromium code to set textureSharedHandle. HANDLE sharedHandle = nullptr; - HANDLE textureSharedHandle = nullptr; - // Optional shared handle to a D3D11/12 fence which can be used to synchronize using wait/signal - // values specified in the access descriptor below. If null, the texture will be assumed to have - // an associated DXGI keyed mutex which will be used with a fixed key of 0 for synchronization. - HANDLE fenceSharedHandle = nullptr; + // Whether fence synchronization should be used instead of texture's keyed mutex. + bool useFenceSynchronization = false; }; // Keyed mutex acquire/release uses a fixed key of 0 to match Chromium behavior. constexpr UINT64 kDXGIKeyedMutexAcquireReleaseKey = 0; -struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGISharedHandle - : ExternalImageAccessDescriptor { - public: - // Value used for fence wait. A value of 0 is valid, but essentially a no-op since the fence - // lifetime starts with the 0 value signaled. A value of UINT64_MAX is ignored since it's also - // used by the D3D runtime to indicate that the device was removed. - uint64_t fenceWaitValue = 0; +struct DAWN_NATIVE_EXPORT ExternalImageDXGIFenceDescriptor { + // Shared handle for the fence. This never passes ownership to the callee (when used as an input + // parameter) or to the caller (when used as a return value or output parameter). + HANDLE fenceHandle = nullptr; - // Value to signal the fence with after the texture is destroyed. A value of 0 means the fence - // will not be signaled. - uint64_t fenceSignalValue = 0; + // The value that was previously signaled on this fence and should be waited on. + uint64_t fenceValue = 0; +}; + +struct DAWN_NATIVE_EXPORT ExternalImageDXGIBeginAccessDescriptor { + bool isInitialized = false; // Whether the texture is initialized on import + WGPUTextureUsageFlags usage = WGPUTextureUsage_None; + + // A list of fences to wait on before accessing the texture. + std::vector waitFences; // Whether the texture is for a WebGPU swap chain. bool isSwapChainTexture = false; @@ -85,7 +86,7 @@ struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGISharedHandle // TODO(dawn:576): Remove after changing Chromium code to use the new struct name. struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGIKeyedMutex - : ExternalImageAccessDescriptorDXGISharedHandle { + : ExternalImageDXGIBeginAccessDescriptor { public: // TODO(chromium:1241533): Remove deprecated keyed mutex params after removing associated // code from Chromium - we use a fixed key of 0 for acquire and release everywhere now. @@ -105,11 +106,19 @@ class DAWN_NATIVE_EXPORT ExternalImageDXGI { // guaranteed to fail e.g. after device destruction. bool IsValid() const; - // TODO(sunnyps): |device| is ignored - remove after Chromium migrates to single parameter call. + // TODO(sunnyps): |device| is ignored - remove after Chromium migrates to BeginAccess(). WGPUTexture ProduceTexture(WGPUDevice device, - const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + const ExternalImageDXGIBeginAccessDescriptor* descriptor); - WGPUTexture ProduceTexture(const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + // Creates WGPUTexture wrapping the DXGI shared handle. The provided wait fences or the + // texture's keyed mutex will be synchronized before using the texture in any command lists. + // Empty fences (nullptr handle) are ignored for convenience (EndAccess can return such fences). + WGPUTexture BeginAccess(const ExternalImageDXGIBeginAccessDescriptor* descriptor); + + // Returns the signalFence that the client must wait on for correct synchronization. Can return + // an empty fence (nullptr handle) if the texture wasn't accessed by Dawn. + // Note that merely calling Destroy() on the WGPUTexture does not ensure synchronization. + void EndAccess(WGPUTexture texture, ExternalImageDXGIFenceDescriptor* signalFence); private: explicit ExternalImageDXGI(std::unique_ptr impl); diff --git a/include/dawn/native/DawnNative.h b/include/dawn/native/DawnNative.h index 0d0df145d4..1d7bf42116 100644 --- a/include/dawn/native/DawnNative.h +++ b/include/dawn/native/DawnNative.h @@ -237,12 +237,6 @@ struct DAWN_NATIVE_EXPORT ExternalImageDescriptor { ExternalImageType mType; }; -struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptor { - public: - bool isInitialized = false; // Whether the texture is initialized on import - WGPUTextureUsageFlags usage = WGPUTextureUsage_None; -}; - struct DAWN_NATIVE_EXPORT ExternalImageExportInfo { public: bool isInitialized = false; // Whether the texture is initialized after export diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn index 19ca4d4b85..c5b5366867 100644 --- a/src/dawn/native/BUILD.gn +++ b/src/dawn/native/BUILD.gn @@ -417,6 +417,8 @@ source_set("sources") { "d3d12/DeviceD3D12.h", "d3d12/ExternalImageDXGIImpl.cpp", "d3d12/ExternalImageDXGIImpl.h", + "d3d12/FenceD3D12.cpp", + "d3d12/FenceD3D12.h", "d3d12/Forward.h", "d3d12/GPUDescriptorHeapAllocationD3D12.cpp", "d3d12/GPUDescriptorHeapAllocationD3D12.h", diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt index 3f417bcbd2..6eececf9fb 100644 --- a/src/dawn/native/CMakeLists.txt +++ b/src/dawn/native/CMakeLists.txt @@ -284,6 +284,8 @@ if (DAWN_ENABLE_D3D12) "d3d12/DeviceD3D12.h" "d3d12/ExternalImageDXGIImpl.cpp" "d3d12/ExternalImageDXGIImpl.h" + "d3d12/FenceD3D12.cpp" + "d3d12/FenceD3D12.h" "d3d12/Forward.h" "d3d12/GPUDescriptorHeapAllocationD3D12.cpp" "d3d12/GPUDescriptorHeapAllocationD3D12.h" diff --git a/src/dawn/native/d3d12/CommandRecordingContext.cpp b/src/dawn/native/d3d12/CommandRecordingContext.cpp index 1f7e2766f5..7c9cf0e73b 100644 --- a/src/dawn/native/d3d12/CommandRecordingContext.cpp +++ b/src/dawn/native/d3d12/CommandRecordingContext.cpp @@ -123,8 +123,10 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { ID3D12CommandList* d3d12CommandList = GetCommandList(); device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); + DAWN_TRY(device->NextSerial()); + for (Texture* texture : mSharedTextures) { - texture->SynchronizeImportedTextureAfterUse(); + DAWN_TRY(texture->SynchronizeImportedTextureAfterUse()); } mIsOpen = false; diff --git a/src/dawn/native/d3d12/D3D11on12Util.cpp b/src/dawn/native/d3d12/D3D11on12Util.cpp index 01a0081bf7..0d165a5598 100644 --- a/src/dawn/native/d3d12/D3D11on12Util.cpp +++ b/src/dawn/native/d3d12/D3D11on12Util.cpp @@ -101,13 +101,15 @@ MaybeError D3D11on12ResourceCacheEntry::AcquireKeyedMutex() { return {}; } -void D3D11on12ResourceCacheEntry::ReleaseKeyedMutex() { +MaybeError D3D11on12ResourceCacheEntry::ReleaseKeyedMutex() { ASSERT(mDXGIKeyedMutex != nullptr); ASSERT(mAcquireCount > 0); mAcquireCount--; if (mAcquireCount == 0) { - mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); + DAWN_TRY(CheckHRESULT(mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey), + "D3D12 releasing keyed mutex")); } + return {}; } size_t D3D11on12ResourceCacheEntry::HashFunc::operator()( diff --git a/src/dawn/native/d3d12/D3D11on12Util.h b/src/dawn/native/d3d12/D3D11on12Util.h index 92dde83f64..9ac403e250 100644 --- a/src/dawn/native/d3d12/D3D11on12Util.h +++ b/src/dawn/native/d3d12/D3D11on12Util.h @@ -39,7 +39,7 @@ class D3D11on12ResourceCacheEntry : public RefCounted { ~D3D11on12ResourceCacheEntry() override; MaybeError AcquireKeyedMutex(); - void ReleaseKeyedMutex(); + MaybeError ReleaseKeyedMutex(); // Functors necessary for the // unordered_set-based cache. diff --git a/src/dawn/native/d3d12/D3D12Backend.cpp b/src/dawn/native/d3d12/D3D12Backend.cpp index 05ddd6b4c0..736af5f4da 100644 --- a/src/dawn/native/d3d12/D3D12Backend.cpp +++ b/src/dawn/native/d3d12/D3D12Backend.cpp @@ -67,17 +67,26 @@ bool ExternalImageDXGI::IsValid() const { WGPUTexture ExternalImageDXGI::ProduceTexture( WGPUDevice device, - const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { - return ProduceTexture(descriptor); + const ExternalImageDXGIBeginAccessDescriptor* descriptor) { + return BeginAccess(descriptor); } -WGPUTexture ExternalImageDXGI::ProduceTexture( - const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { +WGPUTexture ExternalImageDXGI::BeginAccess( + const ExternalImageDXGIBeginAccessDescriptor* descriptor) { if (!IsValid()) { - dawn::ErrorLog() << "Cannot produce texture from external image after device destruction"; + dawn::ErrorLog() << "Cannot use external image after device destruction"; return nullptr; } - return mImpl->ProduceTexture(descriptor); + return mImpl->BeginAccess(descriptor); +} + +void ExternalImageDXGI::EndAccess(WGPUTexture texture, + ExternalImageDXGIFenceDescriptor* signalFence) { + if (!IsValid()) { + dawn::ErrorLog() << "Cannot use external image after device destruction"; + return; + } + mImpl->EndAccess(texture, signalFence); } // static diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp index 7e67cecd6c..2a6c97f18e 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp @@ -102,12 +102,17 @@ MaybeError Device::Initialize(const DeviceDescriptor* descriptor) { mCommandQueue.As(&mD3d12SharingContract); DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(uint64_t(GetLastSubmittedCommandSerial()), - D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)), + D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&mFence)), "D3D12 create fence")); mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); ASSERT(mFenceEvent != nullptr); + DAWN_TRY(CheckHRESULT(mD3d12Device->CreateSharedHandle(mFence.Get(), nullptr, GENERIC_ALL, + nullptr, &mFenceHandle), + "D3D12 create fence handle")); + ASSERT(mFenceHandle != nullptr); + // Initialize backend services mCommandAllocatorManager = std::make_unique(this); @@ -200,6 +205,10 @@ ID3D12SharingContract* Device::GetSharingContract() const { return mD3d12SharingContract.Get(); } +HANDLE Device::GetFenceHandle() const { + return mFenceHandle; +} + ComPtr Device::GetDispatchIndirectSignature() const { return mDispatchIndirectSignature; } @@ -327,8 +336,8 @@ MaybeError Device::TickImpl() { mUsedComObjectRefs.ClearUpTo(completedSerial); if (mPendingCommands.IsOpen()) { + // CommandRecordingContext::ExecuteCommandList() calls NextSerial(). DAWN_TRY(ExecutePendingCommandContext()); - DAWN_TRY(NextSerial()); } DAWN_TRY(CheckDebugLayerAndGenerateErrors()); @@ -543,25 +552,12 @@ std::unique_ptr Device::CreateExternalImageDXGIImpl( return nullptr; } - // Use sharedHandle as a fallback until Chromium code is changed to set textureSharedHandle. - HANDLE textureSharedHandle = descriptor->textureSharedHandle; - if (!textureSharedHandle) { - textureSharedHandle = descriptor->sharedHandle; - } - Microsoft::WRL::ComPtr d3d12Resource; - if (FAILED(GetD3D12Device()->OpenSharedHandle(textureSharedHandle, + if (FAILED(GetD3D12Device()->OpenSharedHandle(descriptor->sharedHandle, IID_PPV_ARGS(&d3d12Resource)))) { return nullptr; } - Microsoft::WRL::ComPtr d3d12Fence; - if (descriptor->fenceSharedHandle && - FAILED(GetD3D12Device()->OpenSharedHandle(descriptor->fenceSharedHandle, - IID_PPV_ARGS(&d3d12Fence)))) { - return nullptr; - } - const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor); if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) { @@ -589,7 +585,7 @@ std::unique_ptr Device::CreateExternalImageDXGIImpl( } auto impl = std::make_unique( - this, std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor); + this, std::move(d3d12Resource), textureDescriptor, descriptor->useFenceSynchronization); mExternalImageList.Append(impl.get()); return impl; } @@ -597,17 +593,14 @@ std::unique_ptr Device::CreateExternalImageDXGIImpl( Ref Device::CreateD3D12ExternalTexture( const TextureDescriptor* descriptor, ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture, bool isInitialized) { Ref dawnTexture; if (ConsumedError(Texture::CreateExternalImage( - this, descriptor, std::move(d3d12Texture), std::move(d3d12Fence), - std::move(d3d11on12Resource), fenceWaitValue, fenceSignalValue, - isSwapChainTexture, isInitialized), + this, descriptor, std::move(d3d12Texture), std::move(waitFences), + std::move(d3d11on12Resource), isSwapChainTexture, isInitialized), &dawnTexture)) { return nullptr; } diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h index 0373cf9d82..49bc3014b1 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h @@ -60,6 +60,7 @@ class Device final : public DeviceBase { ID3D12Device* GetD3D12Device() const; ComPtr GetCommandQueue() const; ID3D12SharingContract* GetSharingContract() const; + HANDLE GetFenceHandle() const; ComPtr GetDispatchIndirectSignature() const; ComPtr GetDrawIndirectSignature() const; @@ -88,6 +89,7 @@ class Device final : public DeviceBase { void ReferenceUntilUnused(ComPtr object); + // Execute pending CommandRecordingContext, and increment last submitted serial if needed. MaybeError ExecutePendingCommandContext(); ResultOrError> CreateStagingBuffer(size_t size) override; @@ -136,10 +138,8 @@ class Device final : public DeviceBase { Ref CreateD3D12ExternalTexture(const TextureDescriptor* descriptor, ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture, bool isInitialized); @@ -218,6 +218,7 @@ class Device final : public DeviceBase { ComPtr mFence; HANDLE mFenceEvent = nullptr; + HANDLE mFenceHandle = nullptr; ResultOrError CheckAndUpdateCompletedSerials() override; ComPtr mD3d12Device; // Device is owned by adapter and will not be outlived. diff --git a/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp b/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp index 8bf2c6c8f8..268e430d95 100644 --- a/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp +++ b/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp @@ -14,9 +14,8 @@ #include "dawn/native/d3d12/ExternalImageDXGIImpl.h" -#include - #include +#include #include "dawn/common/Log.h" #include "dawn/native/D3D12Backend.h" @@ -28,26 +27,26 @@ namespace dawn::native::d3d12 { ExternalImageDXGIImpl::ExternalImageDXGIImpl(Device* backendDevice, Microsoft::WRL::ComPtr d3d12Resource, - Microsoft::WRL::ComPtr d3d12Fence, - const WGPUTextureDescriptor* descriptor) + const TextureDescriptor* textureDescriptor, + bool useFenceSynchronization) : mBackendDevice(backendDevice), mD3D12Resource(std::move(d3d12Resource)), - mD3D12Fence(std::move(d3d12Fence)), + mUseFenceSynchronization(useFenceSynchronization), mD3D11on12ResourceCache(std::make_unique()), - mUsage(descriptor->usage), - mDimension(descriptor->dimension), - mSize(descriptor->size), - mFormat(descriptor->format), - mMipLevelCount(descriptor->mipLevelCount), - mSampleCount(descriptor->sampleCount) { + mUsage(textureDescriptor->usage), + mDimension(textureDescriptor->dimension), + mSize(textureDescriptor->size), + mFormat(textureDescriptor->format), + mMipLevelCount(textureDescriptor->mipLevelCount), + mSampleCount(textureDescriptor->sampleCount) { ASSERT(mBackendDevice != nullptr); ASSERT(mD3D12Resource != nullptr); - ASSERT(!descriptor->nextInChain || - descriptor->nextInChain->sType == WGPUSType_DawnTextureInternalUsageDescriptor); - if (descriptor->nextInChain) { - mUsageInternal = - reinterpret_cast(descriptor->nextInChain) - ->internalUsage; + ASSERT(!textureDescriptor->nextInChain || textureDescriptor->nextInChain->sType == + wgpu::SType::DawnTextureInternalUsageDescriptor); + if (textureDescriptor->nextInChain) { + mUsageInternal = reinterpret_cast( + textureDescriptor->nextInChain) + ->internalUsage; } } @@ -62,45 +61,55 @@ bool ExternalImageDXGIImpl::IsValid() const { void ExternalImageDXGIImpl::Destroy() { if (IsInList()) { RemoveFromList(); - - // Keep fence alive until any pending signal calls are done on the GPU. - mBackendDevice->ConsumedError(mBackendDevice->ExecutePendingCommandContext()); - mBackendDevice->ConsumedError(mBackendDevice->NextSerial()); - mBackendDevice->ReferenceUntilUnused(mD3D12Fence.Get()); mBackendDevice = nullptr; - - mD3D12Resource.Reset(); - mD3D12Fence.Reset(); - mD3D11on12ResourceCache.reset(); + mD3D12Resource = nullptr; + mD3D11on12ResourceCache = nullptr; } } -WGPUTexture ExternalImageDXGIImpl::ProduceTexture( - const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { +WGPUTexture ExternalImageDXGIImpl::BeginAccess( + const ExternalImageDXGIBeginAccessDescriptor* descriptor) { ASSERT(mBackendDevice != nullptr); + ASSERT(descriptor != nullptr); // Ensure the texture usage is allowed - if (!IsSubset(descriptor->usage, mUsage)) { + if (!IsSubset(descriptor->usage, static_cast(mUsage))) { dawn::ErrorLog() << "Texture usage is not valid for external image"; return nullptr; } TextureDescriptor textureDescriptor = {}; textureDescriptor.usage = static_cast(descriptor->usage); - textureDescriptor.dimension = static_cast(mDimension); + textureDescriptor.dimension = mDimension; textureDescriptor.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers}; - textureDescriptor.format = static_cast(mFormat); + textureDescriptor.format = mFormat; textureDescriptor.mipLevelCount = mMipLevelCount; textureDescriptor.sampleCount = mSampleCount; DawnTextureInternalUsageDescriptor internalDesc = {}; - if (mUsageInternal) { + if (mUsageInternal != wgpu::TextureUsage::None) { textureDescriptor.nextInChain = &internalDesc; - internalDesc.internalUsage = static_cast(mUsageInternal); + internalDesc.internalUsage = mUsageInternal; internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; } + std::vector> waitFences; Ref d3d11on12Resource; - if (!mD3D12Fence) { + if (mUseFenceSynchronization) { + for (const ExternalImageDXGIFenceDescriptor& fenceDescriptor : descriptor->waitFences) { + ASSERT(fenceDescriptor.fenceHandle != nullptr); + // TODO(sunnyps): Use a fence cache instead of re-importing fences on each BeginAccess. + Ref fence; + if (mBackendDevice->ConsumedError( + Fence::CreateFromHandle(mBackendDevice->GetD3D12Device(), + fenceDescriptor.fenceHandle, + fenceDescriptor.fenceValue), + &fence)) { + dawn::ErrorLog() << "Unable to create D3D12 fence for external image"; + return nullptr; + } + waitFences.push_back(std::move(fence)); + } + } else { d3d11on12Resource = mD3D11on12ResourceCache->GetOrCreateD3D11on12Resource( mBackendDevice, mD3D12Resource.Get()); if (d3d11on12Resource == nullptr) { @@ -110,10 +119,27 @@ WGPUTexture ExternalImageDXGIImpl::ProduceTexture( } Ref texture = mBackendDevice->CreateD3D12ExternalTexture( - &textureDescriptor, mD3D12Resource, mD3D12Fence, std::move(d3d11on12Resource), - descriptor->fenceWaitValue, descriptor->fenceSignalValue, descriptor->isSwapChainTexture, - descriptor->isInitialized); + &textureDescriptor, mD3D12Resource, std::move(waitFences), std::move(d3d11on12Resource), + descriptor->isSwapChainTexture, descriptor->isInitialized); return ToAPI(texture.Detach()); } +void ExternalImageDXGIImpl::EndAccess(WGPUTexture texture, + ExternalImageDXGIFenceDescriptor* signalFence) { + ASSERT(signalFence != nullptr); + + Texture* backendTexture = ToBackend(FromAPI(texture)); + ASSERT(backendTexture != nullptr); + + if (mUseFenceSynchronization) { + ExecutionSerial fenceValue; + if (mBackendDevice->ConsumedError(backendTexture->EndAccess(), &fenceValue)) { + dawn::ErrorLog() << "D3D12 fence end access failed"; + return; + } + signalFence->fenceHandle = mBackendDevice->GetFenceHandle(); + signalFence->fenceValue = static_cast(fenceValue); + } +} + } // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/ExternalImageDXGIImpl.h b/src/dawn/native/d3d12/ExternalImageDXGIImpl.h index 016ce4a0e5..ee12a1664e 100644 --- a/src/dawn/native/d3d12/ExternalImageDXGIImpl.h +++ b/src/dawn/native/d3d12/ExternalImageDXGIImpl.h @@ -21,7 +21,12 @@ #include "dawn/common/LinkedList.h" #include "dawn/dawn_wsi.h" +#include "dawn/native/D3D12Backend.h" +#include "dawn/native/Error.h" #include "dawn/native/Forward.h" +#include "dawn/native/IntegerTypes.h" +#include "dawn/native/d3d12/FenceD3D12.h" +#include "dawn/webgpu_cpp.h" struct ID3D12Resource; struct ID3D12Fence; @@ -30,15 +35,15 @@ namespace dawn::native::d3d12 { class D3D11on12ResourceCache; class Device; -struct ExternalImageAccessDescriptorDXGISharedHandle; +struct ExternalImageDXGIBeginAccessDescriptor; struct ExternalImageDescriptorDXGISharedHandle; class ExternalImageDXGIImpl : public LinkNode { public: ExternalImageDXGIImpl(Device* backendDevice, Microsoft::WRL::ComPtr d3d12Resource, - Microsoft::WRL::ComPtr d3d12Fence, - const WGPUTextureDescriptor* descriptor); + const TextureDescriptor* textureDescriptor, + bool useFenceSynchronization); ~ExternalImageDXGIImpl(); ExternalImageDXGIImpl(const ExternalImageDXGIImpl&) = delete; @@ -48,21 +53,22 @@ class ExternalImageDXGIImpl : public LinkNode { bool IsValid() const; - WGPUTexture ProduceTexture(const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + WGPUTexture BeginAccess(const ExternalImageDXGIBeginAccessDescriptor* descriptor); + + void EndAccess(WGPUTexture texture, ExternalImageDXGIFenceDescriptor* signalFence); private: - Device* mBackendDevice; + Device* mBackendDevice = nullptr; Microsoft::WRL::ComPtr mD3D12Resource; - Microsoft::WRL::ComPtr mD3D12Fence; + const bool mUseFenceSynchronization; + std::unique_ptr mD3D11on12ResourceCache; - // Contents of WGPUTextureDescriptor are stored individually since the descriptor - // could outlive this image. - WGPUTextureUsageFlags mUsage; - WGPUTextureUsageFlags mUsageInternal = WGPUTextureUsage_None; - WGPUTextureDimension mDimension; - WGPUExtent3D mSize; - WGPUTextureFormat mFormat; + wgpu::TextureUsage mUsage; + wgpu::TextureUsage mUsageInternal = wgpu::TextureUsage::None; + wgpu::TextureDimension mDimension; + Extent3D mSize; + wgpu::TextureFormat mFormat; uint32_t mMipLevelCount; uint32_t mSampleCount; }; diff --git a/src/dawn/native/d3d12/FenceD3D12.cpp b/src/dawn/native/d3d12/FenceD3D12.cpp new file mode 100644 index 0000000000..5838ca0ff2 --- /dev/null +++ b/src/dawn/native/d3d12/FenceD3D12.cpp @@ -0,0 +1,61 @@ +// Copyright 2022 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn/native/d3d12/FenceD3D12.h" + +#include + +#include "dawn/common/Log.h" +#include "dawn/native/Error.h" +#include "dawn/native/d3d12/D3D12Error.h" +#include "dawn/native/d3d12/DeviceD3D12.h" + +namespace dawn::native::d3d12 { + +// static +ResultOrError> Fence::CreateFromHandle(ID3D12Device* device, + HANDLE unownedHandle, + UINT64 fenceValue) { + ASSERT(unownedHandle != nullptr); + HANDLE ownedHandle = nullptr; + if (!::DuplicateHandle(::GetCurrentProcess(), unownedHandle, ::GetCurrentProcess(), + &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + return DAWN_DEVICE_LOST_ERROR("D3D12 fence dup handle"); + } + ComPtr d3d12Fence; + DAWN_TRY_WITH_CLEANUP( + CheckHRESULT(device->OpenSharedHandle(ownedHandle, IID_PPV_ARGS(&d3d12Fence)), + "D3D12 fence open handle"), + { ::CloseHandle(ownedHandle); }); + return AcquireRef(new Fence(std::move(d3d12Fence), fenceValue, ownedHandle)); +} + +Fence::Fence(ComPtr d3d12Fence, UINT64 fenceValue, HANDLE sharedHandle) + : mD3D12Fence(std::move(d3d12Fence)), mFenceValue(fenceValue), mSharedHandle(sharedHandle) {} + +Fence::~Fence() { + if (mSharedHandle != nullptr) { + ::CloseHandle(mSharedHandle); + } +} + +ID3D12Fence* Fence::GetD3D12Fence() const { + return mD3D12Fence.Get(); +} + +UINT64 Fence::GetFenceValue() const { + return mFenceValue; +} + +} // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/FenceD3D12.h b/src/dawn/native/d3d12/FenceD3D12.h new file mode 100644 index 0000000000..aa73cfabcd --- /dev/null +++ b/src/dawn/native/d3d12/FenceD3D12.h @@ -0,0 +1,45 @@ +// Copyright 2022 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_DAWN_NATIVE_D3D12_FENCED3D12_H_ +#define SRC_DAWN_NATIVE_D3D12_FENCED3D12_H_ + +#include "dawn/common/RefCounted.h" +#include "dawn/native/DawnNative.h" +#include "dawn/native/Error.h" +#include "dawn/native/d3d12/d3d12_platform.h" + +namespace dawn::native::d3d12 { + +class Fence : public RefCounted { + public: + static ResultOrError> CreateFromHandle(ID3D12Device* device, + HANDLE unownedHandle, + UINT64 fenceValue); + + ID3D12Fence* GetD3D12Fence() const; + UINT64 GetFenceValue() const; + + private: + Fence(ComPtr d3d12Fence, UINT64 fenceValue, HANDLE sharedHandle); + ~Fence() override; + + ComPtr mD3D12Fence; + const UINT64 mFenceValue; + const HANDLE mSharedHandle; +}; + +} // namespace dawn::native::d3d12 + +#endif // SRC_DAWN_NATIVE_D3D12_FENCED3D12_H_ diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp index 1d0e6d4cac..02588c2259 100644 --- a/src/dawn/native/d3d12/TextureD3D12.cpp +++ b/src/dawn/native/d3d12/TextureD3D12.cpp @@ -22,11 +22,15 @@ #include "dawn/native/DynamicUploader.h" #include "dawn/native/EnumMaskIterator.h" #include "dawn/native/Error.h" +#include "dawn/native/IntegerTypes.h" +#include "dawn/native/ResourceMemoryAllocation.h" +#include "dawn/native/ToBackend.h" #include "dawn/native/d3d12/BufferD3D12.h" #include "dawn/native/d3d12/CommandRecordingContext.h" #include "dawn/native/d3d12/D3D11on12Util.h" #include "dawn/native/d3d12/D3D12Error.h" #include "dawn/native/d3d12/DeviceD3D12.h" +#include "dawn/native/d3d12/Forward.h" #include "dawn/native/d3d12/HeapD3D12.h" #include "dawn/native/d3d12/ResourceAllocatorManagerD3D12.h" #include "dawn/native/d3d12/StagingBufferD3D12.h" @@ -510,17 +514,16 @@ ResultOrError> Texture::CreateExternalImage( Device* device, const TextureDescriptor* descriptor, ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture, bool isInitialized) { Ref dawnTexture = AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal)); - DAWN_TRY(dawnTexture->InitializeAsExternalTexture( - std::move(d3d12Texture), std::move(d3d12Fence), std::move(d3d11on12Resource), - fenceWaitValue, fenceSignalValue, isSwapChainTexture)); + + DAWN_TRY( + dawnTexture->InitializeAsExternalTexture(std::move(d3d12Texture), std::move(waitFences), + std::move(d3d11on12Resource), isSwapChainTexture)); // Importing a multi-planar format must be initialized. This is required because // a shared multi-planar format cannot be initialized by Dawn. @@ -545,10 +548,8 @@ ResultOrError> Texture::Create(Device* device, } MaybeError Texture::InitializeAsExternalTexture(ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture) { D3D12_RESOURCE_DESC desc = d3d12Texture->GetDesc(); mD3D12ResourceFlags = desc.Flags; @@ -560,10 +561,8 @@ MaybeError Texture::InitializeAsExternalTexture(ComPtr d3d12Text // memory management. mResourceAllocation = {info, 0, std::move(d3d12Texture), nullptr}; - mD3D12Fence = std::move(d3d12Fence); + mWaitFences = std::move(waitFences); mD3D11on12Resource = std::move(d3d11on12Resource); - mFenceWaitValue = fenceWaitValue; - mFenceSignalValue = fenceSignalValue; mSwapChainTexture = isSwapChainTexture; SetLabelHelper("Dawn_ExternalTexture"); @@ -679,23 +678,34 @@ void Texture::DestroyImpl() { // ID3D12SharingContract::Present. mSwapChainTexture = false; - // Signal the fence on destroy after all uses of the texture. - if (mD3D12Fence != nullptr && mFenceSignalValue != 0) { - // Enqueue a fence wait if we haven't already; otherwise the fence signal will be racy. - if (mFenceWaitValue != UINT64_MAX) { - device->ConsumedError( - CheckHRESULT(device->GetCommandQueue()->Wait(mD3D12Fence.Get(), mFenceWaitValue), - "D3D12 fence wait")); - } - device->ConsumedError( - CheckHRESULT(device->GetCommandQueue()->Signal(mD3D12Fence.Get(), mFenceSignalValue), - "D3D12 fence signal")); - } - // Now that the texture has been destroyed. It should release the refptr of the d3d11on12 // resource and the fence. mD3D11on12Resource = nullptr; - mD3D12Fence = nullptr; +} + +ResultOrError Texture::EndAccess() { + ASSERT(mD3D11on12Resource == nullptr); + + // 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(ToBackend(GetDevice())->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(ToBackend(GetDevice())->NextSerial()); + DAWN_TRY(SynchronizeImportedTextureAfterUse()); + ASSERT(mSignalFenceValue.has_value()); + } + } + + // Explicitly call reset() since std::move() on optional doesn't make it std::nullopt. + ExecutionSerial ret = mSignalFenceValue.value(); + mSignalFenceValue.reset(); + return ret; } DXGI_FORMAT Texture::GetD3D12Format() const { @@ -735,23 +745,25 @@ MaybeError Texture::SynchronizeImportedTextureBeforeUse() { if (mD3D11on12Resource != nullptr) { DAWN_TRY(mD3D11on12Resource->AcquireKeyedMutex()); } - // Perform the wait only on the first call. We can use UINT64_MAX as a sentinel value since it's - // also used by the D3D runtime to indicate device removed according to: - // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12fence-getcompletedvalue#return-value - if (mD3D12Fence != nullptr && mFenceWaitValue != UINT64_MAX) { - DAWN_TRY(CheckHRESULT( - ToBackend(GetDevice())->GetCommandQueue()->Wait(mD3D12Fence.Get(), mFenceWaitValue), - "D3D12 fence wait")); - mFenceWaitValue = UINT64_MAX; + // Perform the wait only on the first call. + Device* device = ToBackend(GetDevice()); + for (Ref& fence : mWaitFences) { + DAWN_TRY(CheckHRESULT(device->GetCommandQueue()->Wait(fence->GetD3D12Fence(), + fence->GetFenceValue()), + "D3D12 fence wait");); } + mWaitFences.clear(); return {}; } -void Texture::SynchronizeImportedTextureAfterUse() { +MaybeError Texture::SynchronizeImportedTextureAfterUse() { if (mD3D11on12Resource != nullptr) { - mD3D11on12Resource->ReleaseKeyedMutex(); + DAWN_TRY(mD3D11on12Resource->ReleaseKeyedMutex()); + } else { + // CommandRecordingContext will call NextSerial() to increment the fence before this call. + mSignalFenceValue = ToBackend(GetDevice())->GetLastSubmittedCommandSerial(); } - // Defer signaling the fence until destroy after all uses of the fence. + return {}; } void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, diff --git a/src/dawn/native/d3d12/TextureD3D12.h b/src/dawn/native/d3d12/TextureD3D12.h index 3e49bc232b..866fcdf5f0 100644 --- a/src/dawn/native/d3d12/TextureD3D12.h +++ b/src/dawn/native/d3d12/TextureD3D12.h @@ -15,13 +15,16 @@ #ifndef SRC_DAWN_NATIVE_D3D12_TEXTURED3D12_H_ #define SRC_DAWN_NATIVE_D3D12_TEXTURED3D12_H_ +#include #include +#include "dawn/native/Error.h" #include "dawn/native/Texture.h" #include "dawn/native/DawnNative.h" #include "dawn/native/IntegerTypes.h" #include "dawn/native/PassResourceUsage.h" +#include "dawn/native/d3d12/FenceD3D12.h" #include "dawn/native/d3d12/IntegerTypes.h" #include "dawn/native/d3d12/ResourceHeapAllocationD3D12.h" #include "dawn/native/d3d12/d3d12_platform.h" @@ -45,16 +48,19 @@ class Texture final : public TextureBase { Device* device, const TextureDescriptor* descriptor, ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture, bool isInitialized); static ResultOrError> Create(Device* device, const TextureDescriptor* descriptor, ComPtr d3d12Texture); + // For external textures, returns the Device internal fence's value associated with the last + // ExecuteCommandLists that used this texture. If nullopt is returned, the texture wasn't used + // or keyed mutex is used instead of fences for synchronization. + ResultOrError EndAccess(); + DXGI_FORMAT GetD3D12Format() const; ID3D12Resource* GetD3D12Resource() const; DXGI_FORMAT GetD3D12CopyableSubresourceFormat(Aspect aspect) const; @@ -75,7 +81,7 @@ class Texture final : public TextureBase { const SubresourceRange& range); MaybeError SynchronizeImportedTextureBeforeUse(); - void SynchronizeImportedTextureAfterUse(); + MaybeError SynchronizeImportedTextureAfterUse(); void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext, std::vector* barrier, @@ -102,10 +108,8 @@ class Texture final : public TextureBase { MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsExternalTexture(ComPtr d3d12Texture, - ComPtr d3d12Fence, + std::vector> waitFences, Ref d3d11on12Resource, - uint64_t fenceWaitValue, - uint64_t fenceSignalValue, bool isSwapChainTexture); MaybeError InitializeAsSwapChainTexture(ComPtr d3d12Texture); @@ -142,10 +146,9 @@ class Texture final : public TextureBase { ResourceHeapAllocation mResourceAllocation; // TODO(dawn:1460): Encapsulate imported image fields e.g. std::unique_ptr. - ComPtr mD3D12Fence; + std::vector> mWaitFences; + std::optional mSignalFenceValue; Ref mD3D11on12Resource; - uint64_t mFenceWaitValue = 0; - uint64_t mFenceSignalValue = 0; bool mSwapChainTexture = false; SubresourceStorage mSubresourceStateAndDecay; diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp index 44efc943be..f1c3fb49ca 100644 --- a/src/dawn/tests/DawnTest.cpp +++ b/src/dawn/tests/DawnTest.cpp @@ -1006,7 +1006,7 @@ std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file, uint64_t size, detail::Expectation* expectation) { uint64_t alignedSize = Align(size, uint64_t(4)); - auto readback = ReserveReadback(alignedSize); + auto readback = ReserveReadback(device, alignedSize); // We need to enqueue the copy immediately because by the time we resolve the expectation, // the buffer might have been modified. @@ -1031,6 +1031,7 @@ std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file, std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file, int line, + wgpu::Device targetDevice, detail::Expectation* expectation, const wgpu::Texture& texture, wgpu::Origin3D origin, @@ -1039,6 +1040,8 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file, wgpu::TextureAspect aspect, uint32_t dataSize, uint32_t bytesPerRow) { + ASSERT(targetDevice != nullptr); + if (bytesPerRow == 0) { bytesPerRow = Align(extent.width * dataSize, kTextureBytesPerRowAlignment); } else { @@ -1050,7 +1053,7 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file, uint32_t size = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent.width, extent.height, extent.depthOrArrayLayers, dataSize); - auto readback = ReserveReadback(Align(size, 4)); + auto readback = ReserveReadback(targetDevice, Align(size, 4)); // We need to enqueue the copy immediately because by the time we resolve the expectation, // the texture might have been modified. @@ -1059,11 +1062,11 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file, wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(readback.buffer, readback.offset, bytesPerRow, rowsPerImage); - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = targetDevice.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &extent); wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); + targetDevice.GetQueue().Submit(1, &commands); DeferredExpectation deferred; deferred.file = file; @@ -1357,9 +1360,12 @@ std::ostringstream& DawnTestBase::ExpectAttachmentDepthStencilTestData( return EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, {0, 0}, {width, height}); } -void DawnTestBase::WaitABit() { - if (device) { - device.Tick(); +void DawnTestBase::WaitABit(wgpu::Device targetDevice) { + if (targetDevice == nullptr) { + targetDevice = this->device; + } + if (targetDevice != nullptr) { + targetDevice.Tick(); } FlushWire(); @@ -1385,18 +1391,21 @@ void DawnTestBase::WaitForAllOperations() { } } -DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(uint64_t readbackSize) { +DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(wgpu::Device targetDevice, + uint64_t readbackSize) { ReadbackSlot slot; + slot.device = targetDevice; slot.bufferSize = readbackSize; // Create and initialize the slot buffer so that it won't unexpectedly affect the count of // resource lazy clear in the tests. const std::vector initialBufferData(readbackSize, 0u); slot.buffer = - utils::CreateBufferFromData(device, initialBufferData.data(), readbackSize, + utils::CreateBufferFromData(targetDevice, initialBufferData.data(), readbackSize, wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst); ReadbackReservation reservation; + reservation.device = targetDevice; reservation.buffer = slot.buffer; reservation.slot = mReadbackSlots.size(); reservation.offset = 0; @@ -1410,6 +1419,8 @@ void DawnTestBase::MapSlotsSynchronously() { // immediately. mNumPendingMapOperations = mReadbackSlots.size(); + std::vector pendingDevices; + // Map all readback slots for (size_t i = 0; i < mReadbackSlots.size(); ++i) { MapReadUserdata* userdata = new MapReadUserdata{this, i}; @@ -1417,11 +1428,15 @@ void DawnTestBase::MapSlotsSynchronously() { const ReadbackSlot& slot = mReadbackSlots[i]; slot.buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize, SlotMapCallback, userdata); + + pendingDevices.push_back(slot.device); } // Busy wait until all map operations are done. while (mNumPendingMapOperations != 0) { - WaitABit(); + for (const wgpu::Device& device : pendingDevices) { + WaitABit(device); + } } } diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h index d374652a37..7622dd23d5 100644 --- a/src/dawn/tests/DawnTest.h +++ b/src/dawn/tests/DawnTest.h @@ -302,11 +302,29 @@ class DawnTestBase { uint64_t size, detail::Expectation* expectation); + template + std::ostringstream& AddTextureExpectation(const char* file, + int line, + const T* expectedData, + const wgpu::Texture& texture, + wgpu::Origin3D origin, + wgpu::Extent3D extent, + wgpu::TextureFormat format, + T tolerance = 0, + uint32_t level = 0, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + uint32_t bytesPerRow = 0) { + // No device passed explicitly. Default it, and forward the rest of the args. + return AddTextureExpectation(file, line, this->device, expectedData, texture, origin, + extent, format, tolerance, level, aspect, bytesPerRow); + } + // T - expected value Type // U - actual value Type (defaults = T) template std::ostringstream& AddTextureExpectation(const char* file, int line, + wgpu::Device targetDevice, const T* expectedData, const wgpu::Texture& texture, wgpu::Origin3D origin, @@ -320,7 +338,7 @@ class DawnTestBase { uint32_t texelComponentCount = utils::GetWGSLRenderableColorTextureComponentCount(format); return AddTextureExpectationImpl( - file, line, + file, line, std::move(targetDevice), new detail::ExpectEq( expectedData, texelComponentCount * extent.width * extent.height * extent.depthOrArrayLayers, @@ -338,8 +356,24 @@ class DawnTestBase { uint32_t level = 0, wgpu::TextureAspect aspect = wgpu::TextureAspect::All, uint32_t bytesPerRow = 0) { + // No device passed explicitly. Default it, and forward the rest of the args. + return AddTextureExpectation(file, line, this->device, expectedData, texture, origin, + extent, level, aspect, bytesPerRow); + } + + template + std::ostringstream& AddTextureExpectation(const char* file, + int line, + wgpu::Device targetDevice, + const T* expectedData, + const wgpu::Texture& texture, + wgpu::Origin3D origin, + wgpu::Extent3D extent, + uint32_t level = 0, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + uint32_t bytesPerRow = 0) { return AddTextureExpectationImpl( - file, line, + file, line, std::move(targetDevice), new detail::ExpectEq(expectedData, extent.width * extent.height * extent.depthOrArrayLayers), texture, origin, extent, level, aspect, sizeof(U), bytesPerRow); @@ -354,9 +388,24 @@ class DawnTestBase { uint32_t level = 0, wgpu::TextureAspect aspect = wgpu::TextureAspect::All, uint32_t bytesPerRow = 0) { - return AddTextureExpectationImpl(file, line, new detail::ExpectEq(expectedData), - texture, origin, {1, 1}, level, aspect, sizeof(U), - bytesPerRow); + // No device passed explicitly. Default it, and forward the rest of the args. + return AddTextureExpectation(file, line, this->device, expectedData, texture, origin, + level, aspect, bytesPerRow); + } + + template + std::ostringstream& AddTextureExpectation(const char* file, + int line, + wgpu::Device targetDevice, + const T& expectedData, + const wgpu::Texture& texture, + wgpu::Origin3D origin, + uint32_t level = 0, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + uint32_t bytesPerRow = 0) { + return AddTextureExpectationImpl(file, line, std::move(targetDevice), + new detail::ExpectEq(expectedData), texture, origin, + {1, 1}, level, aspect, sizeof(U), bytesPerRow); } template DataSize(), bytesPerRow); + // No device passed explicitly. Default it, and forward the rest of the args. + return AddTextureExpectation(file, line, this->device, expectation, texture, origin, extent, + level, aspect, bytesPerRow); + } + + template ::value>::type> + std::ostringstream& AddTextureExpectation(const char* file, + int line, + wgpu::Device targetDevice, + E* expectation, + const wgpu::Texture& texture, + wgpu::Origin3D origin, + wgpu::Extent3D extent, + uint32_t level = 0, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + uint32_t bytesPerRow = 0) { + return AddTextureExpectationImpl(file, line, std::move(targetDevice), expectation, texture, + origin, extent, level, aspect, expectation->DataSize(), + bytesPerRow); } template @@ -387,9 +455,27 @@ class DawnTestBase { uint32_t level = 0, wgpu::TextureAspect aspect = wgpu::TextureAspect::All, uint32_t bytesPerRow = 0) { + // No device passed explicitly. Default it, and forward the rest of the args. + return AddTextureBetweenColorsExpectation(file, line, this->device, color0, color1, texture, + x, y, level, aspect, bytesPerRow); + } + + template + std::ostringstream& AddTextureBetweenColorsExpectation( + const char* file, + int line, + const wgpu::Device& targetDevice, + const T& color0, + const T& color1, + const wgpu::Texture& texture, + uint32_t x, + uint32_t y, + uint32_t level = 0, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All, + uint32_t bytesPerRow = 0) { return AddTextureExpectationImpl( - file, line, new detail::ExpectBetweenColors(color0, color1), texture, {x, y}, {1, 1}, - level, aspect, sizeof(T), bytesPerRow); + file, line, std::move(targetDevice), new detail::ExpectBetweenColors(color0, color1), + texture, {x, y}, {1, 1}, level, aspect, sizeof(T), bytesPerRow); } std::ostringstream& ExpectSampledFloatData(wgpu::Texture texture, @@ -452,7 +538,7 @@ class DawnTestBase { mipLevel, {}, &expectedStencil); } - void WaitABit(); + void WaitABit(wgpu::Device = nullptr); void FlushWire(); void WaitForAllOperations(); @@ -489,6 +575,7 @@ class DawnTestBase { std::ostringstream& AddTextureExpectationImpl(const char* file, int line, + wgpu::Device targetDevice, detail::Expectation* expectation, const wgpu::Texture& texture, wgpu::Origin3D origin, @@ -508,6 +595,7 @@ class DawnTestBase { // MapRead buffers used to get data for the expectations struct ReadbackSlot { + wgpu::Device device; wgpu::Buffer buffer; uint64_t bufferSize; const void* mappedData = nullptr; @@ -521,11 +609,12 @@ class DawnTestBase { // Reserve space where the data for an expectation can be copied struct ReadbackReservation { + wgpu::Device device; wgpu::Buffer buffer; size_t slot; uint64_t offset; }; - ReadbackReservation ReserveReadback(uint64_t readbackSize); + ReadbackReservation ReserveReadback(wgpu::Device targetDevice, uint64_t readbackSize); struct DeferredExpectation { const char* file; diff --git a/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp b/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp index d3112fe1e4..52de5d33c8 100644 --- a/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp +++ b/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp @@ -117,71 +117,57 @@ class D3D12ResourceTestBase : public DawnTestWithParams } protected: + std::unique_ptr CreateExternalImage( + WGPUDevice targetDevice, + ID3D11Texture2D* d3d11Texture, + const wgpu::TextureDescriptor* dawnDesc) const { + ComPtr dxgiResource; + EXPECT_EQ(d3d11Texture->QueryInterface(IID_PPV_ARGS(&dxgiResource)), S_OK); + + HANDLE textureSharedHandle = nullptr; + EXPECT_EQ(dxgiResource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &textureSharedHandle), + S_OK); + + dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc; + externalImageDesc.cTextureDescriptor = + reinterpret_cast(dawnDesc); + externalImageDesc.sharedHandle = textureSharedHandle; + externalImageDesc.useFenceSynchronization = GetParam().mSyncMode == SyncMode::kFence; + + std::unique_ptr externalImage = + dawn::native::d3d12::ExternalImageDXGI::Create(targetDevice, &externalImageDesc); + + // Now that we've created all of our resources, we can close the handle + // since we no longer need it. + ::CloseHandle(textureSharedHandle); + + return externalImage; + } + void WrapSharedHandle( const wgpu::TextureDescriptor* dawnDesc, const D3D11_TEXTURE2D_DESC* baseD3dDescriptor, wgpu::Texture* dawnTexture, ID3D11Texture2D** d3d11TextureOut, - std::unique_ptr* externalImageOut = nullptr, - uint64_t fenceSignalValue = 1) const { + std::unique_ptr* externalImageOut) const { ComPtr d3d11Texture; HRESULT hr = mD3d11Device->CreateTexture2D(baseD3dDescriptor, nullptr, &d3d11Texture); ASSERT_EQ(hr, S_OK); - ComPtr dxgiResource; - hr = d3d11Texture.As(&dxgiResource); - ASSERT_EQ(hr, S_OK); - - HANDLE textureSharedHandle; - hr = dxgiResource->CreateSharedHandle( - nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, - &textureSharedHandle); - ASSERT_EQ(hr, S_OK); - - HANDLE fenceSharedHandle = nullptr; - ComPtr d3d11Fence; - - if (GetParam().mSyncMode == SyncMode::kFence) { - ComPtr d3d11Device5; - hr = mD3d11Device.As(&d3d11Device5); - ASSERT_EQ(hr, S_OK); - - hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence)); - ASSERT_EQ(hr, S_OK); - - hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle); - ASSERT_EQ(hr, S_OK); - } - - dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc; - externalImageDesc.cTextureDescriptor = - reinterpret_cast(dawnDesc); - externalImageDesc.textureSharedHandle = textureSharedHandle; - externalImageDesc.fenceSharedHandle = fenceSharedHandle; - std::unique_ptr externalImage = - dawn::native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc); - - // Now that we've created all of our resources, we can close the handle - // since we no longer need it. - ::CloseHandle(textureSharedHandle); - if (fenceSharedHandle != nullptr) { - ::CloseHandle(fenceSharedHandle); - } + CreateExternalImage(device.Get(), d3d11Texture.Get(), dawnDesc); // Cannot access a non-existent external image (ex. validation error). if (externalImage == nullptr) { return; } - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; externalAccessDesc.usage = static_cast(dawnDesc->usage); - if (d3d11Fence != nullptr) { - externalAccessDesc.fenceWaitValue = 0; - externalAccessDesc.fenceSignalValue = fenceSignalValue; - } - *dawnTexture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + *dawnTexture = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); *d3d11TextureOut = d3d11Texture.Detach(); if (externalImageOut != nullptr) { @@ -211,9 +197,15 @@ TEST_P(D3D12SharedHandleValidation, Success) { wgpu::Texture texture; ComPtr d3d11Texture; - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture); + std::unique_ptr externalImage; + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, + &externalImage); ASSERT_NE(texture.Get(), nullptr); + + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); } // Test a successful wrapping of an D3D12Resource with DawnTextureInternalUsageDescriptor @@ -227,9 +219,15 @@ TEST_P(D3D12SharedHandleValidation, SuccessWithInternalUsageDescriptor) { wgpu::Texture texture; ComPtr d3d11Texture; - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture); + std::unique_ptr externalImage; + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, + &externalImage); ASSERT_NE(texture.Get(), nullptr); + + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); } // Test an error occurs if an invalid sType is the nextInChain @@ -242,8 +240,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidTextureDescriptor) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -255,8 +254,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidMipLevelCount) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -268,8 +268,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidDepth) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -281,8 +282,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidSampleCount) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -294,8 +296,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidWidth) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -307,8 +310,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidHeight) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -320,8 +324,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidFormat) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -333,8 +338,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidNumD3DMipLevels) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -346,8 +352,9 @@ TEST_P(D3D12SharedHandleValidation, InvalidD3DArraySize) { wgpu::Texture texture; ComPtr d3d11Texture; - ASSERT_DEVICE_ERROR( - WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + std::unique_ptr externalImage; + ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, + &d3d11Texture, &externalImage)); ASSERT_EQ(texture.Get(), nullptr); } @@ -387,15 +394,14 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { queue.Submit(1, &commands); } - void WrapAndClearD3D11Texture(const wgpu::TextureDescriptor& dawnDescriptor, - const D3D11_TEXTURE2D_DESC& d3dDescriptor, - const wgpu::Color& clearColor, - wgpu::Texture* dawnTextureOut, - ID3D11Texture2D** d3d11TextureOut, - bool isInitialized = true, - IDXGIKeyedMutex** dxgiKeyedMutexOut = nullptr, - ID3D11Fence** d3d11FenceOut = nullptr, - uint64_t* nextFenceWaitValue = nullptr) const { + void WrapAndClearD3D11Texture( + const wgpu::TextureDescriptor& dawnDescriptor, + const D3D11_TEXTURE2D_DESC& d3dDescriptor, + const wgpu::Color& clearColor, + wgpu::Texture* dawnTextureOut, + ID3D11Texture2D** d3d11TextureOut, + std::unique_ptr* externalImageOut, + bool isInitialized = true) const { ComPtr d3d11Texture; HRESULT hr = mD3d11Device->CreateTexture2D(&d3dDescriptor, nullptr, &d3d11Texture); ASSERT_EQ(hr, S_OK); @@ -404,10 +410,10 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { hr = d3d11Texture.As(&dxgiResource); ASSERT_EQ(hr, S_OK); - HANDLE textureSharedHandle; + HANDLE sharedHandle; hr = dxgiResource->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, - &textureSharedHandle); + &sharedHandle); ASSERT_EQ(hr, S_OK); ComPtr dxgiKeyedMutex; @@ -444,56 +450,47 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { static_cast(clearColor.b), static_cast(clearColor.a)}; mD3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), colorRGBA); - dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {}; - externalImageDesc.textureSharedHandle = textureSharedHandle; - externalImageDesc.fenceSharedHandle = fenceSharedHandle; - externalImageDesc.cTextureDescriptor = - reinterpret_cast(&dawnDescriptor); - - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; - externalAccessDesc.isInitialized = isInitialized; - externalAccessDesc.usage = static_cast(dawnDescriptor.usage); - + constexpr uint64_t kFenceSignalValue = 1; if (dxgiKeyedMutex) { hr = dxgiKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); ASSERT_EQ(hr, S_OK); } else { hr = mD3d11DeviceContext.As(&d3d11DeviceContext4); ASSERT_EQ(hr, S_OK); - // The fence starts with 0 signaled, but that won't capture the render target view clear // above, so signal explicitly with 1 and make the next Dawn access wait on 1. - d3d11DeviceContext4->Signal(d3d11Fence.Get(), 1); - - externalAccessDesc.fenceWaitValue = 1; - externalAccessDesc.fenceSignalValue = 2; + d3d11DeviceContext4->Signal(d3d11Fence.Get(), kFenceSignalValue); } + dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {}; + externalImageDesc.sharedHandle = sharedHandle; + externalImageDesc.cTextureDescriptor = + reinterpret_cast(&dawnDescriptor); + externalImageDesc.useFenceSynchronization = GetParam().mSyncMode == SyncMode::kFence; + std::unique_ptr externalImage = dawn::native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc); - *dawnTextureOut = - wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; + externalAccessDesc.isInitialized = isInitialized; + externalAccessDesc.usage = static_cast(dawnDescriptor.usage); + if (fenceSharedHandle != nullptr) { + externalAccessDesc.waitFences.push_back({fenceSharedHandle, kFenceSignalValue}); + } + + *dawnTextureOut = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); *d3d11TextureOut = d3d11Texture.Detach(); + *externalImageOut = std::move(externalImage); - if (dxgiKeyedMutexOut != nullptr) { - *dxgiKeyedMutexOut = dxgiKeyedMutex.Detach(); - } - - if (d3d11FenceOut != nullptr) { - *d3d11FenceOut = d3d11Fence.Detach(); - } - - if (nextFenceWaitValue != nullptr) { - *nextFenceWaitValue = externalAccessDesc.fenceSignalValue; + if (fenceSharedHandle != nullptr) { + ::CloseHandle(fenceSharedHandle); } } - void ExpectPixelRGBA8EQ(ID3D11Texture2D* d3d11Texture, - IDXGIKeyedMutex* dxgiKeyedMutex, - ID3D11Fence* d3d11Fence, - uint64_t fenceWaitValue, - const wgpu::Color& color) { + void ExpectPixelRGBA8EQ( + ID3D11Texture2D* d3d11Texture, + const wgpu::Color& color, + const dawn::native::d3d12::ExternalImageDXGIFenceDescriptor* waitFence = nullptr) { D3D11_TEXTURE2D_DESC texture2DDesc; d3d11Texture->GetDesc(&texture2DDesc); @@ -520,16 +517,28 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { d3dRc.bottom = texture2DDesc.Height; d3dRc.right = texture2DDesc.Width; + ComPtr dxgiKeyedMutex; + d3d11Texture->QueryInterface(IID_PPV_ARGS(&dxgiKeyedMutex)); + if (dxgiKeyedMutex != nullptr) { hr = dxgiKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE); ASSERT_EQ(hr, S_OK); } - if (d3d11Fence != nullptr) { + + if (waitFence != nullptr && waitFence->fenceHandle != nullptr) { + ComPtr d3d11Device5; + mD3d11Device.As(&d3d11Device5); + ASSERT_EQ(hr, S_OK); + + ComPtr d3d11Fence; + hr = d3d11Device5->OpenSharedFence(waitFence->fenceHandle, IID_PPV_ARGS(&d3d11Fence)); + ASSERT_EQ(hr, S_OK); + ComPtr d3d11DeviceContext4; hr = mD3d11DeviceContext.As(&d3d11DeviceContext4); ASSERT_EQ(hr, S_OK); - hr = d3d11DeviceContext4->Wait(d3d11Fence, fenceWaitValue); + hr = d3d11DeviceContext4->Wait(d3d11Fence.Get(), waitFence->fenceValue); ASSERT_EQ(hr, S_OK); } @@ -571,14 +580,19 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11CopyAndReadbackInD3D12) { const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; wgpu::Texture dawnSrcTexture; ComPtr d3d11Texture; + std::unique_ptr externalImage; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnSrcTexture, - &d3d11Texture); + &d3d11Texture, &externalImage); ASSERT_NE(dawnSrcTexture.Get(), nullptr); // Create a texture on the device and copy the source texture to it. wgpu::Texture dawnCopyDestTexture = device.CreateTexture(&baseDawnDescriptor); SimpleCopyTextureToTexture(dawnSrcTexture, dawnCopyDestTexture); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(dawnSrcTexture.Get(), &signalFence); + dawnSrcTexture.Destroy(); + // Readback the destination texture and ensure it contains the colors we used // to clear the source texture on the D3D device. EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(clearColor.r * 255u, clearColor.g * 255u, @@ -594,8 +608,9 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11ReadbackInD3D12) { const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; wgpu::Texture dawnTexture; ComPtr d3d11Texture; + std::unique_ptr externalImage; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnTexture, - &d3d11Texture); + &d3d11Texture, &externalImage); ASSERT_NE(dawnTexture.Get(), nullptr); // Readback the destination texture and ensure it contains the colors we used @@ -603,6 +618,10 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11ReadbackInD3D12) { EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(clearColor.r * 255, clearColor.g * 255, clearColor.b * 255, clearColor.a * 255), dawnTexture, 0, 0); + + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(dawnTexture.Get(), &signalFence); + dawnTexture.Destroy(); } // 1. Create and clear a D3D11 texture @@ -618,24 +637,23 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) { const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f}; wgpu::Texture dawnTexture; ComPtr d3d11Texture; + std::unique_ptr externalImage; ComPtr dxgiKeyedMutex; - ComPtr d3d11Fence; - uint64_t nextFenceWaitValue; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, d3d11ClearColor, &dawnTexture, - &d3d11Texture, /*isInitialized=*/true, &dxgiKeyedMutex, &d3d11Fence, - &nextFenceWaitValue); + &d3d11Texture, &externalImage, /*isInitialized=*/true); ASSERT_NE(dawnTexture.Get(), nullptr); const wgpu::Color d3d12ClearColor{0.0f, 0.0f, 1.0f, 1.0f}; ClearImage(dawnTexture, d3d12ClearColor, device); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(dawnTexture.Get(), &signalFence); dawnTexture.Destroy(); // Now that Dawn (via D3D12) has finished writing to the texture, we should be // able to read it back by copying it to a staging texture and verifying the // color matches the D3D12 clear color. - ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d11Fence.Get(), - nextFenceWaitValue, d3d12ClearColor); + ExpectPixelRGBA8EQ(d3d11Texture.Get(), d3d12ClearColor, &signalFence); } // 1. Create and clear a D3D11 texture @@ -652,12 +670,9 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) { const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f}; wgpu::Texture dawnTexture; ComPtr d3d11Texture; - ComPtr dxgiKeyedMutex; - ComPtr d3d11Fence; - uint64_t nextFenceWaitValue; + std::unique_ptr externalImage; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, d3d11ClearColor, &dawnTexture, - &d3d11Texture, /*isInitialized=*/true, &dxgiKeyedMutex, &d3d11Fence, - &nextFenceWaitValue); + &d3d11Texture, &externalImage, /*isInitialized=*/true); ASSERT_NE(dawnTexture.Get(), nullptr); const wgpu::Color d3d12ClearColor1{0.0f, 0.0f, 1.0f, 1.0f}; @@ -666,13 +681,14 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) { const wgpu::Color d3d12ClearColor2{0.0f, 1.0f, 1.0f, 1.0f}; ClearImage(dawnTexture, d3d12ClearColor2, device); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(dawnTexture.Get(), &signalFence); dawnTexture.Destroy(); // Now that Dawn (via D3D12) has finished writing to the texture, we should be // able to read it back by copying it to a staging texture and verifying the // color matches the last D3D12 clear color. - ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d11Fence.Get(), - nextFenceWaitValue, d3d12ClearColor2); + ExpectPixelRGBA8EQ(d3d11Texture.Get(), d3d12ClearColor2, &signalFence); } // 1. Create and clear a D3D11 texture with clearColor @@ -684,13 +700,18 @@ TEST_P(D3D12SharedHandleUsageTests, UninitializedTextureIsCleared) { const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f}; wgpu::Texture dawnTexture; ComPtr d3d11Texture; + std::unique_ptr externalImage; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnTexture, - &d3d11Texture, /*isInitialized=*/false); + &d3d11Texture, &externalImage, /*isInitialized=*/false); ASSERT_NE(dawnTexture.Get(), nullptr); // Readback the destination texture and ensure it contains the colors we used // to clear the source texture on the D3D device. EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0, 0, 0, 0), dawnTexture, 0, 0); + + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(dawnTexture.Get(), &signalFence); + dawnTexture.Destroy(); } // 1. Create an external image from the DX11 texture. @@ -704,7 +725,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) { ComPtr d3d11Texture; std::unique_ptr externalImage; WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, - &externalImage, /*fenceSignalValue=*/1); + &externalImage); { const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; ASSERT_NE(texture.Get(), nullptr); @@ -715,16 +736,17 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) { // Once finished with the first texture, destroy it so we may re-acquire the external image // again. + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); texture.Destroy(); // Create another Dawn texture then clear it with another color. - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; externalAccessDesc.isInitialized = true; externalAccessDesc.usage = static_cast(baseDawnDescriptor.usage); - externalAccessDesc.fenceWaitValue = 1; - externalAccessDesc.fenceSignalValue = 2; + externalAccessDesc.waitFences.push_back(signalFence); - texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + texture = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); // Check again that the new texture is still red EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0); @@ -738,47 +760,161 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) { EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0); } + externalImage->EndAccess(texture.Get(), &signalFence); texture.Destroy(); } TEST_P(D3D12SharedHandleUsageTests, ConcurrentExternalImageReadAccess) { DAWN_TEST_UNSUPPORTED_IF(UsesWire()); + DAWN_TEST_UNSUPPORTED_IF(GetParam().mSyncMode == SyncMode::kKeyedMutex); + + wgpu::Device device2 = CreateDevice(); + EXPECT_NE(device2, nullptr); + + wgpu::Device device3 = CreateDevice(); + EXPECT_NE(device3, nullptr); + + wgpu::Device device4 = CreateDevice(); + EXPECT_NE(device4, nullptr); + + wgpu::Device device5 = CreateDevice(); + EXPECT_NE(device5, nullptr); // Create Dawn texture with write access, then clear it to red. wgpu::Texture texture; ComPtr d3d11Texture; std::unique_ptr externalImage; WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, - &externalImage, /*fenceSignalValue=*/1); - { - const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; - ASSERT_NE(texture.Get(), nullptr); - ClearImage(texture.Get(), solidRed, device); + &externalImage); - EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0); - } + // Clear to red. + const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; + ASSERT_NE(texture.Get(), nullptr); + ClearImage(texture.Get(), solidRed, device); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); texture.Destroy(); - // Create two Dawn textures for concurrent read. - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + std::unique_ptr externalImage2 = + CreateExternalImage(device2.Get(), d3d11Texture.Get(), &baseDawnDescriptor); + EXPECT_NE(externalImage2, nullptr); + + std::unique_ptr externalImage3 = + CreateExternalImage(device3.Get(), d3d11Texture.Get(), &baseDawnDescriptor); + EXPECT_NE(externalImage3, nullptr); + + std::unique_ptr externalImage4 = + CreateExternalImage(device4.Get(), d3d11Texture.Get(), &baseDawnDescriptor); + EXPECT_NE(externalImage4, nullptr); + + std::unique_ptr externalImage5 = + CreateExternalImage(device5.Get(), d3d11Texture.Get(), &baseDawnDescriptor); + EXPECT_NE(externalImage5, nullptr); + + // Create two Dawn textures for concurrent read on second device. + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; externalAccessDesc.isInitialized = true; externalAccessDesc.usage = WGPUTextureUsage_CopySrc; - externalAccessDesc.fenceWaitValue = 1; - externalAccessDesc.fenceSignalValue = 2; + externalAccessDesc.waitFences = {signalFence}; - wgpu::Texture texture1 = - wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + // Concurrent read access on device 2 and 3. + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence2; + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence3; + { + wgpu::Texture texture2 = + wgpu::Texture::Acquire(externalImage2->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture2, nullptr); - wgpu::Texture texture2 = - wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + wgpu::Texture texture3 = + wgpu::Texture::Acquire(externalImage3->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture3, nullptr); - // Check again that the new textures are also red. - EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0xFF, 0, 0, 0xFF), texture1.Get(), 0, 0); - EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8(0xFF, 0, 0, 0xFF), texture2.Get(), 0, 0); + // Check again that the new textures are also red. + const utils::RGBA8 solidRed(0xFF, 0, 0, 0xFF); + EXPECT_TEXTURE_EQ(device2, solidRed, texture2.Get(), {0, 0}); + EXPECT_TEXTURE_EQ(device3, solidRed, texture3.Get(), {0, 0}); - texture1.Destroy(); - texture2.Destroy(); + externalImage2->EndAccess(texture2.Get(), &signalFence2); + texture2.Destroy(); + + externalImage3->EndAccess(texture3.Get(), &signalFence3); + texture3.Destroy(); + } + + externalAccessDesc.usage = static_cast(baseDawnDescriptor.usage); + externalAccessDesc.waitFences = {signalFence2, signalFence3}; + + // Exclusive read-write access on device 4. + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence4; + { + wgpu::Texture texture4 = + wgpu::Texture::Acquire(externalImage4->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture4, nullptr); + + const utils::RGBA8 solidRed(0xFF, 0, 0, 0xFF); + EXPECT_TEXTURE_EQ(device4, solidRed, texture4.Get(), {0, 0}); + + // Clear to blue. + const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f}; + ASSERT_NE(texture4.Get(), nullptr); + ClearImage(texture4.Get(), solidBlue, device4); + + externalImage4->EndAccess(texture4.Get(), &signalFence4); + texture4.Destroy(); + } + + externalAccessDesc.waitFences = {signalFence4}; + + // Import texture on device 5, but do nothing with it. + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence5; + { + wgpu::Texture texture5 = + wgpu::Texture::Acquire(externalImage5->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture5, nullptr); + externalImage5->EndAccess(texture5.Get(), &signalFence5); + texture5.Destroy(); + } + + externalAccessDesc.usage = WGPUTextureUsage_CopySrc; + externalAccessDesc.waitFences = {signalFence5}; + + // Concurrent read access on device 1 (twice), 2 and 3. + { + texture = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture, nullptr); + + wgpu::Texture texture1 = + wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture1, nullptr); + + wgpu::Texture texture2 = + wgpu::Texture::Acquire(externalImage2->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture2, nullptr); + + wgpu::Texture texture3 = + wgpu::Texture::Acquire(externalImage3->BeginAccess(&externalAccessDesc)); + EXPECT_NE(texture3, nullptr); + + // Check again that the new textures are now blue. + const utils::RGBA8 solidBlue(0, 0, 0xFF, 0xFF); + EXPECT_TEXTURE_EQ(device, solidBlue, texture.Get(), {0, 0}); + EXPECT_TEXTURE_EQ(device, solidBlue, texture1.Get(), {0, 0}); + EXPECT_TEXTURE_EQ(device2, solidBlue, texture2.Get(), {0, 0}); + EXPECT_TEXTURE_EQ(device3, solidBlue, texture3.Get(), {0, 0}); + + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); + + externalImage->EndAccess(texture1.Get(), &signalFence); + texture1.Destroy(); + + externalImage2->EndAccess(texture2.Get(), &signalFence2); + texture2.Destroy(); + + externalImage3->EndAccess(texture3.Get(), &signalFence3); + texture3.Destroy(); + } } // Produce a new texture with a usage not specified in the external image. @@ -789,20 +925,25 @@ TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) { ComPtr d3d11Texture; std::unique_ptr externalImage; WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, - &externalImage, /*fenceSignalValue=*/1); + &externalImage); ASSERT_NE(texture.Get(), nullptr); - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); + + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; externalAccessDesc.isInitialized = true; externalAccessDesc.usage = WGPUTextureUsage_StorageBinding; - externalAccessDesc.fenceWaitValue = 1; - externalAccessDesc.fenceSignalValue = 0; // No need to signal - texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + externalAccessDesc.waitFences.push_back(signalFence); + texture = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); ASSERT_EQ(texture.Get(), nullptr); externalAccessDesc.usage = WGPUTextureUsage_TextureBinding; - texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); + texture = wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)); ASSERT_NE(texture.Get(), nullptr); + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); } // Verify external image cannot be used after its creating device is destroyed. @@ -815,21 +956,23 @@ TEST_P(D3D12SharedHandleUsageTests, InvalidateExternalImageOnDestroyDevice) { // Create the Dawn texture then clear it to red. WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, - &externalImage, /*fenceSignalValue=*/1); + &externalImage); const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; ASSERT_NE(texture.Get(), nullptr); ClearImage(texture.Get(), solidRed, device); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); + // Do not readback pixels since that requires device to be alive during DawnTest::TearDown(). DestroyDevice(); - dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + dawn::native::d3d12::ExternalImageDXGIBeginAccessDescriptor externalAccessDesc; externalAccessDesc.isInitialized = true; externalAccessDesc.usage = static_cast(baseDawnDescriptor.usage); - externalAccessDesc.fenceWaitValue = 1; - externalAccessDesc.fenceSignalValue = 2; - EXPECT_EQ(wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)), nullptr); + EXPECT_EQ(wgpu::Texture::Acquire(externalImage->BeginAccess(&externalAccessDesc)), nullptr); } // Verify external image cannot be created after the target device is destroyed. @@ -843,7 +986,7 @@ TEST_P(D3D12SharedHandleUsageTests, DisallowExternalImageAfterDestroyDevice) { DestroyDevice(); ASSERT_DEVICE_ERROR(WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, - &d3d11Texture, &externalImage, /*fenceSignalValue=*/1)); + &d3d11Texture, &externalImage)); EXPECT_EQ(externalImage, nullptr); EXPECT_EQ(texture, nullptr); @@ -858,7 +1001,7 @@ TEST_P(D3D12SharedHandleUsageTests, CallWriteBufferBeforeDestroyingExternalImage ComPtr d3d11Texture; std::unique_ptr externalImage; WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, - &externalImage, /*fenceSignalValue=*/1); + &externalImage); // In utils::CreateBufferFromData() we will call queue.WriteBuffer(), which will make a // recording context pending. @@ -866,6 +1009,9 @@ TEST_P(D3D12SharedHandleUsageTests, CallWriteBufferBeforeDestroyingExternalImage wgpu::Buffer buffer = utils::CreateBufferFromData( device, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst, {kExpected}); + dawn::native::d3d12::ExternalImageDXGIFenceDescriptor signalFence; + externalImage->EndAccess(texture.Get(), &signalFence); + texture.Destroy(); externalImage = nullptr; EXPECT_BUFFER_U32_EQ(kExpected, buffer, 0);