| // |
| // 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" |
| |
| 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 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"; |
| } // 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 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 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"; |
| |
| mTextureProgram = CompileProgram(kVS, kTextureFS); |
| if (mTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); |
| |
| 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"); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mTextureProgram); |
| glDeleteProgram(mTextureExternalProgram); |
| glDeleteProgram(mTextureExternalESSL3Program); |
| } |
| |
| void createEGLImage2DTextureSource(size_t width, |
| size_t height, |
| GLenum format, |
| GLenum type, |
| 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(); |
| |
| EGLint attribs[] = { |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| |
| 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, |
| 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(); |
| |
| EGLint attribs[] = { |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| |
| 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, |
| void *data, |
| size_t imageLayer, |
| 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(); |
| |
| EGLint attribs[] = { |
| EGL_GL_TEXTURE_ZOFFSET_KHR, |
| static_cast<EGLint>(imageLayer), |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| 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, |
| 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(); |
| |
| EGLint attribs[] = { |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| |
| 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 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; |
| } |
| |
| 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 verifyResultsTexture(GLuint texture, |
| GLubyte data[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 has the same color as the source texture |
| EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]); |
| } |
| |
| void verifyResults2D(GLuint texture, GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_2D, mTextureProgram, |
| mTextureUniformLocation); |
| } |
| |
| 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 verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte data[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 has the same color as the source texture |
| EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]); |
| |
| glDeleteFramebuffers(1, &framebuffer); |
| } |
| |
| 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 hasOESExt() const { return IsGLExtensionEnabled(kOESExt); } |
| |
| bool hasExternalExt() const { return IsGLExtensionEnabled(kExternalExt); } |
| |
| bool hasExternalESSL3Ext() const { return IsGLExtensionEnabled(kExternalESSL3Ext); } |
| |
| 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); |
| } |
| |
| GLuint mTextureProgram; |
| GLint mTextureUniformLocation; |
| |
| GLuint mTextureExternalProgram = 0; |
| GLint mTextureExternalUniformLocation = -1; |
| |
| GLuint mTextureExternalESSL3Program = 0; |
| GLint mTextureExternalESSL3UniformLocation = -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 |
| { |
| 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) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &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 minFilter : validWrapModes) |
| { |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, minFilter); |
| EXPECT_GL_NO_ERROR(); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, minFilter); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| GLenum invalidWrapModes[]{ |
| GL_REPEAT, |
| GL_MIRRORED_REPEAT, |
| }; |
| for (auto minFilter : invalidWrapModes) |
| { |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, minFilter); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, minFilter); |
| 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) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &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, data); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| |
| TEST_P(ImageTest, Source2DTargetRenderbuffer) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetRenderbuffer(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsRenderbuffer(target, data); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteRenderbuffers(1, &target); |
| } |
| |
| TEST_P(ImageTest, Source2DTargetExternal) |
| { |
| 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()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsExternal(target, data); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteRenderbuffers(1, &target); |
| } |
| |
| TEST_P(ImageTestES3, Source2DTargetExternalESSL3) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasExternalESSL3Ext()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsExternalESSL3(target, data); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteRenderbuffers(1, &target); |
| } |
| |
| TEST_P(ImageTest, SourceCubeTarget2D) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt()); |
| |
| GLubyte data[24] = { |
| 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, |
| 0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255, |
| }; |
| |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4, |
| EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &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, &data[faceIdx * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| } |
| |
| TEST_P(ImageTest, SourceCubeTargetRenderbuffer) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt()); |
| |
| // http://anglebug.com/3145 |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia()); |
| |
| GLubyte data[24] = { |
| 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, |
| 0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255, |
| }; |
| |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4, |
| EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetRenderbuffer(image, &target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsRenderbuffer(target, &data[faceIdx * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteRenderbuffers(1, &target); |
| } |
| } |
| |
| // Test cubemap -> external texture EGL images. |
| TEST_P(ImageTest, SourceCubeTargetExternal) |
| { |
| 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()); |
| |
| GLubyte data[24] = { |
| 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, |
| 0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255, |
| }; |
| |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4, |
| EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsExternal(target, &data[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) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || !hasCubemapExt()); |
| |
| GLubyte data[24] = { |
| 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, |
| 0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255, |
| }; |
| |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<uint8_t *>(data), sizeof(GLubyte) * 4, |
| EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsExternalESSL3(target, &data[faceIdx * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteRenderbuffers(1, &target); |
| } |
| } |
| |
| TEST_P(ImageTest, Source3DTargetTexture) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has3DTextureExt()); |
| |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); |
| |
| const size_t depth = 2; |
| GLubyte data[4 * depth] = { |
| 255, 0, 255, 255, 255, 255, 0, 255, |
| }; |
| |
| for (size_t layer = 0; layer < depth; layer++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source, |
| &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTexture2D(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResults2D(target, &data[layer * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| } |
| |
| TEST_P(ImageTest, Source3DTargetRenderbuffer) |
| { |
| // 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")); |
| |
| const size_t depth = 2; |
| GLubyte data[4 * depth] = { |
| 255, 0, 255, 255, 255, 255, 0, 255, |
| }; |
| |
| for (size_t layer = 0; layer < depth; layer++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source, |
| &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetRenderbuffer(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsRenderbuffer(target, &data[layer * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| } |
| |
| // Test 3D -> external texture EGL images. |
| TEST_P(ImageTest, Source3DTargetExternal) |
| { |
| 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()); |
| |
| const size_t depth = 2; |
| GLubyte data[4 * depth] = { |
| 255, 0, 255, 255, 255, 255, 0, 255, |
| }; |
| |
| for (size_t layer = 0; layer < depth; layer++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source, |
| &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsExternal(target, &data[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) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || |
| !has3DTextureExt()); |
| |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D")); |
| |
| const size_t depth = 2; |
| GLubyte data[4 * depth] = { |
| 255, 0, 255, 255, 255, 255, 0, 255, |
| }; |
| |
| for (size_t layer = 0; layer < depth; layer++) |
| { |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, data, layer, &source, |
| &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsExternalESSL3(target, &data[layer * 4]); |
| |
| // Clean up |
| glDeleteTextures(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| } |
| |
| TEST_P(ImageTest, SourceRenderbufferTargetTexture) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &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, data); |
| |
| // Clean up |
| glDeleteRenderbuffers(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| |
| // Test renderbuffer -> external texture EGL images. |
| TEST_P(ImageTest, SourceRenderbufferTargetTextureExternal) |
| { |
| 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()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsExternal(target, data); |
| |
| // 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) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || |
| !hasRenderbufferExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetTextureExternal(image, &target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsExternalESSL3(target, data); |
| |
| // Clean up |
| glDeleteRenderbuffers(1, &source); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| glDeleteTextures(1, &target); |
| } |
| |
| TEST_P(ImageTest, SourceRenderbufferTargetRenderbuffer) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); |
| |
| GLubyte data[4] = {255, 0, 255, 255}; |
| |
| // Create the Image |
| GLuint source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, data, &source, &image); |
| |
| // Create the target |
| GLuint target; |
| createEGLImageTargetRenderbuffer(image, &target); |
| |
| // Expect that the target renderbuffer has the same color as the source texture |
| verifyResultsRenderbuffer(target, data); |
| |
| // 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, 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, 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, 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, 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, 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, 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); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ImageTest); |
| ANGLE_INSTANTIATE_TEST_ES3(ImageTestES3); |
| } // namespace angle |