From ce263437ca353e2b600ee91181478185bed02980 Mon Sep 17 00:00:00 2001 From: Mohan Maiya Date: Mon, 11 Sep 2023 12:25:28 -0700 Subject: [PATCH] Vulkan: Perform CPU wait in clientWait outside the global lock Leverage UnlockedTailCall and move the CPU side wait during a clientWait outside of the global mutex lock. Bug: angleproject:8340 Tests: FenceSyncTest.BasicOperations* Tests: EGLSyncTest.EglClientWaitSync* Change-Id: I8c05e62e74cc64d38bf8797d28faaf49135e71fc Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4851649 Reviewed-by: Shahbaz Youssefi Commit-Queue: mohan maiya Reviewed-by: Charlie Lao --- .../GL_EGL_entry_points.json | 8 +- scripts/generate_entry_points.py | 8 +- src/libANGLE/renderer/vulkan/SyncVk.cpp | 221 ++++++++++++------ src/libANGLE/renderer/vulkan/SyncVk.h | 30 ++- src/libGLESv2/egl_ext_stubs.cpp | 27 ++- src/libGLESv2/egl_stubs.cpp | 23 +- src/libGLESv2/entry_points_egl_autogen.cpp | 2 +- .../entry_points_egl_ext_autogen.cpp | 2 +- .../entry_points_gles_3_0_autogen.cpp | 2 +- src/tests/egl_tests/EGLSyncTest.cpp | 51 ++++ src/tests/gl_tests/FenceSyncTests.cpp | 17 +- 11 files changed, 309 insertions(+), 82 deletions(-) diff --git a/scripts/code_generation_hashes/GL_EGL_entry_points.json b/scripts/code_generation_hashes/GL_EGL_entry_points.json index 5d1cb3dec..10acd8b72 100644 --- a/scripts/code_generation_hashes/GL_EGL_entry_points.json +++ b/scripts/code_generation_hashes/GL_EGL_entry_points.json @@ -6,7 +6,7 @@ "scripts/entry_point_packed_gl_enums.json": "1c6b036918aabb9822a638fbf33f87f4", "scripts/generate_entry_points.py": - "02bccd68542886dd17a751f98ad7d3eb", + "64ef644064c7fcbbf89458dc53ca5c94", "scripts/gl_angle_ext.xml": "3d6d9529c6d0da797652c366ddaa9087", "scripts/registry_xml.py": @@ -130,11 +130,11 @@ "src/libGLESv2/entry_points_cl_autogen.h": "dde2f94c3004874a7da995dae69da811", "src/libGLESv2/entry_points_egl_autogen.cpp": - "ec731a77790f0a557702b395d03af432", + "66320459d4a7d1df208cf9fc15779b80", "src/libGLESv2/entry_points_egl_autogen.h": "3bc7a8df9deadd7cfd615d0cfad0c6a8", "src/libGLESv2/entry_points_egl_ext_autogen.cpp": - "7d86e5a581ef3062202552542afe6755", + "23f30e2c3e1cf0196e838cfecd8ac279", "src/libGLESv2/entry_points_egl_ext_autogen.h": "2d005f4cb16dcdd61e08cfec97a12f86", "src/libGLESv2/entry_points_gl_1_autogen.cpp": @@ -162,7 +162,7 @@ "src/libGLESv2/entry_points_gles_2_0_autogen.h": "691c60c2dfed9beca68aa1f32aa2c71b", "src/libGLESv2/entry_points_gles_3_0_autogen.cpp": - "3acd48e02ac5bb362c031b6f0b2c4b8b", + "5d44e91d02b54ef46169e31f7631aad5", "src/libGLESv2/entry_points_gles_3_0_autogen.h": "4ac2582759cdc6a30f78f83ab684d555", "src/libGLESv2/entry_points_gles_3_1_autogen.cpp": diff --git a/scripts/generate_entry_points.py b/scripts/generate_entry_points.py index 033b366f9..c21a3a9e3 100755 --- a/scripts/generate_entry_points.py +++ b/scripts/generate_entry_points.py @@ -416,7 +416,7 @@ TEMPLATE_GLES_ENTRY_POINT_WITH_RETURN = """\ {constext_lost_error_generator} returnValue = GetDefaultReturnValue(); }} - ASSERT(!egl::Display::GetCurrentThreadUnlockedTailCall()->any()); + {epilog} return returnValue; }} """ @@ -3068,6 +3068,9 @@ def get_unlocked_tail_call(api, cmd_name): # - eglSwapBuffers, eglSwapBuffersWithDamageKHR and # eglSwapBuffersWithFrameTokenANGLE -> May throttle the CPU in tail call # + # - eglClientWaitSyncKHR, eglClientWaitSync, glClientWaitSync -> May wait on + # fence in tail call + # if cmd_name in [ 'eglDestroySurface', 'eglMakeCurrent', 'eglReleaseThread', 'eglCreateWindowSurface', 'eglCreatePlatformWindowSurface', 'eglCreatePlatformWindowSurfaceEXT', @@ -3076,6 +3079,9 @@ def get_unlocked_tail_call(api, cmd_name): ]: return 'egl::Display::GetCurrentThreadUnlockedTailCall()->run(nullptr);' + if cmd_name in ['eglClientWaitSyncKHR', 'eglClientWaitSync', 'glClientWaitSync']: + return 'egl::Display::GetCurrentThreadUnlockedTailCall()->run(&returnValue);' + # Otherwise assert that no tail calls where generated return 'ASSERT(!egl::Display::GetCurrentThreadUnlockedTailCall()->any());' diff --git a/src/libANGLE/renderer/vulkan/SyncVk.cpp b/src/libANGLE/renderer/vulkan/SyncVk.cpp index db4b69dac..822fc1f6d 100644 --- a/src/libANGLE/renderer/vulkan/SyncVk.cpp +++ b/src/libANGLE/renderer/vulkan/SyncVk.cpp @@ -68,6 +68,64 @@ VkResult SyncWaitFd(int fd, uint64_t timeoutNs, VkResult timeoutResult = VK_TIME #endif } +// Map VkResult to GLenum +void MapVkResultToGlenum(VkResult vkResult, angle::Result angleResult, void *outResult) +{ + GLenum *glEnumOut = static_cast(outResult); + ASSERT(glEnumOut); + + if (angleResult != angle::Result::Continue) + { + *glEnumOut = GL_WAIT_FAILED; + return; + } + + switch (vkResult) + { + case VK_EVENT_SET: + *glEnumOut = GL_ALREADY_SIGNALED; + break; + case VK_SUCCESS: + *glEnumOut = GL_CONDITION_SATISFIED; + break; + case VK_TIMEOUT: + *glEnumOut = GL_TIMEOUT_EXPIRED; + break; + default: + *glEnumOut = GL_WAIT_FAILED; + break; + } +} + +// Map VkResult to EGLint +void MapVkResultToEglint(VkResult result, angle::Result angleResult, void *outResult) +{ + EGLint *eglIntOut = static_cast(outResult); + ASSERT(eglIntOut); + + if (angleResult != angle::Result::Continue) + { + *eglIntOut = EGL_FALSE; + return; + } + + switch (result) + { + case VK_EVENT_SET: + // fall through. EGL doesn't differentiate between event being already set, or set + // before timeout. + case VK_SUCCESS: + *eglIntOut = EGL_CONDITION_SATISFIED_KHR; + break; + case VK_TIMEOUT: + *eglIntOut = EGL_TIMEOUT_EXPIRED_KHR; + break; + default: + *eglIntOut = EGL_FALSE; + break; + } +} + } // anonymous namespace namespace rx @@ -86,27 +144,25 @@ angle::Result SyncHelper::initialize(ContextVk *contextVk, bool isEGLSyncObject) return contextVk->onSyncObjectInit(this, isEGLSyncObject); } -angle::Result SyncHelper::clientWait(Context *context, - ContextVk *contextVk, - bool flushCommands, - uint64_t timeout, - VkResult *outResult) +angle::Result SyncHelper::prepareForClientWait(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + VkResult *resultOut) { - RendererVk *renderer = context->getRenderer(); - // If the event is already set, don't wait bool alreadySignaled = false; ANGLE_TRY(getStatus(context, contextVk, &alreadySignaled)); if (alreadySignaled) { - *outResult = VK_EVENT_SET; + *resultOut = VK_EVENT_SET; return angle::Result::Continue; } // If timeout is zero, there's no need to wait, so return timeout already. if (timeout == 0) { - *outResult = VK_TIMEOUT; + *resultOut = VK_TIMEOUT; return angle::Result::Continue; } @@ -116,19 +172,74 @@ angle::Result SyncHelper::clientWait(Context *context, ANGLE_TRY(contextVk->flushCommandsAndEndRenderPassIfDeferredSyncInit( RenderPassClosureReason::SyncObjectClientWait)); } + // Submit commands if it was deferred on the context that issued the sync object ANGLE_TRY(submitSyncIfDeferred(contextVk, RenderPassClosureReason::SyncObjectClientWait)); - VkResult status = VK_SUCCESS; - ANGLE_TRY(renderer->waitForResourceUseToFinishWithUserTimeout(context, mUse, timeout, &status)); + *resultOut = VK_INCOMPLETE; + return angle::Result::Continue; +} - // Check for errors, but don't consider timeout as such. - if (status != VK_TIMEOUT) +angle::Result SyncHelper::clientWait(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + VkResult *resultOut) +{ + ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelper clientWait"); + ANGLE_TRY(prepareForClientWait(context, contextVk, flushCommands, timeout, resultOut)); + + if (*resultOut == VK_INCOMPLETE) { - ANGLE_VK_TRY(context, status); + ANGLE_TRY(contextVk->getRenderer()->waitForResourceUseToFinishWithUserTimeout( + context, mUse, timeout, resultOut)); + + // Check for errors, but don't consider timeout as such. + if (*resultOut != VK_TIMEOUT) + { + ANGLE_VK_TRY(context, *resultOut); + } + } + + return angle::Result::Continue; +} + +angle::Result SyncHelper::clientWaitUnlocked(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + void *resultOut, + MapVkResultToApiType mappingFunction) +{ + ANGLE_TRACE_EVENT0("gpu.angle", "SyncHelper clientWaitUnlocked"); + VkResult status = VK_INCOMPLETE; + ANGLE_TRY(prepareForClientWait(context, contextVk, flushCommands, timeout, &status)); + + if (status == VK_INCOMPLETE) + { + RendererVk *renderer = context->getRenderer(); + + // If we need to perform a CPU wait don't set the resultOut parameter passed into the + // method, instead set the parameter passed into the unlocked tail call. + auto clientWaitUnlocked = [renderer, context, mappingFunction, use = mUse, + timeout](void *resultOut) { + ANGLE_TRACE_EVENT0("gpu.angle", "UnlockedTailCall clientWait"); + ASSERT(resultOut); + + VkResult status = VK_INCOMPLETE; + angle::Result angleResult = + renderer->waitForResourceUseToFinishWithUserTimeout(context, use, timeout, &status); + mappingFunction(status, angleResult, resultOut); + }; + + egl::Display::GetCurrentThreadUnlockedTailCall()->add(clientWaitUnlocked); + return angle::Result::Continue; + } + else + { + mappingFunction(status, angle::Result::Continue, resultOut); } - *outResult = status; return angle::Result::Continue; } @@ -343,7 +454,7 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context, ContextVk *contextVk, bool flushCommands, uint64_t timeout, - VkResult *outResult) + VkResult *resultOut) { RendererVk *renderer = context->getRenderer(); @@ -352,14 +463,14 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context, ANGLE_TRY(getStatus(context, contextVk, &alreadySignaled)); if (alreadySignaled) { - *outResult = VK_SUCCESS; + *resultOut = VK_SUCCESS; return angle::Result::Continue; } // If timeout is zero, there's no need to wait, so return timeout already. if (timeout == 0) { - *outResult = VK_TIMEOUT; + *resultOut = VK_TIMEOUT; return angle::Result::Continue; } @@ -369,16 +480,31 @@ angle::Result SyncHelperNativeFence::clientWait(Context *context, contextVk->flushImpl(nullptr, nullptr, RenderPassClosureReason::SyncObjectClientWait)); } - VkResult status = mExternalFence->wait(renderer->getDevice(), timeout); - if (status != VK_TIMEOUT) + *resultOut = mExternalFence->wait(renderer->getDevice(), timeout); + if (*resultOut != VK_TIMEOUT) { - ANGLE_VK_TRY(contextVk, status); + ANGLE_VK_TRY(contextVk, *resultOut); } - *outResult = status; return angle::Result::Continue; } +angle::Result SyncHelperNativeFence::clientWaitUnlocked(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + void *resultOut, + MapVkResultToApiType mappingFunction) +{ + // Unlocked clientWait is not supported for SyncHelperNativeFence, yet. + // For now call into clientWait(...) + VkResult status = VK_INCOMPLETE; + angle::Result angleResult = clientWait(context, contextVk, flushCommands, timeout, &status); + + mappingFunction(status, angleResult, resultOut); + return angleResult; +} + angle::Result SyncHelperNativeFence::serverWait(ContextVk *contextVk) { RendererVk *renderer = contextVk->getRenderer(); @@ -466,30 +592,10 @@ angle::Result SyncVk::clientWait(const gl::Context *context, ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0); bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0; - VkResult result; - ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast(timeout), - &result)); - - switch (result) - { - case VK_EVENT_SET: - *outResult = GL_ALREADY_SIGNALED; - return angle::Result::Continue; - - case VK_SUCCESS: - *outResult = GL_CONDITION_SATISFIED; - return angle::Result::Continue; - - case VK_TIMEOUT: - *outResult = GL_TIMEOUT_EXPIRED; - return angle::Result::Incomplete; - - default: - UNREACHABLE(); - *outResult = GL_WAIT_FAILED; - return angle::Result::Stop; - } + return mSyncHelper.clientWaitUnlocked(contextVk, contextVk, flush, + static_cast(timeout), outResult, + MapVkResultToGlenum); } angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout) @@ -569,33 +675,16 @@ egl::Error EGLSyncVk::clientWait(const egl::Display *display, ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0); bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0; - VkResult result; ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr; - if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush, - static_cast(timeout), &result) == angle::Result::Stop) + if (mSyncHelper->clientWaitUnlocked(vk::GetImpl(display), contextVk, flush, + static_cast(timeout), outResult, + MapVkResultToEglint) == angle::Result::Stop) { return egl::Error(EGL_BAD_ALLOC); } - switch (result) - { - case VK_EVENT_SET: - // fall through. EGL doesn't differentiate between event being already set, or set - // before timeout. - case VK_SUCCESS: - *outResult = EGL_CONDITION_SATISFIED_KHR; - return egl::NoError(); - - case VK_TIMEOUT: - *outResult = EGL_TIMEOUT_EXPIRED_KHR; - return egl::NoError(); - - default: - UNREACHABLE(); - *outResult = EGL_FALSE; - return egl::Error(EGL_BAD_ALLOC); - } + return egl::NoError(); } egl::Error EGLSyncVk::serverWait(const egl::Display *display, diff --git a/src/libANGLE/renderer/vulkan/SyncVk.h b/src/libANGLE/renderer/vulkan/SyncVk.h index 89b44f515..2ca2b6972 100644 --- a/src/libANGLE/renderer/vulkan/SyncVk.h +++ b/src/libANGLE/renderer/vulkan/SyncVk.h @@ -51,7 +51,8 @@ class ExternalFence final : angle::NonCopyable int mFenceFd; }; -using SharedExternalFence = std::shared_ptr; +using SharedExternalFence = std::shared_ptr; +using MapVkResultToApiType = std::function; class SyncHelperInterface : angle::NonCopyable { @@ -65,6 +66,12 @@ class SyncHelperInterface : angle::NonCopyable bool flushCommands, uint64_t timeout, VkResult *outResult) = 0; + virtual angle::Result clientWaitUnlocked(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + void *outResult, + MapVkResultToApiType mappingFunction) = 0; virtual angle::Result serverWait(ContextVk *contextVk) = 0; virtual angle::Result getStatus(Context *context, ContextVk *contextVk, bool *signaledOut) = 0; virtual angle::Result dupNativeFenceFD(Context *context, int *fdOut) const = 0; @@ -89,7 +96,13 @@ class SyncHelper final : public vk::Resource, public SyncHelperInterface ContextVk *contextVk, bool flushCommands, uint64_t timeout, - VkResult *outResult) override; + VkResult *resultOut) override; + angle::Result clientWaitUnlocked(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + void *resultOut, + MapVkResultToApiType mappingFunction) override; angle::Result serverWait(ContextVk *contextVk) override; angle::Result getStatus(Context *context, ContextVk *contextVk, bool *signaledOut) override; angle::Result dupNativeFenceFD(Context *context, int *fdOut) const override @@ -99,6 +112,11 @@ class SyncHelper final : public vk::Resource, public SyncHelperInterface private: angle::Result submitSyncIfDeferred(ContextVk *contextVk, RenderPassClosureReason reason); + angle::Result prepareForClientWait(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + VkResult *resultOut); }; // Implementation of sync types: EGLSync(EGL_SYNC_ANDROID_NATIVE_FENCE_ANDROID). @@ -118,7 +136,13 @@ class SyncHelperNativeFence final : public SyncHelperInterface ContextVk *contextVk, bool flushCommands, uint64_t timeout, - VkResult *outResult) override; + VkResult *resultOut) override; + angle::Result clientWaitUnlocked(Context *context, + ContextVk *contextVk, + bool flushCommands, + uint64_t timeout, + void *resultOut, + MapVkResultToApiType mappingFunction) override; angle::Result serverWait(ContextVk *contextVk) override; angle::Result getStatus(Context *context, ContextVk *contextVk, bool *signaledOut) override; angle::Result dupNativeFenceFD(Context *context, int *fdOut) const override; diff --git a/src/libGLESv2/egl_ext_stubs.cpp b/src/libGLESv2/egl_ext_stubs.cpp index 0deb5e0ca..0377021cd 100644 --- a/src/libGLESv2/egl_ext_stubs.cpp +++ b/src/libGLESv2/egl_ext_stubs.cpp @@ -28,16 +28,37 @@ EGLint ClientWaitSyncKHR(Thread *thread, EGLint flags, EGLTimeKHR timeout) { - ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglClientWaitSync", + ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglClientWaitSyncKHR", GetDisplayIfValid(display), EGL_FALSE); gl::Context *currentContext = thread->getContext(); EGLint syncStatus = EGL_FALSE; Sync *sync = display->getSync(syncID); ANGLE_EGL_TRY_RETURN(thread, sync->clientWait(display, currentContext, flags, timeout, &syncStatus), - "eglClientWaitSync", GetSyncIfValid(display, syncID), EGL_FALSE); + "eglClientWaitSyncKHR", GetSyncIfValid(display, syncID), EGL_FALSE); - thread->setSuccess(); + // When performing CPU wait through UnlockedTailCall we need to handle any error conditions + if (egl::Display::GetCurrentThreadUnlockedTailCall()->any()) + { + auto handleErrorStatus = [thread, display, syncID](void *result) { + EGLint *eglResult = static_cast(result); + ASSERT(eglResult); + if (*eglResult == EGL_FALSE) + { + thread->setError(egl::Error(EGL_BAD_ALLOC), "eglClientWaitSyncKHR", + GetSyncIfValid(display, syncID)); + } + else + { + thread->setSuccess(); + } + }; + egl::Display::GetCurrentThreadUnlockedTailCall()->add(handleErrorStatus); + } + else + { + thread->setSuccess(); + } return syncStatus; } diff --git a/src/libGLESv2/egl_stubs.cpp b/src/libGLESv2/egl_stubs.cpp index 9fb6e5b3e..b035a3269 100644 --- a/src/libGLESv2/egl_stubs.cpp +++ b/src/libGLESv2/egl_stubs.cpp @@ -104,7 +104,28 @@ EGLint ClientWaitSync(Thread *thread, thread, syncObject->clientWait(display, currentContext, flags, timeout, &syncStatus), "eglClientWaitSync", GetSyncIfValid(display, syncID), EGL_FALSE); - thread->setSuccess(); + // When performing CPU wait through UnlockedTailCall we need to handle any error conditions + if (egl::Display::GetCurrentThreadUnlockedTailCall()->any()) + { + auto handleErrorStatus = [thread, display, syncID](void *result) { + EGLint *eglResult = static_cast(result); + ASSERT(eglResult); + if (*eglResult == EGL_FALSE) + { + thread->setError(egl::Error(EGL_BAD_ALLOC), "eglClientWaitSync", + GetSyncIfValid(display, syncID)); + } + else + { + thread->setSuccess(); + } + }; + egl::Display::GetCurrentThreadUnlockedTailCall()->add(handleErrorStatus); + } + else + { + thread->setSuccess(); + } return syncStatus; } diff --git a/src/libGLESv2/entry_points_egl_autogen.cpp b/src/libGLESv2/entry_points_egl_autogen.cpp index ea5ad1520..031cb8e31 100644 --- a/src/libGLESv2/entry_points_egl_autogen.cpp +++ b/src/libGLESv2/entry_points_egl_autogen.cpp @@ -1002,7 +1002,7 @@ EGLint EGLAPIENTRY EGL_ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags ANGLE_CAPTURE_EGL(ClientWaitSync, true, thread, dpyPacked, syncPacked, flags, timeout, returnValue); } - ASSERT(!egl::Display::GetCurrentThreadUnlockedTailCall()->any()); + egl::Display::GetCurrentThreadUnlockedTailCall()->run(&returnValue); return returnValue; } diff --git a/src/libGLESv2/entry_points_egl_ext_autogen.cpp b/src/libGLESv2/entry_points_egl_ext_autogen.cpp index 22b23498d..09ee2af9f 100644 --- a/src/libGLESv2/entry_points_egl_ext_autogen.cpp +++ b/src/libGLESv2/entry_points_egl_ext_autogen.cpp @@ -1422,7 +1422,7 @@ EGLint EGLAPIENTRY EGL_ClientWaitSyncKHR(EGLDisplay dpy, ANGLE_CAPTURE_EGL(ClientWaitSyncKHR, true, thread, dpyPacked, syncPacked, flags, timeout, returnValue); } - ASSERT(!egl::Display::GetCurrentThreadUnlockedTailCall()->any()); + egl::Display::GetCurrentThreadUnlockedTailCall()->run(&returnValue); return returnValue; } diff --git a/src/libGLESv2/entry_points_gles_3_0_autogen.cpp b/src/libGLESv2/entry_points_gles_3_0_autogen.cpp index 163054bae..2d2723648 100644 --- a/src/libGLESv2/entry_points_gles_3_0_autogen.cpp +++ b/src/libGLESv2/entry_points_gles_3_0_autogen.cpp @@ -408,7 +408,7 @@ GLenum GL_APIENTRY GL_ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 tim GenerateContextLostErrorOnCurrentGlobalContext(); returnValue = GetDefaultReturnValue(); } - ASSERT(!egl::Display::GetCurrentThreadUnlockedTailCall()->any()); + egl::Display::GetCurrentThreadUnlockedTailCall()->run(&returnValue); return returnValue; } diff --git a/src/tests/egl_tests/EGLSyncTest.cpp b/src/tests/egl_tests/EGLSyncTest.cpp index b2b5261c9..1b38c61eb 100644 --- a/src/tests/egl_tests/EGLSyncTest.cpp +++ b/src/tests/egl_tests/EGLSyncTest.cpp @@ -236,6 +236,57 @@ TEST_P(EGLSyncTest, BasicOperations) EXPECT_EGL_TRUE(eglDestroySyncKHR(display, sync)); } +// Test that eglClientWaitSync* APIs work. +TEST_P(EGLSyncTest, EglClientWaitSync) +{ + ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension()); + + EGLDisplay display = getEGLWindow()->getDisplay(); + ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); + + // Test eglClientWaitSyncKHR + for (size_t i = 0; i < 5; i++) + { + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + drawQuad(greenProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); + ASSERT_GL_NO_ERROR(); + + // Don't wait forever to make sure the test terminates + constexpr GLuint64 kTimeout = 1'000'000'000; // 1 second + EGLSyncKHR clientWaitSync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr); + EXPECT_NE(clientWaitSync, EGL_NO_SYNC_KHR); + + ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, + eglClientWaitSyncKHR(display, clientWaitSync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + kTimeout)); + + EXPECT_EGL_TRUE(eglDestroySyncKHR(display, clientWaitSync)); + ASSERT_EGL_SUCCESS(); + } + + // Test eglClientWaitSync + for (size_t i = 0; i < 5; i++) + { + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + drawQuad(greenProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); + ASSERT_GL_NO_ERROR(); + + // Don't wait forever to make sure the test terminates + constexpr GLuint64 kTimeout = 1'000'000'000; // 1 second + EGLSyncKHR clientWaitSync = eglCreateSync(display, EGL_SYNC_FENCE, nullptr); + EXPECT_NE(clientWaitSync, EGL_NO_SYNC); + + ASSERT_EQ( + EGL_CONDITION_SATISFIED, + eglClientWaitSync(display, clientWaitSync, EGL_SYNC_FLUSH_COMMANDS_BIT, kTimeout)); + + EXPECT_EGL_TRUE(eglDestroySync(display, clientWaitSync)); + ASSERT_EGL_SUCCESS(); + } +} + // Test eglWaitClient api TEST_P(EGLSyncTest, WaitClient) { diff --git a/src/tests/gl_tests/FenceSyncTests.cpp b/src/tests/gl_tests/FenceSyncTests.cpp index 88f38a821..df01eb4c4 100644 --- a/src/tests/gl_tests/FenceSyncTests.cpp +++ b/src/tests/gl_tests/FenceSyncTests.cpp @@ -265,11 +265,26 @@ TEST_P(FenceSyncTest, BasicOperations) ASSERT_GLENUM_EQ(GL_SIGNALED, value); + ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); for (size_t i = 0; i < 20; i++) { glClear(GL_COLOR_BUFFER_BIT); - glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + drawQuad(greenProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); + ASSERT_GL_NO_ERROR(); + + GLsync clientWaitSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + ASSERT_GL_NO_ERROR(); + + // Don't wait forever to make sure the test terminates + constexpr GLuint64 kTimeout = 1'000'000'000; // 1 second + GLenum clientWaitResult = + glClientWaitSync(clientWaitSync, GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout); EXPECT_GL_NO_ERROR(); + EXPECT_TRUE(clientWaitResult == GL_CONDITION_SATISFIED || + clientWaitResult == GL_ALREADY_SIGNALED); + + glDeleteSync(clientWaitSync); + ASSERT_GL_NO_ERROR(); } }