DirectX? On my Linux??

You: DirectX 11? At this time of year? At this time of day? In this part of the country? Localized entirely within your Linux system? Me: Yes. You: May I see it? Me: No. Yes!

Yes, it's true! Set up all nice and fast, with no WINE. Just a native executable, with full diagnostics for any IDE, compatibility with debuggers, nice and (usually) helpful output, and maintaining full Windows build support at the same time.

One of my favorite courses this year at DAE has to be Graphics Programming 1. In it, we go through the logic and techniques to create a software raytracer (laggy web build), then a rasterizer. I've really enjoyed the course so far, and it's been painless to follow on Linux... with one small looming issue: the last few weeks of the semester involve learning the DirectX 11 API, and porting the rasterizer to it such that you can seamlessly switch between hardware and software rendering. Really cool assignment, but this won't work on Linux!

An image of a flying vehicle with one seat, with some basic shading. Software rasterizer

DirectX is a proprietary closed standard made by Microsoft, and is thus likely never to come to Linux and other platforms due to its poor self-contradicting documentation.

Wait, but how come I can play DirectX Windows games on Linux?

Aha! I'm glad you asked, even if by proxy of me prewriting that question as the section title. As part of Valve's Proton compatibility layer for playing Windows games seamlessly on Linux, some really smart folks (mainly Philip Rebohle) maintain a wonderful project called DXVK. DXVK works together with WINE to translate DirectX 9-11 API calls into Vulkan calls, enabling compatibility with any system that supports a recent enough Vulkan spec. When a game kindly asks DirectX to draw a triangle, DXVK will handle this function and make the equivalent Vulkan API calls for the GPU's Vulkan driver to handle. I'm just now learning about these APIs (I have exclusively used OpenGL in my previous projects), so I can't explain how this works much further. It's magic to me, and I don't cease to be impressed when I can just launch a Windows-only DirectX-based game and it “just works” on Linux, often even with better performance than if it were running on the native Windows DirectX drivers.

Anyways, this is all great, but that covers running Windows games through WINE and having DXVK translate DirectX calls to Vulkan. Having to build .exe files, and especially the tooling for handling them on Linux (WineDbg, profiling...) sucks. It works, and I wrote a blog post about doing exactly this, but I'd still much rather avoid having to do this. So what's the trick?

The trick

I asked in a few chat rooms whether there was another way, and by pure chance someone at the ASUS Linux community happened to mention I'd have to use “dxvk native.” Yes, DXVK was designed to work together with WINE. Nowhere in its wiki does it mention any other use cases (that I could find). However, checking its recent releases we can see a file named dxvk-native-X.X-steamrt-sniper.tar.gz. Native? As in, doesn't depend on WINE? As in I can link against it and produce a normal Linux ELF binary that can be debugged and poked at just like I'd do an OpenGL program?

I immediately got to work, and found shockingly little documentation online about using this. The best resource I found is the build script of the Deus Ex: Human Revolution decompilation, which enables Linux support through DXVK. I whipped up a small CMake script, and ended up with this snippet:

if(UNIX)
    include(ExternalProject)

    ExternalProject_Add(dxvk-native
        GIT_REPOSITORY https://github.com/doitsujin/dxvk.git
        GIT_TAG v2.3
        CONFIGURE_COMMAND meson setup <SOURCE_DIR>
        BUILD_COMMAND ninja src/d3d11/libdxvk_d3d11.so src/dxgi/libdxvk_dxgi.so
        INSTALL_COMMAND "")

    ExternalProject_Get_property(dxvk-native SOURCE_DIR BINARY_DIR)
    set(DXVK_SOURCE_DIR ${SOURCE_DIR})
    set(DXVK_BINARY_DIR ${BINARY_DIR}) 
    unset(SOURCE_DIR)
    unset(BINARY_DIR)
    add_dependencies(${PROJECT_NAME} dxvk-native)

    include_directories(SYSTEM
        ${DXVK_SOURCE_DIR}/include/native/directx
        ${DXVK_SOURCE_DIR}/include/native/windows)
    target_link_directories(${PROJECT_NAME} PRIVATE
        ${DXVK_BINARY_DIR}/src/d3d11
        ${DXVK_BINARY_DIR}/src/dxgi
    )

    target_link_libraries(
        ${PROJECT_NAME} PUBLIC dxvk_d3d11 dxvk_dxgi
    )
endif()

Drop this into your CMake project, and you should be able to use and interface with DirectX just like you would on Windows! Just make sure that, instead of a HWND, you give DirectX (well, DXVK) a pointer to your SDL_Window. I added this bit of code to handle that:

#ifdef WIN32
        // Get the handle (HWND) from the SDL window
        SDL_SysWMinfo sysWMInfo{};
        SDL_GetVersion(&sysWMInfo.version);
        SDL_GetWindowWMInfo(m_pWindow, &sysWMInfo);
        swapChainDesc.OutputWindow = sysWMInfo.info.win.window;
#else
        swapChainDesc.OutputWindow = m_pWindow;
#endif

After a pretty quick build, here it is! A pure DirectX window running on Linux, in all its glory!

A screenshot showing a lengthy info dump from DXVK, with a few warnings and errors amidst the logs. An empty blue window is open next to it, with a glowing shadow effect behind it. Framerates around 5000 are reported at the end of the logs.

Well, it's not drawing anything interesting, but that's because I still need to port my rasterizer! Seeing the default blue background is already amazing given how far from an officially supported DirectX platform I'm running this on.

Full source code for the project is available on my Gitea instance here.


Thanks for reading! Feel free to contact me if you have any suggestions or comments. Find me on Mastodon and Matrix.

You can follow the blog through: – ActivityPub by inputting @mat@blog.allpurposem.at – RSS/Atom: Copy this link into your reader: https://blog.allpurposem.at

My website: https://allpurposem.at