Edit

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

Branch :

  • Show log

    Commit

  • Author : Mohan Maiya
    Date : 2021-04-26 15:54:25
    Hash : 904ac340
    Message : Bug fix for WaitClient, WaitGL and WaitNative It is valid to call eglWaitClient, eglWaitGL and eglWaitNative when there is no active context in the current thread. Update code to account for that possibility. Bug: angleproject:5898 Test: EGLSyncTest.WaitClient* EGLSyncTest.WaitGL* EGLSyncTest.WaitNative* Change-Id: I81f05a27f1f641cf8986e2e6c05c183ea8471889 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2849587 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>

  • 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 eglWaitClient api
    TEST_P(EGLSyncTest, WaitClient)
    {
        // Clear to red color
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_EGL_TRUE(eglWaitClient());
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
    
        EGLDisplay display = getEGLWindow()->getDisplay();
        EGLContext context = getEGLWindow()->getContext();
        EGLSurface surface = getEGLWindow()->getSurface();
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EXPECT_EGL_TRUE(eglWaitClient());
        eglMakeCurrent(display, surface, surface, context);
    }
    
    // Test eglWaitGL api
    TEST_P(EGLSyncTest, WaitGL)
    {
        // Clear to red color
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_EGL_TRUE(eglWaitGL());
        EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
    
        EGLDisplay display = getEGLWindow()->getDisplay();
        EGLContext context = getEGLWindow()->getContext();
        EGLSurface surface = getEGLWindow()->getSurface();
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EXPECT_EGL_TRUE(eglWaitGL());
        eglMakeCurrent(display, surface, surface, context);
    }
    
    // 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);
    
        EGLDisplay display = getEGLWindow()->getDisplay();
        EGLContext context = getEGLWindow()->getContext();
        EGLSurface surface = getEGLWindow()->getSurface();
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EXPECT_EGL_TRUE(eglWaitNative(EGL_CORE_NATIVE_ENGINE));
        eglMakeCurrent(display, surface, surface, context);
    }
    
    // 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);