@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.
@@ -24,6 +24,48 @@ const GLYPHS_LEAST = 1024;
24
24
  const LEAST = 1 << 16;
25
25
  /** Holes past which, and past the live vertices, the atlas is packed again. */
26
26
  const SLACK = 1 << 16;
27
+ /**
28
+ * The vertices, and the indices, staged before an upload: geometries placed
29
+ * one after another go to the GPU in one call for each buffer, not two a
30
+ * geometry, which shapes drawn anew each frame paid for some thousand times
31
+ * a frame. A geometry larger than the staging is uploaded from its own copy.
32
+ */
33
+ const STAGED_VERTICES = 1 << 14;
34
+ const STAGED_INDICES = 1 << 16;
35
+
36
+ /**
37
+ * `data`'s `count` vertices interleaved into `floats` from float `at`, `words` the same bytes:
38
+ * a position, a texel or zeros, then the colour's bytes or a mesh's depth.
39
+ */
40
+ function interleave(
41
+ data: GeometryData,
42
+ count: number,
43
+ floats: Float32Array,
44
+ words: Uint32Array,
45
+ at: number,
46
+ ): void {
47
+ const positions = data.positions;
48
+ const texels = data.texels;
49
+ const depths = data.depths;
50
+ const colors = data.colors;
51
+ for (let i = 0; i < count; i++, at += 5) {
52
+ floats[at] = positions[i * 2];
53
+ floats[at + 1] = positions[i * 2 + 1];
54
+ if (texels) {
55
+ floats[at + 2] = texels[i * 2];
56
+ floats[at + 3] = texels[i * 2 + 1];
57
+ } else {
58
+ floats[at + 2] = 0;
59
+ floats[at + 3] = 0;
60
+ }
61
+
62
+ if (depths) {
63
+ floats[at + 4] = depths[i];
64
+ } else {
65
+ words[at + 4] = colors[i];
66
+ }
67
+ }
68
+ }
27
69
 
28
70
  /** Where a geometry lies in the atlas, or the glyph atlas with its text's mode and table. */
29
71
  export interface Place {
@@ -58,6 +100,15 @@ export class GeometryAtlas {
58
100
  private indexTop = 0;
59
101
  private live = 0;
60
102
  private holes = 0;
103
+ /** Vertices staged for the atlas from vertex `stagedVertexAt`, as floats and as words. */
104
+ private readonly stagedFloats = new Float32Array(STAGED_VERTICES * (VERTEX_BYTES / 4));
105
+ private readonly stagedWords = new Uint32Array(this.stagedFloats.buffer);
106
+ private stagedVertexAt = 0;
107
+ private stagedVertices = 0;
108
+ /** Indices staged for the atlas from index `stagedIndexAt`. */
109
+ private readonly stagedIndices = new Uint32Array(STAGED_INDICES);
110
+ private stagedIndexAt = 0;
111
+ private stagedIndexCount = 0;
61
112
  places: (Place | null)[] = [];
62
113
  glyphArray!: WebGLVertexArrayObject;
63
114
  private glyphBuffer!: WebGLBuffer;
@@ -123,6 +174,8 @@ export class GeometryAtlas {
123
174
  });
124
175
  this.vertexCapacity = 0;
125
176
  this.indexCapacity = 0;
177
+ this.stagedVertices = 0;
178
+ this.stagedIndexCount = 0;
126
179
  this.places = [];
127
180
  }
128
181
 
@@ -147,6 +200,13 @@ export class GeometryAtlas {
147
200
  }
148
201
 
149
202
  geometries.removed.length = 0;
203
+ // Nothing left in it: filled again from its start, in the buffers it has.
204
+ if (this.live === 0) {
205
+ this.vertexTop = 0;
206
+ this.indexTop = 0;
207
+ this.holes = 0;
208
+ }
209
+
150
210
  // Changed in place: copied again where they lie, else as added.
151
211
  for (const id of geometries.updated) {
152
212
  const place = this.places[id];
@@ -211,6 +271,7 @@ export class GeometryAtlas {
211
271
  }
212
272
 
213
273
  geometries.added.length = 0;
274
+ this.flush();
214
275
  }
215
276
 
216
277
  /** Every live shape's geometry copied into new buffers, with room for twice what is live. */
@@ -233,6 +294,9 @@ export class GeometryAtlas {
233
294
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
234
295
  gl.bufferData(gl.ARRAY_BUFFER, this.vertexCapacity * VERTEX_BYTES, gl.STATIC_DRAW);
235
296
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indexCapacity * 4, gl.STATIC_DRAW);
297
+ // What was staged for the buffers replaced is copied again below.
298
+ this.stagedVertices = 0;
299
+ this.stagedIndexCount = 0;
236
300
  this.vertexTop = 0;
237
301
  this.indexTop = 0;
238
302
  this.live = 0;
@@ -243,6 +307,8 @@ export class GeometryAtlas {
243
307
  this.copyIn(id, data);
244
308
  }
245
309
  }
310
+
311
+ this.flush();
246
312
  }
247
313
 
248
314
  /** Every live text's quads copied into new glyph buffers: none till a text needs them. */
@@ -285,54 +351,110 @@ export class GeometryAtlas {
285
351
  return;
286
352
  }
287
353
 
288
- const gl = this.gl;
289
354
  const count = data.positions.length / 2;
290
355
  const base = this.vertexTop;
291
356
  this.copyVertices(data, base);
292
- const indices = new Uint32Array(data.indices.length);
293
- for (let i = 0; i < indices.length; i++) {
294
- indices[i] = data.indices[i] + base;
357
+ const source = data.indices;
358
+ const length = source.length;
359
+ if (length > STAGED_INDICES) {
360
+ const indices = new Uint32Array(length);
361
+ for (let i = 0; i < length; i++) {
362
+ indices[i] = source[i] + base;
363
+ }
364
+
365
+ this.flushIndices();
366
+ this.state.bindVertexArray(this.vertexArray);
367
+ this.gl.bufferSubData(this.gl.ELEMENT_ARRAY_BUFFER, this.indexTop * 4, indices);
368
+ } else {
369
+ if (
370
+ this.stagedIndexCount + length > STAGED_INDICES ||
371
+ this.stagedIndexAt + this.stagedIndexCount !== this.indexTop
372
+ ) {
373
+ this.flushIndices();
374
+ this.stagedIndexAt = this.indexTop;
375
+ }
376
+
377
+ const staged = this.stagedIndices;
378
+ const at = this.stagedIndexCount;
379
+ for (let i = 0; i < length; i++) {
380
+ staged[at + i] = source[i] + base;
381
+ }
382
+
383
+ this.stagedIndexCount = at + length;
295
384
  }
296
385
 
297
- this.state.bindVertexArray(this.vertexArray);
298
- gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, this.indexTop * 4, indices);
299
386
  this.places[id] = {
300
387
  vertexStart: base,
301
388
  vertices: count,
302
389
  indexStart: this.indexTop,
303
- indices: indices.length,
390
+ indices: length,
304
391
  glyphs: null,
305
392
  flat: data.flat === true,
306
393
  batch: this.batch(data),
307
394
  };
308
395
  this.vertexTop += count;
309
- this.indexTop += indices.length;
396
+ this.indexTop += length;
310
397
  this.live += count;
311
398
  }
312
399
 
313
- /** `data`'s vertices interleaved into the atlas from vertex `base`. */
400
+ /** `data`'s vertices interleaved for the atlas from vertex `base`: staged, or uploaded if too many. */
314
401
  private copyVertices(data: GeometryData, base: number): void {
315
- const gl = this.gl;
316
402
  const count = data.positions.length / 2;
317
- const bytes = new ArrayBuffer(count * VERTEX_BYTES);
318
- const floats = new Float32Array(bytes);
319
- const words = new Uint32Array(bytes);
320
- const texels = data.texels;
321
- const depths = data.depths;
322
- for (let i = 0; i < count; i++) {
323
- floats[i * 5] = data.positions[i * 2];
324
- floats[i * 5 + 1] = data.positions[i * 2 + 1];
325
- floats[i * 5 + 2] = texels ? texels[i * 2] : 0;
326
- floats[i * 5 + 3] = texels ? texels[i * 2 + 1] : 0;
327
- if (depths) {
328
- floats[i * 5 + 4] = depths[i];
329
- } else {
330
- words[i * 5 + 4] = data.colors[i];
331
- }
403
+ if (count > STAGED_VERTICES) {
404
+ const floats = new Float32Array(count * (VERTEX_BYTES / 4));
405
+ interleave(data, count, floats, new Uint32Array(floats.buffer), 0);
406
+ this.flushVertices();
407
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
408
+ this.gl.bufferSubData(this.gl.ARRAY_BUFFER, base * VERTEX_BYTES, floats);
409
+ return;
332
410
  }
333
411
 
334
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
335
- gl.bufferSubData(gl.ARRAY_BUFFER, base * VERTEX_BYTES, floats);
412
+ if (
413
+ this.stagedVertices + count > STAGED_VERTICES ||
414
+ this.stagedVertexAt + this.stagedVertices !== base
415
+ ) {
416
+ this.flushVertices();
417
+ this.stagedVertexAt = base;
418
+ }
419
+
420
+ interleave(data, count, this.stagedFloats, this.stagedWords, this.stagedVertices * 5);
421
+ this.stagedVertices += count;
422
+ }
423
+
424
+ /** What is staged, uploaded: before anything draws from the atlas. */
425
+ private flush(): void {
426
+ this.flushVertices();
427
+ this.flushIndices();
428
+ }
429
+
430
+ private flushVertices(): void {
431
+ if (this.stagedVertices > 0) {
432
+ const gl = this.gl;
433
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
434
+ gl.bufferSubData(
435
+ gl.ARRAY_BUFFER,
436
+ this.stagedVertexAt * VERTEX_BYTES,
437
+ this.stagedFloats,
438
+ 0,
439
+ this.stagedVertices * 5,
440
+ );
441
+ this.stagedVertices = 0;
442
+ }
443
+ }
444
+
445
+ private flushIndices(): void {
446
+ if (this.stagedIndexCount > 0) {
447
+ const gl = this.gl;
448
+ this.state.bindVertexArray(this.vertexArray);
449
+ gl.bufferSubData(
450
+ gl.ELEMENT_ARRAY_BUFFER,
451
+ this.stagedIndexAt * 4,
452
+ this.stagedIndices,
453
+ 0,
454
+ this.stagedIndexCount,
455
+ );
456
+ this.stagedIndexCount = 0;
457
+ }
336
458
  }
337
459
 
338
460
  private batch(data: GeometryData): Place["batch"] {