Skip to content

Fix tvOS simulator support (Jolt sim archive, libc++, CAMetalLayer present, wgpu limits, file I/O)#58

Merged
proggeramlug merged 3 commits into
mainfrom
fix/tvos-simulator-support
Jun 8, 2026
Merged

Fix tvOS simulator support (Jolt sim archive, libc++, CAMetalLayer present, wgpu limits, file I/O)#58
proggeramlug merged 3 commits into
mainfrom
fix/tvos-simulator-support

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

A game compiled for --target tvos-simulator failed to run on the tvOS simulator at five separate points. This PR fixes all of them; the result is the full game (title screen and gameplay — tiles, sprites, enemies, HUD) rendering correctly on the Apple TV simulator.

Each failure was found by fixing the prior one and re-running, so they're layered:

# Symptom Root cause Fix
1 Link error: building for 'tvOS-simulator', but linking in object file built for 'tvOS' find_prebuilt_dir keyed jolt-prebuilt only on {os}-{arch}, so every Apple *-sim target linked the device Jolt archive Append -sim when CARGO_CFG_TARGET_ABI == "sim" to select the shipped <os>-<arch>-sim archive
2 Link error: unresolved ___cxa_guard_acquire/release tvos target declared frameworks but no libs; Jolt's C++ needs libc++ Add "libs": ["c++"] (matches macos)
3 Runtime panic: request_deviceLimitsExceeded { max_inter_stage_shader_variables: requested 16, allowed 15 } Requested wgpu::Limits::default(); the simulator's Metal caps it at 15 Clamp that field to adapter.limits()
4 App runs, render loop healthy, but screen is black (only UIKit subviews show) CAMetalLayer.presentsWithTransaction = YES — incompatible with wgpu's async -presentDrawable:; CoreAnimation waits for a CATransaction commit that never happens Set it to NO (both scene_will_connect and deferred_init)
5 SIGSEGV at 0xfffffffffffffff8 entering a level bloom_read_file returned null on a missing file; Perry's inline .length dereferences (ptr-8). Also used the obsolete 8-byte string header (data@8 vs 0.5.x's 20-byte StringHeader data@20), corrupting even valid reads Route both branches through alloc_perry_string; return an empty string, never null

Also removes two leftover debug UILabels that were drawn over the game to verify window visibility.

Scope note

Fix #1 (build.rs) also repairs the iOS and watchOS simulator link paths, which had the same device-archive bug.

Validation

  • Built bloom-tvos for aarch64-apple-tvos-sim — compiles clean.
  • The equivalent changes against the published 0.4.12 build run the full game on the Apple TV 4K (3rd gen) tvOS 26 simulator: title screen, then a playable level (player, grass/dirt tiles, coins, walker enemies, spikes, gem, HUD hearts/lives).

Note: a companion fix is needed in the Perry compiler so the tvOS bundle includes the project assets/ dir (it currently skips the resource copy that iOS/watchOS/visionOS do) — submitted separately to PerryTS/perry. Without it the engine runs but level/sprite files aren't in the bundle.

find_prebuilt_dir keyed the jolt-prebuilt lookup only on
{target_os}-{target_arch}, so every Apple *-sim target (tvos/ios/watchos)
resolved to the device archive (e.g. tvos-arm64) and linked a device-built
Jolt object into a simulator build. ld then rejects it:

  building for 'tvOS-simulator', but linking in object file built for 'tvOS'

jolt-prebuilt already ships the correct <os>-<arch>-sim archives; select
them by appending -sim when CARGO_CFG_TARGET_ABI == "sim". Device builds
are unaffected. Fixes the link step for the tvOS/iOS/watchOS simulators.
The tvos target in perry.nativeLibrary.targets declared frameworks but no
libs, unlike macos which lists ["c++"]. The Jolt C++ objects pull in
libc++ symbols (___cxa_guard_acquire/release, etc.), so a tvOS link failed
with unresolved symbols. Add "libs": ["c++"] to match macos.
… I/O

Three runtime bugs that prevented the tvOS native layer from running a game,
plus debug-scaffold cleanup:

- CAMetalLayer.presentsWithTransaction was YES in both scene_will_connect and
  deferred_init. wgpu presents its drawable asynchronously via
  -presentDrawable:, which is incompatible: CoreAnimation waits for a
  synchronous CATransaction commit that never comes, so the layer never
  displays rendered frames (screen stays black behind UIKit subviews). Set NO.

- request_device asked for wgpu::Limits::default()
  (max_inter_stage_shader_variables = 16), but the tvOS/iOS simulators' Metal
  only allows 15, so device creation panicked with LimitsExceeded. Clamp that
  limit to adapter.limits(); real hardware meets/exceeds default() so it is
  unchanged.

- bloom_read_file returned std::ptr::null() on a missing/unreadable file and
  hand-rolled the obsolete Perry-0.4 8-byte string header. Perry 0.5.x reads
  .length by dereferencing (ptr - 8), so a null string segfaults the caller,
  and the 8-byte header (data@8 vs the 0.5.x 20-byte StringHeader's data@20)
  returned garbage even for existing files. Route both branches through
  alloc_perry_string; return an empty string, never null.

- Remove two debug UILabels ("BLOOM JUMP TVOS" / "...DEBUG") that were
  drawn over the game to verify window visibility.
@proggeramlug proggeramlug merged commit 38eaf0e into main Jun 8, 2026
8 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.

Cascaded shadow maps (CSM)

1 participant