@geastack/native-webgl-angle 0.1.13 → 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/native/angle_webgl_host.mm +36 -7
- package/native/audio_host.mm +26 -1
- package/native/win32_webgl_surface.cpp +230 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -50,9 +50,36 @@ native/angle_webgl_host.mm
|
|
|
50
50
|
|
|
51
51
|
## Native sources in apps
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
Apple apps name the host in `gea.nativeSources`, either app-relative or by
|
|
54
|
+
package path (`@geastack/native-webgl-angle/native/angle_webgl_host.mm`).
|
|
55
|
+
|
|
56
|
+
The Windows desktop target needs nothing in the app: `gea-native.json`
|
|
57
|
+
declares the host (`native/angle_webgl_host.mm` and `native/audio_host.mm`,
|
|
58
|
+
compiled as C++) and `native/win32_webgl_surface.cpp`, a host extension that
|
|
59
|
+
gives the program a child window filling the main window's content area,
|
|
60
|
+
attaches the WebGL context to it before the program's top level runs, makes
|
|
61
|
+
the context current around initialization and each frame, hands frame pacing
|
|
62
|
+
to the presentation, and reports keys (as macOS virtual key codes, with a
|
|
63
|
+
release held until a frame has seen the key down) and the pointer (in CSS
|
|
64
|
+
pixels).
|
|
65
|
+
|
|
66
|
+
The package requires a `gles` provider: an EGL and OpenGL ES 3
|
|
67
|
+
implementation. The app configures it, for example
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
"gea": { "targets": { "windows": { "providers": {
|
|
71
|
+
"gles": { "directory": "build/renderer", "files": { "egl": "GL.dll", "gles": "GL.dll" }, "runtimeFiles": ["d3d12/D3D12Core.dll"] }
|
|
72
|
+
} } } }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
or `"gles": "path/to/angle"` for a directory holding ANGLE's `libEGL.dll` and
|
|
76
|
+
`libGLESv2.dll`. The build stages those files beside the executable and
|
|
77
|
+
compiles their names in (`GEA_WEBGL_EGL_LIBRARY`, `GEA_WEBGL_GLES_LIBRARY`);
|
|
78
|
+
the host loads them from the executable's directory. `GEA_ANGLE_EGL_DYLIB` and
|
|
79
|
+
`GEA_ANGLE_GLES_DYLIB` still override the paths at run time. Without a
|
|
80
|
+
provider the build stops and says so. Audio files resolve from
|
|
81
|
+
`GEA_THREE_AUDIO_DIR`, else `Resources\Sounds` beside the executable, unless
|
|
82
|
+
the app names a directory with `gea_three_audio_set_directory_uwp`.
|
|
56
83
|
|
|
57
84
|
The bridge implementation itself lives here.
|
|
58
85
|
|
package/gea-native.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "What a GeaStack native target needs in order to build this package into an app. Paths are relative to the package root.",
|
|
3
|
+
"windows": {
|
|
4
|
+
"sources": ["native/angle_webgl_host.mm", "native/audio_host.mm", "native/win32_webgl_surface.cpp"],
|
|
5
|
+
"includeDirs": ["native"],
|
|
6
|
+
"requires": {
|
|
7
|
+
"gles": {
|
|
8
|
+
"description": "an EGL and OpenGL ES 3 implementation (ANGLE's libEGL.dll and libGLESv2.dll, or one DLL exporting both)",
|
|
9
|
+
"files": {
|
|
10
|
+
"egl": { "define": "GEA_WEBGL_EGL_LIBRARY", "default": "libEGL.dll" },
|
|
11
|
+
"gles": { "define": "GEA_WEBGL_GLES_LIBRARY", "default": "libGLESv2.dll" }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
#include "webgl_readback.h"
|
|
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
|
|
2
11
|
// GEA_WEBGL_STATIC_GLES: EGL/GLES are statically linked into the program, so
|
|
3
12
|
// nothing is loaded and entry points resolve through eglGetProcAddress.
|
|
4
13
|
#if defined(_WIN32)
|
|
@@ -1151,22 +1160,42 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1151
1160
|
gWebGL.egl = &staticGles;
|
|
1152
1161
|
gWebGL.gles = &staticGles;
|
|
1153
1162
|
#elif defined(_WIN32)
|
|
1154
|
-
// An override
|
|
1155
|
-
//
|
|
1156
|
-
|
|
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 {
|
|
1157
1169
|
wchar_t overridePath[MAX_PATH];
|
|
1158
1170
|
const DWORD n = GetEnvironmentVariableW(overrideEnv, overridePath, MAX_PATH);
|
|
1159
1171
|
if (n > 0 && n < MAX_PATH) {
|
|
1160
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());
|
|
1161
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)));
|
|
1162
1177
|
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP
|
|
1163
|
-
return LoadPackagedLibrary(
|
|
1178
|
+
return LoadPackagedLibrary(file.c_str(), 0);
|
|
1164
1179
|
#else
|
|
1165
|
-
|
|
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());
|
|
1166
1191
|
#endif
|
|
1167
1192
|
};
|
|
1168
|
-
gWebGL.egl = openWin32(L"GEA_ANGLE_EGL_DYLIB",
|
|
1169
|
-
gWebGL.gles = openWin32(L"GEA_ANGLE_GLES_DYLIB",
|
|
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
|
+
}
|
|
1170
1199
|
#elif defined(__APPLE__)
|
|
1171
1200
|
static const char *const eglPaths[] = {
|
|
1172
1201
|
"/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
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');
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// The WebGL surface of a Windows desktop build: a child window filling the
|
|
3
|
+
// main window's content area, which the native WebGL host renders into, and
|
|
4
|
+
// the input the host reports to the program.
|
|
5
|
+
//
|
|
6
|
+
// It plugs into the Windows target as a host extension (gea_win32_host.h):
|
|
7
|
+
// the target attaches it before the program's top level runs -- the program
|
|
8
|
+
// creates its WebGL context while it initializes -- and brackets the
|
|
9
|
+
// initialization and every frame with begin/end, which is when the host's
|
|
10
|
+
// EGL context is current. Keys reach the host as macOS virtual key codes, the
|
|
11
|
+
// code space the program's key polling uses on every platform; pointer
|
|
12
|
+
// positions are in CSS pixels of the surface.
|
|
13
|
+
#include "gea_win32_host.h"
|
|
14
|
+
|
|
15
|
+
#include <windowsx.h>
|
|
16
|
+
|
|
17
|
+
#include <algorithm>
|
|
18
|
+
#include <cstdio>
|
|
19
|
+
#include <cstdlib>
|
|
20
|
+
|
|
21
|
+
extern "C" bool gea_three_webgl_attach_uwp(void *nativeWindow, double width, double height, double devicePixelRatio);
|
|
22
|
+
extern "C" void gea_three_webgl_update_size_uwp(double width, double height, double devicePixelRatio);
|
|
23
|
+
extern "C" bool gea_three_webgl_begin_frame_uwp();
|
|
24
|
+
extern "C" void gea_three_webgl_end_frame_uwp();
|
|
25
|
+
extern "C" void gea_three_set_key_state(int code, bool down);
|
|
26
|
+
extern "C" void gea_three_set_pointer_state(double x, double y, double width, double height, bool down);
|
|
27
|
+
|
|
28
|
+
namespace {
|
|
29
|
+
|
|
30
|
+
HWND gSurface = nullptr;
|
|
31
|
+
HWND gMainWindow = nullptr;
|
|
32
|
+
double gScale = 1.0;
|
|
33
|
+
bool gPointerDown = false;
|
|
34
|
+
// The program polls key state once per frame, so a tap whose key-down and
|
|
35
|
+
// key-up both arrive between two frames would never be seen. A release waits
|
|
36
|
+
// here until a frame has run with the key down.
|
|
37
|
+
bool gReleasePending[256] = {};
|
|
38
|
+
|
|
39
|
+
// macOS virtual key codes (Carbon kVK_*), for the keys a game typically polls.
|
|
40
|
+
int macKeyCode(WPARAM vk)
|
|
41
|
+
{
|
|
42
|
+
switch (vk) {
|
|
43
|
+
case VK_LEFT: return 123;
|
|
44
|
+
case VK_RIGHT: return 124;
|
|
45
|
+
case VK_DOWN: return 125;
|
|
46
|
+
case VK_UP: return 126;
|
|
47
|
+
case VK_SPACE: return 49;
|
|
48
|
+
case VK_RETURN: return 36;
|
|
49
|
+
case VK_ESCAPE: return 53;
|
|
50
|
+
case VK_TAB: return 48;
|
|
51
|
+
case VK_BACK: return 51;
|
|
52
|
+
case VK_DELETE: return 117;
|
|
53
|
+
case VK_HOME: return 115;
|
|
54
|
+
case VK_END: return 119;
|
|
55
|
+
case VK_PRIOR: return 116;
|
|
56
|
+
case VK_NEXT: return 121;
|
|
57
|
+
case VK_SHIFT: case VK_LSHIFT: return 56;
|
|
58
|
+
case VK_RSHIFT: return 60;
|
|
59
|
+
case VK_CONTROL: case VK_LCONTROL: return 59;
|
|
60
|
+
case VK_RCONTROL: return 62;
|
|
61
|
+
case VK_MENU: case VK_LMENU: return 58;
|
|
62
|
+
case VK_RMENU: return 61;
|
|
63
|
+
case VK_OEM_MINUS: return 27;
|
|
64
|
+
case VK_OEM_PLUS: return 24;
|
|
65
|
+
case VK_OEM_COMMA: return 43;
|
|
66
|
+
case VK_OEM_PERIOD: return 47;
|
|
67
|
+
case VK_OEM_1: return 41; // ;
|
|
68
|
+
case VK_OEM_2: return 44; // /
|
|
69
|
+
case VK_OEM_3: return 50; // `
|
|
70
|
+
case VK_OEM_4: return 33; // [
|
|
71
|
+
case VK_OEM_5: return 42; // backslash
|
|
72
|
+
case VK_OEM_6: return 30; // ]
|
|
73
|
+
case VK_OEM_7: return 39; // '
|
|
74
|
+
default: break;
|
|
75
|
+
}
|
|
76
|
+
static const int letters[26] = {0, 11, 8, 2, 14, 3, 5, 4, 34, 38, 40, 37, 46, 45, 31, 35, 12, 15, 1, 17, 32, 9, 13, 7, 16, 6};
|
|
77
|
+
if (vk >= 'A' && vk <= 'Z') return letters[vk - 'A'];
|
|
78
|
+
static const int digits[10] = {29, 18, 19, 20, 21, 23, 22, 26, 28, 25};
|
|
79
|
+
if (vk >= '0' && vk <= '9') return digits[vk - '0'];
|
|
80
|
+
static const int functions[12] = {122, 120, 99, 118, 96, 97, 98, 100, 101, 109, 103, 111};
|
|
81
|
+
if (vk >= VK_F1 && vk <= VK_F12) return functions[vk - VK_F1];
|
|
82
|
+
return -1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void releaseAllKeys()
|
|
86
|
+
{
|
|
87
|
+
for (int code = 0; code < 256; ++code) {
|
|
88
|
+
gReleasePending[code] = false;
|
|
89
|
+
gea_three_set_key_state(code, false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void reportPointer(LPARAM lParam)
|
|
94
|
+
{
|
|
95
|
+
RECT client{};
|
|
96
|
+
GetClientRect(gSurface, &client);
|
|
97
|
+
gea_three_set_pointer_state(GET_X_LPARAM(lParam) / gScale, GET_Y_LPARAM(lParam) / gScale, std::max<LONG>(1, client.right) / gScale,
|
|
98
|
+
std::max<LONG>(1, client.bottom) / gScale, gPointerDown);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
LRESULT CALLBACK surfaceProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
102
|
+
{
|
|
103
|
+
switch (message) {
|
|
104
|
+
case WM_KEYDOWN:
|
|
105
|
+
case WM_SYSKEYDOWN:
|
|
106
|
+
if (message == WM_SYSKEYDOWN && wParam == VK_F4) break; // Alt+F4 closes the window
|
|
107
|
+
if (const int code = macKeyCode(wParam); code >= 0) {
|
|
108
|
+
gReleasePending[code] = false;
|
|
109
|
+
gea_three_set_key_state(code, true);
|
|
110
|
+
}
|
|
111
|
+
return 0; // no menu activation on Alt or F10
|
|
112
|
+
case WM_KEYUP:
|
|
113
|
+
case WM_SYSKEYUP:
|
|
114
|
+
if (const int code = macKeyCode(wParam); code >= 0) gReleasePending[code] = true;
|
|
115
|
+
return 0;
|
|
116
|
+
case WM_SYSCHAR:
|
|
117
|
+
return 0; // no beep for Alt+key
|
|
118
|
+
case WM_KILLFOCUS:
|
|
119
|
+
gPointerDown = false;
|
|
120
|
+
ReleaseCapture();
|
|
121
|
+
releaseAllKeys();
|
|
122
|
+
return 0;
|
|
123
|
+
case WM_LBUTTONDOWN:
|
|
124
|
+
SetFocus(hwnd);
|
|
125
|
+
gPointerDown = true;
|
|
126
|
+
SetCapture(hwnd);
|
|
127
|
+
reportPointer(lParam);
|
|
128
|
+
return 0;
|
|
129
|
+
case WM_LBUTTONUP:
|
|
130
|
+
gPointerDown = false;
|
|
131
|
+
ReleaseCapture();
|
|
132
|
+
reportPointer(lParam);
|
|
133
|
+
return 0;
|
|
134
|
+
case WM_MOUSEMOVE:
|
|
135
|
+
reportPointer(lParam);
|
|
136
|
+
return 0;
|
|
137
|
+
case WM_ERASEBKGND:
|
|
138
|
+
return 1; // the swap chain owns every pixel
|
|
139
|
+
case WM_PAINT:
|
|
140
|
+
ValidateRect(hwnd, nullptr);
|
|
141
|
+
return 0;
|
|
142
|
+
default:
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
return DefWindowProcW(hwnd, message, wParam, lParam);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
int attach(HWND window, RECT area, double scale)
|
|
149
|
+
{
|
|
150
|
+
gMainWindow = window;
|
|
151
|
+
gScale = scale > 0 ? scale : 1.0;
|
|
152
|
+
HINSTANCE instance = GetModuleHandleW(nullptr);
|
|
153
|
+
WNDCLASSEXW cls{};
|
|
154
|
+
cls.cbSize = sizeof(cls);
|
|
155
|
+
cls.style = CS_OWNDC | CS_DBLCLKS;
|
|
156
|
+
cls.lpfnWndProc = surfaceProc;
|
|
157
|
+
cls.hInstance = instance;
|
|
158
|
+
cls.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
|
159
|
+
cls.lpszClassName = L"GeaWebGLSurface";
|
|
160
|
+
RegisterClassExW(&cls);
|
|
161
|
+
// The main window must not paint over its swap-chain child.
|
|
162
|
+
SetWindowLongPtrW(window, GWL_STYLE, GetWindowLongPtrW(window, GWL_STYLE) | WS_CLIPCHILDREN);
|
|
163
|
+
const int width = std::max<int>(1, area.right - area.left);
|
|
164
|
+
const int height = std::max<int>(1, area.bottom - area.top);
|
|
165
|
+
gSurface = CreateWindowExW(0, cls.lpszClassName, L"", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, area.left, area.top, width, height, window, nullptr,
|
|
166
|
+
instance, nullptr);
|
|
167
|
+
if (!gSurface) {
|
|
168
|
+
std::fprintf(stderr, "[native-webgl-angle] could not create the surface window (error %lu)\n", GetLastError());
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
SetWindowPos(gSurface, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
172
|
+
// Present with vsync unless the environment says otherwise.
|
|
173
|
+
if (!std::getenv("GEA_ANGLE_SWAP_INTERVAL")) _putenv_s("GEA_ANGLE_SWAP_INTERVAL", "1");
|
|
174
|
+
if (!gea_three_webgl_attach_uwp(gSurface, width, height, gScale)) {
|
|
175
|
+
std::fprintf(stderr, "[native-webgl-angle] the WebGL surface could not start: no usable EGL/OpenGL ES implementation\n");
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
// eglSwapBuffers paces the loop: it waits for the refresh with vsync on and
|
|
179
|
+
// renders unthrottled with it off (GEA_ANGLE_SWAP_INTERVAL=0).
|
|
180
|
+
gea_win32_set_frame_pacing(GEA_WIN32_FRAME_PACING_PRESENT);
|
|
181
|
+
SetFocus(gSurface);
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
void layout(RECT area, double scale)
|
|
186
|
+
{
|
|
187
|
+
if (!gSurface) return;
|
|
188
|
+
gScale = scale > 0 ? scale : 1.0;
|
|
189
|
+
const int width = std::max<int>(1, area.right - area.left);
|
|
190
|
+
const int height = std::max<int>(1, area.bottom - area.top);
|
|
191
|
+
SetWindowPos(gSurface, nullptr, area.left, area.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
192
|
+
gea_three_webgl_update_size_uwp(width, height, gScale);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
int beginFrame() { return gea_three_webgl_begin_frame_uwp() ? 1 : 0; }
|
|
196
|
+
|
|
197
|
+
void endFrame()
|
|
198
|
+
{
|
|
199
|
+
gea_three_webgl_end_frame_uwp();
|
|
200
|
+
for (int code = 0; code < 256; ++code) {
|
|
201
|
+
if (!gReleasePending[code]) continue;
|
|
202
|
+
gReleasePending[code] = false;
|
|
203
|
+
gea_three_set_key_state(code, false);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
int windowMessage(HWND, UINT message, WPARAM, LPARAM, LRESULT *result)
|
|
208
|
+
{
|
|
209
|
+
// Activating the main window gives it the keyboard focus; the surface is
|
|
210
|
+
// where keys are read.
|
|
211
|
+
if (message == WM_SETFOCUS && gSurface) {
|
|
212
|
+
SetFocus(gSurface);
|
|
213
|
+
*result = 0;
|
|
214
|
+
return 1;
|
|
215
|
+
}
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
void detach()
|
|
220
|
+
{
|
|
221
|
+
if (gSurface) DestroyWindow(gSurface);
|
|
222
|
+
gSurface = nullptr;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const gea_win32_host_extension kSurfaceExtension = {
|
|
226
|
+
GEA_WIN32_HOST_EXTENSION_VERSION, "The WebGL surface", attach, layout, beginFrame, endFrame, windowMessage, detach,
|
|
227
|
+
};
|
|
228
|
+
const bool kRegistered = (gea_win32_register_host_extension(&kSurfaceExtension), true);
|
|
229
|
+
|
|
230
|
+
} // namespace
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geastack/native-webgl-angle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"./text/native-text-types": "./src/text/native-text-types.ts",
|
|
29
29
|
"./geatsc-plugin-batched-probe": "./geatsc-plugin-batched-probe.mjs",
|
|
30
30
|
"./test/batched-render-scene": "./test/batched-render-scene.ts",
|
|
31
|
-
"./test/auto-instanced-render-scene": "./test/auto-instanced-render-scene.ts"
|
|
31
|
+
"./test/auto-instanced-render-scene": "./test/auto-instanced-render-scene.ts",
|
|
32
|
+
"./gea-native.json": "./gea-native.json"
|
|
32
33
|
},
|
|
33
34
|
"files": [
|
|
34
35
|
"src",
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"geatsc-plugin-batched-probe.mjs",
|
|
41
42
|
"test/batched-render-scene.ts",
|
|
42
43
|
"test/auto-instanced-render-scene.ts",
|
|
43
|
-
"geatsc-plugin-readback.mjs"
|
|
44
|
+
"geatsc-plugin-readback.mjs",
|
|
45
|
+
"gea-native.json"
|
|
44
46
|
],
|
|
45
47
|
"publishConfig": {
|
|
46
48
|
"access": "public",
|
|
@@ -51,7 +53,7 @@
|
|
|
51
53
|
"test:host-effects": "node test/native-host-effects-contract.test.mjs",
|
|
52
54
|
"test:allocation-contracts": "node test/render-sort-contract.test.mjs && node test/upload-array-contract.test.mjs && node test/draw-dispatch-contract.test.mjs && node test/native-upload-transport.test.mjs && node test/fixed-uniform-contract.test.mjs && node test/uniform-binding-contract.test.mjs && node test/buffer-streaming-contract.test.mjs",
|
|
53
55
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
54
|
-
"test": "node test/native-blending-contract.test.mjs && node test/native-transform-contract.test.mjs && node test/troika-three-text.test.mjs && node test/typed-void-contract.test.mjs && node test/instancing-transport-contract.test.mjs && node test/automatic-instancing-contract.test.mjs && node test/jsdoc-fold-contract.test.mjs",
|
|
56
|
+
"test": "node test/native-blending-contract.test.mjs && node test/native-transform-contract.test.mjs && node test/troika-three-text.test.mjs && node test/typed-void-contract.test.mjs && node test/instancing-transport-contract.test.mjs && node test/automatic-instancing-contract.test.mjs && node test/jsdoc-fold-contract.test.mjs && node test/gea-native-declaration.test.mjs",
|
|
55
57
|
"test:package": "node --experimental-vm-modules test/package.test.mjs"
|
|
56
58
|
},
|
|
57
59
|
"dependencies": {
|