D3D12: Cache DXGI keyed mutexes on the external image.

Maintains a 11on12 resource cache on the external image
to prevent re-creating the wrapped resource then
flushing it per produced texture. To prevent
unbounded growth, a basic cap is used which gets
cleared.

This change fixes signficant CPU time spent in the WebGPU command
decoder for video import workloads and excessive memory overhead
from swap chain buffers.

Fixed: dawn:625

Change-Id: I72c07b02f6ab6877a9f21758650962c895933bf9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51421
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Bryan Bernhart 2021-08-12 23:32:49 +00:00 committed by Dawn LUCI CQ
parent f209e8ba83
commit 31f039350b
11 changed files with 402 additions and 116 deletions

View File

@ -358,6 +358,8 @@ source_set("dawn_native_sources") {
"d3d12/CommandRecordingContext.h", "d3d12/CommandRecordingContext.h",
"d3d12/ComputePipelineD3D12.cpp", "d3d12/ComputePipelineD3D12.cpp",
"d3d12/ComputePipelineD3D12.h", "d3d12/ComputePipelineD3D12.h",
"d3d12/D3D11on12Util.cpp",
"d3d12/D3D11on12Util.h",
"d3d12/D3D12Error.cpp", "d3d12/D3D12Error.cpp",
"d3d12/D3D12Error.h", "d3d12/D3D12Error.h",
"d3d12/D3D12Info.cpp", "d3d12/D3D12Info.cpp",

View File

@ -232,6 +232,8 @@ if (DAWN_ENABLE_D3D12)
"d3d12/CommandRecordingContext.h" "d3d12/CommandRecordingContext.h"
"d3d12/ComputePipelineD3D12.cpp" "d3d12/ComputePipelineD3D12.cpp"
"d3d12/ComputePipelineD3D12.h" "d3d12/ComputePipelineD3D12.h"
"d3d12/D3D11on12Util.cpp"
"d3d12/D3D11on12Util.h"
"d3d12/D3D12Error.cpp" "d3d12/D3D12Error.cpp"
"d3d12/D3D12Error.h" "d3d12/D3D12Error.h"
"d3d12/D3D12Info.cpp" "d3d12/D3D12Info.cpp"

View File

@ -0,0 +1,164 @@
// Copyright 2021 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.
// D3D12Backend.cpp: contains the definition of symbols exported by D3D12Backend.h so that they
// can be compiled twice: once export (shared library), once not exported (static library)
#include "dawn_native/d3d12/D3D11on12Util.h"
#include "common/HashUtils.h"
#include "common/Log.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
namespace dawn_native { namespace d3d12 {
void Flush11On12DeviceToAvoidLeaks(ComPtr<ID3D11On12Device> d3d11on12Device) {
if (d3d11on12Device == nullptr) {
return;
}
ComPtr<ID3D11Device> d3d11Device;
if (FAILED(d3d11on12Device.As(&d3d11Device))) {
return;
}
ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
d3d11Device->GetImmediateContext(&d3d11DeviceContext);
ASSERT(d3d11DeviceContext != nullptr);
// 11on12 has a bug where D3D12 resources used only for keyed shared mutexes
// are not released until work is submitted to the device context and flushed.
// The most minimal work we can get away with is issuing a TiledResourceBarrier.
// ID3D11DeviceContext2 is available in Win8.1 and above. This suffices for a
// D3D12 backend since both D3D12 and 11on12 first appeared in Windows 10.
ComPtr<ID3D11DeviceContext2> d3d11DeviceContext2;
if (FAILED(d3d11DeviceContext.As(&d3d11DeviceContext2))) {
return;
}
d3d11DeviceContext2->TiledResourceBarrier(nullptr, nullptr);
d3d11DeviceContext2->Flush();
}
D3D11on12ResourceCacheEntry::D3D11on12ResourceCacheEntry(
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex,
ComPtr<ID3D11On12Device> d3d11On12Device)
: mDXGIKeyedMutex(std::move(dxgiKeyedMutex)), mD3D11on12Device(std::move(d3d11On12Device)) {
}
D3D11on12ResourceCacheEntry::D3D11on12ResourceCacheEntry(
ComPtr<ID3D11On12Device> d3d11On12Device)
: mD3D11on12Device(std::move(d3d11On12Device)) {
}
D3D11on12ResourceCacheEntry::~D3D11on12ResourceCacheEntry() {
if (mDXGIKeyedMutex == nullptr) {
return;
}
ComPtr<ID3D11Resource> d3d11Resource;
if (FAILED(mDXGIKeyedMutex.As(&d3d11Resource))) {
return;
}
ASSERT(mD3D11on12Device != nullptr);
ID3D11Resource* d3d11ResourceRaw = d3d11Resource.Get();
mD3D11on12Device->ReleaseWrappedResources(&d3d11ResourceRaw, 1);
d3d11Resource.Reset();
mDXGIKeyedMutex.Reset();
Flush11On12DeviceToAvoidLeaks(std::move(mD3D11on12Device));
}
ComPtr<IDXGIKeyedMutex> D3D11on12ResourceCacheEntry::GetDXGIKeyedMutex() const {
ASSERT(mDXGIKeyedMutex != nullptr);
return mDXGIKeyedMutex;
}
size_t D3D11on12ResourceCacheEntry::HashFunc::operator()(
const Ref<D3D11on12ResourceCacheEntry> a) const {
size_t hash = 0;
HashCombine(&hash, a->mD3D11on12Device.Get());
return hash;
}
bool D3D11on12ResourceCacheEntry::EqualityFunc::operator()(
const Ref<D3D11on12ResourceCacheEntry> a,
const Ref<D3D11on12ResourceCacheEntry> b) const {
return a->mD3D11on12Device == b->mD3D11on12Device;
}
D3D11on12ResourceCache::D3D11on12ResourceCache() = default;
D3D11on12ResourceCache::~D3D11on12ResourceCache() = default;
Ref<D3D11on12ResourceCacheEntry> D3D11on12ResourceCache::GetOrCreateD3D11on12Resource(
WGPUDevice device,
ID3D12Resource* d3d12Resource) {
Device* backendDevice = reinterpret_cast<Device*>(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
// using the 11on12 device as the cache key.
ComPtr<ID3D11On12Device> d3d11on12Device = backendDevice->GetOrCreateD3D11on12Device();
if (d3d11on12Device == nullptr) {
dawn::ErrorLog() << "Unable to create 11on12 device for external image";
return nullptr;
}
D3D11on12ResourceCacheEntry blueprint(d3d11on12Device);
auto iter = mCache.find(&blueprint);
if (iter != mCache.end()) {
return *iter;
}
// We use IDXGIKeyedMutexes to synchronize access between D3D11 and D3D12. D3D11/12 fences
// are a viable alternative but are, unfortunately, not available on all versions of Windows
// 10. Since D3D12 does not directly support keyed mutexes, we need to wrap the D3D12
// resource using 11on12 and QueryInterface the D3D11 representation for the keyed mutex.
ComPtr<ID3D11Texture2D> d3d11Texture;
D3D11_RESOURCE_FLAGS resourceFlags;
resourceFlags.BindFlags = 0;
resourceFlags.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
resourceFlags.CPUAccessFlags = 0;
resourceFlags.StructureByteStride = 0;
if (FAILED(d3d11on12Device->CreateWrappedResource(
d3d12Resource, &resourceFlags, D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_COMMON, IID_PPV_ARGS(&d3d11Texture)))) {
return nullptr;
}
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
if (FAILED(d3d11Texture.As(&dxgiKeyedMutex))) {
return nullptr;
}
// Keep this cache from growing unbounded.
// TODO(dawn:625): Consider using a replacement policy based cache.
if (mCache.size() > kMaxD3D11on12ResourceCacheSize) {
mCache.clear();
}
Ref<D3D11on12ResourceCacheEntry> entry =
AcquireRef(new D3D11on12ResourceCacheEntry(dxgiKeyedMutex, std::move(d3d11on12Device)));
mCache.insert(entry);
return entry;
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,89 @@
// Copyright 2021 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 DAWNNATIVE_D3D11ON12UTIL_H_
#define DAWNNATIVE_D3D11ON12UTIL_H_
#include "common/RefCounted.h"
#include "dawn_native/d3d12/d3d12_platform.h"
#include <dawn_native/DawnNative.h>
#include <memory>
#include <unordered_set>
struct ID3D11On12Device;
struct IDXGIKeyedMutex;
namespace dawn_native { namespace d3d12 {
// Wraps 11 wrapped resources in a cache.
class D3D11on12ResourceCacheEntry : public RefCounted {
public:
D3D11on12ResourceCacheEntry(ComPtr<ID3D11On12Device> d3d11on12Device);
D3D11on12ResourceCacheEntry(ComPtr<IDXGIKeyedMutex> d3d11on12Resource,
ComPtr<ID3D11On12Device> d3d11on12Device);
~D3D11on12ResourceCacheEntry();
ComPtr<IDXGIKeyedMutex> GetDXGIKeyedMutex() const;
// Functors necessary for the
// unordered_set<D3D11on12ResourceCacheEntry&>-based cache.
struct HashFunc {
size_t operator()(const Ref<D3D11on12ResourceCacheEntry> a) const;
};
struct EqualityFunc {
bool operator()(const Ref<D3D11on12ResourceCacheEntry> a,
const Ref<D3D11on12ResourceCacheEntry> b) const;
};
private:
ComPtr<IDXGIKeyedMutex> mDXGIKeyedMutex;
ComPtr<ID3D11On12Device> mD3D11on12Device;
};
// |D3D11on12ResourceCache| maintains a cache of 11 wrapped resources.
// Each entry represents a 11 resource that is exclusively accessed by Dawn device.
// Since each Dawn device creates and stores a 11on12 device, the 11on12 device
// is used as the key for the cache entry which ensures only the same 11 wrapped
// resource is re-used and also fully released.
//
// The cache is primarily needed to avoid repeatedly calling CreateWrappedResource
// and special release code per ProduceTexture(device).
class D3D11on12ResourceCache {
public:
D3D11on12ResourceCache();
~D3D11on12ResourceCache();
Ref<D3D11on12ResourceCacheEntry> GetOrCreateD3D11on12Resource(
WGPUDevice device,
ID3D12Resource* d3d12Resource);
private:
// TODO(dawn:625): Figure out a large enough cache size.
static constexpr uint64_t kMaxD3D11on12ResourceCacheSize = 5;
// 11on12 resource cache entries are refcounted to ensure if the ExternalImage outlives the
// Dawn texture (or vice-versa), we always fully release the 11 wrapped resource without
// waiting until Dawn device to shutdown.
using Cache = std::unordered_set<Ref<D3D11on12ResourceCacheEntry>,
D3D11on12ResourceCacheEntry::HashFunc,
D3D11on12ResourceCacheEntry::EqualityFunc>;
Cache mCache;
};
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D11ON12UTIL_H_

View File

@ -20,6 +20,7 @@
#include "common/Log.h" #include "common/Log.h"
#include "common/Math.h" #include "common/Math.h"
#include "common/SwapChainUtils.h" #include "common/SwapChainUtils.h"
#include "dawn_native/d3d12/D3D11on12Util.h"
#include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.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"
@ -69,8 +70,11 @@ namespace dawn_native { namespace d3d12 {
descriptor->nextInChain) descriptor->nextInChain)
->internalUsage; ->internalUsage;
} }
mD3D11on12ResourceCache = std::make_unique<D3D11on12ResourceCache>();
} }
ExternalImageDXGI::~ExternalImageDXGI() = default;
WGPUTexture ExternalImageDXGI::ProduceTexture( WGPUTexture ExternalImageDXGI::ProduceTexture(
WGPUDevice device, WGPUDevice device,
const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor) { const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor) {
@ -97,10 +101,19 @@ namespace dawn_native { namespace d3d12 {
internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
} }
Ref<D3D11on12ResourceCacheEntry> 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->CreateExternalTexture( Ref<TextureBase> texture = backendDevice->CreateExternalTexture(
&textureDescriptor, mD3D12Resource, ExternalMutexSerial(descriptor->acquireMutexKey), &textureDescriptor, mD3D12Resource, std::move(d3d11on12Resource),
ExternalMutexSerial(descriptor->acquireMutexKey),
ExternalMutexSerial(descriptor->releaseMutexKey), descriptor->isSwapChainTexture, ExternalMutexSerial(descriptor->releaseMutexKey), descriptor->isSwapChainTexture,
descriptor->isInitialized); descriptor->isInitialized);
return reinterpret_cast<WGPUTexture>(texture.Detach()); return reinterpret_cast<WGPUTexture>(texture.Detach());
} }

View File

@ -23,6 +23,7 @@
#include "dawn_native/d3d12/CommandAllocatorManager.h" #include "dawn_native/d3d12/CommandAllocatorManager.h"
#include "dawn_native/d3d12/CommandBufferD3D12.h" #include "dawn_native/d3d12/CommandBufferD3D12.h"
#include "dawn_native/d3d12/ComputePipelineD3D12.h" #include "dawn_native/d3d12/ComputePipelineD3D12.h"
#include "dawn_native/d3d12/D3D11on12Util.h"
#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/D3D12Error.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"
@ -458,90 +459,43 @@ namespace dawn_native { namespace d3d12 {
initialUsage); initialUsage);
} }
Ref<TextureBase> Device::CreateExternalTexture(const TextureDescriptor* descriptor, Ref<TextureBase> Device::CreateExternalTexture(
const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture, bool isSwapChainTexture,
bool isInitialized) { bool isInitialized) {
Ref<Texture> dawnTexture; Ref<Texture> dawnTexture;
if (ConsumedError(Texture::CreateExternalImage(this, descriptor, std::move(d3d12Texture), if (ConsumedError(
acquireMutexKey, releaseMutexKey, Texture::CreateExternalImage(this, descriptor, std::move(d3d12Texture),
isSwapChainTexture, isInitialized), std::move(d3d11on12Resource), acquireMutexKey,
releaseMutexKey, isSwapChainTexture, isInitialized),
&dawnTexture)) { &dawnTexture)) {
return nullptr; return nullptr;
} }
return {dawnTexture}; return {dawnTexture};
} }
// We use IDXGIKeyedMutexes to synchronize access between D3D11 and D3D12. D3D11/12 fences ComPtr<ID3D11On12Device> Device::GetOrCreateD3D11on12Device() {
// are a viable alternative but are, unfortunately, not available on all versions of Windows
// 10. Since D3D12 does not directly support keyed mutexes, we need to wrap the D3D12
// resource using 11on12 and QueryInterface the D3D11 representation for the keyed mutex.
ResultOrError<ComPtr<IDXGIKeyedMutex>> Device::CreateKeyedMutexForTexture(
ID3D12Resource* d3d12Resource) {
if (mD3d11On12Device == nullptr) { if (mD3d11On12Device == nullptr) {
ComPtr<ID3D11Device> d3d11Device; ComPtr<ID3D11Device> d3d11Device;
ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
D3D_FEATURE_LEVEL d3dFeatureLevel; D3D_FEATURE_LEVEL d3dFeatureLevel;
IUnknown* const iUnknownQueue = mCommandQueue.Get(); IUnknown* const iUnknownQueue = mCommandQueue.Get();
DAWN_TRY(CheckHRESULT(GetFunctions()->d3d11on12CreateDevice( if (FAILED(GetFunctions()->d3d11on12CreateDevice(mD3d12Device.Get(), 0, nullptr, 0,
mD3d12Device.Get(), 0, nullptr, 0, &iUnknownQueue, 1, 1, &iUnknownQueue, 1, 1, &d3d11Device,
&d3d11Device, &d3d11DeviceContext, &d3dFeatureLevel), nullptr, &d3dFeatureLevel))) {
"D3D12 11on12 device create")); return nullptr;
}
ComPtr<ID3D11On12Device> d3d11on12Device; ComPtr<ID3D11On12Device> d3d11on12Device;
DAWN_TRY(CheckHRESULT(d3d11Device.As(&d3d11on12Device), HRESULT hr = d3d11Device.As(&d3d11on12Device);
"D3D12 QueryInterface ID3D11Device to ID3D11On12Device")); ASSERT(SUCCEEDED(hr));
ComPtr<ID3D11DeviceContext2> d3d11DeviceContext2;
DAWN_TRY(
CheckHRESULT(d3d11DeviceContext.As(&d3d11DeviceContext2),
"D3D12 QueryInterface ID3D11DeviceContext to ID3D11DeviceContext2"));
mD3d11On12DeviceContext = std::move(d3d11DeviceContext2);
mD3d11On12Device = std::move(d3d11on12Device); mD3d11On12Device = std::move(d3d11on12Device);
} }
return mD3d11On12Device;
ComPtr<ID3D11Texture2D> d3d11Texture;
D3D11_RESOURCE_FLAGS resourceFlags;
resourceFlags.BindFlags = 0;
resourceFlags.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
resourceFlags.CPUAccessFlags = 0;
resourceFlags.StructureByteStride = 0;
DAWN_TRY(CheckHRESULT(mD3d11On12Device->CreateWrappedResource(
d3d12Resource, &resourceFlags, D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_COMMON, IID_PPV_ARGS(&d3d11Texture)),
"D3D12 creating a wrapped resource"));
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
DAWN_TRY(CheckHRESULT(d3d11Texture.As(&dxgiKeyedMutex),
"D3D12 QueryInterface ID3D11Texture2D to IDXGIKeyedMutex"));
return std::move(dxgiKeyedMutex);
}
void Device::ReleaseKeyedMutexForTexture(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex) {
ComPtr<ID3D11Resource> d3d11Resource;
HRESULT hr = dxgiKeyedMutex.As(&d3d11Resource);
if (FAILED(hr)) {
return;
}
ID3D11Resource* d3d11ResourceRaw = d3d11Resource.Get();
mD3d11On12Device->ReleaseWrappedResources(&d3d11ResourceRaw, 1);
d3d11Resource.Reset();
dxgiKeyedMutex.Reset();
// 11on12 has a bug where D3D12 resources used only for keyed shared mutexes
// are not released until work is submitted to the device context and flushed.
// The most minimal work we can get away with is issuing a TiledResourceBarrier.
// ID3D11DeviceContext2 is available in Win8.1 and above. This suffices for a
// D3D12 backend since both D3D12 and 11on12 first appeared in Windows 10.
mD3d11On12DeviceContext->TiledResourceBarrier(nullptr, nullptr);
mD3d11On12DeviceContext->Flush();
} }
const D3D12DeviceInfo& Device::GetDeviceInfo() const { const D3D12DeviceInfo& Device::GetDeviceInfo() const {

View File

@ -124,13 +124,13 @@ namespace dawn_native { namespace d3d12 {
Ref<TextureBase> CreateExternalTexture(const TextureDescriptor* descriptor, Ref<TextureBase> CreateExternalTexture(const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture, bool isSwapChainTexture,
bool isInitialized); bool isInitialized);
ResultOrError<ComPtr<IDXGIKeyedMutex>> CreateKeyedMutexForTexture(
ID3D12Resource* d3d12Resource); ComPtr<ID3D11On12Device> GetOrCreateD3D11on12Device();
void ReleaseKeyedMutexForTexture(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex);
void InitTogglesFromDriver(); void InitTogglesFromDriver();
@ -192,9 +192,8 @@ namespace dawn_native { namespace d3d12 {
ComPtr<ID3D12CommandQueue> mCommandQueue; ComPtr<ID3D12CommandQueue> mCommandQueue;
ComPtr<ID3D12SharingContract> mD3d12SharingContract; ComPtr<ID3D12SharingContract> mD3d12SharingContract;
// 11on12 device and device context corresponding to mCommandQueue // 11on12 device corresponding to mCommandQueue
ComPtr<ID3D11On12Device> mD3d11On12Device; ComPtr<ID3D11On12Device> mD3d11On12Device;
ComPtr<ID3D11DeviceContext2> mD3d11On12DeviceContext;
ComPtr<ID3D12CommandSignature> mDispatchIndirectSignature; ComPtr<ID3D12CommandSignature> mDispatchIndirectSignature;
ComPtr<ID3D12CommandSignature> mDrawIndirectSignature; ComPtr<ID3D12CommandSignature> mDrawIndirectSignature;

View File

@ -21,6 +21,7 @@
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#include "dawn_native/d3d12/BufferD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h"
#include "dawn_native/d3d12/CommandRecordingContext.h" #include "dawn_native/d3d12/CommandRecordingContext.h"
#include "dawn_native/d3d12/D3D11on12Util.h"
#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/HeapD3D12.h" #include "dawn_native/d3d12/HeapD3D12.h"
@ -418,18 +419,20 @@ namespace dawn_native { namespace d3d12 {
} }
// static // static
ResultOrError<Ref<Texture>> Texture::CreateExternalImage(Device* device, ResultOrError<Ref<Texture>> Texture::CreateExternalImage(
Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture, bool isSwapChainTexture,
bool isInitialized) { bool isInitialized) {
Ref<Texture> dawnTexture = Ref<Texture> dawnTexture =
AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal)); AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal));
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(descriptor, std::move(d3d12Texture), DAWN_TRY(dawnTexture->InitializeAsExternalTexture(
acquireMutexKey, releaseMutexKey, descriptor, std::move(d3d12Texture), std::move(d3d11on12Resource), acquireMutexKey,
isSwapChainTexture)); releaseMutexKey, isSwapChainTexture));
// Importing a multi-planar format must be initialized. This is required because // Importing a multi-planar format must be initialized. This is required because
// a shared multi-planar format cannot be initialized by Dawn. // a shared multi-planar format cannot be initialized by Dawn.
@ -453,22 +456,20 @@ namespace dawn_native { namespace d3d12 {
return std::move(dawnTexture); return std::move(dawnTexture);
} }
MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError Texture::InitializeAsExternalTexture(
const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture) { bool isSwapChainTexture) {
Device* dawnDevice = ToBackend(GetDevice()); DAWN_TRY(CheckHRESULT(d3d11on12Resource->GetDXGIKeyedMutex()->AcquireSync(
uint64_t(acquireMutexKey), INFINITE),
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
DAWN_TRY_ASSIGN(dxgiKeyedMutex, dawnDevice->CreateKeyedMutexForTexture(d3d12Texture.Get()));
DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(uint64_t(acquireMutexKey), INFINITE),
"D3D12 acquiring shared mutex")); "D3D12 acquiring shared mutex"));
mAcquireMutexKey = acquireMutexKey; mAcquireMutexKey = acquireMutexKey;
mReleaseMutexKey = releaseMutexKey; mReleaseMutexKey = releaseMutexKey;
mDxgiKeyedMutex = std::move(dxgiKeyedMutex); mD3D11on12Resource = std::move(d3d11on12Resource);
mSwapChainTexture = isSwapChainTexture; mSwapChainTexture = isSwapChainTexture;
D3D12_RESOURCE_DESC desc = d3d12Texture->GetDesc(); D3D12_RESOURCE_DESC desc = d3d12Texture->GetDesc();
@ -583,9 +584,8 @@ namespace dawn_native { namespace d3d12 {
// ID3D12SharingContract::Present. // ID3D12SharingContract::Present.
mSwapChainTexture = false; mSwapChainTexture = false;
if (mDxgiKeyedMutex != nullptr) { if (mD3D11on12Resource != nullptr) {
mDxgiKeyedMutex->ReleaseSync(uint64_t(mReleaseMutexKey)); mD3D11on12Resource->GetDXGIKeyedMutex()->ReleaseSync(uint64_t(mReleaseMutexKey));
device->ReleaseKeyedMutexForTexture(std::move(mDxgiKeyedMutex));
} }
} }
@ -754,7 +754,7 @@ namespace dawn_native { namespace d3d12 {
// Textures with keyed mutexes can be written from other graphics queues. Hence, they // Textures with keyed mutexes can be written from other graphics queues. Hence, they
// must be acquired before command list submission to ensure work from the other queues // must be acquired before command list submission to ensure work from the other queues
// has finished. See Device::ExecuteCommandContext. // has finished. See Device::ExecuteCommandContext.
if (mDxgiKeyedMutex != nullptr) { if (mD3D11on12Resource != nullptr) {
commandContext->AddToSharedTextureList(this); commandContext->AddToSharedTextureList(this);
} }
} }

View File

@ -28,6 +28,7 @@ namespace dawn_native { namespace d3d12 {
class CommandRecordingContext; class CommandRecordingContext;
class Device; class Device;
class D3D11on12ResourceCacheEntry;
DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format); DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format);
MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource, MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
@ -39,9 +40,11 @@ namespace dawn_native { namespace d3d12 {
public: public:
static ResultOrError<Ref<Texture>> Create(Device* device, static ResultOrError<Ref<Texture>> Create(Device* device,
const TextureDescriptor* descriptor); const TextureDescriptor* descriptor);
static ResultOrError<Ref<Texture>> CreateExternalImage(Device* device, static ResultOrError<Ref<Texture>> CreateExternalImage(
Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture, bool isSwapChainTexture,
@ -89,6 +92,7 @@ namespace dawn_native { namespace d3d12 {
MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsInternalTexture();
MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
ExternalMutexSerial releaseMutexKey, ExternalMutexSerial releaseMutexKey,
bool isSwapChainTexture); bool isSwapChainTexture);
@ -127,7 +131,7 @@ namespace dawn_native { namespace d3d12 {
ExternalMutexSerial mAcquireMutexKey = ExternalMutexSerial(0); ExternalMutexSerial mAcquireMutexKey = ExternalMutexSerial(0);
ExternalMutexSerial mReleaseMutexKey = ExternalMutexSerial(0); ExternalMutexSerial mReleaseMutexKey = ExternalMutexSerial(0);
ComPtr<IDXGIKeyedMutex> mDxgiKeyedMutex; Ref<D3D11on12ResourceCacheEntry> mD3D11on12Resource;
}; };
class TextureView final : public TextureViewBase { class TextureView final : public TextureViewBase {

View File

@ -29,6 +29,9 @@ struct ID3D12Device;
struct ID3D12Resource; struct ID3D12Resource;
namespace dawn_native { namespace d3d12 { namespace dawn_native { namespace d3d12 {
class D3D11on12ResourceCache;
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,
HWND window); HWND window);
@ -62,6 +65,8 @@ namespace dawn_native { namespace d3d12 {
class DAWN_NATIVE_EXPORT ExternalImageDXGI { class DAWN_NATIVE_EXPORT ExternalImageDXGI {
public: public:
~ExternalImageDXGI();
// Note: SharedHandle must be a handle to a texture object. // 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,
@ -85,6 +90,8 @@ namespace dawn_native { namespace d3d12 {
WGPUTextureFormat mFormat; WGPUTextureFormat mFormat;
uint32_t mMipLevelCount; uint32_t mMipLevelCount;
uint32_t mSampleCount; uint32_t mSampleCount;
std::unique_ptr<D3D11on12ResourceCache> mD3D11on12ResourceCache;
}; };
struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase { struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase {

View File

@ -319,18 +319,21 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
} }
// Clear a texture on a given device // Clear a texture on a given device
void ClearImage(wgpu::Texture wrappedTexture, const wgpu::Color& clearColor) { void ClearImage(wgpu::Texture wrappedTexture,
const wgpu::Color& clearColor,
wgpu::Device wgpuDevice) {
wgpu::TextureView wrappedView = wrappedTexture.CreateView(); wgpu::TextureView wrappedView = wrappedTexture.CreateView();
// Submit a clear operation // Submit a clear operation
utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::CommandEncoder encoder = wgpuDevice.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
pass.EndPass(); pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish(); wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = wgpuDevice.GetQueue();
queue.Submit(1, &commands); queue.Submit(1, &commands);
} }
@ -517,7 +520,7 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) {
ASSERT_NE(dawnTexture.Get(), nullptr); ASSERT_NE(dawnTexture.Get(), nullptr);
const wgpu::Color d3d12ClearColor{0.0f, 0.0f, 1.0f, 1.0f}; const wgpu::Color d3d12ClearColor{0.0f, 0.0f, 1.0f, 1.0f};
ClearImage(dawnTexture, d3d12ClearColor); ClearImage(dawnTexture, d3d12ClearColor, device);
dawnTexture.Destroy(); dawnTexture.Destroy();
@ -547,10 +550,10 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) {
ASSERT_NE(dawnTexture.Get(), nullptr); ASSERT_NE(dawnTexture.Get(), nullptr);
const wgpu::Color d3d12ClearColor1{0.0f, 0.0f, 1.0f, 1.0f}; const wgpu::Color d3d12ClearColor1{0.0f, 0.0f, 1.0f, 1.0f};
ClearImage(dawnTexture, d3d12ClearColor1); ClearImage(dawnTexture, d3d12ClearColor1, device);
const wgpu::Color d3d12ClearColor2{0.0f, 1.0f, 1.0f, 1.0f}; const wgpu::Color d3d12ClearColor2{0.0f, 1.0f, 1.0f, 1.0f};
ClearImage(dawnTexture, d3d12ClearColor2); ClearImage(dawnTexture, d3d12ClearColor2, device);
dawnTexture.Destroy(); dawnTexture.Destroy();
@ -594,7 +597,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
{ {
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); ClearImage(texture.Get(), solidRed, device);
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);
} }
@ -620,7 +623,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
{ {
const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f}; const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f};
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
ClearImage(texture.Get(), solidBlue); ClearImage(texture.Get(), solidBlue, device);
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0);
} }
@ -653,5 +656,54 @@ TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) {
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
} }
// Verify two Dawn devices can reuse the same external image.
TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
wgpu::Texture texture;
ComPtr<ID3D11Texture2D> d3d11Texture;
std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage;
// Create the Dawn texture then clear it to red using the first (default) device.
WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage);
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);
// 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::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.acquireMutexKey = 1;
externalAccessDesc.releaseMutexKey = 2;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
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.acquireMutexKey = 2;
externalAccessDesc.isInitialized = true;
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(D3D12SharedHandleValidation, D3D12Backend()); DAWN_INSTANTIATE_TEST(D3D12SharedHandleValidation, D3D12Backend());
DAWN_INSTANTIATE_TEST(D3D12SharedHandleUsageTests, D3D12Backend()); DAWN_INSTANTIATE_TEST(D3D12SharedHandleUsageTests, D3D12Backend());