|  | // Copyright 2023 The Dawn & Tint Authors | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // 1. Redistributions of source code must retain the above copyright notice, this | 
|  | //    list of conditions and the following disclaimer. | 
|  | // | 
|  | // 2. Redistributions in binary form must reproduce the above copyright notice, | 
|  | //    this list of conditions and the following disclaimer in the documentation | 
|  | //    and/or other materials provided with the distribution. | 
|  | // | 
|  | // 3. Neither the name of the copyright holder nor the names of its | 
|  | //    contributors may be used to endorse or promote products derived from | 
|  | //    this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
|  | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
|  | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
|  | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
|  | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | #include <d3d11.h> | 
|  | #include <d3d11_4.h> | 
|  | #include <d3d12.h> | 
|  | #include <dxgi1_4.h> | 
|  | #include <wrl/client.h> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "dawn/native/D3D11Backend.h" | 
|  | #include "dawn/native/D3DBackend.h" | 
|  | #include "dawn/tests/white_box/SharedTextureMemoryTests.h" | 
|  | #include "dawn/webgpu_cpp.h" | 
|  |  | 
|  | namespace dawn { | 
|  | namespace { | 
|  |  | 
|  | using Microsoft::WRL::ComPtr; | 
|  |  | 
|  | enum class Mode { | 
|  | DXGISharedHandle, | 
|  | D3D11Texture2D, | 
|  | }; | 
|  |  | 
|  | class Backend : public SharedTextureMemoryTestBackend { | 
|  | public: | 
|  | template <Mode kMode> | 
|  | static Backend* GetInstance() { | 
|  | static Backend b(kMode, false); | 
|  | return &b; | 
|  | } | 
|  |  | 
|  | template <Mode kMode> | 
|  | static Backend* GetKeyedMutexInstance() { | 
|  | static Backend b(kMode, true); | 
|  | return &b; | 
|  | } | 
|  |  | 
|  | std::string Name() const override { | 
|  | switch (mMode) { | 
|  | case Mode::D3D11Texture2D: { | 
|  | return mUseKeyedMutex ? "D3D11Texture2D KeyedMutex" : "D3D11Texture2D"; | 
|  | } | 
|  | case Mode::DXGISharedHandle: { | 
|  | return mUseKeyedMutex ? "DXGISharedHandle KeyedMutex" : "DXGISharedHandle"; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool UseSameDevice() const override { return mMode == Mode::D3D11Texture2D; } | 
|  | bool SupportsConcurrentRead() const override { return !mUseKeyedMutex; } | 
|  |  | 
|  | std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override { | 
|  | switch (mMode) { | 
|  | case Mode::D3D11Texture2D: { | 
|  | return {wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D, | 
|  | wgpu::FeatureName::SharedFenceDXGISharedHandle, | 
|  | wgpu::FeatureName::DawnMultiPlanarFormats}; | 
|  | } | 
|  | case Mode::DXGISharedHandle: { | 
|  | return {wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle, | 
|  | wgpu::FeatureName::SharedFenceDXGISharedHandle, | 
|  | wgpu::FeatureName::DawnMultiPlanarFormats}; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ComPtr<ID3D11Device> MakeD3D11Device(const wgpu::Device& device) { | 
|  | switch (mMode) { | 
|  | case Mode::D3D11Texture2D: { | 
|  | return native::d3d11::GetD3D11Device(device.Get()); | 
|  | } | 
|  | case Mode::DXGISharedHandle: { | 
|  | ComPtr<IDXGIAdapter> dxgiAdapter = | 
|  | native::d3d::GetDXGIAdapter(device.GetAdapter().Get()); | 
|  |  | 
|  | ComPtr<ID3D11Device> d3d11Device; | 
|  | D3D_FEATURE_LEVEL d3dFeatureLevel; | 
|  | ComPtr<ID3D11DeviceContext> d3d11DeviceContext; | 
|  | HRESULT hr = ::D3D11CreateDevice( | 
|  | dxgiAdapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, nullptr, 0, | 
|  | D3D11_SDK_VERSION, &d3d11Device, &d3dFeatureLevel, &d3d11DeviceContext); | 
|  | DAWN_ASSERT(hr == S_OK); | 
|  |  | 
|  | return d3d11Device; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string LabelName(DXGI_FORMAT format, size_t size) const { | 
|  | std::stringstream ss; | 
|  | ss << "format 0x" << std::hex << format << " size " << size << "x" << size; | 
|  | return ss.str(); | 
|  | } | 
|  |  | 
|  | // Create one basic shared texture memory. It should support most operations. | 
|  | wgpu::SharedTextureMemory CreateSharedTextureMemory(const wgpu::Device& device) override { | 
|  | ComPtr<ID3D11Device> d3d11Device = MakeD3D11Device(device); | 
|  |  | 
|  | // Create a DX11 texture with data then wrap it in a shared handle. | 
|  | D3D11_TEXTURE2D_DESC d3dDescriptor; | 
|  | d3dDescriptor.Width = 16; | 
|  | d3dDescriptor.Height = 16; | 
|  | d3dDescriptor.MipLevels = 1; | 
|  | d3dDescriptor.ArraySize = 1; | 
|  | d3dDescriptor.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | 
|  | d3dDescriptor.SampleDesc.Count = 1; | 
|  | d3dDescriptor.SampleDesc.Quality = 0; | 
|  | d3dDescriptor.Usage = D3D11_USAGE_DEFAULT; | 
|  | d3dDescriptor.BindFlags = | 
|  | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_RENDER_TARGET; | 
|  | d3dDescriptor.CPUAccessFlags = 0; | 
|  | d3dDescriptor.MiscFlags = | 
|  | D3D11_RESOURCE_MISC_SHARED_NTHANDLE | | 
|  | (mUseKeyedMutex ? D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX : D3D11_RESOURCE_MISC_SHARED); | 
|  |  | 
|  | ComPtr<ID3D11Texture2D> d3d11Texture; | 
|  | HRESULT hr = d3d11Device->CreateTexture2D(&d3dDescriptor, nullptr, &d3d11Texture); | 
|  |  | 
|  | switch (mMode) { | 
|  | case Mode::D3D11Texture2D: { | 
|  | native::d3d11::SharedTextureMemoryD3D11Texture2DDescriptor texture2DDesc; | 
|  | texture2DDesc.texture = std::move(d3d11Texture); | 
|  |  | 
|  | wgpu::SharedTextureMemoryDescriptor desc; | 
|  | desc.nextInChain = &texture2DDesc; | 
|  |  | 
|  | return device.ImportSharedTextureMemory(&desc); | 
|  | } | 
|  | case Mode::DXGISharedHandle: { | 
|  | ComPtr<IDXGIResource1> dxgiResource; | 
|  | hr = d3d11Texture.As(&dxgiResource); | 
|  | DAWN_ASSERT(hr == S_OK); | 
|  |  | 
|  | HANDLE sharedHandle; | 
|  | hr = dxgiResource->CreateSharedHandle( | 
|  | nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, | 
|  | &sharedHandle); | 
|  | DAWN_ASSERT(hr == S_OK); | 
|  |  | 
|  | wgpu::SharedTextureMemoryDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = sharedHandle; | 
|  |  | 
|  | std::string label = LabelName(d3dDescriptor.Format, d3dDescriptor.Width); | 
|  | wgpu::SharedTextureMemoryDescriptor desc; | 
|  | desc.nextInChain = &sharedHandleDesc; | 
|  | desc.label = label.c_str(); | 
|  |  | 
|  | auto memory = device.ImportSharedTextureMemory(&desc); | 
|  | ::CloseHandle(sharedHandle); | 
|  |  | 
|  | return memory; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<std::vector<wgpu::SharedTextureMemory>> CreatePerDeviceSharedTextureMemories( | 
|  | const std::vector<wgpu::Device>& devices) override { | 
|  | std::vector<std::vector<wgpu::SharedTextureMemory>> memories; | 
|  |  | 
|  | ComPtr<ID3D11Device> d3d11Device = MakeD3D11Device(devices[0]); | 
|  |  | 
|  | bool supportsNV12Sharing = false; | 
|  | D3D11_FEATURE_DATA_D3D11_OPTIONS5 featureOptions5{}; | 
|  | if (d3d11Device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS5, &featureOptions5, | 
|  | sizeof(featureOptions5)) == S_OK) { | 
|  | if (featureOptions5.SharedResourceTier >= D3D11_SHARED_RESOURCE_TIER_2) { | 
|  | UINT formatSupport; | 
|  | if (d3d11Device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport) == S_OK && | 
|  | (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0) { | 
|  | supportsNV12Sharing = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | struct D3DFormat { | 
|  | DXGI_FORMAT format; | 
|  | wgpu::FeatureName requiredFeature = wgpu::FeatureName::Undefined; | 
|  | }; | 
|  | std::vector<D3DFormat> formats = {{ | 
|  | {DXGI_FORMAT_R16G16B16A16_FLOAT}, | 
|  | {DXGI_FORMAT_R16G16_FLOAT}, | 
|  | {DXGI_FORMAT_R16_FLOAT}, | 
|  | {DXGI_FORMAT_R8G8B8A8_UNORM}, | 
|  | {DXGI_FORMAT_B8G8R8A8_UNORM}, | 
|  | {DXGI_FORMAT_R10G10B10A2_UNORM}, | 
|  | {DXGI_FORMAT_R16G16_UNORM, wgpu::FeatureName::Norm16TextureFormats}, | 
|  | {DXGI_FORMAT_R16_UNORM, wgpu::FeatureName::Norm16TextureFormats}, | 
|  | {DXGI_FORMAT_R8G8_UNORM}, | 
|  | {DXGI_FORMAT_R8_UNORM}, | 
|  | }}; | 
|  |  | 
|  | if (supportsNV12Sharing) { | 
|  | formats.push_back({DXGI_FORMAT_NV12}); | 
|  | } | 
|  |  | 
|  | for (auto f : formats) { | 
|  | for (uint32_t size : {4, 64}) { | 
|  | // Create a DX11 texture with data then wrap it in a shared handle. | 
|  | D3D11_TEXTURE2D_DESC d3dDescriptor; | 
|  | d3dDescriptor.Width = size; | 
|  | d3dDescriptor.Height = size; | 
|  | d3dDescriptor.MipLevels = 1; | 
|  | d3dDescriptor.ArraySize = 1; | 
|  | d3dDescriptor.Format = f.format; | 
|  | d3dDescriptor.SampleDesc.Count = 1; | 
|  | d3dDescriptor.SampleDesc.Quality = 0; | 
|  | d3dDescriptor.Usage = D3D11_USAGE_DEFAULT; | 
|  | d3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS | | 
|  | D3D11_BIND_RENDER_TARGET; | 
|  | d3dDescriptor.CPUAccessFlags = 0; | 
|  | d3dDescriptor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | | 
|  | (mUseKeyedMutex ? D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | 
|  | : D3D11_RESOURCE_MISC_SHARED); | 
|  |  | 
|  | ComPtr<ID3D11Texture2D> d3d11Texture; | 
|  | HRESULT hr = d3d11Device->CreateTexture2D(&d3dDescriptor, nullptr, &d3d11Texture); | 
|  |  | 
|  | std::vector<wgpu::SharedTextureMemory> perDeviceMemories; | 
|  | switch (mMode) { | 
|  | case Mode::D3D11Texture2D: { | 
|  | native::d3d11::SharedTextureMemoryD3D11Texture2DDescriptor texture2DDesc; | 
|  | texture2DDesc.texture = d3d11Texture; | 
|  |  | 
|  | wgpu::SharedTextureMemoryDescriptor desc; | 
|  | desc.nextInChain = &texture2DDesc; | 
|  |  | 
|  | for (auto& device : devices) { | 
|  | if (f.requiredFeature != wgpu::FeatureName::Undefined && | 
|  | !device.HasFeature(f.requiredFeature)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc)); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case Mode::DXGISharedHandle: { | 
|  | ComPtr<IDXGIResource1> dxgiResource; | 
|  | hr = d3d11Texture.As(&dxgiResource); | 
|  | DAWN_ASSERT(hr == S_OK); | 
|  |  | 
|  | HANDLE sharedHandle; | 
|  | hr = dxgiResource->CreateSharedHandle( | 
|  | nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, | 
|  | nullptr, &sharedHandle); | 
|  | DAWN_ASSERT(hr == S_OK); | 
|  |  | 
|  | wgpu::SharedTextureMemoryDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = sharedHandle; | 
|  |  | 
|  | std::string label = LabelName(f.format, size); | 
|  |  | 
|  | wgpu::SharedTextureMemoryDescriptor desc; | 
|  | desc.nextInChain = &sharedHandleDesc; | 
|  | desc.label = label.c_str(); | 
|  |  | 
|  | for (auto& device : devices) { | 
|  | if (f.requiredFeature != wgpu::FeatureName::Undefined && | 
|  | !device.HasFeature(f.requiredFeature)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc)); | 
|  | } | 
|  |  | 
|  | ::CloseHandle(sharedHandle); | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!perDeviceMemories.empty()) { | 
|  | memories.push_back(std::move(perDeviceMemories)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return memories; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Backend(Mode mode, bool useKeyedMutex) : mMode(mode), mUseKeyedMutex(useKeyedMutex) {} | 
|  |  | 
|  | const Mode mMode; | 
|  | const bool mUseKeyedMutex; | 
|  | }; | 
|  |  | 
|  | // Test that it is an error to import a shared fence without enabling the feature. | 
|  | TEST_P(SharedTextureMemoryNoFeatureTests, SharedFenceImportWithoutFeature) { | 
|  | ComPtr<ID3D11Device> d3d11Device = | 
|  | static_cast<Backend*>(GetParam().mBackend)->MakeD3D11Device(device); | 
|  |  | 
|  | ComPtr<ID3D11Device5> d3d11Device5; | 
|  | HRESULT hr = d3d11Device.As(&d3d11Device5); | 
|  |  | 
|  | ComPtr<ID3D11Fence> d3d11Fence; | 
|  | hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence)); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | HANDLE fenceSharedHandle = nullptr; | 
|  | hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | wgpu::SharedFenceDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = fenceSharedHandle; | 
|  |  | 
|  | wgpu::SharedFenceDescriptor fenceDesc; | 
|  | fenceDesc.nextInChain = &sharedHandleDesc; | 
|  |  | 
|  | ASSERT_DEVICE_ERROR_MSG(wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc), | 
|  | testing::HasSubstr("is not enabled")); | 
|  | ::CloseHandle(fenceSharedHandle); | 
|  | } | 
|  |  | 
|  | // Test that a shared handle can be imported, and then exported. | 
|  | TEST_P(SharedTextureMemoryTests, SharedFenceSuccessfulImportExport) { | 
|  | ComPtr<ID3D11Device> d3d11Device = | 
|  | static_cast<Backend*>(GetParam().mBackend)->MakeD3D11Device(device); | 
|  |  | 
|  | ComPtr<ID3D11Device5> d3d11Device5; | 
|  | HRESULT hr = d3d11Device.As(&d3d11Device5); | 
|  |  | 
|  | ComPtr<ID3D11Fence> d3d11Fence; | 
|  | hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence)); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | HANDLE fenceSharedHandle = nullptr; | 
|  | hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | wgpu::SharedFenceDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = fenceSharedHandle; | 
|  |  | 
|  | wgpu::SharedFenceDescriptor fenceDesc; | 
|  | fenceDesc.nextInChain = &sharedHandleDesc; | 
|  |  | 
|  | wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc); | 
|  | ::CloseHandle(fenceSharedHandle); | 
|  |  | 
|  | wgpu::SharedFenceDXGISharedHandleExportInfo sharedHandleInfo; | 
|  | wgpu::SharedFenceExportInfo exportInfo; | 
|  | exportInfo.nextInChain = &sharedHandleInfo; | 
|  | fence.ExportInfo(&exportInfo); | 
|  |  | 
|  | // The exported handle should be the same as the imported one. | 
|  | EXPECT_EQ(exportInfo.type, wgpu::SharedFenceType::DXGISharedHandle); | 
|  |  | 
|  | // Now, test that the fence handle is indeed the same one by making a new fence | 
|  | // with it. Changes it the first fence should be reflected in the second fence. | 
|  |  | 
|  | // Open the exported handle. | 
|  | ComPtr<ID3D11Fence> d3d11Fence2; | 
|  | hr = d3d11Device5->OpenSharedFence(sharedHandleInfo.handle, IID_PPV_ARGS(&d3d11Fence2)); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | // Check the fences point to the same object. | 
|  | uint64_t fenceValue = d3d11Fence->GetCompletedValue(); | 
|  | EXPECT_EQ(fenceValue, d3d11Fence2->GetCompletedValue()); | 
|  |  | 
|  | ComPtr<ID3D11DeviceContext> d3d11DeviceContext; | 
|  | d3d11Device->GetImmediateContext(&d3d11DeviceContext); | 
|  |  | 
|  | ComPtr<ID3D11DeviceContext4> d3d11DeviceContext4; | 
|  | hr = d3d11DeviceContext.As(&d3d11DeviceContext4); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | hr = d3d11DeviceContext4->Signal(d3d11Fence.Get(), fenceValue + 1); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | HANDLE ev = ::CreateEvent(NULL,                  // default security attributes | 
|  | TRUE,                  // manual-reset event | 
|  | FALSE,                 // initial state is nonsignaled | 
|  | TEXT("FenceComplete")  // object name | 
|  | ); | 
|  |  | 
|  | // Wait for the fence. | 
|  | d3d11Fence->SetEventOnCompletion(fenceValue + 1, ev); | 
|  | ::WaitForSingleObject(ev, INFINITE); | 
|  | ::CloseHandle(ev); | 
|  |  | 
|  | // Both fences should see the completed value. | 
|  | EXPECT_EQ(fenceValue + 1, d3d11Fence->GetCompletedValue()); | 
|  | EXPECT_EQ(fenceValue + 1, d3d11Fence2->GetCompletedValue()); | 
|  | } | 
|  |  | 
|  | // Test that it is an error to import a shared fence with a null DXGISharedHandle | 
|  | TEST_P(SharedTextureMemoryTests, SharedFenceImportDXGISharedHandleMissing) { | 
|  | wgpu::SharedFenceDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = nullptr; | 
|  |  | 
|  | wgpu::SharedFenceDescriptor fenceDesc; | 
|  | fenceDesc.nextInChain = &sharedHandleDesc; | 
|  |  | 
|  | ASSERT_DEVICE_ERROR_MSG(wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc), | 
|  | testing::HasSubstr("missing")); | 
|  | } | 
|  |  | 
|  | // Test exporting info from a shared fence with no chained struct. | 
|  | // It should be valid and the fence type is exported. | 
|  | TEST_P(SharedTextureMemoryTests, SharedFenceExportInfoNoChainedStruct) { | 
|  | ComPtr<ID3D11Device> d3d11Device = | 
|  | static_cast<Backend*>(GetParam().mBackend)->MakeD3D11Device(device); | 
|  |  | 
|  | ComPtr<ID3D11Device5> d3d11Device5; | 
|  | HRESULT hr = d3d11Device.As(&d3d11Device5); | 
|  |  | 
|  | ComPtr<ID3D11Fence> d3d11Fence; | 
|  | hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence)); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | HANDLE fenceSharedHandle = nullptr; | 
|  | hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | wgpu::SharedFenceDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = fenceSharedHandle; | 
|  |  | 
|  | wgpu::SharedFenceDescriptor fenceDesc; | 
|  | fenceDesc.nextInChain = &sharedHandleDesc; | 
|  |  | 
|  | wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc); | 
|  | ::CloseHandle(fenceSharedHandle); | 
|  |  | 
|  | // Test no chained struct. | 
|  | wgpu::SharedFenceExportInfo exportInfo; | 
|  | exportInfo.nextInChain = nullptr; | 
|  |  | 
|  | fence.ExportInfo(&exportInfo); | 
|  | EXPECT_EQ(exportInfo.type, wgpu::SharedFenceType::DXGISharedHandle); | 
|  | } | 
|  |  | 
|  | // Test exporting info from a shared fence with an invalid chained struct. | 
|  | // It should not be valid, but the fence type should still be exported. | 
|  | TEST_P(SharedTextureMemoryTests, SharedFenceExportInfoInvalidChainedStruct) { | 
|  | ComPtr<ID3D11Device> d3d11Device = | 
|  | static_cast<Backend*>(GetParam().mBackend)->MakeD3D11Device(device); | 
|  |  | 
|  | ComPtr<ID3D11Device5> d3d11Device5; | 
|  | HRESULT hr = d3d11Device.As(&d3d11Device5); | 
|  |  | 
|  | ComPtr<ID3D11Fence> d3d11Fence; | 
|  | hr = d3d11Device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d11Fence)); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | HANDLE fenceSharedHandle = nullptr; | 
|  | hr = d3d11Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceSharedHandle); | 
|  | ASSERT_EQ(hr, S_OK); | 
|  |  | 
|  | wgpu::SharedFenceDXGISharedHandleDescriptor sharedHandleDesc; | 
|  | sharedHandleDesc.handle = fenceSharedHandle; | 
|  |  | 
|  | wgpu::SharedFenceDescriptor fenceDesc; | 
|  | fenceDesc.nextInChain = &sharedHandleDesc; | 
|  |  | 
|  | wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc); | 
|  | ::CloseHandle(fenceSharedHandle); | 
|  |  | 
|  | // Test an invalid chained struct. | 
|  | wgpu::ChainedStructOut otherStruct; | 
|  | wgpu::SharedFenceExportInfo exportInfo; | 
|  | exportInfo.nextInChain = &otherStruct; | 
|  |  | 
|  | ASSERT_DEVICE_ERROR(fence.ExportInfo(&exportInfo)); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D, | 
|  | SharedTextureMemoryNoFeatureTests, | 
|  | {D3D11Backend(), D3D12Backend()}, | 
|  | {Backend::GetInstance<Mode::DXGISharedHandle>(), | 
|  | Backend::GetKeyedMutexInstance<Mode::DXGISharedHandle>()}); | 
|  |  | 
|  | DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D, | 
|  | SharedTextureMemoryTests, | 
|  | {D3D11Backend(), D3D12Backend()}, | 
|  | {Backend::GetInstance<Mode::DXGISharedHandle>(), | 
|  | Backend::GetKeyedMutexInstance<Mode::DXGISharedHandle>()}); | 
|  |  | 
|  | DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D11, | 
|  | SharedTextureMemoryNoFeatureTests, | 
|  | {D3D11Backend()}, | 
|  | {Backend::GetInstance<Mode::D3D11Texture2D>(), | 
|  | Backend::GetKeyedMutexInstance<Mode::D3D11Texture2D>()}); | 
|  |  | 
|  | DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D11, | 
|  | SharedTextureMemoryTests, | 
|  | {D3D11Backend()}, | 
|  | {Backend::GetInstance<Mode::D3D11Texture2D>(), | 
|  | Backend::GetKeyedMutexInstance<Mode::D3D11Texture2D>()}); | 
|  |  | 
|  | }  // anonymous namespace | 
|  | }  // namespace dawn |