{"id":47663289,"url":"https://github.com/displayxr/displayxr-unity","last_synced_at":"2026-07-04T03:04:01.052Z","repository":{"id":341791377,"uuid":"1171471887","full_name":"DisplayXR/displayxr-unity","owner":"DisplayXR","description":"Unity plugin for eye-tracked 3D light field displays via OpenXR","archived":false,"fork":false,"pushed_at":"2026-05-25T08:13:35.000Z","size":6085,"stargazers_count":1,"open_issues_count":36,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T10:31:07.950Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DisplayXR.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-03T09:12:11.000Z","updated_at":"2026-05-25T08:12:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/DisplayXR/displayxr-unity","commit_stats":null,"previous_names":["dfattal/unity-3d-display","displayxr/displayxr-unity"],"tags_count":184,"template":false,"template_full_name":null,"purl":"pkg:github/DisplayXR/displayxr-unity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DisplayXR%2Fdisplayxr-unity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DisplayXR%2Fdisplayxr-unity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DisplayXR%2Fdisplayxr-unity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DisplayXR%2Fdisplayxr-unity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DisplayXR","download_url":"https://codeload.github.com/DisplayXR/displayxr-unity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DisplayXR%2Fdisplayxr-unity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33718446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-02T11:45:17.018Z","updated_at":"2026-07-04T03:04:01.046Z","avatar_url":"https://github.com/DisplayXR.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DisplayXR — Unity Plugin\n\n[![Build Native Plugin](https://github.com/DisplayXR/displayxr-unity/actions/workflows/build-native.yml/badge.svg)](https://github.com/DisplayXR/displayxr-unity/actions/workflows/build-native.yml)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)\n\nUnity plugin for rendering on eye-tracked 3D light field displays via the DisplayXR OpenXR runtime. Works with any OpenXR-compatible 3D display.\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Requirements](#requirements)\n- [Installing the Plugin](#installing-the-plugin)\n- [Enabling the Feature](#enabling-the-feature)\n- [Scene Setup](#scene-setup)\n  - [Camera-Centric Mode (Recommended)](#camera-centric-mode-recommended)\n  - [Display-Centric Mode](#display-centric-mode)\n- [Stereo Tunables Reference](#stereo-tunables-reference)\n- [2D UI Overlay](#2d-ui-overlay)\n- [Preview (Play Mode)](#preview-play-mode)\n- [Building Your App](#building-your-app)\n  - [Windows Standalone](#windows-standalone)\n  - [macOS Standalone](#macOS-standalone)\n  - [Cross-Compiling (macOS Editor to Windows Build)](#cross-compiling-macos-editor-to-windows-build)\n- [Deploying to End Users](#deploying-to-end-users)\n- [Testing Without Hardware](#testing-without-hardware)\n- [Troubleshooting](#troubleshooting)\n- [Architecture](#architecture)\n\n\u003e **New to the plugin?** Start with the [Quick Start Guide](docs~/quick-start-guide.md) — a step-by-step walkthrough that covers installation, demo scenes for both stereo modes, building standalone apps, and end-to-end testing on Windows and macOS.\n\n\u003e **Want a ready-to-open Unity project?** Clone [displayxr-unity-test](https://github.com/DisplayXR/displayxr-unity-test) — a minimal Unity 6 project that depends on this plugin via UPM. Open it in Unity, hit Play, and you're rendering on a spatial display.\n\n---\n\n## Overview\n\nDisplayXR ships as a custom **Unity display provider** (`IUnityXRDisplay`, the same integration route as Oculus/Varjo/Cardboard) that drives the DisplayXR OpenXR runtime directly while Unity keeps rendering the scene. It provides:\n\n- **Eye-tracked stereo rendering** — runtime-owned Kooima asymmetric frustum projection from real-time eye positions (`XR_EXT_view_rig`)\n- **Two stereo rig modes** — Camera-centric (add to existing camera) or display-centric (place a virtual display in the scene)\n- **2D/3D display zones + 2D UI overlay** — frame 3D content to window-pixel zones and route any Canvas to a window-space composition layer with stereo disparity\n- **BiRP and URP** — off-axis projection on both pipelines, plus Single-Pass-Instanced (SPI) on URP+Windows+D3D12 and Multi-Pass elsewhere\n- **In-editor Play Mode preview** — pressing Play runs the provider itself, giving full parity with built apps (#171). Play Mode *is* the preview — there is no separate edit-mode preview window.\n\n**How it works:** the provider registers a `DisplayXR Display` display subsystem, binds an OpenXR session to Unity's graphics device, chains the runtime's `XR_EXT_view_rig` descriptor onto `xrLocateViews`, and submits Unity's rendered eye textures back to the runtime compositor via `xrEndFrame`. Enable it under **XR Plug-in Management \u003e Standalone \u003e DisplayXR Display** (see [Enabling the Feature](#enabling-the-feature)). See [`docs~/architecture/xr-display-provider.md`](docs~/architecture/xr-display-provider.md) for the full provider design.\n\n---\n\n## Requirements\n\n| Requirement | Details |\n|-------------|---------|\n| **Unity** | 2022.3 LTS or later (including Unity 6) |\n| **OpenXR Plugin** | `com.unity.xr.openxr` 1.9.1+ (installed via Package Manager) |\n| **XR Plugin Management** | `com.unity.xr.management` 4.4.0+ (auto-installed with OpenXR) |\n| **DisplayXR Runtime** | [`DisplayXRSetup-*.exe`](https://github.com/DisplayXR/displayxr-runtime/releases/latest) (v1.4.0+) — required on every target machine. See [Deploying to End Users](#deploying-to-end-users). |\n| **Vendor display plug-in** (Leia hardware only) | [`DisplayXRLeiaSRSetup-*.exe`](https://github.com/DisplayXR/displayxr-leia-plugin/releases/latest) — required for Leia SR hardware. Not needed for sim_display testing or non-Leia displays. Install **after** the runtime. |\n\n### Platform Support\n\n| Editor Platform | Build Target | Native Plugin | Status |\n|----------------|--------------|---------------|--------|\n| Windows | Windows x64 | `displayxr_unity.dll` | Supported |\n| macOS | macOS | `libdisplayxr_unity.dylib` | Supported |\n| macOS | Windows x64 | `displayxr_unity.dll` | Supported (cross-compile) |\n\n---\n\n## Installing the Plugin\n\n### Option A: From Local Folder (Development)\n\nClone and build the native plugin, then add the package from disk:\n\n```bash\ngit clone https://github.com/DisplayXR/displayxr-unity.git\ncd displayxr-unity/native~\nmkdir build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release    # on Windows, add: -A x64\ncmake --build . --config Release\n```\n\nIn Unity: **Window \u003e Package Manager \u003e + \u003e Add package from disk...** → select `displayxr-unity/package.json`.\n\n### Option B: From Git URL (Recommended)\n\n\u003e **Git must be in PATH.** Unity's Package Manager requires Git installed and accessible to GUI apps.\n\u003e - **Windows:** Install [Git for Windows](https://gitforwindows.org/) and ensure it's in your system PATH.\n\u003e - **macOS:** If Git is installed via Homebrew, Unity launched from Hub/Finder may not find it (GUI apps don't inherit your shell PATH). Fix: run `xcode-select --install` to ensure `/usr/bin/git` works, or launch Unity from a terminal. See [Troubleshooting](#troubleshooting) for details.\n\n1. In Unity: **Window \u003e Package Manager**\n2. Click **+** \u003e **Add package from git URL...**\n3. Enter one of:\n\n   **Pinned to a specific version (recommended for production):**\n   ```\n   https://github.com/DisplayXR/displayxr-unity.git#upm/v1.0.0\n   ```\n\n   **Always latest release:**\n   ```\n   https://github.com/DisplayXR/displayxr-unity.git#upm\n   ```\n\nBoth URLs install from the `upm` branch, which includes pre-built native binaries (Windows x64 + macOS Universal) — no local build required. See the [releases page](https://github.com/DisplayXR/displayxr-unity/releases) for available versions.\n\nTo **update** to a newer version later: change the URL fragment (e.g., `#upm/v1.0.1`) in `Packages/manifest.json` and use **Package Manager → Refresh**, or re-add the package with the new URL.\n\n### Option C: From Release Tarball\n\n1. Download the `.tgz` file from the [latest release](https://github.com/DisplayXR/displayxr-unity/releases)\n2. In Unity: **Window \u003e Package Manager**\n3. Click **+** \u003e **Add package from tarball...**\n4. Select the downloaded `.tgz` file\n\nAfter installation, the package appears as **DisplayXR** in the Package Manager.\n\n---\n\n## Enabling the Feature\n\nDisplayXR is enabled as a display provider (the shipping path):\n\n1. Go to **Edit \u003e Project Settings \u003e XR Plug-in Management**\n2. Under the **Standalone** tab (Windows or macOS), check **DisplayXR Display**\n3. Make sure **OpenXR** is **not** also checked on the same tab (the provider drives the runtime itself; running both loaders conflicts)\n4. Check **Initialize XR on Startup** so the provider comes up in Play Mode and in built apps\n\nYou can verify the runtime status in the **DisplayXR** rig inspectors and the **DisplayXR** settings panel (display resolution, eye-tracking state).\n\n---\n\n## Scene Setup\n\n### Camera-Centric Mode (Recommended)\n\nThis is the simplest setup. Your existing Main Camera stays in place and becomes the nominal viewing position. The plugin creates stereo eye offsets around it.\n\n**Step 1:** Select your **Main Camera** in the hierarchy.\n\n**Step 2:** Add Component \u003e **DisplayXRCamera** (found under DisplayXR category).\n\nThat's it. The component:\n- Reads eye tracking data from the runtime each frame\n- Computes Kooima asymmetric frustums around the camera position\n- Pushes modified FOVs into the OpenXR pipeline before Unity renders\n\n```\nHierarchy:\n  Main Camera              \u003c-- has Camera + DisplayXRCamera\n    ├── Scene objects...\n    └── (everything else in your scene)\n```\n\n**Inspector tunables:**\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| IPD Factor | 1.0 | Scales inter-eye distance. \u003c1 = reduced stereo, \u003e1 = exaggerated |\n| Parallax Factor | 1.0 | Scales eye X/Y offset from viewing axis |\n| Inv. Convergence Distance | 0 | 1/meters. 0 = infinity (parallel projection). Higher values = screen plane closer to camera. The inspector shows the equivalent distance in meters. |\n\n\u003e **Note:** The component inherits the camera's vertical FOV automatically. Change it on the Camera component, not on DisplayXRCamera.\n\n**When to use camera-centric mode:**\n- First-person games and apps\n- Retrofitting stereo into an existing project (just add the component)\n- When the camera moves freely through the scene\n\n### Display-Centric Mode\n\nThe display is a fixed object in the scene. Eye positions are transformed relative to this virtual display.\n\n**Step 1:** Create an empty GameObject where the virtual display should be:\n- **GameObject \u003e Create Empty**, name it \"VirtualDisplay\"\n- Position and orient it in the scene (e.g., at `(0, 1.5, 2)` facing the player)\n\n**Step 2:** Add Component \u003e **DisplayXRDisplay**\n\n**Step 3:** Make your Main Camera a child of this object, or position it separately — the display's world transform is sent to the native plugin as the \"scene transform\" applied to raw eye positions.\n\n```\nHierarchy:\n  VirtualDisplay           \u003c-- has DisplayXRDisplay\n    └── Main Camera        \u003c-- standard Camera component\n```\n\n**Inspector tunables:**\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| IPD Factor | 1.0 | Scales inter-eye distance |\n| Parallax Factor | 1.0 | Scales eye X/Y offset from display center |\n| Perspective Factor | 1.0 | Scales eye Z only (depth intensity without changing baseline) |\n| Virtual Display Height | 0 | Virtual display height in meters. 0 = use physical display height. The inspector shows the computed virtual size. The parent transform's scale acts as zoom. |\n\n**When to use display-centric mode:**\n- Digital signage, museum exhibits, kiosks\n- The virtual display is a physical object in the scene (e.g., a TV on a wall)\n- You want the display's position and orientation to be an explicit scene element\n\n\u003e **Try it:** Import the **Display Scene** sample from Package Manager for a ready-made tabletop turntable demo.\n\n---\n\n## Stereo Tunables Reference\n\nBoth modes share a common tunable interface that maps to the native plugin's Kooima computation. Here's what each parameter does physically:\n\n### IPD Factor (both modes)\nScales the horizontal distance between left and right eye positions.\n- `1.0` = natural inter-pupillary distance from eye tracker\n- `0.5` = half the real IPD (gentler stereo, less eye strain)\n- `2.0` = double the real IPD (exaggerated depth, \"macro\" effect)\n- `0.0` = mono rendering (both eyes at center)\n\n### Parallax Factor (both modes)\nScales the eye's X and Y offset from the viewing axis (or display center).\n- `1.0` = natural head parallax\n- `0.0` = no parallax (stereo still works via IPD, but no motion parallax)\n- `\u003e1.0` = exaggerated motion parallax\n\n### Perspective Factor (display-centric only)\nScales the eye's Z position (distance from display) without changing the baseline.\n- `1.0` = natural depth\n- `\u003c1.0` = eyes appear closer to display (stronger perspective)\n- `\u003e1.0` = eyes appear farther (flatter perspective)\n\n### Virtual Display Height (display-centric only)\nSets the virtual display height in meters for the Kooima projection.\n- `0` = use the physical display's actual height (default)\n- `0.3` = 30 cm virtual display (scene objects appear larger — magnifier effect)\n- `0.6` = 60 cm virtual display (scene objects appear smaller)\n\nThe parent transform's scale acts as zoom: scaling up the DisplayXRDisplay object zooms into the scene.\n\n### Inv. Convergence Distance (camera-centric only)\nInverse convergence distance in 1/meters. Controls where the virtual screen plane sits relative to the camera. Objects at the convergence distance appear at the display surface; closer objects pop out, farther objects recede.\n- `0` = infinity (parallel projection — no convergence)\n- `2.0` = screen plane at 0.5 m from camera\n- `1.0` = screen plane at 1.0 m from camera\n- The inspector shows the equivalent distance in meters or \"∞\"\n\n\u003e **Note:** Camera-centric FOV is inherited automatically from the Camera component. There is no separate FOV tunable.\n\n---\n\n## 2D UI Overlay\n\nRoute a Canvas to a window-space composition layer that the DisplayXR compositor overlays on both eyes with per-eye disparity shift (renders pre-interlace).\n\n1. Create a **Canvas** (any render mode)\n2. Add Component \u003e **DisplayXRWindowSpaceUI** to the Canvas\n3. Configure position and size in fractional window coordinates [0..1]:\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| Position X | 0.0 | Left edge of overlay in window (0 = left, 1 = right) |\n| Position Y | 0.0 | Bottom edge (0 = bottom, 1 = top) |\n| Width | 1.0 | Fractional width |\n| Height | 1.0 | Fractional height |\n| Disparity | 0.0 | Stereo disparity in pixels. 0 = at screen plane, positive = in front |\n| Resolution | 512 | Render texture resolution (square) |\n\nThe overlay is submitted as `XrCompositionLayerWindowSpaceEXT` and composited by DisplayXR before display processing. This means 2D UI text stays sharp and is not interlaced — ideal for HUDs, menus, and status displays.\n\n---\n\n## Preview (Play Mode)\n\n**Press Play — Play Mode is the preview.** With the **DisplayXR Display** provider active (the shipping path), Play Mode runs the provider itself: the same backend as a built app, weaving to the display in a dedicated editor window (#171). This gives true parity with built apps — provider behavior and bugs surface directly in the editor — with no separate preview session or Play/Stop-free workflow to manage.\n\nRendering modes are enumerated dynamically from the runtime and depend on the connected display hardware; mode switching is app policy (see `DisplayXRModeSwitch`). The runtime status (resolution, physical dimensions, eye-tracking state, current mode) is visible in the DisplayXR rig inspectors and settings panel.\n\n---\n\n## Building Your App\n\n### Windows Standalone\n\n1. **File \u003e Build Settings**\n2. Select **Windows, Mac, Linux** platform\n3. Set **Target Platform** to **Windows** and **Architecture** to **x86_64**\n4. Verify in **Player Settings \u003e XR Plug-in Management \u003e Standalone** that **DisplayXR Display** is enabled\n5. Click **Build** or **Build And Run**\n\nThe build output includes `displayxr_unity.dll` in the `Plugins/` folder alongside your executable.\n\n### macOS Standalone\n\n1. **File \u003e Build Settings**\n2. Select **macOS** platform\n3. Verify **DisplayXR Display** is enabled in Standalone XR settings\n4. Click **Build**\n\nThe `.app` bundle includes `libdisplayxr_unity.dylib` in the plugins folder.\n\n### Cross-Compiling (macOS Editor to Windows Build)\n\n**Yes, you can build a Windows app from Unity for Mac.** Unity's cross-compilation support handles this:\n\n1. Install the **Windows Build Support** module in Unity Hub:\n   - Unity Hub \u003e Installs \u003e your Unity version \u003e Add Modules \u003e **Windows Build Support (Mono)**\n2. In Unity: **File \u003e Build Settings \u003e Windows, Mac, Linux**\n3. Set **Target Platform** to **Windows**\n4. Build as normal\n\nThe plugin includes both platform binaries (`displayxr_unity.dll` for Windows, `libdisplayxr_unity.dylib` for macOS). Unity automatically selects the correct one based on the build target.\n\n**Important:** The built Windows `.exe` still requires the DisplayXR runtime installed on the **target Windows machine** — see [Deploying to End Users](#deploying-to-end-users). You cannot run the Windows build on macOS.\n\n---\n\n## Deploying to End Users\n\nYour built app is a standard OpenXR application. It needs an OpenXR runtime on the target machine. The plugin is hardware-agnostic — it communicates only through the OpenXR API and does not depend on any specific display vendor SDK.\n\n### Windows Deployment\n\nTwo installers, in order:\n\n**1. DisplayXR Runtime** (always required):\n\nInstall [`DisplayXRSetup-*.exe`](https://github.com/DisplayXR/displayxr-runtime/releases/latest) from the [displayxr-runtime](https://github.com/DisplayXR/displayxr-runtime/releases) releases. Registers the OpenXR runtime JSON, drops the runtime DLLs at `C:\\Program Files\\DisplayXR\\Runtime\\`, sets `HKLM\\Software\\Khronos\\OpenXR\\1\\ActiveRuntime` to the DisplayXR manifest, and auto-starts `displayxr-service.exe` at user logon.\n\nThe runtime ships with the `sim_display` plug-in (vendor-neutral SBS/anaglyph/blend fallback for any GPU). For real 3D hardware you also need the matching vendor plug-in below.\n\n**2. Leia SR plug-in** (required for Leia SR hardware only):\n\nInstall [`DisplayXRLeiaSRSetup-*.exe`](https://github.com/DisplayXR/displayxr-leia-plugin/releases/latest) from the [displayxr-leia-plugin](https://github.com/DisplayXR/displayxr-leia-plugin/releases) releases. Drops `DisplayXR-LeiaSR.dll` at `C:\\Program Files\\DisplayXR\\Plugins\\LeiaSR\\` and registers it at `HKLM\\Software\\DisplayXR\\DisplayProcessors\\leia-sr` (`ProbeOrder=50`). The runtime's registry-driven discovery picks it up at `xrCreateInstance` time; on Leia hardware it wins the probe and renders through the SR weaver, on non-Leia hardware it declines and the runtime falls back to `sim_display`.\n\n**Install order matters** — the Leia plug-in installer hard-prereqs `HKLM\\Software\\DisplayXR\\Runtime\\InstallPath` and refuses install if the runtime isn't already present.\n\nThe plug-in cascade-uninstalls with the runtime: uninstalling the runtime via `Add/Remove Programs` automatically uninstalls all registered vendor plug-ins (#286 fixed cleanup in runtime v1.4.1+).\n\n\u003e **Why two installers?** Pre-v1.4.0 the runtime installer bundled the Leia plug-in. As of v1.4.0 (`ADR-019` / [#263](https://github.com/DisplayXR/displayxr-runtime/issues/263)), vendor display drivers ship as separate plug-in installers from their own repos so the runtime stays vendor-source-clean and additional vendors don't bloat the runtime install set.\n\nOr, for development/testing without installation, set the environment variable to your dev runtime JSON:\n```cmd\nset XR_RUNTIME_JSON=C:\\path\\to\\openxr_displayxr-dev.json\n```\n\n### macOS Deployment\n\nSet before launching the app:\n```bash\nexport XR_RUNTIME_JSON=/path/to/DisplayXR-macOS/share/openxr/1/openxr_displayxr.json\n```\n\n#### Unsigned builds and the runtime symlink\n\nIf you have a runtime symlink at `~/Library/Application Support/openxr/1/active_runtime.json` (the OpenXR loader's standard discovery path), it works for the Unity editor but **not for unsigned built `.app` bundles**. macOS' file-access protections block unsigned apps from reading other apps' Application Support directories, so the loader can't find the runtime manifest. Symptom:\n\n```\nLoading OpenXR loader library at path: openxr_loader\n[XR] [FAILURE] xrCreateInstance: XR_ERROR_RUNTIME_UNAVAILABLE\n[DisplayXR] provider not active.\n```\n\nTwo ways around it:\n\n**1. Set `XR_RUNTIME_JSON` explicitly when launching** (works with any build, signed or not):\n```bash\nXR_RUNTIME_JSON=/path/to/openxr_displayxr.json /path/to/MyApp.app/Contents/MacOS/MyApp\n```\n\n**2. Code-sign the `.app`** so macOS grants it normal user-file access. Ad-hoc signing is enough for local development — no Apple Developer account needed:\n```bash\ncodesign --deep --force --sign - MyApp.app\n```\nAfter signing, the runtime symlink is reachable and you can launch the app without `XR_RUNTIME_JSON`.\n\nFor distributing to other users, sign with a Developer ID and notarize:\n```bash\ncodesign --deep --force --sign \"Developer ID Application: Your Name (TEAMID)\" \\\n         --options runtime --timestamp MyApp.app\nxcrun notarytool submit MyApp.app --apple-id you@example.com --team-id TEAMID --wait\nxcrun stapler staple MyApp.app\n```\n\n### What Happens Without the Runtime\n\nIf the DisplayXR runtime is not installed, the provider fails to bind an OpenXR session and logs:\n```\n[OpenXR] No OpenXR runtime found\n```\nThe plugin logs a warning but doesn't crash — your app runs in mono (non-stereo) mode. Query the DisplayXR runtime status (via the rig components / `DisplayXRProvider`) to detect this and show a user-facing message.\n\n---\n\n## Testing Without Hardware\n\nThe **sim_display** driver provides a software 3D display for development:\n\n```bash\n# Windows\nset SIM_DISPLAY_ENABLE=1\nset SIM_DISPLAY_OUTPUT=sbs          # side-by-side stereo\n# or: anaglyph (red-cyan), blend (50/50 alpha)\n\n# macOS\nexport SIM_DISPLAY_ENABLE=1\nexport SIM_DISPLAY_OUTPUT=sbs\n```\n\nWith sim_display:\n- Eye tracking is simulated via keyboard/mouse (qwerty driver)\n- Display dimensions are synthetic (configurable)\n- Output renders as SBS, anaglyph, or alpha-blend in a regular window\n\nThis lets you develop and test the full stereo pipeline on any machine.\n\n---\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| \"No OpenXR runtime found\" | `XR_RUNTIME_JSON` not set or points to missing file | Set the env var to the DisplayXR runtime JSON path |\n| Black screen | DisplayXR not enabled | Check **Project Settings \u003e XR Plug-in Management \u003e Standalone \u003e DisplayXR Display**, and **Initialize XR on Startup**. |\n| Scene renders 2D side-by-side on Leia hardware (no depth) | Leia plug-in not installed/registered | Install `DisplayXRLeiaSRSetup-*.exe` from [displayxr-leia-plugin](https://github.com/DisplayXR/displayxr-leia-plugin/releases). Verify `HKLM\\Software\\DisplayXR\\DisplayProcessors\\leia-sr` exists. Without the plug-in, the runtime falls back to `sim_display` (SBS output) on any hardware. |\n| No stereo (flat image) | Eye tracking not running | Verify the DisplayXR runtime is configured with a display that supports eye tracking, or use sim_display for testing |\n| Stereo looks wrong | Tunables misconfigured | Reset to defaults (IPD=1, Parallax=1, Scale=1) |\n| UPM \"cannot add package from git URL\" (Windows) | Git not installed or not in PATH | Install [Git for Windows](https://gitforwindows.org/), restart Unity. Verify with `git --version` in a terminal. |\n| UPM \"cannot add package from git URL\" (macOS) | GUI apps can't find Homebrew Git | Run `xcode-select --install` to set up `/usr/bin/git`, or launch Unity from terminal (`open -a Unity`). Check `~/Library/Logs/Unity/upm.log` for details. |\n| `DllNotFoundException: displayxr_unity` | Native plugin not found by Unity | Ensure the plugin binaries are in `Runtime/Plugins/Windows/x64/` or `Runtime/Plugins/macOS/` |\n| macOS: `XR_ERROR_RUNTIME_UNAVAILABLE` in built `.app` despite the symlink working in editor | Unsigned `.app` bundles can't read `~/Library/Application Support/openxr/` due to macOS sandbox protections | Either set `XR_RUNTIME_JSON` when launching, or ad-hoc sign with `codesign --deep --force --sign - MyApp.app`. See [macOS Deployment](#macos-deployment). |\n| HDRP stereo artifacts | Single-pass instanced issue | Verify both eye views have correct FOVs in Frame Debugger |\n| Play Mode shows \"Not Connected\" | `XR_RUNTIME_JSON` not set or runtime not running | Set the env var before launching Unity; verify the runtime process is active |\n| Play Mode shows black | Runtime connected but no output | Check runtime logs; verify sim_display or hardware is configured |\n| `VK_ERROR_EXTENSION_NOT_PRESENT` on macOS | MoltenVK limitation | Known issue — use sim_display for testing |\n\n### Debug Logging\n\nEnable **Log Eye Tracking** on the DisplayXRCamera or DisplayXRDisplay component to see per-frame eye positions in the Console:\n```\n[DisplayXR] Eyes: L=(0.032, 0.001, 0.504), R=(-0.031, 0.001, 0.504), tracked=True\n```\n\n### Checking Runtime Status in Editor\n\nWith the provider active, the **DisplayXRCamera / DisplayXRDisplay inspectors** and the **DisplayXR settings panel** show runtime status:\n- Connected display properties (resolution, physical size, nominal viewer distance)\n- Eye tracking status\n\n---\n\n## Architecture\n\nDisplayXR ships as a custom **`IUnityXRDisplay` display provider** (the shipping path). The provider\nregisters a `DisplayXR Display` subsystem, owns an OpenXR session on Unity's graphics device, and hands\nUnity's rendered eye textures to the runtime compositor — Unity keeps rendering the scene (BiRP/URP,\nSPI/Multi-Pass), the runtime owns the Kooima math (`XR_EXT_view_rig`).\n\n```\nUnity Editor / Player\n┌──────────────────────────────────────────────────────────┐\n│  C# Layer                                                │\n│                                                          │\n│  DisplayXRDisplayLoader : XRLoaderHelper                 │\n│    starts the \"DisplayXR Display\" display subsystem      │\n│  DisplayXRProvider / DisplayXRProviderDriver             │\n│    push rig tunables, modes/zones, events (dxr_prov_*)   │\n│                                                          │\n│  DisplayXRCamera          DisplayXRDisplay                 │\n│  (camera-centric)        (display-centric)               │\n│                                                          │\n│  DisplayXRWindowSpaceUI / DisplayXRLocal2D  (2D layers)   │\n└──────────────────────────────────────────────────────────┘\n        │ P/Invoke (displayxr_unity.dll / .dylib)\n        ▼\n┌──────────────────────────────────────────────────────────┐\n│  Native Plugin (C/C++)                                   │\n│  Display provider (IUnityXRDisplay):                     │\n│    session on Unity's device → render params (SPI/MP)   │\n│    xrLocateViews → chain XR_EXT_view_rig descriptor     │\n│    xrEndFrame → submit projection + zone + 2D layers    │\n└──────────────────────────────────────────────────────────┘\n        │ Standard OpenXR API\n        ▼\n┌──────────────────────────────────────────────────────────┐\n│  DisplayXR Runtime                                          │\n│  Compositor → Display Processor → Output                 │\n└──────────────────────────────────────────────────────────┘\n```\n\nSee [`docs~/architecture/xr-display-provider.md`](docs~/architecture/xr-display-provider.md) for the full\nprovider design, and [`docs~/adr/ADR-007-render-path-by-view-count.md`](docs~/adr/ADR-007-render-path-by-view-count.md)\nfor the roadmap to many-view (light-field / quilt) displays.\n\n### Transform Chain (per frame)\n\n```\nEye Tracker → raw positions in DISPLAY space\n    ↓\nScene Transform (parent camera pose, zoom)\n    ↓\nTunables (IPD factor, parallax, perspective, scale)\n    ↓\nKooima Asymmetric Frustum → XrFovf angles\n    ↓\nUnity builds projection matrices → renders stereo\n```\n\n---\n\n## Package Contents\n\n| Path | Purpose |\n|------|---------|\n| `Runtime/Provider/` | **Display provider (shipping path):** `DisplayXRDisplayLoader` (XR Plug-in Management loader), `DisplayXRProvider`/`DisplayXRProviderDriver` (facade + per-frame driver), `DisplayXRProviderNative` (P/Invoke) |\n| `Editor/Provider/DisplayXRDisplayPackage.cs` | Registers the \"DisplayXR Display\" provider toggle in XR Plug-in Management |\n| `Runtime/DisplayXRCamera.cs` | Camera-centric stereo rig MonoBehaviour |\n| `Runtime/DisplayXRDisplay.cs` | Display-centric stereo rig MonoBehaviour |\n| `Runtime/DisplayXRDisplayInfo.cs` | Display properties data struct |\n| `Runtime/DisplayXRTunables.cs` | Tunable parameters struct |\n| `Runtime/DisplayXRWindowSpaceUI.cs` | 2D UI overlay routing |\n| `Runtime/DisplayXRNative.cs` | P/Invoke bindings to native plugin |\n| `Runtime/Plugins/Windows/x64/` | Windows native plugin (DLL) |\n| `Runtime/Plugins/macOS/` | macOS native plugin (dylib) |\n| `Editor/DisplayXRDisplayEditor.cs` | Custom inspector for display-centric mode |\n| `Editor/DisplayXRCameraEditor.cs` | Custom inspector for camera-centric mode |\n| `Editor/DisplayXRSettingsProvider.cs` | Project Settings page |\n| `native~/` | Native C/C++ plugin source + CMakeLists.txt |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdisplayxr%2Fdisplayxr-unity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdisplayxr%2Fdisplayxr-unity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdisplayxr%2Fdisplayxr-unity/lists"}