protonscr

native: Ability to set a WSI programmatically

dxvkopen enhancementnative
doitsujin/dxvk#4093 · opened 2024-06-27 by smcv · updated 2025-05-19 · 9 comments · github
Ssmcv 2024-06-27 github

When a program uses DXVK-Native, currently it must set the environment variable DXVK_WSI_DRIVER=SDL2 or similar, as documented in the README. This can be done by a wrapper script, or by calling setenv() or putenv() at the beginning of main(), for example something like this in a C++ executable that uses SDL2:

int main(int argc, char** argv)
{
#ifndef _WIN32
  if (::setenv("DXVK_WSI_DRIVER", "SDL2", 1) < 0) {                               
    std::cerr << "setenv:" << std::strerror(errno) << std::endl;                  
    return 1;                                                                     
  }                                                                               
#endif

  ...

However, setting environment variables like this inherits down through the process hierarchy: for example if a game launcher uses SDL2 and DXVK-Native, then every game launched by it would inherit DXVK_WSI_DRIVER=SDL2, even if the game has nothing to do with SDL2 and is actually using GLFW. This seems error-prone: the fact that the parent process (launcher) uses SDL2 for its HWND implementation is a fact about the parent process only, and generally not a true fact about its child processes.

One solution that might be suggested is to unset the environment variable after initializing DXVK, but that usually isn't safe, because environ is shared state with no specific locking, therefore it is unsafe to edit the environment in one thread while another thread might be calling getenv(). Many libraries call getenv() in functions that might be called from a non-main thread, for example anything that uses gettext() for localization, with the result that the only place that the environment can safely be altered is right at the beginning of main(), before the first non-main thread has been created.

Would it be possible to add a DXVK-specific way to set the WSI in use, as non-inheritable process-global state, analogous to SDL_SetHint()? Perhaps something like this?

int main (int argc, char **argv)
{
  HWND window;

#ifndef _WIN32
  dxvkInitWSI("SDL2");
#endif

  ... carry on with initialization ...

#ifdef _WIN32
  window = ... create a Win32 window ...
#else
  window = ... create a SDL2 window ...
#endif

  ... continue with application code ...
}

Or alternatively, a way to tell DXVK "my HWND is actually a SDL2 SDL_Window" via parameters to CreateDeviceEx or CreateSwapChainForHwnd() or equivalent, or by calling a DXVK-specific method on the Device, or something of that sort (whatever layer is the most sensible one), so that you could do something like this?

  Com<ID3D11Device> device;

  HRESULT status = D3D11CreateDevice(...);

#ifndef _WIN32
  device->dxvkSetWSI("SDL2");
#endif

  ... continue with application code ...

cc @flibitijibibo

Fflibitijibibo 2024-06-27 github

At one point I thought about making it accessible via the DXGIFactory since we need to know which WSI to use the moment anything DXGI gets touched, but it's worth noting that a dxvk-native application must set the variable on startup, so if the application and its launcher both use it they're required to specify the WSI as if they were the base process (we treat "no value found" as a hard error to avoid apps depending on any kind of default value), so that would avoid the conflict in this particular example.

A real export wouldn't be so bad though, an example could be tried with this line in FNA3D: https://github.com/FNA-XNA/FNA3D/blob/master/src/FNA3D_Driver_D3D11.c#L5198

Ddoitsujin maintainer 2024-07-03 github

Introducing an entry point for this sounds good, definitely cleaner than the env var we currently have.

Fflibitijibibo 2024-07-03 github

I'd be okay with something like extern "C" int dxvkSetWSI(const char*); in libdxgi; as long as it's easy to dlsym it shouldn't be too hard to add to existing D3D backends!

Ssmcv 2024-07-08 github

I'd be okay with something like extern "C" int dxvkSetWSI(const char*); in libdxgi; as long as it's easy to dlsym it shouldn't be too hard to add to existing D3D backends!

That sounds great to me, but I don't know the D3D/DXGI architecture well enough to know where best to put the global variable that would be set by this function, such that there would only be one copy of it per process and it would be accessible to everything that needs to know which WSI is the active one. (The obvious candidates seem to be static libraries that I think get linked more than once, making them unsuitable for that purpose.)

Fflibitijibibo 2024-07-08 github

That's a good question; I believe WSI instances occur in both DXGI and D3D9/8 devices; the part that's tough is that the very first API call for each library will want WSI immediately so it's tough to add it to existing interfaces... @Joshua-Ashton, any ideas for D3D9 in particular?

Ssmcv 2024-07-08 github

Is there a "bottom-level" library that all of the others call into (is that DXGI?), like the way everything in the GTK ecosystem depends on GLib? If there is, then it could have an extern "C" int dxvkSetWSI(const char*) and a matching extern "C" const char *dxvkGetWSI(void).

But if there's no obvious bottom-level library then it might have to be more like dxvkDXGISetWSI, dxvkD3D9SetWSI, dxvkD3D8SetWSI.

Fflibitijibibo 2024-07-08 github

The latter route is probably the way to go - the bottomest(?) library is libdxvk for DxvkInstance but that's statically linked, so entries for each lib is probably all we can do.

BBlisto91 2024-10-26 github

@smcv @flibitijibibo @misyltoad any of you up for taking a shot at this? Would be nice to have it in the 2.5 release

Ddoitsujin maintainer 2024-10-26 github

Not sure if i want to stall 2.5 based on something that no one seems to be actively working on, but if anyone is actually going to take that up I wouldn't mind getting this sorted out.

I don't really want to mess with this myself too much though since I'm not exactly the one shipping actual dxvk-native apps.

Launch options