1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // EGLIOSurfaceClientBufferTest.cpp: tests for the EGL_ANGLE_iosurface_client_buffer extension.
7 //
8
9 #include "test_utils/ANGLETest.h"
10
11 #include "common/mathutil.h"
12 #include "test_utils/gl_raii.h"
13 #include "util/EGLWindow.h"
14
15 #include <CoreFoundation/CoreFoundation.h>
16 #include <IOSurface/IOSurface.h>
17
18 using namespace angle;
19
20 namespace
21 {
22
23 constexpr char kIOSurfaceExt[] = "EGL_ANGLE_iosurface_client_buffer";
24
AddIntegerValue(CFMutableDictionaryRef dictionary,const CFStringRef key,int32_t value)25 void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value)
26 {
27 CFNumberRef number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
28 CFDictionaryAddValue(dictionary, key, number);
29 CFRelease(number);
30 }
31
32 class ScopedIOSurfaceRef : angle::NonCopyable
33 {
34 public:
ScopedIOSurfaceRef(IOSurfaceRef surface)35 explicit ScopedIOSurfaceRef(IOSurfaceRef surface) : mSurface(surface) {}
36
~ScopedIOSurfaceRef()37 ~ScopedIOSurfaceRef()
38 {
39 if (mSurface != nullptr)
40 {
41 CFRelease(mSurface);
42 mSurface = nullptr;
43 }
44 }
45
get() const46 IOSurfaceRef get() const { return mSurface; }
47
ScopedIOSurfaceRef(ScopedIOSurfaceRef && other)48 ScopedIOSurfaceRef(ScopedIOSurfaceRef &&other)
49 {
50 if (mSurface != nullptr)
51 {
52 CFRelease(mSurface);
53 }
54 mSurface = other.mSurface;
55 other.mSurface = nullptr;
56 }
57
operator =(ScopedIOSurfaceRef && other)58 ScopedIOSurfaceRef &operator=(ScopedIOSurfaceRef &&other)
59 {
60 if (mSurface != nullptr)
61 {
62 CFRelease(mSurface);
63 }
64 mSurface = other.mSurface;
65 other.mSurface = nullptr;
66
67 return *this;
68 }
69
70 private:
71 IOSurfaceRef mSurface = nullptr;
72 };
73
CreateSinglePlaneIOSurface(int width,int height,int32_t format,int bytesPerElement)74 ScopedIOSurfaceRef CreateSinglePlaneIOSurface(int width,
75 int height,
76 int32_t format,
77 int bytesPerElement)
78 {
79 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
80 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
81 AddIntegerValue(dict, kIOSurfaceWidth, width);
82 AddIntegerValue(dict, kIOSurfaceHeight, height);
83 AddIntegerValue(dict, kIOSurfacePixelFormat, format);
84 AddIntegerValue(dict, kIOSurfaceBytesPerElement, bytesPerElement);
85
86 IOSurfaceRef ioSurface = IOSurfaceCreate(dict);
87 EXPECT_NE(nullptr, ioSurface);
88 CFRelease(dict);
89
90 return ScopedIOSurfaceRef(ioSurface);
91 }
92
93 } // anonymous namespace
94
95 class IOSurfaceClientBufferTest : public ANGLETest
96 {
97 protected:
getTextureTarget() const98 EGLint getTextureTarget() const
99 {
100 EGLint target = 0;
101 eglGetConfigAttrib(mDisplay, mConfig, EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &target);
102 return target;
103 }
104
getGLTextureTarget() const105 GLint getGLTextureTarget() const
106 {
107 EGLint targetEGL = getTextureTarget();
108 GLenum targetGL = 0;
109 switch (targetEGL)
110 {
111 case EGL_TEXTURE_2D:
112 targetGL = GL_TEXTURE_2D;
113 break;
114 case EGL_TEXTURE_RECTANGLE_ANGLE:
115 targetGL = GL_TEXTURE_RECTANGLE_ANGLE;
116 break;
117 default:
118 break;
119 }
120 return targetGL;
121 }
122
IOSurfaceClientBufferTest()123 IOSurfaceClientBufferTest() : mConfig(0), mDisplay(nullptr) {}
124
testSetUp()125 void testSetUp() override
126 {
127 mConfig = getEGLWindow()->getConfig();
128 mDisplay = getEGLWindow()->getDisplay();
129 }
130
createIOSurfacePbuffer(const ScopedIOSurfaceRef & ioSurface,EGLint width,EGLint height,EGLint plane,GLenum internalFormat,GLenum type,EGLSurface * pbuffer) const131 void createIOSurfacePbuffer(const ScopedIOSurfaceRef &ioSurface,
132 EGLint width,
133 EGLint height,
134 EGLint plane,
135 GLenum internalFormat,
136 GLenum type,
137 EGLSurface *pbuffer) const
138 {
139 // clang-format off
140 const EGLint attribs[] = {
141 EGL_WIDTH, width,
142 EGL_HEIGHT, height,
143 EGL_IOSURFACE_PLANE_ANGLE, plane,
144 EGL_TEXTURE_TARGET, getTextureTarget(),
145 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, internalFormat,
146 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
147 EGL_TEXTURE_TYPE_ANGLE, type,
148 EGL_NONE, EGL_NONE,
149 };
150 // clang-format on
151
152 *pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(),
153 mConfig, attribs);
154 EXPECT_NE(EGL_NO_SURFACE, *pbuffer);
155 }
156
bindIOSurfaceToTexture(const ScopedIOSurfaceRef & ioSurface,EGLint width,EGLint height,EGLint plane,GLenum internalFormat,GLenum type,EGLSurface * pbuffer,GLTexture * texture) const157 void bindIOSurfaceToTexture(const ScopedIOSurfaceRef &ioSurface,
158 EGLint width,
159 EGLint height,
160 EGLint plane,
161 GLenum internalFormat,
162 GLenum type,
163 EGLSurface *pbuffer,
164 GLTexture *texture) const
165 {
166 createIOSurfacePbuffer(ioSurface, width, height, plane, internalFormat, type, pbuffer);
167
168 // Bind the pbuffer
169 glBindTexture(getGLTextureTarget(), *texture);
170 EGLBoolean result = eglBindTexImage(mDisplay, *pbuffer, EGL_BACK_BUFFER);
171 EXPECT_EGL_TRUE(result);
172 EXPECT_EGL_SUCCESS();
173 }
174
doClearTest(const ScopedIOSurfaceRef & ioSurface,GLenum internalFormat,GLenum type,void * data,size_t dataSize)175 void doClearTest(const ScopedIOSurfaceRef &ioSurface,
176 GLenum internalFormat,
177 GLenum type,
178 void *data,
179 size_t dataSize)
180 {
181 // Bind the IOSurface to a texture and clear it.
182 EGLSurface pbuffer;
183 GLTexture texture;
184 bindIOSurfaceToTexture(ioSurface, 1, 1, 0, internalFormat, type, &pbuffer, &texture);
185
186 GLFramebuffer fbo;
187 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
188 EXPECT_GL_NO_ERROR();
189 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getGLTextureTarget(), texture,
190 0);
191 EXPECT_GL_NO_ERROR();
192 EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
193 EXPECT_GL_NO_ERROR();
194
195 glClearColor(1.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 4.0f / 255.0f);
196 EXPECT_GL_NO_ERROR();
197 glClear(GL_COLOR_BUFFER_BIT);
198 EXPECT_GL_NO_ERROR();
199
200 // Unbind pbuffer and check content.
201 EGLBoolean result = eglReleaseTexImage(mDisplay, pbuffer, EGL_BACK_BUFFER);
202 EXPECT_EGL_TRUE(result);
203 EXPECT_EGL_SUCCESS();
204
205 IOSurfaceLock(ioSurface.get(), kIOSurfaceLockReadOnly, nullptr);
206 ASSERT_EQ(0, memcmp(IOSurfaceGetBaseAddress(ioSurface.get()), data, dataSize));
207 IOSurfaceUnlock(ioSurface.get(), kIOSurfaceLockReadOnly, nullptr);
208
209 result = eglDestroySurface(mDisplay, pbuffer);
210 EXPECT_EGL_TRUE(result);
211 EXPECT_EGL_SUCCESS();
212 }
213
214 enum ColorMask
215 {
216 R = 1,
217 G = 2,
218 B = 4,
219 A = 8,
220 };
doSampleTest(const ScopedIOSurfaceRef & ioSurface,GLenum internalFormat,GLenum type,void * data,size_t dataSize,int mask)221 void doSampleTest(const ScopedIOSurfaceRef &ioSurface,
222 GLenum internalFormat,
223 GLenum type,
224 void *data,
225 size_t dataSize,
226 int mask)
227 {
228 // Write the data to the IOSurface
229 IOSurfaceLock(ioSurface.get(), 0, nullptr);
230 memcpy(IOSurfaceGetBaseAddress(ioSurface.get()), data, dataSize);
231 IOSurfaceUnlock(ioSurface.get(), 0, nullptr);
232
233 // Bind the IOSurface to a texture and clear it.
234 EGLSurface pbuffer;
235 GLTexture texture;
236 bindIOSurfaceToTexture(ioSurface, 1, 1, 0, internalFormat, type, &pbuffer, &texture);
237
238 constexpr char kVS[] =
239 "attribute vec4 position;\n"
240 "void main()\n"
241 "{\n"
242 " gl_Position = vec4(position.xy, 0.0, 1.0);\n"
243 "}\n";
244 constexpr char kFS_rect[] =
245 "#extension GL_ARB_texture_rectangle : require\n"
246 "precision mediump float;\n"
247 "uniform sampler2DRect tex;\n"
248 "void main()\n"
249 "{\n"
250 " gl_FragColor = texture2DRect(tex, vec2(0, 0));\n"
251 "}\n";
252 constexpr char kFS_2D[] =
253 "precision mediump float;\n"
254 "uniform sampler2D tex;\n"
255 "void main()\n"
256 "{\n"
257 " gl_FragColor = texture2D(tex, vec2(0, 0));\n"
258 "}\n";
259
260 ANGLE_GL_PROGRAM(program, kVS,
261 (getTextureTarget() == EGL_TEXTURE_RECTANGLE_ANGLE ? kFS_rect : kFS_2D));
262 glUseProgram(program);
263
264 GLint location = glGetUniformLocation(program, "tex");
265 ASSERT_NE(-1, location);
266 glUniform1i(location, 0);
267
268 glClearColor(0.0, 0.0, 0.0, 0.0);
269 glClear(GL_COLOR_BUFFER_BIT);
270 drawQuad(program, "position", 0.5f, 1.0f, false);
271
272 GLColor expectedColor((mask & R) ? 1 : 0, (mask & G) ? 2 : 0, (mask & B) ? 3 : 0,
273 (mask & A) ? 4 : 255);
274 EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor);
275 ASSERT_GL_NO_ERROR();
276 }
277
doBlitTest(bool ioSurfaceIsSource,int width,int height)278 void doBlitTest(bool ioSurfaceIsSource, int width, int height)
279 {
280 // Create IOSurface and bind it to a texture.
281 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(width, height, 'BGRA', 4);
282 EGLSurface pbuffer;
283 GLTexture texture;
284 bindIOSurfaceToTexture(ioSurface, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &pbuffer,
285 &texture);
286
287 GLFramebuffer iosurfaceFbo;
288 glBindFramebuffer(GL_FRAMEBUFFER, iosurfaceFbo);
289 EXPECT_GL_NO_ERROR();
290 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getGLTextureTarget(), texture,
291 0);
292 EXPECT_GL_NO_ERROR();
293 EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
294 EXPECT_GL_NO_ERROR();
295
296 // Create another framebuffer with a regular renderbuffer.
297 GLFramebuffer fbo;
298 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
299 EXPECT_GL_NO_ERROR();
300 GLRenderbuffer rbo;
301 glBindRenderbuffer(GL_RENDERBUFFER, rbo);
302 EXPECT_GL_NO_ERROR();
303 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
304 EXPECT_GL_NO_ERROR();
305 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
306 EXPECT_GL_NO_ERROR();
307 EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
308 EXPECT_GL_NO_ERROR();
309
310 glBindRenderbuffer(GL_RENDERBUFFER, 0);
311 EXPECT_GL_NO_ERROR();
312 glBindFramebuffer(GL_FRAMEBUFFER, 0);
313 EXPECT_GL_NO_ERROR();
314
315 // Choose which is going to be the source and destination.
316 GLFramebuffer &src = ioSurfaceIsSource ? iosurfaceFbo : fbo;
317 GLFramebuffer &dst = ioSurfaceIsSource ? fbo : iosurfaceFbo;
318
319 // Clear source to known color.
320 glBindFramebuffer(GL_FRAMEBUFFER, src);
321 glClearColor(1.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 4.0f / 255.0f);
322 EXPECT_GL_NO_ERROR();
323 glClear(GL_COLOR_BUFFER_BIT);
324 EXPECT_GL_NO_ERROR();
325
326 // Blit to destination.
327 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, dst);
328 glBlitFramebufferANGLE(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
329 GL_NEAREST);
330
331 // Read back from destination.
332 glBindFramebuffer(GL_FRAMEBUFFER, dst);
333 GLColor expectedColor(1, 2, 3, 4);
334 EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor);
335
336 // Unbind pbuffer and check content.
337 EGLBoolean result = eglReleaseTexImage(mDisplay, pbuffer, EGL_BACK_BUFFER);
338 EXPECT_EGL_TRUE(result);
339 EXPECT_EGL_SUCCESS();
340
341 result = eglDestroySurface(mDisplay, pbuffer);
342 EXPECT_EGL_TRUE(result);
343 EXPECT_EGL_SUCCESS();
344 }
345
hasIOSurfaceExt() const346 bool hasIOSurfaceExt() const { return IsEGLDisplayExtensionEnabled(mDisplay, kIOSurfaceExt); }
347
348 EGLConfig mConfig;
349 EGLDisplay mDisplay;
350 };
351
352 // Test using BGRA8888 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest,RenderToBGRA8888IOSurface)353 TEST_P(IOSurfaceClientBufferTest, RenderToBGRA8888IOSurface)
354 {
355 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
356
357 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
358
359 GLColor color(3, 2, 1, 4);
360 doClearTest(ioSurface, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &color, sizeof(color));
361 }
362
363 // Test reading from BGRA8888 IOSurfaces
TEST_P(IOSurfaceClientBufferTest,ReadFromBGRA8888IOSurface)364 TEST_P(IOSurfaceClientBufferTest, ReadFromBGRA8888IOSurface)
365 {
366 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
367
368 // TODO(http://anglebug.com/4369)
369 ANGLE_SKIP_TEST_IF(isSwiftshader());
370
371 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
372
373 GLColor color(3, 2, 1, 4);
374 doSampleTest(ioSurface, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &color, sizeof(color), R | G | B | A);
375 }
376
377 // Test using BGRX8888 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest,RenderToBGRX8888IOSurface)378 TEST_P(IOSurfaceClientBufferTest, RenderToBGRX8888IOSurface)
379 {
380 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
381
382 // TODO(http://anglebug.com/4369)
383 ANGLE_SKIP_TEST_IF(isSwiftshader());
384
385 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
386
387 GLColor color(3, 2, 1, 255);
388 doClearTest(ioSurface, GL_RGB, GL_UNSIGNED_BYTE, &color, sizeof(color));
389 }
390
391 // Test reading from BGRX8888 IOSurfaces
TEST_P(IOSurfaceClientBufferTest,ReadFromBGRX8888IOSurface)392 TEST_P(IOSurfaceClientBufferTest, ReadFromBGRX8888IOSurface)
393 {
394 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
395
396 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
397
398 GLColor color(3, 2, 1, 4);
399 doSampleTest(ioSurface, GL_RGB, GL_UNSIGNED_BYTE, &color, sizeof(color), R | G | B);
400 }
401
402 // Test using RG88 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest,RenderToRG88IOSurface)403 TEST_P(IOSurfaceClientBufferTest, RenderToRG88IOSurface)
404 {
405 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
406
407 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2);
408
409 uint8_t color[2] = {1, 2};
410 doClearTest(ioSurface, GL_RG, GL_UNSIGNED_BYTE, &color, sizeof(color));
411 }
412
413 // Test reading from RG88 IOSurfaces
TEST_P(IOSurfaceClientBufferTest,ReadFromRG88IOSurface)414 TEST_P(IOSurfaceClientBufferTest, ReadFromRG88IOSurface)
415 {
416 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
417
418 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2);
419
420 uint8_t color[2] = {1, 2};
421 doSampleTest(ioSurface, GL_RG, GL_UNSIGNED_BYTE, &color, sizeof(color), R | G);
422 }
423
424 // Test using R8 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest,RenderToR8IOSurface)425 TEST_P(IOSurfaceClientBufferTest, RenderToR8IOSurface)
426 {
427 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
428
429 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1);
430
431 uint8_t color = 1;
432 doClearTest(ioSurface, GL_RED, GL_UNSIGNED_BYTE, &color, sizeof(color));
433 }
434
435 // Test reading from R8 IOSurfaces
TEST_P(IOSurfaceClientBufferTest,ReadFromR8IOSurface)436 TEST_P(IOSurfaceClientBufferTest, ReadFromR8IOSurface)
437 {
438 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
439
440 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1);
441
442 uint8_t color = 1;
443 doSampleTest(ioSurface, GL_RED, GL_UNSIGNED_BYTE, &color, sizeof(color), R);
444 }
445
446 // Test using R16 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest,RenderToR16IOSurface)447 TEST_P(IOSurfaceClientBufferTest, RenderToR16IOSurface)
448 {
449 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
450
451 // This test only works on ES3.
452 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
453 // TODO(http://anglebug.com/4369)
454 ANGLE_SKIP_TEST_IF(isSwiftshader());
455
456 // HACK(cwallez@chromium.org) 'L016' doesn't seem to be an official pixel format but it works
457 // sooooooo let's test using it
458 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L016', 2);
459
460 uint16_t color = 257;
461 doClearTest(ioSurface, GL_R16UI, GL_UNSIGNED_SHORT, &color, sizeof(color));
462 }
463 // TODO(cwallez@chromium.org): test reading from R16? It returns 0 maybe because samplerRect is
464 // only for floating textures?
465
466 // TODO(cwallez@chromium.org): Test using RGBA half float IOSurfaces ('RGhA')
467
468 // Test blitting from IOSurface
TEST_P(IOSurfaceClientBufferTest,BlitFromIOSurface)469 TEST_P(IOSurfaceClientBufferTest, BlitFromIOSurface)
470 {
471 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
472
473 doBlitTest(true, 2, 2);
474 }
475
476 // Test blitting to IOSurface
TEST_P(IOSurfaceClientBufferTest,BlitToIOSurface)477 TEST_P(IOSurfaceClientBufferTest, BlitToIOSurface)
478 {
479 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
480
481 doBlitTest(false, 2, 2);
482 }
483
484 // Test the validation errors for missing attributes for eglCreatePbufferFromClientBuffer with
485 // IOSurface
TEST_P(IOSurfaceClientBufferTest,NegativeValidationMissingAttributes)486 TEST_P(IOSurfaceClientBufferTest, NegativeValidationMissingAttributes)
487 {
488 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
489
490 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
491
492 // Success case
493 {
494 // clang-format off
495 const EGLint attribs[] = {
496 EGL_WIDTH, 10,
497 EGL_HEIGHT, 10,
498 EGL_IOSURFACE_PLANE_ANGLE, 0,
499 EGL_TEXTURE_TARGET, getTextureTarget(),
500 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
501 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
502 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
503 EGL_NONE, EGL_NONE,
504 };
505 // clang-format off
506
507 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(), mConfig, attribs);
508 EXPECT_NE(EGL_NO_SURFACE, pbuffer);
509
510 EGLBoolean result = eglDestroySurface(mDisplay, pbuffer);
511 EXPECT_EGL_TRUE(result);
512 EXPECT_EGL_SUCCESS();
513 }
514
515 // Missing EGL_WIDTH
516 {
517 // clang-format off
518 const EGLint attribs[] = {
519 EGL_HEIGHT, 10,
520 EGL_IOSURFACE_PLANE_ANGLE, 0,
521 EGL_TEXTURE_TARGET, getTextureTarget(),
522 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
523 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
524 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
525 EGL_NONE, EGL_NONE,
526 };
527 // clang-format on
528
529 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
530 ioSurface.get(), mConfig, attribs);
531 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
532 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
533 }
534
535 // Missing EGL_HEIGHT
536 {
537 // clang-format off
538 const EGLint attribs[] = {
539 EGL_WIDTH, 10,
540 EGL_IOSURFACE_PLANE_ANGLE, 0,
541 EGL_TEXTURE_TARGET, getTextureTarget(),
542 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
543 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
544 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
545 EGL_NONE, EGL_NONE,
546 };
547 // clang-format on
548
549 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
550 ioSurface.get(), mConfig, attribs);
551 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
552 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
553 }
554
555 // Missing EGL_IOSURFACE_PLANE_ANGLE
556 {
557 // clang-format off
558 const EGLint attribs[] = {
559 EGL_WIDTH, 10,
560 EGL_HEIGHT, 10,
561 EGL_TEXTURE_TARGET, getTextureTarget(),
562 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
563 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
564 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
565 EGL_NONE, EGL_NONE,
566 };
567 // clang-format on
568
569 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
570 ioSurface.get(), mConfig, attribs);
571 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
572 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
573 }
574
575 // Missing EGL_TEXTURE_TARGET - EGL_BAD_MATCH from the base spec of
576 // eglCreatePbufferFromClientBuffer
577 {
578 // clang-format off
579 const EGLint attribs[] = {
580 EGL_WIDTH, 10,
581 EGL_HEIGHT, 10,
582 EGL_IOSURFACE_PLANE_ANGLE, 0,
583 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
584 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
585 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
586 EGL_NONE, EGL_NONE,
587 };
588 // clang-format on
589
590 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
591 ioSurface.get(), mConfig, attribs);
592 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
593 EXPECT_EGL_ERROR(EGL_BAD_MATCH);
594 }
595
596 // Missing EGL_TEXTURE_INTERNAL_FORMAT_ANGLE
597 {
598 // clang-format off
599 const EGLint attribs[] = {
600 EGL_WIDTH, 10,
601 EGL_HEIGHT, 10,
602 EGL_IOSURFACE_PLANE_ANGLE, 0,
603 EGL_TEXTURE_TARGET, getTextureTarget(),
604 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
605 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
606 EGL_NONE, EGL_NONE,
607 };
608 // clang-format on
609
610 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
611 ioSurface.get(), mConfig, attribs);
612 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
613 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
614 }
615
616 // Missing EGL_TEXTURE_FORMAT - EGL_BAD_MATCH from the base spec of
617 // eglCreatePbufferFromClientBuffer
618 {
619 // clang-format off
620 const EGLint attribs[] = {
621 EGL_WIDTH, 10,
622 EGL_HEIGHT, 10,
623 EGL_IOSURFACE_PLANE_ANGLE, 0,
624 EGL_TEXTURE_TARGET, getTextureTarget(),
625 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
626 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
627 EGL_NONE, EGL_NONE,
628 };
629 // clang-format on
630
631 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
632 ioSurface.get(), mConfig, attribs);
633 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
634 EXPECT_EGL_ERROR(EGL_BAD_MATCH);
635 }
636
637 // Missing EGL_TEXTURE_TYPE_ANGLE
638 {
639 // clang-format off
640 const EGLint attribs[] = {
641 EGL_WIDTH, 10,
642 EGL_HEIGHT, 10,
643 EGL_IOSURFACE_PLANE_ANGLE, 0,
644 EGL_TEXTURE_TARGET, getTextureTarget(),
645 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
646 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
647 EGL_NONE, EGL_NONE,
648 };
649 // clang-format on
650
651 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
652 ioSurface.get(), mConfig, attribs);
653 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
654 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
655 }
656 }
657
658 // Test the validation errors for bad parameters for eglCreatePbufferFromClientBuffer with IOSurface
TEST_P(IOSurfaceClientBufferTest,NegativeValidationBadAttributes)659 TEST_P(IOSurfaceClientBufferTest, NegativeValidationBadAttributes)
660 {
661 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
662
663 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
664
665 // Success case
666 {
667 // clang-format off
668 const EGLint attribs[] = {
669 EGL_WIDTH, 10,
670 EGL_HEIGHT, 10,
671 EGL_IOSURFACE_PLANE_ANGLE, 0,
672 EGL_TEXTURE_TARGET, getTextureTarget(),
673 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
674 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
675 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
676 EGL_NONE, EGL_NONE,
677 };
678 // clang-format off
679
680 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(), mConfig, attribs);
681 EXPECT_NE(EGL_NO_SURFACE, pbuffer);
682
683 EGLBoolean result = eglDestroySurface(mDisplay, pbuffer);
684 EXPECT_EGL_TRUE(result);
685 EXPECT_EGL_SUCCESS();
686 }
687
688 // EGL_TEXTURE_FORMAT must be EGL_TEXTURE_RGBA
689 {
690 // clang-format off
691 const EGLint attribs[] = {
692 EGL_WIDTH, 10,
693 EGL_HEIGHT, 10,
694 EGL_IOSURFACE_PLANE_ANGLE, 0,
695 EGL_TEXTURE_TARGET, getTextureTarget(),
696 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
697 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
698 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
699 EGL_NONE, EGL_NONE,
700 };
701 // clang-format on
702
703 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
704 ioSurface.get(), mConfig, attribs);
705 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
706 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
707 }
708
709 // EGL_WIDTH must be at least 1
710 {
711 // clang-format off
712 const EGLint attribs[] = {
713 EGL_WIDTH, 0,
714 EGL_HEIGHT, 10,
715 EGL_IOSURFACE_PLANE_ANGLE, 0,
716 EGL_TEXTURE_TARGET, getTextureTarget(),
717 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
718 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
719 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
720 EGL_NONE, EGL_NONE,
721 };
722 // clang-format on
723
724 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
725 ioSurface.get(), mConfig, attribs);
726 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
727 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
728 }
729
730 // EGL_WIDTH must be at most the width of the IOSurface
731 {
732 // clang-format off
733 const EGLint attribs[] = {
734 EGL_WIDTH, 11,
735 EGL_HEIGHT, 10,
736 EGL_IOSURFACE_PLANE_ANGLE, 0,
737 EGL_TEXTURE_TARGET, getTextureTarget(),
738 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
739 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
740 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
741 EGL_NONE, EGL_NONE,
742 };
743 // clang-format on
744
745 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
746 ioSurface.get(), mConfig, attribs);
747 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
748 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
749 }
750
751 // EGL_HEIGHT must be at least 1
752 {
753 // clang-format off
754 const EGLint attribs[] = {
755 EGL_WIDTH, 10,
756 EGL_HEIGHT, 0,
757 EGL_IOSURFACE_PLANE_ANGLE, 0,
758 EGL_TEXTURE_TARGET, getTextureTarget(),
759 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
760 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
761 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
762 EGL_NONE, EGL_NONE,
763 };
764 // clang-format on
765
766 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
767 ioSurface.get(), mConfig, attribs);
768 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
769 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
770 }
771
772 // EGL_HEIGHT must be at most the height of the IOSurface
773 {
774 // clang-format off
775 const EGLint attribs[] = {
776 EGL_WIDTH, 10,
777 EGL_HEIGHT, 11,
778 EGL_IOSURFACE_PLANE_ANGLE, 0,
779 EGL_TEXTURE_TARGET, getTextureTarget(),
780 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
781 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
782 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
783 EGL_NONE, EGL_NONE,
784 };
785 // clang-format on
786
787 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
788 ioSurface.get(), mConfig, attribs);
789 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
790 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
791 }
792
793 // EGL_TEXTURE_FORMAT must be equal to the config's texture target
794 {
795 EGLint target = getTextureTarget();
796 EGLint wrongTarget = 0;
797 switch (target)
798 {
799 case EGL_TEXTURE_RECTANGLE_ANGLE:
800 wrongTarget = EGL_TEXTURE_2D;
801 break;
802 case EGL_TEXTURE_2D:
803 wrongTarget = EGL_TEXTURE_RECTANGLE_ANGLE;
804 break;
805 default:
806 break;
807 }
808 // clang-format off
809 const EGLint attribs[] = {
810 EGL_WIDTH, 10,
811 EGL_HEIGHT, 10,
812 EGL_IOSURFACE_PLANE_ANGLE, 0,
813 EGL_TEXTURE_TARGET, wrongTarget,
814 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
815 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
816 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
817 EGL_NONE, EGL_NONE,
818 };
819 // clang-format on
820
821 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
822 ioSurface.get(), mConfig, attribs);
823 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
824 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
825 }
826
827 // EGL_IOSURFACE_PLANE_ANGLE must be at least 0
828 {
829 // clang-format off
830 const EGLint attribs[] = {
831 EGL_WIDTH, 10,
832 EGL_HEIGHT, 10,
833 EGL_IOSURFACE_PLANE_ANGLE, -1,
834 EGL_TEXTURE_TARGET, getTextureTarget(),
835 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
836 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
837 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
838 EGL_NONE, EGL_NONE,
839 };
840 // clang-format on
841
842 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
843 ioSurface.get(), mConfig, attribs);
844 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
845 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
846 }
847
848 // EGL_IOSURFACE_PLANE_ANGLE must less than the number of planes of the IOSurface
849 {
850 // clang-format off
851 const EGLint attribs[] = {
852 EGL_WIDTH, 10,
853 EGL_HEIGHT, 10,
854 EGL_IOSURFACE_PLANE_ANGLE, 1,
855 EGL_TEXTURE_TARGET, getTextureTarget(),
856 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
857 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
858 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
859 EGL_NONE, EGL_NONE,
860 };
861 // clang-format on
862
863 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
864 ioSurface.get(), mConfig, attribs);
865 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
866 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
867 }
868
869 // The internal format / type most be listed in the table
870 {
871 // clang-format off
872 const EGLint attribs[] = {
873 EGL_WIDTH, 10,
874 EGL_HEIGHT, 10,
875 EGL_IOSURFACE_PLANE_ANGLE, 0,
876 EGL_TEXTURE_TARGET, getTextureTarget(),
877 EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGBA,
878 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
879 EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
880 EGL_NONE, EGL_NONE,
881 };
882 // clang-format on
883
884 EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
885 ioSurface.get(), mConfig, attribs);
886 EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
887 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
888 }
889 }
890
891 // Test IOSurface pbuffers can be made current
TEST_P(IOSurfaceClientBufferTest,MakeCurrent)892 TEST_P(IOSurfaceClientBufferTest, MakeCurrent)
893 {
894 ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
895
896 ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
897
898 EGLSurface pbuffer;
899 createIOSurfacePbuffer(ioSurface, 10, 10, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &pbuffer);
900
901 EGLContext context = getEGLWindow()->getContext();
902 EGLBoolean result = eglMakeCurrent(mDisplay, pbuffer, pbuffer, context);
903 EXPECT_EGL_TRUE(result);
904 EXPECT_EGL_SUCCESS();
905 // The test harness expects the EGL state to be restored before the test exits.
906 result = eglMakeCurrent(mDisplay, getEGLWindow()->getSurface(), getEGLWindow()->getSurface(),
907 context);
908 EXPECT_EGL_TRUE(result);
909 EXPECT_EGL_SUCCESS();
910 }
911
912 // TODO(cwallez@chromium.org): Test setting width and height to less than the IOSurface's work as
913 // expected.
914
915 ANGLE_INSTANTIATE_TEST(IOSurfaceClientBufferTest,
916 ES2_OPENGL(),
917 ES3_OPENGL(),
918 ES2_VULKAN_SWIFTSHADER(),
919 ES3_VULKAN_SWIFTSHADER());
920