Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2018-12-29 10:29:33
    Hash : ba319ba3
    Message : Re-land "Load entry points dynamically in tests and samples." Fixes the Android/ChromeOS/Fuchsia builds by using consistent EGL headers. This CL adds a dynamic loader generator based on XML files. It also refactors the entry point generation script to move the XML parsing into a helper class. Additionally this includes a new GLES 1.0 base header. The new header allows for function pointer types and hiding prototypes. All tests and samples now load ANGLE dynamically. In the future this will be extended to load entry points from the driver directly when possible. This will allow us to perform more accurate A/B testing. The new build configuration leads to some tests having more warnings applied. The CL includes fixes for the new warnings. Bug: angleproject:2995 Change-Id: I5a8772f41a0f89570b3736b785f44b7de1539b57 Reviewed-on: https://chromium-review.googlesource.com/c/1392382 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/tests/gl_tests/TimerQueriesTest.cpp
  • //
    // Copyright 2016 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.
    //
    // TimerQueriesTest.cpp
    //   Various tests for EXT_disjoint_timer_query functionality and validation
    //
    
    #include "test_utils/ANGLETest.h"
    #include "util/EGLWindow.h"
    #include "util/random_utils.h"
    #include "util/system_utils.h"
    
    using namespace angle;
    
    class TimerQueriesTest : public ANGLETest
    {
      protected:
        TimerQueriesTest() : mProgram(0), mProgramCostly(0)
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    
        virtual void SetUp()
        {
            ANGLETest::SetUp();
    
            constexpr char kCostlyVS[] =
                "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
                "{\n"
                "    testPos     = position;\n"
                "    gl_Position = position;\n"
                "}\n";
    
            constexpr char kCostlyFS[] =
                "precision highp float; varying highp vec4 testPos; void main(void)\n"
                "{\n"
                "    vec4 test = testPos;\n"
                "    for (int i = 0; i < 500; i++)\n"
                "    {\n"
                "        test = sqrt(test);\n"
                "    }\n"
                "    gl_FragColor = test;\n"
                "}\n";
    
            mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
            ASSERT_NE(0u, mProgram) << "shader compilation failed.";
    
            mProgramCostly = CompileProgram(kCostlyVS, kCostlyFS);
            ASSERT_NE(0u, mProgramCostly) << "shader compilation failed.";
        }
    
        virtual void TearDown()
        {
            glDeleteProgram(mProgram);
            glDeleteProgram(mProgramCostly);
            ANGLETest::TearDown();
        }
    
        GLuint mProgram;
        GLuint mProgramCostly;
    };
    
    // Test that all proc addresses are loadable
    TEST_P(TimerQueriesTest, ProcAddresses)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        ASSERT_NE(nullptr, eglGetProcAddress("glGenQueriesEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glDeleteQueriesEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glIsQueryEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glBeginQueryEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glEndQueryEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glQueryCounterEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryivEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectivEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectuivEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjecti64vEXT"));
        ASSERT_NE(nullptr, eglGetProcAddress("glGetQueryObjectui64vEXT"));
    }
    
    // Tests the time elapsed query
    TEST_P(TimerQueriesTest, TimeElapsed)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        GLint queryTimeElapsedBits = 0;
        glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
    
        // Skip test if the number of bits is 0
        ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
    
        glDepthMask(GL_TRUE);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        GLuint query1 = 0;
        GLuint query2 = 0;
        glGenQueriesEXT(1, &query1);
        glGenQueriesEXT(1, &query2);
    
        // Test time elapsed for a single quad
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query1);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        ASSERT_GL_NO_ERROR();
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Test time elapsed for costly quad
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query2);
        drawQuad(mProgramCostly, "position", 0.8f);
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        ASSERT_GL_NO_ERROR();
    
        swapBuffers();
    
        int timeout  = 200000;
        GLuint ready = GL_FALSE;
        while (ready == GL_FALSE && timeout > 0)
        {
            angle::Sleep(0);
            glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
            timeout--;
        }
        ready = GL_FALSE;
        while (ready == GL_FALSE && timeout > 0)
        {
            angle::Sleep(0);
            glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
            timeout--;
        }
        ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
    
        GLuint64 result1 = 0;
        GLuint64 result2 = 0;
        glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
        glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
        ASSERT_GL_NO_ERROR();
    
        glDeleteQueriesEXT(1, &query1);
        glDeleteQueriesEXT(1, &query2);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
        std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
    
        // The time elapsed should be nonzero
        EXPECT_LT(0ul, result1);
        EXPECT_LT(0ul, result2);
    
        // The time elapsed should be less than a second.  Not an actual
        // requirement, but longer than a second to draw something basic hints at
        // an issue with the queries themselves.
        EXPECT_LT(result1, 1000000000ul);
        EXPECT_LT(result2, 1000000000ul);
    
        // TODO(geofflang): Re-enable this check when it is non-flaky
        // The costly quad should take longer than the cheap quad
        // EXPECT_LT(result1, result2);
    }
    
    // Tests time elapsed for a non draw call (texture upload)
    TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
    {
        // OSX drivers don't seem to properly time non-draw calls so we skip the test on Mac
        ANGLE_SKIP_TEST_IF(IsOSX());
    
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        GLint queryTimeElapsedBits = 0;
        glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
    
        // Skip test if the number of bits is 0
        ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
    
        GLubyte pixels[] = {0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0};
    
        // Query and texture initialization
        GLuint texture;
        GLuint query = 0;
        glGenQueriesEXT(1, &query);
        glGenTextures(1, &texture);
    
        // Upload a texture inside the query
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
        glGenerateMipmap(GL_TEXTURE_2D);
        glFinish();
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        ASSERT_GL_NO_ERROR();
    
        int timeout  = 200000;
        GLuint ready = GL_FALSE;
        while (ready == GL_FALSE && timeout > 0)
        {
            angle::Sleep(0);
            glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
            timeout--;
        }
        ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
    
        GLuint64 result = 0;
        glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &result);
        ASSERT_GL_NO_ERROR();
    
        glDeleteTextures(1, &texture);
        glDeleteQueriesEXT(1, &query);
    
        std::cout << "Elapsed time: " << result << std::endl;
        EXPECT_LT(0ul, result);
    
        // an issue with the queries themselves.
        EXPECT_LT(result, 1000000000ul);
    }
    
    // Tests validation of query functions with respect to elapsed time query
    TEST_P(TimerQueriesTest, TimeElapsedValidationTest)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        GLint queryTimeElapsedBits = 0;
        glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
    
        // Skip test if the number of bits is 0
        ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
    
        GLuint query = 0;
        glGenQueriesEXT(-1, &query);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        glGenQueriesEXT(1, &query);
        EXPECT_GL_NO_ERROR();
    
        glBeginQueryEXT(GL_TIMESTAMP_EXT, query);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, 0);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
        EXPECT_GL_NO_ERROR();
    
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        EXPECT_GL_NO_ERROR();
    
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    // Tests timer queries operating under multiple EGL contexts with mid-query switching
    TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
    {
        // TODO(jmadill): Figure out why this test is flaky on AMD/OpenGL.
        // http://anglebug.com/1541
        ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
    
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        // Test skipped because the Vulkan backend doesn't account for (and remove) time spent in other
        // contexts.
        ANGLE_SKIP_TEST_IF(IsVulkan());
    
        GLint queryTimeElapsedBits = 0;
        glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
    
        // Skip test if the number of bits is 0
        ANGLE_SKIP_TEST_IF(!queryTimeElapsedBits);
    
        // Without a glClear, the first draw call on GL takes a huge amount of time when run after the
        // D3D test on certain NVIDIA drivers
        glDepthMask(GL_TRUE);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        EGLint contextAttributes[] = {
            EGL_CONTEXT_MAJOR_VERSION_KHR,
            GetParam().majorVersion,
            EGL_CONTEXT_MINOR_VERSION_KHR,
            GetParam().minorVersion,
            EGL_NONE,
        };
    
        EGLWindow *window = getEGLWindow();
    
        EGLDisplay display = window->getDisplay();
        EGLConfig config   = window->getConfig();
        EGLSurface surface = window->getSurface();
    
        struct ContextInfo
        {
            EGLContext context;
            GLuint program;
            GLuint query;
            EGLDisplay display;
    
            ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {}
    
            ~ContextInfo()
            {
                if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY)
                {
                    eglDestroyContext(display, context);
                }
            }
        };
        ContextInfo contexts[2];
    
        constexpr char kCostlyVS[] =
            "attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
            "{\n"
            "    testPos     = position;\n"
            "    gl_Position = position;\n"
            "}\n";
    
        constexpr char kCostlyFS[] =
            "precision highp float; varying highp vec4 testPos; void main(void)\n"
            "{\n"
            "    vec4 test = testPos;\n"
            "    for (int i = 0; i < 500; i++)\n"
            "    {\n"
            "        test = sqrt(test);\n"
            "    }\n"
            "    gl_FragColor = test;\n"
            "}\n";
    
        // Setup the first context with a cheap shader
        contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
        contexts[0].display = display;
        ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT);
        eglMakeCurrent(display, surface, surface, contexts[0].context);
        contexts[0].program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glGenQueriesEXT(1, &contexts[0].query);
        ASSERT_GL_NO_ERROR();
    
        // Setup the second context with an expensive shader
        contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
        contexts[1].display = display;
        ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT);
        eglMakeCurrent(display, surface, surface, contexts[1].context);
        contexts[1].program = CompileProgram(kCostlyVS, kCostlyFS);
        glGenQueriesEXT(1, &contexts[1].query);
        ASSERT_GL_NO_ERROR();
    
        // Start the query and draw a quad on the first context without ending the query
        eglMakeCurrent(display, surface, surface, contexts[0].context);
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query);
        drawQuad(contexts[0].program, essl1_shaders::PositionAttrib(), 0.8f);
        ASSERT_GL_NO_ERROR();
    
        // Switch contexts, draw the expensive quad and end its query
        eglMakeCurrent(display, surface, surface, contexts[1].context);
        glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query);
        drawQuad(contexts[1].program, "position", 0.8f);
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Go back to the first context, end its query, and get the result
        eglMakeCurrent(display, surface, surface, contexts[0].context);
        glEndQueryEXT(GL_TIME_ELAPSED_EXT);
    
        GLuint64 result1 = 0;
        GLuint64 result2 = 0;
        glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1);
        glDeleteQueriesEXT(1, &contexts[0].query);
        glDeleteProgram(contexts[0].program);
        ASSERT_GL_NO_ERROR();
    
        // Get the 2nd context's results
        eglMakeCurrent(display, surface, surface, contexts[1].context);
        glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2);
        glDeleteQueriesEXT(1, &contexts[1].query);
        glDeleteProgram(contexts[1].program);
        ASSERT_GL_NO_ERROR();
    
        // Switch back to main context
        eglMakeCurrent(display, surface, surface, window->getContext());
    
        // Compare the results. The cheap quad should be smaller than the expensive one if
        // virtualization is working correctly
        std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
        std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
        EXPECT_LT(0ul, result1);
        EXPECT_LT(0ul, result2);
        EXPECT_LT(result1, 1000000000ul);
        EXPECT_LT(result2, 1000000000ul);
        EXPECT_LT(result1, result2);
    }
    
    // Tests GPU timestamp functionality
    TEST_P(TimerQueriesTest, Timestamp)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        GLint queryTimestampBits = 0;
        glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
    
        // Macs for some reason return 0 bits so skip the test for now if either are 0
        ANGLE_SKIP_TEST_IF(!queryTimestampBits);
    
        glDepthMask(GL_TRUE);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        GLuint query1 = 0;
        GLuint query2 = 0;
        glGenQueriesEXT(1, &query1);
        glGenQueriesEXT(1, &query2);
        glQueryCounterEXT(query1, GL_TIMESTAMP_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
        glQueryCounterEXT(query2, GL_TIMESTAMP_EXT);
    
        ASSERT_GL_NO_ERROR();
    
        swapBuffers();
    
        int timeout  = 200000;
        GLuint ready = GL_FALSE;
        while (ready == GL_FALSE && timeout > 0)
        {
            angle::Sleep(0);
            glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
            timeout--;
        }
        ready = GL_FALSE;
        while (ready == GL_FALSE && timeout > 0)
        {
            angle::Sleep(0);
            glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
            timeout--;
        }
        ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
    
        GLuint64 result1 = 0;
        GLuint64 result2 = 0;
        glGetQueryObjectui64vEXT(query1, GL_QUERY_RESULT_EXT, &result1);
        glGetQueryObjectui64vEXT(query2, GL_QUERY_RESULT_EXT, &result2);
    
        ASSERT_GL_NO_ERROR();
    
        glDeleteQueriesEXT(1, &query1);
        glDeleteQueriesEXT(1, &query2);
    
        std::cout << "Timestamps: " << result1 << " " << result2 << std::endl;
        EXPECT_LT(0ul, result1);
        EXPECT_LT(0ul, result2);
        EXPECT_LT(result1, result2);
    }
    
    class TimerQueriesTestES3 : public TimerQueriesTest
    {};
    
    // Tests getting timestamps via glGetInteger64v
    TEST_P(TimerQueriesTestES3, TimestampGetInteger64)
    {
        ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
    
        GLint queryTimestampBits = 0;
        glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimestampBits);
        ASSERT_GL_NO_ERROR();
    
        std::cout << "Timestamp counter bits: " << queryTimestampBits << std::endl;
    
        ANGLE_SKIP_TEST_IF(!queryTimestampBits);
    
        glDepthMask(GL_TRUE);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        GLint64 result1 = 0;
        GLint64 result2 = 0;
        glGetInteger64v(GL_TIMESTAMP_EXT, &result1);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.8f);
        glGetInteger64v(GL_TIMESTAMP_EXT, &result2);
        ASSERT_GL_NO_ERROR();
        std::cout << "Timestamps (getInteger64v): " << result1 << " " << result2 << std::endl;
        EXPECT_LT(0l, result1);
        EXPECT_LT(0l, result2);
        EXPECT_LT(result1, result2);
    }
    
    ANGLE_INSTANTIATE_TEST(TimerQueriesTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES3_D3D11(),
                           ES2_OPENGL(),
                           ES3_OPENGL(),
                           ES2_VULKAN());
    
    ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL());