iOS: asset-path + device-limit fixes, touch-slot API, target docs#89
Merged
Conversation
Every asset-loading FFI routes its path through the platform's resolve_path() hook, which on iOS prepends the app bundle's resource path — relative paths do not resolve against the working directory there, because the CWD is not the bundle. compile_material_from_file was the one loader that didn't, passing the raw relative path straight to the filesystem. That works on the desktop hosts, where the CWD happens to be the asset root, and fails on device. The symptom is a scene that renders with every from-file material silently dropped — in the shooter that meant no water at all, and terrain/glass/muzzle-flash falling back to a default — with nothing but a `canonicalize "assets/materials/water.wgsl": No such file` line on the console to explain it. FFIs that *write* a file (take_screenshot, dump_shadow_map) keep the raw path deliberately: the bundle is read-only, and their output belongs outside it.
Touch points are addressed by slot, but getTouchCount() returns the
number of *live* fingers — and the two spaces diverge the moment a
finger lifts out of order. Hold two fingers, lift the first, and the
live finger sits at slot 1 while the count reads 1.
The obvious loop, `for (i = 0; i < getTouchCount(); i++)`, therefore
reads slot 0 — released, but still carrying the coordinates it had when
it left the glass — as though it were a live touch. It presents as a
finger frozen in place: a virtual stick that stays deflected and keeps
walking the player after the thumb is long gone. src/mobile's
VirtualJoystick has this shape today, and games that hand-roll their
touch input (jump, garden) inherited it.
There was no way to tell from TypeScript which slots were live —
getTouchPointCount() just aliases getTouchCount(). Expose the two
primitives that make a correct scan possible:
for (let i = 0; i < getMaxTouchPoints(); i++) {
if (!isTouchActive(i)) continue;
...
}
Added to the shared define_core_ffi! macro, so every platform on it
picks them up; watchOS and web hand-roll their FFI and get explicit
implementations. FFI parity is green on all seven.
Jolt is C++, and bloom-ios is a staticlib — so Perry, not cargo, performs the final link, and it takes the libraries to link from `libs` in the manifest. macOS, tvOS and visionOS all declare `c++` there for exactly this reason; iOS was the one Apple target that didn't, leaving any physics-using game to fail the link on undefined std::__1 symbols.
… defaults
A user material's fragment stage binds 19 sampled textures once the
scene/GI inputs sit alongside the material's own maps. wgpu's default
max_sampled_textures_per_shader_stage is 16.
Device creation asked for Limits::default() and only fell back to the
adapter's real limits when request_device *failed*. On macOS it doesn't
fail — the device is created happily at 16 — so the shortfall surfaced
later and further away, as an abort inside
create_pipeline_layout('user_material'):
Too many bindings of type SampledTextures in Stage FRAGMENT,
limit is 16, count was 19
which took the shooter down on every macOS launch, before a frame.
iOS was only ever passing by luck: its first request_device fails on an
unrelated limit (max_inter_stage_shader_variables, 15 vs a requested 16),
so it always retried with adapter.limits() and picked up the texture
headroom as a side effect.
Ask the adapter for what it actually offers on the limits the material
system depends on, never dropping below wgpu's defaults.
iOS is the engine's only App Store platform and had no target doc, while watchOS and web both do. The only build instruction anywhere in the repo was the text of a panic! string in native/ios/src/lib.rs. Covers the ios-game-loop requirement and the UIApplicationMain handshake, asset bundling and why every read path must go through resolve_path(), the touch-slot contract, and the known gaps (no CI build, no gamepad, EN-024).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughThe change documents the iOS target, resolves iOS asset paths, expands touch-slot APIs across native and TypeScript layers, updates iOS linking, and aligns renderer limits with adapter capabilities. ChangesiOS and touch support
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant TouchAPI as TypeScript Touch API
participant NativeFFI as bloom_is_touch_active
participant InputState
TouchAPI->>NativeFFI: query touch index
NativeFFI->>InputState: is_touch_active(index)
InputState-->>NativeFFI: active boolean
NativeFFI-->>TouchAPI: 1.0 or 0.0
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Everything needed to bring a full 3D game up on a physical iPhone. Found by porting the shooter to iOS; it now runs on device at ~50 fps, and these are the engine-side blockers that were in the way.
Three of the five are real bugs, one of which was taking the shooter down on macOS as well.
fix(ios): resolve the asset path incompile_material_from_fileEvery asset loader routes its path through the platform's
resolve_path()hook — on iOS the CWD is not the app bundle, so a relative path resolves against nothing.compile_material_from_filewas the single loader that didn't, and passed the raw path straight to the filesystem.That works on the desktop hosts, where the CWD happens to be the asset root, and fails on device. The symptom is a scene that renders with every from-file material silently dropped — in the shooter, no water at all — with one
canonicalize "assets/materials/water.wgsl": No such fileline to explain it.FFIs that write a file (
take_screenshot,dump_shadow_map) keep the raw path deliberately: the bundle is read-only.fix(render): request the adapter's sampled-texture limits, not wgpu's defaultsA user material's fragment stage binds 19 sampled textures once the scene/GI inputs sit alongside the material's own maps. wgpu's default cap is 16.
Device creation asked for
Limits::default()and only fell back to the adapter's real limits whenrequest_devicefailed. On macOS it doesn't fail — the device is created happily at 16 — so the shortfall surfaced later and further away, as an abort insidecreate_pipeline_layout('user_material'):The shooter could not open on macOS at all; this is not iOS-specific and reproduces on a clean
main.iOS was only ever passing by luck: its first
request_devicefails on an unrelated limit (max_inter_stage_shader_variables, 15 vs a requested 16), so it always retried withadapter.limits()and picked up the texture headroom as a side effect.fix(ios): declare thec++runtime in the native-library manifestJolt is C++ and
bloom-iosis a staticlib, so Perry performs the final link and readslibsfrom the manifest. macOS, tvOS and visionOS all declarec++; iOS was the one Apple target that didn't, so any physics-using game failed the link on undefinedstd::__1symbols.feat(input): addisTouchActive/getMaxTouchPointsTouch points are addressed by slot, but
getTouchCount()returns the number of live fingers — and the two diverge the moment a finger lifts out of order. Hold two fingers, lift the first, and the live finger sits at slot 1 while the count reads 1.So the obvious loop:
reads slot 0 — released, but still carrying the coordinates it had when it left the glass — as though it were live. It presents as a finger frozen in place: a virtual stick that stays deflected and keeps walking the player after the thumb is gone.
src/mobile'sVirtualJoystickhas this shape today, and the games that hand-roll their touch input inherited it.There was no way to tell from TypeScript which slots were live (
getTouchPointCount()just aliasesgetTouchCount()). The correct scan is now expressible:Added to the shared
define_core_ffi!macro so every platform on it picks them up; watchOS and web hand-roll their FFI and get explicit implementations.docs: adddocs/ios-target.mdiOS is the engine's only App Store platform and had no target doc, while watchOS and web both do — the only build instruction anywhere in the repo was the text of a
panic!string. Covers the mandatoryios-game-loopfeature and theUIApplicationMainhandshake, asset bundling and the resolve-path contract, the touch-slot rule above, and the known gaps (no CI build for iOS, no gamepad, EN-024).Testing
tools/validate-ffi.js: 0 failures, 0 warnings across all 7 platforms (415 manifest functions).Summary by CodeRabbit
New Features
Bug Fixes