Shopify / Shopify/react-native-skia
Android 17: TextureView presents only a fraction of rendered frames (updateTexImage drain from #1980 no longer works); SurfaceView opt-in proposal
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 8.6k
- Forks
- 647
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 35
Description
Description
On Android 17 the default SkiaTextureView path presents only a fraction of the frames Skia renders, so Reanimated-driven canvases look like a low-fps stepping animation. The same build is smooth on iOS and was smooth on the same device on Android 16.
Cause: the per-frame SurfaceTexture.updateTexImage() drain added in #1980 (the fix for #1960) no longer drains on Android 17. On Android ≤16 it fails after acquiring/releasing the pending buffer (updateAndRelease: EGLConsumer is not attached, #2054), so it still kept the TextureView in step by accident. On 17 it returns early:
E <app>: [SurfaceTexture-0-<pid>-5] updateTexImage: LegacySurfaceTexture is not attached!
I RNSkia : updateAndRelease() failed. The exception above can safely be ignored
Evidence (Pixel 10, Android 17 build CP2A.260805.005, app render rate 60 Hz):
RNSkialog above fires every ~16.6 ms → Skia renders 60 fps.dumpsys gfxinfo <pkg> framestats: HWUI presents the TextureView content in RenderThread-only frames (flag 2) at ~19–23/s, interleaved with main-thread frames every 2 vsyncs; ~44 HWUI frames/s total, intervals alternating 16.6/33.3 ms. GPU time 1 ms p99, so it is not GPU load.- Switching the same canvas to
SkiaSurfaceViewpresents every frame and the animation is smooth (verified on the device).
React Native Skia Version
2.11.2
React Native Version
0.87.1 (react-native-reanimated 4.6.0, react-native-worklets 0.12.1)
Using New Architecture
- Enabled
Steps to Reproduce
- Pixel 10 on Android 17.
<Canvas>with a<Path>whosepathis auseDerivedValuedriven bywithSpringshared values (any continuously animated Skia prop works; ours is a 4-corner polygon overlaying a camera preview).- Retarget the springs continuously (or just let one settle) and watch the motion; enable Developer options → "Show refresh rate" to confirm the display is not the limit.
adb logcat | grep -E "RNSkia|SurfaceTexture"andadb shell dumpsys gfxinfo <pkg> framestatswhile it animates.
Snack, Code Example, Screenshot, or Link to Repository
const x = useSharedValue(0)
useEffect(() => { x.value = withRepeat(withSpring(300), -1, true) }, [])
const path = useDerivedValue(() => {
const p = Skia.Path.Make()
p.addRect({ x: x.value, y: 100, width: 120, height: 160 })
return p
})
<Canvas style={StyleSheet.absoluteFill}>
<Path path={path} color="lime" style="stroke" strokeWidth={4} />
</Canvas>
Fix we are shipping (patched locally, tested working on a real Pixel 10): use SkiaSurfaceView, made usable as an overlay:
// SkiaSurfaceView constructor
getHolder().setFormat(PixelFormat.TRANSLUCENT); // EGL config already has alpha 8; renderer clears to transparent
setZOrderMediaOverlay(true); // above sibling SurfaceViews (camera preview), below the window's views
Today the only way to reach SkiaSurfaceView is opaque, and it is opaque black. Proposal: an opt-in androidSurfaceView prop on Canvas that selects the SurfaceView and applies the translucent format + media-overlay z-order when opaque is false, leaving opaque unchanged. Trade-offs to document: the canvas composites below the window's own views, and usual SurfaceView limitations during view transitions.
Should we submit a PR with this implementation?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the SkiaSurfaceView constructor and the Canvas handling for the proposed androidSurfaceView and existing opaque options; compare this with the current TextureView and updateTexImage path. Confirm the Pixel 10 Android 17 reproduction, then verify that the opt-in SurfaceView works as a translucent media overlay, opaque behavior is unchanged, and the documented SurfaceView limitations are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, java, react-native, typescript
- Domain
- computer-graphics, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100