Cache ruler paints, disable preserveDrawingBuffer outside tests

This commit is contained in:
Danila Poyarkov 2026-03-01 22:16:13 +03:00
parent 5e209eca1c
commit 97f4727e26
3 changed files with 66 additions and 45 deletions

View file

@ -1,6 +1,11 @@
# Changelog
## 0.2.2 (2026-03-01)
## Unreleased
### Performance
- Cache ruler paints instead of allocating per frame
- Only enable `preserveDrawingBuffer` in test mode
### Bug Fixes

View file

@ -132,6 +132,12 @@ export class SkiaRenderer {
private fontProvider: TypefaceFontProvider | null = null
private fontsLoaded = false
private imageCache = new Map<string, CKImage>()
private rulerBgPaint: Paint
private rulerTickPaint: Paint
private rulerTextPaint: Paint
private rulerHlPaint: Paint
private rulerBadgePaint: Paint
private rulerLabelPaint: Paint
panX = 0
panY = 0
@ -201,6 +207,32 @@ export class SkiaRenderer {
this.opacityPaint = new ck.Paint()
this.textFont = new ck.Font(null, DEFAULT_FONT_SIZE)
const bg = RULER_BG_COLOR
this.rulerBgPaint = new ck.Paint()
this.rulerBgPaint.setColor(ck.Color4f(bg.r, bg.g, bg.b, 1))
this.rulerTickPaint = new ck.Paint()
this.rulerTickPaint.setColor(
ck.Color4f(RULER_TICK_COLOR.r, RULER_TICK_COLOR.g, RULER_TICK_COLOR.b, 1)
)
this.rulerTickPaint.setStrokeWidth(1)
this.rulerTickPaint.setAntiAlias(true)
const tc = RULER_TEXT_COLOR
this.rulerTextPaint = new ck.Paint()
this.rulerTextPaint.setColor(ck.Color4f(tc.r, tc.g, tc.b, 1))
this.rulerTextPaint.setAntiAlias(true)
this.rulerHlPaint = new ck.Paint()
this.rulerHlPaint.setAntiAlias(true)
this.rulerBadgePaint = new ck.Paint()
this.rulerBadgePaint.setAntiAlias(true)
this.rulerLabelPaint = new ck.Paint()
this.rulerLabelPaint.setColor(ck.Color4f(1, 1, 1, 1))
this.rulerLabelPaint.setAntiAlias(true)
}
getFontProvider(): TypefaceFontProvider | null {
@ -2084,9 +2116,9 @@ export class SkiaRenderer {
const vh = this.viewportHeight
if (vw === 0 || vh === 0) return
const bg = RULER_BG_COLOR
const bgPaint = new this.ck.Paint()
bgPaint.setColor(this.ck.Color4f(bg.r, bg.g, bg.b, 1))
const bgPaint = this.rulerBgPaint
const tickPaint = this.rulerTickPaint
const textPaint = this.rulerTextPaint
canvas.drawRect(this.ck.LTRBRect(0, 0, vw, R), bgPaint)
canvas.drawRect(this.ck.LTRBRect(0, R, R, vh), bgPaint)
@ -2094,25 +2126,8 @@ export class SkiaRenderer {
// Corner square
canvas.drawRect(this.ck.LTRBRect(0, 0, R, R), bgPaint)
const tickPaint = new this.ck.Paint()
tickPaint.setColor(
this.ck.Color4f(RULER_TICK_COLOR.r, RULER_TICK_COLOR.g, RULER_TICK_COLOR.b, 1)
)
tickPaint.setStrokeWidth(1)
tickPaint.setAntiAlias(true)
const textColor = RULER_TEXT_COLOR
const textPaint = new this.ck.Paint()
textPaint.setColor(this.ck.Color4f(textColor.r, textColor.g, textColor.b, 1))
textPaint.setAntiAlias(true)
const font = this.sizeFont ?? this.textFont
if (!font) {
bgPaint.delete()
tickPaint.delete()
textPaint.delete()
return
}
if (!font) return
const step = this.rulerStep()
const minorStep = step / 5
@ -2205,11 +2220,10 @@ export class SkiaRenderer {
// Selection highlight + badges
if (selNodes.length > 0) {
const hlPaint = new this.ck.Paint()
hlPaint.setColor(this.selColor(RULER_HIGHLIGHT_ALPHA))
this.rulerHlPaint.setColor(this.selColor(RULER_HIGHLIGHT_ALPHA))
canvas.drawRect(this.ck.LTRBRect(Math.max(R, sx1), 0, sx2, R), hlPaint)
canvas.drawRect(this.ck.LTRBRect(0, Math.max(R, sy1), R, sy2), hlPaint)
canvas.drawRect(this.ck.LTRBRect(Math.max(R, sx1), 0, sx2, R), this.rulerHlPaint)
canvas.drawRect(this.ck.LTRBRect(0, Math.max(R, sy1), R, sy2), this.rulerHlPaint)
this.drawRulerBadge(
canvas,
@ -2244,12 +2258,7 @@ export class SkiaRenderer {
'vertical'
)
hlPaint.delete()
}
bgPaint.delete()
tickPaint.delete()
textPaint.delete()
}
private drawRulerBadge(
@ -2267,11 +2276,7 @@ export class SkiaRenderer {
const pad = RULER_BADGE_PADDING
const h = RULER_BADGE_HEIGHT
const badgePaint = new this.ck.Paint()
badgePaint.setColor(this.selColor())
const labelPaint = new this.ck.Paint()
labelPaint.setColor(this.ck.Color4f(1, 1, 1, 1))
labelPaint.setAntiAlias(true)
this.rulerBadgePaint.setColor(this.selColor())
if (axis === 'horizontal') {
const bx = x - (textW + pad * 2) / 2
@ -2282,9 +2287,9 @@ export class SkiaRenderer {
RULER_BADGE_RADIUS,
RULER_BADGE_RADIUS
),
badgePaint
this.rulerBadgePaint
)
canvas.drawText(label, bx + pad, R * RULER_TEXT_BASELINE, labelPaint, font)
canvas.drawText(label, bx + pad, R * RULER_TEXT_BASELINE, this.rulerLabelPaint, font)
} else {
const bw = textW + pad * 2
const bx = (R - h) / 2
@ -2298,14 +2303,11 @@ export class SkiaRenderer {
RULER_BADGE_RADIUS,
RULER_BADGE_RADIUS
),
badgePaint
this.rulerBadgePaint
)
canvas.drawText(label, -bw / 2 + pad, h / 2 - 3, labelPaint, font)
canvas.drawText(label, -bw / 2 + pad, h / 2 - 3, this.rulerLabelPaint, font)
canvas.restore()
}
badgePaint.delete()
labelPaint.delete()
}
private rulerStep(): number {
@ -2339,6 +2341,12 @@ export class SkiaRenderer {
this.sizeFont?.delete()
this.fontMgr?.delete()
this.fontProvider?.delete()
this.rulerBgPaint.delete()
this.rulerTickPaint.delete()
this.rulerTextPaint.delete()
this.rulerHlPaint.delete()
this.rulerBadgePaint.delete()
this.rulerLabelPaint.delete()
this.surface.delete()
}
}

View file

@ -41,7 +41,12 @@ export function useCanvas(canvasRef: Ref<HTMLCanvasElement | null>, store: Edito
canvas.width = w * dpr
canvas.height = h * dpr
const surface = ck.MakeWebGLCanvasSurface(canvas, undefined, { preserveDrawingBuffer: 1 })
const isTest = new URLSearchParams(window.location.search).has('test')
const surface = ck.MakeWebGLCanvasSurface(
canvas,
undefined,
isTest ? { preserveDrawingBuffer: 1 } : undefined
)
if (!surface) {
console.error('Failed to create WebGL surface')
return
@ -54,6 +59,9 @@ export function useCanvas(canvasRef: Ref<HTMLCanvasElement | null>, store: Edito
canvas.dataset.ready = '1'
}
const params = new URLSearchParams(window.location.search)
const showRulers = !params.has('no-rulers')
let rafId = 0
function renderNow() {
@ -64,7 +72,7 @@ export function useCanvas(canvasRef: Ref<HTMLCanvasElement | null>, store: Edito
renderer.zoom = store.state.zoom
renderer.viewportWidth = canvasRef.value?.clientWidth ?? 0
renderer.viewportHeight = canvasRef.value?.clientHeight ?? 0
renderer.showRulers = !new URLSearchParams(window.location.search).has('no-rulers')
renderer.showRulers = showRulers
renderer.pageColor = store.state.pageColor
renderer.pageId = store.state.currentPageId
renderer.render(store.graph, store.state.selectedIds, {