WIP: Single GL context

This commit is contained in:
Jamie Wong
2018-01-22 11:22:24 -08:00
parent c5f9ae9cf9
commit b3213a2f44
5 changed files with 340 additions and 173 deletions
+128 -4
View File
@@ -1,16 +1,19 @@
import {h} from 'preact'
import * as regl from 'regl'
import {StyleSheet, css} from 'aphrodite'
import {ReloadableComponent} from './reloadable'
import {importFromBGFlameGraph} from './import/bg-flamegraph'
import {importFromStackprof} from './import/stackprof'
import {importFromChromeTimeline, importFromChromeCPUProfile} from './import/chrome'
import { RectangleBatch, RectangleBatchRenderer } from './rectangle-batch-renderer'
import {Profile, Frame} from './profile'
import {Flamechart} from './flamechart'
import { FlamechartView } from './flamechart-view'
import { FontFamily, FontSize, Colors } from './style'
import { FrameColorGenerator } from './color'
import { Vec2, Rect } from './math'
const enum SortOrder {
CHRONO,
@@ -20,7 +23,9 @@ const enum SortOrder {
interface ApplicationState {
profile: Profile | null
flamechart: Flamechart | null
flamechartRectBatch: RectangleBatch | null
sortedFlamechart: Flamechart | null
sortedFlamechartRectBatch: RectangleBatch | null
sortOrder: SortOrder
loading: boolean
}
@@ -118,6 +123,85 @@ export class Toolbar extends ReloadableComponent<ToolbarProps, void> {
}
}
interface GLCanvasProps {
setGL(gl: regl.Instance | null): void
}
export class GLCanvas extends ReloadableComponent<GLCanvasProps, void> {
private canvas: HTMLCanvasElement | null
private gl: regl.Instance | null
private ref = (canvas?: Element) => {
if (canvas instanceof HTMLCanvasElement) {
this.canvas = canvas
this.gl = regl(canvas)
} else {
this.gl = null
}
this.props.setGL(this.gl)
}
private maybeResize() {
if (!this.canvas || !this.gl) return
let { width, height } = this.canvas.getBoundingClientRect()
width = Math.floor(width) * window.devicePixelRatio
height = Math.floor(height) * window.devicePixelRatio
// Still initializing: don't resize yet
if (width < 4 || height < 4) return
const oldWidth = this.canvas.width
const oldHeight = this.canvas.height
// Already at the right size
if (width === oldWidth && height === oldHeight) return
this.canvas.width = width
this.canvas.height = height
}
onWindowResize = () => {
this.maybeResize()
window.addEventListener('resize', this.onWindowResize)
}
componentDidMount() {
window.addEventListener('resize', this.onWindowResize)
requestAnimationFrame(() => this.maybeResize())
}
componentWillUnmount() {
window.removeEventListener('resize', this.onWindowResize)
}
render() {
return <canvas className={css(style.glCanvasView)} ref={this.ref} width={1} height={1} />
}
}
function rectangleBatchForFlamechart(gl: regl.Instance, flamechart: Flamechart) {
console.time('rectangle batch generation')
const batch = new RectangleBatch(gl)
const layers = flamechart.getLayers()
for (let i = 0; i < layers.length; i++) {
const layer = layers[i]
for (let flamechartFrame of layer) {
const configSpaceBounds = new Rect(
new Vec2(flamechartFrame.start, i + 1),
new Vec2(flamechartFrame.end - flamechartFrame.start, 1)
)
const color = flamechart.getColorForFrame(flamechartFrame.node.frame)
batch.addRect(configSpaceBounds, color)
}
}
// GPU upload
batch.uploadToGPU()
console.timeEnd('rectangle batch generation')
return batch
}
export class Application extends ReloadableComponent<{}, ApplicationState> {
constructor() {
super()
@@ -125,12 +209,16 @@ export class Application extends ReloadableComponent<{}, ApplicationState> {
loading: false,
profile: null,
flamechart: null,
flamechartRectBatch: null,
sortedFlamechart: null,
sortedFlamechartRectBatch: null,
sortOrder: SortOrder.CHRONO
}
}
loadFromString(fileName: string, contents: string) {
if (!this.gl) return
console.time('import')
const profile = importProfile(contents, fileName)
if (profile == null) {
@@ -153,6 +241,7 @@ export class Application extends ReloadableComponent<{}, ApplicationState> {
formatValue: profile.formatValue.bind(profile),
getColorForFrame: colorGenerator.getColorForFrame.bind(colorGenerator)
})
const flamechartRectBatch = rectangleBatchForFlamechart(this.gl, flamechart)
const sortedFlamechart = new Flamechart({
getTotalWeight: profile.getTotalNonIdleWeight.bind(profile),
@@ -160,10 +249,19 @@ export class Application extends ReloadableComponent<{}, ApplicationState> {
formatValue: profile.formatValue.bind(profile),
getColorForFrame: colorGenerator.getColorForFrame.bind(colorGenerator)
})
const sortedFlamechartRectBatch = rectangleBatchForFlamechart(this.gl, sortedFlamechart)
console.timeEnd('import')
console.time('first setState')
this.setState({ profile, flamechart, sortedFlamechart, loading: false }, () => {
this.setState({
profile,
flamechart,
flamechartRectBatch,
sortedFlamechart,
sortedFlamechartRectBatch,
loading: false
}, () => {
console.timeEnd('first setState')
})
}
@@ -263,22 +361,48 @@ export class Application extends ReloadableComponent<{}, ApplicationState> {
this.setState({ sortOrder })
}
private gl: regl.Instance | null = null
private rectangleBatchRenderer: RectangleBatchRenderer | null = null
private setGL = (gl: regl.Instance | null) => {
if (gl) {
this.gl = gl
this.rectangleBatchRenderer = new RectangleBatchRenderer(gl)
} else {
this.gl = null
this.rectangleBatchRenderer = null
}
}
render() {
const {flamechart, sortedFlamechart, sortOrder, loading} = this.state
const {flamechart, flamechartRectBatch, sortedFlamechart, sortedFlamechartRectBatch, sortOrder, loading} = this.state
const flamechartToView = sortOrder == SortOrder.CHRONO ? flamechart : sortedFlamechart
const rectangleBatch = sortOrder == SortOrder.CHRONO ? flamechartRectBatch : sortedFlamechartRectBatch
return <div onDrop={this.onDrop} onDragOver={this.onDragOver} className={css(style.root)}>
<GLCanvas setGL={this.setGL} />
<Toolbar setSortOrder={this.setSortOrder} {...this.state} />
{loading ?
this.renderLoadingBar() :
flamechartToView ?
<FlamechartView ref={this.flamechartRef} flamechart={flamechartToView} /> :
this.gl && flamechartToView && this.rectangleBatchRenderer && rectangleBatch ?
<FlamechartView
gl={this.gl}
renderer={this.rectangleBatchRenderer}
rectangles={rectangleBatch}
ref={this.flamechartRef}
flamechart={flamechartToView} /> :
this.renderLanding()}
</div>
}
}
const style = StyleSheet.create({
glCanvasView: {
position: 'absolute',
width: '100vw',
height: '100vh',
zIndex: -1,
pointerEvents: 'none'
},
loading: {
height: 3,
marginBottom: -3,
+33 -47
View File
@@ -1,10 +1,10 @@
import * as regl from 'regl'
import { vec3, Command } from 'regl'
import { Command } from 'regl'
import { h, Component } from 'preact'
import { css } from 'aphrodite'
import { Flamechart } from './flamechart'
import { Rect, Vec2, AffineTransform, clamp } from './math'
import { rectangleBatchRenderer, RectangleBatchRendererProps } from "./rectangle-batch-renderer"
import { RectangleBatchRenderer, RectangleBatch } from "./rectangle-batch-renderer"
import { atMostOnceAFrame, cachedMeasureTextWidth } from "./utils";
import { style, Sizes } from "./flamechart-style";
import { FontFamily, FontSize, Colors } from "./style"
@@ -14,6 +14,13 @@ const DEVICE_PIXEL_RATIO = window.devicePixelRatio
interface FlamechartMinimapViewProps {
flamechart: Flamechart
configSpaceViewportRect: Rect
// TODO(jlfwong): Encapsulate the regl.Instance and the batch renderer
// in a CanvasContext object or something along those lines
gl: regl.Instance
renderer: RectangleBatchRenderer
rectangles: RectangleBatch
transformViewport: (transform: AffineTransform) => void
setConfigSpaceViewportRect: (rect: Rect) => void
}
@@ -24,11 +31,8 @@ enum DraggingMode {
}
export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps, {}> {
renderer: Command<RectangleBatchRendererProps> | null = null
viewportRectRenderer: Command<OverlayRectangleRendererProps> | null = null
ctx: WebGLRenderingContext | null = null
gl: regl.Instance | null = null
canvas: HTMLCanvasElement | null = null
overlayCanvas: HTMLCanvasElement | null = null
@@ -77,21 +81,39 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
}
private renderRects() {
if (!this.renderer || !this.canvas || !this.viewportRectRenderer) return
if (!this.canvas) return
this.resizeCanvasIfNeeded()
const configSpaceToNDC = this.physicalViewSpaceToNDC().times(this.configSpaceToPhysicalViewSpace())
this.renderer({
configSpaceToNDC: configSpaceToNDC,
physicalSize: this.physicalViewSize()
const bounds = this.canvas.getBoundingClientRect()
const physicalBounds = this.logicalToPhysicalViewSpace().transformRect(new Rect(
new Vec2(bounds.left, bounds.top),
new Vec2(bounds.width, bounds.height)
))
this.props.gl({
viewport: {
x: physicalBounds.left(),
y: window.devicePixelRatio * window.innerHeight - physicalBounds.top() - physicalBounds.height(),
width: physicalBounds.width(),
height: physicalBounds.height()
}
})(() => {
this.props.renderer.render({
configSpaceToNDC: configSpaceToNDC,
physicalSize: this.physicalViewSize(),
strokeSize: 0,
batch: this.props.rectangles
})
})
/*
this.viewportRectRenderer({
configSpaceViewportRect: this.props.configSpaceViewportRect,
configSpaceToPhysicalViewSpace: this.configSpaceToPhysicalViewSpace(),
physicalSize: this.physicalViewSize()
})
*/
}
private renderOverlays() {
@@ -152,7 +174,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
componentWillReceiveProps(nextProps: FlamechartMinimapViewProps) {
if (this.props.flamechart !== nextProps.flamechart) {
this.renderer = null
this.renderCanvas()
} else if (this.props.configSpaceViewportRect != nextProps.configSpaceViewportRect) {
this.renderCanvas()
@@ -160,7 +181,7 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
}
private resizeCanvasIfNeeded() {
if (!this.canvas || !this.ctx) return
if (!this.canvas) return
let { width, height } = this.canvas.getBoundingClientRect()
width = Math.floor(width) * DEVICE_PIXEL_RATIO
height = Math.floor(height) * DEVICE_PIXEL_RATIO
@@ -175,8 +196,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
this.canvas.width = width
this.canvas.height = height
this.ctx.viewport(0, 0, width, height)
}
private resizeOverlayCanvasIfNeeded() {
@@ -211,12 +230,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
// size.
requestAnimationFrame(() => this.renderCanvas())
} else {
if (!this.gl) return;
if (!this.renderer) this.preprocess(this.props.flamechart)
this.gl.clear({
color: [1, 1, 1, 1],
depth: 1
})
this.renderRects()
this.renderOverlays()
}
@@ -225,8 +238,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
private canvasRef = (element?: Element) => {
if (element) {
this.canvas = element as HTMLCanvasElement
this.ctx = this.canvas.getContext('webgl')!
this.gl = regl(this.ctx)
this.renderCanvas()
} else {
this.canvas = null
@@ -410,31 +421,6 @@ export class FlamechartMinimapView extends Component<FlamechartMinimapViewProps,
this.updateCursor(configSpaceMouse)
}
private preprocess(flamechart: Flamechart) {
if (!this.canvas || !this.gl) return
console.time('minimap preprocess')
const configSpaceRects: Rect[] = []
const colors: vec3[] = []
const layers = flamechart.getLayers()
for (let i = 0; i < layers.length; i++) {
const layer = layers[i]
for (let flamechartFrame of layer) {
const configSpaceBounds = new Rect(
new Vec2(flamechartFrame.start, i),
new Vec2(flamechartFrame.end - flamechartFrame.start, 1)
)
configSpaceRects.push(configSpaceBounds)
const color = flamechart.getColorForFrame(flamechartFrame.node.frame)
colors.push([color.r, color.g, color.b])
}
}
this.renderer = rectangleBatchRenderer(this.gl, configSpaceRects, colors, 0)
this.viewportRectRenderer = viewportRectangleRenderer(this.gl);
console.timeEnd('minimap preprocess')
}
private overlayCanvasRef = (element?: Element) => {
if (element) {
this.overlayCanvas = element as HTMLCanvasElement
+38 -47
View File
@@ -1,3 +1,4 @@
import * as regl from 'regl'
import {h} from 'preact'
import {css} from 'aphrodite'
import {ReloadableComponent} from './reloadable'
@@ -5,12 +6,9 @@ import {ReloadableComponent} from './reloadable'
import { CallTreeNode } from './profile'
import { Flamechart, FlamechartFrame } from './flamechart'
import * as regl from 'regl'
import { vec3, Command, Instance } from 'regl'
import { Rect, Vec2, AffineTransform, clamp } from './math'
import { atMostOnceAFrame, cachedMeasureTextWidth } from "./utils";
import { rectangleBatchRenderer, RectangleBatchRendererProps } from "./rectangle-batch-renderer"
import { RectangleBatchRenderer, RectangleBatch } from "./rectangle-batch-renderer"
import { FlamechartMinimapView } from "./flamechart-minimap-view"
import { style, Sizes } from './flamechart-style'
@@ -74,6 +72,11 @@ const DEVICE_PIXEL_RATIO = window.devicePixelRatio
*/
interface FlamechartPanZoomViewProps {
flamechart: Flamechart
gl: regl.Instance
renderer: RectangleBatchRenderer
rectangles: RectangleBatch
setNodeHover: (node: CallTreeNode | null, logicalViewSpaceMouse: Vec2) => void
configSpaceViewportRect: Rect
transformViewport: (transform: AffineTransform) => void
@@ -81,10 +84,6 @@ interface FlamechartPanZoomViewProps {
}
export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoomViewProps, {}> {
renderer: Command<RectangleBatchRendererProps> | null = null
ctx: WebGLRenderingContext | null = null
gl: Instance | null = null
canvas: HTMLCanvasElement | null = null
overlayCanvas: HTMLCanvasElement | null = null
@@ -96,38 +95,9 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
this.props.setConfigSpaceViewportRect(r)
}
private preprocess(flamechart: Flamechart) {
if (!this.canvas || !this.gl) return
console.time('panzoom preprocess')
const configSpaceRects: Rect[] = []
const colors: vec3[] = []
const layers = flamechart.getLayers()
for (let i = 0; i < layers.length; i++) {
const layer = layers[i]
for (let flamechartFrame of layer) {
const configSpaceBounds = new Rect(
new Vec2(flamechartFrame.start, i+1),
new Vec2(flamechartFrame.end - flamechartFrame.start, 1)
)
configSpaceRects.push(configSpaceBounds)
const color = flamechart.getColorForFrame(flamechartFrame.node.frame)
colors.push([color.r, color.g, color.b])
}
}
this.renderer = rectangleBatchRenderer(this.gl, configSpaceRects, colors)
this.setConfigSpaceViewportRect(new Rect())
this.hoveredLabel = null
console.timeEnd('panzoom preprocess')
}
private canvasRef = (element?: Element) => {
if (element) {
this.canvas = element as HTMLCanvasElement
this.ctx = this.canvas.getContext('webgl')!
this.gl = regl(this.ctx)
this.renderCanvas()
} else {
this.canvas = null
@@ -319,7 +289,7 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
}
private resizeCanvasIfNeeded(windowResized = false) {
if (!this.canvas || !this.ctx) return
if (!this.canvas) return
let { width, height } = this.canvas.getBoundingClientRect()
const logicalHeight = height
width = Math.floor(width) * DEVICE_PIXEL_RATIO
@@ -351,8 +321,6 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
this.canvas.width = width
this.canvas.height = height
this.ctx.viewport(0, 0, width, height)
}
onWindowResize = () => {
@@ -361,17 +329,33 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
}
private renderRects() {
if (!this.renderer || !this.canvas) return
if (!this.canvas) return
this.resizeCanvasIfNeeded()
if (this.props.configSpaceViewportRect.isEmpty()) return
const configSpaceToNDC = this.physicalViewSpaceToNDC().times(this.configSpaceToPhysicalViewSpace())
this.renderer({
configSpaceToNDC: configSpaceToNDC,
physicalSize: this.physicalViewSize()
const bounds = this.canvas.getBoundingClientRect()
const physicalBounds = this.logicalToPhysicalViewSpace().transformRect(new Rect(
new Vec2(bounds.left, bounds.top),
new Vec2(bounds.width, bounds.height)
))
this.props.gl({
viewport: {
x: physicalBounds.left(),
y: window.devicePixelRatio * window.innerHeight - physicalBounds.top() - physicalBounds.height(),
width: physicalBounds.width(),
height: physicalBounds.height()
}
})(() => {
this.props.renderer.render({
configSpaceToNDC: configSpaceToNDC,
physicalSize: this.physicalViewSize(),
strokeSize: 1,
batch: this.props.rectangles
})
})
}
@@ -411,7 +395,6 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
// size.
requestAnimationFrame(() => this.renderCanvas())
} else {
if (!this.renderer) this.preprocess(this.props.flamechart)
this.renderRects()
this.renderOverlays()
}
@@ -584,7 +567,6 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
shouldComponentUpdate() { return false }
componentWillReceiveProps(nextProps: FlamechartPanZoomViewProps) {
if (this.props.flamechart !== nextProps.flamechart) {
this.renderer = null
this.renderCanvas()
} else if (this.props.configSpaceViewportRect !== nextProps.configSpaceViewportRect) {
this.renderCanvas()
@@ -622,6 +604,9 @@ export class FlamechartPanZoomView extends ReloadableComponent<FlamechartPanZoom
interface FlamechartViewProps {
flamechart: Flamechart
gl: regl.Instance
renderer: RectangleBatchRenderer
rectangles: RectangleBatch
}
interface FlamechartViewState {
@@ -751,10 +736,16 @@ export class FlamechartView extends ReloadableComponent<FlamechartViewProps, Fla
<FlamechartMinimapView
configSpaceViewportRect={this.state.configSpaceViewportRect}
transformViewport={this.transformViewport}
gl={this.props.gl}
renderer={this.props.renderer}
rectangles={this.props.rectangles}
setConfigSpaceViewportRect={this.setConfigSpaceViewportRect}
flamechart={this.props.flamechart} />
<FlamechartPanZoomView
ref={this.panZoomRef}
gl={this.props.gl}
renderer={this.props.renderer}
rectangles={this.props.rectangles}
flamechart={this.props.flamechart}
setNodeHover={this.onNodeHover}
transformViewport={this.transformViewport}
+140 -74
View File
@@ -1,44 +1,82 @@
import * as regl from 'regl'
import { Rect, Vec2, AffineTransform } from './math'
import { vec3 } from 'regl'
import { Rect, Vec2, AffineTransform } from './math'
import { Color } from './color'
export interface RectangleBatchRendererProps {
configSpaceToNDC: AffineTransform
physicalSize: Vec2
}
export class RectangleBatch {
private vertexCapacity = 60
private vertexCount = 0
private positions = new Float32Array(this.vertexCapacity * 6 * 2)
private physicalSpaceOffsets = new Float32Array(this.vertexCapacity * 6 * 2)
private vertexColors = new Float32Array(this.vertexCapacity * 6 * 3)
export const rectangleBatchRenderer = (gl: regl.Instance, rects: Rect[], colors: vec3[], strokeSize = 1) => {
const positions = new Float32Array(rects.length * 6 * 2)
const physicalSpaceOffsets = new Float32Array(rects.length * 6 * 2)
const vertexColors = new Float32Array(rects.length * 6 * 3)
private offset = {
topLeft: new Vec2(1, -1),
topRight: new Vec2(-1, -1),
bottomRight: new Vec2(-1, 1),
bottomLeft: new Vec2(1, 1)
}
constructor(private gl: regl.Instance) { }
const offset = {
topLeft: new Vec2(strokeSize, -strokeSize),
topRight: new Vec2(-strokeSize, -strokeSize),
bottomRight: new Vec2(-strokeSize, strokeSize),
bottomLeft: new Vec2(strokeSize, strokeSize)
getVertexCount() { return this.vertexCount }
private positionBuffer: regl.Buffer | null = null
getPositionBuffer() {
if (!this.positionBuffer) this.positionBuffer = this.gl.buffer(this.positions)
return this.positionBuffer
}
let vertexIndex = 0
function addVertex(y: number, x: number, offset: Vec2, color: vec3) {
const index = vertexIndex++
positions[index * 2] = x
positions[index * 2 + 1] = y
physicalSpaceOffsets[index * 2] = offset.x
physicalSpaceOffsets[index * 2 + 1] = offset.y
vertexColors[index * 3] = color[0]
vertexColors[index * 3 + 1] = color[1]
vertexColors[index * 3 + 2] = color[2]
private offsetBuffer: any = null
getPhysicalSpaceOffsetBuffer() {
if (!this.offsetBuffer) this.offsetBuffer = this.gl.buffer(this.physicalSpaceOffsets)
return this.offsetBuffer
}
for (let i = 0; i < rects.length; i++) {
const r = rects[i]
const color = colors[i]
private colorBuffer: any = null
getVertexColorBuffer() {
if (!this.colorBuffer) this.colorBuffer = this.gl.buffer(this.vertexColors)
return this.colorBuffer
}
const top = r.top()
const bottom = r.bottom()
const left = r.left()
const right = r.right()
uploadToGPU() {
this.getPositionBuffer()
this.getPhysicalSpaceOffsetBuffer()
this.getVertexColorBuffer()
}
private addVertex(y: number, x: number, offset: Vec2, color: vec3) {
const index = this.vertexCount++
if (index >= this.vertexCapacity) {
// Not enough capacity, time to resize! We'll double the capacity each time.
this.vertexCapacity *= 2
const positions = new Float32Array(this.vertexCapacity * 6 * 2)
const physicalSpaceOffsets = new Float32Array(this.vertexCapacity * 6 * 2)
const vertexColors = new Float32Array(this.vertexCapacity * 6 * 3)
positions.set(this.positions)
physicalSpaceOffsets.set(this.physicalSpaceOffsets)
vertexColors.set(this.vertexColors)
this.positions = positions
this.physicalSpaceOffsets = physicalSpaceOffsets
this.vertexColors = vertexColors
}
this.positions[index * 2] = x
this.positions[index * 2 + 1] = y
this.physicalSpaceOffsets[index * 2] = offset.x
this.physicalSpaceOffsets[index * 2 + 1] = offset.y
this.vertexColors[index * 3] = color[0]
this.vertexColors[index * 3 + 1] = color[1]
this.vertexColors[index * 3 + 2] = color[2]
}
addRect(rect: Rect, color: Color) {
const color_: vec3 = [color.r, color.g, color.b]
const top = rect.top()
const bottom = rect.bottom()
const left = rect.left()
const right = rect.right()
// 2 disjoint triangles.
//
@@ -46,19 +84,31 @@ export const rectangleBatchRenderer = (gl: regl.Instance, rects: Rect[], colors:
// | /|
// |/ |
// 3 +--+ 2
addVertex(top, left, offset.topLeft, color)
addVertex(bottom, left, offset.bottomLeft, color)
addVertex(top, right, offset.topRight, color)
this.addVertex(top, left, this.offset.topLeft, color_)
this.addVertex(bottom, left, this.offset.bottomLeft, color_)
this.addVertex(top, right, this.offset.topRight, color_)
addVertex(bottom, left, offset.bottomLeft, color)
addVertex(top, right, offset.topRight, color)
addVertex(bottom, right, offset.bottomRight, color)
this.addVertex(bottom, left, this.offset.bottomLeft, color_)
this.addVertex(top, right, this.offset.topRight, color_)
this.addVertex(bottom, right, this.offset.bottomRight, color_)
}
}
return gl<RectangleBatchRendererProps>({
vert: `
export interface RectangleBatchRendererProps {
configSpaceToNDC: AffineTransform
physicalSize: Vec2
strokeSize: number
batch: RectangleBatch
}
export class RectangleBatchRenderer {
private command: regl.Command<RectangleBatchRendererProps>
constructor(gl: regl.Instance) {
this.command = gl({
vert: `
uniform mat3 configSpaceToNDC;
uniform vec2 physicalSize;
uniform float strokeSize;
attribute vec2 position;
attribute vec3 color;
attribute vec2 physicalSpaceOffset;
@@ -69,15 +119,15 @@ export const rectangleBatchRenderer = (gl: regl.Instance, rects: Rect[], colors:
vec2 halfSize = physicalSize / 2.0;
vec2 physicalPixelSize = 2.0 / physicalSize;
roundedPosition = floor(roundedPosition * halfSize) / halfSize;
gl_Position = vec4(roundedPosition + physicalPixelSize * physicalSpaceOffset, 0, 1);
gl_Position = vec4(roundedPosition + physicalPixelSize * physicalSpaceOffset * strokeSize, 0, 1);
}
`,
depth: {
enable: false
},
depth: {
enable: false
},
frag: `
frag: `
precision mediump float;
varying vec3 vColor;
void main() {
@@ -85,38 +135,54 @@ export const rectangleBatchRenderer = (gl: regl.Instance, rects: Rect[], colors:
}
`,
attributes: {
position: {
buffer: gl.buffer(positions),
offset: 0,
stride: 2 * 4,
size: 2
attributes: {
position: (context, props) => {
return {
buffer: props.batch.getPositionBuffer(),
offset: 0,
stride: 2 * 4,
size: 2
}
},
physicalSpaceOffset: (context, props) => {
return {
buffer: props.batch.getPhysicalSpaceOffsetBuffer(),
offset: 0,
stride: 2 * 4,
size: 2
}
},
color: (context, props) => {
return {
buffer: props.batch.getVertexColorBuffer(),
offset: 0,
stride: 3 * 4,
size: 3
}
}
},
physicalSpaceOffset: {
buffer: gl.buffer(physicalSpaceOffsets),
offset: 0,
stride: 2 * 4,
size: 2
uniforms: {
configSpaceToNDC: (context, props) => {
return props.configSpaceToNDC.flatten()
},
physicalSize: (context, props) => {
return props.physicalSize.flatten()
},
strokeSize: (context, props) => {
return props.strokeSize
}
},
color: {
buffer: gl.buffer(vertexColors),
offset: 0,
stride: 3 * 4,
size: 3
primitive: 'triangles',
count: (context, props) => {
return props.batch.getVertexCount()
}
},
})
}
uniforms: {
configSpaceToNDC: (context, props) => {
return props.configSpaceToNDC.flatten()
},
physicalSize: (context, props) => {
return props.physicalSize.flatten()
}
},
primitive: 'triangles',
count: vertexColors.length / 3
})
render(props: RectangleBatchRendererProps) {
this.command(props)
}
}
Vendored
+1 -1
View File
@@ -285,7 +285,7 @@ declare module "regl" {
uniforms?: { [uniformName: string]: MaybeComputed<P, Uniform> }
attributes: { [attributeName: string]: MaybeComputed<P, Attribute> }
attributes?: { [attributeName: string]: MaybeComputed<P, Attribute> }
primitive?: DrawMode