Edit

kc3-lang/angle/src/tests/gl_tests/D3DTextureTest.cpp

Branch :

  • Show log

    Commit

  • Author : Sunny Sachanandani
    Date : 2019-08-28 14:35:32
    Hash : c1af9abf
    Message : Add support for overriding internal format for D3D11 texture EGLImages Add an optional EGL_D3D11_TEXTURE_FORMAT_ANGLE attribute that is the GL internal format to use for EGLImage being created. This will be used for RGB emulation of swap chain texture backed WebGL contexts in Chrome when they have alpha:false set. Without RGB emulation it is possible to observe side-effects of the underlying swap chain being RGBA such as reading/writing the alpha channel, BlitFramebuffer working/not working when expected, etc. Also document creating EGLImages from D3D11 textures in the existing EGL_ANGLE_d3d_texture_client_buffer extension along with RGB emulation. Bug: chromium:699566, chromium:939657 Change-Id: I4931cb7bdc46e9bc6debd56b79ecc10ea27bd78b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1777099 Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>

  • src/tests/gl_tests/D3DTextureTest.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.
    //
    // D3DTextureTest:
    //   Tests of the EGL_ANGLE_d3d_texture_client_buffer extension
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    #include <d3d11.h>
    #include <windows.h>
    
    #include "util/EGLWindow.h"
    #include "util/com_utils.h"
    
    namespace angle
    {
    
    class D3DTextureTest : public ANGLETest
    {
      protected:
        D3DTextureTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
            setConfigStencilBits(8);
        }
    
        void testSetUp() override
        {
            constexpr char kVS[] =
                R"(precision highp float;
                attribute vec4 position;
                varying vec2 texcoord;
    
                void main()
                {
                    gl_Position = position;
                    texcoord = (position.xy * 0.5) + 0.5;
                    texcoord.y = 1.0 - texcoord.y;
                })";
    
            constexpr char kTextureFS[] =
                R"(precision highp float;
                uniform sampler2D tex;
                varying vec2 texcoord;
    
                void main()
                {
                    gl_FragColor = texture2D(tex, texcoord);
                })";
    
            constexpr char kTextureFSNoSampling[] =
                R"(precision highp float;
    
                void main()
                {
                    gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
                })";
    
            mTextureProgram = CompileProgram(kVS, kTextureFS);
            ASSERT_NE(0u, mTextureProgram) << "shader compilation failed.";
    
            mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
            ASSERT_NE(-1, mTextureUniformLocation);
    
            mTextureProgramNoSampling = CompileProgram(kVS, kTextureFSNoSampling);
            ASSERT_NE(0u, mTextureProgramNoSampling) << "shader compilation failed.";
    
            mD3D11Module = LoadLibrary(TEXT("d3d11.dll"));
            ASSERT_NE(nullptr, mD3D11Module);
    
            PFN_D3D11_CREATE_DEVICE createDeviceFunc = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
                GetProcAddress(mD3D11Module, "D3D11CreateDevice"));
    
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
            if (IsEGLDisplayExtensionEnabled(display, "EGL_EXT_device_query"))
            {
                PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
                    reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
                        eglGetProcAddress("eglQueryDisplayAttribEXT"));
                PFNEGLQUERYDEVICEATTRIBEXTPROC eglQueryDeviceAttribEXT =
                    reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
                        eglGetProcAddress("eglQueryDeviceAttribEXT"));
    
                EGLDeviceEXT device = 0;
                {
                    EGLAttrib result = 0;
                    EXPECT_EGL_TRUE(eglQueryDisplayAttribEXT(display, EGL_DEVICE_EXT, &result));
                    device = reinterpret_cast<EGLDeviceEXT>(result);
                }
    
                if (IsEGLDeviceExtensionEnabled(device, "EGL_ANGLE_device_d3d"))
                {
                    EGLAttrib result = 0;
                    if (eglQueryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &result))
                    {
                        mD3D11Device = reinterpret_cast<ID3D11Device *>(result);
                        mD3D11Device->AddRef();
                    }
                    else if (eglQueryDeviceAttribEXT(device, EGL_D3D9_DEVICE_ANGLE, &result))
                    {
                        mD3D9Device = reinterpret_cast<IDirect3DDevice9 *>(result);
                        mD3D9Device->AddRef();
                    }
                }
            }
            else
            {
                ASSERT_TRUE(
                    SUCCEEDED(createDeviceFunc(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr,
                                               0, D3D11_SDK_VERSION, &mD3D11Device, nullptr, nullptr)));
            }
        }
    
        void testTearDown() override
        {
            glDeleteProgram(mTextureProgram);
    
            if (mD3D11Device)
            {
                mD3D11Device->Release();
                mD3D11Device = nullptr;
            }
    
            FreeLibrary(mD3D11Module);
            mD3D11Module = nullptr;
    
            if (mD3D9Device)
            {
                mD3D9Device->Release();
                mD3D9Device = nullptr;
            }
        }
    
        EGLSurface createD3D11PBuffer(size_t width,
                                      size_t height,
                                      UINT sampleCount,
                                      UINT sampleQuality,
                                      UINT bindFlags,
                                      DXGI_FORMAT format,
                                      const EGLint *attribs)
        {
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
            EGLConfig config   = window->getConfig();
    
            EXPECT_TRUE(mD3D11Device != nullptr);
            ID3D11Texture2D *texture = nullptr;
            CD3D11_TEXTURE2D_DESC desc(format, static_cast<UINT>(width), static_cast<UINT>(height), 1,
                                       1, bindFlags);
            desc.SampleDesc.Count   = sampleCount;
            desc.SampleDesc.Quality = sampleQuality;
            EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &texture)));
    
            EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
                                                                  texture, config, attribs);
    
            texture->Release();
    
            return pbuffer;
        }
    
        EGLSurface createD3D11PBuffer(size_t width,
                                      size_t height,
                                      EGLint eglTextureFormat,
                                      EGLint eglTextureTarget,
                                      UINT sampleCount,
                                      UINT sampleQuality,
                                      UINT bindFlags,
                                      DXGI_FORMAT format)
        {
            EGLint attribs[] = {
                EGL_TEXTURE_FORMAT, eglTextureFormat, EGL_TEXTURE_TARGET,
                eglTextureTarget,   EGL_NONE,         EGL_NONE,
            };
            return createD3D11PBuffer(width, height, sampleCount, sampleQuality, bindFlags, format,
                                      attribs);
        }
    
        EGLSurface createPBuffer(size_t width,
                                 size_t height,
                                 EGLint eglTextureFormat,
                                 EGLint eglTextureTarget,
                                 UINT sampleCount,
                                 UINT sampleQuality)
        {
            if (mD3D11Device)
            {
                return createD3D11PBuffer(
                    width, height, eglTextureFormat, eglTextureTarget, sampleCount, sampleQuality,
                    D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, DXGI_FORMAT_R8G8B8A8_UNORM);
            }
    
            if (mD3D9Device)
            {
                EGLWindow *window  = getEGLWindow();
                EGLDisplay display = window->getDisplay();
                EGLConfig config   = window->getConfig();
    
                EGLint attribs[] = {
                    EGL_TEXTURE_FORMAT, eglTextureFormat, EGL_TEXTURE_TARGET,
                    eglTextureTarget,   EGL_NONE,         EGL_NONE,
                };
    
                // Multisampled textures are not supported on D3D9.
                EXPECT_TRUE(sampleCount <= 1);
                EXPECT_TRUE(sampleQuality == 0);
    
                IDirect3DTexture9 *texture = nullptr;
                EXPECT_TRUE(SUCCEEDED(mD3D9Device->CreateTexture(
                    static_cast<UINT>(width), static_cast<UINT>(height), 1, D3DUSAGE_RENDERTARGET,
                    D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, nullptr)));
    
                EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
                                                                      texture, config, attribs);
    
                texture->Release();
    
                return pbuffer;
            }
            else
            {
                return EGL_NO_SURFACE;
            }
        }
    
        bool valid() const
        {
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
            if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_d3d_texture_client_buffer"))
            {
                std::cout << "Test skipped due to missing EGL_ANGLE_d3d_texture_client_buffer"
                          << std::endl;
                return false;
            }
    
            if (!mD3D11Device && !mD3D9Device)
            {
                std::cout << "Test skipped due to no D3D devices being available." << std::endl;
                return false;
            }
    
            if (IsWindows() && IsAMD() && IsOpenGL())
            {
                std::cout << "Test skipped on Windows AMD OpenGL." << std::endl;
                return false;
            }
    
            if (IsWindows() && IsIntel() && IsOpenGL())
            {
                std::cout << "Test skipped on Windows Intel OpenGL." << std::endl;
                return false;
            }
            return true;
        }
    
        void testTextureSamplesAs50PercentGreen(GLuint texture)
        {
            GLFramebuffer scratchFbo;
            glBindFramebuffer(GL_FRAMEBUFFER, scratchFbo);
            GLTexture scratchTexture;
            glBindTexture(GL_TEXTURE_2D, scratchTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, scratchTexture,
                                   0);
    
            glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
    
            glUseProgram(mTextureProgram);
            glUniform1i(mTextureUniformLocation, 0);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
            drawQuad(mTextureProgram, "position", 0.5f);
            ASSERT_GL_NO_ERROR();
    
            EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 127u, 0u, 255u), 2);
        }
    
        GLuint mTextureProgram;
        GLuint mTextureProgramNoSampling;
        GLint mTextureUniformLocation;
    
        HMODULE mD3D11Module       = nullptr;
        ID3D11Device *mD3D11Device = nullptr;
    
        IDirect3DDevice9 *mD3D9Device = nullptr;
    };
    
    // Test creating pbuffer from textures with several different DXGI formats.
    TEST_P(D3DTextureTest, TestD3D11SupportedFormatsSurface)
    {
        bool srgbSupported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
        ANGLE_SKIP_TEST_IF(!valid() || !mD3D11Device || !srgbSupported);
    
        const DXGI_FORMAT formats[] = {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
                                       DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB};
        for (size_t i = 0; i < 4; ++i)
        {
            if (formats[i] == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
            {
                if (IsOpenGL())
                {
                    // This generates an invalid format error when calling wglDXRegisterObjectNV().
                    // Reproducible at least on NVIDIA driver 390.65 on Windows 10.
                    std::cout << "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB subtest skipped: IsOpenGL().\n";
                    continue;
                }
            }
    
            EGLSurface pbuffer = createD3D11PBuffer(32, 32, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D, 1, 0,
                                                    D3D11_BIND_RENDER_TARGET, formats[i]);
            ASSERT_EGL_SUCCESS();
            ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
    
            EGLint colorspace = EGL_NONE;
            eglQuerySurface(display, pbuffer, EGL_GL_COLORSPACE, &colorspace);
    
            if (formats[i] == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB ||
                formats[i] == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
            {
                EXPECT_EQ(EGL_GL_COLORSPACE_SRGB, colorspace);
            }
            else
            {
                EXPECT_EQ(EGL_GL_COLORSPACE_LINEAR, colorspace);
            }
    
            eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
            ASSERT_EGL_SUCCESS();
            window->makeCurrent();
            eglDestroySurface(display, pbuffer);
        }
    }
    
    // Test binding a pbuffer created from a D3D texture as a texture image with several different DXGI
    // formats. The test renders to and samples from the pbuffer.
    TEST_P(D3DTextureTest, TestD3D11SupportedFormatsTexture)
    {
        bool srgb8alpha8TextureAttachmentSupported = getClientMajorVersion() >= 3;
        ANGLE_SKIP_TEST_IF(!valid() || !mD3D11Device || !srgb8alpha8TextureAttachmentSupported);
    
        bool srgbWriteControlSupported =
            IsGLExtensionEnabled("GL_EXT_sRGB_write_control") && !IsOpenGL();
    
        const DXGI_FORMAT formats[] = {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM,
                                       DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
                                       DXGI_FORMAT_B8G8R8A8_UNORM_SRGB};
        for (size_t i = 0; i < 4; ++i)
        {
            if (formats[i] == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
            {
                if (IsOpenGL())
                {
                    // This generates an invalid format error when calling wglDXRegisterObjectNV().
                    // Reproducible at least on NVIDIA driver 390.65 on Windows 10.
                    std::cout << "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB subtest skipped: IsOpenGL().\n";
                    continue;
                }
            }
    
            SCOPED_TRACE(std::string("Test case:") + std::to_string(i));
            EGLWindow *window  = getEGLWindow();
            EGLDisplay display = window->getDisplay();
    
            EGLSurface pbuffer =
                createD3D11PBuffer(32, 32, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D, 1, 0,
                                   D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, formats[i]);
            ASSERT_EGL_SUCCESS();
            ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
            EGLint colorspace = EGL_NONE;
            eglQuerySurface(display, pbuffer, EGL_GL_COLORSPACE, &colorspace);
    
            GLuint texture = 0u;
            glGenTextures(1, &texture);
            glBindTexture(GL_TEXTURE_2D, texture);
            EGLBoolean result = eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER);
            ASSERT_EGL_SUCCESS();
            ASSERT_EGL_TRUE(result);
    
            GLuint fbo = 0u;
            glGenFramebuffers(1, &fbo);
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
            glViewport(0, 0, 32, 32);
    
            GLint colorEncoding = 0;
            glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                  GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
                                                  &colorEncoding);
    
            if (formats[i] == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB ||
                formats[i] == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
            {
                EXPECT_EQ(EGL_GL_COLORSPACE_SRGB, colorspace);
                EXPECT_EQ(GL_SRGB_EXT, colorEncoding);
            }
            else
            {
                EXPECT_EQ(EGL_GL_COLORSPACE_LINEAR, colorspace);
                EXPECT_EQ(GL_LINEAR, colorEncoding);
            }
    
            // Clear the texture with 50% green and check that the color value written is correct.
            glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
    
            if (colorEncoding == GL_SRGB_EXT)
            {
                glClear(GL_COLOR_BUFFER_BIT);
                EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 188u, 0u, 255u), 2);
                // Disable SRGB and run the non-sRGB test case.
                if (srgbWriteControlSupported)
                    glDisable(GL_FRAMEBUFFER_SRGB_EXT);
            }
    
            if (colorEncoding == GL_LINEAR || srgbWriteControlSupported)
            {
                glClear(GL_COLOR_BUFFER_BIT);
                EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 127u, 0u, 255u), 2);
            }
    
            // Draw with the texture to a linear framebuffer and check that the color value written is
            // correct.
            testTextureSamplesAs50PercentGreen(texture);
    
            glBindFramebuffer(GL_FRAMEBUFFER, 0u);
            glBindTexture(GL_TEXTURE_2D, 0u);
            glDeleteTextures(1, &texture);
            glDeleteFramebuffers(1, &fbo);
            eglDestroySurface(display, pbuffer);
        }
    }
    
    // Test binding a pbuffer created from a D3D texture as a texture image with typeless texture
    // formats.
    TEST_P(D3DTextureTest, TestD3D11TypelessTexture)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        ANGLE_SKIP_TEST_IF(!valid());
    
        // Typeless formats are optional in the spec and currently only supported on D3D11 backend.
        ANGLE_SKIP_TEST_IF(!IsD3D11());
    
        // GL_SRGB8_ALPHA8 texture attachment support is required.
        ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
    
        const std::array<EGLint, 2> eglGlColorspaces = {EGL_GL_COLORSPACE_LINEAR,
                                                        EGL_GL_COLORSPACE_SRGB};
        const std::array<DXGI_FORMAT, 2> dxgiFormats = {DXGI_FORMAT_R8G8B8A8_TYPELESS,
                                                        DXGI_FORMAT_B8G8R8A8_TYPELESS};
        for (auto eglGlColorspace : eglGlColorspaces)
        {
            for (auto dxgiFormat : dxgiFormats)
            {
                SCOPED_TRACE(std::string("Test case:") + std::to_string(eglGlColorspace) + " / " +
                             std::to_string(dxgiFormat));
    
                EGLint attribs[] = {
                    EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
                    EGL_GL_COLORSPACE,  eglGlColorspace,  EGL_NONE,           EGL_NONE,
                };
    
                EGLSurface pbuffer = createD3D11PBuffer(
                    32, 32, 1, 0, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, dxgiFormat,
                    attribs);
    
                ASSERT_EGL_SUCCESS();
                ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
                EGLint colorspace = EGL_NONE;
                eglQuerySurface(display, pbuffer, EGL_GL_COLORSPACE, &colorspace);
    
                GLuint texture = 0u;
                glGenTextures(1, &texture);
                glBindTexture(GL_TEXTURE_2D, texture);
                EGLBoolean result = eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER);
                ASSERT_EGL_SUCCESS();
                ASSERT_EGL_TRUE(result);
    
                GLuint fbo = 0u;
                glGenFramebuffers(1, &fbo);
                glBindFramebuffer(GL_FRAMEBUFFER, fbo);
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
                glViewport(0, 0, 32, 32);
    
                GLint colorEncoding = 0;
                glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                      GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
                                                      &colorEncoding);
    
                if (eglGlColorspace == EGL_GL_COLORSPACE_LINEAR)
                {
                    EXPECT_EQ(EGL_GL_COLORSPACE_LINEAR, colorspace);
                    EXPECT_EQ(GL_LINEAR, colorEncoding);
                }
                else
                {
                    EXPECT_EQ(EGL_GL_COLORSPACE_SRGB, colorspace);
                    EXPECT_EQ(GL_SRGB_EXT, colorEncoding);
                }
    
                // Clear the texture with 50% green and check that the color value written is correct.
                glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
    
                if (colorEncoding == GL_SRGB_EXT)
                {
                    glClear(GL_COLOR_BUFFER_BIT);
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 188u, 0u, 255u), 2);
                }
                if (colorEncoding == GL_LINEAR)
                {
                    glClear(GL_COLOR_BUFFER_BIT);
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 127u, 0u, 255u), 2);
                }
    
                // Draw with the texture to a linear framebuffer and check that the color value written
                // is correct.
                testTextureSamplesAs50PercentGreen(texture);
    
                glBindFramebuffer(GL_FRAMEBUFFER, 0u);
                glBindTexture(GL_TEXTURE_2D, 0u);
                glDeleteTextures(1, &texture);
                glDeleteFramebuffers(1, &fbo);
                eglDestroySurface(display, pbuffer);
            }
        }
    }
    
    class D3DTextureTestES3 : public D3DTextureTest
    {
      protected:
        D3DTextureTestES3() : D3DTextureTest() {}
    };
    
    // Test swizzling a pbuffer created from a D3D texture as a texture image with typeless texture
    // formats.
    TEST_P(D3DTextureTestES3, TestD3D11TypelessTextureSwizzle)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        ANGLE_SKIP_TEST_IF(!valid());
    
        // Typeless formats are optional in the spec and currently only supported on D3D11 backend.
        ANGLE_SKIP_TEST_IF(!IsD3D11());
    
        const std::array<EGLint, 2> eglGlColorspaces = {EGL_GL_COLORSPACE_LINEAR,
                                                        EGL_GL_COLORSPACE_SRGB};
        const std::array<DXGI_FORMAT, 2> dxgiFormats = {DXGI_FORMAT_R8G8B8A8_TYPELESS,
                                                        DXGI_FORMAT_B8G8R8A8_TYPELESS};
        for (auto eglGlColorspace : eglGlColorspaces)
        {
            for (auto dxgiFormat : dxgiFormats)
            {
                SCOPED_TRACE(std::string("Test case:") + std::to_string(eglGlColorspace) + " / " +
                             std::to_string(dxgiFormat));
    
                EGLint attribs[] = {
                    EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
                    EGL_GL_COLORSPACE,  eglGlColorspace,  EGL_NONE,           EGL_NONE,
                };
    
                EGLSurface pbuffer = createD3D11PBuffer(
                    32, 32, 1, 0, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, dxgiFormat,
                    attribs);
    
                ASSERT_EGL_SUCCESS();
                ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
                EGLint colorspace = EGL_NONE;
                eglQuerySurface(display, pbuffer, EGL_GL_COLORSPACE, &colorspace);
    
                GLuint texture = 0u;
                glGenTextures(1, &texture);
                glBindTexture(GL_TEXTURE_2D, texture);
                EGLBoolean result = eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER);
                ASSERT_EGL_SUCCESS();
                ASSERT_EGL_TRUE(result);
    
                GLuint fbo = 0u;
                glGenFramebuffers(1, &fbo);
                glBindFramebuffer(GL_FRAMEBUFFER, fbo);
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
                glViewport(0, 0, 32, 32);
    
                GLint colorEncoding = 0;
                glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                      GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
                                                      &colorEncoding);
    
                // Clear the texture with 50% blue and check that the color value written is correct.
                glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
    
                if (colorEncoding == GL_SRGB_EXT)
                {
                    glClear(GL_COLOR_BUFFER_BIT);
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 0u, 188u, 255u), 2);
                }
                if (colorEncoding == GL_LINEAR)
                {
                    glClear(GL_COLOR_BUFFER_BIT);
                    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(0u, 0u, 127u, 255u), 2);
                }
    
                // Swizzle the green channel to be sampled from the blue channel of the texture and vice
                // versa.
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_GREEN);
                ASSERT_GL_NO_ERROR();
    
                // Draw with the texture to a linear framebuffer and check that the color value written
                // is correct.
                testTextureSamplesAs50PercentGreen(texture);
    
                glBindFramebuffer(GL_FRAMEBUFFER, 0u);
                glBindTexture(GL_TEXTURE_2D, 0u);
                glDeleteTextures(1, &texture);
                glDeleteFramebuffers(1, &fbo);
                eglDestroySurface(display, pbuffer);
            }
        }
    }
    
    // Test that EGL_GL_COLORSPACE attrib is not allowed for typed D3D textures.
    TEST_P(D3DTextureTest, GlColorspaceNotAllowedForTypedD3DTexture)
    {
        ANGLE_SKIP_TEST_IF(!valid());
    
        // D3D11 device is required to be able to create the texture.
        ANGLE_SKIP_TEST_IF(!mD3D11Device);
    
        // SRGB support is required.
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3);
    
        EGLint attribsExplicitColorspace[] = {
            EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,       EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
            EGL_GL_COLORSPACE,  EGL_GL_COLORSPACE_SRGB, EGL_NONE,           EGL_NONE,
        };
        EGLSurface pbuffer =
            createD3D11PBuffer(32, 32, 1, 0, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE,
                               DXGI_FORMAT_R8G8B8A8_UNORM, attribsExplicitColorspace);
    
        ASSERT_EGL_ERROR(EGL_BAD_MATCH);
        ASSERT_EQ(EGL_NO_SURFACE, pbuffer);
    }
    
    // Test that trying to create a pbuffer from a typeless texture fails as expected on the backends
    // where they are known not to be supported.
    TEST_P(D3DTextureTest, TypelessD3DTextureNotSupported)
    {
        ANGLE_SKIP_TEST_IF(!valid());
    
        // D3D11 device is required to be able to create the texture.
        ANGLE_SKIP_TEST_IF(!mD3D11Device);
    
        // Currently typeless textures are supported on the D3D11 backend. We're testing the backends
        // where there is no support.
        ANGLE_SKIP_TEST_IF(IsD3D11());
    
        // SRGB support is required.
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3);
    
        EGLint attribs[] = {
            EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_TEXTURE_TARGET,
            EGL_TEXTURE_2D,     EGL_NONE,         EGL_NONE,
        };
        EGLSurface pbuffer =
            createD3D11PBuffer(32, 32, 1, 0, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE,
                               DXGI_FORMAT_R8G8B8A8_TYPELESS, attribs);
        ASSERT_EGL_ERROR(EGL_BAD_PARAMETER);
        ASSERT_EQ(EGL_NO_SURFACE, pbuffer);
    }
    
    // Test creating a pbuffer with unnecessary EGL_WIDTH and EGL_HEIGHT attributes because that's what
    // Chromium does. This is a regression test for crbug.com/794086
    TEST_P(D3DTextureTest, UnnecessaryWidthHeightAttributes)
    {
        ANGLE_SKIP_TEST_IF(!valid() || !IsD3D11());
        ASSERT_TRUE(mD3D11Device != nullptr);
        ID3D11Texture2D *texture = nullptr;
        CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 1, 1, D3D11_BIND_RENDER_TARGET);
        desc.SampleDesc.Count   = 1;
        desc.SampleDesc.Quality = 0;
        EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &texture)));
    
        EGLint attribs[] = {
            EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
            EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
            EGL_WIDTH,          1,
            EGL_HEIGHT,         1,
            EGL_NONE,           EGL_NONE,
        };
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
        EGLConfig config   = window->getConfig();
    
        EGLSurface pbuffer =
            eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE, texture, config, attribs);
    
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        texture->Release();
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test creating a pbuffer from a d3d surface and clearing it
    TEST_P(D3DTextureTest, Clear)
    {
        if (!valid())
        {
            return;
        }
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        const size_t bufferSize = 32;
    
        EGLSurface pbuffer =
            createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 1, 0);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        // Apply the Pbuffer and clear it to purple and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
                        255, 255);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test creating a pbuffer with a D3D texture and depth stencil bits in the EGL config creates keeps
    // its depth stencil buffer
    TEST_P(D3DTextureTest, DepthStencil)
    {
        if (!valid())
        {
            return;
        }
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        const size_t bufferSize = 32;
    
        EGLSurface pbuffer =
            createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 1, 0);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        // Apply the Pbuffer and clear it to purple and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
        glClearDepthf(0.5f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_FALSE);
    
        glUseProgram(mTextureProgram);
        glUniform1i(mTextureUniformLocation, 0);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        // Draw a quad that will fail the depth test and verify that the buffer is unchanged
        drawQuad(mTextureProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::cyan);
    
        // Draw a quad that will pass the depth test and verify that the buffer is green
        drawQuad(mTextureProgram, "position", -1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::green);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test creating a pbuffer from a d3d surface and binding it to a texture
    TEST_P(D3DTextureTest, BindTexImage)
    {
        if (!valid())
        {
            return;
        }
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        const size_t bufferSize = 32;
    
        EGLSurface pbuffer =
            createPBuffer(bufferSize, bufferSize, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D, 1, 0);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        // Apply the Pbuffer and clear it to purple
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
                        255, 255);
    
        // Apply the window surface
        eglMakeCurrent(display, window->getSurface(), window->getSurface(), window->getContext());
    
        // Create a texture and bind the Pbuffer to it
        GLuint texture = 0;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        EXPECT_GL_NO_ERROR();
    
        eglBindTexImage(display, pbuffer, EGL_BACK_BUFFER);
        glViewport(0, 0, getWindowWidth(), getWindowHeight());
        ASSERT_EGL_SUCCESS();
    
        // Draw a quad and verify that it is purple
        glUseProgram(mTextureProgram);
        glUniform1i(mTextureUniformLocation, 0);
    
        drawQuad(mTextureProgram, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
    
        // Unbind the texture
        eglReleaseTexImage(display, pbuffer, EGL_BACK_BUFFER);
        ASSERT_EGL_SUCCESS();
    
        // Verify that purple was drawn
        EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 255, 255);
    
        glDeleteTextures(1, &texture);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Verify that creating a pbuffer with a multisampled texture will fail on a non-multisampled
    // window.
    TEST_P(D3DTextureTest, CheckSampleMismatch)
    {
        if (!valid())
        {
            return;
        }
    
        // Multisampling is not supported on D3D9 or OpenGL.
        ANGLE_SKIP_TEST_IF(IsD3D9() || IsOpenGL());
    
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 2,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
        EXPECT_EQ(pbuffer, nullptr);
    }
    
    // Tests what happens when we make a PBuffer that isn't shader-readable.
    TEST_P(D3DTextureTest, NonReadablePBuffer)
    {
        ANGLE_SKIP_TEST_IF(!valid() || !IsD3D11());
    
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer =
            createD3D11PBuffer(bufferSize, bufferSize, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D, 1, 0,
                               D3D11_BIND_RENDER_TARGET, DXGI_FORMAT_R8G8B8A8_UNORM);
    
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
    
        // Clear to green.
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Copy the green color to a texture.
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, bufferSize, bufferSize, 0);
        ASSERT_GL_NO_ERROR();
    
        // Clear to red.
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Draw with the texture and expect green.
        draw2DTexturedQuad(0.5f, 1.0f, false);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    class D3DTextureTestMS : public D3DTextureTest
    {
      protected:
        D3DTextureTestMS() : D3DTextureTest()
        {
            setSamples(4);
            setMultisampleEnabled(true);
        }
    };
    
    // Test creating a pbuffer from a multisampled d3d surface and clearing it.
    TEST_P(D3DTextureTestMS, Clear)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        constexpr size_t bufferSize = 32;
        constexpr UINT testpoint    = bufferSize / 2;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        // Apply the Pbuffer and clear it to magenta and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_COLOR_EQ(testpoint, testpoint, GLColor::magenta);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test creating a pbuffer from a multisampled d3d surface and drawing with a program.
    TEST_P(D3DTextureTestMS, DrawProgram)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(pbuffer, EGL_NO_SURFACE);
    
        // Apply the Pbuffer and clear it to magenta
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        constexpr GLint testPoint = bufferSize / 2;
        EXPECT_PIXEL_COLOR_EQ(testPoint, testPoint, GLColor::magenta);
    
        // Apply the window surface
        eglMakeCurrent(display, window->getSurface(), window->getSurface(), window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, getWindowWidth(), getWindowHeight());
        ASSERT_EGL_SUCCESS();
    
        // Draw a quad and verify that it is magenta
        glUseProgram(mTextureProgramNoSampling);
        EXPECT_GL_NO_ERROR();
    
        drawQuad(mTextureProgramNoSampling, "position", 0.5f);
        EXPECT_GL_NO_ERROR();
    
        // Verify that magenta was drawn
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::magenta);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test for failure when creating a pbuffer from a multisampled d3d surface to bind to a texture.
    TEST_P(D3DTextureTestMS, BindTexture)
    {
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
    
        EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
        EXPECT_EQ(pbuffer, nullptr);
    }
    
    // Verify that creating a pbuffer from a multisampled texture with a multisampled window will fail
    // when the sample counts do not match.
    TEST_P(D3DTextureTestMS, CheckSampleMismatch)
    {
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 2,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
    
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
        EXPECT_EQ(pbuffer, nullptr);
    }
    
    // Test creating a pbuffer with a D3D texture and depth stencil bits in the EGL config creates keeps
    // its depth stencil buffer
    TEST_P(D3DTextureTestMS, DepthStencil)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        const size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
        // Apply the Pbuffer and clear it to purple and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
        glClearDepthf(0.5f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_FALSE);
    
        glUseProgram(mTextureProgram);
        glUniform1i(mTextureUniformLocation, 0);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        // Draw a quad that will fail the depth test and verify that the buffer is unchanged
        drawQuad(mTextureProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::cyan);
    
        // Draw a quad that will pass the depth test and verify that the buffer is green
        drawQuad(mTextureProgram, "position", -1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::green);
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test copyTexImage2D with a multisampled resource
    TEST_P(D3DTextureTestMS, CopyTexImage2DTest)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
        // Apply the Pbuffer and clear it to magenta and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        glUseProgram(mTextureProgram);
        glUniform1i(mTextureUniformLocation, 0);
    
        // Specify a 2D texture and set it to green
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        // Copy from the multisampled framebuffer to the 2D texture
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
    
        // Draw a quad and verify the color is magenta, not green
        drawQuad(mTextureProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::magenta);
        ASSERT_GL_NO_ERROR();
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    // Test copyTexSubImage2D with a multisampled resource
    TEST_P(D3DTextureTestMS, CopyTexSubImage2DTest)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        constexpr size_t bufferSize = 32;
    
        EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE, 4,
                                           static_cast<UINT>(D3D11_STANDARD_MULTISAMPLE_PATTERN));
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(EGL_NO_SURFACE, pbuffer);
    
        // Apply the Pbuffer and clear it to magenta and verify
        eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
        ASSERT_EGL_SUCCESS();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        glUseProgram(mTextureProgram);
        glUniform1i(mTextureUniformLocation, 0);
    
        // Specify a 2D texture and set it to green
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    
        // Copy from the multisampled framebuffer to the 2D texture
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
    
        // Draw a quad and verify the color is magenta, not green
        drawQuad(mTextureProgram, "position", 1.0f);
        EXPECT_PIXEL_COLOR_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2,
                              GLColor::magenta);
        ASSERT_GL_NO_ERROR();
    
        // Make current with fixture EGL to ensure the Surface can be released immediately.
        getEGLWindow()->makeCurrent();
        eglDestroySurface(display, pbuffer);
    }
    
    TEST_P(D3DTextureTest, ClearTextureImage)
    {
        ANGLE_SKIP_TEST_IF(!valid() || !IsD3D11());
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        window->makeCurrent();
    
        const UINT bufferSize = 32;
        EXPECT_TRUE(mD3D11Device != nullptr);
        ID3D11Texture2D *d3d11_texture = nullptr;
        CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, bufferSize, bufferSize, 1, 1,
                                   D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
        EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &d3d11_texture)));
    
        const EGLint attribs[] = {EGL_NONE};
    
        EGLImage image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_D3D11_TEXTURE_ANGLE,
                                           static_cast<EGLClientBuffer>(d3d11_texture), attribs);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(image, EGL_NO_IMAGE_KHR);
    
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        ASSERT_GL_NO_ERROR();
    
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
        ASSERT_GL_NO_ERROR();
    
        GLuint fbo;
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
                  static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
                        255, 255);
    
        glDeleteFramebuffers(1, &fbo);
        glDeleteTextures(1, &texture);
    
        d3d11_texture->Release();
    }
    
    TEST_P(D3DTextureTest, NonRenderableTextureImage)
    {
        ANGLE_SKIP_TEST_IF(!valid() || !IsD3D11());
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        window->makeCurrent();
    
        const UINT bufferSize = 32;
        EXPECT_TRUE(mD3D11Device != nullptr);
        ID3D11Texture2D *d3d11_texture = nullptr;
        CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, bufferSize, bufferSize, 1, 1,
                                   D3D11_BIND_SHADER_RESOURCE);
        EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &d3d11_texture)));
    
        const EGLint attribs[] = {EGL_NONE};
    
        EGLImage image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_D3D11_TEXTURE_ANGLE,
                                           static_cast<EGLClientBuffer>(d3d11_texture), attribs);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(image, EGL_NO_IMAGE_KHR);
    
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        ASSERT_GL_NO_ERROR();
    
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
        ASSERT_GL_NO_ERROR();
    
        GLuint fbo;
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
                  static_cast<unsigned>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
        ASSERT_GL_NO_ERROR();
    
        glDeleteFramebuffers(1, &fbo);
        glDeleteTextures(1, &texture);
    
        d3d11_texture->Release();
    }
    
    TEST_P(D3DTextureTest, RGBEmulationTextureImage)
    {
        ANGLE_SKIP_TEST_IF(!valid() || !IsD3D11());
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
    
        window->makeCurrent();
    
        const UINT bufferSize = 32;
        EXPECT_TRUE(mD3D11Device != nullptr);
        ID3D11Texture2D *d3d11_texture = nullptr;
        CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, bufferSize, bufferSize, 1, 1,
                                   D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
        EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &d3d11_texture)));
    
        const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGB, EGL_NONE};
    
        EGLImage image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_D3D11_TEXTURE_ANGLE,
                                           static_cast<EGLClientBuffer>(d3d11_texture), attribs);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(image, EGL_NO_IMAGE_KHR);
    
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        ASSERT_GL_NO_ERROR();
    
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
        ASSERT_GL_NO_ERROR();
    
        GLuint fbo;
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
                  static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
        ASSERT_GL_NO_ERROR();
    
        // Although we are writing 0.5 to the alpha channel it should have the same
        // side effects as if alpha were 1.0.
        glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
        glClearColor(1.0f, 0.0f, 1.0f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
                        255, 255);
    
        GLuint rgbaRbo;
        glGenRenderbuffers(1, &rgbaRbo);
        glBindRenderbuffer(GL_RENDERBUFFER, rgbaRbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, bufferSize, bufferSize);
    
        GLuint rgbaFbo;
        glGenFramebuffers(1, &rgbaFbo);
        glBindFramebuffer(GL_FRAMEBUFFER, rgbaFbo);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rgbaRbo);
        EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
                  static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
        ASSERT_GL_NO_ERROR();
    
        // BlitFramebuffer from/to RGBA framebuffer fails.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, rgbaFbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glBlitFramebufferANGLE(0, 0, bufferSize, bufferSize, 0, 0, bufferSize, bufferSize,
                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_ERROR(GL_INVALID_OPERATION);
        ASSERT_GL_NO_ERROR();
    
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rgbaFbo);
        glBlitFramebufferANGLE(0, 0, bufferSize, bufferSize, 0, 0, bufferSize, bufferSize,
                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_ERROR(GL_INVALID_OPERATION);
        ASSERT_GL_NO_ERROR();
    
        GLuint rgbRbo;
        glGenRenderbuffers(1, &rgbRbo);
        glBindRenderbuffer(GL_RENDERBUFFER, rgbRbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8_OES, bufferSize, bufferSize);
    
        GLuint rgbFbo;
        glGenFramebuffers(1, &rgbFbo);
        glBindFramebuffer(GL_FRAMEBUFFER, rgbFbo);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rgbRbo);
        EXPECT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
                  static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
        ASSERT_GL_NO_ERROR();
        glClearColor(1.0f, 0.0f, 1.0f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Clear texture framebuffer.
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 0, 0, 0,
                        255);
    
        // BlitFramebuffer from/to RGB framebuffer succeeds.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, rgbFbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glBlitFramebufferANGLE(0, 0, bufferSize, bufferSize, 0, 0, bufferSize, bufferSize,
                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
                        255, 255);
    
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rgbFbo);
        glBlitFramebufferANGLE(0, 0, bufferSize, bufferSize, 0, 0, bufferSize, bufferSize,
                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
        glBindFramebuffer(GL_FRAMEBUFFER, rgbFbo);
        EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 0, 0, 0,
                        255);
    
        glDeleteFramebuffers(1, &rgbFbo);
        glDeleteRenderbuffers(1, &rgbRbo);
        glDeleteFramebuffers(1, &rgbaFbo);
        glDeleteRenderbuffers(1, &rgbaRbo);
        glDeleteFramebuffers(1, &fbo);
        glDeleteTextures(1, &texture);
    
        d3d11_texture->Release();
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST(D3DTextureTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(D3DTextureTestES3, ES3_D3D11(), ES3_OPENGL());
    // D3D Debug device reports an error. http://anglebug.com/3513
    // ANGLE_INSTANTIATE_TEST(D3DTextureTestMS, ES2_D3D11());
    }  // namespace angle