What exactly would this look like from an app PoV?
If we have an interface that the app needs to query from the DXGI factory before interacting with WSI in any way then something like D3D11CreateDeviceAndSwapChain with a NULL adapter basically cannot work with anything other than the default path, so I'm not entrely sure if I understand the proposal correctly.
One of the main concessions I've currently made so far is that there is no way that applications will be able to use libdxvk without some kind of change, even if we were to retain the current SDL2 behavior - so one thing that has simplified things a bit is adding a requirement that CreateDevice must be passed an adapter, and therefore the application must create a DXGIFactory manually. This helps to define a more concrete framework that the WSI can be implemented within, while also adding code that actually ends up being useful to the application anyhow; in FNA3D for example we do this even on Windows because it's the only way to specify the GPU preference "officially", so axing the NULL adapter, while not 100% convenient, does help shape this implementation a whole lot better. (The NULL path would likely default to the old SDL2 behavior, for compatibility's sake.)
I've made a branch of FNA3D that demonstrates what a port might look like. This demonstrates the changes needed for both the NULL and non-NULL adapter paths, and does so complete with dynamic loading of the D3D/DXGI libraries:
https://github.com/FNA-XNA/FNA3D/commit/cc09a34d8708789bbb56c6924f759517849de198
Second revision of the port example: https://github.com/FNA-XNA/FNA3D/commit/242c0985e77112348a518dce2c10c71c015d8bb3
The main change is that I remembered that QueryInterface is a thing, so instead of having to construct a special DXGIFactory explicitly we only have to create any DXGIFactory of our choosing. Then, the single-file headers can call QueryInterface with a DXVK-specific IID that exposes the SetWSI() call.
So, with the new design in mind:
dxvkInitWSI(factory).This has the added benefit of working nicely with IDirect3D9, which DXVK can implement with the exact same interface; instead of a factory the application would pass the IDirect3D9 pointer and the code is otherwise identical.
Apologies for repeat posting, but did a third revision since it wasn't much work to fake an IDxvkWsi interface to show the idea off a little better:
https://github.com/FNA-XNA/FNA3D/commit/f86b7023832d7e0bac2584b9c36e2b6caad1cf0a
Slapped together a draft of the documentation:
DXVK provides its own DirectX headers that allow for building existing client
code with as few changes as possible - this includes:
d3d9.h, d3d11.h, dxgi.h, etc.windows.h implementation (basic typedefs, macros, etc.)dxvk_wsi.h, used for custom Window System IntegrationAs-is, your client should build without changing any Direct3D code. You may
have to replace Win32 API calls for non-Win32 platforms (for example, DXVK does
not implement calls like MonitorFromWindow), so you are encouraged to design
your rendering abstraction layer to consider D3D as a multiplatform renderer,
rather than just a Windows renderer.
Because non-Windows platforms will not be using the expected HWND window
handle, DXVK is designed to support custom window system APIs using the HWND
as a multiplatform opaque pointer. For example, applications can instead pass
an SDL_Window* pointer where appropriate, and DXVK will be able to process the
information correctly.
By default, DXVK assumes that native applications will be using SDL 2.0 as the
windowing API. However, it is possible to use any API of your choice by
providing a custom WSI function table to either the IDXGIFactory or the
IDirect3D9 handle. You can find the API in dxvk_wsi.h.
As for example implementations, the community has written a series of
single-file headers that allow quick integration of a number of popular APIs,
including SDL, GLFW, and a headless "None" WSI. These headers can be found
here.
Consider the following D3D call:
/* Test to see if we meet the system requirements */
HRESULT res = D3D11CreateDevice(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
ARRAYSIZE(levels),
D3D11_SDK_VERSION,
NULL,
NULL,
NULL
);
CHECK_HRESULT(res) /* Pretend this actually means something */
By default, this creates a DXGI Factory in the background - as a result, this
assumes that SDL2 is used for window management. To explicitly set a WSI, an
IDXGIAdapter must be passed to this function, and therefore you must
explicitly create your own IDXGIFactory. If you already do this, or you're using
D3D9 (and thus create an IDirect3D9 instead), you can skip the following
example.
The above example will now look something like this:
HRESULT res;
IDXGIFactory1 *factory;
IDXGIAdapter1 *adapter;
/* Create the adapter manually so we can set WSI for non-Win32 */
res = CreateDXGIFactory1(&D3D_IID_IDXGIFactory2, &factory);
CHECK_HRESULT(res)
/* Feel free to use IDXGIFactory6_EnumAdapterByGpuPreference instead... */
res = IDXGIFactory2_EnumAdapters1((IDXGIFactory2*) factory, 0, &adapter);
CHECK_HRESULT(res)
/* Test to see if we meet the system requirements */
res = D3D11CreateDevice(
(IDXGIAdapter*) adapter, /* This is the only change for this call! */
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
ARRAYSIZE(levels),
D3D11_SDK_VERSION,
NULL,
NULL,
NULL
);
CHECK_HRESULT(res)
/* Don't forget to clean up! */
IDXGIAdapter1_Release(adapter);
IDXGIFactory1_Release(factory);
This sets up the application to change the WSI, but we have not done it just
yet! For this example we will be explicitly setting SDL2 as our WSI. The
single-file headers are designed to work like this:
#define DXVK_SDL2_IMPL /* Define this in exactly one build unit */
#include "dxvk_sdl2.h"
/* Later... */
void* yourFactoryOrIDirect3D9;
HRESULT res = dxvkInitWSI(yourFactoryOrIDirect3D9);
CHECK_HRESULT(res)
As a result, the final example will look like this:
HRESULT res;
IDXGIFactory1 *factory;
IDXGIAdapter1 *adapter;
/* Create the adapter manually so we can set WSI for non-Win32 */
res = CreateDXGIFactory1(&D3D_IID_IDXGIFactory2, &factory);
CHECK_HRESULT(res)
/* For non-Win32, set the custom WSI */
#ifndef _WIN32
res = dxvkInitWSI(factory);
CHECK_HRESULT(res)
#endif /* _WIN32 */
/* Feel free to use IDXGIFactory6_EnumAdapterByGpuPreference instead... */
res = IDXGIFactory2_EnumAdapters1((IDXGIFactory2*) factory, 0, &adapter);
CHECK_HRESULT(res)
/* Test to see if we meet the system requirements */
res = D3D11CreateDevice(
(IDXGIAdapter*) adapter,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
ARRAYSIZE(levels),
D3D11_SDK_VERSION,
NULL,
NULL,
NULL
);
CHECK_HRESULT(res)
/* Don't forget to clean up! */
IDXGIAdapter1_Release(adapter);
IDXGIFactory1_Release(factory);
Note that the WSI does not persist statically, you will need to set the WSI for
each factory you create!
From then on, you can treat the HWND handle as the type of your choice - for
example, an SDL application using D3D11 might have something like this:
HWND GetWindowForDXGI(SDL_Window *window)
{
#ifndef _WIN32
/* DXVK accepts the SDL_Window* directly */
return (HWND) window;
#else
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(window, &info);
return info.info.win.window;
#endif
}
...
DXGI_SWAP_CHAIN_DESC swapchainDesc;
swapchainDesc.OutputWindow = GetWindowForDXGI(sdlWindow);
I was thinking about the "wsi interface provided by the app" idea, but I have some gripes with that approach.
The major one being that if there are any subtle bugs in that wsi interface implementation and a game ships that, we can't fix it from the DXVK side.
There are enough broken linux native ports (especially around windowing...) so I think we definitely need for the implementations to be resident in DXVK (but we can still use an interface internally).
I imagine we would do something like this and converge the stuff into an interface per WSI:
class IDXVKWsi {
public:
/*********************
* Monitor Interface
*********************/
/**
* \brief Default monitor
*
* \returns The default monitor
*/
virtual HMONITOR GetDefaultMonitor() = 0;
/**
* \brief Enumerators monitors on the system
*
* \returns The monitor of given index
*/
virtual HMONITOR EnumMonitors(uint32_t index) = 0;
/**
* \brief Enumerators monitors on the system
* \param [in] adapterLUID array of adapters' LUIDs
* \param [in] numLUIDs adapterLUID array size (0 for all monitors)
* \param [in] index Monitor index within enumeration
*
* \returns The monitor of given index
*/
virtual HMONITOR EnumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) = 0;
/**
* \brief Get the GDI name of a HMONITOR
*
* Get the GDI Device Name of a HMONITOR to
* return to the end user.
*
* This typically looks like \.\\DISPLAY1
* and has a maximum length of 32 chars.
*
* \param [in] hMonitor The monitor
* \param [out] Name The GDI display name
* \returns \c true on success, \c false if an error occured
*/
virtual bool GetDisplayName(
HMONITOR hMonitor,
WCHAR (&Name)[32]) = 0;
/**
* \brief Get the encompassing coords of a monitor
*/
virtual bool GetDesktopCoordinates(
HMONITOR hMonitor,
RECT* pRect) = 0;
/**
* \brief Get the nth display mode
*
* \param [in] hMonitor The monitor
* \param [in] modeNumber The nth mode
* \param [out] pMode The resultant mode
* \returns \c true on success, \c false if the mode could not be found
*/
virtual bool GetDisplayMode(
HMONITOR hMonitor,
uint32_t modeNumber,
WsiMode* pMode) = 0;
/**
* \brief Get the current display mode
*
* This is the display mode right now.
*
* \param [in] hMonitor The monitor
* \param [out] pMode The resultant mode
* \returns \c true on success, \c false on failure
*/
virtual bool GetCurrentDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) = 0;
/**
* \brief Get the current display mode
*
* This is the display mode of the user's
* default desktop.
*
* \param [in] hMonitor The monitor
* \param [out] pMode The resultant mode
* \returns \c true on success, \c false on failure
*/
virtual bool GetDesktopDisplayMode(
HMONITOR hMonitor,
WsiMode* pMode) = 0;
/**
* \brief Get the EDID of a monitor
*
* Helper function to grab the EDID of a monitor.
* This is needed to get the HDR static metadata + colorimetry
* info of a display for exposing HDR.
*
* \param [in] hMonitor The monitor
* \returns \c EDID if successful, an empty vector if failure.
*/
virtual WsiEdidData GetMonitorEdid(HMONITOR hMonitor) = 0;
/**
* \brief Get the size of a monitor
*
* Helper function to grab the size of a monitor
* using getDesktopCoordinates to mirror the window code.
*/
inline void GetMonitorClientSize(
HMONITOR hMonitor,
UINT* pWidth,
UINT* pHeight) {
RECT rect = { };
getDesktopCoordinates(hMonitor, &rect);
if (pWidth)
*pWidth = rect.right - rect.left;
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
/*********************
* Window Interface
*********************/
/**
* \brief The size of the window
*
* \param [in] hWindow The window
* \param [out] pWidth The width (optional)
* \param [out] pHeight The height (optional)
*/
virtual void GetWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pWeight) = 0;
/**
* \brief Resize a window
*
* \param [in] hWindow The window
* \param [in] pState The swapchain's window state
* \param [in] width The new width
* \param [in] height The new height
*/
virtual void ResizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t weight) = 0;
/**
* \brief Sets the display mode for a window/monitor
*
* \param [in] hMonitor The monitor
* \param [in] hWindow The window (may be unused on some platforms)
* \param [in] mode The mode
* \returns \c true on success, \c false on failure
*/
virtual bool SetWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode) = 0;
/**
* \brief Enter fullscreen mode for a window & monitor
*
* \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] modeSwitch Whether mode switching is allowed
* \returns \c true on success, \c false on failure
*/
virtual bool EnterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch) = 0;
/**
* \brief Exit fullscreen mode for a window
*
* \param [in] hWindow The window
* \param [in] pState The swapchain's window state
* \returns \c true on success, \c false on failure
*/
virtual bool LeaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates) = 0;
/**
* \brief Restores the display mode if necessary
*
* \returns \c true on success, \c false on failure
*/
virtual bool RestoreDisplayMode() = 0;
/**
* \brief The monitor a window is on
*
* \param [in] hWindow The window
* \returns The monitor the window is on
*/
virtual HMONITOR GetWindowMonitor(HWND hWindow) = 0;
/**
* \brief Is a HWND a window?
*
* \param [in] hWindow The window
* \returns Is it a window?
*/
virtual bool IsWindow(HWND hWindow) = 0;
/**
* \brief Update a fullscreen window's position/size
*
* \param [in] hMonitor The monitor
* \param [in] hWindow The window
* \param [in] forceTopmost Whether to force the window to become topmost again (D3D9 behaviour)
*/
virtual void UpdateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost) = 0;
/**
* \brief Create a surface for a window
*
* \param [in] hWindow The window
* \param [in] pfnVkGetInstanceProcAddr \c vkGetInstanceProcAddr pointer
* \param [in] instance Vulkan instance
* \param [out] pSurface The surface
*/
virtual VkResult CreateSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface) = 0;
};
That all makes sense to me, except for one thing: We could still override the vtable with patched functions if we knew of a title that used an old WSI impl and we recognized the executable name.
I'm okay with either internal or external, but wanted to throw the idea of external out there in case WSI was looking to be a maintenance burden with all these backends coming up.
I would definitely like to avoid more app profiles (especially for native linux games) going forward. :-)
@Joshua-Ashton do I understand correctly that you want the implementation of that interface to reside within DXVK but off-load the responsibility of creating an instance of that interface to the app and have something like a IDXVKDXGIFactorySomething::InitWindowSystem function to do so?
(all of which sounds perfectly fine to me, just want to confirm if this is what we're going for)
Yes, the mechanism for how an app will pick an interface is still a little TBD though.
Trying to itemize this task as best as I can... would this be an acceptable route to getting this done? It's less the details and more about the process itself:
Took the first couple steps to getting this done:
I don't have experience with glfw so it'd help a lot to find someone who knows what they're doing to make sure we're not breaking that backend (and also to implement the dlopening for it...?). Once that's done all we have to do is the refactoring to support multiple backends in one binary and that should be the MVP for establishing ABI version 0 that all applications can use. The IDxvkWsi may not necessarily need to be present right from the start, as we could probably steal from SDL's design and expose a DXVK_WSIDRIVER variable for GLFW and None to use.
First, establish a versioning scheme, so that new applications 100% always link to a new libdxvk_dxgi1.so.0, for example. Packagers will probably want this anyway, so this seems like a good time to get that over with.
To accompany a new, stable ABI, it would be good if we can resurrect something similar to #3325 and use that to provide a new, stable build-time interface.
On that PR, I wrote:
It's a bit unfortunate that one of these is
dxvk_and the other isdxvk-. I've kept the naming from the Fedora packaging for now, but I'd be very tempted to change it todxvk_d3d9.pc,dxvk_glfw_dxgi.pcand so on, to be consistent with the library names and avoid having two subtly different name-prefixes.
and perhaps this would be a good opportunity to make that change official?
First, establish a versioning scheme, so that new applications 100% always link to a new libdxvk_dxgi1.so.0, for example. Packagers will probably want this anyway, so this seems like a good time to get that over with.
To accompany a new, stable ABI, it would be good if we can resurrect something similar to #3325 and use that to provide a new, stable build-time interface.
On that PR, I wrote:
It's a bit unfortunate that one of these is
dxvk_and the other isdxvk-. I've kept the naming from the Fedora packaging for now, but I'd be very tempted to change it todxvk_d3d9.pc,dxvk_glfw_dxgi.pcand so on, to be consistent with the library names and avoid having two subtly different name-prefixes.and perhaps this would be a good opportunity to make that change official?
+1 to all of this!
I've finished a first draft of the dynamic WSI system. It's very SDL/FNA3D-like and probably could be done with C++ interfaces or something along those lines, but I can confirm that this works for my applications at least:
https://github.com/doitsujin/dxvk/compare/master...flibitijibibo:dxvk:wsi-dynamic
It required ripping things up pretty badly for each step, so I may defer to Joshua for how the final patchset should look...
EDIT: CI should succeed with this branch now! Current plan for new series:
Cleaned up the wsi-dynamic branch, #3738 is the result. It should pass CI and it works on the games I've tested it with!
Took a guess at trying to establish the soname and wasn't able to get the Windows build to cooperate - the Linux/macOS conventions are fine but I don't see any way to make Meson produce the right DLL name without forcing the version numbers in. CMake does this by default so I don't really know if there's a good way to do this elsewhere... here's the patch if someone knows how to do this correctly:
diff --git a/meson.build b/meson.build
index b571729d..6f52a690 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,10 @@
project('dxvk', ['c', 'cpp'], version : 'v2.3', meson_version : '>= 0.49', default_options : [ 'cpp_std=c++17', 'warning_level=2' ])
+dxvk_abi_version = '0'
+dxvk_major_version = '2'
+dxvk_minor_version = '3'
+dxvk_so_version = dxvk_abi_version + '.' + dxvk_major_version + '.' + dxvk_minor_version
+
cpu_family = target_machine.cpu_family()
platform = target_machine.system()
diff --git a/src/d3d10/meson.build b/src/d3d10/meson.build
index 51db067c..b68d41b1 100644
--- a/src/d3d10/meson.build
+++ b/src/d3d10/meson.build
@@ -23,6 +23,7 @@ d3d10_core_dll = shared_library('d3d10core'+dll_ext, d3d10_core_src, d3d10_core_
vs_module_defs : 'd3d10core'+def_spec_ext,
link_args : d3d10_core_ld_args,
link_depends : [ d3d10_core_link_depends ],
+ version : dxvk_so_version,
)
d3d10_core_dep = declare_dependency(
diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build
index 9b51e6ea..1f86b00e 100644
--- a/src/d3d11/meson.build
+++ b/src/d3d11/meson.build
@@ -86,6 +86,7 @@ d3d11_dll = shared_library('d3d11'+dll_ext, dxgi_common_src + d3d11_src + d3d10_
vs_module_defs : 'd3d11'+def_spec_ext,
link_args : d3d11_ld_args,
link_depends : [ d3d11_link_depends ],
+ version : dxvk_so_version,
)
d3d11_dep = declare_dependency(
diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build
index dd6b2316..06c1a5d1 100644
--- a/src/d3d9/meson.build
+++ b/src/d3d9/meson.build
@@ -65,6 +65,7 @@ d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_
vs_module_defs : 'd3d9'+def_spec_ext,
link_args : d3d9_ld_args,
link_depends : [ d3d9_link_depends ],
+ version : dxvk_so_version,
)
d3d9_dep = declare_dependency(
diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build
index a6e83b54..e293bf51 100644
--- a/src/dxgi/meson.build
+++ b/src/dxgi/meson.build
@@ -29,6 +29,7 @@ dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res,
vs_module_defs : 'dxgi'+def_spec_ext,
link_args : dxgi_ld_args,
link_depends : [ dxgi_link_depends ],
+ version : dxvk_so_version,
)
dxgi_dep = declare_dependency(
I don't see any way to make Meson produce the right DLL name
What do you consider to be the right DLL name?
What you pasted above should produce a regular file libd3d10core.so.0.2.3 plus runtime symlink libd3d10core.so.0 and development symlink libd3d10core.so on Linux, and meanwhile should produce libd3d10core-0.dll on Windows, if I'm understanding correctly. (And similar for all the other libraries.) Is that what you want?
For Linux and macOS this is correct, I don't think Windows can have those version numbers in the name - it would probably be okay if they didn't link between each other since we could always just rename them, but since they do depend on each other it means they no longer match up to Windows' official filenames for the DLLs.
If what you are aiming for is the situation I described above on Linux, but with an unversioned name like libd3d10core.dll (not what I described above) on Windows: then, yes, I think the only way to achieve that is to specify a version when building for Linux but leave the version unspecified when building for Windows, similar to #3743.
Spent some time getting Fedora's dxvk-native package updated to 2.3, to see if anything new had come up since 1.9 that packagers would care about: https://src.fedoraproject.org/rpms/dxvk-native/tree/rawhide
Thankfully the move to submodules actually made it a bit easier, though the directx header copies will probably raise an eyebrow or two. It's not wildly different compared to before though, so ¯_(ツ)_/¯
Aside from that, #3325 getting finished should make packaging this as straightforward as possible. Preemptively rebasing on #3738 and #3743 could help verify this.
@flibitijibibo: feel free to take over #3325 if it would be useful to you, I'm unlikely to have much time to work on it any time soon.
All known issues have been resolved as of the latest head revision! Now's a great time for packagers to try everything out and give feedback in case there's anything we missed.
DXVK_WSIDRIVERx1 2023-12SDL_Sx2 2023-06SDL_Wx2 2023-06DXVK_SDL2_IMPLx1 2023-06SDL_Gx1 2023-06SDL_VERSIONx1 2023-06DXVK_HAS_WSI_SDL2x1 2023-05DXVK_HAS_WSI_SDL3x1 2023-05DXVK_NATIVEx1 2023-05DXVK_WSI_GLFWx1 2023-05DXVK_WSI_NONEx1 2023-05DXVK_WSI_SDL2x1 2023-05DXVK_WSI_SDL3x1 2023-05DXVK_WSI_VERSIONx1 2023-05SDL_Ix1 2023-05libd3d10core.dllx1 2023-12libd3d10core-0.dllx1 2023-12
This is mostly a follow-up for #3321, #3322, and #3323. It's also an attempt to document the occasional one-off discussions Joshua and I have had re: making WSI work long-term. That said, this should not be considered a definitive document for how this will be implemented until all maintainers have formally approved it!
Remaining Tasks
Where we're at
Currently, dxvk-native acts pretty much like Win32 DXGI would, except we just treat the HWND window handle as an SDL_Window* instead and implement the rest with a custom WSI backend in dxvk. This has worked fine up until now, but will fall apart pretty soon:
What we can't do
My first gut reaction would have been to turn the HWND into a public structure, similar to that of SDL_SysWMInfo, where applications could inform dxvk of which WSI driver to use...
... but this doesn't work because of one specific question: When does the WSI initialize? Unlike SDL, which has a clear startup location at SDL_Init, D3D has a handful of calls that could be the first. There's each D3D version's CreateDevice, combined with the possibility of passing a window handle or not (usually just for feature level testing, but still), as well as CreateDXGIFactory1. From what I understand, a DXGI factory is made when one isn't available in the CreateDevice function, so I'm inclined to lean towards doing this in CreateDXGIFactory, but that of course doesn't have any parameters with which we could pass WSI information, and there are just way too many assumptions that have to be made for it to determine which WSI it should use.
The best option we have for a custom CreateDXGIFactory without adding new functions would be to expose custom interfaces, so one could do this...
... but then there's the question of just how many backends dxvk would actually need to support. This adds a lot of maintenance burden to the project that doesn't really have anything to do with implementing Direct3D over Vulkan, so I fully understand why this would be undesirable.
There's also one last detail, which is that libdxvk is technically out in the wild already, via Source 1 and FNA's Linux D3D11 Alpha. While not a huge deal for myself and Joshua to just update to the new ABI, there might still be some hesitation to break the ABIs on the existing sofiles, so no matter what we do we're kind of stuck with the current SDL2 path with the given sonames.
What we can try
The above work led me to one last idea, which was to offload WSI responsibility entirely to applications, so instead of there being
D3D_IID_IDXGIFactoryDXVK_SDL2,D3D_IID_IDXGIFactoryDXVK_SDL3, etc, we'd just haveD3D_IID_IDXGIFactoryDXVK:Then, instead of having the WSI be in dxvk, we would instead just have single-file headers like "dxvk_sdl3.h" for platforms that are widely re-used, which would fill in this function table for developers automatically.
As for the current ABI, there are two things we can do to keep things as stable as possible:
First, establish a versioning scheme, so that new applications 100% always link to a new
libdxvk_dxgi1.so.0, for example. Packagers will probably want this anyway, so this seems like a good time to get that over with. Second, we can actually keep the current SDL2 path as-is, and when applications do not use the DXVK interface, it will be assumed that the application intends to use DXVK's existing SDL2 path, as shipping applications currently do. Maybe some day that functionality can be deprecated, but for now this seems like the best route for keeping as many applications happy as possible.This is all super high-level, and Joshua and I haven't dug into this a whole lot (despite the wall of text suggesting otherwise), but I'm happy to get into the gritty details to make this all work!