@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.
- package/README.md +30 -3
- package/gea-native.json +16 -0
- package/geatsc-plugin-readback.mjs +21 -0
- package/geatsc-plugin-uniforms.mjs +214 -59
- package/geatsc-plugin-uploads.mjs +7 -2
- package/geatsc-plugin.mjs +912 -67
- package/native/angle_webgl_host.mm +253 -29
- package/native/audio_host.mm +26 -1
- package/native/win32_webgl_surface.cpp +230 -0
- package/package.json +6 -4
- package/src/inflate.ts +1 -1
- package/src/nativeWebGL.ts +67 -13
- package/src/nativeWebGLHost.ts +19 -0
|
@@ -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.
|
|
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
|
|
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
|
package/src/nativeWebGL.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
1129
|
-
|
|
1130
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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):
|
|
1217
|
-
|
|
1218
|
-
|
|
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 }
|
package/src/nativeWebGLHost.ts
CHANGED
|
@@ -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
|
}
|