KINGDOM HEARTS Dream Drop Distance
Cutscenes run with sound and a solid green picture: the luma and chroma planes never reach the game's own textures. The same DLL also swallows the shutdown fault that makes Wine open a crash dialog when a launcher or a title closes, so one install fixes both.
What you see
Cutscene runs with sound, picture solid green
Why it happens
The game opens the decoded NV12 texture in D3D12 through a shared handle and copies its two planes into textures of its own; asking D3DMetal for NV12 is fatal, and a frame in any other format leaves those planes empty, which is the solid green.
What the fix changes
Software decode, and the luma and chroma planes written straight into the game's own plane textures
The code
The game asks for decode into D3D video textures; the reader gets a copy of its attributes without the two that ask for it, so it decodes in software.
fixes/dwo-video-bridge.c · strip_d3d_attributes, lines 1508–1524static IMFAttributes *strip_d3d_attributes(IMFAttributes *attrs)
{
IMFAttributes *plain = NULL;
UINT32 count = 0;
if (!attrs || !real_MFCreateAttributes) return NULL;
IMFAttributes_GetCount(attrs, &count);
if (SUCCEEDED(real_MFCreateAttributes(&plain, count + 2)) && plain
&& SUCCEEDED(IMFAttributes_CopyAllItems(attrs, plain)))
{
IMFAttributes_DeleteItem(plain, &MF_SOURCE_READER_D3D_MANAGER);
IMFAttributes_DeleteItem(plain, &MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS);
return plain;
}
if (plain) IMFAttributes_Release(plain);
return NULL;
}The decoded NV12 frame is written straight into the luma and chroma textures the game created, so its own shader has planes to convert.
fixes/dwo-video-bridge.c · plane_write, lines 2112–2161static void plane_write(const BYTE *nv12, UINT stride, UINT width, UINT height)
{
D3D12_TEXTURE_COPY_LOCATION dst = { 0 }, src = { 0 };
const BYTE *uv = nv12 + (size_t)stride * height;
BYTE *mapped = NULL;
UINT y;
if (!plane_ready || !bridge_list || !bridge_queue) return;
/*
* Copy the planes' geometry, not the frame's.
*
* The footprints were computed from the descriptors captured when the pair
* was created. If a later clip is larger and the game has not yet made a
* new pair, writing the frame's rows into those footprints runs off the end
* of the upload buffer. Refusing is the right answer: the next pair the
* game creates re-arms this.
*/
if (width != plane_y_desc.Width || height != plane_y_desc.Height) return;
if (FAILED(ID3D12Resource_Map(plane_upload, 0, NULL, (void **)&mapped)) || !mapped)
return;
for (y = 0; y < height; ++y)
memcpy(mapped + plane_y_fp.Offset + (size_t)y * plane_y_fp.Footprint.RowPitch,
nv12 + (size_t)y * stride, width);
for (y = 0; y < height / 2; ++y)
memcpy(mapped + plane_uv_fp.Offset + (size_t)y * plane_uv_fp.Footprint.RowPitch,
uv + (size_t)y * stride, width);
ID3D12Resource_Unmap(plane_upload, 0, NULL);
ID3D12CommandAllocator_Reset(bridge_alloc);
ID3D12GraphicsCommandList_Reset(bridge_list, bridge_alloc, NULL);
src.pResource = plane_upload;
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst.pResource = plane_y; src.PlacedFootprint = plane_y_fp;
ID3D12GraphicsCommandList_CopyTextureRegion(bridge_list, &dst, 0, 0, 0, &src, NULL);
dst.pResource = plane_uv; src.PlacedFootprint = plane_uv_fp;
ID3D12GraphicsCommandList_CopyTextureRegion(bridge_list, &dst, 0, 0, 0, &src, NULL);
ID3D12GraphicsCommandList_Close(bridge_list);
ID3D12CommandQueue_ExecuteCommandLists(bridge_queue, 1,
(ID3D12CommandList *const *)&bridge_list);
++bridge_fence_value;
if (SUCCEEDED(ID3D12CommandQueue_Signal(bridge_queue, bridge_fence, bridge_fence_value))
&& ID3D12Fence_GetCompletedValue(bridge_fence) < bridge_fence_value
&& bridge_event)
{
ID3D12Fence_SetEventOnCompletion(bridge_fence, bridge_fence_value, bridge_event);
WaitForSingleObject(bridge_event, 1000);
}
}Details
- Needs
- D3DMetal, D3D11 and D3D12
- Installer
- install-kh-bridge.sh
- Executable
- KINGDOM HEARTS Dream Drop Distance.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)