Skip to content

Commit

Permalink
make cache happy (#7)
Browse files Browse the repository at this point in the history
* feat: reset should cleanup loc

* do some optimize

* feat: support cache

* feat: make clone fast
  • Loading branch information
nonzzz authored Nov 26, 2024
1 parent 0b5d19c commit 1dbc91a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 41 deletions.
18 changes: 9 additions & 9 deletions src/etoile/graph/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,25 @@ export class Box extends C {
clone() {
const box = new Box()
if (this.elements.length) {
const traverse = (elements: Display[], parent: Box) => {
const els: Display[] = []
const stack: { elements: Display[]; parent: Box }[] = [{ elements: this.elements, parent: box }]

while (stack.length > 0) {
const { elements, parent } = stack.pop()!
const cap = elements.length
for (let i = 0; i < cap; i++) {
const element = elements[i]
if (asserts.isBox(element)) {
const box = new Box()
box.parent = parent
box.add(...traverse(element.elements, box))
els.push(box)
const newBox = new Box()
newBox.parent = parent
parent.add(newBox)
stack.push({ elements: element.elements, parent: newBox })
} else if (asserts.isGraph(element)) {
const el = element.clone()
el.parent = parent
els.push(el)
parent.add(el)
}
}
return els
}
box.add(...traverse(this.elements, box))
}
return box
}
Expand Down
43 changes: 24 additions & 19 deletions src/etoile/graph/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,47 +81,52 @@ type Mod<
> = T[K] extends (...args: any) => any ? [K, Parameters<T[K]>] : never

interface Instruction extends InstructionAssignMappings, InstructionWithFunctionCall {
mods: Mod[]
mods: Array<{ mod: Mod; type: number }>
}

const ASSIGN_MAPPINGS = {
fillStyle: !0,
strokeStyle: !0,
font: !0,
lineWidth: !0,
textAlign: !0,
textBaseline: !0
}
fillStyle: 0o1,
strokeStyle: 0o2,
font: 0o4,
lineWidth: 0o10,
textAlign: 0o20,
textBaseline: 0o40
} as const

export const ASSIGN_MAPPINGS_MODE = ASSIGN_MAPPINGS.fillStyle | ASSIGN_MAPPINGS.strokeStyle | ASSIGN_MAPPINGS.font |
ASSIGN_MAPPINGS.lineWidth | ASSIGN_MAPPINGS.textAlign | ASSIGN_MAPPINGS.textBaseline

export const CALL_MAPPINGS_MODE = 0o0

function createInstruction() {
return <Instruction> {
mods: [],
fillStyle(...args) {
this.mods.push(['fillStyle', args])
this.mods.push({ mod: ['fillStyle', args], type: ASSIGN_MAPPINGS.fillStyle })
},
fillRect(...args) {
this.mods.push(['fillRect', args])
this.mods.push({ mod: ['fillRect', args], type: CALL_MAPPINGS_MODE })
},
strokeStyle(...args) {
this.mods.push(['strokeStyle', args])
this.mods.push({ mod: ['strokeStyle', args], type: ASSIGN_MAPPINGS.strokeStyle })
},
lineWidth(...args) {
this.mods.push(['lineWidth', args])
this.mods.push({ mod: ['lineWidth', args], type: ASSIGN_MAPPINGS.lineWidth })
},
strokeRect(...args) {
this.mods.push(['strokeRect', args])
this.mods.push({ mod: ['strokeRect', args], type: CALL_MAPPINGS_MODE })
},
fillText(...args) {
this.mods.push(['fillText', args])
this.mods.push({ mod: ['fillText', args], type: CALL_MAPPINGS_MODE })
},
font(...args) {
this.mods.push(['font', args])
this.mods.push({ mod: ['font', args], type: ASSIGN_MAPPINGS.font })
},
textBaseline(...args) {
this.mods.push(['textBaseline', args])
this.mods.push({ mod: ['textBaseline', args], type: ASSIGN_MAPPINGS.textBaseline })
},
textAlign(...args) {
this.mods.push(['textAlign', args])
this.mods.push({ mod: ['textAlign', args], type: ASSIGN_MAPPINGS.textAlign })
}
}
}
Expand Down Expand Up @@ -168,9 +173,9 @@ export abstract class Graph extends S {
const cap = this.instruction.mods.length

for (let i = 0; i < cap; i++) {
const mod = this.instruction.mods[i]
const { mod, type } = this.instruction.mods[i]
const [direct, ...args] = mod
if (direct in ASSIGN_MAPPINGS) {
if (type & ASSIGN_MAPPINGS_MODE) {
// @ts-expect-error
ctx[direct] = args[0]
continue
Expand Down
7 changes: 6 additions & 1 deletion src/etoile/graph/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,15 @@ export class Layer extends C implements S {
writeBoundingRectForCanvas(this.c.c.canvas, options.width || 0, options.height || 0, options.devicePixelRatio || 1)
}

setCacheSnapshot(c: HTMLCanvasElement) {
cleanCacheSnapshot() {
const dpr = this.options.devicePixelRatio || 1
const matrix = this.matrix.create({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 })
this.ctx.clearRect(0, 0, this.options.width, this.options.height)
return { dpr, matrix }
}

setCacheSnapshot(c: HTMLCanvasElement) {
const { matrix, dpr } = this.cleanCacheSnapshot()
matrix.transform(this.x, this.y, this.scaleX, this.scaleY, this.rotation, this.skewX, this.skewY)
applyCanvasTransform(this.ctx, matrix, dpr)
this.ctx.drawImage(c, 0, 0, this.options.width / dpr, this.options.height / dpr)
Expand Down
3 changes: 3 additions & 0 deletions src/primitives/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ export class TreemapLayout extends etoile.Schedule<InternalEventDefinition> {
for (const node of this.layoutNodes) {
this.drawBackgroundNode(node)
}
} else {
// Unlike foreground layer, background laer don't need clone so we should reset the loc informaton
this.bgLayer.initLoc()
}
if (!this.fgBox.elements.length || refresh) {
this.render.ctx.textBaseline = 'middle'
Expand Down
38 changes: 26 additions & 12 deletions src/primitives/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { createFillBlock, mixin } from '../shared'
import { Display, S } from '../etoile/graph/display'
import { Schedule, Event as _Event, easing, etoile } from '../etoile'
import { Schedule, Event as _Event, asserts, drawGraphIntoCanvas, easing, etoile } from '../etoile'
import type { BindThisParameter } from '../etoile'
import type { ColorDecoratorResultRGB } from '../etoile/native/runtime'
import type { InheritedCollections } from '../shared'
Expand Down Expand Up @@ -251,7 +251,6 @@ export class SelfEvent extends RegisterModule {
refreshBackgroundLayer(this)
}
this.treemap.reset()
stackMatrixTransform(this.treemap.backgroundLayer, 0, 0, 0)
stackMatrixTransformWithGraphAndLayer(this.treemap.elements, this.self.translateX, this.self.translateY, this.self.scaleRatio)
this.treemap.update()
}
Expand Down Expand Up @@ -302,7 +301,6 @@ export class SelfEvent extends RegisterModule {
treemap.reset()
this.self.highlight.reset()
this.self.highlight.setDisplayLayerForHighlight()
stackMatrixTransform(this.treemap.backgroundLayer, 0, 0, 0)
const factor = absWheelDelta > 3 ? 1.4 : absWheelDelta > 1 ? 1.2 : 1.1
const delta = wheelDelta > 0 ? factor : 1 / factor
self.scaleRatio *= delta
Expand Down Expand Up @@ -431,6 +429,11 @@ function onZoom(ctx: SelfEventContenxt, node: LayoutModule, root: LayoutModule |
const c = treemap.render.canvas
const boundingClientRect = c.getBoundingClientRect()
const [w, h] = estimateZoomingArea(node, root, boundingClientRect.width, boundingClientRect.height)
if (self.layoutHeight !== w || self.layoutHeight !== h) {
// remove font caches
delete treemap.fontsCaches[node.node.id]
delete treemap.ellispsisWidthCache[node.node.id]
}
resetLayout(treemap, w, h)
const module = findRelativeNodeById(node.node.id, treemap.layoutNodes)
if (module) {
Expand All @@ -443,11 +446,7 @@ function onZoom(ctx: SelfEventContenxt, node: LayoutModule, root: LayoutModule |
const initialTranslateY = self.translateY
const startTime = Date.now()
const animationDuration = 300
if (self.layoutHeight !== w || self.layoutHeight !== h) {
// remove font caches
delete treemap.fontsCaches[module.node.id]
delete treemap.ellispsisWidthCache[module.node.id]
}

const { run, stop } = createEffectScope()
run(() => {
const elapsed = Date.now() - startTime
Expand Down Expand Up @@ -524,9 +523,24 @@ function createHighlight(): HighlightContext {
}
}

// TODO: cache the background layer
function refreshBackgroundLayer(c: SelfEventContenxt) {
const { treemap } = c
const { backgroundLayer } = treemap
function refreshBackgroundLayer(c: SelfEventContenxt): boolean | void {
const { treemap, self } = c
const { backgroundLayer, render } = treemap
const { canvas, ctx, options: { width: ow, height: oh } } = render
const { layoutWidth: sw, layoutHeight: sh, scaleRatio: ss } = self

const capture = sw * ss >= ow || sh * ss >= oh
backgroundLayer.__refresh__ = false
if (!capture) {
resetLayout(treemap, sw * ss, sh * ss)
render.clear(ow, oh)
const { dpr } = backgroundLayer.cleanCacheSnapshot()
drawGraphIntoCanvas(backgroundLayer, { c: canvas, ctx, dpr }, (opts, graph) => {
if (asserts.isLayer(graph) && !graph.__refresh__) {
graph.setCacheSnapshot(opts.c)
}
})
self.triggerZoom = false
return true
}
}

0 comments on commit 1dbc91a

Please sign in to comment.