| // | |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // ImageTest: | |
| // Tests the correctness of eglImage. | |
| // | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.h" | |
| #include "util/EGLWindow.h" | |
| #include "common/android_util.h" | |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 26 | |
| # define ANGLE_AHARDWARE_BUFFER_SUPPORT | |
| // NDK header file for access to Android Hardware Buffers | |
| # include <android/hardware_buffer.h> | |
| # if __ANDROID_API__ >= 29 | |
| # define ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| # endif | |
| #endif | |
| namespace angle | |
| { | |
| namespace | |
| { | |
| constexpr char kOESExt[] = "GL_OES_EGL_image"; | |
| constexpr char kExternalExt[] = "GL_OES_EGL_image_external"; | |
| constexpr char kExternalESSL3Ext[] = "GL_OES_EGL_image_external_essl3"; | |
| constexpr char kYUVTargetExt[] = "GL_EXT_YUV_target"; | |
| constexpr char kBaseExt[] = "EGL_KHR_image_base"; | |
| constexpr char k2DTextureExt[] = "EGL_KHR_gl_texture_2D_image"; | |
| constexpr char k3DTextureExt[] = "EGL_KHR_gl_texture_3D_image"; | |
| constexpr char kPixmapExt[] = "EGL_KHR_image_pixmap"; | |
| constexpr char kRenderbufferExt[] = "EGL_KHR_gl_renderbuffer_image"; | |
| constexpr char kCubemapExt[] = "EGL_KHR_gl_texture_cubemap_image"; | |
| constexpr char kImageGLColorspaceExt[] = "EGL_EXT_image_gl_colorspace"; | |
| constexpr char kEGLImageArrayExt[] = "GL_EXT_EGL_image_array"; | |
| constexpr char kEGLAndroidImageNativeBufferExt[] = "EGL_ANDROID_image_native_buffer"; | |
| constexpr EGLint kDefaultAttribs[] = { | |
| EGL_IMAGE_PRESERVED, | |
| EGL_TRUE, | |
| EGL_NONE, | |
| }; | |
| constexpr EGLint kColorspaceAttribs[] = { | |
| EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE, | |
| }; | |
| constexpr EGLint kNativeClientBufferAttribs_RGBA8_Texture[] = { | |
| EGL_WIDTH, | |
| 1, | |
| EGL_HEIGHT, | |
| 1, | |
| EGL_RED_SIZE, | |
| 8, | |
| EGL_GREEN_SIZE, | |
| 8, | |
| EGL_BLUE_SIZE, | |
| 8, | |
| EGL_ALPHA_SIZE, | |
| 8, | |
| EGL_NATIVE_BUFFER_USAGE_ANDROID, | |
| EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID, | |
| EGL_NONE}; | |
| constexpr EGLint kNativeClientBufferAttribs_RGBA8_Renderbuffer[] = { | |
| EGL_WIDTH, | |
| 1, | |
| EGL_HEIGHT, | |
| 1, | |
| EGL_RED_SIZE, | |
| 8, | |
| EGL_GREEN_SIZE, | |
| 8, | |
| EGL_BLUE_SIZE, | |
| 8, | |
| EGL_ALPHA_SIZE, | |
| 8, | |
| EGL_NATIVE_BUFFER_USAGE_ANDROID, | |
| EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID, | |
| EGL_NONE}; | |
| // Color data in linear and sRGB colorspace | |
| // 2D texture data | |
| GLubyte kLinearColor[] = {132, 55, 219, 255}; | |
| GLubyte kSrgbColor[] = {59, 10, 180, 255}; | |
| // 3D texture data | |
| GLubyte kLinearColor3D[] = {131, 242, 100, 255, 201, 89, 133, 255}; | |
| GLubyte kSrgbColor3D[] = {58, 226, 32, 255, 149, 26, 60, 255}; | |
| // Cubemap texture data | |
| GLubyte kLinearColorCube[] = {75, 135, 205, 255, 201, 89, 133, 255, 111, 201, 108, 255, | |
| 30, 90, 230, 255, 180, 210, 70, 255, 77, 111, 99, 255}; | |
| GLubyte kSrgbColorCube[] = {18, 62, 155, 255, 149, 26, 60, 255, 41, 149, 38, 255, | |
| 3, 26, 202, 255, 117, 164, 16, 255, 19, 41, 32, 255}; | |
| constexpr int kColorspaceAttributeIndex = 2; | |
| constexpr int AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1; | |
| constexpr int AHARDWAREBUFFER_FORMAT_D24_UNORM = 0x31; | |
| constexpr int AHARDWAREBUFFER_FORMAT_Y8Cr8Cb8_420_SP = 0x11; | |
| constexpr int AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23; | |
| constexpr int AHARDWAREBUFFER_FORMAT_YV12 = 0x32315659; | |
| } // anonymous namespace | |
| class ImageTest : public ANGLETest | |
| { | |
| protected: | |
| ImageTest() | |
| { | |
| setWindowWidth(128); | |
| setWindowHeight(128); | |
| setConfigRedBits(8); | |
| setConfigGreenBits(8); | |
| setConfigBlueBits(8); | |
| setConfigAlphaBits(8); | |
| setConfigDepthBits(24); | |
| } | |
| void testSetUp() override | |
| { | |
| constexpr char kVS[] = | |
| "precision highp float;\n" | |
| "attribute vec4 position;\n" | |
| "varying vec2 texcoord;\n" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_Position = position;\n" | |
| " texcoord = (position.xy * 0.5) + 0.5;\n" | |
| " texcoord.y = 1.0 - texcoord.y;\n" | |
| "}\n"; | |
| constexpr char kVS2DArray[] = | |
| "#version 300 es\n" | |
| "out vec2 texcoord;\n" | |
| "in vec4 position;\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_Position = vec4(position.xy, 0.0, 1.0);\n" | |
| " texcoord = (position.xy * 0.5) + 0.5;\n" | |
| "}\n"; | |
| constexpr char kVSESSL3[] = | |
| "#version 300 es\n" | |
| "precision highp float;\n" | |
| "in vec4 position;\n" | |
| "out vec2 texcoord;\n" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_Position = position;\n" | |
| " texcoord = (position.xy * 0.5) + 0.5;\n" | |
| " texcoord.y = 1.0 - texcoord.y;\n" | |
| "}\n"; | |
| constexpr char kTextureFS[] = | |
| "precision highp float;\n" | |
| "uniform sampler2D tex;\n" | |
| "varying vec2 texcoord;\n" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_FragColor = texture2D(tex, texcoord);\n" | |
| "}\n"; | |
| constexpr char kTexture2DArrayFS[] = | |
| "#version 300 es\n" | |
| "precision highp float;\n" | |
| "uniform highp sampler2DArray tex2DArray;\n" | |
| "in vec2 texcoord;\n" | |
| "out vec4 fragColor;\n" | |
| "void main()\n" | |
| "{\n" | |
| " fragColor = texture(tex2DArray, vec3(texcoord.x, texcoord.y, 0.0));\n" | |
| "}\n"; | |
| constexpr char kTextureExternalFS[] = | |
| "#extension GL_OES_EGL_image_external : require\n" | |
| "precision highp float;\n" | |
| "uniform samplerExternalOES tex;\n" | |
| "varying vec2 texcoord;\n" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_FragColor = texture2D(tex, texcoord);\n" | |
| "}\n"; | |
| constexpr char kTextureExternalESSL3FS[] = | |
| "#version 300 es\n" | |
| "#extension GL_OES_EGL_image_external_essl3 : require\n" | |
| "precision highp float;\n" | |
| "uniform samplerExternalOES tex;\n" | |
| "in vec2 texcoord;\n" | |
| "out vec4 color;" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " color = texture(tex, texcoord);\n" | |
| "}\n"; | |
| constexpr char kTextureYUVFS[] = | |
| "#version 300 es\n" | |
| "#extension GL_EXT_YUV_target : require\n" | |
| "precision highp float;\n" | |
| "uniform __samplerExternal2DY2YEXT tex;\n" | |
| "in vec2 texcoord;\n" | |
| "out vec4 color;" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " color = texture(tex, texcoord);\n" | |
| "}\n"; | |
| constexpr char kRenderYUVFS[] = | |
| "#version 300 es\n" | |
| "#extension GL_EXT_YUV_target : require\n" | |
| "precision highp float;\n" | |
| "uniform vec4 u_color;\n" | |
| "layout (yuv) out vec4 color;" | |
| "\n" | |
| "void main()\n" | |
| "{\n" | |
| " color = u_color;\n" | |
| "}\n"; | |
| mTextureProgram = CompileProgram(kVS, kTextureFS); | |
| if (mTextureProgram == 0) | |
| { | |
| FAIL() << "shader compilation failed."; | |
| } | |
| mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); | |
| if (getClientMajorVersion() >= 3) | |
| { | |
| m2DArrayTextureProgram = CompileProgram(kVS2DArray, kTexture2DArrayFS); | |
| if (m2DArrayTextureProgram == 0) | |
| { | |
| FAIL() << "shader compilation failed."; | |
| } | |
| m2DArrayTextureUniformLocation = | |
| glGetUniformLocation(m2DArrayTextureProgram, "tex2DArray"); | |
| } | |
| if (IsGLExtensionEnabled("GL_OES_EGL_image_external")) | |
| { | |
| mTextureExternalProgram = CompileProgram(kVS, kTextureExternalFS); | |
| ASSERT_NE(0u, mTextureExternalProgram) << "shader compilation failed."; | |
| mTextureExternalUniformLocation = glGetUniformLocation(mTextureExternalProgram, "tex"); | |
| } | |
| if (IsGLExtensionEnabled("GL_OES_EGL_image_external_essl3")) | |
| { | |
| mTextureExternalESSL3Program = CompileProgram(kVSESSL3, kTextureExternalESSL3FS); | |
| ASSERT_NE(0u, mTextureExternalESSL3Program) << "shader compilation failed."; | |
| mTextureExternalESSL3UniformLocation = | |
| glGetUniformLocation(mTextureExternalESSL3Program, "tex"); | |
| } | |
| if (IsGLExtensionEnabled(kYUVTargetExt)) | |
| { | |
| mTextureYUVProgram = CompileProgram(kVSESSL3, kTextureYUVFS); | |
| ASSERT_NE(0u, mTextureYUVProgram) << "shader compilation failed."; | |
| mTextureYUVUniformLocation = glGetUniformLocation(mTextureYUVProgram, "tex"); | |
| mRenderYUVProgram = CompileProgram(kVSESSL3, kRenderYUVFS); | |
| ASSERT_NE(0u, mRenderYUVProgram) << "shader compilation failed."; | |
| mRenderYUVUniformLocation = glGetUniformLocation(mRenderYUVProgram, "u_color"); | |
| } | |
| ASSERT_GL_NO_ERROR(); | |
| } | |
| void testTearDown() override | |
| { | |
| glDeleteProgram(mTextureProgram); | |
| glDeleteProgram(mTextureExternalProgram); | |
| glDeleteProgram(mTextureExternalESSL3Program); | |
| glClearColor(0, 0, 0, 0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| } | |
| void createEGLImage2DTextureSource(size_t width, | |
| size_t height, | |
| GLenum format, | |
| GLenum type, | |
| const EGLint *attribs, | |
| void *data, | |
| GLuint *outSourceTexture, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Create a source 2D texture | |
| GLuint source; | |
| glGenTextures(1, &source); | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexImage2D(GL_TEXTURE_2D, 0, format, static_cast<GLsizei>(width), | |
| static_cast<GLsizei>(height), 0, format, type, data); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| // Create an image from the source texture | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(source), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceTexture = source; | |
| *outSourceImage = image; | |
| } | |
| void createEGLImageCubemapTextureSource(size_t width, | |
| size_t height, | |
| GLenum format, | |
| GLenum type, | |
| const EGLint *attribs, | |
| uint8_t *data, | |
| size_t dataStride, | |
| EGLenum imageTarget, | |
| GLuint *outSourceTexture, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Create a source cube map texture | |
| GLuint source; | |
| glGenTextures(1, &source); | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, source); | |
| for (GLenum faceIdx = 0; faceIdx < 6; faceIdx++) | |
| { | |
| glTexImage2D(faceIdx + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, | |
| static_cast<GLsizei>(width), static_cast<GLsizei>(height), 0, format, type, | |
| data + (faceIdx * dataStride)); | |
| } | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| // Create an image from the source texture | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), imageTarget, | |
| reinterpretHelper<EGLClientBuffer>(source), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceTexture = source; | |
| *outSourceImage = image; | |
| } | |
| void createEGLImage3DTextureSource(size_t width, | |
| size_t height, | |
| size_t depth, | |
| GLenum format, | |
| GLenum type, | |
| const EGLint *attribs, | |
| void *data, | |
| GLuint *outSourceTexture, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Create a source 3D texture | |
| GLuint source; | |
| glGenTextures(1, &source); | |
| glBindTexture(GL_TEXTURE_3D, source); | |
| glTexImage3D(GL_TEXTURE_3D, 0, format, static_cast<GLsizei>(width), | |
| static_cast<GLsizei>(height), static_cast<GLsizei>(depth), 0, format, type, | |
| data); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| // Create an image from the source texture | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_3D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(source), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceTexture = source; | |
| *outSourceImage = image; | |
| } | |
| void createEGLImageRenderbufferSource(size_t width, | |
| size_t height, | |
| GLenum internalFormat, | |
| const EGLint *attribs, | |
| GLubyte data[4], | |
| GLuint *outSourceRenderbuffer, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Create a source renderbuffer | |
| GLuint source; | |
| glGenRenderbuffers(1, &source); | |
| glBindRenderbuffer(GL_RENDERBUFFER, source); | |
| glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, static_cast<GLsizei>(width), | |
| static_cast<GLsizei>(height)); | |
| // Create a framebuffer and clear it to set the data | |
| GLuint framebuffer; | |
| glGenFramebuffers(1, &framebuffer); | |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, source); | |
| glClearColor(data[0] / 255.0f, data[1] / 255.0f, data[2] / 255.0f, data[3] / 255.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| glDeleteFramebuffers(1, &framebuffer); | |
| ASSERT_GL_NO_ERROR(); | |
| // Create an image from the source renderbuffer | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_RENDERBUFFER_KHR, | |
| reinterpretHelper<EGLClientBuffer>(source), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceRenderbuffer = source; | |
| *outSourceImage = image; | |
| } | |
| void createEGLImageTargetTexture2D(EGLImageKHR image, GLuint *outTargetTexture) | |
| { | |
| // Create a target texture from the image | |
| GLuint target; | |
| glGenTextures(1, &target); | |
| glBindTexture(GL_TEXTURE_2D, target); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| *outTargetTexture = target; | |
| } | |
| void createEGLImageTargetTexture2DArray(EGLImageKHR image, GLuint *outTargetTexture) | |
| { | |
| // Create a target texture from the image | |
| GLuint target; | |
| glGenTextures(1, &target); | |
| glBindTexture(GL_TEXTURE_2D_ARRAY, target); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| *outTargetTexture = target; | |
| } | |
| void createEGLImageTargetTextureExternal(EGLImageKHR image, GLuint *outTargetTexture) | |
| { | |
| // Create a target texture from the image | |
| GLuint target; | |
| glGenTextures(1, &target); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| *outTargetTexture = target; | |
| } | |
| struct AHBPlaneData | |
| { | |
| const GLubyte *data; | |
| size_t bytesPerPixel; | |
| }; | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| void writeAHBData(AHardwareBuffer *aHardwareBuffer, | |
| size_t width, | |
| size_t height, | |
| size_t depth, | |
| bool isYUV, | |
| const std::vector<AHBPlaneData> &data) | |
| { | |
| # if defined(ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT) | |
| AHardwareBuffer_Planes planeInfo; | |
| int res = AHardwareBuffer_lockPlanes( | |
| aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, nullptr, &planeInfo); | |
| EXPECT_EQ(res, 0); | |
| EXPECT_EQ(data.size(), planeInfo.planeCount); | |
| for (size_t planeIdx = 0; planeIdx < data.size(); planeIdx++) | |
| { | |
| const AHBPlaneData &planeData = data[planeIdx]; | |
| const AHardwareBuffer_Plane &plane = planeInfo.planes[planeIdx]; | |
| size_t planeHeight = (isYUV && planeIdx > 0) ? (height / 2) : height; | |
| size_t planeWidth = (isYUV && planeIdx > 0) ? (width / 2) : width; | |
| for (size_t y = 0; y < planeHeight; y++) | |
| { | |
| for (size_t x = 0; x < planeWidth; x++) | |
| { | |
| const void *src = reinterpret_cast<const uint8_t *>(planeData.data) + | |
| (((y * planeWidth) + x) * planeData.bytesPerPixel); | |
| void *dst = reinterpret_cast<uint8_t *>(plane.data) + (y * plane.rowStride) + | |
| (x * plane.pixelStride); | |
| memcpy(dst, src, planeData.bytesPerPixel); | |
| } | |
| } | |
| } | |
| res = AHardwareBuffer_unlock(aHardwareBuffer, nullptr); | |
| EXPECT_EQ(res, 0); | |
| # else | |
| EXPECT_EQ(1u, data.size()); | |
| void *mappedMemory = nullptr; | |
| int res = AHardwareBuffer_lock(aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, | |
| nullptr, &mappedMemory); | |
| EXPECT_EQ(res, 0); | |
| // Need to grab the stride the implementation might have enforced | |
| AHardwareBuffer_Desc aHardwareBufferDescription = {}; | |
| AHardwareBuffer_describe(aHardwareBuffer, &aHardwareBufferDescription); | |
| const uint32_t stride = aHardwareBufferDescription.stride; | |
| uint32_t rowSize = stride * height; | |
| for (uint32_t i = 0; i < height; i++) | |
| { | |
| uint32_t dstPtrOffset = stride * i * (uint32_t)data[0].bytesPerPixel; | |
| uint32_t srcPtrOffset = width * i * (uint32_t)data[0].bytesPerPixel; | |
| void *dst = reinterpret_cast<uint8_t *>(mappedMemory) + dstPtrOffset; | |
| memcpy(dst, data[0].data + srcPtrOffset, rowSize); | |
| } | |
| res = AHardwareBuffer_unlock(aHardwareBuffer, nullptr); | |
| EXPECT_EQ(res, 0); | |
| # endif | |
| } | |
| #endif | |
| AHardwareBuffer *createAndroidHardwareBuffer(size_t width, | |
| size_t height, | |
| size_t depth, | |
| int androidFormat, | |
| const std::vector<AHBPlaneData> &data) | |
| { | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| // The height and width are number of pixels of size format | |
| AHardwareBuffer_Desc aHardwareBufferDescription = {}; | |
| aHardwareBufferDescription.width = width; | |
| aHardwareBufferDescription.height = height; | |
| aHardwareBufferDescription.layers = depth; | |
| aHardwareBufferDescription.format = androidFormat; | |
| aHardwareBufferDescription.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | | |
| AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | | |
| AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; | |
| aHardwareBufferDescription.stride = 0; | |
| aHardwareBufferDescription.rfu0 = 0; | |
| aHardwareBufferDescription.rfu1 = 0; | |
| // Allocate memory from Android Hardware Buffer | |
| AHardwareBuffer *aHardwareBuffer = nullptr; | |
| EXPECT_EQ(0, AHardwareBuffer_allocate(&aHardwareBufferDescription, &aHardwareBuffer)); | |
| if (!data.empty()) | |
| { | |
| writeAHBData(aHardwareBuffer, width, height, depth, | |
| androidFormat == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, data); | |
| } | |
| return aHardwareBuffer; | |
| #else | |
| return nullptr; | |
| #endif // ANGLE_PLATFORM_ANDROID | |
| } | |
| void destroyAndroidHardwareBuffer(AHardwareBuffer *aHardwarebuffer) | |
| { | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| AHardwareBuffer_release(aHardwarebuffer); | |
| #endif | |
| } | |
| void createEGLImageAndroidHardwareBufferSource(size_t width, | |
| size_t height, | |
| size_t depth, | |
| int androidPixelFormat, | |
| const EGLint *attribs, | |
| const std::vector<AHBPlaneData> &data, | |
| AHardwareBuffer **outSourceAHB, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Set Android Memory | |
| AHardwareBuffer *aHardwareBuffer = | |
| createAndroidHardwareBuffer(width, height, depth, androidPixelFormat, data); | |
| EXPECT_NE(aHardwareBuffer, nullptr); | |
| // Create an image from the source AHB | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = eglCreateImageKHR( | |
| window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, | |
| angle::android::AHardwareBufferToClientBuffer(aHardwareBuffer), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceAHB = aHardwareBuffer; | |
| *outSourceImage = image; | |
| } | |
| void createEGLImageANWBClientBufferSource(size_t width, | |
| size_t height, | |
| size_t depth, | |
| const EGLint *attribsANWB, | |
| const EGLint *attribsImage, | |
| const std::vector<AHBPlaneData> &data, | |
| EGLImageKHR *outSourceImage) | |
| { | |
| // Set Android Memory | |
| EGLClientBuffer eglClientBuffer = eglCreateNativeClientBufferANDROID(attribsANWB); | |
| EXPECT_NE(eglClientBuffer, nullptr); | |
| // allocate AHB memory | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| AHardwareBuffer *pAHardwareBuffer = angle::android::ANativeWindowBufferToAHardwareBuffer( | |
| angle::android::ClientBufferToANativeWindowBuffer(eglClientBuffer)); | |
| if (!data.empty()) | |
| { | |
| writeAHBData(pAHardwareBuffer, width, height, depth, false, data); | |
| } | |
| #endif // ANGLE_AHARDWARE_BUFFER_SUPPORT | |
| // Create an image from the source eglClientBuffer | |
| EGLWindow *window = getEGLWindow(); | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, | |
| eglClientBuffer, attribsImage); | |
| ASSERT_EGL_SUCCESS(); | |
| *outSourceImage = image; | |
| } | |
| void createEGLImageTargetRenderbuffer(EGLImageKHR image, GLuint *outTargetRenderbuffer) | |
| { | |
| // Create a target texture from the image | |
| GLuint target; | |
| glGenRenderbuffers(1, &target); | |
| glBindRenderbuffer(GL_RENDERBUFFER, target); | |
| glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image); | |
| ASSERT_GL_NO_ERROR(); | |
| *outTargetRenderbuffer = target; | |
| } | |
| void ValidationGLEGLImage_helper(const EGLint *attribs); | |
| void SourceAHBTarget2D_helper(const EGLint *attribs); | |
| void SourceAHBTarget2DArray_helper(const EGLint *attribs); | |
| void SourceAHBTargetExternal_helper(const EGLint *attribs); | |
| void SourceAHBTargetExternalESSL3_helper(const EGLint *attribs); | |
| void SourceNativeClientBufferTargetExternal_helper(const EGLint *attribs); | |
| void SourceNativeClientBufferTargetRenderbuffer_helper(const EGLint *attribs); | |
| void Source2DTarget2D_helper(const EGLint *attribs); | |
| void Source2DTarget2DArray_helper(const EGLint *attribs); | |
| void Source2DTargetRenderbuffer_helper(const EGLint *attribs); | |
| void Source2DTargetExternal_helper(const EGLint *attribs); | |
| void Source2DTargetExternalESSL3_helper(const EGLint *attribs); | |
| void SourceCubeTarget2D_helper(const EGLint *attribs); | |
| void SourceCubeTargetRenderbuffer_helper(const EGLint *attribs); | |
| void SourceCubeTargetExternal_helper(const EGLint *attribs); | |
| void SourceCubeTargetExternalESSL3_helper(const EGLint *attribs); | |
| void Source3DTargetTexture_helper(const bool withColorspace); | |
| void Source3DTargetRenderbuffer_helper(const bool withColorspace); | |
| void Source3DTargetExternal_helper(const bool withColorspace); | |
| void Source3DTargetExternalESSL3_helper(const bool withColorspace); | |
| void SourceRenderbufferTargetTexture_helper(const EGLint *attribs); | |
| void SourceRenderbufferTargetTextureExternal_helper(const EGLint *attribs); | |
| void SourceRenderbufferTargetRenderbuffer_helper(const EGLint *attribs); | |
| void SourceRenderbufferTargetTextureExternalESSL3_helper(const EGLint *attribs); | |
| void verifyResultsTexture(GLuint texture, | |
| GLubyte referenceColor[4], | |
| GLenum textureTarget, | |
| GLuint program, | |
| GLuint textureUniform) | |
| { | |
| // Draw a quad with the target texture | |
| glUseProgram(program); | |
| glBindTexture(textureTarget, texture); | |
| glUniform1i(textureUniform, 0); | |
| drawQuad(program, "position", 0.5f); | |
| // Expect that the rendered quad's color is the same as the reference color with a tolerance | |
| // of 1 | |
| EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2], | |
| referenceColor[3], 1); | |
| } | |
| void verifyResults2D(GLuint texture, GLubyte data[4]) | |
| { | |
| verifyResultsTexture(texture, data, GL_TEXTURE_2D, mTextureProgram, | |
| mTextureUniformLocation); | |
| } | |
| void verifyResults2DArray(GLuint texture, GLubyte data[4]) | |
| { | |
| verifyResultsTexture(texture, data, GL_TEXTURE_2D_ARRAY, m2DArrayTextureProgram, | |
| m2DArrayTextureUniformLocation); | |
| } | |
| void verifyResultsExternal(GLuint texture, GLubyte data[4]) | |
| { | |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureExternalProgram, | |
| mTextureExternalUniformLocation); | |
| } | |
| void verifyResultsExternalESSL3(GLuint texture, GLubyte data[4]) | |
| { | |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureExternalESSL3Program, | |
| mTextureExternalESSL3UniformLocation); | |
| } | |
| void verifyResultsExternalYUV(GLuint texture, GLubyte data[4]) | |
| { | |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureYUVProgram, | |
| mTextureYUVUniformLocation); | |
| } | |
| void verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte referenceColor[4]) | |
| { | |
| // Bind the renderbuffer to a framebuffer | |
| GLuint framebuffer; | |
| glGenFramebuffers(1, &framebuffer); | |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | |
| renderbuffer); | |
| // Expect that the rendered quad's color is the same as the reference color with a tolerance | |
| // of 1 | |
| EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2], | |
| referenceColor[3], 1); | |
| glDeleteFramebuffers(1, &framebuffer); | |
| } | |
| void verifyResultAHB(AHardwareBuffer *source, const std::vector<AHBPlaneData> &data) | |
| { | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| AHardwareBuffer_Desc aHardwareBufferDescription; | |
| AHardwareBuffer_describe(source, &aHardwareBufferDescription); | |
| bool isYUV = (aHardwareBufferDescription.format == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420); | |
| const uint32_t width = aHardwareBufferDescription.width; | |
| const uint32_t height = aHardwareBufferDescription.height; | |
| # if defined(ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT) | |
| AHardwareBuffer_Planes planeInfo; | |
| ASSERT_EQ(0, AHardwareBuffer_lockPlanes(source, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, | |
| nullptr, &planeInfo)); | |
| ASSERT_EQ(data.size(), planeInfo.planeCount); | |
| for (size_t planeIdx = 0; planeIdx < data.size(); planeIdx++) | |
| { | |
| const AHBPlaneData &planeData = data[planeIdx]; | |
| const AHardwareBuffer_Plane &plane = planeInfo.planes[planeIdx]; | |
| size_t planeHeight = (isYUV && planeIdx > 0) ? (height / 2) : height; | |
| size_t planeWidth = (isYUV && planeIdx > 0) ? (width / 2) : width; | |
| for (size_t y = 0; y < planeHeight; y++) | |
| { | |
| for (size_t x = 0; x < planeWidth; x++) | |
| { | |
| const uint8_t *referenceData = | |
| reinterpret_cast<const uint8_t *>(planeData.data) + | |
| (((y * planeWidth) + x) * planeData.bytesPerPixel); | |
| std::vector<uint8_t> reference(referenceData, | |
| referenceData + planeData.bytesPerPixel); | |
| const uint8_t *ahbData = reinterpret_cast<uint8_t *>(plane.data) + | |
| (y * plane.rowStride) + (x * plane.pixelStride); | |
| std::vector<uint8_t> ahb(ahbData, ahbData + planeData.bytesPerPixel); | |
| EXPECT_EQ(reference, ahb) | |
| << "at (" << x << ", " << y << ") on plane " << planeIdx; | |
| } | |
| } | |
| } | |
| ASSERT_EQ(0, AHardwareBuffer_unlock(source, nullptr)); | |
| # else | |
| ASSERT_EQ(1u, data.size()); | |
| ASSERT_FALSE(isYUV); | |
| const uint32_t rowStride = aHardwareBufferDescription.stride; | |
| void *mappedMemory = nullptr; | |
| ASSERT_EQ(0, AHardwareBuffer_lock(source, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, | |
| nullptr, &mappedMemory)); | |
| for (size_t y = 0; y < height; y++) | |
| { | |
| for (size_t x = 0; x < width; x++) | |
| { | |
| const uint8_t *referenceData = reinterpret_cast<const uint8_t *>(mappedMemory) + | |
| (((y * width) + x) * data[0].bytesPerPixel); | |
| std::vector<uint8_t> reference(referenceData, | |
| referenceData + data[0].bytesPerPixel); | |
| const uint8_t *ahbData = reinterpret_cast<uint8_t *>(mappedMemory) + | |
| (y * rowStride) + (x * data[0].bytesPerPixel); | |
| std::vector<uint8_t> ahb(ahbData, ahbData + data[0].bytesPerPixel); | |
| EXPECT_EQ(reference, ahb) << "at (" << x << ", " << y << ")"; | |
| } | |
| } | |
| ASSERT_EQ(0, AHardwareBuffer_unlock(source, nullptr)); | |
| # endif | |
| #endif | |
| } | |
| template <typename destType, typename sourcetype> | |
| destType reinterpretHelper(sourcetype source) | |
| { | |
| static_assert(sizeof(destType) == sizeof(size_t), | |
| "destType should be the same size as a size_t"); | |
| size_t sourceSizeT = static_cast<size_t>(source); | |
| return reinterpret_cast<destType>(sourceSizeT); | |
| } | |
| bool hasImageGLColorspaceExt() const | |
| { | |
| // Possible GLES driver bug on Pixel2 devices: http://anglebug.com/5321 | |
| if (IsPixel2() && IsOpenGLES()) | |
| { | |
| return false; | |
| } | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kImageGLColorspaceExt); | |
| } | |
| bool hasAndroidImageNativeBufferExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), | |
| kEGLAndroidImageNativeBufferExt); | |
| } | |
| bool hasAndroidHardwareBufferSupport() const | |
| { | |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) | |
| return true; | |
| #else | |
| return false; | |
| #endif | |
| } | |
| bool hasEglImageArrayExt() const { return IsGLExtensionEnabled(kEGLImageArrayExt); } | |
| bool hasOESExt() const { return IsGLExtensionEnabled(kOESExt); } | |
| bool hasExternalExt() const { return IsGLExtensionEnabled(kExternalExt); } | |
| bool hasExternalESSL3Ext() const { return IsGLExtensionEnabled(kExternalESSL3Ext); } | |
| bool hasYUVTargetExt() const { return IsGLExtensionEnabled(kYUVTargetExt); } | |
| bool hasBaseExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kBaseExt); | |
| } | |
| bool has2DTextureExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), k2DTextureExt); | |
| } | |
| bool has3DTextureExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), k3DTextureExt); | |
| } | |
| bool hasPixmapExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kPixmapExt); | |
| } | |
| bool hasRenderbufferExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kRenderbufferExt); | |
| } | |
| bool hasCubemapExt() const | |
| { | |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kCubemapExt); | |
| } | |
| EGLint *get3DAttributes(const bool withColorspace = false, EGLint layer = 0) | |
| { | |
| if (!withColorspace) | |
| { | |
| default3DAttribs[1] = static_cast<EGLint>(layer); | |
| return default3DAttribs; | |
| } | |
| colorspace3DAttribs[1] = static_cast<EGLint>(layer); | |
| return colorspace3DAttribs; | |
| } | |
| EGLint default3DAttribs[5] = { | |
| EGL_GL_TEXTURE_ZOFFSET_KHR, static_cast<EGLint>(0), EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_NONE, | |
| }; | |
| EGLint colorspace3DAttribs[7] = { | |
| EGL_GL_TEXTURE_ZOFFSET_KHR, | |
| static_cast<EGLint>(0), | |
| EGL_IMAGE_PRESERVED, | |
| EGL_TRUE, | |
| EGL_GL_COLORSPACE, | |
| EGL_GL_COLORSPACE_SRGB_KHR, | |
| EGL_NONE, | |
| }; | |
| GLuint mTextureProgram; | |
| GLuint m2DArrayTextureProgram; | |
| GLint mTextureUniformLocation; | |
| GLuint m2DArrayTextureUniformLocation; | |
| GLuint mTextureExternalProgram = 0; | |
| GLint mTextureExternalUniformLocation = -1; | |
| GLuint mTextureExternalESSL3Program = 0; | |
| GLint mTextureExternalESSL3UniformLocation = -1; | |
| GLuint mTextureYUVProgram = 0; | |
| GLint mTextureYUVUniformLocation = -1; | |
| GLuint mRenderYUVProgram = 0; | |
| GLint mRenderYUVUniformLocation = -1; | |
| }; | |
| class ImageTestES3 : public ImageTest | |
| {}; | |
| // Tests that the extension is exposed on the platforms we think it should be. Please modify this as | |
| // you change extension availability. | |
| TEST_P(ImageTest, ANGLEExtensionAvailability) | |
| { | |
| // EGL support is based on driver extension availability. | |
| ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsOzone()); | |
| if (IsD3D11() || IsD3D9()) | |
| { | |
| EXPECT_TRUE(hasOESExt()); | |
| EXPECT_TRUE(hasExternalExt()); | |
| EXPECT_TRUE(hasBaseExt()); | |
| EXPECT_TRUE(has2DTextureExt()); | |
| EXPECT_TRUE(hasRenderbufferExt()); | |
| if (IsD3D11()) | |
| { | |
| EXPECT_TRUE(hasCubemapExt()); | |
| if (getClientMajorVersion() >= 3) | |
| { | |
| EXPECT_TRUE(hasExternalESSL3Ext()); | |
| } | |
| else | |
| { | |
| EXPECT_FALSE(hasExternalESSL3Ext()); | |
| } | |
| } | |
| else | |
| { | |
| EXPECT_FALSE(hasCubemapExt()); | |
| EXPECT_FALSE(hasExternalESSL3Ext()); | |
| } | |
| } | |
| else if (IsVulkan()) | |
| { | |
| EXPECT_TRUE(hasOESExt()); | |
| EXPECT_TRUE(hasExternalExt()); | |
| EXPECT_TRUE(hasBaseExt()); | |
| EXPECT_TRUE(has2DTextureExt()); | |
| EXPECT_TRUE(hasCubemapExt()); | |
| EXPECT_TRUE(hasRenderbufferExt()); | |
| if (getClientMajorVersion() >= 3) | |
| { | |
| EXPECT_TRUE(hasExternalESSL3Ext()); | |
| } | |
| else | |
| { | |
| EXPECT_FALSE(hasExternalESSL3Ext()); | |
| } | |
| } | |
| else if (IsMetal()) | |
| { | |
| // NOTE(hqle): Metal currently doesn't implement any image extensions besides | |
| // EGL_ANGLE_metal_texture_client_buffer | |
| EXPECT_TRUE(hasOESExt()); | |
| EXPECT_TRUE(hasBaseExt()); | |
| EXPECT_FALSE(hasExternalExt()); | |
| EXPECT_FALSE(hasExternalESSL3Ext()); | |
| EXPECT_FALSE(has2DTextureExt()); | |
| EXPECT_FALSE(has3DTextureExt()); | |
| EXPECT_FALSE(hasRenderbufferExt()); | |
| } | |
| else | |
| { | |
| EXPECT_FALSE(hasOESExt()); | |
| EXPECT_FALSE(hasExternalExt()); | |
| EXPECT_FALSE(hasExternalESSL3Ext()); | |
| EXPECT_FALSE(hasBaseExt()); | |
| EXPECT_FALSE(has2DTextureExt()); | |
| EXPECT_FALSE(has3DTextureExt()); | |
| EXPECT_FALSE(hasRenderbufferExt()); | |
| } | |
| // These extensions are not yet available on any platform. | |
| EXPECT_FALSE(hasPixmapExt()); | |
| EXPECT_FALSE(has3DTextureExt()); | |
| } | |
| // Check validation from the EGL_KHR_image_base extension | |
| TEST_P(ImageTest, ValidationImageBase) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLuint glTexture2D; | |
| glGenTextures(1, &glTexture2D); | |
| glBindTexture(GL_TEXTURE_2D, glTexture2D); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EGLDisplay display = window->getDisplay(); | |
| EGLContext context = window->getContext(); | |
| EGLConfig config = window->getConfig(); | |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; | |
| EGLClientBuffer texture2D = reinterpretHelper<EGLClientBuffer>(glTexture2D); | |
| // Test validation of eglCreateImageKHR | |
| // If <dpy> is not the handle of a valid EGLDisplay object, the error EGL_BAD_DISPLAY is | |
| // generated. | |
| image = eglCreateImageKHR(reinterpretHelper<EGLDisplay>(0xBAADF00D), context, | |
| EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_DISPLAY); | |
| // If <ctx> is neither the handle of a valid EGLContext object on <dpy> nor EGL_NO_CONTEXT, the | |
| // error EGL_BAD_CONTEXT is generated. | |
| image = eglCreateImageKHR(display, reinterpretHelper<EGLContext>(0xBAADF00D), | |
| EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_CONTEXT); | |
| // Test EGL_NO_CONTEXT with a 2D texture target which does require a context. | |
| image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_CONTEXT); | |
| // If an attribute specified in <attrib_list> is not one of the attributes listed in Table bbb, | |
| // the error EGL_BAD_PARAMETER is generated. | |
| EGLint badAttributes[] = { | |
| static_cast<EGLint>(0xDEADBEEF), | |
| 0, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, badAttributes); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // If the resource specified by <dpy>, <ctx>, <target>, <buffer> and <attrib_list> has an off - | |
| // screen buffer bound to it(e.g., by a | |
| // previous call to eglBindTexImage), the error EGL_BAD_ACCESS is generated. | |
| EGLint surfaceType = 0; | |
| eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfaceType); | |
| EGLint bindToTextureRGBA = 0; | |
| eglGetConfigAttrib(display, config, EGL_BIND_TO_TEXTURE_RGBA, &bindToTextureRGBA); | |
| if ((surfaceType & EGL_PBUFFER_BIT) != 0 && bindToTextureRGBA == EGL_TRUE) | |
| { | |
| EGLint pbufferAttributes[] = { | |
| EGL_WIDTH, 1, | |
| EGL_HEIGHT, 1, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferSurface(display, config, pbufferAttributes); | |
| ASSERT_NE(pbuffer, EGL_NO_SURFACE); | |
| EXPECT_EGL_SUCCESS(); | |
| eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER); | |
| EXPECT_EGL_SUCCESS(); | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_ACCESS); | |
| eglReleaseTexImage(display, pbuffer, EGL_BACK_BUFFER); | |
| eglDestroySurface(display, pbuffer); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_EGL_SUCCESS(); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // If the resource specified by <dpy>, <ctx>, <target>, <buffer> and | |
| // <attrib_list> is itself an EGLImage sibling, the error EGL_BAD_ACCESS is generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, texture2D, nullptr); | |
| EXPECT_NE(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_SUCCESS(); | |
| /* TODO(geofflang): Enable this validation when it passes. | |
| EGLImageKHR image2 = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpret_cast<EGLClientBuffer>(texture2D), nullptr); | |
| EXPECT_EQ(image2, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_ACCESS); | |
| */ | |
| // Test validation of eglDestroyImageKHR | |
| // Note: image is now a valid EGL image | |
| EGLBoolean result = EGL_FALSE; | |
| // If <dpy> is not the handle of a valid EGLDisplay object, the error EGL_BAD_DISPLAY is | |
| // generated. | |
| result = eglDestroyImageKHR(reinterpretHelper<EGLDisplay>(0xBAADF00D), image); | |
| EXPECT_EQ(result, static_cast<EGLBoolean>(EGL_FALSE)); | |
| EXPECT_EGL_ERROR(EGL_BAD_DISPLAY); | |
| // If <image> is not a valid EGLImageKHR object created with respect to <dpy>, the error | |
| // EGL_BAD_PARAMETER is generated. | |
| result = eglDestroyImageKHR(display, reinterpretHelper<EGLImageKHR>(0xBAADF00D)); | |
| EXPECT_EQ(result, static_cast<EGLBoolean>(EGL_FALSE)); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // Clean up and validate image is destroyed | |
| result = eglDestroyImageKHR(display, image); | |
| EXPECT_EQ(result, static_cast<EGLBoolean>(EGL_TRUE)); | |
| EXPECT_EGL_SUCCESS(); | |
| glDeleteTextures(1, &glTexture2D); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // Check validation from the EGL_KHR_gl_texture_2D_image, EGL_KHR_gl_texture_cubemap_image, | |
| // EGL_KHR_gl_texture_3D_image and EGL_KHR_gl_renderbuffer_image extensions | |
| TEST_P(ImageTest, ValidationGLImage) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); | |
| EGLDisplay display = window->getDisplay(); | |
| EGLContext context = window->getContext(); | |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; | |
| if (has2DTextureExt()) | |
| { | |
| // If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR or | |
| // EGL_GL_TEXTURE_3D_KHR and <buffer> is not the name of a texture object of type <target>, | |
| // the error EGL_BAD_PARAMETER is generated. | |
| GLuint textureCube; | |
| glGenTextures(1, &textureCube); | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, textureCube); | |
| for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; | |
| face++) | |
| { | |
| glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| } | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(textureCube), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_2D_KHR, | |
| // EGL_GL_TEXTURE_CUBE_MAP_*_KHR or EGL_GL_TEXTURE_3D_KHR, <buffer> is the name of an | |
| // incomplete GL texture object, and any mipmap levels other than mipmap level 0 are | |
| // specified, the error EGL_BAD_PARAMETER is generated. | |
| GLuint incompleteTexture; | |
| glGenTextures(1, &incompleteTexture); | |
| glBindTexture(GL_TEXTURE_2D, incompleteTexture); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EGLint level0Attribute[] = { | |
| EGL_GL_TEXTURE_LEVEL_KHR, | |
| 0, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(incompleteTexture), | |
| level0Attribute); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_2D_KHR or | |
| // EGL_GL_TEXTURE_3D_KHR, <buffer> is not the name of a complete GL texture object, and | |
| // mipmap level 0 is not specified, the error EGL_BAD_PARAMETER is generated. | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(incompleteTexture), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR, | |
| // EGL_GL_RENDERBUFFER_KHR or EGL_GL_TEXTURE_3D_KHR and <buffer> refers to the default GL | |
| // texture object(0) for the corresponding GL target, the error EGL_BAD_PARAMETER is | |
| // generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, 0, nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // If <target> is EGL_GL_TEXTURE_2D_KHR, EGL_GL_TEXTURE_CUBE_MAP_*_KHR, or | |
| // EGL_GL_TEXTURE_3D_KHR, and the value specified in <attr_list> for | |
| // EGL_GL_TEXTURE_LEVEL_KHR is not a valid mipmap level for the specified GL texture object | |
| // <buffer>, the error EGL_BAD_MATCH is generated. | |
| EGLint level2Attribute[] = { | |
| EGL_GL_TEXTURE_LEVEL_KHR, | |
| 2, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(incompleteTexture), | |
| level2Attribute); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| else | |
| { | |
| GLuint texture2D; | |
| glGenTextures(1, &texture2D); | |
| glBindTexture(GL_TEXTURE_2D, texture2D); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| // From EGL_KHR_image_base: | |
| // If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is | |
| // generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(texture2D), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| if (hasCubemapExt()) | |
| { | |
| // If EGL_GL_TEXTURE_LEVEL_KHR is 0, <target> is EGL_GL_TEXTURE_CUBE_MAP_*_KHR, <buffer> is | |
| // not the name of a complete GL texture object, and one or more faces do not have mipmap | |
| // level 0 specified, the error EGL_BAD_PARAMETER is generated. | |
| GLuint incompleteTextureCube; | |
| glGenTextures(1, &incompleteTextureCube); | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, incompleteTextureCube); | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| EGLint level0Attribute[] = { | |
| EGL_GL_TEXTURE_LEVEL_KHR, | |
| 0, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, | |
| reinterpretHelper<EGLClientBuffer>(incompleteTextureCube), | |
| level0Attribute); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| else | |
| { | |
| GLuint textureCube; | |
| glGenTextures(1, &textureCube); | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, textureCube); | |
| for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; | |
| face++) | |
| { | |
| glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| } | |
| // From EGL_KHR_image_base: | |
| // If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is | |
| // generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, | |
| reinterpretHelper<EGLClientBuffer>(textureCube), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| if (has3DTextureExt() && getClientMajorVersion() >= 3) | |
| { | |
| // If <target> is EGL_GL_TEXTURE_3D_KHR, and the value specified in <attr_list> for | |
| // EGL_GL_TEXTURE_ZOFFSET_KHR exceeds the depth of the specified mipmap level - of - detail | |
| // in <buffer>, the error EGL_BAD_PARAMETER is generated. | |
| GLuint texture3D; | |
| glGenTextures(1, &texture3D); | |
| glBindTexture(GL_TEXTURE_3D, texture3D); | |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EGLint zOffset3Parameter[] = { | |
| EGL_GL_TEXTURE_ZOFFSET_KHR, | |
| 3, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(texture3D), zOffset3Parameter); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| EGLint zOffsetNegative1Parameter[] = { | |
| EGL_GL_TEXTURE_ZOFFSET_KHR, | |
| -1, | |
| EGL_NONE, | |
| }; | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(texture3D), | |
| zOffsetNegative1Parameter); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| else | |
| { | |
| if (has2DTextureExt()) | |
| { | |
| GLuint texture2D; | |
| glGenTextures(1, &texture2D); | |
| glBindTexture(GL_TEXTURE_2D, texture2D); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| // Verify EGL_GL_TEXTURE_ZOFFSET_KHR is not a valid parameter | |
| EGLint zOffset0Parameter[] = { | |
| EGL_GL_TEXTURE_ZOFFSET_KHR, | |
| 0, | |
| EGL_NONE, | |
| }; | |
| image = | |
| eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(texture2D), zOffset0Parameter); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| if (getClientMajorVersion() >= 3) | |
| { | |
| GLuint texture3D; | |
| glGenTextures(1, &texture3D); | |
| glBindTexture(GL_TEXTURE_3D, texture3D); | |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| // From EGL_KHR_image_base: | |
| // If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is | |
| // generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_TEXTURE_3D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(texture3D), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| } | |
| if (hasRenderbufferExt()) | |
| { | |
| // If <target> is EGL_GL_RENDERBUFFER_KHR and <buffer> is not the name of a renderbuffer | |
| // object, or if <buffer> is the name of a multisampled renderbuffer object, the error | |
| // EGL_BAD_PARAMETER is generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR, | |
| reinterpret_cast<EGLClientBuffer>(0), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| if (IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample")) | |
| { | |
| GLuint renderbuffer; | |
| glGenRenderbuffers(1, &renderbuffer); | |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | |
| glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 1, GL_RGBA8, 1, 1); | |
| EXPECT_GL_NO_ERROR(); | |
| image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR, | |
| reinterpret_cast<EGLClientBuffer>(0), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| } | |
| else | |
| { | |
| GLuint renderbuffer; | |
| glGenRenderbuffers(1, &renderbuffer); | |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 1, 1); | |
| // From EGL_KHR_image_base: | |
| // If <target> is not one of the values in Table aaa, the error EGL_BAD_PARAMETER is | |
| // generated. | |
| image = eglCreateImageKHR(display, context, EGL_GL_RENDERBUFFER_KHR, | |
| reinterpretHelper<EGLClientBuffer>(renderbuffer), nullptr); | |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); | |
| EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| } | |
| } | |
| // Check validation from the GL_OES_EGL_image extension | |
| TEST_P(ImageTest, ValidationGLEGLImage) | |
| { | |
| ValidationGLEGLImage_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, ValidationGLEGLImage_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| ValidationGLEGLImage_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::ValidationGLEGLImage_helper(const EGLint *attribs) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source, | |
| &image); | |
| // If <target> is not TEXTURE_2D, the error INVALID_ENUM is generated. | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| // If <image> does not refer to a valid eglImageOES object, the error INVALID_VALUE is | |
| // generated. | |
| GLuint texture; | |
| glGenTextures(1, &texture); | |
| glBindTexture(GL_TEXTURE_2D, texture); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, reinterpretHelper<GLeglImageOES>(0xBAADF00D)); | |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); | |
| // <target> must be RENDERBUFFER_OES, and <image> must be the handle of a valid EGLImage | |
| // resource, cast into the type | |
| // eglImageOES. | |
| glEGLImageTargetRenderbufferStorageOES(GL_TEXTURE_2D, image); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| // If the GL is unable to create a renderbuffer using the specified eglImageOES, the error | |
| // INVALID_OPERATION is generated.If <image> | |
| // does not refer to a valid eglImageOES object, the error INVALID_VALUE is generated. | |
| GLuint renderbuffer; | |
| glGenRenderbuffers(1, &renderbuffer); | |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | |
| glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, | |
| reinterpretHelper<GLeglImageOES>(0xBAADF00D)); | |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), image); | |
| glDeleteTextures(1, &texture); | |
| glDeleteRenderbuffers(1, &renderbuffer); | |
| } | |
| // Check validation from the GL_OES_EGL_image_external extension | |
| TEST_P(ImageTest, ValidationGLEGLImageExternal) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasExternalExt()); | |
| GLuint texture; | |
| glGenTextures(1, &texture); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); | |
| // In the initial state of a TEXTURE_EXTERNAL_OES texture object, the value assigned to | |
| // TEXTURE_MIN_FILTER and TEXTURE_MAG_FILTER is LINEAR, and the s and t wrap modes are both set | |
| // to CLAMP_TO_EDGE | |
| auto getTexParam = [](GLenum target, GLenum pname) { | |
| GLint value = 0; | |
| glGetTexParameteriv(target, pname, &value); | |
| EXPECT_GL_NO_ERROR(); | |
| return value; | |
| }; | |
| EXPECT_GLENUM_EQ(GL_LINEAR, getTexParam(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER)); | |
| EXPECT_GLENUM_EQ(GL_LINEAR, getTexParam(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER)); | |
| EXPECT_GLENUM_EQ(GL_CLAMP_TO_EDGE, getTexParam(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S)); | |
| EXPECT_GLENUM_EQ(GL_CLAMP_TO_EDGE, getTexParam(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T)); | |
| // "When <target> is TEXTURE_EXTERNAL_OES only NEAREST and LINEAR are accepted as | |
| // TEXTURE_MIN_FILTER, only CLAMP_TO_EDGE is accepted as TEXTURE_WRAP_S and TEXTURE_WRAP_T, and | |
| // only FALSE is accepted as GENERATE_MIPMAP. Attempting to set other values for | |
| // TEXTURE_MIN_FILTER, TEXTURE_WRAP_S, TEXTURE_WRAP_T, or GENERATE_MIPMAP will result in an | |
| // INVALID_ENUM error. | |
| GLenum validMinFilters[]{ | |
| GL_NEAREST, | |
| GL_LINEAR, | |
| }; | |
| for (auto minFilter : validMinFilters) | |
| { | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, minFilter); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| GLenum invalidMinFilters[]{ | |
| GL_NEAREST_MIPMAP_LINEAR, | |
| GL_NEAREST_MIPMAP_NEAREST, | |
| GL_LINEAR_MIPMAP_LINEAR, | |
| GL_LINEAR_MIPMAP_NEAREST, | |
| }; | |
| for (auto minFilter : invalidMinFilters) | |
| { | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, minFilter); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| } | |
| GLenum validWrapModes[]{ | |
| GL_CLAMP_TO_EDGE, | |
| }; | |
| for (auto wrapMode : validWrapModes) | |
| { | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, wrapMode); | |
| EXPECT_GL_NO_ERROR(); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, wrapMode); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| if (IsGLExtensionEnabled("GL_EXT_EGL_image_external_wrap_modes")) | |
| { | |
| GLenum validWrapModesEXT[]{GL_REPEAT, GL_MIRRORED_REPEAT}; | |
| for (auto wrapMode : validWrapModesEXT) | |
| { | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, wrapMode); | |
| EXPECT_GL_NO_ERROR(); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, wrapMode); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| } | |
| else | |
| { | |
| GLenum invalidWrapModes[]{ | |
| GL_REPEAT, | |
| GL_MIRRORED_REPEAT, | |
| }; | |
| for (auto wrapMode : invalidWrapModes) | |
| { | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, wrapMode); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, wrapMode); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| } | |
| } | |
| // When <target> is set to TEXTURE_EXTERNAL_OES, GenerateMipmap always fails and generates an | |
| // INVALID_ENUM error. | |
| glGenerateMipmap(GL_TEXTURE_EXTERNAL_OES); | |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); | |
| glDeleteTextures(1, &texture); | |
| } | |
| // Check validation from the GL_OES_EGL_image_external_essl3 extension | |
| TEST_P(ImageTest, ValidationGLEGLImageExternalESSL3) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasExternalESSL3Ext()); | |
| // Make sure this extension is not exposed without ES3. | |
| ASSERT_GE(getClientMajorVersion(), 3); | |
| GLuint texture; | |
| glGenTextures(1, &texture); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); | |
| // It is an INVALID_OPERATION error to set the TEXTURE_BASE_LEVEL to a value other than zero. | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BASE_LEVEL, 1); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BASE_LEVEL, 10); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BASE_LEVEL, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| glDeleteTextures(1, &texture); | |
| } | |
| TEST_P(ImageTest, Source2DTarget2D) | |
| { | |
| Source2DTarget2D_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, Source2DTarget2D_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source2DTarget2D_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::Source2DTarget2D_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, | |
| static_cast<void *>(&kLinearColor), &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Create target texture from EGL image and then trigger texture respecification. | |
| TEST_P(ImageTest, Source2DTarget2DTargetTextureRespecifyColorspace) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_format_sRGB_override")); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, | |
| static_cast<void *>(&kLinearColor), &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| // Respecify texture colorspace and verify results | |
| glBindTexture(GL_TEXTURE_2D, target); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, kSrgbColor); | |
| // Reset texture parameter and verify results again | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Create target texture from EGL image and then trigger texture respecification. | |
| TEST_P(ImageTest, Source2DTarget2DTargetTextureRespecifySize) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, | |
| static_cast<void *>(&kLinearColor), &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| // Respecify texture size and verify results | |
| std::array<GLubyte, 16> referenceColor; | |
| referenceColor.fill(127); | |
| glBindTexture(GL_TEXTURE_2D, target); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| referenceColor.data()); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that the target texture has the reference color values | |
| verifyResults2D(target, referenceColor.data()); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Create target texture from EGL image and then trigger texture respecification. | |
| TEST_P(ImageTestES3, Source2DTarget2DTargetTextureRespecifyLevel) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, | |
| static_cast<void *>(&kLinearColor), &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| // Respecify texture levels and verify results | |
| glBindTexture(GL_TEXTURE_2D, target); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that the target texture has the reference color values | |
| verifyResults2D(target, kLinearColor); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source 2D texture, target 2D array texture | |
| TEST_P(ImageTest, Source2DTarget2DArray) | |
| { | |
| Source2DTarget2DArray_helper(kDefaultAttribs); | |
| } | |
| // Testing source 2D texture with colorspace, target 2D array texture | |
| TEST_P(ImageTest, Source2DTarget2DArray_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source2DTarget2DArray_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::Source2DTarget2DArray_helper(const EGLint *attribs) | |
| { | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || | |
| !hasEglImageArrayExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source, | |
| &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2DArray(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2DArray(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2DArray(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source AHB EGL image, target 2D texture and delete when in use | |
| // If refcounted correctly, the test should pass without issues | |
| TEST_P(ImageTest, SourceAHBTarget2DEarlyDelete) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| GLubyte data[4] = {7, 51, 197, 231}; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {{data, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Delete the source AHB when in use | |
| destroyAndroidHardwareBuffer(source); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| // Verify that data in framebuffer matches that in the egl image | |
| verifyResults2D(target, data); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| } | |
| // Testing source AHB EGL image, target 2D texture | |
| TEST_P(ImageTest, SourceAHBTarget2D) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceAHBTarget2D_helper(kDefaultAttribs); | |
| } | |
| // Testing source AHB EGL image with colorspace, target 2D texture | |
| TEST_P(ImageTest, SourceAHBTarget2D_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceAHBTarget2D_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceAHBTarget2D_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| attribs, {{kLinearColor, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| } | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source AHB EGL images, target 2D external texture, cycling through YUV sources. | |
| TEST_P(ImageTest, SourceAHBTarget2DExternalCycleThroughYuvSourcesNoData) | |
| { | |
| // http://issuetracker.google.com/175021871 | |
| ANGLE_SKIP_TEST_IF(IsPixel2() || IsPixel2XL()); | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create YCbCr source and image but without initial data | |
| AHardwareBuffer *ycbcrSource; | |
| EGLImageKHR ycbcrImage; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, | |
| kDefaultAttribs, {}, &ycbcrSource, &ycbcrImage); | |
| EXPECT_NE(ycbcrSource, nullptr); | |
| EXPECT_NE(ycbcrImage, EGL_NO_IMAGE_KHR); | |
| // Create YCrCb source and image but without initial data | |
| AHardwareBuffer *ycrcbSource; | |
| EGLImageKHR ycrcbImage; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cr8Cb8_420_SP, | |
| kDefaultAttribs, {}, &ycrcbSource, &ycrcbImage); | |
| EXPECT_NE(ycrcbSource, nullptr); | |
| EXPECT_NE(ycrcbImage, EGL_NO_IMAGE_KHR); | |
| // Create YV12 source and image but without initial data | |
| AHardwareBuffer *yv12Source; | |
| EGLImageKHR yv12Image; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_YV12, kDefaultAttribs, | |
| {}, &yv12Source, &yv12Image); | |
| EXPECT_NE(yv12Source, nullptr); | |
| EXPECT_NE(yv12Image, EGL_NO_IMAGE_KHR); | |
| // Create a texture target to bind the egl image | |
| GLTexture target; | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| // Bind YCbCr image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, ycbcrImage); | |
| // Draw while sampling should result in no EGL/GL errors | |
| glUseProgram(mTextureExternalProgram); | |
| glUniform1i(mTextureExternalUniformLocation, 0); | |
| drawQuad(mTextureExternalProgram, "position", 0.5f); | |
| ASSERT_GL_NO_ERROR(); | |
| // Bind YCrCb image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, ycrcbImage); | |
| // Draw while sampling should result in no EGL/GL errors | |
| glUseProgram(mTextureExternalProgram); | |
| glUniform1i(mTextureExternalUniformLocation, 0); | |
| drawQuad(mTextureExternalProgram, "position", 0.5f); | |
| ASSERT_GL_NO_ERROR(); | |
| // Bind YV12 image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, yv12Image); | |
| // Draw while sampling should result in no EGL/GL errors | |
| glUseProgram(mTextureExternalProgram); | |
| glUniform1i(mTextureExternalUniformLocation, 0); | |
| drawQuad(mTextureExternalProgram, "position", 0.5f); | |
| ASSERT_GL_NO_ERROR(); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), ycbcrImage); | |
| destroyAndroidHardwareBuffer(ycbcrSource); | |
| eglDestroyImageKHR(window->getDisplay(), ycrcbImage); | |
| destroyAndroidHardwareBuffer(ycrcbSource); | |
| eglDestroyImageKHR(window->getDisplay(), yv12Image); | |
| destroyAndroidHardwareBuffer(yv12Source); | |
| } | |
| // Testing source AHB EGL images, target 2D external texture, cycling through RGB and YUV sources. | |
| TEST_P(ImageTest, SourceAHBTarget2DExternalCycleThroughRgbAndYuvSources) | |
| { | |
| // http://issuetracker.google.com/175021871 | |
| ANGLE_SKIP_TEST_IF(IsPixel2() || IsPixel2XL()); | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create RGB Image | |
| GLubyte rgbColor[4] = {0, 0, 255, 255}; | |
| AHardwareBuffer *rgbSource; | |
| EGLImageKHR rgbImage; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {{rgbColor, 4}}, &rgbSource, | |
| &rgbImage); | |
| // Create YUV Image | |
| // 3 planes of data | |
| GLubyte dataY[4] = {40, 40, 40, 40}; | |
| GLubyte dataCb[1] = { | |
| 240, | |
| }; | |
| GLubyte dataCr[1] = { | |
| 109, | |
| }; | |
| GLubyte expectedRgbColor[4] = {0, 0, 255, 255}; | |
| AHardwareBuffer *yuvSource; | |
| EGLImageKHR yuvImage; | |
| createEGLImageAndroidHardwareBufferSource( | |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAttribs, | |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &yuvSource, &yuvImage); | |
| // Create a texture target to bind the egl image | |
| GLTexture target; | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| // Bind YUV image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, yuvImage); | |
| // Expect render target to have the same color as expectedRgbColor | |
| verifyResultsExternal(target, expectedRgbColor); | |
| // Bind RGB image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, rgbImage); | |
| // Expect render target to have the same color as rgbColor | |
| verifyResultsExternal(target, rgbColor); | |
| // Bind YUV image | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, yuvImage); | |
| // Expect render target to have the same color as expectedRgbColor | |
| verifyResultsExternal(target, expectedRgbColor); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), yuvImage); | |
| destroyAndroidHardwareBuffer(yuvSource); | |
| eglDestroyImageKHR(window->getDisplay(), rgbImage); | |
| destroyAndroidHardwareBuffer(rgbSource); | |
| } | |
| // Testing source AHB EGL images, target 2D external textures, cycling through RGB and YUV targets. | |
| TEST_P(ImageTest, SourceAHBTarget2DExternalCycleThroughRgbAndYuvTargets) | |
| { | |
| // http://issuetracker.google.com/175021871 | |
| ANGLE_SKIP_TEST_IF(IsPixel2() || IsPixel2XL()); | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create RGBA Image | |
| GLubyte rgbaColor[4] = {0, 0, 255, 255}; | |
| AHardwareBuffer *rgbaSource; | |
| EGLImageKHR rgbaImage; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {{rgbaColor, 4}}, &rgbaSource, | |
| &rgbaImage); | |
| // Create YUV Image | |
| // 3 planes of data | |
| GLubyte dataY[4] = {40, 40, 40, 40}; | |
| GLubyte dataCb[1] = { | |
| 240, | |
| }; | |
| GLubyte dataCr[1] = { | |
| 109, | |
| }; | |
| GLubyte expectedRgbColor[4] = {0, 0, 255, 255}; | |
| AHardwareBuffer *yuvSource; | |
| EGLImageKHR yuvImage; | |
| createEGLImageAndroidHardwareBufferSource( | |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAttribs, | |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &yuvSource, &yuvImage); | |
| // Create texture target siblings to bind the egl images | |
| // Create YUV target and bind the image | |
| GLTexture yuvTarget; | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTarget); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, yuvImage); | |
| ASSERT_GL_NO_ERROR(); | |
| // Create RGBA target and bind the image | |
| GLTexture rgbaTarget; | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, rgbaTarget); | |
| // Disable mipmapping | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, rgbaImage); | |
| ASSERT_GL_NO_ERROR(); | |
| // Cycle through targets | |
| // YUV target | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTarget); | |
| // Expect render target to have the same color as expectedRgbColor | |
| verifyResultsExternal(yuvTarget, expectedRgbColor); | |
| // RGBA target | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, rgbaTarget); | |
| // Expect render target to have the same color as rgbColor | |
| verifyResultsExternal(rgbaTarget, rgbaColor); | |
| // YUV target | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTarget); | |
| // Expect render target to have the same color as expectedRgbColor | |
| verifyResultsExternal(yuvTarget, expectedRgbColor); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), yuvImage); | |
| destroyAndroidHardwareBuffer(yuvSource); | |
| eglDestroyImageKHR(window->getDisplay(), rgbaImage); | |
| destroyAndroidHardwareBuffer(rgbaSource); | |
| } | |
| // Testing source AHB EGL image, target 2D texture retaining initial data. | |
| TEST_P(ImageTest, SourceAHBTarget2DRetainInitialData) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| GLubyte data[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {{data, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Create a framebuffer, and blend into the texture. | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| // Blend into the framebuffer. | |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); | |
| glEnable(GL_BLEND); | |
| glBlendFunc(GL_ONE, GL_ONE); | |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f); | |
| ASSERT_GL_NO_ERROR(); | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| glDisable(GL_BLEND); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| // Verify that data in framebuffer matches that in the egl image | |
| GLubyte expect[4] = {255, 255, 0, 255}; | |
| verifyResults2D(target, expect); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source AHB EGL image, target 2D array texture | |
| TEST_P(ImageTest, SourceAHBTarget2DArray) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceAHBTarget2DArray_helper(kDefaultAttribs); | |
| } | |
| // Testing source AHB EGL image with colorspace, target 2D array texture | |
| TEST_P(ImageTest, SourceAHBTarget2DArray_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceAHBTarget2DArray_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceAHBTarget2DArray_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || | |
| !hasEglImageArrayExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| attribs, {{kLinearColor, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTexture2DArray(image, &target); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2DArray(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2DArray(target, kLinearColor); | |
| } | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source AHB EGL image, target external texture | |
| TEST_P(ImageTest, SourceAHBTargetExternal) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceAHBTargetExternal_helper(kDefaultAttribs); | |
| } | |
| // Testing source AHB EGL image with colorspace, target external texture | |
| TEST_P(ImageTest, SourceAHBTargetExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceAHBTargetExternal_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceAHBTargetExternal_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasExternalExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import | |
| ANGLE_SKIP_TEST_IF(IsOzone()); | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| attribs, {{kLinearColor, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, kLinearColor); | |
| } | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Testing source AHB EGL image, target external ESSL3 texture | |
| TEST_P(ImageTestES3, SourceAHBTargetExternalESSL3) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceAHBTargetExternalESSL3_helper(kDefaultAttribs); | |
| } | |
| // Test sampling from a YUV AHB with a regular external sampler and pre-initialized data | |
| TEST_P(ImageTest, SourceYUVAHBTargetExternalRGBSampleInitData) | |
| { | |
| #ifndef ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| std::cout << "Test skipped: !ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT." << std::endl; | |
| return; | |
| #else | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // http://issuetracker.google.com/175021871 | |
| ANGLE_SKIP_TEST_IF(IsPixel2() || IsPixel2XL()); | |
| // 3 planes of data | |
| GLubyte dataY[4] = {7, 51, 197, 231}; | |
| GLubyte dataCb[1] = { | |
| 128, | |
| }; | |
| GLubyte dataCr[1] = { | |
| 192, | |
| }; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource( | |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAttribs, | |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| GLubyte pixelColor[4] = {255, 159, 211, 255}; | |
| verifyResultsExternal(target, pixelColor); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| #endif | |
| } | |
| // Test sampling from a YUV AHB with a regular external sampler without data. This gives coverage of | |
| // sampling even if we can't verify the results. | |
| TEST_P(ImageTest, SourceYUVAHBTargetExternalRGBSampleNoData) | |
| { | |
| // Multiple issues sampling AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 in the Vulkan backend: | |
| // http://issuetracker.google.com/172649538 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, | |
| kDefaultAttribs, {}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| glUseProgram(mTextureExternalProgram); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target); | |
| glUniform1i(mTextureExternalUniformLocation, 0); | |
| // Sample from the YUV texture with a nearest sampler | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| drawQuad(mTextureExternalProgram, "position", 0.5f); | |
| // Sample from the YUV texture with a linear sampler | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| drawQuad(mTextureExternalProgram, "position", 0.5f); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| } | |
| // Test sampling from a YUV AHB using EXT_yuv_target | |
| TEST_P(ImageTestES3, SourceYUVAHBTargetExternalYUVSample) | |
| { | |
| #ifndef ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| std::cout << "Test skipped: !ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT." << std::endl; | |
| return; | |
| #else | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // 3 planes of data | |
| GLubyte dataY[4] = {7, 51, 197, 231}; | |
| GLubyte dataCb[1] = { | |
| 128, | |
| }; | |
| GLubyte dataCr[1] = { | |
| 192, | |
| }; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource( | |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAttribs, | |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| GLubyte pixelColor[4] = {197, 128, 192, 255}; | |
| verifyResultsExternalYUV(target, pixelColor); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| #endif | |
| } | |
| // Test rendering to a YUV AHB using EXT_yuv_target | |
| TEST_P(ImageTestES3, RenderToYUVAHB) | |
| { | |
| #ifndef ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| std::cout << "Test skipped: !ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT." << std::endl; | |
| return; | |
| #else | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // 3 planes of data, initialize to all zeroes | |
| GLubyte dataY[4] = {0, 0, 0, 0}; | |
| GLubyte dataCb[1] = { | |
| 0, | |
| }; | |
| GLubyte dataCr[1] = { | |
| 0, | |
| }; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource( | |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAttribs, | |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| // Set up a framebuffer to render into the AHB | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, target, | |
| 0); | |
| ASSERT_GL_NO_ERROR(); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| GLubyte drawColor[4] = {197, 128, 192, 255}; | |
| glUseProgram(mRenderYUVProgram); | |
| glUniform4f(mRenderYUVUniformLocation, drawColor[0] / 255.0f, drawColor[1] / 255.0f, | |
| drawColor[2] / 255.0f, drawColor[3] / 255.0f); | |
| drawQuad(mRenderYUVProgram, "position", 0.0f); | |
| ASSERT_GL_NO_ERROR(); | |
| // ReadPixels returns the RGB converted color | |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 159, 212, 255), 1.0); | |
| // Finish before reading back AHB data | |
| glFinish(); | |
| GLubyte expectedDataY[4] = {drawColor[0], drawColor[0], drawColor[0], drawColor[0]}; | |
| GLubyte expectedDataCb[1] = { | |
| drawColor[1], | |
| }; | |
| GLubyte expectedDataCr[1] = { | |
| drawColor[2], | |
| }; | |
| verifyResultAHB(source, {{expectedDataY, 1}, {expectedDataCb, 1}, {expectedDataCr, 1}}); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| #endif | |
| } | |
| // Test clearing to a YUV AHB using EXT_yuv_target | |
| TEST_P(ImageTestES3, ClearYUVAHB) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, | |
| kDefaultAttribs, {}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| // Set up a framebuffer to render into the AHB | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, target, | |
| 0); | |
| ASSERT_GL_NO_ERROR(); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| // Clearing a YUV framebuffer reinterprets the rgba clear color as YUV values and writes them | |
| // directly to the buffer | |
| GLubyte clearColor[4] = {197, 128, 192, 255}; | |
| glClearColor(clearColor[0] / 255.0f, clearColor[1] / 255.0f, clearColor[2] / 255.0f, | |
| clearColor[3] / 255.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| ASSERT_GL_NO_ERROR(); | |
| // ReadPixels returns the RGB converted color | |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 159, 212, 255), 1.0); | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| } | |
| // Test validatin of using EXT_yuv_target | |
| TEST_P(ImageTestES3, YUVValidation) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT | |
| AHardwareBuffer *yuvSource; | |
| EGLImageKHR yuvImage; | |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, | |
| kDefaultAttribs, {}, &yuvSource, &yuvImage); | |
| GLuint yuvTexture; | |
| createEGLImageTargetTextureExternal(yuvImage, &yuvTexture); | |
| GLFramebuffer yuvFbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, yuvFbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, | |
| yuvTexture, 0); | |
| ASSERT_GL_NO_ERROR(); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| // Create an rgba image | |
| AHardwareBuffer *rgbaSource; | |
| EGLImageKHR rgbaImage; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {}, &rgbaSource, &rgbaImage); | |
| GLuint rgbaExternalTexture; | |
| createEGLImageTargetTextureExternal(rgbaImage, &rgbaExternalTexture); | |
| GLFramebuffer rgbaExternalFbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, rgbaExternalFbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, | |
| rgbaExternalTexture, 0); | |
| ASSERT_GL_NO_ERROR(); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| // Create a 2d rgb texture/framebuffer | |
| GLTexture rgb2DTexture; | |
| glBindTexture(GL_TEXTURE_2D, rgb2DTexture); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); | |
| GLFramebuffer rgb2DFbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, rgb2DFbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rgb2DTexture, 0); | |
| ASSERT_GL_NO_ERROR(); | |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| // It's an error to sample from a non-yuv external texture with a yuv sampler | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| glUseProgram(mTextureYUVProgram); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, rgbaExternalTexture); | |
| glUniform1i(mTextureYUVUniformLocation, 0); | |
| drawQuad(mTextureYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to render into a YUV framebuffer without a YUV writing program | |
| glBindFramebuffer(GL_FRAMEBUFFER, yuvFbo); | |
| glUseProgram(mTextureExternalESSL3Program); | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, rgbaExternalTexture); | |
| glUniform1i(mTextureExternalESSL3UniformLocation, 0); | |
| drawQuad(mTextureExternalESSL3Program, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to render to a RGBA framebuffer with a YUV writing program | |
| glBindFramebuffer(GL_FRAMEBUFFER, rgb2DFbo); | |
| glUseProgram(mRenderYUVProgram); | |
| drawQuad(mRenderYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to set disable r, g, or b color writes when rendering to a yuv framebuffer | |
| glBindFramebuffer(GL_FRAMEBUFFER, yuvFbo); | |
| glUseProgram(mRenderYUVProgram); | |
| glColorMask(false, true, true, true); | |
| drawQuad(mRenderYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glColorMask(true, false, true, true); | |
| drawQuad(mRenderYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glColorMask(true, true, false, true); | |
| drawQuad(mRenderYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to enable blending when rendering to a yuv framebuffer | |
| glBindFramebuffer(GL_FRAMEBUFFER, yuvFbo); | |
| glUseProgram(mRenderYUVProgram); | |
| glDisable(GL_BLEND); | |
| drawQuad(mRenderYUVProgram, "position", 0.5f); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to blit to/from a yuv framebuffer | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, yuvFbo); | |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rgb2DFbo); | |
| glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, rgb2DFbo); | |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, yuvFbo); | |
| glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // It's an error to glCopyTexImage/glCopyTexSubImage from a YUV framebuffer | |
| glBindFramebuffer(GL_FRAMEBUFFER, yuvFbo); | |
| glBindTexture(GL_TEXTURE_2D, rgb2DTexture); | |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 1, 1, 0); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| // Clean up | |
| glDeleteTextures(1, &yuvTexture); | |
| eglDestroyImageKHR(window->getDisplay(), yuvImage); | |
| destroyAndroidHardwareBuffer(yuvSource); | |
| glDeleteTextures(1, &rgbaExternalTexture); | |
| eglDestroyImageKHR(window->getDisplay(), rgbaImage); | |
| destroyAndroidHardwareBuffer(rgbaSource); | |
| } | |
| // Testing source AHB EGL image with colorspace, target external ESSL3 texture | |
| TEST_P(ImageTestES3, SourceAHBTargetExternalESSL3_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceAHBTargetExternalESSL3_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceAHBTargetExternalESSL3_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || | |
| !hasExternalESSL3Ext()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| attribs, {{kLinearColor, 4}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| // Use texture target bound to egl image as source and render to framebuffer | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternalESSL3(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternalESSL3(target, kLinearColor); | |
| } | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Create a depth format AHB backed EGL image and verify that the image's aspect is honored | |
| TEST_P(ImageTest, SourceAHBTarget2DDepth) | |
| { | |
| // TODO - Support for depth formats in AHB is missing (http://anglebug.com/4818) | |
| ANGLE_SKIP_TEST_IF(true); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| GLint level = 0; | |
| GLsizei width = 1; | |
| GLsizei height = 1; | |
| GLsizei depth = 1; | |
| GLint depthStencilValue = 0; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource( | |
| width, height, depth, AHARDWAREBUFFER_FORMAT_D24_UNORM, kDefaultAttribs, | |
| {{reinterpret_cast<GLubyte *>(&depthStencilValue), 3}}, &source, &image); | |
| // Create a texture target to bind the egl image | |
| GLuint depthTextureTarget; | |
| createEGLImageTargetTexture2D(image, &depthTextureTarget); | |
| // Create a color texture and fill it with red | |
| GLTexture colorTexture; | |
| glBindTexture(GL_TEXTURE_2D, colorTexture); | |
| glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| GLColor::red.data()); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| glBindTexture(GL_TEXTURE_2D, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| EXPECT_GL_NO_ERROR(); | |
| // Attach the color and depth texture to the FBO | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTextureTarget, | |
| 0); | |
| EXPECT_GL_NO_ERROR(); | |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| // Clear the color texture to red | |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); | |
| // Enable Depth test but disable depth writes. The depth function is set to ">". | |
| glEnable(GL_DEPTH_TEST); | |
| glDepthMask(GL_FALSE); | |
| glDepthFunc(GL_GREATER); | |
| // Fill any fragment of the color attachment with blue if it passes the depth test. | |
| ANGLE_GL_PROGRAM(colorFillProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); | |
| drawQuad(colorFillProgram, essl1_shaders::PositionAttrib(), 1.0f, 1.0f); | |
| // Since 1.0f > 0.0f, all fragments of the color attachment should be blue. | |
| EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255); | |
| // Clean up | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| } | |
| TEST_P(ImageTest, Source2DTargetRenderbuffer) | |
| { | |
| Source2DTargetRenderbuffer_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, Source2DTargetRenderbuffer_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| // Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| Source2DTargetRenderbuffer_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::Source2DTargetRenderbuffer_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source, | |
| &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetRenderbuffer(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsRenderbuffer(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsRenderbuffer(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteRenderbuffers(1, &target); | |
| } | |
| // Testing source native client buffer EGL image, target external texture | |
| // where source native client buffer is created using EGL_ANDROID_create_native_client_buffer API | |
| TEST_P(ImageTest, SourceNativeClientBufferTargetExternal) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceNativeClientBufferTargetExternal_helper(kDefaultAttribs); | |
| } | |
| // Testing source native client buffer EGL image with colorspace, target external texture | |
| // where source native client buffer is created using EGL_ANDROID_create_native_client_buffer API | |
| TEST_P(ImageTest, SourceNativeClientBufferTargetExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceNativeClientBufferTargetExternal_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceNativeClientBufferTargetExternal_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create an Image backed by a native client buffer allocated using | |
| // EGL_ANDROID_create_native_client_buffer API | |
| EGLImageKHR image; | |
| createEGLImageANWBClientBufferSource(1, 1, 1, kNativeClientBufferAttribs_RGBA8_Texture, attribs, | |
| {{kLinearColor, 4}}, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| } | |
| // Testing source native client buffer EGL image, target Renderbuffer | |
| // where source native client buffer is created using EGL_ANDROID_create_native_client_buffer API | |
| TEST_P(ImageTest, SourceNativeClientBufferTargetRenderbuffer) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| SourceNativeClientBufferTargetRenderbuffer_helper(kDefaultAttribs); | |
| } | |
| // Testing source native client buffer EGL image with colorspace, target Renderbuffer | |
| // where source native client buffer is created using EGL_ANDROID_create_native_client_buffer API | |
| TEST_P(ImageTest, SourceNativeClientBufferTargetRenderbuffer_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| // Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| SourceNativeClientBufferTargetRenderbuffer_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceNativeClientBufferTargetRenderbuffer_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| // Create an Image backed by a native client buffer allocated using | |
| // EGL_ANDROID_create_native_client_buffer API | |
| EGLImageKHR image; | |
| createEGLImageANWBClientBufferSource(1, 1, 1, kNativeClientBufferAttribs_RGBA8_Renderbuffer, | |
| attribs, {{kLinearColor, 4}}, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetRenderbuffer(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsRenderbuffer(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsRenderbuffer(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteRenderbuffers(1, &target); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| } | |
| TEST_P(ImageTest, Source2DTargetExternal) | |
| { | |
| Source2DTargetExternal_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, Source2DTargetExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source2DTargetExternal_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::Source2DTargetExternal_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasExternalExt()); | |
| // Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import | |
| ANGLE_SKIP_TEST_IF(IsOzone()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source, | |
| &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| TEST_P(ImageTestES3, Source2DTargetExternalESSL3) | |
| { | |
| Source2DTargetExternalESSL3_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTestES3, Source2DTargetExternalESSL3_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source2DTargetExternalESSL3_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::Source2DTargetExternalESSL3_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || | |
| !hasExternalESSL3Ext()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source, | |
| &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternalESSL3(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternalESSL3(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| TEST_P(ImageTest, SourceCubeTarget2D) | |
| { | |
| SourceCubeTarget2D_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceCubeTarget2D_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceCubeTarget2D_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceCubeTarget2D_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt()); | |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageCubemapTextureSource( | |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube), | |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, &kSrgbColorCube[faceIdx * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, &kLinearColorCube[faceIdx * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| } | |
| TEST_P(ImageTest, SourceCubeTargetRenderbuffer) | |
| { | |
| SourceCubeTargetRenderbuffer_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceCubeTargetRenderbuffer_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| // Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| SourceCubeTargetRenderbuffer_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceCubeTargetRenderbuffer_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt()); | |
| // http://anglebug.com/3145 | |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia()); | |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageCubemapTextureSource( | |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube), | |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetRenderbuffer(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsRenderbuffer(target, &kSrgbColorCube[faceIdx * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsRenderbuffer(target, &kLinearColorCube[faceIdx * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteRenderbuffers(1, &target); | |
| } | |
| } | |
| // Test cubemap -> external texture EGL images. | |
| TEST_P(ImageTest, SourceCubeTargetExternal) | |
| { | |
| SourceCubeTargetExternal_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceCubeTargetExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceCubeTargetExternal_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceCubeTargetExternal_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt() || !hasExternalExt()); | |
| // Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import | |
| ANGLE_SKIP_TEST_IF(IsOzone()); | |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageCubemapTextureSource( | |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube), | |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, &kSrgbColorCube[faceIdx * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, &kLinearColorCube[faceIdx * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteRenderbuffers(1, &target); | |
| } | |
| } | |
| // Test cubemap -> external texture EGL images using ESSL3 shaders. | |
| TEST_P(ImageTestES3, SourceCubeTargetExternalESSL3) | |
| { | |
| SourceCubeTargetExternalESSL3_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTestES3, SourceCubeTargetExternalESSL3_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceCubeTargetExternalESSL3_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceCubeTargetExternalESSL3_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || !hasCubemapExt()); | |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageCubemapTextureSource( | |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube), | |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternalESSL3(target, &kSrgbColorCube[faceIdx * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternalESSL3(target, &kLinearColorCube[faceIdx * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteRenderbuffers(1, &target); | |
| } | |
| } | |
| TEST_P(ImageTest, Source3DTargetTexture) | |
| { | |
| Source3DTargetTexture_helper(false); | |
| } | |
| TEST_P(ImageTest, Source3DTargetTexture_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source3DTargetTexture_helper(true); | |
| } | |
| void ImageTest::Source3DTargetTexture_helper(const bool withColorspace) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has3DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); | |
| constexpr size_t depth = 2; | |
| for (size_t layer = 0; layer < depth; layer++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, | |
| get3DAttributes(withColorspace, layer), kLinearColor3D, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| if (withColorspace) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, &kSrgbColor3D[layer * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, &kLinearColor3D[layer * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| } | |
| TEST_P(ImageTest, Source3DTargetRenderbuffer) | |
| { | |
| Source3DTargetRenderbuffer_helper(false); | |
| } | |
| TEST_P(ImageTest, Source3DTargetRenderbuffer_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| // Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| Source3DTargetRenderbuffer_helper(true); | |
| } | |
| void ImageTest::Source3DTargetRenderbuffer_helper(const bool withColorspace) | |
| { | |
| // Qualcom drivers appear to always bind the 0 layer of the source 3D texture when the | |
| // target is a renderbuffer. They work correctly when the target is a 2D texture. | |
| // http://anglebug.com/2745 | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has3DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); | |
| constexpr size_t depth = 2; | |
| for (size_t layer = 0; layer < depth; layer++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, | |
| get3DAttributes(withColorspace, layer), kLinearColor3D, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetRenderbuffer(image, &target); | |
| if (withColorspace) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsRenderbuffer(target, &kSrgbColor3D[layer * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsRenderbuffer(target, &kLinearColor3D[layer * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| } | |
| // Test 3D -> external texture EGL images. | |
| TEST_P(ImageTest, Source3DTargetExternal) | |
| { | |
| Source3DTargetExternal_helper(false); | |
| } | |
| TEST_P(ImageTest, Source3DTargetExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source3DTargetExternal_helper(true); | |
| } | |
| void ImageTest::Source3DTargetExternal_helper(const bool withColorspace) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalExt() || !hasBaseExt() || !has3DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); | |
| // Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import | |
| ANGLE_SKIP_TEST_IF(IsOzone()); | |
| constexpr size_t depth = 2; | |
| for (size_t layer = 0; layer < depth; layer++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, | |
| get3DAttributes(withColorspace, layer), kLinearColor3D, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (withColorspace) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, &kSrgbColor3D[layer * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, &kLinearColor3D[layer * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| } | |
| // Test 3D -> external texture EGL images using ESSL3 shaders. | |
| TEST_P(ImageTestES3, Source3DTargetExternalESSL3) | |
| { | |
| Source3DTargetExternalESSL3_helper(false); | |
| } | |
| TEST_P(ImageTestES3, Source3DTargetExternalESSL3_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| Source3DTargetExternalESSL3_helper(true); | |
| } | |
| void ImageTest::Source3DTargetExternalESSL3_helper(const bool withColorspace) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || | |
| !has3DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); | |
| constexpr size_t depth = 2; | |
| for (size_t layer = 0; layer < depth; layer++) | |
| { | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, | |
| get3DAttributes(withColorspace, layer), kLinearColor3D, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (withColorspace) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternalESSL3(target, &kSrgbColor3D[layer * 4]); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternalESSL3(target, &kLinearColor3D[layer * 4]); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| } | |
| TEST_P(ImageTest, SourceRenderbufferTargetTexture) | |
| { | |
| SourceRenderbufferTargetTexture_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceRenderbufferTargetTexture_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceRenderbufferTargetTexture_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceRenderbufferTargetTexture_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResults2D(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResults2D(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteRenderbuffers(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Test renderbuffer -> external texture EGL images. | |
| TEST_P(ImageTest, SourceRenderbufferTargetTextureExternal) | |
| { | |
| SourceRenderbufferTargetTextureExternal_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceRenderbufferTargetTextureExternal_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceRenderbufferTargetTextureExternal_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceRenderbufferTargetTextureExternal_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalExt() || !hasBaseExt() || !hasRenderbufferExt()); | |
| // Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import | |
| ANGLE_SKIP_TEST_IF(IsOzone()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternal(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternal(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteRenderbuffers(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Test renderbuffer -> external texture EGL images using ESSL3 shaders. | |
| TEST_P(ImageTestES3, SourceRenderbufferTargetTextureExternalESSL3) | |
| { | |
| SourceRenderbufferTargetTextureExternalESSL3_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTestES3, SourceRenderbufferTargetTextureExternalESSL3_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| SourceRenderbufferTargetTextureExternalESSL3_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceRenderbufferTargetTextureExternalESSL3_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || | |
| !hasRenderbufferExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTextureExternal(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsExternalESSL3(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsExternalESSL3(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteRenderbuffers(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| TEST_P(ImageTest, SourceRenderbufferTargetRenderbuffer) | |
| { | |
| SourceRenderbufferTargetRenderbuffer_helper(kDefaultAttribs); | |
| } | |
| TEST_P(ImageTest, SourceRenderbufferTargetRenderbuffer_Colorspace) | |
| { | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); | |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); | |
| // Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281 | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| SourceRenderbufferTargetRenderbuffer_helper(kColorspaceAttribs); | |
| } | |
| void ImageTest::SourceRenderbufferTargetRenderbuffer_helper(const EGLint *attribs) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetRenderbuffer(image, &target); | |
| if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE) | |
| { | |
| // Expect that the target texture has the corresponding sRGB color values | |
| verifyResultsRenderbuffer(target, kSrgbColor); | |
| } | |
| else | |
| { | |
| // Expect that the target texture has the same color as the source texture | |
| verifyResultsRenderbuffer(target, kLinearColor); | |
| } | |
| // Clean up | |
| glDeleteRenderbuffers(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteRenderbuffers(1, &target); | |
| } | |
| // Delete the source texture and EGL image. The image targets should still have the same data | |
| // because | |
| // they hold refs to the image. | |
| TEST_P(ImageTest, Deletion) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create multiple targets | |
| GLuint targetTexture; | |
| createEGLImageTargetTexture2D(image, &targetTexture); | |
| GLuint targetRenderbuffer; | |
| createEGLImageTargetRenderbuffer(image, &targetRenderbuffer); | |
| // Delete the source texture | |
| glDeleteTextures(1, &source); | |
| source = 0; | |
| // Expect that both the targets have the original data | |
| verifyResults2D(targetTexture, originalData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, originalData); | |
| // Update the data of the target | |
| glBindTexture(GL_TEXTURE_2D, targetTexture); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that both targets have the updated data | |
| verifyResults2D(targetTexture, updateData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, updateData); | |
| // Delete the EGL image | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| image = EGL_NO_IMAGE_KHR; | |
| // Update the data of the target back to the original data | |
| glBindTexture(GL_TEXTURE_2D, targetTexture); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData); | |
| // Expect that both targets have the original data again | |
| verifyResults2D(targetTexture, originalData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, originalData); | |
| // Clean up | |
| glDeleteTextures(1, &targetTexture); | |
| glDeleteRenderbuffers(1, &targetRenderbuffer); | |
| } | |
| TEST_P(ImageTest, MipLevels) | |
| { | |
| // Driver returns OOM in read pixels, some internal error. | |
| ANGLE_SKIP_TEST_IF(IsOzone() && IsOpenGLES()); | |
| // Also fails on NVIDIA Shield TV bot. | |
| // http://anglebug.com/3850 | |
| ANGLE_SKIP_TEST_IF(IsNVIDIAShield() && IsOpenGLES()); | |
| // On Vulkan, the clear operation in the loop is optimized with a render pass loadOp=Clear. On | |
| // Linux/Intel, that operation is mistakenly clearing the rest of the mips to 0. | |
| // http://anglebug.com/3284 | |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsLinux() && IsIntel()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| const size_t mipLevels = 3; | |
| const size_t textureSize = 4; | |
| std::vector<GLColor> mip0Data(textureSize * textureSize, GLColor::red); | |
| std::vector<GLColor> mip1Data(mip0Data.size() << 1, GLColor::green); | |
| std::vector<GLColor> mip2Data(mip0Data.size() << 2, GLColor::blue); | |
| GLubyte *data[mipLevels] = { | |
| reinterpret_cast<GLubyte *>(&mip0Data[0]), | |
| reinterpret_cast<GLubyte *>(&mip1Data[0]), | |
| reinterpret_cast<GLubyte *>(&mip2Data[0]), | |
| }; | |
| GLuint source; | |
| glGenTextures(1, &source); | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| for (size_t level = 0; level < mipLevels; level++) | |
| { | |
| glTexImage2D(GL_TEXTURE_2D, static_cast<GLint>(level), GL_RGBA, textureSize >> level, | |
| textureSize >> level, 0, GL_RGBA, GL_UNSIGNED_BYTE, data[level]); | |
| } | |
| ASSERT_GL_NO_ERROR(); | |
| for (size_t level = 0; level < mipLevels; level++) | |
| { | |
| // Create the Image | |
| EGLint attribs[] = { | |
| EGL_GL_TEXTURE_LEVEL_KHR, | |
| static_cast<EGLint>(level), | |
| EGL_NONE, | |
| }; | |
| EGLImageKHR image = | |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, | |
| reinterpretHelper<EGLClientBuffer>(source), attribs); | |
| ASSERT_EGL_SUCCESS(); | |
| // Create a texture and renderbuffer target | |
| GLuint textureTarget; | |
| createEGLImageTargetTexture2D(image, &textureTarget); | |
| // Disable mipmapping | |
| glBindTexture(GL_TEXTURE_2D, textureTarget); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| GLuint renderbufferTarget; | |
| createEGLImageTargetRenderbuffer(image, &renderbufferTarget); | |
| // Expect that the targets have the same color as the source texture | |
| verifyResults2D(textureTarget, data[level]); | |
| verifyResultsRenderbuffer(renderbufferTarget, data[level]); | |
| // Update the data by uploading data to the texture | |
| std::vector<GLuint> textureUpdateData(textureSize * textureSize, level); | |
| glBindTexture(GL_TEXTURE_2D, textureTarget); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, textureSize >> level, textureSize >> level, GL_RGBA, | |
| GL_UNSIGNED_BYTE, textureUpdateData.data()); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that both the texture and renderbuffer see the updated texture data | |
| verifyResults2D(textureTarget, reinterpret_cast<GLubyte *>(textureUpdateData.data())); | |
| verifyResultsRenderbuffer(renderbufferTarget, | |
| reinterpret_cast<GLubyte *>(textureUpdateData.data())); | |
| // Update the renderbuffer by clearing it | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | |
| renderbufferTarget); | |
| GLubyte clearValue = static_cast<GLubyte>(level); | |
| GLubyte renderbufferClearData[4]{clearValue, clearValue, clearValue, clearValue}; | |
| glClearColor(renderbufferClearData[0] / 255.0f, renderbufferClearData[1] / 255.0f, | |
| renderbufferClearData[2] / 255.0f, renderbufferClearData[3] / 255.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| ASSERT_GL_NO_ERROR(); | |
| // Expect that both the texture and renderbuffer see the cleared renderbuffer data | |
| verifyResults2D(textureTarget, renderbufferClearData); | |
| verifyResultsRenderbuffer(renderbufferTarget, renderbufferClearData); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &textureTarget); | |
| glDeleteRenderbuffers(1, &renderbufferTarget); | |
| } | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| } | |
| // Respecify the source texture, orphaning it. The target texture should not have updated data. | |
| TEST_P(ImageTest, Respecification) | |
| { | |
| // Respecification of textures that does not change the size of the level attached to the EGL | |
| // image does not cause orphaning on Qualcomm devices. http://anglebug.com/2744 | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); | |
| ANGLE_SKIP_TEST_IF(IsOzone() && IsOpenGLES()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Respecify source | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that the target texture has the original data | |
| verifyResults2D(target, originalData); | |
| // Expect that the source texture has the updated data | |
| verifyResults2D(source, updateData); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Respecify the source texture with a different size, orphaning it. The target texture should not | |
| // have updated data. | |
| TEST_P(ImageTest, RespecificationDifferentSize) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[16] = {0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Respecify source | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that the target texture has the original data | |
| verifyResults2D(target, originalData); | |
| // Expect that the source texture has the updated data | |
| verifyResults2D(source, updateData); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // First render to a target texture, then respecify the source texture, orphaning it. | |
| // The target texture's FBO should be notified of the target texture's orphaning. | |
| TEST_P(ImageTest, RespecificationWithFBO) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLuint program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); | |
| ASSERT_NE(0u, program); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Render to the target texture | |
| GLuint fbo; | |
| glGenFramebuffers(1, &fbo); | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); | |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); | |
| // Respecify source with same parameters. This should not change the texture storage in D3D11. | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that the source texture has the updated data | |
| verifyResults2D(source, updateData); | |
| // Render to the target texture again and verify it gets the rendered pixels. | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); | |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| glDeleteProgram(program); | |
| glDeleteFramebuffers(1, &fbo); | |
| } | |
| // Test that respecifying a level of the target texture orphans it and keeps a copy of the EGLimage | |
| // data | |
| TEST_P(ImageTest, RespecificationOfOtherLevel) | |
| { | |
| // Respecification of textures that does not change the size of the level attached to the EGL | |
| // image does not cause orphaning on Qualcomm devices. http://anglebug.com/2744 | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); | |
| // It is undefined what happens to the mip 0 of the dest texture after it is orphaned. Some | |
| // backends explicitly copy the data but Vulkan does not. | |
| ANGLE_SKIP_TEST_IF(IsVulkan()); | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[2 * 2 * 4] = { | |
| 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, | |
| }; | |
| GLubyte updateData[2 * 2 * 4] = { | |
| 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, | |
| }; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(2, 2, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create the target | |
| GLuint target; | |
| createEGLImageTargetTexture2D(image, &target); | |
| // Expect that the target and source textures have the original data | |
| verifyResults2D(source, originalData); | |
| verifyResults2D(target, originalData); | |
| // Add a new mipLevel to the target, orphaning it | |
| glBindTexture(GL_TEXTURE_2D, target); | |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, originalData); | |
| EXPECT_GL_NO_ERROR(); | |
| // Expect that the target and source textures still have the original data | |
| verifyResults2D(source, originalData); | |
| verifyResults2D(target, originalData); | |
| // Update the source's data | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that the target still has the original data and source has the updated data | |
| verifyResults2D(source, updateData); | |
| verifyResults2D(target, originalData); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &target); | |
| } | |
| // Update the data of the source and target textures. All image siblings should have the new data. | |
| TEST_P(ImageTest, UpdatedData) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create multiple targets | |
| GLuint targetTexture; | |
| createEGLImageTargetTexture2D(image, &targetTexture); | |
| GLuint targetRenderbuffer; | |
| createEGLImageTargetRenderbuffer(image, &targetRenderbuffer); | |
| // Expect that both the source and targets have the original data | |
| verifyResults2D(source, originalData); | |
| verifyResults2D(targetTexture, originalData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, originalData); | |
| // Update the data of the source | |
| glBindTexture(GL_TEXTURE_2D, source); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Expect that both the source and targets have the updated data | |
| verifyResults2D(source, updateData); | |
| verifyResults2D(targetTexture, updateData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, updateData); | |
| // Update the data of the target back to the original data | |
| glBindTexture(GL_TEXTURE_2D, targetTexture); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData); | |
| // Expect that both the source and targets have the original data again | |
| verifyResults2D(source, originalData); | |
| verifyResults2D(targetTexture, originalData); | |
| verifyResultsRenderbuffer(targetRenderbuffer, originalData); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| glDeleteTextures(1, &targetTexture); | |
| glDeleteRenderbuffers(1, &targetRenderbuffer); | |
| } | |
| // Check that the external texture is successfully updated when only glTexSubImage2D is called. | |
| TEST_P(ImageTest, UpdatedExternalTexture) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| const uint32_t bytesPerPixel = 4; | |
| // Create the Image | |
| AHardwareBuffer *source; | |
| EGLImageKHR image; | |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, | |
| kDefaultAttribs, {{originalData, bytesPerPixel}}, | |
| &source, &image); | |
| // Create target | |
| GLuint targetTexture; | |
| createEGLImageTargetTexture2D(image, &targetTexture); | |
| // Expect that both the target have the original data | |
| verifyResults2D(targetTexture, originalData); | |
| // Update the data of the source | |
| glBindTexture(GL_TEXTURE_2D, targetTexture); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| // Set sync object and flush the GL commands | |
| EGLSyncKHR fence = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, NULL); | |
| ASSERT_NE(fence, EGL_NO_SYNC_KHR); | |
| glFlush(); | |
| // Delete the target texture | |
| glDeleteTextures(1, &targetTexture); | |
| // Wait that the flush command is finished | |
| EGLint result = eglClientWaitSyncKHR(window->getDisplay(), fence, 0, 1000000000); | |
| ASSERT_EQ(result, EGL_CONDITION_SATISFIED_KHR); | |
| ASSERT_EGL_TRUE(eglDestroySyncKHR(window->getDisplay(), fence)); | |
| // Delete the EGL image | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| // Access the android hardware buffer directly to check the data is updated | |
| verifyResultAHB(source, {{updateData, bytesPerPixel}}); | |
| // Create the EGL image again | |
| image = | |
| eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, | |
| angle::android::AHardwareBufferToClientBuffer(source), kDefaultAttribs); | |
| ASSERT_EGL_SUCCESS(); | |
| // Create the target texture again | |
| createEGLImageTargetTexture2D(image, &targetTexture); | |
| // Expect that the target have the update data | |
| verifyResults2D(targetTexture, updateData); | |
| // Clean up | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| destroyAndroidHardwareBuffer(source); | |
| glDeleteTextures(1, &targetTexture); | |
| } | |
| // Check that the texture successfully updates when an image is deleted | |
| TEST_P(ImageTest, DeletedImageWithSameSizeAndFormat) | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); | |
| GLubyte originalData[4] = {255, 0, 255, 255}; | |
| GLubyte updateData[4] = {0, 255, 0, 255}; | |
| // Create the Image | |
| GLuint source; | |
| EGLImageKHR image; | |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, | |
| &source, &image); | |
| // Create texture & bind to Image | |
| GLuint texture; | |
| createEGLImageTargetTexture2D(image, &texture); | |
| // Delete Image | |
| eglDestroyImageKHR(window->getDisplay(), image); | |
| ASSERT_EGL_SUCCESS(); | |
| // Redefine Texture | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData); | |
| ASSERT_GL_NO_ERROR(); | |
| // Clean up | |
| glDeleteTextures(1, &source); | |
| glDeleteTextures(1, &texture); | |
| } | |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ImageTest); | |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImageTestES3); | |
| ANGLE_INSTANTIATE_TEST_ES3(ImageTestES3); | |
| } // namespace angle |