From 7ae5c41412be17ea489cf20344141ec18410f5f2 Mon Sep 17 00:00:00 2001 From: Sunny Sachanandani Date: Wed, 13 Jul 2022 11:33:51 +0000 Subject: [PATCH] d3d12: Destroy ExternalImageDXGI resources on device destruction D3D12 objects can have implicit dependencies on device resources that are not captured by holding ComPtrs: "Direct3D 12 uses COM-style reference counting only for the lifetimes of interfaces (by using the weak reference model of Direct3D tied to the lifetime of the device). All resource and description memory lifetimes are the sole responsibly of the app to maintain for the proper duration, and are not reference counted. Direct3D 11 uses reference counting to manage the lifetimes of interface dependencies as well." Source: https://docs.microsoft.com/en-us/windows/win32/direct3d12/important-changes-from-directx-11-to-directx-12 ExternalImageDXGI can outlive the device it was created on e.g. the D3D shared image backing holds on to the ExternalImageDXGI for its lifetime. ExternalImageDXGI destructor can invoke code that depends on D3D12 resources that might have already been destroyed. In particular, this shows up as ComPtr::Release for ID3D12Fence crashing mysteriously, and is also speculated as the cause for a racy invalid function pointer dereference in crbug.com/1338470. This CL makes the D3D12 backend device destroy the ExternalImageDXGI's resources on device destruction making it effectively a weak pointer. This unblocks landing https://crrev.com/c/3700811 and hopefully fixes crbug.com/1338470 as well. This CL also deprecates unnecessary WGPUDevice param to ProduceTexture, and adds an IsValid() method so that the shared image can check it and decide to recreate the ExternalImageDXGI if needed. Bug: dawn:576, chromium:1338470 Change-Id: I2122cf807587cf3b1218ba29ea291263df0cf698 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95860 Kokoro: Kokoro Auto-Submit: Sunny Sachanandani Reviewed-by: Corentin Wallez Commit-Queue: Corentin Wallez --- include/dawn/native/D3D12Backend.h | 29 ++-- src/dawn/native/BUILD.gn | 2 + src/dawn/native/CMakeLists.txt | 2 + src/dawn/native/d3d12/D3D11on12Util.cpp | 3 +- src/dawn/native/d3d12/D3D11on12Util.h | 4 +- src/dawn/native/d3d12/D3D12Backend.cpp | 124 +++--------------- src/dawn/native/d3d12/DeviceD3D12.cpp | 61 +++++++++ src/dawn/native/d3d12/DeviceD3D12.h | 9 ++ .../native/d3d12/ExternalImageDXGIImpl.cpp | 113 ++++++++++++++++ src/dawn/native/d3d12/ExternalImageDXGIImpl.h | 72 ++++++++++ .../end2end/D3D12ResourceWrappingTests.cpp | 57 +++----- 11 files changed, 309 insertions(+), 167 deletions(-) create mode 100644 src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp create mode 100644 src/dawn/native/d3d12/ExternalImageDXGIImpl.h diff --git a/include/dawn/native/D3D12Backend.h b/include/dawn/native/D3D12Backend.h index 8384ff5c52..611ac80620 100644 --- a/include/dawn/native/D3D12Backend.h +++ b/include/dawn/native/D3D12Backend.h @@ -31,6 +31,8 @@ struct ID3D12Resource; namespace dawn::native::d3d12 { class D3D11on12ResourceCache; +class Device; +class ExternalImageDXGIImpl; DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr GetD3D12Device(WGPUDevice device); DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, @@ -95,33 +97,24 @@ class DAWN_NATIVE_EXPORT ExternalImageDXGI { public: ~ExternalImageDXGI(); - // Note: SharedHandle must be a handle to a texture object. static std::unique_ptr Create( WGPUDevice device, const ExternalImageDescriptorDXGISharedHandle* descriptor); + // Returns true if the external image resources are still valid, otherwise ProduceTexture() is + // guaranteed to fail e.g. after device destruction. + bool IsValid() const; + + // TODO(sunnyps): |device| is ignored - remove after Chromium migrates to single parameter call. WGPUTexture ProduceTexture(WGPUDevice device, const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + WGPUTexture ProduceTexture(const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + private: - ExternalImageDXGI(Microsoft::WRL::ComPtr d3d12Resource, - Microsoft::WRL::ComPtr d3d12Fence, - const WGPUTextureDescriptor* descriptor); + explicit ExternalImageDXGI(std::unique_ptr impl); - Microsoft::WRL::ComPtr mD3D12Resource; - Microsoft::WRL::ComPtr mD3D12Fence; - - // 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; - uint32_t mMipLevelCount; - uint32_t mSampleCount; - - std::unique_ptr mD3D11on12ResourceCache; + std::unique_ptr mImpl; }; struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase { diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn index e6cbc4de4f..07617ba87b 100644 --- a/src/dawn/native/BUILD.gn +++ b/src/dawn/native/BUILD.gn @@ -404,6 +404,8 @@ source_set("sources") { "d3d12/D3D12Info.h", "d3d12/DeviceD3D12.cpp", "d3d12/DeviceD3D12.h", + "d3d12/ExternalImageDXGIImpl.cpp", + "d3d12/ExternalImageDXGIImpl.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 9cf3b685f1..7d88df1d9e 100644 --- a/src/dawn/native/CMakeLists.txt +++ b/src/dawn/native/CMakeLists.txt @@ -271,6 +271,8 @@ if (DAWN_ENABLE_D3D12) "d3d12/D3D12Info.h" "d3d12/DeviceD3D12.cpp" "d3d12/DeviceD3D12.h" + "d3d12/ExternalImageDXGIImpl.cpp" + "d3d12/ExternalImageDXGIImpl.h" "d3d12/Forward.h" "d3d12/GPUDescriptorHeapAllocationD3D12.cpp" "d3d12/GPUDescriptorHeapAllocationD3D12.h" diff --git a/src/dawn/native/d3d12/D3D11on12Util.cpp b/src/dawn/native/d3d12/D3D11on12Util.cpp index a31c833d80..01a0081bf7 100644 --- a/src/dawn/native/d3d12/D3D11on12Util.cpp +++ b/src/dawn/native/d3d12/D3D11on12Util.cpp @@ -128,9 +128,8 @@ D3D11on12ResourceCache::D3D11on12ResourceCache() = default; D3D11on12ResourceCache::~D3D11on12ResourceCache() = default; Ref D3D11on12ResourceCache::GetOrCreateD3D11on12Resource( - WGPUDevice device, + Device* backendDevice, ID3D12Resource* d3d12Resource) { - Device* backendDevice = reinterpret_cast(device); // The Dawn and 11on12 device share the same D3D12 command queue whereas this external image // could be accessed/produced with multiple Dawn devices. To avoid cross-queue sharing // restrictions, the 11 wrapped resource is forbidden to be shared between Dawn devices by diff --git a/src/dawn/native/d3d12/D3D11on12Util.h b/src/dawn/native/d3d12/D3D11on12Util.h index 41ec11ce8e..92dde83f64 100644 --- a/src/dawn/native/d3d12/D3D11on12Util.h +++ b/src/dawn/native/d3d12/D3D11on12Util.h @@ -28,6 +28,8 @@ struct IDXGIKeyedMutex; namespace dawn::native::d3d12 { +class Device; + // Wraps 11 wrapped resources in a cache. class D3D11on12ResourceCacheEntry : public RefCounted { public: @@ -69,7 +71,7 @@ class D3D11on12ResourceCache { D3D11on12ResourceCache(); ~D3D11on12ResourceCache(); - Ref GetOrCreateD3D11on12Resource(WGPUDevice device, + Ref GetOrCreateD3D11on12Resource(Device* backendDevice, ID3D12Resource* d3d12Resource); private: diff --git a/src/dawn/native/d3d12/D3D12Backend.cpp b/src/dawn/native/d3d12/D3D12Backend.cpp index 3006193221..05ddd6b4c0 100644 --- a/src/dawn/native/d3d12/D3D12Backend.cpp +++ b/src/dawn/native/d3d12/D3D12Backend.cpp @@ -25,6 +25,7 @@ #include "dawn/common/SwapChainUtils.h" #include "dawn/native/d3d12/D3D11on12Util.h" #include "dawn/native/d3d12/DeviceD3D12.h" +#include "dawn/native/d3d12/ExternalImageDXGIImpl.h" #include "dawn/native/d3d12/NativeSwapChainImplD3D12.h" #include "dawn/native/d3d12/ResidencyManagerD3D12.h" #include "dawn/native/d3d12/TextureD3D12.h" @@ -53,71 +54,30 @@ WGPUTextureFormat GetNativeSwapChainPreferredFormat(const DawnSwapChainImplement ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle() : ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {} -ExternalImageDXGI::ExternalImageDXGI(ComPtr d3d12Resource, - ComPtr d3d12Fence, - const WGPUTextureDescriptor* descriptor) - : mD3D12Resource(std::move(d3d12Resource)), - mD3D12Fence(std::move(d3d12Fence)), - mUsage(descriptor->usage), - mDimension(descriptor->dimension), - mSize(descriptor->size), - mFormat(descriptor->format), - mMipLevelCount(descriptor->mipLevelCount), - mSampleCount(descriptor->sampleCount) { - ASSERT(!descriptor->nextInChain || - descriptor->nextInChain->sType == WGPUSType_DawnTextureInternalUsageDescriptor); - if (descriptor->nextInChain) { - mUsageInternal = - reinterpret_cast(descriptor->nextInChain) - ->internalUsage; - } - mD3D11on12ResourceCache = std::make_unique(); +ExternalImageDXGI::ExternalImageDXGI(std::unique_ptr impl) + : mImpl(std::move(impl)) { + ASSERT(mImpl != nullptr); } ExternalImageDXGI::~ExternalImageDXGI() = default; +bool ExternalImageDXGI::IsValid() const { + return mImpl->IsValid(); +} + WGPUTexture ExternalImageDXGI::ProduceTexture( WGPUDevice device, const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { - Device* backendDevice = ToBackend(FromAPI(device)); + return ProduceTexture(descriptor); +} - // Ensure the texture usage is allowed - if (!IsSubset(descriptor->usage, mUsage)) { - dawn::ErrorLog() << "Texture usage is not valid for external image"; +WGPUTexture ExternalImageDXGI::ProduceTexture( + const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { + if (!IsValid()) { + dawn::ErrorLog() << "Cannot produce texture from external image after device destruction"; return nullptr; } - - TextureDescriptor textureDescriptor = {}; - textureDescriptor.usage = static_cast(descriptor->usage); - textureDescriptor.dimension = static_cast(mDimension); - textureDescriptor.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers}; - textureDescriptor.format = static_cast(mFormat); - textureDescriptor.mipLevelCount = mMipLevelCount; - textureDescriptor.sampleCount = mSampleCount; - - DawnTextureInternalUsageDescriptor internalDesc = {}; - if (mUsageInternal) { - textureDescriptor.nextInChain = &internalDesc; - internalDesc.internalUsage = static_cast(mUsageInternal); - internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; - } - - Ref d3d11on12Resource; - if (!mD3D12Fence) { - d3d11on12Resource = - mD3D11on12ResourceCache->GetOrCreateD3D11on12Resource(device, mD3D12Resource.Get()); - if (d3d11on12Resource == nullptr) { - dawn::ErrorLog() << "Unable to create 11on12 resource for external image"; - return nullptr; - } - } - - Ref texture = backendDevice->CreateD3D12ExternalTexture( - &textureDescriptor, mD3D12Resource, mD3D12Fence, std::move(d3d11on12Resource), - descriptor->fenceWaitValue, descriptor->fenceSignalValue, descriptor->isSwapChainTexture, - descriptor->isInitialized); - - return ToAPI(texture.Detach()); + return mImpl->ProduceTexture(descriptor); } // static @@ -125,57 +85,13 @@ std::unique_ptr ExternalImageDXGI::Create( WGPUDevice device, const ExternalImageDescriptorDXGISharedHandle* descriptor) { Device* backendDevice = ToBackend(FromAPI(device)); - - // 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(backendDevice->GetD3D12Device()->OpenSharedHandle(textureSharedHandle, - IID_PPV_ARGS(&d3d12Resource)))) { + std::unique_ptr impl = + backendDevice->CreateExternalImageDXGIImpl(descriptor); + if (!impl) { + dawn::ErrorLog() << "Failed to create DXGI external image"; return nullptr; } - - Microsoft::WRL::ComPtr d3d12Fence; - if (descriptor->fenceSharedHandle && - FAILED(backendDevice->GetD3D12Device()->OpenSharedHandle(descriptor->fenceSharedHandle, - IID_PPV_ARGS(&d3d12Fence)))) { - return nullptr; - } - - const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor); - - if (backendDevice->ConsumedError(ValidateTextureDescriptor(backendDevice, textureDescriptor))) { - return nullptr; - } - - if (backendDevice->ConsumedError( - ValidateTextureDescriptorCanBeWrapped(textureDescriptor), - "validating that a D3D12 external image can be wrapped with %s", textureDescriptor)) { - return nullptr; - } - - if (backendDevice->ConsumedError( - ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), textureDescriptor))) { - return nullptr; - } - - // Shared handle is assumed to support resource sharing capability. The resource - // shared capability tier must agree to share resources between D3D devices. - const Format* format = - backendDevice->GetInternalFormat(textureDescriptor->format).AcquireSuccess(); - if (format->IsMultiPlanar()) { - if (backendDevice->ConsumedError(ValidateD3D12VideoTextureCanBeShared( - backendDevice, D3D12TextureFormat(textureDescriptor->format)))) { - return nullptr; - } - } - - std::unique_ptr result(new ExternalImageDXGI( - std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor)); - return result; + return std::unique_ptr(new ExternalImageDXGI(std::move(impl))); } uint64_t SetExternalMemoryReservation(WGPUDevice device, diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp index f5467eb451..ef29f143eb 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp @@ -20,6 +20,7 @@ #include #include "dawn/common/GPUInfo.h" +#include "dawn/native/D3D12Backend.h" #include "dawn/native/DynamicUploader.h" #include "dawn/native/Instance.h" #include "dawn/native/d3d12/AdapterD3D12.h" @@ -31,6 +32,7 @@ #include "dawn/native/d3d12/ComputePipelineD3D12.h" #include "dawn/native/d3d12/D3D11on12Util.h" #include "dawn/native/d3d12/D3D12Error.h" +#include "dawn/native/d3d12/ExternalImageDXGIImpl.h" #include "dawn/native/d3d12/PipelineLayoutD3D12.h" #include "dawn/native/d3d12/PlatformFunctions.h" #include "dawn/native/d3d12/QuerySetD3D12.h" @@ -532,6 +534,59 @@ ResultOrError Device::AllocateMemory( return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor, initialUsage); } +std::unique_ptr Device::CreateExternalImageDXGIImpl( + const ExternalImageDescriptorDXGISharedHandle* descriptor) { + // 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, + 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))) { + return nullptr; + } + + if (ConsumedError(ValidateTextureDescriptorCanBeWrapped(textureDescriptor), + "validating that a D3D12 external image can be wrapped with %s", + textureDescriptor)) { + return nullptr; + } + + if (ConsumedError(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), textureDescriptor))) { + return nullptr; + } + + // Shared handle is assumed to support resource sharing capability. The resource + // shared capability tier must agree to share resources between D3D devices. + const Format* format = GetInternalFormat(textureDescriptor->format).AcquireSuccess(); + if (format->IsMultiPlanar()) { + if (ConsumedError(ValidateD3D12VideoTextureCanBeShared( + this, D3D12TextureFormat(textureDescriptor->format)))) { + return nullptr; + } + } + + auto impl = std::make_unique( + this, std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor); + mExternalImageList.Append(impl.get()); + return impl; +} + Ref Device::CreateD3D12ExternalTexture( const TextureDescriptor* descriptor, ComPtr d3d12Texture, @@ -701,6 +756,12 @@ void Device::AppendDebugLayerMessages(ErrorData* error) { void Device::DestroyImpl() { ASSERT(GetState() == State::Disconnected); + while (!mExternalImageList.empty()) { + ExternalImageDXGIImpl* externalImage = mExternalImageList.head()->value(); + // ExternalImageDXGIImpl::Destroy() calls RemoveFromList(). + externalImage->Destroy(); + } + mZeroBuffer = nullptr; // Immediately forget about all pending commands for the case where device is lost on its diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h index b352eb97de..399fd64551 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h @@ -16,6 +16,7 @@ #define SRC_DAWN_NATIVE_D3D12_DEVICED3D12_H_ #include +#include #include "dawn/common/SerialQueue.h" #include "dawn/native/Device.h" @@ -27,6 +28,8 @@ namespace dawn::native::d3d12 { class CommandAllocatorManager; +struct ExternalImageDescriptorDXGISharedHandle; +class ExternalImageDXGIImpl; class PlatformFunctions; class ResidencyManager; class ResourceAllocatorManager; @@ -128,6 +131,9 @@ class Device final : public DeviceBase { StagingDescriptorAllocator* GetDepthStencilViewAllocator() const; + std::unique_ptr CreateExternalImageDXGIImpl( + const ExternalImageDescriptorDXGISharedHandle* descriptor); + Ref CreateD3D12ExternalTexture(const TextureDescriptor* descriptor, ComPtr d3d12Texture, ComPtr d3d12Fence, @@ -268,6 +274,9 @@ class Device final : public DeviceBase { // The number of nanoseconds required for a timestamp query to be incremented by 1 float mTimestampPeriod = 1.0f; + + // List of external image resources opened using this device. + LinkedList mExternalImageList; }; } // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp b/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp new file mode 100644 index 0000000000..b7838b2488 --- /dev/null +++ b/src/dawn/native/d3d12/ExternalImageDXGIImpl.cpp @@ -0,0 +1,113 @@ +// 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/ExternalImageDXGIImpl.h" + +#include + +#include + +#include "dawn/common/Log.h" +#include "dawn/native/D3D12Backend.h" +#include "dawn/native/DawnNative.h" +#include "dawn/native/d3d12/D3D11on12Util.h" +#include "dawn/native/d3d12/DeviceD3D12.h" + +namespace dawn::native::d3d12 { + +ExternalImageDXGIImpl::ExternalImageDXGIImpl(Device* backendDevice, + Microsoft::WRL::ComPtr d3d12Resource, + Microsoft::WRL::ComPtr d3d12Fence, + const WGPUTextureDescriptor* descriptor) + : mBackendDevice(backendDevice), + mD3D12Resource(std::move(d3d12Resource)), + mD3D12Fence(std::move(d3d12Fence)), + mD3D11on12ResourceCache(std::make_unique()), + mUsage(descriptor->usage), + mDimension(descriptor->dimension), + mSize(descriptor->size), + mFormat(descriptor->format), + mMipLevelCount(descriptor->mipLevelCount), + mSampleCount(descriptor->sampleCount) { + ASSERT(mBackendDevice != nullptr); + ASSERT(mD3D12Resource != nullptr); + ASSERT(!descriptor->nextInChain || + descriptor->nextInChain->sType == WGPUSType_DawnTextureInternalUsageDescriptor); + if (descriptor->nextInChain) { + mUsageInternal = + reinterpret_cast(descriptor->nextInChain) + ->internalUsage; + } +} + +ExternalImageDXGIImpl::~ExternalImageDXGIImpl() { + Destroy(); +} + +bool ExternalImageDXGIImpl::IsValid() const { + return IsInList(); +} + +void ExternalImageDXGIImpl::Destroy() { + if (IsInList()) { + RemoveFromList(); + mBackendDevice = nullptr; + mD3D12Resource.Reset(); + mD3D12Fence.Reset(); + mD3D11on12ResourceCache.reset(); + } +} + +WGPUTexture ExternalImageDXGIImpl::ProduceTexture( + const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { + ASSERT(mBackendDevice != nullptr); + // Ensure the texture usage is allowed + if (!IsSubset(descriptor->usage, 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.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers}; + textureDescriptor.format = static_cast(mFormat); + textureDescriptor.mipLevelCount = mMipLevelCount; + textureDescriptor.sampleCount = mSampleCount; + + DawnTextureInternalUsageDescriptor internalDesc = {}; + if (mUsageInternal) { + textureDescriptor.nextInChain = &internalDesc; + internalDesc.internalUsage = static_cast(mUsageInternal); + internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; + } + + Ref d3d11on12Resource; + if (!mD3D12Fence) { + d3d11on12Resource = mD3D11on12ResourceCache->GetOrCreateD3D11on12Resource( + mBackendDevice, mD3D12Resource.Get()); + if (d3d11on12Resource == nullptr) { + dawn::ErrorLog() << "Unable to create 11on12 resource for external image"; + return nullptr; + } + } + + Ref texture = mBackendDevice->CreateD3D12ExternalTexture( + &textureDescriptor, mD3D12Resource, mD3D12Fence, std::move(d3d11on12Resource), + descriptor->fenceWaitValue, descriptor->fenceSignalValue, descriptor->isSwapChainTexture, + descriptor->isInitialized); + return ToAPI(texture.Detach()); +} + +} // namespace dawn::native::d3d12 diff --git a/src/dawn/native/d3d12/ExternalImageDXGIImpl.h b/src/dawn/native/d3d12/ExternalImageDXGIImpl.h new file mode 100644 index 0000000000..016ce4a0e5 --- /dev/null +++ b/src/dawn/native/d3d12/ExternalImageDXGIImpl.h @@ -0,0 +1,72 @@ +// 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_EXTERNALIMAGEDXGIIMPL_H_ +#define SRC_DAWN_NATIVE_D3D12_EXTERNALIMAGEDXGIIMPL_H_ + +#include + +#include + +#include "dawn/common/LinkedList.h" +#include "dawn/dawn_wsi.h" +#include "dawn/native/Forward.h" + +struct ID3D12Resource; +struct ID3D12Fence; + +namespace dawn::native::d3d12 { + +class D3D11on12ResourceCache; +class Device; +struct ExternalImageAccessDescriptorDXGISharedHandle; +struct ExternalImageDescriptorDXGISharedHandle; + +class ExternalImageDXGIImpl : public LinkNode { + public: + ExternalImageDXGIImpl(Device* backendDevice, + Microsoft::WRL::ComPtr d3d12Resource, + Microsoft::WRL::ComPtr d3d12Fence, + const WGPUTextureDescriptor* descriptor); + ~ExternalImageDXGIImpl(); + + ExternalImageDXGIImpl(const ExternalImageDXGIImpl&) = delete; + ExternalImageDXGIImpl& operator=(const ExternalImageDXGIImpl&) = delete; + + void Destroy(); + + bool IsValid() const; + + WGPUTexture ProduceTexture(const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); + + private: + Device* mBackendDevice; + Microsoft::WRL::ComPtr mD3D12Resource; + Microsoft::WRL::ComPtr mD3D12Fence; + 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; + uint32_t mMipLevelCount; + uint32_t mSampleCount; +}; + +} // namespace dawn::native::d3d12 + +#endif // SRC_DAWN_NATIVE_D3D12_EXTERNALIMAGEDXGIIMPL_H_ diff --git a/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp b/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp index fc57036865..01a15b4dfd 100644 --- a/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp +++ b/src/dawn/tests/end2end/D3D12ResourceWrappingTests.cpp @@ -181,8 +181,7 @@ class D3D12ResourceTestBase : public DawnTestWithParams externalAccessDesc.fenceSignalValue = fenceSignalValue; } - *dawnTexture = wgpu::Texture::Acquire( - externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + *dawnTexture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); *d3d11TextureOut = d3d11Texture.Detach(); if (externalImageOut != nullptr) { @@ -473,8 +472,8 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { std::unique_ptr externalImage = dawn::native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc); - *dawnTextureOut = wgpu::Texture::Acquire( - externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + *dawnTextureOut = + wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); *d3d11TextureOut = d3d11Texture.Detach(); if (dxgiKeyedMutexOut != nullptr) { @@ -725,8 +724,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) { externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceSignalValue = 2; - texture = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); // Check again that the new texture is still red EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0); @@ -770,10 +768,10 @@ TEST_P(D3D12SharedHandleUsageTests, ConcurrentExternalImageReadAccess) { externalAccessDesc.fenceSignalValue = 2; wgpu::Texture texture1 = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); wgpu::Texture texture2 = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); // Check again that the new textures are also red. EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture1.Get(), 0, 0); @@ -799,64 +797,39 @@ TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) { externalAccessDesc.usage = WGPUTextureUsage_StorageBinding; externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceSignalValue = 0; // No need to signal - texture = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); ASSERT_EQ(texture.Get(), nullptr); externalAccessDesc.usage = WGPUTextureUsage_TextureBinding; - texture = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)); ASSERT_NE(texture.Get(), nullptr); } -// Verify two Dawn devices can reuse the same external image. -TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) { +// Verify external image cannot be used after its creating device is destroyed. +TEST_P(D3D12SharedHandleUsageTests, InvalidateExternalImageOnDestroyDevice) { DAWN_TEST_UNSUPPORTED_IF(UsesWire()); wgpu::Texture texture; ComPtr d3d11Texture; std::unique_ptr externalImage; - // Create the Dawn texture then clear it to red using the first (default) device. + // Create the Dawn texture then clear it to red. 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); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0); + // Do not readback pixels since that requires device to be alive during DawnTest::TearDown(). + DestroyDevice(); - // Release the texture so we can re-acquire another one from the same external image. - texture.Destroy(); - - // Create the Dawn texture then clear it to blue using the second device. dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc; + externalAccessDesc.isInitialized = true; externalAccessDesc.usage = static_cast(baseDawnDescriptor.usage); externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceSignalValue = 2; - wgpu::Device otherDevice = wgpu::Device::Acquire(GetAdapter().CreateDevice()); - - wgpu::Texture otherTexture = wgpu::Texture::Acquire( - externalImage->ProduceTexture(otherDevice.Get(), &externalAccessDesc)); - - ASSERT_NE(otherTexture.Get(), nullptr); - const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f}; - ClearImage(otherTexture.Get(), solidBlue, otherDevice); - - otherTexture.Destroy(); - - // Re-create the Dawn texture using the first (default) device. - externalAccessDesc.isInitialized = true; - externalAccessDesc.fenceWaitValue = 2; - externalAccessDesc.fenceSignalValue = 3; - texture = - wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); - ASSERT_NE(texture.Get(), nullptr); - - // Ensure the texture is still blue. - - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0); + EXPECT_EQ(wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)), nullptr); } DAWN_INSTANTIATE_TEST_P(D3D12SharedHandleValidation,