Add WrapSharedHandle to D3D12 backend

WrapSharedHandle uses a HANDLE and an acquire key to create
a Dawn texture object.

A future change will use the acquire key to manage a keyed shared
mutex with Chromium code.

Bug: dawn:27
Change-Id: I1c0ef8d022158abf3f1c6731a37ee3f51632fcf9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10540
Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Rafael Cintron 2019-09-02 19:19:34 +00:00 committed by Commit Bot service account
parent 824424fa35
commit 179d7b28a3
8 changed files with 355 additions and 1 deletions

View File

@ -810,6 +810,14 @@ source_set("dawn_end2end_tests_sources") {
libs += [ "IOSurface.framework" ]
}
if (dawn_enable_d3d12) {
sources += [ "src/tests/end2end/D3D12ResourceWrappingTests.cpp" ]
libs += [
"d3d11.lib",
"dxgi.lib",
]
}
if (dawn_enable_opengl) {
deps += [ "third_party:glfw" ]
}

View File

@ -23,6 +23,12 @@
namespace dawn_native { namespace d3d12 {
ComPtr<ID3D12Device> GetD3D12Device(DawnDevice device) {
Device* backendDevice = reinterpret_cast<Device*>(device);
return backendDevice->GetD3D12Device();
}
DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device, HWND window) {
Device* backendDevice = reinterpret_cast<Device*>(device);
@ -39,4 +45,13 @@ namespace dawn_native { namespace d3d12 {
return static_cast<DawnTextureFormat>(impl->GetPreferredFormat());
}
DawnTexture WrapSharedHandle(DawnDevice device,
const DawnTextureDescriptor* descriptor,
HANDLE sharedHandle) {
Device* backendDevice = reinterpret_cast<Device*>(device);
const TextureDescriptor* backendDescriptor =
reinterpret_cast<const TextureDescriptor*>(descriptor);
TextureBase* texture = backendDevice->WrapSharedHandle(backendDescriptor, sharedHandle);
return reinterpret_cast<DawnTexture>(texture);
}
}} // namespace dawn_native::d3d12

View File

@ -380,4 +380,28 @@ namespace dawn_native { namespace d3d12 {
return allocation;
}
TextureBase* Device::WrapSharedHandle(const TextureDescriptor* descriptor,
HANDLE sharedHandle) {
if (ConsumedError(ValidateTextureDescriptor(this, descriptor))) {
return nullptr;
}
if (ConsumedError(ValidateTextureDescriptorCanBeWrapped(descriptor))) {
return nullptr;
}
ComPtr<ID3D12Resource> d3d12Resource;
const HRESULT hr =
mD3d12Device->OpenSharedHandle(sharedHandle, IID_PPV_ARGS(&d3d12Resource));
if (FAILED(hr)) {
return nullptr;
}
if (ConsumedError(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor))) {
return nullptr;
}
return new Texture(this, descriptor, d3d12Resource.Get());
}
}} // namespace dawn_native::d3d12

View File

@ -90,6 +90,8 @@ namespace dawn_native { namespace d3d12 {
void DeallocateMemory(ResourceMemoryAllocation& allocation);
TextureBase* WrapSharedHandle(const TextureDescriptor* descriptor, HANDLE sharedHandle);
private:
ResultOrError<BindGroupBase*> CreateBindGroupImpl(
const BindGroupDescriptor* descriptor) override;

View File

@ -100,7 +100,6 @@ namespace dawn_native { namespace d3d12 {
UNREACHABLE();
}
}
} // namespace
DXGI_FORMAT D3D12TextureFormat(dawn::TextureFormat format) {
@ -221,6 +220,56 @@ namespace dawn_native { namespace d3d12 {
}
}
MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor) {
if (descriptor->dimension != dawn::TextureDimension::e2D) {
return DAWN_VALIDATION_ERROR("Texture must be 2D");
}
if (descriptor->mipLevelCount != 1) {
return DAWN_VALIDATION_ERROR("Mip level count must be 1");
}
if (descriptor->arrayLayerCount != 1) {
return DAWN_VALIDATION_ERROR("Array layer count must be 1");
}
if (descriptor->sampleCount != 1) {
return DAWN_VALIDATION_ERROR("Sample count must be 1");
}
return {};
}
MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
const TextureDescriptor* dawnDescriptor) {
const D3D12_RESOURCE_DESC d3dDescriptor = d3d12Resource->GetDesc();
if ((dawnDescriptor->size.width != d3dDescriptor.Width) ||
(dawnDescriptor->size.height != d3dDescriptor.Height) ||
(dawnDescriptor->size.depth != 1)) {
return DAWN_VALIDATION_ERROR("D3D12 texture size doesn't match descriptor");
}
const DXGI_FORMAT dxgiFormatFromDescriptor = D3D12TextureFormat(dawnDescriptor->format);
if (dxgiFormatFromDescriptor != d3dDescriptor.Format) {
return DAWN_VALIDATION_ERROR(
"D3D12 texture format must be compatible with descriptor format.");
}
if (d3dDescriptor.MipLevels != 1) {
return DAWN_VALIDATION_ERROR("D3D12 texture number of miplevels must be 1.");
}
if (d3dDescriptor.DepthOrArraySize != 1) {
return DAWN_VALIDATION_ERROR("D3D12 texture array size must be 1.");
}
// Shared textures cannot be multi-sample so no need to check those.
ASSERT(d3dDescriptor.SampleDesc.Count == 1);
ASSERT(d3dDescriptor.SampleDesc.Quality == 0);
return {};
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
D3D12_RESOURCE_DESC resourceDescriptor;
@ -258,6 +307,8 @@ namespace dawn_native { namespace d3d12 {
const TextureDescriptor* descriptor,
ID3D12Resource* nativeTexture)
: TextureBase(device, descriptor, TextureState::OwnedExternal), mResource(nativeTexture) {
SetIsSubresourceContentInitialized(0, descriptor->mipLevelCount, 0,
descriptor->arrayLayerCount);
}
Texture::~Texture() {

View File

@ -25,6 +25,9 @@ namespace dawn_native { namespace d3d12 {
class Device;
DXGI_FORMAT D3D12TextureFormat(dawn::TextureFormat format);
MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource,
const TextureDescriptor* descriptor);
MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor);
class Texture : public TextureBase {
public:

View File

@ -19,12 +19,20 @@
#include <dawn_native/DawnNative.h>
#include <windows.h>
#include <wrl/client.h>
struct ID3D12Device;
namespace dawn_native { namespace d3d12 {
DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device(DawnDevice device);
DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device,
HWND window);
DAWN_NATIVE_EXPORT DawnTextureFormat
GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain);
DAWN_NATIVE_EXPORT DawnTexture WrapSharedHandle(DawnDevice device,
const DawnTextureDescriptor* descriptor,
HANDLE sharedHandle);
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12BACKEND_H_

View File

@ -0,0 +1,243 @@
// Copyright 2019 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.
#include "tests/DawnTest.h"
#include <d3d11.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <wrl/client.h>
#include "dawn_native/D3D12Backend.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h"
using Microsoft::WRL::ComPtr;
namespace {
class D3D12ResourceTestBase : public DawnTest {
public:
void SetUp() override {
DawnTest::SetUp();
if (UsesWire()) {
return;
}
// Create the D3D11 device/contexts that will be used in subsequent tests
ComPtr<ID3D12Device> d3d12Device = dawn_native::d3d12::GetD3D12Device(device.Get());
const LUID adapterLuid = d3d12Device->GetAdapterLuid();
ComPtr<IDXGIFactory4> dxgiFactory;
HRESULT hr = ::CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory));
ASSERT_EQ(hr, S_OK);
ComPtr<IDXGIAdapter> dxgiAdapter;
hr = dxgiFactory->EnumAdapterByLuid(adapterLuid, IID_PPV_ARGS(&dxgiAdapter));
ASSERT_EQ(hr, S_OK);
ComPtr<ID3D11Device> d3d11Device;
D3D_FEATURE_LEVEL d3dFeatureLevel;
ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
hr = ::D3D11CreateDevice(dxgiAdapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
nullptr, 0, D3D11_SDK_VERSION, &d3d11Device, &d3dFeatureLevel,
&d3d11DeviceContext);
ASSERT_EQ(hr, S_OK);
mD3d11Device = std::move(d3d11Device);
mD3d11DeviceContext = std::move(d3d11DeviceContext);
}
protected:
void WrapSharedHandle(const dawn::TextureDescriptor* dawnDescriptor,
const D3D11_TEXTURE2D_DESC* d3dDescriptor,
dawn::Texture* dawnTexture) const {
ComPtr<ID3D11Texture2D> d3d11Texture;
HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture);
ASSERT_EQ(hr, S_OK);
ComPtr<IDXGIResource1> dxgiResource;
hr = d3d11Texture.As(&dxgiResource);
ASSERT_EQ(hr, S_OK);
HANDLE sharedHandle;
hr = dxgiResource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&sharedHandle);
ASSERT_EQ(hr, S_OK);
DawnTexture texture = dawn_native::d3d12::WrapSharedHandle(
device.Get(), reinterpret_cast<const DawnTextureDescriptor*>(dawnDescriptor),
sharedHandle);
// Now that we've created all of our resources, we can close the handle
// since we no longer need it.
::CloseHandle(sharedHandle);
*dawnTexture = dawn::Texture::Acquire(texture);
}
static constexpr size_t kTestWidth = 10;
static constexpr size_t kTestHeight = 10;
ComPtr<ID3D11Device> mD3d11Device;
ComPtr<ID3D11DeviceContext> mD3d11DeviceContext;
};
} // anonymous namespace
// A small fixture used to initialize default data for the D3D12Resource validation tests.
// These tests are skipped if the harness is using the wire.
class D3D12SharedHandleValidation : public D3D12ResourceTestBase {
public:
void SetUp() override {
D3D12ResourceTestBase::SetUp();
dawnDescriptor.dimension = dawn::TextureDimension::e2D;
dawnDescriptor.format = dawn::TextureFormat::BGRA8Unorm;
dawnDescriptor.size = {kTestWidth, kTestHeight, 1};
dawnDescriptor.sampleCount = 1;
dawnDescriptor.arrayLayerCount = 1;
dawnDescriptor.mipLevelCount = 1;
dawnDescriptor.usage = dawn::TextureUsage::OutputAttachment;
d3dDescriptor.Width = kTestWidth;
d3dDescriptor.Height = kTestHeight;
d3dDescriptor.MipLevels = 1;
d3dDescriptor.ArraySize = 1;
d3dDescriptor.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
d3dDescriptor.SampleDesc.Count = 1;
d3dDescriptor.SampleDesc.Quality = 0;
d3dDescriptor.Usage = D3D11_USAGE_DEFAULT;
d3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
d3dDescriptor.CPUAccessFlags = 0;
d3dDescriptor.MiscFlags =
D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
}
protected:
D3D11_TEXTURE2D_DESC d3dDescriptor;
dawn::TextureDescriptor dawnDescriptor;
};
// Test a successful wrapping of an D3D12Resource in a texture
TEST_P(D3D12SharedHandleValidation, Success) {
DAWN_SKIP_TEST_IF(UsesWire());
dawn::Texture texture;
WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture);
ASSERT_NE(texture.Get(), nullptr);
}
// Test an error occurs if the texture descriptor is invalid
TEST_P(D3D12SharedHandleValidation, InvalidTextureDescriptor) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.nextInChain = this;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor mip level count isn't 1
TEST_P(D3D12SharedHandleValidation, InvalidMipLevelCount) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.mipLevelCount = 2;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor array layer count isn't 1
TEST_P(D3D12SharedHandleValidation, InvalidArrayLayerCount) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.arrayLayerCount = 2;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor sample count isn't 1
TEST_P(D3D12SharedHandleValidation, InvalidSampleCount) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.sampleCount = 4;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor width doesn't match the texture's
TEST_P(D3D12SharedHandleValidation, InvalidWidth) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.size.width = kTestWidth + 1;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor height doesn't match the texture's
TEST_P(D3D12SharedHandleValidation, InvalidHeight) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.size.height = kTestHeight + 1;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the descriptor format isn't compatible with the D3D12 Resource
TEST_P(D3D12SharedHandleValidation, InvalidFormat) {
DAWN_SKIP_TEST_IF(UsesWire());
dawnDescriptor.format = dawn::TextureFormat::R8Unorm;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the number of D3D mip levels is greater than 1.
TEST_P(D3D12SharedHandleValidation, InvalidNumD3DMipLevels) {
DAWN_SKIP_TEST_IF(UsesWire());
d3dDescriptor.MipLevels = 2;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
// Test an error occurs if the number of array levels is greater than 1.
TEST_P(D3D12SharedHandleValidation, InvalidD3DArraySize) {
DAWN_SKIP_TEST_IF(UsesWire());
d3dDescriptor.ArraySize = 2;
dawn::Texture texture;
ASSERT_DEVICE_ERROR(WrapSharedHandle(&dawnDescriptor, &d3dDescriptor, &texture));
ASSERT_EQ(texture.Get(), nullptr);
}
DAWN_INSTANTIATE_TEST(D3D12SharedHandleValidation, D3D12Backend);