diff --git a/.claude/skills/steam-frame/SKILL.md b/.claude/skills/steam-frame/SKILL.md
index 0437825..48c9f1c 100644
--- a/.claude/skills/steam-frame/SKILL.md
+++ b/.claude/skills/steam-frame/SKILL.md
@@ -48,3 +48,9 @@ running `--help`: `paste-to-frame.sh`, `serve-bootstrap.sh` and
- `sudo` on the Frame asks for the user's Developer Mode password. Hand those
steps to the user (open Terminal) and keep automation to non-sudo commands.
- The Mac uses BSD userland and zsh (no `timeout`, use `head -n`).
+- **First-party first.** For any new capability, investigate the first-party
+ way before anything else: Valve (SteamOS, Steam, Steam Link), Apple (the Mac
+ and iPhone), and KDE (the Frame's desktop is Plasma). It's usually the best
+ answer. If it isn't, write down why not. If it is, find what Frame Control
+ can do to make it easier to set up (install over SSH, pre-seed settings,
+ pair automatically, tell the user the one setting to turn on).
diff --git a/.gitignore b/.gitignore
index 230bfe5..c2019bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ apk-catalog/data/index-v2*.json*
compat-db/.env.lakebed.server
compat-db/.lakebed/
tests/smoke/results/
+mac/bin/
diff --git a/README.md b/README.md
index cd87619..a30e430 100644
--- a/README.md
+++ b/README.md
@@ -207,6 +207,7 @@ Frame's software fits together, all checked against a real headset and labelled
| [Sideloading Linux and Windows games](docs/sideloading.md) | A .zip, folder or .exe as a Steam Devkit Game, runtime detection |
| [Install links for websites](docs/web-install.md) | `frame-control://install` links and manifests, the rules, a button to paste |
| [Steam games](docs/steam-games.md) · [VR video](docs/vr-video.md) · [WebXR in Chromium](docs/webxr-chromium.md) | Installing and buying, watching VR180/360, the Chromium build |
+| [Mac in the headset](docs/mac-in-headset.md) | Mac windows and screens as panels in the Frame, with laser and keyboard input |
| [VR mods and custom songs](docs/mods.md) | Per-game feasibility, real-Frame results and blockers; no installer yet |
| [SSH](docs/ssh.md) · [Streaming](docs/streaming.md) · [Files](docs/file-transfer.md) · [Panels](docs/panels.md) · [Tailscale](docs/tailscale.md) | Topic notes |
| [Frame Control for iPhone](docs/iphone.md) | The iPhone and iPad app, how it runs the server on the Frame, pairing |
diff --git a/app/package.json b/app/package.json
index 78f6989..6875a3b 100644
--- a/app/package.json
+++ b/app/package.json
@@ -9,8 +9,8 @@
"scripts": {
"start": "env -u ELECTRON_RUN_AS_NODE electron .",
"icon": "env -u ELECTRON_RUN_AS_NODE electron build/make-icon.js",
- "dist": "node build/fetch-deps.js mac arm64 && electron-builder --mac --arm64 --publish never",
- "dist:dir": "node build/fetch-deps.js mac arm64 && electron-builder --mac --arm64 --dir",
+ "dist": "sh ../mac/frame-mac-view/build.sh && node build/fetch-deps.js mac arm64 && electron-builder --mac --arm64 --publish never",
+ "dist:dir": "sh ../mac/frame-mac-view/build.sh && node build/fetch-deps.js mac arm64 && electron-builder --mac --arm64 --dir",
"dist:linux": "node build/fetch-deps.js linux x64 arm64 && electron-builder --linux --x64 --arm64 --publish never",
"dist:win": "node build/fetch-deps.js win x64 && electron-builder --win --x64 --publish never"
},
@@ -66,6 +66,14 @@
"*.py"
]
},
+ {
+ "from": "../frame/openxr-compat",
+ "to": "frame/openxr-compat",
+ "filter": [
+ "XrApiLayer_FRAME_compat.json",
+ "prebuilt/**/*.so"
+ ]
+ },
{
"from": "../frame/devkit-utils",
"to": "frame/devkit-utils",
@@ -107,9 +115,19 @@
"dmg",
"zip"
],
+ "extraResources": [
+ {
+ "from": "../mac/bin",
+ "to": "mac/bin",
+ "filter": [
+ "frame-mac-view"
+ ]
+ }
+ ],
"extendInfo": {
"NSAppleEventsUsageDescription": "Frame Control opens Terminal for SSH sessions and for power actions that need the Developer Mode password.",
- "NSLocalNetworkUsageDescription": "Frame Control connects to your Steam Frame over SSH on the local network."
+ "NSLocalNetworkUsageDescription": "Frame Control connects to your Steam Frame over SSH on the local network.",
+ "NSScreenCaptureUsageDescription": "Frame Control shows your Mac's windows and screens inside the Steam Frame when you ask it to."
},
"artifactName": "Frame-Control-mac-${arch}.${ext}"
},
diff --git a/bench/pages/present.html b/bench/pages/present.html
new file mode 100644
index 0000000..2e783fd
--- /dev/null
+++ b/bench/pages/present.html
@@ -0,0 +1,24 @@
+
+
+
fmv-present
+
+
diff --git a/bench/pages/scroll.html b/bench/pages/scroll.html
new file mode 100644
index 0000000..602ced9
--- /dev/null
+++ b/bench/pages/scroll.html
@@ -0,0 +1,32 @@
+
+
+fmv-bench scroll
+
+
diff --git a/bench/pages/type.html b/bench/pages/type.html
new file mode 100644
index 0000000..288904c
--- /dev/null
+++ b/bench/pages/type.html
@@ -0,0 +1,9 @@
+
+
+fmv-bench type
+
+
diff --git a/bench/results/2026-09-28-1d05f57-usb1.json b/bench/results/2026-09-28-1d05f57-usb1.json
new file mode 100644
index 0000000..47c65d3
--- /dev/null
+++ b/bench/results/2026-09-28-1d05f57-usb1.json
@@ -0,0 +1,677 @@
+{
+ "label": "usb1",
+ "date": "2026-09-28T21:16:03",
+ "commit": "1d05f57",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "10.86.200.233",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 911,
+ "frames_drawn": 911,
+ "frames_shown": 610,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 911
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 911
+ },
+ "encode": {
+ "p50": 4.4,
+ "p95": 5.0,
+ "p99": 5.2,
+ "n": 911
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 911
+ },
+ "network": {
+ "p50": 1.1,
+ "p95": 1.6,
+ "p99": 2.1,
+ "n": 911
+ },
+ "decode": {
+ "p50": 1.2,
+ "p95": 2.6,
+ "p99": 3.9,
+ "n": 911
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.9,
+ "p99": 1.5,
+ "n": 911
+ },
+ "present": {
+ "p50": 5.8,
+ "p95": 13.6,
+ "p99": 16.7,
+ "n": 610
+ },
+ "content": {
+ "p50": 7.3,
+ "p95": 8.8,
+ "p99": 10.0,
+ "n": 911
+ },
+ "content_shown": {
+ "p50": 13.6,
+ "p95": 20.4,
+ "p99": 24.9,
+ "n": 610
+ }
+ },
+ "fps": 59.9,
+ "fps_shown": 40.1,
+ "late_pct": 0.22,
+ "stall_max": 32.3,
+ "stalls_over_100ms": 0,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 912,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 16.6,
+ "p95": 24.3,
+ "p99": 24.5,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 20.8,
+ "p95": 31.8,
+ "p99": 33.8,
+ "n": 22
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 0.7,
+ "p95": 1.0,
+ "p99": 1.0,
+ "n": 30
+ },
+ "mac": {
+ "p50": 9.1,
+ "p95": 15.0,
+ "p99": 15.2,
+ "n": 30
+ },
+ "back": {
+ "p50": 7.2,
+ "p95": 8.5,
+ "p99": 12.7,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "asked": 30,
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 7.3,
+ "content_p95": 8.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 7.1,
+ "content_p95": 8.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 59,
+ "mbps": 0.48,
+ "content_p50": 7.2,
+ "content_p95": 8.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 7.1,
+ "content_p95": 9.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 6.9,
+ "content_p95": 8.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 7.3,
+ "content_p95": 8.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 7.4,
+ "content_p95": 8.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 7.4,
+ "content_p95": 8.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 7.3,
+ "content_p95": 8.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 7.3,
+ "content_p95": 8.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 7.3,
+ "content_p95": 8.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 7.4,
+ "content_p95": 9.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 7.4,
+ "content_p95": 8.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 7.6,
+ "content_p95": 9.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 7.6,
+ "content_p95": 8.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.09,
+ "content_p50": 7.5,
+ "content_p95": 9.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 7.3,
+ "content_p95": 8.8,
+ "input_p50": 16.6,
+ "input_p95": 24.3,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 0.917,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 60.0,
+ "cpu": {
+ "mac_agent_pct": 16.7,
+ "frame_viewer_pct": 48.5,
+ "frame_tailscaled_pct": 0.5,
+ "frame_sshd_pct": 1.1,
+ "frame_gamescope_pct": 12.0,
+ "frame_total_pct": 39.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 137 Hz",
+ "show_s": 1.15,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 848,
+ "frames_drawn": 848,
+ "frames_shown": 617,
+ "duration_s": 15.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.9,
+ "p95": 4.2,
+ "p99": 7.3,
+ "n": 848
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 848
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.7,
+ "p99": 17.7,
+ "n": 848
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 848
+ },
+ "network": {
+ "p50": 1.7,
+ "p95": 2.7,
+ "p99": 5.7,
+ "n": 848
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 10.3,
+ "p99": 13.1,
+ "n": 848
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.6,
+ "p99": 2.2,
+ "n": 848
+ },
+ "present": {
+ "p50": 4.4,
+ "p95": 15.8,
+ "p99": 20.6,
+ "n": 617
+ },
+ "content": {
+ "p50": 15.7,
+ "p95": 23.0,
+ "p99": 39.3,
+ "n": 848
+ },
+ "content_shown": {
+ "p50": 21.3,
+ "p95": 34.1,
+ "p99": 45.6,
+ "n": 617
+ }
+ },
+ "fps": 56.1,
+ "fps_shown": 40.7,
+ "late_pct": 3.3,
+ "stall_max": 238.5,
+ "stalls_over_100ms": 1,
+ "mbps": 9.01,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 857,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 44,
+ "mbps": 8.13,
+ "content_p50": 15.7,
+ "content_p95": 35.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 56,
+ "mbps": 7.04,
+ "content_p50": 16.3,
+ "content_p95": 47.3,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 8.58,
+ "content_p50": 16.0,
+ "content_p95": 22.6,
+ "bitrate": 10055362,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 56,
+ "mbps": 9.29,
+ "content_p50": 16.4,
+ "content_p95": 21.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 9.01,
+ "content_p50": 15.6,
+ "content_p95": 21.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 9.13,
+ "content_p50": 15.9,
+ "content_p95": 21.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 56,
+ "mbps": 11.63,
+ "content_p50": 16.1,
+ "content_p95": 22.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.7,
+ "content_p50": 16.3,
+ "content_p95": 22.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 57,
+ "mbps": 9.24,
+ "content_p50": 15.7,
+ "content_p95": 21.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.21,
+ "content_p50": 15.0,
+ "content_p95": 20.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 56,
+ "mbps": 8.55,
+ "content_p50": 15.7,
+ "content_p95": 20.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 9.51,
+ "content_p50": 14.7,
+ "content_p95": 19.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 57,
+ "mbps": 8.75,
+ "content_p50": 16.0,
+ "content_p95": 20.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 58,
+ "mbps": 9.25,
+ "content_p50": 15.0,
+ "content_p95": 20.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.13,
+ "content_p50": 15.3,
+ "content_p95": 21.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 7,
+ "mbps": 1.17,
+ "content_p50": 14.2,
+ "content_p95": 15.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 15.7,
+ "content_p95": 23.0,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 1.25,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 1.06,
+ "e": "down to 7430 kbit/s: queue 35 ms, held 4, oldest 32 ms, base 1 ms, sent 6282 got 4914 wants 11892"
+ }
+ ],
+ "source_fps": 56.8,
+ "cpu": {
+ "mac_agent_pct": 8.8,
+ "frame_viewer_pct": 74.8,
+ "frame_tailscaled_pct": 0.3,
+ "frame_sshd_pct": 1.2,
+ "frame_gamescope_pct": 10.5,
+ "frame_total_pct": 39.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 137 Hz",
+ "show_s": 1.16,
+ "panel": "valve.steam.desktopgame.2001968301",
+ "src": "separate:7914"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-1d05f57-usb2.json b/bench/results/2026-09-28-1d05f57-usb2.json
new file mode 100644
index 0000000..88464f7
--- /dev/null
+++ b/bench/results/2026-09-28-1d05f57-usb2.json
@@ -0,0 +1,690 @@
+{
+ "label": "usb2",
+ "date": "2026-09-28T21:17:48",
+ "commit": "1d05f57",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "10.86.200.233",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 913,
+ "frames_drawn": 913,
+ "frames_shown": 856,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 913
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 913
+ },
+ "encode": {
+ "p50": 4.3,
+ "p95": 4.9,
+ "p99": 5.2,
+ "n": 913
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 913
+ },
+ "network": {
+ "p50": 0.9,
+ "p95": 1.2,
+ "p99": 1.7,
+ "n": 913
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.5,
+ "p99": 3.8,
+ "n": 913
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 913
+ },
+ "present": {
+ "p50": 2.6,
+ "p95": 8.6,
+ "p99": 15.9,
+ "n": 856
+ },
+ "content": {
+ "p50": 6.9,
+ "p95": 8.4,
+ "p99": 9.6,
+ "n": 913
+ },
+ "content_shown": {
+ "p50": 9.7,
+ "p95": 15.8,
+ "p99": 23.1,
+ "n": 856
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 56.3,
+ "late_pct": 0.0,
+ "stall_max": 22.0,
+ "stalls_over_100ms": 0,
+ "mbps": 0.57,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 913,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 16.9,
+ "p95": 24.1,
+ "p99": 26.1,
+ "n": 33
+ },
+ "input_shown_ms": {
+ "p50": 19.7,
+ "p95": 26.6,
+ "p99": 29.2,
+ "n": 31
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 0.7,
+ "p95": 2.5,
+ "p99": 5.7,
+ "n": 33
+ },
+ "mac": {
+ "p50": 8.1,
+ "p95": 15.4,
+ "p99": 17.2,
+ "n": 33
+ },
+ "back": {
+ "p50": 7.3,
+ "p95": 8.3,
+ "p99": 8.9,
+ "n": 33
+ }
+ },
+ "inputs": {
+ "asked": 30,
+ "sent": 33,
+ "seen": 33
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 7.1,
+ "content_p95": 8.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 7.0,
+ "content_p95": 8.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 6.9,
+ "content_p95": 8.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 7.0,
+ "content_p95": 9.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 6.8,
+ "content_p95": 8.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.67,
+ "content_p50": 7.0,
+ "content_p95": 8.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 7.0,
+ "content_p95": 8.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.63,
+ "content_p50": 6.7,
+ "content_p95": 7.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.62,
+ "content_p50": 6.9,
+ "content_p95": 8.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.64,
+ "content_p50": 6.8,
+ "content_p95": 8.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.67,
+ "content_p50": 6.8,
+ "content_p95": 8.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.62,
+ "content_p50": 6.7,
+ "content_p95": 7.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 6.8,
+ "content_p95": 7.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 6.7,
+ "content_p95": 8.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 7.0,
+ "content_p95": 8.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 13,
+ "mbps": 0.13,
+ "content_p50": 6.4,
+ "content_p95": 7.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 6.9,
+ "content_p95": 8.4,
+ "input_p50": 16.9,
+ "input_p95": 24.1,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 0.709,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 60.1,
+ "cpu": {
+ "mac_agent_pct": 17.9,
+ "frame_viewer_pct": 51.9,
+ "frame_tailscaled_pct": 0.2,
+ "frame_sshd_pct": 1.1,
+ "frame_gamescope_pct": 9.6,
+ "frame_total_pct": 27.9
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 134 Hz",
+ "show_s": 1.15,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 868,
+ "frames_drawn": 868,
+ "frames_shown": 868,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.2,
+ "p95": 0.5,
+ "p99": 6.7,
+ "n": 868
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.2,
+ "n": 868
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 10.2,
+ "p99": 17.9,
+ "n": 868
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 868
+ },
+ "network": {
+ "p50": 1.5,
+ "p95": 2.3,
+ "p99": 7.6,
+ "n": 868
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 10.2,
+ "p99": 20.3,
+ "n": 868
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.4,
+ "p99": 2.1,
+ "n": 868
+ },
+ "present": {
+ "p50": 0.9,
+ "p95": 6.1,
+ "p99": 6.8,
+ "n": 868
+ },
+ "content": {
+ "p50": 15.3,
+ "p95": 23.5,
+ "p99": 53.1,
+ "n": 868
+ },
+ "content_shown": {
+ "p50": 17.7,
+ "p95": 25.6,
+ "p99": 57.5,
+ "n": 868
+ }
+ },
+ "fps": 57.2,
+ "fps_shown": 57.2,
+ "late_pct": 3.23,
+ "stall_max": 108.0,
+ "stalls_over_100ms": 1,
+ "mbps": 9.84,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 870,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 24.2,
+ "p95": 36.3,
+ "p99": 36.3,
+ "n": 6
+ },
+ "input_shown_ms": {
+ "p50": 24.8,
+ "p95": 38.5,
+ "p99": 38.5,
+ "n": 6
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 2.0,
+ "p95": 5.4,
+ "p99": 5.4,
+ "n": 6
+ },
+ "mac": {
+ "p50": 7.5,
+ "p95": 15.8,
+ "p99": 15.8,
+ "n": 6
+ },
+ "back": {
+ "p50": 15.3,
+ "p95": 16.1,
+ "p99": 16.1,
+ "n": 6
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 6,
+ "seen": 6
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 53,
+ "mbps": 9.21,
+ "content_p50": 16.1,
+ "content_p95": 71.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 57,
+ "mbps": 9.64,
+ "content_p50": 15.9,
+ "content_p95": 46.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 58,
+ "mbps": 8.99,
+ "content_p50": 14.8,
+ "content_p95": 19.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.72,
+ "content_p50": 15.2,
+ "content_p95": 22.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 9.64,
+ "content_p50": 16.1,
+ "content_p95": 23.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 11.27,
+ "content_p50": 15.6,
+ "content_p95": 20.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 10.44,
+ "content_p50": 15.1,
+ "content_p95": 19.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 9.31,
+ "content_p50": 15.0,
+ "content_p95": 19.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 11.69,
+ "content_p50": 15.9,
+ "content_p95": 22.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 58,
+ "mbps": 10.31,
+ "content_p50": 14.5,
+ "content_p95": 20.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 10.47,
+ "content_p50": 15.2,
+ "content_p95": 20.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 9.44,
+ "content_p50": 15.3,
+ "content_p95": 20.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 57,
+ "mbps": 9.17,
+ "content_p50": 15.9,
+ "content_p95": 18.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 58,
+ "mbps": 8.77,
+ "content_p50": 15.1,
+ "content_p95": 18.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 58,
+ "mbps": 9.72,
+ "content_p50": 15.2,
+ "content_p95": 19.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 9,
+ "mbps": 1.33,
+ "content_p50": 14.8,
+ "content_p95": 17.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 15.3,
+ "content_p95": 23.5,
+ "input_p50": 24.2,
+ "input_p95": 36.3,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 1.375,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 57.2,
+ "cpu": {
+ "mac_agent_pct": 11.4,
+ "frame_viewer_pct": 83.5,
+ "frame_tailscaled_pct": 0.3,
+ "frame_sshd_pct": 1.5,
+ "frame_gamescope_pct": 9.7,
+ "frame_total_pct": 32.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.15,
+ "panel": "valve.steam.desktopgame.2001786096",
+ "src": "separate:8187"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-1d05f57-wifi1.json b/bench/results/2026-09-28-1d05f57-wifi1.json
new file mode 100644
index 0000000..ba818b0
--- /dev/null
+++ b/bench/results/2026-09-28-1d05f57-wifi1.json
@@ -0,0 +1,682 @@
+{
+ "label": "wifi1",
+ "date": "2026-09-28T21:16:56",
+ "commit": "1d05f57",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 912,
+ "frames_drawn": 912,
+ "frames_shown": 611,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 912
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 912
+ },
+ "encode": {
+ "p50": 4.3,
+ "p95": 4.9,
+ "p99": 5.1,
+ "n": 912
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 912
+ },
+ "network": {
+ "p50": 4.0,
+ "p95": 5.3,
+ "p99": 7.8,
+ "n": 912
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.8,
+ "p99": 4.0,
+ "n": 912
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 912
+ },
+ "present": {
+ "p50": 5.9,
+ "p95": 14.0,
+ "p99": 18.5,
+ "n": 611
+ },
+ "content": {
+ "p50": 10.1,
+ "p95": 12.3,
+ "p99": 13.9,
+ "n": 912
+ },
+ "content_shown": {
+ "p50": 16.7,
+ "p95": 23.6,
+ "p99": 28.3,
+ "n": 611
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 40.1,
+ "late_pct": 0.11,
+ "stall_max": 25.8,
+ "stalls_over_100ms": 0,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 912,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 26.4,
+ "p95": 34.2,
+ "p99": 53.5,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 28.6,
+ "p95": 41.5,
+ "p99": 55.9,
+ "n": 18
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 8.9,
+ "p95": 20.4,
+ "p99": 28.0,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.1,
+ "p95": 14.5,
+ "p99": 16.2,
+ "n": 30
+ },
+ "back": {
+ "p50": 10.1,
+ "p95": 13.7,
+ "p99": 13.8,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "asked": 30,
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 10.3,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.5,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.1,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.9,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.7,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 10.2,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.2,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.7,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.4,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.9,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.3,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.2,
+ "content_p95": 12.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.0,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.2,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.1,
+ "content_p50": 10.7,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.1,
+ "content_p95": 12.3,
+ "input_p50": 26.4,
+ "input_p95": 34.2,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 4.958,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 47.417,
+ "target": 2764800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 16.58,
+ "e": "down to 2764 kbit/s: queue 19 ms, held 6, oldest 210 ms, base 4 ms, sent 322 got 265 wants 459"
+ }
+ ],
+ "source_fps": 60.0,
+ "cpu": {
+ "mac_agent_pct": 15.7,
+ "frame_viewer_pct": 42.5,
+ "frame_tailscaled_pct": 5.5,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 8.7,
+ "frame_total_pct": 26.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 136 Hz",
+ "show_s": 1.26,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 852,
+ "frames_drawn": 852,
+ "frames_shown": 614,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.4,
+ "p95": 2.6,
+ "p99": 4.2,
+ "n": 852
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 852
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.8,
+ "p99": 16.7,
+ "n": 852
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 852
+ },
+ "network": {
+ "p50": 5.3,
+ "p95": 13.1,
+ "p99": 28.2,
+ "n": 852
+ },
+ "decode": {
+ "p50": 6.0,
+ "p95": 10.9,
+ "p99": 19.0,
+ "n": 852
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.4,
+ "p99": 1.8,
+ "n": 852
+ },
+ "present": {
+ "p50": 4.1,
+ "p95": 16.2,
+ "p99": 20.6,
+ "n": 614
+ },
+ "content": {
+ "p50": 19.5,
+ "p95": 31.4,
+ "p99": 64.5,
+ "n": 852
+ },
+ "content_shown": {
+ "p50": 25.4,
+ "p95": 38.8,
+ "p99": 76.7,
+ "n": 614
+ }
+ },
+ "fps": 56.2,
+ "fps_shown": 40.5,
+ "late_pct": 4.69,
+ "stall_max": 253.7,
+ "stalls_over_100ms": 2,
+ "mbps": 9.02,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 866,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 45,
+ "mbps": 7.77,
+ "content_p50": 19.0,
+ "content_p95": 163.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 51,
+ "mbps": 6.54,
+ "content_p50": 19.4,
+ "content_p95": 107.0,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 8.64,
+ "content_p50": 19.9,
+ "content_p95": 31.8,
+ "bitrate": 10055362,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 58,
+ "mbps": 9.46,
+ "content_p50": 19.1,
+ "content_p95": 24.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 9.11,
+ "content_p50": 19.4,
+ "content_p95": 22.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 9.18,
+ "content_p50": 19.9,
+ "content_p95": 25.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 11.64,
+ "content_p50": 19.8,
+ "content_p95": 49.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 9.12,
+ "content_p50": 20.5,
+ "content_p95": 31.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.29,
+ "content_p50": 19.5,
+ "content_p95": 25.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.43,
+ "content_p50": 19.8,
+ "content_p95": 49.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 58,
+ "mbps": 8.59,
+ "content_p50": 19.2,
+ "content_p95": 24.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 57,
+ "mbps": 9.42,
+ "content_p50": 19.9,
+ "content_p95": 27.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 58,
+ "mbps": 9.0,
+ "content_p50": 20.6,
+ "content_p95": 31.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 57,
+ "mbps": 8.84,
+ "content_p50": 19.6,
+ "content_p95": 25.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.17,
+ "content_p50": 19.3,
+ "content_p95": 27.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 9,
+ "mbps": 1.52,
+ "content_p50": 19.3,
+ "content_p95": 30.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.5,
+ "content_p95": 31.4,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.959,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 43.54,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 1.09,
+ "e": "down to 7430 kbit/s: queue 131 ms, held 3, oldest 231 ms, base 6 ms, sent 4272 got 3724 wants 9321"
+ }
+ ],
+ "source_fps": 57.0,
+ "cpu": {
+ "mac_agent_pct": 8.5,
+ "frame_viewer_pct": 71.7,
+ "frame_tailscaled_pct": 8.4,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 8.4,
+ "frame_total_pct": 30.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.25,
+ "panel": "valve.steam.desktopgame.2001195625",
+ "src": "separate:8050"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-1d05f57-wifi2.json b/bench/results/2026-09-28-1d05f57-wifi2.json
new file mode 100644
index 0000000..00f9504
--- /dev/null
+++ b/bench/results/2026-09-28-1d05f57-wifi2.json
@@ -0,0 +1,682 @@
+{
+ "label": "wifi2",
+ "date": "2026-09-28T21:18:40",
+ "commit": "1d05f57",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 902,
+ "frames_drawn": 902,
+ "frames_shown": 604,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 902
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 902
+ },
+ "encode": {
+ "p50": 4.3,
+ "p95": 5.0,
+ "p99": 5.3,
+ "n": 902
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 902
+ },
+ "network": {
+ "p50": 3.8,
+ "p95": 5.3,
+ "p99": 8.0,
+ "n": 902
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.8,
+ "p99": 3.8,
+ "n": 902
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 902
+ },
+ "present": {
+ "p50": 5.9,
+ "p95": 17.0,
+ "p99": 18.6,
+ "n": 604
+ },
+ "content": {
+ "p50": 9.8,
+ "p95": 12.1,
+ "p99": 14.4,
+ "n": 902
+ },
+ "content_shown": {
+ "p50": 15.2,
+ "p95": 27.0,
+ "p99": 28.4,
+ "n": 604
+ }
+ },
+ "fps": 59.3,
+ "fps_shown": 39.7,
+ "late_pct": 0.55,
+ "stall_max": 187.5,
+ "stalls_over_100ms": 1,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 913,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 27.8,
+ "p95": 34.9,
+ "p99": 206.9,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 35.1,
+ "p95": 52.6,
+ "p99": 207.2,
+ "n": 24
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 9.8,
+ "p95": 22.5,
+ "p99": 184.8,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.3,
+ "p95": 14.4,
+ "p99": 19.6,
+ "n": 30
+ },
+ "back": {
+ "p50": 9.8,
+ "p95": 13.4,
+ "p99": 13.9,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "asked": 30,
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.3,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 50,
+ "mbps": 0.39,
+ "content_p50": 9.1,
+ "content_p95": 14.4,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.2,
+ "content_p95": 11.8,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.4,
+ "content_p95": 10.5,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.5,
+ "content_p95": 12.5,
+ "bitrate": 3091280,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.0,
+ "content_p95": 12.8,
+ "bitrate": 4757991,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 9.5,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.2,
+ "content_p95": 12.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.0,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.2,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.9,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.0,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.3,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.8,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.1,
+ "content_p50": 9.3,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.8,
+ "content_p95": 12.1,
+ "input_p50": 27.8,
+ "input_p95": 34.9,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.084,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 49.603,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 1.77,
+ "e": "down to 2764 kbit/s: queue 23 ms, held 5, oldest 0 ms, base 5 ms, sent 315 got 315 wants 472"
+ }
+ ],
+ "source_fps": 60.1,
+ "cpu": {
+ "mac_agent_pct": 16.1,
+ "frame_viewer_pct": 40.9,
+ "frame_tailscaled_pct": 5.2,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 8.4,
+ "frame_total_pct": 25.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 136 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 832,
+ "frames_drawn": 832,
+ "frames_shown": 602,
+ "duration_s": 15.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.9,
+ "p95": 0.8,
+ "p99": 6.8,
+ "n": 832
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 1.9,
+ "n": 832
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 10.5,
+ "p99": 18.8,
+ "n": 832
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 832
+ },
+ "network": {
+ "p50": 5.8,
+ "p95": 18.7,
+ "p99": 56.7,
+ "n": 832
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 11.7,
+ "p99": 26.0,
+ "n": 832
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.4,
+ "p99": 1.7,
+ "n": 832
+ },
+ "present": {
+ "p50": 4.4,
+ "p95": 18.5,
+ "p99": 25.0,
+ "n": 602
+ },
+ "content": {
+ "p50": 20.2,
+ "p95": 36.9,
+ "p99": 90.7,
+ "n": 832
+ },
+ "content_shown": {
+ "p50": 26.4,
+ "p95": 45.4,
+ "p99": 101.0,
+ "n": 602
+ }
+ },
+ "fps": 54.9,
+ "fps_shown": 39.7,
+ "late_pct": 6.96,
+ "stall_max": 204.9,
+ "stalls_over_100ms": 2,
+ "mbps": 9.04,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 852,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 49,
+ "mbps": 8.68,
+ "content_p50": 19.8,
+ "content_p95": 119.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 47,
+ "mbps": 6.06,
+ "content_p50": 19.8,
+ "content_p95": 88.6,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 52,
+ "mbps": 8.07,
+ "content_p50": 19.4,
+ "content_p95": 25.5,
+ "bitrate": 10055362,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 56,
+ "mbps": 9.79,
+ "content_p50": 21.4,
+ "content_p95": 29.1,
+ "bitrate": 13549185,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 8.93,
+ "content_p50": 21.5,
+ "content_p95": 35.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 10.59,
+ "content_p50": 21.1,
+ "content_p95": 35.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 56,
+ "mbps": 11.55,
+ "content_p50": 19.7,
+ "content_p95": 66.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.8,
+ "content_p50": 18.8,
+ "content_p95": 24.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 57,
+ "mbps": 9.17,
+ "content_p50": 19.5,
+ "content_p95": 24.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.39,
+ "content_p50": 19.4,
+ "content_p95": 23.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 8.53,
+ "content_p50": 19.5,
+ "content_p95": 38.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 56,
+ "mbps": 9.23,
+ "content_p50": 20.7,
+ "content_p95": 30.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 53,
+ "mbps": 8.87,
+ "content_p50": 20.6,
+ "content_p95": 35.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 57,
+ "mbps": 8.94,
+ "content_p50": 20.4,
+ "content_p95": 28.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.14,
+ "content_p50": 21.0,
+ "content_p95": 35.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 7,
+ "mbps": 1.2,
+ "content_p50": 21.8,
+ "content_p95": 23.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 20.2,
+ "content_p95": 36.9,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.833,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 44.791,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 1.22,
+ "e": "down to 7430 kbit/s: queue 74 ms, held 7, oldest 235 ms, base 6 ms, sent 5436 got 4487 wants 11082"
+ }
+ ],
+ "source_fps": 56.4,
+ "cpu": {
+ "mac_agent_pct": 8.8,
+ "frame_viewer_pct": 69.5,
+ "frame_tailscaled_pct": 9.5,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 8.4,
+ "frame_total_pct": 31.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.25,
+ "panel": "valve.steam.desktopgame.2001116820",
+ "src": "separate:8327"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-39afb23-worn.json b/bench/results/2026-09-28-39afb23-worn.json
new file mode 100644
index 0000000..ee0abec
--- /dev/null
+++ b/bench/results/2026-09-28-39afb23-worn.json
@@ -0,0 +1,1180 @@
+{
+ "label": "worn",
+ "date": "2026-09-28T22:01:33",
+ "commit": "39afb23",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "usb": false,
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 1177,
+ "frames_drawn": 1177,
+ "frames_shown": 795,
+ "duration_s": 20.3,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1177
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1177
+ },
+ "encode": {
+ "p50": 4.4,
+ "p95": 5.3,
+ "p99": 5.7,
+ "n": 1177
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 1177
+ },
+ "network": {
+ "p50": 4.5,
+ "p95": 8.4,
+ "p99": 15.1,
+ "n": 1177
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.5,
+ "p99": 4.0,
+ "n": 1177
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.8,
+ "p99": 1.4,
+ "n": 1177
+ },
+ "present": {
+ "p50": 5.3,
+ "p95": 16.2,
+ "p99": 19.4,
+ "n": 795
+ },
+ "content": {
+ "p50": 10.8,
+ "p95": 15.3,
+ "p99": 24.4,
+ "n": 1177
+ },
+ "content_shown": {
+ "p50": 17.0,
+ "p95": 28.9,
+ "p99": 32.7,
+ "n": 795
+ }
+ },
+ "fps": 58.0,
+ "fps_shown": 39.2,
+ "late_pct": 1.02,
+ "stall_max": 262.4,
+ "stalls_over_100ms": 4,
+ "mbps": 0.45,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1217,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 25.7,
+ "p95": 34.4,
+ "p99": 47.3,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 29.8,
+ "p95": 44.3,
+ "p99": 47.7,
+ "n": 23
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 7.4,
+ "p95": 16.7,
+ "p99": 18.1,
+ "n": 40
+ },
+ "mac": {
+ "p50": 6.2,
+ "p95": 16.0,
+ "p99": 19.2,
+ "n": 40
+ },
+ "back": {
+ "p50": 11.4,
+ "p95": 15.3,
+ "p99": 22.9,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "asked": 40,
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.4,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.1,
+ "content_p95": 10.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.2,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.1,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.1,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.2,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.56,
+ "content_p50": 10.3,
+ "content_p95": 13.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 10.2,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 50,
+ "mbps": 0.38,
+ "content_p50": 10.4,
+ "content_p95": 136.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.9,
+ "content_p95": 12.8,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.9,
+ "content_p95": 12.7,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 53,
+ "mbps": 0.39,
+ "content_p50": 10.2,
+ "content_p95": 16.6,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 55,
+ "mbps": 0.41,
+ "content_p50": 10.9,
+ "content_p95": 16.1,
+ "bitrate": 1382400,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 12.6,
+ "content_p95": 15.7,
+ "bitrate": 1382400,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.42,
+ "content_p50": 12.8,
+ "content_p95": 15.4,
+ "bitrate": 1382400,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 12.7,
+ "content_p95": 15.3,
+ "bitrate": 2256021,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 13.0,
+ "content_p95": 16.4,
+ "bitrate": 3168263,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 57,
+ "mbps": 0.43,
+ "content_p50": 12.8,
+ "content_p95": 16.7,
+ "bitrate": 4382456,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 45,
+ "mbps": 0.35,
+ "content_p50": 13.0,
+ "content_p95": 15.9,
+ "bitrate": 2435350,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 13.4,
+ "content_p95": 15.4,
+ "bitrate": 2435350,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 17,
+ "mbps": 0.12,
+ "content_p50": 13.7,
+ "content_p95": 16.1,
+ "bitrate": 2435350,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.8,
+ "content_p95": 15.3,
+ "input_p50": 25.7,
+ "input_p95": 34.4,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.542,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 50.978,
+ "target": 3797645,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 9.07,
+ "e": "down to 2764 kbit/s: queue 14 ms, held 3, oldest 0 ms, base 5 ms, sent 307 got 322 wants 452"
+ },
+ {
+ "t": 12.07,
+ "e": "down to 1382 kbit/s: queue 18 ms, held 6, oldest 242 ms, base 5 ms, sent 295 got 268 wants 466"
+ },
+ {
+ "t": 18.17,
+ "e": "down to 2435 kbit/s: queue 20 ms, held 6, oldest 274 ms, base 5 ms, sent 256 got 221 wants 452"
+ }
+ ],
+ "source_fps": 60.0,
+ "cpu": {
+ "mac_agent_pct": 16.9,
+ "frame_viewer_pct": 38.7,
+ "frame_tailscaled_pct": 5.0,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 6.8,
+ "frame_total_pct": 24.9
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 1.99,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test",
+ "route": "network"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 762,
+ "frames_drawn": 762,
+ "frames_shown": 651,
+ "duration_s": 20.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.6,
+ "p95": 0.8,
+ "p99": 11.0,
+ "n": 762
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 16.2,
+ "p99": 17.3,
+ "n": 762
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 8.5,
+ "p99": 14.2,
+ "n": 762
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 762
+ },
+ "network": {
+ "p50": 5.2,
+ "p95": 115.6,
+ "p99": 159.6,
+ "n": 762
+ },
+ "decode": {
+ "p50": 5.5,
+ "p95": 18.4,
+ "p99": 27.7,
+ "n": 762
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.4,
+ "p99": 1.9,
+ "n": 762
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 12.3,
+ "p99": 23.8,
+ "n": 651
+ },
+ "content": {
+ "p50": 22.2,
+ "p95": 141.0,
+ "p99": 175.6,
+ "n": 762
+ },
+ "content_shown": {
+ "p50": 24.6,
+ "p95": 101.5,
+ "p99": 177.7,
+ "n": 651
+ }
+ },
+ "fps": 37.7,
+ "fps_shown": 32.2,
+ "late_pct": 42.26,
+ "stall_max": 332.6,
+ "stalls_over_100ms": 18,
+ "mbps": 4.94,
+ "keyframes": 4,
+ "size": "1920x1290",
+ "captured": 1097,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 55,
+ "mbps": 9.56,
+ "content_p50": 19.6,
+ "content_p95": 26.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 56,
+ "mbps": 8.8,
+ "content_p50": 18.9,
+ "content_p95": 25.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 52,
+ "mbps": 8.96,
+ "content_p50": 18.6,
+ "content_p95": 34.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.35,
+ "content_p50": 18.6,
+ "content_p95": 24.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.8,
+ "content_p50": 19.6,
+ "content_p95": 28.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 49,
+ "mbps": 10.39,
+ "content_p50": 31.3,
+ "content_p95": 135.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 48,
+ "mbps": 8.29,
+ "content_p50": 53.6,
+ "content_p95": 175.6,
+ "bitrate": 8168400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 33,
+ "mbps": 2.07,
+ "content_p50": 109.8,
+ "content_p95": 172.8,
+ "bitrate": 2613888,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 35,
+ "mbps": 1.17,
+ "content_p50": 111.0,
+ "content_p95": 177.0,
+ "bitrate": 1094296,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 22,
+ "mbps": 0.44,
+ "content_p50": 79.5,
+ "content_p95": 169.5,
+ "bitrate": 875436,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 31,
+ "mbps": 0.56,
+ "content_p50": 20.5,
+ "content_p95": 35.1,
+ "bitrate": 741574,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 21,
+ "mbps": 0.57,
+ "content_p50": 27.1,
+ "content_p95": 42.5,
+ "bitrate": 1002304,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 30,
+ "mbps": 0.78,
+ "content_p50": 22.2,
+ "content_p95": 29.3,
+ "bitrate": 801843,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 30,
+ "mbps": 1.01,
+ "content_p50": 24.2,
+ "content_p95": 87.7,
+ "bitrate": 1232751,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 30,
+ "mbps": 1.51,
+ "content_p50": 21.1,
+ "content_p95": 38.3,
+ "bitrate": 1806290,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 30,
+ "mbps": 2.34,
+ "content_p50": 21.7,
+ "content_p95": 41.9,
+ "bitrate": 2876638,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 30,
+ "mbps": 3.57,
+ "content_p50": 25.4,
+ "content_p95": 51.7,
+ "bitrate": 3994304,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 30,
+ "mbps": 4.67,
+ "content_p50": 31.1,
+ "content_p95": 54.3,
+ "bitrate": 5481917,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 30,
+ "mbps": 6.77,
+ "content_p50": 30.3,
+ "content_p95": 48.0,
+ "bitrate": 8258121,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 30,
+ "mbps": 7.43,
+ "content_p50": 35.4,
+ "content_p95": 43.0,
+ "bitrate": 11157058,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 6,
+ "mbps": 1.63,
+ "content_p50": 33.6,
+ "content_p95": 41.1,
+ "bitrate": 12322763,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 22.2,
+ "content_p95": 141.0,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "bad",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.792,
+ "ceiling": 14860800,
+ "fps": 45,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 61.471,
+ "target": 14860800,
+ "tier": 1
+ },
+ "events": [
+ {
+ "t": 6.13,
+ "e": "down to 11888 kbit/s: queue 56 ms, held 0, oldest 19 ms, base 6 ms, sent 13722 got 14074 wants 14249"
+ },
+ {
+ "t": 6.73,
+ "e": "down to 8168 kbit/s: queue 72 ms, held 5, oldest 0 ms, base 6 ms, sent 8768 got 9076 wants 9996"
+ },
+ {
+ "t": 7.03,
+ "e": "down to 4084 kbit/s: queue 25 ms, held 5, oldest 221 ms, base 6 ms, sent 5652 got 4017 wants 8949"
+ },
+ {
+ "t": 7.33,
+ "e": "down to 3267 kbit/s: queue 364 ms, held 1, oldest 28 ms, base 6 ms, sent 3634 got 4790 wants 12946"
+ },
+ {
+ "t": 7.73,
+ "e": "down to 2613 kbit/s: queue 90 ms, held 1, oldest 127 ms, base 6 ms, sent 3380 got 4111 wants 4188"
+ },
+ {
+ "t": 8.13,
+ "e": "down to 1367 kbit/s: queue 87 ms, held 3, oldest 8 ms, base 6 ms, sent 1478 got 1519 wants 2178"
+ },
+ {
+ "t": 8.53,
+ "e": "down to 1094 kbit/s: queue 122 ms, held 0, oldest 111 ms, base 6 ms, sent 1010 got 1247 wants 1347"
+ },
+ {
+ "t": 8.73,
+ "e": "tier 4"
+ },
+ {
+ "t": 9.13,
+ "e": "down to 875 kbit/s: queue 147 ms, held 2, oldest 63 ms, base 5 ms, sent 1121 got 1101 wants 1529"
+ },
+ {
+ "t": 9.73,
+ "e": "down to 875 kbit/s: queue 50 ms, held 1, oldest 1 ms, base 5 ms, sent 494 got 458 wants 529"
+ },
+ {
+ "t": 10.03,
+ "e": "down to 628 kbit/s: queue 72 ms, held 1, oldest 0 ms, base 5 ms, sent 663 got 698 wants 765"
+ },
+ {
+ "t": 12.13,
+ "e": "down to 801 kbit/s: queue 42 ms, held 0, oldest 0 ms, base 5 ms, sent 918 got 918 wants 918"
+ },
+ {
+ "t": 16.43,
+ "e": "tier 3"
+ },
+ {
+ "t": 18.63,
+ "e": "tier 2"
+ },
+ {
+ "t": 20.73,
+ "e": "tier 1"
+ }
+ ],
+ "source_fps": 54.3,
+ "cpu": {
+ "mac_agent_pct": 6.8,
+ "frame_viewer_pct": 45.0,
+ "frame_tailscaled_pct": 7.2,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 5.6,
+ "frame_total_pct": 26.9
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.3,
+ "panel": "valve.steam.desktopgame.2001614738",
+ "src": "separate:8635",
+ "route": "network"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 242,
+ "frames_drawn": 242,
+ "frames_shown": 242,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -5.7,
+ "p95": 1.4,
+ "p99": 6.9,
+ "n": 242
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 242
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 8.3,
+ "p99": 16.3,
+ "n": 242
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 242
+ },
+ "network": {
+ "p50": 3.3,
+ "p95": 8.8,
+ "p99": 18.8,
+ "n": 242
+ },
+ "decode": {
+ "p50": 2.1,
+ "p95": 4.5,
+ "p99": 6.6,
+ "n": 242
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.4,
+ "p99": 2.0,
+ "n": 242
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 5.1,
+ "p99": 6.8,
+ "n": 242
+ },
+ "content": {
+ "p50": 13.5,
+ "p95": 24.3,
+ "p99": 31.1,
+ "n": 242
+ },
+ "content_shown": {
+ "p50": 15.0,
+ "p95": 25.1,
+ "p99": 32.2,
+ "n": 242
+ }
+ },
+ "fps": 11.5,
+ "fps_shown": 11.5,
+ "late_pct": 37.45,
+ "stall_max": 6722.9,
+ "stalls_over_100ms": 50,
+ "mbps": 0.07,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 242,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 56.8,
+ "p95": 106.3,
+ "p99": 120.5,
+ "n": 62
+ },
+ "input_shown_ms": {
+ "p50": 57.3,
+ "p95": 107.1,
+ "p99": 121.5,
+ "n": 62
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 6.7,
+ "p95": 36.8,
+ "p99": 38.6,
+ "n": 62
+ },
+ "mac": {
+ "p50": 30.7,
+ "p95": 66.7,
+ "p99": 93.7,
+ "n": 62
+ },
+ "back": {
+ "p50": 13.8,
+ "p95": 25.5,
+ "p99": 29.5,
+ "n": 62
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 62
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 31,
+ "mbps": 0.26,
+ "content_p50": 14.1,
+ "content_p95": 17.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 22,
+ "mbps": 0.09,
+ "content_p50": 12.6,
+ "content_p95": 16.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 14,
+ "mbps": 0.06,
+ "content_p50": 13.4,
+ "content_p95": 16.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.2,
+ "content_p95": 16.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 8,
+ "mbps": 0.03,
+ "content_p50": 13.8,
+ "content_p95": 17.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 12,
+ "mbps": 0.08,
+ "content_p50": 13.6,
+ "content_p95": 16.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 10,
+ "mbps": 0.05,
+ "content_p50": 12.9,
+ "content_p95": 17.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 12,
+ "mbps": 0.06,
+ "content_p50": 13.9,
+ "content_p95": 19.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 32,
+ "mbps": 0.21,
+ "content_p50": 13.5,
+ "content_p95": 17.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 51,
+ "mbps": 0.24,
+ "content_p50": 12.9,
+ "content_p95": 26.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 19,
+ "mbps": 0.12,
+ "content_p50": 15.5,
+ "content_p95": 29.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 9,
+ "mbps": 0.05,
+ "content_p50": 13.0,
+ "content_p95": 14.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 8,
+ "mbps": 0.06,
+ "content_p50": 15.1,
+ "content_p95": 18.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 6,
+ "mbps": 0.12,
+ "content_p50": 15.6,
+ "content_p95": 20.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 16.9,
+ "content_p95": 28.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 13.5,
+ "content_p95": 24.3,
+ "input_p50": 56.8,
+ "input_p95": 106.3,
+ "grades": {
+ "input_replies": "acceptable",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "acceptable",
+ "input_p95": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.958,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 11.5,
+ "cpu": {
+ "mac_agent_pct": 2.2,
+ "frame_viewer_pct": 11.7,
+ "frame_tailscaled_pct": 1.9,
+ "frame_sshd_pct": 0.0,
+ "frame_gamescope_pct": 3.5,
+ "frame_total_pct": 17.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.35,
+ "panel": "valve.steam.desktopgame.2001525486",
+ "src": "separate:8785",
+ "route": "network"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-9e4dcdd-final.json b/bench/results/2026-09-28-9e4dcdd-final.json
new file mode 100644
index 0000000..f0f657f
--- /dev/null
+++ b/bench/results/2026-09-28-9e4dcdd-final.json
@@ -0,0 +1,1173 @@
+{
+ "label": "final",
+ "date": "2026-09-28T21:02:06",
+ "commit": "9e4dcdd",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 1216,
+ "frames_drawn": 1216,
+ "frames_shown": 640,
+ "duration_s": 20.3,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1216
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1216
+ },
+ "encode": {
+ "p50": 4.5,
+ "p95": 5.2,
+ "p99": 5.6,
+ "n": 1216
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 1216
+ },
+ "network": {
+ "p50": 4.3,
+ "p95": 8.9,
+ "p99": 11.3,
+ "n": 1216
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.8,
+ "p99": 3.6,
+ "n": 1216
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.8,
+ "p99": 2.0,
+ "n": 1216
+ },
+ "present": {
+ "p50": 4.6,
+ "p95": 14.9,
+ "p99": 19.7,
+ "n": 640
+ },
+ "content": {
+ "p50": 10.5,
+ "p95": 16.7,
+ "p99": 18.9,
+ "n": 1216
+ },
+ "content_shown": {
+ "p50": 15.1,
+ "p95": 26.4,
+ "p99": 32.6,
+ "n": 640
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 31.6,
+ "late_pct": 0.66,
+ "stall_max": 66.0,
+ "stalls_over_100ms": 0,
+ "mbps": 0.47,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1216,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 29.6,
+ "p95": 36.3,
+ "p99": 53.4,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 32.5,
+ "p95": 46.1,
+ "p99": 65.7,
+ "n": 21
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 10.5,
+ "p95": 20.3,
+ "p99": 24.9,
+ "n": 40
+ },
+ "mac": {
+ "p50": 6.5,
+ "p95": 16.4,
+ "p99": 19.2,
+ "n": 40
+ },
+ "back": {
+ "p50": 11.9,
+ "p95": 16.4,
+ "p99": 17.1,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "asked": 40,
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.5,
+ "content_p95": 10.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.2,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.4,
+ "content_p95": 10.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.7,
+ "content_p95": 24.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.4,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 9.5,
+ "content_p95": 11.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.5,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.0,
+ "content_p95": 10.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.5,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.9,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.3,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.3,
+ "content_p95": 14.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 13.9,
+ "content_p95": 16.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 14.2,
+ "content_p95": 16.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 16.6,
+ "content_p95": 18.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.55,
+ "content_p50": 14.7,
+ "content_p95": 17.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.8,
+ "content_p95": 18.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 14.3,
+ "content_p95": 17.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 13.8,
+ "content_p95": 16.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 16,
+ "mbps": 0.13,
+ "content_p50": 15.0,
+ "content_p95": 16.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.5,
+ "content_p95": 16.7,
+ "input_p50": 29.6,
+ "input_p95": 36.3,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.625,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 49.79,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 59.9,
+ "cpu": {
+ "mac_agent_pct": 17.1,
+ "frame_viewer_pct": 38.5,
+ "frame_tailscaled_pct": 4.4,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 12.3,
+ "frame_total_pct": 52.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 101 Hz",
+ "show_s": 1.85,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 1130,
+ "frames_drawn": 1130,
+ "frames_shown": 800,
+ "duration_s": 20.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.4,
+ "p95": 3.0,
+ "p99": 4.6,
+ "n": 1130
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1130
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 10.5,
+ "p99": 18.9,
+ "n": 1130
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 1130
+ },
+ "network": {
+ "p50": 5.5,
+ "p95": 8.6,
+ "p99": 18.9,
+ "n": 1130
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 9.9,
+ "p99": 13.8,
+ "n": 1130
+ },
+ "draw": {
+ "p50": 0.8,
+ "p95": 1.5,
+ "p99": 2.2,
+ "n": 1130
+ },
+ "present": {
+ "p50": 4.8,
+ "p95": 15.8,
+ "p99": 21.3,
+ "n": 800
+ },
+ "content": {
+ "p50": 19.8,
+ "p95": 27.4,
+ "p99": 46.3,
+ "n": 1130
+ },
+ "content_shown": {
+ "p50": 25.3,
+ "p95": 37.8,
+ "p99": 50.1,
+ "n": 800
+ }
+ },
+ "fps": 55.9,
+ "fps_shown": 39.6,
+ "late_pct": 4.78,
+ "stall_max": 222.5,
+ "stalls_over_100ms": 2,
+ "mbps": 9.15,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 1151,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "asked": 0,
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 44,
+ "mbps": 7.88,
+ "content_p50": 19.9,
+ "content_p95": 48.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 46,
+ "mbps": 5.9,
+ "content_p50": 18.1,
+ "content_p95": 104.3,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 58,
+ "mbps": 8.45,
+ "content_p50": 19.6,
+ "content_p95": 31.6,
+ "bitrate": 10055362,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 58,
+ "mbps": 9.54,
+ "content_p50": 19.0,
+ "content_p95": 24.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.33,
+ "content_p50": 19.2,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 55,
+ "mbps": 10.11,
+ "content_p50": 19.3,
+ "content_p95": 26.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 57,
+ "mbps": 11.68,
+ "content_p50": 21.0,
+ "content_p95": 46.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.76,
+ "content_p50": 20.6,
+ "content_p95": 29.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.21,
+ "content_p50": 18.9,
+ "content_p95": 25.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.4,
+ "content_p50": 19.5,
+ "content_p95": 26.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 8.51,
+ "content_p50": 19.4,
+ "content_p95": 24.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 9.46,
+ "content_p50": 19.3,
+ "content_p95": 24.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 57,
+ "mbps": 8.96,
+ "content_p50": 20.2,
+ "content_p95": 25.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 57,
+ "mbps": 8.85,
+ "content_p50": 20.4,
+ "content_p95": 26.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.16,
+ "content_p50": 19.6,
+ "content_p95": 26.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 57,
+ "mbps": 8.81,
+ "content_p50": 19.3,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 58,
+ "mbps": 11.7,
+ "content_p50": 21.1,
+ "content_p95": 37.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 57,
+ "mbps": 9.27,
+ "content_p50": 20.2,
+ "content_p95": 24.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 57,
+ "mbps": 8.77,
+ "content_p50": 20.0,
+ "content_p95": 27.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 57,
+ "mbps": 9.16,
+ "content_p50": 20.2,
+ "content_p95": 26.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 11,
+ "mbps": 1.8,
+ "content_p50": 19.9,
+ "content_p95": 23.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.8,
+ "content_p95": 27.4,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.833,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 44.915,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 1.03,
+ "e": "down to 7430 kbit/s: queue 149 ms, held 5, oldest 79 ms, base 6 ms, sent 5371 got 5599 wants 11155"
+ }
+ ],
+ "source_fps": 57.0,
+ "cpu": {
+ "mac_agent_pct": 9.0,
+ "frame_viewer_pct": 75.3,
+ "frame_tailscaled_pct": 9.9,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 10.6,
+ "frame_total_pct": 43.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 31 Hz",
+ "show_s": 1.31,
+ "panel": "valve.steam.desktopgame.2001906569",
+ "src": "separate:7610"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 96,
+ "frames_drawn": 96,
+ "frames_shown": 96,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.3,
+ "p95": 5.5,
+ "p99": 6.2,
+ "n": 96
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 96
+ },
+ "encode": {
+ "p50": 6.9,
+ "p95": 10.7,
+ "p99": 15.1,
+ "n": 96
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 96
+ },
+ "network": {
+ "p50": 4.1,
+ "p95": 6.3,
+ "p99": 8.5,
+ "n": 96
+ },
+ "decode": {
+ "p50": 2.2,
+ "p95": 5.0,
+ "p99": 6.3,
+ "n": 96
+ },
+ "draw": {
+ "p50": 0.8,
+ "p95": 1.4,
+ "p99": 1.9,
+ "n": 96
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 1.2,
+ "p99": 4.5,
+ "n": 96
+ },
+ "content": {
+ "p50": 15.0,
+ "p95": 21.4,
+ "p99": 30.7,
+ "n": 96
+ },
+ "content_shown": {
+ "p50": 15.8,
+ "p95": 22.2,
+ "p99": 33.0,
+ "n": 96
+ }
+ },
+ "fps": 4.6,
+ "fps_shown": 4.6,
+ "late_pct": 82.47,
+ "stall_max": 1002.0,
+ "stalls_over_100ms": 74,
+ "mbps": 0.01,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 96,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 43.0,
+ "p95": 60.5,
+ "p99": 80.0,
+ "n": 66
+ },
+ "input_shown_ms": {
+ "p50": 43.8,
+ "p95": 61.5,
+ "p99": 80.7,
+ "n": 66
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 4.3,
+ "p95": 6.9,
+ "p99": 32.6,
+ "n": 66
+ },
+ "mac": {
+ "p50": 23.5,
+ "p95": 34.8,
+ "p99": 39.6,
+ "n": 66
+ },
+ "back": {
+ "p50": 15.0,
+ "p95": 20.7,
+ "p99": 21.9,
+ "n": 66
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 66
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 17,
+ "mbps": 0.04,
+ "content_p50": 13.7,
+ "content_p95": 19.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 9,
+ "mbps": 0.04,
+ "content_p50": 15.5,
+ "content_p95": 41.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 7,
+ "mbps": 0.02,
+ "content_p50": 14.3,
+ "content_p95": 18.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.3,
+ "content_p95": 21.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.4,
+ "content_p95": 16.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.7,
+ "content_p95": 20.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 17.8,
+ "content_p95": 20.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.5,
+ "content_p95": 17.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.6,
+ "content_p95": 16.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.5,
+ "content_p95": 29.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.8,
+ "content_p95": 18.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.7,
+ "content_p95": 18.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 13.6,
+ "content_p95": 19.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 17.8,
+ "content_p95": 17.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 19.8,
+ "content_p95": 20.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 15.7,
+ "content_p95": 15.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 13.6,
+ "content_p95": 19.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 15.3,
+ "content_p95": 30.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 17.4,
+ "content_p95": 19.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 2,
+ "mbps": 0.0,
+ "content_p50": 11.6,
+ "content_p95": 15.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.8,
+ "content_p95": 14.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 15.0,
+ "content_p95": 21.4,
+ "input_p50": 43.0,
+ "input_p95": 60.5,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.75,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 4.5,
+ "cpu": {
+ "mac_agent_pct": 1.4,
+ "frame_viewer_pct": 5.3,
+ "frame_tailscaled_pct": 1.5,
+ "frame_sshd_pct": 0.1,
+ "frame_gamescope_pct": 5.9,
+ "frame_total_pct": 28.4
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.3,
+ "panel": "valve.steam.desktopgame.2001175832",
+ "src": "separate:7747"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean.json b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean.json
new file mode 100644
index 0000000..0b1f835
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean.json
@@ -0,0 +1,4211 @@
+{
+ "label": "ab-adapt-clean",
+ "date": "2026-09-28T17:50:22",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": ":35.258683 [Info] - 0 - entering standby",
+ "scenarios": [],
+ "arms": [
+ "off:FRAME_MAC_VIEW_ADAPT=0",
+ "on:"
+ ],
+ "runs": {
+ "off": [
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 944,
+ "frames_drawn": 944,
+ "frames_shown": 648,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 944
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 944
+ },
+ "encode": {
+ "p50": 4.3,
+ "p95": 5.0,
+ "p99": 5.2,
+ "n": 944
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 944
+ },
+ "network": {
+ "p50": 5.6,
+ "p95": 9.6,
+ "p99": 12.9,
+ "n": 944
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 1.7,
+ "p99": 4.2,
+ "n": 944
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.5,
+ "p99": 0.9,
+ "n": 944
+ },
+ "present": {
+ "p50": 4.5,
+ "p95": 15.8,
+ "p99": 18.4,
+ "n": 648
+ },
+ "content": {
+ "p50": 11.5,
+ "p95": 15.8,
+ "p99": 19.3,
+ "n": 944
+ },
+ "content_shown": {
+ "p50": 17.0,
+ "p95": 28.4,
+ "p99": 31.5,
+ "n": 648
+ }
+ },
+ "fps": 60.1,
+ "fps_shown": 41.2,
+ "late_pct": 0.95,
+ "stall_max": 68.8,
+ "stalls_over_100ms": 0,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1034,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 28.8,
+ "p95": 38.1,
+ "p99": 45.1,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 30.2,
+ "p95": 45.6,
+ "p99": 50.1,
+ "n": 19
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 7.1,
+ "p95": 21.5,
+ "p99": 23.6,
+ "n": 30
+ },
+ "mac": {
+ "p50": 4.2,
+ "p95": 15.1,
+ "p99": 15.8,
+ "n": 30
+ },
+ "back": {
+ "p50": 12.2,
+ "p95": 16.3,
+ "p99": 16.9,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 10.9,
+ "content_p95": 16.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.2,
+ "content_p95": 15.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 10.2,
+ "content_p95": 13.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 10.3,
+ "content_p95": 15.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 10.4,
+ "content_p95": 14.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.7,
+ "content_p95": 14.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 10.7,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.1,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.4,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.6,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 13.8,
+ "content_p95": 16.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 14.3,
+ "content_p95": 18.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 13.7,
+ "content_p95": 15.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.1,
+ "content_p95": 17.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.1,
+ "content_p95": 18.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 44,
+ "mbps": 0.32,
+ "content_p50": 12.1,
+ "content_p95": 13.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 11.5,
+ "content_p95": 15.8,
+ "input_p50": 28.8,
+ "input_p95": 38.1,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.833,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.9,
+ "cpu": {
+ "mac_agent_pct": 16.1,
+ "frame_viewer_pct": 66.4,
+ "frame_tailscaled_pct": 5.8,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 28.1,
+ "frame_total_pct": 31.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 1.52,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 742,
+ "frames_drawn": 742,
+ "frames_shown": 603,
+ "duration_s": 15.8,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.8,
+ "p95": 12.1,
+ "p99": 22.3,
+ "n": 742
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 742
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.0,
+ "p99": 16.4,
+ "n": 742
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 742
+ },
+ "network": {
+ "p50": 8.1,
+ "p95": 24.9,
+ "p99": 61.3,
+ "n": 742
+ },
+ "decode": {
+ "p50": 6.0,
+ "p95": 12.4,
+ "p99": 26.2,
+ "n": 742
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.2,
+ "p99": 1.9,
+ "n": 742
+ },
+ "present": {
+ "p50": 2.1,
+ "p95": 19.6,
+ "p99": 24.7,
+ "n": 603
+ },
+ "content": {
+ "p50": 19.5,
+ "p95": 44.4,
+ "p99": 118.6,
+ "n": 742
+ },
+ "content_shown": {
+ "p50": 25.0,
+ "p95": 51.3,
+ "p99": 125.2,
+ "n": 603
+ }
+ },
+ "fps": 47.1,
+ "fps_shown": 38.3,
+ "late_pct": 20.92,
+ "stall_max": 128.0,
+ "stalls_over_100ms": 2,
+ "mbps": 9.15,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 815,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 40,
+ "mbps": 9.15,
+ "content_p50": 28.7,
+ "content_p95": 139.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 36,
+ "mbps": 7.35,
+ "content_p50": 23.6,
+ "content_p95": 37.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 44,
+ "mbps": 9.92,
+ "content_p50": 21.9,
+ "content_p95": 39.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 53,
+ "mbps": 9.98,
+ "content_p50": 20.4,
+ "content_p95": 37.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 45,
+ "mbps": 9.58,
+ "content_p50": 20.2,
+ "content_p95": 41.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 53,
+ "mbps": 10.27,
+ "content_p50": 19.2,
+ "content_p95": 37.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 48,
+ "mbps": 11.11,
+ "content_p50": 29.9,
+ "content_p95": 107.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 44,
+ "mbps": 8.39,
+ "content_p50": 24.1,
+ "content_p95": 34.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 49,
+ "mbps": 8.68,
+ "content_p50": 14.5,
+ "content_p95": 39.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 43,
+ "mbps": 8.08,
+ "content_p50": 16.6,
+ "content_p95": 36.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 53,
+ "mbps": 8.85,
+ "content_p50": 15.1,
+ "content_p95": 25.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 50,
+ "mbps": 8.82,
+ "content_p50": 20.1,
+ "content_p95": 35.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 54,
+ "mbps": 8.42,
+ "content_p50": 18.9,
+ "content_p95": 26.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 52,
+ "mbps": 9.27,
+ "content_p50": 12.8,
+ "content_p95": 28.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 47,
+ "mbps": 9.89,
+ "content_p50": 21.5,
+ "content_p95": 42.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 31,
+ "mbps": 6.44,
+ "content_p50": 21.6,
+ "content_p95": 39.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.5,
+ "content_p95": 44.4,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 7.541,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 51.6,
+ "cpu": {
+ "mac_agent_pct": 7.1,
+ "frame_viewer_pct": 80.9,
+ "frame_tailscaled_pct": 7.9,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 25.3,
+ "frame_total_pct": 31.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 1.68,
+ "panel": "valve.steam.desktopgame.2001300498",
+ "src": "separate:13051"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 944,
+ "frames_drawn": 944,
+ "frames_shown": 608,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 944
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 944
+ },
+ "encode": {
+ "p50": 4.0,
+ "p95": 4.7,
+ "p99": 5.2,
+ "n": 944
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 944
+ },
+ "network": {
+ "p50": 4.8,
+ "p95": 40.4,
+ "p99": 74.2,
+ "n": 944
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 5.4,
+ "p99": 10.8,
+ "n": 944
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.5,
+ "p99": 0.9,
+ "n": 944
+ },
+ "present": {
+ "p50": 4.9,
+ "p95": 17.7,
+ "p99": 22.2,
+ "n": 608
+ },
+ "content": {
+ "p50": 10.5,
+ "p95": 52.3,
+ "p99": 82.1,
+ "n": 944
+ },
+ "content_shown": {
+ "p50": 16.0,
+ "p95": 33.4,
+ "p99": 82.8,
+ "n": 608
+ }
+ },
+ "fps": 60.1,
+ "fps_shown": 38.7,
+ "late_pct": 3.61,
+ "stall_max": 120.1,
+ "stalls_over_100ms": 2,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1034,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 26.7,
+ "p95": 89.3,
+ "p99": 106.5,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 29.4,
+ "p95": 44.4,
+ "p99": 106.9,
+ "n": 18
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 9.2,
+ "p95": 38.3,
+ "p99": 70.5,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.2,
+ "p95": 14.1,
+ "p99": 18.3,
+ "n": 30
+ },
+ "back": {
+ "p50": 10.4,
+ "p95": 16.2,
+ "p99": 86.2,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 10.3,
+ "content_p95": 14.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 10.4,
+ "content_p95": 13.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.8,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 9.6,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 9.6,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 61,
+ "mbps": 0.57,
+ "content_p50": 9.9,
+ "content_p95": 34.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 9.7,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 10.5,
+ "content_p95": 40.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 11.8,
+ "content_p95": 82.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.4,
+ "content_p95": 68.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 61,
+ "mbps": 0.46,
+ "content_p50": 12.4,
+ "content_p95": 71.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 59,
+ "mbps": 0.44,
+ "content_p50": 11.8,
+ "content_p95": 70.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 11.8,
+ "content_p95": 70.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 43,
+ "mbps": 0.32,
+ "content_p50": 12.1,
+ "content_p95": 69.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.5,
+ "content_p95": 52.3,
+ "input_p50": 26.7,
+ "input_p95": 89.3,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "local",
+ "input_p95": "acceptable",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.458,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 5,
+ "scale": 1,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.9,
+ "cpu": {
+ "mac_agent_pct": 14.0,
+ "frame_viewer_pct": 66.0,
+ "frame_tailscaled_pct": 5.1,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 28.5,
+ "frame_total_pct": 32.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 135 Hz",
+ "show_s": 1.73,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 823,
+ "frames_drawn": 823,
+ "frames_shown": 523,
+ "duration_s": 15.6,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.4,
+ "p95": 5.1,
+ "p99": 12.6,
+ "n": 823
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 823
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 7.8,
+ "p99": 10.5,
+ "n": 823
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 823
+ },
+ "network": {
+ "p50": 16.2,
+ "p95": 90.9,
+ "p99": 145.2,
+ "n": 823
+ },
+ "decode": {
+ "p50": 8.7,
+ "p95": 25.2,
+ "p99": 35.5,
+ "n": 823
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 0.9,
+ "p99": 1.7,
+ "n": 823
+ },
+ "present": {
+ "p50": 1.9,
+ "p95": 20.9,
+ "p99": 26.5,
+ "n": 523
+ },
+ "content": {
+ "p50": 33.3,
+ "p95": 108.6,
+ "p99": 167.3,
+ "n": 823
+ },
+ "content_shown": {
+ "p50": 35.0,
+ "p95": 106.8,
+ "p99": 141.4,
+ "n": 523
+ }
+ },
+ "fps": 52.6,
+ "fps_shown": 33.4,
+ "late_pct": 14.84,
+ "stall_max": 232.0,
+ "stalls_over_100ms": 32,
+ "mbps": 9.35,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 895,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 52,
+ "mbps": 8.9,
+ "content_p50": 30.2,
+ "content_p95": 85.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 53,
+ "mbps": 9.82,
+ "content_p50": 32.6,
+ "content_p95": 106.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 53,
+ "mbps": 9.45,
+ "content_p50": 31.9,
+ "content_p95": 96.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 56,
+ "mbps": 9.42,
+ "content_p50": 37.6,
+ "content_p95": 99.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.26,
+ "content_p50": 38.2,
+ "content_p95": 97.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 11.43,
+ "content_p50": 56.3,
+ "content_p95": 144.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 50,
+ "mbps": 8.55,
+ "content_p50": 27.0,
+ "content_p95": 104.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 52,
+ "mbps": 9.63,
+ "content_p50": 37.1,
+ "content_p95": 99.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 46,
+ "mbps": 8.79,
+ "content_p50": 31.2,
+ "content_p95": 115.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 55,
+ "mbps": 9.43,
+ "content_p50": 29.4,
+ "content_p95": 97.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 50,
+ "mbps": 9.29,
+ "content_p50": 29.8,
+ "content_p95": 93.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 55,
+ "mbps": 9.78,
+ "content_p50": 27.0,
+ "content_p95": 98.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 52,
+ "mbps": 9.19,
+ "content_p50": 53.0,
+ "content_p95": 116.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 55,
+ "mbps": 9.33,
+ "content_p50": 45.5,
+ "content_p95": 102.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 45,
+ "mbps": 7.78,
+ "content_p50": 28.6,
+ "content_p95": 102.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 35,
+ "mbps": 6.17,
+ "content_p50": 62.5,
+ "content_p95": 211.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 33.3,
+ "content_p95": 108.6,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "acceptable",
+ "content_p95": "bad",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 7.791,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 57.4,
+ "cpu": {
+ "mac_agent_pct": 7.6,
+ "frame_viewer_pct": 83.9,
+ "frame_tailscaled_pct": 7.8,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 26.7,
+ "frame_total_pct": 32.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 135 Hz",
+ "show_s": 1.66,
+ "panel": "valve.steam.desktopgame.2001667691",
+ "src": "separate:13436"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 943,
+ "frames_drawn": 943,
+ "frames_shown": 631,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 943
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 943
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 943
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 943
+ },
+ "network": {
+ "p50": 6.4,
+ "p95": 23.1,
+ "p99": 64.7,
+ "n": 943
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.8,
+ "p99": 6.3,
+ "n": 943
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.5,
+ "p99": 0.9,
+ "n": 943
+ },
+ "present": {
+ "p50": 5.2,
+ "p95": 15.7,
+ "p99": 21.4,
+ "n": 631
+ },
+ "content": {
+ "p50": 12.2,
+ "p95": 29.7,
+ "p99": 72.2,
+ "n": 943
+ },
+ "content_shown": {
+ "p50": 18.4,
+ "p95": 32.7,
+ "p99": 75.5,
+ "n": 631
+ }
+ },
+ "fps": 60.1,
+ "fps_shown": 40.2,
+ "late_pct": 5.73,
+ "stall_max": 123.4,
+ "stalls_over_100ms": 1,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1034,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 31.4,
+ "p95": 78.1,
+ "p99": 187.4,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 38.8,
+ "p95": 56.9,
+ "p99": 62.9,
+ "n": 20
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 11.4,
+ "p95": 40.8,
+ "p99": 170.5,
+ "n": 30
+ },
+ "mac": {
+ "p50": 7.0,
+ "p95": 15.0,
+ "p99": 16.1,
+ "n": 30
+ },
+ "back": {
+ "p50": 11.6,
+ "p95": 18.8,
+ "p99": 29.4,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 9.3,
+ "content_p95": 53.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.3,
+ "content_p95": 51.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 22.3,
+ "content_p95": 86.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.51,
+ "content_p50": 16.9,
+ "content_p95": 65.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 11.6,
+ "content_p95": 37.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 12.3,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 12.6,
+ "content_p95": 16.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.9,
+ "content_p95": 15.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 11.8,
+ "content_p95": 17.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.8,
+ "content_p95": 19.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.1,
+ "content_p95": 15.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.6,
+ "content_p95": 18.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.5,
+ "content_p95": 17.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 13.0,
+ "content_p95": 18.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 14.0,
+ "content_p95": 22.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 43,
+ "mbps": 0.3,
+ "content_p50": 13.6,
+ "content_p95": 22.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 12.2,
+ "content_p95": 29.7,
+ "input_p50": 31.4,
+ "input_p95": 78.1,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "acceptable",
+ "fps": "local",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.125,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.9,
+ "cpu": {
+ "mac_agent_pct": 15.5,
+ "frame_viewer_pct": 67.2,
+ "frame_tailscaled_pct": 22.5,
+ "frame_sshd_pct": 2.6,
+ "frame_gamescope_pct": 28.6,
+ "frame_total_pct": 38.9
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 132 Hz",
+ "show_s": 1.52,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 624,
+ "frames_drawn": 622,
+ "frames_shown": 522,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -2.0,
+ "p95": 13.8,
+ "p99": 34.2,
+ "n": 624
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.2,
+ "n": 624
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 8.1,
+ "p99": 13.7,
+ "n": 624
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 624
+ },
+ "network": {
+ "p50": 7.5,
+ "p95": 29.1,
+ "p99": 83.6,
+ "n": 624
+ },
+ "decode": {
+ "p50": 7.6,
+ "p95": 83.1,
+ "p99": 242.3,
+ "n": 622
+ },
+ "draw": {
+ "p50": 0.8,
+ "p95": 2.2,
+ "p99": 5.1,
+ "n": 622
+ },
+ "present": {
+ "p50": 1.5,
+ "p95": 19.9,
+ "p99": 25.1,
+ "n": 522
+ },
+ "content": {
+ "p50": 25.3,
+ "p95": 110.6,
+ "p99": 326.3,
+ "n": 622
+ },
+ "content_shown": {
+ "p50": 30.3,
+ "p95": 91.4,
+ "p99": 143.8,
+ "n": 522
+ }
+ },
+ "fps": 39.5,
+ "fps_shown": 33.2,
+ "late_pct": 33.66,
+ "stall_max": 362.0,
+ "stalls_over_100ms": 5,
+ "mbps": 8.59,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 678,
+ "viewer_never_drawn": 2,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 42,
+ "mbps": 9.07,
+ "content_p50": 24.3,
+ "content_p95": 39.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 42,
+ "mbps": 9.11,
+ "content_p50": 27.3,
+ "content_p95": 76.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 43,
+ "mbps": 8.81,
+ "content_p50": 25.6,
+ "content_p95": 96.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 44,
+ "mbps": 8.11,
+ "content_p50": 46.5,
+ "content_p95": 89.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 37,
+ "mbps": 8.52,
+ "content_p50": 19.4,
+ "content_p95": 49.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 43,
+ "mbps": 9.51,
+ "content_p50": 28.1,
+ "content_p95": 92.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 35,
+ "mbps": 10.84,
+ "content_p50": 24.8,
+ "content_p95": 61.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 25,
+ "mbps": 6.13,
+ "content_p50": 21.8,
+ "content_p95": 62.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 52,
+ "mbps": 9.53,
+ "content_p50": 17.0,
+ "content_p95": 29.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 37,
+ "mbps": 8.18,
+ "content_p50": 35.9,
+ "content_p95": 78.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 21,
+ "mbps": 5.48,
+ "content_p50": 44.5,
+ "content_p95": 350.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 34,
+ "mbps": 7.4,
+ "content_p50": 27.0,
+ "content_p95": 54.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 54,
+ "mbps": 8.87,
+ "content_p50": 15.4,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 43,
+ "mbps": 10.59,
+ "content_p50": 131.1,
+ "content_p95": 322.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 43,
+ "mbps": 9.22,
+ "content_p50": 23.9,
+ "content_p95": 42.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 27,
+ "mbps": 5.81,
+ "content_p50": 27.6,
+ "content_p95": 53.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 25.3,
+ "content_p95": 110.6,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "acceptable",
+ "content_p95": "bad",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 7.583,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 43.2,
+ "cpu": {
+ "mac_agent_pct": 5.8,
+ "frame_viewer_pct": 86.7,
+ "frame_tailscaled_pct": 9.9,
+ "frame_sshd_pct": 1.0,
+ "frame_gamescope_pct": 25.0,
+ "frame_total_pct": 77.7
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 1.54,
+ "panel": "valve.steam.desktopgame.2001796574",
+ "src": "separate:13568"
+ }
+ ]
+ ],
+ "on": [
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 843,
+ "frames_drawn": 843,
+ "frames_shown": 580,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 843
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 12.6,
+ "n": 843
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 843
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 843
+ },
+ "network": {
+ "p50": 4.8,
+ "p95": 66.0,
+ "p99": 82.6,
+ "n": 843
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 4.1,
+ "p99": 6.3,
+ "n": 843
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.6,
+ "p99": 1.0,
+ "n": 843
+ },
+ "present": {
+ "p50": 4.0,
+ "p95": 17.6,
+ "p99": 23.5,
+ "n": 580
+ },
+ "content": {
+ "p50": 10.9,
+ "p95": 75.0,
+ "p99": 91.6,
+ "n": 843
+ },
+ "content_shown": {
+ "p50": 16.5,
+ "p95": 81.2,
+ "p99": 93.9,
+ "n": 580
+ }
+ },
+ "fps": 53.6,
+ "fps_shown": 36.9,
+ "late_pct": 6.06,
+ "stall_max": 127.2,
+ "stalls_over_100ms": 2,
+ "mbps": 0.43,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1037,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 27.2,
+ "p95": 95.5,
+ "p99": 107.8,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 32.1,
+ "p95": 95.9,
+ "p99": 108.5,
+ "n": 19
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 8.0,
+ "p95": 44.4,
+ "p99": 44.8,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.8,
+ "p95": 13.7,
+ "p99": 17.9,
+ "n": 30
+ },
+ "back": {
+ "p50": 11.2,
+ "p95": 84.3,
+ "p99": 85.8,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.1,
+ "content_p95": 13.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.1,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.6,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.0,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 9.9,
+ "content_p95": 35.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 54,
+ "mbps": 0.43,
+ "content_p50": 9.8,
+ "content_p95": 51.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 52,
+ "mbps": 0.52,
+ "content_p50": 10.6,
+ "content_p95": 68.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 50,
+ "mbps": 0.39,
+ "content_p50": 10.6,
+ "content_p95": 73.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 47,
+ "mbps": 0.39,
+ "content_p50": 11.2,
+ "content_p95": 80.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 52,
+ "mbps": 0.41,
+ "content_p50": 11.2,
+ "content_p95": 79.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 52,
+ "mbps": 0.4,
+ "content_p50": 10.5,
+ "content_p95": 75.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 51,
+ "mbps": 0.4,
+ "content_p50": 13.4,
+ "content_p95": 75.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 50,
+ "mbps": 0.4,
+ "content_p50": 14.7,
+ "content_p95": 77.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 45,
+ "mbps": 0.37,
+ "content_p50": 15.3,
+ "content_p95": 102.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 52,
+ "mbps": 0.4,
+ "content_p50": 15.6,
+ "content_p95": 80.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 39,
+ "mbps": 0.3,
+ "content_p50": 14.7,
+ "content_p95": 80.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.9,
+ "content_p95": 75.0,
+ "input_p50": 27.2,
+ "input_p95": 95.5,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "local",
+ "input_p95": "acceptable",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.875,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 66.1,
+ "cpu": {
+ "mac_agent_pct": 14.9,
+ "frame_viewer_pct": 58.7,
+ "frame_tailscaled_pct": 4.9,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 25.8,
+ "frame_total_pct": 28.7
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.32,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 756,
+ "frames_drawn": 756,
+ "frames_shown": 606,
+ "duration_s": 15.6,
+ "stages_ms": {
+ "capture": {
+ "p50": -7.9,
+ "p95": 7.1,
+ "p99": 14.9,
+ "n": 756
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 7.8,
+ "n": 756
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 9.0,
+ "p99": 12.8,
+ "n": 756
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 756
+ },
+ "network": {
+ "p50": 7.1,
+ "p95": 27.0,
+ "p99": 45.8,
+ "n": 756
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 11.9,
+ "p99": 20.9,
+ "n": 756
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 0.8,
+ "p99": 1.6,
+ "n": 756
+ },
+ "present": {
+ "p50": 3.3,
+ "p95": 20.5,
+ "p99": 25.9,
+ "n": 606
+ },
+ "content": {
+ "p50": 16.6,
+ "p95": 45.7,
+ "p99": 64.8,
+ "n": 756
+ },
+ "content_shown": {
+ "p50": 22.8,
+ "p95": 50.9,
+ "p99": 68.4,
+ "n": 606
+ }
+ },
+ "fps": 48.4,
+ "fps_shown": 38.8,
+ "late_pct": 21.06,
+ "stall_max": 88.2,
+ "stalls_over_100ms": 0,
+ "mbps": 9.16,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 841,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 50,
+ "mbps": 8.74,
+ "content_p50": 14.0,
+ "content_p95": 30.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 45,
+ "mbps": 8.64,
+ "content_p50": 22.9,
+ "content_p95": 49.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 47,
+ "mbps": 8.91,
+ "content_p50": 20.8,
+ "content_p95": 45.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 46,
+ "mbps": 9.41,
+ "content_p50": 18.6,
+ "content_p95": 53.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 49,
+ "mbps": 8.61,
+ "content_p50": 13.0,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 53,
+ "mbps": 11.17,
+ "content_p50": 11.7,
+ "content_p95": 51.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 51,
+ "mbps": 9.1,
+ "content_p50": 12.4,
+ "content_p95": 27.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 48,
+ "mbps": 9.45,
+ "content_p50": 19.5,
+ "content_p95": 56.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 55,
+ "mbps": 9.21,
+ "content_p50": 11.9,
+ "content_p95": 30.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 50,
+ "mbps": 8.55,
+ "content_p50": 23.1,
+ "content_p95": 63.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 49,
+ "mbps": 8.94,
+ "content_p50": 19.5,
+ "content_p95": 36.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 54,
+ "mbps": 9.51,
+ "content_p50": 16.0,
+ "content_p95": 31.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 41,
+ "mbps": 8.36,
+ "content_p50": 21.5,
+ "content_p95": 45.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 37,
+ "mbps": 8.55,
+ "content_p50": 22.4,
+ "content_p95": 51.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 48,
+ "mbps": 10.2,
+ "content_p50": 19.6,
+ "content_p95": 34.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 33,
+ "mbps": 5.73,
+ "content_p50": 10.7,
+ "content_p95": 54.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 16.6,
+ "content_p95": 45.7,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 7.041,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 0.19,
+ "e": "tier 0"
+ }
+ ],
+ "source_fps": 53.9,
+ "cpu": {
+ "mac_agent_pct": 7.7,
+ "frame_viewer_pct": 83.2,
+ "frame_tailscaled_pct": 9.0,
+ "frame_sshd_pct": 1.0,
+ "frame_gamescope_pct": 25.4,
+ "frame_total_pct": 32.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 1.54,
+ "panel": "valve.steam.desktopgame.2001527998",
+ "src": "separate:13175"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 932,
+ "frames_drawn": 932,
+ "frames_shown": 637,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 932
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.2,
+ "n": 932
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.9,
+ "p99": 5.2,
+ "n": 932
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 932
+ },
+ "network": {
+ "p50": 4.6,
+ "p95": 9.1,
+ "p99": 12.6,
+ "n": 932
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 1.6,
+ "p99": 4.1,
+ "n": 932
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.5,
+ "p99": 0.9,
+ "n": 932
+ },
+ "present": {
+ "p50": 5.0,
+ "p95": 15.8,
+ "p99": 19.3,
+ "n": 637
+ },
+ "content": {
+ "p50": 10.5,
+ "p95": 15.4,
+ "p99": 19.0,
+ "n": 932
+ },
+ "content_shown": {
+ "p50": 16.1,
+ "p95": 28.2,
+ "p99": 33.7,
+ "n": 637
+ }
+ },
+ "fps": 59.4,
+ "fps_shown": 40.6,
+ "late_pct": 0.75,
+ "stall_max": 126.0,
+ "stalls_over_100ms": 1,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1031,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 24.6,
+ "p95": 39.8,
+ "p99": 45.9,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 34.5,
+ "p95": 41.8,
+ "p99": 58.5,
+ "n": 24
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.1,
+ "p95": 19.2,
+ "p99": 21.0,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.2,
+ "p95": 14.8,
+ "p99": 16.1,
+ "n": 30
+ },
+ "back": {
+ "p50": 10.7,
+ "p95": 14.8,
+ "p99": 14.9,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.2,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 9.5,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 53,
+ "mbps": 0.45,
+ "content_p50": 10.0,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.8,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 0.47,
+ "content_p50": 10.7,
+ "content_p95": 14.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 10.3,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.8,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.9,
+ "content_p95": 12.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 10.1,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 10.2,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 10.6,
+ "content_p95": 14.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 14.3,
+ "content_p95": 17.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 14.1,
+ "content_p95": 16.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 13.8,
+ "content_p95": 15.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 42,
+ "mbps": 0.32,
+ "content_p50": 13.8,
+ "content_p95": 18.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.5,
+ "content_p95": 15.4,
+ "input_p50": 24.6,
+ "input_p95": 39.8,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.459,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.7,
+ "cpu": {
+ "mac_agent_pct": 15.9,
+ "frame_viewer_pct": 66.0,
+ "frame_tailscaled_pct": 5.4,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 28.8,
+ "frame_total_pct": 32.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 1.49,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 604,
+ "frames_drawn": 604,
+ "frames_shown": 496,
+ "duration_s": 15.8,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.6,
+ "p95": 11.2,
+ "p99": 19.8,
+ "n": 604
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 15.4,
+ "p99": 23.5,
+ "n": 604
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 9.3,
+ "p99": 19.2,
+ "n": 604
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.7,
+ "n": 604
+ },
+ "network": {
+ "p50": 7.9,
+ "p95": 76.6,
+ "p99": 94.2,
+ "n": 604
+ },
+ "decode": {
+ "p50": 6.3,
+ "p95": 19.1,
+ "p99": 25.5,
+ "n": 604
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.0,
+ "p99": 1.6,
+ "n": 604
+ },
+ "present": {
+ "p50": 0.9,
+ "p95": 19.0,
+ "p99": 22.0,
+ "n": 496
+ },
+ "content": {
+ "p50": 23.4,
+ "p95": 100.3,
+ "p99": 145.3,
+ "n": 604
+ },
+ "content_shown": {
+ "p50": 28.9,
+ "p95": 104.7,
+ "p99": 158.2,
+ "n": 496
+ }
+ },
+ "fps": 38.2,
+ "fps_shown": 31.3,
+ "late_pct": 25.87,
+ "stall_max": 180.7,
+ "stalls_over_100ms": 22,
+ "mbps": 6.88,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 843,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 28,
+ "mbps": 6.12,
+ "content_p50": 30.5,
+ "content_p95": 161.7,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 24,
+ "mbps": 4.2,
+ "content_p50": 36.9,
+ "content_p95": 120.7,
+ "bitrate": 8223440,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 41,
+ "mbps": 8.28,
+ "content_p50": 20.3,
+ "content_p95": 62.3,
+ "bitrate": 11110898,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 42,
+ "mbps": 9.01,
+ "content_p50": 22.2,
+ "content_p95": 91.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 47,
+ "mbps": 8.61,
+ "content_p50": 15.8,
+ "content_p95": 81.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 43,
+ "mbps": 8.02,
+ "content_p50": 17.4,
+ "content_p95": 56.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 40,
+ "mbps": 7.97,
+ "content_p50": 29.0,
+ "content_p95": 82.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 36,
+ "mbps": 7.83,
+ "content_p50": 33.3,
+ "content_p95": 100.5,
+ "bitrate": 6992996,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 40,
+ "mbps": 5.6,
+ "content_p50": 21.7,
+ "content_p95": 97.9,
+ "bitrate": 7742295,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 43,
+ "mbps": 7.07,
+ "content_p50": 19.5,
+ "content_p95": 90.7,
+ "bitrate": 10470493,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 44,
+ "mbps": 7.09,
+ "content_p50": 19.9,
+ "content_p95": 97.5,
+ "bitrate": 7246454,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 35,
+ "mbps": 5.19,
+ "content_p50": 22.4,
+ "content_p95": 166.6,
+ "bitrate": 5307220,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 39,
+ "mbps": 4.5,
+ "content_p50": 22.9,
+ "content_p95": 84.3,
+ "bitrate": 5887942,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 36,
+ "mbps": 5.81,
+ "content_p50": 28.4,
+ "content_p95": 94.8,
+ "bitrate": 8002349,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 36,
+ "mbps": 7.68,
+ "content_p50": 27.4,
+ "content_p95": 107.5,
+ "bitrate": 11948287,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 30,
+ "mbps": 5.87,
+ "content_p50": 33.0,
+ "content_p95": 104.9,
+ "bitrate": 14562426,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 23.4,
+ "content_p95": 100.3,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "bad",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.959,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "target": 7430400,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 0.87,
+ "e": "down to 7430 kbit/s: queue 191 ms, held 3, oldest 218 ms, base 7 ms, sent 4207 got 3329"
+ },
+ {
+ "t": 7.54,
+ "e": "down to 8741 kbit/s: queue 52 ms, held 3, oldest 35 ms, base 7 ms, sent 10505 got 9712"
+ },
+ {
+ "t": 7.84,
+ "e": "down to 6992 kbit/s: queue 17 ms, held 3, oldest 3 ms, base 7 ms, sent 8895 got 9452"
+ },
+ {
+ "t": 10.54,
+ "e": "down to 7246 kbit/s: queue 19 ms, held 3, oldest 80 ms, base 7 ms, sent 8009 got 8051"
+ },
+ {
+ "t": 11.74,
+ "e": "down to 5307 kbit/s: queue 26 ms, held 3, oldest 9 ms, base 6 ms, sent 5808 got 5896"
+ },
+ {
+ "t": 12.34,
+ "e": "tier 1"
+ },
+ {
+ "t": 16.14,
+ "e": "tier 0"
+ },
+ {
+ "t": 17.34,
+ "e": "down to 7430 kbit/s: queue 94 ms, held 2, oldest 94 ms, base 6 ms, sent 7608 got 6892"
+ }
+ ],
+ "source_fps": 53.4,
+ "cpu": {
+ "mac_agent_pct": 6.5,
+ "frame_viewer_pct": 69.3,
+ "frame_tailscaled_pct": 6.4,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 23.2,
+ "frame_total_pct": 27.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.35,
+ "panel": "valve.steam.desktopgame.2001652781",
+ "src": "separate:13306"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 482,
+ "frames_drawn": 482,
+ "frames_shown": 479,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 482
+ },
+ "queue": {
+ "p50": 8.8,
+ "p95": 16.5,
+ "p99": 18.6,
+ "n": 482
+ },
+ "encode": {
+ "p50": 3.7,
+ "p95": 4.5,
+ "p99": 5.0,
+ "n": 482
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 482
+ },
+ "network": {
+ "p50": 6.0,
+ "p95": 36.8,
+ "p99": 84.9,
+ "n": 482
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.7,
+ "p99": 4.8,
+ "n": 482
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 0.9,
+ "n": 482
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 10.8,
+ "p99": 13.0,
+ "n": 479
+ },
+ "content": {
+ "p50": 22.9,
+ "p95": 54.1,
+ "p99": 104.8,
+ "n": 482
+ },
+ "content_shown": {
+ "p50": 24.9,
+ "p95": 54.9,
+ "p99": 77.6,
+ "n": 479
+ }
+ },
+ "fps": 30.6,
+ "fps_shown": 30.4,
+ "late_pct": 68.81,
+ "stall_max": 218.0,
+ "stalls_over_100ms": 10,
+ "mbps": 0.19,
+ "keyframes": 2,
+ "size": "960x540",
+ "captured": 1037,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 53.9,
+ "p95": 117.8,
+ "p99": 222.5,
+ "n": 29
+ },
+ "input_shown_ms": {
+ "p50": 54.4,
+ "p95": 123.9,
+ "p99": 223.5,
+ "n": 29
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 17.7,
+ "p95": 77.5,
+ "p99": 127.7,
+ "n": 29
+ },
+ "mac": {
+ "p50": 8.2,
+ "p95": 30.4,
+ "p99": 63.3,
+ "n": 29
+ },
+ "back": {
+ "p50": 19.5,
+ "p95": 38.3,
+ "p99": 38.6,
+ "n": 29
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 29
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 55,
+ "mbps": 0.46,
+ "content_p50": 9.9,
+ "content_p95": 20.9,
+ "bitrate": 1545640,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 33,
+ "mbps": 0.28,
+ "content_p50": 27.7,
+ "content_p95": 40.2,
+ "bitrate": 772820,
+ "tier": 3,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 32,
+ "mbps": 0.28,
+ "content_p50": 22.9,
+ "content_p95": 29.0,
+ "bitrate": 772820,
+ "tier": 3,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 25,
+ "mbps": 0.23,
+ "content_p50": 26.5,
+ "content_p95": 106.6,
+ "bitrate": 386410,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 21,
+ "mbps": 0.14,
+ "content_p50": 37.0,
+ "content_p95": 54.1,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 24,
+ "mbps": 0.1,
+ "content_p50": 32.6,
+ "content_p95": 96.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 29.6,
+ "content_p95": 46.7,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 30,
+ "mbps": 0.12,
+ "content_p50": 25.7,
+ "content_p95": 80.1,
+ "bitrate": 380000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 32,
+ "mbps": 0.2,
+ "content_p50": 29.0,
+ "content_p95": 56.4,
+ "bitrate": 671280,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 26,
+ "mbps": 0.13,
+ "content_p50": 31.8,
+ "content_p95": 47.8,
+ "bitrate": 458624,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 30,
+ "mbps": 0.14,
+ "content_p50": 36.1,
+ "content_p95": 64.2,
+ "bitrate": 458624,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 29,
+ "mbps": 0.14,
+ "content_p50": 28.1,
+ "content_p95": 56.7,
+ "bitrate": 458624,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 26,
+ "mbps": 0.11,
+ "content_p50": 18.7,
+ "content_p95": 27.8,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 32,
+ "mbps": 0.24,
+ "content_p50": 20.1,
+ "content_p95": 24.8,
+ "bitrate": 468000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 32,
+ "mbps": 0.18,
+ "content_p50": 19.4,
+ "content_p95": 32.8,
+ "bitrate": 468000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 24,
+ "mbps": 0.12,
+ "content_p50": 21.9,
+ "content_p95": 35.2,
+ "bitrate": 671280,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 22.9,
+ "content_p95": 54.1,
+ "input_p50": 53.9,
+ "input_p95": 117.8,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "acceptable",
+ "input_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.417,
+ "ceiling": 5529600,
+ "fps": 30,
+ "inFlight": 2,
+ "scale": 0.5,
+ "target": 1386355,
+ "tier": 4
+ },
+ "events": [
+ {
+ "t": 0.87,
+ "e": "down to 1545 kbit/s: queue 18 ms, held 3, oldest 11 ms, base 5 ms, sent 424 got 420"
+ },
+ {
+ "t": 1.27,
+ "e": "down to 772 kbit/s: queue 40 ms, held 3, oldest 55 ms, base 5 ms, sent 374 got 368"
+ },
+ {
+ "t": 1.48,
+ "e": "tier 3"
+ },
+ {
+ "t": 3.37,
+ "e": "down to 386 kbit/s: queue 119 ms, held 5, oldest 20 ms, base 5 ms, sent 210 got 208"
+ },
+ {
+ "t": 3.88,
+ "e": "tier 4"
+ },
+ {
+ "t": 4.07,
+ "e": "down to 300 kbit/s: queue 55 ms, held 0, oldest 0 ms, base 5 ms, sent 254 got 254"
+ },
+ {
+ "t": 4.97,
+ "e": "down to 300 kbit/s: queue 113 ms, held 1, oldest 91 ms, base 5 ms, sent 154 got 161"
+ },
+ {
+ "t": 9.78,
+ "e": "down to 458 kbit/s: queue 206 ms, held 6, oldest 0 ms, base 6 ms, sent 109 got 127"
+ },
+ {
+ "t": 12.17,
+ "e": "down to 300 kbit/s: queue 4 ms, held 6, oldest 215 ms, base 6 ms, sent 116 got 103"
+ }
+ ],
+ "source_fps": 66.1,
+ "cpu": {
+ "mac_agent_pct": 10.9,
+ "frame_viewer_pct": 42.3,
+ "frame_tailscaled_pct": 3.8,
+ "frame_sshd_pct": 0.4,
+ "frame_gamescope_pct": 15.4,
+ "frame_total_pct": 23.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 70 Hz",
+ "show_s": 1.46,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 523,
+ "frames_drawn": 523,
+ "frames_shown": 515,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.7,
+ "p95": 10.3,
+ "p99": 17.5,
+ "n": 523
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 18.2,
+ "p99": 33.6,
+ "n": 523
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 8.4,
+ "p99": 17.2,
+ "n": 523
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 523
+ },
+ "network": {
+ "p50": 7.7,
+ "p95": 34.2,
+ "p99": 205.8,
+ "n": 523
+ },
+ "decode": {
+ "p50": 6.2,
+ "p95": 15.1,
+ "p99": 23.5,
+ "n": 523
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 0.8,
+ "p99": 1.7,
+ "n": 523
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 11.4,
+ "p99": 14.3,
+ "n": 515
+ },
+ "content": {
+ "p50": 23.9,
+ "p95": 65.6,
+ "p99": 214.3,
+ "n": 523
+ },
+ "content_shown": {
+ "p50": 26.4,
+ "p95": 66.0,
+ "p99": 215.2,
+ "n": 515
+ }
+ },
+ "fps": 33.3,
+ "fps_shown": 32.7,
+ "late_pct": 56.13,
+ "stall_max": 321.6,
+ "stalls_over_100ms": 10,
+ "mbps": 6.29,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 789,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 29,
+ "mbps": 6.01,
+ "content_p50": 17.9,
+ "content_p95": 139.0,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 26,
+ "mbps": 2.1,
+ "content_p50": 33.3,
+ "content_p95": 86.5,
+ "bitrate": 2202984,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 30,
+ "mbps": 2.11,
+ "content_p50": 23.4,
+ "content_p95": 40.3,
+ "bitrate": 2770610,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 30,
+ "mbps": 3.24,
+ "content_p50": 19.3,
+ "content_p95": 47.2,
+ "bitrate": 3853181,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 26,
+ "mbps": 3.85,
+ "content_p50": 20.9,
+ "content_p95": 120.6,
+ "bitrate": 5294082,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 26,
+ "mbps": 2.95,
+ "content_p50": 23.3,
+ "content_p95": 46.7,
+ "bitrate": 3187180,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 29,
+ "mbps": 3.51,
+ "content_p50": 27.5,
+ "content_p95": 48.7,
+ "bitrate": 4898398,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 28,
+ "mbps": 5.29,
+ "content_p50": 28.2,
+ "content_p95": 48.8,
+ "bitrate": 6685266,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 29,
+ "mbps": 7.09,
+ "content_p50": 23.8,
+ "content_p95": 50.0,
+ "bitrate": 9063588,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 35,
+ "mbps": 9.72,
+ "content_p50": 31.0,
+ "content_p95": 59.7,
+ "bitrate": 13502047,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 42,
+ "mbps": 12.62,
+ "content_p50": 23.9,
+ "content_p95": 34.4,
+ "bitrate": 14860800,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 47,
+ "mbps": 11.31,
+ "content_p50": 20.5,
+ "content_p95": 41.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 35,
+ "mbps": 8.1,
+ "content_p50": 20.7,
+ "content_p95": 120.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 35,
+ "mbps": 6.11,
+ "content_p50": 21.1,
+ "content_p95": 36.8,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 42,
+ "mbps": 8.02,
+ "content_p50": 24.8,
+ "content_p95": 38.7,
+ "bitrate": 10055362,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 34,
+ "mbps": 6.95,
+ "content_p50": 19.2,
+ "content_p95": 309.3,
+ "bitrate": 12271987,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 23.9,
+ "content_p95": 65.6,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 7.209,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "target": 7502051,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 0.78,
+ "e": "down to 7430 kbit/s: queue 95 ms, held 3, oldest 0 ms, base 7 ms, sent 4459 got 5191"
+ },
+ {
+ "t": 1.08,
+ "e": "down to 3715 kbit/s: queue 130 ms, held 2, oldest 127 ms, base 7 ms, sent 2434 got 2754"
+ },
+ {
+ "t": 1.48,
+ "e": "down to 2202 kbit/s: queue 44 ms, held 1, oldest 37 ms, base 7 ms, sent 1917 got 2447"
+ },
+ {
+ "t": 1.68,
+ "e": "tier 3"
+ },
+ {
+ "t": 4.98,
+ "e": "down to 3187 kbit/s: queue 102 ms, held 0, oldest 31 ms, base 7 ms, sent 3802 got 3541"
+ },
+ {
+ "t": 5.18,
+ "e": "tier 2"
+ },
+ {
+ "t": 9.38,
+ "e": "tier 1"
+ },
+ {
+ "t": 11.48,
+ "e": "tier 0"
+ },
+ {
+ "t": 13.28,
+ "e": "down to 7430 kbit/s: queue 257 ms, held 5, oldest 240 ms, base 6 ms, sent 4758 got 6534"
+ },
+ {
+ "t": 15.99,
+ "e": "down to 6774 kbit/s: queue 1 ms, held 6, oldest 284 ms, base 7 ms, sent 5665 got 4867"
+ }
+ ],
+ "source_fps": 50.3,
+ "cpu": {
+ "mac_agent_pct": 5.8,
+ "frame_viewer_pct": 64.7,
+ "frame_tailscaled_pct": 5.9,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 16.2,
+ "frame_total_pct": 26.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.37,
+ "panel": "valve.steam.desktopgame.2001605080",
+ "src": "separate:13687"
+ }
+ ]
+ ]
+ },
+ "table": {
+ "test": {
+ "content_p50": {
+ "off": [
+ 11.5,
+ 10.5,
+ 12.2
+ ],
+ "on": [
+ 10.9,
+ 10.5,
+ 22.9
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 15.8,
+ 52.3,
+ 29.7
+ ],
+ "on": [
+ 75.0,
+ 15.4,
+ 54.1
+ ]
+ },
+ "input_p50": {
+ "off": [
+ 28.8,
+ 26.7,
+ 31.4
+ ],
+ "on": [
+ 27.2,
+ 24.6,
+ 53.9
+ ]
+ },
+ "input_p95": {
+ "off": [
+ 38.1,
+ 89.3,
+ 78.1
+ ],
+ "on": [
+ 95.5,
+ 39.8,
+ 117.8
+ ]
+ },
+ "fps": {
+ "off": [
+ 60.1,
+ 60.1,
+ 60.1
+ ],
+ "on": [
+ 53.6,
+ 59.4,
+ 30.6
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 0.95,
+ 3.61,
+ 5.73
+ ],
+ "on": [
+ 6.06,
+ 0.75,
+ 68.81
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 68.8,
+ 120.1,
+ 123.4
+ ],
+ "on": [
+ 127.2,
+ 126.0,
+ 218.0
+ ]
+ },
+ "mbps": {
+ "off": [
+ 0.48,
+ 0.47,
+ 0.48
+ ],
+ "on": [
+ 0.43,
+ 0.48,
+ 0.19
+ ]
+ }
+ },
+ "scroll": {
+ "content_p50": {
+ "off": [
+ 19.5,
+ 33.3,
+ 25.3
+ ],
+ "on": [
+ 16.6,
+ 23.4,
+ 23.9
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 44.4,
+ 108.6,
+ 110.6
+ ],
+ "on": [
+ 45.7,
+ 100.3,
+ 65.6
+ ]
+ },
+ "input_p50": {
+ "off": [],
+ "on": []
+ },
+ "input_p95": {
+ "off": [],
+ "on": []
+ },
+ "fps": {
+ "off": [
+ 47.1,
+ 52.6,
+ 39.5
+ ],
+ "on": [
+ 48.4,
+ 38.2,
+ 33.3
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 20.92,
+ 14.84,
+ 33.66
+ ],
+ "on": [
+ 21.06,
+ 25.87,
+ 56.13
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 128.0,
+ 232.0,
+ 362.0
+ ],
+ "on": [
+ 88.2,
+ 180.7,
+ 321.6
+ ]
+ },
+ "mbps": {
+ "off": [
+ 9.15,
+ 9.35,
+ 8.59
+ ],
+ "on": [
+ 9.16,
+ 6.88,
+ 6.29
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean2.json b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean2.json
new file mode 100644
index 0000000..d88f64a
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-clean2.json
@@ -0,0 +1,4150 @@
+{
+ "label": "ab-adapt-clean2",
+ "date": "2026-09-28T20:18:23",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 15.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "8:23.135290 [Info] - 0 - leaving standby",
+ "scenarios": [],
+ "arms": [
+ "off:FRAME_MAC_VIEW_ADAPT=0",
+ "on:"
+ ],
+ "runs": {
+ "off": [
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 925,
+ "frames_drawn": 925,
+ "frames_shown": 649,
+ "duration_s": 15.4,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 925
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 925
+ },
+ "encode": {
+ "p50": 3.6,
+ "p95": 4.5,
+ "p99": 4.8,
+ "n": 925
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 925
+ },
+ "network": {
+ "p50": 4.0,
+ "p95": 10.6,
+ "p99": 57.5,
+ "n": 925
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.9,
+ "p99": 6.5,
+ "n": 925
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.8,
+ "p99": 1.0,
+ "n": 925
+ },
+ "present": {
+ "p50": 4.2,
+ "p95": 14.9,
+ "p99": 19.7,
+ "n": 649
+ },
+ "content": {
+ "p50": 9.5,
+ "p95": 16.2,
+ "p99": 68.6,
+ "n": 925
+ },
+ "content_shown": {
+ "p50": 14.8,
+ "p95": 26.6,
+ "p99": 33.5,
+ "n": 649
+ }
+ },
+ "fps": 59.9,
+ "fps_shown": 42.1,
+ "late_pct": 2.05,
+ "stall_max": 150.2,
+ "stalls_over_100ms": 2,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1047,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 28.3,
+ "p95": 38.3,
+ "p99": 41.2,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 34.8,
+ "p95": 43.5,
+ "p99": 47.2,
+ "n": 21
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 11.3,
+ "p95": 19.6,
+ "p99": 23.2,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.5,
+ "p95": 14.1,
+ "p99": 14.8,
+ "n": 30
+ },
+ "back": {
+ "p50": 9.9,
+ "p95": 13.8,
+ "p99": 15.2,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.0,
+ "content_p95": 12.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.8,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.0,
+ "content_p95": 15.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 9.2,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 61,
+ "mbps": 0.46,
+ "content_p50": 9.1,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 9.0,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 59,
+ "mbps": 0.48,
+ "content_p50": 9.5,
+ "content_p95": 20.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 9.2,
+ "content_p95": 20.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.4,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.8,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.8,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 59,
+ "mbps": 0.46,
+ "content_p50": 13.6,
+ "content_p95": 67.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 13.1,
+ "content_p95": 102.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.1,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 25,
+ "mbps": 0.19,
+ "content_p50": 9.2,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.5,
+ "content_p95": 16.2,
+ "input_p50": 28.3,
+ "input_p95": 38.3,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.375,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 68.0,
+ "cpu": {
+ "mac_agent_pct": 11.5,
+ "frame_viewer_pct": 39.7,
+ "frame_tailscaled_pct": 4.8,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 6.2,
+ "frame_total_pct": 23.9
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 69 Hz",
+ "show_s": 2.03,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 835,
+ "frames_drawn": 835,
+ "frames_shown": 628,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.6,
+ "p95": 1.6,
+ "p99": 8.2,
+ "n": 835
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 835
+ },
+ "encode": {
+ "p50": 6.4,
+ "p95": 8.6,
+ "p99": 14.2,
+ "n": 835
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 835
+ },
+ "network": {
+ "p50": 6.1,
+ "p95": 12.0,
+ "p99": 24.3,
+ "n": 835
+ },
+ "decode": {
+ "p50": 5.8,
+ "p95": 10.9,
+ "p99": 14.7,
+ "n": 835
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.4,
+ "p99": 1.8,
+ "n": 835
+ },
+ "present": {
+ "p50": 4.2,
+ "p95": 17.9,
+ "p99": 22.4,
+ "n": 628
+ },
+ "content": {
+ "p50": 20.0,
+ "p95": 32.3,
+ "p99": 45.5,
+ "n": 835
+ },
+ "content_shown": {
+ "p50": 25.2,
+ "p95": 42.2,
+ "p99": 53.7,
+ "n": 628
+ }
+ },
+ "fps": 54.9,
+ "fps_shown": 41.3,
+ "late_pct": 6.59,
+ "stall_max": 84.3,
+ "stalls_over_100ms": 0,
+ "mbps": 9.44,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 946,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 57,
+ "mbps": 10.21,
+ "content_p50": 19.2,
+ "content_p95": 24.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 55,
+ "mbps": 9.18,
+ "content_p50": 18.6,
+ "content_p95": 45.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 8.93,
+ "content_p50": 19.1,
+ "content_p95": 24.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.42,
+ "content_p50": 19.5,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 56,
+ "mbps": 9.88,
+ "content_p50": 19.9,
+ "content_p95": 25.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 52,
+ "mbps": 9.25,
+ "content_p50": 19.7,
+ "content_p95": 27.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 53,
+ "mbps": 12.28,
+ "content_p50": 20.6,
+ "content_p95": 38.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 53,
+ "mbps": 9.14,
+ "content_p50": 19.2,
+ "content_p95": 24.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 55,
+ "mbps": 9.31,
+ "content_p50": 20.4,
+ "content_p95": 28.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 55,
+ "mbps": 9.22,
+ "content_p50": 21.8,
+ "content_p95": 34.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 56,
+ "mbps": 8.46,
+ "content_p50": 22.7,
+ "content_p95": 41.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 56,
+ "mbps": 9.18,
+ "content_p50": 20.0,
+ "content_p95": 23.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 54,
+ "mbps": 9.08,
+ "content_p50": 21.4,
+ "content_p95": 27.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 55,
+ "mbps": 8.78,
+ "content_p50": 19.6,
+ "content_p95": 28.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 55,
+ "mbps": 9.45,
+ "content_p50": 18.8,
+ "content_p95": 26.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 9,
+ "mbps": 1.68,
+ "content_p50": 25.8,
+ "content_p95": 44.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 20.0,
+ "content_p95": 32.3,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 6.875,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 62.2,
+ "cpu": {
+ "mac_agent_pct": 7.4,
+ "frame_viewer_pct": 70.3,
+ "frame_tailscaled_pct": 7.7,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 6.7,
+ "frame_total_pct": 28.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001924380",
+ "src": "separate:5316"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 912,
+ "frames_drawn": 912,
+ "frames_shown": 618,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 912
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.2,
+ "n": 912
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.9,
+ "p99": 5.2,
+ "n": 912
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 912
+ },
+ "network": {
+ "p50": 3.8,
+ "p95": 8.0,
+ "p99": 11.1,
+ "n": 912
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.3,
+ "p99": 4.2,
+ "n": 912
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 1.0,
+ "n": 912
+ },
+ "present": {
+ "p50": 5.1,
+ "p95": 16.1,
+ "p99": 17.8,
+ "n": 618
+ },
+ "content": {
+ "p50": 9.7,
+ "p95": 14.3,
+ "p99": 17.6,
+ "n": 912
+ },
+ "content_shown": {
+ "p50": 15.3,
+ "p95": 27.8,
+ "p99": 29.4,
+ "n": 618
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 40.7,
+ "late_pct": 0.33,
+ "stall_max": 120.5,
+ "stalls_over_100ms": 1,
+ "mbps": 0.48,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1032,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 22.6,
+ "p95": 32.7,
+ "p99": 46.2,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 28.6,
+ "p95": 39.0,
+ "p99": 47.0,
+ "n": 23
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 7.3,
+ "p95": 18.0,
+ "p99": 22.1,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.2,
+ "p95": 14.0,
+ "p99": 17.2,
+ "n": 30
+ },
+ "back": {
+ "p50": 9.9,
+ "p95": 13.6,
+ "p99": 14.3,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 8.9,
+ "content_p95": 71.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 8.7,
+ "content_p95": 10.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 8.7,
+ "content_p95": 10.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.1,
+ "content_p95": 10.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.7,
+ "content_p95": 10.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 8.7,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 8.9,
+ "content_p95": 11.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 8.7,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.2,
+ "content_p95": 13.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.6,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.6,
+ "content_p95": 14.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 12.3,
+ "content_p95": 15.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 12.8,
+ "content_p95": 15.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 12.7,
+ "content_p95": 14.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.9,
+ "content_p95": 17.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.09,
+ "content_p50": 12.6,
+ "content_p95": 14.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.7,
+ "content_p95": 14.3,
+ "input_p50": 22.6,
+ "input_p95": 32.7,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.334,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 40,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 67.9,
+ "cpu": {
+ "mac_agent_pct": 16.1,
+ "frame_viewer_pct": 46.3,
+ "frame_tailscaled_pct": 5.1,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 8.9,
+ "frame_total_pct": 28.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 140 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 864,
+ "frames_drawn": 864,
+ "frames_shown": 626,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.6,
+ "p95": 0.4,
+ "p99": 0.8,
+ "n": 864
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 864
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 8.5,
+ "p99": 11.1,
+ "n": 864
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 864
+ },
+ "network": {
+ "p50": 6.0,
+ "p95": 9.2,
+ "p99": 14.7,
+ "n": 864
+ },
+ "decode": {
+ "p50": 5.7,
+ "p95": 9.9,
+ "p99": 12.9,
+ "n": 864
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.3,
+ "p99": 1.9,
+ "n": 864
+ },
+ "present": {
+ "p50": 4.3,
+ "p95": 16.8,
+ "p99": 21.2,
+ "n": 626
+ },
+ "content": {
+ "p50": 19.5,
+ "p95": 25.2,
+ "p99": 32.2,
+ "n": 864
+ },
+ "content_shown": {
+ "p50": 24.4,
+ "p95": 36.6,
+ "p99": 41.8,
+ "n": 626
+ }
+ },
+ "fps": 57.0,
+ "fps_shown": 41.3,
+ "late_pct": 3.36,
+ "stall_max": 105.4,
+ "stalls_over_100ms": 1,
+ "mbps": 9.44,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 978,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 56,
+ "mbps": 9.38,
+ "content_p50": 19.1,
+ "content_p95": 24.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 56,
+ "mbps": 9.47,
+ "content_p50": 19.2,
+ "content_p95": 24.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 56,
+ "mbps": 8.94,
+ "content_p50": 19.3,
+ "content_p95": 25.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.51,
+ "content_p50": 19.7,
+ "content_p95": 26.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.65,
+ "content_p50": 18.6,
+ "content_p95": 22.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 9.0,
+ "content_p50": 18.7,
+ "content_p95": 24.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 55,
+ "mbps": 11.82,
+ "content_p50": 20.2,
+ "content_p95": 33.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 9.24,
+ "content_p50": 18.9,
+ "content_p95": 21.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.37,
+ "content_p50": 19.3,
+ "content_p95": 25.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.2,
+ "content_p50": 20.5,
+ "content_p95": 27.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 58,
+ "mbps": 9.12,
+ "content_p50": 20.1,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 57,
+ "mbps": 9.29,
+ "content_p50": 19.4,
+ "content_p95": 25.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 57,
+ "mbps": 9.29,
+ "content_p50": 19.9,
+ "content_p95": 23.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 58,
+ "mbps": 8.8,
+ "content_p50": 19.8,
+ "content_p95": 23.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 58,
+ "mbps": 9.34,
+ "content_p50": 20.3,
+ "content_p95": 28.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 10,
+ "mbps": 1.68,
+ "content_p50": 19.6,
+ "content_p95": 22.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.5,
+ "content_p95": 25.2,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 6.75,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 40,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 64.3,
+ "cpu": {
+ "mac_agent_pct": 8.6,
+ "frame_viewer_pct": 73.0,
+ "frame_tailscaled_pct": 8.0,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 7.8,
+ "frame_total_pct": 30.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 1.26,
+ "panel": "valve.steam.desktopgame.2001204762",
+ "src": "separate:5722"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 912,
+ "frames_drawn": 912,
+ "frames_shown": 618,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 912
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 912
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 912
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 912
+ },
+ "network": {
+ "p50": 4.6,
+ "p95": 8.9,
+ "p99": 13.7,
+ "n": 912
+ },
+ "decode": {
+ "p50": 1.2,
+ "p95": 2.2,
+ "p99": 3.9,
+ "n": 912
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 1.0,
+ "n": 912
+ },
+ "present": {
+ "p50": 5.4,
+ "p95": 17.0,
+ "p99": 19.3,
+ "n": 618
+ },
+ "content": {
+ "p50": 10.6,
+ "p95": 15.0,
+ "p99": 19.9,
+ "n": 912
+ },
+ "content_shown": {
+ "p50": 16.0,
+ "p95": 27.7,
+ "p99": 30.8,
+ "n": 618
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 40.6,
+ "late_pct": 0.99,
+ "stall_max": 37.7,
+ "stalls_over_100ms": 0,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1034,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 30.9,
+ "p95": 103.7,
+ "p99": 196.7,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 36.3,
+ "p95": 106.8,
+ "p99": 200.8,
+ "n": 19
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 13.9,
+ "p95": 74.2,
+ "p99": 183.2,
+ "n": 30
+ },
+ "mac": {
+ "p50": 5.5,
+ "p95": 16.4,
+ "p99": 18.7,
+ "n": 30
+ },
+ "back": {
+ "p50": 11.2,
+ "p95": 15.5,
+ "p99": 20.2,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 9.4,
+ "content_p95": 13.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.5,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.4,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.6,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.1,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 10.5,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.3,
+ "content_p95": 13.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.3,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.1,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.9,
+ "content_p95": 14.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.4,
+ "content_p95": 15.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 11.5,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 13.3,
+ "content_p95": 17.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.1,
+ "content_p95": 16.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.5,
+ "content_p95": 16.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.09,
+ "content_p50": 12.9,
+ "content_p95": 15.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.6,
+ "content_p95": 15.0,
+ "input_p50": 30.9,
+ "input_p95": 103.7,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "acceptable",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.625,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 40,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 68.0,
+ "cpu": {
+ "mac_agent_pct": 15.7,
+ "frame_viewer_pct": 52.1,
+ "frame_tailscaled_pct": 8.9,
+ "frame_sshd_pct": 1.3,
+ "frame_gamescope_pct": 9.1,
+ "frame_total_pct": 23.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.26,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 870,
+ "frames_drawn": 870,
+ "frames_shown": 627,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.4,
+ "p95": 0.4,
+ "p99": 0.8,
+ "n": 870
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 870
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 8.5,
+ "p99": 13.9,
+ "n": 870
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 870
+ },
+ "network": {
+ "p50": 6.3,
+ "p95": 27.2,
+ "p99": 133.0,
+ "n": 870
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 15.5,
+ "p99": 21.7,
+ "n": 870
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.5,
+ "p99": 1.9,
+ "n": 870
+ },
+ "present": {
+ "p50": 3.6,
+ "p95": 16.4,
+ "p99": 21.1,
+ "n": 627
+ },
+ "content": {
+ "p50": 20.5,
+ "p95": 45.9,
+ "p99": 170.0,
+ "n": 870
+ },
+ "content_shown": {
+ "p50": 27.1,
+ "p95": 55.8,
+ "p99": 200.4,
+ "n": 627
+ }
+ },
+ "fps": 57.3,
+ "fps_shown": 41.3,
+ "late_pct": 4.6,
+ "stall_max": 321.6,
+ "stalls_over_100ms": 2,
+ "mbps": 9.4,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 986,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 56,
+ "mbps": 9.16,
+ "content_p50": 27.2,
+ "content_p95": 49.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 55,
+ "mbps": 9.46,
+ "content_p50": 23.6,
+ "content_p95": 237.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 58,
+ "mbps": 8.98,
+ "content_p50": 21.5,
+ "content_p95": 26.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 58,
+ "mbps": 9.81,
+ "content_p50": 22.4,
+ "content_p95": 55.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.66,
+ "content_p50": 21.1,
+ "content_p95": 31.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 58,
+ "mbps": 8.89,
+ "content_p50": 19.9,
+ "content_p95": 24.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 11.75,
+ "content_p50": 20.5,
+ "content_p95": 74.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 9.0,
+ "content_p50": 20.1,
+ "content_p95": 27.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 57,
+ "mbps": 9.35,
+ "content_p50": 20.5,
+ "content_p95": 24.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.17,
+ "content_p50": 19.8,
+ "content_p95": 24.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 8.89,
+ "content_p50": 19.3,
+ "content_p95": 25.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 9.09,
+ "content_p50": 18.9,
+ "content_p95": 22.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 58,
+ "mbps": 9.74,
+ "content_p50": 19.7,
+ "content_p95": 31.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 57,
+ "mbps": 8.62,
+ "content_p50": 18.5,
+ "content_p95": 21.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 58,
+ "mbps": 9.36,
+ "content_p50": 25.9,
+ "content_p95": 37.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 11,
+ "mbps": 1.84,
+ "content_p50": 21.5,
+ "content_p95": 123.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 20.5,
+ "content_p95": 45.9,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "acceptable",
+ "late_pct": "acceptable",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 7.125,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 64.9,
+ "cpu": {
+ "mac_agent_pct": 8.8,
+ "frame_viewer_pct": 84.1,
+ "frame_tailscaled_pct": 13.1,
+ "frame_sshd_pct": -0.9,
+ "frame_gamescope_pct": 9.9,
+ "frame_total_pct": 35.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 143 Hz",
+ "show_s": 1.44,
+ "panel": "valve.steam.desktopgame.2001042553",
+ "src": "separate:5856"
+ }
+ ]
+ ],
+ "on": [
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 942,
+ "frames_drawn": 942,
+ "frames_shown": 641,
+ "duration_s": 15.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 942
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 942
+ },
+ "encode": {
+ "p50": 3.7,
+ "p95": 4.5,
+ "p99": 5.5,
+ "n": 942
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 942
+ },
+ "network": {
+ "p50": 4.0,
+ "p95": 7.1,
+ "p99": 14.6,
+ "n": 942
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.3,
+ "p99": 3.7,
+ "n": 942
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 1.0,
+ "n": 942
+ },
+ "present": {
+ "p50": 4.9,
+ "p95": 17.5,
+ "p99": 19.9,
+ "n": 641
+ },
+ "content": {
+ "p50": 9.5,
+ "p95": 13.2,
+ "p99": 23.3,
+ "n": 942
+ },
+ "content_shown": {
+ "p50": 15.0,
+ "p95": 27.3,
+ "p99": 29.9,
+ "n": 641
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 40.9,
+ "late_pct": 1.17,
+ "stall_max": 75.6,
+ "stalls_over_100ms": 0,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1064,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 27.2,
+ "p95": 51.1,
+ "p99": 60.4,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 33.7,
+ "p95": 53.0,
+ "p99": 72.9,
+ "n": 20
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 11.8,
+ "p95": 39.5,
+ "p99": 41.9,
+ "n": 30
+ },
+ "mac": {
+ "p50": 4.6,
+ "p95": 14.7,
+ "p99": 19.7,
+ "n": 30
+ },
+ "back": {
+ "p50": 9.5,
+ "p95": 14.0,
+ "p99": 15.3,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.2,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.9,
+ "content_p95": 24.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 8.7,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.6,
+ "content_p95": 11.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.0,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 8.6,
+ "content_p95": 10.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 8.5,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.6,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 8.0,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.1,
+ "content_p95": 14.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.2,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.8,
+ "content_p95": 14.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.6,
+ "content_p95": 12.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.4,
+ "content_p95": 15.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 11.0,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 42,
+ "mbps": 0.31,
+ "content_p50": 11.2,
+ "content_p95": 14.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.5,
+ "content_p95": 13.2,
+ "input_p50": 27.2,
+ "input_p95": 51.1,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.541,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 52.292,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 67.8,
+ "cpu": {
+ "mac_agent_pct": 12.1,
+ "frame_viewer_pct": 45.2,
+ "frame_tailscaled_pct": 5.1,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 7.9,
+ "frame_total_pct": 27.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 1.3,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 857,
+ "frames_drawn": 857,
+ "frames_shown": 618,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -2.9,
+ "p95": 4.5,
+ "p99": 6.3,
+ "n": 857
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 857
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 9.0,
+ "p99": 15.0,
+ "n": 857
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 857
+ },
+ "network": {
+ "p50": 5.2,
+ "p95": 12.8,
+ "p99": 25.4,
+ "n": 857
+ },
+ "decode": {
+ "p50": 5.8,
+ "p95": 10.4,
+ "p99": 13.5,
+ "n": 857
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.4,
+ "p99": 1.8,
+ "n": 857
+ },
+ "present": {
+ "p50": 4.7,
+ "p95": 16.8,
+ "p99": 20.3,
+ "n": 618
+ },
+ "content": {
+ "p50": 19.5,
+ "p95": 31.7,
+ "p99": 45.5,
+ "n": 857
+ },
+ "content_shown": {
+ "p50": 25.7,
+ "p95": 38.5,
+ "p99": 47.8,
+ "n": 618
+ }
+ },
+ "fps": 56.4,
+ "fps_shown": 40.7,
+ "late_pct": 5.6,
+ "stall_max": 120.5,
+ "stalls_over_100ms": 1,
+ "mbps": 9.4,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 971,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 53,
+ "mbps": 9.14,
+ "content_p50": 20.2,
+ "content_p95": 40.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 58,
+ "mbps": 9.36,
+ "content_p50": 20.2,
+ "content_p95": 43.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 54,
+ "mbps": 9.42,
+ "content_p50": 18.9,
+ "content_p95": 28.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.46,
+ "content_p50": 18.1,
+ "content_p95": 25.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 56,
+ "mbps": 8.81,
+ "content_p50": 18.9,
+ "content_p95": 25.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 11.11,
+ "content_p50": 18.6,
+ "content_p95": 34.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 57,
+ "mbps": 9.96,
+ "content_p50": 18.1,
+ "content_p95": 24.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.79,
+ "content_p50": 18.4,
+ "content_p95": 27.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.1,
+ "content_p50": 20.3,
+ "content_p95": 27.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 56,
+ "mbps": 9.65,
+ "content_p50": 19.3,
+ "content_p95": 28.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 56,
+ "mbps": 9.15,
+ "content_p50": 20.4,
+ "content_p95": 34.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 57,
+ "mbps": 9.46,
+ "content_p50": 20.4,
+ "content_p95": 31.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 57,
+ "mbps": 9.53,
+ "content_p50": 20.4,
+ "content_p95": 31.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 57,
+ "mbps": 8.94,
+ "content_p50": 20.2,
+ "content_p95": 25.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.2,
+ "content_p50": 19.4,
+ "content_p95": 27.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 10,
+ "mbps": 1.69,
+ "content_p50": 18.1,
+ "content_p95": 23.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.5,
+ "content_p95": 31.7,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.666,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 47.293,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 63.9,
+ "cpu": {
+ "mac_agent_pct": 8.5,
+ "frame_viewer_pct": 72.5,
+ "frame_tailscaled_pct": 8.3,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 7.1,
+ "frame_total_pct": 30.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001321860",
+ "src": "separate:5452"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 740,
+ "frames_drawn": 740,
+ "frames_shown": 520,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 740
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 13.2,
+ "p99": 19.4,
+ "n": 740
+ },
+ "encode": {
+ "p50": 4.1,
+ "p95": 4.8,
+ "p99": 5.2,
+ "n": 740
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 740
+ },
+ "network": {
+ "p50": 3.6,
+ "p95": 125.3,
+ "p99": 165.0,
+ "n": 740
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 6.1,
+ "p99": 10.2,
+ "n": 740
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.6,
+ "p99": 1.1,
+ "n": 740
+ },
+ "present": {
+ "p50": 0.9,
+ "p95": 15.9,
+ "p99": 20.1,
+ "n": 520
+ },
+ "content": {
+ "p50": 9.5,
+ "p95": 137.4,
+ "p99": 173.0,
+ "n": 740
+ },
+ "content_shown": {
+ "p50": 14.9,
+ "p95": 66.7,
+ "p99": 174.2,
+ "n": 520
+ }
+ },
+ "fps": 48.7,
+ "fps_shown": 34.2,
+ "late_pct": 16.35,
+ "stall_max": 267.4,
+ "stalls_over_100ms": 16,
+ "mbps": 0.36,
+ "keyframes": 2,
+ "size": "960x540",
+ "captured": 1032,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 32.1,
+ "p95": 63.7,
+ "p99": 188.2,
+ "n": 29
+ },
+ "input_shown_ms": {
+ "p50": 36.2,
+ "p95": 64.1,
+ "p99": 188.9,
+ "n": 20
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 10.5,
+ "p95": 31.9,
+ "p99": 47.7,
+ "n": 29
+ },
+ "mac": {
+ "p50": 5.4,
+ "p95": 19.2,
+ "p99": 30.2,
+ "n": 29
+ },
+ "back": {
+ "p50": 9.7,
+ "p95": 29.3,
+ "p99": 168.5,
+ "n": 29
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 29
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.6,
+ "content_p95": 10.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.8,
+ "content_p95": 11.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.6,
+ "content_p95": 10.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.3,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 8.6,
+ "content_p95": 10.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.56,
+ "content_p50": 9.1,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 9.1,
+ "content_p95": 25.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.2,
+ "content_p95": 57.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 52,
+ "mbps": 0.4,
+ "content_p50": 22.0,
+ "content_p95": 159.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 47,
+ "mbps": 0.35,
+ "content_p50": 90.3,
+ "content_p95": 164.7,
+ "bitrate": 2764800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 45,
+ "mbps": 0.34,
+ "content_p50": 93.8,
+ "content_p95": 166.0,
+ "bitrate": 345600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 21,
+ "mbps": 0.18,
+ "content_p50": 81.0,
+ "content_p95": 177.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 27,
+ "mbps": 0.13,
+ "content_p50": 29.0,
+ "content_p95": 176.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 31,
+ "mbps": 0.11,
+ "content_p50": 21.7,
+ "content_p95": 29.4,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 31,
+ "mbps": 0.11,
+ "content_p50": 19.6,
+ "content_p95": 29.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 7,
+ "mbps": 0.02,
+ "content_p50": 21.9,
+ "content_p95": 29.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.5,
+ "content_p95": 137.4,
+ "input_p50": 32.1,
+ "input_p95": 63.7,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "bad",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.625,
+ "ceiling": 5529600,
+ "fps": 30,
+ "inFlight": 3,
+ "scale": 0.5,
+ "slack": 86.958,
+ "target": 917248,
+ "tier": 4
+ },
+ "events": [
+ {
+ "t": 9.22,
+ "e": "down to 2764 kbit/s: queue 25 ms, held 4, oldest 0 ms, base 5 ms, sent 354 got 379"
+ },
+ {
+ "t": 10.02,
+ "e": "down to 1382 kbit/s: queue 99 ms, held 5, oldest 0 ms, base 5 ms, sent 346 got 346"
+ },
+ {
+ "t": 10.32,
+ "e": "down to 691 kbit/s: queue 109 ms, held 4, oldest 15 ms, base 5 ms, sent 316 got 309"
+ },
+ {
+ "t": 10.82,
+ "e": "down to 345 kbit/s: queue 105 ms, held 5, oldest 177 ms, base 5 ms, sent 304 got 243"
+ },
+ {
+ "t": 11.22,
+ "e": "down to 300 kbit/s: queue 118 ms, held 0, oldest 42 ms, base 5 ms, sent 302 got 332"
+ },
+ {
+ "t": 11.42,
+ "e": "tier 4"
+ },
+ {
+ "t": 11.62,
+ "e": "down to 300 kbit/s: queue 140 ms, held 3, oldest 0 ms, base 5 ms, sent 291 got 291"
+ },
+ {
+ "t": 12.22,
+ "e": "down to 300 kbit/s: queue 61 ms, held 2, oldest 1 ms, base 5 ms, sent 152 got 137"
+ },
+ {
+ "t": 12.52,
+ "e": "down to 300 kbit/s: queue 102 ms, held 0, oldest 4 ms, base 5 ms, sent 156 got 146"
+ }
+ ],
+ "source_fps": 67.9,
+ "cpu": {
+ "mac_agent_pct": 13.4,
+ "frame_viewer_pct": 35.6,
+ "frame_tailscaled_pct": 4.1,
+ "frame_sshd_pct": 0.5,
+ "frame_gamescope_pct": 7.0,
+ "frame_total_pct": 24.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 138 Hz",
+ "show_s": 1.29,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 852,
+ "frames_drawn": 852,
+ "frames_shown": 607,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.5,
+ "p95": 0.5,
+ "p99": 6.7,
+ "n": 852
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.5,
+ "n": 852
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 9.3,
+ "p99": 16.5,
+ "n": 852
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 852
+ },
+ "network": {
+ "p50": 5.9,
+ "p95": 15.8,
+ "p99": 58.8,
+ "n": 852
+ },
+ "decode": {
+ "p50": 5.8,
+ "p95": 11.6,
+ "p99": 23.2,
+ "n": 852
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.3,
+ "p99": 1.8,
+ "n": 852
+ },
+ "present": {
+ "p50": 4.2,
+ "p95": 16.4,
+ "p99": 20.1,
+ "n": 607
+ },
+ "content": {
+ "p50": 19.6,
+ "p95": 37.9,
+ "p99": 106.2,
+ "n": 852
+ },
+ "content_shown": {
+ "p50": 25.7,
+ "p95": 45.1,
+ "p99": 110.7,
+ "n": 607
+ }
+ },
+ "fps": 56.2,
+ "fps_shown": 40.0,
+ "late_pct": 5.52,
+ "stall_max": 147.0,
+ "stalls_over_100ms": 3,
+ "mbps": 9.36,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 982,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 44,
+ "mbps": 8.61,
+ "content_p50": 29.3,
+ "content_p95": 126.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 55,
+ "mbps": 9.63,
+ "content_p50": 19.5,
+ "content_p95": 29.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 9.2,
+ "content_p50": 20.4,
+ "content_p95": 71.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 53,
+ "mbps": 9.04,
+ "content_p50": 19.5,
+ "content_p95": 36.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 9.08,
+ "content_p50": 18.9,
+ "content_p95": 22.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 9.22,
+ "content_p50": 19.3,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 57,
+ "mbps": 11.78,
+ "content_p50": 20.5,
+ "content_p95": 37.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.73,
+ "content_p50": 19.2,
+ "content_p95": 24.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.29,
+ "content_p50": 19.1,
+ "content_p95": 23.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 58,
+ "mbps": 9.5,
+ "content_p50": 18.9,
+ "content_p95": 24.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 58,
+ "mbps": 8.58,
+ "content_p50": 19.2,
+ "content_p95": 23.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 57,
+ "mbps": 9.44,
+ "content_p50": 20.2,
+ "content_p95": 28.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 58,
+ "mbps": 9.7,
+ "content_p50": 20.1,
+ "content_p95": 37.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 58,
+ "mbps": 9.15,
+ "content_p50": 20.8,
+ "content_p95": 41.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 58,
+ "mbps": 9.29,
+ "content_p50": 19.7,
+ "content_p95": 23.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 9,
+ "mbps": 1.64,
+ "content_p50": 20.9,
+ "content_p95": 38.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.6,
+ "content_p95": 37.9,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.625,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 48.604,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 64.6,
+ "cpu": {
+ "mac_agent_pct": 8.3,
+ "frame_viewer_pct": 71.7,
+ "frame_tailscaled_pct": 8.0,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 7.2,
+ "frame_total_pct": 29.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001299121",
+ "src": "separate:5587"
+ }
+ ],
+ [
+ {
+ "scenario": "test",
+ "frames_sent": 908,
+ "frames_drawn": 908,
+ "frames_shown": 382,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 908
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.2,
+ "n": 908
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 908
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 908
+ },
+ "network": {
+ "p50": 4.2,
+ "p95": 7.5,
+ "p99": 10.1,
+ "n": 908
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 4.0,
+ "p99": 4.6,
+ "n": 908
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 1.1,
+ "p99": 1.3,
+ "n": 908
+ },
+ "present": {
+ "p50": 6.2,
+ "p95": 17.0,
+ "p99": 18.3,
+ "n": 382
+ },
+ "content": {
+ "p50": 10.5,
+ "p95": 14.2,
+ "p99": 16.6,
+ "n": 908
+ },
+ "content_shown": {
+ "p50": 17.1,
+ "p95": 27.2,
+ "p99": 29.4,
+ "n": 382
+ }
+ },
+ "fps": 59.7,
+ "fps_shown": 25.1,
+ "late_pct": 0.33,
+ "stall_max": 122.8,
+ "stalls_over_100ms": 1,
+ "mbps": 0.47,
+ "keyframes": 1,
+ "size": "1280x720",
+ "captured": 1033,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 25.2,
+ "p95": 33.5,
+ "p99": 34.1,
+ "n": 30
+ },
+ "input_shown_ms": {
+ "p50": 32.4,
+ "p95": 43.4,
+ "p99": 43.4,
+ "n": 9
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.9,
+ "p95": 17.6,
+ "p99": 18.3,
+ "n": 30
+ },
+ "mac": {
+ "p50": 6.2,
+ "p95": 14.5,
+ "p99": 20.8,
+ "n": 30
+ },
+ "back": {
+ "p50": 10.7,
+ "p95": 13.5,
+ "p99": 14.8,
+ "n": 30
+ }
+ },
+ "inputs": {
+ "sent": 30,
+ "seen": 30
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.7,
+ "content_p95": 15.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 12.1,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 11.0,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.9,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.3,
+ "content_p95": 13.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.56,
+ "content_p50": 10.3,
+ "content_p95": 13.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.3,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 56,
+ "mbps": 0.44,
+ "content_p50": 8.9,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.4,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.6,
+ "content_p95": 11.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.1,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.1,
+ "content_p95": 13.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.3,
+ "content_p95": 14.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 12.3,
+ "content_p95": 14.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.4,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 12,
+ "mbps": 0.09,
+ "content_p50": 12.5,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.5,
+ "content_p95": 14.2,
+ "input_p50": 25.2,
+ "input_p95": 33.5,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.167,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 49.102,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 68.0,
+ "cpu": {
+ "mac_agent_pct": 15.5,
+ "frame_viewer_pct": 51.6,
+ "frame_tailscaled_pct": 5.2,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 9.5,
+ "frame_total_pct": 33.4
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 135 Hz",
+ "show_s": 1.37,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 843,
+ "frames_drawn": 843,
+ "frames_shown": 414,
+ "duration_s": 15.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.7,
+ "p95": 0.5,
+ "p99": 1.3,
+ "n": 843
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 843
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 9.2,
+ "p99": 17.1,
+ "n": 843
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 843
+ },
+ "network": {
+ "p50": 6.4,
+ "p95": 14.7,
+ "p99": 68.7,
+ "n": 843
+ },
+ "decode": {
+ "p50": 6.0,
+ "p95": 11.7,
+ "p99": 16.1,
+ "n": 843
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.4,
+ "p99": 1.7,
+ "n": 843
+ },
+ "present": {
+ "p50": 4.0,
+ "p95": 18.1,
+ "p99": 25.9,
+ "n": 414
+ },
+ "content": {
+ "p50": 20.5,
+ "p95": 33.6,
+ "p99": 103.0,
+ "n": 843
+ },
+ "content_shown": {
+ "p50": 26.1,
+ "p95": 41.0,
+ "p99": 122.7,
+ "n": 414
+ }
+ },
+ "fps": 55.6,
+ "fps_shown": 27.3,
+ "late_pct": 6.29,
+ "stall_max": 153.6,
+ "stalls_over_100ms": 3,
+ "mbps": 9.25,
+ "keyframes": 1,
+ "size": "1920x1290",
+ "captured": 970,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 53,
+ "mbps": 9.13,
+ "content_p50": 22.1,
+ "content_p95": 110.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 53,
+ "mbps": 9.46,
+ "content_p50": 20.7,
+ "content_p95": 27.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 56,
+ "mbps": 9.19,
+ "content_p50": 20.2,
+ "content_p95": 26.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.36,
+ "content_p50": 20.1,
+ "content_p95": 27.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 9.66,
+ "content_p50": 19.9,
+ "content_p95": 24.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 8.68,
+ "content_p50": 20.5,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 46,
+ "mbps": 10.48,
+ "content_p50": 25.4,
+ "content_p95": 126.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 56,
+ "mbps": 9.16,
+ "content_p50": 20.1,
+ "content_p95": 35.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 57,
+ "mbps": 8.76,
+ "content_p50": 21.1,
+ "content_p95": 28.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.29,
+ "content_p50": 20.5,
+ "content_p95": 24.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 8.87,
+ "content_p50": 20.4,
+ "content_p95": 40.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 9.17,
+ "content_p50": 20.3,
+ "content_p95": 23.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 56,
+ "mbps": 9.05,
+ "content_p50": 19.5,
+ "content_p95": 23.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 56,
+ "mbps": 8.85,
+ "content_p50": 20.8,
+ "content_p95": 31.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 9.48,
+ "content_p50": 20.6,
+ "content_p95": 34.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 10,
+ "mbps": 1.65,
+ "content_p50": 24.8,
+ "content_p95": 39.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 20.5,
+ "content_p95": 33.6,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 7.167,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 48.229,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 63.8,
+ "cpu": {
+ "mac_agent_pct": 8.5,
+ "frame_viewer_pct": 78.1,
+ "frame_tailscaled_pct": 8.2,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 9.0,
+ "frame_total_pct": 29.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.26,
+ "panel": "valve.steam.desktopgame.2001733678",
+ "src": "separate:5994"
+ }
+ ]
+ ]
+ },
+ "table": {
+ "test": {
+ "content_p50": {
+ "off": [
+ 9.5,
+ 9.7,
+ 10.6
+ ],
+ "on": [
+ 9.5,
+ 9.5,
+ 10.5
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 16.2,
+ 14.3,
+ 15.0
+ ],
+ "on": [
+ 13.2,
+ 137.4,
+ 14.2
+ ]
+ },
+ "input_p50": {
+ "off": [
+ 28.3,
+ 22.6,
+ 30.9
+ ],
+ "on": [
+ 27.2,
+ 32.1,
+ 25.2
+ ]
+ },
+ "input_p95": {
+ "off": [
+ 38.3,
+ 32.7,
+ 103.7
+ ],
+ "on": [
+ 51.1,
+ 63.7,
+ 33.5
+ ]
+ },
+ "fps": {
+ "off": [
+ 59.9,
+ 60.0,
+ 60.0
+ ],
+ "on": [
+ 60.0,
+ 48.7,
+ 59.7
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 2.05,
+ 0.33,
+ 0.99
+ ],
+ "on": [
+ 1.17,
+ 16.35,
+ 0.33
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 150.2,
+ 120.5,
+ 37.7
+ ],
+ "on": [
+ 75.6,
+ 267.4,
+ 122.8
+ ]
+ },
+ "mbps": {
+ "off": [
+ 0.47,
+ 0.48,
+ 0.47
+ ],
+ "on": [
+ 0.47,
+ 0.36,
+ 0.47
+ ]
+ }
+ },
+ "scroll": {
+ "content_p50": {
+ "off": [
+ 20.0,
+ 19.5,
+ 20.5
+ ],
+ "on": [
+ 19.5,
+ 19.6,
+ 20.5
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 32.3,
+ 25.2,
+ 45.9
+ ],
+ "on": [
+ 31.7,
+ 37.9,
+ 33.6
+ ]
+ },
+ "input_p50": {
+ "off": [],
+ "on": []
+ },
+ "input_p95": {
+ "off": [],
+ "on": []
+ },
+ "fps": {
+ "off": [
+ 54.9,
+ 57.0,
+ 57.3
+ ],
+ "on": [
+ 56.4,
+ 56.2,
+ 55.6
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 6.59,
+ 3.36,
+ 4.6
+ ],
+ "on": [
+ 5.6,
+ 5.52,
+ 6.29
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 84.3,
+ 105.4,
+ 321.6
+ ],
+ "on": [
+ 120.5,
+ 147.0,
+ 153.6
+ ]
+ },
+ "mbps": {
+ "off": [
+ 9.44,
+ 9.44,
+ 9.4
+ ],
+ "on": [
+ 9.4,
+ 9.36,
+ 9.25
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-step3.json b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-step3.json
new file mode 100644
index 0000000..332db76
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-ab-adapt-step3.json
@@ -0,0 +1,3743 @@
+{
+ "label": "ab-adapt-step3",
+ "date": "2026-09-28T20:24:10",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "50@0,3@8,50@16",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 24.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "8:23.135290 [Info] - 0 - leaving standby",
+ "scenarios": [],
+ "arms": [
+ "off:FRAME_MAC_VIEW_ADAPT=0",
+ "on:"
+ ],
+ "runs": {
+ "off": [
+ [
+ {
+ "scenario": "scroll",
+ "frames_sent": 1179,
+ "frames_drawn": 1110,
+ "frames_shown": 576,
+ "duration_s": 24.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.7,
+ "p95": 0.5,
+ "p99": 4.3,
+ "n": 1179
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.3,
+ "n": 1179
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 8.7,
+ "p99": 13.8,
+ "n": 1179
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 1690.5,
+ "p99": 2066.6,
+ "n": 1179
+ },
+ "network": {
+ "p50": 13.6,
+ "p95": 4872.0,
+ "p99": 5491.7,
+ "n": 1179
+ },
+ "decode": {
+ "p50": 6.0,
+ "p95": 46.0,
+ "p99": 75.8,
+ "n": 1110
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.3,
+ "p99": 2.0,
+ "n": 1110
+ },
+ "present": {
+ "p50": 5.1,
+ "p95": 34.3,
+ "p99": 44.3,
+ "n": 576
+ },
+ "content": {
+ "p50": 27.5,
+ "p95": 4986.4,
+ "p99": 5514.6,
+ "n": 1110
+ },
+ "content_shown": {
+ "p50": 36.0,
+ "p95": 4759.3,
+ "p99": 5454.2,
+ "n": 576
+ }
+ },
+ "fps": 45.8,
+ "fps_shown": 23.8,
+ "late_pct": 19.19,
+ "stall_max": 232.1,
+ "stalls_over_100ms": 5,
+ "mbps": 8.62,
+ "keyframes": 3,
+ "size": "1920x1290",
+ "captured": 1500,
+ "viewer_never_drawn": 69,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 55,
+ "mbps": 9.44,
+ "content_p50": 22.8,
+ "content_p95": 36.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 57,
+ "mbps": 9.75,
+ "content_p50": 25.3,
+ "content_p95": 139.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 58,
+ "mbps": 9.8,
+ "content_p50": 24.2,
+ "content_p95": 110.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 58,
+ "mbps": 9.52,
+ "content_p50": 22.5,
+ "content_p95": 37.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 56,
+ "mbps": 8.91,
+ "content_p50": 23.4,
+ "content_p95": 30.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 53,
+ "mbps": 8.91,
+ "content_p50": 23.4,
+ "content_p95": 77.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 12.0,
+ "content_p50": 22.9,
+ "content_p95": 54.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 58,
+ "mbps": 8.82,
+ "content_p50": 21.8,
+ "content_p95": 28.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 58,
+ "mbps": 9.48,
+ "content_p50": 1079.9,
+ "content_p95": 2016.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 57,
+ "mbps": 9.19,
+ "content_p50": 3208.0,
+ "content_p95": 4125.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 58,
+ "mbps": 8.91,
+ "content_p50": 5139.1,
+ "content_p95": 5585.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 29,
+ "mbps": 6.33,
+ "content_p50": 5245.6,
+ "content_p95": 5430.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 10,
+ "mbps": 2.92,
+ "content_p50": 4133.7,
+ "content_p95": 4182.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 9,
+ "mbps": 4.39,
+ "content_p50": 3552.8,
+ "content_p95": 3589.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 25,
+ "mbps": 5.77,
+ "content_p50": 2820.8,
+ "content_p95": 2926.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 10,
+ "mbps": 2.52,
+ "content_p50": 1747.5,
+ "content_p95": 1791.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 13,
+ "mbps": 10.51,
+ "content_p50": 1139.0,
+ "content_p95": 1179.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 31,
+ "mbps": 13.74,
+ "content_p50": 164.3,
+ "content_p95": 295.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 57,
+ "mbps": 9.05,
+ "content_p50": 24.4,
+ "content_p95": 30.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 57,
+ "mbps": 9.45,
+ "content_p50": 29.5,
+ "content_p95": 43.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 58,
+ "mbps": 9.16,
+ "content_p50": 27.3,
+ "content_p95": 63.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 58,
+ "mbps": 9.75,
+ "content_p50": 26.9,
+ "content_p95": 40.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 57,
+ "mbps": 9.45,
+ "content_p50": 28.5,
+ "content_p95": 34.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 58,
+ "mbps": 9.21,
+ "content_p50": 25.9,
+ "content_p95": 30.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 12,
+ "mbps": 1.9,
+ "content_p50": 25.9,
+ "content_p95": 28.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 63.8,
+ "worst_p95": 5585.2,
+ "settled_s": 10.0
+ }
+ ],
+ "content_p50": 27.5,
+ "content_p95": 4986.4,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "acceptable",
+ "content_p95": "bad",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable",
+ "adapt@8s": "bad"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 10.625,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 62.0,
+ "cpu": {
+ "mac_agent_pct": 7.7,
+ "frame_viewer_pct": 62.6,
+ "frame_tailscaled_pct": 20.6,
+ "frame_sshd_pct": 2.4,
+ "frame_gamescope_pct": 9.7,
+ "frame_total_pct": 44.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.28,
+ "panel": "valve.steam.desktopgame.2001525774",
+ "src": "separate:6129"
+ },
+ {
+ "scenario": "test",
+ "frames_sent": 1464,
+ "frames_drawn": 1464,
+ "frames_shown": 1198,
+ "duration_s": 24.4,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1464
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1464
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.9,
+ "p99": 5.2,
+ "n": 1464
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 1464
+ },
+ "network": {
+ "p50": 6.8,
+ "p95": 102.3,
+ "p99": 162.7,
+ "n": 1464
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 10.7,
+ "p99": 16.6,
+ "n": 1464
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 1464
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 12.6,
+ "p99": 15.1,
+ "n": 1198
+ },
+ "content": {
+ "p50": 12.8,
+ "p95": 116.8,
+ "p99": 168.7,
+ "n": 1464
+ },
+ "content_shown": {
+ "p50": 14.7,
+ "p95": 30.1,
+ "p99": 173.8,
+ "n": 1198
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 49.1,
+ "late_pct": 3.69,
+ "stall_max": 185.4,
+ "stalls_over_100ms": 16,
+ "mbps": 0.49,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1584,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 34.4,
+ "p95": 86.0,
+ "p99": 207.9,
+ "n": 48
+ },
+ "input_shown_ms": {
+ "p50": 34.9,
+ "p95": 86.7,
+ "p99": 208.5,
+ "n": 46
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 13.9,
+ "p95": 29.5,
+ "p99": 55.9,
+ "n": 48
+ },
+ "mac": {
+ "p50": 6.5,
+ "p95": 16.2,
+ "p99": 18.2,
+ "n": 48
+ },
+ "back": {
+ "p50": 12.8,
+ "p95": 48.6,
+ "p99": 178.9,
+ "n": 48
+ }
+ },
+ "inputs": {
+ "sent": 48,
+ "seen": 48
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.52,
+ "content_p50": 11.7,
+ "content_p95": 15.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 11.8,
+ "content_p95": 16.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 11.2,
+ "content_p95": 16.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.3,
+ "content_p95": 14.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 11.1,
+ "content_p95": 15.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 10.7,
+ "content_p95": 13.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 11.6,
+ "content_p95": 15.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 11.9,
+ "content_p95": 14.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 15.7,
+ "content_p95": 19.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 15.3,
+ "content_p95": 19.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 16.1,
+ "content_p95": 55.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 23.0,
+ "content_p95": 71.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 72.3,
+ "content_p95": 174.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.5,
+ "content_p50": 79.0,
+ "content_p95": 173.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 48.3,
+ "content_p95": 163.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 68.1,
+ "content_p95": 168.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 13.8,
+ "content_p95": 125.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 13.7,
+ "content_p95": 19.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.6,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 12.1,
+ "content_p95": 16.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 12.1,
+ "content_p95": 21.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.0,
+ "content_p95": 14.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 12.5,
+ "content_p95": 15.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.8,
+ "content_p95": 13.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 24,
+ "mbps": 0.17,
+ "content_p50": 10.2,
+ "content_p95": 14.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 40.0,
+ "worst_p95": 174.5,
+ "settled_s": 9.0
+ }
+ ],
+ "content_p50": 12.8,
+ "content_p95": 116.8,
+ "input_p50": 34.4,
+ "input_p95": 86.0,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "bad",
+ "input_p50": "local",
+ "input_p95": "acceptable",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable",
+ "adapt@8s": "bad"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 6.042,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 40,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 64.9,
+ "cpu": {
+ "mac_agent_pct": 14.9,
+ "frame_viewer_pct": 43.7,
+ "frame_tailscaled_pct": 21.3,
+ "frame_sshd_pct": -1.7,
+ "frame_gamescope_pct": 9.8,
+ "frame_total_pct": 42.4
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 72 Hz",
+ "show_s": 1.42,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ }
+ ],
+ [
+ {
+ "scenario": "scroll",
+ "frames_sent": 1168,
+ "frames_drawn": 1081,
+ "frames_shown": 565,
+ "duration_s": 24.3,
+ "stages_ms": {
+ "capture": {
+ "p50": -7.8,
+ "p95": 0.4,
+ "p99": 3.8,
+ "n": 1168
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1168
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.3,
+ "p99": 15.8,
+ "n": 1168
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 1496.6,
+ "p99": 2773.0,
+ "n": 1168
+ },
+ "network": {
+ "p50": 9.8,
+ "p95": 4895.9,
+ "p99": 5418.1,
+ "n": 1168
+ },
+ "decode": {
+ "p50": 5.9,
+ "p95": 17.7,
+ "p99": 65.2,
+ "n": 1081
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.3,
+ "p99": 1.8,
+ "n": 1081
+ },
+ "present": {
+ "p50": 5.3,
+ "p95": 30.8,
+ "p99": 41.9,
+ "n": 565
+ },
+ "content": {
+ "p50": 23.8,
+ "p95": 4355.1,
+ "p99": 5463.9,
+ "n": 1081
+ },
+ "content_shown": {
+ "p50": 32.9,
+ "p95": 4606.2,
+ "p99": 5408.3,
+ "n": 565
+ }
+ },
+ "fps": 44.5,
+ "fps_shown": 23.3,
+ "late_pct": 17.67,
+ "stall_max": 398.2,
+ "stalls_over_100ms": 2,
+ "mbps": 8.39,
+ "keyframes": 3,
+ "size": "1920x1290",
+ "captured": 1509,
+ "viewer_never_drawn": 87,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 55,
+ "mbps": 9.32,
+ "content_p50": 23.8,
+ "content_p95": 73.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 58,
+ "mbps": 9.52,
+ "content_p50": 22.8,
+ "content_p95": 28.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 58,
+ "mbps": 9.4,
+ "content_p50": 22.9,
+ "content_p95": 26.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 57,
+ "mbps": 9.62,
+ "content_p50": 23.6,
+ "content_p95": 33.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 8.86,
+ "content_p50": 22.4,
+ "content_p95": 25.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 57,
+ "mbps": 9.67,
+ "content_p50": 23.2,
+ "content_p95": 29.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 12.04,
+ "content_p50": 23.7,
+ "content_p95": 46.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 58,
+ "mbps": 8.85,
+ "content_p50": 22.5,
+ "content_p95": 27.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 57,
+ "mbps": 9.27,
+ "content_p50": 979.8,
+ "content_p95": 1991.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 58,
+ "mbps": 8.89,
+ "content_p50": 3131.5,
+ "content_p95": 3947.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 58,
+ "mbps": 9.25,
+ "content_p50": 5057.4,
+ "content_p95": 5532.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 5,
+ "mbps": 6.6,
+ "content_p50": 5369.2,
+ "content_p95": 5392.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 0,
+ "mbps": 3.01,
+ "content_p50": null,
+ "content_p95": null,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 0,
+ "mbps": 5.04,
+ "content_p50": null,
+ "content_p95": null,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 0,
+ "mbps": 1.64,
+ "content_p50": null,
+ "content_p95": null,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 0,
+ "mbps": 2.19,
+ "content_p50": null,
+ "content_p95": null,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 27,
+ "mbps": 13.34,
+ "content_p50": 332.4,
+ "content_p95": 451.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 57,
+ "mbps": 9.49,
+ "content_p50": 24.1,
+ "content_p95": 32.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 58,
+ "mbps": 9.06,
+ "content_p50": 23.0,
+ "content_p95": 28.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 58,
+ "mbps": 9.39,
+ "content_p50": 23.3,
+ "content_p95": 26.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 57,
+ "mbps": 8.89,
+ "content_p50": 23.4,
+ "content_p95": 32.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 58,
+ "mbps": 9.87,
+ "content_p50": 23.7,
+ "content_p95": 28.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 57,
+ "mbps": 9.49,
+ "content_p50": 22.2,
+ "content_p95": 29.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 57,
+ "mbps": 8.75,
+ "content_p50": 22.3,
+ "content_p95": 27.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 15,
+ "mbps": 2.32,
+ "content_p50": 22.0,
+ "content_p95": 25.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 42.7,
+ "worst_p95": 5532.6,
+ "settled_s": 9.0
+ }
+ ],
+ "content_p50": 23.8,
+ "content_p95": 4355.1,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "bad",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad",
+ "adapt@8s": "bad"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 10.292,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 62.1,
+ "cpu": {
+ "mac_agent_pct": 7.4,
+ "frame_viewer_pct": 65.4,
+ "frame_tailscaled_pct": 8.1,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 11.5,
+ "frame_total_pct": 38.7
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.29,
+ "panel": "valve.steam.desktopgame.2001170343",
+ "src": "separate:6551"
+ },
+ {
+ "scenario": "test",
+ "frames_sent": 1459,
+ "frames_drawn": 1459,
+ "frames_shown": 572,
+ "duration_s": 24.3,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1459
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1459
+ },
+ "encode": {
+ "p50": 4.1,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 1459
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 1459
+ },
+ "network": {
+ "p50": 4.6,
+ "p95": 9.0,
+ "p99": 17.9,
+ "n": 1459
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.6,
+ "p99": 4.2,
+ "n": 1459
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 1459
+ },
+ "present": {
+ "p50": 5.1,
+ "p95": 15.6,
+ "p99": 18.8,
+ "n": 572
+ },
+ "content": {
+ "p50": 10.7,
+ "p95": 15.6,
+ "p99": 24.6,
+ "n": 1459
+ },
+ "content_shown": {
+ "p50": 16.1,
+ "p95": 27.1,
+ "p99": 31.8,
+ "n": 572
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 23.5,
+ "late_pct": 1.92,
+ "stall_max": 157.3,
+ "stalls_over_100ms": 1,
+ "mbps": 0.46,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1580,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 29.6,
+ "p95": 50.1,
+ "p99": 163.0,
+ "n": 48
+ },
+ "input_shown_ms": {
+ "p50": 36.5,
+ "p95": 52.4,
+ "p99": 163.6,
+ "n": 21
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 12.0,
+ "p95": 21.1,
+ "p99": 34.4,
+ "n": 48
+ },
+ "mac": {
+ "p50": 5.5,
+ "p95": 15.1,
+ "p99": 16.3,
+ "n": 48
+ },
+ "back": {
+ "p50": 11.0,
+ "p95": 15.7,
+ "p99": 150.9,
+ "n": 48
+ }
+ },
+ "inputs": {
+ "sent": 48,
+ "seen": 48
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.3,
+ "content_p95": 14.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.8,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.1,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.0,
+ "content_p95": 12.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.6,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.9,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.56,
+ "content_p50": 10.1,
+ "content_p95": 13.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.8,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.8,
+ "content_p95": 16.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.4,
+ "content_p95": 14.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.8,
+ "content_p95": 14.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.7,
+ "content_p95": 15.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.9,
+ "content_p95": 17.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 13.3,
+ "content_p95": 16.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 13.4,
+ "content_p95": 107.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 13.4,
+ "content_p95": 18.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.55,
+ "content_p50": 9.5,
+ "content_p95": 13.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.43,
+ "content_p50": 9.3,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.8,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.5,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.7,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 9.3,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.5,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 19,
+ "mbps": 0.13,
+ "content_p50": 10.2,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 40.0,
+ "worst_p95": 107.3,
+ "settled_s": 0.0
+ }
+ ],
+ "content_p50": 10.7,
+ "content_p95": 15.6,
+ "input_p50": 29.6,
+ "input_p95": 50.1,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable",
+ "adapt@8s": "local"
+ },
+ "controller": {
+ "adapt": false,
+ "baseRtt": 5.916,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 40,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.0,
+ "cpu": {
+ "mac_agent_pct": 14.7,
+ "frame_viewer_pct": 54.2,
+ "frame_tailscaled_pct": 6.0,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 14.5,
+ "frame_total_pct": 39.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 70 Hz",
+ "show_s": 1.37,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ }
+ ]
+ ],
+ "on": [
+ [
+ {
+ "scenario": "scroll",
+ "frames_sent": 1012,
+ "frames_drawn": 1012,
+ "frames_shown": 975,
+ "duration_s": 24.2,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.2,
+ "p95": 3.0,
+ "p99": 4.5,
+ "n": 1012
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 15.9,
+ "p99": 17.4,
+ "n": 1012
+ },
+ "encode": {
+ "p50": 6.4,
+ "p95": 7.5,
+ "p99": 8.5,
+ "n": 1012
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.4,
+ "n": 1012
+ },
+ "network": {
+ "p50": 9.9,
+ "p95": 51.0,
+ "p99": 167.2,
+ "n": 1012
+ },
+ "decode": {
+ "p50": 5.2,
+ "p95": 8.7,
+ "p99": 13.2,
+ "n": 1012
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.4,
+ "p99": 1.7,
+ "n": 1012
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 11.6,
+ "p99": 14.3,
+ "n": 975
+ },
+ "content": {
+ "p50": 26.6,
+ "p95": 69.2,
+ "p99": 189.9,
+ "n": 1012
+ },
+ "content_shown": {
+ "p50": 31.6,
+ "p95": 71.1,
+ "p99": 190.4,
+ "n": 975
+ }
+ },
+ "fps": 41.8,
+ "fps_shown": 40.3,
+ "late_pct": 31.82,
+ "stall_max": 245.3,
+ "stalls_over_100ms": 5,
+ "mbps": 6.13,
+ "keyframes": 3,
+ "size": "1920x1290",
+ "captured": 1507,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 56,
+ "mbps": 9.54,
+ "content_p50": 21.9,
+ "content_p95": 42.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 58,
+ "mbps": 9.38,
+ "content_p50": 21.4,
+ "content_p95": 26.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 9.41,
+ "content_p50": 22.3,
+ "content_p95": 27.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 58,
+ "mbps": 9.67,
+ "content_p50": 23.0,
+ "content_p95": 25.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 57,
+ "mbps": 8.99,
+ "content_p50": 22.9,
+ "content_p95": 28.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 58,
+ "mbps": 9.25,
+ "content_p50": 23.3,
+ "content_p95": 32.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 57,
+ "mbps": 11.74,
+ "content_p50": 22.2,
+ "content_p95": 46.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 58,
+ "mbps": 8.89,
+ "content_p50": 21.1,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 25,
+ "mbps": 3.02,
+ "content_p50": 75.9,
+ "content_p95": 218.9,
+ "bitrate": 3715200,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 50,
+ "mbps": 3.15,
+ "content_p50": 56.5,
+ "content_p95": 84.0,
+ "bitrate": 2112376,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 24,
+ "mbps": 0.94,
+ "content_p50": 37.9,
+ "content_p95": 54.8,
+ "bitrate": 1689900,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 30,
+ "mbps": 1.93,
+ "content_p50": 48.0,
+ "content_p95": 57.0,
+ "bitrate": 2414756,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 30,
+ "mbps": 2.74,
+ "content_p50": 58.5,
+ "content_p95": 92.4,
+ "bitrate": 2611641,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 30,
+ "mbps": 2.12,
+ "content_p50": 45.3,
+ "content_p95": 79.0,
+ "bitrate": 2089312,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 30,
+ "mbps": 2.24,
+ "content_p50": 52.0,
+ "content_p95": 71.4,
+ "bitrate": 2633067,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 30,
+ "mbps": 2.47,
+ "content_p50": 53.7,
+ "content_p95": 93.6,
+ "bitrate": 2357098,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 30,
+ "mbps": 2.61,
+ "content_p50": 24.7,
+ "content_p95": 39.8,
+ "bitrate": 2957087,
+ "tier": 3,
+ "w": 1440,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 30,
+ "mbps": 3.74,
+ "content_p50": 29.5,
+ "content_p95": 38.0,
+ "bitrate": 4561519,
+ "tier": 3,
+ "w": 1440,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 30,
+ "mbps": 5.32,
+ "content_p50": 29.2,
+ "content_p95": 41.6,
+ "bitrate": 6236880,
+ "tier": 3,
+ "w": 1440,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 30,
+ "mbps": 6.62,
+ "content_p50": 35.0,
+ "content_p95": 68.5,
+ "bitrate": 8466786,
+ "tier": 2,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 30,
+ "mbps": 7.48,
+ "content_p50": 36.1,
+ "content_p95": 41.1,
+ "bitrate": 12628270,
+ "tier": 2,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 42,
+ "mbps": 7.71,
+ "content_p50": 31.7,
+ "content_p95": 39.0,
+ "bitrate": 14860800,
+ "tier": 1,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 46,
+ "mbps": 8.79,
+ "content_p50": 33.6,
+ "content_p95": 40.6,
+ "bitrate": 14860800,
+ "tier": 1,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 53,
+ "mbps": 8.78,
+ "content_p50": 28.4,
+ "content_p95": 42.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 13,
+ "mbps": 1.89,
+ "content_p50": 24.7,
+ "content_p95": 28.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 45.2,
+ "worst_p95": 218.9,
+ "settled_s": 8.0
+ }
+ ],
+ "content_p50": 26.6,
+ "content_p95": 69.2,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "acceptable",
+ "content_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "acceptable",
+ "adapt@8s": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.333,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 51.355,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 8.31,
+ "e": "down to 7430 kbit/s: queue 100 ms, held 6, oldest 197 ms, base 10 ms, sent 5528 got 5268 wants 8906"
+ },
+ {
+ "t": 8.71,
+ "e": "down to 3715 kbit/s: queue 209 ms, held 4, oldest 115 ms, base 10 ms, sent 2861 got 2421 wants 10373"
+ },
+ {
+ "t": 9.41,
+ "e": "down to 2640 kbit/s: queue 48 ms, held 0, oldest 59 ms, base 10 ms, sent 3222 got 2933 wants 3166"
+ },
+ {
+ "t": 9.71,
+ "e": "down to 2112 kbit/s: queue 48 ms, held 0, oldest 31 ms, base 10 ms, sent 2907 got 3032 wants 2906"
+ },
+ {
+ "t": 9.81,
+ "e": "tier 3"
+ },
+ {
+ "t": 10.21,
+ "e": "down to 1689 kbit/s: queue 186 ms, held 4, oldest 0 ms, base 10 ms, sent 2091 got 2288 wants 3137"
+ },
+ {
+ "t": 12.91,
+ "e": "down to 2611 kbit/s: queue 44 ms, held 0, oldest 27 ms, base 10 ms, sent 3243 got 2901 wants 3243"
+ },
+ {
+ "t": 13.31,
+ "e": "down to 2089 kbit/s: queue 41 ms, held 0, oldest 60 ms, base 10 ms, sent 2775 got 2764 wants 2775"
+ },
+ {
+ "t": 15.31,
+ "e": "down to 2357 kbit/s: queue 44 ms, held 0, oldest 28 ms, base 10 ms, sent 2539 got 2729 wants 2539"
+ },
+ {
+ "t": 19.11,
+ "e": "tier 2"
+ },
+ {
+ "t": 21.31,
+ "e": "tier 1"
+ },
+ {
+ "t": 23.51,
+ "e": "tier 0"
+ }
+ ],
+ "source_fps": 62.3,
+ "cpu": {
+ "mac_agent_pct": 7.3,
+ "frame_viewer_pct": 57.5,
+ "frame_tailscaled_pct": 7.4,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 10.6,
+ "frame_total_pct": 47.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001853011",
+ "src": "separate:6269"
+ },
+ {
+ "scenario": "test",
+ "frames_sent": 1457,
+ "frames_drawn": 1457,
+ "frames_shown": 578,
+ "duration_s": 24.3,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1457
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1457
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.9,
+ "p99": 5.2,
+ "n": 1457
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 1457
+ },
+ "network": {
+ "p50": 5.0,
+ "p95": 10.1,
+ "p99": 13.0,
+ "n": 1457
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.2,
+ "p99": 3.6,
+ "n": 1457
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 1.1,
+ "n": 1457
+ },
+ "present": {
+ "p50": 5.6,
+ "p95": 16.1,
+ "p99": 18.0,
+ "n": 578
+ },
+ "content": {
+ "p50": 11.1,
+ "p95": 16.2,
+ "p99": 19.2,
+ "n": 1457
+ },
+ "content_shown": {
+ "p50": 18.9,
+ "p95": 27.0,
+ "p99": 31.0,
+ "n": 578
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 23.8,
+ "late_pct": 0.82,
+ "stall_max": 32.9,
+ "stalls_over_100ms": 0,
+ "mbps": 0.47,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1578,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 30.0,
+ "p95": 47.7,
+ "p99": 76.0,
+ "n": 48
+ },
+ "input_shown_ms": {
+ "p50": 33.7,
+ "p95": 47.5,
+ "p99": 81.8,
+ "n": 17
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 10.2,
+ "p95": 27.4,
+ "p99": 49.5,
+ "n": 48
+ },
+ "mac": {
+ "p50": 5.3,
+ "p95": 15.1,
+ "p99": 16.3,
+ "n": 48
+ },
+ "back": {
+ "p50": 12.5,
+ "p95": 18.7,
+ "p99": 19.2,
+ "n": 48
+ }
+ },
+ "inputs": {
+ "sent": 48,
+ "seen": 48
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.0,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.5,
+ "content_p95": 15.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.1,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.6,
+ "content_p95": 13.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.7,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.8,
+ "content_p95": 12.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.56,
+ "content_p50": 10.8,
+ "content_p95": 13.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.9,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 13.4,
+ "content_p95": 15.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 13.8,
+ "content_p95": 16.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.1,
+ "content_p95": 16.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 13.8,
+ "content_p95": 22.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.3,
+ "content_p95": 18.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.2,
+ "content_p95": 18.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 14.1,
+ "content_p95": 18.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 14.2,
+ "content_p95": 17.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 9.9,
+ "content_p95": 18.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.9,
+ "content_p95": 11.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.0,
+ "content_p95": 12.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.0,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.8,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.9,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.9,
+ "content_p95": 12.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 17,
+ "mbps": 0.11,
+ "content_p50": 10.9,
+ "content_p95": 12.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 40.0,
+ "worst_p95": 22.7,
+ "settled_s": 0.0
+ }
+ ],
+ "content_p50": 11.1,
+ "content_p95": 16.2,
+ "input_p50": 30.0,
+ "input_p95": 47.7,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "local",
+ "adapt@8s": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.083,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 47.104,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 64.9,
+ "cpu": {
+ "mac_agent_pct": 15.6,
+ "frame_viewer_pct": 49.6,
+ "frame_tailscaled_pct": 5.8,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 11.3,
+ "frame_total_pct": 37.4
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 70 Hz",
+ "show_s": 1.29,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ }
+ ],
+ [
+ {
+ "scenario": "scroll",
+ "frames_sent": 1044,
+ "frames_drawn": 1044,
+ "frames_shown": 786,
+ "duration_s": 24.3,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.6,
+ "p95": 0.4,
+ "p99": 1.8,
+ "n": 1044
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 14.9,
+ "p99": 17.0,
+ "n": 1044
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 8.8,
+ "p99": 17.0,
+ "n": 1044
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.5,
+ "n": 1044
+ },
+ "network": {
+ "p50": 10.1,
+ "p95": 52.0,
+ "p99": 225.2,
+ "n": 1044
+ },
+ "decode": {
+ "p50": 5.4,
+ "p95": 9.5,
+ "p99": 17.6,
+ "n": 1044
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.3,
+ "p99": 1.7,
+ "n": 1044
+ },
+ "present": {
+ "p50": 0.7,
+ "p95": 14.4,
+ "p99": 22.3,
+ "n": 786
+ },
+ "content": {
+ "p50": 25.5,
+ "p95": 74.6,
+ "p99": 236.2,
+ "n": 1044
+ },
+ "content_shown": {
+ "p50": 33.0,
+ "p95": 74.9,
+ "p99": 234.6,
+ "n": 786
+ }
+ },
+ "fps": 43.0,
+ "fps_shown": 32.4,
+ "late_pct": 27.59,
+ "stall_max": 430.7,
+ "stalls_over_100ms": 7,
+ "mbps": 6.22,
+ "keyframes": 3,
+ "size": "1920x1290",
+ "captured": 1499,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 54,
+ "mbps": 9.98,
+ "content_p50": 23.9,
+ "content_p95": 33.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 57,
+ "mbps": 9.24,
+ "content_p50": 23.4,
+ "content_p95": 32.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 57,
+ "mbps": 9.09,
+ "content_p50": 23.0,
+ "content_p95": 32.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 54,
+ "mbps": 9.77,
+ "content_p50": 24.1,
+ "content_p95": 36.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 58,
+ "mbps": 9.09,
+ "content_p50": 23.2,
+ "content_p95": 27.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 52,
+ "mbps": 8.72,
+ "content_p50": 24.5,
+ "content_p95": 63.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 58,
+ "mbps": 11.69,
+ "content_p50": 23.1,
+ "content_p95": 50.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 57,
+ "mbps": 8.99,
+ "content_p50": 22.4,
+ "content_p95": 32.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 20,
+ "mbps": 3.23,
+ "content_p50": 205.5,
+ "content_p95": 270.8,
+ "bitrate": 7430400,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 34,
+ "mbps": 1.66,
+ "content_p50": 18.4,
+ "content_p95": 428.1,
+ "bitrate": 1765116,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 57,
+ "mbps": 1.27,
+ "content_p50": 23.3,
+ "content_p95": 101.0,
+ "bitrate": 2240789,
+ "tier": 0,
+ "w": 1920,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 35,
+ "mbps": 1.73,
+ "content_p50": 31.8,
+ "content_p95": 148.1,
+ "bitrate": 1405577,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 30,
+ "mbps": 1.26,
+ "content_p50": 38.8,
+ "content_p95": 64.3,
+ "bitrate": 1596134,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 31,
+ "mbps": 1.84,
+ "content_p50": 43.3,
+ "content_p95": 59.6,
+ "bitrate": 2289953,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 30,
+ "mbps": 2.61,
+ "content_p50": 59.7,
+ "content_p95": 85.4,
+ "bitrate": 2373537,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 30,
+ "mbps": 2.39,
+ "content_p50": 53.7,
+ "content_p95": 87.6,
+ "bitrate": 2660890,
+ "tier": 3,
+ "w": 1440,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 30,
+ "mbps": 3.04,
+ "content_p50": 25.4,
+ "content_p95": 37.6,
+ "bitrate": 3707143,
+ "tier": 3,
+ "w": 1440,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 30,
+ "mbps": 4.45,
+ "content_p50": 31.8,
+ "content_p95": 41.7,
+ "bitrate": 5099706,
+ "tier": 3,
+ "w": 1440,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 28,
+ "mbps": 5.76,
+ "content_p50": 34.2,
+ "content_p95": 51.9,
+ "bitrate": 7698527,
+ "tier": 2,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 30,
+ "mbps": 7.38,
+ "content_p50": 37.7,
+ "content_p95": 45.2,
+ "bitrate": 10412237,
+ "tier": 2,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 41,
+ "mbps": 8.63,
+ "content_p50": 34.5,
+ "content_p95": 64.1,
+ "bitrate": 14024186,
+ "tier": 1,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 46,
+ "mbps": 7.87,
+ "content_p50": 35.1,
+ "content_p95": 45.4,
+ "bitrate": 14860800,
+ "tier": 1,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 53,
+ "mbps": 9.21,
+ "content_p50": 24.4,
+ "content_p95": 37.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 57,
+ "mbps": 9.68,
+ "content_p50": 23.2,
+ "content_p95": 31.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 15,
+ "mbps": 2.34,
+ "content_p50": 25.1,
+ "content_p95": 34.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 62.6,
+ "worst_p95": 428.1,
+ "settled_s": 8.0
+ }
+ ],
+ "content_p50": 25.5,
+ "content_p95": 74.6,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "acceptable",
+ "content_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad",
+ "adapt@8s": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 7.875,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 4,
+ "scale": 1,
+ "slack": 48.916,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 8.32,
+ "e": "down to 7430 kbit/s: queue 98 ms, held 6, oldest 191 ms, base 9 ms, sent 6068 got 6112 wants 9608"
+ },
+ {
+ "t": 8.92,
+ "e": "down to 3715 kbit/s: queue 197 ms, held 5, oldest 239 ms, base 9 ms, sent 2451 got 2882 wants 8733"
+ },
+ {
+ "t": 9.22,
+ "e": "down to 2206 kbit/s: queue 237 ms, held 4, oldest 161 ms, base 9 ms, sent 3261 got 2451 wants 10510"
+ },
+ {
+ "t": 9.52,
+ "e": "down to 1765 kbit/s: queue 392 ms, held 6, oldest 456 ms, base 9 ms, sent 2553 got 2705 wants 10396"
+ },
+ {
+ "t": 11.22,
+ "e": "down to 1756 kbit/s: queue 48 ms, held 0, oldest 3 ms, base 8 ms, sent 1996 got 1952 wants 2107"
+ },
+ {
+ "t": 11.52,
+ "e": "tier 3"
+ },
+ {
+ "t": 11.92,
+ "e": "down to 1405 kbit/s: queue 158 ms, held 2, oldest 0 ms, base 8 ms, sent 1907 got 1907 wants 2384"
+ },
+ {
+ "t": 14.92,
+ "e": "down to 2373 kbit/s: queue 47 ms, held 0, oldest 58 ms, base 8 ms, sent 2973 got 2637 wants 2972"
+ },
+ {
+ "t": 18.22,
+ "e": "tier 2"
+ },
+ {
+ "t": 20.32,
+ "e": "tier 1"
+ },
+ {
+ "t": 22.42,
+ "e": "tier 0"
+ }
+ ],
+ "source_fps": 61.7,
+ "cpu": {
+ "mac_agent_pct": 7.1,
+ "frame_viewer_pct": 59.9,
+ "frame_tailscaled_pct": 7.4,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 9.8,
+ "frame_total_pct": 37.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 70 Hz",
+ "show_s": 1.32,
+ "panel": "valve.steam.desktopgame.2001220345",
+ "src": "separate:6403"
+ },
+ {
+ "scenario": "test",
+ "frames_sent": 1455,
+ "frames_drawn": 1455,
+ "frames_shown": 569,
+ "duration_s": 24.3,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1455
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1455
+ },
+ "encode": {
+ "p50": 4.0,
+ "p95": 4.7,
+ "p99": 5.0,
+ "n": 1455
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 1455
+ },
+ "network": {
+ "p50": 5.7,
+ "p95": 9.8,
+ "p99": 13.3,
+ "n": 1455
+ },
+ "decode": {
+ "p50": 1.1,
+ "p95": 2.6,
+ "p99": 3.9,
+ "n": 1455
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 1455
+ },
+ "present": {
+ "p50": 5.7,
+ "p95": 17.2,
+ "p99": 19.5,
+ "n": 569
+ },
+ "content": {
+ "p50": 11.5,
+ "p95": 15.9,
+ "p99": 19.4,
+ "n": 1455
+ },
+ "content_shown": {
+ "p50": 17.6,
+ "p95": 29.4,
+ "p99": 31.2,
+ "n": 569
+ }
+ },
+ "fps": 59.8,
+ "fps_shown": 23.4,
+ "late_pct": 1.1,
+ "stall_max": 115.1,
+ "stalls_over_100ms": 1,
+ "mbps": 0.47,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1580,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 29.1,
+ "p95": 50.1,
+ "p99": 55.5,
+ "n": 48
+ },
+ "input_shown_ms": {
+ "p50": 44.2,
+ "p95": 51.9,
+ "p99": 55.9,
+ "n": 15
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 10.1,
+ "p95": 23.2,
+ "p99": 27.8,
+ "n": 48
+ },
+ "mac": {
+ "p50": 6.0,
+ "p95": 17.1,
+ "p99": 20.1,
+ "n": 48
+ },
+ "back": {
+ "p50": 11.6,
+ "p95": 17.2,
+ "p99": 18.3,
+ "n": 48
+ }
+ },
+ "inputs": {
+ "sent": 48,
+ "seen": 48
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.9,
+ "content_p95": 13.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 11.0,
+ "content_p95": 13.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 11.0,
+ "content_p95": 14.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.9,
+ "content_p95": 15.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 4,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 11.4,
+ "content_p95": 18.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 5,
+ "fps": 59,
+ "mbps": 0.47,
+ "content_p50": 10.8,
+ "content_p95": 13.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 6,
+ "fps": 61,
+ "mbps": 0.58,
+ "content_p50": 11.3,
+ "content_p95": 25.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 7,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 11.0,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 8,
+ "fps": 61,
+ "mbps": 0.48,
+ "content_p50": 13.9,
+ "content_p95": 17.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 9,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 13.8,
+ "content_p95": 18.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 10,
+ "fps": 61,
+ "mbps": 0.45,
+ "content_p50": 13.9,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 11,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 11.8,
+ "content_p95": 15.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 12,
+ "fps": 61,
+ "mbps": 0.46,
+ "content_p50": 13.4,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 13,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 13.4,
+ "content_p95": 16.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 14,
+ "fps": 57,
+ "mbps": 0.43,
+ "content_p50": 13.2,
+ "content_p95": 18.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 15,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 13.4,
+ "content_p95": 16.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 3.0
+ },
+ {
+ "t": 16,
+ "fps": 61,
+ "mbps": 0.56,
+ "content_p50": 10.5,
+ "content_p95": 15.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 17,
+ "fps": 59,
+ "mbps": 0.43,
+ "content_p50": 10.8,
+ "content_p95": 14.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 18,
+ "fps": 61,
+ "mbps": 0.46,
+ "content_p50": 10.7,
+ "content_p95": 13.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 19,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 10.7,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 20,
+ "fps": 61,
+ "mbps": 0.46,
+ "content_p50": 10.5,
+ "content_p95": 15.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 21,
+ "fps": 59,
+ "mbps": 0.44,
+ "content_p50": 10.7,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 22,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.8,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 23,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.6,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ },
+ {
+ "t": 24,
+ "fps": 19,
+ "mbps": 0.13,
+ "content_p50": 10.6,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": 50.0
+ }
+ ],
+ "adapt": [
+ {
+ "at": 8.0,
+ "to_mbit": 3.0,
+ "p95_bound": 40.0,
+ "worst_p95": 18.3,
+ "settled_s": 0.0
+ }
+ ],
+ "content_p50": 11.5,
+ "content_p95": 15.9,
+ "input_p50": 29.1,
+ "input_p95": 50.1,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable",
+ "adapt@8s": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6,
+ "ceiling": 5529600,
+ "fps": 60,
+ "inFlight": 2,
+ "scale": 1,
+ "slack": 49.792,
+ "target": 5529600,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 65.0,
+ "cpu": {
+ "mac_agent_pct": 14.2,
+ "frame_viewer_pct": 52.2,
+ "frame_tailscaled_pct": 5.8,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 12.2,
+ "frame_total_pct": 38.8
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 72 Hz",
+ "show_s": 1.38,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ }
+ ]
+ ]
+ },
+ "table": {
+ "scroll": {
+ "content_p50": {
+ "off": [
+ 27.5,
+ 23.8
+ ],
+ "on": [
+ 26.6,
+ 25.5
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 4986.4,
+ 4355.1
+ ],
+ "on": [
+ 69.2,
+ 74.6
+ ]
+ },
+ "input_p50": {
+ "off": [],
+ "on": []
+ },
+ "input_p95": {
+ "off": [],
+ "on": []
+ },
+ "fps": {
+ "off": [
+ 45.8,
+ 44.5
+ ],
+ "on": [
+ 41.8,
+ 43.0
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 19.19,
+ 17.67
+ ],
+ "on": [
+ 31.82,
+ 27.59
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 232.1,
+ 398.2
+ ],
+ "on": [
+ 245.3,
+ 430.7
+ ]
+ },
+ "mbps": {
+ "off": [
+ 8.62,
+ 8.39
+ ],
+ "on": [
+ 6.13,
+ 6.22
+ ]
+ }
+ },
+ "test": {
+ "content_p50": {
+ "off": [
+ 12.8,
+ 10.7
+ ],
+ "on": [
+ 11.1,
+ 11.5
+ ]
+ },
+ "content_p95": {
+ "off": [
+ 116.8,
+ 15.6
+ ],
+ "on": [
+ 16.2,
+ 15.9
+ ]
+ },
+ "input_p50": {
+ "off": [
+ 34.4,
+ 29.6
+ ],
+ "on": [
+ 30.0,
+ 29.1
+ ]
+ },
+ "input_p95": {
+ "off": [
+ 86.0,
+ 50.1
+ ],
+ "on": [
+ 47.7,
+ 50.1
+ ]
+ },
+ "fps": {
+ "off": [
+ 60.0,
+ 60.0
+ ],
+ "on": [
+ 60.0,
+ 59.8
+ ]
+ },
+ "late_pct": {
+ "off": [
+ 3.69,
+ 1.92
+ ],
+ "on": [
+ 0.82,
+ 1.1
+ ]
+ },
+ "stall_max": {
+ "off": [
+ 185.4,
+ 157.3
+ ],
+ "on": [
+ 32.9,
+ 115.1
+ ]
+ },
+ "mbps": {
+ "off": [
+ 0.49,
+ 0.46
+ ],
+ "on": [
+ 0.47,
+ 0.47
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-ab-keepwarm.json b/bench/results/2026-09-28-a3c6e5c-dirty-ab-keepwarm.json
new file mode 100644
index 0000000..21c9e37
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-ab-keepwarm.json
@@ -0,0 +1,2172 @@
+{
+ "label": "ab-keepwarm",
+ "date": "2026-09-28T20:28:57",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": "8:23.135290 [Info] - 0 - leaving standby",
+ "scenarios": [],
+ "arms": [
+ "cold:FRAME_MAC_VIEW_WARM=0",
+ "warm:FRAME_MAC_VIEW_WARM=40"
+ ],
+ "runs": {
+ "cold": [
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 145,
+ "frames_drawn": 145,
+ "frames_shown": 142,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -2.5,
+ "p95": 2.7,
+ "p99": 19.4,
+ "n": 145
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.3,
+ "n": 145
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 9.6,
+ "p99": 18.2,
+ "n": 145
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 1.0,
+ "n": 145
+ },
+ "network": {
+ "p50": 3.7,
+ "p95": 7.5,
+ "p99": 22.8,
+ "n": 145
+ },
+ "decode": {
+ "p50": 2.1,
+ "p95": 4.2,
+ "p99": 5.1,
+ "n": 145
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.3,
+ "p99": 2.0,
+ "n": 145
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 9.3,
+ "p99": 12.6,
+ "n": 142
+ },
+ "content": {
+ "p50": 13.9,
+ "p95": 25.6,
+ "p99": 38.0,
+ "n": 145
+ },
+ "content_shown": {
+ "p50": 14.9,
+ "p95": 26.0,
+ "p99": 36.9,
+ "n": 142
+ }
+ },
+ "fps": 6.9,
+ "fps_shown": 6.7,
+ "late_pct": 51.37,
+ "stall_max": 1007.5,
+ "stalls_over_100ms": 66,
+ "mbps": 0.03,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 145,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 44.5,
+ "p95": 68.2,
+ "p99": 72.7,
+ "n": 66
+ },
+ "input_shown_ms": {
+ "p50": 44.9,
+ "p95": 68.9,
+ "p99": 73.1,
+ "n": 66
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 4.2,
+ "p95": 17.4,
+ "p99": 41.0,
+ "n": 66
+ },
+ "mac": {
+ "p50": 22.8,
+ "p95": 36.8,
+ "p99": 42.9,
+ "n": 66
+ },
+ "back": {
+ "p50": 14.5,
+ "p95": 20.7,
+ "p99": 25.4,
+ "n": 66
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 66
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 37,
+ "mbps": 0.26,
+ "content_p50": 14.9,
+ "content_p95": 36.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 42,
+ "mbps": 0.15,
+ "content_p50": 12.7,
+ "content_p95": 15.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 15.1,
+ "content_p95": 20.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.6,
+ "content_p95": 14.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.5,
+ "content_p95": 18.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.0,
+ "content_p95": 20.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.5,
+ "content_p95": 15.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.5,
+ "content_p95": 18.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.4,
+ "content_p95": 16.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.3,
+ "content_p95": 16.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.9,
+ "content_p95": 16.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.0,
+ "content_p95": 18.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 13.2,
+ "content_p95": 17.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 12.9,
+ "content_p95": 13.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 14.2,
+ "content_p95": 25.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 13.9,
+ "content_p95": 13.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 15.8,
+ "content_p95": 15.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 13.6,
+ "content_p95": 25.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 15.7,
+ "content_p95": 15.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 17.6,
+ "content_p95": 17.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 16.0,
+ "content_p95": 16.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 13.9,
+ "content_p95": 25.6,
+ "input_p50": 44.5,
+ "input_p95": 68.2,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.75,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 6.9,
+ "cpu": {
+ "mac_agent_pct": 1.7,
+ "frame_viewer_pct": 10.2,
+ "frame_tailscaled_pct": 1.6,
+ "frame_sshd_pct": 0.2,
+ "frame_gamescope_pct": 5.4,
+ "frame_total_pct": 32.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.57,
+ "panel": "valve.steam.desktopgame.2001615880",
+ "src": "separate:6692"
+ }
+ ],
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 190,
+ "frames_drawn": 190,
+ "frames_shown": 190,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.7,
+ "p95": 4.9,
+ "p99": 7.4,
+ "n": 190
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 190
+ },
+ "encode": {
+ "p50": 6.4,
+ "p95": 9.0,
+ "p99": 12.6,
+ "n": 190
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 190
+ },
+ "network": {
+ "p50": 2.8,
+ "p95": 5.1,
+ "p99": 7.8,
+ "n": 190
+ },
+ "decode": {
+ "p50": 2.0,
+ "p95": 4.0,
+ "p99": 5.9,
+ "n": 190
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.5,
+ "p99": 2.0,
+ "n": 190
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 8.5,
+ "p99": 13.4,
+ "n": 190
+ },
+ "content": {
+ "p50": 12.8,
+ "p95": 19.4,
+ "p99": 21.3,
+ "n": 190
+ },
+ "content_shown": {
+ "p50": 13.8,
+ "p95": 22.9,
+ "p99": 25.0,
+ "n": 190
+ }
+ },
+ "fps": 9.0,
+ "fps_shown": 9.0,
+ "late_pct": 41.88,
+ "stall_max": 2314.7,
+ "stalls_over_100ms": 67,
+ "mbps": 0.03,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 190,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 35.9,
+ "p95": 91.9,
+ "p99": 180.6,
+ "n": 65
+ },
+ "input_shown_ms": {
+ "p50": 36.3,
+ "p95": 92.4,
+ "p99": 181.0,
+ "n": 65
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.3,
+ "p95": 19.9,
+ "p99": 57.7,
+ "n": 65
+ },
+ "mac": {
+ "p50": 17.6,
+ "p95": 38.0,
+ "p99": 89.2,
+ "n": 65
+ },
+ "back": {
+ "p50": 13.2,
+ "p95": 20.9,
+ "p99": 21.3,
+ "n": 65
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 65
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 14,
+ "mbps": 0.03,
+ "content_p50": 12.3,
+ "content_p95": 17.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 12.0,
+ "content_p95": 20.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.5,
+ "content_p95": 19.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 12.5,
+ "content_p95": 18.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 11,
+ "mbps": 0.03,
+ "content_p50": 13.2,
+ "content_p95": 19.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 12,
+ "mbps": 0.03,
+ "content_p50": 14.7,
+ "content_p95": 18.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 12.6,
+ "content_p95": 14.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 13,
+ "mbps": 0.03,
+ "content_p50": 13.5,
+ "content_p95": 22.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 19,
+ "mbps": 0.05,
+ "content_p50": 12.4,
+ "content_p95": 16.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.0,
+ "content_p95": 14.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 12.9,
+ "content_p95": 13.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 10.9,
+ "content_p95": 21.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 14.2,
+ "content_p95": 14.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 13.7,
+ "content_p95": 13.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 12.7,
+ "content_p95": 15.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 42,
+ "mbps": 0.14,
+ "content_p50": 12.4,
+ "content_p95": 16.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 25,
+ "mbps": 0.1,
+ "content_p50": 12.2,
+ "content_p95": 14.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 7,
+ "mbps": 0.11,
+ "content_p50": 15.2,
+ "content_p95": 20.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 14.2,
+ "content_p95": 14.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 12.8,
+ "content_p95": 19.4,
+ "input_p50": 35.9,
+ "input_p95": 91.9,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.709,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 9.0,
+ "cpu": {
+ "mac_agent_pct": 2.0,
+ "frame_viewer_pct": 12.8,
+ "frame_tailscaled_pct": 2.0,
+ "frame_sshd_pct": 0.2,
+ "frame_gamescope_pct": 6.0,
+ "frame_total_pct": 26.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.29,
+ "panel": "valve.steam.desktopgame.2001147903",
+ "src": "separate:7108"
+ }
+ ],
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 83,
+ "frames_drawn": 83,
+ "frames_shown": 83,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.2,
+ "p95": 19.9,
+ "p99": 50.0,
+ "n": 83
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 21.0,
+ "n": 83
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 8.9,
+ "p99": 19.9,
+ "n": 83
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.3,
+ "p99": 9.8,
+ "n": 83
+ },
+ "network": {
+ "p50": 3.4,
+ "p95": 8.8,
+ "p99": 43.6,
+ "n": 83
+ },
+ "decode": {
+ "p50": 2.6,
+ "p95": 8.7,
+ "p99": 11.1,
+ "n": 83
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.1,
+ "p99": 1.7,
+ "n": 83
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 5.7,
+ "p99": 11.5,
+ "n": 83
+ },
+ "content": {
+ "p50": 14.6,
+ "p95": 60.4,
+ "p99": 124.2,
+ "n": 83
+ },
+ "content_shown": {
+ "p50": 15.9,
+ "p95": 61.1,
+ "p99": 124.8,
+ "n": 83
+ }
+ },
+ "fps": 3.9,
+ "fps_shown": 3.9,
+ "late_pct": 61.45,
+ "stall_max": 9230.5,
+ "stalls_over_100ms": 39,
+ "mbps": 0.15,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 114,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 44.3,
+ "p95": 236.4,
+ "p99": 1853.7,
+ "n": 39
+ },
+ "input_shown_ms": {
+ "p50": 45.2,
+ "p95": 236.9,
+ "p99": 1854.2,
+ "n": 39
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.3,
+ "p95": 18.0,
+ "p99": 45.1,
+ "n": 39
+ },
+ "mac": {
+ "p50": 21.2,
+ "p95": 150.9,
+ "p99": 1809.2,
+ "n": 39
+ },
+ "back": {
+ "p50": 13.9,
+ "p95": 23.3,
+ "p99": 179.5,
+ "n": 39
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 39
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 7,
+ "mbps": 0.04,
+ "content_p50": 16.2,
+ "content_p95": 179.5,
+ "bitrate": 469728,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 10,
+ "mbps": 0.04,
+ "content_p50": 16.5,
+ "content_p95": 30.9,
+ "bitrate": 469728,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.8,
+ "content_p95": 14.6,
+ "bitrate": 469728,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 13.5,
+ "content_p95": 18.5,
+ "bitrate": 673370,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 7,
+ "mbps": 0.02,
+ "content_p50": 12.7,
+ "content_p95": 13.4,
+ "bitrate": 1217929,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 15,
+ "mbps": 0.06,
+ "content_p50": 13.3,
+ "content_p95": 19.4,
+ "bitrate": 1786562,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 13.9,
+ "content_p95": 20.1,
+ "bitrate": 2543412,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.11,
+ "content_p50": 14.2,
+ "content_p95": 23.2,
+ "bitrate": 3182528,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 13.5,
+ "content_p95": 13.5,
+ "bitrate": 4891587,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 5,
+ "mbps": 0.97,
+ "content_p50": 41.2,
+ "content_p95": 89.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 5,
+ "mbps": 0.65,
+ "content_p50": 36.5,
+ "content_p95": 44.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 5,
+ "mbps": 0.72,
+ "content_p50": 26.1,
+ "content_p95": 40.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 21,
+ "fps": 4,
+ "mbps": 0.5,
+ "content_p50": 17.6,
+ "content_p95": 24.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 14.6,
+ "content_p95": 60.4,
+ "input_p50": 44.3,
+ "input_p95": 236.4,
+ "grades": {
+ "input_replies": "bad",
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "local",
+ "input_p95": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 6.375,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 44.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 0.39,
+ "e": "down to 469 kbit/s: queue 165 ms, held 4, oldest 213 ms, base 5 ms, sent 44 got 28 wants 234"
+ }
+ ],
+ "source_fps": 5.4,
+ "cpu": {
+ "mac_agent_pct": 1.5,
+ "frame_viewer_pct": 8.1,
+ "frame_tailscaled_pct": 1.8,
+ "frame_sshd_pct": 0.1,
+ "frame_gamescope_pct": 5.5,
+ "frame_total_pct": 25.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 72 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001547827",
+ "src": "separate:7247"
+ }
+ ]
+ ],
+ "warm": [
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 114,
+ "frames_drawn": 114,
+ "frames_shown": 112,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.2,
+ "p95": 5.8,
+ "p99": 10.8,
+ "n": 114
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 114
+ },
+ "encode": {
+ "p50": 6.6,
+ "p95": 12.2,
+ "p99": 14.3,
+ "n": 114
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.4,
+ "p99": 2.9,
+ "n": 114
+ },
+ "network": {
+ "p50": 3.9,
+ "p95": 12.5,
+ "p99": 44.8,
+ "n": 114
+ },
+ "decode": {
+ "p50": 2.2,
+ "p95": 5.4,
+ "p99": 9.2,
+ "n": 114
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.5,
+ "p99": 1.9,
+ "n": 114
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 0.9,
+ "p99": 1.2,
+ "n": 112
+ },
+ "content": {
+ "p50": 14.5,
+ "p95": 29.9,
+ "p99": 65.7,
+ "n": 114
+ },
+ "content_shown": {
+ "p50": 15.1,
+ "p95": 28.9,
+ "p99": 52.5,
+ "n": 112
+ }
+ },
+ "fps": 5.4,
+ "fps_shown": 5.3,
+ "late_pct": 72.17,
+ "stall_max": 1001.0,
+ "stalls_over_100ms": 67,
+ "mbps": 0.03,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 162,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 46.6,
+ "p95": 71.3,
+ "p99": 84.0,
+ "n": 66
+ },
+ "input_shown_ms": {
+ "p50": 47.1,
+ "p95": 71.8,
+ "p99": 84.7,
+ "n": 66
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 4.3,
+ "p95": 9.7,
+ "p99": 34.2,
+ "n": 66
+ },
+ "mac": {
+ "p50": 25.8,
+ "p95": 42.8,
+ "p99": 44.3,
+ "n": 66
+ },
+ "back": {
+ "p50": 15.2,
+ "p95": 29.3,
+ "p99": 29.9,
+ "n": 66
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 66
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 29,
+ "mbps": 0.26,
+ "content_p50": 14.3,
+ "content_p95": 21.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 7,
+ "mbps": 0.03,
+ "content_p50": 13.4,
+ "content_p95": 15.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.9,
+ "content_p95": 18.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 9,
+ "mbps": 0.03,
+ "content_p50": 16.1,
+ "content_p95": 20.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 14.4,
+ "content_p95": 19.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 10,
+ "mbps": 0.04,
+ "content_p50": 13.8,
+ "content_p95": 29.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 14.2,
+ "content_p95": 28.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 4,
+ "mbps": 0.02,
+ "content_p50": 16.8,
+ "content_p95": 28.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 6,
+ "mbps": 0.03,
+ "content_p50": 13.8,
+ "content_p95": 16.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.5,
+ "content_p95": 20.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 9,
+ "mbps": 0.05,
+ "content_p50": 29.9,
+ "content_p95": 95.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 7,
+ "mbps": 0.02,
+ "content_p50": 13.8,
+ "content_p95": 30.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 13.3,
+ "content_p95": 17.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 16.6,
+ "content_p95": 16.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 15.2,
+ "content_p95": 17.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.2,
+ "content_p95": 14.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 19.1,
+ "content_p95": 19.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 14.2,
+ "content_p95": 14.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 13.9,
+ "content_p95": 13.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.4,
+ "content_p95": 14.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 15.1,
+ "content_p95": 15.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 14.5,
+ "content_p95": 29.9,
+ "input_p50": 46.6,
+ "input_p95": 71.3,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "acceptable"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.541,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 1,
+ "scale": 1,
+ "slack": 48.292,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 7.7,
+ "cpu": {
+ "mac_agent_pct": 2.0,
+ "frame_viewer_pct": 10.9,
+ "frame_tailscaled_pct": 2.8,
+ "frame_sshd_pct": 0.4,
+ "frame_gamescope_pct": 5.4,
+ "frame_total_pct": 36.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 70 Hz",
+ "show_s": 1.28,
+ "panel": "valve.steam.desktopgame.2001621121",
+ "src": "separate:6829"
+ }
+ ],
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 220,
+ "frames_drawn": 220,
+ "frames_shown": 217,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -5.5,
+ "p95": 3.8,
+ "p99": 14.3,
+ "n": 220
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.3,
+ "n": 220
+ },
+ "encode": {
+ "p50": 6.5,
+ "p95": 9.0,
+ "p99": 18.8,
+ "n": 220
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 1.7,
+ "n": 220
+ },
+ "network": {
+ "p50": 3.1,
+ "p95": 8.9,
+ "p99": 46.7,
+ "n": 220
+ },
+ "decode": {
+ "p50": 2.0,
+ "p95": 4.1,
+ "p99": 7.6,
+ "n": 220
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.4,
+ "p99": 1.8,
+ "n": 220
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 10.4,
+ "p99": 13.9,
+ "n": 217
+ },
+ "content": {
+ "p50": 13.0,
+ "p95": 25.6,
+ "p99": 122.5,
+ "n": 220
+ },
+ "content_shown": {
+ "p50": 14.1,
+ "p95": 27.2,
+ "p99": 122.9,
+ "n": 217
+ }
+ },
+ "fps": 10.4,
+ "fps_shown": 10.3,
+ "late_pct": 38.46,
+ "stall_max": 4500.7,
+ "stalls_over_100ms": 64,
+ "mbps": 0.05,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 220,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 51.4,
+ "p95": 63.8,
+ "p99": 80.7,
+ "n": 64
+ },
+ "input_shown_ms": {
+ "p50": 51.8,
+ "p95": 64.3,
+ "p99": 81.3,
+ "n": 64
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 4.1,
+ "p95": 17.8,
+ "p99": 29.2,
+ "n": 64
+ },
+ "mac": {
+ "p50": 31.3,
+ "p95": 35.3,
+ "p99": 39.4,
+ "n": 64
+ },
+ "back": {
+ "p50": 13.6,
+ "p95": 19.9,
+ "p99": 26.1,
+ "n": 64
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 64
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 28,
+ "mbps": 0.27,
+ "content_p50": 16.7,
+ "content_p95": 61.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 8,
+ "mbps": 0.03,
+ "content_p50": 13.7,
+ "content_p95": 19.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.3,
+ "content_p95": 14.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.2,
+ "content_p95": 13.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 12.9,
+ "content_p95": 14.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.9,
+ "content_p95": 24.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.6,
+ "content_p95": 17.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 13.1,
+ "content_p95": 15.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 13.8,
+ "content_p95": 15.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 14.5,
+ "content_p95": 16.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 12.6,
+ "content_p95": 19.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 48,
+ "mbps": 0.15,
+ "content_p50": 12.8,
+ "content_p95": 16.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 47,
+ "mbps": 0.15,
+ "content_p50": 12.6,
+ "content_p95": 16.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 30,
+ "mbps": 0.09,
+ "content_p50": 12.8,
+ "content_p95": 15.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 11.6,
+ "content_p95": 13.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 6,
+ "mbps": 0.11,
+ "content_p50": 42.9,
+ "content_p95": 143.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 12.8,
+ "content_p95": 13.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 13.0,
+ "content_p95": 25.6,
+ "input_p50": 51.4,
+ "input_p95": 63.8,
+ "grades": {
+ "input_replies": "local",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "acceptable",
+ "input_p95": "local"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 5.792,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14860800,
+ "tier": 0
+ },
+ "events": [],
+ "source_fps": 10.4,
+ "cpu": {
+ "mac_agent_pct": 2.5,
+ "frame_viewer_pct": 14.7,
+ "frame_tailscaled_pct": 2.9,
+ "frame_sshd_pct": 0.4,
+ "frame_gamescope_pct": 6.1,
+ "frame_total_pct": 33.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 71 Hz",
+ "show_s": 1.27,
+ "panel": "valve.steam.desktopgame.2001606834",
+ "src": "separate:6969"
+ }
+ ],
+ [
+ {
+ "scenario": "type",
+ "frames_sent": 128,
+ "frames_drawn": 128,
+ "frames_shown": 126,
+ "duration_s": 21.1,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.5,
+ "p95": 1.4,
+ "p99": 26.9,
+ "n": 128
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.7,
+ "n": 128
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.9,
+ "p99": 23.0,
+ "n": 128
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.3,
+ "p99": 2.8,
+ "n": 128
+ },
+ "network": {
+ "p50": 3.3,
+ "p95": 5.1,
+ "p99": 205.9,
+ "n": 128
+ },
+ "decode": {
+ "p50": 2.1,
+ "p95": 4.2,
+ "p99": 6.4,
+ "n": 128
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.7,
+ "p99": 1.9,
+ "n": 128
+ },
+ "present": {
+ "p50": 0.6,
+ "p95": 9.2,
+ "p99": 13.7,
+ "n": 126
+ },
+ "content": {
+ "p50": 13.4,
+ "p95": 21.6,
+ "p99": 264.8,
+ "n": 128
+ },
+ "content_shown": {
+ "p50": 14.5,
+ "p95": 26.1,
+ "p99": 265.4,
+ "n": 126
+ }
+ },
+ "fps": 6.1,
+ "fps_shown": 6.0,
+ "late_pct": 17.83,
+ "stall_max": 9246.3,
+ "stalls_over_100ms": 17,
+ "mbps": 0.02,
+ "keyframes": 0,
+ "size": "1920x1290",
+ "captured": 129,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 42.5,
+ "p95": 194.0,
+ "p99": 404.5,
+ "n": 25
+ },
+ "input_shown_ms": {
+ "p50": 43.2,
+ "p95": 194.6,
+ "p99": 405.1,
+ "n": 25
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 9.4,
+ "p95": 26.2,
+ "p99": 35.0,
+ "n": 25
+ },
+ "mac": {
+ "p50": 23.3,
+ "p95": 135.9,
+ "p99": 176.2,
+ "n": 25
+ },
+ "back": {
+ "p50": 13.8,
+ "p95": 21.6,
+ "p99": 264.8,
+ "n": 25
+ }
+ },
+ "inputs": {
+ "asked": 65,
+ "sent": 66,
+ "seen": 25
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 24,
+ "mbps": 0.06,
+ "content_p50": 13.0,
+ "content_p95": 14.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 58,
+ "mbps": 0.19,
+ "content_p50": 13.6,
+ "content_p95": 18.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 26,
+ "mbps": 0.08,
+ "content_p50": 12.9,
+ "content_p95": 15.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 6,
+ "mbps": 0.08,
+ "content_p50": 13.5,
+ "content_p95": 17.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 4,
+ "mbps": 0.05,
+ "content_p50": 18.8,
+ "content_p95": 25.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 13.3,
+ "content_p95": 13.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 4,
+ "mbps": 0.03,
+ "content_p50": 264.8,
+ "content_p95": 354.5,
+ "bitrate": 300000,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 13.8,
+ "content_p95": 15.1,
+ "bitrate": 300000,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 13.4,
+ "content_p95": 21.6,
+ "input_p50": 42.5,
+ "input_p95": 194.0,
+ "grades": {
+ "input_replies": "bad",
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "bad"
+ },
+ "controller": {
+ "adapt": true,
+ "baseRtt": 0,
+ "ceiling": 14860800,
+ "fps": 60,
+ "inFlight": 0,
+ "scale": 1,
+ "slack": 41.666,
+ "target": 14855423,
+ "tier": 0
+ },
+ "events": [
+ {
+ "t": 10.93,
+ "e": "down to 300 kbit/s: queue 0 ms, held 0, oldest 201 ms, base 5 ms, sent 61 got 0 wants 51"
+ }
+ ],
+ "source_fps": 6.1,
+ "cpu": {
+ "mac_agent_pct": 2.1,
+ "frame_viewer_pct": 11.1,
+ "frame_tailscaled_pct": 2.6,
+ "frame_sshd_pct": 0.4,
+ "frame_gamescope_pct": 6.0,
+ "frame_total_pct": 25.6
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 72 Hz",
+ "show_s": 1.32,
+ "panel": "valve.steam.desktopgame.2001763888",
+ "src": "separate:7401"
+ }
+ ]
+ ]
+ },
+ "table": {
+ "type": {
+ "content_p50": {
+ "cold": [
+ 13.9,
+ 12.8,
+ 14.6
+ ],
+ "warm": [
+ 14.5,
+ 13.0,
+ 13.4
+ ]
+ },
+ "content_p95": {
+ "cold": [
+ 25.6,
+ 19.4,
+ 60.4
+ ],
+ "warm": [
+ 29.9,
+ 25.6,
+ 21.6
+ ]
+ },
+ "input_p50": {
+ "cold": [
+ 44.5,
+ 35.9,
+ 44.3
+ ],
+ "warm": [
+ 46.6,
+ 51.4,
+ 42.5
+ ]
+ },
+ "input_p95": {
+ "cold": [
+ 68.2,
+ 91.9,
+ 236.4
+ ],
+ "warm": [
+ 71.3,
+ 63.8,
+ 194.0
+ ]
+ },
+ "fps": {
+ "cold": [
+ 6.9,
+ 9.0,
+ 3.9
+ ],
+ "warm": [
+ 5.4,
+ 10.4,
+ 6.1
+ ]
+ },
+ "late_pct": {
+ "cold": [
+ 51.37,
+ 41.88,
+ 61.45
+ ],
+ "warm": [
+ 72.17,
+ 38.46,
+ 17.83
+ ]
+ },
+ "stall_max": {
+ "cold": [
+ 1007.5,
+ 2314.7,
+ 9230.5
+ ],
+ "warm": [
+ 1001.0,
+ 4500.7,
+ 9246.3
+ ]
+ },
+ "mbps": {
+ "cold": [
+ 0.03,
+ 0.03,
+ 0.15
+ ],
+ "warm": [
+ 0.03,
+ 0.05,
+ 0.02
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean.json b/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean.json
new file mode 100644
index 0000000..7f449e1
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean.json
@@ -0,0 +1,984 @@
+{
+ "label": "adapt-clean",
+ "date": "2026-09-28T17:13:59",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": ":39.639829 [Info] - 0 - entering standby",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 653,
+ "frames_drawn": 653,
+ "frames_shown": 649,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 653
+ },
+ "queue": {
+ "p50": 11.2,
+ "p95": 16.1,
+ "p99": 17.1,
+ "n": 653
+ },
+ "encode": {
+ "p50": 3.5,
+ "p95": 4.2,
+ "p99": 4.4,
+ "n": 653
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 653
+ },
+ "network": {
+ "p50": 2.9,
+ "p95": 7.0,
+ "p99": 12.0,
+ "n": 653
+ },
+ "decode": {
+ "p50": 1.0,
+ "p95": 2.4,
+ "p99": 2.8,
+ "n": 653
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.6,
+ "p99": 0.9,
+ "n": 653
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 0.9,
+ "p99": 5.9,
+ "n": 649
+ },
+ "content": {
+ "p50": 19.8,
+ "p95": 26.2,
+ "p99": 30.1,
+ "n": 653
+ },
+ "content_shown": {
+ "p50": 20.3,
+ "p95": 26.9,
+ "p99": 31.1,
+ "n": 649
+ }
+ },
+ "fps": 31.5,
+ "fps_shown": 31.3,
+ "late_pct": 98.01,
+ "stall_max": 144.0,
+ "stalls_over_100ms": 2,
+ "mbps": 0.13,
+ "keyframes": 2,
+ "size": "960x540",
+ "captured": 1336,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 43.5,
+ "p95": 61.8,
+ "p99": 63.4,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 44.3,
+ "p95": 62.3,
+ "p99": 63.9,
+ "n": 40
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 11.1,
+ "p95": 29.2,
+ "p99": 36.1,
+ "n": 40
+ },
+ "mac": {
+ "p50": 10.4,
+ "p95": 28.2,
+ "p99": 32.4,
+ "n": 40
+ },
+ "back": {
+ "p50": 17.5,
+ "p95": 27.9,
+ "p99": 28.8,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 31,
+ "mbps": 0.13,
+ "content_p50": 21.5,
+ "content_p95": 27.1,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 22.4,
+ "content_p95": 26.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 18.9,
+ "content_p95": 24.8,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 18.2,
+ "content_p95": 24.6,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 21.0,
+ "content_p95": 25.6,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 32,
+ "mbps": 0.12,
+ "content_p50": 19.5,
+ "content_p95": 24.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 22.2,
+ "content_p95": 25.6,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 19.9,
+ "content_p95": 24.0,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 31,
+ "mbps": 0.21,
+ "content_p50": 22.7,
+ "content_p95": 26.9,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 20.2,
+ "content_p95": 26.1,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 32,
+ "mbps": 0.12,
+ "content_p50": 18.2,
+ "content_p95": 23.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 19.8,
+ "content_p95": 24.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 19.2,
+ "content_p95": 24.8,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 32,
+ "mbps": 0.12,
+ "content_p50": 21.0,
+ "content_p95": 24.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 30,
+ "mbps": 0.13,
+ "content_p50": 20.4,
+ "content_p95": 96.7,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 18.2,
+ "content_p95": 24.4,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 31,
+ "mbps": 0.12,
+ "content_p50": 18.4,
+ "content_p95": 25.1,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 32,
+ "mbps": 0.13,
+ "content_p50": 17.2,
+ "content_p95": 23.5,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 32,
+ "mbps": 0.19,
+ "content_p50": 19.4,
+ "content_p95": 25.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 31,
+ "mbps": 0.14,
+ "content_p50": 21.1,
+ "content_p95": 84.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 24,
+ "mbps": 0.08,
+ "content_p50": 20.8,
+ "content_p95": 25.1,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.8,
+ "content_p95": 26.2,
+ "input_p50": 43.5,
+ "input_p95": 61.8,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "source_fps": 64.5,
+ "cpu": {
+ "mac_agent_pct": 11.3,
+ "frame_viewer_pct": 35.9,
+ "frame_tailscaled_pct": 3.5,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 7.5,
+ "frame_total_pct": 16.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 4.68,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 937,
+ "frames_drawn": 937,
+ "frames_shown": 720,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -3.9,
+ "p95": 0.9,
+ "p99": 8.2,
+ "n": 937
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 16.3,
+ "p99": 20.1,
+ "n": 937
+ },
+ "encode": {
+ "p50": 6.4,
+ "p95": 7.2,
+ "p99": 9.3,
+ "n": 937
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 937
+ },
+ "network": {
+ "p50": 7.3,
+ "p95": 13.0,
+ "p99": 22.8,
+ "n": 937
+ },
+ "decode": {
+ "p50": 6.2,
+ "p95": 12.7,
+ "p99": 18.6,
+ "n": 937
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.1,
+ "p99": 1.9,
+ "n": 937
+ },
+ "present": {
+ "p50": 2.9,
+ "p95": 18.5,
+ "p99": 24.5,
+ "n": 720
+ },
+ "content": {
+ "p50": 19.9,
+ "p95": 37.2,
+ "p99": 50.2,
+ "n": 937
+ },
+ "content_shown": {
+ "p50": 27.0,
+ "p95": 45.7,
+ "p99": 55.0,
+ "n": 720
+ }
+ },
+ "fps": 45.3,
+ "fps_shown": 34.8,
+ "late_pct": 28.63,
+ "stall_max": 99.3,
+ "stalls_over_100ms": 0,
+ "mbps": 7.59,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 1215,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 30,
+ "mbps": 4.01,
+ "content_p50": 31.9,
+ "content_p95": 52.3,
+ "bitrate": 4867140,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 30,
+ "mbps": 4.12,
+ "content_p50": 29.9,
+ "content_p95": 45.3,
+ "bitrate": 3807054,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 30,
+ "mbps": 3.26,
+ "content_p50": 31.8,
+ "content_p95": 52.5,
+ "bitrate": 3235995,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 30,
+ "mbps": 3.72,
+ "content_p50": 24.5,
+ "content_p95": 37.7,
+ "bitrate": 4238740,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 30,
+ "mbps": 5.09,
+ "content_p50": 25.2,
+ "content_p95": 39.6,
+ "bitrate": 5992063,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 31,
+ "mbps": 7.29,
+ "content_p50": 29.1,
+ "content_p95": 53.5,
+ "bitrate": 5526067,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 35,
+ "mbps": 4.58,
+ "content_p50": 25.3,
+ "content_p95": 32.4,
+ "bitrate": 6018152,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 46,
+ "mbps": 7.12,
+ "content_p50": 25.1,
+ "content_p95": 34.3,
+ "bitrate": 8412933,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 46,
+ "mbps": 8.25,
+ "content_p50": 27.1,
+ "content_p95": 40.3,
+ "bitrate": 10760191,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 47,
+ "mbps": 9.3,
+ "content_p50": 27.8,
+ "content_p95": 41.2,
+ "bitrate": 13717060,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 55,
+ "mbps": 8.98,
+ "content_p50": 16.5,
+ "content_p95": 21.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 51,
+ "mbps": 9.32,
+ "content_p50": 18.6,
+ "content_p95": 35.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 56,
+ "mbps": 9.53,
+ "content_p50": 15.4,
+ "content_p95": 26.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 55,
+ "mbps": 8.46,
+ "content_p50": 18.6,
+ "content_p95": 23.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 54,
+ "mbps": 9.82,
+ "content_p50": 15.9,
+ "content_p95": 28.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 54,
+ "mbps": 11.11,
+ "content_p50": 15.5,
+ "content_p95": 26.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 54,
+ "mbps": 9.79,
+ "content_p50": 17.4,
+ "content_p95": 25.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 57,
+ "mbps": 9.3,
+ "content_p50": 16.3,
+ "content_p95": 23.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 55,
+ "mbps": 8.3,
+ "content_p50": 19.5,
+ "content_p95": 31.0,
+ "bitrate": 7984791,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 51,
+ "mbps": 8.56,
+ "content_p50": 16.4,
+ "content_p95": 29.1,
+ "bitrate": 10220855,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 40,
+ "mbps": 7.13,
+ "content_p50": 18.3,
+ "content_p95": 23.9,
+ "bitrate": 12025604,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 19.9,
+ "content_p95": 37.2,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "local"
+ },
+ "source_fps": 58.7,
+ "cpu": {
+ "mac_agent_pct": 6.6,
+ "frame_viewer_pct": 72.4,
+ "frame_tailscaled_pct": 8.1,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 10.0,
+ "frame_total_pct": 24.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 6.43,
+ "panel": "valve.steam.desktopgame.2001414593",
+ "src": "separate:9384"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 25,
+ "frames_drawn": 25,
+ "frames_shown": 25,
+ "duration_s": 14.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -0.3,
+ "p95": 0.9,
+ "p99": 1.1,
+ "n": 25
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 25
+ },
+ "encode": {
+ "p50": 5.0,
+ "p95": 33.4,
+ "p99": 33.6,
+ "n": 25
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.1,
+ "p99": 0.2,
+ "n": 25
+ },
+ "network": {
+ "p50": 6.1,
+ "p95": 14.9,
+ "p99": 24.4,
+ "n": 25
+ },
+ "decode": {
+ "p50": 3.0,
+ "p95": 10.5,
+ "p99": 11.4,
+ "n": 25
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.0,
+ "p99": 1.1,
+ "n": 25
+ },
+ "present": {
+ "p50": 0.8,
+ "p95": 1.4,
+ "p99": 1.7,
+ "n": 25
+ },
+ "content": {
+ "p50": 12.6,
+ "p95": 41.0,
+ "p99": 46.3,
+ "n": 25
+ },
+ "content_shown": {
+ "p50": 13.9,
+ "p95": 41.8,
+ "p99": 46.6,
+ "n": 25
+ }
+ },
+ "fps": 1.7,
+ "fps_shown": 1.7,
+ "late_pct": 100.0,
+ "stall_max": 3099.2,
+ "stalls_over_100ms": 21,
+ "mbps": 0.04,
+ "keyframes": 4,
+ "size": "960x646",
+ "captured": 79,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 41.3,
+ "p95": 60.3,
+ "p99": 68.6,
+ "n": 18
+ },
+ "input_shown_ms": {
+ "p50": 43.0,
+ "p95": 61.1,
+ "p99": 69.6,
+ "n": 18
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.3,
+ "p95": 11.6,
+ "p99": 14.2,
+ "n": 18
+ },
+ "mac": {
+ "p50": 23.1,
+ "p95": 35.0,
+ "p99": 42.6,
+ "n": 18
+ },
+ "back": {
+ "p50": 11.8,
+ "p95": 19.2,
+ "p99": 22.7,
+ "n": 18
+ }
+ },
+ "inputs": {
+ "sent": 66,
+ "seen": 18
+ },
+ "timeline": [
+ {
+ "t": 3,
+ "fps": 1,
+ "mbps": 0.01,
+ "content_p50": 11.8,
+ "content_p95": 11.8,
+ "bitrate": 1224075,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 1,
+ "mbps": 0.02,
+ "content_p50": 14.7,
+ "content_p95": 14.7,
+ "bitrate": 2091896,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 4,
+ "mbps": 0.1,
+ "content_p50": 18.6,
+ "content_p95": 37.4,
+ "bitrate": 3071304,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.13,
+ "content_p50": 4.2,
+ "content_p95": 19.2,
+ "bitrate": 4031277,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 6,
+ "mbps": 0.04,
+ "content_p50": 12.0,
+ "content_p95": 46.3,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 1,
+ "mbps": 0.01,
+ "content_p50": 12.2,
+ "content_p95": 12.2,
+ "bitrate": 300000,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 1,
+ "mbps": 0.02,
+ "content_p50": -1.2,
+ "content_p95": -1.2,
+ "bitrate": 842857,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 1,
+ "mbps": 0.02,
+ "content_p50": 14.4,
+ "content_p95": 14.4,
+ "bitrate": 2091896,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 12.6,
+ "content_p95": 41.0,
+ "input_p50": 41.3,
+ "input_p95": 60.3,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "local",
+ "input_p95": "local"
+ },
+ "source_fps": 5.4,
+ "cpu": {
+ "mac_agent_pct": 1.2,
+ "frame_viewer_pct": 5.0,
+ "frame_tailscaled_pct": 1.5,
+ "frame_sshd_pct": 0.2,
+ "frame_gamescope_pct": 4.7,
+ "frame_total_pct": 8.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 140 Hz",
+ "show_s": 5.12,
+ "panel": "valve.steam.desktopgame.2001910179",
+ "src": "separate:9510"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean2.json b/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean2.json
new file mode 100644
index 0000000..79fd46c
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-adapt-clean2.json
@@ -0,0 +1,1116 @@
+{
+ "label": "adapt-clean2",
+ "date": "2026-09-28T17:16:27",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": ":39.639829 [Info] - 0 - entering standby",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 1229,
+ "frames_drawn": 1229,
+ "frames_shown": 814,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1229
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1229
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.0,
+ "n": 1229
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 1229
+ },
+ "network": {
+ "p50": 6.1,
+ "p95": 10.9,
+ "p99": 28.8,
+ "n": 1229
+ },
+ "decode": {
+ "p50": 1.2,
+ "p95": 3.4,
+ "p99": 4.4,
+ "n": 1229
+ },
+ "draw": {
+ "p50": 0.3,
+ "p95": 0.7,
+ "p99": 1.1,
+ "n": 1229
+ },
+ "present": {
+ "p50": 5.5,
+ "p95": 16.5,
+ "p99": 19.2,
+ "n": 814
+ },
+ "content": {
+ "p50": 12.2,
+ "p95": 17.6,
+ "p99": 34.3,
+ "n": 1229
+ },
+ "content_shown": {
+ "p50": 18.0,
+ "p95": 29.6,
+ "p99": 38.7,
+ "n": 814
+ }
+ },
+ "fps": 59.3,
+ "fps_shown": 39.3,
+ "late_pct": 1.55,
+ "stall_max": 168.0,
+ "stalls_over_100ms": 2,
+ "mbps": 0.47,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1337,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 27.8,
+ "p95": 41.1,
+ "p99": 46.2,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 32.3,
+ "p95": 47.0,
+ "p99": 53.4,
+ "n": 25
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 8.0,
+ "p95": 19.4,
+ "p99": 31.7,
+ "n": 40
+ },
+ "mac": {
+ "p50": 5.3,
+ "p95": 14.2,
+ "p99": 18.3,
+ "n": 40
+ },
+ "back": {
+ "p50": 12.7,
+ "p95": 16.6,
+ "p99": 17.7,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.2,
+ "content_p95": 13.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.0,
+ "content_p95": 13.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.6,
+ "content_p95": 17.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.9,
+ "content_p95": 15.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 11.0,
+ "content_p95": 17.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.6,
+ "content_p95": 13.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.59,
+ "content_p50": 10.4,
+ "content_p95": 15.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.3,
+ "content_p95": 15.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 11.8,
+ "content_p95": 15.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 11.9,
+ "content_p95": 14.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 12.3,
+ "content_p95": 23.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 0.44,
+ "content_p50": 12.5,
+ "content_p95": 17.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 14.2,
+ "content_p95": 17.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 58,
+ "mbps": 0.44,
+ "content_p50": 14.2,
+ "content_p95": 27.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 14.3,
+ "content_p95": 17.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 13.3,
+ "content_p95": 17.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.57,
+ "content_p50": 11.6,
+ "content_p95": 14.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.3,
+ "content_p95": 18.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.44,
+ "content_p50": 12.4,
+ "content_p95": 14.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 13.7,
+ "content_p95": 16.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 33,
+ "mbps": 0.24,
+ "content_p50": 13.3,
+ "content_p95": 135.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 12.2,
+ "content_p95": 17.6,
+ "input_p50": 27.8,
+ "input_p95": 41.1,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "acceptable",
+ "stall_max": "acceptable"
+ },
+ "source_fps": 64.6,
+ "cpu": {
+ "mac_agent_pct": 15.4,
+ "frame_viewer_pct": 57.6,
+ "frame_tailscaled_pct": 5.8,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 11.1,
+ "frame_total_pct": 22.2
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 5.83,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 734,
+ "frames_drawn": 734,
+ "frames_shown": 707,
+ "duration_s": 20.6,
+ "stages_ms": {
+ "capture": {
+ "p50": -4.8,
+ "p95": 6.5,
+ "p99": 13.1,
+ "n": 734
+ },
+ "queue": {
+ "p50": 8.4,
+ "p95": 19.8,
+ "p99": 28.9,
+ "n": 734
+ },
+ "encode": {
+ "p50": 6.3,
+ "p95": 7.1,
+ "p99": 10.1,
+ "n": 734
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 734
+ },
+ "network": {
+ "p50": 7.4,
+ "p95": 20.9,
+ "p99": 48.9,
+ "n": 734
+ },
+ "decode": {
+ "p50": 5.4,
+ "p95": 13.5,
+ "p99": 17.2,
+ "n": 734
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 1.2,
+ "p99": 1.8,
+ "n": 734
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 13.6,
+ "p99": 21.3,
+ "n": 707
+ },
+ "content": {
+ "p50": 24.8,
+ "p95": 48.1,
+ "p99": 69.4,
+ "n": 734
+ },
+ "content_shown": {
+ "p50": 26.9,
+ "p95": 50.5,
+ "p99": 68.5,
+ "n": 707
+ }
+ },
+ "fps": 35.6,
+ "fps_shown": 34.3,
+ "late_pct": 58.12,
+ "stall_max": 168.7,
+ "stalls_over_100ms": 2,
+ "mbps": 4.09,
+ "keyframes": 3,
+ "size": "960x646",
+ "captured": 1122,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 45,
+ "mbps": 7.92,
+ "content_p50": 23.2,
+ "content_p95": 38.8,
+ "bitrate": 8869129,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 46,
+ "mbps": 8.52,
+ "content_p50": 31.8,
+ "content_p95": 45.2,
+ "bitrate": 11334867,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 45,
+ "mbps": 8.17,
+ "content_p50": 29.2,
+ "content_p95": 65.7,
+ "bitrate": 6587726,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 45,
+ "mbps": 6.56,
+ "content_p50": 26.5,
+ "content_p95": 43.5,
+ "bitrate": 7164744,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 46,
+ "mbps": 6.64,
+ "content_p50": 21.5,
+ "content_p95": 30.9,
+ "bitrate": 6256828,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 44,
+ "mbps": 7.24,
+ "content_p50": 22.0,
+ "content_p95": 47.5,
+ "bitrate": 6291668,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 44,
+ "mbps": 5.19,
+ "content_p50": 28.2,
+ "content_p95": 50.0,
+ "bitrate": 5347917,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 44,
+ "mbps": 4.54,
+ "content_p50": 23.7,
+ "content_p95": 43.0,
+ "bitrate": 3863869,
+ "tier": 1,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 35,
+ "mbps": 3.83,
+ "content_p50": 28.2,
+ "content_p95": 41.2,
+ "bitrate": 4222978,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 29,
+ "mbps": 4.65,
+ "content_p50": 30.0,
+ "content_p95": 53.8,
+ "bitrate": 5482055,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 28,
+ "mbps": 4.05,
+ "content_p50": 26.4,
+ "content_p95": 121.7,
+ "bitrate": 3981384,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 30,
+ "mbps": 3.12,
+ "content_p50": 24.5,
+ "content_p95": 42.4,
+ "bitrate": 2876549,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 31,
+ "mbps": 2.72,
+ "content_p50": 22.6,
+ "content_p95": 36.5,
+ "bitrate": 2445066,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 29,
+ "mbps": 2.4,
+ "content_p50": 26.4,
+ "content_p95": 46.1,
+ "bitrate": 2690671,
+ "tier": 2,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 26,
+ "mbps": 2.21,
+ "content_p50": 24.8,
+ "content_p95": 55.1,
+ "bitrate": 2168791,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 31,
+ "mbps": 1.65,
+ "content_p50": 24.1,
+ "content_p95": 33.2,
+ "bitrate": 1566951,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 30,
+ "mbps": 1.43,
+ "content_p50": 19.8,
+ "content_p95": 38.0,
+ "bitrate": 1317132,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 31,
+ "mbps": 1.33,
+ "content_p50": 23.2,
+ "content_p95": 41.1,
+ "bitrate": 1190325,
+ "tier": 3,
+ "w": 1440,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 25,
+ "mbps": 1.11,
+ "content_p50": 23.6,
+ "content_p95": 99.7,
+ "bitrate": 860009,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 29,
+ "mbps": 0.64,
+ "content_p50": 22.1,
+ "content_p95": 41.5,
+ "bitrate": 731007,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 21,
+ "mbps": 0.47,
+ "content_p50": 26.1,
+ "content_p95": 31.1,
+ "bitrate": 619308,
+ "tier": 4,
+ "w": 960,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 24.8,
+ "content_p95": 48.1,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "source_fps": 54.5,
+ "cpu": {
+ "mac_agent_pct": 5.6,
+ "frame_viewer_pct": 57.2,
+ "frame_tailscaled_pct": 5.3,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 8.9,
+ "frame_total_pct": 21.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 6.59,
+ "panel": "valve.steam.desktopgame.2001551415",
+ "src": "separate:9635"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 75,
+ "frames_drawn": 75,
+ "frames_shown": 75,
+ "duration_s": 20.0,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.3,
+ "p95": 4.2,
+ "p99": 5.1,
+ "n": 75
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 75
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 9.4,
+ "p99": 11.7,
+ "n": 75
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 75
+ },
+ "network": {
+ "p50": 4.0,
+ "p95": 12.4,
+ "p99": 26.6,
+ "n": 75
+ },
+ "decode": {
+ "p50": 2.3,
+ "p95": 7.2,
+ "p99": 14.4,
+ "n": 75
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.5,
+ "p99": 1.6,
+ "n": 75
+ },
+ "present": {
+ "p50": 0.5,
+ "p95": 1.1,
+ "p99": 1.2,
+ "n": 75
+ },
+ "content": {
+ "p50": 14.4,
+ "p95": 25.1,
+ "p99": 37.7,
+ "n": 75
+ },
+ "content_shown": {
+ "p50": 14.9,
+ "p95": 25.6,
+ "p99": 38.9,
+ "n": 75
+ }
+ },
+ "fps": 3.8,
+ "fps_shown": 3.8,
+ "late_pct": 97.3,
+ "stall_max": 1109.7,
+ "stalls_over_100ms": 70,
+ "mbps": 0.03,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 75,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 45.2,
+ "p95": 77.2,
+ "p99": 83.1,
+ "n": 66
+ },
+ "input_shown_ms": {
+ "p50": 45.9,
+ "p95": 77.9,
+ "p99": 84.3,
+ "n": 66
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.8,
+ "p95": 16.5,
+ "p99": 29.9,
+ "n": 66
+ },
+ "mac": {
+ "p50": 24.4,
+ "p95": 41.2,
+ "p99": 41.6,
+ "n": 66
+ },
+ "back": {
+ "p50": 14.5,
+ "p95": 25.1,
+ "p99": 31.6,
+ "n": 66
+ }
+ },
+ "inputs": {
+ "sent": 66,
+ "seen": 66
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 2.0,
+ "content_p95": 4.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.7,
+ "content_p95": 37.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 17.1,
+ "content_p95": 18.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.7,
+ "content_p95": 26.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.9,
+ "content_p95": 19.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 17.8,
+ "content_p95": 21.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.22,
+ "content_p50": 18.7,
+ "content_p95": 31.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 18.1,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 12.5,
+ "content_p95": 13.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 6.4,
+ "content_p95": 16.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 14.4,
+ "content_p95": 18.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 11.9,
+ "content_p95": 14.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 12.9,
+ "content_p95": 22.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 13.5,
+ "content_p95": 13.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 2.2,
+ "content_p95": 2.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 110.2,
+ "content_p95": 110.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.4,
+ "content_p95": 14.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 2,
+ "mbps": 0.25,
+ "content_p50": -0.0,
+ "content_p95": 17.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.1,
+ "content_p95": 14.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 17.5,
+ "content_p95": 17.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 14.4,
+ "content_p95": 25.1,
+ "input_p50": 45.2,
+ "input_p95": 77.2,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "acceptable"
+ },
+ "source_fps": 3.8,
+ "cpu": {
+ "mac_agent_pct": 1.2,
+ "frame_viewer_pct": 7.1,
+ "frame_tailscaled_pct": 2.2,
+ "frame_sshd_pct": 0.3,
+ "frame_gamescope_pct": 4.7,
+ "frame_total_pct": 15.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 6.64,
+ "panel": "valve.steam.desktopgame.2001684656",
+ "src": "separate:9763"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-baseline-fixed.json b/bench/results/2026-09-28-a3c6e5c-dirty-baseline-fixed.json
new file mode 100644
index 0000000..d12e58c
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-baseline-fixed.json
@@ -0,0 +1,1124 @@
+{
+ "label": "baseline-fixed",
+ "date": "2026-09-28T17:02:53",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": ":51.824645 [Info] - 0 - entering standby",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 1246,
+ "frames_drawn": 1223,
+ "frames_shown": 804,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1246
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1246
+ },
+ "encode": {
+ "p50": 4.0,
+ "p95": 4.7,
+ "p99": 4.9,
+ "n": 1246
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 1246
+ },
+ "network": {
+ "p50": 4.0,
+ "p95": 6.8,
+ "p99": 221.1,
+ "n": 1246
+ },
+ "decode": {
+ "p50": 1.3,
+ "p95": 2.5,
+ "p99": 4.2,
+ "n": 1223
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.5,
+ "p99": 0.9,
+ "n": 1223
+ },
+ "present": {
+ "p50": 6.2,
+ "p95": 15.6,
+ "p99": 16.9,
+ "n": 804
+ },
+ "content": {
+ "p50": 9.9,
+ "p95": 12.9,
+ "p99": 16.4,
+ "n": 1223
+ },
+ "content_shown": {
+ "p50": 16.1,
+ "p95": 25.5,
+ "p99": 27.9,
+ "n": 804
+ }
+ },
+ "fps": 59.0,
+ "fps_shown": 38.8,
+ "late_pct": 0.33,
+ "stall_max": 433.3,
+ "stalls_over_100ms": 1,
+ "mbps": 0.48,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1598,
+ "viewer_never_drawn": 23,
+ "input_ms": {
+ "p50": 28.8,
+ "p95": 39.0,
+ "p99": 45.2,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 33.7,
+ "p95": 55.8,
+ "p99": 58.6,
+ "n": 29
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 9.8,
+ "p95": 21.7,
+ "p99": 22.5,
+ "n": 40
+ },
+ "mac": {
+ "p50": 4.5,
+ "p95": 15.7,
+ "p99": 18.4,
+ "n": 40
+ },
+ "back": {
+ "p50": 10.6,
+ "p95": 13.2,
+ "p99": 15.7,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 59,
+ "mbps": 0.49,
+ "content_p50": 9.3,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.6,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.2,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.8,
+ "content_p95": 11.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 38,
+ "mbps": 0.58,
+ "content_p50": 9.3,
+ "content_p95": 395.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.3,
+ "content_p95": 11.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.4,
+ "content_p95": 12.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.5,
+ "content_p95": 10.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 8.9,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 8.8,
+ "content_p95": 12.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 11.5,
+ "content_p95": 14.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 11.2,
+ "content_p95": 14.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 61,
+ "mbps": 0.47,
+ "content_p50": 10.7,
+ "content_p95": 12.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 59,
+ "mbps": 0.44,
+ "content_p50": 10.3,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 61,
+ "mbps": 0.57,
+ "content_p50": 10.1,
+ "content_p95": 14.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 59,
+ "mbps": 0.45,
+ "content_p50": 10.2,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.1,
+ "content_p95": 11.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.0,
+ "content_p95": 11.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 9.9,
+ "content_p95": 11.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.5,
+ "content_p95": 13.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 46,
+ "mbps": 0.34,
+ "content_p50": 9.2,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 9.9,
+ "content_p95": 12.9,
+ "input_p50": 28.8,
+ "input_p95": 39.0,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "bad"
+ },
+ "cpu": {
+ "mac_agent_pct": 14.3,
+ "frame_viewer_pct": 49.7,
+ "frame_tailscaled_pct": 5.7,
+ "frame_sshd_pct": 0.7,
+ "frame_gamescope_pct": 6.7,
+ "frame_total_pct": 16.5
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 142 Hz",
+ "show_s": 3.62,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 1047,
+ "frames_drawn": 1042,
+ "frames_shown": 798,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -5.5,
+ "p95": 5.8,
+ "p99": 14.2,
+ "n": 1047
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 1047
+ },
+ "encode": {
+ "p50": 6.7,
+ "p95": 9.0,
+ "p99": 14.1,
+ "n": 1047
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 1047
+ },
+ "network": {
+ "p50": 4.9,
+ "p95": 9.3,
+ "p99": 47.3,
+ "n": 1047
+ },
+ "decode": {
+ "p50": 6.4,
+ "p95": 15.2,
+ "p99": 22.6,
+ "n": 1042
+ },
+ "draw": {
+ "p50": 0.6,
+ "p95": 0.8,
+ "p99": 1.6,
+ "n": 1042
+ },
+ "present": {
+ "p50": 4.7,
+ "p95": 19.6,
+ "p99": 24.9,
+ "n": 798
+ },
+ "content": {
+ "p50": 14.7,
+ "p95": 32.2,
+ "p99": 73.2,
+ "n": 1042
+ },
+ "content_shown": {
+ "p50": 21.1,
+ "p95": 41.7,
+ "p99": 66.3,
+ "n": 798
+ }
+ },
+ "fps": 50.4,
+ "fps_shown": 38.6,
+ "late_pct": 16.04,
+ "stall_max": 152.4,
+ "stalls_over_100ms": 3,
+ "mbps": 9.37,
+ "keyframes": 3,
+ "size": "1920x1290",
+ "captured": 1312,
+ "viewer_never_drawn": 5,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 51,
+ "mbps": 8.99,
+ "content_p50": 14.9,
+ "content_p95": 32.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 55,
+ "mbps": 10.09,
+ "content_p50": 17.4,
+ "content_p95": 26.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 51,
+ "mbps": 8.51,
+ "content_p50": 15.1,
+ "content_p95": 25.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 44,
+ "mbps": 7.9,
+ "content_p50": 16.7,
+ "content_p95": 73.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 46,
+ "mbps": 11.34,
+ "content_p50": 17.4,
+ "content_p95": 120.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 45,
+ "mbps": 8.55,
+ "content_p50": 15.1,
+ "content_p95": 25.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 51,
+ "mbps": 9.61,
+ "content_p50": 17.4,
+ "content_p95": 24.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 54,
+ "mbps": 9.52,
+ "content_p50": 16.9,
+ "content_p95": 27.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 50,
+ "mbps": 8.51,
+ "content_p50": 15.8,
+ "content_p95": 33.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 54,
+ "mbps": 9.22,
+ "content_p50": 12.4,
+ "content_p95": 27.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 51,
+ "mbps": 9.96,
+ "content_p50": 9.9,
+ "content_p95": 28.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 51,
+ "mbps": 9.27,
+ "content_p50": 14.1,
+ "content_p95": 22.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 52,
+ "mbps": 9.58,
+ "content_p50": 14.4,
+ "content_p95": 25.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 54,
+ "mbps": 8.61,
+ "content_p50": 7.8,
+ "content_p95": 22.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 43,
+ "mbps": 10.54,
+ "content_p50": 19.0,
+ "content_p95": 37.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 49,
+ "mbps": 8.51,
+ "content_p50": 16.1,
+ "content_p95": 27.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 51,
+ "mbps": 11.23,
+ "content_p50": 11.1,
+ "content_p95": 110.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 52,
+ "mbps": 8.99,
+ "content_p50": 13.9,
+ "content_p95": 26.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 52,
+ "mbps": 9.05,
+ "content_p50": 11.6,
+ "content_p95": 26.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 49,
+ "mbps": 9.3,
+ "content_p50": 12.4,
+ "content_p95": 28.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 37,
+ "mbps": 6.58,
+ "content_p50": 15.7,
+ "content_p95": 31.0,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 14.7,
+ "content_p95": 32.2,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "fps": "acceptable",
+ "late_pct": "bad",
+ "stall_max": "acceptable"
+ },
+ "cpu": {
+ "mac_agent_pct": 7.6,
+ "frame_viewer_pct": 73.9,
+ "frame_tailscaled_pct": 8.0,
+ "frame_sshd_pct": 0.9,
+ "frame_gamescope_pct": 6.5,
+ "frame_total_pct": 19.4
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 138 Hz",
+ "show_s": 3.38,
+ "panel": "valve.steam.desktopgame.2001739297",
+ "src": "separate:8529"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 77,
+ "frames_drawn": 77,
+ "frames_shown": 77,
+ "duration_s": 20.0,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.2,
+ "p95": 4.7,
+ "p99": 4.8,
+ "n": 77
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 77
+ },
+ "encode": {
+ "p50": 6.8,
+ "p95": 11.7,
+ "p99": 14.7,
+ "n": 77
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 77
+ },
+ "network": {
+ "p50": 4.9,
+ "p95": 8.7,
+ "p99": 75.8,
+ "n": 77
+ },
+ "decode": {
+ "p50": 4.2,
+ "p95": 9.9,
+ "p99": 14.5,
+ "n": 77
+ },
+ "draw": {
+ "p50": 1.0,
+ "p95": 1.6,
+ "p99": 2.1,
+ "n": 77
+ },
+ "present": {
+ "p50": 0.9,
+ "p95": 1.3,
+ "p99": 1.7,
+ "n": 77
+ },
+ "content": {
+ "p50": 16.7,
+ "p95": 34.2,
+ "p99": 88.1,
+ "n": 77
+ },
+ "content_shown": {
+ "p50": 17.9,
+ "p95": 34.6,
+ "p99": 88.9,
+ "n": 77
+ }
+ },
+ "fps": 3.8,
+ "fps_shown": 3.8,
+ "late_pct": 94.74,
+ "stall_max": 1156.0,
+ "stalls_over_100ms": 71,
+ "mbps": 0.03,
+ "keyframes": 2,
+ "size": "1920x1290",
+ "captured": 151,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 51.2,
+ "p95": 73.2,
+ "p99": 79.9,
+ "n": 66
+ },
+ "input_shown_ms": {
+ "p50": 52.1,
+ "p95": 73.9,
+ "p99": 80.5,
+ "n": 66
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 5.6,
+ "p95": 13.2,
+ "p99": 17.5,
+ "n": 66
+ },
+ "mac": {
+ "p50": 34.1,
+ "p95": 43.6,
+ "p99": 48.7,
+ "n": 66
+ },
+ "back": {
+ "p50": 16.4,
+ "p95": 26.5,
+ "p99": 26.8,
+ "n": 66
+ }
+ },
+ "inputs": {
+ "sent": 66,
+ "seen": 66
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 7.7,
+ "content_p95": 15.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 7.4,
+ "content_p95": 16.7,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 16.2,
+ "content_p95": 21.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 6,
+ "mbps": 0.02,
+ "content_p50": 19.1,
+ "content_p95": 26.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 18.9,
+ "content_p95": 20.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.21,
+ "content_p50": 13.7,
+ "content_p95": 43.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 20.2,
+ "content_p95": 24.5,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 16.4,
+ "content_p95": 16.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 15.2,
+ "content_p95": 24.6,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 7.7,
+ "content_p95": 17.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 5,
+ "mbps": 0.02,
+ "content_p50": 6.0,
+ "content_p95": 26.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 7,
+ "mbps": 0.02,
+ "content_p50": 19.2,
+ "content_p95": 46.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 21.7,
+ "content_p95": 22.3,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 13.4,
+ "content_p95": 13.4,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 3,
+ "mbps": 0.01,
+ "content_p50": 19.0,
+ "content_p95": 20.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 88.1,
+ "content_p95": 88.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 1,
+ "mbps": 0.25,
+ "content_p50": 26.8,
+ "content_p95": 26.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 2,
+ "mbps": 0.01,
+ "content_p50": 19.1,
+ "content_p95": 19.2,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 175.1,
+ "content_p95": 175.1,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 16.9,
+ "content_p95": 16.9,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 14.8,
+ "content_p95": 14.8,
+ "bitrate": 14860800,
+ "tier": 0,
+ "w": 1920,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 16.7,
+ "content_p95": 34.2,
+ "input_p50": 51.2,
+ "input_p95": 73.2,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "acceptable",
+ "input_p95": "acceptable"
+ },
+ "cpu": {
+ "mac_agent_pct": 1.1,
+ "frame_viewer_pct": 8.0,
+ "frame_tailscaled_pct": 1.8,
+ "frame_sshd_pct": 0.5,
+ "frame_gamescope_pct": 3.7,
+ "frame_total_pct": 9.1
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 143 Hz",
+ "show_s": 3.79,
+ "panel": "valve.steam.desktopgame.2001132791",
+ "src": "separate:8652"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bench/results/2026-09-28-a3c6e5c-dirty-baseline.json b/bench/results/2026-09-28-a3c6e5c-dirty-baseline.json
new file mode 100644
index 0000000..25b9f92
--- /dev/null
+++ b/bench/results/2026-09-28-a3c6e5c-dirty-baseline.json
@@ -0,0 +1,1124 @@
+{
+ "label": "baseline",
+ "date": "2026-09-28T16:53:43",
+ "commit": "a3c6e5c+dirty",
+ "config": {
+ "quality": "balanced",
+ "mode": "separate",
+ "net": "none",
+ "delay_ms": 0,
+ "buffer_ms": 250,
+ "host": "frame",
+ "ssh_opts": [],
+ "encoder": "",
+ "duration_s": 20.0,
+ "browser_flags": []
+ },
+ "frame_build": "20260925.6191901",
+ "mac": "26.5.2",
+ "headset": ":51.824645 [Info] - 0 - entering standby",
+ "scenarios": [
+ {
+ "scenario": "test",
+ "frames_sent": 1248,
+ "frames_drawn": 1245,
+ "frames_shown": 809,
+ "duration_s": 20.8,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.0,
+ "n": 1248
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.1,
+ "p99": 0.1,
+ "n": 1248
+ },
+ "encode": {
+ "p50": 4.2,
+ "p95": 4.8,
+ "p99": 5.1,
+ "n": 1248
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 1248
+ },
+ "network": {
+ "p50": 4.2,
+ "p95": 6.9,
+ "p99": 13.9,
+ "n": 1248
+ },
+ "decode": {
+ "p50": 1.3,
+ "p95": 2.5,
+ "p99": 4.6,
+ "n": 1245
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.6,
+ "p99": 1.0,
+ "n": 1245
+ },
+ "present": {
+ "p50": 6.9,
+ "p95": 15.4,
+ "p99": 19.5,
+ "n": 809
+ },
+ "content": {
+ "p50": 10.3,
+ "p95": 13.5,
+ "p99": 19.6,
+ "n": 1245
+ },
+ "content_shown": {
+ "p50": 17.4,
+ "p95": 25.6,
+ "p99": 30.7,
+ "n": 809
+ }
+ },
+ "fps": 60.0,
+ "fps_shown": 39.0,
+ "late_pct": 0.96,
+ "stall_max": 110.3,
+ "stalls_over_100ms": 1,
+ "mbps": 0.47,
+ "keyframes": 2,
+ "size": "1280x720",
+ "captured": 1588,
+ "viewer_never_drawn": 3,
+ "input_ms": {
+ "p50": 28.0,
+ "p95": 34.4,
+ "p99": 37.6,
+ "n": 40
+ },
+ "input_shown_ms": {
+ "p50": 35.1,
+ "p95": 49.5,
+ "p99": 50.5,
+ "n": 27
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 11.5,
+ "p95": 19.7,
+ "p99": 22.1,
+ "n": 40
+ },
+ "mac": {
+ "p50": 4.7,
+ "p95": 12.3,
+ "p99": 16.1,
+ "n": 40
+ },
+ "back": {
+ "p50": 11.2,
+ "p95": 12.9,
+ "p99": 16.5,
+ "n": 40
+ }
+ },
+ "inputs": {
+ "sent": 40,
+ "seen": 40
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 60,
+ "mbps": 0.49,
+ "content_p50": 10.2,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.4,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.5,
+ "content_p95": 12.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 60,
+ "mbps": 0.48,
+ "content_p50": 10.7,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.6,
+ "content_p95": 13.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 60,
+ "mbps": 0.58,
+ "content_p50": 10.6,
+ "content_p95": 12.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.8,
+ "content_p95": 13.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.5,
+ "content_p95": 13.0,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 10.5,
+ "content_p95": 12.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.3,
+ "content_p95": 13.6,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 10.8,
+ "content_p95": 14.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 58,
+ "mbps": 0.56,
+ "content_p50": 10.4,
+ "content_p95": 61.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 10.8,
+ "content_p95": 12.7,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 12.4,
+ "content_p95": 16.1,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.8,
+ "content_p95": 12.2,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 60,
+ "mbps": 0.47,
+ "content_p50": 9.7,
+ "content_p95": 12.3,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 60,
+ "mbps": 0.45,
+ "content_p50": 9.7,
+ "content_p95": 12.5,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 10.9,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.7,
+ "content_p95": 11.4,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 60,
+ "mbps": 0.46,
+ "content_p50": 9.9,
+ "content_p95": 18.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 47,
+ "mbps": 0.36,
+ "content_p50": 9.9,
+ "content_p95": 11.8,
+ "bitrate": 5529600,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 10.3,
+ "content_p95": 13.5,
+ "input_p50": 28.0,
+ "input_p95": 34.4,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "local",
+ "input_p50": "local",
+ "input_p95": "local",
+ "fps": "local",
+ "late_pct": "local",
+ "stall_max": "acceptable"
+ },
+ "cpu": {
+ "mac_agent_pct": 15.4,
+ "frame_viewer_pct": 51.1,
+ "frame_tailscaled_pct": 5.3,
+ "frame_sshd_pct": 0.8,
+ "frame_gamescope_pct": 5.7,
+ "frame_total_pct": 15.3
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 140 Hz",
+ "show_s": 1.87,
+ "panel": "valve.steam.desktopgame.2001639889",
+ "src": "test"
+ },
+ {
+ "scenario": "scroll",
+ "frames_sent": 690,
+ "frames_drawn": 683,
+ "frames_shown": 645,
+ "duration_s": 20.7,
+ "stages_ms": {
+ "capture": {
+ "p50": -1.2,
+ "p95": 16.9,
+ "p99": 24.5,
+ "n": 690
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 690
+ },
+ "encode": {
+ "p50": 4.1,
+ "p95": 7.0,
+ "p99": 13.2,
+ "n": 690
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.3,
+ "n": 690
+ },
+ "network": {
+ "p50": 6.2,
+ "p95": 14.6,
+ "p99": 119.2,
+ "n": 690
+ },
+ "decode": {
+ "p50": 3.7,
+ "p95": 9.4,
+ "p99": 23.3,
+ "n": 683
+ },
+ "draw": {
+ "p50": 0.4,
+ "p95": 0.8,
+ "p99": 1.2,
+ "n": 683
+ },
+ "present": {
+ "p50": 0.8,
+ "p95": 18.6,
+ "p99": 23.6,
+ "n": 645
+ },
+ "content": {
+ "p50": 15.0,
+ "p95": 40.3,
+ "p99": 112.7,
+ "n": 683
+ },
+ "content_shown": {
+ "p50": 20.2,
+ "p95": 42.7,
+ "p99": 106.9,
+ "n": 645
+ }
+ },
+ "fps": 33.0,
+ "fps_shown": 31.1,
+ "late_pct": 42.82,
+ "stall_max": 492.6,
+ "stalls_over_100ms": 6,
+ "mbps": 3.11,
+ "keyframes": 3,
+ "size": "1280x860",
+ "captured": 905,
+ "viewer_never_drawn": 7,
+ "input_ms": {
+ "n": 0
+ },
+ "input_shown_ms": {
+ "n": 0
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "n": 0
+ },
+ "mac": {
+ "n": 0
+ },
+ "back": {
+ "n": 0
+ }
+ },
+ "inputs": {
+ "sent": 0,
+ "seen": 0
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 36,
+ "mbps": 3.42,
+ "content_p50": 17.7,
+ "content_p95": 34.3,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 35,
+ "mbps": 2.8,
+ "content_p50": 13.6,
+ "content_p95": 27.7,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 31,
+ "mbps": 2.15,
+ "content_p50": 12.1,
+ "content_p95": 34.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 37,
+ "mbps": 2.29,
+ "content_p50": 14.6,
+ "content_p95": 28.5,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 35,
+ "mbps": 2.11,
+ "content_p50": 16.1,
+ "content_p95": 29.3,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 33,
+ "mbps": 2.05,
+ "content_p50": 10.9,
+ "content_p95": 27.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 33,
+ "mbps": 5.71,
+ "content_p50": 17.1,
+ "content_p95": 41.7,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 31,
+ "mbps": 3.3,
+ "content_p50": 12.1,
+ "content_p95": 31.3,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 24,
+ "mbps": 4.37,
+ "content_p50": 26.6,
+ "content_p95": 236.8,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 36,
+ "mbps": 3.19,
+ "content_p50": 11.6,
+ "content_p95": 26.5,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 36,
+ "mbps": 3.53,
+ "content_p50": 15.2,
+ "content_p95": 29.6,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 29,
+ "mbps": 3.56,
+ "content_p50": 11.5,
+ "content_p95": 36.8,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 29,
+ "mbps": 1.99,
+ "content_p50": 13.2,
+ "content_p95": 41.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 31,
+ "mbps": 3.4,
+ "content_p50": 23.9,
+ "content_p95": 44.5,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 32,
+ "mbps": 2.85,
+ "content_p50": 14.3,
+ "content_p95": 37.6,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 31,
+ "mbps": 3.39,
+ "content_p50": 19.8,
+ "content_p95": 35.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 22,
+ "mbps": 2.74,
+ "content_p50": 22.5,
+ "content_p95": 112.7,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 44,
+ "mbps": 3.2,
+ "content_p50": 14.0,
+ "content_p95": 31.0,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 35,
+ "mbps": 4.12,
+ "content_p50": 16.8,
+ "content_p95": 32.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 36,
+ "mbps": 2.63,
+ "content_p50": 13.9,
+ "content_p95": 25.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 27,
+ "mbps": 1.71,
+ "content_p50": 17.2,
+ "content_p95": 40.3,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 15.0,
+ "content_p95": 40.3,
+ "input_p50": null,
+ "input_p95": null,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "fps": "bad",
+ "late_pct": "bad",
+ "stall_max": "bad"
+ },
+ "cpu": {
+ "mac_agent_pct": 5.1,
+ "frame_viewer_pct": 44.2,
+ "frame_tailscaled_pct": 4.8,
+ "frame_sshd_pct": 0.6,
+ "frame_gamescope_pct": 4.2,
+ "frame_total_pct": 13.7
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 139 Hz",
+ "show_s": 1.82,
+ "panel": "valve.steam.desktopgame.2001631563",
+ "src": "separate:6889"
+ },
+ {
+ "scenario": "type",
+ "frames_sent": 76,
+ "frames_drawn": 76,
+ "frames_shown": 76,
+ "duration_s": 20.2,
+ "stages_ms": {
+ "capture": {
+ "p50": 0.5,
+ "p95": 7.2,
+ "p99": 10.1,
+ "n": 76
+ },
+ "queue": {
+ "p50": 0.0,
+ "p95": 0.0,
+ "p99": 0.1,
+ "n": 76
+ },
+ "encode": {
+ "p50": 4.4,
+ "p95": 9.7,
+ "p99": 16.7,
+ "n": 76
+ },
+ "socket": {
+ "p50": 0.1,
+ "p95": 0.2,
+ "p99": 0.2,
+ "n": 76
+ },
+ "network": {
+ "p50": 5.2,
+ "p95": 43.2,
+ "p99": 263.5,
+ "n": 76
+ },
+ "decode": {
+ "p50": 2.1,
+ "p95": 4.8,
+ "p99": 13.4,
+ "n": 76
+ },
+ "draw": {
+ "p50": 0.7,
+ "p95": 1.0,
+ "p99": 1.1,
+ "n": 76
+ },
+ "present": {
+ "p50": 0.9,
+ "p95": 1.5,
+ "p99": 1.8,
+ "n": 76
+ },
+ "content": {
+ "p50": 12.9,
+ "p95": 54.2,
+ "p99": 264.6,
+ "n": 76
+ },
+ "content_shown": {
+ "p50": 14.1,
+ "p95": 55.3,
+ "p99": 265.9,
+ "n": 76
+ }
+ },
+ "fps": 3.8,
+ "fps_shown": 3.8,
+ "late_pct": 96.0,
+ "stall_max": 1327.9,
+ "stalls_over_100ms": 68,
+ "mbps": 0.02,
+ "keyframes": 2,
+ "size": "1280x860",
+ "captured": 125,
+ "viewer_never_drawn": 0,
+ "input_ms": {
+ "p50": 50.1,
+ "p95": 110.6,
+ "p99": 165.7,
+ "n": 65
+ },
+ "input_shown_ms": {
+ "p50": 50.8,
+ "p95": 111.5,
+ "p99": 166.2,
+ "n": 65
+ },
+ "input_parts_ms": {
+ "uplink": {
+ "p50": 7.6,
+ "p95": 18.3,
+ "p99": 23.2,
+ "n": 65
+ },
+ "mac": {
+ "p50": 30.6,
+ "p95": 72.4,
+ "p99": 119.2,
+ "n": 65
+ },
+ "back": {
+ "p50": 12.3,
+ "p95": 28.1,
+ "p99": 33.9,
+ "n": 65
+ }
+ },
+ "inputs": {
+ "sent": 66,
+ "seen": 65
+ },
+ "timeline": [
+ {
+ "t": 0,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 12.3,
+ "content_p95": 28.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 1,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 16.8,
+ "content_p95": 33.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 2,
+ "fps": 6,
+ "mbps": 0.01,
+ "content_p50": 9.0,
+ "content_p95": 22.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 3,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 11.5,
+ "content_p95": 20.6,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 4,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 9.2,
+ "content_p95": 13.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 5,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 3.0,
+ "content_p95": 15.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 6,
+ "fps": 5,
+ "mbps": 0.14,
+ "content_p50": 14.0,
+ "content_p95": 21.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 7,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 9.5,
+ "content_p95": 16.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 8,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 13.8,
+ "content_p95": 16.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 9,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 14.0,
+ "content_p95": 28.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 10,
+ "fps": 5,
+ "mbps": 0.01,
+ "content_p50": 12.8,
+ "content_p95": 31.7,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 11,
+ "fps": 6,
+ "mbps": 0.01,
+ "content_p50": 12.3,
+ "content_p95": 14.4,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 12,
+ "fps": 4,
+ "mbps": 0.01,
+ "content_p50": 15.2,
+ "content_p95": 17.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 13,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 18.9,
+ "content_p95": 18.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 14,
+ "fps": 2,
+ "mbps": 0.0,
+ "content_p50": 203.5,
+ "content_p95": 215.0,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 15,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 16.1,
+ "content_p95": 16.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 16,
+ "fps": 2,
+ "mbps": 0.16,
+ "content_p50": 15.5,
+ "content_p95": 35.1,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 17,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 264.6,
+ "content_p95": 264.6,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 18,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 12.9,
+ "content_p95": 12.9,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 19,
+ "fps": 2,
+ "mbps": 0.0,
+ "content_p50": 11.7,
+ "content_p95": 54.2,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ },
+ {
+ "t": 20,
+ "fps": 1,
+ "mbps": 0.0,
+ "content_p50": 339.6,
+ "content_p95": 339.6,
+ "bitrate": 6604800,
+ "tier": 0,
+ "w": 1280,
+ "net": null
+ }
+ ],
+ "adapt": [],
+ "content_p50": 12.9,
+ "content_p95": 54.2,
+ "input_p50": 50.1,
+ "input_p95": 110.6,
+ "grades": {
+ "content_p50": "local",
+ "content_p95": "acceptable",
+ "input_p50": "acceptable",
+ "input_p95": "acceptable"
+ },
+ "cpu": {
+ "mac_agent_pct": 1.2,
+ "frame_viewer_pct": 6.5,
+ "frame_tailscaled_pct": 1.6,
+ "frame_sshd_pct": 0.3,
+ "frame_gamescope_pct": 2.4,
+ "frame_total_pct": 8.0
+ },
+ "viewer": "h264 software; ANGLE (Mesa, zink Vulkan 1.4(Turnip Adreno (TM) 750 (MESA_TURNIP)), OpenGL 4.6); raf 141 Hz",
+ "show_s": 1.43,
+ "panel": "valve.steam.desktopgame.2001427053",
+ "src": "separate:7011"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/frame-control.md b/docs/frame-control.md
index b764a83..cc4fa79 100644
--- a/docs/frame-control.md
+++ b/docs/frame-control.md
@@ -72,6 +72,10 @@ counts them while they run.
Runtime picked from the program's header), listed under **Sideloaded titles**
with Launch and Remove; see [sideloading.md](sideloading.md). Send typed text, or your computer's clipboard, to the
Frame clipboard.
+- **Mac in the headset** (macOS): show any Mac window, or a whole screen, as
+ its own panel in the headset. Place it with the SteamVR dashboard, click and
+ scroll with the laser, and type on the Mac. Streams hardware H.264 through
+ an SSH tunnel; see [mac-in-headset.md](mac-in-headset.md).
- **Flatpaks**: install and remove them (quick picks: Moonlight, Firefox, VLC,
Remmina).
- **One-click tools**: SSH or SFTP in a terminal window, Steam Link, and remote
diff --git a/docs/how-the-frame-works.md b/docs/how-the-frame-works.md
index cdf2498..2947a8e 100644
--- a/docs/how-the-frame-works.md
+++ b/docs/how-the-frame-works.md
@@ -52,6 +52,9 @@ Lepton (Android 11, podman container "lepton-dev") ← its own panel, app 305600
| **T3 Code desktop runs natively.** The stock release `T3-Code-0.0.42-arm64.AppImage` in `~/Applications/T3CodeDesktop/` starts with no extra setup: glibc 2.39, `libfuse.so.2`, GTK 3, NSS and libsecret are on the image. `panel-on-frame.sh --name t3code-desktop -- '~/Applications/T3CodeDesktop/T3-Code.AppImage'` gives it its own panel (`valve.steam.desktopgame.2000281357`, `--ozone-platform=x11`). Its bundled server listens on `127.0.0.1:3773` and shows up in onboarding as the `frame` computer, with `passwordStore: gnome-libsecret`. The image has no agent CLI and no `node`. Agents run through the LAN CLIProxyAPI (`llm-proxy.lan:8317`, which resolves on the Frame). Claude Code 2.1.283 comes from `claude.ai/install.sh`, and Codex 0.157.1 from the `codex-aarch64-unknown-linux-musl` release tarball, both into `~/.local/bin`. `with-cliproxy` and a mode-600 `~/.config/cliproxyapi/secrets.env` are copied from the Mac. The wrappers `claude-cliproxy` and `codex-cliproxy` (a `-c model_provider=cliproxy`, `wire_api="responses"`, `env_key="CLIPROXY_API_KEY"`) are set as `providers.claudeAgent.binaryPath` and `providers.codex.binaryPath` in `~/.t3/userdata/settings.json`, and T3 picked that up without a restart. Through the wrappers, `claude auth status` reports `loggedIn: true` (`oauth_token`), and both CLIs answered a prompt with `kimi-k3`. `gamescopectl screenshot` captured another layer (the Lepton T3 app) rather than this panel. `DISPLAY=:0 xwd -id ` piped to `ffmpeg` captures the window itself (1920×1080). **Verified 2026-09-26**, BUILD_ID 20260922.6101926. | Running T3 Code as a host on the Frame |
| Power actions need `sudo`, which asks for the Developer Mode password over SSH. | Frame Control's power buttons |
| **SSH server:** OpenSSH 9.7p1. It offers `publickey,password` (keyboard-interactive is off, PAM on) and also asks `userdbctl ssh-authorized-keys` for keys. OpenSSH ≥ 8.8 rejects SHA-1 `ssh-rsa` signatures by default, so a client whose RSA support is SHA-1 only (the Swift library Citadel, for one) can't log in with the RSA key that devkit pairing installs; use ed25519 (**inferred** from OpenSSH defaults). **Verified 2026-09-27**, BUILD_ID 20260922.6101926. | [iphone.md](iphone.md), `ui/frame_connect.py` |
+| **A Chromium app window on gamescope's `:0` with its own `STEAM_GAME` becomes a panel, even when started over SSH.** `chromium-xr/chrome --ozone-platform=x11 --app=URL --window-size=1280,720` got a 1920×1080 window, and tagging it produced `[Overlays] Created: valve.steam.desktopgame.` in `~/.local/share/Steam/logs/vrwebhelper_systemui.txt`. Chromium XR decoded a 1080p H.264 WebCodecs stream from the Mac at about 60 fps. XTest events sent to `:0` (libXtst through Python ctypes) did not reach the page. The Frame has `libXtst`, `xprop`, `xwininfo`, `curl` and the `C.utf8` locale. **Verified 2026-09-28**, BUILD_ID 20260925.6191901. | [mac-in-headset.md](mac-in-headset.md) |
+| **Streaming video into a panel: what the Frame adds.** Chromium XR decodes H.264 in **software** (1920×1290 at about 9 Mbit/s took 4–6 ms per frame); its GL is ANGLE → zink → Turnip on the Adreno 750, and it logs `GetVSyncParametersIfAvailable() failed`. An idle page's `requestAnimationFrame` runs at about 140 Hz; when the headset is worn or woken, SteamVR picks the 4320×2160 @ 144 Hz mode. **An unworn headset throttles panel apps** whatever they draw: a local canvas page ran at 58 fps for about 6 s, then 28, then about 15 once in standby (vrserver logs `entering standby`, with `power.pauseCompositorOnStandby 1`). `vrcmd --handlewakeup` gave 137–144 fps for about 2 s, then 36. So a panel's frame rate and compositor delay can only be measured while it's worn. `tailscaled` runs with `--tun=userspace-networking` and cost about 8% of a core at 9 Mbit/s (0.5% over the LAN address). Wi-Fi power saving is on (`iw … get power_save`). **Verified 2026-09-28**, BUILD_ID 20260925.6191901. | [mac-in-headset.md](mac-in-headset.md#measuring) |
+| **USB-C networking.** Plugged into a Mac, the Frame is a USB network device: macOS names the port "Steam Frame" (here `en9`, 10.86.200.234/29), and the Frame's `usb0` is 10.86.200.233. Ping is about 0.9 ms, and SSH works with the usual host key (`-o HostName=10.86.200.233 -o HostKeyAlias=`). Steam's Remote Play discovery also broadcasts over it. **Verified 2026-09-28**, BUILD_ID 20260925.6191901. | Frame Control's Mac stream uses it when present ([mac-in-headset.md](mac-in-headset.md)) |
| **Tools on the image:** Python 3.12.3, `ffmpeg`, `openssl`, `curl`, `rsync`, `zip`/`unzip`, `flatpak`, `wpctl`, `podman`. **No `adb`.** `steamos` is uid 1000, in `wheel`, and sudoers has `%wheel ALL=(ALL) ALL`, so `sudo -S` takes the Developer Mode password on stdin. **Verified 2026-09-27.** | Running Frame Control's server on the Frame (`FRAME_LOCAL=1`, [iphone.md](iphone.md)) |
| **Each Lepton instance is a podman container** named `lepton-steamlaunch-`, labelled with its ADB port (`podman ps --format '{{.Names}} {{.Labels.adb_port}}'`). `podman exec /system/bin/sh -c '…'` runs Android's shell inside it with no adb at all (used for `pidof` and `logcat` by the app tester). Running `wm size`/`wm density` that way is untested. **Verified 2026-09-27.** | `ui/frame_android.py`, the iPhone app's display settings |
| **Asleep means off the network.** In standby the Frame stops answering on its LAN address, `frame.local` and Tailscale alike (`Host is down`, `No route to host`, timeouts), and ping fails. It was unreachable for about 2.5 hours until woken. Nothing over SSH can wake it. **Verified 2026-09-27.** | Frame Control's offline banner and retries |
@@ -59,7 +62,7 @@ Lepton (Android 11, podman container "lepton-dev") ← its own panel, app 305600
| **Battery at full on a charger** can read `Discharging` at about 0 W (for example 99 %, 0.0 W, USB-C PD 18 W). Treat under 0.5 W on a charger as "not charging", not "draining". **Verified 2026-09-27.** | Frame Control's battery card |
| **The OS image is downloadable.** Valve's recovery images for the Frame are at `https://steamdeck-images.steamos.cloud/recovery/`. The root filesystem inside is btrfs, and it runs as an SSH test target on ARM64 Linux without the headset (`tests/frame-container/frame-image.sh`). **Verified 2026-09-27.** | [recovery-and-images.md](recovery-and-images.md) |
| **Boot / recovery menu.** Hold Power ~10 s until the LED goes off, then power on while holding the **AUX button on top of the Power button** (not the volume keys) until a text menu appears. Entries: `Current` (SteamOS-A/B + build), `Previous` (the other A/B slot), `Boot from USB`, `Repair Steam Installation`, `Erase User Data` (factory reset), `ADB mode`, `Battery Ship Mode`. It auto-boots `Current` after a ~15 s countdown. **Volume Up/Down (left side) move, AUX (right side) selects.** For a boot loop, Valve says pick `Previous` (keeps user data); then `Repair Steam Installation`; `Erase User Data` wipes `~` (SSH keys, Tailscale, Flatpaks, T3 setup). Last resort is a full re-image, two ways: (1) USB: write `steamframe-oobe-repair-.img.bz2` to an 8 GB+ USB-C stick (Balena Etcher on the Mac), pick `Boot from USB`, then use "Wipe Device & Install SteamOS" / "Repair SteamOS" (keeps games and personal content) from the recovery desktop; (2) cable/EDL: `steamframe-oobe-repair-qdl-.tar.gz`, run `flash.sh` (Linux) or `flash.cmd` (Windows), then with the Frame off for 10 s hold Power + Vol Up + Vol Down for 10 s and plug it in; it reflashes and reboots. Both images: `https://steamdeck-images.steamos.cloud/recovery/` (build 20260922.5153644, 0.3.0, 3.8 GiB each, no published checksums); local copies in `~/Downloads/steam-frame-recovery/`. File names, checksums and what's inside: [recovery-and-images.md](recovery-and-images.md). Source: Valve's [SteamOS Recovery FAQ](https://help.steampowered.com/en/faqs/view/1B71-EDF2-EB6D-2BB3) and [Installation and Repair FAQ](https://help.steampowered.com/en/faqs/view/65B4-2AA3-5F37-4227), plus a menu photo in [EloiStree/HelloSteamFrame#9](https://github.com/EloiStree/HelloSteamFrame/issues/9). **Inferred** (Valve docs, 2026-09-26); not yet tried on our Frame. | Recovering from a boot loop |
-| **Boot loop cause: the SteamVR health check.** `steamvr.service` runs `/usr/share/deckard/steamvr-health-check`, which appends `frog:glasses:` to `$XDG_RUNTIME_DIR/steamvr-short-session-tracker` on every failed or <10 s SteamVR run. At 3 it runs `steam-health-check --repair-now`, which **deletes all of `~/.local/share/Steam` (games, login, Developer Mode) and `~/.steam`**, keeping only `registry.vdf`. At 4 it also tries `steamos-bootconf set-mode reboot-other` (fails as the user: `bootenv: Permission denied`). SteamVR normally fails 1–2 times per boot while it waits for the Steam client (`SteamAPI_InitEx failed … Steam is probably not running`, then `fatal stalled cross-thread pipe`). Once Steam has been wiped, it has to re-download a ~210 MB client on every boot, so SteamVR keeps failing, Steam keeps getting wiped and the Frame reboots, in a loop. Also, the Steam updater can deadlock at `Installing update...` (main process blocked writing to the `-child-update-ui` process, which is stuck in `drm_syncobj_array_wait_timeout`). Killing only the `-child-update-ui` process lets the install finish (`package/*.installed` appears). **Fix without sudo:** over USB-C ADB (`adb -s frame shell` works as `steamos` while the Frame is looping; SSH is refused once Developer Mode is lost), truncate both `/run/user/1000/steam{,vr}-short-session-tracker` files and `chmod 444` them (the health check then logs `Permission denied` and does nothing; this is tmpfs, so it resets on reboot). Unstick the updater if needed, let Steam finish installing, then hold Power 10 s and start the Frame normally. `systemctl reboot` over ADB needs interactive auth. After the fix, sign in to Steam and turn Developer Mode back on. **Verified 2026-09-26**, BUILD_ID 20260922.6101926, slot B (clean boot: 0 SteamVR failures, SSH and Tailscale back). | Diagnosing a boot loop |
+| **Boot loop cause: the SteamVR health check.** `steamvr.service` runs `/usr/share/deckard/steamvr-health-check`, which appends `frog:glasses:` to `$XDG_RUNTIME_DIR/steamvr-short-session-tracker` on every failed or <10 s SteamVR run. At 3 it runs `steam-health-check --repair-now`, which **deletes all of `~/.local/share/Steam` (games, login, Developer Mode) and `~/.steam`**, keeping only `registry.vdf`. At 4 it also tries `steamos-bootconf set-mode reboot-other` (fails as the user: `bootenv: Permission denied`). SteamVR normally fails 1–2 times per boot while it waits for the Steam client (`SteamAPI_InitEx failed … Steam is probably not running`, then `fatal stalled cross-thread pipe`). Once Steam has been wiped, it has to re-download a ~210 MB client on every boot, so SteamVR keeps failing, Steam keeps getting wiped and the Frame reboots, in a loop. Also, the Steam updater can deadlock at `Installing update...` (main process blocked writing to the `-child-update-ui` process, which is stuck in `drm_syncobj_array_wait_timeout`). Killing only the `-child-update-ui` process lets the install finish (`package/*.installed` appears). **Fix without sudo:** over USB-C ADB (`adb -s frame shell` works as `steamos` while the Frame is looping; SSH is refused once Developer Mode is lost), truncate both `/run/user/1000/steam{,vr}-short-session-tracker` files and `chmod 444` them (the health check then logs `Permission denied` and does nothing; this is tmpfs, so it resets on reboot). Unstick the updater if needed, let Steam finish installing, then hold Power 10 s and start the Frame normally. `systemctl reboot` over ADB needs interactive auth. After the fix, sign in to Steam and turn Developer Mode back on. **Verified 2026-09-26**, BUILD_ID 20260922.6101926, slot B (clean boot: 0 SteamVR failures, SSH and Tailscale back). **Seen again 2026-09-28** on BUILD_ID 20260925.6191901, beta client 1790377368. The Frame rebooted by itself while off the network. At 21:13 the check tried `reboot-other` (`bootenv: Permission denied`) and reset the unpacked Steam install, and the tracker had 15 entries. The tracker fix above, applied over SSH, stopped the resets (the checks then log `Permission denied`), but Steam itself kept exiting about 17 s after each start (34 restarts). Cause not found yet. | Diagnosing a boot loop |
## Debug recipes
diff --git a/docs/mac-in-headset.md b/docs/mac-in-headset.md
new file mode 100644
index 0000000..590a831
--- /dev/null
+++ b/docs/mac-in-headset.md
@@ -0,0 +1,470 @@
+# Mac in the headset
+
+Frame Control can show any Mac window, or a whole Mac screen, as its own panel
+in the Steam Frame. You place each panel anywhere in the room with the SteamVR
+dashboard. The laser clicks and drags, the thumbstick scrolls, and you type on
+the Mac's own keyboard. Find it under **Tools → Mac in the headset** (macOS
+only).
+
+The confidence labels are the same as in [ssh.md](ssh.md).
+
+## Why this design
+
+First-party options come first, as the repo's rule asks, with the reason
+each one was or wasn't chosen. The full list for every device is in
+[streaming.md](streaming.md#first-party-options-and-why-they-do-or-dont-fit).
+Checked 2026-09-28.
+
+| Goal | First-party option | Chosen? | Why |
+|---|---|---|---|
+| One Mac screen in the headset | **Apple Screen Sharing** (VNC) → Remmina (Remmina 1.4.43 is already installed on this Frame) | Kept as the fallback (`panel-on-frame.sh mac-screen`) | It's the closest to first-party and needs nothing new. But VNC sends compressed tiles rather than video, so moving content is slow: noticeable lag even on a good 5 GHz link (**verified** 2026-09-27, see [streaming.md](streaming.md)), and the Mac's pointer isn't in the picture without a helper. It shows only whole screens |
+| One Mac screen | **Steam Remote Play**, Mac as host (Valve) | For Mac games only; see [Steam's own streaming](#steams-own-streaming) | The Mac's and the Frame's Steam clients already find each other (**verified**). But Remote Play streams a game (the whole desktop only while the game is out of focus, untested from a Mac), never single windows, and a Mac can't host the Frame's VR streaming |
+| One Mac screen | **AirPlay** (Apple) | No | Apple licenses AirPlay receivers only to TV and speaker makers, and nothing official runs on Linux. UxPlay is an unofficial receiver, and it mirrors a whole screen, not single windows |
+| One Mac screen | **Sidecar / Mac Virtual Display** (Apple) | No | These work only with an iPad or Apple Vision Pro |
+| **Each Mac window as its own panel** | None | – | No first-party way does this: Apple's per-app streaming is only for Vision Pro, and Valve's desktop streaming needs a Windows SteamVR host. So Frame Control does it itself |
+| Mac keyboard and trackpad driving the headset | **Bluetooth HID** | No | macOS can't act as a Bluetooth keyboard or mouse. A real Bluetooth keyboard paired with the Frame still works |
+| Mac keyboard and trackpad | **KDE Connect** (KDE) | No | The Frame has no `kdeconnectd` and it isn't on Flathub. Its Mac app has no keyboard or mouse sharing (**inferred**), and on Wayland it can only reach the desktop panel |
+| Mac keyboard and trackpad | **xrdp** (Valve, Developer Mode) | No | It runs a separate Linux session that you view on the Mac. It isn't the headset's view, and it doesn't carry input the other way |
+
+What that leaves is our own stream: nothing to install on the Mac or the
+Frame, and whole screens or single windows. Here the Mac's own keyboard and
+trackpad need no forwarding, because the windows are still on the Mac. The
+laser is the only input that has to be sent back.
+
+Other routes that were compared:
+
+| Option | One screen | Each window | Speed | Verdict |
+|---|---|---|---|---|
+| Sunshine → Moonlight | ✓ | – | Good | Sunshine's macOS support is still experimental ([discussion #777](https://github.com/orgs/LizardByte/discussions/777)), and it captures whole screens only |
+| Virtual Desktop, Immersed | – | – | – | No Frame client as of September 2026 |
+| **Frame Control's own stream** | ✓ | ✓ | Hardware H.264, sending only changed frames | **Built** |
+
+To type into VR surfaces other than these panels (SteamVR's dashboard,
+games), the Frame supports a uinput keyboard and mouse without sudo
+(verified 2026-09-27: `steamos` is in `input`, and `/dev/uinput` is
+`root:input 660`). That's a separate feature, not part of this one.
+
+## Steam's own streaming
+
+The Frame is built around Steam streaming, so this was checked first
+(2026-09-28). It fits Mac games, not Mac windows.
+
+- **VR streaming from the Mac: no.** The Frame streams VR from a PC running
+ SteamVR ("Steam Link" with foveated streaming). SteamVR dropped macOS in
+ 2020, and Valve lists PCs, laptops, Steam Deck and Steam Machine as
+ hosts, never a Mac (**documented**:
+ [UploadVR](https://www.uploadvr.com/steamvr-drops-mac-support/),
+ [Road to VR](https://roadtovr.com/steam-frame-game-certification-specs/)).
+- **Flat Remote Play from the Mac: probably, for Steam games.**
+ - Steam on this Mac has streaming on, and the two Steam clients already
+ see each other. The Frame's `remote_connections.txt` shows it
+ connecting directly to "Alexs-MacBook-Pro-7" at 192.168.1.211:27036,
+ and the Mac's shows the Frame connecting over Wi-Fi and over the USB-C
+ link (**verified** in both clients' logs).
+ - Whether a stream then starts, and how a flat game looks in the headset
+ (reviews describe a theater screen), is **not tested yet**. The Frame's
+ Steam was crash-looping during this session (below).
+ - Mac-hosted Remote Play has a long-standing report of the stream
+ closing as the game loads
+ ([Steam forum](https://steamcommunity.com/groups/homestream/discussions/1/574921459914429988/),
+ **reported**).
+- **The Mac desktop through Steam: untested; single windows: no.** Valve
+ says Remote Play shows the host's desktop when the game loses focus
+ ([Steam Remote Play FAQ](https://help.steampowered.com/en/faqs/view/0689-74B8-92AC-10F2),
+ **documented**), so a whole Mac screen may be reachable by starting a
+ game, then switching away from it. Nobody has tried that from a Mac
+ host. It would still be one screen in one panel: Remote Play has
+ nothing like one panel per Mac window, so Frame Control's own stream
+ stays the way to see separate windows.
+- **Steam has a "stream desktop" call, and it pairs with a Mac
+ (verified 2026-09-28).** In the Remote Play device list, the Frame's
+ Steam UI calls `SteamClient.RemotePlay.StartDesktopStream()`
+ for a connected device. Called over CDP with the Mac's client ID, it made
+ the Mac's Steam show "Authorize Device" and ask for a 4-digit code shown
+ on the Frame. Once the code was entered, the Mac logged
+ `k_ERemoteDeviceAuthorizationSuccess`. No stream started in that attempt,
+ and a second attempt, now that the device is authorized, is the next
+ test. If it streams the Mac's desktop, that's a whole-screen option built
+ into Steam: one panel, Valve's encoder and transport. It still wouldn't
+ give each window its own panel.
+- **What Steam's work did give us: the USB-C link.** Plugged into the Mac,
+ the Frame appears as a network port called "Steam Frame". Steam's Remote
+ Play discovery uses it, and so does Frame Control's stream now (see
+ "USB-C, when it's plugged in" below).
+
+Next steps, once Steam on the Frame is healthy:
+
+1. Call `StartDesktopStream` again now that the Frame is authorized, and
+ compare its latency and sharpness with Frame Control's stream of the same
+ screen.
+2. Stream the one Mac game installed here (Fortune Mill) from the Frame's
+ library, over Wi-Fi and over USB-C.
+3. Record whether it starts, how it's shown, and its latency. Steam's
+ streaming overlay shows this; our benchmark can't measure it.
+4. While streaming, switch away from the game on the Mac, and see whether
+ the Mac's desktop appears in the headset, and whether its keyboard and
+ pointer work.
+5. If it works, Frame Control's Games page could offer "Stream from the
+ Mac" for Mac-installed games.
+
+## How it works
+
+```
+Mac Frame
+ScreenCaptureKit (one window or display)
+ → VideoToolbox H.264 (hardware, low-latency,
+ no B-frames)
+ → frame-mac-view, 127.0.0.1 ──ssh -R──→ 127.0.0.1:479xx
+ → Chromium app window per stream
+ (WebCodecs decode), on gamescope's
+ X display, tagged STEAM_GAME
+ → its own SteamVR panel
+ ← CGEvent (clicks, drags, wheel, keys) ←──── pointer, wheel and key events
+```
+
+- **The agent** is `mac/bin/frame-mac-view`, built from `mac/frame-mac-view`
+ (Swift, no dependencies; `build.sh`). Frame Control's server starts it on
+ first use and stops it on quit.
+ - It captures with ScreenCaptureKit, which sends frames only when something
+ changes, so idle windows cost nothing.
+ - It encodes in hardware with VideoToolbox's low-latency rate control (plain
+ real-time mode where that's unavailable).
+ - While anyone is watching, it keeps the Mac's display awake. A sleeping
+ display isn't drawn, so there would be nothing to capture.
+- **The link** is an `ssh -R` tunnel on its own connection. It's encrypted and
+ works anywhere `ssh frame` works, Tailscale included, with no firewall
+ changes on the Mac. If the headset sleeps or the network drops, Frame
+ Control reopens the tunnel on the same port, and open viewers reconnect by
+ themselves.
+- **USB-C, when it's plugged in.** Connected to the Mac by cable, the Frame
+ is also a USB network device: macOS lists a network port called "Steam
+ Frame", and the Frame's `usb0` answers in under 1 ms. Frame Control
+ checks for it each time it opens the tunnel and uses it when it's there,
+ with the Frame's usual SSH host key. Otherwise it uses the normal path.
+ `FRAME_MACVIEW_USB=0` turns this off. **Verified** 2026-09-28, in two
+ interleaved pairs of runs:
+
+ | | USB-C | Wi-Fi (Tailscale) |
+ |---|---|---|
+ | test: content p50 / p95 | 6.9–7.3 / 8.4–8.8 ms | 9.8–10.1 / 12.1–12.3 ms |
+ | test: click to drawn p50 | 16.6–16.9 ms | 26.4–27.8 ms |
+ | scroll: content p95 | 23.0–23.5 ms | 31.4–36.9 ms |
+ | scroll: late frames | 3.2–3.3% | 4.7–7.0% |
+
+ (`bench/results/2026-09-28-*-usb1.json`, `-usb2`, `-wifi1`, `-wifi2`.)
+- **Access.**
+ - Frame Control's own key never leaves the Mac.
+ - Each viewer is opened with a **single-use ticket**. It's tied to one
+ window or display and expires after a minute. It's spent as soon as the
+ viewer confirms it has received its reconnect key. Until then, a retry
+ gets the same key, so a connection lost at that moment doesn't strand
+ the viewer. Stop revokes tickets that haven't been used yet.
+ - After that, the viewer holds a reconnect key for that one source, in
+ memory only. **Stop** revokes it.
+ - Remaining risk: a program running as `steamos` on the Frame could read a
+ ticket from Chromium's command line in the first second or so and use it
+ first. That gets it the one source being opened, not the Mac, and the
+ real viewer would then fail to connect. Android apps in Lepton run in
+ their own podman container, so they shouldn't see the Frame's process
+ list (inferred, not checked).
+- **The viewer** is `ui/mac-view.html`, served by the agent. It opens on the
+ Frame as a Chromium app window, preferring Chromium XR (`~/chromium-xr`,
+ built with H.264) over Flathub Chromium.
+ - The page puts `[fcNNNNN]` in its title. The launcher finds the window by
+ that tag and sets `STEAM_GAME` to a stable id per source, which gives it
+ its own panel (see [panels.md](panels.md)). The same Mac window gets the
+ same panel id each time.
+ - It decodes with WebCodecs. If it falls behind, it skips to the next
+ keyframe instead of showing old frames late.
+ - It falls back to JPEG stills (**Compatible** quality) where H.264 isn't
+ available.
+- **Flow control.** The agent never lets frames queue up anywhere on the
+ way. It skips capture frames *before* encoding, so no reference frame goes
+ missing, and it lowers the bitrate, then the frame rate, then the size, to
+ fit the link (see [Adapting to the network](#adapting-to-the-network)).
+- **Input.**
+ - A click on a window's panel brings that Mac window to the front
+ (Accessibility API), then clicks at the same point. Double clicks, right
+ clicks, drags and the wheel work too.
+ - Keys typed into the panel are sent as Mac key codes. Any keys or buttons
+ still held down are released if the viewer loses focus or disconnects, or
+ when the stream stops. Characters the key
+ table doesn't know, such as those from other keyboard layouts, are typed
+ as text.
+ - The Mac's own keyboard and trackpad keep working as normal. Click a panel
+ with the laser, then type on the Mac.
+
+## Permissions (Mac)
+
+- **Screen Recording**, to see windows. Without it, the card asks for it.
+- **Accessibility**, so input from the headset reaches the Mac. Without it the
+ stream still works, and the viewer says clicks won't go through.
+
+Both are granted to Frame Control. After granting, press **Refresh**, which
+restarts the helper so it picks them up. The app is ad-hoc signed, so macOS
+may ask again after an update.
+
+## Quality settings
+
+| Setting | Long side | fps | Codec | Use |
+|---|---|---|---|---|
+| Sharp | 2560 | 60 | H.264, ~0.14 bits/pixel | Text-heavy windows on a strong link |
+| Balanced (default) | 1920 | 60 | H.264, ~0.1 bits/pixel | Most things |
+| Light | 1280 | 30 | H.264 | Weak Wi-Fi or Tailscale off the LAN |
+| Compatible | 1280 | 20 | JPEG | A Frame browser without H.264 |
+
+## Measuring
+
+Every frame carries a sequence number, and the agent records its journey on
+the Mac's clock (`Sources/Stats.swift`):
+
+| Stage | From → to |
+|---|---|
+| capture | the Mac composited it (ScreenCaptureKit's display time) → the agent got it |
+| queue, encode | → encoding started → the encoder finished |
+| network | → the viewer received it |
+| decode, draw | → WebCodecs decoded it → it was drawn on the page's canvas |
+| present | → the page's next animation frame |
+
+- **Clock sync.** The viewer syncs its clock to the Mac's the way NTP does:
+ it pings over the stream's own WebSocket and keeps the sample with the
+ shortest round trip. It then reports, in Mac time, when each frame arrived
+ (right away, so the agent can pace itself) and when it was decoded and
+ drawn (in batches every 250 ms).
+- **Input.** The first frame captured after a click or key carries that
+ event's id. So input latency is the viewer's event → injected on the Mac →
+ the first frame after it → drawn in the headset.
+- **Where to see it.**
+ - `GET /stats` (key required) returns every frame and input record.
+ - `/status` includes a two-second summary, which Frame Control's card
+ shows next to each live stream.
+ - In the headset, add `?stats=1` to the viewer or press
+ Ctrl+Alt+Shift+S for an overlay.
+- **The benchmark.** `scripts/macview-bench.py` runs fixed scenarios on the
+ real Frame, from the Mac, with nobody wearing the headset:
+ - **test** is the moving test pattern.
+ - **scroll** is a Chrome page on its own display scrolling at 240 pt/s,
+ which gives about 9 Mbit/s of real 1920×1290 video.
+ - **type** types into a Chrome text box, first fast and then with pauses.
+
+ It writes `bench/results/--.json`, compares two
+ results, and runs interleaved A/B tests between agent settings (`ab`).
+ Wi-Fi changes from minute to minute, so single runs at different times
+ aren't comparable. Throttled links come from a shaping relay on the Mac,
+ which needs no sudo (`--net 50@0,3@8,50@16` means 50 Mbit/s, then 3 from
+ 8 s, then 50 from 16 s).
+- **What's graded.** "Content" runs from when the Mac composited a frame
+ (or when ScreenCaptureKit delivered it, if that was earlier) to when it was
+ drawn in the viewer. The Frame's compositor adds its own delay after that.
+ That part is reported, but not graded: an unworn Frame throttles panels to
+ about 36 fps after a few seconds, and to 15 fps in standby, whatever they
+ draw. This was **verified** with a local canvas page that ran on the Frame
+ with no network involved (`bench/pages/present.html`). The compositor's
+ share needs a run with the headset worn.
+
+Targets: content p50 ≤ 25 ms (p95 ≤ 40), click to photon p50 ≤ 50 ms
+(p95 ≤ 70), 60 fps with ≤ 1% late frames, no stall over 100 ms, and adapting
+to a new link rate within 1 s.
+
+Baseline on 2026-09-28 (**verified**, home Wi-Fi, Tailscale, Balanced,
+`bench/results/2026-09-28-a3c6e5c-dirty-baseline-fixed.json`; ms p50/p95):
+
+| Scenario | Content | Input to drawn | fps drawn | Notes |
+|---|---|---|---|---|
+| test (1280×720) | 9.9 | 28.8 / 39.0 | 59 | encode 4.0, network 4.0, decode 1.3 |
+| scroll (1920×1290) | 14.7 | – | 50.4 | encode 6.7, decode 6.4 (software), 9.4 Mbit/s, 16% late |
+| type (1920×1290) | 16.7 | 51.2 / 73.2 | – | most of the input time is the Mac app reacting |
+
+After this work, with the controller on (**verified**, same setup,
+`bench/results/2026-09-28-9e4dcdd-final.json`; ms p50/p95). Content is
+now measured from the earlier of display time and delivery, which adds
+about 5 ms to scroll compared with the baseline's way of measuring:
+
+| Scenario | Content | Input to drawn | fps drawn | Grades |
+|---|---|---|---|---|
+| test | 10.5 / 16.7 | 29.6 / 36.3 | 60 | all within target |
+| scroll | 19.8 / 27.4 | – | 55.9 | fps, late frames (4.8%) and worst gap (222 ms) only "acceptable": Wi-Fi stalls (the Mac captured 57 fps in this run; earlier runs got 46–53 from virtual displays) |
+| type | 15.0 / 21.4 | 43.0 / 60.5 | – | all within target |
+
+Of the targets, click to photon is met without the Frame's compositor (the
+headset has to be worn to measure its share), and so is content latency.
+The frame-rate and no-stall targets aren't yet met while scrolling.
+
+**Worn** (**verified** 2026-09-28 22:00, `vrcmd --stats` activity level 1,
+home Wi-Fi over Tailscale, `bench/results/2026-09-28-39afb23-worn.json`;
+ms p50/p95):
+
+| Scenario | Content | Input to drawn | fps drawn | What happened |
+|---|---|---|---|---|
+| test | 10.8 / 15.3 | 25.7 / 34.4 | 58 | as unworn |
+| scroll | 22.2 / 141 | – | 37.7 | Wi-Fi queued 56–364 ms and held frames for 220–270 ms; the controller went to 2.6 Mbit/s and 45 fps |
+| type | 13.5 / 24.3 | 56.8 / 106 | – | slower replies than unworn (43 / 60) |
+
+The Frame's CPU wasn't the limit: about 27% in total, and the viewer took
+45% of one core. The link to a headset on someone's head is much rougher
+than to one lying still. The adaptation keeps latency bounded there, but
+the frame rate drops. The USB-C cable avoids Wi-Fi entirely. Frames
+actually shown were still about 39 fps for the test pattern while worn, so
+the unworn throttling isn't the whole story. The cause is **unknown**.
+
+What was learned (all **verified**, unless marked):
+
+- The biggest costs are encoding (4–7 ms), network (4–6 ms), and decoding
+ on the Frame. Chromium XR on the Frame decodes H.264 in **software**.
+- Wi-Fi alone stalls for 240–580 ms now and then, over Tailscale and over
+ the LAN alike. Over the LAN (`--host 192.168.1.237`) latency was no
+ better, but the Frame used about 8% less CPU, because tailscaled runs in
+ userspace there.
+- With no controller, a link that slows down queues without limit. In one
+ run, frames arrived 1.9 s late, and at worst 9.4 s late.
+- Tried, and no help, so not kept: VideoToolbox options (require hardware,
+ no frame delay, prioritise speed, a hard data-rate cap), Chromium flags
+ (`--disable-gpu-vsync`, `--disable-frame-rate-limit`,
+ `--use-angle=vulkan`), and a 120 Hz virtual display.
+- Inconclusive, so off by default: keeping the Frame's Wi-Fi awake during
+ typing (`FRAME_MAC_VIEW_WARM=40`, a tiny message every 40 ms for 5 s
+ after input). Over three interleaved runs each, input p50 went 44 → 47 ms
+ and p95 92 → 71 ms, and the ranges overlapped widely
+ (`…-ab-keepwarm.json`). In that run, and in the baseline's typing, the
+ harness typed spaces as "+" (a URL-encoding bug, since fixed), so they
+ went through the text path rather than as space keys.
+- Pointer moves now go out on an 8 ms timer, not on the page's next
+ animation frame, which an unworn Frame slows to 15–36 Hz. This is
+ **inferred** to help dragging; the benchmark has no drag scenario yet.
+- Kept: the encoder's timestamps never jump more than two frame intervals.
+ Before this, the first frame after a pause got a quarter of a second's
+ bit budget, and one P-frame reached 204 KB.
+
+## Adapting to the network
+
+`Sources/Controller.swift`, per stream, latency first:
+
+- **The gate.** The viewer acknowledges every frame as it arrives. A new
+ frame is sent only while the oldest unacknowledged one is younger than
+ the path's usual round trip, plus one frame interval, plus room for this
+ link's normal jitter (1.5 times its recent spread, 25–80 ms). So frames
+ never queue in SSH, TCP or the Wi-Fi driver. While the link is stuck, the
+ newest picture waits and goes out as soon as it moves.
+- **The bitrate.** The link counts as congested when, for two checks in a
+ row (100 ms apart), round trips grow by more than 40 ms while the stream
+ uses much of its budget, or the gate holds frames back, or a frame is
+ stuck for 100 ms. Then the bitrate drops to a bit under what actually got
+ through: at least a fifth off, and at most half. Once the link has been
+ clear for a second, it rises by 10% steps, never above the quality
+ setting's bitrate.
+- **The tier.** When the bitrate stays low, and the stream is really
+ limited by the link rather than having little to send, it steps down:
+ 60 → 45 → 30 fps, then 75%, then 50% of the pixels. It goes straight to
+ the tier the bitrate supports after half a second, and steps back up one
+ tier at a time after two seconds with room to spare.
+- `FRAME_MAC_VIEW_ADAPT=0` turns it off, for comparison.
+
+Measured on the real Frame, 2026-09-28 (**verified**; interleaved A/B, off
+versus on, medians of the runs, ms):
+
+| Link | Scenario | Content p95, off → on | fps drawn, off → on | Result file |
+|---|---|---|---|---|
+| Clean Wi-Fi (3 runs each) | scroll | 32.3 → 33.6 | 57 → 56.2 | `…-ab-adapt-clean2.json` |
+| Clean Wi-Fi | test | 15 → 14.2 | 60 → 59.7 | same |
+| 50 → 3 → 50 Mbit/s at 8 s and 16 s (2 runs each) | scroll | **4670 → 72** | 45 → 42 | `…-ab-adapt-step3.json` |
+| 50 → 3 → 50 Mbit/s | test | 66 → 16 | 60 → 60 | same |
+
+- **Clean link.** On a clean link it costs nothing measurable. An earlier
+ version with a fixed gate slack lost 9 fps to Wi-Fi jitter while
+ scrolling (47 → 38 fps), and that's why the slack now follows the link's
+ jitter.
+- **Throttled link.** Without the controller, frames queued for up to 5.6 s
+ and never caught up while the link was slow (p95 1.8–5.6 s, second by
+ second). With it, in the two runs:
+
+ | | Run 1 | Run 2 |
+ |---|---|---|
+ | Worst second's p95 just after the drop | 219 ms | 428 ms |
+ | p95 back under 100 ms for 3 s in a row | after 1 s | after 4 s |
+ | Stepped down to 1440 px at 30 fps | 1.8 s after the drop | 3.5 s after |
+ | p95 per second after that, on the 3 Mbit/s link | 55–94 ms | 60–148 ms |
+
+ Sending one frame takes about 27 ms on that link by itself. After the
+ link recovered, the stream was back at full size and 60 fps in about
+ 7.5 s. It steps up one tier every two seconds, on purpose, so it doesn't
+ bounce. The 1 s adaptation target was met in one run of two.
+- **A hiccup on a small stream.** In one clean run, a Wi-Fi hiccup made an
+ earlier version halve the test pattern's bitrate five times and drop it
+ to half size. That cut couldn't help: the stream only sends
+ 0.47 Mbit/s. Now the controller estimates what a stream wants (captures
+ per second × average frame size). While a stream wants about half its
+ budget or less, no cut takes it below twice what it wants, so it doesn't change
+ tier.
+
+## Checked so far (2026-09-28)
+
+- **Mac (checked by hand, macOS 26.5.2).**
+ - The agent builds, and lists windows and displays.
+ - In a browser, the test pattern decoded at about 60 fps (H.264) and 30 fps
+ (JPEG, about 22 Mbps).
+ - A click in the viewer arrived at the same point in the source.
+ - `pmset -g assertions` showed the display-awake assertion only while a
+ stream was being watched.
+- **Automated** (`tests/test_macview.py`, on CI's macOS runner): the agent
+ builds (a build failure fails the job).
+ - `/ping` and the page are open; everything else needs the key.
+ - Tickets work once and only for their own source, and Stop revokes
+ reconnect keys.
+ - A WebSocket frame claiming 2^63 bytes closes that socket, and the agent
+ keeps running.
+ - A stream sends an SPS-led H.264 keyframe, and sends another when asked.
+ - Stop ends the stream on the Mac even if the viewer ignores it.
+ - The test doesn't decode video, time it, or check where clicks land.
+- **Linux aarch64 (verified in a stand-in, not on the Frame).** In an Arch
+ Linux ARM container with sshd, Xvfb as `:0` and Chromium 153:
+ - The real `show` path worked in 1–2.3 s: tunnel, ticket, launcher,
+ window found and tagged `STEAM_GAME`.
+ - Frame Control's key didn't appear anywhere in the stand-in's process
+ list.
+ - After the tunnel was killed, it came back on the same port within 4 s,
+ and the viewer reconnected by itself.
+ - Chromium decoded the stream in software with the GPU off.
+ - Stop closed the window, and Chromium exited.
+ - This test found and fixed a bug: in a C locale, `xwininfo` can't print a
+ title with non-ASCII characters, so the launcher reads `_NET_WM_NAME`
+ with `xprop`.
+- **On the Frame (verified 2026-09-28, SteamOS build 20260925.6191901,
+ from the Mac, nobody wearing the headset).** Frame Control's **Show** with
+ the test pattern:
+ - The panel was ready in 1.45 s. SteamVR logged `[Overlays] Created:
+ valve.steam.desktopgame.2001639889` (in `vrwebhelper_systemui.txt`).
+ - The window was tagged `STEAM_GAME`, and gamescope sized it to 1920×1080
+ although 1280×720 was asked for.
+ - Chromium XR decoded it live at about 60 fps: the frame counter advanced
+ 62 in 1.04 s.
+ - Over home Wi-Fi, the frames in the viewer window had been drawn on the
+ Mac about 11–17 ms earlier, plus `xwd`'s own time. That's measured
+ against the two clocks, which were 37–39 ms apart (±4 ms, measured over
+ one SSH session). SteamVR's compositor and the display come on top.
+ - Clicks and keys injected with XTest into gamescope's Xwayland didn't
+ reach the page. That's inconclusive, not a failure: XTest on gamescope
+ isn't how real input arrives. The laser should arrive as a left mouse
+ button and the thumbstick as a wheel, because the window has an app id
+ (from gamescope's source; see [panels.md](panels.md)).
+- **Not yet checked:**
+ - Clicking, dragging and scrolling with the laser while wearing the
+ headset.
+ - Real window capture and input on a Mac with both permissions granted.
+ - Keys from SteamVR's on-screen keyboard.
+ - Whether Flathub Chromium has H.264. Chromium XR is used when it's
+ installed, as it is on this Frame.
+ - Latency with real, busy windows at Sharp.
+ - A benchmark run while wearing the headset. Only then does the Frame show
+ panels at full rate, so only then can the compositor's share of the
+ latency, and the frame rate you actually see, be measured.
+
+## Limits
+
+- Only windows on the Mac's current desktop (Space) are listed, and
+ minimised windows can't be captured.
+- A window's panel shows only that window. Its menus and sheets are separate
+ windows on the Mac, so open them from the Mac or use **Whole screen**.
+- Keys go to whichever Mac window is in front. Clicking a panel brings its
+ window to the front first.
+- Ctrl stays Ctrl. On the Mac, copy is ⌘C, so use Meta+C on a keyboard paired
+ with the Frame.
diff --git a/docs/mac-window-separation.md b/docs/mac-window-separation.md
new file mode 100644
index 0000000..4c0d169
--- /dev/null
+++ b/docs/mac-window-separation.md
@@ -0,0 +1,200 @@
+# Real separation of Mac windows in the headset
+
+Research and experiments on 2026-09-28 (macOS 26.5.2, Apple M5 Pro), for
+making each streamed Mac window truly independent. Builds on
+[mac-in-headset.md](mac-in-headset.md). Labels: **verified** (tried here),
+**documented**, **source** (read in someone's code) or **reported**.
+
+## What "separate" lacks today
+
+Today each window is captured on its own
+(`SCContentFilter(desktopIndependentWindow:)`), but it still lives on the
+Mac's one desktop:
+
+- **Clicks.** A click has to raise the window first, so it reorders the
+ Mac's windows, steals focus and moves the real cursor.
+- **Child windows.** A window's menus, popovers, sheets and tooltips are
+ separate windows, so they aren't in its panel. Apple documents that the
+ single-window filter leaves them out.
+- **Size.** A panel's size is tied to the window's size on the Mac screen.
+- **Hidden windows.** Minimised windows, and windows on other Spaces, can't
+ be shown live.
+
+## How the Mac draws, and where pixels can be read
+
+Apps draw with Core Animation into IOSurfaces. WindowServer's compositor
+stacks every window onto each display, including virtual ones, and sends
+the result to the screen. Pixels can be read at three points:
+
+| Where | How | Gets | Doesn't get |
+|---|---|---|---|
+| One window's content | ScreenCaptureKit `desktopIndependentWindow` (public, what we use) | Live, zero-copy, even when covered by other windows (**documented**) | Menus, popovers, sheets. It pauses while the window is minimised (**reported**) |
+| One window plus its children | `SCStreamConfiguration.includeChildWindows` (macOS 14.2+, **reported**) | Menus, popovers and sheets attached to the window | Anything the app puts outside the window's bounds |
+| A whole display | ScreenCaptureKit display filter, with apps or windows included or excluded | Everything on that display, cursor included | Only what's on that display |
+| A snapshot of any window | Private `CGSHWCaptureWindowList`, which AltTab uses for minimised windows and other Spaces (**source**: `alt-tab-macos` `PrivateApis.swift`) | Minimised windows and other Spaces | It's one still image, not a live stream |
+| Another app's layer tree | Private `CALayerHost` with a context id | A live, zero-copy picture | It only works when the other app cooperates, so it's no good for arbitrary windows (**reported**) |
+
+`CGWindowListCreateImage` and `CGDisplayStream` are obsolete in the macOS 15
+SDK (**reported** by MacPorts and JUCE). Nothing reads another app's pixels
+without the Screen Recording permission.
+
+## The idea: each window gets its own virtual display
+
+A virtual display (the private `CGVirtualDisplay`, as used by BetterDisplay,
+DeskPad and quest-display) is a compositor target with no physical screen.
+Put one streamed window alone on its own virtual display, sized to its
+panel, and capture the whole display:
+
+- **Nothing can overlap it.** A plain click at that point always lands on
+ that window, so input doesn't need raising or background-event tricks.
+- **Menus, sheets, popovers, tooltips and context menus appear on the same
+ display, so they're in the panel.** With "Displays have separate Spaces"
+ on (it is on this Mac), each display also has its own menu bar, so the
+ app's menu bar can be part of the panel.
+- **The panel's size is the display's size,** in HiDPI. Resizing the panel
+ means changing the display mode and resizing the window to fill it
+ (Accessibility API).
+- **Minimised windows and other Spaces stop being a problem,** because
+ streamed windows live on their own displays.
+- **The cursor goes where the laser points.** That display's panel is the
+ one being used, much as Vision Pro's Mac Virtual Display works. Keys from
+ the Mac's keyboard go to the window last clicked.
+
+### Verified here (no permission needed)
+
+- **Creating one.** It needs no permission or entitlement. 1920×1080 HiDPI
+ gives a 3840×2160-pixel, 60 Hz display, placed next to the built-in
+ screen, and `NSScreen.screensHaveSeparateSpaces` was true.
+- **Many at once.** 16 were created at once with no error.
+- **Placement.** `CGConfigureDisplayOrigin` far away failed with error
+ 1014: macOS keeps displays edge to edge. Disabling one with the private
+ `CGSConfigureDisplayEnabled` from a command-line tool also failed with 1014.
+- **Removal is deferred while the physical screen sleeps.** With the
+ built-in display asleep, virtual displays were not removed when released,
+ or when their process exited, even from their own `.app`. A second display
+ with the same vendor, product and serial couldn't be created. All of them
+ disappeared as soon as the screen woke (`caffeinate -u`). The helper must
+ therefore:
+ - keep a fixed pool of displays and reuse them rather than create new ones;
+ - keep the screen awake while they exist, which it already does while
+ streaming.
+
+### Built: "Give each window its own display"
+
+Frame Control's helper now does this (`mac/frame-mac-view/Sources/Separate.swift`),
+and it's the default in the Tools card. Streams of this kind are named
+`separate:`.
+
+- For each window shown, it creates a HiDPI virtual display. The display is
+ sized to the window plus a menu bar, with a fresh serial each time, so a
+ display left over while the screen slept can't block a new one.
+- The window is moved onto the display and resized to fill it (Accessibility
+ API), and the whole display is captured.
+- On **Stop** the window goes back where it was, and the display is released.
+- Plain per-window capture is still there: untick "Give each window its own
+ display". Separate mode needs Accessibility (to move the window), and
+ without it, Show says so rather than quietly falling back.
+
+Verified 2026-09-28 (macOS 26.5.2, M5 Pro), using a TextEdit test document
+and the benchmark's Chrome windows:
+
+- **Separation works.** The window moved onto its own display and streamed,
+ with its first frames in 3.8 s. The display showed TextEdit's own menu bar.
+- **Child windows come along.** A context menu and the Page Setup sheet
+ opened on the same display and were in the capture.
+- **Input.** Typing through the stream reached the window. The benchmark's
+ typing scenario does this on every run.
+- **Stop.** Stop put the window back at exactly its old size (656×422), and
+ the display was removed.
+- **The helper must run a real Cocoa event loop.** Earlier, it ran only a
+ `RunLoop`. With that, AppKit never learned about new displays:
+ - `NSScreen.screens` never listed them, so placing the window always waited
+ 3 s and then gave up;
+ - the display's HiDPI mode never applied, so windows were captured at 1×
+ (1280×860 instead of 1920×1290 pixels) and text was soft.
+
+ Running `NSApplication` (with no Dock icon) fixed both. The screen now
+ appears within 100 ms, and captures are at 2× (**verified** in
+ `bench/results/*-baseline.json` against `*-baseline-fixed.json`).
+- **Frame rate.** Chrome drew at 60–61 fps on the virtual display, but
+ ScreenCaptureKit delivered only about 46–53 fps from it, whereas the
+ built-in ProMotion screen gave 121 fps (**verified**). Neither a 120 Hz
+ virtual display (`FRAME_MAC_VIEW_VD_HZ=120`) nor a looser
+ `minimumFrameInterval` changed that. Cause unknown.
+- **Windows that keep their own size** (Calculator, 230×408). The window
+ moved and streamed, but stayed small in the display's corner, so the panel
+ was mostly wallpaper. Now only that corner is captured
+ (`SCStreamConfiguration.sourceRect`): the menu bar and the window, at least
+ 480×360 points so menus fit. Clicks map to the cropped area. The first
+ frame arrived in 0.6–1.1 s, and on Stop the window went back and the
+ display was removed. A display's menu bar names the active app, so it
+ shows the window's own menus only once the panel has been clicked.
+- **Several windows at once** (on the Mac, with a local stand-in viewer that
+ acknowledges frames). Three and four Chrome windows, each scrolling on its
+ own display at 1920×1290 pixels, streamed at 53–56 fps and about
+ 9 Mbit/s each. The helper used 20% (three) or 24% (four) of one CPU core.
+ The hardware encoder is shared: its time per frame went from 6.7 ms for
+ one stream to 7–15 ms for three and 11–25 ms for four, so each extra
+ moving window adds latency to the others. Once in four runs, before a fix,
+ one window left its display when several displays appeared at the same
+ moment, and its stream stopped: nothing on that display was changing any
+ more. The helper now checks every second and puts the window back. Three
+ further runs with four windows were clean, and every display was gone
+ afterwards (checked with `CGGetOnlineDisplayList`).
+- **Quitting.** On SIGTERM, or when Frame Control goes away, the helper ends
+ every stream first, so windows go back before their displays disappear.
+ Verified: a 900×600 window was back at 900×600 after SIGTERM. Closing a
+ viewer 0.05–1.5 s into startup left the window at its old size and no
+ extra display.
+- **A consent prompt.** macOS 26 asks whether to let ScreenCaptureKit apps
+ "bypass the system private window picker". The prompt appeared *on the
+ virtual display*, so it would show up inside the headset panel. Allow it
+ once on the Mac.
+
+### Still to check
+
+1. That a context menu opens over the text being clicked, not just somewhere
+ on the display. This needs a retest after the consent prompt above is
+ allowed.
+2. Stage Manager, which is reported to undo Accessibility resizes.
+3. Where the Dock and the cursor go on a virtual display.
+4. Closing the lid with virtual displays attached (clamshell).
+5. Many moving windows at once in the headset: whether to lower the bitrate
+ or frame rate of panels you aren't using, so the one you are using keeps
+ the encoder to itself.
+6. All of it in the headset, with the laser.
+
+## Input without disturbing the Mac (a finer option)
+
+For windows that stay on the Mac's own screen, input can go to a window
+behind others without raising it:
+
+- **Background click.** `CGEventPostToPid` with the CGEvent fields 91 and
+ 92, which say which window a click is for (`kCGMouseEventWindowUnderMousePointer`
+ and `...ThatCanHandleThisEvent`), plus a window-relative location
+ (**reported**, reverse-engineered, needs testing).
+- **Focus without raise.** yabai makes a window key without raising it by
+ posting event records with the private `SLPSPostEventRecordTo` (**source**:
+ `yabai/src/window_manager.c`).
+- **Known failures.**
+ - Chromium and Electron ignore background clicks unless they're built
+ with the 2026 `acceptsFirstMouse` fix (electron/electron#54493).
+ - Games and canvas apps often need real activation.
+ - Password fields (Secure Event Input) drop synthetic keys.
+ - IME composition, as for Chinese or Japanese input, isn't reliable.
+
+With a virtual display per window, most of this isn't needed. It's worth
+having for hovering over one panel while typing in another.
+
+## Encoding and transport
+
+- **Codec.** Keep H.264 4:2:0. Apple's High Performance Screen Sharing uses
+ 4:4:4 over two virtual displays (**documented**), but the Frame's Chromium
+ decodes H.264 in software, and no 4:4:4 decode path is confirmed there.
+- **Sharper static text.** When a panel has been still for a moment, send a
+ high-quality refresh, either a keyframe at low quantisation or a lossless
+ WebP overlay, so static text is sharp. Moving content stays as video.
+- **Transport.** Keep WebSocket over SSH for now, as it works everywhere.
+ WebRTC (UDP, congestion control) is the proven step up for Wi-Fi.
+ WebTransport over QUIC is promising, but its server side on macOS is
+ unverified.
diff --git a/docs/open-questions.md b/docs/open-questions.md
index 37ecaee..770f20a 100644
--- a/docs/open-questions.md
+++ b/docs/open-questions.md
@@ -130,6 +130,19 @@ Still open: 4, 6, 7, 12–15, 16 (off-LAN and after a reboot), 17–21.
- The Mac EDL flashing script in `~/Downloads/steam-frame-recovery/` hasn't
been run against a Frame.
+## Mac in the headset (2026-09-28)
+
+The test pattern streams to the Frame as its own panel at about 60 fps
+(verified, build 20260925.6191901; see
+[mac-in-headset.md](mac-in-headset.md#checked-so-far-2026-09-28)). Still to
+check in the headset:
+
+- Laser clicks, drags and thumbstick scrolling in a viewer panel.
+- Real window capture and input once Screen Recording and Accessibility are
+ granted to Frame Control.
+- Keys from SteamVR's on-screen keyboard.
+- Whether Flathub Chromium decodes H.264 (otherwise use Compatible).
+
## Unconfirmed claims made in these docs
- `/home` and `/etc` persist across Frame OS updates. This is inferred from
diff --git a/docs/streaming.md b/docs/streaming.md
index 778e886..c4fb64f 100644
--- a/docs/streaming.md
+++ b/docs/streaming.md
@@ -38,7 +38,8 @@ flat 2D desktop streaming into a window on the Frame's Linux desktop.
| Option | Setup | Confidence | Verdict |
|---|---|---|---|
-| **macOS Screen Sharing (VNC) → Remmina on the Frame** | **Mac:** System Settings → General → Sharing → Screen Sharing on → (i) → enable "VNC viewers may control screen with password". **Frame:** `./scripts/install-apps.sh remmina` from the Mac, then open Remmina in the headset and connect to `vnc://.local` | **Verified 2026-09-27** (Frame BUILD_ID 20260925.6191901, macOS 27.0), in its own panel via `panel-on-frame.sh mac-screen`. Remmina is on Flathub for **aarch64** with VNC and RDP ([Flathub](https://flathub.org/apps/org.remmina.Remmina)). The Frame desktop runs Flatpaks ([UploadVR](https://www.uploadvr.com/flatpaks-open-source-steam-frame/)). macOS VNC is built in. | **Recommended.** Nothing to install on the Mac, and it's easy to set up. Noticeable lag, even at lower Remmina quality settings on a good 5 GHz link, where neither Wi-Fi nor the Frame's CPU was the bottleneck. Usable for reading and coding, but not for games. You'll type the Mac's hostname once in Remmina on the headset, then save the profile. To avoid even that, the script can pre-seed a Remmina profile over SSH (see below). |
+| **Frame Control → Tools → Mac in the headset** | Nothing to install. Allow Screen Recording and Accessibility for Frame Control, then press **Show** next to any window or screen | **Verified 2026-09-28** on the Frame (panel in 1.5 s, measured with `scripts/macview-bench.py`: about 10–20 ms from the Mac drawing a frame to the viewer drawing it); laser input not yet tried while wearing it | **Recommended.** Hardware H.264 over an SSH tunnel, adapting to the link. Each window becomes its own panel you can place anywhere. Laser clicks and scrolls, and the Mac's keyboard types. See [mac-in-headset.md](mac-in-headset.md) |
+| **macOS Screen Sharing (VNC) → Remmina on the Frame** | **Mac:** System Settings → General → Sharing → Screen Sharing on → (i) → enable "VNC viewers may control screen with password". **Frame:** `./scripts/install-apps.sh remmina` from the Mac, then open Remmina in the headset and connect to `vnc://.local` | **Verified 2026-09-27** (Frame BUILD_ID 20260925.6191901, macOS 27.0), in its own panel via `panel-on-frame.sh mac-screen`. Remmina is on Flathub for **aarch64** with VNC and RDP ([Flathub](https://flathub.org/apps/org.remmina.Remmina)). The Frame desktop runs Flatpaks ([UploadVR](https://www.uploadvr.com/flatpaks-open-source-steam-frame/)). macOS VNC is built in. | **Fallback** (whole screens only). Nothing to install on the Mac, and it's easy to set up. Noticeable lag, even at lower Remmina quality settings on a good 5 GHz link, where neither Wi-Fi nor the Frame's CPU was the bottleneck. Usable for reading and coding, but not for games. You'll type the Mac's hostname once in Remmina on the headset, then save the profile. To avoid even that, the script can pre-seed a Remmina profile over SSH (see below). |
| Sunshine (Mac) → Moonlight (Frame Flatpak) | `brew install` Sunshine on the Mac, then `./scripts/install-apps.sh moonlight` | Moonlight Flatpak supports **aarch64** ([Flathub](https://flathub.org/apps/com.moonlight_stream.Moonlight)). **Sunshine on macOS is poorly supported**: install problems on Apple Silicon/Sequoia, and no virtual gamepads ([LizardByte discussion #777](https://github.com/orgs/LizardByte/discussions/777)). | Try it if VNC is too laggy. Expect some friction. |
| Steam Remote Play with the Mac as host | Steam on the Mac, Steam Link/Remote Play on the Frame | macOS-hosted Remote Play is reported broken or flaky in 2024–2026 ([Steam discussion](https://steamcommunity.com/groups/homestream/discussions/1/574921459914429988/)) | Not recommended. It's only for games, if it works at all. |
| Immersed / Virtual Desktop | Vendor apps | Immersed has a Mac agent but no known Frame client. Virtual Desktop's developer said he'd "try" to port it ([NewsBreak](https://www.newsbreak.com/news/4892834783961-virtual-desktop-dev-says-he-ll-try-to-bring-the-app-to-steam-frame)). | Not available as of 2026-09-25. Check again later. |
@@ -86,6 +87,21 @@ its header. (Verified 2026-09-27.)
Going the other way, pointing a controller at the panel moves the Mac's mouse,
because Remmina forwards input (`viewonly=0`).
+## First-party options, and why they do or don't fit
+
+Checked 2026-09-28. The first-party way is usually the best one, so these are
+listed first; the sections above and below explain the alternatives.
+
+| Goal | First-party option | Fits? | Why, and what would make it easier |
+|---|---|---|---|
+| Type and point from the **iPhone** | **KDE Connect** (KDE; official [iOS app](https://apps.apple.com/app/kde-connect/id1580245991)) remote touchpad and keyboard, plus clipboard and files | **Best candidate, untested on the Frame** | The Frame doesn't have it (verified: no `kdeconnectd`), it isn't on Flathub, and the root is read-only, so it would have to run from `~` or a container. On Wayland it types through KWin, so it can only reach the desktop panel, not SteamVR or games (**inferred**). Steam Deck users report its remote input breaking after SteamOS updates ([SteamOS #1939](https://github.com/ValveSoftware/SteamOS/issues/1939)). If it works, Frame Control could install it and pair it for you. |
+| Type and point from the **Mac** | KDE Connect for macOS (KDE builds) | Same as above | Its Mac app sends clipboard and files but has no keyboard/mouse sharing (**inferred**). |
+| Either | **Bluetooth keyboard and mouse** paired in SteamOS (Valve) | Yes, with real hardware | Neither device can pretend to be one: iOS refuses the HID service ([Apple forums](https://developer.apple.com/forums/thread/733916)), and macOS has no built-in way. |
+| Either | **xrdp** in Developer Mode (Valve) | No | Input goes into a *separate* desktop shown on the Mac, not into what you see in the headset. |
+| **Mac screen** in the Frame | **Screen Sharing** (Apple's VNC server) + Remmina (already installed on this Frame, profile pre-seeded by `install-apps.sh`) | **Yes, closest to first-party** | Only the Mac side is first-party; Remmina is the client. Turn on System Settings → General → Sharing → Screen Sharing → (i) → "VNC viewers may control screen with password". Still to test in the headset (open question 11). |
+| Mac screen | **Steam Remote Play** with the Mac as host (Valve) | Probably not | macOS isn't a SteamVR host, and Mac-hosted Remote Play is reported broken ([Steam forum](https://steamcommunity.com/groups/homestream/discussions/1/574921459914429988/)). One quick test is worth doing: Steam on the Mac, then Remote Play from the Frame's Steam. |
+| Mac or **iPhone screen** | **AirPlay** (Apple) | Not officially | It's Apple's own mirroring for both, but Apple only licenses receivers to TV and speaker makers; nothing official runs on Linux. UxPlay (below) is the unofficial receiver. |
+
## C. Show the iPhone's screen inside the Frame
iOS only shares its screen two ways: **AirPlay** (Screen Mirroring in Control
diff --git a/docs/vr-apks.md b/docs/vr-apks.md
new file mode 100644
index 0000000..e57325c
--- /dev/null
+++ b/docs/vr-apks.md
@@ -0,0 +1,148 @@
+# VR APKs and Quest games in Lepton
+
+What it takes to run an immersive (OpenXR) Android app, including Meta Quest
+builds, on the Frame. Checked on SteamOS BUILD_ID 20260925.6191901, Lepton
+v2.8.14 (rootfs v2.8.11), SteamVR 2.18.1, on 2026-09-28, unless marked
+**inferred**.
+
+## How a VR APK reaches SteamVR (verified)
+
+- Lepton ships a standard Khronos system runtime manifest,
+ `/vendor/etc/openxr/1/active_runtime.json`, pointing at SteamVR's Android
+ client, `/data/steamvr/runtime/bin/androidarm64/vrclient.so`. That is the
+ host's `/opt/steamvr/bin/androidarm64/`, bind-mounted in.
+- An APK's own Khronos-style `libopenxr_loader.so` tries the runtime brokers
+ (`org.khronos.openxr.runtime_broker`, `…system_runtime_broker`), finds
+ neither, then falls back to that manifest. Nothing in the APK has to change
+ for discovery.
+- Install the APK without the flatscreen marker
+ (`python3 ui/frame_android.py install app.apk --vr`). The marker only
+ controls Lepton's 2D Android surface; the app itself has to start an
+ OpenXR session.
+- Lepton also loads Valve's `XR_APILAYER_VALVE_fdm_injection` layer from
+ `/vendor/etc/openxr/1/api_layers/implicit.d/`. Only layers in Valve's own
+ directories are picked up (`liblepton/vulkan_layers.sh`), so a third-party
+ layer has to ship inside the APK.
+
+**Open Brush 2.32.29, the Quest APK from its GitHub release (Unity OpenXR,
+Vulkan), works unmodified.** Its manifest already has `LAUNCHER` next to
+`com.oculus.intent.category.VR`. Unity asked for OpenXR 1.1, got
+`XR_ERROR_API_VERSION_UNSUPPORTED`, retried with 1.0 and succeeded. SteamVR
+took it as the scene app, created Touch, simple-controller and Frame-controller
+bindings, and the session reached `XR_SESSION_STATE_FOCUSED`. A headset capture
+(`ui/frame_vrshot.py`, after waking the compositor and closing the dashboard)
+showed a dark sky over a mountain horizon; nobody wore the headset to confirm
+it was Open Brush's scene or to try drawing.
+
+**Khronos `hello_xr` (Vulkan, 1.1.63 release APK) works unmodified:**
+`Instance RuntimeName=SteamVR/OpenXR RuntimeVersion=2.18.1`, 1728×1728
+swapchains per eye, session `IDLE → READY → SYNCHRONIZED` (the headset was
+not being worn, so it did not reach `FOCUSED`).
+
+## What SteamVR's Android runtime supports (verified, from `vrclient.so`)
+
+- **OpenXR 1.0 only.** An app requesting `XR_API_VERSION_1_0` works; one
+ requesting 1.1 (`XR_CURRENT_API_VERSION` in a 1.1 SDK) gets
+ `XR_ERROR_API_VERSION_UNSUPPORTED` from the runtime.
+- Extensions include `XR_KHR_opengl_es_enable`, `XR_KHR_vulkan_enable{,2}`,
+ `XR_KHR_composition_layer_depth`, `XR_KHR_locate_spaces`,
+ `XR_EXT_local_floor`, `XR_EXT_uuid`, `XR_EXT_palm_pose`,
+ `XR_EXT_hand_tracking`, `XR_EXT_eye_gaze_interaction`, and these Meta ones:
+ `XR_FB_display_refresh_rate`, `XR_FB_foveation{,_configuration,_vulkan}`,
+ `XR_FB_space_warp`, `XR_FB_swapchain_update_state`,
+ `XR_META_foveation_eye_tracked`, `XR_META_recommended_layer_resolution`,
+ `XR_META_vulkan_swapchain_create_info`, `XR_META_performance_metrics`.
+- Not present: `XR_FB_passthrough`, `XR_FB_hand_tracking_*`,
+ `XR_FB_spatial_entity*`, `XR_FB_color_space`,
+ `XR_KHR_android_thread_settings`, `XR_OCULUS_*`.
+- Interaction profiles include `oculus/touch_controller`, `khr/simple_controller`,
+ `valve/frame_controller` and the usual PC controllers. Valve documents Touch
+ bindings as a working fallback on the Frame controllers.
+
+## What stops a Quest APK (verified with Wolvic 1.9, `oculusvr` build)
+
+1. **Lepton won't start it.** Lepton's `apk-info-extractor` only accepts an
+ activity whose intent filter has `android.intent.action.MAIN` and
+ `android.intent.category.LAUNCHER`. Quest apps use
+ `com.oculus.intent.category.VR` instead, so Lepton logs `APP_ACTIVITY is
+ empty` and exits. There is no override. **Fix:** add the `LAUNCHER`
+ category to that intent filter and re-sign. After that, Wolvic started.
+2. **OpenXR 1.1.** Wolvic's Quest build then requested OpenXR 1.1 and aborted
+ on `XR_ERROR_API_VERSION_UNSUPPORTED`. Unity's OpenXR plugin retries with
+ 1.0 (Open Brush, above), so this mostly bites native and non-Unity apps. **Fix (inferred):** an API layer
+ inside the APK that asks the runtime for 1.0 and maps the 1.1 core
+ functions to the extensions the runtime does have (`XR_KHR_locate_spaces`,
+ `XR_EXT_local_floor`, `XR_EXT_uuid`, `XR_EXT_palm_pose`).
+3. **Lepton's missing clipboard service** still applies to VR apps. The Godot
+ XR Tools demo's Quest build (itch.io) dies in `Godot.` casting the
+ null clipboard service to `ClipboardManager`, before any OpenXR call. See
+ the clipboard table in [apks.md](apks.md).
+4. **Not yet reached:** required Meta-only extensions (each app differs),
+ swapchain formats (the Lynx Wolvic build needed `GL_SRGB8_ALPHA8`), and
+ Meta platform services.
+
+The loader was never the problem: Wolvic's Quest `libopenxr_loader.so` is a
+Khronos-style loader and found SteamVR through `/vendor`.
+
+## Out of scope
+
+- **Meta entitlement.** Apps that call the Oculus Platform SDK
+ (`libovrplatformloader.so`) to check the Quest store licence need Meta's
+ services. Frame Control won't work around that.
+- **VrApi-era apps** (`libvrapi.so`, before OpenXR) need an API translator,
+ not a patch.
+
+## Frame Control does this for you
+
+APK uploads and `python3 ui/frame_android.py install app.apk` detect VR
+manifest categories, Samsung's `vr_only` flag and the arm64 OpenXR loader.
+VR apps default to immersive mode without the flatscreen marker. The upload
+selector or CLI `--flat` / `--vr` overrides that choice. Compatibility notes
+identify legacy VrApi, Meta platform SDK and OpenXR libraries.
+
+Lepton only starts an `` whose MAIN intent filter has LAUNCHER; it
+ignores ``, which is where Godot 4 exports put LAUNCHER. When no
+real activity qualifies, Frame Control adds LAUNCHER to the VR activity's MAIN
+filter, or to the activity the launcher alias targets, then repacks and v2-signs
+the APK locally before copying it; `meta.json` records
+`"patched": ["launcher"]`. Unchanged ZIP members retain their compressed
+bytes; stored libraries are aligned to 16 KiB. The RSA signing identity lives
+in Frame Control's per-user app-data directory as `apk-signing-key.json`
+(mode 0600). Keep this key to preserve the signer on subsequent patched
+updates. A re-signed APK cannot update an installation signed by its original
+publisher; Android also treats it as a different signer for signature checks.
+
+VR apps with an arm64 OpenXR loader also get the OpenXR compatibility layer
+([frame/openxr-compat](../frame/openxr-compat/README.md)): an implicit API
+layer in the APK's `assets/openxr/1/api_layers/implicit.d/`, which the app's
+own loader picks up next to Valve's layer. It asks SteamVR for OpenXR 1.0 when
+the app wants 1.1 and enables the extensions that became 1.1 core; maps
+`xrLocateSpaces` to `xrLocateSpacesKHR` and `grip_surface` to `palm_ext`; drops
+1.1 controller profiles SteamVR doesn't know; stubs
+`XR_KHR_android_thread_settings` and `XR_OCULUS_android_session_state_enable`;
+and keeps the current refresh rate when SteamVR refuses a requested one.
+`meta.json` records `"patched": ["openxr-compat"]`. Skip it with
+`install … --no-xr-compat`. Its decisions go to logcat under `FrameXrCompat`.
+
+Verified on the headset (2026-09-28):
+
+- **Wolvic 1.9, Quest build**, installed as downloaded: Frame Control added
+ `LAUNCHER` and the layer. The layer turned OpenXR 1.1.48 into 1.0.63, the
+ instance and session were created, and a 144 Hz refresh request that SteamVR
+ refused was kept at the current rate. The session reached `SYNCHRONIZED`;
+ then Wolvic's Gecko engine crashed (null SIGSEGV on its Gecko thread, the
+ same crash its Lynx build has), which is Wolvic's, not OpenXR's.
+- **Open Brush, Quest build**, with the layer: 1.1.54 → 1.0.63, the thread
+ settings stub in use, `bytedance/pico4_controller` bindings dropped, and the
+ session reached `FOCUSED`, the same as without the layer.
+
+Inspect or prepare an APK without contacting the headset:
+
+```sh
+python3 ui/frame_android.py info app.apk
+python3 ui/frame_android.py patch app.apk patched.apk
+python3 ui/frame_android.py patch app.apk patched.apk --add assets/openxr/1/api_layers/implicit.d/X.json=X.json --add lib/arm64-v8a/libX.so=libX.so
+```
+
+The patch fixes Lepton's launch-category requirement. It does not supply an
+OpenXR 1.1 translation layer, Meta services or a VrApi implementation.
diff --git a/frame/openxr-compat/.gitattributes b/frame/openxr-compat/.gitattributes
new file mode 100644
index 0000000..15fc64b
--- /dev/null
+++ b/frame/openxr-compat/.gitattributes
@@ -0,0 +1,2 @@
+# Preserve upstream headers byte-for-byte, including their existing whitespace.
+vendor/** -whitespace
diff --git a/frame/openxr-compat/.gitignore b/frame/openxr-compat/.gitignore
new file mode 100644
index 0000000..cebbcdf
--- /dev/null
+++ b/frame/openxr-compat/.gitignore
@@ -0,0 +1 @@
+/build-*/
diff --git a/frame/openxr-compat/CMakeLists.txt b/frame/openxr-compat/CMakeLists.txt
new file mode 100644
index 0000000..6f75b6b
--- /dev/null
+++ b/frame/openxr-compat/CMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.22)
+project(FrameXrCompat LANGUAGES CXX)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+add_library(compat_logic INTERFACE)
+target_include_directories(compat_logic INTERFACE . vendor)
+if(ANDROID)
+ add_library(XrApiLayer_FRAME_compat SHARED layer.cpp)
+ target_link_libraries(XrApiLayer_FRAME_compat PRIVATE compat_logic log)
+ target_compile_definitions(XrApiLayer_FRAME_compat PRIVATE XR_USE_PLATFORM_ANDROID XR_NO_PROTOTYPES)
+ target_compile_options(XrApiLayer_FRAME_compat PRIVATE -O2 -fvisibility=hidden -Wall -Wextra -Werror)
+ target_link_options(XrApiLayer_FRAME_compat PRIVATE -Wl,-z,max-page-size=16384 -Wl,--no-undefined -Wl,--exclude-libs,ALL -Wl,-s)
+else()
+ enable_testing()
+ add_executable(compat_tests tests/compat_tests.cpp)
+ target_link_libraries(compat_tests PRIVATE compat_logic)
+ target_compile_options(compat_tests PRIVATE -Wall -Wextra -Werror)
+ add_test(NAME compat_logic COMMAND compat_tests)
+ add_executable(dispatch_tests tests/dispatch_tests.cpp)
+ target_link_libraries(dispatch_tests PRIVATE compat_logic)
+ target_include_directories(dispatch_tests PRIVATE tests/shims)
+ target_compile_definitions(dispatch_tests PRIVATE XR_USE_PLATFORM_ANDROID XR_NO_PROTOTYPES)
+ target_compile_options(dispatch_tests PRIVATE -Wall -Wextra -Werror)
+ add_test(NAME layer_dispatch COMMAND dispatch_tests)
+endif()
diff --git a/frame/openxr-compat/README.md b/frame/openxr-compat/README.md
new file mode 100644
index 0000000..5978979
--- /dev/null
+++ b/frame/openxr-compat/README.md
@@ -0,0 +1,254 @@
+# Frame OpenXR compatibility API layer
+
+`XR_APILAYER_FRAME_compat` is an APK-local implicit API layer for Steam Frame's
+OpenXR 1.0 Android runtime. It translates selected 1.1 functionality; it is not
+a conformant replacement for a complete 1.1 runtime. Device facts are in
+[../../docs/vr-apks.md](../../docs/vr-apks.md). No headset was contacted during
+implementation. Loading, Valve-layer coexistence, controller usability, and
+Wolvic startup/rendering on Lepton remain **unverified on device**.
+
+## Behavior
+
+- The negotiated `createApiLayerInstance` hook handles `xrCreateInstance`.
+ API requests >= 1.1 become `XR_API_VERSION_1_0` (1.0.63 with these headers),
+ rather than carrying an arbitrary application's patch into the 1.0 request.
+ Requests below 1.1 are unchanged. The loader may reject future API versions
+ before any layer runs.
+- Enumerate downstream extensions through the **next layer's** GIPA before
+ creation. Only advertised promoted extensions are added, with duplicates
+ removed. All other application extensions remain, except the two stubs below.
+ Log added, stubbed and missing extensions with tag `FrameXrCompat`.
+- Missing `XR_KHR_android_thread_settings` is advertised and accepted;
+ `xrSetAndroidApplicationThreadKHR` logs and returns success without changing
+ thread scheduling. Missing `XR_OCULUS_android_session_state_enable` is
+ advertised and accepted, with no commands or additional session behavior.
+ If downstream supports either extension, preserve it and its real commands.
+ Stubbing these capabilities is intentionally limited to accepting the app's
+ request; it does not provide Meta services.
+- The manifest's `instance_extensions` makes both stubs visible during the
+ loader's pre-instance enumeration and validation. The layer also exposes
+ an enumeration implementation with count/capacity handling. Before a next
+ dispatch is available it can enumerate only its own extensions; normal app
+ global enumeration is performed by the loader, merging runtime and manifest
+ entries. See [loader_core.cpp L106–145](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_core.cpp#L106)
+ and [manifest parsing L549–558](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L549).
+- Unknown missing extensions are never silently removed. Khronos may reject
+ them **before** entering the layer and name them in `OpenXR-Loader` logs
+ ([loader_instance.cpp L149–176](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_instance.cpp#L149)).
+ If they reach this layer, it logs their names and preserves them for the
+ downstream failure.
+- `xrLocateSpaces` dispatches to `xrLocateSpacesKHR` per session/instance.
+ This is the only command added in 1.1. The three relevant structure types
+ (`SPACES_LOCATE_INFO`, `SPACE_LOCATIONS`, `SPACE_VELOCITIES`) and their KHR
+ versions are **numeric aliases**, and the structs are typedef aliases in
+ these headers. Explicit type conversion of local input/output copies is
+ therefore an identity conversion; the velocity chain passes through without
+ mutating its types or linkage. Output arrays remain shared and receive the
+ runtime's results, including on error; caller struct headers remain intact.
+- With palm pose enabled, rewrite both hand paths ending in
+ `/input/grip_surface/pose` to `/input/palm_ext/pose` in `xrStringToPath` and
+ in binding suggestions (including paths obtained before this layer rewrote
+ them). Unknown paths are left alone.
+- `xrGetInstanceProperties` and unrelated commands pass through. The real
+ runtime's identity is not spoofed. Dispatch tables and session ownership are
+ locked, and removed after successful destruction. Downstream calls occur
+ outside the lock.
+
+## Spec basis and interaction profiles
+
+The pinned [1.1 promotions appendix, versions.adoc L16–156](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/sources/chapters/versions.adoc#L16)
+includes a generated promotion list. Its source is the
+[1.1.63 XML registry](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/registry/xr.xml),
+selecting `extension[@promotedto='XR_VERSION_1_1']`. The full list implemented:
+
+| Promoted extension | Handling |
+| --- | --- |
+| `XR_KHR_locate_spaces` | Enable if advertised; core command alias |
+| `XR_EXT_local_floor` | Enable if advertised; reference-space enum is an alias |
+| `XR_EXT_uuid` | Enable if advertised; type alias, no commands |
+| `XR_EXT_palm_pose` | Enable if advertised; rewrite grip-surface paths |
+| `XR_VARJO_quad_views` | Enable if advertised; view-configuration enum alias, support remains optional |
+| `XR_EXT_samsung_odyssey_controller` | Enable if advertised |
+| `XR_EXT_hp_mixed_reality_controller` | Enable if advertised |
+| `XR_HTC_vive_cosmos_controller_interaction` | Enable if advertised |
+| `XR_HTC_vive_focus3_controller_interaction` | Enable if advertised |
+| `XR_ML_ml2_controller_interaction` | Enable if advertised |
+| `XR_FB_touch_controller_pro` | Enable if advertised; old profile still usable |
+| `XR_META_touch_controller_plus` | Enable if advertised; old profile still usable |
+| `XR_BD_controller_interaction` | Enable if advertised |
+| `XR_KHR_maintenance1` | Enable if advertised (included in the pinned registry's promotion list) |
+
+`XR_EXT_hand_interaction` is **not** promoted to 1.1. Preserve it if requested;
+it is not auto-enabled merely because the runtime advertises it. The parent
+provided its availability; the device notes list extensions non-exhaustively.
+
+The registry's `XR_VERSION_1_1` feature introduces 13 profile paths. For
+Samsung Odyssey, HP mixed reality, HTC Cosmos/Focus3, ML2 and the three
+ByteDance Pico profiles, drop a whole suggestion call with success when the
+corresponding extension is absent. Keep it when the extension is enabled.
+Drop the five new Meta paths: `touch_pro_controller`, `touch_plus_controller`,
+`touch_controller_rift_cv1`, `touch_controller_quest_1_rift_s`, and
+`touch_controller_quest_2`. The verified runtime notes do not list these;
+Pro/Plus promotion also renames several components, so the old extension's
+presence alone does not prove support for the new profile. This layer does
+not implement those component conversions. Existing old Pro/Plus profile
+paths are passed through if the app uses them.
+
+Preserve the documented `oculus/touch_controller`, `khr/simple_controller`,
+`valve/frame_controller` and original PC profile paths. The notes' "usual PC
+controllers" is not a complete enumerated list: availability of promoted PC
+profiles is inferred from the runtime's extension advertisement, not invented
+from that phrase. OpenXR has no general profile enumeration API. Dropping
+suggestions prevents an unsupported 1.1 profile from aborting initialization;
+it does not create bindings, so the app must also suggest a supported fallback.
+
+## Headers and licensing
+
+The required public headers (`openxr.h`, `openxr_platform.h`,
+`openxr_platform_defines.h`) are unmodified from
+[OpenXR-SDK release-1.1.63](https://github.com/KhronosGroup/OpenXR-SDK/tree/release-1.1.63/include/openxr),
+commit [f2448a8](https://github.com/KhronosGroup/OpenXR-SDK/commit/f2448a8797c85814aa892efc1ab8707900fbcc78).
+The requested filename `loader_interfaces.h` no longer exists in 1.1 SDK
+releases: negotiation was ratified and moved to `openxr_loader_negotiation.h`
+in 1.0.33 ([spec history L166–186](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/sources/chapters/versions.adoc#L166)).
+To retain that requested interface filename, the layer uses the unmodified,
+ABI-compatible [last legacy header from SDK-Source release-1.0.32](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.32/src/common/loader_interfaces.h).
+Only these four headers are vendored. They offer Apache-2.0 OR MIT; this
+vendoring uses Apache-2.0, reproduced in [vendor/LICENSE](vendor/LICENSE).
+
+## Android discovery and coexistence
+
+**Minimum asset-discovery loader release: 1.0.25 (2022-09-02).** Its
+[release commit, 15c3d8e](https://github.com/KhronosGroup/OpenXR-SDK-Source/commit/15c3d8eb99994e5365d6b6f96eefaf4c51a65a9d)
+explicitly announces APK-packaged API layers;
+[manifest_file.cpp L665–723](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.25/src/loader/manifest_file.cpp#L665)
+implements discovery, and [L934–940](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.25/src/loader/manifest_file.cpp#L934)
+adds asset manifests after filesystem manifests. This is not a 1.1-only feature.
+
+**For an app requesting API 1.1, use a 1.1 loader (first release 1.1.36) or
+newer.** A 1.0 loader rejects the request before the layer can downgrade it;
+see [loader_core.cpp L217–230](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_core.cpp#L217).
+The runtime can remain 1.0.
+
+Source inspection at release-1.1.63 confirms:
+
+- [manifest_file.cpp L720–783](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L720)
+ uses the initialized Android asset manager and scans
+ `openxr/1/api_layers/implicit.d/` for JSON. The application must initialize
+ the loader with its Android context (`xrInitializeLoaderKHR`). In the APK,
+ the entry is `assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json`.
+- A bare `library_path`, as used here, is **not explicitly concatenated** with
+ `nativeLibraryDir`. [L878–900](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L878)
+ leaves a bare name for normal dynamic-linker search in the application's
+ namespace (including its native library directory). Relative paths with a
+ slash use [LocateLibraryInAssets L943–952](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L943),
+ which resolves against `GetAndroidNativeLibraryDir()`.
+ [loader_init_data.cpp L87–96](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_init_data.cpp#L87)
+ obtains the asset manager and `ApplicationInfo.nativeLibraryDir` via JNI.
+ Wolvic's supplied manifest has `android:extractNativeLibs="true"`.
+- [android_utilities.cpp L267–322](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/android_utilities.cpp#L267)
+ handles runtime broker discovery, not APK layer discovery. Its
+ `native_lib_dir + so_filename` and `dlopen` refer to the runtime package.
+ They must not be confused with the app's layer library resolution.
+- [manifest_file.cpp L1008–1023](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L1008)
+ retains filesystem manifests and then appends asset manifests. This leaves
+ `/vendor`'s `XR_APILAYER_VALVE_fdm_injection` discoverable. This layer advances
+ `nextInfo` exactly once, uses next-layer GIPA, preserves the rest of the
+ create-info chain and never opens `vrclient.so` directly. No environment
+ layer-list override or assumed ordering is needed.
+
+`DISABLE_FRAME_XR_COMPAT` is the manifest's disable environment variable.
+Leave it unset to activate the implicit layer. No `enable_environment` is
+required.
+
+## Rebuild and verify
+
+NDK **r30 / 30.0.16248370**, installed at
+`~/Library/Android/ndk/30.0.16248370`, was the latest stable/LTS package shown
+by the [Android download page](https://developer.android.com/ndk/downloads)
+on 2026-09-28. Downloaded
+[android-ndk-r30-darwin.dmg](https://dl.google.com/android/repository/android-ndk-r30-darwin.dmg)
+(1,072,970,961 bytes). The page publishes SHA-1, not SHA-256:
+
+```
+expected: 48591224b6657f46eebbc9d95d4af09bbed8d107
+actual: 48591224b6657f46eebbc9d95d4af09bbed8d107 (PASS)
+```
+
+Mounted read-only with `hdiutil`; copied
+`AndroidNDK16248370.app/Contents/NDK` into that version directory. Despite the
+`darwin-x86_64` toolchain directory name, clang is universal arm64/x86_64 and
+ran on this Apple Silicon Mac.
+
+```sh
+frame/openxr-compat/build.sh
+# Or set ANDROID_NDK_HOME to an installed NDK.
+cmake -S frame/openxr-compat -B frame/openxr-compat/build-host -G Ninja
+cmake --build frame/openxr-compat/build-host
+ctest --test-dir frame/openxr-compat/build-host --output-on-failure
+```
+
+The script produces arm64-v8a / android-24, C++17, `-O2`, static libc++, hidden
+internal symbols, stripped output, and `-Wl,-z,max-page-size=16384`.
+The committed prebuilt is `prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so`.
+Its SHA-256 is recorded below with the APK checks.
+
+[Host test output](evidence/ctest.txt): two tests passed, covering pure logic
+and the actual layer with a fake next layer and host-only log/JNI shims.
+[Full llvm-readelf -d -s -l output](evidence/readelf.txt) records three LOAD
+segments aligned `0x4000`, only `xrNegotiateLoaderApiLayerInterface` exported,
+and only `libc.so`, `libm.so`, `libdl.so`, `liblog.so` as NEEDED libraries.
+All 46 undefined imports are versioned `@LIBC` except `__android_log_print`.
+There is no `libc++_shared.so` dependency and no unstripped `.symtab`.
+
+No independent model review was run: the task explicitly prohibits delegation.
+No `CODING_STANDARDS.md` exists in this worktree or the primary worktree.
+
+## Wolvic injection artifact (Mac only)
+
+Copied `/tmp/vrapk/wq-dec` to `/tmp/vrapk/wq-layer-dec`; retained its existing
+LAUNCHER fix and native-library extraction setting. Added only:
+
+```
+assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json
+lib/arm64-v8a/libXrApiLayer_FRAME_compat.so
+```
+
+Built using `/tmp/vrapk/jdk/Contents/Home/bin/java` and
+`/tmp/vrapk/apktool_3.0.3.jar`, then signed in place using
+`/tmp/vrapk/uber-apk-signer-1.3.0.jar --overwrite` (embedded debug certificate):
+
+```sh
+/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/apktool_3.0.3.jar \
+ b /tmp/vrapk/wq-layer-dec -o /tmp/vrapk/wolvic-quest-compat.apk
+/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar \
+ -a /tmp/vrapk/wolvic-quest-compat.apk --overwrite
+/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar \
+ -a /tmp/vrapk/wolvic-quest-compat.apk --onlyVerify
+```
+
+Final APK: `/tmp/vrapk/wolvic-quest-compat.apk` (not committed).
+The bundled `lib/arm64-v8a/libopenxr_loader.so` is byte-for-byte unchanged.
+Its strings include both `openxr/1/api_layers/implicit.d/` and
+`openxr/1/api_layers/explicit.d/`, `AddManifestFilesAndroid` error messages,
+and `xrLocateSpaces`. Thus it contains APK asset discovery and evidence of
+1.1 command support. Its exact upstream release number was not established
+from the binary; runtime execution of those paths remains unverified.
+
+Prebuilt library SHA-256:
+
+```
+479d31c374f137906e03f73209b581d65d7e6a41b8476e6425fa55a6aa40bd68
+```
+
+APK SHA-256:
+
+```
+355a681625e38b71563dcffecb031799eff27894d6ab910a659bace057e82c1f
+```
+
+Final `--onlyVerify` exited 0: zip alignment verified, v2/v3 signatures
+verified, one APK processed and zero errors. ZIP readback confirmed that the
+injected library and manifest match the committed inputs, the loader is
+unchanged, and the binary Android manifest retains the LAUNCHER category.
+See [APK verification evidence](evidence/apk-verification.txt).
diff --git a/frame/openxr-compat/XrApiLayer_FRAME_compat.json b/frame/openxr-compat/XrApiLayer_FRAME_compat.json
new file mode 100644
index 0000000..143d8e3
--- /dev/null
+++ b/frame/openxr-compat/XrApiLayer_FRAME_compat.json
@@ -0,0 +1,21 @@
+{
+ "file_format_version": "1.0.0",
+ "api_layer": {
+ "name": "XR_APILAYER_FRAME_compat",
+ "library_path": "libXrApiLayer_FRAME_compat.so",
+ "api_version": "1.1",
+ "implementation_version": "1",
+ "description": "Steam Frame OpenXR 1.1 to 1.0 compatibility",
+ "disable_environment": "DISABLE_FRAME_XR_COMPAT",
+ "instance_extensions": [
+ {
+ "name": "XR_KHR_android_thread_settings",
+ "extension_version": "6"
+ },
+ {
+ "name": "XR_OCULUS_android_session_state_enable",
+ "extension_version": "1"
+ }
+ ]
+ }
+}
diff --git a/frame/openxr-compat/build.sh b/frame/openxr-compat/build.sh
new file mode 100755
index 0000000..3b08089
--- /dev/null
+++ b/frame/openxr-compat/build.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -eu
+here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
+ndk=${ANDROID_NDK_HOME:-"$HOME/Library/Android/ndk/30.0.16248370"}
+cmake -S "$here" -B "$here/build-android" -G Ninja \
+ -DCMAKE_TOOLCHAIN_FILE="$ndk/build/cmake/android.toolchain.cmake" \
+ -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 \
+ -DANDROID_STL=c++_static -DCMAKE_BUILD_TYPE=Release
+cmake --build "$here/build-android"
+mkdir -p "$here/prebuilt/arm64-v8a"
+cp "$here/build-android/libXrApiLayer_FRAME_compat.so" "$here/prebuilt/arm64-v8a/"
+shasum -a 256 "$here/prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so"
diff --git a/frame/openxr-compat/compat_logic.h b/frame/openxr-compat/compat_logic.h
new file mode 100644
index 0000000..9d94fbc
--- /dev/null
+++ b/frame/openxr-compat/compat_logic.h
@@ -0,0 +1,88 @@
+#pragma once
+#include
+#include
+#include
+#include
+
+namespace frame {
+using Names = std::vector;
+inline constexpr const char* layerName = "XR_APILAYER_FRAME_compat";
+inline constexpr const char* stubs[] = {"XR_KHR_android_thread_settings", "XR_OCULUS_android_session_state_enable"};
+// OpenXR-Docs release-1.1.63 registry: promotedto="XR_VERSION_1_1".
+inline constexpr const char* promotions[] = {
+ "XR_KHR_locate_spaces", "XR_EXT_local_floor", "XR_EXT_uuid", "XR_EXT_palm_pose",
+ "XR_VARJO_quad_views", "XR_EXT_samsung_odyssey_controller", "XR_EXT_hp_mixed_reality_controller",
+ "XR_HTC_vive_cosmos_controller_interaction", "XR_HTC_vive_focus3_controller_interaction",
+ "XR_ML_ml2_controller_interaction", "XR_FB_touch_controller_pro", "XR_META_touch_controller_plus",
+ "XR_BD_controller_interaction", "XR_KHR_maintenance1"};
+inline bool has(const Names& names, const std::string& n) {
+ return std::find(names.begin(), names.end(), n) != names.end();
+}
+inline bool downgrade(XrVersion v) { return v >= XR_MAKE_VERSION(1, 1, 0); }
+inline XrVersion runtimeVersion(XrVersion v) { return downgrade(v) ? XR_API_VERSION_1_0 : v; }
+struct ExtensionPlan { Names downstream, stubbed, missing, added; };
+inline ExtensionPlan extensions(const Names& requested, const Names& available, bool promote) {
+ ExtensionPlan p;
+ for (const auto& n : requested) {
+ if (!has(available, n) && (n == stubs[0] || n == stubs[1])) p.stubbed.push_back(n);
+ else {
+ if (!has(p.downstream, n)) p.downstream.push_back(n);
+ if (!has(available, n)) p.missing.push_back(n);
+ }
+ }
+ if (promote) for (auto n : promotions) if (has(available, n) && !has(p.downstream, n)) {
+ p.downstream.push_back(n); p.added.push_back(n);
+ }
+ return p;
+}
+// 1.1's grip_surface is XR_EXT_palm_pose's palm_ext renamed (same pose), not the grip pose.
+inline std::string rewritePath(std::string p, bool palm) {
+ const std::string suffix = "/input/grip_surface/pose";
+ if (palm && (p == "/user/hand/left" + suffix || p == "/user/hand/right" + suffix))
+ p.replace(p.size() - suffix.size(), suffix.size(), "/input/palm_ext/pose");
+ return p;
+}
+// Profiles added to 1.1. The three legacy Meta splits have no 1.0 extension.
+// The renamed Pro/Plus profiles have different component semantics; do not
+// claim the old extension implements the new profile merely because it exists.
+inline bool dropProfile(const std::string& p, const Names& enabled) {
+ for (auto name : {"touch_pro_controller", "touch_plus_controller", "touch_controller_rift_cv1",
+ "touch_controller_quest_1_rift_s", "touch_controller_quest_2"})
+ if (p == std::string("/interaction_profiles/meta/") + name) return true;
+ const char* profiles[][2] = {
+ {"samsung/odyssey_controller", "XR_EXT_samsung_odyssey_controller"},
+ {"hp/mixed_reality_controller", "XR_EXT_hp_mixed_reality_controller"},
+ {"htc/vive_cosmos_controller", "XR_HTC_vive_cosmos_controller_interaction"},
+ {"htc/vive_focus3_controller", "XR_HTC_vive_focus3_controller_interaction"},
+ {"ml/ml2_controller", "XR_ML_ml2_controller_interaction"},
+ {"bytedance/pico_neo3_controller", "XR_BD_controller_interaction"},
+ {"bytedance/pico4_controller", "XR_BD_controller_interaction"},
+ {"bytedance/pico_g3_controller", "XR_BD_controller_interaction"}};
+ for (auto& entry : profiles) if (p == std::string("/interaction_profiles/") + entry[0])
+ return !has(enabled, entry[1]);
+ return false;
+}
+inline XrStructureType extensionType(XrStructureType t) {
+ switch (t) {
+ case XR_TYPE_SPACES_LOCATE_INFO: return XR_TYPE_SPACES_LOCATE_INFO_KHR;
+ case XR_TYPE_SPACE_LOCATIONS: return XR_TYPE_SPACE_LOCATIONS_KHR;
+ case XR_TYPE_SPACE_VELOCITIES: return XR_TYPE_SPACE_VELOCITIES_KHR;
+ default: return t;
+ }
+}
+inline XrResult locate(PFN_xrLocateSpacesKHR next, XrSession session,
+ const XrSpacesLocateInfo* info, XrSpaceLocations* locations) {
+ if (!info || !locations) return XR_ERROR_VALIDATION_FAILURE;
+ static_assert(XR_TYPE_SPACE_VELOCITIES == XR_TYPE_SPACE_VELOCITIES_KHR);
+ static_assert(XR_TYPE_SPACE_LOCATIONS == XR_TYPE_SPACE_LOCATIONS_KHR);
+ static_assert(XR_TYPE_SPACES_LOCATE_INFO == XR_TYPE_SPACES_LOCATE_INFO_KHR);
+ auto in = *info;
+ auto out = *locations;
+ in.type = extensionType(in.type); out.type = extensionType(out.type);
+ // The enums and typedefs are exact aliases. All chained velocities,
+ // including unknown next structures, can be forwarded without mutation.
+ const auto result = next(session, &in, &out);
+ locations->locationCount = out.locationCount;
+ return result;
+}
+}
diff --git a/frame/openxr-compat/evidence/apk-verification.txt b/frame/openxr-compat/evidence/apk-verification.txt
new file mode 100644
index 0000000..bfed1f5
--- /dev/null
+++ b/frame/openxr-compat/evidence/apk-verification.txt
@@ -0,0 +1,29 @@
+2026-09-28, Mac only; no device connection.
+APK: /tmp/vrapk/wolvic-quest-compat.apk
+SHA-256: 355a681625e38b71563dcffecb031799eff27894d6ab910a659bace057e82c1f
+
+Command:
+/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar -a /tmp/vrapk/wolvic-quest-compat.apk --onlyVerify
+Exit status: 0
+Relevant output:
+- zipalign verified
+- signature verified [v2, v3]
+Successfully processed 1 APKs and 0 errors in 0.63 seconds.
+
+ZIP readback checks: PASS
+- Injected JSON identical to frame/openxr-compat/XrApiLayer_FRAME_compat.json.
+- Injected SO identical to prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so.
+- Existing libopenxr_loader.so identical to /tmp/vrapk/wq-dec version.
+- Binary AndroidManifest.xml contains android.intent.category.LAUNCHER.
+
+Loader strings present:
+openxr/1/api_layers/implicit.d/
+openxr/1/api_layers/explicit.d/
+ApiLayerManifestFile::AddManifestFilesAndroid unable to open asset
+xrLocateSpaces
+
+NDK: 30.0.16248370 (r30)
+DMG size: 1072970961 bytes
+Download-page SHA-1: 48591224b6657f46eebbc9d95d4af09bbed8d107
+Measured SHA-1: 48591224b6657f46eebbc9d95d4af09bbed8d107
+SHA check: PASS
diff --git a/frame/openxr-compat/evidence/ctest.txt b/frame/openxr-compat/evidence/ctest.txt
new file mode 100644
index 0000000..3e3686d
--- /dev/null
+++ b/frame/openxr-compat/evidence/ctest.txt
@@ -0,0 +1,9 @@
+Test project /Users/saphid/projects/steam-frame-xrlayer/frame/openxr-compat/build-host
+ Start 1: compat_logic
+1/2 Test #1: compat_logic ..................... Passed 0.01 sec
+ Start 2: layer_dispatch
+2/2 Test #2: layer_dispatch ................... Passed 0.42 sec
+
+100% tests passed out of 2
+
+Total Test time (real) = 0.43 sec
diff --git a/frame/openxr-compat/evidence/readelf.txt b/frame/openxr-compat/evidence/readelf.txt
new file mode 100644
index 0000000..67a0dbe
--- /dev/null
+++ b/frame/openxr-compat/evidence/readelf.txt
@@ -0,0 +1,110 @@
+
+Elf file type is DYN (Shared object file)
+Entry point 0x0
+There are 9 program headers, starting at offset 64
+
+Program Headers:
+ Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+ PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0001f8 0x0001f8 R 0x8
+ LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0169e0 0x0169e0 R E 0x4000
+ LOAD 0x0169e0 0x000000000001a9e0 0x000000000001a9e0 0x000a90 0x001620 RW 0x4000
+ LOAD 0x017470 0x000000000001f470 0x000000000001f470 0x000068 0x000bc0 RW 0x4000
+ DYNAMIC 0x017048 0x000000000001b048 0x000000000001b048 0x0001c0 0x0001c0 RW 0x8
+ GNU_RELRO 0x0169e0 0x000000000001a9e0 0x000000000001a9e0 0x000a90 0x001620 R 0x1
+ GNU_EH_FRAME 0x004fb4 0x0000000000004fb4 0x0000000000004fb4 0x00070c 0x00070c R 0x4
+ GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0
+ NOTE 0x000238 0x0000000000000238 0x0000000000000238 0x0000bc 0x0000bc R 0x4
+
+ Section to Segment mapping:
+ Segment Sections...
+ 00
+ 01 .note.android.ident .note.gnu.build-id .dynsym .gnu.version .gnu.version_r .gnu.hash .dynstr .rela.dyn .rela.plt .gcc_except_table .rodata .eh_frame_hdr .eh_frame .text __lcxx_override .plt
+ 02 .data.rel.ro .fini_array .init_array .dynamic .got .got.plt .relro_padding
+ 03 .data .bss
+ 04 .dynamic
+ 05 .data.rel.ro .fini_array .init_array .dynamic .got .got.plt .relro_padding
+ 06 .eh_frame_hdr
+ 07
+ 08 .note.android.ident .note.gnu.build-id
+ None .comment .shstrtab
+Dynamic section at offset 0x17048 contains 28 entries:
+ Tag Type Name/Value
+ 0x0000000000000001 (NEEDED) Shared library: [liblog.so]
+ 0x0000000000000001 (NEEDED) Shared library: [libm.so]
+ 0x0000000000000001 (NEEDED) Shared library: [libdl.so]
+ 0x0000000000000001 (NEEDED) Shared library: [libc.so]
+ 0x000000000000000e (SONAME) Library soname: [libXrApiLayer_FRAME_compat.so]
+ 0x000000000000001e (FLAGS) BIND_NOW
+ 0x000000006ffffffb (FLAGS_1) NOW
+ 0x0000000000000007 (RELA) 0xae8
+ 0x0000000000000008 (RELASZ) 5424 (bytes)
+ 0x0000000000000009 (RELAENT) 24 (bytes)
+ 0x000000006ffffff9 (RELACOUNT) 225
+ 0x0000000000000017 (JMPREL) 0x2018
+ 0x0000000000000002 (PLTRELSZ) 1080 (bytes)
+ 0x0000000000000003 (PLTGOT) 0x1b2f0
+ 0x0000000000000014 (PLTREL) RELA
+ 0x0000000000000006 (SYMTAB) 0x2f8
+ 0x000000000000000b (SYMENT) 24 (bytes)
+ 0x0000000000000005 (STRTAB) 0x838
+ 0x000000000000000a (STRSZ) 681 (bytes)
+ 0x000000006ffffef5 (GNU_HASH) 0x818
+ 0x0000000000000019 (INIT_ARRAY) 0x1b030
+ 0x000000000000001b (INIT_ARRAYSZ) 24 (bytes)
+ 0x000000000000001a (FINI_ARRAY) 0x1b020
+ 0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
+ 0x000000006ffffff0 (VERSYM) 0x778
+ 0x000000006ffffffe (VERNEED) 0x7d8
+ 0x000000006fffffff (VERNEEDNUM) 2
+ 0x0000000000000000 (NULL) 0x0
+
+Symbol table '.dynsym' contains 48 entries:
+ Num: Value Size Type Bind Vis Ndx Name
+ 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
+ 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC
+ 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC
+ 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcmp@LIBC
+ 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __android_log_print
+ 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@LIBC
+ 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memmove@LIBC
+ 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@LIBC
+ 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@LIBC
+ 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __strcpy_chk@LIBC
+ 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@LIBC
+ 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcmp@LIBC
+ 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_lock@LIBC
+ 13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_unlock@LIBC
+ 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno@LIBC
+ 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND snprintf@LIBC
+ 16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_destroy@LIBC
+ 17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strerror_r@LIBC
+ 18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort@LIBC
+ 19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_getspecific@LIBC
+ 20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_setspecific@LIBC
+ 21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_create@LIBC
+ 22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND syscall@LIBC
+ 23: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND __sF@LIBC
+ 24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fwrite@LIBC
+ 25: 0000000000000000 0 FUNC GLOBAL DEFAULT UND vfprintf@LIBC
+ 26: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputc@LIBC
+ 27: 0000000000000000 0 FUNC GLOBAL DEFAULT UND vasprintf@LIBC
+ 28: 0000000000000000 0 FUNC GLOBAL DEFAULT UND android_set_abort_message@LIBC
+ 29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND openlog@LIBC
+ 30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND syslog@LIBC
+ 31: 0000000000000000 0 FUNC GLOBAL DEFAULT UND closelog@LIBC
+ 32: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc@LIBC
+ 33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@LIBC
+ 34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND posix_memalign@LIBC
+ 35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND realloc@LIBC
+ 36: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_once@LIBC
+ 37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_delete@LIBC
+ 38: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getauxval@LIBC
+ 39: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __system_property_get@LIBC
+ 40: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strncmp@LIBC
+ 41: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fprintf@LIBC
+ 42: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fflush@LIBC
+ 43: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_wrlock@LIBC
+ 44: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_unlock@LIBC
+ 45: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dl_iterate_phdr@LIBC
+ 46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_rdlock@LIBC
+ 47: 00000000000081a0 284 FUNC GLOBAL DEFAULT 14 xrNegotiateLoaderApiLayerInterface
diff --git a/frame/openxr-compat/layer.cpp b/frame/openxr-compat/layer.cpp
new file mode 100644
index 0000000..79b858c
--- /dev/null
+++ b/frame/openxr-compat/layer.cpp
@@ -0,0 +1,289 @@
+#include "compat_logic.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "FrameXrCompat", __VA_ARGS__)
+#define GUARD_END catch (const std::bad_alloc&) { return XR_ERROR_OUT_OF_MEMORY; } catch (...) { return XR_ERROR_RUNTIME_FAILURE; }
+namespace {
+struct Dispatch {
+ PFN_xrGetInstanceProcAddr gipa{};
+ PFN_xrDestroyInstance destroy{};
+ PFN_xrCreateSession createSession{};
+ PFN_xrDestroySession destroySession{};
+ PFN_xrLocateSpacesKHR locate{};
+ PFN_xrStringToPath stringToPath{};
+ PFN_xrPathToString pathToString{};
+ PFN_xrSuggestInteractionProfileBindings suggest{};
+ PFN_xrSetAndroidApplicationThreadKHR thread{};
+ PFN_xrRequestDisplayRefreshRateFB refresh{};
+ frame::Names enabled, stubbed;
+ XrInstance instance{};
+ bool promote{}, palm{};
+};
+std::mutex mutex;
+std::unordered_map> instances;
+std::unordered_map> sessions;
+// No runtime library is opened here: every call goes through the next layer.
+PFN_xrEnumerateInstanceExtensionProperties enumerateNext{};
+template T proc(PFN_xrGetInstanceProcAddr gipa, XrInstance i, const char* name) {
+ PFN_xrVoidFunction f{};
+ if (XR_FAILED(gipa(i, name, &f))) return nullptr;
+ return reinterpret_cast(f);
+}
+std::shared_ptr get(XrInstance i) {
+ std::lock_guard lock(mutex);
+ auto it = instances.find(i); return it == instances.end() ? nullptr : it->second;
+}
+std::shared_ptr get(XrSession s) {
+ std::lock_guard lock(mutex);
+ auto it = sessions.find(s); return it == sessions.end() ? nullptr : it->second;
+}
+XrResult properties(PFN_xrEnumerateInstanceExtensionProperties fn,
+ std::vector& out) {
+ if (!fn) return XR_ERROR_FUNCTION_UNSUPPORTED;
+ for (int attempt = 0; attempt < 4; ++attempt) {
+ uint32_t n{};
+ auto r = fn(nullptr, 0, &n, nullptr);
+ if (XR_FAILED(r)) return r;
+ out.assign(n, {XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0});
+ if (!n) return XR_SUCCESS;
+ r = fn(nullptr, n, &n, out.data());
+ if (r == XR_ERROR_SIZE_INSUFFICIENT) continue;
+ if (XR_FAILED(r)) return r;
+ out.resize(n); return XR_SUCCESS;
+ }
+ return XR_ERROR_RUNTIME_FAILURE;
+}
+XrResult XRAPI_CALL enumerate(const char* layer, uint32_t capacity, uint32_t* count,
+ XrExtensionProperties* output) try {
+ if (!count || (capacity && !output)) return XR_ERROR_VALIDATION_FAILURE;
+ const bool own = layer && std::strcmp(layer, frame::layerName) == 0;
+ PFN_xrEnumerateInstanceExtensionProperties next;
+ { std::lock_guard lock(mutex); next = enumerateNext; }
+ if (layer && *layer && !own) return next ? next(layer, capacity, count, output) : XR_ERROR_API_LAYER_NOT_PRESENT;
+ std::vector all;
+ if (!own && next) {
+ auto r = properties(next, all); if (XR_FAILED(r)) return r;
+ }
+ for (unsigned i = 0; i < 2; ++i) {
+ bool found = false;
+ for (auto& e : all) if (!std::strcmp(e.extensionName, frame::stubs[i])) found = true;
+ if (!found) {
+ XrExtensionProperties p{XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, i == 0 ? XR_KHR_android_thread_settings_SPEC_VERSION : 1u};
+ std::strcpy(p.extensionName, frame::stubs[i]); all.push_back(p);
+ }
+ }
+ *count = static_cast(all.size());
+ LOG("enumerate extensions: %u (includes stubs)", *count);
+ if (!capacity) return XR_SUCCESS;
+ if (capacity < all.size()) return XR_ERROR_SIZE_INSUFFICIENT;
+ for (size_t i = 0; i < all.size(); ++i) {
+ if (output[i].type != XR_TYPE_EXTENSION_PROPERTIES) return XR_ERROR_VALIDATION_FAILURE;
+ std::strcpy(output[i].extensionName, all[i].extensionName);
+ output[i].extensionVersion = all[i].extensionVersion;
+ }
+ return XR_SUCCESS;
+} GUARD_END
+XrResult XRAPI_CALL destroyInstance(XrInstance i) try {
+ auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
+ auto r = d->destroy(i);
+ if (XR_SUCCEEDED(r)) {
+ std::lock_guard lock(mutex);
+ for (auto it = sessions.begin(); it != sessions.end();) {
+ if (it->second == d) it = sessions.erase(it); else ++it;
+ }
+ instances.erase(i);
+ if (instances.empty()) enumerateNext = nullptr;
+ }
+ return r;
+} GUARD_END
+XrResult XRAPI_CALL createSession(XrInstance i, const XrSessionCreateInfo* info, XrSession* s) try {
+ auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
+ auto r = d->createSession(i, info, s);
+ if (XR_SUCCEEDED(r)) {
+ try { std::lock_guard lock(mutex); sessions.emplace(*s, d); }
+ catch (...) { d->destroySession(*s); *s = XR_NULL_HANDLE; throw; }
+ }
+ return r;
+} GUARD_END
+XrResult XRAPI_CALL destroySession(XrSession s) try {
+ auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
+ auto r = d->destroySession(s);
+ if (XR_SUCCEEDED(r)) { std::lock_guard lock(mutex); sessions.erase(s); }
+ return r;
+} GUARD_END
+XrResult XRAPI_CALL locateSpaces(XrSession s, const XrSpacesLocateInfo* in, XrSpaceLocations* out) try {
+ auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
+ if (!d->locate) return XR_ERROR_FUNCTION_UNSUPPORTED;
+ static std::once_flag logged; // called every frame
+ std::call_once(logged, [] { LOG("xrLocateSpaces -> xrLocateSpacesKHR (core/KHR type aliases)"); });
+ return frame::locate(d->locate, s, in, out);
+} GUARD_END
+XrResult XRAPI_CALL threadSettings(XrSession s, XrAndroidThreadTypeKHR type, uint32_t tid) try {
+ auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
+ if (frame::has(d->stubbed, frame::stubs[0])) {
+ LOG("stub xrSetAndroidApplicationThreadKHR type=%d tid=%u -> XR_SUCCESS (no scheduling change)", type, tid);
+ return XR_SUCCESS;
+ }
+ return d->thread ? d->thread(s, type, tid) : XR_ERROR_FUNCTION_UNSUPPORTED;
+} GUARD_END
+// SteamVR offers only the current refresh rate; Quest apps ask for 72/90/120 Hz
+// and abort on the error, so keep the current rate and report success.
+XrResult XRAPI_CALL requestRefreshRate(XrSession s, float hz) try {
+ auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
+ auto r = d->refresh(s, hz);
+ if (r == XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB) {
+ LOG("xrRequestDisplayRefreshRateFB %.1f Hz unsupported; keeping the current rate", hz);
+ return XR_SUCCESS;
+ }
+ return r;
+} GUARD_END
+XrResult XRAPI_CALL stringToPath(XrInstance i, const char* path, XrPath* out) try {
+ auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
+ if (!path) return XR_ERROR_VALIDATION_FAILURE;
+ auto rewritten = frame::rewritePath(path, d->palm);
+ if (rewritten != path) LOG("path %s -> %s", path, rewritten.c_str());
+ return d->stringToPath(i, rewritten.c_str(), out);
+} GUARD_END
+XrResult pathString(const Dispatch& d, XrPath path, std::string& out) {
+ uint32_t n{};
+ auto r = d.pathToString(d.instance, path, 0, &n, nullptr);
+ if (XR_FAILED(r)) return r;
+ std::vector text(n);
+ r = d.pathToString(d.instance, path, n, &n, text.data());
+ if (XR_SUCCEEDED(r)) out.assign(text.data());
+ return r;
+}
+XrResult XRAPI_CALL suggest(XrInstance i, const XrInteractionProfileSuggestedBinding* info) try {
+ auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
+ if (!info || (info->countSuggestedBindings && !info->suggestedBindings)) return XR_ERROR_VALIDATION_FAILURE;
+ std::string profile;
+ auto r = pathString(*d, info->interactionProfile, profile);
+ if (XR_FAILED(r)) return r;
+ if (d->promote && frame::dropProfile(profile, d->enabled)) {
+ LOG("drop unsupported 1.1 interaction profile %s (%u suggestions)", profile.c_str(), info->countSuggestedBindings);
+ return XR_SUCCESS;
+ }
+ auto copy = *info;
+ std::vector bindings;
+ for (uint32_t index = 0; index < info->countSuggestedBindings; ++index) {
+ auto b = info->suggestedBindings[index];
+ std::string path;
+ r = pathString(*d, b.binding, path); if (XR_FAILED(r)) return r;
+ auto rewritten = frame::rewritePath(path, d->palm);
+ if (rewritten != path) {
+ LOG("binding %s -> %s", path.c_str(), rewritten.c_str());
+ r = d->stringToPath(i, rewritten.c_str(), &b.binding); if (XR_FAILED(r)) return r;
+ }
+ bindings.push_back(b);
+ }
+ copy.suggestedBindings = bindings.data();
+ return d->suggest(i, ©);
+} GUARD_END
+XrResult XRAPI_CALL gipa(XrInstance, const char*, PFN_xrVoidFunction*);
+XrResult XRAPI_CALL createLayer(const XrInstanceCreateInfo* info, const XrApiLayerCreateInfo* layer,
+ XrInstance* instance) try {
+ if (!info || !instance || !layer || layer->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO ||
+ layer->structVersion != XR_API_LAYER_CREATE_INFO_STRUCT_VERSION || layer->structSize != sizeof(*layer) ||
+ !layer->nextInfo || layer->nextInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO ||
+ layer->nextInfo->structVersion != XR_API_LAYER_NEXT_INFO_STRUCT_VERSION ||
+ layer->nextInfo->structSize != sizeof(XrApiLayerNextInfo) ||
+ !layer->nextInfo->nextGetInstanceProcAddr || !layer->nextInfo->nextCreateApiLayerInstance ||
+ (info->enabledExtensionCount && !info->enabledExtensionNames)) return XR_ERROR_INITIALIZATION_FAILED;
+ *instance = XR_NULL_HANDLE;
+ auto next = layer->nextInfo->nextGetInstanceProcAddr;
+ auto enumFn = proc(next, XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties");
+ std::vector available;
+ auto r = properties(enumFn, available); if (XR_FAILED(r)) { LOG("runtime extension enumeration failed: %d", r); return r; }
+ frame::Names requested, supported;
+ for (auto& p : available) supported.emplace_back(p.extensionName);
+ for (uint32_t n = 0; n < info->enabledExtensionCount; ++n) {
+ if (!info->enabledExtensionNames[n]) return XR_ERROR_VALIDATION_FAILURE;
+ requested.emplace_back(info->enabledExtensionNames[n]);
+ }
+ auto d = std::make_shared();
+ d->promote = frame::downgrade(info->applicationInfo.apiVersion);
+ auto plan = frame::extensions(requested, supported, d->promote);
+ for (auto& n : plan.added) LOG("enable promoted extension %s", n.c_str());
+ for (auto& n : plan.stubbed) LOG("emulate missing extension %s; remove downstream", n.c_str());
+ for (auto& n : plan.missing) LOG("unsupported extension %s retained; runtime must reject", n.c_str());
+ auto copy = *info;
+ copy.applicationInfo.apiVersion = frame::runtimeVersion(info->applicationInfo.apiVersion);
+ if (d->promote) LOG("API %u.%u.%u -> 1.0.%u", unsigned(XR_VERSION_MAJOR(info->applicationInfo.apiVersion)),
+ unsigned(XR_VERSION_MINOR(info->applicationInfo.apiVersion)), unsigned(XR_VERSION_PATCH(info->applicationInfo.apiVersion)),
+ unsigned(XR_VERSION_PATCH(copy.applicationInfo.apiVersion)));
+ std::vector names;
+ for (auto& n : plan.downstream) names.push_back(n.c_str());
+ copy.enabledExtensionCount = static_cast(names.size()); copy.enabledExtensionNames = names.data();
+ auto chain = *layer; chain.nextInfo = layer->nextInfo->next;
+ r = layer->nextInfo->nextCreateApiLayerInstance(©, &chain, instance);
+ if (XR_FAILED(r)) { LOG("downstream xrCreateInstance -> %d", r); return r; }
+ d->gipa = next; d->instance = *instance;
+ d->destroy = proc(next, *instance, "xrDestroyInstance");
+ // A successful runtime instance must expose xrDestroyInstance.
+ if (!d->destroy) { // nothing could ever destroy it, so don't hand it out
+ LOG("invalid downstream: missing xrDestroyInstance"); *instance = XR_NULL_HANDLE; return XR_ERROR_INITIALIZATION_FAILED;
+ }
+ try {
+ d->enabled = std::move(plan.downstream); d->stubbed = std::move(plan.stubbed);
+ d->palm = frame::has(d->enabled, "XR_EXT_palm_pose");
+#define LOAD(field, name) d->field = proc(next, *instance, #name)
+ LOAD(createSession, xrCreateSession); LOAD(destroySession, xrDestroySession);
+ LOAD(stringToPath, xrStringToPath); LOAD(pathToString, xrPathToString);
+ LOAD(suggest, xrSuggestInteractionProfileBindings);
+ if (frame::has(d->enabled, "XR_KHR_locate_spaces")) LOAD(locate, xrLocateSpacesKHR);
+ if (frame::has(d->enabled, frame::stubs[0])) LOAD(thread, xrSetAndroidApplicationThreadKHR);
+ if (frame::has(d->enabled, "XR_FB_display_refresh_rate")) LOAD(refresh, xrRequestDisplayRefreshRateFB);
+#undef LOAD
+ if (!d->createSession || !d->destroySession || !d->stringToPath || !d->pathToString || !d->suggest) {
+ d->destroy(*instance); *instance = XR_NULL_HANDLE; return XR_ERROR_INITIALIZATION_FAILED;
+ }
+ std::lock_guard lock(mutex);
+ instances.emplace(*instance, d); enumerateNext = enumFn;
+ } catch (...) { d->destroy(*instance); *instance = XR_NULL_HANDLE; throw; }
+ LOG("created instance; next-layer dispatch retained (Valve layer coexists)");
+ return r;
+} GUARD_END
+XrResult XRAPI_CALL gipa(XrInstance i, const char* name, PFN_xrVoidFunction* out) try {
+ if (!name || !out) return XR_ERROR_VALIDATION_FAILURE;
+ *out = nullptr;
+#define RETURN_PROC(n, fn) if (!std::strcmp(name, n)) { *out = reinterpret_cast(fn); return XR_SUCCESS; }
+ RETURN_PROC("xrGetInstanceProcAddr", gipa)
+ RETURN_PROC("xrCreateApiLayerInstance", createLayer)
+ RETURN_PROC("xrEnumerateInstanceExtensionProperties", enumerate)
+ auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
+ RETURN_PROC("xrDestroyInstance", destroyInstance)
+ RETURN_PROC("xrCreateSession", createSession)
+ RETURN_PROC("xrDestroySession", destroySession)
+ RETURN_PROC("xrStringToPath", stringToPath)
+ RETURN_PROC("xrSuggestInteractionProfileBindings", suggest)
+ if (d->locate && d->promote) { RETURN_PROC("xrLocateSpaces", locateSpaces) }
+ if (frame::has(d->stubbed, frame::stubs[0])) { RETURN_PROC("xrSetAndroidApplicationThreadKHR", threadSettings) }
+ if (d->refresh) { RETURN_PROC("xrRequestDisplayRefreshRateFB", requestRefreshRate) }
+#undef RETURN_PROC
+ // Includes xrGetInstanceProperties: report the actual runtime's identity.
+ return d->gipa(i, name, out);
+} GUARD_END
+} // namespace
+extern "C" __attribute__((visibility("default"))) XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(
+ const XrNegotiateLoaderInfo* loader, const char* name, XrNegotiateApiLayerRequest* request) {
+ if (!loader || !name || !request || std::strcmp(name, frame::layerName) ||
+ loader->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO ||
+ loader->structVersion != XR_LOADER_INFO_STRUCT_VERSION || loader->structSize != sizeof(*loader) ||
+ request->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST ||
+ request->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || request->structSize != sizeof(*request) ||
+ loader->minInterfaceVersion > 1 || loader->maxInterfaceVersion < 1 ||
+ loader->minApiVersion > XR_CURRENT_API_VERSION || loader->maxApiVersion < XR_MAKE_VERSION(1, 1, 0))
+ return XR_ERROR_INITIALIZATION_FAILED;
+ request->layerInterfaceVersion = 1;
+ request->layerApiVersion = std::min(XR_CURRENT_API_VERSION, loader->maxApiVersion);
+ request->getInstanceProcAddr = gipa; request->createApiLayerInstance = createLayer;
+ LOG("negotiated interface 1, API 1.1");
+ return XR_SUCCESS;
+}
diff --git a/frame/openxr-compat/prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so b/frame/openxr-compat/prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so
new file mode 100755
index 0000000..608cdd1
Binary files /dev/null and b/frame/openxr-compat/prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so differ
diff --git a/frame/openxr-compat/tests/compat_tests.cpp b/frame/openxr-compat/tests/compat_tests.cpp
new file mode 100644
index 0000000..addf345
--- /dev/null
+++ b/frame/openxr-compat/tests/compat_tests.cpp
@@ -0,0 +1,70 @@
+#include "compat_logic.h"
+#include
+#include
+#include
+#define CHECK(expr) do { if (!(expr)) { std::cerr << __LINE__ << ": " #expr "\n"; std::exit(1); } } while (false)
+namespace {
+void* sentinel = reinterpret_cast(0x1234);
+XrResult XRAPI_CALL fakeLocate(XrSession, const XrSpacesLocateInfo* in, XrSpaceLocations* out) {
+ CHECK(in->type == XR_TYPE_SPACES_LOCATE_INFO_KHR);
+ CHECK(out->type == XR_TYPE_SPACE_LOCATIONS_KHR);
+ CHECK(in->next == sentinel);
+ auto* velocities = static_cast(out->next);
+ CHECK(velocities->type == XR_TYPE_SPACE_VELOCITIES_KHR);
+ CHECK(velocities->next == sentinel);
+ CHECK(in->spaceCount == 1 && in->time == 123);
+ out->locations[0].pose.position.x = 42;
+ velocities->velocities[0].linearVelocity.y = 7;
+ return XR_SUCCESS;
+}
+}
+int main() {
+ using namespace frame;
+ CHECK(!downgrade(XR_MAKE_VERSION(1, 0, 99)));
+ CHECK(runtimeVersion(XR_MAKE_VERSION(1, 0, 42)) == XR_MAKE_VERSION(1, 0, 42));
+ CHECK(runtimeVersion(XR_MAKE_VERSION(1, 1, 0)) == XR_API_VERSION_1_0);
+ CHECK(runtimeVersion(XR_MAKE_VERSION(1, 1, 999)) == XR_API_VERSION_1_0);
+ CHECK(runtimeVersion(XR_MAKE_VERSION(2, 0, 0)) == XR_API_VERSION_1_0);
+ Names available{"XR_KHR_locate_spaces", "XR_EXT_palm_pose", "XR_EXT_hand_interaction", "XR_VARJO_quad_views"};
+ Names requested{stubs[0], stubs[1], "XR_MISSING_test", "XR_EXT_palm_pose"};
+ auto p = extensions(requested, available, true);
+ CHECK(p.stubbed.size() == 2 && p.missing == Names{"XR_MISSING_test"});
+ CHECK(has(p.downstream, "XR_MISSING_test"));
+ CHECK(has(p.downstream, "XR_KHR_locate_spaces") && has(p.downstream, "XR_VARJO_quad_views"));
+ CHECK(!has(p.downstream, "XR_EXT_hand_interaction"));
+ CHECK(!has(p.downstream, "XR_EXT_local_floor"));
+ CHECK(std::count(p.downstream.begin(), p.downstream.end(), "XR_EXT_palm_pose") == 1);
+ available.push_back(stubs[0]);
+ CHECK(extensions(requested, available, true).stubbed == Names{stubs[1]});
+ CHECK(extensions({}, available, false).downstream.empty());
+ CHECK(rewritePath("/user/hand/left/input/grip_surface/pose", true) == "/user/hand/left/input/palm_ext/pose");
+ CHECK(rewritePath("/user/hand/right/input/grip_surface/pose", true) == "/user/hand/right/input/palm_ext/pose");
+ CHECK(rewritePath("/user/hand/left/input/grip_surface/pose", false) == "/user/hand/left/input/grip_surface/pose");
+ CHECK(rewritePath("/user/hand/left/input/grip_surface/pose/extra", true) == "/user/hand/left/input/grip_surface/pose/extra");
+ CHECK(rewritePath("/user/hand/left/input/grip/pose", true) == "/user/hand/left/input/grip/pose");
+ CHECK(dropProfile("/interaction_profiles/meta/touch_pro_controller", {}));
+ CHECK(dropProfile("/interaction_profiles/meta/touch_controller_quest_2", {}));
+ CHECK(dropProfile("/interaction_profiles/hp/mixed_reality_controller", {}));
+ CHECK(!dropProfile("/interaction_profiles/hp/mixed_reality_controller", {"XR_EXT_hp_mixed_reality_controller"}));
+ CHECK(!dropProfile("/interaction_profiles/oculus/touch_controller", {}));
+ CHECK(!dropProfile("/interaction_profiles/khr/simple_controller", {}));
+ CHECK(!dropProfile("/interaction_profiles/valve/frame_controller", {}));
+ CHECK(!dropProfile("/interaction_profiles/ext/hand_interaction_ext", {}));
+ CHECK(extensionType(XR_TYPE_SPACES_LOCATE_INFO) == XR_TYPE_SPACES_LOCATE_INFO_KHR);
+ CHECK(extensionType(XR_TYPE_SPACE_LOCATIONS) == XR_TYPE_SPACE_LOCATIONS_KHR);
+ CHECK(extensionType(XR_TYPE_SPACE_VELOCITIES) == XR_TYPE_SPACE_VELOCITIES_KHR);
+ CHECK(extensionType(XR_TYPE_INSTANCE_CREATE_INFO) == XR_TYPE_INSTANCE_CREATE_INFO);
+ static_assert(std::is_same_v);
+ XrSpace space{};
+ XrSpaceLocationData data{};
+ XrSpaceVelocityData velocity{};
+ XrSpaceVelocities velocities{XR_TYPE_SPACE_VELOCITIES, sentinel, 1, &velocity};
+ XrSpaceLocations locations{XR_TYPE_SPACE_LOCATIONS, &velocities, 1, &data};
+ XrSpacesLocateInfo info{XR_TYPE_SPACES_LOCATE_INFO, sentinel, XR_NULL_HANDLE, 123, 1, &space};
+ CHECK(locate(fakeLocate, XR_NULL_HANDLE, &info, &locations) == XR_SUCCESS);
+ CHECK(data.pose.position.x == 42 && velocity.linearVelocity.y == 7);
+ CHECK(info.next == sentinel && locations.next == &velocities && velocities.next == sentinel);
+ CHECK(info.type == XR_TYPE_SPACES_LOCATE_INFO && locations.type == XR_TYPE_SPACE_LOCATIONS && velocities.type == XR_TYPE_SPACE_VELOCITIES);
+ CHECK(locate(fakeLocate, XR_NULL_HANDLE, nullptr, &locations) == XR_ERROR_VALIDATION_FAILURE);
+ std::cout << "All version, extension, path/profile and locate-chain checks passed\n";
+}
diff --git a/frame/openxr-compat/tests/dispatch_tests.cpp b/frame/openxr-compat/tests/dispatch_tests.cpp
new file mode 100644
index 0000000..97aa48b
--- /dev/null
+++ b/frame/openxr-compat/tests/dispatch_tests.cpp
@@ -0,0 +1,134 @@
+// Compile the actual layer with host-only JNI/logging shims, and a fake next
+// layer. This exercises ABI routing without a runtime or a headset.
+#include "../layer.cpp"
+#include
+#include
+#define CHECK(expr) do { if (!(expr)) { std::cerr << __LINE__ << ": " #expr "\n"; std::exit(1); } } while (false)
+namespace {
+uintptr_t serial = 100;
+frame::Names seenExtensions;
+XrVersion seenVersion{};
+XrApiLayerNextInfo* seenNext{};
+int suggestionCalls{}, locateCalls{}, realThreadCalls{};
+bool nativeThread{};
+std::unordered_map paths;
+XrResult XRAPI_CALL fakeEnumerate(const char*, uint32_t capacity, uint32_t* count, XrExtensionProperties* out) {
+ const char* extensions[] = {"XR_KHR_locate_spaces", "XR_EXT_palm_pose", frame::stubs[0]};
+ *count = nativeThread ? 3 : 2;
+ if (!capacity) return XR_SUCCESS;
+ if (capacity < *count) return XR_ERROR_SIZE_INSUFFICIENT;
+ for (uint32_t j = 0; j < *count; ++j) { std::strcpy(out[j].extensionName, extensions[j]); out[j].extensionVersion = 1; }
+ return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakeCreate(const XrInstanceCreateInfo* in, const XrApiLayerCreateInfo* layer, XrInstance* i) {
+ seenVersion = in->applicationInfo.apiVersion; seenNext = layer->nextInfo;
+ seenExtensions.clear();
+ for (uint32_t j = 0; j < in->enabledExtensionCount; ++j) seenExtensions.emplace_back(in->enabledExtensionNames[j]);
+ if (frame::has(seenExtensions, "XR_MISSING_test")) return XR_ERROR_EXTENSION_NOT_PRESENT;
+ *i = reinterpret_cast(++serial); return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakeDestroy(XrInstance) { return XR_SUCCESS; }
+XrResult XRAPI_CALL fakeCreateSession(XrInstance, const XrSessionCreateInfo*, XrSession* s) {
+ *s = reinterpret_cast(++serial); return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakeDestroySession(XrSession) { return XR_SUCCESS; }
+XrResult XRAPI_CALL fakeString(XrInstance, const char* text, XrPath* p) {
+ *p = ++serial; paths[*p] = text; return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakePath(XrInstance, XrPath p, uint32_t cap, uint32_t* count, char* out) {
+ if (!paths.count(p)) return XR_ERROR_PATH_INVALID;
+ *count = static_cast(paths[p].size() + 1);
+ if (!cap) return XR_SUCCESS;
+ if (cap < *count) return XR_ERROR_SIZE_INSUFFICIENT;
+ std::strcpy(out, paths[p].c_str()); return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakeSuggest(XrInstance, const XrInteractionProfileSuggestedBinding* info) {
+ ++suggestionCalls;
+ if (info->countSuggestedBindings) CHECK(paths[info->suggestedBindings[0].binding] == "/user/hand/left/input/palm_ext/pose");
+ return XR_SUCCESS;
+}
+XrResult XRAPI_CALL fakeLocate(XrSession, const XrSpacesLocateInfo*, XrSpaceLocations*) { ++locateCalls; return XR_SUCCESS; }
+XrResult XRAPI_CALL fakeThread(XrSession, XrAndroidThreadTypeKHR, uint32_t) { ++realThreadCalls; return XR_TIMEOUT_EXPIRED; }
+XrResult XRAPI_CALL fakeProperties(XrInstance, XrInstanceProperties*) { return XR_SUCCESS; }
+XrResult XRAPI_CALL fakeGipa(XrInstance, const char* n, PFN_xrVoidFunction* out) {
+#define MAP(name, f) if (!std::strcmp(n, name)) { *out = reinterpret_cast(f); return XR_SUCCESS; }
+ MAP("xrEnumerateInstanceExtensionProperties", fakeEnumerate)
+ MAP("xrDestroyInstance", fakeDestroy)
+ MAP("xrCreateSession", fakeCreateSession) MAP("xrDestroySession", fakeDestroySession)
+ MAP("xrStringToPath", fakeString) MAP("xrPathToString", fakePath)
+ MAP("xrSuggestInteractionProfileBindings", fakeSuggest) MAP("xrLocateSpacesKHR", fakeLocate)
+ MAP("xrSetAndroidApplicationThreadKHR", fakeThread) MAP("xrGetInstanceProperties", fakeProperties)
+#undef MAP
+ *out = nullptr; return XR_ERROR_FUNCTION_UNSUPPORTED;
+}
+}
+int main() {
+ XrNegotiateLoaderInfo loader{XR_LOADER_INTERFACE_STRUCT_LOADER_INFO, 1, sizeof(XrNegotiateLoaderInfo), 1, 1,
+ XR_MAKE_VERSION(1,0,0), XR_MAKE_VERSION(1,1023,4095)};
+ XrNegotiateApiLayerRequest request{};
+ request.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST; request.structVersion = 1; request.structSize = sizeof(request);
+ CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_SUCCESS);
+ CHECK(request.getInstanceProcAddr && request.createApiLayerInstance);
+ loader.maxApiVersion = XR_MAKE_VERSION(1,1,0);
+ CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_SUCCESS);
+ CHECK(request.layerApiVersion == loader.maxApiVersion);
+ loader.maxApiVersion = XR_MAKE_VERSION(1,0,99);
+ CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_ERROR_INITIALIZATION_FAILED);
+ loader.maxApiVersion = XR_MAKE_VERSION(1,1023,4095);
+ CHECK(xrNegotiateLoaderApiLayerInterface(&loader, "wrong", &request) == XR_ERROR_INITIALIZATION_FAILED);
+ XrApiLayerNextInfo tail{};
+ XrApiLayerNextInfo next{XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO, 1, sizeof(XrApiLayerNextInfo), {}, fakeGipa, fakeCreate, &tail};
+ XrApiLayerCreateInfo layer{XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO, 1, sizeof(XrApiLayerCreateInfo), nullptr, {}, &next};
+ XrInstanceCreateInfo info{XR_TYPE_INSTANCE_CREATE_INFO, nullptr, 0, {}, 0, nullptr, 2, frame::stubs};
+ info.applicationInfo.apiVersion = XR_MAKE_VERSION(1,1,999);
+ XrInstance a{}, b{};
+ CHECK(createLayer(&info, &layer, &a) == XR_SUCCESS);
+ CHECK(seenVersion == XR_API_VERSION_1_0 && info.applicationInfo.apiVersion == XR_MAKE_VERSION(1,1,999));
+ CHECK(seenNext == &tail && layer.nextInfo == &next);
+ CHECK(!frame::has(seenExtensions, frame::stubs[0]) && frame::has(seenExtensions, "XR_KHR_locate_spaces"));
+ XrSession sa{}, sb{};
+ CHECK(createSession(a, nullptr, &sa) == XR_SUCCESS);
+ CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_SUCCESS);
+ nativeThread = true;
+ CHECK(createLayer(&info, &layer, &b) == XR_SUCCESS);
+ CHECK(frame::has(seenExtensions, frame::stubs[0]));
+ CHECK(createSession(b, nullptr, &sb) == XR_SUCCESS);
+ PFN_xrVoidFunction fn{};
+ CHECK(gipa(b, "xrSetAndroidApplicationThreadKHR", &fn) == XR_SUCCESS);
+ CHECK(reinterpret_cast(fn)(sb, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_TIMEOUT_EXPIRED);
+ CHECK(realThreadCalls == 1);
+ CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_SUCCESS);
+ CHECK(gipa(a, "xrGetInstanceProperties", &fn) == XR_SUCCESS && fn == reinterpret_cast(fakeProperties));
+ CHECK(gipa(a, "xrLocateSpaces", &fn) == XR_SUCCESS);
+ XrSpacesLocateInfo locateInfo{XR_TYPE_SPACES_LOCATE_INFO, nullptr, XR_NULL_HANDLE, 1, 0, nullptr};
+ XrSpaceLocations locations{XR_TYPE_SPACE_LOCATIONS, nullptr, 0, nullptr};
+ CHECK(reinterpret_cast(fn)(sa, &locateInfo, &locations) == XR_SUCCESS && locateCalls == 1);
+ XrPath binding{}, profile{};
+ CHECK(stringToPath(a, "/user/hand/left/input/grip_surface/pose", &binding) == XR_SUCCESS);
+ CHECK(paths[binding] == "/user/hand/left/input/palm_ext/pose");
+ fakeString(a, "/user/hand/left/input/grip_surface/pose", &binding); // preexisting path: rewrite in suggest too
+ fakeString(a, "/interaction_profiles/oculus/touch_controller", &profile);
+ XrActionSuggestedBinding action{XR_NULL_HANDLE, binding};
+ XrInteractionProfileSuggestedBinding suggested{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING, nullptr, profile, 1, &action};
+ CHECK(suggest(a, &suggested) == XR_SUCCESS && suggestionCalls == 1);
+ CHECK(paths[action.binding] == "/user/hand/left/input/grip_surface/pose");
+ fakeString(a, "/interaction_profiles/meta/touch_pro_controller", &suggested.interactionProfile);
+ CHECK(suggest(a, &suggested) == XR_SUCCESS && suggestionCalls == 1);
+ uint32_t count{};
+ CHECK(enumerate(frame::layerName, 0, &count, nullptr) == XR_SUCCESS && count == 2);
+ XrExtensionProperties props[2]{{XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0}, {XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0}};
+ CHECK(enumerate(frame::layerName, 1, &count, props) == XR_ERROR_SIZE_INSUFFICIENT);
+ CHECK(enumerate(frame::layerName, 2, &count, props) == XR_SUCCESS && count == 2);
+ CHECK(enumerate(nullptr, 0, &count, nullptr) == XR_SUCCESS && count == 4);
+ const char* missing = "XR_MISSING_test";
+ info.enabledExtensionCount = 1; info.enabledExtensionNames = &missing;
+ XrInstance failed{};
+ CHECK(createLayer(&info, &layer, &failed) == XR_ERROR_EXTENSION_NOT_PRESENT && failed == XR_NULL_HANDLE);
+ CHECK(destroySession(sa) == XR_SUCCESS);
+ CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_ERROR_HANDLE_INVALID);
+ CHECK(destroyInstance(a) == XR_SUCCESS);
+ CHECK(get(sb) && !get(a));
+ CHECK(destroyInstance(b) == XR_SUCCESS && !get(sb));
+ CHECK(instances.empty() && sessions.empty() && enumerateNext == nullptr);
+ std::cout << "Negotiation, next-layer chaining, multi-instance dispatch, stubs, forwarding and cleanup passed\n";
+}
diff --git a/frame/openxr-compat/tests/shims/android/log.h b/frame/openxr-compat/tests/shims/android/log.h
new file mode 100644
index 0000000..dcaf894
--- /dev/null
+++ b/frame/openxr-compat/tests/shims/android/log.h
@@ -0,0 +1,9 @@
+#pragma once
+// Host-only logcat replacement. Android builds use the NDK header and liblog.
+#include
+#include
+#define ANDROID_LOG_INFO 4
+inline int __android_log_print(int, const char*, const char* fmt, ...) {
+ va_list args; va_start(args, fmt); auto r = std::vfprintf(stderr, fmt, args);
+ va_end(args); std::fputc('\n', stderr); return r;
+}
diff --git a/frame/openxr-compat/tests/shims/jni.h b/frame/openxr-compat/tests/shims/jni.h
new file mode 100644
index 0000000..6223bc2
--- /dev/null
+++ b/frame/openxr-compat/tests/shims/jni.h
@@ -0,0 +1,3 @@
+#pragma once
+// Only the opaque jobject declaration is needed by openxr_platform.h.
+typedef void* jobject;
diff --git a/frame/openxr-compat/vendor/LICENSE b/frame/openxr-compat/vendor/LICENSE
new file mode 100644
index 0000000..527a83a
--- /dev/null
+++ b/frame/openxr-compat/vendor/LICENSE
@@ -0,0 +1,208 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
+AND DISTRIBUTION
+
+ 1. Definitions.
+
+
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution
+as defined by Sections 1 through 9 of this document.
+
+
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct
+or indirect, to cause the direction or management of such entity, whether
+by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
+of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
+granted by this License.
+
+
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+
+
+"Object" form shall mean any form resulting from mechanical transformation
+or translation of a Source form, including but not limited to compiled object
+code, generated documentation, and conversions to other media types.
+
+
+
+"Work" shall mean the work of authorship, whether in Source or Object form,
+made available under the License, as indicated by a copyright notice that
+is included in or attached to the work (an example is provided in the Appendix
+below).
+
+
+
+"Derivative Works" shall mean any work, whether in Source or Object form,
+that is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative
+Works shall not include works that remain separable from, or merely link (or
+bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative
+Works thereof, that is intentionally submitted to Licensor for inclusion in
+the Work by the copyright owner or by an individual or Legal Entity authorized
+to submit on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication
+sent to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor
+for the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently incorporated
+within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
+Derivative Works of, publicly display, publicly perform, sublicense, and distribute
+the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License,
+each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section) patent
+license to make, have made, use, offer to sell, sell, import, and otherwise
+transfer the Work, where such license applies only to those patent claims
+licensable by such Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work to which such
+Contribution(s) was submitted. If You institute patent litigation against
+any entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that the Work or a Contribution incorporated within the Work constitutes direct
+or contributory patent infringement, then any patent licenses granted to You
+under this License for that Work shall terminate as of the date such litigation
+is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or
+Derivative Works thereof in any medium, with or without modifications, and
+in Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy
+of this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that
+You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source
+form of the Work, excluding those notices that do not pertain to any part
+of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable copy
+of the attribution notices contained within such NOTICE file, excluding those
+notices that do not pertain to any part of the Derivative Works, in at least
+one of the following places: within a NOTICE text file distributed as part
+of the Derivative Works; within the Source form or documentation, if provided
+along with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents
+of the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works
+that You distribute, alongside or as an addendum to the NOTICE text from the
+Work, provided that such additional attribution notices cannot be construed
+as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction,
+or distribution of Your modifications, or for any such Derivative Works as
+a whole, provided Your use, reproduction, and distribution of the Work otherwise
+complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any
+Contribution intentionally submitted for inclusion in the Work by You to the
+Licensor shall be under the terms and conditions of this License, without
+any additional terms or conditions. Notwithstanding the above, nothing herein
+shall supersede or modify the terms of any separate license agreement you
+may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as required
+for reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to
+in writing, Licensor provides the Work (and each Contributor provides its
+Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied, including, without limitation, any warranties
+or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
+A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
+of using or redistributing the Work and assume any risks associated with Your
+exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether
+in tort (including negligence), contract, or otherwise, unless required by
+applicable law (such as deliberate and grossly negligent acts) or agreed to
+in writing, shall any Contributor be liable to You for damages, including
+any direct, indirect, special, incidental, or consequential damages of any
+character arising as a result of this License or out of the use or inability
+to use the Work (including but not limited to damages for loss of goodwill,
+work stoppage, computer failure or malfunction, or any and all other commercial
+damages or losses), even if such Contributor has been advised of the possibility
+of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work
+or Derivative Works thereof, You may choose to offer, and charge a fee for,
+acceptance of support, warranty, indemnity, or other liability obligations
+and/or rights consistent with this License. However, in accepting such obligations,
+You may act only on Your own behalf and on Your sole responsibility, not on
+behalf of any other Contributor, and only if You agree to indemnify, defend,
+and hold each Contributor harmless for any liability incurred by, or claims
+asserted against, such Contributor by reason of your accepting any such warranty
+or additional liability. END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own identifying
+information. (Don't include the brackets!) The text should be enclosed in
+the appropriate comment syntax for the file format. We also recommend that
+a file or class name and description of purpose be included on the same "printed
+page" as the copyright notice for easier identification within third-party
+archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+
+you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+
+distributed under the License is distributed on an "AS IS" BASIS,
+
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+
+See the License for the specific language governing permissions and
+
+limitations under the License.
diff --git a/frame/openxr-compat/vendor/loader_interfaces.h b/frame/openxr-compat/vendor/loader_interfaces.h
new file mode 100644
index 0000000..020c345
--- /dev/null
+++ b/frame/openxr-compat/vendor/loader_interfaces.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2017-2023, The Khronos Group Inc.
+// Copyright (c) 2017 Valve Corporation
+// Copyright (c) 2017 LunarG, Inc.
+//
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+//
+// Initial Author: Mark Young
+//
+
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Forward declare.
+typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo;
+
+// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance.
+// This function allows us to pass special API layer information to each layer during the process of creating an Instance.
+typedef XrResult(XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo *info,
+ const XrApiLayerCreateInfo *apiLayerInfo, XrInstance *instance);
+
+// Loader/API Layer Interface versions
+// 1 - First version, introduces negotiation structure and functions
+#define XR_CURRENT_LOADER_API_LAYER_VERSION 1
+
+// Loader/Runtime Interface versions
+// 1 - First version, introduces negotiation structure and functions
+#define XR_CURRENT_LOADER_RUNTIME_VERSION 1
+
+// Version negotiation values
+typedef enum XrLoaderInterfaceStructs {
+ XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0,
+ XR_LOADER_INTERFACE_STRUCT_LOADER_INFO,
+ XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST,
+ XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST,
+ XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO,
+ XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO,
+} XrLoaderInterfaceStructs;
+
+#define XR_LOADER_INFO_STRUCT_VERSION 1
+typedef struct XrNegotiateLoaderInfo {
+ XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_LOADER_INFO
+ uint32_t structVersion; // XR_LOADER_INFO_STRUCT_VERSION
+ size_t structSize; // sizeof(XrNegotiateLoaderInfo)
+ uint32_t minInterfaceVersion;
+ uint32_t maxInterfaceVersion;
+ XrVersion minApiVersion;
+ XrVersion maxApiVersion;
+} XrNegotiateLoaderInfo;
+
+#define XR_API_LAYER_INFO_STRUCT_VERSION 1
+typedef struct XrNegotiateApiLayerRequest {
+ XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST
+ uint32_t structVersion; // XR_API_LAYER_INFO_STRUCT_VERSION
+ size_t structSize; // sizeof(XrNegotiateApiLayerRequest)
+ uint32_t layerInterfaceVersion; // CURRENT_LOADER_API_LAYER_VERSION
+ XrVersion layerApiVersion;
+ PFN_xrGetInstanceProcAddr getInstanceProcAddr;
+ PFN_xrCreateApiLayerInstance createApiLayerInstance;
+} XrNegotiateApiLayerRequest;
+
+#define XR_RUNTIME_INFO_STRUCT_VERSION 1
+typedef struct XrNegotiateRuntimeRequest {
+ XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST
+ uint32_t structVersion; // XR_RUNTIME_INFO_STRUCT_VERSION
+ size_t structSize; // sizeof(XrNegotiateRuntimeRequest)
+ uint32_t runtimeInterfaceVersion; // CURRENT_LOADER_RUNTIME_VERSION
+ XrVersion runtimeApiVersion;
+ PFN_xrGetInstanceProcAddr getInstanceProcAddr;
+} XrNegotiateRuntimeRequest;
+
+// Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or
+// more API layers needs to expose at least this function.
+typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo *loaderInfo,
+ const char *apiLayerName,
+ XrNegotiateApiLayerRequest *apiLayerRequest);
+
+// Function used to negotiate an interface betewen the loader and a runtime. Each runtime should expose
+// at least this function.
+typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo *loaderInfo,
+ XrNegotiateRuntimeRequest *runtimeRequest);
+
+// Forward declare.
+typedef struct XrApiLayerNextInfo XrApiLayerNextInfo;
+
+#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1
+struct XrApiLayerNextInfo {
+ XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO
+ uint32_t structVersion; // XR_API_LAYER_NEXT_INFO_STRUCT_VERSION
+ size_t structSize; // sizeof(XrApiLayerNextInfo)
+ char layerName[XR_MAX_API_LAYER_NAME_SIZE]; // Name of API layer which should receive this info
+ PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; // Pointer to next API layer's xrGetInstanceProcAddr
+ PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; // Pointer to next API layer's xrCreateApiLayerInstance
+ XrApiLayerNextInfo *next; // Pointer to the next API layer info in the sequence
+};
+
+#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512
+#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1
+typedef struct XrApiLayerCreateInfo {
+ XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO
+ uint32_t structVersion; // XR_API_LAYER_CREATE_INFO_STRUCT_VERSION
+ size_t structSize; // sizeof(XrApiLayerCreateInfo)
+ void *loaderInstance; // Pointer to the LoaderInstance class
+ char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; // Location to the found settings file (or empty '\0')
+ XrApiLayerNextInfo *nextInfo; // Pointer to the next API layer's Info
+} XrApiLayerCreateInfo;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/frame/openxr-compat/vendor/openxr/openxr.h b/frame/openxr-compat/vendor/openxr/openxr.h
new file mode 100644
index 0000000..af56c0f
--- /dev/null
+++ b/frame/openxr-compat/vendor/openxr/openxr.h
@@ -0,0 +1,14911 @@
+#ifndef OPENXR_H_
+#define OPENXR_H_ 1
+
+/*
+** Copyright 2017-2026 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0 OR MIT
+*/
+
+/*
+** This header is generated from the Khronos OpenXR XML API Registry.
+**
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+// XR_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls.
+#define XR_VERSION_1_0 1
+#include "openxr_platform_defines.h"
+#define XR_MAKE_VERSION(major, minor, patch) \
+ ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
+
+// OpenXR current version number.
+#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 63)
+
+// OpenXR 1.0 version number
+#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION))
+
+#define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL)
+#define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL)
+#define XR_VERSION_PATCH(version) (uint32_t)((uint64_t)(version) & 0xffffffffULL)
+
+#define XR_MIN_COMPOSITION_LAYERS_SUPPORTED 16
+
+
+#if !defined(XR_NULL_HANDLE)
+#if (XR_PTR_SIZE == 8) && XR_CPP_NULLPTR_SUPPORTED
+ #define XR_NULL_HANDLE nullptr
+#else
+ #define XR_NULL_HANDLE 0
+#endif
+#endif
+
+
+#define XR_NULL_SYSTEM_ID 0
+
+
+#define XR_NULL_PATH 0
+
+
+#define XR_SUCCEEDED(result) ((result) >= 0)
+
+
+#define XR_FAILED(result) ((result) < 0)
+
+
+#define XR_UNQUALIFIED_SUCCESS(result) ((result) == 0)
+
+
+#define XR_NO_DURATION 0
+
+
+#define XR_INFINITE_DURATION 0x7fffffffffffffffLL
+
+
+#define XR_MIN_HAPTIC_DURATION -1
+
+
+#define XR_FREQUENCY_UNSPECIFIED 0
+
+
+#define XR_MAX_EVENT_DATA_SIZE sizeof(XrEventDataBuffer)
+
+
+#define XR_EXTENSION_ENUM_BASE 1000000000
+
+
+#define XR_EXTENSION_ENUM_STRIDE 1000
+
+
+#if !defined(XR_MAY_ALIAS)
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4))
+#define XR_MAY_ALIAS __attribute__((__may_alias__))
+#else
+#define XR_MAY_ALIAS
+#endif
+#endif
+
+
+#if !defined(XR_DEFINE_HANDLE)
+#if (XR_PTR_SIZE == 8)
+ #define XR_DEFINE_HANDLE(object) typedef struct object##_T* object;
+#else
+ #define XR_DEFINE_HANDLE(object) typedef uint64_t object;
+#endif
+#endif
+
+
+#if !defined(XR_DEFINE_OPAQUE_64)
+ #if (XR_PTR_SIZE == 8)
+ #define XR_DEFINE_OPAQUE_64(object) typedef struct object##_T* object;
+ #else
+ #define XR_DEFINE_OPAQUE_64(object) typedef uint64_t object;
+ #endif
+#endif
+
+
+#if !defined(XR_DEFINE_ATOM)
+ #define XR_DEFINE_ATOM(object) typedef uint64_t object;
+#endif
+
+typedef uint64_t XrVersion;
+typedef uint64_t XrFlags64;
+XR_DEFINE_ATOM(XrSystemId)
+typedef uint32_t XrBool32;
+XR_DEFINE_ATOM(XrPath)
+typedef int64_t XrTime;
+typedef int64_t XrDuration;
+XR_DEFINE_HANDLE(XrInstance)
+XR_DEFINE_HANDLE(XrSession)
+XR_DEFINE_HANDLE(XrSpace)
+XR_DEFINE_HANDLE(XrAction)
+XR_DEFINE_HANDLE(XrSwapchain)
+XR_DEFINE_HANDLE(XrActionSet)
+#define XR_TRUE 1
+#define XR_FALSE 0
+#define XR_MAX_EXTENSION_NAME_SIZE 128
+#define XR_MAX_API_LAYER_NAME_SIZE 256
+#define XR_MAX_API_LAYER_DESCRIPTION_SIZE 256
+#define XR_MAX_SYSTEM_NAME_SIZE 256
+#define XR_MAX_APPLICATION_NAME_SIZE 128
+#define XR_MAX_ENGINE_NAME_SIZE 128
+#define XR_MAX_RUNTIME_NAME_SIZE 128
+#define XR_MAX_PATH_LENGTH 256
+#define XR_MAX_STRUCTURE_NAME_SIZE 64
+#define XR_MAX_RESULT_STRING_SIZE 64
+#define XR_MAX_ACTION_SET_NAME_SIZE 64
+#define XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE 128
+#define XR_MAX_ACTION_NAME_SIZE 64
+#define XR_MAX_LOCALIZED_ACTION_NAME_SIZE 128
+
+typedef enum XrResult {
+ XR_SUCCESS = 0,
+ XR_TIMEOUT_EXPIRED = 1,
+ XR_SESSION_LOSS_PENDING = 3,
+ XR_EVENT_UNAVAILABLE = 4,
+ XR_SPACE_BOUNDS_UNAVAILABLE = 7,
+ XR_SESSION_NOT_FOCUSED = 8,
+ XR_FRAME_DISCARDED = 9,
+ XR_ERROR_VALIDATION_FAILURE = -1,
+ XR_ERROR_RUNTIME_FAILURE = -2,
+ XR_ERROR_OUT_OF_MEMORY = -3,
+ XR_ERROR_API_VERSION_UNSUPPORTED = -4,
+ XR_ERROR_INITIALIZATION_FAILED = -6,
+ XR_ERROR_FUNCTION_UNSUPPORTED = -7,
+ XR_ERROR_FEATURE_UNSUPPORTED = -8,
+ XR_ERROR_EXTENSION_NOT_PRESENT = -9,
+ XR_ERROR_LIMIT_REACHED = -10,
+ XR_ERROR_SIZE_INSUFFICIENT = -11,
+ XR_ERROR_HANDLE_INVALID = -12,
+ XR_ERROR_INSTANCE_LOST = -13,
+ XR_ERROR_SESSION_RUNNING = -14,
+ XR_ERROR_SESSION_NOT_RUNNING = -16,
+ XR_ERROR_SESSION_LOST = -17,
+ XR_ERROR_SYSTEM_INVALID = -18,
+ XR_ERROR_PATH_INVALID = -19,
+ XR_ERROR_PATH_COUNT_EXCEEDED = -20,
+ XR_ERROR_PATH_FORMAT_INVALID = -21,
+ XR_ERROR_PATH_UNSUPPORTED = -22,
+ XR_ERROR_LAYER_INVALID = -23,
+ XR_ERROR_LAYER_LIMIT_EXCEEDED = -24,
+ XR_ERROR_SWAPCHAIN_RECT_INVALID = -25,
+ XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED = -26,
+ XR_ERROR_ACTION_TYPE_MISMATCH = -27,
+ XR_ERROR_SESSION_NOT_READY = -28,
+ XR_ERROR_SESSION_NOT_STOPPING = -29,
+ XR_ERROR_TIME_INVALID = -30,
+ XR_ERROR_REFERENCE_SPACE_UNSUPPORTED = -31,
+ XR_ERROR_FILE_ACCESS_ERROR = -32,
+ XR_ERROR_FILE_CONTENTS_INVALID = -33,
+ XR_ERROR_FORM_FACTOR_UNSUPPORTED = -34,
+ XR_ERROR_FORM_FACTOR_UNAVAILABLE = -35,
+ XR_ERROR_API_LAYER_NOT_PRESENT = -36,
+ XR_ERROR_CALL_ORDER_INVALID = -37,
+ XR_ERROR_GRAPHICS_DEVICE_INVALID = -38,
+ XR_ERROR_POSE_INVALID = -39,
+ XR_ERROR_INDEX_OUT_OF_RANGE = -40,
+ XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED = -41,
+ XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED = -42,
+ XR_ERROR_NAME_DUPLICATED = -44,
+ XR_ERROR_NAME_INVALID = -45,
+ XR_ERROR_ACTIONSET_NOT_ATTACHED = -46,
+ XR_ERROR_ACTIONSETS_ALREADY_ATTACHED = -47,
+ XR_ERROR_LOCALIZED_NAME_DUPLICATED = -48,
+ XR_ERROR_LOCALIZED_NAME_INVALID = -49,
+ XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING = -50,
+ XR_ERROR_RUNTIME_UNAVAILABLE = -51,
+ XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED = -1000710001,
+ XR_ERROR_PERMISSION_INSUFFICIENT = -1000710000,
+ XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR = -1000003000,
+ XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR = -1000003001,
+ XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT = -1000039001,
+ XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT = -1000053000,
+ XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT = -1000055000,
+ XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT = -1000066000,
+ XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT = -1000097000,
+ XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT = -1000097001,
+ XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT = -1000097002,
+ XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT = -1000097003,
+ XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT = -1000097004,
+ XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT = -1000097005,
+ XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB = -1000101000,
+ XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB = -1000108000,
+ XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB = -1000113000,
+ XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB = -1000113001,
+ XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB = -1000113002,
+ XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB = -1000113003,
+ XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB = -1000118000,
+ XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB = -1000118001,
+ XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB = -1000118002,
+ XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB = -1000118003,
+ XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB = -1000118004,
+ XR_ERROR_UNKNOWN_PASSTHROUGH_FB = -1000118050,
+ XR_ERROR_RENDER_MODEL_KEY_INVALID_FB = -1000119000,
+ XR_RENDER_MODEL_UNAVAILABLE_FB = 1000119020,
+ XR_ERROR_MARKER_NOT_TRACKED_VARJO = -1000124000,
+ XR_ERROR_MARKER_ID_INVALID_VARJO = -1000124001,
+ XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML = -1000138000,
+ XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML = -1000138001,
+ XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML = -1000138002,
+ XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML = -1000138003,
+ XR_ERROR_MARKER_INVALID_ML = -1000138004,
+ XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML = -1000139000,
+ XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML = -1000139001,
+ XR_ERROR_LOCALIZATION_MAP_FAIL_ML = -1000139002,
+ XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML = -1000139003,
+ XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML = -1000139004,
+ XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML = -1000139005,
+ XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML = -1000139006,
+ XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML = -1000140000,
+ XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML = -1000140001,
+ XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML = -1000140002,
+ XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML = -1000140003,
+ XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML = -1000141000,
+ XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT = -1000142001,
+ XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT = -1000142002,
+ XR_SCENE_MARKER_DATA_NOT_STRING_MSFT = 1000147000,
+ XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB = -1000169000,
+ XR_ERROR_SPACE_LOCALIZATION_FAILED_FB = -1000169001,
+ XR_ERROR_SPACE_NETWORK_TIMEOUT_FB = -1000169002,
+ XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003,
+ XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004,
+ XR_ERROR_SPACE_INSUFFICIENT_RESOURCES_META = -1000259000,
+ XR_ERROR_SPACE_STORAGE_AT_CAPACITY_META = -1000259001,
+ XR_ERROR_SPACE_INSUFFICIENT_VIEW_META = -1000259002,
+ XR_ERROR_SPACE_PERMISSION_INSUFFICIENT_META = -1000259003,
+ XR_ERROR_SPACE_RATE_LIMITED_META = -1000259004,
+ XR_ERROR_SPACE_TOO_DARK_META = -1000259005,
+ XR_ERROR_SPACE_TOO_BRIGHT_META = -1000259006,
+ XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000,
+ XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META = 1000291000,
+ XR_ERROR_RENDER_MODEL_ID_INVALID_EXT = -1000300000,
+ XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT = -1000300001,
+ XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT = -1000300002,
+ XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT = -1000301000,
+ XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000,
+ XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000,
+ XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD = -1000389000,
+ XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD = -1000389001,
+ XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD = -1000389002,
+ XR_ERROR_SPATIAL_ANCHOR_NOT_FOUND_BD = -1000390000,
+ XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_BD = -1000391000,
+ XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_BD = -1000391001,
+ XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_BD = -1000391002,
+ XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_BD = -1000391003,
+ XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_BD = -1000391004,
+ XR_ERROR_SCENE_CAPTURE_FAILURE_BD = -1000392000,
+ XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000,
+ XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001,
+ XR_ERROR_MISMATCHING_TRACKABLE_TYPE_ANDROID = -1000455000,
+ XR_ERROR_TRACKABLE_TYPE_NOT_SUPPORTED_ANDROID = -1000455001,
+ XR_ERROR_ANCHOR_ID_NOT_FOUND_ANDROID = -1000457000,
+ XR_ERROR_ANCHOR_ALREADY_PERSISTED_ANDROID = -1000457001,
+ XR_ERROR_ANCHOR_NOT_TRACKING_ANDROID = -1000457002,
+ XR_ERROR_PERSISTED_DATA_NOT_READY_ANDROID = -1000457003,
+ XR_ERROR_SERVICE_NOT_READY_ANDROID = -1000458000,
+ XR_ERROR_MESH_DATA_LIMIT_EXCEEDED_ANDROID = -1000462000,
+ XR_ERROR_FUTURE_PENDING_EXT = -1000469001,
+ XR_ERROR_FUTURE_INVALID_EXT = -1000469002,
+ XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML = -1000473000,
+ XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML = -1000473001,
+ XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML = -1000474000,
+ XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML = -1000474001,
+ XR_ERROR_FACIAL_EXPRESSION_PERMISSION_DENIED_ML = 1000482000,
+ XR_BOUNDARY_VISIBILITY_SUPPRESSION_NOT_ALLOWED_META = 1000528000,
+ XR_ERROR_COLOCATION_DISCOVERY_NETWORK_FAILED_META = -1000571001,
+ XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META = -1000571002,
+ XR_COLOCATION_DISCOVERY_ALREADY_ADVERTISING_META = 1000571003,
+ XR_COLOCATION_DISCOVERY_ALREADY_DISCOVERING_META = 1000571004,
+ XR_ERROR_SPACE_GROUP_NOT_FOUND_META = -1000572002,
+ XR_ERROR_ANCHOR_NOT_OWNED_BY_CALLER_ANDROID = -1000701000,
+ XR_ERROR_IMAGE_FORMAT_UNSUPPORTED_ANDROID = -1000709000,
+ XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT = -1000740001,
+ XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT = -1000740002,
+ XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT = -1000740003,
+ XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT = -1000740004,
+ XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT = -1000740005,
+ XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT = -1000740006,
+ XR_ERROR_CAMERA_UNAVAILABLE_BD = -1000755000,
+ XR_ERROR_CAMERA_OCCUPIED_BD = -1000755001,
+ XR_ERROR_CAMERA_CAPTURE_SESSION_CAPTURING_BD = -1000755002,
+ XR_ERROR_CAMERA_CAPTURE_SESSION_NOT_CAPTURING_BD = -1000755003,
+ XR_ERROR_CAMERA_ID_INVALID_BD = -1000755004,
+ XR_ERROR_CAMERA_IMAGE_ID_INVALID_BD = -1000755005,
+ XR_ERROR_CAMERA_PROPERTY_TYPE_INVALID_BD = -1000755006,
+ XR_ERROR_CAMERA_CAPABILITY_TYPE_INVALID_BD = -1000755007,
+ XR_ERROR_IMAGE_BUFFER_EXHAUSTED_BD = -1000755008,
+ XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT = -1000763001,
+ XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT = -1000781001,
+ XR_ERROR_SPATIAL_IMAGE_FORMAT_UNSUPPORTED_EXT = -1000782001,
+ XR_ERROR_SPATIAL_IMAGE_INVALID_EXT = -1000782002,
+ XR_ERROR_SPATIAL_IMAGE_SIZE_MISSING_EXT = -1000782003,
+ XR_ERROR_KEYLESS_AUTH_NOT_SETUP_ANDROID = -1000787000,
+ XR_ERROR_KEYLESS_AUTH_FAILED_ANDROID = -1000787001,
+ XR_ERROR_GEOSPATIAL_TRACKER_NOT_RUNNING_ANDROID = -1000789000,
+ XR_ERROR_GEOSPATIAL_COORDINATES_INVALID_ANDROID = -1000789001,
+ XR_ERROR_GEOSPATIAL_CLOUD_AUTH_FAILED_ANDROID = -1000789002,
+ XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID = -1000790001,
+ XR_ERROR_SPATIAL_ANCHOR_ENTITY_ID_INVALID_ANDROID = -1000795001,
+ XR_ERROR_SURFACE_ANCHOR_LOCATION_UNSUPPORTED_ANDROID = -1000797000,
+ XR_ERROR_SPATIAL_CONTAINER_CLOSED_EXT = -1000810000,
+ XR_ERROR_SPATIAL_CONTAINERS_ENABLED_EXT = -1000810001,
+ XR_ERROR_SPATIAL_CONTAINERS_NOT_ENABLED_EXT = -1000810002,
+ XR_ERROR_COMPATIBLE_SPATIAL_CONTAINER_MISSING_EXT = -1000810003,
+ XR_ERROR_SPATIAL_CONTAINER_IS_RENDERING_EXT = -1000813001,
+ XR_ERROR_SPATIAL_CONTAINER_NOT_RENDERING_EXT = -1000813002,
+ XR_ERROR_SPATIAL_CONTAINER_MISSING_EXT = -1000813003,
+ XR_ERROR_SPATIAL_CONTAINER_DUPLICATE_EXT = -1000813004,
+ XR_ERROR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MISSING_EXT = -1000813005,
+ XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED,
+ XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT,
+ XR_RESULT_MAX_ENUM = 0x7FFFFFFF
+} XrResult;
+
+typedef enum XrStructureType {
+ XR_TYPE_UNKNOWN = 0,
+ XR_TYPE_API_LAYER_PROPERTIES = 1,
+ XR_TYPE_EXTENSION_PROPERTIES = 2,
+ XR_TYPE_INSTANCE_CREATE_INFO = 3,
+ XR_TYPE_SYSTEM_GET_INFO = 4,
+ XR_TYPE_SYSTEM_PROPERTIES = 5,
+ XR_TYPE_VIEW_LOCATE_INFO = 6,
+ XR_TYPE_VIEW = 7,
+ XR_TYPE_SESSION_CREATE_INFO = 8,
+ XR_TYPE_SWAPCHAIN_CREATE_INFO = 9,
+ XR_TYPE_SESSION_BEGIN_INFO = 10,
+ XR_TYPE_VIEW_STATE = 11,
+ XR_TYPE_FRAME_END_INFO = 12,
+ XR_TYPE_HAPTIC_VIBRATION = 13,
+ XR_TYPE_EVENT_DATA_BUFFER = 16,
+ XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING = 17,
+ XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED = 18,
+ XR_TYPE_ACTION_STATE_BOOLEAN = 23,
+ XR_TYPE_ACTION_STATE_FLOAT = 24,
+ XR_TYPE_ACTION_STATE_VECTOR2F = 25,
+ XR_TYPE_ACTION_STATE_POSE = 27,
+ XR_TYPE_ACTION_SET_CREATE_INFO = 28,
+ XR_TYPE_ACTION_CREATE_INFO = 29,
+ XR_TYPE_INSTANCE_PROPERTIES = 32,
+ XR_TYPE_FRAME_WAIT_INFO = 33,
+ XR_TYPE_COMPOSITION_LAYER_PROJECTION = 35,
+ XR_TYPE_COMPOSITION_LAYER_QUAD = 36,
+ XR_TYPE_REFERENCE_SPACE_CREATE_INFO = 37,
+ XR_TYPE_ACTION_SPACE_CREATE_INFO = 38,
+ XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING = 40,
+ XR_TYPE_VIEW_CONFIGURATION_VIEW = 41,
+ XR_TYPE_SPACE_LOCATION = 42,
+ XR_TYPE_SPACE_VELOCITY = 43,
+ XR_TYPE_FRAME_STATE = 44,
+ XR_TYPE_VIEW_CONFIGURATION_PROPERTIES = 45,
+ XR_TYPE_FRAME_BEGIN_INFO = 46,
+ XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW = 48,
+ XR_TYPE_EVENT_DATA_EVENTS_LOST = 49,
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING = 51,
+ XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED = 52,
+ XR_TYPE_INTERACTION_PROFILE_STATE = 53,
+ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO = 55,
+ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO = 56,
+ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO = 57,
+ XR_TYPE_ACTION_STATE_GET_INFO = 58,
+ XR_TYPE_HAPTIC_ACTION_INFO = 59,
+ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO = 60,
+ XR_TYPE_ACTIONS_SYNC_INFO = 61,
+ XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO = 62,
+ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO = 63,
+ XR_TYPE_SPACES_LOCATE_INFO = 1000471000,
+ XR_TYPE_SPACE_LOCATIONS = 1000471001,
+ XR_TYPE_SPACE_VELOCITIES = 1000471002,
+ XR_TYPE_COMPOSITION_LAYER_CUBE_KHR = 1000006000,
+ XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR = 1000008000,
+ XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR = 1000010000,
+ XR_TYPE_VULKAN_SWAPCHAIN_FORMAT_LIST_CREATE_INFO_KHR = 1000014000,
+ XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT = 1000015000,
+ XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR = 1000017000,
+ XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR = 1000018000,
+ XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000019000,
+ XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000019001,
+ XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000019002,
+ XR_TYPE_DEBUG_UTILS_LABEL_EXT = 1000019003,
+ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR = 1000023000,
+ XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR = 1000023001,
+ XR_TYPE_GRAPHICS_BINDING_OPENGL_XCB_KHR = 1000023002,
+ XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR = 1000023003,
+ XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR = 1000023004,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR = 1000023005,
+ XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR = 1000024001,
+ XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR = 1000024002,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR = 1000024003,
+ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR = 1000025000,
+ XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR = 1000025001,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR = 1000025002,
+ XR_TYPE_GRAPHICS_BINDING_D3D11_KHR = 1000027000,
+ XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR = 1000027001,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR = 1000027002,
+ XR_TYPE_GRAPHICS_BINDING_D3D12_KHR = 1000028000,
+ XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR = 1000028001,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR = 1000028002,
+ XR_TYPE_GRAPHICS_BINDING_METAL_KHR = 1000029000,
+ XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR = 1000029001,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR = 1000029002,
+ XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT = 1000030000,
+ XR_TYPE_EYE_GAZE_SAMPLE_TIME_EXT = 1000030001,
+ XR_TYPE_VISIBILITY_MASK_KHR = 1000031000,
+ XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR = 1000031001,
+ XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX = 1000033000,
+ XR_TYPE_EVENT_DATA_MAIN_SESSION_VISIBILITY_CHANGED_EXTX = 1000033003,
+ XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR = 1000034000,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT = 1000039000,
+ XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT = 1000039001,
+ XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB = 1000040000,
+ XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB = 1000041001,
+ XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT = 1000046000,
+ XR_TYPE_GRAPHICS_BINDING_EGL_MNDX = 1000048004,
+ XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT = 1000049000,
+ XR_TYPE_SPATIAL_GRAPH_STATIC_NODE_BINDING_CREATE_INFO_MSFT = 1000049001,
+ XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_GET_INFO_MSFT = 1000049002,
+ XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_MSFT = 1000049003,
+ XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT = 1000051000,
+ XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT = 1000051001,
+ XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT = 1000051002,
+ XR_TYPE_HAND_JOINT_LOCATIONS_EXT = 1000051003,
+ XR_TYPE_HAND_JOINT_VELOCITIES_EXT = 1000051004,
+ XR_TYPE_SYSTEM_HAND_TRACKING_MESH_PROPERTIES_MSFT = 1000052000,
+ XR_TYPE_HAND_MESH_SPACE_CREATE_INFO_MSFT = 1000052001,
+ XR_TYPE_HAND_MESH_UPDATE_INFO_MSFT = 1000052002,
+ XR_TYPE_HAND_MESH_MSFT = 1000052003,
+ XR_TYPE_HAND_POSE_TYPE_INFO_MSFT = 1000052004,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SESSION_BEGIN_INFO_MSFT = 1000053000,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_STATE_MSFT = 1000053001,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_STATE_MSFT = 1000053002,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT = 1000053003,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT = 1000053004,
+ XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SWAPCHAIN_CREATE_INFO_MSFT = 1000053005,
+ XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT = 1000055000,
+ XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT = 1000055001,
+ XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT = 1000055002,
+ XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT = 1000055003,
+ XR_TYPE_CONTROLLER_MODEL_STATE_MSFT = 1000055004,
+ XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC = 1000059000,
+ XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT = 1000063000,
+ XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT = 1000066000,
+ XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT = 1000066001,
+ XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB = 1000070000,
+ XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB = 1000072000,
+ XR_TYPE_BODY_TRACKER_CREATE_INFO_FB = 1000076001,
+ XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB = 1000076002,
+ XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB = 1000076004,
+ XR_TYPE_BODY_JOINT_LOCATIONS_FB = 1000076005,
+ XR_TYPE_BODY_SKELETON_FB = 1000076006,
+ XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT = 1000078000,
+ XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE = 1000079000,
+ XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT = 1000080000,
+ XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR = 1000089000,
+ XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR = 1000090000,
+ XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR = 1000090001,
+ XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR = 1000090003,
+ XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR = 1000091000,
+ XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT = 1000097000,
+ XR_TYPE_SCENE_CREATE_INFO_MSFT = 1000097001,
+ XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT = 1000097002,
+ XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT = 1000097003,
+ XR_TYPE_SCENE_COMPONENTS_MSFT = 1000097004,
+ XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT = 1000097005,
+ XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT = 1000097006,
+ XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT = 1000097007,
+ XR_TYPE_SCENE_OBJECTS_MSFT = 1000097008,
+ XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT = 1000097009,
+ XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT = 1000097010,
+ XR_TYPE_SCENE_PLANES_MSFT = 1000097011,
+ XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT = 1000097012,
+ XR_TYPE_SCENE_MESHES_MSFT = 1000097013,
+ XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT = 1000097014,
+ XR_TYPE_SCENE_MESH_BUFFERS_MSFT = 1000097015,
+ XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT = 1000097016,
+ XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT = 1000097017,
+ XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT = 1000097018,
+ XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT = 1000098000,
+ XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT = 1000098001,
+ XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB = 1000101000,
+ XR_TYPE_VIVE_TRACKER_PATHS_HTCX = 1000103000,
+ XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX = 1000103001,
+ XR_TYPE_SYSTEM_FACIAL_TRACKING_PROPERTIES_HTC = 1000104000,
+ XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC = 1000104001,
+ XR_TYPE_FACIAL_EXPRESSIONS_HTC = 1000104002,
+ XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB = 1000108000,
+ XR_TYPE_HAND_TRACKING_MESH_FB = 1000110001,
+ XR_TYPE_HAND_TRACKING_SCALE_FB = 1000110003,
+ XR_TYPE_HAND_TRACKING_AIM_STATE_FB = 1000111001,
+ XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB = 1000112000,
+ XR_TYPE_SYSTEM_SPATIAL_ENTITY_PROPERTIES_FB = 1000113004,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_FB = 1000113003,
+ XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB = 1000113007,
+ XR_TYPE_SPACE_COMPONENT_STATUS_FB = 1000113001,
+ XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB = 1000113005,
+ XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB = 1000113006,
+ XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB = 1000114000,
+ XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB = 1000114001,
+ XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB = 1000114002,
+ XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB = 1000115000,
+ XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB = 1000116009,
+ XR_TYPE_KEYBOARD_TRACKING_QUERY_FB = 1000116004,
+ XR_TYPE_SYSTEM_KEYBOARD_TRACKING_PROPERTIES_FB = 1000116002,
+ XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB = 1000117001,
+ XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB = 1000118000,
+ XR_TYPE_PASSTHROUGH_CREATE_INFO_FB = 1000118001,
+ XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB = 1000118002,
+ XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB = 1000118003,
+ XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB = 1000118004,
+ XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB = 1000118005,
+ XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB = 1000118006,
+ XR_TYPE_PASSTHROUGH_STYLE_FB = 1000118020,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB = 1000118021,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB = 1000118022,
+ XR_TYPE_PASSTHROUGH_BRIGHTNESS_CONTRAST_SATURATION_FB = 1000118023,
+ XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB = 1000118030,
+ XR_TYPE_RENDER_MODEL_PATH_INFO_FB = 1000119000,
+ XR_TYPE_RENDER_MODEL_PROPERTIES_FB = 1000119001,
+ XR_TYPE_RENDER_MODEL_BUFFER_FB = 1000119002,
+ XR_TYPE_RENDER_MODEL_LOAD_INFO_FB = 1000119003,
+ XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB = 1000119004,
+ XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB = 1000119005,
+ XR_TYPE_BINDING_MODIFICATIONS_KHR = 1000120000,
+ XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO = 1000121000,
+ XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO = 1000121001,
+ XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO = 1000121002,
+ XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO = 1000122000,
+ XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO = 1000124000,
+ XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO = 1000124001,
+ XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO = 1000124002,
+ XR_TYPE_FRAME_END_INFO_ML = 1000135000,
+ XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML = 1000136000,
+ XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML = 1000137000,
+ XR_TYPE_SYSTEM_MARKER_UNDERSTANDING_PROPERTIES_ML = 1000138000,
+ XR_TYPE_MARKER_DETECTOR_CREATE_INFO_ML = 1000138001,
+ XR_TYPE_MARKER_DETECTOR_ARUCO_INFO_ML = 1000138002,
+ XR_TYPE_MARKER_DETECTOR_SIZE_INFO_ML = 1000138003,
+ XR_TYPE_MARKER_DETECTOR_APRIL_TAG_INFO_ML = 1000138004,
+ XR_TYPE_MARKER_DETECTOR_CUSTOM_PROFILE_INFO_ML = 1000138005,
+ XR_TYPE_MARKER_DETECTOR_SNAPSHOT_INFO_ML = 1000138006,
+ XR_TYPE_MARKER_DETECTOR_STATE_ML = 1000138007,
+ XR_TYPE_MARKER_SPACE_CREATE_INFO_ML = 1000138008,
+ XR_TYPE_LOCALIZATION_MAP_ML = 1000139000,
+ XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML = 1000139001,
+ XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML = 1000139002,
+ XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML = 1000139003,
+ XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML = 1000139004,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML = 1000140000,
+ XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML = 1000140001,
+ XR_TYPE_SPATIAL_ANCHOR_STATE_ML = 1000140002,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_STORAGE_INFO_ML = 1000141000,
+ XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML = 1000141001,
+ XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML = 1000141002,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML = 1000141003,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_INFO_ML = 1000141004,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML = 1000141005,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_INFO_ML = 1000141006,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML = 1000141007,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_INFO_ML = 1000141008,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML = 1000141009,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_DETAILS_ML = 1000141010,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_DETAILS_ML = 1000141011,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_DETAILS_ML = 1000141012,
+ XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML = 1000472000,
+ XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML = 1000472001,
+ XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML = 1000472002,
+ XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT = 1000142000,
+ XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT = 1000142001,
+ XR_TYPE_SCENE_MARKERS_MSFT = 1000147000,
+ XR_TYPE_SCENE_MARKER_TYPE_FILTER_MSFT = 1000147001,
+ XR_TYPE_SCENE_MARKER_QR_CODES_MSFT = 1000147002,
+ XR_TYPE_SPACE_QUERY_INFO_FB = 1000156001,
+ XR_TYPE_SPACE_QUERY_RESULTS_FB = 1000156002,
+ XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB = 1000156003,
+ XR_TYPE_SPACE_UUID_FILTER_INFO_FB = 1000156054,
+ XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB = 1000156052,
+ XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB = 1000156103,
+ XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB = 1000156104,
+ XR_TYPE_SPACE_SAVE_INFO_FB = 1000158000,
+ XR_TYPE_SPACE_ERASE_INFO_FB = 1000158001,
+ XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB = 1000158106,
+ XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB = 1000158107,
+ XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB = 1000160000,
+ XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB = 1000161000,
+ XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB = 1000162000,
+ XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB = 1000163000,
+ XR_TYPE_SPACE_SHARE_INFO_FB = 1000169001,
+ XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB = 1000169002,
+ XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB = 1000171000,
+ XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB = 1000171001,
+ XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB = 1000173001,
+ XR_TYPE_SEMANTIC_LABELS_FB = 1000175000,
+ XR_TYPE_ROOM_LAYOUT_FB = 1000175001,
+ XR_TYPE_BOUNDARY_2D_FB = 1000175002,
+ XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB = 1000175010,
+ XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE = 1000196000,
+ XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB = 1000198001,
+ XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB = 1000198050,
+ XR_TYPE_SPACE_CONTAINER_FB = 1000199000,
+ XR_TYPE_FOVEATION_EYE_TRACKED_PROFILE_CREATE_INFO_META = 1000200000,
+ XR_TYPE_FOVEATION_EYE_TRACKED_STATE_META = 1000200001,
+ XR_TYPE_SYSTEM_FOVEATION_EYE_TRACKED_PROPERTIES_META = 1000200002,
+ XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB = 1000201004,
+ XR_TYPE_FACE_TRACKER_CREATE_INFO_FB = 1000201005,
+ XR_TYPE_FACE_EXPRESSION_INFO_FB = 1000201002,
+ XR_TYPE_FACE_EXPRESSION_WEIGHTS_FB = 1000201006,
+ XR_TYPE_EYE_TRACKER_CREATE_INFO_FB = 1000202001,
+ XR_TYPE_EYE_GAZES_INFO_FB = 1000202002,
+ XR_TYPE_EYE_GAZES_FB = 1000202003,
+ XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB = 1000202004,
+ XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB = 1000203002,
+ XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB = 1000204000,
+ XR_TYPE_HAPTIC_PCM_VIBRATION_FB = 1000209001,
+ XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB = 1000209002,
+ XR_TYPE_FRAME_SYNTHESIS_INFO_EXT = 1000211000,
+ XR_TYPE_FRAME_SYNTHESIS_CONFIG_VIEW_EXT = 1000211001,
+ XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB = 1000212000,
+ XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META = 1000216000,
+ XR_TYPE_PASSTHROUGH_PREFERENCES_META = 1000217000,
+ XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META = 1000219001,
+ XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META = 1000219002,
+ XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META = 1000219003,
+ XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META = 1000219004,
+ XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META = 1000219005,
+ XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META = 1000219006,
+ XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META = 1000219007,
+ XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META = 1000219009,
+ XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META = 1000219010,
+ XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META = 1000219011,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META = 1000219014,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META = 1000219015,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META = 1000219016,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META = 1000219017,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META = 1000219018,
+ XR_TYPE_EXTERNAL_CAMERA_OCULUS = 1000226000,
+ XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META = 1000227000,
+ XR_TYPE_PERFORMANCE_METRICS_STATE_META = 1000232001,
+ XR_TYPE_PERFORMANCE_METRICS_COUNTER_META = 1000232002,
+ XR_TYPE_SPACE_LIST_SAVE_INFO_FB = 1000238000,
+ XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB = 1000238001,
+ XR_TYPE_SPACE_USER_CREATE_INFO_FB = 1000241001,
+ XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META = 1000245000,
+ XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META = 1000247000,
+ XR_TYPE_SPACE_DISCOVERY_INFO_META = 1000247001,
+ XR_TYPE_SPACE_FILTER_UUID_META = 1000247003,
+ XR_TYPE_SPACE_FILTER_COMPONENT_META = 1000247004,
+ XR_TYPE_SPACE_DISCOVERY_RESULT_META = 1000247005,
+ XR_TYPE_SPACE_DISCOVERY_RESULTS_META = 1000247006,
+ XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META = 1000247007,
+ XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META = 1000247008,
+ XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META = 1000254000,
+ XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META = 1000254001,
+ XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META = 1000259000,
+ XR_TYPE_SPACES_SAVE_INFO_META = 1000259001,
+ XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META = 1000259002,
+ XR_TYPE_SPACES_ERASE_INFO_META = 1000259003,
+ XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META = 1000259004,
+ XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META = 1000266000,
+ XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META = 1000266001,
+ XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META = 1000266002,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META = 1000266100,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META = 1000266101,
+ XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META = 1000269001,
+ XR_TYPE_SPACE_TRIANGLE_MESH_META = 1000269002,
+ XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META = 1000274000,
+ XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META = 1000282000,
+ XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META = 1000283002,
+ XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META = 1000283003,
+ XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META = 1000283004,
+ XR_TYPE_BODY_TRACKING_FIDELITY_STATUS_META = 1000284000,
+ XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FIDELITY_META = 1000284001,
+ XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB = 1000287013,
+ XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB = 1000287014,
+ XR_TYPE_FACE_EXPRESSION_INFO2_FB = 1000287015,
+ XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB = 1000287016,
+ XR_TYPE_SYSTEM_SPATIAL_ENTITY_SHARING_PROPERTIES_META = 1000290000,
+ XR_TYPE_SHARE_SPACES_INFO_META = 1000290001,
+ XR_TYPE_EVENT_DATA_SHARE_SPACES_COMPLETE_META = 1000290002,
+ XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META = 1000291000,
+ XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META = 1000291001,
+ XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META = 1000291002,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META = 1000291003,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META = 1000291004,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META = 1000291005,
+ XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META = 1000291006,
+ XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META = 1000291007,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_TIMESTAMP_META = 1000291008,
+ XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT = 1000300000,
+ XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT = 1000300001,
+ XR_TYPE_RENDER_MODEL_PROPERTIES_EXT = 1000300002,
+ XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT = 1000300003,
+ XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT = 1000300004,
+ XR_TYPE_RENDER_MODEL_STATE_EXT = 1000300005,
+ XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT = 1000300006,
+ XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT = 1000300007,
+ XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT = 1000300008,
+ XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT = 1000300009,
+ XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT = 1000300010,
+ XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT = 1000301000,
+ XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT = 1000301001,
+ XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT = 1000301002,
+ XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT = 1000301003,
+ XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001,
+ XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002,
+ XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003,
+ XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC = 1000317004,
+ XR_TYPE_FOVEATION_APPLY_INFO_HTC = 1000318000,
+ XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC = 1000318001,
+ XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC = 1000318002,
+ XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC = 1000319000,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC = 1000319001,
+ XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_HTC = 1000320000,
+ XR_TYPE_BODY_TRACKER_CREATE_INFO_HTC = 1000320001,
+ XR_TYPE_BODY_JOINTS_LOCATE_INFO_HTC = 1000320002,
+ XR_TYPE_BODY_JOINT_LOCATIONS_HTC = 1000320003,
+ XR_TYPE_BODY_SKELETON_HTC = 1000320004,
+ XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT = 1000373000,
+ XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX = 1000375000,
+ XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX = 1000375001,
+ XR_TYPE_BODY_TRACKER_CREATE_INFO_BD = 1000385001,
+ XR_TYPE_BODY_JOINTS_LOCATE_INFO_BD = 1000385002,
+ XR_TYPE_BODY_JOINT_LOCATIONS_BD = 1000385003,
+ XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD = 1000385004,
+ XR_TYPE_SYSTEM_FACIAL_SIMULATION_PROPERTIES_BD = 1000386001,
+ XR_TYPE_FACE_TRACKER_CREATE_INFO_BD = 1000386002,
+ XR_TYPE_FACIAL_SIMULATION_DATA_GET_INFO_BD = 1000386003,
+ XR_TYPE_FACIAL_SIMULATION_DATA_BD = 1000386004,
+ XR_TYPE_LIP_EXPRESSION_DATA_BD = 1000386005,
+ XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD = 1000389000,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD = 1000389001,
+ XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD = 1000389002,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD = 1000389003,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD = 1000389004,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD = 1000389005,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD = 1000389006,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD = 1000389007,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD = 1000389008,
+ XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD = 1000389009,
+ XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD = 1000389010,
+ XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD = 1000389011,
+ XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD = 1000389012,
+ XR_TYPE_SENSE_DATA_QUERY_INFO_BD = 1000389013,
+ XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD = 1000389014,
+ XR_TYPE_SENSE_DATA_FILTER_UUID_BD = 1000389015,
+ XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD = 1000389016,
+ XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD = 1000389017,
+ XR_TYPE_QUERIED_SENSE_DATA_BD = 1000389018,
+ XR_TYPE_SPATIAL_ENTITY_STATE_BD = 1000389019,
+ XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD = 1000389020,
+ XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD = 1000389021,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_BD = 1000389022,
+ XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD = 1000390000,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD = 1000390001,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD = 1000390002,
+ XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD = 1000390003,
+ XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD = 1000390004,
+ XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD = 1000391000,
+ XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD = 1000391001,
+ XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD = 1000391002,
+ XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD = 1000392000,
+ XR_TYPE_SCENE_CAPTURE_INFO_BD = 1000392001,
+ XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD = 1000393000,
+ XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD = 1000393001,
+ XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD = 1000394001,
+ XR_TYPE_BODY_TRACKING_POSTURE_DATA_BD = 1000395001,
+ XR_TYPE_BODY_JOINT_VELOCITIES_BD = 1000395002,
+ XR_TYPE_BODY_JOINT_ACCELERATIONS_BD = 1000395003,
+ XR_TYPE_BODY_TRACKING_STATE_BD = 1000395004,
+ XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD = 1000396000,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD = 1000396001,
+ XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD = 1000396002,
+ XR_TYPE_SYSTEM_LIGHT_ESTIMATION_PROPERTIES_BD = 1000397000,
+ XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_LIGHT_ESTIMATION_BD = 1000397001,
+ XR_TYPE_ENVIRONMENT_TEXTURE_CREATE_CONFIG_INFO_BD = 1000397002,
+ XR_TYPE_LIGHT_ESTIMATION_DATA_ENVIRONMENT_TEXTURE_RAW_BD = 1000397003,
+ XR_TYPE_LIGHT_ESTIMATION_DATA_SPHERICAL_HARMONICS_BD = 1000397005,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LIGHT_ESTIMATION_BD = 1000397006,
+ XR_TYPE_SPATIAL_AUDIO_RENDERER_CREATE_INFO_BD = 1000409000,
+ XR_TYPE_AUDIO_BUFFER_BD = 1000409001,
+ XR_TYPE_SOUND_OBJECT_DIRECTIVITY_CARDIOID_BD = 1000409003,
+ XR_TYPE_SOUND_OBJECT_SHAPE_SPHERE_BD = 1000409004,
+ XR_TYPE_SOUND_OBJECT_DISTANCE_ATTENUATION_BD = 1000409005,
+ XR_TYPE_SOUND_OBJECT_DISTANCE_ATTENUATION_CURVE_BD = 1000409006,
+ XR_TYPE_SOUND_OBJECT_CONFIG_BD = 1000409007,
+ XR_TYPE_SOUND_FIELD_CONFIG_BD = 1000409008,
+ XR_TYPE_SOUND_FIELD_CHANNEL_DEFINITION_SURROUND_BD = 1000409009,
+ XR_TYPE_SOUND_FIELD_CHANNEL_DEFINITION_AMBIX_BD = 1000409010,
+ XR_TYPE_SOUND_FIELD_CHANNEL_DEFINITION_FUMA_BD = 1000409011,
+ XR_TYPE_SOUND_TRIANGLE_MESH_BD = 1000409012,
+ XR_TYPE_SOUND_OBSTACLE_CONFIG_BD = 1000409013,
+ XR_TYPE_SOUND_OBSTACLE_MATERIAL_CONFIG_BD = 1000409014,
+ XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT = 1000428000,
+ XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT = 1000428001,
+ XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT = 1000429001,
+ XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT = 1000429002,
+ XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT = 1000429003,
+ XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT = 1000429004,
+ XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005,
+ XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006,
+ XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007,
+ XR_TYPE_TRACKABLE_GET_INFO_ANDROID = 1000455000,
+ XR_TYPE_ANCHOR_SPACE_CREATE_INFO_ANDROID = 1000455001,
+ XR_TYPE_TRACKABLE_PLANE_ANDROID = 1000455003,
+ XR_TYPE_TRACKABLE_TRACKER_CREATE_INFO_ANDROID = 1000455004,
+ XR_TYPE_SYSTEM_TRACKABLES_PROPERTIES_ANDROID = 1000455005,
+ XR_TYPE_EYES_ANDROID = 1000456000,
+ XR_TYPE_EYE_TRACKER_CREATE_INFO_ANDROID = 1000456001,
+ XR_TYPE_EYES_GET_INFO_ANDROID = 1000456002,
+ XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_ANDROID = 1000456004,
+ XR_TYPE_PERSISTED_ANCHOR_SPACE_CREATE_INFO_ANDROID = 1000457001,
+ XR_TYPE_PERSISTED_ANCHOR_SPACE_INFO_ANDROID = 1000457002,
+ XR_TYPE_DEVICE_ANCHOR_PERSISTENCE_CREATE_INFO_ANDROID = 1000457003,
+ XR_TYPE_SYSTEM_DEVICE_ANCHOR_PERSISTENCE_PROPERTIES_ANDROID = 1000457004,
+ XR_TYPE_FACE_TRACKER_CREATE_INFO_ANDROID = 1000458000,
+ XR_TYPE_FACE_STATE_GET_INFO_ANDROID = 1000458001,
+ XR_TYPE_FACE_STATE_ANDROID = 1000458002,
+ XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_ANDROID = 1000458003,
+ XR_TYPE_PASSTHROUGH_CAMERA_STATE_GET_INFO_ANDROID = 1000460000,
+ XR_TYPE_SYSTEM_PASSTHROUGH_CAMERA_STATE_PROPERTIES_ANDROID = 1000460001,
+ XR_TYPE_EVENT_DATA_RECOMMENDED_RESOLUTION_CHANGED_ANDROID = 1000461000,
+ XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_ANDROID = 1000462000,
+ XR_TYPE_PASSTHROUGH_LAYER_MESH_ANDROID = 1000462001,
+ XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_ANDROID = 1000462002,
+ XR_TYPE_SYSTEM_PASSTHROUGH_LAYER_PROPERTIES_ANDROID = 1000462003,
+ XR_TYPE_RAYCAST_INFO_ANDROID = 1000463000,
+ XR_TYPE_RAYCAST_HIT_RESULTS_ANDROID = 1000463001,
+ XR_TYPE_PERFORMANCE_METRICS_STATE_ANDROID = 1000465000,
+ XR_TYPE_PERFORMANCE_METRICS_COUNTER_ANDROID = 1000465001,
+ XR_TYPE_TRACKABLE_OBJECT_ANDROID = 1000466000,
+ XR_TYPE_TRACKABLE_OBJECT_CONFIGURATION_ANDROID = 1000466001,
+ XR_TYPE_FUTURE_CANCEL_INFO_EXT = 1000469000,
+ XR_TYPE_FUTURE_POLL_INFO_EXT = 1000469001,
+ XR_TYPE_FUTURE_COMPLETION_EXT = 1000469002,
+ XR_TYPE_FUTURE_POLL_RESULT_EXT = 1000469003,
+ XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT = 1000470000,
+ XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT = 1000470001,
+ XR_TYPE_SYSTEM_NOTIFICATIONS_SET_INFO_ML = 1000473000,
+ XR_TYPE_WORLD_MESH_DETECTOR_CREATE_INFO_ML = 1000474001,
+ XR_TYPE_WORLD_MESH_STATE_REQUEST_INFO_ML = 1000474002,
+ XR_TYPE_WORLD_MESH_BLOCK_STATE_ML = 1000474003,
+ XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML = 1000474004,
+ XR_TYPE_WORLD_MESH_BUFFER_RECOMMENDED_SIZE_INFO_ML = 1000474005,
+ XR_TYPE_WORLD_MESH_BUFFER_SIZE_ML = 1000474006,
+ XR_TYPE_WORLD_MESH_BUFFER_ML = 1000474007,
+ XR_TYPE_WORLD_MESH_BLOCK_REQUEST_ML = 1000474008,
+ XR_TYPE_WORLD_MESH_GET_INFO_ML = 1000474009,
+ XR_TYPE_WORLD_MESH_BLOCK_ML = 1000474010,
+ XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML = 1000474011,
+ XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_INFO_ML = 1000474012,
+ XR_TYPE_SYSTEM_FACIAL_EXPRESSION_PROPERTIES_ML = 1000482004,
+ XR_TYPE_FACIAL_EXPRESSION_CLIENT_CREATE_INFO_ML = 1000482005,
+ XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_GET_INFO_ML = 1000482006,
+ XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML = 1000482007,
+ XR_TYPE_SYSTEM_BOUNDARY_VISIBILITY_PROPERTIES_META = 1000528000,
+ XR_TYPE_EVENT_DATA_BOUNDARY_VISIBILITY_CHANGED_META = 1000528001,
+ XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META = 1000532001,
+ XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META = 1000532002,
+ XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META = 1000532003,
+ XR_TYPE_FACE_TRACKING_VISEMES_META = 1000541000,
+ XR_TYPE_SYSTEM_FACE_TRACKING_VISEMES_PROPERTIES_META = 1000541001,
+ XR_TYPE_ROOM_MESH_FACE_INDICES_META = 1000553000,
+ XR_TYPE_SPACE_ROOM_MESH_GET_INFO_META = 1000553001,
+ XR_TYPE_ROOM_MESH_META = 1000553002,
+ XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META = 1000571010,
+ XR_TYPE_COLOCATION_DISCOVERY_STOP_INFO_META = 1000571011,
+ XR_TYPE_COLOCATION_ADVERTISEMENT_START_INFO_META = 1000571012,
+ XR_TYPE_COLOCATION_ADVERTISEMENT_STOP_INFO_META = 1000571013,
+ XR_TYPE_EVENT_DATA_START_COLOCATION_ADVERTISEMENT_COMPLETE_META = 1000571020,
+ XR_TYPE_EVENT_DATA_STOP_COLOCATION_ADVERTISEMENT_COMPLETE_META = 1000571021,
+ XR_TYPE_EVENT_DATA_COLOCATION_ADVERTISEMENT_COMPLETE_META = 1000571022,
+ XR_TYPE_EVENT_DATA_START_COLOCATION_DISCOVERY_COMPLETE_META = 1000571023,
+ XR_TYPE_EVENT_DATA_COLOCATION_DISCOVERY_RESULT_META = 1000571024,
+ XR_TYPE_EVENT_DATA_COLOCATION_DISCOVERY_COMPLETE_META = 1000571025,
+ XR_TYPE_EVENT_DATA_STOP_COLOCATION_DISCOVERY_COMPLETE_META = 1000571026,
+ XR_TYPE_SYSTEM_COLOCATION_DISCOVERY_PROPERTIES_META = 1000571030,
+ XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META = 1000572000,
+ XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META = 1000572001,
+ XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META = 1000572100,
+ XR_TYPE_SYSTEM_ENVIRONMENT_RAYCAST_PROPERTIES_META = 1000592000,
+ XR_TYPE_ENVIRONMENT_RAYCASTER_CREATE_INFO_META = 1000592001,
+ XR_TYPE_ENVIRONMENT_RAYCASTER_CREATE_COMPLETION_META = 1000592002,
+ XR_TYPE_ENVIRONMENT_RAYCAST_HIT_GET_INFO_META = 1000592003,
+ XR_TYPE_ENVIRONMENT_RAYCAST_HIT_META = 1000592004,
+ XR_TYPE_ENVIRONMENT_RAYCAST_FILTER_DISTANCE_META = 1000592005,
+ XR_TYPE_TILE_PROPERTIES_META = 1000609000,
+ XR_TYPE_TILE_PROPERTIES_HINT_META = 1000609001,
+ XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_REQUEST_META = 1000693000,
+ XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_META = 1000693001,
+ XR_TYPE_LIGHT_ESTIMATOR_CREATE_INFO_ANDROID = 1000700000,
+ XR_TYPE_LIGHT_ESTIMATE_GET_INFO_ANDROID = 1000700001,
+ XR_TYPE_LIGHT_ESTIMATE_ANDROID = 1000700002,
+ XR_TYPE_DIRECTIONAL_LIGHT_ANDROID = 1000700003,
+ XR_TYPE_SPHERICAL_HARMONICS_ANDROID = 1000700004,
+ XR_TYPE_AMBIENT_LIGHT_ANDROID = 1000700005,
+ XR_TYPE_SYSTEM_LIGHT_ESTIMATION_PROPERTIES_ANDROID = 1000700006,
+ XR_TYPE_ANCHOR_SHARING_INFO_ANDROID = 1000701000,
+ XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID = 1000701001,
+ XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID = 1000701002,
+ XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_ANDROID = 1000707000,
+ XR_TYPE_TRACKABLE_MARKER_CONFIGURATION_ANDROID = 1000707001,
+ XR_TYPE_TRACKABLE_MARKER_ANDROID = 1000707002,
+ XR_TYPE_SYSTEM_QR_CODE_TRACKING_PROPERTIES_ANDROID = 1000708000,
+ XR_TYPE_TRACKABLE_QR_CODE_CONFIGURATION_ANDROID = 1000708001,
+ XR_TYPE_TRACKABLE_QR_CODE_ANDROID = 1000708002,
+ XR_TYPE_SYSTEM_IMAGE_TRACKING_PROPERTIES_ANDROID = 1000709000,
+ XR_TYPE_TRACKABLE_IMAGE_DATABASE_ENTRY_ANDROID = 1000709001,
+ XR_TYPE_TRACKABLE_IMAGE_DATABASE_CREATE_INFO_ANDROID = 1000709002,
+ XR_TYPE_CREATE_TRACKABLE_IMAGE_DATABASE_COMPLETION_ANDROID = 1000709003,
+ XR_TYPE_TRACKABLE_IMAGE_CONFIGURATION_ANDROID = 1000709004,
+ XR_TYPE_TRACKABLE_IMAGE_ANDROID = 1000709005,
+ XR_TYPE_EVENT_DATA_IMAGE_TRACKING_LOST_ANDROID = 1000709006,
+ XR_TYPE_SYSTEM_SCENE_MESHING_PROPERTIES_ANDROID = 1000718000,
+ XR_TYPE_SCENE_MESHING_TRACKER_CREATE_INFO_ANDROID = 1000718001,
+ XR_TYPE_SCENE_MESH_SNAPSHOT_CREATE_INFO_ANDROID = 1000718002,
+ XR_TYPE_SCENE_MESH_SNAPSHOT_CREATION_RESULT_ANDROID = 1000718003,
+ XR_TYPE_SCENE_SUBMESH_STATE_ANDROID = 1000718004,
+ XR_TYPE_SCENE_SUBMESH_DATA_ANDROID = 1000718005,
+ XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT = 1000740000,
+ XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT = 1000740001,
+ XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT = 1000740002,
+ XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT = 1000740003,
+ XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT = 1000740004,
+ XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT = 1000740005,
+ XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT = 1000740006,
+ XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT = 1000740007,
+ XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT = 1000740008,
+ XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT = 1000740009,
+ XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT = 1000740010,
+ XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT = 1000740011,
+ XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT = 1000740012,
+ XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT = 1000740013,
+ XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT = 1000740014,
+ XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT = 1000740015,
+ XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT = 1000740016,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT = 1000741000,
+ XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT = 1000741001,
+ XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT = 1000741002,
+ XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT = 1000741003,
+ XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT = 1000741004,
+ XR_TYPE_STATIONARY_REFERENCE_SPACE_GENERATION_ID_GET_INFO_EXT = 1000742001,
+ XR_TYPE_STATIONARY_REFERENCE_SPACE_GENERATION_ID_RESULT_EXT = 1000742002,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT = 1000743000,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT = 1000743001,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT = 1000743002,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT = 1000743003,
+ XR_TYPE_SPATIAL_MARKER_SIZE_EXT = 1000743004,
+ XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT = 1000743005,
+ XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT = 1000743006,
+ XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_BD = 1000746000,
+ XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_BD = 1000746001,
+ XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_BD = 1000746002,
+ XR_TYPE_DYNAMIC_OBJECT_DATA_BD = 1000746003,
+ XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_BD = 1000746004,
+ XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_BD = 1000747000,
+ XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_BD = 1000748000,
+ XR_TYPE_AVAILABLE_CAMERAS_ENUMERATE_INFO_BD = 1000755000,
+ XR_TYPE_CAMERA_PROPERTIES_GET_INFO_BD = 1000755001,
+ XR_TYPE_CAMERA_PROPERTIES_BD = 1000755002,
+ XR_TYPE_CAMERA_PROPERTY_FACING_BD = 1000755003,
+ XR_TYPE_CAMERA_PROPERTY_POSITION_BD = 1000755004,
+ XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_BD = 1000755005,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_GET_INFO_BD = 1000755006,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_BD = 1000755007,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_AND_FRAME_RATE_BD = 1000755008,
+ XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_AND_FRAME_RATE_BD = 1000755009,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_BD = 1000755010,
+ XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_BD = 1000755011,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_BD = 1000755012,
+ XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_BD = 1000755013,
+ XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_BD = 1000755014,
+ XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_BD = 1000755015,
+ XR_TYPE_CAMERA_DEVICE_CREATE_INFO_BD = 1000755016,
+ XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_BD = 1000755017,
+ XR_TYPE_CAMERA_CAPTURE_SESSION_CREATE_INFO_BD = 1000755018,
+ XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_BD = 1000755019,
+ XR_TYPE_CAMERA_INTRINSICS_BD = 1000755020,
+ XR_TYPE_CAMERA_EXTRINSICS_BD = 1000755021,
+ XR_TYPE_CAMERA_CAPTURE_BEGIN_INFO_BD = 1000755022,
+ XR_TYPE_CAMERA_IMAGE_ACQUIRE_INFO_BD = 1000755023,
+ XR_TYPE_CAMERA_IMAGE_BD = 1000755024,
+ XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_BD = 1000755025,
+ XR_TYPE_CAMERA_CAPABILITIES_BD = 1000755026,
+ XR_TYPE_CAMERA_CAPABILITY_TYPES_ENUMERATE_INFO_BD = 1000755027,
+ XR_TYPE_CAMERA_CAPABILITY_TYPES_BD = 1000755028,
+ XR_TYPE_CAMERA_PROPERTY_TYPES_ENUMERATE_INFO_BD = 1000755029,
+ XR_TYPE_CAMERA_PROPERTY_TYPES_BD = 1000755030,
+ XR_TYPE_AVAILABLE_CAMERA_BD = 1000755031,
+ XR_TYPE_SPATIAL_BOUNDS_SPHEREF_ANDROID = 1000761000,
+ XR_TYPE_SPATIAL_BOUNDS_BOXF_ANDROID = 1000761001,
+ XR_TYPE_SPATIAL_BOUNDS_FRUSTUMF_ANDROID = 1000761002,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT = 1000762000,
+ XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT = 1000762001,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT = 1000762002,
+ XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT = 1000763000,
+ XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT = 1000763001,
+ XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT = 1000763002,
+ XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT = 1000763003,
+ XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT = 1000763004,
+ XR_TYPE_HAPTIC_PARAMETRIC_VIBRATION_EXT = 1000775000,
+ XR_TYPE_HAPTIC_PARAMETRIC_PROPERTIES_EXT = 1000775001,
+ XR_TYPE_SYSTEM_HAPTIC_PARAMETRIC_PROPERTIES_EXT = 1000775002,
+ XR_TYPE_COLOR_SPACES_ENUMERATE_INFO_SONY = 1000776000,
+ XR_TYPE_SWAPCHAIN_CREATE_INFO_COLOR_SPACE_SONY = 1000776001,
+ XR_TYPE_HDR_METADATA_SONY = 1000777000,
+ XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT = 1000781000,
+ XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781001,
+ XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT = 1000781002,
+ XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781003,
+ XR_TYPE_SPATIAL_REFERENCE_IMAGE_EXT = 1000782000,
+ XR_TYPE_SPATIAL_IMAGE_STATIC_OPTIMIZATION_EXT = 1000782001,
+ XR_TYPE_SPATIAL_IMAGE_SIZE_EXT = 1000782002,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_IMAGE_TRACKING_EXT = 1000782003,
+ XR_TYPE_SPATIAL_IMAGE_TRACKING_DATABASE_CREATE_INFO_EXT = 1000782004,
+ XR_TYPE_SPATIAL_COMPONENT_IMAGE_2D_LIST_EXT = 1000782005,
+ XR_TYPE_CREATE_SPATIAL_IMAGE_TRACKING_DATABASE_COMPLETION_EXT = 1000782006,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_OBJECT_TRACKING_ANDROID = 1000785000,
+ XR_TYPE_SPATIAL_COMPONENT_OBJECT_SEMANTIC_LABEL_LIST_ANDROID = 1000785001,
+ XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_DEPTH_RAYCAST_ANDROID = 1000786000,
+ XR_TYPE_SPATIAL_RAYCAST_INFO_ANDROID = 1000786001,
+ XR_TYPE_SPATIAL_COMPONENT_RAYCAST_RESULT_LIST_ANDROID = 1000786002,
+ XR_TYPE_SPATIAL_RAYCAST_SNAPSHOT_CREATE_INFO_ANDROID = 1000786003,
+ XR_TYPE_GOOGLE_CLOUD_AUTH_INFO_API_KEY_ANDROID = 1000787000,
+ XR_TYPE_GOOGLE_CLOUD_AUTH_INFO_TOKEN_ANDROID = 1000787001,
+ XR_TYPE_GOOGLE_CLOUD_AUTH_INFO_KEYLESS_ANDROID = 1000787002,
+ XR_TYPE_GOOGLE_CLOUD_AUTH_ERROR_RESULT_ANDROID = 1000787003,
+ XR_TYPE_SYSTEM_GEOSPATIAL_PROPERTIES_ANDROID = 1000789000,
+ XR_TYPE_GEOSPATIAL_TRACKER_CREATE_INFO_ANDROID = 1000789001,
+ XR_TYPE_EVENT_DATA_GEOSPATIAL_TRACKER_STATE_CHANGED_ANDROID = 1000789002,
+ XR_TYPE_GEOSPATIAL_POSE_FROM_POSE_LOCATE_INFO_ANDROID = 1000789003,
+ XR_TYPE_GEOSPATIAL_POSE_RESULT_ANDROID = 1000789004,
+ XR_TYPE_GEOSPATIAL_POSE_LOCATE_INFO_ANDROID = 1000789005,
+ XR_TYPE_VPS_AVAILABILITY_CHECK_COMPLETION_ANDROID = 1000789006,
+ XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID = 1000790000,
+ XR_TYPE_SPATIAL_DISCOVERY_UNIQUE_ENTITIES_FILTER_ANDROID = 1000791001,
+ XR_TYPE_SPATIAL_COMPONENT_SUBSUMED_BY_LIST_ANDROID = 1000791002,
+ XR_TYPE_SPATIAL_ANCHOR_SPACE_FROM_ID_CREATE_INFO_ANDROID = 1000795000,
+ XR_TYPE_GEOSPATIAL_ANCHOR_CREATE_INFO_ANDROID = 1000797000,
+ XR_TYPE_SURFACE_ANCHOR_CREATE_INFO_ANDROID = 1000797001,
+ XR_TYPE_SURFACE_ANCHOR_CREATE_COMPLETION_ANDROID = 1000797002,
+ XR_TYPE_SYSTEM_GEOSPATIAL_ANCHOR_PROPERTIES_ANDROID = 1000797003,
+ XR_TYPE_GEOSPATIAL_TRACKER_ANCHOR_TRACKING_INFO_ANDROID = 1000797004,
+ XR_TYPE_SPATIAL_CONTAINER_CREATE_INFO_EXT = 1000810000,
+ XR_TYPE_SPATIAL_CONTAINER_SPACE_CREATE_INFO_EXT = 1000810001,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_CLOSED_EXT = 1000810003,
+ XR_TYPE_SYSTEM_SPATIAL_CONTAINER_PROPERTIES_EXT = 1000810004,
+ XR_TYPE_SPATIAL_CONTAINER_BOUNDS_EXT = 1000810005,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_BOUNDS_CHANGED_EXT = 1000810006,
+ XR_TYPE_SPATIAL_CONTAINER_BOUNDS_GET_INFO_EXT = 1000810007,
+ XR_TYPE_SPATIAL_CONTAINER_STATE_GET_INFO_EXT = 1000810008,
+ XR_TYPE_SPATIAL_CONTAINER_VISIBLE_REQUEST_INFO_EXT = 1000810009,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_VISIBLE_CHANGED_EXT = 1000810010,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_VISIBLE_REQUEST_DENIED_EXT = 1000810011,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_INTERACTABLE_CHANGED_EXT = 1000810012,
+ XR_TYPE_SPATIAL_CONTAINER_BOUNDS_MODE_REQUEST_INFO_EXT = 1000810013,
+ XR_TYPE_EVENT_DATA_SPATIAL_CONTAINER_BOUNDS_MODE_REQUEST_DENIED_EXT = 1000810014,
+ XR_TYPE_SPATIAL_CONTAINER_STATE_EXT = 1000810015,
+ XR_TYPE_SESSION_CREATE_INFO_SPATIAL_CONTAINERS_EXT = 1000810016,
+ XR_TYPE_SPATIAL_CONTAINER_VIEW_LOCATE_INFO_EXT = 1000813000,
+ XR_TYPE_SPATIAL_CONTAINER_VIEWS_LOCATE_INFO_EXT = 1000813001,
+ XR_TYPE_SPATIAL_CONTAINER_VIEW_STATE_EXT = 1000813002,
+ XR_TYPE_SPATIAL_CONTAINER_LAYER_EXT = 1000813003,
+ XR_TYPE_SPATIAL_CONTAINER_LAYER_FRAME_END_INFO_EXT = 1000813004,
+ XR_TYPE_SPATIAL_CONTAINER_BEGIN_INFO_EXT = 1000813005,
+ XR_TYPE_SPATIAL_CONTAINER_COMPOSITION_LAYER_VIEW_CONFIGURATION_EXT = 1000813006,
+ XR_TYPE_SPATIAL_CONTAINER_END_INFO_EXT = 1000813007,
+ XR_TYPE_SPATIAL_CONTAINER_LAYER_VOLUME_CLIPPING_EXT = 1000813008,
+ XR_TYPE_BATTERY_STATE_DISPLAY_EXT = 1000836000,
+ XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT = 1000838000,
+ XR_TYPE_EVENT_DATA_VIEW_CONFIGURATION_VIEWS_CHANGED_EXT = 1000839000,
+ XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
+ XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
+ XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB,
+ XR_TYPE_SPACES_LOCATE_INFO_KHR = XR_TYPE_SPACES_LOCATE_INFO,
+ XR_TYPE_SPACE_LOCATIONS_KHR = XR_TYPE_SPACE_LOCATIONS,
+ XR_TYPE_SPACE_VELOCITIES_KHR = XR_TYPE_SPACE_VELOCITIES,
+ XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
+} XrStructureType;
+
+typedef enum XrFormFactor {
+ XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY = 1,
+ XR_FORM_FACTOR_HANDHELD_DISPLAY = 2,
+ XR_FORM_FACTOR_MAX_ENUM = 0x7FFFFFFF
+} XrFormFactor;
+
+typedef enum XrEnvironmentBlendMode {
+ XR_ENVIRONMENT_BLEND_MODE_OPAQUE = 1,
+ XR_ENVIRONMENT_BLEND_MODE_ADDITIVE = 2,
+ XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND = 3,
+ XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM = 0x7FFFFFFF
+} XrEnvironmentBlendMode;
+
+typedef enum XrViewConfigurationType {
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO = 1,
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO = 2,
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET = 1000037000,
+ XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT = 1000054000,
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET,
+ XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM = 0x7FFFFFFF
+} XrViewConfigurationType;
+
+typedef enum XrReferenceSpaceType {
+ XR_REFERENCE_SPACE_TYPE_VIEW = 1,
+ XR_REFERENCE_SPACE_TYPE_LOCAL = 2,
+ XR_REFERENCE_SPACE_TYPE_STAGE = 3,
+ XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR = 1000426000,
+ XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000,
+ XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000,
+ XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML = 1000139000,
+ XR_REFERENCE_SPACE_TYPE_UNBOUNDED_ANDROID = 1000467000,
+ XR_REFERENCE_SPACE_TYPE_STATIONARY_EXT = 1000742000,
+ XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR,
+ XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF
+} XrReferenceSpaceType;
+
+typedef enum XrEyeVisibility {
+ XR_EYE_VISIBILITY_BOTH = 0,
+ XR_EYE_VISIBILITY_LEFT = 1,
+ XR_EYE_VISIBILITY_RIGHT = 2,
+ XR_EYE_VISIBILITY_MAX_ENUM = 0x7FFFFFFF
+} XrEyeVisibility;
+
+typedef enum XrActionType {
+ XR_ACTION_TYPE_BOOLEAN_INPUT = 1,
+ XR_ACTION_TYPE_FLOAT_INPUT = 2,
+ XR_ACTION_TYPE_VECTOR2F_INPUT = 3,
+ XR_ACTION_TYPE_POSE_INPUT = 4,
+ XR_ACTION_TYPE_VIBRATION_OUTPUT = 100,
+ XR_ACTION_TYPE_MAX_ENUM = 0x7FFFFFFF
+} XrActionType;
+
+typedef enum XrSessionState {
+ XR_SESSION_STATE_UNKNOWN = 0,
+ XR_SESSION_STATE_IDLE = 1,
+ XR_SESSION_STATE_READY = 2,
+ XR_SESSION_STATE_SYNCHRONIZED = 3,
+ XR_SESSION_STATE_VISIBLE = 4,
+ XR_SESSION_STATE_FOCUSED = 5,
+ XR_SESSION_STATE_STOPPING = 6,
+ XR_SESSION_STATE_LOSS_PENDING = 7,
+ XR_SESSION_STATE_EXITING = 8,
+ XR_SESSION_STATE_MAX_ENUM = 0x7FFFFFFF
+} XrSessionState;
+
+typedef enum XrObjectType {
+ XR_OBJECT_TYPE_UNKNOWN = 0,
+ XR_OBJECT_TYPE_INSTANCE = 1,
+ XR_OBJECT_TYPE_SESSION = 2,
+ XR_OBJECT_TYPE_SWAPCHAIN = 3,
+ XR_OBJECT_TYPE_SPACE = 4,
+ XR_OBJECT_TYPE_ACTION_SET = 5,
+ XR_OBJECT_TYPE_ACTION = 6,
+ XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000019000,
+ XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT = 1000039000,
+ XR_OBJECT_TYPE_SPATIAL_GRAPH_NODE_BINDING_MSFT = 1000049000,
+ XR_OBJECT_TYPE_HAND_TRACKER_EXT = 1000051000,
+ XR_OBJECT_TYPE_BODY_TRACKER_FB = 1000076000,
+ XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT = 1000097000,
+ XR_OBJECT_TYPE_SCENE_MSFT = 1000097001,
+ XR_OBJECT_TYPE_FACIAL_TRACKER_HTC = 1000104000,
+ XR_OBJECT_TYPE_FOVEATION_PROFILE_FB = 1000114000,
+ XR_OBJECT_TYPE_TRIANGLE_MESH_FB = 1000117000,
+ XR_OBJECT_TYPE_PASSTHROUGH_FB = 1000118000,
+ XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB = 1000118002,
+ XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB = 1000118004,
+ XR_OBJECT_TYPE_MARKER_DETECTOR_ML = 1000138000,
+ XR_OBJECT_TYPE_EXPORTED_LOCALIZATION_MAP_ML = 1000139000,
+ XR_OBJECT_TYPE_SPATIAL_ANCHORS_STORAGE_ML = 1000141000,
+ XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT = 1000142000,
+ XR_OBJECT_TYPE_FACE_TRACKER_FB = 1000201000,
+ XR_OBJECT_TYPE_EYE_TRACKER_FB = 1000202000,
+ XR_OBJECT_TYPE_VIRTUAL_KEYBOARD_META = 1000219000,
+ XR_OBJECT_TYPE_SPACE_USER_FB = 1000241000,
+ XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META = 1000266000,
+ XR_OBJECT_TYPE_FACE_TRACKER2_FB = 1000287012,
+ XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000,
+ XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001,
+ XR_OBJECT_TYPE_RENDER_MODEL_EXT = 1000300000,
+ XR_OBJECT_TYPE_RENDER_MODEL_ASSET_EXT = 1000300001,
+ XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000,
+ XR_OBJECT_TYPE_BODY_TRACKER_HTC = 1000320000,
+ XR_OBJECT_TYPE_BODY_TRACKER_BD = 1000385000,
+ XR_OBJECT_TYPE_FACE_TRACKER_BD = 1000386000,
+ XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_BD = 1000389000,
+ XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_BD = 1000389001,
+ XR_OBJECT_TYPE_ANCHOR_BD = 1000389002,
+ XR_OBJECT_TYPE_SPATIAL_AUDIO_RENDERER_BD = 1000409000,
+ XR_OBJECT_TYPE_SOUND_FIELD_BD = 1000409001,
+ XR_OBJECT_TYPE_SOUND_OBJECT_BD = 1000409002,
+ XR_OBJECT_TYPE_SOUND_OBSTACLE_BD = 1000409003,
+ XR_OBJECT_TYPE_SOUND_OBSTACLE_MATERIAL_BD = 1000409004,
+ XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000,
+ XR_OBJECT_TYPE_TRACKABLE_TRACKER_ANDROID = 1000455001,
+ XR_OBJECT_TYPE_EYE_TRACKER_ANDROID = 1000456000,
+ XR_OBJECT_TYPE_DEVICE_ANCHOR_PERSISTENCE_ANDROID = 1000457000,
+ XR_OBJECT_TYPE_FACE_TRACKER_ANDROID = 1000458000,
+ XR_OBJECT_TYPE_PASSTHROUGH_LAYER_ANDROID = 1000462000,
+ XR_OBJECT_TYPE_WORLD_MESH_DETECTOR_ML = 1000474000,
+ XR_OBJECT_TYPE_FACIAL_EXPRESSION_CLIENT_ML = 1000482000,
+ XR_OBJECT_TYPE_ENVIRONMENT_RAYCASTER_META = 1000592000,
+ XR_OBJECT_TYPE_LIGHT_ESTIMATOR_ANDROID = 1000700000,
+ XR_OBJECT_TYPE_TRACKABLE_IMAGE_DATABASE_ANDROID = 1000709000,
+ XR_OBJECT_TYPE_SCENE_MESHING_TRACKER_ANDROID = 1000718000,
+ XR_OBJECT_TYPE_SCENE_MESH_SNAPSHOT_ANDROID = 1000718001,
+ XR_OBJECT_TYPE_SPATIAL_ENTITY_EXT = 1000740000,
+ XR_OBJECT_TYPE_SPATIAL_CONTEXT_EXT = 1000740001,
+ XR_OBJECT_TYPE_SPATIAL_SNAPSHOT_EXT = 1000740002,
+ XR_OBJECT_TYPE_CAMERA_DEVICE_BD = 1000755000,
+ XR_OBJECT_TYPE_CAMERA_CAPTURE_SESSION_BD = 1000755001,
+ XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT = 1000763000,
+ XR_OBJECT_TYPE_SPATIAL_IMAGE_TRACKING_DATABASE_EXT = 1000782000,
+ XR_OBJECT_TYPE_GEOSPATIAL_TRACKER_ANDROID = 1000789000,
+ XR_OBJECT_TYPE_SPATIAL_CONTAINER_EXT = 1000810000,
+ XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
+} XrObjectType;
+typedef XrFlags64 XrInstanceCreateFlags;
+
+// Flag bits for XrInstanceCreateFlags
+
+typedef XrFlags64 XrSessionCreateFlags;
+
+// Flag bits for XrSessionCreateFlags
+
+typedef XrFlags64 XrSpaceLocationFlags;
+
+// Flag bits for XrSpaceLocationFlags
+static const XrSpaceLocationFlags XR_SPACE_LOCATION_ORIENTATION_VALID_BIT = 0x00000001;
+static const XrSpaceLocationFlags XR_SPACE_LOCATION_POSITION_VALID_BIT = 0x00000002;
+static const XrSpaceLocationFlags XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT = 0x00000004;
+static const XrSpaceLocationFlags XR_SPACE_LOCATION_POSITION_TRACKED_BIT = 0x00000008;
+
+typedef XrFlags64 XrSpaceVelocityFlags;
+
+// Flag bits for XrSpaceVelocityFlags
+static const XrSpaceVelocityFlags XR_SPACE_VELOCITY_LINEAR_VALID_BIT = 0x00000001;
+static const XrSpaceVelocityFlags XR_SPACE_VELOCITY_ANGULAR_VALID_BIT = 0x00000002;
+
+typedef XrFlags64 XrSwapchainCreateFlags;
+
+// Flag bits for XrSwapchainCreateFlags
+static const XrSwapchainCreateFlags XR_SWAPCHAIN_CREATE_PROTECTED_CONTENT_BIT = 0x00000001;
+static const XrSwapchainCreateFlags XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT = 0x00000002;
+
+typedef XrFlags64 XrSwapchainUsageFlags;
+
+// Flag bits for XrSwapchainUsageFlags
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT = 0x00000001;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000002;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT = 0x00000004;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_TRANSFER_SRC_BIT = 0x00000008;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT = 0x00000010;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_SAMPLED_BIT = 0x00000020;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT = 0x00000040;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND = 0x00000080;
+static const XrSwapchainUsageFlags XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_KHR = 0x00000080; // alias of XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND
+
+typedef XrFlags64 XrCompositionLayerFlags;
+
+// Flag bits for XrCompositionLayerFlags
+// XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT is deprecated and should not be used
+static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
+static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
+static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004;
+static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT = 0x00000008;
+
+typedef XrFlags64 XrViewStateFlags;
+
+// Flag bits for XrViewStateFlags
+static const XrViewStateFlags XR_VIEW_STATE_ORIENTATION_VALID_BIT = 0x00000001;
+static const XrViewStateFlags XR_VIEW_STATE_POSITION_VALID_BIT = 0x00000002;
+static const XrViewStateFlags XR_VIEW_STATE_ORIENTATION_TRACKED_BIT = 0x00000004;
+static const XrViewStateFlags XR_VIEW_STATE_POSITION_TRACKED_BIT = 0x00000008;
+
+typedef XrFlags64 XrInputSourceLocalizedNameFlags;
+
+// Flag bits for XrInputSourceLocalizedNameFlags
+static const XrInputSourceLocalizedNameFlags XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT = 0x00000001;
+static const XrInputSourceLocalizedNameFlags XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT = 0x00000002;
+static const XrInputSourceLocalizedNameFlags XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT = 0x00000004;
+
+typedef void (XRAPI_PTR *PFN_xrVoidFunction)(void);
+typedef struct XrApiLayerProperties {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ char layerName[XR_MAX_API_LAYER_NAME_SIZE];
+ XrVersion specVersion;
+ uint32_t layerVersion;
+ char description[XR_MAX_API_LAYER_DESCRIPTION_SIZE];
+} XrApiLayerProperties;
+
+typedef struct XrExtensionProperties {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ char extensionName[XR_MAX_EXTENSION_NAME_SIZE];
+ uint32_t extensionVersion;
+} XrExtensionProperties;
+
+typedef struct XrApplicationInfo {
+ char applicationName[XR_MAX_APPLICATION_NAME_SIZE];
+ uint32_t applicationVersion;
+ char engineName[XR_MAX_ENGINE_NAME_SIZE];
+ uint32_t engineVersion;
+ XrVersion apiVersion;
+} XrApplicationInfo;
+
+typedef struct XrInstanceCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrInstanceCreateFlags createFlags;
+ XrApplicationInfo applicationInfo;
+ uint32_t enabledApiLayerCount;
+ const char* const* enabledApiLayerNames;
+ uint32_t enabledExtensionCount;
+ const char* const* enabledExtensionNames;
+} XrInstanceCreateInfo;
+
+typedef struct XrInstanceProperties {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVersion runtimeVersion;
+ char runtimeName[XR_MAX_RUNTIME_NAME_SIZE];
+} XrInstanceProperties;
+
+typedef struct XrEventDataBuffer {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint8_t varying[4000];
+} XrEventDataBuffer;
+
+typedef struct XrSystemGetInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFormFactor formFactor;
+} XrSystemGetInfo;
+
+typedef struct XrSystemGraphicsProperties {
+ uint32_t maxSwapchainImageHeight;
+ uint32_t maxSwapchainImageWidth;
+ uint32_t maxLayerCount;
+} XrSystemGraphicsProperties;
+
+typedef struct XrSystemTrackingProperties {
+ XrBool32 orientationTracking;
+ XrBool32 positionTracking;
+} XrSystemTrackingProperties;
+
+typedef struct XrSystemProperties {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSystemId systemId;
+ uint32_t vendorId;
+ char systemName[XR_MAX_SYSTEM_NAME_SIZE];
+ XrSystemGraphicsProperties graphicsProperties;
+ XrSystemTrackingProperties trackingProperties;
+} XrSystemProperties;
+
+typedef struct XrSessionCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSessionCreateFlags createFlags;
+ XrSystemId systemId;
+} XrSessionCreateInfo;
+
+typedef struct XrQuaternionf {
+ float x;
+ float y;
+ float z;
+ float w;
+} XrQuaternionf;
+
+typedef struct XrVector3f {
+ float x;
+ float y;
+ float z;
+} XrVector3f;
+
+typedef struct XrPosef {
+ XrQuaternionf orientation;
+ XrVector3f position;
+} XrPosef;
+
+typedef struct XrReferenceSpaceCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrReferenceSpaceType referenceSpaceType;
+ XrPosef poseInReferenceSpace;
+} XrReferenceSpaceCreateInfo;
+
+typedef struct XrActionSpaceCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAction action;
+ XrPath subactionPath;
+ XrPosef poseInActionSpace;
+} XrActionSpaceCreateInfo;
+
+typedef struct XrSpaceLocation {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrSpaceLocation;
+
+// XrSpaceVelocity extends XrSpaceLocation
+typedef struct XrSpaceVelocity {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpaceVelocityFlags velocityFlags;
+ XrVector3f linearVelocity;
+ XrVector3f angularVelocity;
+} XrSpaceVelocity;
+
+typedef struct XrExtent2Df {
+ float width;
+ float height;
+} XrExtent2Df;
+
+typedef struct XrViewConfigurationProperties {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+ XrBool32 fovMutable;
+} XrViewConfigurationProperties;
+
+typedef struct XrViewConfigurationView {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t recommendedImageRectWidth;
+ uint32_t maxImageRectWidth;
+ uint32_t recommendedImageRectHeight;
+ uint32_t maxImageRectHeight;
+ uint32_t recommendedSwapchainSampleCount;
+ uint32_t maxSwapchainSampleCount;
+} XrViewConfigurationView;
+
+typedef struct XrSwapchainCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSwapchainCreateFlags createFlags;
+ XrSwapchainUsageFlags usageFlags;
+ int64_t format;
+ uint32_t sampleCount;
+ uint32_t width;
+ uint32_t height;
+ uint32_t faceCount;
+ uint32_t arraySize;
+ uint32_t mipCount;
+} XrSwapchainCreateInfo;
+
+typedef struct XR_MAY_ALIAS XrSwapchainImageBaseHeader {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrSwapchainImageBaseHeader;
+
+typedef struct XrSwapchainImageAcquireInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSwapchainImageAcquireInfo;
+
+typedef struct XrSwapchainImageWaitInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDuration timeout;
+} XrSwapchainImageWaitInfo;
+
+typedef struct XrSwapchainImageReleaseInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSwapchainImageReleaseInfo;
+
+typedef struct XrSessionBeginInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType primaryViewConfigurationType;
+} XrSessionBeginInfo;
+
+typedef struct XrFrameState {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTime predictedDisplayTime;
+ XrDuration predictedDisplayPeriod;
+ XrBool32 shouldRender;
+} XrFrameState;
+
+typedef struct XrFrameWaitInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrFrameWaitInfo;
+
+typedef struct XrFrameBeginInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrFrameBeginInfo;
+
+typedef struct XR_MAY_ALIAS XrCompositionLayerBaseHeader {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+} XrCompositionLayerBaseHeader;
+
+typedef struct XrFrameEndInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime displayTime;
+ XrEnvironmentBlendMode environmentBlendMode;
+ uint32_t layerCount;
+ const XrCompositionLayerBaseHeader* const* layers;
+} XrFrameEndInfo;
+
+typedef struct XrViewState {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrViewStateFlags viewStateFlags;
+} XrViewState;
+
+typedef struct XrViewLocateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+ XrTime displayTime;
+ XrSpace space;
+} XrViewLocateInfo;
+
+typedef struct XrFovf {
+ float angleLeft;
+ float angleRight;
+ float angleUp;
+ float angleDown;
+} XrFovf;
+
+typedef struct XrView {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPosef pose;
+ XrFovf fov;
+} XrView;
+
+typedef struct XrActionSetCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ char actionSetName[XR_MAX_ACTION_SET_NAME_SIZE];
+ char localizedActionSetName[XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE];
+ uint32_t priority;
+} XrActionSetCreateInfo;
+
+typedef struct XrActionCreateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ char actionName[XR_MAX_ACTION_NAME_SIZE];
+ XrActionType actionType;
+ uint32_t countSubactionPaths;
+ const XrPath* subactionPaths;
+ char localizedActionName[XR_MAX_LOCALIZED_ACTION_NAME_SIZE];
+} XrActionCreateInfo;
+
+typedef struct XrActionSuggestedBinding {
+ XrAction action;
+ XrPath binding;
+} XrActionSuggestedBinding;
+
+typedef struct XrInteractionProfileSuggestedBinding {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPath interactionProfile;
+ uint32_t countSuggestedBindings;
+ const XrActionSuggestedBinding* suggestedBindings;
+} XrInteractionProfileSuggestedBinding;
+
+typedef struct XrSessionActionSetsAttachInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t countActionSets;
+ const XrActionSet* actionSets;
+} XrSessionActionSetsAttachInfo;
+
+typedef struct XrInteractionProfileState {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPath interactionProfile;
+} XrInteractionProfileState;
+
+typedef struct XrActionStateGetInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAction action;
+ XrPath subactionPath;
+} XrActionStateGetInfo;
+
+typedef struct XrActionStateBoolean {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 currentState;
+ XrBool32 changedSinceLastSync;
+ XrTime lastChangeTime;
+ XrBool32 isActive;
+} XrActionStateBoolean;
+
+typedef struct XrActionStateFloat {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ float currentState;
+ XrBool32 changedSinceLastSync;
+ XrTime lastChangeTime;
+ XrBool32 isActive;
+} XrActionStateFloat;
+
+typedef struct XrVector2f {
+ float x;
+ float y;
+} XrVector2f;
+
+typedef struct XrActionStateVector2f {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVector2f currentState;
+ XrBool32 changedSinceLastSync;
+ XrTime lastChangeTime;
+ XrBool32 isActive;
+} XrActionStateVector2f;
+
+typedef struct XrActionStatePose {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+} XrActionStatePose;
+
+typedef struct XrActiveActionSet {
+ XrActionSet actionSet;
+ XrPath subactionPath;
+} XrActiveActionSet;
+
+typedef struct XrActionsSyncInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t countActiveActionSets;
+ const XrActiveActionSet* activeActionSets;
+} XrActionsSyncInfo;
+
+typedef struct XrBoundSourcesForActionEnumerateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAction action;
+} XrBoundSourcesForActionEnumerateInfo;
+
+typedef struct XrInputSourceLocalizedNameGetInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPath sourcePath;
+ XrInputSourceLocalizedNameFlags whichComponents;
+} XrInputSourceLocalizedNameGetInfo;
+
+typedef struct XR_MAY_ALIAS XrHapticBaseHeader {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrHapticBaseHeader;
+
+typedef struct XrHapticActionInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAction action;
+ XrPath subactionPath;
+} XrHapticActionInfo;
+
+typedef struct XR_MAY_ALIAS XrBaseInStructure {
+ XrStructureType type;
+ const struct XrBaseInStructure* next;
+} XrBaseInStructure;
+
+typedef struct XR_MAY_ALIAS XrBaseOutStructure {
+ XrStructureType type;
+ struct XrBaseOutStructure* next;
+} XrBaseOutStructure;
+
+typedef struct XrOffset2Di {
+ int32_t x;
+ int32_t y;
+} XrOffset2Di;
+
+typedef struct XrExtent2Di {
+ int32_t width;
+ int32_t height;
+} XrExtent2Di;
+
+typedef struct XrRect2Di {
+ XrOffset2Di offset;
+ XrExtent2Di extent;
+} XrRect2Di;
+
+typedef struct XrSwapchainSubImage {
+ XrSwapchain swapchain;
+ XrRect2Di imageRect;
+ uint32_t imageArrayIndex;
+} XrSwapchainSubImage;
+
+typedef struct XrCompositionLayerProjectionView {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPosef pose;
+ XrFovf fov;
+ XrSwapchainSubImage subImage;
+} XrCompositionLayerProjectionView;
+
+typedef struct XrCompositionLayerProjection {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ uint32_t viewCount;
+ const XrCompositionLayerProjectionView* views;
+} XrCompositionLayerProjection;
+
+typedef struct XrCompositionLayerQuad {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrEyeVisibility eyeVisibility;
+ XrSwapchainSubImage subImage;
+ XrPosef pose;
+ XrExtent2Df size;
+} XrCompositionLayerQuad;
+
+typedef struct XR_MAY_ALIAS XrEventDataBaseHeader {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEventDataBaseHeader;
+
+typedef struct XrEventDataEventsLost {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t lostEventCount;
+} XrEventDataEventsLost;
+
+typedef struct XrEventDataInstanceLossPending {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime lossTime;
+} XrEventDataInstanceLossPending;
+
+typedef struct XrEventDataSessionStateChanged {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+ XrSessionState state;
+ XrTime time;
+} XrEventDataSessionStateChanged;
+
+typedef struct XrEventDataReferenceSpaceChangePending {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+ XrReferenceSpaceType referenceSpaceType;
+ XrTime changeTime;
+ XrBool32 poseValid;
+ XrPosef poseInPreviousSpace;
+} XrEventDataReferenceSpaceChangePending;
+
+typedef struct XrEventDataInteractionProfileChanged {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+} XrEventDataInteractionProfileChanged;
+
+typedef struct XrHapticVibration {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDuration duration;
+ float frequency;
+ float amplitude;
+} XrHapticVibration;
+
+typedef struct XrOffset2Df {
+ float x;
+ float y;
+} XrOffset2Df;
+
+typedef struct XrRect2Df {
+ XrOffset2Df offset;
+ XrExtent2Df extent;
+} XrRect2Df;
+
+typedef struct XrVector4f {
+ float x;
+ float y;
+ float z;
+ float w;
+} XrVector4f;
+
+typedef struct XrColor4f {
+ float r;
+ float g;
+ float b;
+ float a;
+} XrColor4f;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetInstanceProcAddr)(XrInstance instance, const char* name, PFN_xrVoidFunction* function);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateApiLayerProperties)(uint32_t propertyCapacityInput, uint32_t* propertyCountOutput, XrApiLayerProperties* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateInstanceExtensionProperties)(const char* layerName, uint32_t propertyCapacityInput, uint32_t* propertyCountOutput, XrExtensionProperties* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateInstance)(const XrInstanceCreateInfo* createInfo, XrInstance* instance);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyInstance)(XrInstance instance);
+typedef XrResult (XRAPI_PTR *PFN_xrGetInstanceProperties)(XrInstance instance, XrInstanceProperties* instanceProperties);
+typedef XrResult (XRAPI_PTR *PFN_xrPollEvent)(XrInstance instance, XrEventDataBuffer* eventData);
+typedef XrResult (XRAPI_PTR *PFN_xrResultToString)(XrInstance instance, XrResult value, char buffer[XR_MAX_RESULT_STRING_SIZE]);
+typedef XrResult (XRAPI_PTR *PFN_xrStructureTypeToString)(XrInstance instance, XrStructureType value, char buffer[XR_MAX_STRUCTURE_NAME_SIZE]);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSystem)(XrInstance instance, const XrSystemGetInfo* getInfo, XrSystemId* systemId);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSystemProperties)(XrInstance instance, XrSystemId systemId, XrSystemProperties* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentBlendModes)(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t environmentBlendModeCapacityInput, uint32_t* environmentBlendModeCountOutput, XrEnvironmentBlendMode* environmentBlendModes);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSession)(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySession)(XrSession session);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateReferenceSpaces)(XrSession session, uint32_t spaceCapacityInput, uint32_t* spaceCountOutput, XrReferenceSpaceType* spaces);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateReferenceSpace)(XrSession session, const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrGetReferenceSpaceBoundsRect)(XrSession session, XrReferenceSpaceType referenceSpaceType, XrExtent2Df* bounds);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateActionSpace)(XrSession session, const XrActionSpaceCreateInfo* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpace)(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpace)(XrSpace space);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateViewConfigurations)(XrInstance instance, XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, uint32_t* viewConfigurationTypeCountOutput, XrViewConfigurationType* viewConfigurationTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrGetViewConfigurationProperties)(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, XrViewConfigurationProperties* configurationProperties);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateViewConfigurationViews)(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrViewConfigurationView* views);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSwapchainFormats)(XrSession session, uint32_t formatCapacityInput, uint32_t* formatCountOutput, int64_t* formats);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchain)(XrSession session, const XrSwapchainCreateInfo* createInfo, XrSwapchain* swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySwapchain)(XrSwapchain swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSwapchainImages)(XrSwapchain swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images);
+typedef XrResult (XRAPI_PTR *PFN_xrAcquireSwapchainImage)(XrSwapchain swapchain, const XrSwapchainImageAcquireInfo* acquireInfo, uint32_t* index);
+typedef XrResult (XRAPI_PTR *PFN_xrWaitSwapchainImage)(XrSwapchain swapchain, const XrSwapchainImageWaitInfo* waitInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrReleaseSwapchainImage)(XrSwapchain swapchain, const XrSwapchainImageReleaseInfo* releaseInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrBeginSession)(XrSession session, const XrSessionBeginInfo* beginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEndSession)(XrSession session);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestExitSession)(XrSession session);
+typedef XrResult (XRAPI_PTR *PFN_xrWaitFrame)(XrSession session, const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState);
+typedef XrResult (XRAPI_PTR *PFN_xrBeginFrame)(XrSession session, const XrFrameBeginInfo* frameBeginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEndFrame)(XrSession session, const XrFrameEndInfo* frameEndInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateViews)(XrSession session, const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views);
+typedef XrResult (XRAPI_PTR *PFN_xrStringToPath)(XrInstance instance, const char* pathString, XrPath* path);
+typedef XrResult (XRAPI_PTR *PFN_xrPathToString)(XrInstance instance, XrPath path, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateActionSet)(XrInstance instance, const XrActionSetCreateInfo* createInfo, XrActionSet* actionSet);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyActionSet)(XrActionSet actionSet);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateAction)(XrActionSet actionSet, const XrActionCreateInfo* createInfo, XrAction* action);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyAction)(XrAction action);
+typedef XrResult (XRAPI_PTR *PFN_xrSuggestInteractionProfileBindings)(XrInstance instance, const XrInteractionProfileSuggestedBinding* suggestedBindings);
+typedef XrResult (XRAPI_PTR *PFN_xrAttachSessionActionSets)(XrSession session, const XrSessionActionSetsAttachInfo* attachInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetCurrentInteractionProfile)(XrSession session, XrPath topLevelUserPath, XrInteractionProfileState* interactionProfile);
+typedef XrResult (XRAPI_PTR *PFN_xrGetActionStateBoolean)(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateBoolean* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetActionStateFloat)(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateFloat* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetActionStateVector2f)(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateVector2f* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetActionStatePose)(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStatePose* state);
+typedef XrResult (XRAPI_PTR *PFN_xrSyncActions)(XrSession session, const XrActionsSyncInfo* syncInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateBoundSourcesForAction)(XrSession session, const XrBoundSourcesForActionEnumerateInfo* enumerateInfo, uint32_t sourceCapacityInput, uint32_t* sourceCountOutput, XrPath* sources);
+typedef XrResult (XRAPI_PTR *PFN_xrGetInputSourceLocalizedName)(XrSession session, const XrInputSourceLocalizedNameGetInfo* getInfo, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrApplyHapticFeedback)(XrSession session, const XrHapticActionInfo* hapticActionInfo, const XrHapticBaseHeader* hapticFeedback);
+typedef XrResult (XRAPI_PTR *PFN_xrStopHapticFeedback)(XrSession session, const XrHapticActionInfo* hapticActionInfo);
+
+#ifndef XR_NO_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProcAddr(
+ XrInstance instance,
+ const char* name,
+ PFN_xrVoidFunction* function);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateApiLayerProperties(
+ uint32_t propertyCapacityInput,
+ uint32_t* propertyCountOutput,
+ XrApiLayerProperties* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInstanceExtensionProperties(
+ const char* layerName,
+ uint32_t propertyCapacityInput,
+ uint32_t* propertyCountOutput,
+ XrExtensionProperties* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateInstance(
+ const XrInstanceCreateInfo* createInfo,
+ XrInstance* instance);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyInstance(
+ XrInstance instance);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProperties(
+ XrInstance instance,
+ XrInstanceProperties* instanceProperties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPollEvent(
+ XrInstance instance,
+ XrEventDataBuffer* eventData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrResultToString(
+ XrInstance instance,
+ XrResult value,
+ char buffer[XR_MAX_RESULT_STRING_SIZE]);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString(
+ XrInstance instance,
+ XrStructureType value,
+ char buffer[XR_MAX_STRUCTURE_NAME_SIZE]);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSystem(
+ XrInstance instance,
+ const XrSystemGetInfo* getInfo,
+ XrSystemId* systemId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSystemProperties(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrSystemProperties* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentBlendModes(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrViewConfigurationType viewConfigurationType,
+ uint32_t environmentBlendModeCapacityInput,
+ uint32_t* environmentBlendModeCountOutput,
+ XrEnvironmentBlendMode* environmentBlendModes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSession(
+ XrInstance instance,
+ const XrSessionCreateInfo* createInfo,
+ XrSession* session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySession(
+ XrSession session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReferenceSpaces(
+ XrSession session,
+ uint32_t spaceCapacityInput,
+ uint32_t* spaceCountOutput,
+ XrReferenceSpaceType* spaces);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateReferenceSpace(
+ XrSession session,
+ const XrReferenceSpaceCreateInfo* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetReferenceSpaceBoundsRect(
+ XrSession session,
+ XrReferenceSpaceType referenceSpaceType,
+ XrExtent2Df* bounds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSpace(
+ XrSession session,
+ const XrActionSpaceCreateInfo* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpace(
+ XrSpace space,
+ XrSpace baseSpace,
+ XrTime time,
+ XrSpaceLocation* location);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpace(
+ XrSpace space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurations(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t viewConfigurationTypeCapacityInput,
+ uint32_t* viewConfigurationTypeCountOutput,
+ XrViewConfigurationType* viewConfigurationTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetViewConfigurationProperties(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrViewConfigurationType viewConfigurationType,
+ XrViewConfigurationProperties* configurationProperties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurationViews(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrViewConfigurationType viewConfigurationType,
+ uint32_t viewCapacityInput,
+ uint32_t* viewCountOutput,
+ XrViewConfigurationView* views);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainFormats(
+ XrSession session,
+ uint32_t formatCapacityInput,
+ uint32_t* formatCountOutput,
+ int64_t* formats);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchain(
+ XrSession session,
+ const XrSwapchainCreateInfo* createInfo,
+ XrSwapchain* swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySwapchain(
+ XrSwapchain swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainImages(
+ XrSwapchain swapchain,
+ uint32_t imageCapacityInput,
+ uint32_t* imageCountOutput,
+ XrSwapchainImageBaseHeader* images);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAcquireSwapchainImage(
+ XrSwapchain swapchain,
+ const XrSwapchainImageAcquireInfo* acquireInfo,
+ uint32_t* index);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrWaitSwapchainImage(
+ XrSwapchain swapchain,
+ const XrSwapchainImageWaitInfo* waitInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrReleaseSwapchainImage(
+ XrSwapchain swapchain,
+ const XrSwapchainImageReleaseInfo* releaseInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginSession(
+ XrSession session,
+ const XrSessionBeginInfo* beginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEndSession(
+ XrSession session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestExitSession(
+ XrSession session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrWaitFrame(
+ XrSession session,
+ const XrFrameWaitInfo* frameWaitInfo,
+ XrFrameState* frameState);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginFrame(
+ XrSession session,
+ const XrFrameBeginInfo* frameBeginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEndFrame(
+ XrSession session,
+ const XrFrameEndInfo* frameEndInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateViews(
+ XrSession session,
+ const XrViewLocateInfo* viewLocateInfo,
+ XrViewState* viewState,
+ uint32_t viewCapacityInput,
+ uint32_t* viewCountOutput,
+ XrView* views);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStringToPath(
+ XrInstance instance,
+ const char* pathString,
+ XrPath* path);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPathToString(
+ XrInstance instance,
+ XrPath path,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSet(
+ XrInstance instance,
+ const XrActionSetCreateInfo* createInfo,
+ XrActionSet* actionSet);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyActionSet(
+ XrActionSet actionSet);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateAction(
+ XrActionSet actionSet,
+ const XrActionCreateInfo* createInfo,
+ XrAction* action);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAction(
+ XrAction action);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSuggestInteractionProfileBindings(
+ XrInstance instance,
+ const XrInteractionProfileSuggestedBinding* suggestedBindings);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAttachSessionActionSets(
+ XrSession session,
+ const XrSessionActionSetsAttachInfo* attachInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetCurrentInteractionProfile(
+ XrSession session,
+ XrPath topLevelUserPath,
+ XrInteractionProfileState* interactionProfile);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateBoolean(
+ XrSession session,
+ const XrActionStateGetInfo* getInfo,
+ XrActionStateBoolean* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateFloat(
+ XrSession session,
+ const XrActionStateGetInfo* getInfo,
+ XrActionStateFloat* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateVector2f(
+ XrSession session,
+ const XrActionStateGetInfo* getInfo,
+ XrActionStateVector2f* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStatePose(
+ XrSession session,
+ const XrActionStateGetInfo* getInfo,
+ XrActionStatePose* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSyncActions(
+ XrSession session,
+ const XrActionsSyncInfo* syncInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateBoundSourcesForAction(
+ XrSession session,
+ const XrBoundSourcesForActionEnumerateInfo* enumerateInfo,
+ uint32_t sourceCapacityInput,
+ uint32_t* sourceCountOutput,
+ XrPath* sources);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetInputSourceLocalizedName(
+ XrSession session,
+ const XrInputSourceLocalizedNameGetInfo* getInfo,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrApplyHapticFeedback(
+ XrSession session,
+ const XrHapticActionInfo* hapticActionInfo,
+ const XrHapticBaseHeader* hapticFeedback);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
+ XrSession session,
+ const XrHapticActionInfo* hapticActionInfo);
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_VERSION_1_1 is a preprocessor guard. Do not pass it to API calls.
+#define XR_VERSION_1_1 1
+// OpenXR 1.1 version number
+#define XR_API_VERSION_1_1 XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION))
+
+#define XR_UUID_SIZE 16
+typedef struct XrColor3f {
+ float r;
+ float g;
+ float b;
+} XrColor3f;
+
+typedef struct XrExtent3Df {
+ float width;
+ float height;
+ float depth;
+} XrExtent3Df;
+
+typedef struct XrSpheref {
+ XrPosef center;
+ float radius;
+} XrSpheref;
+
+typedef struct XrBoxf {
+ XrPosef center;
+ XrExtent3Df extents;
+} XrBoxf;
+
+typedef struct XrFrustumf {
+ XrPosef pose;
+ XrFovf fov;
+ float nearZ;
+ float farZ;
+} XrFrustumf;
+
+typedef struct XrUuid {
+ uint8_t data[XR_UUID_SIZE];
+} XrUuid;
+
+typedef struct XrSpacesLocateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ uint32_t spaceCount;
+ const XrSpace* spaces;
+} XrSpacesLocateInfo;
+
+typedef struct XrSpaceLocationData {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrSpaceLocationData;
+
+typedef struct XrSpaceLocations {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrSpaceLocationData* locations;
+} XrSpaceLocations;
+
+typedef struct XrSpaceVelocityData {
+ XrSpaceVelocityFlags velocityFlags;
+ XrVector3f linearVelocity;
+ XrVector3f angularVelocity;
+} XrSpaceVelocityData;
+
+// XrSpaceVelocities extends XrSpaceLocations
+typedef struct XrSpaceVelocities {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t velocityCount;
+ XrSpaceVelocityData* velocities;
+} XrSpaceVelocities;
+
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpaces)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations);
+
+#ifndef XR_NO_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations);
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_composition_layer_cube is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_cube 1
+#define XR_KHR_composition_layer_cube_SPEC_VERSION 8
+#define XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME "XR_KHR_composition_layer_cube"
+typedef struct XrCompositionLayerCubeKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrEyeVisibility eyeVisibility;
+ XrSwapchain swapchain;
+ uint32_t imageArrayIndex;
+ XrQuaternionf orientation;
+} XrCompositionLayerCubeKHR;
+
+
+
+// XR_KHR_composition_layer_depth is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_depth 1
+#define XR_KHR_composition_layer_depth_SPEC_VERSION 6
+#define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth"
+// XrCompositionLayerDepthInfoKHR extends XrCompositionLayerProjectionView
+typedef struct XrCompositionLayerDepthInfoKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSwapchainSubImage subImage;
+ float minDepth;
+ float maxDepth;
+ float nearZ;
+ float farZ;
+} XrCompositionLayerDepthInfoKHR;
+
+
+
+// XR_KHR_composition_layer_cylinder is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_cylinder 1
+#define XR_KHR_composition_layer_cylinder_SPEC_VERSION 4
+#define XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME "XR_KHR_composition_layer_cylinder"
+typedef struct XrCompositionLayerCylinderKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrEyeVisibility eyeVisibility;
+ XrSwapchainSubImage subImage;
+ XrPosef pose;
+ float radius;
+ float centralAngle;
+ float aspectRatio;
+} XrCompositionLayerCylinderKHR;
+
+
+
+// XR_KHR_composition_layer_equirect is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_equirect 1
+#define XR_KHR_composition_layer_equirect_SPEC_VERSION 3
+#define XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME "XR_KHR_composition_layer_equirect"
+typedef struct XrCompositionLayerEquirectKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrEyeVisibility eyeVisibility;
+ XrSwapchainSubImage subImage;
+ XrPosef pose;
+ float radius;
+ XrVector2f scale;
+ XrVector2f bias;
+} XrCompositionLayerEquirectKHR;
+
+
+
+// XR_KHR_visibility_mask is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_visibility_mask 1
+#define XR_KHR_visibility_mask_SPEC_VERSION 2
+#define XR_KHR_VISIBILITY_MASK_EXTENSION_NAME "XR_KHR_visibility_mask"
+
+typedef enum XrVisibilityMaskTypeKHR {
+ XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR = 1,
+ XR_VISIBILITY_MASK_TYPE_VISIBLE_TRIANGLE_MESH_KHR = 2,
+ XR_VISIBILITY_MASK_TYPE_LINE_LOOP_KHR = 3,
+ XR_VISIBILITY_MASK_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
+} XrVisibilityMaskTypeKHR;
+typedef struct XrVisibilityMaskKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector2f* vertices;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrVisibilityMaskKHR;
+
+typedef struct XrEventDataVisibilityMaskChangedKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+ XrViewConfigurationType viewConfigurationType;
+ uint32_t viewIndex;
+} XrEventDataVisibilityMaskChangedKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetVisibilityMaskKHR)(XrSession session, XrViewConfigurationType viewConfigurationType, uint32_t viewIndex, XrVisibilityMaskTypeKHR visibilityMaskType, XrVisibilityMaskKHR* visibilityMask);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVisibilityMaskKHR(
+ XrSession session,
+ XrViewConfigurationType viewConfigurationType,
+ uint32_t viewIndex,
+ XrVisibilityMaskTypeKHR visibilityMaskType,
+ XrVisibilityMaskKHR* visibilityMask);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_composition_layer_color_scale_bias is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_color_scale_bias 1
+#define XR_KHR_composition_layer_color_scale_bias_SPEC_VERSION 5
+#define XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME "XR_KHR_composition_layer_color_scale_bias"
+// XrCompositionLayerColorScaleBiasKHR extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerColorScaleBiasKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrColor4f colorScale;
+ XrColor4f colorBias;
+} XrCompositionLayerColorScaleBiasKHR;
+
+
+
+// XR_KHR_loader_init is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_loader_init 1
+#define XR_KHR_loader_init_SPEC_VERSION 2
+#define XR_KHR_LOADER_INIT_EXTENSION_NAME "XR_KHR_loader_init"
+typedef struct XR_MAY_ALIAS XrLoaderInitInfoBaseHeaderKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrLoaderInitInfoBaseHeaderKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrInitializeLoaderKHR)(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR(
+ const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_composition_layer_equirect2 is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_composition_layer_equirect2 1
+#define XR_KHR_composition_layer_equirect2_SPEC_VERSION 1
+#define XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME "XR_KHR_composition_layer_equirect2"
+typedef struct XrCompositionLayerEquirect2KHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrEyeVisibility eyeVisibility;
+ XrSwapchainSubImage subImage;
+ XrPosef pose;
+ float radius;
+ float centralHorizontalAngle;
+ float upperVerticalAngle;
+ float lowerVerticalAngle;
+} XrCompositionLayerEquirect2KHR;
+
+
+
+// XR_KHR_binding_modification is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_binding_modification 1
+#define XR_KHR_binding_modification_SPEC_VERSION 1
+#define XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME "XR_KHR_binding_modification"
+typedef struct XR_MAY_ALIAS XrBindingModificationBaseHeaderKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrBindingModificationBaseHeaderKHR;
+
+// XrBindingModificationsKHR extends XrInteractionProfileSuggestedBinding
+typedef struct XrBindingModificationsKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t bindingModificationCount;
+ const XrBindingModificationBaseHeaderKHR* const* bindingModifications;
+} XrBindingModificationsKHR;
+
+
+
+// XR_KHR_extended_struct_name_lengths is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_extended_struct_name_lengths 1
+#define XR_KHR_extended_struct_name_lengths_SPEC_VERSION 2
+#define XR_KHR_EXTENDED_STRUCT_NAME_LENGTHS_EXTENSION_NAME "XR_KHR_extended_struct_name_lengths"
+#define XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR 256
+typedef XrResult (XRAPI_PTR *PFN_xrStructureTypeToString2KHR)(XrInstance instance, XrStructureType value, char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString2KHR(
+ XrInstance instance,
+ XrStructureType value,
+ char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_swapchain_usage_input_attachment_bit 1
+#define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3
+#define XR_KHR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_KHR_swapchain_usage_input_attachment_bit"
+
+
+// XR_KHR_locate_spaces is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_locate_spaces 1
+#define XR_KHR_locate_spaces_SPEC_VERSION 1
+#define XR_KHR_LOCATE_SPACES_EXTENSION_NAME "XR_KHR_locate_spaces"
+typedef XrSpacesLocateInfo XrSpacesLocateInfoKHR;
+
+typedef XrSpaceLocationData XrSpaceLocationDataKHR;
+
+typedef XrSpaceLocations XrSpaceLocationsKHR;
+
+typedef XrSpaceVelocityData XrSpaceVelocityDataKHR;
+
+typedef XrSpaceVelocities XrSpaceVelocitiesKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpacesKHR)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpacesKHR(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_maintenance1 is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_maintenance1 1
+#define XR_KHR_maintenance1_SPEC_VERSION 1
+#define XR_KHR_MAINTENANCE1_EXTENSION_NAME "XR_KHR_maintenance1"
+typedef XrColor3f XrColor3fKHR;
+
+typedef XrExtent3Df XrExtent3DfKHR;
+
+typedef XrSpheref XrSpherefKHR;
+
+typedef XrBoxf XrBoxfKHR;
+
+typedef XrFrustumf XrFrustumfKHR;
+
+
+
+// XR_KHR_generic_controller is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_generic_controller 1
+#define XR_KHR_generic_controller_SPEC_VERSION 1
+#define XR_KHR_GENERIC_CONTROLLER_EXTENSION_NAME "XR_KHR_generic_controller"
+
+
+// XR_KHR_extended_result_name_lengths is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_extended_result_name_lengths 1
+#define XR_KHR_extended_result_name_lengths_SPEC_VERSION 1
+#define XR_KHR_EXTENDED_RESULT_NAME_LENGTHS_EXTENSION_NAME "XR_KHR_extended_result_name_lengths"
+#define XR_MAX_RESULT_STRING_SIZE_EXTENDED_KHR 256
+typedef XrResult (XRAPI_PTR *PFN_xrResultToString2KHR)(XrInstance instance, XrResult value, char buffer[XR_MAX_RESULT_STRING_SIZE_EXTENDED_KHR]);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrResultToString2KHR(
+ XrInstance instance,
+ XrResult value,
+ char buffer[XR_MAX_RESULT_STRING_SIZE_EXTENDED_KHR]);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_performance_settings is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_performance_settings 1
+#define XR_EXT_performance_settings_SPEC_VERSION 4
+#define XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME "XR_EXT_performance_settings"
+
+typedef enum XrPerfSettingsDomainEXT {
+ XR_PERF_SETTINGS_DOMAIN_CPU_EXT = 1,
+ XR_PERF_SETTINGS_DOMAIN_GPU_EXT = 2,
+ XR_PERF_SETTINGS_DOMAIN_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPerfSettingsDomainEXT;
+
+typedef enum XrPerfSettingsSubDomainEXT {
+ XR_PERF_SETTINGS_SUB_DOMAIN_COMPOSITING_EXT = 1,
+ XR_PERF_SETTINGS_SUB_DOMAIN_RENDERING_EXT = 2,
+ XR_PERF_SETTINGS_SUB_DOMAIN_THERMAL_EXT = 3,
+ XR_PERF_SETTINGS_SUB_DOMAIN_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPerfSettingsSubDomainEXT;
+
+typedef enum XrPerfSettingsLevelEXT {
+ XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT = 0,
+ XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT = 25,
+ XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT = 50,
+ XR_PERF_SETTINGS_LEVEL_BOOST_EXT = 75,
+ XR_PERF_SETTINGS_LEVEL_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPerfSettingsLevelEXT;
+
+typedef enum XrPerfSettingsNotificationLevelEXT {
+ XR_PERF_SETTINGS_NOTIF_LEVEL_NORMAL_EXT = 0,
+ XR_PERF_SETTINGS_NOTIF_LEVEL_WARNING_EXT = 25,
+ XR_PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED_EXT = 75,
+ XR_PERF_SETTINGS_NOTIFICATION_LEVEL_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPerfSettingsNotificationLevelEXT;
+typedef struct XrEventDataPerfSettingsEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPerfSettingsDomainEXT domain;
+ XrPerfSettingsSubDomainEXT subDomain;
+ XrPerfSettingsNotificationLevelEXT fromLevel;
+ XrPerfSettingsNotificationLevelEXT toLevel;
+} XrEventDataPerfSettingsEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrPerfSettingsSetPerformanceLevelEXT)(XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsLevelEXT level);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrPerfSettingsSetPerformanceLevelEXT(
+ XrSession session,
+ XrPerfSettingsDomainEXT domain,
+ XrPerfSettingsLevelEXT level);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_thermal_query is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_thermal_query 1
+#define XR_EXT_thermal_query_SPEC_VERSION 2
+#define XR_EXT_THERMAL_QUERY_EXTENSION_NAME "XR_EXT_thermal_query"
+typedef XrResult (XRAPI_PTR *PFN_xrThermalGetTemperatureTrendEXT)(XrSession session, XrPerfSettingsDomainEXT domain, XrPerfSettingsNotificationLevelEXT* notificationLevel, float* tempHeadroom, float* tempSlope);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT(
+ XrSession session,
+ XrPerfSettingsDomainEXT domain,
+ XrPerfSettingsNotificationLevelEXT* notificationLevel,
+ float* tempHeadroom,
+ float* tempSlope);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_debug_utils is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_debug_utils 1
+XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT)
+#define XR_EXT_debug_utils_SPEC_VERSION 5
+#define XR_EXT_DEBUG_UTILS_EXTENSION_NAME "XR_EXT_debug_utils"
+typedef XrFlags64 XrDebugUtilsMessageSeverityFlagsEXT;
+
+// Flag bits for XrDebugUtilsMessageSeverityFlagsEXT
+static const XrDebugUtilsMessageSeverityFlagsEXT XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001;
+static const XrDebugUtilsMessageSeverityFlagsEXT XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 0x00000010;
+static const XrDebugUtilsMessageSeverityFlagsEXT XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100;
+static const XrDebugUtilsMessageSeverityFlagsEXT XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000;
+
+typedef XrFlags64 XrDebugUtilsMessageTypeFlagsEXT;
+
+// Flag bits for XrDebugUtilsMessageTypeFlagsEXT
+static const XrDebugUtilsMessageTypeFlagsEXT XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001;
+static const XrDebugUtilsMessageTypeFlagsEXT XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 0x00000002;
+static const XrDebugUtilsMessageTypeFlagsEXT XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004;
+static const XrDebugUtilsMessageTypeFlagsEXT XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT = 0x00000008;
+
+typedef struct XrDebugUtilsObjectNameInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrObjectType objectType;
+ uint64_t objectHandle;
+ const char* objectName;
+} XrDebugUtilsObjectNameInfoEXT;
+
+typedef struct XrDebugUtilsLabelEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* labelName;
+} XrDebugUtilsLabelEXT;
+
+typedef struct XrDebugUtilsMessengerCallbackDataEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* messageId;
+ const char* functionName;
+ const char* message;
+ uint32_t objectCount;
+ XrDebugUtilsObjectNameInfoEXT* objects;
+ uint32_t sessionLabelCount;
+ XrDebugUtilsLabelEXT* sessionLabels;
+} XrDebugUtilsMessengerCallbackDataEXT;
+
+typedef XrBool32 (XRAPI_PTR *PFN_xrDebugUtilsMessengerCallbackEXT)(
+ XrDebugUtilsMessageSeverityFlagsEXT messageSeverity,
+ XrDebugUtilsMessageTypeFlagsEXT messageTypes,
+ const XrDebugUtilsMessengerCallbackDataEXT* callbackData,
+ void* userData);
+
+
+// XrDebugUtilsMessengerCreateInfoEXT extends XrInstanceCreateInfo
+typedef struct XrDebugUtilsMessengerCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDebugUtilsMessageSeverityFlagsEXT messageSeverities;
+ XrDebugUtilsMessageTypeFlagsEXT messageTypes;
+ PFN_xrDebugUtilsMessengerCallbackEXT userCallback;
+ void* XR_MAY_ALIAS userData;
+} XrDebugUtilsMessengerCreateInfoEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetDebugUtilsObjectNameEXT)(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT* nameInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateDebugUtilsMessengerEXT)(XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT* createInfo, XrDebugUtilsMessengerEXT* messenger);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyDebugUtilsMessengerEXT)(XrDebugUtilsMessengerEXT messenger);
+typedef XrResult (XRAPI_PTR *PFN_xrSubmitDebugUtilsMessageEXT)(XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes, const XrDebugUtilsMessengerCallbackDataEXT* callbackData);
+typedef XrResult (XRAPI_PTR *PFN_xrSessionBeginDebugUtilsLabelRegionEXT)(XrSession session, const XrDebugUtilsLabelEXT* labelInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrSessionEndDebugUtilsLabelRegionEXT)(XrSession session);
+typedef XrResult (XRAPI_PTR *PFN_xrSessionInsertDebugUtilsLabelEXT)(XrSession session, const XrDebugUtilsLabelEXT* labelInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetDebugUtilsObjectNameEXT(
+ XrInstance instance,
+ const XrDebugUtilsObjectNameInfoEXT* nameInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateDebugUtilsMessengerEXT(
+ XrInstance instance,
+ const XrDebugUtilsMessengerCreateInfoEXT* createInfo,
+ XrDebugUtilsMessengerEXT* messenger);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyDebugUtilsMessengerEXT(
+ XrDebugUtilsMessengerEXT messenger);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSubmitDebugUtilsMessageEXT(
+ XrInstance instance,
+ XrDebugUtilsMessageSeverityFlagsEXT messageSeverity,
+ XrDebugUtilsMessageTypeFlagsEXT messageTypes,
+ const XrDebugUtilsMessengerCallbackDataEXT* callbackData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSessionBeginDebugUtilsLabelRegionEXT(
+ XrSession session,
+ const XrDebugUtilsLabelEXT* labelInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSessionEndDebugUtilsLabelRegionEXT(
+ XrSession session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT(
+ XrSession session,
+ const XrDebugUtilsLabelEXT* labelInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_eye_gaze_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_eye_gaze_interaction 1
+#define XR_EXT_eye_gaze_interaction_SPEC_VERSION 2
+#define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction"
+// XrSystemEyeGazeInteractionPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemEyeGazeInteractionPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEyeGazeInteraction;
+} XrSystemEyeGazeInteractionPropertiesEXT;
+
+// XrEyeGazeSampleTimeEXT extends XrSpaceLocation
+typedef struct XrEyeGazeSampleTimeEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrEyeGazeSampleTimeEXT;
+
+
+
+// XR_EXTX_overlay is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXTX_overlay 1
+#define XR_EXTX_overlay_SPEC_VERSION 5
+#define XR_EXTX_OVERLAY_EXTENSION_NAME "XR_EXTX_overlay"
+typedef XrFlags64 XrOverlaySessionCreateFlagsEXTX;
+
+// Flag bits for XrOverlaySessionCreateFlagsEXTX
+
+typedef XrFlags64 XrOverlayMainSessionFlagsEXTX;
+
+// Flag bits for XrOverlayMainSessionFlagsEXTX
+static const XrOverlayMainSessionFlagsEXTX XR_OVERLAY_MAIN_SESSION_ENABLED_COMPOSITION_LAYER_INFO_DEPTH_BIT_EXTX = 0x00000001;
+
+// XrSessionCreateInfoOverlayEXTX extends XrSessionCreateInfo
+typedef struct XrSessionCreateInfoOverlayEXTX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrOverlaySessionCreateFlagsEXTX createFlags;
+ uint32_t sessionLayersPlacement;
+} XrSessionCreateInfoOverlayEXTX;
+
+typedef struct XrEventDataMainSessionVisibilityChangedEXTX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 visible;
+ XrOverlayMainSessionFlagsEXTX flags;
+} XrEventDataMainSessionVisibilityChangedEXTX;
+
+
+
+// XR_VARJO_quad_views is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_quad_views 1
+#define XR_VARJO_quad_views_SPEC_VERSION 2
+#define XR_VARJO_QUAD_VIEWS_EXTENSION_NAME "XR_VARJO_quad_views"
+
+
+// XR_MSFT_unbounded_reference_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_unbounded_reference_space 1
+#define XR_MSFT_unbounded_reference_space_SPEC_VERSION 1
+#define XR_MSFT_UNBOUNDED_REFERENCE_SPACE_EXTENSION_NAME "XR_MSFT_unbounded_reference_space"
+
+
+// XR_MSFT_spatial_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_spatial_anchor 1
+XR_DEFINE_HANDLE(XrSpatialAnchorMSFT)
+#define XR_MSFT_spatial_anchor_SPEC_VERSION 2
+#define XR_MSFT_SPATIAL_ANCHOR_EXTENSION_NAME "XR_MSFT_spatial_anchor"
+typedef struct XrSpatialAnchorCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef pose;
+ XrTime time;
+} XrSpatialAnchorCreateInfoMSFT;
+
+typedef struct XrSpatialAnchorSpaceCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialAnchorMSFT anchor;
+ XrPosef poseInAnchorSpace;
+} XrSpatialAnchorSpaceCreateInfoMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorMSFT)(XrSession session, const XrSpatialAnchorCreateInfoMSFT* createInfo, XrSpatialAnchorMSFT* anchor);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorSpaceMSFT)(XrSession session, const XrSpatialAnchorSpaceCreateInfoMSFT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorMSFT)(XrSpatialAnchorMSFT anchor);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorMSFT(
+ XrSession session,
+ const XrSpatialAnchorCreateInfoMSFT* createInfo,
+ XrSpatialAnchorMSFT* anchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorSpaceMSFT(
+ XrSession session,
+ const XrSpatialAnchorSpaceCreateInfoMSFT* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorMSFT(
+ XrSpatialAnchorMSFT anchor);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_composition_layer_image_layout is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_composition_layer_image_layout 1
+#define XR_FB_composition_layer_image_layout_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME "XR_FB_composition_layer_image_layout"
+typedef XrFlags64 XrCompositionLayerImageLayoutFlagsFB;
+
+// Flag bits for XrCompositionLayerImageLayoutFlagsFB
+static const XrCompositionLayerImageLayoutFlagsFB XR_COMPOSITION_LAYER_IMAGE_LAYOUT_VERTICAL_FLIP_BIT_FB = 0x00000001;
+
+// XrCompositionLayerImageLayoutFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerImageLayoutFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrCompositionLayerImageLayoutFlagsFB flags;
+} XrCompositionLayerImageLayoutFB;
+
+
+
+// XR_FB_composition_layer_alpha_blend is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_composition_layer_alpha_blend 1
+#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 3
+#define XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME "XR_FB_composition_layer_alpha_blend"
+
+typedef enum XrBlendFactorFB {
+ XR_BLEND_FACTOR_ZERO_FB = 0,
+ XR_BLEND_FACTOR_ONE_FB = 1,
+ XR_BLEND_FACTOR_SRC_ALPHA_FB = 2,
+ XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB = 3,
+ XR_BLEND_FACTOR_DST_ALPHA_FB = 4,
+ XR_BLEND_FACTOR_ONE_MINUS_DST_ALPHA_FB = 5,
+ XR_BLEND_FACTOR_MAX_ENUM_FB = 0x7FFFFFFF
+} XrBlendFactorFB;
+typedef XrFlags64 XrCompositionLayerSecureContentFlagsFB;
+
+// Flag bits for XrCompositionLayerSecureContentFlagsFB
+static const XrCompositionLayerSecureContentFlagsFB XR_COMPOSITION_LAYER_SECURE_CONTENT_EXCLUDE_LAYER_BIT_FB = 0x00000001;
+static const XrCompositionLayerSecureContentFlagsFB XR_COMPOSITION_LAYER_SECURE_CONTENT_REPLACE_LAYER_BIT_FB = 0x00000002;
+
+// XrCompositionLayerAlphaBlendFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerAlphaBlendFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBlendFactorFB srcFactorColor;
+ XrBlendFactorFB dstFactorColor;
+ XrBlendFactorFB srcFactorAlpha;
+ XrBlendFactorFB dstFactorAlpha;
+} XrCompositionLayerAlphaBlendFB;
+
+
+
+// XR_MND_headless is a preprocessor guard. Do not pass it to API calls.
+#define XR_MND_headless 1
+#define XR_MND_headless_SPEC_VERSION 3
+#define XR_MND_HEADLESS_EXTENSION_NAME "XR_MND_headless"
+
+
+// XR_OCULUS_android_session_state_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_OCULUS_android_session_state_enable 1
+#define XR_OCULUS_android_session_state_enable_SPEC_VERSION 1
+#define XR_OCULUS_ANDROID_SESSION_STATE_ENABLE_EXTENSION_NAME "XR_OCULUS_android_session_state_enable"
+
+
+// XR_EXT_view_configuration_depth_range is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_view_configuration_depth_range 1
+#define XR_EXT_view_configuration_depth_range_SPEC_VERSION 1
+#define XR_EXT_VIEW_CONFIGURATION_DEPTH_RANGE_EXTENSION_NAME "XR_EXT_view_configuration_depth_range"
+// XrViewConfigurationDepthRangeEXT extends XrViewConfigurationView
+typedef struct XrViewConfigurationDepthRangeEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ float recommendedNearZ;
+ float minNearZ;
+ float recommendedFarZ;
+ float maxFarZ;
+} XrViewConfigurationDepthRangeEXT;
+
+
+
+// XR_EXT_conformance_automation is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_conformance_automation 1
+#define XR_EXT_conformance_automation_SPEC_VERSION 3
+#define XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME "XR_EXT_conformance_automation"
+typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceActiveEXT)(XrSession session, XrPath interactionProfile, XrPath topLevelPath, XrBool32 isActive);
+typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceStateBoolEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrBool32 state);
+typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceStateFloatEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, float state);
+typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceStateVector2fEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrVector2f state);
+typedef XrResult (XRAPI_PTR *PFN_xrSetInputDeviceLocationEXT)(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrSpace space, XrPosef pose);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceActiveEXT(
+ XrSession session,
+ XrPath interactionProfile,
+ XrPath topLevelPath,
+ XrBool32 isActive);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceStateBoolEXT(
+ XrSession session,
+ XrPath topLevelPath,
+ XrPath inputSourcePath,
+ XrBool32 state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceStateFloatEXT(
+ XrSession session,
+ XrPath topLevelPath,
+ XrPath inputSourcePath,
+ float state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceStateVector2fEXT(
+ XrSession session,
+ XrPath topLevelPath,
+ XrPath inputSourcePath,
+ XrVector2f state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT(
+ XrSession session,
+ XrPath topLevelPath,
+ XrPath inputSourcePath,
+ XrSpace space,
+ XrPosef pose);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_spatial_graph_bridge is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_spatial_graph_bridge 1
+XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT)
+#define XR_GUID_SIZE_MSFT 16
+#define XR_MSFT_spatial_graph_bridge_SPEC_VERSION 2
+#define XR_MSFT_SPATIAL_GRAPH_BRIDGE_EXTENSION_NAME "XR_MSFT_spatial_graph_bridge"
+
+typedef enum XrSpatialGraphNodeTypeMSFT {
+ XR_SPATIAL_GRAPH_NODE_TYPE_STATIC_MSFT = 1,
+ XR_SPATIAL_GRAPH_NODE_TYPE_DYNAMIC_MSFT = 2,
+ XR_SPATIAL_GRAPH_NODE_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSpatialGraphNodeTypeMSFT;
+typedef struct XrSpatialGraphNodeSpaceCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialGraphNodeTypeMSFT nodeType;
+ uint8_t nodeId[XR_GUID_SIZE_MSFT];
+ XrPosef pose;
+} XrSpatialGraphNodeSpaceCreateInfoMSFT;
+
+typedef struct XrSpatialGraphStaticNodeBindingCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef poseInSpace;
+ XrTime time;
+} XrSpatialGraphStaticNodeBindingCreateInfoMSFT;
+
+typedef struct XrSpatialGraphNodeBindingPropertiesGetInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialGraphNodeBindingPropertiesGetInfoMSFT;
+
+typedef struct XrSpatialGraphNodeBindingPropertiesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint8_t nodeId[XR_GUID_SIZE_MSFT];
+ XrPosef poseInNodeSpace;
+} XrSpatialGraphNodeBindingPropertiesMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialGraphNodeSpaceMSFT)(XrSession session, const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrTryCreateSpatialGraphStaticNodeBindingMSFT)(XrSession session, const XrSpatialGraphStaticNodeBindingCreateInfoMSFT* createInfo, XrSpatialGraphNodeBindingMSFT* nodeBinding);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialGraphNodeBindingMSFT)(XrSpatialGraphNodeBindingMSFT nodeBinding);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialGraphNodeBindingPropertiesMSFT)(XrSpatialGraphNodeBindingMSFT nodeBinding, const XrSpatialGraphNodeBindingPropertiesGetInfoMSFT* getInfo, XrSpatialGraphNodeBindingPropertiesMSFT* properties);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialGraphNodeSpaceMSFT(
+ XrSession session,
+ const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTryCreateSpatialGraphStaticNodeBindingMSFT(
+ XrSession session,
+ const XrSpatialGraphStaticNodeBindingCreateInfoMSFT* createInfo,
+ XrSpatialGraphNodeBindingMSFT* nodeBinding);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialGraphNodeBindingMSFT(
+ XrSpatialGraphNodeBindingMSFT nodeBinding);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialGraphNodeBindingPropertiesMSFT(
+ XrSpatialGraphNodeBindingMSFT nodeBinding,
+ const XrSpatialGraphNodeBindingPropertiesGetInfoMSFT* getInfo,
+ XrSpatialGraphNodeBindingPropertiesMSFT* properties);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_hand_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_hand_interaction 1
+#define XR_MSFT_hand_interaction_SPEC_VERSION 1
+#define XR_MSFT_HAND_INTERACTION_EXTENSION_NAME "XR_MSFT_hand_interaction"
+
+
+// XR_EXT_hand_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_hand_tracking 1
+
+#define XR_HAND_JOINT_COUNT_EXT 26
+
+XR_DEFINE_HANDLE(XrHandTrackerEXT)
+#define XR_EXT_hand_tracking_SPEC_VERSION 4
+#define XR_EXT_HAND_TRACKING_EXTENSION_NAME "XR_EXT_hand_tracking"
+
+typedef enum XrHandEXT {
+ XR_HAND_LEFT_EXT = 1,
+ XR_HAND_RIGHT_EXT = 2,
+ XR_HAND_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandEXT;
+
+typedef enum XrHandJointEXT {
+ XR_HAND_JOINT_PALM_EXT = 0,
+ XR_HAND_JOINT_WRIST_EXT = 1,
+ XR_HAND_JOINT_THUMB_METACARPAL_EXT = 2,
+ XR_HAND_JOINT_THUMB_PROXIMAL_EXT = 3,
+ XR_HAND_JOINT_THUMB_DISTAL_EXT = 4,
+ XR_HAND_JOINT_THUMB_TIP_EXT = 5,
+ XR_HAND_JOINT_INDEX_METACARPAL_EXT = 6,
+ XR_HAND_JOINT_INDEX_PROXIMAL_EXT = 7,
+ XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT = 8,
+ XR_HAND_JOINT_INDEX_DISTAL_EXT = 9,
+ XR_HAND_JOINT_INDEX_TIP_EXT = 10,
+ XR_HAND_JOINT_MIDDLE_METACARPAL_EXT = 11,
+ XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT = 12,
+ XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT = 13,
+ XR_HAND_JOINT_MIDDLE_DISTAL_EXT = 14,
+ XR_HAND_JOINT_MIDDLE_TIP_EXT = 15,
+ XR_HAND_JOINT_RING_METACARPAL_EXT = 16,
+ XR_HAND_JOINT_RING_PROXIMAL_EXT = 17,
+ XR_HAND_JOINT_RING_INTERMEDIATE_EXT = 18,
+ XR_HAND_JOINT_RING_DISTAL_EXT = 19,
+ XR_HAND_JOINT_RING_TIP_EXT = 20,
+ XR_HAND_JOINT_LITTLE_METACARPAL_EXT = 21,
+ XR_HAND_JOINT_LITTLE_PROXIMAL_EXT = 22,
+ XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT = 23,
+ XR_HAND_JOINT_LITTLE_DISTAL_EXT = 24,
+ XR_HAND_JOINT_LITTLE_TIP_EXT = 25,
+ XR_HAND_JOINT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandJointEXT;
+
+typedef enum XrHandJointSetEXT {
+ XR_HAND_JOINT_SET_DEFAULT_EXT = 0,
+ XR_HAND_JOINT_SET_HAND_WITH_FOREARM_ULTRALEAP = 1000149000,
+ XR_HAND_JOINT_SET_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandJointSetEXT;
+// XrSystemHandTrackingPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemHandTrackingPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsHandTracking;
+} XrSystemHandTrackingPropertiesEXT;
+
+typedef struct XrHandTrackerCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrHandEXT hand;
+ XrHandJointSetEXT handJointSet;
+} XrHandTrackerCreateInfoEXT;
+
+typedef struct XrHandJointsLocateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrHandJointsLocateInfoEXT;
+
+typedef struct XrHandJointLocationEXT {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+ float radius;
+} XrHandJointLocationEXT;
+
+typedef struct XrHandJointVelocityEXT {
+ XrSpaceVelocityFlags velocityFlags;
+ XrVector3f linearVelocity;
+ XrVector3f angularVelocity;
+} XrHandJointVelocityEXT;
+
+typedef struct XrHandJointLocationsEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ uint32_t jointCount;
+ XrHandJointLocationEXT* jointLocations;
+} XrHandJointLocationsEXT;
+
+// XrHandJointVelocitiesEXT extends XrHandJointLocationsEXT
+typedef struct XrHandJointVelocitiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCount;
+ XrHandJointVelocityEXT* jointVelocities;
+} XrHandJointVelocitiesEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateHandTrackerEXT)(XrSession session, const XrHandTrackerCreateInfoEXT* createInfo, XrHandTrackerEXT* handTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyHandTrackerEXT)(XrHandTrackerEXT handTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateHandJointsEXT)(XrHandTrackerEXT handTracker, const XrHandJointsLocateInfoEXT* locateInfo, XrHandJointLocationsEXT* locations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateHandTrackerEXT(
+ XrSession session,
+ const XrHandTrackerCreateInfoEXT* createInfo,
+ XrHandTrackerEXT* handTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyHandTrackerEXT(
+ XrHandTrackerEXT handTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateHandJointsEXT(
+ XrHandTrackerEXT handTracker,
+ const XrHandJointsLocateInfoEXT* locateInfo,
+ XrHandJointLocationsEXT* locations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_hand_tracking_mesh 1
+#define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 4
+#define XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME "XR_MSFT_hand_tracking_mesh"
+
+typedef enum XrHandPoseTypeMSFT {
+ XR_HAND_POSE_TYPE_TRACKED_MSFT = 0,
+ XR_HAND_POSE_TYPE_REFERENCE_OPEN_PALM_MSFT = 1,
+ XR_HAND_POSE_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrHandPoseTypeMSFT;
+// XrSystemHandTrackingMeshPropertiesMSFT extends XrSystemProperties
+typedef struct XrSystemHandTrackingMeshPropertiesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsHandTrackingMesh;
+ uint32_t maxHandMeshIndexCount;
+ uint32_t maxHandMeshVertexCount;
+} XrSystemHandTrackingMeshPropertiesMSFT;
+
+typedef struct XrHandMeshSpaceCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrHandPoseTypeMSFT handPoseType;
+ XrPosef poseInHandMeshSpace;
+} XrHandMeshSpaceCreateInfoMSFT;
+
+typedef struct XrHandMeshUpdateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+ XrHandPoseTypeMSFT handPoseType;
+} XrHandMeshUpdateInfoMSFT;
+
+typedef struct XrHandMeshIndexBufferMSFT {
+ uint32_t indexBufferKey;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrHandMeshIndexBufferMSFT;
+
+typedef struct XrHandMeshVertexMSFT {
+ XrVector3f position;
+ XrVector3f normal;
+} XrHandMeshVertexMSFT;
+
+typedef struct XrHandMeshVertexBufferMSFT {
+ XrTime vertexUpdateTime;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrHandMeshVertexMSFT* vertices;
+} XrHandMeshVertexBufferMSFT;
+
+typedef struct XrHandMeshMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ XrBool32 indexBufferChanged;
+ XrBool32 vertexBufferChanged;
+ XrHandMeshIndexBufferMSFT indexBuffer;
+ XrHandMeshVertexBufferMSFT vertexBuffer;
+} XrHandMeshMSFT;
+
+// XrHandPoseTypeInfoMSFT extends XrHandTrackerCreateInfoEXT
+typedef struct XrHandPoseTypeInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrHandPoseTypeMSFT handPoseType;
+} XrHandPoseTypeInfoMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateHandMeshSpaceMSFT)(XrHandTrackerEXT handTracker, const XrHandMeshSpaceCreateInfoMSFT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateHandMeshMSFT)(XrHandTrackerEXT handTracker, const XrHandMeshUpdateInfoMSFT* updateInfo, XrHandMeshMSFT* handMesh);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateHandMeshSpaceMSFT(
+ XrHandTrackerEXT handTracker,
+ const XrHandMeshSpaceCreateInfoMSFT* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateHandMeshMSFT(
+ XrHandTrackerEXT handTracker,
+ const XrHandMeshUpdateInfoMSFT* updateInfo,
+ XrHandMeshMSFT* handMesh);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_secondary_view_configuration is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_secondary_view_configuration 1
+#define XR_MSFT_secondary_view_configuration_SPEC_VERSION 1
+#define XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME "XR_MSFT_secondary_view_configuration"
+// XrSecondaryViewConfigurationSessionBeginInfoMSFT extends XrSessionBeginInfo
+typedef struct XrSecondaryViewConfigurationSessionBeginInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t viewConfigurationCount;
+ const XrViewConfigurationType* enabledViewConfigurationTypes;
+} XrSecondaryViewConfigurationSessionBeginInfoMSFT;
+
+typedef struct XrSecondaryViewConfigurationStateMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+ XrBool32 active;
+} XrSecondaryViewConfigurationStateMSFT;
+
+// XrSecondaryViewConfigurationFrameStateMSFT extends XrFrameState
+typedef struct XrSecondaryViewConfigurationFrameStateMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t viewConfigurationCount;
+ XrSecondaryViewConfigurationStateMSFT* viewConfigurationStates;
+} XrSecondaryViewConfigurationFrameStateMSFT;
+
+typedef struct XrSecondaryViewConfigurationLayerInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+ XrEnvironmentBlendMode environmentBlendMode;
+ uint32_t layerCount;
+ const XrCompositionLayerBaseHeader* const* layers;
+} XrSecondaryViewConfigurationLayerInfoMSFT;
+
+// XrSecondaryViewConfigurationFrameEndInfoMSFT extends XrFrameEndInfo
+typedef struct XrSecondaryViewConfigurationFrameEndInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t viewConfigurationCount;
+ const XrSecondaryViewConfigurationLayerInfoMSFT* viewConfigurationLayersInfo;
+} XrSecondaryViewConfigurationFrameEndInfoMSFT;
+
+// XrSecondaryViewConfigurationSwapchainCreateInfoMSFT extends XrSwapchainCreateInfo
+typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+} XrSecondaryViewConfigurationSwapchainCreateInfoMSFT;
+
+
+
+// XR_MSFT_first_person_observer is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_first_person_observer 1
+#define XR_MSFT_first_person_observer_SPEC_VERSION 1
+#define XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME "XR_MSFT_first_person_observer"
+
+
+// XR_MSFT_controller_model is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_controller_model 1
+
+#define XR_NULL_CONTROLLER_MODEL_KEY_MSFT 0
+
+XR_DEFINE_ATOM(XrControllerModelKeyMSFT)
+#define XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT 64
+#define XR_MSFT_controller_model_SPEC_VERSION 2
+#define XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME "XR_MSFT_controller_model"
+typedef struct XrControllerModelKeyStateMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrControllerModelKeyMSFT modelKey;
+} XrControllerModelKeyStateMSFT;
+
+typedef struct XrControllerModelNodePropertiesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ char parentNodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT];
+ char nodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT];
+} XrControllerModelNodePropertiesMSFT;
+
+typedef struct XrControllerModelPropertiesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t nodeCapacityInput;
+ uint32_t nodeCountOutput;
+ XrControllerModelNodePropertiesMSFT* nodeProperties;
+} XrControllerModelPropertiesMSFT;
+
+typedef struct XrControllerModelNodeStateMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPosef nodePose;
+} XrControllerModelNodeStateMSFT;
+
+typedef struct XrControllerModelStateMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t nodeCapacityInput;
+ uint32_t nodeCountOutput;
+ XrControllerModelNodeStateMSFT* nodeStates;
+} XrControllerModelStateMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetControllerModelKeyMSFT)(XrSession session, XrPath topLevelUserPath, XrControllerModelKeyStateMSFT* controllerModelKeyState);
+typedef XrResult (XRAPI_PTR *PFN_xrLoadControllerModelMSFT)(XrSession session, XrControllerModelKeyMSFT modelKey, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint8_t* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetControllerModelPropertiesMSFT)(XrSession session, XrControllerModelKeyMSFT modelKey, XrControllerModelPropertiesMSFT* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrGetControllerModelStateMSFT)(XrSession session, XrControllerModelKeyMSFT modelKey, XrControllerModelStateMSFT* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelKeyMSFT(
+ XrSession session,
+ XrPath topLevelUserPath,
+ XrControllerModelKeyStateMSFT* controllerModelKeyState);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLoadControllerModelMSFT(
+ XrSession session,
+ XrControllerModelKeyMSFT modelKey,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ uint8_t* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelPropertiesMSFT(
+ XrSession session,
+ XrControllerModelKeyMSFT modelKey,
+ XrControllerModelPropertiesMSFT* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT(
+ XrSession session,
+ XrControllerModelKeyMSFT modelKey,
+ XrControllerModelStateMSFT* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_win32_appcontainer_compatible is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_win32_appcontainer_compatible 1
+#define XR_EXT_win32_appcontainer_compatible_SPEC_VERSION 1
+#define XR_EXT_WIN32_APPCONTAINER_COMPATIBLE_EXTENSION_NAME "XR_EXT_win32_appcontainer_compatible"
+
+
+// XR_EPIC_view_configuration_fov is a preprocessor guard. Do not pass it to API calls.
+#define XR_EPIC_view_configuration_fov 1
+#define XR_EPIC_view_configuration_fov_SPEC_VERSION 2
+#define XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME "XR_EPIC_view_configuration_fov"
+// XrViewConfigurationViewFovEPIC extends XrViewConfigurationView
+typedef struct XrViewConfigurationViewFovEPIC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFovf recommendedFov;
+ XrFovf maxMutableFov;
+} XrViewConfigurationViewFovEPIC;
+
+
+
+// XR_MSFT_composition_layer_reprojection is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_composition_layer_reprojection 1
+#define XR_MSFT_composition_layer_reprojection_SPEC_VERSION 1
+#define XR_MSFT_COMPOSITION_LAYER_REPROJECTION_EXTENSION_NAME "XR_MSFT_composition_layer_reprojection"
+
+typedef enum XrReprojectionModeMSFT {
+ XR_REPROJECTION_MODE_DEPTH_MSFT = 1,
+ XR_REPROJECTION_MODE_PLANAR_FROM_DEPTH_MSFT = 2,
+ XR_REPROJECTION_MODE_PLANAR_MANUAL_MSFT = 3,
+ XR_REPROJECTION_MODE_ORIENTATION_ONLY_MSFT = 4,
+ XR_REPROJECTION_MODE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrReprojectionModeMSFT;
+// XrCompositionLayerReprojectionInfoMSFT extends XrCompositionLayerProjection
+typedef struct XrCompositionLayerReprojectionInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrReprojectionModeMSFT reprojectionMode;
+} XrCompositionLayerReprojectionInfoMSFT;
+
+// XrCompositionLayerReprojectionPlaneOverrideMSFT extends XrCompositionLayerProjection
+typedef struct XrCompositionLayerReprojectionPlaneOverrideMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVector3f position;
+ XrVector3f normal;
+ XrVector3f velocity;
+} XrCompositionLayerReprojectionPlaneOverrideMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateReprojectionModesMSFT)(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t modeCapacityInput, uint32_t* modeCountOutput, XrReprojectionModeMSFT* modes);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReprojectionModesMSFT(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrViewConfigurationType viewConfigurationType,
+ uint32_t modeCapacityInput,
+ uint32_t* modeCountOutput,
+ XrReprojectionModeMSFT* modes);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HUAWEI_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HUAWEI_controller_interaction 1
+#define XR_HUAWEI_controller_interaction_SPEC_VERSION 1
+#define XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HUAWEI_controller_interaction"
+
+
+// XR_FB_swapchain_update_state is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_swapchain_update_state 1
+#define XR_FB_swapchain_update_state_SPEC_VERSION 3
+#define XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME "XR_FB_swapchain_update_state"
+typedef struct XR_MAY_ALIAS XrSwapchainStateBaseHeaderFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrSwapchainStateBaseHeaderFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSwapchainFB)(XrSwapchain swapchain, const XrSwapchainStateBaseHeaderFB* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSwapchainStateFB)(XrSwapchain swapchain, XrSwapchainStateBaseHeaderFB* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSwapchainFB(
+ XrSwapchain swapchain,
+ const XrSwapchainStateBaseHeaderFB* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSwapchainStateFB(
+ XrSwapchain swapchain,
+ XrSwapchainStateBaseHeaderFB* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_composition_layer_secure_content is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_composition_layer_secure_content 1
+#define XR_FB_composition_layer_secure_content_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME "XR_FB_composition_layer_secure_content"
+// XrCompositionLayerSecureContentFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerSecureContentFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerSecureContentFlagsFB flags;
+} XrCompositionLayerSecureContentFB;
+
+
+
+// XR_FB_body_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_body_tracking 1
+XR_DEFINE_HANDLE(XrBodyTrackerFB)
+#define XR_FB_body_tracking_SPEC_VERSION 1
+#define XR_FB_BODY_TRACKING_EXTENSION_NAME "XR_FB_body_tracking"
+
+typedef enum XrBodyJointFB {
+ XR_BODY_JOINT_ROOT_FB = 0,
+ XR_BODY_JOINT_HIPS_FB = 1,
+ XR_BODY_JOINT_SPINE_LOWER_FB = 2,
+ XR_BODY_JOINT_SPINE_MIDDLE_FB = 3,
+ XR_BODY_JOINT_SPINE_UPPER_FB = 4,
+ XR_BODY_JOINT_CHEST_FB = 5,
+ XR_BODY_JOINT_NECK_FB = 6,
+ XR_BODY_JOINT_HEAD_FB = 7,
+ XR_BODY_JOINT_LEFT_SHOULDER_FB = 8,
+ XR_BODY_JOINT_LEFT_SCAPULA_FB = 9,
+ XR_BODY_JOINT_LEFT_ARM_UPPER_FB = 10,
+ XR_BODY_JOINT_LEFT_ARM_LOWER_FB = 11,
+ XR_BODY_JOINT_LEFT_HAND_WRIST_TWIST_FB = 12,
+ XR_BODY_JOINT_RIGHT_SHOULDER_FB = 13,
+ XR_BODY_JOINT_RIGHT_SCAPULA_FB = 14,
+ XR_BODY_JOINT_RIGHT_ARM_UPPER_FB = 15,
+ XR_BODY_JOINT_RIGHT_ARM_LOWER_FB = 16,
+ XR_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_FB = 17,
+ XR_BODY_JOINT_LEFT_HAND_PALM_FB = 18,
+ XR_BODY_JOINT_LEFT_HAND_WRIST_FB = 19,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_FB = 20,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_FB = 21,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_FB = 22,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_TIP_FB = 23,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_FB = 24,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_FB = 25,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_FB = 26,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_FB = 27,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_TIP_FB = 28,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_FB = 29,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_FB = 30,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_FB = 31,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_FB = 32,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_FB = 33,
+ XR_BODY_JOINT_LEFT_HAND_RING_METACARPAL_FB = 34,
+ XR_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_FB = 35,
+ XR_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_FB = 36,
+ XR_BODY_JOINT_LEFT_HAND_RING_DISTAL_FB = 37,
+ XR_BODY_JOINT_LEFT_HAND_RING_TIP_FB = 38,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_FB = 39,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_FB = 40,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_FB = 41,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_FB = 42,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_TIP_FB = 43,
+ XR_BODY_JOINT_RIGHT_HAND_PALM_FB = 44,
+ XR_BODY_JOINT_RIGHT_HAND_WRIST_FB = 45,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_FB = 46,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_FB = 47,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_FB = 48,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_TIP_FB = 49,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_FB = 50,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_FB = 51,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_FB = 52,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_FB = 53,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_TIP_FB = 54,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_FB = 55,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_FB = 56,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_FB = 57,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_FB = 58,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_FB = 59,
+ XR_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_FB = 60,
+ XR_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_FB = 61,
+ XR_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_FB = 62,
+ XR_BODY_JOINT_RIGHT_HAND_RING_DISTAL_FB = 63,
+ XR_BODY_JOINT_RIGHT_HAND_RING_TIP_FB = 64,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_FB = 65,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_FB = 66,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_FB = 67,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_FB = 68,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_FB = 69,
+ XR_BODY_JOINT_COUNT_FB = 70,
+ XR_BODY_JOINT_NONE_FB = -1,
+ XR_BODY_JOINT_MAX_ENUM_FB = 0x7FFFFFFF
+} XrBodyJointFB;
+
+typedef enum XrBodyJointSetFB {
+ XR_BODY_JOINT_SET_DEFAULT_FB = 0,
+ XR_BODY_JOINT_SET_FULL_BODY_META = 1000274000,
+ XR_BODY_JOINT_SET_MAX_ENUM_FB = 0x7FFFFFFF
+} XrBodyJointSetFB;
+typedef struct XrBodyJointLocationFB {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrBodyJointLocationFB;
+
+// XrSystemBodyTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemBodyTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTracking;
+} XrSystemBodyTrackingPropertiesFB;
+
+typedef struct XrBodyTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBodyJointSetFB bodyJointSet;
+} XrBodyTrackerCreateInfoFB;
+
+typedef struct XrBodySkeletonJointFB {
+ int32_t joint;
+ int32_t parentJoint;
+ XrPosef pose;
+} XrBodySkeletonJointFB;
+
+typedef struct XrBodySkeletonFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCount;
+ XrBodySkeletonJointFB* joints;
+} XrBodySkeletonFB;
+
+typedef struct XrBodyJointsLocateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrBodyJointsLocateInfoFB;
+
+typedef struct XrBodyJointLocationsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ float confidence;
+ uint32_t jointCount;
+ XrBodyJointLocationFB* jointLocations;
+ uint32_t skeletonChangedCount;
+ XrTime time;
+} XrBodyJointLocationsFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateBodyTrackerFB)(XrSession session, const XrBodyTrackerCreateInfoFB* createInfo, XrBodyTrackerFB* bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyBodyTrackerFB)(XrBodyTrackerFB bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateBodyJointsFB)(XrBodyTrackerFB bodyTracker, const XrBodyJointsLocateInfoFB* locateInfo, XrBodyJointLocationsFB* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetBodySkeletonFB)(XrBodyTrackerFB bodyTracker, XrBodySkeletonFB* skeleton);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateBodyTrackerFB(
+ XrSession session,
+ const XrBodyTrackerCreateInfoFB* createInfo,
+ XrBodyTrackerFB* bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyBodyTrackerFB(
+ XrBodyTrackerFB bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsFB(
+ XrBodyTrackerFB bodyTracker,
+ const XrBodyJointsLocateInfoFB* locateInfo,
+ XrBodyJointLocationsFB* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonFB(
+ XrBodyTrackerFB bodyTracker,
+ XrBodySkeletonFB* skeleton);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_dpad_binding is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_dpad_binding 1
+#define XR_EXT_dpad_binding_SPEC_VERSION 1
+#define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding"
+typedef struct XrInteractionProfileDpadBindingEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPath binding;
+ XrActionSet actionSet;
+ float forceThreshold;
+ float forceThresholdReleased;
+ float centerRegion;
+ float wedgeAngle;
+ XrBool32 isSticky;
+ const XrHapticBaseHeader* onHaptic;
+ const XrHapticBaseHeader* offHaptic;
+} XrInteractionProfileDpadBindingEXT;
+
+
+
+// XR_VALVE_analog_threshold is a preprocessor guard. Do not pass it to API calls.
+#define XR_VALVE_analog_threshold 1
+#define XR_VALVE_analog_threshold_SPEC_VERSION 2
+#define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold"
+typedef struct XrInteractionProfileAnalogThresholdVALVE {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAction action;
+ XrPath binding;
+ float onThreshold;
+ float offThreshold;
+ const XrHapticBaseHeader* onHaptic;
+ const XrHapticBaseHeader* offHaptic;
+} XrInteractionProfileAnalogThresholdVALVE;
+
+
+
+// XR_EXT_hand_joints_motion_range is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_hand_joints_motion_range 1
+#define XR_EXT_hand_joints_motion_range_SPEC_VERSION 1
+#define XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME "XR_EXT_hand_joints_motion_range"
+
+typedef enum XrHandJointsMotionRangeEXT {
+ XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT = 1,
+ XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT = 2,
+ XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandJointsMotionRangeEXT;
+// XrHandJointsMotionRangeInfoEXT extends XrHandJointsLocateInfoEXT
+typedef struct XrHandJointsMotionRangeInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrHandJointsMotionRangeEXT handJointsMotionRange;
+} XrHandJointsMotionRangeInfoEXT;
+
+
+
+// XR_EXT_samsung_odyssey_controller is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_samsung_odyssey_controller 1
+#define XR_EXT_samsung_odyssey_controller_SPEC_VERSION 1
+#define XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME "XR_EXT_samsung_odyssey_controller"
+
+
+// XR_EXT_hp_mixed_reality_controller is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_hp_mixed_reality_controller 1
+#define XR_EXT_hp_mixed_reality_controller_SPEC_VERSION 1
+#define XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME "XR_EXT_hp_mixed_reality_controller"
+
+
+// XR_MND_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls.
+#define XR_MND_swapchain_usage_input_attachment_bit 1
+#define XR_MND_swapchain_usage_input_attachment_bit_SPEC_VERSION 2
+#define XR_MND_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_MND_swapchain_usage_input_attachment_bit"
+
+
+// XR_MSFT_scene_understanding is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_scene_understanding 1
+
+ XR_DEFINE_HANDLE(XrSceneMSFT)
+
+
+ XR_DEFINE_HANDLE(XrSceneObserverMSFT)
+
+#define XR_MSFT_scene_understanding_SPEC_VERSION 2
+#define XR_MSFT_SCENE_UNDERSTANDING_EXTENSION_NAME "XR_MSFT_scene_understanding"
+
+typedef enum XrSceneComputeFeatureMSFT {
+ XR_SCENE_COMPUTE_FEATURE_PLANE_MSFT = 1,
+ XR_SCENE_COMPUTE_FEATURE_PLANE_MESH_MSFT = 2,
+ XR_SCENE_COMPUTE_FEATURE_VISUAL_MESH_MSFT = 3,
+ XR_SCENE_COMPUTE_FEATURE_COLLIDER_MESH_MSFT = 4,
+ XR_SCENE_COMPUTE_FEATURE_SERIALIZE_SCENE_MSFT = 1000098000,
+ XR_SCENE_COMPUTE_FEATURE_MARKER_MSFT = 1000147000,
+ XR_SCENE_COMPUTE_FEATURE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneComputeFeatureMSFT;
+
+typedef enum XrSceneComputeConsistencyMSFT {
+ XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_COMPLETE_MSFT = 1,
+ XR_SCENE_COMPUTE_CONSISTENCY_SNAPSHOT_INCOMPLETE_FAST_MSFT = 2,
+ XR_SCENE_COMPUTE_CONSISTENCY_OCCLUSION_OPTIMIZED_MSFT = 3,
+ XR_SCENE_COMPUTE_CONSISTENCY_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneComputeConsistencyMSFT;
+
+typedef enum XrMeshComputeLodMSFT {
+ XR_MESH_COMPUTE_LOD_COARSE_MSFT = 1,
+ XR_MESH_COMPUTE_LOD_MEDIUM_MSFT = 2,
+ XR_MESH_COMPUTE_LOD_FINE_MSFT = 3,
+ XR_MESH_COMPUTE_LOD_UNLIMITED_MSFT = 4,
+ XR_MESH_COMPUTE_LOD_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrMeshComputeLodMSFT;
+
+typedef enum XrSceneComponentTypeMSFT {
+ XR_SCENE_COMPONENT_TYPE_INVALID_MSFT = -1,
+ XR_SCENE_COMPONENT_TYPE_OBJECT_MSFT = 1,
+ XR_SCENE_COMPONENT_TYPE_PLANE_MSFT = 2,
+ XR_SCENE_COMPONENT_TYPE_VISUAL_MESH_MSFT = 3,
+ XR_SCENE_COMPONENT_TYPE_COLLIDER_MESH_MSFT = 4,
+ XR_SCENE_COMPONENT_TYPE_SERIALIZED_SCENE_FRAGMENT_MSFT = 1000098000,
+ XR_SCENE_COMPONENT_TYPE_MARKER_MSFT = 1000147000,
+ XR_SCENE_COMPONENT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneComponentTypeMSFT;
+
+typedef enum XrSceneObjectTypeMSFT {
+ XR_SCENE_OBJECT_TYPE_UNCATEGORIZED_MSFT = -1,
+ XR_SCENE_OBJECT_TYPE_BACKGROUND_MSFT = 1,
+ XR_SCENE_OBJECT_TYPE_WALL_MSFT = 2,
+ XR_SCENE_OBJECT_TYPE_FLOOR_MSFT = 3,
+ XR_SCENE_OBJECT_TYPE_CEILING_MSFT = 4,
+ XR_SCENE_OBJECT_TYPE_PLATFORM_MSFT = 5,
+ XR_SCENE_OBJECT_TYPE_INFERRED_MSFT = 6,
+ XR_SCENE_OBJECT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneObjectTypeMSFT;
+
+typedef enum XrScenePlaneAlignmentTypeMSFT {
+ XR_SCENE_PLANE_ALIGNMENT_TYPE_NON_ORTHOGONAL_MSFT = 0,
+ XR_SCENE_PLANE_ALIGNMENT_TYPE_HORIZONTAL_MSFT = 1,
+ XR_SCENE_PLANE_ALIGNMENT_TYPE_VERTICAL_MSFT = 2,
+ XR_SCENE_PLANE_ALIGNMENT_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrScenePlaneAlignmentTypeMSFT;
+
+typedef enum XrSceneComputeStateMSFT {
+ XR_SCENE_COMPUTE_STATE_NONE_MSFT = 0,
+ XR_SCENE_COMPUTE_STATE_UPDATING_MSFT = 1,
+ XR_SCENE_COMPUTE_STATE_COMPLETED_MSFT = 2,
+ XR_SCENE_COMPUTE_STATE_COMPLETED_WITH_ERROR_MSFT = 3,
+ XR_SCENE_COMPUTE_STATE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneComputeStateMSFT;
+typedef struct XrUuidMSFT {
+ uint8_t bytes[16];
+} XrUuidMSFT;
+
+typedef struct XrSceneObserverCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSceneObserverCreateInfoMSFT;
+
+typedef struct XrSceneCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSceneCreateInfoMSFT;
+
+typedef struct XrSceneSphereBoundMSFT {
+ XrVector3f center;
+ float radius;
+} XrSceneSphereBoundMSFT;
+
+typedef struct XrSceneOrientedBoxBoundMSFT {
+ XrPosef pose;
+ XrVector3f extents;
+} XrSceneOrientedBoxBoundMSFT;
+
+typedef struct XrSceneFrustumBoundMSFT {
+ XrPosef pose;
+ XrFovf fov;
+ float farDistance;
+} XrSceneFrustumBoundMSFT;
+
+typedef struct XrSceneBoundsMSFT {
+ XrSpace space;
+ XrTime time;
+ uint32_t sphereCount;
+ const XrSceneSphereBoundMSFT* spheres;
+ uint32_t boxCount;
+ const XrSceneOrientedBoxBoundMSFT* boxes;
+ uint32_t frustumCount;
+ const XrSceneFrustumBoundMSFT* frustums;
+} XrSceneBoundsMSFT;
+
+typedef struct XrNewSceneComputeInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestedFeatureCount;
+ const XrSceneComputeFeatureMSFT* requestedFeatures;
+ XrSceneComputeConsistencyMSFT consistency;
+ XrSceneBoundsMSFT bounds;
+} XrNewSceneComputeInfoMSFT;
+
+// XrVisualMeshComputeLodInfoMSFT extends XrNewSceneComputeInfoMSFT
+typedef struct XrVisualMeshComputeLodInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMeshComputeLodMSFT lod;
+} XrVisualMeshComputeLodInfoMSFT;
+
+typedef struct XrSceneComponentMSFT {
+ XrSceneComponentTypeMSFT componentType;
+ XrUuidMSFT id;
+ XrUuidMSFT parentId;
+ XrTime updateTime;
+} XrSceneComponentMSFT;
+
+typedef struct XrSceneComponentsMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t componentCapacityInput;
+ uint32_t componentCountOutput;
+ XrSceneComponentMSFT* components;
+} XrSceneComponentsMSFT;
+
+typedef struct XrSceneComponentsGetInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSceneComponentTypeMSFT componentType;
+} XrSceneComponentsGetInfoMSFT;
+
+typedef struct XrSceneComponentLocationMSFT {
+ XrSpaceLocationFlags flags;
+ XrPosef pose;
+} XrSceneComponentLocationMSFT;
+
+typedef struct XrSceneComponentLocationsMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrSceneComponentLocationMSFT* locations;
+} XrSceneComponentLocationsMSFT;
+
+typedef struct XrSceneComponentsLocateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ uint32_t componentIdCount;
+ const XrUuidMSFT* componentIds;
+} XrSceneComponentsLocateInfoMSFT;
+
+typedef struct XrSceneObjectMSFT {
+ XrSceneObjectTypeMSFT objectType;
+} XrSceneObjectMSFT;
+
+// XrSceneObjectsMSFT extends XrSceneComponentsMSFT
+typedef struct XrSceneObjectsMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t sceneObjectCount;
+ XrSceneObjectMSFT* sceneObjects;
+} XrSceneObjectsMSFT;
+
+// XrSceneComponentParentFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT
+typedef struct XrSceneComponentParentFilterInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidMSFT parentId;
+} XrSceneComponentParentFilterInfoMSFT;
+
+// XrSceneObjectTypesFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT
+typedef struct XrSceneObjectTypesFilterInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t objectTypeCount;
+ const XrSceneObjectTypeMSFT* objectTypes;
+} XrSceneObjectTypesFilterInfoMSFT;
+
+typedef struct XrScenePlaneMSFT {
+ XrScenePlaneAlignmentTypeMSFT alignment;
+ XrExtent2Df size;
+ uint64_t meshBufferId;
+ XrBool32 supportsIndicesUint16;
+} XrScenePlaneMSFT;
+
+// XrScenePlanesMSFT extends XrSceneComponentsMSFT
+typedef struct XrScenePlanesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t scenePlaneCount;
+ XrScenePlaneMSFT* scenePlanes;
+} XrScenePlanesMSFT;
+
+// XrScenePlaneAlignmentFilterInfoMSFT extends XrSceneComponentsGetInfoMSFT
+typedef struct XrScenePlaneAlignmentFilterInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t alignmentCount;
+ const XrScenePlaneAlignmentTypeMSFT* alignments;
+} XrScenePlaneAlignmentFilterInfoMSFT;
+
+typedef struct XrSceneMeshMSFT {
+ uint64_t meshBufferId;
+ XrBool32 supportsIndicesUint16;
+} XrSceneMeshMSFT;
+
+// XrSceneMeshesMSFT extends XrSceneComponentsMSFT
+typedef struct XrSceneMeshesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t sceneMeshCount;
+ XrSceneMeshMSFT* sceneMeshes;
+} XrSceneMeshesMSFT;
+
+typedef struct XrSceneMeshBuffersGetInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint64_t meshBufferId;
+} XrSceneMeshBuffersGetInfoMSFT;
+
+typedef struct XrSceneMeshBuffersMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrSceneMeshBuffersMSFT;
+
+// XrSceneMeshVertexBufferMSFT extends XrSceneMeshBuffersMSFT
+typedef struct XrSceneMeshVertexBufferMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertices;
+} XrSceneMeshVertexBufferMSFT;
+
+// XrSceneMeshIndicesUint32MSFT extends XrSceneMeshBuffersMSFT
+typedef struct XrSceneMeshIndicesUint32MSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrSceneMeshIndicesUint32MSFT;
+
+// XrSceneMeshIndicesUint16MSFT extends XrSceneMeshBuffersMSFT
+typedef struct XrSceneMeshIndicesUint16MSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint16_t* indices;
+} XrSceneMeshIndicesUint16MSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSceneComputeFeaturesMSFT)(XrInstance instance, XrSystemId systemId, uint32_t featureCapacityInput, uint32_t* featureCountOutput, XrSceneComputeFeatureMSFT* features);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneObserverMSFT)(XrSession session, const XrSceneObserverCreateInfoMSFT* createInfo, XrSceneObserverMSFT* sceneObserver);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneObserverMSFT)(XrSceneObserverMSFT sceneObserver);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrSceneCreateInfoMSFT* createInfo, XrSceneMSFT* scene);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneMSFT)(XrSceneMSFT scene);
+typedef XrResult (XRAPI_PTR *PFN_xrComputeNewSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrNewSceneComputeInfoMSFT* computeInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSceneComputeStateMSFT)(XrSceneObserverMSFT sceneObserver, XrSceneComputeStateMSFT* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSceneComponentsMSFT)(XrSceneMSFT scene, const XrSceneComponentsGetInfoMSFT* getInfo, XrSceneComponentsMSFT* components);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSceneComponentsMSFT)(XrSceneMSFT scene, const XrSceneComponentsLocateInfoMSFT* locateInfo, XrSceneComponentLocationsMSFT* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSceneMeshBuffersMSFT)(XrSceneMSFT scene, const XrSceneMeshBuffersGetInfoMSFT* getInfo, XrSceneMeshBuffersMSFT* buffers);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSceneComputeFeaturesMSFT(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t featureCapacityInput,
+ uint32_t* featureCountOutput,
+ XrSceneComputeFeatureMSFT* features);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneObserverMSFT(
+ XrSession session,
+ const XrSceneObserverCreateInfoMSFT* createInfo,
+ XrSceneObserverMSFT* sceneObserver);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneObserverMSFT(
+ XrSceneObserverMSFT sceneObserver);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneMSFT(
+ XrSceneObserverMSFT sceneObserver,
+ const XrSceneCreateInfoMSFT* createInfo,
+ XrSceneMSFT* scene);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneMSFT(
+ XrSceneMSFT scene);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrComputeNewSceneMSFT(
+ XrSceneObserverMSFT sceneObserver,
+ const XrNewSceneComputeInfoMSFT* computeInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneComputeStateMSFT(
+ XrSceneObserverMSFT sceneObserver,
+ XrSceneComputeStateMSFT* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneComponentsMSFT(
+ XrSceneMSFT scene,
+ const XrSceneComponentsGetInfoMSFT* getInfo,
+ XrSceneComponentsMSFT* components);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSceneComponentsMSFT(
+ XrSceneMSFT scene,
+ const XrSceneComponentsLocateInfoMSFT* locateInfo,
+ XrSceneComponentLocationsMSFT* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMeshBuffersMSFT(
+ XrSceneMSFT scene,
+ const XrSceneMeshBuffersGetInfoMSFT* getInfo,
+ XrSceneMeshBuffersMSFT* buffers);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_scene_understanding_serialization is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_scene_understanding_serialization 1
+#define XR_MSFT_scene_understanding_serialization_SPEC_VERSION 2
+#define XR_MSFT_SCENE_UNDERSTANDING_SERIALIZATION_EXTENSION_NAME "XR_MSFT_scene_understanding_serialization"
+typedef struct XrSerializedSceneFragmentDataGetInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidMSFT sceneFragmentId;
+} XrSerializedSceneFragmentDataGetInfoMSFT;
+
+typedef struct XrDeserializeSceneFragmentMSFT {
+ uint32_t bufferSize;
+ const uint8_t* buffer;
+} XrDeserializeSceneFragmentMSFT;
+
+typedef struct XrSceneDeserializeInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t fragmentCount;
+ const XrDeserializeSceneFragmentMSFT* fragments;
+} XrSceneDeserializeInfoMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrDeserializeSceneMSFT)(XrSceneObserverMSFT sceneObserver, const XrSceneDeserializeInfoMSFT* deserializeInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSerializedSceneFragmentDataMSFT)(XrSceneMSFT scene, const XrSerializedSceneFragmentDataGetInfoMSFT* getInfo, uint32_t countInput, uint32_t* readOutput, uint8_t* buffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrDeserializeSceneMSFT(
+ XrSceneObserverMSFT sceneObserver,
+ const XrSceneDeserializeInfoMSFT* deserializeInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSerializedSceneFragmentDataMSFT(
+ XrSceneMSFT scene,
+ const XrSerializedSceneFragmentDataGetInfoMSFT* getInfo,
+ uint32_t countInput,
+ uint32_t* readOutput,
+ uint8_t* buffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_display_refresh_rate is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_display_refresh_rate 1
+#define XR_FB_display_refresh_rate_SPEC_VERSION 1
+#define XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME "XR_FB_display_refresh_rate"
+typedef struct XrEventDataDisplayRefreshRateChangedFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float fromDisplayRefreshRate;
+ float toDisplayRefreshRate;
+} XrEventDataDisplayRefreshRateChangedFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateDisplayRefreshRatesFB)(XrSession session, uint32_t displayRefreshRateCapacityInput, uint32_t* displayRefreshRateCountOutput, float* displayRefreshRates);
+typedef XrResult (XRAPI_PTR *PFN_xrGetDisplayRefreshRateFB)(XrSession session, float* displayRefreshRate);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestDisplayRefreshRateFB)(XrSession session, float displayRefreshRate);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateDisplayRefreshRatesFB(
+ XrSession session,
+ uint32_t displayRefreshRateCapacityInput,
+ uint32_t* displayRefreshRateCountOutput,
+ float* displayRefreshRates);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetDisplayRefreshRateFB(
+ XrSession session,
+ float* displayRefreshRate);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB(
+ XrSession session,
+ float displayRefreshRate);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_vive_cosmos_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_vive_cosmos_controller_interaction 1
+#define XR_HTC_vive_cosmos_controller_interaction_SPEC_VERSION 1
+#define XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_cosmos_controller_interaction"
+
+
+// XR_HTCX_vive_tracker_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTCX_vive_tracker_interaction 1
+#define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 3
+#define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction"
+typedef struct XrViveTrackerPathsHTCX {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPath persistentPath;
+ XrPath rolePath;
+} XrViveTrackerPathsHTCX;
+
+typedef struct XrEventDataViveTrackerConnectedHTCX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViveTrackerPathsHTCX* paths;
+} XrEventDataViveTrackerConnectedHTCX;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateViveTrackerPathsHTCX)(XrInstance instance, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrViveTrackerPathsHTCX* paths);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViveTrackerPathsHTCX(
+ XrInstance instance,
+ uint32_t pathCapacityInput,
+ uint32_t* pathCountOutput,
+ XrViveTrackerPathsHTCX* paths);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_facial_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_facial_tracking 1
+
+#define XR_FACIAL_EXPRESSION_EYE_COUNT_HTC 14
+
+
+#define XR_FACIAL_EXPRESSION_LIP_COUNT_HTC 37
+
+XR_DEFINE_HANDLE(XrFacialTrackerHTC)
+#define XR_HTC_facial_tracking_SPEC_VERSION 3
+#define XR_HTC_FACIAL_TRACKING_EXTENSION_NAME "XR_HTC_facial_tracking"
+
+typedef enum XrEyeExpressionHTC {
+ XR_EYE_EXPRESSION_LEFT_BLINK_HTC = 0,
+ XR_EYE_EXPRESSION_LEFT_WIDE_HTC = 1,
+ XR_EYE_EXPRESSION_RIGHT_BLINK_HTC = 2,
+ XR_EYE_EXPRESSION_RIGHT_WIDE_HTC = 3,
+ XR_EYE_EXPRESSION_LEFT_SQUEEZE_HTC = 4,
+ XR_EYE_EXPRESSION_RIGHT_SQUEEZE_HTC = 5,
+ XR_EYE_EXPRESSION_LEFT_DOWN_HTC = 6,
+ XR_EYE_EXPRESSION_RIGHT_DOWN_HTC = 7,
+ XR_EYE_EXPRESSION_LEFT_OUT_HTC = 8,
+ XR_EYE_EXPRESSION_RIGHT_IN_HTC = 9,
+ XR_EYE_EXPRESSION_LEFT_IN_HTC = 10,
+ XR_EYE_EXPRESSION_RIGHT_OUT_HTC = 11,
+ XR_EYE_EXPRESSION_LEFT_UP_HTC = 12,
+ XR_EYE_EXPRESSION_RIGHT_UP_HTC = 13,
+ XR_EYE_EXPRESSION_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrEyeExpressionHTC;
+
+typedef enum XrLipExpressionHTC {
+ XR_LIP_EXPRESSION_JAW_RIGHT_HTC = 0,
+ XR_LIP_EXPRESSION_JAW_LEFT_HTC = 1,
+ XR_LIP_EXPRESSION_JAW_FORWARD_HTC = 2,
+ XR_LIP_EXPRESSION_JAW_OPEN_HTC = 3,
+ XR_LIP_EXPRESSION_MOUTH_APE_SHAPE_HTC = 4,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_RIGHT_HTC = 5,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_LEFT_HTC = 6,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_RIGHT_HTC = 7,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_LEFT_HTC = 8,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_OVERTURN_HTC = 9,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_OVERTURN_HTC = 10,
+ XR_LIP_EXPRESSION_MOUTH_POUT_HTC = 11,
+ XR_LIP_EXPRESSION_MOUTH_RAISER_RIGHT_HTC = 12,
+ XR_LIP_EXPRESSION_MOUTH_RAISER_LEFT_HTC = 13,
+ XR_LIP_EXPRESSION_MOUTH_STRETCHER_RIGHT_HTC = 14,
+ XR_LIP_EXPRESSION_MOUTH_STRETCHER_LEFT_HTC = 15,
+ XR_LIP_EXPRESSION_CHEEK_PUFF_RIGHT_HTC = 16,
+ XR_LIP_EXPRESSION_CHEEK_PUFF_LEFT_HTC = 17,
+ XR_LIP_EXPRESSION_CHEEK_SUCK_HTC = 18,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_UPRIGHT_HTC = 19,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_UPLEFT_HTC = 20,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNRIGHT_HTC = 21,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_DOWNLEFT_HTC = 22,
+ XR_LIP_EXPRESSION_MOUTH_UPPER_INSIDE_HTC = 23,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_INSIDE_HTC = 24,
+ XR_LIP_EXPRESSION_MOUTH_LOWER_OVERLAY_HTC = 25,
+ XR_LIP_EXPRESSION_TONGUE_LONGSTEP1_HTC = 26,
+ XR_LIP_EXPRESSION_TONGUE_LEFT_HTC = 27,
+ XR_LIP_EXPRESSION_TONGUE_RIGHT_HTC = 28,
+ XR_LIP_EXPRESSION_TONGUE_UP_HTC = 29,
+ XR_LIP_EXPRESSION_TONGUE_DOWN_HTC = 30,
+ XR_LIP_EXPRESSION_TONGUE_ROLL_HTC = 31,
+ XR_LIP_EXPRESSION_TONGUE_LONGSTEP2_HTC = 32,
+ XR_LIP_EXPRESSION_TONGUE_UPRIGHT_MORPH_HTC = 33,
+ XR_LIP_EXPRESSION_TONGUE_UPLEFT_MORPH_HTC = 34,
+ XR_LIP_EXPRESSION_TONGUE_DOWNRIGHT_MORPH_HTC = 35,
+ XR_LIP_EXPRESSION_TONGUE_DOWNLEFT_MORPH_HTC = 36,
+ XR_LIP_EXPRESSION_MOUTH_SMILE_RIGHT_HTC = XR_LIP_EXPRESSION_MOUTH_RAISER_RIGHT_HTC,
+ XR_LIP_EXPRESSION_MOUTH_SMILE_LEFT_HTC = XR_LIP_EXPRESSION_MOUTH_RAISER_LEFT_HTC,
+ XR_LIP_EXPRESSION_MOUTH_SAD_RIGHT_HTC = XR_LIP_EXPRESSION_MOUTH_STRETCHER_RIGHT_HTC,
+ XR_LIP_EXPRESSION_MOUTH_SAD_LEFT_HTC = XR_LIP_EXPRESSION_MOUTH_STRETCHER_LEFT_HTC,
+ XR_LIP_EXPRESSION_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrLipExpressionHTC;
+
+typedef enum XrFacialTrackingTypeHTC {
+ XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC = 1,
+ XR_FACIAL_TRACKING_TYPE_LIP_DEFAULT_HTC = 2,
+ XR_FACIAL_TRACKING_TYPE_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrFacialTrackingTypeHTC;
+// XrSystemFacialTrackingPropertiesHTC extends XrSystemProperties
+typedef struct XrSystemFacialTrackingPropertiesHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportEyeFacialTracking;
+ XrBool32 supportLipFacialTracking;
+} XrSystemFacialTrackingPropertiesHTC;
+
+typedef struct XrFacialExpressionsHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ XrTime sampleTime;
+ uint32_t expressionCount;
+ float* expressionWeightings;
+} XrFacialExpressionsHTC;
+
+typedef struct XrFacialTrackerCreateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFacialTrackingTypeHTC facialTrackingType;
+} XrFacialTrackerCreateInfoHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFacialTrackerHTC)(XrSession session, const XrFacialTrackerCreateInfoHTC* createInfo, XrFacialTrackerHTC* facialTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFacialTrackerHTC)(XrFacialTrackerHTC facialTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFacialExpressionsHTC)(XrFacialTrackerHTC facialTracker, XrFacialExpressionsHTC* facialExpressions);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFacialTrackerHTC(
+ XrSession session,
+ const XrFacialTrackerCreateInfoHTC* createInfo,
+ XrFacialTrackerHTC* facialTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFacialTrackerHTC(
+ XrFacialTrackerHTC facialTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC(
+ XrFacialTrackerHTC facialTracker,
+ XrFacialExpressionsHTC* facialExpressions);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_vive_focus3_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_vive_focus3_controller_interaction 1
+#define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 2
+#define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction"
+
+
+// XR_HTC_hand_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_hand_interaction 1
+#define XR_HTC_hand_interaction_SPEC_VERSION 1
+#define XR_HTC_HAND_INTERACTION_EXTENSION_NAME "XR_HTC_hand_interaction"
+
+
+// XR_HTC_vive_wrist_tracker_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_vive_wrist_tracker_interaction 1
+#define XR_HTC_vive_wrist_tracker_interaction_SPEC_VERSION 1
+#define XR_HTC_VIVE_WRIST_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_wrist_tracker_interaction"
+
+
+// XR_FB_color_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_color_space 1
+#define XR_FB_color_space_SPEC_VERSION 3
+#define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space"
+
+typedef enum XrColorSpaceFB {
+ XR_COLOR_SPACE_UNMANAGED_FB = 0,
+ XR_COLOR_SPACE_REC2020_FB = 1,
+ XR_COLOR_SPACE_REC709_FB = 2,
+ XR_COLOR_SPACE_RIFT_CV1_FB = 3,
+ XR_COLOR_SPACE_RIFT_S_FB = 4,
+ XR_COLOR_SPACE_QUEST_FB = 5,
+ XR_COLOR_SPACE_P3_FB = 6,
+ XR_COLOR_SPACE_ADOBE_RGB_FB = 7,
+ XR_COLOR_SPACE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrColorSpaceFB;
+// XrSystemColorSpacePropertiesFB extends XrSystemProperties
+typedef struct XrSystemColorSpacePropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrColorSpaceFB colorSpace;
+} XrSystemColorSpacePropertiesFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateColorSpacesFB)(XrSession session, uint32_t colorSpaceCapacityInput, uint32_t* colorSpaceCountOutput, XrColorSpaceFB* colorSpaces);
+typedef XrResult (XRAPI_PTR *PFN_xrSetColorSpaceFB)(XrSession session, const XrColorSpaceFB colorSpace);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateColorSpacesFB(
+ XrSession session,
+ uint32_t colorSpaceCapacityInput,
+ uint32_t* colorSpaceCountOutput,
+ XrColorSpaceFB* colorSpaces);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB(
+ XrSession session,
+ const XrColorSpaceFB colorSpace);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_hand_tracking_mesh 1
+#define XR_FB_hand_tracking_mesh_SPEC_VERSION 3
+#define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh"
+typedef struct XrVector4sFB {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+ int16_t w;
+} XrVector4sFB;
+
+typedef struct XrHandTrackingMeshFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCapacityInput;
+ uint32_t jointCountOutput;
+ XrPosef* jointBindPoses;
+ float* jointRadii;
+ XrHandJointEXT* jointParents;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertexPositions;
+ XrVector3f* vertexNormals;
+ XrVector2f* vertexUVs;
+ XrVector4sFB* vertexBlendIndices;
+ XrVector4f* vertexBlendWeights;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ int16_t* indices;
+} XrHandTrackingMeshFB;
+
+// XrHandTrackingScaleFB extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingScaleFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ float sensorOutput;
+ float currentOutput;
+ XrBool32 overrideHandScale;
+ float overrideValueInput;
+} XrHandTrackingScaleFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetHandMeshFB)(XrHandTrackerEXT handTracker, XrHandTrackingMeshFB* mesh);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB(
+ XrHandTrackerEXT handTracker,
+ XrHandTrackingMeshFB* mesh);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_hand_tracking_aim is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_hand_tracking_aim 1
+#define XR_FB_hand_tracking_aim_SPEC_VERSION 2
+#define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim"
+typedef XrFlags64 XrHandTrackingAimFlagsFB;
+
+// Flag bits for XrHandTrackingAimFlagsFB
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_COMPUTED_BIT_FB = 0x00000001;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_VALID_BIT_FB = 0x00000002;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB = 0x00000004;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB = 0x00000008;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB = 0x00000010;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB = 0x00000020;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_SYSTEM_GESTURE_BIT_FB = 0x00000040;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB = 0x00000080;
+static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB = 0x00000100;
+
+// XrHandTrackingAimStateFB extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingAimStateFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrHandTrackingAimFlagsFB status;
+ XrPosef aimPose;
+ float pinchStrengthIndex;
+ float pinchStrengthMiddle;
+ float pinchStrengthRing;
+ float pinchStrengthLittle;
+} XrHandTrackingAimStateFB;
+
+
+
+// XR_FB_hand_tracking_capsules is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_hand_tracking_capsules 1
+#define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2
+#define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19
+#define XR_FB_hand_tracking_capsules_SPEC_VERSION 3
+#define XR_FB_HAND_TRACKING_CAPSULES_EXTENSION_NAME "XR_FB_hand_tracking_capsules"
+#define XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB
+#define XR_FB_HAND_TRACKING_CAPSULE_COUNT XR_HAND_TRACKING_CAPSULE_COUNT_FB
+typedef struct XrHandCapsuleFB {
+ XrVector3f points[XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB];
+ float radius;
+ XrHandJointEXT joint;
+} XrHandCapsuleFB;
+
+// XrHandTrackingCapsulesStateFB extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingCapsulesStateFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrHandCapsuleFB capsules[XR_HAND_TRACKING_CAPSULE_COUNT_FB];
+} XrHandTrackingCapsulesStateFB;
+
+
+
+// XR_FB_spatial_entity is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity 1
+XR_DEFINE_ATOM(XrAsyncRequestIdFB)
+#define XR_FB_spatial_entity_SPEC_VERSION 3
+#define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity"
+
+typedef enum XrSpaceComponentTypeFB {
+ XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB = 0,
+ XR_SPACE_COMPONENT_TYPE_STORABLE_FB = 1,
+ XR_SPACE_COMPONENT_TYPE_SHARABLE_FB = 2,
+ XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB = 3,
+ XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB = 4,
+ XR_SPACE_COMPONENT_TYPE_SEMANTIC_LABELS_FB = 5,
+ XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB = 6,
+ XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB = 7,
+ XR_SPACE_COMPONENT_TYPE_TRIANGLE_MESH_META = 1000269000,
+ XR_SPACE_COMPONENT_TYPE_ROOM_MESH_META = 1000553000,
+ XR_SPACE_COMPONENT_TYPE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceComponentTypeFB;
+// XrSystemSpatialEntityPropertiesFB extends XrSystemProperties
+typedef struct XrSystemSpatialEntityPropertiesFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialEntity;
+} XrSystemSpatialEntityPropertiesFB;
+
+typedef struct XrSpatialAnchorCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef poseInSpace;
+ XrTime time;
+} XrSpatialAnchorCreateInfoFB;
+
+typedef struct XrSpaceComponentStatusSetInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceComponentTypeFB componentType;
+ XrBool32 enabled;
+ XrDuration timeout;
+} XrSpaceComponentStatusSetInfoFB;
+
+typedef struct XrSpaceComponentStatusFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+ XrBool32 changePending;
+} XrSpaceComponentStatusFB;
+
+typedef XrUuid XrUuidEXT;
+
+typedef struct XrEventDataSpatialAnchorCreateCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+} XrEventDataSpatialAnchorCreateCompleteFB;
+
+typedef struct XrEventDataSpaceSetStatusCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceComponentTypeFB componentType;
+ XrBool32 enabled;
+} XrEventDataSpaceSetStatusCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFB)(XrSession session, const XrSpatialAnchorCreateInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceUuidFB)(XrSpace space, XrUuidEXT* uuid);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpaceSupportedComponentsFB)(XrSpace space, uint32_t componentTypeCapacityInput, uint32_t* componentTypeCountOutput, XrSpaceComponentTypeFB* componentTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrSetSpaceComponentStatusFB)(XrSpace space, const XrSpaceComponentStatusSetInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceComponentStatusFB)(XrSpace space, XrSpaceComponentTypeFB componentType, XrSpaceComponentStatusFB* status);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFB(
+ XrSession session,
+ const XrSpatialAnchorCreateInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceUuidFB(
+ XrSpace space,
+ XrUuidEXT* uuid);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpaceSupportedComponentsFB(
+ XrSpace space,
+ uint32_t componentTypeCapacityInput,
+ uint32_t* componentTypeCountOutput,
+ XrSpaceComponentTypeFB* componentTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetSpaceComponentStatusFB(
+ XrSpace space,
+ const XrSpaceComponentStatusSetInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceComponentStatusFB(
+ XrSpace space,
+ XrSpaceComponentTypeFB componentType,
+ XrSpaceComponentStatusFB* status);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_foveation is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_foveation 1
+XR_DEFINE_HANDLE(XrFoveationProfileFB)
+#define XR_FB_foveation_SPEC_VERSION 1
+#define XR_FB_FOVEATION_EXTENSION_NAME "XR_FB_foveation"
+typedef XrFlags64 XrSwapchainCreateFoveationFlagsFB;
+
+// Flag bits for XrSwapchainCreateFoveationFlagsFB
+static const XrSwapchainCreateFoveationFlagsFB XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB = 0x00000001;
+static const XrSwapchainCreateFoveationFlagsFB XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB = 0x00000002;
+
+typedef XrFlags64 XrSwapchainStateFoveationFlagsFB;
+
+// Flag bits for XrSwapchainStateFoveationFlagsFB
+
+typedef struct XrFoveationProfileCreateInfoFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrFoveationProfileCreateInfoFB;
+
+// XrSwapchainCreateInfoFoveationFB extends XrSwapchainCreateInfo
+typedef struct XrSwapchainCreateInfoFoveationFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSwapchainCreateFoveationFlagsFB flags;
+} XrSwapchainCreateInfoFoveationFB;
+
+typedef struct XrSwapchainStateFoveationFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSwapchainStateFoveationFlagsFB flags;
+ XrFoveationProfileFB profile;
+} XrSwapchainStateFoveationFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFoveationProfileFB)(XrSession session, const XrFoveationProfileCreateInfoFB* createInfo, XrFoveationProfileFB* profile);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFoveationProfileFB)(XrFoveationProfileFB profile);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFoveationProfileFB(
+ XrSession session,
+ const XrFoveationProfileCreateInfoFB* createInfo,
+ XrFoveationProfileFB* profile);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFoveationProfileFB(
+ XrFoveationProfileFB profile);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_foveation_configuration is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_foveation_configuration 1
+#define XR_FB_foveation_configuration_SPEC_VERSION 1
+#define XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME "XR_FB_foveation_configuration"
+
+typedef enum XrFoveationLevelFB {
+ XR_FOVEATION_LEVEL_NONE_FB = 0,
+ XR_FOVEATION_LEVEL_LOW_FB = 1,
+ XR_FOVEATION_LEVEL_MEDIUM_FB = 2,
+ XR_FOVEATION_LEVEL_HIGH_FB = 3,
+ XR_FOVEATION_LEVEL_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFoveationLevelFB;
+
+typedef enum XrFoveationDynamicFB {
+ XR_FOVEATION_DYNAMIC_DISABLED_FB = 0,
+ XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB = 1,
+ XR_FOVEATION_DYNAMIC_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFoveationDynamicFB;
+// XrFoveationLevelProfileCreateInfoFB extends XrFoveationProfileCreateInfoFB
+typedef struct XrFoveationLevelProfileCreateInfoFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrFoveationLevelFB level;
+ float verticalOffset;
+ XrFoveationDynamicFB dynamic;
+} XrFoveationLevelProfileCreateInfoFB;
+
+
+
+// XR_FB_keyboard_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_keyboard_tracking 1
+#define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128
+#define XR_FB_keyboard_tracking_SPEC_VERSION 1
+#define XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME "XR_FB_keyboard_tracking"
+typedef XrFlags64 XrKeyboardTrackingFlagsFB;
+
+// Flag bits for XrKeyboardTrackingFlagsFB
+static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_EXISTS_BIT_FB = 0x00000001;
+static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_LOCAL_BIT_FB = 0x00000002;
+static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_REMOTE_BIT_FB = 0x00000004;
+static const XrKeyboardTrackingFlagsFB XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB = 0x00000008;
+
+typedef XrFlags64 XrKeyboardTrackingQueryFlagsFB;
+
+// Flag bits for XrKeyboardTrackingQueryFlagsFB
+static const XrKeyboardTrackingQueryFlagsFB XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB = 0x00000002;
+static const XrKeyboardTrackingQueryFlagsFB XR_KEYBOARD_TRACKING_QUERY_REMOTE_BIT_FB = 0x00000004;
+
+// XrSystemKeyboardTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemKeyboardTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsKeyboardTracking;
+} XrSystemKeyboardTrackingPropertiesFB;
+
+typedef struct XrKeyboardTrackingDescriptionFB {
+ uint64_t trackedKeyboardId;
+ XrVector3f size;
+ XrKeyboardTrackingFlagsFB flags;
+ char name[XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB];
+} XrKeyboardTrackingDescriptionFB;
+
+typedef struct XrKeyboardSpaceCreateInfoFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint64_t trackedKeyboardId;
+} XrKeyboardSpaceCreateInfoFB;
+
+typedef struct XrKeyboardTrackingQueryFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrKeyboardTrackingQueryFlagsFB flags;
+} XrKeyboardTrackingQueryFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySystemTrackedKeyboardFB)(XrSession session, const XrKeyboardTrackingQueryFB* queryInfo, XrKeyboardTrackingDescriptionFB* keyboard);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateKeyboardSpaceFB)(XrSession session, const XrKeyboardSpaceCreateInfoFB* createInfo, XrSpace* keyboardSpace);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySystemTrackedKeyboardFB(
+ XrSession session,
+ const XrKeyboardTrackingQueryFB* queryInfo,
+ XrKeyboardTrackingDescriptionFB* keyboard);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB(
+ XrSession session,
+ const XrKeyboardSpaceCreateInfoFB* createInfo,
+ XrSpace* keyboardSpace);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_triangle_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_triangle_mesh 1
+XR_DEFINE_HANDLE(XrTriangleMeshFB)
+#define XR_FB_triangle_mesh_SPEC_VERSION 2
+#define XR_FB_TRIANGLE_MESH_EXTENSION_NAME "XR_FB_triangle_mesh"
+
+typedef enum XrWindingOrderFB {
+ XR_WINDING_ORDER_UNKNOWN_FB = 0,
+ XR_WINDING_ORDER_CW_FB = 1,
+ XR_WINDING_ORDER_CCW_FB = 2,
+ XR_WINDING_ORDER_MAX_ENUM_FB = 0x7FFFFFFF
+} XrWindingOrderFB;
+typedef XrFlags64 XrTriangleMeshFlagsFB;
+
+// Flag bits for XrTriangleMeshFlagsFB
+static const XrTriangleMeshFlagsFB XR_TRIANGLE_MESH_MUTABLE_BIT_FB = 0x00000001;
+
+typedef struct XrTriangleMeshCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTriangleMeshFlagsFB flags;
+ XrWindingOrderFB windingOrder;
+ uint32_t vertexCount;
+ const XrVector3f* vertexBuffer;
+ uint32_t triangleCount;
+ const uint32_t* indexBuffer;
+} XrTriangleMeshCreateInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateTriangleMeshFB)(XrSession session, const XrTriangleMeshCreateInfoFB* createInfo, XrTriangleMeshFB* outTriangleMesh);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyTriangleMeshFB)(XrTriangleMeshFB mesh);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshGetVertexBufferFB)(XrTriangleMeshFB mesh, XrVector3f** outVertexBuffer);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshGetIndexBufferFB)(XrTriangleMeshFB mesh, uint32_t** outIndexBuffer);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshBeginUpdateFB)(XrTriangleMeshFB mesh);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshEndUpdateFB)(XrTriangleMeshFB mesh, uint32_t vertexCount, uint32_t triangleCount);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshBeginVertexBufferUpdateFB)(XrTriangleMeshFB mesh, uint32_t* outVertexCount);
+typedef XrResult (XRAPI_PTR *PFN_xrTriangleMeshEndVertexBufferUpdateFB)(XrTriangleMeshFB mesh);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateTriangleMeshFB(
+ XrSession session,
+ const XrTriangleMeshCreateInfoFB* createInfo,
+ XrTriangleMeshFB* outTriangleMesh);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyTriangleMeshFB(
+ XrTriangleMeshFB mesh);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshGetVertexBufferFB(
+ XrTriangleMeshFB mesh,
+ XrVector3f** outVertexBuffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshGetIndexBufferFB(
+ XrTriangleMeshFB mesh,
+ uint32_t** outIndexBuffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshBeginUpdateFB(
+ XrTriangleMeshFB mesh);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndUpdateFB(
+ XrTriangleMeshFB mesh,
+ uint32_t vertexCount,
+ uint32_t triangleCount);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshBeginVertexBufferUpdateFB(
+ XrTriangleMeshFB mesh,
+ uint32_t* outVertexCount);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB(
+ XrTriangleMeshFB mesh);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_passthrough is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_passthrough 1
+XR_DEFINE_HANDLE(XrPassthroughFB)
+XR_DEFINE_HANDLE(XrPassthroughLayerFB)
+XR_DEFINE_HANDLE(XrGeometryInstanceFB)
+#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256
+#define XR_FB_passthrough_SPEC_VERSION 5
+#define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough"
+
+typedef enum XrPassthroughLayerPurposeFB {
+ XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB = 0,
+ XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB = 1,
+ XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB = 1000203001,
+ XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_MASKED_HANDS_FB = 1000203002,
+ XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrPassthroughLayerPurposeFB;
+typedef XrFlags64 XrPassthroughCapabilityFlagsFB;
+
+// Flag bits for XrPassthroughCapabilityFlagsFB
+static const XrPassthroughCapabilityFlagsFB XR_PASSTHROUGH_CAPABILITY_BIT_FB = 0x00000001;
+static const XrPassthroughCapabilityFlagsFB XR_PASSTHROUGH_CAPABILITY_COLOR_BIT_FB = 0x00000002;
+static const XrPassthroughCapabilityFlagsFB XR_PASSTHROUGH_CAPABILITY_LAYER_DEPTH_BIT_FB = 0x00000004;
+
+typedef XrFlags64 XrPassthroughFlagsFB;
+
+// Flag bits for XrPassthroughFlagsFB
+static const XrPassthroughFlagsFB XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB = 0x00000001;
+static const XrPassthroughFlagsFB XR_PASSTHROUGH_LAYER_DEPTH_BIT_FB = 0x00000002;
+
+typedef XrFlags64 XrPassthroughStateChangedFlagsFB;
+
+// Flag bits for XrPassthroughStateChangedFlagsFB
+static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_REINIT_REQUIRED_BIT_FB = 0x00000001;
+static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_NON_RECOVERABLE_ERROR_BIT_FB = 0x00000002;
+static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_RECOVERABLE_ERROR_BIT_FB = 0x00000004;
+static const XrPassthroughStateChangedFlagsFB XR_PASSTHROUGH_STATE_CHANGED_RESTORED_ERROR_BIT_FB = 0x00000008;
+
+// XrSystemPassthroughPropertiesFB extends XrSystemProperties
+typedef struct XrSystemPassthroughPropertiesFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsPassthrough;
+} XrSystemPassthroughPropertiesFB;
+
+// XrSystemPassthroughProperties2FB extends XrSystemProperties
+typedef struct XrSystemPassthroughProperties2FB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughCapabilityFlagsFB capabilities;
+} XrSystemPassthroughProperties2FB;
+
+typedef struct XrPassthroughCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughFlagsFB flags;
+} XrPassthroughCreateInfoFB;
+
+typedef struct XrPassthroughLayerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughFB passthrough;
+ XrPassthroughFlagsFB flags;
+ XrPassthroughLayerPurposeFB purpose;
+} XrPassthroughLayerCreateInfoFB;
+
+typedef struct XrCompositionLayerPassthroughFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags flags;
+ XrSpace space;
+ XrPassthroughLayerFB layerHandle;
+} XrCompositionLayerPassthroughFB;
+
+typedef struct XrGeometryInstanceCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughLayerFB layer;
+ XrTriangleMeshFB mesh;
+ XrSpace baseSpace;
+ XrPosef pose;
+ XrVector3f scale;
+} XrGeometryInstanceCreateInfoFB;
+
+typedef struct XrGeometryInstanceTransformFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrPosef pose;
+ XrVector3f scale;
+} XrGeometryInstanceTransformFB;
+
+typedef struct XrPassthroughStyleFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float textureOpacityFactor;
+ XrColor4f edgeColor;
+} XrPassthroughStyleFB;
+
+// XrPassthroughColorMapMonoToRgbaFB extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapMonoToRgbaFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrColor4f textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB];
+} XrPassthroughColorMapMonoToRgbaFB;
+
+// XrPassthroughColorMapMonoToMonoFB extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapMonoToMonoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint8_t textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB];
+} XrPassthroughColorMapMonoToMonoFB;
+
+// XrPassthroughBrightnessContrastSaturationFB extends XrPassthroughStyleFB
+typedef struct XrPassthroughBrightnessContrastSaturationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float brightness;
+ float contrast;
+ float saturation;
+} XrPassthroughBrightnessContrastSaturationFB;
+
+typedef struct XrEventDataPassthroughStateChangedFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughStateChangedFlagsFB flags;
+} XrEventDataPassthroughStateChangedFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughFB)(XrSession session, const XrPassthroughCreateInfoFB* createInfo, XrPassthroughFB* outPassthrough);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughFB)(XrPassthroughFB passthrough);
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughStartFB)(XrPassthroughFB passthrough);
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughPauseFB)(XrPassthroughFB passthrough);
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughLayerFB)(XrSession session, const XrPassthroughLayerCreateInfoFB* createInfo, XrPassthroughLayerFB* outLayer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughLayerFB)(XrPassthroughLayerFB layer);
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerPauseFB)(XrPassthroughLayerFB layer);
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerResumeFB)(XrPassthroughLayerFB layer);
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerSetStyleFB)(XrPassthroughLayerFB layer, const XrPassthroughStyleFB* style);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateGeometryInstanceFB)(XrSession session, const XrGeometryInstanceCreateInfoFB* createInfo, XrGeometryInstanceFB* outGeometryInstance);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyGeometryInstanceFB)(XrGeometryInstanceFB instance);
+typedef XrResult (XRAPI_PTR *PFN_xrGeometryInstanceSetTransformFB)(XrGeometryInstanceFB instance, const XrGeometryInstanceTransformFB* transformation);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughFB(
+ XrSession session,
+ const XrPassthroughCreateInfoFB* createInfo,
+ XrPassthroughFB* outPassthrough);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughFB(
+ XrPassthroughFB passthrough);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughStartFB(
+ XrPassthroughFB passthrough);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughPauseFB(
+ XrPassthroughFB passthrough);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughLayerFB(
+ XrSession session,
+ const XrPassthroughLayerCreateInfoFB* createInfo,
+ XrPassthroughLayerFB* outLayer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughLayerFB(
+ XrPassthroughLayerFB layer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerPauseFB(
+ XrPassthroughLayerFB layer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerResumeFB(
+ XrPassthroughLayerFB layer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetStyleFB(
+ XrPassthroughLayerFB layer,
+ const XrPassthroughStyleFB* style);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateGeometryInstanceFB(
+ XrSession session,
+ const XrGeometryInstanceCreateInfoFB* createInfo,
+ XrGeometryInstanceFB* outGeometryInstance);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyGeometryInstanceFB(
+ XrGeometryInstanceFB instance);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB(
+ XrGeometryInstanceFB instance,
+ const XrGeometryInstanceTransformFB* transformation);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_render_model is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_render_model 1
+
+#define XR_NULL_RENDER_MODEL_KEY_FB 0
+
+XR_DEFINE_ATOM(XrRenderModelKeyFB)
+#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64
+#define XR_FB_render_model_SPEC_VERSION 4
+#define XR_FB_RENDER_MODEL_EXTENSION_NAME "XR_FB_render_model"
+typedef XrFlags64 XrRenderModelFlagsFB;
+
+// Flag bits for XrRenderModelFlagsFB
+static const XrRenderModelFlagsFB XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_1_BIT_FB = 0x00000001;
+static const XrRenderModelFlagsFB XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB = 0x00000002;
+
+typedef struct XrRenderModelPathInfoFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPath path;
+} XrRenderModelPathInfoFB;
+
+typedef struct XrRenderModelPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vendorId;
+ char modelName[XR_MAX_RENDER_MODEL_NAME_SIZE_FB];
+ XrRenderModelKeyFB modelKey;
+ uint32_t modelVersion;
+ XrRenderModelFlagsFB flags;
+} XrRenderModelPropertiesFB;
+
+typedef struct XrRenderModelBufferFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ uint8_t* buffer;
+} XrRenderModelBufferFB;
+
+typedef struct XrRenderModelLoadInfoFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrRenderModelKeyFB modelKey;
+} XrRenderModelLoadInfoFB;
+
+// XrSystemRenderModelPropertiesFB extends XrSystemProperties
+typedef struct XrSystemRenderModelPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsRenderModelLoading;
+} XrSystemRenderModelPropertiesFB;
+
+// XrRenderModelCapabilitiesRequestFB extends XrRenderModelPropertiesFB
+typedef struct XrRenderModelCapabilitiesRequestFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrRenderModelFlagsFB flags;
+} XrRenderModelCapabilitiesRequestFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRenderModelPathsFB)(XrSession session, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrRenderModelPathInfoFB* paths);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPropertiesFB)(XrSession session, XrPath path, XrRenderModelPropertiesFB* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrLoadRenderModelFB)(XrSession session, const XrRenderModelLoadInfoFB* info, XrRenderModelBufferFB* buffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRenderModelPathsFB(
+ XrSession session,
+ uint32_t pathCapacityInput,
+ uint32_t* pathCountOutput,
+ XrRenderModelPathInfoFB* paths);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPropertiesFB(
+ XrSession session,
+ XrPath path,
+ XrRenderModelPropertiesFB* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB(
+ XrSession session,
+ const XrRenderModelLoadInfoFB* info,
+ XrRenderModelBufferFB* buffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_VARJO_foveated_rendering is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_foveated_rendering 1
+#define XR_VARJO_foveated_rendering_SPEC_VERSION 3
+#define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering"
+// XrViewLocateFoveatedRenderingVARJO extends XrViewLocateInfo
+typedef struct XrViewLocateFoveatedRenderingVARJO {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 foveatedRenderingActive;
+} XrViewLocateFoveatedRenderingVARJO;
+
+// XrFoveatedViewConfigurationViewVARJO extends XrViewConfigurationView
+typedef struct XrFoveatedViewConfigurationViewVARJO {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 foveatedRenderingActive;
+} XrFoveatedViewConfigurationViewVARJO;
+
+// XrSystemFoveatedRenderingPropertiesVARJO extends XrSystemProperties
+typedef struct XrSystemFoveatedRenderingPropertiesVARJO {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFoveatedRendering;
+} XrSystemFoveatedRenderingPropertiesVARJO;
+
+
+
+// XR_VARJO_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_composition_layer_depth_test 1
+#define XR_VARJO_composition_layer_depth_test_SPEC_VERSION 2
+#define XR_VARJO_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_VARJO_composition_layer_depth_test"
+// XrCompositionLayerDepthTestVARJO extends XrCompositionLayerProjection
+typedef struct XrCompositionLayerDepthTestVARJO {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float depthTestRangeNearZ;
+ float depthTestRangeFarZ;
+} XrCompositionLayerDepthTestVARJO;
+
+
+
+// XR_VARJO_environment_depth_estimation is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_environment_depth_estimation 1
+#define XR_VARJO_environment_depth_estimation_SPEC_VERSION 1
+#define XR_VARJO_ENVIRONMENT_DEPTH_ESTIMATION_EXTENSION_NAME "XR_VARJO_environment_depth_estimation"
+typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthEstimationVARJO)(XrSession session, XrBool32 enabled);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthEstimationVARJO(
+ XrSession session,
+ XrBool32 enabled);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_VARJO_marker_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_marker_tracking 1
+#define XR_VARJO_marker_tracking_SPEC_VERSION 1
+#define XR_VARJO_MARKER_TRACKING_EXTENSION_NAME "XR_VARJO_marker_tracking"
+// XrSystemMarkerTrackingPropertiesVARJO extends XrSystemProperties
+typedef struct XrSystemMarkerTrackingPropertiesVARJO {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsMarkerTracking;
+} XrSystemMarkerTrackingPropertiesVARJO;
+
+typedef struct XrEventDataMarkerTrackingUpdateVARJO {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint64_t markerId;
+ XrBool32 isActive;
+ XrBool32 isPredicted;
+ XrTime time;
+} XrEventDataMarkerTrackingUpdateVARJO;
+
+typedef struct XrMarkerSpaceCreateInfoVARJO {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint64_t markerId;
+ XrPosef poseInMarkerSpace;
+} XrMarkerSpaceCreateInfoVARJO;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingVARJO)(XrSession session, XrBool32 enabled);
+typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingTimeoutVARJO)(XrSession session, uint64_t markerId, XrDuration timeout);
+typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingPredictionVARJO)(XrSession session, uint64_t markerId, XrBool32 enable);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerSizeVARJO)(XrSession session, uint64_t markerId, XrExtent2Df* size);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerSpaceVARJO)(XrSession session, const XrMarkerSpaceCreateInfoVARJO* createInfo, XrSpace* space);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingVARJO(
+ XrSession session,
+ XrBool32 enabled);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingTimeoutVARJO(
+ XrSession session,
+ uint64_t markerId,
+ XrDuration timeout);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingPredictionVARJO(
+ XrSession session,
+ uint64_t markerId,
+ XrBool32 enable);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerSizeVARJO(
+ XrSession session,
+ uint64_t markerId,
+ XrExtent2Df* size);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO(
+ XrSession session,
+ const XrMarkerSpaceCreateInfoVARJO* createInfo,
+ XrSpace* space);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_VARJO_view_offset is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_view_offset 1
+#define XR_VARJO_view_offset_SPEC_VERSION 1
+#define XR_VARJO_VIEW_OFFSET_EXTENSION_NAME "XR_VARJO_view_offset"
+typedef XrResult (XRAPI_PTR *PFN_xrSetViewOffsetVARJO)(XrSession session, float offset);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO(
+ XrSession session,
+ float offset);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_VARJO_xr4_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_VARJO_xr4_controller_interaction 1
+#define XR_VARJO_xr4_controller_interaction_SPEC_VERSION 2
+#define XR_VARJO_XR4_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_VARJO_xr4_controller_interaction"
+
+
+// XR_ML_ml2_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_ml2_controller_interaction 1
+#define XR_ML_ml2_controller_interaction_SPEC_VERSION 1
+#define XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_ML_ml2_controller_interaction"
+
+
+// XR_ML_frame_end_info is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_frame_end_info 1
+#define XR_ML_frame_end_info_SPEC_VERSION 1
+#define XR_ML_FRAME_END_INFO_EXTENSION_NAME "XR_ML_frame_end_info"
+typedef XrFlags64 XrFrameEndInfoFlagsML;
+
+// Flag bits for XrFrameEndInfoFlagsML
+static const XrFrameEndInfoFlagsML XR_FRAME_END_INFO_PROTECTED_BIT_ML = 0x00000001;
+static const XrFrameEndInfoFlagsML XR_FRAME_END_INFO_VIGNETTE_BIT_ML = 0x00000002;
+
+// XrFrameEndInfoML extends XrFrameEndInfo
+typedef struct XrFrameEndInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float focusDistance;
+ XrFrameEndInfoFlagsML flags;
+} XrFrameEndInfoML;
+
+
+
+// XR_ML_global_dimmer is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_global_dimmer 1
+#define XR_ML_global_dimmer_SPEC_VERSION 1
+#define XR_ML_GLOBAL_DIMMER_EXTENSION_NAME "XR_ML_global_dimmer"
+typedef XrFlags64 XrGlobalDimmerFrameEndInfoFlagsML;
+
+// Flag bits for XrGlobalDimmerFrameEndInfoFlagsML
+static const XrGlobalDimmerFrameEndInfoFlagsML XR_GLOBAL_DIMMER_FRAME_END_INFO_ENABLED_BIT_ML = 0x00000001;
+
+// XrGlobalDimmerFrameEndInfoML extends XrFrameEndInfo
+typedef struct XrGlobalDimmerFrameEndInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float dimmerValue;
+ XrGlobalDimmerFrameEndInfoFlagsML flags;
+} XrGlobalDimmerFrameEndInfoML;
+
+
+
+// XR_ML_marker_understanding is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_marker_understanding 1
+XR_DEFINE_ATOM(XrMarkerML)
+XR_DEFINE_HANDLE(XrMarkerDetectorML)
+#define XR_ML_marker_understanding_SPEC_VERSION 1
+#define XR_ML_MARKER_UNDERSTANDING_EXTENSION_NAME "XR_ML_marker_understanding"
+
+typedef enum XrMarkerDetectorProfileML {
+ XR_MARKER_DETECTOR_PROFILE_DEFAULT_ML = 0,
+ XR_MARKER_DETECTOR_PROFILE_SPEED_ML = 1,
+ XR_MARKER_DETECTOR_PROFILE_ACCURACY_ML = 2,
+ XR_MARKER_DETECTOR_PROFILE_SMALL_TARGETS_ML = 3,
+ XR_MARKER_DETECTOR_PROFILE_LARGE_FOV_ML = 4,
+ XR_MARKER_DETECTOR_PROFILE_CUSTOM_ML = 5,
+ XR_MARKER_DETECTOR_PROFILE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorProfileML;
+
+typedef enum XrMarkerTypeML {
+ XR_MARKER_TYPE_ARUCO_ML = 0,
+ XR_MARKER_TYPE_APRIL_TAG_ML = 1,
+ XR_MARKER_TYPE_QR_ML = 2,
+ XR_MARKER_TYPE_EAN_13_ML = 3,
+ XR_MARKER_TYPE_UPC_A_ML = 4,
+ XR_MARKER_TYPE_CODE_128_ML = 5,
+ XR_MARKER_TYPE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerTypeML;
+
+typedef enum XrMarkerArucoDictML {
+ XR_MARKER_ARUCO_DICT_4X4_50_ML = 0,
+ XR_MARKER_ARUCO_DICT_4X4_100_ML = 1,
+ XR_MARKER_ARUCO_DICT_4X4_250_ML = 2,
+ XR_MARKER_ARUCO_DICT_4X4_1000_ML = 3,
+ XR_MARKER_ARUCO_DICT_5X5_50_ML = 4,
+ XR_MARKER_ARUCO_DICT_5X5_100_ML = 5,
+ XR_MARKER_ARUCO_DICT_5X5_250_ML = 6,
+ XR_MARKER_ARUCO_DICT_5X5_1000_ML = 7,
+ XR_MARKER_ARUCO_DICT_6X6_50_ML = 8,
+ XR_MARKER_ARUCO_DICT_6X6_100_ML = 9,
+ XR_MARKER_ARUCO_DICT_6X6_250_ML = 10,
+ XR_MARKER_ARUCO_DICT_6X6_1000_ML = 11,
+ XR_MARKER_ARUCO_DICT_7X7_50_ML = 12,
+ XR_MARKER_ARUCO_DICT_7X7_100_ML = 13,
+ XR_MARKER_ARUCO_DICT_7X7_250_ML = 14,
+ XR_MARKER_ARUCO_DICT_7X7_1000_ML = 15,
+ XR_MARKER_ARUCO_DICT_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerArucoDictML;
+
+typedef enum XrMarkerAprilTagDictML {
+ XR_MARKER_APRIL_TAG_DICT_16H5_ML = 0,
+ XR_MARKER_APRIL_TAG_DICT_25H9_ML = 1,
+ XR_MARKER_APRIL_TAG_DICT_36H10_ML = 2,
+ XR_MARKER_APRIL_TAG_DICT_36H11_ML = 3,
+ XR_MARKER_APRIL_TAG_DICT_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerAprilTagDictML;
+
+typedef enum XrMarkerDetectorFpsML {
+ XR_MARKER_DETECTOR_FPS_LOW_ML = 0,
+ XR_MARKER_DETECTOR_FPS_MEDIUM_ML = 1,
+ XR_MARKER_DETECTOR_FPS_HIGH_ML = 2,
+ XR_MARKER_DETECTOR_FPS_MAX_ML = 3,
+ XR_MARKER_DETECTOR_FPS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorFpsML;
+
+typedef enum XrMarkerDetectorResolutionML {
+ XR_MARKER_DETECTOR_RESOLUTION_LOW_ML = 0,
+ XR_MARKER_DETECTOR_RESOLUTION_MEDIUM_ML = 1,
+ XR_MARKER_DETECTOR_RESOLUTION_HIGH_ML = 2,
+ XR_MARKER_DETECTOR_RESOLUTION_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorResolutionML;
+
+typedef enum XrMarkerDetectorCameraML {
+ XR_MARKER_DETECTOR_CAMERA_RGB_CAMERA_ML = 0,
+ XR_MARKER_DETECTOR_CAMERA_WORLD_CAMERAS_ML = 1,
+ XR_MARKER_DETECTOR_CAMERA_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorCameraML;
+
+typedef enum XrMarkerDetectorCornerRefineMethodML {
+ XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_NONE_ML = 0,
+ XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_SUBPIX_ML = 1,
+ XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_CONTOUR_ML = 2,
+ XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_APRIL_TAG_ML = 3,
+ XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorCornerRefineMethodML;
+
+typedef enum XrMarkerDetectorFullAnalysisIntervalML {
+ XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ML = 0,
+ XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_FAST_ML = 1,
+ XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MEDIUM_ML = 2,
+ XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_SLOW_ML = 3,
+ XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorFullAnalysisIntervalML;
+
+typedef enum XrMarkerDetectorStatusML {
+ XR_MARKER_DETECTOR_STATUS_PENDING_ML = 0,
+ XR_MARKER_DETECTOR_STATUS_READY_ML = 1,
+ XR_MARKER_DETECTOR_STATUS_ERROR_ML = 2,
+ XR_MARKER_DETECTOR_STATUS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrMarkerDetectorStatusML;
+// XrSystemMarkerUnderstandingPropertiesML extends XrSystemProperties
+typedef struct XrSystemMarkerUnderstandingPropertiesML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsMarkerUnderstanding;
+} XrSystemMarkerUnderstandingPropertiesML;
+
+typedef struct XrMarkerDetectorCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMarkerDetectorProfileML profile;
+ XrMarkerTypeML markerType;
+} XrMarkerDetectorCreateInfoML;
+
+// XrMarkerDetectorArucoInfoML extends XrMarkerDetectorCreateInfoML
+typedef struct XrMarkerDetectorArucoInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMarkerArucoDictML arucoDict;
+} XrMarkerDetectorArucoInfoML;
+
+// XrMarkerDetectorSizeInfoML extends XrMarkerDetectorCreateInfoML
+typedef struct XrMarkerDetectorSizeInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float markerLength;
+} XrMarkerDetectorSizeInfoML;
+
+// XrMarkerDetectorAprilTagInfoML extends XrMarkerDetectorCreateInfoML
+typedef struct XrMarkerDetectorAprilTagInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMarkerAprilTagDictML aprilTagDict;
+} XrMarkerDetectorAprilTagInfoML;
+
+// XrMarkerDetectorCustomProfileInfoML extends XrMarkerDetectorCreateInfoML
+typedef struct XrMarkerDetectorCustomProfileInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMarkerDetectorFpsML fpsHint;
+ XrMarkerDetectorResolutionML resolutionHint;
+ XrMarkerDetectorCameraML cameraHint;
+ XrMarkerDetectorCornerRefineMethodML cornerRefineMethod;
+ XrBool32 useEdgeRefinement;
+ XrMarkerDetectorFullAnalysisIntervalML fullAnalysisIntervalHint;
+} XrMarkerDetectorCustomProfileInfoML;
+
+typedef struct XrMarkerDetectorSnapshotInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrMarkerDetectorSnapshotInfoML;
+
+typedef struct XrMarkerDetectorStateML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrMarkerDetectorStatusML state;
+} XrMarkerDetectorStateML;
+
+typedef struct XrMarkerSpaceCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrMarkerDetectorML markerDetector;
+ XrMarkerML marker;
+ XrPosef poseInMarkerSpace;
+} XrMarkerSpaceCreateInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerDetectorML)(XrSession session, const XrMarkerDetectorCreateInfoML* createInfo, XrMarkerDetectorML* markerDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyMarkerDetectorML)(XrMarkerDetectorML markerDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrSnapshotMarkerDetectorML)(XrMarkerDetectorML markerDetector, XrMarkerDetectorSnapshotInfoML* snapshotInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerDetectorStateML)(XrMarkerDetectorML markerDetector, XrMarkerDetectorStateML* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkersML)(XrMarkerDetectorML markerDetector, uint32_t markerCapacityInput, uint32_t* markerCountOutput, XrMarkerML* markers);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerReprojectionErrorML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, float* reprojectionErrorMeters);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerLengthML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, float* meters);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerNumberML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, uint64_t* number);
+typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerStringML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerSpaceML)(XrSession session, const XrMarkerSpaceCreateInfoML* createInfo, XrSpace* space);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerDetectorML(
+ XrSession session,
+ const XrMarkerDetectorCreateInfoML* createInfo,
+ XrMarkerDetectorML* markerDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyMarkerDetectorML(
+ XrMarkerDetectorML markerDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSnapshotMarkerDetectorML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerDetectorSnapshotInfoML* snapshotInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerDetectorStateML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerDetectorStateML* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkersML(
+ XrMarkerDetectorML markerDetector,
+ uint32_t markerCapacityInput,
+ uint32_t* markerCountOutput,
+ XrMarkerML* markers);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerReprojectionErrorML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerML marker,
+ float* reprojectionErrorMeters);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerLengthML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerML marker,
+ float* meters);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerNumberML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerML marker,
+ uint64_t* number);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerStringML(
+ XrMarkerDetectorML markerDetector,
+ XrMarkerML marker,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceML(
+ XrSession session,
+ const XrMarkerSpaceCreateInfoML* createInfo,
+ XrSpace* space);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_localization_map is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_localization_map 1
+XR_DEFINE_HANDLE(XrExportedLocalizationMapML)
+#define XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML 64
+#define XR_ML_localization_map_SPEC_VERSION 1
+#define XR_ML_LOCALIZATION_MAP_EXTENSION_NAME "XR_ML_localization_map"
+
+typedef enum XrLocalizationMapStateML {
+ XR_LOCALIZATION_MAP_STATE_NOT_LOCALIZED_ML = 0,
+ XR_LOCALIZATION_MAP_STATE_LOCALIZED_ML = 1,
+ XR_LOCALIZATION_MAP_STATE_LOCALIZATION_PENDING_ML = 2,
+ XR_LOCALIZATION_MAP_STATE_LOCALIZATION_SLEEPING_BEFORE_RETRY_ML = 3,
+ XR_LOCALIZATION_MAP_STATE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrLocalizationMapStateML;
+
+typedef enum XrLocalizationMapTypeML {
+ XR_LOCALIZATION_MAP_TYPE_ON_DEVICE_ML = 0,
+ XR_LOCALIZATION_MAP_TYPE_CLOUD_ML = 1,
+ XR_LOCALIZATION_MAP_TYPE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrLocalizationMapTypeML;
+
+typedef enum XrLocalizationMapConfidenceML {
+ XR_LOCALIZATION_MAP_CONFIDENCE_POOR_ML = 0,
+ XR_LOCALIZATION_MAP_CONFIDENCE_FAIR_ML = 1,
+ XR_LOCALIZATION_MAP_CONFIDENCE_GOOD_ML = 2,
+ XR_LOCALIZATION_MAP_CONFIDENCE_EXCELLENT_ML = 3,
+ XR_LOCALIZATION_MAP_CONFIDENCE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrLocalizationMapConfidenceML;
+typedef XrFlags64 XrLocalizationMapErrorFlagsML;
+
+// Flag bits for XrLocalizationMapErrorFlagsML
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_UNKNOWN_BIT_ML = 0x00000001;
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_OUT_OF_MAPPED_AREA_BIT_ML = 0x00000002;
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_LOW_FEATURE_COUNT_BIT_ML = 0x00000004;
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_EXCESSIVE_MOTION_BIT_ML = 0x00000008;
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_LOW_LIGHT_BIT_ML = 0x00000010;
+static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_HEADPOSE_BIT_ML = 0x00000020;
+
+typedef struct XrLocalizationMapML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ char name[XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML];
+ XrUuidEXT mapUuid;
+ XrLocalizationMapTypeML mapType;
+} XrLocalizationMapML;
+
+typedef struct XrEventDataLocalizationChangedML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+ XrLocalizationMapStateML state;
+ XrLocalizationMapML map;
+ XrLocalizationMapConfidenceML confidence;
+ XrLocalizationMapErrorFlagsML errorFlags;
+} XrEventDataLocalizationChangedML;
+
+typedef struct XrLocalizationMapQueryInfoBaseHeaderML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrLocalizationMapQueryInfoBaseHeaderML;
+
+typedef struct XrMapLocalizationRequestInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidEXT mapUuid;
+} XrMapLocalizationRequestInfoML;
+
+typedef struct XrLocalizationMapImportInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t size;
+ char* data;
+} XrLocalizationMapImportInfoML;
+
+typedef struct XrLocalizationEnableEventsInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrLocalizationEnableEventsInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnableLocalizationEventsML)(XrSession session, const XrLocalizationEnableEventsInfoML * info);
+typedef XrResult (XRAPI_PTR *PFN_xrQueryLocalizationMapsML)(XrSession session, const XrLocalizationMapQueryInfoBaseHeaderML* queryInfo, uint32_t mapCapacityInput, uint32_t * mapCountOutput, XrLocalizationMapML* maps);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestMapLocalizationML)(XrSession session, const XrMapLocalizationRequestInfoML* requestInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrImportLocalizationMapML)(XrSession session, const XrLocalizationMapImportInfoML* importInfo, XrUuidEXT* mapUuid);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateExportedLocalizationMapML)(XrSession session, const XrUuidEXT* mapUuid, XrExportedLocalizationMapML* map);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyExportedLocalizationMapML)(XrExportedLocalizationMapML map);
+typedef XrResult (XRAPI_PTR *PFN_xrGetExportedLocalizationMapDataML)(XrExportedLocalizationMapML map, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnableLocalizationEventsML(
+ XrSession session,
+ const XrLocalizationEnableEventsInfoML * info);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQueryLocalizationMapsML(
+ XrSession session,
+ const XrLocalizationMapQueryInfoBaseHeaderML* queryInfo,
+ uint32_t mapCapacityInput,
+ uint32_t * mapCountOutput,
+ XrLocalizationMapML* maps);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestMapLocalizationML(
+ XrSession session,
+ const XrMapLocalizationRequestInfoML* requestInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrImportLocalizationMapML(
+ XrSession session,
+ const XrLocalizationMapImportInfoML* importInfo,
+ XrUuidEXT* mapUuid);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateExportedLocalizationMapML(
+ XrSession session,
+ const XrUuidEXT* mapUuid,
+ XrExportedLocalizationMapML* map);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyExportedLocalizationMapML(
+ XrExportedLocalizationMapML map);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML(
+ XrExportedLocalizationMapML map,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_spatial_anchors is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_spatial_anchors 1
+XR_DEFINE_OPAQUE_64(XrFutureEXT)
+#define XR_ML_spatial_anchors_SPEC_VERSION 1
+#define XR_ML_SPATIAL_ANCHORS_EXTENSION_NAME "XR_ML_spatial_anchors"
+
+typedef enum XrSpatialAnchorConfidenceML {
+ XR_SPATIAL_ANCHOR_CONFIDENCE_LOW_ML = 0,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_MEDIUM_ML = 1,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_HIGH_ML = 2,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrSpatialAnchorConfidenceML;
+typedef struct XR_MAY_ALIAS XrSpatialAnchorsCreateInfoBaseHeaderML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsCreateInfoBaseHeaderML;
+
+typedef struct XrSpatialAnchorsCreateInfoFromPoseML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrPosef poseInBaseSpace;
+ XrTime time;
+} XrSpatialAnchorsCreateInfoFromPoseML;
+
+typedef struct XrCreateSpatialAnchorsCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+} XrCreateSpatialAnchorsCompletionML;
+
+typedef struct XrSpatialAnchorStateML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpatialAnchorConfidenceML confidence;
+} XrSpatialAnchorStateML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsAsyncML)(XrSession session, const XrSpatialAnchorsCreateInfoBaseHeaderML* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsCompleteML)(XrSession session, XrFutureEXT future, XrCreateSpatialAnchorsCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialAnchorStateML)(XrSpace anchor, XrSpatialAnchorStateML* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsAsyncML(
+ XrSession session,
+ const XrSpatialAnchorsCreateInfoBaseHeaderML* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsCompleteML(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateSpatialAnchorsCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorStateML(
+ XrSpace anchor,
+ XrSpatialAnchorStateML* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_spatial_anchors_storage is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_spatial_anchors_storage 1
+XR_DEFINE_HANDLE(XrSpatialAnchorsStorageML)
+#define XR_ML_spatial_anchors_storage_SPEC_VERSION 1
+#define XR_ML_SPATIAL_ANCHORS_STORAGE_EXTENSION_NAME "XR_ML_spatial_anchors_storage"
+typedef struct XrSpatialAnchorsCreateStorageInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsCreateStorageInfoML;
+
+typedef struct XR_MAY_ALIAS XrSpatialAnchorsQueryInfoBaseHeaderML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsQueryInfoBaseHeaderML;
+
+typedef struct XrSpatialAnchorsQueryInfoRadiusML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrVector3f center;
+ XrTime time;
+ float radius;
+} XrSpatialAnchorsQueryInfoRadiusML;
+
+typedef struct XrSpatialAnchorsQueryCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t uuidCapacityInput;
+ uint32_t uuidCountOutput;
+ XrUuidEXT* uuids;
+} XrSpatialAnchorsQueryCompletionML;
+
+typedef struct XrSpatialAnchorsCreateInfoFromUuidsML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialAnchorsStorageML storage;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSpatialAnchorsCreateInfoFromUuidsML;
+
+typedef struct XrSpatialAnchorsPublishInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t anchorCount;
+ const XrSpace* anchors;
+ uint64_t expiration;
+} XrSpatialAnchorsPublishInfoML;
+
+typedef struct XrSpatialAnchorsPublishCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t uuidCount;
+ XrUuidEXT* uuids;
+} XrSpatialAnchorsPublishCompletionML;
+
+typedef struct XrSpatialAnchorsDeleteInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSpatialAnchorsDeleteInfoML;
+
+typedef struct XrSpatialAnchorsDeleteCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrSpatialAnchorsDeleteCompletionML;
+
+typedef struct XrSpatialAnchorsUpdateExpirationInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+ uint64_t expiration;
+} XrSpatialAnchorsUpdateExpirationInfoML;
+
+typedef struct XrSpatialAnchorsUpdateExpirationCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrSpatialAnchorsUpdateExpirationCompletionML;
+
+typedef struct XrSpatialAnchorCompletionResultML {
+ XrUuidEXT uuid;
+ XrResult result;
+} XrSpatialAnchorCompletionResultML;
+
+// XrSpatialAnchorsPublishCompletionDetailsML extends XrSpatialAnchorsPublishCompletionML
+typedef struct XrSpatialAnchorsPublishCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsPublishCompletionDetailsML;
+
+// XrSpatialAnchorsDeleteCompletionDetailsML extends XrSpatialAnchorsDeleteCompletionML
+typedef struct XrSpatialAnchorsDeleteCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsDeleteCompletionDetailsML;
+
+// XrSpatialAnchorsUpdateExpirationCompletionDetailsML extends XrSpatialAnchorsUpdateExpirationCompletionML
+typedef struct XrSpatialAnchorsUpdateExpirationCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsUpdateExpirationCompletionDetailsML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsStorageML)(XrSession session, const XrSpatialAnchorsCreateStorageInfoML* createInfo, XrSpatialAnchorsStorageML* storage);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorsStorageML)(XrSpatialAnchorsStorageML storage);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsQueryInfoBaseHeaderML* queryInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsQueryCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrPublishSpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsPublishInfoML* publishInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrPublishSpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsPublishCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDeleteSpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsDeleteInfoML* deleteInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrDeleteSpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsDeleteCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSpatialAnchorsExpirationAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsUpdateExpirationInfoML* updateInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSpatialAnchorsExpirationCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsUpdateExpirationCompletionML* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsStorageML(
+ XrSession session,
+ const XrSpatialAnchorsCreateStorageInfoML* createInfo,
+ XrSpatialAnchorsStorageML* storage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorsStorageML(
+ XrSpatialAnchorsStorageML storage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsQueryInfoBaseHeaderML* queryInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsQueryCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPublishSpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsPublishInfoML* publishInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPublishSpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsPublishCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDeleteSpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsDeleteInfoML* deleteInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDeleteSpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsDeleteCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSpatialAnchorsExpirationAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsUpdateExpirationInfoML* updateInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSpatialAnchorsExpirationCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsUpdateExpirationCompletionML* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_spatial_anchor_persistence is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_spatial_anchor_persistence 1
+XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT)
+#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256
+#define XR_MSFT_spatial_anchor_persistence_SPEC_VERSION 2
+#define XR_MSFT_SPATIAL_ANCHOR_PERSISTENCE_EXTENSION_NAME "XR_MSFT_spatial_anchor_persistence"
+typedef struct XrSpatialAnchorPersistenceNameMSFT {
+ char name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT];
+} XrSpatialAnchorPersistenceNameMSFT;
+
+typedef struct XrSpatialAnchorPersistenceInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialAnchorPersistenceNameMSFT spatialAnchorPersistenceName;
+ XrSpatialAnchorMSFT spatialAnchor;
+} XrSpatialAnchorPersistenceInfoMSFT;
+
+typedef struct XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore;
+ XrSpatialAnchorPersistenceNameMSFT spatialAnchorPersistenceName;
+} XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorStoreConnectionMSFT)(XrSession session, XrSpatialAnchorStoreConnectionMSFT* spatialAnchorStore);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorStoreConnectionMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore);
+typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, const XrSpatialAnchorPersistenceInfoMSFT* spatialAnchorPersistenceInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePersistedSpatialAnchorNamesMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, uint32_t spatialAnchorNameCapacityInput, uint32_t* spatialAnchorNameCountOutput, XrSpatialAnchorPersistenceNameMSFT* spatialAnchorNames);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPersistedNameMSFT)(XrSession session, const XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT* spatialAnchorCreateInfo, XrSpatialAnchorMSFT* spatialAnchor);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore, const XrSpatialAnchorPersistenceNameMSFT* spatialAnchorPersistenceName);
+typedef XrResult (XRAPI_PTR *PFN_xrClearSpatialAnchorStoreMSFT)(XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorStoreConnectionMSFT(
+ XrSession session,
+ XrSpatialAnchorStoreConnectionMSFT* spatialAnchorStore);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorStoreConnectionMSFT(
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorMSFT(
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore,
+ const XrSpatialAnchorPersistenceInfoMSFT* spatialAnchorPersistenceInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePersistedSpatialAnchorNamesMSFT(
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore,
+ uint32_t spatialAnchorNameCapacityInput,
+ uint32_t* spatialAnchorNameCountOutput,
+ XrSpatialAnchorPersistenceNameMSFT* spatialAnchorNames);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPersistedNameMSFT(
+ XrSession session,
+ const XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT* spatialAnchorCreateInfo,
+ XrSpatialAnchorMSFT* spatialAnchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorMSFT(
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore,
+ const XrSpatialAnchorPersistenceNameMSFT* spatialAnchorPersistenceName);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT(
+ XrSpatialAnchorStoreConnectionMSFT spatialAnchorStore);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_MSFT_scene_marker is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_scene_marker 1
+#define XR_MSFT_scene_marker_SPEC_VERSION 1
+#define XR_MSFT_SCENE_MARKER_EXTENSION_NAME "XR_MSFT_scene_marker"
+
+typedef enum XrSceneMarkerTypeMSFT {
+ XR_SCENE_MARKER_TYPE_QR_CODE_MSFT = 1,
+ XR_SCENE_MARKER_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneMarkerTypeMSFT;
+
+typedef enum XrSceneMarkerQRCodeSymbolTypeMSFT {
+ XR_SCENE_MARKER_QR_CODE_SYMBOL_TYPE_QR_CODE_MSFT = 1,
+ XR_SCENE_MARKER_QR_CODE_SYMBOL_TYPE_MICRO_QR_CODE_MSFT = 2,
+ XR_SCENE_MARKER_QRCODE_SYMBOL_TYPE_MAX_ENUM_MSFT = 0x7FFFFFFF
+} XrSceneMarkerQRCodeSymbolTypeMSFT;
+typedef struct XrSceneMarkerMSFT {
+ XrSceneMarkerTypeMSFT markerType;
+ XrTime lastSeenTime;
+ XrOffset2Df center;
+ XrExtent2Df size;
+} XrSceneMarkerMSFT;
+
+// XrSceneMarkersMSFT extends XrSceneComponentsMSFT
+typedef struct XrSceneMarkersMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t sceneMarkerCapacityInput;
+ XrSceneMarkerMSFT* sceneMarkers;
+} XrSceneMarkersMSFT;
+
+// XrSceneMarkerTypeFilterMSFT extends XrSceneComponentsGetInfoMSFT
+typedef struct XrSceneMarkerTypeFilterMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t markerTypeCount;
+ XrSceneMarkerTypeMSFT* markerTypes;
+} XrSceneMarkerTypeFilterMSFT;
+
+typedef struct XrSceneMarkerQRCodeMSFT {
+ XrSceneMarkerQRCodeSymbolTypeMSFT symbolType;
+ uint8_t version;
+} XrSceneMarkerQRCodeMSFT;
+
+// XrSceneMarkerQRCodesMSFT extends XrSceneComponentsMSFT
+typedef struct XrSceneMarkerQRCodesMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t qrCodeCapacityInput;
+ XrSceneMarkerQRCodeMSFT* qrCodes;
+} XrSceneMarkerQRCodesMSFT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSceneMarkerRawDataMSFT)(XrSceneMSFT scene, const XrUuidMSFT* markerId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint8_t* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSceneMarkerDecodedStringMSFT)(XrSceneMSFT scene, const XrUuidMSFT* markerId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMarkerRawDataMSFT(
+ XrSceneMSFT scene,
+ const XrUuidMSFT* markerId,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ uint8_t* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMarkerDecodedStringMSFT(
+ XrSceneMSFT scene,
+ const XrUuidMSFT* markerId,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ULTRALEAP_hand_tracking_forearm is a preprocessor guard. Do not pass it to API calls.
+#define XR_ULTRALEAP_hand_tracking_forearm 1
+
+#define XR_HAND_FOREARM_JOINT_COUNT_ULTRALEAP 27
+
+#define XR_ULTRALEAP_hand_tracking_forearm_SPEC_VERSION 1
+#define XR_ULTRALEAP_HAND_TRACKING_FOREARM_EXTENSION_NAME "XR_ULTRALEAP_hand_tracking_forearm"
+
+typedef enum XrHandForearmJointULTRALEAP {
+ XR_HAND_FOREARM_JOINT_PALM_ULTRALEAP = 0,
+ XR_HAND_FOREARM_JOINT_WRIST_ULTRALEAP = 1,
+ XR_HAND_FOREARM_JOINT_THUMB_METACARPAL_ULTRALEAP = 2,
+ XR_HAND_FOREARM_JOINT_THUMB_PROXIMAL_ULTRALEAP = 3,
+ XR_HAND_FOREARM_JOINT_THUMB_DISTAL_ULTRALEAP = 4,
+ XR_HAND_FOREARM_JOINT_THUMB_TIP_ULTRALEAP = 5,
+ XR_HAND_FOREARM_JOINT_INDEX_METACARPAL_ULTRALEAP = 6,
+ XR_HAND_FOREARM_JOINT_INDEX_PROXIMAL_ULTRALEAP = 7,
+ XR_HAND_FOREARM_JOINT_INDEX_INTERMEDIATE_ULTRALEAP = 8,
+ XR_HAND_FOREARM_JOINT_INDEX_DISTAL_ULTRALEAP = 9,
+ XR_HAND_FOREARM_JOINT_INDEX_TIP_ULTRALEAP = 10,
+ XR_HAND_FOREARM_JOINT_MIDDLE_METACARPAL_ULTRALEAP = 11,
+ XR_HAND_FOREARM_JOINT_MIDDLE_PROXIMAL_ULTRALEAP = 12,
+ XR_HAND_FOREARM_JOINT_MIDDLE_INTERMEDIATE_ULTRALEAP = 13,
+ XR_HAND_FOREARM_JOINT_MIDDLE_DISTAL_ULTRALEAP = 14,
+ XR_HAND_FOREARM_JOINT_MIDDLE_TIP_ULTRALEAP = 15,
+ XR_HAND_FOREARM_JOINT_RING_METACARPAL_ULTRALEAP = 16,
+ XR_HAND_FOREARM_JOINT_RING_PROXIMAL_ULTRALEAP = 17,
+ XR_HAND_FOREARM_JOINT_RING_INTERMEDIATE_ULTRALEAP = 18,
+ XR_HAND_FOREARM_JOINT_RING_DISTAL_ULTRALEAP = 19,
+ XR_HAND_FOREARM_JOINT_RING_TIP_ULTRALEAP = 20,
+ XR_HAND_FOREARM_JOINT_LITTLE_METACARPAL_ULTRALEAP = 21,
+ XR_HAND_FOREARM_JOINT_LITTLE_PROXIMAL_ULTRALEAP = 22,
+ XR_HAND_FOREARM_JOINT_LITTLE_INTERMEDIATE_ULTRALEAP = 23,
+ XR_HAND_FOREARM_JOINT_LITTLE_DISTAL_ULTRALEAP = 24,
+ XR_HAND_FOREARM_JOINT_LITTLE_TIP_ULTRALEAP = 25,
+ XR_HAND_FOREARM_JOINT_ELBOW_ULTRALEAP = 26,
+ XR_HAND_FOREARM_JOINT_MAX_ENUM_ULTRALEAP = 0x7FFFFFFF
+} XrHandForearmJointULTRALEAP;
+
+
+// XR_FB_spatial_entity_query is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_query 1
+#define XR_FB_spatial_entity_query_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME "XR_FB_spatial_entity_query"
+
+typedef enum XrSpaceQueryActionFB {
+ XR_SPACE_QUERY_ACTION_LOAD_FB = 0,
+ XR_SPACE_QUERY_ACTION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceQueryActionFB;
+
+typedef enum XrSpaceStorageLocationFB {
+ XR_SPACE_STORAGE_LOCATION_INVALID_FB = 0,
+ XR_SPACE_STORAGE_LOCATION_LOCAL_FB = 1,
+ XR_SPACE_STORAGE_LOCATION_CLOUD_FB = 2,
+ XR_SPACE_STORAGE_LOCATION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceStorageLocationFB;
+typedef struct XR_MAY_ALIAS XrSpaceQueryInfoBaseHeaderFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceQueryInfoBaseHeaderFB;
+
+typedef struct XR_MAY_ALIAS XrSpaceFilterInfoBaseHeaderFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceFilterInfoBaseHeaderFB;
+
+typedef struct XrSpaceQueryInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceQueryActionFB queryAction;
+ uint32_t maxResultCount;
+ XrDuration timeout;
+ const XrSpaceFilterInfoBaseHeaderFB* filter;
+ const XrSpaceFilterInfoBaseHeaderFB* excludeFilter;
+} XrSpaceQueryInfoFB;
+
+// XrSpaceStorageLocationFilterInfoFB extends XrSpaceFilterInfoBaseHeaderFB
+typedef struct XrSpaceStorageLocationFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceStorageLocationFB location;
+} XrSpaceStorageLocationFilterInfoFB;
+
+typedef struct XrSpaceUuidFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ XrUuidEXT* uuids;
+} XrSpaceUuidFilterInfoFB;
+
+typedef struct XrSpaceComponentFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceComponentTypeFB componentType;
+} XrSpaceComponentFilterInfoFB;
+
+typedef struct XrSpaceQueryResultFB {
+ XrSpace space;
+ XrUuidEXT uuid;
+} XrSpaceQueryResultFB;
+
+typedef struct XrSpaceQueryResultsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCapacityInput;
+ uint32_t resultCountOutput;
+ XrSpaceQueryResultFB* results;
+} XrSpaceQueryResultsFB;
+
+typedef struct XrEventDataSpaceQueryResultsAvailableFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+} XrEventDataSpaceQueryResultsAvailableFB;
+
+typedef struct XrEventDataSpaceQueryCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceQueryCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpacesFB)(XrSession session, const XrSpaceQueryInfoBaseHeaderFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpaceQueryResultsFB)(XrSession session, XrAsyncRequestIdFB requestId, XrSpaceQueryResultsFB* results);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpacesFB(
+ XrSession session,
+ const XrSpaceQueryInfoBaseHeaderFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceQueryResultsFB(
+ XrSession session,
+ XrAsyncRequestIdFB requestId,
+ XrSpaceQueryResultsFB* results);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_spatial_entity_storage is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_storage 1
+#define XR_FB_spatial_entity_storage_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME "XR_FB_spatial_entity_storage"
+
+typedef enum XrSpacePersistenceModeFB {
+ XR_SPACE_PERSISTENCE_MODE_INVALID_FB = 0,
+ XR_SPACE_PERSISTENCE_MODE_INDEFINITE_FB = 1,
+ XR_SPACE_PERSISTENCE_MODE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpacePersistenceModeFB;
+typedef struct XrSpaceSaveInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrSpaceStorageLocationFB location;
+ XrSpacePersistenceModeFB persistenceMode;
+} XrSpaceSaveInfoFB;
+
+typedef struct XrSpaceEraseInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrSpaceStorageLocationFB location;
+} XrSpaceEraseInfoFB;
+
+typedef struct XrEventDataSpaceSaveCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceStorageLocationFB location;
+} XrEventDataSpaceSaveCompleteFB;
+
+typedef struct XrEventDataSpaceEraseCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceStorageLocationFB location;
+} XrEventDataSpaceEraseCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSaveSpaceFB)(XrSession session, const XrSpaceSaveInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrEraseSpaceFB)(XrSession session, const XrSpaceEraseInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceFB(
+ XrSession session,
+ const XrSpaceSaveInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB(
+ XrSession session,
+ const XrSpaceEraseInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_touch_controller_pro is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_touch_controller_pro 1
+#define XR_FB_touch_controller_pro_SPEC_VERSION 1
+#define XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME "XR_FB_touch_controller_pro"
+
+
+// XR_FB_spatial_entity_sharing is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_sharing 1
+XR_DEFINE_HANDLE(XrSpaceUserFB)
+#define XR_FB_spatial_entity_sharing_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_SHARING_EXTENSION_NAME "XR_FB_spatial_entity_sharing"
+typedef struct XrSpaceShareInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ uint32_t userCount;
+ XrSpaceUserFB* users;
+} XrSpaceShareInfoFB;
+
+typedef struct XrEventDataSpaceShareCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceShareCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrShareSpacesFB)(XrSession session, const XrSpaceShareInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesFB(
+ XrSession session,
+ const XrSpaceShareInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_space_warp is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_space_warp 1
+#define XR_FB_space_warp_SPEC_VERSION 2
+#define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp"
+typedef XrFlags64 XrCompositionLayerSpaceWarpInfoFlagsFB;
+
+// Flag bits for XrCompositionLayerSpaceWarpInfoFlagsFB
+static const XrCompositionLayerSpaceWarpInfoFlagsFB XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB = 0x00000001;
+
+// XrCompositionLayerSpaceWarpInfoFB extends XrCompositionLayerProjectionView
+typedef struct XrCompositionLayerSpaceWarpInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerSpaceWarpInfoFlagsFB layerFlags;
+ XrSwapchainSubImage motionVectorSubImage;
+ XrPosef appSpaceDeltaPose;
+ XrSwapchainSubImage depthSubImage;
+ float minDepth;
+ float maxDepth;
+ float nearZ;
+ float farZ;
+} XrCompositionLayerSpaceWarpInfoFB;
+
+// XrSystemSpaceWarpPropertiesFB extends XrSystemProperties
+typedef struct XrSystemSpaceWarpPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t recommendedMotionVectorImageRectWidth;
+ uint32_t recommendedMotionVectorImageRectHeight;
+} XrSystemSpaceWarpPropertiesFB;
+
+
+
+// XR_FB_haptic_amplitude_envelope is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_haptic_amplitude_envelope 1
+
+#define XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB 4000u
+
+#define XR_FB_haptic_amplitude_envelope_SPEC_VERSION 1
+#define XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME "XR_FB_haptic_amplitude_envelope"
+typedef struct XrHapticAmplitudeEnvelopeVibrationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDuration duration;
+ uint32_t amplitudeCount;
+ const float* amplitudes;
+} XrHapticAmplitudeEnvelopeVibrationFB;
+
+
+
+// XR_FB_scene is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_scene 1
+#define XR_FB_scene_SPEC_VERSION 4
+#define XR_FB_SCENE_EXTENSION_NAME "XR_FB_scene"
+typedef XrFlags64 XrSemanticLabelsSupportFlagsFB;
+
+// Flag bits for XrSemanticLabelsSupportFlagsFB
+static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB = 0x00000001;
+static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB = 0x00000002;
+static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_INVISIBLE_WALL_FACE_BIT_FB = 0x00000004;
+
+typedef XrExtent3Df XrExtent3DfFB;
+
+typedef struct XrOffset3DfFB {
+ float x;
+ float y;
+ float z;
+} XrOffset3DfFB;
+
+typedef struct XrRect3DfFB {
+ XrOffset3DfFB offset;
+ XrExtent3DfFB extent;
+} XrRect3DfFB;
+
+typedef struct XrSemanticLabelsFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ char* buffer;
+} XrSemanticLabelsFB;
+
+typedef struct XrRoomLayoutFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidEXT floorUuid;
+ XrUuidEXT ceilingUuid;
+ uint32_t wallUuidCapacityInput;
+ uint32_t wallUuidCountOutput;
+ XrUuidEXT* wallUuids;
+} XrRoomLayoutFB;
+
+typedef struct XrBoundary2DFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector2f* vertices;
+} XrBoundary2DFB;
+
+// XrSemanticLabelsSupportInfoFB extends XrSemanticLabelsFB
+typedef struct XrSemanticLabelsSupportInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSemanticLabelsSupportFlagsFB flags;
+ const char* recognizedLabels;
+} XrSemanticLabelsSupportInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceBoundingBox2DFB)(XrSession session, XrSpace space, XrRect2Df* boundingBox2DOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceBoundingBox3DFB)(XrSession session, XrSpace space, XrRect3DfFB* boundingBox3DOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceSemanticLabelsFB)(XrSession session, XrSpace space, XrSemanticLabelsFB* semanticLabelsOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceBoundary2DFB)(XrSession session, XrSpace space, XrBoundary2DFB* boundary2DOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceRoomLayoutFB)(XrSession session, XrSpace space, XrRoomLayoutFB* roomLayoutOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceBoundingBox2DFB(
+ XrSession session,
+ XrSpace space,
+ XrRect2Df* boundingBox2DOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceBoundingBox3DFB(
+ XrSession session,
+ XrSpace space,
+ XrRect3DfFB* boundingBox3DOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceSemanticLabelsFB(
+ XrSession session,
+ XrSpace space,
+ XrSemanticLabelsFB* semanticLabelsOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceBoundary2DFB(
+ XrSession session,
+ XrSpace space,
+ XrBoundary2DFB* boundary2DOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomLayoutFB(
+ XrSession session,
+ XrSpace space,
+ XrRoomLayoutFB* roomLayoutOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_palm_pose is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_palm_pose 1
+#define XR_EXT_palm_pose_SPEC_VERSION 3
+#define XR_EXT_PALM_POSE_EXTENSION_NAME "XR_EXT_palm_pose"
+
+
+// XR_ALMALENCE_digital_lens_control is a preprocessor guard. Do not pass it to API calls.
+#define XR_ALMALENCE_digital_lens_control 1
+#define XR_ALMALENCE_digital_lens_control_SPEC_VERSION 1
+#define XR_ALMALENCE_DIGITAL_LENS_CONTROL_EXTENSION_NAME "XR_ALMALENCE_digital_lens_control"
+typedef XrFlags64 XrDigitalLensControlFlagsALMALENCE;
+
+// Flag bits for XrDigitalLensControlFlagsALMALENCE
+static const XrDigitalLensControlFlagsALMALENCE XR_DIGITAL_LENS_CONTROL_PROCESSING_DISABLE_BIT_ALMALENCE = 0x00000001;
+
+typedef struct XrDigitalLensControlALMALENCE {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDigitalLensControlFlagsALMALENCE flags;
+} XrDigitalLensControlALMALENCE;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetDigitalLensControlALMALENCE)(XrSession session, const XrDigitalLensControlALMALENCE* digitalLensControl);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE(
+ XrSession session,
+ const XrDigitalLensControlALMALENCE* digitalLensControl);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_scene_capture is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_scene_capture 1
+#define XR_FB_scene_capture_SPEC_VERSION 1
+#define XR_FB_SCENE_CAPTURE_EXTENSION_NAME "XR_FB_scene_capture"
+typedef struct XrEventDataSceneCaptureCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSceneCaptureCompleteFB;
+
+typedef struct XrSceneCaptureRequestInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestByteCount;
+ const char* request;
+} XrSceneCaptureRequestInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrRequestSceneCaptureFB)(XrSession session, const XrSceneCaptureRequestInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestSceneCaptureFB(
+ XrSession session,
+ const XrSceneCaptureRequestInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_spatial_entity_container is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_container 1
+#define XR_FB_spatial_entity_container_SPEC_VERSION 2
+#define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container"
+typedef struct XrSpaceContainerFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCapacityInput;
+ uint32_t uuidCountOutput;
+ XrUuidEXT* uuids;
+} XrSpaceContainerFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceContainerFB)(XrSession session, XrSpace space, XrSpaceContainerFB* spaceContainerOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB(
+ XrSession session,
+ XrSpace space,
+ XrSpaceContainerFB* spaceContainerOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_foveation_eye_tracked is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_foveation_eye_tracked 1
+#define XR_FOVEATION_CENTER_SIZE_META 2
+#define XR_META_foveation_eye_tracked_SPEC_VERSION 1
+#define XR_META_FOVEATION_EYE_TRACKED_EXTENSION_NAME "XR_META_foveation_eye_tracked"
+typedef XrFlags64 XrFoveationEyeTrackedProfileCreateFlagsMETA;
+
+// Flag bits for XrFoveationEyeTrackedProfileCreateFlagsMETA
+
+typedef XrFlags64 XrFoveationEyeTrackedStateFlagsMETA;
+
+// Flag bits for XrFoveationEyeTrackedStateFlagsMETA
+static const XrFoveationEyeTrackedStateFlagsMETA XR_FOVEATION_EYE_TRACKED_STATE_VALID_BIT_META = 0x00000001;
+
+// XrFoveationEyeTrackedProfileCreateInfoMETA extends XrFoveationLevelProfileCreateInfoFB
+typedef struct XrFoveationEyeTrackedProfileCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFoveationEyeTrackedProfileCreateFlagsMETA flags;
+} XrFoveationEyeTrackedProfileCreateInfoMETA;
+
+typedef struct XrFoveationEyeTrackedStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVector2f foveationCenter[XR_FOVEATION_CENTER_SIZE_META];
+ XrFoveationEyeTrackedStateFlagsMETA flags;
+} XrFoveationEyeTrackedStateMETA;
+
+// XrSystemFoveationEyeTrackedPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemFoveationEyeTrackedPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFoveationEyeTracked;
+} XrSystemFoveationEyeTrackedPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetFoveationEyeTrackedStateMETA)(XrSession session, XrFoveationEyeTrackedStateMETA* foveationState);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFoveationEyeTrackedStateMETA(
+ XrSession session,
+ XrFoveationEyeTrackedStateMETA* foveationState);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_face_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_face_tracking 1
+
+#define XR_FACE_EXPRESSSION_SET_DEFAULT_FB XR_FACE_EXPRESSION_SET_DEFAULT_FB
+
+XR_DEFINE_HANDLE(XrFaceTrackerFB)
+#define XR_FB_face_tracking_SPEC_VERSION 1
+#define XR_FB_FACE_TRACKING_EXTENSION_NAME "XR_FB_face_tracking"
+
+typedef enum XrFaceExpressionFB {
+ XR_FACE_EXPRESSION_BROW_LOWERER_L_FB = 0,
+ XR_FACE_EXPRESSION_BROW_LOWERER_R_FB = 1,
+ XR_FACE_EXPRESSION_CHEEK_PUFF_L_FB = 2,
+ XR_FACE_EXPRESSION_CHEEK_PUFF_R_FB = 3,
+ XR_FACE_EXPRESSION_CHEEK_RAISER_L_FB = 4,
+ XR_FACE_EXPRESSION_CHEEK_RAISER_R_FB = 5,
+ XR_FACE_EXPRESSION_CHEEK_SUCK_L_FB = 6,
+ XR_FACE_EXPRESSION_CHEEK_SUCK_R_FB = 7,
+ XR_FACE_EXPRESSION_CHIN_RAISER_B_FB = 8,
+ XR_FACE_EXPRESSION_CHIN_RAISER_T_FB = 9,
+ XR_FACE_EXPRESSION_DIMPLER_L_FB = 10,
+ XR_FACE_EXPRESSION_DIMPLER_R_FB = 11,
+ XR_FACE_EXPRESSION_EYES_CLOSED_L_FB = 12,
+ XR_FACE_EXPRESSION_EYES_CLOSED_R_FB = 13,
+ XR_FACE_EXPRESSION_EYES_LOOK_DOWN_L_FB = 14,
+ XR_FACE_EXPRESSION_EYES_LOOK_DOWN_R_FB = 15,
+ XR_FACE_EXPRESSION_EYES_LOOK_LEFT_L_FB = 16,
+ XR_FACE_EXPRESSION_EYES_LOOK_LEFT_R_FB = 17,
+ XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_L_FB = 18,
+ XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_R_FB = 19,
+ XR_FACE_EXPRESSION_EYES_LOOK_UP_L_FB = 20,
+ XR_FACE_EXPRESSION_EYES_LOOK_UP_R_FB = 21,
+ XR_FACE_EXPRESSION_INNER_BROW_RAISER_L_FB = 22,
+ XR_FACE_EXPRESSION_INNER_BROW_RAISER_R_FB = 23,
+ XR_FACE_EXPRESSION_JAW_DROP_FB = 24,
+ XR_FACE_EXPRESSION_JAW_SIDEWAYS_LEFT_FB = 25,
+ XR_FACE_EXPRESSION_JAW_SIDEWAYS_RIGHT_FB = 26,
+ XR_FACE_EXPRESSION_JAW_THRUST_FB = 27,
+ XR_FACE_EXPRESSION_LID_TIGHTENER_L_FB = 28,
+ XR_FACE_EXPRESSION_LID_TIGHTENER_R_FB = 29,
+ XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_L_FB = 30,
+ XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_R_FB = 31,
+ XR_FACE_EXPRESSION_LIP_CORNER_PULLER_L_FB = 32,
+ XR_FACE_EXPRESSION_LIP_CORNER_PULLER_R_FB = 33,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_LB_FB = 34,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_LT_FB = 35,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_RB_FB = 36,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_RT_FB = 37,
+ XR_FACE_EXPRESSION_LIP_PRESSOR_L_FB = 38,
+ XR_FACE_EXPRESSION_LIP_PRESSOR_R_FB = 39,
+ XR_FACE_EXPRESSION_LIP_PUCKER_L_FB = 40,
+ XR_FACE_EXPRESSION_LIP_PUCKER_R_FB = 41,
+ XR_FACE_EXPRESSION_LIP_STRETCHER_L_FB = 42,
+ XR_FACE_EXPRESSION_LIP_STRETCHER_R_FB = 43,
+ XR_FACE_EXPRESSION_LIP_SUCK_LB_FB = 44,
+ XR_FACE_EXPRESSION_LIP_SUCK_LT_FB = 45,
+ XR_FACE_EXPRESSION_LIP_SUCK_RB_FB = 46,
+ XR_FACE_EXPRESSION_LIP_SUCK_RT_FB = 47,
+ XR_FACE_EXPRESSION_LIP_TIGHTENER_L_FB = 48,
+ XR_FACE_EXPRESSION_LIP_TIGHTENER_R_FB = 49,
+ XR_FACE_EXPRESSION_LIPS_TOWARD_FB = 50,
+ XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_L_FB = 51,
+ XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_R_FB = 52,
+ XR_FACE_EXPRESSION_MOUTH_LEFT_FB = 53,
+ XR_FACE_EXPRESSION_MOUTH_RIGHT_FB = 54,
+ XR_FACE_EXPRESSION_NOSE_WRINKLER_L_FB = 55,
+ XR_FACE_EXPRESSION_NOSE_WRINKLER_R_FB = 56,
+ XR_FACE_EXPRESSION_OUTER_BROW_RAISER_L_FB = 57,
+ XR_FACE_EXPRESSION_OUTER_BROW_RAISER_R_FB = 58,
+ XR_FACE_EXPRESSION_UPPER_LID_RAISER_L_FB = 59,
+ XR_FACE_EXPRESSION_UPPER_LID_RAISER_R_FB = 60,
+ XR_FACE_EXPRESSION_UPPER_LIP_RAISER_L_FB = 61,
+ XR_FACE_EXPRESSION_UPPER_LIP_RAISER_R_FB = 62,
+ XR_FACE_EXPRESSION_COUNT_FB = 63,
+ XR_FACE_EXPRESSION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpressionFB;
+
+typedef enum XrFaceExpressionSetFB {
+ XR_FACE_EXPRESSION_SET_DEFAULT_FB = 0,
+ XR_FACE_EXPRESSION_SET_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpressionSetFB;
+
+typedef enum XrFaceConfidenceFB {
+ XR_FACE_CONFIDENCE_LOWER_FACE_FB = 0,
+ XR_FACE_CONFIDENCE_UPPER_FACE_FB = 1,
+ XR_FACE_CONFIDENCE_COUNT_FB = 2,
+ XR_FACE_CONFIDENCE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceConfidenceFB;
+// XrSystemFaceTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemFaceTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFaceTracking;
+} XrSystemFaceTrackingPropertiesFB;
+
+typedef struct XrFaceTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFaceExpressionSetFB faceExpressionSet;
+} XrFaceTrackerCreateInfoFB;
+
+typedef struct XrFaceExpressionInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrFaceExpressionInfoFB;
+
+typedef struct XrFaceExpressionStatusFB {
+ XrBool32 isValid;
+ XrBool32 isEyeFollowingBlendshapesValid;
+} XrFaceExpressionStatusFB;
+
+typedef struct XrFaceExpressionWeightsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t weightCount;
+ float* weights;
+ uint32_t confidenceCount;
+ float* confidences;
+ XrFaceExpressionStatusFB status;
+ XrTime time;
+} XrFaceExpressionWeightsFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerFB)(XrSession session, const XrFaceTrackerCreateInfoFB* createInfo, XrFaceTrackerFB* faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerFB)(XrFaceTrackerFB faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFaceExpressionWeightsFB)(XrFaceTrackerFB faceTracker, const XrFaceExpressionInfoFB* expressionInfo, XrFaceExpressionWeightsFB* expressionWeights);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerFB(
+ XrSession session,
+ const XrFaceTrackerCreateInfoFB* createInfo,
+ XrFaceTrackerFB* faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerFB(
+ XrFaceTrackerFB faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeightsFB(
+ XrFaceTrackerFB faceTracker,
+ const XrFaceExpressionInfoFB* expressionInfo,
+ XrFaceExpressionWeightsFB* expressionWeights);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_eye_tracking_social is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_eye_tracking_social 1
+XR_DEFINE_HANDLE(XrEyeTrackerFB)
+#define XR_FB_eye_tracking_social_SPEC_VERSION 1
+#define XR_FB_EYE_TRACKING_SOCIAL_EXTENSION_NAME "XR_FB_eye_tracking_social"
+
+typedef enum XrEyePositionFB {
+ XR_EYE_POSITION_LEFT_FB = 0,
+ XR_EYE_POSITION_RIGHT_FB = 1,
+ XR_EYE_POSITION_COUNT_FB = 2,
+ XR_EYE_POSITION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrEyePositionFB;
+typedef struct XrEyeGazeFB {
+ XrBool32 isValid;
+ XrPosef gazePose;
+ float gazeConfidence;
+} XrEyeGazeFB;
+
+typedef struct XrEyeTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEyeTrackerCreateInfoFB;
+
+typedef struct XrEyeGazesInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrEyeGazesInfoFB;
+
+// XrSystemEyeTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemEyeTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEyeTracking;
+} XrSystemEyeTrackingPropertiesFB;
+
+typedef struct XrEyeGazesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrEyeGazeFB gaze[XR_EYE_POSITION_COUNT_FB];
+ XrTime time;
+} XrEyeGazesFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEyeTrackerFB)(XrSession session, const XrEyeTrackerCreateInfoFB* createInfo, XrEyeTrackerFB* eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEyeTrackerFB)(XrEyeTrackerFB eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetEyeGazesFB)(XrEyeTrackerFB eyeTracker, const XrEyeGazesInfoFB* gazeInfo, XrEyeGazesFB* eyeGazes);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEyeTrackerFB(
+ XrSession session,
+ const XrEyeTrackerCreateInfoFB* createInfo,
+ XrEyeTrackerFB* eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEyeTrackerFB(
+ XrEyeTrackerFB eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazesFB(
+ XrEyeTrackerFB eyeTracker,
+ const XrEyeGazesInfoFB* gazeInfo,
+ XrEyeGazesFB* eyeGazes);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_passthrough_keyboard_hands is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_passthrough_keyboard_hands 1
+#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2
+#define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands"
+typedef struct XrPassthroughKeyboardHandsIntensityFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float leftHandIntensity;
+ float rightHandIntensity;
+} XrPassthroughKeyboardHandsIntensityFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrPassthroughLayerSetKeyboardHandsIntensityFB)(XrPassthroughLayerFB layer, const XrPassthroughKeyboardHandsIntensityFB* intensity);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB(
+ XrPassthroughLayerFB layer,
+ const XrPassthroughKeyboardHandsIntensityFB* intensity);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_composition_layer_settings is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_composition_layer_settings 1
+#define XR_FB_composition_layer_settings_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME "XR_FB_composition_layer_settings"
+typedef XrFlags64 XrCompositionLayerSettingsFlagsFB;
+
+// Flag bits for XrCompositionLayerSettingsFlagsFB
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SUPER_SAMPLING_BIT_FB = 0x00000001;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SUPER_SAMPLING_BIT_FB = 0x00000002;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB = 0x00000004;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB = 0x00000008;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_AUTO_LAYER_FILTER_BIT_META = 0x00000020;
+
+// XrCompositionLayerSettingsFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerSettingsFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerSettingsFlagsFB layerFlags;
+} XrCompositionLayerSettingsFB;
+
+
+
+// XR_FB_touch_controller_proximity is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_touch_controller_proximity 1
+#define XR_FB_touch_controller_proximity_SPEC_VERSION 1
+#define XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME "XR_FB_touch_controller_proximity"
+
+
+// XR_FB_haptic_pcm is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_haptic_pcm 1
+
+#define XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB 4000
+
+#define XR_FB_haptic_pcm_SPEC_VERSION 1
+#define XR_FB_HAPTIC_PCM_EXTENSION_NAME "XR_FB_haptic_pcm"
+typedef struct XrHapticPcmVibrationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t bufferSize;
+ const float* buffer;
+ float sampleRate;
+ XrBool32 append;
+ uint32_t* samplesConsumed;
+} XrHapticPcmVibrationFB;
+
+typedef struct XrDevicePcmSampleRateStateFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ float sampleRate;
+} XrDevicePcmSampleRateStateFB;
+
+typedef XrDevicePcmSampleRateStateFB XrDevicePcmSampleRateGetInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetDeviceSampleRateFB)(XrSession session, const XrHapticActionInfo* hapticActionInfo, XrDevicePcmSampleRateGetInfoFB* deviceSampleRate);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB(
+ XrSession session,
+ const XrHapticActionInfo* hapticActionInfo,
+ XrDevicePcmSampleRateGetInfoFB* deviceSampleRate);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_frame_synthesis is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_frame_synthesis 1
+#define XR_EXT_frame_synthesis_SPEC_VERSION 1
+#define XR_EXT_FRAME_SYNTHESIS_EXTENSION_NAME "XR_EXT_frame_synthesis"
+typedef XrFlags64 XrFrameSynthesisInfoFlagsEXT;
+
+// Flag bits for XrFrameSynthesisInfoFlagsEXT
+static const XrFrameSynthesisInfoFlagsEXT XR_FRAME_SYNTHESIS_INFO_USE_2D_MOTION_VECTOR_BIT_EXT = 0x00000001;
+static const XrFrameSynthesisInfoFlagsEXT XR_FRAME_SYNTHESIS_INFO_REQUEST_RELAXED_FRAME_INTERVAL_BIT_EXT = 0x00000002;
+
+// XrFrameSynthesisInfoEXT extends XrCompositionLayerProjectionView
+typedef struct XrFrameSynthesisInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFrameSynthesisInfoFlagsEXT layerFlags;
+ XrSwapchainSubImage motionVectorSubImage;
+ XrVector4f motionVectorScale;
+ XrVector4f motionVectorOffset;
+ XrPosef appSpaceDeltaPose;
+ XrSwapchainSubImage depthSubImage;
+ float minDepth;
+ float maxDepth;
+ float nearZ;
+ float farZ;
+} XrFrameSynthesisInfoEXT;
+
+// XrFrameSynthesisConfigViewEXT extends XrViewConfigurationView
+typedef struct XrFrameSynthesisConfigViewEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t recommendedMotionVectorImageRectWidth;
+ uint32_t recommendedMotionVectorImageRectHeight;
+} XrFrameSynthesisConfigViewEXT;
+
+
+
+// XR_FB_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_composition_layer_depth_test 1
+#define XR_FB_composition_layer_depth_test_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_FB_composition_layer_depth_test"
+
+typedef enum XrCompareOpFB {
+ XR_COMPARE_OP_NEVER_FB = 0,
+ XR_COMPARE_OP_LESS_FB = 1,
+ XR_COMPARE_OP_EQUAL_FB = 2,
+ XR_COMPARE_OP_LESS_OR_EQUAL_FB = 3,
+ XR_COMPARE_OP_GREATER_FB = 4,
+ XR_COMPARE_OP_NOT_EQUAL_FB = 5,
+ XR_COMPARE_OP_GREATER_OR_EQUAL_FB = 6,
+ XR_COMPARE_OP_ALWAYS_FB = 7,
+ XR_COMPARE_OP_MAX_ENUM_FB = 0x7FFFFFFF
+} XrCompareOpFB;
+// XrCompositionLayerDepthTestFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerDepthTestFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 depthMask;
+ XrCompareOpFB compareOp;
+} XrCompositionLayerDepthTestFB;
+
+
+
+// XR_META_local_dimming is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_local_dimming 1
+#define XR_META_local_dimming_SPEC_VERSION 1
+#define XR_META_LOCAL_DIMMING_EXTENSION_NAME "XR_META_local_dimming"
+
+typedef enum XrLocalDimmingModeMETA {
+ XR_LOCAL_DIMMING_MODE_OFF_META = 0,
+ XR_LOCAL_DIMMING_MODE_ON_META = 1,
+ XR_LOCAL_DIMMING_MODE_MAX_ENUM_META = 0x7FFFFFFF
+} XrLocalDimmingModeMETA;
+// XrLocalDimmingFrameEndInfoMETA extends XrFrameEndInfo
+typedef struct XrLocalDimmingFrameEndInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrLocalDimmingModeMETA localDimmingMode;
+} XrLocalDimmingFrameEndInfoMETA;
+
+
+
+// XR_META_passthrough_preferences is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_passthrough_preferences 1
+#define XR_META_passthrough_preferences_SPEC_VERSION 1
+#define XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME "XR_META_passthrough_preferences"
+typedef XrFlags64 XrPassthroughPreferenceFlagsMETA;
+
+// Flag bits for XrPassthroughPreferenceFlagsMETA
+static const XrPassthroughPreferenceFlagsMETA XR_PASSTHROUGH_PREFERENCE_DEFAULT_TO_ACTIVE_BIT_META = 0x00000001;
+
+typedef struct XrPassthroughPreferencesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughPreferenceFlagsMETA flags;
+} XrPassthroughPreferencesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetPassthroughPreferencesMETA)(XrSession session, XrPassthroughPreferencesMETA* preferences);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughPreferencesMETA(
+ XrSession session,
+ XrPassthroughPreferencesMETA* preferences);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_virtual_keyboard is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_virtual_keyboard 1
+XR_DEFINE_HANDLE(XrVirtualKeyboardMETA)
+#define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992
+#define XR_META_virtual_keyboard_SPEC_VERSION 1
+#define XR_META_VIRTUAL_KEYBOARD_EXTENSION_NAME "XR_META_virtual_keyboard"
+
+typedef enum XrVirtualKeyboardLocationTypeMETA {
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_CUSTOM_META = 0,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_FAR_META = 1,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_DIRECT_META = 2,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_MAX_ENUM_META = 0x7FFFFFFF
+} XrVirtualKeyboardLocationTypeMETA;
+
+typedef enum XrVirtualKeyboardInputSourceMETA {
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_LEFT_META = 1,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_RIGHT_META = 2,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_LEFT_META = 3,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_RIGHT_META = 4,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_LEFT_META = 5,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_RIGHT_META = 6,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_LEFT_META = 7,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_RIGHT_META = 8,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_MAX_ENUM_META = 0x7FFFFFFF
+} XrVirtualKeyboardInputSourceMETA;
+typedef XrFlags64 XrVirtualKeyboardInputStateFlagsMETA;
+
+// Flag bits for XrVirtualKeyboardInputStateFlagsMETA
+static const XrVirtualKeyboardInputStateFlagsMETA XR_VIRTUAL_KEYBOARD_INPUT_STATE_PRESSED_BIT_META = 0x00000001;
+
+// XrSystemVirtualKeyboardPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemVirtualKeyboardPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsVirtualKeyboard;
+} XrSystemVirtualKeyboardPropertiesMETA;
+
+typedef struct XrVirtualKeyboardCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrVirtualKeyboardCreateInfoMETA;
+
+typedef struct XrVirtualKeyboardSpaceCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardLocationTypeMETA locationType;
+ XrSpace space;
+ XrPosef poseInSpace;
+} XrVirtualKeyboardSpaceCreateInfoMETA;
+
+typedef struct XrVirtualKeyboardLocationInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardLocationTypeMETA locationType;
+ XrSpace space;
+ XrPosef poseInSpace;
+ float scale;
+} XrVirtualKeyboardLocationInfoMETA;
+
+typedef struct XrVirtualKeyboardModelVisibilitySetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 visible;
+} XrVirtualKeyboardModelVisibilitySetInfoMETA;
+
+typedef struct XrVirtualKeyboardAnimationStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ int32_t animationIndex;
+ float fraction;
+} XrVirtualKeyboardAnimationStateMETA;
+
+typedef struct XrVirtualKeyboardModelAnimationStatesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t stateCapacityInput;
+ uint32_t stateCountOutput;
+ XrVirtualKeyboardAnimationStateMETA* states;
+} XrVirtualKeyboardModelAnimationStatesMETA;
+
+typedef struct XrVirtualKeyboardTextureDataMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t textureWidth;
+ uint32_t textureHeight;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ uint8_t* buffer;
+} XrVirtualKeyboardTextureDataMETA;
+
+typedef struct XrVirtualKeyboardInputInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardInputSourceMETA inputSource;
+ XrSpace inputSpace;
+ XrPosef inputPoseInSpace;
+ XrVirtualKeyboardInputStateFlagsMETA inputState;
+} XrVirtualKeyboardInputInfoMETA;
+
+typedef struct XrVirtualKeyboardTextContextChangeInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* textContext;
+} XrVirtualKeyboardTextContextChangeInfoMETA;
+
+typedef struct XrEventDataVirtualKeyboardCommitTextMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+ char text[XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META];
+} XrEventDataVirtualKeyboardCommitTextMETA;
+
+typedef struct XrEventDataVirtualKeyboardBackspaceMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardBackspaceMETA;
+
+typedef struct XrEventDataVirtualKeyboardEnterMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardEnterMETA;
+
+typedef struct XrEventDataVirtualKeyboardShownMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardShownMETA;
+
+typedef struct XrEventDataVirtualKeyboardHiddenMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardHiddenMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVirtualKeyboardMETA)(XrSession session, const XrVirtualKeyboardCreateInfoMETA* createInfo, XrVirtualKeyboardMETA* keyboard);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyVirtualKeyboardMETA)(XrVirtualKeyboardMETA keyboard);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVirtualKeyboardSpaceMETA)(XrSession session, XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardSpaceCreateInfoMETA* createInfo, XrSpace* keyboardSpace);
+typedef XrResult (XRAPI_PTR *PFN_xrSuggestVirtualKeyboardLocationMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardLocationInfoMETA* locationInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardScaleMETA)(XrVirtualKeyboardMETA keyboard, float* scale);
+typedef XrResult (XRAPI_PTR *PFN_xrSetVirtualKeyboardModelVisibilityMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardModelVisibilitySetInfoMETA* modelVisibility);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardModelAnimationStatesMETA)(XrVirtualKeyboardMETA keyboard, XrVirtualKeyboardModelAnimationStatesMETA* animationStates);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardDirtyTexturesMETA)(XrVirtualKeyboardMETA keyboard, uint32_t textureIdCapacityInput, uint32_t* textureIdCountOutput, uint64_t* textureIds);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardTextureDataMETA)(XrVirtualKeyboardMETA keyboard, uint64_t textureId, XrVirtualKeyboardTextureDataMETA* textureData);
+typedef XrResult (XRAPI_PTR *PFN_xrSendVirtualKeyboardInputMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardInputInfoMETA* info, XrPosef* interactorRootPose);
+typedef XrResult (XRAPI_PTR *PFN_xrChangeVirtualKeyboardTextContextMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardTextContextChangeInfoMETA* changeInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVirtualKeyboardMETA(
+ XrSession session,
+ const XrVirtualKeyboardCreateInfoMETA* createInfo,
+ XrVirtualKeyboardMETA* keyboard);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyVirtualKeyboardMETA(
+ XrVirtualKeyboardMETA keyboard);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVirtualKeyboardSpaceMETA(
+ XrSession session,
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardSpaceCreateInfoMETA* createInfo,
+ XrSpace* keyboardSpace);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSuggestVirtualKeyboardLocationMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardLocationInfoMETA* locationInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardScaleMETA(
+ XrVirtualKeyboardMETA keyboard,
+ float* scale);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetVirtualKeyboardModelVisibilityMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardModelVisibilitySetInfoMETA* modelVisibility);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardModelAnimationStatesMETA(
+ XrVirtualKeyboardMETA keyboard,
+ XrVirtualKeyboardModelAnimationStatesMETA* animationStates);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardDirtyTexturesMETA(
+ XrVirtualKeyboardMETA keyboard,
+ uint32_t textureIdCapacityInput,
+ uint32_t* textureIdCountOutput,
+ uint64_t* textureIds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardTextureDataMETA(
+ XrVirtualKeyboardMETA keyboard,
+ uint64_t textureId,
+ XrVirtualKeyboardTextureDataMETA* textureData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSendVirtualKeyboardInputMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardInputInfoMETA* info,
+ XrPosef* interactorRootPose);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrChangeVirtualKeyboardTextContextMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardTextContextChangeInfoMETA* changeInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_OCULUS_external_camera is a preprocessor guard. Do not pass it to API calls.
+#define XR_OCULUS_external_camera 1
+#define XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS 32
+#define XR_OCULUS_external_camera_SPEC_VERSION 1
+#define XR_OCULUS_EXTERNAL_CAMERA_EXTENSION_NAME "XR_OCULUS_external_camera"
+
+typedef enum XrExternalCameraAttachedToDeviceOCULUS {
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_NONE_OCULUS = 0,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_HMD_OCULUS = 1,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_LTOUCH_OCULUS = 2,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_RTOUCH_OCULUS = 3,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_MAX_ENUM_OCULUS = 0x7FFFFFFF
+} XrExternalCameraAttachedToDeviceOCULUS;
+typedef XrFlags64 XrExternalCameraStatusFlagsOCULUS;
+
+// Flag bits for XrExternalCameraStatusFlagsOCULUS
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CONNECTED_BIT_OCULUS = 0x00000001;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATING_BIT_OCULUS = 0x00000002;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATION_FAILED_BIT_OCULUS = 0x00000004;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATED_BIT_OCULUS = 0x00000008;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CAPTURING_BIT_OCULUS = 0x00000010;
+
+typedef struct XrExternalCameraIntrinsicsOCULUS {
+ XrTime lastChangeTime;
+ XrFovf fov;
+ float virtualNearPlaneDistance;
+ float virtualFarPlaneDistance;
+ XrExtent2Di imageSensorPixelResolution;
+} XrExternalCameraIntrinsicsOCULUS;
+
+typedef struct XrExternalCameraExtrinsicsOCULUS {
+ XrTime lastChangeTime;
+ XrExternalCameraStatusFlagsOCULUS cameraStatusFlags;
+ XrExternalCameraAttachedToDeviceOCULUS attachedToDevice;
+ XrPosef relativePose;
+} XrExternalCameraExtrinsicsOCULUS;
+
+typedef struct XrExternalCameraOCULUS {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ char name[XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS];
+ XrExternalCameraIntrinsicsOCULUS intrinsics;
+ XrExternalCameraExtrinsicsOCULUS extrinsics;
+} XrExternalCameraOCULUS;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateExternalCamerasOCULUS)(XrSession session, uint32_t cameraCapacityInput, uint32_t* cameraCountOutput, XrExternalCameraOCULUS* cameras);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateExternalCamerasOCULUS(
+ XrSession session,
+ uint32_t cameraCapacityInput,
+ uint32_t* cameraCountOutput,
+ XrExternalCameraOCULUS* cameras);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_performance_metrics is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_performance_metrics 1
+#define XR_META_performance_metrics_SPEC_VERSION 2
+#define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics"
+
+typedef enum XrPerformanceMetricsCounterUnitMETA {
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META = 0,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META = 1,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META = 2,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_META = 3,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META = 4,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META = 0x7FFFFFFF
+} XrPerformanceMetricsCounterUnitMETA;
+typedef XrFlags64 XrPerformanceMetricsCounterFlagsMETA;
+
+// Flag bits for XrPerformanceMetricsCounterFlagsMETA
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_META = 0x00000001;
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META = 0x00000002;
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META = 0x00000004;
+
+typedef struct XrPerformanceMetricsStateMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrPerformanceMetricsStateMETA;
+
+typedef struct XrPerformanceMetricsCounterMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPerformanceMetricsCounterFlagsMETA counterFlags;
+ XrPerformanceMetricsCounterUnitMETA counterUnit;
+ uint32_t uintValue;
+ float floatValue;
+} XrPerformanceMetricsCounterMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePerformanceMetricsCounterPathsMETA)(XrInstance instance, uint32_t counterPathCapacityInput, uint32_t* counterPathCountOutput, XrPath* counterPaths);
+typedef XrResult (XRAPI_PTR *PFN_xrSetPerformanceMetricsStateMETA)(XrSession session, const XrPerformanceMetricsStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPerformanceMetricsStateMETA)(XrSession session, XrPerformanceMetricsStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrQueryPerformanceMetricsCounterMETA)(XrSession session, XrPath counterPath, XrPerformanceMetricsCounterMETA* counter);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePerformanceMetricsCounterPathsMETA(
+ XrInstance instance,
+ uint32_t counterPathCapacityInput,
+ uint32_t* counterPathCountOutput,
+ XrPath* counterPaths);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetPerformanceMetricsStateMETA(
+ XrSession session,
+ const XrPerformanceMetricsStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPerformanceMetricsStateMETA(
+ XrSession session,
+ XrPerformanceMetricsStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA(
+ XrSession session,
+ XrPath counterPath,
+ XrPerformanceMetricsCounterMETA* counter);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_spatial_entity_storage_batch is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_storage_batch 1
+#define XR_FB_spatial_entity_storage_batch_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME "XR_FB_spatial_entity_storage_batch"
+typedef struct XrSpaceListSaveInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ XrSpaceStorageLocationFB location;
+} XrSpaceListSaveInfoFB;
+
+typedef struct XrEventDataSpaceListSaveCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceListSaveCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSaveSpaceListFB)(XrSession session, const XrSpaceListSaveInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB(
+ XrSession session,
+ const XrSpaceListSaveInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_detached_controllers is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_detached_controllers 1
+#define XR_META_detached_controllers_SPEC_VERSION 1
+#define XR_META_DETACHED_CONTROLLERS_EXTENSION_NAME "XR_META_detached_controllers"
+
+
+// XR_FB_spatial_entity_user is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_spatial_entity_user 1
+typedef uint64_t XrSpaceUserIdFB;
+#define XR_FB_spatial_entity_user_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_USER_EXTENSION_NAME "XR_FB_spatial_entity_user"
+typedef struct XrSpaceUserCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceUserIdFB userId;
+} XrSpaceUserCreateInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpaceUserFB)(XrSession session, const XrSpaceUserCreateInfoFB* info, XrSpaceUserFB* user);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceUserIdFB)(XrSpaceUserFB user, XrSpaceUserIdFB* userId);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpaceUserFB)(XrSpaceUserFB user);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceUserFB(
+ XrSession session,
+ const XrSpaceUserCreateInfoFB* info,
+ XrSpaceUserFB* user);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceUserIdFB(
+ XrSpaceUserFB user,
+ XrSpaceUserIdFB* userId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpaceUserFB(
+ XrSpaceUserFB user);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_headset_id is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_headset_id 1
+#define XR_META_headset_id_SPEC_VERSION 2
+#define XR_META_HEADSET_ID_EXTENSION_NAME "XR_META_headset_id"
+// XrSystemHeadsetIdPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemHeadsetIdPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT id;
+} XrSystemHeadsetIdPropertiesMETA;
+
+
+
+// XR_META_spatial_entity_discovery is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_discovery 1
+#define XR_META_spatial_entity_discovery_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_DISCOVERY_EXTENSION_NAME "XR_META_spatial_entity_discovery"
+// XrSystemSpaceDiscoveryPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemSpaceDiscoveryPropertiesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpaceDiscovery;
+} XrSystemSpaceDiscoveryPropertiesMETA;
+
+typedef struct XR_MAY_ALIAS XrSpaceFilterBaseHeaderMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceFilterBaseHeaderMETA;
+
+typedef struct XrSpaceDiscoveryInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t filterCount;
+ const XrSpaceFilterBaseHeaderMETA* const * filters;
+} XrSpaceDiscoveryInfoMETA;
+
+typedef struct XrSpaceFilterUuidMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSpaceFilterUuidMETA;
+
+typedef struct XrSpaceFilterComponentMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceComponentTypeFB componentType;
+} XrSpaceFilterComponentMETA;
+
+typedef struct XrSpaceDiscoveryResultMETA {
+ XrSpace space;
+ XrUuidEXT uuid;
+} XrSpaceDiscoveryResultMETA;
+
+typedef struct XrSpaceDiscoveryResultsMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t resultCapacityInput;
+ uint32_t resultCountOutput;
+ XrSpaceDiscoveryResultMETA* results;
+} XrSpaceDiscoveryResultsMETA;
+
+typedef struct XrEventDataSpaceDiscoveryResultsAvailableMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+} XrEventDataSpaceDiscoveryResultsAvailableMETA;
+
+typedef struct XrEventDataSpaceDiscoveryCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceDiscoveryCompleteMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrDiscoverSpacesMETA)(XrSession session, const XrSpaceDiscoveryInfoMETA* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpaceDiscoveryResultsMETA)(XrSession session, XrAsyncRequestIdFB requestId, XrSpaceDiscoveryResultsMETA* results);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrDiscoverSpacesMETA(
+ XrSession session,
+ const XrSpaceDiscoveryInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceDiscoveryResultsMETA(
+ XrSession session,
+ XrAsyncRequestIdFB requestId,
+ XrSpaceDiscoveryResultsMETA* results);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_hand_tracking_microgestures is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_hand_tracking_microgestures 1
+#define XR_META_hand_tracking_microgestures_SPEC_VERSION 1
+#define XR_META_HAND_TRACKING_MICROGESTURES_EXTENSION_NAME "XR_META_hand_tracking_microgestures"
+
+
+// XR_META_recommended_layer_resolution is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_recommended_layer_resolution 1
+#define XR_META_recommended_layer_resolution_SPEC_VERSION 1
+#define XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME "XR_META_recommended_layer_resolution"
+typedef struct XrRecommendedLayerResolutionMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrExtent2Di recommendedImageDimensions;
+ XrBool32 isValid;
+} XrRecommendedLayerResolutionMETA;
+
+typedef struct XrRecommendedLayerResolutionGetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const XrCompositionLayerBaseHeader* layer;
+ XrTime predictedDisplayTime;
+} XrRecommendedLayerResolutionGetInfoMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetRecommendedLayerResolutionMETA)(XrSession session, const XrRecommendedLayerResolutionGetInfoMETA* info, XrRecommendedLayerResolutionMETA* resolution);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRecommendedLayerResolutionMETA(
+ XrSession session,
+ const XrRecommendedLayerResolutionGetInfoMETA* info,
+ XrRecommendedLayerResolutionMETA* resolution);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_spatial_entity_persistence is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_persistence 1
+#define XR_META_spatial_entity_persistence_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_PERSISTENCE_EXTENSION_NAME "XR_META_spatial_entity_persistence"
+// XrSystemSpacePersistencePropertiesMETA extends XrSystemProperties
+typedef struct XrSystemSpacePersistencePropertiesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpacePersistence;
+} XrSystemSpacePersistencePropertiesMETA;
+
+typedef struct XrSpacesSaveInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+} XrSpacesSaveInfoMETA;
+
+typedef struct XrEventDataSpacesSaveResultMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpacesSaveResultMETA;
+
+typedef struct XrSpacesEraseInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ uint32_t uuidCount;
+ XrUuidEXT* uuids;
+} XrSpacesEraseInfoMETA;
+
+typedef struct XrEventDataSpacesEraseResultMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpacesEraseResultMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSaveSpacesMETA)(XrSession session, const XrSpacesSaveInfoMETA* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrEraseSpacesMETA)(XrSession session, const XrSpacesEraseInfoMETA* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpacesMETA(
+ XrSession session,
+ const XrSpacesSaveInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpacesMETA(
+ XrSession session,
+ const XrSpacesEraseInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_passthrough_color_lut is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_passthrough_color_lut 1
+XR_DEFINE_HANDLE(XrPassthroughColorLutMETA)
+#define XR_META_passthrough_color_lut_SPEC_VERSION 1
+#define XR_META_PASSTHROUGH_COLOR_LUT_EXTENSION_NAME "XR_META_passthrough_color_lut"
+
+typedef enum XrPassthroughColorLutChannelsMETA {
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGB_META = 1,
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGBA_META = 2,
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_MAX_ENUM_META = 0x7FFFFFFF
+} XrPassthroughColorLutChannelsMETA;
+typedef struct XrPassthroughColorLutDataMETA {
+ uint32_t bufferSize;
+ const uint8_t* buffer;
+} XrPassthroughColorLutDataMETA;
+
+typedef struct XrPassthroughColorLutCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutChannelsMETA channels;
+ uint32_t resolution;
+ XrPassthroughColorLutDataMETA data;
+} XrPassthroughColorLutCreateInfoMETA;
+
+typedef struct XrPassthroughColorLutUpdateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutDataMETA data;
+} XrPassthroughColorLutUpdateInfoMETA;
+
+// XrPassthroughColorMapLutMETA extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapLutMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutMETA colorLut;
+ float weight;
+} XrPassthroughColorMapLutMETA;
+
+// XrPassthroughColorMapInterpolatedLutMETA extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapInterpolatedLutMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutMETA sourceColorLut;
+ XrPassthroughColorLutMETA targetColorLut;
+ float weight;
+} XrPassthroughColorMapInterpolatedLutMETA;
+
+// XrSystemPassthroughColorLutPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemPassthroughColorLutPropertiesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t maxColorLutResolution;
+} XrSystemPassthroughColorLutPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughColorLutMETA)(XrPassthroughFB passthrough, const XrPassthroughColorLutCreateInfoMETA* createInfo, XrPassthroughColorLutMETA* colorLut);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughColorLutMETA)(XrPassthroughColorLutMETA colorLut);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdatePassthroughColorLutMETA)(XrPassthroughColorLutMETA colorLut, const XrPassthroughColorLutUpdateInfoMETA* updateInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughColorLutMETA(
+ XrPassthroughFB passthrough,
+ const XrPassthroughColorLutCreateInfoMETA* createInfo,
+ XrPassthroughColorLutMETA* colorLut);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughColorLutMETA(
+ XrPassthroughColorLutMETA colorLut);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA(
+ XrPassthroughColorLutMETA colorLut,
+ const XrPassthroughColorLutUpdateInfoMETA* updateInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_spatial_entity_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_mesh 1
+#define XR_META_spatial_entity_mesh_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME "XR_META_spatial_entity_mesh"
+typedef struct XrSpaceTriangleMeshGetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceTriangleMeshGetInfoMETA;
+
+typedef struct XrSpaceTriangleMeshMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertices;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrSpaceTriangleMeshMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceTriangleMeshMETA)(XrSpace space, const XrSpaceTriangleMeshGetInfoMETA* getInfo, XrSpaceTriangleMeshMETA* triangleMeshOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceTriangleMeshMETA(
+ XrSpace space,
+ const XrSpaceTriangleMeshGetInfoMETA* getInfo,
+ XrSpaceTriangleMeshMETA* triangleMeshOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_automatic_layer_filter is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_automatic_layer_filter 1
+#define XR_META_automatic_layer_filter_SPEC_VERSION 1
+#define XR_META_AUTOMATIC_LAYER_FILTER_EXTENSION_NAME "XR_META_automatic_layer_filter"
+
+
+// XR_META_body_tracking_full_body is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_body_tracking_full_body 1
+#define XR_META_body_tracking_full_body_SPEC_VERSION 1
+#define XR_META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME "XR_META_body_tracking_full_body"
+
+typedef enum XrFullBodyJointMETA {
+ XR_FULL_BODY_JOINT_ROOT_META = 0,
+ XR_FULL_BODY_JOINT_HIPS_META = 1,
+ XR_FULL_BODY_JOINT_SPINE_LOWER_META = 2,
+ XR_FULL_BODY_JOINT_SPINE_MIDDLE_META = 3,
+ XR_FULL_BODY_JOINT_SPINE_UPPER_META = 4,
+ XR_FULL_BODY_JOINT_CHEST_META = 5,
+ XR_FULL_BODY_JOINT_NECK_META = 6,
+ XR_FULL_BODY_JOINT_HEAD_META = 7,
+ XR_FULL_BODY_JOINT_LEFT_SHOULDER_META = 8,
+ XR_FULL_BODY_JOINT_LEFT_SCAPULA_META = 9,
+ XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META = 10,
+ XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META = 11,
+ XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META = 12,
+ XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META = 13,
+ XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META = 14,
+ XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META = 15,
+ XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META = 16,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META = 17,
+ XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META = 18,
+ XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META = 19,
+ XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META = 20,
+ XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META = 21,
+ XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META = 22,
+ XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META = 23,
+ XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META = 24,
+ XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META = 25,
+ XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META = 26,
+ XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META = 27,
+ XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META = 28,
+ XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META = 29,
+ XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META = 30,
+ XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META = 31,
+ XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META = 32,
+ XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META = 33,
+ XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META = 34,
+ XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META = 35,
+ XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META = 36,
+ XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META = 37,
+ XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META = 38,
+ XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META = 39,
+ XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META = 40,
+ XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META = 41,
+ XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META = 42,
+ XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META = 43,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META = 44,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META = 45,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META = 46,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META = 47,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META = 48,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META = 49,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META = 50,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META = 51,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META = 52,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META = 53,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META = 54,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META = 55,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META = 56,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META = 57,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META = 58,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META = 59,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META = 60,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META = 61,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META = 62,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META = 63,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META = 64,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META = 65,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META = 66,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META = 67,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META = 68,
+ XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META = 69,
+ XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META = 70,
+ XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META = 71,
+ XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META = 72,
+ XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META = 73,
+ XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META = 74,
+ XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META = 75,
+ XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META = 76,
+ XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META = 77,
+ XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META = 78,
+ XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META = 79,
+ XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META = 80,
+ XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META = 81,
+ XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META = 82,
+ XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META = 83,
+ XR_FULL_BODY_JOINT_COUNT_META = 84,
+ XR_FULL_BODY_JOINT_NONE_META = 85,
+ XR_FULL_BODY_JOINT_MAX_ENUM_META = 0x7FFFFFFF
+} XrFullBodyJointMETA;
+// XrSystemPropertiesBodyTrackingFullBodyMETA extends XrSystemProperties
+typedef struct XrSystemPropertiesBodyTrackingFullBodyMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFullBodyTracking;
+} XrSystemPropertiesBodyTrackingFullBodyMETA;
+
+
+
+// XR_META_touch_controller_plus is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_touch_controller_plus 1
+#define XR_META_touch_controller_plus_SPEC_VERSION 1
+#define XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME "XR_META_touch_controller_plus"
+
+
+// XR_META_passthrough_layer_resumed_event is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_passthrough_layer_resumed_event 1
+#define XR_META_passthrough_layer_resumed_event_SPEC_VERSION 1
+#define XR_META_PASSTHROUGH_LAYER_RESUMED_EVENT_EXTENSION_NAME "XR_META_passthrough_layer_resumed_event"
+typedef struct XrEventDataPassthroughLayerResumedMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughLayerFB layer;
+} XrEventDataPassthroughLayerResumedMETA;
+
+
+
+// XR_META_body_tracking_calibration is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_body_tracking_calibration 1
+#define XR_META_body_tracking_calibration_SPEC_VERSION 1
+#define XR_META_BODY_TRACKING_CALIBRATION_EXTENSION_NAME "XR_META_body_tracking_calibration"
+
+typedef enum XrBodyTrackingCalibrationStateMETA {
+ XR_BODY_TRACKING_CALIBRATION_STATE_VALID_META = 1,
+ XR_BODY_TRACKING_CALIBRATION_STATE_CALIBRATING_META = 2,
+ XR_BODY_TRACKING_CALIBRATION_STATE_INVALID_META = 3,
+ XR_BODY_TRACKING_CALIBRATION_STATE_MAX_ENUM_META = 0x7FFFFFFF
+} XrBodyTrackingCalibrationStateMETA;
+// XrBodyTrackingCalibrationStatusMETA extends XrBodyJointLocationsFB
+typedef struct XrBodyTrackingCalibrationStatusMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBodyTrackingCalibrationStateMETA status;
+} XrBodyTrackingCalibrationStatusMETA;
+
+typedef struct XrBodyTrackingCalibrationInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float bodyHeight;
+} XrBodyTrackingCalibrationInfoMETA;
+
+// XrSystemPropertiesBodyTrackingCalibrationMETA extends XrSystemProperties
+typedef struct XrSystemPropertiesBodyTrackingCalibrationMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsHeightOverride;
+} XrSystemPropertiesBodyTrackingCalibrationMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSuggestBodyTrackingCalibrationOverrideMETA)(XrBodyTrackerFB bodyTracker, const XrBodyTrackingCalibrationInfoMETA* calibrationInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrResetBodyTrackingCalibrationMETA)(XrBodyTrackerFB bodyTracker);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSuggestBodyTrackingCalibrationOverrideMETA(
+ XrBodyTrackerFB bodyTracker,
+ const XrBodyTrackingCalibrationInfoMETA* calibrationInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrResetBodyTrackingCalibrationMETA(
+ XrBodyTrackerFB bodyTracker);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_body_tracking_fidelity is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_body_tracking_fidelity 1
+#define XR_META_body_tracking_fidelity_SPEC_VERSION 1
+#define XR_META_BODY_TRACKING_FIDELITY_EXTENSION_NAME "XR_META_body_tracking_fidelity"
+
+typedef enum XrBodyTrackingFidelityMETA {
+ XR_BODY_TRACKING_FIDELITY_LOW_META = 1,
+ XR_BODY_TRACKING_FIDELITY_HIGH_META = 2,
+ XR_BODY_TRACKING_FIDELITY_MAX_ENUM_META = 0x7FFFFFFF
+} XrBodyTrackingFidelityMETA;
+// XrSystemPropertiesBodyTrackingFidelityMETA extends XrSystemProperties
+typedef struct XrSystemPropertiesBodyTrackingFidelityMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTrackingFidelity;
+} XrSystemPropertiesBodyTrackingFidelityMETA;
+
+// XrBodyTrackingFidelityStatusMETA extends XrBodyJointLocationsFB
+typedef struct XrBodyTrackingFidelityStatusMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBodyTrackingFidelityMETA fidelity;
+} XrBodyTrackingFidelityStatusMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrRequestBodyTrackingFidelityMETA)(XrBodyTrackerFB bodyTracker, const XrBodyTrackingFidelityMETA fidelity);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestBodyTrackingFidelityMETA(
+ XrBodyTrackerFB bodyTracker,
+ const XrBodyTrackingFidelityMETA fidelity);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_FB_face_tracking2 is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_face_tracking2 1
+XR_DEFINE_HANDLE(XrFaceTracker2FB)
+#define XR_FB_face_tracking2_SPEC_VERSION 1
+#define XR_FB_FACE_TRACKING2_EXTENSION_NAME "XR_FB_face_tracking2"
+
+typedef enum XrFaceExpression2FB {
+ XR_FACE_EXPRESSION2_BROW_LOWERER_L_FB = 0,
+ XR_FACE_EXPRESSION2_BROW_LOWERER_R_FB = 1,
+ XR_FACE_EXPRESSION2_CHEEK_PUFF_L_FB = 2,
+ XR_FACE_EXPRESSION2_CHEEK_PUFF_R_FB = 3,
+ XR_FACE_EXPRESSION2_CHEEK_RAISER_L_FB = 4,
+ XR_FACE_EXPRESSION2_CHEEK_RAISER_R_FB = 5,
+ XR_FACE_EXPRESSION2_CHEEK_SUCK_L_FB = 6,
+ XR_FACE_EXPRESSION2_CHEEK_SUCK_R_FB = 7,
+ XR_FACE_EXPRESSION2_CHIN_RAISER_B_FB = 8,
+ XR_FACE_EXPRESSION2_CHIN_RAISER_T_FB = 9,
+ XR_FACE_EXPRESSION2_DIMPLER_L_FB = 10,
+ XR_FACE_EXPRESSION2_DIMPLER_R_FB = 11,
+ XR_FACE_EXPRESSION2_EYES_CLOSED_L_FB = 12,
+ XR_FACE_EXPRESSION2_EYES_CLOSED_R_FB = 13,
+ XR_FACE_EXPRESSION2_EYES_LOOK_DOWN_L_FB = 14,
+ XR_FACE_EXPRESSION2_EYES_LOOK_DOWN_R_FB = 15,
+ XR_FACE_EXPRESSION2_EYES_LOOK_LEFT_L_FB = 16,
+ XR_FACE_EXPRESSION2_EYES_LOOK_LEFT_R_FB = 17,
+ XR_FACE_EXPRESSION2_EYES_LOOK_RIGHT_L_FB = 18,
+ XR_FACE_EXPRESSION2_EYES_LOOK_RIGHT_R_FB = 19,
+ XR_FACE_EXPRESSION2_EYES_LOOK_UP_L_FB = 20,
+ XR_FACE_EXPRESSION2_EYES_LOOK_UP_R_FB = 21,
+ XR_FACE_EXPRESSION2_INNER_BROW_RAISER_L_FB = 22,
+ XR_FACE_EXPRESSION2_INNER_BROW_RAISER_R_FB = 23,
+ XR_FACE_EXPRESSION2_JAW_DROP_FB = 24,
+ XR_FACE_EXPRESSION2_JAW_SIDEWAYS_LEFT_FB = 25,
+ XR_FACE_EXPRESSION2_JAW_SIDEWAYS_RIGHT_FB = 26,
+ XR_FACE_EXPRESSION2_JAW_THRUST_FB = 27,
+ XR_FACE_EXPRESSION2_LID_TIGHTENER_L_FB = 28,
+ XR_FACE_EXPRESSION2_LID_TIGHTENER_R_FB = 29,
+ XR_FACE_EXPRESSION2_LIP_CORNER_DEPRESSOR_L_FB = 30,
+ XR_FACE_EXPRESSION2_LIP_CORNER_DEPRESSOR_R_FB = 31,
+ XR_FACE_EXPRESSION2_LIP_CORNER_PULLER_L_FB = 32,
+ XR_FACE_EXPRESSION2_LIP_CORNER_PULLER_R_FB = 33,
+ XR_FACE_EXPRESSION2_LIP_FUNNELER_LB_FB = 34,
+ XR_FACE_EXPRESSION2_LIP_FUNNELER_LT_FB = 35,
+ XR_FACE_EXPRESSION2_LIP_FUNNELER_RB_FB = 36,
+ XR_FACE_EXPRESSION2_LIP_FUNNELER_RT_FB = 37,
+ XR_FACE_EXPRESSION2_LIP_PRESSOR_L_FB = 38,
+ XR_FACE_EXPRESSION2_LIP_PRESSOR_R_FB = 39,
+ XR_FACE_EXPRESSION2_LIP_PUCKER_L_FB = 40,
+ XR_FACE_EXPRESSION2_LIP_PUCKER_R_FB = 41,
+ XR_FACE_EXPRESSION2_LIP_STRETCHER_L_FB = 42,
+ XR_FACE_EXPRESSION2_LIP_STRETCHER_R_FB = 43,
+ XR_FACE_EXPRESSION2_LIP_SUCK_LB_FB = 44,
+ XR_FACE_EXPRESSION2_LIP_SUCK_LT_FB = 45,
+ XR_FACE_EXPRESSION2_LIP_SUCK_RB_FB = 46,
+ XR_FACE_EXPRESSION2_LIP_SUCK_RT_FB = 47,
+ XR_FACE_EXPRESSION2_LIP_TIGHTENER_L_FB = 48,
+ XR_FACE_EXPRESSION2_LIP_TIGHTENER_R_FB = 49,
+ XR_FACE_EXPRESSION2_LIPS_TOWARD_FB = 50,
+ XR_FACE_EXPRESSION2_LOWER_LIP_DEPRESSOR_L_FB = 51,
+ XR_FACE_EXPRESSION2_LOWER_LIP_DEPRESSOR_R_FB = 52,
+ XR_FACE_EXPRESSION2_MOUTH_LEFT_FB = 53,
+ XR_FACE_EXPRESSION2_MOUTH_RIGHT_FB = 54,
+ XR_FACE_EXPRESSION2_NOSE_WRINKLER_L_FB = 55,
+ XR_FACE_EXPRESSION2_NOSE_WRINKLER_R_FB = 56,
+ XR_FACE_EXPRESSION2_OUTER_BROW_RAISER_L_FB = 57,
+ XR_FACE_EXPRESSION2_OUTER_BROW_RAISER_R_FB = 58,
+ XR_FACE_EXPRESSION2_UPPER_LID_RAISER_L_FB = 59,
+ XR_FACE_EXPRESSION2_UPPER_LID_RAISER_R_FB = 60,
+ XR_FACE_EXPRESSION2_UPPER_LIP_RAISER_L_FB = 61,
+ XR_FACE_EXPRESSION2_UPPER_LIP_RAISER_R_FB = 62,
+ XR_FACE_EXPRESSION2_TONGUE_TIP_INTERDENTAL_FB = 63,
+ XR_FACE_EXPRESSION2_TONGUE_TIP_ALVEOLAR_FB = 64,
+ XR_FACE_EXPRESSION2_TONGUE_FRONT_DORSAL_PALATE_FB = 65,
+ XR_FACE_EXPRESSION2_TONGUE_MID_DORSAL_PALATE_FB = 66,
+ XR_FACE_EXPRESSION2_TONGUE_BACK_DORSAL_VELAR_FB = 67,
+ XR_FACE_EXPRESSION2_TONGUE_OUT_FB = 68,
+ XR_FACE_EXPRESSION2_TONGUE_RETREAT_FB = 69,
+ XR_FACE_EXPRESSION2_COUNT_FB = 70,
+ XR_FACE_EXPRESSION_2FB_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpression2FB;
+
+typedef enum XrFaceExpressionSet2FB {
+ XR_FACE_EXPRESSION_SET2_DEFAULT_FB = 0,
+ XR_FACE_EXPRESSION_SET_2FB_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpressionSet2FB;
+
+typedef enum XrFaceTrackingDataSource2FB {
+ XR_FACE_TRACKING_DATA_SOURCE2_VISUAL_FB = 0,
+ XR_FACE_TRACKING_DATA_SOURCE2_AUDIO_FB = 1,
+ XR_FACE_TRACKING_DATA_SOURCE_2FB_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceTrackingDataSource2FB;
+
+typedef enum XrFaceConfidence2FB {
+ XR_FACE_CONFIDENCE2_LOWER_FACE_FB = 0,
+ XR_FACE_CONFIDENCE2_UPPER_FACE_FB = 1,
+ XR_FACE_CONFIDENCE2_COUNT_FB = 2,
+ XR_FACE_CONFIDENCE_2FB_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceConfidence2FB;
+// XrSystemFaceTrackingProperties2FB extends XrSystemProperties
+typedef struct XrSystemFaceTrackingProperties2FB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsVisualFaceTracking;
+ XrBool32 supportsAudioFaceTracking;
+} XrSystemFaceTrackingProperties2FB;
+
+typedef struct XrFaceTrackerCreateInfo2FB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFaceExpressionSet2FB faceExpressionSet;
+ uint32_t requestedDataSourceCount;
+ XrFaceTrackingDataSource2FB* requestedDataSources;
+} XrFaceTrackerCreateInfo2FB;
+
+typedef struct XrFaceExpressionInfo2FB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrFaceExpressionInfo2FB;
+
+typedef struct XrFaceExpressionWeights2FB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t weightCount;
+ float* weights;
+ uint32_t confidenceCount;
+ float* confidences;
+ XrBool32 isValid;
+ XrBool32 isEyeFollowingBlendshapesValid;
+ XrFaceTrackingDataSource2FB dataSource;
+ XrTime time;
+} XrFaceExpressionWeights2FB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTracker2FB)(XrSession session, const XrFaceTrackerCreateInfo2FB* createInfo, XrFaceTracker2FB* faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTracker2FB)(XrFaceTracker2FB faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFaceExpressionWeights2FB)(XrFaceTracker2FB faceTracker, const XrFaceExpressionInfo2FB* expressionInfo, XrFaceExpressionWeights2FB* expressionWeights);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTracker2FB(
+ XrSession session,
+ const XrFaceTrackerCreateInfo2FB* createInfo,
+ XrFaceTracker2FB* faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTracker2FB(
+ XrFaceTracker2FB faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeights2FB(
+ XrFaceTracker2FB faceTracker,
+ const XrFaceExpressionInfo2FB* expressionInfo,
+ XrFaceExpressionWeights2FB* expressionWeights);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_spatial_entity_sharing is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_sharing 1
+#define XR_META_spatial_entity_sharing_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_SHARING_EXTENSION_NAME "XR_META_spatial_entity_sharing"
+#define XR_MAX_SPACES_PER_SHARE_REQUEST_META 32
+// XrSystemSpatialEntitySharingPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemSpatialEntitySharingPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialEntitySharing;
+} XrSystemSpatialEntitySharingPropertiesMETA;
+
+typedef struct XR_MAY_ALIAS XrShareSpacesRecipientBaseHeaderMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrShareSpacesRecipientBaseHeaderMETA;
+
+typedef struct XrShareSpacesInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ const XrShareSpacesRecipientBaseHeaderMETA* recipientInfo;
+} XrShareSpacesInfoMETA;
+
+typedef struct XrEventDataShareSpacesCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataShareSpacesCompleteMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrShareSpacesMETA)(XrSession session, const XrShareSpacesInfoMETA* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesMETA(
+ XrSession session,
+ const XrShareSpacesInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_environment_depth is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_environment_depth 1
+XR_DEFINE_HANDLE(XrEnvironmentDepthProviderMETA)
+XR_DEFINE_HANDLE(XrEnvironmentDepthSwapchainMETA)
+#define XR_META_environment_depth_SPEC_VERSION 2
+#define XR_META_ENVIRONMENT_DEPTH_EXTENSION_NAME "XR_META_environment_depth"
+typedef XrFlags64 XrEnvironmentDepthProviderCreateFlagsMETA;
+
+// Flag bits for XrEnvironmentDepthProviderCreateFlagsMETA
+
+typedef XrFlags64 XrEnvironmentDepthSwapchainCreateFlagsMETA;
+
+// Flag bits for XrEnvironmentDepthSwapchainCreateFlagsMETA
+
+typedef struct XrEnvironmentDepthProviderCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentDepthProviderCreateFlagsMETA createFlags;
+} XrEnvironmentDepthProviderCreateInfoMETA;
+
+typedef struct XrEnvironmentDepthSwapchainCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentDepthSwapchainCreateFlagsMETA createFlags;
+} XrEnvironmentDepthSwapchainCreateInfoMETA;
+
+typedef struct XrEnvironmentDepthSwapchainStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t width;
+ uint32_t height;
+} XrEnvironmentDepthSwapchainStateMETA;
+
+typedef struct XrEnvironmentDepthImageAcquireInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime displayTime;
+} XrEnvironmentDepthImageAcquireInfoMETA;
+
+typedef struct XrEnvironmentDepthImageViewMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFovf fov;
+ XrPosef pose;
+} XrEnvironmentDepthImageViewMETA;
+
+typedef struct XrEnvironmentDepthImageMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t swapchainIndex;
+ float nearZ;
+ float farZ;
+ XrEnvironmentDepthImageViewMETA views[2];
+} XrEnvironmentDepthImageMETA;
+
+// XrEnvironmentDepthImageTimestampMETA extends XrEnvironmentDepthImageMETA
+typedef struct XrEnvironmentDepthImageTimestampMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime captureTime;
+} XrEnvironmentDepthImageTimestampMETA;
+
+typedef struct XrEnvironmentDepthHandRemovalSetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrEnvironmentDepthHandRemovalSetInfoMETA;
+
+// XrSystemEnvironmentDepthPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemEnvironmentDepthPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEnvironmentDepth;
+ XrBool32 supportsHandRemoval;
+} XrSystemEnvironmentDepthPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthProviderMETA)(XrSession session, const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, XrEnvironmentDepthProviderMETA* environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrStartEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrStopEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, XrEnvironmentDepthSwapchainMETA* swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthSwapchainMETA swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentDepthSwapchainImagesMETA)(XrEnvironmentDepthSwapchainMETA swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images);
+typedef XrResult (XRAPI_PTR *PFN_xrGetEnvironmentDepthSwapchainStateMETA)(XrEnvironmentDepthSwapchainMETA swapchain, XrEnvironmentDepthSwapchainStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrAcquireEnvironmentDepthImageMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, XrEnvironmentDepthImageMETA* environmentDepthImage);
+typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthHandRemovalMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthProviderMETA(
+ XrSession session,
+ const XrEnvironmentDepthProviderCreateInfoMETA* createInfo,
+ XrEnvironmentDepthProviderMETA* environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStartEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthSwapchainMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo,
+ XrEnvironmentDepthSwapchainMETA* swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthSwapchainMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentDepthSwapchainImagesMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain,
+ uint32_t imageCapacityInput,
+ uint32_t* imageCountOutput,
+ XrSwapchainImageBaseHeader* images);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetEnvironmentDepthSwapchainStateMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain,
+ XrEnvironmentDepthSwapchainStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAcquireEnvironmentDepthImageMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo,
+ XrEnvironmentDepthImageMETA* environmentDepthImage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthHandRemovalMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_uuid is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_uuid 1
+#define XR_EXT_uuid_SPEC_VERSION 1
+#define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid"
+#define XR_UUID_SIZE_EXT 16
+
+
+// XR_EXT_render_model is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_render_model 1
+XR_DEFINE_ATOM(XrRenderModelIdEXT)
+XR_DEFINE_HANDLE(XrRenderModelEXT)
+XR_DEFINE_HANDLE(XrRenderModelAssetEXT)
+#define XR_MAX_RENDER_MODEL_ASSET_NODE_NAME_SIZE_EXT 64
+#define XR_EXT_render_model_SPEC_VERSION 1
+#define XR_EXT_RENDER_MODEL_EXTENSION_NAME "XR_EXT_render_model"
+#define XR_NULL_RENDER_MODEL_ID_EXT 0
+typedef struct XrRenderModelCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrRenderModelIdEXT renderModelId;
+ uint32_t gltfExtensionCount;
+ const char* const* gltfExtensions;
+} XrRenderModelCreateInfoEXT;
+
+typedef struct XrRenderModelPropertiesGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrRenderModelPropertiesGetInfoEXT;
+
+typedef struct XrRenderModelPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT cacheId;
+ uint32_t animatableNodeCount;
+} XrRenderModelPropertiesEXT;
+
+typedef struct XrRenderModelSpaceCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrRenderModelEXT renderModel;
+} XrRenderModelSpaceCreateInfoEXT;
+
+typedef struct XrRenderModelStateGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime displayTime;
+} XrRenderModelStateGetInfoEXT;
+
+typedef struct XrRenderModelNodeStateEXT {
+ XrPosef nodePose;
+ XrBool32 isVisible;
+} XrRenderModelNodeStateEXT;
+
+typedef struct XrRenderModelStateEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t nodeStateCount;
+ XrRenderModelNodeStateEXT* nodeStates;
+} XrRenderModelStateEXT;
+
+typedef struct XrRenderModelAssetCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidEXT cacheId;
+} XrRenderModelAssetCreateInfoEXT;
+
+typedef struct XrRenderModelAssetDataGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrRenderModelAssetDataGetInfoEXT;
+
+typedef struct XrRenderModelAssetDataEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ uint8_t* buffer;
+} XrRenderModelAssetDataEXT;
+
+typedef struct XrRenderModelAssetPropertiesGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrRenderModelAssetPropertiesGetInfoEXT;
+
+typedef struct XrRenderModelAssetNodePropertiesEXT {
+ char uniqueName[XR_MAX_RENDER_MODEL_ASSET_NODE_NAME_SIZE_EXT];
+} XrRenderModelAssetNodePropertiesEXT;
+
+typedef struct XrRenderModelAssetPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t nodePropertyCount;
+ XrRenderModelAssetNodePropertiesEXT* nodeProperties;
+} XrRenderModelAssetPropertiesEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelEXT)(XrSession session, const XrRenderModelCreateInfoEXT* createInfo, XrRenderModelEXT* renderModel);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyRenderModelEXT)(XrRenderModelEXT renderModel);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPropertiesEXT)(XrRenderModelEXT renderModel, const XrRenderModelPropertiesGetInfoEXT* getInfo, XrRenderModelPropertiesEXT* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelSpaceEXT)(XrSession session, const XrRenderModelSpaceCreateInfoEXT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelAssetEXT)(XrSession session, const XrRenderModelAssetCreateInfoEXT* createInfo, XrRenderModelAssetEXT* asset);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyRenderModelAssetEXT)(XrRenderModelAssetEXT asset);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelAssetDataEXT)(XrRenderModelAssetEXT asset, const XrRenderModelAssetDataGetInfoEXT* getInfo, XrRenderModelAssetDataEXT* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelAssetPropertiesEXT)(XrRenderModelAssetEXT asset, const XrRenderModelAssetPropertiesGetInfoEXT* getInfo, XrRenderModelAssetPropertiesEXT* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelStateEXT)(XrRenderModelEXT renderModel, const XrRenderModelStateGetInfoEXT* getInfo, XrRenderModelStateEXT* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelEXT(
+ XrSession session,
+ const XrRenderModelCreateInfoEXT* createInfo,
+ XrRenderModelEXT* renderModel);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelEXT(
+ XrRenderModelEXT renderModel);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPropertiesEXT(
+ XrRenderModelEXT renderModel,
+ const XrRenderModelPropertiesGetInfoEXT* getInfo,
+ XrRenderModelPropertiesEXT* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelSpaceEXT(
+ XrSession session,
+ const XrRenderModelSpaceCreateInfoEXT* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelAssetEXT(
+ XrSession session,
+ const XrRenderModelAssetCreateInfoEXT* createInfo,
+ XrRenderModelAssetEXT* asset);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelAssetEXT(
+ XrRenderModelAssetEXT asset);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetDataEXT(
+ XrRenderModelAssetEXT asset,
+ const XrRenderModelAssetDataGetInfoEXT* getInfo,
+ XrRenderModelAssetDataEXT* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetPropertiesEXT(
+ XrRenderModelAssetEXT asset,
+ const XrRenderModelAssetPropertiesGetInfoEXT* getInfo,
+ XrRenderModelAssetPropertiesEXT* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelStateEXT(
+ XrRenderModelEXT renderModel,
+ const XrRenderModelStateGetInfoEXT* getInfo,
+ XrRenderModelStateEXT* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_interaction_render_model is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_interaction_render_model 1
+#define XR_EXT_interaction_render_model_SPEC_VERSION 1
+#define XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME "XR_EXT_interaction_render_model"
+typedef struct XrInteractionRenderModelIdsEnumerateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrInteractionRenderModelIdsEnumerateInfoEXT;
+
+typedef struct XrInteractionRenderModelSubactionPathInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrInteractionRenderModelSubactionPathInfoEXT;
+
+typedef struct XrInteractionRenderModelTopLevelUserPathGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t topLevelUserPathCount;
+ const XrPath* topLevelUserPaths;
+} XrInteractionRenderModelTopLevelUserPathGetInfoEXT;
+
+typedef struct XrEventDataInteractionRenderModelsChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEventDataInteractionRenderModelsChangedEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateInteractionRenderModelIdsEXT)(XrSession session, const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo, uint32_t renderModelIdCapacityInput, uint32_t* renderModelIdCountOutput, XrRenderModelIdEXT* renderModelIds);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRenderModelSubactionPathsEXT)(XrRenderModelEXT renderModel, const XrInteractionRenderModelSubactionPathInfoEXT* info, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrPath* paths);
+typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPoseTopLevelUserPathEXT)(XrRenderModelEXT renderModel, const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info, XrPath* topLevelUserPath);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInteractionRenderModelIdsEXT(
+ XrSession session,
+ const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo,
+ uint32_t renderModelIdCapacityInput,
+ uint32_t* renderModelIdCountOutput,
+ XrRenderModelIdEXT* renderModelIds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRenderModelSubactionPathsEXT(
+ XrRenderModelEXT renderModel,
+ const XrInteractionRenderModelSubactionPathInfoEXT* info,
+ uint32_t pathCapacityInput,
+ uint32_t* pathCountOutput,
+ XrPath* paths);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPoseTopLevelUserPathEXT(
+ XrRenderModelEXT renderModel,
+ const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info,
+ XrPath* topLevelUserPath);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_hand_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_hand_interaction 1
+#define XR_EXT_hand_interaction_SPEC_VERSION 2
+#define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction"
+
+
+// XR_QCOM_tracking_optimization_settings is a preprocessor guard. Do not pass it to API calls.
+#define XR_QCOM_tracking_optimization_settings 1
+#define XR_QCOM_tracking_optimization_settings_SPEC_VERSION 2
+#define XR_QCOM_TRACKING_OPTIMIZATION_SETTINGS_EXTENSION_NAME "XR_QCOM_tracking_optimization_settings"
+
+typedef enum XrTrackingOptimizationSettingsDomainQCOM {
+ XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_ALL_QCOM = 1,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_MAX_ENUM_QCOM = 0x7FFFFFFF
+} XrTrackingOptimizationSettingsDomainQCOM;
+
+typedef enum XrTrackingOptimizationSettingsHintQCOM {
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_NONE_QCOM = 0,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LONG_RANGE_PRIORITIZATION_QCOM = 1,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_CLOSE_RANGE_PRIORITIZATION_QCOM = 2,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LOW_POWER_PRIORITIZATION_QCOM = 3,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_HIGH_POWER_PRIORITIZATION_QCOM = 4,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LONG_RANGE_PRIORIZATION_QCOM = XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LONG_RANGE_PRIORITIZATION_QCOM,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_CLOSE_RANGE_PRIORIZATION_QCOM = XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_CLOSE_RANGE_PRIORITIZATION_QCOM,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LOW_POWER_PRIORIZATION_QCOM = XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LOW_POWER_PRIORITIZATION_QCOM,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_HIGH_POWER_PRIORIZATION_QCOM = XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_HIGH_POWER_PRIORITIZATION_QCOM,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_MAX_ENUM_QCOM = 0x7FFFFFFF
+} XrTrackingOptimizationSettingsHintQCOM;
+typedef XrResult (XRAPI_PTR *PFN_xrSetTrackingOptimizationSettingsHintQCOM)(XrSession session, XrTrackingOptimizationSettingsDomainQCOM domain, XrTrackingOptimizationSettingsHintQCOM hint);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetTrackingOptimizationSettingsHintQCOM(
+ XrSession session,
+ XrTrackingOptimizationSettingsDomainQCOM domain,
+ XrTrackingOptimizationSettingsHintQCOM hint);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_QCOM_hand_tracking_gesture is a preprocessor guard. Do not pass it to API calls.
+#define XR_QCOM_hand_tracking_gesture 1
+#define XR_QCOM_hand_tracking_gesture_SPEC_VERSION 1
+#define XR_QCOM_HAND_TRACKING_GESTURE_EXTENSION_NAME "XR_QCOM_hand_tracking_gesture"
+
+typedef enum XrHandGestureTypeQCOM {
+ XR_HAND_GESTURE_TYPE_UNKNOWN_QCOM = -1,
+ XR_HAND_GESTURE_TYPE_OPEN_HAND_QCOM = 0,
+ XR_HAND_GESTURE_TYPE_GRAB_QCOM = 2,
+ XR_HAND_GESTURE_TYPE_PINCH_QCOM = 7,
+ XR_HAND_GESTURE_TYPE_MAX_ENUM_QCOM = 0x7FFFFFFF
+} XrHandGestureTypeQCOM;
+typedef struct XrHandGestureQCOM {
+ XrHandGestureTypeQCOM gesture;
+ float gestureRatio;
+ float flipRatio;
+} XrHandGestureQCOM;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetHandGestureQCOM)(XrHandTrackerEXT handTracker, XrTime time, XrHandGestureQCOM* handGesture);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetHandGestureQCOM(
+ XrHandTrackerEXT handTracker,
+ XrTime time,
+ XrHandGestureQCOM* handGesture);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_passthrough is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_passthrough 1
+XR_DEFINE_HANDLE(XrPassthroughHTC)
+#define XR_HTC_passthrough_SPEC_VERSION 1
+#define XR_HTC_PASSTHROUGH_EXTENSION_NAME "XR_HTC_passthrough"
+
+typedef enum XrPassthroughFormHTC {
+ XR_PASSTHROUGH_FORM_PLANAR_HTC = 0,
+ XR_PASSTHROUGH_FORM_PROJECTED_HTC = 1,
+ XR_PASSTHROUGH_FORM_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrPassthroughFormHTC;
+typedef struct XrPassthroughCreateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughFormHTC form;
+} XrPassthroughCreateInfoHTC;
+
+typedef struct XrPassthroughColorHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float alpha;
+} XrPassthroughColorHTC;
+
+// XrPassthroughMeshTransformInfoHTC extends XrCompositionLayerPassthroughHTC
+typedef struct XrPassthroughMeshTransformInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t vertexCount;
+ const XrVector3f* vertices;
+ uint32_t indexCount;
+ const uint32_t* indices;
+ XrSpace baseSpace;
+ XrTime time;
+ XrPosef pose;
+ XrVector3f scale;
+} XrPassthroughMeshTransformInfoHTC;
+
+typedef struct XrCompositionLayerPassthroughHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrPassthroughHTC passthrough;
+ XrPassthroughColorHTC color;
+} XrCompositionLayerPassthroughHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughHTC)(XrSession session, const XrPassthroughCreateInfoHTC* createInfo, XrPassthroughHTC* passthrough);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughHTC)(XrPassthroughHTC passthrough);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughHTC(
+ XrSession session,
+ const XrPassthroughCreateInfoHTC* createInfo,
+ XrPassthroughHTC* passthrough);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughHTC(
+ XrPassthroughHTC passthrough);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_foveation is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_foveation 1
+#define XR_HTC_foveation_SPEC_VERSION 1
+#define XR_HTC_FOVEATION_EXTENSION_NAME "XR_HTC_foveation"
+
+typedef enum XrFoveationModeHTC {
+ XR_FOVEATION_MODE_DISABLE_HTC = 0,
+ XR_FOVEATION_MODE_FIXED_HTC = 1,
+ XR_FOVEATION_MODE_DYNAMIC_HTC = 2,
+ XR_FOVEATION_MODE_CUSTOM_HTC = 3,
+ XR_FOVEATION_MODE_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrFoveationModeHTC;
+
+typedef enum XrFoveationLevelHTC {
+ XR_FOVEATION_LEVEL_NONE_HTC = 0,
+ XR_FOVEATION_LEVEL_LOW_HTC = 1,
+ XR_FOVEATION_LEVEL_MEDIUM_HTC = 2,
+ XR_FOVEATION_LEVEL_HIGH_HTC = 3,
+ XR_FOVEATION_LEVEL_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrFoveationLevelHTC;
+typedef XrFlags64 XrFoveationDynamicFlagsHTC;
+
+// Flag bits for XrFoveationDynamicFlagsHTC
+static const XrFoveationDynamicFlagsHTC XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_BIT_HTC = 0x00000001;
+static const XrFoveationDynamicFlagsHTC XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC = 0x00000002;
+static const XrFoveationDynamicFlagsHTC XR_FOVEATION_DYNAMIC_FOCAL_CENTER_OFFSET_ENABLED_BIT_HTC = 0x00000004;
+
+typedef struct XrFoveationApplyInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFoveationModeHTC mode;
+ uint32_t subImageCount;
+ XrSwapchainSubImage* subImages;
+} XrFoveationApplyInfoHTC;
+
+typedef struct XrFoveationConfigurationHTC {
+ XrFoveationLevelHTC level;
+ float clearFovDegree;
+ XrVector2f focalCenterOffset;
+} XrFoveationConfigurationHTC;
+
+// XrFoveationDynamicModeInfoHTC extends XrFoveationApplyInfoHTC
+typedef struct XrFoveationDynamicModeInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFoveationDynamicFlagsHTC dynamicFlags;
+} XrFoveationDynamicModeInfoHTC;
+
+// XrFoveationCustomModeInfoHTC extends XrFoveationApplyInfoHTC
+typedef struct XrFoveationCustomModeInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t configCount;
+ const XrFoveationConfigurationHTC* configs;
+} XrFoveationCustomModeInfoHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrApplyFoveationHTC)(XrSession session, const XrFoveationApplyInfoHTC* applyInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrApplyFoveationHTC(
+ XrSession session,
+ const XrFoveationApplyInfoHTC* applyInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_anchor 1
+#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC 256
+#define XR_HTC_anchor_SPEC_VERSION 1
+#define XR_HTC_ANCHOR_EXTENSION_NAME "XR_HTC_anchor"
+// XrSystemAnchorPropertiesHTC extends XrSystemProperties
+typedef struct XrSystemAnchorPropertiesHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsAnchor;
+} XrSystemAnchorPropertiesHTC;
+
+typedef struct XrSpatialAnchorNameHTC {
+ char name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC];
+} XrSpatialAnchorNameHTC;
+
+typedef struct XrSpatialAnchorCreateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef poseInSpace;
+ XrSpatialAnchorNameHTC name;
+} XrSpatialAnchorCreateInfoHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorHTC)(XrSession session, const XrSpatialAnchorCreateInfoHTC* createInfo, XrSpace* anchor);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialAnchorNameHTC)(XrSpace anchor, XrSpatialAnchorNameHTC* name);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorHTC(
+ XrSession session,
+ const XrSpatialAnchorCreateInfoHTC* createInfo,
+ XrSpace* anchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC(
+ XrSpace anchor,
+ XrSpatialAnchorNameHTC* name);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_HTC_body_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_body_tracking 1
+
+#define XR_BODY_JOINT_COUNT_HTC 26
+
+XR_DEFINE_HANDLE(XrBodyTrackerHTC)
+#define XR_HTC_body_tracking_SPEC_VERSION 1
+#define XR_HTC_BODY_TRACKING_EXTENSION_NAME "XR_HTC_body_tracking"
+
+typedef enum XrBodyJointHTC {
+ XR_BODY_JOINT_PELVIS_HTC = 0,
+ XR_BODY_JOINT_LEFT_HIP_HTC = 1,
+ XR_BODY_JOINT_LEFT_KNEE_HTC = 2,
+ XR_BODY_JOINT_LEFT_ANKLE_HTC = 3,
+ XR_BODY_JOINT_LEFT_FEET_HTC = 4,
+ XR_BODY_JOINT_RIGHT_HIP_HTC = 5,
+ XR_BODY_JOINT_RIGHT_KNEE_HTC = 6,
+ XR_BODY_JOINT_RIGHT_ANKLE_HTC = 7,
+ XR_BODY_JOINT_RIGHT_FEET_HTC = 8,
+ XR_BODY_JOINT_WAIST_HTC = 9,
+ XR_BODY_JOINT_SPINE_LOWER_HTC = 10,
+ XR_BODY_JOINT_SPINE_MIDDLE_HTC = 11,
+ XR_BODY_JOINT_SPINE_HIGH_HTC = 12,
+ XR_BODY_JOINT_CHEST_HTC = 13,
+ XR_BODY_JOINT_NECK_HTC = 14,
+ XR_BODY_JOINT_HEAD_HTC = 15,
+ XR_BODY_JOINT_LEFT_CLAVICLE_HTC = 16,
+ XR_BODY_JOINT_LEFT_SCAPULA_HTC = 17,
+ XR_BODY_JOINT_LEFT_ARM_HTC = 18,
+ XR_BODY_JOINT_LEFT_ELBOW_HTC = 19,
+ XR_BODY_JOINT_LEFT_WRIST_HTC = 20,
+ XR_BODY_JOINT_RIGHT_CLAVICLE_HTC = 21,
+ XR_BODY_JOINT_RIGHT_SCAPULA_HTC = 22,
+ XR_BODY_JOINT_RIGHT_ARM_HTC = 23,
+ XR_BODY_JOINT_RIGHT_ELBOW_HTC = 24,
+ XR_BODY_JOINT_RIGHT_WRIST_HTC = 25,
+ XR_BODY_JOINT_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointHTC;
+
+typedef enum XrBodyJointSetHTC {
+ XR_BODY_JOINT_SET_FULL_HTC = 0,
+ XR_BODY_JOINT_SET_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointSetHTC;
+
+typedef enum XrBodyJointConfidenceHTC {
+ XR_BODY_JOINT_CONFIDENCE_NONE_HTC = 0,
+ XR_BODY_JOINT_CONFIDENCE_LOW_HTC = 1,
+ XR_BODY_JOINT_CONFIDENCE_HIGH_HTC = 2,
+ XR_BODY_JOINT_CONFIDENCE_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointConfidenceHTC;
+// XrSystemBodyTrackingPropertiesHTC extends XrSystemProperties
+typedef struct XrSystemBodyTrackingPropertiesHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTracking;
+} XrSystemBodyTrackingPropertiesHTC;
+
+typedef struct XrBodyTrackerCreateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBodyJointSetHTC bodyJointSet;
+} XrBodyTrackerCreateInfoHTC;
+
+typedef struct XrBodyJointsLocateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrBodyJointsLocateInfoHTC;
+
+typedef struct XrBodyJointLocationHTC {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrBodyJointLocationHTC;
+
+typedef struct XrBodyJointLocationsHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpaceLocationFlags combinedLocationFlags;
+ XrBodyJointConfidenceHTC confidenceLevel;
+ uint32_t jointLocationCount;
+ XrBodyJointLocationHTC* jointLocations;
+ uint32_t skeletonGenerationId;
+} XrBodyJointLocationsHTC;
+
+typedef struct XrBodySkeletonJointHTC {
+ XrPosef pose;
+} XrBodySkeletonJointHTC;
+
+typedef struct XrBodySkeletonHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCount;
+ XrBodySkeletonJointHTC* joints;
+} XrBodySkeletonHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateBodyTrackerHTC)(XrSession session, const XrBodyTrackerCreateInfoHTC* createInfo, XrBodyTrackerHTC* bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyBodyTrackerHTC)(XrBodyTrackerHTC bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateBodyJointsHTC)(XrBodyTrackerHTC bodyTracker, const XrBodyJointsLocateInfoHTC* locateInfo, XrBodyJointLocationsHTC* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetBodySkeletonHTC)(XrBodyTrackerHTC bodyTracker, XrSpace baseSpace, uint32_t skeletonGenerationId, XrBodySkeletonHTC* skeleton);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateBodyTrackerHTC(
+ XrSession session,
+ const XrBodyTrackerCreateInfoHTC* createInfo,
+ XrBodyTrackerHTC* bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyBodyTrackerHTC(
+ XrBodyTrackerHTC bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsHTC(
+ XrBodyTrackerHTC bodyTracker,
+ const XrBodyJointsLocateInfoHTC* locateInfo,
+ XrBodyJointLocationsHTC* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonHTC(
+ XrBodyTrackerHTC bodyTracker,
+ XrSpace baseSpace,
+ uint32_t skeletonGenerationId,
+ XrBodySkeletonHTC* skeleton);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_active_action_set_priority is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_active_action_set_priority 1
+#define XR_EXT_active_action_set_priority_SPEC_VERSION 1
+#define XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME "XR_EXT_active_action_set_priority"
+typedef struct XrActiveActionSetPriorityEXT {
+ XrActionSet actionSet;
+ uint32_t priorityOverride;
+} XrActiveActionSetPriorityEXT;
+
+// XrActiveActionSetPrioritiesEXT extends XrActionsSyncInfo
+typedef struct XrActiveActionSetPrioritiesEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t actionSetPriorityCount;
+ const XrActiveActionSetPriorityEXT* actionSetPriorities;
+} XrActiveActionSetPrioritiesEXT;
+
+
+
+// XR_MNDX_force_feedback_curl is a preprocessor guard. Do not pass it to API calls.
+#define XR_MNDX_force_feedback_curl 1
+#define XR_MNDX_force_feedback_curl_SPEC_VERSION 1
+#define XR_MNDX_FORCE_FEEDBACK_CURL_EXTENSION_NAME "XR_MNDX_force_feedback_curl"
+
+typedef enum XrForceFeedbackCurlLocationMNDX {
+ XR_FORCE_FEEDBACK_CURL_LOCATION_THUMB_CURL_MNDX = 0,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_INDEX_CURL_MNDX = 1,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_MIDDLE_CURL_MNDX = 2,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_RING_CURL_MNDX = 3,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_LITTLE_CURL_MNDX = 4,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_MAX_ENUM_MNDX = 0x7FFFFFFF
+} XrForceFeedbackCurlLocationMNDX;
+// XrSystemForceFeedbackCurlPropertiesMNDX extends XrSystemProperties
+typedef struct XrSystemForceFeedbackCurlPropertiesMNDX {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsForceFeedbackCurl;
+} XrSystemForceFeedbackCurlPropertiesMNDX;
+
+typedef struct XrForceFeedbackCurlApplyLocationMNDX {
+ XrForceFeedbackCurlLocationMNDX location;
+ float value;
+} XrForceFeedbackCurlApplyLocationMNDX;
+
+typedef struct XrForceFeedbackCurlApplyLocationsMNDX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrForceFeedbackCurlApplyLocationMNDX* locations;
+} XrForceFeedbackCurlApplyLocationsMNDX;
+
+typedef XrResult (XRAPI_PTR *PFN_xrApplyForceFeedbackCurlMNDX)(XrHandTrackerEXT handTracker, const XrForceFeedbackCurlApplyLocationsMNDX* locations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX(
+ XrHandTrackerEXT handTracker,
+ const XrForceFeedbackCurlApplyLocationsMNDX* locations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_controller_interaction 1
+#define XR_BD_controller_interaction_SPEC_VERSION 2
+#define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction"
+
+
+// XR_BD_body_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_body_tracking 1
+
+#define XR_BODY_JOINT_COUNT_BD 24
+
+
+#define XR_BODY_JOINT_WITHOUT_ARM_COUNT_BD 16
+
+XR_DEFINE_HANDLE(XrBodyTrackerBD)
+#define XR_BD_body_tracking_SPEC_VERSION 1
+#define XR_BD_BODY_TRACKING_EXTENSION_NAME "XR_BD_body_tracking"
+
+typedef enum XrBodyJointBD {
+ XR_BODY_JOINT_PELVIS_BD = 0,
+ XR_BODY_JOINT_LEFT_HIP_BD = 1,
+ XR_BODY_JOINT_RIGHT_HIP_BD = 2,
+ XR_BODY_JOINT_SPINE1_BD = 3,
+ XR_BODY_JOINT_LEFT_KNEE_BD = 4,
+ XR_BODY_JOINT_RIGHT_KNEE_BD = 5,
+ XR_BODY_JOINT_SPINE2_BD = 6,
+ XR_BODY_JOINT_LEFT_ANKLE_BD = 7,
+ XR_BODY_JOINT_RIGHT_ANKLE_BD = 8,
+ XR_BODY_JOINT_SPINE3_BD = 9,
+ XR_BODY_JOINT_LEFT_FOOT_BD = 10,
+ XR_BODY_JOINT_RIGHT_FOOT_BD = 11,
+ XR_BODY_JOINT_NECK_BD = 12,
+ XR_BODY_JOINT_LEFT_COLLAR_BD = 13,
+ XR_BODY_JOINT_RIGHT_COLLAR_BD = 14,
+ XR_BODY_JOINT_HEAD_BD = 15,
+ XR_BODY_JOINT_LEFT_SHOULDER_BD = 16,
+ XR_BODY_JOINT_RIGHT_SHOULDER_BD = 17,
+ XR_BODY_JOINT_LEFT_ELBOW_BD = 18,
+ XR_BODY_JOINT_RIGHT_ELBOW_BD = 19,
+ XR_BODY_JOINT_LEFT_WRIST_BD = 20,
+ XR_BODY_JOINT_RIGHT_WRIST_BD = 21,
+ XR_BODY_JOINT_LEFT_HAND_BD = 22,
+ XR_BODY_JOINT_RIGHT_HAND_BD = 23,
+ XR_BODY_JOINT_MAX_ENUM_BD = 0x7FFFFFFF
+} XrBodyJointBD;
+
+typedef enum XrBodyJointSetBD {
+ XR_BODY_JOINT_SET_BODY_WITHOUT_ARM_BD = 1,
+ XR_BODY_JOINT_SET_FULL_BODY_JOINTS_BD = 2,
+ XR_BODY_JOINT_SET_MAX_ENUM_BD = 0x7FFFFFFF
+} XrBodyJointSetBD;
+// XrSystemBodyTrackingPropertiesBD extends XrSystemProperties
+typedef struct XrSystemBodyTrackingPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTracking;
+} XrSystemBodyTrackingPropertiesBD;
+
+typedef struct XrBodyTrackerCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBodyJointSetBD jointSet;
+} XrBodyTrackerCreateInfoBD;
+
+typedef struct XrBodyJointsLocateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrBodyJointsLocateInfoBD;
+
+typedef struct XrBodyJointLocationBD {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrBodyJointLocationBD;
+
+typedef struct XrBodyJointLocationsBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 allJointPosesTracked;
+ uint32_t jointLocationCount;
+ XrBodyJointLocationBD* jointLocations;
+} XrBodyJointLocationsBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateBodyTrackerBD)(XrSession session, const XrBodyTrackerCreateInfoBD* createInfo, XrBodyTrackerBD* bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyBodyTrackerBD)(XrBodyTrackerBD bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateBodyJointsBD)(XrBodyTrackerBD bodyTracker, const XrBodyJointsLocateInfoBD* locateInfo, XrBodyJointLocationsBD* locations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateBodyTrackerBD(
+ XrSession session,
+ const XrBodyTrackerCreateInfoBD* createInfo,
+ XrBodyTrackerBD* bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyBodyTrackerBD(
+ XrBodyTrackerBD bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsBD(
+ XrBodyTrackerBD bodyTracker,
+ const XrBodyJointsLocateInfoBD* locateInfo,
+ XrBodyJointLocationsBD* locations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_facial_simulation is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_facial_simulation 1
+
+#define XR_FACE_EXPRESSION_COUNT_BD 52
+
+
+#define XR_LIP_EXPRESSION_COUNT_BD 20
+
+ XR_DEFINE_HANDLE(XrFaceTrackerBD)
+#define XR_BD_facial_simulation_SPEC_VERSION 1
+#define XR_BD_FACIAL_SIMULATION_EXTENSION_NAME "XR_BD_facial_simulation"
+
+typedef enum XrFacialSimulationModeBD {
+ XR_FACIAL_SIMULATION_MODE_DEFAULT_BD = 0,
+ XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_BD = 1,
+ XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_WITH_LIP_BD = 2,
+ XR_FACIAL_SIMULATION_MODE_ONLY_AUDIO_WITH_LIP_BD = 3,
+ XR_FACIAL_SIMULATION_MODE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrFacialSimulationModeBD;
+
+typedef enum XrFaceExpressionBD {
+ XR_FACE_EXPRESSION_BROW_DROP_L_BD = 0,
+ XR_FACE_EXPRESSION_BROW_DROP_R_BD = 1,
+ XR_FACE_EXPRESSION_BROW_INNER_UPWARDS_BD = 2,
+ XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_L_BD = 3,
+ XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_R_BD = 4,
+ XR_FACE_EXPRESSION_EYE_BLINK_L_BD = 5,
+ XR_FACE_EXPRESSION_EYE_LOOK_DROP_L_BD = 6,
+ XR_FACE_EXPRESSION_EYE_LOOK_IN_L_BD = 7,
+ XR_FACE_EXPRESSION_EYE_LOOK_OUT_L_BD = 8,
+ XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_L_BD = 9,
+ XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_L_BD = 10,
+ XR_FACE_EXPRESSION_EYE_LOOK_WIDE_L_BD = 11,
+ XR_FACE_EXPRESSION_EYE_BLINK_R_BD = 12,
+ XR_FACE_EXPRESSION_EYE_LOOK_DROP_R_BD = 13,
+ XR_FACE_EXPRESSION_EYE_LOOK_IN_R_BD = 14,
+ XR_FACE_EXPRESSION_EYE_LOOK_OUT_R_BD = 15,
+ XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_R_BD = 16,
+ XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_R_BD = 17,
+ XR_FACE_EXPRESSION_EYE_LOOK_WIDE_R_BD = 18,
+ XR_FACE_EXPRESSION_NOSE_SNEER_L_BD = 19,
+ XR_FACE_EXPRESSION_NOSE_SNEER_R_BD = 20,
+ XR_FACE_EXPRESSION_CHEEK_PUFF_BD = 21,
+ XR_FACE_EXPRESSION_CHEEK_SQUINT_L_BD = 22,
+ XR_FACE_EXPRESSION_CHEEK_SQUINT_R_BD = 23,
+ XR_FACE_EXPRESSION_MOUTH_CLOSE_BD = 24,
+ XR_FACE_EXPRESSION_MOUTH_FUNNEL_BD = 25,
+ XR_FACE_EXPRESSION_MOUTH_PUCKER_BD = 26,
+ XR_FACE_EXPRESSION_MOUTH_L_BD = 27,
+ XR_FACE_EXPRESSION_MOUTH_R_BD = 28,
+ XR_FACE_EXPRESSION_MOUTH_SMILE_L_BD = 29,
+ XR_FACE_EXPRESSION_MOUTH_SMILE_R_BD = 30,
+ XR_FACE_EXPRESSION_MOUTH_FROWN_L_BD = 31,
+ XR_FACE_EXPRESSION_MOUTH_FROWN_R_BD = 32,
+ XR_FACE_EXPRESSION_MOUTH_DIMPLE_L_BD = 33,
+ XR_FACE_EXPRESSION_MOUTH_DIMPLE_R_BD = 34,
+ XR_FACE_EXPRESSION_MOUTH_STRETCH_L_BD = 35,
+ XR_FACE_EXPRESSION_MOUTH_STRETCH_R_BD = 36,
+ XR_FACE_EXPRESSION_MOUTH_ROLL_LOWER_BD = 37,
+ XR_FACE_EXPRESSION_MOUTH_ROLL_UPPER_BD = 38,
+ XR_FACE_EXPRESSION_MOUTH_SHRUG_LOWER_BD = 39,
+ XR_FACE_EXPRESSION_MOUTH_SHRUG_UPPER_BD = 40,
+ XR_FACE_EXPRESSION_MOUTH_PRESS_L_BD = 41,
+ XR_FACE_EXPRESSION_MOUTH_PRESS_R_BD = 42,
+ XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_L_BD = 43,
+ XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_R_BD = 44,
+ XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_L_BD = 45,
+ XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_R_BD = 46,
+ XR_FACE_EXPRESSION_JAW_FORWARD_BD = 47,
+ XR_FACE_EXPRESSION_JAW_L_BD = 48,
+ XR_FACE_EXPRESSION_JAW_R_BD = 49,
+ XR_FACE_EXPRESSION_JAW_OPEN_BD = 50,
+ XR_FACE_EXPRESSION_TONGUE_OUT_BD = 51,
+ XR_FACE_EXPRESSION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrFaceExpressionBD;
+
+typedef enum XrLipExpressionBD {
+ XR_LIP_EXPRESSION_PP_BD = 0,
+ XR_LIP_EXPRESSION_CH_BD = 1,
+ XR_LIP_EXPRESSION_LO_BD = 2,
+ XR_LIP_EXPRESSION_O_BD = 3,
+ XR_LIP_EXPRESSION_I_BD = 4,
+ XR_LIP_EXPRESSION_LU_BD = 5,
+ XR_LIP_EXPRESSION_RR_BD = 6,
+ XR_LIP_EXPRESSION_XX_BD = 7,
+ XR_LIP_EXPRESSION_LAA_BD = 8,
+ XR_LIP_EXPRESSION_LI_BD = 9,
+ XR_LIP_EXPRESSION_FF_BD = 10,
+ XR_LIP_EXPRESSION_U_BD = 11,
+ XR_LIP_EXPRESSION_TH_BD = 12,
+ XR_LIP_EXPRESSION_LKK_BD = 13,
+ XR_LIP_EXPRESSION_SS_BD = 14,
+ XR_LIP_EXPRESSION_LE_BD = 15,
+ XR_LIP_EXPRESSION_DD_BD = 16,
+ XR_LIP_EXPRESSION_E_BD = 17,
+ XR_LIP_EXPRESSION_LNN_BD = 18,
+ XR_LIP_EXPRESSION_SIL_BD = 19,
+ XR_LIP_EXPRESSION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrLipExpressionBD;
+// XrSystemFacialSimulationPropertiesBD extends XrSystemProperties
+typedef struct XrSystemFacialSimulationPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFaceTracking;
+} XrSystemFacialSimulationPropertiesBD;
+
+typedef struct XrFaceTrackerCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFacialSimulationModeBD mode;
+} XrFaceTrackerCreateInfoBD;
+
+typedef struct XrFacialSimulationDataGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrFacialSimulationDataGetInfoBD;
+
+typedef struct XrFacialSimulationDataBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t faceExpressionWeightCount;
+ float* faceExpressionWeights;
+ XrBool32 isUpperFaceDataValid;
+ XrBool32 isLowerFaceDataValid;
+ XrTime time;
+} XrFacialSimulationDataBD;
+
+// XrLipExpressionDataBD extends XrFacialSimulationDataBD
+typedef struct XrLipExpressionDataBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t lipsyncExpressionWeightCount;
+ float* lipsyncExpressionWeights;
+} XrLipExpressionDataBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateFacialSimulationModesBD)(XrSession session, uint32_t modeCapacityInput, uint32_t* modeCountOutput, XrFacialSimulationModeBD* modes);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerBD)(XrSession session, const XrFaceTrackerCreateInfoBD* createInfo, XrFaceTrackerBD* tracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerBD)(XrFaceTrackerBD tracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFacialSimulationDataBD)(XrFaceTrackerBD tracker, const XrFacialSimulationDataGetInfoBD*info, XrFacialSimulationDataBD* facialData);
+typedef XrResult (XRAPI_PTR *PFN_xrSetFacialSimulationModeBD)(XrFaceTrackerBD tracker, XrFacialSimulationModeBD mode);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFacialSimulationModeBD)(XrFaceTrackerBD tracker, XrFacialSimulationModeBD*mode);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateFacialSimulationModesBD(
+ XrSession session,
+ uint32_t modeCapacityInput,
+ uint32_t* modeCountOutput,
+ XrFacialSimulationModeBD* modes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerBD(
+ XrSession session,
+ const XrFaceTrackerCreateInfoBD* createInfo,
+ XrFaceTrackerBD* tracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerBD(
+ XrFaceTrackerBD tracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialSimulationDataBD(
+ XrFaceTrackerBD tracker,
+ const XrFacialSimulationDataGetInfoBD* info,
+ XrFacialSimulationDataBD* facialData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetFacialSimulationModeBD(
+ XrFaceTrackerBD tracker,
+ XrFacialSimulationModeBD mode);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialSimulationModeBD(
+ XrFaceTrackerBD tracker,
+ XrFacialSimulationModeBD* mode);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_sensing is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_sensing 1
+XR_DEFINE_ATOM(XrSpatialEntityIdBD)
+XR_DEFINE_HANDLE(XrSenseDataProviderBD)
+XR_DEFINE_HANDLE(XrSenseDataSnapshotBD)
+XR_DEFINE_HANDLE(XrAnchorBD)
+#define XR_BD_spatial_sensing_SPEC_VERSION 3
+#define XR_BD_SPATIAL_SENSING_EXTENSION_NAME "XR_BD_spatial_sensing"
+
+typedef enum XrSpatialEntityComponentTypeBD {
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_BD = 0,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_BD = 1,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_BD = 2,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_BD = 3,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_BD = 4,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_BD = 5,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_SPHERE_BD = 6,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_PLANE_ORIENTATION_BD = 1000396000,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_LIGHT_ESTIMATION_BD = 1000397000,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_DYNAMIC_OBJECT_BD = 1000746000,
+ XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSpatialEntityComponentTypeBD;
+
+typedef enum XrSemanticLabelBD {
+ XR_SEMANTIC_LABEL_UNKNOWN_BD = 0,
+ XR_SEMANTIC_LABEL_FLOOR_BD = 1,
+ XR_SEMANTIC_LABEL_CEILING_BD = 2,
+ XR_SEMANTIC_LABEL_WALL_BD = 3,
+ XR_SEMANTIC_LABEL_DOOR_BD = 4,
+ XR_SEMANTIC_LABEL_WINDOW_BD = 5,
+ XR_SEMANTIC_LABEL_OPENING_BD = 6,
+ XR_SEMANTIC_LABEL_TABLE_BD = 7,
+ XR_SEMANTIC_LABEL_SOFA_BD = 8,
+ XR_SEMANTIC_LABEL_CHAIR_BD = 9,
+ XR_SEMANTIC_LABEL_HUMAN_BD = 10,
+ XR_SEMANTIC_LABEL_BEAM_BD = 11,
+ XR_SEMANTIC_LABEL_COLUMN_BD = 12,
+ XR_SEMANTIC_LABEL_CURTAIN_BD = 13,
+ XR_SEMANTIC_LABEL_CABINET_BD = 14,
+ XR_SEMANTIC_LABEL_BED_BD = 15,
+ XR_SEMANTIC_LABEL_PLANT_BD = 16,
+ XR_SEMANTIC_LABEL_SCREEN_BD = 17,
+ XR_SEMANTIC_LABEL_VIRTUAL_WALL_BD = 18,
+ XR_SEMANTIC_LABEL_REFRIGERATOR_BD = 19,
+ XR_SEMANTIC_LABEL_WASHING_MACHINE_BD = 20,
+ XR_SEMANTIC_LABEL_AIR_CONDITIONER_BD = 21,
+ XR_SEMANTIC_LABEL_LAMP_BD = 22,
+ XR_SEMANTIC_LABEL_WALL_ART_BD = 23,
+ XR_SEMANTIC_LABEL_STAIRWAY_BD = 24,
+ XR_SEMANTIC_LABEL_KEYBOARD_BD = 25,
+ XR_SEMANTIC_LABEL_MOUSE_BD = 26,
+ XR_SEMANTIC_LABEL_LAPTOP_BD = 27,
+ XR_SEMANTIC_LABEL_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSemanticLabelBD;
+
+typedef enum XrSenseDataProviderTypeBD {
+ XR_SENSE_DATA_PROVIDER_TYPE_ANCHOR_BD = 1000390000,
+ XR_SENSE_DATA_PROVIDER_TYPE_SCENE_BD = 1000392000,
+ XR_SENSE_DATA_PROVIDER_TYPE_MESH_BD = 1000393000,
+ XR_SENSE_DATA_PROVIDER_TYPE_PLANE_BD = 1000396000,
+ XR_SENSE_DATA_PROVIDER_TYPE_LIGHT_ESTIMATION_BD = 1000397000,
+ XR_SENSE_DATA_PROVIDER_TYPE_DYNAMIC_OBJECT_BD = 1000746000,
+ XR_SENSE_DATA_PROVIDER_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSenseDataProviderTypeBD;
+
+typedef enum XrSenseDataProviderStateBD {
+ XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_BD = 0,
+ XR_SENSE_DATA_PROVIDER_STATE_RUNNING_BD = 1,
+ XR_SENSE_DATA_PROVIDER_STATE_STOPPED_BD = 2,
+ XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSenseDataProviderStateBD;
+// XrSystemSpatialSensingPropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialSensingPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialSensing;
+} XrSystemSpatialSensingPropertiesBD;
+
+typedef struct XrSpatialEntityComponentGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialEntityIdBD entityId;
+ XrSpatialEntityComponentTypeBD componentType;
+} XrSpatialEntityComponentGetInfoBD;
+
+typedef struct XrSpatialEntityComponentDataBaseHeaderBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrSpatialEntityComponentDataBaseHeaderBD;
+
+// XrSpatialEntityLocationGetInfoBD extends XrSpatialEntityComponentGetInfoBD
+typedef struct XrSpatialEntityLocationGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+} XrSpatialEntityLocationGetInfoBD;
+
+// XrSpatialEntityComponentDataLocationBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataLocationBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpaceLocation location;
+} XrSpatialEntityComponentDataLocationBD;
+
+// XrSpatialEntityComponentDataSemanticBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataSemanticBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t labelCapacityInput;
+ uint32_t labelCountOutput;
+ XrSemanticLabelBD* labels;
+} XrSpatialEntityComponentDataSemanticBD;
+
+// XrSpatialEntityComponentDataBoundingBox2DBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataBoundingBox2DBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrRect2Df boundingBox2D;
+} XrSpatialEntityComponentDataBoundingBox2DBD;
+
+// XrSpatialEntityComponentDataPolygonBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataPolygonBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector2f* vertices;
+} XrSpatialEntityComponentDataPolygonBD;
+
+// XrSpatialEntityComponentDataBoundingBox3DBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataBoundingBox3DBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBoxf boundingBox3D;
+} XrSpatialEntityComponentDataBoundingBox3DBD;
+
+// XrSpatialEntityComponentDataTriangleMeshBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataTriangleMeshBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertices;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint16_t* indices;
+} XrSpatialEntityComponentDataTriangleMeshBD;
+
+// XrSpatialEntityComponentDataSphereBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataSphereBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpheref sphere;
+} XrSpatialEntityComponentDataSphereBD;
+
+typedef struct XrSenseDataProviderCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSenseDataProviderTypeBD providerType;
+} XrSenseDataProviderCreateInfoBD;
+
+typedef struct XrSenseDataProviderStartInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSenseDataProviderStartInfoBD;
+
+typedef struct XrEventDataSenseDataProviderStateChangedBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSenseDataProviderBD provider;
+ XrSenseDataProviderStateBD newState;
+} XrEventDataSenseDataProviderStateChangedBD;
+
+typedef struct XrEventDataSenseDataUpdatedBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSenseDataProviderBD provider;
+} XrEventDataSenseDataUpdatedBD;
+
+typedef struct XrSenseDataQueryInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSenseDataQueryInfoBD;
+
+typedef struct XrSenseDataQueryCompletionBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSenseDataSnapshotBD snapshot;
+} XrSenseDataQueryCompletionBD;
+
+typedef struct XrQueriedSenseDataGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrQueriedSenseDataGetInfoBD;
+
+typedef struct XrSpatialEntityStateBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpatialEntityIdBD entityId;
+ XrTime lastUpdateTime;
+ XrUuidEXT uuid;
+} XrSpatialEntityStateBD;
+
+typedef struct XrQueriedSenseDataBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t stateCapacityInput;
+ uint32_t stateCountOutput;
+ XrSpatialEntityStateBD* states;
+} XrQueriedSenseDataBD;
+
+// XrSenseDataFilterUuidBD extends XrSenseDataQueryInfoBD
+typedef struct XrSenseDataFilterUuidBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSenseDataFilterUuidBD;
+
+// XrSenseDataFilterSemanticBD extends XrSenseDataQueryInfoBD
+typedef struct XrSenseDataFilterSemanticBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t labelCount;
+ const XrSemanticLabelBD* labels;
+} XrSenseDataFilterSemanticBD;
+
+typedef struct XrSpatialEntityAnchorCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSenseDataSnapshotBD snapshot;
+ XrSpatialEntityIdBD entityId;
+} XrSpatialEntityAnchorCreateInfoBD;
+
+typedef struct XrAnchorSpaceCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAnchorBD anchor;
+ XrPosef poseInAnchorSpace;
+} XrAnchorSpaceCreateInfoBD;
+
+typedef struct XrFutureCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrFutureCompletionEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialEntityComponentTypesBD)(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, uint32_t componentTypeCapacityInput, uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypeBD* componentTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityUuidBD)(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, XrUuidEXT* uuid);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityComponentDataBD)(XrSenseDataSnapshotBD snapshot, const XrSpatialEntityComponentGetInfoBD* getInfo, XrSpatialEntityComponentDataBaseHeaderBD* componentData);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSenseDataProviderBD)(XrSession session, const XrSenseDataProviderCreateInfoBD* createInfo, XrSenseDataProviderBD* provider);
+typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderAsyncBD)(XrSenseDataProviderBD provider, const XrSenseDataProviderStartInfoBD* startInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderCompleteBD)(XrSession session, XrFutureEXT future, XrFutureCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSenseDataProviderStateBD)(XrSenseDataProviderBD provider, XrSenseDataProviderStateBD* state);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataAsyncBD)(XrSenseDataProviderBD provider, const XrSenseDataQueryInfoBD* queryInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrSenseDataQueryCompletionBD* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataSnapshotBD)(XrSenseDataSnapshotBD snapshot);
+typedef XrResult (XRAPI_PTR *PFN_xrGetQueriedSenseDataBD)(XrSenseDataSnapshotBD snapshot, XrQueriedSenseDataGetInfoBD* getInfo, XrQueriedSenseDataBD* queriedSenseData);
+typedef XrResult (XRAPI_PTR *PFN_xrStopSenseDataProviderBD)(XrSenseDataProviderBD provider);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataProviderBD)(XrSenseDataProviderBD provider);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialEntityAnchorBD)(XrSenseDataProviderBD provider, const XrSpatialEntityAnchorCreateInfoBD* createInfo, XrAnchorBD* anchor);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyAnchorBD)(XrAnchorBD anchor);
+typedef XrResult (XRAPI_PTR *PFN_xrGetAnchorUuidBD)(XrAnchorBD anchor, XrUuidEXT* uuid);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateAnchorSpaceBD)(XrSession session, const XrAnchorSpaceCreateInfoBD* createInfo, XrSpace* space);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialEntityComponentTypesBD(
+ XrSenseDataSnapshotBD snapshot,
+ XrSpatialEntityIdBD entityId,
+ uint32_t componentTypeCapacityInput,
+ uint32_t* componentTypeCountOutput,
+ XrSpatialEntityComponentTypeBD* componentTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityUuidBD(
+ XrSenseDataSnapshotBD snapshot,
+ XrSpatialEntityIdBD entityId,
+ XrUuidEXT* uuid);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityComponentDataBD(
+ XrSenseDataSnapshotBD snapshot,
+ const XrSpatialEntityComponentGetInfoBD* getInfo,
+ XrSpatialEntityComponentDataBaseHeaderBD* componentData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSenseDataProviderBD(
+ XrSession session,
+ const XrSenseDataProviderCreateInfoBD* createInfo,
+ XrSenseDataProviderBD* provider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSenseDataProviderStartInfoBD* startInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderCompleteBD(
+ XrSession session,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSenseDataProviderStateBD(
+ XrSenseDataProviderBD provider,
+ XrSenseDataProviderStateBD* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSenseDataQueryInfoBD* queryInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrSenseDataQueryCompletionBD* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataSnapshotBD(
+ XrSenseDataSnapshotBD snapshot);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetQueriedSenseDataBD(
+ XrSenseDataSnapshotBD snapshot,
+ XrQueriedSenseDataGetInfoBD* getInfo,
+ XrQueriedSenseDataBD* queriedSenseData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopSenseDataProviderBD(
+ XrSenseDataProviderBD provider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataProviderBD(
+ XrSenseDataProviderBD provider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityAnchorBD(
+ XrSenseDataProviderBD provider,
+ const XrSpatialEntityAnchorCreateInfoBD* createInfo,
+ XrAnchorBD* anchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAnchorBD(
+ XrAnchorBD anchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorUuidBD(
+ XrAnchorBD anchor,
+ XrUuidEXT* uuid);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateAnchorSpaceBD(
+ XrSession session,
+ const XrAnchorSpaceCreateInfoBD* createInfo,
+ XrSpace* space);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_anchor 1
+#define XR_BD_spatial_anchor_SPEC_VERSION 2
+#define XR_BD_SPATIAL_ANCHOR_EXTENSION_NAME "XR_BD_spatial_anchor"
+
+typedef enum XrPersistenceLocationBD {
+ XR_PERSISTENCE_LOCATION_LOCAL_BD = 0,
+ XR_PERSISTENCE_LOCATION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrPersistenceLocationBD;
+// XrSystemSpatialAnchorPropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialAnchorPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialAnchor;
+} XrSystemSpatialAnchorPropertiesBD;
+
+typedef struct XrSpatialAnchorCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef pose;
+ XrTime time;
+} XrSpatialAnchorCreateInfoBD;
+
+typedef struct XrSpatialAnchorCreateCompletionBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrUuidEXT uuid;
+ XrAnchorBD anchor;
+} XrSpatialAnchorCreateCompletionBD;
+
+typedef struct XrSpatialAnchorPersistInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPersistenceLocationBD location;
+ XrAnchorBD anchor;
+} XrSpatialAnchorPersistInfoBD;
+
+typedef struct XrSpatialAnchorUnpersistInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPersistenceLocationBD location;
+ XrAnchorBD anchor;
+} XrSpatialAnchorUnpersistInfoBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorCreateInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrSpatialAnchorCreateCompletionBD* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorPersistInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorUnpersistInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSpatialAnchorCreateInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrSpatialAnchorCreateCompletionBD* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSpatialAnchorPersistInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSpatialAnchorUnpersistInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_anchor_sharing is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_anchor_sharing 1
+#define XR_BD_spatial_anchor_sharing_SPEC_VERSION 2
+#define XR_BD_SPATIAL_ANCHOR_SHARING_EXTENSION_NAME "XR_BD_spatial_anchor_sharing"
+// XrSystemSpatialAnchorSharingPropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialAnchorSharingPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialAnchorSharing;
+} XrSystemSpatialAnchorSharingPropertiesBD;
+
+typedef struct XrSpatialAnchorShareInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAnchorBD anchor;
+} XrSpatialAnchorShareInfoBD;
+
+typedef struct XrSharedSpatialAnchorDownloadInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+} XrSharedSpatialAnchorDownloadInfoBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorShareInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSharedSpatialAnchorDownloadInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSpatialAnchorShareInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSharedSpatialAnchorDownloadInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_scene is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_scene 1
+#define XR_BD_spatial_scene_SPEC_VERSION 1
+#define XR_BD_SPATIAL_SCENE_EXTENSION_NAME "XR_BD_spatial_scene"
+// XrSystemSpatialScenePropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialScenePropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialScene;
+} XrSystemSpatialScenePropertiesBD;
+
+typedef struct XrSceneCaptureInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSceneCaptureInfoBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCaptureSceneAsyncBD)(XrSenseDataProviderBD provider, const XrSceneCaptureInfoBD* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCaptureSceneCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneAsyncBD(
+ XrSenseDataProviderBD provider,
+ const XrSceneCaptureInfoBD* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneCompleteBD(
+ XrSenseDataProviderBD provider,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_mesh 1
+#define XR_BD_spatial_mesh_SPEC_VERSION 1
+#define XR_BD_SPATIAL_MESH_EXTENSION_NAME "XR_BD_spatial_mesh"
+
+typedef enum XrSpatialMeshLodBD {
+ XR_SPATIAL_MESH_LOD_COARSE_BD = 0,
+ XR_SPATIAL_MESH_LOD_MEDIUM_BD = 1,
+ XR_SPATIAL_MESH_LOD_FINE_BD = 2,
+ XR_SPATIAL_MESH_LOD_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSpatialMeshLodBD;
+typedef XrFlags64 XrSpatialMeshConfigFlagsBD;
+
+// Flag bits for XrSpatialMeshConfigFlagsBD
+static const XrSpatialMeshConfigFlagsBD XR_SPATIAL_MESH_CONFIG_SEMANTIC_BIT_BD = 0x00000001;
+static const XrSpatialMeshConfigFlagsBD XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WITH_VERTEX_BIT_BD = 0x00000002;
+
+// XrSystemSpatialMeshPropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialMeshPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialMesh;
+} XrSystemSpatialMeshPropertiesBD;
+
+// XrSenseDataProviderCreateInfoSpatialMeshBD extends XrSenseDataProviderCreateInfoBD
+typedef struct XrSenseDataProviderCreateInfoSpatialMeshBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialMeshConfigFlagsBD configFlags;
+ XrSpatialMeshLodBD lod;
+} XrSenseDataProviderCreateInfoSpatialMeshBD;
+
+
+
+// XR_BD_future_progress is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_future_progress 1
+#define XR_BD_future_progress_SPEC_VERSION 1
+#define XR_BD_FUTURE_PROGRESS_EXTENSION_NAME "XR_BD_future_progress"
+// XrFuturePollResultProgressBD extends XrFuturePollResultEXT
+typedef struct XrFuturePollResultProgressBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isSupported;
+ uint32_t progressPercentage;
+} XrFuturePollResultProgressBD;
+
+
+
+// XR_BD_body_tracking_auxiliary_metrics is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_body_tracking_auxiliary_metrics 1
+#define XR_BD_body_tracking_auxiliary_metrics_SPEC_VERSION 1
+#define XR_BD_BODY_TRACKING_AUXILIARY_METRICS_EXTENSION_NAME "XR_BD_body_tracking_auxiliary_metrics"
+
+typedef enum XrBodyTrackingPostureBD {
+ XR_BODY_TRACKING_POSTURE_STOMP_BD = 1,
+ XR_BODY_TRACKING_POSTURE_STATIC_BD = 2,
+ XR_BODY_TRACKING_POSTURE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrBodyTrackingPostureBD;
+
+typedef enum XrBodyTrackingStatusBD {
+ XR_BODY_TRACKING_STATUS_INVALID_BD = 0,
+ XR_BODY_TRACKING_STATUS_VALID_BD = 1,
+ XR_BODY_TRACKING_STATUS_LIMITED_BD = 2,
+ XR_BODY_TRACKING_STATUS_MAX_ENUM_BD = 0x7FFFFFFF
+} XrBodyTrackingStatusBD;
+
+typedef enum XrBodyTrackingMessageBD {
+ XR_BODY_TRACKING_MESSAGE_NO_ERROR_BD = 0,
+ XR_BODY_TRACKING_MESSAGE_TRACKER_NOT_CALIBRATED_BD = 1,
+ XR_BODY_TRACKING_MESSAGE_TRACKER_NUM_NOT_ENOUGH_BD = 2,
+ XR_BODY_TRACKING_MESSAGE_TRACKER_STATE_NOT_SATISFIED_BD = 3,
+ XR_BODY_TRACKING_MESSAGE_TRACKER_PERSISTENT_INVISIBILITY_BD = 4,
+ XR_BODY_TRACKING_MESSAGE_TRACKER_DATA_ERROR_BD = 5,
+ XR_BODY_TRACKING_MESSAGE_USER_CHANGE_BD = 6,
+ XR_BODY_TRACKING_MESSAGE_TRACKING_POSE_ERROR_BD = 7,
+ XR_BODY_TRACKING_MESSAGE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrBodyTrackingMessageBD;
+typedef XrFlags64 XrSpaceAccelerationFlagsBD;
+
+// Flag bits for XrSpaceAccelerationFlagsBD
+static const XrSpaceAccelerationFlagsBD XR_SPACE_ACCELERATION_LINEAR_VALID_BIT_BD = 0x00000001;
+static const XrSpaceAccelerationFlagsBD XR_SPACE_ACCELERATION_ANGULAR_VALID_BIT_BD = 0x00000002;
+
+// XrBodyTrackingPostureDataBD extends XrBodyJointLocationsBD
+typedef struct XrBodyTrackingPostureDataBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t postureCount;
+ XrBodyTrackingPostureBD* postureData;
+} XrBodyTrackingPostureDataBD;
+
+typedef struct XrBodyJointVelocityBD {
+ XrSpaceVelocityFlags velocityFlags;
+ XrVector3f linearVelocity;
+ XrVector3f angularVelocity;
+} XrBodyJointVelocityBD;
+
+// XrBodyJointVelocitiesBD extends XrBodyJointLocationsBD
+typedef struct XrBodyJointVelocitiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t velocityCount;
+ XrBodyJointVelocityBD* velocities;
+} XrBodyJointVelocitiesBD;
+
+typedef struct XrBodyJointAccelerationBD {
+ XrSpaceAccelerationFlagsBD accelerationFlags;
+ XrVector3f linearAcceleration;
+ XrVector3f angularAcceleration;
+} XrBodyJointAccelerationBD;
+
+// XrBodyJointAccelerationsBD extends XrBodyJointLocationsBD
+typedef struct XrBodyJointAccelerationsBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t accelerationCount;
+ XrBodyJointAccelerationBD* accelerations;
+} XrBodyJointAccelerationsBD;
+
+typedef struct XrBodyTrackingStateBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBodyTrackingStatusBD status;
+ XrBodyTrackingMessageBD message;
+} XrBodyTrackingStateBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrStartBodyTrackingCalibrationAppBD)(XrSession session);
+typedef XrResult (XRAPI_PTR *PFN_xrGetBodyTrackingStateBD)(XrSession session, XrBodyTrackingStateBD* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrStartBodyTrackingCalibrationAppBD(
+ XrSession session);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetBodyTrackingStateBD(
+ XrSession session,
+ XrBodyTrackingStateBD* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_spatial_plane is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_plane 1
+#define XR_BD_spatial_plane_SPEC_VERSION 1
+#define XR_BD_SPATIAL_PLANE_EXTENSION_NAME "XR_BD_spatial_plane"
+
+typedef enum XrPlaneOrientationBD {
+ XR_PLANE_ORIENTATION_HORIZONTAL_UPWARD_BD = 0,
+ XR_PLANE_ORIENTATION_HORIZONTAL_DOWNWARD_BD = 1,
+ XR_PLANE_ORIENTATION_VERTICAL_BD = 2,
+ XR_PLANE_ORIENTATION_ARBITRARY_BD = 3,
+ XR_PLANE_ORIENTATION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrPlaneOrientationBD;
+// XrSystemSpatialPlanePropertiesBD extends XrSystemProperties
+typedef struct XrSystemSpatialPlanePropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialPlane;
+} XrSystemSpatialPlanePropertiesBD;
+
+// XrSpatialEntityComponentDataPlaneOrientationBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataPlaneOrientationBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPlaneOrientationBD orientation;
+} XrSpatialEntityComponentDataPlaneOrientationBD;
+
+// XrSenseDataFilterPlaneOrientationBD extends XrSenseDataQueryInfoBD
+typedef struct XrSenseDataFilterPlaneOrientationBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t orientationCount;
+ XrPlaneOrientationBD* orientations;
+} XrSenseDataFilterPlaneOrientationBD;
+
+
+
+// XR_BD_spatial_light_estimation is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_light_estimation 1
+#define XR_BD_spatial_light_estimation_SPEC_VERSION 1
+#define XR_BD_SPATIAL_LIGHT_ESTIMATION_EXTENSION_NAME "XR_BD_spatial_light_estimation"
+
+typedef enum XrEnvironmentTexturePixelFormatBD {
+ XR_ENVIRONMENT_TEXTURE_PIXEL_FORMAT_RGB_16FLOAT_BD = 0,
+ XR_ENVIRONMENT_TEXTURE_PIXEL_FORMAT_RGBA_16FLOAT_BD = 1,
+ XR_ENVIRONMENT_TEXTURE_PIXEL_FORMAT_MAX_ENUM_BD = 0x7FFFFFFF
+} XrEnvironmentTexturePixelFormatBD;
+
+typedef enum XrEnvironmentTextureResolutionBD {
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_8_8_BD = 0,
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_16_16_BD = 1,
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_32_32_BD = 2,
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_64_64_BD = 3,
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_128_128_BD = 4,
+ XR_ENVIRONMENT_TEXTURE_RESOLUTION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrEnvironmentTextureResolutionBD;
+
+typedef enum XrEnvironmentTextureTransferTypeBD {
+ XR_ENVIRONMENT_TEXTURE_TRANSFER_TYPE_RAW_BD = 0,
+ XR_ENVIRONMENT_TEXTURE_TRANSFER_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrEnvironmentTextureTransferTypeBD;
+
+typedef enum XrSphericalHarmonicsKindBD {
+ XR_SPHERICAL_HARMONICS_KIND_TOTAL_BD = 0,
+ XR_SPHERICAL_HARMONICS_KIND_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSphericalHarmonicsKindBD;
+typedef XrFlags64 XrLightEstimationCreateFlagsBD;
+
+// Flag bits for XrLightEstimationCreateFlagsBD
+static const XrLightEstimationCreateFlagsBD XR_LIGHT_ESTIMATION_CREATE_SPHERICAL_HARMONICS_BIT_BD = 0x00000001;
+static const XrLightEstimationCreateFlagsBD XR_LIGHT_ESTIMATION_CREATE_ENVIRONMENT_TEXTURE_BIT_BD = 0x00000002;
+
+// XrSystemLightEstimationPropertiesBD extends XrSystemProperties
+typedef struct XrSystemLightEstimationPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsLightEstimation;
+ XrBool32 supportsEnvironmentTexture;
+ XrBool32 supportsSphericalHarmonics;
+} XrSystemLightEstimationPropertiesBD;
+
+// XrSenseDataProviderCreateInfoLightEstimationBD extends XrSenseDataProviderCreateInfoBD
+typedef struct XrSenseDataProviderCreateInfoLightEstimationBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrLightEstimationCreateFlagsBD createFlags;
+} XrSenseDataProviderCreateInfoLightEstimationBD;
+
+// XrEnvironmentTextureCreateConfigInfoBD extends XrSenseDataProviderCreateInfoBD
+typedef struct XrEnvironmentTextureCreateConfigInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentTexturePixelFormatBD pixelFormat;
+ XrEnvironmentTextureResolutionBD resolution;
+ XrEnvironmentTextureTransferTypeBD transferType;
+} XrEnvironmentTextureCreateConfigInfoBD;
+
+// XrLightEstimationDataEnvironmentTextureRawBD extends XrSpatialEntityComponentDataLightEstimationBD
+typedef struct XrLightEstimationDataEnvironmentTextureRawBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentTexturePixelFormatBD pixelFormat;
+ uint32_t cubemapFaceBufferSize;
+ uint8_t* rightCubemapFaceBuffer;
+ uint8_t* leftCubemapFaceBuffer;
+ uint8_t* topCubemapFaceBuffer;
+ uint8_t* bottomCubemapFaceBuffer;
+ uint8_t* frontCubemapFaceBuffer;
+ uint8_t* backCubemapFaceBuffer;
+} XrLightEstimationDataEnvironmentTextureRawBD;
+
+// XrLightEstimationDataSphericalHarmonicsBD extends XrSpatialEntityComponentDataLightEstimationBD
+typedef struct XrLightEstimationDataSphericalHarmonicsBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSphericalHarmonicsKindBD kind;
+ uint32_t coefficientCapacityInput;
+ uint32_t coefficientCountOutput;
+ float* coefficients;
+} XrLightEstimationDataSphericalHarmonicsBD;
+
+// XrSpatialEntityComponentDataLightEstimationBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataLightEstimationBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isValid;
+} XrSpatialEntityComponentDataLightEstimationBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentTextureResolutionsBD)(XrSession session, uint32_t resolutionCapacityInput, uint32_t* resolutionCountOutput, XrEnvironmentTextureResolutionBD* resolutions);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentTexturePixelFormatsBD)(XrSession session, uint32_t pixelFormatCapacityInput, uint32_t* pixelFormatCountOutput, XrEnvironmentTexturePixelFormatBD* pixelFormats);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentTextureTransferTypesBD)(XrSession session, uint32_t transferTypeCapacityInput, uint32_t* transferTypeCountOutput, XrEnvironmentTextureTransferTypeBD* transferTypes);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentTextureResolutionsBD(
+ XrSession session,
+ uint32_t resolutionCapacityInput,
+ uint32_t* resolutionCountOutput,
+ XrEnvironmentTextureResolutionBD* resolutions);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentTexturePixelFormatsBD(
+ XrSession session,
+ uint32_t pixelFormatCapacityInput,
+ uint32_t* pixelFormatCountOutput,
+ XrEnvironmentTexturePixelFormatBD* pixelFormats);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentTextureTransferTypesBD(
+ XrSession session,
+ uint32_t transferTypeCapacityInput,
+ uint32_t* transferTypeCountOutput,
+ XrEnvironmentTextureTransferTypeBD* transferTypes);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_BD_ultra_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_ultra_controller_interaction 1
+#define XR_BD_ultra_controller_interaction_SPEC_VERSION 1
+#define XR_BD_ULTRA_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_ultra_controller_interaction"
+
+
+// XR_BD_spatial_audio_rendering is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_spatial_audio_rendering 1
+XR_DEFINE_HANDLE(XrSpatialAudioRendererBD)
+XR_DEFINE_HANDLE(XrSoundFieldBD)
+XR_DEFINE_HANDLE(XrSoundObjectBD)
+XR_DEFINE_HANDLE(XrSoundObstacleBD)
+XR_DEFINE_HANDLE(XrSoundObstacleMaterialBD)
+#define XR_BD_spatial_audio_rendering_SPEC_VERSION 1
+#define XR_BD_SPATIAL_AUDIO_RENDERING_EXTENSION_NAME "XR_BD_spatial_audio_rendering"
+
+typedef enum XrAudioSampleRateBD {
+ XR_AUDIO_SAMPLE_RATE_192000_HZ_BD = 1,
+ XR_AUDIO_SAMPLE_RATE_96000_HZ_BD = 2,
+ XR_AUDIO_SAMPLE_RATE_48000_HZ_BD = 3,
+ XR_AUDIO_SAMPLE_RATE_44100_HZ_BD = 4,
+ XR_AUDIO_SAMPLE_RATE_32000_HZ_BD = 5,
+ XR_AUDIO_SAMPLE_RATE_24000_HZ_BD = 6,
+ XR_AUDIO_SAMPLE_RATE_22050_HZ_BD = 7,
+ XR_AUDIO_SAMPLE_RATE_16000_HZ_BD = 8,
+ XR_AUDIO_SAMPLE_RATE_12000_HZ_BD = 9,
+ XR_AUDIO_SAMPLE_RATE_11025_HZ_BD = 10,
+ XR_AUDIO_SAMPLE_RATE_8000_HZ_BD = 11,
+ XR_AUDIO_SAMPLE_RATE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrAudioSampleRateBD;
+
+typedef enum XrAudioBufferChannelLayoutBD {
+ XR_AUDIO_BUFFER_CHANNEL_LAYOUT_INTERLEAVED_BD = 0,
+ XR_AUDIO_BUFFER_CHANNEL_LAYOUT_PLANAR_BD = 1,
+ XR_AUDIO_BUFFER_CHANNEL_LAYOUT_MAX_ENUM_BD = 0x7FFFFFFF
+} XrAudioBufferChannelLayoutBD;
+
+typedef enum XrSoundObjectDistanceAttenuationTypeBD {
+ XR_SOUND_OBJECT_DISTANCE_ATTENUATION_TYPE_NONE_BD = 0,
+ XR_SOUND_OBJECT_DISTANCE_ATTENUATION_TYPE_INVERSE_SQUARE_BD = 1,
+ XR_SOUND_OBJECT_DISTANCE_ATTENUATION_TYPE_ROLLOFF_BD = 2,
+ XR_SOUND_OBJECT_DISTANCE_ATTENUATION_TYPE_CUSTOMIZED_BD = 100,
+ XR_SOUND_OBJECT_DISTANCE_ATTENUATION_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSoundObjectDistanceAttenuationTypeBD;
+
+typedef enum XrSoundFieldChannelMaskSurroundBD {
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_FRONT_LEFT_BD = 1,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_FRONT_RIGHT_BD = 2,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_FRONT_CENTER_BD = 4,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_LOW_FREQUENCY_BD = 8,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_BACK_LEFT_BD = 16,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_BACK_RIGHT_BD = 32,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SIDE_LEFT_BD = 64,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SIDE_RIGHT_BD = 128,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_FRONT_LEFT_BD = 256,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_FRONT_RIGHT_BD = 512,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_BACK_LEFT_BD = 1024,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_BACK_RIGHT_BD = 2048,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_BACK_CENTER_BD = 4096,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_SIDE_LEFT_BD = 8192,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_TOP_SIDE_RIGHT_BD = 16384,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_STEREO_BD = 3,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_2_1_BD = 11,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_3_0_BD = 7,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_4_0_BD = 4099,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_BACK_SURROUND_BD = 48,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_QUAD_BD = 51,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_3_1_BD = 15,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_0_BD = 55,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_SIDE_SURROUND_BD = 192,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_0_SIDE_BD = 199,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_1_BD = 63,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_1_SIDE_BD = 207,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_7_0_BD = 247,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_7_1_BD = 255,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_1_2_BD = 831,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_5_1_4_BD = 3903,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_7_1_2_BD = 24831,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_SETUP_7_1_4_BD = 4095,
+ XR_SOUND_FIELD_CHANNEL_MASK_SURROUND_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSoundFieldChannelMaskSurroundBD;
+
+typedef enum XrSoundFieldChannelMaskAmbixBD {
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_1ST_ORDER_BD = 1,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_2ND_ORDER_BD = 2,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_3RD_ORDER_BD = 3,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_4TH_ORDER_BD = 4,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_5TH_ORDER_BD = 5,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_6TH_ORDER_BD = 6,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_7TH_ORDER_BD = 7,
+ XR_SOUND_FIELD_CHANNEL_MASK_AMBIX_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSoundFieldChannelMaskAmbixBD;
+
+typedef enum XrSoundFieldChannelMaskFumaBD {
+ XR_SOUND_FIELD_CHANNEL_MASK_FUMA_1ST_ORDER_BD = 1,
+ XR_SOUND_FIELD_CHANNEL_MASK_FUMA_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSoundFieldChannelMaskFumaBD;
+
+typedef enum XrSoundObstacleMaterialTypeBD {
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_ACOUSTIC_TILE_BD = 0,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_BRICK_BD = 1,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_BRICK_PAINTED_BD = 2,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CARPET_BD = 3,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CARPET_HEAVY_BD = 4,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CARPET_HEAVY_PADDED_BD = 5,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CERAMIC_TILE_BD = 6,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CONCRETE_BD = 7,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CONCRETE_ROUGH_BD = 8,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CONCRETE_BLOCK_BD = 9,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CONCRETE_BLOCK_PAINTED_BD = 10,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CURTAIN_BD = 11,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_FOLIAGE_BD = 12,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_GLASS_BD = 13,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_GLASS_HEAVY_BD = 14,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_GRASS_BD = 15,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_GRAVEL_BD = 16,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_GYPSUM_BOARD_BD = 17,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_PLASTER_ON_BRICK_BD = 18,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_PLASTER_ON_CONCRETE_BLOCK_BD = 19,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_SOIL_BD = 20,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_SOUND_PROOF_BD = 21,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_SNOW_BD = 22,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_STEEL_BD = 23,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_WATER_BD = 24,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_WOOD_THIN_BD = 25,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_WOOD_THICK_BD = 26,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_WOOD_FLOOR_BD = 27,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_WOOD_ON_CONCRETE_BD = 28,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_CUSTOM_BD = 29,
+ XR_SOUND_OBSTACLE_MATERIAL_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrSoundObstacleMaterialTypeBD;
+typedef XrFlags64 XrSoundObstacleFlagsBD;
+
+// Flag bits for XrSoundObstacleFlagsBD
+static const XrSoundObstacleFlagsBD XR_SOUND_OBSTACLE_ENABLED_BIT_BD = 0x00000001;
+static const XrSoundObstacleFlagsBD XR_SOUND_OBSTACLE_POSE_BIT_BD = 0x00000002;
+static const XrSoundObstacleFlagsBD XR_SOUND_OBSTACLE_MESH_BIT_BD = 0x00000004;
+static const XrSoundObstacleFlagsBD XR_SOUND_OBSTACLE_MATERIALS_BIT_BD = 0x00000008;
+
+typedef XrFlags64 XrSoundObjectFlagsBD;
+
+// Flag bits for XrSoundObjectFlagsBD
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_ENABLED_BIT_BD = 0x00000001;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_POSE_BIT_BD = 0x00000002;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_DIRECTIVITY_BIT_BD = 0x00000004;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_SHAPE_BIT_BD = 0x00000008;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_MAIN_VOLUME_BIT_BD = 0x00000010;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_REFLECTION_GAIN_BIT_BD = 0x00000020;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_ENABLE_DOPPLER_BIT_BD = 0x00000040;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_DIRECT_SOUND_ATTENUATION_BIT_BD = 0x00000080;
+static const XrSoundObjectFlagsBD XR_SOUND_OBJECT_INDIRECT_SOUND_ATTENUATION_BIT_BD = 0x00000100;
+
+typedef XrFlags64 XrSoundFieldFlagsBD;
+
+// Flag bits for XrSoundFieldFlagsBD
+static const XrSoundFieldFlagsBD XR_SOUND_FIELD_ENABLED_BIT_BD = 0x00000001;
+static const XrSoundFieldFlagsBD XR_SOUND_FIELD_ORIENTATION_BIT_BD = 0x00000002;
+static const XrSoundFieldFlagsBD XR_SOUND_FIELD_MAIN_VOLUME_BIT_BD = 0x00000004;
+static const XrSoundFieldFlagsBD XR_SOUND_FIELD_LFE_GAIN_BIT_BD = 0x00000008;
+
+typedef struct XrSpatialAudioRendererCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t framesPerBuffer;
+ XrAudioSampleRateBD sampleRate;
+} XrSpatialAudioRendererCreateInfoBD;
+
+typedef struct XrAudioBufferBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAudioBufferChannelLayoutBD channelLayout;
+ uint32_t bufferChannels;
+ uint32_t bufferLength;
+ float* buffer;
+} XrAudioBufferBD;
+
+// XrSoundObjectDirectivityCardioidBD extends XrSoundObjectConfigBD
+typedef struct XrSoundObjectDirectivityCardioidBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float alpha;
+ float order;
+} XrSoundObjectDirectivityCardioidBD;
+
+// XrSoundObjectShapeSphereBD extends XrSoundObjectConfigBD
+typedef struct XrSoundObjectShapeSphereBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float radius;
+} XrSoundObjectShapeSphereBD;
+
+typedef struct XrAttenuationCurvePointBD {
+ float distance;
+ float gain;
+} XrAttenuationCurvePointBD;
+
+typedef struct XrSoundObjectDistanceAttenuationCurveBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t curvePointCount;
+ XrAttenuationCurvePointBD* curvePoints;
+} XrSoundObjectDistanceAttenuationCurveBD;
+
+typedef struct XrSoundObjectDistanceAttenuationBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSoundObjectDistanceAttenuationTypeBD distanceAttenuationType;
+ float minAttenuationRange;
+ float maxAttenuationRange;
+ float referenceDistance;
+ float rolloffFactor;
+ XrSoundObjectDistanceAttenuationCurveBD* customDistanceAttenuationCurve;
+} XrSoundObjectDistanceAttenuationBD;
+
+typedef struct XrSoundObjectConfigBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+ XrPosef pose;
+ XrSpace baseSpace;
+ float mainVolume;
+ float reflectionGain;
+ XrBool32 enableDoppler;
+ const XrSoundObjectDistanceAttenuationBD* directSoundAttenuation;
+ const XrSoundObjectDistanceAttenuationBD* indirectSoundAttenuation;
+} XrSoundObjectConfigBD;
+
+typedef struct XrSoundFieldConfigBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+ XrQuaternionf orientation;
+ XrSpace baseSpace;
+ float mainVolume;
+ float lfeGain;
+} XrSoundFieldConfigBD;
+
+// XrSoundFieldChannelDefinitionSurroundBD extends XrSoundFieldConfigBD
+typedef struct XrSoundFieldChannelDefinitionSurroundBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSoundFieldChannelMaskSurroundBD channelMask;
+} XrSoundFieldChannelDefinitionSurroundBD;
+
+// XrSoundFieldChannelDefinitionAmbixBD extends XrSoundFieldConfigBD
+typedef struct XrSoundFieldChannelDefinitionAmbixBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSoundFieldChannelMaskAmbixBD channelMask;
+} XrSoundFieldChannelDefinitionAmbixBD;
+
+// XrSoundFieldChannelDefinitionFumaBD extends XrSoundFieldConfigBD
+typedef struct XrSoundFieldChannelDefinitionFumaBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSoundFieldChannelMaskFumaBD channelMask;
+} XrSoundFieldChannelDefinitionFumaBD;
+
+typedef struct XrSoundTriangleMeshBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t vertexCount;
+ XrVector3f* vertices;
+ uint32_t indexCount;
+ uint32_t* indices;
+} XrSoundTriangleMeshBD;
+
+typedef struct XrSoundObstacleConfigBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+ XrPosef pose;
+ XrSpace baseSpace;
+ uint32_t materialCount;
+ XrSoundObstacleMaterialBD* materials;
+} XrSoundObstacleConfigBD;
+
+typedef struct XrSoundObstacleMaterialConfigBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSoundObstacleMaterialTypeBD materialType;
+ uint32_t bandCount;
+ float* bandFrequencies;
+ float* bandAbsorptions;
+ float* bandScatterings;
+ float* bandTransmissions;
+} XrSoundObstacleMaterialConfigBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedAudioSampleRateBD)(XrSession session, uint32_t sampleRateCapacityInput, uint32_t* sampleRateCountOutput, XrAudioSampleRateBD* sampleRates);
+typedef XrResult (XRAPI_PTR *PFN_xrQueryFramesPerBufferRangeBD)(XrSession session, XrAudioSampleRateBD sampleRate, uint32_t* min, uint32_t* max);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAudioRendererBD)(XrSession session, const XrSpatialAudioRendererCreateInfoBD* createInfo, XrSpatialAudioRendererBD* renderer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAudioRendererBD)(XrSpatialAudioRendererBD renderer);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSoundObstacleMaterialBD)(XrSpatialAudioRendererBD renderer, const XrSoundObstacleMaterialConfigBD* config, XrSoundObstacleMaterialBD* material);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSoundObstacleMaterialConfigBD)(XrSoundObstacleMaterialBD material, const XrSoundObstacleMaterialConfigBD* config);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySoundObstacleMaterialBD)(XrSoundObstacleMaterialBD material);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSoundObstacleBD)(XrSpatialAudioRendererBD renderer, const XrSoundObstacleConfigBD* config, const XrSoundTriangleMeshBD* mesh, XrSoundObstacleBD* soundObstacle);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSoundObstacleConfigBD)(XrSoundObstacleBD soundObstacle, const XrSoundObstacleConfigBD* config, const XrSoundTriangleMeshBD* mesh, XrSoundObstacleFlagsBD flags);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySoundObstacleBD)(XrSoundObstacleBD soundObstacle);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSoundObjectBD)(XrSpatialAudioRendererBD renderer, const XrSoundObjectConfigBD* config, XrSoundObjectBD* soundObject);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSoundObjectConfigBD)(XrSoundObjectBD soundObject, const XrSoundObjectConfigBD* config, XrSoundObjectFlagsBD flags);
+typedef XrResult (XRAPI_PTR *PFN_xrSubmitSoundObjectBufferBD)(XrSoundObjectBD soundObject, const XrAudioBufferBD* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySoundObjectBD)(XrSoundObjectBD soundObject);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSoundFieldBD)(XrSpatialAudioRendererBD renderer, const XrSoundFieldConfigBD* config, XrSoundFieldBD* soundField);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSoundFieldConfigBD)(XrSoundFieldBD soundField, const XrSoundFieldConfigBD* config, XrSoundFieldFlagsBD flags);
+typedef XrResult (XRAPI_PTR *PFN_xrSubmitSoundFieldBufferBD)(XrSoundFieldBD soundField, const XrAudioBufferBD* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySoundFieldBD)(XrSoundFieldBD soundField);
+typedef XrResult (XRAPI_PTR *PFN_xrWaitAudioPeriodBD)(XrSpatialAudioRendererBD renderer, XrDuration timeout);
+typedef XrResult (XRAPI_PTR *PFN_xrEndAudioPeriodBD)(XrSpatialAudioRendererBD renderer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedAudioSampleRateBD(
+ XrSession session,
+ uint32_t sampleRateCapacityInput,
+ uint32_t* sampleRateCountOutput,
+ XrAudioSampleRateBD* sampleRates);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQueryFramesPerBufferRangeBD(
+ XrSession session,
+ XrAudioSampleRateBD sampleRate,
+ uint32_t* min,
+ uint32_t* max);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAudioRendererBD(
+ XrSession session,
+ const XrSpatialAudioRendererCreateInfoBD* createInfo,
+ XrSpatialAudioRendererBD* renderer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAudioRendererBD(
+ XrSpatialAudioRendererBD renderer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSoundObstacleMaterialBD(
+ XrSpatialAudioRendererBD renderer,
+ const XrSoundObstacleMaterialConfigBD* config,
+ XrSoundObstacleMaterialBD* material);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSoundObstacleMaterialConfigBD(
+ XrSoundObstacleMaterialBD material,
+ const XrSoundObstacleMaterialConfigBD* config);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySoundObstacleMaterialBD(
+ XrSoundObstacleMaterialBD material);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSoundObstacleBD(
+ XrSpatialAudioRendererBD renderer,
+ const XrSoundObstacleConfigBD* config,
+ const XrSoundTriangleMeshBD* mesh,
+ XrSoundObstacleBD* soundObstacle);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSoundObstacleConfigBD(
+ XrSoundObstacleBD soundObstacle,
+ const XrSoundObstacleConfigBD* config,
+ const XrSoundTriangleMeshBD* mesh,
+ XrSoundObstacleFlagsBD flags);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySoundObstacleBD(
+ XrSoundObstacleBD soundObstacle);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSoundObjectBD(
+ XrSpatialAudioRendererBD renderer,
+ const XrSoundObjectConfigBD* config,
+ XrSoundObjectBD* soundObject);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSoundObjectConfigBD(
+ XrSoundObjectBD soundObject,
+ const XrSoundObjectConfigBD* config,
+ XrSoundObjectFlagsBD flags);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSubmitSoundObjectBufferBD(
+ XrSoundObjectBD soundObject,
+ const XrAudioBufferBD* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySoundObjectBD(
+ XrSoundObjectBD soundObject);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSoundFieldBD(
+ XrSpatialAudioRendererBD renderer,
+ const XrSoundFieldConfigBD* config,
+ XrSoundFieldBD* soundField);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSoundFieldConfigBD(
+ XrSoundFieldBD soundField,
+ const XrSoundFieldConfigBD* config,
+ XrSoundFieldFlagsBD flags);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSubmitSoundFieldBufferBD(
+ XrSoundFieldBD soundField,
+ const XrAudioBufferBD* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySoundFieldBD(
+ XrSoundFieldBD soundField);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrWaitAudioPeriodBD(
+ XrSpatialAudioRendererBD renderer,
+ XrDuration timeout);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEndAudioPeriodBD(
+ XrSpatialAudioRendererBD renderer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_local_floor is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_local_floor 1
+#define XR_EXT_local_floor_SPEC_VERSION 1
+#define XR_EXT_LOCAL_FLOOR_EXTENSION_NAME "XR_EXT_local_floor"
+
+
+// XR_EXT_hand_tracking_data_source is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_hand_tracking_data_source 1
+#define XR_EXT_hand_tracking_data_source_SPEC_VERSION 1
+#define XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME "XR_EXT_hand_tracking_data_source"
+
+typedef enum XrHandTrackingDataSourceEXT {
+ XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT = 1,
+ XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT = 2,
+ XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_WIDE_MOTION_META = 1000695000,
+ XR_HAND_TRACKING_DATA_SOURCE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandTrackingDataSourceEXT;
+// XrHandTrackingDataSourceInfoEXT extends XrHandTrackerCreateInfoEXT
+typedef struct XrHandTrackingDataSourceInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestedDataSourceCount;
+ XrHandTrackingDataSourceEXT* requestedDataSources;
+} XrHandTrackingDataSourceInfoEXT;
+
+// XrHandTrackingDataSourceStateEXT extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingDataSourceStateEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ XrHandTrackingDataSourceEXT dataSource;
+} XrHandTrackingDataSourceStateEXT;
+
+
+
+// XR_EXT_plane_detection is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_plane_detection 1
+XR_DEFINE_HANDLE(XrPlaneDetectorEXT)
+#define XR_EXT_plane_detection_SPEC_VERSION 2
+#define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection"
+
+typedef enum XrPlaneDetectorOrientationEXT {
+ XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT = 0,
+ XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT = 1,
+ XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT = 2,
+ XR_PLANE_DETECTOR_ORIENTATION_ARBITRARY_EXT = 3,
+ XR_PLANE_DETECTOR_ORIENTATION_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectorOrientationEXT;
+
+typedef enum XrPlaneDetectorSemanticTypeEXT {
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_UNDEFINED_EXT = 0,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_CEILING_EXT = 1,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_FLOOR_EXT = 2,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_WALL_EXT = 3,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_PLATFORM_EXT = 4,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectorSemanticTypeEXT;
+
+typedef enum XrPlaneDetectionStateEXT {
+ XR_PLANE_DETECTION_STATE_NONE_EXT = 0,
+ XR_PLANE_DETECTION_STATE_PENDING_EXT = 1,
+ XR_PLANE_DETECTION_STATE_DONE_EXT = 2,
+ XR_PLANE_DETECTION_STATE_ERROR_EXT = 3,
+ XR_PLANE_DETECTION_STATE_FATAL_EXT = 4,
+ XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectionStateEXT;
+typedef XrFlags64 XrPlaneDetectionCapabilityFlagsEXT;
+
+// Flag bits for XrPlaneDetectionCapabilityFlagsEXT
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_PLANE_DETECTION_BIT_EXT = 0x00000001;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_PLANE_HOLES_BIT_EXT = 0x00000002;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_CEILING_BIT_EXT = 0x00000004;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_FLOOR_BIT_EXT = 0x00000008;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_WALL_BIT_EXT = 0x00000010;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_PLATFORM_BIT_EXT = 0x00000020;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_ORIENTATION_BIT_EXT = 0x00000040;
+
+typedef XrFlags64 XrPlaneDetectorFlagsEXT;
+
+// Flag bits for XrPlaneDetectorFlagsEXT
+static const XrPlaneDetectorFlagsEXT XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT = 0x00000001;
+
+// XrSystemPlaneDetectionPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemPlaneDetectionPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPlaneDetectionCapabilityFlagsEXT supportedFeatures;
+} XrSystemPlaneDetectionPropertiesEXT;
+
+typedef struct XrPlaneDetectorCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPlaneDetectorFlagsEXT flags;
+} XrPlaneDetectorCreateInfoEXT;
+
+typedef XrExtent3Df XrExtent3DfEXT;
+
+typedef struct XrPlaneDetectorBeginInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ uint32_t orientationCount;
+ const XrPlaneDetectorOrientationEXT* orientations;
+ uint32_t semanticTypeCount;
+ const XrPlaneDetectorSemanticTypeEXT* semanticTypes;
+ uint32_t maxPlanes;
+ float minArea;
+ XrPosef boundingBoxPose;
+ XrExtent3DfEXT boundingBoxExtent;
+} XrPlaneDetectorBeginInfoEXT;
+
+typedef struct XrPlaneDetectorGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrPlaneDetectorGetInfoEXT;
+
+typedef struct XrPlaneDetectorLocationEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint64_t planeId;
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+ XrExtent2Df extents;
+ XrPlaneDetectorOrientationEXT orientation;
+ XrPlaneDetectorSemanticTypeEXT semanticType;
+ uint32_t polygonBufferCount;
+} XrPlaneDetectorLocationEXT;
+
+typedef struct XrPlaneDetectorLocationsEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t planeLocationCapacityInput;
+ uint32_t planeLocationCountOutput;
+ XrPlaneDetectorLocationEXT* planeLocations;
+} XrPlaneDetectorLocationsEXT;
+
+typedef struct XrPlaneDetectorPolygonBufferEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector2f* vertices;
+} XrPlaneDetectorPolygonBufferEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePlaneDetectorEXT)(XrSession session, const XrPlaneDetectorCreateInfoEXT* createInfo, XrPlaneDetectorEXT* planeDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPlaneDetectorEXT)(XrPlaneDetectorEXT planeDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrBeginPlaneDetectionEXT)(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorBeginInfoEXT* beginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlaneDetectionStateEXT)(XrPlaneDetectorEXT planeDetector, XrPlaneDetectionStateEXT* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlaneDetectionsEXT)(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorGetInfoEXT* info, XrPlaneDetectorLocationsEXT* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlanePolygonBufferEXT)(XrPlaneDetectorEXT planeDetector, uint64_t planeId, uint32_t polygonBufferIndex, XrPlaneDetectorPolygonBufferEXT* polygonBuffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePlaneDetectorEXT(
+ XrSession session,
+ const XrPlaneDetectorCreateInfoEXT* createInfo,
+ XrPlaneDetectorEXT* planeDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPlaneDetectorEXT(
+ XrPlaneDetectorEXT planeDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginPlaneDetectionEXT(
+ XrPlaneDetectorEXT planeDetector,
+ const XrPlaneDetectorBeginInfoEXT* beginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlaneDetectionStateEXT(
+ XrPlaneDetectorEXT planeDetector,
+ XrPlaneDetectionStateEXT* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlaneDetectionsEXT(
+ XrPlaneDetectorEXT planeDetector,
+ const XrPlaneDetectorGetInfoEXT* info,
+ XrPlaneDetectorLocationsEXT* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT(
+ XrPlaneDetectorEXT planeDetector,
+ uint64_t planeId,
+ uint32_t polygonBufferIndex,
+ XrPlaneDetectorPolygonBufferEXT* polygonBuffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_OPPO_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_OPPO_controller_interaction 1
+#define XR_OPPO_controller_interaction_SPEC_VERSION 1
+#define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction"
+
+
+// XR_ANDROID_trackables is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_trackables 1
+
+#define XR_NULL_TRACKABLE_ANDROID 0
+
+XR_DEFINE_ATOM(XrTrackableANDROID)
+XR_DEFINE_HANDLE(XrTrackableTrackerANDROID)
+#define XR_ANDROID_trackables_SPEC_VERSION 2
+#define XR_ANDROID_TRACKABLES_EXTENSION_NAME "XR_ANDROID_trackables"
+
+typedef enum XrTrackingStateANDROID {
+ XR_TRACKING_STATE_PAUSED_ANDROID = 0,
+ XR_TRACKING_STATE_STOPPED_ANDROID = 1,
+ XR_TRACKING_STATE_TRACKING_ANDROID = 2,
+ XR_TRACKING_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackingStateANDROID;
+
+typedef enum XrTrackableTypeANDROID {
+ XR_TRACKABLE_TYPE_NOT_VALID_ANDROID = 0,
+ XR_TRACKABLE_TYPE_PLANE_ANDROID = 1,
+ XR_TRACKABLE_TYPE_DEPTH_ANDROID = 1000463000,
+ XR_TRACKABLE_TYPE_OBJECT_ANDROID = 1000466000,
+ XR_TRACKABLE_TYPE_MARKER_ANDROID = 1000707000,
+ XR_TRACKABLE_TYPE_QR_CODE_ANDROID = 1000708000,
+ XR_TRACKABLE_TYPE_IMAGE_ANDROID = 1000709000,
+ XR_TRACKABLE_TYPE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackableTypeANDROID;
+
+typedef enum XrPlaneTypeANDROID {
+ XR_PLANE_TYPE_HORIZONTAL_DOWNWARD_FACING_ANDROID = 0,
+ XR_PLANE_TYPE_HORIZONTAL_UPWARD_FACING_ANDROID = 1,
+ XR_PLANE_TYPE_VERTICAL_ANDROID = 2,
+ XR_PLANE_TYPE_ARBITRARY_ANDROID = 3,
+ XR_PLANE_TYPE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrPlaneTypeANDROID;
+
+typedef enum XrPlaneLabelANDROID {
+ XR_PLANE_LABEL_UNKNOWN_ANDROID = 0,
+ XR_PLANE_LABEL_WALL_ANDROID = 1,
+ XR_PLANE_LABEL_FLOOR_ANDROID = 2,
+ XR_PLANE_LABEL_CEILING_ANDROID = 3,
+ XR_PLANE_LABEL_TABLE_ANDROID = 4,
+ XR_PLANE_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrPlaneLabelANDROID;
+typedef struct XrTrackableTrackerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTrackableTypeANDROID trackableType;
+} XrTrackableTrackerCreateInfoANDROID;
+
+typedef struct XrTrackableGetInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTrackableANDROID trackable;
+ XrSpace baseSpace;
+ XrTime time;
+} XrTrackableGetInfoANDROID;
+
+typedef struct XrTrackablePlaneANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTrackingStateANDROID trackingState;
+ XrPosef centerPose;
+ XrExtent2Df extents;
+ XrPlaneTypeANDROID planeType;
+ XrPlaneLabelANDROID planeLabel;
+ XrTrackableANDROID subsumedByPlane;
+ XrTime lastUpdatedTime;
+ uint32_t vertexCapacityInput;
+ uint32_t* vertexCountOutput;
+ XrVector2f* vertices;
+} XrTrackablePlaneANDROID;
+
+typedef struct XrAnchorSpaceCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrPosef pose;
+ XrTrackableANDROID trackable;
+} XrAnchorSpaceCreateInfoANDROID;
+
+// XrSystemTrackablesPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemTrackablesPropertiesANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsAnchor;
+ uint32_t maxAnchors;
+} XrSystemTrackablesPropertiesANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedAnchorTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateTrackableTrackerANDROID)(XrSession session, const XrTrackableTrackerCreateInfoANDROID* createInfo, XrTrackableTrackerANDROID* trackableTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyTrackableTrackerANDROID)(XrTrackableTrackerANDROID trackableTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetAllTrackablesANDROID)(XrTrackableTrackerANDROID trackableTracker, uint32_t trackableCapacityInput, uint32_t* trackableCountOutput, XrTrackableANDROID* trackables);
+typedef XrResult (XRAPI_PTR *PFN_xrGetTrackablePlaneANDROID)(XrTrackableTrackerANDROID trackableTracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackablePlaneANDROID* planeOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateAnchorSpaceANDROID)(XrSession session, const XrAnchorSpaceCreateInfoANDROID* createInfo, XrSpace* anchorOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedTrackableTypesANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t trackableTypeCapacityInput,
+ uint32_t* trackableTypeCountOutput,
+ XrTrackableTypeANDROID* trackableTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedAnchorTrackableTypesANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t trackableTypeCapacityInput,
+ uint32_t* trackableTypeCountOutput,
+ XrTrackableTypeANDROID* trackableTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateTrackableTrackerANDROID(
+ XrSession session,
+ const XrTrackableTrackerCreateInfoANDROID* createInfo,
+ XrTrackableTrackerANDROID* trackableTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyTrackableTrackerANDROID(
+ XrTrackableTrackerANDROID trackableTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAllTrackablesANDROID(
+ XrTrackableTrackerANDROID trackableTracker,
+ uint32_t trackableCapacityInput,
+ uint32_t* trackableCountOutput,
+ XrTrackableANDROID* trackables);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackablePlaneANDROID(
+ XrTrackableTrackerANDROID trackableTracker,
+ const XrTrackableGetInfoANDROID* getInfo,
+ XrTrackablePlaneANDROID* planeOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateAnchorSpaceANDROID(
+ XrSession session,
+ const XrAnchorSpaceCreateInfoANDROID* createInfo,
+ XrSpace* anchorOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_eye_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_eye_tracking 1
+XR_DEFINE_HANDLE(XrEyeTrackerANDROID)
+#define XR_EYE_MAX_ANDROID 2
+#define XR_ANDROID_eye_tracking_SPEC_VERSION 1
+#define XR_ANDROID_EYE_TRACKING_EXTENSION_NAME "XR_ANDROID_eye_tracking"
+
+typedef enum XrEyeIndexANDROID {
+ XR_EYE_INDEX_LEFT_ANDROID = 0,
+ XR_EYE_INDEX_RIGHT_ANDROID = 1,
+ XR_EYE_INDEX_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrEyeIndexANDROID;
+
+typedef enum XrEyeStateANDROID {
+ XR_EYE_STATE_INVALID_ANDROID = 0,
+ XR_EYE_STATE_GAZING_ANDROID = 1,
+ XR_EYE_STATE_SHUT_ANDROID = 2,
+ XR_EYE_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrEyeStateANDROID;
+
+typedef enum XrEyeTrackingModeANDROID {
+ XR_EYE_TRACKING_MODE_NOT_TRACKING_ANDROID = 0,
+ XR_EYE_TRACKING_MODE_RIGHT_ANDROID = 1,
+ XR_EYE_TRACKING_MODE_LEFT_ANDROID = 2,
+ XR_EYE_TRACKING_MODE_BOTH_ANDROID = 3,
+ XR_EYE_TRACKING_MODE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrEyeTrackingModeANDROID;
+// XrSystemEyeTrackingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemEyeTrackingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEyeTracking;
+} XrSystemEyeTrackingPropertiesANDROID;
+
+typedef struct XrEyeANDROID {
+ XrEyeStateANDROID eyeState;
+ XrPosef eyePose;
+} XrEyeANDROID;
+
+typedef struct XrEyesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrEyeANDROID eyes[XR_EYE_MAX_ANDROID];
+ XrEyeTrackingModeANDROID mode;
+} XrEyesANDROID;
+
+typedef struct XrEyesGetInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+ XrSpace baseSpace;
+} XrEyesGetInfoANDROID;
+
+typedef struct XrEyeTrackerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEyeTrackerCreateInfoANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEyeTrackerANDROID)(XrSession session, const XrEyeTrackerCreateInfoANDROID* createInfo, XrEyeTrackerANDROID* eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEyeTrackerANDROID)(XrEyeTrackerANDROID eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFineTrackingEyesInfoANDROID)(XrEyeTrackerANDROID eyeTracker, const XrEyesGetInfoANDROID* getInfo, XrEyesANDROID* eyesOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetCoarseTrackingEyesInfoANDROID)(XrEyeTrackerANDROID eyeTracker, const XrEyesGetInfoANDROID* getInfo, XrEyesANDROID* eyesOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEyeTrackerANDROID(
+ XrSession session,
+ const XrEyeTrackerCreateInfoANDROID* createInfo,
+ XrEyeTrackerANDROID* eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEyeTrackerANDROID(
+ XrEyeTrackerANDROID eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFineTrackingEyesInfoANDROID(
+ XrEyeTrackerANDROID eyeTracker,
+ const XrEyesGetInfoANDROID* getInfo,
+ XrEyesANDROID* eyesOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetCoarseTrackingEyesInfoANDROID(
+ XrEyeTrackerANDROID eyeTracker,
+ const XrEyesGetInfoANDROID* getInfo,
+ XrEyesANDROID* eyesOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_device_anchor_persistence is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_device_anchor_persistence 1
+XR_DEFINE_HANDLE(XrDeviceAnchorPersistenceANDROID)
+#define XR_ANDROID_device_anchor_persistence_SPEC_VERSION 1
+#define XR_ANDROID_DEVICE_ANCHOR_PERSISTENCE_EXTENSION_NAME "XR_ANDROID_device_anchor_persistence"
+
+typedef enum XrAnchorPersistStateANDROID {
+ XR_ANCHOR_PERSIST_STATE_PERSIST_NOT_REQUESTED_ANDROID = 0,
+ XR_ANCHOR_PERSIST_STATE_PERSIST_PENDING_ANDROID = 1,
+ XR_ANCHOR_PERSIST_STATE_PERSISTED_ANDROID = 2,
+ XR_ANCHOR_PERSIST_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrAnchorPersistStateANDROID;
+typedef struct XrDeviceAnchorPersistenceCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrDeviceAnchorPersistenceCreateInfoANDROID;
+
+typedef struct XrPersistedAnchorSpaceCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuidEXT anchorId;
+} XrPersistedAnchorSpaceCreateInfoANDROID;
+
+typedef struct XrPersistedAnchorSpaceInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace anchor;
+} XrPersistedAnchorSpaceInfoANDROID;
+
+// XrSystemDeviceAnchorPersistencePropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemDeviceAnchorPersistencePropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsAnchorPersistence;
+} XrSystemDeviceAnchorPersistencePropertiesANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedPersistenceAnchorTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateDeviceAnchorPersistenceANDROID)(XrSession session, const XrDeviceAnchorPersistenceCreateInfoANDROID* createInfo, XrDeviceAnchorPersistenceANDROID* outHandle);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyDeviceAnchorPersistenceANDROID)(XrDeviceAnchorPersistenceANDROID handle);
+typedef XrResult (XRAPI_PTR *PFN_xrPersistAnchorANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrPersistedAnchorSpaceInfoANDROID* persistedInfo, XrUuidEXT* anchorIdOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetAnchorPersistStateANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrUuidEXT* anchorId, XrAnchorPersistStateANDROID* persistState);
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePersistedAnchorSpaceANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrPersistedAnchorSpaceCreateInfoANDROID* createInfo, XrSpace* anchorOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePersistedAnchorsANDROID)(XrDeviceAnchorPersistenceANDROID handle, uint32_t anchorIdCapacityInput, uint32_t* anchorIdCountOutput, XrUuidEXT* anchorIds);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistAnchorANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrUuidEXT* anchorId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedPersistenceAnchorTypesANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t trackableTypeCapacityInput,
+ uint32_t* trackableTypeCountOutput,
+ XrTrackableTypeANDROID* trackableTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateDeviceAnchorPersistenceANDROID(
+ XrSession session,
+ const XrDeviceAnchorPersistenceCreateInfoANDROID* createInfo,
+ XrDeviceAnchorPersistenceANDROID* outHandle);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyDeviceAnchorPersistenceANDROID(
+ XrDeviceAnchorPersistenceANDROID handle);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistAnchorANDROID(
+ XrDeviceAnchorPersistenceANDROID handle,
+ const XrPersistedAnchorSpaceInfoANDROID* persistedInfo,
+ XrUuidEXT* anchorIdOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorPersistStateANDROID(
+ XrDeviceAnchorPersistenceANDROID handle,
+ const XrUuidEXT* anchorId,
+ XrAnchorPersistStateANDROID* persistState);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePersistedAnchorSpaceANDROID(
+ XrDeviceAnchorPersistenceANDROID handle,
+ const XrPersistedAnchorSpaceCreateInfoANDROID* createInfo,
+ XrSpace* anchorOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePersistedAnchorsANDROID(
+ XrDeviceAnchorPersistenceANDROID handle,
+ uint32_t anchorIdCapacityInput,
+ uint32_t* anchorIdCountOutput,
+ XrUuidEXT* anchorIds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistAnchorANDROID(
+ XrDeviceAnchorPersistenceANDROID handle,
+ const XrUuidEXT* anchorId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_face_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_face_tracking 1
+XR_DEFINE_HANDLE(XrFaceTrackerANDROID)
+#define XR_ANDROID_face_tracking_SPEC_VERSION 1
+#define XR_ANDROID_FACE_TRACKING_EXTENSION_NAME "XR_ANDROID_face_tracking"
+#define XR_FACE_PARAMETER_COUNT_ANDROID 68
+#define XR_FACE_REGION_CONFIDENCE_COUNT_ANDROID 3
+
+typedef enum XrFaceParameterIndicesANDROID {
+ XR_FACE_PARAMETER_INDICES_BROW_LOWERER_L_ANDROID = 0,
+ XR_FACE_PARAMETER_INDICES_BROW_LOWERER_R_ANDROID = 1,
+ XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_L_ANDROID = 2,
+ XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_R_ANDROID = 3,
+ XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_L_ANDROID = 4,
+ XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_R_ANDROID = 5,
+ XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_L_ANDROID = 6,
+ XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_R_ANDROID = 7,
+ XR_FACE_PARAMETER_INDICES_CHIN_RAISER_B_ANDROID = 8,
+ XR_FACE_PARAMETER_INDICES_CHIN_RAISER_T_ANDROID = 9,
+ XR_FACE_PARAMETER_INDICES_DIMPLER_L_ANDROID = 10,
+ XR_FACE_PARAMETER_INDICES_DIMPLER_R_ANDROID = 11,
+ XR_FACE_PARAMETER_INDICES_EYES_CLOSED_L_ANDROID = 12,
+ XR_FACE_PARAMETER_INDICES_EYES_CLOSED_R_ANDROID = 13,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_L_ANDROID = 14,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_R_ANDROID = 15,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_L_ANDROID = 16,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_R_ANDROID = 17,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_L_ANDROID = 18,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_R_ANDROID = 19,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_L_ANDROID = 20,
+ XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_R_ANDROID = 21,
+ XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_L_ANDROID = 22,
+ XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_R_ANDROID = 23,
+ XR_FACE_PARAMETER_INDICES_JAW_DROP_ANDROID = 24,
+ XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_LEFT_ANDROID = 25,
+ XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_RIGHT_ANDROID = 26,
+ XR_FACE_PARAMETER_INDICES_JAW_THRUST_ANDROID = 27,
+ XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_L_ANDROID = 28,
+ XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_R_ANDROID = 29,
+ XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_L_ANDROID = 30,
+ XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_R_ANDROID = 31,
+ XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_L_ANDROID = 32,
+ XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_R_ANDROID = 33,
+ XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LB_ANDROID = 34,
+ XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LT_ANDROID = 35,
+ XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RB_ANDROID = 36,
+ XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RT_ANDROID = 37,
+ XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_L_ANDROID = 38,
+ XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_R_ANDROID = 39,
+ XR_FACE_PARAMETER_INDICES_LIP_PUCKER_L_ANDROID = 40,
+ XR_FACE_PARAMETER_INDICES_LIP_PUCKER_R_ANDROID = 41,
+ XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_L_ANDROID = 42,
+ XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_R_ANDROID = 43,
+ XR_FACE_PARAMETER_INDICES_LIP_SUCK_LB_ANDROID = 44,
+ XR_FACE_PARAMETER_INDICES_LIP_SUCK_LT_ANDROID = 45,
+ XR_FACE_PARAMETER_INDICES_LIP_SUCK_RB_ANDROID = 46,
+ XR_FACE_PARAMETER_INDICES_LIP_SUCK_RT_ANDROID = 47,
+ XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_L_ANDROID = 48,
+ XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_R_ANDROID = 49,
+ XR_FACE_PARAMETER_INDICES_LIPS_TOWARD_ANDROID = 50,
+ XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_L_ANDROID = 51,
+ XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_R_ANDROID = 52,
+ XR_FACE_PARAMETER_INDICES_MOUTH_LEFT_ANDROID = 53,
+ XR_FACE_PARAMETER_INDICES_MOUTH_RIGHT_ANDROID = 54,
+ XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_L_ANDROID = 55,
+ XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_R_ANDROID = 56,
+ XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_L_ANDROID = 57,
+ XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_R_ANDROID = 58,
+ XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_L_ANDROID = 59,
+ XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_R_ANDROID = 60,
+ XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_L_ANDROID = 61,
+ XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_R_ANDROID = 62,
+ XR_FACE_PARAMETER_INDICES_TONGUE_OUT_ANDROID = 63,
+ XR_FACE_PARAMETER_INDICES_TONGUE_LEFT_ANDROID = 64,
+ XR_FACE_PARAMETER_INDICES_TONGUE_RIGHT_ANDROID = 65,
+ XR_FACE_PARAMETER_INDICES_TONGUE_UP_ANDROID = 66,
+ XR_FACE_PARAMETER_INDICES_TONGUE_DOWN_ANDROID = 67,
+ XR_FACE_PARAMETER_INDICES_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrFaceParameterIndicesANDROID;
+
+typedef enum XrFaceTrackingStateANDROID {
+ XR_FACE_TRACKING_STATE_PAUSED_ANDROID = 0,
+ XR_FACE_TRACKING_STATE_STOPPED_ANDROID = 1,
+ XR_FACE_TRACKING_STATE_TRACKING_ANDROID = 2,
+ XR_FACE_TRACKING_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrFaceTrackingStateANDROID;
+
+typedef enum XrFaceConfidenceRegionsANDROID {
+ XR_FACE_CONFIDENCE_REGIONS_LOWER_ANDROID = 0,
+ XR_FACE_CONFIDENCE_REGIONS_LEFT_UPPER_ANDROID = 1,
+ XR_FACE_CONFIDENCE_REGIONS_RIGHT_UPPER_ANDROID = 2,
+ XR_FACE_CONFIDENCE_REGIONS_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrFaceConfidenceRegionsANDROID;
+typedef struct XrFaceTrackerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrFaceTrackerCreateInfoANDROID;
+
+typedef struct XrFaceStateGetInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrFaceStateGetInfoANDROID;
+
+typedef struct XrFaceStateANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t parametersCapacityInput;
+ uint32_t parametersCountOutput;
+ float* parameters;
+ XrFaceTrackingStateANDROID faceTrackingState;
+ XrTime sampleTime;
+ XrBool32 isValid;
+ uint32_t regionConfidencesCapacityInput;
+ uint32_t regionConfidencesCountOutput;
+ float* regionConfidences;
+} XrFaceStateANDROID;
+
+// XrSystemFaceTrackingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemFaceTrackingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFaceTracking;
+} XrSystemFaceTrackingPropertiesANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerANDROID)(XrSession session, const XrFaceTrackerCreateInfoANDROID* createInfo, XrFaceTrackerANDROID* faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerANDROID)(XrFaceTrackerANDROID faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFaceStateANDROID)(XrFaceTrackerANDROID faceTracker, const XrFaceStateGetInfoANDROID* getInfo, XrFaceStateANDROID* faceStateOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFaceCalibrationStateANDROID)(XrFaceTrackerANDROID faceTracker, XrBool32* faceIsCalibratedOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerANDROID(
+ XrSession session,
+ const XrFaceTrackerCreateInfoANDROID* createInfo,
+ XrFaceTrackerANDROID* faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerANDROID(
+ XrFaceTrackerANDROID faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceStateANDROID(
+ XrFaceTrackerANDROID faceTracker,
+ const XrFaceStateGetInfoANDROID* getInfo,
+ XrFaceStateANDROID* faceStateOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceCalibrationStateANDROID(
+ XrFaceTrackerANDROID faceTracker,
+ XrBool32* faceIsCalibratedOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_passthrough_camera_state is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_passthrough_camera_state 1
+#define XR_ANDROID_passthrough_camera_state_SPEC_VERSION 1
+#define XR_ANDROID_PASSTHROUGH_CAMERA_STATE_EXTENSION_NAME "XR_ANDROID_passthrough_camera_state"
+
+typedef enum XrPassthroughCameraStateANDROID {
+ XR_PASSTHROUGH_CAMERA_STATE_DISABLED_ANDROID = 0,
+ XR_PASSTHROUGH_CAMERA_STATE_INITIALIZING_ANDROID = 1,
+ XR_PASSTHROUGH_CAMERA_STATE_READY_ANDROID = 2,
+ XR_PASSTHROUGH_CAMERA_STATE_ERROR_ANDROID = 3,
+ XR_PASSTHROUGH_CAMERA_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrPassthroughCameraStateANDROID;
+// XrSystemPassthroughCameraStatePropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemPassthroughCameraStatePropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsPassthroughCameraState;
+} XrSystemPassthroughCameraStatePropertiesANDROID;
+
+typedef struct XrPassthroughCameraStateGetInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrPassthroughCameraStateGetInfoANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetPassthroughCameraStateANDROID)(XrSession session, const XrPassthroughCameraStateGetInfoANDROID* getInfo, XrPassthroughCameraStateANDROID* cameraStateOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughCameraStateANDROID(
+ XrSession session,
+ const XrPassthroughCameraStateGetInfoANDROID* getInfo,
+ XrPassthroughCameraStateANDROID* cameraStateOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_recommended_resolution is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_recommended_resolution 1
+#define XR_ANDROID_recommended_resolution_SPEC_VERSION 1
+#define XR_ANDROID_RECOMMENDED_RESOLUTION_EXTENSION_NAME "XR_ANDROID_recommended_resolution"
+typedef struct XrEventDataRecommendedResolutionChangedANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEventDataRecommendedResolutionChangedANDROID;
+
+
+
+// XR_ANDROID_composition_layer_passthrough_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_composition_layer_passthrough_mesh 1
+XR_DEFINE_HANDLE(XrPassthroughLayerANDROID)
+#define XR_ANDROID_composition_layer_passthrough_mesh_SPEC_VERSION 1
+#define XR_ANDROID_COMPOSITION_LAYER_PASSTHROUGH_MESH_EXTENSION_NAME "XR_ANDROID_composition_layer_passthrough_mesh"
+
+typedef enum XrWindingOrderANDROID {
+ XR_WINDING_ORDER_UNKNOWN_ANDROID = 0,
+ XR_WINDING_ORDER_CW_ANDROID = 1,
+ XR_WINDING_ORDER_CCW_ANDROID = 2,
+ XR_WINDING_ORDER_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrWindingOrderANDROID;
+typedef struct XrPassthroughLayerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacity;
+ uint32_t indexCapacity;
+} XrPassthroughLayerCreateInfoANDROID;
+
+// XrPassthroughLayerMeshANDROID extends XrPassthroughLayerCreateInfoANDROID
+typedef struct XrPassthroughLayerMeshANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrWindingOrderANDROID windingOrder;
+ uint32_t vertexCount;
+ const XrVector3f* vertices;
+ uint32_t indexCount;
+ const uint16_t* indices;
+} XrPassthroughLayerMeshANDROID;
+
+typedef struct XrCompositionLayerPassthroughANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerFlags layerFlags;
+ XrSpace space;
+ XrPosef pose;
+ XrVector3f scale;
+ float opacity;
+ XrPassthroughLayerANDROID layer;
+} XrCompositionLayerPassthroughANDROID;
+
+// XrSystemPassthroughLayerPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemPassthroughLayerPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsPassthroughLayer;
+ uint32_t maxMeshIndexCount;
+ uint32_t maxMeshVertexCount;
+} XrSystemPassthroughLayerPropertiesANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughLayerANDROID)(XrSession session, const XrPassthroughLayerCreateInfoANDROID* createInfo, XrPassthroughLayerANDROID* layer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughLayerANDROID)(XrPassthroughLayerANDROID layer);
+typedef XrResult (XRAPI_PTR *PFN_xrSetPassthroughLayerMeshANDROID)(XrPassthroughLayerANDROID layer, const XrPassthroughLayerMeshANDROID* mesh);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughLayerANDROID(
+ XrSession session,
+ const XrPassthroughLayerCreateInfoANDROID* createInfo,
+ XrPassthroughLayerANDROID* layer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughLayerANDROID(
+ XrPassthroughLayerANDROID layer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetPassthroughLayerMeshANDROID(
+ XrPassthroughLayerANDROID layer,
+ const XrPassthroughLayerMeshANDROID* mesh);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_raycast is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_raycast 1
+#define XR_ANDROID_raycast_SPEC_VERSION 1
+#define XR_ANDROID_RAYCAST_EXTENSION_NAME "XR_ANDROID_raycast"
+typedef struct XrRaycastInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t maxResults;
+ uint32_t trackerCount;
+ const XrTrackableTrackerANDROID* trackers;
+ XrVector3f origin;
+ XrVector3f trajectory;
+ XrSpace space;
+ XrTime time;
+} XrRaycastInfoANDROID;
+
+typedef struct XrRaycastHitResultANDROID {
+ XrTrackableTypeANDROID type;
+ XrTrackableANDROID trackable;
+ XrPosef pose;
+} XrRaycastHitResultANDROID;
+
+typedef struct XrRaycastHitResultsANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultsCapacityInput;
+ uint32_t resultsCountOutput;
+ XrRaycastHitResultANDROID* results;
+} XrRaycastHitResultsANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRaycastSupportedTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrRaycastANDROID)(XrSession session, const XrRaycastInfoANDROID* rayInfo, XrRaycastHitResultsANDROID* results);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRaycastSupportedTrackableTypesANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t trackableTypeCapacityInput,
+ uint32_t* trackableTypeCountOutput,
+ XrTrackableTypeANDROID* trackableTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRaycastANDROID(
+ XrSession session,
+ const XrRaycastInfoANDROID* rayInfo,
+ XrRaycastHitResultsANDROID* results);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_performance_metrics is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_performance_metrics 1
+#define XR_ANDROID_performance_metrics_SPEC_VERSION 1
+#define XR_ANDROID_PERFORMANCE_METRICS_EXTENSION_NAME "XR_ANDROID_performance_metrics"
+
+typedef enum XrPerformanceMetricsCounterUnitANDROID {
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_ANDROID = 0,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_ANDROID = 1,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_ANDROID = 2,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_ANDROID = 3,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_ANDROID = 4,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrPerformanceMetricsCounterUnitANDROID;
+typedef XrFlags64 XrPerformanceMetricsCounterFlagsANDROID;
+
+// Flag bits for XrPerformanceMetricsCounterFlagsANDROID
+static const XrPerformanceMetricsCounterFlagsANDROID XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_ANDROID = 0x00000001;
+static const XrPerformanceMetricsCounterFlagsANDROID XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_ANDROID = 0x00000002;
+static const XrPerformanceMetricsCounterFlagsANDROID XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_ANDROID = 0x00000004;
+
+typedef struct XrPerformanceMetricsStateANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrPerformanceMetricsStateANDROID;
+
+typedef struct XrPerformanceMetricsCounterANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPerformanceMetricsCounterFlagsANDROID counterFlags;
+ XrPerformanceMetricsCounterUnitANDROID counterUnit;
+ uint32_t uintValue;
+ float floatValue;
+} XrPerformanceMetricsCounterANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePerformanceMetricsCounterPathsANDROID)(XrInstance instance, uint32_t counterPathCapacityInput, uint32_t* counterPathCountOutput, XrPath* counterPaths);
+typedef XrResult (XRAPI_PTR *PFN_xrSetPerformanceMetricsStateANDROID)(XrSession session, const XrPerformanceMetricsStateANDROID* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPerformanceMetricsStateANDROID)(XrSession session, XrPerformanceMetricsStateANDROID* state);
+typedef XrResult (XRAPI_PTR *PFN_xrQueryPerformanceMetricsCounterANDROID)(XrSession session, XrPath counterPath, XrPerformanceMetricsCounterANDROID* counter);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePerformanceMetricsCounterPathsANDROID(
+ XrInstance instance,
+ uint32_t counterPathCapacityInput,
+ uint32_t* counterPathCountOutput,
+ XrPath* counterPaths);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetPerformanceMetricsStateANDROID(
+ XrSession session,
+ const XrPerformanceMetricsStateANDROID* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPerformanceMetricsStateANDROID(
+ XrSession session,
+ XrPerformanceMetricsStateANDROID* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterANDROID(
+ XrSession session,
+ XrPath counterPath,
+ XrPerformanceMetricsCounterANDROID* counter);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_trackables_object is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_trackables_object 1
+#define XR_ANDROID_trackables_object_SPEC_VERSION 2
+#define XR_ANDROID_TRACKABLES_OBJECT_EXTENSION_NAME "XR_ANDROID_trackables_object"
+
+typedef enum XrObjectLabelANDROID {
+ XR_OBJECT_LABEL_UNKNOWN_ANDROID = 0,
+ XR_OBJECT_LABEL_KEYBOARD_ANDROID = 1,
+ XR_OBJECT_LABEL_MOUSE_ANDROID = 2,
+ XR_OBJECT_LABEL_LAPTOP_ANDROID = 3,
+ XR_OBJECT_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrObjectLabelANDROID;
+typedef struct XrTrackableObjectANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTrackingStateANDROID trackingState;
+ XrPosef centerPose;
+ XrExtent3DfEXT extents;
+ XrObjectLabelANDROID objectLabel;
+ XrTime lastUpdatedTime;
+} XrTrackableObjectANDROID;
+
+// XrTrackableObjectConfigurationANDROID extends XrTrackableTrackerCreateInfoANDROID
+typedef struct XrTrackableObjectConfigurationANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t labelCount;
+ const XrObjectLabelANDROID* activeLabels;
+} XrTrackableObjectConfigurationANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableObjectANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableObjectANDROID* objectOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableObjectANDROID(
+ XrTrackableTrackerANDROID tracker,
+ const XrTrackableGetInfoANDROID* getInfo,
+ XrTrackableObjectANDROID* objectOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_unbounded_reference_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_unbounded_reference_space 1
+#define XR_ANDROID_unbounded_reference_space_SPEC_VERSION 1
+#define XR_ANDROID_UNBOUNDED_REFERENCE_SPACE_EXTENSION_NAME "XR_ANDROID_unbounded_reference_space"
+
+
+// XR_EXT_future is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_future 1
+#define XR_EXT_future_SPEC_VERSION 2
+#define XR_EXT_FUTURE_EXTENSION_NAME "XR_EXT_future"
+#define XR_NULL_FUTURE_EXT 0
+
+typedef enum XrFutureStateEXT {
+ XR_FUTURE_STATE_PENDING_EXT = 1,
+ XR_FUTURE_STATE_READY_EXT = 2,
+ XR_FUTURE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrFutureStateEXT;
+typedef struct XrFutureCancelInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFutureEXT future;
+} XrFutureCancelInfoEXT;
+
+typedef struct XrFuturePollInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFutureEXT future;
+} XrFuturePollInfoEXT;
+
+typedef struct XR_MAY_ALIAS XrFutureCompletionBaseHeaderEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrFutureCompletionBaseHeaderEXT;
+
+typedef struct XrFuturePollResultEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrFutureStateEXT state;
+} XrFuturePollResultEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrPollFutureEXT)(XrInstance instance, const XrFuturePollInfoEXT* pollInfo, XrFuturePollResultEXT* pollResult);
+typedef XrResult (XRAPI_PTR *PFN_xrCancelFutureEXT)(XrInstance instance, const XrFutureCancelInfoEXT* cancelInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrPollFutureEXT(
+ XrInstance instance,
+ const XrFuturePollInfoEXT* pollInfo,
+ XrFuturePollResultEXT* pollResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCancelFutureEXT(
+ XrInstance instance,
+ const XrFutureCancelInfoEXT* cancelInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_user_presence is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_user_presence 1
+#define XR_EXT_user_presence_SPEC_VERSION 1
+#define XR_EXT_USER_PRESENCE_EXTENSION_NAME "XR_EXT_user_presence"
+typedef struct XrEventDataUserPresenceChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSession session;
+ XrBool32 isUserPresent;
+} XrEventDataUserPresenceChangedEXT;
+
+// XrSystemUserPresencePropertiesEXT extends XrSystemProperties
+typedef struct XrSystemUserPresencePropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsUserPresence;
+} XrSystemUserPresencePropertiesEXT;
+
+
+
+// XR_ML_user_calibration is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_user_calibration 1
+#define XR_ML_user_calibration_SPEC_VERSION 1
+#define XR_ML_USER_CALIBRATION_EXTENSION_NAME "XR_ML_user_calibration"
+
+typedef enum XrHeadsetFitStatusML {
+ XR_HEADSET_FIT_STATUS_UNKNOWN_ML = 0,
+ XR_HEADSET_FIT_STATUS_NOT_WORN_ML = 1,
+ XR_HEADSET_FIT_STATUS_GOOD_FIT_ML = 2,
+ XR_HEADSET_FIT_STATUS_BAD_FIT_ML = 3,
+ XR_HEADSET_FIT_STATUS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrHeadsetFitStatusML;
+
+typedef enum XrEyeCalibrationStatusML {
+ XR_EYE_CALIBRATION_STATUS_UNKNOWN_ML = 0,
+ XR_EYE_CALIBRATION_STATUS_NONE_ML = 1,
+ XR_EYE_CALIBRATION_STATUS_COARSE_ML = 2,
+ XR_EYE_CALIBRATION_STATUS_FINE_ML = 3,
+ XR_EYE_CALIBRATION_STATUS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrEyeCalibrationStatusML;
+typedef struct XrEventDataHeadsetFitChangedML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrHeadsetFitStatusML status;
+ XrTime time;
+} XrEventDataHeadsetFitChangedML;
+
+typedef struct XrEventDataEyeCalibrationChangedML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEyeCalibrationStatusML status;
+} XrEventDataEyeCalibrationChangedML;
+
+typedef struct XrUserCalibrationEnableEventsInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrUserCalibrationEnableEventsInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnableUserCalibrationEventsML)(XrInstance instance, const XrUserCalibrationEnableEventsInfoML* enableInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML(
+ XrInstance instance,
+ const XrUserCalibrationEnableEventsInfoML* enableInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_system_notifications is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_system_notifications 1
+#define XR_ML_system_notifications_SPEC_VERSION 1
+#define XR_ML_SYSTEM_NOTIFICATIONS_EXTENSION_NAME "XR_ML_system_notifications"
+typedef struct XrSystemNotificationsSetInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 suppressNotifications;
+} XrSystemNotificationsSetInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetSystemNotificationsML)(XrInstance instance, const XrSystemNotificationsSetInfoML* info);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetSystemNotificationsML(
+ XrInstance instance,
+ const XrSystemNotificationsSetInfoML* info);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_world_mesh_detection is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_world_mesh_detection 1
+XR_DEFINE_HANDLE(XrWorldMeshDetectorML)
+#define XR_ML_world_mesh_detection_SPEC_VERSION 1
+#define XR_ML_WORLD_MESH_DETECTION_EXTENSION_NAME "XR_ML_world_mesh_detection"
+
+typedef enum XrWorldMeshDetectorLodML {
+ XR_WORLD_MESH_DETECTOR_LOD_MINIMUM_ML = 0,
+ XR_WORLD_MESH_DETECTOR_LOD_MEDIUM_ML = 1,
+ XR_WORLD_MESH_DETECTOR_LOD_MAXIMUM_ML = 2,
+ XR_WORLD_MESH_DETECTOR_LOD_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshDetectorLodML;
+
+typedef enum XrWorldMeshBlockStatusML {
+ XR_WORLD_MESH_BLOCK_STATUS_NEW_ML = 0,
+ XR_WORLD_MESH_BLOCK_STATUS_UPDATED_ML = 1,
+ XR_WORLD_MESH_BLOCK_STATUS_DELETED_ML = 2,
+ XR_WORLD_MESH_BLOCK_STATUS_UNCHANGED_ML = 3,
+ XR_WORLD_MESH_BLOCK_STATUS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshBlockStatusML;
+
+typedef enum XrWorldMeshBlockResultML {
+ XR_WORLD_MESH_BLOCK_RESULT_SUCCESS_ML = 0,
+ XR_WORLD_MESH_BLOCK_RESULT_FAILED_ML = 1,
+ XR_WORLD_MESH_BLOCK_RESULT_PENDING_ML = 2,
+ XR_WORLD_MESH_BLOCK_RESULT_PARTIAL_UPDATE_ML = 3,
+ XR_WORLD_MESH_BLOCK_RESULT_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshBlockResultML;
+typedef XrFlags64 XrWorldMeshDetectorFlagsML;
+
+// Flag bits for XrWorldMeshDetectorFlagsML
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_POINT_CLOUD_BIT_ML = 0x00000001;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_COMPUTE_NORMALS_BIT_ML = 0x00000002;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_COMPUTE_CONFIDENCE_BIT_ML = 0x00000004;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_PLANARIZE_BIT_ML = 0x00000008;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_REMOVE_MESH_SKIRT_BIT_ML = 0x00000010;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_INDEX_ORDER_CW_BIT_ML = 0x00000020;
+
+typedef struct XrWorldMeshDetectorCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrWorldMeshDetectorCreateInfoML;
+
+typedef struct XrWorldMeshBlockStateML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrPosef meshBoundingBoxCenter;
+ XrExtent3DfEXT meshBoundingBoxExtents;
+ XrTime lastUpdateTime;
+ XrWorldMeshBlockStatusML status;
+} XrWorldMeshBlockStateML;
+
+typedef struct XrWorldMeshStateRequestInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrPosef boundingBoxCenter;
+ XrExtent3DfEXT boundingBoxExtents;
+} XrWorldMeshStateRequestInfoML;
+
+typedef struct XrWorldMeshStateRequestCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrTime timestamp;
+ uint32_t meshBlockStateCapacityInput;
+ uint32_t meshBlockStateCountOutput;
+ XrWorldMeshBlockStateML* meshBlockStates;
+} XrWorldMeshStateRequestCompletionML;
+
+typedef struct XrWorldMeshBufferRecommendedSizeInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t maxBlockCount;
+} XrWorldMeshBufferRecommendedSizeInfoML;
+
+typedef struct XrWorldMeshBufferSizeML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t size;
+} XrWorldMeshBufferSizeML;
+
+typedef struct XrWorldMeshBufferML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t bufferSize;
+ void* XR_MAY_ALIAS buffer;
+} XrWorldMeshBufferML;
+
+typedef struct XrWorldMeshBlockRequestML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrWorldMeshDetectorLodML lod;
+} XrWorldMeshBlockRequestML;
+
+typedef struct XrWorldMeshGetInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrWorldMeshDetectorFlagsML flags;
+ float fillHoleLength;
+ float disconnectedComponentArea;
+ uint32_t blockCount;
+ XrWorldMeshBlockRequestML* blocks;
+} XrWorldMeshGetInfoML;
+
+typedef struct XrWorldMeshBlockML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrWorldMeshBlockResultML blockResult;
+ XrWorldMeshDetectorLodML lod;
+ XrWorldMeshDetectorFlagsML flags;
+ uint32_t indexCount;
+ uint16_t* indexBuffer;
+ uint32_t vertexCount;
+ XrVector3f* vertexBuffer;
+ uint32_t normalCount;
+ XrVector3f* normalBuffer;
+ uint32_t confidenceCount;
+ float* confidenceBuffer;
+} XrWorldMeshBlockML;
+
+typedef struct XrWorldMeshRequestCompletionInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace meshSpace;
+ XrTime meshSpaceLocateTime;
+} XrWorldMeshRequestCompletionInfoML;
+
+typedef struct XrWorldMeshRequestCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t blockCount;
+ XrWorldMeshBlockML* blocks;
+} XrWorldMeshRequestCompletionML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateWorldMeshDetectorML)(XrSession session, const XrWorldMeshDetectorCreateInfoML* createInfo, XrWorldMeshDetectorML* detector);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyWorldMeshDetectorML)(XrWorldMeshDetectorML detector);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshStateAsyncML)(XrWorldMeshDetectorML detector, const XrWorldMeshStateRequestInfoML* stateRequest, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshStateCompleteML)(XrWorldMeshDetectorML detector, XrFutureEXT future, XrWorldMeshStateRequestCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrGetWorldMeshBufferRecommendSizeML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferRecommendedSizeInfoML* sizeInfo, XrWorldMeshBufferSizeML* size);
+typedef XrResult (XRAPI_PTR *PFN_xrAllocateWorldMeshBufferML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferSizeML* size, XrWorldMeshBufferML* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrFreeWorldMeshBufferML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferML* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshAsyncML)(XrWorldMeshDetectorML detector, const XrWorldMeshGetInfoML* getInfo, XrWorldMeshBufferML* buffer, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshCompleteML)(XrWorldMeshDetectorML detector, const XrWorldMeshRequestCompletionInfoML* completionInfo, XrFutureEXT future, XrWorldMeshRequestCompletionML* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateWorldMeshDetectorML(
+ XrSession session,
+ const XrWorldMeshDetectorCreateInfoML* createInfo,
+ XrWorldMeshDetectorML* detector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyWorldMeshDetectorML(
+ XrWorldMeshDetectorML detector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshStateAsyncML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshStateRequestInfoML* stateRequest,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshStateCompleteML(
+ XrWorldMeshDetectorML detector,
+ XrFutureEXT future,
+ XrWorldMeshStateRequestCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetWorldMeshBufferRecommendSizeML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferRecommendedSizeInfoML* sizeInfo,
+ XrWorldMeshBufferSizeML* size);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAllocateWorldMeshBufferML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferSizeML* size,
+ XrWorldMeshBufferML* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrFreeWorldMeshBufferML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferML* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshAsyncML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshGetInfoML* getInfo,
+ XrWorldMeshBufferML* buffer,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshCompleteML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshRequestCompletionInfoML* completionInfo,
+ XrFutureEXT future,
+ XrWorldMeshRequestCompletionML* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_facial_expression is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_facial_expression 1
+XR_DEFINE_HANDLE(XrFacialExpressionClientML)
+#define XR_ML_facial_expression_SPEC_VERSION 1
+#define XR_ML_FACIAL_EXPRESSION_EXTENSION_NAME "XR_ML_facial_expression"
+
+typedef enum XrFacialBlendShapeML {
+ XR_FACIAL_BLEND_SHAPE_BROW_LOWERER_L_ML = 0,
+ XR_FACIAL_BLEND_SHAPE_BROW_LOWERER_R_ML = 1,
+ XR_FACIAL_BLEND_SHAPE_CHEEK_RAISER_L_ML = 2,
+ XR_FACIAL_BLEND_SHAPE_CHEEK_RAISER_R_ML = 3,
+ XR_FACIAL_BLEND_SHAPE_CHIN_RAISER_ML = 4,
+ XR_FACIAL_BLEND_SHAPE_DIMPLER_L_ML = 5,
+ XR_FACIAL_BLEND_SHAPE_DIMPLER_R_ML = 6,
+ XR_FACIAL_BLEND_SHAPE_EYES_CLOSED_L_ML = 7,
+ XR_FACIAL_BLEND_SHAPE_EYES_CLOSED_R_ML = 8,
+ XR_FACIAL_BLEND_SHAPE_INNER_BROW_RAISER_L_ML = 9,
+ XR_FACIAL_BLEND_SHAPE_INNER_BROW_RAISER_R_ML = 10,
+ XR_FACIAL_BLEND_SHAPE_JAW_DROP_ML = 11,
+ XR_FACIAL_BLEND_SHAPE_LID_TIGHTENER_L_ML = 12,
+ XR_FACIAL_BLEND_SHAPE_LID_TIGHTENER_R_ML = 13,
+ XR_FACIAL_BLEND_SHAPE_LIP_CORNER_DEPRESSOR_L_ML = 14,
+ XR_FACIAL_BLEND_SHAPE_LIP_CORNER_DEPRESSOR_R_ML = 15,
+ XR_FACIAL_BLEND_SHAPE_LIP_CORNER_PULLER_L_ML = 16,
+ XR_FACIAL_BLEND_SHAPE_LIP_CORNER_PULLER_R_ML = 17,
+ XR_FACIAL_BLEND_SHAPE_LIP_FUNNELER_LB_ML = 18,
+ XR_FACIAL_BLEND_SHAPE_LIP_FUNNELER_LT_ML = 19,
+ XR_FACIAL_BLEND_SHAPE_LIP_FUNNELER_RB_ML = 20,
+ XR_FACIAL_BLEND_SHAPE_LIP_FUNNELER_RT_ML = 21,
+ XR_FACIAL_BLEND_SHAPE_LIP_PRESSOR_L_ML = 22,
+ XR_FACIAL_BLEND_SHAPE_LIP_PRESSOR_R_ML = 23,
+ XR_FACIAL_BLEND_SHAPE_LIP_PUCKER_L_ML = 24,
+ XR_FACIAL_BLEND_SHAPE_LIP_PUCKER_R_ML = 25,
+ XR_FACIAL_BLEND_SHAPE_LIP_STRETCHER_L_ML = 26,
+ XR_FACIAL_BLEND_SHAPE_LIP_STRETCHER_R_ML = 27,
+ XR_FACIAL_BLEND_SHAPE_LIP_SUCK_LB_ML = 28,
+ XR_FACIAL_BLEND_SHAPE_LIP_SUCK_LT_ML = 29,
+ XR_FACIAL_BLEND_SHAPE_LIP_SUCK_RB_ML = 30,
+ XR_FACIAL_BLEND_SHAPE_LIP_SUCK_RT_ML = 31,
+ XR_FACIAL_BLEND_SHAPE_LIP_TIGHTENER_L_ML = 32,
+ XR_FACIAL_BLEND_SHAPE_LIP_TIGHTENER_R_ML = 33,
+ XR_FACIAL_BLEND_SHAPE_LIPS_TOWARD_ML = 34,
+ XR_FACIAL_BLEND_SHAPE_LOWER_LIP_DEPRESSOR_L_ML = 35,
+ XR_FACIAL_BLEND_SHAPE_LOWER_LIP_DEPRESSOR_R_ML = 36,
+ XR_FACIAL_BLEND_SHAPE_NOSE_WRINKLER_L_ML = 37,
+ XR_FACIAL_BLEND_SHAPE_NOSE_WRINKLER_R_ML = 38,
+ XR_FACIAL_BLEND_SHAPE_OUTER_BROW_RAISER_L_ML = 39,
+ XR_FACIAL_BLEND_SHAPE_OUTER_BROW_RAISER_R_ML = 40,
+ XR_FACIAL_BLEND_SHAPE_UPPER_LID_RAISER_L_ML = 41,
+ XR_FACIAL_BLEND_SHAPE_UPPER_LID_RAISER_R_ML = 42,
+ XR_FACIAL_BLEND_SHAPE_UPPER_LIP_RAISER_L_ML = 43,
+ XR_FACIAL_BLEND_SHAPE_UPPER_LIP_RAISER_R_ML = 44,
+ XR_FACIAL_BLEND_SHAPE_TONGUE_OUT_ML = 45,
+ XR_FACIAL_BLEND_SHAPE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrFacialBlendShapeML;
+typedef XrFlags64 XrFacialExpressionBlendShapePropertiesFlagsML;
+
+// Flag bits for XrFacialExpressionBlendShapePropertiesFlagsML
+static const XrFacialExpressionBlendShapePropertiesFlagsML XR_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_VALID_BIT_ML = 0x00000001;
+static const XrFacialExpressionBlendShapePropertiesFlagsML XR_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_TRACKED_BIT_ML = 0x00000002;
+
+// XrSystemFacialExpressionPropertiesML extends XrSystemProperties
+typedef struct XrSystemFacialExpressionPropertiesML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFacialExpression;
+} XrSystemFacialExpressionPropertiesML;
+
+typedef struct XrFacialExpressionClientCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestedCount;
+ const XrFacialBlendShapeML* requestedFacialBlendShapes;
+} XrFacialExpressionClientCreateInfoML;
+
+typedef struct XrFacialExpressionBlendShapeGetInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrFacialExpressionBlendShapeGetInfoML;
+
+typedef struct XrFacialExpressionBlendShapePropertiesML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrFacialBlendShapeML requestedFacialBlendShape;
+ float weight;
+ XrFacialExpressionBlendShapePropertiesFlagsML flags;
+ XrTime time;
+} XrFacialExpressionBlendShapePropertiesML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFacialExpressionClientML)(XrSession session, const XrFacialExpressionClientCreateInfoML* createInfo, XrFacialExpressionClientML* facialExpressionClient);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFacialExpressionClientML)(XrFacialExpressionClientML facialExpressionClient);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFacialExpressionBlendShapePropertiesML)(XrFacialExpressionClientML facialExpressionClient, const XrFacialExpressionBlendShapeGetInfoML* blendShapeGetInfo, uint32_t blendShapeCount, XrFacialExpressionBlendShapePropertiesML* blendShapes);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFacialExpressionClientML(
+ XrSession session,
+ const XrFacialExpressionClientCreateInfoML* createInfo,
+ XrFacialExpressionClientML* facialExpressionClient);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFacialExpressionClientML(
+ XrFacialExpressionClientML facialExpressionClient);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionBlendShapePropertiesML(
+ XrFacialExpressionClientML facialExpressionClient,
+ const XrFacialExpressionBlendShapeGetInfoML* blendShapeGetInfo,
+ uint32_t blendShapeCount,
+ XrFacialExpressionBlendShapePropertiesML* blendShapes);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_view_configuration_depth_range_change is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_view_configuration_depth_range_change 1
+#define XR_ML_view_configuration_depth_range_change_SPEC_VERSION 1
+#define XR_ML_VIEW_CONFIGURATION_DEPTH_RANGE_CHANGE_EXTENSION_NAME "XR_ML_view_configuration_depth_range_change"
+
+
+// XR_YVR_controller_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_YVR_controller_interaction 1
+#define XR_YVR_controller_interaction_SPEC_VERSION 1
+#define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction"
+
+
+// XR_META_boundary_visibility is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_boundary_visibility 1
+#define XR_META_boundary_visibility_SPEC_VERSION 1
+#define XR_META_BOUNDARY_VISIBILITY_EXTENSION_NAME "XR_META_boundary_visibility"
+
+typedef enum XrBoundaryVisibilityMETA {
+ XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META = 1,
+ XR_BOUNDARY_VISIBILITY_SUPPRESSED_META = 2,
+ XR_BOUNDARY_VISIBILITY_MAX_ENUM_META = 0x7FFFFFFF
+} XrBoundaryVisibilityMETA;
+// XrSystemBoundaryVisibilityPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemBoundaryVisibilityPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBoundaryVisibility;
+} XrSystemBoundaryVisibilityPropertiesMETA;
+
+typedef struct XrEventDataBoundaryVisibilityChangedMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBoundaryVisibilityMETA boundaryVisibility;
+} XrEventDataBoundaryVisibilityChangedMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrRequestBoundaryVisibilityMETA)(XrSession session, XrBoundaryVisibilityMETA boundaryVisibility);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestBoundaryVisibilityMETA(
+ XrSession session,
+ XrBoundaryVisibilityMETA boundaryVisibility);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_simultaneous_hands_and_controllers is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_simultaneous_hands_and_controllers 1
+#define XR_META_simultaneous_hands_and_controllers_SPEC_VERSION 1
+#define XR_META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME "XR_META_simultaneous_hands_and_controllers"
+// XrSystemSimultaneousHandsAndControllersPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemSimultaneousHandsAndControllersPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSimultaneousHandsAndControllers;
+} XrSystemSimultaneousHandsAndControllersPropertiesMETA;
+
+typedef struct XrSimultaneousHandsAndControllersTrackingResumeInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSimultaneousHandsAndControllersTrackingResumeInfoMETA;
+
+typedef struct XrSimultaneousHandsAndControllersTrackingPauseInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSimultaneousHandsAndControllersTrackingPauseInfoMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrResumeSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrPauseSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrResumeSimultaneousHandsAndControllersTrackingMETA(
+ XrSession session,
+ const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPauseSimultaneousHandsAndControllersTrackingMETA(
+ XrSession session,
+ const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_face_tracking_visemes is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_face_tracking_visemes 1
+#define XR_FACE_TRACKING_VISEME_COUNT_META 15
+#define XR_META_face_tracking_visemes_SPEC_VERSION 1
+#define XR_META_FACE_TRACKING_VISEMES_EXTENSION_NAME "XR_META_face_tracking_visemes"
+
+typedef enum XrFaceTrackingVisemeMETA {
+ XR_FACE_TRACKING_VISEME_SIL_META = 0,
+ XR_FACE_TRACKING_VISEME_PP_META = 1,
+ XR_FACE_TRACKING_VISEME_FF_META = 2,
+ XR_FACE_TRACKING_VISEME_TH_META = 3,
+ XR_FACE_TRACKING_VISEME_DD_META = 4,
+ XR_FACE_TRACKING_VISEME_KK_META = 5,
+ XR_FACE_TRACKING_VISEME_CH_META = 6,
+ XR_FACE_TRACKING_VISEME_SS_META = 7,
+ XR_FACE_TRACKING_VISEME_NN_META = 8,
+ XR_FACE_TRACKING_VISEME_RR_META = 9,
+ XR_FACE_TRACKING_VISEME_AA_META = 10,
+ XR_FACE_TRACKING_VISEME_E_META = 11,
+ XR_FACE_TRACKING_VISEME_IH_META = 12,
+ XR_FACE_TRACKING_VISEME_OH_META = 13,
+ XR_FACE_TRACKING_VISEME_OU_META = 14,
+ XR_FACE_TRACKING_VISEME_MAX_ENUM_META = 0x7FFFFFFF
+} XrFaceTrackingVisemeMETA;
+// XrFaceTrackingVisemesMETA extends XrFaceExpressionWeights2FB
+typedef struct XrFaceTrackingVisemesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 isValid;
+ float visemes[XR_FACE_TRACKING_VISEME_COUNT_META];
+} XrFaceTrackingVisemesMETA;
+
+// XrSystemFaceTrackingVisemesPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemFaceTrackingVisemesPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsVisemes;
+} XrSystemFaceTrackingVisemesPropertiesMETA;
+
+
+
+// XR_META_spatial_entity_semantic_label is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_semantic_label 1
+#define XR_META_spatial_entity_semantic_label_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_SEMANTIC_LABEL_EXTENSION_NAME "XR_META_spatial_entity_semantic_label"
+
+typedef enum XrSemanticLabelMETA {
+ XR_SEMANTIC_LABEL_UNKNOWN_META = 0,
+ XR_SEMANTIC_LABEL_FLOOR_META = 1,
+ XR_SEMANTIC_LABEL_CEILING_META = 2,
+ XR_SEMANTIC_LABEL_WALL_FACE_META = 3,
+ XR_SEMANTIC_LABEL_INNER_WALL_FACE_META = 4,
+ XR_SEMANTIC_LABEL_INVISIBLE_WALL_FACE_META = 5,
+ XR_SEMANTIC_LABEL_DOOR_FRAME_META = 6,
+ XR_SEMANTIC_LABEL_WINDOW_FRAME_META = 7,
+ XR_SEMANTIC_LABEL_MAX_ENUM_META = 0x7FFFFFFF
+} XrSemanticLabelMETA;
+
+
+// XR_META_spatial_entity_room_mesh is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_room_mesh 1
+#define XR_META_spatial_entity_room_mesh_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_ROOM_MESH_EXTENSION_NAME "XR_META_spatial_entity_room_mesh"
+typedef struct XrRoomMeshFaceMETA {
+ XrUuid uuid;
+ XrUuid parentUuid;
+ XrSemanticLabelMETA semanticLabel;
+} XrRoomMeshFaceMETA;
+
+typedef struct XrRoomMeshFaceIndicesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrRoomMeshFaceIndicesMETA;
+
+typedef struct XrSpaceRoomMeshGetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t recognizedSemanticLabelCount;
+ const XrSemanticLabelMETA* recognizedSemanticLabels;
+} XrSpaceRoomMeshGetInfoMETA;
+
+typedef struct XrRoomMeshMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertices;
+ uint32_t faceCapacityInput;
+ uint32_t faceCountOutput;
+ XrRoomMeshFaceMETA* faces;
+} XrRoomMeshMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceRoomMeshMETA)(XrSpace space, const XrSpaceRoomMeshGetInfoMETA* getInfo, XrRoomMeshMETA* roomMeshOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceRoomMeshFaceIndicesMETA)(XrSpace space, const XrUuid* faceUuid, XrRoomMeshFaceIndicesMETA* roomMeshFaceIndicesOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomMeshMETA(
+ XrSpace space,
+ const XrSpaceRoomMeshGetInfoMETA* getInfo,
+ XrRoomMeshMETA* roomMeshOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomMeshFaceIndicesMETA(
+ XrSpace space,
+ const XrUuid* faceUuid,
+ XrRoomMeshFaceIndicesMETA* roomMeshFaceIndicesOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_composition_layer_inverted_alpha 1
+#define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1
+#define XR_EXT_COMPOSITION_LAYER_INVERTED_ALPHA_EXTENSION_NAME "XR_EXT_composition_layer_inverted_alpha"
+
+
+// XR_META_colocation_discovery is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_colocation_discovery 1
+#define XR_MAX_COLOCATION_DISCOVERY_BUFFER_SIZE_META 1024
+#define XR_META_colocation_discovery_SPEC_VERSION 1
+#define XR_META_COLOCATION_DISCOVERY_EXTENSION_NAME "XR_META_colocation_discovery"
+typedef struct XrColocationDiscoveryStartInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrColocationDiscoveryStartInfoMETA;
+
+typedef struct XrColocationDiscoveryStopInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrColocationDiscoveryStopInfoMETA;
+
+typedef struct XrColocationAdvertisementStartInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t bufferSize;
+ uint8_t* buffer;
+} XrColocationAdvertisementStartInfoMETA;
+
+typedef struct XrColocationAdvertisementStopInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrColocationAdvertisementStopInfoMETA;
+
+typedef struct XrEventDataStartColocationAdvertisementCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB advertisementRequestId;
+ XrResult result;
+ XrUuid advertisementUuid;
+} XrEventDataStartColocationAdvertisementCompleteMETA;
+
+typedef struct XrEventDataStopColocationAdvertisementCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataStopColocationAdvertisementCompleteMETA;
+
+typedef struct XrEventDataColocationAdvertisementCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB advertisementRequestId;
+ XrResult result;
+} XrEventDataColocationAdvertisementCompleteMETA;
+
+typedef struct XrEventDataStartColocationDiscoveryCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB discoveryRequestId;
+ XrResult result;
+} XrEventDataStartColocationDiscoveryCompleteMETA;
+
+typedef struct XrEventDataColocationDiscoveryResultMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB discoveryRequestId;
+ XrUuid advertisementUuid;
+ uint32_t bufferSize;
+ uint8_t buffer[XR_MAX_COLOCATION_DISCOVERY_BUFFER_SIZE_META];
+} XrEventDataColocationDiscoveryResultMETA;
+
+typedef struct XrEventDataColocationDiscoveryCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB discoveryRequestId;
+ XrResult result;
+} XrEventDataColocationDiscoveryCompleteMETA;
+
+typedef struct XrEventDataStopColocationDiscoveryCompleteMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataStopColocationDiscoveryCompleteMETA;
+
+// XrSystemColocationDiscoveryPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemColocationDiscoveryPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsColocationDiscovery;
+} XrSystemColocationDiscoveryPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrStartColocationDiscoveryMETA)(XrSession session, const XrColocationDiscoveryStartInfoMETA* info, XrAsyncRequestIdFB* discoveryRequestId);
+typedef XrResult (XRAPI_PTR *PFN_xrStopColocationDiscoveryMETA)(XrSession session, const XrColocationDiscoveryStopInfoMETA* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrStartColocationAdvertisementMETA)(XrSession session, const XrColocationAdvertisementStartInfoMETA* info, XrAsyncRequestIdFB* advertisementRequestId);
+typedef XrResult (XRAPI_PTR *PFN_xrStopColocationAdvertisementMETA)(XrSession session, const XrColocationAdvertisementStopInfoMETA* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrStartColocationDiscoveryMETA(
+ XrSession session,
+ const XrColocationDiscoveryStartInfoMETA* info,
+ XrAsyncRequestIdFB* discoveryRequestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopColocationDiscoveryMETA(
+ XrSession session,
+ const XrColocationDiscoveryStopInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStartColocationAdvertisementMETA(
+ XrSession session,
+ const XrColocationAdvertisementStartInfoMETA* info,
+ XrAsyncRequestIdFB* advertisementRequestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopColocationAdvertisementMETA(
+ XrSession session,
+ const XrColocationAdvertisementStopInfoMETA* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_spatial_entity_group_sharing is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_spatial_entity_group_sharing 1
+#define XR_META_spatial_entity_group_sharing_SPEC_VERSION 1
+#define XR_META_SPATIAL_ENTITY_GROUP_SHARING_EXTENSION_NAME "XR_META_spatial_entity_group_sharing"
+// XrSystemSpatialEntityGroupSharingPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemSpatialEntityGroupSharingPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialEntityGroupSharing;
+} XrSystemSpatialEntityGroupSharingPropertiesMETA;
+
+typedef struct XrShareSpacesRecipientGroupsMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t groupCount;
+ XrUuid* groups;
+} XrShareSpacesRecipientGroupsMETA;
+
+// XrSpaceGroupUuidFilterInfoMETA extends XrSpaceQueryInfoBaseHeaderFB
+typedef struct XrSpaceGroupUuidFilterInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuid groupUuid;
+} XrSpaceGroupUuidFilterInfoMETA;
+
+
+
+// XR_META_environment_raycast is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_environment_raycast 1
+XR_DEFINE_HANDLE(XrEnvironmentRaycasterMETA)
+#define XR_META_environment_raycast_SPEC_VERSION 1
+#define XR_META_ENVIRONMENT_RAYCAST_EXTENSION_NAME "XR_META_environment_raycast"
+
+typedef enum XrEnvironmentRaycastHitStatusMETA {
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_HIT_META = 1,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_NO_HIT_META = 2,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_HIT_POINT_OCCLUDED_META = 3,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_HIT_POINT_OUTSIDE_OF_FOV_META = 4,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_RAY_OCCLUDED_META = 5,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_HIT_INVALID_ORIENTATION_META = 6,
+ XR_ENVIRONMENT_RAYCAST_HIT_STATUS_MAX_ENUM_META = 0x7FFFFFFF
+} XrEnvironmentRaycastHitStatusMETA;
+// XrSystemEnvironmentRaycastPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemEnvironmentRaycastPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEnvironmentRaycast;
+} XrSystemEnvironmentRaycastPropertiesMETA;
+
+typedef struct XrEnvironmentRaycasterCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEnvironmentRaycasterCreateInfoMETA;
+
+typedef struct XrEnvironmentRaycasterCreateCompletionMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrEnvironmentRaycasterMETA environmentRaycaster;
+} XrEnvironmentRaycasterCreateCompletionMETA;
+
+typedef struct XR_MAY_ALIAS XrEnvironmentRaycastFilterBaseHeaderMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEnvironmentRaycastFilterBaseHeaderMETA;
+
+typedef struct XrEnvironmentRaycastHitGetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrVector3f origin;
+ XrVector3f direction;
+ uint32_t filterCount;
+ const XrEnvironmentRaycastFilterBaseHeaderMETA* const * filters;
+} XrEnvironmentRaycastHitGetInfoMETA;
+
+typedef struct XrEnvironmentRaycastHitMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentRaycastHitStatusMETA status;
+ XrPosef pose;
+} XrEnvironmentRaycastHitMETA;
+
+typedef struct XrEnvironmentRaycastFilterDistanceMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float maxDistance;
+} XrEnvironmentRaycastFilterDistanceMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentRaycasterAsyncMETA)(XrSession session, const XrEnvironmentRaycasterCreateInfoMETA* info, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentRaycasterCompleteMETA)(XrSession session, XrFutureEXT future, XrEnvironmentRaycasterCreateCompletionMETA* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentRaycasterMETA)(XrEnvironmentRaycasterMETA environmentRaycaster);
+typedef XrResult (XRAPI_PTR *PFN_xrPerformEnvironmentRaycastMETA)(XrEnvironmentRaycasterMETA environmentRaycaster, const XrEnvironmentRaycastHitGetInfoMETA* info, XrEnvironmentRaycastHitMETA* hitPoint);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentRaycasterAsyncMETA(
+ XrSession session,
+ const XrEnvironmentRaycasterCreateInfoMETA* info,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentRaycasterCompleteMETA(
+ XrSession session,
+ XrFutureEXT future,
+ XrEnvironmentRaycasterCreateCompletionMETA* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentRaycasterMETA(
+ XrEnvironmentRaycasterMETA environmentRaycaster);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPerformEnvironmentRaycastMETA(
+ XrEnvironmentRaycasterMETA environmentRaycaster,
+ const XrEnvironmentRaycastHitGetInfoMETA* info,
+ XrEnvironmentRaycastHitMETA* hitPoint);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_tile_properties_hint is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_tile_properties_hint 1
+#define XR_META_tile_properties_hint_SPEC_VERSION 1
+#define XR_META_TILE_PROPERTIES_HINT_EXTENSION_NAME "XR_META_tile_properties_hint"
+typedef struct XrExtent3DiMETA {
+ int32_t width;
+ int32_t height;
+ int32_t depth;
+} XrExtent3DiMETA;
+
+typedef struct XrTilePropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrExtent3DiMETA tileDimensions;
+ XrExtent2Di apronDimensions;
+ XrOffset2Di origin;
+} XrTilePropertiesMETA;
+
+typedef struct XrTilePropertiesHintMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t propertiesCount;
+ const XrTilePropertiesMETA* properties;
+} XrTilePropertiesHintMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetTilePropertiesHintMETA)(XrSession session, const XrTilePropertiesHintMETA* properties);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetTilePropertiesHintMETA(
+ XrSession session,
+ const XrTilePropertiesHintMETA* properties);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_hand_tracking_unextrapolated_poses is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_hand_tracking_unextrapolated_poses 1
+#define XR_META_hand_tracking_unextrapolated_poses_SPEC_VERSION 1
+#define XR_META_HAND_TRACKING_UNEXTRAPOLATED_POSES_EXTENSION_NAME "XR_META_hand_tracking_unextrapolated_poses"
+// XrHandTrackingUnextrapolatedPosesRequestMETA extends XrHandJointsLocateInfoEXT
+typedef struct XrHandTrackingUnextrapolatedPosesRequestMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrHandTrackingUnextrapolatedPosesRequestMETA;
+
+// XrHandTrackingUnextrapolatedPosesMETA extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingUnextrapolatedPosesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTime captureTime;
+} XrHandTrackingUnextrapolatedPosesMETA;
+
+
+
+// XR_META_hand_tracking_frequency_hint is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_hand_tracking_frequency_hint 1
+#define XR_META_hand_tracking_frequency_hint_SPEC_VERSION 1
+#define XR_META_HAND_TRACKING_FREQUENCY_HINT_EXTENSION_NAME "XR_META_hand_tracking_frequency_hint"
+
+typedef enum XrHandTrackingFrequencyHintMETA {
+ XR_HAND_TRACKING_FREQUENCY_HINT_DEFAULT_META = 1,
+ XR_HAND_TRACKING_FREQUENCY_HINT_HIGH_META = 2,
+ XR_HAND_TRACKING_FREQUENCY_HINT_MAX_ENUM_META = 0x7FFFFFFF
+} XrHandTrackingFrequencyHintMETA;
+typedef XrResult (XRAPI_PTR *PFN_xrSetHandTrackingFrequencyHintMETA)(XrSession session, XrHandTrackingFrequencyHintMETA frequencyHint);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetHandTrackingFrequencyHintMETA(
+ XrSession session,
+ XrHandTrackingFrequencyHintMETA frequencyHint);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_META_hand_tracking_wide_motion_mode2 is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_hand_tracking_wide_motion_mode2 1
+#define XR_META_hand_tracking_wide_motion_mode2_SPEC_VERSION 1
+#define XR_META_HAND_TRACKING_WIDE_MOTION_MODE2_EXTENSION_NAME "XR_META_hand_tracking_wide_motion_mode2"
+
+
+// XR_ANDROID_light_estimation is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_light_estimation 1
+XR_DEFINE_HANDLE(XrLightEstimatorANDROID)
+#define XR_ANDROID_light_estimation_SPEC_VERSION 1
+#define XR_ANDROID_LIGHT_ESTIMATION_EXTENSION_NAME "XR_ANDROID_light_estimation"
+
+typedef enum XrLightEstimateStateANDROID {
+ XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID = 0,
+ XR_LIGHT_ESTIMATE_STATE_INVALID_ANDROID = 1,
+ XR_LIGHT_ESTIMATE_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrLightEstimateStateANDROID;
+
+typedef enum XrSphericalHarmonicsKindANDROID {
+ XR_SPHERICAL_HARMONICS_KIND_TOTAL_ANDROID = 0,
+ XR_SPHERICAL_HARMONICS_KIND_AMBIENT_ANDROID = 1,
+ XR_SPHERICAL_HARMONICS_KIND_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSphericalHarmonicsKindANDROID;
+// XrSystemLightEstimationPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemLightEstimationPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsLightEstimation;
+} XrSystemLightEstimationPropertiesANDROID;
+
+typedef struct XrLightEstimatorCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrLightEstimatorCreateInfoANDROID;
+
+typedef struct XrLightEstimateGetInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+} XrLightEstimateGetInfoANDROID;
+
+typedef struct XrLightEstimateANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrLightEstimateStateANDROID state;
+ XrTime lastUpdatedTime;
+} XrLightEstimateANDROID;
+
+// XrDirectionalLightANDROID extends XrLightEstimateANDROID
+typedef struct XrDirectionalLightANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrLightEstimateStateANDROID state;
+ XrVector3f intensity;
+ XrVector3f direction;
+} XrDirectionalLightANDROID;
+
+// XrAmbientLightANDROID extends XrLightEstimateANDROID
+typedef struct XrAmbientLightANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrLightEstimateStateANDROID state;
+ XrVector3f intensity;
+ XrVector3f colorCorrection;
+} XrAmbientLightANDROID;
+
+// XrSphericalHarmonicsANDROID extends XrLightEstimateANDROID
+typedef struct XrSphericalHarmonicsANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrLightEstimateStateANDROID state;
+ XrSphericalHarmonicsKindANDROID kind;
+ float coefficients[9][3];
+} XrSphericalHarmonicsANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateLightEstimatorANDROID)(XrSession session, XrLightEstimatorCreateInfoANDROID* createInfo, XrLightEstimatorANDROID* outHandle);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyLightEstimatorANDROID)(XrLightEstimatorANDROID estimator);
+typedef XrResult (XRAPI_PTR *PFN_xrGetLightEstimateANDROID)(XrLightEstimatorANDROID estimator, const XrLightEstimateGetInfoANDROID* input, XrLightEstimateANDROID* output);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateLightEstimatorANDROID(
+ XrSession session,
+ XrLightEstimatorCreateInfoANDROID* createInfo,
+ XrLightEstimatorANDROID* outHandle);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyLightEstimatorANDROID(
+ XrLightEstimatorANDROID estimator);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetLightEstimateANDROID(
+ XrLightEstimatorANDROID estimator,
+ const XrLightEstimateGetInfoANDROID* input,
+ XrLightEstimateANDROID* output);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_mouse_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_mouse_interaction 1
+#define XR_ANDROID_mouse_interaction_SPEC_VERSION 1
+#define XR_ANDROID_MOUSE_INTERACTION_EXTENSION_NAME "XR_ANDROID_mouse_interaction"
+
+
+// XR_ANDROID_trackables_marker is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_trackables_marker 1
+#define XR_ANDROID_trackables_marker_SPEC_VERSION 1
+#define XR_ANDROID_TRACKABLES_MARKER_EXTENSION_NAME "XR_ANDROID_trackables_marker"
+
+typedef enum XrTrackableMarkerTrackingModeANDROID {
+ XR_TRACKABLE_MARKER_TRACKING_MODE_DYNAMIC_ANDROID = 0,
+ XR_TRACKABLE_MARKER_TRACKING_MODE_STATIC_ANDROID = 1,
+ XR_TRACKABLE_MARKER_TRACKING_MODE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackableMarkerTrackingModeANDROID;
+
+typedef enum XrTrackableMarkerDictionaryANDROID {
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_50_ANDROID = 0,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_100_ANDROID = 1,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_250_ANDROID = 2,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_1000_ANDROID = 3,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_50_ANDROID = 4,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_100_ANDROID = 5,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_250_ANDROID = 6,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_1000_ANDROID = 7,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_50_ANDROID = 8,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_100_ANDROID = 9,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_250_ANDROID = 10,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_1000_ANDROID = 11,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_50_ANDROID = 12,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_100_ANDROID = 13,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_250_ANDROID = 14,
+ XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_1000_ANDROID = 15,
+ XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_16H5_ANDROID = 16,
+ XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_25H9_ANDROID = 17,
+ XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H10_ANDROID = 18,
+ XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H11_ANDROID = 19,
+ XR_TRACKABLE_MARKER_DICTIONARY_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackableMarkerDictionaryANDROID;
+// XrSystemMarkerTrackingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemMarkerTrackingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsMarkerTracking;
+ XrBool32 supportsMarkerSizeEstimation;
+ uint16_t maxMarkerCount;
+} XrSystemMarkerTrackingPropertiesANDROID;
+
+typedef struct XrTrackableMarkerDatabaseEntryANDROID {
+ int32_t id;
+ float edgeSize;
+} XrTrackableMarkerDatabaseEntryANDROID;
+
+typedef struct XrTrackableMarkerDatabaseANDROID {
+ XrTrackableMarkerDictionaryANDROID dictionary;
+ uint32_t entryCount;
+ const XrTrackableMarkerDatabaseEntryANDROID* entries;
+} XrTrackableMarkerDatabaseANDROID;
+
+// XrTrackableMarkerConfigurationANDROID extends XrTrackableTrackerCreateInfoANDROID
+typedef struct XrTrackableMarkerConfigurationANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTrackableMarkerTrackingModeANDROID trackingMode;
+ uint32_t databaseCount;
+ const XrTrackableMarkerDatabaseANDROID* databases;
+} XrTrackableMarkerConfigurationANDROID;
+
+typedef struct XrTrackableMarkerANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTrackingStateANDROID trackingState;
+ XrTime lastUpdatedTime;
+ XrTrackableMarkerDictionaryANDROID dictionary;
+ int32_t markerId;
+ XrPosef centerPose;
+ XrExtent2Df extents;
+} XrTrackableMarkerANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableMarkerANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableMarkerANDROID* markerOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableMarkerANDROID(
+ XrTrackableTrackerANDROID tracker,
+ const XrTrackableGetInfoANDROID* getInfo,
+ XrTrackableMarkerANDROID* markerOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_trackables_qr_code is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_trackables_qr_code 1
+#define XR_ANDROID_trackables_qr_code_SPEC_VERSION 1
+#define XR_ANDROID_TRACKABLES_QR_CODE_EXTENSION_NAME "XR_ANDROID_trackables_qr_code"
+
+typedef enum XrQrCodeTrackingModeANDROID {
+ XR_QR_CODE_TRACKING_MODE_DYNAMIC_ANDROID = 0,
+ XR_QR_CODE_TRACKING_MODE_STATIC_ANDROID = 1,
+ XR_QR_CODE_TRACKING_MODE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrQrCodeTrackingModeANDROID;
+// XrSystemQrCodeTrackingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemQrCodeTrackingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsQrCodeTracking;
+ XrBool32 supportsQrCodeSizeEstimation;
+ uint16_t maxQrCodeCount;
+} XrSystemQrCodeTrackingPropertiesANDROID;
+
+// XrTrackableQrCodeConfigurationANDROID extends XrTrackableTrackerCreateInfoANDROID
+typedef struct XrTrackableQrCodeConfigurationANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrQrCodeTrackingModeANDROID trackingMode;
+ float qrCodeEdgeSize;
+} XrTrackableQrCodeConfigurationANDROID;
+
+typedef struct XrTrackableQrCodeANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrTrackingStateANDROID trackingState;
+ XrTime lastUpdatedTime;
+ XrPosef centerPose;
+ XrExtent2Df extents;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ char* buffer;
+} XrTrackableQrCodeANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableQrCodeANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableQrCodeANDROID* qrCodeOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableQrCodeANDROID(
+ XrTrackableTrackerANDROID tracker,
+ const XrTrackableGetInfoANDROID* getInfo,
+ XrTrackableQrCodeANDROID* qrCodeOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_trackables_image is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_trackables_image 1
+XR_DEFINE_HANDLE(XrTrackableImageDatabaseANDROID)
+#define XR_ANDROID_trackables_image_SPEC_VERSION 1
+#define XR_ANDROID_TRACKABLES_IMAGE_EXTENSION_NAME "XR_ANDROID_trackables_image"
+
+typedef enum XrTrackableImageTrackingModeANDROID {
+ XR_TRACKABLE_IMAGE_TRACKING_MODE_DYNAMIC_ANDROID = 1,
+ XR_TRACKABLE_IMAGE_TRACKING_MODE_STATIC_ANDROID = 2,
+ XR_TRACKABLE_IMAGE_TRACKING_MODE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackableImageTrackingModeANDROID;
+
+typedef enum XrTrackableImageFormatANDROID {
+ XR_TRACKABLE_IMAGE_FORMAT_R8G8B8A8_ANDROID = 1,
+ XR_TRACKABLE_IMAGE_FORMAT_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrTrackableImageFormatANDROID;
+// XrSystemImageTrackingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemImageTrackingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsImageTracking;
+ XrBool32 supportsPhysicalSizeEstimation;
+ uint32_t maxTrackedImageCount;
+ uint32_t maxLoadedImageCount;
+} XrSystemImageTrackingPropertiesANDROID;
+
+typedef struct XrTrackableImageDatabaseEntryANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTrackableImageTrackingModeANDROID trackingMode;
+ float physicalWidth;
+ uint32_t imageWidth;
+ uint32_t imageHeight;
+ XrTrackableImageFormatANDROID format;
+ uint32_t bufferSize;
+ const uint8_t* buffer;
+} XrTrackableImageDatabaseEntryANDROID;
+
+typedef struct XrTrackableImageDatabaseCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t entryCount;
+ const XrTrackableImageDatabaseEntryANDROID* entries;
+} XrTrackableImageDatabaseCreateInfoANDROID;
+
+typedef struct XrCreateTrackableImageDatabaseCompletionANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrTrackableImageDatabaseANDROID database;
+} XrCreateTrackableImageDatabaseCompletionANDROID;
+
+typedef struct XrTrackableImageConfigurationANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t databaseCount;
+ const XrTrackableImageDatabaseANDROID* databases;
+} XrTrackableImageConfigurationANDROID;
+
+typedef struct XrTrackableImageANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTrackingStateANDROID trackingState;
+ XrTime lastUpdatedTime;
+ XrTrackableImageDatabaseANDROID database;
+ uint32_t databaseEntryIndex;
+ XrPosef centerPose;
+ XrExtent2Df extents;
+} XrTrackableImageANDROID;
+
+typedef struct XrEventDataImageTrackingLostANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrEventDataImageTrackingLostANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateTrackableImageDatabaseAsyncANDROID)(XrSession session, const XrTrackableImageDatabaseCreateInfoANDROID* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateTrackableImageDatabaseCompleteANDROID)(XrSession session, XrFutureEXT future, XrCreateTrackableImageDatabaseCompletionANDROID* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyTrackableImageDatabaseANDROID)(XrTrackableImageDatabaseANDROID database);
+typedef XrResult (XRAPI_PTR *PFN_xrAddTrackableImageDatabaseANDROID)(XrTrackableTrackerANDROID tracker, XrTrackableImageDatabaseANDROID database);
+typedef XrResult (XRAPI_PTR *PFN_xrRemoveTrackableImageDatabaseANDROID)(XrTrackableTrackerANDROID tracker, XrTrackableImageDatabaseANDROID database);
+typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableImageANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableImageANDROID* trackable);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateTrackableImageDatabaseAsyncANDROID(
+ XrSession session,
+ const XrTrackableImageDatabaseCreateInfoANDROID* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateTrackableImageDatabaseCompleteANDROID(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateTrackableImageDatabaseCompletionANDROID* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyTrackableImageDatabaseANDROID(
+ XrTrackableImageDatabaseANDROID database);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAddTrackableImageDatabaseANDROID(
+ XrTrackableTrackerANDROID tracker,
+ XrTrackableImageDatabaseANDROID database);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRemoveTrackableImageDatabaseANDROID(
+ XrTrackableTrackerANDROID tracker,
+ XrTrackableImageDatabaseANDROID database);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableImageANDROID(
+ XrTrackableTrackerANDROID tracker,
+ const XrTrackableGetInfoANDROID* getInfo,
+ XrTrackableImageANDROID* trackable);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_scene_meshing is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_scene_meshing 1
+XR_DEFINE_HANDLE(XrSceneMeshingTrackerANDROID)
+XR_DEFINE_HANDLE(XrSceneMeshSnapshotANDROID)
+#define XR_ANDROID_scene_meshing_SPEC_VERSION 3
+#define XR_ANDROID_SCENE_MESHING_EXTENSION_NAME "XR_ANDROID_scene_meshing"
+
+typedef enum XrSceneMeshSemanticLabelSetANDROID {
+ XR_SCENE_MESH_SEMANTIC_LABEL_SET_NONE_ANDROID = 0,
+ XR_SCENE_MESH_SEMANTIC_LABEL_SET_DEFAULT_ANDROID = 1,
+ XR_SCENE_MESH_SEMANTIC_LABEL_SET_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSceneMeshSemanticLabelSetANDROID;
+
+typedef enum XrSceneMeshTrackingStateANDROID {
+ XR_SCENE_MESH_TRACKING_STATE_INITIALIZING_ANDROID = 0,
+ XR_SCENE_MESH_TRACKING_STATE_TRACKING_ANDROID = 1,
+ XR_SCENE_MESH_TRACKING_STATE_WAITING_ANDROID = 2,
+ XR_SCENE_MESH_TRACKING_STATE_ERROR_ANDROID = 3,
+ XR_SCENE_MESH_TRACKING_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSceneMeshTrackingStateANDROID;
+
+typedef enum XrSceneMeshSemanticLabelANDROID {
+ XR_SCENE_MESH_SEMANTIC_LABEL_OTHER_ANDROID = 0,
+ XR_SCENE_MESH_SEMANTIC_LABEL_FLOOR_ANDROID = 1,
+ XR_SCENE_MESH_SEMANTIC_LABEL_CEILING_ANDROID = 2,
+ XR_SCENE_MESH_SEMANTIC_LABEL_WALL_ANDROID = 3,
+ XR_SCENE_MESH_SEMANTIC_LABEL_TABLE_ANDROID = 4,
+ XR_SCENE_MESH_SEMANTIC_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSceneMeshSemanticLabelANDROID;
+// XrSystemSceneMeshingPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemSceneMeshingPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsSceneMeshing;
+} XrSystemSceneMeshingPropertiesANDROID;
+
+typedef struct XrSceneMeshingTrackerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSceneMeshSemanticLabelSetANDROID semanticLabelSet;
+ XrBool32 enableNormals;
+} XrSceneMeshingTrackerCreateInfoANDROID;
+
+typedef struct XrSceneMeshSnapshotCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrBoxf boundingBox;
+} XrSceneMeshSnapshotCreateInfoANDROID;
+
+typedef struct XrSceneMeshSnapshotCreationResultANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSceneMeshSnapshotANDROID snapshot;
+ XrSceneMeshTrackingStateANDROID trackingState;
+} XrSceneMeshSnapshotCreationResultANDROID;
+
+typedef struct XrSceneSubmeshStateANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuid submeshId;
+ XrTime lastUpdatedTime;
+ XrPosef submeshPoseInBaseSpace;
+ XrExtent3Df bounds;
+} XrSceneSubmeshStateANDROID;
+
+typedef struct XrSceneSubmeshDataANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuid submeshId;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector3f* vertexPositions;
+ XrVector3f* vertexNormals;
+ uint8_t* vertexSemantics;
+ uint32_t indexCapacityInput;
+ uint32_t indexCountOutput;
+ uint32_t* indices;
+} XrSceneSubmeshDataANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedSemanticLabelSetsANDROID)(XrInstance instance, XrSystemId systemId, uint32_t supportedSemanticLabelSetsInputCapacity, uint32_t* supportedSemanticLabelSetsOutputCount, XrSceneMeshSemanticLabelSetANDROID* supportedSemanticLabelSets);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneMeshingTrackerANDROID)(XrSession session, const XrSceneMeshingTrackerCreateInfoANDROID* createInfo, XrSceneMeshingTrackerANDROID* tracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneMeshingTrackerANDROID)(XrSceneMeshingTrackerANDROID tracker);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSceneMeshSnapshotANDROID)(XrSceneMeshingTrackerANDROID tracker, const XrSceneMeshSnapshotCreateInfoANDROID* createInfo, XrSceneMeshSnapshotCreationResultANDROID* outSnapshotCreationResult);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySceneMeshSnapshotANDROID)(XrSceneMeshSnapshotANDROID snapshot);
+typedef XrResult (XRAPI_PTR *PFN_xrGetAllSubmeshStatesANDROID)(XrSceneMeshSnapshotANDROID snapshot, uint32_t submeshStateCapacityInput, uint32_t* submeshStateCountOutput, XrSceneSubmeshStateANDROID* submeshStates);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSubmeshDataANDROID)(XrSceneMeshSnapshotANDROID snapshot, uint32_t submeshDataCount, XrSceneSubmeshDataANDROID* inoutSubmeshData);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedSemanticLabelSetsANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t supportedSemanticLabelSetsInputCapacity,
+ uint32_t* supportedSemanticLabelSetsOutputCount,
+ XrSceneMeshSemanticLabelSetANDROID* supportedSemanticLabelSets);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneMeshingTrackerANDROID(
+ XrSession session,
+ const XrSceneMeshingTrackerCreateInfoANDROID* createInfo,
+ XrSceneMeshingTrackerANDROID* tracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneMeshingTrackerANDROID(
+ XrSceneMeshingTrackerANDROID tracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSceneMeshSnapshotANDROID(
+ XrSceneMeshingTrackerANDROID tracker,
+ const XrSceneMeshSnapshotCreateInfoANDROID* createInfo,
+ XrSceneMeshSnapshotCreationResultANDROID* outSnapshotCreationResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySceneMeshSnapshotANDROID(
+ XrSceneMeshSnapshotANDROID snapshot);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAllSubmeshStatesANDROID(
+ XrSceneMeshSnapshotANDROID snapshot,
+ uint32_t submeshStateCapacityInput,
+ uint32_t* submeshStateCountOutput,
+ XrSceneSubmeshStateANDROID* submeshStates);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSubmeshDataANDROID(
+ XrSceneMeshSnapshotANDROID snapshot,
+ uint32_t submeshDataCount,
+ XrSceneSubmeshDataANDROID* inoutSubmeshData);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_entity is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_entity 1
+
+#define XR_NULL_SPATIAL_ENTITY_ID_EXT 0
+
+
+#define XR_NULL_SPATIAL_BUFFER_ID_EXT 0
+
+XR_DEFINE_ATOM(XrSpatialEntityIdEXT)
+XR_DEFINE_ATOM(XrSpatialBufferIdEXT)
+XR_DEFINE_HANDLE(XrSpatialEntityEXT)
+XR_DEFINE_HANDLE(XrSpatialContextEXT)
+XR_DEFINE_HANDLE(XrSpatialSnapshotEXT)
+#define XR_EXT_spatial_entity_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_ENTITY_EXTENSION_NAME "XR_EXT_spatial_entity"
+
+typedef enum XrSpatialCapabilityEXT {
+ XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT = 1000741000,
+ XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT = 1000743000,
+ XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT = 1000743001,
+ XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT = 1000743002,
+ XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT = 1000743003,
+ XR_SPATIAL_CAPABILITY_ANCHOR_EXT = 1000762000,
+ XR_SPATIAL_CAPABILITY_IMAGE_TRACKING_EXT = 1000782000,
+ XR_SPATIAL_CAPABILITY_OBJECT_TRACKING_ANDROID = 1000785000,
+ XR_SPATIAL_CAPABILITY_DEPTH_RAYCAST_ANDROID = 1000786000,
+ XR_SPATIAL_CAPABILITY_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialCapabilityEXT;
+
+typedef enum XrSpatialCapabilityFeatureEXT {
+ XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_FIXED_SIZE_MARKERS_EXT = 1000743000,
+ XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_STATIC_MARKERS_EXT = 1000743001,
+ XR_SPATIAL_CAPABILITY_FEATURE_SPHERE_BOUNDS_FILTER_ANDROID = 1000761000,
+ XR_SPATIAL_CAPABILITY_FEATURE_BOX_BOUNDS_FILTER_ANDROID = 1000761001,
+ XR_SPATIAL_CAPABILITY_FEATURE_FRUSTUM_BOUNDS_FILTER_ANDROID = 1000761002,
+ XR_SPATIAL_CAPABILITY_FEATURE_IMAGE_TRACKING_AUTOMATIC_SIZE_IMAGES_EXT = 1000782000,
+ XR_SPATIAL_CAPABILITY_FEATURE_IMAGE_TRACKING_STATIC_IMAGES_EXT = 1000782001,
+ XR_SPATIAL_CAPABILITY_FEATURE_IMAGE_TRACKING_FIXED_SIZE_IMAGES_EXT = 1000782002,
+ XR_SPATIAL_CAPABILITY_FEATURE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialCapabilityFeatureEXT;
+
+typedef enum XrSpatialComponentTypeEXT {
+ XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT = 1,
+ XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT = 2,
+ XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT = 3,
+ XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT = 4,
+ XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT = 1000741000,
+ XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT = 1000741001,
+ XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT = 1000741002,
+ XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT = 1000741003,
+ XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT = 1000743000,
+ XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT = 1000762000,
+ XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT = 1000763000,
+ XR_SPATIAL_COMPONENT_TYPE_IMAGE_2D_EXT = 1000782000,
+ XR_SPATIAL_COMPONENT_TYPE_OBJECT_SEMANTIC_LABEL_ANDROID = 1000785000,
+ XR_SPATIAL_COMPONENT_TYPE_RAYCAST_RESULT_ANDROID = 1000786000,
+ XR_SPATIAL_COMPONENT_TYPE_SUBSUMED_BY_ANDROID = 1000791000,
+ XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialComponentTypeEXT;
+
+typedef enum XrSpatialEntityTrackingStateEXT {
+ XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT = 1,
+ XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT = 2,
+ XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT = 3,
+ XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialEntityTrackingStateEXT;
+
+typedef enum XrSpatialBufferTypeEXT {
+ XR_SPATIAL_BUFFER_TYPE_UNKNOWN_EXT = 0,
+ XR_SPATIAL_BUFFER_TYPE_STRING_EXT = 1,
+ XR_SPATIAL_BUFFER_TYPE_UINT8_EXT = 2,
+ XR_SPATIAL_BUFFER_TYPE_UINT16_EXT = 3,
+ XR_SPATIAL_BUFFER_TYPE_UINT32_EXT = 4,
+ XR_SPATIAL_BUFFER_TYPE_FLOAT_EXT = 5,
+ XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT = 6,
+ XR_SPATIAL_BUFFER_TYPE_VECTOR3F_EXT = 7,
+ XR_SPATIAL_BUFFER_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialBufferTypeEXT;
+typedef struct XrSpatialCapabilityComponentTypesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t componentTypeCapacityInput;
+ uint32_t componentTypeCountOutput;
+ XrSpatialComponentTypeEXT* componentTypes;
+} XrSpatialCapabilityComponentTypesEXT;
+
+typedef struct XR_MAY_ALIAS XrSpatialCapabilityConfigurationBaseHeaderEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationBaseHeaderEXT;
+
+typedef struct XrSpatialContextCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t capabilityConfigCount;
+ const XrSpatialCapabilityConfigurationBaseHeaderEXT* const* capabilityConfigs;
+} XrSpatialContextCreateInfoEXT;
+
+typedef struct XrCreateSpatialContextCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialContextEXT spatialContext;
+} XrCreateSpatialContextCompletionEXT;
+
+typedef struct XrSpatialDiscoverySnapshotCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t componentTypeCount;
+ const XrSpatialComponentTypeEXT* componentTypes;
+} XrSpatialDiscoverySnapshotCreateInfoEXT;
+
+typedef struct XrCreateSpatialDiscoverySnapshotCompletionInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrFutureEXT future;
+} XrCreateSpatialDiscoverySnapshotCompletionInfoEXT;
+
+typedef struct XrCreateSpatialDiscoverySnapshotCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialSnapshotEXT snapshot;
+} XrCreateSpatialDiscoverySnapshotCompletionEXT;
+
+typedef struct XrSpatialComponentDataQueryConditionEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t componentTypeCount;
+ const XrSpatialComponentTypeEXT* componentTypes;
+} XrSpatialComponentDataQueryConditionEXT;
+
+typedef struct XrSpatialComponentDataQueryResultEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t entityIdCapacityInput;
+ uint32_t entityIdCountOutput;
+ XrSpatialEntityIdEXT* entityIds;
+ uint32_t entityStateCapacityInput;
+ uint32_t entityStateCountOutput;
+ XrSpatialEntityTrackingStateEXT* entityStates;
+} XrSpatialComponentDataQueryResultEXT;
+
+typedef struct XrSpatialBufferEXT {
+ XrSpatialBufferIdEXT bufferId;
+ XrSpatialBufferTypeEXT bufferType;
+} XrSpatialBufferEXT;
+
+typedef struct XrSpatialBufferGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialBufferIdEXT bufferId;
+} XrSpatialBufferGetInfoEXT;
+
+typedef struct XrSpatialBounded2DDataEXT {
+ XrPosef center;
+ XrExtent2Df extents;
+} XrSpatialBounded2DDataEXT;
+
+// XrSpatialComponentBounded2DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentBounded2DListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t boundCount;
+ XrSpatialBounded2DDataEXT* bounds;
+} XrSpatialComponentBounded2DListEXT;
+
+// XrSpatialComponentBounded3DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentBounded3DListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t boundCount;
+ XrBoxf* bounds;
+} XrSpatialComponentBounded3DListEXT;
+
+// XrSpatialComponentParentListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentParentListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t parentCount;
+ XrSpatialEntityIdEXT* parents;
+} XrSpatialComponentParentListEXT;
+
+typedef struct XrSpatialMeshDataEXT {
+ XrPosef origin;
+ XrSpatialBufferEXT vertexBuffer;
+ XrSpatialBufferEXT indexBuffer;
+} XrSpatialMeshDataEXT;
+
+// XrSpatialComponentMesh3DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentMesh3DListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t meshCount;
+ XrSpatialMeshDataEXT* meshes;
+} XrSpatialComponentMesh3DListEXT;
+
+typedef struct XrSpatialEntityFromIdCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialEntityIdEXT entityId;
+} XrSpatialEntityFromIdCreateInfoEXT;
+
+typedef struct XrSpatialUpdateSnapshotCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t entityCount;
+ const XrSpatialEntityEXT* entities;
+ uint32_t componentTypeCount;
+ const XrSpatialComponentTypeEXT* componentTypes;
+ XrSpace baseSpace;
+ XrTime time;
+} XrSpatialUpdateSnapshotCreateInfoEXT;
+
+typedef struct XrEventDataSpatialDiscoveryRecommendedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContextEXT spatialContext;
+} XrEventDataSpatialDiscoveryRecommendedEXT;
+
+// XrSpatialFilterTrackingStateEXT extends XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT
+typedef struct XrSpatialFilterTrackingStateEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialEntityTrackingStateEXT trackingState;
+} XrSpatialFilterTrackingStateEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilitiesEXT)(XrInstance instance, XrSystemId systemId, uint32_t capabilityCapacityInput, uint32_t* capabilityCountOutput, XrSpatialCapabilityEXT* capabilities);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilityComponentTypesEXT)(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, XrSpatialCapabilityComponentTypesEXT* capabilityComponents);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilityFeaturesEXT)(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, uint32_t capabilityFeatureCapacityInput, uint32_t* capabilityFeatureCountOutput, XrSpatialCapabilityFeatureEXT* capabilityFeatures);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContextAsyncEXT)(XrSession session, const XrSpatialContextCreateInfoEXT* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContextCompleteEXT)(XrSession session, XrFutureEXT future, XrCreateSpatialContextCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialContextEXT)(XrSpatialContextEXT spatialContext);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialDiscoverySnapshotAsyncEXT)(XrSpatialContextEXT spatialContext, const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialDiscoverySnapshotCompleteEXT)(XrSpatialContextEXT spatialContext, const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT* createSnapshotCompletionInfo, XrCreateSpatialDiscoverySnapshotCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialComponentDataEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialComponentDataQueryConditionEXT* queryCondition, XrSpatialComponentDataQueryResultEXT* queryResult);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialSnapshotEXT)(XrSpatialSnapshotEXT snapshot);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialEntityFromIdEXT)(XrSpatialContextEXT spatialContext, const XrSpatialEntityFromIdCreateInfoEXT* createInfo, XrSpatialEntityEXT* spatialEntity);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialEntityEXT)(XrSpatialEntityEXT spatialEntity);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialUpdateSnapshotEXT)(XrSpatialContextEXT spatialContext, const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo, XrSpatialSnapshotEXT* snapshot);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferStringEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint8EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint8_t* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint16EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint16_t* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint32EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint32_t* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferFloatEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, float* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferVector2fEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, XrVector2f* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferVector3fEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, XrVector3f* buffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilitiesEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t capabilityCapacityInput,
+ uint32_t* capabilityCountOutput,
+ XrSpatialCapabilityEXT* capabilities);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilityComponentTypesEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrSpatialCapabilityEXT capability,
+ XrSpatialCapabilityComponentTypesEXT* capabilityComponents);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilityFeaturesEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrSpatialCapabilityEXT capability,
+ uint32_t capabilityFeatureCapacityInput,
+ uint32_t* capabilityFeatureCountOutput,
+ XrSpatialCapabilityFeatureEXT* capabilityFeatures);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextAsyncEXT(
+ XrSession session,
+ const XrSpatialContextCreateInfoEXT* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextCompleteEXT(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateSpatialContextCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialContextEXT(
+ XrSpatialContextEXT spatialContext);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialDiscoverySnapshotAsyncEXT(
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialDiscoverySnapshotCompleteEXT(
+ XrSpatialContextEXT spatialContext,
+ const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT* createSnapshotCompletionInfo,
+ XrCreateSpatialDiscoverySnapshotCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialComponentDataEXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialComponentDataQueryConditionEXT* queryCondition,
+ XrSpatialComponentDataQueryResultEXT* queryResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialSnapshotEXT(
+ XrSpatialSnapshotEXT snapshot);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityFromIdEXT(
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialEntityFromIdCreateInfoEXT* createInfo,
+ XrSpatialEntityEXT* spatialEntity);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialEntityEXT(
+ XrSpatialEntityEXT spatialEntity);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialUpdateSnapshotEXT(
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo,
+ XrSpatialSnapshotEXT* snapshot);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferStringEXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint8EXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ uint8_t* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint16EXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ uint16_t* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint32EXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ uint32_t* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferFloatEXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ float* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector2fEXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ XrVector2f* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector3fEXT(
+ XrSpatialSnapshotEXT snapshot,
+ const XrSpatialBufferGetInfoEXT* info,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ XrVector3f* buffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_plane_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_plane_tracking 1
+#define XR_EXT_spatial_plane_tracking_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_PLANE_TRACKING_EXTENSION_NAME "XR_EXT_spatial_plane_tracking"
+
+typedef enum XrSpatialPlaneAlignmentEXT {
+ XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT = 0,
+ XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT = 1,
+ XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT = 2,
+ XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT = 3,
+ XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialPlaneAlignmentEXT;
+
+typedef enum XrSpatialPlaneSemanticLabelEXT {
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT = 1,
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT = 2,
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT = 3,
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT = 4,
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT = 5,
+ XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialPlaneSemanticLabelEXT;
+typedef struct XrSpatialCapabilityConfigurationPlaneTrackingEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationPlaneTrackingEXT;
+
+// XrSpatialComponentPlaneAlignmentListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentPlaneAlignmentListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t planeAlignmentCount;
+ XrSpatialPlaneAlignmentEXT* planeAlignments;
+} XrSpatialComponentPlaneAlignmentListEXT;
+
+// XrSpatialComponentMesh2DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentMesh2DListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t meshCount;
+ XrSpatialMeshDataEXT* meshes;
+} XrSpatialComponentMesh2DListEXT;
+
+typedef struct XrSpatialPolygon2DDataEXT {
+ XrPosef origin;
+ XrSpatialBufferEXT vertexBuffer;
+} XrSpatialPolygon2DDataEXT;
+
+// XrSpatialComponentPolygon2DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentPolygon2DListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t polygonCount;
+ XrSpatialPolygon2DDataEXT* polygons;
+} XrSpatialComponentPolygon2DListEXT;
+
+// XrSpatialComponentPlaneSemanticLabelListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentPlaneSemanticLabelListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t semanticLabelCount;
+ XrSpatialPlaneSemanticLabelEXT* semanticLabels;
+} XrSpatialComponentPlaneSemanticLabelListEXT;
+
+
+
+// XR_EXT_stationary_reference_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_stationary_reference_space 1
+#define XR_EXT_stationary_reference_space_SPEC_VERSION 1
+#define XR_EXT_STATIONARY_REFERENCE_SPACE_EXTENSION_NAME "XR_EXT_stationary_reference_space"
+typedef struct XrStationaryReferenceSpaceGenerationIdGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrStationaryReferenceSpaceGenerationIdGetInfoEXT;
+
+typedef struct XrStationaryReferenceSpaceGenerationIdResultEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuid generationId;
+} XrStationaryReferenceSpaceGenerationIdResultEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetStationaryReferenceSpaceGenerationIdEXT)(XrSession session, const XrStationaryReferenceSpaceGenerationIdGetInfoEXT* getInfo, XrStationaryReferenceSpaceGenerationIdResultEXT* generationIdResult);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetStationaryReferenceSpaceGenerationIdEXT(
+ XrSession session,
+ const XrStationaryReferenceSpaceGenerationIdGetInfoEXT* getInfo,
+ XrStationaryReferenceSpaceGenerationIdResultEXT* generationIdResult);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_marker_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_marker_tracking 1
+#define XR_EXT_spatial_marker_tracking_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_MARKER_TRACKING_EXTENSION_NAME "XR_EXT_spatial_marker_tracking"
+
+typedef enum XrSpatialMarkerArucoDictEXT {
+ XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT = 1,
+ XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT = 2,
+ XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT = 3,
+ XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT = 4,
+ XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT = 5,
+ XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT = 6,
+ XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT = 7,
+ XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT = 8,
+ XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT = 9,
+ XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT = 10,
+ XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT = 11,
+ XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT = 12,
+ XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT = 13,
+ XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT = 14,
+ XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT = 15,
+ XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT = 16,
+ XR_SPATIAL_MARKER_ARUCO_DICT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialMarkerArucoDictEXT;
+
+typedef enum XrSpatialMarkerAprilTagDictEXT {
+ XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT = 1,
+ XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT = 2,
+ XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT = 3,
+ XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT = 4,
+ XR_SPATIAL_MARKER_APRIL_TAG_DICT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialMarkerAprilTagDictEXT;
+typedef struct XrSpatialCapabilityConfigurationQrCodeEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationQrCodeEXT;
+
+typedef struct XrSpatialCapabilityConfigurationMicroQrCodeEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationMicroQrCodeEXT;
+
+typedef struct XrSpatialCapabilityConfigurationArucoMarkerEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+ XrSpatialMarkerArucoDictEXT arUcoDict;
+} XrSpatialCapabilityConfigurationArucoMarkerEXT;
+
+typedef struct XrSpatialCapabilityConfigurationAprilTagEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+ XrSpatialMarkerAprilTagDictEXT aprilDict;
+} XrSpatialCapabilityConfigurationAprilTagEXT;
+
+// XrSpatialMarkerSizeEXT extends XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT
+typedef struct XrSpatialMarkerSizeEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float markerSideLength;
+} XrSpatialMarkerSizeEXT;
+
+// XrSpatialMarkerStaticOptimizationEXT extends XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT
+typedef struct XrSpatialMarkerStaticOptimizationEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 optimizeForStaticMarker;
+} XrSpatialMarkerStaticOptimizationEXT;
+
+typedef struct XrSpatialMarkerDataEXT {
+ XrSpatialCapabilityEXT capability;
+ uint32_t markerId;
+ XrSpatialBufferEXT data;
+} XrSpatialMarkerDataEXT;
+
+// XrSpatialComponentMarkerListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentMarkerListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t markerCount;
+ XrSpatialMarkerDataEXT* markers;
+} XrSpatialComponentMarkerListEXT;
+
+
+
+// XR_LOGITECH_mx_ink_stylus_interaction is a preprocessor guard. Do not pass it to API calls.
+#define XR_LOGITECH_mx_ink_stylus_interaction 1
+#define XR_LOGITECH_mx_ink_stylus_interaction_SPEC_VERSION 1
+#define XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME "XR_LOGITECH_mx_ink_stylus_interaction"
+
+
+// XR_BD_dynamic_object_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_dynamic_object_tracking 1
+#define XR_BD_dynamic_object_tracking_SPEC_VERSION 1
+#define XR_BD_DYNAMIC_OBJECT_TRACKING_EXTENSION_NAME "XR_BD_dynamic_object_tracking"
+
+typedef enum XrDynamicObjectTypeBD {
+ XR_DYNAMIC_OBJECT_TYPE_UNKNOWN_BD = 0,
+ XR_DYNAMIC_OBJECT_TYPE_KEYBOARD_BD = 1000747000,
+ XR_DYNAMIC_OBJECT_TYPE_MOUSE_BD = 1000748000,
+ XR_DYNAMIC_OBJECT_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrDynamicObjectTypeBD;
+// XrSystemDynamicObjectTrackingPropertiesBD extends XrSystemProperties
+typedef struct XrSystemDynamicObjectTrackingPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsDynamicObjectTracking;
+} XrSystemDynamicObjectTrackingPropertiesBD;
+
+// XrSenseDataProviderCreateInfoDynamicObjectBD extends XrSenseDataProviderCreateInfoBD
+typedef struct XrSenseDataProviderCreateInfoDynamicObjectBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t trackingTypeCount;
+ const XrDynamicObjectTypeBD* trackingTypes;
+} XrSenseDataProviderCreateInfoDynamicObjectBD;
+
+typedef struct XrDynamicObjectDataBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrDynamicObjectTypeBD objectType;
+} XrDynamicObjectDataBD;
+
+// XrSpatialEntityComponentDataDynamicObjectBD extends XrSpatialEntityComponentDataBaseHeaderBD
+typedef struct XrSpatialEntityComponentDataDynamicObjectBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrDynamicObjectDataBD data;
+} XrSpatialEntityComponentDataDynamicObjectBD;
+
+// XrSenseDataFilterDynamicObjectTypeBD extends XrSenseDataQueryInfoBD
+typedef struct XrSenseDataFilterDynamicObjectTypeBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t typeCount;
+ const XrDynamicObjectTypeBD* types;
+} XrSenseDataFilterDynamicObjectTypeBD;
+
+
+
+// XR_BD_dynamic_object_keyboard is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_dynamic_object_keyboard 1
+#define XR_BD_dynamic_object_keyboard_SPEC_VERSION 1
+#define XR_BD_DYNAMIC_OBJECT_KEYBOARD_EXTENSION_NAME "XR_BD_dynamic_object_keyboard"
+// XrSystemDynamicObjectKeyboardPropertiesBD extends XrSystemProperties
+typedef struct XrSystemDynamicObjectKeyboardPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsDynamicObjectKeyboard;
+} XrSystemDynamicObjectKeyboardPropertiesBD;
+
+
+
+// XR_BD_dynamic_object_mouse is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_dynamic_object_mouse 1
+#define XR_BD_dynamic_object_mouse_SPEC_VERSION 1
+#define XR_BD_DYNAMIC_OBJECT_MOUSE_EXTENSION_NAME "XR_BD_dynamic_object_mouse"
+// XrSystemDynamicObjectMousePropertiesBD extends XrSystemProperties
+typedef struct XrSystemDynamicObjectMousePropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsDynamicObjectMouse;
+} XrSystemDynamicObjectMousePropertiesBD;
+
+
+
+// XR_BD_camera_image is a preprocessor guard. Do not pass it to API calls.
+#define XR_BD_camera_image 1
+XR_DEFINE_ATOM(XrCameraIdBD)
+XR_DEFINE_ATOM(XrCameraImageIdBD)
+XR_DEFINE_HANDLE(XrCameraDeviceBD)
+XR_DEFINE_HANDLE(XrCameraCaptureSessionBD)
+#define XR_BD_camera_image_SPEC_VERSION 1
+#define XR_BD_CAMERA_IMAGE_EXTENSION_NAME "XR_BD_camera_image"
+
+typedef enum XrCameraPropertyTypeBD {
+ XR_CAMERA_PROPERTY_TYPE_FACING_BD = 1,
+ XR_CAMERA_PROPERTY_TYPE_POSITION_BD = 2,
+ XR_CAMERA_PROPERTY_TYPE_CAMERA_TYPE_BD = 3,
+ XR_CAMERA_PROPERTY_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraPropertyTypeBD;
+
+typedef enum XrCameraFacingBD {
+ XR_CAMERA_FACING_WORLD_BD = 1,
+ XR_CAMERA_FACING_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraFacingBD;
+
+typedef enum XrCameraPositionBD {
+ XR_CAMERA_POSITION_UNSPECIFIED_BD = 1,
+ XR_CAMERA_POSITION_LEFT_BD = 2,
+ XR_CAMERA_POSITION_RIGHT_BD = 3,
+ XR_CAMERA_POSITION_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraPositionBD;
+
+typedef enum XrCameraTypeBD {
+ XR_CAMERA_TYPE_PASSTHROUGH_COLOR_BD = 1,
+ XR_CAMERA_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraTypeBD;
+
+typedef enum XrCameraCapabilityTypeBD {
+ XR_CAMERA_CAPABILITY_TYPE_IMAGE_RESOLUTION_AND_FRAME_RATE_BD = 1,
+ XR_CAMERA_CAPABILITY_TYPE_IMAGE_FORMAT_BD = 2,
+ XR_CAMERA_CAPABILITY_TYPE_DATA_TRANSFER_TYPE_BD = 3,
+ XR_CAMERA_CAPABILITY_TYPE_CAMERA_MODEL_BD = 4,
+ XR_CAMERA_CAPABILITY_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraCapabilityTypeBD;
+
+typedef enum XrCameraDataTransferTypeBD {
+ XR_CAMERA_DATA_TRANSFER_TYPE_RAW_BUFFER_BD = 1,
+ XR_CAMERA_DATA_TRANSFER_TYPE_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraDataTransferTypeBD;
+
+typedef enum XrCameraImageFormatBD {
+ XR_CAMERA_IMAGE_FORMAT_RGBA_8888_BD = 1,
+ XR_CAMERA_IMAGE_FORMAT_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraImageFormatBD;
+
+typedef enum XrCameraModelBD {
+ XR_CAMERA_MODEL_PINHOLE_BD = 1,
+ XR_CAMERA_MODEL_MAX_ENUM_BD = 0x7FFFFFFF
+} XrCameraModelBD;
+typedef struct XR_MAY_ALIAS XrCameraPropertyBaseHeaderBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrCameraPropertyBaseHeaderBD;
+
+typedef struct XrCameraPropertiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t propertyCount;
+ XrCameraPropertyBaseHeaderBD** properties;
+} XrCameraPropertiesBD;
+
+typedef struct XR_MAY_ALIAS XrCameraCapabilityBaseHeaderBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrCameraCapabilityBaseHeaderBD;
+
+typedef struct XrCameraCapabilitiesBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t capabilityCount;
+ XrCameraCapabilityBaseHeaderBD** capabilities;
+} XrCameraCapabilitiesBD;
+
+typedef struct XrAvailableCamerasEnumerateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const XrCameraPropertiesBD* properties;
+ const XrCameraCapabilitiesBD* capabilities;
+} XrAvailableCamerasEnumerateInfoBD;
+
+typedef struct XrAvailableCameraBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrCameraIdBD cameraId;
+} XrAvailableCameraBD;
+
+typedef struct XrCameraPropertyTypesEnumerateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraIdBD cameraId;
+} XrCameraPropertyTypesEnumerateInfoBD;
+
+typedef struct XrCameraPropertyTypesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t propertyTypeCapacityInput;
+ uint32_t propertyTypeCountOutput;
+ XrCameraPropertyTypeBD* propertyTypes;
+} XrCameraPropertyTypesBD;
+
+typedef struct XrCameraPropertiesGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraIdBD cameraId;
+} XrCameraPropertiesGetInfoBD;
+
+typedef struct XrCameraPropertyFacingBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraFacingBD facing;
+} XrCameraPropertyFacingBD;
+
+typedef struct XrCameraPropertyPositionBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraPositionBD position;
+} XrCameraPropertyPositionBD;
+
+typedef struct XrCameraPropertyCameraTypeBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraTypeBD cameraType;
+} XrCameraPropertyCameraTypeBD;
+
+typedef struct XrCameraCapabilityTypesEnumerateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraIdBD cameraId;
+} XrCameraCapabilityTypesEnumerateInfoBD;
+
+typedef struct XrCameraCapabilityTypesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t capabilityTypeCapacityInput;
+ uint32_t capabilityTypeCountOutput;
+ XrCameraCapabilityTypeBD* capabilityTypes;
+} XrCameraCapabilityTypesBD;
+
+typedef struct XrCameraSupportedCapabilitiesGetInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraIdBD id;
+} XrCameraSupportedCapabilitiesGetInfoBD;
+
+typedef struct XR_MAY_ALIAS XrCameraSupportedCapabilityBaseHeaderBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrCameraSupportedCapabilityBaseHeaderBD;
+
+typedef struct XrCameraSupportedCapabilitiesBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t capabilityCount;
+ XrCameraSupportedCapabilityBaseHeaderBD** capabilities;
+} XrCameraSupportedCapabilitiesBD;
+
+typedef struct XrCameraImageResolutionAndFrameRateBD {
+ XrExtent2Di resolution;
+ uint32_t frameRate;
+} XrCameraImageResolutionAndFrameRateBD;
+
+typedef struct XrCameraSupportedCapabilityImageResolutionAndFrameRateBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resolutionAndFrameRateCapacityInput;
+ uint32_t resolutionAndFrameRateCountOutput;
+ XrCameraImageResolutionAndFrameRateBD* resolutionAndFrameRates;
+} XrCameraSupportedCapabilityImageResolutionAndFrameRateBD;
+
+typedef struct XrCameraCapabilityImageResolutionAndFrameRateBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrExtent2Di resolution;
+ uint32_t frameRate;
+} XrCameraCapabilityImageResolutionAndFrameRateBD;
+
+typedef struct XrCameraSupportedCapabilityDataTransferTypeBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t transferTypeCapacityInput;
+ uint32_t transferTypeCountOutput;
+ XrCameraDataTransferTypeBD* transferTypes;
+} XrCameraSupportedCapabilityDataTransferTypeBD;
+
+typedef struct XrCameraCapabilityDataTransferTypeBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraDataTransferTypeBD transferType;
+} XrCameraCapabilityDataTransferTypeBD;
+
+typedef struct XrCameraSupportedCapabilityImageFormatBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t formatCapacityInput;
+ uint32_t formatCountOutput;
+ XrCameraImageFormatBD* formats;
+} XrCameraSupportedCapabilityImageFormatBD;
+
+typedef struct XrCameraCapabilityImageFormatBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraImageFormatBD format;
+} XrCameraCapabilityImageFormatBD;
+
+typedef struct XrCameraSupportedCapabilityCameraModelBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t modelCapacityInput;
+ uint32_t modelCountOutput;
+ XrCameraModelBD* models;
+} XrCameraSupportedCapabilityCameraModelBD;
+
+typedef struct XrCameraCapabilityCameraModelBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraModelBD model;
+} XrCameraCapabilityCameraModelBD;
+
+typedef struct XrCameraDeviceCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraIdBD cameraId;
+} XrCameraDeviceCreateInfoBD;
+
+typedef struct XrCreateCameraDeviceCompletionBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrCameraDeviceBD device;
+} XrCreateCameraDeviceCompletionBD;
+
+typedef struct XrCameraCaptureSessionCreateInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCameraDeviceBD camera;
+ uint32_t configCount;
+ const XrCameraCapabilityBaseHeaderBD* const* configs;
+} XrCameraCaptureSessionCreateInfoBD;
+
+typedef struct XrCreateCameraCaptureSessionCompletionBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrCameraCaptureSessionBD captureSession;
+} XrCreateCameraCaptureSessionCompletionBD;
+
+// XrCameraIntrinsicsBD extends XrCameraImageDataBaseHeaderBD
+typedef struct XrCameraIntrinsicsBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVector2f focalLength;
+ XrVector2f principalPoint;
+ XrVector2f fov;
+} XrCameraIntrinsicsBD;
+
+// XrCameraExtrinsicsBD extends XrCameraImageDataBaseHeaderBD
+typedef struct XrCameraExtrinsicsBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPosef pose;
+} XrCameraExtrinsicsBD;
+
+typedef struct XrCameraCaptureBeginInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrCameraCaptureBeginInfoBD;
+
+typedef struct XrCameraImageAcquireInfoBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime lastCaptureTime;
+} XrCameraImageAcquireInfoBD;
+
+typedef struct XrCameraImageBD {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 available;
+ XrTime captureTime;
+ XrCameraImageIdBD imageId;
+} XrCameraImageBD;
+
+typedef struct XR_MAY_ALIAS XrCameraImageDataBaseHeaderBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+} XrCameraImageDataBaseHeaderBD;
+
+typedef struct XrCameraImageDataRawBufferBD {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t bytesPerPixel;
+ uint32_t pixelStride;
+ uint32_t bufferSize;
+ uint8_t* buffer;
+} XrCameraImageDataRawBufferBD;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateAvailableCamerasBD)(XrInstance instance, const XrAvailableCamerasEnumerateInfoBD* enumerateInfo, uint32_t cameraCapacityInput, uint32_t* cameraCountOutput, XrAvailableCameraBD* cameras);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateCameraPropertyTypesBD)(XrInstance instance, const XrCameraPropertyTypesEnumerateInfoBD* enumerateInfo, XrCameraPropertyTypesBD* propertyTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrGetCameraPropertiesBD)(XrInstance instance, const XrCameraPropertiesGetInfoBD* getInfo, XrCameraPropertiesBD* properties);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateCameraCapabilityTypesBD)(XrInstance instance, const XrCameraCapabilityTypesEnumerateInfoBD* enumerateInfo, XrCameraCapabilityTypesBD* capabilityTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrGetCameraSupportedCapabilitiesBD)(XrInstance instance, const XrCameraSupportedCapabilitiesGetInfoBD* getInfo, XrCameraSupportedCapabilitiesBD* capabilities);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraDeviceAsyncBD)(XrInstance instance, const XrCameraDeviceCreateInfoBD* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraDeviceCompleteBD)(XrInstance instance, XrFutureEXT future, XrCreateCameraDeviceCompletionBD* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyCameraDeviceBD)(XrCameraDeviceBD device);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraCaptureSessionAsyncBD)(XrSession session, const XrCameraCaptureSessionCreateInfoBD* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraCaptureSessionCompleteBD)(XrSession session, XrFutureEXT future, XrCreateCameraCaptureSessionCompletionBD* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyCameraCaptureSessionBD)(XrCameraCaptureSessionBD captureSession);
+typedef XrResult (XRAPI_PTR *PFN_xrBeginCameraCaptureBD)(XrCameraCaptureSessionBD captureSession, const XrCameraCaptureBeginInfoBD* beginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEndCameraCaptureBD)(XrCameraCaptureSessionBD captureSession);
+typedef XrResult (XRAPI_PTR *PFN_xrAcquireCameraImageBD)(XrCameraCaptureSessionBD captureSession, const XrCameraImageAcquireInfoBD* acquireInfo, XrCameraImageBD* image);
+typedef XrResult (XRAPI_PTR *PFN_xrGetCameraImageDataBD)(XrCameraCaptureSessionBD captureSession, XrCameraImageIdBD imageId, XrCameraImageDataBaseHeaderBD* imageData);
+typedef XrResult (XRAPI_PTR *PFN_xrReleaseCameraImageBD)(XrCameraCaptureSessionBD captureSession, XrCameraImageIdBD imageId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateAvailableCamerasBD(
+ XrInstance instance,
+ const XrAvailableCamerasEnumerateInfoBD* enumerateInfo,
+ uint32_t cameraCapacityInput,
+ uint32_t* cameraCountOutput,
+ XrAvailableCameraBD* cameras);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateCameraPropertyTypesBD(
+ XrInstance instance,
+ const XrCameraPropertyTypesEnumerateInfoBD* enumerateInfo,
+ XrCameraPropertyTypesBD* propertyTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraPropertiesBD(
+ XrInstance instance,
+ const XrCameraPropertiesGetInfoBD* getInfo,
+ XrCameraPropertiesBD* properties);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateCameraCapabilityTypesBD(
+ XrInstance instance,
+ const XrCameraCapabilityTypesEnumerateInfoBD* enumerateInfo,
+ XrCameraCapabilityTypesBD* capabilityTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraSupportedCapabilitiesBD(
+ XrInstance instance,
+ const XrCameraSupportedCapabilitiesGetInfoBD* getInfo,
+ XrCameraSupportedCapabilitiesBD* capabilities);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraDeviceAsyncBD(
+ XrInstance instance,
+ const XrCameraDeviceCreateInfoBD* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraDeviceCompleteBD(
+ XrInstance instance,
+ XrFutureEXT future,
+ XrCreateCameraDeviceCompletionBD* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyCameraDeviceBD(
+ XrCameraDeviceBD device);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraCaptureSessionAsyncBD(
+ XrSession session,
+ const XrCameraCaptureSessionCreateInfoBD* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraCaptureSessionCompleteBD(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateCameraCaptureSessionCompletionBD* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyCameraCaptureSessionBD(
+ XrCameraCaptureSessionBD captureSession);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginCameraCaptureBD(
+ XrCameraCaptureSessionBD captureSession,
+ const XrCameraCaptureBeginInfoBD* beginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEndCameraCaptureBD(
+ XrCameraCaptureSessionBD captureSession);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAcquireCameraImageBD(
+ XrCameraCaptureSessionBD captureSession,
+ const XrCameraImageAcquireInfoBD* acquireInfo,
+ XrCameraImageBD* image);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraImageDataBD(
+ XrCameraCaptureSessionBD captureSession,
+ XrCameraImageIdBD imageId,
+ XrCameraImageDataBaseHeaderBD* imageData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrReleaseCameraImageBD(
+ XrCameraCaptureSessionBD captureSession,
+ XrCameraImageIdBD imageId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_spatial_discovery_bounds is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_discovery_bounds 1
+#define XR_ANDROID_spatial_discovery_bounds_SPEC_VERSION 1
+#define XR_ANDROID_SPATIAL_DISCOVERY_BOUNDS_EXTENSION_NAME "XR_ANDROID_spatial_discovery_bounds"
+// XrSpatialBoundsSpherefANDROID extends XrSpatialDiscoverySnapshotCreateInfoEXT
+typedef struct XrSpatialBoundsSpherefANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrSpheref sphere;
+} XrSpatialBoundsSpherefANDROID;
+
+// XrSpatialBoundsBoxfANDROID extends XrSpatialDiscoverySnapshotCreateInfoEXT
+typedef struct XrSpatialBoundsBoxfANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrBoxf box;
+} XrSpatialBoundsBoxfANDROID;
+
+// XrSpatialBoundsFrustumfANDROID extends XrSpatialDiscoverySnapshotCreateInfoEXT
+typedef struct XrSpatialBoundsFrustumfANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrFrustumf frustum;
+} XrSpatialBoundsFrustumfANDROID;
+
+
+
+// XR_EXT_spatial_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_anchor 1
+#define XR_EXT_spatial_anchor_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_ANCHOR_EXTENSION_NAME "XR_EXT_spatial_anchor"
+typedef struct XrSpatialCapabilityConfigurationAnchorEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationAnchorEXT;
+
+// XrSpatialComponentAnchorListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentAnchorListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrPosef* locations;
+} XrSpatialComponentAnchorListEXT;
+
+typedef struct XrSpatialAnchorCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrPosef pose;
+} XrSpatialAnchorCreateInfoEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorEXT)(XrSpatialContextEXT spatialContext, const XrSpatialAnchorCreateInfoEXT* createInfo, XrSpatialEntityIdEXT* anchorEntityId, XrSpatialEntityEXT* anchorEntity);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorEXT(
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialAnchorCreateInfoEXT* createInfo,
+ XrSpatialEntityIdEXT* anchorEntityId,
+ XrSpatialEntityEXT* anchorEntity);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_persistence is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_persistence 1
+XR_DEFINE_HANDLE(XrSpatialPersistenceContextEXT)
+#define XR_EXT_spatial_persistence_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_PERSISTENCE_EXTENSION_NAME "XR_EXT_spatial_persistence"
+
+typedef enum XrSpatialPersistenceScopeEXT {
+ XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT = 1,
+ XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT = 1000781000,
+ XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialPersistenceScopeEXT;
+
+typedef enum XrSpatialPersistenceContextResultEXT {
+ XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT = 0,
+ XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT = -1000781001,
+ XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT = -1000781002,
+ XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialPersistenceContextResultEXT;
+
+typedef enum XrSpatialPersistenceStateEXT {
+ XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT = 1,
+ XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT = 2,
+ XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialPersistenceStateEXT;
+typedef struct XrSpatialPersistenceContextCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialPersistenceScopeEXT scope;
+} XrSpatialPersistenceContextCreateInfoEXT;
+
+typedef struct XrCreateSpatialPersistenceContextCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialPersistenceContextResultEXT createResult;
+ XrSpatialPersistenceContextEXT persistenceContext;
+} XrCreateSpatialPersistenceContextCompletionEXT;
+
+// XrSpatialContextPersistenceConfigEXT extends XrSpatialContextCreateInfoEXT
+typedef struct XrSpatialContextPersistenceConfigEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t persistenceContextCount;
+ const XrSpatialPersistenceContextEXT* persistenceContexts;
+} XrSpatialContextPersistenceConfigEXT;
+
+// XrSpatialDiscoveryPersistenceUuidFilterEXT extends XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT
+typedef struct XrSpatialDiscoveryPersistenceUuidFilterEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t persistedUuidCount;
+ const XrUuid* persistedUuids;
+} XrSpatialDiscoveryPersistenceUuidFilterEXT;
+
+typedef struct XrSpatialPersistenceDataEXT {
+ XrUuid persistUuid;
+ XrSpatialPersistenceStateEXT persistState;
+} XrSpatialPersistenceDataEXT;
+
+// XrSpatialComponentPersistenceListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentPersistenceListEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t persistDataCount;
+ XrSpatialPersistenceDataEXT* persistData;
+} XrSpatialComponentPersistenceListEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialPersistenceScopesEXT)(XrInstance instance, XrSystemId systemId, uint32_t persistenceScopeCapacityInput, uint32_t* persistenceScopeCountOutput, XrSpatialPersistenceScopeEXT* persistenceScopes);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialPersistenceContextAsyncEXT)(XrSession session, const XrSpatialPersistenceContextCreateInfoEXT* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialPersistenceContextCompleteEXT)(XrSession session, XrFutureEXT future, XrCreateSpatialPersistenceContextCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialPersistenceContextEXT)(XrSpatialPersistenceContextEXT persistenceContext);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialPersistenceScopesEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t persistenceScopeCapacityInput,
+ uint32_t* persistenceScopeCountOutput,
+ XrSpatialPersistenceScopeEXT* persistenceScopes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialPersistenceContextAsyncEXT(
+ XrSession session,
+ const XrSpatialPersistenceContextCreateInfoEXT* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialPersistenceContextCompleteEXT(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateSpatialPersistenceContextCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialPersistenceContextEXT(
+ XrSpatialPersistenceContextEXT persistenceContext);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_haptic_parametric is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_haptic_parametric 1
+
+#define XR_HAPTIC_PARAMETRIC_MAX_POINTS_TRANSIENTS_EXT 500
+
+
+#define XR_HAPTIC_PARAMETRIC_VIBRATION_EXTEND_DURATION_EXT 50000000
+
+
+#define XR_HAPTIC_PARAMETRIC_FREQUENCY_MIN_HZ_EXT 1
+
+
+#define XR_HAPTIC_PARAMETRIC_FREQUENCY_MAX_HZ_EXT 1000
+
+#define XR_EXT_haptic_parametric_SPEC_VERSION 1
+#define XR_EXT_HAPTIC_PARAMETRIC_EXTENSION_NAME "XR_EXT_haptic_parametric"
+
+typedef enum XrHapticParametricStreamFrameTypeEXT {
+ XR_HAPTIC_PARAMETRIC_STREAM_FRAME_TYPE_NONE_EXT = 0,
+ XR_HAPTIC_PARAMETRIC_STREAM_FRAME_TYPE_FIRST_FRAME_EXT = 1,
+ XR_HAPTIC_PARAMETRIC_STREAM_FRAME_TYPE_INTERMEDIATE_FRAME_EXT = 2,
+ XR_HAPTIC_PARAMETRIC_STREAM_FRAME_TYPE_LAST_FRAME_EXT = 3,
+ XR_HAPTIC_PARAMETRIC_STREAM_FRAME_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHapticParametricStreamFrameTypeEXT;
+typedef struct XrHapticParametricPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrDuration idealFrameSubmissionRate;
+ XrDuration minimumFirstFrameDuration;
+ float minFrequencyHz;
+ float maxFrequencyHz;
+} XrHapticParametricPropertiesEXT;
+
+typedef struct XrHapticParametricPointEXT {
+ XrDuration time;
+ float value;
+} XrHapticParametricPointEXT;
+
+typedef struct XrHapticParametricTransientEXT {
+ XrDuration time;
+ float amplitude;
+ float frequency;
+} XrHapticParametricTransientEXT;
+
+typedef struct XrHapticParametricVibrationEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t amplitudePointCount;
+ const XrHapticParametricPointEXT* amplitudePoints;
+ uint32_t frequencyPointCount;
+ const XrHapticParametricPointEXT* frequencyPoints;
+ uint32_t transientCount;
+ const XrHapticParametricTransientEXT* transients;
+ float minFrequencyHz;
+ float maxFrequencyHz;
+ XrHapticParametricStreamFrameTypeEXT streamFrameType;
+} XrHapticParametricVibrationEXT;
+
+// XrSystemHapticParametricPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemHapticParametricPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsParametricHaptics;
+} XrSystemHapticParametricPropertiesEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrHapticParametricGetPropertiesEXT)(XrSession session, const XrHapticActionInfo* hapticActionInfo, XrHapticParametricPropertiesEXT* parametricProperties);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrHapticParametricGetPropertiesEXT(
+ XrSession session,
+ const XrHapticActionInfo* hapticActionInfo,
+ XrHapticParametricPropertiesEXT* parametricProperties);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_SONY_swapchain_color_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_SONY_swapchain_color_space 1
+#define XR_SONY_swapchain_color_space_SPEC_VERSION 1
+#define XR_SONY_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "XR_SONY_swapchain_color_space"
+
+typedef enum XrColorSpaceSONY {
+ XR_COLOR_SPACE_SRGB_NONLINEAR_SONY = 0,
+ XR_COLOR_SPACE_DISPLAY_P3_LINEAR_SONY = 1,
+ XR_COLOR_SPACE_DISPLAY_P3_NONLINEAR_SONY = 2,
+ XR_COLOR_SPACE_DCI_P3_LINEAR_SONY = 3,
+ XR_COLOR_SPACE_DCI_P3_NONLINEAR_SONY = 4,
+ XR_COLOR_SPACE_EXTENDED_SRGB_LINEAR_SONY = 5,
+ XR_COLOR_SPACE_BT709_LINEAR_SONY = 6,
+ XR_COLOR_SPACE_BT709_NONLINEAR_SONY = 7,
+ XR_COLOR_SPACE_BT2020_LINEAR_SONY = 8,
+ XR_COLOR_SPACE_BT2020_PQ_SONY = 9,
+ XR_COLOR_SPACE_BT2020_HLG_SONY = 10,
+ XR_COLOR_SPACE_MAX_ENUM_SONY = 0x7FFFFFFF
+} XrColorSpaceSONY;
+typedef struct XrColorSpacesEnumerateInfoSONY {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ int64_t format;
+} XrColorSpacesEnumerateInfoSONY;
+
+// XrSwapchainCreateInfoColorSpaceSONY extends XrSwapchainCreateInfo
+typedef struct XrSwapchainCreateInfoColorSpaceSONY {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrColorSpaceSONY colorSpace;
+} XrSwapchainCreateInfoColorSpaceSONY;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateColorSpacesSONY)(XrSession session, const XrColorSpacesEnumerateInfoSONY* enumerateInfo, uint32_t colorSpaceCapacityInput, uint32_t* colorSpaceCountOutput, XrColorSpaceSONY* colorSpaces);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateColorSpacesSONY(
+ XrSession session,
+ const XrColorSpacesEnumerateInfoSONY* enumerateInfo,
+ uint32_t colorSpaceCapacityInput,
+ uint32_t* colorSpaceCountOutput,
+ XrColorSpaceSONY* colorSpaces);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_SONY_hdr_metadata is a preprocessor guard. Do not pass it to API calls.
+#define XR_SONY_hdr_metadata 1
+#define XR_SONY_hdr_metadata_SPEC_VERSION 1
+#define XR_SONY_HDR_METADATA_EXTENSION_NAME "XR_SONY_hdr_metadata"
+typedef struct XrXYColorSONY {
+ float x;
+ float y;
+} XrXYColorSONY;
+
+typedef struct XrHdrMetadataSONY {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrXYColorSONY displayPrimaryRed;
+ XrXYColorSONY displayPrimaryGreen;
+ XrXYColorSONY displayPrimaryBlue;
+ XrXYColorSONY whitePoint;
+ float maxLuminance;
+ float minLuminance;
+ float maxContentLightLevel;
+ float maxFrameAverageLightLevel;
+} XrHdrMetadataSONY;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetHdrMetadataSONY)(XrSwapchain swapchain, const XrHdrMetadataSONY* hdrMetadata);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetHdrMetadataSONY(
+ XrSwapchain swapchain,
+ const XrHdrMetadataSONY* hdrMetadata);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_persistence_operations is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_persistence_operations 1
+#define XR_EXT_spatial_persistence_operations_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_PERSISTENCE_OPERATIONS_EXTENSION_NAME "XR_EXT_spatial_persistence_operations"
+typedef struct XrSpatialEntityPersistInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContextEXT spatialContext;
+ XrSpatialEntityIdEXT spatialEntityId;
+} XrSpatialEntityPersistInfoEXT;
+
+typedef struct XrPersistSpatialEntityCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialPersistenceContextResultEXT persistResult;
+ XrUuid persistUuid;
+} XrPersistSpatialEntityCompletionEXT;
+
+typedef struct XrSpatialEntityUnpersistInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrUuid persistUuid;
+} XrSpatialEntityUnpersistInfoEXT;
+
+typedef struct XrUnpersistSpatialEntityCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialPersistenceContextResultEXT unpersistResult;
+} XrUnpersistSpatialEntityCompletionEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, const XrSpatialEntityPersistInfoEXT* persistInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, XrFutureEXT future, XrPersistSpatialEntityCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, const XrSpatialEntityUnpersistInfoEXT* unpersistInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, XrFutureEXT future, XrUnpersistSpatialEntityCompletionEXT* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityAsyncEXT(
+ XrSpatialPersistenceContextEXT persistenceContext,
+ const XrSpatialEntityPersistInfoEXT* persistInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityCompleteEXT(
+ XrSpatialPersistenceContextEXT persistenceContext,
+ XrFutureEXT future,
+ XrPersistSpatialEntityCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityAsyncEXT(
+ XrSpatialPersistenceContextEXT persistenceContext,
+ const XrSpatialEntityUnpersistInfoEXT* unpersistInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityCompleteEXT(
+ XrSpatialPersistenceContextEXT persistenceContext,
+ XrFutureEXT future,
+ XrUnpersistSpatialEntityCompletionEXT* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_image_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_image_tracking 1
+XR_DEFINE_HANDLE(XrSpatialImageTrackingDatabaseEXT)
+#define XR_EXT_spatial_image_tracking_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_IMAGE_TRACKING_EXTENSION_NAME "XR_EXT_spatial_image_tracking"
+
+typedef enum XrSpatialReferenceImageFormatEXT {
+ XR_SPATIAL_REFERENCE_IMAGE_FORMAT_RGBA_8888_EXT = 1,
+ XR_SPATIAL_REFERENCE_IMAGE_FORMAT_RGB_888_EXT = 2,
+ XR_SPATIAL_REFERENCE_IMAGE_FORMAT_YUV_420_888_EXT = 3,
+ XR_SPATIAL_REFERENCE_IMAGE_FORMAT_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialReferenceImageFormatEXT;
+typedef struct XrSpatialReferenceImagePlaneEXT {
+ uint32_t bufferSize;
+ const uint8_t* buffer;
+ uint32_t rowStride;
+ uint32_t pixelStride;
+} XrSpatialReferenceImagePlaneEXT;
+
+typedef struct XrSpatialReferenceImageEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t width;
+ uint32_t height;
+ XrSpatialReferenceImageFormatEXT format;
+ uint32_t planeCount;
+ const XrSpatialReferenceImagePlaneEXT* planes;
+} XrSpatialReferenceImageEXT;
+
+// XrSpatialImageStaticOptimizationEXT extends XrSpatialReferenceImageEXT,XrSpatialImageTrackingDatabaseCreateInfoEXT
+typedef struct XrSpatialImageStaticOptimizationEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 optimizeForStaticImage;
+} XrSpatialImageStaticOptimizationEXT;
+
+// XrSpatialImageSizeEXT extends XrSpatialReferenceImageEXT,XrSpatialImageTrackingDatabaseCreateInfoEXT
+typedef struct XrSpatialImageSizeEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float physicalWidth;
+} XrSpatialImageSizeEXT;
+
+typedef struct XrSpatialCapabilityConfigurationImageTrackingEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+ uint32_t imageTrackingDatabaseCount;
+ const XrSpatialImageTrackingDatabaseEXT* imageTrackingDatabases;
+} XrSpatialCapabilityConfigurationImageTrackingEXT;
+
+typedef struct XrSpatialImageTrackingDatabaseCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spatialReferenceImageCount;
+ const XrSpatialReferenceImageEXT* spatialReferenceImages;
+} XrSpatialImageTrackingDatabaseCreateInfoEXT;
+
+typedef struct XrSpatialImage2DDataEXT {
+ XrSpatialImageTrackingDatabaseEXT imageTrackingDatabase;
+ uint32_t referenceImageIndex;
+} XrSpatialImage2DDataEXT;
+
+// XrSpatialComponentImage2DListEXT extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentImage2DListEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t imageCount;
+ XrSpatialImage2DDataEXT* images;
+} XrSpatialComponentImage2DListEXT;
+
+typedef struct XrCreateSpatialImageTrackingDatabaseCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialImageTrackingDatabaseEXT database;
+} XrCreateSpatialImageTrackingDatabaseCompletionEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialReferenceImageFormatsEXT)(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, uint32_t formatCapacityInput, uint32_t* formatCountOutput, XrSpatialReferenceImageFormatEXT* formats);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialImageTrackingDatabaseAsyncEXT)(XrSession session, const XrSpatialImageTrackingDatabaseCreateInfoEXT* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialImageTrackingDatabaseCompleteEXT)(XrSession session, XrFutureEXT future, XrCreateSpatialImageTrackingDatabaseCompletionEXT* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialImageTrackingDatabaseEXT)(XrSpatialImageTrackingDatabaseEXT database);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialReferenceImageFormatsEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrSpatialCapabilityEXT capability,
+ uint32_t formatCapacityInput,
+ uint32_t* formatCountOutput,
+ XrSpatialReferenceImageFormatEXT* formats);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialImageTrackingDatabaseAsyncEXT(
+ XrSession session,
+ const XrSpatialImageTrackingDatabaseCreateInfoEXT* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialImageTrackingDatabaseCompleteEXT(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateSpatialImageTrackingDatabaseCompletionEXT* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialImageTrackingDatabaseEXT(
+ XrSpatialImageTrackingDatabaseEXT database);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_spatial_object_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_object_tracking 1
+#define XR_ANDROID_spatial_object_tracking_SPEC_VERSION 2
+#define XR_ANDROID_SPATIAL_OBJECT_TRACKING_EXTENSION_NAME "XR_ANDROID_spatial_object_tracking"
+
+typedef enum XrSpatialObjectSemanticLabelANDROID {
+ XR_SPATIAL_OBJECT_SEMANTIC_LABEL_UNCATEGORIZED_ANDROID = 0,
+ XR_SPATIAL_OBJECT_SEMANTIC_LABEL_KEYBOARD_ANDROID = 1,
+ XR_SPATIAL_OBJECT_SEMANTIC_LABEL_MOUSE_ANDROID = 2,
+ XR_SPATIAL_OBJECT_SEMANTIC_LABEL_LAPTOP_BASE_ANDROID = 3,
+ XR_SPATIAL_OBJECT_SEMANTIC_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSpatialObjectSemanticLabelANDROID;
+typedef struct XrSpatialCapabilityConfigurationObjectTrackingANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+ uint32_t activeSemanticLabelCount;
+ const XrSpatialObjectSemanticLabelANDROID* activeSemanticLabels;
+} XrSpatialCapabilityConfigurationObjectTrackingANDROID;
+
+// XrSpatialComponentObjectSemanticLabelListANDROID extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentObjectSemanticLabelListANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t semanticLabelCount;
+ XrSpatialObjectSemanticLabelANDROID* semanticLabels;
+} XrSpatialComponentObjectSemanticLabelListANDROID;
+
+
+
+// XR_ANDROID_spatial_discovery_raycast is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_discovery_raycast 1
+#define XR_ANDROID_spatial_discovery_raycast_SPEC_VERSION 1
+#define XR_ANDROID_SPATIAL_DISCOVERY_RAYCAST_EXTENSION_NAME "XR_ANDROID_spatial_discovery_raycast"
+typedef struct XrSpatialRaycastResultDataANDROID {
+ XrPosef hitPose;
+ float distanceSquared;
+} XrSpatialRaycastResultDataANDROID;
+
+typedef struct XrSpatialCapabilityConfigurationDepthRaycastANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialCapabilityEXT capability;
+ uint32_t enabledComponentCount;
+ const XrSpatialComponentTypeEXT* enabledComponents;
+} XrSpatialCapabilityConfigurationDepthRaycastANDROID;
+
+// XrSpatialRaycastInfoANDROID extends XrSpatialDiscoverySnapshotCreateInfoEXT
+typedef struct XrSpatialRaycastInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrVector3f origin;
+ XrVector3f direction;
+ float maxDistance;
+} XrSpatialRaycastInfoANDROID;
+
+// XrSpatialComponentRaycastResultListANDROID extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentRaycastResultListANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t raycastResultCount;
+ XrSpatialRaycastResultDataANDROID* raycastResults;
+} XrSpatialComponentRaycastResultListANDROID;
+
+typedef struct XrSpatialRaycastSnapshotCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t componentTypeCount;
+ const XrSpatialComponentTypeEXT* componentTypes;
+ const XrSpatialRaycastInfoANDROID* raycastInfo;
+} XrSpatialRaycastSnapshotCreateInfoANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialRaycastSnapshotANDROID)(XrSpatialContextEXT spatialContext, const XrSpatialRaycastSnapshotCreateInfoANDROID* createInfo, XrSpatialSnapshotEXT* snapshot);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialRaycastSnapshotANDROID(
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialRaycastSnapshotCreateInfoANDROID* createInfo,
+ XrSpatialSnapshotEXT* snapshot);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_google_cloud_auth is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_google_cloud_auth 1
+#define XR_ANDROID_google_cloud_auth_SPEC_VERSION 1
+#define XR_ANDROID_GOOGLE_CLOUD_AUTH_EXTENSION_NAME "XR_ANDROID_google_cloud_auth"
+
+typedef enum XrGoogleCloudAuthErrorANDROID {
+ XR_GOOGLE_CLOUD_AUTH_ERROR_NONE_ANDROID = 0,
+ XR_GOOGLE_CLOUD_AUTH_ERROR_QUOTA_EXCEEDED_ANDROID = -1,
+ XR_GOOGLE_CLOUD_AUTH_ERROR_UNREACHABLE_ANDROID = -2,
+ XR_GOOGLE_CLOUD_AUTH_ERROR_ANDROID = -3,
+ XR_GOOGLE_CLOUD_AUTH_ERROR_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrGoogleCloudAuthErrorANDROID;
+typedef struct XR_MAY_ALIAS XrGoogleCloudAuthInfoBaseHeaderANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrGoogleCloudAuthInfoBaseHeaderANDROID;
+
+typedef struct XrGoogleCloudAuthInfoApiKeyANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* apiKey;
+} XrGoogleCloudAuthInfoApiKeyANDROID;
+
+typedef struct XrGoogleCloudAuthInfoTokenANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* authToken;
+} XrGoogleCloudAuthInfoTokenANDROID;
+
+typedef struct XrGoogleCloudAuthInfoKeylessANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrGoogleCloudAuthInfoKeylessANDROID;
+
+// XrGoogleCloudAuthErrorResultANDROID extends XrGoogleCloudAuthInfoBaseHeaderANDROID
+typedef struct XrGoogleCloudAuthErrorResultANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrGoogleCloudAuthErrorANDROID error;
+} XrGoogleCloudAuthErrorResultANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetGoogleCloudAuthAsyncANDROID)(XrSession session, const XrGoogleCloudAuthInfoBaseHeaderANDROID* authInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrSetGoogleCloudAuthCompleteANDROID)(XrSession session, XrFutureEXT future, XrFutureCompletionEXT* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetGoogleCloudAuthAsyncANDROID(
+ XrSession session,
+ const XrGoogleCloudAuthInfoBaseHeaderANDROID* authInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetGoogleCloudAuthCompleteANDROID(
+ XrSession session,
+ XrFutureEXT future,
+ XrFutureCompletionEXT* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_geospatial is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_geospatial 1
+XR_DEFINE_HANDLE(XrGeospatialTrackerANDROID)
+#define XR_ANDROID_geospatial_SPEC_VERSION 1
+#define XR_ANDROID_GEOSPATIAL_EXTENSION_NAME "XR_ANDROID_geospatial"
+
+typedef enum XrGeospatialTrackerStateANDROID {
+ XR_GEOSPATIAL_TRACKER_STATE_STOPPED_ANDROID = 0,
+ XR_GEOSPATIAL_TRACKER_STATE_RUNNING_ANDROID = 1,
+ XR_GEOSPATIAL_TRACKER_STATE_INITIALIZATION_FAILED_ANDROID = 2,
+ XR_GEOSPATIAL_TRACKER_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrGeospatialTrackerStateANDROID;
+
+typedef enum XrVPSAvailabilityANDROID {
+ XR_VPS_AVAILABILITY_UNAVAILABLE_ANDROID = 1,
+ XR_VPS_AVAILABILITY_AVAILABLE_ANDROID = 2,
+ XR_VPSAVAILABILITY_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrVPSAvailabilityANDROID;
+typedef XrFlags64 XrGeospatialPoseFlagsANDROID;
+
+// Flag bits for XrGeospatialPoseFlagsANDROID
+static const XrGeospatialPoseFlagsANDROID XR_GEOSPATIAL_POSE_ORIENTATION_VALID_BIT_ANDROID = 0x00000001;
+static const XrGeospatialPoseFlagsANDROID XR_GEOSPATIAL_POSE_POSITION_VALID_BIT_ANDROID = 0x00000002;
+
+typedef struct XrGeospatialPoseANDROID {
+ XrQuaternionf eastUpSouthOrientation;
+ double latitude;
+ double longitude;
+ double altitude;
+} XrGeospatialPoseANDROID;
+
+// XrSystemGeospatialPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemGeospatialPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsGeospatial;
+} XrSystemGeospatialPropertiesANDROID;
+
+typedef struct XrGeospatialTrackerCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrGeospatialTrackerCreateInfoANDROID;
+
+typedef struct XrEventDataGeospatialTrackerStateChangedANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrGeospatialTrackerANDROID geospatialTracker;
+ XrGeospatialTrackerStateANDROID state;
+ XrResult initializationResult;
+ XrTime time;
+} XrEventDataGeospatialTrackerStateChangedANDROID;
+
+typedef struct XrGeospatialPoseFromPoseLocateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrPosef pose;
+} XrGeospatialPoseFromPoseLocateInfoANDROID;
+
+typedef struct XrGeospatialPoseResultANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrGeospatialPoseFlagsANDROID poseFlags;
+ XrGeospatialPoseANDROID geospatialPose;
+ double horizontalAccuracy;
+ double verticalAccuracy;
+ double orientationYawAccuracy;
+} XrGeospatialPoseResultANDROID;
+
+typedef struct XrGeospatialPoseLocateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime time;
+ XrGeospatialPoseANDROID geospatialPose;
+} XrGeospatialPoseLocateInfoANDROID;
+
+typedef struct XrVPSAvailabilityCheckCompletionANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrVPSAvailabilityANDROID availability;
+} XrVPSAvailabilityCheckCompletionANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateGeospatialTrackerANDROID)(XrSession session, const XrGeospatialTrackerCreateInfoANDROID* createInfo, XrGeospatialTrackerANDROID* geospatialTrackerOutput);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyGeospatialTrackerANDROID)(XrGeospatialTrackerANDROID geospatialTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateGeospatialPoseFromPoseANDROID)(XrGeospatialTrackerANDROID geospatialTracker, const XrGeospatialPoseFromPoseLocateInfoANDROID* locateInfo, XrGeospatialPoseResultANDROID* geospatialPoseResult);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateGeospatialPoseANDROID)(XrGeospatialTrackerANDROID geospatialTracker, const XrGeospatialPoseLocateInfoANDROID* locateInfo, XrSpaceLocation* location);
+typedef XrResult (XRAPI_PTR *PFN_xrCheckVpsAvailabilityAsyncANDROID)(XrSession session, double latitude, double longitude, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCheckVpsAvailabilityCompleteANDROID)(XrSession session, XrFutureEXT future, XrVPSAvailabilityCheckCompletionANDROID* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateGeospatialTrackerANDROID(
+ XrSession session,
+ const XrGeospatialTrackerCreateInfoANDROID* createInfo,
+ XrGeospatialTrackerANDROID* geospatialTrackerOutput);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyGeospatialTrackerANDROID(
+ XrGeospatialTrackerANDROID geospatialTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateGeospatialPoseFromPoseANDROID(
+ XrGeospatialTrackerANDROID geospatialTracker,
+ const XrGeospatialPoseFromPoseLocateInfoANDROID* locateInfo,
+ XrGeospatialPoseResultANDROID* geospatialPoseResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateGeospatialPoseANDROID(
+ XrGeospatialTrackerANDROID geospatialTracker,
+ const XrGeospatialPoseLocateInfoANDROID* locateInfo,
+ XrSpaceLocation* location);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCheckVpsAvailabilityAsyncANDROID(
+ XrSession session,
+ double latitude,
+ double longitude,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCheckVpsAvailabilityCompleteANDROID(
+ XrSession session,
+ XrFutureEXT future,
+ XrVPSAvailabilityCheckCompletionANDROID* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_spatial_entity_bound_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_entity_bound_anchor 1
+#define XR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION 2
+#define XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAME "XR_ANDROID_spatial_entity_bound_anchor"
+// XrSpatialAnchorParentANDROID extends XrSpatialAnchorCreateInfoEXT
+typedef struct XrSpatialAnchorParentANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialEntityIdEXT parentId;
+} XrSpatialAnchorParentANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialAnchorAttachableComponentsANDROID)(XrInstance instance, XrSystemId systemId, uint32_t attachableComponentCapacityInput, uint32_t* attachableComponentCountOutput, XrSpatialComponentTypeEXT* attachableComponents);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialAnchorAttachableComponentsANDROID(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t attachableComponentCapacityInput,
+ uint32_t* attachableComponentCountOutput,
+ XrSpatialComponentTypeEXT* attachableComponents);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_spatial_component_subsumed_by is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_component_subsumed_by 1
+#define XR_ANDROID_spatial_component_subsumed_by_SPEC_VERSION 1
+#define XR_ANDROID_SPATIAL_COMPONENT_SUBSUMED_BY_EXTENSION_NAME "XR_ANDROID_spatial_component_subsumed_by"
+// XrSpatialDiscoveryUniqueEntitiesFilterANDROID extends XrSpatialDiscoverySnapshotCreateInfoEXT
+typedef struct XrSpatialDiscoveryUniqueEntitiesFilterANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialDiscoveryUniqueEntitiesFilterANDROID;
+
+// XrSpatialComponentSubsumedByListANDROID extends XrSpatialComponentDataQueryResultEXT
+typedef struct XrSpatialComponentSubsumedByListANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t subsumedUniqueIdCount;
+ XrSpatialEntityIdEXT* subsumedUniqueIds;
+} XrSpatialComponentSubsumedByListANDROID;
+
+
+
+// XR_ANDROID_spatial_anchor_space is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_spatial_anchor_space 1
+#define XR_ANDROID_spatial_anchor_space_SPEC_VERSION 1
+#define XR_ANDROID_SPATIAL_ANCHOR_SPACE_EXTENSION_NAME "XR_ANDROID_spatial_anchor_space"
+typedef struct XrSpatialAnchorSpaceFromIdCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialEntityIdEXT anchorEntityId;
+} XrSpatialAnchorSpaceFromIdCreateInfoANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorSpaceANDROID)(XrSession session, XrSpatialContextEXT spatialContext, const XrSpatialAnchorCreateInfoEXT* createInfo, XrSpatialEntityIdEXT* anchorEntityId, XrSpace* anchorSpace);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorSpaceFromIdANDROID)(XrSession session, XrSpatialContextEXT spatialContext, const XrSpatialAnchorSpaceFromIdCreateInfoANDROID* createInfo, XrSpace* anchorSpace);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorSpaceANDROID(
+ XrSession session,
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialAnchorCreateInfoEXT* createInfo,
+ XrSpatialEntityIdEXT* anchorEntityId,
+ XrSpace* anchorSpace);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorSpaceFromIdANDROID(
+ XrSession session,
+ XrSpatialContextEXT spatialContext,
+ const XrSpatialAnchorSpaceFromIdCreateInfoANDROID* createInfo,
+ XrSpace* anchorSpace);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ANDROID_geospatial_anchor is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_geospatial_anchor 1
+#define XR_ANDROID_geospatial_anchor_SPEC_VERSION 1
+#define XR_ANDROID_GEOSPATIAL_ANCHOR_EXTENSION_NAME "XR_ANDROID_geospatial_anchor"
+
+typedef enum XrSurfaceAnchorTypeANDROID {
+ XR_SURFACE_ANCHOR_TYPE_TERRAIN_ANDROID = 1,
+ XR_SURFACE_ANCHOR_TYPE_ROOFTOP_ANDROID = 2,
+ XR_SURFACE_ANCHOR_TYPE_MAX_ENUM_ANDROID = 0x7FFFFFFF
+} XrSurfaceAnchorTypeANDROID;
+// XrSystemGeospatialAnchorPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemGeospatialAnchorPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t maxSurfaceAnchorCount;
+} XrSystemGeospatialAnchorPropertiesANDROID;
+
+// XrGeospatialTrackerAnchorTrackingInfoANDROID extends XrGeospatialTrackerCreateInfoANDROID
+typedef struct XrGeospatialTrackerAnchorTrackingInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 shouldTrackPlanes;
+} XrGeospatialTrackerAnchorTrackingInfoANDROID;
+
+typedef struct XrGeospatialAnchorCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrGeospatialTrackerANDROID geospatialTracker;
+ XrGeospatialPoseANDROID geospatialPose;
+} XrGeospatialAnchorCreateInfoANDROID;
+
+typedef struct XrSurfaceAnchorCreateInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrGeospatialTrackerANDROID geospatialTracker;
+ XrSurfaceAnchorTypeANDROID surfaceAnchorType;
+ XrQuaternionf eastUpSouthOrientation;
+ double latitude;
+ double longitude;
+ double altitudeRelativeToSurface;
+} XrSurfaceAnchorCreateInfoANDROID;
+
+typedef struct XrSurfaceAnchorCreateCompletionANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrSpatialEntityIdEXT anchorEntityId;
+} XrSurfaceAnchorCreateCompletionANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateGeospatialAnchorANDROID)(XrSpatialContextEXT spatialContext, const XrGeospatialAnchorCreateInfoANDROID* createInfo, XrSpatialEntityIdEXT* anchorEntityId);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSurfaceAnchorAsyncANDROID)(XrSpatialContextEXT spatialContext, const XrSurfaceAnchorCreateInfoANDROID* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSurfaceAnchorCompleteANDROID)(XrSpatialContextEXT spatialContext, XrFutureEXT future, XrSurfaceAnchorCreateCompletionANDROID* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateGeospatialAnchorANDROID(
+ XrSpatialContextEXT spatialContext,
+ const XrGeospatialAnchorCreateInfoANDROID* createInfo,
+ XrSpatialEntityIdEXT* anchorEntityId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSurfaceAnchorAsyncANDROID(
+ XrSpatialContextEXT spatialContext,
+ const XrSurfaceAnchorCreateInfoANDROID* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSurfaceAnchorCompleteANDROID(
+ XrSpatialContextEXT spatialContext,
+ XrFutureEXT future,
+ XrSurfaceAnchorCreateCompletionANDROID* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_container is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_container 1
+XR_DEFINE_HANDLE(XrSpatialContainerEXT)
+#define XR_EXT_spatial_container_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_CONTAINER_EXTENSION_NAME "XR_EXT_spatial_container"
+
+typedef enum XrSpatialContainerGraphicsPresentationEXT {
+ XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_SELF_RENDERING_EXT = 1000813000,
+ XR_SPATIAL_CONTAINER_GRAPHICS_PRESENTATION_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialContainerGraphicsPresentationEXT;
+
+typedef enum XrSpatialContainerBoundsModeEXT {
+ XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT = 1,
+ XR_SPATIAL_CONTAINER_BOUNDS_MODE_IMMERSIVE_EXT = 2,
+ XR_SPATIAL_CONTAINER_BOUNDS_MODE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialContainerBoundsModeEXT;
+
+typedef enum XrSpatialContainerVolumeClippingEXT {
+ XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_NONE_EXT = 0,
+ XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_STRICT_EXT = 1,
+ XR_SPATIAL_CONTAINER_VOLUME_CLIPPING_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrSpatialContainerVolumeClippingEXT;
+// XrSessionCreateInfoSpatialContainersEXT extends XrSessionCreateInfo
+typedef struct XrSessionCreateInfoSpatialContainersEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSessionCreateInfoSpatialContainersEXT;
+
+typedef struct XrSpatialContainerCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerGraphicsPresentationEXT graphicsPresentation;
+ XrExtent3Df suggestedBounds;
+} XrSpatialContainerCreateInfoEXT;
+
+typedef struct XrSpatialContainerSpaceCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+} XrSpatialContainerSpaceCreateInfoEXT;
+
+typedef struct XrEventDataSpatialContainerClosedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+} XrEventDataSpatialContainerClosedEXT;
+
+// XrSystemSpatialContainerPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemSpatialContainerPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t maxSpatialContainerCount;
+ XrBool32 supportsBounded;
+ XrBool32 supportsImmersive;
+} XrSystemSpatialContainerPropertiesEXT;
+
+typedef struct XrSpatialContainerBoundsEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrExtent3Df bounds;
+ XrBool32 infiniteBounds;
+} XrSpatialContainerBoundsEXT;
+
+typedef struct XrEventDataSpatialContainerBoundsChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+ XrExtent3Df bounds;
+ XrBool32 infiniteBounds;
+ XrSpatialContainerBoundsModeEXT boundsMode;
+} XrEventDataSpatialContainerBoundsChangedEXT;
+
+typedef struct XrSpatialContainerBoundsGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialContainerBoundsGetInfoEXT;
+
+typedef struct XrSpatialContainerStateGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialContainerStateGetInfoEXT;
+
+typedef struct XrSpatialContainerVisibleRequestInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 visible;
+} XrSpatialContainerVisibleRequestInfoEXT;
+
+typedef struct XrEventDataSpatialContainerVisibleChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+ XrBool32 visible;
+} XrEventDataSpatialContainerVisibleChangedEXT;
+
+typedef struct XrEventDataSpatialContainerVisibleRequestDeniedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+} XrEventDataSpatialContainerVisibleRequestDeniedEXT;
+
+typedef struct XrEventDataSpatialContainerInteractableChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+ XrBool32 interactable;
+} XrEventDataSpatialContainerInteractableChangedEXT;
+
+typedef struct XrSpatialContainerBoundsModeRequestInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerBoundsModeEXT boundsMode;
+} XrSpatialContainerBoundsModeRequestInfoEXT;
+
+typedef struct XrEventDataSpatialContainerBoundsModeRequestDeniedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+} XrEventDataSpatialContainerBoundsModeRequestDeniedEXT;
+
+typedef struct XrSpatialContainerStateEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 visible;
+ XrBool32 interactable;
+ XrSpatialContainerBoundsModeEXT boundsMode;
+} XrSpatialContainerStateEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContainerEXT)(XrSession session, const XrSpatialContainerCreateInfoEXT* createInfo, XrSpatialContainerEXT* spatialContainer);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialContainerEXT)(XrSpatialContainerEXT spatialContainer);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContainerSpaceEXT)(XrSession session, const XrSpatialContainerSpaceCreateInfoEXT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestSpatialContainerVisibleEXT)(XrSpatialContainerEXT spatialContainer, const XrSpatialContainerVisibleRequestInfoEXT* info);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestSpatialContainerBoundsModeEXT)(XrSpatialContainerEXT spatialContainer, const XrSpatialContainerBoundsModeRequestInfoEXT* info);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialContainerBoundsEXT)(XrSpatialContainerEXT spatialContainer, const XrSpatialContainerBoundsGetInfoEXT* getInfo, XrSpatialContainerBoundsEXT* bounds);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialContainerStateEXT)(XrSpatialContainerEXT spatialContainer, const XrSpatialContainerStateGetInfoEXT* getInfo, XrSpatialContainerStateEXT* state);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedSpatialContainerGraphicsPresentationsEXT)(XrInstance instance, XrSystemId systemId, uint32_t graphicsPresentationCapacityInput, uint32_t* graphicsPresentationCountOutput, XrSpatialContainerGraphicsPresentationEXT* graphicsPresentations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContainerEXT(
+ XrSession session,
+ const XrSpatialContainerCreateInfoEXT* createInfo,
+ XrSpatialContainerEXT* spatialContainer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialContainerEXT(
+ XrSpatialContainerEXT spatialContainer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContainerSpaceEXT(
+ XrSession session,
+ const XrSpatialContainerSpaceCreateInfoEXT* createInfo,
+ XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestSpatialContainerVisibleEXT(
+ XrSpatialContainerEXT spatialContainer,
+ const XrSpatialContainerVisibleRequestInfoEXT* info);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestSpatialContainerBoundsModeEXT(
+ XrSpatialContainerEXT spatialContainer,
+ const XrSpatialContainerBoundsModeRequestInfoEXT* info);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialContainerBoundsEXT(
+ XrSpatialContainerEXT spatialContainer,
+ const XrSpatialContainerBoundsGetInfoEXT* getInfo,
+ XrSpatialContainerBoundsEXT* bounds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialContainerStateEXT(
+ XrSpatialContainerEXT spatialContainer,
+ const XrSpatialContainerStateGetInfoEXT* getInfo,
+ XrSpatialContainerStateEXT* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedSpatialContainerGraphicsPresentationsEXT(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t graphicsPresentationCapacityInput,
+ uint32_t* graphicsPresentationCountOutput,
+ XrSpatialContainerGraphicsPresentationEXT* graphicsPresentations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_spatial_container_self_rendering is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_spatial_container_self_rendering 1
+#define XR_EXT_spatial_container_self_rendering_SPEC_VERSION 1
+#define XR_EXT_SPATIAL_CONTAINER_SELF_RENDERING_EXTENSION_NAME "XR_EXT_spatial_container_self_rendering"
+typedef struct XrSpatialContainerViewLocateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+ XrSpace space;
+ XrSpatialContainerEXT spatialContainer;
+} XrSpatialContainerViewLocateInfoEXT;
+
+typedef struct XrSpatialContainerViewsLocateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime displayTime;
+ uint32_t viewLocateInfoCount;
+ const XrSpatialContainerViewLocateInfoEXT* viewLocateInfos;
+} XrSpatialContainerViewsLocateInfoEXT;
+
+typedef struct XrSpatialContainerViewStateEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrViewStateFlags viewStateFlags;
+ XrViewConfigurationType viewConfigurationType;
+ XrBool32 shouldSubmitLayers;
+ XrExtent2Di recommendedImageExtent;
+} XrSpatialContainerViewStateEXT;
+
+typedef struct XrSpatialContainerLayerEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+ XrBool32 retainPreviousSubmission;
+ uint32_t layerCount;
+ const XrCompositionLayerBaseHeader* const* layers;
+} XrSpatialContainerLayerEXT;
+
+// XrSpatialContainerLayerFrameEndInfoEXT extends XrFrameEndInfo
+typedef struct XrSpatialContainerLayerFrameEndInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t containerLayerCount;
+ const XrSpatialContainerLayerEXT* containerLayers;
+} XrSpatialContainerLayerFrameEndInfoEXT;
+
+typedef struct XrSpatialContainerBeginInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+ XrViewConfigurationType primaryViewConfigurationType;
+} XrSpatialContainerBeginInfoEXT;
+
+typedef struct XrSpatialContainerEndInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerEXT spatialContainer;
+} XrSpatialContainerEndInfoEXT;
+
+// XrSpatialContainerCompositionLayerViewConfigurationEXT extends XrCompositionLayerProjection
+typedef struct XrSpatialContainerCompositionLayerViewConfigurationEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrViewConfigurationType viewConfigurationType;
+} XrSpatialContainerCompositionLayerViewConfigurationEXT;
+
+// XrSpatialContainerLayerVolumeClippingEXT extends XrSpatialContainerLayerEXT
+typedef struct XrSpatialContainerLayerVolumeClippingEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialContainerVolumeClippingEXT volumeClipping;
+} XrSpatialContainerLayerVolumeClippingEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrBeginSpatialContainerRenderingEXT)(XrSession session, const XrSpatialContainerBeginInfoEXT* beginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrEndSpatialContainerRenderingEXT)(XrSession session, const XrSpatialContainerEndInfoEXT* endInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpatialContainerViewsEXT)(XrSession session, const XrSpatialContainerViewsLocateInfoEXT* locateInfo, uint32_t viewStateCount, XrSpatialContainerViewStateEXT* viewStates, uint32_t viewCount, XrView* views);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginSpatialContainerRenderingEXT(
+ XrSession session,
+ const XrSpatialContainerBeginInfoEXT* beginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEndSpatialContainerRenderingEXT(
+ XrSession session,
+ const XrSpatialContainerEndInfoEXT* endInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpatialContainerViewsEXT(
+ XrSession session,
+ const XrSpatialContainerViewsLocateInfoEXT* locateInfo,
+ uint32_t viewStateCount,
+ XrSpatialContainerViewStateEXT* viewStates,
+ uint32_t viewCount,
+ XrView* views);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_interaction_profile_battery_state_display is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_interaction_profile_battery_state_display 1
+#define XR_EXT_interaction_profile_battery_state_display_SPEC_VERSION 1
+#define XR_EXT_INTERACTION_PROFILE_BATTERY_STATE_DISPLAY_EXTENSION_NAME "XR_EXT_interaction_profile_battery_state_display"
+typedef XrFlags64 XrBatteryStateDisplayStateFlagsEXT;
+
+// Flag bits for XrBatteryStateDisplayStateFlagsEXT
+static const XrBatteryStateDisplayStateFlagsEXT XR_BATTERY_STATE_DISPLAY_STATE_VALID_BIT_EXT = 0x00000001;
+static const XrBatteryStateDisplayStateFlagsEXT XR_BATTERY_STATE_DISPLAY_STATE_CHARGING_BIT_EXT = 0x00000002;
+static const XrBatteryStateDisplayStateFlagsEXT XR_BATTERY_STATE_DISPLAY_STATE_PLUGGED_IN_BIT_EXT = 0x00000004;
+static const XrBatteryStateDisplayStateFlagsEXT XR_BATTERY_STATE_DISPLAY_STATE_NO_BATTERY_BIT_EXT = 0x00000008;
+
+// XrBatteryStateDisplayEXT extends XrInteractionProfileState
+typedef struct XrBatteryStateDisplayEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBatteryStateDisplayStateFlagsEXT stateFlags;
+ float batteryLevel;
+} XrBatteryStateDisplayEXT;
+
+
+
+// XR_EXT_loader_init_properties is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_loader_init_properties 1
+#define XR_EXT_loader_init_properties_SPEC_VERSION 1
+#define XR_EXT_LOADER_INIT_PROPERTIES_EXTENSION_NAME "XR_EXT_loader_init_properties"
+typedef struct XrLoaderInitPropertyValueEXT {
+ const char* name;
+ const char* value;
+} XrLoaderInitPropertyValueEXT;
+
+// XrLoaderInitInfoPropertiesEXT extends XrLoaderInitInfoBaseHeaderKHR
+typedef struct XrLoaderInitInfoPropertiesEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t propertyValueCount;
+ const XrLoaderInitPropertyValueEXT* propertyValues;
+} XrLoaderInitInfoPropertiesEXT;
+
+
+
+// XR_EXT_view_configuration_views_change is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_view_configuration_views_change 1
+#define XR_EXT_view_configuration_views_change_SPEC_VERSION 1
+#define XR_EXT_VIEW_CONFIGURATION_VIEWS_CHANGE_EXTENSION_NAME "XR_EXT_view_configuration_views_change"
+typedef struct XrEventDataViewConfigurationViewsChangedEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSystemId systemId;
+ XrViewConfigurationType viewConfigurationType;
+} XrEventDataViewConfigurationViewsChangedEXT;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/frame/openxr-compat/vendor/openxr/openxr_platform.h b/frame/openxr-compat/vendor/openxr/openxr_platform.h
new file mode 100644
index 0000000..9de27ad
--- /dev/null
+++ b/frame/openxr-compat/vendor/openxr/openxr_platform.h
@@ -0,0 +1,819 @@
+#ifndef OPENXR_PLATFORM_H_
+#define OPENXR_PLATFORM_H_ 1
+
+/*
+** Copyright 2017-2026 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0 OR MIT
+*/
+
+/*
+** This header is generated from the Khronos OpenXR XML API Registry.
+**
+*/
+
+#include "openxr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_KHR_android_thread_settings is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_android_thread_settings 1
+#define XR_KHR_android_thread_settings_SPEC_VERSION 6
+#define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings"
+
+typedef enum XrAndroidThreadTypeKHR {
+ XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR = 1,
+ XR_ANDROID_THREAD_TYPE_APPLICATION_WORKER_KHR = 2,
+ XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR = 3,
+ XR_ANDROID_THREAD_TYPE_RENDERER_WORKER_KHR = 4,
+ XR_ANDROID_THREAD_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
+} XrAndroidThreadTypeKHR;
+typedef XrResult (XRAPI_PTR *PFN_xrSetAndroidApplicationThreadKHR)(XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR(
+ XrSession session,
+ XrAndroidThreadTypeKHR threadType,
+ uint32_t threadId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_KHR_android_surface_swapchain is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_android_surface_swapchain 1
+#define XR_KHR_android_surface_swapchain_SPEC_VERSION 4
+#define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain"
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR(
+ XrSession session,
+ const XrSwapchainCreateInfo* info,
+ XrSwapchain* swapchain,
+ jobject* surface);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_KHR_android_create_instance is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_android_create_instance 1
+#define XR_KHR_android_create_instance_SPEC_VERSION 3
+#define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance"
+// XrInstanceCreateInfoAndroidKHR extends XrInstanceCreateInfo
+typedef struct XrInstanceCreateInfoAndroidKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS applicationVM;
+ void* XR_MAY_ALIAS applicationActivity;
+} XrInstanceCreateInfoAndroidKHR;
+
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_KHR_vulkan_swapchain_format_list is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_vulkan_swapchain_format_list 1
+#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 5
+#define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list"
+// XrVulkanSwapchainFormatListCreateInfoKHR extends XrSwapchainCreateInfo
+typedef struct XrVulkanSwapchainFormatListCreateInfoKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t viewFormatCount;
+ const VkFormat* viewFormats;
+} XrVulkanSwapchainFormatListCreateInfoKHR;
+
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_GRAPHICS_API_OPENGL
+
+// XR_KHR_opengl_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_opengl_enable 1
+#define XR_KHR_opengl_enable_SPEC_VERSION 12
+#define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable"
+#ifdef XR_USE_PLATFORM_WIN32
+// XrGraphicsBindingOpenGLWin32KHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingOpenGLWin32KHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ HDC hDC;
+ HGLRC hGLRC;
+} XrGraphicsBindingOpenGLWin32KHR;
+#endif // XR_USE_PLATFORM_WIN32
+
+#ifdef XR_USE_PLATFORM_XLIB
+// XrGraphicsBindingOpenGLXlibKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingOpenGLXlibKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ Display* xDisplay;
+ uint32_t visualid;
+ GLXFBConfig glxFBConfig;
+ GLXDrawable glxDrawable;
+ GLXContext glxContext;
+} XrGraphicsBindingOpenGLXlibKHR;
+#endif // XR_USE_PLATFORM_XLIB
+
+#ifdef XR_USE_PLATFORM_XCB
+// XrGraphicsBindingOpenGLXcbKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingOpenGLXcbKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ xcb_connection_t* connection;
+ uint32_t screenNumber;
+ xcb_glx_fbconfig_t fbconfigid;
+ xcb_visualid_t visualid;
+ xcb_glx_drawable_t glxDrawable;
+ xcb_glx_context_t glxContext;
+} XrGraphicsBindingOpenGLXcbKHR;
+#endif // XR_USE_PLATFORM_XCB
+
+#ifdef XR_USE_PLATFORM_WAYLAND
+// XrGraphicsBindingOpenGLWaylandKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingOpenGLWaylandKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ struct wl_display* display;
+} XrGraphicsBindingOpenGLWaylandKHR;
+#endif // XR_USE_PLATFORM_WAYLAND
+
+typedef struct XrSwapchainImageOpenGLKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t image;
+} XrSwapchainImageOpenGLKHR;
+
+typedef struct XrGraphicsRequirementsOpenGLKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVersion minApiVersionSupported;
+ XrVersion maxApiVersionSupported;
+} XrGraphicsRequirementsOpenGLKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_OPENGL */
+
+#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+
+// XR_KHR_opengl_es_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_opengl_es_enable 1
+#define XR_KHR_opengl_es_enable_SPEC_VERSION 10
+#define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable"
+#ifdef XR_USE_PLATFORM_ANDROID
+// XrGraphicsBindingOpenGLESAndroidKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingOpenGLESAndroidKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ EGLDisplay display;
+ EGLConfig config;
+ EGLContext context;
+} XrGraphicsBindingOpenGLESAndroidKHR;
+#endif // XR_USE_PLATFORM_ANDROID
+
+typedef struct XrSwapchainImageOpenGLESKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t image;
+} XrSwapchainImageOpenGLESKHR;
+
+typedef struct XrGraphicsRequirementsOpenGLESKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVersion minApiVersionSupported;
+ XrVersion maxApiVersionSupported;
+} XrGraphicsRequirementsOpenGLESKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_KHR_vulkan_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_vulkan_enable 1
+#define XR_KHR_vulkan_enable_SPEC_VERSION 10
+#define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable"
+// XrGraphicsBindingVulkanKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingVulkanKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ VkInstance instance;
+ VkPhysicalDevice physicalDevice;
+ VkDevice device;
+ uint32_t queueFamilyIndex;
+ uint32_t queueIndex;
+} XrGraphicsBindingVulkanKHR;
+
+typedef struct XrSwapchainImageVulkanKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ VkImage image;
+} XrSwapchainImageVulkanKHR;
+
+typedef struct XrGraphicsRequirementsVulkanKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVersion minApiVersionSupported;
+ XrVersion maxApiVersionSupported;
+} XrGraphicsRequirementsVulkanKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanInstanceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanDeviceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDeviceKHR)(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanInstanceExtensionsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanDeviceExtensionsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ uint32_t bufferCapacityInput,
+ uint32_t* bufferCountOutput,
+ char* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDeviceKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ VkInstance vkInstance,
+ VkPhysicalDevice* vkPhysicalDevice);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_GRAPHICS_API_D3D11
+
+// XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_D3D11_enable 1
+#define XR_KHR_D3D11_enable_SPEC_VERSION 11
+#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
+// XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingD3D11KHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ ID3D11Device* device;
+} XrGraphicsBindingD3D11KHR;
+
+typedef struct XrSwapchainImageD3D11KHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ ID3D11Texture2D* texture;
+} XrSwapchainImageD3D11KHR;
+
+typedef struct XrGraphicsRequirementsD3D11KHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ LUID adapterLuid;
+ D3D_FEATURE_LEVEL minFeatureLevel;
+} XrGraphicsRequirementsD3D11KHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_D3D11 */
+
+#ifdef XR_USE_GRAPHICS_API_D3D12
+
+// XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_D3D12_enable 1
+#define XR_KHR_D3D12_enable_SPEC_VERSION 11
+#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
+// XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingD3D12KHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ ID3D12Device* device;
+ ID3D12CommandQueue* queue;
+} XrGraphicsBindingD3D12KHR;
+
+typedef struct XrSwapchainImageD3D12KHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ ID3D12Resource* texture;
+} XrSwapchainImageD3D12KHR;
+
+typedef struct XrGraphicsRequirementsD3D12KHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ LUID adapterLuid;
+ D3D_FEATURE_LEVEL minFeatureLevel;
+} XrGraphicsRequirementsD3D12KHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_D3D12 */
+
+#ifdef XR_USE_GRAPHICS_API_METAL
+
+// XR_KHR_metal_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_metal_enable 1
+#define XR_KHR_metal_enable_SPEC_VERSION 3
+#define XR_KHR_METAL_ENABLE_EXTENSION_NAME "XR_KHR_metal_enable"
+// XrGraphicsBindingMetalKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingMetalKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS commandQueue;
+} XrGraphicsBindingMetalKHR;
+
+typedef struct XrSwapchainImageMetalKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS texture;
+} XrSwapchainImageMetalKHR;
+
+typedef struct XrGraphicsRequirementsMetalKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS metalDevice;
+} XrGraphicsRequirementsMetalKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetMetalGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsMetalKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMetalGraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsMetalKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_METAL */
+
+#ifdef XR_USE_PLATFORM_WIN32
+
+// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_win32_convert_performance_counter_time 1
+#define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1
+#define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time"
+typedef XrResult (XRAPI_PTR *PFN_xrConvertWin32PerformanceCounterToTimeKHR)(XrInstance instance, const LARGE_INTEGER* performanceCounter, XrTime* time);
+typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToWin32PerformanceCounterKHR)(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrConvertWin32PerformanceCounterToTimeKHR(
+ XrInstance instance,
+ const LARGE_INTEGER* performanceCounter,
+ XrTime* time);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR(
+ XrInstance instance,
+ XrTime time,
+ LARGE_INTEGER* performanceCounter);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_WIN32 */
+
+#ifdef XR_USE_TIMESPEC
+
+// XR_KHR_convert_timespec_time is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_convert_timespec_time 1
+#define XR_KHR_convert_timespec_time_SPEC_VERSION 1
+#define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time"
+typedef XrResult (XRAPI_PTR *PFN_xrConvertTimespecTimeToTimeKHR)(XrInstance instance, const struct timespec* timespecTime, XrTime* time);
+typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToTimespecTimeKHR)(XrInstance instance, XrTime time, struct timespec* timespecTime);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimespecTimeToTimeKHR(
+ XrInstance instance,
+ const struct timespec* timespecTime,
+ XrTime* time);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR(
+ XrInstance instance,
+ XrTime time,
+ struct timespec* timespecTime);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_TIMESPEC */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_KHR_loader_init_android is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_loader_init_android 1
+#define XR_KHR_loader_init_android_SPEC_VERSION 1
+#define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android"
+typedef struct XrLoaderInitInfoAndroidKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS applicationVM;
+ void* XR_MAY_ALIAS applicationContext;
+} XrLoaderInitInfoAndroidKHR;
+
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_KHR_vulkan_enable2 is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_vulkan_enable2 1
+#define XR_KHR_vulkan_enable2_SPEC_VERSION 4
+#define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2"
+typedef XrFlags64 XrVulkanInstanceCreateFlagsKHR;
+
+// Flag bits for XrVulkanInstanceCreateFlagsKHR
+
+typedef XrFlags64 XrVulkanDeviceCreateFlagsKHR;
+
+// Flag bits for XrVulkanDeviceCreateFlagsKHR
+
+typedef struct XrVulkanInstanceCreateInfoKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSystemId systemId;
+ XrVulkanInstanceCreateFlagsKHR createFlags;
+ PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
+ const VkInstanceCreateInfo* vulkanCreateInfo;
+ const VkAllocationCallbacks* vulkanAllocator;
+} XrVulkanInstanceCreateInfoKHR;
+
+typedef struct XrVulkanDeviceCreateInfoKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSystemId systemId;
+ XrVulkanDeviceCreateFlagsKHR createFlags;
+ PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
+ VkPhysicalDevice vulkanPhysicalDevice;
+ const VkDeviceCreateInfo* vulkanCreateInfo;
+ const VkAllocationCallbacks* vulkanAllocator;
+} XrVulkanDeviceCreateInfoKHR;
+
+typedef XrGraphicsBindingVulkanKHR XrGraphicsBindingVulkan2KHR;
+
+typedef struct XrVulkanGraphicsDeviceGetInfoKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSystemId systemId;
+ VkInstance vulkanInstance;
+} XrVulkanGraphicsDeviceGetInfoKHR;
+
+typedef XrSwapchainImageVulkanKHR XrSwapchainImageVulkan2KHR;
+
+typedef XrGraphicsRequirementsVulkanKHR XrGraphicsRequirementsVulkan2KHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirements2KHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanInstanceKHR(
+ XrInstance instance,
+ const XrVulkanInstanceCreateInfoKHR* createInfo,
+ VkInstance* vulkanInstance,
+ VkResult* vulkanResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanDeviceKHR(
+ XrInstance instance,
+ const XrVulkanDeviceCreateInfoKHR* createInfo,
+ VkDevice* vulkanDevice,
+ VkResult* vulkanResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDevice2KHR(
+ XrInstance instance,
+ const XrVulkanGraphicsDeviceGetInfoKHR* getInfo,
+ VkPhysicalDevice* vulkanPhysicalDevice);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_PLATFORM_EGL
+
+// XR_MNDX_egl_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_MNDX_egl_enable 1
+#define XR_MNDX_egl_enable_SPEC_VERSION 2
+#define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable"
+typedef PFN_xrVoidFunction (*PFN_xrEglGetProcAddressMNDX)(const char *name);
+// XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingEGLMNDX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ PFN_xrEglGetProcAddressMNDX getProcAddress;
+ EGLDisplay display;
+ EGLConfig config;
+ EGLContext context;
+} XrGraphicsBindingEGLMNDX;
+
+#endif /* XR_USE_PLATFORM_EGL */
+
+#ifdef XR_USE_PLATFORM_WIN32
+
+// XR_MSFT_perception_anchor_interop is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_perception_anchor_interop 1
+#define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1
+#define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop"
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT)(XrSession session, IUnknown* perceptionAnchor, XrSpatialAnchorMSFT* anchor);
+typedef XrResult (XRAPI_PTR *PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT)(XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPerceptionAnchorMSFT(
+ XrSession session,
+ IUnknown* perceptionAnchor,
+ XrSpatialAnchorMSFT* anchor);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(
+ XrSession session,
+ XrSpatialAnchorMSFT anchor,
+ IUnknown** perceptionAnchor);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_WIN32 */
+
+#ifdef XR_USE_PLATFORM_WIN32
+
+// XR_MSFT_holographic_window_attachment is a preprocessor guard. Do not pass it to API calls.
+#define XR_MSFT_holographic_window_attachment 1
+#define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1
+#define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment"
+#ifdef XR_USE_PLATFORM_WIN32
+// XrHolographicWindowAttachmentMSFT extends XrSessionCreateInfo
+typedef struct XrHolographicWindowAttachmentMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ IUnknown* holographicSpace;
+ IUnknown* coreWindow;
+} XrHolographicWindowAttachmentMSFT;
+#endif // XR_USE_PLATFORM_WIN32
+
+#endif /* XR_USE_PLATFORM_WIN32 */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_FB_android_surface_swapchain_create is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_android_surface_swapchain_create 1
+#define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1
+#define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create"
+typedef XrFlags64 XrAndroidSurfaceSwapchainFlagsFB;
+
+// Flag bits for XrAndroidSurfaceSwapchainFlagsFB
+static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_SYNCHRONOUS_BIT_FB = 0x00000001;
+static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_USE_TIMESTAMPS_BIT_FB = 0x00000002;
+
+#ifdef XR_USE_PLATFORM_ANDROID
+// XrAndroidSurfaceSwapchainCreateInfoFB extends XrSwapchainCreateInfo
+typedef struct XrAndroidSurfaceSwapchainCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAndroidSurfaceSwapchainFlagsFB createFlags;
+} XrAndroidSurfaceSwapchainCreateInfoFB;
+#endif // XR_USE_PLATFORM_ANDROID
+
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_PLATFORM_ML
+
+// XR_ML_compat is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_compat 1
+#define XR_ML_compat_SPEC_VERSION 1
+#define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat"
+typedef struct XrCoordinateSpaceCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ MLCoordinateFrameUID cfuid;
+ XrPosef poseInCoordinateSpace;
+} XrCoordinateSpaceCreateInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpaceFromCoordinateFrameUIDML)(XrSession session, const XrCoordinateSpaceCreateInfoML *createInfo, XrSpace* space);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML(
+ XrSession session,
+ const XrCoordinateSpaceCreateInfoML * createInfo,
+ XrSpace* space);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_ML */
+
+#ifdef XR_USE_PLATFORM_WIN32
+
+// XR_OCULUS_audio_device_guid is a preprocessor guard. Do not pass it to API calls.
+#define XR_OCULUS_audio_device_guid 1
+#define XR_OCULUS_audio_device_guid_SPEC_VERSION 1
+#define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid"
+#define XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS 128
+typedef XrResult (XRAPI_PTR *PFN_xrGetAudioOutputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
+typedef XrResult (XRAPI_PTR *PFN_xrGetAudioInputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioOutputDeviceGuidOculus(
+ XrInstance instance,
+ wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus(
+ XrInstance instance,
+ wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_WIN32 */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_FB_foveation_vulkan is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_foveation_vulkan 1
+#define XR_FB_foveation_vulkan_SPEC_VERSION 1
+#define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan"
+// XrSwapchainImageFoveationVulkanFB extends XrSwapchainImageVulkanKHR
+typedef struct XrSwapchainImageFoveationVulkanFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ VkImage image;
+ uint32_t width;
+ uint32_t height;
+} XrSwapchainImageFoveationVulkanFB;
+
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_FB_swapchain_update_state_android_surface is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_swapchain_update_state_android_surface 1
+#define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1
+#define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface"
+#ifdef XR_USE_PLATFORM_ANDROID
+typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t width;
+ uint32_t height;
+} XrSwapchainStateAndroidSurfaceDimensionsFB;
+#endif // XR_USE_PLATFORM_ANDROID
+
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+
+// XR_FB_swapchain_update_state_opengl_es is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_swapchain_update_state_opengl_es 1
+#define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1
+#define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es"
+#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+typedef struct XrSwapchainStateSamplerOpenGLESFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ EGLenum minFilter;
+ EGLenum magFilter;
+ EGLenum wrapModeS;
+ EGLenum wrapModeT;
+ EGLenum swizzleRed;
+ EGLenum swizzleGreen;
+ EGLenum swizzleBlue;
+ EGLenum swizzleAlpha;
+ float maxAnisotropy;
+ XrColor4f borderColor;
+} XrSwapchainStateSamplerOpenGLESFB;
+#endif // XR_USE_GRAPHICS_API_OPENGL_ES
+
+#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_FB_swapchain_update_state_vulkan is a preprocessor guard. Do not pass it to API calls.
+#define XR_FB_swapchain_update_state_vulkan 1
+#define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1
+#define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan"
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+typedef struct XrSwapchainStateSamplerVulkanFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ VkFilter minFilter;
+ VkFilter magFilter;
+ VkSamplerMipmapMode mipmapMode;
+ VkSamplerAddressMode wrapModeS;
+ VkSamplerAddressMode wrapModeT;
+ VkComponentSwizzle swizzleRed;
+ VkComponentSwizzle swizzleGreen;
+ VkComponentSwizzle swizzleBlue;
+ VkComponentSwizzle swizzleAlpha;
+ float maxAnisotropy;
+ XrColor4f borderColor;
+} XrSwapchainStateSamplerVulkanFB;
+#endif // XR_USE_GRAPHICS_API_VULKAN
+
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+// XR_META_vulkan_swapchain_create_info is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_vulkan_swapchain_create_info 1
+#define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1
+#define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info"
+// XrVulkanSwapchainCreateInfoMETA extends XrSwapchainCreateInfo
+typedef struct XrVulkanSwapchainCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ VkImageCreateFlags additionalCreateFlags;
+ VkImageUsageFlags additionalUsageFlags;
+} XrVulkanSwapchainCreateInfoMETA;
+
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
+#ifdef XR_USE_PLATFORM_ANDROID
+
+// XR_ANDROID_anchor_sharing_export is a preprocessor guard. Do not pass it to API calls.
+#define XR_ANDROID_anchor_sharing_export 1
+#define XR_ANDROID_anchor_sharing_export_SPEC_VERSION 1
+#define XR_ANDROID_ANCHOR_SHARING_EXPORT_EXTENSION_NAME "XR_ANDROID_anchor_sharing_export"
+typedef struct XrAnchorSharingInfoANDROID {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace anchor;
+} XrAnchorSharingInfoANDROID;
+
+typedef struct XrAnchorSharingTokenANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ struct AIBinder* token;
+} XrAnchorSharingTokenANDROID;
+
+// XrSystemAnchorSharingExportPropertiesANDROID extends XrSystemProperties
+typedef struct XrSystemAnchorSharingExportPropertiesANDROID {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsAnchorSharingExport;
+} XrSystemAnchorSharingExportPropertiesANDROID;
+
+typedef XrResult (XRAPI_PTR *PFN_xrShareAnchorANDROID)(XrSession session, const XrAnchorSharingInfoANDROID* sharingInfo, XrAnchorSharingTokenANDROID* anchorToken);
+typedef XrResult (XRAPI_PTR *PFN_xrUnshareAnchorANDROID)(XrSession session, XrSpace anchor);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrShareAnchorANDROID(
+ XrSession session,
+ const XrAnchorSharingInfoANDROID* sharingInfo,
+ XrAnchorSharingTokenANDROID* anchorToken);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUnshareAnchorANDROID(
+ XrSession session,
+ XrSpace anchor);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_ANDROID */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/frame/openxr-compat/vendor/openxr/openxr_platform_defines.h b/frame/openxr-compat/vendor/openxr/openxr_platform_defines.h
new file mode 100644
index 0000000..79c5b2c
--- /dev/null
+++ b/frame/openxr-compat/vendor/openxr/openxr_platform_defines.h
@@ -0,0 +1,114 @@
+/*
+** Copyright (c) 2017-2026 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0 OR MIT
+*/
+
+#ifndef OPENXR_PLATFORM_DEFINES_H_
+#define OPENXR_PLATFORM_DEFINES_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Platform-specific calling convention macros.
+ *
+ * Platforms should define these so that OpenXR clients call OpenXR functions
+ * with the same calling conventions that the OpenXR implementation expects.
+ *
+ * XRAPI_ATTR - Placed before the return type in function declarations.
+ * Useful for C++11 and GCC/Clang-style function attribute syntax.
+ * XRAPI_CALL - Placed after the return type in function declarations.
+ * Useful for MSVC-style calling convention syntax.
+ * XRAPI_PTR - Placed between the '(' and '*' in function pointer types.
+ *
+ * Function declaration: XRAPI_ATTR void XRAPI_CALL xrFunction(void);
+ * Function pointer type: typedef void (XRAPI_PTR *PFN_xrFunction)(void);
+ */
+#if defined(_WIN32)
+#define XRAPI_ATTR
+// On Windows, functions use the stdcall convention
+#define XRAPI_CALL __stdcall
+#define XRAPI_PTR XRAPI_CALL
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
+#error "API not supported for the 'armeabi' NDK ABI"
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
+// On Android 32-bit ARM targets, functions use the "hardfloat"
+// calling convention, i.e. float parameters are passed in registers. This
+// is true even if the rest of the application passes floats on the stack,
+// as it does by default when compiling for the armeabi-v7a NDK ABI.
+#define XRAPI_ATTR __attribute__((pcs("aapcs-vfp")))
+#define XRAPI_CALL
+#define XRAPI_PTR XRAPI_ATTR
+#else
+// On other platforms, use the default calling convention
+#define XRAPI_ATTR
+#define XRAPI_CALL
+#define XRAPI_PTR
+#endif
+
+#include
+
+#if !defined(XR_NO_STDINT_H)
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef signed __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef signed __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef signed __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include
+#endif
+#endif // !defined( XR_NO_STDINT_H )
+
+// XR_PTR_SIZE (in bytes)
+#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
+#define XR_PTR_SIZE 8
+#else
+#define XR_PTR_SIZE 4
+#endif
+
+// Needed so we can use clang __has_feature portably.
+#if !defined(XR_COMPILER_HAS_FEATURE)
+#if defined(__clang__)
+#define XR_COMPILER_HAS_FEATURE(x) __has_feature(x)
+#else
+#define XR_COMPILER_HAS_FEATURE(x) 0
+#endif
+#endif
+
+// Identifies if the current compiler has C++11 support enabled.
+// Does not by itself identify if any given C++11 feature is present.
+#if !defined(XR_CPP11_ENABLED) && defined(__cplusplus)
+#if defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define XR_CPP11_ENABLED 1
+#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define XR_CPP11_ENABLED 1
+#elif (__cplusplus >= 201103L) // 201103 is the first C++11 version.
+#define XR_CPP11_ENABLED 1
+#endif
+#endif
+
+// Identifies if the current compiler supports C++11 nullptr.
+#if !defined(XR_CPP_NULLPTR_SUPPORTED)
+#if defined(XR_CPP11_ENABLED) && \
+ ((defined(__clang__) && XR_COMPILER_HAS_FEATURE(cxx_nullptr)) || \
+ (defined(__GNUC__) && (((__GNUC__ * 1000) + __GNUC_MINOR__) >= 4006)) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1600)) || \
+ (defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403)))
+#define XR_CPP_NULLPTR_SUPPORTED 1
+#endif
+#endif
+
+#if !defined(XR_CPP_NULLPTR_SUPPORTED)
+#define XR_CPP_NULLPTR_SUPPORTED 0
+#endif // !defined(XR_CPP_NULLPTR_SUPPORTED)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/mac/frame-mac-view/Sources/CGVirtualDisplay.h b/mac/frame-mac-view/Sources/CGVirtualDisplay.h
new file mode 100644
index 0000000..f67f628
--- /dev/null
+++ b/mac/frame-mac-view/Sources/CGVirtualDisplay.h
@@ -0,0 +1,28 @@
+// Private CoreGraphics classes for virtual displays (as used by DeskPad,
+// BetterDisplay and quest-display). Not in any SDK; see
+// docs/mac-window-separation.md.
+#import
+#import
+@interface CGVirtualDisplayDescriptor : NSObject
+@property (retain, nonatomic) dispatch_queue_t queue;
+@property (retain, nonatomic) NSString *name;
+@property (nonatomic) unsigned int maxPixelsHigh;
+@property (nonatomic) unsigned int maxPixelsWide;
+@property (nonatomic) CGSize sizeInMillimeters;
+@property (nonatomic) unsigned int serialNum;
+@property (nonatomic) unsigned int productID;
+@property (nonatomic) unsigned int vendorID;
+@property (copy, nonatomic) void (^terminationHandler)(id, id);
+@end
+@interface CGVirtualDisplayMode : NSObject
+- (instancetype)initWithWidth:(unsigned int)width height:(unsigned int)height refreshRate:(double)refreshRate;
+@end
+@interface CGVirtualDisplaySettings : NSObject
+@property (retain, nonatomic) NSArray *modes;
+@property (nonatomic) unsigned int hiDPI;
+@end
+@interface CGVirtualDisplay : NSObject
+@property (readonly, nonatomic) unsigned int displayID;
+- (instancetype)initWithDescriptor:(CGVirtualDisplayDescriptor *)descriptor;
+- (BOOL)applySettings:(CGVirtualDisplaySettings *)settings;
+@end
diff --git a/mac/frame-mac-view/Sources/Capture.swift b/mac/frame-mac-view/Sources/Capture.swift
new file mode 100644
index 0000000..dcf3b3b
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Capture.swift
@@ -0,0 +1,414 @@
+// Where pictures come from: one Mac window or one display (ScreenCaptureKit),
+// or a generated test pattern that needs no permissions and shows the input
+// the viewer sends, for checking the whole path without touching the Mac.
+import AppKit
+import CoreMedia
+import CoreVideo
+import Foundation
+import ScreenCaptureKit
+
+enum Source: Equatable {
+ case window(CGWindowID)
+ case display(CGDirectDisplayID)
+ case separate(CGWindowID) // the window on a display of its own
+ case test
+
+ init?(_ s: String) {
+ let parts = s.split(separator: ":", maxSplits: 1).map(String.init)
+ switch (parts.first, parts.count > 1 ? UInt32(parts[1]) : nil) {
+ case ("window", let id?): self = .window(id)
+ case ("display", let id?): self = .display(id)
+ case ("separate", let id?): self = .separate(id)
+ case ("test", _): self = .test
+ default: return nil
+ }
+ }
+
+ var key: String {
+ switch self {
+ case .window(let id): return "window:\(id)"
+ case .display(let id): return "display:\(id)"
+ case .separate(let id): return "separate:\(id)"
+ case .test: return "test"
+ }
+ }
+}
+
+/// Size to capture at: the source's pixel size, shrunk to fit a box whose
+/// long side is `maxLong` (default 1920), even numbers for the encoder.
+func fitSize(width: Double, height: Double, maxLong: Int) -> (Int, Int) {
+ let scale = min(1, Double(maxLong) / max(width, height, 1))
+ let w = max(16, Int((width * scale / 2).rounded()) * 2), h = max(16, Int((height * scale / 2).rounded()) * 2)
+ return (w, h)
+}
+
+/// What CGWindowList says about a window right now (no permission needed for
+/// bounds; titles need Screen Recording).
+struct WindowInfo {
+ let id: CGWindowID
+ let pid: pid_t
+ let app: String
+ let title: String
+ let bounds: CGRect
+ let layer: Int
+ let onScreen: Bool
+
+ static func all(onScreenOnly: Bool = true) -> [WindowInfo] {
+ let opts: CGWindowListOption = onScreenOnly ? [.optionOnScreenOnly, .excludeDesktopElements] : [.excludeDesktopElements]
+ let list = CGWindowListCopyWindowInfo(opts, kCGNullWindowID) as? [[CFString: Any]] ?? []
+ return list.compactMap(WindowInfo.init)
+ }
+
+ static func find(_ id: CGWindowID) -> WindowInfo? {
+ let list = CGWindowListCopyWindowInfo(.optionIncludingWindow, id) as? [[CFString: Any]] ?? []
+ return list.compactMap(WindowInfo.init).first { $0.id == id }
+ }
+
+ init?(_ d: [CFString: Any]) {
+ guard let id = d[kCGWindowNumber] as? CGWindowID, let pid = d[kCGWindowOwnerPID] as? pid_t,
+ let b = d[kCGWindowBounds] as? [String: Any], let rect = CGRect(dictionaryRepresentation: b as CFDictionary)
+ else { return nil }
+ self.id = id
+ self.pid = pid
+ app = d[kCGWindowOwnerName] as? String ?? ""
+ title = d[kCGWindowName] as? String ?? ""
+ bounds = rect
+ layer = d[kCGWindowLayer] as? Int ?? 0
+ onScreen = d[kCGWindowIsOnscreen] as? Bool ?? false
+ }
+}
+
+protocol CaptureSource: AnyObject {
+ /// The picture, its presentation time, and when the Mac composited it (µs, host clock).
+ var onFrame: ((CVPixelBuffer, CMTime, Int64) -> Void)? { get set }
+ var onEnded: ((String) -> Void)? { get set }
+ var onChange: (() -> Void)? { get set } // title or size changed
+ /// True once the window or display is gone for good.
+ var gone: Bool { get }
+ /// Points on the Mac's global display space that the picture covers.
+ var frameRect: CGRect { get }
+ var title: String { get }
+ var app: String { get }
+ var pid: pid_t? { get }
+ func start(maxLong: Int, fps: Int, completion: @escaping (String?) -> Void)
+ /// A smaller or larger picture from now on (the long side, in pixels).
+ func setMaxLong(_ maxLong: Int)
+ func stop()
+ func pointer(x: Double, y: Double, text: String?) // for the test pattern
+}
+
+extension CaptureSource {
+ func pointer(x: Double, y: Double, text: String?) {}
+}
+
+/// ScreenCaptureKit, for a window or a display.
+final class SCKSource: NSObject, CaptureSource, SCStreamOutput, SCStreamDelegate {
+ let source: Source
+ var onFrame: ((CVPixelBuffer, CMTime, Int64) -> Void)?
+ var onEnded: ((String) -> Void)?
+ private(set) var frameRect = CGRect.zero
+ private(set) var title = ""
+ private(set) var app = ""
+ private(set) var pid: pid_t?
+ private var stream: SCStream?
+ private var config = SCStreamConfiguration()
+ private var maxLong = 1920
+ private var scale = 2.0
+ private var poll: DispatchSourceTimer?
+ private let queue = DispatchQueue(label: "frame-mac-view.capture", qos: .userInteractive)
+ /// Set by stop(), on `queue`; a start still enumerating windows checks it
+ /// before it starts capturing, so a viewer that left early leaves nothing on.
+ private var cancelled = false
+ /// The window or display no longer exists, so retrying can't help.
+ private(set) var gone = false
+ var onChange: (() -> Void)? // title or size changed
+ /// For a display: capture only this part of it (display points, top-left
+ /// origin). Set before start().
+ var crop: CGRect?
+
+ init(_ source: Source) { self.source = source }
+
+ /// What's captured, in global points: the display, or the cropped part of it.
+ private func displayRect(_ id: CGDirectDisplayID) -> CGRect {
+ let b = CGDisplayBounds(id)
+ guard let c = crop else { return b }
+ return c.offsetBy(dx: b.minX, dy: b.minY).intersection(b)
+ }
+
+ func start(maxLong: Int, fps: Int, completion: @escaping (String?) -> Void) {
+ self.maxLong = maxLong
+ SCShareableContent.getExcludingDesktopWindows(true, onScreenWindowsOnly: false) { [self] content, error in
+ queue.async { self.begin(content, error, fps: fps, completion: completion) }
+ }
+ }
+
+ private func begin(_ content: SCShareableContent?, _ error: Error?, fps: Int, completion: @escaping (String?) -> Void) {
+ if cancelled { return }
+ guard let content else {
+ return completion("Screen Recording isn't allowed for Frame Control (\(error?.localizedDescription ?? "no content"))")
+ }
+ let filter: SCContentFilter
+ switch source {
+ case .window(let id):
+ guard let w = content.windows.first(where: { $0.windowID == id }) else {
+ gone = true
+ return completion("that window has closed")
+ }
+ filter = SCContentFilter(desktopIndependentWindow: w)
+ title = w.title ?? ""
+ app = w.owningApplication?.applicationName ?? ""
+ pid = w.owningApplication?.processID
+ frameRect = w.frame
+ case .display(let id):
+ guard let d = content.displays.first(where: { $0.displayID == id }) else {
+ gone = true
+ return completion("that display isn't connected")
+ }
+ filter = SCContentFilter(display: d, excludingWindows: [])
+ title = displayName(id)
+ app = "Mac"
+ frameRect = displayRect(id)
+ if crop != nil { config.sourceRect = frameRect.offsetBy(dx: -CGDisplayBounds(id).minX, dy: -CGDisplayBounds(id).minY) }
+ case .test, .separate:
+ return completion("not a ScreenCaptureKit source")
+ }
+ scale = Double(filter.pointPixelScale)
+ // A display's own mode knows best: ScreenCaptureKit can still say 1
+ // for a virtual display that has only just switched to HiDPI.
+ if case .display(let id) = source, let mode = CGDisplayCopyDisplayMode(id), mode.width > 0 {
+ scale = max(scale, Double(mode.pixelWidth) / Double(mode.width))
+ }
+ let (w, h) = fitSize(width: frameRect.width * scale, height: frameRect.height * scale, maxLong: maxLong)
+ config.width = w
+ config.height = h
+ config.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(fps))
+ config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
+ config.colorMatrix = CGDisplayStream.yCbCrMatrix_ITU_R_709_2
+ config.colorSpaceName = CGColorSpace.sRGB
+ config.showsCursor = true
+ config.queueDepth = 5
+ config.scalesToFit = true
+ config.preservesAspectRatio = true
+ config.capturesAudio = false
+ let stream = SCStream(filter: filter, configuration: config, delegate: self)
+ do {
+ try stream.addStreamOutput(self, type: .screen, sampleHandlerQueue: queue)
+ } catch {
+ return completion("couldn't capture: \(error.localizedDescription)")
+ }
+ self.stream = stream
+ stream.startCapture { error in
+ self.queue.async {
+ if self.cancelled {
+ stream.stopCapture { _ in }
+ return
+ }
+ if let error { return completion("couldn't capture: \(error.localizedDescription)") }
+ self.startPolling()
+ completion(nil)
+ }
+ }
+ }
+
+ func stop() {
+ queue.async {
+ self.cancelled = true
+ self.poll?.cancel()
+ self.poll = nil
+ self.stream?.stopCapture { _ in }
+ self.stream = nil
+ }
+ }
+
+ /// Windows move, resize, retitle and close, and displays change mode;
+ /// ScreenCaptureKit doesn't say.
+ private func startPolling() {
+ if case .display(let id) = source { return pollDisplay(id) }
+ guard case .window(let id) = source else { return }
+ let t = DispatchSource.makeTimerSource(queue: queue)
+ t.schedule(deadline: .now() + 1, repeating: 1)
+ t.setEventHandler { [weak self] in
+ guard let self else { return }
+ guard let info = WindowInfo.find(id) else {
+ self.stop()
+ self.onEnded?("the window closed")
+ return
+ }
+ var changed = false
+ if !info.title.isEmpty, info.title != self.title { self.title = info.title; changed = true }
+ let old = self.frameRect
+ self.frameRect = info.bounds
+ if abs(old.width - info.bounds.width) > 1 || abs(old.height - info.bounds.height) > 1 {
+ let (w, h) = fitSize(width: info.bounds.width * self.scale, height: info.bounds.height * self.scale, maxLong: self.maxLong)
+ self.config.width = w
+ self.config.height = h
+ self.stream?.updateConfiguration(self.config) { _ in }
+ changed = true
+ }
+ if changed { self.onChange?() }
+ }
+ t.resume()
+ poll = t
+ }
+
+ func setMaxLong(_ n: Int) {
+ queue.async {
+ self.maxLong = n
+ guard self.stream != nil else { return }
+ let (w, h) = fitSize(width: self.frameRect.width * self.scale, height: self.frameRect.height * self.scale, maxLong: n)
+ guard w != self.config.width || h != self.config.height else { return }
+ self.config.width = w
+ self.config.height = h
+ self.stream?.updateConfiguration(self.config) { _ in }
+ }
+ }
+
+ private func pollDisplay(_ id: CGDirectDisplayID) {
+ let t = DispatchSource.makeTimerSource(queue: queue)
+ t.schedule(deadline: .now() + 0.5, repeating: 1)
+ t.setEventHandler { [weak self] in
+ guard let self, let mode = CGDisplayCopyDisplayMode(id), mode.width > 0 else { return }
+ let bounds = self.displayRect(id), scale = Double(mode.pixelWidth) / Double(mode.width)
+ let (w, h) = fitSize(width: bounds.width * scale, height: bounds.height * scale, maxLong: self.maxLong)
+ self.frameRect = bounds
+ guard w != self.config.width || h != self.config.height else { return }
+ self.scale = scale
+ self.config.width = w
+ self.config.height = h
+ self.stream?.updateConfiguration(self.config) { _ in }
+ self.onChange?()
+ }
+ t.resume()
+ poll = t
+ }
+
+ func stream(_ stream: SCStream, didOutputSampleBuffer sample: CMSampleBuffer, of type: SCStreamOutputType) {
+ guard type == .screen, sample.isValid, let pb = CMSampleBufferGetImageBuffer(sample) else { return }
+ // Only complete frames carry new pixels; idle and blank ones don't.
+ let info = (CMSampleBufferGetSampleAttachmentsArray(sample, createIfNecessary: false) as? [[SCStreamFrameInfo: Any]])?.first
+ if let raw = info?[.status] as? Int, let status = SCFrameStatus(rawValue: raw), status != .complete {
+ return
+ }
+ let pts = CMSampleBufferGetPresentationTimeStamp(sample)
+ // When WindowServer composited it: the moment it showed on the Mac.
+ let shown = (info?[.displayTime] as? UInt64).map(hostUs) ?? hostUs(pts)
+ onFrame?(pb, pts, shown)
+ }
+
+ func stream(_ stream: SCStream, didStopWithError error: Error) {
+ onEnded?("capture stopped: \(error.localizedDescription)")
+ }
+}
+
+func displayName(_ id: CGDirectDisplayID) -> String {
+ for screen in NSScreen.screens {
+ if (screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value == id {
+ return screen.localizedName
+ }
+ }
+ return "Display \(id)"
+}
+
+/// A moving test card: bars, a clock and a frame counter, plus a dot where the
+/// viewer's pointer is and the last key it sent, so input can be checked too.
+final class TestSource: CaptureSource {
+ var onFrame: ((CVPixelBuffer, CMTime, Int64) -> Void)?
+ var onEnded: ((String) -> Void)?
+ var onChange: (() -> Void)?
+ let gone = false
+ let frameRect = CGRect(x: 0, y: 0, width: 1280, height: 720)
+ let title = "Test pattern"
+ let app = "Frame Control"
+ let pid: pid_t? = nil
+ private var timer: DispatchSourceTimer?
+ private var pool: CVPixelBufferPool?
+ private var n = 0
+ private var dot: (Double, Double)?
+ private var lastText = "Point or type in the headset"
+ private let queue = DispatchQueue(label: "frame-mac-view.test")
+ private var size = (1280, 720)
+
+ func start(maxLong: Int, fps: Int, completion: @escaping (String?) -> Void) {
+ size = fitSize(width: 1280, height: 720, maxLong: maxLong)
+ makePool()
+ let t = DispatchSource.makeTimerSource(queue: queue)
+ t.schedule(deadline: .now(), repeating: 1.0 / Double(fps))
+ t.setEventHandler { [weak self] in self?.draw() }
+ t.resume()
+ timer = t
+ completion(nil)
+ }
+
+ func stop() {
+ timer?.cancel()
+ timer = nil
+ }
+
+ func setMaxLong(_ n: Int) {
+ queue.async {
+ self.size = fitSize(width: 1280, height: 720, maxLong: n)
+ self.makePool()
+ }
+ }
+
+ private func makePool() {
+ let attrs: [CFString: Any] = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_32BGRA,
+ kCVPixelBufferWidthKey: size.0, kCVPixelBufferHeightKey: size.1,
+ kCVPixelBufferIOSurfacePropertiesKey: [:] as CFDictionary]
+ pool = nil
+ CVPixelBufferPoolCreate(nil, nil, attrs as CFDictionary, &pool)
+ }
+
+ func pointer(x: Double, y: Double, text: String?) {
+ queue.async {
+ if x >= 0 { self.dot = (x, y) }
+ if let text { self.lastText = text }
+ }
+ }
+
+ private func draw() {
+ guard let pool else { return }
+ var out: CVPixelBuffer?
+ CVPixelBufferPoolCreatePixelBuffer(nil, pool, &out)
+ guard let pb = out else { return }
+ CVPixelBufferLockBaseAddress(pb, [])
+ defer { CVPixelBufferUnlockBaseAddress(pb, []) }
+ let (w, h) = size
+ guard let ctx = CGContext(data: CVPixelBufferGetBaseAddress(pb), width: w, height: h, bitsPerComponent: 8,
+ bytesPerRow: CVPixelBufferGetBytesPerRow(pb), space: CGColorSpace(name: CGColorSpace.sRGB)!,
+ bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
+ else { return }
+ let colors: [(CGFloat, CGFloat, CGFloat)] = [(0.75, 0.75, 0.75), (0.75, 0.75, 0), (0, 0.75, 0.75), (0, 0.75, 0),
+ (0.75, 0, 0.75), (0.75, 0, 0), (0, 0, 0.75)]
+ let bw = CGFloat(w) / CGFloat(colors.count)
+ for (i, c) in colors.enumerated() {
+ ctx.setFillColor(red: c.0, green: c.1, blue: c.2, alpha: 1)
+ ctx.fill(CGRect(x: CGFloat(i) * bw, y: CGFloat(h) * 0.35, width: bw + 1, height: CGFloat(h) * 0.65))
+ }
+ ctx.setFillColor(red: 0.08, green: 0.09, blue: 0.11, alpha: 1)
+ ctx.fill(CGRect(x: 0, y: 0, width: w, height: Int(Double(h) * 0.35)))
+ // A bar sweeping once a second shows motion and dropped frames.
+ let x = CGFloat(n % 60) / 60 * CGFloat(w)
+ ctx.setFillColor(red: 1, green: 1, blue: 1, alpha: 1)
+ ctx.fill(CGRect(x: x, y: CGFloat(h) * 0.35, width: max(4, CGFloat(w) / 120), height: CGFloat(h) * 0.65))
+ let f = DateFormatter()
+ f.dateFormat = "HH:mm:ss.SSS"
+ let label = "Frame Control test pattern \(f.string(from: Date())) frame \(n)\n\(lastText)"
+ let text = NSAttributedString(string: label, attributes: [
+ .font: NSFont.monospacedSystemFont(ofSize: CGFloat(h) / 24, weight: .medium),
+ .foregroundColor: NSColor.white,
+ ])
+ let ns = NSGraphicsContext(cgContext: ctx, flipped: false)
+ NSGraphicsContext.saveGraphicsState()
+ NSGraphicsContext.current = ns
+ text.draw(at: CGPoint(x: CGFloat(w) * 0.03, y: CGFloat(h) * 0.08))
+ NSGraphicsContext.restoreGraphicsState()
+ if let (px, py) = dot {
+ let r = CGFloat(h) / 40
+ ctx.setFillColor(red: 1, green: 0.2, blue: 0.2, alpha: 1)
+ ctx.fillEllipse(in: CGRect(x: CGFloat(px) * CGFloat(w) - r, y: (1 - CGFloat(py)) * CGFloat(h) - r, width: 2 * r, height: 2 * r))
+ }
+ n += 1
+ onFrame?(pb, CMClockGetTime(CMClockGetHostTimeClock()), nowUs())
+ }
+}
diff --git a/mac/frame-mac-view/Sources/Controller.swift b/mac/frame-mac-view/Sources/Controller.swift
new file mode 100644
index 0000000..2cb4b3b
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Controller.swift
@@ -0,0 +1,234 @@
+// Adapts each stream to its network, latency first. The viewer acknowledges
+// every frame as it arrives ("rx"); from those acks the controller knows how
+// long frames take to get through and how fast the link delivers them.
+//
+// - The gate: a new frame is sent only while the oldest unacknowledged one is
+// younger than the path's usual round trip plus a little slack. So frames
+// never queue up in SSH, TCP or the Wi-Fi driver; while the link is stuck
+// the newest picture waits and goes out as soon as it moves again.
+// - The bitrate: when frames start queueing (the round trip grows) or the
+// gate has to hold frames back, it drops to a bit under what the link
+// actually delivered; once things are clear it probes up again slowly, never
+// above the quality setting's bitrate (the ceiling).
+// - The tier: as the bitrate falls, fewer frames per second (60, 45, 30), then
+// a smaller picture (75%, then 50% of the panel's pixels).
+// See docs/mac-in-headset.md ("Adapting to the network"). Thread-safe.
+import Foundation
+
+final class RateController {
+ struct Tier: Equatable {
+ let fps: Int
+ let scale: Double // of the picture's long side
+ }
+
+ static let tiers = [Tier(fps: 60, scale: 1), Tier(fps: 45, scale: 1), Tier(fps: 30, scale: 1),
+ Tier(fps: 30, scale: 0.75), Tier(fps: 30, scale: 0.5)]
+ /// A tier is used while the target bitrate is at least this share of the ceiling.
+ static let floors = [0.45, 0.28, 0.16, 0.08, 0]
+ static let enabled = ProcessInfo.processInfo.environment["FRAME_MAC_VIEW_ADAPT"] != "0"
+
+ let maxFps: Int
+ private let lock = NSLock()
+ private var ceiling = 0 // bits/s at full size and frame rate
+ private(set) var target = 0
+ private(set) var tier = 0
+ private var unacked: [(seq: UInt32, sent: Int64, bytes: Int)] = []
+ /// Round trips (send -> ack arrives here), for the baseline: the lowest
+ /// in the last 10 s is the path without any queue.
+ private var rtts: [(t: Int64, v: Int64)] = []
+ private var acked: [(t: Int64, bytes: Int)] = [] // the last second
+ private var sentLog: [(t: Int64, bytes: Int)] = []
+ private var captures: [Int64] = [] // the last second
+ private var frameBytes = 0 // average recent frame, kept while the gate holds everything back
+ private var held = 0 // frames the gate held back since the last update
+ private var lastSignal = false
+ private var sawAck = false
+ private var lastDecrease: Int64 = 0
+ private var lastIncrease: Int64 = 0
+ private var belowSince: Int64 = 0, aboveSince: Int64 = 0
+ /// What changed, for the timeline: (time, event).
+ private(set) var events: [(Int64, String)] = []
+
+ init(maxFps: Int) { self.maxFps = maxFps }
+
+ private func locked(_ f: () -> T) -> T { lock.lock(); defer { lock.unlock() }; return f() }
+
+ /// The quality setting's bitrate at full size; the first call also starts there.
+ func setCeiling(_ bps: Int) {
+ locked {
+ if target == 0 || target > bps { target = bps }
+ ceiling = bps
+ }
+ }
+
+ var fps: Int { locked { fpsLocked } }
+ private var fpsLocked: Int { min(maxFps, RateController.tiers[tier].fps) }
+ var scale: Double { locked { RateController.tiers[tier].scale } }
+ var baseRtt: Int64 { locked { baseline() } }
+
+ private func baseline() -> Int64 { rtts.map(\.v).min() ?? 0 }
+
+ /// How late a frame may be before the gate holds the next one: one frame
+ /// interval, plus room for the jitter this link normally has (1.5 times
+ /// its recent spread), so ordinary Wi-Fi jitter doesn't cost frames but a
+ /// real queue does. Updated in update().
+ private var slack: Int64 = 40_000
+
+ /// Whether a frame may be sent now without queueing behind earlier ones.
+ /// `counts`: a held frame is a sign of congestion (not when merely
+ /// re-checking whether a held frame can go yet).
+ func maySend(now: Int64, counts: Bool = true) -> Bool {
+ locked {
+ guard RateController.enabled, sawAck else { return true } // not heard from the viewer yet
+ // Unacknowledged for 2 s: gone with a reconnection, not in a queue.
+ unacked.removeAll { now - $0.sent > 2_000_000 }
+ guard let oldest = unacked.first else { return true }
+ // Age is what bounds latency. The count only stops a burst, and it
+ // allows a full round trip of frames, so a long but clear path
+ // (100 ms away) still gets every frame.
+ let interval = Int64(1_000_000 / max(1, fpsLocked))
+ let window = max(3, Int((baseline() + slack) / interval) + 1)
+ if unacked.count < window, now - oldest.sent <= baseline() + slack { return true }
+ if counts { held += 1 }
+ return false
+ }
+ }
+
+ /// A picture was captured (sent or not): with the frame sizes, what this
+ /// stream would send if the link allowed.
+ func captured(at t: Int64) {
+ locked {
+ captures.append(t)
+ if captures.count > 256 { captures.removeFirst(captures.count - 256) }
+ }
+ }
+
+ func sent(seq: UInt32, bytes: Int, at t: Int64) {
+ locked {
+ // Bounded even if the viewer never acknowledges (an old viewer, or
+ // the controller is off).
+ unacked.append((seq, t, bytes))
+ if unacked.count > 512 { unacked.removeFirst(unacked.count - 512) }
+ sentLog.append((t, bytes))
+ if sentLog.count > 1024 { sentLog.removeFirst(sentLog.count - 1024) }
+ }
+ }
+
+ /// The viewer has frame `seq`. Returns true if that may let a held frame go.
+ func acked(seq: UInt32, at now: Int64) -> Bool {
+ locked {
+ sawAck = true
+ guard let i = unacked.firstIndex(where: { $0.seq == seq }) else { return false }
+ let f = unacked[i]
+ unacked.removeSubrange(0...i) // TCP delivers in order: earlier ones arrived too
+ rtts.append((now, now - f.sent))
+ acked.append((now, f.bytes))
+ return true
+ }
+ }
+
+ /// Called every 100 ms. Returns the new bitrate target, or nil if the
+ /// controller is off.
+ func update(now: Int64) -> Int? {
+ locked {
+ rtts.removeAll { now - $0.t > 10_000_000 }
+ acked.removeAll { now - $0.t > 500_000 }
+ sentLog.removeAll { now - $0.t > 500_000 }
+ unacked.removeAll { now - $0.sent > 2_000_000 }
+ captures.removeAll { now - $0 > 1_000_000 }
+ guard RateController.enabled, ceiling > 0 else { return nil }
+ let base = baseline()
+ let spread = rtts.filter { now - $0.t < 2_000_000 }.map(\.v).sorted()
+ let jitter = spread.isEmpty ? 0 : spread[spread.count * 9 / 10] - base
+ let interval = Int64(1_000_000 / max(1, fpsLocked))
+ slack = interval + min(max(jitter * 3 / 2, 25_000), 80_000)
+ let recent = rtts.filter { now - $0.t < 300_000 }.map(\.v).sorted()
+ let queueing = recent.isEmpty ? 0 : recent[recent.count / 2] - base
+ let oldestAge = unacked.first.map { now - $0.sent } ?? 0
+ let stuck = oldestAge > base + 100_000
+ let delivered = acked.reduce(0) { $0 + $1.bytes } * 16 // bits/s over the last half second
+ let sending = sentLog.reduce(0) { $0 + $1.bytes } * 16
+ // Demand: captures per second (up to the tier's rate) times the
+ // average frame. A test card or a mostly still window wants far
+ // less than its budget; when the link hiccups, cutting its bitrate
+ // can't help, and it would only look link-limited afterwards.
+ if !sentLog.isEmpty { frameBytes = sentLog.reduce(0) { $0 + $1.bytes } / sentLog.count }
+ let demand = min(captures.count, fpsLocked) * frameBytes * 8
+ // Delay alone isn't our queue: Wi-Fi jitters by itself. It only
+ // counts while this stream uses a good part of its budget (so its
+ // own data could be what's queueing). Frames the gate had to hold,
+ // or one stuck in flight, show demand the link isn't carrying
+ // whatever was sent (the gate itself keeps what's sent low).
+ let busy = sending >= target / 2
+ // Twice in a row (200 ms), so one late ack doesn't count.
+ let signal = (busy && queueing > 40_000) || held >= 3 || stuck
+ let congested = signal && lastSignal
+ lastSignal = signal
+ let heldNow = held
+ held = 0
+ let floorBps = 300_000
+ if congested, now - lastDecrease > 300_000 {
+ // Down to a bit under what got through: at least a fifth off, at
+ // most half (a stall delivers nothing, but the link is still there).
+ let measured = Int(Double(delivered) * 0.9)
+ var next = max(floorBps, min(target * 4 / 5, max(measured, target / 2)))
+ // App-limited (it wants about half its budget or less): never
+ // below twice what it wants, however many cuts in a row. The
+ // extra quarter is hysteresis, so frame sizes wobbling at the
+ // floor don't switch the protection off.
+ if demand > 0, demand * 2 <= target * 5 / 4 { next = max(next, min(target, demand * 2)) }
+ target = next
+ lastDecrease = now
+ events.append((now, "down to \(target / 1000) kbit/s: queue \(queueing / 1000) ms, held \(heldNow), "
+ + "oldest \(oldestAge / 1000) ms, base \(base / 1000) ms, sent \(sending / 1000) got \(delivered / 1000) "
+ + "wants \(demand / 1000)"))
+ } else if !congested, now - lastDecrease > 1_000_000, now - lastIncrease > 250_000, target < ceiling,
+ sending > target * 6 / 10 || now - lastDecrease > 3_000_000 {
+ // Clear for a second and using what it has: probe up.
+ target = min(ceiling, Int(Double(target) * 1.1) + 50_000)
+ lastIncrease = now
+ }
+ // Fewer frames or pixels only help a stream that fills its budget;
+ // a small one (a still window, a test card) keeps its tier.
+ retier(now: now, linkLimited: sending >= target * 7 / 10)
+ if events.count > 200 { events.removeFirst(events.count - 200) }
+ return target
+ }
+ }
+
+ /// Steps down quickly, straight to the tier the bitrate supports, and back
+ /// up one tier at a time only when there's clearly room (hysteresis).
+ private func retier(now: Int64, linkLimited: Bool) {
+ let share = Double(target) / Double(max(ceiling, 1))
+ if tier < RateController.tiers.count - 1, share < RateController.floors[tier], linkLimited {
+ if belowSince == 0 { belowSince = now }
+ if now - belowSince > 500_000 {
+ tier = RateController.floors.firstIndex { share >= $0 } ?? RateController.tiers.count - 1
+ belowSince = 0
+ events.append((now, "tier \(tier)"))
+ }
+ } else {
+ belowSince = 0
+ }
+ if tier > 0, share > RateController.floors[tier - 1] * 1.25 {
+ if aboveSince == 0 { aboveSince = now }
+ if now - aboveSince > 2_000_000 {
+ tier -= 1
+ aboveSince = 0
+ events.append((now, "tier \(tier)"))
+ }
+ } else {
+ aboveSince = 0
+ }
+ }
+
+ func state() -> [String: Any] {
+ locked {
+ ["target": target, "ceiling": ceiling, "tier": tier, "fps": min(maxFps, RateController.tiers[tier].fps),
+ "scale": RateController.tiers[tier].scale, "baseRtt": Double(baseline()) / 1000,
+ "inFlight": unacked.count, "slack": Double(slack) / 1000, "adapt": RateController.enabled]
+ }
+ }
+
+ func eventList() -> [[String: Any]] { locked { events.map { ["t": $0.0, "e": $0.1] } } }
+}
diff --git a/mac/frame-mac-view/Sources/Encoder.swift b/mac/frame-mac-view/Sources/Encoder.swift
new file mode 100644
index 0000000..d3b507a
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Encoder.swift
@@ -0,0 +1,217 @@
+// VideoToolbox encoding: H.264 for the normal path (hardware, low-latency rate
+// control, no B-frames, Annex B output that WebCodecs takes without a
+// description), or JPEG stills for viewers that can't decode H.264.
+import CoreMedia
+import Foundation
+import VideoToolbox
+
+enum Codec: String {
+ case h264, jpeg
+}
+
+final class Encoder {
+ let codec: Codec
+ let fps: Int
+ let bitsPerPixel: Double
+ private(set) var width = 0
+ private(set) var height = 0
+ private var session: VTCompressionSession?
+ private var forceKey = true
+ private var lastPts = CMTime.invalid
+ private var lastGiven = CMTime.invalid
+ /// Bits per second to aim for; nil means from the size, frame rate and
+ /// bits per pixel. Changing it takes effect on the next frame.
+ private var target: Int?
+ private(set) var bitrate = 0
+ /// Called on VideoToolbox's thread with one access unit (or JPEG) per
+ /// frame, and the sequence number it was submitted with.
+ var onFrame: ((Data, Bool, CMTime, UInt32) -> Void)?
+ var onError: ((String) -> Void)?
+ /// Called once per frame handed to VideoToolbox, however it went.
+ var onDone: ((UInt32) -> Void)?
+ /// Experiment switches (FRAME_MAC_VIEW_ENCODER, comma-separated), so a
+ /// benchmark can compare them without a rebuild.
+ static let options = Set((ProcessInfo.processInfo.environment["FRAME_MAC_VIEW_ENCODER"] ?? "")
+ .split(separator: ",").map(String.init))
+
+ init(codec: Codec, fps: Int, bitsPerPixel: Double) {
+ self.codec = codec
+ self.fps = fps
+ self.bitsPerPixel = bitsPerPixel
+ }
+
+ /// The bitrate the size and quality setting would give.
+ func defaultBitrate(width w: Int, height h: Int) -> Int {
+ Int(min(max(Double(w * h * fps) * bitsPerPixel, 2_000_000), 60_000_000))
+ }
+
+ /// Live, without a new keyframe. Call on the encoding queue.
+ func setBitrate(_ bps: Int?) {
+ target = bps
+ guard codec == .h264, let s = session else { return }
+ let b = bps ?? defaultBitrate(width: width, height: height)
+ guard b != bitrate else { return }
+ bitrate = b
+ VTSessionSetProperty(s, key: kVTCompressionPropertyKey_AverageBitRate, value: b as CFTypeRef)
+ // A hard ceiling too: no more than 200 ms' worth of bits in any 200 ms,
+ // so a keyframe can't hold the link for long.
+ if Encoder.options.contains("cap") {
+ VTSessionSetProperty(s, key: kVTCompressionPropertyKey_DataRateLimits,
+ value: [b / 8 / 5, 0.2] as CFArray)
+ }
+ }
+
+ deinit { invalidate() }
+
+ func requestKeyFrame() { forceKey = true }
+
+ func invalidate() {
+ if let s = session {
+ VTCompressionSessionCompleteFrames(s, untilPresentationTimeStamp: .invalid)
+ VTCompressionSessionInvalidate(s)
+ }
+ session = nil
+ }
+
+ private func makeSession(width w: Int, height h: Int) -> Bool {
+ invalidate()
+ var spec: [CFString: Any] = [:]
+ if codec == .h264 { spec[kVTVideoEncoderSpecification_EnableLowLatencyRateControl] = true }
+ if Encoder.options.contains("hw") { spec[kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder] = true }
+ var s: VTCompressionSession?
+ let type = codec == .h264 ? kCMVideoCodecType_H264 : kCMVideoCodecType_JPEG
+ func create(_ spec: [CFString: Any]) -> OSStatus {
+ VTCompressionSessionCreate(allocator: nil, width: Int32(w), height: Int32(h), codecType: type,
+ encoderSpecification: spec as CFDictionary, imageBufferAttributes: nil,
+ compressedDataAllocator: nil, outputCallback: nil, refcon: nil,
+ compressionSessionOut: &s)
+ }
+ var err = create(spec)
+ // Low-latency rate control needs Apple's hardware encoder; without it
+ // (some VMs), plain real-time encoding still works.
+ if err != noErr, !spec.isEmpty { err = create([:]) }
+ guard err == noErr, let s else {
+ onError?("couldn't start the \(codec.rawValue) encoder (VideoToolbox \(err))")
+ return false
+ }
+ func set(_ key: CFString, _ value: Any) { VTSessionSetProperty(s, key: key, value: value as CFTypeRef) }
+ set(kVTCompressionPropertyKey_RealTime, true)
+ set(kVTCompressionPropertyKey_ColorPrimaries, kCVImageBufferColorPrimaries_ITU_R_709_2)
+ set(kVTCompressionPropertyKey_TransferFunction, kCVImageBufferTransferFunction_ITU_R_709_2)
+ set(kVTCompressionPropertyKey_YCbCrMatrix, kCVImageBufferYCbCrMatrix_ITU_R_709_2)
+ if codec == .h264 {
+ set(kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_ConstrainedHigh_AutoLevel)
+ set(kVTCompressionPropertyKey_AllowFrameReordering, false)
+ set(kVTCompressionPropertyKey_ExpectedFrameRate, fps)
+ if Encoder.options.contains("nodelay") { set(kVTCompressionPropertyKey_MaxFrameDelayCount, 0) }
+ if Encoder.options.contains("speed") { set(kVTCompressionPropertyKey_PrioritizeEncodingSpeedOverQuality, true) }
+ // A keyframe every 10 s at most, so a viewer that lost one recovers
+ // even if it never asks. Viewers ask for one when they start.
+ set(kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, 10)
+ } else {
+ set(kVTCompressionPropertyKey_Quality, 0.8)
+ }
+ VTCompressionSessionPrepareToEncodeFrames(s)
+ session = s
+ lastPts = .invalid
+ lastGiven = .invalid
+ width = w
+ height = h
+ forceKey = true
+ bitrate = 0
+ setBitrate(target)
+ return true
+ }
+
+ /// False if the frame never reached VideoToolbox (then onDone won't come).
+ @discardableResult
+ func encode(_ pb: CVPixelBuffer, pts given: CMTime, seq: UInt32) -> Bool {
+ // The encoder's own timeline: strictly increasing (a resent picture
+ // stamped "now" can be followed by a capture stamped a moment earlier),
+ // and never more than two frames on from the last one. Rate control
+ // budgets bits by elapsed time, so after a pause (the link held frames
+ // back, or nothing changed) one frame would otherwise get a quarter
+ // second's worth of bits: 200 KB that then hold a slow link for half a second.
+ let w = CVPixelBufferGetWidth(pb), h = CVPixelBufferGetHeight(pb)
+ if session == nil || w != width || h != height {
+ guard makeSession(width: w, height: h) else { return false } // a new timeline too
+ }
+ guard let s = session else { return false }
+ var pts = given
+ if lastPts.isValid, lastGiven.isValid {
+ let gap = CMTimeSubtract(given, lastGiven)
+ let most = CMTime(value: 2, timescale: CMTimeScale(fps))
+ let step = CMTimeCompare(gap, most) > 0 ? most : gap
+ pts = CMTimeAdd(lastPts, CMTimeMaximum(step, CMTime(value: 1, timescale: 1_000_000)))
+ }
+ lastGiven = given
+ var props: CFDictionary?
+ if forceKey {
+ props = [kVTEncodeFrameOptionKey_ForceKeyFrame: true] as CFDictionary
+ forceKey = false
+ }
+ let codec = self.codec
+ lastPts = pts
+ let status = VTCompressionSessionEncodeFrame(s, imageBuffer: pb, presentationTimeStamp: pts, duration: .invalid,
+ frameProperties: props, infoFlagsOut: nil) { [weak self] status, _, sample in
+ guard let self else { return }
+ defer { self.onDone?(seq) }
+ guard status == noErr, let sample else { return }
+ if codec == .jpeg {
+ if let data = Self.bytes(sample) { self.onFrame?(data, true, pts, seq) }
+ } else if let (data, key) = Self.annexB(sample) {
+ self.onFrame?(data, key, pts, seq)
+ }
+ }
+ if status != noErr { forceKey = true }
+ return status == noErr
+ }
+
+ private static func bytes(_ sample: CMSampleBuffer) -> Data? {
+ guard let block = CMSampleBufferGetDataBuffer(sample) else { return nil }
+ var length = 0
+ var ptr: UnsafeMutablePointer?
+ guard CMBlockBufferGetDataPointer(block, atOffset: 0, lengthAtOffsetOut: nil, totalLengthOut: &length,
+ dataPointerOut: &ptr) == noErr, let ptr else { return nil }
+ return Data(bytes: ptr, count: length)
+ }
+
+ /// AVCC sample -> Annex B access unit, with SPS and PPS before keyframes.
+ static func annexB(_ sample: CMSampleBuffer) -> (Data, Bool)? {
+ guard let avcc = bytes(sample) else { return nil }
+ var key = true
+ if let atts = CMSampleBufferGetSampleAttachmentsArray(sample, createIfNecessary: false) as? [[CFString: Any]],
+ let first = atts.first, first[kCMSampleAttachmentKey_NotSync] as? Bool == true {
+ key = false
+ }
+ let start: [UInt8] = [0, 0, 0, 1]
+ var out = Data()
+ if key, let fmt = CMSampleBufferGetFormatDescription(sample) {
+ var count = 0
+ CMVideoFormatDescriptionGetH264ParameterSetAtIndex(fmt, parameterSetIndex: 0, parameterSetPointerOut: nil,
+ parameterSetSizeOut: nil, parameterSetCountOut: &count,
+ nalUnitHeaderLengthOut: nil)
+ for i in 0..?
+ var n = 0
+ if CMVideoFormatDescriptionGetH264ParameterSetAtIndex(fmt, parameterSetIndex: i, parameterSetPointerOut: &p,
+ parameterSetSizeOut: &n, parameterSetCountOut: nil,
+ nalUnitHeaderLengthOut: nil) == noErr, let p {
+ out.append(contentsOf: start)
+ out.append(p, count: n)
+ }
+ }
+ }
+ let bytes = [UInt8](avcc)
+ var i = 0
+ while i + 4 <= bytes.count {
+ let n = Int(bytes[i]) << 24 | Int(bytes[i + 1]) << 16 | Int(bytes[i + 2]) << 8 | Int(bytes[i + 3])
+ i += 4
+ guard n > 0, i + n <= bytes.count else { break }
+ out.append(contentsOf: start)
+ out.append(contentsOf: bytes[i..<(i + n)])
+ i += n
+ }
+ return (out, key)
+ }
+}
diff --git a/mac/frame-mac-view/Sources/Input.swift b/mac/frame-mac-view/Sources/Input.swift
new file mode 100644
index 0000000..0f51efc
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Input.swift
@@ -0,0 +1,168 @@
+// Turns the viewer's pointer and key events into real Mac input. Needs the
+// Accessibility permission ("control your computer"); without it macOS drops
+// the events silently, so the agent reports the permission to the viewer.
+import AppKit
+import ApplicationServices
+import Foundation
+
+@_silgen_name("_AXUIElementGetWindow")
+private func _AXUIElementGetWindow(_ element: AXUIElement, _ id: UnsafeMutablePointer) -> AXError
+
+enum Input {
+ static let source = CGEventSource(stateID: .hidSystemState)
+ /// What each viewer (session id) is holding down, so one panel closing
+ /// lets go of its own keys and buttons and nobody else's. Main thread.
+ private static var buttonsHeld: [Int: Set] = [:]
+ private static var keysHeld: [Int: Set] = [:]
+
+ private static var lastDown: (time: TimeInterval, point: CGPoint, button: Int, count: Int)?
+
+ static var allowed: Bool { AXIsProcessTrusted() }
+
+ /// Brings a window to the front so a click lands on it, not on whatever
+ /// covers it, and typing goes to it.
+ static func focus(window id: CGWindowID, pid: pid_t) {
+ if frontWindow() == id { return }
+ let app = AXUIElementCreateApplication(pid)
+ var value: CFTypeRef?
+ if AXUIElementCopyAttributeValue(app, kAXWindowsAttribute as CFString, &value) == .success,
+ let windows = value as? [AXUIElement] {
+ for w in windows {
+ var wid: CGWindowID = 0
+ if _AXUIElementGetWindow(w, &wid) == .success, wid == id {
+ AXUIElementPerformAction(w, kAXRaiseAction as CFString)
+ AXUIElementSetAttributeValue(w, kAXMainAttribute as CFString, kCFBooleanTrue)
+ break
+ }
+ }
+ }
+ AXUIElementSetAttributeValue(app, kAXFrontmostAttribute as CFString, kCFBooleanTrue)
+ NSRunningApplication(processIdentifier: pid)?.activate()
+ }
+
+ static func frontWindow() -> CGWindowID? {
+ WindowInfo.all().first { $0.layer == 0 }?.id
+ }
+
+ /// `button` uses the browser's numbering: 0 left, 1 middle, 2 right.
+ static func mouse(_ kind: String, button: Int, at p: CGPoint, owner: Int) {
+ let b: CGMouseButton = button == 2 ? .right : button == 1 ? .center : .left
+ let type: CGEventType
+ switch kind {
+ case "down":
+ type = b == .left ? .leftMouseDown : b == .right ? .rightMouseDown : .otherMouseDown
+ buttonsHeld[owner, default: []].insert(button)
+ case "up":
+ type = b == .left ? .leftMouseUp : b == .right ? .rightMouseUp : .otherMouseUp
+ buttonsHeld[owner]?.remove(button)
+ // Another viewer still holding it keeps it down.
+ if buttonsHeld.values.contains(where: { $0.contains(button) }) { return }
+ default: // a drag is whatever this viewer is holding
+ let mine = buttonsHeld[owner] ?? []
+ if mine.contains(0) { type = .leftMouseDragged }
+ else if mine.contains(2) { type = .rightMouseDragged }
+ else if mine.contains(1) { type = .otherMouseDragged }
+ else { type = .mouseMoved }
+ }
+ let mine = buttonsHeld[owner] ?? []
+ let held: CGMouseButton = mine.contains(0) ? .left : mine.contains(2) ? .right : mine.contains(1) ? .center : .left
+ guard let e = CGEvent(mouseEventSource: source, mouseType: type, mouseCursorPosition: p,
+ mouseButton: kind == "move" ? held : b) else { return }
+ if kind == "down" || kind == "up" {
+ let now = ProcessInfo.processInfo.systemUptime
+ var count = 1
+ if kind == "down" {
+ if let l = lastDown, l.button == button, now - l.time < NSEvent.doubleClickInterval,
+ abs(l.point.x - p.x) < 5, abs(l.point.y - p.y) < 5 { count = l.count + 1 }
+ lastDown = (now, p, button, count)
+ } else if let l = lastDown, l.button == button {
+ count = l.count
+ }
+ e.setIntegerValueField(.mouseEventClickState, value: Int64(count))
+ }
+ e.post(tap: .cghidEventTap)
+ }
+
+ static func scroll(dx: Double, dy: Double, at p: CGPoint, owner: Int) {
+ mouse("move", button: 0, at: p, owner: owner)
+ // Browsers report pixels with +y meaning "scroll down"; macOS's +y is up.
+ guard let e = CGEvent(scrollWheelEvent2Source: source, units: .pixel, wheelCount: 2,
+ wheel1: Int32(-dy.rounded()), wheel2: Int32(-dx.rounded()), wheel3: 0) else { return }
+ e.location = p
+ e.post(tap: .cghidEventTap)
+ }
+
+ /// Lets go of every button and key this viewer is holding down, so a
+ /// dropped connection can't leave, say, Shift or ⌘ stuck on.
+ static func releaseAll(owner: Int) {
+ let p = CGEvent(source: nil)?.location ?? .zero
+ let buttons = buttonsHeld.removeValue(forKey: owner) ?? []
+ let keys = keysHeld.removeValue(forKey: owner) ?? []
+ // Only what no other viewer is still holding.
+ for b in buttons where !buttonsHeld.values.contains(where: { $0.contains(b) }) {
+ let type: CGEventType = b == 2 ? .rightMouseUp : b == 1 ? .otherMouseUp : .leftMouseUp
+ CGEvent(mouseEventSource: source, mouseType: type, mouseCursorPosition: p,
+ mouseButton: b == 2 ? .right : b == 1 ? .center : .left)?.post(tap: .cghidEventTap)
+ }
+ for k in keys where !keysHeld.values.contains(where: { $0.contains(k) }) {
+ CGEvent(keyboardEventSource: source, virtualKey: k, keyDown: false)?.post(tap: .cghidEventTap)
+ }
+ }
+
+ static func key(code: String, key: String, down: Bool, mods: [String], owner: Int) {
+ var flags = CGEventFlags()
+ if mods.contains("shift") { flags.insert(.maskShift) }
+ if mods.contains("ctrl") { flags.insert(.maskControl) }
+ if mods.contains("alt") { flags.insert(.maskAlternate) }
+ if mods.contains("meta") { flags.insert(.maskCommand) }
+ if let vk = keyCodes[code] {
+ guard let e = CGEvent(keyboardEventSource: source, virtualKey: vk, keyDown: down) else { return }
+ if down {
+ keysHeld[owner, default: []].insert(vk)
+ } else {
+ keysHeld[owner]?.remove(vk)
+ if keysHeld.values.contains(where: { $0.contains(vk) }) { return } // still held elsewhere
+ }
+ e.flags = flags
+ e.post(tap: .cghidEventTap)
+ } else if down, !key.isEmpty, key.count <= 4, key.unicodeScalars.allSatisfy({ $0.value >= 0x20 }) {
+ // A key the table doesn't know (a non-US layout, the headset's
+ // on-screen keyboard): type its character instead.
+ text(key)
+ }
+ }
+
+ static func text(_ s: String) {
+ let units = Array(s.utf16)
+ for chunk in stride(from: 0, to: units.count, by: 16) {
+ let part = Array(units[chunk.. macOS virtual key code (ANSI positions).
+ static let keyCodes: [String: CGKeyCode] = [
+ "KeyA": 0x00, "KeyS": 0x01, "KeyD": 0x02, "KeyF": 0x03, "KeyH": 0x04, "KeyG": 0x05, "KeyZ": 0x06, "KeyX": 0x07,
+ "KeyC": 0x08, "KeyV": 0x09, "IntlBackslash": 0x0A, "KeyB": 0x0B, "KeyQ": 0x0C, "KeyW": 0x0D, "KeyE": 0x0E,
+ "KeyR": 0x0F, "KeyY": 0x10, "KeyT": 0x11, "Digit1": 0x12, "Digit2": 0x13, "Digit3": 0x14, "Digit4": 0x15,
+ "Digit6": 0x16, "Digit5": 0x17, "Equal": 0x18, "Digit9": 0x19, "Digit7": 0x1A, "Minus": 0x1B, "Digit8": 0x1C,
+ "Digit0": 0x1D, "BracketRight": 0x1E, "KeyO": 0x1F, "KeyU": 0x20, "BracketLeft": 0x21, "KeyI": 0x22,
+ "KeyP": 0x23, "Enter": 0x24, "KeyL": 0x25, "KeyJ": 0x26, "Quote": 0x27, "KeyK": 0x28, "Semicolon": 0x29,
+ "Backslash": 0x2A, "Comma": 0x2B, "Slash": 0x2C, "KeyN": 0x2D, "KeyM": 0x2E, "Period": 0x2F, "Tab": 0x30,
+ "Space": 0x31, "Backquote": 0x32, "Backspace": 0x33, "Escape": 0x35, "MetaRight": 0x36, "MetaLeft": 0x37,
+ "ShiftLeft": 0x38, "CapsLock": 0x39, "AltLeft": 0x3A, "ControlLeft": 0x3B, "ShiftRight": 0x3C,
+ "AltRight": 0x3D, "ControlRight": 0x3E, "F17": 0x40, "NumpadDecimal": 0x41, "NumpadMultiply": 0x43,
+ "NumpadAdd": 0x45, "NumLock": 0x47, "NumpadDivide": 0x4B, "NumpadEnter": 0x4C, "NumpadSubtract": 0x4E,
+ "F18": 0x4F, "F19": 0x50, "NumpadEqual": 0x51, "Numpad0": 0x52, "Numpad1": 0x53, "Numpad2": 0x54,
+ "Numpad3": 0x55, "Numpad4": 0x56, "Numpad5": 0x57, "Numpad6": 0x58, "Numpad7": 0x59, "F20": 0x5A,
+ "Numpad8": 0x5B, "Numpad9": 0x5C, "F5": 0x60, "F6": 0x61, "F7": 0x62, "F3": 0x63, "F8": 0x64, "F9": 0x65,
+ "F11": 0x67, "F13": 0x69, "F16": 0x6A, "F14": 0x6B, "F10": 0x6D, "ContextMenu": 0x6E, "F12": 0x6F,
+ "F15": 0x71, "Insert": 0x72, "Help": 0x72, "Home": 0x73, "PageUp": 0x74, "Delete": 0x75, "F4": 0x76,
+ "End": 0x77, "F2": 0x78, "PageDown": 0x79, "F1": 0x7A, "ArrowLeft": 0x7B, "ArrowRight": 0x7C,
+ "ArrowDown": 0x7D, "ArrowUp": 0x7E, "OSLeft": 0x37, "OSRight": 0x36,
+ ]
+}
diff --git a/mac/frame-mac-view/Sources/Separate.swift b/mac/frame-mac-view/Sources/Separate.swift
new file mode 100644
index 0000000..72c02be
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Separate.swift
@@ -0,0 +1,295 @@
+// "Separate" mode: a streamed window gets a virtual display of its own. The
+// window is moved onto it and sized to fill it, and the whole display is
+// captured, so its menus, sheets, popovers and tooltips come along, nothing
+// can cover it, and clicks land on it without raising anything. On stop the
+// window goes back where it was. See docs/mac-window-separation.md.
+import AppKit
+import ApplicationServices
+import CoreMedia
+import Foundation
+
+@_silgen_name("_AXUIElementGetWindow")
+private func axWindowID(_ element: AXUIElement, _ id: UnsafeMutablePointer) -> AXError
+
+/// One of our virtual displays, HiDPI (2 pixels per point). Main thread only.
+final class VirtualDisplay {
+ static let vendor: UInt32 = 0xF0C0
+ /// How often macOS composites our displays (FRAME_MAC_VIEW_VD_HZ to experiment).
+ static let refreshRate = Double(ProcessInfo.processInfo.environment["FRAME_MAC_VIEW_VD_HZ"] ?? "") ?? 60
+ private var display: CGVirtualDisplay?
+ let id: CGDirectDisplayID
+ /// Removal is deferred while the Mac's screen sleeps, and an identity
+ /// can't be reused until it's gone, so each display gets a fresh serial.
+ private static var serial = (UInt32(truncatingIfNeeded: ProcessInfo.processInfo.processIdentifier) & 0xFFFF) << 12
+
+ init?(name: String, width: Int, height: Int) {
+ var made: CGVirtualDisplay?
+ for _ in 0..<8 where made == nil {
+ VirtualDisplay.serial &+= 1
+ let d = CGVirtualDisplayDescriptor()
+ d.queue = DispatchQueue.main
+ d.name = name
+ d.maxPixelsWide = UInt32(2 * width)
+ d.maxPixelsHigh = UInt32(2 * height)
+ d.sizeInMillimeters = CGSize(width: Double(width) * 0.2646, height: Double(height) * 0.2646)
+ d.vendorID = VirtualDisplay.vendor
+ d.productID = 0x5E9A
+ d.serialNum = VirtualDisplay.serial
+ d.terminationHandler = { _, _ in }
+ guard let vd = CGVirtualDisplay(descriptor: d) else { continue }
+ let s = CGVirtualDisplaySettings()
+ s.hiDPI = 1
+ let hz = VirtualDisplay.refreshRate
+ s.modes = [CGVirtualDisplayMode(width: UInt32(2 * width), height: UInt32(2 * height), refreshRate: hz)!,
+ CGVirtualDisplayMode(width: UInt32(width), height: UInt32(height), refreshRate: hz)!]
+ if vd.apply(s), vd.displayID != 0 { made = vd }
+ }
+ guard let made else { return nil }
+ display = made
+ id = made.displayID
+ // Pick the HiDPI mode: `width` points drawn with twice the pixels.
+ let modes = (CGDisplayCopyAllDisplayModes(id, [kCGDisplayShowDuplicateLowResolutionModes: true] as CFDictionary)
+ as? [CGDisplayMode]) ?? []
+ if let hidpi = modes.first(where: {
+ $0.width == width && $0.height == height && $0.pixelWidth == 2 * width && $0.refreshRate == VirtualDisplay.refreshRate
+ }) ?? modes.first(where: { $0.width == width && $0.height == height && $0.pixelWidth == 2 * width }) {
+ var cfg: CGDisplayConfigRef?
+ CGBeginDisplayConfiguration(&cfg)
+ CGConfigureDisplayWithDisplayMode(cfg, id, hidpi, nil)
+ CGCompleteDisplayConfiguration(cfg, .forSession)
+ }
+ }
+
+ var bounds: CGRect { CGDisplayBounds(id) }
+
+ var isHiDPI: Bool { CGDisplayCopyDisplayMode(id).map { $0.pixelWidth > $0.width } ?? false }
+
+ var screen: NSScreen? {
+ NSScreen.screens.first {
+ ($0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value == id
+ }
+ }
+
+ /// Where a window may go, in global (top-left origin) points: the display
+ /// minus its menu bar and anything else macOS reserves there.
+ var usableRect: CGRect {
+ let b = bounds
+ guard let s = screen else { return b }
+ let top = s.frame.maxY - s.visibleFrame.maxY, bottom = s.visibleFrame.minY - s.frame.minY
+ let left = s.visibleFrame.minX - s.frame.minX, right = s.frame.maxX - s.visibleFrame.maxX
+ return CGRect(x: b.minX + left, y: b.minY + top, width: b.width - left - right, height: b.height - top - bottom)
+ }
+
+ func release() { display = nil }
+
+ static func isOurs(_ id: CGDirectDisplayID) -> Bool { CGDisplayVendorNumber(id) == vendor }
+}
+
+/// A window through the Accessibility API.
+struct AXWindow {
+ let element: AXUIElement
+
+ init?(windowID: CGWindowID, pid: pid_t) {
+ let app = AXUIElementCreateApplication(pid)
+ var value: CFTypeRef?
+ guard AXUIElementCopyAttributeValue(app, kAXWindowsAttribute as CFString, &value) == .success,
+ let windows = value as? [AXUIElement] else { return nil }
+ guard let match = windows.first(where: { w in
+ var id: CGWindowID = 0
+ return axWindowID(w, &id) == .success && id == windowID
+ }) else { return nil }
+ element = match
+ }
+
+ var frame: CGRect? {
+ var p: CFTypeRef?, s: CFTypeRef?
+ guard AXUIElementCopyAttributeValue(element, kAXPositionAttribute as CFString, &p) == .success,
+ AXUIElementCopyAttributeValue(element, kAXSizeAttribute as CFString, &s) == .success else { return nil }
+ var point = CGPoint.zero, size = CGSize.zero
+ AXValueGetValue(p as! AXValue, .cgPoint, &point)
+ AXValueGetValue(s as! AXValue, .cgSize, &size)
+ return CGRect(origin: point, size: size)
+ }
+
+ var isFullScreen: Bool {
+ var v: CFTypeRef?
+ return AXUIElementCopyAttributeValue(element, "AXFullScreen" as CFString, &v) == .success && (v as? Bool) == true
+ }
+
+ /// Moving to another display: position first (so the size fits there),
+ /// then size, then position again in case the size change moved it.
+ func setFrame(_ r: CGRect) {
+ var origin = r.origin, size = r.size
+ let pos = AXValueCreate(.cgPoint, &origin)!, sz = AXValueCreate(.cgSize, &size)!
+ AXUIElementSetAttributeValue(element, kAXPositionAttribute as CFString, pos)
+ AXUIElementSetAttributeValue(element, kAXSizeAttribute as CFString, sz)
+ AXUIElementSetAttributeValue(element, kAXPositionAttribute as CFString, pos)
+ }
+}
+
+/// A window on a display of its own, captured as that display.
+final class SeparateSource: CaptureSource {
+ let windowID: CGWindowID
+ var onFrame: ((CVPixelBuffer, CMTime, Int64) -> Void)?
+ var onEnded: ((String) -> Void)?
+ var onChange: (() -> Void)?
+ private(set) var title = ""
+ private(set) var app = ""
+ private(set) var pid: pid_t?
+ private(set) var gone = false
+ private var display: VirtualDisplay?
+ private var inner: SCKSource?
+ private var window: AXWindow?
+ private var original: CGRect?
+ private var crop: CGRect? // display points, when only part of the display is captured
+ private var poll: DispatchSourceTimer?
+ private var stopped = false
+ private let queue = DispatchQueue(label: "frame-mac-view.separate")
+
+ init(windowID: CGWindowID) { self.windowID = windowID }
+
+ var frameRect: CGRect { inner?.frameRect ?? .zero }
+ var displayID: CGDirectDisplayID? { display?.id }
+
+ func start(maxLong: Int, fps: Int, completion: @escaping (String?) -> Void) {
+ guard AXIsProcessTrusted() else {
+ return completion("Separate windows need the Accessibility permission (to move the window)")
+ }
+ guard let info = WindowInfo.find(windowID) else {
+ gone = true
+ return completion("that window has closed")
+ }
+ title = info.title
+ app = info.app
+ pid = info.pid
+ DispatchQueue.main.async { [self] in
+ guard !stopped else { return }
+ guard let w = AXWindow(windowID: windowID, pid: info.pid), let frame = w.frame else {
+ return completion("couldn't reach that window through Accessibility")
+ }
+ if w.isFullScreen { return completion("take the window out of full screen first") }
+ // A display the window's size, plus room for the menu bar, in
+ // points; within what a panel can show sharply.
+ let width = min(max(Int(frame.width.rounded()), 640), 2560) / 2 * 2
+ let height = min(max(Int(frame.height.rounded()) + 40, 480), 1600) / 2 * 2
+ guard let vd = VirtualDisplay(name: app.isEmpty ? "Frame Control" : "\(app) (Frame)", width: width, height: height) else {
+ return completion("macOS wouldn't create a display for this window")
+ }
+ display = vd
+ window = w
+ original = frame
+ // The new screen shows up in NSScreen a moment later; its menu bar
+ // decides where the window can go.
+ placeWindow(attempt: 0, maxLong: maxLong, fps: fps, completion: completion)
+ }
+ }
+
+ private func placeWindow(attempt: Int, maxLong: Int, fps: Int, completion: @escaping (String?) -> Void) {
+ guard !stopped, let vd = display, let w = window else { return }
+ // Wait for the screen to appear, and for its HiDPI mode, so the
+ // first frames are captured sharp.
+ if vd.screen == nil || !vd.isHiDPI, attempt < 30 {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ self.placeWindow(attempt: attempt + 1, maxLong: maxLong, fps: fps, completion: completion)
+ }
+ return
+ }
+ let target = vd.usableRect
+ w.setFrame(target)
+ guard let now = w.frame, vd.bounds.intersects(now) else {
+ restore()
+ return completion("macOS didn't let the window move to its own display (Stage Manager can prevent this)")
+ }
+ let sck = SCKSource(.display(vd.id))
+ // A window that keeps its own size (Calculator, some dialogs) sits in
+ // the display's top-left corner: send only that corner, menu bar
+ // included, with room around it for menus, not a panel of wallpaper.
+ let b = vd.bounds
+ if now.width < target.width - 8 || now.height < target.height - 8 {
+ let w = min(b.width, max(now.maxX - b.minX, 480)), h = min(b.height, max(now.maxY - b.minY, 360))
+ crop = CGRect(x: 0, y: 0, width: (w / 2).rounded(.up) * 2, height: (h / 2).rounded(.up) * 2)
+ sck.crop = crop
+ }
+ sck.onFrame = { [weak self] pb, pts, shown in self?.onFrame?(pb, pts, shown) }
+ sck.onEnded = { [weak self] reason in self?.onEnded?(reason) }
+ inner = sck
+ sck.start(maxLong: maxLong, fps: fps) { [weak self] error in
+ // Back on main, where stop() runs too, so a viewer that left during
+ // startup can't leave a capture running.
+ DispatchQueue.main.async {
+ guard let self, !self.stopped else { return sck.stop() }
+ if let error {
+ self.restore()
+ return completion(error)
+ }
+ self.startPolling()
+ completion(nil)
+ }
+ }
+ }
+
+ /// The window can close or be retitled; ScreenCaptureKit won't say.
+ private func startPolling() {
+ let t = DispatchSource.makeTimerSource(queue: queue)
+ t.schedule(deadline: .now() + 1, repeating: 1)
+ t.setEventHandler { [weak self] in
+ guard let self else { return }
+ guard let info = WindowInfo.find(self.windowID) else {
+ self.gone = true
+ self.onEnded?("the window closed")
+ return
+ }
+ if !info.title.isEmpty, info.title != self.title {
+ self.title = info.title
+ self.onChange?()
+ }
+ // Displays added at the same moment can rearrange each other, and
+ // someone may drag the window away: put it back on its display.
+ if !info.bounds.isEmpty {
+ DispatchQueue.main.async { self.keepOnDisplay(info.bounds) }
+ }
+ }
+ t.resume()
+ poll = t
+ }
+
+ private func keepOnDisplay(_ now: CGRect) {
+ guard !stopped, let vd = display, let w = window else { return }
+ let b = vd.bounds
+ if let c = crop {
+ // Only part of the display is sent: the window must stay inside it.
+ guard !c.offsetBy(dx: b.minX, dy: b.minY).insetBy(dx: -2, dy: -2).contains(now) else { return }
+ } else {
+ guard !b.contains(CGPoint(x: now.midX, y: now.minY + 10)) else { return }
+ }
+ let target = vd.usableRect
+ w.setFrame(CGRect(origin: target.origin, size: now.size.width < target.width - 8 ? now.size : target.size))
+ }
+
+ /// Lifecycle state (stopped, inner, poll, window, display) is only touched on main.
+ func stop() {
+ DispatchQueue.main.async { [self] in
+ stopped = true
+ poll?.cancel()
+ poll = nil
+ inner?.stop()
+ restore()
+ }
+ }
+
+ /// Puts the window back where it was, then removes the display. In that
+ /// order: removing a display first would let macOS pick where it goes.
+ private func restore() {
+ if let w = window, let r = original, WindowInfo.find(windowID) != nil { w.setFrame(r) }
+ window = nil
+ original = nil
+ let vd = display
+ display = nil
+ // Give WindowServer a moment to move the window before the display goes.
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { vd?.release() }
+ }
+
+ func setMaxLong(_ n: Int) { DispatchQueue.main.async { self.inner?.setMaxLong(n) } }
+
+ func pointer(x: Double, y: Double, text: String?) {}
+}
diff --git a/mac/frame-mac-view/Sources/Server.swift b/mac/frame-mac-view/Sources/Server.swift
new file mode 100644
index 0000000..11c58dc
--- /dev/null
+++ b/mac/frame-mac-view/Sources/Server.swift
@@ -0,0 +1,269 @@
+// A small HTTP/1.1 + WebSocket server on Network.framework, loopback only.
+// Enough for the agent's JSON endpoints, the viewer page and one WebSocket per
+// stream; not a general web server.
+import CryptoKit
+import Foundation
+import Network
+
+struct Request {
+ let method: String
+ let path: String
+ let query: [String: String]
+ let headers: [String: String] // lower-cased names
+}
+
+final class Server {
+ let queue = DispatchQueue(label: "frame-mac-view.server")
+ private let listener: NWListener
+ private let handle: (Request, HTTPConnection) -> Void
+
+ init(port: UInt16, handle: @escaping (Request, HTTPConnection) -> Void) throws {
+ let tcp = NWProtocolTCP.Options()
+ tcp.noDelay = true
+ let params = NWParameters(tls: nil, tcp: tcp)
+ // Port 0 lets the system pick; `port` then says which.
+ params.requiredLocalEndpoint = .hostPort(host: "127.0.0.1", port: NWEndpoint.Port(rawValue: port) ?? .any)
+ params.allowLocalEndpointReuse = true
+ listener = try NWListener(using: params)
+ self.handle = handle
+ }
+
+ var port: UInt16? { listener.port?.rawValue }
+
+ func start(ready: @escaping (Error?) -> Void) {
+ listener.stateUpdateHandler = { state in
+ switch state {
+ case .ready: ready(nil)
+ case .failed(let e): ready(e)
+ default: break
+ }
+ }
+ listener.newConnectionHandler = { [weak self] conn in
+ guard let self else { return }
+ HTTPConnection(conn, queue: self.queue, handle: self.handle).start()
+ }
+ listener.start(queue: queue)
+ }
+}
+
+final class HTTPConnection {
+ let conn: NWConnection
+ let queue: DispatchQueue
+ private let handle: (Request, HTTPConnection) -> Void
+ private var buffer = Data()
+ private var retained: HTTPConnection? // alive until the response is sent
+
+ init(_ conn: NWConnection, queue: DispatchQueue, handle: @escaping (Request, HTTPConnection) -> Void) {
+ self.conn = conn
+ self.queue = queue
+ self.handle = handle
+ }
+
+ func start() {
+ retained = self
+ conn.start(queue: queue)
+ readHead()
+ }
+
+ private func readHead() {
+ conn.receive(minimumIncompleteLength: 1, maximumLength: 16384) { [self] data, _, done, error in
+ if let data { buffer.append(data) }
+ if let end = buffer.range(of: Data("\r\n\r\n".utf8)) {
+ guard let req = Self.parse(buffer[.. 16384 {
+ close()
+ } else {
+ readHead()
+ }
+ }
+ }
+
+ static func parse(_ head: Data) -> Request? {
+ guard let text = String(data: head, encoding: .utf8) else { return nil }
+ let lines = text.components(separatedBy: "\r\n")
+ let parts = lines[0].split(separator: " ")
+ guard parts.count >= 2, let url = URLComponents(string: String(parts[1])) else { return nil }
+ var headers: [String: String] = [:]
+ for line in lines.dropFirst() {
+ guard let colon = line.firstIndex(of: ":") else { continue }
+ headers[line[.. WebSocket? {
+ guard req.headers["upgrade"]?.lowercased() == "websocket", let key = req.headers["sec-websocket-key"] else {
+ respond(400, text: "expected a WebSocket upgrade")
+ return nil
+ }
+ let accept = Data(Insecure.SHA1.hash(data: Data((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").utf8))).base64EncodedString()
+ let head = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: \(accept)\r\n\r\n"
+ conn.send(content: Data(head.utf8), completion: .contentProcessed { _ in })
+ let ws = WebSocket(conn, queue: queue)
+ retained = nil
+ return ws
+ }
+}
+
+/// Server side of RFC 6455. Sends are counted until the network stack has
+/// taken them, so the stream can skip frames instead of queueing seconds of
+/// video on a slow link.
+final class WebSocket {
+ static let maxMessage: UInt64 = 1 << 20
+ let conn: NWConnection
+ let queue: DispatchQueue
+ var onText: ((String) -> Void)?
+ var onClose: (() -> Void)?
+ private var buffer = Data()
+ private var fragments = Data()
+ private var fragmentOpcode: UInt8 = 0
+ private var closed = false
+ private var retained: WebSocket?
+ private let lock = NSLock()
+ private var _pending = 0
+ var onDrain: (() -> Void)?
+
+ /// Bytes handed to the connection that it hasn't sent yet. Any thread.
+ var pendingBytes: Int { lock.lock(); defer { lock.unlock() }; return _pending }
+
+ init(_ conn: NWConnection, queue: DispatchQueue) {
+ self.conn = conn
+ self.queue = queue
+ }
+
+ func start() {
+ retained = self
+ read()
+ }
+
+ func sendText(_ s: String) { send(opcode: 1, Data(s.utf8)) }
+ func sendJSON(_ obj: Any) {
+ if let d = try? JSONSerialization.data(withJSONObject: obj), let s = String(data: d, encoding: .utf8) { sendText(s) }
+ }
+ /// `taken` runs once the network stack has taken the whole frame.
+ func sendBinary(_ d: Data, taken: (() -> Void)? = nil) { send(opcode: 2, d, taken: taken) }
+
+ func send(opcode: UInt8, _ payload: Data, taken: (() -> Void)? = nil) {
+ var frame = Data([0x80 | opcode])
+ let n = payload.count
+ if n < 126 {
+ frame.append(UInt8(n))
+ } else if n < 65536 {
+ frame.append(126)
+ frame.append(contentsOf: [UInt8(n >> 8), UInt8(n & 0xff)])
+ } else {
+ frame.append(127)
+ for shift in stride(from: 56, through: 0, by: -8) { frame.append(UInt8((UInt64(n) >> UInt64(shift)) & 0xff)) }
+ }
+ frame.append(payload)
+ let size = frame.count
+ lock.lock(); _pending += size; lock.unlock()
+ conn.send(content: frame, completion: .contentProcessed { [weak self] _ in
+ guard let self else { return }
+ self.lock.lock(); self._pending -= size; self.lock.unlock()
+ taken?()
+ self.onDrain?()
+ })
+ }
+
+ func close() {
+ guard !closed else { return }
+ closed = true
+ send(opcode: 8, Data([0x03, 0xe8])) // 1000, normal closure
+ conn.send(content: nil, isComplete: true, completion: .contentProcessed { [weak self] _ in self?.conn.cancel() })
+ finish()
+ }
+
+ private func finish() {
+ let cb = onClose
+ onClose = nil
+ onText = nil
+ onDrain = nil
+ cb?()
+ retained = nil
+ }
+
+ private func read() {
+ conn.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] data, _, done, error in
+ guard let self, !self.closed else { return }
+ if let data { self.buffer.append(data) }
+ self.parse()
+ if error != nil || done {
+ self.closed = true
+ self.conn.cancel()
+ self.finish()
+ } else if !self.closed {
+ self.read()
+ }
+ }
+ }
+
+ private func parse() {
+ while buffer.count >= 2 {
+ let b = [UInt8](buffer.prefix(14))
+ let fin = b[0] & 0x80 != 0, opcode = b[0] & 0x0f, masked = b[1] & 0x80 != 0
+ // Lengths are unsigned and clients only send small control
+ // messages, so anything big (or a 64-bit length with the top bit
+ // set) is refused before it's turned into an Int.
+ var len64 = UInt64(b[1] & 0x7f), off = 2
+ if len64 == 126 {
+ guard b.count >= 4 else { return }
+ len64 = UInt64(b[2]) << 8 | UInt64(b[3]); off = 4
+ } else if len64 == 127 {
+ guard b.count >= 10 else { return }
+ len64 = 0
+ for i in 2..<10 { len64 = len64 << 8 | UInt64(b[i]) }
+ off = 10
+ }
+ guard len64 <= WebSocket.maxMessage else { return close() }
+ let len = Int(len64)
+ let maskOff = off
+ if masked { off += 4 }
+ guard buffer.count >= off + len else { return }
+ let start = buffer.startIndex
+ var payload = Data(buffer[(start + off)..<(start + off + len)])
+ if masked {
+ let mask = [UInt8](buffer[(start + maskOff)..<(start + maskOff + 4)])
+ payload.withUnsafeMutableBytes { p in
+ for i in 0.. Int64 { hostUs(mach_absolute_time()) }
+
+/// A mach_absolute_time value (as in SCStreamFrameInfo.displayTime) in microseconds.
+func hostUs(_ ticks: UInt64) -> Int64 { Int64(ticks / 1000 * UInt64(timebase.numer) / UInt64(timebase.denom)) }
+
+/// A host-clock CMTime (ScreenCaptureKit's presentation times) in microseconds.
+func hostUs(_ t: CMTime) -> Int64 { t.isValid ? Int64(CMTimeGetSeconds(t) * 1_000_000) : nowUs() }
+
+/// One frame's journey. Zero means "didn't happen" or "not reported yet".
+struct FrameRecord {
+ var seq: UInt32 = 0
+ var key = false
+ var bytes = 0
+ var width = 0, height = 0
+ var capture: Int64 = 0 // the Mac composited it (display time)
+ var arrived: Int64 = 0 // ScreenCaptureKit handed it to us
+ var encodeStart: Int64 = 0
+ var encodeEnd: Int64 = 0
+ var sent: Int64 = 0 // handed to the WebSocket
+ var wire: Int64 = 0 // the socket took it
+ var received: Int64 = 0 // viewer, agent clock
+ var decoded: Int64 = 0
+ var drawn: Int64 = 0
+ var vsync: Int64 = 0 // the viewer's next animation frame after drawing it
+ var echo: UInt32 = 0 // the first frame after input `echo`
+ var tier = 0
+ var bitrate = 0
+
+ var json: [String: Any] {
+ ["s": seq, "k": key ? 1 : 0, "b": bytes, "w": width, "h": height, "cap": capture, "arr": arrived,
+ "e0": encodeStart, "e1": encodeEnd, "snd": sent, "wire": wire, "rx": received, "dec": decoded,
+ "drw": drawn, "vs": vsync, "echo": echo, "tier": tier, "br": bitrate]
+ }
+}
+
+/// One input event from the viewer and what came of it.
+struct InputRecord {
+ var id: UInt32
+ var kind: String
+ var viewer: Int64 // the viewer's event time, agent clock
+ var injected: Int64 = 0 // posted as a CGEvent
+ var frame: UInt32 = 0 // first frame captured after it (0: none yet)
+ var capture: Int64 = 0
+
+ var json: [String: Any] {
+ ["id": id, "kind": kind, "tv": viewer, "inj": injected, "frame": frame, "cap": capture]
+ }
+}
+
+func percentile(_ sorted: [Double], _ p: Double) -> Double? {
+ guard !sorted.isEmpty else { return nil }
+ let i = min(sorted.count - 1, max(0, Int((p * Double(sorted.count - 1)).rounded())))
+ return sorted[i]
+}
+
+/// Frames and inputs of one stream, kept for the last few thousand frames.
+/// Any thread.
+final class StreamStats {
+ static let capacity = 4096
+ private let lock = NSLock()
+ private var frames = [FrameRecord?](repeating: nil, count: StreamStats.capacity)
+ private var inputs: [UInt32: InputRecord] = [:]
+ private var inputOrder: [UInt32] = []
+ private(set) var nextSeq: UInt32 = 1
+ var captured = 0 // frames ScreenCaptureKit delivered
+ var skipped = 0 // held back from encoding (link, encoder, pacing or gate busy); only the newest may go later
+ var viewerDropped = 0 // not shown by the viewer (behind, or waiting for a keyframe)
+ var rtt: Double = 0 // ms, the viewer's best recent ping
+ var clockSynced = false
+ var decoder = "" // what the viewer reports about its decoder
+
+ func withLock(_ f: () -> T) -> T { lock.lock(); defer { lock.unlock() }; return f() }
+
+ func newFrame(_ r: FrameRecord) -> UInt32 {
+ withLock {
+ var r = r
+ r.seq = nextSeq
+ nextSeq &+= 1
+ frames[Int(r.seq) % StreamStats.capacity] = r
+ return r.seq
+ }
+ }
+
+ func update(_ seq: UInt32, _ f: (inout FrameRecord) -> Void) {
+ withLock {
+ let i = Int(seq) % StreamStats.capacity
+ guard var r = frames[i], r.seq == seq else { return }
+ f(&r)
+ frames[i] = r
+ }
+ }
+
+ func frame(_ seq: UInt32) -> FrameRecord? {
+ withLock {
+ let r = frames[Int(seq) % StreamStats.capacity]
+ return r?.seq == seq ? r : nil
+ }
+ }
+
+ func addInput(_ r: InputRecord) {
+ withLock {
+ inputs[r.id] = r
+ inputOrder.append(r.id)
+ if inputOrder.count > 512 { inputs[inputOrder.removeFirst()] = nil }
+ }
+ }
+
+ func updateInput(_ id: UInt32, _ f: (inout InputRecord) -> Void) {
+ withLock { if var r = inputs[id] { f(&r); inputs[id] = r } }
+ }
+
+ /// Frames with seq > `since` that were sent at least `settle` µs ago, so
+ /// the viewer has had time to report on them.
+ func settled(since: UInt32, settle: Int64 = 1_500_000) -> [FrameRecord] {
+ let cutoff = nowUs() - settle
+ return withLock {
+ frames.compactMap { $0 }.filter { $0.seq > since && $0.sent > 0 && $0.sent < cutoff }.sorted { $0.seq < $1.seq }
+ }
+ }
+
+ func inputList() -> [InputRecord] { withLock { inputOrder.compactMap { inputs[$0] } } }
+
+ /// Percentiles over the last `window` µs, for /status and the overlay.
+ func summary(window: Int64 = 2_000_000) -> [String: Any] {
+ let now = nowUs(), from = now - window
+ let recent = withLock { frames.compactMap { $0 }.filter { $0.sent > from } }
+ func ms(_ pick: (FrameRecord) -> (Int64, Int64)) -> [String: Double] {
+ let v = recent.compactMap { r -> Double? in
+ let (a, b) = pick(r)
+ return a > 0 && b > 0 ? Double(b - a) / 1000 : nil
+ }.sorted()
+ guard !v.isEmpty else { return [:] }
+ return ["p50": (percentile(v, 0.5)! * 10).rounded() / 10, "p95": (percentile(v, 0.95)! * 10).rounded() / 10]
+ }
+ let secs = Double(window) / 1_000_000
+ let shown = recent.filter { $0.vsync > 0 }.count
+ let bytes = recent.reduce(0) { $0 + $1.bytes }
+ var s: [String: Any] = [
+ "capture": ms { ($0.capture, $0.arrived) },
+ "queue": ms { ($0.arrived, $0.encodeStart) },
+ "encode": ms { ($0.encodeStart, $0.encodeEnd) },
+ "network": ms { ($0.encodeEnd, $0.received) },
+ "decode": ms { ($0.received, $0.decoded) },
+ "draw": ms { ($0.decoded, $0.vsync) },
+ "total": ms { ($0.capture, $0.vsync) },
+ "fps": (Double(shown) / secs * 10).rounded() / 10,
+ "sentFps": (Double(recent.count) / secs * 10).rounded() / 10,
+ "mbps": (Double(bytes * 8) / secs / 1e5).rounded() / 10,
+ "rtt": (rtt * 10).rounded() / 10,
+ "synced": clockSynced,
+ ]
+ withLock {
+ s["captured"] = captured
+ s["skipped"] = skipped
+ s["dropped"] = viewerDropped
+ s["decoder"] = decoder
+ let done = inputOrder.suffix(20).compactMap { inputs[$0] }.compactMap { i -> Double? in
+ guard i.frame != 0, let f = frames[Int(i.frame) % StreamStats.capacity], f.seq == i.frame, f.vsync > 0
+ else { return nil }
+ return Double(f.vsync - i.viewer) / 1000
+ }.sorted()
+ if !done.isEmpty { s["input"] = ["p50": percentile(done, 0.5)!, "n": done.count] }
+ }
+ return s
+ }
+}
diff --git a/mac/frame-mac-view/Sources/main.swift b/mac/frame-mac-view/Sources/main.swift
new file mode 100644
index 0000000..5eb0f70
--- /dev/null
+++ b/mac/frame-mac-view/Sources/main.swift
@@ -0,0 +1,755 @@
+// frame-mac-view: streams Mac windows or displays to viewers on the Steam
+// Frame and plays their pointer and key input back on the Mac.
+//
+// frame-mac-view serve --port 47811 --page ui/mac-view.html (token in FRAME_MAC_VIEW_TOKEN)
+// frame-mac-view windows | displays | permissions (JSON on stdout)
+// frame-mac-view request-permissions (shows macOS's prompts)
+//
+// It listens on 127.0.0.1 only. Frame Control reaches it from the Frame
+// through an SSH reverse tunnel, and every request must carry the token.
+//
+// HTTP. Frame Control's key (?k=, from FRAME_MAC_VIEW_TOKEN) never leaves the
+// Mac; the Frame gets a single-use ticket per viewer instead.
+// GET /ping, /view open: tunnel check, viewer page
+// GET /stream?src=...&t=TICKET|r=KEY WebSocket (&codec=h264|jpeg&max=&fps=&bpp=)
+// GET /status, /windows, /displays ?k=: permissions, streams, sources
+// POST /ticket?src=... ?k=: a ticket for one viewer of src
+// POST /close[?src=...] ?k=: end those streams, close their windows
+// POST /permissions ?k=: show macOS's permission prompts
+// GET /stats[?id=&since=] ?k=: per-frame timing records (for benchmarks)
+// POST /bench?src=&action=... ?k=: ask viewers of src to type, click or show their overlay
+// src is window:, display: or test.
+import AppKit
+import ApplicationServices
+import Foundation
+import IOKit.pwr_mgt
+import ScreenCaptureKit
+import Security
+
+let version = "1"
+
+func windowsJSON() -> [[String: Any]] {
+ let me = ProcessInfo.processInfo.processIdentifier
+ let skip: Set = ["Window Server", "Dock", "Control Centre", "Control Center", "Notification Centre",
+ "Notification Center", "Spotlight", "SystemUIServer", "Wallpaper", "WindowManager"]
+ return WindowInfo.all().filter {
+ $0.layer == 0 && $0.pid != me && !skip.contains($0.app) && $0.bounds.width >= 120 && $0.bounds.height >= 80
+ }.map {
+ ["id": $0.id, "src": "window:\($0.id)", "pid": $0.pid, "app": $0.app, "title": $0.title,
+ "w": Int($0.bounds.width), "h": Int($0.bounds.height)]
+ }
+}
+
+func displaysJSON() -> [[String: Any]] {
+ var ids = [CGDirectDisplayID](repeating: 0, count: 16)
+ var n: UInt32 = 0
+ // Online, not active: a display that's asleep is still one you can stream.
+ CGGetOnlineDisplayList(16, &ids, &n)
+ return ids.prefix(Int(n)).filter {
+ CGDisplayMirrorsDisplay($0) == kCGNullDirectDisplay && !VirtualDisplay.isOurs($0) // not a separated window's
+ }.map { id in
+ let b = CGDisplayBounds(id)
+ return ["id": id, "src": "display:\(id)", "name": displayName(id), "w": Int(b.width), "h": Int(b.height),
+ "main": CGDisplayIsMain(id) != 0]
+ }
+}
+
+func permissionsJSON() -> [String: Any] {
+ ["screen": CGPreflightScreenCaptureAccess(), "accessibility": AXIsProcessTrusted()]
+}
+
+func printJSON(_ obj: Any) {
+ let d = (try? JSONSerialization.data(withJSONObject: obj, options: [.sortedKeys, .prettyPrinted])) ?? Data()
+ FileHandle.standardOutput.write(d + Data("\n".utf8))
+}
+
+/// A number from a JSON message, whatever its JSON type.
+func num(_ v: Any?) -> Double? { (v as? NSNumber)?.doubleValue }
+
+/// One viewer watching one source.
+final class Session {
+ let id: Int
+ let source: Source
+ let capture: CaptureSource
+ let encoder: Encoder
+ let ws: WebSocket
+ let codec: Codec
+ let reconnectKey: String
+ let stats = StreamStats()
+ let controller: RateController
+ private var appliedScale = 1.0
+ private var lastSubmit: Int64 = 0
+ private var paceScheduled = false
+ private let lock = NSLock()
+ /// A captured picture waiting to be encoded (or the last one encoded).
+ private struct Picture {
+ let pb: CVPixelBuffer
+ let pts: CMTime
+ let capture: Int64
+ let arrived: Int64
+ var echo: UInt32 // the input this picture is the first reply to
+ }
+ private var last: Picture?
+ private var skipped = false
+ private var stopped = false
+ private var inFlight = 0
+ /// Input posted to the Mac whose effect hasn't been captured yet: the
+ /// next picture composited after `after` is tagged with it.
+ private var pendingEcho: (id: UInt32, after: Int64)?
+ private var statsTimer: DispatchSourceTimer?
+ /// Frames already queued for the network; beyond this, or with two frames
+ /// already in the encoder, new frames are skipped (before encoding, so no
+ /// reference frame goes missing) and the newest picture is sent once
+ /// things catch up. Only that newest picture is kept meanwhile.
+ let maxPending: Int
+ static let maxInFlight = 2
+ var onEnd: ((Session) -> Void)?
+ var onAck: (() -> Void)?
+ var onFinished: ((String) -> Void)?
+ private let encodeQueue = DispatchQueue(label: "frame-mac-view.encode", qos: .userInteractive)
+
+ init(id: Int, source: Source, capture: CaptureSource, ws: WebSocket, codec: Codec, fps: Int, bitsPerPixel: Double,
+ reconnectKey: String) {
+ self.id = id
+ self.source = source
+ self.capture = capture
+ self.ws = ws
+ self.codec = codec
+ self.reconnectKey = reconnectKey
+ encoder = Encoder(codec: codec, fps: fps, bitsPerPixel: bitsPerPixel)
+ controller = RateController(maxFps: fps)
+ maxPending = codec == .jpeg ? 3 << 20 : 1 << 20
+ }
+
+ /// Frames come faster than the current tier's frame rate: this one waits,
+ /// and goes when its turn comes (unless a newer one replaces it).
+ /// Call with `lock` held.
+ private func paced(now: Int64) -> Bool {
+ let fps = controller.fps
+ guard fps < controller.maxFps, lastSubmit > 0 else { return false }
+ let interval = Int64(1_000_000 / fps), due = lastSubmit + interval * 9 / 10
+ guard now < due else { return false }
+ if !paceScheduled {
+ paceScheduled = true
+ encodeQueue.asyncAfter(deadline: .now() + .microseconds(Int(due - now))) { [weak self] in
+ guard let self else { return }
+ self.lock.lock(); self.paceScheduled = false; self.lock.unlock()
+ self.resendIfRoom()
+ }
+ }
+ return true
+ }
+
+ /// Encodes `pb` unless the link or the encoder is busy, in which case it
+ /// becomes the picture to send next.
+ private func offer(_ pb: CVPixelBuffer, pts: CMTime, capture shown: Int64) {
+ let arrived = nowUs()
+ stats.withLock { stats.captured += 1 }
+ controller.captured(at: arrived)
+ lock.lock()
+ // A reply to input stays with whichever picture ends up being sent.
+ var echo = skipped ? last?.echo ?? 0 : 0
+ if let p = pendingEcho, shown >= p.after {
+ echo = p.id
+ pendingEcho = nil
+ }
+ let picture = Picture(pb: pb, pts: pts, capture: shown, arrived: arrived, echo: echo)
+ last = picture
+ let busy = stopped || ws.pendingBytes > maxPending || inFlight >= Session.maxInFlight
+ || paced(now: arrived) || !controller.maySend(now: arrived)
+ if busy { skipped = true } else { inFlight += 1; last?.echo = 0; lastSubmit = arrived }
+ lock.unlock()
+ if busy { stats.withLock { stats.skipped += 1 } } else { submit(picture) }
+ }
+
+ private func submit(_ p: Picture) {
+ encodeQueue.async {
+ var r = FrameRecord()
+ r.capture = p.capture
+ r.arrived = p.arrived
+ r.echo = p.echo
+ r.bitrate = self.encoder.bitrate
+ r.tier = self.controller.tier
+ r.encodeStart = nowUs()
+ let seq = self.stats.newFrame(r)
+ if p.echo != 0 { self.stats.updateInput(p.echo) { $0.frame = seq; $0.capture = p.capture } }
+ if !self.encoder.encode(p.pb, pts: p.pts, seq: seq) { self.finished() }
+ }
+ }
+
+ /// An encode finished (or failed): send the newest skipped picture if
+ /// there's room now.
+ private func finished() {
+ lock.lock()
+ inFlight = max(0, inFlight - 1)
+ lock.unlock()
+ resendIfRoom()
+ }
+
+ private func resendIfRoom() {
+ lock.lock()
+ var next: Picture?
+ let now = nowUs()
+ if !stopped, skipped, ws.pendingBytes <= maxPending / 2, inFlight < Session.maxInFlight, let l = last,
+ !paced(now: now), controller.maySend(now: now, counts: false) {
+ next = l
+ skipped = false
+ inFlight += 1
+ last?.echo = 0
+ lastSubmit = now
+ }
+ lock.unlock()
+ // Stamped now: VideoToolbox needs increasing times, and the capture
+ // time stays in the record, so the wait counts as latency.
+ if let p = next {
+ submit(Picture(pb: p.pb, pts: CMClockGetTime(CMClockGetHostTimeClock()), capture: p.capture,
+ arrived: p.arrived, echo: p.echo))
+ }
+ }
+
+ func start(maxLong: Int, fps: Int) {
+ encoder.onFrame = { [weak self] data, key, pts, seq in
+ guard let self else { return }
+ let t = nowUs()
+ // The quality setting's bitrate, at full size, is the most it gets.
+ if self.appliedScale == 1 {
+ self.controller.setCeiling(self.encoder.defaultBitrate(width: self.encoder.width, height: self.encoder.height))
+ }
+ self.controller.sent(seq: seq, bytes: data.count + 17, at: t)
+ var echo: UInt32 = 0
+ let (w, h) = (self.encoder.width, self.encoder.height)
+ self.stats.update(seq) {
+ $0.encodeEnd = t
+ $0.sent = t
+ $0.bytes = data.count
+ $0.key = key
+ $0.width = w
+ $0.height = h
+ echo = $0.echo
+ }
+ // Header: flags (1 = keyframe), pts µs, sequence number, and the
+ // input this frame is the first reply to; all big-endian.
+ var msg = Data(capacity: data.count + 17)
+ msg.append(key ? 1 : 0)
+ var us = UInt64(max(0, CMTimeGetSeconds(pts)) * 1_000_000).bigEndian
+ msg.append(Data(bytes: &us, count: 8))
+ var s = seq.bigEndian, e = echo.bigEndian
+ msg.append(Data(bytes: &s, count: 4))
+ msg.append(Data(bytes: &e, count: 4))
+ msg.append(data)
+ let stats = self.stats
+ self.ws.sendBinary(msg) { stats.update(seq) { $0.wire = nowUs() } }
+ }
+ encoder.onDone = { [weak self] _ in self?.finished() }
+ encoder.onError = { [weak self] message in self?.ws.sendJSON(["t": "error", "message": message]) }
+ capture.onFrame = { [weak self] pb, pts, shown in self?.offer(pb, pts: pts, capture: shown) }
+ capture.onEnded = { [weak self] reason in
+ guard let self else { return }
+ self.ws.sendJSON(["t": "closed", "reason": reason])
+ self.onFinished?(self.source.key)
+ self.end()
+ }
+ ws.onDrain = { [weak self] in self?.resendIfRoom() }
+ ws.onText = { [weak self] text in self?.handle(text) }
+ ws.onClose = { [weak self] in self?.end() }
+ ws.start()
+ // Reconnect with this (kept in the page's memory, never on a command line).
+ ws.sendJSON(["t": "hello", "r": reconnectKey])
+ capture.onChange = { [weak self] in self?.sendInfo() }
+ // The numbers, for the viewer's overlay.
+ // Adapt ten times a second; the numbers go to the viewer once a second.
+ let t = DispatchSource.makeTimerSource(queue: encodeQueue)
+ t.schedule(deadline: .now() + 0.1, repeating: 0.1)
+ var ticks = 0
+ t.setEventHandler { [weak self] in
+ guard let self, !self.isStopped else { return }
+ self.adapt(maxLong: maxLong)
+ ticks += 1
+ guard ticks % 10 == 0 else { return }
+ var s = self.stats.summary()
+ s.merge(self.controller.state()) { a, _ in a }
+ s["t"] = "stats"
+ s["bitrate"] = self.encoder.bitrate
+ s["size"] = "\(self.encoder.width)×\(self.encoder.height)"
+ self.ws.sendJSON(s)
+ }
+ t.resume()
+ statsTimer = t
+ capture.start(maxLong: maxLong, fps: fps) { [weak self] error in
+ guard let self else { return }
+ if let error {
+ if self.capture.gone {
+ // Final, like a window closing mid-stream: the viewer closes
+ // and its key is revoked, rather than retrying for ever.
+ self.ws.sendJSON(["t": "closed", "reason": error])
+ self.onFinished?(self.source.key)
+ } else {
+ self.ws.sendJSON(["t": "error", "message": error])
+ }
+ self.end()
+ return
+ }
+ self.sendInfo()
+ }
+ }
+
+ /// Applies the controller's bitrate and tier. On the encoding queue.
+ private func adapt(maxLong: Int) {
+ guard let bps = controller.update(now: nowUs()) else { return }
+ encoder.setBitrate(bps)
+ let scale = controller.scale
+ if scale != appliedScale {
+ appliedScale = scale
+ capture.setMaxLong(max(320, Int(Double(maxLong) * scale)))
+ }
+ resendIfRoom() // a lower tier may let a held frame go now
+ }
+
+ /// While someone is typing or clicking, the viewer sends a tiny message this
+ /// often (ms, 0 = off), so the Frame's Wi-Fi doesn't doze between the input
+ /// and the frame that answers it (power saving is on there).
+ static let warmMs = Int(ProcessInfo.processInfo.environment["FRAME_MAC_VIEW_WARM"] ?? "") ?? 0
+
+ func sendInfo() {
+ ws.sendJSON(["t": "info", "src": source.key, "title": capture.title, "app": capture.app, "codec": codec.rawValue,
+ "input": source == .test || Input.allowed, "warm": Session.warmMs,
+ "aspect": Double(capture.frameRect.width / max(capture.frameRect.height, 1))])
+ }
+
+ var isStopped: Bool { lock.lock(); defer { lock.unlock() }; return stopped }
+
+ func end() {
+ lock.lock()
+ let was = stopped
+ stopped = true
+ last = nil
+ lock.unlock()
+ guard !was else { return }
+ statsTimer?.cancel()
+ capture.stop()
+ encodeQueue.async { self.encoder.invalidate() }
+ let owner = id
+ DispatchQueue.main.async { Input.releaseAll(owner: owner) }
+ ws.close()
+ onEnd?(self)
+ }
+
+ /// A point in the picture (0...1 each way) -> Mac global coordinates.
+ private func point(_ x: Double, _ y: Double) -> CGPoint {
+ let r = capture.frameRect
+ return CGPoint(x: r.minX + min(max(x, 0), 1) * r.width, y: r.minY + min(max(y, 0), 1) * r.height)
+ }
+
+ /// Input with an id (from the viewer) has been posted: the next picture
+ /// the Mac composites is its first chance to show.
+ private func injected(_ m: [String: Any], kind: String) {
+ guard let iid = (m["i"] as? NSNumber)?.uint32Value, iid != 0 else { return }
+ let now = nowUs()
+ stats.addInput(InputRecord(id: iid, kind: kind, viewer: Int64(num(m["tv"]) ?? 0), injected: now))
+ lock.lock(); pendingEcho = (iid, now); lock.unlock()
+ }
+
+ /// Timing reports from the viewer, in the agent's clock.
+ private func report(_ t: String, _ m: [String: Any]) -> Bool {
+ switch t {
+ case "ping":
+ ws.sendJSON(["t": "pong", "c": m["c"] ?? 0, "a": nowUs()])
+ case "rx":
+ guard let s = (m["s"] as? NSNumber)?.uint32Value else { break }
+ if let r = num(m["r"]) { stats.update(s) { $0.received = Int64(r) } }
+ if controller.acked(seq: s, at: nowUs()) { resendIfRoom() }
+ case "fd":
+ for f in m["f"] as? [[NSNumber]] ?? [] where f.count >= 4 {
+ stats.update(f[0].uint32Value) {
+ $0.decoded = f[1].int64Value
+ $0.drawn = f[2].int64Value
+ $0.vsync = f[3].int64Value
+ }
+ }
+ let dropped = Int(num(m["drop"]) ?? 0)
+ stats.withLock { stats.viewerDropped += dropped }
+ case "w":
+ break // keep-warm filler, see sendInfo
+ case "clock":
+ stats.withLock {
+ stats.rtt = num(m["rtt"]) ?? 0
+ stats.clockSynced = true
+ if let d = m["dec"] as? String { stats.decoder = d }
+ }
+ default:
+ return false
+ }
+ return true
+ }
+
+ /// Asks the viewer to do something for a benchmark (type, click, show its overlay).
+ func bench(_ m: [String: Any]) { ws.sendJSON(m.merging(["t": "bench"]) { a, _ in a }) }
+
+ private func handle(_ text: String) {
+ guard !isStopped, let d = text.data(using: .utf8),
+ let m = try? JSONSerialization.jsonObject(with: d) as? [String: Any], let t = m["t"] as? String else { return }
+ if report(t, m) { return }
+ let x = m["x"] as? Double ?? -1, y = m["y"] as? Double ?? -1
+ if t == "ack" {
+ onAck?()
+ onAck = nil
+ return
+ }
+ if t == "key-frame" {
+ encodeQueue.async { self.encoder.requestKeyFrame() } // before the resend, same queue
+ lock.lock(); skipped = last != nil; lock.unlock()
+ resendIfRoom()
+ return
+ }
+ if source == .test {
+ let desc: String?
+ switch t {
+ case "m": desc = (m["e"] as? String) == "move" ? nil : "mouse \(m["e"] ?? "") button \(m["b"] ?? 0)"
+ case "wheel": desc = "wheel \(m["dx"] ?? 0), \(m["dy"] ?? 0)"
+ case "k": desc = (m["e"] as? String) == "down" ? "key \(m["code"] ?? "") \"\(m["key"] ?? "")\"" : nil
+ case "text": desc = "text \"\(m["s"] ?? "")\""
+ default: desc = nil
+ }
+ capture.pointer(x: x, y: y, text: desc)
+ if desc != nil { injected(m, kind: t) }
+ return
+ }
+ DispatchQueue.main.async { [self] in
+ guard !isStopped else { return } // Stop revokes input at once
+ switch t {
+ case "m":
+ let kind = m["e"] as? String ?? "move", b = m["b"] as? Int ?? 0
+ let p = point(x, y)
+ if kind == "down", case .window(let wid) = source, let pid = capture.pid {
+ Input.focus(window: wid, pid: pid)
+ }
+ Input.mouse(kind, button: b, at: p, owner: id)
+ if kind == "down" { injected(m, kind: "click") }
+ case "wheel":
+ Input.scroll(dx: m["dx"] as? Double ?? 0, dy: m["dy"] as? Double ?? 0, at: point(x, y), owner: id)
+ injected(m, kind: "wheel")
+ case "k":
+ let down = (m["e"] as? String) == "down"
+ Input.key(code: m["code"] as? String ?? "", key: m["key"] as? String ?? "",
+ down: down, mods: m["mods"] as? [String] ?? [], owner: id)
+ if down { injected(m, kind: "key") }
+ case "text":
+ if let s = m["s"] as? String, s.count <= 4096 { Input.text(s); injected(m, kind: "text") }
+ case "release":
+ Input.releaseAll(owner: id)
+ case "focus":
+ if case .window(let wid) = source, let pid = capture.pid { Input.focus(window: wid, pid: pid) }
+ default: break
+ }
+ }
+ }
+}
+
+func randomKey() -> String {
+ var bytes = [UInt8](repeating: 0, count: 24)
+ _ = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
+ return Data(bytes).base64EncodedString().replacingOccurrences(of: "+", with: "-")
+ .replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: "")
+}
+
+func sameSecret(_ a: String, _ b: String) -> Bool {
+ guard !a.isEmpty, a.utf8.count == b.utf8.count else { return false }
+ return zip(a.utf8, b.utf8).reduce(0, { $0 | ($1.0 ^ $1.1) }) == 0
+}
+
+final class Agent {
+ /// Frame Control's own key. It stays on the Mac: the Frame only ever sees
+ /// single-use tickets and per-stream reconnect keys, both tied to one source.
+ let token: String
+ let page: URL?
+ var sessions: [Int: Session] = [:] { didSet { keepDisplayAwake(!sessions.isEmpty) } }
+ var nextId = 1
+ let lock = NSLock()
+ private var assertion: IOPMAssertionID = 0
+ /// ticket -> (source, expiry, reconnect key once redeemed). A ticket opens
+ /// one viewer within a minute; it may be redeemed again, for the same key,
+ /// only until that viewer confirms it has the key ("ack").
+ private var tickets: [String: (src: String, expiry: Date, key: String?)] = [:]
+ /// reconnect key -> source, until Stop for that source.
+ private var reconnectKeys: [String: String] = [:]
+ /// Sources that ended for good (the window closed), for Frame Control.
+ private var finished = Set()
+
+ /// Ends every stream, then exits once separated windows are back on the
+ /// Mac's own screens (they're moved back, then their displays go 0.3 s later).
+ /// Idempotent: SIGTERM and the parent going away can both call it, and a
+ /// session already ended by /close may still be putting its window back.
+ private var quitting = false
+ func quit() {
+ lock.lock()
+ let all = Array(sessions.values), again = quitting
+ quitting = true
+ lock.unlock()
+ guard !again else { return }
+ for s in all { s.ws.sendJSON(["t": "close"]); s.end() }
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { exit(0) }
+ }
+
+ /// While anyone watches, keep the Mac's display on: a sleeping display
+ /// stops being drawn, so there'd be nothing to capture (and it would lock).
+ private func keepDisplayAwake(_ on: Bool) {
+ if on, assertion == 0 {
+ IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep as CFString,
+ IOPMAssertionLevel(kIOPMAssertionLevelOn),
+ "Frame Control is showing this Mac in a Steam Frame" as CFString, &assertion)
+ } else if !on, assertion != 0 {
+ IOPMAssertionRelease(assertion)
+ assertion = 0
+ }
+ }
+
+ init(token: String, page: URL?) {
+ self.token = token
+ self.page = page
+ }
+
+ /// Whether this request may open a stream of `src`: Frame Control's key,
+ /// an unused ticket for that source, or a live reconnect key for it.
+ private func mayStream(_ req: Request, src: String) -> String? {
+ lock.lock()
+ defer { lock.unlock() }
+ let now = Date()
+ tickets = tickets.filter { $0.value.expiry > now }
+ if sameSecret(req.query["k"] ?? "", token) { return randomKey() }
+ if let t = req.query["t"], let entry = tickets[t], entry.src == src {
+ // A retry before the viewer got its key gets the same key back.
+ let key = entry.key ?? randomKey()
+ tickets[t] = (src, entry.expiry, key)
+ reconnectKeys[key] = src
+ return key
+ }
+ if let r = req.query["r"], reconnectKeys[r] == src { return r }
+ return nil
+ }
+
+ /// The viewer has its reconnect key, so the ticket that led to it can't
+ /// be used again (also when the ack comes over a reconnection).
+ func acknowledged(key: String) {
+ lock.lock(); tickets = tickets.filter { $0.value.key != key }; lock.unlock()
+ }
+
+ func handle(_ req: Request, _ c: HTTPConnection) {
+ // Open to anything that reaches the port: a liveness check for the
+ // tunnel, and the viewer page, which holds no secrets.
+ switch (req.method, req.path) {
+ case ("GET", "/ping"): return c.respond(200, text: "frame-mac-view")
+ case ("GET", "/view"):
+ guard let page, let body = try? Data(contentsOf: page) else { return c.respond(404, text: "no viewer page") }
+ return c.respond(200, body: body, type: "text/html; charset=utf-8")
+ case ("GET", "/stream"):
+ guard let src = Source(req.query["src"] ?? "") else { return c.respond(400, text: "bad src") }
+ guard let key = mayStream(req, src: src.key) else { return c.respond(403, text: "forbidden") }
+ return stream(req, c, src: src, key: key)
+ default: break
+ }
+ guard sameSecret(req.query["k"] ?? req.headers["x-token"] ?? "", token) else {
+ return c.respond(403, text: "forbidden")
+ }
+ switch (req.method, req.path) {
+ case ("GET", "/status"):
+ lock.lock()
+ let list = sessions.values.map { s -> [String: Any] in
+ var e: [String: Any] = ["id": s.id, "src": s.source.key, "title": s.capture.title, "app": s.capture.app,
+ "stats": s.stats.summary(), "bitrate": s.encoder.bitrate,
+ "controller": s.controller.state()]
+ if let d = (s.capture as? SeparateSource)?.displayID { e["display"] = d }
+ return e
+ }
+ lock.unlock()
+ var s = permissionsJSON()
+ s["version"] = version
+ s["streams"] = list
+ lock.lock(); s["finished"] = Array(finished); lock.unlock()
+ c.respond(json: s)
+ case ("GET", "/windows"):
+ c.respond(json: ["windows": windowsJSON(), "screen": CGPreflightScreenCaptureAccess()])
+ case ("GET", "/displays"):
+ c.respond(json: ["displays": displaysJSON()])
+ case ("POST", "/ticket"):
+ guard let src = Source(req.query["src"] ?? "") else { return c.respond(400, text: "bad src") }
+ let t = randomKey()
+ lock.lock()
+ tickets[t] = (src.key, Date().addingTimeInterval(60), nil)
+ finished.remove(src.key)
+ lock.unlock()
+ c.respond(json: ["ticket": t])
+ case ("POST", "/close"):
+ // Tell viewers to close their windows, but don't rely on them:
+ // the sessions end here and can't reconnect.
+ let src = req.query["src"]
+ lock.lock()
+ let matching = sessions.values.filter { src == nil || $0.source.key == src }
+ reconnectKeys = reconnectKeys.filter { src != nil && $0.value != src }
+ tickets = tickets.filter { src != nil && $0.value.src != src } // not yet used ones too
+ lock.unlock()
+ for s in matching { s.ws.sendJSON(["t": "close"]) }
+ DispatchQueue.global().asyncAfter(deadline: .now() + 0.3) { for s in matching { s.end() } }
+ c.respond(json: ["closed": matching.count])
+ case ("GET", "/stats"):
+ // Frames the viewer has had time to report on, oldest first.
+ let since = UInt32(req.query["since"] ?? "") ?? 0
+ let settle = Int64(req.query["settle"] ?? "") ?? 1_500_000
+ lock.lock()
+ let list = sessions.values.filter { req.query["id"] == nil || "\($0.id)" == req.query["id"] }
+ lock.unlock()
+ c.respond(json: ["now": nowUs(), "streams": list.map { s -> [String: Any] in
+ ["id": s.id, "src": s.source.key, "frames": s.stats.settled(since: since, settle: settle).map(\.json),
+ "inputs": s.stats.inputList().map(\.json), "summary": s.stats.summary(),
+ "captured": s.stats.withLock { s.stats.captured }, "controller": s.controller.state(),
+ "events": s.controller.eventList()]
+ }])
+ case ("POST", "/bench"):
+ lock.lock()
+ let list = sessions.values.filter { $0.source.key == req.query["src"] }
+ lock.unlock()
+ var m: [String: Any] = [:]
+ for (k, v) in req.query where k != "k" && k != "src" { m[k] = Double(v) ?? v as Any }
+ for s in list { s.bench(m) }
+ c.respond(json: ["sent": list.count])
+ case ("GET", "/snapshot"):
+ // One JPEG of a display (for checks and thumbnails): ?display=ID
+ guard let id = UInt32(req.query["display"] ?? "") else { return c.respond(400, text: "display=ID") }
+ snapshot(display: id) { data, error in
+ if let data { c.respond(200, body: data, type: "image/jpeg") } else { c.respond(500, text: error ?? "failed") }
+ }
+ case ("POST", "/permissions"):
+ DispatchQueue.main.async { requestPermissions() }
+ c.respond(json: permissionsJSON())
+ default:
+ c.respond(404, text: "not found")
+ }
+ }
+
+ private func stream(_ req: Request, _ c: HTTPConnection, src: Source, key: String) {
+ let codec = Codec(rawValue: req.query["codec"] ?? "h264") ?? .h264
+ let maxLong = min(max(Int(req.query["max"] ?? "") ?? 1920, 320), 3840)
+ let fps = min(max(Int(req.query["fps"] ?? "") ?? 60, 5), 120)
+ let bpp = min(max(Double(req.query["bpp"] ?? "") ?? 0.1, 0.02), 0.5)
+ guard let ws = c.upgrade(req) else { return }
+ let capture: CaptureSource
+ switch src {
+ case .test: capture = TestSource()
+ case .separate(let id): capture = SeparateSource(windowID: id)
+ default: capture = SCKSource(src)
+ }
+ lock.lock()
+ // One live viewer per key: a reconnection (or a second use of an
+ // unacknowledged ticket) replaces the one before.
+ let replaced = sessions.values.filter { $0.reconnectKey == key }
+ let session = Session(id: nextId, source: src, capture: capture, ws: ws, codec: codec, fps: fps,
+ bitsPerPixel: bpp, reconnectKey: key)
+ nextId += 1
+ sessions[session.id] = session
+ lock.unlock()
+ for old in replaced { old.end() }
+ session.onEnd = { [weak self] s in
+ guard let self else { return }
+ self.lock.lock(); self.sessions[s.id] = nil; self.lock.unlock()
+ }
+ // The source is gone (its window closed): its viewers can't come back.
+ session.onFinished = { [weak self] src in
+ guard let self else { return }
+ self.lock.lock()
+ self.finished.insert(src)
+ self.reconnectKeys = self.reconnectKeys.filter { $0.value != src }
+ self.lock.unlock()
+ }
+ session.onAck = { [weak self] in self?.acknowledged(key: key) }
+ session.start(maxLong: maxLong, fps: fps)
+ }
+}
+
+func snapshot(display id: CGDirectDisplayID, completion: @escaping (Data?, String?) -> Void) {
+ SCShareableContent.getExcludingDesktopWindows(false, onScreenWindowsOnly: true) { content, error in
+ guard let d = content?.displays.first(where: { $0.displayID == id }) else {
+ return completion(nil, error?.localizedDescription ?? "no such display")
+ }
+ let filter = SCContentFilter(display: d, excludingWindows: [])
+ let cfg = SCStreamConfiguration()
+ cfg.width = Int(Double(d.width) * Double(filter.pointPixelScale))
+ cfg.height = Int(Double(d.height) * Double(filter.pointPixelScale))
+ cfg.showsCursor = true
+ SCScreenshotManager.captureImage(contentFilter: filter, configuration: cfg) { image, error in
+ guard let image else { return completion(nil, error?.localizedDescription ?? "no image") }
+ let rep = NSBitmapImageRep(cgImage: image)
+ completion(rep.representation(using: .jpeg, properties: [.compressionFactor: 0.85]), nil)
+ }
+ }
+}
+
+func requestPermissions() {
+ if !CGPreflightScreenCaptureAccess() { CGRequestScreenCaptureAccess() }
+ if !AXIsProcessTrusted() {
+ AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary)
+ }
+}
+
+func argument(_ name: String, in args: [String]) -> String? {
+ guard let i = args.firstIndex(of: name), i + 1 < args.count else { return nil }
+ return args[i + 1]
+}
+
+let args = Array(CommandLine.arguments.dropFirst())
+switch args.first {
+case "windows":
+ printJSON(["windows": windowsJSON(), "screen": CGPreflightScreenCaptureAccess()])
+case "displays":
+ printJSON(["displays": displaysJSON()])
+case "permissions":
+ printJSON(permissionsJSON())
+case "request-permissions":
+ requestPermissions()
+ printJSON(permissionsJSON())
+case "serve":
+ let port = UInt16(argument("--port", in: args) ?? "") ?? 0
+ let token = ProcessInfo.processInfo.environment["FRAME_MAC_VIEW_TOKEN"] ?? ""
+ guard !token.isEmpty else {
+ FileHandle.standardError.write(Data("frame-mac-view: set FRAME_MAC_VIEW_TOKEN\n".utf8))
+ exit(2)
+ }
+ let page = argument("--page", in: args).map { URL(fileURLWithPath: $0) }
+ let agent = Agent(token: token, page: page)
+ // Quit when the parent goes away (it holds our stdin open), or on SIGTERM,
+ // but first end every stream, so separated windows go back where they
+ // were before their displays disappear.
+ if args.contains("--exit-on-eof") {
+ DispatchQueue.global().async {
+ while FileHandle.standardInput.availableData.count > 0 {}
+ agent.quit()
+ }
+ }
+ signal(SIGTERM, SIG_IGN)
+ let sigterm = DispatchSource.makeSignalSource(signal: SIGTERM, queue: .main)
+ sigterm.setEventHandler { agent.quit() }
+ sigterm.resume()
+ let server: Server
+ do {
+ server = try Server(port: port) { req, c in agent.handle(req, c) }
+ } catch {
+ FileHandle.standardError.write(Data("frame-mac-view: \(error)\n".utf8))
+ exit(1)
+ }
+ server.start { [server] error in
+ if let error {
+ FileHandle.standardError.write(Data("frame-mac-view: can't listen on 127.0.0.1:\(port): \(error)\n".utf8))
+ exit(1)
+ }
+ print("frame-mac-view listening on 127.0.0.1:\(server.port ?? port)")
+ fflush(stdout)
+ }
+ // A real (background, no Dock icon) AppKit event loop, not just a run
+ // loop: without it this process never hears that displays were added or
+ // changed mode, so NSScreen and CGDisplayCopyDisplayMode stay stale for
+ // the displays Separate mode creates.
+ let app = NSApplication.shared
+ app.setActivationPolicy(.prohibited)
+ withExtendedLifetime((server, sigterm)) { app.run() }
+default:
+ FileHandle.standardError.write(Data("usage: frame-mac-view serve|windows|displays|permissions|request-permissions\n".utf8))
+ exit(2)
+}
diff --git a/mac/frame-mac-view/build.sh b/mac/frame-mac-view/build.sh
new file mode 100755
index 0000000..f391a58
--- /dev/null
+++ b/mac/frame-mac-view/build.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Builds the Mac streaming agent into mac/bin/frame-mac-view (arm64, macOS 14+).
+# Frame Control runs it for "Mac in the headset"; the Electron app bundles it.
+set -eu
+here=$(cd "$(dirname "$0")" && pwd)
+out=${1:-$here/../bin/frame-mac-view}
+mkdir -p "$(dirname "$out")"
+xcrun swiftc -O -swift-version 5 -target arm64-apple-macos14.0 \
+ -import-objc-header "$here/Sources/CGVirtualDisplay.h" \
+ -o "$out" "$here"/Sources/*.swift
+echo "built $out"
diff --git a/mac/frame-mac-view/lab.sh b/mac/frame-mac-view/lab.sh
new file mode 100755
index 0000000..11440d4
--- /dev/null
+++ b/mac/frame-mac-view/lab.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+# Wraps the agent in "Frame Mac View Lab.app" for testing from a terminal or
+# an agent session: macOS then asks for Screen Recording and Accessibility
+# for this app rather than for the terminal. Signed with an Apple Development
+# identity if there is one, so the permissions survive rebuilds.
+# mac/frame-mac-view/lab.sh build the app into mac/bin/
+# mac/frame-mac-view/lab.sh serve also start it (token, port and log in ~/Library/Caches/frame-mac-view-lab,
+# readable only by you: the token opens every window and injects input)
+# mac/frame-mac-view/lab.sh serve-only restart it without rebuilding
+set -eu
+here=$(cd "$(dirname "$0")" && pwd)
+app="$here/../bin/Frame Mac View Lab.app"
+if [ "${1:-}" != serve-only ]; then
+ mkdir -p "$app/Contents/MacOS"
+ sh "$here/build.sh" "$app/Contents/MacOS/frame-mac-view" >/dev/null
+ cat > "$app/Contents/Info.plist" <
+
+
+CFBundleIdentifier com.saphid.frame-mac-view-lab
+CFBundleExecutable frame-mac-view
+CFBundleName Frame Mac View Lab
+CFBundlePackageType APPL
+CFBundleShortVersionString 1
+LSUIElement
+NSScreenCaptureUsageDescription Tests streaming Mac windows into the Steam Frame.
+
+PLIST
+ id=$(security find-identity -v -p codesigning 2>/dev/null | awk '/Apple Development/ {print $2; exit}')
+ codesign -f -s "${id:--}" "$app" >/dev/null 2>&1
+ echo "built $app (signed ${id:-ad hoc})"
+fi
+if [ "${1:-}" = serve ] || [ "${1:-}" = serve-only ]; then
+ pkill -f "Frame Mac View Lab.app/Contents/MacOS/frame-mac-view" 2>/dev/null || true
+ dir="$HOME/Library/Caches/frame-mac-view-lab"
+ mkdir -p "$dir" && chmod 700 "$dir"
+ umask 077
+ token=$(openssl rand -hex 16)
+ rm -f "$dir/token" "$dir/port" "$dir/log"
+ echo "$token" > "$dir/token"
+ : > "$dir/log"
+ # Experiment switches pass through: FRAME_MAC_VIEW_ENCODER (Encoder.swift),
+ # _VD_HZ (Separate.swift), _ADAPT (Controller.swift), _WARM (main.swift).
+ open -n "$app" --env "FRAME_MAC_VIEW_TOKEN=$token" --env "FRAME_MAC_VIEW_ENCODER=${FRAME_MAC_VIEW_ENCODER:-}" --env "FRAME_MAC_VIEW_VD_HZ=${FRAME_MAC_VIEW_VD_HZ:-}" \
+ --env "FRAME_MAC_VIEW_ADAPT=${FRAME_MAC_VIEW_ADAPT:-}" --env "FRAME_MAC_VIEW_WARM=${FRAME_MAC_VIEW_WARM:-}" --stdout "$dir/log" --stderr "$dir/log" \
+ --args serve --port 0 --page "$here/../../ui/mac-view.html"
+ for _ in 1 2 3 4 5 6 7 8 9 10; do grep -q listening "$dir/log" && break; sleep 0.3; done
+ sed -n 's/.*127\.0\.0\.1:\([0-9]*\).*/\1/p' "$dir/log" | head -n 1 > "$dir/port"
+ echo "lab on 127.0.0.1:$(cat "$dir/port")"
+fi
diff --git a/scripts/macview-bench.py b/scripts/macview-bench.py
new file mode 100755
index 0000000..cffa8b5
--- /dev/null
+++ b/scripts/macview-bench.py
@@ -0,0 +1,847 @@
+#!/usr/bin/env python3
+"""Benchmark "Mac in the headset" on the real Frame, so every change to the
+stream is judged by numbers (docs/mac-in-headset.md, "Measuring").
+
+ scripts/macview-bench.py run [--scenario test,scroll,type] [--net 8] [--label x]
+ scripts/macview-bench.py compare bench/results/A.json bench/results/B.json
+ scripts/macview-bench.py ab --arm off:FRAME_MAC_VIEW_ADAPT=0 --arm on: --repeat 3 --scenario scroll
+
+`run` uses the lab agent (mac/frame-mac-view/lab.sh serve), which has
+Screen Recording and Accessibility. It opens a viewer on the Frame the way
+Frame Control does, drives a fixed scenario, and reads the agent's per-frame
+records: when the Mac composited each frame, when it was encoded and sent,
+and when the viewer received, decoded and drew it (the viewer's clock is
+synced to the Mac's). "content" is Mac composited -> drawn in the viewer;
+what the Frame's compositor adds after that is reported ("present",
+"fps_shown") but not graded, because an unworn Frame throttles panels to
+36 fps (15 in standby) after a few seconds whatever they draw. It writes a summary to bench/results/ and exits 1 if a
+target is missed ("bad"), or if --baseline is given and a key number got
+more than 10% worse.
+
+Wi-Fi changes from minute to minute, so single runs taken at different times
+can differ by more than a change does. `ab` restarts the lab agent with each
+arm's settings (environment variables read by the agent), runs the arms
+interleaved, and prints each metric's median and range per arm.
+
+Nobody needs to wear the headset. The scroll and type scenarios open a
+throwaway Chrome window (its own profile in /tmp) on the Mac.
+
+Network conditions come from a relay on the Mac between the agent and the
+tunnel, so they need no sudo:
+ --net 8 8 Mbit/s the whole run
+ --net 50@0,8@10,50@20 50 Mbit/s, 8 from 10 s, back to 50 from 20 s
+ --delay 30 30 ms more one-way delay
+The relay holds at most --buffer ms of data at the current rate (default
+250 ms, like a bloated Wi-Fi queue), then pushes back on the agent.
+
+Stdlib only.
+"""
+import argparse
+import asyncio
+import datetime
+import json
+import os
+import platform
+import re
+import statistics
+import subprocess
+import sys
+import threading
+import time
+import urllib.request
+from pathlib import Path
+from urllib.parse import quote, urlencode # %20, not +: the agent's URLComponents keeps +
+
+ROOT = Path(__file__).resolve().parent.parent
+sys.path.insert(0, str(ROOT / "ui"))
+import frame_macview # noqa: E402
+
+RESULTS = ROOT / "bench" / "results"
+PAGES = ROOT / "bench" / "pages"
+CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
+CHROME_PROFILE = "/tmp/fmv-bench-chrome"
+LAB_DIR = Path.home() / "Library" / "Caches" / "frame-mac-view-lab" # lab.sh serve
+
+# The acceptance bar (the handoff's targets): (feels local, acceptable) per
+# metric; worse than acceptable is "bad". Latencies in ms.
+TARGETS = {
+ "content_p50": (25, 60), "content_p95": (40, 100),
+ "input_p50": (50, 100), "input_p95": (70, 150),
+ "fps": (58, 45), # higher is better
+ "late_pct": (1, 5), "stall_max": (100, 250),
+ "adapt_s": (1, 3),
+}
+HIGHER_IS_BETTER = {"fps"}
+# Content that changes every frame, so fps and stalls mean something.
+MOVING = {"test", "scroll"}
+
+
+def ms(a, b):
+ return (b - a) / 1000 if a and b else None
+
+
+def pct(values, p):
+ v = sorted(x for x in values if x is not None)
+ if not v:
+ return None
+ return round(v[min(len(v) - 1, max(0, round(p * (len(v) - 1))))], 1)
+
+
+def dist(values):
+ v = [x for x in values if x is not None]
+ return {"p50": pct(v, 0.5), "p95": pct(v, 0.95), "p99": pct(v, 0.99), "n": len(v)} if v else {"n": 0}
+
+
+# ---- the network relay ----
+
+class Relay:
+ """Agent <-> tunnel, shaping the agent-to-Frame direction: a bottleneck
+ of `rate` Mbit/s with a queue of `buffer_ms`, plus `delay` ms each way."""
+
+ def __init__(self, target_port, schedule, delay_ms=0, buffer_ms=250):
+ self.target_port = target_port
+ self.schedule = schedule # [(seconds from start, Mbit/s or None)]
+ self.delay = delay_ms / 1000
+ self.buffer_ms = buffer_ms
+ self.rate = schedule[0][1] if schedule else None
+ self.next_free = 0.0
+ self.port = None
+ self.loop = asyncio.new_event_loop()
+ # Connections reset when a run tears the tunnel down; that's expected.
+ self.loop.set_exception_handler(lambda loop, ctx: None if isinstance(ctx.get("exception"), ConnectionError)
+ else loop.default_exception_handler(ctx))
+ self.started = None
+ self.queued_max = 0
+
+ def start(self):
+ ready = threading.Event()
+
+ def run():
+ asyncio.set_event_loop(self.loop)
+ server = self.loop.run_until_complete(asyncio.start_server(self._client, "127.0.0.1", 0))
+ self.port = server.sockets[0].getsockname()[1]
+ ready.set()
+ self.loop.run_forever()
+
+ threading.Thread(target=run, daemon=True).start()
+ ready.wait(5)
+
+ def begin(self):
+ """Start the schedule's clock (when the measured part begins)."""
+ self.started = time.monotonic()
+ for at, rate in self.schedule:
+ self.loop.call_soon_threadsafe(self.loop.call_later, at, self._set_rate, rate)
+
+ def _set_rate(self, rate):
+ self.rate = rate
+
+ def rate_at(self, t):
+ r = None
+ for at, rate in self.schedule:
+ if t >= at:
+ r = rate
+ return r
+
+ async def _client(self, reader, writer):
+ try:
+ up_r, up_w = await asyncio.open_connection("127.0.0.1", self.target_port)
+ except OSError:
+ writer.close()
+ return
+ await asyncio.gather(self._shaped(up_r, writer), self._plain(reader, up_w), return_exceptions=True)
+ for w in (writer, up_w):
+ w.close()
+
+ async def _plain(self, reader, writer): # Frame -> agent: delay only
+ if not self.delay:
+ while data := await reader.read(65536):
+ writer.write(data)
+ await writer.drain()
+ writer.close()
+ return
+ # Each chunk leaves `delay` after it arrived, while reading goes on:
+ # a delay line, not a pause (which would add up behind a busy stream).
+ queue = asyncio.Queue()
+
+ async def send():
+ while (item := await queue.get())[1] is not None:
+ wait = item[0] - self.loop.time()
+ if wait > 0:
+ await asyncio.sleep(wait)
+ writer.write(item[1])
+ await writer.drain()
+ writer.close()
+
+ async def pump():
+ try:
+ while data := await reader.read(65536):
+ await queue.put((self.loop.time() + self.delay, data))
+ finally: # EOF or a reset: either way the sender finishes and closes
+ queue.put_nowait((0, None))
+
+ sender, pumper = asyncio.ensure_future(send()), asyncio.ensure_future(pump())
+ done, _ = await asyncio.wait({sender, pumper}, return_when=asyncio.FIRST_COMPLETED)
+ if sender in done: # the agent's side went away: stop reading, so the connection closes
+ pumper.cancel()
+ writer.close()
+ return
+ await sender # the viewer's side ended: deliver what's queued first
+
+ async def _shaped(self, reader, writer): # agent -> Frame
+ queue = asyncio.Queue()
+ state = {"bytes": 0}
+ room = asyncio.Event()
+ room.set()
+
+ async def send():
+ while True:
+ at, data = await queue.get()
+ if data is None:
+ return
+ wait = at - self.loop.time()
+ if wait > 0:
+ await asyncio.sleep(wait)
+ writer.write(data)
+ await writer.drain()
+ state["bytes"] -= len(data)
+ room.set()
+
+ sender = asyncio.ensure_future(send())
+ while data := await reader.read(16384):
+ now = self.loop.time()
+ depart = now
+ if self.rate:
+ depart = max(now, self.next_free) + len(data) * 8 / (self.rate * 1e6)
+ self.next_free = depart
+ state["bytes"] += len(data)
+ self.queued_max = max(self.queued_max, state["bytes"])
+ await queue.put((depart + self.delay, data))
+ # A full queue pushes back, as a real bottleneck's buffer does.
+ while self.rate and state["bytes"] > self.rate * 1e6 / 8 * self.buffer_ms / 1000:
+ room.clear()
+ await room.wait()
+ await queue.put((0, None))
+ await sender
+
+
+# ---- the lab agent, through Frame Control's own code ----
+
+class LabView(frame_macview.MacView):
+ """MacView against the already-running lab agent, with the tunnel
+ pointing at `tunnel_port` (the relay, or the agent itself)."""
+
+ def __init__(self, tunnel_ssh, run, frame, agent_port, token, tunnel_port):
+ super().__init__(tunnel_ssh, run, frame)
+ self.agent_port = agent_port
+ self.token = token
+ self.port = tunnel_port
+
+ class Alive:
+ def poll(self):
+ return None
+
+ self.agent = Alive()
+
+ def ensure_agent(self):
+ pass
+
+ def call(self, path, method="GET", **query):
+ url = f"http://127.0.0.1:{self.agent_port}{path}?{urlencode({**query, 'k': self.token}, quote_via=quote)}"
+ req = urllib.request.Request(url, method=method, data=b"" if method == "POST" else None)
+ with urllib.request.urlopen(req, timeout=15) as r:
+ return json.load(r)
+
+
+def ssh_runner(base):
+ def run(remote, stdin=None, timeout=30):
+ r = subprocess.run([*base, remote], input=stdin, capture_output=True, text=True, timeout=timeout)
+ if r.returncode:
+ e = RuntimeError((r.stderr or r.stdout).strip())
+ e.stdout = r.stdout
+ raise e
+ return r.stdout
+ return run
+
+
+# ---- CPU on both ends ----
+
+FRAME_CPU = r"""
+hz=$(getconf CLK_TCK)
+while :; do
+ t=$(date +%s.%N)
+ for g in viewer:frame-control/mac-view tailscaled:tailscaled sshd:'^sshd' gamescope:'^gamescope'; do
+ name=${g%%:*} pat=${g#*:} sum=0
+ for p in $(pgrep -f "$pat"); do
+ s=$(awk '{print $14+$15}' /proc/$p/stat 2>/dev/null) && sum=$((sum + s))
+ done
+ echo "$t $name $sum $hz"
+ done
+ echo "$t total $(awk '/^cpu /{print $2+$3+$4+$6+$7+$8}' /proc/stat) $hz"
+ sleep 2
+done
+"""
+
+
+class CpuSampler:
+ def __init__(self, frame_ssh, agent_pid):
+ self.agent_pid = agent_pid
+ self.mac = [] # (t, cpu seconds)
+ self.frame = {} # name -> [(t, ticks, hz)]
+ self.proc = subprocess.Popen([*frame_ssh, "bash -s"], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL, text=True)
+ self.proc.stdin.write(FRAME_CPU)
+ self.proc.stdin.close()
+ threading.Thread(target=self._read, daemon=True).start()
+ self.stop_flag = False
+ threading.Thread(target=self._mac, daemon=True).start()
+
+ def _read(self):
+ for line in self.proc.stdout:
+ parts = line.split()
+ if len(parts) == 4:
+ self.frame.setdefault(parts[1], []).append((float(parts[0]), int(parts[2]), int(parts[3])))
+
+ def _mac(self):
+ while not self.stop_flag:
+ out = subprocess.run(["ps", "-o", "cputime=", "-p", str(self.agent_pid)], capture_output=True,
+ text=True).stdout.strip()
+ m = re.match(r"(?:(\d+):)?(\d+):(\d+(?:\.\d+)?)", out)
+ if m:
+ h, mnt, s = m.groups()
+ self.mac.append((time.monotonic(), int(h or 0) * 3600 + int(mnt) * 60 + float(s)))
+ time.sleep(2)
+
+ def stop(self):
+ self.stop_flag = True
+ self.proc.terminate()
+
+ def summary(self):
+ def rate(samples):
+ if len(samples) < 2:
+ return None
+ (t0, a0, *h0), (t1, a1, *_) = samples[0], samples[-1]
+ hz = h0[0] if h0 else 1
+ return round((a1 - a0) / hz / (t1 - t0) * 100, 1)
+ out = {"mac_agent_pct": rate(self.mac)}
+ for name, s in self.frame.items():
+ out[f"frame_{name}_pct"] = rate(s)
+ if "frame_total_pct" in out and out["frame_total_pct"] is not None:
+ out["frame_total_pct"] = round(out["frame_total_pct"] / 8, 1) # 8 cores -> share of the whole
+ return out
+
+
+# ---- scenarios ----
+
+def chrome_window(page, size=(1280, 820)):
+ """Opens a page in a throwaway Chrome app window; returns (proc, title)."""
+ if not Path(CHROME).exists():
+ raise SystemExit("Google Chrome is needed for the scroll and type scenarios.")
+ proc = subprocess.Popen([CHROME, f"--user-data-dir={CHROME_PROFILE}", "--no-first-run",
+ "--no-default-browser-check", f"--window-size={size[0]},{size[1]}",
+ "--window-position=80,80", f"--app=file://{PAGES / page}"],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ return proc
+
+
+def find_window(mv, title, timeout=15):
+ end = time.time() + timeout
+ while time.time() < end:
+ for w in mv.call("/windows").get("windows", []):
+ if title in (w.get("title") or ""):
+ return w
+ time.sleep(0.5)
+ raise SystemExit(f"window {title!r} didn't appear")
+
+
+def git(*a):
+ return subprocess.run(["git", *a], cwd=ROOT, capture_output=True, text=True).stdout.strip()
+
+
+def parse_schedule(net):
+ if not net or net == "none":
+ return []
+ out = []
+ for part in net.split(","):
+ rate, _, at = part.partition("@")
+ out.append((float(at or 0), None if rate in ("", "none", "0") else float(rate)))
+ return sorted(out)
+
+
+def run_scenario(args, scenario, agent_port, token, frame_ssh):
+ schedule = parse_schedule(args.net)
+ relay = None
+ tunnel_port = agent_port
+ if schedule or args.delay:
+ relay = Relay(agent_port, schedule, args.delay, args.buffer)
+ relay.start()
+ tunnel_port = relay.port
+ tunnel_ssh = list(frame_ssh[:-1]) + args.ssh_opt
+ mv = LabView(tunnel_ssh, ssh_runner(frame_ssh), frame_ssh[-1], agent_port, token, tunnel_port)
+ mv.prefer_usb = args.usb # otherwise the path is --host's (or ssh's default)
+ if args.browser_flag is not None:
+ mv.browser_flags = [f for f in args.browser_flag if f]
+ chrome = None
+ src = "test"
+ try:
+ if scenario in ("scroll", "type"):
+ chrome = chrome_window(f"{scenario}.html")
+ w = find_window(mv, f"fmv-bench {scenario}")
+ src = f"{args.mode}:{w['id']}"
+ t = time.time()
+ shown = mv.show(src, args.quality, 1280, 820)
+ show_s = round(time.time() - t, 2)
+ # Wait for frames, then measure from a clean start.
+ stream = None
+ end = time.time() + 20
+ while time.time() < end and not stream:
+ for s in mv.call("/status").get("streams", []):
+ if s["src"] == src and s.get("stats", {}).get("fps", 0) > 0:
+ stream = s
+ time.sleep(0.5)
+ if not stream:
+ raise SystemExit(f"{scenario}: no frames reached the viewer within 20 s")
+ time.sleep(args.warmup)
+ first = mv.call("/stats", id=stream["id"], settle=0)["streams"][0]
+ since = max([f["s"] for f in first["frames"]] or [0])
+ captured_before = first["captured"]
+ start_mac_us = mv.call("/stats", id=stream["id"])["now"]
+ agent_pid = int(subprocess.run(["pgrep", "-f", "Frame Mac View Lab.app/Contents/MacOS/frame-mac-view"],
+ capture_output=True, text=True).stdout.split()[0])
+ cpu = CpuSampler(frame_ssh, agent_pid)
+ if relay:
+ relay.begin()
+ expected = 0 # input events the harness asked the viewer for
+ if scenario == "type":
+ mv.call("/bench", method="POST", src=src, action="click", x=0.5, y=0.3)
+ time.sleep(0.5)
+ # Steady typing, then slow typing that lets the link go idle.
+ fast = "the quick brown fox jumps over the lazy dog "
+ n_fast = max(10, int((args.duration * 0.6) / 0.2))
+ mv.call("/bench", method="POST", src=src, action="type", text=(fast * (n_fast // len(fast) + 1))[:n_fast],
+ interval=200)
+ expected += n_fast
+ time.sleep(n_fast * 0.2 + 0.5)
+ slow_n = max(3, int(args.duration * 0.4 / 1.5))
+ mv.call("/bench", method="POST", src=src, action="type", text=("idle typing test " * (slow_n // 17 + 1))[:slow_n],
+ interval=1500)
+ expected += slow_n
+ time.sleep(slow_n * 1.5 + 0.5)
+ else:
+ # Clicks on the test pattern measure the input path; the scroll
+ # page just scrolls.
+ end = time.time() + args.duration
+ while time.time() < end:
+ if scenario == "test":
+ mv.call("/bench", method="POST", src=src, action="click", x=0.3, y=0.5)
+ expected += 1
+ time.sleep(0.5)
+ at_end = mv.call("/stats", id=stream["id"], since=2**32 - 1)
+ end_mac_us = at_end["now"]
+ captured_end = at_end["streams"][0]["captured"] if at_end["streams"] else None
+ time.sleep(2) # let the viewer report the last frames
+ streams = mv.call("/stats", id=stream["id"], since=since)["streams"]
+ if not streams:
+ raise SystemExit(f"{scenario}: the stream ended during the run (did the Frame go to sleep?)")
+ data = streams[0]
+ cpu.stop()
+ if args.raw:
+ Path(f"{args.raw}-{scenario}.json").write_text(json.dumps(data))
+ result = summarize(scenario, data, start_mac_us, end_mac_us, args, relay, schedule, expected)
+ result["controller"] = data.get("controller", {})
+ result["events"] = [{"t": round((e["t"] - start_mac_us) / 1e6, 2), "e": e["e"]}
+ for e in data.get("events", []) if e["t"] >= start_mac_us]
+ result["captured"] = (captured_end if captured_end is not None else data["captured"]) - captured_before
+ result["source_fps"] = round(result["captured"] / max(result["duration_s"], 1), 1)
+ result["cpu"] = cpu.summary()
+ result["viewer"] = data["summary"].get("decoder", "")
+ result["show_s"] = show_s
+ result["panel"] = shown.get("panel")
+ result["src"] = src
+ result["route"] = mv.route
+ return result
+ finally:
+ try:
+ mv.stop(src)
+ except Exception: # noqa: BLE001 - best effort
+ pass
+ mv.closing = True # or its supervisor reopens the tunnel
+ if mv.tunnel and mv.tunnel.poll() is None:
+ mv.tunnel.terminate()
+ mv.tunnel.wait(5)
+ if relay:
+ relay.loop.call_soon_threadsafe(relay.loop.stop)
+ if chrome:
+ chrome.terminate()
+ time.sleep(3) # Stop ends the Frame's viewer browser 2 s later
+
+
+def composited(f):
+ """When the Mac had the frame: its display time, or when ScreenCaptureKit
+ handed it over if that was earlier (display time can be a few ms ahead of
+ delivery, which would make latency look lower than it is)."""
+ return min(f["cap"], f["arr"]) if f["arr"] else f["cap"]
+
+
+def summarize(scenario, data, start_us, end_us, args, relay, schedule, expected=0):
+ frames = [f for f in data["frames"] if start_us <= f["cap"] <= end_us]
+ inputs = [i for i in data["inputs"] if i["tv"] and i["tv"] >= start_us]
+ by_seq = {f["s"]: f for f in data["frames"]}
+ drawn = [f for f in frames if f["drw"]]
+ shown = [f for f in frames if f["vs"]]
+ # The whole measured interval, so a stall at either end still counts.
+ duration = (end_us - start_us) / 1e6
+ stages = {
+ "capture": [ms(f["cap"], f["arr"]) for f in frames],
+ "queue": [ms(f["arr"], f["e0"]) for f in frames],
+ "encode": [ms(f["e0"], f["e1"]) for f in frames],
+ "socket": [ms(f["snd"], f["wire"]) for f in frames],
+ "network": [ms(f["e1"], f["rx"]) for f in frames],
+ "decode": [ms(f["rx"], f["dec"]) for f in frames],
+ "draw": [ms(f["dec"], f["drw"]) for f in frames],
+ # Waiting for the browser's next frame: set by the Frame's compositor,
+ # which throttles panels nobody is looking at (see "Measuring").
+ "present": [ms(f["drw"], f["vs"]) for f in frames],
+ "content": [ms(composited(f), f["drw"]) for f in frames],
+ "content_shown": [ms(composited(f), f["vs"]) for f in frames],
+ }
+ out = {"scenario": scenario, "frames_sent": len(frames), "frames_drawn": len(drawn),
+ "frames_shown": len(shown), "duration_s": round(duration, 1)}
+ out["stages_ms"] = {k: dist(v) for k, v in stages.items()}
+ # Smoothness: gaps between frames reaching the viewer's canvas.
+ # Smoothness counts only what was drawn inside the interval; a frame
+ # captured just before the end but drawn after it still counts for latency.
+ dr = [start_us] + sorted(f["drw"] for f in drawn if f["drw"] <= end_us) + [end_us]
+ gaps = [(b - a) / 1000 for a, b in zip(dr, dr[1:]) if b > a]
+ target_fps = frame_macview.QUALITY[args.quality]["fps"]
+ out["fps"] = round(sum(f["drw"] <= end_us for f in drawn) / duration, 1) if duration else 0
+ out["fps_shown"] = round(sum(0 < f["vs"] <= end_us for f in shown) / duration, 1) if duration else 0
+ out["late_pct"] = round(100 * sum(g > 1.5 * 1000 / target_fps for g in gaps) / len(gaps), 2) if gaps else None
+ out["stall_max"] = round(max(gaps), 1) if gaps else None
+ out["stalls_over_100ms"] = sum(g > 100 for g in gaps)
+ out["mbps"] = round(sum(f["b"] for f in frames) * 8 / duration / 1e6, 2) if duration else 0
+ out["keyframes"] = sum(f["k"] for f in frames)
+ out["size"] = f"{frames[-1]['w']}x{frames[-1]['h']}" if frames else ""
+ out["captured"] = data.get("captured")
+ out["viewer_never_drawn"] = len(frames) - len(drawn)
+ # Input: the viewer's event -> the first frame after it drawn (and shown).
+ lat, lat_shown = [], []
+ parts = {"uplink": [], "mac": [], "back": []}
+ for i in inputs:
+ f = by_seq.get(i["frame"])
+ if f and f["drw"]:
+ lat.append(ms(i["tv"], f["drw"]))
+ lat_shown.append(ms(i["tv"], f["vs"]))
+ parts["uplink"].append(ms(i["tv"], i["inj"]))
+ parts["mac"].append(ms(i["inj"], composited(f)))
+ parts["back"].append(ms(composited(f), f["drw"]))
+ out["input_ms"] = dist(lat)
+ out["input_shown_ms"] = dist(lat_shown)
+ out["input_parts_ms"] = {k: dist(v) for k, v in parts.items()}
+ out["inputs"] = {"asked": expected, "sent": len(inputs), "seen": len(lat)}
+ # A per-second timeline, for adaptation.
+ timeline = []
+ t0 = start_us
+ for sec in range(int(duration) + 1):
+ a, b = t0 + sec * 1_000_000, t0 + (sec + 1) * 1_000_000
+ fs = [f for f in frames if a <= f["cap"] < b]
+ if not fs:
+ continue
+ timeline.append({
+ "t": sec, "fps": sum(1 for f in fs if f["drw"]), "mbps": round(sum(f["b"] for f in fs) * 8 / 1e6, 2),
+ "content_p50": pct([ms(composited(f), f["drw"]) for f in fs], 0.5),
+ "content_p95": pct([ms(composited(f), f["drw"]) for f in fs], 0.95),
+ "bitrate": fs[-1].get("br"), "tier": fs[-1].get("tier"), "w": fs[-1]["w"],
+ "net": relay.rate_at(sec) if relay else None,
+ })
+ out["timeline"] = timeline
+ out["adapt"] = adaptation(timeline, schedule, duration)
+ out["content_p50"] = out["stages_ms"]["content"].get("p50")
+ out["content_p95"] = out["stages_ms"]["content"].get("p95")
+ out["input_p50"] = out["input_ms"].get("p50")
+ out["input_p95"] = out["input_ms"].get("p95")
+ out["grades"] = grade(out, scenario, args.quality)
+ return out
+
+
+def adaptation(timeline, schedule, duration=float("inf")):
+ """After each drop in the link's rate: how long until latency was within
+ bounds again and stayed there for 3 s. The bound is the acceptable p95
+ (100 ms), or 1.5 times the p95 before the drop if that was higher: on a
+ slower link each frame takes longer to send, so latency can't return to
+ what it was on the fast one."""
+ steps = []
+ for n, ((at, rate), (_, before)) in enumerate(zip(schedule[1:], schedule)):
+ # Settling must happen while this rate lasts, not after the link recovers.
+ until = min(schedule[n + 2][0] if n + 2 < len(schedule) else float("inf"), duration) # or the run ends
+ if before is not None and (rate is None or rate >= before):
+ continue
+ pre = [s["content_p95"] for s in timeline if at - 4 <= s["t"] < at and s["content_p95"]]
+ base = max(float(TARGETS["content_p95"][1]), 1.5 * statistics.median(pre) if pre else 0)
+ after = [s for s in timeline if s["t"] >= at]
+ worst = max([s["content_p95"] or 0 for s in after[:10]] or [0])
+ settled = None
+ for i, s in enumerate(after):
+ window = after[i:i + 3]
+ # Three populated seconds in a row: a second with no frames at all
+ # is a stall, not a pass.
+ if (len(window) == 3 and [w["t"] for w in window] == [s["t"], s["t"] + 1, s["t"] + 2] and s["t"] + 3 <= until
+ and all((w["content_p95"] or 1e9) <= base for w in window)):
+ settled = s["t"] - at
+ break
+ steps.append({"at": at, "to_mbit": rate, "p95_bound": round(base, 1), "worst_p95": worst,
+ "settled_s": settled})
+ return steps
+
+
+def grade(out, scenario, quality="balanced"):
+ g = {}
+ checks = ["content_p50", "content_p95"]
+ asked = max(out["inputs"].get("asked", 0), out["inputs"]["sent"])
+ if asked:
+ checks += ["input_p50", "input_p95"]
+ # Input that never reaches the Mac, or never shows up on screen, is a
+ # failure, not a gap in the data.
+ seen = out["inputs"]["seen"] / asked
+ g["input_replies"] = "local" if seen >= 0.95 else "acceptable" if seen >= 0.8 else "bad"
+ if scenario in MOVING:
+ checks += ["fps", "late_pct", "stall_max"]
+ for k in checks:
+ v = out.get(k)
+ if v is None:
+ g[k] = "missing"
+ continue
+ good, ok = TARGETS[k]
+ if k == "fps": # the targets are for 60 fps; Light and Compatible ask for fewer
+ want = frame_macview.QUALITY[quality]["fps"]
+ good, ok = good * want / 60, ok * want / 60
+ if k in HIGHER_IS_BETTER:
+ g[k] = "local" if v >= good else "acceptable" if v >= ok else "bad"
+ else:
+ g[k] = "local" if v <= good else "acceptable" if v <= ok else "bad"
+ for step in out["adapt"]:
+ s = step["settled_s"]
+ good, ok = TARGETS["adapt_s"]
+ g[f"adapt@{step['at']:g}s"] = "bad" if s is None or s > ok else "local" if s <= good else "acceptable"
+ return g
+
+
+# ---- compare ----
+
+KEYS = [("content_p50", "content p50 ms"), ("content_p95", "content p95 ms"), ("input_p50", "input p50 ms"),
+ ("input_p95", "input p95 ms"), ("fps", "fps"), ("late_pct", "late %"), ("stall_max", "max gap ms"),
+ ("fps_shown", "fps shown"), ("mbps", "Mbit/s")]
+STAGE_KEYS = ["capture", "queue", "encode", "socket", "network", "decode", "draw", "present"]
+
+
+def load(path):
+ return json.loads(Path(path).read_text())
+
+
+def compare(a, b, threshold=0.10):
+ """Prints before/after per scenario; returns the regressions."""
+ regressions = []
+ print(f"A: {a['label']} ({a['commit']}, {a['date']})\nB: {b['label']} ({b['commit']}, {b['date']})")
+ for sc in b["scenarios"]:
+ ra = next((x for x in a["scenarios"] if x["scenario"] == sc["scenario"]), None)
+ if not ra:
+ continue
+ print(f"\n{sc['scenario']:<10} {'A':>10} {'B':>10} {'change':>9}")
+ rows = [(k, n, ra.get(k), sc.get(k)) for k, n in KEYS]
+ rows += [(f"stage.{s}", f" {s} p50", ra["stages_ms"].get(s, {}).get("p50"), sc["stages_ms"].get(s, {}).get("p50"))
+ for s in STAGE_KEYS]
+ for k, name, va, vb in rows:
+ change = ""
+ if isinstance(va, (int, float)) and isinstance(vb, (int, float)) and va:
+ d = (vb - va) / abs(va)
+ change = f"{d:+.0%}"
+ worse = d < -threshold if k == "fps" else d > threshold
+ if worse and k in ("content_p50", "content_p95", "input_p50", "input_p95", "fps") and abs(vb - va) > 2:
+ regressions.append(f"{sc['scenario']} {name}: {va} -> {vb}")
+ change += " worse"
+ print(f"{name:<18} {fmt(va):>10} {fmt(vb):>10} {change:>9}")
+ return regressions
+
+
+def fmt(v):
+ return "–" if v is None else f"{v:g}" if isinstance(v, (int, float)) else str(v)
+
+
+# ---- main ----
+
+def add_run_args(r):
+ r.add_argument("--scenario", default="test,scroll,type")
+ r.add_argument("--duration", type=float, default=20)
+ r.add_argument("--warmup", type=float, default=3)
+ r.add_argument("--quality", default="balanced", choices=list(frame_macview.QUALITY))
+ r.add_argument("--mode", default="separate", choices=["separate", "window"], help="how Mac windows are shown")
+ r.add_argument("--net", default="", help="Mbit/s, or a schedule like 50@0,8@10,50@20")
+ r.add_argument("--delay", type=float, default=0, help="extra one-way delay, ms")
+ r.add_argument("--buffer", type=float, default=250, help="bottleneck queue, ms at the current rate")
+ r.add_argument("--frame", default="frame", help="ssh host of the Frame")
+ r.add_argument("--host", default="", help="connect to this address instead (e.g. the Frame's LAN IP)")
+ r.add_argument("--usb", action="store_true", help="use the Frame's USB-C network when plugged in, as Frame Control does")
+ r.add_argument("--ssh-opt", action="append", default=[], help="extra ssh option for the tunnel, e.g. -oIPQoS=ef")
+ r.add_argument("--browser-flag", action="append", default=None,
+ help="Chromium flag for the viewer (replaces Frame Control's defaults; '' for none)")
+ r.add_argument("--label", default="")
+ r.add_argument("--out", default="")
+ r.add_argument("--raw", default="", help="also write the agent's raw records here")
+
+
+def lab_agent():
+ try:
+ agent_port = int((LAB_DIR / "port").read_text())
+ token = (LAB_DIR / "token").read_text().strip()
+ urllib.request.urlopen(f"http://127.0.0.1:{agent_port}/ping", timeout=3)
+ return agent_port, token
+ except (OSError, ValueError):
+ raise SystemExit("Start the lab agent first: mac/frame-mac-view/lab.sh serve")
+
+
+def restart_lab(env):
+ """The lab agent again, without rebuilding, with these experiment switches."""
+ subprocess.run(["sh", str(ROOT / "mac" / "frame-mac-view" / "lab.sh"), "serve-only"],
+ env={**os.environ, **env}, check=True, capture_output=True)
+ time.sleep(1)
+
+
+def frame_ssh_for(args):
+ frame_ssh = ["ssh", "-o", "BatchMode=yes"]
+ if args.host:
+ # Another address for the same Frame: its host key must still match.
+ alias = next((line.split()[1] for line in subprocess.run(["ssh", "-G", args.frame], capture_output=True,
+ text=True).stdout.splitlines() if line.startswith("hostname ")), args.frame)
+ frame_ssh += ["-o", f"HostName={args.host}", "-o", f"HostKeyAlias={alias}"]
+ frame_ssh.append(args.frame)
+ return frame_ssh
+
+
+def run_suite(args, frame_ssh, quiet=False):
+ agent_port, token = lab_agent()
+ results = []
+ for sc in args.scenario.split(","):
+ if not quiet:
+ print(f"-- {sc} ...", flush=True)
+ res = run_scenario(args, sc, agent_port, token, frame_ssh)
+ if not quiet:
+ print(f" content p50/p95 {res['content_p50']}/{res['content_p95']} ms, input p50/p95 "
+ f"{res['input_p50']}/{res['input_p95']} ms, {res['fps']} fps (shown {res['fps_shown']}), "
+ f"late {res['late_pct']}%, max gap {res['stall_max']} ms, {res['mbps']} Mbit/s, {res['size']}",
+ flush=True)
+ print(f" stages p50: " + ", ".join(f"{k} {v.get('p50')}" for k, v in res["stages_ms"].items()), flush=True)
+ print(f" grades: {res['grades']} cpu: {res['cpu']}\n viewer: {res['viewer']}", flush=True)
+ for e in res["events"][:12]:
+ print(f" {e['t']:6.2f}s {e['e']}", flush=True)
+ results.append(res)
+ return results
+
+
+def document(args, frame_ssh, results, **extra):
+ build = subprocess.run([*frame_ssh, "grep -m1 BUILD_ID /etc/os-release"], capture_output=True, text=True).stdout
+ # Whether the headset is in standby (it throttles panels even harder then).
+ standby = subprocess.run([*frame_ssh, "grep -h standby ~/.local/share/Steam/logs/vrserver.txt | tail -n 1"],
+ capture_output=True, text=True).stdout.strip()
+ commit = git("rev-parse", "--short", "HEAD") + ("+dirty" if git("status", "--porcelain", "--", "mac", "ui") else "")
+ return {
+ "label": args.label or args.cmd, "date": datetime.datetime.now().isoformat(timespec="seconds"), "commit": commit,
+ "config": {"quality": args.quality, "mode": args.mode, "net": args.net or "none", "delay_ms": args.delay,
+ "buffer_ms": args.buffer, "host": args.host or args.frame, "usb": args.usb, "ssh_opts": args.ssh_opt,
+ "encoder": os.environ.get("FRAME_MAC_VIEW_ENCODER", ""), "duration_s": args.duration,
+ "browser_flags": args.browser_flag if args.browser_flag is not None else frame_macview.BROWSER_FLAGS},
+ "frame_build": build.strip().partition("=")[2], "mac": platform.mac_ver()[0], "headset": standby[-40:],
+ "scenarios": results, **extra,
+ }
+
+
+def write(doc, args):
+ RESULTS.mkdir(parents=True, exist_ok=True)
+ name = f"{doc['date'][:10]}-{doc['commit'].replace('+', '-')}-{re.sub(r'[^a-zA-Z0-9_.-]+', '-', doc['label'])}.json"
+ out = Path(args.out) if args.out else RESULTS / name
+ out.write_text(json.dumps(doc, indent=1))
+ print(f"wrote {out}")
+ return out
+
+
+AB_KEYS = ["content_p50", "content_p95", "input_p50", "input_p95", "fps", "late_pct", "stall_max", "mbps"]
+
+
+def ab(args, frame_ssh):
+ """Arms interleaved (A B, B A, ...) so a Wi-Fi link that changes over
+ minutes affects both alike; medians and ranges per arm."""
+ arms = []
+ for a in args.arm:
+ name, _, envs = a.partition(":")
+ arms.append((name, dict(e.split("=", 1) for e in envs.split(",") if e)))
+ runs = {name: [] for name, _ in arms}
+ for rep in range(args.repeat):
+ for name, env in (arms if rep % 2 == 0 else arms[::-1]):
+ print(f"-- repeat {rep + 1}, arm {name} {env}", flush=True)
+ restart_lab(env)
+ runs[name].append(run_suite(args, frame_ssh, quiet=True))
+ table = {}
+ for sc in args.scenario.split(","):
+ print(f"\n{sc:<14}" + "".join(f"{name:>26}" for name, _ in arms))
+ for k in AB_KEYS:
+ row = []
+ for name, _ in arms:
+ vals = [r[k] for suite in runs[name] for r in suite if r["scenario"] == sc and r.get(k) is not None]
+ table.setdefault(sc, {}).setdefault(k, {})[name] = vals
+ row.append(f"{statistics.median(vals):g} ({min(vals):g}-{max(vals):g})" if vals else "–")
+ print(f"{k:<14}" + "".join(f"{c:>26}" for c in row))
+ restart_lab({}) # back to the defaults
+ return runs, table
+
+
+def main():
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ sub = p.add_subparsers(dest="cmd", required=True)
+ r = sub.add_parser("run")
+ add_run_args(r)
+ r.add_argument("--baseline", default="", help="fail on >10%% regressions against this result")
+ a = sub.add_parser("ab", help="compare lab-agent settings, interleaved: --arm name:ENV=V,ENV2=V ...")
+ add_run_args(a)
+ a.add_argument("--arm", action="append", required=True)
+ a.add_argument("--repeat", type=int, default=3)
+ c = sub.add_parser("compare")
+ c.add_argument("a")
+ c.add_argument("b")
+ args = p.parse_args()
+ # The agent keeps the last 4096 frames (Stats.swift) and results are read
+ # at the end: at 60 fps that's 68 s, less warm-up and the reporting wait.
+ if args.cmd != "compare" and args.host and args.usb:
+ p.error("--host and --usb pick the path two ways; use one")
+ if args.cmd != "compare" and args.warmup + args.duration > 60:
+ p.error("--warmup plus --duration can be at most 60 s (the agent keeps 4096 frames)")
+
+ if args.cmd == "compare":
+ regs = compare(load(args.a), load(args.b))
+ if regs:
+ print("\nworse:\n " + "\n ".join(regs))
+ sys.exit(1 if regs else 0)
+
+ lab_agent()
+ frame_ssh = frame_ssh_for(args)
+ # Keep the Mac's screen awake: virtual displays aren't removed while it sleeps.
+ runs = 1 if args.cmd == "run" else args.repeat * len(args.arm)
+ awake = subprocess.Popen(["caffeinate", "-d", "-u", "-t", str(int(runs * (args.duration * 4 + 60) + 120))])
+ try:
+ if args.cmd == "ab":
+ arm_runs, table = ab(args, frame_ssh)
+ write(document(args, frame_ssh, [], arms=args.arm, runs=arm_runs, table=table), args)
+ return
+ results = run_suite(args, frame_ssh)
+ finally:
+ awake.terminate()
+ doc = document(args, frame_ssh, results)
+ write(doc, args)
+ bad = [f"{r['scenario']} {k}" for r in results for k, v in r["grades"].items() if v in ("bad", "missing")]
+ regs = compare(load(args.baseline), doc) if args.baseline else []
+ if bad:
+ print("missed targets: " + ", ".join(bad))
+ if regs:
+ print("worse than baseline:\n " + "\n ".join(regs))
+ sys.exit(1 if bad or regs else 0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/test_frame_apk_vr.py b/tests/test_frame_apk_vr.py
new file mode 100644
index 0000000..49a56ca
--- /dev/null
+++ b/tests/test_frame_apk_vr.py
@@ -0,0 +1,297 @@
+"""Local-only VR manifest, raw ZIP and independent signature checks."""
+import io
+import os
+from pathlib import Path
+import struct
+import subprocess
+import sys
+import tempfile
+import unittest
+from unittest.mock import patch
+import zipfile
+
+sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'ui'))
+import frame_apk
+import frame_apk_vr as vr
+import frame_apk_sign as signing
+import frame_android
+from test_frame_apk import pool
+
+
+DEFAULT = 'android.intent.category.DEFAULT'
+
+
+def manifest(utf8=False, launcher=False, category=None, samsung=False, split=False, alias=False, splash=False):
+ strings = ['manifest', 'package', 'org.test.vr', 'application', 'activity', 'intent-filter',
+ 'action', 'category', 'name', vr.MAIN, category or next(iter(sorted(vr.VR))),
+ vr.LAUNCHER, 'http://schemas.android.com/apk/res/android', 'meta-data', 'value',
+ 'com.samsung.android.vr.application.mode', 'vr_only', 'activity-alias', DEFAULT, next(iter(sorted(vr.VR))), 'targetActivity', '.Splash', '.Game']
+ def start(tag, attrs=()):
+ body = struct.pack('H", self._read(2))[0]
+ elif n == 127:
+ n = struct.unpack(">Q", self._read(8))[0]
+ return b0 & 0x0F, self._read(n)
+
+ def send_text(self, text):
+ data, mask = text.encode(), os.urandom(4)
+ head = bytes([0x81, 0x80 | len(data)]) if len(data) < 126 else bytes([0x81, 0xFE]) + struct.pack(">H", len(data))
+ self.s.sendall(head + mask + bytes(c ^ mask[i % 4] for i, c in enumerate(data)))
+
+ def close(self):
+ self.s.close()
+
+
+@unittest.skipUnless(sys.platform == "darwin" and shutil.which("xcrun"), "the agent is macOS-only")
+class Agent(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.tmp = tempfile.mkdtemp()
+ cls.bin = Path(cls.tmp) / "frame-mac-view"
+ r = subprocess.run(["/bin/sh", str(ROOT / "mac" / "frame-mac-view" / "build.sh"), str(cls.bin)],
+ capture_output=True, text=True, timeout=600)
+ if r.returncode: # a real failure on a Mac with Xcode: don't hide it as a skip
+ raise AssertionError("agent didn't build:\n" + (r.stderr or r.stdout)[-2000:])
+ cls.token = "t0ken-" + os.urandom(6).hex()
+ cls.proc = subprocess.Popen([str(cls.bin), "serve", "--port", "0", "--page", str(ROOT / "ui" / "mac-view.html"),
+ "--exit-on-eof"], env={**os.environ, "FRAME_MAC_VIEW_TOKEN": cls.token},
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
+ line = cls.proc.stdout.readline()
+ cls.port = int(line.rsplit(":", 1)[1])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.proc.stdin.close() # --exit-on-eof
+ cls.proc.stdout.close()
+ try:
+ cls.proc.wait(5)
+ except subprocess.TimeoutExpired:
+ cls.proc.kill()
+ shutil.rmtree(cls.tmp, ignore_errors=True)
+
+ def get(self, path, method="GET"):
+ c = http.client.HTTPConnection("127.0.0.1", self.port, timeout=10)
+ c.request(method, path)
+ r = c.getresponse()
+ return r.status, r.read()
+
+ def test_token_is_required(self):
+ self.assertEqual(self.get("/status")[0], 403)
+ self.assertEqual(self.get("/status?k=wrong")[0], 403)
+ status, body = self.get(f"/status?k={self.token}")
+ self.assertEqual(status, 200)
+ self.assertIn("screen", json.loads(body))
+
+ def test_serves_the_viewer_page(self):
+ status, body = self.get(f"/view?k={self.token}&src=test")
+ self.assertEqual(status, 200)
+ self.assertIn(b"VideoDecoder", body)
+
+ def test_snapshot_needs_the_key(self):
+ self.assertEqual(self.get("/snapshot?display=1")[0], 403)
+
+ def test_separate_without_accessibility_explains(self):
+ if json.loads(self.get(f"/status?k={self.token}")[1])["accessibility"]:
+ self.skipTest("this Mac allows Accessibility here")
+ ws = WS(self.port, f"/stream?k={self.token}&src=separate:1")
+ self.assertEqual(ws.status, 101)
+ for _ in range(5):
+ op, data = ws.recv()
+ msg = json.loads(data) if op == 1 else {}
+ if msg.get("t") in ("error", "closed"):
+ break
+ self.assertIn("Accessibility", msg.get("message", msg.get("reason", "")))
+ ws.close()
+
+ def test_lists_displays(self):
+ status, body = self.get(f"/displays?k={self.token}")
+ self.assertEqual(status, 200)
+ self.assertIsInstance(json.loads(body)["displays"], list)
+
+ def ticket(self, src):
+ status, body = self.get(f"/ticket?k={self.token}&src={src}", method="POST")
+ self.assertEqual(status, 200)
+ return json.loads(body)["ticket"]
+
+ def test_ping_and_page_are_open_but_streams_are_not(self):
+ self.assertEqual(self.get("/ping"), (200, b"frame-mac-view"))
+ self.assertEqual(WS(self.port, "/stream?src=test").status, 403)
+ self.assertEqual(self.get("/ticket?src=test", method="POST")[0], 403)
+
+ def test_tickets_are_single_use_and_tied_to_one_source(self):
+ t = self.ticket("test")
+ self.assertEqual(WS(self.port, f"/stream?src=display:1&t={t}").status, 403) # wrong source
+ ws = WS(self.port, f"/stream?src=test&t={t}")
+ self.assertEqual(ws.status, 101)
+ hello = json.loads(ws.recv()[1])
+ self.assertEqual(hello["t"], "hello")
+ # Until the viewer acknowledges, a retry (the hello got lost) gets the
+ # same key, and replaces the first viewer rather than adding one.
+ retry = WS(self.port, f"/stream?src=test&t={t}")
+ self.assertEqual(retry.status, 101)
+ self.assertEqual(json.loads(retry.recv()[1])["r"], hello["r"])
+ time.sleep(0.3)
+ _, body = self.get(f"/status?k={self.token}")
+ self.assertEqual(len(json.loads(body)["streams"]), 1)
+ ws.close()
+ ws = retry
+ ws.send_text(json.dumps({"t": "ack"}))
+ time.sleep(0.3)
+ self.assertEqual(WS(self.port, f"/stream?src=test&t={t}").status, 403) # spent
+ again = WS(self.port, f"/stream?src=test&r={hello['r']}") # the viewer reconnecting
+ self.assertEqual(again.status, 101)
+ again.close()
+ ws.close()
+ # Stop revokes the reconnect key.
+ self.get(f"/close?k={self.token}&src=test", method="POST")
+ self.assertEqual(WS(self.port, f"/stream?src=test&r={hello['r']}").status, 403)
+
+ def test_stop_revokes_tickets_not_yet_used(self):
+ t = self.ticket("test")
+ self.get(f"/close?k={self.token}&src=test", method="POST")
+ self.assertEqual(WS(self.port, f"/stream?src=test&t={t}").status, 403)
+
+ def test_bad_frame_lengths_close_the_socket_not_the_agent(self):
+ ws = WS(self.port, f"/stream?src=test&t={self.ticket('test')}")
+ self.assertEqual(ws.status, 101)
+ # A masked frame claiming 2^63 bytes.
+ ws.s.sendall(bytes([0x81, 0xFF]) + struct.pack(">Q", 1 << 63) + os.urandom(4))
+ with self.assertRaises((EOFError, OSError)):
+ for _ in range(1000):
+ ws.recv()
+ ws.close()
+ self.assertEqual(self.get(f"/status?k={self.token}")[0], 200)
+
+ def test_stream_input_and_close(self):
+ ws = WS(self.port, f"/stream?k={self.token}&src=test&codec=h264&fps=30&max=640")
+ self.assertEqual(ws.status, 101)
+ info = None
+ key = None
+ for _ in range(200):
+ op, data = ws.recv()
+ if op == 1:
+ msg = json.loads(data)
+ if msg["t"] == "hello":
+ continue
+ if msg["t"] == "error":
+ self.skipTest("no H.264 encoder here: " + msg["message"])
+ if msg["t"] == "info":
+ info = msg
+ elif op == 2 and data[0] == 1:
+ key = data
+ if info and key:
+ break
+ self.assertEqual(info["src"], "test")
+ self.assertTrue(info["input"])
+ # A keyframe: flags, 8-byte timestamp, sequence number, input echoed,
+ # then Annex B with the SPS first.
+ self.assertEqual(key[17:21], b"\x00\x00\x00\x01")
+ self.assertEqual(key[21] & 0x1F, 7) # NAL type 7, SPS
+ ws.send_text(json.dumps({"t": "m", "e": "down", "b": 0, "x": 0.5, "y": 0.5}))
+ ws.send_text(json.dumps({"t": "key-frame"}))
+ got_key = False
+ for _ in range(200):
+ op, data = ws.recv()
+ if op == 2 and data[0] == 1:
+ got_key = True
+ break
+ self.assertTrue(got_key, "no keyframe after asking for one")
+ _, body = self.get(f"/status?k={self.token}")
+ self.assertEqual([s["src"] for s in json.loads(body)["streams"]], ["test"])
+ status, body = self.get(f"/close?k={self.token}", method="POST")
+ self.assertEqual(json.loads(body)["closed"], 1)
+ for _ in range(400):
+ op, data = ws.recv()
+ if op == 1 and json.loads(data)["t"] == "close":
+ break
+ self.assertEqual(json.loads(data)["t"], "close")
+ # Without the viewer doing anything, the agent ends the stream itself.
+ time.sleep(1)
+ _, body = self.get(f"/status?k={self.token}")
+ self.assertEqual(json.loads(body)["streams"], [])
+ ws.close()
+
+ def test_timing_reports_and_input_echo(self):
+ # What a viewer does: sync clocks, report each frame, stamp input.
+ ws = WS(self.port, f"/stream?k={self.token}&src=test&codec=h264&fps=30&max=640")
+ self.assertEqual(ws.status, 101)
+ ws.send_text(json.dumps({"t": "w"})) # keep-warm filler: ignored
+ ws.send_text(json.dumps({"t": "ping", "c": 1.5}))
+ pong, echoed, seqs, info = None, None, [], None
+ clicked = False
+ deadline = time.time() + 10
+ while time.time() < deadline and not (pong and echoed):
+ op, data = ws.recv()
+ if op == 1:
+ msg = json.loads(data)
+ if msg["t"] == "error":
+ self.skipTest("no H.264 encoder here: " + msg["message"])
+ if msg["t"] == "pong":
+ pong = msg
+ if msg["t"] == "info":
+ info = msg
+ elif op == 2:
+ seq, echo = struct.unpack(">II", data[9:17])
+ seqs.append(seq)
+ now = pong["a"] if pong else 0
+ ws.send_text(json.dumps({"t": "rx", "s": seq, "r": now}))
+ ws.send_text(json.dumps({"t": "fd", "f": [[seq, now + 1, now + 2, now + 3]], "drop": 0}))
+ if echo == 7:
+ echoed = seq
+ if len(seqs) == 3 and not clicked:
+ ws.send_text(json.dumps({"t": "m", "e": "down", "b": 0, "x": 0.5, "y": 0.5, "i": 7, "tv": now}))
+ clicked = True
+ self.assertEqual(pong["c"], 1.5)
+ self.assertGreater(pong["a"], 0)
+ self.assertEqual(seqs[:3], sorted(seqs[:3]))
+ self.assertIsNotNone(echoed, "no frame was tagged as the first reply to the click")
+ time.sleep(1.7) # frames are reported once the viewer has had time
+ stream = json.loads(self.get(f"/stats?k={self.token}")[1])["streams"][0]
+ first = stream["frames"][0]
+ self.assertGreater(first["e1"], first["e0"])
+ self.assertGreaterEqual(first["e0"], first["cap"])
+ self.assertEqual(first["vs"] - first["rx"], 3)
+ self.assertEqual([i["frame"] for i in stream["inputs"] if i["id"] == 7], [echoed])
+ self.assertIn("total", stream["summary"])
+ self.assertEqual(info["warm"], 0) # off unless FRAME_MAC_VIEW_WARM is set
+ live = json.loads(self.get(f"/status?k={self.token}")[1])["streams"][0]
+ self.assertEqual(live["controller"]["tier"], 0)
+ self.assertIn("fps", live["stats"])
+ ws.close()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/ui/apk_sources/__init__.py b/ui/apk_sources/__init__.py
new file mode 100644
index 0000000..88df1f4
--- /dev/null
+++ b/ui/apk_sources/__init__.py
@@ -0,0 +1,45 @@
+"""APK sources: every place Frame Control can find and download APKs.
+
+One module per source kind in this package. Each module exposes the same small
+interface so ``search.py`` can query them all and show where every result came
+from. Python stdlib only; must run on Python 3.9.
+
+Module interface
+----------------
+KIND = 'sidequest' # stable id of the source kind
+def sources() -> list[dict] # configured sources of this kind (a kind can have
+ # several, e.g. one per F-Droid-format repo)
+def search(source, query, limit=50) -> list[dict] # Entry dicts, best first
+def details(source, entry_id) -> dict # Entry with 'versions'
+def download(source, entry_id, version_code=None) -> dict
+ # {'apk': local path, 'obb': [paths], 'sha256': hex or None, 'verified': bool}
+ # Raise SourceError with a user-readable message on failure.
+
+Source dict
+-----------
+{'id': 'sidequest', 'kind': KIND, 'name': 'SideQuest', 'url': 'https://...',
+ 'builtin': True, 'enabled': True, 'trust': 'official' | 'community' | 'user'}
+
+Entry dict (missing facts are None, never guessed)
+----------
+{'source': source id, 'id': source-local id, 'package': 'org.example.app' or None,
+ 'name': str, 'summary': str, 'icon': url or None, 'page': url or None,
+ 'version': '1.2', 'version_code': 12, 'min_sdk': 24, 'abis': ['arm64-v8a'],
+ 'vr': True/False/None, 'size': bytes, 'free': True, 'license': 'GPL-3.0' or None,
+ 'updated': 'YYYY-MM-DD', 'downloadable': bool, # False = open page only
+ 'versions': [ {version, version_code, min_sdk, size, updated}, ... ]} # details() only
+
+Rules
+-----
+- Only sources that distribute APKs with the developer's consent: free listings,
+ never paid apps re-hosted, no licence or entitlement workarounds.
+- Honour each site's terms and robots rules; if automated download isn't allowed,
+ return entries with 'downloadable': False and a 'page' link instead.
+- Cache indexes under frame_host.cache_dir('apk-sources'); send a clear
+ User-Agent ('FrameControl/'); keep requests modest.
+- Tests use recorded fixtures, never the network.
+"""
+
+
+class SourceError(Exception):
+ """User-readable failure from a source (network, format, verification)."""
diff --git a/ui/frame_android.py b/ui/frame_android.py
index 1d96b5c..d3a3a4e 100644
--- a/ui/frame_android.py
+++ b/ui/frame_android.py
@@ -6,12 +6,18 @@ apps, the lepton-show-flatscreen marker; plus a non-Steam shortcut, so it shows
in the Steam library and gets its own SteamVR panel. Nothing goes through
Lepton Development, which wipes its apps on exit. See docs/apks.md.
-Python stdlib only. CLI: python3 ui/frame_android.py {info APK|versions APK-or-PKG|install APK|list|launch PKG|stop PKG|remove PKG|probe PKG}
+Python stdlib only. CLI: python3 ui/frame_android.py
+ install APK [--vr|--flat] [--no-xr-compat] | info APK | versions APK-or-PKG
+ patch SRC DST [--add NAME=PATH ...] | list | launch PKG | stop PKG | remove PKG | probe PKG
"""
-import json, os, re, shlex, shutil, subprocess, sys, threading, time, zlib
+import json, os, re, shlex, shutil, struct, subprocess, sys, threading, time, zlib
import frame_apk
import frame_host
+import tempfile
+import zipfile
+from frame_apk_vr import add_launcher_category
+from frame_apk_sign import repack
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FRAME = os.environ.get('FRAME_ALIAS', 'frame')
@@ -20,6 +26,13 @@ COMPAT = '.local/share/Steam/steamapps/compatdata'
SHADERS = '.local/share/Steam/steamapps/shadercache'
LAUNCHER = os.path.join(ROOT, 'frame', 'android', 'lepton-app.sh')
SHORTCUTS = os.path.join(ROOT, 'frame', 'android', 'steam_shortcuts.py')
+# OpenXR API layer that lets OpenXR 1.1 apps run on SteamVR's 1.0-only Android
+# runtime (frame/openxr-compat, docs/vr-apks.md). Injected into VR APKs.
+XR_COMPAT = os.path.join(ROOT, 'frame', 'openxr-compat')
+XR_COMPAT_FILES = {
+ 'assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json': 'XrApiLayer_FRAME_compat.json',
+ 'lib/arm64-v8a/libXrApiLayer_FRAME_compat.so': 'prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so',
+}
PKG_RE = re.compile(r'^[A-Za-z][\w]*(\.[A-Za-z_][\w]*)+$')
SSH_OPTS = ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=8']
@@ -65,6 +78,22 @@ def apk_info(path):
raise FrameError(f'{os.path.basename(path)}: {e}')
+def xr_compat_files(apk_path):
+ """The layer's files to add, or {} if the APK has no OpenXR loader or already has the layer."""
+ with zipfile.ZipFile(apk_path) as z:
+ names = set(z.namelist())
+ if 'lib/arm64-v8a/libopenxr_loader.so' not in names or names & set(XR_COMPAT_FILES):
+ return {}
+ add = {}
+ for entry, rel in XR_COMPAT_FILES.items():
+ try:
+ with open(os.path.join(XR_COMPAT, rel), 'rb') as f:
+ add[entry] = f.read()
+ except OSError:
+ raise FrameError("the OpenXR compatibility layer isn't built; run frame/openxr-compat/build.sh")
+ return add
+
+
def check_installable(info):
if info['min_sdk'] and info['min_sdk'] > 30:
raise FrameError(f"{info['label']} needs Android API {info['min_sdk']}; Lepton is Android 11 (API 30)")
@@ -111,7 +140,7 @@ def _write_meta(d, meta):
install_hooks = []
-def install(apk_path, flatscreen=True, name=None, source=None, icon_png=None):
+def install(apk_path, flatscreen=None, name=None, source=None, icon_png=None, xr_compat=None):
start, info = time.time(), None
try:
info = apk_info(apk_path)
@@ -121,8 +150,20 @@ def install(apk_path, flatscreen=True, name=None, source=None, icon_png=None):
pkg = info['package']
if not PKG_RE.match(pkg):
raise FrameError(f'unexpected package name {pkg!r}')
+ if flatscreen is None:
+ flatscreen = not info['vr']
+ # VR apps get the OpenXR compatibility layer unless told otherwise; it only
+ # changes calls SteamVR would otherwise reject.
+ add = xr_compat_files(apk_path) if (info['vr'] if xr_compat is None else xr_compat) else {}
with _install_lock:
- meta = _install(apk_path, info, pkg, flatscreen, name, source)
+ if add or info['repairable']:
+ with tempfile.TemporaryDirectory(prefix='frame-vr-') as tmp:
+ patched = os.path.join(tmp, 'app.apk')
+ info['patched'] = patch(apk_path, patched, add)['patched']
+ info['launchable'] = True
+ meta = _install(patched, info, pkg, flatscreen, name, source or os.path.basename(apk_path))
+ else:
+ meta = _install(apk_path, info, pkg, flatscreen, name, source)
except FrameError as e:
_after_install(info, None, e, start)
raise
@@ -163,6 +204,8 @@ def _install(apk_path, info, pkg, flatscreen, name, source):
raise FrameError(f'Steam did not return a shortcut id (got {reply[:80]!r})')
meta = {'package': pkg, 'label': name or info['label'], 'version': info['version'],
'instance': iid, 'shortcut': shortcut, 'game_id': game_id(shortcut),
+ 'vr': info.get('vr', False), 'vr_issues': info.get('vr_issues', []),
+ 'launchable': info.get('launchable', False), 'patched': info.get('patched', []),
'flatscreen': flatscreen, 'installed': time.strftime('%Y-%m-%dT%H:%M:%S'),
'source': source or os.path.basename(apk_path)}
_write_meta(d, meta)
@@ -293,19 +336,59 @@ def probe(pkg, wait=20):
'container_up': ctr in running_instances()}
+def patch(src, dst, add=None):
+ try:
+ info = apk_info(src)
+ with zipfile.ZipFile(src) as z:
+ original = frame_apk._read(z, 'AndroidManifest.xml', frame_apk.MAX_MANIFEST)
+ manifest = add_launcher_category(original) if info['repairable'] else original
+ if not info['launchable'] and not info['repairable']:
+ raise FrameError('APK has no MAIN/LAUNCHER activity that Frame Control can patch')
+ repack(src, dst, replace={'AndroidManifest.xml': manifest}, add=add)
+ result = apk_info(dst)
+ result.pop('icon_png', None)
+ result['patched'] = (['launcher'] if manifest != original else []) + \
+ (['openxr-compat'] if add and set(XR_COMPAT_FILES) <= set(add) else [])
+ return result
+ except (OSError, ValueError, IndexError, struct.error, zipfile.BadZipFile, frame_apk.ApkError) as e:
+ raise FrameError(str(e)) from e
+
+
def main():
cmd, *args = sys.argv[1:] or ['help']
try:
if cmd in ('info', 'versions'):
import frame_apk_versions
if cmd == 'info':
- print(frame_apk_versions.describe(apk_info(args[0])))
+ info = apk_info(args[0])
+ print(frame_apk_versions.describe(info))
+ fix = '; Frame Control adds the LAUNCHER entry Lepton needs' if info.get('repairable') else ''
+ if info.get('vr') or fix:
+ print(('VR app' if info.get('vr') else 'Android app') + fix)
+ for note in info.get('vr_issues', []):
+ print(note)
return
info = apk_info(args[0]) if os.path.isfile(args[0]) or args[0].lower().endswith('.apk') else None
r = frame_apk_versions.alternatives(
info['package'] if info else args[0], info.get('version_code') if info else None)
elif cmd == 'install':
- r = install(args[0], flatscreen='--vr' not in args)
+ r = install(args[0], flatscreen=False if '--vr' in args else True if '--flat' in args else None,
+ xr_compat=False if '--no-xr-compat' in args else None)
+ elif cmd == 'patch':
+ import argparse
+ parser = argparse.ArgumentParser(description='Patch and v2-sign an APK locally')
+ parser.add_argument('src')
+ parser.add_argument('dst')
+ parser.add_argument('--add', action='append', default=[], metavar='NAME=PATH')
+ opts = parser.parse_args(args)
+ additions = {}
+ for item in opts.add:
+ if '=' not in item:
+ raise FrameError('--add requires NAME=PATH')
+ entry, path = item.split('=', 1)
+ with open(path, 'rb') as f:
+ additions[entry] = f.read()
+ r = patch(opts.src, opts.dst, additions)
elif cmd == 'list':
r = list_apps()
elif cmd in ('launch', 'stop', 'probe'):
@@ -314,7 +397,7 @@ def main():
r = remove(args[0], keep_data='--keep-data' in args)
else:
sys.exit(__doc__)
- except FrameError as e:
+ except (FrameError, OSError) as e:
sys.exit(f'error: {e}')
print(json.dumps(r, indent=1))
diff --git a/ui/frame_apk.py b/ui/frame_apk.py
index d96f95b..7b7a9ff 100644
--- a/ui/frame_apk.py
+++ b/ui/frame_apk.py
@@ -10,7 +10,8 @@ import zipfile
# android: attribute resource ids; names can be stripped by shrinkers, ids can't.
ATTR = {0x01010001: 'label', 0x01010002: 'icon', 0x01010003: 'name',
- 0x0101021b: 'versionCode', 0x0101021c: 'versionName', 0x0101020c: 'minSdkVersion'}
+ 0x01010024: 'value', 0x0101021b: 'versionCode', 0x0101021c: 'versionName', 0x0101020c: 'minSdkVersion',
+ 0x01010202: 'targetActivity'}
T_REF, T_STRING, T_INT_DEC, T_INT_HEX = 0x01, 0x03, 0x10, 0x11
# APKs can come from websites (install links), so nothing read from one may be
# unbounded. zipfile stops at a member's declared size, so checking it is enough.
@@ -215,8 +216,11 @@ def apk_info(path):
if 'AndroidManifest.xml' not in names:
raise ApkError('not an APK: no AndroidManifest.xml')
try:
- elements = manifest_elements(_read(z, 'AndroidManifest.xml', MAX_MANIFEST))
+ manifest_data = _read(z, 'AndroidManifest.xml', MAX_MANIFEST)
+ elements = manifest_elements(manifest_data)
res = Resources(_read(z, 'resources.arsc', MAX_ARSC) if 'resources.arsc' in names else b'')
+ from frame_apk_vr import detection
+ vr_info = detection(manifest_data, names)
except (struct.error, IndexError, zipfile.BadZipFile) as e:
raise ApkError(f'could not read the APK manifest: {e}')
tags = {}
@@ -236,6 +240,7 @@ def apk_info(path):
'min_sdk': min_sdk[1] if min_sdk and min_sdk[0] in (T_INT_DEC, T_INT_HEX) else None,
'icon_png': None,
}
+ info.update(vr_info)
try:
info['icon_png'] = _icon_png(z, names, _icons(app.get('icon'), res))
except Exception: # noqa: BLE001 - any unreadable icon just means no icon
diff --git a/ui/frame_apk_sign.py b/ui/frame_apk_sign.py
new file mode 100644
index 0000000..68580c5
--- /dev/null
+++ b/ui/frame_apk_sign.py
@@ -0,0 +1,361 @@
+"""Lossless ZIP repacking and APK v2 RSA/SHA-256 signing, Python 3.9 stdlib.
+
+Spec: https://source.android.com/docs/security/features/apksigning/v2
+Sections: APK Signing Block; APK Signature Scheme v2 Block; Integrity-protected
+contents; Verification. No verity algorithm is used, so no verity padding.
+"""
+import hashlib
+import io
+import json
+import math
+import os
+from pathlib import Path
+import re
+import secrets
+import struct
+import tempfile
+import zipfile
+import zlib
+
+import frame_host
+
+MAGIC = b'APK Sig Block 42'
+V2 = 0x7109871a
+ALG = 0x0103
+SHA256_DER = bytes.fromhex('3031300d060960864801650304020105000420')
+
+
+def u32(n):
+ return struct.pack('= n:
+ raise ValueError('invalid RSA signature size/value')
+ actual = pow(int.from_bytes(sig, 'big'), e, n).to_bytes(size, 'big')
+ if actual != encoded_hash(data, size):
+ raise ValueError('RSA signature mismatch')
+
+
+def certificate(key):
+ name = sequence(der(0x31, sequence(bytes.fromhex('0603550403'), der(12, b'Frame Control APK signer'))))
+ validity = sequence(der(0x17, b'200101000000Z'), der(0x18, b'21200101000000Z'))
+ tbs = sequence(der(0xa0, integer(2)), integer(1), CERT_ALG, name, validity, name, public_key(key))
+ return sequence(tbs, CERT_ALG, der(3, b'\0' + rsa_sign(tbs, key)))
+
+
+def _prime(bits):
+ small = (3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)
+ while True:
+ n = secrets.randbits(bits) | (3 << (bits - 2)) | 1
+ if any(n % p == 0 for p in small) or (n - 1) % 65537 == 0:
+ continue
+ d, s = n - 1, 0
+ while d % 2 == 0:
+ d //= 2
+ s += 1
+ for _ in range(40): # Miller-Rabin error bound <= 2^-80
+ x = pow(secrets.randbelow(n - 3) + 2, d, n)
+ if x in (1, n - 1):
+ continue
+ for _ in range(s - 1):
+ x = pow(x, 2, n)
+ if x == n - 1:
+ break
+ else:
+ break
+ else:
+ return n
+
+
+def signing_key(path=None):
+ """Persistent identity in app data, never an evictable cache. Atomic publication.
+
+ Hard-linking a fully written private temp file prevents concurrent first-use
+ callers from selecting different identities or reading a partial key.
+ """
+ path = Path(path) if path is not None else frame_host.data_dir('apk-signing-key.json')
+ if not path.exists():
+ p, q = _prime(1024), _prime(1024)
+ while q == p:
+ q = _prime(1024)
+ key = {'n': p * q, 'e': 65537, 'd': pow(65537, -1, math.lcm(p - 1, q - 1))}
+ path.parent.mkdir(parents=True, exist_ok=True)
+ fd, tmp = tempfile.mkstemp(prefix='.apk-key-', dir=str(path.parent))
+ try:
+ with os.fdopen(fd, 'w') as f:
+ json.dump(key, f)
+ f.flush()
+ os.fsync(f.fileno())
+ try:
+ os.link(tmp, path) # never replaces a key another process wrote first
+ except FileExistsError:
+ pass
+ except OSError: # no hard links (FAT/exFAT): plain rename
+ if not path.exists():
+ os.replace(tmp, path)
+ finally:
+ if os.path.exists(tmp):
+ os.unlink(tmp)
+ os.chmod(path, 0o600) # Windows ignores this; the per-user app-data folder is the protection there
+ key = json.loads(path.read_text())
+ if key['n'].bit_length() != 2048 or key['e'] != 65537:
+ raise ValueError(f'invalid cached APK signing key; delete {path} to make a new one '
+ '(re-signed apps then need reinstalling)')
+ rsa_verify(b'key check', rsa_sign(b'key check', key), key['n'], key['e'])
+ return key
+
+
+def _eocd(data):
+ # ZIP comments can contain the EOCD signature; accept only an exact EOF fit.
+ for at in range(len(data) - 22, max(-1, len(data) - 65558), -1):
+ if data[at:at + 4] == b'PK\5\6' and at + 22 + struct.unpack_from(' len(data):
+ raise ValueError('truncated length prefix')
+ size = struct.unpack_from(' len(data):
+ raise ValueError('length prefix outside block')
+ result.append(data[off:off + size])
+ off += size
+ return result
+
+
+def _der_parts(data):
+ result, off = [], 0
+ while off < len(data):
+ start = off
+ tag, size = data[off:off + 2]
+ off += 2
+ if size & 128:
+ count = size & 127
+ if not count or count > 4:
+ raise ValueError('invalid DER length')
+ size = int.from_bytes(data[off:off + count], 'big')
+ off += count
+ if off + size > len(data):
+ raise ValueError('truncated DER')
+ result.append((tag, data[off:off + size], data[start:off + size]))
+ off += size
+ return result
+
+
+def _cert_key(cert):
+ outer = _der_parts(cert)
+ if len(outer) != 1 or outer[0][0] != 0x30:
+ raise ValueError('invalid certificate')
+ fields = _der_parts(outer[0][1])
+ tbs = _der_parts(fields[0][1])
+ spki = tbs[6 if tbs[0][0] == 0xa0 else 5][2]
+ pub = _der_parts(_der_parts(spki)[0][1])
+ if pub[0][2] != RSA_ALG or pub[1][1][:1] != b'\0':
+ raise ValueError('certificate is not RSA')
+ numbers = _der_parts(_der_parts(pub[1][1][1:])[0][1])
+ n, e = [int.from_bytes(item[1], 'big') for item in numbers]
+ return n, e, spki
+
+
+def verify(path):
+ """Verify this v2-only format; return True or raise ValueError on corruption.
+
+ A valid signature establishes integrity, not trust in the APK publisher.
+ """
+ data = Path(path).read_bytes()
+ try:
+ eo, cd = _eocd(data)
+ if data[cd - 16:cd] != MAGIC:
+ raise ValueError('no APK signing block')
+ size = struct.unpack_from(' cd - 24:
+ raise ValueError('invalid signing pair')
+ ident = struct.unpack_from('= 0xffffffff:
+ raise ValueError('ZIP64 APKs are unsupported')
+ output.write(struct.pack('= 65535 or cd + len(directory) >= 0xffffffff:
+ raise ValueError('ZIP64 APKs are unsupported')
+ output.write(directory)
+ output.write(struct.pack('; Godot 4 puts LAUNCHER only there.
+ real = [f for f in mains if not f['alias']]
+ targets = [f for f in real if VR & f['categories']]
+ if not targets and any(LAUNCHER in f['categories'] or VR & f['categories'] for f in mains if f['alias']):
+ aimed = {f['activity'] for f in mains if f['alias'] and (LAUNCHER in f['categories'] or VR & f['categories'])}
+ targets = [f for f in real if f['templates']] # the patch copies an existing
+ targets = [f for f in targets if f['activity'] in aimed] or targets
+ launchable = any(LAUNCHER in f['categories'] for f in real)
+ return {'launchable': launchable, 'repairable': not launchable and bool(targets),
+ 'vr_activity': bool(vr_filters), 'vr': bool(vr_filters) or samsung}, targets
+
+
+def _append_string(chunk, text):
+ _, hs, size, count, styles, flags, start, style_start = struct.unpack_from('/dev/null 2>&1; then
+ echo "The headset isn't showing anything (gamescope's display :0 isn't up). Wake it and try again." >&2
+ exit 2
+fi
+common=(--ozone-platform=x11 --force-device-scale-factor=1 --no-first-run --no-default-browser-check
+ --password-store=basic --disable-session-crashed-bubble --noerrdialogs --disable-infobars
+ --disable-features=Translate,MediaRouter --autoplay-policy=no-user-gesture-required
+ "--window-size=$w,$h" "$@" "--app=$url")
+if [ -x "$HOME/chromium-xr/chrome" ]; then
+ cmd=("$HOME/chromium-xr/chrome" "--user-data-dir=$HOME/.local/share/frame-control/mac-view" "${common[@]}")
+elif flatpak info org.chromium.Chromium >/dev/null 2>&1; then
+ cmd=(flatpak run org.chromium.Chromium
+ "--user-data-dir=$HOME/.var/app/org.chromium.Chromium/data/frame-mac-view" "${common[@]}")
+else
+ echo "NO_BROWSER"
+ exit 3
+fi
+log=/tmp/frame-mac-view.log
+setsid nohup "${cmd[@]}" >>"$log" 2>&1 /dev/null | awk '/^ +0x/ {print $1}'); do
+ xprop -id "$win" _NET_WM_NAME WM_NAME 2>/dev/null | grep -qF "[$tag]" || continue
+ if xprop -id "$win" -f STEAM_GAME 32c -set STEAM_GAME "$appid" 2>/dev/null; then
+ echo "panel valve.steam.desktopgame.$appid window $win"
+ exit 0
+ fi
+ done
+done
+echo "The viewer started, but its window didn't appear within 30 s. Chromium's log:" >&2
+tail -n 15 "$log" >&2
+exit 1
+"""
+
+
+class MacViewError(Exception):
+ pass
+
+
+def panel_id(src):
+ """A stable panel id per source, in the range panel-on-frame.sh uses."""
+ return 2_001_000_000 + zlib.crc32(f"mac:{src}".encode()) % 1_000_000
+
+
+def fit(w, h, box=PANEL_BOX):
+ s = min(box[0] / max(w, 1), box[1] / max(h, 1))
+ return max(320, round(w * s)), max(200, round(h * s))
+
+
+class MacView:
+ def __init__(self, tunnel_ssh, run, frame, track=None):
+ self.tunnel_ssh = list(tunnel_ssh)
+ self.run = run
+ self.frame = frame
+ self.track = track or (lambda proc: None) # the server ends these on exit
+ self.lock = threading.Lock()
+ self.token = secrets.token_urlsafe(24)
+ self.agent = None
+ self.port = None
+ self.tunnel = None
+ self.remote_port = None
+ self.supervisor = None
+ self.closing = False
+ self.shows = 0 # counts Show presses, so a late cleanup can't close a new viewer
+ self.launching = 0 # Shows in progress (one may be replacing its own stream)
+ # Held by a Show while it counts itself in, and by the cleanup for its
+ # check and its pkill together, so a Show can't start in between.
+ self.viewer_lock = threading.Lock()
+ # Use the Frame's USB-C network when it's plugged into this Mac (see
+ # _usb_route); FRAME_MACVIEW_USB=0 turns that off.
+ self.prefer_usb = os.environ.get("FRAME_MACVIEW_USB") != "0"
+ self.route = "network"
+ self.shown = set() # sources with a viewer out there, connected or retrying
+ self.browser_flags = list(BROWSER_FLAGS)
+
+ # ---- the agent on this Mac ----
+
+ def unavailable(self):
+ """Why this can't work here, or None."""
+ if not SUPPORTED:
+ return "Streaming your computer into the headset needs macOS."
+ if not AGENT.exists() and not (SOURCES.exists() and shutil.which("xcrun")):
+ return "The Mac streaming helper is missing from this copy of Frame Control."
+ return None
+
+ def build(self):
+ if AGENT.exists() and not self._stale():
+ return
+ if not (SOURCES / "build.sh").exists():
+ if AGENT.exists():
+ return
+ raise MacViewError("The Mac streaming helper is missing.")
+ r = subprocess.run(["/bin/sh", str(SOURCES / "build.sh"), str(AGENT)], capture_output=True, text=True,
+ stdin=subprocess.DEVNULL, timeout=600)
+ if r.returncode != 0:
+ raise MacViewError("Couldn't build the Mac streaming helper: " + (r.stderr or r.stdout).strip()[-400:])
+
+ def _stale(self):
+ try:
+ built = AGENT.stat().st_mtime
+ return any(p.stat().st_mtime > built for p in (SOURCES / "Sources").glob("*.swift"))
+ except OSError:
+ return False
+
+ def ensure_agent(self):
+ with self.lock:
+ if self.agent and self.agent.poll() is None:
+ return
+ reason = self.unavailable()
+ if reason:
+ raise MacViewError(reason)
+ self.build()
+ env = {**os.environ, "FRAME_MAC_VIEW_TOKEN": self.token}
+ # The same port as before when restarting, so a running tunnel still
+ # fits; otherwise (or if it's gone) whatever the system gives.
+ for port in dict.fromkeys([self.port or 0, 0]):
+ self.agent = subprocess.Popen([str(AGENT), "serve", "--port", str(port), "--page", str(PAGE),
+ "--exit-on-eof"], env=env, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
+ line = self.agent.stdout.readline()
+ m = re.search(r"listening on 127\.0\.0\.1:(\d+)", line)
+ if m:
+ break
+ self.agent.kill()
+ else:
+ raise MacViewError(f"The Mac streaming helper didn't start: {line.strip() or 'no output'}")
+ if self.port != int(m.group(1)):
+ self._drop_tunnel()
+ self.port = int(m.group(1))
+ self.track(self.agent)
+ threading.Thread(target=self.agent.stdout.read, daemon=True).start() # drain
+
+ def call(self, path, method="GET", **query):
+ """A request to the agent; starts it if needed."""
+ self.ensure_agent()
+ url = f"http://127.0.0.1:{self.port}{path}?{urlencode({**query, 'k': self.token}, quote_via=quote)}"
+ req = urllib.request.Request(url, method=method, data=b"" if method == "POST" else None)
+ try:
+ with urllib.request.urlopen(req, timeout=10) as r:
+ return json.load(r)
+ except (urllib.error.URLError, OSError, ValueError) as e:
+ raise MacViewError(f"The Mac streaming helper didn't answer: {e}")
+
+ # ---- the tunnel from the Frame ----
+
+ def _drop_tunnel(self):
+ if self.tunnel and self.tunnel.poll() is None:
+ self.tunnel.terminate()
+ self.tunnel = None
+
+ def tunnel_up(self):
+ return self.tunnel is not None and self.tunnel.poll() is None
+
+ def ensure_tunnel(self, allow_new_port=False):
+ """Open the tunnel, on the port viewers already use if there is one.
+ A new port only when nobody is watching, since viewers can't move."""
+ with self.lock:
+ if self.tunnel_up():
+ return
+ last = ""
+ ports = [self.remote_port] if self.remote_port else list(REMOTE_PORTS)
+ if self.remote_port and allow_new_port:
+ ports += [p for p in REMOTE_PORTS if p != self.remote_port]
+ usb = self._usb_route()
+ # USB-C first when it's there; if that fails (unplugged just now,
+ # something else at that address), the normal path.
+ for via in ([usb, []] if usb else [[]]):
+ self.route = "usb" if via else "network"
+ if self._open_tunnel(via, ports):
+ return
+ last = self._last_tunnel_error
+ raise MacViewError(f"Couldn't open a tunnel from {self.frame} to this Mac: {last or 'no answer through it'}")
+
+ def _open_tunnel(self, via, ports):
+ """Tries the ports on one route; True once the tunnel answers. With self.lock held."""
+ last = ""
+ for port in ports:
+ proc = subprocess.Popen([*self.tunnel_ssh, *via, "-o", "ExitOnForwardFailure=yes",
+ "-o", "ServerAliveInterval=5", "-o", "ServerAliveCountMax=3", "-N",
+ "-R", f"127.0.0.1:{port}:127.0.0.1:{self.port}", self.frame],
+ stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE, text=True)
+ # A taken port makes ssh exit once it's connected; a working
+ # tunnel answers the agent's status from the Frame's side.
+ ok = False
+ for _ in range(15):
+ time.sleep(0.4)
+ if proc.poll() is not None:
+ break
+ if self._probe(port):
+ ok = True
+ break
+ if ok:
+ self.tunnel, self.remote_port = proc, port
+ self.track(proc)
+ self._supervise()
+ return True
+ if proc.poll() is None:
+ proc.terminate()
+ proc.wait()
+ last = (proc.stderr.read() or "").strip()
+ if "forward" not in last.lower():
+ break # not a port clash: the Frame is unreachable this way
+ self._last_tunnel_error = last
+ return False
+
+ def _usb_route(self):
+ """ssh options to reach the Frame over its USB-C network, or [].
+
+ Plugged into a Mac, the Frame is a USB network device (macOS lists it
+ as "Steam Frame"): the Frame's usb0 answers at about 1 ms, with none
+ of Wi-Fi's stalls. Measured 2026-09-28: content latency 7 ms instead
+ of 10, click to screen 17 ms instead of 27 (docs/mac-in-headset.md).
+ The host key is the same, so it's checked against the usual name."""
+ if not self.prefer_usb:
+ return []
+ try:
+ out = self.run("ip -4 -o addr show usb0 2>/dev/null", timeout=8)
+ except Exception:
+ return []
+ m = re.search(r"inet (\d+\.\d+\.\d+\.\d+)/", out or "")
+ if not m:
+ return []
+ ip = m.group(1)
+ try:
+ socket.create_connection((ip, 22), timeout=1).close()
+ except OSError:
+ return [] # not plugged into this Mac
+ alias = self.frame
+ try:
+ cfg = subprocess.run(["ssh", "-G", self.frame], capture_output=True, text=True, timeout=5).stdout
+ opts = dict(line.split(None, 1) for line in cfg.splitlines() if " " in line)
+ alias = opts.get("hostkeyalias") or opts.get("hostname") or alias # a configured alias wins
+ except (OSError, subprocess.SubprocessError):
+ pass
+ return ["-o", f"HostName={ip}", "-o", f"HostKeyAlias={alias}"]
+
+ def _supervise(self):
+ """Reopen the tunnel on the same port after the headset sleeps or the
+ network drops, so viewers that are retrying find the Mac again."""
+ if self.supervisor and self.supervisor.is_alive():
+ return
+
+ def loop():
+ while not self.closing:
+ time.sleep(5)
+ if self.closing or self.tunnel_up() or not (self.agent and self.agent.poll() is None):
+ continue
+ try:
+ self.ensure_tunnel()
+ except MacViewError:
+ pass # still unreachable; try again shortly
+
+ self.supervisor = threading.Thread(target=loop, daemon=True)
+ self.supervisor.start()
+
+ def _probe(self, port):
+ """Whether the Frame reaches the agent through the tunnel on `port`."""
+ # /ping needs no key, so none appears on the Frame's command lines.
+ cmd = f"curl -s -m 2 'http://127.0.0.1:{port}/ping'"
+ try:
+ return self.run(cmd, timeout=8).strip() == "frame-mac-view"
+ except Exception: # noqa: BLE001 - the server's Failure, timeouts: all mean "not yet"
+ return False
+
+ # ---- viewers on the Frame ----
+
+ def show(self, src, quality="balanced", width=None, height=None):
+ with self.viewer_lock:
+ self.launching += 1
+ try:
+ return self._show(src, quality, width, height)
+ finally:
+ with self.viewer_lock:
+ self.launching -= 1
+
+ def _show(self, src, quality, width, height):
+ if src != "test" and not src.startswith(("window:", "display:", "separate:")):
+ raise MacViewError("Pick a window or display to show.")
+ self.shows += 1
+ q = QUALITY.get(quality) or QUALITY["balanced"]
+ state = self.call("/status")
+ if src != "test" and not state.get("screen"):
+ raise MacViewError("Frame Control needs Screen Recording permission first (Allow… under Mac in the headset).")
+ if src.startswith("separate:") and not state.get("accessibility"):
+ raise MacViewError("A window on its own display needs the Accessibility permission too, to move it "
+ "(Allow… under Mac in the headset).")
+ self.shown -= set(state.get("finished", [])) # their Mac windows closed
+ if src in self.shown or any(st.get("src") == src for st in state.get("streams", [])):
+ # Showing it again replaces the old viewer, connected or not: this
+ # revokes its keys so it can't come back alongside the new one.
+ self.stop(src)
+ # Viewers that lost the tunnel keep retrying its old port, even though
+ # the agent no longer counts them, so only move when none are out there.
+ others = self.shown - {src}
+ self.ensure_tunnel(allow_new_port=not others)
+ appid = panel_id(src)
+ # Unique per launch, so a new window is never confused with an old one.
+ tag = "fc" + secrets.token_hex(4)
+ # A single-use ticket for this source, not Frame Control's key: the URL
+ # is visible in the Frame's process list.
+ ticket = self.call("/ticket", method="POST", src=src)["ticket"]
+ params = {"src": src, "t": ticket, "tag": tag, "codec": q["codec"], "max": q["max"], "fps": q["fps"],
+ "bpp": q["bpp"]}
+ url = f"http://127.0.0.1:{self.remote_port}/view?{urlencode(params)}"
+ w, h = fit(width or 1280, height or 720)
+ args = " ".join(_quote(str(a)) for a in (appid, url, w, h, tag, *self.browser_flags))
+ try:
+ out = self.run("bash -s -- " + args, stdin=LAUNCH, timeout=60)
+ except Exception as e: # noqa: BLE001 - the server's Failure carries the Frame's words
+ if "NO_BROWSER" in (getattr(e, "stdout", "") or ""):
+ raise MacViewError("The Frame needs a browser for this: install Chromium (Tools → Linux apps, "
+ "org.chromium.Chromium) or Chromium XR.")
+ raise MacViewError(str(e))
+ self.shown.add(src)
+ return {"panel": f"valve.steam.desktopgame.{appid}", "src": src, "detail": out.strip()}
+
+ def stop(self, src=None):
+ if src:
+ self.shown.discard(src)
+ else:
+ self.shown.clear()
+ if not (self.agent and self.agent.poll() is None):
+ return {"closed": 0}
+ out = self.call("/close", method="POST", **({"src": src} if src else {}))
+ if not self.shown:
+ threading.Thread(target=self._end_viewer_browser, args=(self.shows,), daemon=True).start()
+ return out
+
+ def _end_viewer_browser(self, shows):
+ """Chromium on the Frame outlives its last viewer window (verified
+ 2026-09-28), so once nothing is shown, end it. It runs with a profile
+ of its own, so nothing else is touched."""
+ time.sleep(2) # the viewers close their windows first
+ with self.viewer_lock:
+ if self.shown or self.shows != shows or self.launching:
+ return
+ try:
+ self.run("pkill -f '[f]rame-control/mac-view|[d]ata/frame-mac-view' || true", timeout=10)
+ except Exception:
+ pass
+
+ def state(self):
+ reason = self.unavailable()
+ if reason:
+ return {"available": False, "reason": reason}
+ status = self.call("/status")
+ windows = self.call("/windows").get("windows", []) if status.get("screen") else []
+ displays = self.call("/displays").get("displays", [])
+ return {"available": True, "screen": status.get("screen", False),
+ "accessibility": status.get("accessibility", False), "streams": status.get("streams", []),
+ "windows": windows, "displays": displays, "tunnel": self.tunnel_up(),
+ "route": self.route}
+
+ def restart_agent(self):
+ """Only if it's missing a permission: a running stream would stop."""
+ with self.lock:
+ if not (self.agent and self.agent.poll() is None):
+ return
+ status = self.call("/status")
+ if status.get("screen") and status.get("accessibility"):
+ return
+ with self.lock:
+ self.agent.terminate()
+ self.agent.wait(5)
+
+ def request_permissions(self):
+ return self.call("/permissions", method="POST")
+
+ def shutdown(self):
+ self.closing = True
+ try:
+ self.stop()
+ except MacViewError:
+ pass
+ for proc in (self.tunnel, self.agent):
+ if proc and proc.poll() is None:
+ proc.terminate()
+ # The helper puts separated windows back before it exits (about 1 s).
+ if self.agent:
+ try:
+ self.agent.wait(3)
+ except subprocess.TimeoutExpired:
+ self.agent.kill()
+
+
+def _quote(s):
+ return "'" + s.replace("'", "'\\''") + "'"
diff --git a/ui/index.html b/ui/index.html
index 2837eb6..708cc19 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -329,6 +329,13 @@
.game-card .bar > i { display: block; height: 100%; background: var(--action); }
.f-0 { color: var(--muted); }
/* ---- Android display ---- */
+ #mvQuality { background: rgba(0,0,0,.28); color: var(--text); border: 1px solid transparent; border-radius: 3px; padding: 4px 6px; font: inherit; font-size: 12px; }
+ .mv-windows { max-height: 420px; overflow-y: auto; }
+ .mv-perm { background: rgba(255,190,60,.08); border: 1px solid rgba(255,190,60,.25); border-radius: 4px; padding: 10px 12px; margin-bottom: 12px; font-size: 13px; }
+ .mv-perm .row { margin-top: 8px; }
+ .mv-live { color: var(--green, #7fd67f); font-size: 12px; }
+ .mv-opt { display: block; font-size: 13px; margin: 0 0 12px; }
+ .mv-opt .sub { display: block; margin-left: 22px; font-size: 12px; }
.disp select { width: 100%; background: rgba(0,0,0,.28); color: var(--text); border: 1px solid transparent; border-radius: 3px;
padding: 8px 10px; font: inherit; font-size: 13px; }
.disp select:focus { outline: none; border-color: var(--blue); }
@@ -641,6 +648,12 @@
You can also drop files anywhere in this window.
+ Android display
+
+ Automatic (detect VR)
+ Flat screen
+ VR app
+
Clipboard
@@ -708,6 +721,27 @@
+
+
Mac in the headset
+
+ Sharp Balanced
+ Light Compatible (JPEG)
+
+ Refresh
+ Give each window its own display
+ its menus and sheets come along, nothing covers it, and it goes back when you stop
+
+
+
Windows
+
+
+ Test pattern
+ Stop all
+
+ Each window or screen becomes its own panel in the headset. Place it with the SteamVR dashboard
+ (Float in World, Move, Size). Point and click with the laser, scroll with the thumbstick, and type on the Mac's keyboard.
+
+
Privacy & updates
Anonymous usage statistics
@@ -1520,6 +1554,7 @@ function upload(file, mode) {
xhr.setRequestHeader("X-Frame-UI", UI_KEY);
xhr.setRequestHeader("X-Filename", encodeURIComponent(file.name));
xhr.setRequestHeader("X-Mode", mode);
+ if (mode === "apk") xhr.setRequestHeader("X-APK-Display", $("apkDisplay").value);
const bar = $("prog").firstElementChild;
$("prog").style.display = "block"; bar.style.width = "0";
xhr.upload.onprogress = e => { if (e.lengthComputable) bar.style.width = (100 * e.loaded / e.total) + "%"; };
@@ -2313,10 +2348,85 @@ $("shotsRefresh").onclick = loadShots;
$("shotsSaveNew").onclick = e => saveShots(shots.list.filter(s => !s.saved), e.currentTarget);
$("shotsFolder").onclick = e => act($("shotsFolder").textContent, () => api("/api/open", { what: "shots" }), e.currentTarget);
+// ---- Mac in the headset: windows and displays as panels (ui/frame_macview.py) ----
+let mvSeq = 0, mvTimer = 0;
+// streaming: the src it's shown as (a window may be "separate:"), or "".
+// How a live stream is doing: frames shown, Mac-to-headset delay, bitrate, and
+// whether the network made it step down (fewer frames, then fewer pixels).
+function mvHealth(st) {
+ if (!st) return "";
+ const x = st.stats || {}, c = st.controller || {}, bits = [];
+ if (x.fps) bits.push(`${Math.round(x.fps)} fps`);
+ if (x.total && x.total.p50 != null) bits.push(`${Math.round(x.total.p50)} ms`);
+ if (x.mbps) bits.push(`${x.mbps} Mbit/s`);
+ if (c.tier > 0) bits.push(`weak link: ${c.fps} fps${c.scale < 1 ? `, ${Math.round(c.scale * 100)}% size` : ""}`);
+ return bits.length ? " · " + esc(bits.join(" · ")) : "";
+}
+function mvRow(src, title, sub, w, h, streaming, st) {
+ return `${esc(title)}
+
${esc(sub)}${streaming ? ' · in the headset ' + mvHealth(st) : ""}
+ ${streaming ? `
Stop `
+ : `
Show `}
+
`;
+}
+async function loadMacView(restart) {
+ const seq = ++mvSeq;
+ let s, err;
+ try { s = await api("/api/macview" + (restart ? "?restart=1" : "")); } catch (e) { err = e; }
+ if (seq !== mvSeq) return;
+ const box = $("macview");
+ if (err) { box.hidden = false; return failed($("mvWindows"), err); }
+ if (!s.available) { box.hidden = true; return; }
+ box.hidden = false;
+ const live = new Set((s.streams || []).map(x => x.src));
+ const byStream = src => (s.streams || []).find(x => x.src === src);
+ $("mvCount").textContent = live.size ? `${live.size} showing${s.route === "usb" ? " · over USB-C" : ""}` : "";
+ const perm = [];
+ if (!s.screen) perm.push(`Frame Control needs Screen Recording permission to show your windows.`);
+ if (!s.accessibility) perm.push(`Allow Accessibility too, so clicks and keys from the headset reach the Mac.`);
+ $("mvPerm").hidden = !perm.length;
+ $("mvPerm").innerHTML = perm.join(" ") + `Allow…
+ Then press Refresh. macOS may ask you to reopen Frame Control.
`;
+ const liveAs = src => live.has(src) ? src : src.startsWith("window:") && live.has("separate:" + src.slice(7)) ? "separate:" + src.slice(7) : "";
+ $("mvDisplays").innerHTML = (s.displays || []).map(d =>
+ mvRow(d.src, `Whole screen: ${d.name}`, `${d.w}×${d.h}${d.main ? " · main display" : ""}`, d.w, d.h, liveAs(d.src), byStream(liveAs(d.src)))).join("");
+ const wins = s.windows || [];
+ $("mvWinCount").textContent = wins.length ? `${wins.length}` : "";
+ $("mvWindows").innerHTML = !s.screen ? `Windows appear here once Screen Recording is allowed.
`
+ : wins.length ? wins.map(w => mvRow(w.src, w.title || w.app, `${w.title ? w.app + " · " : ""}${w.w}×${w.h}`, w.w, w.h, liveAs(w.src), byStream(liveAs(w.src)))).join("")
+ : `No windows open on this Mac's current desktop.
`;
+ // Keep the numbers fresh while something is in the headset and the card is on screen.
+ clearTimeout(mvTimer);
+ const tick = () => { mvTimer = document.hidden || !box.offsetParent ? setTimeout(tick, 3000) : (loadMacView(), 0); };
+ if (live.size) mvTimer = setTimeout(tick, 3000);
+}
+$("mvRefresh").onclick = () => loadMacView(true);
+$("mvSeparate").checked = localStorage.getItem("mvSeparate") !== "0";
+$("mvSeparate").onchange = e => localStorage.setItem("mvSeparate", e.target.checked ? "1" : "0");
+$("macview").onclick = async e => {
+ const b = e.target.closest("[data-mv]"); if (!b) return;
+ const src = b.dataset.src;
+ if (b.dataset.mv === "show") {
+ const as = $("mvSeparate").checked && src.startsWith("window:") ? "separate:" + src.slice(7) : src;
+ await act(`Show ${b.dataset.title} in the headset`, async () => {
+ const r = await api("/api/macview", { action: "show", src: as, quality: $("mvQuality").value,
+ w: +b.dataset.w || undefined, h: +b.dataset.h || undefined });
+ return { message: `${b.dataset.title} is a panel in the headset now. Float it with the SteamVR dashboard.`, ...r };
+ }, b);
+ } else if (b.dataset.mv === "stop") {
+ await act("Stop showing it", () => api("/api/macview", { action: "stop", src }), b);
+ } else if (b.dataset.mv === "stopall") {
+ await act("Stop everything in the headset", () => api("/api/macview", { action: "stop" }), b);
+ } else if (b.dataset.mv === "perm") {
+ await act("Ask macOS for permission", () => api("/api/macview", { action: "permissions" }), b);
+ }
+ setTimeout(loadMacView, 800);
+};
+
// ---- pages: #home, #games, #android, #tools (older section links still work) ----
const PAGES = ["home", "games", "android", "tools"];
const SECTION_PAGE = { view: "home", device: "home", shots: "home", library: "games", sideloaded: "games", getgames: "games",
- display: "android", transfer: "tools", apps: "tools", power: "tools", privacy: "tools" };
+ display: "android", transfer: "tools", apps: "tools", power: "tools", macview: "tools", privacy: "tools" };
let page = "home";
function showPage() {
const id = location.hash.slice(1);
@@ -2328,6 +2438,7 @@ function showPage() {
document.title = page === "home" ? "Frame Control" : `${page[0].toUpperCase() + page.slice(1)} · Frame Control`;
const section = !PAGES.includes(id) && id && $(id);
if (section) section.scrollIntoView(); else window.scrollTo(0, 0);
+ if (page === "tools") loadMacView();
}
window.addEventListener("hashchange", showPage);
// While a text field has focus, phones hide the bottom tab bar (see body.typing in the CSS).
diff --git a/ui/mac-view.html b/ui/mac-view.html
new file mode 100644
index 0000000..4d639f4
--- /dev/null
+++ b/ui/mac-view.html
@@ -0,0 +1,398 @@
+
+
+
+
+
+
+Mac
+
+
+
+
+Connecting to the Mac…
+
+
+
+
diff --git a/ui/server.py b/ui/server.py
index c6c3a33..0f662b4 100755
--- a/ui/server.py
+++ b/ui/server.py
@@ -43,6 +43,7 @@ import frame_android # noqa: E402
import frame_apk_versions # noqa: E402
import frame_catalog # noqa: E402
import frame_host # noqa: E402
+import frame_macview # noqa: E402
import frame_media # noqa: E402
import frame_report # noqa: E402
import frame_store # noqa: E402
@@ -1358,6 +1359,46 @@ def push_media(path):
"id": token + "/" + name}
+# ---- Mac in the headset (frame_macview.py) ----------------------------------
+
+# The tunnel gets its own connection: the shared master's options would win
+# over anything added after them.
+macview = frame_macview.MacView(["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=8"],
+ lambda remote, stdin=None, timeout=30: ssh(remote, stdin=stdin, timeout=timeout),
+ FRAME, track=_live_tunnels.add)
+
+
+def macview_state(query=None):
+ if LOCAL:
+ return {"available": False, "reason": "Runs on your computer, not the headset."}
+ try:
+ # Refresh restarts the helper, which picks up a permission just granted.
+ if (query or {}).get("restart") == ["1"]:
+ macview.restart_agent()
+ return macview.state()
+ except frame_macview.MacViewError as e:
+ raise Failure(str(e), 500)
+
+
+def macview_action(body):
+ """{action: show|stop|permissions, src, quality, w, h}."""
+ if LOCAL:
+ raise Failure("Runs on your computer, not the headset.", 400)
+ action = body.get("action")
+ try:
+ if action == "show":
+ w, h = body.get("w"), body.get("h")
+ return macview.show(str(body.get("src") or ""), str(body.get("quality") or "balanced"),
+ int(w) if w else None, int(h) if h else None)
+ if action == "stop":
+ return macview.stop(body.get("src") or None)
+ if action == "permissions":
+ return macview.request_permissions()
+ except frame_macview.MacViewError as e:
+ raise Failure(str(e), 502)
+ raise Failure("unknown action", 400)
+
+
# ---- Report a problem (frame_report.py) --------------------------------------
def report_preview(body):
@@ -1390,7 +1431,7 @@ POST = {"/api/media": media, "/api/agent/call": agent_call, "/api/agent/approval
"/api/webinstall/check": webinstall_check, "/api/webinstall/start": webinstall_start,
"/api/webinstall/cancel": webinstall_cancel,
"/api/telemetry": frame_telemetry.update_settings, "/api/telemetry/event": frame_telemetry.page_event,
- "/api/report/preview": report_preview, "/api/report": report_send}
+ "/api/report/preview": report_preview, "/api/report": report_send, "/api/macview": macview_action}
# ---- HTTP ------------------------------------------------------------------
@@ -1526,6 +1567,8 @@ class Handler(BaseHTTPRequestHandler):
"shared": frame_catalog.compat_db.shared()})
elif path == "/api/android/catalog":
self.send_json({"apps": frame_catalog.catalog()})
+ elif path == "/api/macview":
+ self.send_json(macview_state(parse_qs(url.query)))
elif path == "/api/telemetry":
self.send_json(frame_telemetry.state())
elif path == "/api/computer/state":
@@ -1708,10 +1751,16 @@ class Handler(BaseHTTPRequestHandler):
raise Failure(str(e), 400, {"package": info["package"], "version_code": info.get("version_code"), "blocker": str(e)})
ensure_master()
try:
- m = frame_android.install(str(dest), source=name)
+ display = self.headers.get("X-APK-Display", "auto")
+ if display not in ("auto", "flat", "vr"):
+ raise frame_android.FrameError("invalid APK display mode")
+ m = frame_android.install(str(dest), source=name,
+ flatscreen=None if display == "auto" else display == "flat")
except frame_android.FrameError as e:
raise Failure(str(e), 400)
- return {"message": f"Installed {m['label']} as its own app in the Steam library", "app": m}
+ kind = "VR app" if not m['flatscreen'] else "app"
+ notes = " ".join(m.get("vr_issues", []))
+ return {"message": f"Installed {m['label']} as its own {kind} in the Steam library. {notes}".strip(), "app": m}
return {"message": push_file(dest)}
finally:
if not keep:
@@ -1756,6 +1805,7 @@ def main():
if not frame_host.WINDOWS:
signal.signal(signal.SIGTERM, signal.SIG_IGN)
webinstall_shutdown()
+ macview.shutdown() # close the headset's viewers before the agent goes
# The master was started with -N, so it stays up until told to exit.
if CONTROL:
subprocess.run([*MUX, "-O", "exit", FRAME], capture_output=True, stdin=subprocess.DEVNULL)