blob: 3c64fe46be728d00ea287cb1268c3da305765a62 [file] [log] [blame]
//
// 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));