webgl2 1.1.18 → 1.2.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webgl2",
3
- "version": "1.1.18",
3
+ "version": "1.2.1",
4
4
  "description": "WebGL2 tools to derisk large GPU projects on the web beyond toys and demos.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -25,10 +25,12 @@ export class WasmWebGL2RenderingContext {
25
25
  FRAGMENT_SHADER = 0x8B30;
26
26
  VERTEX_SHADER = 0x8B31;
27
27
  TRIANGLES = 0x0004;
28
+ TRIANGLE_STRIP = 0x0005;
28
29
  COLOR_BUFFER_BIT = 0x00004000;
29
30
  DEPTH_BUFFER_BIT = 0x00000100;
30
31
  DEPTH_TEST = 0x0B71;
31
32
  STENCIL_TEST = 0x0B90;
33
+ SCISSOR_TEST = 0x0C11;
32
34
  STENCIL_BUFFER_BIT = 0x00000400;
33
35
  COMPILE_STATUS = 0x8B81;
34
36
  LINK_STATUS = 0x8B82;
@@ -36,6 +38,12 @@ export class WasmWebGL2RenderingContext {
36
38
  VALIDATE_STATUS = 0x8B83;
37
39
  ARRAY_BUFFER = 0x8892;
38
40
  ELEMENT_ARRAY_BUFFER = 0x8893;
41
+ COPY_READ_BUFFER = 0x8F36;
42
+ COPY_WRITE_BUFFER = 0x8F37;
43
+ PIXEL_PACK_BUFFER = 0x88EB;
44
+ PIXEL_UNPACK_BUFFER = 0x88EC;
45
+ UNIFORM_BUFFER = 0x8A11;
46
+ TRANSFORM_FEEDBACK_BUFFER = 0x8C8E;
39
47
  STATIC_DRAW = 0x88E4;
40
48
  BYTE = 0x1400;
41
49
  UNSIGNED_BYTE = 0x1401;
@@ -44,7 +52,6 @@ export class WasmWebGL2RenderingContext {
44
52
  INT = 0x1404;
45
53
  UNSIGNED_INT = 0x1405;
46
54
  FLOAT = 0x1406;
47
- RGBA = 0x1908;
48
55
  FLOAT_VEC2 = 0x8B50;
49
56
  FLOAT_VEC3 = 0x8B51;
50
57
  FLOAT_VEC4 = 0x8B52;
@@ -59,6 +66,7 @@ export class WasmWebGL2RenderingContext {
59
66
  FLOAT_MAT3 = 0x8B5B;
60
67
  FLOAT_MAT4 = 0x8B5C;
61
68
  SAMPLER_2D = 0x8B5E;
69
+ SAMPLER_3D = 0x8B5F;
62
70
  SAMPLER_CUBE = 0x8B60;
63
71
  ACTIVE_UNIFORMS = 0x8B86;
64
72
  ACTIVE_ATTRIBUTES = 0x8B89;
@@ -107,6 +115,8 @@ export class WasmWebGL2RenderingContext {
107
115
 
108
116
  RENDERBUFFER = 0x8D41;
109
117
  FRAMEBUFFER = 0x8D40;
118
+ READ_FRAMEBUFFER = 0x8CA8;
119
+ DRAW_FRAMEBUFFER = 0x8CA9;
110
120
  DEPTH_COMPONENT16 = 0x81A5;
111
121
  DEPTH_STENCIL = 0x84F9;
112
122
  RGBA4 = 0x8056;
@@ -141,10 +151,18 @@ export class WasmWebGL2RenderingContext {
141
151
  FRONT_AND_BACK = 0x0408;
142
152
 
143
153
  TEXTURE_2D = 0x0DE1;
154
+ TEXTURE_3D = 0x806F;
155
+ TEXTURE_2D_ARRAY = 0x8C1A;
144
156
  TEXTURE_WRAP_S = 0x2802;
145
157
  TEXTURE_WRAP_T = 0x2803;
158
+ TEXTURE_WRAP_R = 0x8072;
146
159
  TEXTURE_MAG_FILTER = 0x2800;
147
160
  TEXTURE_MIN_FILTER = 0x2801;
161
+ RGBA = 0x1908;
162
+ RED = 0x1903;
163
+ RG = 0x8227;
164
+ UNSIGNED_BYTE = 0x1401;
165
+ FLOAT = 0x1406;
148
166
  NEAREST = 0x2600;
149
167
  LINEAR = 0x2601;
150
168
  NEAREST_MIPMAP_NEAREST = 0x2700;
@@ -212,14 +230,23 @@ export class WasmWebGL2RenderingContext {
212
230
  /** @type {Map<number, WasmWebGL2RenderingContext>} */
213
231
  static _contexts = new Map();
214
232
 
215
- _executeShader(type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr) {
233
+ _executeShader(type, tableIdx, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr) {
216
234
  if (!this._currentProgram) {
217
235
  return;
218
236
  }
237
+
238
+ if (tableIdx > 0 && this._sharedTable) {
239
+ const func = this._sharedTable.get(tableIdx);
240
+ if (func) {
241
+ func(this._ctxHandle, type, tableIdx, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
242
+ return;
243
+ }
244
+ }
245
+
219
246
  const shaderInstance = type === this.VERTEX_SHADER ? this._currentProgram._vsInstance : this._currentProgram._fsInstance;
220
247
  if (shaderInstance && shaderInstance.exports.main) {
221
248
  // @ts-ignore
222
- shaderInstance.exports.main(type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
249
+ shaderInstance.exports.main(this._ctxHandle, type, tableIdx, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
223
250
  }
224
251
  }
225
252
 
@@ -292,8 +319,13 @@ export class WasmWebGL2RenderingContext {
292
319
  }
293
320
 
294
321
  let data = pixels;
295
- if (!data) data = new Uint8Array(width * height * 4);
296
- else if (!(data instanceof Uint8Array)) data = new Uint8Array(data);
322
+ if (!data) {
323
+ data = new Uint8Array(width * height * 4);
324
+ } else if (ArrayBuffer.isView(data)) {
325
+ data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
326
+ } else if (data instanceof ArrayBuffer) {
327
+ data = new Uint8Array(data);
328
+ }
297
329
 
298
330
  const len = data.length;
299
331
  const ptr = ex.wasm_alloc(len);
@@ -328,6 +360,56 @@ export class WasmWebGL2RenderingContext {
328
360
  }
329
361
  }
330
362
 
363
+ texImage3D(target, level, internalFormat, width, height, depth, border, format, type_, pixels) {
364
+ this._assertNotDestroyed();
365
+ const ex = this._instance.exports;
366
+ if (!ex || typeof ex.wasm_ctx_tex_image_3d !== 'function') {
367
+ throw new Error('wasm_ctx_tex_image_3d not found');
368
+ }
369
+
370
+ let data = pixels;
371
+ if (!data) {
372
+ data = new Uint8Array(width * height * depth * 4);
373
+ } else if (ArrayBuffer.isView(data)) {
374
+ data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
375
+ } else if (data instanceof ArrayBuffer) {
376
+ data = new Uint8Array(data);
377
+ }
378
+
379
+ const len = data.length;
380
+ const ptr = ex.wasm_alloc(len);
381
+ if (ptr === 0) throw new Error('Failed to allocate memory for pixel data');
382
+
383
+ try {
384
+ const mem = new Uint8Array(ex.memory.buffer);
385
+ mem.set(data, ptr);
386
+
387
+ const code = ex.wasm_ctx_tex_image_3d(
388
+ this._ctxHandle,
389
+ target >>> 0,
390
+ level >>> 0,
391
+ internalFormat >>> 0,
392
+ width >>> 0,
393
+ height >>> 0,
394
+ depth >>> 0,
395
+ border >>> 0,
396
+ format >>> 0,
397
+ type_ >>> 0,
398
+ ptr >>> 0,
399
+ len >>> 0
400
+ );
401
+ _checkErr(code, this._instance);
402
+
403
+ // Mirror texture data in JS for fast texel fetches by shader imports
404
+ this._textureData = this._textureData || new Map();
405
+ const handle = this._boundTexture || 0;
406
+ const copy = new Uint8Array(mem.slice(ptr, ptr + len));
407
+ this._textureData.set(handle, { width: width >>> 0, height: height >>> 0, depth: depth >>> 0, data: copy });
408
+ } finally {
409
+ ex.wasm_free(ptr);
410
+ }
411
+ }
412
+
331
413
  copyTexImage2D(target, level, internalFormat, x, y, width, height, border) {
332
414
  this._assertNotDestroyed();
333
415
  const ex = this._instance.exports;
@@ -478,9 +560,18 @@ export class WasmWebGL2RenderingContext {
478
560
  throw new Error('wasm_ctx_read_pixels not found');
479
561
  }
480
562
 
481
- const len = width * height * 4;
482
- if (!out || out.length < len) {
483
- throw new Error(`output buffer too small (need ${len}, have ${out ? out.length : 0})`);
563
+ let bpp = 4;
564
+ if (type_ === 0x1406) { // GL_FLOAT
565
+ if (format === 0x1908) bpp = 16; // GL_RGBA
566
+ else if (format === 0x8227) bpp = 8; // GL_RG
567
+ else if (format === 0x1903) bpp = 4; // GL_RED
568
+ } else if (type_ === 0x1401) { // GL_UNSIGNED_BYTE
569
+ if (format === 0x1908) bpp = 4;
570
+ }
571
+
572
+ const len = width * height * bpp;
573
+ if (!out || out.byteLength < len) {
574
+ throw new Error(`output buffer too small (need ${len} bytes, have ${out ? out.byteLength : 0})`);
484
575
  }
485
576
 
486
577
  const ptr = ex.wasm_alloc(len);
@@ -489,8 +580,8 @@ export class WasmWebGL2RenderingContext {
489
580
  try {
490
581
  const code = ex.wasm_ctx_read_pixels(
491
582
  this._ctxHandle,
492
- x >>> 0,
493
- y >>> 0,
583
+ x | 0,
584
+ y | 0,
494
585
  width >>> 0,
495
586
  height >>> 0,
496
587
  format >>> 0,
@@ -502,7 +593,8 @@ export class WasmWebGL2RenderingContext {
502
593
 
503
594
  const mem = new Uint8Array(ex.memory.buffer);
504
595
  const src = mem.subarray(ptr, ptr + len);
505
- out.set(src);
596
+ const out_bytes = new Uint8Array(out.buffer, out.byteOffset, len);
597
+ out_bytes.set(src);
506
598
  } finally {
507
599
  ex.wasm_free(ptr);
508
600
  }
@@ -708,9 +800,6 @@ export class WasmWebGL2RenderingContext {
708
800
  _checkErr(code, this._instance);
709
801
 
710
802
  // After linking, we need to instantiate the WASM modules on the host.
711
- // According to the WebGL 2.0 Spec (https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.6),
712
- // linkProgram sets the LINK_STATUS parameter. If linking fails, no executable is generated.
713
- // We must check LINK_STATUS before attempting to instantiate the WASM, as getProgramWasm will return null.
714
803
  if (program && typeof program === 'object') {
715
804
  const linkStatus = this.getProgramParameter(program, this.LINK_STATUS);
716
805
  if (linkStatus) {
@@ -723,6 +812,10 @@ export class WasmWebGL2RenderingContext {
723
812
  const vsWasm = this.getProgramWasm(program, this.VERTEX_SHADER);
724
813
  const fsWasm = this.getProgramWasm(program, this.FRAGMENT_SHADER);
725
814
 
815
+ if (!vsWasm || !fsWasm) {
816
+ return;
817
+ }
818
+
726
819
  // Allocate table slots for both shaders
727
820
  const vsIdx = this._tableAllocator ? this._tableAllocator.allocate() : null;
728
821
  const fsIdx = this._tableAllocator ? this._tableAllocator.allocate() : null;
@@ -749,7 +842,6 @@ export class WasmWebGL2RenderingContext {
749
842
  return {
750
843
  debug_step: (line, funcIdx, resultPtr) => {
751
844
  if (line === 999999) {
752
- console.log(`DEBUG LOG: val=${funcIdx} (0x${(funcIdx >>> 0).toString(16)})`);
753
845
  return;
754
846
  }
755
847
  const func = stubFuncs[line - 1];
@@ -775,13 +867,26 @@ export class WasmWebGL2RenderingContext {
775
867
  const vsInstanceRef = { current: null };
776
868
  const vsDebugEnv = createDebugEnv(this.VERTEX_SHADER, vsInstanceRef);
777
869
 
870
+ const env = {
871
+ memory: this._instance.exports.memory,
872
+ __indirect_function_table: this._sharedTable,
873
+ ...vsDebugEnv
874
+ };
778
875
 
779
- program._vsInstance = new WebAssembly.Instance(vsModule, {
780
- env: {
781
- memory: this._instance.exports.memory,
782
- __indirect_function_table: this._sharedTable,
783
- ...vsDebugEnv
876
+ // Add math builtins from renderer (skipping host)
877
+ const mathFuncs = [
878
+ 'gl_sin', 'gl_cos', 'gl_tan', 'gl_asin', 'gl_acos', 'gl_atan', 'gl_atan2',
879
+ 'gl_exp', 'gl_exp2', 'gl_log', 'gl_log2', 'gl_pow',
880
+ 'gl_sinh', 'gl_cosh', 'gl_tanh', 'gl_asinh', 'gl_acosh', 'gl_atanh'
881
+ ];
882
+ for (const name of mathFuncs) {
883
+ if (this._instance.exports[name]) {
884
+ env[name] = this._instance.exports[name];
784
885
  }
886
+ }
887
+
888
+ program._vsInstance = new WebAssembly.Instance(vsModule, {
889
+ env
785
890
  });
786
891
  vsInstanceRef.current = program._vsInstance;
787
892
 
@@ -796,13 +901,20 @@ export class WasmWebGL2RenderingContext {
796
901
  const fsInstanceRef = { current: null };
797
902
  const fsDebugEnv = createDebugEnv(this.FRAGMENT_SHADER, fsInstanceRef);
798
903
 
904
+ const fsEnv = {
905
+ memory: this._instance.exports.memory,
906
+ __indirect_function_table: this._sharedTable,
907
+ ...fsDebugEnv
908
+ };
799
909
 
800
- program._fsInstance = new WebAssembly.Instance(fsModule, {
801
- env: {
802
- memory: this._instance.exports.memory,
803
- __indirect_function_table: this._sharedTable,
804
- ...fsDebugEnv
910
+ for (const name of mathFuncs) {
911
+ if (this._instance.exports[name]) {
912
+ fsEnv[name] = this._instance.exports[name];
805
913
  }
914
+ }
915
+
916
+ program._fsInstance = new WebAssembly.Instance(fsModule, {
917
+ env: fsEnv
806
918
  });
807
919
  fsInstanceRef.current = program._fsInstance;
808
920
 
@@ -879,7 +991,7 @@ export class WasmWebGL2RenderingContext {
879
991
  throw new Error('wasm_ctx_delete_program not found');
880
992
  }
881
993
  const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
882
-
994
+
883
995
  // Free table indices
884
996
  if (program && typeof program === 'object') {
885
997
  if (program._vsTableIndex !== undefined && this._tableAllocator) {
@@ -889,7 +1001,7 @@ export class WasmWebGL2RenderingContext {
889
1001
  this._tableAllocator.free(program._fsTableIndex);
890
1002
  }
891
1003
  }
892
-
1004
+
893
1005
  const code = ex.wasm_ctx_delete_program(this._ctxHandle, programHandle);
894
1006
  _checkErr(code, this._instance);
895
1007
  if (program && typeof program === 'object') {
@@ -1087,6 +1199,7 @@ export class WasmWebGL2RenderingContext {
1087
1199
  stride >>> 0,
1088
1200
  offset >>> 0
1089
1201
  );
1202
+ if (code === 5) return; // ERR_GL
1090
1203
  _checkErr(code, this._instance);
1091
1204
  }
1092
1205
 
@@ -1104,6 +1217,7 @@ export class WasmWebGL2RenderingContext {
1104
1217
  stride >>> 0,
1105
1218
  offset >>> 0
1106
1219
  );
1220
+ if (code === 5) return; // ERR_GL
1107
1221
  _checkErr(code, this._instance);
1108
1222
  }
1109
1223
 
@@ -1282,6 +1396,12 @@ export class WasmWebGL2RenderingContext {
1282
1396
  }
1283
1397
 
1284
1398
  const len = bytes.length;
1399
+ if (len === 0) {
1400
+ const code = ex.wasm_ctx_buffer_data(this._ctxHandle, target >>> 0, 0, 0, usage >>> 0);
1401
+ _checkErr(code, this._instance);
1402
+ return;
1403
+ }
1404
+
1285
1405
  const ptr = ex.wasm_alloc(len);
1286
1406
  if (ptr === 0) throw new Error('Failed to allocate memory for bufferData');
1287
1407
 
@@ -1321,7 +1441,22 @@ export class WasmWebGL2RenderingContext {
1321
1441
  ex.wasm_free(ptr);
1322
1442
  }
1323
1443
  }
1324
- copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1444
+ copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) {
1445
+ this._assertNotDestroyed();
1446
+ const ex = this._instance.exports;
1447
+ if (!ex || typeof ex.wasm_ctx_copy_buffer_sub_data !== 'function') {
1448
+ throw new Error('wasm_ctx_copy_buffer_sub_data not found');
1449
+ }
1450
+ const code = ex.wasm_ctx_copy_buffer_sub_data(
1451
+ this._ctxHandle,
1452
+ readTarget >>> 0,
1453
+ writeTarget >>> 0,
1454
+ readOffset >>> 0,
1455
+ writeOffset >>> 0,
1456
+ size >>> 0
1457
+ );
1458
+ _checkErr(code, this._instance);
1459
+ }
1325
1460
  getBufferParameter(target, pname) {
1326
1461
  this._assertNotDestroyed();
1327
1462
  const ex = this._instance.exports;
@@ -1508,10 +1643,65 @@ export class WasmWebGL2RenderingContext {
1508
1643
  _checkErr(code, this._instance);
1509
1644
  }
1510
1645
  copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1511
- texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1646
+ texSubImage2D(target, level, xoffset, yoffset, width, height, format, type_, pixels) {
1647
+ this._assertNotDestroyed();
1648
+ const ex = this._instance.exports;
1649
+ if (!ex || typeof ex.wasm_ctx_tex_sub_image_2d !== 'function') {
1650
+ throw new Error('wasm_ctx_tex_sub_image_2d not found');
1651
+ }
1652
+
1653
+ let data = pixels;
1654
+ if (!data) return; // No-op if no data provided
1655
+ if (!(data instanceof Uint8Array)) {
1656
+ if (ArrayBuffer.isView(data)) {
1657
+ data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1658
+ } else {
1659
+ data = new Uint8Array(data);
1660
+ }
1661
+ }
1662
+
1663
+ const len = data.length;
1664
+ const ptr = ex.wasm_alloc(len);
1665
+ if (ptr === 0) throw new Error('Failed to allocate memory for sub-pixel data');
1666
+
1667
+ try {
1668
+ const mem = new Uint8Array(ex.memory.buffer);
1669
+ mem.set(data, ptr);
1670
+
1671
+ const code = ex.wasm_ctx_tex_sub_image_2d(
1672
+ this._ctxHandle,
1673
+ target >>> 0,
1674
+ level >>> 0,
1675
+ xoffset | 0,
1676
+ yoffset | 0,
1677
+ width >>> 0,
1678
+ height >>> 0,
1679
+ format >>> 0,
1680
+ type_ >>> 0,
1681
+ ptr >>> 0,
1682
+ len >>> 0
1683
+ );
1684
+ _checkErr(code, this._instance);
1685
+ } finally {
1686
+ ex.wasm_free(ptr);
1687
+ }
1688
+ }
1512
1689
 
1513
1690
  checkFramebufferStatus(target) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1514
- blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1691
+ blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) {
1692
+ this._assertNotDestroyed();
1693
+ const ex = this._instance.exports;
1694
+ if (!ex || typeof ex.wasm_ctx_blit_framebuffer !== 'function') {
1695
+ throw new Error('wasm_ctx_blit_framebuffer not found');
1696
+ }
1697
+ const code = ex.wasm_ctx_blit_framebuffer(
1698
+ this._ctxHandle,
1699
+ srcX0 | 0, srcY0 | 0, srcX1 | 0, srcY1 | 0,
1700
+ dstX0 | 0, dstY0 | 0, dstX1 | 0, dstY1 | 0,
1701
+ mask >>> 0, filter >>> 0
1702
+ );
1703
+ _checkErr(code, this._instance);
1704
+ }
1515
1705
  readBuffer(src) { this._assertNotDestroyed(); throw new Error('not implemented'); }
1516
1706
 
1517
1707
  pixelStorei(pname, param) { this._assertNotDestroyed(); throw new Error('not implemented'); }