@swf2es/render 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,7 @@ import {
30
30
  fillVertex,
31
31
  glyphsFragment,
32
32
  glyphsVertex,
33
+ MERGED_UNIT,
33
34
  meshFragment,
34
35
  meshVertex,
35
36
  OUTLINE_UNIT,
@@ -56,6 +57,15 @@ interface MultiDraw {
56
57
  /** Last entry, table start, draws, units, batch start/count, then each unit's texture and sampling. */
57
58
  const RUN_STRIDE = 6 + FILL_TEXTURES * 2;
58
59
 
60
+ /**
61
+ * The fewest fills a run copies into the merged stream (FillBatches): with
62
+ * WEBGL_multi_draw, whose one call already draws a run, enough for the GPU's
63
+ * sub-draws to cost more than the copy; without it, two, each fill
64
+ * otherwise a call of its own.
65
+ */
66
+ const MERGED_LEAST_MULTI = 32;
67
+ const MERGED_LEAST = 2;
68
+
59
69
  /** The most masks the stencil's eight bits count. */
60
70
  const STENCIL_MOST = 255;
61
71
 
@@ -72,9 +82,10 @@ export interface Masks {
72
82
  /**
73
83
  * The draws of a scene list's entries into the target bound: fills in runs,
74
84
  * one multi-draw of the atlas's index ranges a run where WEBGL_multi_draw
75
- * is, meshes and advanced text's glyphs each by a program of its own, and
76
- * the masks' coverage into the stencil; and the view of the target drawn
77
- * into, which each program is told.
85
+ * is, else one draw of the run copied into the merged stream where its
86
+ * geometries are small; meshes and advanced text's glyphs each by a program
87
+ * of its own, and the masks' coverage into the stencil; and the view of the
88
+ * target drawn into, which each program is told.
78
89
  */
79
90
  export class Draws {
80
91
  private readonly gl: WebGL2RenderingContext;
@@ -103,9 +114,16 @@ export class Draws {
103
114
  private blendBackLocation: WebGLUniformLocation | null = null;
104
115
  /** WEBGL_multi_draw, which draws a run of fills alike a call (drawEntries), where there is. */
105
116
  private multi: MultiDraw | null = null;
117
+ /**
118
+ * Whether, without WEBGL_multi_draw, runs are merged; false draws a call a
119
+ * fill, as a reference the merged draws are tested against (multi-draw.ts).
120
+ */
121
+ merging = true;
106
122
  /** Avoid the dynamically indexed uniform table used in the Intel GPU hang reproducer. */
107
123
  private runTable: WebGLTexture | null = null;
108
124
  private runBaseLocation: WebGLUniformLocation | null = null;
125
+ /** The table start the fill program was told last (uRunBase), -1 for none. */
126
+ private runBase = -1;
109
127
  private runTableHeight = 0;
110
128
  private runTableMost = 0;
111
129
  private tableRows = new Int32Array(RUN_MOST);
@@ -198,15 +216,15 @@ export class Draws {
198
216
  const gl = this.gl;
199
217
  this.multi = gl.getExtension("WEBGL_multi_draw") as MultiDraw | null;
200
218
  try {
201
- this.program = linkProgram(gl, fillVertex(this.multi !== null), fillFragment);
219
+ this.program = linkProgram(gl, fillVertex(this.multi !== null, true), fillFragment);
202
220
  } catch (e) {
203
221
  if (!this.multi) {
204
222
  throw e;
205
223
  }
206
224
 
207
- // An extension offered whose shader directive the compiler refuses: a call a draw.
225
+ // An extension offered whose shader directive the compiler refuses: runs merged alone.
208
226
  this.multi = null;
209
- this.program = linkProgram(gl, fillVertex(false), fillFragment);
227
+ this.program = linkProgram(gl, fillVertex(false, true), fillFragment);
210
228
  }
211
229
 
212
230
  this.unitIds.fill(-1);
@@ -223,8 +241,10 @@ export class Draws {
223
241
  this.runTableMost = textureMost;
224
242
  this.prepared = false;
225
243
  this.runBaseLocation = gl.getUniformLocation(this.program, "uRunBase");
244
+ this.runBase = -1;
245
+ this.batches.setUp(textureMost);
246
+ gl.uniform1i(gl.getUniformLocation(this.program, "uMergedRows"), MERGED_UNIT);
226
247
  if (this.multi) {
227
- this.batches.setUp();
228
248
  this.sizeRunTable(1);
229
249
  gl.uniform1i(gl.getUniformLocation(this.program, "uRunTable"), RUN_UNIT);
230
250
  }
@@ -268,13 +288,21 @@ export class Draws {
268
288
  this.lastRow = -1;
269
289
  }
270
290
 
271
- /** Upload before any draws, so that updating a run cannot stall earlier draws reading it. */
291
+ /**
292
+ * Upload before any draws, so that updating a run cannot stall earlier
293
+ * draws reading it: the run table, with WEBGL_multi_draw, and the merged
294
+ * stream and its rows.
295
+ */
272
296
  prepare(commands: Commands, scene: SceneList): void {
273
297
  this.prepared = false;
274
- if (!this.multi) {
275
- return;
276
- }
277
-
298
+ const multi = this.multi !== null;
299
+ // Without a multi-draw, rows are told by uniform or merged; the table is not read.
300
+ const tableMost = multi ? RUN_MOST * this.runTableMost : Number.POSITIVE_INFINITY;
301
+ const least = multi
302
+ ? MERGED_LEAST_MULTI
303
+ : this.merging
304
+ ? MERGED_LEAST
305
+ : Number.POSITIVE_INFINITY;
278
306
  this.batches.reset();
279
307
  if (commands.length > this.commandStarts.length) {
280
308
  this.commandStarts = new Uint32Array(commands.ops.length / 4);
@@ -309,16 +337,13 @@ export class Draws {
309
337
 
310
338
  i = this.gatherRun(scene, i, end, stencil);
311
339
  const needed = count + this.runCount;
312
- if (needed > RUN_MOST * this.runTableMost) {
340
+ if (needed > tableMost) {
313
341
  // An unusually large frame still draws by updating the first row a run at a time.
314
342
  return;
315
343
  }
316
344
 
317
345
  if (needed > this.tableRows.length) {
318
- const size = Math.min(
319
- RUN_MOST * this.runTableMost,
320
- Math.max(needed, this.tableRows.length * 2),
321
- );
346
+ const size = Math.min(tableMost, Math.max(needed, this.tableRows.length * 2));
322
347
  const rows = new Int32Array(size);
323
348
  rows.set(this.tableRows);
324
349
  this.tableRows = rows;
@@ -340,13 +365,21 @@ export class Draws {
340
365
  this.runs[run + 1] = count;
341
366
  this.runs[run + 2] = this.runCount;
342
367
  this.runs[run + 3] = this.runUnits;
343
- const batchStart = this.batches.indexTop;
344
- if (!stencil) {
345
- this.batches.add(this.runPlaces, this.runRows, this.runCount, this.atlas.frame);
346
- }
347
-
348
- this.runs[run + 4] = batchStart;
349
- this.runs[run + 5] = this.batches.indexTop - batchStart;
368
+ // A mask's fills merged too where each would be a call.
369
+ const segment =
370
+ !stencil || !multi
371
+ ? this.batches.add(
372
+ this.runPlaces,
373
+ this.runRows,
374
+ this.runCount,
375
+ this.atlas.frame,
376
+ least,
377
+ !multi,
378
+ )
379
+ : null;
380
+
381
+ this.runs[run + 4] = segment ? segment.indexStart : 0;
382
+ this.runs[run + 5] = segment ? segment.indices : 0;
350
383
  for (let j = 0; j < FILL_TEXTURES; j++) {
351
384
  if (this.runUnits & (1 << j)) {
352
385
  this.runs[run + 6 + j * 2] = this.unitIds[j];
@@ -364,7 +397,7 @@ export class Draws {
364
397
  }
365
398
 
366
399
  const height = Math.ceil(count / RUN_MOST);
367
- if (height > 0) {
400
+ if (multi && height > 0) {
368
401
  this.sizeRunTable(height);
369
402
  const gl = this.gl;
370
403
  this.state.bindTexture(RUN_UNIT, this.runTable);
@@ -641,20 +674,27 @@ export class Draws {
641
674
 
642
675
  if (batchCount > 0) {
643
676
  state.bindVertexArray(this.batches.vertexArray);
644
- state.bindTexture(RUN_UNIT, this.runTable);
645
- state.bindSampler(RUN_UNIT, null);
677
+ state.bindTexture(MERGED_UNIT, this.batches.rowsTexture);
678
+ state.bindSampler(MERGED_UNIT, null);
646
679
  gl.drawElements(gl.TRIANGLES, batchCount, gl.UNSIGNED_INT, batchStart * 4);
647
680
  continue;
648
681
  }
649
682
 
650
683
  state.bindVertexArray(this.atlas.vertexArray);
651
684
  if (!this.multi) {
652
- if (this.runRows[0] !== this.lastRow) {
653
- this.lastRow = this.runRows[0];
654
- gl.uniform1i(this.rowLocation, this.lastRow);
685
+ // A run not merged: a call a fill, its row told as it changes.
686
+ const rows = this.prepared ? this.tableRows : this.runRows;
687
+ const counts = this.prepared ? this.tableCounts : this.runCounts;
688
+ const offsets = this.prepared ? this.tableOffsets : this.runOffsets;
689
+ for (let k = base; k < base + draws; k++) {
690
+ if (rows[k] !== this.lastRow) {
691
+ this.lastRow = rows[k];
692
+ gl.uniform1i(this.rowLocation, this.lastRow);
693
+ }
694
+
695
+ gl.drawElements(gl.TRIANGLES, counts[k], gl.UNSIGNED_INT, offsets[k]);
655
696
  }
656
697
 
657
- gl.drawElements(gl.TRIANGLES, this.runCounts[0], gl.UNSIGNED_INT, this.runOffsets[0]);
658
698
  continue;
659
699
  }
660
700
 
@@ -665,7 +705,7 @@ export class Draws {
665
705
  gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, draws, 1, gl.RED_INTEGER, gl.INT, this.runRows);
666
706
  }
667
707
 
668
- gl.uniform1i(this.runBaseLocation, base);
708
+ this.tellRunBase(base);
669
709
  this.multi.multiDrawElementsWEBGL(
670
710
  gl.TRIANGLES,
671
711
  this.prepared ? this.tableCounts : this.runCounts,
@@ -682,17 +722,34 @@ export class Draws {
682
722
  }
683
723
  }
684
724
 
725
+ /** The fill program told where its run starts in the run table, if it was told another. */
726
+ private tellRunBase(base: number): void {
727
+ if (base !== this.runBase) {
728
+ this.runBase = base;
729
+ this.gl.uniform1i(this.runBaseLocation, base);
730
+ }
731
+ }
732
+
685
733
  /**
686
734
  * The fills from entry `i` on that one call draws, as many as fit its
687
- * textures (FILL_TEXTURES, each with its sampling) and, without
688
- * WEBGL_multi_draw, one: their runCounts, runOffsets and runRows (FILL_SLOT),
689
- * and the units their textures take in runUnits. The last entry gathered,
690
- * past those whose texture is gone, which draw nothing.
735
+ * textures (FILL_TEXTURES, each with its sampling), RUN_MOST at most:
736
+ * their runCounts, runOffsets and runRows (FILL_SLOT), and the units their
737
+ * textures take in runUnits. Without WEBGL_multi_draw, a run is of
738
+ * geometries too large to merge (Place.batch), of geometries added this
739
+ * frame, or of stable ones alone, so that a stable run's merged copy is
740
+ * drawn again while a part of it changes from frame to frame; and without
741
+ * merging each fill is a run. The last entry gathered, past those whose
742
+ * texture is gone, which draw nothing.
691
743
  */
692
744
  private gatherRun(scene: SceneList, i: number, end: number, stencil: boolean): number {
693
745
  const places = this.atlas.places;
694
746
  const textures = this.gpu.textures;
695
- const most = this.multi ? RUN_MOST : 1;
747
+ const single = !this.multi;
748
+ const most = this.multi || this.merging ? RUN_MOST : 1;
749
+ const frame = this.atlas.frame;
750
+ // Without a multi-draw, what the run is of: geometries too large to merge, geometries added
751
+ // this frame, or stable ones, whose merged copy later frames draw again.
752
+ let kind = -1;
696
753
  let draws = 0;
697
754
  let last = i;
698
755
  this.runUnits = 0;
@@ -711,6 +768,15 @@ export class Draws {
711
768
  break;
712
769
  }
713
770
 
771
+ if (single) {
772
+ const of = !place.batch ? 0 : place.batch.frame === frame ? 1 : 2;
773
+ if (draws === 0) {
774
+ kind = of;
775
+ } else if (of !== kind) {
776
+ break;
777
+ }
778
+ }
779
+
714
780
  const id = stencil ? -1 : scene.texture[j];
715
781
  let slot = 0;
716
782
  if (id >= 0) {
@@ -916,9 +982,7 @@ export class Draws {
916
982
 
917
983
  /** The programs and draw table made on the context, deleted. */
918
984
  destroy(): void {
919
- if (this.multi) {
920
- this.batches.destroy();
921
- }
985
+ this.batches.destroy();
922
986
 
923
987
  if (this.runTable) {
924
988
  this.state.dropTexture(this.runTable);
@@ -70,6 +70,10 @@ export const OUTLINE_UNIT = 7;
70
70
  export const FILL_UNIT = 8;
71
71
  /** The multi-draw row table's unit, past the fill textures. */
72
72
  export const RUN_UNIT = FILL_UNIT + FILL_TEXTURES;
73
+ /** The merged fills' rows' unit (FillBatches), past the run table. */
74
+ export const MERGED_UNIT = RUN_UNIT + 1;
75
+ /** The merged fills' rows a line of their integer texture: a power of two, as for RUN_MOST. */
76
+ export const MERGED_LINE = 1024;
73
77
 
74
78
  /**
75
79
  * Places a vertex by its row's transform, on a quarter of a pixel of
@@ -78,17 +82,21 @@ export const RUN_UNIT = FILL_UNIT + FILL_TEXTURES;
78
82
  * space), and passes on its straight colour, the row's colour transform
79
83
  * and the texture it samples. uRow names the draw's row (FILL_SLOT); with
80
84
  * `multi` (WEBGL_multi_draw), uRunTable names each draw's of a multi-draw by
81
- * gl_DrawID, which is 0 in a draw of its own. Merged fills supply the packed
82
- * row as aDraw; the atlas leaves that attribute disabled and negative.
85
+ * gl_DrawID, which is 0 in a draw of its own. With `merged`, as with
86
+ * `multi`, a merged fill's vertices carry as aDraw where its packed row is
87
+ * in uMergedRows, which the stream keeps for its copy, so that the row
88
+ * changes from frame to frame where the vertices need not; the atlas leaves
89
+ * that attribute disabled and negative.
83
90
  */
84
- export const fillVertex = (multi: boolean) => `#version 300 es
91
+ export const fillVertex = (multi: boolean, merged = multi) => `#version 300 es
85
92
  ${multi ? "#extension GL_ANGLE_multi_draw : require" : ""}
86
93
  precision highp float;
87
94
  layout(location = ${POSITION_AT}) in vec2 aPosition;
88
95
  layout(location = ${TEXEL_AT}) in vec2 aTexel;
89
96
  layout(location = ${COLOR_AT}) in vec4 aColor;
90
97
  uniform highp sampler2D uRows;
91
- ${multi ? `layout(location = ${DRAW_AT}) in int aDraw;\nuniform highp isampler2D uRunTable;\nuniform int uRunBase;` : "uniform int uRow;"}
98
+ ${merged ? `layout(location = ${DRAW_AT}) in int aDraw;\nuniform highp isampler2D uMergedRows;` : ""}
99
+ ${multi ? "uniform highp isampler2D uRunTable;\nuniform int uRunBase;" : "uniform int uRow;"}
92
100
  uniform vec4 uView;
93
101
  uniform float uUnit;
94
102
  out vec4 vColor;
@@ -98,7 +106,7 @@ flat out vec4 vAdd;
98
106
  flat out int vSlot;
99
107
 
100
108
  void main(void) {
101
- int draw = ${multi ? `aDraw >= 0 ? aDraw : texelFetch(uRunTable, ivec2((uRunBase + gl_DrawID) & ${RUN_MOST - 1}, (uRunBase + gl_DrawID) >> ${Math.log2(RUN_MOST)}), 0).r` : "uRow"};
109
+ int draw = ${merged ? `aDraw >= 0 ? texelFetch(uMergedRows, ivec2(aDraw & ${MERGED_LINE - 1}, aDraw >> ${Math.log2(MERGED_LINE)}), 0).r : ` : ""}${multi ? `texelFetch(uRunTable, ivec2((uRunBase + gl_DrawID) & ${RUN_MOST - 1}, (uRunBase + gl_DrawID) >> ${Math.log2(RUN_MOST)}), 0).r` : "uRow"};
102
110
  int row = draw & ${(1 << FILL_SLOT) - 1};
103
111
  vSlot = (draw >> ${FILL_SLOT}) & 15;
104
112
  ivec2 at = ivec2((row & ${ROWS_A_LINE - 1}) * ${ROW_TEXELS}, row >> ${Math.log2(ROWS_A_LINE)});