MOOR security

Dieser Eintrag ist auf Englisch verfasst.

Replayable command lists, a Windows-shaped DLL, and reading Apple's license

Entry 00482 of 769 tests pass

Command lists can now run more than once on Metal 4, d3d12.dll is split the way Windows splits it, and passing tests went from 56 to 82 with no new failures.

What changed

  • Command lists are recorded, then replayed. A D3D12 command list now records a compact stream of commands in memory. Each execution turns that stream into a fresh Metal 4 command buffer. This fixes the one-shot command-buffer problem from entry 002.
  • d3d12.dll matches Windows' layout. On Windows, d3d12.dll is a small loader and the implementation lives in d3d12core.dll, part of the Agility SDK. I split mine the same way, because one vkd3d-proton test checks which DLL each object's function table comes from. The loader always loads the core from its own folder, so a D3D12Core.dll that a game ships for Windows is never used against my DXGI.
  • Windows' less obvious behaviors:
    • D3D12CreateDevice returns the same device while one is alive.
    • CheckFeatureSupport returns Windows' exact error codes.
    • Root signatures can be serialized.
    • Executing a command list that is still open removes the device, as on Windows.
  • Apple's Metal Shader Converter license, read and recorded.

The interesting parts

The device singleton has a race condition. While a device is alive, D3D12CreateDevice hands back the same device instead of creating a new one. That makes the singleton a weak pointer, and there is a race: one thread can look up the device just as another releases the last reference. The lookup therefore uses a compare-and-swap that refuses to raise a reference count that is already zero, so a dying device is never brought back to life.

Apple's license says what the converter's dylib may do. Apple's Metal Shader Converter translates DXIL shaders to Metal, and it's what a lot of commercial porting uses. The license bundled with its installer allows distributing the dylibs "for the sole purpose of shader conversion", on Apple hardware only. It forbids reverse engineering or modifying them. The command-line tool is for internal development use only, and the headers are Apache-2.0.

My decision: the converter may become an optional backend that users install from Apple themselves. The default build stays fully open source, and I never bundle the dylib.

How the pieces load: a small d3d12.dll loader, the implementation in d3d12core.dll, and the native Metal side.game → d3d12.dll (small loader); d3d12.dll (small loader) → d3d12core.dll (the implementation) (loads from its own folder); d3d12core.dll (the implementation) → mtl12.so (Metal 4, native side); D3D12Core.dll shipped with the game → d3d12.dll (small loader) (never used); d3d12core.dll (the implementation) → command list: recorded command stream; command list: recorded command stream → fresh Metal 4 command buffer (every ExecuteCommandLists)gamed3d12.dll(small loader)d3d12core.dll(the implementation)mtl12.so(Metal 4, native side)D3D12Core.dll shippedwith the gamecommand list:recorded command streamfresh Metal 4command bufferloads from its own foldernever usedevery ExecuteCommandLists
How the pieces load: a small d3d12.dll loader, the implementation in d3d12core.dll, and the native Metal side. (Mermaid source)
Scene replay: one command list recorded once, executed six times; the CPU moves the hexagon in between
Gallery scene replay: one command list recorded once, executed six times; the CPU moves the hexagon in between. All scenes in the gallery

Numbers

SuitePassFailSkip
Samples and API tests200
vkd3d271567
vkd3d-proton53311213
Total82467220

That is 769 tests, from a full run near the end of the session. Compared with entry 003, 26 tests newly pass and none newly fail.

Three tests are still flaky.

Tests by suite at the end of entry 004: 82 of 769 pass (data as CSV)
  • pass
  • fail
  • skip

suite · share of each row's total

  1. vkd3d-proton (+19)53 pass, 311 fail, 213 skip of 577
  2. vkd3d (+6)27 pass, 156 fail, 7 skip of 190
  3. api (+1)1 pass, 0 fail, 0 skip of 1
  4. samples1 pass, 0 fail, 0 skip of 1