Skip to content

Commit dae7208

Browse files
viewspace symbol collision PoC
1 parent 4bc9b85 commit dae7208

14 files changed

+452
-133
lines changed

src/data/bucket/symbol_attributes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export const collisionCircleLayout = createLayout([ // used to render collision
5858
{name: 'a_extrude', components: 2, type: 'Int16'}
5959
], 4);
6060

61+
export const collisionCircleLayoutTemp = createLayout([ // used to render collision circles for debugging purposes
62+
{name: 'a_pos', components: 2, type: 'Int16'},
63+
{name: 'a_reserved', components: 2, type: 'Int16'}
64+
], 4);
65+
6166
export const placement = createLayout([
6267
{type: 'Int16', name: 'anchorX'},
6368
{type: 'Int16', name: 'anchorY'},

src/data/bucket/symbol_bucket.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ class SymbolBucket implements Bucket {
317317
sortedAngle: number;
318318
featureSortOrder: Array<number>;
319319

320+
collisionCircleArrayTemp: Array<number>;
321+
320322
text: SymbolBuffers;
321323
icon: SymbolBuffers;
322324
textCollisionBox: CollisionBuffers;
@@ -346,6 +348,8 @@ class SymbolBucket implements Bucket {
346348
this.hasRTLText = false;
347349
this.sortKeyRanges = [];
348350

351+
this.collisionCircleArrayTemp = [];
352+
349353
const layer = this.layers[0];
350354
const unevaluatedLayoutValues = layer._unevaluatedLayout._values;
351355

src/gl/vertex_buffer.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,13 @@ class VertexBuffer {
6262
this.context.bindVertexBuffer.set(this.buffer);
6363
}
6464

65-
updateData(array: StructArray) {
66-
assert(array.length === this.length);
65+
updateData(array: StructArray, elementOffset: ?number) {
66+
elementOffset = elementOffset || 0;
67+
assert(elementOffset + array.length <= this.length);
68+
const byteOffset = elementOffset * array.bytesPerElement;
6769
const gl = this.context.gl;
6870
this.bind();
69-
gl.bufferSubData(gl.ARRAY_BUFFER, 0, array.arrayBuffer);
71+
gl.bufferSubData(gl.ARRAY_BUFFER, byteOffset, array.arrayBuffer);
7072
}
7173

7274
enableAttributes(gl: WebGLRenderingContext, program: Program<*>) {

src/render/draw_collision_debug.js

Lines changed: 183 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,207 @@ import type SymbolBucket from '../data/bucket/symbol_bucket';
88
import DepthMode from '../gl/depth_mode';
99
import StencilMode from '../gl/stencil_mode';
1010
import CullFaceMode from '../gl/cull_face_mode';
11-
import {collisionUniformValues} from './program/collision_program';
11+
import {collisionUniformValues, collisionUniformValuesTemp} from './program/collision_program';
12+
13+
import {StructArrayLayout4i8, StructArrayLayout3ui6} from '../data/array_types'
14+
import {collisionCircleLayoutTemp} from '../data/bucket/symbol_attributes';
15+
import SegmentVector from '../data/segment';
16+
import { mat4, vec4 } from 'gl-matrix';
1217

1318
export default drawCollisionDebug;
1419

20+
//const matrixCache = {};
21+
1522
function drawCollisionDebugGeometry(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array<OverscaledTileID>, drawCircles: boolean,
1623
translate: [number, number], translateAnchor: 'map' | 'viewport', isText: boolean) {
1724
const context = painter.context;
1825
const gl = context.gl;
1926
const program = drawCircles ? painter.useProgram('collisionCircle') : painter.useProgram('collisionBox');
2027

28+
if (!drawCircles) {
29+
for (let i = 0; i < coords.length; i++) {
30+
const coord = coords[i];
31+
const tile = sourceCache.getTile(coord);
32+
const bucket: ?SymbolBucket = (tile.getBucket(layer): any);
33+
if (!bucket) continue;
34+
const buffers = drawCircles ? (isText ? bucket.textCollisionCircle : bucket.iconCollisionCircle) : (isText ? bucket.textCollisionBox : bucket.iconCollisionBox);
35+
if (!buffers) continue;
36+
let posMatrix = coord.posMatrix;
37+
if (translate[0] !== 0 || translate[1] !== 0) {
38+
posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor);
39+
}
40+
program.draw(context, drawCircles ? gl.TRIANGLES : gl.LINES,
41+
DepthMode.disabled, StencilMode.disabled,
42+
painter.colorModeForRenderPass(),
43+
CullFaceMode.disabled,
44+
collisionUniformValues(
45+
posMatrix,
46+
painter.transform,
47+
tile),
48+
layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer,
49+
buffers.segments, null, painter.transform.zoom, null, null,
50+
buffers.collisionVertexBuffer);
51+
}
52+
}
53+
54+
// Render collision circles
55+
const program2 = painter.useProgram('collisionCircleTemp');
56+
57+
const maxCirclesPerBatch = 4096;
58+
const maxVerticesPerBatch = maxCirclesPerBatch * 4;
59+
const maxIndicesPerBatch = maxCirclesPerBatch * 6;
60+
61+
if (!('vertexBuffer2' in layer)) {
62+
const array = new StructArrayLayout4i8();
63+
64+
// use temporary array reserve space for x circles (4 vertex per quad)
65+
array.resize(maxVerticesPerBatch);
66+
array._trim();
67+
68+
layer.vertexBuffer2 = context.createVertexBuffer(array, collisionCircleLayoutTemp.members, true);
69+
}
70+
71+
if (!('indexBuffer2' in layer)) {
72+
const array = new StructArrayLayout3ui6();
73+
74+
// Pre-generate index buffer for quads
75+
array.resize(maxIndicesPerBatch);
76+
array._trim();
77+
78+
for (let i = 0; i < maxCirclesPerBatch; i++) {
79+
const idx = i * 6;
80+
81+
array.uint16[idx + 0] = i * 4 + 0;
82+
array.uint16[idx + 1] = i * 4 + 1;
83+
array.uint16[idx + 2] = i * 4 + 2;
84+
array.uint16[idx + 3] = i * 4 + 2;
85+
array.uint16[idx + 4] = i * 4 + 3;
86+
array.uint16[idx + 5] = i * 4 + 0;
87+
}
88+
89+
layer.indexBuffer2 = context.createIndexBuffer(array, true);
90+
}
91+
92+
let colorFlip = false;
93+
94+
// Gather collision circles of tiles and render them in batches
95+
const batchVertices = new StructArrayLayout4i8();
96+
batchVertices.resize(maxVerticesPerBatch);
97+
batchVertices._trim();
98+
99+
const appendVertex = (idx, x, y, z, w) => {
100+
batchVertices.int16[idx * 4 + 0] = x; // anchor center
101+
batchVertices.int16[idx * 4 + 1] = y; // anchor center
102+
batchVertices.int16[idx * 4 + 2] = z; // radius
103+
batchVertices.int16[idx * 4 + 3] = w; // radius
104+
}
105+
21106
for (let i = 0; i < coords.length; i++) {
22107
const coord = coords[i];
23108
const tile = sourceCache.getTile(coord);
24109
const bucket: ?SymbolBucket = (tile.getBucket(layer): any);
25110
if (!bucket) continue;
26-
const buffers = drawCircles ? (isText ? bucket.textCollisionCircle : bucket.iconCollisionCircle) : (isText ? bucket.textCollisionBox : bucket.iconCollisionBox);
27-
if (!buffers) continue;
111+
112+
const arr = bucket.collisionCircleArrayTemp;
113+
114+
if (!arr.length)
115+
continue;
116+
117+
// Collision circle rendering is a little more complex now that they're stored in screen coordinates.
118+
// Screen space positions of previous frames can be reused by transforming them first to tile space and
119+
// then to the new clip space. Depth information of vertices is not preserved during circle generation
120+
// so it has to be reconstructed in vertex shader
28121
let posMatrix = coord.posMatrix;
29122
if (translate[0] !== 0 || translate[1] !== 0) {
30123
posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor);
31124
}
32-
program.draw(context, drawCircles ? gl.TRIANGLES : gl.LINES,
33-
DepthMode.disabled, StencilMode.disabled,
34-
painter.colorModeForRenderPass(),
35-
CullFaceMode.disabled,
36-
collisionUniformValues(
37-
posMatrix,
38-
painter.transform,
39-
tile),
40-
layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer,
41-
buffers.segments, null, painter.transform.zoom, null, null,
42-
buffers.collisionVertexBuffer);
125+
126+
let prevInvPosMatrix = posMatrix;
127+
128+
if ('posMatrixCircles' in bucket) {
129+
prevInvPosMatrix = mat4.invert([], bucket['posMatrixCircles']);
130+
} else {
131+
prevInvPosMatrix = mat4.invert([], posMatrix);
132+
}
133+
134+
const uniforms = collisionUniformValuesTemp(painter.transform.glCoordMatrix, prevInvPosMatrix,
135+
posMatrix, [painter.transform.width, painter.transform.height]);
136+
137+
// Upload and render quads in batches
138+
let batchVertexIdx = 0;
139+
let vertexOffset = 0;
140+
141+
while (vertexOffset < arr.length) {
142+
const verticesLeft = arr.length - vertexOffset;
143+
const vertexSpaceLeftInBatch = maxVerticesPerBatch - batchVertexIdx;
144+
const batchSize = Math.min(verticesLeft, vertexSpaceLeftInBatch);
145+
146+
for (let vIdx = vertexOffset; vIdx < vertexOffset + batchSize; vIdx+=4) {
147+
const r = arr[vIdx + 2];
148+
appendVertex(batchVertexIdx + 0, arr[vIdx + 0], arr[vIdx + 1], -r, -r);
149+
appendVertex(batchVertexIdx + 1, arr[vIdx + 0], arr[vIdx + 1], -r, r);
150+
appendVertex(batchVertexIdx + 2, arr[vIdx + 0], arr[vIdx + 1], r, r);
151+
appendVertex(batchVertexIdx + 3, arr[vIdx + 0], arr[vIdx + 1], r, -r);
152+
153+
batchVertexIdx += 4;
154+
}
155+
156+
vertexOffset += batchSize;
157+
158+
// TODO: Proper buffer orphaning. This might currently cause CPU-GPU sync!
159+
if (batchVertexIdx == maxVerticesPerBatch) {
160+
// Render the batch
161+
layer.vertexBuffer2.updateData(batchVertices, 0);
162+
163+
program2.draw(
164+
context,
165+
gl.TRIANGLES,
166+
DepthMode.disabled,
167+
StencilMode.disabled,
168+
painter.colorModeForRenderPass(),
169+
CullFaceMode.disabled,
170+
uniforms,
171+
layer.id,
172+
layer.vertexBuffer2, // layoutVertexBuffer
173+
layer.indexBuffer2, // indexbuffer,
174+
SegmentVector.simpleSegment(0, 0, batchVertexIdx, batchVertexIdx / 2),
175+
null,
176+
painter.transform.zoom,
177+
null,
178+
null, // vertexBuffer
179+
null // vertexBuffer
180+
);
181+
182+
batchVertexIdx = 0;
183+
}
184+
}
185+
186+
// Render the leftover branch
187+
if (batchVertexIdx) {
188+
// Render the batch
189+
layer.vertexBuffer2.updateData(batchVertices, 0);
190+
191+
program2.draw(
192+
context,
193+
gl.TRIANGLES,
194+
DepthMode.disabled,
195+
StencilMode.disabled,
196+
painter.colorModeForRenderPass(),
197+
CullFaceMode.disabled,
198+
uniforms,
199+
layer.id,
200+
layer.vertexBuffer2, // layoutVertexBuffer
201+
layer.indexBuffer2, // indexbuffer,
202+
SegmentVector.simpleSegment(0, 0, batchVertexIdx, batchVertexIdx / 2),
203+
null,
204+
painter.transform.zoom,
205+
null,
206+
null, // vertexBuffer
207+
null // vertexBuffer
208+
);
209+
210+
batchVertexIdx = 0;
211+
}
43212
}
44213
}
45214

src/render/program/collision_program.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ export type CollisionUniformsType = {|
2020
'u_overscale_factor': Uniform1f
2121
|};
2222

23+
export type COllisionUniformsTypeTemp = {|
24+
'u_matrix': UniformMatrix4f,
25+
'u_toWorld': UniformMatrix4f,
26+
'u_fromWorld': UniformMatrix4f,
27+
'u_viewport_size': Uniform2f
28+
|};
29+
2330
const collisionUniforms = (context: Context, locations: UniformLocations): CollisionUniformsType => ({
2431
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
2532
'u_camera_to_center_distance': new Uniform1f(context, locations.u_camera_to_center_distance),
@@ -28,6 +35,13 @@ const collisionUniforms = (context: Context, locations: UniformLocations): Colli
2835
'u_overscale_factor': new Uniform1f(context, locations.u_overscale_factor)
2936
});
3037

38+
const collisionUniformsTemp = (context: Context, locations: UniformLocations): CollisionUniformsType => ({
39+
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
40+
'u_toWorld': new UniformMatrix4f(context, locations.u_toWorld),
41+
'u_fromWorld': new UniformMatrix4f(context, locations.u_fromWorld),
42+
'u_viewport_size': new Uniform2f(context, locations.u_viewport_size)
43+
});
44+
3145
const collisionUniformValues = (
3246
matrix: Float32Array,
3347
transform: Transform,
@@ -46,4 +60,18 @@ const collisionUniformValues = (
4660
};
4761
};
4862

49-
export {collisionUniforms, collisionUniformValues};
63+
const collisionUniformValuesTemp = (
64+
matrix: Float32Array,
65+
toWorld: Float32Array,
66+
fromWorld: Float32Array,
67+
viewportSize: array<number>
68+
): UniformValues<COllisionUniformsTypeTemp> => {
69+
return {
70+
'u_matrix': matrix,
71+
'u_toWorld': toWorld,
72+
'u_fromWorld': fromWorld,
73+
'u_viewport_size': viewportSize
74+
};
75+
};
76+
77+
export {collisionUniforms, collisionUniformValues, collisionUniformsTemp, collisionUniformValuesTemp};

src/render/program/program_uniforms.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import {fillExtrusionUniforms, fillExtrusionPatternUniforms} from './fill_extrusion_program';
44
import {fillUniforms, fillPatternUniforms, fillOutlineUniforms, fillOutlinePatternUniforms} from './fill_program';
55
import {circleUniforms} from './circle_program';
6-
import {collisionUniforms} from './collision_program';
6+
import {collisionUniforms, collisionUniformsTemp} from './collision_program';
77
import {debugUniforms} from './debug_program';
88
import {clippingMaskUniforms} from './clipping_mask_program';
99
import {heatmapUniforms, heatmapTextureUniforms} from './heatmap_program';
@@ -23,6 +23,7 @@ export const programUniforms = {
2323
circle: circleUniforms,
2424
collisionBox: collisionUniforms,
2525
collisionCircle: collisionUniforms,
26+
collisionCircleTemp: collisionUniformsTemp,
2627
debug: debugUniforms,
2728
clippingMask: clippingMaskUniforms,
2829
heatmap: heatmapUniforms,

src/shaders/collision_circle.fragment.glsl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,20 @@ void main() {
1010
float alpha = 0.5;
1111

1212
// Red = collision, hide label
13-
vec4 color = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
13+
vec4 color = vec4(0.0, 0.0, 1.0, 1.0) * alpha;
1414

1515
// Blue = no collision, label is showing
1616
if (v_placed > 0.5) {
1717
color = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
1818
}
19-
20-
if (v_notUsed > 0.5) {
21-
// This box not used, fade it out
22-
color *= .2;
23-
}
19+
else
20+
discard;
21+
22+
//if (v_notUsed > 0.5) {
23+
// // This box not used, fade it out
24+
// color *= .2;
25+
// discard;
26+
//}
2427

2528
float extrude_scale_length = length(v_extrude_scale);
2629
float extrude_length = length(v_extrude) * extrude_scale_length;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
void main() {
2+
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
3+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
attribute vec2 a_pos;
2+
attribute vec2 a_reserved;
3+
4+
uniform mat4 u_matrix;
5+
uniform mat4 u_toWorld;
6+
uniform mat4 u_fromWorld;
7+
uniform vec2 u_viewport_size;
8+
9+
void main() {
10+
// 100 = hard-coded padding used in collision logic
11+
vec4 clipPos = u_matrix * vec4(a_pos - vec2(100), 0.0, 1.0);
12+
13+
vec4 rayStart = u_toWorld * vec4(clipPos.xy / clipPos.w, -1.0, 1.0);
14+
vec4 rayEnd = u_toWorld * vec4(clipPos.xy / clipPos.w, 1.0, 1.0);
15+
16+
rayStart.xyz /= rayStart.w;
17+
rayEnd.xyz /= rayEnd.w;
18+
19+
float t = (0.0 - rayStart.z) / (rayEnd.z - rayStart.z);
20+
vec3 tilePos = mix(rayStart.xyz, rayEnd.xyz, t);
21+
22+
clipPos = u_fromWorld * vec4(tilePos, 1.0);
23+
24+
gl_Position = vec4(clipPos.xyz / clipPos.w, 1.0) + vec4(a_reserved / u_viewport_size * 2.0, 0.0, 0.0);
25+
}

0 commit comments

Comments
 (0)