Make NDC an implementation detail

This commit is contained in:
Jamie Wong 2018-01-27 21:21:38 -08:00
parent 37e2b78ad8
commit 5412573ba3
4 changed files with 42 additions and 64 deletions

View File

@ -63,15 +63,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
)
}
private physicalViewSpaceToNDC() {
return AffineTransform.withScale(new Vec2(1, -1)).times(
AffineTransform.betweenRects(
new Rect(new Vec2(0, 0), this.physicalViewSize()),
new Rect(new Vec2(-1, -1), new Vec2(2, 2))
)
)
}
private logicalToPhysicalViewSpace() {
return AffineTransform.withScale(new Vec2(DEVICE_PIXEL_RATIO, DEVICE_PIXEL_RATIO))
}
@ -83,8 +74,7 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
}
private cachedRenderer: TextureCachedRenderer<{
physicalSize: Vec2,
configSpaceToNDC: AffineTransform
physicalSize: Vec2
}> | null = null
private renderRects() {
if (!this.container) return
@ -93,8 +83,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
shouldUpdate(oldProps, newProps) {
if (!oldProps.physicalSize.equals(newProps.physicalSize)) {
return true
} else if (!oldProps.configSpaceToNDC.equals(newProps.configSpaceToNDC)) {
return true
}
return false
},
@ -110,11 +98,8 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
})
}
const configSpaceToNDC = this.physicalViewSpaceToNDC().times(this.configSpaceToPhysicalViewSpace())
this.props.canvasContext.renderInto(this.container, (context) => {
this.cachedRenderer!.render(context, {
configSpaceToNDC,
physicalSize: this.physicalViewSize()
})
this.props.canvasContext.drawViewportRectangle({

View File

@ -11,7 +11,7 @@ interface RangeTreeNode {
getMaxRight(): number
getRectCount(): number
getChildren(): RangeTreeNode[]
forEachBatchInViewport(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void): void
forEachBatchWithinBounds(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void): void
}
class RangeTreeLeafNode implements RangeTreeNode {
@ -27,7 +27,7 @@ class RangeTreeLeafNode implements RangeTreeNode {
getMaxRight() { return this.maxRight }
getRectCount() { return this.batch.getRectCount() }
getChildren() { return this.children }
forEachBatchInViewport(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void) {
forEachBatchWithinBounds(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void) {
if (this.maxRight < configSpaceViewport.left()) return
if (this.minLeft > configSpaceViewport.right()) return
cb(this.batch)
@ -53,21 +53,16 @@ class RangeTreeInteriorNode implements RangeTreeNode {
getMaxRight() { return this.children[this.children.length - 1].getMaxRight() }
getRectCount() { return this.rectCount }
getChildren() { return this.children }
forEachBatchInViewport(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void) {
if (this.getMaxRight() < configSpaceViewport.left()) return
if (this.getMinLeft() > configSpaceViewport.right()) return
forEachBatchWithinBounds(configSpaceViewport: Rect, cb: (batch: RectangleBatch) => void) {
// if (this.getMaxRight() < configSpaceViewport.left()) return
// if (this.getMinLeft() > configSpaceViewport.right()) return
for (let child of this.children) {
child.forEachBatchInViewport(configSpaceViewport, cb)
child.forEachBatchWithinBounds(configSpaceViewport, cb)
}
}
}
interface BoundedLayerProps {
configSpaceToNDC: AffineTransform
physicalSize: Vec2
}
export interface FlamechartRendererProps {
configSpaceSrcRect: Rect
physicalSpaceDstRect: Rect
@ -111,30 +106,25 @@ class BoundedLayer {
this.rootNode = new RangeTreeInteriorNode(leafNodes)
}
render(props: BoundedLayerProps) {
render(props: FlamechartRendererProps) {
const configSpaceTop = this.stackDepth + 1
const configSpaceBottom = configSpaceTop + 1
const ndcToConfigSpace = props.configSpaceToNDC.inverted()
if (!ndcToConfigSpace) return
const configSpaceViewportRect = ndcToConfigSpace.transformRect(new Rect(
new Vec2(-1, -1), new Vec2(2, 2)
))
if (configSpaceTop > configSpaceViewportRect.bottom()) {
// Entire layer is below the viewport
const { configSpaceSrcRect, physicalSpaceDstRect } = props
if (configSpaceTop > configSpaceSrcRect.bottom()) {
// Entire layer is below the config space bounds
return
}
if (configSpaceBottom < configSpaceViewportRect.top()) {
// Entire layer is above the viewport
if (configSpaceBottom < configSpaceSrcRect.top()) {
// Entire layer is above the config space bounds
return
}
this.rootNode.forEachBatchInViewport(configSpaceViewportRect, batch => {
this.rootNode.forEachBatchWithinBounds(configSpaceSrcRect, batch => {
this.canvasContext.drawRectangleBatch({
configSpaceToNDC: props.configSpaceToNDC,
physicalSize: props.physicalSize,
configSpaceSrcRect,
physicalSpaceDstRect,
batch
})
})
@ -173,12 +163,7 @@ export class FlamechartRenderer {
})
const fbo = canvasContext.gl.framebuffer({ color: [this.texture] })
const configSpaceToNDC = AffineTransform.withScale(new Vec2(1, -1)).times(
AffineTransform.betweenRects(
this.getConfigSpaceContentRect(),
new Rect(new Vec2(-1, -1), new Vec2(2, 2))
)
)
const configSpaceSrcRect = this.getConfigSpaceContentRect()
canvasContext.gl({
viewport: (context, props) => {
@ -191,12 +176,12 @@ export class FlamechartRenderer {
},
framebuffer: fbo
})((context: regl.Context) => {
const physicalSize = new Vec2(context.drawingBufferWidth, context.drawingBufferHeight)
const physicalSpaceDstRect = new Rect(
new Vec2(),
new Vec2(context.viewportWidth, context.viewportHeight)
)
for (let layer of this.layers) {
layer.render({
physicalSize,
configSpaceToNDC
})
layer.render({ configSpaceSrcRect, physicalSpaceDstRect })
}
})
@ -209,13 +194,13 @@ export class FlamechartRenderer {
const { configSpaceSrcRect, physicalSpaceDstRect } = props
const content = this.getConfigSpaceContentRect()
const textureSize = new Vec2(this.texture.width, this.texture.height)
const physicalSpaceSrcRect = new Rect(
configSpaceSrcRect.origin.dividedByPointwise(content.size).timesPointwise(textureSize),
configSpaceSrcRect.size.dividedByPointwise(content.size).timesPointwise(textureSize)
const textureRect = new Rect(
new Vec2(), new Vec2(this.texture.width, this.texture.height)
)
const configToTexture = AffineTransform.betweenRects(content, textureRect)
const physicalSpaceSrcRect = configToTexture.transformRect(configSpaceSrcRect)
this.canvasContext.drawTexture({
texture: this.texture,
srcRect: physicalSpaceSrcRect,

View File

@ -53,7 +53,7 @@ const DEVICE_PIXEL_RATIO = window.devicePixelRatio
* Component to visualize a Flamechart and interact with it via hovering,
* zooming, and panning.
*
* There are 4 vector spaces involved:
* There are 3 vector spaces involved:
* - Configuration Space: In this space, the horizontal unit is ms, and the
* vertical unit is stack depth. Each stack frame is one unit high.
* - Logical view space: Origin is top-left, with +y downwards. This represents
@ -62,9 +62,6 @@ const DEVICE_PIXEL_RATIO = window.devicePixelRatio
* - Physical view space: Origin is top-left, with +y downwards. This represents
* the coordinate space of the view as specified in hardware pixels: horizontal
* and vertical units are both "physical" pixels.
* - Normalized device coordinates: Origin is center, +y upwards. This is the
* coordinate space used by GL, which we use to render the frame rectangles
* efficiently.
*
* We use two canvases to draw the flamechart itself: one for the rectangles,
* which we render via WebGL, and one for the labels, which we render via 2D

View File

@ -68,9 +68,9 @@ export class RectangleBatch {
}
export interface RectangleBatchRendererProps {
configSpaceToNDC: AffineTransform
physicalSize: Vec2
batch: RectangleBatch
configSpaceSrcRect: Rect
physicalSpaceDstRect: Rect
}
export class RectangleBatchRenderer {
@ -150,7 +150,18 @@ export class RectangleBatchRenderer {
uniforms: {
configSpaceToNDC: (context, props) => {
return props.configSpaceToNDC.flatten()
const configToPhysical = AffineTransform.betweenRects(
props.configSpaceSrcRect,
props.physicalSpaceDstRect
)
const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight)
const physicalToNDC = AffineTransform
.withTranslation(new Vec2(-1, 1))
.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize))
return physicalToNDC.times(configToPhysical).flatten()
}
},