const TYPES = { U8: 1, I8: 2, U16: 3, I16: 4, U32: 5, I32: 6, U64: 7, I64: 8, SizeT: 9, F32: 10, F64: 11, Bool: 12, String: 13, U8Array: 14, I8Array: 15, U16Array: 16, I16Array: 17, U32Array: 18, I32Array: 19, U64Array: 20, I64Array: 21, F32Array: 22, F64Array: 23, Char: 24, CharArray: 25, }; let LoadSize = null; let StoreSize = null; function assert(condition, message) { if(!condition) { console.assert(condition, message); throw "Assert failure"; } } let MEMORY = null; let DATA_VIEW = null; let GL = null; let STDOUT_STRING = ""; let SPRINF_VALUES = []; let PTR_SIZE = 4; let GL_ELEMENT = null; let GL_VERSION = 1.0; let GL_LAST_ERROR = 0; let GL_CURRENT_ID = 1; let BUFFERS = new Map([0, null]); let PROGRAMS = new Map([0, null]); let FRAMEBUFFERS = new Map([0, null]); let RENDERBUFFERS = new Map([0, null]); let TEXTURES = new Map([0, null]); let UNIFORMS = new Map([0, null]); let SHADERS = new Map([0, null]); let VAOS = new Map([0, null]); let QUERIES = new Map([0, null]); let SAMPLERS = new Map([0, null]); let TRANSFORM_FEEDBACK = new Map([0, null]); let SYNCS = new Map([0, null]); let PROGRAM_INFO = new Map([0, null]); function InitWasmManager(memory, ptr_size = 4) { PTR_SIZE = ptr_size; SPRINTF_VALUES = []; STDOUT_STRING = ""; switch(PTR_SIZE) { case 8: LoadSize = LoadU64; StoreSize = StoreU64; break; case 4: default: LoadSize = LoadU32; StoreSize = StoreU32; break; } } function SetMemory(memory) { MEMORY = memory; DATA_VIEW = new DataView(MEMORY.buffer); } function Memory() { return new DataView(MEMORY.buffer); } function GLRemoveItem(map, id) { map.delete(id); } function GLGetItem(map, id) { return map.has(id) ? map.get(id) : null; } function GLAddItem(map, item) { map.set(GL_CURRENT_ID, item); return GL_CURRENT_ID++; } function SetGLError(error) { if(!GL_LAST_ERROR) GL_LAST_ERROR = error; } function SetGLContext(element, settings) { if(!element) return false; if(GL_ELEMENT == element) return true; settings ??= {}; GL = element.getContext("webgl2", settings) || element.getContext("webgl", settings); if(GL) { GL_ELEMENT = element; GL_VERSION = GL.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1 ? 2.0 : 1.0; } return !!GL; } function AssertWebGL2() { if(GL_VERSION < 2.0) { throw new Error("WebGL2 function called without a WebGL2 enabled context"); } } function PopulateUniformTable(program) { let p = GLGetItem(PROGRAMS, program); PROGRAM_INFO[program] = { uniforms: {}, maxUniformLength: 0, maxAttributeLength: -1, maxUniformBlockNameLength: -1, }; let ptable = PROGRAM_INFO[program]; let utable = ptable.uniforms; let numUniforms = GL.getProgramParameter(p, GL.ACTIVE_UNIFORMS); for(i = 0; i < numUniforms; ++i) { let u = GL.getActiveUniform(p, i); let name = u.name; if(ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) { name = name.slice(0, name.lastIndexOf("[")); } let loc = GL.getUniformLocation(p, name); if(loc !== null) { let id = GLAddItem(UNIFORMS, loc); utable[name] = [u.size, id]; for(let j = 1; j < u.size; ++j) { let n = name + "[" + j + "]"; let loc = GL.getUniformLocation(p, n); GLAddItem(UNIFORMS, loc); } } } } function LoadBool(addr) { return Boolean(Memory().getUint8(addr, true)); } function LoadF32(addr) { return Memory().getFloat32(addr, true); } function LoadF64(addr) { return Memory().getFloat64(addr, true); } function LoadU8 (addr) { return Memory().getUint8 (addr, true); } function LoadI8 (addr) { return Memory().getInt8 (addr, true); } function LoadU16(addr) { return Memory().getUint16 (addr, true); } function LoadI16(addr) { return Memory().getInt16 (addr, true); } function LoadU32(addr) { return Memory().getUint32 (addr, true); } function LoadI32(addr) { return Memory().getInt32 (addr, true); } function LoadU64(addr) { const lo = Memory().getUint32(addr+0, true); const hi = Memory().getUint32(addr+4, true); return lo + hi*4294967296; } function LoadI64(addr) { const lo = Memory().getUint32(addr+0, true); const hi = Memory().getInt32 (addr+4, true); return lo + hi*4294967296; } function LoadArray(array_class, length, addr) { return new array_class(MEMORY.buffer, addr, length); } function LoadString(length, addr) { return new TextDecoder().decode(LoadArray(Uint8Array, length, addr)); } function LoadCString(ptr) { if(!ptr) return null; let length = 0; for(; LoadU8(ptr+length); length += 1) {} return LoadString(ptr, length); } function StoreString(ptr, value) { const src = new TextEncoder().encode(value); const dst = new Uint8Array(MEMORY.buffer, ptr, src.length); dst.set(src); return src.length; } function StoreU8(addr, value) { Memory().setUint8(addr, value, true); } function StoreI8(addr, value) { Memory().setInt8 (addr, value, true); } function StoreU16(addr, value) { Memory().setUint16(addr, value, true); } function StoreI16(addr, value) { Memory().setInt16(addr, value, true); } function StoreU32(addr, value) { Memory().setUint32(addr, value, true); } function StoreI32(addr, value) { Memory().setInt32(addr, value, true); } function StoreU64(addr, value) { let div = 4294967296; if(typeof value === 'bigint') { div = BigInt(div); } StoreU32(addr+0, Number(value), true); StoreU32(addr+4, Math.floor(Number(value/div)), true); } function StoreI64(addr, value) { let div = 4294967296; if(typeof value === 'bigint') { div = BigInt(div); } StoreI32(addr+0, Number(value), true); StoreI32(addr+4, Math.floor(Number(value/div)), true); } function StoreF32(addr, value) { Memory().setFloat32(addr, value, true); } function StoreF64(addr, value) { Memory().setFloat64(addr, value, true); } const imports = { env: { Abort(string_length, string_ptr) { console.error(LoadString(string_length, string_ptr)); }, SprintfLoadValue(ptr, type) { let value = null; switch(type) { case TYPES.U8: value = LoadU8(ptr); break; case TYPES.I8: value = LoadI8(ptr); break; case TYPES.U16: value = LoadU16(ptr); break; case TYPES.I16: value = LoadI16(ptr); break; case TYPES.U32: value = LoadU32(ptr); break; case TYPES.I32: value = LoadI32(ptr); break; case TYPES.U64: value = LoadU64(ptr); break; case TYPES.I64: value = LoadI64(ptr); break; case TYPES.SizeT: value = LoadSize(ptr); break; case TYPES.F32: value = LoadF32(ptr); break; case TYPES.F64: value = LoadF64(ptr); break; case TYPES.Bool: value = LoadBool(ptr); break; case TYPES.Char: value = LoadString(1, ptr); break; default: break; } if(value != null) { if(type === TYPES.Bool) { SPRINTF_VALUES.push(value ? 'true' : 'false'); } else { SPRINTF_VALUES.push(''+value); } } }, SprintfLoadArray(length, ptr, type) { let value = null; switch(type) { case TYPES.CharArray: case TYPES.String: value = LoadString(length, ptr); break; case TYPES.U8Array: value = LoadArray(Uint8Array, length, ptr); break; case TYPES.I8Array: value = LoadArray(Int8Array, length, ptr); break; case TYPES.U16Array: value = LoadArray(Uint16Array, length, ptr); break; case TYPES.I16Array: value = LoadArray(Int16Array, length, ptr); break; case TYPES.U32Array: value = LoadArray(Uint32Array, length, ptr); break; case TYPES.I32Array: value = LoadArray(Int32Array, length, ptr); break; case TYPES.U64Array: value = LoadArray(BigUint64Array, length, ptr); break; case TYPES.I64Array: value = LoadArray(BigInt64Array, length, ptr); break; case TYPES.F32Array: value = LoadArray(Float32Array, length, ptr); break; case TYPES.F64Array: value = LoadArray(Float64Array, length, ptr); break; } if(value) { if(type !== TYPES.String && type !== TYPES.CharArray) { value = `[${value.join(", ")}]`; } SPRINTF_VALUES.push(value); } }, SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr) { let format_string = LoadString(string_length, string_ptr); let index = format_string.indexOf("%"); let result = format_string.substring(0, index); let format = format_string.substring(index); function Next() { index = format.indexOf("%"); result += format.substring(0, index); format = format.substring(index); } let value_index = 0; for(; value_index < SPRINTF_VALUES.length;) { assert(format.length); const char = format.charAt(1); assert(char !== " " && char.length, "Invalid format string " + format_string); if(char === "%") { result += "%"; format = format.substring(2); Next(); continue; } else if(char >= '0' && char <= '9') { let digit_index = 2; for(;;) { let next_char = format.charAt(digit_index++); assert(next_char !== " " && next_char.length, "Invalid format string " + format_string); if(next_char < '0' || next_char > '9') { continue; } result += SPRINTF_VALUES[value_index++]; format = format.substring(digit_index); Next(); break; } } else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) { result += SPRINTF_VALUES[value_index++]; format = format.substring(2); Next(); } else assert(false, "Invalid format string " + format_string); } result += format; const bytes_written = StoreString(buffer_ptr, result); assert(bytes_written <= buffer_length, "Format string is longer than buffer length"); SPRINTF_VALUES = []; return bytes_written; }, Console(string_length, string_ptr, write_line) { STDOUT_STRING += LoadString(string_length, string_ptr); const arr = LoadArray(Uint8Array, string_length, string_ptr); if(write_line || STDOUT_STRING.includes("\n")) { console.log(STDOUT_STRING); STDOUT_STRING = ""; } }, Console2(string_length, string_ptr, write_line) { STDOUT_STRING += LoadString(string_length, string_ptr); const arr = LoadArray(Uint8Array, string_length, string_ptr); if(write_line || STDOUT_STRING.includes("\n")) { console.log(STDOUT_STRING); STDOUT_STRING = ""; } }, pow: Math.pow, cos: Math.cos, acos: Math.acos, }, gl: { SetCurrentContextById: (name_length, name_ptr) => { let name = LoadString(name_length, name_ptr); let element = getElement(name); return SetGLContext(element, {alpha: true, antialias: true, depth: true, premultipliedAlpha: true}); }, CreateCurrentContextById: (name_length, name_ptr, attributes) => { let name = LoadString(name_length, name_ptr); let element = getElement(name); let contextSettings = { alpha: ! (attributes & (1<<0)), antialias: ! (attributes & (1<<1)), depth: ! (attributes & (1<<2)), failIfMajorPerformanceCaveat: !!(attributes & (1<<3)), premultipliedAlpha: ! (attributes & (1<<4)), preserveDrawingBuffer: !!(attributes & (1<<5)), stencil: !!(attributes & (1<<6)), desynchronized: !!(attributes & (1<<7)), }; return SetGLContext(element, contextSettings); }, Clear: (x) => { GL.clear(x); }, ClearColor: (r, g, b, a) => { GL.clearColor(r, g, b, a); }, ClearDepth: (x) => { GL.clearDepth(x); }, ClearStencil: (x) => { GL.clearStencil(x); }, ColorMask: (r, g, b, a) => { GL.colorMask(!!r, !!g, !!b, !!a); }, CompileShader: (shader) => { if(shader) { GL.compileShader(GLGetItem(SHADERS, shader)); } }, UseProgram: (program) => { if(program) { GL.useProgram(GLGetItem(PROGRAMS, program)); } }, ValidateProgram: (program) => { if(program) { GL.validateProgram(GLGetItem(PROGRAMS, program)); } }, BindBufferBase: (target, index, buffer) => { AssertWebGL2(); GL.bindBufferBase(target, index, GLGetItem(BUFFERS, buffer)); }, GetUniformBlockIndex: (program, uniformBlockName_ptr) => { AssertWebGL2(); return GL.getUniformBlockIndex(GLGetItem(PROGRAMS, program), LoadCString(uniformBlockName_ptr)); }, GenTextures: (count, texture_ptr) => { for(let i = 0; i < count; i += 1) { const id = GLAddItem(TEXTURES, GL.createTexture()); StoreU32(texture_ptr+4*i, id); } }, CullFace: (mode) => { GL.cullFace(mode); }, GenBuffers: (count, buffer_ptr) => { for(let i = 0; i < count; i += 1) { const id = GLAddItem(BUFFERS, GL.createBuffer()); StoreU32(buffer_ptr+4*i, id); } }, Enable: (cap) => { GL.enable(cap); }, EnableVertexAttribArray: (index) => { GL.enableVertexAttribArray(index); }, Disable: (cap) => { GL.disable(cap); }, DisableVertexAttribArray: (index) => { GL.disableVertexAttribArray(index); }, FrontFace: (mode) => { GL.frontFace(mode); }, BlendFuncSeparate: (srcRGB, dstRGB, srcAlpha, dstAlpha) => { GL.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); }, BindTexture: (target, texture) => { GL.bindTexture(target, GLGetItem(TEXTURES, texture)); }, TexParameterf: (target, pname, param) => { GL.texParameterf(target, pname, param); }, TexParameteri: (target, pname, param) => { GL.texParameteri(target, pname, param); }, TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { let buffer = null; if(data) { switch(internalformat) { case GL.RGBA32F: case GL.RGB32F: case GL.RG32F: case GL.R32F: buffer = LoadArray(Float32Array, size, data); break; case GL.RGBA32UI: case GL.RGB32UI: case GL.RG32UI: case GL.R32UI: buffer = LoadArray(Uint32Array, size, data); break; case GL.RGBA32I: case GL.RGB32I: case GL.RG32I: case GL.R32I: buffer = LoadArray(Int32Array, size, data); break; default: buffer = LoadArray(Uint8Array, size, data); break; } } GL.texImage2D(target, level, internalformat, width, height, border, format, type, buffer); }, GenVertexArrays: (count, vao_ptr) => { AssertWebGL2(); for(let i = 0; i < count; i += 1) { const id = GLAddItem(VAOS, GL.createVertexArray()); StoreU32(vao_ptr+4*i, id); } }, DeleteVertexArrays: (count, vao_ptr) => { AssertWebGL2(); const vaos = LoadArray(Uint32Array, count, vao_ptr); for(let i = 0; i < vaos.length; i += 1) { const id = vaos[i]; let obj = GLGetItem(VAOS, id); if(obj) { GL.deleteVertexArray(obj); GLRemoveItem(VAOS, id); } } }, BindVertexArray: (vertexArray) => { AssertWebGL2(); GL.bindVertexArray(GLGetItem(VAOS, vertexArray)); }, BlendFunc: (sfactor, dfactor) => { GL.blendFunc(sfactor, dfactor); }, BindBuffer: (target, buffer) => { let bufferObj = GLGetItem(BUFFERS, buffer); if(target == 35051) { GL.currentPixelPackBufferBinding = buffer; } else { if(target == 35052) { GL.currentPixelUnpackBufferBinding = buffer; } GL.bindBuffer(target, bufferObj) } }, BufferData: (target, size, data, usage) => { if(data) { GL.bufferData(target, LoadArray(Uint8Array, size, data), usage); } else { GL.bufferData(target, size, usage); } }, BufferSubData: (target, offset, size, data) => { if(data) { GL.bufferSubData(target, offset, LoadArray(Uint8Array, size, data)); } else { GL.bufferSubData(target, offset, null); } }, VertexAttribDivisor: (index, divisor) => { AssertWebGL2(); GL.vertexAttribDivisor(index, divisor); }, VertexAttribPointer: (index, size, type, normalized, stride, ptr) => { GL.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); }, Viewport: (x, y, w, h) => { if(GLELEMENT) { GLELEMENT.width = w; GLELEMENT.height = h; } GL.viewport(x, y, w, h); }, DrawArraysInstanced: (mode, first, count, instanceCount) => { AssertWebGL2(); GL.drawArraysInstanced(mode, first, count, instanceCount); }, DrawElementsInstanced: (mode, count, type, offset, instanceCount) => { AssertWebGL2(); GL.drawElementsInstanced(mode, count, type, offset, instanceCount); }, ActiveTexture: (x) => { GL.activeTexture(x); }, GetUniformLocation: (program, name_ptr) => { let name = LoadCString(name_ptr); let arrayOffset = 0; if(name.indexOf("]", name.length - 1) !== -1) { let ls = name.lastIndexOf("["); let arrayIndex = name.slice(ls + 1, -1); if(arrayIndex.length > 0 && (arrayOffset = parseInt(arrayIndex)) < 0) { return -1; } name = name.slice(0, ls) } let ptable = PROGRAM_INFO[program]; if(!ptable) { return -1; } var uniformInfo = ptable.uniforms[name]; return (uniformInfo && arrayOffset < uniformInfo[0]) ? uniformInfo[1] + arrayOffset : -1 }, Uniform1f: (location, v0) => { GL.uniform1f(GLGetItem(UNIFORMS, location), v0); }, Uniform2f: (location, v0, v1) => { GL.uniform2f(GLGetItem(UNIFORMS, location), v0, v1); }, Uniform3f: (location, v0, v1, v2) => { GL.uniform3f(GLGetItem(UNIFORMS, location), v0, v1, v2); }, Uniform4f: (location, v0, v1, v2, v3) => { GL.uniform4f(GLGetItem(UNIFORMS, location), v0, v1, v2, v3); }, Uniform1i: (location, v0) => { GL.uniform1i(GLGetItem(UNIFORMS, location), v0); }, Uniform2i: (location, v0, v1) => { GL.uniform2i(GLGetItem(UNIFORMS, location), v0, v1); }, Uniform3i: (location, v0, v1, v2) => { GL.uniform3i(GLGetItem(UNIFORMS, location), v0, v1, v2); }, Uniform4i: (location, v0, v1, v2, v3) => { GL.uniform4i(GLGetItem(UNIFORMS, location), v0, v1, v2, v3); }, UniformMatrix2fv: (location, addr) => { let array = LoadArray(Float32Array, 2*2, addr); GL.uniformMatrix2fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix3fv: (location, addr) => { let array = LoadArray(Float32Array, 3*3, addr); GL.uniformMatrix3fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix4fv: (location, addr) => { let array = LoadArray(Float32Array, 4*4, addr); GL.uniformMatrix4fv(GLGetItem(UNIFORMS, location), false, array); }, CreateProgram: () => { return GLAddItem(PROGRAMS, GL.createProgram()); }, GenRenderbuffer: (count, renderbuffer_ptr) => { for(let i = 0; i < count; i += 1) { const id = GLAddItem(RENDERBUFFERS, GL.createRenderbuffer()); StoreU32(renderbuffer_ptr+4*i, id); } }, CreateShader: (shaderType) => { return GLAddItem(SHADERS, GL.createShader(shaderType)); }, DeleteBuffers: (count, buffer_ptr) => { const buffers = LoadArray(Uint32Array, count, buffer_ptr); for(let i = 0; i < buffers.length; i += 1) { const id = buffers[i]; let obj = GLGetItem(BUFFERS, id); if(obj && id != 0) { GL.deleteBuffer(obj); GLRemoveItem(BUFFERS, id); } } }, DeleteFramebuffers: (count, framebuffer_ptr) => { const framebuffers = LoadArray(Uint32Array, count, framebuffer_ptr); for(let i = 0; i < framebuffers.length; i += 1) { const id = framebuffers[i]; let obj = GLGetItem(FRAMEBUFFERS, id); if(obj && id != 0) { GL.deleteFramebuffer(obj); GLRemoveItem(FRAMEBUFFERS, id); } } }, DeleteProgram: (id) => { let obj = GLGetItem(PROGRAMS, id); if(obj && id != 0) { GL.deleteProgram(obj); GLRemoveItem(PROGRAMS, id); } }, DeleteRenderbuffers: (count, renderbuffer_ptr) => { const renderbuffers = LoadArray(Uint32Array, count, renderbuffer_ptr); for(let i = 0; i < renderbuffers.length; i += 1) { const id = renderbuffers[i]; let obj = GLGetItem(RENDERBUFFERS, id); if(obj && id != 0) { GL.deleteRenderbuffer(obj); GLRemoveItem(RENDERBUFFERS, id); } } }, DeleteShader: (id) => { let obj = GLGetItem(SHADERS, id); if(obj && id != 0) { GL.deleteShader(obj); GLRemoveItem(SHADERS, id); } }, DeleteTextures: (count, texture_ptr) => { const textures = LoadArray(Uint32Array, count, texture_ptr); for(let i = 0; i < textures.length; i += 1) { const id = textures[i]; let obj = GLGetItem(TEXTURES, id); if(obj && id != 0) { GL.deleteTexture(obj); GLRemoveItem(TEXTURES, id); } } }, Scissor: (x, y, width, height) => { GL.scissor(x, y, width, height); }, ShaderSource: (shader, count, strings_ptr, lengths_ptr) => { let lengths = null; if(lengths_ptr) { lengths = LoadArray(Uint32Array, count, lengths_ptr); } let source = ""; for(let i = 0; i < count; i += 1) { const addr = LoadSize(strings_ptr + i*PTR_SIZE); let source_part = lengths && (lengths?.[i] ?? 0) > 0 ? LoadString(lengths[i], addr) : LoadCString(addr); source += source_part; } GL.shaderSource(GLGetItem(SHADERS, shader), source); }, GetError: () => { let err = GL_LAST_ERROR; SetGLError(0); if(err) { return err; } return GL.getError(); }, GetProgramiv: (program, pname, ptr) => { const param = GL.getProgramParameter(GLGetItem(PROGRAMS, program), pname); StoreI32(ptr, param); }, GetProgramInfoLog: (program, buf_length, buf_ptr, length_ptr) => { let log = GL.getProgramInfoLog(GLGetItem(PROGRAMS, program)); if(log === null) { log = "(unknown error)"; } if(buf_len > 0 && buf_ptr) { let n = Math.min(buf_len, log.length); log = log.substring(0, n); LoadArray(Uint8Array, buf_length, buf_ptr).set(new TextEncoder().encode(log)) StoreSize(length_ptr, n); } }, GetShaderInfoLog: (shader, buf_ptr, buf_len, length_ptr) => { let log = GL.getShaderInfoLog(GLGetItem(SHADERS, shader)); if(log === null) { log = "(unknown error)"; } if(buf_len > 0 && buf_ptr) { let n = Math.min(buf_len, log.length); log = log.substring(0, n); LoadArray(Uint8Array, buf_len, buf_ptr).set(new TextEncoder().encode(log)) StoreSize(length_ptr, n); } }, GetShaderiv: (shader_id, pname, p) => { const shader = GLGetItem(SHADERS, shader); if(p && shader) { if(pname == 35716) { let log = GL.getShaderInfoLog(shader); if(log === null) { log = "(unknown error)"; } StoreSize(p, log.length+1); } else if(pname == 35720) { let source = GL.getShaderSource(shader); let sourceLength = (source === null || source.length == 0) ? 0 : source.length+1; this.mem.storeInt(p, sourceLength); } else { let param = GL.getShaderParameter(shader, pname); StoreI32(p, param); } } else { SetGLError(1281); } }, AttachShader: (program, shader) => { GL.attachShader(GLGetItem(PROGRAMS, program), GLGetItem(SHADERS, shader)); }, LinkProgram: (program) => { GL.linkProgram(GLGetItem(PROGRAMS, program)); PROGRAM_INFO[program] = null; PopulateUniformTable(program); }, }, }; const other_gl_funcs = { webgl: { GetCurrentContextAttributes: () => { if(!GL) { return 0; } let attrs = GL.getContextAttributes(); let res = 0; if(!attrs.alpha) res |= 1<<0; if(!attrs.antialias) res |= 1<<1; if(!attrs.depth) res |= 1<<2; if(attrs.failIfMajorPerformanceCaveat) res |= 1<<3; if(!attrs.premultipliedAlpha) res |= 1<<4; if(attrs.preserveDrawingBuffer) res |= 1<<5; if(attrs.stencil) res |= 1<<6; if(attrs.desynchronized) res |= 1<<7; return res; }, DrawingBufferWidth: () => GL.drawingBufferWidth, DrawingBufferHeight: () => GL.drawingBufferHeight, IsExtensionSupported: (name_length, name_ptr) => { let name = LoadString(name_length, name_ptr); let extensions = GL.getSupportedExtensions(); return extensions.indexOf(name) !== -1 }, GetWebGLVersion: (major_ptr, minor_ptr) => { let version = GL.getParameter(0x1F02); if(version.indexOf("WebGL 2.0") !== -1) { this.mem.storeI32(major_ptr, 2); this.mem.storeI32(minor_ptr, 0); return; } this.mem.storeI32(major_ptr, 1); this.mem.storeI32(minor_ptr, 0); }, GetESVersion: (major_ptr, minor_ptr) => { let version = GL.getParameter(0x1F02); if(version.indexOf("OpenGL ES 3.0") !== -1) { this.mem.storeI32(major_ptr, 3); this.mem.storeI32(minor_ptr, 0); return; } this.mem.storeI32(major_ptr, 2); this.mem.storeI32(minor_ptr, 0); }, BindAttribLocation: (program, index, name_length, name_ptr) => { let name = LoadString(name_length, name_ptr); GL.bindAttribLocation(PROGRAMS[program], index, name) }, BindFramebuffer: (target, framebuffer) => { GL.bindFramebuffer(target, framebuffer ? FRAMEBUFFERS[framebuffer] : null) }, BlendColor: (red, green, blue, alpha) => { GL.blendColor(red, green, blue, alpha); }, BlendEquation: (mode) => { GL.blendEquation(mode); }, BlendEquationSeparate: (modeRGB, modeAlpha) => { GL.blendEquationSeparate(modeRGB, modeAlpha); }, CompressedTexImage2D: (target, level, internalformat, width, height, border, imageSize, data) => { if(data) { GL.compressedTexImage2D(target, level, internalformat, width, height, border, this.mem.loadBytes(data, imageSize)); } else { GL.compressedTexImage2D(target, level, internalformat, width, height, border, null); } }, CompressedTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, imageSize, data) => { if(data) { GL.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, this.mem.loadBytes(data, imageSize)); } else { GL.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, null); } }, CopyTexImage2D: (target, level, internalformat, x, y, width, height, border) => { GL.copyTexImage2D(target, level, internalformat, x, y, width, height, border); }, CopyTexSubImage2D: (target, level, xoffset, yoffset, x, y, width, height) => { GL.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); }, CreateFramebuffer: () => { let buffer = GL.createFramebuffer(); let id = this.getNewId(FRAMEBUFFERS); buffer.name = id FRAMEBUFFERS[id] = buffer; return id; }, Clear: (x) => { GL.clear(x); }, ClearColor: (r, g, b, a) => { GL.clearColor(r, g, b, a); }, ClearDepth: (x) => { GL.clearDepth(x); }, ClearStencil: (x) => { GL.clearStencil(x); }, ColorMask: (r, g, b, a) => { GL.colorMask(!!r, !!g, !!b, !!a); }, CompileShader: (shader) => { GL.compileShader(SHADERS[shader]); }, UseProgram: (program) => { if(program) GL.useProgram(PROGRAMS[program]); }, ValidateProgram: (program) => { if(program) GL.validateProgram(PROGRAMS[program]); }, BindBufferBase: (target, index, buffer) => { AssertWebGL2(); GL.bindBufferBase(target, index, BUFFERS[buffer]); }, GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => { AssertWebGL2(); return GL.getUniformBlockIndex(PROGRAMS[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); }, CullFace: (mode) => { GL.cullFace(mode); }, CreateBuffer: () => { let buffer = GL.createBuffer(); if(!buffer) { SetGLError(1282); return 0; } let id = this.getNewId(BUFFERS); buffer.name = id BUFFERS[id] = buffer; return id; }, Enable: (cap) => { GL.enable(cap); }, EnableVertexAttribArray: (index) => { GL.enableVertexAttribArray(index); }, Disable: (cap) => { GL.disable(cap); }, DisableVertexAttribArray: (index) => { GL.disableVertexAttribArray(index); }, FrontFace: (mode) => { GL.frontFace(mode); }, BlendFuncSeparate: (srcRGB, dstRGB, srcAlpha, dstAlpha) => { GL.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); }, BindTexture: (target, texture) => { GL.bindTexture(target, texture ? TEXTURES[texture] : null) }, TexParameterf: (target, pname, param) => { GL.texParameterf(target, pname, param); }, TexParameteri: (target, pname, param) => { GL.texParameteri(target, pname, param); }, TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { let buffer = null; if(data) { switch(internalformat) { case GL.RGBA32F: case GL.RGB32F: case GL.RG32F: case GL.R32F: buffer = this.mem.loadF32Array(data, size); break; case GL.RGBA32UI: case GL.RGB32UI: case GL.RG32UI: case GL.R32UI: buffer = this.mem.loadU32Array(data, size); break; case GL.RGBA32I: case GL.RGB32I: case GL.RG32I: case GL.R32I: buffer = this.mem.loadI32Array(data, size); break; default: buffer = this.mem.loadBytes(data, size); break; } } GL.texImage2D(target, level, internalformat, width, height, border, format, type, buffer); }, DeleteVertexArray: (id) => { AssertWebGL2(); let obj = VAOS[id]; if(obj && id != 0) { GL.deleteVertexArray(obj); VAOS[id] = null; } }, BindVertexArray: (vertexArray) => { AssertWebGL2(); GL.bindVertexArray(VAOS[vertexArray]); }, BlendFunc: (sfactor, dfactor) => { GL.blendFunc(sfactor, dfactor); }, BindBuffer: (target, buffer) => { let bufferObj = buffer ? BUFFERS[buffer] : null; if(target == 35051) { GL.currentPixelPackBufferBinding = buffer; } else { if(target == 35052) { GL.currentPixelUnpackBufferBinding = buffer; } GL.bindBuffer(target, bufferObj) } }, BufferData: (target, size, data, usage) => { if(data) { GL.bufferData(target, this.mem.loadBytes(data, size), usage); } else { GL.bufferData(target, size, usage); } }, BufferSubData: (target, offset, size, data) => { if(data) { GL.bufferSubData(target, offset, this.mem.loadBytes(data, size)); } else { GL.bufferSubData(target, offset, null); } }, VertexAttribDivisor: (index, divisor) => { AssertWebGL2(); GL.vertexAttribDivisor(index, divisor); }, VertexAttribPointer: (index, size, type, normalized, stride, ptr) => { GL.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); }, Viewport: (x, y, w, h) => { if(GLELEMENT) { GLELEMENT.width = w; GLELEMENT.height = h; } GL.viewport(x, y, w, h); }, DrawArraysInstanced: (mode, first, count, instanceCount) => { AssertWebGL2(); GL.drawArraysInstanced(mode, first, count, instanceCount); }, DrawElementsInstanced: (mode, count, type, offset, instanceCount) => { AssertWebGL2(); GL.drawElementsInstanced(mode, count, type, offset, instanceCount); }, ActiveTexture: (x) => { GL.activeTexture(x); }, GetUniformLocation: (program, name_ptr, name_len) => { let name = this.mem.loadString(name_ptr, name_len); let arrayOffset = 0; if(name.indexOf("]", name.length - 1) !== -1) { let ls = name.lastIndexOf("["), arrayIndex = name.slice(ls + 1, -1); if(arrayIndex.length > 0 && (arrayOffset = parseInt(arrayIndex)) < 0) { return -1; } name = name.slice(0, ls) } var ptable = PROGRAM_INFO[program]; if(!ptable) { return -1; } var uniformInfo = ptable.uniforms[name]; return (uniformInfo && arrayOffset < uniformInfo[0]) ? uniformInfo[1] + arrayOffset : -1 }, Uniform1f: (location, v0) => { GL.uniform1f(GLGetItem(UNIFORMS, location), v0); }, Uniform2f: (location, v0, v1) => { GL.uniform2f(GLGetItem(UNIFORMS, location), v0, v1); }, Uniform3f: (location, v0, v1, v2) => { GL.uniform3f(GLGetItem(UNIFORMS, location), v0, v1, v2); }, Uniform4f: (location, v0, v1, v2, v3) => { GL.uniform4f(GLGetItem(UNIFORMS, location), v0, v1, v2, v3); }, Uniform1i: (location, v0) => { GL.uniform1i(GLGetItem(UNIFORMS, location), v0); }, Uniform2i: (location, v0, v1) => { GL.uniform2i(GLGetItem(UNIFORMS, location), v0, v1); }, Uniform3i: (location, v0, v1, v2) => { GL.uniform3i(GLGetItem(UNIFORMS, location), v0, v1, v2); }, Uniform4i: (location, v0, v1, v2, v3) => { GL.uniform4i(GLGetItem(UNIFORMS, location), v0, v1, v2, v3); }, UniformMatrix2fv: (location, addr) => { let array = this.mem.loadF32Array(addr, 2*2); GL.uniformMatrix2fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix3fv: (location, addr) => { let array = this.mem.loadF32Array(addr, 3*3); GL.uniformMatrix3fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix4fv: (location, addr) => { let array = this.mem.loadF32Array(addr, 4*4); GL.uniformMatrix4fv(GLGetItem(UNIFORMS, location), false, array); }, CreateProgram: () => { let program = GL.createProgram(); let id = this.getNewId(PROGRAMS); program.name = id; PROGRAMS[id] = program; return id; }, CreateRenderbuffer: () => { let buffer = GL.createRenderbuffer(); let id = this.getNewId(RENDERBUFFERS); buffer.name = id; RENDERBUFFERS[id] = buffer; return id; }, CreateShader: (shaderType) => { let shader = GL.createShader(shaderType); let id = this.getNewId(SHADERS); shader.name = id; SHADERS[id] = shader; return id; }, DeleteBuffer: (id) => { let obj = BUFFERS[id]; if(obj && id != 0) { GL.deleteBuffer(obj); BUFFERS[id] = null; } }, DeleteProgram: (id) => { let obj = PROGRAMS[id]; if(obj && id != 0) { GL.deleteProgram(obj); PROGRAMS[id] = null; } }, DeleteRenderbuffer: (id) => { let obj = RENDERBUFFERS[id]; if(obj && id != 0) { GL.deleteRenderbuffer(obj); RENDERBUFFERS[id] = null; } }, DeleteShader: (id) => { let obj = SHADERS[id]; if(obj && id != 0) { GL.deleteShader(obj); SHADERS[id] = null; } }, DepthFunc: (func) => { GL.depthFunc(func); }, DepthMask: (flag) => { GL.depthMask(!!flag); }, DepthRange: (zNear, zFar) => { GL.depthRange(zNear, zFar); }, DetachShader: (program, shader) => { GL.detachShader(PROGRAMS[program], SHADERS[shader]); }, DrawArrays: (mode, first, count) => { GL.drawArrays(mode, first, count); }, DrawElements: (mode, count, type, indices) => { GL.drawElements(mode, count, type, indices); }, Finish: () => { GL.finish(); }, Flush: () => { GL.flush(); }, FramebufferRenderbuffer: (target, attachment, renderbuffertarget, renderbuffer) => { GL.framebufferRenderbuffer(target, attachment, renderbuffertarget, RENDERBUFFERS[renderbuffer]); }, FramebufferTexture2D: (target, attachment, textarget, texture, level) => { GL.framebufferTexture2D(target, attachment, textarget, TEXTURES[texture], level); }, GenerateMipmap: (target) => { GL.generateMipmap(target); }, GetAttribLocation: (program, name_length, name_ptr) => { let name = LoadString(name_length, name_ptr); return GL.getAttribLocation(PROGRAMS[program], name); }, GetParameter: (pname) => { return GL.getParameter(pname); }, GetParameter4i: (pname, v0, v1, v2, v3) => { const i4 = GL.getParameter(pname); this.mem.storeI32(v0, i4[0]); this.mem.storeI32(v1, i4[1]); this.mem.storeI32(v2, i4[2]); this.mem.storeI32(v3, i4[3]); }, GetVertexAttribOffset: (index, pname) => { return GL.getVertexAttribOffset(index, pname); }, Hint: (target, mode) => { GL.hint(target, mode); }, IsBuffer: (buffer) => GL.isBuffer(BUFFERS[buffer]), IsEnabled: (cap) => GL.isEnabled(cap), IsFramebuffer: (framebuffer) => GL.isFramebuffer(FRAMEBUFFERS[framebuffer]), IsProgram: (program) => GL.isProgram(PROGRAMS[program]), IsRenderbuffer: (renderbuffer) => GL.isRenderbuffer(RENDERBUFFERS[renderbuffer]), IsShader: (shader) => GL.isShader(SHADERS[shader]), IsTexture: (texture) => GL.isTexture(TEXTURES[texture]), LineWidth: (width) => { GL.lineWidth(width); }, PixelStorei: (pname, param) => { GL.pixelStorei(pname, param); }, PolygonOffset: (factor, units) => { GL.polygonOffset(factor, units); }, ReadnPixels: (x, y, width, height, format, type, bufSize, data) => { GL.readPixels(x, y, width, height, format, type, this.mem.loadBytes(data, bufSize)); }, RenderbufferStorage: (target, internalformat, width, height) => { GL.renderbufferStorage(target, internalformat, width, height); }, SampleCoverage: (value, invert) => { GL.sampleCoverage(value, !!invert); }, StencilFunc: (func, ref, mask) => { GL.stencilFunc(func, ref, mask); }, StencilFuncSeparate: (face, func, ref, mask) => { GL.stencilFuncSeparate(face, func, ref, mask); }, StencilMask: (mask) => { GL.stencilMask(mask); }, StencilMaskSeparate: (face, mask) => { GL.stencilMaskSeparate(face, mask); }, StencilOp: (fail, zfail, zpass) => { GL.stencilOp(fail, zfail, zpass); }, StencilOpSeparate: (face, fail, zfail, zpass) => { GL.stencilOpSeparate(face, fail, zfail, zpass); }, TexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, size, data) => { GL.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, this.mem.loadBytes(data, size)); }, VertexAttrib1f: (index, x) => { GL.vertexAttrib1f(index, x); }, VertexAttrib2f: (index, x, y) => { GL.vertexAttrib2f(index, x, y); }, VertexAttrib3f: (index, x, y, z) => { GL.vertexAttrib3f(index, x, y, z); }, VertexAttrib4f: (index, x, y, z, w) => { GL.vertexAttrib4f(index, x, y, z, w); }, }, webgl2: { CopyBufferSubData: (readTarget, writeTarget, readOffset, writeOffset, size) => { AssertWebGL2(); GL.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }, GetBufferSubData: (target, srcByteOffset, dst_buffer_ptr, dst_buffer_len, dstOffset, length) => { AssertWebGL2(); GL.getBufferSubData(target, srcByteOffset, this.mem.loadBytes(dst_buffer_ptr, dst_buffer_len), dstOffset, length); }, /* Framebuffer objects */ BlitFramebuffer: (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) => { AssertWebGL2(); GL.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); }, FramebufferTextureLayer: (target, attachment, texture, level, layer) => { AssertWebGL2(); GL.framebufferTextureLayer(target, attachment, TEXTURES[texture], level, layer); }, InvalidateFramebuffer: (target, attachments_ptr, attachments_len) => { AssertWebGL2(); let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); GL.invalidateFramebuffer(target, attachments); }, InvalidateSubFramebuffer: (target, attachments_ptr, attachments_len, x, y, width, height) => { AssertWebGL2(); let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); GL.invalidateSubFramebuffer(target, attachments, x, y, width, height); }, ReadBuffer: (src) => { AssertWebGL2(); GL.readBuffer(src); }, /* Renderbuffer objects */ RenderbufferStorageMultisample: (target, samples, internalformat, width, height) => { AssertWebGL2(); GL.renderbufferStorageMultisample(target, samples, internalformat, width, height); }, /* Texture objects */ TexStorage3D: (target, levels, internalformat, width, height, depth) => { AssertWebGL2(); GL.texStorage3D(target, levels, internalformat, width, height, depth); }, TexImage3D: (target, level, internalformat, width, height, depth, border, format, type, size, data) => { AssertWebGL2(); if(data) { GL.texImage3D(target, level, internalformat, width, height, depth, border, format, type, this.mem.loadBytes(data, size)); } else { GL.texImage3D(target, level, internalformat, width, height, depth, border, format, type, null); } }, TexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, size, data) => { AssertWebGL2(); GL.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, this.mem.loadBytes(data, size)); }, CompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, data) => { AssertWebGL2(); if(data) { GL.compressedTexImage3D(target, level, internalformat, width, height, depth, border, this.mem.loadBytes(data, imageSize)); } else { GL.compressedTexImage3D(target, level, internalformat, width, height, depth, border, null); } }, CompressedTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data) => { AssertWebGL2(); if(data) { GL.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, this.mem.loadBytes(data, imageSize)); } else { GL.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, null); } }, CopyTexSubImage3D: (target, level, xoffset, yoffset, zoffset, x, y, width, height) => { AssertWebGL2(); GL.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); }, /* Programs and shaders */ GetFragDataLocation: (program, name_ptr, name_len) => { AssertWebGL2(); return GL.getFragDataLocation(PROGRAMS[program], this.mem.loadString(name_ptr, name_len)); }, /* Uniforms */ Uniform1ui: (location, v0) => { AssertWebGL2(); GL.uniform1ui(GLGetItem(UNIFORMS, location), v0); }, Uniform2ui: (location, v0, v1) => { AssertWebGL2(); GL.uniform2ui(GLGetItem(UNIFORMS, location), v0, v1); }, Uniform3ui: (location, v0, v1, v2) => { AssertWebGL2(); GL.uniform3ui(GLGetItem(UNIFORMS, location), v0, v1, v2); }, Uniform4ui: (location, v0, v1, v2, v3) => { AssertWebGL2(); GL.uniform4ui(GLGetItem(UNIFORMS, location), v0, v1, v2, v3); }, UniformMatrix3x2fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 3*2); GL.uniformMatrix3x2fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix4x2fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 4*2); GL.uniformMatrix4x2fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix2x3fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 2*3); GL.uniformMatrix2x3fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix4x3fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 4*3); GL.uniformMatrix4x3fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix2x4fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 2*4); GL.uniformMatrix2x4fv(GLGetItem(UNIFORMS, location), false, array); }, UniformMatrix3x4fv: (location, addr) => { AssertWebGL2(); let array = this.mem.loadF32Array(addr, 3*4); GL.uniformMatrix3x4fv(GLGetItem(UNIFORMS, location), false, array); }, /* Vertex attribs */ VertexAttribI4i: (index, x, y, z, w) => { AssertWebGL2(); GL.vertexAttribI4i(index, x, y, z, w); }, VertexAttribI4ui: (index, x, y, z, w) => { AssertWebGL2(); GL.vertexAttribI4ui(index, x, y, z, w); }, VertexAttribIPointer: (index, size, type, stride, offset) => { AssertWebGL2(); GL.vertexAttribIPointer(index, size, type, stride, offset); }, /* Writing to the drawing buffer */ DrawRangeElements: (mode, start, end, count, type, offset) => { AssertWebGL2(); GL.drawRangeElements(mode, start, end, count, type, offset); }, /* Multiple Render Targets */ DrawBuffers: (buffers_ptr, buffers_len) => { AssertWebGL2(); let array = this.mem.loadU32Array(buffers_ptr, buffers_len); GL.drawBuffers(array); }, ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => { AssertWebGL2(); let array = this.mem.loadF32Array(values_ptr, values_len); GL.clearBufferfv(buffer, drawbuffer, array); }, ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => { AssertWebGL2(); let array = this.mem.loadI32Array(values_ptr, values_len); GL.clearBufferiv(buffer, drawbuffer, array); }, ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => { AssertWebGL2(); let array = this.mem.loadU32Array(values_ptr, values_len); GL.clearBufferuiv(buffer, drawbuffer, array); }, ClearBufferfi: (buffer, drawbuffer, depth, stencil) => { AssertWebGL2(); GL.clearBufferfi(buffer, drawbuffer, depth, stencil); }, /* Query Objects */ CreateQuery: () => { AssertWebGL2(); let query = GL.createQuery(); let id = this.getNewId(QUERIES); query.name = id; QUERIES[id] = query; return id; }, DeleteQuery: (id) => { AssertWebGL2(); let obj = QUERIES[id]; if(obj && id != 0) { GL.deleteQuery(obj); QUERIES[id] = null; } }, IsQuery: (query) => { AssertWebGL2(); return GL.isQuery(QUERIES[query]); }, BeginQuery: (target, query) => { AssertWebGL2(); GL.beginQuery(target, QUERIES[query]) }, EndQuery: (target) => { AssertWebGL2(); GL.endQuery(target); }, GetQuery: (target, pname) => { AssertWebGL2(); let query = GL.getQuery(target, pname); if(!query) { return 0; } if(QUERIES.indexOf(query) !== -1) { return query.name; } let id = this.getNewId(QUERIES); query.name = id; QUERIES[id] = query; return id; }, /* Sampler Objects */ CreateSampler: () => { AssertWebGL2(); let sampler = GL.createSampler(); let id = this.getNewId(this.samplers); sampler.name = id; this.samplers[id] = sampler; return id; }, DeleteSampler: (id) => { AssertWebGL2(); let obj = this.samplers[id]; if(obj && id != 0) { GL.deleteSampler(obj); this.samplers[id] = null; } }, IsSampler: (sampler) => { AssertWebGL2(); return GL.isSampler(this.samplers[sampler]); }, BindSampler: (unit, sampler) => { AssertWebGL2(); GL.bindSampler(unit, this.samplers[sampler]); }, SamplerParameteri: (sampler, pname, param) => { AssertWebGL2(); GL.samplerParameteri(this.samplers[sampler], pname, param); }, SamplerParameterf: (sampler, pname, param) => { AssertWebGL2(); GL.samplerParameterf(this.samplers[sampler], pname, param); }, /* Sync objects */ FenceSync: (condition, flags) => { AssertWebGL2(); let sync = GL.fenceSync(condition, flags); let id = this.getNewId(SYNCS); sync.name = id; SYNCS[id] = sync; return id; }, IsSync: (sync) => { AssertWebGL2(); return GL.isSync(SYNCS[sync]); }, DeleteSync: (id) => { AssertWebGL2(); let obj = SYNCS[id]; if(obj && id != 0) { GL.deleteSampler(obj); SYNCS[id] = null; } }, ClientWaitSync: (sync, flags, timeout) => { AssertWebGL2(); return GL.clientWaitSync(SYNCS[sync], flags, timeout); }, WaitSync: (sync, flags, timeout) => { AssertWebGL2(); GL.waitSync(SYNCS[sync], flags, timeout) ; }, /* Transform Feedback */ CreateTransformFeedback: () => { AssertWebGL2(); let transformFeedback = GL.createTransformFeedback(); let id = this.getNewId(TRANSFORM_FEEDBACK); transformFeedback.name = id; TRANSFORM_FEEDBACK[id] = transformFeedback; return id; }, DeleteTransformFeedback: (id) => { AssertWebGL2(); let obj = TRANSFORM_FEEDBACK[id]; if(obj && id != 0) { GL.deleteTransformFeedback(obj); TRANSFORM_FEEDBACK[id] = null; } }, IsTransformFeedback: (tf) => { AssertWebGL2(); return GL.isTransformFeedback(TRANSFORM_FEEDBACK[tf]); }, BindTransformFeedback: (target, tf) => { AssertWebGL2(); GL.bindTransformFeedback(target, TRANSFORM_FEEDBACK[tf]); }, BeginTransformFeedback: (primitiveMode) => { AssertWebGL2(); GL.beginTransformFeedback(primitiveMode); }, EndTransformFeedback: () => { AssertWebGL2(); GL.endTransformFeedback(); }, TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => { AssertWebGL2(); const stringSize = this.mem.intSize*2; let varyings = []; for(let i = 0; i < varyings_len; i++) { let ptr = this.mem.loadPtr(varyings_ptr + i*stringSize + 0*4); let len = this.mem.loadPtr(varyings_ptr + i*stringSize + 1*4); varyings.push(this.mem.loadString(ptr, len)); } GL.transformFeedbackVaryings(PROGRAMS[program], varyings, bufferMode); }, PauseTransformFeedback: () => { AssertWebGL2(); GL.pauseTransformFeedback(); }, ResumeTransformFeedback: () => { AssertWebGL2(); GL.resumeTransformFeedback(); }, /* Uniform Buffer Objects and Transform Feedback Buffers */ BindBufferRange: (target, index, buffer, offset, size) => { AssertWebGL2(); GL.bindBufferRange(target, index, BUFFERS[buffer], offset, size); }, // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { AssertWebGL2(); let name = GL.getActiveUniformBlockName(PROGRAMS[program], uniformBlockIndex); let n = Math.min(buf_len, name.length); name = name.substring(0, n); this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(name)) this.mem.storeInt(length_ptr, n); }, UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { AssertWebGL2(); GL.uniformBlockBinding(PROGRAMS[program], uniformBlockIndex, uniformBlockBinding); }, /* Vertex Array Objects */ IsVertexArray: (vertexArray) => { AssertWebGL2(); return GL.isVertexArray(VAOS[vertexArray]); }, }, }; async function StartWasm(path) { const response = await fetch(path); const data = await response.arrayBuffer(); const wasm = await WebAssembly.instantiate(data, imports); const exports = wasm.instance.exports; if(exports.memory) { SetMemory(exports.memory); } exports?._start(); exports?.RunTests(); return; }