#489

dcomp: keep the composition swap chain hook through the IAT sync (Slay the Spire 2 black screen)

Openinsertfloppywants to merge intofrom

Requested by Cameron · project thread

Problem

Slay the Spire 2 played music and showed its cursor on a black screen. Godot's swap_chain_resize failed every frame with DXMT's own "Not implemented" from CreateSwapChainForComposition, even though dcomp.dll had logged that it pointed that vtable slot at composition_swap_chain.

The factory's vtable lives in dxgi.dll's JIT-pool copy. VirtualProtect on that address changes the PE view's page and copies the PE page over the pool copy (virtual_ios.c, "iOS JIT IAT sync: full copy"), once on the way to read/write and again on the way back. The slot was written only in the pool copy, so the second copy put DXMT's entry back. The log shows both syncs of that page around the patch.

Change

build/dcomp/dcomp.c: pe_view() finds the PE view's address of the slot (the pool copy starts with the same DOS and file headers at the same offset). The hook is written there and in the pool copy, and the slot is read back after the protection is restored; the log line says whether the patch stayed. app/Madeira/arm64ec-windows/dcomp.dll is rebuilt from it with llvm-mingw 20260421. The unchanged source rebuilds to the same code as the committed DLL, apart from debug paths.

Testing

  • On device (iPhone, sideloaded build): Slay the Spire 2 shows its picture and plays. The game also needs launch settings for the JIT pool and .NET (env.DOTNET_EnableWriteXorExecute = 0 and others); those are per-game settings, not part of this PR.
  • This branch: main c2c1b03a has the same dcomp.c and dcomp.dll this commit started from, so it applies unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LiLQakAySboBR7FkqkJFKS