Make games run on watchOS (FFI stubs, string FFI, file reading, 2D camera)#60
Merged
Conversation
gen_stubs.js threw on `string` params (rustType only knew f64/i64/void), so it crashed before writing src/ffi_stubs.rs and the checked-in file went stale — a game linking against the watchOS lib failed with undefined `_bloom_*` (post-pass, ssao, wind, …). Map `string` -> i64 (Perry passes string pointers in an integer register) for both params and returns, and add bloom_get_language / bloom_set_direct_2d_mode to OVERRIDES (real impls in lib.rs that regen would otherwise duplicate). Regenerate. Four functions (bloom_set_material_params, bloom_splat_impulse, bloom_profiler_frame_history, bloom_profiler_overlay_text) are declared in the engine's core/index.ts but absent from package.json's nativeLibrary.functions, so gen_stubs can't see them — add hand-written no-op stubs in src/ffi_stubs_manual.rs (perry validates the manifest cross-platform, so the manifest gap is left for a separate change).
Three runtime gaps that kept games from actually rendering on watchOS:
- StringHeader was 16 bytes (4×u32), but Perry 0.5.x's canonical header is 20
bytes (5×u32 incl `flags`, data at +20). `perry_str` read every incoming
string 4 bytes early — text rendered with a 4-null prefix + truncated tail
("BLOOM JUMP" -> "BLOOM") and file paths came back corrupted so levels
never loaded. Add the missing `flags` field.
- bloom_read_file was a stub returning 0 (null); the game's inline `.length`
then dereferenced null and crashed. Implement it: resolve the bundle-relative
path, read the file, and return a real StringHeader via a new
alloc_perry_string (empty string on a miss, never null).
- bloom_begin_mode_2d / bloom_end_mode_2d were no-ops, so world-space draws
(tiles, sprites) rendered at raw coordinates off-screen while the screen-space
HUD showed. Emit BEGIN_2D/END_2D marker commands carrying the camera; the
SwiftUI Canvas applies the matching affine transform (world -> screen) to
every command in between.
Also drop the watchOS `metal_sources` post-fx shader: SCNTechnique/SCNRenderer
is absent from the watchOS SDK so it can't run, and compiling it needs the
separately-downloadable Metal Toolchain. The app renders fine without the
metallib.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings a real 2D game (Bloom Jump) up on the watchOS simulator — title screen and gameplay (player, ground tiles, coins, HUD) rendering through the SwiftUI-
Canvaspath. Five issues stood between "compiles" and "plays":_bloom_add_post_pass,_bloom_set_wind, …gen_stubs.jsthrew onstringparams soffi_stubs.rswent stale — mapstring→i64, add two real impls to OVERRIDES, regenerate_bloom_*bloom_set_material_params/splat_impulse/profiler_*are declared incore/index.tsbut missing from the manifest — hand-written stubs inffi_stubs_manual.rsStringHeaderwas 16 bytes; Perry 0.5.x's is 20 (missingflags).perry_strread every string 4 bytes early. AddflagsSIGSEGVat…fff8)bloom_read_filereturned null; implement it (resolve bundle path, read, return a real StringHeader viaalloc_perry_string, empty-never-null on miss)bloom_begin/end_mode_2dwere no-ops, so world draws used raw coords. Emit BEGIN_2D/END_2D markers; the Canvas applies the camera's affine transformAlso drops the watchOS
metal_sourcespost-fx shader —SCNTechnique/SCNRendereris absent from the watchOS SDK so it can't run, and compiling it requires the separately-downloadable Metal Toolchain. The app renders fine without the metallib.Validation
Built for
watchos-simulator(--features watchos-swift-app) and ran on an Apple Watch Series 11 (watchOS 26) simulator:Notes
package.json'snativeLibrary.functions, because Perry validates that manifest cross-platform and this change was only validated on watchOS. Adding them to the manifest (sogen_stubs.jsemits them) is a reasonable follow-up for someone who can test the other targets.--features watchos-swift-applink reliability (the_main → __perry_user_mainentry-object rename) — submitted to PerryTS/perry separately.