| // | |
| // Copyright 2022 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. | |
| // | |
| #include <gtest/gtest.h> | |
| #include <vector> | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.h" | |
| #include "util/OSWindow.h" | |
| using namespace angle; | |
| class EGLDisplayTest : public ANGLETest<> | |
| { | |
| protected: | |
| EGLConfig chooseConfig(EGLDisplay display) | |
| { | |
| const EGLint attribs[] = {EGL_RED_SIZE, | |
| 8, | |
| EGL_GREEN_SIZE, | |
| 8, | |
| EGL_BLUE_SIZE, | |
| 8, | |
| EGL_ALPHA_SIZE, | |
| 8, | |
| EGL_RENDERABLE_TYPE, | |
| EGL_OPENGL_ES2_BIT, | |
| EGL_SURFACE_TYPE, | |
| EGL_PBUFFER_BIT | EGL_WINDOW_BIT, | |
| EGL_NONE}; | |
| EGLConfig config = EGL_NO_CONFIG_KHR; | |
| EGLint count = 0; | |
| EXPECT_EGL_TRUE(eglChooseConfig(display, attribs, &config, 1, &count)); | |
| EXPECT_EGL_TRUE(count > 0); | |
| return config; | |
| } | |
| EGLContext createContext(EGLDisplay display, EGLConfig config) | |
| { | |
| const EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE}; | |
| EGLContext context = eglCreateContext(display, config, nullptr, attribs); | |
| EXPECT_NE(context, EGL_NO_CONTEXT); | |
| return context; | |
| } | |
| EGLSurface createSurface(EGLDisplay display, EGLConfig config) | |
| { | |
| const EGLint attribs[] = {EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE}; | |
| EGLSurface surface = eglCreatePbufferSurface(display, config, attribs); | |
| EXPECT_NE(surface, EGL_NO_SURFACE); | |
| return surface; | |
| } | |
| }; | |
| class EGLDisplayTestES3 : public EGLDisplayTest | |
| {}; | |
| // Tests that an eglInitialize can be re-initialized. The spec says: | |
| // | |
| // > Initializing an already-initialized display is allowed, but the only effect of such a call is | |
| // to return EGL_TRUE and update the EGL version numbers | |
| TEST_P(EGLDisplayTest, InitializeMultipleTimes) | |
| { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| EGLint major = 0, minor = 0; | |
| EXPECT_EGL_TRUE(eglInitialize(display, &major, &minor) != EGL_FALSE); | |
| for (uint32_t i = 0; i < 10; ++i) | |
| { | |
| EGLint retryMajor = 123456, retryMinor = -1; | |
| EXPECT_EGL_TRUE(eglInitialize(display, &retryMajor, &retryMinor) != EGL_FALSE); | |
| EXPECT_EQ(major, retryMajor) << i; | |
| EXPECT_EQ(minor, retryMinor) << i; | |
| } | |
| } | |
| // Test that call eglInitialize() in parallel in multiple threads works | |
| // > Initializing an already-initialized display is allowed, but the only effect | |
| // of such a call is to return EGL_TRUE and update the EGL version numbers | |
| TEST_P(EGLDisplayTest, InitializeMultipleTimesInDifferentThreads) | |
| { | |
| std::array<std::thread, 10> threads; | |
| for (std::thread &thread : threads) | |
| { | |
| thread = std::thread([&]() { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| const int INVALID_GL_MAJOR_VERSION = -1; | |
| const int INVALID_GL_MINOR_VERSION = -1; | |
| EGLint threadMajor = INVALID_GL_MAJOR_VERSION; | |
| EGLint threadMinor = INVALID_GL_MINOR_VERSION; | |
| EXPECT_EGL_TRUE(eglInitialize(display, &threadMajor, &threadMinor) != EGL_FALSE); | |
| EXPECT_NE(threadMajor, INVALID_GL_MAJOR_VERSION); | |
| EXPECT_NE(threadMinor, INVALID_GL_MINOR_VERSION); | |
| }); | |
| } | |
| for (std::thread &thread : threads) | |
| { | |
| thread.join(); | |
| } | |
| } | |
| // Test that calling eglTerminate() in parallel in multiple threads works | |
| TEST_P(EGLDisplayTest, TerminateMultipleTimesInDifferentThreads) | |
| { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr) != EGL_FALSE); | |
| std::array<std::thread, 10> threads; | |
| for (std::thread &thread : threads) | |
| { | |
| thread = std::thread([&]() { EXPECT_EGL_TRUE(eglTerminate(display) != EGL_FALSE); }); | |
| } | |
| for (std::thread &thread : threads) | |
| { | |
| thread.join(); | |
| } | |
| } | |
| // Tests that an EGLDisplay can be re-initialized. | |
| TEST_P(EGLDisplayTest, InitializeTerminateInitialize) | |
| { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr) != EGL_FALSE); | |
| EXPECT_EGL_TRUE(eglTerminate(display) != EGL_FALSE); | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr) != EGL_FALSE); | |
| } | |
| // Tests that an EGLDisplay can be re-initialized after it was used to draw into a window surface. | |
| TEST_P(EGLDisplayTest, InitializeDrawSwapTerminateLoop) | |
| { | |
| constexpr int kLoopCount = 2; | |
| constexpr EGLint kWidth = 64; | |
| constexpr EGLint kHeight = 64; | |
| OSWindow *osWindow = OSWindow::New(); | |
| osWindow->initialize("LockSurfaceTest", kWidth, kHeight); | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| for (int i = 0; i < kLoopCount; ++i) | |
| { | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr) != EGL_FALSE); | |
| EGLConfig config = chooseConfig(display); | |
| EGLContext context = createContext(display, config); | |
| EGLSurface surface = | |
| eglCreateWindowSurface(display, config, osWindow->getNativeWindow(), nullptr); | |
| EXPECT_NE(surface, EGL_NO_SURFACE); | |
| EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context)); | |
| ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); | |
| drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f); | |
| EXPECT_EGL_TRUE(eglSwapBuffers(display, surface)); | |
| EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); | |
| EXPECT_EGL_TRUE(eglTerminate(display) != EGL_FALSE); | |
| } | |
| osWindow->destroy(); | |
| OSWindow::Delete(&osWindow); | |
| } | |
| // Tests current Context leaking when call eglTerminate() while it is current. | |
| TEST_P(EGLDisplayTest, ContextLeakAfterTerminate) | |
| { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr)); | |
| EGLConfig config = chooseConfig(display); | |
| EGLContext context = createContext(display, config); | |
| EGLSurface surface = createSurface(display, config); | |
| // Make "context" current. | |
| EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context)); | |
| // Terminate display while "context" is current. | |
| EXPECT_EGL_TRUE(eglTerminate(display)); | |
| // Unmake "context" from current and allow Display to actually terminate. | |
| (void)eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
| // Get EGLDisplay again. | |
| display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| // Check if Display was actually terminated. | |
| EGLint val; | |
| EXPECT_EGL_FALSE(eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &val)); | |
| EXPECT_EQ(eglGetError(), EGL_NOT_INITIALIZED); | |
| } | |
| // Tests that eglMakeCurrent with bad display pointer fails but doesn't crash. | |
| TEST_P(EGLDisplayTest, MakeCurrentWithBadDisplay) | |
| { | |
| EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| EXPECT_EGL_TRUE(eglInitialize(display, nullptr, nullptr)); | |
| EGLConfig config = chooseConfig(display); | |
| EGLContext context = createContext(display, config); | |
| // Call eglMakeCurrent with bad display pointer. It should fail validation but not crash. | |
| EXPECT_EGL_FALSE(eglMakeCurrent(EGL_CAST(EGLDisplay, (void *)0xdeadbeef), EGL_NO_SURFACE, | |
| EGL_NO_SURFACE, context)); | |
| EXPECT_EQ(eglGetError(), EGL_BAD_DISPLAY); | |
| // Call eglMakeCurrent with no display. It should fail validation but not crash. | |
| EXPECT_EGL_FALSE(eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); | |
| EXPECT_EQ(eglGetError(), EGL_BAD_DISPLAY); | |
| EXPECT_EGL_TRUE(eglDestroyContext(display, context)); | |
| EXPECT_EGL_TRUE(eglTerminate(display)); | |
| } | |
| // Tests eglGetPlatformDisplayEXT() when EGL_EXT_platform_base is enabled. | |
| TEST_P(EGLDisplayTest, GetPlatformDisplayEXT) | |
| { | |
| // eglGetPlatformDisplayEXT() requires EGL_EXT_platform_base. | |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_EXT_platform_base")); | |
| ASSERT_TRUE(eglGetPlatformDisplayEXT != nullptr); | |
| EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE}; | |
| EGLDisplay display = eglGetPlatformDisplayEXT( | |
| GetEglPlatform(), reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs); | |
| ASSERT_NE(EGL_NO_DISPLAY, display); | |
| ASSERT_EGL_SUCCESS(); | |
| } | |
| // Tests eglQueryString(EGL_EXTENSIONS) with EGL_BAD_DISPLAY should not crash | |
| TEST_P(EGLDisplayTest, QueryEGLExtensionWithBadDisplay) | |
| { | |
| eglQueryString(EGL_CAST(EGLDisplay, (void *)0xdeadbeef), EGL_EXTENSIONS); | |
| EXPECT_EQ(eglGetError(), EGL_BAD_DISPLAY); | |
| } | |
| // Tests eglQueryString(EGL_EXTENSIONS) with EGL_NO_DISPLAY should succeed without error | |
| TEST_P(EGLDisplayTest, QueryEGLExtensionWithNoDisplay) | |
| { | |
| eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); | |
| ASSERT_EGL_SUCCESS(); | |
| } | |
| // Tests eglQueryString with EGL_NO_DISPLAY should fail EGL_BAD_DISPLAY error | |
| TEST_P(EGLDisplayTest, QueryEGLVendorWithBadDisplay) | |
| { | |
| eglQueryString(EGL_NO_DISPLAY, EGL_VENDOR); | |
| EXPECT_EQ(eglGetError(), EGL_BAD_DISPLAY); | |
| } | |
| // Tests that eglGetPlatformDisplayEXT can use EGL_PLATFORM_ANDROID_KHR to get a display. | |
| TEST_P(EGLDisplayTestES3, GetPlatformDisplayAndroidValidation) | |
| { | |
| ANGLE_SKIP_TEST_IF(!IsAndroid()); | |
| ANGLE_SKIP_TEST_IF(getDriverType() != GLESDriverType::SystemEGL); | |
| // Get an EGLDisplay on GBM platform, expect EGL_BAD_PARAMETER | |
| EGLDisplay display1 = eglGetPlatformDisplay( | |
| EGL_PLATFORM_GBM_KHR, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), nullptr); | |
| ASSERT_EQ(EGL_NO_DISPLAY, display1); | |
| ASSERT_EGL_ERROR(EGL_BAD_PARAMETER); | |
| // Get an EGLDisplay on Android platform, expect EGL_SUCCESS | |
| EGLDisplay display2 = eglGetPlatformDisplay( | |
| EGL_PLATFORM_ANDROID_KHR, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), nullptr); | |
| ASSERT_NE(EGL_NO_DISPLAY, display2); | |
| ASSERT_EGL_SUCCESS(); | |
| } | |
| ANGLE_INSTANTIATE_TEST(EGLDisplayTest, | |
| WithNoFixture(ES2_D3D11()), | |
| WithNoFixture(ES2_METAL()), | |
| WithNoFixture(ES2_OPENGL()), | |
| WithNoFixture(ES2_VULKAN()), | |
| WithNoFixture(ES3_D3D11()), | |
| WithNoFixture(ES3_METAL()), | |
| WithNoFixture(ES3_OPENGL()), | |
| WithNoFixture(ES3_VULKAN())); | |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplayTestES3); | |
| ANGLE_INSTANTIATE_TEST(EGLDisplayTestES3, WithNoFixture(ES3_VULKAN())); |