chromium/third_party/angle/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 <thread>
#include <vector>

#include "common/Color.h"
#include "common/platform.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "util/OSWindow.h"
#include "util/Timer.h"

#if defined(ANGLE_ENABLE_D3D11)
#define INITGUID
#    include <guiddef.h>

#    include <d3d11.h>
#    include <dcomp.h>
#endif

usingnamespaceangle;

namespace
{

class EGLSurfaceTest : public ANGLETest<>
{};

class EGLFloatSurfaceTest : public EGLSurfaceTest
{};

class EGLSingleBufferTest : public ANGLETest<>
{};

class EGLAndroidAutoRefreshTest : public EGLSingleBufferTest
{};

// Test clearing and checking the color is correct
TEST_P(EGLFloatSurfaceTest, Clearing)
{}

// Test drawing and checking the color is correct
TEST_P(EGLFloatSurfaceTest, Drawing)
{}

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)
{}

// Tests the message loop bug, but with setting a second context
// instead of null.
TEST_P(EGLSurfaceTest, MessageLoopBugContext)
{}

// Test a bug where calling makeCurrent twice would release the surface
TEST_P(EGLSurfaceTest, MakeCurrentTwice)
{}

// Test that we dont crash during a clear when specified scissor is outside render area
// due to reducing window size.
TEST_P(EGLSurfaceTest, ShrinkWindowThenScissoredClear)
{}

// Test that we dont early return from a clear when specified scissor is outside render area
// before increasing window size.
TEST_P(EGLSurfaceTest, GrowWindowThenScissoredClear)
{}

// Test that just a ClearBuffer* with an invalid scissor doesn't cause an assert.
TEST_P(EGLSurfaceTest3, ShrinkWindowThenScissoredClearBuffer)
{}

// This is a regression test to verify that surfaces are not prematurely destroyed.
TEST_P(EGLSurfaceTest, SurfaceUseAfterFreeBug)
{}

// Test that the window surface is correctly resized after calling swapBuffers
TEST_P(EGLSurfaceTest, ResizeWindow)
{}

// Test that the backbuffer is correctly resized after calling swapBuffers
TEST_P(EGLSurfaceTest, ResizeWindowWithDraw)
{}

// Test that the window can be reset repeatedly before surface creation.
TEST_P(EGLSurfaceTest, ResetNativeWindow)
{}

// Test swap buffer without any draw calls.
TEST_P(EGLSurfaceTest, SwapWithoutAnyDraw)
{}

// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGB565
TEST_P(EGLSurfaceTest, CreateWithEGLConfig5650Support)
{}

// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGBA4
TEST_P(EGLSurfaceTest, CreateWithEGLConfig4444Support)
{}

// Test creating a surface that supports a EGLConfig with 16bit
// support GL_RGB5_A1
TEST_P(EGLSurfaceTest, CreateWithEGLConfig5551Support)
{}

// Test creating a surface that supports a EGLConfig without alpha support
TEST_P(EGLSurfaceTest, CreateWithEGLConfig8880Support)
{}

// Test creating a surface that supports GL_RGB10_A2 with BT2020 colorspaces
TEST_P(EGLSurfaceTest, CreateWithEGLConfig1010102Support)
{}

TEST_P(EGLSurfaceTest, FixedSizeWindow)
{}

TEST_P(EGLSurfaceTest3, MakeCurrentDifferentSurfaces)
{}

#if defined(ANGLE_ENABLE_D3D11)
class EGLSurfaceTestD3D11 : public EGLSurfaceTest
{
  protected:
    // offset - draw into the texture at offset (|offset|, |offset|)
    // pix25 - the expected pixel value at (25, 25)
    // pix75 - the expected pixel value at (75, 75)
    void testTextureOffset(int offset, UINT pix25, UINT pix75)
    {
        initializeDisplay();

        const EGLint configAttributes[] = {
            EGL_RED_SIZE,   8, EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE,      8, EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};

        EGLConfig config;
        ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));

        mConfig = config;
        initializeMainContext();

        EGLAttrib device       = 0;
        EGLAttrib newEglDevice = 0;
        ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
        ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
                                                EGL_D3D11_DEVICE_ANGLE, &device));
        angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
        ASSERT_TRUE(!!d3d11Device);

        constexpr UINT kTextureWidth  = 100;
        constexpr UINT kTextureHeight = 100;
        constexpr Color<uint8_t> kOpaqueBlack(0, 0, 0, 255);
        std::vector<Color<uint8_t>> textureData(kTextureWidth * kTextureHeight, kOpaqueBlack);

        D3D11_SUBRESOURCE_DATA initialData = {};
        initialData.pSysMem                = textureData.data();
        initialData.SysMemPitch            = kTextureWidth * sizeof(kOpaqueBlack);

        D3D11_TEXTURE2D_DESC desc = {};
        desc.Format               = DXGI_FORMAT_B8G8R8A8_UNORM;
        desc.Width                = kTextureWidth;
        desc.Height               = kTextureHeight;
        desc.ArraySize            = 1;
        desc.MipLevels            = 1;
        desc.SampleDesc.Count     = 1;
        desc.Usage                = D3D11_USAGE_DEFAULT;
        desc.BindFlags            = D3D11_BIND_RENDER_TARGET;
        angle::ComPtr<ID3D11Texture2D> texture;
        HRESULT hr = d3d11Device->CreateTexture2D(&desc, &initialData, &texture);
        ASSERT_TRUE(SUCCEEDED(hr));

        angle::ComPtr<ID3D11DeviceContext> d3d11Context;
        d3d11Device->GetImmediateContext(&d3d11Context);

        // Specify a texture offset of (50, 50) when rendering to the pbuffer surface.
        const EGLint surfaceAttributes[] = {EGL_WIDTH,
                                            kTextureWidth,
                                            EGL_HEIGHT,
                                            kTextureHeight,
                                            EGL_TEXTURE_OFFSET_X_ANGLE,
                                            offset,
                                            EGL_TEXTURE_OFFSET_Y_ANGLE,
                                            offset,
                                            EGL_NONE};
        EGLClientBuffer buffer           = reinterpret_cast<EGLClientBuffer>(texture.Get());
        mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
                                                           config, surfaceAttributes);
        ASSERT_EGL_SUCCESS();

        eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
        ASSERT_EGL_SUCCESS();

        // glClear should only clear subrect at offset (50, 50) without explicit scissor.
        glClearColor(0, 0, 1, 1);  // Blue
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_EQ(25, 25, 0, 0, pix25, 255);
        EXPECT_PIXEL_EQ(75, 75, 0, 0, pix75, 255);
        EXPECT_GL_NO_ERROR();

        // Drawing with a shader should also update the same subrect only without explicit viewport.
        GLuint program = createProgram();  // Red
        ASSERT_NE(0u, program);
        GLint positionLocation =
            glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
        glUseProgram(program);
        const GLfloat vertices[] = {
            -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
            -1.0f, 1.0f, 0.5f, 1.0f,  -1.0f, 0.5f, 1.0f, 1.0f,  0.5f,
        };
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glEnableVertexAttribArray(positionLocation);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        glDisableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);

        EXPECT_PIXEL_EQ(25, 25, pix25, 0, 0, 255);
        EXPECT_PIXEL_EQ(75, 75, pix75, 0, 0, 255);
        EXPECT_GL_NO_ERROR();

        glDeleteProgram(program);
        EXPECT_GL_NO_ERROR();

        // Blit framebuffer should also blit to the same subrect despite the dstX/Y arguments.
        GLRenderbuffer renderBuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 50, 50);
        EXPECT_GL_NO_ERROR();

        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                  renderBuffer);
        EXPECT_GL_NO_ERROR();

        glClearColor(0, 1, 0, 1);  // Green
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_EQ(25, 25, 0, 255, 0, 255);
        EXPECT_GL_NO_ERROR();

        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0u);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
        glBlitFramebuffer(0, 0, 50, 50, 0, 0, kTextureWidth, kTextureWidth, GL_COLOR_BUFFER_BIT,
                          GL_NEAREST);
        EXPECT_GL_NO_ERROR();

        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0u);
        EXPECT_PIXEL_EQ(25, 25, 0, pix25, 0, 255);
        EXPECT_PIXEL_EQ(75, 75, 0, pix75, 0, 255);
        EXPECT_GL_NO_ERROR();
    }

    // Draws into a surface at the specified offset using the values of gl_FragCoord in the
    // fragment shader.
    // texturedimension - dimension of the D3D texture and surface.
    // offset - draw into the texture at offset (|offset|, |offset|)
    void setupFragCoordOffset(int textureDimension, int offset)
    {
        ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
        initializeDisplay();

        EGLAttrib device       = 0;
        EGLAttrib newEglDevice = 0;
        ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
        ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
                                                EGL_D3D11_DEVICE_ANGLE, &device));
        angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
        ASSERT_TRUE(!!d3d11Device);

        D3D11_TEXTURE2D_DESC desc = {};
        desc.Format               = DXGI_FORMAT_B8G8R8A8_UNORM;
        desc.Width                = textureDimension;
        desc.Height               = textureDimension;
        desc.ArraySize            = 1;
        desc.MipLevels            = 1;
        desc.SampleDesc.Count     = 1;
        desc.Usage                = D3D11_USAGE_DEFAULT;
        desc.BindFlags            = D3D11_BIND_RENDER_TARGET;
        angle::ComPtr<ID3D11Texture2D> texture;
        HRESULT hr = d3d11Device->CreateTexture2D(&desc, nullptr, &texture);
        ASSERT_TRUE(SUCCEEDED(hr));

        const EGLint surfaceAttributes[] = {EGL_WIDTH,
                                            textureDimension,
                                            EGL_HEIGHT,
                                            textureDimension,
                                            EGL_TEXTURE_OFFSET_X_ANGLE,
                                            offset,
                                            EGL_TEXTURE_OFFSET_Y_ANGLE,
                                            offset,
                                            EGL_NONE};
        EGLClientBuffer buffer           = reinterpret_cast<EGLClientBuffer>(texture.Get());

        const EGLint configAttributes[] = {
            EGL_RED_SIZE,   8, EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE,      8, EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};

        EGLConfig config;
        ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));
        mConfig = config;

        mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
                                                           config, surfaceAttributes);
        ASSERT_EGL_SUCCESS();

        initializeMainContext();

        eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
        ASSERT_EGL_SUCCESS();

        // Fragment shader that uses the gl_FragCoord values to output the (x, y) position of
        // the current pixel as the color.
        //    - Reverse the offset that was applied to the original coordinates
        //    - 0.5 is subtracted because gl_FragCoord gives the pixel center
        //    - Divided by the size to give a max value of 1
        std::stringstream fs;
        fs << "precision mediump float;" << "void main()" << "{" << "    float dimension = float("
           << textureDimension << ");" << "    float offset = float(" << offset << ");"
           << "    gl_FragColor = vec4((gl_FragCoord.x + offset - 0.5) / dimension,"
           << "                        (gl_FragCoord.y + offset - 0.5) / dimension,"
           << "                         gl_FragCoord.z,"
           << "                         gl_FragCoord.w);" << "}";

        GLuint program = createProgram(fs.str().c_str());
        ASSERT_NE(0u, program);
        glUseProgram(program);

        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);

        const GLfloat vertices[] = {
            -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
            -1.0f, 1.0f, 0.5f, 1.0f,  -1.0f, 0.5f, 1.0f, 1.0f,  0.5f,
        };

        GLint positionLocation =
            glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glEnableVertexAttribArray(positionLocation);

        glDrawArrays(GL_TRIANGLES, 0, 6);

        glDisableVertexAttribArray(positionLocation);

        glDeleteProgram(program);

        EXPECT_GL_NO_ERROR();
    }
};

// Test that rendering to an IDCompositionSurface using a pbuffer works.
TEST_P(EGLSurfaceTestD3D11, CreateDirectCompositionSurface)
{
    ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
    initializeDisplay();

    EGLAttrib device       = 0;
    EGLAttrib newEglDevice = 0;
    ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &newEglDevice));
    ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(newEglDevice),
                                            EGL_D3D11_DEVICE_ANGLE, &device));
    angle::ComPtr<ID3D11Device> d3d11Device(reinterpret_cast<ID3D11Device *>(device));
    ASSERT_TRUE(!!d3d11Device);

    HMODULE dcompLibrary = LoadLibraryA("dcomp.dll");
    if (!dcompLibrary)
    {
        std::cout << "DirectComposition not supported" << std::endl;
        return;
    }
    typedef HRESULT(WINAPI * PFN_DCOMPOSITION_CREATE_DEVICE2)(IUnknown * dxgiDevice, REFIID iid,
                                                              void **dcompositionDevice);
    PFN_DCOMPOSITION_CREATE_DEVICE2 createDComp = reinterpret_cast<PFN_DCOMPOSITION_CREATE_DEVICE2>(
        GetProcAddress(dcompLibrary, "DCompositionCreateDevice2"));
    if (!createDComp)
    {
        std::cout << "DirectComposition2 not supported" << std::endl;
        FreeLibrary(dcompLibrary);
        return;
    }

    angle::ComPtr<IDCompositionDevice> dcompDevice;
    HRESULT hr = createDComp(d3d11Device.Get(), IID_PPV_ARGS(dcompDevice.GetAddressOf()));
    ASSERT_TRUE(SUCCEEDED(hr));

    angle::ComPtr<IDCompositionSurface> dcompSurface;
    hr = dcompDevice->CreateSurface(100, 100, DXGI_FORMAT_B8G8R8A8_UNORM,
                                    DXGI_ALPHA_MODE_PREMULTIPLIED, dcompSurface.GetAddressOf());
    ASSERT_TRUE(SUCCEEDED(hr));

    angle::ComPtr<ID3D11Texture2D> texture;
    POINT updateOffset;
    hr = dcompSurface->BeginDraw(nullptr, IID_PPV_ARGS(texture.GetAddressOf()), &updateOffset);
    ASSERT_TRUE(SUCCEEDED(hr));

    const EGLint configAttributes[] = {
        EGL_RED_SIZE,   8, EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE,      8, EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};

    EGLConfig config;
    ASSERT_EGL_TRUE(EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config));

    const EGLint surfaceAttributes[] = {EGL_WIDTH,
                                        100,
                                        EGL_HEIGHT,
                                        100,
                                        EGL_TEXTURE_OFFSET_X_ANGLE,
                                        updateOffset.x,
                                        EGL_TEXTURE_OFFSET_Y_ANGLE,
                                        updateOffset.y,
                                        EGL_NONE};

    EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture.Get());
    mPbufferSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_ANGLE, buffer,
                                                       config, surfaceAttributes);
    ASSERT_EGL_SUCCESS();

    mConfig = config;
    initializeMainContext();

    eglMakeCurrent(mDisplay, mPbufferSurface, mPbufferSurface, mContext);
    ASSERT_EGL_SUCCESS();

    GLuint program = createProgram();
    ASSERT_NE(0u, program);
    drawWithProgram(program);
    EXPECT_GL_NO_ERROR();
    glDeleteProgram(program);
}

// Tests drawing into a surface created with negative offsets.
TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithTextureNegativeOffset)
{
    ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
    testTextureOffset(-50, 255, 0);
}

// Tests drawing into a surface created with offsets.
TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithTextureOffset)
{
    ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));
    testTextureOffset(50, 0, 255);
}

TEST_P(EGLSurfaceTestD3D11, CreateSurfaceWithMSAA)
{
    ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_d3d"));

    // clang-format off
    const EGLint configAttributes[] =
    {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 0,
        EGL_DEPTH_SIZE, 0,
        EGL_STENCIL_SIZE, 0,
        EGL_SAMPLE_BUFFERS, 1,
        EGL_SAMPLES, 4,
        EGL_NONE
    };
    // clang-format on

    initializeDisplay();
    EGLConfig config;
    if (EGLWindow::FindEGLConfig(mDisplay, configAttributes, &config) == EGL_FALSE)
    {
        std::cout << "EGLConfig for 4xMSAA is not supported, skipping test" << std::endl;
        return;
    }

    initializeSurface(config);
    initializeMainContext();

    eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
    ASSERT_EGL_SUCCESS();

    GLuint program = createProgram();
    ASSERT_NE(0u, program);

    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    GLint positionLocation = glGetAttribLocation(program, angle::essl1_shaders::PositionAttrib());
    ASSERT_NE(-1, positionLocation);

    glUseProgram(program);

    const GLfloat halfPixelOffset = 0.5f * 2.0f / mOSWindow->getWidth();
    // clang-format off
    const GLfloat vertices[] =
    {
        -1.0f + halfPixelOffset,  1.0f, 0.5f,
        -1.0f + halfPixelOffset, -1.0f, 0.5f,
         1.0f,                   -1.0f, 0.5f
    };
    // clang-format on

    glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(positionLocation);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(positionLocation);
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);

    EXPECT_PIXEL_NEAR(0, 0, 127, 0, 0, 255, 10);
    EXPECT_GL_NO_ERROR();

    glDeleteProgram(program);
}

// Tests that gl_FragCoord.xy is offset with the EGL_TEXTURE_OFFSET_[X|Y]_ANGLE values specified
// at surface creation, using positive offsets
TEST_P(EGLSurfaceTestD3D11, FragCoordOffset)
{
    constexpr int kTextureDimension = 28;
    constexpr int kOffset           = 6;

    setupFragCoordOffset(kTextureDimension, kOffset);

    // With a positive offset, nothing is drawn in any pixels to the left of and above |kOffset|.
    for (int x = 0; x < kOffset; x++)
    {
        for (int y = 0; y < kOffset; y++)
        {
            EXPECT_PIXEL_EQ(x, y, 0, 0, 0, 0);
        }
    }

    // The rest of the texture's color should be the value of the (x, y) coordinates.
    for (int x = kOffset; x < kTextureDimension; x++)
    {
        for (int y = kOffset; y < kTextureDimension; y++)
        {
            EXPECT_PIXEL_NEAR(x, y, x * 255.0 / kTextureDimension, y * 255.0 / kTextureDimension,
                              191, 255, 0.5);
        }
    }
}

// Tests that gl_FragCoord.xy is offset with the EGL_TEXTURE_OFFSET_[X|Y]_ANGLE values specified
// at surface creation, using negative offsets.
TEST_P(EGLSurfaceTestD3D11, FragCoordOffsetNegative)
{
    constexpr int kTextureDimension = 28;
    constexpr int kOffset           = 6;

    setupFragCoordOffset(kTextureDimension, -kOffset);

    // With a negative offset, nothing is drawn in pixels to the right of and below |koffset|.
    for (int x = kTextureDimension - kOffset; x < kTextureDimension; x++)
    {
        for (int y = kTextureDimension - kOffset; y < kTextureDimension; y++)
        {
            EXPECT_PIXEL_EQ(x, y, 0, 0, 0, 0);
        }
    }

    // The rest of the texture's color should be the value of the (x, y) coordinates.
    for (int x = 0; x < kTextureDimension - kOffset; x++)
    {
        for (int y = 0; y < kTextureDimension - kOffset; y++)
        {
            EXPECT_PIXEL_NEAR(x, y, x * 255.0 / kTextureDimension, y * 255.0 / kTextureDimension,
                              191, 255, 0.5);
        }
    }
}

#endif  // ANGLE_ENABLE_D3D11

// Verify bliting between two surfaces works correctly.
TEST_P(EGLSurfaceTest3, BlitBetweenSurfaces)
{}

// Verify bliting between two surfaces works correctly.
TEST_P(EGLSurfaceTest3, BlitBetweenSurfacesWithDeferredClear)
{}

// Verify switching between a surface with robust resource init and one without still clears alpha.
TEST_P(EGLSurfaceTest, RobustResourceInitAndEmulatedAlpha)
{}

void EGLSurfaceTest::drawQuadThenTearDown()
{}

// Tests the EGL_ANGLE_create_surface_swap_interval extension if available.
TEST_P(EGLSurfaceTest, CreateSurfaceSwapIntervalANGLE)
{}

// Test that setting a surface's timestamp attribute works when the extension
// EGL_ANGLE_timestamp_surface_attribute is supported.
TEST_P(EGLSurfaceTest, TimestampSurfaceAttribute)
{}

TEST_P(EGLSingleBufferTest, OnCreateWindowSurface)
{}

TEST_P(EGLSingleBufferTest, OnSetSurfaceAttrib)
{}

uint32_t EGLSingleBufferTest::drawAndSwap(EGLSurface &surface,
                                          EGLDisplay &display,
                                          uint32_t color,
                                          bool flush)
{}

// Replicate dEQP-EGL.functional.mutable_render_buffer#basic
TEST_P(EGLSingleBufferTest, MutableRenderBuffer)
{}

// Tests bug with incorrect ImageLayout::SharedPresent barrier.
TEST_P(EGLSingleBufferTest, SharedPresentBarrier)
{}

// Tests scissored clear on single buffer surface
TEST_P(EGLSingleBufferTest, ScissoredClear)
{}

// Tests scissored clear on single buffer surface
TEST_P(EGLSingleBufferTest, ScissoredDraw)
{}

// Tests that "one off" submission is waited before destroying the surface.
TEST_P(EGLSingleBufferTest, WaitOneOffSubmission)
{}

// Test that setting a surface to EGL_SINGLE_BUFFER after enabling
// EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID does not disable auto refresh
TEST_P(EGLAndroidAutoRefreshTest, Basic)
{}

void EGLSurfaceTest::runWaitSemaphoreTest(bool useSecondContext)
{}

// Test that there no artifacts because of the bug when wait semaphore could be added after
// rendering commands. This was possible by switching to Pbuffer surface and submit.
TEST_P(EGLSurfaceTest, DISABLED_WaitSemaphoreAddedAfterCommands)
{}

// Test that there no artifacts because of the bug when rendering commands could be submitted
// without adding wait semaphore. This was possible if submit commands from other thread.
TEST_P(EGLSurfaceTest, DISABLED_CommandsSubmittedWithoutWaitSemaphore)
{}

void EGLSurfaceTest::runDestroyNotCurrentSurfaceTest(bool testWindowsSurface)
{}

// Test that there is no crash because of the bug when not current PBuffer Surface destroyed, while
// there are still unflushed work in the Context.
TEST_P(EGLSurfaceTest, DestroyNotCurrentPbufferSurface)
{}

// Test that there is no crash because of the bug when not current Window Surface destroyed, while
// there are still unflushed work in the Context.
TEST_P(EGLSurfaceTest, DestroyNotCurrentWindowSurface)
{}

// Test that there is no tearing because of incorrect pipeline barriers
TEST_P(EGLSurfaceTest, DISABLED_RandomClearTearing)
{}

// Make sure a surface (from the same window) can be recreated after being destroyed, even if it's
// still current.
// This is to recreate the app behavior in https://issuetracker.google.com/292285899, which is
// not the correct spec behavior. It serves as a purpose to test the workaround feature
// uncurrent_egl_surface_upon_surface_destroy that is enabled only on vulkan backend to help
// the app get over the problem.
TEST_P(EGLSurfaceTest, DestroyAndRecreateWhileCurrent)
{}
}  // anonymous namespace

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST();

ANGLE_INSTANTIATE_TEST();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST();

#if defined(ANGLE_ENABLE_D3D11)
ANGLE_INSTANTIATE_TEST(EGLSurfaceTestD3D11, WithNoFixture(ES2_D3D11()), WithNoFixture(ES3_D3D11()));
#endif