webgl.mjs raw

   1  // Moxie jsruntime - WebGL2 Bridge
   2  // Handle-based shim for WebGL2 contexts and objects.
   3  // Functions read/write WASM memory directly via globalThis.__moxie_wasm_memory.
   4  
   5  const _contexts = new Map();
   6  const _buffers = new Map();
   7  const _textures = new Map();
   8  const _framebuffers = new Map();
   9  const _renderbuffers = new Map();
  10  const _shaders = new Map();
  11  const _programs = new Map();
  12  const _uniformLocs = new Map();
  13  const _vaos = new Map();
  14  
  15  let _nextCtx = 1;
  16  let _nextBuf = 1;
  17  let _nextTex = 1;
  18  let _nextFbo = 1;
  19  let _nextRbo = 1;
  20  let _nextShader = 1;
  21  let _nextProg = 1;
  22  let _nextUloc = 1;
  23  let _nextVao = 1;
  24  
  25  function _mem() {
  26    return globalThis.__moxie_wasm_memory;
  27  }
  28  
  29  function _readBytes(ptr, len) {
  30    if (!ptr || len <= 0) return null;
  31    return new Uint8Array(_mem().buffer, ptr, len);
  32  }
  33  
  34  function _readStr(ptr, len) {
  35    if (!ptr || len <= 0) return '';
  36    return new TextDecoder().decode(new Uint8Array(_mem().buffer, ptr, len));
  37  }
  38  
  39  function _writeBytes(ptr, data) {
  40    new Uint8Array(_mem().buffer, ptr, data.length).set(data);
  41  }
  42  
  43  // --- Context ---
  44  
  45  // RegisterContext registers an existing WebGL2 context into the handle system.
  46  // Called by app.mjs after creating the context from a canvas element.
  47  export function RegisterContext(gl) {
  48    if (!gl) return 0;
  49    const id = _nextCtx++;
  50    _contexts.set(id, gl);
  51    return id;
  52  }
  53  
  54  export function CreateContext(canvasId) {
  55    const canvas = document.getElementById(canvasId) || document.querySelector('canvas');
  56    if (!canvas) return 0;
  57    const gl = canvas.getContext('webgl2', {
  58      alpha: true,
  59      depth: true,
  60      stencil: true,
  61      antialias: false,
  62      premultipliedAlpha: true,
  63      preserveDrawingBuffer: false,
  64    });
  65    if (!gl) return 0;
  66    const id = _nextCtx++;
  67    _contexts.set(id, gl);
  68    return id;
  69  }
  70  
  71  export function CreateContextFromHandle(elHandle) {
  72    // For use with DOM handle system - caller passes DOM element handle.
  73    // The canvas element must be retrieved from the DOM module's handle map.
  74    // Fallback: if globalThis.__moxie_dom_elements exists, use it.
  75    const elements = globalThis.__moxie_dom_elements;
  76    if (!elements) return 0;
  77    const canvas = elements.get(elHandle);
  78    if (!canvas || canvas.tagName !== 'CANVAS') return 0;
  79    const gl = canvas.getContext('webgl2', {
  80      alpha: true,
  81      depth: true,
  82      stencil: true,
  83      antialias: false,
  84      premultipliedAlpha: true,
  85      preserveDrawingBuffer: false,
  86    });
  87    if (!gl) return 0;
  88    const id = _nextCtx++;
  89    _contexts.set(id, gl);
  90    return id;
  91  }
  92  
  93  export function DeleteContext(ctx) {
  94    const gl = _contexts.get(ctx);
  95    if (!gl) return;
  96    gl.getExtension('WEBGL_lose_context')?.loseContext();
  97    _contexts.delete(ctx);
  98  }
  99  
 100  export function IsWebGL2(ctx) {
 101    const gl = _contexts.get(ctx);
 102    return gl instanceof WebGL2RenderingContext ? 1 : 0;
 103  }
 104  
 105  // --- State ---
 106  
 107  export function Enable(ctx, cap) {
 108    _contexts.get(ctx)?.enable(cap);
 109  }
 110  
 111  export function Disable(ctx, cap) {
 112    _contexts.get(ctx)?.disable(cap);
 113  }
 114  
 115  export function Viewport(ctx, x, y, w, h) {
 116    _contexts.get(ctx)?.viewport(x, y, w, h);
 117  }
 118  
 119  export function Scissor(ctx, x, y, w, h) {
 120    _contexts.get(ctx)?.scissor(x, y, w, h);
 121  }
 122  
 123  export function BlendFunc(ctx, src, dst) {
 124    _contexts.get(ctx)?.blendFunc(src, dst);
 125  }
 126  
 127  export function BlendFuncSeparate(ctx, srcRGB, dstRGB, srcA, dstA) {
 128    _contexts.get(ctx)?.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
 129  }
 130  
 131  export function DepthFunc(ctx, func) {
 132    _contexts.get(ctx)?.depthFunc(func);
 133  }
 134  
 135  export function DepthMask(ctx, flag) {
 136    _contexts.get(ctx)?.depthMask(!!flag);
 137  }
 138  
 139  export function ColorMask(ctx, r, g, b, a) {
 140    _contexts.get(ctx)?.colorMask(!!r, !!g, !!b, !!a);
 141  }
 142  
 143  export function StencilFunc(ctx, func, ref, mask) {
 144    _contexts.get(ctx)?.stencilFunc(func, ref, mask);
 145  }
 146  
 147  export function StencilOp(ctx, sfail, dpfail, dppass) {
 148    _contexts.get(ctx)?.stencilOp(sfail, dpfail, dppass);
 149  }
 150  
 151  export function ClearColor(ctx, r, g, b, a) {
 152    _contexts.get(ctx)?.clearColor(r, g, b, a);
 153  }
 154  
 155  export function ClearDepth(ctx, d) {
 156    _contexts.get(ctx)?.clearDepth(d);
 157  }
 158  
 159  export function ClearStencil(ctx, s) {
 160    _contexts.get(ctx)?.clearStencil(s);
 161  }
 162  
 163  export function Clear(ctx, mask) {
 164    _contexts.get(ctx)?.clear(mask);
 165  }
 166  
 167  export function Flush(ctx) {
 168    _contexts.get(ctx)?.flush();
 169  }
 170  
 171  export function GetError(ctx) {
 172    const gl = _contexts.get(ctx);
 173    return gl ? gl.getError() : 0;
 174  }
 175  
 176  export function PixelStorei(ctx, pname, param) {
 177    _contexts.get(ctx)?.pixelStorei(pname, param);
 178  }
 179  
 180  export function ReadPixels(ctx, x, y, w, h, format, type, bufPtr, bufLen) {
 181    const gl = _contexts.get(ctx);
 182    if (!gl) return;
 183    const dst = new Uint8Array(_mem().buffer, bufPtr, bufLen);
 184    gl.readPixels(x, y, w, h, format, type, dst);
 185  }
 186  
 187  // --- Buffers ---
 188  
 189  export function CreateBuffer(ctx) {
 190    const gl = _contexts.get(ctx);
 191    if (!gl) return 0;
 192    const buf = gl.createBuffer();
 193    if (!buf) return 0;
 194    const id = _nextBuf++;
 195    _buffers.set(id, buf);
 196    return id;
 197  }
 198  
 199  export function DeleteBuffer(ctx, buf) {
 200    const gl = _contexts.get(ctx);
 201    const obj = _buffers.get(buf);
 202    if (gl && obj) gl.deleteBuffer(obj);
 203    _buffers.delete(buf);
 204  }
 205  
 206  export function BindBuffer(ctx, target, buf) {
 207    const gl = _contexts.get(ctx);
 208    if (!gl) return;
 209    gl.bindBuffer(target, buf ? _buffers.get(buf) || null : null);
 210  }
 211  
 212  export function BufferData(ctx, target, dataPtr, dataLen, usage) {
 213    const gl = _contexts.get(ctx);
 214    if (!gl) return;
 215    const data = _readBytes(dataPtr, dataLen);
 216    if (data) {
 217      gl.bufferData(target, data, usage);
 218    } else {
 219      gl.bufferData(target, dataLen, usage);
 220    }
 221  }
 222  
 223  export function BufferSubData(ctx, target, offset, dataPtr, dataLen) {
 224    const gl = _contexts.get(ctx);
 225    if (!gl) return;
 226    const data = _readBytes(dataPtr, dataLen);
 227    if (data) gl.bufferSubData(target, offset, data);
 228  }
 229  
 230  // --- Textures ---
 231  
 232  export function CreateTexture(ctx) {
 233    const gl = _contexts.get(ctx);
 234    if (!gl) return 0;
 235    const tex = gl.createTexture();
 236    if (!tex) return 0;
 237    const id = _nextTex++;
 238    _textures.set(id, tex);
 239    return id;
 240  }
 241  
 242  export function DeleteTexture(ctx, tex) {
 243    const gl = _contexts.get(ctx);
 244    const obj = _textures.get(tex);
 245    if (gl && obj) gl.deleteTexture(obj);
 246    _textures.delete(tex);
 247  }
 248  
 249  export function BindTexture(ctx, target, tex) {
 250    const gl = _contexts.get(ctx);
 251    if (!gl) return;
 252    gl.bindTexture(target, tex ? _textures.get(tex) || null : null);
 253  }
 254  
 255  export function ActiveTexture(ctx, unit) {
 256    _contexts.get(ctx)?.activeTexture(unit);
 257  }
 258  
 259  export function TexParameteri(ctx, target, pname, param) {
 260    _contexts.get(ctx)?.texParameteri(target, pname, param);
 261  }
 262  
 263  export function TexImage2D(ctx, target, level, internalformat, width, height, border, format, type, dataPtr, dataLen) {
 264    const gl = _contexts.get(ctx);
 265    if (!gl) return;
 266    const data = _readBytes(dataPtr, dataLen);
 267    gl.texImage2D(target, level, internalformat, width, height, border, format, type, data);
 268  }
 269  
 270  export function TexSubImage2D(ctx, target, level, xoff, yoff, width, height, format, type, dataPtr, dataLen) {
 271    const gl = _contexts.get(ctx);
 272    if (!gl) return;
 273    const data = _readBytes(dataPtr, dataLen);
 274    if (data) gl.texSubImage2D(target, level, xoff, yoff, width, height, format, type, data);
 275  }
 276  
 277  export function GenerateMipmap(ctx, target) {
 278    _contexts.get(ctx)?.generateMipmap(target);
 279  }
 280  
 281  // --- Framebuffers ---
 282  
 283  export function CreateFramebuffer(ctx) {
 284    const gl = _contexts.get(ctx);
 285    if (!gl) return 0;
 286    const fbo = gl.createFramebuffer();
 287    if (!fbo) return 0;
 288    const id = _nextFbo++;
 289    _framebuffers.set(id, fbo);
 290    return id;
 291  }
 292  
 293  export function DeleteFramebuffer(ctx, fbo) {
 294    const gl = _contexts.get(ctx);
 295    const obj = _framebuffers.get(fbo);
 296    if (gl && obj) gl.deleteFramebuffer(obj);
 297    _framebuffers.delete(fbo);
 298  }
 299  
 300  export function BindFramebuffer(ctx, target, fbo) {
 301    const gl = _contexts.get(ctx);
 302    if (!gl) return;
 303    gl.bindFramebuffer(target, fbo ? _framebuffers.get(fbo) || null : null);
 304  }
 305  
 306  export function FramebufferTexture2D(ctx, target, attachment, textarget, tex, level) {
 307    const gl = _contexts.get(ctx);
 308    if (!gl) return;
 309    const obj = tex ? _textures.get(tex) || null : null;
 310    gl.framebufferTexture2D(target, attachment, textarget, obj, level);
 311  }
 312  
 313  export function CheckFramebufferStatus(ctx, target) {
 314    const gl = _contexts.get(ctx);
 315    return gl ? gl.checkFramebufferStatus(target) : 0;
 316  }
 317  
 318  // --- Renderbuffers ---
 319  
 320  export function CreateRenderbuffer(ctx) {
 321    const gl = _contexts.get(ctx);
 322    if (!gl) return 0;
 323    const rbo = gl.createRenderbuffer();
 324    if (!rbo) return 0;
 325    const id = _nextRbo++;
 326    _renderbuffers.set(id, rbo);
 327    return id;
 328  }
 329  
 330  export function DeleteRenderbuffer(ctx, rbo) {
 331    const gl = _contexts.get(ctx);
 332    const obj = _renderbuffers.get(rbo);
 333    if (gl && obj) gl.deleteRenderbuffer(obj);
 334    _renderbuffers.delete(rbo);
 335  }
 336  
 337  export function BindRenderbuffer(ctx, target, rbo) {
 338    const gl = _contexts.get(ctx);
 339    if (!gl) return;
 340    gl.bindRenderbuffer(target, rbo ? _renderbuffers.get(rbo) || null : null);
 341  }
 342  
 343  export function RenderbufferStorage(ctx, target, internalformat, width, height) {
 344    _contexts.get(ctx)?.renderbufferStorage(target, internalformat, width, height);
 345  }
 346  
 347  export function FramebufferRenderbuffer(ctx, target, attachment, renderbuffertarget, rbo) {
 348    const gl = _contexts.get(ctx);
 349    if (!gl) return;
 350    const obj = rbo ? _renderbuffers.get(rbo) || null : null;
 351    gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, obj);
 352  }
 353  
 354  // --- Shaders ---
 355  
 356  export function CreateShader(ctx, type) {
 357    const gl = _contexts.get(ctx);
 358    if (!gl) return 0;
 359    const shader = gl.createShader(type);
 360    if (!shader) return 0;
 361    const id = _nextShader++;
 362    _shaders.set(id, shader);
 363    return id;
 364  }
 365  
 366  export function DeleteShader(ctx, shader) {
 367    const gl = _contexts.get(ctx);
 368    const obj = _shaders.get(shader);
 369    if (gl && obj) gl.deleteShader(obj);
 370    _shaders.delete(shader);
 371  }
 372  
 373  export function ShaderSource(ctx, shader, srcPtr, srcLen) {
 374    const gl = _contexts.get(ctx);
 375    const obj = _shaders.get(shader);
 376    if (!gl || !obj) return;
 377    gl.shaderSource(obj, _readStr(srcPtr, srcLen));
 378  }
 379  
 380  export function CompileShader(ctx, shader) {
 381    const gl = _contexts.get(ctx);
 382    const obj = _shaders.get(shader);
 383    if (gl && obj) gl.compileShader(obj);
 384  }
 385  
 386  export function GetShaderParameter(ctx, shader, pname) {
 387    const gl = _contexts.get(ctx);
 388    const obj = _shaders.get(shader);
 389    if (!gl || !obj) return 0;
 390    const v = gl.getShaderParameter(obj, pname);
 391    return typeof v === 'boolean' ? (v ? 1 : 0) : (v | 0);
 392  }
 393  
 394  export function GetShaderInfoLog(ctx, shader, bufPtr, bufLen) {
 395    const gl = _contexts.get(ctx);
 396    const obj = _shaders.get(shader);
 397    if (!gl || !obj) return 0;
 398    const log = gl.getShaderInfoLog(obj) || '';
 399    const encoded = new TextEncoder().encode(log);
 400    const n = Math.min(encoded.length, bufLen);
 401    if (n > 0) _writeBytes(bufPtr, encoded.subarray(0, n));
 402    return encoded.length;
 403  }
 404  
 405  // --- Programs ---
 406  
 407  export function CreateProgram(ctx) {
 408    const gl = _contexts.get(ctx);
 409    if (!gl) return 0;
 410    const prog = gl.createProgram();
 411    if (!prog) return 0;
 412    const id = _nextProg++;
 413    _programs.set(id, prog);
 414    return id;
 415  }
 416  
 417  export function DeleteProgram(ctx, prog) {
 418    const gl = _contexts.get(ctx);
 419    const obj = _programs.get(prog);
 420    if (gl && obj) gl.deleteProgram(obj);
 421    _programs.delete(prog);
 422  }
 423  
 424  export function AttachShader(ctx, prog, shader) {
 425    const gl = _contexts.get(ctx);
 426    const p = _programs.get(prog);
 427    const s = _shaders.get(shader);
 428    if (gl && p && s) gl.attachShader(p, s);
 429  }
 430  
 431  export function LinkProgram(ctx, prog) {
 432    const gl = _contexts.get(ctx);
 433    const obj = _programs.get(prog);
 434    if (gl && obj) gl.linkProgram(obj);
 435  }
 436  
 437  export function UseProgram(ctx, prog) {
 438    const gl = _contexts.get(ctx);
 439    if (!gl) return;
 440    gl.useProgram(prog ? _programs.get(prog) || null : null);
 441  }
 442  
 443  export function GetProgramParameter(ctx, prog, pname) {
 444    const gl = _contexts.get(ctx);
 445    const obj = _programs.get(prog);
 446    if (!gl || !obj) return 0;
 447    const v = gl.getProgramParameter(obj, pname);
 448    return typeof v === 'boolean' ? (v ? 1 : 0) : (v | 0);
 449  }
 450  
 451  export function GetProgramInfoLog(ctx, prog, bufPtr, bufLen) {
 452    const gl = _contexts.get(ctx);
 453    const obj = _programs.get(prog);
 454    if (!gl || !obj) return 0;
 455    const log = gl.getProgramInfoLog(obj) || '';
 456    const encoded = new TextEncoder().encode(log);
 457    const n = Math.min(encoded.length, bufLen);
 458    if (n > 0) _writeBytes(bufPtr, encoded.subarray(0, n));
 459    return encoded.length;
 460  }
 461  
 462  export function BindAttribLocation(ctx, prog, index, namePtr, nameLen) {
 463    const gl = _contexts.get(ctx);
 464    const obj = _programs.get(prog);
 465    if (!gl || !obj) return;
 466    gl.bindAttribLocation(obj, index, _readStr(namePtr, nameLen));
 467  }
 468  
 469  export function GetUniformLocation(ctx, prog, namePtr, nameLen) {
 470    const gl = _contexts.get(ctx);
 471    const obj = _programs.get(prog);
 472    if (!gl || !obj) return 0;
 473    const loc = gl.getUniformLocation(obj, _readStr(namePtr, nameLen));
 474    if (!loc) return 0;
 475    const id = _nextUloc++;
 476    _uniformLocs.set(id, loc);
 477    return id;
 478  }
 479  
 480  // --- Uniforms ---
 481  
 482  export function Uniform1f(ctx, loc, v0) {
 483    const gl = _contexts.get(ctx);
 484    const u = _uniformLocs.get(loc);
 485    if (gl && u) gl.uniform1f(u, v0);
 486  }
 487  
 488  export function Uniform2f(ctx, loc, v0, v1) {
 489    const gl = _contexts.get(ctx);
 490    const u = _uniformLocs.get(loc);
 491    if (gl && u) gl.uniform2f(u, v0, v1);
 492  }
 493  
 494  export function Uniform4f(ctx, loc, v0, v1, v2, v3) {
 495    const gl = _contexts.get(ctx);
 496    const u = _uniformLocs.get(loc);
 497    if (gl && u) gl.uniform4f(u, v0, v1, v2, v3);
 498  }
 499  
 500  export function Uniform1i(ctx, loc, v0) {
 501    const gl = _contexts.get(ctx);
 502    const u = _uniformLocs.get(loc);
 503    if (gl && u) gl.uniform1i(u, v0);
 504  }
 505  
 506  // --- Vertex Attributes ---
 507  
 508  export function EnableVertexAttribArray(ctx, index) {
 509    _contexts.get(ctx)?.enableVertexAttribArray(index);
 510  }
 511  
 512  export function DisableVertexAttribArray(ctx, index) {
 513    _contexts.get(ctx)?.disableVertexAttribArray(index);
 514  }
 515  
 516  export function VertexAttribPointer(ctx, index, size, type, normalized, stride, offset) {
 517    _contexts.get(ctx)?.vertexAttribPointer(index, size, type, !!normalized, stride, offset);
 518  }
 519  
 520  // --- VAO (WebGL2) ---
 521  
 522  export function CreateVertexArray(ctx) {
 523    const gl = _contexts.get(ctx);
 524    if (!gl) return 0;
 525    const vao = gl.createVertexArray();
 526    if (!vao) return 0;
 527    const id = _nextVao++;
 528    _vaos.set(id, vao);
 529    return id;
 530  }
 531  
 532  export function DeleteVertexArray(ctx, vao) {
 533    const gl = _contexts.get(ctx);
 534    const obj = _vaos.get(vao);
 535    if (gl && obj) gl.deleteVertexArray(obj);
 536    _vaos.delete(vao);
 537  }
 538  
 539  export function BindVertexArray(ctx, vao) {
 540    const gl = _contexts.get(ctx);
 541    if (!gl) return;
 542    gl.bindVertexArray(vao ? _vaos.get(vao) || null : null);
 543  }
 544  
 545  // --- Blending and misc state ---
 546  
 547  export function BlendEquation(ctx, mode) {
 548    _contexts.get(ctx)?.blendEquation(mode);
 549  }
 550  
 551  // --- Mipmap textures ---
 552  
 553  export function TexStorage2D(ctx, target, levels, internalformat, width, height) {
 554    _contexts.get(ctx)?.texStorage2D(target, levels, internalformat, width, height);
 555  }
 556  
 557  // --- Framebuffer ops ---
 558  
 559  export function CopyTexSubImage2D(ctx, target, level, xoff, yoff, x, y, w, h) {
 560    _contexts.get(ctx)?.copyTexSubImage2D(target, level, xoff, yoff, x, y, w, h);
 561  }
 562  
 563  export function InvalidateFramebuffer(ctx, target, attachment) {
 564    const gl = _contexts.get(ctx);
 565    if (!gl || !gl.invalidateFramebuffer) return;
 566    gl.invalidateFramebuffer(target, [attachment]);
 567  }
 568  
 569  // --- Queries ---
 570  
 571  export function GetInteger(ctx, pname) {
 572    const gl = _contexts.get(ctx);
 573    return gl ? (gl.getParameter(pname) | 0) : 0;
 574  }
 575  
 576  export function IsEnabled(ctx, cap) {
 577    const gl = _contexts.get(ctx);
 578    return gl && gl.isEnabled(cap) ? 1 : 0;
 579  }
 580  
 581  // --- Finish ---
 582  
 583  export function Finish(ctx) {
 584    _contexts.get(ctx)?.finish();
 585  }
 586  
 587  // --- Extra uniforms ---
 588  
 589  export function Uniform3f(ctx, loc, v0, v1, v2) {
 590    const gl = _contexts.get(ctx);
 591    const u = _uniformLocs.get(loc);
 592    if (gl && u) gl.uniform3f(u, v0, v1, v2);
 593  }
 594  
 595  // --- Drawing ---
 596  
 597  export function DrawArrays(ctx, mode, first, count) {
 598    _contexts.get(ctx)?.drawArrays(mode, first, count);
 599  }
 600  
 601  export function DrawElements(ctx, mode, count, type, offset) {
 602    _contexts.get(ctx)?.drawElements(mode, count, type, offset);
 603  }
 604