-
Notifications
You must be signed in to change notification settings - Fork 5
Build System
Halo Vision has three build products, all produced from the same generated engine translation and the same host sources: the visionOS app HaloVision.app (built either through a generated Xcode project or by a direct SDK-compiler route), and the desktop diagnostic host halo-host for macOS (built by a Makefile). The build sits between engine generation (which writes 32 C translation units plus a dispatch bundle into native/build/engine-reuse/whole-exe/) and device preparation and signing. The command-line app build is always unsigned and source-only: game data is only added by the setup wizard or by Xcode when a private payload has been staged.
| File | Role |
|---|---|
tools/build_engine_vision.py |
App build driver: generated-source inventory, visual-pack check, XcodeGen, xcodebuild, the direct xcrun clang/swiftc route, Info.plist synthesis, build receipt. |
native/EngineVision/project.yml |
XcodeGen spec for the EngineVision target (sources, resources, flags, frameworks, signing, versions). |
native/EngineVision/Info.plist |
Base Info.plist merged with Xcode's generated keys (controller keys, file sharing). |
native/EngineHost/Makefile |
Desktop macOS host halo-host (Cocoa window via metalwin.m, main.c). |
tools/generate_engine_reuse.py, tools/export_engine_imports.py
|
Produce the generated inputs (owned by Static Translation Pipeline). |
tools/visual_assets.py |
ensure_visual_assets() and bundle_visual_assets() used by the build. See Visual Mods Pipeline. |
requirements-development.txt |
Pinned Python dependencies for generation and tooling. |
docs/BUILDING.md |
User-facing build and signing guide. |
flowchart LR
subgraph Inputs
EXE["game/halo.exe (SHA-256 c9acf0c4...)"]
AL["decompilation/c9acf0c46954/*.txt"]
TR["tools/engine_reuse + third_party/xwa"]
HOST["native/EngineHost/*.c *.m"]
REUSE["native/EngineReuse/*.h"]
SW["native/EngineVision/Sources (24 Swift, 2 ObjC)"]
MOJO["third_party/mojoshader (4 C files)"]
VM[".setup/VisualMods (verified packs)"]
end
EXE --> GEN["generate_engine_reuse.py --chunks 32"]
AL --> GEN
TR --> GEN
GEN --> OUT["native/build/engine-reuse/whole-exe: chunk_000..031.c, engine_bundle.c, engine_functions.h, generation.json"]
EXE --> IMP["export_engine_imports.py"] --> OUT2["engine_imports.c"]
OUT --> BEV["build_engine_vision.py"]
OUT2 --> BEV
HOST --> BEV
REUSE --> BEV
SW --> BEV
MOJO --> BEV
VM --> BEV
BEV -->|"default"| XG["xcodegen generate"] --> XB["xcodebuild CODE_SIGNING_ALLOWED=NO"]
XB -->|"visionOS platform runtime missing"| DIR
BEV -->|"--direct"| DIR["direct xros clang + swiftc"]
XB --> APP1[".build/DerivedData/.../HaloVision.app"]
DIR --> APP2[".build/DirectXROS/HaloVision.app"]
XB --> RCPT["build-receipt.json"]
OUT --> MK["make -C native/EngineHost"] --> HH["native/EngineHost/halo-host"]
OUT2 --> MK
HOST --> MK
- Apple Silicon Mac, Xcode with the visionOS SDK. The app's deployment target is visionOS 26.0; the release build used SDK 26.5 (BUILDING.md). "Older toolchain combinations have not been qualified."
- Python 3.12 virtual environment with
requirements-development.txt:capstone==5.0.6,pefile==2024.8.26,numpy==2.4.2,Pillow==11.3.0,unicorn==2.1.4. - XcodeGen for the Xcode route (not needed for
--direct). - The generated engine sources (see below). Without them, every
build_engine_vision.pymode, including--generate-only, fails. - The verified visual packs in
.setup/VisualMods(or a Complete bundle, or network access to download them).ensure_visual_assets()runs on every invocation.
From the repository root, with the venv active and game/halo.exe (or HALO_EXE) pointing at the supported executable:
python tools/generate_engine_reuse.py \
@decompilation/c9acf0c46954/function-addresses.txt \
@decompilation/c9acf0c46954/extra-function-entries.txt \
--label whole-exe --max-functions 10000 --trap-unsupported --discover --chunks 32
python tools/export_engine_imports.pyOutput directory native/build/engine-reuse/whole-exe/:
| File | Content |
|---|---|
sub_XXXXXXXX.c |
One lifted original function each. |
chunk_000.c .. chunk_031.c
|
32 translation units; each #includes an equal slice of the sub_*.c files (generate_engine_reuse.py:208-213). Stale chunks from a larger chunk count are deleted so they cannot define functions twice. |
engine_functions.h |
Prototypes for every sub_* plus the dispatch API; includes engine_hooks.h. |
engine_bundle.c |
Sorted entry table, ENGINE_FN_COUNT, and engine_dispatch() (binary search, override and external-import hooks), plus engine_reuse_entry(). |
engine_imports.c |
Import thunk table from export_engine_imports.py. |
generation.json |
Generation receipt (function counts, unresolved boundaries, trap annotations). |
The 1.0.3 validation recorded 8,336 functions in 32 chunks (VALIDATION.md). The setup wizard runs these two commands automatically and skips them when a fingerprint of the inputs is unchanged (Setup Wizard, stage 9).
Parsed in main() (build_engine_vision.py:218-224):
| Option | Default | Effect |
|---|---|---|
--configuration {Debug,Release} |
Debug |
Selects -O0/-Onone (Debug) or -O2/-O (Release) on the direct route, and the Xcode configuration otherwise. |
--clean |
off | Xcode route: deletes .build/DerivedData before building. Direct route: deletes all of .build/DirectXROS (objects and product). |
--generate-only |
off | Run XcodeGen and stop. Prints Generated native/EngineVision/EngineVision.xcodeproj. |
--direct |
off | Skip XcodeGen and xcodebuild; compile and link with the SDK compilers via xcrun --sdk xros. Produces an unsigned device app. |
Every invocation, before branching (build_engine_vision.py:226-235):
-
Tool check.
--directneeds onlyxcrun; all other modes (including--generate-only) needxcodegen,xcodebuildandxcrunonPATH. A missing tool raisesrequired tool not found. -
Generated inventory.
generated_inventory()(build_engine_vision.py:37-52) requires exactly 32chunk_*.cfiles plusengine_bundle.c,engine_imports.candengine_functions.h, elsewhole-engine source incomplete: chunks=N, missing=[...]. It returnschunkCount,functionCount(parsed fromENGINE_FN_COUNT = Ninengine_bundle.c),generatedCBytes(sum of all*.csizes), and the SHA-256 ofengine_bundle.candengine_imports.c. -
Visual packs.
ensure_visual_assets()makes sure.setup/VisualModsexists and verifies (downloading the pinned archive if necessary). The Xcode project references these files as required resources, so this also applies to--generate-only. -
.build/is created;--cleanremoves.build/DerivedData. - Prints
Whole engine: N functions in 32 chunks, X generated C bytes.
Any exception escaping main() is printed as build_engine_vision.py: <error> and the process exits 2 (build_engine_vision.py:299-304).
-
xcodegen generate --spec native/EngineVision/project.yml --project native/EngineVision(build_engine_vision.py:242-247). With--generate-only, stop here. -
xcodebuild -project EngineVision.xcodeproj -scheme EngineVision -configuration <cfg> -sdk xros -destination generic/platform=visionOS -derivedDataPath native/EngineVision/.build/DerivedData -jobs 2 CODE_SIGNING_ALLOWED=NO COMPILER_INDEX_STORE_ENABLE=NO build(build_engine_vision.py:252-257). Output is streamed throughrun_stream()intonative/EngineVision/.build/build.log; only lines containingCompileC,SwiftCompile,Ld,BUILD SUCCEEDED,error:orwarning:are echoed. -
Automatic fallback. If
xcodebuildfails and the log contains bothplatform:visionOSandis not installed(the SDK is present but Xcode's generic-device platform runtime is not), the script reports "xcodebuild unavailable: Xcode SDK is installed, but its generic-device platform runtime is absent; using direct xros toolchain" and runs Route B (build_engine_vision.py:266-270). -
Build receipt. Written to
native/EngineVision/build-receipt.jsonand printed (build_engine_vision.py:271-296). The exit status is the build result.
The Xcode route is unsigned on the command line (CODE_SIGNING_ALLOWED=NO), but the project itself has CODE_SIGNING_ALLOWED: YES and CODE_SIGN_STYLE: Automatic, so pressing Run in Xcode signs with the user's selected Team.
| Field | Meaning |
|---|---|
builtAtUTC |
ISO timestamp |
configuration |
Debug or Release
|
sdk |
xcrun --sdk xros --show-sdk-version |
jobs |
always 2 |
optimization |
O2 generated C (Release) or O0 generated C
|
swiftOptimization |
O or Onone
|
exitCode |
final result |
builder |
xcodebuild or direct-xros-toolchain
|
xcodebuildExitCode, xcodebuildFallbackReason
|
the original xcodebuild status and fallback reason (or null) |
elapsedSeconds |
wall time from the xcodebuild start |
project, log, directLog
|
repository-relative paths (directLog only when the fallback ran) |
products |
every HaloVision.app under DerivedData/Build/Products, plus the direct product if built |
chunkCount, functionCount, generatedCBytes, engineBundleSHA256, engineImportsSHA256
|
the generated inventory |
executableSHA256 |
SHA-256 of HaloVision in the last listed product (only on success) |
build-receipt.json is listed in .gitignore. The --direct route does not write a receipt; it prints a JSON object {exitCode, product, ...inventory} instead (build_engine_vision.py:237-240).
direct_xros_build() (build_engine_vision.py:69-215) "Build[s] an unsigned device bundle without Xcode's separately installed platform runtime." It is the route used by setup.sh --stage build and by the release builds.
| Group | Files | Language flags |
|---|---|---|
| EngineHost C (17) |
host.c, shims_kernel32.c, shims_misc.c, directsound.c, directsound_mixer.c, vorbis_shim.c, d3d9.c, texture_decode.c, metalshader.c, dinput8.c, ddraw.c, resources.c, overrides.c, threading.c, pointer.c, haptics.c, halo_settings.c
|
common |
| MojoShader (4) |
third_party/mojoshader/mojoshader.c, mojoshader_common.c, profiles/mojoshader_profile_common.c, profiles/mojoshader_profile_metal.c
|
common |
| Generated (34) |
chunk_000.c .. chunk_031.c, engine_bundle.c, engine_imports.c
|
common |
| Objective-C (4) |
native/EngineVision/Sources/EngineVisionRuntime.m, EngineDiagnosticsBridge.m, native/EngineHost/gamecontroller.m, metalrenderer.m
|
common plus -fobjc-arc -fblocks
|
| Swift (24) | every native/EngineVision/Sources/*.swift
|
see below |
That is 59 native translation units and 24 Swift sources, matching the counts recorded in VALIDATION.md. native/EngineReuse contributes headers only (engine_cpu.h, engine_hooks.h, engine_flags.h, engine_registers.h, engine_arm64_fenv_prototype.h) via -I; native/EngineReuse/engine_runtime.c is not compiled into the app by either route (it is exercised by native/Tests/EngineRuntimeContract.c, see Testing and Source Checks). The desktop-only main.c and metalwin.m are not part of the app.
From build_engine_vision.py:78-86:
| Flag | Purpose |
|---|---|
-target arm64-apple-xros26.0 -isysroot <xros SDK> |
visionOS 26.0 device target |
-ffile-prefix-map=<repo>=. |
Rewrites the absolute checkout path in debug info and __FILE__ to . so build machine paths do not leak into binaries |
-DENGINE_FLAT_MEMORY=1 |
Flat guest memory model (see Guest Memory and Heap) |
-DHALO_ARM64_FENV_FAST=1 |
Fast ARM64 floating-point environment helpers (see x87 Floating Point) |
-frounding-math -ffp-contract=off |
Honour dynamic rounding modes and forbid FMA contraction so translated x87 arithmetic stays bit-exact |
-DMOJOSHADER_NO_VERSION_INCLUDE=1 and -DSUPPORT_PROFILE_{D3D,BYTECODE,HLSL,GLSL120,GLSLES,GLSLES3,GLSL,ARB1,ARB1_NV,SPIRV,GLSPIRV}=0
|
Build MojoShader with only the Metal profile (see Shader Translation) |
-I third_party/mojoshader -I native/EngineVision/Sources -I native/EngineHost -I native/EngineReuse -I <GEN> |
include paths |
-O2 (Release) / -O0 (Debug) |
optimisation |
Compilation runs in a two-worker ThreadPoolExecutor ("jobs=2"), printing native K/N every eight units. Objects go to .build/DirectXROS/objects/native_NNN_<stem>.o. Diagnostics are appended to .build/direct-xros-build.log. The first compile error aborts with return code 1.
One swiftc invocation compiles all Swift sources (build_engine_vision.py:132-157): -target arm64-apple-xros26.0 -sdk <sdk>, -O/-Onone, -parse-as-library -swift-version 5 -Xfrontend -strict-concurrency=minimal, -import-objc-header native/EngineVision/Sources/EngineVision-Bridging-Header.h, -Xcc -I for Sources and EngineHost, -module-name EngineVision -emit-module, an output file map at .build/DirectXROS/swift-output-map.json (objects swift_NN_<stem>.o), and -j 2.
swiftc links all native and Swift objects into .build/DirectXROS/HaloVision.app/HaloVision with -rpath @executable_path/Frameworks, -lm, and the frameworks Foundation, SwiftUI, UIKit, Metal, MetalKit, QuartzCore, CompositorServices, ARKit, GameController, CoreHaptics, AudioToolbox, AVFAudio (build_engine_vision.py:159-174).
The direct route writes a binary Info.plist itself (build_engine_vision.py:176-207):
| Key | Value |
|---|---|
CFBundleIdentifier |
org.example.halovision (public placeholder) |
CFBundleExecutable / CFBundleName
|
HaloVision |
CFBundleDisplayName |
Halo Vision |
CFBundleShortVersionString / CFBundleVersion
|
1.0.3 / 103
|
HaloBuildID |
the UTC build timestamp; reported as buildID by the app's diagnostics (EngineDiagnostics.swift:178), which fall back to unknown when the key is absent; see Diagnostics and Telemetry
|
MinimumOSVersion |
26.0 |
CFBundleSupportedPlatforms / DTPlatformName / UIDeviceFamily
|
["XROS"] / xros / [7]
|
GCSupportsControllerUserInteraction, GCSupportedGameControllers (ExtendedGamepad), GCRequiresControllerUserInteraction (visionOS: true) |
game controller declarations |
NSHandsTrackingUsageDescription, NSWorldSensingUsageDescription
|
permission strings |
UIApplicationPreferredDefaultSceneSessionRole and UIApplicationSceneManifest
|
launch directly into a CPSceneSessionRoleImmersiveSpaceApplication scene with UIImmersionStyleFull
|
It then writes PkgInfo (APPL????), copies Resources/ThirdPartyNotices.txt and Resources/PrivacyInfo.xcprivacy, and calls bundle_visual_assets(product, ensure_visual_assets()), which clone-copies TextureMods.hvt, ShaderMods.hvs, CEnshineSources.zip and VisualModsManifest.json into the bundle root and re-verifies them. If an existing product already holds different pack bytes, it raises "Existing app has different visual assets; rebuild with --clean".
Without --clean, the product directory is reused across builds. This is what lets setup.sh --stage build add GamePayload/ to the app after the build and detect a mismatched payload on the next run.
| Section | Setting | Notes |
|---|---|---|
options |
bundleIdPrefix: org.example.halovision, deploymentTarget.visionOS: "26.0"
|
|
settings.base |
SWIFT_VERSION 5.0, SWIFT_STRICT_CONCURRENCY minimal, CODE_SIGNING_ALLOWED YES, CODE_SIGN_STYLE Automatic, ENABLE_USER_SCRIPT_SANDBOXING YES, CLANG_ENABLE_MODULES YES, GCC_OPTIMIZATION_LEVEL 0, COMPILER_INDEX_STORE_ENABLE NO
|
Release overrides GCC_OPTIMIZATION_LEVEL 2. |
targets.EngineVision.sources |
Sources/; resources ThirdPartyNotices.txt, PrivacyInfo.xcprivacy; ../../.setup/VisualMods/{TextureMods.hvt,ShaderMods.hvs,CEnshineSources.zip,VisualModsManifest.json}; optional ../../.setup/GamePayload (folder) and ../../.setup/GamePayloadManifest.json; the same 17 EngineHost C files plus gamecontroller.m and metalrenderer.m; the four MojoShader files; and ../build/engine-reuse/whole-exe filtered to chunk_*.c, engine_bundle.c, engine_imports.c
|
The payload entries carry the comment "Created locally by setup.sh; never included in public source releases." |
dependencies |
the same 12 SDK frameworks as the direct link | |
settings.base (target) |
PRODUCT_BUNDLE_IDENTIFIER org.example.halovision, PRODUCT_NAME HaloVision, PRODUCT_MODULE_NAME EngineVision, GENERATE_INFOPLIST_FILE YES with INFOPLIST_FILE Info.plist, display name, controller and usage-description keys, scene manifest generation, TARGETED_DEVICE_FAMILY 7, CURRENT_PROJECT_VERSION 103, MARKETING_VERSION 1.0.3, bridging header, HEADER_SEARCH_PATHS (Sources, mojoshader, EngineHost, EngineReuse, generated dir), OTHER_CFLAGS (same defines as the direct route), OTHER_LDFLAGS -lm
|
|
scheme |
gatherCoverageData: false |
See project.yml:1-108.
These are visible in the source and worth knowing when comparing builds:
| Aspect | Xcode route | Direct route |
|---|---|---|
| Signing | Automatic signing when run from Xcode; unsigned from the script | Always unsigned |
| Info.plist | Generated keys merged with native/EngineVision/Info.plist, which also sets UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace
|
Written by the script; does not set those two keys |
| Hands-tracking string | "A pinch chooses the menu item you are looking at; hands are never recorded." | "A pinch selects the menu item you are looking at." |
HaloBuildID |
not set by project.yml (diagnostics report buildID as unknown) |
UTC build timestamp |
| Game payload | Included automatically if .setup/GamePayload exists (optional resource) |
Never added by the build; setup.sh --stage build copies it in afterwards |
| Receipt | build-receipt.json |
JSON on stdout only (unless reached through the fallback) |
| Path | Content |
|---|---|
native/EngineVision/EngineVision.xcodeproj |
Generated project (ignored by Git). |
native/EngineVision/.build/DerivedData/Build/Products/*/HaloVision.app |
Xcode-route product. |
native/EngineVision/.build/build.log |
Full xcodebuild log. |
native/EngineVision/.build/DirectXROS/HaloVision.app |
Direct-route product (the path printed by setup and referenced by prepare_engine_vision_device.py as DEFAULT_APP). |
native/EngineVision/.build/DirectXROS/objects/ |
Direct-route objects and EngineVision.swiftmodule. |
native/EngineVision/.build/direct-xros-build.log |
Direct-route diagnostics. |
native/EngineVision/build-receipt.json |
Xcode-route receipt. |
native/EngineHost/halo-host |
Desktop host binary. |
native/build/engine-reuse/whole-exe/host-obj/ |
Desktop host objects. |
All are ignored by .gitignore.
make -C native/EngineHost -j2The Makefile builds halo-host, a macOS arm64 runner for the same translated engine with a Cocoa/Metal window. It is used for desktop probes (for example tools/probe_engine_menu_input.py) and is described further on EngineHost Overview.
| Variable | Value |
|---|---|
VISION |
repository root (../..) |
GEN |
$(VISION)/native/build/engine-reuse/whole-exe |
OBJ |
$(GEN)/host-obj |
CFLAGS |
-O2 -w -std=c11 -DENGINE_FLAT_MEMORY=1 -DHALO_ARM64_FENV_FAST=1 -I. -I$(VISION)/native/EngineReuse -I$(GEN) |
HOST_SRCS |
the 17 app host C files plus main.c
|
HOST_OBJC |
metalwin.m, metalrenderer.m, gamecontroller.m (built with -O2 -ObjC -fobjc-arc) |
MOJO_OBJS |
the four MojoShader objects, built with the Metal-only SUPPORT_PROFILE_* defines |
| Link | -lm -framework Cocoa -framework Metal -framework QuartzCore -framework GameController -framework CoreHaptics -framework AudioToolbox |
Notable rules:
-
Per-chunk dependency tracking. Line 12 uses
sedto extract every"sub_*.c"include from each generated chunk and makes the chunk object depend on those files, so editing or regenerating a single lifted function rebuilds only its chunk, even for objects cached before dependency files existed (Makefile:10-12). -
Header dependencies. Explicit prerequisites tie
overrides.o,d3d9.o,host.o,pointer.oand others to their.inc/.hfiles (texture/shader mod runtime, panorama, native gather, model capture, process-vertices helpers). -
Generation rules.
$(GEN)/engine_bundle.cand$(GEN)/engine_imports.chave recipes that run the generator and import exporter with the systempython3, somakecan regenerate from scratch when the dependencies andgame/halo.exe(orHALO_EXE) are available. -
Floating-point flags. The Makefile's
CFLAGSdo not add-frounding-math -ffp-contract=off, unlike both app routes.run_source_checks.pycorrespondingly runs its native-leaf comparisons twice: "with the release flags and with the headset's (-frounding-math, the FPSR helper)" (run_source_checks.py:129-137). -
make cleanremoveshost-obj/andhalo-host.
The repository's tests are not built by this Makefile; they are compiled ad hoc by tools/run_source_checks.py.
The public version appears in several places, all of which must be updated together for a release (see Release Process and Hygiene):
| Location | Value at this commit |
|---|---|
VERSION |
1.0.3 |
project.yml:101-102 |
CURRENT_PROJECT_VERSION 103, MARKETING_VERSION 1.0.3
|
build_engine_vision.py:184,186 |
CFBundleShortVersionString 1.0.3, CFBundleVersion 103
|
mods/visual-assets.json, mods/runtime-settings.json, SOURCE_MANIFEST.json
|
"version": "1.0.3" |
The public build-number series (100, 101, 102, 103) is separate from the owner's private development build numbers (for example Build91); RELEASING.md asks that both be identified when comparing results.
| Message | Cause | Fix |
|---|---|---|
required tool not found: xcodegen (or xcodebuild, xcrun) |
toolchain missing | Install XcodeGen/Xcode, or use --direct. |
whole-engine source incomplete: chunks=N, missing=[...] |
generation not run, failed, or used a different chunk count | Re-run generation with --chunks 32. |
Visual asset is missing or changed: ... / Visual archive checksum mismatch
|
packs absent or modified | See Visual Mods Pipeline. |
Existing app has different visual assets; rebuild with --clean |
stale direct product |
--direct --clean. |
direct xros native compile error: <file> ... |
a C/ObjC compile error | See .build/direct-xros-build.log. |
xcodebuild unavailable: ... using direct xros toolchain |
visionOS platform runtime not installed | Informational; the direct route is used automatically. |
There is no dedicated test of build_engine_vision.py itself. Relevant coverage:
-
test_public_project_includes_optional_payload_and_signingintools/test_setup_halo.pyasserts thatproject.ymlkeepsCODE_SIGNING_ALLOWED: YES, the../../.setup/GamePayloadManifest.jsonresource andoptional: true. -
tools/check_core_telemetry_xros.py(run byrun_source_checks.pyon macOS) compiles and links a probe forarm64-apple-xros26.0with-Werror -Wl,-fatal_warnings, checks that the telemetry APIs are stronglibSystemimports, and thatLC_BUILD_VERSIONsays platform 11 (xrOS) minos 26.0. It prints SKIP when no xros SDK is installed. - Release validation (recorded in VALIDATION.md) records an unsigned Release build of 59 native units and 24 Swift sources with SDK 26.5, and XcodeGen projects containing every visual pack in Copy Bundle Resources.
Documents master-chef at commit 9f915af (v1.0.3). Unofficial project, not affiliated with Microsoft, Bungie, Gearbox or Apple. Original code is MIT licensed; game content is not included.
Overview
- Architecture Overview
- Repository Layout
- Glossary
- Environment Variables
- Contributing Guide
- Open Questions
Translation
- Static Translation Pipeline
- XWA Decoder and Lifter
- Function Address Lists
- EngineReuse Runtime
- x87 Floating Point
Host runtime
- EngineHost Overview
- Win32 Compatibility Layer
- Threading and Synchronization
- Guest Memory and Heap
- Engine Overrides and Hooks
- Runtime Settings
Graphics
- Direct3D9 Bridge
- Metal Renderer
- Shader Translation
- Textures and Texture Packs
- Geometry Fast Paths
- Radial Fog
Panorama and presentation
- Panorama System
- Panorama Budget and LOD
- Frame Pacing
- visionOS App
- Immersive Presenter
- Layer Alignment
Audio and input
Tooling and process