diff --git a/src/dawn_native/d3d12/CommandRecordingContext.cpp b/src/dawn_native/d3d12/CommandRecordingContext.cpp index ce7c22a201..ad207640fa 100644 --- a/src/dawn_native/d3d12/CommandRecordingContext.cpp +++ b/src/dawn_native/d3d12/CommandRecordingContext.cpp @@ -61,6 +61,7 @@ namespace dawn_native::d3d12 { // common state right before command list submission. TransitionUsageNow itself ensures // no unnecessary transitions happen if the resources is already in the common state. for (Texture* texture : mSharedTextures) { + DAWN_TRY(texture->AcquireKeyedMutex()); texture->TrackAllUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON); } @@ -76,6 +77,10 @@ namespace dawn_native::d3d12 { ID3D12CommandList* d3d12CommandList = GetCommandList(); device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); + for (Texture* texture : mSharedTextures) { + texture->ReleaseKeyedMutex(); + } + mIsOpen = false; mSharedTextures.clear(); mHeapsPendingUsage.clear(); diff --git a/src/dawn_native/d3d12/D3D11on12Util.cpp b/src/dawn_native/d3d12/D3D11on12Util.cpp index 0c526a0f52..55b23932da 100644 --- a/src/dawn_native/d3d12/D3D11on12Util.cpp +++ b/src/dawn_native/d3d12/D3D11on12Util.cpp @@ -19,8 +19,11 @@ #include "common/HashUtils.h" #include "common/Log.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" +#include + namespace dawn_native::d3d12 { void Flush11On12DeviceToAvoidLeaks(ComPtr d3d11on12Device) { @@ -69,6 +72,10 @@ namespace dawn_native::d3d12 { return; } + if (mAcquireCount > 0) { + mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); + } + ComPtr d3d11Resource; if (FAILED(mDXGIKeyedMutex.As(&d3d11Resource))) { return; @@ -85,9 +92,25 @@ namespace dawn_native::d3d12 { Flush11On12DeviceToAvoidLeaks(std::move(mD3D11on12Device)); } - ComPtr D3D11on12ResourceCacheEntry::GetDXGIKeyedMutex() const { + MaybeError D3D11on12ResourceCacheEntry::AcquireKeyedMutex() { ASSERT(mDXGIKeyedMutex != nullptr); - return mDXGIKeyedMutex; + 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()( @@ -161,4 +184,4 @@ namespace dawn_native::d3d12 { return entry; } -} // namespace dawn_native::d3d12 \ No newline at end of file +} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/D3D11on12Util.h b/src/dawn_native/d3d12/D3D11on12Util.h index ab622f0d0c..9abf7c61dc 100644 --- a/src/dawn_native/d3d12/D3D11on12Util.h +++ b/src/dawn_native/d3d12/D3D11on12Util.h @@ -16,6 +16,7 @@ #define DAWNNATIVE_D3D11ON12UTIL_H_ #include "common/RefCounted.h" +#include "dawn_native/Error.h" #include "dawn_native/d3d12/d3d12_platform.h" #include @@ -35,7 +36,8 @@ namespace dawn_native::d3d12 { ComPtr d3d11on12Device); ~D3D11on12ResourceCacheEntry(); - ComPtr GetDXGIKeyedMutex() const; + MaybeError AcquireKeyedMutex(); + void ReleaseKeyedMutex(); // Functors necessary for the // unordered_set-based cache. @@ -51,6 +53,7 @@ namespace dawn_native::d3d12 { private: ComPtr mDXGIKeyedMutex; ComPtr mD3D11on12Device; + int64_t mAcquireCount = 0; }; // |D3D11on12ResourceCache| maintains a cache of 11 wrapped resources. diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index bf07cad82e..623ac02688 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -552,10 +552,6 @@ namespace dawn_native::d3d12 { ComPtr d3d12Texture, Ref d3d11on12Resource, bool isSwapChainTexture) { - DAWN_TRY(CheckHRESULT(d3d11on12Resource->GetDXGIKeyedMutex()->AcquireSync( - kDXGIKeyedMutexAcquireReleaseKey, INFINITE), - "D3D12 acquiring shared mutex")); - mD3D11on12Resource = std::move(d3d11on12Resource); mSwapChainTexture = isSwapChainTexture; @@ -672,10 +668,6 @@ namespace dawn_native::d3d12 { // We can set mSwapChainTexture to false to avoid passing a nullptr to // ID3D12SharingContract::Present. mSwapChainTexture = false; - - if (mD3D11on12Resource != nullptr) { - mD3D11on12Resource->GetDXGIKeyedMutex()->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); - } } DXGI_FORMAT Texture::GetD3D12Format() const { @@ -707,6 +699,16 @@ namespace dawn_native::d3d12 { } } + MaybeError Texture::AcquireKeyedMutex() { + ASSERT(mD3D11on12Resource != nullptr); + return mD3D11on12Resource->AcquireKeyedMutex(); + } + + void Texture::ReleaseKeyedMutex() { + ASSERT(mD3D11on12Resource != nullptr); + mD3D11on12Resource->ReleaseKeyedMutex(); + } + void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, wgpu::TextureUsage usage, const SubresourceRange& range) { diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index fc0894076f..9bc6cedda2 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -64,9 +64,13 @@ namespace dawn_native::d3d12 { Aspect aspects, bool depthReadOnly, bool stencilReadOnly) const; + void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, const SubresourceRange& range); + MaybeError AcquireKeyedMutex(); + void ReleaseKeyedMutex(); + void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext, std::vector* barrier, const TextureSubresourceUsage& textureUsages); diff --git a/src/tests/end2end/D3D12ResourceWrappingTests.cpp b/src/tests/end2end/D3D12ResourceWrappingTests.cpp index f12e98c3af..81a6b526b1 100644 --- a/src/tests/end2end/D3D12ResourceWrappingTests.cpp +++ b/src/tests/end2end/D3D12ResourceWrappingTests.cpp @@ -624,6 +624,51 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) { } } +TEST_P(D3D12SharedHandleUsageTests, RecursiveExternalImageAccess) { + DAWN_TEST_UNSUPPORTED_IF(UsesWire()); + + // Create the first Dawn texture then clear it to red. + wgpu::Texture texture1; + ComPtr d3d11Texture; + std::unique_ptr externalImage; + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture1, &d3d11Texture, + &externalImage); + { + const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f}; + ASSERT_NE(texture1.Get(), nullptr); + ClearImage(texture1.Get(), solidRed, device); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture1.Get(), 0, 0); + } + + // Create another Dawn texture then clear it with another color. + dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc; + externalAccessDesc.isInitialized = true; + externalAccessDesc.usage = static_cast(baseDawnDescriptor.usage); + + // Acquire the ExternalImageDXGI again without destroying the original texture. + wgpu::Texture texture2 = + wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); + + // Check again that the new texture is still red + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture2.Get(), 0, 0); + + // Clear the new texture to blue + { + const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f}; + ASSERT_NE(texture2.Get(), nullptr); + ClearImage(texture2.Get(), solidBlue, device); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture2.Get(), 0, 0); + } + + // Check that the original texture is also blue + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture1.Get(), 0, 0); + + texture1.Destroy(); + texture2.Destroy(); +} + // Produce a new texture with a usage not specified in the external image. TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) { DAWN_TEST_UNSUPPORTED_IF(UsesWire());