webgl2
Version:
WebGL2 tools to derisk large GPU projects on the web beyond toys and demos.
1,420 lines (1,286 loc) • 104 kB
JavaScript
// Thin forwarding WasmWebGL2RenderingContext and helpers
// This module contains the class and small helpers that operate on the
// WebAssembly instance. It is intentionally minimal: JS forwards calls to
// WASM and reads last-error strings when needed.
/** @typedef {number} u32 */
// Errno constants (must match src/webgl2_context.rs)
export const ERR_OK = 0;
export const ERR_INVALID_HANDLE = 1;
export const ERR_OOM = 2;
export const ERR_INVALID_ARGS = 3;
export const ERR_NOT_IMPLEMENTED = 4;
export const ERR_GL = 5;
export const ERR_INTERNAL = 6;
function getBPP(internalFormat) {
switch (internalFormat) {
case 0x822E: // R32F
case 0x8236: // R32UI
case 0x8235: // R32I
return 4;
case 0x8230: // RG32F
case 0x823C: // RG32UI
case 0x823B: // RG32I
return 8;
case 0x8814: // RGBA32F
case 0x8D70: // RGBA32UI
case 0x8D82: // RGBA32I
case 0x8815: // RGB32F (often promoted to RGBA32F)
case 0x8D71: // RGB32UI
case 0x8D83: // RGB32I
return 16;
case 0x822D: // R16F
case 0x8234: // R16UI
case 0x8233: // R16I
return 2;
case 0x822F: // RG16F
case 0x823A: // RG16UI
case 0x8239: // RG16I
return 4;
case 0x881A: // RGBA16F
case 0x8D76: // RGBA16UI
case 0x8D88: // RGBA16I
case 0x881B: // RGB16F
case 0x8D77: // RGB16UI
case 0x8D89: // RGB16I
return 8;
default:
return 4; // Default to RGBA8
}
}
import { WasmWebGLTexture } from './webgl2_texture.js';
import {
WasmWebGLShader,
WasmWebGLProgram,
WasmWebGLBuffer,
WasmWebGLRenderbuffer,
WasmWebGLFramebuffer,
WasmWebGLVertexArrayObject,
WasmWebGLQuery,
WasmWebGLSampler,
WasmWebGLSync,
WasmWebGLTransformFeedback,
WasmWebGLUniformLocation
} from './webgl2_resources.js';
/**
* @implements {WebGL2RenderingContext}
*/
export class WasmWebGL2RenderingContext {
// Constants
FRAGMENT_SHADER = 0x8B30;
VERTEX_SHADER = 0x8B31;
TRIANGLES = 0x0004;
TRIANGLE_STRIP = 0x0005;
COLOR_BUFFER_BIT = 0x00004000;
DEPTH_BUFFER_BIT = 0x00000100;
DEPTH_TEST = 0x0B71;
STENCIL_TEST = 0x0B90;
SCISSOR_TEST = 0x0C11;
BLEND = 0x0BE2;
CULL_FACE = 0x0B44;
STENCIL_BUFFER_BIT = 0x00000400;
COMPILE_STATUS = 0x8B81;
LINK_STATUS = 0x8B82;
DELETE_STATUS = 0x8B80;
VALIDATE_STATUS = 0x8B83;
ARRAY_BUFFER = 0x8892;
ELEMENT_ARRAY_BUFFER = 0x8893;
COPY_READ_BUFFER = 0x8F36;
COPY_WRITE_BUFFER = 0x8F37;
PIXEL_PACK_BUFFER = 0x88EB;
PIXEL_UNPACK_BUFFER = 0x88EC;
UNIFORM_BUFFER = 0x8A11;
TRANSFORM_FEEDBACK_BUFFER = 0x8C8E;
TRANSFORM_FEEDBACK_BUFFER_BINDING = 0x8C8F;
TRANSFORM_FEEDBACK_BUFFER_START = 0x8C84;
TRANSFORM_FEEDBACK_BUFFER_SIZE = 0x8C85;
TRANSFORM_FEEDBACK_BINDING = 0x8E25;
TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F;
TRANSFORM_FEEDBACK_VARYINGS = 0x8C83;
TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH = 0x8C76;
MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 0x8C8B;
MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A;
MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 0x8C80;
INTERLEAVED_ATTRIBS = 0x8C8C;
SEPARATE_ATTRIBS = 0x8C8D;
TRANSFORM_FEEDBACK_PAUSED = 0x8E23;
TRANSFORM_FEEDBACK_ACTIVE = 0x8E24;
TRANSFORM_FEEDBACK = 0x8E22;
RASTERIZER_DISCARD = 0x8C89;
STATIC_DRAW = 0x88E4;
BYTE = 0x1400;
UNSIGNED_BYTE = 0x1401;
SHORT = 0x1402;
UNSIGNED_SHORT = 0x1403;
INT = 0x1404;
UNSIGNED_INT = 0x1405;
FLOAT = 0x1406;
FLOAT_VEC2 = 0x8B50;
FLOAT_VEC3 = 0x8B51;
FLOAT_VEC4 = 0x8B52;
INT_VEC2 = 0x8B53;
INT_VEC3 = 0x8B54;
INT_VEC4 = 0x8B55;
BOOL = 0x8B56;
BOOL_VEC2 = 0x8B57;
BOOL_VEC3 = 0x8B58;
BOOL_VEC4 = 0x8B59;
FLOAT_MAT2 = 0x8B5A;
FLOAT_MAT3 = 0x8B5B;
FLOAT_MAT4 = 0x8B5C;
SAMPLER_2D = 0x8B5E;
SAMPLER_3D = 0x8B5F;
SAMPLER_CUBE = 0x8B60;
// Texture units (GL_TEXTURE0 .. GL_TEXTURE7)
TEXTURE0 = 0x84C0;
TEXTURE1 = 0x84C1;
TEXTURE2 = 0x84C2;
TEXTURE3 = 0x84C3;
TEXTURE4 = 0x84C4;
TEXTURE5 = 0x84C5;
TEXTURE6 = 0x84C6;
TEXTURE7 = 0x84C7;
// Max combined texture image units
MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D;
ACTIVE_UNIFORMS = 0x8B86;
ACTIVE_ATTRIBUTES = 0x8B89;
VIEWPORT = 0x0BA2;
COLOR_CLEAR_VALUE = 0x0C22;
COLOR_WRITEMASK = 0x0C23;
DEPTH_WRITEMASK = 0x0B72;
STENCIL_WRITEMASK = 0x0B98;
STENCIL_BACK_WRITEMASK = 0x8CA5;
DEPTH_FUNC = 0x0B74;
STENCIL_FUNC = 0x0B92;
STENCIL_VALUE_MASK = 0x0B93;
STENCIL_REF = 0x0B97;
STENCIL_BACK_FUNC = 0x8800;
STENCIL_BACK_VALUE_MASK = 0x8CA4;
STENCIL_BACK_REF = 0x8CA3;
STENCIL_FAIL = 0x0B94;
STENCIL_PASS_DEPTH_FAIL = 0x0B95;
STENCIL_PASS_DEPTH_PASS = 0x0B96;
STENCIL_BACK_FAIL = 0x8801;
STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802;
STENCIL_BACK_PASS_DEPTH_PASS = 0x8803;
BUFFER_SIZE = 0x8764;
MAX_VERTEX_ATTRIBS = 0x8869;
NO_ERROR = 0;
INVALID_ENUM = 0x0500;
INVALID_VALUE = 0x0501;
INVALID_OPERATION = 0x0502;
OUT_OF_MEMORY = 0x0505;
ZERO = 0;
ONE = 1;
CURRENT_VERTEX_ATTRIB = 0x8626;
VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622;
VERTEX_ATTRIB_ARRAY_SIZE = 0x8623;
VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624;
VERTEX_ATTRIB_ARRAY_TYPE = 0x8625;
VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A;
VERTEX_ATTRIB_ARRAY_POINTER = 0x8645;
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F;
VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88FE;
VERTEX_ATTRIB_ARRAY_INTEGER = 0x88FD;
RENDERBUFFER = 0x8D41;
FRAMEBUFFER = 0x8D40;
READ_FRAMEBUFFER = 0x8CA8;
DRAW_FRAMEBUFFER = 0x8CA9;
FRAMEBUFFER_COMPLETE = 0x8CD5;
FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6;
FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7;
FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9;
FRAMEBUFFER_UNSUPPORTED = 0x8CDD;
FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56;
RENDERBUFFER_SAMPLES = 0x8CAB;
FRAMEBUFFER_UNDEFINED = 0x8219;
MAX_DRAW_BUFFERS = 0x8824;
DRAW_BUFFER0 = 0x8825;
DRAW_BUFFER1 = 0x8826;
DRAW_BUFFER2 = 0x8827;
DRAW_BUFFER3 = 0x8828;
DRAW_BUFFER4 = 0x8829;
DRAW_BUFFER5 = 0x882A;
DRAW_BUFFER6 = 0x882B;
DRAW_BUFFER7 = 0x882C;
MAX_COLOR_ATTACHMENTS = 0x8CDF;
FRAMEBUFFER_BINDING = 0x8CA6;
DRAW_FRAMEBUFFER_BINDING = 0x8CA6;
READ_FRAMEBUFFER_BINDING = 0x8CAA;
RENDERBUFFER_BINDING = 0x8CA7;
DEPTH_COMPONENT16 = 0x81A5;
DEPTH_STENCIL = 0x84F9;
RGBA4 = 0x8056;
RGB565 = 0x8D62;
RGB5_A1 = 0x8057;
RGBA8 = 0x8058;
RGBA32F = 0x8814;
RGB32F = 0x8815;
RGBA16F = 0x881A;
RGB16F = 0x881B;
R8UI = 0x8232;
RG8UI = 0x8238;
RGB8UI = 0x8D7D;
RGBA8UI = 0x8D7C;
R16UI = 0x8234;
RG16UI = 0x823A;
RGB16UI = 0x8D77;
RGBA16UI = 0x8D76;
R32UI = 0x8236;
RG32UI = 0x823C;
RGB32UI = 0x8D71;
RGBA32UI = 0x8D70;
R8I = 0x8231;
RG8I = 0x8237;
RGB8I = 0x8D8F;
RGBA8I = 0x8D8E;
R16I = 0x8233;
RG16I = 0x8239;
RGB16I = 0x8D89;
RGBA16I = 0x8D88;
R32I = 0x8235;
RG32I = 0x823B;
RGB32I = 0x8D83;
RGBA32I = 0x8D82;
RED_INTEGER = 0x8D94;
RG_INTEGER = 0x8228;
RGB_INTEGER = 0x8D98;
RGBA_INTEGER = 0x8D9E;
R32F = 0x822E;
RG32F = 0x8230;
R16F = 0x822D;
RG16F = 0x822F;
STENCIL_INDEX8 = 0x8D48;
COLOR_ATTACHMENT0 = 0x8CE0;
COLOR_ATTACHMENT1 = 0x8CE1;
COLOR_ATTACHMENT2 = 0x8CE2;
COLOR_ATTACHMENT3 = 0x8CE3;
COLOR_ATTACHMENT4 = 0x8CE4;
COLOR_ATTACHMENT5 = 0x8CE5;
COLOR_ATTACHMENT6 = 0x8CE6;
COLOR_ATTACHMENT7 = 0x8CE7;
DEPTH_ATTACHMENT = 0x8D00;
STENCIL_ATTACHMENT = 0x8D20;
DEPTH_STENCIL_ATTACHMENT = 0x821A;
LESS = 0x0201;
EQUAL = 0x0202;
LEQUAL = 0x0203;
GREATER = 0x0204;
NOTEQUAL = 0x0205;
GEQUAL = 0x0206;
ALWAYS = 0x0207;
NEVER = 0x0200;
KEEP = 0x1E00;
REPLACE = 0x1E01;
INCR = 0x1E02;
DECR = 0x1E03;
INVERT = 0x150A;
INCR_WRAP = 0x8507;
DECR_WRAP = 0x8508;
FRONT = 0x0404;
BACK = 0x0405;
FRONT_AND_BACK = 0x0408;
TEXTURE_2D = 0x0DE1;
TEXTURE_3D = 0x806F;
TEXTURE_2D_ARRAY = 0x8C1A;
TEXTURE_WRAP_S = 0x2802;
TEXTURE_WRAP_T = 0x2803;
TEXTURE_WRAP_R = 0x8072;
TEXTURE_MAG_FILTER = 0x2800;
TEXTURE_MIN_FILTER = 0x2801;
RGBA = 0x1908;
RED = 0x1903;
RG = 0x8227;
UNSIGNED_BYTE = 0x1401;
FLOAT = 0x1406;
NEAREST = 0x2600;
LINEAR = 0x2601;
NEAREST_MIPMAP_NEAREST = 0x2700;
LINEAR_MIPMAP_NEAREST = 0x2701;
NEAREST_MIPMAP_LINEAR = 0x2702;
LINEAR_MIPMAP_LINEAR = 0x2703;
REPEAT = 0x2901;
CLAMP_TO_EDGE = 0x812F;
MIRRORED_REPEAT = 0x8370;
/**
* @param {{
* instance: WebAssembly.Instance,
* ctxHandle: number,
* width: number,
* height: number,
* debugShaders: boolean,
* sharedTable: any,
* tableAllocator: any
* }} options
*/
constructor({ instance, ctxHandle, width, height, debugShaders = false, sharedTable = null, tableAllocator = null, turboGlobals = null }) {
this._instance = instance;
this._ctxHandle = ctxHandle;
this._destroyed = false;
/** @type {import('./webgl2_resources.js').WasmWebGLProgram | null} */
this._currentProgram = null;
// Explicit booleans for clarity
this._debugShaders = !!debugShaders;
this._drawingBufferWidth = width;
this._drawingBufferHeight = height;
this._sharedTable = sharedTable;
this._tableAllocator = tableAllocator;
// TODO: potentially retrieve those one demand from the main WASM module when shader WASM modules are initialised
this._turboGlobals = turboGlobals;
/** @type {Map<number, WasmWebGLTexture>} */
this._textureHandles = new Map();
/** @type {Map<number, WasmWebGLFramebuffer>} */
this._fbHandles = new Map();
/** @type {Map<number, WasmWebGLRenderbuffer>} */
this._rbHandles = new Map();
/** @type {Map<number, WasmWebGLBuffer>} */
this._bufferHandles = new Map();
/** @type {Map<number, WasmWebGLProgram>} */
this._programHandles = new Map();
/** @type {Map<number, WasmWebGLShader>} */
this._shaderHandles = new Map();
/** @type {Map<number, WasmWebGLVertexArrayObject>} */
this._vaoHandles = new Map();
/** @type {Map<number, WasmWebGLSampler>} */
this._samplerHandles = new Map();
/** @type {Map<number, WasmWebGLQuery>} */
this._queryHandles = new Map();
/** @type {Map<number, WasmWebGLSync>} */
this._syncHandles = new Map();
/** @type {Map<number, WasmWebGLTransformFeedback>} */
this._tfHandles = new Map();
WasmWebGL2RenderingContext._contexts.set(this._ctxHandle, this);
}
get drawingBufferWidth() {
return this._drawingBufferWidth;
}
get drawingBufferHeight() {
return this._drawingBufferHeight;
}
resize(width, height) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_resize !== 'function') {
throw new Error('wasm_ctx_resize not found');
}
const code = ex.wasm_ctx_resize(this._ctxHandle, width, height);
_checkErr(code, this._instance);
this._drawingBufferWidth = width;
this._drawingBufferHeight = height;
}
// Set the viewport for rendering
viewport(x, y, width, height) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_viewport !== 'function') {
throw new Error('wasm_ctx_viewport not found');
}
const code = ex.wasm_ctx_viewport(this._ctxHandle, x | 0, y | 0, width >>> 0, height >>> 0);
_checkErr(code, this._instance);
}
/** @type {Map<number, WasmWebGL2RenderingContext>} */
static _contexts = new Map();
destroy() {
if (this._destroyed) return;
WasmWebGL2RenderingContext._contexts.delete(this._ctxHandle);
const ex = this._instance.exports;
if (ex && typeof ex.wasm_destroy_context === 'function') {
const code = ex.wasm_destroy_context(this._ctxHandle);
_checkErr(code, this._instance);
}
this._destroyed = true;
}
_assertNotDestroyed() {
if (this._destroyed) throw new Error('context has been destroyed');
}
createTexture() {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_create_texture !== 'function') {
throw new Error('wasm_ctx_create_texture not found');
}
const handle = ex.wasm_ctx_create_texture(this._ctxHandle);
if (handle === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`Failed to create texture: ${msg}`);
}
// Return a thin wrapper object representing the texture.
const tex = new WasmWebGLTexture(this, handle);
this._textureHandles.set(handle, tex);
return tex;
}
deleteTexture(tex) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_delete_texture !== 'function') {
throw new Error('wasm_ctx_delete_texture not found');
}
const handle = tex && typeof tex === 'object' && typeof tex._handle === 'number' ? tex._handle : (tex >>> 0);
const code = ex.wasm_ctx_delete_texture(this._ctxHandle, handle);
_checkErr(code, this._instance);
this._textureHandles.delete(handle);
// If a wrapper object was passed, mark it as deleted.
if (tex && typeof tex === 'object') {
try { tex._handle = 0; tex._deleted = true; } catch (e) { /* ignore */ }
}
}
bindTexture(target, tex) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_bind_texture !== 'function') {
throw new Error('wasm_ctx_bind_texture not found');
}
const handle = tex && typeof tex === 'object' && typeof tex._handle === 'number' ? tex._handle : (tex >>> 0);
const code = ex.wasm_ctx_bind_texture(this._ctxHandle, target >>> 0, handle);
_checkErr(code, this._instance);
// Record bound texture in JS so we can map units to texture data for texel fetch
this._boundTexture = handle;
this._textureUnits = this._textureUnits || [];
const unit = this._activeTextureUnit || 0;
this._textureUnits[unit] = handle;
}
texImage2D(target, level, internalFormat, width, height, border, format, type_, pixels) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_tex_image_2d !== 'function') {
throw new Error('wasm_ctx_tex_image_2d not found');
}
let data = pixels;
if (!data) {
data = new Uint8Array(width * height * getBPP(internalFormat));
} else if (ArrayBuffer.isView(data)) {
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
} else if (data instanceof ArrayBuffer) {
data = new Uint8Array(data);
}
const len = data.length;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for pixel data');
try {
const mem = new Uint8Array(ex.memory.buffer);
mem.set(data, ptr);
const code = ex.wasm_ctx_tex_image_2d(
this._ctxHandle,
target >>> 0,
level >>> 0,
internalFormat >>> 0,
width >>> 0,
height >>> 0,
border >>> 0,
format >>> 0,
type_ >>> 0,
ptr >>> 0,
len >>> 0
);
_checkErr(code, this._instance);
} finally {
ex.wasm_free(ptr);
}
}
texImage3D(target, level, internalFormat, width, height, depth, border, format, type_, pixels) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_tex_image_3d !== 'function') {
throw new Error('wasm_ctx_tex_image_3d not found');
}
let data = pixels;
if (!data) {
data = new Uint8Array(width * height * depth * getBPP(internalFormat));
} else if (ArrayBuffer.isView(data)) {
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
} else if (data instanceof ArrayBuffer) {
data = new Uint8Array(data);
}
const len = data.length;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for pixel data');
try {
const mem = new Uint8Array(ex.memory.buffer);
mem.set(data, ptr);
const code = ex.wasm_ctx_tex_image_3d(
this._ctxHandle,
target >>> 0,
level >>> 0,
internalFormat >>> 0,
width >>> 0,
height >>> 0,
depth >>> 0,
border >>> 0,
format >>> 0,
type_ >>> 0,
ptr >>> 0,
len >>> 0
);
_checkErr(code, this._instance);
} finally {
ex.wasm_free(ptr);
}
}
copyTexImage2D(target, level, internalFormat, x, y, width, height, border) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_copy_tex_image_2d !== 'function') {
throw new Error('wasm_ctx_copy_tex_image_2d not found');
}
const code = ex.wasm_ctx_copy_tex_image_2d(
this._ctxHandle,
target >>> 0,
level >>> 0,
internalFormat >>> 0,
x | 0,
y | 0,
width >>> 0,
height >>> 0,
border >>> 0
);
_checkErr(code, this._instance);
}
createFramebuffer() {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_create_framebuffer !== 'function') {
throw new Error('wasm_ctx_create_framebuffer not found');
}
const handle = ex.wasm_ctx_create_framebuffer(this._ctxHandle);
if (handle === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`Failed to create framebuffer: ${msg}`);
}
const fb = new WasmWebGLFramebuffer(this, handle);
this._fbHandles.set(handle, fb);
return fb;
}
deleteFramebuffer(fb) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_delete_framebuffer !== 'function') {
throw new Error('wasm_ctx_delete_framebuffer not found');
}
const handle = fb && typeof fb === 'object' && typeof fb._handle === 'number' ? fb._handle : (fb >>> 0);
const code = ex.wasm_ctx_delete_framebuffer(this._ctxHandle, handle);
_checkErr(code, this._instance);
this._fbHandles.delete(handle);
if (fb && typeof fb === 'object') {
try { fb._handle = 0; fb._deleted = true; } catch (e) { /* ignore */ }
}
}
bindFramebuffer(target, fb) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_bind_framebuffer !== 'function') {
throw new Error('wasm_ctx_bind_framebuffer not found');
}
const handle = fb && typeof fb === 'object' && typeof fb._handle === 'number' ? fb._handle : (fb >>> 0);
const code = ex.wasm_ctx_bind_framebuffer(this._ctxHandle, target >>> 0, handle);
_checkErr(code, this._instance);
}
framebufferTexture2D(target, attachment, textarget, texture, level) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_framebuffer_texture2d !== 'function') {
throw new Error('wasm_ctx_framebuffer_texture2d not found');
}
const texHandle = texture && typeof texture === 'object' && typeof texture._handle === 'number' ? texture._handle : (texture >>> 0);
const code = ex.wasm_ctx_framebuffer_texture2d(
this._ctxHandle,
target >>> 0,
attachment >>> 0,
textarget >>> 0,
texHandle,
level >>> 0
);
_checkErr(code, this._instance);
}
createRenderbuffer() {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_create_renderbuffer !== 'function') {
throw new Error('wasm_ctx_create_renderbuffer not found');
}
const handle = ex.wasm_ctx_create_renderbuffer(this._ctxHandle);
if (handle === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`Failed to create renderbuffer: ${msg}`);
}
const rb = new WasmWebGLRenderbuffer(this, handle);
this._rbHandles.set(handle, rb);
return rb;
}
bindRenderbuffer(target, renderbuffer) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_bind_renderbuffer !== 'function') {
throw new Error('wasm_ctx_bind_renderbuffer not found');
}
const rbHandle = renderbuffer && typeof renderbuffer === 'object' && typeof renderbuffer._handle === 'number' ? renderbuffer._handle : (renderbuffer >>> 0);
const code = ex.wasm_ctx_bind_renderbuffer(this._ctxHandle, target >>> 0, rbHandle);
_checkErr(code, this._instance);
}
deleteRenderbuffer(renderbuffer) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_delete_renderbuffer !== 'function') {
throw new Error('wasm_ctx_delete_renderbuffer not found');
}
const rbHandle = renderbuffer && typeof renderbuffer === 'object' && typeof renderbuffer._handle === 'number' ? renderbuffer._handle : (renderbuffer >>> 0);
const code = ex.wasm_ctx_delete_renderbuffer(this._ctxHandle, rbHandle);
_checkErr(code, this._instance);
this._rbHandles.delete(rbHandle);
if (renderbuffer && typeof renderbuffer === 'object') {
try { renderbuffer._handle = 0; renderbuffer._deleted = true; } catch (e) { /* ignore */ }
}
}
isRenderbuffer(rb) {
this._assertNotDestroyed();
if (!rb || typeof rb !== 'object' || !(rb instanceof WasmWebGLRenderbuffer)) return false;
if (rb._ctx !== this) return false;
const ex = this._instance.exports;
return ex.wasm_ctx_is_renderbuffer(this._ctxHandle, rb._handle) !== 0;
}
renderbufferStorage(target, internalFormat, width, height) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_renderbuffer_storage !== 'function') {
throw new Error('wasm_ctx_renderbuffer_storage not found');
}
const code = ex.wasm_ctx_renderbuffer_storage(this._ctxHandle, target >>> 0, internalFormat >>> 0, width | 0, height | 0);
_checkErr(code, this._instance);
}
framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_framebuffer_renderbuffer !== 'function') {
throw new Error('wasm_ctx_framebuffer_renderbuffer not found');
}
const rbHandle = renderbuffer && typeof renderbuffer === 'object' && typeof renderbuffer._handle === 'number' ? renderbuffer._handle : (renderbuffer >>> 0);
const code = ex.wasm_ctx_framebuffer_renderbuffer(
this._ctxHandle,
target >>> 0,
attachment >>> 0,
renderbuffertarget >>> 0,
rbHandle
);
_checkErr(code, this._instance);
}
readPixels(x, y, width, height, format, type_, out) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_read_pixels !== 'function') {
throw new Error('wasm_ctx_read_pixels not found');
}
const ptr = ex.wasm_ctx_read_pixels(
this._ctxHandle,
x | 0,
y | 0,
width >>> 0,
height >>> 0,
format >>> 0,
type_ >>> 0
);
if (ptr === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`readPixels failed: ${msg}`);
}
const dv = new DataView(ex.memory.buffer);
const len = dv.getUint32(ptr - 16, true);
if (!out || out.byteLength < len) {
throw new Error(`output buffer too small (need ${len} bytes, have ${out ? out.byteLength : 0})`);
}
const src = new Uint8Array(ex.memory.buffer, ptr, len);
const out_bytes = new Uint8Array(out.buffer, out.byteOffset, len);
out_bytes.set(src);
}
// --- Stubs for unimplemented WebGL2 methods (forwarding API surface) ---
// These are intentionally not implemented in the prototype. They allow
// callers to detect missing functionality early with a uniform error.
createShader(type) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_create_shader !== 'function') {
throw new Error('wasm_ctx_create_shader not found');
}
const handle = ex.wasm_ctx_create_shader(this._ctxHandle, type >>> 0);
if (handle === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`Failed to create shader: ${msg}`);
}
return new WasmWebGLShader(this, handle);
}
shaderSource(shader, source) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_shader_source !== 'function') {
throw new Error('wasm_ctx_shader_source not found');
}
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const sourceStr = String(source);
const bytes = new TextEncoder().encode(sourceStr);
const len = bytes.length;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for shaderSource');
try {
const mem = new Uint8Array(ex.memory.buffer);
mem.set(bytes, ptr);
const code = ex.wasm_ctx_shader_source(this._ctxHandle, shaderHandle, ptr, len);
_checkErr(code, this._instance);
} finally {
ex.wasm_free(ptr);
}
}
compileShader(shader) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_compile_shader !== 'function') {
throw new Error('wasm_ctx_compile_shader not found');
}
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const code = ex.wasm_ctx_compile_shader(this._ctxHandle, shaderHandle);
_checkErr(code, this._instance);
}
deleteShader(shader) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_delete_shader !== 'function') {
throw new Error('wasm_ctx_delete_shader not found');
}
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const code = ex.wasm_ctx_delete_shader(this._ctxHandle, shaderHandle);
_checkErr(code, this._instance);
if (shader && typeof shader === 'object') {
try { shader._handle = 0; shader._deleted = true; } catch (e) { /* ignore */ }
}
}
createProgram() {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_create_program !== 'function') {
throw new Error('wasm_ctx_create_program not found');
}
const handle = ex.wasm_ctx_create_program(this._ctxHandle);
if (handle === 0) {
const msg = readErrorMessage(this._instance);
throw new Error(`Failed to create program: ${msg}`);
}
return new WasmWebGLProgram(this, handle);
}
attachShader(program, shader) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_attach_shader !== 'function') {
throw new Error('wasm_ctx_attach_shader not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const code = ex.wasm_ctx_attach_shader(this._ctxHandle, programHandle, shaderHandle);
_checkErr(code, this._instance);
}
detachShader(program, shader) { this._assertNotDestroyed(); throw new Error('not implemented'); }
getActiveUniform(program, index) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_active_uniform !== 'function') {
throw new Error('wasm_ctx_get_active_uniform not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const ptr = ex.wasm_ctx_get_active_uniform(this._ctxHandle, programHandle, index >>> 0);
if (ptr === 0) return null;
const dv = new DataView(ex.memory.buffer);
const totalLen = dv.getUint32(ptr - 16, true);
const size = dv.getInt32(ptr, true);
const type_ = dv.getUint32(ptr + 4, true);
const nameLen = totalLen - 8;
const nameBytes = new Uint8Array(ex.memory.buffer, ptr + 8, nameLen);
const name = new TextDecoder('utf-8').decode(nameBytes);
return { name, size, type: type_ };
}
getActiveAttrib(program, index) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_active_attrib !== 'function') {
throw new Error('wasm_ctx_get_active_attrib not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const ptr = ex.wasm_ctx_get_active_attrib(this._ctxHandle, programHandle, index >>> 0);
if (ptr === 0) return null;
const dv = new DataView(ex.memory.buffer);
const totalLen = dv.getUint32(ptr - 16, true);
const size = dv.getInt32(ptr, true);
const type_ = dv.getUint32(ptr + 4, true);
const nameLen = totalLen - 8;
const nameBytes = new Uint8Array(ex.memory.buffer, ptr + 8, nameLen);
const name = new TextDecoder('utf-8').decode(nameBytes);
return { name, size, type: type_ };
}
linkProgram(program) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_link_program !== 'function') {
throw new Error('wasm_ctx_link_program not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const code = ex.wasm_ctx_link_program(this._ctxHandle, programHandle);
_checkErr(code, this._instance);
// After linking, we need to instantiate the WASM modules on the host.
if (program && typeof program === 'object') {
const linkStatus = this.getProgramParameter(program, this.LINK_STATUS);
if (linkStatus) {
this._instantiateProgramShaders(program);
}
}
}
_instantiateProgramShaders(program) {
const vsWasm = this.getProgramWasm(program, this.VERTEX_SHADER);
const fsWasm = this.getProgramWasm(program, this.FRAGMENT_SHADER);
if (!vsWasm || !fsWasm) {
return;
}
// Allocate table slots for both shaders
const vsIdx = this._tableAllocator ? this._tableAllocator.allocate() : null;
const fsIdx = this._tableAllocator ? this._tableAllocator.allocate() : null;
const createDebugEnv = (type, instanceRef) => {
if (!this._debugShaders) return {};
const stubCode = this.getProgramDebugStub(program, type);
if (!stubCode) return {};
// // Add sourceURL for debugging
// const debugName = `shader_stub_program_${program._handle}_${type === this.VERTEX_SHADER ? 'vs' : 'fs'}.js`;
// const codeWithUrl = stubCode + `\n//# sourceURL=${debugName}`;
let stubFuncs;
try {
// Eval the stub array
stubFuncs = (0, eval)(stubCode);
} catch (e) {
console.error("Failed to eval debug stub:", e);
return {};
}
return {
debug_step: (line, funcIdx, resultPtr) => {
if (line === 999999) {
return;
}
const func = stubFuncs[line - 1];
if (func) {
const ctx = {
go: () => {
// Trampoline logic would go here
// For now we rely on WASM calling the function after debug_step returns
}
};
try {
func.call(ctx);
} catch (e) {
console.error("Error in debug stub:", e);
}
}
}
};
};
let vsModule;
vsModule = new WebAssembly.Module(vsWasm);
const vsInstanceRef = { current: null };
const vsDebugEnv = createDebugEnv(this.VERTEX_SHADER, vsInstanceRef);
const env = {
memory: this._instance.exports.memory,
__indirect_function_table: this._sharedTable,
ACTIVE_ATTR_PTR: this._turboGlobals.ACTIVE_ATTR_PTR,
ACTIVE_UNIFORM_PTR: this._turboGlobals.ACTIVE_UNIFORM_PTR,
ACTIVE_VARYING_PTR: this._turboGlobals.ACTIVE_VARYING_PTR,
ACTIVE_PRIVATE_PTR: this._turboGlobals.ACTIVE_PRIVATE_PTR,
ACTIVE_TEXTURE_PTR: this._turboGlobals.ACTIVE_TEXTURE_PTR,
ACTIVE_FRAME_SP: this._turboGlobals.ACTIVE_FRAME_SP,
...vsDebugEnv
};
// Add math builtins from renderer (skipping host)
const mathFuncs = [
'gl_sin', 'gl_cos', 'gl_tan', 'gl_asin', 'gl_acos', 'gl_atan', 'gl_atan2',
'gl_exp', 'gl_exp2', 'gl_log', 'gl_log2', 'gl_pow',
'gl_sinh', 'gl_cosh', 'gl_tanh', 'gl_asinh', 'gl_acosh', 'gl_atanh',
'gl_inverse_mat2', 'gl_inverse_mat3',
'gl_debug4'
];
for (const name of mathFuncs) {
if (this._instance.exports[name]) {
env[name] = this._instance.exports[name];
}
}
program._vsInstance = new WebAssembly.Instance(vsModule, {
env
});
vsInstanceRef.current = program._vsInstance;
// Register in table
if (this._sharedTable && vsIdx !== null && program._vsInstance.exports.main) {
this._sharedTable.set(vsIdx, program._vsInstance.exports.main);
program._vsTableIndex = vsIdx;
}
let fsModule;
// Dump WASM to disk when debug_shaders is enabled to aid diagnostics
if (this._debugShaders) {
try {
require('fs').writeFileSync('test_debug/failing_fragment.wasm', Buffer.from(fsWasm));
} catch (e) {
// Best-effort; ignore write failures in constrained environments
}
}
if (this._debug) {
console.log("Compiling FS Module...");
}
fsModule = new WebAssembly.Module(fsWasm);
const fsInstanceRef = { current: null };
const fsDebugEnv = createDebugEnv(this.FRAGMENT_SHADER, fsInstanceRef);
const fsEnv = {
memory: this._instance.exports.memory,
__indirect_function_table: this._sharedTable,
ACTIVE_ATTR_PTR: this._turboGlobals.ACTIVE_ATTR_PTR,
ACTIVE_UNIFORM_PTR: this._turboGlobals.ACTIVE_UNIFORM_PTR,
ACTIVE_VARYING_PTR: this._turboGlobals.ACTIVE_VARYING_PTR,
ACTIVE_PRIVATE_PTR: this._turboGlobals.ACTIVE_PRIVATE_PTR,
ACTIVE_TEXTURE_PTR: this._turboGlobals.ACTIVE_TEXTURE_PTR,
ACTIVE_FRAME_SP: this._turboGlobals.ACTIVE_FRAME_SP,
...fsDebugEnv
};
for (const name of mathFuncs) {
if (this._instance.exports[name]) {
fsEnv[name] = this._instance.exports[name];
}
}
if (!fsEnv.gl_debug4) fsEnv.gl_debug4 = (a, b, c, d) => {};
if (!fsEnv.gl_inverse_mat2) fsEnv.gl_inverse_mat2 = (in_ptr, out_ptr) => {};
if (!fsEnv.gl_inverse_mat3) fsEnv.gl_inverse_mat3 = (in_ptr, out_ptr) => {};
program._fsInstance = new WebAssembly.Instance(fsModule, {
env: fsEnv
});
fsInstanceRef.current = program._fsInstance;
// Register in table
if (this._sharedTable && fsIdx !== null && program._fsInstance.exports.main) {
this._sharedTable.set(fsIdx, program._fsInstance.exports.main);
program._fsTableIndex = fsIdx;
}
// Notify Rust of table indices (requires Phase 4)
if (vsIdx !== null && fsIdx !== null) {
const ex = this._instance.exports;
if (ex.wasm_ctx_register_shader_indices) {
ex.wasm_ctx_register_shader_indices(
this._ctxHandle,
program._handle,
vsIdx,
fsIdx
);
}
}
}
getProgramDebugStub(program, shaderType) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_program_debug_stub !== 'function') {
return null;
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const len = ex.wasm_ctx_get_program_debug_stub(this._ctxHandle, programHandle, shaderType, 0, 0);
if (len === 0) return null;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) return null;
try {
const actualLen = ex.wasm_ctx_get_program_debug_stub(this._ctxHandle, programHandle, shaderType, ptr, len);
const mem = new Uint8Array(ex.memory.buffer);
const bytes = mem.subarray(ptr, ptr + actualLen);
return new TextDecoder().decode(bytes);
} finally {
ex.wasm_free(ptr);
}
}
deleteProgram(program) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_delete_program !== 'function') {
throw new Error('wasm_ctx_delete_program not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
// Table indices are now freed by Rust when the Program's refcount reaches zero.
// This allows bound programs to remain valid even if deleted by the user,
// as required by the WebGL specification.
const code = ex.wasm_ctx_delete_program(this._ctxHandle, programHandle);
_checkErr(code, this._instance);
if (program && typeof program === 'object') {
try { program._handle = 0; program._deleted = true; } catch (e) { /* ignore */ }
}
}
useProgram(program) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_use_program !== 'function') {
throw new Error('wasm_ctx_use_program not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const code = ex.wasm_ctx_use_program(this._ctxHandle, programHandle);
_checkErr(code, this._instance);
this._currentProgram = program;
}
getShaderParameter(shader, pname) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_shader_parameter !== 'function') {
throw new Error('wasm_ctx_get_shader_parameter not found');
}
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const val = ex.wasm_ctx_get_shader_parameter(this._ctxHandle, shaderHandle, pname >>> 0);
// WebGL returns boolean for status parameters
if (pname === 0x8B81 /* COMPILE_STATUS */ || pname === 0x8B80 /* DELETE_STATUS */) {
return !!val;
}
return val;
}
getProgramParameter(program, pname) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_program_parameter !== 'function') {
throw new Error('wasm_ctx_get_program_parameter not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const val = ex.wasm_ctx_get_program_parameter(this._ctxHandle, programHandle, pname >>> 0);
// WebGL returns boolean for status parameters
if (pname === 0x8B82 /* LINK_STATUS */ || pname === 0x8B80 /* DELETE_STATUS */ || pname === 0x8B83 /* VALIDATE_STATUS */) {
return !!val;
}
return val;
}
getShaderInfoLog(shader) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_shader_info_log !== 'function') {
throw new Error('wasm_ctx_get_shader_info_log not found');
}
const shaderHandle = shader && typeof shader === 'object' && typeof shader._handle === 'number' ? shader._handle : (shader >>> 0);
const ptr = ex.wasm_ctx_get_shader_info_log(this._ctxHandle, shaderHandle);
if (ptr === 0) return "";
const dv = new DataView(ex.memory.buffer);
const len = dv.getUint32(ptr - 16, true);
const mem = new Uint8Array(ex.memory.buffer);
const bytes = mem.slice(ptr, ptr + len);
return new TextDecoder().decode(bytes);
}
getProgramInfoLog(program) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_program_info_log !== 'function') {
throw new Error('wasm_ctx_get_program_info_log not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const ptr = ex.wasm_ctx_get_program_info_log(this._ctxHandle, programHandle);
if (ptr === 0) return "";
const dv = new DataView(ex.memory.buffer);
const len = dv.getUint32(ptr - 16, true);
const mem = new Uint8Array(ex.memory.buffer);
const bytes = mem.slice(ptr, ptr + len);
return new TextDecoder().decode(bytes);
}
getProgramWasm(program, shaderType) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_program_wasm_len !== 'function') {
throw new Error('wasm_ctx_get_program_wasm_len not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const len = ex.wasm_ctx_get_program_wasm_len(this._ctxHandle, programHandle, shaderType);
if (len === 0) return null;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for getProgramWasm');
try {
const actualLen = ex.wasm_ctx_get_program_wasm(this._ctxHandle, programHandle, shaderType, ptr, len);
const mem = new Uint8Array(ex.memory.buffer);
return new Uint8Array(mem.buffer, ptr, actualLen).slice();
} finally {
ex.wasm_free(ptr);
}
}
getAttribLocation(program, name) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_get_attrib_location !== 'function') {
throw new Error('wasm_ctx_get_attrib_location not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const nameStr = String(name);
const bytes = new TextEncoder().encode(nameStr);
const len = bytes.length;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for getAttribLocation');
try {
const mem = new Uint8Array(ex.memory.buffer);
mem.set(bytes, ptr);
return ex.wasm_ctx_get_attrib_location(this._ctxHandle, programHandle, ptr, len);
} finally {
ex.wasm_free(ptr);
}
}
bindAttribLocation(program, index, name) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_bind_attrib_location !== 'function') {
throw new Error('wasm_ctx_bind_attrib_location not found');
}
const programHandle = program && typeof program === 'object' && typeof program._handle === 'number' ? program._handle : (program >>> 0);
const nameStr = String(name);
const bytes = new TextEncoder().encode(nameStr);
const len = bytes.length;
const ptr = ex.wasm_alloc(len);
if (ptr === 0) throw new Error('Failed to allocate memory for bindAttribLocation');
try {
const mem = new Uint8Array(ex.memory.buffer);
mem.set(bytes, ptr);
const code = ex.wasm_ctx_bind_attrib_location(this._ctxHandle, programHandle, index >>> 0, ptr, len);
_checkErr(code, this._instance);
} finally {
ex.wasm_free(ptr);
}
}
enableVertexAttribArray(index) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_enable_vertex_attrib_array !== 'function') {
throw new Error('wasm_ctx_enable_vertex_attrib_array not found');
}
const code = ex.wasm_ctx_enable_vertex_attrib_array(this._ctxHandle, index >>> 0);
_checkErr(code, this._instance);
}
disableVertexAttribArray(index) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_disable_vertex_attrib_array !== 'function') {
throw new Error('wasm_ctx_disable_vertex_attrib_array not found');
}
const code = ex.wasm_ctx_disable_vertex_attrib_array(this._ctxHandle, index >>> 0);
_checkErr(code, this._instance);
}
vertexAttribPointer(index, size, type, normalized, stride, offset) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib_pointer !== 'function') {
throw new Error('wasm_ctx_vertex_attrib_pointer not found');
}
const code = ex.wasm_ctx_vertex_attrib_pointer(
this._ctxHandle,
index >>> 0,
size >>> 0,
type >>> 0,
normalized ? 1 : 0,
stride >>> 0,
offset >>> 0
);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttribIPointer(index, size, type, stride, offset) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib_ipointer !== 'function') {
throw new Error('wasm_ctx_vertex_attrib_ipointer not found');
}
const code = ex.wasm_ctx_vertex_attrib_ipointer(
this._ctxHandle,
index >>> 0,
size >>> 0,
type >>> 0,
stride >>> 0,
offset >>> 0
);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttrib1f(index, v0) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib1f !== 'function') {
throw new Error('wasm_ctx_vertex_attrib1f not found');
}
const code = ex.wasm_ctx_vertex_attrib1f(this._ctxHandle, index >>> 0, +v0);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttrib2f(index, v0, v1) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib2f !== 'function') {
throw new Error('wasm_ctx_vertex_attrib2f not found');
}
const code = ex.wasm_ctx_vertex_attrib2f(this._ctxHandle, index >>> 0, +v0, +v1);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttrib3f(index, v0, v1, v2) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib3f !== 'function') {
throw new Error('wasm_ctx_vertex_attrib3f not found');
}
const code = ex.wasm_ctx_vertex_attrib3f(this._ctxHandle, index >>> 0, +v0, +v1, +v2);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttrib4f(index, v0, v1, v2, v3) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib4f !== 'function') {
throw new Error('wasm_ctx_vertex_attrib4f not found');
}
const code = ex.wasm_ctx_vertex_attrib4f(this._ctxHandle, index >>> 0, +v0, +v1, +v2, +v3);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttrib1fv(index, v) {
if (v && v.length >= 1) {
this.vertexAttrib1f(index, v[0]);
} else {
this._setError(0x0501);
}
}
vertexAttrib2fv(index, v) {
if (v && v.length >= 2) {
this.vertexAttrib2f(index, v[0], v[1]);
} else {
this._setError(0x0501);
}
}
vertexAttrib3fv(index, v) {
if (v && v.length >= 3) {
this.vertexAttrib3f(index, v[0], v[1], v[2]);
} else {
this._setError(0x0501);
}
}
vertexAttrib4fv(index, v) {
if (v && v.length >= 4) {
this.vertexAttrib4f(index, v[0], v[1], v[2], v[3]);
} else {
this._setError(0x0501);
}
}
vertexAttribI4i(index, v0, v1, v2, v3) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib_i4i !== 'function') {
throw new Error('wasm_ctx_vertex_attrib_i4i not found');
}
const code = ex.wasm_ctx_vertex_attrib_i4i(this._ctxHandle, index >>> 0, v0 | 0, v1 | 0, v2 | 0, v3 | 0);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttribI4ui(index, v0, v1, v2, v3) {
this._assertNotDestroyed();
const ex = this._instance.exports;
if (!ex || typeof ex.wasm_ctx_vertex_attrib_i4ui !== 'function') {
throw new Error('wasm_ctx_vertex_attrib_i4ui not found');
}
const code = ex.wasm_ctx_vertex_attrib_i4ui(this._ctxHandle, index >>> 0, v0 >>> 0, v1 >>> 0, v2 >>> 0, v3 >>> 0);
if (code === 5) return; // ERR_GL
_checkErr(code, this._instance);
}
vertexAttribI4iv(index, v) {
if (v && v.length >= 4) {
this.vertexAttribI4i(index, v[0], v[1], v[2], v[3]);
} else {
this._setError(0x0501);
}
}
vertexAttribI4uiv(index, v) {
if (v && v.length >= 4) {
this.vertexAttribI4ui(index, v[0], v[1], v[2], v[3]);
} else {
this._setError(0x0501);