Edit

kc3-lang/angle/src/tests/egl_tests/EGLSyncTest.cpp

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2020-11-20 13:07:53
    Hash : 909ea88b
    Message : Reland "Vulkan: Ignore glFlush to reduce vkQueueSubmits in Asphalt 9" This is a reland of 5cf7472dd161bbda329dfc5e4e65bb6ce0c06fbd The ShareGroupVk::mResourceUseLists was not being cleared each call to RendererVk::submitFrame(), so it was growing indefinitely. Each vk::ResourceUseList within it was cleared, so it was holding an essentially "infinite" list of empty lists, but that caused the loop in RendererVk::submitFrame() to take more and more time until the tests timed out. The fix is to do 'resourceUseLists.clear()' once the loop to release all resources has completed, like releaseResourceUsesAndUpdateSerials() does for each individual list. Additionally, ASSERTs are added to guarantee that the lists are empty when the ContextVk and ShareGroupVk are destroyed. Original change's description: > Vulkan: Ignore glFlush to reduce vkQueueSubmits in Asphalt 9 > > Multithreaded apps can use the following pattern: > > glDrawElements() > glFenceSync() > glFlush() > glWaitSync() > > This currently results in a vkQueueSubmit for every glFlush() to ensure > that the work has landed in the command queue in the correct order. > However, ANGLE can instead avoid the vkQueueSubmit during the glFlush() > in this situation by instead flushing the ContextVk's commands and > ending the render pass to ensure the commands are submitted in the > correct order to the renderer. This improves performance for Asphalt 9 > by reducing frame times from 150-200msec to 35-55msec. > > Specifically, ANGLE will call flushCommandsAndEndRenderPass() when > there is a sync object pending a flush or if the ContextVk is currently > shared. > > Additionally, on all devices except Qualcomm, ANGLE can ignore all other > glFlush() calls entirely and return immediately. For Qualcomm devices, > ANGLE is still required to perform a full flush (resulting in a > vkQueueSubmit), since ignoring the glFlush() reduces the Manhattan 3.0 > offscreen score by ~3%. > > Bug: angleproject:5306 > Bug: angleproject:5425 > Change-Id: I9d747caf5bf306166be0fec630a78caf41208c27 > Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2552718 > Commit-Queue: Tim Van Patten <timvp@google.com> > Reviewed-by: Charlie Lao <cclao@google.com> > Reviewed-by: Jamie Madill <jmadill@chromium.org> Bug: angleproject:5306 Bug: angleproject:5425 Bug: angleproject:5470 Change-Id: I14ee424d032f22e5285d67accbec078ad1955dd0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2595811 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: Charlie Lao <cclao@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/tests/egl_tests/EGLSyncTest.cpp
  • //
    // Copyright 2019 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.
    //
    // EGLSyncTest.cpp:
    //   Tests of EGL_KHR_fence_sync and EGL_KHR_wait_sync extensions.
    
    #include <gtest/gtest.h>
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/angle_test_configs.h"
    #include "util/EGLWindow.h"
    
    using namespace angle;
    
    class EGLSyncTest : public ANGLETest
    {
      protected:
        bool hasFenceSyncExtension() const
        {
            return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_fence_sync");
        }
        bool hasWaitSyncExtension() const
        {
            return hasFenceSyncExtension() &&
                   IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_wait_sync");
        }
        bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); }
    
        bool hasAndroidNativeFenceSyncExtension() const
        {
            return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
                                                "EGL_ANDROID_native_fence_sync");
        }
    };
    
    // Test error cases for all EGL_KHR_fence_sync functions
    TEST_P(EGLSyncTest, FenceSyncErrors)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
    
        EGLDisplay display = getEGLWindow()->getDisplay();
    
        // If the client API doesn't have the necessary extension, test that sync creation fails and
        // ignore the rest of the tests.
        if (!hasGLSyncExtension())
        {
            EXPECT_EQ(EGL_NO_SYNC_KHR, eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr));
            EXPECT_EGL_ERROR(EGL_BAD_MATCH);
        }
    
        ANGLE_SKIP_TEST_IF(!hasGLSyncExtension());
    
        EGLContext context     = eglGetCurrentContext();
        EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
        EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
    
        EXPECT_NE(context, EGL_NO_CONTEXT);
        EXPECT_NE(drawSurface, EGL_NO_SURFACE);
        EXPECT_NE(readSurface, EGL_NO_SURFACE);
    
        // CreateSync with no attribute shouldn't cause an error
        EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(sync, EGL_NO_SYNC_KHR);
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync));
    
        // CreateSync with empty attribute shouldn't cause an error
        const EGLint emptyAttributes[] = {EGL_NONE};
        sync                           = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, emptyAttributes);
        EXPECT_NE(sync, EGL_NO_SYNC_KHR);
    
        // DestroySync generates BAD_PARAMETER if the sync is not valid
        EXPECT_EGL_FALSE(eglDestroySyncKHR(display, reinterpret_cast<EGLSyncKHR>(20)));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
    
        // CreateSync generates BAD_DISPLAY if display is not valid
        EXPECT_EQ(EGL_NO_SYNC_KHR, eglCreateSyncKHR(EGL_NO_DISPLAY, EGL_SYNC_FENCE_KHR, nullptr));
        EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
    
        // CreateSync generates BAD_ATTRIBUTE if attribute is neither nullptr nor empty.
        const EGLint nonEmptyAttributes[] = {
            EGL_CL_EVENT_HANDLE,
            0,
            EGL_NONE,
        };
        EXPECT_EQ(EGL_NO_SYNC_KHR, eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nonEmptyAttributes));
        EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
    
        // CreateSync generates BAD_ATTRIBUTE if type is not valid
        EXPECT_EQ(EGL_NO_SYNC_KHR, eglCreateSyncKHR(display, 0, nullptr));
        EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
    
        // CreateSync generates BAD_MATCH if no context is current
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EXPECT_EQ(EGL_NO_SYNC_KHR, eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr));
        EXPECT_EGL_ERROR(EGL_BAD_MATCH);
        eglMakeCurrent(display, drawSurface, readSurface, context);
    
        // ClientWaitSync generates EGL_BAD_PARAMETER if the sync object is not valid
        EXPECT_EGL_FALSE(eglClientWaitSyncKHR(display, reinterpret_cast<EGLSyncKHR>(30), 0, 0));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
    
        // GetSyncAttrib generates EGL_BAD_PARAMETER if the sync object is not valid, and value is not
        // modified
        constexpr EGLint kSentinelAttribValue = 123456789;
        EGLint attribValue                    = kSentinelAttribValue;
        EXPECT_EGL_FALSE(eglGetSyncAttribKHR(display, reinterpret_cast<EGLSyncKHR>(40),
                                             EGL_SYNC_TYPE_KHR, &attribValue));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
        EXPECT_EQ(attribValue, kSentinelAttribValue);
    
        // GetSyncAttrib generates EGL_BAD_ATTRIBUTE if the attribute is not valid, and value is not
        // modified
        EXPECT_EGL_FALSE(eglGetSyncAttribKHR(display, sync, EGL_CL_EVENT_HANDLE, &attribValue));
        EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
        EXPECT_EQ(attribValue, kSentinelAttribValue);
    
        // GetSyncAttrib generates EGL_BAD_MATCH if the attribute is valid for sync, but not the
        // particular sync type. We don't have such a case at the moment.
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync));
    }
    
    // Test error cases for all EGL_KHR_wait_sync functions
    TEST_P(EGLSyncTest, WaitSyncErrors)
    {
        // The client API that shows support for eglWaitSyncKHR is the same as the one required for
        // eglCreateSyncKHR.  As such, there is no way to create a sync and not be able to wait on it.
        // This would have created an EGL_BAD_MATCH error.
        ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
    
        EGLDisplay display     = getEGLWindow()->getDisplay();
        EGLContext context     = eglGetCurrentContext();
        EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
        EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
    
        EXPECT_NE(context, EGL_NO_CONTEXT);
        EXPECT_NE(drawSurface, EGL_NO_SURFACE);
        EXPECT_NE(readSurface, EGL_NO_SURFACE);
    
        EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(sync, EGL_NO_SYNC_KHR);
    
        // WaitSync generates BAD_MATCH if no context is current
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EXPECT_EGL_FALSE(eglWaitSyncKHR(display, sync, 0));
        EXPECT_EGL_ERROR(EGL_BAD_MATCH);
        eglMakeCurrent(display, drawSurface, readSurface, context);
    
        // WaitSync generates BAD_PARAMETER if the sync is not valid
        EXPECT_EGL_FALSE(eglWaitSyncKHR(display, reinterpret_cast<EGLSyncKHR>(20), 0));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
    
        // WaitSync generates BAD_PARAMETER if flags is non-zero
        EXPECT_EGL_FALSE(eglWaitSyncKHR(display, sync, 1));
        EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync));
    }
    
    // Test usage of eglGetSyncAttribKHR
    TEST_P(EGLSyncTest, GetSyncAttrib)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
    
        EGLDisplay display = getEGLWindow()->getDisplay();
    
        EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(sync, EGL_NO_SYNC_KHR);
    
        // Fence sync attributes are:
        //
        // EGL_SYNC_TYPE_KHR: EGL_SYNC_FENCE_KHR
        // EGL_SYNC_STATUS_KHR: EGL_UNSIGNALED_KHR or EGL_SIGNALED_KHR
        // EGL_SYNC_CONDITION_KHR: EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR
    
        constexpr EGLint kSentinelAttribValue = 123456789;
        EGLint attribValue                    = kSentinelAttribValue;
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, sync, EGL_SYNC_TYPE_KHR, &attribValue));
        EXPECT_EQ(attribValue, EGL_SYNC_FENCE_KHR);
    
        attribValue = kSentinelAttribValue;
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, sync, EGL_SYNC_CONDITION_KHR, &attribValue));
        EXPECT_EQ(attribValue, EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR);
    
        attribValue = kSentinelAttribValue;
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, sync, EGL_SYNC_STATUS_KHR, &attribValue));
    
        // Hack around EXPECT_* not having an "either this or that" variant:
        if (attribValue != EGL_SIGNALED_KHR)
        {
            EXPECT_EQ(attribValue, EGL_UNSIGNALED_KHR);
        }
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync));
    }
    
    // Test that basic usage works and doesn't generate errors or crash
    TEST_P(EGLSyncTest, BasicOperations)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
    
        EGLDisplay display = getEGLWindow()->getDisplay();
    
        EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(sync, EGL_NO_SYNC_KHR);
    
        glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
    
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_EGL_TRUE(eglWaitSyncKHR(display, sync, 0));
    
        glFlush();
    
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Don't wait forever to make sure the test terminates
        constexpr GLuint64 kTimeout = 1'000'000'000;  // 1 second
        EGLint value                = 0;
        ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR,
                  eglClientWaitSyncKHR(display, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, kTimeout));
    
        for (size_t i = 0; i < 20; i++)
        {
            glClear(GL_COLOR_BUFFER_BIT);
            EXPECT_EQ(
                EGL_CONDITION_SATISFIED_KHR,
                eglClientWaitSyncKHR(display, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR));
            EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, sync, EGL_SYNC_STATUS_KHR, &value));
            EXPECT_EQ(value, EGL_SIGNALED_KHR);
        }
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync));
    }
    
    // Test eglWaitNative api
    TEST_P(EGLSyncTest, WaitNative)
    {
        // Clear to red color
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_EGL_TRUE(eglWaitNative(EGL_CORE_NATIVE_ENGINE));
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
    }
    
    // Verify eglDupNativeFence for EGL_ANDROID_native_fence_sync
    TEST_P(EGLSyncTest, AndroidNativeFence_DupNativeFenceFD)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
    
        EGLDisplay display = getEGLWindow()->getDisplay();
    
        // We can ClientWait on this
        EGLSyncKHR syncWithGeneratedFD =
            eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
        EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
    
        int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
        EXPECT_EGL_SUCCESS();
    
        // Clean up created objects.
        if (fd != EGL_NO_NATIVE_FENCE_FD_ANDROID)
        {
            close(fd);
        }
    
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
    }
    
    // Verify CreateSync and ClientWait for EGL_ANDROID_native_fence_sync
    TEST_P(EGLSyncTest, AndroidNativeFence_ClientWait)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
    
        EGLint value       = 0;
        EGLDisplay display = getEGLWindow()->getDisplay();
    
        // We can ClientWait on this
        EGLSyncKHR syncWithGeneratedFD =
            eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
        EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
    
        // Create work to do
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
    
        // Wait for draw to complete
        EXPECT_EQ(EGL_CONDITION_SATISFIED,
                  eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                       1000000000));
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
        EXPECT_EQ(value, EGL_SIGNALED_KHR);
    
        // Clean up created objects.
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
    }
    
    // Verify WaitSync with EGL_ANDROID_native_fence_sync
    // Simulate passing FDs across processes by passing across Contexts.
    TEST_P(EGLSyncTest, AndroidNativeFence_WaitSync)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
    
        EGLint value       = 0;
        EGLDisplay display = getEGLWindow()->getDisplay();
        EGLSurface surface = getEGLWindow()->getSurface();
    
        /*- First Context ------------------------*/
    
        // We can ClientWait on this
        EGLSyncKHR syncWithGeneratedFD =
            eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
        EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
    
        int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
        EXPECT_EGL_SUCCESS();  // Can return -1 (when signaled) or valid FD.
    
        // Create work to do
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
    
        /*- Second Context ------------------------*/
        if (fd > EGL_NO_NATIVE_FENCE_FD_ANDROID)
        {
            EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    
            EGLContext context2 = getEGLWindow()->createContext(EGL_NO_CONTEXT);
            EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
    
            // We can eglWaitSync on this - import FD from first sync.
            EGLint syncAttribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, (EGLint)fd, EGL_NONE};
            EGLSyncKHR syncWithDupFD =
                eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, syncAttribs);
            EXPECT_NE(syncWithDupFD, EGL_NO_SYNC_KHR);
    
            // Second draw waits for first to complete. May already be signaled - ignore error.
            if (eglWaitSyncKHR(display, syncWithDupFD, 0) == EGL_TRUE)
            {
                // Create work to do
                glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                glFlush();
            }
    
            // Wait for second draw to complete
            EXPECT_EQ(EGL_CONDITION_SATISFIED,
                      eglClientWaitSyncKHR(display, syncWithDupFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                           1000000000));
            EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithDupFD, EGL_SYNC_STATUS_KHR, &value));
            EXPECT_EQ(value, EGL_SIGNALED_KHR);
    
            // Reset to default context and surface.
            EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
            EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, getEGLWindow()->getContext()));
    
            // Clean up created objects.
            EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithDupFD));
            EXPECT_EGL_TRUE(eglDestroyContext(display, context2));
        }
    
        // Wait for first draw to complete
        EXPECT_EQ(EGL_CONDITION_SATISFIED,
                  eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                       1000000000));
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
        EXPECT_EQ(value, EGL_SIGNALED_KHR);
    
        // Clean up created objects.
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
    }
    
    // Verify EGL_ANDROID_native_fence_sync
    // Simulate passing FDs across processes by passing across Contexts.
    TEST_P(EGLSyncTest, AndroidNativeFence_withFences)
    {
        ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
        ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
    
        EGLint value       = 0;
        EGLDisplay display = getEGLWindow()->getDisplay();
        EGLSurface surface = getEGLWindow()->getSurface();
    
        /*- First Context ------------------------*/
    
        // Extra fence syncs to ensure that Fence and Android Native fences work together
        EGLSyncKHR syncFence1 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(syncFence1, EGL_NO_SYNC_KHR);
    
        // We can ClientWait on this
        EGLSyncKHR syncWithGeneratedFD =
            eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
        EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
    
        int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
        EXPECT_EGL_SUCCESS();  // Can return -1 (when signaled) or valid FD.
    
        EGLSyncKHR syncFence2 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
        EXPECT_NE(syncFence2, EGL_NO_SYNC_KHR);
    
        // Create work to do
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
    
        /*- Second Context ------------------------*/
        if (fd > EGL_NO_NATIVE_FENCE_FD_ANDROID)
        {
            EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    
            EGLContext context2 = getEGLWindow()->createContext(EGL_NO_CONTEXT);
            EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
    
            // check that Fence and Android fences work together
            EGLSyncKHR syncFence3 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
            EXPECT_NE(syncFence3, EGL_NO_SYNC_KHR);
    
            // We can eglWaitSync on this
            EGLint syncAttribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, (EGLint)fd, EGL_NONE};
            EGLSyncKHR syncWithDupFD =
                eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, syncAttribs);
            EXPECT_NE(syncWithDupFD, EGL_NO_SYNC_KHR);
    
            EGLSyncKHR syncFence4 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
            EXPECT_NE(syncFence4, EGL_NO_SYNC_KHR);
    
            // Second draw waits for first to complete. May already be signaled - ignore error.
            if (eglWaitSyncKHR(display, syncWithDupFD, 0) == EGL_TRUE)
            {
                // Create work to do
                glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                glFlush();
            }
    
            // Wait for second draw to complete
            EXPECT_EQ(EGL_CONDITION_SATISFIED,
                      eglClientWaitSyncKHR(display, syncWithDupFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                           1000000000));
            EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithDupFD, EGL_SYNC_STATUS_KHR, &value));
            EXPECT_EQ(value, EGL_SIGNALED_KHR);
    
            // Reset to default context and surface.
            EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
            EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, getEGLWindow()->getContext()));
    
            // Clean up created objects.
            EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence3));
            EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence4));
            EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithDupFD));
            EXPECT_EGL_TRUE(eglDestroyContext(display, context2));
        }
    
        // Wait for first draw to complete
        EXPECT_EQ(EGL_CONDITION_SATISFIED,
                  eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                       1000000000));
        EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
        EXPECT_EQ(value, EGL_SIGNALED_KHR);
    
        // Clean up created objects.
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence1));
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence2));
        EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
    }
    
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(EGLSyncTest);