Edit

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

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2021-01-20 14:14:02
    Hash : 535d4783
    Message : Vulkan: Flush if sync object is pending during SyncVk::getStatus() When a glGetSynciv() is performed for GL_SYNC_STATUS, we should flush any pending commands if a sync object is pending a flush, since the caller is interested in the status of a fence. This will guarantee that the work is submitted to the hardware and eventually completes. This is accomplished by moving mSyncObjectPendingFlush from ContextVk to ShareGroupVk, so that any sync objects used by any contexts within the share group are submitted to hardware and the required work completes. Bug: angleproject:5306 Bug: angleproject:5425 Test: FenceSyncTest.BasicOperations Change-Id: I2e2681ad01fda429ba37f061c9bac5eb91f800fd Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2641095 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/tests/gl_tests/FenceSyncTests.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.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    class FenceNVTest : public ANGLETest
    {
      protected:
        FenceNVTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    };
    
    class FenceSyncTest : public ANGLETest
    {
      public:
        static constexpr uint32_t kSize = 1024;
    
      protected:
        FenceSyncTest()
        {
            setWindowWidth(kSize);
            setWindowHeight(kSize);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    };
    
    // FenceNV objects should respond false to glIsFenceNV until they've been set
    TEST_P(FenceNVTest, IsFence)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_fence"));
    
        GLuint fence = 0;
        glGenFencesNV(1, &fence);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_GL_FALSE(glIsFenceNV(fence));
        EXPECT_GL_NO_ERROR();
    
        glSetFenceNV(fence, GL_ALL_COMPLETED_NV);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_GL_TRUE(glIsFenceNV(fence));
        EXPECT_GL_NO_ERROR();
    }
    
    // Test error cases for all FenceNV functions
    TEST_P(FenceNVTest, Errors)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_fence"));
    
        EXPECT_GL_TRUE(glTestFenceNV(10)) << "glTestFenceNV should still return TRUE for an invalid "
                                             "fence and generate an INVALID_OPERATION";
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        GLuint fence = 20;
    
        // glGenFencesNV should generate INVALID_VALUE for a negative n and not write anything to the
        // fences pointer
        glGenFencesNV(-1, &fence);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
        EXPECT_EQ(20u, fence);
    
        // Generate a real fence
        glGenFencesNV(1, &fence);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_GL_TRUE(glTestFenceNV(fence)) << "glTestFenceNV should still return TRUE for a fence "
                                                "that is not started and generate an INVALID_OPERATION";
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    
        // glGetFenceivNV should generate an INVALID_OPERATION for an invalid or unstarted fence and not
        // modify the params
        GLint result = 30;
        glGetFenceivNV(10, GL_FENCE_STATUS_NV, &result);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
        EXPECT_EQ(30, result);
    
        glGetFenceivNV(fence, GL_FENCE_STATUS_NV, &result);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
        EXPECT_EQ(30, result);
    
        // glSetFenceNV should generate an error for any condition that is not ALL_COMPLETED_NV
        glSetFenceNV(fence, 0);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        // glSetFenceNV should generate INVALID_OPERATION for an invalid fence
        glSetFenceNV(10, GL_ALL_COMPLETED_NV);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    // Test that basic usage works and doesn't generate errors or crash
    TEST_P(FenceNVTest, BasicOperations)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_fence"));
    
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
    
        constexpr size_t kFenceCount = 20;
        GLuint fences[kFenceCount]   = {0};
        glGenFencesNV(static_cast<GLsizei>(ArraySize(fences)), fences);
        EXPECT_GL_NO_ERROR();
    
        for (GLuint fence : fences)
        {
            glClear(GL_COLOR_BUFFER_BIT);
            glSetFenceNV(fence, GL_ALL_COMPLETED_NV);
        }
    
        // Finish the last fence, all fences before should be marked complete
        glFinishFenceNV(fences[kFenceCount - 1]);
    
        for (GLuint fence : fences)
        {
            GLint status = 0;
            glGetFenceivNV(fence, GL_FENCE_STATUS_NV, &status);
            EXPECT_GL_NO_ERROR();
    
            // Fence should be complete now that Finish has been called
            EXPECT_GL_TRUE(status);
        }
    
        EXPECT_PIXEL_EQ(0, 0, 255, 0, 255, 255);
    }
    
    // Sync objects should respond true to IsSync after they are created with glFenceSync
    TEST_P(FenceSyncTest, IsSync)
    {
        GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_GL_TRUE(glIsSync(sync));
        EXPECT_GL_FALSE(glIsSync(reinterpret_cast<GLsync>(40)));
    }
    
    // Test error cases for all Sync function
    TEST_P(FenceSyncTest, Errors)
    {
        GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    
        // DeleteSync generates INVALID_VALUE when the sync is not valid
        glDeleteSync(reinterpret_cast<GLsync>(20));
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glFenceSync generates GL_INVALID_ENUM if the condition is not GL_SYNC_GPU_COMMANDS_COMPLETE
        EXPECT_EQ(0, glFenceSync(0, 0));
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        // glFenceSync generates GL_INVALID_ENUM if the flags is not 0
        EXPECT_EQ(0, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 10));
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glClientWaitSync generates GL_INVALID_VALUE and returns GL_WAIT_FAILED if flags contains more
        // than just GL_SYNC_FLUSH_COMMANDS_BIT
        EXPECT_GLENUM_EQ(GL_WAIT_FAILED, glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT | 0x2, 0));
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glClientWaitSync generates GL_INVALID_VALUE and returns GL_WAIT_FAILED if the sync object is
        // not valid
        EXPECT_GLENUM_EQ(GL_WAIT_FAILED,
                         glClientWaitSync(reinterpret_cast<GLsync>(30), GL_SYNC_FLUSH_COMMANDS_BIT, 0));
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glWaitSync generates GL_INVALID_VALUE if flags is non-zero
        glWaitSync(sync, 1, GL_TIMEOUT_IGNORED);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glWaitSync generates GL_INVALID_VALUE if GLuint64 is not GL_TIMEOUT_IGNORED
        glWaitSync(sync, 0, 0);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glWaitSync generates GL_INVALID_VALUE if the sync object is not valid
        glWaitSync(reinterpret_cast<GLsync>(30), 0, GL_TIMEOUT_IGNORED);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // glGetSynciv generates GL_INVALID_VALUE if bufSize is less than zero, results should be
        // untouched
        GLsizei length = 20;
        GLint value    = 30;
        glGetSynciv(sync, GL_OBJECT_TYPE, -1, &length, &value);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
        EXPECT_EQ(20, length);
        EXPECT_EQ(30, value);
    
        // glGetSynciv generates GL_INVALID_VALUE if the sync object is not valid, results should be
        // untouched
        glGetSynciv(reinterpret_cast<GLsync>(30), GL_OBJECT_TYPE, 1, &length, &value);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
        EXPECT_EQ(20, length);
        EXPECT_EQ(30, value);
    }
    
    // Test usage of glGetSynciv
    TEST_P(FenceSyncTest, BasicQueries)
    {
        GLsizei length = 0;
        GLint value    = 0;
        GLsync sync    = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    
        glGetSynciv(sync, GL_SYNC_CONDITION, 1, &length, &value);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(GL_SYNC_GPU_COMMANDS_COMPLETE, value);
    
        glGetSynciv(sync, GL_OBJECT_TYPE, 1, &length, &value);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(GL_SYNC_FENCE, value);
    
        glGetSynciv(sync, GL_SYNC_FLAGS, 1, &length, &value);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(0, value);
    }
    
    // Test that basic usage works and doesn't generate errors or crash
    TEST_P(FenceSyncTest, BasicOperations)
    {
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
    
        GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    
        glClear(GL_COLOR_BUFFER_BIT);
        glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
        EXPECT_GL_NO_ERROR();
    
        GLsizei length         = 0;
        GLint value            = 0;
        unsigned int loopCount = 0;
    
        glFlush();
    
        // Use 'loopCount' to make sure the test doesn't get stuck in an infinite loop
        while (value != GL_SIGNALED && loopCount <= 1000000)
        {
            loopCount++;
    
            glGetSynciv(sync, GL_SYNC_STATUS, 1, &length, &value);
            ASSERT_GL_NO_ERROR();
        }
    
        ASSERT_GLENUM_EQ(GL_SIGNALED, value);
    
        for (size_t i = 0; i < 20; i++)
        {
            glClear(GL_COLOR_BUFFER_BIT);
            glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
            EXPECT_GL_NO_ERROR();
        }
    }
    
    // Test that multiple fences and draws can be issued
    TEST_P(FenceSyncTest, MultipleFenceDraw)
    {
        constexpr int kNumIterations = 10;
        constexpr int kNumDraws      = 5;
    
        // Create a texture/FBO to draw to
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture.get());
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
        ASSERT_GL_NO_ERROR();
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
    
        bool drawGreen = true;
        for (int numIterations = 0; numIterations < kNumIterations; ++numIterations)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            ASSERT_GL_NO_ERROR();
    
            for (int i = 0; i < kNumDraws; ++i)
            {
                GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
                ASSERT_GL_NO_ERROR();
    
                drawGreen      = !drawGreen;
                GLuint program = 0;
                if (drawGreen)
                {
                    program = greenProgram.get();
                }
                else
                {
                    program = redProgram.get();
                }
                drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.0f);
                ASSERT_GL_NO_ERROR();
    
                glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
                EXPECT_GL_NO_ERROR();
                glDeleteSync(sync);
                ASSERT_GL_NO_ERROR();
            }
    
            // Blit to the default FBO
            glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
            glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
            ASSERT_GL_NO_ERROR();
            swapBuffers();
    
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            GLColor color;
            if (drawGreen)
            {
                color = GLColor::green;
            }
            else
            {
                color = GLColor::red;
            }
            EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, color);
        }
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(FenceNVTest);
    ANGLE_INSTANTIATE_TEST_ES3(FenceSyncTest);