• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include <d3d11.h>
18 #include <d3d12.h>
19 #include <dxgi1_4.h>
20 #include <wrl/client.h>
21 
22 #include "dawn_native/D3D12Backend.h"
23 #include "utils/ComboRenderPipelineDescriptor.h"
24 #include "utils/WGPUHelpers.h"
25 
26 using Microsoft::WRL::ComPtr;
27 
28 namespace {
29 
30     class D3D12ResourceTestBase : public DawnTest {
31       protected:
GetRequiredFeatures()32         std::vector<const char*> GetRequiredFeatures() override {
33             return {"dawn-internal-usages"};
34         }
35 
36       public:
SetUp()37         void SetUp() override {
38             DawnTest::SetUp();
39             if (UsesWire()) {
40                 return;
41             }
42 
43             // Create the D3D11 device/contexts that will be used in subsequent tests
44             ComPtr<ID3D12Device> d3d12Device = dawn_native::d3d12::GetD3D12Device(device.Get());
45 
46             const LUID adapterLuid = d3d12Device->GetAdapterLuid();
47 
48             ComPtr<IDXGIFactory4> dxgiFactory;
49             HRESULT hr = ::CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory));
50             ASSERT_EQ(hr, S_OK);
51 
52             ComPtr<IDXGIAdapter> dxgiAdapter;
53             hr = dxgiFactory->EnumAdapterByLuid(adapterLuid, IID_PPV_ARGS(&dxgiAdapter));
54             ASSERT_EQ(hr, S_OK);
55 
56             ComPtr<ID3D11Device> d3d11Device;
57             D3D_FEATURE_LEVEL d3dFeatureLevel;
58             ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
59             hr = ::D3D11CreateDevice(dxgiAdapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
60                                      nullptr, 0, D3D11_SDK_VERSION, &d3d11Device, &d3dFeatureLevel,
61                                      &d3d11DeviceContext);
62             ASSERT_EQ(hr, S_OK);
63 
64             mD3d11Device = std::move(d3d11Device);
65             mD3d11DeviceContext = std::move(d3d11DeviceContext);
66 
67             baseDawnDescriptor.dimension = wgpu::TextureDimension::e2D;
68             baseDawnDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
69             baseDawnDescriptor.size = {kTestWidth, kTestHeight, 1};
70             baseDawnDescriptor.sampleCount = 1;
71             baseDawnDescriptor.mipLevelCount = 1;
72             baseDawnDescriptor.usage =
73                 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc |
74                 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst;
75 
76             baseD3dDescriptor.Width = kTestWidth;
77             baseD3dDescriptor.Height = kTestHeight;
78             baseD3dDescriptor.MipLevels = 1;
79             baseD3dDescriptor.ArraySize = 1;
80             baseD3dDescriptor.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
81             baseD3dDescriptor.SampleDesc.Count = 1;
82             baseD3dDescriptor.SampleDesc.Quality = 0;
83             baseD3dDescriptor.Usage = D3D11_USAGE_DEFAULT;
84             baseD3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
85             baseD3dDescriptor.CPUAccessFlags = 0;
86             baseD3dDescriptor.MiscFlags =
87                 D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
88         }
89 
90       protected:
WrapSharedHandle(const wgpu::TextureDescriptor * dawnDesc,const D3D11_TEXTURE2D_DESC * baseD3dDescriptor,wgpu::Texture * dawnTexture,ID3D11Texture2D ** d3d11TextureOut,std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> * externalImageOut=nullptr) const91         void WrapSharedHandle(const wgpu::TextureDescriptor* dawnDesc,
92                               const D3D11_TEXTURE2D_DESC* baseD3dDescriptor,
93                               wgpu::Texture* dawnTexture,
94                               ID3D11Texture2D** d3d11TextureOut,
95                               std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI>*
96                                   externalImageOut = nullptr) const {
97             ComPtr<ID3D11Texture2D> d3d11Texture;
98             HRESULT hr = mD3d11Device->CreateTexture2D(baseD3dDescriptor, nullptr, &d3d11Texture);
99             ASSERT_EQ(hr, S_OK);
100 
101             ComPtr<IDXGIResource1> dxgiResource;
102             hr = d3d11Texture.As(&dxgiResource);
103             ASSERT_EQ(hr, S_OK);
104 
105             HANDLE sharedHandle;
106             hr = dxgiResource->CreateSharedHandle(
107                 nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
108                 &sharedHandle);
109             ASSERT_EQ(hr, S_OK);
110 
111             dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc;
112             externalImageDesc.cTextureDescriptor =
113                 reinterpret_cast<const WGPUTextureDescriptor*>(dawnDesc);
114             externalImageDesc.sharedHandle = sharedHandle;
115 
116             std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage =
117                 dawn_native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc);
118 
119             // Now that we've created all of our resources, we can close the handle
120             // since we no longer need it.
121             ::CloseHandle(sharedHandle);
122 
123             // Cannot access a non-existent external image (ex. validation error).
124             if (externalImage == nullptr) {
125                 return;
126             }
127 
128             dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
129             externalAccessDesc.acquireMutexKey = 0;
130             externalAccessDesc.releaseMutexKey = 1;
131             externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDesc->usage);
132 
133             *dawnTexture = wgpu::Texture::Acquire(
134                 externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
135             *d3d11TextureOut = d3d11Texture.Detach();
136 
137             if (externalImageOut != nullptr) {
138                 *externalImageOut = std::move(externalImage);
139             }
140         }
141 
142         static constexpr size_t kTestWidth = 10;
143         static constexpr size_t kTestHeight = 10;
144 
145         ComPtr<ID3D11Device> mD3d11Device;
146         ComPtr<ID3D11DeviceContext> mD3d11DeviceContext;
147 
148         D3D11_TEXTURE2D_DESC baseD3dDescriptor;
149         wgpu::TextureDescriptor baseDawnDescriptor;
150     };
151 
152 }  // anonymous namespace
153 
154 // A small fixture used to initialize default data for the D3D12Resource validation tests.
155 // These tests are skipped if the harness is using the wire.
156 class D3D12SharedHandleValidation : public D3D12ResourceTestBase {};
157 
158 // Test a successful wrapping of an D3D12Resource in a texture
TEST_P(D3D12SharedHandleValidation,Success)159 TEST_P(D3D12SharedHandleValidation, Success) {
160     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
161 
162     wgpu::Texture texture;
163     ComPtr<ID3D11Texture2D> d3d11Texture;
164     WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture);
165 
166     ASSERT_NE(texture.Get(), nullptr);
167 }
168 
169 // Test a successful wrapping of an D3D12Resource with DawnTextureInternalUsageDescriptor
TEST_P(D3D12SharedHandleValidation,SuccessWithInternalUsageDescriptor)170 TEST_P(D3D12SharedHandleValidation, SuccessWithInternalUsageDescriptor) {
171     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
172 
173     wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
174     baseDawnDescriptor.nextInChain = &internalDesc;
175     internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
176     internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
177 
178     wgpu::Texture texture;
179     ComPtr<ID3D11Texture2D> d3d11Texture;
180     WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture);
181 
182     ASSERT_NE(texture.Get(), nullptr);
183 }
184 
185 // Test an error occurs if an invalid sType is the nextInChain
TEST_P(D3D12SharedHandleValidation,InvalidTextureDescriptor)186 TEST_P(D3D12SharedHandleValidation, InvalidTextureDescriptor) {
187     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
188 
189     wgpu::ChainedStruct chainedDescriptor;
190     chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel;
191     baseDawnDescriptor.nextInChain = &chainedDescriptor;
192 
193     wgpu::Texture texture;
194     ComPtr<ID3D11Texture2D> d3d11Texture;
195     ASSERT_DEVICE_ERROR(
196         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
197 
198     ASSERT_EQ(texture.Get(), nullptr);
199 }
200 
201 // Test an error occurs if the descriptor mip level count isn't 1
TEST_P(D3D12SharedHandleValidation,InvalidMipLevelCount)202 TEST_P(D3D12SharedHandleValidation, InvalidMipLevelCount) {
203     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
204     baseDawnDescriptor.mipLevelCount = 2;
205 
206     wgpu::Texture texture;
207     ComPtr<ID3D11Texture2D> d3d11Texture;
208     ASSERT_DEVICE_ERROR(
209         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
210 
211     ASSERT_EQ(texture.Get(), nullptr);
212 }
213 
214 // Test an error occurs if the descriptor depth isn't 1
TEST_P(D3D12SharedHandleValidation,InvalidDepth)215 TEST_P(D3D12SharedHandleValidation, InvalidDepth) {
216     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
217     baseDawnDescriptor.size.depthOrArrayLayers = 2;
218 
219     wgpu::Texture texture;
220     ComPtr<ID3D11Texture2D> d3d11Texture;
221     ASSERT_DEVICE_ERROR(
222         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
223 
224     ASSERT_EQ(texture.Get(), nullptr);
225 }
226 
227 // Test an error occurs if the descriptor sample count isn't 1
TEST_P(D3D12SharedHandleValidation,InvalidSampleCount)228 TEST_P(D3D12SharedHandleValidation, InvalidSampleCount) {
229     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
230     baseDawnDescriptor.sampleCount = 4;
231 
232     wgpu::Texture texture;
233     ComPtr<ID3D11Texture2D> d3d11Texture;
234     ASSERT_DEVICE_ERROR(
235         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
236 
237     ASSERT_EQ(texture.Get(), nullptr);
238 }
239 
240 // Test an error occurs if the descriptor width doesn't match the texture's
TEST_P(D3D12SharedHandleValidation,InvalidWidth)241 TEST_P(D3D12SharedHandleValidation, InvalidWidth) {
242     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
243     baseDawnDescriptor.size.width = kTestWidth + 1;
244 
245     wgpu::Texture texture;
246     ComPtr<ID3D11Texture2D> d3d11Texture;
247     ASSERT_DEVICE_ERROR(
248         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
249 
250     ASSERT_EQ(texture.Get(), nullptr);
251 }
252 
253 // Test an error occurs if the descriptor height doesn't match the texture's
TEST_P(D3D12SharedHandleValidation,InvalidHeight)254 TEST_P(D3D12SharedHandleValidation, InvalidHeight) {
255     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
256     baseDawnDescriptor.size.height = kTestHeight + 1;
257 
258     wgpu::Texture texture;
259     ComPtr<ID3D11Texture2D> d3d11Texture;
260     ASSERT_DEVICE_ERROR(
261         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
262 
263     ASSERT_EQ(texture.Get(), nullptr);
264 }
265 
266 // Test an error occurs if the descriptor format isn't compatible with the D3D12 Resource
TEST_P(D3D12SharedHandleValidation,InvalidFormat)267 TEST_P(D3D12SharedHandleValidation, InvalidFormat) {
268     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
269     baseDawnDescriptor.format = wgpu::TextureFormat::R8Unorm;
270 
271     wgpu::Texture texture;
272     ComPtr<ID3D11Texture2D> d3d11Texture;
273     ASSERT_DEVICE_ERROR(
274         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
275 
276     ASSERT_EQ(texture.Get(), nullptr);
277 }
278 
279 // Test an error occurs if the number of D3D mip levels is greater than 1.
TEST_P(D3D12SharedHandleValidation,InvalidNumD3DMipLevels)280 TEST_P(D3D12SharedHandleValidation, InvalidNumD3DMipLevels) {
281     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
282     baseD3dDescriptor.MipLevels = 2;
283 
284     wgpu::Texture texture;
285     ComPtr<ID3D11Texture2D> d3d11Texture;
286     ASSERT_DEVICE_ERROR(
287         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
288 
289     ASSERT_EQ(texture.Get(), nullptr);
290 }
291 
292 // Test an error occurs if the number of array levels is greater than 1.
TEST_P(D3D12SharedHandleValidation,InvalidD3DArraySize)293 TEST_P(D3D12SharedHandleValidation, InvalidD3DArraySize) {
294     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
295     baseD3dDescriptor.ArraySize = 2;
296 
297     wgpu::Texture texture;
298     ComPtr<ID3D11Texture2D> d3d11Texture;
299     ASSERT_DEVICE_ERROR(
300         WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture));
301 
302     ASSERT_EQ(texture.Get(), nullptr);
303 }
304 
305 class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase {
306   protected:
307     // Submits a 1x1x1 copy from source to destination
SimpleCopyTextureToTexture(wgpu::Texture source,wgpu::Texture destination)308     void SimpleCopyTextureToTexture(wgpu::Texture source, wgpu::Texture destination) {
309         wgpu::ImageCopyTexture copySrc = utils::CreateImageCopyTexture(source, 0, {0, 0, 0});
310         wgpu::ImageCopyTexture copyDst = utils::CreateImageCopyTexture(destination, 0, {0, 0, 0});
311 
312         wgpu::Extent3D copySize = {1, 1, 1};
313 
314         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
315         encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
316         wgpu::CommandBuffer commands = encoder.Finish();
317 
318         queue.Submit(1, &commands);
319     }
320 
321     // Clear a texture on a given device
ClearImage(wgpu::Texture wrappedTexture,const wgpu::Color & clearColor,wgpu::Device wgpuDevice)322     void ClearImage(wgpu::Texture wrappedTexture,
323                     const wgpu::Color& clearColor,
324                     wgpu::Device wgpuDevice) {
325         wgpu::TextureView wrappedView = wrappedTexture.CreateView();
326 
327         // Submit a clear operation
328         utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
329         renderPassDescriptor.cColorAttachments[0].clearColor = clearColor;
330 
331         wgpu::CommandEncoder encoder = wgpuDevice.CreateCommandEncoder();
332         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
333         pass.EndPass();
334 
335         wgpu::CommandBuffer commands = encoder.Finish();
336         wgpu::Queue queue = wgpuDevice.GetQueue();
337         queue.Submit(1, &commands);
338     }
339 
WrapAndClearD3D11Texture(const wgpu::TextureDescriptor * dawnDescriptor,const D3D11_TEXTURE2D_DESC * d3dDescriptor,wgpu::Texture * dawnTextureOut,const wgpu::Color & clearColor,ID3D11Texture2D ** d3d11TextureOut,IDXGIKeyedMutex ** dxgiKeyedMutexOut,bool isInitialized=true) const340     void WrapAndClearD3D11Texture(const wgpu::TextureDescriptor* dawnDescriptor,
341                                   const D3D11_TEXTURE2D_DESC* d3dDescriptor,
342                                   wgpu::Texture* dawnTextureOut,
343                                   const wgpu::Color& clearColor,
344                                   ID3D11Texture2D** d3d11TextureOut,
345                                   IDXGIKeyedMutex** dxgiKeyedMutexOut,
346                                   bool isInitialized = true) const {
347         ComPtr<ID3D11Texture2D> d3d11Texture;
348         HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture);
349         ASSERT_EQ(hr, S_OK);
350 
351         ComPtr<IDXGIResource1> dxgiResource;
352         hr = d3d11Texture.As(&dxgiResource);
353         ASSERT_EQ(hr, S_OK);
354 
355         HANDLE sharedHandle;
356         hr = dxgiResource->CreateSharedHandle(
357             nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
358             &sharedHandle);
359         ASSERT_EQ(hr, S_OK);
360 
361         ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
362         hr = d3d11Texture.As(&dxgiKeyedMutex);
363         ASSERT_EQ(hr, S_OK);
364 
365         ComPtr<ID3D11RenderTargetView> d3d11RTV;
366         hr = mD3d11Device->CreateRenderTargetView(d3d11Texture.Get(), nullptr, &d3d11RTV);
367         ASSERT_EQ(hr, S_OK);
368 
369         hr = dxgiKeyedMutex->AcquireSync(0, INFINITE);
370         ASSERT_EQ(hr, S_OK);
371 
372         const float colorRGBA[] = {
373             static_cast<float>(clearColor.r), static_cast<float>(clearColor.g),
374             static_cast<float>(clearColor.b), static_cast<float>(clearColor.a)};
375         mD3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), colorRGBA);
376 
377         hr = dxgiKeyedMutex->ReleaseSync(1);
378         ASSERT_EQ(hr, S_OK);
379 
380         dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externalImageDesc = {};
381         externalImageDesc.sharedHandle = sharedHandle;
382         externalImageDesc.cTextureDescriptor =
383             reinterpret_cast<const WGPUTextureDescriptor*>(dawnDescriptor);
384 
385         std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage =
386             dawn_native::d3d12::ExternalImageDXGI::Create(device.Get(), &externalImageDesc);
387 
388         dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
389         externalAccessDesc.acquireMutexKey = 1;
390         externalAccessDesc.releaseMutexKey = 2;
391         externalAccessDesc.isInitialized = isInitialized;
392         externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(dawnDescriptor->usage);
393 
394         *dawnTextureOut = wgpu::Texture::Acquire(
395             externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
396         *d3d11TextureOut = d3d11Texture.Detach();
397         *dxgiKeyedMutexOut = dxgiKeyedMutex.Detach();
398     }
399 
ExpectPixelRGBA8EQ(UINT64 acquireKey,ID3D11Texture2D * d3d11Texture,IDXGIKeyedMutex * dxgiKeyedMutex,const wgpu::Color & color)400     void ExpectPixelRGBA8EQ(UINT64 acquireKey,
401                             ID3D11Texture2D* d3d11Texture,
402                             IDXGIKeyedMutex* dxgiKeyedMutex,
403                             const wgpu::Color& color) {
404         HRESULT hr = dxgiKeyedMutex->AcquireSync(acquireKey, INFINITE);
405         ASSERT_EQ(hr, S_OK);
406 
407         D3D11_TEXTURE2D_DESC texture2DDesc;
408         d3d11Texture->GetDesc(&texture2DDesc);
409 
410         const CD3D11_TEXTURE2D_DESC texture2DStagingDesc(
411             texture2DDesc.Format,                             // Format
412             texture2DDesc.Width,                              // Width
413             texture2DDesc.Height,                             // Height
414             1,                                                // ArraySize
415             1,                                                // MipLevels
416             0,                                                // BindFlags
417             D3D11_USAGE_STAGING,                              // Usage
418             D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);  // CPUAccessFlags
419 
420         ComPtr<ID3D11Texture2D> spD3DTextureStaging;
421         hr = mD3d11Device->CreateTexture2D(&texture2DStagingDesc, nullptr, &spD3DTextureStaging);
422         ASSERT_EQ(hr, S_OK);
423 
424         D3D11_BOX d3dRc;
425         d3dRc.back = 1;
426         d3dRc.front = 0;
427         d3dRc.top = 0;
428         d3dRc.left = 0;
429         d3dRc.bottom = texture2DDesc.Height;
430         d3dRc.right = texture2DDesc.Width;
431 
432         mD3d11DeviceContext->CopySubresourceRegion(spD3DTextureStaging.Get(),  // pDstResource
433                                                    0,                          // DstSubresource
434                                                    0,                          // DstX
435                                                    0,                          // DstY
436                                                    0,                          // DstZ
437                                                    d3d11Texture,               // pSrcResource
438                                                    0,                          // SrcSubresource
439                                                    &d3dRc);                    // pSrcBox
440 
441         D3D11_MAPPED_SUBRESOURCE mappedResource;
442         hr = mD3d11DeviceContext->Map(spD3DTextureStaging.Get(), 0, D3D11_MAP_READ_WRITE, 0,
443                                       &mappedResource);
444         ASSERT_EQ(hr, S_OK);
445 
446         const uint8_t* colorData = static_cast<uint8_t*>(mappedResource.pData);
447         EXPECT_EQ(colorData[0], color.r * 255u);
448         EXPECT_EQ(colorData[1], color.g * 255u);
449         EXPECT_EQ(colorData[2], color.b * 255u);
450         EXPECT_EQ(colorData[3], color.a * 255u);
451 
452         mD3d11DeviceContext->Unmap(spD3DTextureStaging.Get(), 0);
453 
454         hr = dxgiKeyedMutex->ReleaseSync(acquireKey + 1);
455         ASSERT_EQ(hr, S_OK);
456     }
457 };
458 
459 // 1. Create and clear a D3D11 texture
460 // 2. Copy the wrapped texture to another dawn texture
461 // 3. Readback the copied texture and ensure the color matches the original clear color.
TEST_P(D3D12SharedHandleUsageTests,ClearInD3D11CopyAndReadbackInD3D12)462 TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11CopyAndReadbackInD3D12) {
463     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
464 
465     const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f};
466     wgpu::Texture dawnSrcTexture;
467     ComPtr<ID3D11Texture2D> d3d11Texture;
468     ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
469     WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnSrcTexture, clearColor,
470                              &d3d11Texture, &dxgiKeyedMutex);
471     ASSERT_NE(dawnSrcTexture.Get(), nullptr);
472 
473     // Create a texture on the device and copy the source texture to it.
474     wgpu::Texture dawnCopyDestTexture = device.CreateTexture(&baseDawnDescriptor);
475     SimpleCopyTextureToTexture(dawnSrcTexture, dawnCopyDestTexture);
476 
477     // Readback the destination texture and ensure it contains the colors we used
478     // to clear the source texture on the D3D device.
479     EXPECT_PIXEL_RGBA8_EQ(
480         RGBA8(clearColor.r * 255u, clearColor.g * 255u, clearColor.b * 255u, clearColor.a * 255u),
481         dawnCopyDestTexture, 0, 0);
482 }
483 
484 // 1. Create and clear a D3D11 texture
485 // 2. Readback the wrapped texture and ensure the color matches the original clear color.
TEST_P(D3D12SharedHandleUsageTests,ClearInD3D11ReadbackInD3D12)486 TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11ReadbackInD3D12) {
487     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
488 
489     const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f};
490     wgpu::Texture dawnTexture;
491     ComPtr<ID3D11Texture2D> d3d11Texture;
492     ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
493     WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor,
494                              &d3d11Texture, &dxgiKeyedMutex);
495     ASSERT_NE(dawnTexture.Get(), nullptr);
496 
497     // Readback the destination texture and ensure it contains the colors we used
498     // to clear the source texture on the D3D device.
499     EXPECT_PIXEL_RGBA8_EQ(
500         RGBA8(clearColor.r * 255, clearColor.g * 255, clearColor.b * 255, clearColor.a * 255),
501         dawnTexture, 0, 0);
502 }
503 
504 // 1. Create and clear a D3D11 texture
505 // 2. Wrap it in a Dawn texture and clear it to a different color
506 // 3. Readback the texture with D3D11 and ensure we receive the color we cleared with Dawn.
TEST_P(D3D12SharedHandleUsageTests,ClearInD3D12ReadbackInD3D11)507 TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) {
508     // TODO(crbug.com/dawn/735): This test appears to hang for
509     // D3D12_Microsoft_Basic_Render_Driver_CPU when validation is enabled.
510     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP() && IsBackendValidationEnabled());
511 
512     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
513 
514     const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f};
515     wgpu::Texture dawnTexture;
516     ComPtr<ID3D11Texture2D> d3d11Texture;
517     ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
518     WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor,
519                              &d3d11Texture, &dxgiKeyedMutex);
520     ASSERT_NE(dawnTexture.Get(), nullptr);
521 
522     const wgpu::Color d3d12ClearColor{0.0f, 0.0f, 1.0f, 1.0f};
523     ClearImage(dawnTexture, d3d12ClearColor, device);
524 
525     dawnTexture.Destroy();
526 
527     // Now that Dawn (via D3D12) has finished writing to the texture, we should be
528     // able to read it back by copying it to a staging texture and verifying the
529     // color matches the D3D12 clear color.
530     ExpectPixelRGBA8EQ(2, d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor);
531 }
532 
533 // 1. Create and clear a D3D11 texture
534 // 2. Wrap it in a Dawn texture and clear the texture to two different colors.
535 // 3. Readback the texture with D3D11.
536 // 4. Verify the readback color was the final color cleared.
TEST_P(D3D12SharedHandleUsageTests,ClearTwiceInD3D12ReadbackInD3D11)537 TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) {
538     // TODO(crbug.com/dawn/735): This test appears to hang for
539     // D3D12_Microsoft_Basic_Render_Driver_CPU when validation is enabled.
540     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP() && IsBackendValidationEnabled());
541 
542     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
543 
544     const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f};
545     wgpu::Texture dawnTexture;
546     ComPtr<ID3D11Texture2D> d3d11Texture;
547     ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
548     WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor,
549                              &d3d11Texture, &dxgiKeyedMutex);
550     ASSERT_NE(dawnTexture.Get(), nullptr);
551 
552     const wgpu::Color d3d12ClearColor1{0.0f, 0.0f, 1.0f, 1.0f};
553     ClearImage(dawnTexture, d3d12ClearColor1, device);
554 
555     const wgpu::Color d3d12ClearColor2{0.0f, 1.0f, 1.0f, 1.0f};
556     ClearImage(dawnTexture, d3d12ClearColor2, device);
557 
558     dawnTexture.Destroy();
559 
560     // Now that Dawn (via D3D12) has finished writing to the texture, we should be
561     // able to read it back by copying it to a staging texture and verifying the
562     // color matches the last D3D12 clear color.
563     ExpectPixelRGBA8EQ(2, d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor2);
564 }
565 
566 // 1. Create and clear a D3D11 texture with clearColor
567 // 2. Import the texture with isInitialized = false
568 // 3. Verify clearColor is not visible in wrapped texture
TEST_P(D3D12SharedHandleUsageTests,UninitializedTextureIsCleared)569 TEST_P(D3D12SharedHandleUsageTests, UninitializedTextureIsCleared) {
570     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
571 
572     const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f};
573     wgpu::Texture dawnTexture;
574     ComPtr<ID3D11Texture2D> d3d11Texture;
575     ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
576     WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor,
577                              &d3d11Texture, &dxgiKeyedMutex, false);
578     ASSERT_NE(dawnTexture.Get(), nullptr);
579 
580     // Readback the destination texture and ensure it contains the colors we used
581     // to clear the source texture on the D3D device.
582     EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), dawnTexture, 0, 0);
583 }
584 
585 // 1. Create an external image from the DX11 texture.
586 // 2. Produce two Dawn textures from the external image.
587 // 3. Clear each Dawn texture and verify the texture was cleared to a unique color.
TEST_P(D3D12SharedHandleUsageTests,ReuseExternalImage)588 TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImage) {
589     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
590 
591     // Create the first Dawn texture then clear it to red.
592     wgpu::Texture texture;
593     ComPtr<ID3D11Texture2D> d3d11Texture;
594     std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage;
595     WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
596                      &externalImage);
597     {
598         const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f};
599         ASSERT_NE(texture.Get(), nullptr);
600         ClearImage(texture.Get(), solidRed, device);
601 
602         EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
603     }
604 
605     // Once finished with the first texture, destroy it so we may re-acquire the external image
606     // again.
607     texture.Destroy();
608 
609     // Create another Dawn texture then clear it with another color.
610     dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
611     externalAccessDesc.acquireMutexKey = 1;
612     externalAccessDesc.releaseMutexKey = 2;
613     externalAccessDesc.isInitialized = true;
614     externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
615 
616     texture =
617         wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
618 
619     // Check again that the new texture is still red
620     EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
621 
622     // Clear the new texture to blue
623     {
624         const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f};
625         ASSERT_NE(texture.Get(), nullptr);
626         ClearImage(texture.Get(), solidBlue, device);
627 
628         EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0);
629     }
630 }
631 
632 // Produce a new texture with a usage not specified in the external image.
TEST_P(D3D12SharedHandleUsageTests,ExternalImageUsage)633 TEST_P(D3D12SharedHandleUsageTests, ExternalImageUsage) {
634     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
635 
636     dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
637     externalAccessDesc.acquireMutexKey = 1;
638     externalAccessDesc.releaseMutexKey = 2;
639     externalAccessDesc.isInitialized = true;
640 
641     wgpu::Texture texture;
642     ComPtr<ID3D11Texture2D> d3d11Texture;
643     std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage;
644     WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
645                      &externalImage);
646     ASSERT_NE(texture.Get(), nullptr);
647 
648     externalAccessDesc.usage = WGPUTextureUsage_StorageBinding;
649     texture =
650         wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
651     ASSERT_EQ(texture.Get(), nullptr);
652 
653     externalAccessDesc.usage = WGPUTextureUsage_TextureBinding;
654     texture =
655         wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
656     ASSERT_NE(texture.Get(), nullptr);
657 }
658 
659 // Verify two Dawn devices can reuse the same external image.
TEST_P(D3D12SharedHandleUsageTests,ReuseExternalImageWithMultipleDevices)660 TEST_P(D3D12SharedHandleUsageTests, ReuseExternalImageWithMultipleDevices) {
661     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
662 
663     wgpu::Texture texture;
664     ComPtr<ID3D11Texture2D> d3d11Texture;
665     std::unique_ptr<dawn_native::d3d12::ExternalImageDXGI> externalImage;
666 
667     // Create the Dawn texture then clear it to red using the first (default) device.
668     WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture,
669                      &externalImage);
670     const wgpu::Color solidRed{1.0f, 0.0f, 0.0f, 1.0f};
671     ASSERT_NE(texture.Get(), nullptr);
672     ClearImage(texture.Get(), solidRed, device);
673 
674     EXPECT_PIXEL_RGBA8_EQ(RGBA8(0xFF, 0, 0, 0xFF), texture.Get(), 0, 0);
675 
676     // Release the texture so we can re-acquire another one from the same external image.
677     texture.Destroy();
678 
679     // Create the Dawn texture then clear it to blue using the second device.
680     dawn_native::d3d12::ExternalImageAccessDescriptorDXGIKeyedMutex externalAccessDesc;
681     externalAccessDesc.acquireMutexKey = 1;
682     externalAccessDesc.releaseMutexKey = 2;
683     externalAccessDesc.usage = static_cast<WGPUTextureUsageFlags>(baseDawnDescriptor.usage);
684 
685     wgpu::Device otherDevice = wgpu::Device::Acquire(GetAdapter().CreateDevice());
686 
687     wgpu::Texture otherTexture = wgpu::Texture::Acquire(
688         externalImage->ProduceTexture(otherDevice.Get(), &externalAccessDesc));
689 
690     ASSERT_NE(otherTexture.Get(), nullptr);
691     const wgpu::Color solidBlue{0.0f, 0.0f, 1.0f, 1.0f};
692     ClearImage(otherTexture.Get(), solidBlue, otherDevice);
693 
694     otherTexture.Destroy();
695 
696     // Re-create the Dawn texture using the first (default) device.
697     externalAccessDesc.acquireMutexKey = 2;
698     externalAccessDesc.isInitialized = true;
699     texture =
700         wgpu::Texture::Acquire(externalImage->ProduceTexture(device.Get(), &externalAccessDesc));
701     ASSERT_NE(texture.Get(), nullptr);
702 
703     // Ensure the texture is still blue.
704 
705     EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0xFF, 0xFF), texture.Get(), 0, 0);
706 }
707 
708 DAWN_INSTANTIATE_TEST(D3D12SharedHandleValidation, D3D12Backend());
709 DAWN_INSTANTIATE_TEST(D3D12SharedHandleUsageTests, D3D12Backend());
710