Edit

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

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2019-01-18 14:27:43
    Hash : 82fddcb1
    Message : Vulkan: Implement GLsync and EGLSync fence syncs That is required in GLES 3 for GLsync and EGL_KHR_fence_sync and EGL_KHR_wait_sync (or EGL 1.5) for EGLSync. The two constructs (GLsync and EGLSync) have similar semantics and share the implementation on the Vulkan backend. The implementation of a fence sync object is achieved through the combined use of a vkEvent and the implicit vkFence inserted at the end of every submission. Imagine the following command buffer: glDraw : Draw glCreateSync: Set Event <-- insertion of fence sync glDraw : Draw : Signal Fence <-- implicit fence at the end of submission glFlush : Submit Assume the serial S is associated to this submission. The following hold: - If event is set, the fence sync is signaled - If S is already finished, the fence sync is signaled - If client is waiting on the sync and S is not yet flushed, there will be a deadlock (unless multi-threaded and another thread performs the flush). The event is used to implement server waits (glWaitSync), as vkEvent is the only entity the GPU can signal and wait on within the command buffer. The wait is inserted in the command graph without incurring a flush, i.e. the wait can be within the same command buffer as event set. The event however does not support CPU waits (glClientWaitSync). vkFence is the only entity the CPU can wait on. For client wait therefore, the following algorithm is used: - If the event is already set, there's no wait -> already signaled - If timeout is zero, there's no wait -> timeout expired - If S is not flushed, flush it to ensure forward progress. - Wait until S is finished -> condition satisfied / timeout expired. Bug: angleproject:2466 Change-Id: I678995a6139dd9533fa8ad361a3d292b202c52a4 Reviewed-on: https://chromium-review.googlesource.com/c/1422552 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • 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"
    
    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
    {
      protected:
        FenceSyncTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            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(!extensionEnabled("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(!extensionEnabled("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(!extensionEnabled("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 tis 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();
        }
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST(FenceNVTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES3_D3D11(),
                           ES2_OPENGL(),
                           ES3_OPENGL(),
                           ES2_OPENGLES(),
                           ES3_OPENGLES(),
                           ES2_VULKAN());
    ANGLE_INSTANTIATE_TEST(FenceSyncTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(), ES3_VULKAN());