@@ -10,7 +10,7 @@ import StencilMode from '../gl/stencil_mode';
10
10
import CullFaceMode from '../gl/cull_face_mode' ;
11
11
import { collisionUniformValues , collisionCircleUniformValues } from './program/collision_program' ;
12
12
13
- import { StructArrayLayout2i4 , StructArrayLayout3ui6 } from '../data/array_types' ;
13
+ import { StructArrayLayout2i4 , StructArrayLayout3ui6 , CollisionCircleLayoutArray } from '../data/array_types' ;
14
14
import { collisionCircleLayout } from '../data/bucket/symbol_attributes' ;
15
15
import SegmentVector from '../data/segment' ;
16
16
import { mat4 } from 'gl-matrix' ;
@@ -56,42 +56,20 @@ function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: S
56
56
}
57
57
58
58
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
- }
79
59
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 ;
85
63
86
64
for ( let i = 0 ; i < coords . length ; i ++ ) {
87
65
const coord = coords [ i ] ;
88
66
const tile = sourceCache . getTile ( coord ) ;
89
67
const bucket : ?SymbolBucket = ( tile . getBucket ( layer ) : any ) ;
90
68
if ( ! bucket ) continue ;
91
69
92
- const arr = bucket . collisionCircleArray ;
70
+ const circleArray = bucket . collisionCircleArray ;
93
71
94
- if ( ! arr . length )
72
+ if ( ! circleArray . length )
95
73
continue ;
96
74
97
75
let posMatrix = coord . posMatrix ;
@@ -103,92 +81,88 @@ function drawCollisionCircles(painter: Painter, sourceCache: SourceCache, layer:
103
81
// We need to know the projection matrix that was used for projecting collision circles to the screen.
104
82
// This might vary between buckets as the symbol placement is a continous process. This matrix is
105
83
// 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 ;
136
86
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 ;
141
99
}
142
100
143
- quadIndexBuffer . destroy ( ) ;
144
- quadVertexBuffer . destroy ( ) ;
145
- }
101
+ if ( ! tileBatches . length )
102
+ return ;
146
103
147
- function drawBatch ( painter : Painter , proj : mat4 , invPrevProj : mat4 , quads : any , numQuads : number , layerId : string , vb : VertexBuffer , ib : IndexBuffer ) {
148
104
const context = painter . context ;
149
105
const gl = context . gl ;
150
106
const circleProgram = painter . useProgram ( 'collisionCircle' ) ;
151
107
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
+ }
183
133
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 ) ;
189
162
}
190
163
191
- return array ;
164
+ vertexBuffer . destroy ( ) ;
165
+ indexBuffer . destroy ( ) ;
192
166
}
193
167
194
168
function createQuadTriangles ( quadCount : number ) : StructArrayLayout3ui6 {
0 commit comments