| // 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 "dawn/common/HashUtils.h" |
| #include "dawn/common/Log.h" |
| #include "dawn/native/d3d12/D3D12Error.h" |
| #include "dawn/native/d3d12/DeviceD3D12.h" |
| |
| #include <dawn/native/D3D12Backend.h> |
| |
| namespace dawn::native::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; |
| } |
| |
| if (mAcquireCount > 0) { |
| mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); |
| } |
| |
| 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)); |
| } |
| |
| MaybeError D3D11on12ResourceCacheEntry::AcquireKeyedMutex() { |
| ASSERT(mDXGIKeyedMutex != nullptr); |
| ASSERT(mAcquireCount >= 0); |
| if (mAcquireCount == 0) { |
| DAWN_TRY(CheckHRESULT( |
| mDXGIKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE), |
| "D3D12 acquiring shared mutex")); |
| } |
| mAcquireCount++; |
| return {}; |
| } |
| |
| void D3D11on12ResourceCacheEntry::ReleaseKeyedMutex() { |
| ASSERT(mDXGIKeyedMutex != nullptr); |
| ASSERT(mAcquireCount > 0); |
| mAcquireCount--; |
| if (mAcquireCount == 0) { |
| mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); |
| } |
| } |
| |
| 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 |