| // |
| // 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"); } |
| }; |
| |
| // 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(); |
| |
| 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)); |
| } |
| |
| ANGLE_INSTANTIATE_TEST(EGLSyncTest, |
| ES2_D3D9(), |
| ES2_D3D11(), |
| ES3_D3D11(), |
| ES2_OPENGL(), |
| ES3_OPENGL(), |
| ES2_OPENGLES(), |
| ES3_OPENGLES(), |
| ES2_VULKAN(), |
| ES3_VULKAN()); |