diff --git a/canvas-context.ts b/canvas-context.ts index a2accdb..0cb93f3 100644 --- a/canvas-context.ts +++ b/canvas-context.ts @@ -149,6 +149,10 @@ export class CanvasContext { this.setViewportScope({ physicalBounds }, cb) } + setViewport(physicalBounds: Rect, cb: (context: regl.Context) => void) { + this.setViewportScope({ physicalBounds }, cb) + } + getMaxTextureSize() { return this.gl.limits.maxTextureSize } diff --git a/flamechart-renderer.ts b/flamechart-renderer.ts index 6fe584b..1f39266 100644 --- a/flamechart-renderer.ts +++ b/flamechart-renderer.ts @@ -4,14 +4,16 @@ import { RectangleBatch } from './rectangle-batch-renderer' import { CanvasContext } from './canvas-context'; import { Vec2, Rect, AffineTransform } from './math' import { LRUCache } from './lru-cache' +import { Color } from './color' const MAX_BATCH_SIZE = 10000 class RowAtlas { - private texture: regl.Texture + texture: regl.Texture private framebuffer: regl.Framebuffer private renderToFramebuffer: regl.Command<{}> private rowCache: LRUCache + private clearLineBatch: RectangleBatch constructor(private canvasContext: CanvasContext) { this.texture = canvasContext.gl.texture({ @@ -25,6 +27,8 @@ class RowAtlas { this.renderToFramebuffer = canvasContext.gl({ framebuffer: this.framebuffer }) + this.clearLineBatch = canvasContext.createRectangleBatch() + this.clearLineBatch.addRect(Rect.unit, new Color(1, 1, 1, 1)) } has(key: K) { return this.rowCache.has(key) } @@ -53,20 +57,23 @@ class RowAtlas { let row = this.rowCache.get(key) if (row != null) { // Already cached! - return + continue } - // Not cached -- we'll have to actually render row = this.allocateLine(key) + const textureRect = new Rect( new Vec2(0, row), new Vec2(this.texture.width, 1) ) - + this.canvasContext.drawRectangleBatch({ + batch: this.clearLineBatch, + configSpaceSrcRect: Rect.unit, + physicalSpaceDstRect: textureRect + }) render(textureRect, key) } }) - return } renderViaAtlas(key: K, dstRect: Rect): boolean { @@ -165,7 +172,7 @@ export class FlamechartRenderer { private root: RangeTreeNode private rowAtlas: RowAtlas - constructor(private canvasContext: CanvasContext, flamechart: Flamechart) { + constructor(private canvasContext: CanvasContext, private flamechart: Flamechart) { const nLayers = flamechart.getLayers().length this.rowAtlas = new RowAtlas(canvasContext) @@ -173,7 +180,7 @@ export class FlamechartRenderer { for (let stackDepth = 0; stackDepth < nLayers; stackDepth++) { const leafNodes: RangeTreeLeafNode[] = [] - const y = stackDepth + 1 + const y = stackDepth let minLeft = Infinity let maxRight = -Infinity @@ -229,9 +236,7 @@ export class FlamechartRenderer { // which is even more expensive than not using a cache at all! Instead, // we'll cache the first 2 entries in that case, and re-use that cache each time, // while rendering the final 2 items without use of the cache. - // An exception here is if the node is already in the cache! - let useCache = renderedBatchCount++ < cacheCapacity || this.rowAtlas.has(leaf) - + let useCache = renderedBatchCount++ < cacheCapacity if (useCache) { cachedLeaves.push(leaf) } else { @@ -240,17 +245,24 @@ export class FlamechartRenderer { }) this.rowAtlas.writeToAtlasIfNeeded(cachedLeaves, (textureDstRect, leaf) => { + const configSpaceBounds = new Rect( + new Vec2(0, leaf.getBounds().top()), + new Vec2(this.flamechart.getTotalWeight(), 1) + ) this.canvasContext.drawRectangleBatch({ batch: leaf.getBatch(), - configSpaceSrcRect: leaf.getBounds(), + configSpaceSrcRect: configSpaceBounds, physicalSpaceDstRect: textureDstRect }) }) const configToPhysical = AffineTransform.betweenRects(configSpaceSrcRect, physicalSpaceDstRect) for (let leaf of cachedLeaves) { - const configSpaceLeafBounds = leaf.getBounds() - const physicalLeafBounds = configToPhysical.transformRect(configSpaceLeafBounds) + const configSpaceBounds = new Rect( + new Vec2(0, leaf.getBounds().top()), + new Vec2(this.flamechart.getTotalWeight(), 1) + ) + const physicalLeafBounds = configToPhysical.transformRect(configSpaceBounds) if (!this.rowAtlas.renderViaAtlas(leaf, physicalLeafBounds)) { console.error('Failed to render from cache') } @@ -263,5 +275,14 @@ export class FlamechartRenderer { physicalSpaceDstRect }) } + + // Overlay the atlas on top of the canvas for debugging + /* + this.canvasContext.drawTexture({ + texture: this.rowAtlas.texture, + srcRect: new Rect(Vec2.zero, new Vec2(this.rowAtlas.texture.width, this.rowAtlas.texture.height)), + dstRect: new Rect(Vec2.zero, new Vec2(800, 800)) + }) + */ } } \ No newline at end of file diff --git a/flamechart-view.tsx b/flamechart-view.tsx index 2517da8..c3f2453 100644 --- a/flamechart-view.tsx +++ b/flamechart-view.tsx @@ -195,7 +195,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { const width = frame.end - frame.start const configSpaceBounds = new Rect( - new Vec2(frame.start, depth + 1), + new Vec2(frame.start, depth), new Vec2(width, 1) ) @@ -281,7 +281,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { + this.props.canvasContext.renderInto(this.container, (context) => { this.props.flamechartRenderer.render({ physicalSpaceDstRect: new Rect(Vec2.zero, this.physicalViewSize()), configSpaceSrcRect: this.props.configSpaceViewportRect @@ -430,7 +430,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { const width = frame.end - frame.start const configSpaceBounds = new Rect( - new Vec2(frame.start, depth + 1), + new Vec2(frame.start, depth), new Vec2(width, 1) ) if (configSpaceMouse.x < configSpaceBounds.left()) return null @@ -585,11 +585,13 @@ export class FlamechartView extends ReloadableComponent { const configSpaceOriginBounds = new Rect( - new Vec2(0, 0), + new Vec2(0, -1), Vec2.max(new Vec2(0, 0), this.configSpaceSize().minus(viewportRect.size)) ) diff --git a/lru-cache.ts b/lru-cache.ts index 7fad10a..2869607 100644 --- a/lru-cache.ts +++ b/lru-cache.ts @@ -83,8 +83,8 @@ export class List { node.prev.next = node.next node.next = null node.prev = null + this.size-- } - this.size-- } } diff --git a/rectangle-batch-renderer.ts b/rectangle-batch-renderer.ts index fc5ae1d..4608f3f 100644 --- a/rectangle-batch-renderer.ts +++ b/rectangle-batch-renderer.ts @@ -157,9 +157,8 @@ export class RectangleBatchRenderer { const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight) - const physicalToNDC = AffineTransform - .withTranslation(new Vec2(-1, 1)) - .withScale(new Vec2(2, -2).dividedByPointwise(viewportSize)) + const physicalToNDC = AffineTransform.withTranslation(new Vec2(-1, 1)) + .times(AffineTransform.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize))) return physicalToNDC.times(configToPhysical).flatten() } diff --git a/sample/tall.txt b/sample/tall.txt new file mode 100644 index 0000000..9452c00 --- /dev/null +++ b/sample/tall.txt @@ -0,0 +1 @@ +a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z 1 \ No newline at end of file