import {
WasmWebGL2RenderingContext,
ERR_OK,
ERR_INVALID_HANDLE,
readErrorMessage
} from './src/webgl2_context.js';
const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
async function webGL2({ debug = process.env.WEBGL2_DEBUG === 'true' } = {}) {
if (!wasmInitPromise) {
wasmInitPromise = (async () => {
let succeeded = false;
try {
const wasm = await initWASM({ debug });
succeeded = true;
return wasm;
} finally {
if (!succeeded)
wasmInitPromise = undefined;
}
})();
}
const { ex, instance } = await wasmInitPromise;
if (ex.wasm_init_coverage && ex.COV_MAP_PTR) {
const mapPtr = ex.COV_MAP_PTR.value;
const mem = new Uint32Array(ex.memory.buffer);
const numEntries = mem[mapPtr >>> 2];
ex.wasm_init_coverage(numEntries);
}
const ctxHandle = ex.wasm_create_context();
if (ctxHandle === 0) {
const msg = readErrorMessage(instance);
throw new Error(`Failed to create context: ${msg}`);
}
const gl = new WasmWebGL2RenderingContext(instance, ctxHandle);
return gl;
}
var wasmInitPromise;
async function initWASM({ debug } = {}) {
const wasmFile = debug ? 'webgl2.debug.wasm' : 'webgl2.wasm';
let wasmBuffer;
if (isNode) {
const path = await import('path');
const fs = await import('fs');
const { fileURLToPath } = await import('url');
const wasmPath = path.join(path.dirname(fileURLToPath(import.meta.url)), wasmFile);
if (!fs.existsSync(wasmPath)) {
throw new Error(`WASM not found at ${wasmPath}. Run: npm run build:wasm`);
}
wasmBuffer = fs.readFileSync(wasmPath);
} else {
const resp = await fetch(new URL('./' + wasmFile, import.meta.url));
if (!resp.ok) {
throw new Error(`Failed to fetch ${wasmFile}: ${resp.status}`);
}
wasmBuffer = await resp.arrayBuffer();
}
const wasmModule = await WebAssembly.compile(wasmBuffer);
let instance;
const importObject = {
env: {
print: (ptr, len) => {
const mem = new Uint8Array(instance.exports.memory.buffer);
const bytes = mem.subarray(ptr, ptr + len);
console.log(new TextDecoder('utf-8').decode(bytes));
},
wasm_execute_shader: (type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr) => {
if (WasmWebGL2RenderingContext.activeContext) {
WasmWebGL2RenderingContext.activeContext._executeShader(type, attrPtr, uniformPtr, varyingPtr, privatePtr, texturePtr);
}
}
}
};
instance = await WebAssembly.instantiate(wasmModule, importObject);
const ex = instance.exports;
if (typeof ex.wasm_create_context !== 'function') {
throw new Error('WASM module missing wasm_create_context export');
}
if (!(ex.memory instanceof WebAssembly.Memory)) {
throw new Error('WASM module missing memory export');
}
return wasmInitPromise = { ex, instance, module: wasmModule };
}
function _readErrorMessage(instance) {
const ex = instance.exports;
if (!ex || typeof ex.wasm_last_error_ptr !== 'function' || typeof ex.wasm_last_error_len !== 'function') {
return '(no error message available)';
}
const ptr = ex.wasm_last_error_ptr();
const len = ex.wasm_last_error_len();
if (ptr === 0 || len === 0) {
return '';
}
const mem = new Uint8Array(ex.memory.buffer);
const bytes = mem.subarray(ptr, ptr + len);
return new TextDecoder('utf-8').decode(bytes);
}
function _checkErr(code, instance) {
if (code === ERR_OK) return;
const msg = _readErrorMessage(instance);
throw new Error(`WASM error ${code}: ${msg}`);
}
export { webGL2, WasmWebGL2RenderingContext, ERR_OK, ERR_INVALID_HANDLE };
if (typeof window !== 'undefined' && window) {
try {
window.webGL2 = webGL2;
window.WasmWebGL2RenderingContext = WasmWebGL2RenderingContext;
} catch (e) {
}
}
async function nodeDemo() {
console.log('Running index2.js demo...');
const gl = await webGL2();
console.log(`✓ Context created (handle will be managed by destroy())`);
const tex = gl.createTexture();
console.log(`✓ Texture created (handle: ${tex})`);
gl.bindTexture(0, tex);
const pixel = new Uint8Array([100, 149, 237, 255]);
gl.texImage2D(0, 0, 0, 1, 1, 0, 0, 0, pixel);
console.log(`✓ Texture uploaded`);
const fb = gl.createFramebuffer();
console.log(`✓ Framebuffer created (handle: ${fb})`);
gl.bindFramebuffer(0, fb);
gl.framebufferTexture2D(0, 0, 0, tex, 0);
console.log(`✓ Texture attached to framebuffer`);
const out = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, 0, 0, out);
console.log(
`✓ Pixel read: r=${out[0]}, g=${out[1]}, b=${out[2]}, a=${out[3]}`
);
if (out[0] === 100 && out[1] === 149 && out[2] === 237 && out[3] === 255) {
console.log('✓ Pixel matches expected CornflowerBlue!');
} else {
console.error('✗ Pixel mismatch!');
process.exit(1);
}
gl.destroy();
console.log('✓ Context destroyed');
console.log('\n✓ Demo passed!');
process.exit(0);
}
if (isNode) {
(async () => {
const { fileURLToPath } = await import('url');
const path = (await import('path'));
if (fileURLToPath(import.meta.url) === process.argv[1]) {
nodeDemo();
}
})();
}