diff --git a/include/native/wsi/native_glfw.h b/include/native/wsi/native_glfw.h new file mode 100644 index 00000000..e4946e02 --- /dev/null +++ b/include/native/wsi/native_glfw.h @@ -0,0 +1,25 @@ +#include + +#include + +namespace dxvk::wsi { + + inline GLFWwindow* fromHwnd(HWND hWindow) { + return reinterpret_cast(hWindow); + } + + inline HWND toHwnd(GLFWwindow* pWindow) { + return reinterpret_cast(pWindow); + } + + // Offset so null HMONITORs go to -1 + inline int32_t fromHmonitor(HMONITOR hMonitor) { + return static_cast(reinterpret_cast(hMonitor)) - 1; + } + + // Offset so -1 display id goes to 0 == NULL + inline HMONITOR toHmonitor(int32_t displayId) { + return reinterpret_cast(static_cast(displayId + 1)); + } + +} \ No newline at end of file diff --git a/include/native/wsi/native_wsi.h b/include/native/wsi/native_wsi.h index 00a29906..cfb64f12 100644 --- a/include/native/wsi/native_wsi.h +++ b/include/native/wsi/native_wsi.h @@ -4,6 +4,8 @@ #error You shouldnt be using this code path. #elif DXVK_WSI_SDL2 #include "wsi/native_sdl2.h" +#elif DXVK_WSI_GLFW +#include "wsi/native_glfw.h" #else #error Unknown wsi! #endif \ No newline at end of file diff --git a/meson.build b/meson.build index 12d37300..39a28e05 100644 --- a/meson.build +++ b/meson.build @@ -103,8 +103,6 @@ if platform == 'windows' dxvk_name_prefix = '' compiler_args += ['-DDXVK_WSI_WIN32'] else - lib_sdl2 = cpp.find_library('SDL2') - wrc = find_program('touch') wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] ) @@ -114,9 +112,17 @@ else './include/native/directx' ] - dxvk_wsi = 'sdl2' + dxvk_wsi = get_option('dxvk_native_wsi') + + if dxvk_wsi == 'sdl2' + lib_sdl2 = cpp.find_library('SDL2') + compiler_args += ['-DDXVK_WSI_SDL2'] + elif dxvk_wsi == 'glfw' + lib_glfw = cpp.find_library('glfw') + compiler_args += ['-DDXVK_WSI_GLFW'] + endif + dxvk_name_prefix = 'libdxvk_' - compiler_args += ['-DDXVK_WSI_SDL2'] link_args += [ '-static-libgcc', diff --git a/meson_options.txt b/meson_options.txt index 36fd0136..5ac9ea7b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,3 +3,5 @@ option('enable_d3d9', type : 'boolean', value : true, description: 'Build D3D9' option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10') option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11') option('build_id', type : 'boolean', value : false) + +option('dxvk_native_wsi', type : 'string', value : 'sdl2', description: 'WSI system to use if building natively.') \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 387bc4d4..678ab268 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -127,6 +127,10 @@ elif dxvk_wsi == 'sdl2' dxvk_src += [ 'platform/dxvk_sdl2_exts.cpp' ] +elif dxvk_wsi == 'glfw' + dxvk_src += [ + 'platform/dxvk_glfw_exts.cpp' + ] endif dxvk_extra_deps = [ dependency('threads') ] diff --git a/src/dxvk/platform/dxvk_glfw_exts.cpp b/src/dxvk/platform/dxvk_glfw_exts.cpp new file mode 100644 index 00000000..f5cfee88 --- /dev/null +++ b/src/dxvk/platform/dxvk_glfw_exts.cpp @@ -0,0 +1,49 @@ +#include "../dxvk_platform_exts.h" + +#include "../../vulkan/vulkan_loader.h" +#include + +namespace dxvk { + + DxvkPlatformExts DxvkPlatformExts::s_instance; + + std::string_view DxvkPlatformExts::getName() { + return "GLFW WSI"; + } + + DxvkNameSet DxvkPlatformExts::getInstanceExtensions() { + if (!glfwVulkanSupported()) + throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!")); + + uint32_t extensionCount = 0; + const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount); + + if (extensionCount == 0) + throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions")); + + DxvkNameSet names; + for (uint32_t i = 0; i < extensionCount; ++i) { + names.add(extensionArray[i]); + } + + return names; + } + + + DxvkNameSet DxvkPlatformExts::getDeviceExtensions( + uint32_t adapterId) { + return DxvkNameSet(); + } + + + void DxvkPlatformExts::initInstanceExtensions() { + //Nothing needs to be done here on GLFW + } + + + void DxvkPlatformExts::initDeviceExtensions( + const DxvkInstance* instance) { + //Nothing needs to be done here on GLFW + } + +} \ No newline at end of file diff --git a/src/wsi/glfw/wsi_monitor_glfw.cpp b/src/wsi/glfw/wsi_monitor_glfw.cpp new file mode 100644 index 00000000..7715118b --- /dev/null +++ b/src/wsi/glfw/wsi_monitor_glfw.cpp @@ -0,0 +1,163 @@ +#include "../wsi_monitor.h" + +#include "wsi/native_wsi.h" +#include "wsi_platform_glfw.h" + +#include "../../util/util_string.h" +#include "../../util/log/log.h" + +#include +#include + +namespace dxvk::wsi { + + HMONITOR getDefaultMonitor() { + return enumMonitors(0); + } + + + HMONITOR enumMonitors(uint32_t index) { + return isDisplayValid(int32_t(index)) + ? toHmonitor(index) + : nullptr; + } + + bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]) { + const int32_t displayId = fromHmonitor(hMonitor); + + if (!isDisplayValid(displayId)) + return false; + + std::wstringstream nameStream; + nameStream << LR"(\\.\DISPLAY)" << (displayId + 1); + + std::wstring name = nameStream.str(); + + std::memset(Name, 0, sizeof(Name)); + name.copy(Name, name.length(), 0); + + return true; + } + + + bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect) { + const int32_t displayId = fromHmonitor(hMonitor); + + if (!isDisplayValid(displayId)) + return false; + + int32_t displayCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&displayCount); + GLFWmonitor* monitor = monitors[displayId]; + + int32_t x; + int32_t y; + int32_t w; + int32_t h; + glfwGetMonitorWorkarea(monitor, &x, &y, &w, &h); + + pRect->left = x; + pRect->top = y; + pRect->right = x + w; + pRect->bottom = y + h; + + return true; + } + + static inline uint32_t roundToNextPow2(uint32_t num) { + if (num-- == 0) + return 0; + + num |= num >> 1; + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + + return ++num; + } + + + static inline void convertMode(const GLFWvidmode& mode, WsiMode* pMode) { + pMode->width = uint32_t(mode.width); + pMode->height = uint32_t(mode.height); + pMode->refreshRate = WsiRational{uint32_t(mode.refreshRate) * 1000, 1000}; + // BPP should always be a power of two + // to match Windows behaviour of including padding. + pMode->bitsPerPixel = roundToNextPow2(mode.blueBits + mode.redBits + mode.greenBits); + pMode->interlaced = false; + } + + + bool getDisplayMode( + HMONITOR hMonitor, + uint32_t ModeNumber, + WsiMode* pMode) { + const int32_t displayId = fromHmonitor(hMonitor); + int32_t displayCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&displayCount); + GLFWmonitor* monitor = monitors[displayId]; + + if (!isDisplayValid(displayId)) + return false; + + int32_t count = 0; + const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); + + if(ModeNumber >= uint32_t(count)) + return false; + + convertMode(modes[ModeNumber], pMode); + + return true; + } + + + bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + const int32_t displayId = fromHmonitor(hMonitor); + + if (!isDisplayValid(displayId)) + return false; + + int32_t displayCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&displayCount); + GLFWmonitor* monitor = monitors[displayId]; + + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + convertMode(*mode, pMode); + + return true; + } + + + bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + const int32_t displayId = fromHmonitor(hMonitor); + + if (!isDisplayValid(displayId)) + return false; + + int32_t displayCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&displayCount); + GLFWmonitor* monitor = monitors[displayId]; + + //TODO: actually implement this properly, currently we just grab the current one + convertMode(*glfwGetVideoMode(monitor), pMode); + + return true; + } + + std::vector getMonitorEdid(HMONITOR hMonitor) { + Logger::err("getMonitorEdid not implemented on this platform."); + return {}; + } + +} \ No newline at end of file diff --git a/src/wsi/glfw/wsi_platform_glfw.h b/src/wsi/glfw/wsi_platform_glfw.h new file mode 100644 index 00000000..25753494 --- /dev/null +++ b/src/wsi/glfw/wsi_platform_glfw.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../vulkan/vulkan_loader.h" +#include + +#include "../wsi_monitor.h" + +namespace dxvk::wsi { + + /** + * \brief Impl-dependent state + */ + struct DxvkWindowState { + }; + + inline bool isDisplayValid(int32_t displayId) { + int32_t displayCount = 0; + glfwGetMonitors(&displayCount); + + return displayId < displayCount && displayId >= 0; + } + +} \ No newline at end of file diff --git a/src/wsi/glfw/wsi_window_glfw.cpp b/src/wsi/glfw/wsi_window_glfw.cpp new file mode 100644 index 00000000..70031250 --- /dev/null +++ b/src/wsi/glfw/wsi_window_glfw.cpp @@ -0,0 +1,144 @@ +#include "../wsi_window.h" + +#include "native/wsi/native_wsi.h" +#include "wsi_platform_glfw.h" + +#include "../../util/util_string.h" +#include "../../util/log/log.h" + +#include +#include "../../vulkan/vulkan_loader.h" +#include + +namespace dxvk::wsi { + + void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pHeight) { + GLFWwindow* window = fromHwnd(hWindow); + + int32_t w, h; + glfwGetWindowSize(window, &w, &h); + + if (pWidth) + *pWidth = uint32_t(w); + + if (pHeight) + *pHeight = uint32_t(h); + } + + + void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t Width, + uint32_t Height) { + GLFWwindow* window = fromHwnd(hWindow); + + glfwSetWindowSize(window, int32_t(Width), int32_t(Height)); + } + + + bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& pMode) { + const int32_t displayId = fromHmonitor(hMonitor); + GLFWwindow* window = fromHwnd(hWindow); + + if (!isDisplayValid(displayId)) + return false; + + int32_t displayCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&displayCount); + GLFWmonitor* monitor = monitors[displayId]; + + GLFWvidmode wantedMode = {}; + wantedMode.width = pMode.width; + wantedMode.height = pMode.height; + wantedMode.refreshRate = pMode.refreshRate.numerator != 0 + ? pMode.refreshRate.numerator / pMode.refreshRate.denominator + : 0; + // TODO: Implement lookup format for bitsPerPixel here. + + glfwSetWindowMonitor(window, monitor, 0, 0, wantedMode.width, wantedMode.width, wantedMode.refreshRate); + + return true; + } + + bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + bool ModeSwitch) { + const int32_t displayId = fromHmonitor(hMonitor); + GLFWwindow* window = fromHwnd(hWindow); + + if (!isDisplayValid(displayId)) + return false; + + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + auto videoMode = glfwGetVideoMode(monitor); + + // TODO: Set this on the correct monitor. + // Docs aren't clear on this... + glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); + + return true; + } + + + bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates) { + GLFWwindow* window = fromHwnd(hWindow); + + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + auto videoMode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(window, nullptr, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); + + return true; + } + + + bool restoreDisplayMode() { + // Don't need to do anything with GLFW here. + return true; + } + + + HMONITOR getWindowMonitor(HWND hWindow) { + // TODO: implement this with glfwGetWindowMonitor + // (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows) + // GLFWwindow* window = fromHwnd(hWindow); + const int32_t displayId = 0; + + return toHmonitor(displayId); + } + + + bool isWindow(HWND hWindow) { + GLFWwindow* window = fromHwnd(hWindow); + return window != nullptr; + } + + void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost) { + // Don't need to do anything with GLFW here. + } + + VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface) { + GLFWwindow* window = fromHwnd(hWindow); + + return glfwCreateWindowSurface(instance, window, nullptr, pSurface); + } + +} \ No newline at end of file diff --git a/src/wsi/meson.build b/src/wsi/meson.build index 17efa26b..27603cea 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -12,12 +12,20 @@ wsi_sdl2_src = [ 'sdl2/wsi_window_sdl2.cpp', ] +wsi_glfw_src = [ + 'glfw/wsi_monitor_glfw.cpp', + 'glfw/wsi_window_glfw.cpp', +] + if dxvk_wsi == 'win32' wsi_src = wsi_common_src + wsi_win32_src wsi_deps = [ dep_displayinfo ] elif dxvk_wsi == 'sdl2' wsi_src = wsi_common_src + wsi_sdl2_src wsi_deps = [ dep_displayinfo, lib_sdl2 ] +elif dxvk_wsi == 'glfw' + wsi_src = wsi_common_src + wsi_glfw_src + wsi_deps = [ dep_displayinfo, lib_glfw ] else error('Unknown wsi') endif diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 2cbcd229..38b5c5aa 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -4,4 +4,6 @@ #include "win32/wsi_platform_win32.h" #elif defined(DXVK_WSI_SDL2) #include "sdl2/wsi_platform_sdl2.h" +#elif defined(DXVK_WSI_GLFW) +#include "glfw/wsi_platform_glfw.h" #endif