From eeb7e775f3c1ed0853c8b51e47fb602cd44f37ff Mon Sep 17 00:00:00 2001 From: fendevel Date: Sat, 28 Feb 2026 00:37:34 +0000 Subject: [PATCH] webgl: Add bindings, fix `Tex*Image*D` --- core/sys/wasm/js/odin.js | 98 +++++++++++++++++++++++++++-------- vendor/wasm/WebGL/webgl.odin | 16 ++++++ vendor/wasm/WebGL/webgl2.odin | 1 + 3 files changed, 92 insertions(+), 23 deletions(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 19ac3093e..4d19916bd 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -10,7 +10,7 @@ function getElement(name) { } function stripNewline(str) { - return str.replace(/\n/, ' ') + return str.replace(/\n/, ' ') } class WasmMemoryInterface { @@ -40,22 +40,14 @@ class WasmMemoryInterface { } - loadF32Array(addr, len) { - let array = new Float32Array(this.memory.buffer, addr, len); - return array; - } - loadF64Array(addr, len) { - let array = new Float64Array(this.memory.buffer, addr, len); - return array; - } - loadU32Array(addr, len) { - let array = new Uint32Array(this.memory.buffer, addr, len); - return array; - } - loadI32Array(addr, len) { - let array = new Int32Array(this.memory.buffer, addr, len); - return array; - } + loadF32Array(addr, len) { return new Float32Array(this.memory.buffer, addr, len); } + loadF64Array(addr, len) { return new Float64Array(this.memory.buffer, addr, len); } + loadU32Array(addr, len) { return new Uint32Array(this.memory.buffer, addr, len); } + loadI32Array(addr, len) { return new Int32Array(this.memory.buffer, addr, len); } + loadU16Array(addr, len) { return new Uint16Array(this.memory.buffer, addr, len); } + loadI16Array(addr, len) { return new Int16Array(this.memory.buffer, addr, len); } + loadU8Array(addr, len) { return new Uint8Array(this.memory.buffer, addr, len); } + loadI8Array(addr, len) { return new Int8Array(this.memory.buffer, addr, len); } loadU8(addr) { return this.mem.getUint8 (addr); } @@ -200,6 +192,7 @@ class WebGLInterface { this.transformFeedbacks = []; this.syncs = []; this.programInfos = {}; + this.extensions = []; } get mem() { @@ -225,6 +218,9 @@ class WebGLInterface { } else { this.ctxVersion = 1.0; } + + this.extensions = this.ctx.getSupportedExtensions(); + return true; } @@ -281,6 +277,30 @@ class WebGLInterface { return source; } + loadTypedArrayFromTexelType(type, size, data) { + switch (type) { + case this.ctx.SHORT: return this.mem.loadI16Array(data, size/2); + case this.ctx.UNSIGNED_SHORT_5_5_5_1: + case this.ctx.UNSIGNED_SHORT_5_6_5: + case this.ctx.UNSIGNED_SHORT_4_4_4_4: + case this.ctx.UNSIGNED_SHORT: + case this.ctx.HALF_FLOAT: return this.mem.loadU16Array(data, size/2); + case this.ctx.FLOAT: return this.mem.loadF32Array(data, size/4); + case this.ctx.INT: return this.mem.loadI32Array(data, size/4) + case this.ctx.UNSIGNED_INT_24_8: + case this.ctx.UNSIGNED_INT_5_9_9_9_REV: + case this.ctx.UNSIGNED_INT_10F_11F_11F_REV: + case this.ctx.UNSIGNED_INT_2_10_10_10_REV: + case this.ctx.UNSIGNED_INT: return this.mem.loadU32Array(data, size/4) + case this.ctx.BYTE: return this.mem.loadI8Array(data, size); + case this.ctx.UNSIGNED_BYTE: return this.mem.loadU8Array(data, size); + case this.ctx.FLOAT_32_UNSIGNED_INT_24_8_REV: + throw new Error("Source data of type FLOAT_32_UNSIGNED_INT_24_8_REV must be null"); + } + + throw new Error(`Unsupported texture data type: 0x${type.toString(16)}`); + } + getWebGL1Interface() { return { SetCurrentContextById: (name_ptr, name_len) => { @@ -327,10 +347,30 @@ class WebGLInterface { IsExtensionSupported: (name_ptr, name_len) => { let name = this.mem.loadString(name_ptr, name_len); - let extensions = this.ctx.getSupportedExtensions(); + let extensions = this.extensions; return extensions.indexOf(name) !== -1 }, + GetSupportedExtensionFromIndex: (index, buf_ptr, buf_len, length_ptr) => { + let extensions = this.extensions; + if (index < 0 || index >= extensions.length) { + return false; + } + + let ext = extensions[index]; + const n = Math.min(buf_len, ext.length); + ext = ext.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(ext)); + + this.mem.storeInt(length_ptr, n); + + return true; + }, + + GetExtension: (name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + this.ctx.getExtension(name); + }, GetError: () => { let err = this.lastError; @@ -628,6 +668,11 @@ class WebGLInterface { this.ctx.generateMipmap(target); }, + GetBufferParameter: (target, pname) => { + return this.ctx.getBufferParameter(target, pname); + }, + + GetActiveAttrib: (program, index, size_ptr, type_ptr, name_buf_ptr, name_buf_len, name_len_ptr) => { const info = this.ctx.getActiveAttrib(this.programs[program], index); @@ -830,7 +875,8 @@ class WebGLInterface { TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { if (data) { - this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, this.mem.loadBytes(data, size)); + const texelData = this.loadTypedArrayFromTexelType(type, size, data); + this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, texelData); } else { this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, null); } @@ -842,7 +888,8 @@ class WebGLInterface { this.ctx.texParameteri(target, pname, param); }, TexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, size, data) => { - this.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, this.mem.loadBytes(data, size)); + const texelData = this.loadTypedArrayFromTexelType(type, size, data); + this.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, texelData); }, @@ -976,7 +1023,10 @@ class WebGLInterface { }, /* Texture objects */ - + TexStorage2D: (target, levels, internalformat, width, height) => { + this.assertWebGL2(); + this.ctx.texStorage2D(target, levels, internalformat, width, height); + }, TexStorage3D: (target, levels, internalformat, width, height, depth) => { this.assertWebGL2(); this.ctx.texStorage3D(target, levels, internalformat, width, height, depth); @@ -984,14 +1034,16 @@ class WebGLInterface { TexImage3D: (target, level, internalformat, width, height, depth, border, format, type, size, data) => { this.assertWebGL2(); if (data) { - this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, this.mem.loadBytes(data, size)); + const texelData = this.loadTypedArrayFromTexelType(type, size, data); + this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, texelData); } else { this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, null); } }, TexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, size, data) => { this.assertWebGL2(); - this.ctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, this.mem.loadBytes(data, size)); + const texelData = this.loadTypedArrayFromTexelType(type, size, data); + this.ctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texelData); }, CompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, data) => { this.assertWebGL2(); diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index d607ec743..6338d6271 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -52,6 +52,7 @@ foreign webgl { GetError :: proc() -> Enum --- IsExtensionSupported :: proc(name: string) -> bool --- + GetExtension :: proc(name: string) --- ActiveTexture :: proc(x: Enum) --- AttachShader :: proc(program: Program, shader: Shader) --- @@ -118,6 +119,7 @@ foreign webgl { GenerateMipmap :: proc(target: Enum) --- + GetBufferParameter :: proc(target, name: Enum) -> int --- GetAttribLocation :: proc(program: Program, name: string) -> i32 --- GetUniformLocation :: proc(program: Program, name: string) -> i32 --- GetVertexAttribOffset :: proc(index: i32, pname: Enum) -> uintptr --- @@ -373,7 +375,21 @@ GetShaderInfoLog :: proc "contextless" (shader: Shader, buf: []byte) -> string { return string(buf[:length]) } +IterateSupportedExtensions :: proc(index: ^int, buf: []byte) -> (string, bool) { + foreign webgl { + @(link_name="GetSupportedExtensionFromIndex") + _GetSupportedExtensionFromIndex :: proc "contextless" (index: int, buf: []byte, length: ^int) -> bool --- + } + length: int + if !_GetSupportedExtensionFromIndex(index^, buf[:], &length) { + return "", false + } + + index^ += 1 + + return string(buf[:length]), true +} BufferDataSlice :: proc "contextless" (target: Enum, slice: $S/[]$E, usage: Enum) { BufferData(target, len(slice)*size_of(E), raw_data(slice), usage) diff --git a/vendor/wasm/WebGL/webgl2.odin b/vendor/wasm/WebGL/webgl2.odin index 1e36fd0fd..677287bd1 100644 --- a/vendor/wasm/WebGL/webgl2.odin +++ b/vendor/wasm/WebGL/webgl2.odin @@ -35,6 +35,7 @@ foreign webgl2 { RenderbufferStorageMultisample :: proc(target: Enum, samples: i32, internalformat: Enum, width, height: i32) --- /* Texture objects */ + TexStorage2D :: proc(target: Enum, levels: i32, internalformat: Enum, width, height: i32) --- TexStorage3D :: proc(target: Enum, levels: i32, internalformat: Enum, width, height, depth: i32) --- TexImage3D :: proc(target: Enum, level: i32, internalformat: Enum, width, height, depth: i32, border: i32, format, type: Enum, size: int, data: rawptr) --- TexSubImage3D :: proc(target: Enum, level: i32, xoffset, yoffset, zoffset, width, height, depth: i32, format, type: Enum, size: int, data: rawptr) ---