"use strict";
class HandleTable {
constructor() {
this.handles = {
0: null,
};
this.nextHandle = 1;
}
add(obj) {
const handle = this.nextHandle++;
this.handles[handle] = obj;
return handle;
}
get(handle) {
if (!this.has(handle)) {
throw new Error(`Resource with handle ${handle} not found.`);
}
return this.handles[handle];
}
remove(handle) {
let value = this.get(handle);
delete this.handles[handle];
return value;
}
has(handle) {
return this.handles.hasOwnProperty(handle);
}
}
export function createWasmAPI(canvas, options) {
let memory = null;
let handles = new HandleTable();
let decoder = new TextDecoder();
let encoder = new TextEncoder();
let gl = canvas.getContext("webgl2", options);
function getString(ptr, len) {
if (ptr === 0) {
return null;
}
return decoder.decode(new Uint8Array(memory.buffer, ptr, len));
}
function getTypedView(type, ptr, len) {
switch (type) {
case gl.UNSIGNED_BYTE:
return new Uint8Array(memory.buffer, ptr, len);
case gl.UNSIGNED_SHORT:
return new Uint16Array(memory.buffer, ptr, len / 2);
case gl.UNSIGNED_INT:
case gl.UNSIGNED_INT_24_8:
return new Uint32Array(memory.buffer, ptr, len / 4);
case gl.FLOAT:
return new Float32Array(memory.buffer, ptr, len / 4);
case gl.HALF_FLOAT:
return new Uint16Array(memory.buffer, ptr, len / 2);
default:
return new Uint8Array(memory.buffer, ptr, len);
}
}
return {
bindInstance(instance) {
memory = instance.exports.memory;
},
consoleLog(ptr, len) {
console.log(getString(ptr, len));
},
now() { return performance.now(); },
enable(cap) { gl.enable(cap); },
disable(cap) { gl.disable(cap); },
scissor(x, y, width, height) { gl.scissor(x, y, width, height); },
blendFunc(sfactor, dfactor) { gl.blendFunc(sfactor, dfactor); },
blendEquation(mode) { gl.blendEquation(mode); },
depthFunc(func) { gl.depthFunc(func); },
cullFace(mode) { gl.cullFace(mode); },
clearColor(r, g, b, a) { gl.clearColor(r, g, b, a); },
clearDepth(depth) { gl.clearDepth(depth); },
clearStencil(s) { gl.clearStencil(s); },
clear(mask) { gl.clear(mask); },
colorMask(r, g, b, a) { gl.colorMask(r, g, b, a); },
depthMask(flag) { gl.depthMask(flag); },
stencilMask(mask) { gl.stencilMask(mask); },
viewport(x, y, width, height) { gl.viewport(x, y, width, height); },
createBuffer() { return handles.add(gl.createBuffer()); },
bindBuffer(target, buffer) { gl.bindBuffer(target, handles.get(buffer)); },
bufferData(target, size, data_ptr, usage) {
if (data_ptr === 0) {
gl.bufferData(target, size, usage);
}
else if (memory) {
const data = new Uint8Array(memory.buffer, data_ptr, size);
gl.bufferData(target, data, usage);
}
},
bufferSubData(target, offset, size, data_ptr) {
if (!memory) return;
const data = new Uint8Array(memory.buffer, data_ptr, size);
gl.bufferSubData(target, offset, data);
},
deleteBuffer(buffer) {
gl.deleteBuffer(handles.get(buffer));
handles.remove(buffer);
},
enableVertexAttribArray(index) { gl.enableVertexAttribArray(index); },
disableVertexAttribArray(index) { gl.disableVertexAttribArray(index); },
vertexAttribPointer(index, size, type, normalized, stride, offset) {
gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
},
vertexAttribDivisor(index, divisor) { gl.vertexAttribDivisor(index, divisor); },
createProgram() { return handles.add(gl.createProgram()); },
deleteProgram(program) { let value = handles.remove(program); gl.deleteProgram(value); },
createShader(type) { return handles.add(gl.createShader(type)); },
deleteShader(shader) { let value = handles.remove(shader); gl.deleteShader(value); },
shaderSource(shader, source_ptr, source_len) {
if (!memory) return;
const source = decoder.decode(new Uint8Array(memory.buffer, source_ptr, source_len));
gl.shaderSource(handles.get(shader), source);
},
compileShader(shader) { gl.compileShader(handles.get(shader)); },
getShaderParameter(shader, pname) { return gl.getShaderParameter(handles.get(shader), pname); },
getShaderInfoLog(shader) {
const log = gl.getShaderInfoLog(handles.get(shader));
console.error(`Shader Info Log: ${log}`);
},
attachShader(program, shader) { gl.attachShader(handles.get(program), handles.get(shader)); },
linkProgram(program) { gl.linkProgram(handles.get(program)); },
useProgram(program) { gl.useProgram(handles.get(program)); },
getProgramParameter(program, pname) { return gl.getProgramParameter(handles.get(program), pname); },
getProgramInfoLog(program) {
const log = gl.getProgramInfoLog(handles.get(program));
console.error(`Program Info Log: ${log}`);
},
getActiveUniform(program, index, bufSize, length_ptr, size_ptr, type_ptr, name_ptr) {
const info = gl.getActiveUniform(handles.get(program), index);
if (!info) {
throw new Error(`No active uniform at index ${index}`);
}
const name = info.name;
const nameBytes = encoder.encode(name);
if (nameBytes.length >= bufSize) {
throw new Error(`Uniform name buffer too small: ${bufSize} bytes needed, ${nameBytes.length} bytes provided.`);
}
if (!memory) return;
new Uint8Array(memory.buffer, name_ptr, nameBytes.length).set(nameBytes);
if (length_ptr) {
new Uint32Array(memory.buffer, length_ptr, 1)[0] = nameBytes.length;
}
if (size_ptr) {
new Uint32Array(memory.buffer, size_ptr, 1)[0] = info.size;
}
if (type_ptr) {
new Uint32Array(memory.buffer, type_ptr, 1)[0] = info.type;
}
},
getUniformLocation(program, name_ptr, name_len) {
if (!memory) return 0;
const name = decoder.decode(new Uint8Array(memory.buffer, name_ptr, name_len));
const location = gl.getUniformLocation(handles.get(program), name);
if (!location) {
throw new Error(`Uniform location for '${name}' not found in program.`);
}
return handles.add(location);
},
getActiveAttrib(program, index, bufSize, length_ptr, size_ptr, type_ptr, name_ptr) {
const info = gl.getActiveAttrib(handles.get(program), index);
if (!info) {
throw new Error(`No active uniform at index ${index}`);
}
const name = info.name;
const nameBytes = encoder.encode(name);
if (nameBytes.length >= bufSize) {
throw new Error(`Attrib name buffer too small: ${bufSize} bytes needed, ${nameBytes.length} bytes provided.`);
}
if (!memory) return;
new Uint8Array(memory.buffer, name_ptr, nameBytes.length).set(nameBytes);
if (length_ptr) {
new Uint32Array(memory.buffer, length_ptr, 1)[0] = nameBytes.length;
}
if (size_ptr) {
new Uint32Array(memory.buffer, size_ptr, 1)[0] = info.size;
}
if (type_ptr) {
new Uint32Array(memory.buffer, type_ptr, 1)[0] = info.type;
}
},
getAttribLocation(program, name_ptr, name_len) {
if (!memory) return -1;
const name = decoder.decode(new Uint8Array(memory.buffer, name_ptr, name_len));
return gl.getAttribLocation(handles.get(program), name);
},
uniform1fv(location, count, value) {
if (!memory) return;
gl.uniform1fv(handles.get(location), new Float32Array(memory.buffer, value, count * 1));
},
uniform2fv(location, count, value) {
if (!memory) return;
gl.uniform2fv(handles.get(location), new Float32Array(memory.buffer, value, count * 2));
},
uniform3fv(location, count, value) {
if (!memory) return;
gl.uniform3fv(handles.get(location), new Float32Array(memory.buffer, value, count * 3));
},
uniform4fv(location, count, value) {
if (!memory) return;
gl.uniform4fv(handles.get(location), new Float32Array(memory.buffer, value, count * 4));
},
uniform1iv(location, count, value) {
if (!memory) return;
gl.uniform1iv(handles.get(location), new Int32Array(memory.buffer, value, count * 1));
},
uniform2iv(location, count, value) {
if (!memory) return;
gl.uniform2iv(handles.get(location), new Int32Array(memory.buffer, value, count * 2));
},
uniform3iv(location, count, value) {
if (!memory) return;
gl.uniform3iv(handles.get(location), new Int32Array(memory.buffer, value, count * 3));
},
uniform4iv(location, count, value) {
if (!memory) return;
gl.uniform4iv(handles.get(location), new Int32Array(memory.buffer, value, count * 4));
},
uniformMatrix2fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix2fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 4));
},
uniformMatrix2x3fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix2x3fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 6));
},
uniformMatrix2x4fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix2x4fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 8));
},
uniformMatrix3x2fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix3x2fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 6));
},
uniformMatrix3fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix3fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 9));
},
uniformMatrix3x4fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix3x4fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 12));
},
uniformMatrix4x2fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix4x2fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 8));
},
uniformMatrix4x3fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix4x3fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 12));
},
uniformMatrix4fv(location, count, transpose, value) {
if (!memory) return;
gl.uniformMatrix4fv(handles.get(location), transpose, new Float32Array(memory.buffer, value, count * 16));
},
createTexture() { return handles.add(gl.createTexture()); },
deleteTexture(texture) {
let value = handles.remove(texture);
gl.deleteTexture(value);
},
activeTexture(texture) { gl.activeTexture(texture); },
bindTexture(target, texture) { gl.bindTexture(target, handles.get(texture)); },
generateMipmap(target) { gl.generateMipmap(target); },
pixelStorei(pname, param) { gl.pixelStorei(pname, param); },
texParameteri(target, pname, param) { gl.texParameteri(target, pname, param); },
texStorage2D(target, levels, internalformat, width, height) { gl.texStorage2D(target, levels, internalformat, width, height); },
texImage2D(target, level, internalformat, width, height, border, format, type, pixels_ptr, pixels_len) {
if (!memory) return;
const pixels = getTypedView(type, pixels_ptr, pixels_len);
gl.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
},
texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels_ptr, pixels_len) {
if (!memory) return;
const pixels = getTypedView(type, pixels_ptr, pixels_len);
gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
},
createFramebuffer() { return handles.add(gl.createFramebuffer()); },
deleteFramebuffer(framebuffer) {
const value = handles.remove(framebuffer);
gl.deleteFramebuffer(value);
},
bindFramebuffer(target, framebuffer) { gl.bindFramebuffer(target, handles.get(framebuffer)); },
framebufferTexture2D(target, attachment, textarget, texture, level) {
gl.framebufferTexture2D(target, attachment, textarget, handles.get(texture), level);
},
drawBuffers(n, bufs_ptr) {
if (!memory) return;
const bufs = new Uint32Array(memory.buffer, bufs_ptr, n);
gl.drawBuffers(bufs);
},
readBuffer(src) { gl.readBuffer(src); },
checkFramebufferStatus(target) { return gl.checkFramebufferStatus(target); },
readPixels(x, y, width, height, format, type, pixels_ptr, pixels_len) {
if (!memory) return;
const pixels = getTypedView(type, pixels_ptr, pixels_len);
gl.readPixels(x, y, width, height, format, type, pixels);
},
drawArrays(mode, first, count) { gl.drawArrays(mode, first, count); },
drawElements(mode, count, type, offset) { gl.drawElements(mode, count, type, offset); },
drawArraysInstanced(mode, first, count, instancecount) { gl.drawArraysInstanced(mode, first, count, instancecount); },
drawElementsInstanced(mode, count, type, offset, instancecount) { gl.drawElementsInstanced(mode, count, type, offset, instancecount); },
}
}