Skip to content

feat: expose GPU Canvas (3D) on iOS and Android - #381

Open
mfazekas wants to merge 3 commits into
mainfrom
claude/gpu-canvas-beta-android-ios-7ccb40
Open

feat: expose GPU Canvas (3D) on iOS and Android#381
mfazekas wants to merge 3 commits into
mainfrom
claude/gpu-canvas-beta-android-ios-7ccb40

Conversation

@mfazekas

@mfazekas mfazekas commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Exposes Rive's GPU Canvas renderer (required for 3D content) on both platforms of the new runtime as RiveRuntime.setGPUCanvasEnabled(true) / RiveRuntime.isGPUCanvasEnabled(), and bumps the pinned SDKs to rive-ios 6.26.0 and rive-android 11.12.0, the first releases that ship it. The setting is process-wide and follows the same contract as setAndroidRenderBackend: call it before the first Rive file loads; once the shared worker exists a later call logs a warning and is ignored. Both settings now live in RiveWorkerConfig (per source set), replacing RiveRenderBackendConfig.

iOS maps to Worker(configuration: .init(enableGPUCanvas:)). Android maps to rive-android's experimental deferred renderer. Its CommandQueue.createDeferred is Kotlin-internal (only the Compose rememberDeferredRiveWorker() is public), so DeferredRiveWorker.java calls the mangled JVM entry point; Java is not bound by Kotlin visibility. Worth asking upstream to make it public.

The bump also surfaced an upstream bug that predates GPU Canvas: on rive-android 11.10+ a getValueAsync() on a nonexistent boolean path aborts debuggable apps (JNI DETECTED ERROR: unexpected jboolean value). rive-runtime's listViewModelPropertyValue handler reports "property not found" but still sends a value message with the uninitialized union member, and the JNI bridge forwards that byte as a jboolean; 11.9.1 has the same code and only passes by luck of stack contents. Bisected 11.9.1 pass / 11.10.0+ abort, deterministic. Reported as rive-app/rive-android#470. As a mitigation HybridViewModelInstance.hasBooleanProperty resolves the path against ViewModel metadata before the native read: for nested paths it references the parent instance, asks it for its ViewModel name (getViewModelName(), public since 11.12) and checks that ViewModel's property list, so nested and view-obtained instances are covered too; results are cached per path. The harness test now includes nested misses.

Example: demos/GPUCanvasDemo.tsx renders ore.riv (rive-android's GPU Canvas sample, MIT) and must be the first Rive page opened after a cold start. With the flag the three cubes render on both platforms; without it they are black and iOS logs GPUCanvas:beginRenderPass() requires the deferred recorder. No harness test since the flag cannot be toggled per process.

Verified: iOS harness 29/29 suites (209 tests) on 6.26.0; Android harness 29/29 suites (209 tests) on 11.12.0 with the guard (4 suites crashed before it); Android legacy source set compiles. Docs in docs/runtime-backends.md.

🤖 Generated with Claude Code

mfazekas and others added 3 commits September 10, 2026 03:08
Both are the first releases that ship GPU Canvas (rive-ios 6.25.0,
rive-android 11.12.0). rive-android 11.10+ also surfaces an upstream bug:
reading a nonexistent boolean property aborts debuggable apps with
"JNI DETECTED ERROR: unexpected jboolean value" because rive-runtime's
listViewModelPropertyValue handler still sends a value message with an
uninitialized union member after reporting the not-found error. As a
mitigation, boolean reads on instances with known ViewModel metadata are
validated against the property list before touching the native getter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… + Android)

Adds RiveRuntime.setGPUCanvasEnabled()/isGPUCanvasEnabled(). The setting
is process-wide and fixed once the shared render worker exists, like
setAndroidRenderBackend; both now live in RiveWorkerConfig per source set.
iOS maps to Worker(configuration: .init(enableGPUCanvas:)); Android maps to
rive-android's experimental deferred renderer, reached through a Java shim
because CommandQueue.createDeferred is Kotlin-internal. No-op on the legacy
runtime. Adds the ore.riv GPU Canvas demo page and docs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Walks the path via ViewModelInstance.create(Reference(parent)) and
getViewModelName() so nested and view-obtained instances are covered by
the rive-app/rive-android#470 guard too, and caches the result per path.
Adds nested-miss cases to the harness test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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