mirror of
https://github.com/jlfwong/speedscope.git
synced 2024-11-25 20:51:51 +03:00
Row atlas seems like it is working!
This commit is contained in:
parent
4fcd40da98
commit
31829c4aa0
@ -149,6 +149,10 @@ export class CanvasContext {
|
|||||||
this.setViewportScope({ physicalBounds }, cb)
|
this.setViewportScope({ physicalBounds }, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setViewport(physicalBounds: Rect, cb: (context: regl.Context) => void) {
|
||||||
|
this.setViewportScope({ physicalBounds }, cb)
|
||||||
|
}
|
||||||
|
|
||||||
getMaxTextureSize() {
|
getMaxTextureSize() {
|
||||||
return this.gl.limits.maxTextureSize
|
return this.gl.limits.maxTextureSize
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,16 @@ import { RectangleBatch } from './rectangle-batch-renderer'
|
|||||||
import { CanvasContext } from './canvas-context';
|
import { CanvasContext } from './canvas-context';
|
||||||
import { Vec2, Rect, AffineTransform } from './math'
|
import { Vec2, Rect, AffineTransform } from './math'
|
||||||
import { LRUCache } from './lru-cache'
|
import { LRUCache } from './lru-cache'
|
||||||
|
import { Color } from './color'
|
||||||
|
|
||||||
const MAX_BATCH_SIZE = 10000
|
const MAX_BATCH_SIZE = 10000
|
||||||
|
|
||||||
class RowAtlas<K> {
|
class RowAtlas<K> {
|
||||||
private texture: regl.Texture
|
texture: regl.Texture
|
||||||
private framebuffer: regl.Framebuffer
|
private framebuffer: regl.Framebuffer
|
||||||
private renderToFramebuffer: regl.Command<{}>
|
private renderToFramebuffer: regl.Command<{}>
|
||||||
private rowCache: LRUCache<K, number>
|
private rowCache: LRUCache<K, number>
|
||||||
|
private clearLineBatch: RectangleBatch
|
||||||
|
|
||||||
constructor(private canvasContext: CanvasContext) {
|
constructor(private canvasContext: CanvasContext) {
|
||||||
this.texture = canvasContext.gl.texture({
|
this.texture = canvasContext.gl.texture({
|
||||||
@ -25,6 +27,8 @@ class RowAtlas<K> {
|
|||||||
this.renderToFramebuffer = canvasContext.gl({
|
this.renderToFramebuffer = canvasContext.gl({
|
||||||
framebuffer: this.framebuffer
|
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) }
|
has(key: K) { return this.rowCache.has(key) }
|
||||||
@ -53,20 +57,23 @@ class RowAtlas<K> {
|
|||||||
let row = this.rowCache.get(key)
|
let row = this.rowCache.get(key)
|
||||||
if (row != null) {
|
if (row != null) {
|
||||||
// Already cached!
|
// Already cached!
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not cached -- we'll have to actually render
|
// Not cached -- we'll have to actually render
|
||||||
row = this.allocateLine(key)
|
row = this.allocateLine(key)
|
||||||
|
|
||||||
const textureRect = new Rect(
|
const textureRect = new Rect(
|
||||||
new Vec2(0, row),
|
new Vec2(0, row),
|
||||||
new Vec2(this.texture.width, 1)
|
new Vec2(this.texture.width, 1)
|
||||||
)
|
)
|
||||||
|
this.canvasContext.drawRectangleBatch({
|
||||||
|
batch: this.clearLineBatch,
|
||||||
|
configSpaceSrcRect: Rect.unit,
|
||||||
|
physicalSpaceDstRect: textureRect
|
||||||
|
})
|
||||||
render(textureRect, key)
|
render(textureRect, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderViaAtlas(key: K, dstRect: Rect): boolean {
|
renderViaAtlas(key: K, dstRect: Rect): boolean {
|
||||||
@ -165,7 +172,7 @@ export class FlamechartRenderer {
|
|||||||
private root: RangeTreeNode
|
private root: RangeTreeNode
|
||||||
private rowAtlas: RowAtlas<RangeTreeLeafNode>
|
private rowAtlas: RowAtlas<RangeTreeLeafNode>
|
||||||
|
|
||||||
constructor(private canvasContext: CanvasContext, flamechart: Flamechart) {
|
constructor(private canvasContext: CanvasContext, private flamechart: Flamechart) {
|
||||||
const nLayers = flamechart.getLayers().length
|
const nLayers = flamechart.getLayers().length
|
||||||
this.rowAtlas = new RowAtlas(canvasContext)
|
this.rowAtlas = new RowAtlas(canvasContext)
|
||||||
|
|
||||||
@ -173,7 +180,7 @@ export class FlamechartRenderer {
|
|||||||
|
|
||||||
for (let stackDepth = 0; stackDepth < nLayers; stackDepth++) {
|
for (let stackDepth = 0; stackDepth < nLayers; stackDepth++) {
|
||||||
const leafNodes: RangeTreeLeafNode[] = []
|
const leafNodes: RangeTreeLeafNode[] = []
|
||||||
const y = stackDepth + 1
|
const y = stackDepth
|
||||||
|
|
||||||
let minLeft = Infinity
|
let minLeft = Infinity
|
||||||
let maxRight = -Infinity
|
let maxRight = -Infinity
|
||||||
@ -229,9 +236,7 @@ export class FlamechartRenderer {
|
|||||||
// which is even more expensive than not using a cache at all! Instead,
|
// 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,
|
// 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.
|
// 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
|
||||||
let useCache = renderedBatchCount++ < cacheCapacity || this.rowAtlas.has(leaf)
|
|
||||||
|
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
cachedLeaves.push(leaf)
|
cachedLeaves.push(leaf)
|
||||||
} else {
|
} else {
|
||||||
@ -240,17 +245,24 @@ export class FlamechartRenderer {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.rowAtlas.writeToAtlasIfNeeded(cachedLeaves, (textureDstRect, leaf) => {
|
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({
|
this.canvasContext.drawRectangleBatch({
|
||||||
batch: leaf.getBatch(),
|
batch: leaf.getBatch(),
|
||||||
configSpaceSrcRect: leaf.getBounds(),
|
configSpaceSrcRect: configSpaceBounds,
|
||||||
physicalSpaceDstRect: textureDstRect
|
physicalSpaceDstRect: textureDstRect
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const configToPhysical = AffineTransform.betweenRects(configSpaceSrcRect, physicalSpaceDstRect)
|
const configToPhysical = AffineTransform.betweenRects(configSpaceSrcRect, physicalSpaceDstRect)
|
||||||
for (let leaf of cachedLeaves) {
|
for (let leaf of cachedLeaves) {
|
||||||
const configSpaceLeafBounds = leaf.getBounds()
|
const configSpaceBounds = new Rect(
|
||||||
const physicalLeafBounds = configToPhysical.transformRect(configSpaceLeafBounds)
|
new Vec2(0, leaf.getBounds().top()),
|
||||||
|
new Vec2(this.flamechart.getTotalWeight(), 1)
|
||||||
|
)
|
||||||
|
const physicalLeafBounds = configToPhysical.transformRect(configSpaceBounds)
|
||||||
if (!this.rowAtlas.renderViaAtlas(leaf, physicalLeafBounds)) {
|
if (!this.rowAtlas.renderViaAtlas(leaf, physicalLeafBounds)) {
|
||||||
console.error('Failed to render from cache')
|
console.error('Failed to render from cache')
|
||||||
}
|
}
|
||||||
@ -263,5 +275,14 @@ export class FlamechartRenderer {
|
|||||||
physicalSpaceDstRect
|
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))
|
||||||
|
})
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -195,7 +195,7 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
|
|||||||
const renderFrameLabelAndChildren = (frame: FlamechartFrame, depth = 0) => {
|
const renderFrameLabelAndChildren = (frame: FlamechartFrame, depth = 0) => {
|
||||||
const width = frame.end - frame.start
|
const width = frame.end - frame.start
|
||||||
const configSpaceBounds = new Rect(
|
const configSpaceBounds = new Rect(
|
||||||
new Vec2(frame.start, depth + 1),
|
new Vec2(frame.start, depth),
|
||||||
new Vec2(width, 1)
|
new Vec2(width, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
|
|||||||
|
|
||||||
if (this.lastBounds == null) {
|
if (this.lastBounds == null) {
|
||||||
this.setConfigSpaceViewportRect(new Rect(
|
this.setConfigSpaceViewportRect(new Rect(
|
||||||
new Vec2(0, 0),
|
new Vec2(0, -1),
|
||||||
new Vec2(this.configSpaceSize().x, height / this.LOGICAL_VIEW_SPACE_FRAME_HEIGHT)
|
new Vec2(this.configSpaceSize().x, height / this.LOGICAL_VIEW_SPACE_FRAME_HEIGHT)
|
||||||
))
|
))
|
||||||
} else if (windowResized) {
|
} else if (windowResized) {
|
||||||
@ -308,7 +308,7 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
|
|||||||
|
|
||||||
if (this.props.configSpaceViewportRect.isEmpty()) return
|
if (this.props.configSpaceViewportRect.isEmpty()) return
|
||||||
|
|
||||||
this.props.canvasContext.renderInto(this.container, () => {
|
this.props.canvasContext.renderInto(this.container, (context) => {
|
||||||
this.props.flamechartRenderer.render({
|
this.props.flamechartRenderer.render({
|
||||||
physicalSpaceDstRect: new Rect(Vec2.zero, this.physicalViewSize()),
|
physicalSpaceDstRect: new Rect(Vec2.zero, this.physicalViewSize()),
|
||||||
configSpaceSrcRect: this.props.configSpaceViewportRect
|
configSpaceSrcRect: this.props.configSpaceViewportRect
|
||||||
@ -430,7 +430,7 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
|
|||||||
const setHoveredLabel = (frame: FlamechartFrame, depth = 0) => {
|
const setHoveredLabel = (frame: FlamechartFrame, depth = 0) => {
|
||||||
const width = frame.end - frame.start
|
const width = frame.end - frame.start
|
||||||
const configSpaceBounds = new Rect(
|
const configSpaceBounds = new Rect(
|
||||||
new Vec2(frame.start, depth + 1),
|
new Vec2(frame.start, depth),
|
||||||
new Vec2(width, 1)
|
new Vec2(width, 1)
|
||||||
)
|
)
|
||||||
if (configSpaceMouse.x < configSpaceBounds.left()) return null
|
if (configSpaceMouse.x < configSpaceBounds.left()) return null
|
||||||
@ -585,11 +585,13 @@ export class FlamechartView extends ReloadableComponent<FlamechartViewProps, Fla
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private minConfigSpaceViewportRectWidth() { return 3 * this.props.flamechart.getMinFrameWidth(); }
|
private minConfigSpaceViewportRectWidth() {
|
||||||
|
return Math.min(this.props.flamechart.getTotalWeight(), 3 * this.props.flamechart.getMinFrameWidth());
|
||||||
|
}
|
||||||
|
|
||||||
private setConfigSpaceViewportRect = (viewportRect: Rect): void => {
|
private setConfigSpaceViewportRect = (viewportRect: Rect): void => {
|
||||||
const configSpaceOriginBounds = new Rect(
|
const configSpaceOriginBounds = new Rect(
|
||||||
new Vec2(0, 0),
|
new Vec2(0, -1),
|
||||||
Vec2.max(new Vec2(0, 0), this.configSpaceSize().minus(viewportRect.size))
|
Vec2.max(new Vec2(0, 0), this.configSpaceSize().minus(viewportRect.size))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ export class List<V> {
|
|||||||
node.prev.next = node.next
|
node.prev.next = node.next
|
||||||
node.next = null
|
node.next = null
|
||||||
node.prev = null
|
node.prev = null
|
||||||
|
this.size--
|
||||||
}
|
}
|
||||||
this.size--
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,9 +157,8 @@ export class RectangleBatchRenderer {
|
|||||||
|
|
||||||
const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight)
|
const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight)
|
||||||
|
|
||||||
const physicalToNDC = AffineTransform
|
const physicalToNDC = AffineTransform.withTranslation(new Vec2(-1, 1))
|
||||||
.withTranslation(new Vec2(-1, 1))
|
.times(AffineTransform.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize)))
|
||||||
.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize))
|
|
||||||
|
|
||||||
return physicalToNDC.times(configToPhysical).flatten()
|
return physicalToNDC.times(configToPhysical).flatten()
|
||||||
}
|
}
|
||||||
|
1
sample/tall.txt
Normal file
1
sample/tall.txt
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user