Wo Long: Fallen Dynasty

The player needs D3D11 video interfaces D3DMetal lacks, and the decoded frame carried across to its D3D12 renderer.

GPL-3.0-or-laterThis fix in MacGameFix

What you see

The cutscene runs with sound and no picture

Why it happens

The video is decoded on a D3D11 device and presented by a D3D12 renderer; the game negotiates a player, is told yes and tears it down, never reading a sample, because the frame has no way across.

What the fix changes

The DYNASTY WARRIORS bridge, on libxess.dll

The code

The D3D12 renderer opens the handle of a frame decoded on the D3D11 device; the bridge answers with a texture on the game's own device that carries the frame across.

fixes/dwo-video-bridge.c · d3d12_open_shared_handle, lines 2163–2215
c
static HRESULT WINAPI d3d12_open_shared_handle(void *self, HANDLE handle,
                                               REFIID iid, void **out)
{
    {
        static LONG told;
        if (InterlockedIncrement(&told) <= 12)
            logf_("D3D12 OpenSharedHandle %p %s%s", handle, guid_text_(iid),
                  handle == BRIDGE_HANDLE ? "   << ours" : "");
    }
    if (handle == BRIDGE_HANDLE)
    {
        HRESULT hr;
        if (!out) return E_INVALIDARG;
        *out = NULL;
        if (!IsEqualGUID(iid, &IID_ID3D12Resource)) return E_NOINTERFACE;

        EnterCriticalSection(&frame_lock);
        /* A new clip of another size: the game has made a new texture and is
         * opening it now, so this is the one moment the bridge can change size.
         * Handing back the old one leaves the game drawing from a texture no
         * frame of this clip can be written into. */
        if (bridge_texture && frame_width && frame_height &&
            (texture_width != frame_width || texture_height != frame_height))
        {
            logf_("bridge: the game opens a %ux%u clip on a %ux%u bridge; rebuilding",
                  frame_width, frame_height, texture_width, texture_height);
            bridge_destroy();
        }
        if (!bridge_texture && frame_width && frame_height)
        {
            /*
             * The size the game asked to share, when it said one.
             *
             * The handle it passes belongs to a texture it created itself, and
             * what comes back has to describe that texture -- not the clip.
             * Kingdom Hearts shares a 2048x1080 surface to carry a 1920x1080
             * frame, so a texture built to the clip's size is the wrong
             * resource under the same handle, and what the game copies out of
             * it is not a picture.
             */
            bridge_create((ID3D12Device *)self, frame_width, frame_height);
        }
        hr = bridge_texture ? S_OK : E_FAIL;
        if (bridge_texture)
        {
            ID3D12Resource_AddRef(bridge_texture);
            *out = bridge_texture;
        }
        LeaveCriticalSection(&frame_lock);
        return hr;
    }
    return real_open_shared_handle(self, handle, iid, out);
}

On the D3D11 side IDXGIResource::GetSharedHandle returns E_NOTIMPL; the bridge hands out a handle of its own that the D3D12 side recognises.

fixes/dwo-video-bridge.c · res_get_shared_handle, lines 1863–1884
c
static HRESULT WINAPI res_get_shared_handle(void *self, HANDLE *handle)
{
    HRESULT hr = real_res_get_shared_handle(self, handle);
    {
        static LONG told;
        if (InterlockedIncrement(&told) <= 12)
            logf_("GetSharedHandle -> 0x%08lx%s", (unsigned long)hr,
                  (FAILED(hr) && d3d12_bridge_armed) ? "   (answering BRIDGE_HANDLE)" : "");
    }
    /*
     * Only invent a handle if the side that can recognise it is armed.
     *
     * BRIDGE_HANDLE is meaningful to d3d12_open_shared_handle and to nothing
     * else. Handing it out while that hook is missing turns a clean failure --
     * the game learns it cannot share, and says so -- into a real D3D12 device
     * dereferencing 0xD3D12B21D. Nioh 3 crashed exactly that way, because its
     * D3D12 arrives through Streamline and the hook had not been placed there
     * yet. Reporting the original failure is the safe answer.
     */
    if (FAILED(hr) && handle && d3d12_bridge_armed) { *handle = BRIDGE_HANDLE; return S_OK; }
    return hr;
}

Details

Needs
D3DMetal, D3D12
Installer
install-dwo-bridge.sh
Executable
WoLong.exe
Built from
dwo-video-bridge.c
Verified on
Stable 26.3, on a copy of it carrying this project's winegstreamer (2026-08-31)

The full story, with the measurements and the wrong turns