Skip to content
Closed
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ As an alternative for the default wine based build DXMT can be built natively on
meson setup --native-file build-osx.txt -Ddxmt_native=true -Dnative_llvm_path=toolchains/llvm-darwin build
meson compile -C build
```
You also need to specify a specific WSI backend to use, otherwise the library falls back to the headless WSI implementation. You can do so by appending the `-Ddxmt_wsi_sdl2=true` flag on running meson setup.
6 changes: 6 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ endif
if dxmt_native
add_project_arguments('-DDXMT_NATIVE=1', language: 'cpp')
add_project_arguments('-DDXMT_NATIVE=1', language: 'c')

if get_option('dxmt_wsi_sdl2') == true
add_project_arguments('-DDXMT_WSI_SDL2=1', language: 'cpp')
add_project_arguments('-DDXMT_WSI_SDL2=1', language: 'c')
endif

endif

add_project_arguments('-DDXMT_PAGE_SIZE=4096', language: 'cpp')
Expand Down
1 change: 1 addition & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ option('native_llvm_path', type : 'string', value: '/usr/local/opt/llvm@15')
option('build_airconv_for_windows', type : 'boolean', value : false)
option('dxmt_debug', type : 'boolean', value : false)
option('dxmt_native', type : 'boolean', value : false)
option('dxmt_wsi_sdl2', type : 'boolean', value : false)
option('wine_build_path', type : 'string')
option('wine_install_path', type : 'string')
option('wine_builtin_dll', type : 'boolean', value : false)
Expand Down
14 changes: 12 additions & 2 deletions src/d3d11/d3d11_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,21 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject<IDXGISwapChain4, MTLD3D1
monitor_(wsi::getWindowMonitor(hWnd)),
hud(WMT::DeveloperHUDProperties::instance()) {

#if defined(DXMT_NATIVE)
native_view_ = wsi::createMetalViewFromHWND((intptr_t)hWnd, pDevice->GetMTLDevice(), layer_weak_);

if (!native_view_) {
ERR("Failed to create metal view, it seems like the used wsi implementation can't create metal view from window handle.");
abort();
}
#else
native_view_ = WMT::CreateMetalViewFromHWND((intptr_t)hWnd, pDevice->GetMTLDevice(), layer_weak_);

if (!native_view_) {
ERR("Failed to create metal view, it seems like your Wine has no exported symbols needed by DXMT.");
abort();
}
#endif

if constexpr (EnableMetalFX) {
scale_factor = std::max(Config::getInstance().getOption<float>("d3d11.metalSpatialUpscaleFactor", 2), 1.0f);
Expand Down Expand Up @@ -140,8 +149,9 @@ class MTLD3D11SwapChain final : public MTLDXGISubObject<IDXGISwapChain4, MTLD3D1
.MiscFlags = {},
.TextureLayout = {},
};
// if (desc_.BufferUsage & DXGI_USAGE_SHADER_INPUT)
backbuffer_desc_.BindFlags |= D3D11_BIND_SHADER_RESOURCE;

if (desc_.BufferUsage & DXGI_USAGE_SHADER_INPUT)
backbuffer_desc_.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
if (desc_.BufferUsage & DXGI_USAGE_UNORDERED_ACCESS)
backbuffer_desc_.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;

Expand Down
22 changes: 20 additions & 2 deletions src/util/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,29 @@ util_src = files([
'sha1/sha1_util.cpp',
])

wsi_deps = []

if dxmt_native
util_src += files([
'wsi_monitor_headless.cpp',
'wsi_window_headless.cpp',
'wsi_platform_darwin.cpp',
])

# sdl2 wsi
lib_sdl2 = dependency('SDL2', required: false)
if lib_sdl2.found() and get_option('dxmt_wsi_sdl2') == true
util_src += files([
'wsi_monitor_sdl2.cpp',
'wsi_window_sdl2.cpp',
'wsi_platform_sdl2.cpp',
])

wsi_deps += [ lib_sdl2.partial_dependency(compile_args: true, includes: true)]
else # headless
util_src += files([
'wsi_monitor_headless.cpp',
'wsi_window_headless.cpp',
])
endif
else
util_src += files([
'wsi_monitor_win32.cpp',
Expand All @@ -38,6 +55,7 @@ else
endif

util_lib = static_library('util', util_src,
dependencies: wsi_deps,
include_directories : [ dxmt_include_path ],
)

Expand Down
130 changes: 130 additions & 0 deletions src/util/wsi_monitor_sdl2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* This file is part of DXMT, Copyright (c) 2023 Feifan He
*
* Derived from a part of DXVK (originally under zlib License),
* Copyright (c) 2017 Philip Rebohle
* Copyright (c) 2019 Joshua Ashton
*
* See <https://github.com/doitsujin/dxvk/blob/master/LICENSE>
*/

#if defined(DXMT_WSI_SDL2)

#include "wsi_monitor.hpp"
#include "wsi_platform_sdl2.hpp"

#include "log/log.hpp"
#include "util_string.hpp"

#include <SDL2/SDL.h>

#include <cstring>

namespace dxmt::wsi {

HMONITOR enumMonitors(uint32_t index) {
if (!isValidDisplay(index)) {
Logger::err(str::format("SDL2 WSI: Failed to get display for index ", index));
return nullptr;
}
return toHmonitor((int32_t)index);
}

HMONITOR getDefaultMonitor() {
return enumMonitors(0);
}

bool getDisplayName(HMONITOR hMonitor, WCHAR (&Name)[32]) {
int32_t display_id = fromHmonitor(hMonitor);
if (!isValidDisplay(display_id)) {
Logger::err("SDL2 WSI: Invalid display ID for monitor");
return false;
}

std::wstringstream display_name;
display_name << L"\\\\.\\DISPLAY" << display_id;
std::wstring display_name_str = display_name.str();

std::memset(Name, 0, sizeof(WCHAR) * 32);
if (display_name_str.length() >= 32) {
Logger::err("SDL2 WSI: Display name exceeds maximum length of 32 characters");
return false;
}
display_name_str.copy(Name, display_name_str.length(), 0);

return true;
}

bool getDesktopCoordinates(HMONITOR hMonitor, RECT *pRect) {
const int32_t displayId = fromHmonitor(hMonitor);

if (!isValidDisplay(displayId))
return false;

SDL_Rect rect = { };
SDL2Initializer::get().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 getDisplayMode(HMONITOR hMonitor, uint32_t modeNumber, WsiMode *pMode) {
const int32_t displayId = fromHmonitor(hMonitor);

if (!isValidDisplay(displayId))
return false;

SDL_DisplayMode mode = { };
if (SDL2Initializer::get().SDL_GetDisplayMode(displayId, modeNumber, &mode) != 0)
return false;

pMode->width = uint32_t(mode.w);
pMode->height = uint32_t(mode.h);
pMode->refreshRate = WsiRational{ uint32_t(mode.refresh_rate) * 1000, 1000 };
pMode->bitsPerPixel = roundToNextPow2(SDL_BITSPERPIXEL(mode.format));
pMode->interlaced = false;

return true;
}

bool getCurrentDisplayMode(HMONITOR hMonitor, WsiMode *pMode) {
const int32_t displayId = fromHmonitor(hMonitor);

if (!isValidDisplay(displayId))
return false;

SDL_DisplayMode mode = { };
if (SDL2Initializer::get().SDL_GetCurrentDisplayMode(displayId, &mode) != 0) {
Logger::err(str::format("SDL_GetCurrentDisplayMode: ", SDL2Initializer::get().SDL_GetError()));
return false;
}

convertMode(mode, pMode);

return true;
}

bool getDesktopDisplayMode(HMONITOR hMonitor, WsiMode *pMode) {
const int32_t displayId = fromHmonitor(hMonitor);

if (!isValidDisplay(displayId))
return false;

SDL_DisplayMode mode = { };
if (SDL2Initializer::get().SDL_GetDesktopDisplayMode(displayId, &mode) != 0) {
Logger::err(str::format("SDL_GetCurrentDisplayMode: ", SDL2Initializer::get().SDL_GetError()));
return false;
}

convertMode(mode, pMode);

return true;
}

} // namespace dxmt::wsi

#endif // defined(DXMT_WSI_SDL2)
48 changes: 48 additions & 0 deletions src/util/wsi_platform_sdl2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#if defined(DXMT_WSI_SDL2)

#include "wsi_platform_sdl2.hpp"

#include "log/log.hpp"
#include "util_string.hpp"

#include <dlfcn.h>
#include <SDL2/SDL.h>

#include <mach-o/dyld.h>

namespace dxmt::wsi {

// this is messy, but dynamically linking on macos seems pretty finicky
SDL2Initializer::SDL2Initializer() {

// first, try to see if we already loaded the dylib in the process...
uint32_t image_count = _dyld_image_count();
for (uint32_t i = 0; i < image_count; i++) {
const char* image_name = _dyld_get_image_name(i);
if(strstr(image_name, "SDL2") != nullptr) {
libsdl = dlopen(image_name, RTLD_NOW | RTLD_LOCAL);
if (libsdl) {
Logger::trace(str::format("SDL2 WSI: Found SDL2 in loaded images: ", image_name));
break;
}
}
}

// if there's no sdl, the app might've statically linked against sdl, so we can try dlsym with RTLD_DEFAULT
#define SDL_PROC(ret, name, params) \
name = reinterpret_cast<pfn_##name>(dlsym((libsdl == nullptr ? RTLD_DEFAULT : libsdl), #name)); \
if (!name) { \
Logger::err(str::format("SDL2 WSI: Failed to get function named ", #name)); \
}
#include "wsi_platform_sdl2_funcs.h"

if (!SDL_GetClosestDisplayMode) {
ERR("SDL2 WSI: failed to get SDL function pointers.");
abort();
}

}

}

#endif // defined(DXMT_WSI_SDL2)
69 changes: 69 additions & 0 deletions src/util/wsi_platform_sdl2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#if defined(DXMT_WSI_SDL2)

#include "wsi_monitor.hpp"

#include <SDL2/SDL.h>

namespace dxmt::wsi {

class SDL2Initializer { // using this to force loadlibrary sdl2 at startup for now
public:
void* libsdl;
#define SDL_PROC(ret, name, params) \
typedef ret (SDLCALL *pfn_##name) params; \
pfn_##name name;
#include "wsi_platform_sdl2_funcs.h"

SDL2Initializer();

static SDL2Initializer get() {
static SDL2Initializer instance;
return instance;
}
};

static inline int32_t fromHmonitor(HMONITOR hMonitor) {
return static_cast<int32_t>(reinterpret_cast<intptr_t>(hMonitor)) - 1;
}

static inline HMONITOR toHmonitor(int32_t displayId) {
return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1));
}

static inline bool isValidDisplay(int32_t display_id) {
const int32_t display_cnt = SDL2Initializer::get().SDL_GetNumVideoDisplays();
return display_id >= 0 && display_id < display_cnt;
}

static inline SDL_Window* fromHwnd(HWND hWindow) {
return reinterpret_cast<SDL_Window*>(hWindow);
}

static inline HWND toHwnd(SDL_Window* pWindow) {
return reinterpret_cast<HWND>(pWindow);
}

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 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) * 1000, 1000 };
// BPP should always be a power of two
// to match Windows behaviour of including padding.
pMode->bitsPerPixel = roundToNextPow2(SDL_BITSPERPIXEL(mode.format));
pMode->interlaced = false;
}

}

#endif // defined(DXMT_WSI_SDL2)
28 changes: 28 additions & 0 deletions src/util/wsi_platform_sdl2_funcs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* This file is part of DXMT, Copyright (c) 2023 Feifan He
*
* Derived from a part of DXVK (originally under zlib License),
* Copyright (c) 2017 Philip Rebohle
* Copyright (c) 2019 Joshua Ashton
*
* See <https://github.com/doitsujin/dxvk/blob/master/LICENSE>
*/

#if defined(DXMT_WSI_SDL2)
SDL_PROC(SDL_DisplayMode*, SDL_GetClosestDisplayMode, (int, const SDL_DisplayMode*, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetCurrentDisplayMode, (int, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetDesktopDisplayMode, (int, SDL_DisplayMode*))
SDL_PROC(int, SDL_GetDisplayBounds, (int, SDL_Rect*))
SDL_PROC(int, SDL_GetDisplayMode, (int, int, SDL_DisplayMode*))
SDL_PROC(const char*, SDL_GetError, (void))
SDL_PROC(int, SDL_GetNumVideoDisplays, (void))
SDL_PROC(int, SDL_GetWindowDisplayIndex, (SDL_Window*))
SDL_PROC(int, SDL_SetWindowDisplayMode, (SDL_Window*, const SDL_DisplayMode*))
SDL_PROC(int, SDL_SetWindowFullscreen, (SDL_Window*, Uint32))
SDL_PROC(SDL_WindowFlags, SDL_GetWindowFlags, (SDL_Window *))
SDL_PROC(void, SDL_GetWindowSize, (SDL_Window*, int*, int*))
SDL_PROC(void, SDL_SetWindowSize, (SDL_Window*, int, int))
SDL_PROC(SDL_MetalView, SDL_Metal_CreateView, (SDL_Window*))
SDL_PROC(void*, SDL_Metal_GetLayer, (SDL_MetalView))
#undef SDL_PROC
#endif // defined(DXMT_WSI_SDL2)
Loading