Skip to content

Case Studies & ProjectsSystems Researcher

Bloodborne on macOS — Apple Silicon Porting Research

By Burhan Moin

Background

Bloodborne remains a PlayStation-exclusive title with no official PC or Mac port. The game's internal engine (a modified Havok-based engine built for the PS4's GNM graphics API) is a significant distance from macOS and Metal. This project documents my attempt to understand what it would take to run Bloodborne natively on Apple Silicon — not through emulation, but through compatibility layer research and binary analysis.

This is exploratory systems research. No retail game assets are distributed. The work is documented at: github.com/burhanmoin1/bloodborne-macos-native

The PS4 Execution Environment

Architecture

The PS4 uses a modified FreeBSD kernel with a custom x86-64 ABI. The GPU is an AMD GCN 1.1 part addressed through the proprietary GNM/GNMX graphics API. Bloodborne's executable is an ORBIS ELF (Sony's modified ELF format).

Getting from an ORBIS ELF on FreeBSD/x86-64 + GNM to a Mach-O on macOS/ARM64 + Metal involves:

1. Binary format translation (ORBIS ELF → Mach-O)

2. ISA translation (x86-64 → ARM64)

3. Graphics API translation (GNM → Metal)

4. OS API shim (FreeBSD syscalls → macOS equivalents)

Phase 1 — Binary Analysis

I loaded the ORBIS ELF through a custom loader that remaps PS4-specific segment types. LLDB on macOS can attach to x86-64 processes via Rosetta, giving a debugging path for the x86-64 layer before ARM translation is considered.

bash·elf_inspect.sh
# Inspect ORBIS ELF structure
readelf -h eboot.bin
readelf -S eboot.bin | grep -E ".text|.data|.rodata|.got"

# Symbol extraction
nm -D eboot.bin | grep -i "render\|shader\|gnm\|draw" | head -50

The binary's symbol table (partially stripped but recoverable through the dynamic export table) revealed the GNM command-buffer building functions — the layer I needed to intercept for graphics translation.

Phase 2 — Shader Translation Research

GCN to Metal Shading Language

PS4 shaders are compiled GCN ISA binaries. Metal requires either MSL (Metal Shading Language) source or AIR (Apple Intermediate Representation) bytecode. The translation path I researched was GCN ISA → GLSL (via existing GCN disassemblers) → MSL (via SPIRV-Cross).

text·shader_pipeline.txt
GCN ISA binary
    ↓  gcn-disasm  (open source GCN disassembler)
GLSL source
    ↓  glslang → SPIRV
SPIR-V bytecode
    ↓  SPIRV-Cross
Metal Shading Language
    ↓  xcrun metal
AIR bytecode (ready for Metal GPU)

I tested this pipeline on isolated shader binaries extracted from game assets. Simple vertex and fragment shaders translated correctly. Complex geometry shaders and compute shaders used GCN-specific wave intrinsics (ds_swizzle, v_cndmask) with no direct MSL equivalent — these required manual mapping.

Phase 3 — GPU Command Buffer Translation

GNM command buffers are a list of PM4 packets — a command protocol shared with AMD's public GPU architecture. I wrote a C++ interceptor that walks a captured GNM command buffer and maps PM4 packets to their Metal equivalents:

cpp·gnm_to_metal.cpp
// Simplified PM4 → Metal command mapping
void translateDrawIndexAuto(const PM4Packet* pkt, id<MTLRenderCommandEncoder> enc) {
    // GNM: DRAW_INDEX_AUTO — indexed draw with auto-generated indices
    NSUInteger vertexCount = pkt->count;
    [enc drawPrimitives:MTLPrimitiveTypeTriangle
           vertexStart:0
           vertexCount:vertexCount];
}

Performance on M1 Pro

Using a compatibility harness with Rosetta for the x86-64 game logic and a Metal backend for rendering, I measured performance on an M1 Pro MacBook Pro:

text·benchmarks.txt
Scene: Yharnam opening area (outdoor)
Resolution: 1920×1200 (native display)

Rosetta x86-64 + Metal (translated shaders):
  Average FPS:  34
  1% low FPS:   21
  VRAM usage:   3.8 GB / 16 GB unified

Bottleneck: Rosetta JIT translation overhead on the render thread (~40% CPU overhead vs native ARM64 estimate)

Native ARM64 recompilation of the game logic (via ahead-of-time translation with a custom x86-64 → ARM64 recompiler) is the next research direction — expected to push the frame rate above 60 on M1 Pro.

Current Status

The project is ongoing. The shader translation pipeline handles ~70% of Bloodborne's shaders automatically. The remaining 30% (wave-level operations, async compute) require hand-written Metal equivalents. CPU-side is functional via Rosetta. The long-term goal is a full native ARM64 build.

[ Gameplay screenshots and performance graphs to be added here ]

Full documentation and source: github.com/burhanmoin1/bloodborne-macos-native