| // |
| // 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. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| #include "util/EGLWindow.h" |
| |
| using namespace angle; |
| |
| class PbufferTest : public ANGLETest<> |
| { |
| protected: |
| PbufferTest() |
| { |
| setWindowWidth(512); |
| setWindowHeight(512); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void testSetUp() override |
| { |
| constexpr char kVS[] = |
| 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; |
| })"; |
| |
| constexpr char kFS[] = |
| R"(precision highp float; |
| uniform sampler2D tex; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texcoord); |
| })"; |
| |
| mTextureProgram = CompileProgram(kVS, kFS); |
| if (mTextureProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| EGLint surfaceType = 0; |
| eglGetConfigAttrib(window->getDisplay(), window->getConfig(), EGL_SURFACE_TYPE, |
| &surfaceType); |
| mSupportsPbuffers = (surfaceType & EGL_PBUFFER_BIT) != 0; |
| |
| mPbuffer = createTestPbufferSurface(); |
| if (mSupportsPbuffers) |
| { |
| ASSERT_NE(mPbuffer, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| } |
| else |
| { |
| ASSERT_EQ(mPbuffer, EGL_NO_SURFACE); |
| ASSERT_EGL_ERROR(EGL_BAD_MATCH); |
| } |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| EGLSurface createTestPbufferSurface() |
| { |
| EGLWindow *window = getEGLWindow(); |
| EGLint bindToTextureRGBA = 0; |
| eglGetConfigAttrib(window->getDisplay(), window->getConfig(), EGL_BIND_TO_TEXTURE_RGBA, |
| &bindToTextureRGBA); |
| mSupportsBindTexImage = (bindToTextureRGBA == EGL_TRUE); |
| |
| const EGLint pBufferAttributes[] = { |
| EGL_WIDTH, static_cast<EGLint>(mPbufferSize), |
| EGL_HEIGHT, static_cast<EGLint>(mPbufferSize), |
| EGL_TEXTURE_FORMAT, mSupportsBindTexImage ? EGL_TEXTURE_RGBA : EGL_NO_TEXTURE, |
| EGL_TEXTURE_TARGET, mSupportsBindTexImage ? EGL_TEXTURE_2D : EGL_NO_TEXTURE, |
| EGL_NONE, EGL_NONE, |
| }; |
| |
| return eglCreatePbufferSurface(window->getDisplay(), window->getConfig(), |
| pBufferAttributes); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mTextureProgram); |
| |
| destroyPbuffer(); |
| } |
| |
| void destroyPbuffer() |
| { |
| if (mPbuffer) |
| { |
| destroyTestPbufferSurface(mPbuffer); |
| } |
| } |
| |
| void destroyTestPbufferSurface(EGLSurface pbuffer) |
| { |
| EGLWindow *window = getEGLWindow(); |
| eglDestroySurface(window->getDisplay(), pbuffer); |
| } |
| |
| void recreatePbufferInSrgbColorspace() |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| destroyPbuffer(); |
| |
| const EGLint pBufferSrgbAttributes[] = { |
| EGL_WIDTH, |
| static_cast<EGLint>(mPbufferSize), |
| EGL_HEIGHT, |
| static_cast<EGLint>(mPbufferSize), |
| EGL_TEXTURE_FORMAT, |
| mSupportsBindTexImage ? EGL_TEXTURE_RGBA : EGL_NO_TEXTURE, |
| EGL_TEXTURE_TARGET, |
| mSupportsBindTexImage ? EGL_TEXTURE_2D : EGL_NO_TEXTURE, |
| EGL_GL_COLORSPACE_KHR, |
| EGL_GL_COLORSPACE_SRGB_KHR, |
| EGL_NONE, |
| EGL_NONE, |
| }; |
| |
| mPbuffer = eglCreatePbufferSurface(window->getDisplay(), window->getConfig(), |
| pBufferSrgbAttributes); |
| } |
| |
| void drawColorQuad(GLColor color) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(program); |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| glUniform4fv(colorUniformLocation, 1, color.toNormalizedVector().data()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0); |
| glUseProgram(0); |
| } |
| |
| GLuint mTextureProgram; |
| GLint mTextureUniformLocation; |
| |
| const size_t mPbufferSize = 32; |
| EGLSurface mPbuffer = EGL_NO_SURFACE; |
| bool mSupportsPbuffers; |
| bool mSupportsBindTexImage; |
| }; |
| |
| class PbufferColorspaceTest : public PbufferTest |
| {}; |
| |
| // Test clearing a Pbuffer and checking the color is correct |
| TEST_P(PbufferTest, Clearing) |
| { |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| // Clear the window surface to blue and verify |
| window->makeCurrent(); |
| ASSERT_EGL_SUCCESS(); |
| |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Apply the Pbuffer and clear it to purple and verify |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_EQ(static_cast<GLint>(mPbufferSize) / 2, static_cast<GLint>(mPbufferSize) / 2, 255, |
| 0, 255, 255); |
| |
| // Rebind the window surface and verify that it is still blue |
| window->makeCurrent(); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); |
| } |
| |
| // Bind the Pbuffer to a texture and verify it renders correctly |
| TEST_P(PbufferTest, BindTexImage) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| // Apply the Pbuffer and clear it to purple |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| // Apply the window surface |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLuint texture = 0; |
| glGenTextures(1, &texture); |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Draw a quad and verify that it is purple |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Unbind the texture |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Verify that purple was drawn |
| EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 255, 255); |
| |
| glDeleteTextures(1, &texture); |
| } |
| |
| // Test various EGL level cases for eglBindTexImage. |
| TEST_P(PbufferTest, BindTexImageAlreadyBound) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| // If buffer is already bound to a texture then an EGL_BAD_ACCESS error is returned. |
| EXPECT_FALSE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_ERROR(EGL_BAD_ACCESS); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| drawColorQuad(GLColor::magenta); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| destroyPbuffer(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage overwriting previous bind works. |
| TEST_P(PbufferTest, BindTexImageOverwrite) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested: setup a binding that will be overwritten. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| drawColorQuad(GLColor::magenta); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| EGLSurface otherPbuffer = createTestPbufferSurface(); |
| ASSERT_NE(otherPbuffer, EGL_NO_SURFACE); |
| // This is being tested: replace the previous binding. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| drawColorQuad(GLColor::yellow); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::yellow); |
| |
| destroyTestPbufferSurface(otherPbuffer); |
| destroyPbuffer(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage overwriting previous bind works and does not crash on releaseTexImage. |
| TEST_P(PbufferTest, BindTexImageOverwriteNoCrashOnReleaseTexImage) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| EGLSurface otherPbuffer = createTestPbufferSurface(); |
| ASSERT_NE(otherPbuffer, EGL_NO_SURFACE); |
| |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_TRUE(eglReleaseTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_TRUE(eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); // No-op. |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| EXPECT_TRUE(eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| destroyTestPbufferSurface(otherPbuffer); |
| destroyPbuffer(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage pbuffer is unbound when the texture is destroyed. |
| TEST_P(PbufferTest, BindTexImageReleaseViaTextureDestroy) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| // Bind to a texture that will be destroyed. |
| { |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested: setup a binding that will be overwritten. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| drawColorQuad(GLColor::magenta); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| destroyPbuffer(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage pbuffer is unbound when eglReleaseTexImage is called. |
| TEST_P(PbufferTest, BindTexImagePbufferReleaseWhileBoundToFBOColorBuffer) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested: setup a binding to a pbuffer that will be unbound. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| // This is being tested: unbind the pbuffer, detect it via framebuffer status. |
| EXPECT_TRUE(eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status); |
| |
| destroyPbuffer(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage pbuffer is bound when the pbuffer is destroyed. |
| TEST_P(PbufferTest, BindTexImagePbufferDestroyWhileBound) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested: setup a binding to a pbuffer that will be destroyed. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| drawColorQuad(GLColor::magenta); |
| ASSERT_GL_NO_ERROR(); |
| |
| // This is being tested: destroy the pbuffer, but the underlying binding still works. |
| destroyPbuffer(); |
| status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that eglBindTexImage overwrite releases the previous pbuffer if the previous is orphaned. |
| TEST_P(PbufferTest, BindTexImageOverwriteReleasesOrphanedPbuffer) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| GLTexture texture; |
| 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); |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is being tested: setup a binding to a pbuffer that will be destroyed. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER)); |
| ASSERT_EGL_SUCCESS(); |
| |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| // Write magenta. This shouldn't be read below. |
| drawColorQuad(GLColor::magenta); |
| ASSERT_GL_NO_ERROR(); |
| |
| // This is being tested: destroy the pbuffer, but the underlying binding still works. |
| destroyPbuffer(); |
| status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| |
| EGLSurface otherPbuffer = createTestPbufferSurface(); |
| // This is being tested: bind a new pbuffer. The one orphaned above will now be really |
| // deallocated and we hope some sort of assert fires if something goes wrong. |
| EXPECT_TRUE(eglBindTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER)); |
| |
| // Write yellow. |
| drawColorQuad(GLColor::yellow); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::yellow); |
| |
| destroyTestPbufferSurface(otherPbuffer); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Verify that binding a pbuffer works after using a texture normally. |
| TEST_P(PbufferTest, BindTexImageAfterTexImage) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| // Apply the Pbuffer and clear it to magenta |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(mPbufferSize) / 2, |
| static_cast<GLint>(mPbufferSize) / 2, GLColor::magenta); |
| |
| // Apply the window surface |
| window->makeCurrent(); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| |
| // Create a simple blue texture. |
| GLTexture texture; |
| 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); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::blue); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw a quad and verify blue |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Bind the Pbuffer to the texture |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Draw a quad and verify magenta |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); |
| |
| // Unbind the texture |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| } |
| |
| // Test clearing a Pbuffer in sRGB colorspace and checking the color is correct. |
| // Then bind the Pbuffer to a texture and verify it renders correctly |
| TEST_P(PbufferTest, ClearAndBindTexImageSrgb) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| ANGLE_SKIP_TEST_IF( |
| !IsEGLDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_colorspace")); |
| // Possible GLES driver bug on Pixel2 devices: http://anglebug.com/42263865 |
| ANGLE_SKIP_TEST_IF(IsPixel2() && IsOpenGLES()); |
| |
| GLubyte kLinearColor[] = {132, 55, 219, 255}; |
| GLubyte kSrgbColor[] = {190, 128, 238, 255}; |
| |
| // Switch to sRGB |
| recreatePbufferInSrgbColorspace(); |
| EGLint colorspace = 0; |
| eglQuerySurface(window->getDisplay(), mPbuffer, EGL_GL_COLORSPACE, &colorspace); |
| EXPECT_EQ(colorspace, EGL_GL_COLORSPACE_SRGB_KHR); |
| |
| // Clear the Pbuffer surface with `kLinearColor` |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| 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(); |
| |
| // Expect glReadPixels to be `kSrgbColor` with a tolerance of 1 |
| EXPECT_PIXEL_NEAR(static_cast<GLint>(mPbufferSize) / 2, static_cast<GLint>(mPbufferSize) / 2, |
| kSrgbColor[0], kSrgbColor[1], kSrgbColor[2], kSrgbColor[3], 1); |
| |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLuint texture = 0; |
| glGenTextures(1, &texture); |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Sample from a texture with `kSrgbColor` data and render into a surface in linear colorspace. |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Unbind the texture |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Expect glReadPixels to be `kLinearColor` with a tolerance of 1 |
| EXPECT_PIXEL_NEAR(getWindowWidth() / 2, getWindowHeight() / 2, kLinearColor[0], kLinearColor[1], |
| kLinearColor[2], kLinearColor[3], 1); |
| |
| glDeleteTextures(1, &texture); |
| } |
| |
| // Test clearing a Pbuffer in sRGB colorspace and checking the color is correct. |
| // Then bind the Pbuffer to a texture and verify it renders correctly. |
| // Then change texture state to skip decode and verify it renders correctly. |
| TEST_P(PbufferTest, ClearAndBindTexImageSrgbSkipDecode) |
| { |
| EGLWindow *window = getEGLWindow(); |
| |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| ANGLE_SKIP_TEST_IF( |
| !IsEGLDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_colorspace")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode")); |
| // Possible GLES driver bug on Pixel devices: http://anglebug.com/42263865 |
| ANGLE_SKIP_TEST_IF((IsPixel2() || IsPixel4()) && IsOpenGLES()); |
| |
| GLubyte kLinearColor[] = {132, 55, 219, 255}; |
| GLubyte kSrgbColor[] = {190, 128, 238, 255}; |
| |
| // Switch to sRGB |
| recreatePbufferInSrgbColorspace(); |
| EGLint colorspace = 0; |
| eglQuerySurface(window->getDisplay(), mPbuffer, EGL_GL_COLORSPACE, &colorspace); |
| EXPECT_EQ(colorspace, EGL_GL_COLORSPACE_SRGB_KHR); |
| |
| // Clear the Pbuffer surface with `kLinearColor` |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| 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(); |
| |
| // Expect glReadPixels to be `kSrgbColor` with a tolerance of 1 |
| EXPECT_PIXEL_NEAR(static_cast<GLint>(mPbufferSize) / 2, static_cast<GLint>(mPbufferSize) / 2, |
| kSrgbColor[0], kSrgbColor[1], kSrgbColor[2], kSrgbColor[3], 1); |
| |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLuint texture = 0; |
| glGenTextures(1, &texture); |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Sample from a texture with `kSrgbColor` data and render into a surface in linear colorspace. |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Expect glReadPixels to be `kLinearColor` with a tolerance of 1 |
| EXPECT_PIXEL_NEAR(getWindowWidth() / 2, getWindowHeight() / 2, kLinearColor[0], kLinearColor[1], |
| kLinearColor[2], kLinearColor[3], 1); |
| |
| // Set skip decode for the texture |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); |
| drawQuad(mTextureProgram, "position", 0.5f); |
| |
| // Texture is in skip decode mode, expect glReadPixels to be `kSrgbColor` with tolerance of 1 |
| EXPECT_PIXEL_NEAR(getWindowWidth() / 2, getWindowHeight() / 2, kSrgbColor[0], kSrgbColor[1], |
| kSrgbColor[2], kSrgbColor[3], 1); |
| |
| // Unbind the texture |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| glDeleteTextures(1, &texture); |
| } |
| |
| // Verify that when eglBind/ReleaseTexImage are called, the texture images are freed and their |
| // size information is correctly updated. |
| TEST_P(PbufferTest, TextureSizeReset) |
| { |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers); |
| ANGLE_SKIP_TEST_IF(!mSupportsBindTexImage); |
| ANGLE_SKIP_TEST_IF(IsARM64() && IsWindows() && IsD3D()); |
| |
| GLTexture texture; |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| // Fill the texture with white pixels |
| std::vector<GLColor> whitePixels(mPbufferSize * mPbufferSize, GLColor::white); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(mPbufferSize), |
| static_cast<GLsizei>(mPbufferSize), 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| whitePixels.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw the white texture and verify that the pixels are correct |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| |
| // Bind the EGL surface and draw with it, results are undefined since nothing has |
| // been written to it |
| EGLWindow *window = getEGLWindow(); |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Clear the back buffer to a unique color (green) |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Unbind the EGL surface and try to draw with the texture again, the texture's size should |
| // now be zero and incomplete so the back buffer should be black |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| } |
| |
| // Bind a Pbuffer, redefine the texture, and verify it renders correctly |
| TEST_P(PbufferTest, BindTexImageAndRedefineTexture) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| // Apply the Pbuffer and clear it to purple |
| eglMakeCurrent(window->getDisplay(), mPbuffer, mPbuffer, window->getContext()); |
| ASSERT_EGL_SUCCESS(); |
| |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_EQ(static_cast<GLint>(mPbufferSize) / 2, static_cast<GLint>(mPbufferSize) / 2, 255, |
| 0, 255, 255); |
| |
| // Apply the window surface |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLuint texture = 0; |
| glGenTextures(1, &texture); |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Redefine the texture |
| unsigned int pixelValue = 0xFFFF00FF; |
| std::vector<unsigned int> pixelData(getWindowWidth() * getWindowHeight(), pixelValue); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, &pixelData[0]); |
| |
| // Draw a quad and verify that it is magenta |
| glUseProgram(mTextureProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| drawQuad(mTextureProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Verify that magenta was drawn |
| EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 255, 255); |
| |
| glDeleteTextures(1, &texture); |
| } |
| |
| // Bind the Pbuffer to a texture, use that texture as Framebuffer color attachment and then |
| // destroy framebuffer, texture and Pbuffer. |
| TEST_P(PbufferTest, UseAsFramebufferColorThenDestroy) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| |
| EGLWindow *window = getEGLWindow(); |
| |
| // Apply the window surface |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLuint texture = 0; |
| glGenTextures(1, &texture); |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create Framebuffer and use texture as color attachment |
| GLuint fbo; |
| glGenFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ANGLE_SKIP_TEST_IF(status == GL_FRAMEBUFFER_UNSUPPORTED); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, status); |
| glDisable(GL_DEPTH_TEST); |
| glViewport(0, 0, static_cast<GLsizei>(mPbufferSize), static_cast<GLsizei>(mPbufferSize)); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw a quad in order to open a RenderPass |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| glUseProgram(redProgram); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Unbind resources |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Delete resources |
| glDeleteFramebuffers(1, &fbo); |
| glDeleteTextures(1, &texture); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Destroy Pbuffer |
| destroyPbuffer(); |
| |
| // Finish work |
| glFinish(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Bind the Pbuffer to a texture, use that texture as Framebuffer color attachment and then |
| // destroy framebuffer, texture and Pbuffer. A bound but released TexImages are destroyed |
| // only when the binding is overwritten. |
| TEST_P(PbufferTest, UseAsFramebufferColorThenDeferredDestroy) |
| { |
| // Test skipped because Pbuffers are not supported or Pbuffer does not support binding to RGBA |
| // textures. |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers || !mSupportsBindTexImage); |
| EGLWindow *window = getEGLWindow(); |
| window->makeCurrent(); |
| |
| // Create a texture and bind the Pbuffer to it |
| GLTexture texture; |
| 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); |
| EXPECT_GL_NO_ERROR(); |
| |
| EGLSurface otherPbuffer = createTestPbufferSurface(); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_NE(otherPbuffer, EGL_NO_SURFACE); |
| |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_ERROR(EGL_BAD_ACCESS); |
| eglBindTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| eglReleaseTexImage(window->getDisplay(), otherPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| eglBindTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| eglReleaseTexImage(window->getDisplay(), mPbuffer, EGL_BACK_BUFFER); |
| ASSERT_EGL_SUCCESS(); |
| |
| destroyPbuffer(); |
| destroyTestPbufferSurface(otherPbuffer); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Finish work |
| glFinish(); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test the validation errors for bad parameters for eglCreatePbufferSurface |
| TEST_P(PbufferTest, NegativeValidationBadAttributes) |
| { |
| EGLWindow *window = getEGLWindow(); |
| EGLSurface pbufferSurface; |
| |
| const EGLint invalidPBufferAttributeList[][3] = { |
| {EGL_MIPMAP_TEXTURE, EGL_MIPMAP_TEXTURE, EGL_NONE}, |
| {EGL_LARGEST_PBUFFER, EGL_LARGEST_PBUFFER, EGL_NONE}, |
| }; |
| |
| for (size_t i = 0; i < 2; i++) |
| { |
| pbufferSurface = eglCreatePbufferSurface(window->getDisplay(), window->getConfig(), |
| &invalidPBufferAttributeList[i][0]); |
| ASSERT_EQ(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE); |
| } |
| } |
| |
| // Test that passing colorspace attributes do not generate EGL validation errors |
| // when EGL_ANGLE_colorspace_attribute_passthrough extension is supported. |
| TEST_P(PbufferColorspaceTest, CreateSurfaceWithColorspace) |
| { |
| EGLDisplay dpy = getEGLWindow()->getDisplay(); |
| const bool extensionSupported = |
| IsEGLDisplayExtensionEnabled(dpy, "EGL_EXT_gl_colorspace_display_p3_passthrough"); |
| const bool passthroughExtensionSupported = |
| IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_colorspace_attribute_passthrough"); |
| |
| EGLSurface pbufferSurface = EGL_NO_SURFACE; |
| const EGLint pBufferAttributes[] = { |
| EGL_WIDTH, static_cast<EGLint>(mPbufferSize), |
| EGL_HEIGHT, static_cast<EGLint>(mPbufferSize), |
| EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, |
| EGL_NONE, EGL_NONE, |
| }; |
| |
| pbufferSurface = eglCreatePbufferSurface(dpy, getEGLWindow()->getConfig(), pBufferAttributes); |
| if (extensionSupported) |
| { |
| // If EGL_EXT_gl_colorspace_display_p3_passthrough is supported |
| // "pbufferSurface" should be a valid pbuffer surface. |
| ASSERT_NE(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| } |
| else if (!extensionSupported && passthroughExtensionSupported) |
| { |
| // If EGL_ANGLE_colorspace_attribute_passthrough was the only extension supported |
| // we should not expect a validation error. |
| ASSERT_NE(eglGetError(), EGL_BAD_ATTRIBUTE); |
| } |
| else |
| { |
| // Otherwise we should expect an EGL_BAD_ATTRIBUTE validation error. |
| ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE); |
| } |
| |
| // Cleanup |
| if (pbufferSurface != EGL_NO_SURFACE) |
| { |
| eglDestroySurface(dpy, pbufferSurface); |
| } |
| } |
| |
| // Test the implementation for EGL_LARGEST_PBUFFER |
| TEST_P(PbufferTest, LargestPbuffer) |
| { |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers); |
| ANGLE_SKIP_TEST_IF(!IsARM()); |
| EGLWindow *window = getEGLWindow(); |
| EGLDisplay display = window->getDisplay(); |
| EGLSurface pbufferSurface; |
| |
| EGLint maxPbufferWidth; |
| EGLint maxPbufferHeight; |
| EGLint value; |
| EGLint pBufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, |
| EGL_LARGEST_PBUFFER, EGL_FALSE, EGL_NONE}; |
| |
| // Check that eglCreatePbufferSurface can succeed when EGL_LARGEST_PBUFFER is set to EGL_FALSE |
| pbufferSurface = eglCreatePbufferSurface(display, window->getConfig(), pBufferAttributes); |
| ASSERT_NE(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Cleanup |
| eglDestroySurface(display, pbufferSurface); |
| |
| EXPECT_EGL_TRUE( |
| eglGetConfigAttrib(display, window->getConfig(), EGL_MAX_PBUFFER_WIDTH, &maxPbufferWidth)); |
| pBufferAttributes[1] = maxPbufferWidth + 1; |
| pBufferAttributes[5] = EGL_TRUE; |
| |
| // Check that eglCreatePbufferSurface clamps an EGL_WIDTH that is too large with |
| // EGL_LARGEST_PBUFFER set |
| pbufferSurface = eglCreatePbufferSurface(display, window->getConfig(), pBufferAttributes); |
| ASSERT_NE(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| |
| EXPECT_EGL_TRUE(eglQuerySurface(display, pbufferSurface, EGL_WIDTH, &value)); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_EQ(value, maxPbufferWidth); |
| |
| // Cleanup |
| eglDestroySurface(display, pbufferSurface); |
| |
| pBufferAttributes[1] = 1; |
| |
| EXPECT_EGL_TRUE(eglGetConfigAttrib(display, window->getConfig(), EGL_MAX_PBUFFER_HEIGHT, |
| &maxPbufferHeight)); |
| pBufferAttributes[3] = maxPbufferHeight + 1; |
| |
| // Check that eglCreatePbufferSurface clamps an EGL_HEIGHT that is too large with |
| // EGL_LARGEST_PBUFFER set |
| pbufferSurface = eglCreatePbufferSurface(display, window->getConfig(), pBufferAttributes); |
| ASSERT_NE(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| |
| EXPECT_EGL_TRUE(eglQuerySurface(display, pbufferSurface, EGL_HEIGHT, &value)); |
| ASSERT_EGL_SUCCESS(); |
| ASSERT_EQ(value, maxPbufferHeight); |
| |
| // Cleanup |
| eglDestroySurface(display, pbufferSurface); |
| } |
| |
| // Test the implementation for query format sizes from zero sized pbuffer surface |
| TEST_P(PbufferTest, ZeroSizedSurfaceFormatQuery) |
| { |
| ANGLE_SKIP_TEST_IF(!mSupportsPbuffers); |
| EGLWindow *window = getEGLWindow(); |
| EGLDisplay display = window->getDisplay(); |
| EGLSurface pbufferSurface; |
| |
| EGLint pBufferAttributes[] = {EGL_WIDTH, 0, EGL_HEIGHT, 0, EGL_NONE}; |
| |
| pbufferSurface = eglCreatePbufferSurface(display, window->getConfig(), pBufferAttributes); |
| ASSERT_NE(pbufferSurface, EGL_NO_SURFACE); |
| ASSERT_EGL_SUCCESS(); |
| |
| EGLint width, height; |
| EXPECT_EGL_TRUE(eglQuerySurface(display, pbufferSurface, EGL_WIDTH, &width)); |
| EXPECT_EGL_TRUE(eglQuerySurface(display, pbufferSurface, EGL_HEIGHT, &height)); |
| EXPECT_EQ(width, 0); |
| EXPECT_EQ(height, 0); |
| |
| window->makeCurrent(pbufferSurface, pbufferSurface, window->getContext()); |
| |
| GLint redBits, greenBits, blueBits, alphaBits; |
| glGetIntegerv(GL_RED_BITS, &redBits); |
| glGetIntegerv(GL_GREEN_BITS, &greenBits); |
| glGetIntegerv(GL_BLUE_BITS, &blueBits); |
| glGetIntegerv(GL_ALPHA_BITS, &alphaBits); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_EQ(redBits, 8); |
| EXPECT_EQ(greenBits, 8); |
| EXPECT_EQ(blueBits, 8); |
| EXPECT_EQ(alphaBits, 8); |
| |
| // Cleanup |
| window->makeCurrent(); |
| eglDestroySurface(display, pbufferSurface); |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES2(PbufferTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PbufferColorspaceTest); |
| ANGLE_INSTANTIATE_TEST_ES3_AND(PbufferColorspaceTest, |
| ES3_VULKAN().enable(Feature::EglColorspaceAttributePassthrough)); |