From 6cb2222da8e34114b7b568dba7c2846c62f9ac49 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 9 Sep 2021 10:49:39 -0700 Subject: [PATCH] Linux native window (#3975) Initial implementation of Native Window for Linux Signed-off-by: Steve Pham --- .../Windowing/NativeWindow_Android.cpp | 1 + .../AzFramework/API/ApplicationAPI_Linux.h | 23 ++ .../Application/Application_Linux.cpp | 97 +------ .../Application/Application_Linux_xcb.cpp | 94 +++++++ .../Application/Application_Linux_xcb.h | 40 +++ .../Windowing/NativeWindow_Linux.cpp | 48 +--- .../Windowing/NativeWindow_Linux_xcb.cpp | 244 ++++++++++++++++++ .../Windowing/NativeWindow_Linux_xcb.h | 53 ++++ .../Platform/Linux/platform_linux_files.cmake | 4 + .../RHI/Code/Include/Atom/RHI/SwapChain.h | 6 +- Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp | 4 +- .../Vulkan/Code/Source/RHI/CommandQueue.cpp | 2 +- .../RHI/Vulkan/Code/Source/RHI/SwapChain.cpp | 12 + 13 files changed, 502 insertions(+), 126 deletions(-) create mode 100644 Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp create mode 100644 Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h create mode 100644 Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp create mode 100644 Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h diff --git a/Code/Framework/AzFramework/Platform/Android/AzFramework/Windowing/NativeWindow_Android.cpp b/Code/Framework/AzFramework/Platform/Android/AzFramework/Windowing/NativeWindow_Android.cpp index e655435011..5910111ab7 100644 --- a/Code/Framework/AzFramework/Platform/Android/AzFramework/Windowing/NativeWindow_Android.cpp +++ b/Code/Framework/AzFramework/Platform/Android/AzFramework/Windowing/NativeWindow_Android.cpp @@ -57,6 +57,7 @@ namespace AzFramework uint32_t NativeWindowImpl_Android::GetDisplayRefreshRate() const { + // [GFX TODO][GHI - 2678] // Using 60 for now until proper support is added return 60; } diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h index 9b57d1d49e..833f4019f8 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h @@ -55,6 +55,29 @@ namespace AzFramework using LinuxXcbConnectionManagerBus = AZ::EBus; using LinuxXcbConnectionManagerInterface = AZ::Interface; + + class LinuxXcbEventHandler + { + public: + AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}"); + + virtual ~LinuxXcbEventHandler() = default; + + virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; + }; + + class LinuxXcbEventHandlerBusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using LinuxXcbEventHandlerBus = AZ::EBus; #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 407e256052..5cc147b038 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -6,101 +6,28 @@ * */ -#include #include +#include "Application_Linux_xcb.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - class LinuxXcbConnectionManagerImpl - : public LinuxXcbConnectionManagerBus::Handler - { - public: - LinuxXcbConnectionManagerImpl() - { - m_xcbConnection = xcb_connect(nullptr, nullptr); - AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); - LinuxXcbConnectionManagerBus::Handler::BusConnect(); - } - - ~LinuxXcbConnectionManagerImpl() override - { - LinuxXcbConnectionManagerBus::Handler::BusDisconnect(); - xcb_disconnect(m_xcbConnection); - } - xcb_connection_t* GetXcbConnection() const override - { - return m_xcbConnection; - } - private: - xcb_connection_t* m_xcbConnection = nullptr; - }; -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - - //////////////////////////////////////////////////////////////////////////////////////////////// - class ApplicationLinux - : public Application::Implementation - , public LinuxLifecycleEvents::Bus::Handler - { - public: - //////////////////////////////////////////////////////////////////////////////////////////// - AZ_CLASS_ALLOCATOR(ApplicationLinux, AZ::SystemAllocator, 0); - ApplicationLinux(); - ~ApplicationLinux() override; - - //////////////////////////////////////////////////////////////////////////////////////////// - // Application::Implementation - void PumpSystemEventLoopOnce() override; - void PumpSystemEventLoopUntilEmpty() override; - private: - -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - AZStd::unique_ptr m_xcbConnectionManager; -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - - }; - //////////////////////////////////////////////////////////////////////////////////////////////// Application::Implementation* Application::Implementation::Create() { - return aznew ApplicationLinux(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux::ApplicationLinux() - { - LinuxLifecycleEvents::Bus::Handler::BusConnect(); - #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - m_xcbConnectionManager = AZStd::make_unique(); - if (LinuxXcbConnectionManagerInterface::Get() == nullptr) - { - LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); - } + return aznew ApplicationLinux_xcb(); +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND + #error "Linux Window Manager Wayland not supported." + return nullptr; +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB + #error "Linux Window Manager XLIB not supported." + return nullptr; +#else + #error "Linux Window Manager not recognized." + return nullptr; #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } - //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux::~ApplicationLinux() - { -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) - { - LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); - } - m_xcbConnectionManager.reset(); -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux::PumpSystemEventLoopOnce() - { - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux::PumpSystemEventLoopUntilEmpty() - { - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp new file mode 100644 index 0000000000..aaab67b2a1 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include "Application_Linux_xcb.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +namespace AzFramework +{ +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + //////////////////////////////////////////////////////////////////////////////////////////////// + class LinuxXcbConnectionManagerImpl + : public LinuxXcbConnectionManagerBus::Handler + { + public: + LinuxXcbConnectionManagerImpl() + { + m_xcbConnection = xcb_connect(nullptr, nullptr); + AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); + LinuxXcbConnectionManagerBus::Handler::BusConnect(); + } + + ~LinuxXcbConnectionManagerImpl() + { + LinuxXcbConnectionManagerBus::Handler::BusDisconnect(); + xcb_disconnect(m_xcbConnection); + } + + xcb_connection_t* GetXcbConnection() const override + { + return m_xcbConnection; + } + + private: + xcb_connection_t* m_xcbConnection = nullptr; + }; + + //////////////////////////////////////////////////////////////////////////////////////////////// + ApplicationLinux_xcb::ApplicationLinux_xcb() + { + LinuxLifecycleEvents::Bus::Handler::BusConnect(); + m_xcbConnectionManager = AZStd::make_unique(); + if (LinuxXcbConnectionManagerInterface::Get() == nullptr) + { + LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + ApplicationLinux_xcb::~ApplicationLinux_xcb() + { + if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) + { + LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); + } + m_xcbConnectionManager.reset(); + LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void ApplicationLinux_xcb::PumpSystemEventLoopOnce() + { + if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) + { + if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) + { + LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event); + free(event); + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty() + { + if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) + { + while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) + { + LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event); + free(event); + } + } + } + +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h new file mode 100644 index 0000000000..55daedb4dd --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////// +namespace AzFramework +{ + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + + //////////////////////////////////////////////////////////////////////////////////////////////// + class ApplicationLinux_xcb + : public Application::Implementation + , public LinuxLifecycleEvents::Bus::Handler + { + public: + //////////////////////////////////////////////////////////////////////////////////////////// + AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0); + ApplicationLinux_xcb(); + ~ApplicationLinux_xcb() override; + + //////////////////////////////////////////////////////////////////////////////////////////// + // Application::Implementation + void PumpSystemEventLoopOnce() override; + void PumpSystemEventLoopUntilEmpty() override; + + private: + AZStd::unique_ptr m_xcbConnectionManager; + }; + +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp index 0ab1281eda..436be28ee6 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp @@ -6,48 +6,24 @@ * */ -#include +#include "NativeWindow_Linux_xcb.h" namespace AzFramework { - class NativeWindowImpl_Linux final - : public NativeWindow::Implementation - { - public: - AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux, AZ::SystemAllocator, 0); - NativeWindowImpl_Linux() = default; - ~NativeWindowImpl_Linux() override = default; - - // NativeWindow::Implementation overrides... - void InitWindow(const AZStd::string& title, - const WindowGeometry& geometry, - const WindowStyleMasks& styleMasks) override; - NativeWindowHandle GetWindowHandle() const override; - uint32_t GetDisplayRefreshRate() const override; - }; - NativeWindow::Implementation* NativeWindow::Implementation::Create() { - return aznew NativeWindowImpl_Linux(); - } - - void NativeWindowImpl_Linux::InitWindow([[maybe_unused]]const AZStd::string& title, - const WindowGeometry& geometry, - [[maybe_unused]]const WindowStyleMasks& styleMasks) - { - m_width = geometry.m_width; - m_height = geometry.m_height; - } - - NativeWindowHandle NativeWindowImpl_Linux::GetWindowHandle() const - { - AZ_Assert(false, "NativeWindow not implemented for Linux"); +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + return aznew NativeWindowImpl_Linux_xcb(); +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND + #error "Linux Window Manager Wayland not supported." return nullptr; +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB + #error "Linux Window Manager XLIB not supported." + return nullptr; +#else + #error "Linux Window Manager not recognized." + return nullptr; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } - uint32_t NativeWindowImpl_Linux::GetDisplayRefreshRate() const - { - //Using 60 for now until proper support is added - return 60; - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp new file mode 100644 index 0000000000..005c1858a3 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include + +#include "NativeWindow_Linux_xcb.h" + +namespace AzFramework +{ +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + + [[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb"; + static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages + static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified + static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event + + //////////////////////////////////////////////////////////////////////////////////////////////// + NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb() + : NativeWindow::Implementation() + { + if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + xcbConnectionManager != nullptr) + { + m_xcbConnection = xcbConnectionManager->GetXcbConnection(); + } + AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection"); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb() + { + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title, + const WindowGeometry& geometry, + const WindowStyleMasks& styleMasks) + { + // Get the parent window + const xcb_setup_t* xcbSetup = xcb_get_setup(m_xcbConnection); + xcb_screen_t* xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data; + xcb_window_t xcbParentWindow = xcbRootScreen->root; + + // Create an XCB window from the connection + m_xcbWindow = xcb_generate_id(m_xcbConnection); + + uint16_t borderWidth = 0; + const uint32_t mask = styleMasks.m_platformAgnosticStyleMask; + if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) || + (mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE)) + { + borderWidth = s_DefaultXcbWindowBorderWidth; + } + + uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + + uint32_t valueList[] = { xcbRootScreen->black_pixel, + XCB_EVENT_MASK_STRUCTURE_NOTIFY }; + + xcb_void_cookie_t xcbCheckResult; + + xcbCheckResult = xcb_create_window_checked(m_xcbConnection, + XCB_COPY_FROM_PARENT, + m_xcbWindow, + xcbParentWindow, + aznumeric_cast(geometry.m_posX), + aznumeric_cast(geometry.m_posY), + aznumeric_cast(geometry.m_width), + aznumeric_cast(geometry.m_height), + borderWidth, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + xcbRootScreen->root_visual, + eventMask, + valueList); + + AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to create xcb window."); + + SetWindowTitle(title); + + // Setup the window close event + const static char* wmProtocolString = "WM_PROTOCOLS"; + + xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString); + xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr); + AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString); + m_xcbAtomProtocols = replyProtocol->atom; + + const static char* wmDeleteWindowString = "WM_DELETE_WINDOW"; + xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString); + xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr); + AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString); + m_xcbAtomDeleteWindow = replyDeleteWindow->atom; + + xcbCheckResult = xcb_change_property_checked(m_xcbConnection, + XCB_PROP_MODE_REPLACE, + m_xcbWindow, + m_xcbAtomProtocols, + XCB_ATOM_ATOM, + s_XcbFormatDataSize, + 1, + &m_xcbAtomDeleteWindow); + + AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to change the xcb atom property for WM_CLOSE event"); + + m_width = geometry.m_width; + m_height = geometry.m_height; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::Activate() + { + LinuxXcbEventHandlerBus::Handler::BusConnect(); + + if (!m_activated) // nothing to do if window was already activated + { + m_activated = true; + + xcb_map_window(m_xcbConnection, m_xcbWindow); + xcb_flush(m_xcbConnection); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::Deactivate() + { + if (m_activated) // nothing to do if window was already deactivated + { + m_activated = false; + + WindowNotificationBus::Event(reinterpret_cast(m_xcbWindow), &WindowNotificationBus::Events::OnWindowClosed); + + xcb_unmap_window(m_xcbConnection, m_xcbWindow); + xcb_flush(m_xcbConnection); + } + LinuxXcbEventHandlerBus::Handler::BusDisconnect(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const + { + return reinterpret_cast(m_xcbWindow); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title) + { + xcb_void_cookie_t xcbCheckResult; + xcbCheckResult = xcb_change_property(m_xcbConnection, + XCB_PROP_MODE_REPLACE, + m_xcbWindow, + XCB_ATOM_WM_NAME, + XCB_ATOM_STRING, + 8, + static_cast(title.size()), + title.c_str()); + AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title."); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize) + { + const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height }; + + xcb_configure_window(m_xcbConnection, m_xcbWindow, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); + + m_width = clientAreaSize.m_width; + m_height = clientAreaSize.m_height; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const + { + // [GFX TODO][GHI - 2678] + // Using 60 for now until proper support is added + return 60; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie) + { + bool result = true; + if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie)) + { + AZ_TracePrintf("Error","Error code %d", error->error_code); + result = false; + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event) + { + switch (event->response_type & s_XcbResponseTypeMask) + { + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t* cne = reinterpret_cast(event); + WindowSizeChanged(aznumeric_cast(cne->width), + aznumeric_cast(cne->height)); + + break; + } + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t* cme = reinterpret_cast(event); + if ((cme->type == m_xcbAtomProtocols) && + (cme->format == s_XcbFormatDataSize) && + (cme->data.data32[0] == m_xcbAtomDeleteWindow)) + { + Deactivate(); + + ApplicationRequests::Bus::Broadcast(&ApplicationRequests::ExitMainLoop); + } + break; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height) + { + if (m_width != width || m_height != height) + { + m_width = width; + m_height = height; + + if (m_activated) + { + WindowNotificationBus::Event(reinterpret_cast(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height); + } + } + } + +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h new file mode 100644 index 0000000000..73e255bab0 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include + +namespace AzFramework +{ +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + class NativeWindowImpl_Linux_xcb final + : public NativeWindow::Implementation + , public LinuxXcbEventHandlerBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0); + NativeWindowImpl_Linux_xcb(); + ~NativeWindowImpl_Linux_xcb() override; + + //////////////////////////////////////////////////////////////////////////////////////////// + // NativeWindow::Implementation + void InitWindow(const AZStd::string& title, + const WindowGeometry& geometry, + const WindowStyleMasks& styleMasks) override; + void Activate() override; + void Deactivate() override; + NativeWindowHandle GetWindowHandle() const override; + void SetWindowTitle(const AZStd::string& title) override; + void ResizeClientArea(WindowSize clientAreaSize) override; + uint32_t GetDisplayRefreshRate() const override; + + //////////////////////////////////////////////////////////////////////////////////////////// + // LinuxXcbEventHandlerBus::Handler + void HandleXcbEvent(xcb_generic_event_t* event) override; + + private: + bool ValidateXcbResult(xcb_void_cookie_t cookie); + void WindowSizeChanged(const uint32_t width, const uint32_t height); + + xcb_connection_t* m_xcbConnection = nullptr; + xcb_window_t m_xcbWindow = 0; + xcb_atom_t m_xcbAtomProtocols; + xcb_atom_t m_xcbAtomDeleteWindow; + }; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake index 21201d954d..4330675fc7 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake @@ -12,6 +12,8 @@ set(FILES AzFramework/API/ApplicationAPI_Platform.h AzFramework/API/ApplicationAPI_Linux.h AzFramework/Application/Application_Linux.cpp + AzFramework/Application/Application_Linux_xcb.h + AzFramework/Application/Application_Linux_xcb.cpp AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp AzFramework/Process/ProcessWatcher_Linux.cpp AzFramework/Process/ProcessCommon.h @@ -20,6 +22,8 @@ set(FILES ../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp ../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp AzFramework/Windowing/NativeWindow_Linux.cpp + AzFramework/Windowing/NativeWindow_Linux_xcb.h + AzFramework/Windowing/NativeWindow_Linux_xcb.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Unimplemented.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h index c1fd4453d4..42ba2d2e25 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h @@ -83,11 +83,11 @@ namespace AZ #if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) // On Linux platforms that uses XCB, a resize may occur in the swap chain but the command queue may still - // reference the original surface. This flag is a temporary fix to make sure that all the swap chains - // have finished their resize events before presenting the command queue. + // reference the original surface. This flag is a temporary fix to make sure the swap chain is ready to present + // We need to remove this work around with // [GFX TODO][GHI - 2678] - AZStd::atomic_bool m_resized{ false }; + AZStd::atomic_bool m_readyToPresent { false }; #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB protected: diff --git a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp index 5fbb83fccf..92d7a125d8 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp @@ -165,7 +165,9 @@ namespace AZ } #if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - m_resized.store(true); + // If we are presenting through the editor, the resize is triggered through the editor's window, which + // won't happen until after the surface is ready to present + m_readyToPresent.store(true); #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB return resultCode; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp index dc52e530e0..e4d8212228 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp @@ -45,7 +45,7 @@ namespace AZ #if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) for (RHI::SwapChain* swapChain : rhiRequest.m_swapChainsToPresent) { - if (!swapChain->m_resized) + if (!swapChain->m_readyToPresent) { return; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp index 0ae3ae1114..e1d3e8c060 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,17 @@ namespace AZ nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); } +#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) + // When launching in game mode, the surface will be ready at this point, meaning that after + // intialization, this swap chain is ready to present + AZ::ApplicationTypeQuery appType; + ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); + if (appType.IsGame()) + { + m_readyToPresent.store(true); + } +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + SetName(GetName()); return result; }