// // 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" using namespace angle; class PbufferTest : public ANGLETest { protected: PbufferTest() { setWindowWidth(512); setWindowHeight(512); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } virtual void SetUp() { ANGLETest::SetUp(); const std::string vsSource = SHADER_SOURCE ( 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 std::string textureFSSource = SHADER_SOURCE ( precision highp float; uniform sampler2D tex; varying vec2 texcoord; void main() { gl_FragColor = texture2D(tex, texcoord); } ); mTextureProgram = CompileProgram(vsSource, textureFSSource); 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; 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(mPbufferSize), EGL_HEIGHT, static_cast(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, }; mPbuffer = eglCreatePbufferSurface(window->getDisplay(), window->getConfig(), pBufferAttributes); 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(); } virtual void TearDown() { glDeleteProgram(mTextureProgram); EGLWindow *window = getEGLWindow(); eglDestroySurface(window->getDisplay(), mPbuffer); ANGLETest::TearDown(); } GLuint mTextureProgram; GLint mTextureUniformLocation; const size_t mPbufferSize = 32; EGLSurface mPbuffer; bool mSupportsPbuffers; bool mSupportsBindTexImage; }; // Test clearing a Pbuffer and checking the color is correct TEST_P(PbufferTest, Clearing) { if (!mSupportsPbuffers) { std::cout << "Test skipped because Pbuffers are not supported." << std::endl; return; } EGLWindow *window = getEGLWindow(); // Clear the window surface to blue and verify eglMakeCurrent(window->getDisplay(), window->getSurface(), window->getSurface(), window->getContext()); ASSERT_EGL_SUCCESS(); glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); // 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(mPbufferSize), static_cast(mPbufferSize)); glClearColor(1.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(static_cast(mPbufferSize) / 2, static_cast(mPbufferSize) / 2, 255, 0, 255, 255); // Rebind the window surface and verify that it is still blue eglMakeCurrent(window->getDisplay(), window->getSurface(), window->getSurface(), window->getContext()); 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) { if (!mSupportsPbuffers) { std::cout << "Test skipped because Pbuffers are not supported." << std::endl; return; } if (!mSupportsBindTexImage) { std::cout << "Test skipped because Pbuffer does not support binding to RGBA textures." << std::endl; return; } 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(mPbufferSize), static_cast(mPbufferSize)); glClearColor(1.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(static_cast(mPbufferSize) / 2, static_cast(mPbufferSize) / 2, 255, 0, 255, 255); // Apply the window surface eglMakeCurrent(window->getDisplay(), window->getSurface(), window->getSurface(), window->getContext()); // 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); } // Verify that when eglBind/ReleaseTexImage are called, the texture images are freed and their // size information is correctly updated. TEST_P(PbufferTest, TextureSizeReset) { if (!mSupportsPbuffers) { std::cout << "Test skipped because Pbuffers are not supported." << std::endl; return; } if (!mSupportsBindTexImage) { std::cout << "Test skipped because Pbuffer does not support binding to RGBA textures." << std::endl; return; } 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(); glUseProgram(mTextureProgram); glUniform1i(mTextureUniformLocation, 0); // Fill the texture with white pixels std::vector whitePixels(mPbufferSize * mPbufferSize * 4, 255); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(mPbufferSize), static_cast(mPbufferSize), 0, GL_RGBA, GL_UNSIGNED_BYTE, &whitePixels[0]); EXPECT_GL_NO_ERROR(); // Draw the white texture and verify that the pixels are correct drawQuad(mTextureProgram, "position", 0.5f); EXPECT_PIXEL_EQ(0, 0, 255, 255, 255, 255); // 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_EQ(0, 0, 0, 255, 0, 255); // 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_EQ(0, 0, 0, 0, 0, 255); } // Bind a Pbuffer, redefine the texture, and verify it renders correctly TEST_P(PbufferTest, BindTexImageAndRedefineTexture) { if (!mSupportsPbuffers) { std::cout << "Test skipped because Pbuffers are not supported." << std::endl; return; } if (!mSupportsBindTexImage) { std::cout << "Test skipped because Pbuffer does not support binding to RGBA textures." << std::endl; return; } 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(mPbufferSize), static_cast(mPbufferSize)); glClearColor(1.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_EQ(static_cast(mPbufferSize) / 2, static_cast(mPbufferSize) / 2, 255, 0, 255, 255); // Apply the window surface eglMakeCurrent(window->getDisplay(), window->getSurface(), window->getSurface(), window->getContext()); // 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 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); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. ANGLE_INSTANTIATE_TEST(PbufferTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_D3D11_WARP(), ES2_D3D11_REFERENCE(), ES2_OPENGLES());