#19

d3d11: drop airconv's shader object until a variant needs it (less memory on low-RAM devices)

Openspitefulowlwants to merge intofrom

For devices with little RAM. Ori and the Will of the Wisps creates thousands of shaders on its title screen (18,432 in one run) while only a few hundred are ever drawn, and PipelineCache keeps every one of them for the device's lifetime. Since d4c38a2 (#6) SM50Initialize no longer keeps the parsed program, but each shader object still holds the resource maps, the argument buffer layout, the signatures and a copy of the bytecode in the host heap. This PR drops the object until a variant compile needs it: about 300 MB of host memory in this game.

willfaust/Madeira#219 is the Madeira side of the same low-RAM work (swap tier freezes); the two PRs do not depend on each other.

What changes

  • CachedSM50Shader takes the reflection and the argument info at creation, keeps a copy of the bytecode (a few KB on the PE heap) and destroys the shader object.
  • handle(), called only by the variant compile tasks, creates the object again from the bytecode under a per-shader mutex the first time, keeps it and frees the copy. A disk shader-cache hit never calls it. A shader's first compile now parses its bytecode twice.
  • The destructor frees the argument buffer also when the object is gone.
  • dxmt = d3d11.keepShaderIR=1 keeps every shader object as SM50Initialize returns it (the behaviour before this PR).
  • [shader-ir] ml1222 N unique shaders cached, shader objects dropped; bytecode kept K KB; recreated for a compile: R is logged at the first shader and every 1024th.
  • If creating the object again ever failed, handle() would return null, which SM50Compile and the pipeline compiles report as invalid bytecode since #6. The same bytecode was parsed once already, so this is not expected.

Testing

On the device (iPad, A16, iPadOS 26.6.1), Ori and the Will of the Wisps, on top of #6:

  • the host's default malloc zone stays at 38-81 MB for the whole run, against 340-372 MB with #6 alone;
  • 18,432 shaders cached, 131 MB of bytecode kept, 55 objects created again for a compile;
  • the game starts and plays with d3d11.mipClampBC=2 and a 640 MB JIT pool; without this change that configuration ran out of memory at launch (the app may use ~4.0 GB);
  • used in every run of the game since; no rendering problems noticed in play.

Not tested in this form with other games.

After merging

Please rebuild arm64ec-windows/d3d11.dll (DXMT PE meson build) and the i386 DXMT DLLs (build/wine-i386/build.sh) and move Madeira's dxmt pin. No binaries are included.

🤖 Generated with Claude Code