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 "dawn_native/MetalBackend.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20
21 #include <CoreFoundation/CoreFoundation.h>
22 #include <CoreVideo/CVPixelBuffer.h>
23 #include <IOSurface/IOSurface.h>
24
25 namespace {
26
AddIntegerValue(CFMutableDictionaryRef dictionary,const CFStringRef key,int32_t value)27 void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) {
28 CFNumberRef number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
29 CFDictionaryAddValue(dictionary, key, number);
30 CFRelease(number);
31 }
32
33 class ScopedIOSurfaceRef {
34 public:
ScopedIOSurfaceRef()35 ScopedIOSurfaceRef() : mSurface(nullptr) {
36 }
ScopedIOSurfaceRef(IOSurfaceRef surface)37 explicit ScopedIOSurfaceRef(IOSurfaceRef surface) : mSurface(surface) {
38 }
39
~ScopedIOSurfaceRef()40 ~ScopedIOSurfaceRef() {
41 if (mSurface != nullptr) {
42 CFRelease(mSurface);
43 mSurface = nullptr;
44 }
45 }
46
get() const47 IOSurfaceRef get() const {
48 return mSurface;
49 }
50
ScopedIOSurfaceRef(ScopedIOSurfaceRef && other)51 ScopedIOSurfaceRef(ScopedIOSurfaceRef&& other) {
52 if (mSurface != nullptr) {
53 CFRelease(mSurface);
54 }
55 mSurface = other.mSurface;
56 other.mSurface = nullptr;
57 }
58
operator =(ScopedIOSurfaceRef && other)59 ScopedIOSurfaceRef& operator=(ScopedIOSurfaceRef&& other) {
60 if (mSurface != nullptr) {
61 CFRelease(mSurface);
62 }
63 mSurface = other.mSurface;
64 other.mSurface = nullptr;
65
66 return *this;
67 }
68
69 ScopedIOSurfaceRef(const ScopedIOSurfaceRef&) = delete;
70 ScopedIOSurfaceRef& operator=(const ScopedIOSurfaceRef&) = delete;
71
72 private:
73 IOSurfaceRef mSurface = nullptr;
74 };
75
CreateSinglePlaneIOSurface(uint32_t width,uint32_t height,uint32_t format,uint32_t bytesPerElement)76 ScopedIOSurfaceRef CreateSinglePlaneIOSurface(uint32_t width,
77 uint32_t height,
78 uint32_t format,
79 uint32_t bytesPerElement) {
80 CFMutableDictionaryRef dict =
81 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
82 &kCFTypeDictionaryValueCallBacks);
83 AddIntegerValue(dict, kIOSurfaceWidth, width);
84 AddIntegerValue(dict, kIOSurfaceHeight, height);
85 AddIntegerValue(dict, kIOSurfacePixelFormat, format);
86 AddIntegerValue(dict, kIOSurfaceBytesPerElement, bytesPerElement);
87
88 IOSurfaceRef ioSurface = IOSurfaceCreate(dict);
89 EXPECT_NE(nullptr, ioSurface);
90 CFRelease(dict);
91
92 return ScopedIOSurfaceRef(ioSurface);
93 }
94
95 class IOSurfaceTestBase : public DawnTest {
96 public:
WrapIOSurface(const wgpu::TextureDescriptor * descriptor,IOSurfaceRef ioSurface,uint32_t plane,bool isInitialized=true)97 wgpu::Texture WrapIOSurface(const wgpu::TextureDescriptor* descriptor,
98 IOSurfaceRef ioSurface,
99 uint32_t plane,
100 bool isInitialized = true) {
101 dawn_native::metal::ExternalImageDescriptorIOSurface externDesc;
102 externDesc.cTextureDescriptor =
103 reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
104 externDesc.ioSurface = ioSurface;
105 externDesc.plane = plane;
106 externDesc.isInitialized = isInitialized;
107 WGPUTexture texture = dawn_native::metal::WrapIOSurface(device.Get(), &externDesc);
108 return wgpu::Texture::Acquire(texture);
109 }
110 };
111
112 } // anonymous namespace
113
114 // A small fixture used to initialize default data for the IOSurface validation tests.
115 // These tests are skipped if the harness is using the wire.
116 class IOSurfaceValidationTests : public IOSurfaceTestBase {
117 public:
IOSurfaceValidationTests()118 IOSurfaceValidationTests() {
119 defaultIOSurface = CreateSinglePlaneIOSurface(10, 10, kCVPixelFormatType_32BGRA, 4);
120
121 descriptor.dimension = wgpu::TextureDimension::e2D;
122 descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
123 descriptor.size = {10, 10, 1};
124 descriptor.sampleCount = 1;
125 descriptor.mipLevelCount = 1;
126 descriptor.usage = wgpu::TextureUsage::RenderAttachment;
127 }
128
129 protected:
130 wgpu::TextureDescriptor descriptor;
131 ScopedIOSurfaceRef defaultIOSurface;
132 };
133
134 // Test a successful wrapping of an IOSurface in a texture
TEST_P(IOSurfaceValidationTests,Success)135 TEST_P(IOSurfaceValidationTests, Success) {
136 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
137 wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0);
138 ASSERT_NE(texture.Get(), nullptr);
139 }
140
141 // Test an error occurs if the texture descriptor is invalid
TEST_P(IOSurfaceValidationTests,InvalidTextureDescriptor)142 TEST_P(IOSurfaceValidationTests, InvalidTextureDescriptor) {
143 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
144
145 wgpu::ChainedStruct chainedDescriptor;
146 descriptor.nextInChain = &chainedDescriptor;
147
148 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
149 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
150 ASSERT_EQ(texture.Get(), nullptr);
151 }
152
153 // Test an error occurs if the plane is too large
TEST_P(IOSurfaceValidationTests,PlaneTooLarge)154 TEST_P(IOSurfaceValidationTests, PlaneTooLarge) {
155 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
156 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
157 WrapIOSurface(&descriptor, defaultIOSurface.get(), 1));
158 ASSERT_EQ(texture.Get(), nullptr);
159 }
160
161 // Test an error occurs if the descriptor dimension isn't 2D
162 // TODO(crbug.com/dawn/814): Test 1D textures when implemented
TEST_P(IOSurfaceValidationTests,InvalidTextureDimension)163 TEST_P(IOSurfaceValidationTests, InvalidTextureDimension) {
164 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
165 descriptor.dimension = wgpu::TextureDimension::e3D;
166
167 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
168 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
169 ASSERT_EQ(texture.Get(), nullptr);
170 }
171
172 // Test an error occurs if the descriptor mip level count isn't 1
TEST_P(IOSurfaceValidationTests,InvalidMipLevelCount)173 TEST_P(IOSurfaceValidationTests, InvalidMipLevelCount) {
174 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
175 descriptor.mipLevelCount = 2;
176
177 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
178 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
179 ASSERT_EQ(texture.Get(), nullptr);
180 }
181
182 // Test an error occurs if the descriptor depth isn't 1
TEST_P(IOSurfaceValidationTests,InvalidDepth)183 TEST_P(IOSurfaceValidationTests, InvalidDepth) {
184 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
185 descriptor.size.depthOrArrayLayers = 2;
186
187 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
188 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
189 ASSERT_EQ(texture.Get(), nullptr);
190 }
191
192 // Test an error occurs if the descriptor sample count isn't 1
TEST_P(IOSurfaceValidationTests,InvalidSampleCount)193 TEST_P(IOSurfaceValidationTests, InvalidSampleCount) {
194 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
195 descriptor.sampleCount = 4;
196
197 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
198 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
199 ASSERT_EQ(texture.Get(), nullptr);
200 }
201
202 // Test an error occurs if the descriptor width doesn't match the surface's
TEST_P(IOSurfaceValidationTests,InvalidWidth)203 TEST_P(IOSurfaceValidationTests, InvalidWidth) {
204 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
205 descriptor.size.width = 11;
206
207 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
208 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
209 ASSERT_EQ(texture.Get(), nullptr);
210 }
211
212 // Test an error occurs if the descriptor height doesn't match the surface's
TEST_P(IOSurfaceValidationTests,InvalidHeight)213 TEST_P(IOSurfaceValidationTests, InvalidHeight) {
214 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
215 descriptor.size.height = 11;
216
217 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
218 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
219 ASSERT_EQ(texture.Get(), nullptr);
220 }
221
222 // Test an error occurs if the descriptor format isn't compatible with the IOSurface's
TEST_P(IOSurfaceValidationTests,InvalidFormat)223 TEST_P(IOSurfaceValidationTests, InvalidFormat) {
224 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
225 descriptor.format = wgpu::TextureFormat::R8Unorm;
226
227 ASSERT_DEVICE_ERROR(wgpu::Texture texture =
228 WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
229 ASSERT_EQ(texture.Get(), nullptr);
230 }
231
232 // Fixture to test using IOSurfaces through different usages.
233 // These tests are skipped if the harness is using the wire.
234 class IOSurfaceUsageTests : public IOSurfaceTestBase {
235 public:
236 // Test that sampling a 1x1 works.
DoSampleTest(IOSurfaceRef ioSurface,wgpu::TextureFormat format,void * data,size_t dataSize,RGBA8 expectedColor)237 void DoSampleTest(IOSurfaceRef ioSurface,
238 wgpu::TextureFormat format,
239 void* data,
240 size_t dataSize,
241 RGBA8 expectedColor) {
242 // Write the data to the IOSurface
243 IOSurfaceLock(ioSurface, 0, nullptr);
244 memcpy(IOSurfaceGetBaseAddress(ioSurface), data, dataSize);
245 IOSurfaceUnlock(ioSurface, 0, nullptr);
246
247 // The simplest texture sampling pipeline.
248 wgpu::RenderPipeline pipeline;
249 {
250 wgpu::ShaderModule vs = utils::CreateShaderModule(device, R"(
251 struct VertexOut {
252 [[location(0)]] texCoord : vec2<f32>;
253 [[builtin(position)]] position : vec4<f32>;
254 };
255
256 [[stage(vertex)]]
257 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
258 var pos = array<vec2<f32>, 6>(
259 vec2<f32>(-2.0, -2.0),
260 vec2<f32>(-2.0, 2.0),
261 vec2<f32>( 2.0, -2.0),
262 vec2<f32>(-2.0, 2.0),
263 vec2<f32>( 2.0, -2.0),
264 vec2<f32>( 2.0, 2.0));
265
266 var texCoord = array<vec2<f32>, 6>(
267 vec2<f32>(0.0, 0.0),
268 vec2<f32>(0.0, 1.0),
269 vec2<f32>(1.0, 0.0),
270 vec2<f32>(0.0, 1.0),
271 vec2<f32>(1.0, 0.0),
272 vec2<f32>(1.0, 1.0));
273
274 var output : VertexOut;
275 output.position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
276 output.texCoord = texCoord[VertexIndex];
277 return output;
278 }
279 )");
280 wgpu::ShaderModule fs = utils::CreateShaderModule(device, R"(
281 [[group(0), binding(0)]] var sampler0 : sampler;
282 [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
283
284 [[stage(fragment)]]
285 fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
286 return textureSample(texture0, sampler0, texCoord);
287 }
288 )");
289
290 utils::ComboRenderPipelineDescriptor descriptor;
291 descriptor.vertex.module = vs;
292 descriptor.cFragment.module = fs;
293 descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
294
295 pipeline = device.CreateRenderPipeline(&descriptor);
296 }
297
298 // The bindgroup containing the texture view for the ioSurface as well as the sampler.
299 wgpu::BindGroup bindGroup;
300 {
301 wgpu::TextureDescriptor textureDescriptor;
302 textureDescriptor.dimension = wgpu::TextureDimension::e2D;
303 textureDescriptor.format = format;
304 textureDescriptor.size = {1, 1, 1};
305 textureDescriptor.sampleCount = 1;
306 textureDescriptor.mipLevelCount = 1;
307 textureDescriptor.usage = wgpu::TextureUsage::TextureBinding;
308 wgpu::Texture wrappingTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0);
309
310 wgpu::TextureView textureView = wrappingTexture.CreateView();
311
312 wgpu::Sampler sampler = device.CreateSampler();
313
314 bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
315 {{0, sampler}, {1, textureView}});
316 }
317
318 // Submit commands samping from the ioSurface and writing the result to renderPass.color
319 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
320 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
321 {
322 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
323 pass.SetPipeline(pipeline);
324 pass.SetBindGroup(0, bindGroup);
325 pass.Draw(6);
326 pass.EndPass();
327 }
328
329 wgpu::CommandBuffer commands = encoder.Finish();
330 queue.Submit(1, &commands);
331
332 EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0);
333 }
334
335 // Test that clearing using BeginRenderPass writes correct data in the ioSurface.
DoClearTest(IOSurfaceRef ioSurface,wgpu::TextureFormat format,void * data,size_t dataSize)336 void DoClearTest(IOSurfaceRef ioSurface,
337 wgpu::TextureFormat format,
338 void* data,
339 size_t dataSize) {
340 // Get a texture view for the ioSurface
341 wgpu::TextureDescriptor textureDescriptor;
342 textureDescriptor.dimension = wgpu::TextureDimension::e2D;
343 textureDescriptor.format = format;
344 textureDescriptor.size = {1, 1, 1};
345 textureDescriptor.sampleCount = 1;
346 textureDescriptor.mipLevelCount = 1;
347 textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
348 wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0);
349
350 wgpu::TextureView ioSurfaceView = ioSurfaceTexture.CreateView();
351
352 utils::ComboRenderPassDescriptor renderPassDescriptor({ioSurfaceView}, {});
353 renderPassDescriptor.cColorAttachments[0].clearColor = {1 / 255.0f, 2 / 255.0f, 3 / 255.0f,
354 4 / 255.0f};
355
356 // Execute commands to clear the ioSurface
357 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
358 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
359 pass.EndPass();
360
361 wgpu::CommandBuffer commands = encoder.Finish();
362 queue.Submit(1, &commands);
363
364 // Wait for the commands touching the IOSurface to be scheduled
365 dawn_native::metal::WaitForCommandsToBeScheduled(device.Get());
366
367 // Check the correct data was written
368 IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nullptr);
369 ASSERT_EQ(0, memcmp(IOSurfaceGetBaseAddress(ioSurface), data, dataSize));
370 IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nullptr);
371 }
372 };
373
374 // Test sampling from a R8 IOSurface
TEST_P(IOSurfaceUsageTests,SampleFromR8IOSurface)375 TEST_P(IOSurfaceUsageTests, SampleFromR8IOSurface) {
376 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
377 ScopedIOSurfaceRef ioSurface =
378 CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_OneComponent8, 1);
379
380 uint8_t data = 0x01;
381 DoSampleTest(ioSurface.get(), wgpu::TextureFormat::R8Unorm, &data, sizeof(data),
382 RGBA8(1, 0, 0, 255));
383 }
384
385 // Test clearing a R8 IOSurface
TEST_P(IOSurfaceUsageTests,ClearR8IOSurface)386 TEST_P(IOSurfaceUsageTests, ClearR8IOSurface) {
387 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
388 ScopedIOSurfaceRef ioSurface =
389 CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_OneComponent8, 1);
390
391 uint8_t data = 0x01;
392 DoClearTest(ioSurface.get(), wgpu::TextureFormat::R8Unorm, &data, sizeof(data));
393 }
394
395 // Test sampling from a RG8 IOSurface
TEST_P(IOSurfaceUsageTests,SampleFromRG8IOSurface)396 TEST_P(IOSurfaceUsageTests, SampleFromRG8IOSurface) {
397 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
398 ScopedIOSurfaceRef ioSurface =
399 CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_TwoComponent8, 2);
400
401 uint16_t data = 0x0102; // Stored as (G, R)
402 DoSampleTest(ioSurface.get(), wgpu::TextureFormat::RG8Unorm, &data, sizeof(data),
403 RGBA8(2, 1, 0, 255));
404 }
405
406 // Test clearing a RG8 IOSurface
TEST_P(IOSurfaceUsageTests,ClearRG8IOSurface)407 TEST_P(IOSurfaceUsageTests, ClearRG8IOSurface) {
408 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
409 ScopedIOSurfaceRef ioSurface =
410 CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_TwoComponent8, 2);
411
412 uint16_t data = 0x0201;
413 DoClearTest(ioSurface.get(), wgpu::TextureFormat::RG8Unorm, &data, sizeof(data));
414 }
415
416 // Test sampling from a BGRA8 IOSurface
TEST_P(IOSurfaceUsageTests,SampleFromBGRA8IOSurface)417 TEST_P(IOSurfaceUsageTests, SampleFromBGRA8IOSurface) {
418 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
419 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32BGRA, 4);
420
421 uint32_t data = 0x01020304; // Stored as (A, R, G, B)
422 DoSampleTest(ioSurface.get(), wgpu::TextureFormat::BGRA8Unorm, &data, sizeof(data),
423 RGBA8(2, 3, 4, 1));
424 }
425
426 // Test clearing a BGRA8 IOSurface
TEST_P(IOSurfaceUsageTests,ClearBGRA8IOSurface)427 TEST_P(IOSurfaceUsageTests, ClearBGRA8IOSurface) {
428 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
429 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32BGRA, 4);
430
431 uint32_t data = 0x04010203;
432 DoClearTest(ioSurface.get(), wgpu::TextureFormat::BGRA8Unorm, &data, sizeof(data));
433 }
434
435 // Test sampling from an RGBA8 IOSurface
TEST_P(IOSurfaceUsageTests,SampleFromRGBA8IOSurface)436 TEST_P(IOSurfaceUsageTests, SampleFromRGBA8IOSurface) {
437 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
438 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4);
439
440 uint32_t data = 0x01020304; // Stored as (A, B, G, R)
441 DoSampleTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data),
442 RGBA8(4, 3, 2, 1));
443 }
444
445 // Test clearing an RGBA8 IOSurface
TEST_P(IOSurfaceUsageTests,ClearRGBA8IOSurface)446 TEST_P(IOSurfaceUsageTests, ClearRGBA8IOSurface) {
447 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
448 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4);
449
450 uint32_t data = 0x04030201;
451 DoClearTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data));
452 }
453
454 // Test that texture with color is cleared when isInitialized = false
TEST_P(IOSurfaceUsageTests,UninitializedTextureIsCleared)455 TEST_P(IOSurfaceUsageTests, UninitializedTextureIsCleared) {
456 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
457
458 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4);
459 uint32_t data = 0x04030201;
460
461 IOSurfaceLock(ioSurface.get(), 0, nullptr);
462 memcpy(IOSurfaceGetBaseAddress(ioSurface.get()), &data, sizeof(data));
463 IOSurfaceUnlock(ioSurface.get(), 0, nullptr);
464
465 wgpu::TextureDescriptor textureDescriptor;
466 textureDescriptor.dimension = wgpu::TextureDimension::e2D;
467 textureDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
468 textureDescriptor.size = {1, 1, 1};
469 textureDescriptor.sampleCount = 1;
470 textureDescriptor.mipLevelCount = 1;
471 textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
472
473 // wrap ioSurface and ensure color is not visible when isInitialized set to false
474 wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), 0, false);
475 EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), ioSurfaceTexture, 0, 0);
476 }
477
478 DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend());
479 DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend());
480