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 +9 -2
- package/coverage.md +23 -19
- package/daebug.md +29 -0
- package/index.js +121 -80
- package/package.json +2 -2
- package/src/webgl2_context.js +349 -37
- package/src/webgpu_context.js +927 -0
- package/webgl2.debug.wasm +0 -0
- package/webgl2.wasm +0 -0
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 |
|
|
8
|
-
| src/naga_wasm_backend/backend.rs |
|
|
9
|
-
| src/naga_wasm_backend/control_flow.rs |
|
|
10
|
-
| src/naga_wasm_backend/
|
|
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/
|
|
13
|
-
| src/
|
|
14
|
-
| src/webgl2_context/
|
|
15
|
-
| src/webgl2_context/
|
|
16
|
-
| src/webgl2_context/
|
|
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 |
|
|
19
|
-
| src/webgl2_context/state.rs |
|
|
20
|
-
| src/webgl2_context/textures.rs |
|
|
21
|
-
| src/webgl2_context/types.rs |
|
|
22
|
-
| src/webgl2_context/vaos.rs |
|
|
23
|
-
|
|
|
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 |
|
|
30
|
-
| src/
|
|
31
|
-
| src/
|
|
32
|
-
| src/naga_wasm_backend/control_flow.rs |
|
|
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
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* {
|
|
93
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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
|
|
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.
|
|
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": "
|
|
15
|
+
"test": "node --import ./test/all-coverage.js --test test/**/*.test.js test/*.test.js"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|