Performance
Measured against react-native-skia, and what keeps a scene cheap.
Benchmark
20,000 shapes, 3,600 images, a 40-octave shader, an image pattern, 900 paths and 6,000 texts against react-native-skia 3.8.1 on a Samsung SM-E146B (90 Hz), release builds, mean of two runs. Present FPS is frames that reached the screen.
| engine · scene | Present FPS | UI FPS | JS FPS |
|---|---|---|---|
| tgfx · shapes | 60.5 | 90 | 90 |
| skia · shapes | 25.8 | 90 | 90 |
| tgfx · images | 90.3 | 90 | 90 |
| skia · images | 66.1 | 90 | 90 |
| tgfx · shader | 15.0 | 90 | 90 |
| skia · shader | 9.3 | 90 | 90 |
| tgfx · pattern | 90.3 | 90 | 90 |
| skia · pattern | 84.8 | 90 | 90 |
| tgfx · paths | 90.3 | 90 | 90 |
| skia · paths | 17.6 | 90 | 90 |
| tgfx · text | 68.3 | 90 | 90 |
| skia · text | 34.4 | 90 | 90 |
Think a number is off? Open an issue.
Prewarm at launch
You can prewarm the GPU device and fonts at launch, so the first <Canvas>
mounts faster.
Android
import com.margelo.nitro.tgfx.Tgfx
override fun onCreate() {
super.onCreate()
Tgfx.prewarm()
loadReactNative(this)
}iOS
Set TgfxPrewarm in Info.plist; no launch code needed.
<key>TgfxPrewarm</key>
<true/>TGFX_PREWARM=1 or 0 in the scheme overrides the key on local runs. To time it
yourself, leave the key out and call prewarm; Swift needs a bridging header.
#import <NitroTgfx/TgfxPrewarm.h>TgfxPrewarm.prewarm()What it buys
Samsung SM-E146B, four cold starts per arm:
| without prewarm | with prewarm | |
|---|---|---|
libNitroTgfx load | 10 ms, on the main thread | 15–19 ms, on the render thread |
| first canvas window | 25.3 / 28.4 / 29.0 / 31.0 ms | 9.6 / 10.9 / 13.5 / 13.5 ms |
The first canvas window drops from 28.4 ms to 11.9 ms on average.
Keep it cheap
-
Animate through
SharedValueprops orNodeHandle.set; changing JSX props re-uploads the scene. -
Keep children primitive; wrapper components take a slower path.
{/* fast */} <Canvas>{shapes.map((s) => <Rect key={s.id} {...s} />)}</Canvas> {/* falls back */} <Canvas>{shapes.map((s) => <Star key={s.id} {...s} />)}</Canvas> -
Group same-kind shapes as siblings.
-
Adjacent
<Path>siblings with the same solid paint draw as one batch. Keep thepathstring stable. -
<Group picture>records a static subtree once. Use<Group layer>only for group opacity or filters. -
Pool handles with
createHandleand togglevisibleinstead of remounting. -
For large sprite or mesh collections, use
useSpriteBufferoruseVertexBuffer. -
One animated opacity per chain (Animation).
-
purgeCachesfrees the image and GPU caches;getCacheInforeports them (Images § Cache).