romdevtools 0.44.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +572 -0
  2. package/README.md +9 -7
  3. package/examples/dreamcast/hello/main.c +24 -0
  4. package/examples/n64/platformer/main.c +158 -0
  5. package/examples/n64/puzzle/main.c +117 -0
  6. package/examples/n64/racing/main.c +147 -0
  7. package/examples/n64/shmup/main.c +122 -0
  8. package/examples/n64/sports/main.c +127 -0
  9. package/examples/ps1/platformer/main.c +158 -0
  10. package/examples/ps1/puzzle/main.c +125 -0
  11. package/examples/ps1/racing/main.c +147 -0
  12. package/examples/ps1/shmup/main.c +192 -0
  13. package/examples/ps1/sports/main.c +127 -0
  14. package/examples/ps1/sprite_move/main.c +38 -0
  15. package/package.json +11 -2
  16. package/src/analysis/analyze.js +224 -29
  17. package/src/analysis/decompile.js +7 -0
  18. package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
  19. package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
  20. package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
  21. package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
  22. package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
  23. package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
  24. package/src/analysis/rizin.js +24 -1
  25. package/src/cores/capabilities.js +122 -2
  26. package/src/cores/registry.js +17 -0
  27. package/src/host/LibretroGL.js +273 -0
  28. package/src/host/LibretroGLBridge.js +836 -0
  29. package/src/host/LibretroHost.js +203 -3
  30. package/src/host/callbacks.js +22 -2
  31. package/src/host/coreLoader.js +65 -5
  32. package/src/host/cpu-state.js +27 -0
  33. package/src/host/framebuffer.js +14 -1
  34. package/src/host/glOptionalDep.js +60 -0
  35. package/src/host/n64-ai-state.js +43 -0
  36. package/src/host/ps1-spu-state.js +65 -0
  37. package/src/host/retroConstants.js +14 -0
  38. package/src/mcp/tools/disasm.js +2 -0
  39. package/src/mcp/tools/frame.js +18 -9
  40. package/src/mcp/tools/lifecycle.js +1 -1
  41. package/src/mcp/tools/platform-tools.js +20 -4
  42. package/src/mcp/tools/platforms.js +38 -4
  43. package/src/mcp/tools/project.js +116 -0
  44. package/src/mcp/tools/rendering-context.js +2 -1
  45. package/src/mcp/tools/toolchain.js +1 -1
  46. package/src/platforms/n64/lib/c/n64.c +196 -0
  47. package/src/platforms/n64/lib/c/n64.h +68 -0
  48. package/src/platforms/ps1/lib/c/psx.c +200 -0
  49. package/src/platforms/ps1/lib/c/psx.h +83 -0
  50. package/src/toolchains/index.js +65 -0
  51. package/src/toolchains/mips-c/lib/be/libc.a +0 -0
  52. package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
  53. package/src/toolchains/mips-c/lib/be/libm.a +0 -0
  54. package/src/toolchains/mips-c/lib/el/libc.a +0 -0
  55. package/src/toolchains/mips-c/lib/el/libm.a +0 -0
  56. package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
  57. package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
  58. package/src/toolchains/mips-c/lib/n64.ld +15 -0
  59. package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
  60. package/src/toolchains/mips-c/lib/ps1.ld +15 -0
  61. package/src/toolchains/mips-c/lib/softint.c +37 -0
  62. package/src/toolchains/mips-c/mips-c.js +155 -0
  63. package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
  64. package/src/toolchains/sh-c/lib/dc-crt0.s +23 -0
  65. package/src/toolchains/sh-c/lib/dc.h +102 -0
  66. package/src/toolchains/sh-c/lib/dc.ld +11 -0
  67. package/src/toolchains/sh-c/lib/libc.a +0 -0
  68. package/src/toolchains/sh-c/lib/libgcc.a +0 -0
  69. package/src/toolchains/sh-c/lib/libm.a +0 -0
  70. package/src/toolchains/sh-c/sh-c.js +101 -0
  71. package/src/toolchains/sh-elf-gcc/gcc.js +122 -0
@@ -0,0 +1,836 @@
1
+ // LibretroGLBridge.js — Maps Emscripten GL imports to native-gles
2
+ //
3
+ // Emscripten-compiled libretro cores import GL functions as env.glClear,
4
+ // env.glBindFramebuffer, etc. (plus env.emscripten_glXxx for get_proc_address).
5
+ // These normally use GLctx (a WebGL context) which doesn't exist in Node.js.
6
+ //
7
+ // This module provides native-gles implementations that can be patched into
8
+ // the Emscripten env imports, using the same WASM memory marshaling patterns
9
+ // as wasmcart's gl_imports.js.
10
+
11
+ /* eslint-disable no-unused-vars -- GL bridge stubs mirror the libretro/Emscripten
12
+ GL ABI signatures verbatim; some params are unused but kept so each function
13
+ documents its real ABI (and so the port stays diff-able against upstream). */
14
+
15
+ // `native-gles` is injected by the caller (createEmscriptenGLBridge(getMemory, gl))
16
+ // rather than imported here — it's an OPTIONAL dependency loaded lazily by the host
17
+ // only when a HW-render core boots (see glOptionalDep.js / LibretroHost).
18
+ let gl = null;
19
+
20
+ const decoder = new TextDecoder();
21
+ const encoder = new TextEncoder();
22
+
23
+ function readCString(u8, ptr) {
24
+ let end = ptr;
25
+ while (u8[end] !== 0) end++;
26
+ return decoder.decode(u8.subarray(ptr, end));
27
+ }
28
+
29
+ // GL format/type → bytes per pixel
30
+ function _bytesPerPixel(format, type) {
31
+ const GL_ALPHA = 0x1906, GL_LUMINANCE = 0x1909, GL_RED = 0x1903, GL_DEPTH_COMPONENT = 0x1902;
32
+ const GL_LUMINANCE_ALPHA = 0x190A, GL_RG = 0x8227;
33
+ const GL_RGB = 0x1907, GL_RGBA = 0x1908;
34
+
35
+ let channels;
36
+ switch (format) {
37
+ case GL_ALPHA: case GL_LUMINANCE: case GL_RED: case GL_DEPTH_COMPONENT: channels = 1; break;
38
+ case GL_LUMINANCE_ALPHA: case GL_RG: channels = 2; break;
39
+ case GL_RGB: channels = 3; break;
40
+ case GL_RGBA: default: channels = 4; break;
41
+ }
42
+
43
+ switch (type) {
44
+ case 0x1401: return channels; // GL_UNSIGNED_BYTE
45
+ case 0x8363: case 0x8033: case 0x8034: return 2; // packed 16-bit
46
+ case 0x140B: case 0x1403: return channels * 2; // HALF_FLOAT, UNSIGNED_SHORT
47
+ case 0x1406: case 0x1405: return channels * 4; // FLOAT, UNSIGNED_INT
48
+ default: return channels;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Create GL function implementations for patching into Emscripten's env imports.
54
+ *
55
+ * @param {function} getMemory - returns the WASM Memory object
56
+ * @param {object} nativeGl - the loaded `native-gles` module (from glOptionalDep)
57
+ * @returns {object} map of function name → implementation
58
+ */
59
+ export function createEmscriptenGLBridge(getMemory, nativeGl) {
60
+ gl = nativeGl;
61
+ function u8() { return new Uint8Array(getMemory().buffer); }
62
+ function u32() { return new Uint32Array(getMemory().buffer); }
63
+ function i32() { return new Int32Array(getMemory().buffer); }
64
+ function f32() { return new Float32Array(getMemory().buffer); }
65
+
66
+ // String pool for glGetString results (allocated at top of WASM memory)
67
+ let _glStringCache = {};
68
+ let _glStringPool = null;
69
+
70
+ function _allocStringInPool(str) {
71
+ if (!_glStringPool) {
72
+ const mem = getMemory();
73
+ _glStringPool = { base: mem.buffer.byteLength - 16384, offset: 0 };
74
+ }
75
+ const encoded = encoder.encode(str);
76
+ const ptr = _glStringPool.base + _glStringPool.offset;
77
+ const mem = u8();
78
+ if (ptr + encoded.length + 1 < mem.length) {
79
+ mem.set(encoded, ptr);
80
+ mem[ptr + encoded.length] = 0;
81
+ _glStringPool.offset += encoded.length + 1;
82
+ _glStringPool.offset = (_glStringPool.offset + 3) & ~3;
83
+ return ptr;
84
+ }
85
+ return 0;
86
+ }
87
+
88
+ // Gen/Delete helper
89
+ function genObjects(n, ptr, genFn) {
90
+ const arr = new Uint32Array(n);
91
+ genFn(n, arr);
92
+ const view = u32();
93
+ for (let i = 0; i < n; i++) view[(ptr >> 2) + i] = arr[i];
94
+ }
95
+ function deleteObjects(n, ptr, delFn) {
96
+ const view = u32();
97
+ const arr = new Uint32Array(n);
98
+ for (let i = 0; i < n; i++) arr[i] = view[(ptr >> 2) + i];
99
+ delFn(n, arr);
100
+ }
101
+
102
+ // Write info log to WASM memory
103
+ function writeInfoLog(log, bufSize, lengthPtr, infoLogPtr) {
104
+ const mem = u8();
105
+ const encoded = encoder.encode(log || '');
106
+ const copyLen = Math.min(encoded.length, bufSize - 1);
107
+ mem.set(encoded.subarray(0, copyLen), infoLogPtr);
108
+ mem[infoLogPtr + copyLen] = 0;
109
+ if (lengthPtr) u32()[lengthPtr >> 2] = copyLen;
110
+ }
111
+
112
+ const funcs = {
113
+ // ─── State ────────────────────────────────────────────────────────
114
+ glEnable: (cap) => gl.glEnable(cap),
115
+ glDisable: (cap) => gl.glDisable(cap),
116
+ glGetError: () => gl.glGetError(),
117
+ glFinish: () => gl.glFinish(),
118
+ glFlush: () => gl.glFlush(),
119
+ glHint: (target, mode) => gl.glHint(target, mode),
120
+ glPixelStorei: (pname, param) => gl.glPixelStorei(pname, param),
121
+ glIsEnabled: (cap) => gl.glIsEnabled(cap),
122
+
123
+ glGetIntegerv: (pname, paramsPtr) => {
124
+ const view = new Int32Array(getMemory().buffer, paramsPtr, 4);
125
+ gl.glGetIntegerv(pname, view);
126
+ },
127
+ glGetFloatv: (pname, dataPtr) => {
128
+ const view = new Float32Array(getMemory().buffer, dataPtr, 16);
129
+ gl.glGetFloatv(pname, view);
130
+ },
131
+ glGetBooleanv: (pname, dataPtr) => {
132
+ const val = gl.glGetBooleanv(pname);
133
+ u8()[dataPtr] = val ? 1 : 0;
134
+ },
135
+ glGetString: (name) => {
136
+ if (_glStringCache[name] !== undefined) return _glStringCache[name];
137
+ const str = gl.glGetString(name);
138
+ if (!str) { _glStringCache[name] = 0; return 0; }
139
+ const ptr = _allocStringInPool(str);
140
+ _glStringCache[name] = ptr;
141
+ return ptr;
142
+ },
143
+ glGetStringi: (name, index) => {
144
+ const key = `${name}_${index}`;
145
+ if (_glStringCache[key] !== undefined) return _glStringCache[key];
146
+ const str = gl.glGetStringi(name, index);
147
+ if (!str) { _glStringCache[key] = 0; return 0; }
148
+ const ptr = _allocStringInPool(str);
149
+ _glStringCache[key] = ptr;
150
+ return ptr;
151
+ },
152
+
153
+ // ─── Viewport / Clear ────────────────────────────────────────────
154
+ glViewport: (x, y, w, h) => gl.glViewport(x, y, w, h),
155
+ glScissor: (x, y, w, h) => gl.glScissor(x, y, w, h),
156
+ glClear: (mask) => gl.glClear(mask),
157
+ glClearColor: (r, g, b, a) => gl.glClearColor(r, g, b, a),
158
+ glClearDepthf: (d) => gl.glClearDepthf(d),
159
+ glClearStencil: (s) => gl.glClearStencil(s),
160
+
161
+ // ─── Blending ────────────────────────────────────────────────────
162
+ glBlendFunc: (sfactor, dfactor) => gl.glBlendFunc(sfactor, dfactor),
163
+ glBlendFuncSeparate: (srcRGB, dstRGB, srcA, dstA) => gl.glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA),
164
+ glBlendEquation: (mode) => gl.glBlendEquation(mode),
165
+ glBlendEquationSeparate: (modeRGB, modeA) => gl.glBlendEquationSeparate(modeRGB, modeA),
166
+ glBlendColor: (r, g, b, a) => gl.glBlendColor(r, g, b, a),
167
+ glColorMask: (r, g, b, a) => gl.glColorMask(!!r, !!g, !!b, !!a),
168
+
169
+ // ─── Depth / Stencil ─────────────────────────────────────────────
170
+ glDepthFunc: (func) => gl.glDepthFunc(func),
171
+ glDepthMask: (flag) => gl.glDepthMask(!!flag),
172
+ glDepthRangef: (n, f) => gl.glDepthRangef(n, f),
173
+ glStencilFunc: (func, ref, mask) => gl.glStencilFunc(func, ref, mask),
174
+ glStencilFuncSeparate: (face, func, ref, mask) => gl.glStencilFuncSeparate(face, func, ref, mask),
175
+ glStencilOp: (fail, zfail, zpass) => gl.glStencilOp(fail, zfail, zpass),
176
+ glStencilOpSeparate: (face, sfail, dpfail, dppass) => gl.glStencilOpSeparate(face, sfail, dpfail, dppass),
177
+ glStencilMask: (mask) => gl.glStencilMask(mask),
178
+ glStencilMaskSeparate: (face, mask) => gl.glStencilMaskSeparate(face, mask),
179
+
180
+ // ─── Face culling ────────────────────────────────────────────────
181
+ glCullFace: (mode) => gl.glCullFace(mode),
182
+ glFrontFace: (mode) => gl.glFrontFace(mode),
183
+ glPolygonOffset: (factor, units) => gl.glPolygonOffset(factor, units),
184
+ glLineWidth: (width) => gl.glLineWidth(width),
185
+ glSampleCoverage: (value, invert) => gl.glSampleCoverage(value, !!invert),
186
+
187
+ // ─── Buffers ─────────────────────────────────────────────────────
188
+ glGenBuffers: (n, ptr) => genObjects(n, ptr, gl.glGenBuffers.bind(gl)),
189
+ glDeleteBuffers: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteBuffers.bind(gl)),
190
+ glBindBuffer: (target, buffer) => gl.glBindBuffer(target, buffer),
191
+ glBufferData: (target, size, dataPtr, usage) => {
192
+ if (dataPtr) {
193
+ gl.glBufferData(target, u8().subarray(dataPtr, dataPtr + size), usage);
194
+ } else {
195
+ // Allocate without data — pass a zeroed buffer
196
+ gl.glBufferData(target, new Uint8Array(size), usage);
197
+ }
198
+ },
199
+ glBufferSubData: (target, offset, size, dataPtr) => {
200
+ gl.glBufferSubData(target, offset, u8().subarray(dataPtr, dataPtr + size));
201
+ },
202
+ glCopyBufferSubData: (readTarget, writeTarget, readOffset, writeOffset, size) => {
203
+ gl.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
204
+ },
205
+ glIsBuffer: (buffer) => gl.glIsBuffer(buffer),
206
+
207
+ // ─── Textures ────────────────────────────────────────────────────
208
+ glGenTextures: (n, ptr) => genObjects(n, ptr, gl.glGenTextures.bind(gl)),
209
+ glDeleteTextures: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteTextures.bind(gl)),
210
+ glBindTexture: (target, texture) => gl.glBindTexture(target, texture),
211
+ glActiveTexture: (texture) => gl.glActiveTexture(texture),
212
+ glTexImage2D: (target, level, internalformat, width, height, border, format, type, pixelsPtr) => {
213
+ if (pixelsPtr === 0) {
214
+ gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, null);
215
+ } else {
216
+ const bpp = _bytesPerPixel(format, type);
217
+ const size = width * height * bpp;
218
+ gl.glTexImage2D(target, level, internalformat, width, height, border, format, type,
219
+ u8().subarray(pixelsPtr, pixelsPtr + size));
220
+ }
221
+ },
222
+ glTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, pixelsPtr) => {
223
+ const bpp = _bytesPerPixel(format, type);
224
+ const size = width * height * bpp;
225
+ gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
226
+ u8().subarray(pixelsPtr, pixelsPtr + size));
227
+ },
228
+ glTexParameteri: (target, pname, param) => gl.glTexParameteri(target, pname, param),
229
+ glTexParameterf: (target, pname, param) => gl.glTexParameterf(target, pname, param),
230
+ glTexParameteriv: (target, pname, paramsPtr) => {
231
+ gl.glTexParameteriv(target, pname, new Int32Array(getMemory().buffer, paramsPtr, 4));
232
+ },
233
+ glGenerateMipmap: (target) => gl.glGenerateMipmap(target),
234
+ glCompressedTexImage2D: (target, level, internalformat, width, height, border, imageSize, dataPtr) => {
235
+ gl.glCompressedTexImage2D(target, level, internalformat, width, height, border,
236
+ u8().subarray(dataPtr, dataPtr + imageSize));
237
+ },
238
+ glCompressedTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, imageSize, dataPtr) => {
239
+ gl.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format,
240
+ u8().subarray(dataPtr, dataPtr + imageSize));
241
+ },
242
+ glTexImage3D: (target, level, internalformat, width, height, depth, border, format, type, pixelsPtr) => {
243
+ if (pixelsPtr === 0) {
244
+ gl.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, null);
245
+ } else {
246
+ const bpp = _bytesPerPixel(format, type);
247
+ const size = width * height * depth * bpp;
248
+ gl.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
249
+ u8().subarray(pixelsPtr, pixelsPtr + size));
250
+ }
251
+ },
252
+ glTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixelsPtr) => {
253
+ const bpp = _bytesPerPixel(format, type);
254
+ const size = width * height * depth * bpp;
255
+ gl.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type,
256
+ u8().subarray(pixelsPtr, pixelsPtr + size));
257
+ },
258
+ glTexStorage2D: (target, levels, internalformat, width, height) => {
259
+ gl.glTexStorage2D(target, levels, internalformat, width, height);
260
+ },
261
+ glTexStorage3D: (target, levels, internalformat, width, height, depth) => {
262
+ gl.glTexStorage3D(target, levels, internalformat, width, height, depth);
263
+ },
264
+ glCopyTexImage2D: (target, level, internalformat, x, y, width, height, border) => {
265
+ gl.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
266
+ },
267
+ glCopyTexSubImage2D: (target, level, xoffset, yoffset, x, y, width, height) => {
268
+ gl.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
269
+ },
270
+ glCopyTexSubImage3D: (target, level, xoffset, yoffset, zoffset, x, y, width, height) => {
271
+ gl.glCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
272
+ },
273
+ glCompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, dataPtr) => {
274
+ gl.glCompressedTexImage3D(target, level, internalformat, width, height, depth, border,
275
+ u8().subarray(dataPtr, dataPtr + imageSize));
276
+ },
277
+ glCompressedTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, dataPtr) => {
278
+ gl.glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format,
279
+ u8().subarray(dataPtr, dataPtr + imageSize));
280
+ },
281
+ glIsTexture: (texture) => gl.glIsTexture(texture),
282
+ glGetTexParameteriv: (target, pname, paramsPtr) => {
283
+ const buf = Buffer.alloc(4);
284
+ gl.glGetTexParameteriv(target, pname, buf);
285
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
286
+ },
287
+ glGetTexParameterfv: (target, pname, paramsPtr) => {
288
+ const buf = Buffer.alloc(4);
289
+ gl.glGetTexParameterfv(target, pname, buf);
290
+ const view = new DataView(getMemory().buffer);
291
+ view.setFloat32(paramsPtr, buf.readFloatLE(0), true);
292
+ },
293
+
294
+ // ─── Shaders ─────────────────────────────────────────────────────
295
+ glCreateShader: (type) => gl.glCreateShader(type),
296
+ glDeleteShader: (shader) => gl.glDeleteShader(shader),
297
+ glIsShader: (shader) => gl.glIsShader(shader),
298
+ glShaderSource: (shader, count, stringsPtr, lengthsPtr) => {
299
+ const mem = u8();
300
+ const ptrs = u32();
301
+ const lens = lengthsPtr ? i32() : null;
302
+ let fullSource = '';
303
+ for (let i = 0; i < count; i++) {
304
+ const strPtr = ptrs[(stringsPtr >> 2) + i];
305
+ if (lens && lens[(lengthsPtr >> 2) + i] > 0) {
306
+ const len = lens[(lengthsPtr >> 2) + i];
307
+ fullSource += decoder.decode(mem.subarray(strPtr, strPtr + len));
308
+ } else {
309
+ fullSource += readCString(mem, strPtr);
310
+ }
311
+ }
312
+ gl.glShaderSource(shader, fullSource);
313
+ },
314
+ glCompileShader: (shader) => gl.glCompileShader(shader),
315
+ glGetShaderiv: (shader, pname, paramsPtr) => {
316
+ const result = gl.glGetShaderiv(shader, pname);
317
+ if (paramsPtr) i32()[paramsPtr >> 2] = result;
318
+ return result;
319
+ },
320
+ glGetShaderInfoLog: (shader, bufSize, lengthPtr, infoLogPtr) => {
321
+ writeInfoLog(gl.glGetShaderInfoLog(shader), bufSize, lengthPtr, infoLogPtr);
322
+ },
323
+ glGetShaderSource: (shader, bufSize, lengthPtr, sourcePtr) => {
324
+ writeInfoLog(gl.glGetShaderSource(shader), bufSize, lengthPtr, sourcePtr);
325
+ },
326
+ glShaderBinary: (count, shadersPtr, binaryFormat, binaryPtr, length) => {
327
+ // Usually not supported on GLES — no-op
328
+ },
329
+ glReleaseShaderCompiler: () => {},
330
+ glGetShaderPrecisionFormat: (shadertype, precisiontype, rangePtr, precisionPtr) => {
331
+ // Return reasonable defaults for GLES2/3
332
+ const view = i32();
333
+ if (rangePtr) { view[rangePtr >> 2] = 127; view[(rangePtr >> 2) + 1] = 127; }
334
+ if (precisionPtr) view[precisionPtr >> 2] = 23;
335
+ },
336
+
337
+ // ─── Programs ────────────────────────────────────────────────────
338
+ glCreateProgram: () => gl.glCreateProgram(),
339
+ glDeleteProgram: (program) => gl.glDeleteProgram(program),
340
+ glIsProgram: (program) => gl.glIsProgram(program),
341
+ glAttachShader: (program, shader) => gl.glAttachShader(program, shader),
342
+ glDetachShader: (program, shader) => gl.glDetachShader(program, shader),
343
+ glLinkProgram: (program) => gl.glLinkProgram(program),
344
+ glUseProgram: (program) => gl.glUseProgram(program),
345
+ glValidateProgram: (program) => gl.glValidateProgram(program),
346
+ glGetProgramiv: (program, pname, paramsPtr) => {
347
+ const result = gl.glGetProgramiv(program, pname);
348
+ if (paramsPtr) i32()[paramsPtr >> 2] = result;
349
+ return result;
350
+ },
351
+ glGetProgramInfoLog: (program, bufSize, lengthPtr, infoLogPtr) => {
352
+ writeInfoLog(gl.glGetProgramInfoLog(program), bufSize, lengthPtr, infoLogPtr);
353
+ },
354
+ glBindAttribLocation: (program, index, namePtr) => {
355
+ gl.glBindAttribLocation(program, index, readCString(u8(), namePtr));
356
+ },
357
+ glGetAttribLocation: (program, namePtr) => {
358
+ return gl.glGetAttribLocation(program, readCString(u8(), namePtr));
359
+ },
360
+ glGetUniformLocation: (program, namePtr) => {
361
+ return gl.glGetUniformLocation(program, readCString(u8(), namePtr));
362
+ },
363
+ glGetActiveAttrib: (program, index, bufSize, lengthPtr, sizePtr, typePtr, namePtr) => {
364
+ const result = gl.glGetActiveAttrib(program, index, bufSize);
365
+ const mem = u8();
366
+ if (result && result.name) {
367
+ const encoded = encoder.encode(result.name);
368
+ const copyLen = Math.min(encoded.length, bufSize - 1);
369
+ mem.set(encoded.subarray(0, copyLen), namePtr);
370
+ mem[namePtr + copyLen] = 0;
371
+ if (lengthPtr) u32()[lengthPtr >> 2] = copyLen;
372
+ if (sizePtr) i32()[sizePtr >> 2] = result.size;
373
+ if (typePtr) u32()[typePtr >> 2] = result.type;
374
+ } else {
375
+ if (lengthPtr) u32()[lengthPtr >> 2] = 0;
376
+ if (namePtr) mem[namePtr] = 0;
377
+ }
378
+ },
379
+ glGetActiveUniform: (program, index, bufSize, lengthPtr, sizePtr, typePtr, namePtr) => {
380
+ const result = gl.glGetActiveUniform(program, index, bufSize);
381
+ const mem = u8();
382
+ if (result && result.name) {
383
+ const encoded = encoder.encode(result.name);
384
+ const copyLen = Math.min(encoded.length, bufSize - 1);
385
+ mem.set(encoded.subarray(0, copyLen), namePtr);
386
+ mem[namePtr + copyLen] = 0;
387
+ if (lengthPtr) u32()[lengthPtr >> 2] = copyLen;
388
+ if (sizePtr) i32()[sizePtr >> 2] = result.size;
389
+ if (typePtr) u32()[typePtr >> 2] = result.type;
390
+ } else {
391
+ if (lengthPtr) u32()[lengthPtr >> 2] = 0;
392
+ if (namePtr) mem[namePtr] = 0;
393
+ }
394
+ },
395
+ glGetAttachedShaders: (program, maxCount, countPtr, shadersPtr) => {
396
+ // Rarely used — stub with empty result
397
+ if (countPtr) i32()[countPtr >> 2] = 0;
398
+ },
399
+ glGetProgramBinary: (program, bufSize, lengthPtr, binaryFormatPtr, binaryPtr) => {
400
+ // Not critical — stub
401
+ if (lengthPtr) i32()[lengthPtr >> 2] = 0;
402
+ },
403
+ glProgramBinary: (program, binaryFormat, binaryPtr, length) => {
404
+ const data = u8().subarray(binaryPtr, binaryPtr + length);
405
+ gl.glProgramBinary(program, binaryFormat, data, length);
406
+ },
407
+ glProgramParameteri: (program, pname, value) => {
408
+ // GL_PROGRAM_BINARY_RETRIEVABLE_HINT — no-op is fine
409
+ },
410
+
411
+ // ─── Uniforms ────────────────────────────────────────────────────
412
+ glUniform1i: (loc, v0) => gl.glUniform1i(loc, v0),
413
+ glUniform2i: (loc, v0, v1) => gl.glUniform2i(loc, v0, v1),
414
+ glUniform3i: (loc, v0, v1, v2) => gl.glUniform3i(loc, v0, v1, v2),
415
+ glUniform4i: (loc, v0, v1, v2, v3) => gl.glUniform4i(loc, v0, v1, v2, v3),
416
+ glUniform1f: (loc, v0) => gl.glUniform1f(loc, v0),
417
+ glUniform2f: (loc, v0, v1) => gl.glUniform2f(loc, v0, v1),
418
+ glUniform3f: (loc, v0, v1, v2) => gl.glUniform3f(loc, v0, v1, v2),
419
+ glUniform4f: (loc, v0, v1, v2, v3) => gl.glUniform4f(loc, v0, v1, v2, v3),
420
+ glUniform1ui: (loc, v0) => gl.glUniform1ui(loc, v0),
421
+ glUniform1iv: (loc, count, ptr) => gl.glUniform1iv(loc, new Int32Array(getMemory().buffer, ptr, count)),
422
+ glUniform2iv: (loc, count, ptr) => gl.glUniform2iv(loc, new Int32Array(getMemory().buffer, ptr, count * 2)),
423
+ glUniform3iv: (loc, count, ptr) => gl.glUniform3iv(loc, new Int32Array(getMemory().buffer, ptr, count * 3)),
424
+ glUniform4iv: (loc, count, ptr) => gl.glUniform4iv(loc, new Int32Array(getMemory().buffer, ptr, count * 4)),
425
+ glUniform1fv: (loc, count, ptr) => gl.glUniform1fv(loc, new Float32Array(getMemory().buffer, ptr, count)),
426
+ glUniform2fv: (loc, count, ptr) => gl.glUniform2fv(loc, new Float32Array(getMemory().buffer, ptr, count * 2)),
427
+ glUniform3fv: (loc, count, ptr) => gl.glUniform3fv(loc, new Float32Array(getMemory().buffer, ptr, count * 3)),
428
+ glUniform4fv: (loc, count, ptr) => gl.glUniform4fv(loc, new Float32Array(getMemory().buffer, ptr, count * 4)),
429
+ glUniform1uiv: (loc, count, ptr) => gl.glUniform1uiv(loc, new Uint32Array(getMemory().buffer, ptr, count)),
430
+ glUniform2uiv: (loc, count, ptr) => gl.glUniform2uiv(loc, new Uint32Array(getMemory().buffer, ptr, count * 2)),
431
+ glUniform3uiv: (loc, count, ptr) => gl.glUniform3uiv(loc, new Uint32Array(getMemory().buffer, ptr, count * 3)),
432
+ glUniform4uiv: (loc, count, ptr) => gl.glUniform4uiv(loc, new Uint32Array(getMemory().buffer, ptr, count * 4)),
433
+ glUniformMatrix2fv: (loc, count, transpose, ptr) => gl.glUniformMatrix2fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 4)),
434
+ glUniformMatrix3fv: (loc, count, transpose, ptr) => gl.glUniformMatrix3fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 9)),
435
+ glUniformMatrix4fv: (loc, count, transpose, ptr) => gl.glUniformMatrix4fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 16)),
436
+ glUniformMatrix2x3fv: (loc, count, transpose, ptr) => gl.glUniformMatrix2x3fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 6)),
437
+ glUniformMatrix3x2fv: (loc, count, transpose, ptr) => gl.glUniformMatrix3x2fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 6)),
438
+ glUniformMatrix2x4fv: (loc, count, transpose, ptr) => gl.glUniformMatrix2x4fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 8)),
439
+ glUniformMatrix4x2fv: (loc, count, transpose, ptr) => gl.glUniformMatrix4x2fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 8)),
440
+ glUniformMatrix3x4fv: (loc, count, transpose, ptr) => gl.glUniformMatrix3x4fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 12)),
441
+ glUniformMatrix4x3fv: (loc, count, transpose, ptr) => gl.glUniformMatrix4x3fv(loc, !!transpose, new Float32Array(getMemory().buffer, ptr, count * 12)),
442
+
443
+ // UBO
444
+ glGetUniformBlockIndex: (program, namePtr) => gl.glGetUniformBlockIndex(program, readCString(u8(), namePtr)),
445
+ glUniformBlockBinding: (program, index, binding) => gl.glUniformBlockBinding(program, index, binding),
446
+ glGetActiveUniformBlockiv: (program, index, pname, paramsPtr) => {
447
+ const view = new Int32Array(getMemory().buffer, paramsPtr, 4);
448
+ gl.glGetActiveUniformBlockiv(program, index, pname, view);
449
+ },
450
+ glGetActiveUniformBlockName: (program, index, bufSize, lengthPtr, namePtr) => {
451
+ const result = gl.glGetActiveUniformBlockName(program, index, bufSize);
452
+ writeInfoLog(result, bufSize, lengthPtr, namePtr);
453
+ },
454
+ glGetActiveUniformsiv: (program, uniformCount, uniformIndicesPtr, pname, paramsPtr) => {
455
+ // Stub — rarely used
456
+ for (let i = 0; i < uniformCount; i++) i32()[(paramsPtr >> 2) + i] = 0;
457
+ },
458
+ glGetUniformIndices: (program, uniformCount, uniformNamesPtr, uniformIndicesPtr) => {
459
+ // Stub
460
+ for (let i = 0; i < uniformCount; i++) u32()[(uniformIndicesPtr >> 2) + i] = 0xFFFFFFFF;
461
+ },
462
+ glGetFragDataLocation: (program, namePtr) => {
463
+ return gl.glGetFragDataLocation(program, readCString(u8(), namePtr));
464
+ },
465
+
466
+ // ─── Vertex attribs ──────────────────────────────────────────────
467
+ glEnableVertexAttribArray: (index) => gl.glEnableVertexAttribArray(index),
468
+ glDisableVertexAttribArray: (index) => gl.glDisableVertexAttribArray(index),
469
+ glVertexAttribPointer: (index, size, type, normalized, stride, offset) => {
470
+ gl.glVertexAttribPointer(index, size, type, !!normalized, stride, offset);
471
+ },
472
+ glVertexAttribIPointer: (index, size, type, stride, offset) => {
473
+ gl.glVertexAttribIPointer(index, size, type, stride, offset);
474
+ },
475
+ glVertexAttribDivisor: (index, divisor) => gl.glVertexAttribDivisor(index, divisor),
476
+ glVertexAttrib1f: (index, x) => gl.glVertexAttrib1f(index, x),
477
+ glVertexAttrib1fv: (index, vPtr) => gl.glVertexAttrib1fv(index, new Float32Array(getMemory().buffer, vPtr, 1)),
478
+ glVertexAttrib2f: (index, x, y) => gl.glVertexAttrib2f(index, x, y),
479
+ glVertexAttrib2fv: (index, vPtr) => gl.glVertexAttrib2fv(index, new Float32Array(getMemory().buffer, vPtr, 2)),
480
+ glVertexAttrib3f: (index, x, y, z) => gl.glVertexAttrib3f(index, x, y, z),
481
+ glVertexAttrib3fv: (index, vPtr) => gl.glVertexAttrib3fv(index, new Float32Array(getMemory().buffer, vPtr, 3)),
482
+ glVertexAttrib4f: (index, x, y, z, w) => gl.glVertexAttrib4f(index, x, y, z, w),
483
+ glVertexAttrib4fv: (index, vPtr) => gl.glVertexAttrib4fv(index, new Float32Array(getMemory().buffer, vPtr, 4)),
484
+ glVertexAttribI4i: (index, x, y, z, w) => gl.glVertexAttribI4i(index, x, y, z, w),
485
+ glVertexAttribI4ui: (index, x, y, z, w) => gl.glVertexAttribI4ui(index, x, y, z, w),
486
+ glVertexAttribI4iv: (index, vPtr) => gl.glVertexAttribI4iv(index, new Int32Array(getMemory().buffer, vPtr, 4)),
487
+ glVertexAttribI4uiv: (index, vPtr) => gl.glVertexAttribI4uiv(index, new Uint32Array(getMemory().buffer, vPtr, 4)),
488
+ glGetVertexAttribiv: (index, pname, paramsPtr) => {
489
+ const buf = Buffer.alloc(4);
490
+ gl.glGetVertexAttribiv(index, pname, buf);
491
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
492
+ },
493
+ glGetVertexAttribfv: (index, pname, paramsPtr) => {
494
+ const buf = Buffer.alloc(4);
495
+ gl.glGetVertexAttribfv(index, pname, buf);
496
+ new DataView(getMemory().buffer).setFloat32(paramsPtr, buf.readFloatLE(0), true);
497
+ },
498
+ glGetVertexAttribPointerv: (index, pname, pointerPtr) => {
499
+ const buf = Buffer.alloc(4);
500
+ gl.glGetVertexAttribPointerv(index, pname, buf);
501
+ u32()[pointerPtr >> 2] = buf.readUInt32LE(0);
502
+ },
503
+ glGetVertexAttribIiv: (index, pname, paramsPtr) => {
504
+ const buf = Buffer.alloc(4);
505
+ gl.glGetVertexAttribIiv(index, pname, buf);
506
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
507
+ },
508
+ glGetVertexAttribIuiv: (index, pname, paramsPtr) => {
509
+ const buf = Buffer.alloc(4);
510
+ gl.glGetVertexAttribIuiv(index, pname, buf);
511
+ u32()[paramsPtr >> 2] = buf.readUInt32LE(0);
512
+ },
513
+
514
+ // ─── Drawing ─────────────────────────────────────────────────────
515
+ glDrawArrays: (mode, first, count) => gl.glDrawArrays(mode, first, count),
516
+ glDrawElements: (mode, count, type, offset) => gl.glDrawElements(mode, count, type, offset),
517
+ glDrawArraysInstanced: (mode, first, count, instancecount) => gl.glDrawArraysInstanced(mode, first, count, instancecount),
518
+ glDrawElementsInstanced: (mode, count, type, offset, instancecount) => gl.glDrawElementsInstanced(mode, count, type, offset, instancecount),
519
+ glDrawRangeElements: (mode, start, end, count, type, offset) => gl.glDrawRangeElements(mode, start, end, count, type, offset),
520
+ glDrawBuffers: (n, bufsPtr) => {
521
+ const arr = new Uint32Array(n);
522
+ const view = u32();
523
+ for (let i = 0; i < n; i++) arr[i] = view[(bufsPtr >> 2) + i];
524
+ gl.glDrawBuffers(arr);
525
+ },
526
+
527
+ // ─── FBOs ────────────────────────────────────────────────────────
528
+ glGenFramebuffers: (n, ptr) => genObjects(n, ptr, gl.glGenFramebuffers.bind(gl)),
529
+ glDeleteFramebuffers: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteFramebuffers.bind(gl)),
530
+ glBindFramebuffer: (target, framebuffer) => gl.glBindFramebuffer(target, framebuffer),
531
+ glCheckFramebufferStatus: (target) => gl.glCheckFramebufferStatus(target),
532
+ glFramebufferTexture2D: (target, attachment, textarget, texture, level) => {
533
+ gl.glFramebufferTexture2D(target, attachment, textarget, texture, level);
534
+ },
535
+ glFramebufferRenderbuffer: (target, attachment, renderbuffertarget, renderbuffer) => {
536
+ gl.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
537
+ },
538
+ glFramebufferTextureLayer: (target, attachment, texture, level, layer) => {
539
+ gl.glFramebufferTextureLayer(target, attachment, texture, level, layer);
540
+ },
541
+ glBlitFramebuffer: (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) => {
542
+ gl.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
543
+ },
544
+ glInvalidateFramebuffer: (target, numAttachments, attachmentsPtr) => {
545
+ const arr = new Uint32Array(numAttachments);
546
+ const view = u32();
547
+ for (let i = 0; i < numAttachments; i++) arr[i] = view[(attachmentsPtr >> 2) + i];
548
+ gl.glInvalidateFramebuffer(target, arr);
549
+ },
550
+ glInvalidateSubFramebuffer: (target, numAttachments, attachmentsPtr, x, y, width, height) => {
551
+ // Stub — optimization hint only
552
+ },
553
+ glIsFramebuffer: (framebuffer) => gl.glIsFramebuffer(framebuffer),
554
+ glGetFramebufferAttachmentParameteriv: (target, attachment, pname, paramsPtr) => {
555
+ const buf = Buffer.alloc(4);
556
+ gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, buf);
557
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
558
+ },
559
+ glReadBuffer: (mode) => gl.glReadBuffer(mode),
560
+
561
+ // ─── RBOs ────────────────────────────────────────────────────────
562
+ glGenRenderbuffers: (n, ptr) => genObjects(n, ptr, gl.glGenRenderbuffers.bind(gl)),
563
+ glDeleteRenderbuffers: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteRenderbuffers.bind(gl)),
564
+ glBindRenderbuffer: (target, renderbuffer) => gl.glBindRenderbuffer(target, renderbuffer),
565
+ glRenderbufferStorage: (target, internalformat, width, height) => {
566
+ gl.glRenderbufferStorage(target, internalformat, width, height);
567
+ },
568
+ glRenderbufferStorageMultisample: (target, samples, internalformat, width, height) => {
569
+ gl.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
570
+ },
571
+ glIsRenderbuffer: (renderbuffer) => gl.glIsRenderbuffer(renderbuffer),
572
+ glGetRenderbufferParameteriv: (target, pname, paramsPtr) => {
573
+ const buf = Buffer.alloc(4);
574
+ gl.glGetRenderbufferParameteriv(target, pname, buf);
575
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
576
+ },
577
+ glGetInternalformativ: (target, internalformat, pname, bufSize, paramsPtr) => {
578
+ // Return reasonable defaults
579
+ i32()[paramsPtr >> 2] = 4; // at least 4 samples
580
+ },
581
+
582
+ // ─── Readback ────────────────────────────────────────────────────
583
+ glReadPixels: (x, y, width, height, format, type, pixelsPtr) => {
584
+ const bpp = _bytesPerPixel(format, type);
585
+ const size = width * height * bpp;
586
+ const buf = u8().subarray(pixelsPtr, pixelsPtr + size);
587
+ gl.glReadPixels(x, y, width, height, format, type, buf);
588
+ },
589
+
590
+ // ─── VAO ─────────────────────────────────────────────────────────
591
+ glGenVertexArrays: (n, ptr) => genObjects(n, ptr, gl.glGenVertexArrays.bind(gl)),
592
+ glDeleteVertexArrays: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteVertexArrays.bind(gl)),
593
+ glBindVertexArray: (array) => gl.glBindVertexArray(array),
594
+ glIsVertexArray: (array) => gl.glIsVertexArray(array),
595
+
596
+ // ─── Queries ─────────────────────────────────────────────────────
597
+ glGenQueries: (n, ptr) => genObjects(n, ptr, gl.glGenQueries.bind(gl)),
598
+ glDeleteQueries: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteQueries.bind(gl)),
599
+ glBeginQuery: (target, id) => gl.glBeginQuery(target, id),
600
+ glEndQuery: (target) => gl.glEndQuery(target),
601
+ glIsQuery: (id) => gl.glIsQuery(id),
602
+ glGetQueryiv: (target, pname, paramsPtr) => {
603
+ const buf = Buffer.alloc(4);
604
+ gl.glGetQueryiv(target, pname, buf);
605
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
606
+ },
607
+ glGetQueryObjectuiv: (id, pname, paramsPtr) => {
608
+ const result = gl.glGetQueryObjectuiv(id, pname);
609
+ u32()[paramsPtr >> 2] = result;
610
+ },
611
+ glGetBufferParameteriv: (target, pname, paramsPtr) => {
612
+ const buf = Buffer.alloc(4);
613
+ gl.glGetBufferParameteriv(target, pname, buf);
614
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
615
+ },
616
+ glGetBufferParameteri64v: (target, pname, paramsPtr) => {
617
+ // Return buffer param as i64
618
+ const buf = Buffer.alloc(4);
619
+ gl.glGetBufferParameteriv(target, pname, buf);
620
+ const view = new DataView(getMemory().buffer);
621
+ view.setBigInt64(paramsPtr, BigInt(buf.readInt32LE(0)), true);
622
+ },
623
+
624
+ // ─── Samplers ────────────────────────────────────────────────────
625
+ glGenSamplers: (n, ptr) => genObjects(n, ptr, gl.glGenSamplers.bind(gl)),
626
+ glDeleteSamplers: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteSamplers.bind(gl)),
627
+ glBindSampler: (unit, sampler) => gl.glBindSampler(unit, sampler),
628
+ glSamplerParameteri: (sampler, pname, param) => gl.glSamplerParameteri(sampler, pname, param),
629
+ glSamplerParameterf: (sampler, pname, param) => gl.glSamplerParameterf(sampler, pname, param),
630
+ glSamplerParameteriv: (sampler, pname, paramsPtr) => {
631
+ gl.glSamplerParameteriv(sampler, pname, new Int32Array(getMemory().buffer, paramsPtr, 4));
632
+ },
633
+ glSamplerParameterfv: (sampler, pname, paramsPtr) => {
634
+ gl.glSamplerParameterfv(sampler, pname, new Float32Array(getMemory().buffer, paramsPtr, 4));
635
+ },
636
+ glGetSamplerParameteriv: (sampler, pname, paramsPtr) => {
637
+ const buf = Buffer.alloc(4);
638
+ gl.glGetSamplerParameteriv(sampler, pname, buf);
639
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
640
+ },
641
+ glGetSamplerParameterfv: (sampler, pname, paramsPtr) => {
642
+ const buf = Buffer.alloc(4);
643
+ gl.glGetSamplerParameterfv(sampler, pname, buf);
644
+ new DataView(getMemory().buffer).setFloat32(paramsPtr, buf.readFloatLE(0), true);
645
+ },
646
+ glIsSampler: (sampler) => gl.glIsSampler(sampler),
647
+
648
+ // ─── Transform feedback ──────────────────────────────────────────
649
+ glGenTransformFeedbacks: (n, ptr) => genObjects(n, ptr, gl.glGenTransformFeedbacks.bind(gl)),
650
+ glDeleteTransformFeedbacks: (n, ptr) => deleteObjects(n, ptr, gl.glDeleteTransformFeedbacks.bind(gl)),
651
+ glBindTransformFeedback: (target, id) => gl.glBindTransformFeedback(target, id),
652
+ glBeginTransformFeedback: (primitiveMode) => gl.glBeginTransformFeedback(primitiveMode),
653
+ glEndTransformFeedback: () => gl.glEndTransformFeedback(),
654
+ glPauseTransformFeedback: () => gl.glPauseTransformFeedback(),
655
+ glResumeTransformFeedback: () => gl.glResumeTransformFeedback(),
656
+ glIsTransformFeedback: (id) => gl.glIsTransformFeedback(id),
657
+ glTransformFeedbackVaryings: (program, count, varyingsPtr, bufferMode) => {
658
+ const ptrs = new Uint32Array(getMemory().buffer, varyingsPtr, count);
659
+ const names = [];
660
+ const mem = u8();
661
+ for (let i = 0; i < count; i++) names.push(readCString(mem, ptrs[i]));
662
+ gl.glTransformFeedbackVaryings(program, names, bufferMode);
663
+ },
664
+ glGetTransformFeedbackVarying: (program, index, bufSize, lengthPtr, sizePtr, typePtr, namePtr) => {
665
+ // Stub
666
+ if (lengthPtr) u32()[lengthPtr >> 2] = 0;
667
+ if (namePtr) u8()[namePtr] = 0;
668
+ },
669
+
670
+ // ─── Sync ────────────────────────────────────────────────────────
671
+ glFenceSync: (condition, flags) => gl.glFenceSync(condition, flags),
672
+ glClientWaitSync: (sync, flags, timeout) => gl.glClientWaitSync(sync, flags, timeout),
673
+ glDeleteSync: (sync) => gl.glDeleteSync(sync),
674
+ glWaitSync: (sync, flags, timeout) => gl.glWaitSync(sync, flags, timeout),
675
+ glIsSync: (sync) => gl.glIsSync(sync),
676
+ glGetSynciv: (sync, pname, bufSize, lengthPtr, valuesPtr) => {
677
+ const buf = Buffer.alloc(bufSize * 4);
678
+ const lenBuf = Buffer.alloc(4);
679
+ gl.glGetSynciv(sync, pname, bufSize, lenBuf, buf);
680
+ if (lengthPtr) i32()[lengthPtr >> 2] = lenBuf.readInt32LE(0);
681
+ const count = Math.min(lenBuf.readInt32LE(0), bufSize);
682
+ const dst = i32();
683
+ for (let i = 0; i < count; i++) dst[(valuesPtr >> 2) + i] = buf.readInt32LE(i * 4);
684
+ },
685
+
686
+ // ─── Clear buffers (ES3) ─────────────────────────────────────────
687
+ glClearBufferfv: (buffer, drawbuffer, valuePtr) => {
688
+ const GL_COLOR = 0x1800;
689
+ const numFloats = (buffer === GL_COLOR) ? 4 : 1;
690
+ gl.glClearBufferfv(buffer, drawbuffer, new Float32Array(getMemory().buffer, valuePtr, numFloats));
691
+ },
692
+ glClearBufferiv: (buffer, drawbuffer, valuePtr) => {
693
+ gl.glClearBufferiv(buffer, drawbuffer, new Int32Array(getMemory().buffer, valuePtr, 4));
694
+ },
695
+ glClearBufferuiv: (buffer, drawbuffer, valuePtr) => {
696
+ gl.glClearBufferuiv(buffer, drawbuffer, new Uint32Array(getMemory().buffer, valuePtr, 4));
697
+ },
698
+ glClearBufferfi: (buffer, drawbuffer, depth, stencil) => {
699
+ gl.glClearBufferfi(buffer, drawbuffer, depth, stencil);
700
+ },
701
+
702
+ // ─── Buffer binding ──────────────────────────────────────────────
703
+ glBindBufferBase: (target, index, buffer) => gl.glBindBufferBase(target, index, buffer),
704
+ glBindBufferRange: (target, index, buffer, offset, size) => gl.glBindBufferRange(target, index, buffer, offset, size),
705
+
706
+ // ─── Integer state queries ───────────────────────────────────────
707
+ glGetIntegeri_v: (target, index, dataPtr) => {
708
+ const view = new Int32Array(getMemory().buffer, dataPtr, 1);
709
+ gl.glGetIntegeri_v(target, index, view);
710
+ },
711
+ glGetInteger64v: (pname, paramsPtr) => {
712
+ const result = gl.glGetInteger64v(pname);
713
+ new DataView(getMemory().buffer).setBigInt64(paramsPtr, BigInt(Math.round(result)), true);
714
+ },
715
+ glGetInteger64i_v: (target, index, dataPtr) => {
716
+ // Stub — use 32-bit fallback
717
+ const view = new Int32Array(getMemory().buffer, dataPtr, 1);
718
+ gl.glGetIntegeri_v(target, index, view);
719
+ },
720
+
721
+ // ─── Misc queries ────────────────────────────────────────────────
722
+ glGetUniformfv: (program, location, paramsPtr) => {
723
+ const buf = Buffer.alloc(4);
724
+ gl.glGetUniformfv(program, location, buf);
725
+ new DataView(getMemory().buffer).setFloat32(paramsPtr, buf.readFloatLE(0), true);
726
+ },
727
+ glGetUniformiv: (program, location, paramsPtr) => {
728
+ const buf = Buffer.alloc(4);
729
+ gl.glGetUniformiv(program, location, buf);
730
+ i32()[paramsPtr >> 2] = buf.readInt32LE(0);
731
+ },
732
+ glGetUniformuiv: (program, location, paramsPtr) => {
733
+ const buf = Buffer.alloc(4);
734
+ gl.glGetUniformuiv(program, location, buf);
735
+ u32()[paramsPtr >> 2] = buf.readUInt32LE(0);
736
+ },
737
+ };
738
+
739
+ // Build the full import map including emscripten_gl* aliases and OES/ANGLE/EXT variants
740
+ const result = {};
741
+
742
+ // All direct gl* functions
743
+ for (const [name, fn] of Object.entries(funcs)) {
744
+ result[name] = fn;
745
+ }
746
+
747
+ // emscripten_gl* duplicates (same implementation, different name)
748
+ for (const [name, fn] of Object.entries(funcs)) {
749
+ result[`emscripten_${name}`] = fn;
750
+ }
751
+
752
+ // OES extension aliases (VAO)
753
+ result.glBindVertexArrayOES = funcs.glBindVertexArray;
754
+ result.glDeleteVertexArraysOES = funcs.glDeleteVertexArrays;
755
+ result.glGenVertexArraysOES = funcs.glGenVertexArrays;
756
+ result.glIsVertexArrayOES = funcs.glIsVertexArray;
757
+ result.emscripten_glBindVertexArrayOES = funcs.glBindVertexArray;
758
+ result.emscripten_glDeleteVertexArraysOES = funcs.glDeleteVertexArrays;
759
+ result.emscripten_glGenVertexArraysOES = funcs.glGenVertexArrays;
760
+ result.emscripten_glIsVertexArrayOES = funcs.glIsVertexArray;
761
+
762
+ // ANGLE/ARB/EXT/NV instanced drawing aliases
763
+ for (const suffix of ['ANGLE', 'ARB', 'EXT', 'NV']) {
764
+ result[`glDrawArraysInstanced${suffix}`] = funcs.glDrawArraysInstanced;
765
+ result[`glDrawElementsInstanced${suffix}`] = funcs.glDrawElementsInstanced;
766
+ result[`glVertexAttribDivisor${suffix}`] = funcs.glVertexAttribDivisor;
767
+ result[`emscripten_glDrawArraysInstanced${suffix}`] = funcs.glDrawArraysInstanced;
768
+ result[`emscripten_glDrawElementsInstanced${suffix}`] = funcs.glDrawElementsInstanced;
769
+ result[`emscripten_glVertexAttribDivisor${suffix}`] = funcs.glVertexAttribDivisor;
770
+ }
771
+
772
+ // WEBGL/EXT draw buffers aliases
773
+ result.glDrawBuffersWEBGL = funcs.glDrawBuffers;
774
+ result.glDrawBuffersEXT = funcs.glDrawBuffers;
775
+ result.emscripten_glDrawBuffersWEBGL = funcs.glDrawBuffers;
776
+ result.emscripten_glDrawBuffersEXT = funcs.glDrawBuffers;
777
+
778
+ // EXT query aliases (timer queries)
779
+ result.glGenQueriesEXT = funcs.glGenQueries;
780
+ result.glDeleteQueriesEXT = funcs.glDeleteQueries;
781
+ result.glIsQueryEXT = funcs.glIsQuery;
782
+ result.glBeginQueryEXT = funcs.glBeginQuery;
783
+ result.glEndQueryEXT = funcs.glEndQuery;
784
+ result.glQueryCounterEXT = (id, target) => {}; // timer query — stub
785
+ result.glGetQueryivEXT = funcs.glGetQueryiv;
786
+ result.glGetQueryObjectivEXT = (id, pname, paramsPtr) => { i32()[paramsPtr >> 2] = 0; };
787
+ result.glGetQueryObjectuivEXT = funcs.glGetQueryObjectuiv;
788
+ result.glGetQueryObjecti64vEXT = (id, pname, paramsPtr) => {
789
+ new DataView(getMemory().buffer).setBigInt64(paramsPtr, 0n, true);
790
+ };
791
+ result.glGetQueryObjectui64vEXT = (id, pname, paramsPtr) => {
792
+ new DataView(getMemory().buffer).setBigUint64(paramsPtr, 0n, true);
793
+ };
794
+ result.emscripten_glGenQueriesEXT = result.glGenQueriesEXT;
795
+ result.emscripten_glDeleteQueriesEXT = result.glDeleteQueriesEXT;
796
+ result.emscripten_glIsQueryEXT = result.glIsQueryEXT;
797
+ result.emscripten_glBeginQueryEXT = result.glBeginQueryEXT;
798
+ result.emscripten_glEndQueryEXT = result.glEndQueryEXT;
799
+ result.emscripten_glQueryCounterEXT = result.glQueryCounterEXT;
800
+ result.emscripten_glGetQueryivEXT = result.glGetQueryivEXT;
801
+ result.emscripten_glGetQueryObjectivEXT = result.glGetQueryObjectivEXT;
802
+ result.emscripten_glGetQueryObjectuivEXT = result.glGetQueryObjectuivEXT;
803
+ result.emscripten_glGetQueryObjecti64vEXT = result.glGetQueryObjecti64vEXT;
804
+ result.emscripten_glGetQueryObjectui64vEXT = result.glGetQueryObjectui64vEXT;
805
+
806
+ // Polygon mode WEBGL extension — stub
807
+ result.glPolygonModeWEBGL = (face, mode) => {};
808
+ result.emscripten_glPolygonModeWEBGL = result.glPolygonModeWEBGL;
809
+
810
+ // Polygon offset clamp EXT — stub
811
+ result.glPolygonOffsetClampEXT = (factor, units, clamp) => gl.glPolygonOffset(factor, units);
812
+ result.emscripten_glPolygonOffsetClampEXT = result.glPolygonOffsetClampEXT;
813
+
814
+ // Clip control EXT — stub (not in GLES3 core)
815
+ result.glClipControlEXT = (origin, depth) => {};
816
+ result.emscripten_glClipControlEXT = result.glClipControlEXT;
817
+
818
+ // GL call tracing
819
+ if (process.env.GL_TRACE === '1') {
820
+ process.stderr.write(`[libretro-gl-trace] Wrapping ${Object.keys(result).length} GL functions\n`);
821
+ const raw = { ...result };
822
+ for (const name of Object.keys(result)) {
823
+ const orig = raw[name];
824
+ result[name] = (...args) => {
825
+ const ret = orig(...args);
826
+ // Only log non-emscripten variants to avoid double logging
827
+ if (!name.startsWith('emscripten_')) {
828
+ process.stderr.write(`[gl] ${name}(${args.map(a => typeof a === 'number' ? '0x'+a.toString(16) : a).join(',')}) => ${ret}\n`);
829
+ }
830
+ return ret;
831
+ };
832
+ }
833
+ }
834
+
835
+ return result;
836
+ }