Skip to content

Latest commit

 

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

d3dmetal-native

Direct3D 11 and Direct3D 12 on macOS for native (non-Wine) applications, via Apple's D3DMetal.framework from the Game Porting Toolkit.

D3DMetal implements D3D11/D3D12/DXGI on top of Metal, but it is built to be hosted by Apple's Wine environment: it expects a GFXT host interface for windowing, events, registry, and memory services. This library implements that host interface natively — link libd3dmetal-native.dylib, and the exported D3D entry points (D3D11CreateDevice, D3D12CreateDevice, CreateDXGIFactory1, D3DCompile, ...) just work in an ordinary macOS process.

your app ──> libd3dmetal-native.dylib ──> D3DMetal.framework ──> Metal
             (GFXT host: windows, events,
              registry, allocation, swapchains,
              cross-process resource sharing)

Features

  • D3D11 and D3D12 devices with swapchains on:
    • an NSView (the library installs a CAMetalLayer), or
    • a caller-owned CAMetalLayer (offscreen or custom layer hierarchies).
  • Pseudo-HWNDs: dmn_window_get_hwnd() returns a small stable handle to pass anywhere a HWND is expected (CreateSwapChainForHwnd, ...).
  • Cross-process resource sharing, entirely through the standard D3D APIs (D3DMetal itself stubs these out; the library re-implements them):
    • shared textures and buffers (D3D11_RESOURCE_MISC_SHARED*, D3D12_HEAP_FLAG_SHARED, committed and placed resources), exported with GetSharedHandle / CreateSharedHandle and opened with OpenSharedResource(1) / OpenSharedHandle — cross-API in both directions (D3D11 producer → D3D12 consumer and vice versa);
    • shared fences (CreateFence + CreateSharedHandle, OpenSharedFence / OpenSharedHandle) with GPU-side queue/context Wait and Signal, SetEventOnCompletion, and multi-fence waits;
    • keyed mutexes (D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX + IDXGIKeyedMutex via QueryInterface).
  • Win32-style events: HANDLEs surfaced through D3D APIs (frame-latency waitable objects, SetEventOnCompletion) are waitable with dmn_event_wait(). dmn_event_create() mints HANDLEs to pass into those APIs yourself, and dmn_event_dup_fd() vends a poll(2)-compatible fd mirroring an event's signaled state (for fd-driven event loops).
  • D3D12 reserved (tiled) resources at TiledResourcesTier 2. D3DMetal itself has no tiled-resource support, so CreateReservedResource textures are placed on sparse Metal heaps and UpdateTileMappings maps real tiles: a virtual-texturing app pays RAM for the tiles it maps, not for the textures' virtual size. GetResourceTiling, CopyTiles, and packed mips follow the D3D12 tile model.
  • Registry pre-seeding (dmn_registry_set_*) and per-value environment overrides for the settings D3DMetal reads.
  • Executable-path override (dmn_set_executable_path) so D3DMetal's per-app profile matcher keys on the program you are hosting rather than your own process.

Requirements

  • macOS 14+.
  • x86_64 only: D3DMetal.framework ships as x86_64, so the entire process must be x86_64. On Apple Silicon everything runs under Rosetta 2.
  • D3DMetal.framework from Apple's Game Porting Toolkit. It is Apple-licensed and NOT bundled; obtain it from the GPTk evaluation environment and point the library at it (see below). If macOS quarantines it: xattr -dr com.apple.quarantine D3DMetal.framework.

Building

meson setup build --cross-file build-macos-x86_64.txt
meson compile -C build
meson test -C build          # optional; needs a D3DMetal.framework (see below)

The cross file is required on Apple Silicon hosts (it selects -arch x86_64; Rosetta runs the results, including the tests, transparently).

Using it

  1. Link against libd3dmetal-native.dylib only. Do not link or dlopen D3DMetal yourself; the library locates it, loads it, and installs the GFXT host interface. The search order for the framework:
    • dmn_options::framework_path passed to dmn_init(),
    • $D3DMETAL_FRAMEWORK_PATH,
    • next to the dylib (<dylib dir>/D3DMetal.framework), then <dylib dir>/../Frameworks/D3DMetal.framework.
  2. Bring your own D3D headers (Wine/MinGW widl-generated). The D3D/DXGI symbols resolve from this library at link time. TUs that include d3d12.h must be compiled with -DWIDL_EXPLICIT_AGGREGATE_RETURNS=1 to match D3DMetal's ABI for aggregate-returning methods (GetDesc, ...).
  3. Create a window object for your view or layer and use its pseudo-HWND:
#include <d3dmetal_native.h>

dmn_window_t win = dmn_window_create_for_view(nsview); /* main thread */
dmn_window_set_size(win, pixel_w, pixel_h, scale);
HWND hwnd = (HWND)dmn_window_get_hwnd(win);
/* ... D3D11CreateDevice / CreateDXGIFactory1 /
   IDXGIFactory2::CreateSwapChainForHwnd(dev, hwnd, ...) as on Windows ... */

dmn_init() is optional — the first D3D entry point performs lazy default initialization. See include/d3dmetal_native.h for the full API, including the shared-handle lifetime rules.

Cross-process sharing model

A HANDLE returned by the GetSharedHandle / CreateSharedHandle family is a pointer to a POD (dmn_shared_texture_handle, dmn_shared_buffer_handle, dmn_shared_fence_handle). The POD is copy-by-value; the only live resource in it is an fd, which you transport to the peer out-of-band (SCM_RIGHTS over a Unix socket) and write back into the received copy before OpenShared*. Handle lifetime follows the two Windows regimes: legacy GetSharedHandle values live and die with the exporting resource; NT-style CreateSharedHandle values own an fd and must be released with dmn_shared_handle_close(). tests/shared_texture_test.cpp and tests/fence_xapi_test.cpp are complete producer/consumer examples.

Environment variables

Variable Effect
D3DMETAL_FRAMEWORK_PATH Path to D3DMetal.framework (or its binary).
DMN_LOG Log threshold: error, warn (default), info, debug, trace, quiet.
DMN_REG_<NAME> Overrides registry value <NAME> (uppercased, non-alphanumerics as _) at read time.
DMN_VSYNC 0/1: sets displaySyncEnabled on the presentation layer.
DMN_RETINA Opt into the window's real backing scale (default is 1x, point == pixel).
DMN_PRESENT_FALLBACK Present the last vended drawable from the host side if D3DMetal's own present path does not.
DMN_RESIDENCY_SET 0 declares every substituted resource's GPU residency with a per-command-buffer useResources: batch instead of the MTLResidencySet used on macOS 15+ (sparse textures always take the per-command-buffer path).
DMN_SPARSE 0 disables sparse backing of D3D12 reserved (tiled) resources; they are then fully backed committed textures.
DMN_SPARSE_HEAP_MB / DMN_SPARSE_HEAP_MAX_MB Size of each sparse heap chunk (default 1024), and the largest chunk that may be opened for a single reserved resource that needs more than that (default 4096).
DMN_SPARSE_MAX_MB A hard cap on the total across sparse chunks; unset, the budget starts at DMN_SPARSE_BUDGET_FLOOR_MB (default 6144) and grows with the tile pools the app names, since it cannot map more tiles than it owns. Reserved resources created past the budget are fully backed.
DMN_SPARSE_OVERSUBSCRIBE How much virtual texture a sparse chunk may carry, as a multiple of its size (default 8, i.e. a tile density down to 12.5%). A denser app needs a lower value; the dry-chunk warning reports the value it implies.
DMN_SPARSE_STATS <n>: log the sparse module's sizing summary every n UpdateTileMappings calls at warn level (it is logged at info regardless).

Tests

meson test -C build runs the suite: device bring-up, on-screen triangle and cube demos, cross-process shared textures/buffers/fences/keyed mutexes in both API directions, GPU waits on imported fences, event-driven waits, shared heaps with placed resources, sparse-backed reserved resources, lifecycle/fd-leak churn, and a multi-threaded stress test. The windowed demos (d3d11-triangle, d3d11-cube, d3d12-triangle --shared, shared-compute-triangle without DMN_HEADLESS) can also be run directly as on-screen examples.

License

MIT (see LICENSE). D3DMetal.framework and the dylibs bundled with it are Apple's, licensed under the Game Porting Toolkit's own terms, and are not part of this project. tests/common/com.h and the D3D11 triangle demo are adapted from dxvk (zlib license).

About

DirectX implementation on Metal

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages