diff --git a/BUILD.gn b/BUILD.gn index 6e8a001a1..b92e52fc2 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) { @@ -825,6 +838,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" ] } @@ -1014,6 +1031,38 @@ angle_source_set("angle_gl_enum_utils") { ] } +if (angle_use_wayland) { + config("angle_wayland_config") { + if (!defined(use_system_libwayland) || use_system_libwayland) { + 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(use_system_libwayland) && !use_system_libwayland) { + # Use chromium third-party targets + public_deps = [ + "//third_party/wayland:wayland_client", + "//third_party/wayland:wayland_egl", + ] + } + } +} + 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 e7463b446..9cf51921e 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/tests/BUILD.gn b/src/tests/BUILD.gn index a5a165dea..7521e8d06 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 a1581caac..a9401220c 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..b02b8a386 --- /dev/null +++ b/util/linux/wayland/WaylandWindow.cpp @@ -0,0 +1,182 @@ +// +// 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}; + + mY = 0; + mX = 0; + mWidth = width; + mHeight = height; + + 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); + + mWidth = width; + mHeight = height; + + 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: