blob: 34a5444a078e262edaa580b6651866d894687b0f [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.
//
// EGLSurfaceTest:
// Tests pertaining to egl::Surface.
//
#include <gtest/gtest.h>
#include <thread>
#include <vector>
#include "common/Color.h"
#include "common/platform.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "util/OSWindow.h"
#include "util/Timer.h"
#if defined(ANGLE_ENABLE_D3D11)
# define INITGUID
# include <guiddef.h>
# include <d3d11.h>
# include <dcomp.h>
#endif
using namespace angle;
namespace
{
class EGLSurfaceTest : public ANGLETest<>
{
protected:
EGLSurfaceTest()
: mDisplay(EGL_NO_DISPLAY),
mWindowSurface(EGL_NO_SURFACE),
mPbufferSurface(EGL_NO_SURFACE),
mContext(EGL_NO_CONTEXT),
mSecondContext(EGL_NO_CONTEXT),
mOSWindow(nullptr)
{}
void testSetUp() override
{
mOSWindow = OSWindow::New();
mOSWindow->initialize("EGLSurfaceTest", 64, 64);
}
void tearDownContextAndSurface()
{
if (mDisplay == EGL_NO_DISPLAY)
{
return;
}
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (mWindowSurface != EGL_NO_SURFACE)
{
eglDestroySurface(mDisplay, mWindowSurface);
mWindowSurface = EGL_NO_SURFACE;
}
if (mPbufferSurface != EGL_NO_SURFACE)
{
eglDestroySurface(mDisplay, mPbufferSurface);
mPbufferSurface = EGL_NO_SURFACE;
}
if (mContext != EGL_NO_CONTEXT)
{
eglDestroyContext(mDisplay, mContext);
mContext = EGL_NO_CONTEXT;
}
if (mSecondContext != EGL_NO_CONTEXT)
{
eglDestroyContext(mDisplay, mSecondContext);
mSecondContext = EGL_NO_CONTEXT;
}
}
// Release any resources created in the test body
void testTearDown() override
{
tearDownContextAndSurface();
if (mDisplay != EGL_NO_DISPLAY)
{
eglTerminate(mDisplay);
mDisplay = EGL_NO_DISPLAY;
}
mOSWindow->destroy();
OSWindow::Delete(&mOSWindow);
ASSERT_TRUE(mWindowSurface == EGL_NO_SURFACE && mContext == EGL_NO_CONTEXT);
}
void initializeDisplay()
{
GLenum platformType = GetParam().getRenderer();
GLenum deviceType = GetParam().getDeviceType();
std::vector<EGLint> displayAttributes;
displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
displayAttributes.push_back(platformType);
displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE);
displayAttributes.push_back(EGL_DONT_CARE);
displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE);
displayAttributes.push_back(EGL_DONT_CARE);
displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
displayAttributes.push_back(deviceType);
displayAttributes.push_back(EGL_NONE);
mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
displayAttributes.data());
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
EGLint majorVersion, minorVersion;
ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE);
eglBindAPI(EGL_OPENGL_ES_API);
ASSERT_EGL_SUCCESS();
}
void initializeSingleContext(EGLContext *context, EGLint virtualizationGroup = EGL_DONT_CARE)
{
ASSERT_TRUE(*context == EGL_NO_CONTEXT);
EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, GetParam().majorVersion,
EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, virtualizationGroup,
EGL_NONE};
if (!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_context_virtualization"))
{
contextAttibutes[2] = EGL_NONE;
}
*context = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes);
ASSERT_EGL_SUCCESS();
ASSERT_TRUE(*context != EGL_NO_CONTEXT);
}
void initializeMainContext() { initializeSingleContext(&mContext); }
void initializeAllContexts()
{
initializeSingleContext(&mContext);
initializeSingleContext(&mSecondContext);
}
void initializeWindowSurfaceWithAttribs(EGLConfig config,
const std::vector<EGLint> &additionalAttributes,
EGLenum expectedResult)
{
ASSERT_TRUE(mWindowSurface == EGL_NO_SURFACE);
EGLint surfaceType = EGL_NONE;
eglGetConfigAttrib(mDisplay, mConfig, EGL_SURFACE_TYPE, &surfaceType);
if (surfaceType & EGL_WINDOW_BIT)
{
std::vector<EGLint> windowAttributes = additionalAttributes;
windowAttributes.push_back(EGL_NONE);
// Create first window surface
mWindowSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
windowAttributes.data());
}
ASSERT_EGLENUM_EQ(eglGetError(), expectedResult);
}
void initializeSurfaceWithAttribs(EGLConfig config,
const std::vector<EGLint> &additionalAttributes)
{
mConfig = config;
EGLint surfaceType = EGL_NONE;
eglGetConfigAttrib(mDisplay, mConfig, EGL_SURFACE_TYPE, &surfaceType);
if (surfaceType & EGL_WINDOW_BIT)
{
initializeWindowSurfaceWithAttribs(config, additionalAttributes, EGL_SUCCESS);
}
if (surfaceType & EGL_PBUFFER_BIT)
{
ASSERT_TRUE(mPbufferSurface == EGL_NO_SURFACE);
std::vector<EGLint> pbufferAttributes = additionalAttributes;
// Give pbuffer non-zero dimensions.
pbufferAttributes.push_back(EGL_WIDTH);
pbufferAttributes.push_back(64);
pbufferAttributes.push_back(EGL_HEIGHT);
pbufferAttributes.push_back(64);
pbufferAttributes.push_back(EGL_NONE);
mPbufferSurface = eglCreatePbufferSurface(mDisplay, mConfig, pbufferAttributes.data());
ASSERT_EGL_SUCCESS();
}
}
void initializeSurface(EGLConfig config)
{
std::vector<EGLint> additionalAttributes;
initializeSurfaceWithAttribs(config, additionalAttributes);
}
EGLConfig chooseDefaultConfig(bool requireWindowSurface) const
{
const EGLint configAttributes[] = {EGL_RED_SIZE,
EGL_DONT_CARE,
EGL_GREEN_SIZE,
EGL_DONT_CARE,
EGL_BLUE_SIZE,
EGL_DONT_CARE,
EGL_ALPHA_SIZE,
EGL_DONT_CARE,
EGL_DEPTH_SIZE,
EGL_DONT_CARE,
EGL_STENCIL_SIZE,
EGL_DONT_CARE,
EGL_SAMPLE_BUFFERS,
0,
EGL_SURFACE_TYPE,
requireWindowSurface ? EGL_WINDOW_BIT : EGL_DONT_CARE,
EGL_NONE};
EGLint configCount;
EGLConfig config;
if (eglChooseConfig(mDisplay, configAttributes, &config, 1, &configCount) != EGL_TRUE)
return nullptr;
if (configCount != 1)
return nullptr;
return config;
}
void initializeSurfaceWithDefaultConfig(bool requireWindowSurface)
{
EGLConfig defaultConfig = chooseDefaultConfig(requireWindowSurface);
ASSERT_NE(defaultConfig, nullptr);
initializeSurface(defaultConfig);
}
GLuint createProgram(const char *fs = angle::essl1_shaders::fs::Red())
{
return CompileProgram(angle::essl1_shaders::vs::Simple(), fs);
}
void drawWithProgram(GLuint program)
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation =
glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
glUseProgram(program);
const GLfloat vertices[] = {
-1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
-1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
};
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_PIXEL_EQ(mOSWindow->getWidth() / 2, mOSWindow->getHeight() / 2, 255, 0, 0, 255);
}
void runMessageLoopTest(EGLSurface secondSurface, EGLContext secondContext)
{
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
// Make a second context current
eglMakeCurrent(mDisplay, secondSurface, secondSurface, secondContext);
eglDestroySurface(mDisplay, mWindowSurface);
// Create second window surface
std::vector<EGLint> surfaceAttributes;
surfaceAttributes.push_back(EGL_NONE);
surfaceAttributes.push_back(EGL_NONE);
mWindowSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
&surfaceAttributes[0]);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
mOSWindow->signalTestEvent();
mOSWindow->messageLoop();
ASSERT_TRUE(mOSWindow->didTestEventFire());
// Simple operation to test the FBO is set appropriately
glClear(GL_COLOR_BUFFER_BIT);
}
void runWaitSemaphoreTest(bool useSecondContext);
void runDestroyNotCurrentSurfaceTest(bool testWindowsSurface);
void drawQuadThenTearDown();
EGLDisplay mDisplay;
EGLSurface mWindowSurface;
EGLSurface mPbufferSurface;
EGLContext mContext;
EGLContext mSecondContext;
EGLConfig mConfig;
OSWindow *mOSWindow;
};
class EGLFloatSurfaceTest : public EGLSurfaceTest
{
protected:
EGLFloatSurfaceTest() : EGLSurfaceTest()
{
setWindowWidth(512);
setWindowHeight(512);
}
void testSetUp() override
{
mOSWindow = OSWindow::New();
mOSWindow->initialize("EGLFloatSurfaceTest", 64, 64);
}
void testTearDown() override
{
EGLSurfaceTest::testTearDown();
glDeleteProgram(mProgram);
}
GLuint createProgram()
{
constexpr char kFS[] =
"precision highp float;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0, 2.0, 3.0, 4.0);\n"
"}\n";
return CompileProgram(angle::essl1_shaders::vs::Simple(), kFS);
}
bool initializeSurfaceWithFloatConfig()
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
16,
EGL_GREEN_SIZE,
16,
EGL_BLUE_SIZE,
16,
EGL_ALPHA_SIZE,
16,
EGL_COLOR_COMPONENT_TYPE_EXT,
EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
EGL_NONE,
EGL_NONE};
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a float surface is not supported, skipping test"
<< std::endl;
return false;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
mProgram = createProgram();
return true;
}
GLuint mProgram;
};
class EGLSingleBufferTest : public ANGLETest<>
{
protected:
EGLSingleBufferTest() {}
void testSetUp() override
{
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
mDisplay = eglGetPlatformDisplayEXT(
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
ASSERT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
mMajorVersion = GetParam().majorVersion;
}
void testTearDown() override
{
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mDisplay);
}
bool chooseConfig(EGLConfig *config, bool mutableRenderBuffer) const
{
bool result = false;
EGLint count = 0;
EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
EGLint attribs[] = {
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
0,
EGL_RENDERABLE_TYPE,
clientVersion,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | (mutableRenderBuffer ? EGL_MUTABLE_RENDER_BUFFER_BIT_KHR : 0),
EGL_NONE};
result = eglChooseConfig(mDisplay, attribs, config, 1, &count);
return result && (count > 0);
}
bool createContext(EGLConfig config, EGLContext *context)
{
EXPECT_TRUE(*context == EGL_NO_CONTEXT);
bool result = false;
EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
*context = eglCreateContext(mDisplay, config, nullptr, attribs);
result = (*context != EGL_NO_CONTEXT);
EXPECT_TRUE(result);
return result;
}
bool createWindowSurface(EGLConfig config,
EGLNativeWindowType win,
EGLSurface *surface,
EGLint renderBuffer) const
{
EXPECT_TRUE(*surface == EGL_NO_SURFACE);
bool result = false;
EGLint attribs[] = {EGL_RENDER_BUFFER, renderBuffer, EGL_NONE};
*surface = eglCreateWindowSurface(mDisplay, config, win, attribs);
result = (*surface != EGL_NO_SURFACE);
EXPECT_TRUE(result);
return result;
}
uint32_t drawAndSwap(EGLSurface &surface, EGLDisplay &display, uint32_t color, bool flush);
EGLDisplay mDisplay = EGL_NO_DISPLAY;
EGLint mMajorVersion = 0;
const EGLint kWidth = 32;
const EGLint kHeight = 32;
};
class EGLAndroidAutoRefreshTest : public EGLSingleBufferTest
{};
// Test clearing and checking the color is correct
TEST_P(EGLFloatSurfaceTest, Clearing)
{
ANGLE_SKIP_TEST_IF(!initializeSurfaceWithFloatConfig());
ASSERT_NE(0u, mProgram) << "shader compilation failed.";
ASSERT_GL_NO_ERROR();
GLColor32F clearColor(0.0f, 1.0f, 2.0f, 3.0f);
glClearColor(clearColor.R, clearColor.G, clearColor.B, clearColor.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR32F_EQ(0, 0, clearColor);
}
// Test drawing and checking the color is correct
TEST_P(EGLFloatSurfaceTest, Drawing)
{
ANGLE_SKIP_TEST_IF(!initializeSurfaceWithFloatConfig());
ASSERT_NE(0u, mProgram) << "shader compilation failed.";
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_32F_EQ(0, 0, 1.0f, 2.0f, 3.0f, 4.0f);
}
class EGLSurfaceTest3 : public EGLSurfaceTest
{};
// Test a surface bug where we could have two Window surfaces active
// at one time, blocking message loops. See http://crbug.com/475085
TEST_P(EGLSurfaceTest, MessageLoopBug)
{
// http://anglebug.com/3123
ANGLE_SKIP_TEST_IF(IsAndroid());
// http://anglebug.com/3138
ANGLE_SKIP_TEST_IF(IsOzone());
// http://anglebug.com/5485
ANGLE_SKIP_TEST_IF(IsIOS());
initializeDisplay();
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
runMessageLoopTest(EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
// Tests the message loop bug, but with setting a second context
// instead of null.
TEST_P(EGLSurfaceTest, MessageLoopBugContext)
{
// http://anglebug.com/3123
ANGLE_SKIP_TEST_IF(IsAndroid());
// http://anglebug.com/3138
ANGLE_SKIP_TEST_IF(IsOzone());
// http://anglebug.com/5485
ANGLE_SKIP_TEST_IF(IsIOS());
initializeDisplay();
initializeSurfaceWithDefaultConfig(true);
initializeAllContexts();
ANGLE_SKIP_TEST_IF(!mPbufferSurface);
runMessageLoopTest(mPbufferSurface, mSecondContext);
}
// Test a bug where calling makeCurrent twice would release the surface
TEST_P(EGLSurfaceTest, MakeCurrentTwice)
{
initializeDisplay();
initializeSurfaceWithDefaultConfig(false);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
// Simple operation to test the FBO is set appropriately
glClear(GL_COLOR_BUFFER_BIT);
}
// This is a regression test to verify that surfaces are not prematurely destroyed.
TEST_P(EGLSurfaceTest, SurfaceUseAfterFreeBug)
{
initializeDisplay();
// Initialize an RGBA8 window and pbuffer surface
constexpr EGLint kSurfaceAttributes[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_NONE};
EGLint configCount = 0;
EGLConfig surfaceConfig = nullptr;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, kSurfaceAttributes, &surfaceConfig, 1, &configCount));
ASSERT_NE(configCount, 0);
ASSERT_NE(surfaceConfig, nullptr);
initializeSurface(surfaceConfig);
initializeAllContexts();
ASSERT_EGL_SUCCESS();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
ASSERT_NE(mPbufferSurface, EGL_NO_SURFACE);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mSecondContext);
ASSERT_EGL_SUCCESS();
glClear(GL_COLOR_BUFFER_BIT);
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
ASSERT_EGL_SUCCESS();
glClear(GL_COLOR_BUFFER_BIT);
eglDestroySurface(mDisplay, mPbufferSurface);
ASSERT_EGL_SUCCESS();
mPbufferSurface = EGL_NO_SURFACE;
eglDestroyContext(mDisplay, mSecondContext);
ASSERT_EGL_SUCCESS();
mSecondContext = EGL_NO_CONTEXT;
}
// Test that the window surface is correctly resized after calling swapBuffers
TEST_P(EGLSurfaceTest, ResizeWindow)
{
// http://anglebug.com/4453
ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
// Flaky on Linux SwANGLE http://anglebug.com/4453
ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
// http://anglebug.com/5485
ANGLE_SKIP_TEST_IF(IsIOS());
ANGLE_SKIP_TEST_IF(IsLinux() && IsARM());
// Necessary for a window resizing test if there is no per-frame window size query
setWindowVisible(mOSWindow, true);
GLenum platform = GetParam().getRenderer();
bool platformSupportsZeroSize = platform == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE ||
platform == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
int minSize = platformSupportsZeroSize ? 0 : 1;
initializeDisplay();
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
EGLint height;
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
ASSERT_EGL_SUCCESS();
ASSERT_EQ(64, height); // initial size
// set window's height to 0 (if possible) or 1
mOSWindow->resize(64, minSize);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
// TODO(syoussefi): the GLX implementation still reads the window size as 64x64 through
// XGetGeometry. http://anglebug.com/3122
ANGLE_SKIP_TEST_IF(IsLinux() && IsOpenGL());
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
ASSERT_EGL_SUCCESS();
ASSERT_EQ(minSize, height);
// restore window's height
mOSWindow->resize(64, 64);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
ASSERT_EGL_SUCCESS();
ASSERT_EQ(64, height);
}
// Test that the backbuffer is correctly resized after calling swapBuffers
TEST_P(EGLSurfaceTest, ResizeWindowWithDraw)
{
// http://anglebug.com/4453
ANGLE_SKIP_TEST_IF(IsLinux());
// http://anglebug.com/5485
ANGLE_SKIP_TEST_IF(IsIOS());
// Necessary for a window resizing test if there is no per-frame window size query
setWindowVisible(mOSWindow, true);
initializeDisplay();
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
int size = 64;
EGLint height = 0;
EGLint width = 0;
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
// Clear to red
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &width);
ASSERT_EGL_SUCCESS();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(size - 1, size - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, size - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(-1, -1, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, 0, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(0, size, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, size, GLColor::transparentBlack);
// set window's size small
size = 1;
mOSWindow->resize(size, size);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &width);
ASSERT_EGL_SUCCESS();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(size - 1, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(size - 1, size - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, size - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(-1, -1, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, 0, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(0, size, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, size, GLColor::transparentBlack);
// set window's height large
size = 128;
mOSWindow->resize(size, size);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
// Clear to blue
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &height);
eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &width);
ASSERT_EGL_SUCCESS();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(size - 1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(size - 1, size - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(0, size - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(-1, -1, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, 0, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(0, size, GLColor::transparentBlack);
EXPECT_PIXEL_COLOR_EQ(size, size, GLColor::transparentBlack);
}
// Test that the window can be reset repeatedly before surface creation.
TEST_P(EGLSurfaceTest, ResetNativeWindow)
{
setWindowVisible(mOSWindow, true);
initializeDisplay();
for (int i = 0; i < 10; ++i)
{
mOSWindow->resetNativeWindow();
}
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
}
// Test swap buffer without any draw calls.
TEST_P(EGLSurfaceTest, SwapWithoutAnyDraw)
{
initializeDisplay();
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
for (int i = 0; i < 10; ++i)
{
eglSwapBuffers(mDisplay, mWindowSurface);
}
}
// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGB565
TEST_P(EGLSurfaceTest, CreateWithEGLConfig5650Support)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
5,
EGL_GREEN_SIZE,
6,
EGL_BLUE_SIZE,
5,
EGL_ALPHA_SIZE,
0,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a GL_RGB565 surface is not supported, skipping test"
<< std::endl;
return;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGBA4
TEST_P(EGLSurfaceTest, CreateWithEGLConfig4444Support)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
4,
EGL_GREEN_SIZE,
4,
EGL_BLUE_SIZE,
4,
EGL_ALPHA_SIZE,
4,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a GL_RGBA4 surface is not supported, skipping test"
<< std::endl;
return;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGB5_A1
TEST_P(EGLSurfaceTest, CreateWithEGLConfig5551Support)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
5,
EGL_GREEN_SIZE,
5,
EGL_BLUE_SIZE,
5,
EGL_ALPHA_SIZE,
1,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a GL_RGB5_A1 surface is not supported, skipping test"
<< std::endl;
return;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Test creating a surface that supports a EGLConfig without alpha support
TEST_P(EGLSurfaceTest, CreateWithEGLConfig8880Support)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
0,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a GL_RGB8_OES surface is not supported, skipping test"
<< std::endl;
return;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Test creating a surface that supports GL_RGB10_A2 with BT2020 colorspaces
TEST_P(EGLSurfaceTest, CreateWithEGLConfig1010102Support)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
10,
EGL_GREEN_SIZE,
10,
EGL_BLUE_SIZE,
10,
EGL_ALPHA_SIZE,
2,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for a GL_RGB10_A2 surface is not supported, skipping test"
<< std::endl;
return;
}
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_gl_colorspace_bt2020_hlg"));
ANGLE_SKIP_TEST_IF(
!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_gl_colorspace_bt2020_linear"));
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_gl_colorspace_bt2020_pq"));
constexpr std::array<EGLint, 3u> kBt2020Colorspaces = {EGL_GL_COLORSPACE_BT2020_HLG_EXT,
EGL_GL_COLORSPACE_BT2020_LINEAR_EXT,
EGL_GL_COLORSPACE_BT2020_PQ_EXT};
for (EGLint bt2020Colorspace : kBt2020Colorspaces)
{
std::vector<EGLint> winSurfaceAttribs;
winSurfaceAttribs.push_back(EGL_GL_COLORSPACE_KHR);
winSurfaceAttribs.push_back(bt2020Colorspace);
initializeSurfaceWithAttribs(config, winSurfaceAttribs);
ASSERT_EGL_SUCCESS();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext));
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(mDisplay, mWindowSurface);
mWindowSurface = EGL_NO_SURFACE;
}
}
TEST_P(EGLSurfaceTest, FixedSizeWindow)
{
const EGLint configAttributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
0,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_SAMPLE_BUFFERS,
0,
EGL_NONE};
initializeDisplay();
ANGLE_SKIP_TEST_IF(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &mConfig) == EGL_FALSE);
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_window_fixed_size"));
constexpr EGLint kInitialSize = 64;
constexpr EGLint kUpdateSize = 32;
EGLint surfaceAttributes[] = {
EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, kInitialSize, EGL_HEIGHT, kInitialSize, EGL_NONE,
};
// Create first window surface
mWindowSurface =
eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), surfaceAttributes);
ASSERT_EGL_SUCCESS();
ASSERT_NE(EGL_NO_SURFACE, mWindowSurface);
initializeMainContext();
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext));
ASSERT_EGL_SUCCESS();
EGLint queryIsFixedSize = 0;
EXPECT_EGL_TRUE(
eglQuerySurface(mDisplay, mWindowSurface, EGL_FIXED_SIZE_ANGLE, &queryIsFixedSize));
ASSERT_EGL_SUCCESS();
EXPECT_EGL_TRUE(queryIsFixedSize);
EGLint queryWidth = 0;
EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &queryWidth));
ASSERT_EGL_SUCCESS();
EXPECT_EQ(kInitialSize, queryWidth);
EGLint queryHeight = 0;
EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &queryHeight));
ASSERT_EGL_SUCCESS();
EXPECT_EQ(kInitialSize, queryHeight);
// Update the size
EXPECT_EGL_TRUE(eglSurfaceAttrib(mDisplay, mWindowSurface, EGL_WIDTH, kUpdateSize));
ASSERT_EGL_SUCCESS();
EXPECT_EGL_TRUE(eglWaitNative(EGL_CORE_NATIVE_ENGINE));
ASSERT_EGL_SUCCESS();
EGLint queryUpdatedWidth = 0;
EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &queryUpdatedWidth));
ASSERT_EGL_SUCCESS();
EXPECT_EQ(kUpdateSize, queryUpdatedWidth);
}
TEST_P(EGLSurfaceTest3, MakeCurrentDifferentSurfaces)
{
const EGLint configAttributes[] = {
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
EGLSurface firstPbufferSurface;
EGLSurface secondPbufferSurface;
initializeDisplay();
ANGLE_SKIP_TEST_IF(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &mConfig) == EGL_FALSE);
EGLint surfaceType = 0;
eglGetConfigAttrib(mDisplay, mConfig, EGL_SURFACE_TYPE, &surfaceType);
bool supportsPbuffers = (surfaceType & EGL_PBUFFER_BIT) != 0;
EGLint bindToTextureRGBA = 0;
eglGetConfigAttrib(mDisplay, mConfig, EGL_BIND_TO_TEXTURE_RGBA, &bindToTextureRGBA);
bool supportsBindTexImage = (bindToTextureRGBA == EGL_TRUE);
const EGLint pBufferAttributes[] = {
EGL_WIDTH, 64,
EGL_HEIGHT, 64,
EGL_TEXTURE_FORMAT, supportsPbuffers ? EGL_TEXTURE_RGBA : EGL_NO_TEXTURE,
EGL_TEXTURE_TARGET, supportsBindTexImage ? EGL_TEXTURE_2D : EGL_NO_TEXTURE,
EGL_NONE, EGL_NONE,
};
// Create the surfaces
firstPbufferSurface = eglCreatePbufferSurface(mDisplay, mConfig, pBufferAttributes);
ASSERT_EGL_SUCCESS();
ASSERT_NE(EGL_NO_SURFACE, firstPbufferSurface);
secondPbufferSurface = eglCreatePbufferSurface(mDisplay, mConfig, pBufferAttributes);
ASSERT_EGL_SUCCESS();
ASSERT_NE(EGL_NO_SURFACE, secondPbufferSurface);
initializeMainContext();
// Use the same surface for both draw and read
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, firstPbufferSurface, firstPbufferSurface, mContext));
// TODO(http://www.anglebug.com/6284): Failing with OpenGL ES backend on Android.
// Must be after the eglMakeCurrent() so the renderer string is initialized.
ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsAndroid());
glClearColor(kFloatRed.R, kFloatRed.G, kFloatRed.B, kFloatRed.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
// Use different surfaces for draw and read, read should stay the same
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, secondPbufferSurface, firstPbufferSurface, mContext));
glClearColor(kFloatBlue.R, kFloatBlue.G, kFloatBlue.B, kFloatBlue.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
// Verify draw surface was cleared
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, secondPbufferSurface, secondPbufferSurface, mContext));
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, firstPbufferSurface, secondPbufferSurface, mContext));
ASSERT_EGL_SUCCESS();
// Blit the source surface to the destination surface
glBlitFramebuffer(0, 0, 64, 64, 0, 0, 64, 64, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, firstPbufferSurface, firstPbufferSurface, mContext));
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
#if defined(ANGLE_ENABLE_D3D11)
class EGLSurfaceTestD3D11 : public EGLSurfaceTest
{
protected:
// offset - draw into the texture at offset (|offset|, |offset|)
// pix25 - the expected pixel value at (25, 25)
// pix75 - the expected pixel value at (75, 75)
void testTextureOffset(int offset, UINT pix25, UINT pix75)
{
initializeDisplay();
const EGLint configAttributes[] = {
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
EGLConfig config;
ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));
mConfig = config;
initializeMainContext();
EGLAttrib device = 0;
EGLAttrib newEglDevice = 0;
ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
EGL_D3D11_DEVICE_ANGLE, &device));
angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
ASSERT_TRUE(!!d3d11Device);
constexpr UINT kTextureWidth = 100;
constexpr UINT kTextureHeight = 100;
constexpr Color<uint8_t> kOpaqueBlack(0, 0, 0, 255);
std::vector<Color<uint8_t>> textureData(kTextureWidth * kTextureHeight, kOpaqueBlack);
D3D11_SUBRESOURCE_DATA initialData = {};
initialData.pSysMem = textureData.data();
initialData.SysMemPitch = kTextureWidth * sizeof(kOpaqueBlack);
D3D11_TEXTURE2D_DESC desc = {};
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Width = kTextureWidth;
desc.Height = kTextureHeight;
desc.ArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
angle::ComPtr<ID3D11Texture2D> texture;
HRESULT hr = d3d11Device->CreateTexture2D(&desc, &initialData, &texture);
ASSERT_TRUE(SUCCEEDED(hr));
angle::ComPtr<ID3D11DeviceContext> d3d11Context;
d3d11Device->GetImmediateContext(&d3d11Context);
// Specify a texture offset of (50, 50) when rendering to the pbuffer surface.
const EGLint surfaceAttributes[] = {EGL_WIDTH,
kTextureWidth,
EGL_HEIGHT,
kTextureHeight,
EGL_TEXTURE_OFFSET_X_ANGLE,
offset,
EGL_TEXTURE_OFFSET_Y_ANGLE,
offset,
EGL_NONE};
EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture.Get());
mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
config, surfaceAttributes);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
ASSERT_EGL_SUCCESS();
// glClear should only clear subrect at offset (50, 50) without explicit scissor.
glClearColor(0, 0, 1, 1); // Blue
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(25, 25, 0, 0, pix25, 255);
EXPECT_PIXEL_EQ(75, 75, 0, 0, pix75, 255);
EXPECT_GL_NO_ERROR();
// Drawing with a shader should also update the same subrect only without explicit viewport.
GLuint program = createProgram(); // Red
ASSERT_NE(0u, program);
GLint positionLocation =
glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
glUseProgram(program);
const GLfloat vertices[] = {
-1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
-1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
};
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_PIXEL_EQ(25, 25, pix25, 0, 0, 255);
EXPECT_PIXEL_EQ(75, 75, pix75, 0, 0, 255);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
EXPECT_GL_NO_ERROR();
// Blit framebuffer should also blit to the same subrect despite the dstX/Y arguments.
GLRenderbuffer renderBuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 50, 50);
EXPECT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderBuffer);
EXPECT_GL_NO_ERROR();
glClearColor(0, 1, 0, 1); // Green
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(25, 25, 0, 255, 0, 255);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0u);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, 50, 50, 0, 0, kTextureWidth, kTextureWidth, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0u);
EXPECT_PIXEL_EQ(25, 25, 0, pix25, 0, 255);
EXPECT_PIXEL_EQ(75, 75, 0, pix75, 0, 255);
EXPECT_GL_NO_ERROR();
}
// Draws into a surface at the specified offset using the values of gl_FragCoord in the
// fragment shader.
// texturedimension - dimension of the D3D texture and surface.
// offset - draw into the texture at offset (|offset|, |offset|)
void setupFragCoordOffset(int textureDimension, int offset)
{
ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
initializeDisplay();
EGLAttrib device = 0;
EGLAttrib newEglDevice = 0;
ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
EGL_D3D11_DEVICE_ANGLE, &device));
angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
ASSERT_TRUE(!!d3d11Device);
D3D11_TEXTURE2D_DESC desc = {};
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Width = textureDimension;
desc.Height = textureDimension;
desc.ArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
angle::ComPtr<ID3D11Texture2D> texture;
HRESULT hr = d3d11Device->CreateTexture2D(&desc, nullptr, &texture);
ASSERT_TRUE(SUCCEEDED(hr));
const EGLint surfaceAttributes[] = {EGL_WIDTH,
textureDimension,
EGL_HEIGHT,
textureDimension,
EGL_TEXTURE_OFFSET_X_ANGLE,
offset,
EGL_TEXTURE_OFFSET_Y_ANGLE,
offset,
EGL_NONE};
EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture.Get());
const EGLint configAttributes[] = {
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
EGLConfig config;
ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));
mConfig = config;
mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
config, surfaceAttributes);
ASSERT_EGL_SUCCESS();
initializeMainContext();
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
ASSERT_EGL_SUCCESS();
// Fragment shader that uses the gl_FragCoord values to output the (x, y) position of
// the current pixel as the color.
// - Reverse the offset that was applied to the original coordinates
// - 0.5 is subtracted because gl_FragCoord gives the pixel center
// - Divided by the size to give a max value of 1
std::stringstream fs;
fs << "precision mediump float;"
<< "void main()"
<< "{"
<< " float dimension = float(" << textureDimension << ");"
<< " float offset = float(" << offset << ");"
<< " gl_FragColor = vec4((gl_FragCoord.x + offset - 0.5) / dimension,"
<< " (gl_FragCoord.y + offset - 0.5) / dimension,"
<< " gl_FragCoord.z,"
<< " gl_FragCoord.w);"
<< "}";
GLuint program = createProgram(fs.str().c_str());
ASSERT_NE(0u, program);
glUseProgram(program);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
const GLfloat vertices[] = {
-1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
-1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
};
GLint positionLocation =
glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glDeleteProgram(program);
EXPECT_GL_NO_ERROR();
}
};
// Test that rendering to an IDCompositionSurface using a pbuffer works.
TEST_P(EGLSurfaceTestD3D11, CreateDirectCompositionSurface)
{
ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
initializeDisplay();
EGLAttrib device = 0;
EGLAttrib newEglDevice = 0;
ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
EGL_D3D11_DEVICE_ANGLE, &device));
angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
ASSERT_TRUE(!!d3d11Device);
HMODULE dcompLibrary = LoadLibraryA("dcomp.dll");
if (!dcompLibrary)
{
std::cout << "DirectComposition not supported" << std::endl;
return;
}
typedef HRESULT(WINAPI * PFN_DCOMPOSITION_CREATE_DEVICE2)(IUnknown * dxgiDevice, REFIID iid,
void **dcompositionDevice);
PFN_DCOMPOSITION_CREATE_DEVICE2 createDComp = reinterpret_cast<PFN_DCOMPOSITION_CREATE_DEVICE2>(
GetProcAddress(dcompLibrary, "DCompositionCreateDevice2"));
if (!createDComp)
{
std::cout << "DirectComposition2 not supported" << std::endl;
FreeLibrary(dcompLibrary);
return;
}
angle::ComPtr<IDCompositionDevice> dcompDevice;
HRESULT hr = createDComp(d3d11Device.Get(), IID_PPV_ARGS(dcompDevice.GetAddressOf()));
ASSERT_TRUE(SUCCEEDED(hr));
angle::ComPtr<IDCompositionSurface> dcompSurface;
hr = dcompDevice->CreateSurface(100, 100, DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_PREMULTIPLIED, dcompSurface.GetAddressOf());
ASSERT_TRUE(SUCCEEDED(hr));
angle::ComPtr<ID3D11Texture2D> texture;
POINT updateOffset;
hr = dcompSurface->BeginDraw(nullptr, IID_PPV_ARGS(texture.GetAddressOf()), &updateOffset);
ASSERT_TRUE(SUCCEEDED(hr));
const EGLint configAttributes[] = {
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
EGLConfig config;
ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));
const EGLint surfaceAttributes[] = {EGL_WIDTH,
100,
EGL_HEIGHT,
100,
EGL_TEXTURE_OFFSET_X_ANGLE,
updateOffset.x,
EGL_TEXTURE_OFFSET_Y_ANGLE,
updateOffset.y,
EGL_NONE};
EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture.Get());
mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
config, surfaceAttributes);
ASSERT_EGL_SUCCESS();
mConfig = config;
initializeMainContext();
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
drawWithProgram(program);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Tests drawing into a surface created with negative offsets.
TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithTextureNegativeOffset)
{
ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
testTextureOffset(-50, 255, 0);
}
// Tests drawing into a surface created with offsets.
TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithTextureOffset)
{
ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
testTextureOffset(50, 0, 255);
}
TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithMSAA)
{
ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
// clang-format off
const EGLint configAttributes[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
EGL_SAMPLE_BUFFERS, 1,
EGL_SAMPLES, 4,
EGL_NONE
};
// clang-format on
initializeDisplay();
EGLConfig config;
if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
{
std::cout << "EGLConfig for 4xMSAA is not supported, skipping test" << std::endl;
return;
}
initializeSurface(config);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
GLuint program = createProgram();
ASSERT_NE(0u, program);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
glUseProgram(program);
const GLfloat halfPixelOffset = 0.5f * 2.0f / mOSWindow->getWidth();
// clang-format off
const GLfloat vertices[] =
{
-1.0f + halfPixelOffset, 1.0f, 0.5f,
-1.0f + halfPixelOffset, -1.0f, 0.5f,
1.0f, -1.0f, 0.5f
};
// clang-format on
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_PIXEL_NEAR(0, 0, 127, 0, 0, 255, 10);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
// Tests that gl_FragCoord.xy is offset with the EGL_TEXTURE_OFFSET_[X|Y]_ANGLE values specified
// at surface creation, using positive offsets
TEST_P(EGLSurfaceTestD3D11, FragCoordOffset)
{
constexpr int kTextureDimension = 28;
constexpr int kOffset = 6;
setupFragCoordOffset(kTextureDimension, kOffset);
// With a positive offset, nothing is drawn in any pixels to the left of and above |kOffset|.
for (int x = 0; x < kOffset; x++)
{
for (int y = 0; y < kOffset; y++)
{
EXPECT_PIXEL_EQ(x, y, 0, 0, 0, 0);
}
}
// The rest of the texture's color should be the value of the (x, y) coordinates.
for (int x = kOffset; x < kTextureDimension; x++)
{
for (int y = kOffset; y < kTextureDimension; y++)
{
EXPECT_PIXEL_NEAR(x, y, x * 255.0 / kTextureDimension, y * 255.0 / kTextureDimension,
191, 255, 0.5);
}
}
}
// Tests that gl_FragCoord.xy is offset with the EGL_TEXTURE_OFFSET_[X|Y]_ANGLE values specified
// at surface creation, using negative offsets.
TEST_P(EGLSurfaceTestD3D11, FragCoordOffsetNegative)
{
constexpr int kTextureDimension = 28;
constexpr int kOffset = 6;
setupFragCoordOffset(kTextureDimension, -kOffset);
// With a negative offset, nothing is drawn in pixels to the right of and below |koffset|.
for (int x = kTextureDimension - kOffset; x < kTextureDimension; x++)
{
for (int y = kTextureDimension - kOffset; y < kTextureDimension; y++)
{
EXPECT_PIXEL_EQ(x, y, 0, 0, 0, 0);
}
}
// The rest of the texture's color should be the value of the (x, y) coordinates.
for (int x = 0; x < kTextureDimension - kOffset; x++)
{
for (int y = 0; y < kTextureDimension - kOffset; y++)
{
EXPECT_PIXEL_NEAR(x, y, x * 255.0 / kTextureDimension, y * 255.0 / kTextureDimension,
191, 255, 0.5);
}
}
}
#endif // ANGLE_ENABLE_D3D11
// Verify bliting between two surfaces works correctly.
TEST_P(EGLSurfaceTest3, BlitBetweenSurfaces)
{
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
ASSERT_NE(mContext, EGL_NO_CONTEXT);
EGLSurface surface1;
EGLSurface surface2;
const EGLint surfaceAttributes[] = {
EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE,
};
surface1 = eglCreatePbufferSurface(mDisplay, mConfig, surfaceAttributes);
ASSERT_EGL_SUCCESS();
surface2 = eglCreatePbufferSurface(mDisplay, mConfig, surfaceAttributes);
ASSERT_EGL_SUCCESS();
// Clear surface1.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface1, surface1, mContext));
// TODO(http://www.anglebug.com/6284): Failing with OpenGL ES backend on Android and Windows.
// Must be after the eglMakeCurrent() so the renderer string is initialized.
ANGLE_SKIP_TEST_IF(IsOpenGLES() && (IsAndroid() || IsWindows()));
glClearColor(kFloatRed.R, kFloatRed.G, kFloatRed.B, kFloatRed.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Blit from surface1 to surface2.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface2, surface1, mContext));
glBlitFramebuffer(0, 0, 64, 64, 0, 0, 64, 64, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Confirm surface1 has the clear color.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface1, surface1, mContext));
EXPECT_PIXEL_COLOR_EQ(32, 32, GLColor::red);
// Confirm surface2 has the blited clear color.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface2, surface2, mContext));
EXPECT_PIXEL_COLOR_EQ(32, 32, GLColor::red);
eglDestroySurface(mDisplay, surface1);
eglDestroySurface(mDisplay, surface2);
}
// Verify bliting between two surfaces works correctly.
TEST_P(EGLSurfaceTest3, BlitBetweenSurfacesWithDeferredClear)
{
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
ASSERT_NE(mContext, EGL_NO_CONTEXT);
EGLSurface surface1;
EGLSurface surface2;
const EGLint surfaceAttributes[] = {
EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE,
};
surface1 = eglCreatePbufferSurface(mDisplay, mConfig, surfaceAttributes);
ASSERT_EGL_SUCCESS();
surface2 = eglCreatePbufferSurface(mDisplay, mConfig, surfaceAttributes);
ASSERT_EGL_SUCCESS();
// Clear surface1.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface1, surface1, mContext));
// TODO(http://www.anglebug.com/6284): Failing with OpenGL ES backend on Android and Windows.
// Must be after the eglMakeCurrent() so the renderer string is initialized.
ANGLE_SKIP_TEST_IF(IsOpenGLES() && (IsAndroid() || IsWindows()));
glClearColor(kFloatRed.R, kFloatRed.G, kFloatRed.B, kFloatRed.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Force the clear to be flushed
EXPECT_PIXEL_COLOR_EQ(32, 32, GLColor::red);
// Clear to green, but don't read it back so the clear is deferred.
glClearColor(kFloatGreen.R, kFloatGreen.G, kFloatGreen.B, kFloatGreen.A);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Blit from surface1 to surface2.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface2, surface1, mContext));
glBlitFramebuffer(0, 0, 64, 64, 0, 0, 64, 64, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Confirm surface1 has the clear color.
EXPECT_PIXEL_COLOR_EQ(32, 32, GLColor::green);
// Confirm surface2 has the blited clear color.
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface2, surface2, mContext));
EXPECT_PIXEL_COLOR_EQ(32, 32, GLColor::green);
eglDestroySurface(mDisplay, surface1);
eglDestroySurface(mDisplay, surface2);
}
// Verify switching between a surface with robust resource init and one without still clears alpha.
TEST_P(EGLSurfaceTest, RobustResourceInitAndEmulatedAlpha)
{
// http://anglebug.com/5279
ANGLE_SKIP_TEST_IF(IsNVIDIA() && isGLRenderer() && IsLinux());
// http://anglebug.com/5280
ANGLE_SKIP_TEST_IF(IsAndroid() && IsNexus5X() && isGLESRenderer());
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
ANGLE_SKIP_TEST_IF(
!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_robust_resource_initialization"));
// Initialize and draw red to a Surface with robust resource init enabled.
constexpr EGLint kRGBAAttributes[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE};
EGLint configCount = 0;
EGLConfig rgbaConfig = nullptr;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, kRGBAAttributes, &rgbaConfig, 1, &configCount));
ASSERT_EQ(configCount, 1);
ASSERT_NE(rgbaConfig, nullptr);
std::vector<EGLint> robustInitAttribs;
robustInitAttribs.push_back(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE);
robustInitAttribs.push_back(EGL_TRUE);
initializeSurfaceWithAttribs(rgbaConfig, robustInitAttribs);
ASSERT_EGL_SUCCESS();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
initializeMainContext();
ASSERT_EGL_SUCCESS();
ASSERT_NE(mContext, EGL_NO_CONTEXT);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
eglSwapBuffers(mDisplay, mWindowSurface);
// RGBA robust init setup complete. Draw red and verify.
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
eglSwapBuffers(mDisplay, mWindowSurface);
}
tearDownContextAndSurface();
// Create second RGB surface with robust resource disabled.
constexpr EGLint kRGBAttributes[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE};
configCount = 0;
EGLConfig rgbConfig = nullptr;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, kRGBAttributes, &rgbConfig, 1, &configCount));
ASSERT_EQ(configCount, 1);
ASSERT_NE(rgbConfig, nullptr);
initializeSurface(rgbConfig);
ASSERT_EGL_SUCCESS();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
// RGB non-robust init setup complete. Draw red and verify.
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
eglSwapBuffers(mDisplay, mWindowSurface);
}
}
void EGLSurfaceTest::drawQuadThenTearDown()
{
initializeMainContext();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
{
ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
}
tearDownContextAndSurface();
}
// Tests the EGL_ANGLE_create_surface_swap_interval extension if available.
TEST_P(EGLSurfaceTest, CreateSurfaceSwapIntervalANGLE)
{
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
mConfig = chooseDefaultConfig(true);
ASSERT_NE(mConfig, nullptr);
if (IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_create_surface_swap_interval"))
{
// Test error conditions.
EGLint minSwapInterval = 0;
eglGetConfigAttrib(mDisplay, mConfig, EGL_MIN_SWAP_INTERVAL, &minSwapInterval);
ASSERT_EGL_SUCCESS();
if (minSwapInterval > 0)
{
std::vector<EGLint> min1SwapAttribs = {EGL_SWAP_INTERVAL_ANGLE, minSwapInterval - 1};
initializeWindowSurfaceWithAttribs(mConfig, min1SwapAttribs, EGL_BAD_ATTRIBUTE);
}
EGLint maxSwapInterval = 0;
eglGetConfigAttrib(mDisplay, mConfig, EGL_MAX_SWAP_INTERVAL, &maxSwapInterval);
ASSERT_EGL_SUCCESS();
if (maxSwapInterval < std::numeric_limits<EGLint>::max())
{
std::vector<EGLint> max1SwapAttribs = {EGL_SWAP_INTERVAL_ANGLE, maxSwapInterval + 1};
initializeWindowSurfaceWithAttribs(mConfig, max1SwapAttribs, EGL_BAD_ATTRIBUTE);
}
// Test valid min/max usage.
{
std::vector<EGLint> minSwapAttribs = {EGL_SWAP_INTERVAL_ANGLE, minSwapInterval};
initializeWindowSurfaceWithAttribs(mConfig, minSwapAttribs, EGL_SUCCESS);
drawQuadThenTearDown();
}
if (minSwapInterval != maxSwapInterval)
{
std::vector<EGLint> maxSwapAttribs = {EGL_SWAP_INTERVAL_ANGLE, maxSwapInterval};
initializeWindowSurfaceWithAttribs(mConfig, maxSwapAttribs, EGL_SUCCESS);
drawQuadThenTearDown();
}
}
else
{
// Test extension unavailable error.
std::vector<EGLint> swapInterval1Attribs = {EGL_SWAP_INTERVAL_ANGLE, 1};
initializeWindowSurfaceWithAttribs(mConfig, swapInterval1Attribs, EGL_BAD_ATTRIBUTE);
}
}
// Test that setting a surface's timestamp attribute works when the extension
// EGL_ANGLE_timestamp_surface_attribute is supported.
TEST_P(EGLSurfaceTest, TimestampSurfaceAttribute)
{
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
mConfig = chooseDefaultConfig(true);
ASSERT_NE(mConfig, nullptr);
initializeSurface(mConfig);
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
initializeMainContext();
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
const bool extensionSupported =
IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANDROID_get_frame_timestamps") ||
IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_timestamp_surface_attribute");
EGLBoolean setSurfaceAttrib =
eglSurfaceAttrib(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
if (extensionSupported)
{
EXPECT_EGL_TRUE(setSurfaceAttrib);
// Swap so the swapchain gets created.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, mWindowSurface));
// Query to confirm the attribute persists across swaps.
EGLint timestampEnabled = 0;
EXPECT_EGL_TRUE(
eglQuerySurface(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, &timestampEnabled));
EXPECT_NE(timestampEnabled, 0);
// Resize window and swap.
mOSWindow->resize(256, 256);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, mWindowSurface));
// Query to confirm the attribute persists across swapchain recreations.
timestampEnabled = 0;
EXPECT_EGL_TRUE(
eglQuerySurface(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, &timestampEnabled));
EXPECT_NE(timestampEnabled, 0);
}
else
{
EXPECT_EGL_FALSE(setSurfaceAttrib);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
}
TEST_P(EGLSingleBufferTest, OnCreateWindowSurface)
{
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, false));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_SINGLE_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
EGLint actualRenderbuffer;
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
if (actualRenderbuffer == EGL_SINGLE_BUFFER)
{
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_SINGLE_BUFFER);
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
ASSERT_GL_NO_ERROR();
// Flush should result in update of screen. Must be visually confirmed.
// Pixel test for automation.
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
else
{
std::cout << "SKIP test, no EGL_SINGLE_BUFFER support." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
TEST_P(EGLSingleBufferTest, OnSetSurfaceAttrib)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
// Transition into EGL_SINGLE_BUFFER mode.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
EGLint actualRenderbuffer;
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_SINGLE_BUFFER);
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
// Flush should result in update of screen. Must be visually confirmed Green window.
// Check color for automation.
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
// Switch back to EGL_BACK_BUFFEr and check.
EXPECT_EGL_TRUE(eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_BACK_BUFFER));
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_BACK_BUFFER);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
uint32_t EGLSingleBufferTest::drawAndSwap(EGLSurface &surface,
EGLDisplay &display,
uint32_t color,
bool flush)
{
ASSERT(color < 256);
glClearColor((float)color / 255.f, (float)color / 255.f, (float)color / 255.f,
(float)color / 255.f);
glClear(GL_COLOR_BUFFER_BIT);
if (flush)
{
glFlush();
}
else
{
eglSwapBuffers(display, surface);
}
return (color | color << 8 | color << 16 | color << 24);
}
// Replicate dEQP-EGL.functional.mutable_render_buffer#basic
TEST_P(EGLSingleBufferTest, MutableRenderBuffer)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
const EGLint attribs[] = {EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | EGL_MUTABLE_RENDER_BUFFER_BIT_KHR,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_NONE};
EGLint count = 0;
ANGLE_SKIP_TEST_IF(!eglChooseConfig(mDisplay, attribs, &config, 1, &count));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
int frameNumber = 1;
// run a few back-buffered frames
for (; frameNumber < 5; frameNumber++)
{
drawAndSwap(surface, mDisplay, frameNumber, false);
}
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
drawAndSwap(surface, mDisplay, frameNumber, false);
frameNumber++;
// test a few single-buffered frames
for (; frameNumber < 10; frameNumber++)
{
uint32_t backBufferPixel = 0xFFFFFFFF;
uint32_t frontBufferPixel = drawAndSwap(surface, mDisplay, frameNumber, true);
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &backBufferPixel);
EXPECT_EQ(backBufferPixel, frontBufferPixel);
}
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
// switch back to back-buffer rendering
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_BACK_BUFFER))
{
for (; frameNumber < 14; frameNumber++)
{
drawAndSwap(surface, mDisplay, frameNumber, false);
}
}
else
{
std::cout << "EGL_BACK_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
// Tests bug with incorrect ImageLayout::SharedPresent barrier.
TEST_P(EGLSingleBufferTest, SharedPresentBarrier)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
// Transition into EGL_SINGLE_BUFFER mode.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
EGLint actualRenderbuffer;
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_SINGLE_BUFFER);
for (int i = 0; i < 5; ++i)
{
GLColor testColor(rand() % 256, rand() % 256, rand() % 256, 255);
angle::Vector4 clearColor = testColor.toNormalizedVector();
glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
glClear(GL_COLOR_BUFFER_BIT);
// Skip flush because present operations may add other barriers that will make appear
// that everything works as expected.
// Check color without flush - may get invalid result if have incorrect barrier bug.
EXPECT_PIXEL_COLOR_EQ(1, 1, testColor);
}
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
// Tests scissored clear on single buffer surface
TEST_P(EGLSingleBufferTest, ScissoredClear)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
eglSwapBuffers(mDisplay, surface);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
glEnable(GL_SCISSOR_TEST);
glScissor(1, 1, 10, 10);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
// Tests scissored clear on single buffer surface
TEST_P(EGLSingleBufferTest, ScissoredDraw)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
eglSwapBuffers(mDisplay, surface);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
glEnable(GL_SCISSOR_TEST);
glScissor(1, 1, 10, 10);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
glFlush();
glDisable(GL_SCISSOR_TEST);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(2, 2, GLColor::green);
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
// Tests that "one off" submission is waited before destroying the surface.
TEST_P(EGLSingleBufferTest, WaitOneOffSubmission)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLContext context2 = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context2));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
const EGLint pbufferSurfaceAttrs[] = {
EGL_WIDTH, 1024, EGL_HEIGHT, 1024, EGL_NONE,
};
EGLSurface pbufferSurface = eglCreatePbufferSurface(mDisplay, config, pbufferSurfaceAttrs);
ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
// Query age for the first time to avoid submitting debug information a second time.
EGLint age = 0;
EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, surface, EGL_BUFFER_AGE_EXT, &age));
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
// Transition into EGL_SINGLE_BUFFER mode.
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
// Submit heavy work to the GPU before querying the buffer age.
std::thread([this, context2, pbufferSurface]() {
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, pbufferSurface, pbufferSurface, context2));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuadInstanced(greenProgram, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, false,
1000);
EXPECT_EGL_TRUE(
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
}).join();
// Querying the buffer age should perform first acquire of the image and "one off"
// submission to change image layout to the VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR.
EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, surface, EGL_BUFFER_AGE_EXT, &age));
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroySurface(mDisplay, pbufferSurface);
pbufferSurface = EGL_NO_SURFACE;
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
eglDestroyContext(mDisplay, context2);
context2 = EGL_NO_CONTEXT;
}
// Test that setting a surface to EGL_SINGLE_BUFFER after enabling
// EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID does not disable auto refresh
TEST_P(EGLAndroidAutoRefreshTest, Basic)
{
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_mutable_render_buffer"));
ANGLE_SKIP_TEST_IF(!IsAndroid());
EGLConfig config = EGL_NO_CONFIG_KHR;
ANGLE_SKIP_TEST_IF(!chooseConfig(&config, true));
EGLContext context = EGL_NO_CONTEXT;
EXPECT_EGL_TRUE(createContext(config, &context));
ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
EGLSurface surface = EGL_NO_SURFACE;
OSWindow *osWindow = OSWindow::New();
osWindow->initialize("EGLSingleBufferTest", kWidth, kHeight);
EXPECT_EGL_TRUE(
createWindowSurface(config, osWindow->getNativeWindow(), &surface, EGL_BACK_BUFFER));
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
EXPECT_EGL_TRUE(
eglSurfaceAttrib(mDisplay, surface, EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID, EGL_TRUE));
if (eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER))
{
// Transition into EGL_SINGLE_BUFFER mode.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
EGLint actualRenderbuffer;
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_SINGLE_BUFFER);
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
// Flush should result in update of screen. Must be visually confirmed Green window.
// Check color for automation.
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
// Switch back to EGL_BACK_BUFFER and check.
EXPECT_EGL_TRUE(eglSurfaceAttrib(mDisplay, surface, EGL_RENDER_BUFFER, EGL_BACK_BUFFER));
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, surface));
EXPECT_EGL_TRUE(eglQueryContext(mDisplay, context, EGL_RENDER_BUFFER, &actualRenderbuffer));
EXPECT_EGL_TRUE(actualRenderbuffer == EGL_BACK_BUFFER);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
}
else
{
std::cout << "EGL_SINGLE_BUFFER mode is not supported." << std::endl;
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
eglDestroySurface(mDisplay, surface);
surface = EGL_NO_SURFACE;
osWindow->destroy();
OSWindow::Delete(&osWindow);
eglDestroyContext(mDisplay, context);
context = EGL_NO_CONTEXT;
}
void EGLSurfaceTest::runWaitSemaphoreTest(bool useSecondContext)
{
// Note: This test requires visual inspection for rendering artifacts.
// However, absence of artifacts does not guarantee that there is no problem.
initializeDisplay();
constexpr int kInitialSize = 64;
constexpr int kWindowWidth = 1080;
constexpr int kWindowWHeight = 1920;
mOSWindow->resize(kWindowWidth, kWindowWHeight);
// Initialize an RGBA8 window and pbuffer surface
constexpr EGLint kSurfaceAttributes[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_NONE};
EGLint configCount = 0;
EGLConfig surfaceConfig = nullptr;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, kSurfaceAttributes, &surfaceConfig, 1, &configCount));
ASSERT_NE(configCount, 0);
ASSERT_NE(surfaceConfig, nullptr);
initializeSurface(surfaceConfig);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
ASSERT_NE(mPbufferSurface, EGL_NO_SURFACE);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
if (useSecondContext)
{
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
initializeSingleContext(&mSecondContext, 0);
}
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
ASSERT_GL_NO_ERROR();
GLint posAttrib = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(posAttrib, -1);
glEnableVertexAttribArray(posAttrib);
ASSERT_GL_NO_ERROR();
GLint colorUniformLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
constexpr int kFrameCount = 60 * 4; // 4 sec @ 60Hz; 2 sec @ 120Hz;
constexpr int kGridW = 5;
constexpr int kGridH = 5;
constexpr int kAnimDiv = 20;
for (int frame = 0; frame < kFrameCount; ++frame)
{
glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
for (int y = 0; y < kGridH; ++y)
{
// This should force "flushToPrimary()" each line in ANGLE
GLuint query;
glGenQueries(1, &query);
ASSERT_GL_NO_ERROR();
glBeginQuery(GL_TIME_ELAPSED_EXT, query);
ASSERT_GL_NO_ERROR();
for (int x = 0; x < kGridW; ++x)
{
const int xc = (x + frame / kAnimDiv) % kGridW;
const Vector4 color = {(xc + 0.5f) / kGridW, (y + 0.5f) / kGridH, 0.0f, 1.0f};
const GLfloat x0 = (x + 0.1f) / kGridW * 2.0f - 1.0f;
const GLfloat x1 = (x + 0.9f) / kGridW * 2.0f - 1.0f;
const GLfloat y0 = (y + 0.1f) / kGridH * 2.0f - 1.0f;
const GLfloat y1 = (y + 0.9f) / kGridH * 2.0f - 1.0f;
std::array<Vector3, 6> vertexData;
vertexData[0] = {x0, y1, 0.5f};
vertexData[1] = {x0, y0, 0.5f};
vertexData[2] = {x1, y1, 0.5f};
vertexData[3] = {x1, y1, 0.5f};
vertexData[4] = {x0, y0, 0.5f};
vertexData[5] = {x1, y0, 0.5f};
glUniform4f(colorUniformLocation, color.x(), color.y(), color.z(), color.w());
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, vertexData.data());
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
}
glEndQuery(GL_TIME_ELAPSED_EXT);
glDeleteQueries(1, &query);
ASSERT_GL_NO_ERROR();
}
if (useSecondContext)
{
std::thread([this] {
eglBindAPI(EGL_OPENGL_ES_API);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mSecondContext);
ASSERT_EGL_SUCCESS();
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
ASSERT_EGL_SUCCESS();
}).join();
}
else
{
eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
ASSERT_EGL_SUCCESS();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
}
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
}
mOSWindow->resize(kInitialSize, kInitialSize);
}
// Test that there no artifacts because of the bug when wait semaphore could be added after
// rendering commands. This was possible by switching to Pbuffer surface and submit.
TEST_P(EGLSurfaceTest, DISABLED_WaitSemaphoreAddedAfterCommands)
{
runWaitSemaphoreTest(false);
}
// Test that there no artifacts because of the bug when rendering commands could be submitted
// without adding wait semaphore. This was possible if submit commands from other thread.
TEST_P(EGLSurfaceTest, DISABLED_CommandsSubmittedWithoutWaitSemaphore)
{
runWaitSemaphoreTest(true);
}
void EGLSurfaceTest::runDestroyNotCurrentSurfaceTest(bool testWindowsSurface)
{
initializeDisplay();
// Initialize an RGBA8 window and pbuffer surface
constexpr EGLint kSurfaceAttributes[] = {EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_NONE};
EGLint configCount = 0;
EGLConfig surfaceConfig = nullptr;
ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, kSurfaceAttributes, &surfaceConfig, 1, &configCount));
ASSERT_NE(configCount, 0);
ASSERT_NE(surfaceConfig, nullptr);
initializeSurface(surfaceConfig);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
ASSERT_NE(mPbufferSurface, EGL_NO_SURFACE);
EGLSurface &testSurface = testWindowsSurface ? mWindowSurface : mPbufferSurface;
EGLSurface &otherSurface = testWindowsSurface ? mPbufferSurface : mWindowSurface;
eglMakeCurrent(mDisplay, testSurface, testSurface, mContext);
ASSERT_EGL_SUCCESS();
// Start RenderPass in the testSurface
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 4, 4);
glClearColor(0.5f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
ASSERT_GL_NO_ERROR();
// Make other surface current keeping the context.
// If bug present, the context may have unflushed work, related to the testSurface.
eglMakeCurrent(mDisplay, otherSurface, otherSurface, mContext);
ASSERT_EGL_SUCCESS();
if (testWindowsSurface)
{
// This may flush Window Surface RenderPass
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 4, 4);
glClearColor(0.5f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
ASSERT_GL_NO_ERROR();
}
// Destroy the surface
eglDestroySurface(mDisplay, testSurface);
testSurface = EGL_NO_SURFACE;
// This will submit all work (if bug present - include work related to the deleted testSurface).
eglMakeCurrent(mDisplay, otherSurface, otherSurface, mContext);
ASSERT_EGL_SUCCESS();
}
// Test that there is no crash because of the bug when not current PBuffer Surface destroyed, while
// there are still unflushed work in the Context.
TEST_P(EGLSurfaceTest, DestroyNotCurrentPbufferSurface)
{
runDestroyNotCurrentSurfaceTest(false);
}
// Test that there is no crash because of the bug when not current Window Surface destroyed, while
// there are still unflushed work in the Context.
TEST_P(EGLSurfaceTest, DestroyNotCurrentWindowSurface)
{
runDestroyNotCurrentSurfaceTest(true);
}
// Test that there is no tearing because of incorrect pipeline barriers
TEST_P(EGLSurfaceTest, DISABLED_RandomClearTearing)
{
// Note: This test requires visual inspection for rendering artifacts.
// However, absence of artifacts does not guarantee that there is no problem.
initializeDisplay();
constexpr int kInitialSize = 64;
constexpr int kWindowWidth = 1080;
constexpr int kWindowWHeight = 1920;
mOSWindow->resize(kWindowWidth, kWindowWHeight);
initializeSurfaceWithDefaultConfig(true);
initializeMainContext();
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
constexpr int kFrameCount = 60 * 4; // 4 sec @ 60Hz; 2 sec @ 120Hz;
for (int frame = 0; frame < kFrameCount; ++frame)
{
glClearColor(rand() % 256 / 255.0f, rand() % 256 / 255.0f, rand() % 256 / 255.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
}
mOSWindow->resize(kInitialSize, kInitialSize);
}
} // anonymous namespace
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLSingleBufferTest);
ANGLE_INSTANTIATE_TEST(EGLSingleBufferTest,
WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLAndroidAutoRefreshTest);
ANGLE_INSTANTIATE_TEST(EGLAndroidAutoRefreshTest, WithNoFixture(ES3_VULKAN()));
ANGLE_INSTANTIATE_TEST(EGLSurfaceTest,
WithNoFixture(ES2_D3D9()),
WithNoFixture(ES2_D3D11()),
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES2_OPENGLES()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES2_VULKAN_SWIFTSHADER()),
WithNoFixture(ES3_VULKAN_SWIFTSHADER()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLFloatSurfaceTest);
ANGLE_INSTANTIATE_TEST(EGLFloatSurfaceTest,
WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLSurfaceTest3);
ANGLE_INSTANTIATE_TEST(EGLSurfaceTest3,
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES3_VULKAN_SWIFTSHADER()));
#if defined(ANGLE_ENABLE_D3D11)
ANGLE_INSTANTIATE_TEST(EGLSurfaceTestD3D11, WithNoFixture(ES2_D3D11()), WithNoFixture(ES3_D3D11()));
#endif