Edit

kc3-lang/angle/src/tests/test_utils/ANGLETest.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2017-07-11 13:51:05
    Hash : 293e114d
    Message : Add tests for the cache control extension. A first test verifies the validation of the extension, and a second compiles a simple shader and checks the cache behaviour. These tests don't currently run since the extension is disabled, but they will start running as soon as we expose it. BUG=angleproject:1897 Change-Id: Id4a76b83090712a267576069b711eeee29aa81dd

  • src/tests/test_utils/ANGLETest.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.
    //
    // ANGLETest:
    //   Implementation of common ANGLE testing fixture.
    //
    
    #include "ANGLETest.h"
    #include "EGLWindow.h"
    #include "OSWindow.h"
    
    namespace angle
    {
    
    const GLColorRGB GLColorRGB::black(0u, 0u, 0u);
    const GLColorRGB GLColorRGB::blue(0u, 0u, 255u);
    const GLColorRGB GLColorRGB::green(0u, 255u, 0u);
    const GLColorRGB GLColorRGB::red(255u, 0u, 0u);
    const GLColorRGB GLColorRGB::yellow(255u, 255u, 0);
    
    const GLColor GLColor::black            = GLColor(0u, 0u, 0u, 255u);
    const GLColor GLColor::blue             = GLColor(0u, 0u, 255u, 255u);
    const GLColor GLColor::cyan             = GLColor(0u, 255u, 255u, 255u);
    const GLColor GLColor::green            = GLColor(0u, 255u, 0u, 255u);
    const GLColor GLColor::red              = GLColor(255u, 0u, 0u, 255u);
    const GLColor GLColor::transparentBlack = GLColor(0u, 0u, 0u, 0u);
    const GLColor GLColor::white            = GLColor(255u, 255u, 255u, 255u);
    const GLColor GLColor::yellow           = GLColor(255u, 255u, 0, 255u);
    const GLColor GLColor::magenta          = GLColor(255u, 0u, 255u, 255u);
    
    namespace
    {
    float ColorNorm(GLubyte channelValue)
    {
        return static_cast<float>(channelValue) / 255.0f;
    }
    
    GLubyte ColorDenorm(float colorValue)
    {
        return static_cast<GLubyte>(colorValue * 255.0f);
    }
    
    void TestPlatform_logError(angle::PlatformMethods *platform, const char *errorMessage)
    {
        auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
        if (testPlatformContext->ignoreMessages)
            return;
    
        FAIL() << errorMessage;
    }
    
    void TestPlatform_logWarning(angle::PlatformMethods *platform, const char *warningMessage)
    {
        auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
        if (testPlatformContext->ignoreMessages)
            return;
    
        std::cerr << "Warning: " << warningMessage << std::endl;
    }
    
    void TestPlatform_logInfo(angle::PlatformMethods *platform, const char *infoMessage)
    {
        auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
        if (testPlatformContext->ignoreMessages)
            return;
    
        angle::WriteDebugMessage("%s\n", infoMessage);
    }
    
    void TestPlatform_overrideWorkaroundsD3D(angle::PlatformMethods *platform,
                                             WorkaroundsD3D *workaroundsD3D)
    {
        auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
        if (testPlatformContext->currentTest)
        {
            testPlatformContext->currentTest->overrideWorkaroundsD3D(workaroundsD3D);
        }
    }
    
    std::array<angle::Vector3, 4> GetIndexedQuadVertices()
    {
        std::array<angle::Vector3, 4> vertices;
        vertices[0] = angle::Vector3(-1.0f, 1.0f, 0.5f);
        vertices[1] = angle::Vector3(-1.0f, -1.0f, 0.5f);
        vertices[2] = angle::Vector3(1.0f, -1.0f, 0.5f);
        vertices[3] = angle::Vector3(1.0f, 1.0f, 0.5f);
        return vertices;
    }
    
    static constexpr GLushort IndexedQuadIndices[6] = {0, 1, 2, 0, 2, 3};
    
    }  // anonymous namespace
    
    GLColorRGB::GLColorRGB() : R(0), G(0), B(0)
    {
    }
    
    GLColorRGB::GLColorRGB(GLubyte r, GLubyte g, GLubyte b) : R(r), G(g), B(b)
    {
    }
    
    GLColorRGB::GLColorRGB(const angle::Vector3 &floatColor)
        : R(ColorDenorm(floatColor.x())), G(ColorDenorm(floatColor.y())), B(ColorDenorm(floatColor.z()))
    {
    }
    
    GLColor::GLColor() : R(0), G(0), B(0), A(0)
    {
    }
    
    GLColor::GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a) : R(r), G(g), B(b), A(a)
    {
    }
    
    GLColor::GLColor(const angle::Vector4 &floatColor)
        : R(ColorDenorm(floatColor.x())),
          G(ColorDenorm(floatColor.y())),
          B(ColorDenorm(floatColor.z())),
          A(ColorDenorm(floatColor.w()))
    {
    }
    
    GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0)
    {
        memcpy(&R, &colorValue, sizeof(GLuint));
    }
    
    angle::Vector4 GLColor::toNormalizedVector() const
    {
        return angle::Vector4(ColorNorm(R), ColorNorm(G), ColorNorm(B), ColorNorm(A));
    }
    
    GLColor32F::GLColor32F() : GLColor32F(0.0f, 0.0f, 0.0f, 0.0f)
    {
    }
    
    GLColor32F::GLColor32F(GLfloat r, GLfloat g, GLfloat b, GLfloat a) : R(r), G(g), B(b), A(a)
    {
    }
    
    GLColor ReadColor(GLint x, GLint y)
    {
        GLColor actual;
        glReadPixels((x), (y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual.R);
        EXPECT_GL_NO_ERROR();
        return actual;
    }
    
    bool operator==(const GLColor &a, const GLColor &b)
    {
        return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A;
    }
    
    bool operator!=(const GLColor &a, const GLColor &b)
    {
        return !(a == b);
    }
    
    std::ostream &operator<<(std::ostream &ostream, const GLColor &color)
    {
        ostream << "(" << static_cast<unsigned int>(color.R) << ", "
                << static_cast<unsigned int>(color.G) << ", " << static_cast<unsigned int>(color.B)
                << ", " << static_cast<unsigned int>(color.A) << ")";
        return ostream;
    }
    
    bool operator==(const GLColor32F &a, const GLColor32F &b)
    {
        return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A;
    }
    
    std::ostream &operator<<(std::ostream &ostream, const GLColor32F &color)
    {
        ostream << "(" << color.R << ", " << color.G << ", " << color.B << ", " << color.A << ")";
        return ostream;
    }
    
    GLColor32F ReadColor32F(GLint x, GLint y)
    {
        GLColor32F actual;
        glReadPixels((x), (y), 1, 1, GL_RGBA, GL_FLOAT, &actual.R);
        EXPECT_GL_NO_ERROR();
        return actual;
    }
    
    }  // namespace angle
    
    // static
    std::array<angle::Vector3, 6> ANGLETestBase::GetQuadVertices()
    {
        std::array<angle::Vector3, 6> vertices;
        vertices[0] = angle::Vector3(-1.0f, 1.0f, 0.5f);
        vertices[1] = angle::Vector3(-1.0f, -1.0f, 0.5f);
        vertices[2] = angle::Vector3(1.0f, -1.0f, 0.5f);
        vertices[3] = angle::Vector3(-1.0f, 1.0f, 0.5f);
        vertices[4] = angle::Vector3(1.0f, -1.0f, 0.5f);
        vertices[5] = angle::Vector3(1.0f, 1.0f, 0.5f);
        return vertices;
    }
    
    ANGLETestBase::ANGLETestBase(const angle::PlatformParameters &params)
        : mEGLWindow(nullptr),
          mWidth(16),
          mHeight(16),
          mIgnoreD3D11SDKLayersWarnings(false),
          mQuadVertexBuffer(0),
          mQuadIndexBuffer(0),
          mDeferContextInit(false)
    {
        mEGLWindow = new EGLWindow(params.majorVersion, params.minorVersion, params.eglParameters);
    
        // Default vulkan layers to enabled.
        EGLint renderer = params.getRenderer();
        if (renderer == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)
        {
            mEGLWindow->setVulkanLayersEnabled(true);
        }
    
        // Workaround for NVIDIA not being able to share OpenGL and Vulkan contexts.
        bool needsWindowSwap = mLastRendererType.valid() &&
                               ((renderer != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) !=
                                (mLastRendererType.value() != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE));
    
        if (needsWindowSwap)
        {
            DestroyTestWindow();
            if (!InitTestWindow())
            {
                std::cerr << "Failed to create ANGLE test window.";
            }
        }
        mLastRendererType = renderer;
    }
    
    ANGLETestBase::~ANGLETestBase()
    {
        if (mQuadVertexBuffer)
        {
            glDeleteBuffers(1, &mQuadVertexBuffer);
        }
        if (mQuadIndexBuffer)
        {
            glDeleteBuffers(1, &mQuadIndexBuffer);
        }
        SafeDelete(mEGLWindow);
    }
    
    void ANGLETestBase::ANGLETestSetUp()
    {
        mPlatformContext.ignoreMessages = false;
        mPlatformContext.currentTest    = this;
    
        // Resize the window before creating the context so that the first make current
        // sets the viewport and scissor box to the right size.
        bool needSwap = false;
        if (mOSWindow->getWidth() != mWidth || mOSWindow->getHeight() != mHeight)
        {
            if (!mOSWindow->resize(mWidth, mHeight))
            {
                FAIL() << "Failed to resize ANGLE test window.";
            }
            needSwap = true;
        }
    
        mPlatformMethods.overrideWorkaroundsD3D = angle::TestPlatform_overrideWorkaroundsD3D;
        mPlatformMethods.logError               = angle::TestPlatform_logError;
        mPlatformMethods.logWarning             = angle::TestPlatform_logWarning;
        mPlatformMethods.logInfo                = angle::TestPlatform_logInfo;
        mPlatformMethods.context                = &mPlatformContext;
        mEGLWindow->setPlatformMethods(&mPlatformMethods);
    
        if (!mEGLWindow->initializeDisplayAndSurface(mOSWindow))
        {
            FAIL() << "egl display or surface init failed.";
        }
    
        if (!mDeferContextInit && !mEGLWindow->initializeContext())
        {
            FAIL() << "GL Context init failed.";
        }
    
        if (needSwap)
        {
            // Swap the buffers so that the default framebuffer picks up the resize
            // which will allow follow-up test code to assume the framebuffer covers
            // the whole window.
            swapBuffers();
        }
    
        // This Viewport command is not strictly necessary but we add it so that programs
        // taking OpenGL traces can guess the size of the default framebuffer and show it
        // in their UIs
        glViewport(0, 0, mWidth, mHeight);
    
        const auto &info = testing::UnitTest::GetInstance()->current_test_info();
        angle::WriteDebugMessage("Entering %s.%s\n", info->test_case_name(), info->name());
    }
    
    void ANGLETestBase::ANGLETestTearDown()
    {
        mEGLWindow->setPlatformMethods(nullptr);
    
        mPlatformContext.currentTest = nullptr;
        checkD3D11SDKLayersMessages();
    
        const auto &info = testing::UnitTest::GetInstance()->current_test_info();
        angle::WriteDebugMessage("Exiting %s.%s\n", info->test_case_name(), info->name());
    
        swapBuffers();
    
        if (eglGetError() != EGL_SUCCESS)
        {
            FAIL() << "egl error during swap.";
        }
    
        mOSWindow->messageLoop();
    
        if (!destroyEGLContext())
        {
            FAIL() << "egl context destruction failed.";
        }
    
        // Check for quit message
        Event myEvent;
        while (mOSWindow->popEvent(&myEvent))
        {
            if (myEvent.Type == Event::EVENT_CLOSED)
            {
                exit(0);
            }
        }
    }
    
    void ANGLETestBase::swapBuffers()
    {
        if (mEGLWindow->isGLInitialized())
        {
            mEGLWindow->swap();
        }
    }
    
    void ANGLETestBase::setupQuadVertexBuffer(GLfloat positionAttribZ, GLfloat positionAttribXYScale)
    {
        if (mQuadVertexBuffer == 0)
        {
            glGenBuffers(1, &mQuadVertexBuffer);
        }
    
        auto quadVertices = GetQuadVertices();
        for (angle::Vector3 &vertex : quadVertices)
        {
            vertex.x() *= positionAttribXYScale;
            vertex.y() *= positionAttribXYScale;
            vertex.z() = positionAttribZ;
        }
    
        glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
    }
    
    void ANGLETestBase::setupIndexedQuadVertexBuffer(GLfloat positionAttribZ,
                                                     GLfloat positionAttribXYScale)
    {
        if (mQuadVertexBuffer == 0)
        {
            glGenBuffers(1, &mQuadVertexBuffer);
        }
    
        auto quadVertices = angle::GetIndexedQuadVertices();
        for (angle::Vector3 &vertex : quadVertices)
        {
            vertex.x() *= positionAttribXYScale;
            vertex.y() *= positionAttribXYScale;
            vertex.z() = positionAttribZ;
        }
    
        glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 4, quadVertices.data(), GL_STATIC_DRAW);
    }
    
    void ANGLETestBase::setupIndexedQuadIndexBuffer()
    {
        if (mQuadIndexBuffer == 0)
        {
            glGenBuffers(1, &mQuadIndexBuffer);
        }
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadIndexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(angle::IndexedQuadIndices),
                     angle::IndexedQuadIndices, GL_STATIC_DRAW);
    }
    
    // static
    void ANGLETestBase::drawQuad(GLuint program,
                                 const std::string &positionAttribName,
                                 GLfloat positionAttribZ)
    {
        drawQuad(program, positionAttribName, positionAttribZ, 1.0f);
    }
    
    // static
    void ANGLETestBase::drawQuad(GLuint program,
                                 const std::string &positionAttribName,
                                 GLfloat positionAttribZ,
                                 GLfloat positionAttribXYScale)
    {
        drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, false);
    }
    
    void ANGLETestBase::drawQuad(GLuint program,
                                 const std::string &positionAttribName,
                                 GLfloat positionAttribZ,
                                 GLfloat positionAttribXYScale,
                                 bool useVertexBuffer)
    {
        GLint previousProgram = 0;
        glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram);
        if (previousProgram != static_cast<GLint>(program))
        {
            glUseProgram(program);
        }
    
        GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
    
        if (useVertexBuffer)
        {
            setupQuadVertexBuffer(positionAttribZ, positionAttribXYScale);
            glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
        }
        else
        {
            auto quadVertices = GetQuadVertices();
            for (angle::Vector3 &vertex : quadVertices)
            {
                vertex.x() *= positionAttribXYScale;
                vertex.y() *= positionAttribXYScale;
                vertex.z() = positionAttribZ;
            }
    
            glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
        }
        glEnableVertexAttribArray(positionLocation);
    
        glDrawArrays(GL_TRIANGLES, 0, 6);
    
        glDisableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
    
        if (previousProgram != static_cast<GLint>(program))
        {
            glUseProgram(previousProgram);
        }
    }
    
    void ANGLETestBase::drawIndexedQuad(GLuint program,
                                        const std::string &positionAttribName,
                                        GLfloat positionAttribZ)
    {
        drawIndexedQuad(program, positionAttribName, positionAttribZ, 1.0f);
    }
    
    void ANGLETestBase::drawIndexedQuad(GLuint program,
                                        const std::string &positionAttribName,
                                        GLfloat positionAttribZ,
                                        GLfloat positionAttribXYScale)
    {
        drawIndexedQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, false);
    }
    
    void ANGLETestBase::drawIndexedQuad(GLuint program,
                                        const std::string &positionAttribName,
                                        GLfloat positionAttribZ,
                                        GLfloat positionAttribXYScale,
                                        bool useIndexBuffer)
    {
        GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
    
        GLint activeProgram = 0;
        glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram);
        if (static_cast<GLuint>(activeProgram) != program)
        {
            glUseProgram(program);
        }
    
        GLuint prevCoordBinding = 0;
        glGetIntegerv(GL_ARRAY_BUFFER_BINDING, reinterpret_cast<GLint *>(&prevCoordBinding));
    
        setupIndexedQuadVertexBuffer(positionAttribZ, positionAttribXYScale);
    
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(positionLocation);
        glBindBuffer(GL_ARRAY_BUFFER, prevCoordBinding);
    
        GLuint prevIndexBinding = 0;
        const GLvoid *indices;
        if (useIndexBuffer)
        {
            glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING,
                          reinterpret_cast<GLint *>(&prevIndexBinding));
    
            setupIndexedQuadIndexBuffer();
            indices = 0;
        }
        else
        {
            indices = angle::IndexedQuadIndices;
        }
    
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
    
        if (useIndexBuffer)
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prevIndexBinding);
        }
    
        glDisableVertexAttribArray(positionLocation);
        glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
    
        if (static_cast<GLuint>(activeProgram) != program)
        {
            glUseProgram(static_cast<GLuint>(activeProgram));
        }
    }
    
    GLuint ANGLETestBase::compileShader(GLenum type, const std::string &source)
    {
        GLuint shader = glCreateShader(type);
    
        const char *sourceArray[1] = { source.c_str() };
        glShaderSource(shader, 1, sourceArray, nullptr);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
    
        if (compileResult == 0)
        {
            GLint infoLogLength;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
    
            if (infoLogLength == 0)
            {
                std::cerr << "shader compilation failed with empty log." << std::endl;
            }
            else
            {
                std::vector<GLchar> infoLog(infoLogLength);
                glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
    
                std::cerr << "shader compilation failed: " << &infoLog[0] << std::endl;
            }
    
            glDeleteShader(shader);
            shader = 0;
        }
    
        return shader;
    }
    
    void ANGLETestBase::checkD3D11SDKLayersMessages()
    {
    #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(NDEBUG)
        // In debug D3D11 mode, check ID3D11InfoQueue to see if any D3D11 SDK Layers messages
        // were outputted by the test
        if (mIgnoreD3D11SDKLayersWarnings ||
            mEGLWindow->getPlatform().renderer != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE ||
            mEGLWindow->getDisplay() == EGL_NO_DISPLAY)
        {
            return;
        }
    
        const char *extensionString =
            static_cast<const char *>(eglQueryString(mEGLWindow->getDisplay(), EGL_EXTENSIONS));
        if (!extensionString)
        {
            std::cout << "Error getting extension string from EGL Window." << std::endl;
            return;
        }
    
        if (!strstr(extensionString, "EGL_EXT_device_query"))
        {
            return;
        }
    
        EGLAttrib device      = 0;
        EGLAttrib angleDevice = 0;
    
        PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT;
        PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT;
    
        queryDisplayAttribEXT = reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
            eglGetProcAddress("eglQueryDisplayAttribEXT"));
        queryDeviceAttribEXT = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
            eglGetProcAddress("eglQueryDeviceAttribEXT"));
        ASSERT_NE(nullptr, queryDisplayAttribEXT);
        ASSERT_NE(nullptr, queryDeviceAttribEXT);
    
        ASSERT_EGL_TRUE(queryDisplayAttribEXT(mEGLWindow->getDisplay(), EGL_DEVICE_EXT, &angleDevice));
        ASSERT_EGL_TRUE(queryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice),
                                             EGL_D3D11_DEVICE_ANGLE, &device));
        ID3D11Device *d3d11Device = reinterpret_cast<ID3D11Device *>(device);
    
        ID3D11InfoQueue *infoQueue = nullptr;
        HRESULT hr =
            d3d11Device->QueryInterface(__uuidof(infoQueue), reinterpret_cast<void **>(&infoQueue));
        if (SUCCEEDED(hr))
        {
            UINT64 numStoredD3DDebugMessages =
                infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter();
    
            if (numStoredD3DDebugMessages > 0)
            {
                for (UINT64 i = 0; i < numStoredD3DDebugMessages; i++)
                {
                    SIZE_T messageLength = 0;
                    hr                   = infoQueue->GetMessage(i, nullptr, &messageLength);
    
                    if (SUCCEEDED(hr))
                    {
                        D3D11_MESSAGE *pMessage =
                            reinterpret_cast<D3D11_MESSAGE *>(malloc(messageLength));
                        infoQueue->GetMessage(i, pMessage, &messageLength);
    
                        std::cout << "Message " << i << ":"
                                  << " " << pMessage->pDescription << "\n";
                        free(pMessage);
                    }
                }
    
                FAIL() << numStoredD3DDebugMessages
                       << " D3D11 SDK Layers message(s) detected! Test Failed.\n";
            }
        }
    
        SafeRelease(infoQueue);
    #endif
    }
    
    bool ANGLETestBase::extensionEnabled(const std::string &extName)
    {
        return CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
                                    extName);
    }
    
    bool ANGLETestBase::extensionRequestable(const std::string &extName)
    {
        return CheckExtensionExists(
            reinterpret_cast<const char *>(glGetString(GL_REQUESTABLE_EXTENSIONS_ANGLE)), extName);
    }
    
    bool ANGLETestBase::eglDisplayExtensionEnabled(EGLDisplay display, const std::string &extName)
    {
        return CheckExtensionExists(eglQueryString(display, EGL_EXTENSIONS), extName);
    }
    
    bool ANGLETestBase::eglClientExtensionEnabled(const std::string &extName)
    {
        return EGLWindow::ClientExtensionEnabled(extName);
    }
    
    bool ANGLETestBase::eglDeviceExtensionEnabled(EGLDeviceEXT device, const std::string &extName)
    {
        PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
            reinterpret_cast<PFNEGLQUERYDEVICESTRINGEXTPROC>(
                eglGetProcAddress("eglQueryDeviceStringEXT"));
        return CheckExtensionExists(eglQueryDeviceStringEXT(device, EGL_EXTENSIONS), extName);
    }
    
    void ANGLETestBase::setWindowWidth(int width)
    {
        mWidth = width;
    }
    
    void ANGLETestBase::setWindowHeight(int height)
    {
        mHeight = height;
    }
    
    void ANGLETestBase::setConfigRedBits(int bits)
    {
        mEGLWindow->setConfigRedBits(bits);
    }
    
    void ANGLETestBase::setConfigGreenBits(int bits)
    {
        mEGLWindow->setConfigGreenBits(bits);
    }
    
    void ANGLETestBase::setConfigBlueBits(int bits)
    {
        mEGLWindow->setConfigBlueBits(bits);
    }
    
    void ANGLETestBase::setConfigAlphaBits(int bits)
    {
        mEGLWindow->setConfigAlphaBits(bits);
    }
    
    void ANGLETestBase::setConfigDepthBits(int bits)
    {
        mEGLWindow->setConfigDepthBits(bits);
    }
    
    void ANGLETestBase::setConfigStencilBits(int bits)
    {
        mEGLWindow->setConfigStencilBits(bits);
    }
    
    void ANGLETestBase::setConfigComponentType(EGLenum componentType)
    {
        mEGLWindow->setConfigComponentType(componentType);
    }
    
    void ANGLETestBase::setMultisampleEnabled(bool enabled)
    {
        mEGLWindow->setMultisample(enabled);
    }
    
    void ANGLETestBase::setSamples(EGLint samples)
    {
        mEGLWindow->setSamples(samples);
    }
    
    void ANGLETestBase::setDebugEnabled(bool enabled)
    {
        mEGLWindow->setDebugEnabled(enabled);
    }
    
    void ANGLETestBase::setNoErrorEnabled(bool enabled)
    {
        mEGLWindow->setNoErrorEnabled(enabled);
    }
    
    void ANGLETestBase::setWebGLCompatibilityEnabled(bool webglCompatibility)
    {
        mEGLWindow->setWebGLCompatibilityEnabled(webglCompatibility);
    }
    
    void ANGLETestBase::setBindGeneratesResource(bool bindGeneratesResource)
    {
        mEGLWindow->setBindGeneratesResource(bindGeneratesResource);
    }
    
    void ANGLETestBase::setVulkanLayersEnabled(bool enabled)
    {
        mEGLWindow->setVulkanLayersEnabled(enabled);
    }
    
    void ANGLETestBase::setClientArraysEnabled(bool enabled)
    {
        mEGLWindow->setClientArraysEnabled(enabled);
    }
    
    void ANGLETestBase::setRobustResourceInit(bool enabled)
    {
        mEGLWindow->setRobustResourceInit(enabled);
    }
    
    void ANGLETestBase::setContextProgramCacheEnabled(bool enabled)
    {
        mEGLWindow->setContextProgramCacheEnabled(enabled);
    }
    
    void ANGLETestBase::setDeferContextInit(bool enabled)
    {
        mDeferContextInit = enabled;
    }
    
    int ANGLETestBase::getClientMajorVersion() const
    {
        return mEGLWindow->getClientMajorVersion();
    }
    
    int ANGLETestBase::getClientMinorVersion() const
    {
        return mEGLWindow->getClientMinorVersion();
    }
    
    EGLWindow *ANGLETestBase::getEGLWindow() const
    {
        return mEGLWindow;
    }
    
    int ANGLETestBase::getWindowWidth() const
    {
        return mWidth;
    }
    
    int ANGLETestBase::getWindowHeight() const
    {
        return mHeight;
    }
    
    bool ANGLETestBase::isMultisampleEnabled() const
    {
        return mEGLWindow->isMultisample();
    }
    
    bool ANGLETestBase::destroyEGLContext()
    {
        mEGLWindow->destroyGL();
        return true;
    }
    
    // static
    bool ANGLETestBase::InitTestWindow()
    {
        mOSWindow = CreateOSWindow();
        if (!mOSWindow->initialize("ANGLE_TEST", 128, 128))
        {
            return false;
        }
    
        mOSWindow->setVisible(true);
    
        mGLESLibrary.reset(angle::loadLibrary("libGLESv2"));
    
        return true;
    }
    
    // static
    bool ANGLETestBase::DestroyTestWindow()
    {
        if (mOSWindow)
        {
            mOSWindow->destroy();
            delete mOSWindow;
            mOSWindow = nullptr;
        }
    
        mGLESLibrary.reset(nullptr);
    
        return true;
    }
    
    void ANGLETestBase::SetWindowVisible(bool isVisible)
    {
        mOSWindow->setVisible(isVisible);
    }
    
    ANGLETest::ANGLETest() : ANGLETestBase(GetParam())
    {
    }
    
    void ANGLETest::SetUp()
    {
        ANGLETestBase::ANGLETestSetUp();
    }
    
    void ANGLETest::TearDown()
    {
        ANGLETestBase::ANGLETestTearDown();
    }
    
    bool IsIntel()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Intel") != std::string::npos);
    }
    
    bool IsAdreno()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Adreno") != std::string::npos);
    }
    
    bool IsAMD()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("AMD") != std::string::npos) ||
               (rendererString.find("ATI") != std::string::npos);
    }
    
    bool IsNVIDIA()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("NVIDIA") != std::string::npos);
    }
    
    bool IsD3D11()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Direct3D11 vs_5_0") != std::string::npos);
    }
    
    bool IsD3D11_FL93()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Direct3D11 vs_4_0_") != std::string::npos);
    }
    
    bool IsD3D9()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Direct3D9") != std::string::npos);
    }
    
    bool IsD3DSM3()
    {
        return IsD3D9() || IsD3D11_FL93();
    }
    
    bool IsDesktopOpenGL()
    {
        return IsOpenGL() && !IsOpenGLES();
    }
    
    bool IsOpenGLES()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("OpenGL ES") != std::string::npos);
    }
    
    bool IsOpenGL()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("OpenGL") != std::string::npos);
    }
    
    bool IsNULL()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("NULL") != std::string::npos);
    }
    
    bool IsAndroid()
    {
    #if defined(ANGLE_PLATFORM_ANDROID)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsVulkan()
    {
        std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
        return (rendererString.find("Vulkan") != std::string::npos);
    }
    
    bool IsOzone()
    {
    #if defined(USE_OZONE)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsLinux()
    {
    #if defined(ANGLE_PLATFORM_LINUX)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsOSX()
    {
    #if defined(ANGLE_PLATFORM_APPLE)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsWindows()
    {
    #if defined(ANGLE_PLATFORM_WINDOWS)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsDebug()
    {
    #if !defined(NDEBUG)
        return true;
    #else
        return false;
    #endif
    }
    
    bool IsRelease()
    {
        return !IsDebug();
    }
    
    EGLint ANGLETestBase::getPlatformRenderer() const
    {
        assert(mEGLWindow);
        return mEGLWindow->getPlatform().renderer;
    }
    
    void ANGLETestBase::ignoreD3D11SDKLayersWarnings()
    {
        // Some tests may need to disable the D3D11 SDK Layers Warnings checks
        mIgnoreD3D11SDKLayersWarnings = true;
    }
    
    OSWindow *ANGLETestBase::mOSWindow = nullptr;
    Optional<EGLint> ANGLETestBase::mLastRendererType;
    std::unique_ptr<angle::Library> ANGLETestBase::mGLESLibrary;
    
    void ANGLETestEnvironment::SetUp()
    {
        if (!ANGLETestBase::InitTestWindow())
        {
            FAIL() << "Failed to create ANGLE test window.";
        }
    }
    
    void ANGLETestEnvironment::TearDown()
    {
        ANGLETestBase::DestroyTestWindow();
    }