From f5f1c9a530b7b15adc37c6c520088848662b6249 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 Oct 2024 13:50:57 +0200 Subject: [PATCH 1/2] wsi: Pass window state to setWindowMode --- src/d3d9/d3d9_swapchain.cpp | 2 +- src/dxgi/dxgi_swapchain.cpp | 2 +- src/wsi/glfw/wsi_platform_glfw.h | 1 + src/wsi/glfw/wsi_window_glfw.cpp | 1 + src/wsi/sdl2/wsi_platform_sdl2.h | 1 + src/wsi/sdl2/wsi_window_sdl2.cpp | 1 + src/wsi/win32/wsi_platform_win32.h | 1 + src/wsi/win32/wsi_window_win32.cpp | 1 + src/wsi/wsi_platform.cpp | 3 ++- src/wsi/wsi_platform.h | 1 + src/wsi/wsi_window.h | 2 ++ 11 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 749fb6ba5e0..b9b036e04b9 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -1331,7 +1331,7 @@ namespace dxvk { HMONITOR monitor = wsi::getDefaultMonitor(); - if (!wsi::setWindowMode(monitor, m_window, wsiMode)) + if (!wsi::setWindowMode(monitor, m_window, &m_windowState, wsiMode)) return D3DERR_NOTAVAILABLE; if (wsi::getCurrentDisplayMode(monitor, &wsiMode)) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 67e2a54cdf0..d329af39f49 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -813,7 +813,7 @@ namespace dxvk { if (!selectedMode.RefreshRate.Denominator) selectedMode.RefreshRate.Denominator = 1; - if (!wsi::setWindowMode(outputDesc.Monitor, m_window, ConvertDisplayMode(selectedMode))) + if (!wsi::setWindowMode(outputDesc.Monitor, m_window, &m_windowState, ConvertDisplayMode(selectedMode))) return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; DXGI_VK_MONITOR_DATA* monitorData = nullptr; diff --git a/src/wsi/glfw/wsi_platform_glfw.h b/src/wsi/glfw/wsi_platform_glfw.h index ee6261baee6..5ef0cfc4d9e 100644 --- a/src/wsi/glfw/wsi_platform_glfw.h +++ b/src/wsi/glfw/wsi_platform_glfw.h @@ -75,6 +75,7 @@ namespace dxvk::wsi { virtual bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode); virtual bool enterFullscreenMode( diff --git a/src/wsi/glfw/wsi_window_glfw.cpp b/src/wsi/glfw/wsi_window_glfw.cpp index 357c427d241..28510f7581b 100644 --- a/src/wsi/glfw/wsi_window_glfw.cpp +++ b/src/wsi/glfw/wsi_window_glfw.cpp @@ -45,6 +45,7 @@ namespace dxvk::wsi { bool GlfwWsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& pMode) { const int32_t displayId = fromHmonitor(hMonitor); GLFWwindow* window = fromHwnd(hWindow); diff --git a/src/wsi/sdl2/wsi_platform_sdl2.h b/src/wsi/sdl2/wsi_platform_sdl2.h index bc29812a27b..44f8836edaf 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.h +++ b/src/wsi/sdl2/wsi_platform_sdl2.h @@ -73,6 +73,7 @@ namespace dxvk::wsi { virtual bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode); virtual bool enterFullscreenMode( diff --git a/src/wsi/sdl2/wsi_window_sdl2.cpp b/src/wsi/sdl2/wsi_window_sdl2.cpp index 90b6107bc0f..e0c48fdcec5 100644 --- a/src/wsi/sdl2/wsi_window_sdl2.cpp +++ b/src/wsi/sdl2/wsi_window_sdl2.cpp @@ -44,6 +44,7 @@ namespace dxvk::wsi { bool Sdl2WsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& pMode) { const int32_t displayId = fromHmonitor(hMonitor); SDL_Window* window = fromHwnd(hWindow); diff --git a/src/wsi/win32/wsi_platform_win32.h b/src/wsi/win32/wsi_platform_win32.h index 3fdbba0191a..3f03e64dbe9 100644 --- a/src/wsi/win32/wsi_platform_win32.h +++ b/src/wsi/win32/wsi_platform_win32.h @@ -60,6 +60,7 @@ namespace dxvk::wsi { virtual bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode); virtual bool enterFullscreenMode( diff --git a/src/wsi/win32/wsi_window_win32.cpp b/src/wsi/win32/wsi_window_win32.cpp index b30608a2dd7..faf963d3f3d 100644 --- a/src/wsi/win32/wsi_window_win32.cpp +++ b/src/wsi/win32/wsi_window_win32.cpp @@ -134,6 +134,7 @@ namespace dxvk::wsi { bool Win32WsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode) { ::MONITORINFOEXW monInfo; monInfo.cbSize = sizeof(monInfo); diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp index 76e72f0225e..cf1c2391c11 100644 --- a/src/wsi/wsi_platform.cpp +++ b/src/wsi/wsi_platform.cpp @@ -80,8 +80,9 @@ namespace dxvk::wsi { bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode) { - return s_driver->setWindowMode(hMonitor, hWindow, mode); + return s_driver->setWindowMode(hMonitor, hWindow, pState, mode); } bool enterFullscreenMode( diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 50794bf53e6..0cd3b9ef02e 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -60,6 +60,7 @@ namespace dxvk::wsi { virtual bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode) = 0; virtual bool enterFullscreenMode( diff --git a/src/wsi/wsi_window.h b/src/wsi/wsi_window.h index ed0f87d8880..a67283df154 100644 --- a/src/wsi/wsi_window.h +++ b/src/wsi/wsi_window.h @@ -58,12 +58,14 @@ namespace dxvk::wsi { * * \param [in] hMonitor The monitor * \param [in] hWindow The window (may be unused on some platforms) + * \param [in] pState The swapchain's window state * \param [in] mode The mode * \returns \c true on success, \c false on failure */ bool setWindowMode( HMONITOR hMonitor, HWND hWindow, + DxvkWindowState* pState, const WsiMode& mode); /** From e1c833d406f67e97207d0d8187de7c2e7e5eb3df Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Sat, 5 Oct 2024 15:23:11 -0400 Subject: [PATCH 2/2] native: Add SDL3 WSI Co-authored-by: Philip Rebohle --- README.md | 2 +- include/native/meson.build | 1 + include/native/wsi/native_sdl3.h | 25 ++++ include/native/wsi/native_wsi.h | 2 + meson.build | 8 +- src/wsi/glfw/wsi_monitor_glfw.cpp | 2 +- src/wsi/meson.build | 4 + src/wsi/sdl2/wsi_monitor_sdl2.cpp | 2 +- src/wsi/sdl2/wsi_platform_sdl2.cpp | 2 +- src/wsi/sdl3/wsi_monitor_sdl3.cpp | 157 ++++++++++++++++++++++ src/wsi/sdl3/wsi_platform_sdl3.cpp | 78 +++++++++++ src/wsi/sdl3/wsi_platform_sdl3.h | 120 +++++++++++++++++ src/wsi/sdl3/wsi_platform_sdl3_funcs.h | 22 +++ src/wsi/sdl3/wsi_window_sdl3.cpp | 177 +++++++++++++++++++++++++ src/wsi/wsi_platform.cpp | 3 + src/wsi/wsi_platform.h | 3 + src/wsi/wsi_window.h | 5 + 17 files changed, 607 insertions(+), 6 deletions(-) create mode 100644 include/native/wsi/native_sdl3.h create mode 100644 src/wsi/sdl3/wsi_monitor_sdl3.cpp create mode 100644 src/wsi/sdl3/wsi_platform_sdl3.cpp create mode 100644 src/wsi/sdl3/wsi_platform_sdl3.h create mode 100644 src/wsi/sdl3/wsi_platform_sdl3_funcs.h create mode 100644 src/wsi/sdl3/wsi_window_sdl3.cpp diff --git a/README.md b/README.md index d8d22f4d574..2be8cfa51ac 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ This is primarily useful for game and application ports to either avoid having t DXVK Native replaces certain Windows-isms with a platform and framework-agnostic replacement, for example, `HWND`s can become `SDL_Window*`s, etc. All it takes to do that is to add another WSI backend. -**Note:** DXVK Native requires a backend to be explicitly set via the `DXVK_WSI_DRIVER` environment variable. The current built-in options are `SDL2` and `GLFW`. +**Note:** DXVK Native requires a backend to be explicitly set via the `DXVK_WSI_DRIVER` environment variable. The current built-in options are `SDL3`, `SDL2`, and `GLFW`. DXVK Native comes with a slim set of Windows header definitions required for D3D9/11 and the MinGW headers for D3D9/11. In most cases, it will end up being plug and play with your renderer, but there may be certain teething issues such as: diff --git a/include/native/meson.build b/include/native/meson.build index 14e191a5f0c..ca493093065 100644 --- a/include/native/meson.build +++ b/include/native/meson.build @@ -13,6 +13,7 @@ install_subdir( install_headers( 'wsi/native_wsi.h', + 'wsi/native_sdl3.h', 'wsi/native_sdl2.h', 'wsi/native_glfw.h', subdir: 'dxvk/wsi', diff --git a/include/native/wsi/native_sdl3.h b/include/native/wsi/native_sdl3.h new file mode 100644 index 00000000000..5244575615e --- /dev/null +++ b/include/native/wsi/native_sdl3.h @@ -0,0 +1,25 @@ +#include + +#include + +namespace dxvk::wsi { + + inline SDL_Window* fromHwnd(HWND hWindow) { + return reinterpret_cast(hWindow); + } + + inline HWND toHwnd(SDL_Window* pWindow) { + return reinterpret_cast(pWindow); + } + + // Offset so null HMONITORs go to -1 + inline SDL_DisplayID fromHmonitor(HMONITOR hMonitor) { + return SDL_DisplayID(reinterpret_cast(hMonitor)); + } + + // Offset so -1 display id goes to 0 == NULL + inline HMONITOR toHmonitor(SDL_DisplayID display) { + return reinterpret_cast(uintptr_t(display)); + } + +} diff --git a/include/native/wsi/native_wsi.h b/include/native/wsi/native_wsi.h index cfb64f12d3e..e5d73210a60 100644 --- a/include/native/wsi/native_wsi.h +++ b/include/native/wsi/native_wsi.h @@ -2,6 +2,8 @@ #ifdef DXVK_WSI_WIN32 #error You shouldnt be using this code path. +#elif DXVK_WSI_SDL3 +#include "wsi/native_sdl3.h" #elif DXVK_WSI_SDL2 #include "wsi/native_sdl2.h" #elif DXVK_WSI_GLFW diff --git a/meson.build b/meson.build index a19a9fbbd5a..59606e3ffaa 100644 --- a/meson.build +++ b/meson.build @@ -148,16 +148,20 @@ else './include/native/directx' ] + lib_sdl3 = dependency('SDL3', required: false) lib_sdl2 = dependency('SDL2', required: false) lib_glfw = dependency('glfw', required: false) + if lib_sdl3.found() + compiler_args += ['-DDXVK_WSI_SDL3'] + endif if lib_sdl2.found() compiler_args += ['-DDXVK_WSI_SDL2'] endif if lib_glfw.found() compiler_args += ['-DDXVK_WSI_GLFW'] endif - if (not lib_sdl2.found() and not lib_glfw.found()) - error('SDL2 or GLFW are required to build dxvk-native') + if (not lib_sdl3.found() and not lib_sdl2.found() and not lib_glfw.found()) + error('SDL3, SDL2, or GLFW are required to build dxvk-native') endif dxvk_name_prefix = 'dxvk_' diff --git a/src/wsi/glfw/wsi_monitor_glfw.cpp b/src/wsi/glfw/wsi_monitor_glfw.cpp index f6c09aaf0e3..358fb146201 100644 --- a/src/wsi/glfw/wsi_monitor_glfw.cpp +++ b/src/wsi/glfw/wsi_monitor_glfw.cpp @@ -2,7 +2,7 @@ #include "../wsi_monitor.h" -#include "wsi/native_wsi.h" +#include "wsi/native_glfw.h" #include "wsi_platform_glfw.h" #include "../../util/util_string.h" diff --git a/src/wsi/meson.build b/src/wsi/meson.build index 4cfb13a8e2a..d72c221c762 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -4,6 +4,9 @@ wsi_src = [ 'win32/wsi_monitor_win32.cpp', 'win32/wsi_platform_win32.cpp', 'win32/wsi_window_win32.cpp', + 'sdl3/wsi_monitor_sdl3.cpp', + 'sdl3/wsi_platform_sdl3.cpp', + 'sdl3/wsi_window_sdl3.cpp', 'sdl2/wsi_monitor_sdl2.cpp', 'sdl2/wsi_platform_sdl2.cpp', 'sdl2/wsi_window_sdl2.cpp', @@ -16,6 +19,7 @@ wsi_deps = [ dep_displayinfo ] if platform != 'windows' wsi_deps += [ + lib_sdl3.partial_dependency(compile_args: true, includes: true), lib_sdl2.partial_dependency(compile_args: true, includes: true), lib_glfw.partial_dependency(compile_args: true, includes: true), ] diff --git a/src/wsi/sdl2/wsi_monitor_sdl2.cpp b/src/wsi/sdl2/wsi_monitor_sdl2.cpp index 8f5f3290047..d021346f315 100644 --- a/src/wsi/sdl2/wsi_monitor_sdl2.cpp +++ b/src/wsi/sdl2/wsi_monitor_sdl2.cpp @@ -2,7 +2,7 @@ #include "../wsi_monitor.h" -#include "wsi/native_wsi.h" +#include "wsi/native_sdl2.h" #include "wsi_platform_sdl2.h" #include "../../util/util_string.h" diff --git a/src/wsi/sdl2/wsi_platform_sdl2.cpp b/src/wsi/sdl2/wsi_platform_sdl2.cpp index 9223b5980ae..203cb6d9aaf 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.cpp +++ b/src/wsi/sdl2/wsi_platform_sdl2.cpp @@ -5,7 +5,7 @@ #include "../../util/util_string.h" #include "../../util/util_win32_compat.h" -#include +#include namespace dxvk::wsi { diff --git a/src/wsi/sdl3/wsi_monitor_sdl3.cpp b/src/wsi/sdl3/wsi_monitor_sdl3.cpp new file mode 100644 index 00000000000..f5688478618 --- /dev/null +++ b/src/wsi/sdl3/wsi_monitor_sdl3.cpp @@ -0,0 +1,157 @@ +#if defined(DXVK_WSI_SDL3) + +#include "../wsi_monitor.h" + +#include "wsi/native_sdl3.h" +#include "wsi_platform_sdl3.h" + +#include "../../util/util_string.h" +#include "../../util/log/log.h" + +#include +#include + + +namespace dxvk::wsi { + + HMONITOR Sdl3WsiDriver::getDefaultMonitor() { + return enumMonitors(0); + } + + + HMONITOR Sdl3WsiDriver::enumMonitors(uint32_t index) { + int count = 0; + + SDL_DisplayID* displays = SDL_GetDisplays(&count); + + HMONITOR result = displays && int(index) < count + ? toHmonitor(displays[index]) + : nullptr; + + SDL_free(displays); + return result; + } + + + HMONITOR Sdl3WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + return enumMonitors(index); + } + + + bool Sdl3WsiDriver::getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + std::wstringstream nameStream; + nameStream << LR"(\\.\DISPLAY)" << displayId; + + std::wstring name = nameStream.str(); + + std::memset(Name, 0, sizeof(Name)); + name.copy(Name, name.length(), 0); + + return true; + } + + + bool Sdl3WsiDriver::getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + SDL_Rect rect = { }; + SDL_GetDisplayBounds(displayId, &rect); + + pRect->left = rect.x; + pRect->top = rect.y; + pRect->right = rect.x + rect.w; + pRect->bottom = rect.y + rect.h; + + return true; + } + + + bool Sdl3WsiDriver::getDisplayMode( + HMONITOR hMonitor, + uint32_t ModeNumber, + WsiMode* pMode) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + int count = 0; + auto* modes = SDL_GetFullscreenDisplayModes(displayId, &count); + + if (!modes) { + Logger::err(str::format("SDL_GetFullscreenDisplayModes: ", SDL_GetError())); + return false; + } + + if (int(ModeNumber) >= count) { + SDL_free(modes); + return false; + } + + convertMode(*modes[ModeNumber], pMode); + + SDL_free(modes); + return true; + } + + + bool Sdl3WsiDriver::getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + auto* mode = SDL_GetCurrentDisplayMode(displayId); + + if (!mode) { + Logger::err(str::format("SDL_GetCurrentDisplayMode: ", SDL_GetError())); + return false; + } + + convertMode(*mode, pMode); + return true; + } + + + bool Sdl3WsiDriver::getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + auto* mode = SDL_GetDesktopDisplayMode(displayId); + + if (!mode) { + Logger::err(str::format("SDL_GetDesktopDisplayMode: ", SDL_GetError())); + return false; + } + + convertMode(*mode, pMode); + return true; + } + + + std::vector Sdl3WsiDriver::getMonitorEdid(HMONITOR hMonitor) { + Logger::err("getMonitorEdid not implemented on this platform."); + return {}; + } + +} + +#endif diff --git a/src/wsi/sdl3/wsi_platform_sdl3.cpp b/src/wsi/sdl3/wsi_platform_sdl3.cpp new file mode 100644 index 00000000000..9975cdac3af --- /dev/null +++ b/src/wsi/sdl3/wsi_platform_sdl3.cpp @@ -0,0 +1,78 @@ +#if defined(DXVK_WSI_SDL3) + +#include "wsi_platform_sdl3.h" +#include "../../util/util_error.h" +#include "../../util/util_string.h" +#include "../../util/util_win32_compat.h" + +#include + +namespace dxvk::wsi { + + Sdl3WsiDriver::Sdl3WsiDriver() { + libsdl = LoadLibraryA( // FIXME: Get soname as string from meson +#if defined(_WIN32) + "SDL3.dll" +#elif defined(__APPLE__) + "libSDL3.0.dylib" +#else + "libSDL3.so.0" +#endif + ); + if (libsdl == nullptr) + throw DxvkError("SDL3 WSI: Failed to load SDL3 DLL."); + + #define SDL_PROC(ret, name, params) \ + name = reinterpret_cast(GetProcAddress(libsdl, #name)); \ + if (name == nullptr) { \ + FreeLibrary(libsdl); \ + libsdl = nullptr; \ + throw DxvkError("SDL3 WSI: Failed to load " #name "."); \ + } + #include "wsi_platform_sdl3_funcs.h" + + if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) + throw DxvkError("SDL3 WSI: Failed to initialize video subsystem."); \ + } + + Sdl3WsiDriver::~Sdl3WsiDriver() { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + FreeLibrary(libsdl); + } + + std::vector Sdl3WsiDriver::getInstanceExtensions() { + if (!SDL_Vulkan_LoadLibrary(nullptr)) + throw DxvkError(str::format("SDL3 WSI: Failed to load Vulkan library: ", SDL_GetError())); + + uint32_t extensionCount = 0; + auto extensions = SDL_Vulkan_GetInstanceExtensions(&extensionCount); + + if (!extensions) + throw DxvkError(str::format("SDL3 WSI: Failed to get instance extensions: ", SDL_GetError())); + + std::vector result(extensionCount); + + for (uint32_t i = 0; i < extensionCount; i++) + result[i] = extensions[i]; + + return result; + } + + static bool createSdl3WsiDriver(WsiDriver **driver) { + try { + *driver = new Sdl3WsiDriver(); + } catch (const DxvkError& e) { + Logger::err(str::format(e.message())); + return false; + } + return true; + } + + WsiBootstrap Sdl3WSI = { + "SDL3", + createSdl3WsiDriver + }; + +} + +#endif diff --git a/src/wsi/sdl3/wsi_platform_sdl3.h b/src/wsi/sdl3/wsi_platform_sdl3.h new file mode 100644 index 00000000000..979a7a97fc8 --- /dev/null +++ b/src/wsi/sdl3/wsi_platform_sdl3.h @@ -0,0 +1,120 @@ +#pragma once + +#include + +#include "../wsi_platform.h" + +#include "../util/util_bit.h" + +namespace dxvk::wsi { + + class Sdl3WsiDriver : public WsiDriver { + private: + HMODULE libsdl; + #define SDL_PROC(ret, name, params) \ + typedef ret (SDLCALL *pfn_##name) params; \ + pfn_##name name; + #include "wsi_platform_sdl3_funcs.h" + + static void convertMode(const SDL_DisplayMode& mode, WsiMode* pMode) { + pMode->width = uint32_t(mode.w); + pMode->height = uint32_t(mode.h); + pMode->refreshRate = WsiRational { + uint32_t(mode.refresh_rate_numerator), + uint32_t(mode.refresh_rate_denominator) }; + // BPP should always be a power of two + // to match Windows behaviour of including padding. + pMode->bitsPerPixel = (uint32_t(-1) >> bit::lzcnt(uint32_t(SDL_BITSPERPIXEL(mode.format) - 1u))) + 1u; + pMode->interlaced = false; + } + public: + Sdl3WsiDriver(); + ~Sdl3WsiDriver(); + + // Platform + virtual std::vector getInstanceExtensions(); + + // Monitor + virtual HMONITOR getDefaultMonitor(); + + virtual HMONITOR enumMonitors(uint32_t index); + + virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index); + + virtual bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]); + + virtual bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect); + + virtual bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode); + + virtual bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor); + + // Window + + virtual void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pWeight); + + virtual void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t weight); + + virtual bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + const WsiMode& mode); + + virtual bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch); + + virtual bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates); + + virtual bool restoreDisplayMode(); + + virtual HMONITOR getWindowMonitor(HWND hWindow); + + virtual bool isWindow(HWND hWindow); + + virtual bool isMinimized(HWND hWindow); + + virtual bool isOccluded(HWND hWindow); + + virtual void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost); + + virtual VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface); + }; + +} diff --git a/src/wsi/sdl3/wsi_platform_sdl3_funcs.h b/src/wsi/sdl3/wsi_platform_sdl3_funcs.h new file mode 100644 index 00000000000..624c2193cf9 --- /dev/null +++ b/src/wsi/sdl3/wsi_platform_sdl3_funcs.h @@ -0,0 +1,22 @@ +SDL_PROC(const char*, SDL_GetError, (void)) +SDL_PROC(SDL_DisplayID*, SDL_GetDisplays, (int*)) +SDL_PROC(bool, SDL_GetDisplayBounds, (SDL_DisplayID, SDL_Rect*)) +SDL_PROC(bool, SDL_GetDisplayUsableBounds, (SDL_DisplayID, SDL_Rect*)) +SDL_PROC(SDL_DisplayMode*, SDL_GetCurrentDisplayMode, (SDL_DisplayID)) +SDL_PROC(SDL_DisplayMode*, SDL_GetDesktopDisplayMode, (SDL_DisplayID)) +SDL_PROC(SDL_DisplayMode**, SDL_GetFullscreenDisplayModes, (SDL_DisplayID, int*)) +SDL_PROC(bool, SDL_GetClosestFullscreenDisplayMode, (SDL_DisplayID, int, int, float, bool, SDL_DisplayMode*)) +SDL_PROC(SDL_DisplayID, SDL_GetDisplayForWindow, (SDL_Window*)) +SDL_PROC(SDL_WindowFlags, SDL_GetWindowFlags, (SDL_Window *)) +SDL_PROC(bool, SDL_GetWindowSize, (SDL_Window*, int*, int*)) +SDL_PROC(bool, SDL_SetWindowSize, (SDL_Window*, int, int)) +SDL_PROC(bool, SDL_SetWindowPosition, (SDL_Window*, int, int)) +SDL_PROC(bool, SDL_SetWindowFullscreen, (SDL_Window*, bool)) +SDL_PROC(bool, SDL_SetWindowFullscreenMode, (SDL_Window*, const SDL_DisplayMode*)) +SDL_PROC(char const* const*, SDL_Vulkan_GetInstanceExtensions, (Uint32*)) +SDL_PROC(bool, SDL_Vulkan_CreateSurface, (SDL_Window*, VkInstance, const VkAllocationCallbacks*, VkSurfaceKHR*)) +SDL_PROC(bool, SDL_Vulkan_LoadLibrary, (const char*)) +SDL_PROC(void, SDL_free, (void*)) +SDL_PROC(bool, SDL_InitSubSystem, (SDL_InitFlags)) +SDL_PROC(void, SDL_QuitSubSystem, (SDL_InitFlags)) +#undef SDL_PROC diff --git a/src/wsi/sdl3/wsi_window_sdl3.cpp b/src/wsi/sdl3/wsi_window_sdl3.cpp new file mode 100644 index 00000000000..10fca478df8 --- /dev/null +++ b/src/wsi/sdl3/wsi_window_sdl3.cpp @@ -0,0 +1,177 @@ +#if defined(DXVK_WSI_SDL3) + +#include "../wsi_window.h" + +#include "native/wsi/native_sdl3.h" +#include "wsi_platform_sdl3.h" + +#include "../../util/util_string.h" +#include "../../util/log/log.h" + +#include +#include + +namespace dxvk::wsi { + + void Sdl3WsiDriver::getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pHeight) { + SDL_Window* window = fromHwnd(hWindow); + + int w = 0; + int h = 0; + + if (!SDL_GetWindowSize(window, &w, &h)) + Logger::err(str::format("SDL3 WSI: SDL_GetWindowSize: ", SDL_GetError())); + + if (pWidth) + *pWidth = uint32_t(w); + + if (pHeight) + *pHeight = uint32_t(h); + } + + + void Sdl3WsiDriver::resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t Width, + uint32_t Height) { + SDL_Window* window = fromHwnd(hWindow); + + if (!SDL_SetWindowSize(window, int32_t(Width), int32_t(Height))) + Logger::err(str::format("SDL3 WSI: SDL_SetWindowSize: ", SDL_GetError())); + } + + + bool Sdl3WsiDriver::setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + const WsiMode& pMode) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + + if (!displayId) + return false; + + pState->sdl3.fullscreenMode = pMode; + return true; + } + + + + bool Sdl3WsiDriver::enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + bool ModeSwitch) { + SDL_DisplayID displayId = fromHmonitor(hMonitor); + SDL_Window* window = fromHwnd(hWindow); + + if (!displayId) + return false; + + SDL_Rect bounds = { }; + + if (!SDL_GetDisplayUsableBounds(displayId, &bounds)) { + Logger::err(str::format("SDL3 WSI: enterFullscreenMode: SDL_GetDisplayUsableBounds: ", SDL_GetError())); + return false; + } + + if (!SDL_SetWindowPosition(window, bounds.x, bounds.y)) { + Logger::err(str::format("SDL3 WSI: enterFullscreenMode: SDL_SetWindowPosition: ", SDL_GetError())); + return false; + } + + SDL_DisplayMode closestMode = { }; + + if (ModeSwitch) { + const auto& mode = pState->sdl3.fullscreenMode; + + if (!SDL_GetClosestFullscreenDisplayMode(displayId, mode.width, mode.height, + float(mode.refreshRate.numerator) / float(mode.refreshRate.denominator), true, &closestMode)) { + Logger::err(str::format("SDL3 WSI: enterFullscreenMode: SDL_GetClosestFullscreenDisplayMode: ", SDL_GetError())); + return false; + } + } + + if (!SDL_SetWindowFullscreenMode(window, ModeSwitch ? &closestMode : nullptr)) { + Logger::err(str::format("SDL3 WSI: enterFullscreenMode: SDL_SetWindowFullscreenMode: ", SDL_GetError())); + return false; + } + + if (!SDL_SetWindowFullscreen(window, true)) { + Logger::err(str::format("SDL3 WSI: enterFullscreenMode: SDL_SetWindowFullscreen: ", SDL_GetError())); + return false; + } + + return true; + } + + + bool Sdl3WsiDriver::leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates) { + SDL_Window* window = fromHwnd(hWindow); + + if (!SDL_SetWindowFullscreen(window, false)) { + Logger::err(str::format("SDL3 WSI: leaveFullscreenMode: SDL_SetWindowFullscreen: ", SDL_GetError())); + return false; + } + + return true; + } + + + bool Sdl3WsiDriver::restoreDisplayMode() { + // Don't need to do anything with SDL3 here. + return true; + } + + + HMONITOR Sdl3WsiDriver::getWindowMonitor(HWND hWindow) { + return toHmonitor(SDL_GetDisplayForWindow(fromHwnd(hWindow))); + } + + + bool Sdl3WsiDriver::isWindow(HWND hWindow) { + SDL_Window* window = fromHwnd(hWindow); + return window != nullptr; + } + + + bool Sdl3WsiDriver::isMinimized(HWND hWindow) { + SDL_Window* window = fromHwnd(hWindow); + return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0; + } + + + bool Sdl3WsiDriver::isOccluded(HWND hWindow) { + return false; + } + + + void Sdl3WsiDriver::updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost) { + // Don't need to do anything with SDL3 here. + } + + + VkResult Sdl3WsiDriver::createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface) { + SDL_Window* window = fromHwnd(hWindow); + + return SDL_Vulkan_CreateSurface(window, instance, nullptr, pSurface) + ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + } + +} + +#endif diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp index cf1c2391c11..c6f12bd50bd 100644 --- a/src/wsi/wsi_platform.cpp +++ b/src/wsi/wsi_platform.cpp @@ -12,6 +12,9 @@ namespace dxvk::wsi { #if defined(DXVK_WSI_WIN32) &Win32WSI, #endif +#if defined(DXVK_WSI_SDL3) + &Sdl3WSI, +#endif #if defined(DXVK_WSI_SDL2) &Sdl2WSI, #endif diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 0cd3b9ef02e..80dd8c507d5 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -105,6 +105,9 @@ namespace dxvk::wsi { #if defined(DXVK_WSI_WIN32) extern WsiBootstrap Win32WSI; #endif +#if defined(DXVK_WSI_SDL3) + extern WsiBootstrap Sdl3WSI; +#endif #if defined(DXVK_WSI_SDL2) extern WsiBootstrap Sdl2WSI; #endif diff --git a/src/wsi/wsi_window.h b/src/wsi/wsi_window.h index a67283df154..4f65feaf82f 100644 --- a/src/wsi/wsi_window.h +++ b/src/wsi/wsi_window.h @@ -19,6 +19,11 @@ namespace dxvk::wsi { RECT rect = { 0, 0, 0, 0 }; } win; #endif +#if defined(DXVK_WSI_SDL3) + struct { + WsiMode fullscreenMode = { }; + } sdl3; +#endif #if defined(DXVK_WSI_SDL2) // Nothing to store #endif