MOOR security

mtl12: Direct3D 12 auf Metal

Ein frühes Forschungsprojekt von Fabian Moor: eine quelloffene Direct3D-12-Schicht, mit der Windows-Programme unter Wine auf Apple-Silicon-Macs über Metal laufen sollen. Mit Devlog, Testzahlen und Galerie.

Sechs Bilder in einem Raster aus zwei Reihen zu je drei, gerendert von mtl12: ein farbiges Dreieck, drei halbtransparente überlagerte Kreise, zwei sich durchdringende Dreiecke mit Tiefentest, ein Raster aus 144 gedrehten farbigen Quadraten, eine kleine Textur mit Punkt- und linearer Filterung sowie eine farbige Mandelbrot-Menge aus einem Compute-Shader.
Sechs Testszenen, gerendert von mtl12 unter Wine auf einem Apple-Silicon-Mac: erstes Dreieck, Alpha-Blending, Tiefentest, Instancing, Texturfilterung und ein Compute-Shader.

Stand des Projekts

Konformitätstests
873 von 967 bestanden, 26 fehlgeschlagen, 68 übersprungen; vollständiger Lauf aus Eintrag 077 vom 3. Oktober 2026
Spiele
Laufen noch keine.
Entwicklung
Fabian Moor entwickelt mtl12 und prüft jeden Schritt gegen Tests.
Einordnung
Ein frühes Forschungsprojekt, kein fertiges Produkt.

Über das Projekt

Windows-Spiele und -Programme, die Direct3D 12 verwenden, laufen auf Apple-Silicon-Macs heute vor allem über Apples D3DMetal, das nicht quelloffen ist. mtl12 untersucht eine offene Alternative: eine Implementierung von d3d12.dll und den benötigten Teilen von dxgi.dll auf Metal 4, die unter Wine läuft. Das Projekt steht unter der LGPL-2.1-or-later. Bei dieser Arbeit ist auch ein Sicherheitsbefund im GPU-Stack von Apple entstanden: Stellungnahme.

Mehr zum Projekt

Am Anfang stand keine Grundsatzdiskussion, sondern je ein kleines Experiment gegen die riskanteste Annahme von drei Architekturen. Der Umweg über vkd3d-proton und eine Vulkan-auf-Metal-Schicht scheiterte bereits beim Erstellen eines Geräts. Gewählt wurde eine eigene D3D12-Schicht direkt auf Metal 4. Shader übersetzt sie mit bestehenden Open-Source-Werkzeugen: dxil-spirv von Hans-Kristian Arntzen und SPIRV-Cross, betreut von der Khronos Group.

Fabian Moor entwickelt mtl12 als eigenes Forschungsprojekt. Er legt Architektur und Regeln fest und prüft jeden Schritt gegen Tests. Das Devlog entsteht aus dieser Arbeit und ist auf Englisch verfasst.

Bei einer so grossen Schnittstelle zählt nur, was sich belegen lässt. Gemessen wird an den öffentlichen Konformitätssuiten von vkd3d und vkd3d-proton sowie an Referenzbildern, die Microsofts Software-Renderer WARP erzeugt. Jeder Devlog-Eintrag nennt seine Testzahlen, auch wenn sie ernüchternd sind. Eine Clean-Room-Regel schliesst Beiträge aus, die aus dem Disassemblieren oder Dekompilieren proprietärer Software wie D3DMetal stammen.

Für die Sicherheitsarbeit ist das Projekt aus zwei Gründen interessant. Erstens zeigt es, wie sich komplexe Entwicklungsarbeit nachvollziehbar belegen lässt: mit klaren Grenzen, überprüfbaren Belegen und Prüfung statt blindem Vertrauen. Zweitens bricht Metal bei vielen Fehlern den ganzen Prozess ab, statt einen Fehlercode zu liefern. Eingaben müssen deshalb geprüft werden, bevor sie die Grafikschnittstelle erreichen – ein Grundsatz, der auch für sichere Software allgemein gilt.

Einträge

Die Einträge sind auf Englisch verfasst. 72 Einträge, neueste zuerst. Diese Seite zeigt 077 bis 065.

  1. Entry 077

    Triangle fans from ExecuteIndirect, where only the GPU knows how many there are

    mtl12 now draws triangle fans and strips with adjacency that a D3D12 program issues with ExecuteIndirect, by sizing and converting their indices on the GPU.

  2. Entry 076

    Three formats the tables say Metal can write, and one that a buffer actually takes

    mtl12 now tells D3D12 programs they can write BGRA8 pixels from compute shaders; two other formats Apple's tables list as writable were left out, because on an M1 their buffer stores vanish.

  3. Entry 074

    Inverted viewports for free, and the depth clamp that wasn't there

    mtl12 now reports inverted viewport depth, which Metal does natively, and clamps the depth a pixel shader writes to the viewport's range, which Metal doesn't do.

  4. Entry 073

    Asking a device about itself: ID3D12DeviceConfiguration1

    mtl12 devices and device factories now answer ID3D12DeviceConfiguration1, which reports how the device was set up and can pull a root signature out of a compiled shader library by name.

  5. Entry 072

    Culling triangles in a pass that has no pixel shader

    mtl12 now honours Direct3D 12 cull distances in depth-only passes by generating an empty pixel shader for SPIRV-Cross to add the discard to, and a gallery scene found a second cull-distance bug on the way.

  6. Entry 071

    Buffer views that start in the middle of an element

    mtl12 now supports Direct3D 12 buffer views given in bytes, including 12-byte-per-element views that start 4 or 8 bytes into an element, which Metal's texture buffers can't start at.

  7. Entry 070

    Counting, flipping and packing: three small Direct3D 12 options on Metal

    mtl12 now reports three more optional Direct3D 12 features: indirect draws that know their own index, upside-down viewports, and resources packed tighter than 64 KB.

  8. Entry 069

    Reading past the end of a vertex buffer, the Direct3D way

    Direct3D 12 promises zeros when a draw reads past the end of a vertex buffer and Metal promises nothing, so mtl12 now has each vertex shader check its own reads.

  9. Entry 068

    Triangle fans on a GPU API that has none

    Metal can't draw triangle fans, so mtl12 rewrites each fan into a triangle list on the GPU just before the draw, keeping the vertex that flat shading reads in the right place.

  10. Entry 067

    A zero the Metal compiler must not see: keeping wave operations inside their loop

    A common GPU loop pattern gave wrong results on Metal because the compiler could prove a break was always taken; reading one constant zero through a volatile pointer fixes it.

  11. Entry 066

    Shader Model 6.8 was already there: it took two flags and one validation rule

    The layer now tells D3D12 applications it supports Shader Model 6.8. The shader translation already handled everything 6.8 adds; the one fix needed was letting buffers start in the unordered-access state.

  12. Entry 065

    Metal's SIMD-group functions take no 64-bit values, so they travel in two halves

    Any D3D12 shader that ran a wave operation on a 64-bit integer failed to compile on Metal, because Metal's SIMD-group functions have no 64-bit overloads. A SPIRV-Cross patch now splits such values into two 32-bit halves, or loops over the lanes for sums, products, min and max.

mtl12 ist ein Forschungsprojekt und kein Versprechen, dass bestimmte Spiele laufen werden. Das Devlog dokumentiert jeden Schritt mit seinen Zahlen, auch die Rückschläge.