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 <noreply+kokoro@google.com>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Sunny Sachanandani 2022-07-13 11:33:51 +00:00 committed by Dawn LUCI CQ
parent 7a4f2b4496
commit 7ae5c41412
11 changed files with 309 additions and 167 deletions

View File

@ -31,6 +31,8 @@ struct ID3D12Resource;
namespace dawn::native::d3d12 { namespace dawn::native::d3d12 {
class D3D11on12ResourceCache; class D3D11on12ResourceCache;
class Device;
class ExternalImageDXGIImpl;
DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device(WGPUDevice device); DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device(WGPUDevice device);
DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device,
@ -95,33 +97,24 @@ class DAWN_NATIVE_EXPORT ExternalImageDXGI {
public: public:
~ExternalImageDXGI(); ~ExternalImageDXGI();
// Note: SharedHandle must be a handle to a texture object.
static std::unique_ptr<ExternalImageDXGI> Create( static std::unique_ptr<ExternalImageDXGI> Create(
WGPUDevice device, WGPUDevice device,
const ExternalImageDescriptorDXGISharedHandle* descriptor); 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, WGPUTexture ProduceTexture(WGPUDevice device,
const ExternalImageAccessDescriptorDXGISharedHandle* descriptor); const ExternalImageAccessDescriptorDXGISharedHandle* descriptor);
WGPUTexture ProduceTexture(const ExternalImageAccessDescriptorDXGISharedHandle* descriptor);
private: private:
ExternalImageDXGI(Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource, explicit ExternalImageDXGI(std::unique_ptr<ExternalImageDXGIImpl> impl);
Microsoft::WRL::ComPtr<ID3D12Fence> d3d12Fence,
const WGPUTextureDescriptor* descriptor);
Microsoft::WRL::ComPtr<ID3D12Resource> mD3D12Resource; std::unique_ptr<ExternalImageDXGIImpl> mImpl;
Microsoft::WRL::ComPtr<ID3D12Fence> 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<D3D11on12ResourceCache> mD3D11on12ResourceCache;
}; };
struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase { struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase {

View File

@ -404,6 +404,8 @@ source_set("sources") {
"d3d12/D3D12Info.h", "d3d12/D3D12Info.h",
"d3d12/DeviceD3D12.cpp", "d3d12/DeviceD3D12.cpp",
"d3d12/DeviceD3D12.h", "d3d12/DeviceD3D12.h",
"d3d12/ExternalImageDXGIImpl.cpp",
"d3d12/ExternalImageDXGIImpl.h",
"d3d12/Forward.h", "d3d12/Forward.h",
"d3d12/GPUDescriptorHeapAllocationD3D12.cpp", "d3d12/GPUDescriptorHeapAllocationD3D12.cpp",
"d3d12/GPUDescriptorHeapAllocationD3D12.h", "d3d12/GPUDescriptorHeapAllocationD3D12.h",

View File

@ -271,6 +271,8 @@ if (DAWN_ENABLE_D3D12)
"d3d12/D3D12Info.h" "d3d12/D3D12Info.h"
"d3d12/DeviceD3D12.cpp" "d3d12/DeviceD3D12.cpp"
"d3d12/DeviceD3D12.h" "d3d12/DeviceD3D12.h"
"d3d12/ExternalImageDXGIImpl.cpp"
"d3d12/ExternalImageDXGIImpl.h"
"d3d12/Forward.h" "d3d12/Forward.h"
"d3d12/GPUDescriptorHeapAllocationD3D12.cpp" "d3d12/GPUDescriptorHeapAllocationD3D12.cpp"
"d3d12/GPUDescriptorHeapAllocationD3D12.h" "d3d12/GPUDescriptorHeapAllocationD3D12.h"

View File

@ -128,9 +128,8 @@ D3D11on12ResourceCache::D3D11on12ResourceCache() = default;
D3D11on12ResourceCache::~D3D11on12ResourceCache() = default; D3D11on12ResourceCache::~D3D11on12ResourceCache() = default;
Ref<D3D11on12ResourceCacheEntry> D3D11on12ResourceCache::GetOrCreateD3D11on12Resource( Ref<D3D11on12ResourceCacheEntry> D3D11on12ResourceCache::GetOrCreateD3D11on12Resource(
WGPUDevice device, Device* backendDevice,
ID3D12Resource* d3d12Resource) { ID3D12Resource* d3d12Resource) {
Device* backendDevice = reinterpret_cast<Device*>(device);
// The Dawn and 11on12 device share the same D3D12 command queue whereas this external image // 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 // 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 // restrictions, the 11 wrapped resource is forbidden to be shared between Dawn devices by

View File

@ -28,6 +28,8 @@ struct IDXGIKeyedMutex;
namespace dawn::native::d3d12 { namespace dawn::native::d3d12 {
class Device;
// Wraps 11 wrapped resources in a cache. // Wraps 11 wrapped resources in a cache.
class D3D11on12ResourceCacheEntry : public RefCounted { class D3D11on12ResourceCacheEntry : public RefCounted {
public: public:
@ -69,7 +71,7 @@ class D3D11on12ResourceCache {
D3D11on12ResourceCache(); D3D11on12ResourceCache();
~D3D11on12ResourceCache(); ~D3D11on12ResourceCache();
Ref<D3D11on12ResourceCacheEntry> GetOrCreateD3D11on12Resource(WGPUDevice device, Ref<D3D11on12ResourceCacheEntry> GetOrCreateD3D11on12Resource(Device* backendDevice,
ID3D12Resource* d3d12Resource); ID3D12Resource* d3d12Resource);
private: private:

View File

@ -25,6 +25,7 @@
#include "dawn/common/SwapChainUtils.h" #include "dawn/common/SwapChainUtils.h"
#include "dawn/native/d3d12/D3D11on12Util.h" #include "dawn/native/d3d12/D3D11on12Util.h"
#include "dawn/native/d3d12/DeviceD3D12.h" #include "dawn/native/d3d12/DeviceD3D12.h"
#include "dawn/native/d3d12/ExternalImageDXGIImpl.h"
#include "dawn/native/d3d12/NativeSwapChainImplD3D12.h" #include "dawn/native/d3d12/NativeSwapChainImplD3D12.h"
#include "dawn/native/d3d12/ResidencyManagerD3D12.h" #include "dawn/native/d3d12/ResidencyManagerD3D12.h"
#include "dawn/native/d3d12/TextureD3D12.h" #include "dawn/native/d3d12/TextureD3D12.h"
@ -53,71 +54,30 @@ WGPUTextureFormat GetNativeSwapChainPreferredFormat(const DawnSwapChainImplement
ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle() ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle()
: ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {} : ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {}
ExternalImageDXGI::ExternalImageDXGI(ComPtr<ID3D12Resource> d3d12Resource, ExternalImageDXGI::ExternalImageDXGI(std::unique_ptr<ExternalImageDXGIImpl> impl)
ComPtr<ID3D12Fence> d3d12Fence, : mImpl(std::move(impl)) {
const WGPUTextureDescriptor* descriptor) ASSERT(mImpl != nullptr);
: 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<const WGPUDawnTextureInternalUsageDescriptor*>(descriptor->nextInChain)
->internalUsage;
}
mD3D11on12ResourceCache = std::make_unique<D3D11on12ResourceCache>();
} }
ExternalImageDXGI::~ExternalImageDXGI() = default; ExternalImageDXGI::~ExternalImageDXGI() = default;
bool ExternalImageDXGI::IsValid() const {
return mImpl->IsValid();
}
WGPUTexture ExternalImageDXGI::ProduceTexture( WGPUTexture ExternalImageDXGI::ProduceTexture(
WGPUDevice device, WGPUDevice device,
const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) { const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = ToBackend(FromAPI(device)); return ProduceTexture(descriptor);
}
// Ensure the texture usage is allowed WGPUTexture ExternalImageDXGI::ProduceTexture(
if (!IsSubset(descriptor->usage, mUsage)) { const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) {
dawn::ErrorLog() << "Texture usage is not valid for external image"; if (!IsValid()) {
dawn::ErrorLog() << "Cannot produce texture from external image after device destruction";
return nullptr; return nullptr;
} }
return mImpl->ProduceTexture(descriptor);
TextureDescriptor textureDescriptor = {};
textureDescriptor.usage = static_cast<wgpu::TextureUsage>(descriptor->usage);
textureDescriptor.dimension = static_cast<wgpu::TextureDimension>(mDimension);
textureDescriptor.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers};
textureDescriptor.format = static_cast<wgpu::TextureFormat>(mFormat);
textureDescriptor.mipLevelCount = mMipLevelCount;
textureDescriptor.sampleCount = mSampleCount;
DawnTextureInternalUsageDescriptor internalDesc = {};
if (mUsageInternal) {
textureDescriptor.nextInChain = &internalDesc;
internalDesc.internalUsage = static_cast<wgpu::TextureUsage>(mUsageInternal);
internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
}
Ref<D3D11on12ResourceCacheEntry> 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<TextureBase> texture = backendDevice->CreateD3D12ExternalTexture(
&textureDescriptor, mD3D12Resource, mD3D12Fence, std::move(d3d11on12Resource),
descriptor->fenceWaitValue, descriptor->fenceSignalValue, descriptor->isSwapChainTexture,
descriptor->isInitialized);
return ToAPI(texture.Detach());
} }
// static // static
@ -125,57 +85,13 @@ std::unique_ptr<ExternalImageDXGI> ExternalImageDXGI::Create(
WGPUDevice device, WGPUDevice device,
const ExternalImageDescriptorDXGISharedHandle* descriptor) { const ExternalImageDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = ToBackend(FromAPI(device)); Device* backendDevice = ToBackend(FromAPI(device));
std::unique_ptr<ExternalImageDXGIImpl> impl =
// Use sharedHandle as a fallback until Chromium code is changed to set textureSharedHandle. backendDevice->CreateExternalImageDXGIImpl(descriptor);
HANDLE textureSharedHandle = descriptor->textureSharedHandle; if (!impl) {
if (!textureSharedHandle) { dawn::ErrorLog() << "Failed to create DXGI external image";
textureSharedHandle = descriptor->sharedHandle;
}
Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource;
if (FAILED(backendDevice->GetD3D12Device()->OpenSharedHandle(textureSharedHandle,
IID_PPV_ARGS(&d3d12Resource)))) {
return nullptr; return nullptr;
} }
return std::unique_ptr<ExternalImageDXGI>(new ExternalImageDXGI(std::move(impl)));
Microsoft::WRL::ComPtr<ID3D12Fence> 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<ExternalImageDXGI> result(new ExternalImageDXGI(
std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor));
return result;
} }
uint64_t SetExternalMemoryReservation(WGPUDevice device, uint64_t SetExternalMemoryReservation(WGPUDevice device,

View File

@ -20,6 +20,7 @@
#include <utility> #include <utility>
#include "dawn/common/GPUInfo.h" #include "dawn/common/GPUInfo.h"
#include "dawn/native/D3D12Backend.h"
#include "dawn/native/DynamicUploader.h" #include "dawn/native/DynamicUploader.h"
#include "dawn/native/Instance.h" #include "dawn/native/Instance.h"
#include "dawn/native/d3d12/AdapterD3D12.h" #include "dawn/native/d3d12/AdapterD3D12.h"
@ -31,6 +32,7 @@
#include "dawn/native/d3d12/ComputePipelineD3D12.h" #include "dawn/native/d3d12/ComputePipelineD3D12.h"
#include "dawn/native/d3d12/D3D11on12Util.h" #include "dawn/native/d3d12/D3D11on12Util.h"
#include "dawn/native/d3d12/D3D12Error.h" #include "dawn/native/d3d12/D3D12Error.h"
#include "dawn/native/d3d12/ExternalImageDXGIImpl.h"
#include "dawn/native/d3d12/PipelineLayoutD3D12.h" #include "dawn/native/d3d12/PipelineLayoutD3D12.h"
#include "dawn/native/d3d12/PlatformFunctions.h" #include "dawn/native/d3d12/PlatformFunctions.h"
#include "dawn/native/d3d12/QuerySetD3D12.h" #include "dawn/native/d3d12/QuerySetD3D12.h"
@ -532,6 +534,59 @@ ResultOrError<ResourceHeapAllocation> Device::AllocateMemory(
return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor, initialUsage); return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor, initialUsage);
} }
std::unique_ptr<ExternalImageDXGIImpl> 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<ID3D12Resource> d3d12Resource;
if (FAILED(GetD3D12Device()->OpenSharedHandle(textureSharedHandle,
IID_PPV_ARGS(&d3d12Resource)))) {
return nullptr;
}
Microsoft::WRL::ComPtr<ID3D12Fence> 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<ExternalImageDXGIImpl>(
this, std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor);
mExternalImageList.Append(impl.get());
return impl;
}
Ref<TextureBase> Device::CreateD3D12ExternalTexture( Ref<TextureBase> Device::CreateD3D12ExternalTexture(
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
@ -701,6 +756,12 @@ void Device::AppendDebugLayerMessages(ErrorData* error) {
void Device::DestroyImpl() { void Device::DestroyImpl() {
ASSERT(GetState() == State::Disconnected); ASSERT(GetState() == State::Disconnected);
while (!mExternalImageList.empty()) {
ExternalImageDXGIImpl* externalImage = mExternalImageList.head()->value();
// ExternalImageDXGIImpl::Destroy() calls RemoveFromList().
externalImage->Destroy();
}
mZeroBuffer = nullptr; mZeroBuffer = nullptr;
// Immediately forget about all pending commands for the case where device is lost on its // Immediately forget about all pending commands for the case where device is lost on its

View File

@ -16,6 +16,7 @@
#define SRC_DAWN_NATIVE_D3D12_DEVICED3D12_H_ #define SRC_DAWN_NATIVE_D3D12_DEVICED3D12_H_
#include <memory> #include <memory>
#include <vector>
#include "dawn/common/SerialQueue.h" #include "dawn/common/SerialQueue.h"
#include "dawn/native/Device.h" #include "dawn/native/Device.h"
@ -27,6 +28,8 @@
namespace dawn::native::d3d12 { namespace dawn::native::d3d12 {
class CommandAllocatorManager; class CommandAllocatorManager;
struct ExternalImageDescriptorDXGISharedHandle;
class ExternalImageDXGIImpl;
class PlatformFunctions; class PlatformFunctions;
class ResidencyManager; class ResidencyManager;
class ResourceAllocatorManager; class ResourceAllocatorManager;
@ -128,6 +131,9 @@ class Device final : public DeviceBase {
StagingDescriptorAllocator* GetDepthStencilViewAllocator() const; StagingDescriptorAllocator* GetDepthStencilViewAllocator() const;
std::unique_ptr<ExternalImageDXGIImpl> CreateExternalImageDXGIImpl(
const ExternalImageDescriptorDXGISharedHandle* descriptor);
Ref<TextureBase> CreateD3D12ExternalTexture(const TextureDescriptor* descriptor, Ref<TextureBase> CreateD3D12ExternalTexture(const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Fence> d3d12Fence, ComPtr<ID3D12Fence> d3d12Fence,
@ -268,6 +274,9 @@ class Device final : public DeviceBase {
// The number of nanoseconds required for a timestamp query to be incremented by 1 // The number of nanoseconds required for a timestamp query to be incremented by 1
float mTimestampPeriod = 1.0f; float mTimestampPeriod = 1.0f;
// List of external image resources opened using this device.
LinkedList<ExternalImageDXGIImpl> mExternalImageList;
}; };
} // namespace dawn::native::d3d12 } // namespace dawn::native::d3d12

View File

@ -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 <d3d12.h>
#include <utility>
#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<ID3D12Resource> d3d12Resource,
Microsoft::WRL::ComPtr<ID3D12Fence> d3d12Fence,
const WGPUTextureDescriptor* descriptor)
: mBackendDevice(backendDevice),
mD3D12Resource(std::move(d3d12Resource)),
mD3D12Fence(std::move(d3d12Fence)),
mD3D11on12ResourceCache(std::make_unique<D3D11on12ResourceCache>()),
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<const WGPUDawnTextureInternalUsageDescriptor*>(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<wgpu::TextureUsage>(descriptor->usage);
textureDescriptor.dimension = static_cast<wgpu::TextureDimension>(mDimension);
textureDescriptor.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers};
textureDescriptor.format = static_cast<wgpu::TextureFormat>(mFormat);
textureDescriptor.mipLevelCount = mMipLevelCount;
textureDescriptor.sampleCount = mSampleCount;
DawnTextureInternalUsageDescriptor internalDesc = {};
if (mUsageInternal) {
textureDescriptor.nextInChain = &internalDesc;
internalDesc.internalUsage = static_cast<wgpu::TextureUsage>(mUsageInternal);
internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
}
Ref<D3D11on12ResourceCacheEntry> 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<TextureBase> 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

View File

@ -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 <wrl/client.h>
#include <memory>
#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<ExternalImageDXGIImpl> {
public:
ExternalImageDXGIImpl(Device* backendDevice,
Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource,
Microsoft::WRL::ComPtr<ID3D12Fence> 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<ID3D12Resource> mD3D12Resource;
Microsoft::WRL::ComPtr<ID3D12Fence> mD3D12Fence;
std::unique_ptr<D3D11on12ResourceCache> 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_

View File

@ -181,8 +181,7 @@ class D3D12ResourceTestBase : public DawnTestWithParams<D3D12ResourceTestParams>
externalAccessDesc.fenceSignalValue = fenceSignalValue; externalAccessDesc.fenceSignalValue = fenceSignalValue;
} }
*dawnTexture = wgpu::Texture::Acquire( *dawnTexture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
*d3d11TextureOut = d3d11Texture.Detach(); *d3d11TextureOut = d3d11Texture.Detach();
if (externalImageOut != nullptr) { if (externalImageOut != nullptr) {
@ -473,8 +472,8 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> externalImage = std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> externalImage =
dawn::native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc); dawn::native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc);
*dawnTextureOut = wgpu::Texture::Acquire( *dawnTextureOut =
externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
*d3d11TextureOut = d3d11Texture.Detach(); *d3d11TextureOut = d3d11Texture.Detach();
if (dxgiKeyedMutexOut != nullptr) { if (dxgiKeyedMutexOut != nullptr) {
@ -725,8 +724,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2; externalAccessDesc.fenceSignalValue = 2;
texture = texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
// Check again that the new texture is still red // Check again that the new texture is still red
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
@ -770,10 +768,10 @@ TEST_P(D3D12SharedHandleUsageTests, ConcurrentExternalImageReadAccess) {
externalAccessDesc.fenceSignalValue = 2; externalAccessDesc.fenceSignalValue = 2;
wgpu::Texture texture1 = wgpu::Texture texture1 =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
wgpu::Texture texture2 = 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. // Check again that the new textures are also red.
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture1.Get(), 0, 0); 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.usage = WGPUTextureUsage_StorageBinding;
externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 0; // No need to signal externalAccessDesc.fenceSignalValue = 0; // No need to signal
texture = texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
ASSERT_EQ(texture.Get(), nullptr); ASSERT_EQ(texture.Get(), nullptr);
externalAccessDesc.usage = WGPUTextureUsage_TextureBinding; externalAccessDesc.usage = WGPUTextureUsage_TextureBinding;
texture = texture = wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc));
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
} }
// Verify two Dawn devices can reuse the same external image. // Verify external image cannot be used after its creating device is destroyed.
TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) { TEST_P(D3D12SharedHandleUsageTests, InvalidateExternalImageOnDestroyDevice) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire()); DAWN_TEST_UNSUPPORTED_IF(UsesWire());
wgpu::Texture texture; wgpu::Texture texture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> externalImage; std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> 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, WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage, /*fenceSignalValue=*/1); &externalImage, /*fenceSignalValue=*/1);
const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f};
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
ClearImage(texture.Get(), solidRed, device); 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; dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.isInitialized = true;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage); externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
externalAccessDesc.fenceWaitValue = 1; externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2; externalAccessDesc.fenceSignalValue = 2;
wgpu::Device otherDevice = wgpu::Device::Acquire(GetAdapter().CreateDevice()); EXPECT_EQ(wgpu::Texture::Acquire(externalImage->ProduceTexture(&externalAccessDesc)), nullptr);
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);
} }
DAWN_INSTANTIATE_TEST_P(D3D12SharedHandleValidation, DAWN_INSTANTIATE_TEST_P(D3D12SharedHandleValidation,