Edit

kc3-lang/angle/src/tests/egl_tests/EGLSurfaceTest.cpp

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2021-07-22 19:06:40
    Hash : 8bb3c827
    Message : Fix Multithreaded eglDestroyContext()/eglTerminate() The following EGL calls can lead to a crash in eglMakeCurrent(): Thread A: eglMakeCurrent(context A) Thread B: eglDestroyContext(context A) B: eglTerminate() <<--- this release context A Thread A: eglMakeCurrent(context B) The eglMakeCurrent(context B) call will assert when attempting to unMakeCurrent(), since thread A doesn't know that context A was already destroyed by thread B. To fix this: 1.) A Context will only be released once there are no Threads that currently have a reference to it (no longer have the Context current). - Context::mIsCurrent is being removed, since it was inaccurate and not thread-safe. For example, when eglTerminate() was called, the eglTerminate()'ing-Thread would "steal" the Context that was current on another Thread to destroy it. 2.) A Display will only be fully terminated and its resources released once all Contexts have been destroyed and are no longer current. Otherwise, Display::terminate() will return if any Contexts are still in use by a Thread. EGL 1.5 Specification 3.2 Initialization If contexts or surfaces, created with respect to dpy are current (see section 3.7.3) to any thread, then they are not actually destroyed while they remain current. If other resources created with respect to dpy are in use by any current context or surface, then they are also not destroyed until the corresponding context or surface is no longer current. With this fix, the app com.netmarble.sknightsmmo can start. This also exposed an issue with GlslangFinalize(), since glslang can only be initialized/finalized once per process. Otherwise, the following EGL commands will call GlslangFinalize() without ever being able to GlslangInitialize() again, leading to crashes since GlslangFinalize() cleans up glslang for the entire process. dpy1 = eglGetPlatformDisplay() | eglInitialize(dpy1) | GlslangInitialize() dpy2 = eglGetPlatformDisplay() | eglInitialize(dpy2) | GlslangInitialize() eglTerminate(dpy2) | GlslangFinalize() eglInitialize(dpy1) | isInitialized() == true Since Display::isInitialized() == true, the rest of Display::initialize() is skipped and GlslangInitialize() is not called. Later, the next test that attempts to compile a program will crash due to glslang no longer being initialized. Finally, this exposed the following tests leaking EGLContext handles: - EGLSurfaceTest::initializeContext() - EGLContextSharingTest.DisplayShareGroupContextCreation - EGLCreateContextAttribsTest.IMGContextPriorityExtension - EGLMultiContextTest.TestContextDestroySimple Other tests were failing to reset the context, preventing the Display from being terminated since there were still references to Contexts owned by the display: eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); Bug: angleproject:6208 Bug: angleproject:6304 Bug: angleproject:6322 Test: EGLContextSharingTest.EglTerminateMultiThreaded Test: EGLContextSharingTestNoFixture.EglDestoryContextManyTimesSameContext Test: Load com.netmarble.sknightsmmo Change-Id: I160922af93db6cabe0ed396be77762fa8dfc7656 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3046961 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/tests/egl_tests/EGLSurfaceTest.cpp
  • //
    // 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 <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 contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, GetParam().majorVersion, EGL_NONE};
    
            *context = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes);
            ASSERT_EGL_SUCCESS();
        }
    
        void initializeContext()
        {
            // Only initialize the contexts once.
            if (mContext == EGL_NO_CONTEXT)
            {
                initializeSingleContext(&mContext);
            }
            if (mSecondContext == EGL_NO_CONTEXT)
            {
                initializeSingleContext(&mSecondContext);
            }
        }
    
        void initializeWindowSurfaceWithAttribs(EGLConfig config,
                                                const std::vector<EGLint> &additionalAttributes,
                                                EGLenum expectedResult)
        {
            ASSERT_EQ(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)
            {
                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();
            }
    
            initializeContext();
        }
    
        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()
        {
            return CompileProgram(angle::essl1_shaders::vs::Simple(), angle::essl1_shaders::fs::Red());
        }
    
        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 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);
    
            eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
            mProgram = createProgram();
            return true;
        }
    
        GLuint mProgram;
    };
    
    // 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);
    
        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);
    
        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);
    
        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);
    }
    
    // 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);
        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);
        initializeContext();
        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);
        initializeContext();
        ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
    
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    }
    
    // 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);
    
        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);
    
        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);
    
        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);
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        drawWithProgram(program);
        EXPECT_GL_NO_ERROR();
        glDeleteProgram(program);
    }
    
    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);
    
        initializeContext();
        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);
    
        initializeContext();
    
        // 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(GLColor::red.R, GLColor::red.G, GLColor::red.B, GLColor::red.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(GLColor::blue.R, GLColor::blue.G, GLColor::blue.B, GLColor::blue.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
    {};
    
    // 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_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE,
                                            EGL_TRUE,
                                            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;
        initializeContext();
    
        eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        drawWithProgram(program);
        EXPECT_GL_NO_ERROR();
        glDeleteProgram(program);
    }
    
    TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithTextureOffset)
    {
        ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
        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;
        initializeContext();
    
        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_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE,
                                            EGL_TRUE,
                                            EGL_TEXTURE_OFFSET_X_ANGLE,
                                            50,
                                            EGL_TEXTURE_OFFSET_Y_ANGLE,
                                            50,
                                            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, 0, 255);
        EXPECT_PIXEL_EQ(75, 75, 0, 0, 255, 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, 0, 0, 0, 255);
        EXPECT_PIXEL_EQ(75, 75, 255, 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, 50, 50, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        EXPECT_GL_NO_ERROR();
    
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0u);
        EXPECT_PIXEL_EQ(25, 25, 0, 0, 0, 255);
        EXPECT_PIXEL_EQ(75, 75, 0, 255, 0, 255);
        EXPECT_GL_NO_ERROR();
    }
    
    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);
    
        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);
    }
    
    #endif  // ANGLE_ENABLE_D3D11
    
    // Verify bliting between two surfaces works correctly.
    TEST_P(EGLSurfaceTest3, BlitBetweenSurfaces)
    {
        initializeDisplay();
        ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
    
        initializeSurfaceWithDefaultConfig(true);
        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(GLColor::red.R, GLColor::red.G, GLColor::red.B, GLColor::red.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);
        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(GLColor::red.R, GLColor::red.G, GLColor::red.B, GLColor::red.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(GLColor::green.R, GLColor::green.G, GLColor::green.B, GLColor::green.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);
    
        initializeSingleContext(&mContext);
        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);
    
        initializeSingleContext(&mContext);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(mContext, EGL_NO_CONTEXT);
    
        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()
    {
        initializeSingleContext(&mContext);
    
        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);
        }
    }
    }  // anonymous namespace
    
    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