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.
- package/CHANGELOG.md +572 -0
- package/README.md +9 -7
- package/examples/dreamcast/hello/main.c +24 -0
- package/examples/n64/platformer/main.c +158 -0
- package/examples/n64/puzzle/main.c +117 -0
- package/examples/n64/racing/main.c +147 -0
- package/examples/n64/shmup/main.c +122 -0
- package/examples/n64/sports/main.c +127 -0
- package/examples/ps1/platformer/main.c +158 -0
- package/examples/ps1/puzzle/main.c +125 -0
- package/examples/ps1/racing/main.c +147 -0
- package/examples/ps1/shmup/main.c +192 -0
- package/examples/ps1/sports/main.c +127 -0
- package/examples/ps1/sprite_move/main.c +38 -0
- package/package.json +11 -2
- package/src/analysis/analyze.js +224 -29
- package/src/analysis/decompile.js +7 -0
- package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
- package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
- package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
- package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
- package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
- package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
- package/src/analysis/rizin.js +24 -1
- package/src/cores/capabilities.js +122 -2
- package/src/cores/registry.js +17 -0
- package/src/host/LibretroGL.js +273 -0
- package/src/host/LibretroGLBridge.js +836 -0
- package/src/host/LibretroHost.js +203 -3
- package/src/host/callbacks.js +22 -2
- package/src/host/coreLoader.js +65 -5
- package/src/host/cpu-state.js +27 -0
- package/src/host/framebuffer.js +14 -1
- package/src/host/glOptionalDep.js +60 -0
- package/src/host/n64-ai-state.js +43 -0
- package/src/host/ps1-spu-state.js +65 -0
- package/src/host/retroConstants.js +14 -0
- package/src/mcp/tools/disasm.js +2 -0
- package/src/mcp/tools/frame.js +18 -9
- package/src/mcp/tools/lifecycle.js +1 -1
- package/src/mcp/tools/platform-tools.js +20 -4
- package/src/mcp/tools/platforms.js +38 -4
- package/src/mcp/tools/project.js +116 -0
- package/src/mcp/tools/rendering-context.js +2 -1
- package/src/mcp/tools/toolchain.js +1 -1
- package/src/platforms/n64/lib/c/n64.c +196 -0
- package/src/platforms/n64/lib/c/n64.h +68 -0
- package/src/platforms/ps1/lib/c/psx.c +200 -0
- package/src/platforms/ps1/lib/c/psx.h +83 -0
- package/src/toolchains/index.js +65 -0
- package/src/toolchains/mips-c/lib/be/libc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libm.a +0 -0
- package/src/toolchains/mips-c/lib/el/libc.a +0 -0
- package/src/toolchains/mips-c/lib/el/libm.a +0 -0
- package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
- package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
- package/src/toolchains/mips-c/lib/n64.ld +15 -0
- package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
- package/src/toolchains/mips-c/lib/ps1.ld +15 -0
- package/src/toolchains/mips-c/lib/softint.c +37 -0
- package/src/toolchains/mips-c/mips-c.js +155 -0
- package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
- package/src/toolchains/sh-c/lib/dc-crt0.s +23 -0
- package/src/toolchains/sh-c/lib/dc.h +102 -0
- package/src/toolchains/sh-c/lib/dc.ld +11 -0
- package/src/toolchains/sh-c/lib/libc.a +0 -0
- package/src/toolchains/sh-c/lib/libgcc.a +0 -0
- package/src/toolchains/sh-c/lib/libm.a +0 -0
- package/src/toolchains/sh-c/sh-c.js +101 -0
- 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
|
+
}
|