@geastack/native-webgl-angle 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/gea-native.json +16 -0
- package/geatsc-plugin-readback.mjs +21 -0
- package/geatsc-plugin-uniforms.mjs +214 -59
- package/geatsc-plugin-uploads.mjs +7 -2
- package/geatsc-plugin.mjs +912 -67
- package/native/angle_webgl_host.mm +253 -29
- package/native/audio_host.mm +26 -1
- package/native/win32_webgl_surface.cpp +230 -0
- package/package.json +6 -4
- package/src/inflate.ts +1 -1
- package/src/nativeWebGL.ts +67 -13
- package/src/nativeWebGLHost.ts +19 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#include "webgl_readback.h"
|
|
2
2
|
#if defined(_WIN32)
|
|
3
|
+
// The EGL and OpenGL ES libraries the Windows host loads beside the executable.
|
|
4
|
+
#ifndef GEA_WEBGL_EGL_LIBRARY
|
|
5
|
+
#define GEA_WEBGL_EGL_LIBRARY "libEGL.dll"
|
|
6
|
+
#endif
|
|
7
|
+
#ifndef GEA_WEBGL_GLES_LIBRARY
|
|
8
|
+
#define GEA_WEBGL_GLES_LIBRARY "libGLESv2.dll"
|
|
9
|
+
#endif
|
|
10
|
+
#endif
|
|
11
|
+
// GEA_WEBGL_STATIC_GLES: EGL/GLES are statically linked into the program, so
|
|
12
|
+
// nothing is loaded and entry points resolve through eglGetProcAddress.
|
|
13
|
+
#if defined(_WIN32)
|
|
3
14
|
#ifndef NOMINMAX
|
|
4
15
|
#define NOMINMAX
|
|
5
16
|
#endif
|
|
@@ -7,13 +18,14 @@
|
|
|
7
18
|
// Legacy Win16 macros are not C++ identifiers and must not leak into the runtime.
|
|
8
19
|
#undef near
|
|
9
20
|
#undef far
|
|
10
|
-
#
|
|
21
|
+
#elif defined(__APPLE__)
|
|
11
22
|
#include "gea/apple/native_bridge.h"
|
|
12
23
|
|
|
13
24
|
#import <AppKit/AppKit.h>
|
|
14
25
|
#import <QuartzCore/QuartzCore.h>
|
|
15
26
|
#import <dispatch/dispatch.h>
|
|
16
|
-
|
|
27
|
+
#endif
|
|
28
|
+
#if !defined(_WIN32) && !defined(GEA_WEBGL_STATIC_GLES)
|
|
17
29
|
#include <dlfcn.h>
|
|
18
30
|
#endif
|
|
19
31
|
// Typed uniform arguments use the compiler runtime on both native platforms.
|
|
@@ -172,7 +184,7 @@ extern "C" bool gea_three_key_pressed(double code) {
|
|
|
172
184
|
return gGeaKeyState[c];
|
|
173
185
|
}
|
|
174
186
|
|
|
175
|
-
#if defined(
|
|
187
|
+
#if !defined(__APPLE__)
|
|
176
188
|
// The Win32 window procedure owns input on Windows; it reports into the same
|
|
177
189
|
// tables the AppKit host view fills on macOS (keys as macOS virtual key codes).
|
|
178
190
|
extern "C" void gea_three_set_key_state(int code, bool down) {
|
|
@@ -189,7 +201,7 @@ extern "C" void gea_three_set_pointer_state(double x, double y, double width, do
|
|
|
189
201
|
}
|
|
190
202
|
#endif
|
|
191
203
|
|
|
192
|
-
#if
|
|
204
|
+
#if defined(__APPLE__)
|
|
193
205
|
@interface GeaAngleWebGLHostView : NSView
|
|
194
206
|
@end
|
|
195
207
|
|
|
@@ -410,6 +422,10 @@ using PFNGLTEXIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLi
|
|
|
410
422
|
using PFNGLTEXPARAMETERFPROC = void (*)(GLenum, GLenum, GLfloat);
|
|
411
423
|
using PFNGLTEXPARAMETERIPROC = void (*)(GLenum, GLenum, GLint);
|
|
412
424
|
using PFNGLTEXSUBIMAGE2DPROC = void (*)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void *);
|
|
425
|
+
using PFNGLTEXIMAGE3DPROC = void (*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *);
|
|
426
|
+
using PFNGLTEXSUBIMAGE3DPROC = void (*)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void *);
|
|
427
|
+
using PFNGLTEXSTORAGE3DPROC = void (*)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei);
|
|
428
|
+
using PFNGLFRAMEBUFFERTEXTURELAYERPROC = void (*)(GLenum, GLenum, GLuint, GLint, GLint);
|
|
413
429
|
using PFNGLTEXSTORAGE2DPROC = void (*)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);
|
|
414
430
|
using PFNGLUNIFORM1FPROC = void (*)(GLint, GLfloat);
|
|
415
431
|
using PFNGLUNIFORM1FVPROC = void (*)(GLint, GLsizei, const GLfloat *);
|
|
@@ -447,6 +463,13 @@ using PFNGLCOPYTEXIMAGE2DPROC=void(*)(GLenum,GLint,GLenum,GLint,GLint,GLsizei,GL
|
|
|
447
463
|
using PFNGLCOPYTEXSUBIMAGE2DPROC=void(*)(GLenum,GLint,GLint,GLint,GLint,GLint,GLsizei,GLsizei);
|
|
448
464
|
using PFNGLBLITFRAMEBUFFERPROC = void (*)(GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLbitfield,GLenum);
|
|
449
465
|
using PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC = void (*)(GLenum,GLsizei,GLenum,GLsizei,GLsizei);
|
|
466
|
+
using GLuint64 = std::uint64_t;
|
|
467
|
+
using GLsync = struct __GLsync *;
|
|
468
|
+
using PFNGLMAPBUFFERRANGEPROC = void *(*)(GLenum, GLintptr, GLsizeiptr, GLbitfield);
|
|
469
|
+
using PFNGLUNMAPBUFFERPROC = GLboolean (*)(GLenum);
|
|
470
|
+
using PFNGLFENCESYNCPROC = GLsync (*)(GLenum, GLbitfield);
|
|
471
|
+
using PFNGLCLIENTWAITSYNCPROC = GLenum (*)(GLsync, GLbitfield, GLuint64);
|
|
472
|
+
using PFNGLDELETESYNCPROC = void (*)(GLsync);
|
|
450
473
|
struct AngleWebGLState {
|
|
451
474
|
PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D=nullptr;
|
|
452
475
|
PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D=nullptr;
|
|
@@ -533,6 +556,12 @@ struct AngleWebGLState {
|
|
|
533
556
|
PFNGLPIXELSTOREIPROC glPixelStorei = nullptr;
|
|
534
557
|
PFNGLPOLYGONOFFSETPROC glPolygonOffset = nullptr;
|
|
535
558
|
PFNGLREADPIXELSPROC glReadPixels = nullptr;
|
|
559
|
+
// WebGL2 asynchronous readback: PIXEL_PACK_BUFFER reads, sync objects and read-mapping.
|
|
560
|
+
PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr;
|
|
561
|
+
PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr;
|
|
562
|
+
PFNGLFENCESYNCPROC glFenceSync = nullptr;
|
|
563
|
+
PFNGLCLIENTWAITSYNCPROC glClientWaitSync = nullptr;
|
|
564
|
+
PFNGLDELETESYNCPROC glDeleteSync = nullptr;
|
|
536
565
|
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
|
|
537
566
|
PFNGLSCISSORPROC glScissor = nullptr;
|
|
538
567
|
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
|
|
@@ -547,6 +576,12 @@ struct AngleWebGLState {
|
|
|
547
576
|
PFNGLTEXPARAMETERIPROC glTexParameteri = nullptr;
|
|
548
577
|
PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D = nullptr;
|
|
549
578
|
PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr;
|
|
579
|
+
// Array/3D entry points are optional: a GLES without them still starts,
|
|
580
|
+
// and uploads to those targets report GL_INVALID_OPERATION in the log.
|
|
581
|
+
PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr;
|
|
582
|
+
PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr;
|
|
583
|
+
PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr;
|
|
584
|
+
PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = nullptr;
|
|
550
585
|
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
|
|
551
586
|
PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
|
|
552
587
|
PFNGLUNIFORM1IPROC glUniform1i = nullptr;
|
|
@@ -579,7 +614,7 @@ struct AngleWebGLState {
|
|
|
579
614
|
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
|
|
580
615
|
PFNGLVIEWPORTPROC glViewport = nullptr;
|
|
581
616
|
|
|
582
|
-
#if defined(
|
|
617
|
+
#if !defined(__APPLE__)
|
|
583
618
|
void *nativeWindow = nullptr;
|
|
584
619
|
#else
|
|
585
620
|
NSView *hostView = nil;
|
|
@@ -617,7 +652,7 @@ static bool nativeShadowColorAuditEnabled() {
|
|
|
617
652
|
}
|
|
618
653
|
|
|
619
654
|
static void smokeLog(const char *format, ...) {
|
|
620
|
-
char message[
|
|
655
|
+
char message[16384] = {};
|
|
621
656
|
va_list args;
|
|
622
657
|
va_start(args, format);
|
|
623
658
|
std::vsnprintf(message, sizeof(message), format, args);
|
|
@@ -631,8 +666,13 @@ static void smokeLog(const char *format, ...) {
|
|
|
631
666
|
std::fprintf(stderr, "%s\n", message);
|
|
632
667
|
return;
|
|
633
668
|
}
|
|
634
|
-
#
|
|
669
|
+
#elif defined(__APPLE__)
|
|
635
670
|
if (!path || path[0] == '\0') path = "/tmp/three-angle-metal-smoke.log";
|
|
671
|
+
#else
|
|
672
|
+
if (!path || path[0] == '\0') {
|
|
673
|
+
std::fprintf(stderr, "%s\n", message);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
636
676
|
#endif
|
|
637
677
|
if (FILE *file = std::fopen(path, "a")) {
|
|
638
678
|
std::fprintf(file, "%s\n", message);
|
|
@@ -653,7 +693,7 @@ extern "C" void gea_three_angle_smoke_log(std::string message) {
|
|
|
653
693
|
// handler records the position in the host view's coordinate space with the
|
|
654
694
|
// same top-left-origin flip the per-view path used. Returning the event
|
|
655
695
|
// unmodified keeps normal dispatch (clicks, first responder) intact.
|
|
656
|
-
#if
|
|
696
|
+
#if defined(__APPLE__)
|
|
657
697
|
static id gGeaPointerMonitor = nil;
|
|
658
698
|
static void geaInstallPointerMonitor(void) {
|
|
659
699
|
if (gGeaPointerMonitor) return;
|
|
@@ -696,9 +736,16 @@ static void *openFirst(const char *envName, const char *const *paths) {
|
|
|
696
736
|
|
|
697
737
|
#endif
|
|
698
738
|
|
|
739
|
+
#if defined(GEA_WEBGL_STATIC_GLES)
|
|
740
|
+
extern "C" void *eglGetProcAddress(const char *name);
|
|
741
|
+
#endif
|
|
699
742
|
template <typename Fn>
|
|
700
743
|
static Fn loadSymbol(void *library, const char *name) {
|
|
701
|
-
#if defined(
|
|
744
|
+
#if defined(GEA_WEBGL_STATIC_GLES)
|
|
745
|
+
(void)library;
|
|
746
|
+
if (std::strcmp(name, "eglGetProcAddress") == 0) return reinterpret_cast<Fn>(&eglGetProcAddress);
|
|
747
|
+
return reinterpret_cast<Fn>(eglGetProcAddress(name));
|
|
748
|
+
#elif defined(_WIN32)
|
|
702
749
|
return reinterpret_cast<Fn>(GetProcAddress(static_cast<HMODULE>(library), name));
|
|
703
750
|
#else
|
|
704
751
|
return reinterpret_cast<Fn>(dlsym(library, name));
|
|
@@ -797,7 +844,7 @@ static void logEglFailure(const char *step) {
|
|
|
797
844
|
std::fprintf(stderr, "[three-angle-metal] %s failed, EGL error 0x%04x\n", step, lastEglError());
|
|
798
845
|
}
|
|
799
846
|
|
|
800
|
-
#if
|
|
847
|
+
#if defined(__APPLE__)
|
|
801
848
|
static CGFloat backingScaleForHostView(NSView *nativeView) {
|
|
802
849
|
CGFloat scale = nativeView.window ? nativeView.window.backingScaleFactor : 0.0;
|
|
803
850
|
if (scale <= 0.0 && nativeView.window.screen) scale = nativeView.window.screen.backingScaleFactor;
|
|
@@ -870,13 +917,13 @@ extern "C" void gea_three_webgl_update_size_uwp(double width, double height, dou
|
|
|
870
917
|
}
|
|
871
918
|
#endif
|
|
872
919
|
|
|
873
|
-
#if defined(
|
|
920
|
+
#if !defined(__APPLE__)
|
|
874
921
|
// The UWP frame driver owns its EGL context exclusively until it returns to
|
|
875
922
|
// CoreWindow event dispatch. Other hosts retain per-call binding semantics.
|
|
876
923
|
static thread_local bool gUwpFrameContextCurrent = false;
|
|
877
924
|
#endif
|
|
878
925
|
static bool makeCurrent(const char *label) {
|
|
879
|
-
#if defined(
|
|
926
|
+
#if !defined(__APPLE__)
|
|
880
927
|
if (gUwpFrameContextCurrent) return true;
|
|
881
928
|
#endif
|
|
882
929
|
if (!gWebGL.ready && gWebGL.context == EGL_NO_CONTEXT) return false;
|
|
@@ -907,7 +954,7 @@ static bool makeCurrent(const char *label) {
|
|
|
907
954
|
return true;
|
|
908
955
|
}
|
|
909
956
|
|
|
910
|
-
#if defined(
|
|
957
|
+
#if !defined(__APPLE__)
|
|
911
958
|
extern "C" bool gea_three_webgl_begin_frame_uwp() {
|
|
912
959
|
// Always rebind after the dispatcher, where arbitrary platform callbacks
|
|
913
960
|
// may have used a different context. No such callbacks run within the frame.
|
|
@@ -921,7 +968,7 @@ extern "C" void gea_three_webgl_end_frame_uwp() {
|
|
|
921
968
|
}
|
|
922
969
|
#endif
|
|
923
970
|
|
|
924
|
-
#if
|
|
971
|
+
#if defined(__APPLE__)
|
|
925
972
|
static bool rebuildWindowSurface(const char *label) {
|
|
926
973
|
if (!gWebGL.eglCreateWindowSurface || !gWebGL.eglDestroySurface || !gWebGL.eglMakeCurrent ||
|
|
927
974
|
!gWebGL.metalLayer || !gWebGL.config || gWebGL.context == EGL_NO_CONTEXT) {
|
|
@@ -1029,6 +1076,11 @@ static bool loadGlSymbols() {
|
|
|
1029
1076
|
LOAD_GL(glPixelStorei, PFNGLPIXELSTOREIPROC)
|
|
1030
1077
|
LOAD_GL(glPolygonOffset, PFNGLPOLYGONOFFSETPROC)
|
|
1031
1078
|
LOAD_GL(glReadPixels, PFNGLREADPIXELSPROC)
|
|
1079
|
+
LOAD_GL(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC)
|
|
1080
|
+
LOAD_GL(glUnmapBuffer, PFNGLUNMAPBUFFERPROC)
|
|
1081
|
+
LOAD_GL(glFenceSync, PFNGLFENCESYNCPROC)
|
|
1082
|
+
LOAD_GL(glClientWaitSync, PFNGLCLIENTWAITSYNCPROC)
|
|
1083
|
+
LOAD_GL(glDeleteSync, PFNGLDELETESYNCPROC)
|
|
1032
1084
|
LOAD_GL(glCopyTexImage2D,PFNGLCOPYTEXIMAGE2DPROC)
|
|
1033
1085
|
LOAD_GL(glCopyTexSubImage2D,PFNGLCOPYTEXSUBIMAGE2DPROC)
|
|
1034
1086
|
LOAD_GL(glBlitFramebuffer, PFNGLBLITFRAMEBUFFERPROC)
|
|
@@ -1047,6 +1099,10 @@ static bool loadGlSymbols() {
|
|
|
1047
1099
|
LOAD_GL(glTexParameteri, PFNGLTEXPARAMETERIPROC)
|
|
1048
1100
|
LOAD_GL(glTexSubImage2D, PFNGLTEXSUBIMAGE2DPROC)
|
|
1049
1101
|
LOAD_GL(glTexStorage2D, PFNGLTEXSTORAGE2DPROC)
|
|
1102
|
+
gWebGL.glTexImage3D = loadGl<PFNGLTEXIMAGE3DPROC>("glTexImage3D");
|
|
1103
|
+
gWebGL.glTexSubImage3D = loadGl<PFNGLTEXSUBIMAGE3DPROC>("glTexSubImage3D");
|
|
1104
|
+
gWebGL.glTexStorage3D = loadGl<PFNGLTEXSTORAGE3DPROC>("glTexStorage3D");
|
|
1105
|
+
gWebGL.glFramebufferTextureLayer = loadGl<PFNGLFRAMEBUFFERTEXTURELAYERPROC>("glFramebufferTextureLayer");
|
|
1050
1106
|
LOAD_GL(glUniform1f, PFNGLUNIFORM1FPROC)
|
|
1051
1107
|
LOAD_GL(glUniform1fv, PFNGLUNIFORM1FVPROC)
|
|
1052
1108
|
LOAD_GL(glUniform1i, PFNGLUNIFORM1IPROC)
|
|
@@ -1082,7 +1138,7 @@ static bool loadGlSymbols() {
|
|
|
1082
1138
|
return true;
|
|
1083
1139
|
}
|
|
1084
1140
|
|
|
1085
|
-
#if defined(
|
|
1141
|
+
#if !defined(__APPLE__)
|
|
1086
1142
|
extern "C" bool gea_three_webgl_attach_uwp(
|
|
1087
1143
|
void *nativeWindow,
|
|
1088
1144
|
#else
|
|
@@ -1097,24 +1153,50 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1097
1153
|
smokeLog("[three-angle-metal] WebGL host attach requested");
|
|
1098
1154
|
if (gWebGL.ready) return true;
|
|
1099
1155
|
|
|
1100
|
-
#if defined(
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
|
|
1156
|
+
#if defined(GEA_WEBGL_STATIC_GLES)
|
|
1157
|
+
// Linked into the program: nothing to load; loadSymbol resolves through
|
|
1158
|
+
// eglGetProcAddress. The handles only need to be non-null.
|
|
1159
|
+
static int staticGles;
|
|
1160
|
+
gWebGL.egl = &staticGles;
|
|
1161
|
+
gWebGL.gles = &staticGles;
|
|
1162
|
+
#elif defined(_WIN32)
|
|
1163
|
+
// An override path from the environment first, then the packaged library of
|
|
1164
|
+
// a UWP app, then the file beside the executable. The file names default to
|
|
1165
|
+
// ANGLE's; a build that stages another EGL/GLES implementation names its
|
|
1166
|
+
// files through GEA_WEBGL_EGL_LIBRARY / GEA_WEBGL_GLES_LIBRARY (paths
|
|
1167
|
+
// relative to the executable's directory; one DLL may implement both).
|
|
1168
|
+
auto openWin32 = [](const wchar_t *overrideEnv, const char *name) -> HMODULE {
|
|
1104
1169
|
wchar_t overridePath[MAX_PATH];
|
|
1105
1170
|
const DWORD n = GetEnvironmentVariableW(overrideEnv, overridePath, MAX_PATH);
|
|
1106
1171
|
if (n > 0 && n < MAX_PATH) {
|
|
1107
1172
|
if (HMODULE m = LoadLibraryW(overridePath)) return m;
|
|
1173
|
+
smokeLog("[native-webgl-angle] %ls names a library that does not load (error %lu)", overrideEnv, GetLastError());
|
|
1108
1174
|
}
|
|
1175
|
+
std::wstring file;
|
|
1176
|
+
for (const char *c = name; *c; ++c) file.push_back(*c == '/' ? L'\\' : static_cast<wchar_t>(static_cast<unsigned char>(*c)));
|
|
1109
1177
|
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP
|
|
1110
|
-
return LoadPackagedLibrary(
|
|
1178
|
+
return LoadPackagedLibrary(file.c_str(), 0);
|
|
1111
1179
|
#else
|
|
1112
|
-
|
|
1180
|
+
wchar_t module[MAX_PATH] = {};
|
|
1181
|
+
const DWORD length = GetModuleFileNameW(nullptr, module, MAX_PATH);
|
|
1182
|
+
std::wstring beside(module, length < MAX_PATH ? length : 0);
|
|
1183
|
+
const size_t slash = beside.find_last_of(L"\\/");
|
|
1184
|
+
if (slash != std::wstring::npos) {
|
|
1185
|
+
beside.resize(slash + 1);
|
|
1186
|
+
beside += file;
|
|
1187
|
+
// Altered search path: the library's own dependencies resolve from its directory.
|
|
1188
|
+
if (HMODULE m = LoadLibraryExW(beside.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH)) return m;
|
|
1189
|
+
}
|
|
1190
|
+
return LoadLibraryW(file.c_str());
|
|
1113
1191
|
#endif
|
|
1114
1192
|
};
|
|
1115
|
-
gWebGL.egl = openWin32(L"GEA_ANGLE_EGL_DYLIB",
|
|
1116
|
-
gWebGL.gles = openWin32(L"GEA_ANGLE_GLES_DYLIB",
|
|
1117
|
-
|
|
1193
|
+
gWebGL.egl = openWin32(L"GEA_ANGLE_EGL_DYLIB", GEA_WEBGL_EGL_LIBRARY);
|
|
1194
|
+
gWebGL.gles = openWin32(L"GEA_ANGLE_GLES_DYLIB", GEA_WEBGL_GLES_LIBRARY);
|
|
1195
|
+
if (!gWebGL.egl || !gWebGL.gles) {
|
|
1196
|
+
smokeLog("[native-webgl-angle] EGL/GLES implementation not found: %s and %s beside the executable "
|
|
1197
|
+
"(GEA_ANGLE_EGL_DYLIB / GEA_ANGLE_GLES_DYLIB override them)", GEA_WEBGL_EGL_LIBRARY, GEA_WEBGL_GLES_LIBRARY);
|
|
1198
|
+
}
|
|
1199
|
+
#elif defined(__APPLE__)
|
|
1118
1200
|
static const char *const eglPaths[] = {
|
|
1119
1201
|
"/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
|
1120
1202
|
"/Applications/Cursor.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
|
@@ -1132,6 +1214,8 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1132
1214
|
|
|
1133
1215
|
gWebGL.egl = openFirst("GEA_ANGLE_EGL_DYLIB", eglPaths);
|
|
1134
1216
|
gWebGL.gles = openFirst("GEA_ANGLE_GLES_DYLIB", glesPaths);
|
|
1217
|
+
#else
|
|
1218
|
+
#error "load libEGL/libGLESv2 for this platform, or build with GEA_WEBGL_STATIC_GLES"
|
|
1135
1219
|
#endif
|
|
1136
1220
|
if (!gWebGL.egl || !gWebGL.gles) {
|
|
1137
1221
|
smokeLog("[three-angle-metal] ANGLE libEGL/libGLESv2 were not found");
|
|
@@ -1209,7 +1293,7 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1209
1293
|
}
|
|
1210
1294
|
gWebGL.config = config;
|
|
1211
1295
|
|
|
1212
|
-
#if defined(
|
|
1296
|
+
#if !defined(__APPLE__)
|
|
1213
1297
|
gWebGL.nativeWindow = nativeWindow;
|
|
1214
1298
|
gea_three_webgl_update_size_uwp(width, height, devicePixelRatio);
|
|
1215
1299
|
if (gWebGL.eglCreateWindowSurface) {
|
|
@@ -1275,7 +1359,7 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1275
1359
|
|
|
1276
1360
|
gWebGL.ready = true;
|
|
1277
1361
|
gWebGL.glViewport(0, 0, gWebGL.widthPx, gWebGL.heightPx);
|
|
1278
|
-
#if
|
|
1362
|
+
#if defined(__APPLE__)
|
|
1279
1363
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1280
1364
|
if (gWebGL.ready && rebuildWindowSurface("eglCreateWindowSurface(deferred)")) {
|
|
1281
1365
|
smokeLog("[three-angle-metal] WebGL EGL surface rebound at %dx%d", gWebGL.widthPx, gWebGL.heightPx);
|
|
@@ -1391,7 +1475,7 @@ extern "C" void gea_three_webgl_swap() {
|
|
|
1391
1475
|
// TEMP-DIAG: GEA_THREE_VIEW_DUMP=1 — at frame 60, log the window's full
|
|
1392
1476
|
// subview tree and a hit-test grid so overlay views that would swallow
|
|
1393
1477
|
// mouse events are visible without Accessibility permission.
|
|
1394
|
-
#if
|
|
1478
|
+
#if defined(__APPLE__)
|
|
1395
1479
|
if (std::getenv("GEA_THREE_VIEW_DUMP")) {
|
|
1396
1480
|
static int viewDumpCount = 0;
|
|
1397
1481
|
if (++viewDumpCount == 60) {
|
|
@@ -1424,6 +1508,16 @@ extern "C" void gea_three_webgl_swap() {
|
|
|
1424
1508
|
}
|
|
1425
1509
|
}
|
|
1426
1510
|
#endif
|
|
1511
|
+
// The debug dump and recording read the window into client memory. A PIXEL_PACK_BUFFER the app left
|
|
1512
|
+
// bound (WebGL2 asynchronous readback) would make the pointer a buffer offset instead, so the read
|
|
1513
|
+
// runs with it unbound and the binding is restored afterwards.
|
|
1514
|
+
const auto readWindowRgba = [](int w, int h, unsigned char *rgba) {
|
|
1515
|
+
GLint packBuffer = 0;
|
|
1516
|
+
if (gWebGL.glGetIntegerv) gWebGL.glGetIntegerv(0x88ED /* PIXEL_PACK_BUFFER_BINDING */, &packBuffer);
|
|
1517
|
+
if (packBuffer) gWebGL.glBindBuffer(0x88EB /* PIXEL_PACK_BUFFER */, 0);
|
|
1518
|
+
gWebGL.glReadPixels(0, 0, w, h, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba);
|
|
1519
|
+
if (packBuffer) gWebGL.glBindBuffer(0x88EB, static_cast<GLuint>(packBuffer));
|
|
1520
|
+
};
|
|
1427
1521
|
// Debug frame dump: GEA_WEBGL_DUMP_FRAME=<path-prefix> writes the full
|
|
1428
1522
|
// framebuffer of frames 90/180/300 as PPM (P6) — visual ground truth for
|
|
1429
1523
|
// native-vs-browser comparisons without needing a window screenshot.
|
|
@@ -1434,7 +1528,7 @@ extern "C" void gea_three_webgl_swap() {
|
|
|
1434
1528
|
const int w = gWebGL.widthPx;
|
|
1435
1529
|
const int h = gWebGL.heightPx;
|
|
1436
1530
|
std::vector<unsigned char> rgba(static_cast<size_t>(w) * h * 4);
|
|
1437
|
-
|
|
1531
|
+
readWindowRgba(w, h, rgba.data());
|
|
1438
1532
|
char path[512];
|
|
1439
1533
|
std::snprintf(path, sizeof path, "%s-f%d.ppm", dumpPrefix, dumpCount);
|
|
1440
1534
|
if (FILE *file = std::fopen(path, "wb")) {
|
|
@@ -1483,7 +1577,7 @@ extern "C" void gea_three_webgl_swap() {
|
|
|
1483
1577
|
if (due > recordWritten && gWebGL.widthPx == recordW && gWebGL.heightPx == recordH) {
|
|
1484
1578
|
std::vector<unsigned char> rgba(static_cast<size_t>(recordW) * recordH * 4);
|
|
1485
1579
|
std::vector<unsigned char> rgb(static_cast<size_t>(recordW) * recordH * 3);
|
|
1486
|
-
|
|
1580
|
+
readWindowRgba(recordW, recordH, rgba.data());
|
|
1487
1581
|
for (int y = 0; y < recordH; ++y) {
|
|
1488
1582
|
const unsigned char *src = rgba.data() + static_cast<size_t>(recordH - 1 - y) * recordW * 4;
|
|
1489
1583
|
unsigned char *dst = rgb.data() + static_cast<size_t>(y) * recordW * 3;
|
|
@@ -2498,6 +2592,36 @@ extern "C" void gea_three_webgl_tex_image_2d_bytes(double target, double level,
|
|
|
2498
2592
|
gWebGL.glTexImage2D(asGLenum(target), asGLint(level), asGLint(internalFormat), asGLsizei(width), asGLsizei(height), 0, asGLenum(format), asGLenum(type), bytes);
|
|
2499
2593
|
}
|
|
2500
2594
|
|
|
2595
|
+
static bool requireGl(const void *fn, const char *name) {
|
|
2596
|
+
if (fn) return true;
|
|
2597
|
+
smokeLog("[native-webgl] %s is not provided by this GLES; the call is dropped", name);
|
|
2598
|
+
return false;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
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) {
|
|
2602
|
+
GEA_WEBGL_VOID("texImage3DBytes");
|
|
2603
|
+
if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexImage3D), "glTexImage3D")) return;
|
|
2604
|
+
gWebGL.glTexImage3D(asGLenum(target), asGLint(level), asGLint(internalFormat), asGLsizei(width), asGLsizei(height), asGLsizei(depth), 0, asGLenum(format), asGLenum(type), bytes);
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
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) {
|
|
2608
|
+
GEA_WEBGL_VOID("texSubImage3DBytes");
|
|
2609
|
+
if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexSubImage3D), "glTexSubImage3D")) return;
|
|
2610
|
+
gWebGL.glTexSubImage3D(asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset), asGLint(zoffset), asGLsizei(width), asGLsizei(height), asGLsizei(depth), asGLenum(format), asGLenum(type), bytes);
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
extern "C" void gea_three_webgl_tex_storage_3d(double target, double levels, double internalFormat, double width, double height, double depth) {
|
|
2614
|
+
GEA_WEBGL_VOID("texStorage3D");
|
|
2615
|
+
if (!requireGl(reinterpret_cast<const void *>(gWebGL.glTexStorage3D), "glTexStorage3D")) return;
|
|
2616
|
+
gWebGL.glTexStorage3D(asGLenum(target), asGLsizei(std::max(1.0, levels)), asGLenum(internalFormat), asGLsizei(width), asGLsizei(height), asGLsizei(depth));
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
extern "C" void gea_three_webgl_framebuffer_texture_layer(double target, double attachment, double texture, double level, double layer) {
|
|
2620
|
+
GEA_WEBGL_VOID("framebufferTextureLayer");
|
|
2621
|
+
if (!requireGl(reinterpret_cast<const void *>(gWebGL.glFramebufferTextureLayer), "glFramebufferTextureLayer")) return;
|
|
2622
|
+
gWebGL.glFramebufferTextureLayer(asGLenum(target), asGLenum(attachment), asGLuint(texture), asGLint(level), asGLint(layer));
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2501
2625
|
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
2626
|
GEA_WEBGL_VOID("texSubImage2DBytes");
|
|
2503
2627
|
gWebGL.glTexSubImage2D(asGLenum(target), asGLint(level), asGLint(xoffset), asGLint(yoffset), asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type), bytes);
|
|
@@ -2805,6 +2929,106 @@ extern "C" void gea_three_webgl_read_pixels_bytes(double x, double y, double wid
|
|
|
2805
2929
|
gWebGL.glReadPixels(asGLint(x),asGLint(y),asGLsizei(width),asGLsizei(height),asGLenum(format),asGLenum(type),bytes);
|
|
2806
2930
|
}
|
|
2807
2931
|
|
|
2932
|
+
// WebGL2 asynchronous readback (three's readRenderTargetPixelsAsync): readPixels into the bound
|
|
2933
|
+
// PIXEL_PACK_BUFFER at a byte offset, a fence, polling, then getBufferSubData through a read mapping.
|
|
2934
|
+
extern "C" void gea_three_webgl_read_pixels_pack(double x, double y, double width, double height, double format, double type, double offset) {
|
|
2935
|
+
GEA_WEBGL_VOID("readPixels");
|
|
2936
|
+
GLint packBuffer = 0;
|
|
2937
|
+
gWebGL.glGetIntegerv(0x88ED, &packBuffer);
|
|
2938
|
+
if (!packBuffer || !std::isfinite(offset) || offset < 0 || std::floor(offset) != offset) { if (!hostReadbackError) hostReadbackError = 0x0502; return; }
|
|
2939
|
+
gWebGL.glReadPixels(asGLint(x), asGLint(y), asGLsizei(width), asGLsizei(height), asGLenum(format), asGLenum(type),
|
|
2940
|
+
reinterpret_cast<void *>(static_cast<std::uintptr_t>(offset)));
|
|
2941
|
+
}
|
|
2942
|
+
static std::unordered_map<std::uint32_t, GLsync> gWebGLSyncs;
|
|
2943
|
+
static std::uint32_t gWebGLNextSync = 1;
|
|
2944
|
+
extern "C" double gea_three_webgl_fence_sync() {
|
|
2945
|
+
GEA_WEBGL_DOUBLE("fenceSync");
|
|
2946
|
+
GLsync sync = gWebGL.glFenceSync(0x9117 /* SYNC_GPU_COMMANDS_COMPLETE */, 0);
|
|
2947
|
+
if (!sync) return 0.0;
|
|
2948
|
+
const std::uint32_t id = gWebGLNextSync++;
|
|
2949
|
+
gWebGLSyncs[id] = sync;
|
|
2950
|
+
return static_cast<double>(id);
|
|
2951
|
+
}
|
|
2952
|
+
extern "C" double gea_three_webgl_client_wait_sync(double sync, double flags, double timeout) {
|
|
2953
|
+
GEA_WEBGL_DOUBLE("clientWaitSync");
|
|
2954
|
+
auto it = gWebGLSyncs.find(static_cast<std::uint32_t>(sync));
|
|
2955
|
+
if (it == gWebGLSyncs.end()) return 0x911D; // WAIT_FAILED
|
|
2956
|
+
return static_cast<double>(gWebGL.glClientWaitSync(it->second, static_cast<GLbitfield>(flags), static_cast<GLuint64>(std::max(0.0, timeout))));
|
|
2957
|
+
}
|
|
2958
|
+
extern "C" void gea_three_webgl_delete_sync(double sync) {
|
|
2959
|
+
GEA_WEBGL_VOID("deleteSync");
|
|
2960
|
+
auto it = gWebGLSyncs.find(static_cast<std::uint32_t>(sync));
|
|
2961
|
+
if (it == gWebGLSyncs.end()) return;
|
|
2962
|
+
gWebGL.glDeleteSync(it->second);
|
|
2963
|
+
gWebGLSyncs.erase(it);
|
|
2964
|
+
}
|
|
2965
|
+
// GPU timers for the app's frame profiler, on GL_EXT_disjoint_timer_query.
|
|
2966
|
+
struct HostGpuTimers {
|
|
2967
|
+
bool initialized = false, supported = false;
|
|
2968
|
+
void (*gen)(GLsizei, GLuint *) = nullptr;
|
|
2969
|
+
void (*remove)(GLsizei, const GLuint *) = nullptr;
|
|
2970
|
+
void (*begin)(GLenum, GLuint) = nullptr;
|
|
2971
|
+
void (*end)(GLenum) = nullptr;
|
|
2972
|
+
void (*available)(GLuint, GLenum, GLuint *) = nullptr;
|
|
2973
|
+
void (*result)(GLuint, GLenum, std::uint64_t *) = nullptr;
|
|
2974
|
+
bool open = false;
|
|
2975
|
+
bool ensure() {
|
|
2976
|
+
if (initialized) return supported;
|
|
2977
|
+
initialized = true;
|
|
2978
|
+
const auto extensions = reinterpret_cast<const char *>(gWebGL.glGetString(0x1F03));
|
|
2979
|
+
const std::string list = " " + std::string(extensions ? extensions : "") + " ";
|
|
2980
|
+
if (list.find(" GL_EXT_disjoint_timer_query ") == std::string::npos) return false;
|
|
2981
|
+
gen = loadGl<decltype(gen)>("glGenQueriesEXT");
|
|
2982
|
+
remove = loadGl<decltype(remove)>("glDeleteQueriesEXT");
|
|
2983
|
+
begin = loadGl<decltype(begin)>("glBeginQueryEXT");
|
|
2984
|
+
end = loadGl<decltype(end)>("glEndQueryEXT");
|
|
2985
|
+
available = loadGl<decltype(available)>("glGetQueryObjectuivEXT");
|
|
2986
|
+
result = loadGl<decltype(result)>("glGetQueryObjectui64vEXT");
|
|
2987
|
+
supported = gen && remove && begin && end && available && result;
|
|
2988
|
+
return supported;
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
static HostGpuTimers gHostGpuTimers;
|
|
2992
|
+
extern "C" double gea_three_webgl_timer_begin() {
|
|
2993
|
+
GEA_WEBGL_DOUBLE("timerBegin");
|
|
2994
|
+
auto &t = gHostGpuTimers;
|
|
2995
|
+
if (!t.ensure() || t.open) return 0.0;
|
|
2996
|
+
GLuint query = 0;
|
|
2997
|
+
t.gen(1, &query);
|
|
2998
|
+
if (!query) return 0.0;
|
|
2999
|
+
t.begin(0x88BF /* TIME_ELAPSED_EXT */, query);
|
|
3000
|
+
t.open = true;
|
|
3001
|
+
return static_cast<double>(query);
|
|
3002
|
+
}
|
|
3003
|
+
extern "C" void gea_three_webgl_timer_end() {
|
|
3004
|
+
GEA_WEBGL_VOID("timerEnd");
|
|
3005
|
+
auto &t = gHostGpuTimers;
|
|
3006
|
+
if (!t.supported || !t.open) return;
|
|
3007
|
+
t.end(0x88BF);
|
|
3008
|
+
t.open = false;
|
|
3009
|
+
}
|
|
3010
|
+
extern "C" double gea_three_webgl_timer_result(double timer) {
|
|
3011
|
+
GEA_WEBGL_DOUBLE("timerResult");
|
|
3012
|
+
auto &t = gHostGpuTimers;
|
|
3013
|
+
const GLuint query = static_cast<GLuint>(timer);
|
|
3014
|
+
if (!t.supported || !query) return 0.0;
|
|
3015
|
+
GLuint ready = 0;
|
|
3016
|
+
t.available(query, 0x8867 /* QUERY_RESULT_AVAILABLE */, &ready);
|
|
3017
|
+
if (!ready) return -1.0;
|
|
3018
|
+
std::uint64_t ns = 0;
|
|
3019
|
+
t.result(query, 0x8866 /* QUERY_RESULT */, &ns);
|
|
3020
|
+
t.remove(1, &query);
|
|
3021
|
+
return static_cast<double>(ns);
|
|
3022
|
+
}
|
|
3023
|
+
extern "C" void gea_three_webgl_get_buffer_sub_data_bytes(double target, double srcOffset, void *bytes, double byteLength) {
|
|
3024
|
+
GEA_WEBGL_VOID("getBufferSubData");
|
|
3025
|
+
if (!bytes || !(byteLength > 0) || !(srcOffset >= 0)) { if (!hostReadbackError) hostReadbackError = 0x0501; return; }
|
|
3026
|
+
void *mapped = gWebGL.glMapBufferRange(asGLenum(target), static_cast<GLintptr>(srcOffset), static_cast<GLsizeiptr>(byteLength), 0x0001 /* MAP_READ_BIT */);
|
|
3027
|
+
if (!mapped) { if (!hostReadbackError) hostReadbackError = 0x0502; return; }
|
|
3028
|
+
std::memcpy(bytes, mapped, static_cast<std::size_t>(byteLength));
|
|
3029
|
+
gWebGL.glUnmapBuffer(asGLenum(target));
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2808
3032
|
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
3033
|
GEA_WEBGL_VOID("blitFramebuffer");
|
|
2810
3034
|
gWebGL.glBlitFramebuffer(asGLint(sx0),asGLint(sy0),asGLint(sx1),asGLint(sy1),asGLint(dx0),asGLint(dy0),asGLint(dx1),asGLint(dy1),asGLenum(mask),asGLenum(filter));
|
package/native/audio_host.mm
CHANGED
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
#if defined(_WIN32)
|
|
22
22
|
#define _USE_MATH_DEFINES
|
|
23
23
|
#include "audio_uwp.h"
|
|
24
|
+
// Legacy Win16 macros are not C++ identifiers and must not leak into the runtime.
|
|
25
|
+
#undef near
|
|
26
|
+
#undef far
|
|
24
27
|
#else
|
|
25
28
|
#import <AVFoundation/AVFoundation.h>
|
|
26
29
|
#import <Foundation/Foundation.h>
|
|
@@ -44,8 +47,30 @@
|
|
|
44
47
|
|
|
45
48
|
#if defined(_WIN32)
|
|
46
49
|
static std::wstring geaAudioAssetDirectory;
|
|
50
|
+
static bool geaAudioAssetDirectorySet = false;
|
|
47
51
|
extern "C" void gea_three_audio_set_directory_uwp(const wchar_t *path) {
|
|
48
52
|
geaAudioAssetDirectory = path ? path : L"";
|
|
53
|
+
geaAudioAssetDirectorySet = true;
|
|
54
|
+
}
|
|
55
|
+
// Without a driver naming the directory: GEA_THREE_AUDIO_DIR, else the
|
|
56
|
+
// Resources\Sounds folder beside the executable (where the desktop target
|
|
57
|
+
// stages an app's sounds), matching the macOS host's bundle lookup.
|
|
58
|
+
static const std::wstring &geaAudioDirectory() {
|
|
59
|
+
if (!geaAudioAssetDirectorySet) {
|
|
60
|
+
geaAudioAssetDirectorySet = true;
|
|
61
|
+
wchar_t value[MAX_PATH] = {};
|
|
62
|
+
const DWORD n = GetEnvironmentVariableW(L"GEA_THREE_AUDIO_DIR", value, MAX_PATH);
|
|
63
|
+
if (n > 0 && n < MAX_PATH) {
|
|
64
|
+
geaAudioAssetDirectory = value;
|
|
65
|
+
} else {
|
|
66
|
+
wchar_t module[MAX_PATH] = {};
|
|
67
|
+
const DWORD length = GetModuleFileNameW(nullptr, module, MAX_PATH);
|
|
68
|
+
std::wstring directory(module, length < MAX_PATH ? length : 0);
|
|
69
|
+
const size_t slash = directory.find_last_of(L"\\/");
|
|
70
|
+
if (slash != std::wstring::npos) geaAudioAssetDirectory = directory.substr(0, slash) + L"\\Resources\\Sounds";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return geaAudioAssetDirectory;
|
|
49
74
|
}
|
|
50
75
|
#endif
|
|
51
76
|
|
|
@@ -427,7 +452,7 @@ struct GeaAudioEngine {
|
|
|
427
452
|
long loadBufferFile(const std::string &name) {
|
|
428
453
|
if (name.empty() || name.find('/') != std::string::npos || name.find("..") != std::string::npos) return 0;
|
|
429
454
|
#if defined(_WIN32)
|
|
430
|
-
if (name.find('\\') != std::string::npos ||
|
|
455
|
+
if (name.find('\\') != std::string::npos || geaAudioDirectory().empty()) return 0;
|
|
431
456
|
const int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name.data(), static_cast<int>(name.size()), nullptr, 0);
|
|
432
457
|
if (length <= 0) return 0;
|
|
433
458
|
std::wstring base(static_cast<size_t>(length), L'\0');
|