1// 2// Copyright 2021 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// ImageTestMetal: 7// Tests the correctness of eglImage with native Metal texture extensions. 8// 9 10#include "test_utils/ANGLETest.h" 11 12#include "common/mathutil.h" 13#include "test_utils/gl_raii.h" 14#include "util/EGLWindow.h" 15 16#include <CoreFoundation/CoreFoundation.h> 17#include <Metal/Metal.h> 18#include <gmock/gmock.h> 19#include <span> 20 21namespace angle 22{ 23namespace 24{ 25constexpr char kOESExt[] = "GL_OES_EGL_image"; 26constexpr char kBaseExt[] = "EGL_KHR_image_base"; 27constexpr char kDeviceMtlExt[] = "EGL_ANGLE_device_metal"; 28constexpr char kEGLMtlImageNativeTextureExt[] = "EGL_ANGLE_metal_texture_client_buffer"; 29constexpr EGLint kDefaultAttribs[] = { 30 EGL_NONE, 31}; 32 33template <typename T> 34class ScopedMetalObjectRef : angle::NonCopyable 35{ 36 public: 37 ScopedMetalObjectRef() = default; 38 39 explicit ScopedMetalObjectRef(T &&surface) : mObject(surface) {} 40 41 ~ScopedMetalObjectRef() 42 { 43 if (mObject) 44 { 45 release(); 46 mObject = nil; 47 } 48 } 49 50 T get() const { return mObject; } 51 52 operator bool() const { return !!mObject; } 53 54 // auto cast to T 55 operator T() const { return mObject; } 56 ScopedMetalObjectRef(const ScopedMetalObjectRef &other) 57 { 58 if (mObject) 59 { 60 release(); 61 } 62 mObject = other.mObject; 63 } 64 65 explicit ScopedMetalObjectRef(ScopedMetalObjectRef &&other) 66 { 67 if (mObject) 68 { 69 release(); 70 } 71 mObject = other.mObject; 72 other.mObject = nil; 73 } 74 75 ScopedMetalObjectRef &operator=(ScopedMetalObjectRef &&other) 76 { 77 if (mObject) 78 { 79 release(); 80 } 81 mObject = other.mObject; 82 other.mObject = nil; 83 84 return *this; 85 } 86 87 ScopedMetalObjectRef &operator=(const ScopedMetalObjectRef &other) 88 { 89 if (mObject) 90 { 91 release(); 92 } 93 mObject = other.mObject; 94 95 return *this; 96 } 97 98 private: 99 void release() 100 { 101#if !__has_feature(objc_arc) 102 [mObject release]; 103#endif 104 } 105 106 T mObject = nil; 107}; 108 109using ScopedMetalTextureRef = ScopedMetalObjectRef<id<MTLTexture>>; 110using ScopedMetalBufferRef = ScopedMetalObjectRef<id<MTLBuffer>>; 111using ScopedMetalCommandQueueRef = ScopedMetalObjectRef<id<MTLCommandQueue>>; 112 113} // anonymous namespace 114 115bool IsDepthOrStencil(MTLPixelFormat format) 116{ 117 switch (format) 118 { 119 case MTLPixelFormatDepth16Unorm: 120 case MTLPixelFormatDepth32Float: 121 case MTLPixelFormatStencil8: 122 case MTLPixelFormatDepth24Unorm_Stencil8: 123 case MTLPixelFormatDepth32Float_Stencil8: 124 case MTLPixelFormatX32_Stencil8: 125 case MTLPixelFormatX24_Stencil8: 126 return true; 127 128 default: 129 return false; 130 } 131} 132 133ScopedMetalTextureRef CreateMetalTexture2D(id<MTLDevice> deviceMtl, 134 int width, 135 int height, 136 MTLPixelFormat format, 137 int arrayLength) 138{ 139 @autoreleasepool 140 { 141 MTLTextureDescriptor *desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format 142 width:width 143 height:width 144 mipmapped:NO]; 145 desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; 146 if (IsDepthOrStencil(format)) 147 { 148 desc.storageMode = MTLStorageModePrivate; 149 } 150 if (arrayLength) 151 { 152 desc.arrayLength = arrayLength; 153 desc.textureType = MTLTextureType2DArray; 154 } 155 ScopedMetalTextureRef re([deviceMtl newTextureWithDescriptor:desc]); 156 return re; 157 } 158} 159 160id<MTLSharedEvent> CreateMetalSharedEvent(id<MTLDevice> deviceMtl) 161{ 162 id<MTLSharedEvent> sharedEvent = [deviceMtl newSharedEvent]; 163 sharedEvent.label = @"TestSharedEvent"; 164 return sharedEvent; 165} 166 167EGLSync CreateEGLSyncFromMetalSharedEvent(EGLDisplay display, 168 id<MTLSharedEvent> sharedEvent, 169 uint64_t signalValue, 170 bool signaled) 171{ 172 EGLAttrib signalValueHi = signalValue >> 32; 173 EGLAttrib signalValueLo = signalValue & 0xffffffff; 174 std::vector<EGLAttrib> syncAttribs = { 175 EGL_SYNC_METAL_SHARED_EVENT_OBJECT_ANGLE, reinterpret_cast<EGLAttrib>(sharedEvent), 176 EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_HI_ANGLE, signalValueHi, 177 EGL_SYNC_METAL_SHARED_EVENT_SIGNAL_VALUE_LO_ANGLE, signalValueLo}; 178 179 if (signaled) 180 { 181 syncAttribs.push_back(EGL_SYNC_CONDITION); 182 syncAttribs.push_back(EGL_SYNC_METAL_SHARED_EVENT_SIGNALED_ANGLE); 183 } 184 185 syncAttribs.push_back(EGL_NONE); 186 187 EGLSync syncWithSharedEvent = 188 eglCreateSync(display, EGL_SYNC_METAL_SHARED_EVENT_ANGLE, syncAttribs.data()); 189 EXPECT_NE(syncWithSharedEvent, EGL_NO_SYNC); 190 191 return syncWithSharedEvent; 192} 193 194class ImageTestMetal : public ANGLETest<> 195{ 196 protected: 197 ImageTestMetal() 198 { 199 setWindowWidth(128); 200 setWindowHeight(128); 201 setConfigRedBits(8); 202 setConfigGreenBits(8); 203 setConfigBlueBits(8); 204 setConfigAlphaBits(8); 205 setConfigDepthBits(24); 206 } 207 208 void testSetUp() override 209 { 210 constexpr char kVS[] = "precision highp float;\n" 211 "attribute vec4 position;\n" 212 "varying vec2 texcoord;\n" 213 "\n" 214 "void main()\n" 215 "{\n" 216 " gl_Position = position;\n" 217 " texcoord = (position.xy * 0.5) + 0.5;\n" 218 " texcoord.y = 1.0 - texcoord.y;\n" 219 "}\n"; 220 221 constexpr char kTextureFS[] = "precision highp float;\n" 222 "uniform sampler2D tex;\n" 223 "varying vec2 texcoord;\n" 224 "\n" 225 "void main()\n" 226 "{\n" 227 " gl_FragColor = texture2D(tex, texcoord);\n" 228 "}\n"; 229 230 mTextureProgram = CompileProgram(kVS, kTextureFS); 231 if (mTextureProgram == 0) 232 { 233 FAIL() << "shader compilation failed."; 234 } 235 236 mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); 237 238 ASSERT_GL_NO_ERROR(); 239 } 240 241 void testTearDown() override { glDeleteProgram(mTextureProgram); } 242 243 id<MTLDevice> getMtlDevice() 244 { 245 EGLAttrib angleDevice = 0; 246 EGLAttrib device = 0; 247 EXPECT_EGL_TRUE( 248 eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); 249 250 EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), 251 EGL_METAL_DEVICE_ANGLE, &device)); 252 253 return (__bridge id<MTLDevice>)reinterpret_cast<void *>(device); 254 } 255 256 ScopedMetalTextureRef createMtlTexture2D(int width, int height, MTLPixelFormat format) 257 { 258 id<MTLDevice> device = getMtlDevice(); 259 260 return CreateMetalTexture2D(device, width, height, format, 0); 261 } 262 263 ScopedMetalTextureRef createMtlTexture2DArray(int width, 264 int height, 265 int arrayLength, 266 MTLPixelFormat format) 267 { 268 id<MTLDevice> device = getMtlDevice(); 269 270 return CreateMetalTexture2D(device, width, height, format, arrayLength); 271 } 272 273 void getTextureSliceBytes(id<MTLTexture> texture, 274 unsigned bytesPerRow, 275 MTLRegion region, 276 unsigned mipmapLevel, 277 unsigned slice, 278 std::span<uint8_t> sliceImage) 279 { 280 @autoreleasepool 281 { 282 id<MTLDevice> device = texture.device; 283 ScopedMetalBufferRef readBuffer([device 284 newBufferWithLength:sliceImage.size() 285 options:MTLResourceStorageModeShared]); 286 ScopedMetalCommandQueueRef commandQueue([device newCommandQueue]); 287 id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer]; 288 id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder]; 289 [blitEncoder copyFromTexture:texture 290 sourceSlice:slice 291 sourceLevel:mipmapLevel 292 sourceOrigin:region.origin 293 sourceSize:region.size 294 toBuffer:readBuffer 295 destinationOffset:0 296 destinationBytesPerRow:bytesPerRow 297 destinationBytesPerImage:sliceImage.size()]; 298 [blitEncoder endEncoding]; 299 [commandBuffer commit]; 300 [commandBuffer waitUntilCompleted]; 301 memcpy(sliceImage.data(), readBuffer.get().contents, sliceImage.size()); 302 } 303 } 304 void sourceMetalTarget2D_helper(GLubyte data[4], 305 const EGLint *attribs, 306 EGLImageKHR *imageOut, 307 GLuint *textureOut); 308 309 void verifyResultsTexture(GLuint texture, 310 const GLubyte data[4], 311 GLenum textureTarget, 312 GLuint program, 313 GLuint textureUniform) 314 { 315 // Draw a quad with the target texture 316 glUseProgram(program); 317 glBindTexture(textureTarget, texture); 318 glUniform1i(textureUniform, 0); 319 320 drawQuad(program, "position", 0.5f); 321 322 // Expect that the rendered quad has the same color as the source texture 323 EXPECT_PIXEL_NEAR(0, 0, data[0], data[1], data[2], data[3], 1.0); 324 } 325 326 void verifyResults2D(GLuint texture, const GLubyte data[4]) 327 { 328 verifyResultsTexture(texture, data, GL_TEXTURE_2D, mTextureProgram, 329 mTextureUniformLocation); 330 } 331 332 void drawColorQuad(GLColor color) 333 { 334 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); 335 glUseProgram(program); 336 GLint colorUniformLocation = 337 glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); 338 ASSERT_NE(colorUniformLocation, -1); 339 glUniform4fv(colorUniformLocation, 1, color.toNormalizedVector().data()); 340 drawQuad(program, essl1_shaders::PositionAttrib(), 0); 341 glUseProgram(0); 342 } 343 344 bool hasDepth24Stencil8PixelFormat() 345 { 346 id<MTLDevice> device = getMtlDevice(); 347 return device.depth24Stencil8PixelFormatSupported; 348 } 349 350 bool hasImageNativeMetalTextureExt() const 351 { 352 if (!IsMetal()) 353 { 354 return false; 355 } 356 EGLAttrib angleDevice = 0; 357 eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice); 358 if (!angleDevice) 359 { 360 return false; 361 } 362 auto extensionString = static_cast<const char *>( 363 eglQueryDeviceStringEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), EGL_EXTENSIONS)); 364 if (strstr(extensionString, kDeviceMtlExt) == nullptr) 365 { 366 return false; 367 } 368 return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), 369 kEGLMtlImageNativeTextureExt); 370 } 371 372 bool hasOESExt() const { return IsGLExtensionEnabled(kOESExt); } 373 374 bool hasBaseExt() const 375 { 376 return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kBaseExt); 377 } 378 379 GLuint mTextureProgram; 380 GLint mTextureUniformLocation; 381}; 382 383void ImageTestMetal::sourceMetalTarget2D_helper(GLubyte data[4], 384 const EGLint *attribs, 385 EGLImageKHR *imageOut, 386 GLuint *textureOut) 387{ 388 EGLWindow *window = getEGLWindow(); 389 390 // Create MTLTexture 391 ScopedMetalTextureRef textureMtl = createMtlTexture2D(1, 1, MTLPixelFormatRGBA8Unorm); 392 393 // Create image 394 EGLImageKHR image = 395 eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 396 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 397 ASSERT_EGL_SUCCESS(); 398 399 // Write the data to the MTLTexture 400 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 401 mipmapLevel:0 402 slice:0 403 withBytes:data 404 bytesPerRow:4 405 bytesPerImage:0]; 406 407 // Create a texture target to bind the egl image 408 GLuint target; 409 glGenTextures(1, &target); 410 glBindTexture(GL_TEXTURE_2D, target); 411 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 412 413 *imageOut = image; 414 *textureOut = target; 415} 416 417// Testing source metal EGL image, target 2D texture 418TEST_P(ImageTestMetal, SourceMetalTarget2D) 419{ 420 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 421 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 422 423 EGLWindow *window = getEGLWindow(); 424 425 // Create the Image 426 EGLImageKHR image; 427 GLuint texTarget; 428 GLubyte data[4] = {7, 51, 197, 231}; 429 sourceMetalTarget2D_helper(data, kDefaultAttribs, &image, &texTarget); 430 431 // Use texture target bound to egl image as source and render to framebuffer 432 // Verify that data in framebuffer matches that in the egl image 433 verifyResults2D(texTarget, data); 434 435 // Clean up 436 eglDestroyImageKHR(window->getDisplay(), image); 437 glDeleteTextures(1, &texTarget); 438} 439 440// Create source metal EGL image, target 2D texture, then trigger texture respecification. 441TEST_P(ImageTestMetal, SourceMetal2DTargetTextureRespecifySize) 442{ 443 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 444 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 445 446 EGLWindow *window = getEGLWindow(); 447 448 // Create the Image 449 EGLImageKHR image; 450 GLuint texTarget; 451 GLubyte data[4] = {7, 51, 197, 231}; 452 sourceMetalTarget2D_helper(data, kDefaultAttribs, &image, &texTarget); 453 454 // Use texture target bound to egl image as source and render to framebuffer 455 // Verify that data in framebuffer matches that in the egl image 456 verifyResults2D(texTarget, data); 457 458 // Respecify texture size and verify results 459 std::array<GLubyte, 16> referenceColor; 460 referenceColor.fill(127); 461 glBindTexture(GL_TEXTURE_2D, texTarget); 462 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 463 referenceColor.data()); 464 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 465 referenceColor.data()); 466 ASSERT_GL_NO_ERROR(); 467 468 // Expect that the target texture has the reference color values 469 verifyResults2D(texTarget, referenceColor.data()); 470 471 // Clean up 472 eglDestroyImageKHR(window->getDisplay(), image); 473 glDeleteTextures(1, &texTarget); 474} 475 476// Tests that OpenGL can sample from a texture bound with Metal texture slice. 477TEST_P(ImageTestMetal, SourceMetalTarget2DArray) 478{ 479 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 480 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 481 ScopedMetalTextureRef textureMtl = createMtlTexture2DArray(1, 1, 3, MTLPixelFormatRGBA8Unorm); 482 483 GLubyte data0[4] = {93, 83, 75, 128}; 484 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 485 mipmapLevel:0 486 slice:0 487 withBytes:data0 488 bytesPerRow:4 489 bytesPerImage:4]; 490 GLubyte data1[4] = {7, 51, 197, 231}; 491 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 492 mipmapLevel:0 493 slice:1 494 withBytes:data1 495 bytesPerRow:4 496 bytesPerImage:4]; 497 GLubyte data2[4] = {33, 51, 44, 33}; 498 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 499 mipmapLevel:0 500 slice:2 501 withBytes:data2 502 bytesPerRow:4 503 bytesPerImage:4]; 504 505 EGLDisplay display = getEGLWindow()->getDisplay(); 506 EGLImageKHR image0 = 507 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 508 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), nullptr); 509 ASSERT_EGL_SUCCESS(); 510 const EGLint attribs1[] = {EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 1, EGL_NONE}; 511 EGLImageKHR image1 = 512 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 513 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs1); 514 ASSERT_EGL_SUCCESS(); 515 const EGLint attribs2[] = {EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 2, EGL_NONE}; 516 EGLImageKHR image2 = 517 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 518 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs2); 519 ASSERT_EGL_SUCCESS(); 520 521 GLTexture targetTexture; 522 glBindTexture(GL_TEXTURE_2D, targetTexture); 523 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image0); 524 verifyResults2D(targetTexture, data0); 525 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image1); 526 verifyResults2D(targetTexture, data1); 527 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image2); 528 verifyResults2D(targetTexture, data2); 529 eglDestroyImageKHR(display, image0); 530 eglDestroyImageKHR(display, image1); 531 eglDestroyImageKHR(display, image2); 532 EXPECT_GL_NO_ERROR(); 533 EXPECT_EGL_SUCCESS(); 534} 535 536// Test that bound slice to EGLImage is not affected by releasing the source texture. 537TEST_P(ImageTestMetal, SourceMetalTarget2DArrayReleasedSourceOk) 538{ 539 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 540 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 541 ScopedMetalTextureRef textureMtl = createMtlTexture2DArray(1, 1, 3, MTLPixelFormatRGBA8Unorm); 542 543 GLubyte data1[4] = {7, 51, 197, 231}; 544 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 545 mipmapLevel:0 546 slice:1 547 withBytes:data1 548 bytesPerRow:4 549 bytesPerImage:4]; 550 551 EGLDisplay display = getEGLWindow()->getDisplay(); 552 const EGLint attribs1[] = {EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 1, EGL_NONE}; 553 EGLImageKHR image1 = 554 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 555 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs1); 556 ASSERT_EGL_SUCCESS(); 557 // This is being tested: release the source texture but the slice keeps working. 558 textureMtl = {}; 559 GLTexture targetTexture; 560 glBindTexture(GL_TEXTURE_2D, targetTexture); 561 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image1); 562 verifyResults2D(targetTexture, data1); 563 eglDestroyImageKHR(display, image1); 564 EXPECT_GL_NO_ERROR(); 565 EXPECT_EGL_SUCCESS(); 566} 567 568// Tests that OpenGL can draw to a texture bound with Metal texture. 569TEST_P(ImageTestMetal, DrawMetalTarget2D) 570{ 571 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 572 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 573 574 EGLDisplay display = getEGLWindow()->getDisplay(); 575 576 ScopedMetalTextureRef textureMtl = createMtlTexture2D(1, 1, MTLPixelFormatRGBA8Unorm); 577 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 578 mipmapLevel:0 579 slice:0 580 withBytes:GLColor::red.data() 581 bytesPerRow:4 582 bytesPerImage:4]; 583 584 EGLImageKHR image = 585 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 586 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), nullptr); 587 ASSERT_EGL_SUCCESS(); 588 589 GLTexture texture; 590 glBindTexture(GL_TEXTURE_2D, texture); 591 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 592 593 GLFramebuffer targetFbo; 594 glBindFramebuffer(GL_FRAMEBUFFER, targetFbo); 595 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 596 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); 597 598 glViewport(0, 0, 1, 1); 599 drawColorQuad(GLColor::magenta); 600 EXPECT_GL_NO_ERROR(); 601 eglDestroyImageKHR(display, image); 602 eglWaitUntilWorkScheduledANGLE(display); 603 EXPECT_GL_NO_ERROR(); 604 EXPECT_EGL_SUCCESS(); 605 606 GLColor result; 607 getTextureSliceBytes(textureMtl, 4, MTLRegionMake2D(0, 0, 1, 1), 0, 0, {result.data(), 4}); 608 EXPECT_EQ(result, GLColor::magenta); 609} 610 611// Tests that OpenGL can draw to a texture bound with Metal texture slice. 612TEST_P(ImageTestMetal, DrawMetalTarget2DArray) 613{ 614 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 615 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 616 617 EGLDisplay display = getEGLWindow()->getDisplay(); 618 619 ScopedMetalTextureRef textureMtl = createMtlTexture2DArray(1, 1, 2, MTLPixelFormatRGBA8Unorm); 620 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 621 mipmapLevel:0 622 slice:0 623 withBytes:GLColor::red.data() 624 bytesPerRow:4 625 bytesPerImage:4]; 626 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 627 mipmapLevel:0 628 slice:1 629 withBytes:GLColor::red.data() 630 bytesPerRow:4 631 bytesPerImage:4]; 632 633 const EGLint attribs[] = {EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 1, EGL_NONE}; 634 EGLImageKHR image = 635 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 636 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 637 ASSERT_EGL_SUCCESS(); 638 639 GLTexture texture; 640 glBindTexture(GL_TEXTURE_2D, texture); 641 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 642 EXPECT_GL_NO_ERROR(); 643 GLFramebuffer targetFbo; 644 glBindFramebuffer(GL_FRAMEBUFFER, targetFbo); 645 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 646 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); 647 648 glViewport(0, 0, 1, 1); 649 drawColorQuad(GLColor::magenta); 650 EXPECT_GL_NO_ERROR(); 651 eglDestroyImageKHR(display, image); 652 eglWaitUntilWorkScheduledANGLE(display); 653 EXPECT_GL_NO_ERROR(); 654 EXPECT_EGL_SUCCESS(); 655 656 GLColor result; 657 getTextureSliceBytes(textureMtl, 4, MTLRegionMake2D(0, 0, 1, 1), 0, 1, {result.data(), 4}); 658 EXPECT_EQ(result, GLColor::magenta); 659} 660 661// Tests that OpenGL can blit to a texture bound with Metal texture slice. 662TEST_P(ImageTestMetal, BlitMetalTarget2DArray) 663{ 664 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 665 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 666 667 EGLDisplay display = getEGLWindow()->getDisplay(); 668 669 GLTexture colorBuffer; 670 glBindTexture(GL_TEXTURE_2D, colorBuffer); 671 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 672 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 673 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); 674 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, GLColor::green.data()); 675 glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 676 GLColor::yellow.data()); 677 EXPECT_GL_NO_ERROR(); 678 679 GLFramebuffer sourceFbo; 680 glBindFramebuffer(GL_FRAMEBUFFER, sourceFbo); 681 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0); 682 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); 683 684 ScopedMetalTextureRef textureMtl = createMtlTexture2DArray(1, 1, 2, MTLPixelFormatRGBA8Unorm); 685 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 686 mipmapLevel:0 687 slice:0 688 withBytes:GLColor::red.data() 689 bytesPerRow:4 690 bytesPerImage:4]; 691 [textureMtl replaceRegion:MTLRegionMake2D(0, 0, 1, 1) 692 mipmapLevel:0 693 slice:1 694 withBytes:GLColor::red.data() 695 bytesPerRow:4 696 bytesPerImage:4]; 697 698 for (int slice = 0; slice < 2; ++slice) 699 { 700 const EGLint attribs[] = {EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, slice, EGL_NONE}; 701 EGLImageKHR image = 702 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 703 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 704 ASSERT_EGL_SUCCESS(); 705 706 GLTexture texture; 707 glBindTexture(GL_TEXTURE_2D, texture); 708 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 709 glBindFramebuffer(GL_FRAMEBUFFER, 0); 710 verifyResults2D(texture, GLColor::red.data()); 711 EXPECT_GL_NO_ERROR(); 712 713 GLFramebuffer targetFbo; 714 glBindFramebuffer(GL_FRAMEBUFFER, targetFbo); 715 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 716 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); 717 718 glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, sourceFbo); 719 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, targetFbo); 720 glBlitFramebufferANGLE(slice, 0, slice + 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); 721 EXPECT_GL_NO_ERROR(); 722 723 glBindFramebuffer(GL_FRAMEBUFFER, 0); 724 verifyResults2D(texture, slice == 0 ? GLColor::green.data() : GLColor::yellow.data()); 725 eglDestroyImageKHR(display, image); 726 EXPECT_GL_NO_ERROR(); 727 EXPECT_EGL_SUCCESS(); 728 } 729 eglWaitUntilWorkScheduledANGLE(display); 730 EXPECT_EGL_SUCCESS(); 731 732 GLColor result; 733 getTextureSliceBytes(textureMtl, 4, MTLRegionMake2D(0, 0, 1, 1), 0, 0, {result.data(), 4}); 734 EXPECT_EQ(result, GLColor::green); 735 getTextureSliceBytes(textureMtl, 4, MTLRegionMake2D(0, 0, 1, 1), 0, 1, {result.data(), 4}); 736 EXPECT_EQ(result, GLColor::yellow); 737} 738 739// Tests that OpenGL can override the internal format for a texture bound with 740// Metal texture. 741TEST_P(ImageTestMetal, OverrideMetalTextureInternalFormat) 742{ 743 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 744 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 745 ANGLE_SKIP_TEST_IF(hasDepth24Stencil8PixelFormat()); 746 747 EGLDisplay display = getEGLWindow()->getDisplay(); 748 749 // On iOS devices, GL_DEPTH24_STENCIL8 is unavailable and is interally converted into 750 // GL_DEPTH32F_STENCIL8. This tests the ability to attach MTLPixelFormatDepth32Float_Stencil8 751 // and have GL treat it as GL_DEPTH24_STENCIL8 instead of GL_DEPTH32F_STENCIL8. 752 ScopedMetalTextureRef textureMtl = 753 createMtlTexture2DArray(1, 1, 1, MTLPixelFormatDepth32Float_Stencil8); 754 const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_DEPTH24_STENCIL8, EGL_NONE}; 755 EGLImageKHR image = 756 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 757 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 758 EXPECT_EGL_SUCCESS(); 759 EXPECT_NE(image, nullptr); 760} 761 762// Tests that OpenGL can override the internal format for a texture bound with 763// Metal texture and that rendering to the texture is successful. 764TEST_P(ImageTestMetal, RenderingTest) 765{ 766 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 767 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 768 ANGLE_SKIP_TEST_IF(hasDepth24Stencil8PixelFormat()); 769 770 EGLDisplay display = getEGLWindow()->getDisplay(); 771 772 const int bufferSize = 32; 773 ScopedMetalTextureRef textureMtl = 774 createMtlTexture2DArray(bufferSize, bufferSize, 1, MTLPixelFormatDepth32Float_Stencil8); 775 const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_DEPTH24_STENCIL8, EGL_NONE}; 776 EGLImageKHR image = 777 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 778 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 779 EXPECT_EGL_SUCCESS(); 780 EXPECT_NE(image, nullptr); 781 782 GLRenderbuffer colorBuffer; 783 glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); 784 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, bufferSize, bufferSize); 785 EXPECT_GL_NO_ERROR(); 786 787 GLRenderbuffer depthStencilBuffer; 788 glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer); 789 glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image); 790 EXPECT_GL_NO_ERROR(); 791 792 GLFramebuffer fb; 793 glBindFramebuffer(GL_FRAMEBUFFER, fb); 794 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); 795 if (getClientMajorVersion() >= 3) 796 { 797 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 798 depthStencilBuffer); 799 } 800 else 801 { 802 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 803 depthStencilBuffer); 804 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 805 depthStencilBuffer); 806 } 807 808 EXPECT_GL_NO_ERROR(); 809 ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); 810 811 glClearColor(1.f, 0.f, 0.f, 1.f); 812 glClearDepthf(1.f); 813 glClearStencil(0x55); 814 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 815 EXPECT_GL_NO_ERROR(); 816 817 glEnable(GL_DEPTH_TEST); 818 glDepthFunc(GL_LESS); 819 820 glEnable(GL_STENCIL_TEST); 821 glStencilFunc(GL_EQUAL, 0x55, 0xFF); 822 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 823 glStencilMask(0xFF); 824 825 // Draw green. 826 ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); 827 drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.95f); 828 829 // Verify that green was drawn. 830 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); 831 EXPECT_PIXEL_COLOR_EQ(0, bufferSize - 1, GLColor::green); 832 EXPECT_PIXEL_COLOR_EQ(bufferSize - 1, 0, GLColor::green); 833 EXPECT_PIXEL_COLOR_EQ(bufferSize - 1, bufferSize - 1, GLColor::green); 834 835 eglDestroyImageKHR(display, image); 836} 837 838// Tests that OpenGL override the with a bad internal format for a texture bound 839// with Metal texture fails. 840TEST_P(ImageTestMetal, OverrideMetalTextureInternalFormatBadFormat) 841{ 842 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 843 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 844 845 EGLDisplay display = getEGLWindow()->getDisplay(); 846 847 // On iOS devices, GL_DEPTH24_STENCIL8 is unavailable and is interally converted into 848 // GL_DEPTH32F_STENCIL8. This tests the ability to attach MTLPixelFormatDepth32Float_Stencil8 849 // and have GL treat it as GL_DEPTH24_STENCIL8 instead of GL_DEPTH32F_STENCIL8. 850 ScopedMetalTextureRef textureMtl = 851 createMtlTexture2DArray(1, 1, 1, MTLPixelFormatDepth32Float_Stencil8); 852 const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_TRIANGLES, EGL_NONE}; 853 EGLImageKHR image = 854 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 855 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 856 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE); 857 EXPECT_EQ(image, nullptr); 858} 859 860// Tests that OpenGL override the with an incompatible internal format for a texture bound 861// with Metal texture fails. 862TEST_P(ImageTestMetal, OverrideMetalTextureInternalFormatIncompatibleFormat) 863{ 864 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 865 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 866 867 EGLDisplay display = getEGLWindow()->getDisplay(); 868 869 // On iOS devices, GL_DEPTH24_STENCIL8 is unavailable and is interally converted into 870 // GL_DEPTH32F_STENCIL8. This tests the ability to attach MTLPixelFormatDepth32Float_Stencil8 871 // and have GL treat it as GL_DEPTH24_STENCIL8 instead of GL_DEPTH32F_STENCIL8. 872 ScopedMetalTextureRef textureMtl = 873 createMtlTexture2DArray(1, 1, 1, MTLPixelFormatDepth32Float_Stencil8); 874 const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGBA8, EGL_NONE}; 875 EGLImageKHR image = 876 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 877 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 878 EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE); 879 EXPECT_EQ(image, nullptr); 880} 881 882// Test this scenario: 883// Metal and GL share the same MTL texture (called texture1). 884// GL Context draws to texture1: 885// - draw. 886// - upload texture2 887// - draw using the texture2 as source. 888// - place a sync object. 889// 890// Metal reads the texture1: 891// - wait for the shared event sync object. 892// - copy the texture1 to a buffer. 893// - The buffer should contain color from texture2 after being uploaded. 894// 895// Previously this would cause a bug in Metal backend because texture upload would 896// create a new blit encoder in a middle of a render pass and the command buffer would mistrack the 897// ongoing render encoder. Thus making the metal sync object being placed incorrectly. 898TEST_P(ImageTestMetal, SharedEventSyncWhenThereIsTextureUploadBetweenDraws) 899{ 900 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 901 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 902 903 EGLDisplay display1 = getEGLWindow()->getDisplay(); 904 905 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); 906 ANGLE_SKIP_TEST_IF( 907 !IsEGLDisplayExtensionEnabled(display1, "EGL_ANGLE_metal_shared_event_sync")); 908 909 @autoreleasepool 910 { 911 // Create MTLTexture 912 constexpr int kSharedTextureSize = 1024; 913 ScopedMetalTextureRef textureMtl = 914 createMtlTexture2D(kSharedTextureSize, kSharedTextureSize, MTLPixelFormatR32Sint); 915 916 // Create SharedEvent 917 id<MTLDevice> deviceMtl = getMtlDevice(); 918 id<MTLSharedEvent> sharedEventMtl = CreateMetalSharedEvent(deviceMtl); 919 920 // -------------------------- Metal --------------------------- 921 // Create a buffer on Metal to store the final value. 922 ScopedMetalBufferRef dstBuffer( 923 [deviceMtl newBufferWithLength:sizeof(float) options:MTLResourceStorageModeShared]); 924 ScopedMetalCommandQueueRef commandQueue([deviceMtl newCommandQueue]); 925 id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer]; 926 927 // Wait for drawing on GL context to finish on server side. 928 // Note: we issue a wait even before the draw calls are issued on GL context. 929 // GL context will issue a signaling later (see below). 930 constexpr uint64_t kSignalValue = 0xff; 931 [commandBuffer encodeWaitForEvent:sharedEventMtl value:kSignalValue]; 932 933 // Copy a pixel from texture1 to dstBuffer 934 id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder]; 935 [blitEncoder copyFromTexture:textureMtl 936 sourceSlice:0 937 sourceLevel:0 938 sourceOrigin:MTLOriginMake(kSharedTextureSize - 1, kSharedTextureSize - 2, 939 0) 940 sourceSize:MTLSizeMake(1, 1, 1) 941 toBuffer:dstBuffer 942 destinationOffset:0 943 destinationBytesPerRow:sizeof(float) * kSharedTextureSize 944 destinationBytesPerImage:0]; 945 [blitEncoder endEncoding]; 946 [commandBuffer commit]; 947 948 // -------------------------- GL context --------------------------- 949 constexpr int kNumValues = 1000; 950 // A deliberately slow shader that reads a texture many times then write 951 // the sum value to an ouput varible. 952 constexpr char kFS[] = R"(#version 300 es 953out highp ivec4 outColor; 954 955uniform highp isampler2D u_valuesTex; 956 957void main() 958{ 959 highp int value = 0; 960 for (int i = 0; i < 1000; ++i) { 961 highp float uCoords = (float(i) + 0.5) / float(1000); 962 value += textureLod(u_valuesTex, vec2(uCoords, 0.0), 0.0).r; 963 } 964 965 outColor = ivec4(value); 966})"; 967 968 ANGLE_GL_PROGRAM(complexProgram, essl3_shaders::vs::Simple(), kFS); 969 GLint valuesTexLocation = glGetUniformLocation(complexProgram, "u_valuesTex"); 970 ASSERT_NE(valuesTexLocation, -1); 971 972 // Create the shared texture from MTLTexture. 973 EGLImageKHR image = 974 eglCreateImageKHR(display1, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 975 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), kDefaultAttribs); 976 EXPECT_EGL_SUCCESS(); 977 GLTexture texture1; 978 glBindTexture(GL_TEXTURE_2D, texture1); 979 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 980 EXPECT_GL_ERROR(GL_NO_ERROR); 981 glViewport(0, 0, kSharedTextureSize, kSharedTextureSize); 982 983 // Create texture holding multiple values to be accumulated in shader. 984 GLTexture texture2; 985 glBindTexture(GL_TEXTURE_2D, texture2); 986 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 987 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 988 glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, kNumValues, 1, 0, GL_RED_INTEGER, GL_INT, nullptr); 989 glFlush(); 990 EXPECT_GL_ERROR(GL_NO_ERROR); 991 992 // Using GL context to draw to the texture1 993 glBindTexture(GL_TEXTURE_2D, texture1); 994 GLFramebuffer framebuffer1; 995 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1); 996 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0); 997 998 // First draw with initial color 999 { 1000 constexpr char kSimpleFS[] = R"(#version 300 es 1001out highp ivec4 outColor; 1002 1003void main() 1004{ 1005 outColor = ivec4(1); 1006})"; 1007 ANGLE_GL_PROGRAM(colorProgram, angle::essl3_shaders::vs::Simple(), kSimpleFS); 1008 drawQuad(colorProgram, angle::essl3_shaders::PositionAttrib(), 0.5f); 1009 } 1010 1011 // Upload the texture2 1012 std::vector<int32_t> values(kNumValues); 1013 for (size_t i = 0; i < values.size(); ++i) 1014 { 1015 values[i] = i; 1016 } 1017 glBindTexture(GL_TEXTURE_2D, texture2); 1018 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kNumValues, 1, GL_RED_INTEGER, GL_INT, 1019 values.data()); 1020 1021 // 2nd draw call draw the texture2 to texture1. 1022 glUseProgram(complexProgram); 1023 drawQuad(complexProgram, angle::essl1_shaders::PositionAttrib(), 0.5f); 1024 1025 // Place a sync object on GL context's commands stream. 1026 EGLSync syncGL = CreateEGLSyncFromMetalSharedEvent(display1, sharedEventMtl, kSignalValue, 1027 /*signaled=*/false); 1028 glFlush(); 1029 1030 // -------------------------- Metal --------------------------- 1031 [commandBuffer waitUntilCompleted]; 1032 1033 // Read dstBuffer 1034 const int32_t kExpectedSum = kNumValues * (kNumValues - 1) / 2; 1035 int32_t *mappedInts = static_cast<int32_t *>(dstBuffer.get().contents); 1036 EXPECT_EQ(mappedInts[0], kExpectedSum); 1037 1038 eglDestroySync(display1, syncGL); 1039 eglDestroyImage(display1, image); 1040 1041 } // @autoreleasepool 1042} 1043 1044class ImageClearTestMetal : public ImageTestMetal 1045{ 1046 protected: 1047 ImageClearTestMetal() : ImageTestMetal() {} 1048 1049 void RunUnsizedClearTest(MTLPixelFormat format) 1050 { 1051 ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); 1052 ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); 1053 1054 EGLWindow *window = getEGLWindow(); 1055 EGLDisplay display = window->getDisplay(); 1056 1057 window->makeCurrent(); 1058 1059 const GLint bufferSize = 32; 1060 ScopedMetalTextureRef textureMtl = 1061 createMtlTexture2DArray(bufferSize, bufferSize, 1, format); 1062 EXPECT_TRUE(textureMtl); 1063 1064 EGLint internalFormat = GL_NONE; 1065 switch (format) 1066 { 1067 case MTLPixelFormatR8Unorm: 1068 case MTLPixelFormatR16Unorm: 1069 internalFormat = GL_RED_EXT; 1070 break; 1071 case MTLPixelFormatRG8Unorm: 1072 case MTLPixelFormatRG16Unorm: 1073 internalFormat = GL_RG_EXT; 1074 break; 1075 case MTLPixelFormatRGBA8Unorm: 1076 case MTLPixelFormatRGBA16Float: 1077 case MTLPixelFormatRGB10A2Unorm: 1078 internalFormat = GL_RGBA; 1079 break; 1080 case MTLPixelFormatRGBA8Unorm_sRGB: 1081 internalFormat = GL_SRGB_ALPHA_EXT; 1082 break; 1083 case MTLPixelFormatBGRA8Unorm: 1084 internalFormat = GL_BGRA_EXT; 1085 break; 1086 default: 1087 break; 1088 } 1089 1090 const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, internalFormat, EGL_NONE}; 1091 1092 EGLImageKHR image = 1093 eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE, 1094 reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs); 1095 ASSERT_EGL_SUCCESS(); 1096 ASSERT_NE(image, EGL_NO_IMAGE_KHR); 1097 1098 GLTexture texture; 1099 glBindTexture(GL_TEXTURE_2D, texture); 1100 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1101 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1102 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1103 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1104 EXPECT_GL_NO_ERROR(); 1105 1106 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); 1107 EXPECT_GL_NO_ERROR(); 1108 1109 GLFramebuffer fbo; 1110 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 1111 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 1112 EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), 1113 static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE)); 1114 EXPECT_GL_NO_ERROR(); 1115 1116 glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize)); 1117 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 1118 glClear(GL_COLOR_BUFFER_BIT); 1119 ASSERT_GL_NO_ERROR(); 1120 1121 if (format == MTLPixelFormatRGBA16Float) 1122 { 1123 EXPECT_PIXEL_32F_EQ(bufferSize / 2, bufferSize / 2, 1.0f, 1.0f, 1.0f, 1.0f); 1124 } 1125 else if (format == MTLPixelFormatR16Unorm) 1126 { 1127 EXPECT_PIXEL_16_NEAR(bufferSize / 2, bufferSize / 2, 65535, 0, 0, 65535, 0); 1128 } 1129 else if (format == MTLPixelFormatRG16Unorm) 1130 { 1131 EXPECT_PIXEL_16_NEAR(bufferSize / 2, bufferSize / 2, 65535, 65535, 0, 65535, 0); 1132 } 1133 else 1134 { 1135 GLuint readColor[4] = {0, 0, 0, 255}; 1136 switch (format) 1137 { 1138 case MTLPixelFormatR8Unorm: 1139 readColor[0] = 255; 1140 break; 1141 case MTLPixelFormatRG8Unorm: 1142 readColor[0] = readColor[1] = 255; 1143 break; 1144 case MTLPixelFormatRGBA8Unorm: 1145 case MTLPixelFormatRGB10A2Unorm: 1146 case MTLPixelFormatRGBA16Float: 1147 case MTLPixelFormatRGBA8Unorm_sRGB: 1148 case MTLPixelFormatBGRA8Unorm: 1149 readColor[0] = readColor[1] = readColor[2] = 255; 1150 break; 1151 default: 1152 break; 1153 } 1154 // Read back as GL_UNSIGNED_BYTE even though the texture might have more than 8bpc. 1155 EXPECT_PIXEL_EQ(bufferSize / 2, bufferSize / 2, readColor[0], readColor[1], 1156 readColor[2], readColor[3]); 1157 } 1158 } 1159}; 1160 1161TEST_P(ImageClearTestMetal, ClearUnsizedRGBA8) 1162{ 1163 RunUnsizedClearTest(MTLPixelFormatRGBA8Unorm); 1164} 1165 1166TEST_P(ImageClearTestMetal, ClearUnsizedsRGBA8) 1167{ 1168 RunUnsizedClearTest(MTLPixelFormatRGBA8Unorm_sRGB); 1169} 1170 1171TEST_P(ImageClearTestMetal, ClearUnsizedBGRA8) 1172{ 1173 RunUnsizedClearTest(MTLPixelFormatBGRA8Unorm); 1174} 1175 1176TEST_P(ImageClearTestMetal, ClearUnsizedR8) 1177{ 1178 RunUnsizedClearTest(MTLPixelFormatR8Unorm); 1179} 1180 1181TEST_P(ImageClearTestMetal, ClearUnsizedRG8) 1182{ 1183 RunUnsizedClearTest(MTLPixelFormatRG8Unorm); 1184} 1185 1186TEST_P(ImageClearTestMetal, ClearUnsizedRGB10A2) 1187{ 1188 RunUnsizedClearTest(MTLPixelFormatRGB10A2Unorm); 1189} 1190 1191TEST_P(ImageClearTestMetal, ClearUnsizedRGBAF16) 1192{ 1193 RunUnsizedClearTest(MTLPixelFormatRGBA16Float); 1194} 1195 1196TEST_P(ImageClearTestMetal, ClearUnsizedR16) 1197{ 1198 RunUnsizedClearTest(MTLPixelFormatR16Unorm); 1199} 1200 1201TEST_P(ImageClearTestMetal, ClearUnsizedRG16) 1202{ 1203 RunUnsizedClearTest(MTLPixelFormatRG16Unorm); 1204} 1205 1206// Use this to select which configurations (e.g. which renderer, which GLES major version) these 1207// tests should be run against. 1208ANGLE_INSTANTIATE_TEST(ImageTestMetal, ES2_METAL(), ES3_METAL()); 1209ANGLE_INSTANTIATE_TEST(ImageClearTestMetal, ES2_METAL(), ES3_METAL()); 1210GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImageTestMetal); 1211GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImageClearTestMetal); 1212} // namespace angle 1213