@geastack/native-webgl-angle 0.1.12 → 0.1.14

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.
@@ -0,0 +1,230 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // The WebGL surface of a Windows desktop build: a child window filling the
3
+ // main window's content area, which the native WebGL host renders into, and
4
+ // the input the host reports to the program.
5
+ //
6
+ // It plugs into the Windows target as a host extension (gea_win32_host.h):
7
+ // the target attaches it before the program's top level runs -- the program
8
+ // creates its WebGL context while it initializes -- and brackets the
9
+ // initialization and every frame with begin/end, which is when the host's
10
+ // EGL context is current. Keys reach the host as macOS virtual key codes, the
11
+ // code space the program's key polling uses on every platform; pointer
12
+ // positions are in CSS pixels of the surface.
13
+ #include "gea_win32_host.h"
14
+
15
+ #include <windowsx.h>
16
+
17
+ #include <algorithm>
18
+ #include <cstdio>
19
+ #include <cstdlib>
20
+
21
+ extern "C" bool gea_three_webgl_attach_uwp(void *nativeWindow, double width, double height, double devicePixelRatio);
22
+ extern "C" void gea_three_webgl_update_size_uwp(double width, double height, double devicePixelRatio);
23
+ extern "C" bool gea_three_webgl_begin_frame_uwp();
24
+ extern "C" void gea_three_webgl_end_frame_uwp();
25
+ extern "C" void gea_three_set_key_state(int code, bool down);
26
+ extern "C" void gea_three_set_pointer_state(double x, double y, double width, double height, bool down);
27
+
28
+ namespace {
29
+
30
+ HWND gSurface = nullptr;
31
+ HWND gMainWindow = nullptr;
32
+ double gScale = 1.0;
33
+ bool gPointerDown = false;
34
+ // The program polls key state once per frame, so a tap whose key-down and
35
+ // key-up both arrive between two frames would never be seen. A release waits
36
+ // here until a frame has run with the key down.
37
+ bool gReleasePending[256] = {};
38
+
39
+ // macOS virtual key codes (Carbon kVK_*), for the keys a game typically polls.
40
+ int macKeyCode(WPARAM vk)
41
+ {
42
+ switch (vk) {
43
+ case VK_LEFT: return 123;
44
+ case VK_RIGHT: return 124;
45
+ case VK_DOWN: return 125;
46
+ case VK_UP: return 126;
47
+ case VK_SPACE: return 49;
48
+ case VK_RETURN: return 36;
49
+ case VK_ESCAPE: return 53;
50
+ case VK_TAB: return 48;
51
+ case VK_BACK: return 51;
52
+ case VK_DELETE: return 117;
53
+ case VK_HOME: return 115;
54
+ case VK_END: return 119;
55
+ case VK_PRIOR: return 116;
56
+ case VK_NEXT: return 121;
57
+ case VK_SHIFT: case VK_LSHIFT: return 56;
58
+ case VK_RSHIFT: return 60;
59
+ case VK_CONTROL: case VK_LCONTROL: return 59;
60
+ case VK_RCONTROL: return 62;
61
+ case VK_MENU: case VK_LMENU: return 58;
62
+ case VK_RMENU: return 61;
63
+ case VK_OEM_MINUS: return 27;
64
+ case VK_OEM_PLUS: return 24;
65
+ case VK_OEM_COMMA: return 43;
66
+ case VK_OEM_PERIOD: return 47;
67
+ case VK_OEM_1: return 41; // ;
68
+ case VK_OEM_2: return 44; // /
69
+ case VK_OEM_3: return 50; // `
70
+ case VK_OEM_4: return 33; // [
71
+ case VK_OEM_5: return 42; // backslash
72
+ case VK_OEM_6: return 30; // ]
73
+ case VK_OEM_7: return 39; // '
74
+ default: break;
75
+ }
76
+ static const int letters[26] = {0, 11, 8, 2, 14, 3, 5, 4, 34, 38, 40, 37, 46, 45, 31, 35, 12, 15, 1, 17, 32, 9, 13, 7, 16, 6};
77
+ if (vk >= 'A' && vk <= 'Z') return letters[vk - 'A'];
78
+ static const int digits[10] = {29, 18, 19, 20, 21, 23, 22, 26, 28, 25};
79
+ if (vk >= '0' && vk <= '9') return digits[vk - '0'];
80
+ static const int functions[12] = {122, 120, 99, 118, 96, 97, 98, 100, 101, 109, 103, 111};
81
+ if (vk >= VK_F1 && vk <= VK_F12) return functions[vk - VK_F1];
82
+ return -1;
83
+ }
84
+
85
+ void releaseAllKeys()
86
+ {
87
+ for (int code = 0; code < 256; ++code) {
88
+ gReleasePending[code] = false;
89
+ gea_three_set_key_state(code, false);
90
+ }
91
+ }
92
+
93
+ void reportPointer(LPARAM lParam)
94
+ {
95
+ RECT client{};
96
+ GetClientRect(gSurface, &client);
97
+ gea_three_set_pointer_state(GET_X_LPARAM(lParam) / gScale, GET_Y_LPARAM(lParam) / gScale, std::max<LONG>(1, client.right) / gScale,
98
+ std::max<LONG>(1, client.bottom) / gScale, gPointerDown);
99
+ }
100
+
101
+ LRESULT CALLBACK surfaceProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
102
+ {
103
+ switch (message) {
104
+ case WM_KEYDOWN:
105
+ case WM_SYSKEYDOWN:
106
+ if (message == WM_SYSKEYDOWN && wParam == VK_F4) break; // Alt+F4 closes the window
107
+ if (const int code = macKeyCode(wParam); code >= 0) {
108
+ gReleasePending[code] = false;
109
+ gea_three_set_key_state(code, true);
110
+ }
111
+ return 0; // no menu activation on Alt or F10
112
+ case WM_KEYUP:
113
+ case WM_SYSKEYUP:
114
+ if (const int code = macKeyCode(wParam); code >= 0) gReleasePending[code] = true;
115
+ return 0;
116
+ case WM_SYSCHAR:
117
+ return 0; // no beep for Alt+key
118
+ case WM_KILLFOCUS:
119
+ gPointerDown = false;
120
+ ReleaseCapture();
121
+ releaseAllKeys();
122
+ return 0;
123
+ case WM_LBUTTONDOWN:
124
+ SetFocus(hwnd);
125
+ gPointerDown = true;
126
+ SetCapture(hwnd);
127
+ reportPointer(lParam);
128
+ return 0;
129
+ case WM_LBUTTONUP:
130
+ gPointerDown = false;
131
+ ReleaseCapture();
132
+ reportPointer(lParam);
133
+ return 0;
134
+ case WM_MOUSEMOVE:
135
+ reportPointer(lParam);
136
+ return 0;
137
+ case WM_ERASEBKGND:
138
+ return 1; // the swap chain owns every pixel
139
+ case WM_PAINT:
140
+ ValidateRect(hwnd, nullptr);
141
+ return 0;
142
+ default:
143
+ break;
144
+ }
145
+ return DefWindowProcW(hwnd, message, wParam, lParam);
146
+ }
147
+
148
+ int attach(HWND window, RECT area, double scale)
149
+ {
150
+ gMainWindow = window;
151
+ gScale = scale > 0 ? scale : 1.0;
152
+ HINSTANCE instance = GetModuleHandleW(nullptr);
153
+ WNDCLASSEXW cls{};
154
+ cls.cbSize = sizeof(cls);
155
+ cls.style = CS_OWNDC | CS_DBLCLKS;
156
+ cls.lpfnWndProc = surfaceProc;
157
+ cls.hInstance = instance;
158
+ cls.hCursor = LoadCursorW(nullptr, IDC_ARROW);
159
+ cls.lpszClassName = L"GeaWebGLSurface";
160
+ RegisterClassExW(&cls);
161
+ // The main window must not paint over its swap-chain child.
162
+ SetWindowLongPtrW(window, GWL_STYLE, GetWindowLongPtrW(window, GWL_STYLE) | WS_CLIPCHILDREN);
163
+ const int width = std::max<int>(1, area.right - area.left);
164
+ const int height = std::max<int>(1, area.bottom - area.top);
165
+ gSurface = CreateWindowExW(0, cls.lpszClassName, L"", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, area.left, area.top, width, height, window, nullptr,
166
+ instance, nullptr);
167
+ if (!gSurface) {
168
+ std::fprintf(stderr, "[native-webgl-angle] could not create the surface window (error %lu)\n", GetLastError());
169
+ return 0;
170
+ }
171
+ SetWindowPos(gSurface, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
172
+ // Present with vsync unless the environment says otherwise.
173
+ if (!std::getenv("GEA_ANGLE_SWAP_INTERVAL")) _putenv_s("GEA_ANGLE_SWAP_INTERVAL", "1");
174
+ if (!gea_three_webgl_attach_uwp(gSurface, width, height, gScale)) {
175
+ std::fprintf(stderr, "[native-webgl-angle] the WebGL surface could not start: no usable EGL/OpenGL ES implementation\n");
176
+ return 0;
177
+ }
178
+ // eglSwapBuffers paces the loop: it waits for the refresh with vsync on and
179
+ // renders unthrottled with it off (GEA_ANGLE_SWAP_INTERVAL=0).
180
+ gea_win32_set_frame_pacing(GEA_WIN32_FRAME_PACING_PRESENT);
181
+ SetFocus(gSurface);
182
+ return 1;
183
+ }
184
+
185
+ void layout(RECT area, double scale)
186
+ {
187
+ if (!gSurface) return;
188
+ gScale = scale > 0 ? scale : 1.0;
189
+ const int width = std::max<int>(1, area.right - area.left);
190
+ const int height = std::max<int>(1, area.bottom - area.top);
191
+ SetWindowPos(gSurface, nullptr, area.left, area.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
192
+ gea_three_webgl_update_size_uwp(width, height, gScale);
193
+ }
194
+
195
+ int beginFrame() { return gea_three_webgl_begin_frame_uwp() ? 1 : 0; }
196
+
197
+ void endFrame()
198
+ {
199
+ gea_three_webgl_end_frame_uwp();
200
+ for (int code = 0; code < 256; ++code) {
201
+ if (!gReleasePending[code]) continue;
202
+ gReleasePending[code] = false;
203
+ gea_three_set_key_state(code, false);
204
+ }
205
+ }
206
+
207
+ int windowMessage(HWND, UINT message, WPARAM, LPARAM, LRESULT *result)
208
+ {
209
+ // Activating the main window gives it the keyboard focus; the surface is
210
+ // where keys are read.
211
+ if (message == WM_SETFOCUS && gSurface) {
212
+ SetFocus(gSurface);
213
+ *result = 0;
214
+ return 1;
215
+ }
216
+ return 0;
217
+ }
218
+
219
+ void detach()
220
+ {
221
+ if (gSurface) DestroyWindow(gSurface);
222
+ gSurface = nullptr;
223
+ }
224
+
225
+ const gea_win32_host_extension kSurfaceExtension = {
226
+ GEA_WIN32_HOST_EXTENSION_VERSION, "The WebGL surface", attach, layout, beginFrame, endFrame, windowMessage, detach,
227
+ };
228
+ const bool kRegistered = (gea_win32_register_host_extension(&kSurfaceExtension), true);
229
+
230
+ } // namespace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geastack/native-webgl-angle",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -28,7 +28,8 @@
28
28
  "./text/native-text-types": "./src/text/native-text-types.ts",
29
29
  "./geatsc-plugin-batched-probe": "./geatsc-plugin-batched-probe.mjs",
30
30
  "./test/batched-render-scene": "./test/batched-render-scene.ts",
31
- "./test/auto-instanced-render-scene": "./test/auto-instanced-render-scene.ts"
31
+ "./test/auto-instanced-render-scene": "./test/auto-instanced-render-scene.ts",
32
+ "./gea-native.json": "./gea-native.json"
32
33
  },
33
34
  "files": [
34
35
  "src",
@@ -40,7 +41,8 @@
40
41
  "geatsc-plugin-batched-probe.mjs",
41
42
  "test/batched-render-scene.ts",
42
43
  "test/auto-instanced-render-scene.ts",
43
- "geatsc-plugin-readback.mjs"
44
+ "geatsc-plugin-readback.mjs",
45
+ "gea-native.json"
44
46
  ],
45
47
  "publishConfig": {
46
48
  "access": "public",
@@ -51,7 +53,7 @@
51
53
  "test:host-effects": "node test/native-host-effects-contract.test.mjs",
52
54
  "test:allocation-contracts": "node test/render-sort-contract.test.mjs && node test/upload-array-contract.test.mjs && node test/draw-dispatch-contract.test.mjs && node test/native-upload-transport.test.mjs && node test/fixed-uniform-contract.test.mjs && node test/uniform-binding-contract.test.mjs && node test/buffer-streaming-contract.test.mjs",
53
55
  "check": "tsc -p tsconfig.json --noEmit",
54
- "test": "node test/native-blending-contract.test.mjs && node test/native-transform-contract.test.mjs && node test/troika-three-text.test.mjs && node test/typed-void-contract.test.mjs && node test/instancing-transport-contract.test.mjs && node test/automatic-instancing-contract.test.mjs && node test/jsdoc-fold-contract.test.mjs",
56
+ "test": "node test/native-blending-contract.test.mjs && node test/native-transform-contract.test.mjs && node test/troika-three-text.test.mjs && node test/typed-void-contract.test.mjs && node test/instancing-transport-contract.test.mjs && node test/automatic-instancing-contract.test.mjs && node test/jsdoc-fold-contract.test.mjs && node test/gea-native-declaration.test.mjs",
55
57
  "test:package": "node --experimental-vm-modules test/package.test.mjs"
56
58
  },
57
59
  "dependencies": {
package/src/inflate.ts CHANGED
@@ -18,7 +18,7 @@ const codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4,
18
18
  // native build narrows a function's integer locals to machine integers, but a
19
19
  // class's number fields stay doubles, and on a core without a double FPU (the
20
20
  // ESP32-S31) every bit read through a field was several soft-float calls --
21
- // inflating skytail's kit at boot took over 80 seconds.
21
+ // inflating a game's asset kit at boot took over 80 seconds.
22
22
 
23
23
  /** Decoder state: read position, bit buffer, bits held, bytes written. */
24
24
  const position = 0
@@ -1,4 +1,6 @@
1
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
1
2
  declare function threeWebGLCopyTexImage2D(target:number,level:number,internalFormat:number,x:number,y:number,width:number,height:number,border:number):void;
3
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
2
4
  declare function threeWebGLCopyTexSubImage2D(target:number,level:number,xoffset:number,yoffset:number,x:number,y:number,width:number,height:number):void;
3
5
  /// <reference path="./gea-native-types.d.ts" />
4
6
 
@@ -93,6 +95,7 @@ type NativeHandleKind =
93
95
  | 'program'
94
96
  | 'renderbuffer'
95
97
  | 'shader'
98
+ | 'sync'
96
99
  | 'texture'
97
100
  | 'uniform'
98
101
  | 'vertexArray'
@@ -200,11 +203,26 @@ function typedArrayKind(value: BufferSourceLike): number {
200
203
  return 1
201
204
  }
202
205
 
206
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
203
207
  declare function threeWebGLBlitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void
208
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
204
209
  declare function threeWebGLRenderbufferStorageMultisample(target:number, samples:number, format:number, width:number, height:number): void
205
210
  type ReadPixelsDestination = Uint8Array | Uint16Array | Float32Array | Uint32Array | Int32Array | Int16Array | Int8Array
206
211
  // Mutates the destination view; deliberately not marked host-inert.
212
+ /** @gea-host-typed-array-element-writes Stores pixels into the destination typed array's elements only; no other property of any object, no callbacks. */
207
213
  declare function threeWebGLReadPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset: number): void
214
+ // WebGL2 asynchronous readback: into the bound PIXEL_PACK_BUFFER, fenced, then read through getBufferSubData.
215
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
216
+ declare function threeWebGLReadPixelsPack(x: number, y: number, width: number, height: number, format: number, type: number, offset: number): void
217
+ /** @gea-host-inert Creates a GL sync object; never calls back into script. */
218
+ declare function threeWebGLFenceSync(): number
219
+ /** @gea-host-inert Reads scalar arguments, waits on GL state only; never calls back into script. */
220
+ declare function threeWebGLClientWaitSync(sync: number, flags: number, timeout: number): number
221
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
222
+ declare function threeWebGLDeleteSync(sync: number): void
223
+ // Mutates the destination view; deliberately not marked host-inert.
224
+ /** @gea-host-typed-array-element-writes Stores buffer bytes into the destination typed array's elements only; no other property of any object, no callbacks. */
225
+ declare function threeWebGLGetBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset: number, length: number): void
208
226
 
209
227
  function zeroF32Array(length: number): f32[] {
210
228
  const out: f32[] = []
@@ -254,6 +272,14 @@ declare function threeWebGLTexImage2D(target: number, level: number, internalFor
254
272
  declare function threeWebGLTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, values: BufferSourceLike): void
255
273
  /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
256
274
  declare function threeWebGLTexStorage2D(target: number, levels: number, internalFormat: number, width: number, height: number): void
275
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
276
+ declare function threeWebGLTexImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
277
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
278
+ declare function threeWebGLTexSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
279
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
280
+ declare function threeWebGLTexStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void
281
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
282
+ declare function threeWebGLFramebufferTextureLayer(target: number, attachment: number, texture: number, level: number, layer: number): void
257
283
 
258
284
  // Shader source keyed by the stable native GL handle. A NativeShader instance
259
285
  // can be value-copied when it round-trips through a function-return / gcv
@@ -663,6 +689,10 @@ export class NativeWebGL2RenderingContext {
663
689
  readonly TIMEOUT_EXPIRED = 0x911B
664
690
  readonly CONDITION_SATISFIED = 0x911C
665
691
  readonly WAIT_FAILED = 0x911D
692
+ readonly SYNC_GPU_COMMANDS_COMPLETE = 0x9117
693
+ readonly PIXEL_PACK_BUFFER = 0x88EB
694
+ readonly PIXEL_PACK_BUFFER_BINDING = 0x88ED
695
+ readonly STREAM_READ = 0x88E1
666
696
  readonly COLOR = 0x1800
667
697
  readonly DEPTH_COMPONENT = 0x1902
668
698
  readonly ALPHA = 0x1906
@@ -935,11 +965,14 @@ export class NativeWebGL2RenderingContext {
935
965
  if (name === this.MAX_RENDERBUFFER_SIZE || name === this.MAX_3D_TEXTURE_SIZE) return 16384
936
966
  if (name === this.MAX_ARRAY_TEXTURE_LAYERS) return 2048
937
967
  if (name === this.MAX_VERTEX_ATTRIBS) return 16
938
- if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 16
968
+ // threejs-rendozer binds up to 32 textures per program (both stages); three checks a program's total against this.
969
+ if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 32
939
970
  if (name === this.MAX_COMBINED_TEXTURE_IMAGE_UNITS) return 32
940
971
  if (name === this.MAX_VERTEX_UNIFORM_VECTORS || name === this.MAX_FRAGMENT_UNIFORM_VECTORS) return 1024
941
972
  if (name === this.MAX_VARYING_VECTORS) return 32
942
- if (name === this.MAX_SAMPLES || name === this.SAMPLES) return 4
973
+ // threejs-rendozer reports 8 when every multisampled renderbuffer format supports it (and steps down per format).
974
+ if (name === this.MAX_SAMPLES) return 8
975
+ if (name === this.SAMPLES) return 4
943
976
  if (name === this.MAX_UNIFORM_BUFFER_BINDINGS) return 24
944
977
  if (name === this.DEPTH_BITS) return 24
945
978
  if (name === this.STENCIL_BITS) return 8
@@ -1106,10 +1139,10 @@ export class NativeWebGL2RenderingContext {
1106
1139
  texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number | NativeTextureImage | null, format?: number, type?: number, pixels?: BufferSourceLike | null): void {
1107
1140
  if (typeof borderOrSource === 'number' && typeof format === 'number' && typeof type === 'number') {
1108
1141
  threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels ?? new Uint8Array(0))
1109
- } else if (borderOrSource && typeof borderOrSource !== 'number') {
1142
+ } else if (borderOrSource !== null && borderOrSource !== undefined && typeof borderOrSource !== 'number') {
1110
1143
  threeWebGLTexImage2D(target, level, internalFormat, borderOrSource.width, borderOrSource.height, widthOrFormat, heightOrType, borderOrSource.data)
1111
1144
  } else {
1112
- throw new Error('Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage')
1145
+ throw new Error(`Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage (target ${target}, level ${level}, internal format ${internalFormat}, source ${borderOrSource === null ? 'null' : typeof borderOrSource})`)
1113
1146
  }
1114
1147
  }
1115
1148
  texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: BufferSourceLike): void {
@@ -1125,9 +1158,15 @@ export class NativeWebGL2RenderingContext {
1125
1158
  // Three uploads 3D and array textures only from typed `image.data` /
1126
1159
  // `mipmap.data` (or `null` to allocate), the same carrier the 2D uploads
1127
1160
  // take; no TexImageSource ever reaches these entry points.
1128
- texImage3D(_target: number, _level: number, _internalFormat: number, _width: number, _height: number, _depth: number, _border: number, _format: number, _type: number, _source: BufferSourceLike | null): void {}
1129
- texSubImage3D(_target: number, _level: number, _xoffset: number, _yoffset: number, _zoffset: number, _width: number, _height: number, _depth: number, _format: number, _type: number, _source: BufferSourceLike): void {}
1130
- texStorage3D(_target: number, _levels: number, _internalFormat: number, _width: number, _height: number, _depth: number): void {}
1161
+ texImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, _border: number, format: number, type: number, source: BufferSourceLike | null): void {
1162
+ threeWebGLTexImage3D(target, level, internalFormat, width, height, depth, format, type, source ?? new Uint8Array(0))
1163
+ }
1164
+ texSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, source: BufferSourceLike): void {
1165
+ threeWebGLTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, source)
1166
+ }
1167
+ texStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void {
1168
+ threeWebGLTexStorage3D(target, levels, internalFormat, width, height, depth)
1169
+ }
1131
1170
 
1132
1171
  createVertexArray(): NativeHandle { return createHandle('vertexArray', nativeWebGLCreateVertexArray()) }
1133
1172
  bindVertexArray(vertexArray: NativeHandle | null): void { nativeWebGLBindVertexArray(nullableNativeHandle(vertexArray)) }
@@ -1150,7 +1189,9 @@ export class NativeWebGL2RenderingContext {
1150
1189
  framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: NativeHandle | null): void {
1151
1190
  nativeWebGLFramebufferRenderbuffer(target, attachment, renderbuffertarget, nullableNativeHandle(renderbuffer))
1152
1191
  }
1153
- framebufferTextureLayer(_target: number, _attachment: number, _texture: NativeHandle | null, _level: number, _layer: number): void {}
1192
+ framebufferTextureLayer(target: number, attachment: number, texture: NativeHandle | null, level: number, layer: number): void {
1193
+ threeWebGLFramebufferTextureLayer(target, attachment, nullableNativeHandle(texture), level, layer)
1194
+ }
1154
1195
  drawBuffers(buffers: number[]): void { nativeWebGLDrawBuffers(buffers[0] ?? this.COLOR_ATTACHMENT0) }
1155
1196
 
1156
1197
  clearColor(r: number, g: number, b: number, a: number): void { nativeWebGLClearColor(r, g, b, a) }
@@ -1193,7 +1234,13 @@ export class NativeWebGL2RenderingContext {
1193
1234
  invalidateFramebuffer(_target: number, _attachments: BufferSourceLike): void {}
1194
1235
  blitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void { threeWebGLBlitFramebuffer(sx0,sy0,sx1,sy1,dx0,dy0,dx1,dy1,mask,filter) }
1195
1236
  readBuffer(_src: number): void {}
1196
- readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset = 0): void {
1237
+ // WebGL2 also reads into the bound PIXEL_PACK_BUFFER at a byte offset (three's
1238
+ // readRenderTargetPixelsAsync): the GPU copy is recorded without waiting for the GPU.
1239
+ readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination | number, offset = 0): void {
1240
+ if (typeof destination === 'number') {
1241
+ threeWebGLReadPixelsPack(x, y, width, height, format, type, destination)
1242
+ return
1243
+ }
1197
1244
  threeWebGLReadPixels(x, y, width, height, format, type, destination, offset)
1198
1245
  }
1199
1246
  copyTexImage2D(target: number, level: number, internalFormat: number, x: number, y: number, width: number, height: number, border: number): void {threeWebGLCopyTexImage2D(target,level,internalFormat,x,y,width,height,border);}
@@ -1202,7 +1249,9 @@ export class NativeWebGL2RenderingContext {
1202
1249
  clearBufferiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1203
1250
  clearBufferuiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1204
1251
  getBufferParameter(_target: number, _pname: number): number { return 0 }
1205
- getBufferSubData(_target: number, _srcByteOffset: number, _destination: unknown): void {}
1252
+ getBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset = 0, length = 0): void {
1253
+ threeWebGLGetBufferSubData(target, srcByteOffset, destination, dstOffset, length)
1254
+ }
1206
1255
  getFramebufferAttachmentParameter(_target: number, _attachment: number, _pname: number): number { return 0 }
1207
1256
  getRenderbufferParameter(_target: number, _pname: number): number { return 0 }
1208
1257
  getTexParameter(_target: number, _pname: number): number { return 0 }
@@ -1213,9 +1262,14 @@ export class NativeWebGL2RenderingContext {
1213
1262
  uniformBlockBinding(_program: NativeProgram | null, _uniformBlockIndex: number, _uniformBlockBinding: number): void {}
1214
1263
  bindBufferBase(_target: number, _index: number, _buffer: NativeHandle | null): void {}
1215
1264
  bindBufferRange(_target: number, _index: number, _buffer: NativeHandle | null, _offset: number, _size: number): void {}
1216
- fenceSync(_condition: number, _flags: number): null { return null }
1217
- clientWaitSync(_sync: NativeHandle | null, _flags: number, _timeout: number): number { return 0 }
1218
- deleteSync(_sync: NativeHandle | null): void {}
1265
+ fenceSync(_condition: number, _flags: number): NativeHandle | null {
1266
+ const sync = threeWebGLFenceSync()
1267
+ return sync ? createHandle('sync', sync) : null
1268
+ }
1269
+ clientWaitSync(sync: NativeHandle | null, flags: number, timeout: number): number {
1270
+ return sync ? threeWebGLClientWaitSync(nativeHandle(sync), flags, timeout) : this.WAIT_FAILED
1271
+ }
1272
+ deleteSync(sync: NativeHandle | null): void { if (sync) threeWebGLDeleteSync(nativeHandle(sync)) }
1219
1273
  isBuffer(_buffer: NativeHandle | null): boolean { return true }
1220
1274
  isFramebuffer(_framebuffer: NativeHandle | null): boolean { return true }
1221
1275
  isProgram(_program: NativeProgram | null): boolean { return true }
@@ -35,6 +35,25 @@ export function nativeWebGLSwap(): void {
35
35
  threeWebGLSwap()
36
36
  }
37
37
 
38
+ // GPU time-elapsed timers (GL_EXT_disjoint_timer_query): begin returns a timer id (0 when
39
+ // unsupported), end closes the open timer, and result is the elapsed nanoseconds, or -1 while the
40
+ // GPU has not finished it. A timer is released once its result has been read.
41
+ export function nativeWebGLTimerBegin(): number {
42
+ return threeWebGLTimerBegin()
43
+ }
44
+ export function nativeWebGLTimerEnd(): void {
45
+ threeWebGLTimerEnd()
46
+ }
47
+ export function nativeWebGLTimerResult(timer: number): number {
48
+ return threeWebGLTimerResult(timer)
49
+ }
50
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
51
+ declare function threeWebGLTimerBegin(): number
52
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
53
+ declare function threeWebGLTimerEnd(): void
54
+ /** @gea-host-inert Native state only; retains no script references and invokes no script. */
55
+ declare function threeWebGLTimerResult(timer: number): number
56
+
38
57
  export function logNativeWebGL(message: string): void {
39
58
  threeAngleSmokeLog(message)
40
59
  }