//
// Copyright 2021 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.
//
// EGLLockSurface3Test.cpp:
//   EGL extension EGL_KHR_lock_surface
//

#include <gtest/gtest.h>

#include <chrono>
#include <iostream>
#include <thread>
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "util/OSWindow.h"

using namespace std::chrono_literals;

using namespace angle;

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLLockSurface3Test);

class EGLLockSurface3Test : public ANGLETest<>
{
  public:
    EGLLockSurface3Test() : mDisplay(EGL_NO_DISPLAY) {}

    void testSetUp() override
    {
        mMajorVersion = GetParam().majorVersion;

        EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
        mDisplay           = eglGetPlatformDisplayEXT(
                      EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
        EXPECT_NE(mDisplay, EGL_NO_DISPLAY);
        EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
    }

    bool supportsLockSurface3Extension()
    {
        return IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_lock_surface3");
    }

    void testTearDown() override
    {
        if (mDisplay != EGL_NO_DISPLAY)
        {
            eglTerminate(mDisplay);
            eglReleaseThread();
            mDisplay = EGL_NO_DISPLAY;
        }
        ASSERT_EGL_SUCCESS() << "Error during test TearDown";
    }

    // This will work for Windows and linux systemsAny other BGRA systems will have to be included.
    GLColor ChannelOrder(GLColor colorIn)
    {
        GLColor color = colorIn;
        if (IsWindows() || IsLinux())
        {  // BGRA channel order
            color.R = colorIn.B;
            color.B = colorIn.R;
        }
        return color;
    }

    void fillBitMapRGBA32(GLColor colorIn, uint32_t *bitMapPtr, EGLint stride)
    {
        for (uint32_t y = 0; y < kHeight; y++)
        {
            uint32_t *pixelPtr = bitMapPtr + (y * (stride / 4));
            for (uint32_t x = 0; x < kWidth; x++)
            {
                pixelPtr[x] = ChannelOrder(colorIn).asUint();
            }
        }
    }

    bool checkBitMapRGBA32(GLColor colorIn, uint32_t *bitMapPtr, EGLint stride)
    {
        std::array<uint32_t, (kWidth * kHeight)> checkmap;
        std::fill_n(checkmap.begin(), (kWidth * kHeight), ChannelOrder(colorIn).asUint());

        std::array<uint32_t, (kWidth * kHeight)> bitmap;
        for (uint32_t i = 0; i < (kWidth * kHeight); i++)
        {
            bitmap[i] = bitMapPtr[i];
        }

        if (bitmap != checkmap)
        {
            return false;
        }

        return true;
    }

    bool checkSurfaceRGBA32(GLColor color)
    {
        std::array<uint32_t, (kWidth * kHeight)> actual;
        glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, actual.data());
        EXPECT_GL_NO_ERROR();

        std::array<uint32_t, (kWidth * kHeight)> checkmap;
        std::fill_n(checkmap.begin(), (kWidth * kHeight), color.asUint());

        if (actual != checkmap)
        {
            return false;
        }

        return true;
    }

    GLuint createTexture()
    {
        GLuint texture = 0;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     nullptr);
        EXPECT_GL_NO_ERROR();
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
        EXPECT_GL_NO_ERROR();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        EXPECT_NE(texture, static_cast<GLuint>(0));
        return texture;
    }

    bool fillTexture(GLuint textureId, GLColor color)
    {
        std::array<GLuint, (kWidth * kHeight)> pixels;
        std::fill_n(pixels.begin(), (kWidth * kHeight), color.asUint());
        glBindTexture(GL_TEXTURE_2D, textureId);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE,
                        static_cast<void *>(pixels.data()));
        EXPECT_GL_NO_ERROR();
        return true;
    }

    void renderTexture(GLuint textureId)
    {
        const char *kVertexShader   = R"(
            precision highp float;
            attribute vec4 position;
            varying vec2 texcoord;

            void main()
            {
                gl_Position = vec4(position.xy, 0.0, 1.0);
                texcoord = (position.xy * 0.5) + 0.5;
            }
        )";
        const char *kFragmentShader = R"(
            precision highp float;
            uniform sampler2D tex;
            varying vec2 texcoord;

            void main()
            {
                gl_FragColor = texture2D(tex, texcoord);
            }
        )";

        ANGLE_GL_PROGRAM(program, kVertexShader, kFragmentShader);
        glUseProgram(program);
        glBindTexture(GL_TEXTURE_2D, textureId);
        glActiveTexture(GL_TEXTURE0);
        GLint texture2DUniformLocation = glGetUniformLocation(program, "tex");
        glUniform1i(texture2DUniformLocation, 0);
        drawQuad(program, "position", 0.5f);
        glDeleteProgram(program);
        EXPECT_GL_NO_ERROR();
    }

    int mMajorVersion   = 2;
    EGLDisplay mDisplay = EGL_NO_DISPLAY;

    static constexpr EGLint kWidth  = 5;
    static constexpr EGLint kHeight = 5;
};

// Create PBufferSurface, Lock, check all the attributes, unlock.
TEST_P(EGLLockSurface3Test, AttributeTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    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,
                            8,
                            EGL_RENDERABLE_TYPE,
                            clientVersion,
                            EGL_SURFACE_TYPE,
                            (EGL_PBUFFER_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                            EGL_NONE};
    EGLint count         = 0;
    EGLConfig config     = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, attribs, &config, 1, &count));
    EXPECT_GT(count, 0);
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);

    EGLint pBufferAttribs[]   = {EGL_WIDTH, kWidth, EGL_HEIGHT, kHeight, EGL_NONE};
    EGLSurface pBufferSurface = eglCreatePbufferSurface(mDisplay, config, pBufferAttribs);
    EXPECT_NE(pBufferSurface, EGL_NO_SURFACE);

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_WRITE_SURFACE_BIT_KHR, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, pBufferSurface, lockAttribs));

    EGLAttribKHR bitMapPtr = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_POINTER_KHR, &bitMapPtr));
    EXPECT_NE(bitMapPtr, 0);

    EGLAttribKHR bitMapPitch = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));
    EXPECT_NE(bitMapPitch, 0);

    EGLAttribKHR bitMapOrigin = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_ORIGIN_KHR, &bitMapOrigin));
    EXPECT_TRUE((bitMapOrigin == EGL_LOWER_LEFT_KHR) || (bitMapOrigin == EGL_UPPER_LEFT_KHR));

    EGLAttribKHR bitMapPixelRedOffset = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_RED_OFFSET_KHR,
                                         &bitMapPixelRedOffset));
    EXPECT_TRUE((bitMapPixelRedOffset == 0) || (bitMapPixelRedOffset == 16));

    EGLAttribKHR bitMapPixelGreenOffset = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(
        mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR, &bitMapPixelGreenOffset));
    EXPECT_EQ(bitMapPixelGreenOffset, 8);

    EGLAttribKHR bitMapPixelBlueOffset = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR,
                                         &bitMapPixelBlueOffset));
    EXPECT_TRUE((bitMapPixelBlueOffset == 16) || (bitMapPixelBlueOffset == 0));

    EGLAttribKHR bitMapPixelAlphaOffset = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(
        mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR, &bitMapPixelAlphaOffset));
    EXPECT_EQ(bitMapPixelAlphaOffset, 24);

    EGLAttribKHR bitMapPixelLuminanceOffset = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, pBufferSurface,
                                         EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR,
                                         &bitMapPixelLuminanceOffset));
    EXPECT_EQ(bitMapPixelLuminanceOffset, 0);

    EGLAttribKHR bitMapPixelSize = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_SIZE_KHR,
                                         &bitMapPixelSize));
    EXPECT_EQ(bitMapPixelSize, 32);

    EGLint bitMapPitchInt = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitchInt));
    EXPECT_NE(bitMapPitchInt, 0);

    EGLint bitMapOriginInt = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_ORIGIN_KHR, &bitMapOriginInt));
    EXPECT_TRUE((bitMapOriginInt == EGL_LOWER_LEFT_KHR) || (bitMapOriginInt == EGL_UPPER_LEFT_KHR));

    EGLint bitMapPixelRedOffsetInt = 0;
    EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_RED_OFFSET_KHR,
                                    &bitMapPixelRedOffsetInt));
    EXPECT_TRUE((bitMapPixelRedOffsetInt == 0) || (bitMapPixelRedOffsetInt == 16));

    EGLint bitMapPixelGreenOffsetInt = 0;
    EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR,
                                    &bitMapPixelGreenOffsetInt));
    EXPECT_EQ(bitMapPixelGreenOffsetInt, 8);

    EGLint bitMapPixelBlueOffsetInt = 0;
    EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR,
                                    &bitMapPixelBlueOffsetInt));
    EXPECT_TRUE((bitMapPixelBlueOffsetInt == 16) || (bitMapPixelBlueOffsetInt == 0));

    EGLint bitMapPixelAlphaOffsetInt = 0;
    EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR,
                                    &bitMapPixelAlphaOffsetInt));
    EXPECT_EQ(bitMapPixelAlphaOffsetInt, 24);

    EGLint bitMapPixelLuminanceOffsetInt = 0;
    EXPECT_EGL_TRUE(eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR,
                                    &bitMapPixelLuminanceOffsetInt));
    EXPECT_EQ(bitMapPixelLuminanceOffsetInt, 0);

    EGLint bitMapPixelSizeInt = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface(mDisplay, pBufferSurface, EGL_BITMAP_PIXEL_SIZE_KHR, &bitMapPixelSizeInt));
    EXPECT_EQ(bitMapPixelSizeInt, 32);

    EXPECT_EGL_TRUE(eglUnlockSurfaceKHR(mDisplay, pBufferSurface));
}

// Create PBufferSurface, glClear Green, Draw red quad, Lock, check buffer for red,
//  Write white pixels, Unlock, Test pixels for white
TEST_P(EGLLockSurface3Test, PbufferSurfaceReadWriteTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    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,
                            8,
                            EGL_RENDERABLE_TYPE,
                            clientVersion,
                            EGL_SURFACE_TYPE,
                            (EGL_PBUFFER_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                            EGL_NONE};
    EGLint count         = 0;
    EGLConfig config     = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, attribs, &config, 1, &count));
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
    EXPECT_GT(count, 0);

    EGLint pBufferAttribs[]   = {EGL_WIDTH, kWidth, EGL_HEIGHT, kHeight, EGL_NONE};
    EGLSurface pBufferSurface = EGL_NO_SURFACE;
    pBufferSurface            = eglCreatePbufferSurface(mDisplay, config, pBufferAttribs);
    EXPECT_NE(pBufferSurface, EGL_NO_SURFACE);

    EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
    EGLContext context  = eglCreateContext(mDisplay, config, nullptr, ctxAttribs);
    EXPECT_NE(context, EGL_NO_CONTEXT);

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, pBufferSurface, pBufferSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    glClearColor(kFloatGreen.R, kFloatGreen.G, kFloatGreen.B, kFloatGreen.A);
    glClear(GL_COLOR_BUFFER_BIT);
    ASSERT_GL_NO_ERROR() << "glClear failed";

    const GLColor drawColor = GLColor::red;
    GLuint texture          = createTexture();
    EXPECT_TRUE(fillTexture(texture, drawColor));
    renderTexture(texture);
    glFinish();
    ASSERT_GL_NO_ERROR() << "glFinish failed";

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_WRITE_SURFACE_BIT_KHR,
                            EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, pBufferSurface, lockAttribs));

    EGLAttribKHR bitMap = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_POINTER_KHR, &bitMap));
    EGLAttribKHR bitMapPitch = 0;
    uint32_t *bitMapPtr      = (uint32_t *)(bitMap);
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));

    EXPECT_TRUE(checkBitMapRGBA32(drawColor, bitMapPtr, bitMapPitch));

    const GLColor fillColor = GLColor::white;
    fillBitMapRGBA32(fillColor, bitMapPtr, bitMapPitch);

    EXPECT_EGL_TRUE(eglUnlockSurfaceKHR(mDisplay, pBufferSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, pBufferSurface, pBufferSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    EXPECT_TRUE(checkSurfaceRGBA32(fillColor));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
}

// Create PBufferSurface, glClear Green, Lock, check buffer for green, Write white pixels,
// Unlock, Test pixels for white.
// This expects that a glClear() alone is sufficient to pre-color the Surface
TEST_P(EGLLockSurface3Test, PbufferSurfaceReadWriteDeferredCleaarTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    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,
                            8,
                            EGL_RENDERABLE_TYPE,
                            clientVersion,
                            EGL_SURFACE_TYPE,
                            (EGL_PBUFFER_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                            EGL_NONE};
    EGLint count         = 0;
    EGLConfig config     = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, attribs, &config, 1, &count));
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
    EXPECT_GT(count, 0);

    EGLint pBufferAttribs[]   = {EGL_WIDTH, kWidth, EGL_HEIGHT, kHeight, EGL_NONE};
    EGLSurface pBufferSurface = EGL_NO_SURFACE;
    pBufferSurface            = eglCreatePbufferSurface(mDisplay, config, pBufferAttribs);
    EXPECT_NE(pBufferSurface, EGL_NO_SURFACE);

    EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
    EGLContext context  = eglCreateContext(mDisplay, config, nullptr, ctxAttribs);
    EXPECT_NE(context, EGL_NO_CONTEXT);

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, pBufferSurface, pBufferSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    const GLColor clearColor = GLColor::green;
    glClearColor(kFloatGreen.R, kFloatGreen.G, kFloatGreen.B, kFloatGreen.A);
    glClear(GL_COLOR_BUFFER_BIT);
    ASSERT_GL_NO_ERROR() << "glClear failed";

    const GLColor drawColor = clearColor;

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_WRITE_SURFACE_BIT_KHR,
                            EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, pBufferSurface, lockAttribs));

    EGLAttribKHR bitMap = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_POINTER_KHR, &bitMap));
    EGLAttribKHR bitMapPitch = 0;
    uint32_t *bitMapPtr      = (uint32_t *)(bitMap);
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, pBufferSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));

    EXPECT_TRUE(checkBitMapRGBA32(drawColor, bitMapPtr, bitMapPitch));

    const GLColor fillColor = GLColor::white;
    fillBitMapRGBA32(fillColor, bitMapPtr, bitMapPitch);

    EXPECT_EGL_TRUE(eglUnlockSurfaceKHR(mDisplay, pBufferSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, pBufferSurface, pBufferSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    EXPECT_TRUE(checkSurfaceRGBA32(fillColor));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
}

// Create WindowSurface, Clear Color to GREEN, draw red quad, Lock with PRESERVE_PIXELS,
// read/check pixels, Unlock.
TEST_P(EGLLockSurface3Test, WindowSurfaceReadTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    EGLint configAttribs[] = {EGL_RED_SIZE,
                              8,
                              EGL_GREEN_SIZE,
                              8,
                              EGL_BLUE_SIZE,
                              8,
                              EGL_ALPHA_SIZE,
                              8,
                              EGL_RENDERABLE_TYPE,
                              (mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT),
                              EGL_SURFACE_TYPE,
                              (EGL_WINDOW_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                              EGL_NONE};
    EGLint count           = 0;
    EGLConfig config       = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, configAttribs, &config, 1, &count));
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
    EXPECT_GT(count, 0);

    OSWindow *osWindow = OSWindow::New();
    osWindow->initialize("LockSurfaceTest", kWidth, kHeight);
    EGLint winAttribs[] = {EGL_NONE};
    EGLSurface windowSurface =
        eglCreateWindowSurface(mDisplay, config, osWindow->getNativeWindow(), winAttribs);
    EXPECT_NE(windowSurface, EGL_NO_SURFACE);

    EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
    EGLContext context  = eglCreateContext(mDisplay, config, nullptr, ctxAttribs);
    EXPECT_NE(context, EGL_NO_CONTEXT);

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    glClearColor(kFloatGreen.R, kFloatGreen.G, kFloatGreen.B, kFloatGreen.A);
    glClear(GL_COLOR_BUFFER_BIT);
    ASSERT_GL_NO_ERROR() << "glClear failed";

    const GLColor drawColor = GLColor::red;
    GLuint texture          = createTexture();
    EXPECT_TRUE(fillTexture(texture, drawColor));
    renderTexture(texture);
    glFinish();
    ASSERT_GL_NO_ERROR() << "glFinish failed";

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR,
                            EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, windowSurface, lockAttribs));

    EGLAttribKHR bitMap = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_POINTER_KHR, &bitMap));
    EGLAttribKHR bitMapPitch = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));

    uint32_t *bitMapPtr = (uint32_t *)(bitMap);
    EXPECT_TRUE(checkBitMapRGBA32(drawColor, bitMapPtr, bitMapPitch));

    EXPECT_TRUE(eglUnlockSurfaceKHR(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
    EXPECT_TRUE(checkSurfaceRGBA32(drawColor));
    EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    glDeleteTextures(1, &texture);

    eglDestroySurface(mDisplay, windowSurface);
    osWindow->destroy();
    OSWindow::Delete(&osWindow);
}

// Test default msaa surface resolve path.
TEST_P(EGLLockSurface3Test, WindowMsaaSurfaceReadTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    EGLint configAttribs[] = {EGL_RED_SIZE,
                              8,
                              EGL_GREEN_SIZE,
                              8,
                              EGL_BLUE_SIZE,
                              8,
                              EGL_ALPHA_SIZE,
                              8,
                              EGL_RENDERABLE_TYPE,
                              (mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT),
                              EGL_SAMPLE_BUFFERS,
                              1,
                              EGL_SAMPLES,
                              4,
                              EGL_SURFACE_TYPE,
                              (EGL_WINDOW_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                              EGL_NONE};
    EGLint count           = 0;
    EGLConfig config       = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, configAttribs, &config, 1, &count));
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
    EXPECT_GT(count, 0);

    OSWindow *osWindow = OSWindow::New();
    osWindow->initialize("LockSurfaceTest", kWidth, kHeight);
    EGLint winAttribs[] = {EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER, EGL_NONE};
    EGLSurface windowSurface =
        eglCreateWindowSurface(mDisplay, config, osWindow->getNativeWindow(), winAttribs);
    EXPECT_NE(windowSurface, EGL_NO_SURFACE);

    EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
    EGLContext context  = eglCreateContext(mDisplay, config, nullptr, ctxAttribs);
    EXPECT_NE(context, EGL_NO_CONTEXT);

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";

    glClearColor(kFloatGreen.R, kFloatGreen.G, kFloatGreen.B, kFloatGreen.A);
    glClear(GL_COLOR_BUFFER_BIT);
    ASSERT_GL_NO_ERROR() << "glClear failed";

    const GLColor drawColor = GLColor::red;
    GLuint texture          = createTexture();
    EXPECT_TRUE(fillTexture(texture, drawColor));
    renderTexture(texture);
    eglSwapBuffers(mDisplay, windowSurface);

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR,
                            EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, windowSurface, lockAttribs));

    EGLAttribKHR bitMap = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_POINTER_KHR, &bitMap));
    EGLAttribKHR bitMapPitch = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));

    uint32_t *bitMapPtr = (uint32_t *)(bitMap);
    EXPECT_TRUE(checkBitMapRGBA32(drawColor, bitMapPtr, bitMapPitch));

    EXPECT_TRUE(eglUnlockSurfaceKHR(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
    EXPECT_TRUE(checkSurfaceRGBA32(drawColor));
    EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    glDeleteTextures(1, &texture);

    eglDestroySurface(mDisplay, windowSurface);
    osWindow->destroy();
    OSWindow::Delete(&osWindow);
}

// Create WindowSurface, Lock surface, Write pixels red, Unlock, check pixels,
//  then swapbuffers to visually check.
TEST_P(EGLLockSurface3Test, WindowSurfaceWritePreserveTest)
{
    ANGLE_SKIP_TEST_IF(!supportsLockSurface3Extension());

    EGLint attribs[] = {EGL_RED_SIZE,
                        8,
                        EGL_GREEN_SIZE,
                        8,
                        EGL_BLUE_SIZE,
                        8,
                        EGL_ALPHA_SIZE,
                        8,
                        EGL_RENDERABLE_TYPE,
                        (mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT),
                        EGL_SURFACE_TYPE,
                        (EGL_WINDOW_BIT | EGL_LOCK_SURFACE_BIT_KHR),
                        EGL_NONE};
    EGLint count     = 0;
    EGLConfig config = EGL_NO_CONFIG_KHR;
    EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, attribs, &config, 1, &count));
    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
    EXPECT_GT(count, 0);

    OSWindow *osWindow = OSWindow::New();
    osWindow->initialize("LockSurfaceTest", kWidth, kHeight);
    EGLint winAttribs[] = {EGL_NONE};
    EGLSurface windowSurface =
        eglCreateWindowSurface(mDisplay, config, osWindow->getNativeWindow(), winAttribs);
    EXPECT_NE(windowSurface, EGL_NO_SURFACE);

    EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
    EGLContext context  = eglCreateContext(mDisplay, config, nullptr, ctxAttribs);
    EXPECT_NE(context, EGL_NO_CONTEXT);

    EGLint lockAttribs[] = {EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR, EGL_NONE};
    EXPECT_EGL_TRUE(eglLockSurfaceKHR(mDisplay, windowSurface, lockAttribs));

    EGLAttribKHR bitMap = 0;
    EXPECT_EGL_TRUE(eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_POINTER_KHR, &bitMap));
    EGLAttribKHR bitMapPitch = 0;
    EXPECT_EGL_TRUE(
        eglQuerySurface64KHR(mDisplay, windowSurface, EGL_BITMAP_PITCH_KHR, &bitMapPitch));
    uint32_t *bitMapPtr = (uint32_t *)(bitMap);

    const GLColor fillColor = GLColor::red;
    fillBitMapRGBA32(fillColor, bitMapPtr, bitMapPitch);

    EXPECT_EGL_TRUE(eglUnlockSurfaceKHR(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
    EXPECT_TRUE(checkSurfaceRGBA32(fillColor));
    EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, windowSurface));

    EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));

    eglDestroySurface(mDisplay, windowSurface);
    osWindow->destroy();
    OSWindow::Delete(&osWindow);
}

ANGLE_INSTANTIATE_TEST(EGLLockSurface3Test,
                       WithNoFixture(ES2_VULKAN()),
                       WithNoFixture(ES3_VULKAN()));
