1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-18 20:52:10 +01:00

[wsi] Implement wsi interface for Windows

This commit is contained in:
Joshua Ashton 2022-08-09 12:38:15 +01:00 committed by Philip Rebohle
parent 050e5b327c
commit efa6523e3e
4 changed files with 441 additions and 0 deletions

View File

@ -1,3 +1,4 @@
subdir('wsi')
subdir('util')
subdir('spirv')
subdir('vulkan')

18
src/wsi/meson.build Normal file
View File

@ -0,0 +1,18 @@
wsi_win32_src = [
'win32/wsi_monitor_win32.cpp',
'win32/wsi_window_win32.cpp',
]
if dxvk_wsi == 'win32'
wsi_src = wsi_win32_src
wsi_deps = []
else
error('Unknown wsi')
endif
wsi_lib = static_library('wsi', wsi_src,
dependencies : wsi_deps,
include_directories : [ dxvk_include_path ])
wsi_dep = declare_dependency(
link_with : [ wsi_lib ])

View File

@ -0,0 +1,134 @@
#include "../wsi_monitor.h"
#include "../../util/log/log.h"
#include <cstring>
namespace dxvk::wsi {
HMONITOR getDefaultMonitor() {
return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
}
struct MonitorEnumInfo {
UINT iMonitorId;
HMONITOR oMonitor;
};
static BOOL CALLBACK MonitorEnumProc(
HMONITOR hmon,
HDC hdc,
LPRECT rect,
LPARAM lp) {
auto data = reinterpret_cast<MonitorEnumInfo*>(lp);
if (data->iMonitorId--)
return TRUE; /* continue */
data->oMonitor = hmon;
return FALSE; /* stop */
}
HMONITOR enumMonitors(uint32_t index) {
MonitorEnumInfo info;
info.iMonitorId = index;
info.oMonitor = nullptr;
::EnumDisplayMonitors(
nullptr, nullptr, &MonitorEnumProc,
reinterpret_cast<LPARAM>(&info));
return info.oMonitor;
}
bool getDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]) {
// Query monitor info to get the device name
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Win32 WSI: getDisplayName: Failed to query monitor info");
return false;
}
std::memcpy(Name, monInfo.szDevice, sizeof(Name));
return true;
}
bool getDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Win32 WSI: getDisplayName: Failed to query monitor info");
return false;
}
*pRect = monInfo.rcMonitor;
return true;
}
static inline void convertMode(const DEVMODEW& mode, WsiMode* pMode) {
pMode->width = mode.dmPelsWidth;
pMode->height = mode.dmPelsHeight;
pMode->refreshRate = WsiRational{ mode.dmDisplayFrequency * 1000, 1000 };
pMode->bitsPerPixel = mode.dmBitsPerPel;
pMode->interlaced = mode.dmDisplayFlags & DM_INTERLACED;
}
static inline bool retrieveDisplayMode(
HMONITOR hMonitor,
DWORD modeNumber,
WsiMode* pMode) {
// Query monitor info to get the device name
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Win32 WSI: retrieveDisplayMode: Failed to query monitor info");
return false;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
if (!::EnumDisplaySettingsW(monInfo.szDevice, modeNumber, &devMode))
return false;
convertMode(devMode, pMode);
return true;
}
bool getDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode) {
return retrieveDisplayMode(hMonitor, modeNumber, pMode);
}
bool getCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) {
return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode);
}
bool getDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) {
return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode);
}
}

View File

@ -0,0 +1,288 @@
#include "../wsi_window.h"
#include "../wsi_monitor.h"
#include "../../util/util_string.h"
#include "../../util/log/log.h"
namespace dxvk::wsi {
static bool getMonitorDisplayMode(
HMONITOR hMonitor,
DWORD modeNum,
DEVMODEW* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Failed to query monitor info");
return false;
}
return ::EnumDisplaySettingsW(monInfo.szDevice, modeNum, pMode);
}
static bool setMonitorDisplayMode(
HMONITOR hMonitor,
DEVMODEW* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Failed to query monitor info");
return E_FAIL;
}
Logger::info(str::format("Setting display mode: ",
pMode->dmPelsWidth, "x", pMode->dmPelsHeight, "@",
pMode->dmDisplayFrequency));
DEVMODEW curMode = { };
curMode.dmSize = sizeof(curMode);
if (getMonitorDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, &curMode)) {
bool eq = curMode.dmPelsWidth == pMode->dmPelsWidth
&& curMode.dmPelsHeight == pMode->dmPelsHeight
&& curMode.dmBitsPerPel == pMode->dmBitsPerPel;
if (pMode->dmFields & DM_DISPLAYFREQUENCY)
eq &= curMode.dmDisplayFrequency == pMode->dmDisplayFrequency;
if (eq)
return true;
}
LONG status = ::ChangeDisplaySettingsExW(monInfo.szDevice,
pMode, nullptr, CDS_FULLSCREEN, nullptr);
if (status != DISP_CHANGE_SUCCESSFUL) {
pMode->dmFields &= ~DM_DISPLAYFREQUENCY;
status = ::ChangeDisplaySettingsExW(monInfo.szDevice,
pMode, nullptr, CDS_FULLSCREEN, nullptr);
}
return status == DISP_CHANGE_SUCCESSFUL;
}
static BOOL CALLBACK restoreDisplayModeCallback(
HMONITOR hMonitor,
HDC hDC,
LPRECT pRect,
LPARAM pUserdata) {
auto success = reinterpret_cast<bool*>(pUserdata);
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
if (!getMonitorDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, &devMode)) {
*success = false;
return false;
}
Logger::info(str::format("Restoring display mode: ",
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
devMode.dmDisplayFrequency));
if (!setMonitorDisplayMode(hMonitor, &devMode)) {
*success = false;
return false;
}
return true;
}
void getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pHeight) {
RECT rect = { };
::GetClientRect(hWindow, &rect);
if (pWidth)
*pWidth = rect.right - rect.left;
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
void resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t height) {
// Adjust window position and size
RECT newRect = { 0, 0, 0, 0 };
RECT oldRect = { 0, 0, 0, 0 };
::GetWindowRect(hWindow, &oldRect);
::SetRect(&newRect, 0, 0, width, height);
::AdjustWindowRectEx(&newRect,
::GetWindowLongW(hWindow, GWL_STYLE), FALSE,
::GetWindowLongW(hWindow, GWL_EXSTYLE));
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
::OffsetRect(&newRect, oldRect.left, oldRect.top);
::MoveWindow(hWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
}
bool setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Win32 WSI: setWindowMode: Failed to query monitor info");
return false;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
devMode.dmPelsWidth = mode.width;
devMode.dmPelsHeight = mode.height;
devMode.dmBitsPerPel = mode.bitsPerPixel;
if (mode.refreshRate.numerator != 0) {
devMode.dmFields |= DM_DISPLAYFREQUENCY;
devMode.dmDisplayFrequency = mode.refreshRate.numerator
/ mode.refreshRate.denominator;
}
Logger::info(str::format("Setting display mode: ",
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
devMode.dmDisplayFrequency));
return setMonitorDisplayMode(hMonitor, &devMode);
}
bool enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch) {
// Find a display mode that matches what we need
::GetWindowRect(hWindow, &pState->rect);
// Change the window flags to remove the decoration etc.
LONG style = ::GetWindowLongW(hWindow, GWL_STYLE);
LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);
pState->style = style;
pState->exstyle = exstyle;
style &= ~WS_OVERLAPPEDWINDOW;
exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
::SetWindowLongW(hWindow, GWL_STYLE, style);
::SetWindowLongW(hWindow, GWL_EXSTYLE, exstyle);
RECT rect = { };
getDesktopCoordinates(hMonitor, &rect);
::SetWindowPos(hWindow, HWND_TOPMOST,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
return true;
}
bool leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState) {
// Only restore the window style if the application hasn't
// changed them. This is in line with what native DXGI does.
LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE;
LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
if (curStyle == (pState->style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
&& curExstyle == (pState->exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
::SetWindowLongW(hWindow, GWL_STYLE, pState->style);
::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->exstyle);
}
// Restore window position and apply the style
const RECT rect = pState->rect;
::SetWindowPos(hWindow, (pState->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_FRAMECHANGED | SWP_NOACTIVATE);
return true;
}
bool restoreDisplayMode() {
bool success = true;
bool result = ::EnumDisplayMonitors(nullptr, nullptr,
&restoreDisplayModeCallback,
reinterpret_cast<LPARAM>(&success));
return result && success;
}
HMONITOR getWindowMonitor(HWND hWindow) {
RECT windowRect = { 0, 0, 0, 0 };
::GetWindowRect(hWindow, &windowRect);
HMONITOR monitor = ::MonitorFromPoint(
{ (windowRect.left + windowRect.right) / 2,
(windowRect.top + windowRect.bottom) / 2 },
MONITOR_DEFAULTTOPRIMARY);
return monitor;
}
bool isWindow(HWND hWindow) {
return ::IsWindow(hWindow);
}
void updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost) {
RECT bounds = { };
wsi::getDesktopCoordinates(hMonitor, &bounds);
// In D3D9, changing display modes re-forces the window
// to become top most, whereas in DXGI, it does not.
if (forceTopmost) {
::SetWindowPos(hWindow, HWND_TOPMOST,
bounds.left, bounds.top,
bounds.right - bounds.left, bounds.bottom - bounds.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
} else {
::MoveWindow(hWindow, bounds.left, bounds.top,
bounds.right - bounds.left, bounds.bottom - bounds.top, TRUE);
}
}
VkResult createSurface(
HWND hWindow,
const Rc<vk::InstanceFn>& vki,
VkSurfaceKHR* pSurface) {
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(
GetWindowLongPtr(hWindow, GWLP_HINSTANCE));
VkWin32SurfaceCreateInfoKHR info = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
info.hinstance = hInstance;
info.hwnd = hWindow;
return vki->vkCreateWin32SurfaceKHR(
vki->instance(), &info, nullptr, pSurface);
}
}