D3D12: Enable external texture reuse

This change allows multiple Dawn textures to be created from the same
D3D11 resource. This avoids re-opening the shared handle by caching the
D3D12 resource outside of the Dawn texture.

Re-opening the handle costs 5-10% of CPU cycles per frame, which far
exceeded syncronization costs.

In a future change, WrapSharedHandle will be depreciated.

BUG=dawn:625

Change-Id: If0d2dc9b7445ec3ae718bc5305164db88057c4ea
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/42140
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Bryan Bernhart 2021-02-26 22:44:48 +00:00 committed by Commit Bot service account
parent 4d9f2ca07d
commit 9c3aefa4bd
9 changed files with 278 additions and 90 deletions

View File

@ -51,6 +51,83 @@ namespace dawn_native { namespace d3d12 {
: ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) { : ExternalImageDescriptor(ExternalImageType::DXGISharedHandle) {
} }
ExternalImageDXGI::ExternalImageDXGI(ComPtr<ID3D12Resource> d3d12Resource,
const WGPUTextureDescriptor* descriptor)
: mD3D12Resource(std::move(d3d12Resource)),
mUsage(descriptor->usage),
mDimension(descriptor->dimension),
mSize(descriptor->size),
mFormat(descriptor->format),
mMipLevelCount(descriptor->mipLevelCount),
mSampleCount(descriptor->sampleCount) {
ASSERT(descriptor->nextInChain == nullptr);
}
WGPUTexture ExternalImageDXGI::ProduceTexture(
WGPUDevice device,
const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor) {
Device* backendDevice = reinterpret_cast<Device*>(device);
TextureDescriptor textureDescriptor = {};
textureDescriptor.usage = static_cast<wgpu::TextureUsage>(mUsage);
textureDescriptor.dimension = static_cast<wgpu::TextureDimension>(mDimension);
textureDescriptor.size = {mSize.width, mSize.height, mSize.depth};
textureDescriptor.format = static_cast<wgpu::TextureFormat>(mFormat);
textureDescriptor.mipLevelCount = mMipLevelCount;
textureDescriptor.sampleCount = mSampleCount;
Ref<TextureBase> texture = backendDevice->CreateExternalTexture(
&textureDescriptor, mD3D12Resource, ExternalMutexSerial(descriptor->acquireMutexKey),
descriptor->isSwapChainTexture, descriptor->isInitialized);
return reinterpret_cast<WGPUTexture>(texture.Detach());
}
// static
std::unique_ptr<ExternalImageDXGI> ExternalImageDXGI::Create(
WGPUDevice device,
const ExternalImageDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = reinterpret_cast<Device*>(device);
Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource;
if (FAILED(backendDevice->GetD3D12Device()->OpenSharedHandle(
descriptor->sharedHandle, IID_PPV_ARGS(&d3d12Resource)))) {
return nullptr;
}
const TextureDescriptor* textureDescriptor =
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
if (backendDevice->ConsumedError(
ValidateTextureDescriptor(backendDevice, textureDescriptor))) {
return nullptr;
}
if (backendDevice->ConsumedError(
ValidateTextureDescriptorCanBeWrapped(textureDescriptor))) {
return nullptr;
}
if (backendDevice->ConsumedError(
ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), textureDescriptor))) {
return nullptr;
}
// Shared handle is assumed to support resource sharing capability. The resource
// shared capability tier must agree to share resources between D3D devices.
const Format* format =
backendDevice->GetInternalFormat(textureDescriptor->format).AcquireSuccess();
if (format->IsMultiPlanar()) {
if (backendDevice->ConsumedError(ValidateD3D12VideoTextureCanBeShared(
backendDevice, D3D12TextureFormat(textureDescriptor->format)))) {
return nullptr;
}
}
std::unique_ptr<ExternalImageDXGI> result(
new ExternalImageDXGI(std::move(d3d12Resource), descriptor->cTextureDescriptor));
return result;
}
uint64_t SetExternalMemoryReservation(WGPUDevice device, uint64_t SetExternalMemoryReservation(WGPUDevice device,
uint64_t requestedReservationSize, uint64_t requestedReservationSize,
MemorySegment memorySegment) { MemorySegment memorySegment) {
@ -62,11 +139,18 @@ namespace dawn_native { namespace d3d12 {
WGPUTexture WrapSharedHandle(WGPUDevice device, WGPUTexture WrapSharedHandle(WGPUDevice device,
const ExternalImageDescriptorDXGISharedHandle* descriptor) { const ExternalImageDescriptorDXGISharedHandle* descriptor) {
Device* backendDevice = reinterpret_cast<Device*>(device); std::unique_ptr<ExternalImageDXGI> externalImage =
Ref<TextureBase> texture = backendDevice->WrapSharedHandle( ExternalImageDXGI::Create(device, descriptor);
descriptor, descriptor->sharedHandle, ExternalMutexSerial(descriptor->acquireMutexKey), if (externalImage == nullptr) {
descriptor->isSwapChainTexture); return nullptr;
return reinterpret_cast<WGPUTexture>(texture.Detach()); }
ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc = {};
externalAccessDesc.isInitialized = descriptor->isInitialized;
externalAccessDesc.isSwapChainTexture = descriptor->isSwapChainTexture;
externalAccessDesc.acquireMutexKey = descriptor->acquireMutexKey;
return externalImage->ProduceTexture(device, &externalAccessDesc);
} }
AdapterDiscoveryOptions::AdapterDiscoveryOptions(ComPtr<IDXGIAdapter> adapter) AdapterDiscoveryOptions::AdapterDiscoveryOptions(ComPtr<IDXGIAdapter> adapter)

View File

@ -436,14 +436,16 @@ namespace dawn_native { namespace d3d12 {
initialUsage); initialUsage);
} }
Ref<TextureBase> Device::WrapSharedHandle(const ExternalImageDescriptor* descriptor, Ref<TextureBase> Device::CreateExternalTexture(const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture) { bool isSwapChainTexture,
bool isInitialized) {
Ref<Texture> dawnTexture; Ref<Texture> dawnTexture;
if (ConsumedError(Texture::Create(this, descriptor, sharedHandle, acquireMutexKey, if (ConsumedError(
isSwapChainTexture), Texture::CreateExternalImage(this, descriptor, std::move(d3d12Texture),
&dawnTexture)) { acquireMutexKey, isSwapChainTexture, isInitialized),
&dawnTexture)) {
return nullptr; return nullptr;
} }
return {dawnTexture}; return {dawnTexture};

View File

@ -121,10 +121,11 @@ namespace dawn_native { namespace d3d12 {
StagingDescriptorAllocator* GetDepthStencilViewAllocator() const; StagingDescriptorAllocator* GetDepthStencilViewAllocator() const;
Ref<TextureBase> WrapSharedHandle(const ExternalImageDescriptor* descriptor, Ref<TextureBase> CreateExternalTexture(const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture); bool isSwapChainTexture,
bool isInitialized);
ResultOrError<ComPtr<IDXGIKeyedMutex>> CreateKeyedMutexForTexture( ResultOrError<ComPtr<IDXGIKeyedMutex>> CreateKeyedMutexForTexture(
ID3D12Resource* d3d12Resource); ID3D12Resource* d3d12Resource);
void ReleaseKeyedMutexForTexture(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex); void ReleaseKeyedMutexForTexture(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex);

View File

@ -418,27 +418,25 @@ namespace dawn_native { namespace d3d12 {
} }
// static // static
ResultOrError<Ref<Texture>> Texture::Create(Device* device, ResultOrError<Ref<Texture>> Texture::CreateExternalImage(Device* device,
const ExternalImageDescriptor* descriptor, const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture) { bool isSwapChainTexture,
const TextureDescriptor* textureDescriptor = bool isInitialized) {
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
Ref<Texture> dawnTexture = Ref<Texture> dawnTexture =
AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal)); AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal));
DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle, DAWN_TRY(dawnTexture->InitializeAsExternalTexture(descriptor, std::move(d3d12Texture),
acquireMutexKey, isSwapChainTexture)); acquireMutexKey, 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.
if (!descriptor->isInitialized && dawnTexture->GetFormat().IsMultiPlanar()) { if (!isInitialized && dawnTexture->GetFormat().IsMultiPlanar()) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Cannot create a multi-planar formatted texture without being initialized"); "Cannot create a multi-planar formatted texture without being initialized");
} }
dawnTexture->SetIsSubresourceContentInitialized(descriptor->isInitialized, dawnTexture->SetIsSubresourceContentInitialized(isInitialized,
dawnTexture->GetAllSubresources()); dawnTexture->GetAllSubresources());
return std::move(dawnTexture); return std::move(dawnTexture);
} }
@ -454,30 +452,13 @@ namespace dawn_native { namespace d3d12 {
} }
MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture) { bool isSwapChainTexture) {
Device* dawnDevice = ToBackend(GetDevice()); Device* dawnDevice = ToBackend(GetDevice());
DAWN_TRY(ValidateTextureDescriptor(dawnDevice, descriptor));
DAWN_TRY(ValidateTextureDescriptorCanBeWrapped(descriptor));
ComPtr<ID3D12Resource> d3d12Resource;
DAWN_TRY(CheckHRESULT(dawnDevice->GetD3D12Device()->OpenSharedHandle(
sharedHandle, IID_PPV_ARGS(&d3d12Resource)),
"D3D12 opening shared handle"));
DAWN_TRY(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor));
// Shared handle is assumed to support resource sharing capability. The resource
// shared capability tier must agree to share resources between D3D devices.
if (GetFormat().IsMultiPlanar()) {
DAWN_TRY(ValidateD3D12VideoTextureCanBeShared(ToBackend(GetDevice()),
D3D12TextureFormat(descriptor->format)));
}
ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex; ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
DAWN_TRY_ASSIGN(dxgiKeyedMutex, DAWN_TRY_ASSIGN(dxgiKeyedMutex, dawnDevice->CreateKeyedMutexForTexture(d3d12Texture.Get()));
dawnDevice->CreateKeyedMutexForTexture(d3d12Resource.Get()));
DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(uint64_t(acquireMutexKey), INFINITE), DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(uint64_t(acquireMutexKey), INFINITE),
"D3D12 acquiring shared mutex")); "D3D12 acquiring shared mutex"));
@ -491,7 +472,7 @@ namespace dawn_native { namespace d3d12 {
// When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
// texture is owned externally. The texture's owning entity must remain responsible for // texture is owned externally. The texture's owning entity must remain responsible for
// memory management. // memory management.
mResourceAllocation = {info, 0, std::move(d3d12Resource), nullptr}; mResourceAllocation = {info, 0, std::move(d3d12Texture), nullptr};
return {}; return {};
} }

View File

@ -33,16 +33,18 @@ namespace dawn_native { namespace d3d12 {
MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource, MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
const TextureDescriptor* descriptor); const TextureDescriptor* descriptor);
MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor); MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor);
MaybeError ValidateD3D12VideoTextureCanBeShared(Device* device, DXGI_FORMAT textureFormat);
class Texture final : public TextureBase { class Texture final : public TextureBase {
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>> Create(Device* device, static ResultOrError<Ref<Texture>> CreateExternalImage(Device* device,
const ExternalImageDescriptor* descriptor, const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture); bool isSwapChainTexture,
bool isInitialized);
static ResultOrError<Ref<Texture>> Create(Device* device, static ResultOrError<Ref<Texture>> Create(Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ComPtr<ID3D12Resource> d3d12Texture); ComPtr<ID3D12Resource> d3d12Texture);
@ -85,7 +87,7 @@ namespace dawn_native { namespace d3d12 {
MaybeError InitializeAsInternalTexture(); MaybeError InitializeAsInternalTexture();
MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor, MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor,
HANDLE sharedHandle, ComPtr<ID3D12Resource> d3d12Texture,
ExternalMutexSerial acquireMutexKey, ExternalMutexSerial acquireMutexKey,
bool isSwapChainTexture); bool isSwapChainTexture);
MaybeError InitializeAsSwapChainTexture(ComPtr<ID3D12Resource> d3d12Texture); MaybeError InitializeAsSwapChainTexture(ComPtr<ID3D12Resource> d3d12Texture);

View File

@ -19,10 +19,14 @@
#include <dawn_native/DawnNative.h> #include <dawn_native/DawnNative.h>
#include <DXGI1_4.h> #include <DXGI1_4.h>
#include <d3d12.h>
#include <windows.h> #include <windows.h>
#include <wrl/client.h> #include <wrl/client.h>
#include <memory>
struct ID3D12Device; struct ID3D12Device;
struct ID3D12Resource;
namespace dawn_native { namespace d3d12 { namespace dawn_native { namespace d3d12 {
DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device(WGPUDevice device); DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device(WGPUDevice device);
@ -45,10 +49,46 @@ namespace dawn_native { namespace d3d12 {
ExternalImageDescriptorDXGISharedHandle(); ExternalImageDescriptorDXGISharedHandle();
HANDLE sharedHandle; HANDLE sharedHandle;
// Warning: depreciated, replaced by ExternalImageAccessDescriptorDXGIKeyedMutex.
uint64_t acquireMutexKey; uint64_t acquireMutexKey;
bool isSwapChainTexture = false; bool isSwapChainTexture = false;
}; };
struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptorDXGIKeyedMutex
: ExternalImageAccessDescriptor {
public:
uint64_t acquireMutexKey;
bool isSwapChainTexture = false;
};
class DAWN_NATIVE_EXPORT ExternalImageDXGI {
public:
// Note: SharedHandle must be a handle to a texture object.
static std::unique_ptr<ExternalImageDXGI> Create(
WGPUDevice device,
const ExternalImageDescriptorDXGISharedHandle* descriptor);
WGPUTexture ProduceTexture(WGPUDevice device,
const ExternalImageAccessDescriptorDXGIKeyedMutex* descriptor);
private:
ExternalImageDXGI(Microsoft::WRL::ComPtr<ID3D12Resource> d3d12Resource,
const WGPUTextureDescriptor* descriptor);
Microsoft::WRL::ComPtr<ID3D12Resource> mD3D12Resource;
// Contents of WGPUTextureDescriptor are stored individually since the descriptor
// could outlive this image.
WGPUTextureUsageFlags mUsage;
WGPUTextureDimension mDimension;
WGPUExtent3D mSize;
WGPUTextureFormat mFormat;
uint32_t mMipLevelCount;
uint32_t mSampleCount;
};
// Warning: depreciated, replaced by ExternalImageDXGI::Create.
// Note: SharedHandle must be a handle to a texture object. // Note: SharedHandle must be a handle to a texture object.
DAWN_NATIVE_EXPORT WGPUTexture DAWN_NATIVE_EXPORT WGPUTexture
WrapSharedHandle(WGPUDevice device, const ExternalImageDescriptorDXGISharedHandle* descriptor); WrapSharedHandle(WGPUDevice device, const ExternalImageDescriptorDXGISharedHandle* descriptor);

View File

@ -228,6 +228,11 @@ namespace dawn_native {
ExternalImageDescriptor(ExternalImageType type); ExternalImageDescriptor(ExternalImageType type);
}; };
struct DAWN_NATIVE_EXPORT ExternalImageAccessDescriptor {
public:
bool isInitialized; // Whether the texture is initialized on import
};
struct DAWN_NATIVE_EXPORT ExternalImageExportInfo { struct DAWN_NATIVE_EXPORT ExternalImageExportInfo {
public: public:
const ExternalImageType type; const ExternalImageType type;

View File

@ -86,7 +86,9 @@ namespace {
void WrapSharedHandle(const wgpu::TextureDescriptor* dawnDesc, void WrapSharedHandle(const wgpu::TextureDescriptor* dawnDesc,
const D3D11_TEXTURE2D_DESC* baseD3dDescriptor, const D3D11_TEXTURE2D_DESC* baseD3dDescriptor,
wgpu::Texture* dawnTexture, wgpu::Texture* dawnTexture,
ID3D11Texture2D** d3d11TextureOut) const { ID3D11Texture2D** d3d11TextureOut,
std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI>*
externalImageOut = nullptr) 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);
@ -101,19 +103,33 @@ namespace {
&sharedHandle); &sharedHandle);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externDesc; dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc;
externDesc.cTextureDescriptor = externalImageDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(dawnDesc); reinterpret_cast<const WGPUTextureDescriptor*>(dawnDesc);
externDesc.sharedHandle = sharedHandle; externalImageDesc.sharedHandle = sharedHandle;
externDesc.acquireMutexKey = 0;
WGPUTexture texture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc); std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage =
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(sharedHandle);
*dawnTexture = wgpu::Texture::Acquire(texture); // Cannot access a non-existent external image (ex. validation error).
if (externalImage == nullptr) {
return;
}
dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.acquireMutexKey = 0;
*dawnTexture = wgpu::Texture::Acquire(
externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
*d3d11TextureOut = d3d11Texture.Detach(); *d3d11TextureOut = d3d11Texture.Detach();
if (externalImageOut != nullptr) {
*externalImageOut = std::move(externalImage);
}
} }
static constexpr size_t kTestWidth = 10; static constexpr size_t kTestWidth = 10;
@ -334,15 +350,20 @@ class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
hr = dxgiKeyedMutex->ReleaseSync(1); hr = dxgiKeyedMutex->ReleaseSync(1);
ASSERT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externDesc; dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {};
externDesc.cTextureDescriptor = externalImageDesc.sharedHandle = sharedHandle;
externalImageDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor); reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor);
externDesc.sharedHandle = sharedHandle;
externDesc.acquireMutexKey = 1;
externDesc.isInitialized = isInitialized;
WGPUTexture dawnTexture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc);
*dawnTextureOut = wgpu::Texture::Acquire(dawnTexture); std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage =
dawn_native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc);
dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.acquireMutexKey = 1;
externalAccessDesc.isInitialized = isInitialized;
*dawnTextureOut = wgpu::Texture::Acquire(
externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
*d3d11TextureOut = d3d11Texture.Detach(); *d3d11TextureOut = d3d11Texture.Detach();
*dxgiKeyedMutexOut = dxgiKeyedMutex.Detach(); *dxgiKeyedMutexOut = dxgiKeyedMutex.Detach();
} }
@ -519,5 +540,50 @@ TEST_P(D3D12SharedHandleUsageTests, UninitializedTextureIsCleared) {
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), dawnTexture, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), dawnTexture, 0, 0);
} }
// 1. Create an external image from the DX11 texture.
// 2. Produce two Dawn textures from the external image.
// 3. Clear each Dawn texture and verify the texture was cleared to a unique color.
TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
DAWN_SKIP_TEST_IF(UsesWire());
// Create the first Dawn texture then clear it to red.
wgpu::Texture texture;
ComPtr<ID3D11Texture2D> d3d11Texture;
std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage;
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);
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
}
// Once finished with the first texture, destroy it so we may re-acquire the external image
// again.
texture.Destroy();
// Create another Dawn texture then clear it with another color.
dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.acquireMutexKey = 1;
externalAccessDesc.isInitialized = true;
texture =
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), texture.Get(), 0, 0);
// Clear the new texture to blue
{
const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f};
ASSERT_NE(texture.Get(), nullptr);
ClearImage(texture.Get(), solidBlue);
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());

View File

@ -139,9 +139,10 @@ namespace {
} }
} }
wgpu::Texture CreateVideoTextureForTest(wgpu::TextureFormat format, void CreateVideoTextureForTest(wgpu::TextureFormat format,
wgpu::TextureUsage usage, wgpu::TextureUsage usage,
bool isCheckerboard = false) { bool isCheckerboard,
wgpu::Texture* dawnTextureOut) {
wgpu::TextureDescriptor textureDesc; wgpu::TextureDescriptor textureDesc;
textureDesc.format = format; textureDesc.format = format;
textureDesc.dimension = wgpu::TextureDimension::e2D; textureDesc.dimension = wgpu::TextureDimension::e2D;
@ -171,24 +172,17 @@ namespace {
ComPtr<ID3D11Texture2D> d3d11Texture; ComPtr<ID3D11Texture2D> d3d11Texture;
HRESULT hr = mD3d11Device->CreateTexture2D(&d3dDescriptor, &subres, &d3d11Texture); HRESULT hr = mD3d11Device->CreateTexture2D(&d3dDescriptor, &subres, &d3d11Texture);
EXPECT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
ComPtr<IDXGIResource1> dxgiResource; ComPtr<IDXGIResource1> dxgiResource;
hr = d3d11Texture.As(&dxgiResource); hr = d3d11Texture.As(&dxgiResource);
EXPECT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
HANDLE sharedHandle; HANDLE sharedHandle;
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); &sharedHandle);
EXPECT_EQ(hr, S_OK); ASSERT_EQ(hr, S_OK);
dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externDesc;
externDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(&textureDesc);
externDesc.sharedHandle = sharedHandle;
externDesc.acquireMutexKey = 1;
externDesc.isInitialized = true;
// DX11 texture should be initialized upon CreateTexture2D. However, if we do not // DX11 texture should be initialized upon CreateTexture2D. However, if we do not
// acquire/release the keyed mutex before using the wrapped WebGPU texture, the WebGPU // acquire/release the keyed mutex before using the wrapped WebGPU texture, the WebGPU
@ -205,13 +199,23 @@ namespace {
// Open the DX11 texture in Dawn from the shared handle and return it as a WebGPU // Open the DX11 texture in Dawn from the shared handle and return it as a WebGPU
// texture. // texture.
wgpu::Texture wgpuTexture = wgpu::Texture::Acquire( dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc;
dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc)); externalImageDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(&textureDesc);
externalImageDesc.sharedHandle = sharedHandle;
std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage =
dawn_native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc);
// Handle is no longer needed once resources are created. // Handle is no longer needed once resources are created.
::CloseHandle(sharedHandle); ::CloseHandle(sharedHandle);
return wgpuTexture; dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
externalAccessDesc.acquireMutexKey = 1;
externalAccessDesc.isInitialized = true;
*dawnTextureOut = wgpu::Texture::Acquire(
externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
} }
// Vertex shader used to render a sampled texture into a quad. // Vertex shader used to render a sampled texture into a quad.
@ -259,8 +263,9 @@ namespace {
// Samples the luminance (Y) plane from an imported NV12 texture into a single channel of an RGBA // Samples the luminance (Y) plane from an imported NV12 texture into a single channel of an RGBA
// output attachment and checks for the expected pixel value in the rendered quad. // output attachment and checks for the expected pixel value in the rendered quad.
TEST_P(D3D12VideoViewsTests, NV12SampleYtoR) { TEST_P(D3D12VideoViewsTests, NV12SampleYtoR) {
wgpu::Texture wgpuTexture = CreateVideoTextureForTest( wgpu::Texture wgpuTexture;
wgpu::TextureFormat::R8BG8Biplanar420Unorm, wgpu::TextureUsage::Sampled); CreateVideoTextureForTest(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
wgpu::TextureUsage::Sampled, /*isCheckerboard*/ false, &wgpuTexture);
wgpu::TextureViewDescriptor viewDesc; wgpu::TextureViewDescriptor viewDesc;
viewDesc.aspect = wgpu::TextureAspect::Plane0Only; viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
@ -310,8 +315,9 @@ TEST_P(D3D12VideoViewsTests, NV12SampleYtoR) {
// Samples the chrominance (UV) plane from an imported texture into two channels of an RGBA output // Samples the chrominance (UV) plane from an imported texture into two channels of an RGBA output
// attachment and checks for the expected pixel value in the rendered quad. // attachment and checks for the expected pixel value in the rendered quad.
TEST_P(D3D12VideoViewsTests, NV12SampleUVtoRG) { TEST_P(D3D12VideoViewsTests, NV12SampleUVtoRG) {
wgpu::Texture wgpuTexture = CreateVideoTextureForTest( wgpu::Texture wgpuTexture;
wgpu::TextureFormat::R8BG8Biplanar420Unorm, wgpu::TextureUsage::Sampled); CreateVideoTextureForTest(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
wgpu::TextureUsage::Sampled, /*isCheckerboard*/ false, &wgpuTexture);
wgpu::TextureViewDescriptor viewDesc; wgpu::TextureViewDescriptor viewDesc;
viewDesc.aspect = wgpu::TextureAspect::Plane1Only; viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
@ -362,8 +368,9 @@ TEST_P(D3D12VideoViewsTests, NV12SampleUVtoRG) {
// Renders a NV12 "checkerboard" texture into a RGB quad then checks the color at specific // Renders a NV12 "checkerboard" texture into a RGB quad then checks the color at specific
// points to ensure the image has not been flipped. // points to ensure the image has not been flipped.
TEST_P(D3D12VideoViewsTests, NV12SampleYUVtoRGB) { TEST_P(D3D12VideoViewsTests, NV12SampleYUVtoRGB) {
wgpu::Texture wgpuTexture = CreateVideoTextureForTest( wgpu::Texture wgpuTexture;
wgpu::TextureFormat::R8BG8Biplanar420Unorm, wgpu::TextureUsage::Sampled, true); CreateVideoTextureForTest(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
wgpu::TextureUsage::Sampled, /*isCheckerboard*/ true, &wgpuTexture);
wgpu::TextureViewDescriptor lumaViewDesc; wgpu::TextureViewDescriptor lumaViewDesc;
lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only; lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only;