// // 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 #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"); } }; // 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(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(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(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(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(); EGLint value = 0; unsigned int loopCount = 0; // Use 'loopCount' to make sure the test doesn't get stuck in an infinite loop while (value != EGL_SIGNALED_KHR && loopCount <= 1000000) { loopCount++; EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, sync, EGL_SYNC_STATUS_KHR, &value)); } ASSERT_EQ(value, EGL_SIGNALED_KHR); 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); } ANGLE_INSTANTIATE_TEST(EGLSyncTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL(), ES2_OPENGLES(), ES3_OPENGLES(), ES2_VULKAN(), ES3_VULKAN());