webgl2 1.0.3 → 1.0.5

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/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2025 WebGL2 Platform Team
2
+ All Rights Reserved.
3
+
4
+ THIS IS A RESTRICTIVE PLACEHOLDER LICENSE (PROPRIETARY).
5
+
6
+ Permission to use, copy, modify, distribute, or publish this software, in whole
7
+ or in part, is NOT granted. All rights are reserved by the copyright holder.
8
+
9
+ This file is intentionally restrictive and intended only as a temporary
10
+ placeholder to allow packaging/testing in private. Replace this file with an
11
+ appropriate open-source license (for example, MIT or Apache-2.0)
12
+ if you intend to grant any rights to others.
13
+
14
+ -- END OF PLACEHOLDER LICENSE --
package/README.md CHANGED
@@ -1,120 +1,205 @@
1
- ## GLSL++: Unified WASM Core for Debugging and Generation
1
+ # WebGL2 Development Platform: VISION
2
2
 
3
- This plan is a comprehensive roadmap for the **GLSL++** project. The core is a single **WASM artifact** that provides two distinct, clean interfaces for both runtime emulation (Component 1) and build-time code generation (Component 2).
3
+ A **Rust + WASM** based toolkit for debugging GLSL shaders and generating ergonomic WebGL2 bindings.
4
4
 
5
- The entire system is focused on **Correctness and Debugging Fidelity**, leveraging existing Rust graphics crates to minimize the implementation of the $\sim$300 WebGL2 APIs.
5
+ ![WebGL2 Singing Dog Logo](./webgl2.png)
6
6
 
7
- -----
7
+ ## 🎯 Quick Start
8
8
 
9
- ## I. Unified WASM Architecture and Delivery
9
+ ```bash
10
+ # Build the project
11
+ cargo build --release
10
12
 
11
- The project compiles into a single `.wasm` file and a single `.js` glue file, ensuring maximum portability between Node.js and browsers.
13
+ # Compile a GLSL shader to WASM with debug info
14
+ cargo run --bin webgl2 -- compile tests/fixtures/simple.vert --debug
12
15
 
13
- ### A. Final Artifacts
16
+ # Validate a shader
17
+ cargo run --bin webgl2 -- validate tests/fixtures/simple.frag
14
18
 
15
- | Artifact | Content | Purpose |
16
- | :--- | :--- | :--- |
17
- | **`glsl_plus_plus.wasm`** | Compiled Rust code for both Component 1 (GL Emulation) and Component 2 (Transpiler). | The **Core Engine**. |
18
- | **`glsl_plus_plus.js`** | Universal ES Module wrapper for asynchronous loading and initialization. | The **Clean Interface** for consumers. |
19
+ # Generate TypeScript harness
20
+ cargo run --bin webgl2 -- codegen tests/fixtures/simple.vert -o output.ts
21
+ ```
19
22
 
20
- ### B. Core Dependencies (Shared `Cargo.toml`)
23
+ ### Build via npm
24
+
25
+ This repository is both a Rust workspace and an npm package. Run the npm build helper to build the Rust workspace for the WASM target and copy produced .wasm files into `runners/wasm/`:
26
+
27
+ ```bash
28
+ # from repository root
29
+ npm run build
30
+ ```
21
31
 
22
- | Crate | Role | Component(s) |
32
+ Notes:
33
+ - The script runs `cargo build --target wasm32-unknown-unknown --release` and copies any `.wasm` files from `target/wasm32-unknown-unknown/release/` into `runners/wasm/`.
34
+ - If you need `wasm-bindgen` output (JS glue), run `wasm-bindgen` manually on the produced `.wasm` files; adding automated wasm-bindgen support is a follow-up task.
35
+
36
+ ## 🚀 Project Overview and Goals
37
+
38
+ The project aims to create a **Composite WebGL2 Development Platform** built with **Rust and WASM**. The primary objective is to significantly improve the developer experience by introducing standard software engineering practices—specifically, **robust debugging and streamlined resource management**—into the WebGL/GLSL workflow, which is currently hindered by platform-specific API complexity and opaque GPU execution.
39
+
40
+ | Key Goals | Target Block | Value Proposition |
23
41
  | :--- | :--- | :--- |
24
- | **`glow`** | **API Surface Definition (300+ methods).** | **C1** |
25
- | **`wasm-bindgen`** | **JS/WASM Interop and Struct Export.** | **C1 & C2** |
26
- | **`naga`** | **GLSL Parsing, Reflection, and IR Generation.** | **C1 & C2** |
27
- | **`glam`** | **Vector/Matrix Math for Rasterizer.** | **C1** |
28
- | **`hashbrown`** | **Fast Resource Registry (`u32` handles).** | **C1** |
29
- | **`serde` / `serde-wasm-bindgen`** | **Safe, clean transfer of configuration and metadata objects.** | **C2** |
30
- | **`wgpu-types`** | **Validated Resource Structs (optional, highly recommended).** | **C1** |
42
+ | **GPU Debugging** | Block 1 (Emulator) | Enable **step-through debugging**, breakpoints, and variable inspection for GLSL code. |
43
+ | **Unit Testing** | Block 1 (Emulator) | Provide a stable, deterministic environment for **automated testing** of graphics logic. |
44
+ | **API Ergonomics** | Block 2 (Codegen) | **Automate boilerplate code** for resource binding, attribute setup, and uniform linking. |
45
+ | **Tech Stack** | Both | Utilize **Rust for safety and WASM for high-performance cross-platform execution** in the browser. |
31
46
 
32
47
  -----
33
48
 
34
- ## II. Component 1: WASM GL Emulation Core (Runtime) 🎨
49
+ ## 🛠️ Functional Block 1: WebGL2 Software Rendering Pipeline (The Debugger)
35
50
 
36
- This component implements the $\sim$300 WebGL2 API methods via the `glow` trait, running a pure software rasterizer for unit testing.
51
+ This block provides the **deterministic, inspectable execution environment** for GLSL logic.
37
52
 
38
- ### A. External Interface: The Factory Function
53
+ ### 1\. **Core Component: Rust-based WebGL2 Emulator (`wasm-gl-emu`)**
39
54
 
40
- Component 1 is accessed through a factory function to manage state initialization, ensuring the clean API shape requested.
55
+ * **State Machine Emulation:** Implement a full software model of the WebGL2 state (e.g., Framebuffer Objects, Renderbuffers, Texture Units, Vertex Array Objects, current programs, depth/stencil settings). This component will track all API calls and maintain a CPU-accessible copy of all state.
56
+ * **Rasterization Logic:** Implement CPU-based vertex and fragment processing. This logic must precisely follow the WebGL2/OpenGL ES 3.0 specification, including clipping, primitive assembly, and the fragment pipeline (culling, depth test, blending).
57
+ * **Input/Output:** Expose the emulator's state and rendering results via a standard Rust interface that can be compiled to WASM and wrapped for JavaScript access.
41
58
 
42
- ```rust
43
- // In src/gl_emulation.rs
44
- #[wasm_bindgen(js_name = createGLContext)]
45
- pub fn create_gl_context(width: u32, height: u32) -> GLContextHandle {
46
- // Initializes internal state (framebuffer, viewport, etc.)
47
- // Returns a handle to the initialized context instance.
48
- // ...
49
- }
59
+ ### 2\. **GLSL Translation and Debugging Integration (`glsl-to-wasm`)**
50
60
 
51
- // All 300+ methods (e.g., buffer_data, draw_arrays) are implemented as methods on GLContextHandle
52
- // and are automatically exposed by wasm-bindgen.
53
- ```
61
+ * **GLSL Frontend:** Use a Rust-based GLSL parser (e.g., based on `naga` or a custom parser) to convert shader source code into an Intermediate Representation (IR).
62
+ * **WASM Backend:** Compile the IR into **WASM module functions**. Each shader stage (Vertex, Fragment) will be converted into a function that executes the shader logic on a single vertex or fragment input.
63
+ * **Source Map Generation:** The crucial step is to generate **high-quality source maps** that link the generated WASM instructions back to the original GLSL source code line and variable names. This enables DevTools/IDE to pause WASM execution and display the corresponding GLSL line and variable values.
64
+ * **JIT Compilation:** For performance during debugging, the WASM modules can be compiled using a fast JIT compiler (potentially leveraging existing browser capabilities or a custom runtime) to execute the many vertex/fragment calls.
54
65
 
55
- ### B. Internal Implementation: The `glow::Context` Trait
66
+ ### 3\. **Debugging and Testing Harness**
56
67
 
57
- | Implementation Area | Detail | Rationale |
58
- | :--- | :--- | :--- |
59
- | **API Surface** | Implement the **`glow::Context`** trait on the internal `WasmGLContext` struct. Methods not required for unit testing (e.g., `end_query`, `fence_sync`) are stubbed out to return success/no-op. | Minimizes implementation effort while guaranteeing API correctness. |
60
- | **Resource Management** | **`WasmGLContext`** contains a **`hashbrown::HashMap<u32, ResourceData>`** registry. All API calls (`createBuffer`, `bindTexture`) reference and mutate resources based on these internal `u32` handles. | Avoids complex host pointers and ensures a deterministic, debuggable state machine. |
61
- | **Buffer Data Transfer** | The body of `buffer_data()` performs a **transactional copy** of the incoming JavaScript `TypedArray` data directly into the target `Vec<u8>` within the WASM linear memory heap. | Ensures data is persistent and safely managed within the WASM sandbox. |
68
+ * **Test Runner:** Develop a testing harness in Rust/WASM that can execute a defined set of inputs (e.g., vertex attributes, uniform values) against the emulated pipeline and assert on the final pixel output or intermediate variable state.
69
+ * **Step-Through Integration:** The WASM modules, when run by the browser's JavaScript engine, will expose the generated source maps, allowing the developer to naturally set **breakpoints** within the GLSL source file viewed in the browser's DevTools (or a connected IDE).
62
70
 
63
- ### C. The Software Rasterizer Loop (Triggered by `draw_arrays`)
71
+ -----
64
72
 
65
- The core of the emulation resides here:
73
+ ## ⚙️ Functional Block 2: Introspection and Codegen Tool (The Ergonomics Layer)
66
74
 
67
- 1. **Shader Integration (Naga):** The `link_program` step uses **`naga`** to translate the GLSL into a Rust function pointer/closure (the **CPU Kernel**).
68
- 2. **Execution Stages:** The `draw_arrays()` body runs the full pipeline: Input Assembly $\rightarrow$ **Vertex Kernel Execution** (calling the Rust function for each vertex) $\rightarrow$ Rasterization (`glam` math) $\rightarrow$ **Fragment Kernel Execution** (calling the Rust function for each pixel).
69
- 3. **Output:** Writes the final color values to the **`WasmGLContext.framebuffer`** array.
75
+ This block automates the error-prone, repetitive JavaScript/TypeScript code required for WebGL2 resource setup.
70
76
 
71
- ### D. Final Output
77
+ ### 1\. **GLSL Introspection Engine (`glsl-parser-rs`)**
72
78
 
73
- The last step exposes the pixel data cleanly for display:
79
+ * **Parsing:** Use a dedicated Rust parser to analyze the GLSL source files (both Vertex and Fragment shaders).
80
+ * **Annotation Extraction:** The parser must identify and extract **discardable annotations** embedded in the GLSL source.
81
+ * *Example Annotation:*
82
+ ```glsl
83
+ //! @buffer_layout MeshData
84
+ layout(location = 0) in vec3 a_position;
85
+ // JSDoc-like for resource description
86
+ /** @uniform_group Camera @semantic ProjectionMatrix */
87
+ uniform mat4 u_projection;
88
+ ```
89
+ * **Resource Mapping:** Generate a structured data model (e.g., JSON or Rust structs) that lists all attributes, uniforms, uniform blocks, and their associated types, locations, and extracted metadata (like semantic hints from the annotations).
74
90
 
75
- ```rust
76
- // In src/gl_emulation.rs
77
- #[wasm_bindgen(js_name = presentFrame)]
78
- pub fn present_frame(handle: &GLContextHandle) -> js_sys::Uint8Array {
79
- // Safely creates a view/copy of the framebuffer Vec<u8> for JavaScript.
80
- // ...
81
- }
82
- ```
91
+ ### 2\. **Code Generation Module (`js-harness-codegen`)**
92
+
93
+ * **Harness Template:** Define a set of customizable templates (e.g., Handlebars, Tera) for generating the target **JavaScript/TypeScript harness code**.
94
+ * **Code Generation:** Using the resource map data from the parser, the module will generate files that:
95
+ * Define **JavaScript/TypeScript classes** for each shader program.
96
+ * Provide methods for **binding resource objects** (e.g., `program.setUniforms(cameraData)`).
97
+ * Implement all the necessary `gl.bindBuffer()`, `gl.getUniformLocation()`, `gl.vertexAttribPointer()`, and `gl.uniformX()` calls.
98
+ * *Goal:* Reduce the developer's WebGL setup code to simple, type-safe resource assignments.
99
+
100
+ ### 3\. **Tool Integration**
101
+
102
+ * Package the Rust component as a **Command-Line Interface (CLI)** tool (or an accompanying library) that runs during the build step, consuming GLSL files and outputting the JavaScript/TypeScript harness code. This integrates smoothly into modern build pipelines (e.g., Webpack, Vite).
83
103
 
84
104
  -----
85
105
 
86
- ## III. Component 2: Transpiler/Codegen Tool (Build-Time) 🛠️
106
+ ## Project Phases and Deliverables
107
+
108
+ | Phase | Duration | Focus | Key Deliverables |
109
+ | :--- | :--- | :--- | :--- |
110
+ | **Phase 1** | 3 Months | Core Compiler & Codegen | Functional GLSL parser (Block 2). CLI tool for JS harness generation (Block 2). Prototype `glsl-to-wasm` compiler (Block 1). |
111
+ | **Phase 2** | 4 Months | Core Emulator Implementation | Basic WebGL2 State Machine and Triangle Rasterizer (Block 1). Source Map integration (GLSL \<-\> WASM \<-\> DevTools) (Block 1). |
112
+ | **Phase 3** | 3 Months | Feature Completion & Polishing | Full WebGL2 feature set support in emulator (textures, complex blending, stencil). Robustness testing and documentation. |
113
+ | **Phase 4** | 2 Months | Integration & Release | Unit testing framework integration. Final documentation and developer tutorials. Full platform release. |
114
+
115
+ -----
116
+
117
+ ## � Project Structure
118
+
119
+ ```
120
+ webgl2/
121
+ ├── crates/
122
+ │ ├── naga-wasm-backend/ # Naga IR → WASM compiler with DWARF
123
+ │ ├── wasm-gl-emu/ # Software rasterizer & WASM runtime
124
+ │ ├── glsl-introspection/ # GLSL parser + annotation extraction
125
+ │ ├── js-codegen/ # TypeScript harness generator
126
+ │ └── webgl2-cli/ # Command-line interface
127
+ ├── tests/fixtures/ # Test shaders
128
+ ├── docs/ # Detailed documentation
129
+ │ ├── 1-plan.md # Original project plan
130
+ │ └── 1.1-ir-wasm.md # Naga IR → WASM architecture
131
+ └── external/ # Reference implementations (naga, wgpu, servo)
132
+ ```
133
+
134
+ ## 🏗️ Architecture
135
+
136
+ This project uses **Naga** (from the wgpu/WebGPU ecosystem) as the shader IR, rather than building a custom IR from scratch. This significantly reduces complexity while providing a proven, well-maintained foundation.
137
+
138
+ ### Key Components
139
+
140
+ 1. **naga-wasm-backend**: Translates Naga IR to WebAssembly with DWARF debug information
141
+ 2. **wasm-gl-emu**: Executes WASM shaders in a software rasterizer for debugging
142
+ 3. **glsl-introspection**: Parses GLSL and extracts resource metadata
143
+ 4. **js-codegen**: Generates ergonomic TypeScript bindings
144
+ 5. **webgl2-cli**: Unified command-line tool
145
+
146
+ See [`docs/1.1-ir-wasm.md`](docs/1.1-ir-wasm.md) for detailed architecture documentation.
147
+
148
+ ## 🔧 Development Status
149
+
150
+ **Current Phase: Phase 0 - Foundation Setup** ✅
151
+
152
+ - [x] Workspace structure created
153
+ - [x] Core crate skeletons implemented
154
+ - [x] Basic WASM backend (emits empty functions)
155
+ - [x] Runtime structure (wasmtime integration)
156
+ - [x] CLI tool with compile/validate/codegen/run commands
157
+ - [ ] DWARF debug information generation (in progress)
158
+ - [ ] Browser DevTools integration validation
159
+
160
+ ## 📚 Documentation
87
161
 
88
- Component 2 runs once during the build process, taking GLSL source and returning a structured object containing the generated JS code and metadata. It operates as a stateless function.
162
+ - [`docs/1-plan.md`](docs/1-plan.md) - Original project proposal and plan
163
+ - [`docs/1.1-ir-wasm.md`](docs/1.1-ir-wasm.md) - Naga IR → WASM architecture (recommended reading)
164
+ - [`external/use.md`](external/use.md) - Guide to leveraging external repositories
89
165
 
90
- ### A. External Interface: Structured Object Return
166
+ ## Project Phases
91
167
 
92
- The function accepts the GLSL source and a native JS options object, returning a structured JS object that prevents string-whackery.
168
+ | Phase | Duration | Focus | Status |
169
+ | :--- | :--- | :--- | :--- |
170
+ | **Phase 0** | 2 Weeks | Foundation & DWARF Validation | **In Progress** |
171
+ | **Phase 1** | 8 Weeks | Core Backend (scalars, vectors, control flow) | Not Started |
172
+ | **Phase 2** | 6 Weeks | Advanced Features (uniforms, textures, matrices) | Not Started |
173
+ | **Phase 3** | 4 Weeks | Software Rasterizer Integration | Not Started |
174
+ | **Phase 4** | 8 Weeks | Codegen Tool & Polish | Not Started |
93
175
 
94
- ```rust
95
- // In src/codegen.rs
96
- #[wasm_bindgen(js_name = generateKernelJS)]
97
- pub fn generate_kernel_js(
98
- glsl_source: &str,
99
- options_js: JsValue // Incoming JS object for configuration
100
- ) -> Result<GeneratedOutput, JsValue> {
101
- // ... logic ...
102
- }
176
+ ## 🧪 Testing
177
+
178
+ ```bash
179
+ # Run all tests
180
+ cargo test
181
+
182
+ # Test with a simple shader
183
+ cargo run --bin webgl2 -- compile tests/fixtures/simple.vert --debug -o output.wasm
184
+ cargo run --bin webgl2 -- run output.wasm
103
185
  ```
104
186
 
105
- ### B. Internal Implementation: Reflection and Codec
106
-
107
- 1. **Options Deserialization:** Use **`serde-wasm-bindgen`** to safely convert the incoming `JsValue` (the options object) into a **`CodegenOptions`** Rust struct for internal type safety.
108
- 2. **Naga Reflection:** The core logic uses **`naga`** to parse the GLSL and analyze the Intermediate Representation (IR). This analysis extracts:
109
- * All **Uniform** names, types, and locations.
110
- * All **Attribute** names and types.
111
- * All **Texture/Sampler** definitions.
112
- 3. **JavaScript Generation:** Use the reflection data to programmatically generate the user-facing JavaScript class string, including:
113
- * Class definition (`class MyKernel { ... }`).
114
- * **Typed Setter Methods** (e.g., `setUniformFloat(name, value)`).
115
- * WASM calls to Component 1's low-level `buffer_data`, `uniform_1f`, etc., methods.
116
- 4. **Structured Output (Codec):** Build the final **`GeneratedOutput`** struct, which uses **`#[wasm_bindgen]`** to ensure clean object transfer:
117
- * `kernel_code`: The generated JavaScript class string.
118
- * `metadata`: Array of objects detailing the introspected resources.
119
- * `diagnostics`: Array of objects containing warnings and errors from `naga` and custom checks.
187
+ ## 🤝 Contributing
188
+
189
+ This project is in early development. Contributions are welcome once Phase 0 is complete and the architecture is validated.
190
+
191
+ ## 📄 License
192
+
193
+ MIT OR Apache-2.0
194
+
195
+ -----
196
+
197
+ ## �💰 Resource Requirements
198
+
199
+ The project will require specialized expertise in:
200
+
201
+ * **Rust and WASM Development**
202
+ * **Computer Graphics / GPU Pipeline Implementation** (for the emulator)
203
+ * **Compiler Design / Language Tooling** (for GLSL parsing and source map generation)
120
204
 
205
+ Would you like to discuss the **specific toolchain recommendations** for the GLSL-to-WASM compilation process?
package/index.js ADDED
@@ -0,0 +1,221 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ WasmWebGL2RenderingContext,
5
+ ERR_OK,
6
+ ERR_INVALID_HANDLE,
7
+ readErrorMessage
8
+ } from './src/webgl2_context.js';
9
+
10
+ /**
11
+ * WebGL2 Prototype: Rust-owned Context, JS thin-forwarder
12
+ * Implements docs/1.1.1-webgl2-prototype.md
13
+ *
14
+ * This module provides:
15
+ * - WasmWebGL2RenderingContext: JS class that forwards all calls to WASM
16
+ * - webGL2(): factory function to create a new context
17
+ *
18
+ * WASM owns all runtime state (textures, framebuffers, contexts).
19
+ * JS is a thin forwarder with no emulation of WebGL behavior.
20
+ *
21
+ * Explicit lifecycle: caller must call destroy() to free resources.
22
+ * All operations return errno (0 = OK). Non-zero errno causes JS to throw
23
+ * with the message from wasm_last_error_ptr/len.
24
+ */
25
+
26
+
27
+ const isNode =
28
+ typeof process !== 'undefined' &&
29
+ process.versions != null &&
30
+ process.versions.node != null;
31
+
32
+ /** @typedef {number} u32 */
33
+
34
+ /**
35
+ * Factory function: create a new WebGL2 context.
36
+ *
37
+ * This function:
38
+ * 1. Auto-loads webgl2.wasm (expects it next to index2.js)
39
+ * 2. Instantiates the WASM module with memory
40
+ * 3. Creates a Rust-owned context via wasm_create_context()
41
+ * 4. Returns a WasmWebGL2RenderingContext JS wrapper
42
+ *
43
+ * @param {Object} opts - options (unused for now)
44
+ * @returns {Promise<WasmWebGL2RenderingContext>}
45
+ * @throws {Error} if WASM loading or instantiation fails
46
+ */
47
+ async function webGL2(opts = {}) {
48
+ // Load WASM binary
49
+ const { ex, instance } = await (
50
+ wasmInitPromise ||
51
+ (async () => {
52
+ // ensure success is cached but not failure
53
+ let succeeded = false;
54
+ try {
55
+ const wasm = await initWASM();
56
+ succeeded = true;
57
+ return wasm;
58
+ } finally {
59
+ if (!succeeded)
60
+ wasmInitPromise = undefined;
61
+ }
62
+ })());
63
+
64
+ // Create a context in WASM
65
+ const ctxHandle = ex.wasm_create_context();
66
+ if (ctxHandle === 0) {
67
+ const msg = readErrorMessage(instance);
68
+ throw new Error(`Failed to create context: ${msg}`);
69
+ }
70
+
71
+ // Wrap and return
72
+ const gl = new WasmWebGL2RenderingContext(instance, ctxHandle);
73
+ return gl;
74
+ }
75
+
76
+ /**
77
+ * @type {(
78
+ * Promise<{ ex: WebAssembly.Exports, instance: WebAssembly.Instance }> |
79
+ * { ex: WebAssembly.Exports, instance: WebAssembly.Instance } |
80
+ * undefined
81
+ * )}
82
+ */
83
+ var wasmInitPromise;
84
+
85
+ async function initWASM() {
86
+ let wasmBuffer;
87
+ if (isNode) {
88
+ // Use dynamic imports so this module can be loaded in the browser too.
89
+ const path = await import('path');
90
+ const fs = await import('fs');
91
+ const { fileURLToPath } = await import('url');
92
+ const wasmPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'webgl2.wasm');
93
+ if (!fs.existsSync(wasmPath)) {
94
+ throw new Error(`WASM not found at ${wasmPath}. Run: npm run build:wasm`);
95
+ }
96
+ // readFileSync is available on the imported namespace
97
+ wasmBuffer = fs.readFileSync(wasmPath);
98
+ } else {
99
+ // Browser: fetch the wasm relative to this module
100
+ const resp = await fetch(new URL('./webgl2.wasm', import.meta.url));
101
+ if (!resp.ok) {
102
+ throw new Error(`Failed to fetch webgl2.wasm: ${resp.status}`);
103
+ }
104
+ wasmBuffer = await resp.arrayBuffer();
105
+ }
106
+
107
+ // Compile WASM module
108
+ const wasmModule = await WebAssembly.compile(wasmBuffer);
109
+
110
+ // Create memory (WASM will import it)
111
+ const memory = new WebAssembly.Memory({ initial: 16, maximum: 256 });
112
+
113
+ // Instantiate WASM
114
+ const importObj = { env: { memory } };
115
+ const instance = await WebAssembly.instantiate(wasmModule, importObj);
116
+
117
+ // Verify required exports
118
+ const ex = instance.exports;
119
+ if (typeof ex.wasm_create_context !== 'function') {
120
+ throw new Error('WASM module missing wasm_create_context export');
121
+ }
122
+ return wasmInitPromise = { ex, instance };
123
+ }
124
+
125
+ /**
126
+ * Reads an error message from WASM memory and returns it.
127
+ * @param {WebAssembly.Instance} instance
128
+ * @returns {string}
129
+ */
130
+ function _readErrorMessage(instance) {
131
+ const ex = instance.exports;
132
+ if (!ex || typeof ex.wasm_last_error_ptr !== 'function' || typeof ex.wasm_last_error_len !== 'function') {
133
+ return '(no error message available)';
134
+ }
135
+ const ptr = ex.wasm_last_error_ptr();
136
+ const len = ex.wasm_last_error_len();
137
+ if (ptr === 0 || len === 0) {
138
+ return '';
139
+ }
140
+ const mem = new Uint8Array(ex.memory.buffer);
141
+ const bytes = mem.subarray(ptr, ptr + len);
142
+ return new TextDecoder('utf-8').decode(bytes);
143
+ }
144
+
145
+ /**
146
+ * Checks a WASM return code (errno).
147
+ * If non-zero, reads the error message and throws.
148
+ * @param {number} code
149
+ * @param {WebAssembly.Instance} instance
150
+ * @throws {Error} if code !== 0
151
+ */
152
+ function _checkErr(code, instance) {
153
+ if (code === ERR_OK) return;
154
+ const msg = _readErrorMessage(instance);
155
+ throw new Error(`WASM error ${code}: ${msg}`);
156
+ }
157
+
158
+
159
+ // Exports: ESM-style. Also attach globals in browser for convenience.
160
+ export { webGL2, WasmWebGL2RenderingContext, ERR_OK, ERR_INVALID_HANDLE };
161
+
162
+ if (typeof window !== 'undefined' && window) {
163
+ // also populate globals when running in a browser environment
164
+ try {
165
+ window.webGL2 = webGL2;
166
+ window.WasmWebGL2RenderingContext = WasmWebGL2RenderingContext;
167
+ } catch (e) {
168
+ // ignore if window is not writable
169
+ }
170
+ }
171
+
172
+ async function nodeDemo() {
173
+ console.log('Running index2.js demo...');
174
+ const gl = await webGL2();
175
+ console.log(`✓ Context created (handle will be managed by destroy())`);
176
+
177
+ // 1x1 texture with CornflowerBlue (100, 149, 237, 255)
178
+ const tex = gl.createTexture();
179
+ console.log(`✓ Texture created (handle: ${tex})`);
180
+
181
+ gl.bindTexture(0, tex);
182
+ const pixel = new Uint8Array([100, 149, 237, 255]);
183
+ gl.texImage2D(0, 0, 0, 1, 1, 0, 0, 0, pixel);
184
+ console.log(`✓ Texture uploaded`);
185
+
186
+ const fb = gl.createFramebuffer();
187
+ console.log(`✓ Framebuffer created (handle: ${fb})`);
188
+
189
+ gl.bindFramebuffer(0, fb);
190
+ gl.framebufferTexture2D(0, 0, 0, tex, 0);
191
+ console.log(`✓ Texture attached to framebuffer`);
192
+
193
+ const out = new Uint8Array(4);
194
+ gl.readPixels(0, 0, 1, 1, 0, 0, out);
195
+ console.log(
196
+ `✓ Pixel read: r=${out[0]}, g=${out[1]}, b=${out[2]}, a=${out[3]}`
197
+ );
198
+
199
+ if (out[0] === 100 && out[1] === 149 && out[2] === 237 && out[3] === 255) {
200
+ console.log('✓ Pixel matches expected CornflowerBlue!');
201
+ } else {
202
+ console.error('✗ Pixel mismatch!');
203
+ process.exit(1);
204
+ }
205
+
206
+ gl.destroy();
207
+ console.log('✓ Context destroyed');
208
+ console.log('\n✓ Demo passed!');
209
+ process.exit(0);
210
+ }
211
+
212
+ // CLI demo: run when executed directly in Node
213
+ if (isNode) {
214
+ (async () => {
215
+ const { fileURLToPath } = await import('url');
216
+ const path = (await import('path'));
217
+ if (fileURLToPath(import.meta.url) === process.argv[1]) {
218
+ nodeDemo();
219
+ }
220
+ })();
221
+ }
package/package.json CHANGED
@@ -1,10 +1,20 @@
1
1
  {
2
2
  "name": "webgl2",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "WebGL2 tools to derisk large GPU projects on the web beyond toys and demos.",
5
+ "type": "module",
5
6
  "main": "index.js",
6
7
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "start": "node ./index.js",
9
+ "build": "cargo build --target wasm32-unknown-unknown --release && cp target/wasm32-unknown-unknown/release/*.wasm .",
10
+ "prepublishOnly": "npm run build",
11
+ "test": "node --test test/*.test.js --parallel=1",
12
+ "test:smoke": "npm run build:wasm && node ./test/smoke.js"
13
+ },
14
+ "wasmBuild": {
15
+ "outDir": "wasm",
16
+ "target": "wasm32-unknown-unknown",
17
+ "profile": "release"
8
18
  },
9
19
  "repository": {
10
20
  "type": "git",
@@ -17,4 +27,11 @@
17
27
  "url": "https://github.com/mavity/mavity/issues"
18
28
  },
19
29
  "homepage": "https://github.com/mavity/mavity#readme"
30
+ ,
31
+ "files": [
32
+ "index.js",
33
+ "wasm",
34
+ "README.md",
35
+ "LICENSE"
36
+ ]
20
37
  }
@@ -1,51 +0,0 @@
1
- # WebGL Emulator (Component 1) Deep Dive
2
-
3
- The **WebGL Emulator** is the **pure-Rust software graphics driver** that compiles to a single, single-threaded WASM module. Its primary goal is **correctness, fidelity, and debuggability** by mimicking the entire WebGL2 state machine and rendering pipeline on the CPU.
4
-
5
- ## 1. ⚙️ Core Architecture and State Management
6
-
7
- The emulator's design centers on satisfying the **`glow::Context` trait** while managing all resources within the WASM linear memory.
8
-
9
- * **Factory Function:** The entire system is instantiated via the clean factory function: `createGLContext(width, height)`. This function initializes the full internal state and returns a handle.
10
- * **State Container:** The central struct, e.g., `WasmGLContext`, is the single source of truth for the entire GL state. It manages:
11
- * **Resource Registry (`hashbrown::HashMap<u32, ResourceData>`):** Maps the user-facing $\mathbf{u32}$ integer handles (returned by `gl.createBuffer()`, etc.) to the actual Rust structs holding the data and parameters.
12
- * **Framebuffer:** A large `Vec<u32>` or `Vec<u8>` in the WASM heap that stores the final pixel output.
13
- * **Bound State:** Fields tracking the currently bound program, active VAO, active texture unit, viewport dimensions, etc.
14
-
15
- ## 2. 🔌 Implementing the $\mathbf{300+}$ API Surface with `glow`
16
-
17
- This step minimizes bug surface by reusing the `glow` API definition.
18
-
19
- * **Trait Implementation:** The `WasmGLContext` struct implements every method defined in the `glow::Context` trait.
20
- * **Stubbing:** Approximately $\mathbf{80\%}$ of the methods implement simple state updates or stubs:
21
- * **State Setting:** Methods like `enable(DEPTH_TEST)` simply flip a boolean in the `WasmGLContext` struct.
22
- * **Resource Binding:** Methods like `bind_buffer(ARRAY_BUFFER, handle)` update the state machine's field to hold the input $\mathbf{u32}$ handle.
23
- * **Unsupported Features:** Methods for complex GPU features like `begin_query` or `fence_sync` are implemented as **no-ops** (stubs), returning success without affecting internal state, as they are non-goals for a unit-testing platform.
24
- * **Data Transfer (`buffer_data`):** The body of this function handles the vital **transactional copy** of `TypedArray` data from JavaScript into the WASM heap buffer referenced by the `u32` handle, ensuring data integrity within the emulation.
25
-
26
- ## 3. 🧠 The Complex Core: Shader Translation and Execution
27
-
28
- The most bespoke and critical logic is transforming the GLSL source into executable Rust code.
29
-
30
- ### A. Shader Compilation Flow (`compile_shader` / `link_program`)
31
-
32
- 1. **Parse & Reflect:** The `shader_source()` and `compile_shader()` methods feed the GLSL code into **`naga`**.
33
- * **Naga Output:** `naga` performs parsing, validation, and produces its **Intermediate Representation (IR)**.
34
- 2. **Code Generation (IR $\rightarrow$ Rust):** A custom routine is required to traverse the Naga IR and output a **Rust source code string** that accurately replicates the shader's logic.
35
- * **Vertex Kernel:** Generates a Rust function signature like `fn vs_kernel(uniforms: &Uniforms, vertex_in: &VertexIn) -> VertexOut`, where `VertexIn` and `VertexOut` are structs derived from the shader's attributes and varyings.
36
- * **Fragment Kernel:** Generates a Rust function signature like `fn fs_kernel(uniforms: &Uniforms, fragment_in: &FragmentIn) -> Color`, implementing all shading logic.
37
- 3. **Kernel Storage:** The Rust source code is compiled (or dynamically built/cached), and a **function pointer or closure** to this executable kernel is stored in the internal `ProgramData` resource.
38
-
39
- ### B. Software Rasterizer Execution (`draw_arrays` and similar)
40
-
41
- The `draw_arrays()` call initiates the CPU-bound process:
42
-
43
- 1. **Vertex Execution:** The emulation loop iterates through the vertex data. For each vertex, it calls the **Vertex Kernel (Rust function)**, which performs the vertex transformation using **`glam`** math and outputs the clip-space coordinates.
44
- 2. **Rasterization:** The **`glam`**-powered core logic handles triangle setup, clipping, barycentric interpolation of varyings, and determining which pixels are covered.
45
- 3. **Fragment Execution:** For every covered pixel, the loop calls the **Fragment Kernel (Rust function)**, passing the interpolated varyings and uniform data. This executes the final shading calculation.
46
- 4. **Output Merger:** Applies depth, stencil, and blending logic based on the current state before writing the final color result to the **WASM Framebuffer**.
47
-
48
- ## 4. 🔬 Debugging and Output
49
-
50
- * **Debugging Hooks:** The structure of the generated **Rust CPU Kernels** is explicitly designed to be inspected by WASM debugging tools (like browser DevTools). Because the GLSL is translated into traceable Rust code, the developer can pause execution inside the shader logic.
51
- * **Final Output:** The separate `presentFrame()` function acts as the single bridge to the host, copying the contents of the **WASM Framebuffer** to a JavaScript `Uint8Array` for display via `ctx.putImageData()`.