Rendering
Performance
One instanced draw call per SilkGraphics and about 12 ms of CPU to rebuild 50,000 primitives, plus tips for static layers, gradients and fill rate.
Updated pixi-silk 0.1.0
Live demo
Source src/demos/stress.demo.ts
import { Text } from 'pixi.js';
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
// Everything is rebuilt every frame: one SilkGraphics, one draw call, however many primitives.
export default defineDemo({
size: [480, 260],
controls: {
count: { type: 'range', label: 'primitives', min: 500, max: 30000, step: 500, value: 5000 },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
const stats = new Text({
text: '',
style: { fill: 0xffffff, fontSize: 12, fontFamily: 'ui-monospace, monospace' },
});
stats.position.set(12, 10);
stage.addChild(g, stats);
let cpu = 0;
tick((t) => {
const start = performance.now();
g.clear();
for (let i = 0; i < params.count; i++) {
const a = i * 2.399963 + t * 0.2;
const r = Math.sqrt(i / params.count) * 120;
const x = 240 + Math.cos(a) * r * 1.8;
const y = 130 + Math.sin(a) * r;
if (i % 3 === 0) g.circle(x, y, 2.2).fill(0xff375f);
else if (i % 3 === 1) g.roundRect(x - 2, y - 2, 4, 4, 1).fill(0x0a84ff);
else g.line(x, y, x + 5, y + 2).stroke({ width: 1, color: 0x30d158 });
}
cpu = cpu * 0.9 + (performance.now() - start) * 0.1;
stats.text = `${params.count} primitives, 1 draw call, rebuilt in ${cpu.toFixed(1)} ms`;
});
},
});The cost model#
- Each
SilkGraphicsis one draw call, whatever it contains. - The CPU writes 40 floats per primitive into a reusable buffer. Rebuilding 50,000 primitives every frame takes about 12 ms on a laptop. Typical UIs (hundreds of primitives) cost well under 0.5 ms.
- The GPU shades each primitive as one quad sized to its bounds plus a one-pixel margin, once per covered pixel. Cost scales with covered area, like any 2D renderer. Large blurs grow the quad by 3σ.
- Buffers grow to the largest frame and stay in memory. Gradients share rows in one 256x256 half-float atlas.
Tips#
- Split static and dynamic parts. Draw backgrounds and label shapes once, in one object. Redraw only the animated parts every frame.
- Move objects instead of rebuilding them. Animating
position,rotation,scaleoralphaof aSilkGraphicschanges one matrix. - Reuse gradients. Create them at module scope. Identical ramps share an atlas row anyway, but reusing the object avoids re-parsing stops.
- Group primitives that share a layer into one
SilkGraphicsto save draw calls. - Cap the resolution on large canvases:
createSilkApp({ maxResolution: 2 }). - Stop redrawing when idle. Check
Spring.settled, or redraw only on data changes.
API reference: SilkGraphics