Outline rendering WIP
This commit is contained in:
+8
-1
@@ -5,6 +5,7 @@ import { TextureCachedRenderer, TextureRenderer, TextureRendererProps } from './
|
||||
import { StatsPanel } from './stats'
|
||||
|
||||
import { Vec2, Rect } from './math';
|
||||
import { OutlineRenderer, OutlineRendererProps } from './outline-renderer';
|
||||
|
||||
type FrameCallback = () => void
|
||||
|
||||
@@ -17,6 +18,7 @@ export class CanvasContext {
|
||||
private rectangleBatchRenderer: RectangleBatchRenderer
|
||||
private viewportRectangleRenderer: ViewportRectangleRenderer
|
||||
private textureRenderer: TextureRenderer
|
||||
private outlineRenderer: OutlineRenderer
|
||||
private setViewportScope: regl.Command<{ physicalBounds: Rect }>
|
||||
private setScissor: regl.Command<{}>
|
||||
|
||||
@@ -26,7 +28,7 @@ export class CanvasContext {
|
||||
attributes: {
|
||||
antialias: false
|
||||
},
|
||||
extensions: ['ANGLE_instanced_arrays'],
|
||||
extensions: ['ANGLE_instanced_arrays', 'WEBGL_depth_texture'],
|
||||
optionalExtensions: ['EXT_disjoint_timer_query'],
|
||||
profile: true
|
||||
})
|
||||
@@ -34,6 +36,7 @@ export class CanvasContext {
|
||||
this.rectangleBatchRenderer = new RectangleBatchRenderer(this.gl)
|
||||
this.viewportRectangleRenderer = new ViewportRectangleRenderer(this.gl)
|
||||
this.textureRenderer = new TextureRenderer(this.gl)
|
||||
this.outlineRenderer = new OutlineRenderer(this.gl)
|
||||
this.setScissor = this.gl({ scissor: { enable: true } })
|
||||
this.setViewportScope = this.gl<SetViewportScopeProps>({
|
||||
context: {
|
||||
@@ -122,6 +125,10 @@ export class CanvasContext {
|
||||
this.textureRenderer.render(props)
|
||||
}
|
||||
|
||||
drawOutlines(props: OutlineRendererProps) {
|
||||
this.outlineRenderer.render(props)
|
||||
}
|
||||
|
||||
createRectangleBatch(): RectangleBatch {
|
||||
return new RectangleBatch(this.gl)
|
||||
}
|
||||
|
||||
+79
-23
@@ -112,7 +112,8 @@ class RangeTreeLeafNode implements RangeTreeNode {
|
||||
|
||||
constructor(
|
||||
private batch: RectangleBatch,
|
||||
private bounds: Rect
|
||||
private bounds: Rect,
|
||||
private numPrecedingRectanglesInRow: number
|
||||
) {
|
||||
batch.uploadToGPU()
|
||||
}
|
||||
@@ -121,6 +122,7 @@ class RangeTreeLeafNode implements RangeTreeNode {
|
||||
getBounds() { return this.bounds }
|
||||
getRectCount() { return this.batch.getRectCount() }
|
||||
getChildren() { return this.children }
|
||||
getParity() { return this.numPrecedingRectanglesInRow % 2 }
|
||||
forEachLeafNodeWithinBounds(configSpaceBounds: Rect, cb: (leaf: RangeTreeLeafNode) => void) {
|
||||
if (!this.bounds.hasIntersectionWith(configSpaceBounds)) return
|
||||
cb(this)
|
||||
@@ -178,11 +180,15 @@ interface FlamechartRowAtlasKey {
|
||||
export class FlamechartRenderer {
|
||||
private layers: RangeTreeNode[] = []
|
||||
private rowAtlas: RowAtlas<FlamechartRowAtlasKey>
|
||||
private colorTexture: regl.Texture
|
||||
private depthTexture: regl.Texture
|
||||
private framebuffer: regl.Framebuffer
|
||||
private renderToFramebuffer: regl.Command<{}>
|
||||
private withContext: regl.Command<{}>
|
||||
|
||||
constructor(private canvasContext: CanvasContext, private flamechart: Flamechart) {
|
||||
const nLayers = flamechart.getLayers().length
|
||||
this.rowAtlas = new RowAtlas(canvasContext)
|
||||
|
||||
for (let stackDepth = 0; stackDepth < nLayers; stackDepth++) {
|
||||
const leafNodes: RangeTreeLeafNode[] = []
|
||||
const y = stackDepth
|
||||
@@ -191,12 +197,14 @@ export class FlamechartRenderer {
|
||||
let maxRight = -Infinity
|
||||
let batch = canvasContext.createRectangleBatch()
|
||||
|
||||
let rectCount = 0
|
||||
|
||||
for (let frame of flamechart.getLayers()[stackDepth]) {
|
||||
if (batch.getRectCount() >= MAX_BATCH_SIZE) {
|
||||
leafNodes.push(new RangeTreeLeafNode(batch, new Rect(
|
||||
new Vec2(minLeft, stackDepth),
|
||||
new Vec2(maxRight - minLeft, 1)
|
||||
)))
|
||||
), rectCount))
|
||||
minLeft = Infinity
|
||||
maxRight = -Infinity
|
||||
batch = canvasContext.createRectangleBatch()
|
||||
@@ -209,17 +217,33 @@ export class FlamechartRenderer {
|
||||
maxRight = Math.max(maxRight, configSpaceBounds.right())
|
||||
const color = flamechart.getColorForFrame(frame.node.frame)
|
||||
batch.addRect(configSpaceBounds, color)
|
||||
rectCount++
|
||||
}
|
||||
|
||||
if (batch.getRectCount() > 0) {
|
||||
leafNodes.push(new RangeTreeLeafNode(batch, new Rect(
|
||||
new Vec2(minLeft, stackDepth),
|
||||
new Vec2(maxRight - minLeft, 1)
|
||||
)))
|
||||
), rectCount))
|
||||
}
|
||||
|
||||
// TODO(jlfwong): Probably want this to be a binary tree
|
||||
// TODO(jlfwong): Making this into a binary tree
|
||||
// range than a tree of always-height-two might make this run faster
|
||||
this.layers.push(new RangeTreeInteriorNode(leafNodes))
|
||||
|
||||
// TODO(jlfwong): Extract this to CanvasContext
|
||||
this.withContext = canvasContext.gl({})
|
||||
|
||||
this.colorTexture = this.canvasContext.gl.texture({ width: 1, height: 1 })
|
||||
this.depthTexture = this.canvasContext.gl.texture({ width: 1, height: 1, format: 'depth', type: 'uint16' })
|
||||
this.framebuffer = this.canvasContext.gl.framebuffer({
|
||||
color: [this.colorTexture],
|
||||
depth: this.depthTexture
|
||||
})
|
||||
|
||||
this.renderToFramebuffer = canvasContext.gl({
|
||||
framebuffer: this.framebuffer
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +285,7 @@ export class FlamechartRenderer {
|
||||
}
|
||||
|
||||
const top = Math.max(0, Math.floor(configSpaceSrcRect.top()))
|
||||
const bottom = Math.min(this.layers.length - 1, Math.ceil(configSpaceSrcRect.bottom()))
|
||||
const bottom = Math.min(this.layers.length, Math.ceil(configSpaceSrcRect.bottom()))
|
||||
|
||||
const configSpaceContentWidth = this.flamechart.getTotalWeight()
|
||||
const numAtlasEntriesPerLayer = Math.pow(2, zoomLevel)
|
||||
@@ -288,29 +312,61 @@ export class FlamechartRenderer {
|
||||
this.canvasContext.drawRectangleBatch({
|
||||
batch: leaf.getBatch(),
|
||||
configSpaceSrcRect: configSpaceBounds,
|
||||
physicalSpaceDstRect: textureDstRect
|
||||
physicalSpaceDstRect: textureDstRect,
|
||||
parityMin: key.stackDepth % 2 == 0 ? 2 : 0,
|
||||
parityOffset: leaf.getParity()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Render from the cache
|
||||
for (let key of keysToRenderCached) {
|
||||
const configSpaceSrcRect = this.configSpaceBoundsForKey(key)
|
||||
this.rowAtlas.renderViaAtlas(key, configToPhysical.transformRect(configSpaceSrcRect))
|
||||
}
|
||||
this.withContext((context: regl.Context) => {
|
||||
this.framebuffer.resize(context.viewportWidth, context.viewportHeight)
|
||||
})
|
||||
|
||||
// Render entries that didn't make it into the cache
|
||||
for (let key of keysToRenderUncached) {
|
||||
const configSpaceBounds = this.configSpaceBoundsForKey(key)
|
||||
const physicalBounds = configToPhysical.transformRect(configSpaceBounds)
|
||||
this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, (leaf) => {
|
||||
this.canvasContext.drawRectangleBatch({
|
||||
batch: leaf.getBatch(),
|
||||
configSpaceSrcRect,
|
||||
physicalSpaceDstRect: physicalBounds
|
||||
this.renderToFramebuffer((context: regl.Context) => {
|
||||
// this.withContext((context: regl.Context) => {
|
||||
this.canvasContext.gl.clear({color: [0, 0, 0, 0]})
|
||||
const viewportRect = new Rect(Vec2.zero, new Vec2(context.viewportWidth, context.viewportHeight))
|
||||
|
||||
const configToViewport = AffineTransform.betweenRects(configSpaceSrcRect, viewportRect)
|
||||
|
||||
// Render from the cache
|
||||
for (let key of keysToRenderCached) {
|
||||
const configSpaceSrcRect = this.configSpaceBoundsForKey(key)
|
||||
this.rowAtlas.renderViaAtlas(key, configToViewport.transformRect(configSpaceSrcRect))
|
||||
}
|
||||
|
||||
// Render entries that didn't make it into the cache
|
||||
for (let key of keysToRenderUncached) {
|
||||
const configSpaceBounds = this.configSpaceBoundsForKey(key)
|
||||
const physicalBounds = configToViewport.transformRect(configSpaceBounds)
|
||||
this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, (leaf) => {
|
||||
this.canvasContext.drawRectangleBatch({
|
||||
batch: leaf.getBatch(),
|
||||
configSpaceSrcRect,
|
||||
physicalSpaceDstRect: physicalBounds,
|
||||
parityMin: key.stackDepth % 2 == 0 ? 2 : 0,
|
||||
parityOffset: leaf.getParity()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.canvasContext.drawOutlines({
|
||||
colorTexture: this.colorTexture,
|
||||
depthTexture: this.depthTexture,
|
||||
srcRect: new Rect(Vec2.zero, new Vec2(this.colorTexture.width, this.colorTexture.height)),
|
||||
dstRect: physicalSpaceDstRect
|
||||
})
|
||||
|
||||
// Paint the color texture for debugging
|
||||
/*
|
||||
this.canvasContext.drawTexture({
|
||||
texture: this.colorTexture,
|
||||
srcRect: new Rect(Vec2.zero, new Vec2(this.colorTexture.width, this.colorTexture.height)),
|
||||
dstRect: physicalSpaceDstRect
|
||||
})
|
||||
*/
|
||||
|
||||
// Overlay the atlas on top of the canvas for debugging
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import * as regl from 'regl'
|
||||
import { Vec2, Rect, AffineTransform } from './math'
|
||||
|
||||
export class OutlineRendererProps {
|
||||
colorTexture: regl.Texture
|
||||
depthTexture: regl.Texture
|
||||
srcRect: Rect
|
||||
dstRect: Rect
|
||||
}
|
||||
export class OutlineRenderer {
|
||||
private command: regl.Command<OutlineRendererProps>
|
||||
constructor(gl: regl.Instance) {
|
||||
this.command = gl({
|
||||
vert: `
|
||||
uniform mat3 uvTransform;
|
||||
uniform mat3 positionTransform;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec2 uv;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = (uvTransform * vec3(uv, 1)).xy;
|
||||
gl_Position = vec4((positionTransform * vec3(position, 1)).xy, 0, 1);
|
||||
}
|
||||
`,
|
||||
|
||||
frag: `
|
||||
precision mediump float;
|
||||
|
||||
uniform vec2 uvSpacePixelSize;
|
||||
|
||||
varying vec2 vUv;
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
|
||||
void main() {
|
||||
// Sample the 4 surrounding pixels in the depth texture to determine
|
||||
// if we should draw a boundary here or not.
|
||||
float N = texture2D(depthTexture, vUv + vec2(0, uvSpacePixelSize.y)).r;
|
||||
float E = texture2D(depthTexture, vUv + vec2(uvSpacePixelSize.x, 0)).r;
|
||||
float S = texture2D(depthTexture, vUv + vec2(0, -uvSpacePixelSize.y)).r;
|
||||
float W = texture2D(depthTexture, vUv + vec2(-uvSpacePixelSize.x, 0)).r;
|
||||
float here = texture2D(depthTexture, vUv).x;
|
||||
|
||||
if (
|
||||
here == N && here != S || // Top edge
|
||||
here == S && here != N || // Bottom edge
|
||||
here == E && here != W || // Left edge
|
||||
here == W && here != E
|
||||
) {
|
||||
// We're on an edge! Draw white.
|
||||
gl_FragColor = vec4(1, 1, 1, 1);
|
||||
} else {
|
||||
gl_FragColor = texture2D(colorTexture, vUv);
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
depth: {
|
||||
enable: false
|
||||
},
|
||||
|
||||
attributes: {
|
||||
// Cover full canvas with a rectangle
|
||||
// with 2 triangles using a triangle
|
||||
// strip.
|
||||
//
|
||||
// 0 +--+ 1
|
||||
// | /|
|
||||
// |/ |
|
||||
// 2 +--+ 3
|
||||
position: gl.buffer([
|
||||
[-1, 1],
|
||||
[1, 1],
|
||||
[-1, -1],
|
||||
[1, -1]
|
||||
]),
|
||||
uv: gl.buffer([
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
[0, 0],
|
||||
[1, 0]
|
||||
])
|
||||
},
|
||||
|
||||
count: 4,
|
||||
|
||||
primitive: 'triangle strip',
|
||||
|
||||
uniforms: {
|
||||
colorTexture: (context, props) => props.colorTexture,
|
||||
depthTexture: (context, props) => props.depthTexture,
|
||||
uvTransform: (context, props) => {
|
||||
const { srcRect, colorTexture } = props
|
||||
const physicalToUV = AffineTransform.withTranslation(new Vec2(0, 1))
|
||||
.times(AffineTransform.withScale(new Vec2(1, -1)))
|
||||
.times(AffineTransform.betweenRects(
|
||||
new Rect(Vec2.zero, new Vec2(colorTexture.width, colorTexture.height)),
|
||||
Rect.unit
|
||||
))
|
||||
const uvRect = physicalToUV.transformRect(srcRect)
|
||||
return AffineTransform.betweenRects(
|
||||
Rect.unit,
|
||||
uvRect,
|
||||
).flatten()
|
||||
},
|
||||
uvSpacePixelSize: (context, props) => {
|
||||
return Vec2.unit.dividedByPointwise(new Vec2(props.colorTexture.width, props.colorTexture.height)).flatten()
|
||||
},
|
||||
positionTransform: (context, props) => {
|
||||
const { dstRect } = props
|
||||
const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight)
|
||||
|
||||
const physicalToNDC = AffineTransform.withScale(new Vec2(1, -1))
|
||||
.times(AffineTransform.betweenRects(
|
||||
new Rect(Vec2.zero, viewportSize),
|
||||
Rect.NDC)
|
||||
)
|
||||
const ndcRect = physicalToNDC.transformRect(dstRect)
|
||||
return AffineTransform.betweenRects(Rect.NDC, ndcRect).flatten()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
render(props: OutlineRendererProps) {
|
||||
this.command(props)
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,21 @@ export class RectangleBatch {
|
||||
return this.colorBuffer
|
||||
}
|
||||
|
||||
private indexBuffer: regl.Buffer | null = null
|
||||
getIndexBuffer() {
|
||||
if (!this.indexBuffer) {
|
||||
const indices = new Float32Array(this.rectCount)
|
||||
for (let i = 0; i < this.rectCount; i++) indices[i] = i
|
||||
this.indexBuffer = this.gl.buffer(indices)
|
||||
}
|
||||
return this.indexBuffer
|
||||
}
|
||||
|
||||
uploadToGPU() {
|
||||
this.getConfigSpaceOffsetBuffer()
|
||||
this.getConfigSpaceSizeBuffer()
|
||||
this.getColorBuffer()
|
||||
this.getIndexBuffer()
|
||||
}
|
||||
|
||||
addRect(rect: Rect, color: Color) {
|
||||
@@ -71,14 +82,23 @@ export interface RectangleBatchRendererProps {
|
||||
batch: RectangleBatch
|
||||
configSpaceSrcRect: Rect
|
||||
physicalSpaceDstRect: Rect
|
||||
parityMin?: number
|
||||
parityOffset?: number
|
||||
}
|
||||
|
||||
export class RectangleBatchRenderer {
|
||||
private command: regl.Command<RectangleBatchRendererProps>
|
||||
constructor(gl: regl.Instance) {
|
||||
// We draw the parity / 4 into the depth channel so it
|
||||
// can be used in a post-processing step to draw boundaries
|
||||
// between rectangles. We use 4 different values (2 per row)
|
||||
// so we can distingish both between adjacent rectangles on a row
|
||||
// and between rows!
|
||||
this.command = gl({
|
||||
vert: `
|
||||
uniform mat3 configSpaceToNDC;
|
||||
uniform float parityMin;
|
||||
uniform float parityOffset;
|
||||
|
||||
// Non-instanced
|
||||
attribute vec2 corner;
|
||||
@@ -87,23 +107,27 @@ export class RectangleBatchRenderer {
|
||||
attribute vec2 configSpaceOffset;
|
||||
attribute vec2 configSpaceSize;
|
||||
attribute vec3 color;
|
||||
attribute float index;
|
||||
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vColor = color;
|
||||
float depth = parityMin + mod(parityOffset + index, 2.0);
|
||||
vec2 configSpacePos = configSpaceOffset + corner * configSpaceSize;
|
||||
vec2 position = (configSpaceToNDC * vec3(configSpacePos, 1)).xy;
|
||||
gl_Position = vec4(position, 0, 1);
|
||||
gl_Position = vec4(position, depth / 4.0, 1);
|
||||
}
|
||||
`,
|
||||
|
||||
depth: {
|
||||
enable: false
|
||||
},
|
||||
depth: {
|
||||
enable: false
|
||||
},
|
||||
|
||||
frag: `
|
||||
frag: `
|
||||
precision mediump float;
|
||||
varying vec3 vColor;
|
||||
varying float vParity;
|
||||
void main() {
|
||||
gl_FragColor = vec4(vColor, 1);
|
||||
}
|
||||
@@ -145,6 +169,15 @@ export class RectangleBatchRenderer {
|
||||
size: 3,
|
||||
divisor: 1
|
||||
}
|
||||
},
|
||||
index: (context, props) => {
|
||||
return {
|
||||
buffer: props.batch.getIndexBuffer(),
|
||||
offset: 0,
|
||||
stride: 4,
|
||||
size: 1,
|
||||
divisor: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -161,6 +194,14 @@ export class RectangleBatchRenderer {
|
||||
.times(AffineTransform.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize)))
|
||||
|
||||
return physicalToNDC.times(configToPhysical).flatten()
|
||||
},
|
||||
|
||||
parityOffset: (context, props) => {
|
||||
return props.parityOffset || 0
|
||||
},
|
||||
|
||||
parityMin: (context, props) => {
|
||||
return props.parityMin || 0
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ declare module "regl" {
|
||||
height: number
|
||||
|
||||
(args: TextureArgs): void
|
||||
resize(width: number, height: number): void
|
||||
destroy(): void
|
||||
stats: {
|
||||
size: number
|
||||
@@ -259,6 +260,11 @@ declare module "regl" {
|
||||
colorType?: 'uint8' | 'half float' | 'float'
|
||||
}
|
||||
interface Framebuffer {
|
||||
width: number
|
||||
height: number
|
||||
color?: RenderBuffer[]
|
||||
depth?: RenderBuffer
|
||||
|
||||
(options: FramebufferOptions): void
|
||||
resize(width: number, height: number): void
|
||||
destroy(): void
|
||||
@@ -414,14 +420,9 @@ declare module "regl" {
|
||||
(p: P, cb: (context: Context) => void): void
|
||||
|
||||
stats: {
|
||||
/** Aggregate time spent on the GPU in ms */
|
||||
gpuTime: number
|
||||
|
||||
/** Aggregate time spent on the CPU in ms */
|
||||
cpuTime: number
|
||||
|
||||
/** Total number of calls of this command */
|
||||
count: number
|
||||
hitCount: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
a;b 1
|
||||
c;d 1
|
||||
@@ -142,6 +142,7 @@ export class TextureCachedRenderer<T> {
|
||||
this.withContext((context: regl.Context) => {
|
||||
let needsRender = false
|
||||
if (this.texture.width !== context.viewportWidth || this.texture.height !== context.viewportHeight) {
|
||||
// TODO(jlfwong): Can probably just use this.framebuffer.resize
|
||||
this.texture({ width: context.viewportWidth, height: context.viewportHeight })
|
||||
this.framebuffer({ color: [this.texture] })
|
||||
needsRender = true
|
||||
@@ -155,6 +156,9 @@ export class TextureCachedRenderer<T> {
|
||||
|
||||
if (needsRender) {
|
||||
// Render to texture
|
||||
// TODO(jlfwong): Re-enable this when I figure out how to
|
||||
// resize a framebuffer while another framebuffer is active.
|
||||
/*
|
||||
this.gl({
|
||||
viewport: (context, props) => {
|
||||
return {
|
||||
@@ -169,6 +173,7 @@ export class TextureCachedRenderer<T> {
|
||||
this.gl.clear({color: [0, 0, 0, 0]})
|
||||
this.renderUncached(props)
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
const glViewportRect = new Rect(Vec2.zero, new Vec2(context.viewportWidth, context.viewportHeight))
|
||||
|
||||
Reference in New Issue
Block a user