Render rectangle batches via instancing
This commit is contained in:
@@ -21,6 +21,7 @@ export class CanvasContext {
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.gl = regl({
|
||||
canvas: canvas,
|
||||
extensions: ['ANGLE_instanced_arrays'],
|
||||
optionalExtensions: ['EXT_disjoint_timer_query'],
|
||||
profile: true
|
||||
})
|
||||
|
||||
+91
-84
@@ -4,93 +4,67 @@ import { Rect, Vec2, AffineTransform } from './math'
|
||||
import { Color } from './color'
|
||||
|
||||
export class RectangleBatch {
|
||||
private vertexCapacity = 60
|
||||
private vertexCount = 0
|
||||
private positions = new Float32Array(this.vertexCapacity * 2)
|
||||
private physicalSpaceOffsets = new Float32Array(this.vertexCapacity * 2)
|
||||
private vertexColors = new Float32Array(this.vertexCapacity * 3)
|
||||
private rectCapacity = 10
|
||||
private rectCount = 0
|
||||
|
||||
private configSpaceOffsets = new Float32Array(this.rectCapacity * 2)
|
||||
private configSpaceSizes = new Float32Array(this.rectCapacity * 2)
|
||||
private colors = new Float32Array(this.rectCapacity * 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) { }
|
||||
|
||||
getVertexCount() { return this.vertexCount }
|
||||
getRectCount() { return this.rectCount }
|
||||
|
||||
private positionBuffer: regl.Buffer | null = null
|
||||
getPositionBuffer() {
|
||||
if (!this.positionBuffer) this.positionBuffer = this.gl.buffer(this.positions)
|
||||
return this.positionBuffer
|
||||
private configSpaceOffsetBuffer: regl.Buffer | null = null
|
||||
getConfigSpaceOffsetBuffer() {
|
||||
if (!this.configSpaceOffsetBuffer) this.configSpaceOffsetBuffer = this.gl.buffer(this.configSpaceOffsets)
|
||||
return this.configSpaceOffsetBuffer
|
||||
}
|
||||
|
||||
private offsetBuffer: any = null
|
||||
getPhysicalSpaceOffsetBuffer() {
|
||||
if (!this.offsetBuffer) this.offsetBuffer = this.gl.buffer(this.physicalSpaceOffsets)
|
||||
return this.offsetBuffer
|
||||
private configSpaceSizeBuffer: regl.Buffer | null = null
|
||||
getConfigSpaceSizeBuffer() {
|
||||
if (!this.configSpaceSizeBuffer) this.configSpaceSizeBuffer = this.gl.buffer(this.configSpaceSizes)
|
||||
return this.configSpaceSizeBuffer
|
||||
}
|
||||
|
||||
private colorBuffer: any = null
|
||||
getVertexColorBuffer() {
|
||||
if (!this.colorBuffer) this.colorBuffer = this.gl.buffer(this.vertexColors)
|
||||
private colorBuffer: regl.Buffer | null = null
|
||||
getColorBuffer() {
|
||||
if (!this.colorBuffer) this.colorBuffer = this.gl.buffer(this.colors)
|
||||
return this.colorBuffer
|
||||
}
|
||||
|
||||
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 * 2)
|
||||
const physicalSpaceOffsets = new Float32Array(this.vertexCapacity * 2)
|
||||
const vertexColors = new Float32Array(this.vertexCapacity * 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]
|
||||
this.getConfigSpaceOffsetBuffer()
|
||||
this.getConfigSpaceSizeBuffer()
|
||||
this.getColorBuffer()
|
||||
}
|
||||
|
||||
addRect(rect: Rect, color: Color) {
|
||||
const color_: vec3 = [color.r, color.g, color.b]
|
||||
const index = this.rectCount++
|
||||
if (index >= this.rectCapacity) {
|
||||
// Not enough capacity, time to resize! We'll double the capacity each time.
|
||||
this.rectCapacity *= 2
|
||||
const configSpaceOffsets = new Float32Array(this.rectCapacity * 2)
|
||||
const configSpaceSizes = new Float32Array(this.rectCapacity * 2)
|
||||
const colors = new Float32Array(this.rectCapacity * 3)
|
||||
|
||||
const top = rect.top()
|
||||
const bottom = rect.bottom()
|
||||
const left = rect.left()
|
||||
const right = rect.right()
|
||||
configSpaceOffsets.set(this.configSpaceOffsets)
|
||||
configSpaceSizes.set(this.configSpaceSizes)
|
||||
colors.set(this.colors)
|
||||
|
||||
// 2 disjoint triangles.
|
||||
//
|
||||
// 0 +--+ 1
|
||||
// | /|
|
||||
// |/ |
|
||||
// 3 +--+ 2
|
||||
this.addVertex(top, left, this.offset.topLeft, color_)
|
||||
this.addVertex(bottom, left, this.offset.bottomLeft, color_)
|
||||
this.addVertex(top, right, this.offset.topRight, color_)
|
||||
this.configSpaceOffsets = configSpaceOffsets
|
||||
this.configSpaceSizes = configSpaceSizes
|
||||
this.colors = colors
|
||||
}
|
||||
this.configSpaceOffsets[index * 2] = rect.origin.x
|
||||
this.configSpaceOffsets[index * 2 + 1] = rect.origin.y
|
||||
|
||||
this.addVertex(bottom, left, this.offset.bottomLeft, color_)
|
||||
this.addVertex(top, right, this.offset.topRight, color_)
|
||||
this.addVertex(bottom, right, this.offset.bottomRight, color_)
|
||||
this.configSpaceSizes[index * 2] = rect.size.x
|
||||
this.configSpaceSizes[index * 2 + 1] = rect.size.y
|
||||
|
||||
this.colors[index * 3] = color.r
|
||||
this.colors[index * 3 + 1] = color.g
|
||||
this.colors[index * 3 + 2] = color.b
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,13 +83,37 @@ export class RectangleBatchRenderer {
|
||||
uniform mat3 configSpaceToNDC;
|
||||
uniform vec2 physicalSize;
|
||||
uniform float strokeSize;
|
||||
attribute vec2 position;
|
||||
|
||||
// Non-instanced
|
||||
attribute float corner;
|
||||
|
||||
// Instanced
|
||||
attribute vec2 configSpaceOffset;
|
||||
attribute vec2 configSpaceSize;
|
||||
attribute vec3 color;
|
||||
attribute vec2 physicalSpaceOffset;
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vColor = color;
|
||||
vec2 roundedPosition = (configSpaceToNDC * vec3(position.xy, 1)).xy;
|
||||
|
||||
vec2 configSpacePos = configSpaceOffset;
|
||||
vec2 physicalSpaceOffset = vec2(0, 0);
|
||||
|
||||
// Corners go NW, NE, SW, SE
|
||||
if (corner == 0.0) {
|
||||
physicalSpaceOffset = vec2(1, -1);
|
||||
} else if (corner == 1.0) {
|
||||
physicalSpaceOffset = vec2(-1, -1);
|
||||
configSpacePos.x += configSpaceSize.x;
|
||||
} else if (corner == 2.0) {
|
||||
physicalSpaceOffset = vec2(1, 1);
|
||||
configSpacePos.y += configSpaceSize.y;
|
||||
} else if (corner == 3.0) {
|
||||
physicalSpaceOffset = vec2(-1, 1);
|
||||
configSpacePos += configSpaceSize;
|
||||
}
|
||||
|
||||
vec2 roundedPosition = (configSpaceToNDC * vec3(configSpacePos, 1)).xy;
|
||||
vec2 halfSize = physicalSize / 2.0;
|
||||
vec2 physicalPixelSize = 2.0 / physicalSize;
|
||||
roundedPosition = floor(roundedPosition * halfSize) / halfSize;
|
||||
@@ -136,28 +134,35 @@ export class RectangleBatchRenderer {
|
||||
`,
|
||||
|
||||
attributes: {
|
||||
position: (context, props) => {
|
||||
// Non-instanced attributes
|
||||
corner: gl.buffer(new Float32Array([0, 1, 2, 3])),
|
||||
|
||||
// Instanced attributes
|
||||
configSpaceOffset: (context, props) => {
|
||||
return {
|
||||
buffer: props.batch.getPositionBuffer(),
|
||||
buffer: props.batch.getConfigSpaceOffsetBuffer(),
|
||||
offset: 0,
|
||||
stride: 2 * 4,
|
||||
size: 2
|
||||
size: 2,
|
||||
divisor: 1
|
||||
}
|
||||
},
|
||||
physicalSpaceOffset: (context, props) => {
|
||||
configSpaceSize: (context, props) => {
|
||||
return {
|
||||
buffer: props.batch.getPhysicalSpaceOffsetBuffer(),
|
||||
buffer: props.batch.getConfigSpaceSizeBuffer(),
|
||||
offset: 0,
|
||||
stride: 2 * 4,
|
||||
size: 2
|
||||
size: 2,
|
||||
divisor: 1
|
||||
}
|
||||
},
|
||||
color: (context, props) => {
|
||||
return {
|
||||
buffer: props.batch.getVertexColorBuffer(),
|
||||
buffer: props.batch.getColorBuffer(),
|
||||
offset: 0,
|
||||
stride: 3 * 4,
|
||||
size: 3
|
||||
size: 3,
|
||||
divisor: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -174,11 +179,13 @@ export class RectangleBatchRenderer {
|
||||
}
|
||||
},
|
||||
|
||||
primitive: 'triangles',
|
||||
instances: (context, props) => {
|
||||
return props.batch.getRectCount()
|
||||
},
|
||||
|
||||
count: (context, props) => {
|
||||
return props.batch.getVertexCount()
|
||||
}
|
||||
count: 4,
|
||||
|
||||
primitive: 'triangle strip',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user