@@ -24,14 +24,9 @@ const DefaultPenAttributes = {
24
24
diameter : 1
25
25
} ;
26
26
27
- /**
28
- * Reused memory location for storing a premultiplied pen color.
29
- * @type {FloatArray }
30
- */
31
- const __premultipliedColor = [ 0 , 0 , 0 , 0 ] ;
32
-
33
- const PEN_BUFFER_SIZE_LARGER = 65520 ;
34
- const PEN_BUFFER_SIZE_SMALLER = 32760 ;
27
+ const PEN_ATTRIBUTE_BUFFER_SIZE = 163800 ;
28
+ const PEN_ATTRIBUTE_STRIDE = 10 ;
29
+ const PEN_ATTRIBUTE_STRIDE_BYTES = PEN_ATTRIBUTE_STRIDE * 4 ;
35
30
36
31
class PenSkin extends Skin {
37
32
/**
@@ -71,67 +66,90 @@ class PenSkin extends Skin {
71
66
exit : ( ) => this . _exitUsePenBuffer ( )
72
67
} ;
73
68
69
+ /** @type {WebGLRenderingContext } */
70
+ const gl = this . _renderer . gl ;
71
+
74
72
// tw: renderQuality attribute
75
73
this . renderQuality = 1 ;
76
74
77
75
// tw: keep track of native size
78
76
this . _nativeSize = renderer . getNativeSize ( ) ;
79
77
80
- // tw: create the extra data structures needed to buffer pen
81
- this . _resetAttributeIndexes ( ) ;
82
- this . a_lineColor = new Float32Array ( PEN_BUFFER_SIZE_LARGER ) ;
83
- this . a_lineThicknessAndLength = new Float32Array ( PEN_BUFFER_SIZE_SMALLER ) ;
84
- this . a_penPoints = new Float32Array ( PEN_BUFFER_SIZE_LARGER ) ;
85
- this . a_position = new Float32Array ( PEN_BUFFER_SIZE_SMALLER ) ;
86
- for ( let i = 0 ; i < this . a_position . length ; i += 12 ) {
87
- this . a_position [ i + 0 ] = 1 ;
88
- this . a_position [ i + 1 ] = 0 ;
89
- this . a_position [ i + 2 ] = 0 ;
90
- this . a_position [ i + 3 ] = 0 ;
91
- this . a_position [ i + 4 ] = 1 ;
92
- this . a_position [ i + 5 ] = 1 ;
93
- this . a_position [ i + 6 ] = 1 ;
94
- this . a_position [ i + 7 ] = 1 ;
95
- this . a_position [ i + 8 ] = 0 ;
96
- this . a_position [ i + 9 ] = 0 ;
97
- this . a_position [ i + 10 ] = 0 ;
98
- this . a_position [ i + 11 ] = 1 ;
99
- }
100
- /** @type {twgl.BufferInfo } */
101
- this . _lineBufferInfo = twgl . createBufferInfoFromArrays ( this . _renderer . gl , {
102
- a_position : {
103
- numComponents : 2 ,
104
- data : this . a_position
105
- } ,
106
- a_lineColor : {
107
- numComponents : 4 ,
108
- drawType : this . _renderer . gl . STREAM_DRAW ,
109
- data : this . a_lineColor
110
- } ,
111
- a_lineThicknessAndLength : {
112
- numComponents : 2 ,
113
- drawType : this . _renderer . gl . STREAM_DRAW ,
114
- data : this . a_lineThicknessAndLength
115
- } ,
116
- a_penPoints : {
117
- numComponents : 4 ,
118
- drawType : this . _renderer . gl . STREAM_DRAW ,
119
- data : this . a_penPoints
120
- }
121
- } ) ;
122
-
123
78
const NO_EFFECTS = 0 ;
124
79
/** @type {twgl.ProgramInfo } */
125
80
this . _lineShader = this . _renderer . _shaderManager . getShader ( ShaderManager . DRAW_MODE . line , NO_EFFECTS ) ;
126
81
127
- // tw: draw region used to preserve texture when resizing
82
+ // Draw region used to preserve texture when resizing
128
83
this . _drawTextureShader = this . _renderer . _shaderManager . getShader ( ShaderManager . DRAW_MODE . default , NO_EFFECTS ) ;
129
84
/** @type {object } */
130
85
this . _drawTextureRegionId = {
131
86
enter : ( ) => this . _enterDrawTexture ( ) ,
132
87
exit : ( ) => this . _exitDrawTexture ( )
133
88
} ;
134
89
90
+ this . a_position_glbuffer = gl . createBuffer ( ) ;
91
+ this . a_position_loc = gl . getAttribLocation ( this . _lineShader . program , 'a_position' ) ;
92
+
93
+ this . a_lineColor_loc = gl . getAttribLocation ( this . _lineShader . program , 'a_lineColor' ) ;
94
+ this . a_lineThicknessAndLength_loc = gl . getAttribLocation ( this . _lineShader . program , 'a_lineThicknessAndLength' ) ;
95
+ this . a_penPoints_loc = gl . getAttribLocation ( this . _lineShader . program , 'a_penPoints' ) ;
96
+
97
+ this . attribute_glbuffer = gl . createBuffer ( ) ;
98
+ this . attribute_index = 0 ;
99
+ this . attribute_data = new Float32Array ( PEN_ATTRIBUTE_BUFFER_SIZE ) ;
100
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . attribute_glbuffer ) ;
101
+ gl . bufferData ( gl . ARRAY_BUFFER , this . attribute_data . length * 4 , gl . STREAM_DRAW ) ;
102
+
103
+ if ( gl . drawArraysInstanced ) {
104
+ // WebGL2 has native instanced rendering
105
+ this . instancedRendering = true ;
106
+ this . glDrawArraysInstanced = gl . drawArraysInstanced . bind ( gl ) ;
107
+ this . glVertexAttribDivisor = gl . vertexAttribDivisor . bind ( gl ) ;
108
+ } else {
109
+ // WebGL1 may have instanced rendering through the ANGLE_instanced_arrays extension
110
+ const instancedArraysExtension = gl . getExtension ( 'ANGLE_instanced_arrays' ) ;
111
+ if ( instancedArraysExtension ) {
112
+ this . instancedRendering = true ;
113
+ this . glDrawArraysInstanced = instancedArraysExtension . drawArraysInstancedANGLE . bind (
114
+ instancedArraysExtension
115
+ ) ;
116
+ this . glVertexAttribDivisor = instancedArraysExtension . vertexAttribDivisorANGLE . bind (
117
+ instancedArraysExtension
118
+ ) ;
119
+ } else {
120
+ // Inefficient but still supported
121
+ this . instancedRendering = false ;
122
+ }
123
+ }
124
+
125
+ if ( this . instancedRendering ) {
126
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . a_position_glbuffer ) ;
127
+ gl . bufferData ( gl . ARRAY_BUFFER , new Float32Array ( [
128
+ 1 , 0 ,
129
+ 0 , 0 ,
130
+ 1 , 1 ,
131
+ 0 , 1
132
+ ] ) , gl . STATIC_DRAW ) ;
133
+ } else {
134
+ const positionBuffer = new Float32Array ( PEN_ATTRIBUTE_BUFFER_SIZE / PEN_ATTRIBUTE_STRIDE * 2 ) ;
135
+ for ( let i = 0 ; i < positionBuffer . length ; i += 12 ) {
136
+ positionBuffer [ i + 0 ] = 1 ;
137
+ positionBuffer [ i + 1 ] = 0 ;
138
+ positionBuffer [ i + 2 ] = 0 ;
139
+ positionBuffer [ i + 3 ] = 0 ;
140
+ positionBuffer [ i + 4 ] = 1 ;
141
+ positionBuffer [ i + 5 ] = 1 ;
142
+ positionBuffer [ i + 6 ] = 1 ;
143
+ positionBuffer [ i + 7 ] = 1 ;
144
+ positionBuffer [ i + 8 ] = 0 ;
145
+ positionBuffer [ i + 9 ] = 0 ;
146
+ positionBuffer [ i + 10 ] = 0 ;
147
+ positionBuffer [ i + 11 ] = 1 ;
148
+ }
149
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . a_position_glbuffer ) ;
150
+ gl . bufferData ( gl . ARRAY_BUFFER , positionBuffer , gl . STATIC_DRAW ) ;
151
+ }
152
+
135
153
this . onNativeSizeChanged = this . onNativeSizeChanged . bind ( this ) ;
136
154
this . _renderer . on ( RenderConstants . Events . NativeSizeChanged , this . onNativeSizeChanged ) ;
137
155
@@ -222,8 +240,6 @@ class PenSkin extends Skin {
222
240
* Prepare to draw lines in the _lineOnBufferDrawRegionId region.
223
241
*/
224
242
_enterDrawLineOnBuffer ( ) {
225
- // tw: reset attributes when starting pen drawing
226
- this . _resetAttributeIndexes ( ) ;
227
243
const gl = this . _renderer . gl ;
228
244
229
245
twgl . bindFramebufferInfo ( gl , this . _framebuffer ) ;
@@ -232,22 +248,27 @@ class PenSkin extends Skin {
232
248
233
249
const currentShader = this . _lineShader ;
234
250
gl . useProgram ( currentShader . program ) ;
235
- twgl . setBuffersAndAttributes ( gl , currentShader , this . _lineBufferInfo ) ;
236
251
237
252
const uniforms = {
238
253
u_skin : this . _texture ,
239
254
u_stageSize : this . _size
240
255
} ;
241
256
242
257
twgl . setUniforms ( currentShader , uniforms ) ;
258
+
259
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . a_position_glbuffer ) ;
260
+ gl . enableVertexAttribArray ( this . a_position_loc ) ;
261
+ gl . vertexAttribPointer ( this . a_position_loc , 2 , gl . FLOAT , false , 2 * 4 , 0 ) ;
262
+
263
+ this . attribute_index = 0 ;
243
264
}
244
265
245
266
/**
246
267
* Return to a base state from _lineOnBufferDrawRegionId.
247
268
*/
248
269
_exitDrawLineOnBuffer ( ) {
249
270
// tw: flush when exiting pen rendering
250
- if ( this . a_lineColorIndex ) {
271
+ if ( this . attribute_index ) {
251
272
this . _flushLines ( ) ;
252
273
}
253
274
@@ -326,19 +347,15 @@ class PenSkin extends Skin {
326
347
_drawLineOnBuffer ( penAttributes , x0 , y0 , x1 , y1 ) {
327
348
this . _renderer . enterDrawRegion ( this . _lineOnBufferDrawRegionId ) ;
328
349
329
- // tw: flush if this line would overflow buffers
330
- // For some reason, looking up the size of a_lineColor with .length is very slow in some browsers.
331
- // We see measurable performance improvements by comparing to a constant instead.
332
- if ( this . a_lineColorIndex + 24 > PEN_BUFFER_SIZE_LARGER ) {
350
+ const iters = this . instancedRendering ? 1 : 6 ;
351
+
352
+ // For some reason, looking up the size of a buffer through .length can be slow,
353
+ // so use a constant instead.
354
+ if ( this . attribute_index + ( PEN_ATTRIBUTE_STRIDE * iters ) > PEN_ATTRIBUTE_BUFFER_SIZE ) {
333
355
this . _flushLines ( ) ;
334
356
}
335
357
336
- // Premultiply pen color by pen transparency
337
358
const penColor = penAttributes . color4f || DefaultPenAttributes . color4f ;
338
- __premultipliedColor [ 0 ] = penColor [ 0 ] * penColor [ 3 ] ;
339
- __premultipliedColor [ 1 ] = penColor [ 1 ] * penColor [ 3 ] ;
340
- __premultipliedColor [ 2 ] = penColor [ 2 ] * penColor [ 3 ] ;
341
- __premultipliedColor [ 3 ] = penColor [ 3 ] ;
342
359
343
360
// tw: apply renderQuality
344
361
x0 *= this . renderQuality ;
@@ -357,92 +374,82 @@ class PenSkin extends Skin {
357
374
358
375
// tw: apply renderQuality
359
376
const lineThickness = ( penAttributes . diameter || DefaultPenAttributes . diameter ) * this . renderQuality ;
360
- // tw: write pen draws to buffers where they will be flushed later
361
- for ( let i = 0 ; i < 6 ; i ++ ) {
362
- this . a_lineColor [ this . a_lineColorIndex ] = __premultipliedColor [ 0 ] ;
363
- this . a_lineColorIndex ++ ;
364
- this . a_lineColor [ this . a_lineColorIndex ] = __premultipliedColor [ 1 ] ;
365
- this . a_lineColorIndex ++ ;
366
- this . a_lineColor [ this . a_lineColorIndex ] = __premultipliedColor [ 2 ] ;
367
- this . a_lineColorIndex ++ ;
368
- this . a_lineColor [ this . a_lineColorIndex ] = __premultipliedColor [ 3 ] ;
369
- this . a_lineColorIndex ++ ;
370
-
371
- this . a_lineThicknessAndLength [ this . a_lineThicknessAndLengthIndex ] = lineThickness ;
372
- this . a_lineThicknessAndLengthIndex ++ ;
373
-
374
- this . a_lineThicknessAndLength [ this . a_lineThicknessAndLengthIndex ] = lineLength ;
375
- this . a_lineThicknessAndLengthIndex ++ ;
376
-
377
- this . a_penPoints [ this . a_penPointsIndex ] = x0 ;
378
- this . a_penPointsIndex ++ ;
379
- this . a_penPoints [ this . a_penPointsIndex ] = - y0 ;
380
- this . a_penPointsIndex ++ ;
381
- this . a_penPoints [ this . a_penPointsIndex ] = lineDiffX ;
382
- this . a_penPointsIndex ++ ;
383
- this . a_penPoints [ this . a_penPointsIndex ] = - lineDiffY ;
384
- this . a_penPointsIndex ++ ;
385
- }
386
- }
387
377
388
- // tw: resets indexes in the pen drawing buffers
389
- _resetAttributeIndexes ( ) {
390
- this . a_lineColorIndex = 0 ;
391
- this . a_lineThicknessAndLengthIndex = 0 ;
392
- this . a_penPointsIndex = 0 ;
378
+ for ( let i = 0 ; i < iters ; i ++ ) {
379
+ // Pen color sent to the GPU is pre-multiplied by transparency
380
+ this . attribute_data [ this . attribute_index ] = penColor [ 0 ] * penColor [ 3 ] ;
381
+ this . attribute_index ++ ;
382
+ this . attribute_data [ this . attribute_index ] = penColor [ 1 ] * penColor [ 3 ] ;
383
+ this . attribute_index ++ ;
384
+ this . attribute_data [ this . attribute_index ] = penColor [ 2 ] * penColor [ 3 ] ;
385
+ this . attribute_index ++ ;
386
+ this . attribute_data [ this . attribute_index ] = penColor [ 3 ] ;
387
+ this . attribute_index ++ ;
388
+
389
+ this . attribute_data [ this . attribute_index ] = lineThickness ;
390
+ this . attribute_index ++ ;
391
+
392
+ this . attribute_data [ this . attribute_index ] = lineLength ;
393
+ this . attribute_index ++ ;
394
+
395
+ this . attribute_data [ this . attribute_index ] = x0 ;
396
+ this . attribute_index ++ ;
397
+ this . attribute_data [ this . attribute_index ] = - y0 ;
398
+ this . attribute_index ++ ;
399
+ this . attribute_data [ this . attribute_index ] = lineDiffX ;
400
+ this . attribute_index ++ ;
401
+ this . attribute_data [ this . attribute_index ] = - lineDiffY ;
402
+ this . attribute_index ++ ;
403
+ }
393
404
}
394
405
395
- // tw: flushes buffered pen lines to the GPU
396
406
_flushLines ( ) {
407
+ /** @type {WebGLRenderingContext } */
397
408
const gl = this . _renderer . gl ;
398
409
399
- const currentShader = this . _lineShader ;
410
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . attribute_glbuffer ) ;
411
+ gl . bufferSubData ( gl . ARRAY_BUFFER , 0 , new Float32Array ( this . attribute_data . buffer , 0 , this . attribute_index ) ) ;
400
412
401
- // If only a small amount of data needs to be uploaded, only upload part of the data.
402
- // todo: need to see if this helps and fine tune this number
403
- if ( this . a_lineColorIndex < 1000 ) {
404
- twgl . setAttribInfoBufferFromArray (
405
- gl ,
406
- this . _lineBufferInfo . attribs . a_lineColor ,
407
- new Float32Array ( this . a_lineColor . buffer , 0 , this . a_lineColorIndex ) ,
408
- 0
409
- ) ;
410
- twgl . setAttribInfoBufferFromArray (
411
- gl ,
412
- this . _lineBufferInfo . attribs . a_penPoints ,
413
- new Float32Array ( this . a_penPoints . buffer , 0 , this . a_penPointsIndex ) ,
414
- 0
415
- ) ;
416
- twgl . setAttribInfoBufferFromArray (
417
- gl ,
418
- this . _lineBufferInfo . attribs . a_lineThicknessAndLength ,
419
- new Float32Array ( this . a_lineThicknessAndLength . buffer , 0 , this . a_lineThicknessAndLengthIndex ) ,
420
- 0
413
+ gl . enableVertexAttribArray ( this . a_lineColor_loc ) ;
414
+ gl . vertexAttribPointer (
415
+ this . a_lineColor_loc ,
416
+ 4 , gl . FLOAT , false ,
417
+ PEN_ATTRIBUTE_STRIDE_BYTES , 0
418
+ ) ;
419
+
420
+ gl . enableVertexAttribArray ( this . a_lineThicknessAndLength_loc ) ;
421
+ gl . vertexAttribPointer (
422
+ this . a_lineThicknessAndLength_loc ,
423
+ 2 , gl . FLOAT , false ,
424
+ PEN_ATTRIBUTE_STRIDE_BYTES , 4 * 4
425
+ ) ;
426
+
427
+ gl . enableVertexAttribArray ( this . a_penPoints_loc ) ;
428
+ gl . vertexAttribPointer (
429
+ this . a_penPoints_loc ,
430
+ 4 , gl . FLOAT , false ,
431
+ PEN_ATTRIBUTE_STRIDE_BYTES , 6 * 4
432
+ ) ;
433
+
434
+ if ( this . instancedRendering ) {
435
+ this . glVertexAttribDivisor ( this . a_lineColor_loc , 1 ) ;
436
+ this . glVertexAttribDivisor ( this . a_lineThicknessAndLength_loc , 1 ) ;
437
+ this . glVertexAttribDivisor ( this . a_penPoints_loc , 1 ) ;
438
+
439
+ this . glDrawArraysInstanced (
440
+ gl . TRIANGLE_STRIP ,
441
+ 0 , 4 ,
442
+ this . attribute_index / PEN_ATTRIBUTE_STRIDE
421
443
) ;
444
+
445
+ this . glVertexAttribDivisor ( this . a_lineColor_loc , 0 ) ;
446
+ this . glVertexAttribDivisor ( this . a_lineThicknessAndLength_loc , 0 ) ;
447
+ this . glVertexAttribDivisor ( this . a_penPoints_loc , 0 ) ;
422
448
} else {
423
- twgl . setAttribInfoBufferFromArray (
424
- gl ,
425
- this . _lineBufferInfo . attribs . a_lineColor ,
426
- this . a_lineColor
427
- ) ;
428
- twgl . setAttribInfoBufferFromArray (
429
- gl ,
430
- this . _lineBufferInfo . attribs . a_penPoints ,
431
- this . a_penPoints
432
- ) ;
433
- twgl . setAttribInfoBufferFromArray (
434
- gl ,
435
- this . _lineBufferInfo . attribs . a_lineThicknessAndLength ,
436
- this . a_lineThicknessAndLength
437
- ) ;
449
+ gl . drawArrays ( gl . TRIANGLES , 0 , this . attribute_index / PEN_ATTRIBUTE_STRIDE ) ;
438
450
}
439
- // todo: if we skip twgl and do all this buffer stuff ourselves, we can skip some unneeded gl calls
440
- twgl . setBuffersAndAttributes ( gl , currentShader , this . _lineBufferInfo ) ;
441
-
442
- twgl . drawBufferInfo ( gl , this . _lineBufferInfo , gl . TRIANGLES , this . a_lineThicknessAndLengthIndex / 2 ) ;
443
-
444
- this . _resetAttributeIndexes ( ) ;
445
451
452
+ this . attribute_index = 0 ;
446
453
this . _silhouetteDirty = true ;
447
454
}
448
455
0 commit comments