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–2215c
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–1884c
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)