@geastack/native-webgl-angle 0.1.6
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/LICENSE +202 -0
- package/README.md +184 -0
- package/geatsc-plugin-batched-probe.mjs +98 -0
- package/geatsc-plugin-instancing.mjs +160 -0
- package/geatsc-plugin-uniforms.mjs +1955 -0
- package/geatsc-plugin-uploads.mjs +29 -0
- package/geatsc-plugin.mjs +10275 -0
- package/native/angle_webgl_host.mm +2687 -0
- package/native/audio_host.mm +857 -0
- package/native/audio_uwp.h +116 -0
- package/package.json +70 -0
- package/src/gea-native-types.d.ts +4 -0
- package/src/nativeAudioHost.ts +296 -0
- package/src/nativeThreeUtils.ts +11 -0
- package/src/nativeWebGL.ts +1211 -0
- package/src/nativeWebGLAnimation.ts +13 -0
- package/src/nativeWebGLHost.ts +333 -0
- package/src/nativeWebXRManager.ts +48 -0
- package/src/text/atlas-sans.ts +195 -0
- package/src/text/atlas-serif-italic.ts +195 -0
- package/src/text/atlas-serif.ts +195 -0
- package/src/text/native-text-types.ts +17 -0
- package/src/text/native-text.ts +203 -0
- package/src/troika-three-text.ts +248 -0
- package/test/auto-instanced-render-scene.ts +66 -0
- package/test/batched-render-scene.ts +58 -0
|
@@ -0,0 +1,2687 @@
|
|
|
1
|
+
#if defined(_WIN32)
|
|
2
|
+
#ifndef NOMINMAX
|
|
3
|
+
#define NOMINMAX
|
|
4
|
+
#endif
|
|
5
|
+
#include <windows.h>
|
|
6
|
+
#else
|
|
7
|
+
#include "gea/apple/native_bridge.h"
|
|
8
|
+
|
|
9
|
+
#import <AppKit/AppKit.h>
|
|
10
|
+
#import <QuartzCore/QuartzCore.h>
|
|
11
|
+
#import <dispatch/dispatch.h>
|
|
12
|
+
|
|
13
|
+
#include <dlfcn.h>
|
|
14
|
+
#endif
|
|
15
|
+
// Typed uniform arguments use the compiler runtime on both native platforms.
|
|
16
|
+
#include "gea_runtime.h"
|
|
17
|
+
#include <algorithm>
|
|
18
|
+
#include <chrono>
|
|
19
|
+
#include <cstdarg>
|
|
20
|
+
#include <cmath>
|
|
21
|
+
#include <cstddef>
|
|
22
|
+
#include <cstdint>
|
|
23
|
+
#include <cstdio>
|
|
24
|
+
#include <cstdlib>
|
|
25
|
+
#include <cstring>
|
|
26
|
+
#include <string>
|
|
27
|
+
#include <vector>
|
|
28
|
+
#include <span>
|
|
29
|
+
#include <unordered_map>
|
|
30
|
+
|
|
31
|
+
static void geaTrapCheck(const char *where);
|
|
32
|
+
|
|
33
|
+
using EGLBoolean = unsigned int;
|
|
34
|
+
using EGLenum = unsigned int;
|
|
35
|
+
using EGLint = int;
|
|
36
|
+
using EGLDisplay = void *;
|
|
37
|
+
using EGLConfig = void *;
|
|
38
|
+
using EGLContext = void *;
|
|
39
|
+
using EGLSurface = void *;
|
|
40
|
+
using EGLNativeDisplayType = void *;
|
|
41
|
+
using EGLNativeWindowType = void *;
|
|
42
|
+
|
|
43
|
+
using GLenum = unsigned int;
|
|
44
|
+
using GLboolean = unsigned char;
|
|
45
|
+
using GLbitfield = unsigned int;
|
|
46
|
+
using GLchar = char;
|
|
47
|
+
using GLint = int;
|
|
48
|
+
using GLsizei = int;
|
|
49
|
+
using GLsizeiptr = std::ptrdiff_t;
|
|
50
|
+
using GLintptr = std::ptrdiff_t;
|
|
51
|
+
using GLuint = unsigned int;
|
|
52
|
+
using GLfloat = float;
|
|
53
|
+
|
|
54
|
+
// Keyboard state for the native host. macOS delivers key events to the first
|
|
55
|
+
// responder; the host view records the pressed state of each hardware key code
|
|
56
|
+
// (0-127 macOS virtual key codes) into this table, and the game reads it through
|
|
57
|
+
// the `gea_three_key_pressed` intrinsic. This is what makes the native build
|
|
58
|
+
// PLAYABLE — the TS game's `window`-based listeners are inert off the web.
|
|
59
|
+
static bool gGeaKeyState[256] = {false};
|
|
60
|
+
|
|
61
|
+
// Pointer state for the native host. The host view tracks the mouse through an
|
|
62
|
+
// NSTrackingArea (mouseMoved: is only delivered with one — hover without a
|
|
63
|
+
// button press produces no events on a plain NSView) and records the latest
|
|
64
|
+
// position in view coordinates with a TOP-LEFT origin (the web's
|
|
65
|
+
// clientX/clientY convention; AppKit's default view space is bottom-left).
|
|
66
|
+
// The game polls it through `gea_three_pointer_state`, mirroring the keyboard's
|
|
67
|
+
// `gea_three_key_pressed` bridge — this is what makes the plane FOLLOW THE
|
|
68
|
+
// MOUSE natively like the original web game.
|
|
69
|
+
static double gGeaPointerX = 0.0; // view coords, points, top-left origin
|
|
70
|
+
static double gGeaPointerY = 0.0;
|
|
71
|
+
static double gGeaPointerW = 0.0; // view size at the last pointer event
|
|
72
|
+
static double gGeaPointerH = 0.0;
|
|
73
|
+
static double gGeaPointerSeq = 0.0; // increments on every pointer event
|
|
74
|
+
static bool gGeaPointerDown = false; // primary button
|
|
75
|
+
|
|
76
|
+
// gea_three_pointer_state(channel): 0=x, 1=y (top-left origin), 2=view width,
|
|
77
|
+
// 3=view height (same units as x/y), 4=move sequence (0 = pointer never
|
|
78
|
+
// seen; changes on every pointer event), 5=primary button (1 = down).
|
|
79
|
+
//
|
|
80
|
+
// GEA_THREE_SYNTH_POINTER="<x0>,<y0>:<x1>,<y1>:<startMs>:<endMs>" — perf/CI
|
|
81
|
+
// hook, the pointer twin of GEA_THREE_SYNTH_KEY: report a pointer sweeping
|
|
82
|
+
// linearly from (x0,y0) to (x1,y1) during [startMs, endMs) after process
|
|
83
|
+
// start, then hold the end position. Coordinates are NORMALIZED [0,1]
|
|
84
|
+
// fractions of the view (top-left origin), reported with width=height=1, so
|
|
85
|
+
// the sweep is resolution-independent. Combine with GEA_THREE_SYNTH_KEY
|
|
86
|
+
// (Space=49) to start the game headlessly and steer the plane without
|
|
87
|
+
// Accessibility permission for real event injection.
|
|
88
|
+
extern "C" double gea_three_pointer_state(double channel) {
|
|
89
|
+
static bool synthParsed = false;
|
|
90
|
+
static bool synthActive = false;
|
|
91
|
+
static bool synthLatched = false;
|
|
92
|
+
static double sx0 = 0.0, sy0 = 0.0, sx1 = 0.0, sy1 = 0.0;
|
|
93
|
+
static long long synthStartMs = 0, synthEndMs = 0;
|
|
94
|
+
static std::chrono::steady_clock::time_point processStart = std::chrono::steady_clock::now();
|
|
95
|
+
if (!synthParsed) {
|
|
96
|
+
synthParsed = true;
|
|
97
|
+
if (const char *spec = std::getenv("GEA_THREE_SYNTH_POINTER")) {
|
|
98
|
+
double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
|
|
99
|
+
long long s = 0, e = 0;
|
|
100
|
+
if (std::sscanf(spec, "%lf,%lf:%lf,%lf:%lld:%lld", &x0, &y0, &x1, &y1, &s, &e) == 6 && e > s) {
|
|
101
|
+
sx0 = x0; sy0 = y0; sx1 = x1; sy1 = y1;
|
|
102
|
+
synthStartMs = s;
|
|
103
|
+
synthEndMs = e;
|
|
104
|
+
synthActive = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (synthActive) {
|
|
109
|
+
const long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
110
|
+
std::chrono::steady_clock::now() - processStart)
|
|
111
|
+
.count();
|
|
112
|
+
if (elapsed >= synthStartMs && (elapsed < synthEndMs || !synthLatched)) {
|
|
113
|
+
const double t = std::min(1.0, static_cast<double>(elapsed - synthStartMs) /
|
|
114
|
+
static_cast<double>(synthEndMs - synthStartMs));
|
|
115
|
+
gGeaPointerX = sx0 + (sx1 - sx0) * t;
|
|
116
|
+
gGeaPointerY = sy0 + (sy1 - sy0) * t;
|
|
117
|
+
gGeaPointerW = 1.0;
|
|
118
|
+
gGeaPointerH = 1.0;
|
|
119
|
+
// Monotonic per-millisecond sequence: a fresh value each frame during
|
|
120
|
+
// the sweep, one final tick to latch the end position.
|
|
121
|
+
gGeaPointerSeq = 1.0 + static_cast<double>(elapsed - synthStartMs);
|
|
122
|
+
if (elapsed >= synthEndMs) synthLatched = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
switch (static_cast<int>(channel)) {
|
|
126
|
+
case 0: return gGeaPointerX;
|
|
127
|
+
case 1: return gGeaPointerY;
|
|
128
|
+
case 2: return gGeaPointerW;
|
|
129
|
+
case 3: return gGeaPointerH;
|
|
130
|
+
case 4: return gGeaPointerSeq;
|
|
131
|
+
case 5: return gGeaPointerDown ? 1.0 : 0.0;
|
|
132
|
+
}
|
|
133
|
+
return 0.0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Forward declaration for geaInstallPointerMonitor (defined after the state
|
|
137
|
+
// struct; it needs gWebGL.hostView).
|
|
138
|
+
static void geaInstallPointerMonitor(void);
|
|
139
|
+
|
|
140
|
+
extern "C" bool gea_three_key_pressed(double code) {
|
|
141
|
+
const int c = static_cast<int>(code);
|
|
142
|
+
if (c < 0 || c >= 256) return false;
|
|
143
|
+
// GEA_THREE_SYNTH_KEY="<vk>:<startMs>:<endMs>" — perf/CI hook: report the
|
|
144
|
+
// key held during [startMs, endMs) after process start. Drives game-state
|
|
145
|
+
// transitions (e.g. Space=49 to leave the splash) in headless runs where
|
|
146
|
+
// no Accessibility permission is available for real event injection.
|
|
147
|
+
static int synthKey = -1;
|
|
148
|
+
static long long synthStartMs = 0, synthEndMs = 0;
|
|
149
|
+
static std::chrono::steady_clock::time_point processStart = std::chrono::steady_clock::now();
|
|
150
|
+
static bool synthParsed = false;
|
|
151
|
+
if (!synthParsed) {
|
|
152
|
+
synthParsed = true;
|
|
153
|
+
if (const char *spec = std::getenv("GEA_THREE_SYNTH_KEY")) {
|
|
154
|
+
long long k = -1, s = 0, e = 0;
|
|
155
|
+
if (std::sscanf(spec, "%lld:%lld:%lld", &k, &s, &e) == 3) {
|
|
156
|
+
synthKey = static_cast<int>(k);
|
|
157
|
+
synthStartMs = s;
|
|
158
|
+
synthEndMs = e;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (c == synthKey) {
|
|
163
|
+
const long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
164
|
+
std::chrono::steady_clock::now() - processStart)
|
|
165
|
+
.count();
|
|
166
|
+
if (elapsed >= synthStartMs && elapsed < synthEndMs) return true;
|
|
167
|
+
}
|
|
168
|
+
return gGeaKeyState[c];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#if !defined(_WIN32)
|
|
172
|
+
@interface GeaAngleWebGLHostView : NSView
|
|
173
|
+
@end
|
|
174
|
+
|
|
175
|
+
@implementation GeaAngleWebGLHostView
|
|
176
|
+
- (instancetype)initWithFrame:(NSRect)frame {
|
|
177
|
+
self = [super initWithFrame:frame];
|
|
178
|
+
if (self) {
|
|
179
|
+
self.wantsLayer = YES;
|
|
180
|
+
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
181
|
+
}
|
|
182
|
+
return self;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Accept key events (become first responder) so keyDown:/keyUp: fire.
|
|
186
|
+
- (BOOL)acceptsFirstResponder { return YES; }
|
|
187
|
+
|
|
188
|
+
- (void)viewDidMoveToWindow {
|
|
189
|
+
[super viewDidMoveToWindow];
|
|
190
|
+
if (self.window) {
|
|
191
|
+
[self.window makeFirstResponder:self];
|
|
192
|
+
// Mouse-move events (no button held) are only delivered when the window
|
|
193
|
+
// accepts them; the NSTrackingArea below scopes them to this view.
|
|
194
|
+
self.window.acceptsMouseMovedEvents = YES;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Track mouse movement over the view without requiring a button press.
|
|
199
|
+
// NSTrackingInVisibleRect keeps the area glued to the view's visible bounds
|
|
200
|
+
// across resizes (the rect argument is ignored).
|
|
201
|
+
- (void)updateTrackingAreas {
|
|
202
|
+
[super updateTrackingAreas];
|
|
203
|
+
for (NSTrackingArea *area in [self.trackingAreas copy]) [self removeTrackingArea:area];
|
|
204
|
+
[self addTrackingArea:[[NSTrackingArea alloc]
|
|
205
|
+
initWithRect:NSZeroRect
|
|
206
|
+
options:NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
|
|
207
|
+
owner:self
|
|
208
|
+
userInfo:nil]];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Pointer RECORDING lives in a window-level NSEvent local monitor (see
|
|
212
|
+
// geaInstallPointerMonitor, installed from gea_three_webgl_attach), NOT in
|
|
213
|
+
// per-view mouseMoved:/mouseDown: overrides: AppKit delivers mouse events to
|
|
214
|
+
// the TOPMOST hit-tested view, so any sibling/overlay view above this one
|
|
215
|
+
// would carve pointer dead zones out of the game surface. The local monitor
|
|
216
|
+
// observes every mouse event dispatched to the window regardless of which
|
|
217
|
+
// view hit-tests it. The tracking area above (plus acceptsMouseMovedEvents)
|
|
218
|
+
// is still required — it is what makes AppKit GENERATE mouse-moved events for
|
|
219
|
+
// the window at all; NSView's default mouseMoved: then simply does nothing.
|
|
220
|
+
|
|
221
|
+
- (void)keyDown:(NSEvent *)event {
|
|
222
|
+
unsigned short code = event.keyCode;
|
|
223
|
+
if (code < 256) gGeaKeyState[code] = true;
|
|
224
|
+
// Swallow the event (no super) so AppKit doesn't emit the system "funk" beep
|
|
225
|
+
// for keys with no menu/command binding.
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
- (void)keyUp:(NSEvent *)event {
|
|
229
|
+
unsigned short code = event.keyCode;
|
|
230
|
+
if (code < 256) gGeaKeyState[code] = false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
- (void)flagsChanged:(NSEvent *)event {
|
|
234
|
+
// Track modifier keys as their own key codes too (Shift/Ctrl/etc.), pressed
|
|
235
|
+
// while the corresponding mask bit is set.
|
|
236
|
+
unsigned short code = event.keyCode;
|
|
237
|
+
if (code < 256) gGeaKeyState[code] = (event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) != 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
- (CALayer *)makeBackingLayer {
|
|
241
|
+
CAMetalLayer *layer = [CAMetalLayer layer];
|
|
242
|
+
layer.opaque = YES;
|
|
243
|
+
layer.backgroundColor = NSColor.blackColor.CGColor;
|
|
244
|
+
layer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
|
|
245
|
+
layer.needsDisplayOnBoundsChange = YES;
|
|
246
|
+
layer.presentsWithTransaction = NO;
|
|
247
|
+
layer.displaySyncEnabled = YES;
|
|
248
|
+
// Block on nextDrawable instead of dropping the frame. With the default
|
|
249
|
+
// allowsNextDrawableTimeout=YES, [layer nextDrawable] returns nil while the
|
|
250
|
+
// layer is still being realized on-screen at startup (and immediately after a
|
|
251
|
+
// surface rebind); ANGLE then presents nothing and the opaque black
|
|
252
|
+
// backgroundColor shows through — an intermittent black window for the first
|
|
253
|
+
// stretch of frames. Waiting for the drawable keeps rendered content on
|
|
254
|
+
// screen from the first frame.
|
|
255
|
+
layer.allowsNextDrawableTimeout = NO;
|
|
256
|
+
layer.maximumDrawableCount = 3;
|
|
257
|
+
return layer;
|
|
258
|
+
}
|
|
259
|
+
@end
|
|
260
|
+
#endif
|
|
261
|
+
|
|
262
|
+
static constexpr EGLDisplay EGL_NO_DISPLAY = nullptr;
|
|
263
|
+
static constexpr EGLSurface EGL_NO_SURFACE = nullptr;
|
|
264
|
+
static constexpr EGLContext EGL_NO_CONTEXT = nullptr;
|
|
265
|
+
static constexpr EGLNativeDisplayType EGL_DEFAULT_DISPLAY = nullptr;
|
|
266
|
+
static constexpr EGLBoolean EGL_FALSE_VALUE = 0;
|
|
267
|
+
|
|
268
|
+
static constexpr EGLint EGL_NONE = 0x3038;
|
|
269
|
+
static constexpr EGLint EGL_WIDTH = 0x3057;
|
|
270
|
+
static constexpr EGLint EGL_HEIGHT = 0x3056;
|
|
271
|
+
static constexpr EGLint EGL_SURFACE_TYPE = 0x3033;
|
|
272
|
+
static constexpr EGLint EGL_WINDOW_BIT = 0x0004;
|
|
273
|
+
static constexpr EGLint EGL_PBUFFER_BIT = 0x0001;
|
|
274
|
+
static constexpr EGLint EGL_RENDERABLE_TYPE = 0x3040;
|
|
275
|
+
static constexpr EGLint EGL_OPENGL_ES2_BIT = 0x0004;
|
|
276
|
+
static constexpr EGLint EGL_OPENGL_ES3_BIT = 0x0040;
|
|
277
|
+
static constexpr EGLint EGL_RED_SIZE = 0x3024;
|
|
278
|
+
static constexpr EGLint EGL_GREEN_SIZE = 0x3023;
|
|
279
|
+
static constexpr EGLint EGL_BLUE_SIZE = 0x3022;
|
|
280
|
+
static constexpr EGLint EGL_ALPHA_SIZE = 0x3021;
|
|
281
|
+
static constexpr EGLint EGL_DEPTH_SIZE = 0x3025;
|
|
282
|
+
static constexpr EGLint EGL_STENCIL_SIZE = 0x3026;
|
|
283
|
+
static constexpr EGLint EGL_CONTEXT_CLIENT_VERSION = 0x3098;
|
|
284
|
+
static constexpr EGLenum EGL_OPENGL_ES_API = 0x30A0;
|
|
285
|
+
static constexpr EGLenum EGL_PLATFORM_ANGLE_ANGLE = 0x3202;
|
|
286
|
+
static constexpr EGLint EGL_PLATFORM_ANGLE_TYPE_ANGLE = 0x3203;
|
|
287
|
+
static constexpr EGLint EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE = 0x3489;
|
|
288
|
+
|
|
289
|
+
static constexpr GLboolean GL_FALSE_VALUE = 0;
|
|
290
|
+
static constexpr GLboolean GL_TRUE_VALUE = 1;
|
|
291
|
+
static constexpr GLenum GL_FLOAT = 0x1406;
|
|
292
|
+
static constexpr GLenum GL_UNSIGNED_BYTE = 0x1401;
|
|
293
|
+
static constexpr GLenum GL_UNSIGNED_SHORT = 0x1403;
|
|
294
|
+
static constexpr GLenum GL_UNSIGNED_INT = 0x1405;
|
|
295
|
+
static constexpr GLenum GL_RGBA_VALUE = 0x1908;
|
|
296
|
+
static constexpr GLenum GL_VIEWPORT_VALUE = 0x0BA2;
|
|
297
|
+
static constexpr GLenum GL_FRAMEBUFFER_BINDING_VALUE = 0x8CA6;
|
|
298
|
+
|
|
299
|
+
using PFNEGLGETPROCADDRESS = void *(*)(const char *);
|
|
300
|
+
using PFNEGLGETPLATFORMDISPLAYEXTPROC = EGLDisplay (*)(EGLenum, void *, const EGLint *);
|
|
301
|
+
using PFNEGLGETERRORPROC = EGLint (*)();
|
|
302
|
+
using PFNEGLINITIALIZEPROC = EGLBoolean (*)(EGLDisplay, EGLint *, EGLint *);
|
|
303
|
+
using PFNEGLBINDAPIPROC = EGLBoolean (*)(EGLenum);
|
|
304
|
+
using PFNEGLCHOOSECONFIGPROC = EGLBoolean (*)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
|
|
305
|
+
using PFNEGLCREATEWINDOWSURFACEPROC = EGLSurface (*)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *);
|
|
306
|
+
using PFNEGLCREATEPBUFFERSURFACEPROC = EGLSurface (*)(EGLDisplay, EGLConfig, const EGLint *);
|
|
307
|
+
using PFNEGLCREATECONTEXTPROC = EGLContext (*)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
|
|
308
|
+
using PFNEGLDESTROYSURFACEPROC = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
|
309
|
+
using PFNEGLMAKECURRENTPROC = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
|
|
310
|
+
using PFNEGLSWAPBUFFERSPROC = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
|
311
|
+
using PFNEGLSWAPINTERVALPROC = EGLBoolean (*)(EGLDisplay, EGLint);
|
|
312
|
+
|
|
313
|
+
using PFNGLACTIVETEXTUREPROC = void (*)(GLenum);
|
|
314
|
+
using PFNGLATTACHSHADERPROC = void (*)(GLuint, GLuint);
|
|
315
|
+
using PFNGLBINDBUFFERPROC = void (*)(GLenum, GLuint);
|
|
316
|
+
using PFNGLBINDFRAMEBUFFERPROC = void (*)(GLenum, GLuint);
|
|
317
|
+
using PFNGLBINDRENDERBUFFERPROC = void (*)(GLenum, GLuint);
|
|
318
|
+
using PFNGLBINDTEXTUREPROC = void (*)(GLenum, GLuint);
|
|
319
|
+
using PFNGLBINDVERTEXARRAYPROC = void (*)(GLuint);
|
|
320
|
+
using PFNGLBLENDEQUATIONPROC = void (*)(GLenum);
|
|
321
|
+
using PFNGLBLENDEQUATIONSEPARATEPROC = void (*)(GLenum, GLenum);
|
|
322
|
+
using PFNGLBLENDFUNCPROC = void (*)(GLenum, GLenum);
|
|
323
|
+
using PFNGLBLENDFUNCSEPARATEPROC = void (*)(GLenum, GLenum, GLenum, GLenum);
|
|
324
|
+
using PFNGLBUFFERDATAPROC = void (*)(GLenum, GLsizeiptr, const void *, GLenum);
|
|
325
|
+
using PFNGLCOPYBUFFERSUBDATAPROC = void (*)(GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr);
|
|
326
|
+
using PFNGLBUFFERSUBDATAPROC = void (*)(GLenum, GLintptr, GLsizeiptr, const void *);
|
|
327
|
+
using PFNGLCHECKFRAMEBUFFERSTATUSPROC = GLenum (*)(GLenum);
|
|
328
|
+
using PFNGLCLEARPROC = void (*)(GLbitfield);
|
|
329
|
+
using PFNGLCLEARCOLORPROC = void (*)(GLfloat, GLfloat, GLfloat, GLfloat);
|
|
330
|
+
using PFNGLCLEARDEPTHFPROC = void (*)(GLfloat);
|
|
331
|
+
using PFNGLCLEARSTENCILPROC = void (*)(GLint);
|
|
332
|
+
using PFNGLCOLORMASKPROC = void (*)(GLboolean, GLboolean, GLboolean, GLboolean);
|
|
333
|
+
using PFNGLCOMPILESHADERPROC = void (*)(GLuint);
|
|
334
|
+
using PFNGLCREATEPROGRAMPROC = GLuint (*)();
|
|
335
|
+
using PFNGLCREATESHADERPROC = GLuint (*)(GLenum);
|
|
336
|
+
using PFNGLCULLFACEPROC = void (*)(GLenum);
|
|
337
|
+
using PFNGLDELETEBUFFERSPROC = void (*)(GLsizei, const GLuint *);
|
|
338
|
+
using PFNGLDELETEFRAMEBUFFERSPROC = void (*)(GLsizei, const GLuint *);
|
|
339
|
+
using PFNGLDELETEPROGRAMPROC = void (*)(GLuint);
|
|
340
|
+
using PFNGLDELETERENDERBUFFERSPROC = void (*)(GLsizei, const GLuint *);
|
|
341
|
+
using PFNGLDELETESHADERPROC = void (*)(GLuint);
|
|
342
|
+
using PFNGLDELETETEXTURESPROC = void (*)(GLsizei, const GLuint *);
|
|
343
|
+
using PFNGLDELETEVERTEXARRAYSPROC = void (*)(GLsizei, const GLuint *);
|
|
344
|
+
using PFNGLDEPTHFUNCPROC = void (*)(GLenum);
|
|
345
|
+
using PFNGLDEPTHMASKPROC = void (*)(GLboolean);
|
|
346
|
+
using PFNGLDISABLEPROC = void (*)(GLenum);
|
|
347
|
+
using PFNGLDISABLEVERTEXATTRIBARRAYPROC = void (*)(GLuint);
|
|
348
|
+
using PFNGLDRAWARRAYSPROC = void (*)(GLenum, GLint, GLsizei);
|
|
349
|
+
using PFNGLDRAWARRAYSINSTANCEDPROC = void (*)(GLenum, GLint, GLsizei, GLsizei);
|
|
350
|
+
using PFNGLDRAWBUFFERSPROC = void (*)(GLsizei, const GLenum *);
|
|
351
|
+
using PFNGLDRAWELEMENTSPROC = void (*)(GLenum, GLsizei, GLenum, const void *);
|
|
352
|
+
using PFNGLDRAWELEMENTSINSTANCEDPROC = void (*)(GLenum, GLsizei, GLenum, const void *, GLsizei);
|
|
353
|
+
using PFNGLENABLEPROC = void (*)(GLenum);
|
|
354
|
+
using PFNGLENABLEVERTEXATTRIBARRAYPROC = void (*)(GLuint);
|
|
355
|
+
using PFNGLFRAMEBUFFERRENDERBUFFERPROC = void (*)(GLenum, GLenum, GLenum, GLuint);
|
|
356
|
+
using PFNGLFRAMEBUFFERTEXTURE2DPROC = void (*)(GLenum, GLenum, GLenum, GLuint, GLint);
|
|
357
|
+
using PFNGLFRONTFACEPROC = void (*)(GLenum);
|
|
358
|
+
using PFNGLGENBUFFERSPROC = void (*)(GLsizei, GLuint *);
|
|
359
|
+
using PFNGLGENFRAMEBUFFERSPROC = void (*)(GLsizei, GLuint *);
|
|
360
|
+
using PFNGLGENRENDERBUFFERSPROC = void (*)(GLsizei, GLuint *);
|
|
361
|
+
using PFNGLGENTEXTURESPROC = void (*)(GLsizei, GLuint *);
|
|
362
|
+
using PFNGLGENVERTEXARRAYSPROC = void (*)(GLsizei, GLuint *);
|
|
363
|
+
using PFNGLGENERATEMIPMAPPROC = void (*)(GLenum);
|
|
364
|
+
using PFNGLGETACTIVEATTRIBPROC = void (*)(GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *);
|
|
365
|
+
using PFNGLGETACTIVEUNIFORMPROC = void (*)(GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *);
|
|
366
|
+
using PFNGLGETATTRIBLOCATIONPROC = GLint (*)(GLuint, const GLchar *);
|
|
367
|
+
using PFNGLGETERRORPROC = GLenum (*)();
|
|
368
|
+
using PFNGLGETINTEGERVPROC = void (*)(GLenum, GLint *);
|
|
369
|
+
using PFNGLGETPROGRAMIVPROC = void (*)(GLuint, GLenum, GLint *);
|
|
370
|
+
using PFNGLGETSHADERIVPROC = void (*)(GLuint, GLenum, GLint *);
|
|
371
|
+
using PFNGLGETSHADERINFOLOGPROC = void (*)(GLuint, GLsizei, GLsizei *, GLchar *);
|
|
372
|
+
using PFNGLGETSTRINGPROC = const unsigned char *(*)(GLenum);
|
|
373
|
+
using PFNGLGETUNIFORMLOCATIONPROC = GLint (*)(GLuint, const GLchar *);
|
|
374
|
+
using PFNGLLINEWIDTHPROC = void (*)(GLfloat);
|
|
375
|
+
using PFNGLLINKPROGRAMPROC = void (*)(GLuint);
|
|
376
|
+
using PFNGLPIXELSTOREIPROC = void (*)(GLenum, GLint);
|
|
377
|
+
using PFNGLPOLYGONOFFSETPROC = void (*)(GLfloat, GLfloat);
|
|
378
|
+
using PFNGLREADPIXELSPROC = void (*)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void *);
|
|
379
|
+
using PFNGLRENDERBUFFERSTORAGEPROC = void (*)(GLenum, GLenum, GLsizei, GLsizei);
|
|
380
|
+
using PFNGLSCISSORPROC = void (*)(GLint, GLint, GLsizei, GLsizei);
|
|
381
|
+
using PFNGLSHADERSOURCEPROC = void (*)(GLuint, GLsizei, const GLchar *const *, const GLint *);
|
|
382
|
+
using PFNGLSTENCILFUNCPROC = void (*)(GLenum, GLint, GLuint);
|
|
383
|
+
using PFNGLSTENCILFUNCSEPARATEPROC = void (*)(GLenum, GLenum, GLint, GLuint);
|
|
384
|
+
using PFNGLSTENCILMASKPROC = void (*)(GLuint);
|
|
385
|
+
using PFNGLSTENCILMASKSEPARATEPROC = void (*)(GLenum, GLuint);
|
|
386
|
+
using PFNGLSTENCILOPPROC = void (*)(GLenum, GLenum, GLenum);
|
|
387
|
+
using PFNGLSTENCILOPSEPARATEPROC = void (*)(GLenum, GLenum, GLenum, GLenum);
|
|
388
|
+
using PFNGLTEXIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *);
|
|
389
|
+
using PFNGLTEXPARAMETERFPROC = void (*)(GLenum, GLenum, GLfloat);
|
|
390
|
+
using PFNGLTEXPARAMETERIPROC = void (*)(GLenum, GLenum, GLint);
|
|
391
|
+
using PFNGLTEXSUBIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void *);
|
|
392
|
+
using PFNGLTEXSTORAGE2DPROC = void (*)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);
|
|
393
|
+
using PFNGLUNIFORM1FPROC = void (*)(GLint, GLfloat);
|
|
394
|
+
using PFNGLUNIFORM1FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
|
|
395
|
+
using PFNGLUNIFORM1IPROC = void (*)(GLint, GLint);
|
|
396
|
+
using PFNGLUNIFORM1IVPROC = void (*)(GLint, GLsizei, const GLint *);
|
|
397
|
+
using PFNGLUNIFORM1UIPROC = void (*)(GLint, GLuint);
|
|
398
|
+
using PFNGLUNIFORM1UIVPROC = void (*)(GLint, GLsizei, const GLuint *);
|
|
399
|
+
using PFNGLUNIFORM2FPROC = void (*)(GLint, GLfloat, GLfloat);
|
|
400
|
+
using PFNGLUNIFORM2FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
|
|
401
|
+
using PFNGLUNIFORM2IPROC = void (*)(GLint, GLint, GLint);
|
|
402
|
+
using PFNGLUNIFORM2IVPROC = void (*)(GLint, GLsizei, const GLint *);
|
|
403
|
+
using PFNGLUNIFORM2UIPROC = void (*)(GLint, GLuint, GLuint);
|
|
404
|
+
using PFNGLUNIFORM2UIVPROC = void (*)(GLint, GLsizei, const GLuint *);
|
|
405
|
+
using PFNGLUNIFORM3FPROC = void (*)(GLint, GLfloat, GLfloat, GLfloat);
|
|
406
|
+
using PFNGLUNIFORM3FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
|
|
407
|
+
using PFNGLUNIFORM3IPROC = void (*)(GLint, GLint, GLint, GLint);
|
|
408
|
+
using PFNGLUNIFORM3IVPROC = void (*)(GLint, GLsizei, const GLint *);
|
|
409
|
+
using PFNGLUNIFORM3UIPROC = void (*)(GLint, GLuint, GLuint, GLuint);
|
|
410
|
+
using PFNGLUNIFORM3UIVPROC = void (*)(GLint, GLsizei, const GLuint *);
|
|
411
|
+
using PFNGLUNIFORM4FPROC = void (*)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
|
|
412
|
+
using PFNGLUNIFORM4FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
|
|
413
|
+
using PFNGLUNIFORM4IPROC = void (*)(GLint, GLint, GLint, GLint, GLint);
|
|
414
|
+
using PFNGLUNIFORM4IVPROC = void (*)(GLint, GLsizei, const GLint *);
|
|
415
|
+
using PFNGLUNIFORM4UIPROC = void (*)(GLint, GLuint, GLuint, GLuint, GLuint);
|
|
416
|
+
using PFNGLUNIFORM4UIVPROC = void (*)(GLint, GLsizei, const GLuint *);
|
|
417
|
+
using PFNGLUNIFORMMATRIX2FVPROC = void (*)(GLint, GLsizei, GLboolean, const GLfloat *);
|
|
418
|
+
using PFNGLUNIFORMMATRIX3FVPROC = void (*)(GLint, GLsizei, GLboolean, const GLfloat *);
|
|
419
|
+
using PFNGLUNIFORMMATRIX4FVPROC = void (*)(GLint, GLsizei, GLboolean, const GLfloat *);
|
|
420
|
+
using PFNGLUSEPROGRAMPROC = void (*)(GLuint);
|
|
421
|
+
using PFNGLVERTEXATTRIBDIVISORPROC = void (*)(GLuint, GLuint);
|
|
422
|
+
using PFNGLVERTEXATTRIBPOINTERPROC = void (*)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void *);
|
|
423
|
+
using PFNGLVIEWPORTPROC = void (*)(GLint, GLint, GLsizei, GLsizei);
|
|
424
|
+
|
|
425
|
+
struct AngleWebGLState {
|
|
426
|
+
void *egl = nullptr;
|
|
427
|
+
void *gles = nullptr;
|
|
428
|
+
EGLDisplay display = EGL_NO_DISPLAY;
|
|
429
|
+
EGLConfig config = nullptr;
|
|
430
|
+
EGLSurface surface = EGL_NO_SURFACE;
|
|
431
|
+
EGLContext context = EGL_NO_CONTEXT;
|
|
432
|
+
PFNEGLGETPROCADDRESS eglGetProcAddress = nullptr;
|
|
433
|
+
PFNEGLGETERRORPROC eglGetError = nullptr;
|
|
434
|
+
PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurface = nullptr;
|
|
435
|
+
PFNEGLCREATEPBUFFERSURFACEPROC eglCreatePbufferSurface = nullptr;
|
|
436
|
+
PFNEGLDESTROYSURFACEPROC eglDestroySurface = nullptr;
|
|
437
|
+
PFNEGLMAKECURRENTPROC eglMakeCurrent = nullptr;
|
|
438
|
+
PFNEGLSWAPBUFFERSPROC eglSwapBuffers = nullptr;
|
|
439
|
+
PFNEGLSWAPINTERVALPROC eglSwapInterval = nullptr;
|
|
440
|
+
|
|
441
|
+
PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
|
|
442
|
+
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
|
|
443
|
+
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
|
|
444
|
+
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
|
|
445
|
+
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr;
|
|
446
|
+
PFNGLBINDTEXTUREPROC glBindTexture = nullptr;
|
|
447
|
+
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
|
|
448
|
+
PFNGLBLENDEQUATIONPROC glBlendEquation = nullptr;
|
|
449
|
+
PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate = nullptr;
|
|
450
|
+
PFNGLBLENDFUNCPROC glBlendFunc = nullptr;
|
|
451
|
+
PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate = nullptr;
|
|
452
|
+
PFNGLBUFFERDATAPROC glBufferData = nullptr;
|
|
453
|
+
PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr;
|
|
454
|
+
PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData = nullptr;
|
|
455
|
+
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr;
|
|
456
|
+
PFNGLCLEARPROC glClear = nullptr;
|
|
457
|
+
PFNGLCLEARCOLORPROC glClearColor = nullptr;
|
|
458
|
+
PFNGLCLEARDEPTHFPROC glClearDepthf = nullptr;
|
|
459
|
+
PFNGLCLEARSTENCILPROC glClearStencil = nullptr;
|
|
460
|
+
PFNGLCOLORMASKPROC glColorMask = nullptr;
|
|
461
|
+
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
|
|
462
|
+
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
|
|
463
|
+
PFNGLCREATESHADERPROC glCreateShader = nullptr;
|
|
464
|
+
PFNGLCULLFACEPROC glCullFace = nullptr;
|
|
465
|
+
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
|
|
466
|
+
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
|
|
467
|
+
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
|
|
468
|
+
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr;
|
|
469
|
+
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
|
|
470
|
+
PFNGLDELETETEXTURESPROC glDeleteTextures = nullptr;
|
|
471
|
+
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
|
|
472
|
+
PFNGLDEPTHFUNCPROC glDepthFunc = nullptr;
|
|
473
|
+
PFNGLDEPTHMASKPROC glDepthMask = nullptr;
|
|
474
|
+
PFNGLDISABLEPROC glDisable = nullptr;
|
|
475
|
+
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
|
|
476
|
+
PFNGLDRAWARRAYSPROC glDrawArrays = nullptr;
|
|
477
|
+
PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced = nullptr;
|
|
478
|
+
PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr;
|
|
479
|
+
PFNGLDRAWELEMENTSPROC glDrawElements = nullptr;
|
|
480
|
+
PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced = nullptr;
|
|
481
|
+
PFNGLENABLEPROC glEnable = nullptr;
|
|
482
|
+
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
|
|
483
|
+
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr;
|
|
484
|
+
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
|
|
485
|
+
PFNGLFRONTFACEPROC glFrontFace = nullptr;
|
|
486
|
+
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
|
|
487
|
+
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
|
|
488
|
+
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr;
|
|
489
|
+
PFNGLGENTEXTURESPROC glGenTextures = nullptr;
|
|
490
|
+
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
|
|
491
|
+
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;
|
|
492
|
+
PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = nullptr;
|
|
493
|
+
PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = nullptr;
|
|
494
|
+
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = nullptr;
|
|
495
|
+
PFNGLGETERRORPROC glGetError = nullptr;
|
|
496
|
+
PFNGLGETINTEGERVPROC glGetIntegerv = nullptr;
|
|
497
|
+
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
|
|
498
|
+
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
|
|
499
|
+
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
|
|
500
|
+
PFNGLGETSTRINGPROC glGetString = nullptr;
|
|
501
|
+
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
|
|
502
|
+
PFNGLLINEWIDTHPROC glLineWidth = nullptr;
|
|
503
|
+
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
|
|
504
|
+
PFNGLPIXELSTOREIPROC glPixelStorei = nullptr;
|
|
505
|
+
PFNGLPOLYGONOFFSETPROC glPolygonOffset = nullptr;
|
|
506
|
+
PFNGLREADPIXELSPROC glReadPixels = nullptr;
|
|
507
|
+
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
|
|
508
|
+
PFNGLSCISSORPROC glScissor = nullptr;
|
|
509
|
+
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
|
|
510
|
+
PFNGLSTENCILFUNCPROC glStencilFunc = nullptr;
|
|
511
|
+
PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate = nullptr;
|
|
512
|
+
PFNGLSTENCILMASKPROC glStencilMask = nullptr;
|
|
513
|
+
PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate = nullptr;
|
|
514
|
+
PFNGLSTENCILOPPROC glStencilOp = nullptr;
|
|
515
|
+
PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate = nullptr;
|
|
516
|
+
PFNGLTEXIMAGE2DPROC glTexImage2D = nullptr;
|
|
517
|
+
PFNGLTEXPARAMETERFPROC glTexParameterf = nullptr;
|
|
518
|
+
PFNGLTEXPARAMETERIPROC glTexParameteri = nullptr;
|
|
519
|
+
PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D = nullptr;
|
|
520
|
+
PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr;
|
|
521
|
+
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
|
|
522
|
+
PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
|
|
523
|
+
PFNGLUNIFORM1IPROC glUniform1i = nullptr;
|
|
524
|
+
PFNGLUNIFORM1IVPROC glUniform1iv = nullptr;
|
|
525
|
+
PFNGLUNIFORM1UIPROC glUniform1ui = nullptr;
|
|
526
|
+
PFNGLUNIFORM1UIVPROC glUniform1uiv = nullptr;
|
|
527
|
+
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
|
|
528
|
+
PFNGLUNIFORM2FVPROC glUniform2fv = nullptr;
|
|
529
|
+
PFNGLUNIFORM2IPROC glUniform2i = nullptr;
|
|
530
|
+
PFNGLUNIFORM2IVPROC glUniform2iv = nullptr;
|
|
531
|
+
PFNGLUNIFORM2UIPROC glUniform2ui = nullptr;
|
|
532
|
+
PFNGLUNIFORM2UIVPROC glUniform2uiv = nullptr;
|
|
533
|
+
PFNGLUNIFORM3FPROC glUniform3f = nullptr;
|
|
534
|
+
PFNGLUNIFORM3FVPROC glUniform3fv = nullptr;
|
|
535
|
+
PFNGLUNIFORM3IPROC glUniform3i = nullptr;
|
|
536
|
+
PFNGLUNIFORM3IVPROC glUniform3iv = nullptr;
|
|
537
|
+
PFNGLUNIFORM3UIPROC glUniform3ui = nullptr;
|
|
538
|
+
PFNGLUNIFORM3UIVPROC glUniform3uiv = nullptr;
|
|
539
|
+
PFNGLUNIFORM4FPROC glUniform4f = nullptr;
|
|
540
|
+
PFNGLUNIFORM4FVPROC glUniform4fv = nullptr;
|
|
541
|
+
PFNGLUNIFORM4IPROC glUniform4i = nullptr;
|
|
542
|
+
PFNGLUNIFORM4IVPROC glUniform4iv = nullptr;
|
|
543
|
+
PFNGLUNIFORM4UIPROC glUniform4ui = nullptr;
|
|
544
|
+
PFNGLUNIFORM4UIVPROC glUniform4uiv = nullptr;
|
|
545
|
+
PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv = nullptr;
|
|
546
|
+
PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv = nullptr;
|
|
547
|
+
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
|
|
548
|
+
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
|
|
549
|
+
PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = nullptr;
|
|
550
|
+
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
|
|
551
|
+
PFNGLVIEWPORTPROC glViewport = nullptr;
|
|
552
|
+
|
|
553
|
+
#if defined(_WIN32)
|
|
554
|
+
void *nativeWindow = nullptr;
|
|
555
|
+
#else
|
|
556
|
+
NSView *hostView = nil;
|
|
557
|
+
CAMetalLayer *metalLayer = nil;
|
|
558
|
+
#endif
|
|
559
|
+
int widthPx = 1;
|
|
560
|
+
int heightPx = 1;
|
|
561
|
+
double devicePixelRatio = 1.0;
|
|
562
|
+
bool ready = false;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
static AngleWebGLState gWebGL;
|
|
566
|
+
static std::chrono::steady_clock::time_point gProfileSizeSync;
|
|
567
|
+
static bool gProfileSizeSyncPending = false;
|
|
568
|
+
static bool nativeFrameProfileEnabled() {
|
|
569
|
+
static const bool enabled = std::getenv("GEA_NATIVE_FRAME_PROFILE") != nullptr;
|
|
570
|
+
return enabled;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Diagnostic switches are launch settings, like nativeFrameProfileEnabled.
|
|
574
|
+
// Cache them lazily, after UWP has applied its launch configuration: repeated
|
|
575
|
+
// getenv calls acquire the CRT environment lock in per-draw uniform/state paths.
|
|
576
|
+
static bool nativeWebGLTrapEnabled() {
|
|
577
|
+
static const bool enabled = std::getenv("GEA_WEBGL_TRAP") != nullptr;
|
|
578
|
+
return enabled;
|
|
579
|
+
}
|
|
580
|
+
static bool nativeBlendDebugEnabled() {
|
|
581
|
+
static const bool enabled = std::getenv("GEA_WEBGL_BLEND_DEBUG") != nullptr;
|
|
582
|
+
return enabled;
|
|
583
|
+
}
|
|
584
|
+
static bool nativeShadowColorAuditEnabled() {
|
|
585
|
+
static const bool enabled = std::getenv("GEA_WEBGL_SHADOW_COLOR_AUDIT") != nullptr;
|
|
586
|
+
return enabled;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
static void smokeLog(const char *format, ...) {
|
|
590
|
+
char message[1024] = {};
|
|
591
|
+
va_list args;
|
|
592
|
+
va_start(args, format);
|
|
593
|
+
std::vsnprintf(message, sizeof(message), format, args);
|
|
594
|
+
va_end(args);
|
|
595
|
+
|
|
596
|
+
const char *path = std::getenv("GEA_THREE_ANGLE_SMOKE_LOG");
|
|
597
|
+
#if defined(_WIN32)
|
|
598
|
+
OutputDebugStringA(message);
|
|
599
|
+
OutputDebugStringA("\n");
|
|
600
|
+
if (!path || path[0] == '\0') {
|
|
601
|
+
std::fprintf(stderr, "%s\n", message);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
#else
|
|
605
|
+
if (!path || path[0] == '\0') path = "/tmp/three-angle-metal-smoke.log";
|
|
606
|
+
#endif
|
|
607
|
+
if (FILE *file = std::fopen(path, "a")) {
|
|
608
|
+
std::fprintf(file, "%s\n", message);
|
|
609
|
+
std::fclose(file);
|
|
610
|
+
}
|
|
611
|
+
std::fprintf(stderr, "%s\n", message);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
extern "C" void gea_three_angle_smoke_log(std::string message) {
|
|
615
|
+
smokeLog("[three-angle-metal] %s", message.c_str());
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Window-level pointer capture: a local NSEvent monitor sees EVERY mouse
|
|
619
|
+
// event dispatched to this app before window/view routing, so pointer
|
|
620
|
+
// tracking cannot be blocked by whatever view AppKit hit-tests topmost
|
|
621
|
+
// (overlay labels etc. would otherwise carve dead zones — per-view
|
|
622
|
+
// mouseMoved: overrides only fire when THAT view wins hit-testing). The
|
|
623
|
+
// handler records the position in the host view's coordinate space with the
|
|
624
|
+
// same top-left-origin flip the per-view path used. Returning the event
|
|
625
|
+
// unmodified keeps normal dispatch (clicks, first responder) intact.
|
|
626
|
+
#if !defined(_WIN32)
|
|
627
|
+
static id gGeaPointerMonitor = nil;
|
|
628
|
+
static void geaInstallPointerMonitor(void) {
|
|
629
|
+
if (gGeaPointerMonitor) return;
|
|
630
|
+
const NSEventMask mask = NSEventMaskMouseMoved | NSEventMaskLeftMouseDragged |
|
|
631
|
+
NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp;
|
|
632
|
+
gGeaPointerMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:mask
|
|
633
|
+
handler:^NSEvent *(NSEvent *event) {
|
|
634
|
+
NSView *host = gWebGL.hostView;
|
|
635
|
+
if (host && host.window && event.window == host.window) {
|
|
636
|
+
if (event.type == NSEventTypeLeftMouseDown) gGeaPointerDown = true;
|
|
637
|
+
else if (event.type == NSEventTypeLeftMouseUp) gGeaPointerDown = false;
|
|
638
|
+
const NSPoint p = [host convertPoint:event.locationInWindow fromView:nil];
|
|
639
|
+
gGeaPointerX = p.x;
|
|
640
|
+
gGeaPointerY = host.bounds.size.height - p.y; // clientY: top-left origin
|
|
641
|
+
gGeaPointerW = host.bounds.size.width;
|
|
642
|
+
gGeaPointerH = host.bounds.size.height;
|
|
643
|
+
gGeaPointerSeq += 1.0;
|
|
644
|
+
}
|
|
645
|
+
return event;
|
|
646
|
+
}];
|
|
647
|
+
smokeLog("[three-angle-metal] pointer monitor installed (window-level)");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
static void *openFirst(const char *envName, const char *const *paths) {
|
|
651
|
+
if (const char *overridePath = std::getenv(envName)) {
|
|
652
|
+
if (void *handle = dlopen(overridePath, RTLD_NOW | RTLD_LOCAL)) {
|
|
653
|
+
std::fprintf(stderr, "[three-angle-metal] loaded %s from %s\n", envName, overridePath);
|
|
654
|
+
return handle;
|
|
655
|
+
}
|
|
656
|
+
std::fprintf(stderr, "[three-angle-metal] failed to load %s from %s: %s\n", envName, overridePath, dlerror());
|
|
657
|
+
}
|
|
658
|
+
for (int i = 0; paths[i] != nullptr; i++) {
|
|
659
|
+
if (void *handle = dlopen(paths[i], RTLD_NOW | RTLD_LOCAL)) {
|
|
660
|
+
std::fprintf(stderr, "[three-angle-metal] loaded %s\n", paths[i]);
|
|
661
|
+
return handle;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return nullptr;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
#endif
|
|
668
|
+
|
|
669
|
+
template <typename Fn>
|
|
670
|
+
static Fn loadSymbol(void *library, const char *name) {
|
|
671
|
+
#if defined(_WIN32)
|
|
672
|
+
return reinterpret_cast<Fn>(GetProcAddress(static_cast<HMODULE>(library), name));
|
|
673
|
+
#else
|
|
674
|
+
return reinterpret_cast<Fn>(dlsym(library, name));
|
|
675
|
+
#endif
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
template <typename Fn>
|
|
679
|
+
static Fn loadGl(const char *name) {
|
|
680
|
+
if (gWebGL.eglGetProcAddress) {
|
|
681
|
+
if (void *symbol = gWebGL.eglGetProcAddress(name)) return reinterpret_cast<Fn>(symbol);
|
|
682
|
+
}
|
|
683
|
+
return loadSymbol<Fn>(gWebGL.gles, name);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
static EGLint lastEglError() {
|
|
687
|
+
return gWebGL.eglGetError ? gWebGL.eglGetError() : 0;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Optional asynchronous GPU timings. Never wait for a result or insert a
|
|
691
|
+
// glFinish: an unavailable ring slot simply skips that frame's measurement.
|
|
692
|
+
// The single host context owns these query names until context destruction.
|
|
693
|
+
struct GpuFrameProfile {
|
|
694
|
+
void (*gen)(GLsizei, GLuint *) = nullptr;
|
|
695
|
+
void (*begin)(GLenum, GLuint) = nullptr;
|
|
696
|
+
void (*end)(GLenum) = nullptr;
|
|
697
|
+
void (*available)(GLuint, GLenum, GLuint *) = nullptr;
|
|
698
|
+
void (*result)(GLuint, GLenum, std::uint64_t *) = nullptr;
|
|
699
|
+
GLuint queries[8]{};
|
|
700
|
+
bool pending[8]{}, valid[8]{};
|
|
701
|
+
bool initialized = false, enabled = false, active = false;
|
|
702
|
+
unsigned slot = 0, count = 0;
|
|
703
|
+
double total = 0, maximum = 0;
|
|
704
|
+
std::chrono::steady_clock::time_point window = std::chrono::steady_clock::now();
|
|
705
|
+
|
|
706
|
+
void start() {
|
|
707
|
+
if (!nativeFrameProfileEnabled() || !gWebGL.ready || active) return;
|
|
708
|
+
if (!initialized) {
|
|
709
|
+
initialized = true;
|
|
710
|
+
const auto extensions = reinterpret_cast<const char *>(gWebGL.glGetString(0x1F03));
|
|
711
|
+
const std::string list = " " + std::string(extensions ? extensions : "") + " ";
|
|
712
|
+
if (list.find(" GL_EXT_disjoint_timer_query ") != std::string::npos) {
|
|
713
|
+
gen = loadGl<decltype(gen)>("glGenQueriesEXT");
|
|
714
|
+
begin = loadGl<decltype(begin)>("glBeginQueryEXT");
|
|
715
|
+
end = loadGl<decltype(end)>("glEndQueryEXT");
|
|
716
|
+
available = loadGl<decltype(available)>("glGetQueryObjectuivEXT");
|
|
717
|
+
result = loadGl<decltype(result)>("glGetQueryObjectui64vEXT");
|
|
718
|
+
auto bits = loadGl<void (*)(GLenum, GLenum, GLint *)>("glGetQueryivEXT");
|
|
719
|
+
GLint counterBits = 0;
|
|
720
|
+
if (bits) bits(0x88BF, 0x8864, &counterBits);
|
|
721
|
+
enabled = gen && begin && end && available && result && counterBits > 0;
|
|
722
|
+
}
|
|
723
|
+
smokeLog("[gea.gpu-profile] timer_supported=%d", enabled ? 1 : 0);
|
|
724
|
+
if (enabled) gen(8, queries);
|
|
725
|
+
}
|
|
726
|
+
if (!enabled) return;
|
|
727
|
+
GLint disjoint = 0;
|
|
728
|
+
gWebGL.glGetIntegerv(0x8FBB, &disjoint);
|
|
729
|
+
if (disjoint) {
|
|
730
|
+
for (auto &value : valid) value = false;
|
|
731
|
+
total = maximum = 0; count = 0;
|
|
732
|
+
}
|
|
733
|
+
for (unsigned i = 0; i < 8; ++i) {
|
|
734
|
+
if (!pending[i]) continue;
|
|
735
|
+
GLuint ready = 0;
|
|
736
|
+
available(queries[i], 0x8867, &ready);
|
|
737
|
+
if (!ready) continue;
|
|
738
|
+
if (valid[i]) {
|
|
739
|
+
std::uint64_t ns = 0;
|
|
740
|
+
result(queries[i], 0x8866, &ns);
|
|
741
|
+
const double ms = static_cast<double>(ns) / 1000000.0;
|
|
742
|
+
total += ms; maximum = std::max(maximum, ms); ++count;
|
|
743
|
+
}
|
|
744
|
+
pending[i] = false;
|
|
745
|
+
}
|
|
746
|
+
const auto now = std::chrono::steady_clock::now();
|
|
747
|
+
if (std::chrono::duration<double>(now - window).count() >= 1.0) {
|
|
748
|
+
if (count) smokeLog("[gea.gpu-profile] mean_ms=%.3f max_ms=%.3f samples=%u", total / count, maximum, count);
|
|
749
|
+
total = maximum = 0; count = 0; window = now;
|
|
750
|
+
}
|
|
751
|
+
if (!pending[slot]) {
|
|
752
|
+
begin(0x88BF, queries[slot]);
|
|
753
|
+
active = true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
void finish() {
|
|
757
|
+
if (!active) return;
|
|
758
|
+
end(0x88BF);
|
|
759
|
+
pending[slot] = valid[slot] = true;
|
|
760
|
+
slot = (slot + 1) % 8;
|
|
761
|
+
active = false;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
static GpuFrameProfile gGpuFrameProfile;
|
|
765
|
+
|
|
766
|
+
static void logEglFailure(const char *step) {
|
|
767
|
+
std::fprintf(stderr, "[three-angle-metal] %s failed, EGL error 0x%04x\n", step, lastEglError());
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
#if !defined(_WIN32)
|
|
771
|
+
static CGFloat backingScaleForHostView(NSView *nativeView) {
|
|
772
|
+
CGFloat scale = nativeView.window ? nativeView.window.backingScaleFactor : 0.0;
|
|
773
|
+
if (scale <= 0.0 && nativeView.window.screen) scale = nativeView.window.screen.backingScaleFactor;
|
|
774
|
+
if (scale <= 0.0 && NSScreen.mainScreen) scale = NSScreen.mainScreen.backingScaleFactor;
|
|
775
|
+
if (scale <= 0.0) scale = static_cast<CGFloat>(gWebGL.devicePixelRatio > 0.0 ? gWebGL.devicePixelRatio : 1.0);
|
|
776
|
+
return std::max<CGFloat>(1.0, scale);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
static NSRect resolvedHostBounds(NSView *nativeView) {
|
|
780
|
+
if (NSView *contentView = nativeView.window.contentView) [contentView layoutSubtreeIfNeeded];
|
|
781
|
+
NSView *containerView = nativeView.superview;
|
|
782
|
+
if (containerView) [containerView layoutSubtreeIfNeeded];
|
|
783
|
+
NSRect targetBounds = containerView ? containerView.bounds : nativeView.bounds;
|
|
784
|
+
if ((targetBounds.size.width <= 0.0 || targetBounds.size.height <= 0.0) && nativeView.window.contentView) {
|
|
785
|
+
targetBounds = nativeView.window.contentView.bounds;
|
|
786
|
+
}
|
|
787
|
+
return targetBounds;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
static double syncHostViewSize(double fallbackAspect) {
|
|
791
|
+
NSView *nativeView = gWebGL.hostView;
|
|
792
|
+
if (!nativeView) return fallbackAspect > 0.0 ? fallbackAspect : 1.0;
|
|
793
|
+
NSRect targetBounds = resolvedHostBounds(nativeView);
|
|
794
|
+
if (targetBounds.size.width <= 0.0 || targetBounds.size.height <= 0.0) {
|
|
795
|
+
return fallbackAspect > 0.0 ? fallbackAspect : 1.0;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
[CATransaction begin];
|
|
799
|
+
[CATransaction setDisableActions:YES];
|
|
800
|
+
nativeView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
801
|
+
NSRect targetFrame = NSMakeRect(0.0, 0.0, targetBounds.size.width, targetBounds.size.height);
|
|
802
|
+
nativeView.frame = targetFrame;
|
|
803
|
+
nativeView.bounds = targetFrame;
|
|
804
|
+
[nativeView layoutSubtreeIfNeeded];
|
|
805
|
+
|
|
806
|
+
CGFloat scale = backingScaleForHostView(nativeView);
|
|
807
|
+
gWebGL.widthPx = std::max(1, static_cast<int>(std::round(targetBounds.size.width * scale)));
|
|
808
|
+
gWebGL.heightPx = std::max(1, static_cast<int>(std::round(targetBounds.size.height * scale)));
|
|
809
|
+
gWebGL.devicePixelRatio = static_cast<double>(scale);
|
|
810
|
+
|
|
811
|
+
if ([nativeView.layer isKindOfClass:CAMetalLayer.class]) {
|
|
812
|
+
CAMetalLayer *metalLayer = (CAMetalLayer *)nativeView.layer;
|
|
813
|
+
metalLayer.frame = nativeView.bounds;
|
|
814
|
+
metalLayer.bounds = nativeView.bounds;
|
|
815
|
+
metalLayer.contentsScale = scale;
|
|
816
|
+
metalLayer.drawableSize = CGSizeMake(gWebGL.widthPx, gWebGL.heightPx);
|
|
817
|
+
metalLayer.needsDisplayOnBoundsChange = YES;
|
|
818
|
+
}
|
|
819
|
+
[CATransaction commit];
|
|
820
|
+
return static_cast<double>(targetBounds.size.width) / static_cast<double>(targetBounds.size.height);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Re-assert the presentation properties ANGLE may reset when it adopts the
|
|
824
|
+
// CAMetalLayer as its window surface, so nextDrawable keeps blocking (never
|
|
825
|
+
// drops a frame to black) for the surface ANGLE actually renders into.
|
|
826
|
+
static void configureMetalLayerForPresentation(CAMetalLayer *layer) {
|
|
827
|
+
if (!layer) return;
|
|
828
|
+
layer.allowsNextDrawableTimeout = NO;
|
|
829
|
+
layer.maximumDrawableCount = 3;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
#else
|
|
833
|
+
static double syncHostViewSize(double fallbackAspect) {
|
|
834
|
+
return gWebGL.heightPx > 0 ? static_cast<double>(gWebGL.widthPx) / gWebGL.heightPx : fallbackAspect;
|
|
835
|
+
}
|
|
836
|
+
extern "C" void gea_three_webgl_update_size_uwp(double width, double height, double pixelRatio) {
|
|
837
|
+
gWebGL.widthPx = std::max(1, static_cast<int>(width));
|
|
838
|
+
gWebGL.heightPx = std::max(1, static_cast<int>(height));
|
|
839
|
+
gWebGL.devicePixelRatio = pixelRatio > 0.0 ? pixelRatio : 1.0;
|
|
840
|
+
}
|
|
841
|
+
#endif
|
|
842
|
+
|
|
843
|
+
#if defined(_WIN32)
|
|
844
|
+
// The UWP frame driver owns its EGL context exclusively until it returns to
|
|
845
|
+
// CoreWindow event dispatch. Other hosts retain per-call binding semantics.
|
|
846
|
+
static thread_local bool gUwpFrameContextCurrent = false;
|
|
847
|
+
#endif
|
|
848
|
+
static bool makeCurrent(const char *label) {
|
|
849
|
+
#if defined(_WIN32)
|
|
850
|
+
if (gUwpFrameContextCurrent) return true;
|
|
851
|
+
#endif
|
|
852
|
+
if (!gWebGL.ready && gWebGL.context == EGL_NO_CONTEXT) return false;
|
|
853
|
+
// THIS FRAME LOOP IS ALREADY PACED. macOS drives it from the window's
|
|
854
|
+
// CADisplayLink at the screen's own cadence (`macos_main.mm`
|
|
855
|
+
// `updateDisplayLinkRate` pins `preferredFrameRateRange` to
|
|
856
|
+
// `screen.maximumFramesPerSecond`), and the CAMetalLayer presents with
|
|
857
|
+
// `displaySyncEnabled`. EGL's DEFAULT swap interval of 1 adds a THIRD
|
|
858
|
+
// waiter: `eglSwapBuffers` blocks for a vsync that the display link has
|
|
859
|
+
// already accounted for. The two pacers beat against each other -- a frame
|
|
860
|
+
// that finishes just after its own vsync waits for the next one, so the
|
|
861
|
+
// period lands at 2 refreshes now and then. A reference app measured
|
|
862
|
+
// `fps=55.5 wall=18.00ms busy=3.23ms idle=14.77ms(disp=14.77)`: only 3.2 ms
|
|
863
|
+
// of work against a 16.67 ms budget, missing 60 Hz purely on presentation
|
|
864
|
+
// pacing. Interval 0 hands pacing to the display link alone; tearing is not
|
|
865
|
+
// reintroduced because the layer still syncs its own present.
|
|
866
|
+
// `GEA_ANGLE_SWAP_INTERVAL` overrides it (set `1` to restore the old
|
|
867
|
+
// double-paced behaviour) so both arms can be measured from one binary.
|
|
868
|
+
if (gWebGL.eglSwapInterval) {
|
|
869
|
+
const char *requested = std::getenv("GEA_ANGLE_SWAP_INTERVAL");
|
|
870
|
+
const EGLint interval = requested ? static_cast<EGLint>(std::atoi(requested)) : 0;
|
|
871
|
+
gWebGL.eglSwapInterval(gWebGL.display, interval);
|
|
872
|
+
}
|
|
873
|
+
if (gWebGL.eglMakeCurrent(gWebGL.display, gWebGL.surface, gWebGL.surface, gWebGL.context) == EGL_FALSE_VALUE) {
|
|
874
|
+
logEglFailure(label);
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
#if defined(_WIN32)
|
|
881
|
+
extern "C" bool gea_three_webgl_begin_frame_uwp() {
|
|
882
|
+
// Always rebind after the dispatcher, where arbitrary platform callbacks
|
|
883
|
+
// may have used a different context. No such callbacks run within the frame.
|
|
884
|
+
gUwpFrameContextCurrent = false;
|
|
885
|
+
if (!gWebGL.ready || !makeCurrent("eglMakeCurrent(frame)")) return false;
|
|
886
|
+
gUwpFrameContextCurrent = true;
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
extern "C" void gea_three_webgl_end_frame_uwp() {
|
|
890
|
+
gUwpFrameContextCurrent = false;
|
|
891
|
+
}
|
|
892
|
+
#endif
|
|
893
|
+
|
|
894
|
+
#if !defined(_WIN32)
|
|
895
|
+
static bool rebuildWindowSurface(const char *label) {
|
|
896
|
+
if (!gWebGL.eglCreateWindowSurface || !gWebGL.eglDestroySurface || !gWebGL.eglMakeCurrent ||
|
|
897
|
+
!gWebGL.metalLayer || !gWebGL.config || gWebGL.context == EGL_NO_CONTEXT) {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
syncHostViewSize(static_cast<double>(gWebGL.widthPx) / static_cast<double>(std::max(1, gWebGL.heightPx)));
|
|
901
|
+
EGLSurface oldSurface = gWebGL.surface;
|
|
902
|
+
gWebGL.eglMakeCurrent(gWebGL.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
903
|
+
if (oldSurface != EGL_NO_SURFACE) gWebGL.eglDestroySurface(gWebGL.display, oldSurface);
|
|
904
|
+
gWebGL.surface = gWebGL.eglCreateWindowSurface(
|
|
905
|
+
gWebGL.display,
|
|
906
|
+
gWebGL.config,
|
|
907
|
+
(__bridge EGLNativeWindowType)gWebGL.metalLayer,
|
|
908
|
+
nullptr
|
|
909
|
+
);
|
|
910
|
+
if (gWebGL.surface == EGL_NO_SURFACE) {
|
|
911
|
+
logEglFailure(label);
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
if (!makeCurrent("eglMakeCurrent(rebound-window-surface)")) return false;
|
|
915
|
+
configureMetalLayerForPresentation(gWebGL.metalLayer);
|
|
916
|
+
if (gWebGL.glViewport) gWebGL.glViewport(0, 0, gWebGL.widthPx, gWebGL.heightPx);
|
|
917
|
+
// Re-issue a full clear into the freshly rebound surface so its backbuffer is
|
|
918
|
+
// initialised to the app's frame instead of presenting an uninitialised
|
|
919
|
+
// (black) drawable before the app's next render lands.
|
|
920
|
+
if (gWebGL.glClearColor && gWebGL.glClear) {
|
|
921
|
+
gWebGL.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
922
|
+
gWebGL.glClear(0x00004000 /* GL_COLOR_BUFFER_BIT */ | 0x00000100 /* GL_DEPTH_BUFFER_BIT */);
|
|
923
|
+
}
|
|
924
|
+
return true;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
#endif
|
|
928
|
+
|
|
929
|
+
static bool loadGlSymbols() {
|
|
930
|
+
#define LOAD_GL(name, type) \
|
|
931
|
+
gWebGL.name = loadGl<type>(#name); \
|
|
932
|
+
if (!gWebGL.name) { \
|
|
933
|
+
std::fprintf(stderr, "[three-angle-metal] missing GL symbol %s\n", #name); \
|
|
934
|
+
return false; \
|
|
935
|
+
}
|
|
936
|
+
LOAD_GL(glActiveTexture, PFNGLACTIVETEXTUREPROC)
|
|
937
|
+
LOAD_GL(glAttachShader, PFNGLATTACHSHADERPROC)
|
|
938
|
+
LOAD_GL(glBindBuffer, PFNGLBINDBUFFERPROC)
|
|
939
|
+
LOAD_GL(glBindFramebuffer, PFNGLBINDFRAMEBUFFERPROC)
|
|
940
|
+
LOAD_GL(glBindRenderbuffer, PFNGLBINDRENDERBUFFERPROC)
|
|
941
|
+
LOAD_GL(glBindTexture, PFNGLBINDTEXTUREPROC)
|
|
942
|
+
LOAD_GL(glBindVertexArray, PFNGLBINDVERTEXARRAYPROC)
|
|
943
|
+
LOAD_GL(glBlendEquation, PFNGLBLENDEQUATIONPROC)
|
|
944
|
+
LOAD_GL(glBlendEquationSeparate, PFNGLBLENDEQUATIONSEPARATEPROC)
|
|
945
|
+
LOAD_GL(glBlendFunc, PFNGLBLENDFUNCPROC)
|
|
946
|
+
LOAD_GL(glBlendFuncSeparate, PFNGLBLENDFUNCSEPARATEPROC)
|
|
947
|
+
LOAD_GL(glBufferData, PFNGLBUFFERDATAPROC)
|
|
948
|
+
LOAD_GL(glBufferSubData, PFNGLBUFFERSUBDATAPROC)
|
|
949
|
+
LOAD_GL(glCopyBufferSubData, PFNGLCOPYBUFFERSUBDATAPROC)
|
|
950
|
+
LOAD_GL(glCheckFramebufferStatus, PFNGLCHECKFRAMEBUFFERSTATUSPROC)
|
|
951
|
+
LOAD_GL(glClear, PFNGLCLEARPROC)
|
|
952
|
+
LOAD_GL(glClearColor, PFNGLCLEARCOLORPROC)
|
|
953
|
+
LOAD_GL(glClearDepthf, PFNGLCLEARDEPTHFPROC)
|
|
954
|
+
LOAD_GL(glClearStencil, PFNGLCLEARSTENCILPROC)
|
|
955
|
+
LOAD_GL(glColorMask, PFNGLCOLORMASKPROC)
|
|
956
|
+
LOAD_GL(glCompileShader, PFNGLCOMPILESHADERPROC)
|
|
957
|
+
LOAD_GL(glCreateProgram, PFNGLCREATEPROGRAMPROC)
|
|
958
|
+
LOAD_GL(glCreateShader, PFNGLCREATESHADERPROC)
|
|
959
|
+
LOAD_GL(glCullFace, PFNGLCULLFACEPROC)
|
|
960
|
+
LOAD_GL(glDeleteBuffers, PFNGLDELETEBUFFERSPROC)
|
|
961
|
+
LOAD_GL(glDeleteFramebuffers, PFNGLDELETEFRAMEBUFFERSPROC)
|
|
962
|
+
LOAD_GL(glDeleteProgram, PFNGLDELETEPROGRAMPROC)
|
|
963
|
+
LOAD_GL(glDeleteRenderbuffers, PFNGLDELETERENDERBUFFERSPROC)
|
|
964
|
+
LOAD_GL(glDeleteShader, PFNGLDELETESHADERPROC)
|
|
965
|
+
LOAD_GL(glDeleteTextures, PFNGLDELETETEXTURESPROC)
|
|
966
|
+
LOAD_GL(glDeleteVertexArrays, PFNGLDELETEVERTEXARRAYSPROC)
|
|
967
|
+
LOAD_GL(glDepthFunc, PFNGLDEPTHFUNCPROC)
|
|
968
|
+
LOAD_GL(glDepthMask, PFNGLDEPTHMASKPROC)
|
|
969
|
+
LOAD_GL(glDisable, PFNGLDISABLEPROC)
|
|
970
|
+
LOAD_GL(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC)
|
|
971
|
+
LOAD_GL(glDrawArrays, PFNGLDRAWARRAYSPROC)
|
|
972
|
+
LOAD_GL(glDrawArraysInstanced, PFNGLDRAWARRAYSINSTANCEDPROC)
|
|
973
|
+
LOAD_GL(glDrawBuffers, PFNGLDRAWBUFFERSPROC)
|
|
974
|
+
LOAD_GL(glDrawElements, PFNGLDRAWELEMENTSPROC)
|
|
975
|
+
LOAD_GL(glDrawElementsInstanced, PFNGLDRAWELEMENTSINSTANCEDPROC)
|
|
976
|
+
LOAD_GL(glEnable, PFNGLENABLEPROC)
|
|
977
|
+
LOAD_GL(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC)
|
|
978
|
+
LOAD_GL(glFramebufferRenderbuffer, PFNGLFRAMEBUFFERRENDERBUFFERPROC)
|
|
979
|
+
LOAD_GL(glFramebufferTexture2D, PFNGLFRAMEBUFFERTEXTURE2DPROC)
|
|
980
|
+
LOAD_GL(glFrontFace, PFNGLFRONTFACEPROC)
|
|
981
|
+
LOAD_GL(glGenBuffers, PFNGLGENBUFFERSPROC)
|
|
982
|
+
LOAD_GL(glGenFramebuffers, PFNGLGENFRAMEBUFFERSPROC)
|
|
983
|
+
LOAD_GL(glGenRenderbuffers, PFNGLGENRENDERBUFFERSPROC)
|
|
984
|
+
LOAD_GL(glGenTextures, PFNGLGENTEXTURESPROC)
|
|
985
|
+
LOAD_GL(glGenVertexArrays, PFNGLGENVERTEXARRAYSPROC)
|
|
986
|
+
LOAD_GL(glGenerateMipmap, PFNGLGENERATEMIPMAPPROC)
|
|
987
|
+
LOAD_GL(glGetActiveAttrib, PFNGLGETACTIVEATTRIBPROC)
|
|
988
|
+
LOAD_GL(glGetActiveUniform, PFNGLGETACTIVEUNIFORMPROC)
|
|
989
|
+
LOAD_GL(glGetAttribLocation, PFNGLGETATTRIBLOCATIONPROC)
|
|
990
|
+
LOAD_GL(glGetError, PFNGLGETERRORPROC)
|
|
991
|
+
LOAD_GL(glGetIntegerv, PFNGLGETINTEGERVPROC)
|
|
992
|
+
LOAD_GL(glGetProgramiv, PFNGLGETPROGRAMIVPROC)
|
|
993
|
+
LOAD_GL(glGetShaderiv, PFNGLGETSHADERIVPROC)
|
|
994
|
+
LOAD_GL(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC)
|
|
995
|
+
LOAD_GL(glGetString, PFNGLGETSTRINGPROC)
|
|
996
|
+
LOAD_GL(glGetUniformLocation, PFNGLGETUNIFORMLOCATIONPROC)
|
|
997
|
+
LOAD_GL(glLineWidth, PFNGLLINEWIDTHPROC)
|
|
998
|
+
LOAD_GL(glLinkProgram, PFNGLLINKPROGRAMPROC)
|
|
999
|
+
LOAD_GL(glPixelStorei, PFNGLPIXELSTOREIPROC)
|
|
1000
|
+
LOAD_GL(glPolygonOffset, PFNGLPOLYGONOFFSETPROC)
|
|
1001
|
+
LOAD_GL(glReadPixels, PFNGLREADPIXELSPROC)
|
|
1002
|
+
LOAD_GL(glRenderbufferStorage, PFNGLRENDERBUFFERSTORAGEPROC)
|
|
1003
|
+
LOAD_GL(glScissor, PFNGLSCISSORPROC)
|
|
1004
|
+
LOAD_GL(glShaderSource, PFNGLSHADERSOURCEPROC)
|
|
1005
|
+
LOAD_GL(glStencilFunc, PFNGLSTENCILFUNCPROC)
|
|
1006
|
+
LOAD_GL(glStencilFuncSeparate, PFNGLSTENCILFUNCSEPARATEPROC)
|
|
1007
|
+
LOAD_GL(glStencilMask, PFNGLSTENCILMASKPROC)
|
|
1008
|
+
LOAD_GL(glStencilMaskSeparate, PFNGLSTENCILMASKSEPARATEPROC)
|
|
1009
|
+
LOAD_GL(glStencilOp, PFNGLSTENCILOPPROC)
|
|
1010
|
+
LOAD_GL(glStencilOpSeparate, PFNGLSTENCILOPSEPARATEPROC)
|
|
1011
|
+
LOAD_GL(glTexImage2D, PFNGLTEXIMAGE2DPROC)
|
|
1012
|
+
LOAD_GL(glTexParameterf, PFNGLTEXPARAMETERFPROC)
|
|
1013
|
+
LOAD_GL(glTexParameteri, PFNGLTEXPARAMETERIPROC)
|
|
1014
|
+
LOAD_GL(glTexSubImage2D, PFNGLTEXSUBIMAGE2DPROC)
|
|
1015
|
+
LOAD_GL(glTexStorage2D, PFNGLTEXSTORAGE2DPROC)
|
|
1016
|
+
LOAD_GL(glUniform1f, PFNGLUNIFORM1FPROC)
|
|
1017
|
+
LOAD_GL(glUniform1fv, PFNGLUNIFORM1FVPROC)
|
|
1018
|
+
LOAD_GL(glUniform1i, PFNGLUNIFORM1IPROC)
|
|
1019
|
+
LOAD_GL(glUniform1iv, PFNGLUNIFORM1IVPROC)
|
|
1020
|
+
LOAD_GL(glUniform1ui, PFNGLUNIFORM1UIPROC)
|
|
1021
|
+
LOAD_GL(glUniform1uiv, PFNGLUNIFORM1UIVPROC)
|
|
1022
|
+
LOAD_GL(glUniform2f, PFNGLUNIFORM2FPROC)
|
|
1023
|
+
LOAD_GL(glUniform2fv, PFNGLUNIFORM2FVPROC)
|
|
1024
|
+
LOAD_GL(glUniform2i, PFNGLUNIFORM2IPROC)
|
|
1025
|
+
LOAD_GL(glUniform2iv, PFNGLUNIFORM2IVPROC)
|
|
1026
|
+
LOAD_GL(glUniform2ui, PFNGLUNIFORM2UIPROC)
|
|
1027
|
+
LOAD_GL(glUniform2uiv, PFNGLUNIFORM2UIVPROC)
|
|
1028
|
+
LOAD_GL(glUniform3f, PFNGLUNIFORM3FPROC)
|
|
1029
|
+
LOAD_GL(glUniform3fv, PFNGLUNIFORM3FVPROC)
|
|
1030
|
+
LOAD_GL(glUniform3i, PFNGLUNIFORM3IPROC)
|
|
1031
|
+
LOAD_GL(glUniform3iv, PFNGLUNIFORM3IVPROC)
|
|
1032
|
+
LOAD_GL(glUniform3ui, PFNGLUNIFORM3UIPROC)
|
|
1033
|
+
LOAD_GL(glUniform3uiv, PFNGLUNIFORM3UIVPROC)
|
|
1034
|
+
LOAD_GL(glUniform4f, PFNGLUNIFORM4FPROC)
|
|
1035
|
+
LOAD_GL(glUniform4fv, PFNGLUNIFORM4FVPROC)
|
|
1036
|
+
LOAD_GL(glUniform4i, PFNGLUNIFORM4IPROC)
|
|
1037
|
+
LOAD_GL(glUniform4iv, PFNGLUNIFORM4IVPROC)
|
|
1038
|
+
LOAD_GL(glUniform4ui, PFNGLUNIFORM4UIPROC)
|
|
1039
|
+
LOAD_GL(glUniform4uiv, PFNGLUNIFORM4UIVPROC)
|
|
1040
|
+
LOAD_GL(glUniformMatrix2fv, PFNGLUNIFORMMATRIX2FVPROC)
|
|
1041
|
+
LOAD_GL(glUniformMatrix3fv, PFNGLUNIFORMMATRIX3FVPROC)
|
|
1042
|
+
LOAD_GL(glUniformMatrix4fv, PFNGLUNIFORMMATRIX4FVPROC)
|
|
1043
|
+
LOAD_GL(glUseProgram, PFNGLUSEPROGRAMPROC)
|
|
1044
|
+
LOAD_GL(glVertexAttribDivisor, PFNGLVERTEXATTRIBDIVISORPROC)
|
|
1045
|
+
LOAD_GL(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC)
|
|
1046
|
+
LOAD_GL(glViewport, PFNGLVIEWPORTPROC)
|
|
1047
|
+
#undef LOAD_GL
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
#if defined(_WIN32)
|
|
1052
|
+
extern "C" bool gea_three_webgl_attach_uwp(
|
|
1053
|
+
void *nativeWindow,
|
|
1054
|
+
#else
|
|
1055
|
+
extern "C" bool gea_three_webgl_attach(
|
|
1056
|
+
gea::apple::AppKit::NSView view,
|
|
1057
|
+
#endif
|
|
1058
|
+
double width,
|
|
1059
|
+
double height,
|
|
1060
|
+
double devicePixelRatio
|
|
1061
|
+
) {
|
|
1062
|
+
geaTrapCheck("gea_three_webgl_attach");
|
|
1063
|
+
smokeLog("[three-angle-metal] WebGL host attach requested");
|
|
1064
|
+
if (gWebGL.ready) return true;
|
|
1065
|
+
|
|
1066
|
+
#if defined(_WIN32)
|
|
1067
|
+
gWebGL.egl = LoadPackagedLibrary(L"libEGL.dll", 0);
|
|
1068
|
+
gWebGL.gles = LoadPackagedLibrary(L"libGLESv2.dll", 0);
|
|
1069
|
+
#else
|
|
1070
|
+
static const char *const eglPaths[] = {
|
|
1071
|
+
"/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
|
1072
|
+
"/Applications/Cursor.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
|
1073
|
+
"/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/149.0.7827.201/Libraries/libEGL.dylib",
|
|
1074
|
+
"/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/149.0.7827.197/Libraries/libEGL.dylib",
|
|
1075
|
+
nullptr,
|
|
1076
|
+
};
|
|
1077
|
+
static const char *const glesPaths[] = {
|
|
1078
|
+
"/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib",
|
|
1079
|
+
"/Applications/Cursor.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib",
|
|
1080
|
+
"/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/149.0.7827.201/Libraries/libGLESv2.dylib",
|
|
1081
|
+
"/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/149.0.7827.197/Libraries/libGLESv2.dylib",
|
|
1082
|
+
nullptr,
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
gWebGL.egl = openFirst("GEA_ANGLE_EGL_DYLIB", eglPaths);
|
|
1086
|
+
gWebGL.gles = openFirst("GEA_ANGLE_GLES_DYLIB", glesPaths);
|
|
1087
|
+
#endif
|
|
1088
|
+
if (!gWebGL.egl || !gWebGL.gles) {
|
|
1089
|
+
smokeLog("[three-angle-metal] ANGLE libEGL/libGLESv2 were not found");
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
gWebGL.eglGetProcAddress = loadSymbol<PFNEGLGETPROCADDRESS>(gWebGL.egl, "eglGetProcAddress");
|
|
1094
|
+
auto eglInitialize = loadSymbol<PFNEGLINITIALIZEPROC>(gWebGL.egl, "eglInitialize");
|
|
1095
|
+
auto eglBindAPI = loadSymbol<PFNEGLBINDAPIPROC>(gWebGL.egl, "eglBindAPI");
|
|
1096
|
+
auto eglChooseConfig = loadSymbol<PFNEGLCHOOSECONFIGPROC>(gWebGL.egl, "eglChooseConfig");
|
|
1097
|
+
gWebGL.eglCreateWindowSurface = loadSymbol<PFNEGLCREATEWINDOWSURFACEPROC>(gWebGL.egl, "eglCreateWindowSurface");
|
|
1098
|
+
gWebGL.eglCreatePbufferSurface = loadSymbol<PFNEGLCREATEPBUFFERSURFACEPROC>(gWebGL.egl, "eglCreatePbufferSurface");
|
|
1099
|
+
auto eglCreateContext = loadSymbol<PFNEGLCREATECONTEXTPROC>(gWebGL.egl, "eglCreateContext");
|
|
1100
|
+
gWebGL.eglDestroySurface = loadSymbol<PFNEGLDESTROYSURFACEPROC>(gWebGL.egl, "eglDestroySurface");
|
|
1101
|
+
gWebGL.eglGetError = loadSymbol<PFNEGLGETERRORPROC>(gWebGL.egl, "eglGetError");
|
|
1102
|
+
gWebGL.eglMakeCurrent = loadSymbol<PFNEGLMAKECURRENTPROC>(gWebGL.egl, "eglMakeCurrent");
|
|
1103
|
+
gWebGL.eglSwapBuffers = loadSymbol<PFNEGLSWAPBUFFERSPROC>(gWebGL.egl, "eglSwapBuffers");
|
|
1104
|
+
// Optional: an older EGL without it simply keeps the driver default below.
|
|
1105
|
+
gWebGL.eglSwapInterval = loadSymbol<PFNEGLSWAPINTERVALPROC>(gWebGL.egl, "eglSwapInterval");
|
|
1106
|
+
if (!gWebGL.eglGetProcAddress || !eglInitialize || !eglBindAPI || !eglChooseConfig ||
|
|
1107
|
+
!eglCreateContext || !gWebGL.eglMakeCurrent || !gWebGL.eglSwapBuffers) {
|
|
1108
|
+
smokeLog("[three-angle-metal] required EGL symbols were missing");
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
auto eglGetPlatformDisplayEXT =
|
|
1113
|
+
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(gWebGL.eglGetProcAddress("eglGetPlatformDisplayEXT"));
|
|
1114
|
+
if (!eglGetPlatformDisplayEXT) {
|
|
1115
|
+
smokeLog("[three-angle-metal] eglGetPlatformDisplayEXT unavailable");
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const EGLint displayAttrs[] = {
|
|
1120
|
+
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
|
1121
|
+
#if defined(_WIN32)
|
|
1122
|
+
0x3208, // EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE
|
|
1123
|
+
#else
|
|
1124
|
+
EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
|
|
1125
|
+
#endif
|
|
1126
|
+
EGL_NONE,
|
|
1127
|
+
};
|
|
1128
|
+
gWebGL.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttrs);
|
|
1129
|
+
if (gWebGL.display == EGL_NO_DISPLAY) {
|
|
1130
|
+
logEglFailure("eglGetPlatformDisplayEXT");
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
EGLint major = 0;
|
|
1135
|
+
EGLint minor = 0;
|
|
1136
|
+
if (eglInitialize(gWebGL.display, &major, &minor) == EGL_FALSE_VALUE) {
|
|
1137
|
+
logEglFailure("eglInitialize");
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE_VALUE) {
|
|
1141
|
+
logEglFailure("eglBindAPI");
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const EGLint configAttrs[] = {
|
|
1146
|
+
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
|
|
1147
|
+
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT | EGL_OPENGL_ES2_BIT,
|
|
1148
|
+
EGL_RED_SIZE, 8,
|
|
1149
|
+
EGL_GREEN_SIZE, 8,
|
|
1150
|
+
EGL_BLUE_SIZE, 8,
|
|
1151
|
+
EGL_ALPHA_SIZE, 8,
|
|
1152
|
+
EGL_DEPTH_SIZE, 24,
|
|
1153
|
+
EGL_STENCIL_SIZE, 8,
|
|
1154
|
+
EGL_NONE,
|
|
1155
|
+
};
|
|
1156
|
+
EGLConfig config = nullptr;
|
|
1157
|
+
EGLint configCount = 0;
|
|
1158
|
+
if (eglChooseConfig(gWebGL.display, configAttrs, &config, 1, &configCount) == EGL_FALSE_VALUE || configCount < 1) {
|
|
1159
|
+
logEglFailure("eglChooseConfig");
|
|
1160
|
+
return false;
|
|
1161
|
+
}
|
|
1162
|
+
gWebGL.config = config;
|
|
1163
|
+
|
|
1164
|
+
#if defined(_WIN32)
|
|
1165
|
+
gWebGL.nativeWindow = nativeWindow;
|
|
1166
|
+
gea_three_webgl_update_size_uwp(width, height, devicePixelRatio);
|
|
1167
|
+
if (gWebGL.eglCreateWindowSurface) {
|
|
1168
|
+
gWebGL.surface = gWebGL.eglCreateWindowSurface(gWebGL.display, config, nativeWindow, nullptr);
|
|
1169
|
+
}
|
|
1170
|
+
if (gWebGL.surface == EGL_NO_SURFACE) {
|
|
1171
|
+
logEglFailure("eglCreateWindowSurface(CoreWindow)");
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
#else
|
|
1175
|
+
NSView *containerView = (__bridge NSView *)gea::apple::objc::object(view.handle);
|
|
1176
|
+
containerView.wantsLayer = YES;
|
|
1177
|
+
containerView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
1178
|
+
[containerView layoutSubtreeIfNeeded];
|
|
1179
|
+
|
|
1180
|
+
GeaAngleWebGLHostView *nativeView = [[GeaAngleWebGLHostView alloc] initWithFrame:containerView.bounds];
|
|
1181
|
+
[containerView addSubview:nativeView];
|
|
1182
|
+
gWebGL.hostView = nativeView;
|
|
1183
|
+
geaInstallPointerMonitor();
|
|
1184
|
+
gWebGL.devicePixelRatio = devicePixelRatio > 0.0 ? devicePixelRatio : 1.0;
|
|
1185
|
+
const double fallbackAspect = width > 0.0 && height > 0.0 ? width / height : 1.0;
|
|
1186
|
+
|
|
1187
|
+
CAMetalLayer *metalLayer = [nativeView.layer isKindOfClass:CAMetalLayer.class]
|
|
1188
|
+
? (CAMetalLayer *)nativeView.layer
|
|
1189
|
+
: [CAMetalLayer layer];
|
|
1190
|
+
nativeView.layer = metalLayer;
|
|
1191
|
+
gWebGL.metalLayer = metalLayer;
|
|
1192
|
+
syncHostViewSize(fallbackAspect);
|
|
1193
|
+
[nativeView displayIfNeeded];
|
|
1194
|
+
[nativeView layoutSubtreeIfNeeded];
|
|
1195
|
+
[CATransaction flush];
|
|
1196
|
+
|
|
1197
|
+
if (gWebGL.eglCreateWindowSurface) {
|
|
1198
|
+
gWebGL.surface = gWebGL.eglCreateWindowSurface(gWebGL.display, config, (__bridge EGLNativeWindowType)metalLayer, nullptr);
|
|
1199
|
+
}
|
|
1200
|
+
if (gWebGL.surface == EGL_NO_SURFACE && gWebGL.eglCreatePbufferSurface) {
|
|
1201
|
+
logEglFailure("eglCreateWindowSurface");
|
|
1202
|
+
const EGLint surfaceAttrs[] = {
|
|
1203
|
+
EGL_WIDTH, static_cast<EGLint>(gWebGL.widthPx),
|
|
1204
|
+
EGL_HEIGHT, static_cast<EGLint>(gWebGL.heightPx),
|
|
1205
|
+
EGL_NONE,
|
|
1206
|
+
};
|
|
1207
|
+
gWebGL.surface = gWebGL.eglCreatePbufferSurface(gWebGL.display, config, surfaceAttrs);
|
|
1208
|
+
}
|
|
1209
|
+
if (gWebGL.surface == EGL_NO_SURFACE) {
|
|
1210
|
+
logEglFailure("eglCreatePbufferSurface");
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
configureMetalLayerForPresentation(gWebGL.metalLayer);
|
|
1214
|
+
#endif
|
|
1215
|
+
|
|
1216
|
+
const EGLint contextAttrs[] = {
|
|
1217
|
+
EGL_CONTEXT_CLIENT_VERSION, 3,
|
|
1218
|
+
EGL_NONE,
|
|
1219
|
+
};
|
|
1220
|
+
gWebGL.context = eglCreateContext(gWebGL.display, config, EGL_NO_CONTEXT, contextAttrs);
|
|
1221
|
+
if (gWebGL.context == EGL_NO_CONTEXT) {
|
|
1222
|
+
logEglFailure("eglCreateContext");
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
if (!makeCurrent("eglMakeCurrent")) return false;
|
|
1226
|
+
if (!loadGlSymbols()) return false;
|
|
1227
|
+
|
|
1228
|
+
gWebGL.ready = true;
|
|
1229
|
+
gWebGL.glViewport(0, 0, gWebGL.widthPx, gWebGL.heightPx);
|
|
1230
|
+
#if !defined(_WIN32)
|
|
1231
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1232
|
+
if (gWebGL.ready && rebuildWindowSurface("eglCreateWindowSurface(deferred)")) {
|
|
1233
|
+
smokeLog("[three-angle-metal] WebGL EGL surface rebound at %dx%d", gWebGL.widthPx, gWebGL.heightPx);
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
#endif
|
|
1237
|
+
smokeLog("[native-webgl-angle] context attached, EGL %d.%d at %dx%d", major, minor, gWebGL.widthPx, gWebGL.heightPx);
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
extern "C" double gea_three_webgl_sync_size(double fallbackAspect) {
|
|
1242
|
+
if (nativeFrameProfileEnabled()) {
|
|
1243
|
+
gProfileSizeSync = std::chrono::steady_clock::now();
|
|
1244
|
+
gProfileSizeSyncPending = true;
|
|
1245
|
+
}
|
|
1246
|
+
geaTrapCheck("gea_three_webgl_sync_size");
|
|
1247
|
+
if (std::getenv("GEA_WEBGL_HOST_DEBUG")) {
|
|
1248
|
+
smokeLog("[three-angle-metal] gea_three_webgl_sync_size begin fallback=%g ready=%d size=%dx%d",
|
|
1249
|
+
fallbackAspect, gWebGL.ready ? 1 : 0, gWebGL.widthPx, gWebGL.heightPx);
|
|
1250
|
+
}
|
|
1251
|
+
const double aspect = syncHostViewSize(fallbackAspect);
|
|
1252
|
+
gGpuFrameProfile.start();
|
|
1253
|
+
if (gWebGL.ready && gWebGL.glViewport) gWebGL.glViewport(0, 0, gWebGL.widthPx, gWebGL.heightPx);
|
|
1254
|
+
if (std::getenv("GEA_WEBGL_HOST_DEBUG")) {
|
|
1255
|
+
smokeLog("[three-angle-metal] gea_three_webgl_sync_size end aspect=%g size=%dx%d",
|
|
1256
|
+
aspect, gWebGL.widthPx, gWebGL.heightPx);
|
|
1257
|
+
}
|
|
1258
|
+
return aspect;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
extern "C" double gea_three_webgl_width() {
|
|
1262
|
+
geaTrapCheck("gea_three_webgl_width");
|
|
1263
|
+
return static_cast<double>(gWebGL.widthPx);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
extern "C" double gea_three_webgl_height() {
|
|
1267
|
+
geaTrapCheck("gea_three_webgl_height");
|
|
1268
|
+
return static_cast<double>(gWebGL.heightPx);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// The backing scale the host actually applied (window backingScaleFactor,
|
|
1272
|
+
// refreshed by every syncHostViewSize) — the native analog of
|
|
1273
|
+
// window.devicePixelRatio. widthPx/heightPx are DEVICE pixels; dividing by
|
|
1274
|
+
// this yields the CSS-pixel (logical) size the web build lays out in.
|
|
1275
|
+
extern "C" double gea_three_webgl_device_pixel_ratio() {
|
|
1276
|
+
geaTrapCheck("gea_three_webgl_device_pixel_ratio");
|
|
1277
|
+
return gWebGL.devicePixelRatio > 0.0 ? gWebGL.devicePixelRatio : 1.0;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
extern "C" void gea_three_webgl_swap() {
|
|
1281
|
+
geaTrapCheck("gea_three_webgl_swap");
|
|
1282
|
+
if (!gWebGL.ready || !makeCurrent("eglMakeCurrent(swap)")) return;
|
|
1283
|
+
gGpuFrameProfile.finish();
|
|
1284
|
+
// GEA_WEBGL_FPS=1 — rolling real-time present cadence to stderr. Measures the
|
|
1285
|
+
// wall-clock interval between successive swaps (the true windowed frame rate,
|
|
1286
|
+
// which the vsync-free headless [gea.perf] cannot see). Harmless when unset.
|
|
1287
|
+
if (std::getenv("GEA_WEBGL_FPS")) {
|
|
1288
|
+
using clock = std::chrono::steady_clock;
|
|
1289
|
+
static clock::time_point last = clock::now();
|
|
1290
|
+
static clock::time_point windowStart = last;
|
|
1291
|
+
static int frames = 0;
|
|
1292
|
+
const clock::time_point nowTp = clock::now();
|
|
1293
|
+
++frames;
|
|
1294
|
+
const double windowMs =
|
|
1295
|
+
std::chrono::duration<double, std::milli>(nowTp - windowStart).count();
|
|
1296
|
+
if (windowMs >= 1000.0) {
|
|
1297
|
+
const double dtMs =
|
|
1298
|
+
std::chrono::duration<double, std::milli>(nowTp - last).count();
|
|
1299
|
+
smokeLog("[gea.fps] %.1f fps over %d frames (%.0fms), last dt=%.2fms",
|
|
1300
|
+
frames * 1000.0 / windowMs, frames, windowMs, dtMs);
|
|
1301
|
+
frames = 0;
|
|
1302
|
+
windowStart = nowTp;
|
|
1303
|
+
}
|
|
1304
|
+
last = nowTp;
|
|
1305
|
+
}
|
|
1306
|
+
if (std::getenv("GEA_WEBGL_SAMPLE_PIXELS") && gWebGL.glReadPixels && gWebGL.glGetError && gWebGL.glGetIntegerv) {
|
|
1307
|
+
static int sampleCount = 0;
|
|
1308
|
+
if (sampleCount < 16 || sampleCount % 60 == 0) {
|
|
1309
|
+
unsigned char pixel[4] = {0, 0, 0, 0};
|
|
1310
|
+
GLint viewport[4] = {0, 0, 0, 0};
|
|
1311
|
+
GLint framebuffer = 0;
|
|
1312
|
+
gWebGL.glGetIntegerv(GL_VIEWPORT_VALUE, viewport);
|
|
1313
|
+
gWebGL.glGetIntegerv(GL_FRAMEBUFFER_BINDING_VALUE, &framebuffer);
|
|
1314
|
+
gWebGL.glReadPixels(
|
|
1315
|
+
std::max(0, gWebGL.widthPx / 2),
|
|
1316
|
+
std::max(0, gWebGL.heightPx / 2),
|
|
1317
|
+
1,
|
|
1318
|
+
1,
|
|
1319
|
+
GL_RGBA_VALUE,
|
|
1320
|
+
GL_UNSIGNED_BYTE,
|
|
1321
|
+
pixel
|
|
1322
|
+
);
|
|
1323
|
+
const GLenum readError = gWebGL.glGetError();
|
|
1324
|
+
smokeLog(
|
|
1325
|
+
"[three-angle-metal] swap sample frame=%d surface=%dx%d viewport=%d,%d,%d,%d framebuffer=%d center=%u,%u,%u,%u glError=0x%04x",
|
|
1326
|
+
sampleCount,
|
|
1327
|
+
gWebGL.widthPx,
|
|
1328
|
+
gWebGL.heightPx,
|
|
1329
|
+
viewport[0],
|
|
1330
|
+
viewport[1],
|
|
1331
|
+
viewport[2],
|
|
1332
|
+
viewport[3],
|
|
1333
|
+
framebuffer,
|
|
1334
|
+
pixel[0],
|
|
1335
|
+
pixel[1],
|
|
1336
|
+
pixel[2],
|
|
1337
|
+
pixel[3],
|
|
1338
|
+
readError
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
sampleCount++;
|
|
1342
|
+
}
|
|
1343
|
+
// TEMP-DIAG: GEA_THREE_VIEW_DUMP=1 — at frame 60, log the window's full
|
|
1344
|
+
// subview tree and a hit-test grid so overlay views that would swallow
|
|
1345
|
+
// mouse events are visible without Accessibility permission.
|
|
1346
|
+
#if !defined(_WIN32)
|
|
1347
|
+
if (std::getenv("GEA_THREE_VIEW_DUMP")) {
|
|
1348
|
+
static int viewDumpCount = 0;
|
|
1349
|
+
if (++viewDumpCount == 60) {
|
|
1350
|
+
NSView *host = gWebGL.hostView;
|
|
1351
|
+
NSWindow *window = host ? host.window : nil;
|
|
1352
|
+
NSView *content = window ? window.contentView : nil;
|
|
1353
|
+
if (content) {
|
|
1354
|
+
__block void (^walk)(NSView *, int) = nil;
|
|
1355
|
+
walk = ^(NSView *view, int depth) {
|
|
1356
|
+
smokeLog("[view-dump] %*s%s frame=(%.0f,%.0f %.0fx%.0f) hidden=%d", depth * 2, "",
|
|
1357
|
+
object_getClassName(view), view.frame.origin.x, view.frame.origin.y,
|
|
1358
|
+
view.frame.size.width, view.frame.size.height, view.isHidden ? 1 : 0);
|
|
1359
|
+
for (NSView *sub in view.subviews) walk(sub, depth + 1);
|
|
1360
|
+
};
|
|
1361
|
+
walk(content, 0);
|
|
1362
|
+
const CGFloat w = content.bounds.size.width;
|
|
1363
|
+
const CGFloat h = content.bounds.size.height;
|
|
1364
|
+
for (int gy = 1; gy < 8; gy += 2) {
|
|
1365
|
+
for (int gx = 1; gx < 8; gx += 2) {
|
|
1366
|
+
const NSPoint inContent = NSMakePoint(w * gx / 8.0, h * gy / 8.0);
|
|
1367
|
+
const NSPoint inSuper = [content.superview convertPoint:inContent fromView:content];
|
|
1368
|
+
NSView *hit = [content hitTest:inSuper];
|
|
1369
|
+
smokeLog("[hit-grid] (%.0f,%.0f) -> %s", inContent.x, inContent.y,
|
|
1370
|
+
hit ? object_getClassName(hit) : "nil");
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
} else {
|
|
1374
|
+
smokeLog("[view-dump] no window/content view");
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
#endif
|
|
1379
|
+
// Debug frame dump: GEA_WEBGL_DUMP_FRAME=<path-prefix> writes the full
|
|
1380
|
+
// framebuffer of frames 90/180/300 as PPM (P6) — visual ground truth for
|
|
1381
|
+
// native-vs-browser comparisons without needing a window screenshot.
|
|
1382
|
+
if (const char *dumpPrefix = std::getenv("GEA_WEBGL_DUMP_FRAME"); dumpPrefix && gWebGL.glReadPixels) {
|
|
1383
|
+
static int dumpCount = 0;
|
|
1384
|
+
++dumpCount;
|
|
1385
|
+
if (dumpCount == 90 || dumpCount == 180 || dumpCount == 300) {
|
|
1386
|
+
const int w = gWebGL.widthPx;
|
|
1387
|
+
const int h = gWebGL.heightPx;
|
|
1388
|
+
std::vector<unsigned char> rgba(static_cast<size_t>(w) * h * 4);
|
|
1389
|
+
gWebGL.glReadPixels(0, 0, w, h, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba.data());
|
|
1390
|
+
char path[512];
|
|
1391
|
+
std::snprintf(path, sizeof path, "%s-f%d.ppm", dumpPrefix, dumpCount);
|
|
1392
|
+
if (FILE *file = std::fopen(path, "wb")) {
|
|
1393
|
+
std::fprintf(file, "P6\n%d %d\n255\n", w, h);
|
|
1394
|
+
for (int y = h - 1; y >= 0; --y) {
|
|
1395
|
+
const unsigned char *row = rgba.data() + static_cast<size_t>(y) * w * 4;
|
|
1396
|
+
for (int x = 0; x < w; ++x) std::fwrite(row + x * 4, 1, 3, file);
|
|
1397
|
+
}
|
|
1398
|
+
std::fclose(file);
|
|
1399
|
+
smokeLog("[three-angle-metal] dumped frame %d to %s", dumpCount, path);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
// Measure presentation separately from the display-link callback. The
|
|
1404
|
+
// latter includes this wait and cannot by itself distinguish frame work
|
|
1405
|
+
// from drawable/GPU backpressure. No clock reads run when profiling is off.
|
|
1406
|
+
const bool profile = nativeFrameProfileEnabled();
|
|
1407
|
+
using Clock = std::chrono::steady_clock;
|
|
1408
|
+
const auto swapBegin = profile ? Clock::now() : Clock::time_point{};
|
|
1409
|
+
if (gWebGL.eglSwapBuffers(gWebGL.display, gWebGL.surface) == EGL_FALSE_VALUE) {
|
|
1410
|
+
logEglFailure("eglSwapBuffers");
|
|
1411
|
+
}
|
|
1412
|
+
if (profile) {
|
|
1413
|
+
const auto end = Clock::now();
|
|
1414
|
+
static auto windowStart = swapBegin;
|
|
1415
|
+
static double totalMs = 0.0;
|
|
1416
|
+
static double frameWorkMs = 0.0;
|
|
1417
|
+
static unsigned timedFrames = 0;
|
|
1418
|
+
static double maximumMs = 0.0;
|
|
1419
|
+
static unsigned frames = 0;
|
|
1420
|
+
const double elapsedMs = std::chrono::duration<double, std::milli>(end - swapBegin).count();
|
|
1421
|
+
totalMs += elapsedMs;
|
|
1422
|
+
if (gProfileSizeSyncPending) {
|
|
1423
|
+
frameWorkMs += std::chrono::duration<double, std::milli>(swapBegin - gProfileSizeSync).count();
|
|
1424
|
+
++timedFrames;
|
|
1425
|
+
gProfileSizeSyncPending = false;
|
|
1426
|
+
}
|
|
1427
|
+
maximumMs = std::max(maximumMs, elapsedMs);
|
|
1428
|
+
++frames;
|
|
1429
|
+
if (std::chrono::duration<double>(end - windowStart).count() >= 1.0) {
|
|
1430
|
+
smokeLog("[gea.swap-profile] swap_mean_ms=%.3f swap_max_ms=%.3f size_sync_to_swap_mean_ms=%.3f timed_frames=%u frames=%u surface=%dx%d",
|
|
1431
|
+
totalMs / frames, maximumMs, timedFrames ? frameWorkMs / timedFrames : 0.0, timedFrames, frames, gWebGL.widthPx, gWebGL.heightPx);
|
|
1432
|
+
frames = 0;
|
|
1433
|
+
totalMs = frameWorkMs = maximumMs = 0.0;
|
|
1434
|
+
timedFrames = 0;
|
|
1435
|
+
windowStart = end;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
enum WebGLOp {
|
|
1441
|
+
OP_CLEAR_COLOR = 1,
|
|
1442
|
+
OP_CLEAR = 2,
|
|
1443
|
+
OP_VIEWPORT = 3,
|
|
1444
|
+
OP_ENABLE = 4,
|
|
1445
|
+
OP_DISABLE = 5,
|
|
1446
|
+
OP_DEPTH_FUNC = 6,
|
|
1447
|
+
OP_DEPTH_MASK = 7,
|
|
1448
|
+
OP_COLOR_MASK = 8,
|
|
1449
|
+
OP_BLEND_FUNC = 9,
|
|
1450
|
+
OP_BLEND_FUNC_SEPARATE = 10,
|
|
1451
|
+
OP_BLEND_EQUATION = 11,
|
|
1452
|
+
OP_BLEND_EQUATION_SEPARATE = 12,
|
|
1453
|
+
OP_CULL_FACE = 13,
|
|
1454
|
+
OP_FRONT_FACE = 14,
|
|
1455
|
+
OP_CREATE_BUFFER = 15,
|
|
1456
|
+
OP_DELETE_BUFFER = 16,
|
|
1457
|
+
OP_BIND_BUFFER = 17,
|
|
1458
|
+
OP_CREATE_SHADER = 20,
|
|
1459
|
+
OP_SHADER_SOURCE = 21,
|
|
1460
|
+
OP_COMPILE_SHADER = 22,
|
|
1461
|
+
OP_GET_SHADER_PARAMETER = 23,
|
|
1462
|
+
OP_DELETE_SHADER = 24,
|
|
1463
|
+
OP_CREATE_PROGRAM = 25,
|
|
1464
|
+
OP_ATTACH_SHADER = 26,
|
|
1465
|
+
OP_LINK_PROGRAM = 27,
|
|
1466
|
+
OP_GET_PROGRAM_PARAMETER = 28,
|
|
1467
|
+
OP_DELETE_PROGRAM = 29,
|
|
1468
|
+
OP_USE_PROGRAM = 30,
|
|
1469
|
+
OP_GET_ATTRIB_LOCATION = 31,
|
|
1470
|
+
OP_GET_UNIFORM_LOCATION = 32,
|
|
1471
|
+
OP_ENABLE_VERTEX_ATTRIB_ARRAY = 33,
|
|
1472
|
+
OP_DISABLE_VERTEX_ATTRIB_ARRAY = 34,
|
|
1473
|
+
OP_VERTEX_ATTRIB_POINTER = 35,
|
|
1474
|
+
OP_UNIFORM_1F = 37,
|
|
1475
|
+
OP_UNIFORM_1I = 38,
|
|
1476
|
+
OP_UNIFORM_2F = 39,
|
|
1477
|
+
OP_UNIFORM_3F = 40,
|
|
1478
|
+
OP_UNIFORM_4F = 41,
|
|
1479
|
+
OP_DRAW_ELEMENTS = 42,
|
|
1480
|
+
OP_DRAW_ARRAYS = 43,
|
|
1481
|
+
OP_ACTIVE_TEXTURE = 44,
|
|
1482
|
+
OP_CREATE_TEXTURE = 45,
|
|
1483
|
+
OP_DELETE_TEXTURE = 46,
|
|
1484
|
+
OP_BIND_TEXTURE = 47,
|
|
1485
|
+
OP_TEX_PARAMETERI = 48,
|
|
1486
|
+
OP_PIXEL_STOREI = 49,
|
|
1487
|
+
OP_GENERATE_MIPMAP = 52,
|
|
1488
|
+
OP_CREATE_VERTEX_ARRAY = 53,
|
|
1489
|
+
OP_BIND_VERTEX_ARRAY = 54,
|
|
1490
|
+
OP_DELETE_VERTEX_ARRAY = 55,
|
|
1491
|
+
OP_GET_ERROR = 56,
|
|
1492
|
+
OP_SCISSOR = 57,
|
|
1493
|
+
OP_LINE_WIDTH = 58,
|
|
1494
|
+
OP_POLYGON_OFFSET = 59,
|
|
1495
|
+
OP_CLEAR_DEPTH = 60,
|
|
1496
|
+
OP_CLEAR_STENCIL = 61,
|
|
1497
|
+
OP_STENCIL_MASK = 62,
|
|
1498
|
+
OP_STENCIL_FUNC = 63,
|
|
1499
|
+
OP_STENCIL_OP = 64,
|
|
1500
|
+
OP_STENCIL_FUNC_SEPARATE = 65,
|
|
1501
|
+
OP_STENCIL_OP_SEPARATE = 66,
|
|
1502
|
+
OP_STENCIL_MASK_SEPARATE = 67,
|
|
1503
|
+
OP_CREATE_FRAMEBUFFER = 68,
|
|
1504
|
+
OP_BIND_FRAMEBUFFER = 69,
|
|
1505
|
+
OP_DELETE_FRAMEBUFFER = 70,
|
|
1506
|
+
OP_FRAMEBUFFER_TEXTURE_2D = 71,
|
|
1507
|
+
OP_CHECK_FRAMEBUFFER_STATUS = 72,
|
|
1508
|
+
OP_CREATE_RENDERBUFFER = 73,
|
|
1509
|
+
OP_BIND_RENDERBUFFER = 74,
|
|
1510
|
+
OP_RENDERBUFFER_STORAGE = 75,
|
|
1511
|
+
OP_FRAMEBUFFER_RENDERBUFFER = 76,
|
|
1512
|
+
OP_DELETE_RENDERBUFFER = 77,
|
|
1513
|
+
OP_DRAW_BUFFERS = 78,
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
static GLuint generatedBuffer() {
|
|
1517
|
+
GLuint handle = 0;
|
|
1518
|
+
gWebGL.glGenBuffers(1, &handle);
|
|
1519
|
+
return handle;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
static GLuint generatedTexture() {
|
|
1523
|
+
GLuint handle = 0;
|
|
1524
|
+
gWebGL.glGenTextures(1, &handle);
|
|
1525
|
+
return handle;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
static GLuint generatedVertexArray() {
|
|
1529
|
+
GLuint handle = 0;
|
|
1530
|
+
gWebGL.glGenVertexArrays(1, &handle);
|
|
1531
|
+
return handle;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
static GLuint generatedFramebuffer() {
|
|
1535
|
+
GLuint handle = 0;
|
|
1536
|
+
gWebGL.glGenFramebuffers(1, &handle);
|
|
1537
|
+
return handle;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
static GLuint generatedRenderbuffer() {
|
|
1541
|
+
GLuint handle = 0;
|
|
1542
|
+
gWebGL.glGenRenderbuffers(1, &handle);
|
|
1543
|
+
return handle;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
static bool ensureWebGLCurrent(const char *step) {
|
|
1547
|
+
return gWebGL.ready && makeCurrent(step);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
static GLenum asGLenum(double value) { return static_cast<GLenum>(value); }
|
|
1551
|
+
static GLint asGLint(double value) { return static_cast<GLint>(value); }
|
|
1552
|
+
static GLsizei asGLsizei(double value) { return static_cast<GLsizei>(value); }
|
|
1553
|
+
static GLuint asGLuint(double value) { return static_cast<GLuint>(value); }
|
|
1554
|
+
static GLbitfield asGLbitfield(double value) { return static_cast<GLbitfield>(value); }
|
|
1555
|
+
|
|
1556
|
+
static int vectorTypeCodeForGLType(double value) {
|
|
1557
|
+
const GLenum type = asGLenum(value);
|
|
1558
|
+
if (type == 0x1403) return 2; // UNSIGNED_SHORT
|
|
1559
|
+
if (type == 0x1405) return 3; // UNSIGNED_INT
|
|
1560
|
+
if (type == 0x1401) return 4; // UNSIGNED_BYTE
|
|
1561
|
+
return 1;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
#if defined(GEA_PROFILE_ALLOCATIONS) || defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1565
|
+
namespace {
|
|
1566
|
+
using ProfileClock = std::chrono::steady_clock;
|
|
1567
|
+
static double nativeThreadCpuMs() {
|
|
1568
|
+
#if defined(_WIN32)
|
|
1569
|
+
FILETIME created{}, exited{}, kernel{}, user{};
|
|
1570
|
+
if (!GetThreadTimes(GetCurrentThread(), &created, &exited, &kernel, &user)) return 0;
|
|
1571
|
+
const auto ticks = [](const FILETIME& t) { return (static_cast<std::uint64_t>(t.dwHighDateTime) << 32) | t.dwLowDateTime; };
|
|
1572
|
+
return static_cast<double>(ticks(kernel) + ticks(user)) / 10000.0;
|
|
1573
|
+
#else
|
|
1574
|
+
return 0;
|
|
1575
|
+
#endif
|
|
1576
|
+
}
|
|
1577
|
+
struct NativeFrameProfile {
|
|
1578
|
+
double phases[4]{}, phaseCpu[4]{}, cpuStarted = 0, host[4]{};
|
|
1579
|
+
std::uint64_t calls[4]{};
|
|
1580
|
+
unsigned phase = 0, frames = 0;
|
|
1581
|
+
ProfileClock::time_point started{};
|
|
1582
|
+
};
|
|
1583
|
+
thread_local NativeFrameProfile nativeProfile;
|
|
1584
|
+
struct NativeCallTime { const char* name = nullptr; double ms = 0; std::uint64_t calls = 0; };
|
|
1585
|
+
thread_local NativeCallTime nativeCalls[128];
|
|
1586
|
+
struct NativeHostProfile {
|
|
1587
|
+
unsigned category;
|
|
1588
|
+
const char* name;
|
|
1589
|
+
ProfileClock::time_point start = ProfileClock::now();
|
|
1590
|
+
explicit NativeHostProfile(const char* step) : category(std::strstr(step, "draw") ? 0 : std::strstr(step, "uniform") ? 1 : std::strstr(step, "buffer") || std::strstr(step, "Buffer") || std::strstr(step, "tex") ? 2 : 3), name(step) {}
|
|
1591
|
+
~NativeHostProfile() {
|
|
1592
|
+
const double elapsed = std::chrono::duration<double, std::milli>(ProfileClock::now() - start).count();
|
|
1593
|
+
nativeProfile.host[category] += elapsed;
|
|
1594
|
+
for (auto& row : nativeCalls) {
|
|
1595
|
+
if (!row.name) row.name = name;
|
|
1596
|
+
if (row.name == name || std::strcmp(row.name, name) == 0) { row.ms += elapsed; ++row.calls; break; }
|
|
1597
|
+
}
|
|
1598
|
+
++nativeProfile.calls[category];
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
#define GEA_PROFILE_HOST(step) NativeHostProfile geaHostScope(step)
|
|
1603
|
+
#else
|
|
1604
|
+
#define GEA_PROFILE_HOST(step)
|
|
1605
|
+
#endif
|
|
1606
|
+
|
|
1607
|
+
extern "C" void gea_three_native_profile_phase(double phaseValue) {
|
|
1608
|
+
#if defined(GEA_PROFILE_ALLOCATIONS) || defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1609
|
+
auto& p = nativeProfile;
|
|
1610
|
+
const auto now = ProfileClock::now();
|
|
1611
|
+
const double cpu = nativeThreadCpuMs();
|
|
1612
|
+
const unsigned next = static_cast<unsigned>(phaseValue);
|
|
1613
|
+
if (p.phase) p.phaseCpu[p.phase] += cpu - p.cpuStarted;
|
|
1614
|
+
if (p.phase) p.phases[p.phase] += std::chrono::duration<double, std::milli>(now - p.started).count();
|
|
1615
|
+
if (p.phase == 2 && next == 0 && ++p.frames == 60) {
|
|
1616
|
+
std::fprintf(stderr, "[gea.regions] frames=%u update_ms=%.6f render_ms=%.6f scene_ms=%.6f update_cpu_ms=%.6f render_cpu_ms=%.6f scene_cpu_ms=%.6f draw_ms=%.6f uniform_ms=%.6f upload_ms=%.6f state_ms=%.6f draw_calls=%llu uniform_calls=%llu upload_calls=%llu state_calls=%llu\n",
|
|
1617
|
+
p.frames, p.phases[1] / p.frames, p.phases[2] / p.frames, p.phases[3] / p.frames, p.phaseCpu[1] / p.frames, p.phaseCpu[2] / p.frames, p.phaseCpu[3] / p.frames,
|
|
1618
|
+
p.host[0] / p.frames, p.host[1] / p.frames, p.host[2] / p.frames, p.host[3] / p.frames,
|
|
1619
|
+
p.calls[0], p.calls[1], p.calls[2], p.calls[3]);
|
|
1620
|
+
for (auto& row : nativeCalls) {
|
|
1621
|
+
if (row.calls) std::fprintf(stderr, "[gea.host] name=%s calls=%llu ms=%.6f\n", row.name, row.calls, row.ms);
|
|
1622
|
+
row = {};
|
|
1623
|
+
}
|
|
1624
|
+
p = {};
|
|
1625
|
+
}
|
|
1626
|
+
p.phase = next; p.started = now; p.cpuStarted = cpu;
|
|
1627
|
+
#endif
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
#define GEA_WEBGL_VOID(step) GEA_PROFILE_HOST(step); if (!ensureWebGLCurrent(step)) return
|
|
1631
|
+
#define GEA_WEBGL_DOUBLE(step) GEA_PROFILE_HOST(step); if (!ensureWebGLCurrent(step)) return 0.0
|
|
1632
|
+
|
|
1633
|
+
// GEA_WEBGL_TRAP=1: poll glGetError at every intrinsic ENTRY and log the
|
|
1634
|
+
// first offenders. GL errors are sticky until polled, so the intrinsic that
|
|
1635
|
+
// OBSERVES the error ran right after the intrinsic that PRODUCED it — the
|
|
1636
|
+
// log sequence pins the producer of the chronic per-frame GL_INVALID_OPERATION.
|
|
1637
|
+
static void geaTrapCheck(const char *where) {
|
|
1638
|
+
if (!nativeWebGLTrapEnabled() || !gWebGL.glGetError || !gWebGL.ready) return;
|
|
1639
|
+
static int logged = 0;
|
|
1640
|
+
if (logged >= 60) return;
|
|
1641
|
+
const GLenum err = gWebGL.glGetError();
|
|
1642
|
+
if (err == 0) return;
|
|
1643
|
+
++logged;
|
|
1644
|
+
smokeLog("[gea-webgl-trap] error=0x%04x observed entering %s", err, where);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
#if defined(GEA_PROFILE_ALLOCATIONS) || defined(GEA_PROFILE_NATIVE_FRAMES) || defined(_WIN32)
|
|
1648
|
+
// Streaming policies preserve the GL object identity (and VAO
|
|
1649
|
+
// references). Only known full vertex/index replacements may discard storage.
|
|
1650
|
+
// Partial updates and other targets always retain the ordinary subdata path.
|
|
1651
|
+
struct NativeBufferState { GLsizeiptr size = 0; GLenum usage = 0; GLuint staging[3]{}; unsigned next = 0; };
|
|
1652
|
+
static GLuint gArrayBuffer = 0, gCopyReadBuffer = 0, gCopyWriteBuffer = 0;
|
|
1653
|
+
static std::unordered_map<GLuint, NativeBufferState> gBuffers;
|
|
1654
|
+
static GLuint nativeUploadBuffer(GLenum target) {
|
|
1655
|
+
if (target == 0x8892) return gArrayBuffer;
|
|
1656
|
+
if (target == 0x8893) {
|
|
1657
|
+
// The element binding belongs to the current VAO. Query it instead of
|
|
1658
|
+
// shadowing one global binding that becomes stale on a VAO switch.
|
|
1659
|
+
GLint buffer = 0;
|
|
1660
|
+
gWebGL.glGetIntegerv(0x8895, &buffer);
|
|
1661
|
+
return static_cast<GLuint>(buffer);
|
|
1662
|
+
}
|
|
1663
|
+
return 0;
|
|
1664
|
+
}
|
|
1665
|
+
static int nativeBufferStreamingMode() {
|
|
1666
|
+
static const int mode = [] {
|
|
1667
|
+
const char* value = std::getenv("GEA_WEBGL_BUFFER_STREAMING");
|
|
1668
|
+
if (value) return std::atoi(value);
|
|
1669
|
+
#if defined(_WIN32)
|
|
1670
|
+
return 8; // D3D11: stage vertex writes; retain CPU-accessible index data.
|
|
1671
|
+
#else
|
|
1672
|
+
return 0;
|
|
1673
|
+
#endif
|
|
1674
|
+
}();
|
|
1675
|
+
return mode;
|
|
1676
|
+
}
|
|
1677
|
+
static GLint nativeUploadUniformLimit() {
|
|
1678
|
+
static const GLint limit = [] {
|
|
1679
|
+
GLint value = 0;
|
|
1680
|
+
gWebGL.glGetIntegerv(0x8A30, &value); // MAX_UNIFORM_BLOCK_SIZE
|
|
1681
|
+
return value;
|
|
1682
|
+
}();
|
|
1683
|
+
return limit;
|
|
1684
|
+
}
|
|
1685
|
+
static void bindNativeBuffer(GLenum target, GLuint handle) {
|
|
1686
|
+
gWebGL.glBindBuffer(target, handle);
|
|
1687
|
+
#if !defined(GEA_PROFILE_ALLOCATIONS) && !defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1688
|
+
if (nativeBufferStreamingMode() == 0) return;
|
|
1689
|
+
#endif
|
|
1690
|
+
if (target == 0x8892) gArrayBuffer = handle;
|
|
1691
|
+
if (target == 0x8F36) gCopyReadBuffer = handle;
|
|
1692
|
+
if (target == 0x8F37) gCopyWriteBuffer = handle;
|
|
1693
|
+
}
|
|
1694
|
+
static void deleteNativeBuffer(GLuint handle) {
|
|
1695
|
+
gWebGL.glDeleteBuffers(1, &handle);
|
|
1696
|
+
#if !defined(GEA_PROFILE_ALLOCATIONS) && !defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1697
|
+
if (nativeBufferStreamingMode() == 0) return;
|
|
1698
|
+
#endif
|
|
1699
|
+
const auto found = gBuffers.find(handle);
|
|
1700
|
+
if (found != gBuffers.end()) gWebGL.glDeleteBuffers(3, found->second.staging);
|
|
1701
|
+
gBuffers.erase(handle);
|
|
1702
|
+
if (gArrayBuffer == handle) gArrayBuffer = 0;
|
|
1703
|
+
if (gCopyReadBuffer == handle) gCopyReadBuffer = 0;
|
|
1704
|
+
if (gCopyWriteBuffer == handle) gCopyWriteBuffer = 0;
|
|
1705
|
+
}
|
|
1706
|
+
static void nativeBufferData(GLenum target, GLsizeiptr size, const void* bytes, GLenum usage) {
|
|
1707
|
+
gWebGL.glBufferData(target, size, bytes, usage);
|
|
1708
|
+
#if !defined(GEA_PROFILE_ALLOCATIONS) && !defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1709
|
+
if (nativeBufferStreamingMode() == 0) return;
|
|
1710
|
+
#endif
|
|
1711
|
+
const GLuint buffer = nativeUploadBuffer(target);
|
|
1712
|
+
if (buffer && size >= 0) {
|
|
1713
|
+
auto& state = gBuffers[buffer];
|
|
1714
|
+
if (state.size != size) { gWebGL.glDeleteBuffers(3, state.staging); state = {}; }
|
|
1715
|
+
state.size = size; state.usage = usage;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
static void nativeBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* bytes) {
|
|
1719
|
+
#if !defined(GEA_PROFILE_ALLOCATIONS) && !defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1720
|
+
if (nativeBufferStreamingMode() == 0) { gWebGL.glBufferSubData(target, offset, size, bytes); return; }
|
|
1721
|
+
#endif
|
|
1722
|
+
const GLuint buffer = nativeUploadBuffer(target);
|
|
1723
|
+
const auto found = gBuffers.find(buffer);
|
|
1724
|
+
const bool full = found != gBuffers.end() && offset == 0 && size > 0 && size == found->second.size;
|
|
1725
|
+
const int mode = nativeBufferStreamingMode();
|
|
1726
|
+
// 1..4 retain the original vertex-only experiments. 5/6 include indices;
|
|
1727
|
+
// 7 isolates index streaming; 8 combines vertex staging with dynamic indices.
|
|
1728
|
+
// 9 writes small vertex staging buffers through uniform storage. ANGLE D3D11
|
|
1729
|
+
// maps this storage with WRITE_DISCARD instead of waiting on a staging map.
|
|
1730
|
+
const bool eligible = full && (mode >= 5 || target == 0x8892);
|
|
1731
|
+
#if defined(GEA_PROFILE_ALLOCATIONS) || defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1732
|
+
const auto start = ProfileClock::now();
|
|
1733
|
+
#endif
|
|
1734
|
+
if (eligible && (mode == 4 || mode == 6 || ((mode == 8 || mode == 9) && target == 0x8892)) && gWebGL.glCopyBufferSubData) {
|
|
1735
|
+
auto& state = found->second;
|
|
1736
|
+
if (!state.staging[0]) {
|
|
1737
|
+
gWebGL.glGenBuffers(3, state.staging);
|
|
1738
|
+
for (GLuint staging : state.staging) {
|
|
1739
|
+
gWebGL.glBindBuffer(0x8F36, staging);
|
|
1740
|
+
gWebGL.glBufferData(0x8F36, size, nullptr, 0x88E4);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
// Rotate upload storage while keeping the destination's object identity,
|
|
1744
|
+
// so every existing VAO continues referencing the correct buffer.
|
|
1745
|
+
const GLuint staging = state.staging[state.next];
|
|
1746
|
+
gWebGL.glBindBuffer(0x8F36, staging);
|
|
1747
|
+
state.next = (state.next + 1) % 3;
|
|
1748
|
+
if (mode == 9 && size <= nativeUploadUniformLimit()) {
|
|
1749
|
+
// Query rather than shadow this binding: bindBufferBase/Range may also
|
|
1750
|
+
// change it. The destination GL object, usage and VAOs remain untouched.
|
|
1751
|
+
GLint uniform = 0;
|
|
1752
|
+
gWebGL.glGetIntegerv(0x8A28, &uniform); // UNIFORM_BUFFER_BINDING
|
|
1753
|
+
gWebGL.glBindBuffer(0x8A11, staging); // UNIFORM_BUFFER
|
|
1754
|
+
gWebGL.glBufferSubData(0x8A11, 0, size, bytes);
|
|
1755
|
+
gWebGL.glBindBuffer(0x8A11, static_cast<GLuint>(uniform));
|
|
1756
|
+
} else {
|
|
1757
|
+
gWebGL.glBufferSubData(0x8F36, 0, size, bytes);
|
|
1758
|
+
}
|
|
1759
|
+
gWebGL.glBindBuffer(0x8F37, buffer);
|
|
1760
|
+
gWebGL.glCopyBufferSubData(0x8F36, 0x8F37, 0, 0, size);
|
|
1761
|
+
gWebGL.glBindBuffer(0x8F36, gCopyReadBuffer);
|
|
1762
|
+
gWebGL.glBindBuffer(0x8F37, gCopyWriteBuffer);
|
|
1763
|
+
} else if (eligible && mode == 1) {
|
|
1764
|
+
gWebGL.glBufferData(target, size, nullptr, found->second.usage);
|
|
1765
|
+
gWebGL.glBufferSubData(target, offset, size, bytes);
|
|
1766
|
+
} else if (eligible && (mode == 3 || mode == 5 || ((mode == 7 || mode == 8 || mode == 9) && target == 0x8893))) {
|
|
1767
|
+
gWebGL.glBufferData(target, size, bytes, 0x88E0); // STREAM_DRAW selects ANGLE dynamic storage.
|
|
1768
|
+
} else if (eligible && mode == 2) {
|
|
1769
|
+
gWebGL.glBufferData(target, size, bytes, found->second.usage);
|
|
1770
|
+
} else {
|
|
1771
|
+
gWebGL.glBufferSubData(target, offset, size, bytes);
|
|
1772
|
+
}
|
|
1773
|
+
#if defined(GEA_PROFILE_ALLOCATIONS) || defined(GEA_PROFILE_NATIVE_FRAMES)
|
|
1774
|
+
// Fixed storage keeps the profiler from contributing per-upload heap work.
|
|
1775
|
+
struct Row { GLuint buffer = 0; GLsizeiptr size = 0; GLintptr offset = 0; GLenum target = 0, usage = 0; unsigned calls = 0, full = 0; double ms = 0, maximum = 0; };
|
|
1776
|
+
static Row rows[32];
|
|
1777
|
+
static auto window = start;
|
|
1778
|
+
const double elapsed = std::chrono::duration<double, std::milli>(ProfileClock::now() - start).count();
|
|
1779
|
+
for (auto& row : rows) {
|
|
1780
|
+
if (row.calls && (row.buffer != buffer || row.size != size || row.offset != offset || row.target != target)) continue;
|
|
1781
|
+
row.buffer = buffer; row.size = size; row.offset = offset; row.target = target;
|
|
1782
|
+
row.usage = found != gBuffers.end() ? found->second.usage : 0;
|
|
1783
|
+
++row.calls; row.full += full; row.ms += elapsed; row.maximum = std::max(row.maximum, elapsed);
|
|
1784
|
+
break;
|
|
1785
|
+
}
|
|
1786
|
+
if (std::chrono::duration<double>(start - window).count() >= 1.0) {
|
|
1787
|
+
for (auto& row : rows) {
|
|
1788
|
+
if (row.calls) std::fprintf(stderr, "[gea.buffer] mode=%d buffer=%u target=%u usage=%u bytes=%lld offset=%lld calls=%u full=%u ms=%.6f max_ms=%.6f\n",
|
|
1789
|
+
mode, row.buffer, row.target, row.usage, static_cast<long long>(row.size), static_cast<long long>(row.offset), row.calls, row.full, row.ms, row.maximum);
|
|
1790
|
+
row = {};
|
|
1791
|
+
}
|
|
1792
|
+
window = start;
|
|
1793
|
+
}
|
|
1794
|
+
#endif
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// Streaming experiment ends here.
|
|
1798
|
+
#else
|
|
1799
|
+
static void bindNativeBuffer(GLenum target, GLuint handle) { gWebGL.glBindBuffer(target, handle); }
|
|
1800
|
+
static void deleteNativeBuffer(GLuint handle) { gWebGL.glDeleteBuffers(1, &handle); }
|
|
1801
|
+
static void nativeBufferData(GLenum target, GLsizeiptr size, const void* bytes, GLenum usage) { gWebGL.glBufferData(target, size, bytes, usage); }
|
|
1802
|
+
static void nativeBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* bytes) { gWebGL.glBufferSubData(target, offset, size, bytes); }
|
|
1803
|
+
#endif
|
|
1804
|
+
|
|
1805
|
+
extern "C" double gea_three_webgl_create_buffer() {
|
|
1806
|
+
geaTrapCheck("gea_three_webgl_create_buffer"); GEA_WEBGL_DOUBLE("createBuffer"); return generatedBuffer(); }
|
|
1807
|
+
extern "C" void gea_three_webgl_delete_buffer(double buffer) {
|
|
1808
|
+
geaTrapCheck("gea_three_webgl_delete_buffer"); GEA_WEBGL_VOID("deleteBuffer"); deleteNativeBuffer(asGLuint(buffer)); }
|
|
1809
|
+
extern "C" void gea_three_webgl_bind_buffer(double target, double buffer) {
|
|
1810
|
+
geaTrapCheck("gea_three_webgl_bind_buffer"); GEA_WEBGL_VOID("bindBuffer"); bindNativeBuffer(asGLenum(target), asGLuint(buffer)); }
|
|
1811
|
+
|
|
1812
|
+
extern "C" double gea_three_webgl_create_shader(double type) {
|
|
1813
|
+
geaTrapCheck("gea_three_webgl_create_shader"); GEA_WEBGL_DOUBLE("createShader"); return gWebGL.glCreateShader(asGLenum(type)); }
|
|
1814
|
+
extern "C" void gea_three_webgl_shader_source(double shader, std::string source) {
|
|
1815
|
+
geaTrapCheck("gea_three_webgl_shader_source");
|
|
1816
|
+
GEA_WEBGL_VOID("shaderSource");
|
|
1817
|
+
const char *sourceBytes = source.c_str();
|
|
1818
|
+
const GLint length = static_cast<GLint>(source.size());
|
|
1819
|
+
gWebGL.glShaderSource(asGLuint(shader), 1, &sourceBytes, &length);
|
|
1820
|
+
}
|
|
1821
|
+
extern "C" void gea_three_webgl_compile_shader(double shader) {
|
|
1822
|
+
geaTrapCheck("gea_three_webgl_compile_shader");
|
|
1823
|
+
GEA_WEBGL_VOID("compileShader");
|
|
1824
|
+
gWebGL.glCompileShader(asGLuint(shader));
|
|
1825
|
+
}
|
|
1826
|
+
extern "C" double gea_three_webgl_get_shader_parameter(double shader, double pname) {
|
|
1827
|
+
geaTrapCheck("gea_three_webgl_get_shader_parameter");
|
|
1828
|
+
GEA_WEBGL_DOUBLE("getShaderParameter");
|
|
1829
|
+
GLint value = 0;
|
|
1830
|
+
gWebGL.glGetShaderiv(asGLuint(shader), asGLenum(pname), &value);
|
|
1831
|
+
return value;
|
|
1832
|
+
}
|
|
1833
|
+
extern "C" void gea_three_webgl_delete_shader(double shader) {
|
|
1834
|
+
geaTrapCheck("gea_three_webgl_delete_shader"); GEA_WEBGL_VOID("deleteShader"); gWebGL.glDeleteShader(asGLuint(shader)); }
|
|
1835
|
+
|
|
1836
|
+
extern "C" double gea_three_webgl_create_program() {
|
|
1837
|
+
geaTrapCheck("gea_three_webgl_create_program"); GEA_WEBGL_DOUBLE("createProgram"); return gWebGL.glCreateProgram(); }
|
|
1838
|
+
extern "C" void gea_three_webgl_attach_shader(double program, double shader) {
|
|
1839
|
+
geaTrapCheck("gea_three_webgl_attach_shader"); GEA_WEBGL_VOID("attachShader"); gWebGL.glAttachShader(asGLuint(program), asGLuint(shader)); }
|
|
1840
|
+
extern "C" void gea_three_webgl_link_program(double program) {
|
|
1841
|
+
geaTrapCheck("gea_three_webgl_link_program");
|
|
1842
|
+
GEA_WEBGL_VOID("linkProgram");
|
|
1843
|
+
gWebGL.glLinkProgram(asGLuint(program));
|
|
1844
|
+
}
|
|
1845
|
+
extern "C" double gea_three_webgl_get_program_parameter(double program, double pname) {
|
|
1846
|
+
geaTrapCheck("gea_three_webgl_get_program_parameter");
|
|
1847
|
+
GEA_WEBGL_DOUBLE("getProgramParameter");
|
|
1848
|
+
GLint value = 0;
|
|
1849
|
+
gWebGL.glGetProgramiv(asGLuint(program), asGLenum(pname), &value);
|
|
1850
|
+
return value;
|
|
1851
|
+
}
|
|
1852
|
+
extern "C" void gea_three_webgl_delete_program(double program) {
|
|
1853
|
+
geaTrapCheck("gea_three_webgl_delete_program"); GEA_WEBGL_VOID("deleteProgram"); gWebGL.glDeleteProgram(asGLuint(program)); }
|
|
1854
|
+
extern "C" void gea_three_webgl_use_program(double program) {
|
|
1855
|
+
geaTrapCheck("gea_three_webgl_use_program"); GEA_WEBGL_VOID("useProgram");
|
|
1856
|
+
if (nativeWebGLTrapEnabled()) {
|
|
1857
|
+
static int logged = 0;
|
|
1858
|
+
if (logged++ < 40) smokeLog("[gea-webgl-trap] useProgram(%f -> %u)", program, asGLuint(program));
|
|
1859
|
+
}
|
|
1860
|
+
gWebGL.glUseProgram(asGLuint(program)); }
|
|
1861
|
+
extern "C" double gea_three_webgl_get_attrib_location(double program, std::string name) {
|
|
1862
|
+
geaTrapCheck("gea_three_webgl_get_attrib_location");
|
|
1863
|
+
GEA_WEBGL_DOUBLE("getAttribLocation");
|
|
1864
|
+
return gWebGL.glGetAttribLocation(asGLuint(program), name.c_str());
|
|
1865
|
+
}
|
|
1866
|
+
extern "C" double gea_three_webgl_get_uniform_location(double program, std::string name) {
|
|
1867
|
+
geaTrapCheck("gea_three_webgl_get_uniform_location");
|
|
1868
|
+
GEA_WEBGL_DOUBLE("getUniformLocation");
|
|
1869
|
+
const GLint loc = gWebGL.glGetUniformLocation(asGLuint(program), name.c_str());
|
|
1870
|
+
if (nativeWebGLTrapEnabled()) {
|
|
1871
|
+
static int logged = 0;
|
|
1872
|
+
if (logged++ < 120) {
|
|
1873
|
+
smokeLog("[gea-webgl-trap] getUniformLocation(prog=%u, %s) = %d", asGLuint(program), name.c_str(), loc);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
return loc;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// Active-uniform/attrib reflection, serialized as "name|type|size" (empty
|
|
1880
|
+
// string past the end). This is REAL driver reflection via ANGLE — the TS shim
|
|
1881
|
+
// must NOT reconstruct it by text-parsing GLSL: three.js relies on
|
|
1882
|
+
// glGetActiveUniform semantics (struct arrays expanded per member as
|
|
1883
|
+
// "lights[0].member", basic arrays reported as "name[0]" with size=N) to build
|
|
1884
|
+
// its uniform upload tree; a source-text parser misses #define-sized arrays
|
|
1885
|
+
// and struct expansion, which silently kills all struct uniforms (lights).
|
|
1886
|
+
static std::string geaActiveInfoString(bool uniform, double program, double index) {
|
|
1887
|
+
GLchar name[512] = {0};
|
|
1888
|
+
GLsizei length = 0;
|
|
1889
|
+
GLint size = 0;
|
|
1890
|
+
GLenum type = 0;
|
|
1891
|
+
if (uniform) {
|
|
1892
|
+
if (!gWebGL.glGetActiveUniform) return std::string();
|
|
1893
|
+
gWebGL.glGetActiveUniform(asGLuint(program), asGLuint(index), sizeof name, &length, &size, &type, name);
|
|
1894
|
+
} else {
|
|
1895
|
+
if (!gWebGL.glGetActiveAttrib) return std::string();
|
|
1896
|
+
gWebGL.glGetActiveAttrib(asGLuint(program), asGLuint(index), sizeof name, &length, &size, &type, name);
|
|
1897
|
+
}
|
|
1898
|
+
if (length <= 0) return std::string();
|
|
1899
|
+
std::string out(name, static_cast<size_t>(length));
|
|
1900
|
+
out += "|";
|
|
1901
|
+
out += std::to_string(static_cast<unsigned>(type));
|
|
1902
|
+
out += "|";
|
|
1903
|
+
out += std::to_string(size);
|
|
1904
|
+
if (nativeWebGLTrapEnabled()) {
|
|
1905
|
+
static int logged = 0;
|
|
1906
|
+
if (logged++ < 240) {
|
|
1907
|
+
smokeLog("[gea-webgl-trap] active%s(prog=%u, index=%u, name=%s, type=%u, size=%d)",
|
|
1908
|
+
uniform ? "Uniform" : "Attrib", asGLuint(program), asGLuint(index), name,
|
|
1909
|
+
static_cast<unsigned>(type), size);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
return out;
|
|
1913
|
+
}
|
|
1914
|
+
extern "C" std::string gea_three_webgl_get_active_uniform_info(double program, double index) {
|
|
1915
|
+
geaTrapCheck("gea_three_webgl_get_active_uniform_info");
|
|
1916
|
+
if (!ensureWebGLCurrent("getActiveUniform")) return std::string();
|
|
1917
|
+
return geaActiveInfoString(true, program, index);
|
|
1918
|
+
}
|
|
1919
|
+
extern "C" std::string gea_three_webgl_get_active_attrib_info(double program, double index) {
|
|
1920
|
+
geaTrapCheck("gea_three_webgl_get_active_attrib_info");
|
|
1921
|
+
if (!ensureWebGLCurrent("getActiveAttrib")) return std::string();
|
|
1922
|
+
return geaActiveInfoString(false, program, index);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
extern "C" void gea_three_webgl_enable_vertex_attrib_array(double index) {
|
|
1926
|
+
geaTrapCheck("gea_three_webgl_enable_vertex_attrib_array"); GEA_WEBGL_VOID("enableVertexAttribArray"); gWebGL.glEnableVertexAttribArray(asGLuint(index)); }
|
|
1927
|
+
extern "C" void gea_three_webgl_disable_vertex_attrib_array(double index) {
|
|
1928
|
+
geaTrapCheck("gea_three_webgl_disable_vertex_attrib_array"); GEA_WEBGL_VOID("disableVertexAttribArray"); gWebGL.glDisableVertexAttribArray(asGLuint(index)); }
|
|
1929
|
+
extern "C" void gea_three_webgl_vertex_attrib_pointer(double index, double size, double type, double normalized, double stride, double offset) {
|
|
1930
|
+
geaTrapCheck("gea_three_webgl_vertex_attrib_pointer");
|
|
1931
|
+
GEA_WEBGL_VOID("vertexAttribPointer");
|
|
1932
|
+
gWebGL.glVertexAttribPointer(
|
|
1933
|
+
asGLuint(index),
|
|
1934
|
+
asGLint(size),
|
|
1935
|
+
asGLenum(type),
|
|
1936
|
+
normalized != 0.0,
|
|
1937
|
+
asGLsizei(stride),
|
|
1938
|
+
reinterpret_cast<const void *>(static_cast<std::uintptr_t>(offset))
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
extern "C" void gea_three_webgl_uniform_1f(double location, double x) {
|
|
1943
|
+
geaTrapCheck("gea_three_webgl_uniform_1f"); GEA_WEBGL_VOID("uniform1f");
|
|
1944
|
+
gWebGL.glUniform1f(asGLint(location), static_cast<GLfloat>(x)); }
|
|
1945
|
+
extern "C" void gea_three_webgl_uniform_1i(double location, double x) {
|
|
1946
|
+
geaTrapCheck("gea_three_webgl_uniform_1i"); GEA_WEBGL_VOID("uniform1i");
|
|
1947
|
+
gWebGL.glUniform1i(asGLint(location), asGLint(x)); }
|
|
1948
|
+
extern "C" void gea_three_webgl_uniform_1ui(double location, double x) {
|
|
1949
|
+
geaTrapCheck("gea_three_webgl_uniform_1ui"); GEA_WEBGL_VOID("uniform1ui"); gWebGL.glUniform1ui(asGLint(location), asGLuint(x)); }
|
|
1950
|
+
extern "C" void gea_three_webgl_uniform_2f(double location, double x, double y) {
|
|
1951
|
+
geaTrapCheck("gea_three_webgl_uniform_2f"); GEA_WEBGL_VOID("uniform2f");
|
|
1952
|
+
gWebGL.glUniform2f(asGLint(location), static_cast<GLfloat>(x), static_cast<GLfloat>(y)); }
|
|
1953
|
+
extern "C" void gea_three_webgl_uniform_2i(double location, double x, double y) {
|
|
1954
|
+
geaTrapCheck("gea_three_webgl_uniform_2i"); GEA_WEBGL_VOID("uniform2i"); gWebGL.glUniform2i(asGLint(location), asGLint(x), asGLint(y)); }
|
|
1955
|
+
extern "C" void gea_three_webgl_uniform_2ui(double location, double x, double y) {
|
|
1956
|
+
geaTrapCheck("gea_three_webgl_uniform_2ui"); GEA_WEBGL_VOID("uniform2ui"); gWebGL.glUniform2ui(asGLint(location), asGLuint(x), asGLuint(y)); }
|
|
1957
|
+
extern "C" void gea_three_webgl_uniform_3f(double location, double x, double y, double z) {
|
|
1958
|
+
geaTrapCheck("gea_three_webgl_uniform_3f"); GEA_WEBGL_VOID("uniform3f");
|
|
1959
|
+
gWebGL.glUniform3f(asGLint(location), static_cast<GLfloat>(x), static_cast<GLfloat>(y), static_cast<GLfloat>(z)); }
|
|
1960
|
+
extern "C" void gea_three_webgl_uniform_3i(double location, double x, double y, double z) {
|
|
1961
|
+
geaTrapCheck("gea_three_webgl_uniform_3i"); GEA_WEBGL_VOID("uniform3i"); gWebGL.glUniform3i(asGLint(location), asGLint(x), asGLint(y), asGLint(z)); }
|
|
1962
|
+
extern "C" void gea_three_webgl_uniform_3ui(double location, double x, double y, double z) {
|
|
1963
|
+
geaTrapCheck("gea_three_webgl_uniform_3ui"); GEA_WEBGL_VOID("uniform3ui"); gWebGL.glUniform3ui(asGLint(location), asGLuint(x), asGLuint(y), asGLuint(z)); }
|
|
1964
|
+
extern "C" void gea_three_webgl_uniform_4f(double location, double x, double y, double z, double w) {
|
|
1965
|
+
geaTrapCheck("gea_three_webgl_uniform_4f"); GEA_WEBGL_VOID("uniform4f"); gWebGL.glUniform4f(asGLint(location), static_cast<GLfloat>(x), static_cast<GLfloat>(y), static_cast<GLfloat>(z), static_cast<GLfloat>(w)); }
|
|
1966
|
+
extern "C" void gea_three_webgl_uniform_4i(double location, double x, double y, double z, double w) {
|
|
1967
|
+
geaTrapCheck("gea_three_webgl_uniform_4i"); GEA_WEBGL_VOID("uniform4i"); gWebGL.glUniform4i(asGLint(location), asGLint(x), asGLint(y), asGLint(z), asGLint(w)); }
|
|
1968
|
+
extern "C" void gea_three_webgl_uniform_4ui(double location, double x, double y, double z, double w) {
|
|
1969
|
+
geaTrapCheck("gea_three_webgl_uniform_4ui"); GEA_WEBGL_VOID("uniform4ui"); gWebGL.glUniform4ui(asGLint(location), asGLuint(x), asGLuint(y), asGLuint(z), asGLuint(w)); }
|
|
1970
|
+
|
|
1971
|
+
extern "C" double gea_three_webgl_create_texture() {
|
|
1972
|
+
geaTrapCheck("gea_three_webgl_create_texture"); GEA_WEBGL_DOUBLE("createTexture"); return generatedTexture(); }
|
|
1973
|
+
extern "C" void gea_three_webgl_delete_texture(double texture) {
|
|
1974
|
+
geaTrapCheck("gea_three_webgl_delete_texture"); GEA_WEBGL_VOID("deleteTexture"); GLuint handle = asGLuint(texture); gWebGL.glDeleteTextures(1, &handle); }
|
|
1975
|
+
extern "C" void gea_three_webgl_bind_texture(double target, double texture) {
|
|
1976
|
+
geaTrapCheck("gea_three_webgl_bind_texture"); GEA_WEBGL_VOID("bindTexture"); gWebGL.glBindTexture(asGLenum(target), asGLuint(texture)); }
|
|
1977
|
+
extern "C" void gea_three_webgl_active_texture(double texture) {
|
|
1978
|
+
geaTrapCheck("gea_three_webgl_active_texture"); GEA_WEBGL_VOID("activeTexture"); gWebGL.glActiveTexture(asGLenum(texture)); }
|
|
1979
|
+
extern "C" void gea_three_webgl_tex_parameteri(double target, double pname, double param) {
|
|
1980
|
+
geaTrapCheck("gea_three_webgl_tex_parameteri"); GEA_WEBGL_VOID("texParameteri");
|
|
1981
|
+
gWebGL.glTexParameteri(asGLenum(target), asGLenum(pname), asGLint(param));
|
|
1982
|
+
}
|
|
1983
|
+
extern "C" void gea_three_webgl_pixel_storei(double pname, double param) {
|
|
1984
|
+
geaTrapCheck("gea_three_webgl_pixel_storei"); GEA_WEBGL_VOID("pixelStorei");
|
|
1985
|
+
const GLenum glPname = asGLenum(pname);
|
|
1986
|
+
// UNPACK_FLIP_Y_WEBGL (0x9240), UNPACK_PREMULTIPLY_ALPHA_WEBGL (0x9241) and
|
|
1987
|
+
// UNPACK_COLORSPACE_CONVERSION_WEBGL (0x9243) are WebGL-only client-side
|
|
1988
|
+
// pixel-store parameters: a browser's WebGL layer consumes them (applying any
|
|
1989
|
+
// flip/premultiply in software during texImage) and never forwards them to
|
|
1990
|
+
// GL. ANGLE ES3 has no such enums and rejects them with GL_INVALID_ENUM —
|
|
1991
|
+
// three.js sets all three on every texture upload, which is the source of the
|
|
1992
|
+
// frame-0 0x0500 seen at startup (later tex_parameteri/tex_storage_2d calls
|
|
1993
|
+
// only observe that sticky error). This host performs no GL-side flip either
|
|
1994
|
+
// way, so dropping these to match browser semantics is behaviour-preserving
|
|
1995
|
+
// and keeps the GL error state clean.
|
|
1996
|
+
if (glPname == 0x9240 || glPname == 0x9241 || glPname == 0x9243) return;
|
|
1997
|
+
gWebGL.glPixelStorei(glPname, asGLint(param)); }
|
|
1998
|
+
extern "C" void gea_three_webgl_generate_mipmap(double target) {
|
|
1999
|
+
geaTrapCheck("gea_three_webgl_generate_mipmap"); GEA_WEBGL_VOID("generateMipmap"); gWebGL.glGenerateMipmap(asGLenum(target)); }
|
|
2000
|
+
|
|
2001
|
+
extern "C" double gea_three_webgl_create_vertex_array() {
|
|
2002
|
+
geaTrapCheck("gea_three_webgl_create_vertex_array"); GEA_WEBGL_DOUBLE("createVertexArray"); return generatedVertexArray(); }
|
|
2003
|
+
extern "C" void gea_three_webgl_bind_vertex_array(double vertexArray) {
|
|
2004
|
+
geaTrapCheck("gea_three_webgl_bind_vertex_array"); GEA_WEBGL_VOID("bindVertexArray"); gWebGL.glBindVertexArray(asGLuint(vertexArray)); }
|
|
2005
|
+
extern "C" void gea_three_webgl_delete_vertex_array(double vertexArray) {
|
|
2006
|
+
geaTrapCheck("gea_three_webgl_delete_vertex_array"); GEA_WEBGL_VOID("deleteVertexArray"); GLuint handle = asGLuint(vertexArray); gWebGL.glDeleteVertexArrays(1, &handle); }
|
|
2007
|
+
|
|
2008
|
+
extern "C" double gea_three_webgl_create_framebuffer() {
|
|
2009
|
+
geaTrapCheck("gea_three_webgl_create_framebuffer"); GEA_WEBGL_DOUBLE("createFramebuffer"); return generatedFramebuffer(); }
|
|
2010
|
+
extern "C" void gea_three_webgl_bind_framebuffer(double target, double framebuffer) {
|
|
2011
|
+
geaTrapCheck("gea_three_webgl_bind_framebuffer"); GEA_WEBGL_VOID("bindFramebuffer");
|
|
2012
|
+
if (nativeShadowColorAuditEnabled()) {
|
|
2013
|
+
static int bindLogs = 0;
|
|
2014
|
+
if (bindLogs++ < 80) smokeLog("[shadow-color-state] bindFramebuffer target=0x%04x fbo=%u", asGLenum(target), asGLuint(framebuffer));
|
|
2015
|
+
}
|
|
2016
|
+
if (nativeShadowColorAuditEnabled() && gWebGL.glReadPixels && gWebGL.glGetIntegerv && gWebGL.glGetError) {
|
|
2017
|
+
static GLuint previousFramebuffer = 0;
|
|
2018
|
+
static bool auditedShadowTarget = false;
|
|
2019
|
+
const GLuint nextFramebuffer = asGLuint(framebuffer);
|
|
2020
|
+
if (previousFramebuffer != 0 && nextFramebuffer == 0 && !auditedShadowTarget) {
|
|
2021
|
+
GLint viewport[4] = {0, 0, 0, 0};
|
|
2022
|
+
gWebGL.glGetIntegerv(0x0BA2 /* GL_VIEWPORT */, viewport);
|
|
2023
|
+
if (viewport[2] == 2048 && viewport[3] == 2048) {
|
|
2024
|
+
const size_t pixelCount = static_cast<size_t>(viewport[2]) * static_cast<size_t>(viewport[3]);
|
|
2025
|
+
std::vector<uint8_t> rgba(pixelCount * 4);
|
|
2026
|
+
while (gWebGL.glGetError() != 0) {}
|
|
2027
|
+
gWebGL.glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
|
|
2028
|
+
0x1908 /* GL_RGBA */, 0x1401 /* GL_UNSIGNED_BYTE */, rgba.data());
|
|
2029
|
+
size_t nonWhite = 0;
|
|
2030
|
+
uint8_t minimum = 255;
|
|
2031
|
+
uint8_t maximum = 0;
|
|
2032
|
+
for (size_t i = 0; i < pixelCount; ++i) {
|
|
2033
|
+
const uint8_t r = rgba[i * 4];
|
|
2034
|
+
const uint8_t g = rgba[i * 4 + 1];
|
|
2035
|
+
const uint8_t b = rgba[i * 4 + 2];
|
|
2036
|
+
minimum = std::min(minimum, std::min(r, std::min(g, b)));
|
|
2037
|
+
maximum = std::max(maximum, std::max(r, std::max(g, b)));
|
|
2038
|
+
if (r != 255 || g != 255 || b != 255) ++nonWhite;
|
|
2039
|
+
}
|
|
2040
|
+
const GLenum error = gWebGL.glGetError();
|
|
2041
|
+
smokeLog("[shadow-color-audit] fbo=%u viewport=%d,%d %dx%d nonWhite=%zu/%zu min=%u max=%u error=0x%04x",
|
|
2042
|
+
previousFramebuffer, viewport[0], viewport[1], viewport[2], viewport[3], nonWhite, pixelCount,
|
|
2043
|
+
static_cast<unsigned>(minimum), static_cast<unsigned>(maximum), error);
|
|
2044
|
+
auditedShadowTarget = true;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
previousFramebuffer = nextFramebuffer;
|
|
2048
|
+
}
|
|
2049
|
+
gWebGL.glBindFramebuffer(asGLenum(target), asGLuint(framebuffer)); }
|
|
2050
|
+
extern "C" void gea_three_webgl_delete_framebuffer(double framebuffer) {
|
|
2051
|
+
geaTrapCheck("gea_three_webgl_delete_framebuffer"); GEA_WEBGL_VOID("deleteFramebuffer"); GLuint handle = asGLuint(framebuffer); gWebGL.glDeleteFramebuffers(1, &handle); }
|
|
2052
|
+
extern "C" void gea_three_webgl_framebuffer_texture_2d(double target, double attachment, double textarget, double texture, double level) {
|
|
2053
|
+
geaTrapCheck("gea_three_webgl_framebuffer_texture_2d"); GEA_WEBGL_VOID("framebufferTexture2D");
|
|
2054
|
+
gWebGL.glFramebufferTexture2D(asGLenum(target), asGLenum(attachment), asGLenum(textarget), asGLuint(texture), asGLint(level)); }
|
|
2055
|
+
extern "C" double gea_three_webgl_check_framebuffer_status(double target) {
|
|
2056
|
+
geaTrapCheck("gea_three_webgl_check_framebuffer_status"); GEA_WEBGL_DOUBLE("checkFramebufferStatus"); return gWebGL.glCheckFramebufferStatus(asGLenum(target)); }
|
|
2057
|
+
extern "C" double gea_three_webgl_create_renderbuffer() {
|
|
2058
|
+
geaTrapCheck("gea_three_webgl_create_renderbuffer"); GEA_WEBGL_DOUBLE("createRenderbuffer"); return generatedRenderbuffer(); }
|
|
2059
|
+
extern "C" void gea_three_webgl_bind_renderbuffer(double target, double renderbuffer) {
|
|
2060
|
+
geaTrapCheck("gea_three_webgl_bind_renderbuffer"); GEA_WEBGL_VOID("bindRenderbuffer"); gWebGL.glBindRenderbuffer(asGLenum(target), asGLuint(renderbuffer)); }
|
|
2061
|
+
extern "C" void gea_three_webgl_delete_renderbuffer(double renderbuffer) {
|
|
2062
|
+
geaTrapCheck("gea_three_webgl_delete_renderbuffer"); GEA_WEBGL_VOID("deleteRenderbuffer"); GLuint handle = asGLuint(renderbuffer); gWebGL.glDeleteRenderbuffers(1, &handle); }
|
|
2063
|
+
extern "C" void gea_three_webgl_renderbuffer_storage(double target, double internalFormat, double width, double height) {
|
|
2064
|
+
geaTrapCheck("gea_three_webgl_renderbuffer_storage"); GEA_WEBGL_VOID("renderbufferStorage"); gWebGL.glRenderbufferStorage(asGLenum(target), asGLenum(internalFormat), asGLsizei(width), asGLsizei(height)); }
|
|
2065
|
+
extern "C" void gea_three_webgl_framebuffer_renderbuffer(double target, double attachment, double renderbuffertarget, double renderbuffer) {
|
|
2066
|
+
geaTrapCheck("gea_three_webgl_framebuffer_renderbuffer"); GEA_WEBGL_VOID("framebufferRenderbuffer"); gWebGL.glFramebufferRenderbuffer(asGLenum(target), asGLenum(attachment), asGLenum(renderbuffertarget), asGLuint(renderbuffer)); }
|
|
2067
|
+
extern "C" void gea_three_webgl_draw_buffers(double attachment) {
|
|
2068
|
+
geaTrapCheck("gea_three_webgl_draw_buffers"); GEA_WEBGL_VOID("drawBuffers"); const GLenum glAttachment = asGLenum(attachment); gWebGL.glDrawBuffers(1, &glAttachment); }
|
|
2069
|
+
|
|
2070
|
+
extern "C" void gea_three_webgl_clear_color(double r, double g, double b, double a) {
|
|
2071
|
+
geaTrapCheck("gea_three_webgl_clear_color"); GEA_WEBGL_VOID("clearColor");
|
|
2072
|
+
if (nativeShadowColorAuditEnabled()) {
|
|
2073
|
+
static int clearColorLogs = 0;
|
|
2074
|
+
if (clearColorLogs++ < 20) smokeLog("[shadow-color-state] clearColor %.3f %.3f %.3f %.3f", r, g, b, a);
|
|
2075
|
+
}
|
|
2076
|
+
gWebGL.glClearColor(static_cast<GLfloat>(r), static_cast<GLfloat>(g), static_cast<GLfloat>(b), static_cast<GLfloat>(a)); }
|
|
2077
|
+
extern "C" void gea_three_webgl_clear(double mask) {
|
|
2078
|
+
geaTrapCheck("gea_three_webgl_clear"); GEA_WEBGL_VOID("clear");
|
|
2079
|
+
if (nativeShadowColorAuditEnabled() && gWebGL.glGetIntegerv) {
|
|
2080
|
+
static int clearLogs = 0;
|
|
2081
|
+
if (clearLogs++ < 20) {
|
|
2082
|
+
GLint framebuffer = 0;
|
|
2083
|
+
gWebGL.glGetIntegerv(0x8CA6 /* GL_FRAMEBUFFER_BINDING */, &framebuffer);
|
|
2084
|
+
smokeLog("[shadow-color-state] clear fbo=%d mask=0x%04x", framebuffer, asGLbitfield(mask));
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
gWebGL.glClear(asGLbitfield(mask)); }
|
|
2088
|
+
extern "C" void gea_three_webgl_clear_depth(double depth) {
|
|
2089
|
+
geaTrapCheck("gea_three_webgl_clear_depth"); GEA_WEBGL_VOID("clearDepth"); gWebGL.glClearDepthf(static_cast<GLfloat>(depth)); }
|
|
2090
|
+
extern "C" void gea_three_webgl_clear_stencil(double stencil) {
|
|
2091
|
+
geaTrapCheck("gea_three_webgl_clear_stencil"); GEA_WEBGL_VOID("clearStencil"); gWebGL.glClearStencil(asGLint(stencil)); }
|
|
2092
|
+
extern "C" void gea_three_webgl_color_mask(double r, double g, double b, double a) {
|
|
2093
|
+
geaTrapCheck("gea_three_webgl_color_mask"); GEA_WEBGL_VOID("colorMask"); gWebGL.glColorMask(r != 0.0, g != 0.0, b != 0.0, a != 0.0); }
|
|
2094
|
+
extern "C" void gea_three_webgl_depth_mask(double flag) {
|
|
2095
|
+
geaTrapCheck("gea_three_webgl_depth_mask"); GEA_WEBGL_VOID("depthMask"); gWebGL.glDepthMask(flag != 0.0 ? GL_TRUE_VALUE : GL_FALSE_VALUE); }
|
|
2096
|
+
extern "C" void gea_three_webgl_depth_func(double func) {
|
|
2097
|
+
geaTrapCheck("gea_three_webgl_depth_func"); GEA_WEBGL_VOID("depthFunc"); gWebGL.glDepthFunc(asGLenum(func)); }
|
|
2098
|
+
extern "C" void gea_three_webgl_enable(double cap) {
|
|
2099
|
+
geaTrapCheck("gea_three_webgl_enable"); GEA_WEBGL_VOID("enable");
|
|
2100
|
+
if (nativeBlendDebugEnabled() && asGLenum(cap) == 0x0BE2) {
|
|
2101
|
+
smokeLog("[blend-dbg] enable BLEND");
|
|
2102
|
+
}
|
|
2103
|
+
gWebGL.glEnable(asGLenum(cap)); }
|
|
2104
|
+
extern "C" void gea_three_webgl_disable(double cap) {
|
|
2105
|
+
geaTrapCheck("gea_three_webgl_disable"); GEA_WEBGL_VOID("disable");
|
|
2106
|
+
if (nativeBlendDebugEnabled() && asGLenum(cap) == 0x0BE2) {
|
|
2107
|
+
smokeLog("[blend-dbg] disable BLEND");
|
|
2108
|
+
}
|
|
2109
|
+
gWebGL.glDisable(asGLenum(cap)); }
|
|
2110
|
+
extern "C" void gea_three_webgl_blend_func(double sfactor, double dfactor) {
|
|
2111
|
+
geaTrapCheck("gea_three_webgl_blend_func"); GEA_WEBGL_VOID("blendFunc");
|
|
2112
|
+
if (nativeBlendDebugEnabled()) {
|
|
2113
|
+
smokeLog("[blend-dbg] blendFunc src=0x%04x dst=0x%04x", asGLenum(sfactor), asGLenum(dfactor));
|
|
2114
|
+
}
|
|
2115
|
+
gWebGL.glBlendFunc(asGLenum(sfactor), asGLenum(dfactor)); }
|
|
2116
|
+
extern "C" void gea_three_webgl_blend_func_separate(double srcRGB, double dstRGB, double srcAlpha, double dstAlpha) {
|
|
2117
|
+
geaTrapCheck("gea_three_webgl_blend_func_separate"); GEA_WEBGL_VOID("blendFuncSeparate");
|
|
2118
|
+
if (nativeBlendDebugEnabled()) {
|
|
2119
|
+
smokeLog("[blend-dbg] blendFuncSeparate rgb=(0x%04x,0x%04x) alpha=(0x%04x,0x%04x)",
|
|
2120
|
+
asGLenum(srcRGB), asGLenum(dstRGB), asGLenum(srcAlpha), asGLenum(dstAlpha));
|
|
2121
|
+
}
|
|
2122
|
+
gWebGL.glBlendFuncSeparate(asGLenum(srcRGB), asGLenum(dstRGB), asGLenum(srcAlpha), asGLenum(dstAlpha)); }
|
|
2123
|
+
extern "C" void gea_three_webgl_blend_equation(double mode) {
|
|
2124
|
+
geaTrapCheck("gea_three_webgl_blend_equation"); GEA_WEBGL_VOID("blendEquation");
|
|
2125
|
+
if (nativeBlendDebugEnabled()) {
|
|
2126
|
+
smokeLog("[blend-dbg] blendEquation mode=0x%04x", asGLenum(mode));
|
|
2127
|
+
}
|
|
2128
|
+
gWebGL.glBlendEquation(asGLenum(mode)); }
|
|
2129
|
+
extern "C" void gea_three_webgl_blend_equation_separate(double modeRGB, double modeAlpha) {
|
|
2130
|
+
geaTrapCheck("gea_three_webgl_blend_equation_separate"); GEA_WEBGL_VOID("blendEquationSeparate");
|
|
2131
|
+
if (nativeBlendDebugEnabled()) {
|
|
2132
|
+
smokeLog("[blend-dbg] blendEquationSeparate rgb=0x%04x alpha=0x%04x", asGLenum(modeRGB), asGLenum(modeAlpha));
|
|
2133
|
+
}
|
|
2134
|
+
gWebGL.glBlendEquationSeparate(asGLenum(modeRGB), asGLenum(modeAlpha)); }
|
|
2135
|
+
extern "C" void gea_three_webgl_cull_face(double mode) {
|
|
2136
|
+
geaTrapCheck("gea_three_webgl_cull_face"); GEA_WEBGL_VOID("cullFace"); gWebGL.glCullFace(asGLenum(mode)); }
|
|
2137
|
+
extern "C" void gea_three_webgl_front_face(double mode) {
|
|
2138
|
+
geaTrapCheck("gea_three_webgl_front_face"); GEA_WEBGL_VOID("frontFace"); gWebGL.glFrontFace(asGLenum(mode)); }
|
|
2139
|
+
extern "C" void gea_three_webgl_viewport(double x, double y, double width, double height) {
|
|
2140
|
+
geaTrapCheck("gea_three_webgl_viewport"); GEA_WEBGL_VOID("viewport"); gWebGL.glViewport(asGLint(x), asGLint(y), asGLsizei(width), asGLsizei(height)); }
|
|
2141
|
+
extern "C" void gea_three_webgl_scissor(double x, double y, double width, double height) {
|
|
2142
|
+
geaTrapCheck("gea_three_webgl_scissor"); GEA_WEBGL_VOID("scissor"); gWebGL.glScissor(asGLint(x), asGLint(y), asGLsizei(width), asGLsizei(height)); }
|
|
2143
|
+
extern "C" void gea_three_webgl_line_width(double width) {
|
|
2144
|
+
geaTrapCheck("gea_three_webgl_line_width"); GEA_WEBGL_VOID("lineWidth"); gWebGL.glLineWidth(static_cast<GLfloat>(width)); }
|
|
2145
|
+
extern "C" void gea_three_webgl_polygon_offset(double factor, double units) {
|
|
2146
|
+
geaTrapCheck("gea_three_webgl_polygon_offset"); GEA_WEBGL_VOID("polygonOffset"); gWebGL.glPolygonOffset(static_cast<GLfloat>(factor), static_cast<GLfloat>(units)); }
|
|
2147
|
+
extern "C" void gea_three_webgl_stencil_mask(double mask) {
|
|
2148
|
+
geaTrapCheck("gea_three_webgl_stencil_mask"); GEA_WEBGL_VOID("stencilMask"); gWebGL.glStencilMask(asGLuint(mask)); }
|
|
2149
|
+
extern "C" void gea_three_webgl_stencil_mask_separate(double face, double mask) {
|
|
2150
|
+
geaTrapCheck("gea_three_webgl_stencil_mask_separate"); GEA_WEBGL_VOID("stencilMaskSeparate"); gWebGL.glStencilMaskSeparate(asGLenum(face), asGLuint(mask)); }
|
|
2151
|
+
extern "C" void gea_three_webgl_stencil_func(double func, double ref, double mask) {
|
|
2152
|
+
geaTrapCheck("gea_three_webgl_stencil_func"); GEA_WEBGL_VOID("stencilFunc"); gWebGL.glStencilFunc(asGLenum(func), asGLint(ref), asGLuint(mask)); }
|
|
2153
|
+
extern "C" void gea_three_webgl_stencil_func_separate(double face, double func, double ref, double mask) {
|
|
2154
|
+
geaTrapCheck("gea_three_webgl_stencil_func_separate"); GEA_WEBGL_VOID("stencilFuncSeparate"); gWebGL.glStencilFuncSeparate(asGLenum(face), asGLenum(func), asGLint(ref), asGLuint(mask)); }
|
|
2155
|
+
extern "C" void gea_three_webgl_stencil_op(double fail, double zfail, double zpass) {
|
|
2156
|
+
geaTrapCheck("gea_three_webgl_stencil_op"); GEA_WEBGL_VOID("stencilOp"); gWebGL.glStencilOp(asGLenum(fail), asGLenum(zfail), asGLenum(zpass)); }
|
|
2157
|
+
extern "C" void gea_three_webgl_stencil_op_separate(double face, double fail, double zfail, double zpass) {
|
|
2158
|
+
geaTrapCheck("gea_three_webgl_stencil_op_separate"); GEA_WEBGL_VOID("stencilOpSeparate"); gWebGL.glStencilOpSeparate(asGLenum(face), asGLenum(fail), asGLenum(zfail), asGLenum(zpass)); }
|
|
2159
|
+
extern "C" void gea_three_webgl_draw_elements(double mode, double count, double type, double offset) {
|
|
2160
|
+
geaTrapCheck("gea_three_webgl_draw_elements"); GEA_WEBGL_VOID("drawElements");
|
|
2161
|
+
gWebGL.glDrawElements(asGLenum(mode), asGLsizei(count), asGLenum(type), reinterpret_cast<const void *>(static_cast<std::uintptr_t>(offset))); }
|
|
2162
|
+
extern "C" void gea_three_webgl_draw_arrays(double mode, double first, double count) {
|
|
2163
|
+
geaTrapCheck("gea_three_webgl_draw_arrays"); GEA_WEBGL_VOID("drawArrays");
|
|
2164
|
+
gWebGL.glDrawArrays(asGLenum(mode), asGLint(first), asGLsizei(count)); }
|
|
2165
|
+
extern "C" void gea_three_webgl_vertex_attrib_divisor(double index, double divisor) {
|
|
2166
|
+
geaTrapCheck("gea_three_webgl_vertex_attrib_divisor"); GEA_WEBGL_VOID("vertexAttribDivisor");
|
|
2167
|
+
gWebGL.glVertexAttribDivisor(asGLuint(index), asGLuint(divisor)); }
|
|
2168
|
+
extern "C" void gea_three_webgl_draw_elements_instanced(double mode, double count, double type, double offset, double instanceCount) {
|
|
2169
|
+
geaTrapCheck("gea_three_webgl_draw_elements_instanced"); GEA_WEBGL_VOID("drawElementsInstanced");
|
|
2170
|
+
gWebGL.glDrawElementsInstanced(asGLenum(mode), asGLsizei(count), asGLenum(type),
|
|
2171
|
+
reinterpret_cast<const void *>(static_cast<std::uintptr_t>(offset)), asGLsizei(instanceCount)); }
|
|
2172
|
+
extern "C" void gea_three_webgl_draw_arrays_instanced(double mode, double first, double count, double instanceCount) {
|
|
2173
|
+
geaTrapCheck("gea_three_webgl_draw_arrays_instanced"); GEA_WEBGL_VOID("drawArraysInstanced");
|
|
2174
|
+
gWebGL.glDrawArraysInstanced(asGLenum(mode), asGLint(first), asGLsizei(count), asGLsizei(instanceCount)); }
|
|
2175
|
+
extern "C" double gea_three_webgl_get_error() {
|
|
2176
|
+
geaTrapCheck("gea_three_webgl_get_error"); GEA_WEBGL_DOUBLE("getError"); return gWebGL.glGetError(); }
|
|
2177
|
+
|
|
2178
|
+
extern "C" double gea_three_webgl_call(
|
|
2179
|
+
double opValue,
|
|
2180
|
+
double a,
|
|
2181
|
+
double b,
|
|
2182
|
+
double c,
|
|
2183
|
+
double d,
|
|
2184
|
+
double e,
|
|
2185
|
+
double f,
|
|
2186
|
+
double h,
|
|
2187
|
+
double i
|
|
2188
|
+
) {
|
|
2189
|
+
geaTrapCheck("gea_three_webgl_call");
|
|
2190
|
+
if (!gWebGL.ready || !makeCurrent("eglMakeCurrent(call)")) return 0.0;
|
|
2191
|
+
const int op = static_cast<int>(opValue);
|
|
2192
|
+
switch (op) {
|
|
2193
|
+
case OP_CLEAR_COLOR: gWebGL.glClearColor(a, b, c, d); return 0.0;
|
|
2194
|
+
case OP_CLEAR: gWebGL.glClear(static_cast<GLbitfield>(a)); return 0.0;
|
|
2195
|
+
case OP_VIEWPORT: gWebGL.glViewport(a, b, c, d); return 0.0;
|
|
2196
|
+
case OP_ENABLE: gWebGL.glEnable(static_cast<GLenum>(a)); return 0.0;
|
|
2197
|
+
case OP_DISABLE: gWebGL.glDisable(static_cast<GLenum>(a)); return 0.0;
|
|
2198
|
+
case OP_DEPTH_FUNC: gWebGL.glDepthFunc(static_cast<GLenum>(a)); return 0.0;
|
|
2199
|
+
case OP_DEPTH_MASK: gWebGL.glDepthMask(a != 0.0 ? GL_TRUE_VALUE : GL_FALSE_VALUE); return 0.0;
|
|
2200
|
+
case OP_COLOR_MASK: gWebGL.glColorMask(a != 0.0, b != 0.0, c != 0.0, d != 0.0); return 0.0;
|
|
2201
|
+
case OP_BLEND_FUNC: gWebGL.glBlendFunc(static_cast<GLenum>(a), static_cast<GLenum>(b)); return 0.0;
|
|
2202
|
+
case OP_BLEND_FUNC_SEPARATE: gWebGL.glBlendFuncSeparate(a, b, c, d); return 0.0;
|
|
2203
|
+
case OP_BLEND_EQUATION: gWebGL.glBlendEquation(static_cast<GLenum>(a)); return 0.0;
|
|
2204
|
+
case OP_BLEND_EQUATION_SEPARATE: gWebGL.glBlendEquationSeparate(a, b); return 0.0;
|
|
2205
|
+
case OP_CULL_FACE: gWebGL.glCullFace(static_cast<GLenum>(a)); return 0.0;
|
|
2206
|
+
case OP_FRONT_FACE: gWebGL.glFrontFace(static_cast<GLenum>(a)); return 0.0;
|
|
2207
|
+
case OP_CREATE_BUFFER: return generatedBuffer();
|
|
2208
|
+
case OP_DELETE_BUFFER: deleteNativeBuffer(a); return 0.0;
|
|
2209
|
+
case OP_BIND_BUFFER: bindNativeBuffer(a, b); return 0.0;
|
|
2210
|
+
case OP_CREATE_SHADER: return gWebGL.glCreateShader(static_cast<GLenum>(a));
|
|
2211
|
+
case OP_COMPILE_SHADER: gWebGL.glCompileShader(a); return 0.0;
|
|
2212
|
+
case OP_GET_SHADER_PARAMETER: { GLint value = 0; gWebGL.glGetShaderiv(a, b, &value); return value; }
|
|
2213
|
+
case OP_DELETE_SHADER: gWebGL.glDeleteShader(static_cast<GLuint>(a)); return 0.0;
|
|
2214
|
+
case OP_CREATE_PROGRAM: return gWebGL.glCreateProgram();
|
|
2215
|
+
case OP_ATTACH_SHADER: gWebGL.glAttachShader(a, b); return 0.0;
|
|
2216
|
+
case OP_LINK_PROGRAM: gWebGL.glLinkProgram(a); return 0.0;
|
|
2217
|
+
case OP_GET_PROGRAM_PARAMETER: { GLint value = 0; gWebGL.glGetProgramiv(a, b, &value); return value; }
|
|
2218
|
+
case OP_DELETE_PROGRAM: gWebGL.glDeleteProgram(static_cast<GLuint>(a)); return 0.0;
|
|
2219
|
+
case OP_USE_PROGRAM: gWebGL.glUseProgram(a); return 0.0;
|
|
2220
|
+
case OP_ENABLE_VERTEX_ATTRIB_ARRAY: gWebGL.glEnableVertexAttribArray(a); return 0.0;
|
|
2221
|
+
case OP_DISABLE_VERTEX_ATTRIB_ARRAY: gWebGL.glDisableVertexAttribArray(a); return 0.0;
|
|
2222
|
+
case OP_VERTEX_ATTRIB_POINTER: gWebGL.glVertexAttribPointer(a, b, c, d != 0.0, e, reinterpret_cast<const void *>(static_cast<std::uintptr_t>(f))); return 0.0;
|
|
2223
|
+
case OP_UNIFORM_1F: gWebGL.glUniform1f(a, b); return 0.0;
|
|
2224
|
+
case OP_UNIFORM_1I: gWebGL.glUniform1i(a, b); return 0.0;
|
|
2225
|
+
case OP_UNIFORM_2F: gWebGL.glUniform2f(a, b, c); return 0.0;
|
|
2226
|
+
case OP_UNIFORM_3F: gWebGL.glUniform3f(a, b, c, d); return 0.0;
|
|
2227
|
+
case OP_UNIFORM_4F: gWebGL.glUniform4f(a, b, c, d, e); return 0.0;
|
|
2228
|
+
case OP_DRAW_ELEMENTS: gWebGL.glDrawElements(a, b, c, reinterpret_cast<const void *>(static_cast<std::uintptr_t>(d))); return 0.0;
|
|
2229
|
+
case OP_DRAW_ARRAYS: gWebGL.glDrawArrays(a, b, c); return 0.0;
|
|
2230
|
+
case OP_ACTIVE_TEXTURE: gWebGL.glActiveTexture(a); return 0.0;
|
|
2231
|
+
case OP_CREATE_TEXTURE: return generatedTexture();
|
|
2232
|
+
case OP_DELETE_TEXTURE: { GLuint handle = a; gWebGL.glDeleteTextures(1, &handle); return 0.0; }
|
|
2233
|
+
case OP_BIND_TEXTURE: gWebGL.glBindTexture(a, b); return 0.0;
|
|
2234
|
+
case OP_TEX_PARAMETERI: gWebGL.glTexParameteri(a, b, c); return 0.0;
|
|
2235
|
+
case OP_PIXEL_STOREI: gWebGL.glPixelStorei(a, b); return 0.0;
|
|
2236
|
+
case OP_GENERATE_MIPMAP: gWebGL.glGenerateMipmap(a); return 0.0;
|
|
2237
|
+
case OP_CREATE_VERTEX_ARRAY: return generatedVertexArray();
|
|
2238
|
+
case OP_BIND_VERTEX_ARRAY: gWebGL.glBindVertexArray(a); return 0.0;
|
|
2239
|
+
case OP_DELETE_VERTEX_ARRAY: { GLuint handle = a; gWebGL.glDeleteVertexArrays(1, &handle); return 0.0; }
|
|
2240
|
+
case OP_GET_ERROR: return gWebGL.glGetError();
|
|
2241
|
+
case OP_SCISSOR: gWebGL.glScissor(a, b, c, d); return 0.0;
|
|
2242
|
+
case OP_LINE_WIDTH: gWebGL.glLineWidth(a); return 0.0;
|
|
2243
|
+
case OP_POLYGON_OFFSET: gWebGL.glPolygonOffset(a, b); return 0.0;
|
|
2244
|
+
case OP_CLEAR_DEPTH: gWebGL.glClearDepthf(a); return 0.0;
|
|
2245
|
+
case OP_CLEAR_STENCIL: gWebGL.glClearStencil(a); return 0.0;
|
|
2246
|
+
case OP_STENCIL_MASK: gWebGL.glStencilMask(static_cast<GLuint>(a)); return 0.0;
|
|
2247
|
+
case OP_STENCIL_FUNC: gWebGL.glStencilFunc(a, b, c); return 0.0;
|
|
2248
|
+
case OP_STENCIL_OP: gWebGL.glStencilOp(a, b, c); return 0.0;
|
|
2249
|
+
case OP_STENCIL_FUNC_SEPARATE: gWebGL.glStencilFuncSeparate(a, b, c, d); return 0.0;
|
|
2250
|
+
case OP_STENCIL_OP_SEPARATE: gWebGL.glStencilOpSeparate(a, b, c, d); return 0.0;
|
|
2251
|
+
case OP_STENCIL_MASK_SEPARATE: gWebGL.glStencilMaskSeparate(a, static_cast<GLuint>(b)); return 0.0;
|
|
2252
|
+
case OP_CREATE_FRAMEBUFFER: return generatedFramebuffer();
|
|
2253
|
+
case OP_BIND_FRAMEBUFFER: gWebGL.glBindFramebuffer(a, b); return 0.0;
|
|
2254
|
+
case OP_DELETE_FRAMEBUFFER: { GLuint handle = a; gWebGL.glDeleteFramebuffers(1, &handle); return 0.0; }
|
|
2255
|
+
case OP_FRAMEBUFFER_TEXTURE_2D: gWebGL.glFramebufferTexture2D(a, b, c, d, e); return 0.0;
|
|
2256
|
+
case OP_CHECK_FRAMEBUFFER_STATUS: return gWebGL.glCheckFramebufferStatus(a);
|
|
2257
|
+
case OP_CREATE_RENDERBUFFER: return generatedRenderbuffer();
|
|
2258
|
+
case OP_BIND_RENDERBUFFER: gWebGL.glBindRenderbuffer(a, b); return 0.0;
|
|
2259
|
+
case OP_RENDERBUFFER_STORAGE: gWebGL.glRenderbufferStorage(a, b, c, d); return 0.0;
|
|
2260
|
+
case OP_FRAMEBUFFER_RENDERBUFFER: gWebGL.glFramebufferRenderbuffer(a, b, c, d); return 0.0;
|
|
2261
|
+
case OP_DELETE_RENDERBUFFER: { GLuint handle = a; gWebGL.glDeleteRenderbuffers(1, &handle); return 0.0; }
|
|
2262
|
+
case OP_DRAW_BUFFERS: {
|
|
2263
|
+
const GLenum attachment = static_cast<GLenum>(a);
|
|
2264
|
+
gWebGL.glDrawBuffers(1, &attachment);
|
|
2265
|
+
return 0.0;
|
|
2266
|
+
}
|
|
2267
|
+
default: return 0.0;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
extern "C" double gea_three_webgl_call_string(
|
|
2272
|
+
double opValue,
|
|
2273
|
+
double a,
|
|
2274
|
+
double b,
|
|
2275
|
+
std::string value
|
|
2276
|
+
) {
|
|
2277
|
+
geaTrapCheck("gea_three_webgl_call_string");
|
|
2278
|
+
if (!gWebGL.ready || !makeCurrent("eglMakeCurrent(call-string)")) return 0.0;
|
|
2279
|
+
const int op = static_cast<int>(opValue);
|
|
2280
|
+
if (op == OP_SHADER_SOURCE) {
|
|
2281
|
+
const char *source = value.c_str();
|
|
2282
|
+
const GLint length = static_cast<GLint>(value.size());
|
|
2283
|
+
gWebGL.glShaderSource(static_cast<GLuint>(a), 1, &source, &length);
|
|
2284
|
+
return 0.0;
|
|
2285
|
+
}
|
|
2286
|
+
if (op == OP_GET_ATTRIB_LOCATION) {
|
|
2287
|
+
return gWebGL.glGetAttribLocation(static_cast<GLuint>(a), value.c_str());
|
|
2288
|
+
}
|
|
2289
|
+
if (op == OP_GET_UNIFORM_LOCATION) {
|
|
2290
|
+
return gWebGL.glGetUniformLocation(static_cast<GLuint>(a), value.c_str());
|
|
2291
|
+
}
|
|
2292
|
+
return 0.0;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
// Most uploads here are one vec2/vec3/vec4 or one 3x3/4x4 matrix. Convert
|
|
2296
|
+
// those without an allocator round trip; larger arrays retain owned heap
|
|
2297
|
+
// storage until the synchronous GL upload returns. No shared mutable scratch.
|
|
2298
|
+
class FloatUploadData {
|
|
2299
|
+
float inlineValues_[16];
|
|
2300
|
+
std::vector<float> heapValues_;
|
|
2301
|
+
std::size_t size_;
|
|
2302
|
+
public:
|
|
2303
|
+
explicit FloatUploadData(std::span<const double> values) : size_(values.size()) {
|
|
2304
|
+
if (size_ <= 16) {
|
|
2305
|
+
for (std::size_t i = 0; i < size_; ++i) inlineValues_[i] = static_cast<float>(values[i]);
|
|
2306
|
+
} else {
|
|
2307
|
+
heapValues_.reserve(size_);
|
|
2308
|
+
for (double value : values) heapValues_.push_back(static_cast<float>(value));
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
const float* data() const { return size_ <= 16 ? inlineValues_ : heapValues_.data(); }
|
|
2312
|
+
std::size_t size() const { return size_; }
|
|
2313
|
+
bool empty() const { return size_ == 0; }
|
|
2314
|
+
};
|
|
2315
|
+
|
|
2316
|
+
static std::vector<std::uint16_t> asU16Data(std::span<const double> values) {
|
|
2317
|
+
std::vector<std::uint16_t> out;
|
|
2318
|
+
out.reserve(values.size());
|
|
2319
|
+
for (double value : values) out.push_back(static_cast<std::uint16_t>(std::max(0.0f, static_cast<float>(value))));
|
|
2320
|
+
return out;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
static std::vector<std::uint32_t> asU32Data(std::span<const double> values) {
|
|
2324
|
+
std::vector<std::uint32_t> out;
|
|
2325
|
+
out.reserve(values.size());
|
|
2326
|
+
for (double value : values) out.push_back(static_cast<std::uint32_t>(std::max(0.0f, static_cast<float>(value))));
|
|
2327
|
+
return out;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
static std::vector<std::uint8_t> asU8Data(std::span<const double> values) {
|
|
2331
|
+
std::vector<std::uint8_t> out;
|
|
2332
|
+
out.reserve(values.size());
|
|
2333
|
+
for (double value : values) out.push_back(static_cast<std::uint8_t>(std::max(0.0f, std::min(255.0f, static_cast<float>(value)))));
|
|
2334
|
+
return out;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
template <typename Fn>
|
|
2338
|
+
static void withVectorBytes(std::span<const double> values, int typeCode, Fn fn) {
|
|
2339
|
+
if (values.empty()) {
|
|
2340
|
+
fn(nullptr, 0);
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
if (typeCode == 2) {
|
|
2344
|
+
const std::vector<std::uint16_t> data = asU16Data(values);
|
|
2345
|
+
fn(data.data(), static_cast<GLsizeiptr>(data.size() * sizeof(std::uint16_t)));
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
if (typeCode == 3) {
|
|
2349
|
+
const std::vector<std::uint32_t> data = asU32Data(values);
|
|
2350
|
+
fn(data.data(), static_cast<GLsizeiptr>(data.size() * sizeof(std::uint32_t)));
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
if (typeCode == 4) {
|
|
2354
|
+
const std::vector<std::uint8_t> data = asU8Data(values);
|
|
2355
|
+
fn(data.data(), static_cast<GLsizeiptr>(data.size() * sizeof(std::uint8_t)));
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
const FloatUploadData data(values);
|
|
2359
|
+
fn(data.data(), static_cast<GLsizeiptr>(data.size() * sizeof(float)));
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
extern "C" void gea_three_webgl_buffer_data(
|
|
2363
|
+
double target,
|
|
2364
|
+
double usage,
|
|
2365
|
+
double typeCode,
|
|
2366
|
+
std::span<const double> values
|
|
2367
|
+
) {
|
|
2368
|
+
geaTrapCheck("gea_three_webgl_buffer_data");
|
|
2369
|
+
GEA_WEBGL_VOID("bufferData");
|
|
2370
|
+
withVectorBytes(values, static_cast<int>(typeCode), [&](const void *bytes, GLsizeiptr size) {
|
|
2371
|
+
nativeBufferData(asGLenum(target), size, bytes, asGLenum(usage));
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
extern "C" void gea_three_webgl_buffer_sub_data(
|
|
2376
|
+
double target,
|
|
2377
|
+
double offset,
|
|
2378
|
+
double typeCode,
|
|
2379
|
+
std::span<const double> values
|
|
2380
|
+
) {
|
|
2381
|
+
geaTrapCheck("gea_three_webgl_buffer_sub_data");
|
|
2382
|
+
GEA_WEBGL_VOID("bufferSubData");
|
|
2383
|
+
withVectorBytes(values, static_cast<int>(typeCode), [&](const void *bytes, GLsizeiptr size) {
|
|
2384
|
+
nativeBufferSubData(asGLenum(target), static_cast<GLintptr>(offset), size, bytes);
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
extern "C" void gea_three_webgl_buffer_data_bytes(double target, double usage, const void* bytes, double byteLength) {
|
|
2389
|
+
GEA_WEBGL_VOID("bufferDataBytes");
|
|
2390
|
+
nativeBufferData(asGLenum(target), static_cast<GLsizeiptr>(byteLength), bytes, asGLenum(usage));
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
extern "C" void gea_three_webgl_tex_image_2d_bytes(double target, double level, double internalFormat, double width, double height, double format, double type, const void* bytes, double) {
|
|
2394
|
+
GEA_WEBGL_VOID("texImage2DBytes");
|
|
2395
|
+
gWebGL.glTexImage2D(asGLenum(target), asGLint(level), asGLint(internalFormat), asGLsizei(width), asGLsizei(height), 0, asGLenum(format), asGLenum(type), bytes);
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
extern "C" void gea_three_webgl_tex_sub_image_2d_bytes(double target, double level, double xoffset, double yoffset, double width, double height, double format, double type, const void* bytes, double) {
|
|
2399
|
+
GEA_WEBGL_VOID("texSubImage2DBytes");
|
|
2400
|
+
gWebGL.glTexSubImage2D(asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset), asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type), bytes);
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
// Native typed arrays already own byte-exact, alias-preserving storage. This
|
|
2404
|
+
// entry point lets generated code upload that storage directly instead of
|
|
2405
|
+
// promoting every element through a temporary std::vector<double> first.
|
|
2406
|
+
extern "C" void gea_three_webgl_buffer_sub_data_bytes(
|
|
2407
|
+
double target,
|
|
2408
|
+
double offset,
|
|
2409
|
+
const void *bytes,
|
|
2410
|
+
double byteLength
|
|
2411
|
+
) {
|
|
2412
|
+
geaTrapCheck("gea_three_webgl_buffer_sub_data_bytes");
|
|
2413
|
+
GEA_WEBGL_VOID("bufferSubDataBytes");
|
|
2414
|
+
const GLsizeiptr size = static_cast<GLsizeiptr>(std::max(0.0, byteLength));
|
|
2415
|
+
nativeBufferSubData(asGLenum(target), static_cast<GLintptr>(offset), size, bytes);
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
#define GEA_DEFINE_UNIFORM_FV(WIDTH) \
|
|
2419
|
+
extern "C" void gea_three_webgl_uniform_##WIDTH##fv( \
|
|
2420
|
+
double location, const gea::detail::HostNumericArgument<float>& values \
|
|
2421
|
+
) { \
|
|
2422
|
+
geaTrapCheck("gea_three_webgl_uniform_" #WIDTH "fv"); \
|
|
2423
|
+
GEA_WEBGL_VOID("uniform" #WIDTH "fv"); \
|
|
2424
|
+
const auto& data = values; \
|
|
2425
|
+
const GLsizei count = static_cast<GLsizei>(data.size() / WIDTH); \
|
|
2426
|
+
if (count > 0) gWebGL.glUniform##WIDTH##fv(asGLint(location), count, data.data()); \
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
#define GEA_DEFINE_UNIFORM_IV(WIDTH) \
|
|
2430
|
+
extern "C" void gea_three_webgl_uniform_##WIDTH##iv( \
|
|
2431
|
+
double location, const gea::detail::HostNumericArgument<std::int32_t>& values \
|
|
2432
|
+
) { \
|
|
2433
|
+
geaTrapCheck("gea_three_webgl_uniform_" #WIDTH "iv"); \
|
|
2434
|
+
GEA_WEBGL_VOID("uniform" #WIDTH "iv"); \
|
|
2435
|
+
const auto& data = values; \
|
|
2436
|
+
const GLsizei count = static_cast<GLsizei>(data.size() / WIDTH); \
|
|
2437
|
+
if (count > 0) gWebGL.glUniform##WIDTH##iv(asGLint(location), count, data.data()); \
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
#define GEA_DEFINE_UNIFORM_UIV(WIDTH) \
|
|
2441
|
+
extern "C" void gea_three_webgl_uniform_##WIDTH##uiv( \
|
|
2442
|
+
double location, const gea::detail::HostNumericArgument<std::uint32_t>& values \
|
|
2443
|
+
) { \
|
|
2444
|
+
geaTrapCheck("gea_three_webgl_uniform_" #WIDTH "uiv"); \
|
|
2445
|
+
GEA_WEBGL_VOID("uniform" #WIDTH "uiv"); \
|
|
2446
|
+
const auto& data = values; \
|
|
2447
|
+
const GLsizei count = static_cast<GLsizei>(data.size() / WIDTH); \
|
|
2448
|
+
if (count > 0) gWebGL.glUniform##WIDTH##uiv(asGLint(location), count, data.data()); \
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
GEA_DEFINE_UNIFORM_FV(1)
|
|
2452
|
+
GEA_DEFINE_UNIFORM_FV(2)
|
|
2453
|
+
GEA_DEFINE_UNIFORM_FV(3)
|
|
2454
|
+
GEA_DEFINE_UNIFORM_FV(4)
|
|
2455
|
+
GEA_DEFINE_UNIFORM_IV(1)
|
|
2456
|
+
GEA_DEFINE_UNIFORM_IV(2)
|
|
2457
|
+
GEA_DEFINE_UNIFORM_IV(3)
|
|
2458
|
+
GEA_DEFINE_UNIFORM_IV(4)
|
|
2459
|
+
GEA_DEFINE_UNIFORM_UIV(1)
|
|
2460
|
+
GEA_DEFINE_UNIFORM_UIV(2)
|
|
2461
|
+
GEA_DEFINE_UNIFORM_UIV(3)
|
|
2462
|
+
GEA_DEFINE_UNIFORM_UIV(4)
|
|
2463
|
+
|
|
2464
|
+
#undef GEA_DEFINE_UNIFORM_FV
|
|
2465
|
+
#undef GEA_DEFINE_UNIFORM_IV
|
|
2466
|
+
#undef GEA_DEFINE_UNIFORM_UIV
|
|
2467
|
+
|
|
2468
|
+
extern "C" void gea_three_webgl_uniform_matrix_2fv(
|
|
2469
|
+
double location,
|
|
2470
|
+
double count,
|
|
2471
|
+
double transpose,
|
|
2472
|
+
const gea::detail::HostNumericArgument<float>& values
|
|
2473
|
+
) {
|
|
2474
|
+
geaTrapCheck("gea_three_webgl_uniform_matrix_2fv");
|
|
2475
|
+
GEA_WEBGL_VOID("uniformMatrix2fv");
|
|
2476
|
+
const auto& data = values;
|
|
2477
|
+
if (data.size() % 4 != 0) return;
|
|
2478
|
+
const GLsizei matrixCount = static_cast<GLsizei>(data.size() / 4);
|
|
2479
|
+
if (!data.empty()) gWebGL.glUniformMatrix2fv(asGLint(location), matrixCount, transpose != 0.0, data.data());
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
extern "C" void gea_three_webgl_uniform_matrix_3fv(
|
|
2483
|
+
double location,
|
|
2484
|
+
double count,
|
|
2485
|
+
double transpose,
|
|
2486
|
+
const gea::detail::HostNumericArgument<float>& values
|
|
2487
|
+
) {
|
|
2488
|
+
geaTrapCheck("gea_three_webgl_uniform_matrix_3fv");
|
|
2489
|
+
GEA_WEBGL_VOID("uniformMatrix3fv");
|
|
2490
|
+
const auto& data = values;
|
|
2491
|
+
if (data.size() % 9 != 0) return;
|
|
2492
|
+
const GLsizei matrixCount = static_cast<GLsizei>(data.size() / 9);
|
|
2493
|
+
if (!data.empty()) gWebGL.glUniformMatrix3fv(asGLint(location), matrixCount, transpose != 0.0, data.data());
|
|
2494
|
+
if (nativeWebGLTrapEnabled()) {
|
|
2495
|
+
static int logged = 0;
|
|
2496
|
+
const GLenum err = gWebGL.glGetError ? gWebGL.glGetError() : 0;
|
|
2497
|
+
if (err != 0 && logged < 24) {
|
|
2498
|
+
++logged;
|
|
2499
|
+
GLint prog = -1;
|
|
2500
|
+
gWebGL.glGetIntegerv(0x8B8D /* GL_CURRENT_PROGRAM */, &prog);
|
|
2501
|
+
smokeLog("[gea-webgl-trap] uniformMatrix3fv ERR=0x%04x loc=%d count=%d n=%zu prog=%d",
|
|
2502
|
+
err, asGLint(location), asGLsizei(std::max(1.0, count)), values.size(), prog);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
extern "C" void gea_three_webgl_uniform_matrix_4fv(
|
|
2508
|
+
double location,
|
|
2509
|
+
double count,
|
|
2510
|
+
double transpose,
|
|
2511
|
+
const gea::detail::HostNumericArgument<float>& values
|
|
2512
|
+
) {
|
|
2513
|
+
geaTrapCheck("gea_three_webgl_uniform_matrix_4fv");
|
|
2514
|
+
GEA_WEBGL_VOID("uniformMatrix4fv");
|
|
2515
|
+
const auto& data = values;
|
|
2516
|
+
if (data.size() % 16 != 0) return;
|
|
2517
|
+
const GLsizei matrixCount = static_cast<GLsizei>(data.size() / 16);
|
|
2518
|
+
if (!data.empty()) gWebGL.glUniformMatrix4fv(asGLint(location), matrixCount, transpose != 0.0, data.data());
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
extern "C" void gea_three_webgl_tex_image_2d(
|
|
2522
|
+
double target,
|
|
2523
|
+
double level,
|
|
2524
|
+
double internalFormat,
|
|
2525
|
+
double width,
|
|
2526
|
+
double height,
|
|
2527
|
+
double format,
|
|
2528
|
+
double type,
|
|
2529
|
+
std::span<const double> values
|
|
2530
|
+
) {
|
|
2531
|
+
geaTrapCheck("gea_three_webgl_tex_image_2d");
|
|
2532
|
+
GEA_WEBGL_VOID("texImage2D");
|
|
2533
|
+
withVectorBytes(values, vectorTypeCodeForGLType(type), [&](const void *bytes, GLsizeiptr) {
|
|
2534
|
+
gWebGL.glTexImage2D(
|
|
2535
|
+
asGLenum(target),
|
|
2536
|
+
asGLint(level),
|
|
2537
|
+
asGLint(internalFormat),
|
|
2538
|
+
asGLsizei(width),
|
|
2539
|
+
asGLsizei(height),
|
|
2540
|
+
0,
|
|
2541
|
+
asGLenum(format),
|
|
2542
|
+
asGLenum(type),
|
|
2543
|
+
bytes
|
|
2544
|
+
);
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
extern "C" void gea_three_webgl_tex_storage_2d(
|
|
2549
|
+
double target,
|
|
2550
|
+
double levels,
|
|
2551
|
+
double internalFormat,
|
|
2552
|
+
double width,
|
|
2553
|
+
double height
|
|
2554
|
+
) {
|
|
2555
|
+
geaTrapCheck("gea_three_webgl_tex_storage_2d");
|
|
2556
|
+
GEA_WEBGL_VOID("texStorage2D");
|
|
2557
|
+
if (nativeWebGLTrapEnabled()) {
|
|
2558
|
+
smokeLog("[gea-webgl-trap] texStorage2D(target=0x%04x levels=%d fmt=0x%04x %dx%d)",
|
|
2559
|
+
asGLenum(target), asGLsizei(std::max(1.0, levels)), asGLenum(internalFormat),
|
|
2560
|
+
asGLsizei(width), asGLsizei(height));
|
|
2561
|
+
}
|
|
2562
|
+
gWebGL.glTexStorage2D(
|
|
2563
|
+
asGLenum(target),
|
|
2564
|
+
asGLsizei(std::max(1.0, levels)),
|
|
2565
|
+
asGLenum(internalFormat),
|
|
2566
|
+
asGLsizei(width),
|
|
2567
|
+
asGLsizei(height)
|
|
2568
|
+
);
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
extern "C" void gea_three_webgl_tex_sub_image_2d(
|
|
2572
|
+
double target,
|
|
2573
|
+
double level,
|
|
2574
|
+
double xoffset,
|
|
2575
|
+
double yoffset,
|
|
2576
|
+
double width,
|
|
2577
|
+
double height,
|
|
2578
|
+
double format,
|
|
2579
|
+
double type,
|
|
2580
|
+
std::span<const double> values
|
|
2581
|
+
) {
|
|
2582
|
+
geaTrapCheck("gea_three_webgl_tex_sub_image_2d");
|
|
2583
|
+
GEA_WEBGL_VOID("texSubImage2D");
|
|
2584
|
+
if (nativeWebGLTrapEnabled()) {
|
|
2585
|
+
static int logged = 0;
|
|
2586
|
+
if (logged++ < 12) {
|
|
2587
|
+
smokeLog("[gea-webgl-trap] texSubImage2D(target=0x%04x level=%d off=%d,%d %dx%d fmt=0x%04x type=0x%04x n=%zu)",
|
|
2588
|
+
asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset),
|
|
2589
|
+
asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type), values.size());
|
|
2590
|
+
if (values.size() >= 3000) {
|
|
2591
|
+
unsigned long sum = 0;
|
|
2592
|
+
char preview[128];
|
|
2593
|
+
int pl = 0;
|
|
2594
|
+
for (size_t i = 2048; i < 2048 + 16 && i < values.size(); i++) {
|
|
2595
|
+
pl += snprintf(preview + pl, sizeof(preview) - pl, "%d,", (int)values[i]);
|
|
2596
|
+
}
|
|
2597
|
+
for (size_t i = 0; i < values.size(); i++) sum += (unsigned long)(unsigned char)(int)values[i];
|
|
2598
|
+
smokeLog("[gea-webgl-trap] texSubImage2D bytes@2048: %s sum%%1e9+7=%lu", preview, sum % 1000000007ul);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
withVectorBytes(values, vectorTypeCodeForGLType(type), [&](const void *bytes, GLsizeiptr) {
|
|
2603
|
+
gWebGL.glTexSubImage2D(
|
|
2604
|
+
asGLenum(target),
|
|
2605
|
+
asGLint(level),
|
|
2606
|
+
asGLint(xoffset),
|
|
2607
|
+
asGLint(yoffset),
|
|
2608
|
+
asGLsizei(width),
|
|
2609
|
+
asGLsizei(height),
|
|
2610
|
+
asGLenum(format),
|
|
2611
|
+
asGLenum(type),
|
|
2612
|
+
bytes
|
|
2613
|
+
);
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
extern "C" double gea_three_webgl_call_f32(
|
|
2618
|
+
double opValue,
|
|
2619
|
+
double a,
|
|
2620
|
+
double b,
|
|
2621
|
+
double c,
|
|
2622
|
+
double d,
|
|
2623
|
+
double e,
|
|
2624
|
+
double f,
|
|
2625
|
+
double h,
|
|
2626
|
+
double i,
|
|
2627
|
+
std::span<const double> values
|
|
2628
|
+
) {
|
|
2629
|
+
geaTrapCheck("gea_three_webgl_call_f32");
|
|
2630
|
+
if (!gWebGL.ready || !makeCurrent("eglMakeCurrent(call-f32)")) return 0.0;
|
|
2631
|
+
const int op = static_cast<int>(opValue);
|
|
2632
|
+
if (op == 18) {
|
|
2633
|
+
withVectorBytes(values, static_cast<int>(c), [&](const void *bytes, GLsizeiptr size) {
|
|
2634
|
+
nativeBufferData(static_cast<GLenum>(a), size, bytes, static_cast<GLenum>(b));
|
|
2635
|
+
});
|
|
2636
|
+
return 0.0;
|
|
2637
|
+
}
|
|
2638
|
+
if (op == 19) {
|
|
2639
|
+
withVectorBytes(values, static_cast<int>(c), [&](const void *bytes, GLsizeiptr size) {
|
|
2640
|
+
nativeBufferSubData(static_cast<GLenum>(a), static_cast<GLintptr>(b), size, bytes);
|
|
2641
|
+
});
|
|
2642
|
+
return 0.0;
|
|
2643
|
+
}
|
|
2644
|
+
if (op == 36) {
|
|
2645
|
+
const FloatUploadData data(values);
|
|
2646
|
+
gWebGL.glUniformMatrix4fv(static_cast<GLint>(a), static_cast<GLsizei>(std::max(1.0, b)), c != 0.0, data.data());
|
|
2647
|
+
return 0.0;
|
|
2648
|
+
}
|
|
2649
|
+
if (op == 80) {
|
|
2650
|
+
const FloatUploadData data(values);
|
|
2651
|
+
gWebGL.glUniformMatrix3fv(static_cast<GLint>(a), static_cast<GLsizei>(std::max(1.0, b)), c != 0.0, data.data());
|
|
2652
|
+
return 0.0;
|
|
2653
|
+
}
|
|
2654
|
+
if (op == 50) {
|
|
2655
|
+
withVectorBytes(values, static_cast<int>(h), [&](const void *bytes, GLsizeiptr) {
|
|
2656
|
+
gWebGL.glTexImage2D(
|
|
2657
|
+
static_cast<GLenum>(a),
|
|
2658
|
+
static_cast<GLint>(b),
|
|
2659
|
+
static_cast<GLint>(c),
|
|
2660
|
+
static_cast<GLsizei>(d),
|
|
2661
|
+
static_cast<GLsizei>(e),
|
|
2662
|
+
0,
|
|
2663
|
+
static_cast<GLenum>(f),
|
|
2664
|
+
static_cast<GLenum>(h),
|
|
2665
|
+
bytes
|
|
2666
|
+
);
|
|
2667
|
+
});
|
|
2668
|
+
return 0.0;
|
|
2669
|
+
}
|
|
2670
|
+
if (op == 51) {
|
|
2671
|
+
withVectorBytes(values, static_cast<int>(h), [&](const void *bytes, GLsizeiptr) {
|
|
2672
|
+
gWebGL.glTexSubImage2D(
|
|
2673
|
+
static_cast<GLenum>(a),
|
|
2674
|
+
static_cast<GLint>(b),
|
|
2675
|
+
static_cast<GLint>(c),
|
|
2676
|
+
static_cast<GLint>(d),
|
|
2677
|
+
static_cast<GLsizei>(e),
|
|
2678
|
+
static_cast<GLsizei>(f),
|
|
2679
|
+
static_cast<GLenum>(h),
|
|
2680
|
+
static_cast<GLenum>(i),
|
|
2681
|
+
bytes
|
|
2682
|
+
);
|
|
2683
|
+
});
|
|
2684
|
+
return 0.0;
|
|
2685
|
+
}
|
|
2686
|
+
return 0.0;
|
|
2687
|
+
}
|