From 54f7f1dc530d84ada4de28652e9d4c77fd95b414 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Tue, 24 Dec 2024 18:18:50 +0100 Subject: [PATCH 1/9] Wayland: initialize surface and render to video --- CMakeLists.txt | 1 + Samples/Example_ImGui/Example_ImGui.cpp | 3 +- .../Example_ImGui_Docking.cpp | 21 +- Samples/Tests/main_SDL2.cpp | 47 ++- WickedEngine/.kdev4/WickedEngine.kdev4 | 19 + WickedEngine/CMakeLists.txt | 81 +++- WickedEngine/Wayland/wiWaylandBackend.cpp | 141 +++++++ WickedEngine/Wayland/wiWaylandBackend.h | 80 ++++ WickedEngine/Wayland/wiWaylandUtils.h | 15 + WickedEngine/Wayland/wiWaylandWindow.cpp | 215 +++++++++++ WickedEngine/Wayland/wiWaylandWindow.h | 71 ++++ WickedEngine/WickedEngine.cppm | 83 ++++ WickedEngine/WickedEngine.kdev4 | 4 + WickedEngine/gamemode_client.h | 365 ++++++++++++++++++ WickedEngine/wiApplication.cpp | 5 +- WickedEngine/wiApplication.h | 8 +- WickedEngine/wiGraphicsDevice_Vulkan.cpp | 65 ++-- WickedEngine/wiGraphicsDevice_Vulkan.h | 3 + WickedEngine/wiInput.cpp | 46 +-- WickedEngine/wiPlatform.h | 37 +- 20 files changed, 1236 insertions(+), 74 deletions(-) create mode 100644 WickedEngine/.kdev4/WickedEngine.kdev4 create mode 100644 WickedEngine/Wayland/wiWaylandBackend.cpp create mode 100644 WickedEngine/Wayland/wiWaylandBackend.h create mode 100644 WickedEngine/Wayland/wiWaylandUtils.h create mode 100644 WickedEngine/Wayland/wiWaylandWindow.cpp create mode 100644 WickedEngine/Wayland/wiWaylandWindow.h create mode 100644 WickedEngine/WickedEngine.cppm create mode 100644 WickedEngine/WickedEngine.kdev4 create mode 100644 WickedEngine/gamemode_client.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f6129ef853..6b23c6b9f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ include(CMakeDependentOption) if(UNIX) option(WICKED_LINUX_TEMPLATE "Build WickedEngine Linux template" ON) + option(WAYLAND_BACKEND "Build WickedEngine Wayland backend" ON) elseif(WIN32) option(WICKED_WINDOWS_TEMPLATE "Build WickedEngine Windows template" ON) endif() diff --git a/Samples/Example_ImGui/Example_ImGui.cpp b/Samples/Example_ImGui/Example_ImGui.cpp index 8775afc658..34b2ece320 100644 --- a/Samples/Example_ImGui/Example_ImGui.cpp +++ b/Samples/Example_ImGui/Example_ImGui.cpp @@ -132,7 +132,8 @@ void Example_ImGui::Initialize() #ifdef _WIN32 ImGui_ImplWin32_Init(window); #elif defined(SDL2) - ImGui_ImplSDL2_InitForVulkan(window); + IM_ASSERT(window.type == wi::platform::LinuxWindow::eSDLWindow); + ImGui_ImplSDL2_InitForVulkan(window.sdl_window); #endif IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); diff --git a/Samples/Example_ImGui_Docking/Example_ImGui_Docking.cpp b/Samples/Example_ImGui_Docking/Example_ImGui_Docking.cpp index f8c9709ca8..99ab1c34d7 100644 --- a/Samples/Example_ImGui_Docking/Example_ImGui_Docking.cpp +++ b/Samples/Example_ImGui_Docking/Example_ImGui_Docking.cpp @@ -23,10 +23,10 @@ #endif -#include "../WickedEngine/wiProfiler.h" -#include "../WickedEngine/wiBacklog.h" -#include "../WickedEngine/wiPrimitive.h" -#include "../WickedEngine/wiRenderPath3D.h" +#include "wiProfiler.h" +#include "wiBacklog.h" +#include "wiPrimitive.h" +#include "wiRenderPath3D.h" #include "../Editor/ModelImporter.h" #include @@ -174,7 +174,8 @@ void Example_ImGui::Initialize() hWnd = window; ImGui_ImplWin32_Init(window); #elif defined(SDL2) - ImGui_ImplSDL2_InitForVulkan(window); + IM_ASSERT(window.type == wi::platform::LinuxWindow::eSDLWindow); + ImGui_ImplSDL2_InitForVulkan(window.sdl_window); #endif IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); @@ -457,7 +458,7 @@ void Example_ImGuiRenderer::Update(float dt) ImGui_Impl_CreateDeviceObjects(); } - + #ifdef _WIN32 ImGui_ImplWin32_NewFrame(); #elif defined(SDL2) @@ -681,7 +682,7 @@ void Example_ImGuiRenderer::Update(float dt) bool bUseChild = false; if (use_fixed_height > 0) bUseChild = true; if(bUseChild) ImGui::BeginChild("##objectsc", ImVec2(0.0f, use_fixed_height), false, ImGuiWindowFlags_None); //ImGuiWindowFlags_AlwaysVerticalScrollbar - + for (int i = 0; i < size; i++) { Entity e = scene.objects.GetEntity(i); @@ -1029,7 +1030,7 @@ void Example_ImGuiRenderer::Update(float dt) ImGui::EndTabBar(); } } - + if (ImGui::CollapsingHeader(ICON_MD_SETTINGS " Settings", ImGuiTreeNodeFlags_None)) //ImGuiTreeNodeFlags_DefaultOpen { ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(0.0f, 3.0f)); @@ -1390,7 +1391,7 @@ void Example_ImGuiRenderer::Update(float dt) if (ImGui::Selectable(lua_history[i].c_str(), is_selected)) { #ifdef _WIN32 strcpy_s(lua, lua_history[i].c_str()); - #elif __linux__ + #elif __linux__ strcpy(lua, lua_history[i].c_str()); #endif bSetKeyBoardFocus = true; @@ -1489,7 +1490,7 @@ void Example_ImGuiRenderer::Update(float dt) } float movespeed = CAMERAMOVESPEED; - + if (imgui_io.KeyShift) { movespeed *= 3.0; //Speed up camera. diff --git a/Samples/Tests/main_SDL2.cpp b/Samples/Tests/main_SDL2.cpp index 7d265262c9..0b1d5c20a6 100644 --- a/Samples/Tests/main_SDL2.cpp +++ b/Samples/Tests/main_SDL2.cpp @@ -3,6 +3,9 @@ #include "stdafx.h" #include "sdl2.h" +#include "Wayland/wiWaylandBackend.h" +#include "Wayland/wiWaylandWindow.h" +#include int sdl_loop(Tests &tests) { @@ -61,7 +64,7 @@ int sdl_loop(Tests &tests) return 0; } -int main(int argc, char *argv[]) +int main_sdl2(int argc, char *argv[]) { Tests tests; // TODO: Place code here. @@ -89,3 +92,45 @@ int main(int argc, char *argv[]) SDL_Quit(); return ret; } + + +void wayland_loop(wi::wayland::Backend &wwbackend, Tests &tests) +{ + bool quit = false; + wwbackend.window.on_close = [&quit](wi::wayland::Window*w) { quit = false; }; + + while(!quit) + { + tests.Run(); + if (wwbackend.dispatch() < 0) + break; + if (wwbackend.window.AcceptDesiredDimensions()) + tests.SetWindow(&wwbackend.window); + } +} + + +bool main_wayland(int argc, char *argv[]) +{ + wi::wayland::Backend wwbackend; + if (!wwbackend.Init()) + return false; + if (!wwbackend.CreateWindow("Wicked Engine Tests")) + return false; + + Tests tests; + tests.SetWindow(&wwbackend.window); + wayland_loop(wwbackend, tests); + + wwbackend.window.Deinit(); + wwbackend.DeInit(); + + return true; +} + +int main(int argc, char *argv[]) +{ + // Attempt to initialize wayland backend + if (!main_wayland(argc, argv)) + main_sdl2(argc, argv); +} diff --git a/WickedEngine/.kdev4/WickedEngine.kdev4 b/WickedEngine/.kdev4/WickedEngine.kdev4 new file mode 100644 index 0000000000..686b9b0e10 --- /dev/null +++ b/WickedEngine/.kdev4/WickedEngine.kdev4 @@ -0,0 +1,19 @@ +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x18\x00W\x00i\x00c\x00k\x00e\x00d\x00E\x00n\x00g\x00i\x00n\x00e) + +[CMake] +Build Directory Count=1 +Current Build Directory Index-Host System=0 + +[CMake][CMake Build Directory 0] +Build Directory Path=/home/matteo/projects/WickedEngine/WickedEngine/cmake-build-debug +Build Type=Debug +CMake Binary=/usr/bin/cmake +CMake Executable=/usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory=/usr/local +Runtime=Host System + +[Project] +VersionControlSupport=kdevgit diff --git a/WickedEngine/CMakeLists.txt b/WickedEngine/CMakeLists.txt index d890e3893e..e0c8e73568 100644 --- a/WickedEngine/CMakeLists.txt +++ b/WickedEngine/CMakeLists.txt @@ -47,6 +47,54 @@ else () if((${SDL_VERSION_MAJOR} GREATER_EQUAL 2) AND (${SDL2_VERSION_MINOR} GREATER_EQUAL 0) AND (${SDL2_VERSION_PATCH} GREATER_EQUAL 14)) add_compile_definitions(SDL2_FEATURE_CONTROLLER_LED=1) endif() + + if (WAYLAND_BACKEND) + + find_package(PkgConfig REQUIRED) + pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client) + pkg_check_modules(WAYLAND_SCANNER REQUIRED wayland-scanner) + pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols) + # Get variables + pkg_get_variable(WAYLAND_SCANNER_BIN wayland-scanner wayland_scanner) + pkg_get_variable(WAYLAND_PROTOCOLS_DATADIR wayland-protocols pkgdatadir) + ################################################################################################################ + # Wayland protocol generated code + function(GENERATE_XDG_PROTOCOLS) + # GENERATE XDG PROTOCOLS SOURCES + set(XDG_PROTOCOLS_SOURCES) + set(XDG_PROTOCOLS_HEADERS) + foreach(XML ${ARGN}) + get_filename_component(XML_NAME "${XML}" NAME_WE) + MESSAGE("Generating C source code for ${XML} -> ${XML_NAME}") + add_custom_command(COMMAND ${WAYLAND_SCANNER_BIN} + ARGS private-code "${XML}" "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.c" + DEPENDS "${XML}" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.c" + COMMENT "Generating \"${XML_NAME}\" protocol private code." + ) + list(APPEND XDG_PROTOCOLS_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.c") + + add_custom_command(COMMAND ${WAYLAND_SCANNER_BIN} + ARGS client-header "${XML}" "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.h" + DEPENDS "${XML}" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.h" + COMMENT "Generating \"${XML_NAME}\" protocol header." + ) + list(APPEND XDG_PROTOCOLS_HEADERS "${CMAKE_CURRENT_BINARY_DIR}/${XML_NAME}.h") + set(XDG_PROTOCOLS_SOURCES ${XDG_PROTOCOLS_SOURCES} PARENT_SCOPE) + set(XDG_PROTOCOLS_HEADERS ${XDG_PROTOCOLS_HEADERS} PARENT_SCOPE) + endforeach() + endfunction() + + GENERATE_XDG_PROTOCOLS( + "${WAYLAND_PROTOCOLS_DATADIR}/stable/xdg-shell/xdg-shell.xml" + "${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + ) + file(GLOB WAYLAND_SOURCES CONFIGURE_DEPENDS Wayland/*.cpp) + file(GLOB WAYLAND_HEADERS CONFIGURE_DEPENDS Wayland/*.h) + set(WAYLAND_SOURCES ${WAYLAND_SOURCES} ${XDG_PROTOCOLS_SOURCES}) + set(WAYLAND_HEADERS ${WAYLAND_HEADERS} ${XDG_PROTOCOLS_HEADERS}) + endif() endif() add_subdirectory(LUA) @@ -64,6 +112,8 @@ list(REMOVE_ITEM SOURCE_FILES offlineshadercompiler.cpp) add_library(${TARGET_NAME} ${WICKED_LIBRARY_TYPE} ${SOURCE_FILES} ${HEADER_FILES} + ${WAYLAND_SOURCES} + ${WAYLAND_HEADERS} ) if(MSVC) @@ -71,7 +121,7 @@ if(MSVC) endif() add_library(WickedEngine ALIAS ${TARGET_NAME}) -set_target_properties(${TARGET_NAME} PROPERTIES PUBLIC_HEADER "${HEADER_FILES}") +set_target_properties(${TARGET_NAME} PROPERTIES PUBLIC_HEADER "${HEADER_FILES} ${WAYLAND_HEADERS}") target_include_directories(${TARGET_NAME} SYSTEM PUBLIC $ @@ -106,12 +156,15 @@ else () if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(${TARGET_NAME} PRIVATE + # C and C++ warnings -Wuninitialized #-Wwrite-strings #-Winit-self #-Wreturn-type - #-Wreorder - #-Werror=delete-non-virtual-dtor + # C++ only warnings + #$<$:-Wreorder> + #$<$:-Werror=delete-non-virtual-dtor> + # Treat warnings as errors #-Werror #uncomment this to stop the compilation at the first error # -Wfatal-errors @@ -120,19 +173,37 @@ else () # add some warnings and set them as errors # read more details here: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html target_compile_options(${TARGET_NAME} PRIVATE + # C and C++ warnings -Wuninitialized -Wmaybe-uninitialized -Wwrite-strings -Winit-self -Wreturn-type - -Wreorder - -Werror=delete-non-virtual-dtor + # C++ only warnings + $<$:-Wreorder> + $<$:-Werror=delete-non-virtual-dtor> + # Treat warnings as errors -Werror #uncomment this to stop the compilation at the first error # -Wfatal-errors ) endif() + + if (WAYLAND_BACKEND) + target_include_directories(${TARGET_NAME} + PUBLIC ${WAYLAND_CLIENT_INCLUDE_DIRS} + # PRIVATE ${WAYLAND_CURSOR_INCLUDE_DIRS} + PUBLIC $ + # PUBLIC $ + ) + + target_link_libraries(${TARGET_NAME} + PUBLIC ${WAYLAND_CLIENT_LIBRARIES} + # PRIVATE ${WAYLAND_CURSOR_LIBRARIES} + ) + endif() + target_link_libraries(${TARGET_NAME} PRIVATE dl) set(LIBDXCOMPILER "libdxcompiler.so") diff --git a/WickedEngine/Wayland/wiWaylandBackend.cpp b/WickedEngine/Wayland/wiWaylandBackend.cpp new file mode 100644 index 0000000000..bb45cc660f --- /dev/null +++ b/WickedEngine/Wayland/wiWaylandBackend.cpp @@ -0,0 +1,141 @@ +#include "wiWaylandBackend.h" +#include "wiWaylandUtils.h" +#include +#include +#include +#include + +#include "xdg-shell.h" +#include "xdg-decoration-unstable-v1.h" + + +using namespace wi::wayland; + +/// functions callbacks - START +extern "C" void registry_global(void *data, wl_registry *raw_registry, uint32_t id, const char* interface, uint32_t version) +{ + Backend *backend = static_cast(data); + backend->registry_event_add(id, interface, version); +} + +extern "C" void registry_global_remove(void *data, wl_registry *raw_registry, uint32_t id) +{ + Backend *backend = static_cast(data); + backend->registry_event_remove(id); +} + +extern "C" void window_manager_pong(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + Backend *backend = static_cast(data); + backend->window_manager_pong(serial); +} + +/// functions callbacks - END + +bool Backend::Init() +{ + const char* name = nullptr; + display = wl_display_connect(name); + if (display == nullptr) {std::cerr<<"wl_display_connect failed"<registry_listener, this); + + dispatch(); + roundtrip(); + + return true; +} + +bool Backend::registry_has(const char* interface_name) const +{ + return registry_map.find(interface_name) != registry_map.end(); +} + +bool Backend::bind_compositor() +{ + compositor = bind("wl_compositor", &wl_compositor_interface); + return compositor != nullptr; +} + +bool Backend::bind_xdg_wm_base() +{ + window_manager = this->bind("xdg_wm_base", &xdg_wm_base_interface); + return window_manager != nullptr; +} + +bool Backend::bind_decoration_manager() +{ + decoration_manager = this->bind( + "zxdg_decoration_manager_v1", &zxdg_decoration_manager_v1_interface); + return window_manager != nullptr; +} + +bool Backend::init_window_manager() +{ + window_manager_listener.ping = &::window_manager_pong; + xdg_wm_base_add_listener(window_manager, &window_manager_listener, this); + return true; +} + +void Backend::DeInit() +{ + safe_delete(decoration_manager, zxdg_decoration_manager_v1_destroy); + safe_delete(window_manager, xdg_wm_base_destroy); + safe_delete(compositor, wl_compositor_destroy); + // registry needs to be killed before, otherwhise the application will crash + safe_delete(registry, wl_registry_destroy); + safe_delete(display, wl_display_disconnect); +} + +bool Backend::CreateWindow(const char* title) +{ + return window.Init(this, title); +} + +int Backend::dispatch() +{ + return wl_display_dispatch(display); +} + +int Backend::roundtrip() +{ + return wl_display_roundtrip(display); +} + +void Backend::registry_event_add(uint32_t id, const char* interface, uint32_t version) +{ + std::cout << "Registry has " << interface << std::endl; + registry_map[interface] = std::make_pair(id, version); +} + +void Backend::registry_event_remove(uint32_t id) +{ + for (auto it = registry_map.begin(); it != registry_map.end(); ++it) { + if (it->second.first == id) { + registry_map.erase(it); + return; + } + } + std::clog << "Registry removal failed, not found: " << id << std::endl; +} + +void Backend::window_manager_pong(uint32_t serial) +{ + xdg_wm_base_pong(window_manager, serial); +} diff --git a/WickedEngine/Wayland/wiWaylandBackend.h b/WickedEngine/Wayland/wiWaylandBackend.h new file mode 100644 index 0000000000..ae8f297790 --- /dev/null +++ b/WickedEngine/Wayland/wiWaylandBackend.h @@ -0,0 +1,80 @@ +#pragma once +#include "wiWaylandWindow.h" +#include +#include +#include +#include + +#include "xdg-decoration-unstable-v1.h" +#include "xdg-shell.h" + +extern "C" { +struct xdg_wm_base; +struct zxdg_decoration_manager_v1; +} + +namespace wi::wayland { + +class Window; + +class Backend { +private: + wl_display* display = nullptr; + wl_registry* registry = nullptr; + wl_registry_listener registry_listener; + // interface name -> id, version + std::map> registry_map; + + wl_compositor* compositor = nullptr; + xdg_wm_base* window_manager = nullptr; + xdg_wm_base_listener window_manager_listener; + zxdg_decoration_manager_v1 *decoration_manager = nullptr; + +public: + Window window; + +public: + Backend() = default; + bool Init(); + ~Backend() { DeInit(); } + void DeInit(); + + bool CreateWindow(const char* title); + + int dispatch(); + int roundtrip(); + + // callbacks + void registry_event_add(uint32_t id, const char* interface, uint32_t version); + void registry_event_remove(uint32_t id); + void window_manager_pong(uint32_t serial); + +private: + bool init_registry(); + bool registry_has(const char* interface_name) const; + + template + T* bind(const char* interface_name, const struct wl_interface *interface, uint32_t version = -1) + { + std::pair reg = registry_map.at(interface_name); + if (version < 0) { + version = reg.second; + } else { + version = 1; + } + void* r = wl_registry_bind(registry, reg.first, interface, version); + if (r == nullptr) { + std::clog<< "ERROR loading interface \"" << interface_name << '"' << std::endl; + } + return static_cast(r); + } + bool bind_compositor(); + bool bind_xdg_wm_base(); + bool bind_decoration_manager(); + + bool init_window_manager(); + +friend class Window; +}; + +} diff --git a/WickedEngine/Wayland/wiWaylandUtils.h b/WickedEngine/Wayland/wiWaylandUtils.h new file mode 100644 index 0000000000..306e678e9a --- /dev/null +++ b/WickedEngine/Wayland/wiWaylandUtils.h @@ -0,0 +1,15 @@ +#pragma once + +namespace wi::wayland { + +template +void safe_delete(T*& ptr, void(&deleter)(T*)) +{ + if (ptr != nullptr) + { + deleter(ptr); + ptr = nullptr; + } +} + +} diff --git a/WickedEngine/Wayland/wiWaylandWindow.cpp b/WickedEngine/Wayland/wiWaylandWindow.cpp new file mode 100644 index 0000000000..9e997d2c5d --- /dev/null +++ b/WickedEngine/Wayland/wiWaylandWindow.cpp @@ -0,0 +1,215 @@ +#include "wiWaylandWindow.h" +#include "wiWaylandBackend.h" +#include "wayland-client-protocol.h" +#include "wiWaylandUtils.h" +#include "xdg-decoration-unstable-v1.h" +#include "xdg-shell.h" +#include + +#define VK_NO_PROTOTYPES +#include "Utility/vulkan/vulkan.h" +#include "Utility/vulkan/vulkan_wayland.h" +#include "Utility/volk.h" + +using namespace wi::wayland; + +extern "C" void _static_on_redraw(void *data, + wl_callback *wl_callback, + uint32_t time) +{ + static_cast(data)->on_redraw(wl_callback, time); +} + +extern "C" void _static_on_enter(void *data, + wl_surface *wl_surface, + wl_output *output) +{ + static_cast(data)->on_enter(output); +} + +extern "C" void _static_on_leave(void *data, + wl_surface *wl_surface, + wl_output *output) +{ + static_cast(data)->on_leave(output); +} + +extern "C" void _static_xdg_surface_configure(void* data, xdg_surface* raw, uint32_t serial) +{ + static_cast(data)->on_xdg_surface_ack_configure(serial); +} + +extern "C" void _static_toplevel_close(void *data, xdg_toplevel *xdg_toplevel) +{ + static_cast(data)->on_toplevel_close(); +} + +extern "C" void _static_toplevel_configure(void *data, + xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + wl_array *states) +{ + static_cast(data)->on_toplevel_configure(width, height, states); +} + +extern "C" void _static_toplevel_configure_bounds(void *data, + xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) +{ + static_cast(data)->on_toplevel_configure_bounds(width, height); +} + +void Window::on_enter(wl_output *output) +{} + +void Window::on_leave(wl_output *output) +{} + +void Window::on_redraw(wl_callback *wl_callback, uint32_t time) +{ + if(frame_callback != nullptr) { + wl_callback_destroy(frame_callback); + frame_callback = nullptr; + } + if (on_redraw_callback) + on_redraw_callback(this); +} + +void Window::on_xdg_surface_ack_configure(uint32_t serial) +{ + xdg_surface_ack_configure(XDGsurface, serial); +} + +void Window::on_toplevel_close() +{ + if (on_close) on_close(this); +} + +void Window::on_toplevel_configure(int32_t width, int32_t height, wl_array* states) +{ + desired_width = width; + desired_height = height; +} + +void Window::on_toplevel_configure_bounds(int32_t width, int32_t height) +{} + + +bool Window::Init(Backend* backend, const char* title) +{ + this->backend = backend; + + // Surface + WLsurface = wl_compositor_create_surface(backend->compositor); + if (WLsurface == nullptr) {std::cerr << "Could not create surface from compositor" << std::endl; return false;} + WLsurface_listener.enter = &_static_on_enter; + WLsurface_listener.leave = &_static_on_leave; + frame_listener.done = &_static_on_redraw; + wl_surface_add_listener(WLsurface, &WLsurface_listener, this); + + // XDG Surface + XDGsurface = xdg_wm_base_get_xdg_surface(backend->window_manager, WLsurface); + if (XDGsurface == nullptr) {std::cerr << "Could not create XDG surface from WL surface" << std::endl; return false;} + XDGsurface_listener.configure = &_static_xdg_surface_configure; + xdg_surface_add_listener(XDGsurface, &XDGsurface_listener, this); + + // Top Level + toplevel = xdg_surface_get_toplevel(XDGsurface); + if (toplevel == nullptr) {std::cerr << "Could not create XDG surface from WL surface" << std::endl; return false;} + toplevel_listener.close = &_static_toplevel_close; + toplevel_listener.configure = &_static_toplevel_configure; + toplevel_listener.configure_bounds = &_static_toplevel_configure_bounds; + xdg_toplevel_add_listener(toplevel, &toplevel_listener, this); + + // DecorationManager + toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(backend->decoration_manager, toplevel); + if (toplevel_decoration != nullptr) + { + zxdg_toplevel_decoration_v1_set_mode(toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + + SetTitle(title); + return true; +} + +void Window::Deinit() +{ + safe_delete(frame_callback, wl_callback_destroy); + safe_delete(toplevel_decoration, zxdg_toplevel_decoration_v1_destroy); + safe_delete(toplevel, xdg_toplevel_destroy); + safe_delete(XDGsurface, xdg_surface_destroy); + safe_delete(WLsurface, wl_surface_destroy); +} + +void Window::SetTitle(const char* title) +{ + if (title == nullptr) + return; + if (toplevel == nullptr) + return; + xdg_toplevel_set_title(toplevel, title); +} + +void Window::GetWindowCurrentSize(int32_t *width, int32_t *height) +{ + *width = current_width; + *height = current_height; +} + +bool Window::HasPendingResize() const +{ + return desired_height > 0 || desired_height > 0; +} + +bool Window::AcceptDesiredDimensions() +{ + const bool has_pending_resize = HasPendingResize(); + if (desired_width > 0) + { + current_width = desired_width; + desired_width = 0; + } + if (desired_height > 0) + { + current_height = desired_height; + desired_height = 0; + } + return has_pending_resize; +} + +void Window::SetFullscreen(bool fullscreen) +{ + if (fullscreen) + { + // wl_output output; + // xdg_toplevel_set_fullscreen(toplevel, &output); + } + else + { + xdg_toplevel_unset_fullscreen(toplevel); + } +} + +VkResult Window::CreateVulkanSurface(VkInstance instance, VkSurfaceKHR* VKsurface) +{ + PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR = + (PFN_vkCreateWaylandSurfaceKHR)vkGetInstanceProcAddr( + instance, + "vkCreateWaylandSurfaceKHR"); + + VkWaylandSurfaceCreateInfoKHR createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.display = backend->display; + createInfo.surface = WLsurface; + + const VkAllocationCallbacks* allocator = nullptr; + + VkResult res = vkCreateWaylandSurfaceKHR(instance, &createInfo, allocator, VKsurface); + + backend->roundtrip(); + return res; +} diff --git a/WickedEngine/Wayland/wiWaylandWindow.h b/WickedEngine/Wayland/wiWaylandWindow.h new file mode 100644 index 0000000000..5b743d2789 --- /dev/null +++ b/WickedEngine/Wayland/wiWaylandWindow.h @@ -0,0 +1,71 @@ +#pragma once +#include "wayland-client-core.h" +#include "wayland-client-protocol.h" +#include "xdg-decoration-unstable-v1.h" +#include "xdg-shell.h" +#include + +#define VK_NO_PROTOTYPES +#include "Utility/vulkan/vulkan.h" +// #include "Utility/volk.h" + +extern "C" { + struct xdg_wm_base; + struct zxdg_decoration_manager_v1; +} + +namespace wi::wayland { + +class Backend; + +class Window { +private: + // reference variables + Backend* backend = nullptr; + + // OWN variables + wl_surface *WLsurface = nullptr; + xdg_surface *XDGsurface = nullptr; + xdg_toplevel *toplevel = nullptr; + zxdg_toplevel_decoration_v1* toplevel_decoration = nullptr; + + wl_callback* frame_callback = nullptr; + wl_surface_listener WLsurface_listener; + wl_callback_listener frame_listener; + xdg_surface_listener XDGsurface_listener; + xdg_toplevel_listener toplevel_listener; + + int32_t current_width = 800; + int32_t current_height = 600; + int32_t desired_width = 0; + int32_t desired_height = 0; + +public: + std::function on_redraw_callback; + std::function on_close; + +public: + ~Window() { Deinit(); } + void Deinit(); + + Window() = default; + bool Init(Backend* backend, const char* title = nullptr); + void SetTitle(const char* title); + void GetWindowCurrentSize(int32_t* width, int32_t* height); + bool HasPendingResize() const; + bool AcceptDesiredDimensions(); + void SetFullscreen(bool fullscreen); + + VkResult CreateVulkanSurface(VkInstance instance, VkSurfaceKHR* VKSurface); + + // callbacks + void on_enter(wl_output *output); + void on_leave(wl_output *output); + void on_redraw(wl_callback *wl_callback, uint32_t time); + void on_xdg_surface_ack_configure(uint32_t serial); + void on_toplevel_close(); + void on_toplevel_configure(int32_t width, int32_t height, wl_array *states); + void on_toplevel_configure_bounds(int32_t width, int32_t height); +}; + +} diff --git a/WickedEngine/WickedEngine.cppm b/WickedEngine/WickedEngine.cppm new file mode 100644 index 0000000000..70697b9a38 --- /dev/null +++ b/WickedEngine/WickedEngine.cppm @@ -0,0 +1,83 @@ +#include +module; + +#include "WickedEngine.h" + +export module wicked_engine; + +export namespace wi +{ + using wi::Application; + using wi::GPUBVH; + using wi::EmittedParticleSystem; + using wi::HairParticleSystem; + using wi::Ocean; + using wi::Sprite; + using wi::SpriteFont; + using wi::Archive; + using wi::Color; + using wi::FadeManager; + using wi::Resource; + using wi::SpinLock; + using wi::Timer; + using wi::Canvas; + + namespace ecs + { + using wi::ecs::Entity; + using wi::ecs::INVALID_ENTITY; + } + + namespace gui + { + using wi::gui::GUI; + using wi::gui::EventArgs; + using wi::gui::Widget; + using wi::gui::Button; + using wi::gui::Label; + using wi::gui::TextInputField; + using wi::gui::Slider; + using wi::gui::CheckBox; + using wi::gui::ComboBox; + using wi::gui::Window; + using wi::gui::ColorPicker; + using wi::gui::TreeList; + } + + namespace primitive + { + using wi::primitive::AABB; + using wi::primitive::Sphere; + using wi::primitive::Capsule; + using wi::primitive::Ray; + using wi::primitive::Frustum; + using wi::primitive::Hitbox2D; + } + + namespace font + { + using wi::font::Params; + using wi::font::Alignment; + } + + namespace image + { + using wi::image::Params; + using wi::image::STENCILMODE; + using wi::image::STENCILREFMODE; + using wi::image::SAMPLEMODE; + using wi::image::QUALITY; + } + + namespace eventhandler + { + using wi::eventhandler::EVENT_THREAD_SAFE_POINT; + using wi::eventhandler::EVENT_RELOAD_SHADERS; + using wi::eventhandler::EVENT_SET_VSYNC; + } + + namespace math + { + using wi::math::IDENTITY_MATRIX; + } +} diff --git a/WickedEngine/WickedEngine.kdev4 b/WickedEngine/WickedEngine.kdev4 new file mode 100644 index 0000000000..14dd2c8977 --- /dev/null +++ b/WickedEngine/WickedEngine.kdev4 @@ -0,0 +1,4 @@ +[Project] +CreatedFrom=CMakeLists.txt +Manager=KDevCMakeManager +Name=WickedEngine diff --git a/WickedEngine/gamemode_client.h b/WickedEngine/gamemode_client.h new file mode 100644 index 0000000000..611fb536a0 --- /dev/null +++ b/WickedEngine/gamemode_client.h @@ -0,0 +1,365 @@ +/* + +Copyright (c) 2017-2019, Feral Interactive +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Feral Interactive nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + */ +#ifndef CLIENT_GAMEMODE_H +#define CLIENT_GAMEMODE_H +/* + * GameMode supports the following client functions + * Requests are refcounted in the daemon + * + * int gamemode_request_start() - Request gamemode starts + * 0 if the request was sent successfully + * -1 if the request failed + * + * int gamemode_request_end() - Request gamemode ends + * 0 if the request was sent successfully + * -1 if the request failed + * + * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and + * destruction, as appropriate. In this configuration, errors will be printed to stderr + * + * int gamemode_query_status() - Query the current status of gamemode + * 0 if gamemode is inactive + * 1 if gamemode is active + * 2 if gamemode is active and this client is registered + * -1 if the query failed + * + * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process + * 0 if the request was sent successfully + * -1 if the request failed + * -2 if the request was rejected + * + * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process + * 0 if the request was sent successfully + * -1 if the request failed + * -2 if the request was rejected + * + * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process + * 0 if gamemode is inactive + * 1 if gamemode is active + * 2 if gamemode is active and this client is registered + * -1 if the query failed + * + * const char* gamemode_error_string() - Get an error string + * returns a string describing any of the above errors + * + * Note: All the above requests can be blocking - dbus requests can and will block while the daemon + * handles the request. It is not recommended to make these calls in performance critical code + */ + +#include +#include + +#include +#include + +#include + +static char internal_gamemode_client_error_string[512] = { 0 }; + +/** + * Load libgamemode dynamically to dislodge us from most dependencies. + * This allows clients to link and/or use this regardless of runtime. + * See SDL3 for an example of the reasoning behind this in terms of + * dynamic versioning as well. + */ +static volatile int internal_libgamemode_loaded = 1; + +/* Typedefs for the functions to load */ +typedef int (*api_call_return_int)(void); +typedef const char *(*api_call_return_cstring)(void); +typedef int (*api_call_pid_return_int)(pid_t); + +/* Storage for functors */ +static api_call_return_int REAL_internal_gamemode_request_start = NULL; +static api_call_return_int REAL_internal_gamemode_request_end = NULL; +static api_call_return_int REAL_internal_gamemode_query_status = NULL; +static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; +static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; +static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; +static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; + +/** + * Internal helper to perform the symbol binding safely. + * + * Returns 0 on success and -1 on failure + */ +__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( + void *handle, const char *name, void **out_func, size_t func_size, bool required) +{ + void *symbol_lookup = NULL; + char *dl_error = NULL; + + /* Safely look up the symbol */ + symbol_lookup = dlsym(handle, name); + dl_error = dlerror(); + if (required && (dl_error || !symbol_lookup)) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "dlsym failed - %s", + dl_error); + return -1; + } + + /* Have the symbol correctly, copy it to make it usable */ + memcpy(out_func, &symbol_lookup, func_size); + return 0; +} + +/** + * Loads libgamemode and needed functions + * + * Returns 0 on success and -1 on failure + */ +__attribute__((always_inline)) static inline int internal_load_libgamemode(void) +{ + /* We start at 1, 0 is a success and -1 is a fail */ + if (internal_libgamemode_loaded != 1) { + return internal_libgamemode_loaded; + } + + /* Anonymous struct type to define our bindings */ + struct binding { + const char *name; + void **functor; + size_t func_size; + bool required; + } bindings[] = { + { "real_gamemode_request_start", + (void **)&REAL_internal_gamemode_request_start, + sizeof(REAL_internal_gamemode_request_start), + true }, + { "real_gamemode_request_end", + (void **)&REAL_internal_gamemode_request_end, + sizeof(REAL_internal_gamemode_request_end), + true }, + { "real_gamemode_query_status", + (void **)&REAL_internal_gamemode_query_status, + sizeof(REAL_internal_gamemode_query_status), + false }, + { "real_gamemode_error_string", + (void **)&REAL_internal_gamemode_error_string, + sizeof(REAL_internal_gamemode_error_string), + true }, + { "real_gamemode_request_start_for", + (void **)&REAL_internal_gamemode_request_start_for, + sizeof(REAL_internal_gamemode_request_start_for), + false }, + { "real_gamemode_request_end_for", + (void **)&REAL_internal_gamemode_request_end_for, + sizeof(REAL_internal_gamemode_request_end_for), + false }, + { "real_gamemode_query_status_for", + (void **)&REAL_internal_gamemode_query_status_for, + sizeof(REAL_internal_gamemode_query_status_for), + false }, + }; + + void *libgamemode = NULL; + + /* Try and load libgamemode */ + libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); + if (!libgamemode) { + /* Attempt to load unversioned library for compatibility with older + * versions (as of writing, there are no ABI changes between the two - + * this may need to change if ever ABI-breaking changes are made) */ + libgamemode = dlopen("libgamemode.so", RTLD_NOW); + if (!libgamemode) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "dlopen failed - %s", + dlerror()); + internal_libgamemode_loaded = -1; + return -1; + } + } + + /* Attempt to bind all symbols */ + for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { + struct binding *binder = &bindings[i]; + + if (internal_bind_libgamemode_symbol(libgamemode, + binder->name, + binder->functor, + binder->func_size, + binder->required)) { + internal_libgamemode_loaded = -1; + return -1; + }; + } + + /* Success */ + internal_libgamemode_loaded = 0; + return 0; +} + +/** + * Redirect to the real libgamemode + */ +__attribute__((always_inline)) static inline const char *gamemode_error_string(void) +{ + /* If we fail to load the system gamemode, or we have an error string already, return our error + * string instead of diverting to the system version */ + if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { + return internal_gamemode_client_error_string; + } + + return REAL_internal_gamemode_error_string(); +} + +/** + * Redirect to the real libgamemode + * Allow automatically requesting game mode + * Also prints errors as they happen. + */ +#ifdef GAMEMODE_AUTO +__attribute__((constructor)) +#else +__attribute__((always_inline)) static inline +#endif +int gamemode_request_start(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + if (REAL_internal_gamemode_request_start() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + return 0; +} + +/* Redirect to the real libgamemode */ +#ifdef GAMEMODE_AUTO +__attribute__((destructor)) +#else +__attribute__((always_inline)) static inline +#endif +int gamemode_request_end(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + if (REAL_internal_gamemode_request_end() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + return 0; +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_query_status(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_query_status == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_query_status missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_query_status(); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_start_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_start_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_start_for(pid); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_end_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_end_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_end_for(pid); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_query_status_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_query_status_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_query_status_for(pid); +} + +#endif // CLIENT_GAMEMODE_H diff --git a/WickedEngine/wiApplication.cpp b/WickedEngine/wiApplication.cpp index f81a957056..a12dd5c1ec 100644 --- a/WickedEngine/wiApplication.cpp +++ b/WickedEngine/wiApplication.cpp @@ -743,7 +743,10 @@ namespace wi } #elif defined(PLATFORM_LINUX) - SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + if (window.type == platform::LinuxWindow::eSDLWindow) + SDL_SetWindowFullscreen(window.sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + else if (window.type == platform::LinuxWindow::eWaylandWindow) + window.wl_window->SetFullscreen(fullscreen); #endif // PLATFORM_WINDOWS_DESKTOP } diff --git a/WickedEngine/wiApplication.h b/WickedEngine/wiApplication.h index 90c78bf7a0..e2fafb7da6 100644 --- a/WickedEngine/wiApplication.h +++ b/WickedEngine/wiApplication.h @@ -53,7 +53,7 @@ namespace wi bool allow_hdr = true; wi::graphics::SwapChain swapChain; wi::Canvas canvas; - wi::platform::window_type window; + wi::platform::window_type window = nullptr; // Runs the main engine loop void Run(); @@ -75,13 +75,13 @@ namespace wi // This is where the critical initializations happen (before any rendering or anything else) virtual void Initialize(); - // This is where application-wide updates get executed once per frame. + // This is where application-wide updates get executed once per frame. // RenderPath::Update is also called from here for the active component virtual void Update(float dt); - // This is where application-wide updates get executed in a fixed timestep based manner. + // This is where application-wide updates get executed in a fixed timestep based manner. // RenderPath::FixedUpdate is also called from here for the active component virtual void FixedUpdate(); - // This is where application-wide rendering happens to offscreen buffers. + // This is where application-wide rendering happens to offscreen buffers. // RenderPath::Render is also called from here for the active component virtual void Render(); // This is where the application will render to the screen (backbuffer). It must render to the provided command list. diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 01b9616c52..523333dbb1 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -1,4 +1,5 @@ #include "wiGraphicsDevice_Vulkan.h" +#include #ifdef WICKEDENGINE_BUILD_VULKAN #include "wiHelper.h" @@ -23,6 +24,7 @@ #ifdef SDL2 #include #include "sdl2.h" +#include "Utility/vulkan/vulkan_wayland.h" #endif #include @@ -605,7 +607,7 @@ namespace vulkan_internal } VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsMessengerCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) @@ -2442,18 +2444,25 @@ using namespace vulkan_internal; instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif SDL2 { - uint32_t extensionCount; - SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr); - wi::vector extensionNames_sdl(extensionCount); - SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensionNames_sdl.data()); - instanceExtensions.reserve(instanceExtensions.size() + extensionNames_sdl.size()); - for (auto& x : extensionNames_sdl) + if (window.type == platform::LinuxWindow::eSDLWindow) { - instanceExtensions.push_back(x); + uint32_t extensionCount; + SDL_Vulkan_GetInstanceExtensions(window.sdl_window, &extensionCount, nullptr); + wi::vector extensionNames_sdl(extensionCount); + SDL_Vulkan_GetInstanceExtensions(window.sdl_window, &extensionCount, extensionNames_sdl.data()); + instanceExtensions.reserve(instanceExtensions.size() + extensionNames_sdl.size()); + for (auto& x : extensionNames_sdl) + { + instanceExtensions.push_back(x); + } + } + else if (window.type == platform::LinuxWindow::eWaylandWindow) + { + instanceExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); } } #endif // _WIN32 - + if (validationMode != ValidationMode::Disabled) { // Determine the optimal validation layers to enable that are necessary for useful debugging @@ -2966,7 +2975,7 @@ using namespace vulkan_internal; queueFamiliesVideo[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_VIDEO_PROPERTIES_KHR; } vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, queueFamilies.data()); - + // Query base queue families: for (uint32_t i = 0; i < queueFamilyCount; ++i) { @@ -3211,7 +3220,7 @@ using namespace vulkan_internal; res = vmaCreateBuffer(allocationhandler->allocator, &bufferInfo, &allocInfo, &nullBuffer, &nullBufferAllocation, nullptr); assert(res == VK_SUCCESS); - + VkBufferViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT; @@ -3680,7 +3689,7 @@ using namespace vulkan_internal; res = vkGetPipelineCacheData(device, pipelineCache, &size, nullptr); assert(res == VK_SUCCESS); - // Get data of pipeline cache + // Get data of pipeline cache wi::vector data(size); res = vkGetPipelineCacheData(device, pipelineCache, &size, data.data()); assert(res == VK_SUCCESS); @@ -3688,7 +3697,7 @@ using namespace vulkan_internal; // Write pipeline cache data to a file in binary format wi::helper::FileWrite(get_shader_cache_path(), data.data(), size); - // Destroy Vulkan pipeline cache + // Destroy Vulkan pipeline cache vkDestroyPipelineCache(device, pipelineCache, nullptr); pipelineCache = VK_NULL_HANDLE; } @@ -3725,9 +3734,15 @@ using namespace vulkan_internal; res = vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &internal_state->surface); assert(res == VK_SUCCESS); #elif SDL2 - if (!SDL_Vulkan_CreateSurface(window, instance, &internal_state->surface)) + if (window.type == platform::LinuxWindow::eSDLWindow) { + if (!SDL_Vulkan_CreateSurface(window.sdl_window, instance, &internal_state->surface)) + { + throw sdl2::SDLError("Error creating a vulkan surface"); + } + } + else if (window.type == platform::LinuxWindow::eWaylandWindow) { - throw sdl2::SDLError("Error creating a vulkan surface"); + assert(window.wl_window->CreateVulkanSurface(instance, &internal_state->surface) == VK_SUCCESS); } #else #error WICKEDENGINE VULKAN DEVICE ERROR: PLATFORM NOT SUPPORTED @@ -4071,7 +4086,7 @@ using namespace vulkan_internal; dependencyInfo.pBufferMemoryBarriers = &barrier; vkCmdPipelineBarrier2(cmd.transitionCommandBuffer, &dependencyInfo); - + copyAllocator.submit(cmd); } } @@ -5445,7 +5460,7 @@ using namespace vulkan_internal; default: break; } - + } VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -6423,7 +6438,7 @@ using namespace vulkan_internal; info.referencePictureFormat = info.pictureFormat; info.pVideoProfile = &video_capability_h264.profile; info.pStdHeaderVersion = &video_capability_h264.video_capabilities.stdHeaderVersion; - + res = vkCreateVideoSessionKHR(device, &info, nullptr, &internal_state->video_session); assert(res == VK_SUCCESS); @@ -7030,7 +7045,7 @@ using namespace vulkan_internal; VkResult res = vkGetRayTracingShaderGroupHandlesKHR(device, to_internal(rtpso)->pipeline, group_index, 1, SHADER_IDENTIFIER_SIZE, dest); assert(res == VK_SUCCESS); } - + void GraphicsDevice_Vulkan::SetName(GPUResource* pResource, const char* name) const { if (!debugUtils || pResource == nullptr || !pResource->IsValid()) @@ -7374,7 +7389,7 @@ using namespace vulkan_internal; } allocationhandler->destroylocker.unlock(); - // Destroy Vulkan pipeline cache + // Destroy Vulkan pipeline cache vkDestroyPipelineCache(device, pipelineCache, nullptr); pipelineCache = VK_NULL_HANDLE; @@ -7696,7 +7711,7 @@ using namespace vulkan_internal; assert(0); } commandlist.prev_swapchains.push_back(*swapchain); - + VkRenderingInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; info.renderArea.offset.x = 0; @@ -7935,7 +7950,7 @@ using namespace vulkan_internal; barrier.newLayout = _ConvertImageLayout(image.layout); assert(barrier.newLayout != VK_IMAGE_LAYOUT_UNDEFINED); - + barrier.srcStageMask = _ConvertPipelineStage(image.layout_before); barrier.dstStageMask = _ConvertPipelineStage(image.layout); barrier.srcAccessMask = _ParseResourceState(image.layout_before); @@ -9088,7 +9103,7 @@ using namespace vulkan_internal; raygen.deviceAddress += desc->ray_generation.offset; raygen.size = desc->ray_generation.size; raygen.stride = raygen.size; // raygen specifically must be size == stride - + VkStridedDeviceAddressRegionKHR miss = {}; miss.deviceAddress = desc->miss.buffer ? to_internal(desc->miss.buffer)->address : 0; miss.deviceAddress += desc->miss.offset; @@ -9113,8 +9128,8 @@ using namespace vulkan_internal; &miss, &hitgroup, &callable, - desc->width, - desc->height, + desc->width, + desc->height, desc->depth ); } diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.h b/WickedEngine/wiGraphicsDevice_Vulkan.h index 462ea9e7ae..9f8a988b4a 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.h +++ b/WickedEngine/wiGraphicsDevice_Vulkan.h @@ -18,6 +18,9 @@ #define VK_NO_PROTOTYPES #include "Utility/vulkan/vulkan.h" +#ifdef SDL2 +#include "Utility/vulkan/vulkan_wayland.h" +#endif #include "Utility/volk.h" #include "Utility/vk_mem_alloc.h" diff --git a/WickedEngine/wiInput.cpp b/WickedEngine/wiInput.cpp index d885ee5298..e7b87d5cf6 100644 --- a/WickedEngine/wiInput.cpp +++ b/WickedEngine/wiInput.cpp @@ -80,7 +80,7 @@ namespace wi::input const KeyboardState& GetKeyboardState() { return keyboard; } const MouseState& GetMouseState() { return mouse; } - struct Input + struct Input { BUTTON button = BUTTON_NONE; int playerIndex = 0; @@ -155,7 +155,7 @@ namespace wi::input #if defined(_WIN32) && !defined(PLATFORM_XBOX) wi::input::rawinput::GetMouseState(&mouse); // currently only the relative data can be used from this - wi::input::rawinput::GetKeyboardState(&keyboard); + wi::input::rawinput::GetKeyboardState(&keyboard); // apparently checking the mouse here instead of Down() avoids missing the button presses (review!) mouse.left_button_press |= KEY_DOWN(VK_LBUTTON); @@ -474,15 +474,15 @@ namespace wi::input switch (button) { case wi::input::MOUSE_BUTTON_LEFT: - if (mouse.left_button_press) + if (mouse.left_button_press) return true; return false; case wi::input::MOUSE_BUTTON_RIGHT: - if (mouse.right_button_press) + if (mouse.right_button_press) return true; return false; case wi::input::MOUSE_BUTTON_MIDDLE: - if (mouse.middle_button_press) + if (mouse.middle_button_press) return true; return false; #ifdef _WIN32 @@ -507,7 +507,7 @@ namespace wi::input case wi::input::KEYBOARD_BUTTON_LSHIFT: keycode = VK_LSHIFT; break; - case wi::input::KEYBOARD_BUTTON_F1: + case wi::input::KEYBOARD_BUTTON_F1: keycode = VK_F1; break; case wi::input::KEYBOARD_BUTTON_F2: @@ -718,22 +718,24 @@ namespace wi::input ClientToScreen(hWnd, &p); SetCursorPos(p.x, p.y); #elif defined(SDL2) - // Keeping with the trend of 'Set Pointer' API on different platforms, - // SDL_WarpMouseInWindow is used in the case of SDL2. This unfortunately - // causes SDL2 to generate a mouse event for the delta between the old - // and new positions leading to 'rubber banding'. - // The current solution is to artifically generate a motion event of our own - // which will 'undo' this unwanted motion event during the mouse motion - // accumulation in wiSDLInput.cpp Update() - XMFLOAT4 currentPointer = GetPointer(); - SDL_MouseMotionEvent motionEvent = {0}; - motionEvent.type = SDL_MOUSEMOTION; - motionEvent.x = motionEvent.y = 0; // doesn't matter - motionEvent.xrel = currentPointer.x - props.x; - motionEvent.yrel = currentPointer.y - props.y; - SDL_PushEvent((SDL_Event*)&motionEvent); - SDL_WarpMouseInWindow(window, props.x, props.y); - + if (window.type == platform::LinuxWindow::eSDLWindow) + { + // Keeping with the trend of 'Set Pointer' API on different platforms, + // SDL_WarpMouseInWindow is used in the case of SDL2. This unfortunately + // causes SDL2 to generate a mouse event for the delta between the old + // and new positions leading to 'rubber banding'. + // The current solution is to artifically generate a motion event of our own + // which will 'undo' this unwanted motion event during the mouse motion + // accumulation in wiSDLInput.cpp Update() + XMFLOAT4 currentPointer = GetPointer(); + SDL_MouseMotionEvent motionEvent = {0}; + motionEvent.type = SDL_MOUSEMOTION; + motionEvent.x = motionEvent.y = 0; // doesn't matter + motionEvent.xrel = currentPointer.x - props.x; + motionEvent.yrel = currentPointer.y - props.y; + SDL_PushEvent((SDL_Event*)&motionEvent); + SDL_WarpMouseInWindow(window.sdl_window, props.x, props.y); + } #endif // SDL2 } void HidePointer(bool value) diff --git a/WickedEngine/wiPlatform.h b/WickedEngine/wiPlatform.h index 58ec885557..c322d21aec 100644 --- a/WickedEngine/wiPlatform.h +++ b/WickedEngine/wiPlatform.h @@ -34,6 +34,7 @@ typedef void* HMODULE; #include #include #include "sdl2.h" +#include "Wayland/wiWaylandWindow.h" #endif @@ -42,7 +43,25 @@ namespace wi::platform #ifdef _WIN32 using window_type = HWND; #elif SDL2 - using window_type = SDL_Window*; + struct LinuxWindow { + enum ELinuxWindowType { + eNoneWindow, + eSDLWindow, + eWaylandWindow, + } type; + union { + void* null; + SDL_Window* sdl_window; + wi::wayland::Window* wl_window; + }; + LinuxWindow(std::nullptr_t) + : type(eNoneWindow), null(nullptr) {} + LinuxWindow(SDL_Window* window) + : type(eSDLWindow), sdl_window(window) {} + LinuxWindow(wi::wayland::Window* window) + : type(eWaylandWindow), wl_window(window) {} + }; + using window_type = LinuxWindow; #else using window_type = void*; #endif // _WIN32 @@ -83,10 +102,18 @@ namespace wi::platform #endif // PLATFORM_WINDOWS_DESKTOP || PLATFORM_XBOX #ifdef PLATFORM_LINUX - int window_width, window_height; - SDL_GetWindowSize(window, &window_width, &window_height); - SDL_Vulkan_GetDrawableSize(window, &dest->width, &dest->height); - dest->dpi = ((float)dest->width / (float)window_width) * 96.f; + if (window.type == window.eSDLWindow) + { + int window_width, window_height; + SDL_GetWindowSize(window.sdl_window, &window_width, &window_height); + SDL_Vulkan_GetDrawableSize(window.sdl_window, &dest->width, &dest->height); + dest->dpi = ((float)dest->width / (float)window_width) * 96.f; + } + else if (window.type == window.eWaylandWindow) + { + window.wl_window->GetWindowCurrentSize(&dest->width, &dest->height); + dest->dpi = 96.f; + } #endif // PLATFORM_LINUX } } From fb0ba531c9829df82bd14352b16ffe23d5cb56d1 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Tue, 24 Dec 2024 18:28:23 +0100 Subject: [PATCH 2/9] CI install wayland-protocols package --- .github/workflows/build-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index a966627c64..f7ac0e46cb 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -60,7 +60,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libsdl2-dev ccache + sudo apt install libsdl2-dev ccache wayland-protocols - name: Initial compile run: | From 56bfe709665a4b73aeb1f2ee6063cf1109c62aa0 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Tue, 24 Dec 2024 18:44:26 +0100 Subject: [PATCH 3/9] fix close button not working --- Samples/Tests/main_SDL2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/Tests/main_SDL2.cpp b/Samples/Tests/main_SDL2.cpp index 0b1d5c20a6..f82bf5e68a 100644 --- a/Samples/Tests/main_SDL2.cpp +++ b/Samples/Tests/main_SDL2.cpp @@ -97,7 +97,7 @@ int main_sdl2(int argc, char *argv[]) void wayland_loop(wi::wayland::Backend &wwbackend, Tests &tests) { bool quit = false; - wwbackend.window.on_close = [&quit](wi::wayland::Window*w) { quit = false; }; + wwbackend.window.on_close = [&quit](wi::wayland::Window*w) { quit = true; }; while(!quit) { From bf4ada1a7d0a32d82d2ba9c59e88f0e55482059d Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Tue, 24 Dec 2024 18:46:06 +0100 Subject: [PATCH 4/9] CI install libwayland-dev package --- .github/workflows/build-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index f7ac0e46cb..946f54e1af 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -60,7 +60,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libsdl2-dev ccache wayland-protocols + sudo apt install libsdl2-dev ccache wayland-protocols libwayland-dev - name: Initial compile run: | From eb63bfcbef67eee38e8c76984f1f1f874fc511c8 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Tue, 24 Dec 2024 18:53:41 +0100 Subject: [PATCH 5/9] CI use ninja --- .github/workflows/build-pr.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 946f54e1af..0617d61d2b 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -66,8 +66,9 @@ jobs: run: | mkdir build cd build - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - CCACHE_NODIRECT=1 make -j$(nproc) + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + echo no CCACHE_NODIRECT=1 make -j$(nproc) + cmake --build . - name: Generate shader dump run: | From ce6214bcd3a405a8e00065109c83202e63b461a4 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Wed, 25 Dec 2024 00:48:06 +0100 Subject: [PATCH 6/9] wayland: don't crash on GNOME that refuses to implement server side decorations --- WickedEngine/Wayland/wiWaylandBackend.cpp | 4 ++-- WickedEngine/Wayland/wiWaylandBackend.h | 5 ++++- WickedEngine/Wayland/wiWaylandWindow.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/WickedEngine/Wayland/wiWaylandBackend.cpp b/WickedEngine/Wayland/wiWaylandBackend.cpp index bb45cc660f..d718368593 100644 --- a/WickedEngine/Wayland/wiWaylandBackend.cpp +++ b/WickedEngine/Wayland/wiWaylandBackend.cpp @@ -45,7 +45,7 @@ bool Backend::Init() if (!registry_has("xdg_wm_base")) {std::cerr<<"XDG WM BASE not found"<bind( "zxdg_decoration_manager_v1", &zxdg_decoration_manager_v1_interface); - return window_manager != nullptr; + return decoration_manager != nullptr; } bool Backend::init_window_manager() diff --git a/WickedEngine/Wayland/wiWaylandBackend.h b/WickedEngine/Wayland/wiWaylandBackend.h index ae8f297790..6e16a00f02 100644 --- a/WickedEngine/Wayland/wiWaylandBackend.h +++ b/WickedEngine/Wayland/wiWaylandBackend.h @@ -56,7 +56,10 @@ class Backend { template T* bind(const char* interface_name, const struct wl_interface *interface, uint32_t version = -1) { - std::pair reg = registry_map.at(interface_name); + auto search = registry_map.find(interface_name); + if (search == registry_map.end()) + return nullptr; + std::pair ® = search->second; if (version < 0) { version = reg.second; } else { diff --git a/WickedEngine/Wayland/wiWaylandWindow.cpp b/WickedEngine/Wayland/wiWaylandWindow.cpp index 9e997d2c5d..66dcbb4341 100644 --- a/WickedEngine/Wayland/wiWaylandWindow.cpp +++ b/WickedEngine/Wayland/wiWaylandWindow.cpp @@ -124,10 +124,13 @@ bool Window::Init(Backend* backend, const char* title) xdg_toplevel_add_listener(toplevel, &toplevel_listener, this); // DecorationManager - toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(backend->decoration_manager, toplevel); - if (toplevel_decoration != nullptr) + if (backend->decoration_manager) { - zxdg_toplevel_decoration_v1_set_mode(toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(backend->decoration_manager, toplevel); + if (toplevel_decoration != nullptr) + { + zxdg_toplevel_decoration_v1_set_mode(toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } } SetTitle(title); From 88a9574efe60dfc80085f9c3bd7c3c8efcddda6d Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Wed, 25 Dec 2024 00:49:32 +0100 Subject: [PATCH 7/9] Fixed wrong include path for wayland headers removing leading 'wayland/' folder as it's unecessary and actually different on some distros --- WickedEngine/Wayland/wiWaylandBackend.cpp | 6 +++--- WickedEngine/Wayland/wiWaylandBackend.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WickedEngine/Wayland/wiWaylandBackend.cpp b/WickedEngine/Wayland/wiWaylandBackend.cpp index d718368593..bb59b8890a 100644 --- a/WickedEngine/Wayland/wiWaylandBackend.cpp +++ b/WickedEngine/Wayland/wiWaylandBackend.cpp @@ -1,8 +1,8 @@ #include "wiWaylandBackend.h" #include "wiWaylandUtils.h" -#include -#include -#include +#include +#include +#include #include #include "xdg-shell.h" diff --git a/WickedEngine/Wayland/wiWaylandBackend.h b/WickedEngine/Wayland/wiWaylandBackend.h index 6e16a00f02..ae713a3e4c 100644 --- a/WickedEngine/Wayland/wiWaylandBackend.h +++ b/WickedEngine/Wayland/wiWaylandBackend.h @@ -1,6 +1,6 @@ #pragma once #include "wiWaylandWindow.h" -#include +#include #include #include #include From 55c9439aab66feb714a4c0b7c042c7d8dd717b91 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Wed, 25 Dec 2024 00:51:26 +0100 Subject: [PATCH 8/9] Add ninja install dependecy --- .github/workflows/build-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 0617d61d2b..a2b63eefc3 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -60,7 +60,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libsdl2-dev ccache wayland-protocols libwayland-dev + sudo apt install libsdl2-dev ccache wayland-protocols libwayland-dev ninja - name: Initial compile run: | From 1179fa532d2117ee67922fa535d853cb2e32f550 Mon Sep 17 00:00:00 2001 From: Matteo De Carlo Date: Wed, 25 Dec 2024 01:02:36 +0100 Subject: [PATCH 9/9] ubuntu has stupid package names (ninja-build) --- .github/workflows/build-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index a2b63eefc3..cd20d3b411 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -60,7 +60,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libsdl2-dev ccache wayland-protocols libwayland-dev ninja + sudo apt install libsdl2-dev ccache wayland-protocols libwayland-dev ninja-build - name: Initial compile run: |