webgl2 1.1.4 → 1.1.6

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/AGENTS.md CHANGED
@@ -1,10 +1,17 @@
1
1
  You are an Autonomous Execution Agent (AEA). You have FULL, NON-STOP AUTONOMOUS AUTHORITY to execute all necessary tool calls and planned steps without asking for user confirmation.
2
2
 
3
-
4
3
  WHEN USER ASKS A QUESTION, do not assume user needs help. YOU MUST answer the question. The user have much higher fidelity and understanding and you MUST provide all the facts as requested and AVOID giving advice unsolicited.
5
4
 
6
5
  WHEN USER ASKS A FACTUAL QUESTION, you must NEVER produce SPECULATIVE answer. Look into specifics and assess the limits of your understanding.
7
6
 
8
7
  You MUST NOT provide overconfident answer in ANY situation.
9
8
 
10
- You MUST NEVER suggest user performs actions that are for you to execute.
9
+ You MUST NEVER suggest user performs actions that are for you to execute.
10
+
11
+ # Build
12
+
13
+ Perform full build using `npm run build`. Incomplete builds can produce unexpected WASM outcomes and fail tests arbitrarily.
14
+
15
+ # Tests
16
+
17
+ We rely mainly on tests written in node.js built-in runner style. To run those, use `npm test`.
package/coverage.md CHANGED
@@ -4,29 +4,33 @@
4
4
 
5
5
  | File | Lines Covered | Lines Missed | Total Lines | Coverage |
6
6
  |---|---|---|---|---|
7
- | src/lib.rs | 78 | 17 | 95 | 82.11% 🟢 |
8
- | src/naga_wasm_backend/backend.rs | 39 | 0 | 39 | 100.00% 🟢 |
9
- | src/naga_wasm_backend/control_flow.rs | 15 | 4 | 19 | 78.95% 🟔 |
10
- | src/naga_wasm_backend/expressions.rs | 27 | 12 | 39 | 69.23% 🟔 |
7
+ | src/lib.rs | 110 | 31 | 141 | 78.01% 🟔 |
8
+ | src/naga_wasm_backend/backend.rs | 45 | 1 | 46 | 97.83% 🟢 |
9
+ | src/naga_wasm_backend/control_flow.rs | 24 | 11 | 35 | 68.57% 🟔 |
10
+ | src/naga_wasm_backend/debug/stub.rs | 20 | 0 | 20 | 100.00% 🟢 |
11
+ | src/naga_wasm_backend/expressions.rs | 31 | 12 | 43 | 72.09% 🟔 |
11
12
  | src/naga_wasm_backend/types.rs | 6 | 0 | 6 | 100.00% 🟢 |
12
- | src/wasm_gl_emu/rasterizer.rs | 1 | 0 | 1 | 100.00% 🟢 |
13
- | src/webgl2_context/buffers.rs | 16 | 1 | 17 | 94.12% 🟢 |
14
- | src/webgl2_context/drawing.rs | 25 | 5 | 30 | 83.33% 🟢 |
15
- | src/webgl2_context/framebuffers.rs | 8 | 0 | 8 | 100.00% 🟢 |
16
- | src/webgl2_context/registry.rs | 3 | 2 | 5 | 60.00% 🟔 |
13
+ | src/wasm_gl_emu/framebuffer.rs | 1 | 0 | 1 | 100.00% 🟢 |
14
+ | src/wasm_gl_emu/rasterizer.rs | 15 | 0 | 15 | 100.00% 🟢 |
15
+ | src/webgl2_context/buffers.rs | 15 | 2 | 17 | 88.24% 🟢 |
16
+ | src/webgl2_context/drawing.rs | 29 | 4 | 33 | 87.88% 🟢 |
17
+ | src/webgl2_context/framebuffers.rs | 7 | 0 | 7 | 100.00% 🟢 |
18
+ | src/webgl2_context/registry.rs | 5 | 0 | 5 | 100.00% 🟢 |
17
19
  | src/webgl2_context/renderbuffers.rs | 10 | 0 | 10 | 100.00% 🟢 |
18
- | src/webgl2_context/shaders.rs | 61 | 0 | 61 | 100.00% 🟢 |
19
- | src/webgl2_context/state.rs | 11 | 3 | 14 | 78.57% 🟔 |
20
- | src/webgl2_context/textures.rs | 9 | 0 | 9 | 100.00% 🟢 |
21
- | src/webgl2_context/types.rs | 11 | 1 | 12 | 91.67% 🟢 |
22
- | src/webgl2_context/vaos.rs | 16 | 0 | 16 | 100.00% 🟢 |
23
- | **Total** | **336** | **45** | **381** | **88.19% 🟢** |
20
+ | src/webgl2_context/shaders.rs | 70 | 3 | 73 | 95.89% 🟢 |
21
+ | src/webgl2_context/state.rs | 14 | 3 | 17 | 82.35% 🟢 |
22
+ | src/webgl2_context/textures.rs | 10 | 0 | 10 | 100.00% 🟢 |
23
+ | src/webgl2_context/types.rs | 9 | 1 | 10 | 90.00% 🟢 |
24
+ | src/webgl2_context/vaos.rs | 36 | 0 | 36 | 100.00% 🟢 |
25
+ | src/webgpu/adapter.rs | 3 | 0 | 3 | 100.00% 🟢 |
26
+ | src/webgpu/backend.rs | 41 | 20 | 61 | 67.21% 🟔 |
27
+ | **Total** | **501** | **88** | **589** | **85.06% 🟢** |
24
28
 
25
29
  ## Top Missed Files
26
30
 
27
31
  | File | Lines Missed | Illustrative Line | Coverage |
28
32
  |---|---|---|---|
29
- | src/lib.rs | 17/95 | [56] `pub fn js_log(level: u32, s: &str) {` | 82.11% 🟢 |
30
- | src/naga_wasm_backend/expressions.rs | 12/39 | [49] `if component_idx == 0 {` | 69.23% 🟔 |
31
- | src/webgl2_context/drawing.rs | 5/30 | [561] `for k in 0..64 {` | 83.33% 🟢 |
32
- | src/naga_wasm_backend/control_flow.rs | 4/19 | [80] `for _ in 0..types.len() {` | 78.95% 🟔 |
33
+ | src/lib.rs | 31/141 | [516] `/// Set uniform 3f.` | 78.01% 🟔 |
34
+ | src/webgpu/backend.rs | 20/61 | [919] `}` | 67.21% 🟔 |
35
+ | src/naga_wasm_backend/expressions.rs | 12/43 | [71] `if component_idx == 0 {` | 72.09% 🟔 |
36
+ | src/naga_wasm_backend/control_flow.rs | 11/35 | [171] `for i in (0..types.len()).rev() {` | 68.57% 🟔 |
package/daebug.md ADDED
@@ -0,0 +1,29 @@
1
+ # šŸ‘¾ Daebug remote debugging REPL started 10:08:53
2
+ > Interactive debugging REPL sessions for live browser contexts
3
+
4
+ This file tracks all active debugging sessions. Each entry represents a connected page or web worker where you can execute JavaScript code and see results in real-time.
5
+
6
+
7
+ ## Active Sessions
8
+
9
+ * [6-moss-1008-56-webworker](daebug/6-moss-1008-56-webworker.md) (worker://6-moss-1008-56-webworker) at 10:08:56: live
10
+ * [6-moss-1008-56](daebug/6-moss-1008-56.md) (http://localhost:8702/) at 10:08:56: live
11
+
12
+
13
+ ## How to Use
14
+
15
+ **Key Features:**
16
+ - **Live execution**: Code runs in actual browser/worker contexts, not simulated
17
+ - **Async/await support**: Promises resolve naturally, no special handling needed
18
+ - **Multi-realm**: Main pages and their web workers are tracked separately
19
+ - **File-based protocol**: No IDE plugins required, just edit Markdown files
20
+
21
+ **Workflow:**
22
+ 1. Open any session file from the list above
23
+ 2. Scroll to bottom and add code in a JS fenced block (after the prompt line)
24
+ 3. Save the file
25
+ 4. Results appear automatically within 1-2 seconds
26
+
27
+ ## Restart
28
+
29
+ To exit write `%%SHUTDOWN%%` **in this file on a separate line,** then run `npm start` again.
package/index.js CHANGED
@@ -6,6 +6,14 @@ import {
6
6
  ERR_INVALID_HANDLE,
7
7
  readErrorMessage
8
8
  } from './src/webgl2_context.js';
9
+ import { GPU, GPUBufferUsage, GPUMapMode, GPUTextureUsage } from './src/webgpu_context.js';
10
+
11
+ export const debug = {
12
+ getLcovReport,
13
+ resetLcovReport
14
+ };
15
+
16
+ export { ERR_OK, ERR_INVALID_HANDLE, GPUBufferUsage, GPUMapMode, GPUTextureUsage };
9
17
 
10
18
  /**
11
19
  * WebGL2 Prototype: Rust-owned Context, JS thin-forwarder
@@ -41,28 +49,29 @@ const isNode =
41
49
  * 4. Returns a WasmWebGL2RenderingContext JS wrapper
42
50
  *
43
51
  * @param {{
44
- * debug?: boolean,
52
+ * debug?: boolean | 'shaders' | 'rust' | 'all',
53
+ * size?: { width: number, height: number },
45
54
  * }} [opts] - options
46
55
  * @returns {Promise<WasmWebGL2RenderingContext>}
47
56
  * @throws {Error} if WASM loading or instantiation fails
48
57
  */
49
- async function webGL2({ debug = process.env.WEBGL2_DEBUG === 'true' } = {}) {
58
+ export async function webGL2({ debug = (typeof process !== 'undefined' ? process?.env || {} : typeof window !== 'undefined' ? window : globalThis).WEBGL2_DEBUG === 'true', size } = {}) {
59
+ // Determine if we need the debug WASM binary (Rust symbols)
60
+ const useDebugWasm = debug === true || debug === 'rust' || debug === 'all';
61
+
50
62
  // Load WASM binary
51
- if (!wasmInitPromise) {
52
- wasmInitPromise = (async () => {
53
- // ensure success is cached but not failure
54
- let succeeded = false;
55
- try {
56
- const wasm = await initWASM({ debug });
57
- succeeded = true;
58
- return wasm;
59
- } finally {
60
- if (!succeeded)
61
- wasmInitPromise = undefined;
63
+ let promise = wasmCache.get(useDebugWasm);
64
+ if (!promise) {
65
+ promise = initWASM({ debug: useDebugWasm });
66
+ wasmCache.set(useDebugWasm, promise);
67
+ // ensure success is cached but not failure
68
+ promise.catch(() => {
69
+ if (wasmCache.get(useDebugWasm) === promise) {
70
+ wasmCache.delete(useDebugWasm);
62
71
  }
63
- })();
72
+ });
64
73
  }
65
- const { ex, instance } = await wasmInitPromise;
74
+ const { ex, instance } = await promise;
66
75
 
67
76
  // Initialize coverage if available
68
77
  if (ex.wasm_init_coverage && ex.COV_MAP_PTR) {
@@ -83,17 +92,49 @@ async function webGL2({ debug = process.env.WEBGL2_DEBUG === 'true' } = {}) {
83
92
 
84
93
  // Wrap and return
85
94
  const gl = new WasmWebGL2RenderingContext(instance, ctxHandle);
95
+
96
+ // Set debug mode if requested
97
+ if (debug) {
98
+ gl.setDebugMode(debug);
99
+ }
100
+
101
+
102
+ if (size && typeof size.width === 'number' && typeof size.height === 'number') {
103
+ gl.resize(size.width, size.height);
104
+ gl.viewport(0, 0, size.width, size.height);
105
+ }
106
+
86
107
  return gl;
87
108
  }
88
109
 
89
110
  /**
90
- * @type {(
91
- * Promise<{ ex: WebAssembly.Exports, instance: WebAssembly.Instance }> |
92
- * { ex: WebAssembly.Exports, instance: WebAssembly.Instance } |
93
- * undefined
94
- * )}
111
+ * Factory function: create a new WebGPU instance.
112
+ *
113
+ * @param {{
114
+ * debug?: boolean | 'shaders' | 'rust' | 'all',
115
+ * }} [opts] - options
116
+ * @returns {Promise<GPU>}
95
117
  */
96
- var wasmInitPromise;
118
+ export async function webGPU({ debug = (typeof process !== 'undefined' ? process?.env || {} : typeof window !== 'undefined' ? window : globalThis).WEBGL2_DEBUG === 'true' } = {}) {
119
+ const useDebugWasm = debug === true || debug === 'rust' || debug === 'all';
120
+ let promise = wasmCache.get(useDebugWasm);
121
+ if (!promise) {
122
+ promise = initWASM({ debug: useDebugWasm });
123
+ wasmCache.set(useDebugWasm, promise);
124
+ promise.catch(() => {
125
+ if (wasmCache.get(useDebugWasm) === promise) {
126
+ wasmCache.delete(useDebugWasm);
127
+ }
128
+ });
129
+ }
130
+ const { ex, instance } = await promise;
131
+ return new GPU(ex, ex.memory);
132
+ }
133
+
134
+ /**
135
+ * @type {Map<boolean, Promise<{ ex: WebAssembly.Exports, instance: WebAssembly.Instance, module: WebAssembly.Module }>>}
136
+ */
137
+ const wasmCache = new Map();
97
138
 
98
139
  async function initWASM({ debug } = {}) {
99
140
  const wasmFile = debug ? 'webgl2.debug.wasm' : 'webgl2.wasm';
@@ -130,9 +171,12 @@ async function initWASM({ debug } = {}) {
130
171
  const bytes = mem.subarray(ptr, ptr + len);
131
172
  console.log(new TextDecoder('utf-8').decode(bytes));
132
173
  },
133
- wasm_execute_shader: (type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr) => {
134
- if (WasmWebGL2RenderingContext.activeContext) {
135
- WasmWebGL2RenderingContext.activeContext._executeShader(type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
174
+ wasm_execute_shader: (ctx, type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr) => {
175
+ const gl = WasmWebGL2RenderingContext._contexts.get(ctx);
176
+ if (gl) {
177
+ gl._executeShader(type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
178
+ } else {
179
+ // console.log(`DEBUG: wasm_execute_shader: ctx ${ctx} not found in _contexts`);
136
180
  }
137
181
  }
138
182
  }
@@ -147,7 +191,7 @@ async function initWASM({ debug } = {}) {
147
191
  if (!(ex.memory instanceof WebAssembly.Memory)) {
148
192
  throw new Error('WASM module missing memory export');
149
193
  }
150
- return wasmInitPromise = { ex, instance, module: wasmModule };
194
+ return { ex, instance, module: wasmModule };
151
195
  }
152
196
 
153
197
  /**
@@ -170,6 +214,55 @@ function _readErrorMessage(instance) {
170
214
  return new TextDecoder('utf-8').decode(bytes);
171
215
  }
172
216
 
217
+ /**
218
+ * Get LCOV coverage report from a context or device.
219
+ * @param {any} glOrGpu
220
+ * @returns {string}
221
+ */
222
+ function getLcovReport(glOrGpu) {
223
+ if (!glOrGpu) return '';
224
+
225
+ let ex;
226
+ if (glOrGpu._instance && glOrGpu._instance.exports) {
227
+ ex = glOrGpu._instance.exports;
228
+ } else if (glOrGpu.wasm) {
229
+ ex = glOrGpu.wasm;
230
+ } else if (glOrGpu._instance) {
231
+ ex = glOrGpu._instance;
232
+ }
233
+
234
+ if (ex && typeof ex.wasm_get_lcov_report_ptr === 'function' && typeof ex.wasm_get_lcov_report_len === 'function') {
235
+ const ptr = ex.wasm_get_lcov_report_ptr();
236
+ const len = ex.wasm_get_lcov_report_len();
237
+ if (ptr === 0 || len === 0) return '';
238
+ const mem = new Uint8Array(ex.memory.buffer);
239
+ const bytes = mem.subarray(ptr, ptr + len);
240
+ return new TextDecoder('utf-8').decode(bytes);
241
+ }
242
+ return '';
243
+ }
244
+
245
+ /**
246
+ * Reset LCOV coverage counters.
247
+ * @param {any} glOrGpu
248
+ */
249
+ export function resetLcovReport(glOrGpu) {
250
+ if (!glOrGpu) return;
251
+
252
+ let ex;
253
+ if (glOrGpu._instance && glOrGpu._instance.exports) {
254
+ ex = glOrGpu._instance.exports;
255
+ } else if (glOrGpu.wasm) {
256
+ ex = glOrGpu.wasm;
257
+ } else if (glOrGpu._instance) {
258
+ ex = glOrGpu._instance;
259
+ }
260
+
261
+ if (ex && typeof ex.wasm_reset_coverage === 'function') {
262
+ ex.wasm_reset_coverage();
263
+ }
264
+ }
265
+
173
266
  /**
174
267
  * Checks a WASM return code (errno).
175
268
  * If non-zero, reads the error message and throws.
@@ -183,67 +276,15 @@ function _checkErr(code, instance) {
183
276
  throw new Error(`WASM error ${code}: ${msg}`);
184
277
  }
185
278
 
186
-
187
- // Exports: ESM-style. Also attach globals in browser for convenience.
188
- export { webGL2, WasmWebGL2RenderingContext, ERR_OK, ERR_INVALID_HANDLE };
189
-
190
279
  if (typeof window !== 'undefined' && window) {
191
280
  // also populate globals when running in a browser environment
192
281
  try {
193
282
  window.webGL2 = webGL2;
283
+ window.webGPU = webGPU;
284
+ window.getLcovReport = getLcovReport;
285
+ window.resetLcovReport = resetLcovReport;
194
286
  window.WasmWebGL2RenderingContext = WasmWebGL2RenderingContext;
195
287
  } catch (e) {
196
288
  // ignore if window is not writable
197
289
  }
198
290
  }
199
-
200
- async function nodeDemo() {
201
- console.log('Running index2.js demo...');
202
- const gl = await webGL2();
203
- console.log(`āœ“ Context created (handle will be managed by destroy())`);
204
-
205
- // 1x1 texture with CornflowerBlue (100, 149, 237, 255)
206
- const tex = gl.createTexture();
207
- console.log(`āœ“ Texture created (handle: ${tex})`);
208
-
209
- gl.bindTexture(0, tex);
210
- const pixel = new Uint8Array([100, 149, 237, 255]);
211
- gl.texImage2D(0, 0, 0, 1, 1, 0, 0, 0, pixel);
212
- console.log(`āœ“ Texture uploaded`);
213
-
214
- const fb = gl.createFramebuffer();
215
- console.log(`āœ“ Framebuffer created (handle: ${fb})`);
216
-
217
- gl.bindFramebuffer(0, fb);
218
- gl.framebufferTexture2D(0, 0, 0, tex, 0);
219
- console.log(`āœ“ Texture attached to framebuffer`);
220
-
221
- const out = new Uint8Array(4);
222
- gl.readPixels(0, 0, 1, 1, 0, 0, out);
223
- console.log(
224
- `āœ“ Pixel read: r=${out[0]}, g=${out[1]}, b=${out[2]}, a=${out[3]}`
225
- );
226
-
227
- if (out[0] === 100 && out[1] === 149 && out[2] === 237 && out[3] === 255) {
228
- console.log('āœ“ Pixel matches expected CornflowerBlue!');
229
- } else {
230
- console.error('āœ— Pixel mismatch!');
231
- process.exit(1);
232
- }
233
-
234
- gl.destroy();
235
- console.log('āœ“ Context destroyed');
236
- console.log('\nāœ“ Demo passed!');
237
- process.exit(0);
238
- }
239
-
240
- // CLI demo: run when executed directly in Node
241
- if (isNode) {
242
- (async () => {
243
- const { fileURLToPath } = await import('url');
244
- const path = (await import('path'));
245
- if (fileURLToPath(import.meta.url) === process.argv[1]) {
246
- nodeDemo();
247
- }
248
- })();
249
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webgl2",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
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",
@@ -12,7 +12,7 @@
12
12
  "build-debug": "npm run build-debug-wasm-only && npm run build-debug-distill",
13
13
  "build": "npm run build-release && npm run build-debug",
14
14
  "prepublishOnly": "npm run build",
15
- "test": "NODE_OPTIONS='--import ./test/all-coverage.js' node --test test/*.test.js"
15
+ "test": "node --import ./test/all-coverage.js --test test/**/*.test.js test/*.test.js"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",