| // |
| // 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/MultiThreadSteps.h" |
| #include "test_utils/gl_raii.h" |
| #include "util/EGLWindow.h" |
| #include "util/test_utils.h" |
| |
| #include "common/android_util.h" |
| |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 29 |
| # define ANGLE_AHARDWARE_BUFFER_SUPPORT |
| // NDK header file for access to Android Hardware Buffers |
| # include <android/hardware_buffer.h> |
| # define ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT |
| #endif |
| |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 33 |
| constexpr bool kHasAHBFrontBufferUsageSupport = 1; |
| #else |
| [[maybe_unused]] constexpr bool kHasAHBFrontBufferUsageSupport = 0; |
| #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 kYUVInternalFormatExt[] = "GL_ANGLE_yuv_internal_format"; |
| constexpr char kYUVTargetExt[] = "GL_EXT_YUV_target"; |
| constexpr char kRGBXInternalFormatExt[] = "GL_ANGLE_rgbx_internal_format"; |
| 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 char kEGLImageStorageExt[] = "GL_EXT_EGL_image_storage"; |
| constexpr char kEGLImageStorageCompressionExt[] = "GL_EXT_EGL_image_storage_compression"; |
| constexpr char kTextureStorageCompressionExt[] = "GL_EXT_texture_storage_compression"; |
| 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[] = {190, 128, 238, 255}; |
| // 3D texture data |
| GLubyte kLinearColor3D[] = {131, 242, 100, 255, 201, 89, 133, 255}; |
| GLubyte kSrgbColor3D[] = {190, 249, 168, 255, 230, 159, 191, 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[] = {148, 192, 232, 255, 230, 159, 191, 255, 176, 230, 174, 255, |
| 96, 160, 244, 255, 219, 234, 143, 255, 149, 176, 167, 255}; |
| GLfloat kCubeFaceX[] = {1.0, -1.0, 0.0, 0.0, 0.0, 0.0}; |
| GLfloat kCubeFaceY[] = {0.0, 0.0, 1.0, -1.0, 0.0, 0.0}; |
| GLfloat kCubeFaceZ[] = {0.0, 0.0, 0.0, 0.0, 1.0, -1.0}; |
| |
| constexpr int kColorspaceAttributeIndex = 2; |
| constexpr int k3DColorspaceAttributeIndex = 4; |
| constexpr int kTextureZOffsetAttributeIndex = 1; |
| constexpr size_t kCubeFaceCount = 6; |
| |
| constexpr int AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1; |
| constexpr int AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2; |
| constexpr int AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3; |
| 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; |
| |
| [[maybe_unused]] constexpr uint64_t ANGLE_AHARDWAREBUFFER_USAGE_FRONT_BUFFER = (1ULL << 32); |
| |
| } // anonymous namespace |
| |
| class ImageTest : public ANGLETest<> |
| { |
| protected: |
| ImageTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| setConfigDepthBits(24); |
| } |
| |
| const char *getVS() const |
| { |
| return R"(precision highp float; |
| attribute vec4 position; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = position; |
| texcoord = (position.xy * 0.5) + 0.5; |
| texcoord.y = 1.0 - texcoord.y; |
| })"; |
| } |
| |
| const char *getVS2DArray() const |
| { |
| return R"(#version 300 es |
| out vec2 texcoord; |
| in vec4 position; |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| } |
| |
| const char *getVS3D() const |
| { |
| return R"(#version 300 es |
| out vec2 texcoord; |
| in vec4 position; |
| |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| } |
| |
| const char *getVSCube() const |
| { |
| return R"(#version 300 es |
| in vec4 position; |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| })"; |
| } |
| |
| const char *getVSCubeArray() const |
| { |
| return R"(#version 310 es |
| in vec4 position; |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| })"; |
| } |
| |
| const char *getVSESSL3() const |
| { |
| return R"(#version 300 es |
| precision highp float; |
| in vec4 position; |
| out vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = position; |
| texcoord = (position.xy * 0.5) + 0.5; |
| texcoord.y = 1.0 - texcoord.y; |
| })"; |
| } |
| |
| const char *getTextureFS() const |
| { |
| return R"(precision highp float; |
| uniform sampler2D tex; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texcoord); |
| })"; |
| } |
| |
| const char *getTexture2DArrayFS() const |
| { |
| return R"(#version 300 es |
| precision highp float; |
| uniform highp sampler2DArray tex2DArray; |
| uniform uint layer; |
| in vec2 texcoord; |
| out vec4 fragColor; |
| void main() |
| { |
| fragColor = texture(tex2DArray, vec3(texcoord.x, texcoord.y, float(layer))); |
| })"; |
| } |
| |
| const char *getTexture3DFS() const |
| { |
| return R"(#version 300 es |
| precision highp float; |
| uniform highp sampler3D tex3D; |
| uniform uint layer; |
| in vec2 texcoord; |
| out vec4 fragColor; |
| void main() |
| { |
| fragColor = texture(tex3D, vec3(texcoord.x, texcoord.y, float(layer))); |
| })"; |
| } |
| |
| const char *getTextureCubeFS() const |
| { |
| return R"(#version 300 es |
| precision highp float; |
| uniform highp samplerCube texCube; |
| uniform vec3 faceCoord; |
| out vec4 fragColor; |
| void main() |
| { |
| fragColor = texture(texCube, faceCoord); |
| })"; |
| } |
| |
| const char *getTextureCubeArrayFS() const |
| { |
| return R"(#version 310 es |
| #extension GL_OES_texture_cube_map_array : require |
| precision highp float; |
| uniform highp samplerCubeArray texCubeArray; |
| uniform vec3 faceCoord; |
| uniform uint layer; |
| out vec4 fragColor; |
| void main() |
| { |
| fragColor = texture(texCubeArray, vec4(faceCoord, float(layer))); |
| })"; |
| } |
| |
| const char *getTextureExternalFS() const |
| { |
| return R"(#extension GL_OES_EGL_image_external : require |
| precision highp float; |
| uniform samplerExternalOES tex; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texcoord); |
| })"; |
| } |
| |
| const char *getTextureExternalESSL3FS() const |
| { |
| return R"(#version 300 es |
| #extension GL_OES_EGL_image_external_essl3 : require |
| precision highp float; |
| uniform samplerExternalOES tex; |
| in vec2 texcoord; |
| out vec4 color; |
| |
| void main() |
| { |
| color = texture(tex, texcoord); |
| })"; |
| } |
| |
| const char *getTextureYUVVS() const |
| { |
| return R"(#version 300 es |
| #extension GL_EXT_YUV_target : require |
| precision highp float; |
| uniform __samplerExternal2DY2YEXT tex; |
| in vec4 position; |
| out vec4 color; |
| |
| void main() |
| { |
| gl_Position = position; |
| vec2 texcoord = (position.xy * 0.5) + 0.5; |
| texcoord.y = 1.0 - texcoord.y; |
| color = texture(tex, texcoord); |
| })"; |
| } |
| |
| const char *getPassThroughFS() const |
| { |
| return R"(#version 300 es |
| precision highp float; |
| in vec4 color; |
| out vec4 frag_color; |
| |
| void main() |
| { |
| frag_color = color; |
| })"; |
| } |
| |
| const char *getTextureYUVFS() const |
| { |
| return R"(#version 300 es |
| #extension GL_EXT_YUV_target : require |
| precision highp float; |
| uniform __samplerExternal2DY2YEXT tex; |
| in vec2 texcoord; |
| out vec4 color; |
| |
| void main() |
| { |
| color = texture(tex, texcoord); |
| })"; |
| } |
| |
| const char *getRenderYUVFS() const |
| { |
| return R"(#version 300 es |
| #extension GL_EXT_YUV_target : require |
| precision highp float; |
| uniform vec4 u_color; |
| layout (yuv) out vec4 color; |
| |
| void main() |
| { |
| color = u_color; |
| })"; |
| } |
| |
| void testSetUp() override |
| { |
| mTextureProgram = CompileProgram(getVS(), getTextureFS()); |
| if (mTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); |
| |
| if (getClientMajorVersion() >= 3) |
| { |
| m2DArrayTextureProgram = CompileProgram(getVS2DArray(), getTexture2DArrayFS()); |
| if (m2DArrayTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| m2DArrayTextureUniformLocation = |
| glGetUniformLocation(m2DArrayTextureProgram, "tex2DArray"); |
| m2DArrayTextureLayerUniformLocation = |
| glGetUniformLocation(m2DArrayTextureProgram, "layer"); |
| } |
| |
| if (getClientMajorVersion() >= 3) |
| { |
| m3DTextureProgram = CompileProgram(getVS3D(), getTexture3DFS()); |
| if (m3DTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| m3DTextureUniformLocation = glGetUniformLocation(m3DTextureProgram, "tex3D"); |
| m3DTextureLayerUniformLocation = glGetUniformLocation(m3DTextureProgram, "layer"); |
| } |
| |
| if (IsGLExtensionEnabled("GL_OES_EGL_image_external")) |
| { |
| mTextureExternalProgram = CompileProgram(getVS(), getTextureExternalFS()); |
| ASSERT_NE(0u, mTextureExternalProgram) << "shader compilation failed."; |
| |
| mTextureExternalUniformLocation = glGetUniformLocation(mTextureExternalProgram, "tex"); |
| } |
| |
| if (IsGLExtensionEnabled("GL_OES_EGL_image_external_essl3")) |
| { |
| mTextureExternalESSL3Program = |
| CompileProgram(getVSESSL3(), getTextureExternalESSL3FS()); |
| ASSERT_NE(0u, mTextureExternalESSL3Program) << "shader compilation failed."; |
| |
| mTextureExternalESSL3UniformLocation = |
| glGetUniformLocation(mTextureExternalESSL3Program, "tex"); |
| } |
| |
| if (IsGLExtensionEnabled(kYUVTargetExt)) |
| { |
| mTextureYUVProgram = CompileProgram(getVSESSL3(), getTextureYUVFS()); |
| ASSERT_NE(0u, mTextureYUVProgram) << "shader compilation failed."; |
| |
| mTextureYUVUniformLocation = glGetUniformLocation(mTextureYUVProgram, "tex"); |
| |
| mTextureYUVVSProgram = CompileProgram(getTextureYUVVS(), getPassThroughFS()); |
| ASSERT_NE(0u, mTextureYUVVSProgram) << "shader compilation failed."; |
| |
| mTextureYUVVSUniformLocation = glGetUniformLocation(mTextureYUVVSProgram, "tex"); |
| |
| mRenderYUVProgram = CompileProgram(getVSESSL3(), getRenderYUVFS()); |
| ASSERT_NE(0u, mRenderYUVProgram) << "shader compilation failed."; |
| |
| mRenderYUVUniformLocation = glGetUniformLocation(mRenderYUVProgram, "u_color"); |
| } |
| |
| if (IsGLExtensionEnabled(kEGLImageStorageExt)) |
| { |
| mCubeTextureProgram = CompileProgram(getVSCube(), getTextureCubeFS()); |
| if (mCubeTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| mCubeTextureUniformLocation = glGetUniformLocation(mCubeTextureProgram, "texCube"); |
| mCubeTextureFaceCoordUniformLocation = |
| glGetUniformLocation(mCubeTextureProgram, "faceCoord"); |
| |
| if ((getClientMajorVersion() >= 3 && getClientMinorVersion() >= 1) && |
| IsGLExtensionEnabled("GL_EXT_texture_cube_map_array")) |
| { |
| mCubeArrayTextureProgram = |
| CompileProgram(getVSCubeArray(), getTextureCubeArrayFS()); |
| if (mCubeArrayTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| mCubeArrayTextureUniformLocation = |
| glGetUniformLocation(mCubeArrayTextureProgram, "texCubeArray"); |
| mCubeArrayTextureFaceCoordUniformLocation = |
| glGetUniformLocation(mCubeArrayTextureProgram, "faceCoord"); |
| mCubeArrayTextureLayerUniformLocation = |
| glGetUniformLocation(mCubeArrayTextureProgram, "layer"); |
| } |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mTextureProgram); |
| glDeleteProgram(mTextureExternalProgram); |
| glDeleteProgram(mTextureExternalESSL3Program); |
| glClearColor(0, 0, 0, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // 1) For tests that sample from EGLImages with colorspace override - |
| // 1) Always upload color values in sRGB colorspace |
| // 2) The image should be treated as if it was an sRGB image resulting in |
| // the sampled value to be to decoded to linear colorspace |
| // |
| // 2) For tests that render to EGLImages with colorspace override - |
| // 1) Always upload color values in linear colorspace |
| // 2) The image should be treated as if it was an sRGB image resulting in |
| // the rendered color to be encoded in sRGB colorspace |
| |
| enum class EglImageUsage |
| { |
| Sampling, |
| Rendering |
| }; |
| |
| bool attribListHasSrgbColorspace(const EGLint *attribs, |
| const int colorspaceAttributeIndex) const |
| { |
| return (attribs[colorspaceAttributeIndex] == EGL_GL_COLORSPACE && |
| attribs[colorspaceAttributeIndex + 1] == EGL_GL_COLORSPACE_SRGB_KHR); |
| } |
| |
| GLubyte *getExpected2DColorForAttribList(const EGLint *attribs, |
| EglImageUsage usage = EglImageUsage::Sampling) const |
| { |
| const bool srgbColorspace = attribListHasSrgbColorspace(attribs, kColorspaceAttributeIndex); |
| return (usage == EglImageUsage::Sampling) ? (srgbColorspace ? kLinearColor : kSrgbColor) |
| : (srgbColorspace ? kSrgbColor : kLinearColor); |
| } |
| |
| GLubyte *getExpected3DColorForAttribList(const EGLint *attribs, |
| EglImageUsage usage = EglImageUsage::Sampling) const |
| { |
| const bool srgbColorspace = |
| attribListHasSrgbColorspace(attribs, k3DColorspaceAttributeIndex); |
| return (usage == EglImageUsage::Sampling) |
| ? (srgbColorspace ? kLinearColor3D : kSrgbColor3D) |
| : (srgbColorspace ? kSrgbColor3D : kLinearColor3D); |
| } |
| |
| GLubyte *getExpectedCubeColorForAttribList(const EGLint *attribs, |
| EglImageUsage usage = EglImageUsage::Sampling) const |
| { |
| const bool srgbColorspace = attribListHasSrgbColorspace(attribs, kColorspaceAttributeIndex); |
| return (usage == EglImageUsage::Sampling) |
| ? (srgbColorspace ? kLinearColorCube : kSrgbColorCube) |
| : (srgbColorspace ? kSrgbColorCube : kLinearColorCube); |
| } |
| |
| void createEGLImage2DTextureStorage(size_t width, |
| size_t height, |
| GLenum format, |
| const GLint *attribs, |
| GLTexture &sourceTexture, |
| EGLImageKHR *outSourceImage) |
| { |
| |
| glBindTexture(GL_TEXTURE_2D, sourceTexture); |
| glTexStorageAttribs2DEXT(GL_TEXTURE_2D, 1, format, static_cast<GLsizei>(width), |
| static_cast<GLsizei>(height), attribs); |
| |
| ASSERT_GL_NO_ERROR(); |
| // 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>(sourceTexture), nullptr); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| *outSourceImage = image; |
| } |
| |
| void createEGLImage2DTextureSource(size_t width, |
| size_t height, |
| GLenum format, |
| GLenum type, |
| const EGLint *attribs, |
| void *data, |
| GLTexture &sourceTexture, |
| EGLImageKHR *outSourceImage) |
| { |
| // Create a source 2D texture |
| glBindTexture(GL_TEXTURE_2D, sourceTexture); |
| |
| 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>(sourceTexture), attribs); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| *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, |
| GLTexture &sourceTexture, |
| EGLImageKHR *outSourceImage) |
| { |
| // Create a source cube map texture |
| glBindTexture(GL_TEXTURE_CUBE_MAP, sourceTexture); |
| |
| 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>(sourceTexture), attribs); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| *outSourceImage = image; |
| } |
| |
| void createEGLImage3DTextureSource(size_t width, |
| size_t height, |
| size_t depth, |
| GLenum format, |
| GLenum type, |
| const EGLint *attribs, |
| void *data, |
| GLTexture &sourceTexture, |
| EGLImageKHR *outSourceImage) |
| { |
| // Create a source 3D texture |
| glBindTexture(GL_TEXTURE_3D, sourceTexture); |
| |
| 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>(sourceTexture), attribs); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| *outSourceImage = image; |
| } |
| |
| void createEGLImageRenderbufferSource(size_t width, |
| size_t height, |
| GLenum internalFormat, |
| const EGLint *attribs, |
| GLRenderbuffer &sourceRenderbuffer, |
| EGLImageKHR *outSourceImage) |
| { |
| // Create a source renderbuffer |
| glBindRenderbuffer(GL_RENDERBUFFER, sourceRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, static_cast<GLsizei>(width), |
| static_cast<GLsizei>(height)); |
| |
| // Create an image from the source renderbuffer |
| EGLWindow *window = getEGLWindow(); |
| |
| EGLImageKHR image = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_RENDERBUFFER_KHR, |
| reinterpretHelper<EGLClientBuffer>(sourceRenderbuffer), attribs); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| *outSourceImage = image; |
| } |
| |
| void createEGLImageTargetTexture2D(EGLImageKHR image, GLTexture &targetTexture) |
| { |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| 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(); |
| } |
| |
| void createEGLImageTargetTexture2DArray(EGLImageKHR image, GLTexture &targetTexture) |
| { |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_2D_ARRAY, targetTexture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image); |
| |
| // Disable mipmapping |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void createEGLImageTargetTextureExternal(EGLImageKHR image, GLuint targetTexture) |
| { |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, targetTexture); |
| 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(); |
| } |
| |
| void createEGLImageTargetTextureStorage(EGLImageKHR image, |
| GLenum targetType, |
| GLuint targetTexture, |
| const GLint *attribs) |
| { |
| // Create a target texture from the image |
| glBindTexture(targetType, targetTexture); |
| glEGLImageTargetTexStorageEXT(targetType, image, attribs); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Disable mipmapping |
| glTexParameteri(targetType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(targetType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| size_t getLayerPitch(size_t height, size_t rowStride) |
| { |
| // Undocumented alignment of layer stride. This is potentially platform dependent, but |
| // allows functionality to be tested. |
| constexpr size_t kLayerAlignment = 4096; |
| |
| const size_t layerSize = height * rowStride; |
| return (layerSize + kLayerAlignment - 1) & ~(kLayerAlignment - 1); |
| } |
| |
| struct AHBPlaneData |
| { |
| const GLubyte *data; |
| size_t bytesPerPixel; |
| }; |
| |
| bool writeAHBData(AHardwareBuffer *aHardwareBuffer, |
| size_t width, |
| size_t height, |
| size_t depth, |
| bool isYUV, |
| const std::vector<AHBPlaneData> &data) |
| { |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| ASSERT(!data.empty()); |
| # if defined(ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT) |
| AHardwareBuffer_Planes planeInfo; |
| int res = AHardwareBuffer_lockPlanes( |
| aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, nullptr, &planeInfo); |
| if (res != 0) |
| { |
| WARN() << "AHardwareBuffer_lockPlanes failed"; |
| return false; |
| } |
| |
| 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; |
| size_t layerPitch = getLayerPitch(planeHeight, plane.rowStride); |
| |
| for (size_t z = 0; z < depth; z++) |
| { |
| const uint8_t *srcDepthSlice = |
| reinterpret_cast<const uint8_t *>(planeData.data) + |
| z * planeHeight * planeWidth * planeData.bytesPerPixel; |
| |
| for (size_t y = 0; y < planeHeight; y++) |
| { |
| const uint8_t *srcRow = |
| srcDepthSlice + y * planeWidth * planeData.bytesPerPixel; |
| |
| for (size_t x = 0; x < planeWidth; x++) |
| { |
| const uint8_t *src = srcRow + x * planeData.bytesPerPixel; |
| uint8_t *dst = reinterpret_cast<uint8_t *>(plane.data) + z * layerPitch + |
| 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 size_t stride = aHardwareBufferDescription.stride * data[0].bytesPerPixel; |
| size_t layerPitch = getLayerPitch(height, stride); |
| |
| uint32_t rowSize = stride * height; |
| for (size_t z = 0; z < depth; z++) |
| { |
| for (uint32_t y = 0; y < height; y++) |
| { |
| size_t dstPtrOffset = z * layerPitch + y * stride; |
| size_t srcPtrOffset = (z * height + y) * width * data[0].bytesPerPixel; |
| |
| uint8_t *dst = reinterpret_cast<uint8_t *>(mappedMemory) + dstPtrOffset; |
| memcpy(dst, data[0].data + srcPtrOffset, rowSize); |
| } |
| } |
| |
| res = AHardwareBuffer_unlock(aHardwareBuffer, nullptr); |
| EXPECT_EQ(res, 0); |
| # endif |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| enum AHBUsage |
| { |
| kAHBUsageGPUSampledImage = 1 << 0, |
| kAHBUsageGPUFramebuffer = 1 << 1, |
| kAHBUsageGPUCubeMap = 1 << 2, |
| kAHBUsageGPUMipMapComplete = 1 << 3, |
| kAHBUsageFrontBuffer = 1 << 4, |
| }; |
| |
| constexpr static uint32_t kDefaultAHBUsage = kAHBUsageGPUSampledImage | kAHBUsageGPUFramebuffer; |
| constexpr static uint32_t kDefaultAHBYUVUsage = kAHBUsageGPUSampledImage; |
| |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| AHardwareBuffer_Desc createAndroidHardwareBufferDesc(size_t width, |
| size_t height, |
| size_t depth, |
| int androidFormat, |
| uint32_t usage) |
| { |
| // 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; |
| if ((usage & kAHBUsageGPUSampledImage) != 0) |
| { |
| aHardwareBufferDescription.usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; |
| } |
| if ((usage & kAHBUsageGPUFramebuffer) != 0) |
| { |
| aHardwareBufferDescription.usage |= AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; |
| } |
| if ((usage & kAHBUsageGPUCubeMap) != 0) |
| { |
| aHardwareBufferDescription.usage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; |
| } |
| if ((usage & kAHBUsageGPUMipMapComplete) != 0) |
| { |
| aHardwareBufferDescription.usage |= AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE; |
| } |
| if ((usage & kAHBUsageFrontBuffer) != 0) |
| { |
| aHardwareBufferDescription.usage |= ANGLE_AHARDWAREBUFFER_USAGE_FRONT_BUFFER; |
| } |
| aHardwareBufferDescription.stride = 0; |
| aHardwareBufferDescription.rfu0 = 0; |
| aHardwareBufferDescription.rfu1 = 0; |
| return aHardwareBufferDescription; |
| } |
| #endif |
| |
| bool isAndroidHardwareBufferConfigurationSupported(size_t width, |
| size_t height, |
| size_t depth, |
| int androidFormat, |
| uint32_t usage) |
| { |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| const AHardwareBuffer_Desc aHardwareBufferDescription = |
| createAndroidHardwareBufferDesc(width, height, depth, androidFormat, usage); |
| return AHardwareBuffer_isSupported(&aHardwareBufferDescription); |
| #else |
| return false; |
| #endif |
| } |
| |
| AHardwareBuffer *createAndroidHardwareBuffer(size_t width, |
| size_t height, |
| size_t depth, |
| int androidFormat, |
| uint32_t usage, |
| const std::vector<AHBPlaneData> &data) |
| { |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| const AHardwareBuffer_Desc aHardwareBufferDescription = |
| createAndroidHardwareBufferDesc(width, height, depth, androidFormat, usage); |
| |
| // Allocate memory from Android Hardware Buffer |
| AHardwareBuffer *aHardwareBuffer = nullptr; |
| EXPECT_EQ(0, AHardwareBuffer_allocate(&aHardwareBufferDescription, &aHardwareBuffer)); |
| |
| if (!data.empty()) |
| { |
| const bool isYUV = androidFormat == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 || |
| androidFormat == AHARDWAREBUFFER_FORMAT_YV12; |
| writeAHBData(aHardwareBuffer, width, height, depth, isYUV, 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, |
| uint32_t usage, |
| const EGLint *attribs, |
| const std::vector<AHBPlaneData> &data, |
| AHardwareBuffer **outSourceAHB, |
| EGLImageKHR *outSourceImage) |
| { |
| // Set Android Memory |
| AHardwareBuffer *aHardwareBuffer = |
| createAndroidHardwareBuffer(width, height, depth, androidPixelFormat, usage, 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()) |
| { |
| bool success = writeAHBData(pAHardwareBuffer, width, height, depth, false, data); |
| if (!success) |
| { |
| return; |
| } |
| } |
| #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 targetRenderbuffer) |
| { |
| // Create a target texture from the image |
| glBindRenderbuffer(GL_RENDERBUFFER, targetRenderbuffer); |
| glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void ValidationGLEGLImage_helper(const EGLint *attribs); |
| void SourceAHBTarget2D_helper(const EGLint *attribs); |
| void SourceAHBTarget2DImageStorageGenerateMipmap_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(EGLint *attribs); |
| void Source3DTargetRenderbuffer_helper(EGLint *attribs); |
| void Source3DTargetExternal_helper(EGLint *attribs); |
| void Source3DTargetExternalESSL3_helper(EGLint *attribs); |
| void SourceRenderbufferTargetTexture_helper(const EGLint *attribs); |
| void SourceRenderbufferTargetTextureExternal_helper(const EGLint *attribs); |
| void SourceRenderbufferTargetRenderbuffer_helper(const EGLint *attribs); |
| void FixedRatedCompressionBasicHelper(const GLint *attribs); |
| void FixedRatedCompressionImageAttribCheck(EGLImageKHR image, |
| const GLint *attribs, |
| const GLint expectResult); |
| void SourceRenderbufferTargetTextureExternalESSL3_helper(const EGLint *attribs); |
| void ImageStorageGenerateMipmap_helper(const EGLint *attribs, |
| const GLsizei width, |
| const GLsizei height, |
| AHardwareBuffer *srcAhb, |
| GLuint srcTexture, |
| EGLImageKHR *imageOut); |
| void ImageCheckingTextureAccessHelper(GLenum target, bool mipmap); |
| void verifyImageStorageMipmap(const EGLint *attribs, |
| EGLImageKHR image, |
| const GLsizei mipLevelCount); |
| void verifyImageStorageMipmapWithBlend(const EGLint *attribs, |
| EGLImageKHR image, |
| const GLsizei mipLevelCount); |
| void verifyResultsTexture(GLuint texture, |
| const 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 2 |
| EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2], |
| referenceColor[3], 2); |
| } |
| |
| void verifyResultsTextureLeftAndRight(GLuint texture, |
| const GLubyte leftColor[4], |
| const GLubyte rightColor[4], |
| GLenum textureTarget, |
| GLuint program, |
| GLuint textureUniform) |
| { |
| verifyResultsTexture(texture, leftColor, textureTarget, program, textureUniform); |
| |
| // verifyResultsTexture only verifies top-left. Here also verifies top-right. |
| EXPECT_PIXEL_NEAR(getWindowWidth() - 1, 0, rightColor[0], rightColor[1], rightColor[2], |
| rightColor[3], 1); |
| } |
| |
| void verifyResults2D(GLuint texture, const GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_2D, mTextureProgram, |
| mTextureUniformLocation); |
| } |
| |
| void verifyResults3D(GLuint texture, const GLubyte data[4], uint32_t layerIndex = 0) |
| { |
| glUseProgram(m3DTextureProgram); |
| glUniform1ui(m3DTextureLayerUniformLocation, layerIndex); |
| |
| verifyResultsTexture(texture, data, GL_TEXTURE_3D, m3DTextureProgram, |
| m3DTextureUniformLocation); |
| } |
| |
| void verifyResults2DLeftAndRight(GLuint texture, const GLubyte left[4], const GLubyte right[4]) |
| { |
| verifyResultsTextureLeftAndRight(texture, left, right, GL_TEXTURE_2D, mTextureProgram, |
| mTextureUniformLocation); |
| } |
| |
| void verifyResults2DArray(GLuint texture, const GLubyte data[4], uint32_t layerIndex = 0) |
| { |
| glUseProgram(m2DArrayTextureProgram); |
| glUniform1ui(m2DArrayTextureLayerUniformLocation, layerIndex); |
| |
| verifyResultsTexture(texture, data, GL_TEXTURE_2D_ARRAY, m2DArrayTextureProgram, |
| m2DArrayTextureUniformLocation); |
| } |
| |
| void verifyResultsCube(GLuint texture, const GLubyte data[4], uint32_t faceIndex = 0) |
| { |
| glUseProgram(mCubeTextureProgram); |
| glUniform3f(mCubeTextureFaceCoordUniformLocation, kCubeFaceX[faceIndex], |
| kCubeFaceY[faceIndex], kCubeFaceZ[faceIndex]); |
| |
| verifyResultsTexture(texture, data, GL_TEXTURE_CUBE_MAP, mCubeTextureProgram, |
| mCubeTextureUniformLocation); |
| } |
| |
| void verifyResultsCubeArray(GLuint texture, |
| const GLubyte data[4], |
| uint32_t faceIndex = 0, |
| uint32_t layerIndex = 0) |
| { |
| glUseProgram(mCubeArrayTextureProgram); |
| glUniform1ui(mCubeArrayTextureLayerUniformLocation, layerIndex); |
| glUniform3f(mCubeArrayTextureFaceCoordUniformLocation, kCubeFaceX[faceIndex], |
| kCubeFaceY[faceIndex], kCubeFaceZ[faceIndex]); |
| |
| verifyResultsTexture(texture, data, GL_TEXTURE_CUBE_MAP_ARRAY, mCubeArrayTextureProgram, |
| mCubeArrayTextureUniformLocation); |
| } |
| |
| void verifyResultsExternal(GLuint texture, const GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureExternalProgram, |
| mTextureExternalUniformLocation); |
| } |
| |
| void verifyResultsExternalESSL3(GLuint texture, const GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureExternalESSL3Program, |
| mTextureExternalESSL3UniformLocation); |
| } |
| |
| void verifyResultsExternalYUV(GLuint texture, const GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureYUVProgram, |
| mTextureYUVUniformLocation); |
| } |
| |
| void verifyResultsExternalYUVVS(GLuint texture, const GLubyte data[4]) |
| { |
| verifyResultsTexture(texture, data, GL_TEXTURE_EXTERNAL_OES, mTextureYUVVSProgram, |
| mTextureYUVVSUniformLocation); |
| } |
| |
| void verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte referenceColor[4]) |
| { |
| // Bind the renderbuffer to a framebuffer |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| renderbuffer); |
| |
| // Expect renderbuffer to match referenceColor with a tolerance of 1. |
| EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2], |
| referenceColor[3], 1); |
| } |
| |
| void verifyResultsRenderbufferWithClearAndDraw(GLuint texture, |
| GLuint renderbuffer, |
| GLubyte clearColor[4], |
| GLubyte referenceColor[4]) |
| { |
| // Bind the renderbuffer to a framebuffer |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| renderbuffer); |
| |
| // Clear the renderbuffer with the clear color |
| glClearColor(clearColor[0] / 255.0f, clearColor[1] / 255.0f, clearColor[2] / 255.0f, |
| clearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Expect renderbuffer to match referenceColor with a tolerance of 1. |
| EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2], |
| referenceColor[3], 1); |
| |
| // Sample source texture and draw onto renderbuffer and expect rendered quad's color |
| // is the same as the reference color with a tolerance of 1 |
| verifyResultsTexture(texture, referenceColor, GL_TEXTURE_2D, mTextureProgram, |
| mTextureUniformLocation); |
| } |
| |
| enum class AHBVerifyRegion |
| { |
| Entire, |
| LeftHalf, |
| RightHalf, |
| }; |
| |
| void verifyResultAHB(AHardwareBuffer *source, |
| const std::vector<AHBPlaneData> &data, |
| AHBVerifyRegion verifyRegion = AHBVerifyRegion::Entire) |
| { |
| #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; |
| const uint32_t depth = aHardwareBufferDescription.layers; |
| |
| # 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]; |
| |
| const size_t planeHeight = (isYUV && planeIdx > 0) ? (height / 2) : height; |
| const size_t planeWidth = (isYUV && planeIdx > 0) ? (width / 2) : width; |
| size_t layerPitch = getLayerPitch(planeHeight, plane.rowStride); |
| |
| uint32_t xStart = 0; |
| uint32_t xEnd = planeWidth; |
| |
| switch (verifyRegion) |
| { |
| case AHBVerifyRegion::Entire: |
| break; |
| case AHBVerifyRegion::LeftHalf: |
| xEnd = planeWidth / 2; |
| break; |
| case AHBVerifyRegion::RightHalf: |
| xStart = planeWidth / 2; |
| break; |
| } |
| |
| for (size_t z = 0; z < depth; z++) |
| { |
| const uint8_t *referenceDepthSlice = |
| reinterpret_cast<const uint8_t *>(planeData.data) + |
| z * planeHeight * (xEnd - xStart) * planeData.bytesPerPixel; |
| for (size_t y = 0; y < planeHeight; y++) |
| { |
| const uint8_t *referenceRow = |
| referenceDepthSlice + y * (xEnd - xStart) * planeData.bytesPerPixel; |
| for (size_t x = xStart; x < xEnd; x++) |
| { |
| const uint8_t *referenceData = |
| referenceRow + (x - xStart) * planeData.bytesPerPixel; |
| std::vector<uint8_t> reference(referenceData, |
| referenceData + planeData.bytesPerPixel); |
| |
| const uint8_t *ahbData = reinterpret_cast<uint8_t *>(plane.data) + |
| z * layerPitch + 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 * data[0].bytesPerPixel; |
| size_t layerPitch = getLayerPitch(height, rowStride); |
| |
| void *mappedMemory = nullptr; |
| ASSERT_EQ(0, AHardwareBuffer_lock(source, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, |
| nullptr, &mappedMemory)); |
| |
| uint32_t xStart = 0; |
| uint32_t xEnd = width; |
| |
| switch (verifyRegion) |
| { |
| case AHBVerifyRegion::Entire: |
| break; |
| case AHBVerifyRegion::LeftHalf: |
| xEnd = width / 2; |
| break; |
| case AHBVerifyRegion::RightHalf: |
| xStart = width / 2; |
| break; |
| } |
| for (size_t z = 0; z < depth; z++) |
| { |
| const uint8_t *referenceDepthSlice = |
| reinterpret_cast<const uint8_t *>(data[0].data) + |
| z * height * (xEnd - xStart) * data[0].bytesPerPixel; |
| for (size_t y = 0; y < height; y++) |
| { |
| const uint8_t *referenceRow = |
| referenceDepthSlice + y * (xEnd - xStart) * data[0].bytesPerPixel; |
| for (size_t x = xStart; x < xEnd; x++) |
| { |
| const uint8_t *referenceData = |
| referenceRow + (x - xStart) * data[0].bytesPerPixel; |
| std::vector<uint8_t> reference(referenceData, |
| referenceData + data[0].bytesPerPixel); |
| |
| const uint8_t *ahbData = reinterpret_cast<uint8_t *>(mappedMemory) + |
| z * layerPitch + 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(const 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.get()); |
| return reinterpret_cast<destType>(sourceSizeT); |
| } |
| |
| bool hasImageGLColorspaceExt() const |
| { |
| // Possible GLES driver bug on Pixel2 devices: http://anglebug.com/42263865 |
| if (IsPixel2() && IsOpenGLES()) |
| { |
| return false; |
| } |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kImageGLColorspaceExt); |
| } |
| |
| bool hasAndroidImageNativeBufferExt() const |
| { |
| return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), |
| kEGLAndroidImageNativeBufferExt); |
| } |
| |
| bool hasEglImageStorageExt() const { return IsGLExtensionEnabled(kEGLImageStorageExt); } |
| |
| bool hasEglImageStorageCompressionExt() const |
| { |
| return IsGLExtensionEnabled(kEGLImageStorageCompressionExt); |
| } |
| |
| bool hasTextureStorageCompressionExt() const |
| { |
| return IsGLExtensionEnabled(kTextureStorageCompressionExt); |
| } |
| |
| bool hasAndroidHardwareBufferSupport() const |
| { |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| bool hasAhbLockPlanesSupport() const |
| { |
| #if defined(ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_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 hasYUVInternalFormatExt() const { return IsGLExtensionEnabled(kYUVInternalFormatExt); } |
| |
| bool hasYUVTargetExt() const { return IsGLExtensionEnabled(kYUVTargetExt); } |
| |
| bool hasRGBXInternalFormatExt() const { return IsGLExtensionEnabled(kRGBXInternalFormatExt); } |
| |
| 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); |
| } |
| |
| angle::VulkanPerfCounters getPerfCounters() |
| { |
| ASSERT(IsVulkan()); |
| |
| if (mCounterNameToIndexMap.empty()) |
| { |
| mCounterNameToIndexMap = BuildCounterNameToIndexMap(); |
| } |
| |
| return GetPerfCounters(mCounterNameToIndexMap); |
| } |
| |
| void externalTextureTracerTestHelper(const EGLint *attribsToRecoverInMEC) |
| { |
| const EGLWindow *eglWindow = getEGLWindow(); |
| // Frame 1 begins |
| // Create the Image |
| GLTexture sourceTexture1; |
| EGLImageKHR image1; |
| |
| GLubyte data[] = {132, 55, 219, 255}; |
| // Create a source 2D texture |
| glBindTexture(GL_TEXTURE_2D, sourceTexture1); |
| |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(1), static_cast<GLsizei>(1), 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, 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(); |
| |
| image1 = eglCreateImageKHR( |
| eglWindow->getDisplay(), eglWindow->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(sourceTexture1), attribsToRecoverInMEC); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create the target |
| GLTexture targetTexture1; |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, targetTexture1); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image1); |
| |
| // 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(); |
| |
| // Calls On EndFrame(), with MidExecutionSetup to restore external texture targetTexture1 |
| // above |
| EGLDisplay display = eglWindow->getDisplay(); |
| EGLSurface surface = eglWindow->getSurface(); |
| eglSwapBuffers(display, surface); |
| // Frame 1 ends |
| |
| // Frame 2 begins |
| // Create another eglImage with another associated texture |
| // Draw using the eglImage texture targetTexture1 created in frame 1 |
| GLTexture sourceTexture2; |
| EGLImageKHR image2; |
| |
| // Create a source 2D texture |
| glBindTexture(GL_TEXTURE_2D, sourceTexture2); |
| |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(1), static_cast<GLsizei>(1), 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, 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(); |
| |
| constexpr EGLint defaultAttribs[] = { |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| image2 = eglCreateImageKHR( |
| eglWindow->getDisplay(), eglWindow->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(sourceTexture2), defaultAttribs); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create the target |
| GLTexture targetTexture2; |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, targetTexture2); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image2); |
| |
| // 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(); |
| glUseProgram(mTextureExternalProgram); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, targetTexture1); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| |
| // Calls On EndFrame() to save the gl calls creating external texture targetTexture2; |
| // We use this as a reference to check the gl calls we restore for targetTexture1 |
| // in MidExecutionSetup |
| eglSwapBuffers(display, surface); |
| // Frame 2 ends |
| |
| // Frame 3 begins |
| // Draw a quad with the targetTexture2 |
| glUseProgram(mTextureExternalProgram); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, targetTexture2); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| |
| eglSwapBuffers(display, surface); |
| // Frame 3 ends |
| |
| // Clean up |
| eglDestroyImageKHR(eglWindow->getDisplay(), image1); |
| eglDestroyImageKHR(eglWindow->getDisplay(), image2); |
| } |
| |
| void framebufferAttachmentDeletedWhileInUseHelper(bool useTextureAttachment, |
| bool deleteSourceTextureLast); |
| void framebufferResolveAttachmentDeletedWhileInUseHelper(bool useTextureAttachment, |
| bool deleteSourceTextureLast); |
| void useAHBByGLThenForeignThenGLHelper( |
| std::function<void(const GLTexture &, uint32_t, uint32_t)> firstUse, |
| std::function<void(const GLTexture &, uint32_t, uint32_t)> secondUse); |
| |
| 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; |
| GLuint m3DTextureProgram; |
| GLuint mCubeTextureProgram; |
| GLuint mCubeArrayTextureProgram; |
| GLint mTextureUniformLocation; |
| GLuint m2DArrayTextureUniformLocation; |
| GLuint m2DArrayTextureLayerUniformLocation; |
| GLuint m3DTextureUniformLocation; |
| GLuint m3DTextureLayerUniformLocation; |
| GLuint mCubeTextureUniformLocation; |
| GLuint mCubeTextureFaceCoordUniformLocation; |
| GLuint mCubeArrayTextureUniformLocation; |
| GLuint mCubeArrayTextureFaceCoordUniformLocation; |
| GLuint mCubeArrayTextureLayerUniformLocation; |
| |
| GLuint mTextureExternalProgram = 0; |
| GLint mTextureExternalUniformLocation = -1; |
| |
| GLuint mTextureExternalESSL3Program = 0; |
| GLint mTextureExternalESSL3UniformLocation = -1; |
| |
| GLuint mTextureYUVProgram = 0; |
| GLint mTextureYUVUniformLocation = -1; |
| |
| GLuint mTextureYUVVSProgram = 0; |
| GLint mTextureYUVVSUniformLocation = -1; |
| |
| GLuint mRenderYUVProgram = 0; |
| GLint mRenderYUVUniformLocation = -1; |
| |
| CounterNameToIndexMap mCounterNameToIndexMap; |
| }; |
| |
| class ImageTestES3 : public ImageTest |
| {}; |
| |
| class ImageTestES31 : 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()); |
| EXPECT_FALSE(has3DTextureExt()); |
| |
| 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()); |
| } |
| |
| // Check validation from the EGL_KHR_image_base extension |
| TEST_P(ImageTest, ValidationImageBase) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| GLTexture 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(reinterpret_cast<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, reinterpret_cast<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(reinterpret_cast<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, reinterpret_cast<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(); |
| |
| 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. |
| GLTexture 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. |
| GLTexture 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 |
| { |
| GLTexture 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. |
| GLTexture 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 |
| { |
| GLTexture 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. |
| GLTexture 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()) |
| { |
| GLTexture 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) |
| { |
| GLTexture 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")) |
| { |
| GLRenderbuffer 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 |
| { |
| GLRenderbuffer 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 |
| GLTexture 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. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, reinterpret_cast<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 a renderbuffer is not bound, the error INVALID_OPERATION is generated. |
| // (Not in specification.) |
| glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // 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. |
| GLRenderbuffer renderbuffer; |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); |
| glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, |
| reinterpret_cast<GLeglImageOES>(0xBAADF00D)); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // Clean up |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), image); |
| } |
| |
| // Check validation from the GL_OES_EGL_image_external extension |
| TEST_P(ImageTest, ValidationGLEGLImageExternal) |
| { |
| ANGLE_SKIP_TEST_IF(!hasExternalExt()); |
| |
| GLTexture 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); |
| } |
| |
| // 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); |
| |
| GLTexture 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(); |
| } |
| |
| // Check validation from the GL_EXT_EGL_image_storage extension |
| TEST_P(ImageTest, ValidationGLEGLImageStorage) |
| { |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| |
| // Make sure this extension is not exposed without ES3. |
| ASSERT_GE(getClientMajorVersion(), 3); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| // Create the Image |
| GLTexture source2D; |
| EGLImageKHR image2D; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, kLinearColor, |
| source2D, &image2D); |
| |
| // <target> must be one of GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D, |
| // GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY. On OpenGL implementations |
| // (non-ES), <target> can also be GL_TEXTURE_1D or GL_TEXTURE_1D_ARRAY. |
| // If the implementation supports OES_EGL_image_external, <target> can be |
| // GL_TEXTURE_EXTERNAL_OES |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image2D, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| // If <image> is NULL, the error INVALID_VALUE is generated. If <image> is |
| // neither NULL nor a valid value, the behavior is undefined, up to and |
| // including program termination. |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, nullptr, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // If the GL is unable to specify a texture object using the supplied |
| // eglImageOES <image> the error INVALID_OPERATION is generated. |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_3D, image2D, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| GLint nonNullAttrib[1] = {GL_TEXTURE_2D}; |
| |
| // If <attrib_list> is neither NULL nor a pointer to the value GL_NONE, the |
| // error INVALID_VALUE is generated. |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, image2D, nonNullAttrib); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // Clean up |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), image2D); |
| } |
| |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, |
| static_cast<void *>(kSrgbColor), source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResults2D(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| void ImageTest::ImageStorageGenerateMipmap_helper(const EGLint *attribs, |
| const GLsizei width, |
| const GLsizei height, |
| AHardwareBuffer *srcAhb, |
| GLuint srcTexture, |
| EGLImageKHR *imageOut) |
| { |
| ASSERT(srcAhb != nullptr || glIsTexture(srcTexture)); |
| |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| |
| constexpr int kNumTiles = 8; |
| const int tileWidth = width / kNumTiles; |
| const int tileHeight = height / kNumTiles; |
| |
| // Create EGLImage and then a target texture from that image |
| EGLWindow *window = getEGLWindow(); |
| if (srcAhb != nullptr) |
| { |
| *imageOut = |
| eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, |
| angle::android::AHardwareBufferToClientBuffer(srcAhb), attribs); |
| } |
| else |
| { |
| *imageOut = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpret_cast<EGLClientBuffer>(srcTexture), attribs); |
| } |
| ASSERT_EGL_SUCCESS(); |
| |
| GLTexture dstTexture; |
| glBindTexture(GL_TEXTURE_2D, dstTexture); |
| // Setup for mipmapping |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, *imageOut, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create framebuffer, attach level 0 of target texture and render pattern |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| glEnable(GL_SCISSOR_TEST); |
| for (int i = 0; i < kNumTiles; ++i) |
| { |
| for (int j = 0; j < kNumTiles; ++j) |
| { |
| const float v = (i & 1) ^ (j & 1) ? 0.5f : 0.f; |
| glClearColor(v, 0.f, v, v); |
| glScissor(i * tileWidth, j * tileHeight, tileWidth, tileHeight); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| } |
| glDisable(GL_SCISSOR_TEST); |
| |
| // Generate mipmap for target texture |
| glGenerateMipmap(GL_TEXTURE_2D); |
| } |
| |
| void ImageTest::verifyImageStorageMipmap(const EGLint *attribs, |
| EGLImageKHR image, |
| const GLsizei mipLevelCount) |
| { |
| if (image == EGL_NO_IMAGE_KHR) |
| { |
| // Early return if image isn't valid |
| return; |
| } |
| |
| GLubyte linearColor[] = {64, 0, 64, 64}; |
| GLubyte srgbColor[] = {137, 0, 137, 64}; |
| GLubyte *expectedColor = |
| attribListHasSrgbColorspace(attribs, kColorspaceAttributeIndex) ? srgbColor : linearColor; |
| |
| GLTexture targetTexture; |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| // Setup for mipmapping |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, image, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create target framebuffer, attach "(mipLevelCount - 1)" level of target texture and verify |
| // data |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, |
| mipLevelCount - 1); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| EXPECT_PIXEL_NEAR(0, 0, expectedColor[0], expectedColor[1], expectedColor[2], expectedColor[3], |
| 1); |
| |
| // Verify that the target texture generates linear color when sampled |
| glActiveTexture(GL_TEXTURE0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, mipLevelCount - 1); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipLevelCount - 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| verifyResults2D(targetTexture, linearColor); |
| } |
| |
| void ImageTest::verifyImageStorageMipmapWithBlend(const EGLint *attribs, |
| EGLImageKHR image, |
| const GLsizei mipLevelCount) |
| { |
| if (image == EGL_NO_IMAGE_KHR) |
| { |
| // Early return if image isn't valid |
| return; |
| } |
| |
| // Need to have at least miplevel 1 |
| ASSERT(mipLevelCount >= 1); |
| // Verification used by only those tests with colorspace overrides |
| ASSERT(attribListHasSrgbColorspace(attribs, kColorspaceAttributeIndex)); |
| |
| GLTexture targetTexture; |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| // Setup for mipmapping |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, image, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create target framebuffer, attach mipLevel == 1 of target texture and verify |
| // data with blending enabled. |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 1); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Blend green color with contents of mipLevel 1 |
| // source color at (7, 11) of mipLevel 1 = [137, 0, 137, 64] |
| GLubyte blendedColor[] = {137, 255, 137, 255}; |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| glUseProgram(program); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_NEAR(7, 11, blendedColor[0], blendedColor[1], blendedColor[2], blendedColor[3], 1); |
| } |
| |
| void ImageTest::SourceAHBTarget2DImageStorageGenerateMipmap_helper(const EGLint *attribs) |
| { |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| constexpr GLsizei kWidth = 40; |
| constexpr GLsizei kHeight = 32; |
| constexpr GLsizei kDepth = 1; |
| const GLsizei mipLevelCount = static_cast<GLsizei>(std::log2(std::max(kWidth, kHeight)) + 1); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| kWidth, kHeight, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete)); |
| |
| // Create source AHB |
| AHardwareBuffer *aHardwareBuffer = |
| createAndroidHardwareBuffer(kWidth, kHeight, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete, {}); |
| EXPECT_NE(aHardwareBuffer, nullptr); |
| |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; |
| ImageStorageGenerateMipmap_helper(attribs, kWidth, kHeight, aHardwareBuffer, 0, &image); |
| verifyImageStorageMipmap(attribs, image, mipLevelCount); |
| |
| // Clean up image |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), image); |
| |
| // Clean up AHB |
| destroyAndroidHardwareBuffer(aHardwareBuffer); |
| } |
| |
| // Test interaction between AHB, GL_EXT_EGL_image_storage and glGenerateMipmap |
| TEST_P(ImageTestES3, SourceAHBTarget2DGenerateMipmap) |
| { |
| SourceAHBTarget2DImageStorageGenerateMipmap_helper(kDefaultAttribs); |
| } |
| |
| // Test interaction between AHB, GL_EXT_EGL_image_storage and glGenerateMipmap with colorspace |
| // overrides This mirrors the SingleLayer_ColorTest_MipmapComplete_R8G8B8A8_UNORM_sRGB Android CTS |
| // test |
| TEST_P(ImageTestES3, SourceAHBTarget2DGenerateMipmap_Colorspace) |
| { |
| SourceAHBTarget2DImageStorageGenerateMipmap_helper(kColorspaceAttribs); |
| } |
| |
| // Test to ensure that Vulkan backend's LOAD_OP is correct for non-0 miplevels. A bug in |
| // content tracking of mip levels will cause rendering artifacts and result in test failure. |
| TEST_P(ImageTestES3, SourceAHBTarget2DGenerateMipmapColorspaceBlend) |
| { |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| constexpr GLsizei kWidth = 40; |
| constexpr GLsizei kHeight = 32; |
| constexpr GLsizei kDepth = 1; |
| const GLsizei mipLevelCount = static_cast<GLsizei>(std::log2(std::max(kWidth, kHeight)) + 1); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| kWidth, kHeight, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete)); |
| |
| // Create source AHB |
| AHardwareBuffer *aHardwareBuffer = |
| createAndroidHardwareBuffer(kWidth, kHeight, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete, {}); |
| EXPECT_NE(aHardwareBuffer, nullptr); |
| |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; |
| ImageStorageGenerateMipmap_helper(kColorspaceAttribs, kWidth, kHeight, aHardwareBuffer, 0, |
| &image); |
| verifyImageStorageMipmapWithBlend(kColorspaceAttribs, image, mipLevelCount); |
| |
| // Clean up image |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), image); |
| |
| // Clean up AHB |
| destroyAndroidHardwareBuffer(aHardwareBuffer); |
| } |
| |
| // Test that drawing to an AHB works. |
| TEST_P(ImageTestES3, SourceAHBTarget2DDraw) |
| { |
| 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(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Draw to the target and verify results. |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify results for completeness. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test that using an image through a texture, detaching it, then using it again with another |
| // texture works. This is similar to the usage pattern of |SourceAHBTarget2DGenerateMipmap|, but |
| // doesn't require the |kAHBUsageGPUMipMapComplete| flags. |
| TEST_P(ImageTestES3, SourceAHBTarget2DUseAfterDetach) |
| { |
| 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(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Clear the texture, use a temporary framebuffer. |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| } |
| |
| // Use the texture in a command buffer, but don't let it get submitted (use the default |
| // framebuffer). |
| ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); |
| GLint texLocation = glGetUniformLocation(drawTexture, essl1_shaders::Texture2DUniform()); |
| ASSERT_NE(-1, texLocation); |
| glUseProgram(drawTexture); |
| glUniform1i(texLocation, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindTexture(GL_TEXTURE_2D, target); |
| drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Release the texture. In the Vulkan backend, the image is scheduled to be transitioned to the |
| // FORIENG queue because this is an AHB. |
| glBindTexture(GL_TEXTURE_2D, 0); |
| target.reset(); |
| |
| // Now attach another texture to the same image. |
| GLTexture target2; |
| glBindTexture(GL_TEXTURE_2D, target2); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Use the new texture. In the Vulkan backend, the management of transfer to FOREIGN queue and |
| // back should be correct. |
| drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify results for completeness. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Try to orphan image created with the GL_EXT_EGL_image_storage extension |
| TEST_P(ImageTestES3, Source2DTarget2DStorageOrphan) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| |
| // Create the Image |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kLinearColor), source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_2D, target, nullptr); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResults2D(target, kLinearColor); |
| |
| // Try to orphan this target texture |
| glBindTexture(GL_TEXTURE_2D, target); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, kLinearColor); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Try to orphan 3D image created with the GL_EXT_EGL_image_storage extension |
| TEST_P(ImageTestES3, Source3DTarget3DStorageOrphan) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has3DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| |
| constexpr size_t depth = 2; |
| EGLint attribs[3] = {EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_NONE}; |
| |
| // Create the Image |
| GLTexture source; |
| EGLImageKHR image; |
| |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, attribs, |
| static_cast<void *>(&kLinearColor), source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_3D, target, nullptr); |
| |
| for (size_t layer = 0; layer < depth; layer++) |
| { |
| // Expect that the target texture has the same color as the source texture |
| verifyResults3D(target, kLinearColor); |
| } |
| |
| // Try to orphan this target texture |
| glBindTexture(GL_TEXTURE_3D, target); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, depth, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kLinearColor); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kSrgbColor), source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Expect that the target texture has the same color as the source texture |
| verifyResults2D(target, kSrgbColor); |
| |
| // 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 decodes the sRGB color to linear when sampling |
| verifyResults2D(target, kLinearColor); |
| |
| // 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, kSrgbColor); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kLinearColor), source, &image); |
| |
| // Create the target |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kLinearColor), source, &image); |
| |
| // Create the target |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Create target texture from EGL image and then trigger texture respecification which releases the |
| // last image ref. |
| TEST_P(ImageTest, ImageOrphanRefCountingBug) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| // Create the first Image |
| GLTexture source1; |
| EGLImageKHR image1; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kLinearColor), source1, &image1); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image1, target); |
| |
| // Delete the source and image. A ref is still held by the target texture |
| source1.reset(); |
| eglDestroyImageKHR(window->getDisplay(), image1); |
| |
| // Create the second Image |
| GLTexture source2; |
| EGLImageKHR image2; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| static_cast<void *>(&kLinearColor), source2, &image2); |
| |
| // Respecify the target with the second image. |
| glBindTexture(GL_TEXTURE_2D, target); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image2); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image2); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor, source, |
| &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2DArray(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResults2DArray(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Testing source AHB EGL image, if the client buffer is null, the test will not crash |
| TEST_P(ImageTest, SourceAHBInvalid) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !IsVulkan()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| // Create the Image |
| EGLImageKHR image = eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, |
| EGL_NATIVE_BUFFER_ANDROID, nullptr, nullptr); |
| ASSERT_EGL_ERROR(EGL_BAD_PARAMETER); |
| EXPECT_EQ(image, EGL_NO_IMAGE_KHR); |
| } |
| |
| // Testing source AHB EGL image, if the client buffer is not a ANativeWindowBuffer, |
| // eglCreateImageKHR should return NO_IMAGE and generate error EGL_BAD_PARAMETER. |
| TEST_P(ImageTest, SourceAHBCorrupt) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !IsVulkan()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| #if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT) |
| EGLWindow *window = getEGLWindow(); |
| |
| const AHardwareBuffer_Desc aHardwareBufferDescription = createAndroidHardwareBufferDesc( |
| 16, 16, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, kAHBUsageGPUSampledImage); |
| |
| // Allocate memory from Android Hardware Buffer |
| AHardwareBuffer *aHardwareBuffer = nullptr; |
| EXPECT_EQ(0, AHardwareBuffer_allocate(&aHardwareBufferDescription, &aHardwareBuffer)); |
| |
| std::memset( |
| reinterpret_cast<void *>(angle::android::AHardwareBufferToClientBuffer(aHardwareBuffer)), 0, |
| sizeof(int)); |
| EGLImageKHR ahbImage = eglCreateImageKHR( |
| window->getDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, |
| angle::android::AHardwareBufferToClientBuffer(aHardwareBuffer), kDefaultAttribs); |
| |
| ASSERT_EGL_ERROR(EGL_BAD_PARAMETER); |
| EXPECT_EQ(ahbImage, EGL_NO_IMAGE_KHR); |
| |
| AHardwareBuffer_release(aHardwareBuffer); |
| #endif |
| } |
| |
| // Helper function to check if it is reasonable to access texture resource |
| void ImageTest::ImageCheckingTextureAccessHelper(GLenum target, bool mipmap) |
| { |
| constexpr GLsizei width = 2, height = 2, depth = 2; |
| GLTexture source; |
| EGLenum eglTarget; |
| EGLWindow *window = getEGLWindow(); |
| |
| glBindTexture(target, source); |
| switch (target) |
| { |
| case GL_TEXTURE_2D: |
| eglTarget = EGL_GL_TEXTURE_2D_KHR; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| break; |
| case GL_TEXTURE_3D: |
| eglTarget = EGL_GL_TEXTURE_3D_KHR; |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, width, height, depth, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| eglTarget = EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR; |
| for (GLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| glTexImage2D(faceIdx + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, width, height, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| } |
| break; |
| default: |
| return; |
| } |
| |
| if (mipmap) |
| { |
| glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| glGenerateMipmap(target); |
| } |
| |
| EGLImageKHR image = eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(image, EGL_NO_IMAGE_KHR); |
| |
| // If the texture is bound to egl image, EGL_BAD_ACCESS should be returned. |
| EGLImageKHR invalidImage1 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), nullptr); |
| ASSERT_EGL_ERROR(EGL_BAD_ACCESS); |
| EXPECT_EQ(invalidImage1, EGL_NO_IMAGE_KHR); |
| |
| // If the image is destroyed, the texture could be bound to egl image here. |
| eglDestroyImageKHR(window->getDisplay(), image); |
| |
| EGLImageKHR validImage1 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage1, EGL_NO_IMAGE_KHR); |
| |
| if (target == GL_TEXTURE_3D) |
| { |
| constexpr EGLint zOffsetAttribs[] = { |
| EGL_GL_TEXTURE_ZOFFSET, |
| 1, |
| EGL_NONE, |
| }; |
| EGLImageKHR validImage2 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), zOffsetAttribs); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage2, EGL_NO_IMAGE_KHR); |
| eglDestroyImageKHR(window->getDisplay(), validImage2); |
| } |
| |
| if (target == GL_TEXTURE_CUBE_MAP) |
| { |
| for (GLenum faceIdx = 1; faceIdx < 6; faceIdx++) |
| { |
| EGLImageKHR validImage2 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget + faceIdx, |
| reinterpretHelper<EGLClientBuffer>(source), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage2, EGL_NO_IMAGE_KHR); |
| eglDestroyImageKHR(window->getDisplay(), validImage2); |
| } |
| } |
| |
| if (mipmap) |
| { |
| constexpr EGLint mipmapAttribs[] = { |
| EGL_GL_TEXTURE_LEVEL, |
| 1, |
| EGL_NONE, |
| }; |
| EGLImageKHR validImage3 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), mipmapAttribs); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage3, EGL_NO_IMAGE_KHR); |
| |
| EGLImageKHR invalidImage2 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), mipmapAttribs); |
| ASSERT_EGL_ERROR(EGL_BAD_ACCESS); |
| EXPECT_EQ(invalidImage2, EGL_NO_IMAGE_KHR); |
| |
| eglDestroyImageKHR(window->getDisplay(), validImage3); |
| EGLImageKHR validImage4 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), eglTarget, |
| reinterpretHelper<EGLClientBuffer>(source), mipmapAttribs); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage4, EGL_NO_IMAGE_KHR); |
| eglDestroyImageKHR(window->getDisplay(), validImage4); |
| } |
| |
| eglDestroyImageKHR(window->getDisplay(), validImage1); |
| } |
| |
| // Testing GLES resources when creating EGL image, if the client buffer itself is an EGL sibling, |
| // eglCreateImageKHR should return NO_IMAGE and generate error EGL_BAD_ACCESS. |
| TEST_P(ImageTest, SourceBadAccess) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !IsVulkan()); |
| |
| // Validate gles 2D texture |
| if (has2DTextureExt()) |
| { |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_2D, false); |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_2D, true); |
| } |
| |
| // Validate gles 3D texture |
| if (has3DTextureExt() && getClientMajorVersion() >= 3) |
| { |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_3D, false); |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_3D, true); |
| } |
| |
| // Validate gles cube map texture |
| if (hasCubemapExt()) |
| { |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_CUBE_MAP, false); |
| ImageCheckingTextureAccessHelper(GL_TEXTURE_CUBE_MAP, true); |
| } |
| |
| // Validate gles renderbuffer |
| if (hasRenderbufferExt()) |
| { |
| EGLWindow *window = getEGLWindow(); |
| GLRenderbuffer source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, kDefaultAttribs, source, &image); |
| |
| EGLImageKHR invalidImage = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_RENDERBUFFER_KHR, |
| reinterpretHelper<EGLClientBuffer>(source), kDefaultAttribs); |
| ASSERT_EGL_ERROR(EGL_BAD_ACCESS); |
| EXPECT_EQ(invalidImage, EGL_NO_IMAGE_KHR); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| |
| EGLImageKHR validImage = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_RENDERBUFFER_KHR, |
| reinterpretHelper<EGLClientBuffer>(source), kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_NE(validImage, EGL_NO_IMAGE_KHR); |
| eglDestroyImageKHR(window->getDisplay(), validImage); |
| } |
| } |
| |
| // 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, |
| kDefaultAHBUsage, kDefaultAttribs, {{data, 4}}, |
| &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Testing source AHB EGL image, target 2D texture |
| TEST_P(ImageTest, SourceAHBTarget2D) |
| { |
| SourceAHBTarget2D_helper(kDefaultAttribs); |
| } |
| |
| // Testing source AHB EGL image with colorspace, target 2D texture |
| TEST_P(ImageTest, SourceAHBTarget2D_Colorspace) |
| { |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, attribs, {{kSrgbColor, 4}}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| // Verify that the target texture has the expected color |
| verifyResults2D(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // 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()); |
| |
| 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, |
| kDefaultAHBUsage, 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, |
| kDefaultAHBUsage, 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, |
| kDefaultAHBUsage, 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()); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kDefaultAHBUsage)); |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAHBUsage)); |
| |
| // Create RGB Image |
| GLubyte rgbColor[4] = {0, 0, 255, 255}; |
| |
| AHardwareBuffer *rgbSource; |
| EGLImageKHR rgbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, 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, kDefaultAHBUsage, 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()); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create RGBA Image |
| GLubyte rgbaColor[4] = {0, 0, 255, 255}; |
| |
| AHardwareBuffer *rgbaSource; |
| EGLImageKHR rgbaImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, 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, kDefaultAHBUsage, 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 images, target 2D external textures, cycling through YUV targets. |
| TEST_P(ImageTest, SourceAHBTarget2DExternalCycleThroughYuvTargetsNoData) |
| { |
| // http://issuetracker.google.com/175021871 |
| ANGLE_SKIP_TEST_IF(IsPixel2() || IsPixel2XL()); |
| |
| 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &ycbcrSource, |
| &ycbcrImage); |
| EXPECT_NE(ycbcrSource, nullptr); |
| EXPECT_NE(ycbcrImage, EGL_NO_IMAGE_KHR); |
| |
| // Create YV12 source and image but without initial data |
| AHardwareBuffer *yv12Source; |
| EGLImageKHR yv12Image; |
| createEGLImageAndroidHardwareBufferSource(2, 2, 1, AHARDWAREBUFFER_FORMAT_YV12, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &yv12Source, |
| &yv12Image); |
| EXPECT_NE(yv12Source, nullptr); |
| EXPECT_NE(yv12Image, EGL_NO_IMAGE_KHR); |
| |
| // Create texture target siblings to bind the egl images |
| // Create YCbCr target and bind the image |
| GLTexture ycbcrTarget; |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, ycbcrTarget); |
| // 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, ycbcrImage); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create YV12 target and bind the image |
| GLTexture yv12Target; |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yv12Target); |
| // 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, yv12Image); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Cycle through targets |
| glUseProgram(mTextureExternalProgram); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| |
| // Bind YCbCr image |
| // YCbCr target |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, ycbcrTarget); |
| // Draw while sampling should result in no EGL/GL errors |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // YV12 target |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yv12Target); |
| // Draw while sampling should result in no EGL/GL errors |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ycbcrImage); |
| destroyAndroidHardwareBuffer(ycbcrSource); |
| eglDestroyImageKHR(window->getDisplay(), yv12Image); |
| destroyAndroidHardwareBuffer(yv12Source); |
| } |
| |
| // Testing source AHB EGL image, target 2D texture retaining initial data. |
| TEST_P(ImageTest, SourceAHBTarget2DRetainInitialData) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| GLubyte data[4] = {0, 255, 0, 255}; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{data, 4}}, |
| &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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); |
| } |
| |
| // Test interaction between AHBs and GL_EXT_multisampled_render_to_texture |
| TEST_P(ImageTest, SourceAHBTarget2DMSRTTInteraction) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Bind target texture to mulisampled framebuffer |
| GLFramebuffer fboMS; |
| glBindFramebuffer(GL_FRAMEBUFFER, fboMS); |
| glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| target, 0, 4); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear framebuffer |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Check clear result |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Check the AHB is updated |
| verifyResultAHB(source, {{GLColor::blue.data(), 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| // Testing source AHB EGL image, target 2D array texture |
| TEST_P(ImageTest, SourceAHBTarget2DArray) |
| { |
| SourceAHBTarget2DArray_helper(kDefaultAttribs); |
| } |
| |
| // Testing source AHB EGL image with colorspace, target 2D array texture |
| TEST_P(ImageTest, SourceAHBTarget2DArray_Colorspace) |
| { |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, attribs, {{kSrgbColor, 4}}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2DArray(image, target); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| // Verify that the target texture has the expected color |
| verifyResults2DArray(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source AHB EGL image, target external texture |
| TEST_P(ImageTest, SourceAHBTargetExternal) |
| { |
| SourceAHBTargetExternal_helper(kDefaultAttribs); |
| } |
| |
| // Testing source AHB EGL image with colorspace, target external texture |
| TEST_P(ImageTest, SourceAHBTargetExternal_Colorspace) |
| { |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, |
| kDefaultAHBUsage, attribs, {{kSrgbColor, 4}}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source AHB EGL image, target external ESSL3 texture |
| TEST_P(ImageTestES3, SourceAHBTargetExternalESSL3) |
| { |
| SourceAHBTargetExternalESSL3_helper(kDefaultAttribs); |
| } |
| |
| // Test sampling from a YUV texture using GL_ANGLE_yuv_internal_format as external texture and then |
| // switching to raw YUV sampling using EXT_yuv_target |
| TEST_P(ImageTestES3, SourceYUVTextureTargetExternalRGBSampleYUVSample) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasYUVInternalFormatExt() || !hasYUVTargetExt()); |
| |
| // Create source YUV texture |
| GLTexture yuvTexture; |
| GLubyte yuvColor[6] = {7, 51, 197, 231, 128, 192}; |
| GLubyte expectedRgbColor[4] = {255, 159, 211, 255}; |
| constexpr size_t kWidth = 2; |
| constexpr size_t kHeight = 2; |
| |
| glBindTexture(GL_TEXTURE_2D, yuvTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, kWidth, kHeight); |
| ASSERT_GL_NO_ERROR(); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, |
| GL_UNSIGNED_BYTE, yuvColor); |
| ASSERT_GL_NO_ERROR(); |
| // 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 the Image |
| EGLWindow *window = getEGLWindow(); |
| EGLImageKHR image = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(yuvTexture), kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Draw quad with program that samples YUV data with implicit RGB conversion |
| glUseProgram(mTextureExternalProgram); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| // Expect that the rendered quad's color is converted to RGB |
| EXPECT_PIXEL_NEAR(0, 0, expectedRgbColor[0], expectedRgbColor[1], expectedRgbColor[2], |
| expectedRgbColor[3], 1); |
| |
| // Draw quad with program that samples raw YUV data |
| glUseProgram(mTextureYUVProgram); |
| glUniform1i(mTextureYUVUniformLocation, 0); |
| drawQuad(mTextureYUVProgram, "position", 0.5f); |
| // Expect that the rendered quad's color matches the raw YUV data |
| EXPECT_PIXEL_NEAR(0, 0, yuvColor[2], yuvColor[4], yuvColor[5], 255, 1); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Similar to SourceYUVTextureTargetExternalRGBSampleYUVSample, but added swizzle after |
| // __samplerExternal2DY2YEXT from texture. |
| TEST_P(ImageTestES3, SourceYUVTextureTargetExternalRGBSampleYUVSampleWithSwizzle) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasYUVInternalFormatExt() || !hasYUVTargetExt()); |
| |
| // Create source YUV texture |
| GLTexture yuvTexture; |
| GLubyte yuvColor[6] = {7, 51, 197, 231, 128, 192}; |
| constexpr size_t kWidth = 2; |
| constexpr size_t kHeight = 2; |
| |
| glBindTexture(GL_TEXTURE_2D, yuvTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, kWidth, kHeight); |
| ASSERT_GL_NO_ERROR(); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, |
| GL_UNSIGNED_BYTE, yuvColor); |
| ASSERT_GL_NO_ERROR(); |
| // 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 the Image |
| EGLWindow *window = getEGLWindow(); |
| EGLImageKHR image = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(yuvTexture), kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Draw quad with program that samples raw YUV data and then swizzle |
| const char *fragmentShaderSource = R"(#version 300 es |
| #extension GL_EXT_YUV_target : require |
| precision highp float; |
| uniform __samplerExternal2DY2YEXT tex; |
| in vec2 texcoord; |
| out vec4 color; |
| |
| void main() |
| { |
| color = vec4(texture(tex, texcoord).zyx, 1.0); |
| })"; |
| ANGLE_GL_PROGRAM(textureYUVProgram, getVSESSL3(), fragmentShaderSource); |
| ASSERT_NE(0u, textureYUVProgram) << "shader compilation failed."; |
| glUseProgram(textureYUVProgram); |
| GLint uniformLocation = glGetUniformLocation(textureYUVProgram, "tex"); |
| ASSERT_NE(-1, uniformLocation); |
| glUniform1i(uniformLocation, 0); |
| drawQuad(textureYUVProgram, "position", 0.5f); |
| // Expect that the rendered quad's color matches the raw YUV data after component swizzle |
| EXPECT_PIXEL_NEAR(0, 0, yuvColor[5], yuvColor[4], yuvColor[2], 255, 1); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Test interaction between GL_ANGLE_yuv_internal_format and EXT_yuv_target when a program has |
| // both __samplerExternal2DY2YEXT and samplerExternalOES samplers. |
| TEST_P(ImageTestES3, ProgramWithBothExternalY2YAndExternalOESSampler) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasYUVInternalFormatExt() || !hasYUVTargetExt()); |
| |
| GLubyte yuvColor[6] = {40, 40, 40, 40, 240, 109}; |
| GLubyte expectedRgbColor[4] = {0, 0, 255, 255}; |
| constexpr size_t kWidth = 2; |
| constexpr size_t kHeight = 2; |
| |
| // Create 2 plane YUV texture source |
| GLTexture yuvTexture0; |
| glBindTexture(GL_TEXTURE_2D, yuvTexture0); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, kWidth, kHeight); |
| ASSERT_GL_NO_ERROR(); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, |
| GL_UNSIGNED_BYTE, yuvColor); |
| ASSERT_GL_NO_ERROR(); |
| // 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 2 plane YUV texture source |
| GLTexture yuvTexture1; |
| glBindTexture(GL_TEXTURE_2D, yuvTexture1); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, kWidth, kHeight); |
| ASSERT_GL_NO_ERROR(); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_G8_B8R8_2PLANE_420_UNORM_ANGLE, |
| GL_UNSIGNED_BYTE, yuvColor); |
| ASSERT_GL_NO_ERROR(); |
| // 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 the Images |
| EGLWindow *window = getEGLWindow(); |
| EGLImageKHR image0 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(yuvTexture0), kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| |
| EGLImageKHR image1 = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(yuvTexture1), kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create texture targets for EGLImages |
| GLTexture target0; |
| createEGLImageTargetTextureExternal(image0, target0); |
| |
| GLTexture target1; |
| createEGLImageTargetTextureExternal(image1, target1); |
| |
| // Create program with 2 samplers |
| const char *vertexShaderSource = R"(#version 300 es |
| out vec2 texcoord; |
| in vec4 position; |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| const char *fragmentShaderSource = R"(#version 300 es |
| #extension GL_EXT_YUV_target : require |
| #extension GL_OES_EGL_image_external_essl3 : require |
| precision highp float; |
| uniform __samplerExternal2DY2YEXT tex0; |
| uniform samplerExternalOES tex1; |
| uniform uint samplerSelector; |
| in vec2 texcoord; |
| out vec4 fragColor; |
| |
| void main() |
| { |
| vec4 color0 = texture(tex0, texcoord); |
| vec4 color1 = texture(tex1, texcoord); |
| if (samplerSelector == 0u) |
| { |
| fragColor = color0; |
| } |
| else if (samplerSelector == 1u) |
| { |
| fragColor = color1; |
| } |
| else |
| { |
| fragColor = vec4(1.0); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(twoSamplersProgram, vertexShaderSource, fragmentShaderSource); |
| glUseProgram(twoSamplersProgram); |
| GLint tex0Location = glGetUniformLocation(twoSamplersProgram, "tex0"); |
| ASSERT_NE(-1, tex0Location); |
| GLint tex1Location = glGetUniformLocation(twoSamplersProgram, "tex1"); |
| ASSERT_NE(-1, tex1Location); |
| GLint samplerSelectorLocation = glGetUniformLocation(twoSamplersProgram, "samplerSelector"); |
| ASSERT_NE(-1, samplerSelectorLocation); |
| |
| // Bind texture target to GL_TEXTURE_EXTERNAL_OES |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Bind texture target to GL_TEXTURE_EXTERNAL_OES |
| glActiveTexture(GL_TEXTURE1); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target1); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Set sampler uniform values |
| glUniform1i(tex0Location, 0); |
| glUniform1i(tex1Location, 1); |
| |
| // Set sampler selector uniform value and draw |
| glUniform1ui(samplerSelectorLocation, 0); |
| drawQuad(twoSamplersProgram, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(0, 0, yuvColor[3], yuvColor[4], yuvColor[5], 255, 1); |
| |
| // Switch sampler selector uniform value and draw |
| glUniform1ui(samplerSelectorLocation, 1); |
| drawQuad(twoSamplersProgram, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(0, 0, expectedRgbColor[0], expectedRgbColor[1], expectedRgbColor[2], |
| expectedRgbColor[3], 1); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image0); |
| eglDestroyImageKHR(window->getDisplay(), image1); |
| } |
| |
| // Test sampling from a YUV AHB with a regular external sampler and pre-initialized data |
| TEST_P(ImageTest, SourceYUVAHBTargetExternalRGBSampleInitData) |
| { |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| GLubyte pixelColor[4] = {255, 159, 211, 255}; |
| verifyResultsExternal(target, pixelColor); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // 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) |
| { |
| 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test sampling from a YUV AHB using EXT_yuv_target |
| TEST_P(ImageTestES3, SourceYUVAHBTargetExternalYUVSample) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| GLubyte pixelColor[4] = {197, 128, 192, 255}; |
| verifyResultsExternalYUV(target, pixelColor); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test sampling from a YUV AHB using EXT_yuv_target in the vertex shader |
| TEST_P(ImageTestES3, SourceYUVAHBTargetExternalYUVSampleVS) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| GLubyte pixelColor[4] = {197, 128, 192, 255}; |
| verifyResultsExternalYUVVS(target, pixelColor); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test using glCopySubTextureCHROMIUM with YUV AHB as the source |
| TEST_P(ImageTestES3, SourceYUVAHBTargetExternalCopySrc) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_CHROMIUM_copy_texture")); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture yuv; |
| createEGLImageTargetTextureExternal(image, yuv); |
| |
| // Create a texture to be the destination of copy |
| GLTexture copyDst; |
| glBindTexture(GL_TEXTURE_2D, copyDst); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2); |
| glCopySubTextureCHROMIUM(yuv, 0, GL_TEXTURE_2D, copyDst, 0, 0, 0, 0, 0, 2, 2, false, false, |
| false); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify the results |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copyDst, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(92, 0, 0, 255), 2); |
| EXPECT_PIXEL_COLOR_NEAR(1, 0, GLColor(143, 0, 41, 255), 2); |
| EXPECT_PIXEL_COLOR_NEAR(0, 1, GLColor(255, 159, 211, 255), 2); |
| EXPECT_PIXEL_COLOR_NEAR(1, 1, GLColor(255, 198, 250, 255), 2); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| TEST_P(ImageTestES3, SourceYUVAHBTargetExternalYUVSampleLinearFiltering) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 2, 4, 1, AHARDWAREBUFFER_FORMAT_YV12, kDefaultAHBYUVUsage)); |
| |
| // [ Red, Red] |
| // [ Red, Red] |
| // [Black, Black] |
| // [Black, Black] |
| |
| // clang-format off |
| GLubyte dataY[] = { |
| 81, 81, |
| 81, 81, |
| 16, 16, |
| 16, 16, |
| }; |
| GLubyte dataCb[] = { |
| 90, |
| 128, |
| }; |
| GLubyte dataCr[] = { |
| 240, |
| 128, |
| }; |
| // clang-format on |
| |
| // Create the Image |
| AHardwareBuffer *ahbSource; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource( |
| 2, 4, 1, AHARDWAREBUFFER_FORMAT_YV12, kDefaultAHBYUVUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &ahbSource, &ahbImage); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create a texture target to bind the egl image |
| GLTexture ahbTexture; |
| createEGLImageTargetTextureExternal(ahbImage, ahbTexture); |
| |
| // Configure linear filtering |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, ahbTexture); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| // Draw fullscreen sampling from ahbTexture. |
| glUseProgram(mTextureExternalProgram); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, ahbTexture); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| |
| // Framebuffer needs to be bigger than the AHB so there is an area in between that will result |
| // in half-red. |
| const int windowHeight = getWindowHeight(); |
| ASSERT_GE(windowHeight, 8); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor::black, 1); |
| EXPECT_PIXEL_COLOR_NEAR(0, windowHeight - 1, GLColor::red, 1); |
| |
| // Approximately half-red: |
| EXPECT_PIXEL_COLOR_NEAR(0, windowHeight / 2, GLColor(127, 0, 0, 255), 15.0); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahbSource); |
| } |
| |
| // Test rendering to a YUV AHB using EXT_yuv_target |
| TEST_P(ImageTestES3, RenderToYUVAHB) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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); |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); |
| drawQuad(mRenderYUVProgram, "position", 0.0f); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test rendering to a YUV AHB using EXT_yuv_target with a normal depth attachment |
| TEST_P(ImageTestES3, RenderToYUVAHBWithDepth) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasYUVTargetExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 1}, {dataCb, 1}, {dataCr, 1}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Create a depth texture |
| GLTexture depthTexture; |
| glBindTexture(GL_TEXTURE_2D, depthTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 2, 2, 0, GL_DEPTH_COMPONENT, GL_FLOAT, |
| nullptr); |
| EXPECT_GL_NO_ERROR(); |
| |
| // 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); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 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); |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); |
| drawQuad(mRenderYUVProgram, "position", 0.0f); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test clearing to a YUV AHB using EXT_yuv_target with a normal depth attachment |
| TEST_P(ImageTestES3, ClearYUVAHBWithDepth) |
| { |
| 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Create a depth texture |
| GLTexture depthTexture; |
| glBindTexture(GL_TEXTURE_2D, depthTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 2, 2, 0, GL_DEPTH_COMPONENT, GL_FLOAT, |
| nullptr); |
| EXPECT_GL_NO_ERROR(); |
| |
| // 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); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test partial clearing to a YUV AHB using EXT_yuv_target |
| TEST_P(ImageTestES3, PartialClearYUVAHB) |
| { |
| 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 |
| GLubyte dataY[4] = {0, 0, 0, 0}; |
| GLubyte dataCb[1] = { |
| 0, |
| }; |
| GLubyte dataCr[1] = { |
| 0, |
| }; |
| |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource( |
| 8, 8, 1, AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, kDefaultAHBUsage, kDefaultAttribs, |
| {{dataY, 64}, {dataCb, 64}, {dataCr, 64}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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); |
| |
| // Full clear the background |
| GLubyte clearColorFull[4] = {40, 240, 109, 255}; |
| glClearColor(clearColorFull[0] / 255.0f, clearColorFull[1] / 255.0f, clearColorFull[2] / 255.0f, |
| clearColorFull[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0, 0, 255, 255), 2.0); |
| |
| // Partial clear the corner with another color |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(0, 0, 4, 4); |
| |
| 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), 2.0); |
| EXPECT_PIXEL_COLOR_NEAR(4, 4, GLColor(0, 0, 255, 255), 2.0); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test glClear on FBO with AHB attachment is applied to the AHB image before we read back |
| TEST_P(ImageTestES3, AHBClearAppliedBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glFinish(); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Similar to AHBClearAppliedBeforeReadBack, but clear is applied glClearTexImage(). |
| TEST_P(ImageTestES3, AHBClearAppliedViaClearTexImageBeforeReadBack) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clear_texture")); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| EGLWindow *window = getEGLWindow(); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Clear to red |
| glClearTexImageEXT(ahbTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red); |
| glFinish(); |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Similar to AHBClearAppliedBeforeReadBack, but clear is applied twice. |
| TEST_P(ImageTestES3, AHBTwiceClearAppliedBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to green, then to red |
| glClearColor(0, 1, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glFinish(); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Similar to AHBTwiceClearAppliedBeforeReadBack, but clear is applied using glClearTexImage(). |
| TEST_P(ImageTestES3, AHBTwiceClearViaClearTexImageAppliedBeforeReadBack) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clear_texture")); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| EGLWindow *window = getEGLWindow(); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Clear to green, then to red |
| glClearTexImageEXT(ahbTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green); |
| glClearTexImageEXT(ahbTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red); |
| glFinish(); |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that glClear on FBO with AHB attachment is applied to the AHB image before detaching the AHB |
| // image from FBO |
| TEST_P(ImageTestES3, AHBClearAndDetachBeforeReadback) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Detach the AHB image from the FBO color attachment |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| glFinish(); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that glClear on FBO with AHB color attachment is applied to the AHB image before implicity |
| // unbinding the AHB image from FBO |
| TEST_P(ImageTestES3, AHBClearAndAttachAnotherTextureBeforeReadback) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Attach a random texture to the same FBO color attachment slot that AHB image was attached |
| // to, this should implicity detach the AHB image from the FBO. |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, newTexture, 0); |
| glFinish(); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test glClear to FBO with AHB color attachment is applied to the AHB image before we switch back |
| // to the default FBO |
| TEST_P(ImageTestES3, AHBClearAndSwitchToDefaultFBOBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Switch to default FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glFinish(); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test glClear on FBO with AHB color attachment is applied to the AHB image with glClientWaitSync |
| TEST_P(ImageTestES3, AHBClearWithGLClientWaitSyncBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| // Create one image backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB. |
| { |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Create a GLSync object and immediately wait on it |
| GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); |
| } |
| |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test glDraw + glFlush on FBO with AHB attachment are applied to the AHB image before we read back |
| TEST_P(ImageTestES3, AHBDrawFlushAppliedBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport() || !kHasAHBFrontBufferUsageSupport); |
| |
| // Create a GLTexture backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageFrontBuffer, |
| kDefaultAttribs, {{kBlack, 4}}, &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB-based GLTexture |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw to the FBO and call glFlush() |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, kRed[0] / 255.0f, kRed[1] / 255.0f, kRed[2] / 255.0f, |
| kRed[3] / 255.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); |
| glFlush(); |
| // unlike glFinish(), glFlush() does not wait for commands execution to complete. |
| // sleep for 1 second before reading back from AHB. |
| angle::Sleep(1000); |
| |
| // Verify the result |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that glDraw + glFlush on FBO with AHB attachment are applied to the AHB |
| // image before detaching the AHB image from FBO |
| TEST_P(ImageTestES3, AHBDrawFlushAndDetachBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport() || !kHasAHBFrontBufferUsageSupport); |
| |
| // Create a GLTexture backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageFrontBuffer, |
| kDefaultAttribs, {{kBlack, 4}}, &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB-based GLTexture |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw to the FBO and call glFlush() |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, kRed[0] / 255.0f, kRed[1] / 255.0f, kRed[2] / 255.0f, |
| kRed[3] / 255.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glFlush(); |
| // unlike glFinish(), glFlush() does not wait for commands execution to complete. |
| // sleep for 1 second before reading back from AHB. |
| angle::Sleep(1000); |
| |
| // Detach the AHB image from the FBO color attachment |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| |
| // Verify the result |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that glDraw + glFlush on FBO with AHB attachment are applied to the AHB |
| // image before implicitly unbinding the AHB image from FBO |
| TEST_P(ImageTestES3, AHBDrawFlushAndAttachAnotherTextureBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport() || !kHasAHBFrontBufferUsageSupport); |
| |
| // Create a GLTexture backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageFrontBuffer, |
| kDefaultAttribs, {{kBlack, 4}}, &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB-based GLTexture |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw to the FBO and call glFlush() |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, kRed[0] / 255.0f, kRed[1] / 255.0f, kRed[2] / 255.0f, |
| kRed[3] / 255.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glFlush(); |
| // unlike glFinish(), glFlush() does not wait for commands execution to complete. |
| // sleep for 1 second before reading back from AHB. |
| angle::Sleep(1000); |
| |
| // Attach a random texture to the same FBO color attachment slot that AHB image was attached |
| // to, this should implicity detach the AHB image from the FBO. |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, newTexture, 0); |
| |
| // Verify the result |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that glDraw + glFlush on FBO with AHB attachment are applied to the AHB |
| // image before switching to the default FBO |
| TEST_P(ImageTestES3, AHBDrawFlushAndSwitchToDefaultFBOBeforeReadBack) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport() || !kHasAHBFrontBufferUsageSupport); |
| |
| // Create a GLTexture backed by the AHB. |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| const GLubyte kRed[] = {255, 0, 0, 255}; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageFrontBuffer, |
| kDefaultAttribs, {{kBlack, 4}}, &ahb, &ahbImage); |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Create one framebuffer backed by the AHB-based GLTexture |
| GLFramebuffer ahbFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw to the FBO and call glFlush() |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, kRed[0] / 255.0f, kRed[1] / 255.0f, kRed[2] / 255.0f, |
| kRed[3] / 255.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glFlush(); |
| // unlike glFinish(), glFlush() does not wait for commands execution to complete. |
| // sleep for 1 second before reading back from AHB. |
| angle::Sleep(1000); |
| |
| // Switch to default FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| // Verify the result |
| verifyResultAHB(ahb, {{kRed, 4}}); |
| |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that texture swizzle parameters work with EGL image-backed textures |
| TEST_P(ImageTestES3, AHBTextureSwizzleParameters) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_GREEN); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); |
| |
| GLubyte expectedColor[] = {kLinearColor[1], kLinearColor[2], kLinearColor[0], kLinearColor[3]}; |
| verifyResults2D(ahbTexture, expectedColor); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Regression test for a bug in the |
| // Vulkan backend where the image was cleared due to format emulation. |
| TEST_P(ImageTestES3, RGBXAHBImportPreservesData) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| verifyResults2D(ahbTexture, kLinearColor); |
| verifyResultAHB(ahb, {{kLinearColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB created with sRGB color space. |
| TEST_P(ImageTestES3, RGBXAHBImportPreservesData_Colorspace) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kRed50SRGB[] = {188, 0, 0, 255}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 255}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kColorspaceAttribs, |
| {{kRed50SRGB, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50SRGB, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Tests that RGBX can be successfully loaded with 3-channel data and read back as 4-channel data. |
| TEST_P(ImageTestES3, RGBXAHBUploadDownload) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport() || !hasRGBXInternalFormatExt()); |
| |
| const size_t kWidth = 32; |
| const size_t kHeight = 32; |
| |
| const GLubyte kBlack[] = {0, 0, 0, 255}; |
| const GLubyte kCyan[] = {0, 255, 255}; |
| |
| std::vector<GLubyte> pixelsRGBABlack; |
| for (size_t h = 0; h < kHeight; h++) |
| { |
| for (size_t w = 0; w < kWidth; w++) |
| { |
| pixelsRGBABlack.push_back(kBlack[0]); |
| pixelsRGBABlack.push_back(kBlack[1]); |
| pixelsRGBABlack.push_back(kBlack[2]); |
| pixelsRGBABlack.push_back(kBlack[3]); |
| } |
| } |
| |
| std::vector<GLubyte> pixelsRGBCyan; |
| for (size_t h = 0; h < kHeight; h++) |
| { |
| for (size_t w = 0; w < kWidth; w++) |
| { |
| pixelsRGBCyan.push_back(kCyan[0]); |
| pixelsRGBCyan.push_back(kCyan[1]); |
| pixelsRGBCyan.push_back(kCyan[2]); |
| } |
| } |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource( |
| kWidth, kHeight, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, kDefaultAHBUsage, |
| kDefaultAttribs, {{pixelsRGBABlack.data(), 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| verifyResults2D(ahbTexture, kBlack); |
| verifyResultAHB(ahb, {{pixelsRGBABlack.data(), 4}}); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, |
| pixelsRGBCyan.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer ahbFramebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, ahbFramebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| std::vector<GLubyte> readback; |
| readback.resize(kWidth * kHeight * 4); |
| glReadPixels(0, 0, kWidth, kHeight, GL_RGBX8_ANGLE, GL_UNSIGNED_BYTE, readback.data()); |
| |
| for (size_t y = 0; y < kHeight; y++) |
| { |
| const GLubyte *actualRowData = readback.data() + (y * kWidth * 4); |
| for (size_t x = 0; x < kWidth; x++) |
| { |
| const GLubyte *actualPixelData = actualRowData + (x * 4); |
| EXPECT_EQ(actualPixelData[0], kCyan[0]) << "at (" << x << ", " << y << ")"; |
| EXPECT_EQ(actualPixelData[1], kCyan[1]) << "at (" << x << ", " << y << ")"; |
| EXPECT_EQ(actualPixelData[2], kCyan[2]) << "at (" << x << ", " << y << ")"; |
| } |
| } |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBA data are preserved when importing from AHB and glTexSubImage is able to update |
| // data. |
| TEST_P(ImageTestES3, RGBAAHBUploadData) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kGarbage[] = {123, 123, 123, 123}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 127}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kGarbage, 4}}, |
| &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kRed50Linear); |
| glFinish(); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50Linear, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBA data are preserved when importing from AHB with sRGB color space and glTexSubImage |
| // is able to update data. |
| TEST_P(ImageTestES3, RGBAAHBUploadDataColorspace) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kGarbage[] = {123, 123, 123, 123}; |
| const GLubyte kRed50SRGB[] = {188, 0, 0, 128}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 127}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kColorspaceAttribs, {{kGarbage, 4}}, |
| &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kRed50SRGB); |
| glFinish(); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50SRGB, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB and glTexSubImage is able to update |
| // data. |
| TEST_P(ImageTestES3, RGBXAHBUploadData) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kGarbage[] = {123, 123, 123, 123}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 255}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kGarbage, 4}}, |
| &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kRed50Linear); |
| glFinish(); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50Linear, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB created with sRGB color space and |
| // glTexSubImage is able to update data. |
| TEST_P(ImageTestES3, RGBXAHBUploadDataColorspace) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kGarbage[] = {123, 123, 123, 123}; |
| const GLubyte kRed50SRGB[] = {188, 0, 0, 255}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 255}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kColorspaceAttribs, |
| {{kGarbage, sizeof(kGarbage)}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kRed50SRGB); |
| glFinish(); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50SRGB, sizeof(kRed50SRGB)}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGB data are preserved when importing from AHB created with sRGB color space and |
| // glTexSubImage is able to update data. |
| TEST_P(ImageTestES3, RGBAHBUploadDataColorspace) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM, kDefaultAHBUsage)); |
| |
| const GLubyte kGarbage[] = {123, 123, 123}; |
| const GLubyte kRed50SRGB[] = {188, 0, 0}; |
| const GLubyte kRed50Linear[] = {128, 0, 0, 255}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM, |
| kDefaultAHBUsage, kColorspaceAttribs, |
| {{kGarbage, sizeof(kGarbage)}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kRed50SRGB); |
| glFinish(); |
| |
| verifyResults2D(ahbTexture, kRed50Linear); |
| verifyResultAHB(ahb, {{kRed50SRGB, sizeof(kRed50SRGB)}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with no GPU_FRAMEBUFFER usage specified. |
| TEST_P(ImageTestES3, RGBXAHBImportNoFramebufferUsage) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kAHBUsageGPUSampledImage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| verifyResults2D(ahbTexture, kLinearColor); |
| verifyResultAHB(ahb, {{kLinearColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with multiple layers. |
| TEST_P(ImageTestES3, RGBXAHBImportMultipleLayers) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Limit the test to single layer for now. writeAHBData is assuming alignment between layers |
| // being 4096 which may not true on some GPUs. There is no API to retrieve such alignment from |
| // driver. For now just limit to single layer so that we can still test single layer behavior |
| // here. |
| constexpr size_t kLayerCount = 1; |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, kLayerCount, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, kDefaultAHBUsage)); |
| |
| const GLubyte kInitColor[] = {132, 55, 219, 12, 77, 23, 190, 101, 231, 44, 143, 99}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource( |
| 1, 1, kLayerCount, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, kDefaultAHBUsage, kDefaultAttribs, |
| {{kInitColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2DArray(ahbImage, ahbTexture); |
| |
| // RGBX doesn't have alpha, so readback should return 255. |
| const GLubyte kExpectedColor[] = { |
| kInitColor[0], kInitColor[1], kInitColor[2], 255, kInitColor[4], kInitColor[5], |
| kInitColor[6], 255, kInitColor[8], kInitColor[9], kInitColor[10], 255, |
| }; |
| for (uint32_t layerIndex = 0; layerIndex < kLayerCount; ++layerIndex) |
| { |
| verifyResults2DArray(ahbTexture, kExpectedColor + 4 * layerIndex, layerIndex); |
| } |
| verifyResultAHB(ahb, {{kExpectedColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with glReadPixels. |
| TEST_P(ImageTestES3, RGBXAHBImportThenReadPixels) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // RGBX doesn't have alpha, so readback should return 255. kLinearColor[3] is already 255. |
| EXPECT_PIXEL_NEAR(0, 0, kLinearColor[0], kLinearColor[1], kLinearColor[2], kLinearColor[3], 1); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| verifyResults2D(ahbTexture, kLinearColor); |
| verifyResultAHB(ahb, {{kLinearColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with a following clear. |
| TEST_P(ImageTestES3, RGBXAHBImportThenClear) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear |
| const GLubyte kClearColor[] = {63, 127, 191, 55}; |
| glClearColor(kClearColor[0] / 255.0f, kClearColor[1] / 255.0f, kClearColor[2] / 255.0f, |
| kClearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| // RGBX doesn't have alpha, so readback should return 255. |
| const GLubyte kExpectedColor[] = {kClearColor[0], kClearColor[1], kClearColor[2], 255}; |
| verifyResults2D(ahbTexture, kExpectedColor); |
| verifyResultAHB(ahb, {{kExpectedColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with a following clear and a draw call. |
| TEST_P(ImageTestES3, RGBXAHBImportThenClearThenDraw) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear |
| const GLubyte kClearColor[] = {63, 127, 191, 55}; |
| glClearColor(kClearColor[0] / 255.0f, kClearColor[1] / 255.0f, kClearColor[2] / 255.0f, |
| kClearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Draw with blend |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, 0.25f, 0.25f, 0.25f, 0.25f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glDisable(GL_BLEND); |
| |
| // RGBX doesn't have alpha, so readback should return 255. |
| const GLubyte kExpectedColor[] = {static_cast<GLubyte>(kClearColor[0] + 64), |
| static_cast<GLubyte>(kClearColor[1] + 64), |
| static_cast<GLubyte>(kClearColor[2] + 64), 255}; |
| verifyResults2D(ahbTexture, kExpectedColor); |
| verifyResultAHB(ahb, {{kExpectedColor, 4}}); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with a following data upload. |
| TEST_P(ImageTestES3, RGBXAHBImportThenUpload) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kInitColor[] = {132, 55, 219, 12, 132, 55, 219, 12}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(2, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kInitColor, 4}}, |
| &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Upload data |
| const GLubyte kUploadColor[] = {63, 127, 191, 55}; |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kUploadColor); |
| ASSERT_GL_NO_ERROR(); |
| |
| // RGBX doesn't have alpha, so readback should return 255. |
| const GLubyte kExpectedColorRight[] = {kUploadColor[0], kUploadColor[1], kUploadColor[2], 255}; |
| const GLubyte kExpectedColorLeft[] = {kInitColor[0], kInitColor[1], kInitColor[2], 255}; |
| verifyResults2DLeftAndRight(ahbTexture, kExpectedColorLeft, kExpectedColorRight); |
| verifyResultAHB(ahb, {{kExpectedColorLeft, 4}}, AHBVerifyRegion::LeftHalf); |
| verifyResultAHB(ahb, {{kExpectedColorRight, 4}}, AHBVerifyRegion::RightHalf); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Tests interaction of emulated channel being cleared with a following data upload and immediately |
| // ends to check that the image updates are processed and flushed without errors. It is similar to |
| // RGBXAHBImportThenUpload, but there is no pixel reading or destroying the image to avoid extra |
| // staged update flushes. |
| TEST_P(ImageTestES3, IncompleteRGBXAHBImportThenUploadThenEnd) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| const GLubyte kInitColor[] = {132, 55, 219, 12, 132, 55, 219, 12}; |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(2, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kInitColor, 4}}, |
| &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| // Upload data |
| const GLubyte kUploadColor[] = {63, 127, 191, 55}; |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, kUploadColor); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clean up |
| destroyAndroidHardwareBuffer(ahb); |
| // This test relies on internal assertions to catch the issue regarding unflushed updates after |
| // clearing emulated channels. |
| } |
| |
| // Test that RGBX data are preserved when importing from AHB. Tests interaction of emulated channel |
| // being cleared with occlusion queries. |
| TEST_P(ImageTestES3, RGBXAHBImportOcclusionQueryNotCounted) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| GLQueryEXT query; |
| glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, query); |
| |
| // Create the Image |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kLinearColor, 4}}, &ahb, &ahbImage); |
| |
| GLTexture ahbTexture; |
| createEGLImageTargetTexture2D(ahbImage, ahbTexture); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ahbTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Perform a masked clear. Both the emulated clear and the masked clear should be performed, |
| // neither of which should contribute to the occlusion query. |
| const GLubyte kClearColor[] = {63, 127, 191, 55}; |
| glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE); |
| glClearColor(kClearColor[0] / 255.0f, kClearColor[1] / 255.0f, kClearColor[2] / 255.0f, |
| kClearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| |
| // RGBX doesn't have alpha, so readback should return 255. |
| const GLubyte kExpectedColor[] = {kClearColor[0], kLinearColor[1], kClearColor[2], 255}; |
| verifyResults2D(ahbTexture, kExpectedColor); |
| verifyResultAHB(ahb, {{kExpectedColor, 4}}); |
| |
| GLuint result = GL_TRUE; |
| glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_EXT, &result); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_GL_FALSE(result); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| // Test repeatedly importing and releasing AHBs into textures to replicate behavior where |
| // SurfaceFlinger optimistically imports AHBs but never actually ends up using them. Regression |
| // test to check that AHB releases are not left pending and kept alive to avoid running out of |
| // memory. |
| TEST_P(ImageTestES3, AHBImportReleaseStress) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Counters only available on Vulkan. |
| ANGLE_SKIP_TEST_IF(!IsVulkan()); |
| |
| const GLubyte kBlack[] = {0, 0, 0, 0}; |
| |
| glFinish(); |
| |
| GLPerfMonitor monitor; |
| glBeginPerfMonitorAMD(monitor); |
| |
| const uint64_t initialPendingSubmissionGarbageObjects = |
| getPerfCounters().pendingSubmissionGarbageObjects; |
| |
| for (int i = 0; i < 20; i++) |
| { |
| AHardwareBuffer *ahb; |
| EGLImageKHR ahbImage; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{kBlack, 4}}, |
| &ahb, &ahbImage); |
| |
| { |
| GLTexture ahbTexture; |
| glBindTexture(GL_TEXTURE_2D, ahbTexture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ahbImage); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Intentionally not doing anything which may explicitly flush operations on the AHB. |
| } |
| |
| eglDestroyImageKHR(getEGLWindow()->getDisplay(), ahbImage); |
| destroyAndroidHardwareBuffer(ahb); |
| } |
| |
| glEndPerfMonitorAMD(monitor); |
| |
| EXPECT_LE(getPerfCounters().pendingSubmissionGarbageObjects, |
| initialPendingSubmissionGarbageObjects + 10); |
| } |
| |
| // Test validation 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &yuvSource, |
| &yuvImage); |
| |
| GLTexture 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, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &rgbaSource, |
| &rgbaImage); |
| |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), yuvImage); |
| destroyAndroidHardwareBuffer(yuvSource); |
| |
| 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(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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, attribs, {{kSrgbColor, 4}}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| // Verify that the target texture has the expected color |
| verifyResultsExternalESSL3(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source multi-layered AHB EGL image, target 2D array texture |
| TEST_P(ImageTestES3, SourceAHBArrayTarget2DArray) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasEglImageArrayExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| constexpr size_t kDepth = 2; |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kDefaultAHBUsage)); |
| |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {}, &source, |
| &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2DArray(image, target); |
| |
| // Upload texture data |
| glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 1, 1, kDepth, GL_RGBA, GL_UNSIGNED_BYTE, |
| kLinearColor3D); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| for (size_t layer = 0; layer < kDepth; layer++) |
| { |
| // Expect that the target texture has the same color as the source texture |
| verifyResults2DArray(target, &kLinearColor3D[layer * 4], layer); |
| } |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source cubemap AHB EGL image, target cubemap texture |
| TEST_P(ImageTestES3, SourceAHBCubeTargetCube) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, kCubeFaceCount, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUCubeMap)); |
| |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource( |
| 1, 1, kCubeFaceCount, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUCubeMap, kDefaultAttribs, {}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_CUBE_MAP, target, nullptr); |
| |
| // Upload texture data |
| for (size_t faceIdx = 0; faceIdx < kCubeFaceCount; faceIdx++) |
| { |
| glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIdx, 0, 0, 0, 1, 1, GL_RGBA, |
| GL_UNSIGNED_BYTE, &kLinearColorCube[faceIdx * 4]); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| for (size_t faceIdx = 0; faceIdx < kCubeFaceCount; faceIdx++) |
| { |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsCube(target, &kLinearColorCube[faceIdx * 4], faceIdx); |
| } |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source cubemap array AHB EGL image, target cubemap array texture |
| TEST_P(ImageTestES31, SourceAHBCubeArrayTargetCubeArray) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!(getClientMajorVersion() >= 3 && getClientMinorVersion() >= 1)); |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt() || |
| !IsGLExtensionEnabled("GL_EXT_texture_cube_map_array")); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| constexpr size_t kDepth = kCubeFaceCount * 2; |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 1, 1, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUCubeMap)); |
| |
| // Create the Image without data so we don't need ANGLE_AHARDWARE_BUFFER_LOCK_PLANES_SUPPORT |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, kDepth, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUCubeMap, |
| kDefaultAttribs, {}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_CUBE_MAP_ARRAY, target, nullptr); |
| |
| // Upload texture data |
| for (size_t faceIdx = 0; faceIdx < kCubeFaceCount; faceIdx++) |
| { |
| glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, 0, 0, faceIdx, 1, 1, 1, GL_RGBA, |
| GL_UNSIGNED_BYTE, &kLinearColorCube[faceIdx * 4]); |
| glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, 0, 0, 11 - faceIdx, 1, 1, 1, GL_RGBA, |
| GL_UNSIGNED_BYTE, &kLinearColorCube[faceIdx * 4]); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| for (size_t faceIdx = 0; faceIdx < kCubeFaceCount; faceIdx++) |
| { |
| // Expect that the target texture has the same color as the source texture |
| verifyResultsCubeArray(target, &kLinearColorCube[faceIdx * 4], faceIdx, 0); |
| verifyResultsCubeArray(target, &kLinearColorCube[(5 - faceIdx) * 4], faceIdx, 1); |
| } |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Testing source 2D AHB with mipmap EGL image, target 2D texture with mipmap |
| TEST_P(ImageTestES3, SourceAHBMipTarget2DMip) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete)); |
| |
| // 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_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete, |
| kDefaultAttribs, {}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_2D, target, nullptr); |
| |
| // Upload texture data |
| // Set Mip level 0 to one color |
| const std::vector<GLColor> kRedData(4, GLColor::red); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, kRedData.data()); |
| |
| // Set Mip level 1 to a different color |
| const std::vector<GLColor> kGreenData(1, GLColor::green); |
| glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kGreenData.data()); |
| |
| // Use texture target bound to egl image as source and render to framebuffer |
| // Expect that the target texture has the same color as the corresponding mip level in the |
| // source texture |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |
| verifyResults2D(target, GLColor::red.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); |
| verifyResults2D(target, GLColor::green.data()); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // Test glGenerateMipmap and GL_EXT_EGL_image_storage interaction |
| TEST_P(ImageTestES3, SourceAHBMipTarget2DMipGenerateMipmap) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| ANGLE_SKIP_TEST_IF(!isAndroidHardwareBufferConfigurationSupported( |
| 2, 2, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete)); |
| |
| // 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_R8G8B8A8_UNORM, |
| kDefaultAHBUsage | kAHBUsageGPUMipMapComplete, |
| kDefaultAttribs, {}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_2D, target, nullptr); |
| |
| // Upload texture data |
| // Set Mip level 0 to one color |
| const std::vector<GLColor> kRedData(4, GLColor::red); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, kRedData.data()); |
| |
| // Set Mip level 1 to a different color |
| const std::vector<GLColor> kGreenData(1, GLColor::green); |
| glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kGreenData.data()); |
| |
| // Generate mipmap level 1 |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| // Use mipmap level 1 of texture target bound to egl image as source and render to framebuffer |
| // Expect that the target texture has the same color as the mip level 0 in the source texture |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); |
| verifyResults2D(target, GLColor::red.data()); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // 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/42263405) |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| 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, kDefaultAHBUsage, kDefaultAttribs, |
| {{reinterpret_cast<GLubyte *>(&depthStencilValue), 3}}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture 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()); |
| Source2DTargetRenderbuffer_helper(kColorspaceAttribs); |
| } |
| |
| void ImageTest::Source2DTargetRenderbuffer_helper(const EGLint *attribs) |
| { |
| |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| // Create sampling texture |
| GLTexture sampleTexture; |
| glBindTexture(GL_TEXTURE_2D, sampleTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, kLinearColor); |
| // 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 the attachment texture and image |
| GLTexture attachmentTexture; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, nullptr, |
| attachmentTexture, &image); |
| |
| // Create the renderbuffer |
| GLRenderbuffer renderbuffer; |
| createEGLImageTargetRenderbuffer(image, renderbuffer); |
| |
| // Verify that the render buffer has the expected color |
| verifyResultsRenderbufferWithClearAndDraw( |
| sampleTexture, renderbuffer, kLinearColor, |
| getExpected2DColorForAttribList(attribs, EglImageUsage::Rendering)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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) |
| { |
| 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(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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // 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, |
| {{kSrgbColor, 4}}, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| 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) |
| { |
| 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(getClientMajorVersion() < 3); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| 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()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| // Create an Image backed by a native client buffer allocated using |
| // EGL_ANDROID_create_native_client_buffer API |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; |
| createEGLImageANWBClientBufferSource(1, 1, 1, kNativeClientBufferAttribs_RGBA8_Renderbuffer, |
| attribs, {{kSrgbColor, 4}}, &image); |
| // We are locking AHB to initialize AHB with data. The lock is allowed to fail, and may fail if |
| // driver decided to allocate with framebuffer compression enabled. |
| ANGLE_SKIP_TEST_IF(image == EGL_NO_IMAGE_KHR); |
| |
| // Create the target |
| GLRenderbuffer target; |
| createEGLImageTargetRenderbuffer(image, target); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, target); |
| glClearColor(kLinearColor[0] / 255.0f, kLinearColor[1] / 255.0f, kLinearColor[2] / 255.0f, |
| kLinearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify that the render buffer has the expected color |
| verifyResultsRenderbuffer(target, |
| getExpected2DColorForAttribList(attribs, EglImageUsage::Rendering)); |
| |
| // Clean up |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor, source, |
| &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor, source, |
| &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternalESSL3(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kSrgbColorCube), |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResults2D(target, &getExpectedCubeColorForAttribList(attribs)[faceIdx * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| TEST_P(ImageTest, SourceCubeTargetRenderbuffer) |
| { |
| SourceCubeTargetRenderbuffer_helper(kDefaultAttribs); |
| } |
| |
| TEST_P(ImageTest, SourceCubeTargetRenderbuffer_Colorspace) |
| { |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| SourceCubeTargetRenderbuffer_helper(kColorspaceAttribs); |
| } |
| |
| void ImageTest::SourceCubeTargetRenderbuffer_helper(const EGLint *attribs) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt()); |
| |
| // http://anglebug.com/42261821 |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia()); |
| |
| for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kSrgbColorCube), |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, source, &image); |
| |
| // Create the target |
| GLRenderbuffer target; |
| createEGLImageTargetRenderbuffer(image, target); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, target); |
| glClearColor( |
| kLinearColorCube[faceIdx * 4 + 0] / 255.0f, kLinearColorCube[faceIdx * 4 + 1] / 255.0f, |
| kLinearColorCube[faceIdx * 4 + 2] / 255.0f, kLinearColorCube[faceIdx * 4 + 3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify that the render buffer has the expected color |
| verifyResultsRenderbuffer(target, &getExpectedCubeColorForAttribList( |
| attribs, EglImageUsage::Rendering)[faceIdx * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| // Upload sRGB color so that it is converted to linear when sampling. |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kSrgbColorCube), |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, &getExpectedCubeColorForAttribList(attribs)[faceIdx * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| // Upload sRGB color so that it is converted to linear when sampling. |
| createEGLImageCubemapTextureSource( |
| 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kSrgbColorCube), |
| sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternalESSL3(target, |
| &getExpectedCubeColorForAttribList(attribs)[faceIdx * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| TEST_P(ImageTest, Source3DTargetTexture) |
| { |
| Source3DTargetTexture_helper(default3DAttribs); |
| } |
| |
| TEST_P(ImageTest, Source3DTargetTexture_Colorspace) |
| { |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| Source3DTargetTexture_helper(colorspace3DAttribs); |
| } |
| |
| void ImageTest::Source3DTargetTexture_helper(EGLint *attribs) |
| { |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| // Upload sRGB color so that it is converted to linear when sampling. |
| attribs[kTextureZOffsetAttributeIndex] = static_cast<EGLint>(layer); |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor3D, |
| source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResults2D(target, &getExpected3DColorForAttribList(attribs)[layer * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| TEST_P(ImageTest, Source3DTargetRenderbuffer) |
| { |
| Source3DTargetRenderbuffer_helper(default3DAttribs); |
| } |
| |
| TEST_P(ImageTest, Source3DTargetRenderbuffer_Colorspace) |
| { |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| Source3DTargetRenderbuffer_helper(colorspace3DAttribs); |
| } |
| |
| void ImageTest::Source3DTargetRenderbuffer_helper(EGLint *attribs) |
| { |
| // 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/42261453 |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| |
| attribs[kTextureZOffsetAttributeIndex] = static_cast<EGLint>(layer); |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor3D, |
| source, &image); |
| |
| // Create the target |
| GLRenderbuffer target; |
| createEGLImageTargetRenderbuffer(image, target); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, target); |
| glClearColor(kLinearColor3D[layer * 4 + 0] / 255.0f, kLinearColor3D[layer * 4 + 1] / 255.0f, |
| kLinearColor3D[layer * 4 + 2] / 255.0f, |
| kLinearColor3D[layer * 4 + 3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify that the render buffer has the expected color |
| verifyResultsRenderbuffer( |
| target, &getExpected3DColorForAttribList(attribs, EglImageUsage::Rendering)[layer * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // Test 3D -> external texture EGL images. |
| TEST_P(ImageTest, Source3DTargetExternal) |
| { |
| Source3DTargetExternal_helper(default3DAttribs); |
| } |
| |
| TEST_P(ImageTest, Source3DTargetExternal_Colorspace) |
| { |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| Source3DTargetExternal_helper(colorspace3DAttribs); |
| } |
| |
| void ImageTest::Source3DTargetExternal_helper(EGLint *attribs) |
| { |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| // Upload sRGB color so that it is converted to linear when sampling. |
| attribs[kTextureZOffsetAttributeIndex] = static_cast<EGLint>(layer); |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor3D, |
| source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, &getExpected3DColorForAttribList(attribs)[layer * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // Test 3D -> external texture EGL images using ESSL3 shaders. |
| TEST_P(ImageTestES3, Source3DTargetExternalESSL3) |
| { |
| Source3DTargetExternalESSL3_helper(default3DAttribs); |
| } |
| |
| TEST_P(ImageTestES3, Source3DTargetExternalESSL3_Colorspace) |
| { |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB")); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| Source3DTargetExternalESSL3_helper(colorspace3DAttribs); |
| } |
| |
| void ImageTest::Source3DTargetExternalESSL3_helper(EGLint *attribs) |
| { |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| // Upload sRGB color so that it is converted to linear when sampling. |
| attribs[kTextureZOffsetAttributeIndex] = static_cast<EGLint>(layer); |
| createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kSrgbColor3D, |
| source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternalESSL3(target, &getExpected3DColorForAttribList(attribs)[layer * 4]); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| 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 |
| GLRenderbuffer source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, source, &image); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, source); |
| glClearColor(kSrgbColor[0] / 255.0f, kSrgbColor[1] / 255.0f, kSrgbColor[2] / 255.0f, |
| kSrgbColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResults2D(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLRenderbuffer source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, source, &image); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, source); |
| glClearColor(kSrgbColor[0] / 255.0f, kSrgbColor[1] / 255.0f, kSrgbColor[2] / 255.0f, |
| kSrgbColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternal(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLRenderbuffer source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, source, &image); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, source); |
| glClearColor(kSrgbColor[0] / 255.0f, kSrgbColor[1] / 255.0f, kSrgbColor[2] / 255.0f, |
| kSrgbColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Verify that the target texture has the expected color |
| verifyResultsExternalESSL3(target, getExpected2DColorForAttribList(attribs)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| 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()); |
| SourceRenderbufferTargetRenderbuffer_helper(kColorspaceAttribs); |
| } |
| |
| void ImageTest::SourceRenderbufferTargetRenderbuffer_helper(const EGLint *attribs) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); |
| |
| // Create the Image |
| GLRenderbuffer source; |
| EGLImageKHR image; |
| createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, source, &image); |
| |
| // Create the target |
| GLRenderbuffer target; |
| createEGLImageTargetRenderbuffer(image, target); |
| |
| // Create a framebuffer with renderbuffer attachment and clear it |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, target); |
| glClearColor(kLinearColor[0] / 255.0f, kLinearColor[1] / 255.0f, kLinearColor[2] / 255.0f, |
| kLinearColor[3] / 255.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify that the render buffer has the expected color |
| verifyResultsRenderbuffer(target, |
| getExpected2DColorForAttribList(attribs, EglImageUsage::Rendering)); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| void ImageTest::FixedRatedCompressionBasicHelper(const GLint *attribs) |
| { |
| constexpr size_t width = 16; |
| constexpr size_t height = 16; |
| GLTexture textureSource; |
| EGLImageKHR image; |
| EGLWindow *window = getEGLWindow(); |
| createEGLImage2DTextureStorage(width, height, GL_RGBA8, attribs, textureSource, &image); |
| |
| GLTexture textureAttachment; |
| createEGLImageTargetTextureStorage(image, GL_TEXTURE_2D, textureAttachment, attribs); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| ASSERT_GL_NO_ERROR(); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureAttachment, |
| 0); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0); |
| EXPECT_PIXEL_RECT_EQ(0, 0, width, height, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Test basic usage of extension GL_EXT_EGL_image_storage_compression |
| TEST_P(ImageTest, FixedRatedCompressionBasic) |
| { |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt() || !hasEglImageStorageCompressionExt()); |
| ANGLE_SKIP_TEST_IF(!hasTextureStorageCompressionExt()); |
| constexpr GLint kAttribList[3][3] = { |
| {GL_NONE, GL_NONE, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, GL_NONE}, |
| }; |
| for (const GLint *attribs : kAttribList) |
| { |
| FixedRatedCompressionBasicHelper(attribs); |
| } |
| } |
| |
| void ImageTest::FixedRatedCompressionImageAttribCheck(EGLImageKHR image, |
| const GLint *attribs, |
| const GLint expectResult) |
| { |
| GLTexture textureAttachment; |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_2D, textureAttachment); |
| glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, image, attribs); |
| ASSERT_GL_ERROR(expectResult); |
| } |
| |
| // Test whether the result is expected when the attributes mismatched with source |
| TEST_P(ImageTest, FixedRatedCompressionMixedAttrib) |
| { |
| ANGLE_SKIP_TEST_IF(!hasEglImageStorageExt() || !hasEglImageStorageCompressionExt()); |
| ANGLE_SKIP_TEST_IF(!hasTextureStorageCompressionExt()); |
| constexpr size_t width = 16; |
| constexpr size_t height = 16; |
| EGLWindow *window = getEGLWindow(); |
| constexpr GLint textureAttribList[][3] = { |
| {GL_NONE, GL_NONE, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, GL_NONE}, |
| }; |
| constexpr GLint imageAttribList[][3] = { |
| {GL_NONE, GL_NONE, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT, GL_NONE}, |
| }; |
| |
| constexpr GLint invalidImageAttribList[][3] = { |
| {GL_SURFACE_COMPRESSION_EXT, GL_SURFACE_COMPRESSION_EXT, GL_NONE}, |
| {GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT, GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT, |
| GL_NONE}, |
| }; |
| |
| for (const GLint *textureAttribs : textureAttribList) |
| { |
| GLTexture textureSource; |
| EGLImageKHR image; |
| bool isFixRatedCompressed; |
| createEGLImage2DTextureStorage(width, height, GL_RGBA8, textureAttribs, textureSource, |
| &image); |
| /* Query compression rate */ |
| GLint compressRate = GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT; |
| glGetTexParameteriv(GL_TEXTURE_2D, GL_SURFACE_COMPRESSION_EXT, &compressRate); |
| ASSERT_GL_NO_ERROR(); |
| isFixRatedCompressed = (compressRate == GL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT || |
| (compressRate >= GL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT && |
| compressRate <= GL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT)); |
| |
| for (const GLint *attribs : imageAttribList) |
| { |
| if (isFixRatedCompressed && attribs[0] == GL_SURFACE_COMPRESSION_EXT && |
| attribs[1] == GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT) |
| { |
| FixedRatedCompressionImageAttribCheck(image, attribs, GL_INVALID_OPERATION); |
| } |
| else |
| { |
| FixedRatedCompressionImageAttribCheck(image, attribs, GL_NO_ERROR); |
| } |
| } |
| |
| for (const GLint *attribs : invalidImageAttribList) |
| { |
| FixedRatedCompressionImageAttribCheck(image, attribs, GL_INVALID_VALUE); |
| } |
| |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create multiple targets |
| GLTexture targetTexture; |
| createEGLImageTargetTexture2D(image, targetTexture); |
| |
| GLRenderbuffer targetRenderbuffer; |
| createEGLImageTargetRenderbuffer(image, targetRenderbuffer); |
| |
| // Delete the source texture |
| source.reset(); |
| |
| // 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); |
| } |
| |
| 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/42262494 |
| 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/42261962 |
| 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]), |
| }; |
| |
| GLTexture 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 |
| GLTexture 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); |
| |
| GLRenderbuffer 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); |
| } |
| } |
| |
| // 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/42261452 |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create the target |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create the target |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| |
| GLubyte originalData[4] = {255, 0, 255, 255}; |
| GLubyte updateData[4] = {0, 255, 0, 255}; |
| |
| // Create the Image |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Render to the target texture |
| GLFramebuffer 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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/42261452 |
| 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(2, 2, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create the target |
| GLTexture 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create multiple targets |
| GLTexture targetTexture; |
| createEGLImageTargetTexture2D(image, targetTexture); |
| |
| GLRenderbuffer 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 |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Check that the external texture is successfully updated when only glTexSubImage2D is called. |
| TEST_P(ImageTest, AHBUpdatedExternalTexture) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| 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, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{originalData, bytesPerPixel}}, &source, &image); |
| |
| // Create target |
| GLTexture 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 |
| targetTexture.reset(); |
| |
| // 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 |
| GLTexture targetTexture2; |
| createEGLImageTargetTexture2D(image, targetTexture2); |
| |
| // Expect that the target have the update data |
| verifyResults2D(targetTexture2, updateData); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| destroyAndroidHardwareBuffer(source); |
| } |
| |
| // 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 |
| GLTexture source; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source, &image); |
| |
| // Create texture & bind to Image |
| GLTexture 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(); |
| } |
| |
| // Check that create a source cube texture and then redefine the same target texture with each face |
| // of source cube texture renders correctly |
| TEST_P(ImageTest, SourceCubeAndSameTargetTextureWithEachCubeFace) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| |
| // Create a source cube map texture |
| GLTexture sourceTexture; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, sourceTexture); |
| uint8_t *data = reinterpret_cast<uint8_t *>(kLinearColorCube); |
| size_t dataStride = sizeof(GLubyte) * 4; |
| for (GLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| glTexImage2D(faceIdx + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 1, 1, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, data + (faceIdx * dataStride)); |
| } |
| 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(); |
| |
| EGLImageKHR images[6]; |
| GLTexture targetTexture; |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| |
| for (GLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| // Create the Image with EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR |
| images[faceIdx] = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), |
| EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, |
| reinterpretHelper<EGLClientBuffer>(sourceTexture), kDefaultAttribs); |
| |
| // Create a target texture from the image |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, images[faceIdx]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| // Expect that the target texture has the same color as the source texture |
| verifyResults2D(targetTexture, &kLinearColorCube[faceIdx * 4]); |
| } |
| |
| // Clean up |
| for (GLenum faceIdx = 0; faceIdx < 6; faceIdx++) |
| { |
| eglDestroyImageKHR(window->getDisplay(), images[faceIdx]); |
| } |
| } |
| |
| // Case for testing External Texture support in MEC. |
| // To run this test with the right capture setting, make sure to set these environment variables: |
| // |
| // For Linux: |
| // export ANGLE_CAPTURE_FRAME_START=2 |
| // export ANGLE_CAPTURE_FRAME_END=2 |
| // export ANGLE_CAPTURE_LABEL=external_textures |
| // export ANGLE_CAPTURE_OUT_DIR=[PATH_TO_ANGLE]/src/tests/restricted_traces/external_textures/ |
| // |
| // For Android: |
| // adb shell setprop debug.angle.capture.frame_start 2 |
| // adb shell setprop debug.angle.capture.frame_end 2 |
| // adb shell setprop debug.angle.capture.label external_textures |
| // adb shell setprop debug.angle.capture.out_dir /data/data/externaltextures/angle_capture/ |
| TEST_P(ImageTest, AppTraceExternalTextureDefaultAttribs) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasExternalESSL3Ext()); |
| |
| constexpr EGLint attribs[] = { |
| EGL_IMAGE_PRESERVED, |
| EGL_TRUE, |
| EGL_NONE, |
| }; |
| |
| externalTextureTracerTestHelper(attribs); |
| } |
| |
| // Same as AppTraceExternalTextureUseCase, except we will pass additional attrib_list values in |
| // EGLAttrib* for eglCreateImageKHR calls |
| TEST_P(ImageTest, AppTraceExternalTextureOtherAttribs) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasExternalESSL3Ext()); |
| |
| constexpr EGLint attribs[] = { |
| EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_GL_TEXTURE_LEVEL, 0, EGL_NONE, |
| }; |
| |
| externalTextureTracerTestHelper(attribs); |
| } |
| |
| // Same as AppTraceExternalTextureUseCase, except we will pass nullptr as EGLAttrib* for |
| // eglCreateImageKHR calls |
| TEST_P(ImageTest, AppTraceExternalTextureNullAttribs) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || |
| !hasExternalESSL3Ext()); |
| |
| externalTextureTracerTestHelper(nullptr); |
| } |
| |
| // Alternate case for testing External Texture (created with AHB) support in MEC. |
| // Make sure to use the following environment variables for the right capture setting on Android: |
| // |
| // adb shell setprop debug.angle.capture.frame_start 2 |
| // adb shell setprop debug.angle.capture.frame_end 2 |
| // adb shell setprop debug.angle.capture.label AHB_textures |
| // adb shell setprop debug.angle.capture.out_dir /data/data/AHBtextures/angle_capture/ |
| TEST_P(ImageTest, AppTraceExternalTextureWithAHBUseCase) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| |
| GLubyte data[4] = {7, 51, 197, 231}; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{data, 4}}, |
| &source, &image); |
| |
| // Create a texture target to bind the egl image & disable mipmapping |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Calls On EndFrame(), with MidExecutionSetup to restore external target texture above |
| EGLDisplay display = getEGLWindow()->getDisplay(); |
| EGLSurface surface = getEGLWindow()->getSurface(); |
| eglSwapBuffers(display, surface); |
| |
| // Create another eglImage with another associated texture |
| // Draw using the eglImage target texture created in frame 1 |
| AHardwareBuffer *source2; |
| EGLImageKHR image2; |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, {{data, 4}}, |
| &source2, &image2); |
| |
| // Create another texture target to bind the egl image & disable mipmapping |
| GLTexture target2; |
| createEGLImageTargetTextureExternal(image, target2); |
| |
| glUseProgram(mTextureExternalProgram); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| |
| // Calls On EndFrame() to save the gl calls creating external texture target2; |
| // We use this as a reference to check the gl calls we restore for GLTexture target |
| // in MidExecutionSetup |
| eglSwapBuffers(display, surface); |
| |
| // Draw a quad with the GLTexture target2 |
| glUseProgram(mTextureExternalProgram); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, target2); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| |
| drawQuad(mTextureExternalProgram, "position", 0.5f); |
| |
| eglSwapBuffers(display, surface); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| eglDestroyImageKHR(window->getDisplay(), image2); |
| } |
| |
| // Thread 0 creates the AHB and binds it to a texture, thread 1 uses it without synchronization. |
| TEST_P(ImageTest, MultithreadedAHBImportAndUseAsTexture) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| GLuint sharedTexture = 0; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| |
| constexpr GLubyte kInitialData[4] = {127, 63, 191, 255}; |
| |
| std::mutex mutex; |
| std::condition_variable condVar; |
| |
| enum class Step |
| { |
| Start, |
| Thread0CreatedTexture, |
| Thread1UsedTexture, |
| Finish, |
| Abort, |
| }; |
| Step currentStep = Step::Start; |
| |
| auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kInitialData, 4}}, &source, &image); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLTexture texture; |
| sharedTexture = texture; |
| |
| createEGLImageTargetTextureExternal(image, sharedTexture); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait for the other thread to use it. |
| threadSynchronization.nextStep(Step::Thread0CreatedTexture); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsedTexture)); |
| |
| eglDestroyImageKHR(window->getDisplay(), image); |
| texture.reset(); |
| |
| threadSynchronization.nextStep(Step::Finish); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| // Wait for thread 0 to set up |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatedTexture)); |
| |
| // Sample from the texture |
| ANGLE_GL_PROGRAM(drawTexture, getVS(), getTextureExternalFS()); |
| glUseProgram(drawTexture); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, sharedTexture); |
| glUniform1i(mTextureExternalUniformLocation, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(drawTexture, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make a submission |
| EXPECT_PIXEL_COLOR_NEAR( |
| 0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1); |
| |
| // Notify the other thread that it's finished using the texture. |
| threadSynchronization.nextStep(Step::Thread1UsedTexture); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish)); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| std::array<LockStepThreadFunc, 2> threadFuncs = { |
| std::move(thread0), |
| std::move(thread1), |
| }; |
| |
| RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data()); |
| |
| ASSERT_NE(currentStep, Step::Abort); |
| } |
| |
| // Thread 0 creates the AHB and binds it to a renderbuffer, thread 1 uses it without |
| // synchronization. |
| TEST_P(ImageTest, MultithreadedAHBImportAndUseAsRenderbuffer) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!hasAhbLockPlanesSupport()); |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| GLuint sharedRenderbuffer = 0; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| |
| constexpr GLubyte kInitialData[4] = {127, 63, 191, 255}; |
| |
| std::mutex mutex; |
| std::condition_variable condVar; |
| |
| enum class Step |
| { |
| Start, |
| Thread0CreatedRenderbuffer, |
| Thread1UsedRenderbuffer, |
| Finish, |
| Abort, |
| }; |
| Step currentStep = Step::Start; |
| |
| auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| createEGLImageAndroidHardwareBufferSource(1, 1, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| kDefaultAHBUsage, kDefaultAttribs, |
| {{kInitialData, 4}}, &source, &image); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLRenderbuffer renderbuffer; |
| sharedRenderbuffer = renderbuffer; |
| |
| createEGLImageTargetRenderbuffer(image, sharedRenderbuffer); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait for the other thread to use it. |
| threadSynchronization.nextStep(Step::Thread0CreatedRenderbuffer); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsedRenderbuffer)); |
| |
| eglDestroyImageKHR(window->getDisplay(), image); |
| renderbuffer.reset(); |
| |
| threadSynchronization.nextStep(Step::Finish); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| // Wait for thread 0 to set up |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatedRenderbuffer)); |
| |
| // Blend into the renderbuffer |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| sharedRenderbuffer); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make a submission |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, kInitialData[1], kInitialData[2], 255), 1); |
| |
| // Notify the other thread that it's finished using the renderbuffer. |
| threadSynchronization.nextStep(Step::Thread1UsedRenderbuffer); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish)); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| std::array<LockStepThreadFunc, 2> threadFuncs = { |
| std::move(thread0), |
| std::move(thread1), |
| }; |
| |
| RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data()); |
| |
| ASSERT_NE(currentStep, Step::Abort); |
| } |
| |
| void ImageTest::framebufferAttachmentDeletedWhileInUseHelper(bool useTextureAttachment, |
| bool deleteSourceTextureLast) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); |
| ANGLE_SKIP_TEST_IF(useTextureAttachment && !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!useTextureAttachment && !hasRenderbufferExt()); |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| |
| EGLWindow *window = getEGLWindow(); |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; |
| |
| std::mutex mutex; |
| std::condition_variable condVar; |
| EGLSyncKHR sync; |
| |
| enum class Step |
| { |
| Start, |
| Thread0CreatedImage, |
| Thread1UsedImage, |
| Finish, |
| Abort, |
| }; |
| Step currentStep = Step::Start; |
| |
| EXPECT_EGL_TRUE(window->makeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| |
| // This thread will use window context |
| std::thread thread0([&]() { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| window->makeCurrent(); |
| |
| // Create the Image |
| GLTexture source; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| kLinearColor, source, &image); |
| |
| sync = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait thread 1 finish using the Image |
| threadSynchronization.nextStep(Step::Thread0CreatedImage); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsedImage)); |
| |
| if (!deleteSourceTextureLast) |
| { |
| // Delete "source" texture first - image buffer will be deleted with the "image" |
| source.reset(); |
| } |
| |
| // Destroy Image |
| eglDestroyImageKHR(window->getDisplay(), image); |
| |
| if (deleteSourceTextureLast) |
| { |
| // Delete "source" texture last - this will delete image buffer |
| source.reset(); |
| } |
| |
| threadSynchronization.nextStep(Step::Finish); |
| |
| EXPECT_EGL_TRUE(window->makeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }); |
| |
| // This thread will use non Shared context |
| auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatedImage)); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| eglWaitSyncKHR(dpy, sync, 0); |
| |
| // Create the target and set up a framebuffer to render into the Image |
| GLFramebuffer fbo; |
| GLTexture targetTexture; |
| GLRenderbuffer targetRenderbuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| if (useTextureAttachment) |
| { |
| createEGLImageTargetTexture2D(image, targetTexture); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| targetTexture, 0); |
| } |
| else |
| { |
| createEGLImageTargetRenderbuffer(image, targetRenderbuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| targetRenderbuffer); |
| } |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Test that framebuffer has source content |
| EXPECT_PIXEL_EQ(0, 0, kLinearColor[0], kLinearColor[1], kLinearColor[2], kLinearColor[3]); |
| |
| // Create additional target texture |
| GLTexture targetTexture2; |
| createEGLImageTargetTexture2D(image, targetTexture2); |
| |
| // Enable additive blend |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Draw Red quad into the framebuffer |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Delete "targetTexture2" that may affect RenderPass because it uses same Image |
| targetTexture2.reset(); |
| |
| // Clear previous draw |
| glClearColor(128 / 255.0f, 128 / 255.0f, 128 / 255.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Draw Green quad into the framebuffer |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Test that clear and second draw worked as expected |
| EXPECT_PIXEL_EQ(0, 0, 128, 255, 128, 255); |
| |
| // Draw again to open RenderPass after the read pixels |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Delete resources |
| fbo.reset(); |
| targetTexture.reset(); |
| targetRenderbuffer.reset(); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait thread 0 destroys the Image and source |
| threadSynchronization.nextStep(Step::Thread1UsedImage); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish)); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| std::array<LockStepThreadFunc, 1> threadFuncs = { |
| std::move(thread1), |
| }; |
| |
| RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data()); |
| thread0.join(); |
| |
| window->makeCurrent(); |
| |
| ASSERT_NE(currentStep, Step::Abort); |
| } |
| |
| // Testing Target 2D Texture deleted while still used in the RenderPass (Image destroyed last). |
| TEST_P(ImageTest, TargetTexture2DDeletedWhileInUse) |
| { |
| framebufferAttachmentDeletedWhileInUseHelper(true, false); |
| } |
| |
| // Testing Target 2D Texture deleted while still used in the RenderPass (Source deleted last). |
| TEST_P(ImageTest, TargetTexture2DDeletedWhileInUse2) |
| { |
| framebufferAttachmentDeletedWhileInUseHelper(true, true); |
| } |
| |
| // Testing Target Renderbuffer deleted while still used in the RenderPass (Image destroyed last). |
| TEST_P(ImageTest, TargetRenderbufferDeletedWhileInUse) |
| { |
| framebufferAttachmentDeletedWhileInUseHelper(false, false); |
| } |
| |
| // Testing Target Renderbuffer deleted while still used in the RenderPass (Source deleted last). |
| TEST_P(ImageTest, TargetRenderbufferDeletedWhileInUse2) |
| { |
| framebufferAttachmentDeletedWhileInUseHelper(false, true); |
| } |
| |
| void ImageTest::framebufferResolveAttachmentDeletedWhileInUseHelper(bool useTextureAttachment, |
| bool deleteSourceTextureLast) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); |
| ANGLE_SKIP_TEST_IF(useTextureAttachment && !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!useTextureAttachment && !hasRenderbufferExt()); |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| |
| EGLWindow *window = getEGLWindow(); |
| EGLImageKHR image = EGL_NO_IMAGE_KHR; |
| |
| std::mutex mutex; |
| std::condition_variable condVar; |
| EGLSyncKHR sync; |
| |
| enum class Step |
| { |
| Start, |
| Thread0CreatedImage, |
| Thread1UsedImage, |
| Finish, |
| Abort, |
| }; |
| Step currentStep = Step::Start; |
| |
| EXPECT_EGL_TRUE(window->makeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| |
| // This thread will use window context |
| std::thread thread0([&]() { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| window->makeCurrent(); |
| |
| // Create the Image |
| GLTexture source; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| kLinearColor, source, &image); |
| |
| sync = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait thread 1 finish using the Image |
| threadSynchronization.nextStep(Step::Thread0CreatedImage); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsedImage)); |
| |
| if (!deleteSourceTextureLast) |
| { |
| // Delete "source" texture first - image buffer will be deleted with the "image" |
| source.reset(); |
| } |
| |
| // Destroy Image |
| eglDestroyImageKHR(window->getDisplay(), image); |
| |
| if (deleteSourceTextureLast) |
| { |
| // Delete "source" texture last - this will delete image buffer |
| source.reset(); |
| } |
| |
| threadSynchronization.nextStep(Step::Finish); |
| |
| EXPECT_EGL_TRUE(window->makeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }); |
| |
| // This thread will use non Shared context |
| auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatedImage)); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| eglWaitSyncKHR(dpy, sync, 0); |
| |
| // Create the target and set up a framebuffer to render into the Image |
| GLFramebuffer fbo; |
| GLTexture targetTexture; |
| GLRenderbuffer targetRenderbuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| if (useTextureAttachment) |
| { |
| createEGLImageTargetTexture2D(image, targetTexture); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| targetTexture, 0); |
| } |
| else |
| { |
| createEGLImageTargetRenderbuffer(image, targetRenderbuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| targetRenderbuffer); |
| } |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Test that framebuffer has source content |
| EXPECT_PIXEL_EQ(0, 0, kLinearColor[0], kLinearColor[1], kLinearColor[2], kLinearColor[3]); |
| |
| // Create additional target texture |
| GLTexture targetTexture2; |
| createEGLImageTargetTexture2D(image, targetTexture2); |
| |
| // Create MSAA framebuffer |
| GLTexture msaaColor; |
| glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaColor); |
| glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, 1, 1, false); |
| |
| GLFramebuffer msaaFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, |
| msaaColor, 0); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw Red quad into the MSAA framebuffer |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Resolve into image |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Delete "targetTexture2" that may affect RenderPass because it uses same Image |
| targetTexture2.reset(); |
| |
| // Start another render pass and blend into the image. |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Test that resolve and draw worked |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow); |
| |
| // Draw again to open RenderPass after the read pixels |
| glDisable(GL_BLEND); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Delete resources |
| fbo.reset(); |
| targetTexture.reset(); |
| targetRenderbuffer.reset(); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Wait thread 0 destroys the Image and source |
| threadSynchronization.nextStep(Step::Thread1UsedImage); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish)); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| std::array<LockStepThreadFunc, 1> threadFuncs = { |
| std::move(thread1), |
| }; |
| |
| RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data()); |
| thread0.join(); |
| |
| window->makeCurrent(); |
| |
| ASSERT_NE(currentStep, Step::Abort); |
| } |
| |
| // Test whether the dimension size of the target GL_TEXTURE_EXTERNAL_OES is as expected. |
| TEST_P(ImageTestES31, QueryDimFromExternalTex) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() || !hasExternalExt()); |
| |
| // Create the Image |
| GLTexture source; |
| GLsizei src_w = 1, src_h = 1, qw = 0, qh = 0; |
| EGLImageKHR image; |
| createEGLImage2DTextureSource(src_w, src_h, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, |
| kSrgbColor, source, &image); |
| |
| // Create the target |
| GLTexture target; |
| createEGLImageTargetTextureExternal(image, target); |
| |
| // Querying the dimensions should work |
| glGetTexLevelParameteriv(GL_TEXTURE_EXTERNAL_OES, 0, GL_TEXTURE_WIDTH, &qw); |
| EXPECT_EQ(qw, src_w); |
| glGetTexLevelParameteriv(GL_TEXTURE_EXTERNAL_OES, 0, GL_TEXTURE_HEIGHT, &qh); |
| EXPECT_EQ(qh, src_h); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Testing Target 2D Texture deleted while still used in the RenderPass as resolve attachment (Image |
| // destroyed last). |
| TEST_P(ImageTestES31, TargetTexture2DDeletedWhileInUseAsResolve) |
| { |
| framebufferResolveAttachmentDeletedWhileInUseHelper(true, false); |
| } |
| |
| // Testing Target 2D Texture deleted while still used in the RenderPass as resolve attachment |
| // (Source deleted last). |
| TEST_P(ImageTestES31, TargetTexture2DDeletedWhileInUseAsResolve2) |
| { |
| framebufferResolveAttachmentDeletedWhileInUseHelper(true, true); |
| } |
| |
| // Testing Target Renderbuffer deleted while still used in the RenderPass as resolve attachment |
| // (Image destroyed last). |
| TEST_P(ImageTestES31, TargetRenderbufferDeletedWhileInUseAsResolve) |
| { |
| framebufferResolveAttachmentDeletedWhileInUseHelper(false, false); |
| } |
| |
| // Testing Target Renderbuffer deleted while still used in the RenderPass as resolve attachment |
| // (Source deleted last). |
| TEST_P(ImageTestES31, TargetRenderbufferDeletedWhileInUseAsResolve2) |
| { |
| framebufferResolveAttachmentDeletedWhileInUseHelper(false, true); |
| } |
| |
| // Test that the AHB can be used in some way in GL, then accessed by foreign entity, then used again |
| // by GL. This makes sure transitions in and out of the FOREIGN Vulkan queue are correctly |
| // implemented. |
| void ImageTest::useAHBByGLThenForeignThenGLHelper( |
| std::function<void(const GLTexture &, uint32_t, uint32_t)> firstUse, |
| std::function<void(const GLTexture &, uint32_t, uint32_t)> secondUse) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| |
| constexpr uint32_t kWidth = 53; |
| constexpr uint32_t kHeight = 37; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource( |
| kWidth, kHeight, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kDefaultAHBUsage, |
| kDefaultAttribs, {}, &source, &image); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| glViewport(0, 0, kWidth, kHeight); |
| |
| // Use the image in GL once. |
| firstUse(target, kWidth, kHeight); |
| |
| // In between uses, overwrite parts of the image. |
| // First, wait for first use to finish. |
| EGLWindow *window = getEGLWindow(); |
| EGLSyncKHR fence = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, NULL); |
| glFlush(); |
| EGLint result = eglClientWaitSyncKHR(window->getDisplay(), fence, 0, 1'000'000'000); |
| ASSERT_EQ(result, EGL_CONDITION_SATISFIED_KHR); |
| ASSERT_EGL_TRUE(eglDestroySyncKHR(window->getDisplay(), fence)); |
| |
| // Then overwrite a quarter of the image with some color |
| std::vector<GLubyte> data((kWidth / 2) * (kHeight / 2) * 4, 99); |
| writeAHBData(source, kWidth / 2, kHeight / 2, 1, false, {{data.data(), 4}}); |
| |
| // And use the image again in GL. |
| secondUse(target, kWidth, kHeight); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test draw, use in foreign, then draw again |
| TEST_P(ImageTest, DrawForeignDraw) |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, texture); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(width / 4, 0, width / 2, height); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| // Expect the following: |
| // |
| // +-----+-----+-----+-----+ |
| // | | | | | |
| // | 99 | 99 | G | G | |
| // | | + | + | | |
| // | | R | R | | |
| // | | | | | |
| // +-----+-----+-----+ | |
| // | | | | |
| // | G | G + R | G | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 4, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width / 4, height - height / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(width / 4, 0, width / 2 - width / 4, height / 2, |
| GLColor(255, 99, 99, 255)); |
| EXPECT_PIXEL_RECT_EQ(width / 4, height / 2, width / 2, height - height / 2, |
| GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 2, 0, width / 2 - width / 4, height / 2, GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 4 + width / 2, 0, width - width / 2 - width / 4, height, |
| GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test draw, use in foreign, then blit |
| TEST_P(ImageTestES3, DrawForeignBlit) |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| GLFramebuffer otherFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, otherFbo); |
| |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glClearColor(0, 0, 1, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| |
| glBlitFramebuffer(width / 4, 0, width / 2 + width / 4, height, width / 4, 0, |
| width / 2 + width / 4, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); |
| |
| // Expect the following: |
| // |
| // +-----+-----------+-----+ |
| // | | | | |
| // | 99 | B | G | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+ | | |
| // | | | | |
| // | G | | | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 4, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width / 4, height - height / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(width / 4, 0, width / 2 - width / 4, height, GLColor::blue); |
| EXPECT_PIXEL_RECT_EQ(width / 4 + width / 2, 0, width - width / 2 - width / 4, height, |
| GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test draw, readback, use in foreign, then draw again |
| TEST_P(ImageTest, DrawReadbackForeignDraw) |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| // A second usage of the same image |
| EXPECT_PIXEL_RECT_EQ(0, 0, width, height, GLColor::green); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, texture); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(width / 4, 0, width / 2, height); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| // Expect the following: |
| // |
| // +-----+-----+-----+-----+ |
| // | | | | | |
| // | 99 | 99 | G | G | |
| // | | + | + | | |
| // | | R | R | | |
| // | | | | | |
| // +-----+-----+-----+ | |
| // | | | | |
| // | G | G + R | G | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 4, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width / 4, height - height / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(width / 4, 0, width / 2 - width / 4, height / 2, |
| GLColor(255, 99, 99, 255)); |
| EXPECT_PIXEL_RECT_EQ(width / 4, height / 2, width / 2, height - height / 2, |
| GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 2, 0, width / 2 - width / 4, height / 2, GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 4 + width / 2, 0, width - width / 2 - width / 4, height, |
| GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test draw, use in foreign, then readback |
| TEST_P(ImageTest, DrawForeignReadback) |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, texture); |
| |
| // Expect the following: |
| // |
| // +-----------+-----------+ |
| // | | | |
| // | 99 | | |
| // | | | |
| // | | | |
| // | | | |
| // +-----------+ | |
| // | | |
| // | G | |
| // | | |
| // | | |
| // | | |
| // +-----------------------+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 2, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(width / 2, 0, width - width / 2, height / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width, height - height / 2, GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test use as resolve attachment, use in foreign, then draw again |
| TEST_P(ImageTestES3, ResolveForeignDraw) |
| { |
| GLFramebuffer fbo; |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| GLRenderbuffer color; |
| glBindRenderbuffer(GL_RENDERBUFFER, color); |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, width, height); |
| |
| GLFramebuffer msaaFbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFbo); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, |
| 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); |
| |
| glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, |
| GL_NEAREST); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, texture); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(width / 4, 0, width / 2, height); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| // Expect the following: |
| // |
| // +-----+-----+-----+-----+ |
| // | | | | | |
| // | 99 | 99 | G | G | |
| // | | + | + | | |
| // | | R | R | | |
| // | | | | | |
| // +-----+-----+-----+ | |
| // | | | | |
| // | G | G + R | G | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 4, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width / 4, height - height / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(width / 4, 0, width / 2 - width / 4, height / 2, |
| GLColor(255, 99, 99, 255)); |
| EXPECT_PIXEL_RECT_EQ(width / 4, height / 2, width / 2, height - height / 2, |
| GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 2, 0, width / 2 - width / 4, height / 2, GLColor::yellow); |
| EXPECT_PIXEL_RECT_EQ(width / 4 + width / 2, 0, width - width / 2 - width / 4, height, |
| GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test upload, use in foreign, then draw |
| TEST_P(ImageTest, UploadForeignDraw) |
| { |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| std::vector<GLColor> data(width * height, GLColor::blue); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, |
| data.data()); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, width, height, GLColor::blue); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(width / 4, 0, width / 2, height); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| |
| // Expect the following: |
| // |
| // +-----+-----+-----+-----+ |
| // | | | | | |
| // | 99 | 99 | B | B | |
| // | | + | + | | |
| // | | R | R | | |
| // | | | | | |
| // +-----+-----+-----+ | |
| // | | | | |
| // | B | B + R | B | |
| // | | | | |
| // | | | | |
| // | | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 4, height / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width / 4, height - height / 2, GLColor::blue); |
| EXPECT_PIXEL_RECT_EQ(width / 4, 0, width / 2 - width / 4, height / 2, |
| GLColor(255, 99, 99, 255)); |
| EXPECT_PIXEL_RECT_EQ(width / 4, height / 2, width / 2, height - height / 2, |
| GLColor::magenta); |
| EXPECT_PIXEL_RECT_EQ(width / 2, 0, width / 2 - width / 4, height / 2, GLColor::magenta); |
| EXPECT_PIXEL_RECT_EQ(width / 4 + width / 2, 0, width - width / 2 - width / 4, height, |
| GLColor::blue); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test sample in FS, use in foreign, then sample again in VS |
| TEST_P(ImageTestES3, SampleForeignSample) |
| { |
| GLTexture color; |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| auto first = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| std::vector<GLColor> data(width * height, GLColor::blue); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, |
| data.data()); |
| |
| ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| glUseProgram(drawTexture); |
| GLint texLocation = glGetUniformLocation(drawTexture, essl1_shaders::Texture2DUniform()); |
| ASSERT_NE(-1, texLocation); |
| glUniform1i(texLocation, 0); |
| drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, width, height, GLColor::blue); |
| }; |
| auto second = [&](const GLTexture &texture, uint32_t width, uint32_t height) { |
| glBindTexture(GL_TEXTURE_2D, texture); |
| |
| constexpr char kVS[] = R"(precision highp float; |
| uniform highp sampler2D tex; |
| attribute vec2 position; |
| varying vec4 color; |
| void main() |
| { |
| color = texture2D(tex, position * 0.5 + 0.5); |
| gl_Position = vec4(position, 0, 1); |
| })"; |
| |
| constexpr char kFS[] = R"(precision highp float; |
| varying vec4 color; |
| void main() |
| { |
| gl_FragColor = color; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| GLint texLocation = glGetUniformLocation(program, "tex"); |
| ASSERT_NE(-1, texLocation); |
| glUniform1i(texLocation, 0); |
| |
| const std::array<Vector3, 12> kVertices = {{ |
| Vector3(-1.0f, -1.0f, 0.5f), |
| Vector3(-1.0f, -0.1f, 0.5f), |
| Vector3(-0.1f, -0.1f, 0.5f), |
| |
| Vector3(-1.0f, -1.0f, 0.5f), |
| Vector3(-0.1f, -0.1f, 0.5f), |
| Vector3(-0.1f, -1.0f, 0.5f), |
| |
| Vector3(0.1f, -1.0f, 0.5f), |
| Vector3(0.1f, -0.1f, 0.5f), |
| Vector3(1.0f, -0.1f, 0.5f), |
| |
| Vector3(0.1f, -1.0f, 0.5f), |
| Vector3(1.0f, -0.1f, 0.5f), |
| Vector3(1.0f, -1.0f, 0.5f), |
| }}; |
| |
| GLint positionLocation = glGetAttribLocation(program, "position"); |
| ASSERT_NE(-1, positionLocation); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, kVertices.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glClearColor(0, 255, 0, 255); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawArrays(GL_TRIANGLES, 0, 12); |
| |
| // Expect the following: |
| // |
| // +---------+---+---------+ |
| // | | | | |
| // | 99 | | B | |
| // | | | | |
| // | | | | |
| // |---------+ +---------| |
| // | | |
| // | | |
| // | G | |
| // | | |
| // | | |
| // | | |
| // +-----------------------+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, width / 2 - width / 10 - 1, height / 2 - height / 10 - 1, |
| GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(width / 2 + width / 10 + 1, 0, width - width / 2 - width / 10 - 1, |
| height / 2 - 1, GLColor::blue); |
| EXPECT_PIXEL_RECT_EQ(0, height / 2, width, height - height / 2, GLColor::green); |
| }; |
| |
| useAHBByGLThenForeignThenGLHelper(first, second); |
| } |
| |
| // Test draw, use in foreign, readback in another context, use in foreign, then sample in the |
| // original context. |
| TEST_P(ImageTestES3, MultithreadedDrawForeignReadbackForeignSample) |
| { |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport()); |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| constexpr uint32_t kWidth = 53; |
| constexpr uint32_t kHeight = 37; |
| |
| // Create the Image |
| AHardwareBuffer *source; |
| EGLImageKHR image; |
| createEGLImageAndroidHardwareBufferSource( |
| kWidth, kHeight, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kDefaultAHBUsage, |
| kDefaultAttribs, {}, &source, &image); |
| |
| std::mutex mutex; |
| std::condition_variable condVar; |
| |
| enum class Step |
| { |
| Start, |
| Thread0DrawDone, |
| Thread1ReadbackDone, |
| Finish, |
| Abort, |
| }; |
| Step currentStep = Step::Start; |
| |
| auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw into the image in this context. |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glViewport(0, 0, kWidth, kHeight); |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EGLSyncKHR fence = eglCreateSyncKHR(window->getDisplay(), EGL_SYNC_FENCE_KHR, NULL); |
| glFlush(); |
| EGLint result = eglClientWaitSyncKHR(window->getDisplay(), fence, 0, 1'000'000'000); |
| ASSERT_EQ(result, EGL_CONDITION_SATISFIED_KHR); |
| ASSERT_EGL_TRUE(eglDestroySyncKHR(window->getDisplay(), fence)); |
| |
| // Then overwrite a quarter of the image with some color |
| std::vector<GLubyte> data((kWidth / 2) * (kHeight / 2) * 4, 99); |
| writeAHBData(source, kWidth / 2, kHeight / 2, 1, false, {{data.data(), 4}}); |
| |
| // Wait for the other thread to read back from it. |
| threadSynchronization.nextStep(Step::Thread0DrawDone); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1ReadbackDone)); |
| |
| // Create a different framebuffer to render to. |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Sample from the image. |
| glBindTexture(GL_TEXTURE_2D, target); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| glUseProgram(drawTexture); |
| GLint texLocation = glGetUniformLocation(drawTexture, essl1_shaders::Texture2DUniform()); |
| ASSERT_NE(-1, texLocation); |
| glUniform1i(texLocation, 0); |
| drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Expect the following: |
| // |
| // +-----+-----+-----+-----+ |
| // | | | | |
| // | 123 | 99 | G | |
| // | | | | |
| // | | | | |
| // | | | | |
| // | +-----+ | |
| // | | | |
| // | 123 | | |
| // | | | |
| // | | | |
| // | | | |
| // +-----+-----------+-----+ |
| // |
| EXPECT_PIXEL_RECT_EQ(0, 0, kWidth / 4, kHeight, GLColor(123, 123, 123, 123)); |
| EXPECT_PIXEL_RECT_EQ(kWidth / 4, 0, kWidth / 2 - kWidth / 4, kHeight / 2, |
| GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(kWidth / 4, kHeight / 2, kWidth / 2 - kWidth / 4, |
| kHeight - kHeight / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(kWidth / 2, 0, kWidth - kWidth / 2, kHeight, GLColor::green); |
| |
| eglDestroyImageKHR(window->getDisplay(), image); |
| |
| threadSynchronization.nextStep(Step::Finish); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) { |
| ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context)); |
| |
| // Create a texture target to bind the egl image |
| GLTexture target; |
| createEGLImageTargetTexture2D(image, target); |
| |
| // Wait for thread 0 to set up, and rebind the texture. |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DrawDone)); |
| glBindTexture(GL_TEXTURE_2D, target); |
| |
| // Expect the following: |
| // |
| // +-----------+-----------+ |
| // | | | |
| // | 99 | | |
| // | | | |
| // | | | |
| // | | | |
| // +-----------+ | |
| // | | |
| // | G | |
| // | | |
| // | | |
| // | | |
| // +-----------------------+ |
| // |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, kWidth / 2, kHeight / 2, GLColor(99, 99, 99, 99)); |
| EXPECT_PIXEL_RECT_EQ(kWidth / 2, 0, kWidth - kWidth / 2, kHeight / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(0, kHeight / 2, kWidth, kHeight - kHeight / 2, GLColor::green); |
| |
| // Then overwrite parts of the image with another color |
| std::vector<GLubyte> data((kWidth / 4) * kHeight * 4, 123); |
| writeAHBData(source, kWidth / 4, kHeight, 1, false, {{data.data(), 4}}); |
| |
| // Notify the other thread that it's finished reading back the texture. |
| threadSynchronization.nextStep(Step::Thread1ReadbackDone); |
| ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish)); |
| |
| // Clean up |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| }; |
| |
| std::array<LockStepThreadFunc, 2> threadFuncs = { |
| std::move(thread0), |
| std::move(thread1), |
| }; |
| |
| RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data()); |
| |
| ASSERT_NE(currentStep, Step::Abort); |
| } |
| |
| // Test redefining the same GL texture with different EGLImages |
| TEST_P(ImageTest, RedefineWithMultipleImages) |
| { |
| 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 Images |
| GLTexture source1, source2; |
| EGLImageKHR image1, image2; |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source1, &image1); |
| createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kDefaultAttribs, originalData, |
| source2, &image2); |
| |
| // Create texture & bind to Image |
| GLTexture texture; |
| createEGLImageTargetTexture2D(image1, texture); |
| |
| // Upload some data between the redefinition |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Bind the second image to this texture |
| createEGLImageTargetTexture2D(image2, texture); |
| |
| // Delete Image |
| eglDestroyImageKHR(window->getDisplay(), image1); |
| eglDestroyImageKHR(window->getDisplay(), image2); |
| |
| ASSERT_EGL_SUCCESS(); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Regression test to check that sRGB texture can be used to create image in sRGB colorspace. |
| // Also check that creating image using sRGB texture in linear colorspace wouldn't fail. |
| TEST_P(ImageTestES3, CreatesRGBImages) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt()); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB")); |
| ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt()); |
| ANGLE_SKIP_TEST_IF( |
| !IsEGLDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_colorspace")); |
| |
| std::vector<EGLint> colorSpaces = {EGL_GL_COLORSPACE_SRGB_KHR, EGL_GL_COLORSPACE_LINEAR_KHR}; |
| constexpr GLsizei kWidth = 2; |
| constexpr GLsizei kHeight = 2; |
| |
| for (size_t i = 0; i < colorSpaces.size(); i++) |
| { |
| // Create sRGB texture |
| GLTexture sRGBTexture; |
| glBindTexture(GL_TEXTURE_2D, sRGBTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, kWidth, kHeight, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| ASSERT_GL_NO_ERROR(); |
| |
| EGLint createImageAttribs[] = { |
| EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_GL_COLORSPACE_KHR, colorSpaces[i], EGL_NONE, |
| }; |
| |
| // Create the Image using sRGB texture |
| EGLImageKHR image = |
| eglCreateImageKHR(window->getDisplay(), window->getContext(), EGL_GL_TEXTURE_2D_KHR, |
| reinterpretHelper<EGLClientBuffer>(sRGBTexture), createImageAttribs); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_NE(image, EGL_NO_IMAGE_KHR); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| } |
| |
| // Regression test to check that sRGB texture can be used to create image in sRGB colorspace. |
| // Also check that creating image using sRGB texture in linear colorspace wouldn't fail. |
| TEST_P(ImageTestES3, DmaBufNegativeValidation) |
| { |
| EGLWindow *window = getEGLWindow(); |
| ANGLE_SKIP_TEST_IF(!hasBaseExt()); |
| ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), |
| "EGL_EXT_image_dma_buf_import")); |
| |
| const EGLint invalidImageAttributeList[][3] = { |
| {EGL_YUV_COLOR_SPACE_HINT_EXT, EGL_NONE, EGL_NONE}, |
| {EGL_SAMPLE_RANGE_HINT_EXT, EGL_NONE, EGL_NONE}, |
| {EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT, EGL_NONE, EGL_NONE}, |
| {EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT, EGL_NONE, EGL_NONE}, |
| }; |
| |
| EGLImageKHR image; |
| |
| for (size_t i = 0; i < 4; i++) |
| { |
| image = eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, |
| invalidImageAttributeList[i]); |
| ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE); |
| ASSERT_EQ(image, EGL_NO_IMAGE_KHR); |
| } |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(ImageTest, |
| ES3_VULKAN().enable(Feature::AllocateNonZeroMemory)); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImageTestES3); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(ImageTestES3, ES3_VULKAN().enable(Feature::AllocateNonZeroMemory)); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImageTestES31); |
| ANGLE_INSTANTIATE_TEST_ES31_AND(ImageTestES31, |
| ES31_VULKAN().enable(Feature::AllocateNonZeroMemory)); |
| } // namespace angle |