From 510351f2006e32ffc6da722f1fc9ea5666e9c0da Mon Sep 17 00:00:00 2001 From: Antonio Caggiano Date: Thu, 9 Dec 2021 16:52:35 +0100 Subject: [PATCH] Vulkan: Support Wayland Implement DisplayVkWayland and WindowSurfaceVkWayland. Get window size from native window and check egl config is just empty. Then add an EGL wayland test for testing rendering and buffers swapping. Bug: angleproject:6902 Change-Id: I8204a5cc99f26330b74caba241bebf14c5650c2d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3395898 Reviewed-by: mohan maiya Reviewed-by: Shahbaz Youssefi Auto-Submit: Antonio Caggiano Reviewed-by: Jamie Madill Commit-Queue: Jamie Madill --- BUILD.gn | 38 ++++ scripts/export_targets.py | 1 + src/libANGLE/Display.cpp | 9 + src/libANGLE/renderer/vulkan/BUILD.gn | 13 ++ src/libANGLE/renderer/vulkan/DisplayVk_api.h | 3 + .../renderer/vulkan/linux/DisplayVkLinux.h | 4 +- .../vulkan/linux/wayland/DisplayVkWayland.cpp | 103 +++++++++++ .../vulkan/linux/wayland/DisplayVkWayland.h | 44 +++++ .../linux/wayland/WindowSurfaceVkWayland.cpp | 67 +++++++ .../linux/wayland/WindowSurfaceVkWayland.h | 40 ++++ .../vulkan/linux/xcb/DisplayVkXcb.cpp | 6 + src/tests/BUILD.gn | 3 + src/tests/egl_tests/EGLWaylandTest.cpp | 154 ++++++++++++++++ src/tests/egl_tests/EGLX11VisualTest.cpp | 2 +- util/BUILD.gn | 24 ++- util/display/DisplayWindow.cpp | 2 +- util/linux/LinuxWindow.cpp | 40 ++++ util/linux/wayland/WaylandWindow.cpp | 173 ++++++++++++++++++ util/linux/wayland/WaylandWindow.h | 64 +++++++ util/{ => linux}/x11/X11Pixmap.cpp | 2 +- util/{ => linux}/x11/X11Pixmap.h | 0 util/{ => linux}/x11/X11Window.cpp | 13 +- util/{ => linux}/x11/X11Window.h | 2 + 23 files changed, 794 insertions(+), 13 deletions(-) create mode 100644 src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.cpp create mode 100644 src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.h create mode 100644 src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.cpp create mode 100644 src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h create mode 100644 src/tests/egl_tests/EGLWaylandTest.cpp create mode 100644 util/linux/LinuxWindow.cpp create mode 100644 util/linux/wayland/WaylandWindow.cpp create mode 100644 util/linux/wayland/WaylandWindow.h rename util/{ => linux}/x11/X11Pixmap.cpp (97%) rename util/{ => linux}/x11/X11Pixmap.h (100%) rename util/{ => linux}/x11/X11Window.cpp (98%) rename util/{ => linux}/x11/X11Window.h (98%) diff --git a/BUILD.gn b/BUILD.gn index 1f7120fad..38bff30cc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -153,6 +153,15 @@ config("internal_config") { if (!angle_use_x11) { defines += [ "EGL_NO_X11" ] } + + # These two are needed here to correctly select OSWindow::New + if (angle_use_x11) { + defines += [ "ANGLE_USE_X11" ] + } + if (angle_use_wayland) { + defines += [ "ANGLE_USE_WAYLAND" ] + } + if (angle_vulkan_display_mode == "simple") { defines += [ "ANGLE_VULKAN_DISPLAY_MODE_SIMPLE" ] } else if (angle_vulkan_display_mode == "headless") { @@ -513,6 +522,10 @@ angle_static_library("angle_gpu_info_util") { "Xext", ] } + + if (angle_use_wayland && angle_has_build) { + public_deps += [ ":angle_wayland" ] + } } if (use_libpci) { @@ -824,6 +837,10 @@ config("libANGLE_config") { defines += [ "ANGLE_USE_X11" ] } + if (angle_use_wayland) { + defines += [ "ANGLE_USE_WAYLAND" ] + } + if (angle_enable_overlay) { defines += [ "ANGLE_ENABLE_OVERLAY=1" ] } @@ -1018,6 +1035,27 @@ angle_source_set("angle_gl_enum_utils") { ] } +if (angle_use_wayland) { + config("angle_wayland_config") { + libs = [ + "wayland-client", + "wayland-egl", + ] + include_dirs = [ + "$angle_wayland_dir/egl", + "$angle_wayland_dir/src", + + # In case we are building with chromium, we need to take into account the case + # where wayland-egl-backend.h is not installed in the system include directories + "//third_party/wayland/src/egl", + ] + } + + group("angle_wayland") { + public_configs = [ ":angle_wayland_config" ] + } +} + if (!defined(angle_abseil_cpp_dir)) { angle_abseil_cpp_dir = "//third_party/abseil-cpp" } diff --git a/scripts/export_targets.py b/scripts/export_targets.py index 6117a26c7..cbb0663c5 100755 --- a/scripts/export_targets.py +++ b/scripts/export_targets.py @@ -210,6 +210,7 @@ IGNORED_INCLUDES = { b'libANGLE/renderer/vulkan/mac/DisplayVkMac.h', b'libANGLE/renderer/vulkan/win32/DisplayVkWin32.h', b'libANGLE/renderer/vulkan/xcb/DisplayVkXcb.h', + b'libANGLE/renderer/vulkan/wayland/DisplayVkWayland.h', b'loader_cmake_config.h', b'loader_linux.h', b'loader_windows.h', diff --git a/src/libANGLE/Display.cpp b/src/libANGLE/Display.cpp index bd2bd59d7..2d55f644b 100644 --- a/src/libANGLE/Display.cpp +++ b/src/libANGLE/Display.cpp @@ -280,6 +280,8 @@ EGLAttrib GetPlatformTypeFromEnvironment() return 0; #elif defined(ANGLE_USE_X11) return EGL_PLATFORM_X11_EXT; +#elif defined(ANGLE_USE_WAYLAND) + return EGL_PLATFORM_WAYLAND_EXT; #elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_SIMPLE) return EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE; #elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_HEADLESS) @@ -432,6 +434,13 @@ rx::DisplayImpl *CreateDisplayFromAttribs(EGLAttrib displayType, break; } # endif +# if defined(ANGLE_USE_WAYLAND) + if (platformType == EGL_PLATFORM_WAYLAND_EXT && rx::IsVulkanWaylandDisplayAvailable()) + { + impl = rx::CreateVulkanWaylandDisplay(state); + break; + } +# endif # if defined(ANGLE_USE_VULKAN_DISPLAY) if (platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE && rx::IsVulkanSimpleDisplayAvailable()) diff --git a/src/libANGLE/renderer/vulkan/BUILD.gn b/src/libANGLE/renderer/vulkan/BUILD.gn index bf7a68ad2..13281676f 100644 --- a/src/libANGLE/renderer/vulkan/BUILD.gn +++ b/src/libANGLE/renderer/vulkan/BUILD.gn @@ -197,6 +197,15 @@ if (angle_use_x11) { ] } +if (angle_use_wayland) { + _vulkan_backend_sources += [ + "linux/wayland/DisplayVkWayland.cpp", + "linux/wayland/DisplayVkWayland.h", + "linux/wayland/WindowSurfaceVkWayland.cpp", + "linux/wayland/WindowSurfaceVkWayland.h", + ] +} + if (is_fuchsia) { _vulkan_backend_sources += [ "fuchsia/DisplayVkFuchsia.cpp", @@ -292,6 +301,10 @@ angle_source_set("angle_vulkan_backend") { ] public_configs = [ ":angle_vulkan_backend_config" ] + if (angle_use_wayland) { + public_configs += [ "$angle_root:angle_wayland_config" ] + } + data_deps = [] defines = [] diff --git a/src/libANGLE/renderer/vulkan/DisplayVk_api.h b/src/libANGLE/renderer/vulkan/DisplayVk_api.h index 39fd2cb35..24ad07782 100644 --- a/src/libANGLE/renderer/vulkan/DisplayVk_api.h +++ b/src/libANGLE/renderer/vulkan/DisplayVk_api.h @@ -24,6 +24,9 @@ DisplayImpl *CreateVulkanWin32Display(const egl::DisplayState &state); #endif // defined(ANGLE_PLATFORM_WINDOWS) #if defined(ANGLE_PLATFORM_LINUX) +bool IsVulkanWaylandDisplayAvailable(); +DisplayImpl *CreateVulkanWaylandDisplay(const egl::DisplayState &state); + bool IsVulkanXcbDisplayAvailable(); DisplayImpl *CreateVulkanXcbDisplay(const egl::DisplayState &state); diff --git a/src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.h b/src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.h index 9f78491dd..12418b876 100644 --- a/src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.h +++ b/src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.h @@ -5,8 +5,8 @@ // // DisplayVkLinux.h: // Defines the class interface for DisplayVkLinux, which is the base of DisplayVkSimple, -// DisplayVkHeadless and DisplayVkXcb. This base class implements the common functionality of -// handling Linux dma-bufs. +// DisplayVkHeadless, DisplayVkXcb and DisplayVkWayland. This base class implements the +// common functionality of handling Linux dma-bufs. // #ifndef LIBANGLE_RENDERER_VULKAN_DISPLAY_DISPLAYVKLINUX_H_ diff --git a/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.cpp b/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.cpp new file mode 100644 index 000000000..c5a3d7600 --- /dev/null +++ b/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.cpp @@ -0,0 +1,103 @@ +// +// Copyright 2021-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. +// +// DisplayVkWayland.cpp: +// Implements the class methods for DisplayVkWayland. +// + +#include "libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.h" + +#include + +#include "common/system_utils.h" +#include "libANGLE/Display.h" +#include "libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h" +#include "libANGLE/renderer/vulkan/vk_caps_utils.h" + +namespace rx +{ + +DisplayVkWayland::DisplayVkWayland(const egl::DisplayState &state) + : DisplayVkLinux(state), mWaylandDisplay(nullptr) +{} + +egl::Error DisplayVkWayland::initialize(egl::Display *display) +{ + mWaylandDisplay = reinterpret_cast(display->getNativeDisplayId()); + if (!mWaylandDisplay) + { + ERR() << "Failed to retrieve wayland display"; + return egl::EglNotInitialized(); + } + + return DisplayVk::initialize(display); +} + +void DisplayVkWayland::terminate() +{ + mWaylandDisplay = nullptr; + DisplayVk::terminate(); +} + +bool DisplayVkWayland::isValidNativeWindow(EGLNativeWindowType window) const +{ + // Wayland display Errors are fatal. + // If this function returns non-zero, the display is not valid anymore. + int error = wl_display_get_error(mWaylandDisplay); + if (error) + { + WARN() << "Wayland window is not valid: " << error << " " << strerror(error); + } + return error == 0; +} + +SurfaceImpl *DisplayVkWayland::createWindowSurfaceVk(const egl::SurfaceState &state, + EGLNativeWindowType window) +{ + return new WindowSurfaceVkWayland(state, window, mWaylandDisplay); +} + +egl::ConfigSet DisplayVkWayland::generateConfigs() +{ + const std::array kColorFormats = {GL_BGRA8_EXT}; + + std::vector depthStencilFormats( + egl_vk::kConfigDepthStencilFormats, + egl_vk::kConfigDepthStencilFormats + ArraySize(egl_vk::kConfigDepthStencilFormats)); + + if (getCaps().stencil8) + { + depthStencilFormats.push_back(GL_STENCIL_INDEX8); + } + return egl_vk::GenerateConfigs(kColorFormats.data(), kColorFormats.size(), + depthStencilFormats.data(), depthStencilFormats.size(), this); +} + +void DisplayVkWayland::checkConfigSupport(egl::Config *config) +{ + // In wayland there is no native visual ID or type +} + +const char *DisplayVkWayland::getWSIExtension() const +{ + return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; +} + +bool IsVulkanWaylandDisplayAvailable() +{ + wl_display *display = wl_display_connect(nullptr); + if (!display) + { + return false; + } + wl_display_disconnect(display); + return true; +} + +DisplayImpl *CreateVulkanWaylandDisplay(const egl::DisplayState &state) +{ + return new DisplayVkWayland(state); +} +} // namespace rx diff --git a/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.h b/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.h new file mode 100644 index 000000000..df1f2f65b --- /dev/null +++ b/src/libANGLE/renderer/vulkan/linux/wayland/DisplayVkWayland.h @@ -0,0 +1,44 @@ +// +// Copyright 2021-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. +// +// DisplayVkWayland.h: +// Defines the class interface for DisplayVkWayland, implementing DisplayVk for Wayland. +// + +#ifndef LIBANGLE_RENDERER_VULKAN_WAYLAND_DISPLAYVKWAYLAND_H_ +#define LIBANGLE_RENDERER_VULKAN_WAYLAND_DISPLAYVKWAYLAND_H_ + +#include "libANGLE/renderer/vulkan/linux/DisplayVkLinux.h" + +struct wl_display; + +namespace rx +{ + +class DisplayVkWayland : public DisplayVkLinux +{ + public: + DisplayVkWayland(const egl::DisplayState &state); + + egl::Error initialize(egl::Display *display) override; + void terminate() override; + + bool isValidNativeWindow(EGLNativeWindowType window) const override; + + SurfaceImpl *createWindowSurfaceVk(const egl::SurfaceState &state, + EGLNativeWindowType window) override; + + egl::ConfigSet generateConfigs() override; + void checkConfigSupport(egl::Config *config) override; + + const char *getWSIExtension() const override; + + private: + wl_display *mWaylandDisplay; +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_VULKAN_WAYLAND_DISPLAYVKWAYLAND_H_ diff --git a/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.cpp b/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.cpp new file mode 100644 index 000000000..964e7302f --- /dev/null +++ b/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.cpp @@ -0,0 +1,67 @@ +// +// Copyright 2021-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. +// +// WindowSurfaceVkWayland.cpp: +// Implements the class methods for WindowSurfaceVkWayland. +// + +#include "libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h" + +#include "libANGLE/renderer/vulkan/RendererVk.h" + +#include + +namespace rx +{ + +void WindowSurfaceVkWayland::ResizeCallback(wl_egl_window *eglWindow, void *payload) +{ + WindowSurfaceVkWayland *windowSurface = reinterpret_cast(payload); + + windowSurface->mExtents.width = eglWindow->width; + windowSurface->mExtents.height = eglWindow->height; +} + +WindowSurfaceVkWayland::WindowSurfaceVkWayland(const egl::SurfaceState &surfaceState, + EGLNativeWindowType window, + wl_display *display) + : WindowSurfaceVk(surfaceState, window), mWaylandDisplay(display) +{ + wl_egl_window *eglWindow = reinterpret_cast(window); + eglWindow->resize_callback = WindowSurfaceVkWayland::ResizeCallback; + eglWindow->driver_private = this; + + mExtents = gl::Extents(eglWindow->width, eglWindow->height, 1); +} + +angle::Result WindowSurfaceVkWayland::createSurfaceVk(vk::Context *context, gl::Extents *extentsOut) +{ + ANGLE_VK_CHECK(context, + vkGetPhysicalDeviceWaylandPresentationSupportKHR( + context->getRenderer()->getPhysicalDevice(), 0, mWaylandDisplay), + VK_ERROR_INITIALIZATION_FAILED); + + wl_egl_window *eglWindow = reinterpret_cast(mNativeWindowType); + + VkWaylandSurfaceCreateInfoKHR createInfo = {}; + + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.flags = 0; + createInfo.display = mWaylandDisplay; + createInfo.surface = eglWindow->surface; + ANGLE_VK_TRY(context, vkCreateWaylandSurfaceKHR(context->getRenderer()->getInstance(), + &createInfo, nullptr, &mSurface)); + + return getCurrentWindowSize(context, extentsOut); +} + +angle::Result WindowSurfaceVkWayland::getCurrentWindowSize(vk::Context *context, + gl::Extents *extentsOut) +{ + *extentsOut = mExtents; + return angle::Result::Continue; +} + +} // namespace rx diff --git a/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h b/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h new file mode 100644 index 000000000..e57ea3606 --- /dev/null +++ b/src/libANGLE/renderer/vulkan/linux/wayland/WindowSurfaceVkWayland.h @@ -0,0 +1,40 @@ +// +// Copyright 2021-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. +// +// WindowSurfaceVkWayland.h: +// Defines the class interface for WindowSurfaceVkWayland, implementing WindowSurfaceVk. +// + +#ifndef LIBANGLE_RENDERER_VULKAN_WAYLAND_WINDOWSURFACEVKWAYLAND_H_ +#define LIBANGLE_RENDERER_VULKAN_WAYLAND_WINDOWSURFACEVKWAYLAND_H_ + +#include "libANGLE/renderer/vulkan/SurfaceVk.h" + +struct wl_display; +struct wl_egl_window; + +namespace rx +{ + +class WindowSurfaceVkWayland : public WindowSurfaceVk +{ + public: + static void ResizeCallback(wl_egl_window *window, void *payload); + + WindowSurfaceVkWayland(const egl::SurfaceState &surfaceState, + EGLNativeWindowType window, + wl_display *display); + + private: + angle::Result createSurfaceVk(vk::Context *context, gl::Extents *extentsOut) override; + angle::Result getCurrentWindowSize(vk::Context *context, gl::Extents *extentsOut) override; + + wl_display *mWaylandDisplay; + gl::Extents mExtents; +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_VULKAN_WAYLAND_WINDOWSURFACEVKWAYLAND_H_ diff --git a/src/libANGLE/renderer/vulkan/linux/xcb/DisplayVkXcb.cpp b/src/libANGLE/renderer/vulkan/linux/xcb/DisplayVkXcb.cpp index e96b3decc..3ea9c53dc 100644 --- a/src/libANGLE/renderer/vulkan/linux/xcb/DisplayVkXcb.cpp +++ b/src/libANGLE/renderer/vulkan/linux/xcb/DisplayVkXcb.cpp @@ -147,6 +147,12 @@ const char *DisplayVkXcb::getWSIExtension() const bool IsVulkanXcbDisplayAvailable() { + Display *display = XOpenDisplay(nullptr); + if (!display) + { + return false; + } + XCloseDisplay(display); return true; } diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index b3adb9c72..e25a9628c 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -243,6 +243,9 @@ if (is_win || is_linux || is_chromeos || is_android || is_fuchsia || is_apple) { if (angle_use_x11) { sources += [ "egl_tests/EGLX11VisualTest.cpp" ] } + if (angle_use_wayland) { + sources += [ "egl_tests/EGLWaylandTest.cpp" ] + } configs += [ "${angle_root}:libANGLE_config" ] diff --git a/src/tests/egl_tests/EGLWaylandTest.cpp b/src/tests/egl_tests/EGLWaylandTest.cpp new file mode 100644 index 000000000..76b8a1135 --- /dev/null +++ b/src/tests/egl_tests/EGLWaylandTest.cpp @@ -0,0 +1,154 @@ +// +// 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. +// + +// EGLWaylandTest.cpp: tests for EGL_EXT_platform_wayland + +#include + +#include +#include +#include +#include + +#include "test_utils/ANGLETest.h" +#include "util/linux/wayland/WaylandWindow.h" + +using namespace angle; + +namespace +{ +const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; +} + +class EGLWaylandTest : public ANGLETest +{ + public: + std::vector getDisplayAttributes() const + { + std::vector attribs; + + attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + attribs.push_back(GetParam().getRenderer()); + attribs.push_back(EGL_NONE); + + return attribs; + } + + void testSetUp() override + { + mOsWindow = WaylandWindow::New(); + ASSERT_TRUE(mOsWindow->initialize("EGLWaylandTest", 500, 500)); + setWindowVisible(mOsWindow, true); + + EGLNativeDisplayType waylandDisplay = mOsWindow->getNativeDisplay(); + std::vector attribs = getDisplayAttributes(); + mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, (void *)waylandDisplay, + attribs.data()); + ASSERT_NE(EGL_NO_DISPLAY, mDisplay); + + ASSERT_TRUE(EGL_TRUE == eglInitialize(mDisplay, nullptr, nullptr)); + + int nConfigs = 0; + ASSERT_TRUE(EGL_TRUE == eglGetConfigs(mDisplay, nullptr, 0, &nConfigs)); + ASSERT_GE(nConfigs, 1); + + int nReturnedConfigs = 0; + mConfigs.resize(nConfigs); + ASSERT_TRUE(EGL_TRUE == + eglGetConfigs(mDisplay, mConfigs.data(), nConfigs, &nReturnedConfigs)); + ASSERT_EQ(nConfigs, nReturnedConfigs); + } + + void testTearDown() override + { + mConfigs.clear(); + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mDisplay); + OSWindow::Delete(&mOsWindow); + } + + OSWindow *mOsWindow; + EGLDisplay mDisplay; + std::vector mConfigs; +}; + +// Test that a Wayland window can be created and used for rendering +TEST_P(EGLWaylandTest, WaylandWindowRendering) +{ + for (EGLConfig config : mConfigs) + { + // Finally, try to do a clear on the window. + EGLContext context = eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, contextAttribs); + ASSERT_NE(EGL_NO_CONTEXT, context); + + EGLSurface window = + eglCreateWindowSurface(mDisplay, config, mOsWindow->getNativeWindow(), nullptr); + ASSERT_EGL_SUCCESS(); + + eglMakeCurrent(mDisplay, window, window, context); + ASSERT_EGL_SUCCESS(); + + glViewport(0, 0, 500, 500); + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(250, 250, 0, 0, 255, 255); + + // Teardown + eglDestroySurface(mDisplay, window); + ASSERT_EGL_SUCCESS(); + + eglDestroyContext(mDisplay, context); + ASSERT_EGL_SUCCESS(); + + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + ASSERT_EGL_SUCCESS(); + } +} + +// Test that a Wayland window can swap buffers multiple times with no issues +TEST_P(EGLWaylandTest, SwapBuffers) +{ + for (EGLConfig config : mConfigs) + { + EGLContext context = eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, contextAttribs); + ASSERT_NE(EGL_NO_CONTEXT, context); + + EGLSurface surface = + eglCreateWindowSurface(mDisplay, config, mOsWindow->getNativeWindow(), nullptr); + ASSERT_EGL_SUCCESS(); + + eglMakeCurrent(mDisplay, surface, surface, context); + ASSERT_EGL_SUCCESS(); + + const uint32_t loopcount = 16; + for (uint32_t i = 0; i < loopcount; i++) + { + mOsWindow->messageLoop(); + + glViewport(0, 0, 500, 500); + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ASSERT_GL_NO_ERROR() << "glClear failed"; + EXPECT_PIXEL_EQ(250, 250, 0, 0, 255, 255); + + eglSwapBuffers(mDisplay, surface); + ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed."; + } + + // Teardown + eglDestroySurface(mDisplay, surface); + ASSERT_EGL_SUCCESS(); + + eglDestroyContext(mDisplay, context); + ASSERT_EGL_SUCCESS(); + + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + ASSERT_EGL_SUCCESS(); + } +} + +ANGLE_INSTANTIATE_TEST(EGLWaylandTest, WithNoFixture(ES2_VULKAN())); diff --git a/src/tests/egl_tests/EGLX11VisualTest.cpp b/src/tests/egl_tests/EGLX11VisualTest.cpp index c3c072f86..0234d0ba1 100644 --- a/src/tests/egl_tests/EGLX11VisualTest.cpp +++ b/src/tests/egl_tests/EGLX11VisualTest.cpp @@ -14,7 +14,7 @@ #include "test_utils/ANGLETest.h" #include "util/OSWindow.h" -#include "util/x11/X11Window.h" +#include "util/linux/x11/X11Window.h" using namespace angle; diff --git a/util/BUILD.gn b/util/BUILD.gn index 0da1dc8cf..1036a0cba 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -51,13 +51,24 @@ if (is_linux) { if (angle_use_x11) { _util_sources += [ - "x11/X11Pixmap.cpp", - "x11/X11Pixmap.h", - "x11/X11Window.cpp", - "x11/X11Window.h", + "linux/x11/X11Pixmap.cpp", + "linux/x11/X11Pixmap.h", + "linux/x11/X11Window.cpp", + "linux/x11/X11Window.h", ] } +if (angle_use_wayland) { + _util_sources += [ + "linux/wayland/WaylandWindow.cpp", + "linux/wayland/WaylandWindow.h", + ] +} + +if (angle_use_x11 || angle_use_wayland) { + _util_sources += [ "linux/LinuxWindow.cpp" ] +} + if (is_fuchsia) { _util_sources += [ "fuchsia/FuchsiaPixmap.cpp", @@ -309,7 +320,12 @@ config("angle_test_util_config") { angle_source_set("angle_test_utils") { public_configs = [ ":angle_test_util_config" ] + public_deps = [ "$angle_root:angle_common" ] + if (angle_use_wayland) { + public_deps += [ "$angle_root:angle_wayland" ] + } + deps = [] sources = [ "Timer.cpp", diff --git a/util/display/DisplayWindow.cpp b/util/display/DisplayWindow.cpp index 6cd0053cb..423fc052b 100644 --- a/util/display/DisplayWindow.cpp +++ b/util/display/DisplayWindow.cpp @@ -79,7 +79,7 @@ void DisplayWindow::signalTestEvent() } // static -#if defined(ANGLE_USE_VULKAN_DISPLAY) && defined(EGL_NO_X11) +#if defined(ANGLE_USE_VULKAN_DISPLAY) && defined(EGL_NO_X11) && !defined(ANGLE_USE_WAYLAND) OSWindow *OSWindow::New() { return new DisplayWindow(); diff --git a/util/linux/LinuxWindow.cpp b/util/linux/LinuxWindow.cpp new file mode 100644 index 000000000..27d527107 --- /dev/null +++ b/util/linux/LinuxWindow.cpp @@ -0,0 +1,40 @@ +// +// 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. +// + +// LinuxWindow.cpp: Implementation of OSWindow::New for Linux + +#include "util/OSWindow.h" + +#if defined(ANGLE_USE_WAYLAND) +# include "wayland/WaylandWindow.h" +#endif + +#if defined(ANGLE_USE_X11) +# include "x11/X11Window.h" +#endif + +// static +#if defined(ANGLE_USE_X11) || defined(ANGLE_USE_WAYLAND) +OSWindow *OSWindow::New() +{ +# if defined(ANGLE_USE_X11) + // Prefer X11 + if (IsX11WindowAvailable()) + { + return new X11Window(); + } +# endif + +# if defined(ANGLE_USE_WAYLAND) + if (IsWaylandWindowAvailable()) + { + return new WaylandWindow(); + } +# endif + + return nullptr; +} +#endif diff --git a/util/linux/wayland/WaylandWindow.cpp b/util/linux/wayland/WaylandWindow.cpp new file mode 100644 index 000000000..52606e2a0 --- /dev/null +++ b/util/linux/wayland/WaylandWindow.cpp @@ -0,0 +1,173 @@ +// +// 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. +// + +// WaylandWindow.cpp: Implementation of OSWindow for Wayland + +#include "util/linux/wayland/WaylandWindow.h" + +WaylandWindow::WaylandWindow() + : mDisplay{nullptr}, mCompositor{nullptr}, mSurface{nullptr}, mWindow{nullptr} +{} + +WaylandWindow::~WaylandWindow() +{ + destroy(); +} + +void WaylandWindow::RegistryHandleGlobal(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + WaylandWindow *vc = reinterpret_cast(data); + + if (strcmp(interface, "wl_compositor") == 0) + { + void *compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + vc->mCompositor = reinterpret_cast(compositor); + } +} + +void WaylandWindow::RegistryHandleGlobalRemove(void *data, + struct wl_registry *registry, + uint32_t name) +{} + +const struct wl_registry_listener WaylandWindow::registryListener = { + WaylandWindow::RegistryHandleGlobal, WaylandWindow::RegistryHandleGlobalRemove}; + +bool WaylandWindow::initializeImpl(const std::string &name, int width, int height) +{ + destroy(); + + if (!mDisplay) + { + mDisplay = wl_display_connect(nullptr); + if (!mDisplay) + { + return false; + } + } + + // Not get a window + struct wl_registry *registry = wl_display_get_registry(mDisplay); + wl_registry_add_listener(registry, ®istryListener, this); + + // Round-trip to get globals + wl_display_roundtrip(mDisplay); + if (!mCompositor) + { + return false; + } + + // We don't need this anymore + wl_registry_destroy(registry); + + mSurface = wl_compositor_create_surface(mCompositor); + if (!mSurface) + { + return false; + } + + mWindow = wl_egl_window_create(mSurface, width, height); + if (!mWindow) + { + return false; + } + + fds[0] = {wl_display_get_fd(mDisplay), POLLIN, 0}; + + return true; +} + +void WaylandWindow::disableErrorMessageDialog() {} + +void WaylandWindow::destroy() +{ + if (mWindow) + { + wl_egl_window_destroy(mWindow); + mWindow = nullptr; + } + + if (mSurface) + { + wl_surface_destroy(mSurface); + mSurface = nullptr; + } + + if (mCompositor) + { + wl_compositor_destroy(mCompositor); + mCompositor = nullptr; + } +} + +void WaylandWindow::resetNativeWindow() {} + +EGLNativeWindowType WaylandWindow::getNativeWindow() const +{ + return reinterpret_cast(mWindow); +} + +EGLNativeDisplayType WaylandWindow::getNativeDisplay() const +{ + return reinterpret_cast(mDisplay); +} + +void WaylandWindow::messageLoop() +{ + while (wl_display_prepare_read(mDisplay) != 0) + wl_display_dispatch_pending(mDisplay); + if (wl_display_flush(mDisplay) < 0 && errno != EAGAIN) + { + wl_display_cancel_read(mDisplay); + return; + } + if (poll(fds, 1, 0) > 0) + { + wl_display_read_events(mDisplay); + wl_display_dispatch_pending(mDisplay); + } + else + { + wl_display_cancel_read(mDisplay); + } +} + +void WaylandWindow::setMousePosition(int x, int y) {} + +bool WaylandWindow::setOrientation(int width, int height) +{ + return true; +} + +bool WaylandWindow::setPosition(int x, int y) +{ + return true; +} + +bool WaylandWindow::resize(int width, int height) +{ + wl_egl_window_resize(mWindow, width, height, 0, 0); + return true; +} + +void WaylandWindow::setVisible(bool isVisible) {} + +void WaylandWindow::signalTestEvent() {} + +bool IsWaylandWindowAvailable() +{ + wl_display *display = wl_display_connect(nullptr); + if (!display) + { + return false; + } + wl_display_disconnect(display); + return true; +} diff --git a/util/linux/wayland/WaylandWindow.h b/util/linux/wayland/WaylandWindow.h new file mode 100644 index 000000000..95ed1bb5e --- /dev/null +++ b/util/linux/wayland/WaylandWindow.h @@ -0,0 +1,64 @@ +// +// 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. +// + +// WaylandWindow.h: Definition of the implementation of OSWindow for Wayland + +#ifndef UTIL_WAYLAND_WINDOW_H +#define UTIL_WAYLAND_WINDOW_H + +#include +#include +#include + +#include "util/OSWindow.h" +#include "util/util_export.h" + +bool IsWaylandWindowAvailable(); + +class ANGLE_UTIL_EXPORT WaylandWindow : public OSWindow +{ + public: + WaylandWindow(); + ~WaylandWindow() override; + + void disableErrorMessageDialog() override; + void destroy() override; + + void resetNativeWindow() override; + EGLNativeWindowType getNativeWindow() const override; + EGLNativeDisplayType getNativeDisplay() const override; + + void messageLoop() override; + + void setMousePosition(int x, int y) override; + bool setOrientation(int width, int height) override; + bool setPosition(int x, int y) override; + bool resize(int width, int height) override; + void setVisible(bool isVisible) override; + + void signalTestEvent() override; + + private: + static void RegistryHandleGlobal(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version); + static void RegistryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); + + bool initializeImpl(const std::string &name, int width, int height) override; + + static const struct wl_registry_listener registryListener; + + struct wl_display *mDisplay; + struct wl_compositor *mCompositor; + struct wl_surface *mSurface; + struct wl_egl_window *mWindow; + + struct pollfd fds[1]; +}; + +#endif // UTIL_WAYLAND_WINDOW_H diff --git a/util/x11/X11Pixmap.cpp b/util/linux/x11/X11Pixmap.cpp similarity index 97% rename from util/x11/X11Pixmap.cpp rename to util/linux/x11/X11Pixmap.cpp index 534e5a474..612bc7483 100644 --- a/util/x11/X11Pixmap.cpp +++ b/util/linux/x11/X11Pixmap.cpp @@ -6,7 +6,7 @@ // X11Pixmap.cpp: Implementation of OSPixmap for X11 -#include "util/x11/X11Pixmap.h" +#include "util/linux/x11/X11Pixmap.h" X11Pixmap::X11Pixmap() : mPixmap(0), mDisplay(nullptr) {} diff --git a/util/x11/X11Pixmap.h b/util/linux/x11/X11Pixmap.h similarity index 100% rename from util/x11/X11Pixmap.h rename to util/linux/x11/X11Pixmap.h diff --git a/util/x11/X11Window.cpp b/util/linux/x11/X11Window.cpp similarity index 98% rename from util/x11/X11Window.cpp rename to util/linux/x11/X11Window.cpp index 8cdb44896..ae479b925 100644 --- a/util/x11/X11Window.cpp +++ b/util/linux/x11/X11Window.cpp @@ -6,7 +6,7 @@ // X11Window.cpp: Implementation of OSWindow for X11 -#include "util/x11/X11Window.h" +#include "util/linux/x11/X11Window.h" #include "common/debug.h" #include "util/Timer.h" @@ -722,8 +722,13 @@ void X11Window::processEvent(const XEvent &xEvent) } } -// static -OSWindow *OSWindow::New() +bool IsX11WindowAvailable() { - return new X11Window(); + Display *display = XOpenDisplay(nullptr); + if (!display) + { + return false; + } + XCloseDisplay(display); + return true; } diff --git a/util/x11/X11Window.h b/util/linux/x11/X11Window.h similarity index 98% rename from util/x11/X11Window.h rename to util/linux/x11/X11Window.h index 0b502e4f3..4863819fc 100644 --- a/util/x11/X11Window.h +++ b/util/linux/x11/X11Window.h @@ -17,6 +17,8 @@ #include "util/OSWindow.h" #include "util/util_export.h" +bool IsX11WindowAvailable(); + class ANGLE_UTIL_EXPORT X11Window : public OSWindow { public: