d3d12: Add support for fences for external images

DXGI external images can now be imported with both fence and texture
shared handles. Fence wait and signal values can be specified for
ProduceTexture. Keyed mutex functionality is kept as is with no change.
The D3D12 resource wrapping tests now run in both keyed mutex and fence
modes.

Bug: dawn:576
Change-Id: Ic793bcc828e5a8850c1367ecffabedd1c67184d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/78604
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
This commit is contained in:
Sunny Sachanandani 2022-06-10 18:47:05 +00:00 committed by Dawn LUCI CQ
parent faf98b1bbc
commit 7ce85091b9
9 changed files with 347 additions and 139 deletions

View File

@ -52,20 +52,43 @@ struct DAWN_NATIVE_EXPORT ExternalImageDescriptorDXGISharedHandle : ExternalImag
ExternalImageDescriptorDXGISharedHandle(); ExternalImageDescriptorDXGISharedHandle();
// Note: SharedHandle must be a handle to a texture object. // Note: SharedHandle must be a handle to a texture object.
HANDLE sharedHandle; // TODO(dawn:576): Remove after changing Chromium code to set textureSharedHandle.
HANDLE sharedHandle = nullptr;
HANDLE textureSharedHandle = nullptr;
// Optional shared handle to a D3D11/12 fence which can be used to synchronize using wait/signal
// values specified in the access descriptor below. If null, the texture will be assumed to have
// an associated DXGI keyed mutex which will be used with a fixed key of 0 for synchronization.
HANDLE fenceSharedHandle = nullptr;
}; };
// Keyed mutex acquire/release uses a fixed key of 0 to match Chromium behavior. // Keyed mutex acquire/release uses a fixed key of 0 to match Chromium behavior.
constexpr UINT64 kDXGIKeyedMutexAcquireReleaseKey = 0; constexpr UINT64 kDXGIKeyedMutexAcquireReleaseKey = 0;
struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGIKeyedMutex struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGISharedHandle
: ExternalImageAccessDescriptor { : ExternalImageAccessDescriptor {
public:
// Value used for fence wait. A value of 0 is valid, but essentially a no-op since the fence
// lifetime starts with the 0 value signaled. A value of UINT64_MAX is ignored since it's also
// used by the D3D runtime to indicate that the device was removed.
uint64_t fenceWaitValue = 0;
// Value to signal the fence with after the texture is destroyed. A value of 0 means the fence
// will not be signaled.
uint64_t fenceSignalValue = 0;
// Whether the texture is for a WebGPU swap chain.
bool isSwapChainTexture = false;
};
// TODO(dawn:576): Remove after changing Chromium code to use the new struct name.
struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGIKeyedMutex
: ExternalImageAccessDescriptorDXGISharedHandle {
public: public:
// TODO(chromium:1241533): Remove deprecated keyed mutex params after removing associated // TODO(chromium:1241533): Remove deprecated keyed mutex params after removing associated
// code from Chromium - we use a fixed key of 0 for acquire and release everywhere now. // code from Chromium - we use a fixed key of 0 for acquire and release everywhere now.
uint64_t acquireMutexKey; uint64_t acquireMutexKey;
uint64_t releaseMutexKey; uint64_t releaseMutexKey;
bool isSwapChainTexture = false;
}; };
class DAWN_NATIVE_EXPORT ExternalImageDXGI { class DAWN_NATIVE_EXPORT ExternalImageDXGI {
@ -78,13 +101,15 @@ class DAWN_NATIVE_EXPORT ExternalImageDXGI {
const ExternalImageDescriptorDXGISharedHandle* descriptor); const ExternalImageDescriptorDXGISharedHandle* descriptor);
WGPUTexture ProduceTexture(WGPUDevice device, WGPUTexture ProduceTexture(WGPUDevice device,
const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor); const ExternalImageAccessDescriptorDXGISharedHandle* descriptor);
private: private:
ExternalImageDXGI(Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource, ExternalImageDXGI(Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource,
Microsoft::WRL::ComPtr<ID3D12Fence> d3d12Fence,
const WGPUTextureDescriptor* descriptor); const WGPUTextureDescriptor* descriptor);
Microsoft::WRL::ComPtr<ID3D12Resource> mD3D12Resource; Microsoft::WRL::ComPtr<ID3D12Resource> mD3D12Resource;
Microsoft::WRL::ComPtr<ID3D12Fence> mD3D12Fence;
// Contents of WGPUTextureDescriptor are stored individually since the descriptor // Contents of WGPUTextureDescriptor are stored individually since the descriptor
// could outlive this image. // could outlive this image.

View File

@ -237,13 +237,13 @@ struct DAWN_NATIVE_EXPORT ExternalImageDescriptor {
struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptor { struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptor {
public: public:
bool isInitialized; // Whether the texture is initialized on import bool isInitialized = false; // Whether the texture is initialized on import
WGPUTextureUsageFlags usage; WGPUTextureUsageFlags usage = WGPUTextureUsage_None;
}; };
struct DAWN_NATIVE_EXPORT ExternalImageExportInfo { struct DAWN_NATIVE_EXPORT ExternalImageExportInfo {
public: public:
bool isInitialized; // Whether the texture is initialized after export bool isInitialized = false; // Whether the texture is initialized after export
ExternalImageType GetType() const; ExternalImageType GetType() const;
protected: protected:

View File

@ -71,7 +71,7 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) {
// common state right before command list submission. TransitionUsageNow itself ensures // common state right before command list submission. TransitionUsageNow itself ensures
// no unnecessary transitions happen if the resources is already in the common state. // no unnecessary transitions happen if the resources is already in the common state.
for (Texture* texture : mSharedTextures) { for (Texture* texture : mSharedTextures) {
DAWN_TRY(texture->AcquireKeyedMutex()); DAWN_TRY(texture->SynchronizeImportedTextureBeforeUse());
texture->TrackAllUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON); texture->TrackAllUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON);
} }
@ -124,7 +124,7 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) {
device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList);
for (Texture* texture : mSharedTextures) { for (Texture* texture : mSharedTextures) {
texture->ReleaseKeyedMutex(); texture->SynchronizeImportedTextureAfterUse();
} }
mIsOpen = false; mIsOpen = false;

View File

@ -54,8 +54,10 @@ ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle
: ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {} : ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {}
ExternalImageDXGI::ExternalImageDXGI(ComPtr<ID3D12Resource> d3d12Resource, ExternalImageDXGI::ExternalImageDXGI(ComPtr<ID3D12Resource> d3d12Resource,
ComPtr<ID3D12Fence> d3d12Fence,
const WGPUTextureDescriptor* descriptor) const WGPUTextureDescriptor* descriptor)
: mD3D12Resource(std::move(d3d12Resource)), : mD3D12Resource(std::move(d3d12Resource)),
mD3D12Fence(std::move(d3d12Fence)),
mUsage(descriptor->usage), mUsage(descriptor->usage),
mDimension(descriptor->dimension), mDimension(descriptor->dimension),
mSize(descriptor->size), mSize(descriptor->size),
@ -76,7 +78,7 @@ ExternalImageDXGI::~ExternalImageDXGI() = default;
WGPUTexture ExternalImageDXGI::ProduceTexture( WGPUTexture ExternalImageDXGI::ProduceTexture(
WGPUDevice device, WGPUDevice device,
const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor) { const ExternalImageAccessDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = ToBackend(FromAPI(device)); Device* backendDevice = ToBackend(FromAPI(device));
// Ensure the texture usage is allowed // Ensure the texture usage is allowed
@ -100,16 +102,20 @@ WGPUTexture ExternalImageDXGI::ProduceTexture(
internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor; internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
} }
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource = Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource;
mD3D11on12ResourceCache->GetOrCreateD3D11on12Resource(device, mD3D12Resource.Get()); if (!mD3D12Fence) {
if (d3d11on12Resource == nullptr) { d3d11on12Resource =
dawn::ErrorLog() << "Unable to create 11on12 resource for external image"; mD3D11on12ResourceCache->GetOrCreateD3D11on12Resource(device, mD3D12Resource.Get());
return nullptr; if (d3d11on12Resource == nullptr) {
dawn::ErrorLog() << "Unable to create 11on12 resource for external image";
return nullptr;
}
} }
Ref<TextureBase> texture = backendDevice->CreateD3D12ExternalTexture( Ref<TextureBase> texture = backendDevice->CreateD3D12ExternalTexture(
&textureDescriptor, mD3D12Resource, std::move(d3d11on12Resource), &textureDescriptor, mD3D12Resource, mD3D12Fence, std::move(d3d11on12Resource),
descriptor->isSwapChainTexture, descriptor->isInitialized); descriptor->fenceWaitValue, descriptor->fenceSignalValue, descriptor->isSwapChainTexture,
descriptor->isInitialized);
return ToAPI(texture.Detach()); return ToAPI(texture.Detach());
} }
@ -120,12 +126,25 @@ std::unique_ptr<ExternalImageDXGI> ExternalImageDXGI::Create(
const ExternalImageDescriptorDXGISharedHandle* descriptor) { const ExternalImageDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = ToBackend(FromAPI(device)); 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<ID3D12Resource> d3d12Resource; Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource;
if (FAILED(backendDevice->GetD3D12Device()->OpenSharedHandle(descriptor->sharedHandle, if (FAILED(backendDevice->GetD3D12Device()->OpenSharedHandle(textureSharedHandle,
IID_PPV_ARGS(&d3d12Resource)))) { IID_PPV_ARGS(&d3d12Resource)))) {
return nullptr; return nullptr;
} }
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); const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor);
if (backendDevice->ConsumedError(ValidateTextureDescriptor(backendDevice, textureDescriptor))) { if (backendDevice->ConsumedError(ValidateTextureDescriptor(backendDevice, textureDescriptor))) {
@ -154,8 +173,8 @@ std::unique_ptr<ExternalImageDXGI> ExternalImageDXGI::Create(
} }
} }
std::unique_ptr<ExternalImageDXGI> result( std::unique_ptr<ExternalImageDXGI> result(new ExternalImageDXGI(
new ExternalImageDXGI(std::move(d3d12Resource), descriptor->cTextureDescriptor)); std::move(d3d12Resource), std::move(d3d12Fence), descriptor->cTextureDescriptor));
return result; return result;
} }

View File

@ -535,13 +535,17 @@ ResultOrError<ResourceHeapAllocation> Device::AllocateMemory(
Ref<TextureBase> Device::CreateD3D12ExternalTexture( Ref<TextureBase> Device::CreateD3D12ExternalTexture(
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
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(Texture::CreateExternalImage(
std::move(d3d11on12Resource), isSwapChainTexture, this, descriptor, std::move(d3d12Texture), std::move(d3d12Fence),
isInitialized), std::move(d3d11on12Resource), fenceWaitValue, fenceSignalValue,
isSwapChainTexture, isInitialized),
&dawnTexture)) { &dawnTexture)) {
return nullptr; return nullptr;
} }

View File

@ -130,7 +130,10 @@ class Device final : public DeviceBase {
Ref<TextureBase> CreateD3D12ExternalTexture(const TextureDescriptor* descriptor, Ref<TextureBase> CreateD3D12ExternalTexture(const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
bool isSwapChainTexture, bool isSwapChainTexture,
bool isInitialized); bool isInitialized);

View File

@ -511,13 +511,17 @@ ResultOrError<Ref<Texture>> Texture::CreateExternalImage(
Device* device, Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
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( DAWN_TRY(dawnTexture->InitializeAsExternalTexture(
descriptor, std::move(d3d12Texture), std::move(d3d11on12Resource), isSwapChainTexture)); std::move(d3d12Texture), std::move(d3d12Fence), std::move(d3d11on12Resource),
fenceWaitValue, fenceSignalValue, 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.
@ -541,13 +545,12 @@ ResultOrError<Ref<Texture>> Texture::Create(Device* device,
return std::move(dawnTexture); return std::move(dawnTexture);
} }
MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError Texture::InitializeAsExternalTexture(ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
bool isSwapChainTexture) { bool isSwapChainTexture) {
mD3D11on12Resource = std::move(d3d11on12Resource);
mSwapChainTexture = isSwapChainTexture;
D3D12_RESOURCE_DESC desc = d3d12Texture->GetDesc(); D3D12_RESOURCE_DESC desc = d3d12Texture->GetDesc();
mD3D12ResourceFlags = desc.Flags; mD3D12ResourceFlags = desc.Flags;
@ -558,6 +561,12 @@ MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descrip
// memory management. // memory management.
mResourceAllocation = {info, 0, std::move(d3d12Texture), nullptr}; mResourceAllocation = {info, 0, std::move(d3d12Texture), nullptr};
mD3D12Fence = std::move(d3d12Fence);
mD3D11on12Resource = std::move(d3d11on12Resource);
mFenceWaitValue = fenceWaitValue;
mFenceSignalValue = fenceSignalValue;
mSwapChainTexture = isSwapChainTexture;
SetLabelHelper("Dawn_ExternalTexture"); SetLabelHelper("Dawn_ExternalTexture");
return {}; return {};
@ -657,9 +666,15 @@ void Texture::DestroyImpl() {
// ID3D12SharingContract::Present. // ID3D12SharingContract::Present.
mSwapChainTexture = false; mSwapChainTexture = false;
// Now that the texture has been destroyed. It should release the refptr // Signal the fence on destroy after all uses of the texture.
// of the d3d11on12 resource. if (mD3D12Fence != nullptr && mFenceSignalValue != 0) {
device->GetCommandQueue()->Signal(mD3D12Fence.Get(), mFenceSignalValue);
}
// Now that the texture has been destroyed. It should release the refptr of the d3d11on12
// resource and the fence.
mD3D11on12Resource = nullptr; mD3D11on12Resource = nullptr;
mD3D12Fence = nullptr;
} }
DXGI_FORMAT Texture::GetD3D12Format() const { DXGI_FORMAT Texture::GetD3D12Format() const {
@ -692,14 +707,27 @@ DXGI_FORMAT Texture::GetD3D12CopyableSubresourceFormat(Aspect aspect) const {
} }
} }
MaybeError Texture::AcquireKeyedMutex() { MaybeError Texture::SynchronizeImportedTextureBeforeUse() {
ASSERT(mD3D11on12Resource != nullptr); if (mD3D11on12Resource != nullptr) {
return mD3D11on12Resource->AcquireKeyedMutex(); DAWN_TRY(mD3D11on12Resource->AcquireKeyedMutex());
}
// Perform the wait only on the first call. We can use UINT64_MAX as a sentinel value since it's
// also used by the D3D runtime to indicate device removed according to:
// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12fence-getcompletedvalue#return-value
if (mD3D12Fence != nullptr && mFenceWaitValue != UINT64_MAX) {
DAWN_TRY(CheckHRESULT(
ToBackend(GetDevice())->GetCommandQueue()->Wait(mD3D12Fence.Get(), mFenceWaitValue),
"D3D12 fence wait"));
mFenceWaitValue = UINT64_MAX;
}
return {};
} }
void Texture::ReleaseKeyedMutex() { void Texture::SynchronizeImportedTextureAfterUse() {
ASSERT(mD3D11on12Resource != nullptr); if (mD3D11on12Resource != nullptr) {
mD3D11on12Resource->ReleaseKeyedMutex(); mD3D11on12Resource->ReleaseKeyedMutex();
}
// Defer signaling the fence until destroy after all uses of the fence.
} }
void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
@ -850,10 +878,10 @@ void Texture::TransitionSubresourceRange(std::vector<D3D12_RESOURCE_BARRIER>* ba
} }
void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) { void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) {
// Textures with keyed mutexes can be written from other graphics queues. Hence, they // Externally allocated textures can be written from other graphics queues. Hence, they must be
// must be acquired before command list submission to ensure work from the other queues // acquired before command list submission to ensure work from the other queues has finished.
// has finished. See Device::ExecuteCommandContext. // See CommandRecordingContext::ExecuteCommandList.
if (mD3D11on12Resource != nullptr) { if (mResourceAllocation.GetInfo().mMethod == AllocationMethod::kExternal) {
commandContext->AddToSharedTextureList(this); commandContext->AddToSharedTextureList(this);
} }
} }

View File

@ -45,7 +45,10 @@ class Texture final : public TextureBase {
Device* device, Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
bool isSwapChainTexture, bool isSwapChainTexture,
bool isInitialized); bool isInitialized);
static ResultOrError<Ref<Texture>> Create(Device* device, static ResultOrError<Ref<Texture>> Create(Device* device,
@ -70,8 +73,8 @@ class Texture final : public TextureBase {
void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
const SubresourceRange& range); const SubresourceRange& range);
MaybeError AcquireKeyedMutex(); MaybeError SynchronizeImportedTextureBeforeUse();
void ReleaseKeyedMutex(); void SynchronizeImportedTextureAfterUse();
void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext, void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext,
std::vector<D3D12_RESOURCE_BARRIER>* barrier, std::vector<D3D12_RESOURCE_BARRIER>* barrier,
@ -97,9 +100,11 @@ class Texture final : public TextureBase {
using TextureBase::TextureBase; using TextureBase::TextureBase;
MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsInternalTexture();
MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError InitializeAsExternalTexture(ComPtr<ID3D12Resource> d3d12Texture,
ComPtr<ID3D12Resource> d3d12Texture, ComPtr<ID3D12Fence> d3d12Fence,
Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource, Ref<D3D11on12ResourceCacheEntry> d3d11on12Resource,
uint64_t fenceWaitValue,
uint64_t fenceSignalValue,
bool isSwapChainTexture); bool isSwapChainTexture);
MaybeError InitializeAsSwapChainTexture(ComPtr<ID3D12Resource> d3d12Texture); MaybeError InitializeAsSwapChainTexture(ComPtr<ID3D12Resource> d3d12Texture);
@ -132,13 +137,17 @@ class Texture final : public TextureBase {
ExecutionSerial pendingCommandSerial) const; ExecutionSerial pendingCommandSerial) const;
void HandleTransitionSpecialCases(CommandRecordingContext* commandContext); void HandleTransitionSpecialCases(CommandRecordingContext* commandContext);
SubresourceStorage<StateAndDecay> mSubresourceStateAndDecay;
ResourceHeapAllocation mResourceAllocation;
bool mSwapChainTexture = false;
D3D12_RESOURCE_FLAGS mD3D12ResourceFlags; D3D12_RESOURCE_FLAGS mD3D12ResourceFlags;
ResourceHeapAllocation mResourceAllocation;
// TODO(dawn:1460): Encapsulate imported image fields e.g. std::unique_ptr<ExternalImportInfo>.
ComPtr<ID3D12Fence> mD3D12Fence;
Ref<D3D11on12ResourceCacheEntry> mD3D11on12Resource; Ref<D3D11on12ResourceCacheEntry> mD3D11on12Resource;
uint64_t mFenceWaitValue = 0;
uint64_t mFenceSignalValue = 0;
bool mSwapChainTexture = false;
SubresourceStorage<StateAndDecay> mSubresourceStateAndDecay;
}; };
class TextureView final : public TextureViewBase { class TextureView final : public TextureViewBase {

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include <d3d11.h> #include <d3d11.h>
#include <d3d11_4.h>
#include <d3d12.h> #include <d3d12.h>
#include <dxgi1_4.h> #include <dxgi1_4.h>
#include <wrl/client.h> #include <wrl/client.h>
@ -30,9 +31,28 @@ using Microsoft::WRL::ComPtr;
namespace { namespace {
enum class SyncMode {
kKeyedMutex,
kFence,
};
std::ostream& operator<<(std::ostream& o, const SyncMode& m) {
switch (m) {
case SyncMode::kKeyedMutex:
o << "KeyedMutex";
break;
case SyncMode::kFence:
o << "Fence";
break;
}
return o;
}
DAWN_TEST_PARAM_STRUCT(D3D12ResourceTestParams, SyncMode);
using dawn::native::d3d12::kDXGIKeyedMutexAcquireReleaseKey; using dawn::native::d3d12::kDXGIKeyedMutexAcquireReleaseKey;
class D3D12ResourceTestBase : public DawnTest { class D3D12ResourceTestBase : public DawnTestWithParams<D3D12ResourceTestParams> {
protected: protected:
std::vector<wgpu::FeatureName> GetRequiredFeatures() override { std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
return {wgpu::FeatureName::DawnInternalUsages}; return {wgpu::FeatureName::DawnInternalUsages};
@ -40,7 +60,7 @@ class D3D12ResourceTestBase : public DawnTest {
public: public:
void SetUp() override { void SetUp() override {
DawnTest::SetUp(); DawnTestWithParams<D3D12ResourceTestParams>::SetUp();
if (UsesWire()) { if (UsesWire()) {
return; return;
} }
@ -88,8 +108,12 @@ class D3D12ResourceTestBase : public DawnTest {
baseD3dDescriptor.Usage = D3D11_USAGE_DEFAULT; baseD3dDescriptor.Usage = D3D11_USAGE_DEFAULT;
baseD3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; baseD3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
baseD3dDescriptor.CPUAccessFlags = 0; baseD3dDescriptor.CPUAccessFlags = 0;
baseD3dDescriptor.MiscFlags = baseD3dDescriptor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; if (GetParam().mSyncMode == SyncMode::kKeyedMutex) {
baseD3dDescriptor.MiscFlags |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
} else {
baseD3dDescriptor.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
}
} }
protected: protected:
@ -98,7 +122,8 @@ class D3D12ResourceTestBase : public DawnTest {
const D3D11_TEXTURE2D_DESC* baseD3dDescriptor, const D3D11_TEXTURE2D_DESC* baseD3dDescriptor,
wgpu::Texture* dawnTexture, wgpu::Texture* dawnTexture,
ID3D11Texture2D** d3d11TextureOut, ID3D11Texture2D** d3d11TextureOut,
std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI>* externalImageOut = nullptr) const { std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI>* externalImageOut = nullptr,
uint64_t fenceSignalValue = 1) const {
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
HRESULT hr = mD3d11Device->CreateTexture2D(baseD3dDescriptor, nullptr, &d3d11Texture); HRESULT hr = mD3d11Device->CreateTexture2D(baseD3dDescriptor, nullptr, &d3d11Texture);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
@ -107,31 +132,54 @@ class D3D12ResourceTestBase : public DawnTest {
hr = d3d11Texture.As(&dxgiResource); hr = d3d11Texture.As(&dxgiResource);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
HANDLE sharedHandle; HANDLE textureSharedHandle;
hr = dxgiResource->CreateSharedHandle( hr = dxgiResource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&sharedHandle); &textureSharedHandle);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
HANDLE fenceSharedHandle = nullptr;
ComPtr<ID3D11Fence> d3d11Fence;
if (GetParam().mSyncMode == SyncMode::kFence) {
ComPtr<ID3D11Device5> d3d11Device5;
hr = mD3d11Device.As(&d3d11Device5);
ASSERT_EQ(hr, S_OK);
hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence));
ASSERT_EQ(hr, S_OK);
hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle);
ASSERT_EQ(hr, S_OK);
}
dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc; dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc;
externalImageDesc.cTextureDescriptor = externalImageDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(dawnDesc); reinterpret_cast<const WGPUTextureDescriptor*>(dawnDesc);
externalImageDesc.sharedHandle = sharedHandle; externalImageDesc.textureSharedHandle = textureSharedHandle;
externalImageDesc.fenceSharedHandle = fenceSharedHandle;
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);
// Now that we've created all of our resources, we can close the handle // Now that we've created all of our resources, we can close the handle
// since we no longer need it. // since we no longer need it.
::CloseHandle(sharedHandle); ::CloseHandle(textureSharedHandle);
if (fenceSharedHandle != nullptr) {
::CloseHandle(fenceSharedHandle);
}
// Cannot access a non-existent external image (ex. validation error). // Cannot access a non-existent external image (ex. validation error).
if (externalImage == nullptr) { if (externalImage == nullptr) {
return; return;
} }
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc; dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDesc->usage); externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDesc->usage);
if (d3d11Fence != nullptr) {
externalAccessDesc.fenceWaitValue = 0;
externalAccessDesc.fenceSignalValue = fenceSignalValue;
}
*dawnTexture = wgpu::Texture::Acquire( *dawnTexture = wgpu::Texture::Acquire(
externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
@ -340,70 +388,113 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
queue.Submit(1, &commands); queue.Submit(1, &commands);
} }
void WrapAndClearD3D11Texture(const wgpu::TextureDescriptor* dawnDescriptor, void WrapAndClearD3D11Texture(const wgpu::TextureDescriptor& dawnDescriptor,
const D3D11_TEXTURE2D_DESC* d3dDescriptor, const D3D11_TEXTURE2D_DESC& d3dDescriptor,
wgpu::Texture* dawnTextureOut,
const wgpu::Color& clearColor, const wgpu::Color& clearColor,
wgpu::Texture* dawnTextureOut,
ID3D11Texture2D** d3d11TextureOut, ID3D11Texture2D** d3d11TextureOut,
IDXGIKeyedMutex** dxgiKeyedMutexOut, bool isInitialized = true,
bool isInitialized = true) const { IDXGIKeyedMutex** dxgiKeyedMutexOut = nullptr,
ID3D11Fence** d3d11FenceOut = nullptr,
uint64_t* nextFenceWaitValue = nullptr) const {
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture); HRESULT hr = mD3d11Device->CreateTexture2D(&d3dDescriptor, nullptr, &d3d11Texture);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
ComPtr<IDXGIResource1> dxgiResource; ComPtr<IDXGIResource1> dxgiResource;
hr = d3d11Texture.As(&dxgiResource); hr = d3d11Texture.As(&dxgiResource);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
HANDLE sharedHandle; HANDLE textureSharedHandle;
hr = dxgiResource->CreateSharedHandle( hr = dxgiResource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&sharedHandle); &textureSharedHandle);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
hr = d3d11Texture.As(&dxgiKeyedMutex);
ASSERT_EQ(hr, S_OK); HANDLE fenceSharedHandle = nullptr;
ComPtr<ID3D11Fence> d3d11Fence;
ComPtr<ID3D11DeviceContext4> d3d11DeviceContext4;
if (GetParam().mSyncMode == SyncMode::kKeyedMutex) {
hr = d3d11Texture.As(&dxgiKeyedMutex);
ASSERT_EQ(hr, S_OK);
hr = dxgiKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE);
ASSERT_EQ(hr, S_OK);
} else {
ComPtr<ID3D11Device5> d3d11Device5;
hr = mD3d11Device.As(&d3d11Device5);
ASSERT_EQ(hr, S_OK);
hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence));
ASSERT_EQ(hr, S_OK);
hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle);
ASSERT_EQ(hr, S_OK);
}
ComPtr<ID3D11RenderTargetView> d3d11RTV; ComPtr<ID3D11RenderTargetView> d3d11RTV;
hr = mD3d11Device->CreateRenderTargetView(d3d11Texture.Get(), nullptr, &d3d11RTV); hr = mD3d11Device->CreateRenderTargetView(d3d11Texture.Get(), nullptr, &d3d11RTV);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
hr = dxgiKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE);
ASSERT_EQ(hr, S_OK);
const float colorRGBA[] = { const float colorRGBA[] = {
static_cast<float>(clearColor.r), static_cast<float>(clearColor.g), static_cast<float>(clearColor.r), static_cast<float>(clearColor.g),
static_cast<float>(clearColor.b), static_cast<float>(clearColor.a)}; static_cast<float>(clearColor.b), static_cast<float>(clearColor.a)};
mD3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), colorRGBA); mD3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), colorRGBA);
hr = dxgiKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey);
ASSERT_EQ(hr, S_OK);
dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {}; dawn::native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {};
externalImageDesc.sharedHandle = sharedHandle; externalImageDesc.textureSharedHandle = textureSharedHandle;
externalImageDesc.fenceSharedHandle = fenceSharedHandle;
externalImageDesc.cTextureDescriptor = externalImageDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor); reinterpret_cast<const WGPUTextureDescriptor*>(&dawnDescriptor);
dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.isInitialized = isInitialized;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDescriptor.usage);
if (dxgiKeyedMutex) {
hr = dxgiKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey);
ASSERT_EQ(hr, S_OK);
} else {
hr = mD3d11DeviceContext.As(&d3d11DeviceContext4);
ASSERT_EQ(hr, S_OK);
// The fence starts with 0 signaled, but that won't capture the render target view clear
// above, so signal explicitly with 1 and make the next Dawn access wait on 1.
d3d11DeviceContext4->Signal(d3d11Fence.Get(), 1);
externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2;
}
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);
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.isInitialized = isInitialized;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDescriptor->usage);
*dawnTextureOut = wgpu::Texture::Acquire( *dawnTextureOut = wgpu::Texture::Acquire(
externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
*d3d11TextureOut = d3d11Texture.Detach(); *d3d11TextureOut = d3d11Texture.Detach();
*dxgiKeyedMutexOut = dxgiKeyedMutex.Detach();
if (dxgiKeyedMutexOut != nullptr) {
*dxgiKeyedMutexOut = dxgiKeyedMutex.Detach();
}
if (d3d11FenceOut != nullptr) {
*d3d11FenceOut = d3d11Fence.Detach();
}
if (nextFenceWaitValue != nullptr) {
*nextFenceWaitValue = externalAccessDesc.fenceSignalValue;
}
} }
void ExpectPixelRGBA8EQ(ID3D11Texture2D* d3d11Texture, void ExpectPixelRGBA8EQ(ID3D11Texture2D* d3d11Texture,
IDXGIKeyedMutex* dxgiKeyedMutex, IDXGIKeyedMutex* dxgiKeyedMutex,
ID3D11Fence* d3d11Fence,
uint64_t fenceWaitValue,
const wgpu::Color& color) { const wgpu::Color& color) {
HRESULT hr = dxgiKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE);
ASSERT_EQ(hr, S_OK);
D3D11_TEXTURE2D_DESC texture2DDesc; D3D11_TEXTURE2D_DESC texture2DDesc;
d3d11Texture->GetDesc(&texture2DDesc); d3d11Texture->GetDesc(&texture2DDesc);
@ -418,7 +509,8 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE); // CPUAccessFlags D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE); // CPUAccessFlags
ComPtr<ID3D11Texture2D> spD3DTextureStaging; ComPtr<ID3D11Texture2D> spD3DTextureStaging;
hr = mD3d11Device->CreateTexture2D(&texture2DStagingDesc, nullptr, &spD3DTextureStaging); HRESULT hr =
mD3d11Device->CreateTexture2D(&texture2DStagingDesc, nullptr, &spD3DTextureStaging);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
D3D11_BOX d3dRc; D3D11_BOX d3dRc;
@ -429,6 +521,19 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
d3dRc.bottom = texture2DDesc.Height; d3dRc.bottom = texture2DDesc.Height;
d3dRc.right = texture2DDesc.Width; d3dRc.right = texture2DDesc.Width;
if (dxgiKeyedMutex != nullptr) {
hr = dxgiKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireReleaseKey, INFINITE);
ASSERT_EQ(hr, S_OK);
}
if (d3d11Fence != nullptr) {
ComPtr<ID3D11DeviceContext4> d3d11DeviceContext4;
hr = mD3d11DeviceContext.As(&d3d11DeviceContext4);
ASSERT_EQ(hr, S_OK);
hr = d3d11DeviceContext4->Wait(d3d11Fence, fenceWaitValue);
ASSERT_EQ(hr, S_OK);
}
mD3d11DeviceContext->CopySubresourceRegion(spD3DTextureStaging.Get(), // pDstResource mD3d11DeviceContext->CopySubresourceRegion(spD3DTextureStaging.Get(), // pDstResource
0, // DstSubresource 0, // DstSubresource
0, // DstX 0, // DstX
@ -451,8 +556,10 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
mD3d11DeviceContext->Unmap(spD3DTextureStaging.Get(), 0); mD3d11DeviceContext->Unmap(spD3DTextureStaging.Get(), 0);
hr = dxgiKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey); if (dxgiKeyedMutex != nullptr) {
ASSERT_EQ(hr, S_OK); hr = dxgiKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireReleaseKey);
ASSERT_EQ(hr, S_OK);
}
} }
}; };
@ -465,9 +572,8 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11CopyAndReadbackInD3D12) {
const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f};
wgpu::Texture dawnSrcTexture; wgpu::Texture dawnSrcTexture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnSrcTexture,
WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnSrcTexture, clearColor, &d3d11Texture);
&d3d11Texture, &dxgiKeyedMutex);
ASSERT_NE(dawnSrcTexture.Get(), nullptr); ASSERT_NE(dawnSrcTexture.Get(), nullptr);
// Create a texture on the device and copy the source texture to it. // Create a texture on the device and copy the source texture to it.
@ -489,9 +595,8 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11ReadbackInD3D12) {
const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f};
wgpu::Texture dawnTexture; wgpu::Texture dawnTexture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnTexture,
WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor, &d3d11Texture);
&d3d11Texture, &dxgiKeyedMutex);
ASSERT_NE(dawnTexture.Get(), nullptr); ASSERT_NE(dawnTexture.Get(), nullptr);
// Readback the destination texture and ensure it contains the colors we used // Readback the destination texture and ensure it contains the colors we used
@ -515,8 +620,11 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) {
wgpu::Texture dawnTexture; wgpu::Texture dawnTexture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor, ComPtr<ID3D11Fence> d3d11Fence;
&d3d11Texture, &dxgiKeyedMutex); uint64_t nextFenceWaitValue;
WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, d3d11ClearColor, &dawnTexture,
&d3d11Texture, /*isInitialized=*/true, &dxgiKeyedMutex, &d3d11Fence,
&nextFenceWaitValue);
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};
@ -527,7 +635,8 @@ TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) {
// Now that Dawn (via D3D12) has finished writing to the texture, we should be // Now that Dawn (via D3D12) has finished writing to the texture, we should be
// able to read it back by copying it to a staging texture and verifying the // able to read it back by copying it to a staging texture and verifying the
// color matches the D3D12 clear color. // color matches the D3D12 clear color.
ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor); ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d11Fence.Get(),
nextFenceWaitValue, d3d12ClearColor);
} }
// 1. Create and clear a D3D11 texture // 1. Create and clear a D3D11 texture
@ -545,8 +654,11 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) {
wgpu::Texture dawnTexture; wgpu::Texture dawnTexture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor, ComPtr<ID3D11Fence> d3d11Fence;
&d3d11Texture, &dxgiKeyedMutex); uint64_t nextFenceWaitValue;
WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, d3d11ClearColor, &dawnTexture,
&d3d11Texture, /*isInitialized=*/true, &dxgiKeyedMutex, &d3d11Fence,
&nextFenceWaitValue);
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};
@ -560,7 +672,8 @@ TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) {
// Now that Dawn (via D3D12) has finished writing to the texture, we should be // Now that Dawn (via D3D12) has finished writing to the texture, we should be
// able to read it back by copying it to a staging texture and verifying the // able to read it back by copying it to a staging texture and verifying the
// color matches the last D3D12 clear color. // color matches the last D3D12 clear color.
ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor2); ExpectPixelRGBA8EQ(d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d11Fence.Get(),
nextFenceWaitValue, d3d12ClearColor2);
} }
// 1. Create and clear a D3D11 texture with clearColor // 1. Create and clear a D3D11 texture with clearColor
@ -572,9 +685,8 @@ TEST_P(D3D12SharedHandleUsageTests, UninitializedTextureIsCleared) {
const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f}; const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f};
wgpu::Texture dawnTexture; wgpu::Texture dawnTexture;
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; WrapAndClearD3D11Texture(baseDawnDescriptor, baseD3dDescriptor, clearColor, &dawnTexture,
WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor, &d3d11Texture, /*isInitialized=*/false);
&d3d11Texture, &dxgiKeyedMutex, false);
ASSERT_NE(dawnTexture.Get(), nullptr); ASSERT_NE(dawnTexture.Get(), nullptr);
// Readback the destination texture and ensure it contains the colors we used // Readback the destination texture and ensure it contains the colors we used
@ -593,7 +705,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> externalImage; std::unique_ptr<dawn::native::d3d12::ExternalImageDXGI> externalImage;
WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage); &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);
@ -607,9 +719,11 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
texture.Destroy(); texture.Destroy();
// Create another Dawn texture then clear it with another color. // Create another Dawn texture then clear it with another color.
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc; dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.isInitialized = true; externalAccessDesc.isInitialized = true;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage); externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2;
texture = texture =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
@ -625,49 +739,46 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
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);
} }
texture.Destroy();
} }
TEST_P(D3D12SharedHandleUsageTests, RecursiveExternalImageAccess) { TEST_P(D3D12SharedHandleUsageTests, ConcurrentExternalImageReadAccess) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire()); DAWN_TEST_UNSUPPORTED_IF(UsesWire());
// Create the first Dawn texture then clear it to red. // Create Dawn texture with write access, then clear it to red.
wgpu::Texture texture1; 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;
WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture1, &d3d11Texture, WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage); &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(texture1.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
ClearImage(texture1.Get(), solidRed, device); ClearImage(texture.Get(), solidRed, device);
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture1.Get(), 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
} }
// Create another Dawn texture then clear it with another color. texture.Destroy();
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.isInitialized = true; // Create two Dawn textures for concurrent read.
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage); dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.isInitialized = true;
externalAccessDesc.usage = WGPUTextureUsage_CopySrc;
externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2;
wgpu::Texture texture1 =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
// Acquire the ExternalImageDXGI again without destroying the original texture.
wgpu::Texture texture2 = wgpu::Texture texture2 =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
// Check again that the new texture is still 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), texture2.Get(), 0, 0); 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(); texture1.Destroy();
texture2.Destroy(); texture2.Destroy();
} }
@ -676,17 +787,18 @@ TEST_P(D3D12SharedHandleUsageTests, RecursiveExternalImageAccess) {
TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) { TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire()); DAWN_TEST_UNSUPPORTED_IF(UsesWire());
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.isInitialized = true;
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;
WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage); &externalImage, /*fenceSignalValue=*/1);
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.isInitialized = true;
externalAccessDesc.usage = WGPUTextureUsage_StorageBinding; externalAccessDesc.usage = WGPUTextureUsage_StorageBinding;
externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 0; // No need to signal
texture = texture =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
ASSERT_EQ(texture.Get(), nullptr); ASSERT_EQ(texture.Get(), nullptr);
@ -707,7 +819,7 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
// Create the Dawn texture then clear it to red using the first (default) device. // Create the Dawn texture then clear it to red using the first (default) device.
WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture, WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
&externalImage); &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);
@ -718,8 +830,10 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
texture.Destroy(); texture.Destroy();
// Create the Dawn texture then clear it to blue using the second device. // Create the Dawn texture then clear it to blue using the second device.
dawn::native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc; dawn::native::d3d12::ExternalImageAccessDescriptorDXGISharedHandle externalAccessDesc;
externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage); externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
externalAccessDesc.fenceWaitValue = 1;
externalAccessDesc.fenceSignalValue = 2;
wgpu::Device otherDevice = wgpu::Device::Acquire(GetAdapter().CreateDevice()); wgpu::Device otherDevice = wgpu::Device::Acquire(GetAdapter().CreateDevice());
@ -734,6 +848,8 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
// Re-create the Dawn texture using the first (default) device. // Re-create the Dawn texture using the first (default) device.
externalAccessDesc.isInitialized = true; externalAccessDesc.isInitialized = true;
externalAccessDesc.fenceWaitValue = 2;
externalAccessDesc.fenceSignalValue = 3;
texture = texture =
wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc)); wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
ASSERT_NE(texture.Get(), nullptr); ASSERT_NE(texture.Get(), nullptr);
@ -743,5 +859,9 @@ TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
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);
} }
DAWN_INSTANTIATE_TEST(D3D12SharedHandleValidation, D3D12Backend()); DAWN_INSTANTIATE_TEST_P(D3D12SharedHandleValidation,
DAWN_INSTANTIATE_TEST(D3D12SharedHandleUsageTests, D3D12Backend()); {D3D12Backend()},
{SyncMode::kKeyedMutex, SyncMode::kFence});
DAWN_INSTANTIATE_TEST_P(D3D12SharedHandleUsageTests,
{D3D12Backend()},
{SyncMode::kKeyedMutex, SyncMode::kFence});