Skip to content

feat(visionos): UIKit+Metal game-loop link path for visionOS#5087

Merged
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix/visionos-game-loop
Jun 13, 2026
Merged

feat(visionos): UIKit+Metal game-loop link path for visionOS#5087
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix/visionos-game-loop

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

What

visionOS was wired only as a SwiftUI app (linked with swiftc, requiring PerryVisionApp.swift, renaming _main → _perry_main_init). That model can't host a Metal/wgpu game — there's no SwiftUI scene tree, the native lib owns a CAMetalLayer and the runtime provides main().

This adds the UIKit + Metal game-loop model for visionOS, mirroring the existing watchOS dual-model approach: with --features ios-game-loop, visionOS now builds exactly like iOS/tvOS (UIKit UIApplicationMain + CAMetalLayer + wgpu surface on scene-connect).

Changes

  • perry-runtime/src/lib.rs — compile the ios_game_loop module (the C main() that spawns the user's TS on a game thread and calls UIApplicationMain) for target_os = "visionos" as well, not just ios/tvos. Without this the runtime has no main() for visionOS game-loop apps.
  • link/mod.rs — include visionOS in the _main → __perry_user_main entry-rename so the runtime's main() becomes the process entry point (shared with iOS/tvOS).
  • link/platform_cmd.rs — add an ios-game-loop sub-branch to the visionOS linker selection that links with clang (iOS-style: Swift-stdlib search paths + -lc++/-lc++abi for the engine's C++ deps) instead of swiftc. No PerryVisionApp.swift required. The default (non-game-loop) SwiftUI path is unchanged.

Testing

Verified end-to-end with a Bloom Engine 2D game:

  • Builds and runs on the visionOS simulator (--target visionos-simulator) and device (--target visionos).
  • The device .ipa validates and uploads to App Store Connect (TestFlight) with no errors.

aarch64-apple-visionos{,-sim} are tier-2 Rust targets (no -Zbuild-std).

Companion

Pairs with the Bloom Engine native-layer PR (the native/visionos crate) — this is the perry-toolchain half.

Summary by CodeRabbit

  • New Features
    • Extended game-loop support to visionOS, enabling game-loop-based apps on Vision Pro.
    • Added dual-mode visionOS build/link behavior: explicit game-loop (UIKit/Metal) linking and a default SwiftUI app-shell flow with appropriate runtime handling and linking.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df9ad58e-80bf-4521-bdad-2bba4b505da5

📥 Commits

Reviewing files that changed from the base of the PR and between 22386e0 and 7afaa8e.

📒 Files selected for processing (3)
  • crates/perry-runtime/src/lib.rs
  • crates/perry/src/commands/compile/link/mod.rs
  • crates/perry/src/commands/compile/link/platform_cmd.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/perry/src/commands/compile/link/mod.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/perry-runtime/src/lib.rs
  • crates/perry/src/commands/compile/link/platform_cmd.rs

📝 Walkthrough

Walkthrough

VisionOS game-loop support is added by extending the existing ios-game-loop feature to visionOS targets. The runtime module and entry-point symbol renaming are now conditionally compiled for visionOS, and the linker command logic branches between a game-loop mode using clang with explicit Swift library paths and a default SwiftUI mode using swiftc with entry-object renaming.

Changes

VisionOS game-loop feature support

Layer / File(s) Summary
Runtime module and entry-point rename for visionOS
crates/perry-runtime/src/lib.rs, crates/perry/src/commands/compile/link/mod.rs
The ios_game_loop module compilation and the _main__perry_user_main entry-point rename are both expanded to include target_os = "visionos" under the ios-game-loop feature.
VisionOS linker command logic for game-loop and SwiftUI modes
crates/perry/src/commands/compile/link/platform_cmd.rs
VisionOS linker command selection now branches on the ios-game-loop feature: game-loop mode links with clang and explicit Swift library paths plus libc++/libc++abi; default mode discovers and renames the _main entry object, then links via swiftc with the Swift runtime.

🎯 2 (Simple) | ⏱️ ~12 minutes

🐰 hop hop, visionOS joins the game-loop crew,
clang paths and swift runtimes, both old and new,
iOS, tvOS, visionOS all aligned,
linker commands branching, beauty redefined!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(visionos): UIKit+Metal game-loop link path for visionOS' clearly and specifically summarizes the main change: adding a UIKit+Metal game-loop linking path for visionOS.
Description check ✅ Passed The description covers all essential sections: a clear 'What' section explaining the change, detailed bullet-point changes with file paths, comprehensive testing verification, and a note on companion work.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry/src/commands/compile/link/mod.rs`:
- Line 333: The visionOS UI path is wrongly dropping the runtime for apps built
with the ios-game-loop feature; modify the logic that sets skip_runtime so that
when compiled_features contains "ios-game-loop" and ctx.needs_ui && is_visionos
you do NOT set skip_runtime (keep runtime_lib linked instead of only linking
libperry_ui_visionos.a), and also ensure the fallback rebuild invocation
includes the --features ios-game-loop flag so the renamed
ios_game_loop/__perry_user_main export is produced; update references to
compiled_features, skip_runtime, runtime_lib, libperry_ui_visionos.a and the
fallback rebuild invocation accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d2ae5234-7cd3-4852-b2d8-ae576812051e

📥 Commits

Reviewing files that changed from the base of the PR and between e092362 and 22386e0.

📒 Files selected for processing (3)
  • crates/perry-runtime/src/lib.rs
  • crates/perry/src/commands/compile/link/mod.rs
  • crates/perry/src/commands/compile/link/platform_cmd.rs

// entry object file so the perry runtime's main() (from ios_game_loop.rs)
// becomes the process entry point. It spawns _perry_user_main on a game thread.
if (is_ios || is_tvos) && compiled_features.iter().any(|f| f == "ios-game-loop") {
if (is_ios || is_tvos || is_visionos) && compiled_features.iter().any(|f| f == "ios-game-loop") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don’t drop runtime_lib for visionOS ios-game-loop UI apps.

Line 333 turns the game-loop entry rename on for visionOS, but the same function later still sets skip_runtime for every is_visionos && ctx.needs_ui build and then links only libperry_ui_visionos.a. That archive bundles its own perry-runtime, and the fallback rebuild command at Lines 1259-1260 does not pass --features ios-game-loop, so it will not export the new ios_game_loop main() / __perry_user_main handoff. A visionos + perry/ui + ios-game-loop app will therefore lose the process entry shim even though _main was renamed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry/src/commands/compile/link/mod.rs` at line 333, The visionOS UI
path is wrongly dropping the runtime for apps built with the ios-game-loop
feature; modify the logic that sets skip_runtime so that when compiled_features
contains "ios-game-loop" and ctx.needs_ui && is_visionos you do NOT set
skip_runtime (keep runtime_lib linked instead of only linking
libperry_ui_visionos.a), and also ensure the fallback rebuild invocation
includes the --features ios-game-loop flag so the renamed
ios_game_loop/__perry_user_main export is produced; update references to
compiled_features, skip_runtime, runtime_lib, libperry_ui_visionos.a and the
fallback rebuild invocation accordingly.

visionOS was wired only as a SwiftUI app (swiftc + PerryVisionApp.swift,
`_main -> _perry_main_init`), which can't host a Metal/wgpu game. Mirror
the watchOS dual-model approach so `--features ios-game-loop` produces a
UIKit + CAMetalLayer game-loop app on visionOS:

- perry-runtime: compile the `ios_game_loop` module (the C `main()` that
  spawns the user's TS on a game thread and calls UIApplicationMain) for
  `target_os = "visionos"` too, not just ios/tvos.
- link/mod.rs: include visionOS in the `_main -> __perry_user_main`
  entry-rename so the runtime's main() becomes the process entry.
- platform_cmd.rs: add an `ios-game-loop` sub-branch to the visionOS
  linker selection that links with clang (iOS-style: Swift-stdlib search
  paths + libc++) instead of swiftc — no PerryVisionApp.swift required.
  The default (non-game-loop) SwiftUI path is unchanged.

Verified end-to-end: builds + runs a Bloom Engine game on the visionOS
simulator and device, and the device IPA validates + uploads to
App Store Connect (TestFlight).
@proggeramlug proggeramlug force-pushed the fix/visionos-game-loop branch from 22386e0 to 7afaa8e Compare June 13, 2026 13:15
@proggeramlug proggeramlug merged commit c44c0f0 into PerryTS:main Jun 13, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant