Skip to content

Commit 439b613

Browse files
[wip] Remove instanced debug rendering
1 parent f4f59e7 commit 439b613

File tree

5 files changed

+129
-144
lines changed

5 files changed

+129
-144
lines changed

src/data/array_types.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,46 @@ class StructArrayLayout2i2i2i12 extends StructArray {
383383
StructArrayLayout2i2i2i12.prototype.bytesPerElement = 12;
384384
register('StructArrayLayout2i2i2i12', StructArrayLayout2i2i2i12);
385385

386+
/**
387+
* Implementation of the StructArray layout:
388+
* [0]: Float32[2]
389+
* [8]: Float32[1]
390+
* [12]: Int16[2]
391+
*
392+
* @private
393+
*/
394+
class StructArrayLayout2f1f2i16 extends StructArray {
395+
uint8: Uint8Array;
396+
float32: Float32Array;
397+
int16: Int16Array;
398+
399+
_refreshViews() {
400+
this.uint8 = new Uint8Array(this.arrayBuffer);
401+
this.float32 = new Float32Array(this.arrayBuffer);
402+
this.int16 = new Int16Array(this.arrayBuffer);
403+
}
404+
405+
emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number) {
406+
const i = this.length;
407+
this.resize(i + 1);
408+
return this.emplace(i, v0, v1, v2, v3, v4);
409+
}
410+
411+
emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number) {
412+
const o4 = i * 4;
413+
const o2 = i * 8;
414+
this.float32[o4 + 0] = v0;
415+
this.float32[o4 + 1] = v1;
416+
this.float32[o4 + 2] = v2;
417+
this.int16[o2 + 6] = v3;
418+
this.int16[o2 + 7] = v4;
419+
return i;
420+
}
421+
}
422+
423+
StructArrayLayout2f1f2i16.prototype.bytesPerElement = 16;
424+
register('StructArrayLayout2f1f2i16', StructArrayLayout2f1f2i16);
425+
386426
/**
387427
* Implementation of the StructArray layout:
388428
* [0]: Uint8[2]
@@ -1063,6 +1103,7 @@ export {
10631103
StructArrayLayout1ul4,
10641104
StructArrayLayout6i1ul2ui20,
10651105
StructArrayLayout2i2i2i12,
1106+
StructArrayLayout2f1f2i16,
10661107
StructArrayLayout2ub2f12,
10671108
StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48,
10681109
StructArrayLayout8i15ui1ul4f68,
@@ -1086,7 +1127,7 @@ export {
10861127
StructArrayLayout3f12 as SymbolDynamicLayoutArray,
10871128
StructArrayLayout1ul4 as SymbolOpacityArray,
10881129
StructArrayLayout2i2i2i12 as CollisionBoxLayoutArray,
1089-
StructArrayLayout2i4 as CollisionCircleLayoutArray,
1130+
StructArrayLayout2f1f2i16 as CollisionCircleLayoutArray,
10901131
StructArrayLayout2ub2f12 as CollisionVertexArray,
10911132
StructArrayLayout3ui6 as TriangleIndexArray,
10921133
StructArrayLayout2ui4 as LineIndexArray,

src/data/bucket/symbol_attributes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ export const collisionBoxLayout = createLayout([ // used to render collision box
4747
], 4);
4848

4949
export const collisionCircleLayout = createLayout([ // used to render collision circles for debugging purposes
50-
{name: 'a_idx', components: 2, type: 'Int16'}
50+
{name: 'a_pos', components: 2, type: 'Float32'},
51+
{name: 'a_radius', components: 1, type: 'Float32'},
52+
{name: 'a_flags', components: 2, type: 'Int16'}
5153
], 4);
5254

5355
export const placement = createLayout([

src/render/draw_collision_debug.js

Lines changed: 77 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import StencilMode from '../gl/stencil_mode';
1010
import CullFaceMode from '../gl/cull_face_mode';
1111
import {collisionUniformValues, collisionCircleUniformValues} from './program/collision_program';
1212

13-
import {StructArrayLayout2i4, StructArrayLayout3ui6} from '../data/array_types';
13+
import {StructArrayLayout2i4, StructArrayLayout3ui6, CollisionCircleLayoutArray} from '../data/array_types';
1414
import {collisionCircleLayout} from '../data/bucket/symbol_attributes';
1515
import SegmentVector from '../data/segment';
1616
import {mat4} from 'gl-matrix';
@@ -56,42 +56,20 @@ function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: S
5656
}
5757

5858
function drawCollisionCircles(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array<OverscaledTileID>, translate: [number, number], translateAnchor: 'map' | 'viewport') {
59-
// Collision circle rendering is done by using simple shader batching scheme where dynamic properties of
60-
// circles are passed to the GPU using shader uniforms. Circles are first encoded into 4-component vectors
61-
// (center_x, center_y, radius, flag) and then uploaded in batches as "uniform vec4 u_quads[N]".
62-
// Vertex data is just a collection of incremental index values pointing to the quads-array.
63-
//
64-
// If one quad uses 4 vertices then all required values can be deduced from the index value:
65-
// int quad_idx = int(vertex.idx / 4);
66-
// int corner_idx = int(vertex.idx % 4);
67-
//
68-
// OpenGL ES 2.0 spec defines that the maximum number of supported vertex uniform vectors (vec4) should be
69-
// at least 128. Choosing a safe value 64 for the quad array should leave enough space for rest of the
70-
// uniform variables.
71-
const maxQuadsPerDrawCall = 64;
72-
73-
if (!quadVertices) {
74-
quadVertices = createQuadVertices(maxQuadsPerDrawCall);
75-
}
76-
if (!quadTriangles) {
77-
quadTriangles = createQuadTriangles(maxQuadsPerDrawCall);
78-
}
7959

80-
const context = painter.context;
81-
const quadVertexBuffer = context.createVertexBuffer(quadVertices, collisionCircleLayout.members, true);
82-
const quadIndexBuffer = context.createIndexBuffer(quadTriangles, true);
83-
84-
const quads = new Float32Array(maxQuadsPerDrawCall * 4);
60+
let tileBatches = [];
61+
let circleCount = 0;
62+
let circleOffset = 0;
8563

8664
for (let i = 0; i < coords.length; i++) {
8765
const coord = coords[i];
8866
const tile = sourceCache.getTile(coord);
8967
const bucket: ?SymbolBucket = (tile.getBucket(layer): any);
9068
if (!bucket) continue;
9169

92-
const arr = bucket.collisionCircleArray;
70+
const circleArray = bucket.collisionCircleArray;
9371

94-
if (!arr.length)
72+
if (!circleArray.length)
9573
continue;
9674

9775
let posMatrix = coord.posMatrix;
@@ -103,92 +81,88 @@ function drawCollisionCircles(painter: Painter, sourceCache: SourceCache, layer:
10381
// We need to know the projection matrix that was used for projecting collision circles to the screen.
10482
// This might vary between buckets as the symbol placement is a continous process. This matrix is
10583
// required for transforming points from previous screen space to the current one
106-
const batchInvTransform = mat4.create();
107-
const batchTransform = posMatrix;
108-
109-
mat4.mul(batchInvTransform, bucket.placementInvProjMatrix, painter.transform.glCoordMatrix);
110-
mat4.mul(batchInvTransform, batchInvTransform, bucket.placementViewportMatrix);
111-
112-
let batchIdx = 0;
113-
let quadOffset = 0;
114-
115-
while (quadOffset < arr.length) {
116-
const quadsLeft = arr.length - quadOffset;
117-
const quadSpaceInBatch = maxQuadsPerDrawCall - batchIdx;
118-
const batchSize = Math.min(quadsLeft, quadSpaceInBatch);
119-
120-
// Copy collision circles from the bucket array
121-
for (let qIdx = quadOffset; qIdx < quadOffset + batchSize; qIdx++) {
122-
quads[batchIdx * 4 + 0] = arr.float32[qIdx * 4 + 0]; // width
123-
quads[batchIdx * 4 + 1] = arr.float32[qIdx * 4 + 1]; // height
124-
quads[batchIdx * 4 + 2] = arr.float32[qIdx * 4 + 2]; // radius
125-
quads[batchIdx * 4 + 3] = arr.float32[qIdx * 4 + 3]; // collisionFlag
126-
batchIdx++;
127-
}
128-
129-
quadOffset += batchSize;
130-
131-
if (batchIdx === maxQuadsPerDrawCall) {
132-
drawBatch(painter, batchTransform, batchInvTransform, quads, batchIdx, layer.id, quadVertexBuffer, quadIndexBuffer);
133-
batchIdx = 0;
134-
}
135-
}
84+
const invTransform = mat4.create();
85+
const transform = posMatrix;
13686

137-
// Render the leftover batch
138-
if (batchIdx > 0) {
139-
drawBatch(painter, batchTransform, batchInvTransform, quads, batchIdx, layer.id, quadVertexBuffer, quadIndexBuffer);
140-
}
87+
mat4.mul(invTransform, bucket.placementInvProjMatrix, painter.transform.glCoordMatrix);
88+
mat4.mul(invTransform, invTransform, bucket.placementViewportMatrix);
89+
90+
tileBatches.push({
91+
circleArray,
92+
circleOffset,
93+
transform,
94+
invTransform
95+
});
96+
97+
circleCount += circleArray.length;
98+
circleOffset = circleCount;
14199
}
142100

143-
quadIndexBuffer.destroy();
144-
quadVertexBuffer.destroy();
145-
}
101+
if (!tileBatches.length)
102+
return;
146103

147-
function drawBatch(painter: Painter, proj: mat4, invPrevProj: mat4, quads: any, numQuads: number, layerId: string, vb: VertexBuffer, ib: IndexBuffer) {
148104
const context = painter.context;
149105
const gl = context.gl;
150106
const circleProgram = painter.useProgram('collisionCircle');
151107

152-
const uniforms = collisionCircleUniformValues(
153-
proj,
154-
invPrevProj,
155-
quads,
156-
painter.transform);
157-
158-
circleProgram.draw(
159-
context,
160-
gl.TRIANGLES,
161-
DepthMode.disabled,
162-
StencilMode.disabled,
163-
painter.colorModeForRenderPass(),
164-
CullFaceMode.disabled,
165-
uniforms,
166-
layerId,
167-
vb,
168-
ib,
169-
SegmentVector.simpleSegment(0, 0, numQuads * 4, numQuads * 2),
170-
null,
171-
painter.transform.zoom,
172-
null,
173-
null,
174-
null);
175-
}
176-
177-
function createQuadVertices(quadCount: number): StructArrayLayout2i4 {
178-
const vCount = quadCount * 4;
179-
const array = new StructArrayLayout2i4();
180-
181-
array.resize(vCount);
182-
array._trim();
108+
// Construct vertex data
109+
const vertexData = new CollisionCircleLayoutArray();
110+
vertexData.resize(circleCount * 4);
111+
vertexData._trim();
112+
113+
let vertexOffset = 0;
114+
115+
for (const batch of tileBatches) {
116+
for (let i = 0; i < batch.circleArray.length; i++) {
117+
const circleIdx = i * 4;
118+
const x = batch.circleArray.float32[circleIdx + 0];
119+
const y = batch.circleArray.float32[circleIdx + 1];
120+
const radius = batch.circleArray.float32[circleIdx + 2];
121+
const collision = batch.circleArray.float32[circleIdx + 3];
122+
123+
// 4 floats per vertex, 4 vertices per quad
124+
vertexData.emplace(vertexOffset++, x, y, radius, collision, 0);
125+
vertexData.emplace(vertexOffset++, x, y, radius, collision, 1);
126+
vertexData.emplace(vertexOffset++, x, y, radius, collision, 2);
127+
vertexData.emplace(vertexOffset++, x, y, radius, collision, 3);
128+
}
129+
}
130+
if (!quadTriangles || quadTriangles.length < circleCount * 2) {
131+
quadTriangles = createQuadTriangles(circleCount);
132+
}
183133

184-
// Fill the buffer with an incremental index value (2 per vertex)
185-
// [0, 0, 1, 1, 2, 2, 3, 3, 4, 4...]
186-
for (let i = 0; i < vCount; i++) {
187-
array.int16[i * 2 + 0] = i;
188-
array.int16[i * 2 + 1] = i;
134+
const indexBuffer = context.createIndexBuffer(quadTriangles, true);
135+
const vertexBuffer = context.createVertexBuffer(vertexData, collisionCircleLayout.members, true);
136+
137+
// Render batches
138+
for (let batch of tileBatches) {
139+
const uniforms = collisionCircleUniformValues(
140+
batch.transform,
141+
batch.invTransform,
142+
painter.transform
143+
);
144+
145+
circleProgram.draw(
146+
context,
147+
gl.TRIANGLES,
148+
DepthMode.disabled,
149+
StencilMode.disabled,
150+
painter.colorModeForRenderPass(),
151+
CullFaceMode.disabled,
152+
uniforms,
153+
layer.id,
154+
vertexBuffer,
155+
indexBuffer,
156+
SegmentVector.simpleSegment(0, batch.circleOffset * 2, batch.circleArray.length * 4, batch.circleArray.length * 2),
157+
null,
158+
painter.transform.zoom,
159+
null,
160+
null,
161+
null);
189162
}
190163

191-
return array;
164+
vertexBuffer.destroy();
165+
indexBuffer.destroy();
192166
}
193167

194168
function createQuadTriangles(quadCount: number): StructArrayLayout3ui6 {

src/render/program/collision_program.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export type CollisionUniformsType = {|
2424
export type CollisionCircleUniformsType = {|
2525
'u_matrix': UniformMatrix4f,
2626
'u_inv_matrix': UniformMatrix4f,
27-
'u_quads': Uniform4fv,
2827
'u_camera_to_center_distance': Uniform1f,
2928
'u_viewport_size': Uniform2f
3029
|};
@@ -40,7 +39,6 @@ const collisionUniforms = (context: Context, locations: UniformLocations): Colli
4039
const collisionCircleUniforms = (context: Context, locations: UniformLocations): CollisionCircleUniformsType => ({
4140
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
4241
'u_inv_matrix': new UniformMatrix4f(context, locations.u_inv_matrix),
43-
'u_quads': new Uniform4fv(context, locations.u_quads),
4442
'u_camera_to_center_distance': new Uniform1f(context, locations.u_camera_to_center_distance),
4543
'u_viewport_size': new Uniform2f(context, locations.u_viewport_size)
4644
});
@@ -66,13 +64,11 @@ const collisionUniformValues = (
6664
const collisionCircleUniformValues = (
6765
matrix: Float32Array,
6866
invMatrix: Float32Array,
69-
quads: Float32Array,
7067
transform: Transform
7168
): UniformValues<CollisionCircleUniformsType> => {
7269
return {
7370
'u_matrix': matrix,
7471
'u_inv_matrix': invMatrix,
75-
'u_quads': quads,
7672
'u_camera_to_center_distance': transform.cameraToCenterDistance,
7773
'u_viewport_size': [transform.width, transform.height]
7874
};

src/shaders/collision_circle.vertex.glsl

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,12 @@
1-
// This shader implements a simple geometry instancing using uniform vector arrays. Per-circle data is stored
2-
// in u_quads array (circles are rendered as quads) which is then referenced by vertices of different quads.
3-
//
4-
// It is possible to deduce all variables required to render sequential quads by using a single incremental
5-
// index value as the only vertex data. If one quad uses 4 vertices then index of the quad can be found
6-
// quad_idx = floor(vertex.idx / 4) and vertex_idx = vertex.idx % 4.
7-
//
8-
// 1 2 vertex offsets:
9-
// *----* 0: vec2(-1, -1)
10-
// | /| 1: vec2(-1, 1)
11-
// | / | 2: vec2(1, 1)
12-
// | / | 3: vec2(1, -1)
13-
// |/ |
14-
// *----*
15-
// 0 3
16-
//
17-
18-
attribute vec2 a_idx;
1+
attribute vec2 a_pos;
2+
attribute float a_radius;
3+
attribute vec2 a_flags;
194

205
uniform mat4 u_matrix;
216
uniform mat4 u_inv_matrix;
227
uniform vec2 u_viewport_size;
238
uniform float u_camera_to_center_distance;
249

25-
// Rendering information of each quad is packed into a single uniform array.
26-
// NOTE: all values are in screen space (ie. in pixels) already!
27-
// x: center_x
28-
// y: center_y
29-
// z: radius
30-
// w: collision flag [0, 1]
31-
uniform vec4 u_quads[64];
32-
3310
varying float v_radius;
3411
varying vec2 v_extrude;
3512
varying float v_perspective_ratio;
@@ -48,15 +25,10 @@ vec3 toTilePosition(vec2 screenPos) {
4825
}
4926

5027
void main() {
51-
52-
highp float vertexIdx = mod(a_idx.x, 4.0);
53-
54-
// Get the quad this vertex belongs to
55-
vec4 quad = u_quads[int(floor(a_idx.x / 4.0))];
56-
57-
vec2 quadCenterPos = quad.xy;
58-
highp float radius = quad.z;
59-
highp float collision = quad.w;
28+
vec2 quadCenterPos = a_pos;
29+
float radius = a_radius;
30+
float collision = a_flags.x;
31+
float vertexIdx = a_flags.y;
6032

6133
vec2 quadVertexOffset = vec2(
6234
mix(-1.0, 1.0, float(vertexIdx >= 2.0)),

0 commit comments

Comments
 (0)