El problema fundamental que Triton resuelve es la falta de aceleración gráfica moderna y eficiente para sistemas operativos invitados (guests) Windows en entornos de virtualización como QEMU, especialmente en hosts macOS con arquitectura Apple Silicon. Históricamente, la virtualización de GPUs ha sido un desafío complejo debido a la necesidad de traducir las interfaces de programación de aplicaciones (APIs) gráficas del guest a las del host, y la complejidad de la interacción entre los drivers de modo usuario (UMD) y de modo kernel (KMD) del sistema operativo invitado.

La solución de Triton se basa en un enfoque de 'transformación inversa' de las llamadas DDI (Device Driver Interface) de DirectX 11 del guest a las API de DirectX 11, que luego son serializadas por el protocolo Neptune existente y ejecutadas directamente en el host. Esto evita la necesidad de un intérprete de bytecode intermedio en el host, reduciendo la latencia y la complejidad, y mejorando la compatibilidad. La aparición de arquitecturas de memoria unificada (UMA) como Apple Silicon también simplifica el manejo de memoria compartida entre CPU y GPU, facilitando la implementación de texturas y cercas compartidas.

Arquitectura del Sistema

La arquitectura de Triton se integra en la pila de gráficos de Windows y QEMU. En el guest Windows, la aplicación realiza llamadas a las librerías del sistema DirectX y DXGI. Estas librerías invocan a Triton a través de llamadas DDI. Triton, actuando como un UMD, convierte estas llamadas DDI de vuelta a llamadas API de DirectX y DXGI. Estas llamadas API son luego pasadas a Neptune.

Neptune, que funciona como un UMD en el guest, serializa las llamadas API de DirectX y las transfiere a través de un ring buffer gestionado por el KMD. El KMD utiliza la interfaz VirtIO para enviar comandos al host. En el host, QEMU recibe estos comandos y los pasa a virglrenderer. El módulo host de Neptune dentro de virglrenderer deserializa las llamadas API de DirectX y las reenvía a la implementación de DirectX del host (DXVK+MoltenVK, DXMT o D3DMetal). Finalmente, la implementación de DirectX del host renderiza el frame.

Un componente crítico es el manejo de DXBC (DirectX Byte Code) para shaders. Triton no necesita desensamblar o convertir el bytecode; en su lugar, interpreta el bytecode para reconstruir los metadatos que d3d11.dll espera, permitiendo que el bytecode se pase sin modificar al host. Para la sincronización y el intercambio de texturas entre procesos en macOS, se utiliza shm_open() para crear objetos de memoria compartida mapeados a MTLBuffer en arquitecturas UMA, y un mecanismo de 'fences' emulados basado en ID3D11DeviceContext::ClearUnorderedAccessViewUint para escribir valores de línea de tiempo en memoria compartida, permitiendo la sincronización GPU-CPU.

Flujo de Renderizado DirectX 11 (Guest a Host)

  1. 1 App (Guest) Realiza llamadas API de DirectX/DXGI
  2. 2 System D3D/DXGI (Guest) Invoca a Triton a través de llamadas DDI
  3. 3 Triton UMD (Guest) Convierte DDI a API de DirectX/DXGI y reconstruye metadatos DXBC
  4. 4 Neptune UMD (Guest) Serializa llamadas API y las pasa a KMD
  5. 5 KMD (Guest) Usa VirtIO para enviar comandos al host
  6. 6 QEMU (Host) Maneja comandos y pasa llamadas Neptune a virglrenderer
  7. 7 virglrenderer (Host) Módulo Neptune deserializa llamadas API
  8. 8 Host D3D Impl (Host) Renderiza el frame (DXVK+MoltenVK, DXMT, D3DMetal)
CapaTecnologíaJustificación
orchestration QEMU Hipervisor principal que gestiona la máquina virtual y la comunicación con el host. -device virtio-ramfb-gl,hostmem=8G,blob=true,venus=true,neptune=true
networking VirtIO Interfaz paravirtualizada para la comunicación de comandos gráficos entre el guest y el host.
compute Triton (UMD) Driver de modo usuario en el guest Windows que traduce DDI de DirectX 11 a API de DirectX 11. vs Implementación de DDI de VirtualBox (bytecode intermedio), Implementación de DDI de Mesa DirectX 10
compute Neptune (UMD/Host Module) Protocolo de serialización/deserialización de llamadas API de DirectX entre guest y host.
compute virglrenderer Componente del host que recibe y procesa los comandos gráficos de Neptune, reenviándolos a la implementación de DirectX del host.
compute DXVK Traductor de API D3D11 a Vulkan en el host Linux, usado con MoltenVK en macOS.
compute MoltenVK Traductor de API Vulkan a Metal en el host macOS.
compute DXMT Traductor de API D3D11 a Metal en el host macOS. vs DXVK + MoltenVK, D3DMetal
compute D3DMetal Implementación de D3D11/D3D12 sobre Metal de Apple (Game Porting Toolkit) en el host macOS. vs DXVK + MoltenVK, DXMT Requiere Rosetta 2 para ejecución en ARM64 debido a su slice x86_64.
storage Shared Memory (shm_open) Mecanismo para compartir texturas entre procesos en el host macOS, aprovechando UMA. vs MTLSharedTextureHandle + XPC, IOSurface, CALayerHost

Trade-offs

Ganancias
  • Rendimiento gráfico en VMs Windows
  • Compatibilidad con juegos y aplicaciones DirectX 11
  • Experiencia de escritorio fluida en VMs
  • Reducción de latencia en el pipeline gráfico
  • Simplificación del lado del host (no se requiere intérprete de bytecode)
Costes
  • Complejidad en la reconstrucción de metadatos DXBC
  • Latencia por polling en fences emulados (CPU-side wait)
  • Ineficiencia de memoria para texturas lineales compartidas
  • Restricciones de licencia para D3DMetal
export SRC=/path/to/checkouts
export PREFIX=/path/to/prefix
export ANGLE_INC="$SRC/WebKit/Source/ThirdParty/ANGLE/include"
export ANGLE_LIB="$PREFIX/ANGLE.xcarchive/Products/usr/local/lib"

# ANGLE
xcodebuild archive -archivePath "$PREFIX/ANGLE" -scheme ANGLE ...

# DXMT
meson setup build-native "-Dnative_llvm_path=$LLVM15" ...
meson install -C build-native

# virglrenderer (arm64)
meson setup "$SRC/virglrenderer/build-arm64" ...
meson install -C "$SRC/virglrenderer/build-arm64"
cp "$PREFIX/libexec/virgl_render_server" "$SRC/virglrenderer/virgl_render_server.arm64"

# virglrenderer (x86_64)
meson setup "$SRC/virglrenderer/build-x86_64" --cross-file "$SRC/d3dmetal-native/build-macos-x86_64.txt" ...
meson compile -C "$SRC/virglrenderer/build-x86_64"

# Fuse servers
lipo -create "$SRC/virglrenderer/virgl_render_server.arm64" "$SRC/virglrenderer/build-x86_64/server/virgl_render_server" -output "$PREFIX/libexec/virgl_render_server"
Ejemplo de cómo se configuran las variables de entorno y se compilan los componentes (ANGLE, libepoxy, DXMT, d3dmetal-native, virglrenderer, QEMU) en macOS, mostrando la interdependencia a través de `pkg-config` y prefijos de instalación.
D3DMETAL_FRAMEWORK_PATH="$D3DMETAL" \
DYLD_FALLBACK_LIBRARY_PATH="$PREFIX/lib:$ANGLE_LIB" \
ANGLE_DEFAULT_PLATFORM=metal \
VIRGL_LOG_LEVEL=debug \
"$PREFIX/bin/qemu-system-aarch64" \
-machine virt \
-accel hvf,ipa-granule-size=0x1000 \
-cpu host \
-smp cpus=4,sockets=1,cores=4,threads=1 \
-m 4096 \
-nodefaults \
-vga none \
-device virtio-ramfb-gl,hostmem=8G,blob=true,venus=true,neptune=true \
-display cocoa,gl=es \
-drive if=pflash,format=raw,unit=0,file.filename="$PREFIX/share/qemu/edk2-aarch64-code.fd",readonly=on \
-drive if=pflash,unit=1,file.filename="$VM/efi_vars.fd" \
-device nvme,drive=disk,serial=disk,bootindex=1 \
-drive if=none,media=disk,id=disk,file.filename="$VM/windows.qcow2",discard=unmap,detect-zeroes=unmap \
-device nec-usb-xhci,id=usb-bus \
-device usb-tablet,bus=usb-bus.0 \
-device usb-kbd,bus=usb-bus.0 \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22
Comando de ejemplo para iniciar una VM QEMU con los drivers Triton/Neptune habilitados, incluyendo la configuración de dispositivos VirtIO, aceleración HVF y variables de entorno para el renderizado en el host.

Fundamentos Teóricos

El problema de la virtualización de dispositivos de hardware, especialmente GPUs, se relaciona con los principios de la virtualización de sistemas operativos y la abstracción de hardware. Conceptos como la paravirtualización, donde el sistema operativo invitado es modificado para cooperar con el hipervisor, son evidentes en el uso de VirtIO. La serialización de llamadas API y DDI para su transporte a través de límites de proceso o máquina virtual es un patrón común en sistemas distribuidos, similar a los RPC (Remote Procedure Calls) o la serialización de objetos en sistemas distribuidos.

La gestión de memoria compartida y la sincronización entre procesos, especialmente en entornos heterogéneos (CPU-GPU), se basa en principios de concurrencia y sistemas operativos, donde los semáforos, mutexes y eventos de sincronización son fundamentales. La necesidad de reconstruir metadatos a partir de bytecode de shader sin el archivo contenedor original es un problema de ingeniería inversa y análisis de formato de archivo, que tiene paralelos con la ingeniería de compiladores y el análisis de lenguajes intermedios. Aunque no se cita un paper específico, la base teórica se encuentra en trabajos sobre virtualización de E/S, diseño de drivers de hardware y optimización de pipelines gráficos.