| // |
| // Copyright 2021 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. |
| // |
| // EGLDisplaySelectionTest.cpp: |
| // Checks display selection and caching with EGL extensions EGL_ANGLE_display_power_preference, |
| // EGL_ANGLE_platform_angle, and EGL_ANGLE_device_id |
| // |
| |
| #include <gtest/gtest.h> |
| |
| #include <array> |
| #include <set> |
| |
| #include "common/debug.h" |
| #include "common/string_utils.h" |
| #include "gpu_info_util/SystemInfo.h" |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/system_info_util.h" |
| #include "util/OSWindow.h" |
| |
| using namespace angle; |
| |
| class EGLDisplaySelectionTest : public ANGLETest<> |
| { |
| public: |
| void testSetUp() override { (void)GetSystemInfo(&mSystemInfo); } |
| |
| protected: |
| // Returns the index of the low or high power GPU in SystemInfo depending on the argument. |
| int findGPU(bool lowPower) const |
| { |
| if (lowPower) |
| { |
| return FindLowPowerGPU(mSystemInfo); |
| } |
| return FindHighPowerGPU(mSystemInfo); |
| } |
| |
| // Returns the index of the active GPU in SystemInfo based on the renderer string. |
| int findActiveGPU() const { return FindActiveOpenGLGPU(mSystemInfo); } |
| |
| SystemInfo mSystemInfo; |
| }; |
| |
| class EGLDisplaySelectionTestNoFixture : public EGLDisplaySelectionTest |
| { |
| protected: |
| void terminateWindow() |
| { |
| if (mOSWindow) |
| { |
| OSWindow::Delete(&mOSWindow); |
| } |
| } |
| |
| void terminateDisplay(EGLDisplay display) |
| { |
| EXPECT_EGL_TRUE(eglTerminate(display)); |
| EXPECT_EGL_SUCCESS(); |
| } |
| |
| void terminateContext(EGLDisplay display, EGLContext context) |
| { |
| if (context != EGL_NO_CONTEXT) |
| { |
| eglDestroyContext(display, context); |
| ASSERT_EGL_SUCCESS(); |
| } |
| } |
| |
| void initializeWindow() |
| { |
| mOSWindow = OSWindow::New(); |
| mOSWindow->initialize("EGLDisplaySelectionTestMultiDisplay", kWindowWidth, kWindowHeight); |
| setWindowVisible(mOSWindow, true); |
| } |
| |
| void initializeContextForDisplay(EGLDisplay display, EGLContext *context) |
| { |
| // Find a default config. |
| const EGLint configAttributes[] = { |
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, EGL_DONT_CARE, EGL_GREEN_SIZE, |
| EGL_DONT_CARE, EGL_BLUE_SIZE, EGL_DONT_CARE, EGL_ALPHA_SIZE, EGL_DONT_CARE, |
| EGL_DEPTH_SIZE, EGL_DONT_CARE, EGL_STENCIL_SIZE, EGL_DONT_CARE, EGL_NONE}; |
| |
| EGLint configCount; |
| EGLConfig config; |
| EGLint ret = eglChooseConfig(display, configAttributes, &config, 1, &configCount); |
| |
| if (!ret || configCount == 0) |
| { |
| return; |
| } |
| |
| EGLint contextAttributes[] = { |
| EGL_CONTEXT_MAJOR_VERSION_KHR, |
| GetParam().majorVersion, |
| EGL_CONTEXT_MINOR_VERSION_KHR, |
| GetParam().minorVersion, |
| EGL_NONE, |
| }; |
| |
| *context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); |
| ASSERT_TRUE(*context != EGL_NO_CONTEXT); |
| } |
| |
| static constexpr int kWindowWidth = 16; |
| static constexpr int kWindowHeight = 8; |
| |
| OSWindow *mOSWindow = nullptr; |
| }; |
| |
| class EGLDisplaySelectionTestMultiDisplay : public EGLDisplaySelectionTestNoFixture |
| { |
| |
| protected: |
| void initializeDisplayWithPowerPreference(EGLDisplay *display, EGLAttrib powerPreference) |
| { |
| GLenum platformType = GetParam().getRenderer(); |
| GLenum deviceType = GetParam().getDeviceType(); |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(platformType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); |
| displayAttributes.push_back(deviceType); |
| displayAttributes.push_back(EGL_POWER_PREFERENCE_ANGLE); |
| displayAttributes.push_back(powerPreference); |
| displayAttributes.push_back(EGL_NONE); |
| |
| *display = eglGetPlatformDisplay(GetEglPlatform(), |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| ASSERT_TRUE(*display != EGL_NO_DISPLAY); |
| |
| EGLint majorVersion, minorVersion; |
| ASSERT_TRUE(eglInitialize(*display, &majorVersion, &minorVersion) == EGL_TRUE); |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| ASSERT_EGL_SUCCESS(); |
| } |
| |
| void initializeDisplayWithBackend(EGLDisplay *display, EGLAttrib platformType) |
| { |
| GLenum deviceType = GetParam().getDeviceType(); |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(platformType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); |
| displayAttributes.push_back(deviceType); |
| displayAttributes.push_back(EGL_NONE); |
| |
| *display = eglGetPlatformDisplay(GetEglPlatform(), |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| ASSERT_TRUE(*display != EGL_NO_DISPLAY); |
| |
| EGLint majorVersion, minorVersion; |
| ASSERT_TRUE(eglInitialize(*display, &majorVersion, &minorVersion) == EGL_TRUE); |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| ASSERT_EGL_SUCCESS(); |
| } |
| |
| void runReinitializeDisplayPowerPreference(EGLAttrib powerPreference) |
| { |
| initializeWindow(); |
| |
| // Initialize the display with the selected power preference |
| EGLDisplay display; |
| EGLContext context; |
| initializeDisplayWithPowerPreference(&display, powerPreference); |
| initializeContextForDisplay(display, &context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
| |
| bool lowPower = (powerPreference == EGL_LOW_POWER_ANGLE); |
| ASSERT_EQ(findGPU(lowPower), findActiveGPU()); |
| |
| // Terminate the display |
| terminateContext(display, context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display); |
| |
| // Change the power preference |
| if (powerPreference == EGL_LOW_POWER_ANGLE) |
| { |
| powerPreference = EGL_HIGH_POWER_ANGLE; |
| } |
| else |
| { |
| powerPreference = EGL_LOW_POWER_ANGLE; |
| } |
| |
| // Reinitialize the display with a new power preference |
| initializeDisplayWithPowerPreference(&display, powerPreference); |
| initializeContextForDisplay(display, &context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
| |
| // Expect that the power preference has changed |
| lowPower = (powerPreference == EGL_LOW_POWER_ANGLE); |
| ASSERT_EQ(findGPU(lowPower), findActiveGPU()); |
| |
| // Terminate the display |
| terminateContext(display, context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display); |
| |
| terminateWindow(); |
| } |
| |
| void runMultiDisplayBackend(EGLAttrib backend1, |
| bool(checkFunc1)(void), |
| EGLAttrib backend2, |
| bool(checkFunc2)(void)) |
| { |
| initializeWindow(); |
| |
| // Initialize the display with the selected backend |
| EGLDisplay display1; |
| EGLContext context1; |
| initializeDisplayWithBackend(&display1, backend1); |
| initializeContextForDisplay(display1, &context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| |
| // Check that the correct backend is chosen |
| ASSERT_TRUE(checkFunc1()); |
| |
| // Initialize the second display with the second backend |
| EGLDisplay display2; |
| EGLContext context2; |
| initializeDisplayWithBackend(&display2, backend2); |
| initializeContextForDisplay(display2, &context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, context2); |
| |
| // Check that the correct backend is chosen |
| ASSERT_TRUE(checkFunc2()); |
| |
| // Switch back to the first display to verify |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| ASSERT_TRUE(checkFunc1()); |
| |
| // Terminate the displays |
| terminateContext(display1, context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display1); |
| terminateContext(display2, context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display2); |
| |
| terminateWindow(); |
| } |
| |
| void runMultiDisplayBackendDefault(EGLAttrib backend, bool(checkFunc)(void)) |
| { |
| initializeWindow(); |
| |
| // Initialize the display with the selected backend |
| EGLDisplay display1; |
| EGLContext context1; |
| initializeDisplayWithBackend(&display1, backend); |
| initializeContextForDisplay(display1, &context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| |
| // Check that the correct backend is chosen |
| ASSERT_TRUE(checkFunc()); |
| |
| // Initialize the second display with the second backend |
| EGLDisplay display2; |
| EGLContext context2; |
| initializeDisplayWithBackend(&display2, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE); |
| initializeContextForDisplay(display2, &context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, context2); |
| |
| bool sameDisplay = false; |
| // If this backend is the same as the first display, check that the display is cached |
| if (checkFunc()) |
| { |
| ASSERT_EQ(display1, display2); |
| sameDisplay = true; |
| } |
| // If this backend is not the same, check that this is a different display |
| else |
| { |
| ASSERT_NE(display1, display2); |
| } |
| |
| // Switch back to the first display to verify |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| ASSERT_TRUE(checkFunc()); |
| |
| // Terminate the displays |
| terminateContext(display1, context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| if (!sameDisplay) |
| { |
| terminateDisplay(display1); |
| } |
| terminateContext(display2, context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display2); |
| |
| terminateWindow(); |
| } |
| |
| void runMultiDisplayPowerPreference() |
| { |
| initializeWindow(); |
| |
| // Initialize the first display with low power |
| EGLDisplay display1; |
| EGLContext context1; |
| initializeDisplayWithPowerPreference(&display1, EGL_LOW_POWER_ANGLE); |
| initializeContextForDisplay(display1, &context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| |
| ASSERT_EQ(findGPU(true), findActiveGPU()); |
| |
| // Initialize the second display with high power |
| EGLDisplay display2; |
| EGLContext context2; |
| initializeDisplayWithPowerPreference(&display2, EGL_HIGH_POWER_ANGLE); |
| initializeContextForDisplay(display2, &context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, context2); |
| |
| ASSERT_EQ(findGPU(false), findActiveGPU()); |
| |
| // Switch back to the first display to verify |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, context1); |
| ASSERT_EQ(findGPU(true), findActiveGPU()); |
| |
| // Terminate the displays |
| terminateContext(display1, context1); |
| eglMakeCurrent(display1, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display1); |
| terminateContext(display2, context2); |
| eglMakeCurrent(display2, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display2); |
| |
| terminateWindow(); |
| } |
| }; |
| |
| TEST_P(EGLDisplaySelectionTest, SelectGPU) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference")); |
| ASSERT_NE(GetParam().eglParameters.displayPowerPreference, EGL_DONT_CARE); |
| |
| bool lowPower = (GetParam().eglParameters.displayPowerPreference == EGL_LOW_POWER_ANGLE); |
| ASSERT_EQ(findGPU(lowPower), findActiveGPU()); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, ReInitializePowerPreferenceLowToHigh) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference")); |
| |
| runReinitializeDisplayPowerPreference(EGL_LOW_POWER_ANGLE); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, ReInitializePowerPreferenceHighToLow) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference")); |
| |
| runReinitializeDisplayPowerPreference(EGL_HIGH_POWER_ANGLE); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendMetalOpenGL) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_METAL) && defined(ANGLE_ENABLE_OPENGL) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackend(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, IsMetal, |
| EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, IsOpenGL); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendOpenGLMetal) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_METAL) && defined(ANGLE_ENABLE_OPENGL) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackend(EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, IsOpenGL, |
| EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, IsMetal); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendVulkanD3D11) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_VULKAN) && defined(ANGLE_ENABLE_D3D11) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackend(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, IsVulkan, |
| EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, IsD3D11); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendD3D11Vulkan) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_VULKAN) && defined(ANGLE_ENABLE_D3D11) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackend(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, IsD3D11, |
| EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, IsVulkan); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendDefaultMetal) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_METAL) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackendDefault(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, IsMetal); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendDefaultOpenGL) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_OPENGL) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackendDefault(EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, IsOpenGL); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendDefaultD3D11) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_D3D11) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| runMultiDisplayBackendDefault(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, IsD3D11); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, BackendDefaultVulkan) |
| { |
| bool missingBackends = true; |
| #if defined(ANGLE_ENABLE_VULKAN) |
| missingBackends = false; |
| #endif |
| ANGLE_SKIP_TEST_IF(missingBackends); |
| |
| // http://anglebug.com/42265471 |
| ANGLE_SKIP_TEST_IF(IsMac()); |
| |
| runMultiDisplayBackendDefault(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, IsVulkan); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestMultiDisplay, PowerPreference) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference")); |
| |
| runMultiDisplayPowerPreference(); |
| } |
| |
| class EGLDisplaySelectionTestDeviceId : public EGLDisplaySelectionTestNoFixture |
| { |
| |
| protected: |
| void initializeDisplayWithDeviceId(EGLDisplay *display, uint64_t deviceId) |
| { |
| GLenum platformType = GetParam().getRenderer(); |
| GLenum deviceType = GetParam().getDeviceType(); |
| GLint displayPowerPreference = GetParam().eglParameters.displayPowerPreference; |
| |
| EGLAttrib high = ((deviceId >> 32) & 0xFFFFFFFF); |
| EGLAttrib low = (deviceId & 0xFFFFFFFF); |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(platformType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); |
| displayAttributes.push_back(deviceType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE); |
| displayAttributes.push_back(high); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE); |
| displayAttributes.push_back(low); |
| if (displayPowerPreference != EGL_DONT_CARE) |
| { |
| displayAttributes.push_back(EGL_POWER_PREFERENCE_ANGLE); |
| displayAttributes.push_back(displayPowerPreference); |
| } |
| displayAttributes.push_back(EGL_NONE); |
| |
| *display = eglGetPlatformDisplay(GetEglPlatform(), |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| ASSERT_TRUE(*display != EGL_NO_DISPLAY); |
| |
| EGLint majorVersion, minorVersion; |
| ASSERT_TRUE(eglInitialize(*display, &majorVersion, &minorVersion) == EGL_TRUE); |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| ASSERT_EGL_SUCCESS(); |
| } |
| }; |
| |
| TEST_P(EGLDisplaySelectionTestDeviceId, DeviceId) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_device_id")); |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference") && |
| GetParam().eglParameters.displayPowerPreference != EGL_DONT_CARE); |
| |
| initializeWindow(); |
| |
| for (size_t i = 0; i < mSystemInfo.gpus.size(); i++) |
| { |
| // Initialize the display with device id for each GPU |
| EGLDisplay display; |
| EGLContext context; |
| initializeDisplayWithDeviceId(&display, mSystemInfo.gpus[i].systemDeviceId); |
| initializeContextForDisplay(display, &context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
| |
| ASSERT_EQ(static_cast<int>(i), findActiveGPU()); |
| |
| // Terminate the displays |
| terminateContext(display, context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(display); |
| } |
| |
| terminateWindow(); |
| } |
| |
| TEST_P(EGLDisplaySelectionTestDeviceId, DeviceIdConcurrently) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_device_id")); |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_display_power_preference") && |
| GetParam().eglParameters.displayPowerPreference != EGL_DONT_CARE); |
| |
| initializeWindow(); |
| struct ContextForDevice |
| { |
| EGLDisplay display{}; |
| EGLContext context{}; |
| }; |
| std::vector<ContextForDevice> contexts(mSystemInfo.gpus.size()); |
| |
| for (size_t i = 0; i < contexts.size(); i++) |
| { |
| auto &display = contexts[i].display; |
| auto &context = contexts[i].context; |
| initializeDisplayWithDeviceId(&display, mSystemInfo.gpus[i].systemDeviceId); |
| initializeContextForDisplay(display, &context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
| ASSERT_EQ(static_cast<int>(i), findActiveGPU()); |
| } |
| for (auto &context : contexts) |
| { |
| // Terminate the displays |
| terminateContext(context.display, context.context); |
| eglMakeCurrent(context.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(context.display); |
| } |
| |
| terminateWindow(); |
| } |
| |
| class EGLDisplaySelectionTestDisplayKey : public EGLDisplaySelectionTestNoFixture |
| { |
| |
| protected: |
| void initializeDisplayWithKey(EGLDisplay *display, EGLAttrib displayKey) |
| { |
| GLenum platformType = GetParam().getRenderer(); |
| GLenum deviceType = GetParam().getDeviceType(); |
| GLint displayPowerPreference = GetParam().eglParameters.displayPowerPreference; |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(platformType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE); |
| displayAttributes.push_back(EGL_DONT_CARE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); |
| displayAttributes.push_back(deviceType); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DISPLAY_KEY_ANGLE); |
| displayAttributes.push_back(displayKey); |
| if (displayPowerPreference != EGL_DONT_CARE) |
| { |
| displayAttributes.push_back(EGL_POWER_PREFERENCE_ANGLE); |
| displayAttributes.push_back(displayPowerPreference); |
| } |
| displayAttributes.push_back(EGL_NONE); |
| |
| *display = eglGetPlatformDisplay(GetEglPlatform(), |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| ASSERT_TRUE(*display != EGL_NO_DISPLAY); |
| |
| EGLint majorVersion, minorVersion; |
| ASSERT_TRUE(eglInitialize(*display, &majorVersion, &minorVersion) == EGL_TRUE); |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| ASSERT_EGL_SUCCESS(); |
| } |
| }; |
| |
| // Test creating multiple displays with different display keys and verifying they are unique. |
| TEST_P(EGLDisplaySelectionTestDisplayKey, ConcurentDisplayKey) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_display_key")); |
| |
| initializeWindow(); |
| struct ContextForKey |
| { |
| EGLDisplay display{}; |
| EGLContext context{}; |
| }; |
| std::vector<ContextForKey> contexts; |
| constexpr size_t kDisplayCount = 10; |
| |
| for (size_t i = 0; i < kDisplayCount; i++) |
| { |
| ContextForKey contextForKey; |
| auto &display = contextForKey.display; |
| auto &context = contextForKey.context; |
| initializeDisplayWithKey(&display, i); |
| initializeContextForDisplay(display, &context); |
| eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
| |
| // Test that this display is unique |
| for (auto &otherContext : contexts) |
| { |
| EXPECT_NE(display, otherContext.display); |
| } |
| |
| contexts.push_back(contextForKey); |
| } |
| |
| // Test that requesting using the same display key returns the same display |
| for (size_t i = 0; i < kDisplayCount; i++) |
| { |
| EGLDisplay display = EGL_NO_DISPLAY; |
| initializeDisplayWithKey(&display, i); |
| EXPECT_EQ(contexts[i].display, display); |
| } |
| |
| for (auto &context : contexts) |
| { |
| // Terminate the displays |
| terminateContext(context.display, context.context); |
| eglMakeCurrent(context.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| terminateDisplay(context.display); |
| } |
| |
| terminateWindow(); |
| } |
| |
| class EGLDisplaySelectionTestVulkanDeviceUuid : public EGLDisplaySelectionTestNoFixture |
| { |
| |
| protected: |
| static constexpr size_t kVulkanUuidSize = 16; |
| using VulkanUuid = std::array<uint8_t, kVulkanUuidSize>; |
| |
| static VulkanUuid MakeUuid(uint8_t seed) |
| { |
| VulkanUuid uuid; |
| uuid.fill(seed); |
| return uuid; |
| } |
| |
| // Deliberately does not call eglInitialize. Display creation is lazy, so no |
| // physical device is selected here and the test does not depend on the |
| // running system having a device that matches |deviceUuid|. The cache key |
| // is built by eglGetPlatformDisplay alone, which is exactly what is under |
| // test. |
| EGLDisplay getDisplayWithDeviceUuid(const VulkanUuid &deviceUuid) |
| { |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_VULKAN_DEVICE_UUID_ANGLE); |
| displayAttributes.push_back(reinterpret_cast<EGLAttrib>(deviceUuid.data())); |
| displayAttributes.push_back(EGL_NONE); |
| |
| EGLDisplay display = eglGetPlatformDisplay( |
| GetEglPlatform(), reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| EXPECT_EGL_SUCCESS(); |
| return display; |
| } |
| }; |
| |
| // Test that displays requesting different Vulkan device UUIDs are distinct, and |
| // that a UUID supplied through a different buffer still maps to the display |
| // already cached for those bytes. |
| TEST_P(EGLDisplaySelectionTestVulkanDeviceUuid, DistinctDeviceUuidsAreNotShared) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_vulkan_device_uuid")); |
| |
| initializeWindow(); |
| |
| constexpr size_t kDisplayCount = 4; |
| |
| // Built up front so that no reallocation can move the bytes a display was |
| // keyed on: the UUID must outlive the display that references it. |
| std::vector<VulkanUuid> uuids; |
| uuids.reserve(kDisplayCount); |
| for (size_t i = 0; i < kDisplayCount; i++) |
| { |
| uuids.push_back(MakeUuid(static_cast<uint8_t>(i + 1))); |
| } |
| |
| std::set<EGLDisplay> uniqueDisplays; |
| std::vector<EGLDisplay> displays; |
| for (const VulkanUuid &uuid : uuids) |
| { |
| EGLDisplay display = getDisplayWithDeviceUuid(uuid); |
| ASSERT_TRUE(display != EGL_NO_DISPLAY); |
| |
| // Test that this display is unique |
| EXPECT_TRUE(uniqueDisplays.insert(display).second); |
| |
| displays.push_back(display); |
| } |
| |
| // Test that the key holds the UUID bytes rather than the caller's pointer, |
| // by requesting the same UUIDs again through a separate buffer. |
| for (size_t i = 0; i < kDisplayCount; i++) |
| { |
| const VulkanUuid sameBytesOtherBuffer = uuids[i]; |
| EXPECT_EQ(displays[i], getDisplayWithDeviceUuid(sameBytesOtherBuffer)); |
| } |
| |
| // Test mutating UUID contents in-place with the same buffer address: |
| // the mutated contents must produce a new unique display. |
| VulkanUuid mutableUuid = MakeUuid(0xFE); |
| EGLDisplay displayBeforeMutation = getDisplayWithDeviceUuid(mutableUuid); |
| ASSERT_TRUE(displayBeforeMutation != EGL_NO_DISPLAY); |
| EXPECT_TRUE(uniqueDisplays.insert(displayBeforeMutation).second); |
| |
| mutableUuid[0] += 1; |
| EGLDisplay displayAfterMutation = getDisplayWithDeviceUuid(mutableUuid); |
| ASSERT_TRUE(displayAfterMutation != EGL_NO_DISPLAY); |
| EXPECT_TRUE(uniqueDisplays.insert(displayAfterMutation).second); |
| |
| for (EGLDisplay display : uniqueDisplays) |
| { |
| terminateDisplay(display); |
| } |
| |
| terminateWindow(); |
| } |
| |
| // Test that the caller's UUID buffer does not have to outlive |
| // eglGetPlatformDisplay. The physical device is chosen later, during |
| // eglInitialize, and must be chosen from the display's own copy of the bytes. |
| // Reading the caller's buffer there is a use-after-free that only a sanitizer |
| // build reports, so this test guards the contract rather than demonstrating a |
| // visible failure. |
| TEST_P(EGLDisplaySelectionTestVulkanDeviceUuid, DeviceUuidNeedNotOutliveDisplayCreation) |
| { |
| ANGLE_SKIP_TEST_IF(!IsEGLClientExtensionEnabled("EGL_ANGLE_platform_angle_vulkan_device_uuid")); |
| |
| initializeWindow(); |
| |
| EGLDisplay display = EGL_NO_DISPLAY; |
| { |
| // Heap allocated so that a sanitizer poisons the bytes when the buffer |
| // goes out of scope, the way a caller's stack array would be reused. |
| std::vector<uint8_t> deviceUuid(kVulkanUuidSize, 0xAB); |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_VULKAN_DEVICE_UUID_ANGLE); |
| displayAttributes.push_back(reinterpret_cast<EGLAttrib>(deviceUuid.data())); |
| displayAttributes.push_back(EGL_NONE); |
| |
| display = eglGetPlatformDisplay(GetEglPlatform(), |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), |
| displayAttributes.data()); |
| ASSERT_EGL_SUCCESS(); |
| } |
| ASSERT_TRUE(display != EGL_NO_DISPLAY); |
| |
| // No device carries this UUID, so the implementation falls back to another |
| // device and initialization still succeeds. |
| EXPECT_TRUE(eglInitialize(display, nullptr, nullptr) == EGL_TRUE); |
| EXPECT_EGL_SUCCESS(); |
| |
| terminateDisplay(display); |
| terminateWindow(); |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplaySelectionTest); |
| ANGLE_INSTANTIATE_TEST(EGLDisplaySelectionTest, |
| WithLowPowerGPU(ES2_METAL()), |
| WithLowPowerGPU(ES3_METAL()), |
| WithHighPowerGPU(ES2_METAL()), |
| WithHighPowerGPU(ES3_METAL())); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplaySelectionTestMultiDisplay); |
| ANGLE_INSTANTIATE_TEST(EGLDisplaySelectionTestMultiDisplay, |
| WithNoFixture(ES2_METAL()), |
| WithNoFixture(ES3_METAL())); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplaySelectionTestDeviceId); |
| ANGLE_INSTANTIATE_TEST(EGLDisplaySelectionTestDeviceId, |
| WithNoFixture(ES2_D3D11()), |
| WithNoFixture(ES3_D3D11()), |
| WithNoFixture(ES2_METAL()), |
| WithNoFixture(ES3_METAL()), |
| WithNoFixture(WithLowPowerGPU(ES2_METAL())), |
| WithNoFixture(WithLowPowerGPU(ES3_METAL())), |
| WithNoFixture(WithHighPowerGPU(ES2_METAL())), |
| WithNoFixture(WithHighPowerGPU(ES3_METAL()))); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplaySelectionTestDisplayKey); |
| ANGLE_INSTANTIATE_TEST(EGLDisplaySelectionTestDisplayKey, |
| WithNoFixture(ES2_D3D11()), |
| WithNoFixture(ES3_D3D11()), |
| WithNoFixture(ES2_METAL()), |
| WithNoFixture(ES3_METAL()), |
| WithNoFixture(ES2_WEBGPU()), |
| WithNoFixture(ES3_WEBGPU()), |
| WithNoFixture(WithLowPowerGPU(ES2_METAL())), |
| WithNoFixture(WithLowPowerGPU(ES3_METAL())), |
| WithNoFixture(WithHighPowerGPU(ES2_METAL())), |
| WithNoFixture(WithHighPowerGPU(ES3_METAL()))); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLDisplaySelectionTestVulkanDeviceUuid); |
| ANGLE_INSTANTIATE_TEST(EGLDisplaySelectionTestVulkanDeviceUuid, |
| WithNoFixture(ES2_VULKAN()), |
| WithNoFixture(ES3_VULKAN())); |