Hash :
b22124bd
Author :
Date :
2023-01-17T17:44:06
Implement new "Global Mutex" functionality.
This CL improves performance compared to the existing code as well as
allows using non-std mutex implementations. Also acts as a base for
future changes.
CL adds new build option:
angle_enable_global_mutex_recursion = is_android && angle_enable_vulkan
"mutex_recursion" work same way as `std::recursive_mutex` before. It
will help in situations when Vulkan API may return back to the ANGLE.
For example: RenderDoc layer EGL deadlock.
Automatic loading of "libVkLayer_GLES_RenderDoc.so" layer causes
deadlock in EGL.
Recursion stack:
#01 pc 000000000029ea80 /vendor/lib64/egl/libGLESv2_angle.so (egl::GlobalMutexHelper::lock(int)+596)
#02 pc 000000000029c59c /vendor/lib64/egl/libGLESv2_angle.so (EGL_GetError+32)
#04 pc 0000000000062368 /system/lib64/libEGL.so (eglQueryString+20)
#05 pc 0000000000508fec /data/local/debug/vulkan/libVkLayer_GLES_RenderDoc.so
#20 pc 0000000000016690 /system/lib64/libvulkan.so (vulkan::api::EnumerateInstanceLayerProperties(unsigned int*, VkLayerProperties*)+40)
#21 pc 00000000005aa030 /vendor/lib64/egl/libGLESv2_angle.so (rx::RendererVk::initialize(rx::DisplayVk*, egl::Display*, char const*, char const*)+292)
#26 pc 000000000029c7e8 /vendor/lib64/egl/libGLESv2_angle.so (EGL_Initialize+192)
Additionally, recursive mutex will partially solve Android
SurfaceTexture deadlock (angleproject:4354).
Some performance numbers for 1000'000 `eglGetError()` calls.
Mutex Time (ms.)
Android S906B
egl::GetGlobalMutex()(std::recursive_mutex) 41.4
(Default) GlobalMutex (std::recursive_mutex) 39.1
(Recursive) GlobalMutex (std::mutex) 34.9
(Debug) GlobalMutex (std::mutex) 34.7
(Default) GlobalMutex (std::mutex) 34.4
Windows
egl::GetGlobalMutex()(std::recursive_mutex) 20.5
(Default) GlobalMutex (std::recursive_mutex) 20.0
(Recursive) GlobalMutex (std::mutex) 21.9
(Debug) GlobalMutex (std::mutex) 20.5
(Default) GlobalMutex (std::mutex) 19.9
Note: Recursive GlobalMutex enabled only for Android Vulkan by default.
Original fix:
https://chromium-review.googlesource.com/c/angle/angle/+/2029218
Bug: angleproject:8101
Bug: angleproject:4354
Test: angle_unittests --gtest_filter=GlobalMutexTest.*
Change-Id: I9e9d9b5c598ad1177ffa147ea690bd955946a712
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4401940
Commit-Queue: Igor Nazarov <i.nazarov@samsung.com>
Reviewed-by: Charlie Lao <cclao@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
//
// Copyright 2023 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.
//
// GlobalMutex_unittest:
// Tests of the Scoped<*>GlobalMutexLock classes
//
#include <gtest/gtest.h>
#include "libANGLE/GlobalMutex.h"
namespace
{
template <class ScopedGlobalLockT, class... Args>
void runBasicGlobalMutexTest(bool expectToPass, Args &&...args)
{
constexpr size_t kThreadCount = 16;
constexpr size_t kIterationCount = 50'000;
std::array<std::thread, kThreadCount> threads;
std::mutex mutex;
std::condition_variable condVar;
size_t readyCount = 0;
std::atomic<size_t> testVar;
for (size_t i = 0; i < kThreadCount; ++i)
{
threads[i] = std::thread([&]() {
{
std::unique_lock<std::mutex> lock(mutex);
++readyCount;
if (readyCount < kThreadCount)
{
condVar.wait(lock, [&]() { return readyCount == kThreadCount; });
}
else
{
condVar.notify_all();
}
}
for (size_t j = 0; j < kIterationCount; ++j)
{
ScopedGlobalLockT lock(std::forward<Args>(args)...);
const int local = testVar.load(std::memory_order_relaxed);
const int newValue = local + 1;
testVar.store(newValue, std::memory_order_relaxed);
}
});
}
for (size_t i = 0; i < kThreadCount; ++i)
{
threads[i].join();
}
if (expectToPass)
{
EXPECT_EQ(testVar.load(), kThreadCount * kIterationCount);
}
else
{
EXPECT_NE(testVar.load(), kThreadCount * kIterationCount);
}
}
// Tests basic usage of ScopedGlobalMutexLock.
TEST(GlobalMutexTest, ScopedGlobalMutexLock)
{
runBasicGlobalMutexTest<egl::ScopedGlobalMutexLock>(true);
}
// Tests basic usage of ScopedOptionalGlobalMutexLock (Enabled).
TEST(GlobalMutexTest, ScopedOptionalGlobalMutexLockEnabled)
{
runBasicGlobalMutexTest<egl::ScopedOptionalGlobalMutexLock>(true, true);
}
// Tests basic usage of ScopedOptionalGlobalMutexLock (Disabled).
TEST(GlobalMutexTest, ScopedOptionalGlobalMutexLockDisabled)
{
runBasicGlobalMutexTest<egl::ScopedOptionalGlobalMutexLock>(false, false);
}
#if defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION)
// Tests that ScopedGlobalMutexLock can be recursively locked.
TEST(GlobalMutexTest, RecursiveScopedGlobalMutexLock)
{
egl::ScopedGlobalMutexLock lock;
egl::ScopedGlobalMutexLock lock2;
}
// Tests that ScopedOptionalGlobalMutexLock can be recursively locked.
TEST(GlobalMutexTest, RecursiveScopedOptionalGlobalMutexLock)
{
egl::ScopedOptionalGlobalMutexLock lock(true);
egl::ScopedOptionalGlobalMutexLock lock2(true);
}
#endif
} // anonymous namespace