@geastack/native-webgl-angle 0.1.12 → 0.1.13

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.
@@ -1,4 +1,6 @@
1
1
  #include "webgl_readback.h"
2
+ // GEA_WEBGL_STATIC_GLES: EGL/GLES are statically linked into the program, so
3
+ // nothing is loaded and entry points resolve through eglGetProcAddress.
2
4
  #if defined(_WIN32)
3
5
  #ifndef NOMINMAX
4
6
  #define NOMINMAX
@@ -7,13 +9,14 @@
7
9
  // Legacy Win16 macros are not C++ identifiers and must not leak into the runtime.
8
10
  #undef near
9
11
  #undef far
10
- #else
12
+ #elif defined(__APPLE__)
11
13
  #include "gea/apple/native_bridge.h"
12
14
 
13
15
  #import <AppKit/AppKit.h>
14
16
  #import <QuartzCore/QuartzCore.h>
15
17
  #import <dispatch/dispatch.h>
16
-
18
+ #endif
19
+ #if !defined(_WIN32) && !defined(GEA_WEBGL_STATIC_GLES)
17
20
  #include <dlfcn.h>
18
21
  #endif
19
22
  // Typed uniform arguments use the compiler runtime on both native platforms.
@@ -172,7 +175,7 @@ extern "C" bool gea_three_key_pressed(double code) {
172
175
  return gGeaKeyState[c];
173
176
  }
174
177
 
175
- #if defined(_WIN32)
178
+ #if !defined(__APPLE__)
176
179
  // The Win32 window procedure owns input on Windows; it reports into the same
177
180
  // tables the AppKit host view fills on macOS (keys as macOS virtual key codes).
178
181
  extern "C" void gea_three_set_key_state(int code, bool down) {
@@ -189,7 +192,7 @@ extern "C" void gea_three_set_pointer_state(double x, double y, double width, do
189
192
  }
190
193
  #endif
191
194
 
192
- #if !defined(_WIN32)
195
+ #if defined(__APPLE__)
193
196
  @interface GeaAngleWebGLHostView : NSView
194
197
  @end
195
198
 
@@ -410,6 +413,10 @@ using PFNGLTEXIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLi
410
413
  using PFNGLTEXPARAMETERFPROC = void (*)(GLenum, GLenum, GLfloat);
411
414
  using PFNGLTEXPARAMETERIPROC = void (*)(GLenum, GLenum, GLint);
412
415
  using PFNGLTEXSUBIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void *);
416
+ using PFNGLTEXIMAGE3DPROC = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *);
417
+ using PFNGLTEXSUBIMAGE3DPROC = void (*)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void *);
418
+ using PFNGLTEXSTORAGE3DPROC = void (*)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei);
419
+ using PFNGLFRAMEBUFFERTEXTURELAYERPROC = void (*)(GLenum, GLenum, GLuint, GLint, GLint);
413
420
  using PFNGLTEXSTORAGE2DPROC = void (*)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);
414
421
  using PFNGLUNIFORM1FPROC = void (*)(GLint, GLfloat);
415
422
  using PFNGLUNIFORM1FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
@@ -447,6 +454,13 @@ using PFNGLCOPYTEXIMAGE2DPROC=void(*)(GLenum,GLint,GLenum,GLint,GLint,GLsizei,GL
447
454
  using PFNGLCOPYTEXSUBIMAGE2DPROC=void(*)(GLenum,GLint,GLint,GLint,GLint,GLint,GLsizei,GLsizei);
448
455
  using PFNGLBLITFRAMEBUFFERPROC = void (*)(GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLbitfield,GLenum);
449
456
  using PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC = void (*)(GLenum,GLsizei,GLenum,GLsizei,GLsizei);
457
+ using GLuint64 = std::uint64_t;
458
+ using GLsync = struct __GLsync *;
459
+ using PFNGLMAPBUFFERRANGEPROC = void *(*)(GLenum, GLintptr, GLsizeiptr, GLbitfield);
460
+ using PFNGLUNMAPBUFFERPROC = GLboolean (*)(GLenum);
461
+ using PFNGLFENCESYNCPROC = GLsync (*)(GLenum, GLbitfield);
462
+ using PFNGLCLIENTWAITSYNCPROC = GLenum (*)(GLsync, GLbitfield, GLuint64);
463
+ using PFNGLDELETESYNCPROC = void (*)(GLsync);
450
464
  struct AngleWebGLState {
451
465
  PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D=nullptr;
452
466
  PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D=nullptr;
@@ -533,6 +547,12 @@ struct AngleWebGLState {
533
547
  PFNGLPIXELSTOREIPROC glPixelStorei = nullptr;
534
548
  PFNGLPOLYGONOFFSETPROC glPolygonOffset = nullptr;
535
549
  PFNGLREADPIXELSPROC glReadPixels = nullptr;
550
+ // WebGL2 asynchronous readback: PIXEL_PACK_BUFFER reads, sync objects and read-mapping.
551
+ PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr;
552
+ PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr;
553
+ PFNGLFENCESYNCPROC glFenceSync = nullptr;
554
+ PFNGLCLIENTWAITSYNCPROC glClientWaitSync = nullptr;
555
+ PFNGLDELETESYNCPROC glDeleteSync = nullptr;
536
556
  PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
537
557
  PFNGLSCISSORPROC glScissor = nullptr;
538
558
  PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
@@ -547,6 +567,12 @@ struct AngleWebGLState {
547
567
  PFNGLTEXPARAMETERIPROC glTexParameteri = nullptr;
548
568
  PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D = nullptr;
549
569
  PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr;
570
+ // Array/3D entry points are optional: a GLES without them still starts,
571
+ // and uploads to those targets report GL_INVALID_OPERATION in the log.
572
+ PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr;
573
+ PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr;
574
+ PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr;
575
+ PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = nullptr;
550
576
  PFNGLUNIFORM1FPROC glUniform1f = nullptr;
551
577
  PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
552
578
  PFNGLUNIFORM1IPROC glUniform1i = nullptr;
@@ -579,7 +605,7 @@ struct AngleWebGLState {
579
605
  PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
580
606
  PFNGLVIEWPORTPROC glViewport = nullptr;
581
607
 
582
- #if defined(_WIN32)
608
+ #if !defined(__APPLE__)
583
609
  void *nativeWindow = nullptr;
584
610
  #else
585
611
  NSView *hostView = nil;
@@ -617,7 +643,7 @@ static bool nativeShadowColorAuditEnabled() {
617
643
  }
618
644
 
619
645
  static void smokeLog(const char *format, ...) {
620
- char message[1024] = {};
646
+ char message[16384] = {};
621
647
  va_list args;
622
648
  va_start(args, format);
623
649
  std::vsnprintf(message, sizeof(message), format, args);
@@ -631,8 +657,13 @@ static void smokeLog(const char *format, ...) {
631
657
  std::fprintf(stderr, "%s\n", message);
632
658
  return;
633
659
  }
634
- #else
660
+ #elif defined(__APPLE__)
635
661
  if (!path || path[0] == '\0') path = "/tmp/three-angle-metal-smoke.log";
662
+ #else
663
+ if (!path || path[0] == '\0') {
664
+ std::fprintf(stderr, "%s\n", message);
665
+ return;
666
+ }
636
667
  #endif
637
668
  if (FILE *file = std::fopen(path, "a")) {
638
669
  std::fprintf(file, "%s\n", message);
@@ -653,7 +684,7 @@ extern "C" void gea_three_angle_smoke_log(std::string message) {
653
684
  // handler records the position in the host view's coordinate space with the
654
685
  // same top-left-origin flip the per-view path used. Returning the event
655
686
  // unmodified keeps normal dispatch (clicks, first responder) intact.
656
- #if !defined(_WIN32)
687
+ #if defined(__APPLE__)
657
688
  static id gGeaPointerMonitor = nil;
658
689
  static void geaInstallPointerMonitor(void) {
659
690
  if (gGeaPointerMonitor) return;
@@ -696,9 +727,16 @@ static void *openFirst(const char *envName, const char *const *paths) {
696
727
 
697
728
  #endif
698
729
 
730
+ #if defined(GEA_WEBGL_STATIC_GLES)
731
+ extern "C" void *eglGetProcAddress(const char *name);
732
+ #endif
699
733
  template <typename Fn>
700
734
  static Fn loadSymbol(void *library, const char *name) {
701
- #if defined(_WIN32)
735
+ #if defined(GEA_WEBGL_STATIC_GLES)
736
+ (void)library;
737
+ if (std::strcmp(name, "eglGetProcAddress") == 0) return reinterpret_cast<Fn>(&eglGetProcAddress);
738
+ return reinterpret_cast<Fn>(eglGetProcAddress(name));
739
+ #elif defined(_WIN32)
702
740
  return reinterpret_cast<Fn>(GetProcAddress(static_cast<HMODULE>(library), name));
703
741
  #else
704
742
  return reinterpret_cast<Fn>(dlsym(library, name));
@@ -797,7 +835,7 @@ static void logEglFailure(const char *step) {
797
835
  std::fprintf(stderr, "[three-angle-metal] %s failed, EGL error 0x%04x\n", step, lastEglError());
798
836
  }
799
837
 
800
- #if !defined(_WIN32)
838
+ #if defined(__APPLE__)
801
839
  static CGFloat backingScaleForHostView(NSView *nativeView) {
802
840
  CGFloat scale = nativeView.window ? nativeView.window.backingScaleFactor : 0.0;
803
841
  if (scale <= 0.0 && nativeView.window.screen) scale = nativeView.window.screen.backingScaleFactor;
@@ -870,13 +908,13 @@ extern "C" void gea_three_webgl_update_size_uwp(double width, double height, dou
870
908
  }
871
909
  #endif
872
910
 
873
- #if defined(_WIN32)
911
+ #if !defined(__APPLE__)
874
912
  // The UWP frame driver owns its EGL context exclusively until it returns to
875
913
  // CoreWindow event dispatch. Other hosts retain per-call binding semantics.
876
914
  static thread_local bool gUwpFrameContextCurrent = false;
877
915
  #endif
878
916
  static bool makeCurrent(const char *label) {
879
- #if defined(_WIN32)
917
+ #if !defined(__APPLE__)
880
918
  if (gUwpFrameContextCurrent) return true;
881
919
  #endif
882
920
  if (!gWebGL.ready && gWebGL.context == EGL_NO_CONTEXT) return false;
@@ -907,7 +945,7 @@ static bool makeCurrent(const char *label) {
907
945
  return true;
908
946
  }
909
947
 
910
- #if defined(_WIN32)
948
+ #if !defined(__APPLE__)
911
949
  extern "C" bool gea_three_webgl_begin_frame_uwp() {
912
950
  // Always rebind after the dispatcher, where arbitrary platform callbacks
913
951
  // may have used a different context. No such callbacks run within the frame.
@@ -921,7 +959,7 @@ extern "C" void gea_three_webgl_end_frame_uwp() {
921
959
  }
922
960
  #endif
923
961
 
924
- #if !defined(_WIN32)
962
+ #if defined(__APPLE__)
925
963
  static bool rebuildWindowSurface(const char *label) {
926
964
  if (!gWebGL.eglCreateWindowSurface || !gWebGL.eglDestroySurface || !gWebGL.eglMakeCurrent ||
927
965
  !gWebGL.metalLayer || !gWebGL.config || gWebGL.context == EGL_NO_CONTEXT) {
@@ -1029,6 +1067,11 @@ static bool loadGlSymbols() {
1029
1067
  LOAD_GL(glPixelStorei, PFNGLPIXELSTOREIPROC)
1030
1068
  LOAD_GL(glPolygonOffset, PFNGLPOLYGONOFFSETPROC)
1031
1069
  LOAD_GL(glReadPixels, PFNGLREADPIXELSPROC)
1070
+ LOAD_GL(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC)
1071
+ LOAD_GL(glUnmapBuffer, PFNGLUNMAPBUFFERPROC)
1072
+ LOAD_GL(glFenceSync, PFNGLFENCESYNCPROC)
1073
+ LOAD_GL(glClientWaitSync, PFNGLCLIENTWAITSYNCPROC)
1074
+ LOAD_GL(glDeleteSync, PFNGLDELETESYNCPROC)
1032
1075
  LOAD_GL(glCopyTexImage2D,PFNGLCOPYTEXIMAGE2DPROC)
1033
1076
  LOAD_GL(glCopyTexSubImage2D,PFNGLCOPYTEXSUBIMAGE2DPROC)
1034
1077
  LOAD_GL(glBlitFramebuffer, PFNGLBLITFRAMEBUFFERPROC)
@@ -1047,6 +1090,10 @@ static bool loadGlSymbols() {
1047
1090
  LOAD_GL(glTexParameteri, PFNGLTEXPARAMETERIPROC)
1048
1091
  LOAD_GL(glTexSubImage2D, PFNGLTEXSUBIMAGE2DPROC)
1049
1092
  LOAD_GL(glTexStorage2D, PFNGLTEXSTORAGE2DPROC)
1093
+ gWebGL.glTexImage3D = loadGl<PFNGLTEXIMAGE3DPROC>("glTexImage3D");
1094
+ gWebGL.glTexSubImage3D = loadGl<PFNGLTEXSUBIMAGE3DPROC>("glTexSubImage3D");
1095
+ gWebGL.glTexStorage3D = loadGl<PFNGLTEXSTORAGE3DPROC>("glTexStorage3D");
1096
+ gWebGL.glFramebufferTextureLayer = loadGl<PFNGLFRAMEBUFFERTEXTURELAYERPROC>("glFramebufferTextureLayer");
1050
1097
  LOAD_GL(glUniform1f, PFNGLUNIFORM1FPROC)
1051
1098
  LOAD_GL(glUniform1fv, PFNGLUNIFORM1FVPROC)
1052
1099
  LOAD_GL(glUniform1i, PFNGLUNIFORM1IPROC)
@@ -1082,7 +1129,7 @@ static bool loadGlSymbols() {
1082
1129
  return true;
1083
1130
  }
1084
1131
 
1085
- #if defined(_WIN32)
1132
+ #if !defined(__APPLE__)
1086
1133
  extern "C" bool gea_three_webgl_attach_uwp(
1087
1134
  void *nativeWindow,
1088
1135
  #else
@@ -1097,7 +1144,13 @@ extern "C" bool gea_three_webgl_attach(
1097
1144
  smokeLog("[three-angle-metal] WebGL host attach requested");
1098
1145
  if (gWebGL.ready) return true;
1099
1146
 
1100
- #if defined(_WIN32)
1147
+ #if defined(GEA_WEBGL_STATIC_GLES)
1148
+ // Linked into the program: nothing to load; loadSymbol resolves through
1149
+ // eglGetProcAddress. The handles only need to be non-null.
1150
+ static int staticGles;
1151
+ gWebGL.egl = &staticGles;
1152
+ gWebGL.gles = &staticGles;
1153
+ #elif defined(_WIN32)
1101
1154
  // An override (e.g. threejs-rendozer's GeaRendozerGLES.dll) first, then the
1102
1155
  // packaged ANGLE of a UWP app, then a plain desktop load next to the exe.
1103
1156
  auto openWin32 = [](const wchar_t *overrideEnv, const wchar_t *name) -> HMODULE {
@@ -1114,7 +1167,7 @@ extern "C" bool gea_three_webgl_attach(
1114
1167
  };
1115
1168
  gWebGL.egl = openWin32(L"GEA_ANGLE_EGL_DYLIB", L"libEGL.dll");
1116
1169
  gWebGL.gles = openWin32(L"GEA_ANGLE_GLES_DYLIB", L"libGLESv2.dll");
1117
- #else
1170
+ #elif defined(__APPLE__)
1118
1171
  static const char *const eglPaths[] = {
1119
1172
  "/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
1120
1173
  "/Applications/Cursor.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
@@ -1132,6 +1185,8 @@ extern "C" bool gea_three_webgl_attach(
1132
1185
 
1133
1186
  gWebGL.egl = openFirst("GEA_ANGLE_EGL_DYLIB", eglPaths);
1134
1187
  gWebGL.gles = openFirst("GEA_ANGLE_GLES_DYLIB", glesPaths);
1188
+ #else
1189
+ #error "load libEGL/libGLESv2 for this platform, or build with GEA_WEBGL_STATIC_GLES"
1135
1190
  #endif
1136
1191
  if (!gWebGL.egl || !gWebGL.gles) {
1137
1192
  smokeLog("[three-angle-metal] ANGLE libEGL/libGLESv2 were not found");
@@ -1209,7 +1264,7 @@ extern "C" bool gea_three_webgl_attach(
1209
1264
  }
1210
1265
  gWebGL.config = config;
1211
1266
 
1212
- #if defined(_WIN32)
1267
+ #if !defined(__APPLE__)
1213
1268
  gWebGL.nativeWindow = nativeWindow;
1214
1269
  gea_three_webgl_update_size_uwp(width, height, devicePixelRatio);
1215
1270
  if (gWebGL.eglCreateWindowSurface) {
@@ -1275,7 +1330,7 @@ extern "C" bool gea_three_webgl_attach(
1275
1330
 
1276
1331
  gWebGL.ready = true;
1277
1332
  gWebGL.glViewport(0, 0, gWebGL.widthPx, gWebGL.heightPx);
1278
- #if !defined(_WIN32)
1333
+ #if defined(__APPLE__)
1279
1334
  dispatch_async(dispatch_get_main_queue(), ^{
1280
1335
  if (gWebGL.ready && rebuildWindowSurface("eglCreateWindowSurface(deferred)")) {
1281
1336
  smokeLog("[three-angle-metal] WebGL EGL surface rebound at %dx%d", gWebGL.widthPx, gWebGL.heightPx);
@@ -1391,7 +1446,7 @@ extern "C" void gea_three_webgl_swap() {
1391
1446
  // TEMP-DIAG: GEA_THREE_VIEW_DUMP=1 — at frame 60, log the window's full
1392
1447
  // subview tree and a hit-test grid so overlay views that would swallow
1393
1448
  // mouse events are visible without Accessibility permission.
1394
- #if !defined(_WIN32)
1449
+ #if defined(__APPLE__)
1395
1450
  if (std::getenv("GEA_THREE_VIEW_DUMP")) {
1396
1451
  static int viewDumpCount = 0;
1397
1452
  if (++viewDumpCount == 60) {
@@ -1424,6 +1479,16 @@ extern "C" void gea_three_webgl_swap() {
1424
1479
  }
1425
1480
  }
1426
1481
  #endif
1482
+ // The debug dump and recording read the window into client memory. A PIXEL_PACK_BUFFER the app left
1483
+ // bound (WebGL2 asynchronous readback) would make the pointer a buffer offset instead, so the read
1484
+ // runs with it unbound and the binding is restored afterwards.
1485
+ const auto readWindowRgba = [](int w, int h, unsigned char *rgba) {
1486
+ GLint packBuffer = 0;
1487
+ if (gWebGL.glGetIntegerv) gWebGL.glGetIntegerv(0x88ED /* PIXEL_PACK_BUFFER_BINDING */, &packBuffer);
1488
+ if (packBuffer) gWebGL.glBindBuffer(0x88EB /* PIXEL_PACK_BUFFER */, 0);
1489
+ gWebGL.glReadPixels(0, 0, w, h, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba);
1490
+ if (packBuffer) gWebGL.glBindBuffer(0x88EB, static_cast<GLuint>(packBuffer));
1491
+ };
1427
1492
  // Debug frame dump: GEA_WEBGL_DUMP_FRAME=<path-prefix> writes the full
1428
1493
  // framebuffer of frames 90/180/300 as PPM (P6) — visual ground truth for
1429
1494
  // native-vs-browser comparisons without needing a window screenshot.
@@ -1434,7 +1499,7 @@ extern "C" void gea_three_webgl_swap() {
1434
1499
  const int w = gWebGL.widthPx;
1435
1500
  const int h = gWebGL.heightPx;
1436
1501
  std::vector<unsigned char> rgba(static_cast<size_t>(w) * h * 4);
1437
- gWebGL.glReadPixels(0, 0, w, h, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba.data());
1502
+ readWindowRgba(w, h, rgba.data());
1438
1503
  char path[512];
1439
1504
  std::snprintf(path, sizeof path, "%s-f%d.ppm", dumpPrefix, dumpCount);
1440
1505
  if (FILE *file = std::fopen(path, "wb")) {
@@ -1483,7 +1548,7 @@ extern "C" void gea_three_webgl_swap() {
1483
1548
  if (due > recordWritten && gWebGL.widthPx == recordW && gWebGL.heightPx == recordH) {
1484
1549
  std::vector<unsigned char> rgba(static_cast<size_t>(recordW) * recordH * 4);
1485
1550
  std::vector<unsigned char> rgb(static_cast<size_t>(recordW) * recordH * 3);
1486
- gWebGL.glReadPixels(0, 0, recordW, recordH, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba.data());
1551
+ readWindowRgba(recordW, recordH, rgba.data());
1487
1552
  for (int y = 0; y < recordH; ++y) {
1488
1553
  const unsigned char *src = rgba.data() + static_cast<size_t>(recordH - 1 - y) * recordW * 4;
1489
1554
  unsigned char *dst = rgb.data() + static_cast<size_t>(y) * recordW * 3;
@@ -2498,6 +2563,36 @@ extern "C" void gea_three_webgl_tex_image_2d_bytes(double target, double level,
2498
2563
  gWebGL.glTexImage2D(asGLenum(target), asGLint(level), asGLint(internalFormat), asGLsizei(width), asGLsizei(height), 0, asGLenum(format), asGLenum(type), bytes);
2499
2564
  }
2500
2565
 
2566
+ static bool requireGl(const void *fn, const char *name) {
2567
+ if (fn) return true;
2568
+ smokeLog("[native-webgl] %s is not provided by this GLES; the call is dropped", name);
2569
+ return false;
2570
+ }
2571
+
2572
+ extern "C" void gea_three_webgl_tex_image_3d_bytes(double target, double level, double internalFormat, double width, double height, double depth, double format, double type, const void* bytes, double) {
2573
+ GEA_WEBGL_VOID("texImage3DBytes");
2574
+ if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexImage3D), "glTexImage3D")) return;
2575
+ gWebGL.glTexImage3D(asGLenum(target), asGLint(level), asGLint(internalFormat), asGLsizei(width), asGLsizei(height), asGLsizei(depth), 0, asGLenum(format), asGLenum(type), bytes);
2576
+ }
2577
+
2578
+ extern "C" void gea_three_webgl_tex_sub_image_3d_bytes(double target, double level, double xoffset, double yoffset, double zoffset, double width, double height, double depth, double format, double type, const void* bytes, double) {
2579
+ GEA_WEBGL_VOID("texSubImage3DBytes");
2580
+ if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexSubImage3D), "glTexSubImage3D")) return;
2581
+ gWebGL.glTexSubImage3D(asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset), asGLint(zoffset), asGLsizei(width), asGLsizei(height), asGLsizei(depth), asGLenum(format), asGLenum(type), bytes);
2582
+ }
2583
+
2584
+ extern "C" void gea_three_webgl_tex_storage_3d(double target, double levels, double internalFormat, double width, double height, double depth) {
2585
+ GEA_WEBGL_VOID("texStorage3D");
2586
+ if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexStorage3D), "glTexStorage3D")) return;
2587
+ gWebGL.glTexStorage3D(asGLenum(target), asGLsizei(std::max(1.0, levels)), asGLenum(internalFormat), asGLsizei(width), asGLsizei(height), asGLsizei(depth));
2588
+ }
2589
+
2590
+ extern "C" void gea_three_webgl_framebuffer_texture_layer(double target, double attachment, double texture, double level, double layer) {
2591
+ GEA_WEBGL_VOID("framebufferTextureLayer");
2592
+ if (!requireGl(reinterpret_cast<const void *>(gWebGL.glFramebufferTextureLayer), "glFramebufferTextureLayer")) return;
2593
+ gWebGL.glFramebufferTextureLayer(asGLenum(target), asGLenum(attachment), asGLuint(texture), asGLint(level), asGLint(layer));
2594
+ }
2595
+
2501
2596
  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) {
2502
2597
  GEA_WEBGL_VOID("texSubImage2DBytes");
2503
2598
  gWebGL.glTexSubImage2D(asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset), asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type), bytes);
@@ -2805,6 +2900,106 @@ extern "C" void gea_three_webgl_read_pixels_bytes(double x, double y, double wid
2805
2900
  gWebGL.glReadPixels(asGLint(x),asGLint(y),asGLsizei(width),asGLsizei(height),asGLenum(format),asGLenum(type),bytes);
2806
2901
  }
2807
2902
 
2903
+ // WebGL2 asynchronous readback (three's readRenderTargetPixelsAsync): readPixels into the bound
2904
+ // PIXEL_PACK_BUFFER at a byte offset, a fence, polling, then getBufferSubData through a read mapping.
2905
+ extern "C" void gea_three_webgl_read_pixels_pack(double x, double y, double width, double height, double format, double type, double offset) {
2906
+ GEA_WEBGL_VOID("readPixels");
2907
+ GLint packBuffer = 0;
2908
+ gWebGL.glGetIntegerv(0x88ED, &packBuffer);
2909
+ if (!packBuffer || !std::isfinite(offset) || offset < 0 || std::floor(offset) != offset) { if (!hostReadbackError) hostReadbackError = 0x0502; return; }
2910
+ gWebGL.glReadPixels(asGLint(x), asGLint(y), asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type),
2911
+ reinterpret_cast<void *>(static_cast<std::uintptr_t>(offset)));
2912
+ }
2913
+ static std::unordered_map<std::uint32_t, GLsync> gWebGLSyncs;
2914
+ static std::uint32_t gWebGLNextSync = 1;
2915
+ extern "C" double gea_three_webgl_fence_sync() {
2916
+ GEA_WEBGL_DOUBLE("fenceSync");
2917
+ GLsync sync = gWebGL.glFenceSync(0x9117 /* SYNC_GPU_COMMANDS_COMPLETE */, 0);
2918
+ if (!sync) return 0.0;
2919
+ const std::uint32_t id = gWebGLNextSync++;
2920
+ gWebGLSyncs[id] = sync;
2921
+ return static_cast<double>(id);
2922
+ }
2923
+ extern "C" double gea_three_webgl_client_wait_sync(double sync, double flags, double timeout) {
2924
+ GEA_WEBGL_DOUBLE("clientWaitSync");
2925
+ auto it = gWebGLSyncs.find(static_cast<std::uint32_t>(sync));
2926
+ if (it == gWebGLSyncs.end()) return 0x911D; // WAIT_FAILED
2927
+ return static_cast<double>(gWebGL.glClientWaitSync(it->second, static_cast<GLbitfield>(flags), static_cast<GLuint64>(std::max(0.0, timeout))));
2928
+ }
2929
+ extern "C" void gea_three_webgl_delete_sync(double sync) {
2930
+ GEA_WEBGL_VOID("deleteSync");
2931
+ auto it = gWebGLSyncs.find(static_cast<std::uint32_t>(sync));
2932
+ if (it == gWebGLSyncs.end()) return;
2933
+ gWebGL.glDeleteSync(it->second);
2934
+ gWebGLSyncs.erase(it);
2935
+ }
2936
+ // GPU timers for the app's frame profiler, on GL_EXT_disjoint_timer_query.
2937
+ struct HostGpuTimers {
2938
+ bool initialized = false, supported = false;
2939
+ void (*gen)(GLsizei, GLuint *) = nullptr;
2940
+ void (*remove)(GLsizei, const GLuint *) = nullptr;
2941
+ void (*begin)(GLenum, GLuint) = nullptr;
2942
+ void (*end)(GLenum) = nullptr;
2943
+ void (*available)(GLuint, GLenum, GLuint *) = nullptr;
2944
+ void (*result)(GLuint, GLenum, std::uint64_t *) = nullptr;
2945
+ bool open = false;
2946
+ bool ensure() {
2947
+ if (initialized) return supported;
2948
+ initialized = true;
2949
+ const auto extensions = reinterpret_cast<const char *>(gWebGL.glGetString(0x1F03));
2950
+ const std::string list = " " + std::string(extensions ? extensions : "") + " ";
2951
+ if (list.find(" GL_EXT_disjoint_timer_query ") == std::string::npos) return false;
2952
+ gen = loadGl<decltype(gen)>("glGenQueriesEXT");
2953
+ remove = loadGl<decltype(remove)>("glDeleteQueriesEXT");
2954
+ begin = loadGl<decltype(begin)>("glBeginQueryEXT");
2955
+ end = loadGl<decltype(end)>("glEndQueryEXT");
2956
+ available = loadGl<decltype(available)>("glGetQueryObjectuivEXT");
2957
+ result = loadGl<decltype(result)>("glGetQueryObjectui64vEXT");
2958
+ supported = gen && remove && begin && end && available && result;
2959
+ return supported;
2960
+ }
2961
+ };
2962
+ static HostGpuTimers gHostGpuTimers;
2963
+ extern "C" double gea_three_webgl_timer_begin() {
2964
+ GEA_WEBGL_DOUBLE("timerBegin");
2965
+ auto &t = gHostGpuTimers;
2966
+ if (!t.ensure() || t.open) return 0.0;
2967
+ GLuint query = 0;
2968
+ t.gen(1, &query);
2969
+ if (!query) return 0.0;
2970
+ t.begin(0x88BF /* TIME_ELAPSED_EXT */, query);
2971
+ t.open = true;
2972
+ return static_cast<double>(query);
2973
+ }
2974
+ extern "C" void gea_three_webgl_timer_end() {
2975
+ GEA_WEBGL_VOID("timerEnd");
2976
+ auto &t = gHostGpuTimers;
2977
+ if (!t.supported || !t.open) return;
2978
+ t.end(0x88BF);
2979
+ t.open = false;
2980
+ }
2981
+ extern "C" double gea_three_webgl_timer_result(double timer) {
2982
+ GEA_WEBGL_DOUBLE("timerResult");
2983
+ auto &t = gHostGpuTimers;
2984
+ const GLuint query = static_cast<GLuint>(timer);
2985
+ if (!t.supported || !query) return 0.0;
2986
+ GLuint ready = 0;
2987
+ t.available(query, 0x8867 /* QUERY_RESULT_AVAILABLE */, &ready);
2988
+ if (!ready) return -1.0;
2989
+ std::uint64_t ns = 0;
2990
+ t.result(query, 0x8866 /* QUERY_RESULT */, &ns);
2991
+ t.remove(1, &query);
2992
+ return static_cast<double>(ns);
2993
+ }
2994
+ extern "C" void gea_three_webgl_get_buffer_sub_data_bytes(double target, double srcOffset, void *bytes, double byteLength) {
2995
+ GEA_WEBGL_VOID("getBufferSubData");
2996
+ if (!bytes || !(byteLength > 0) || !(srcOffset >= 0)) { if (!hostReadbackError) hostReadbackError = 0x0501; return; }
2997
+ void *mapped = gWebGL.glMapBufferRange(asGLenum(target), static_cast<GLintptr>(srcOffset), static_cast<GLsizeiptr>(byteLength), 0x0001 /* MAP_READ_BIT */);
2998
+ if (!mapped) { if (!hostReadbackError) hostReadbackError = 0x0502; return; }
2999
+ std::memcpy(bytes, mapped, static_cast<std::size_t>(byteLength));
3000
+ gWebGL.glUnmapBuffer(asGLenum(target));
3001
+ }
3002
+
2808
3003
  extern "C" void gea_three_webgl_blit_framebuffer(double sx0,double sy0,double sx1,double sy1,double dx0,double dy0,double dx1,double dy1,double mask,double filter) {
2809
3004
  GEA_WEBGL_VOID("blitFramebuffer");
2810
3005
  gWebGL.glBlitFramebuffer(asGLint(sx0),asGLint(sy0),asGLint(sx1),asGLint(sy1),asGLint(dx0),asGLint(dy0),asGLint(dx1),asGLint(dy1),asGLenum(mask),asGLenum(filter));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geastack/native-webgl-angle",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/inflate.ts CHANGED
@@ -18,7 +18,7 @@ const codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4,
18
18
  // native build narrows a function's integer locals to machine integers, but a
19
19
  // class's number fields stay doubles, and on a core without a double FPU (the
20
20
  // ESP32-S31) every bit read through a field was several soft-float calls --
21
- // inflating skytail's kit at boot took over 80 seconds.
21
+ // inflating a game's asset kit at boot took over 80 seconds.
22
22
 
23
23
  /** Decoder state: read position, bit buffer, bits held, bytes written. */
24
24
  const position = 0
@@ -1,4 +1,6 @@
1
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
1
2
  declare function threeWebGLCopyTexImage2D(target:number,level:number,internalFormat:number,x:number,y:number,width:number,height:number,border:number):void;
3
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
2
4
  declare function threeWebGLCopyTexSubImage2D(target:number,level:number,xoffset:number,yoffset:number,x:number,y:number,width:number,height:number):void;
3
5
  /// <reference path="./gea-native-types.d.ts" />
4
6
 
@@ -93,6 +95,7 @@ type NativeHandleKind =
93
95
  | 'program'
94
96
  | 'renderbuffer'
95
97
  | 'shader'
98
+ | 'sync'
96
99
  | 'texture'
97
100
  | 'uniform'
98
101
  | 'vertexArray'
@@ -200,11 +203,26 @@ function typedArrayKind(value: BufferSourceLike): number {
200
203
  return 1
201
204
  }
202
205
 
206
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
203
207
  declare function threeWebGLBlitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void
208
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
204
209
  declare function threeWebGLRenderbufferStorageMultisample(target:number, samples:number, format:number, width:number, height:number): void
205
210
  type ReadPixelsDestination = Uint8Array | Uint16Array | Float32Array | Uint32Array | Int32Array | Int16Array | Int8Array
206
211
  // Mutates the destination view; deliberately not marked host-inert.
212
+ /** @gea-host-typed-array-element-writes Stores pixels into the destination typed array's elements only; no other property of any object, no callbacks. */
207
213
  declare function threeWebGLReadPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset: number): void
214
+ // WebGL2 asynchronous readback: into the bound PIXEL_PACK_BUFFER, fenced, then read through getBufferSubData.
215
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
216
+ declare function threeWebGLReadPixelsPack(x: number, y: number, width: number, height: number, format: number, type: number, offset: number): void
217
+ /** @gea-host-inert Creates a GL sync object; never calls back into script. */
218
+ declare function threeWebGLFenceSync(): number
219
+ /** @gea-host-inert Reads scalar arguments, waits on GL state only; never calls back into script. */
220
+ declare function threeWebGLClientWaitSync(sync: number, flags: number, timeout: number): number
221
+ /** @gea-host-inert Reads scalar arguments, writes GL state only; never calls back into script. */
222
+ declare function threeWebGLDeleteSync(sync: number): void
223
+ // Mutates the destination view; deliberately not marked host-inert.
224
+ /** @gea-host-typed-array-element-writes Stores buffer bytes into the destination typed array's elements only; no other property of any object, no callbacks. */
225
+ declare function threeWebGLGetBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset: number, length: number): void
208
226
 
209
227
  function zeroF32Array(length: number): f32[] {
210
228
  const out: f32[] = []
@@ -254,6 +272,14 @@ declare function threeWebGLTexImage2D(target: number, level: number, internalFor
254
272
  declare function threeWebGLTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, values: BufferSourceLike): void
255
273
  /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
256
274
  declare function threeWebGLTexStorage2D(target: number, levels: number, internalFormat: number, width: number, height: number): void
275
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
276
+ declare function threeWebGLTexImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
277
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
278
+ declare function threeWebGLTexSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, values: BufferSourceLike): void
279
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
280
+ declare function threeWebGLTexStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void
281
+ /** @gea-host-inert Reads its arguments, writes GL state only; never calls back into script. */
282
+ declare function threeWebGLFramebufferTextureLayer(target: number, attachment: number, texture: number, level: number, layer: number): void
257
283
 
258
284
  // Shader source keyed by the stable native GL handle. A NativeShader instance
259
285
  // can be value-copied when it round-trips through a function-return / gcv
@@ -663,6 +689,10 @@ export class NativeWebGL2RenderingContext {
663
689
  readonly TIMEOUT_EXPIRED = 0x911B
664
690
  readonly CONDITION_SATISFIED = 0x911C
665
691
  readonly WAIT_FAILED = 0x911D
692
+ readonly SYNC_GPU_COMMANDS_COMPLETE = 0x9117
693
+ readonly PIXEL_PACK_BUFFER = 0x88EB
694
+ readonly PIXEL_PACK_BUFFER_BINDING = 0x88ED
695
+ readonly STREAM_READ = 0x88E1
666
696
  readonly COLOR = 0x1800
667
697
  readonly DEPTH_COMPONENT = 0x1902
668
698
  readonly ALPHA = 0x1906
@@ -935,11 +965,14 @@ export class NativeWebGL2RenderingContext {
935
965
  if (name === this.MAX_RENDERBUFFER_SIZE || name === this.MAX_3D_TEXTURE_SIZE) return 16384
936
966
  if (name === this.MAX_ARRAY_TEXTURE_LAYERS) return 2048
937
967
  if (name === this.MAX_VERTEX_ATTRIBS) return 16
938
- if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 16
968
+ // threejs-rendozer binds up to 32 textures per program (both stages); three checks a program's total against this.
969
+ if (name === this.MAX_TEXTURE_IMAGE_UNITS || name === this.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 32
939
970
  if (name === this.MAX_COMBINED_TEXTURE_IMAGE_UNITS) return 32
940
971
  if (name === this.MAX_VERTEX_UNIFORM_VECTORS || name === this.MAX_FRAGMENT_UNIFORM_VECTORS) return 1024
941
972
  if (name === this.MAX_VARYING_VECTORS) return 32
942
- if (name === this.MAX_SAMPLES || name === this.SAMPLES) return 4
973
+ // threejs-rendozer reports 8 when every multisampled renderbuffer format supports it (and steps down per format).
974
+ if (name === this.MAX_SAMPLES) return 8
975
+ if (name === this.SAMPLES) return 4
943
976
  if (name === this.MAX_UNIFORM_BUFFER_BINDINGS) return 24
944
977
  if (name === this.DEPTH_BITS) return 24
945
978
  if (name === this.STENCIL_BITS) return 8
@@ -1106,10 +1139,10 @@ export class NativeWebGL2RenderingContext {
1106
1139
  texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number | NativeTextureImage | null, format?: number, type?: number, pixels?: BufferSourceLike | null): void {
1107
1140
  if (typeof borderOrSource === 'number' && typeof format === 'number' && typeof type === 'number') {
1108
1141
  threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels ?? new Uint8Array(0))
1109
- } else if (borderOrSource && typeof borderOrSource !== 'number') {
1142
+ } else if (borderOrSource !== null && borderOrSource !== undefined && typeof borderOrSource !== 'number') {
1110
1143
  threeWebGLTexImage2D(target, level, internalFormat, borderOrSource.width, borderOrSource.height, widthOrFormat, heightOrType, borderOrSource.data)
1111
1144
  } else {
1112
- throw new Error('Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage')
1145
+ throw new Error(`Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage (target ${target}, level ${level}, internal format ${internalFormat}, source ${borderOrSource === null ? 'null' : typeof borderOrSource})`)
1113
1146
  }
1114
1147
  }
1115
1148
  texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: BufferSourceLike): void {
@@ -1125,9 +1158,15 @@ export class NativeWebGL2RenderingContext {
1125
1158
  // Three uploads 3D and array textures only from typed `image.data` /
1126
1159
  // `mipmap.data` (or `null` to allocate), the same carrier the 2D uploads
1127
1160
  // take; no TexImageSource ever reaches these entry points.
1128
- texImage3D(_target: number, _level: number, _internalFormat: number, _width: number, _height: number, _depth: number, _border: number, _format: number, _type: number, _source: BufferSourceLike | null): void {}
1129
- texSubImage3D(_target: number, _level: number, _xoffset: number, _yoffset: number, _zoffset: number, _width: number, _height: number, _depth: number, _format: number, _type: number, _source: BufferSourceLike): void {}
1130
- texStorage3D(_target: number, _levels: number, _internalFormat: number, _width: number, _height: number, _depth: number): void {}
1161
+ texImage3D(target: number, level: number, internalFormat: number, width: number, height: number, depth: number, _border: number, format: number, type: number, source: BufferSourceLike | null): void {
1162
+ threeWebGLTexImage3D(target, level, internalFormat, width, height, depth, format, type, source ?? new Uint8Array(0))
1163
+ }
1164
+ texSubImage3D(target: number, level: number, xoffset: number, yoffset: number, zoffset: number, width: number, height: number, depth: number, format: number, type: number, source: BufferSourceLike): void {
1165
+ threeWebGLTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, source)
1166
+ }
1167
+ texStorage3D(target: number, levels: number, internalFormat: number, width: number, height: number, depth: number): void {
1168
+ threeWebGLTexStorage3D(target, levels, internalFormat, width, height, depth)
1169
+ }
1131
1170
 
1132
1171
  createVertexArray(): NativeHandle { return createHandle('vertexArray', nativeWebGLCreateVertexArray()) }
1133
1172
  bindVertexArray(vertexArray: NativeHandle | null): void { nativeWebGLBindVertexArray(nullableNativeHandle(vertexArray)) }
@@ -1150,7 +1189,9 @@ export class NativeWebGL2RenderingContext {
1150
1189
  framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: NativeHandle | null): void {
1151
1190
  nativeWebGLFramebufferRenderbuffer(target, attachment, renderbuffertarget, nullableNativeHandle(renderbuffer))
1152
1191
  }
1153
- framebufferTextureLayer(_target: number, _attachment: number, _texture: NativeHandle | null, _level: number, _layer: number): void {}
1192
+ framebufferTextureLayer(target: number, attachment: number, texture: NativeHandle | null, level: number, layer: number): void {
1193
+ threeWebGLFramebufferTextureLayer(target, attachment, nullableNativeHandle(texture), level, layer)
1194
+ }
1154
1195
  drawBuffers(buffers: number[]): void { nativeWebGLDrawBuffers(buffers[0] ?? this.COLOR_ATTACHMENT0) }
1155
1196
 
1156
1197
  clearColor(r: number, g: number, b: number, a: number): void { nativeWebGLClearColor(r, g, b, a) }
@@ -1193,7 +1234,13 @@ export class NativeWebGL2RenderingContext {
1193
1234
  invalidateFramebuffer(_target: number, _attachments: BufferSourceLike): void {}
1194
1235
  blitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void { threeWebGLBlitFramebuffer(sx0,sy0,sx1,sy1,dx0,dy0,dx1,dy1,mask,filter) }
1195
1236
  readBuffer(_src: number): void {}
1196
- readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset = 0): void {
1237
+ // WebGL2 also reads into the bound PIXEL_PACK_BUFFER at a byte offset (three's
1238
+ // readRenderTargetPixelsAsync): the GPU copy is recorded without waiting for the GPU.
1239
+ readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination | number, offset = 0): void {
1240
+ if (typeof destination === 'number') {
1241
+ threeWebGLReadPixelsPack(x, y, width, height, format, type, destination)
1242
+ return
1243
+ }
1197
1244
  threeWebGLReadPixels(x, y, width, height, format, type, destination, offset)
1198
1245
  }
1199
1246
  copyTexImage2D(target: number, level: number, internalFormat: number, x: number, y: number, width: number, height: number, border: number): void {threeWebGLCopyTexImage2D(target,level,internalFormat,x,y,width,height,border);}
@@ -1202,7 +1249,9 @@ export class NativeWebGL2RenderingContext {
1202
1249
  clearBufferiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1203
1250
  clearBufferuiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
1204
1251
  getBufferParameter(_target: number, _pname: number): number { return 0 }
1205
- getBufferSubData(_target: number, _srcByteOffset: number, _destination: unknown): void {}
1252
+ getBufferSubData(target: number, srcByteOffset: number, destination: ReadPixelsDestination, dstOffset = 0, length = 0): void {
1253
+ threeWebGLGetBufferSubData(target, srcByteOffset, destination, dstOffset, length)
1254
+ }
1206
1255
  getFramebufferAttachmentParameter(_target: number, _attachment: number, _pname: number): number { return 0 }
1207
1256
  getRenderbufferParameter(_target: number, _pname: number): number { return 0 }
1208
1257
  getTexParameter(_target: number, _pname: number): number { return 0 }
@@ -1213,9 +1262,14 @@ export class NativeWebGL2RenderingContext {
1213
1262
  uniformBlockBinding(_program: NativeProgram | null, _uniformBlockIndex: number, _uniformBlockBinding: number): void {}
1214
1263
  bindBufferBase(_target: number, _index: number, _buffer: NativeHandle | null): void {}
1215
1264
  bindBufferRange(_target: number, _index: number, _buffer: NativeHandle | null, _offset: number, _size: number): void {}
1216
- fenceSync(_condition: number, _flags: number): null { return null }
1217
- clientWaitSync(_sync: NativeHandle | null, _flags: number, _timeout: number): number { return 0 }
1218
- deleteSync(_sync: NativeHandle | null): void {}
1265
+ fenceSync(_condition: number, _flags: number): NativeHandle | null {
1266
+ const sync = threeWebGLFenceSync()
1267
+ return sync ? createHandle('sync', sync) : null
1268
+ }
1269
+ clientWaitSync(sync: NativeHandle | null, flags: number, timeout: number): number {
1270
+ return sync ? threeWebGLClientWaitSync(nativeHandle(sync), flags, timeout) : this.WAIT_FAILED
1271
+ }
1272
+ deleteSync(sync: NativeHandle | null): void { if (sync) threeWebGLDeleteSync(nativeHandle(sync)) }
1219
1273
  isBuffer(_buffer: NativeHandle | null): boolean { return true }
1220
1274
  isFramebuffer(_framebuffer: NativeHandle | null): boolean { return true }
1221
1275
  isProgram(_program: NativeProgram | null): boolean { return true }