@geastack/native-webgl-angle 0.1.11 → 0.1.12
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/geatsc-plugin-readback.mjs +25 -0
- package/geatsc-plugin.mjs +7 -1
- package/native/angle_webgl_host.mm +50 -1
- package/native/webgl_readback.h +29 -0
- package/package.json +9 -7
- package/src/inflate.ts +252 -0
- package/src/nativeWebGL.ts +24 -7
- package/src/nativeWebGLHost.ts +9 -2
- package/src/text/atlas-sans.ts +19 -178
- package/src/text/atlas-serif-italic.ts +26 -178
- package/src/text/atlas-serif.ts +22 -178
- package/src/text/native-text-types.ts +28 -0
- package/src/text/native-text.ts +154 -50
- package/src/troika-three-text.ts +49 -4
- package/NOTICE +0 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Writable typed views must reach GLES in place. Upload snapshots are not valid
|
|
2
|
+
// here: copying into a temporary would discard readPixels' observable result.
|
|
3
|
+
export const nativeReadbackFunctions = [[
|
|
4
|
+
'threeWebGLReadPixels', 'gea_three_webgl_read_pixels_native', 'void', `
|
|
5
|
+
#include <variant>
|
|
6
|
+
extern "C" void gea_three_webgl_read_pixels_bytes(double x, double y, double width, double height,
|
|
7
|
+
double format, double type, void* bytes, double byteLength, double elementType);
|
|
8
|
+
template <typename T> inline void gea_three_webgl_read_pixels_native(double x, double y, double width, double height,
|
|
9
|
+
double format, double type, const gea::Ref<gea::TypedArray<T>>& destination, double offset) {
|
|
10
|
+
constexpr int elementType = std::is_same_v<T, float> ? 0x1406 : std::is_same_v<T, std::uint16_t> ? 0x1403 :
|
|
11
|
+
std::is_same_v<T, std::uint8_t> ? 0x1401 : std::is_same_v<T, std::uint32_t> ? 0x1405 :
|
|
12
|
+
std::is_same_v<T, std::int32_t> ? 0x1404 : std::is_same_v<T, std::int16_t> ? 0x1402 : 0x1400;
|
|
13
|
+
if (!destination || !std::isfinite(offset) || offset < 0 || std::floor(offset) != offset || offset > destination->size()) {
|
|
14
|
+
gea_three_webgl_read_pixels_bytes(x,y,width,height,format,type,nullptr,-1,elementType); return;
|
|
15
|
+
}
|
|
16
|
+
const auto start = static_cast<std::size_t>(offset);
|
|
17
|
+
auto* data = destination->data();
|
|
18
|
+
gea_three_webgl_read_pixels_bytes(x,y,width,height,format,type,data ? data + start : nullptr,
|
|
19
|
+
static_cast<double>((destination->size()-start)*sizeof(T)), elementType);
|
|
20
|
+
}
|
|
21
|
+
template <typename... T> inline void gea_three_webgl_read_pixels_native(double x, double y, double width, double height,
|
|
22
|
+
double format, double type, const std::variant<T...>& destination, double offset) {
|
|
23
|
+
std::visit([&](const auto& view) { gea_three_webgl_read_pixels_native(x,y,width,height,format,type,view,offset); }, destination);
|
|
24
|
+
}`
|
|
25
|
+
]];
|
package/geatsc-plugin.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
transformCanonicalInstanceHooks,
|
|
4
4
|
} from "./geatsc-plugin-instancing.mjs";
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
+
import { nativeReadbackFunctions } from "./geatsc-plugin-readback.mjs";
|
|
6
7
|
import { nativeUploadFunctions } from "./geatsc-plugin-uploads.mjs";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
@@ -74,6 +75,8 @@ function nativeVoidMemberCallContract(
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const hostFunctions = [
|
|
78
|
+
['threeWebGLCopyTexImage2D','gea_three_webgl_copy_tex_image_2d','void','extern "C" void gea_three_webgl_copy_tex_image_2d(double,double,double,double,double,double,double,double);'],
|
|
79
|
+
['threeWebGLCopyTexSubImage2D','gea_three_webgl_copy_tex_sub_image_2d','void','extern "C" void gea_three_webgl_copy_tex_sub_image_2d(double,double,double,double,double,double,double,double);'],
|
|
77
80
|
[
|
|
78
81
|
"threeWebGLVertexAttribDivisor",
|
|
79
82
|
"gea_three_webgl_vertex_attrib_divisor",
|
|
@@ -93,6 +96,9 @@ const hostFunctions = [
|
|
|
93
96
|
'extern "C" void gea_three_webgl_draw_arrays_instanced(double mode, double first, double count, double instanceCount);',
|
|
94
97
|
],
|
|
95
98
|
...nativeUploadFunctions,
|
|
99
|
+
...nativeReadbackFunctions,
|
|
100
|
+
['threeWebGLBlitFramebuffer','gea_three_webgl_blit_framebuffer','void','extern "C" void gea_three_webgl_blit_framebuffer(double,double,double,double,double,double,double,double,double,double);'],
|
|
101
|
+
['threeWebGLRenderbufferStorageMultisample','gea_three_webgl_renderbuffer_storage_multisample','void','extern "C" void gea_three_webgl_renderbuffer_storage_multisample(double,double,double,double,double);'],
|
|
96
102
|
[
|
|
97
103
|
"threeNativeProfilePhase",
|
|
98
104
|
"gea_three_native_profile_phase",
|
|
@@ -10468,7 +10474,7 @@ export default {
|
|
|
10468
10474
|
embeddedHostNoThrowFunctions,
|
|
10469
10475
|
hostNativeArrayFunctions: hostFunctions
|
|
10470
10476
|
.filter(([, , , declaration]) =>
|
|
10471
|
-
declaration.includes("HostNumericArgument<"),
|
|
10477
|
+
declaration.includes("HostNumericArgument<") || declaration.includes("void* bytes"),
|
|
10472
10478
|
)
|
|
10473
10479
|
.map(([, cppName]) => cppName),
|
|
10474
10480
|
hostArraySnapshotFunctions: hostFunctions
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
#include "webgl_readback.h"
|
|
1
2
|
#if defined(_WIN32)
|
|
2
3
|
#ifndef NOMINMAX
|
|
3
4
|
#define NOMINMAX
|
|
4
5
|
#endif
|
|
5
6
|
#include <windows.h>
|
|
7
|
+
// Legacy Win16 macros are not C++ identifiers and must not leak into the runtime.
|
|
8
|
+
#undef near
|
|
9
|
+
#undef far
|
|
6
10
|
#else
|
|
7
11
|
#include "gea/apple/native_bridge.h"
|
|
8
12
|
|
|
@@ -439,7 +443,15 @@ using PFNGLVERTEXATTRIBDIVISORPROC = void (*)(GLuint, GLuint);
|
|
|
439
443
|
using PFNGLVERTEXATTRIBPOINTERPROC = void (*)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void *);
|
|
440
444
|
using PFNGLVIEWPORTPROC = void (*)(GLint, GLint, GLsizei, GLsizei);
|
|
441
445
|
|
|
446
|
+
using PFNGLCOPYTEXIMAGE2DPROC=void(*)(GLenum,GLint,GLenum,GLint,GLint,GLsizei,GLsizei,GLint);
|
|
447
|
+
using PFNGLCOPYTEXSUBIMAGE2DPROC=void(*)(GLenum,GLint,GLint,GLint,GLint,GLint,GLsizei,GLsizei);
|
|
448
|
+
using PFNGLBLITFRAMEBUFFERPROC = void (*)(GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLbitfield,GLenum);
|
|
449
|
+
using PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC = void (*)(GLenum,GLsizei,GLenum,GLsizei,GLsizei);
|
|
442
450
|
struct AngleWebGLState {
|
|
451
|
+
PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D=nullptr;
|
|
452
|
+
PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D=nullptr;
|
|
453
|
+
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = nullptr;
|
|
454
|
+
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = nullptr;
|
|
443
455
|
void *egl = nullptr;
|
|
444
456
|
void *gles = nullptr;
|
|
445
457
|
EGLDisplay display = EGL_NO_DISPLAY;
|
|
@@ -580,6 +592,7 @@ struct AngleWebGLState {
|
|
|
580
592
|
};
|
|
581
593
|
|
|
582
594
|
static AngleWebGLState gWebGL;
|
|
595
|
+
static GLenum hostReadbackError = 0;
|
|
583
596
|
static std::chrono::steady_clock::time_point gProfileSizeSync;
|
|
584
597
|
static bool gProfileSizeSyncPending = false;
|
|
585
598
|
static bool nativeFrameProfileEnabled() {
|
|
@@ -1016,6 +1029,10 @@ static bool loadGlSymbols() {
|
|
|
1016
1029
|
LOAD_GL(glPixelStorei, PFNGLPIXELSTOREIPROC)
|
|
1017
1030
|
LOAD_GL(glPolygonOffset, PFNGLPOLYGONOFFSETPROC)
|
|
1018
1031
|
LOAD_GL(glReadPixels, PFNGLREADPIXELSPROC)
|
|
1032
|
+
LOAD_GL(glCopyTexImage2D,PFNGLCOPYTEXIMAGE2DPROC)
|
|
1033
|
+
LOAD_GL(glCopyTexSubImage2D,PFNGLCOPYTEXSUBIMAGE2DPROC)
|
|
1034
|
+
LOAD_GL(glBlitFramebuffer, PFNGLBLITFRAMEBUFFERPROC)
|
|
1035
|
+
LOAD_GL(glRenderbufferStorageMultisample, PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
|
|
1019
1036
|
LOAD_GL(glRenderbufferStorage, PFNGLRENDERBUFFERSTORAGEPROC)
|
|
1020
1037
|
LOAD_GL(glScissor, PFNGLSCISSORPROC)
|
|
1021
1038
|
LOAD_GL(glShaderSource, PFNGLSHADERSOURCEPROC)
|
|
@@ -2259,7 +2276,7 @@ extern "C" void gea_three_webgl_draw_arrays_instanced(double mode, double first,
|
|
|
2259
2276
|
geaTrapCheck("gea_three_webgl_draw_arrays_instanced"); GEA_WEBGL_VOID("drawArraysInstanced");
|
|
2260
2277
|
gWebGL.glDrawArraysInstanced(asGLenum(mode), asGLint(first), asGLsizei(count), asGLsizei(instanceCount)); }
|
|
2261
2278
|
extern "C" double gea_three_webgl_get_error() {
|
|
2262
|
-
geaTrapCheck("gea_three_webgl_get_error"); GEA_WEBGL_DOUBLE("getError"); return gWebGL.glGetError(); }
|
|
2279
|
+
geaTrapCheck("gea_three_webgl_get_error"); GEA_WEBGL_DOUBLE("getError"); if (hostReadbackError) { const GLenum e = hostReadbackError; hostReadbackError = 0; return e; } return gWebGL.glGetError(); }
|
|
2263
2280
|
|
|
2264
2281
|
extern "C" double gea_three_webgl_call(
|
|
2265
2282
|
double opValue,
|
|
@@ -2771,3 +2788,35 @@ extern "C" double gea_three_webgl_call_f32(
|
|
|
2771
2788
|
}
|
|
2772
2789
|
return 0.0;
|
|
2773
2790
|
}
|
|
2791
|
+
|
|
2792
|
+
extern "C" void gea_three_webgl_read_pixels_bytes(double x, double y, double width, double height,
|
|
2793
|
+
double format, double type, void* bytes, double byteLength, double elementType) {
|
|
2794
|
+
GEA_WEBGL_VOID("readPixels");
|
|
2795
|
+
GLint alignment = 4, rowLength = 0, skipRows = 0, skipPixels = 0, packBuffer = 0;
|
|
2796
|
+
gWebGL.glGetIntegerv(0x0D05, &alignment);
|
|
2797
|
+
gWebGL.glGetIntegerv(0x0D02, &rowLength);
|
|
2798
|
+
gWebGL.glGetIntegerv(0x0D03, &skipRows);
|
|
2799
|
+
gWebGL.glGetIntegerv(0x0D04, &skipPixels);
|
|
2800
|
+
gWebGL.glGetIntegerv(0x88ED, &packBuffer);
|
|
2801
|
+
GLenum error = gea_webgl::validateReadPixels(width,height,format,type,byteLength,elementType,alignment,rowLength,skipRows,skipPixels);
|
|
2802
|
+
if (packBuffer || (!bytes && width > 0 && height > 0)) error = 0x0502;
|
|
2803
|
+
if (!std::isfinite(x) || !std::isfinite(y) || x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX) error = 0x0501;
|
|
2804
|
+
if (error) { if (!hostReadbackError) hostReadbackError = error; return; }
|
|
2805
|
+
gWebGL.glReadPixels(asGLint(x),asGLint(y),asGLsizei(width),asGLsizei(height),asGLenum(format),asGLenum(type),bytes);
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
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
|
+
GEA_WEBGL_VOID("blitFramebuffer");
|
|
2810
|
+
gWebGL.glBlitFramebuffer(asGLint(sx0),asGLint(sy0),asGLint(sx1),asGLint(sy1),asGLint(dx0),asGLint(dy0),asGLint(dx1),asGLint(dy1),asGLenum(mask),asGLenum(filter));
|
|
2811
|
+
}
|
|
2812
|
+
extern "C" void gea_three_webgl_renderbuffer_storage_multisample(double target,double samples,double format,double width,double height) {
|
|
2813
|
+
GEA_WEBGL_VOID("renderbufferStorageMultisample");
|
|
2814
|
+
gWebGL.glRenderbufferStorageMultisample(asGLenum(target),asGLsizei(samples),asGLenum(format),asGLsizei(width),asGLsizei(height));
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
extern "C" void gea_three_webgl_copy_tex_image_2d(double target,double level,double format,double x,double y,double w,double h,double border){
|
|
2818
|
+
GEA_WEBGL_VOID("copyTexImage2D");gWebGL.glCopyTexImage2D(asGLenum(target),asGLint(level),asGLenum(format),asGLint(x),asGLint(y),asGLsizei(w),asGLsizei(h),asGLint(border));
|
|
2819
|
+
}
|
|
2820
|
+
extern "C" void gea_three_webgl_copy_tex_sub_image_2d(double target,double level,double xo,double yo,double x,double y,double w,double h){
|
|
2821
|
+
GEA_WEBGL_VOID("copyTexSubImage2D");gWebGL.glCopyTexSubImage2D(asGLenum(target),asGLint(level),asGLint(xo),asGLint(yo),asGLint(x),asGLint(y),asGLsizei(w),asGLsizei(h));
|
|
2822
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <cmath>
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
#include <limits>
|
|
5
|
+
|
|
6
|
+
namespace gea_webgl {
|
|
7
|
+
// Calculate the complete destination window, including WebGL2 pack layout.
|
|
8
|
+
// Return a GL error before the driver can touch an undersized typed array.
|
|
9
|
+
inline unsigned validateReadPixels(double width, double height, double format, double type,
|
|
10
|
+
double bytes, double elementType, int alignment, int rowLength, int skipRows, int skipPixels) {
|
|
11
|
+
if (!std::isfinite(width) || !std::isfinite(height) || width < 0 || height < 0 ||
|
|
12
|
+
width > INT32_MAX || height > INT32_MAX || std::floor(width) != width || std::floor(height) != height) return 0x0501;
|
|
13
|
+
if (format != 0x1908 || (type != 0x1401 && type != 0x1406 && type != 0x140B)) return 0x0502;
|
|
14
|
+
if (elementType != type && !(type == 0x140B && elementType == 0x1403)) return 0x0502;
|
|
15
|
+
if (bytes < 0 || !std::isfinite(bytes)) return 0x0501;
|
|
16
|
+
if (width == 0 || height == 0) return 0;
|
|
17
|
+
if (alignment != 1 && alignment != 2 && alignment != 4 && alignment != 8) return 0x0502;
|
|
18
|
+
if (rowLength < 0 || skipRows < 0 || skipPixels < 0) return 0x0502;
|
|
19
|
+
const uint64_t pixel = type == 0x1406 ? 16 : type == 0x140B ? 8 : 4;
|
|
20
|
+
const uint64_t row = rowLength ? rowLength : static_cast<uint64_t>(width);
|
|
21
|
+
if (row < static_cast<uint64_t>(width) + skipPixels) return 0x0502;
|
|
22
|
+
const uint64_t pitch = (row*pixel + alignment-1)/alignment*alignment;
|
|
23
|
+
const uint64_t rows = static_cast<uint64_t>(height)-1+skipRows;
|
|
24
|
+
if (rows && pitch > UINT64_MAX/rows) return 0x0502;
|
|
25
|
+
const uint64_t tail = (static_cast<uint64_t>(skipPixels)+static_cast<uint64_t>(width))*pixel;
|
|
26
|
+
if (rows*pitch > UINT64_MAX-tail) return 0x0502;
|
|
27
|
+
return static_cast<long double>(rows*pitch+tail) <= bytes ? 0 : 0x0502;
|
|
28
|
+
}
|
|
29
|
+
}
|
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.12",
|
|
4
4
|
"description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"./nativeThreeUtils": "./src/nativeThreeUtils.ts",
|
|
21
21
|
"./nativeWebXRManager": "./src/nativeWebXRManager.ts",
|
|
22
22
|
"./troika-three-text": "./src/troika-three-text.ts",
|
|
23
|
+
"./inflate": "./src/inflate.ts",
|
|
23
24
|
"./geatsc-plugin": "./geatsc-plugin.mjs",
|
|
24
25
|
"./native/*": "./native/*",
|
|
25
26
|
"./package.json": "./package.json",
|
|
@@ -38,7 +39,8 @@
|
|
|
38
39
|
"geatsc-plugin-instancing.mjs",
|
|
39
40
|
"geatsc-plugin-batched-probe.mjs",
|
|
40
41
|
"test/batched-render-scene.ts",
|
|
41
|
-
"test/auto-instanced-render-scene.ts"
|
|
42
|
+
"test/auto-instanced-render-scene.ts",
|
|
43
|
+
"geatsc-plugin-readback.mjs"
|
|
42
44
|
],
|
|
43
45
|
"publishConfig": {
|
|
44
46
|
"access": "public",
|
|
@@ -53,16 +55,16 @@
|
|
|
53
55
|
"test:package": "node --experimental-vm-modules test/package.test.mjs"
|
|
54
56
|
},
|
|
55
57
|
"dependencies": {
|
|
56
|
-
"@geastack/apple": "^0.2.
|
|
58
|
+
"@geastack/apple": "^0.2.15"
|
|
57
59
|
},
|
|
58
60
|
"peerDependencies": {
|
|
59
61
|
"three": "^0.185.0",
|
|
60
|
-
"@geastack/compiler": "^1.0.
|
|
62
|
+
"@geastack/compiler": "^1.0.29"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
63
|
-
"@geastack/compiler": "^1.0.
|
|
64
|
-
"@geastack/core": "^0.1.
|
|
65
|
-
"@geastack/engine": "^0.1.
|
|
65
|
+
"@geastack/compiler": "^1.0.29",
|
|
66
|
+
"@geastack/core": "^0.1.40",
|
|
67
|
+
"@geastack/engine": "^0.1.16",
|
|
66
68
|
"@types/three": "^0.185.0",
|
|
67
69
|
"three": "^0.185.0",
|
|
68
70
|
"troika-three-text": "^0.52.4",
|
package/src/inflate.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Raw DEFLATE (RFC 1951) decoder for data baked into a native binary -- glyph
|
|
2
|
+
// atlases, an app's mesh blobs. Baked data ships as base64 string literals,
|
|
3
|
+
// and compressing it first is what keeps a font or a model kit inside a
|
|
4
|
+
// microcontroller's app partition: zlib's deflate packs a 512 KB alpha atlas to
|
|
5
|
+
// ~40 KB and mesh blobs (flat shading triples every vertex) to a third.
|
|
6
|
+
// Encoders are zlib's own (node:zlib deflateRawSync, Python zlib with wbits=-15).
|
|
7
|
+
|
|
8
|
+
const lengthBase = new Uint16Array([3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258])
|
|
9
|
+
const lengthExtra = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0])
|
|
10
|
+
const distanceBase = new Uint16Array([
|
|
11
|
+
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385,
|
|
12
|
+
24577
|
|
13
|
+
])
|
|
14
|
+
const distanceExtra = new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13])
|
|
15
|
+
const codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15])
|
|
16
|
+
|
|
17
|
+
// The decoder is plain functions over locals and typed arrays, no class: a
|
|
18
|
+
// native build narrows a function's integer locals to machine integers, but a
|
|
19
|
+
// class's number fields stay doubles, and on a core without a double FPU (the
|
|
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.
|
|
22
|
+
|
|
23
|
+
/** Decoder state: read position, bit buffer, bits held, bytes written. */
|
|
24
|
+
const position = 0
|
|
25
|
+
const bitBuffer = 1
|
|
26
|
+
const bitCount = 2
|
|
27
|
+
const written = 3
|
|
28
|
+
|
|
29
|
+
function bits(state: Int32Array, source: Uint8Array, count: number): number {
|
|
30
|
+
let buffer = state[bitBuffer]
|
|
31
|
+
let have = state[bitCount]
|
|
32
|
+
let at = state[position]
|
|
33
|
+
while (have < count) {
|
|
34
|
+
buffer |= source[at] << have
|
|
35
|
+
at += 1
|
|
36
|
+
have += 8
|
|
37
|
+
}
|
|
38
|
+
state[position] = at
|
|
39
|
+
state[bitBuffer] = buffer >>> count
|
|
40
|
+
state[bitCount] = have - count
|
|
41
|
+
return buffer & ((1 << count) - 1)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A canonical Huffman code as puff.c keeps it: `counts` (codes per length,
|
|
46
|
+
* 16 entries) and `symbols` (in code order) built from code lengths.
|
|
47
|
+
*/
|
|
48
|
+
function buildHuffman(counts: Uint16Array, symbols: Uint16Array, lengths: Uint8Array, start: number, count: number): void {
|
|
49
|
+
counts.fill(0)
|
|
50
|
+
for (let i = 0; i < count; i++) counts[lengths[start + i]] += 1
|
|
51
|
+
counts[0] = 0
|
|
52
|
+
const offsets = new Uint16Array(16)
|
|
53
|
+
for (let length = 1; length < 15; length++) offsets[length + 1] = offsets[length] + counts[length]
|
|
54
|
+
for (let i = 0; i < count; i++) {
|
|
55
|
+
const length = lengths[start + i]
|
|
56
|
+
if (length !== 0) {
|
|
57
|
+
symbols[offsets[length]] = i
|
|
58
|
+
offsets[length] += 1
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function decode(state: Int32Array, source: Uint8Array, counts: Uint16Array, symbols: Uint16Array): number {
|
|
64
|
+
let code = 0
|
|
65
|
+
let first = 0
|
|
66
|
+
let index = 0
|
|
67
|
+
for (let length = 1; length < 16; length++) {
|
|
68
|
+
code |= bits(state, source, 1)
|
|
69
|
+
const count = counts[length]
|
|
70
|
+
if (code - first < count) return symbols[index + code - first]
|
|
71
|
+
index += count
|
|
72
|
+
first = (first + count) << 1
|
|
73
|
+
code <<= 1
|
|
74
|
+
}
|
|
75
|
+
throw new Error('inflate: invalid Huffman code')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function stored(state: Int32Array, source: Uint8Array, output: Uint8Array): void {
|
|
79
|
+
state[bitBuffer] = 0
|
|
80
|
+
state[bitCount] = 0
|
|
81
|
+
let at = state[position]
|
|
82
|
+
let out = state[written]
|
|
83
|
+
const length = source[at] | (source[at + 1] << 8)
|
|
84
|
+
at += 4
|
|
85
|
+
for (let i = 0; i < length; i++) {
|
|
86
|
+
output[out] = source[at]
|
|
87
|
+
out += 1
|
|
88
|
+
at += 1
|
|
89
|
+
}
|
|
90
|
+
state[position] = at
|
|
91
|
+
state[written] = out
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function fixedCodes(literalCounts: Uint16Array, literals: Uint16Array, distanceCounts: Uint16Array, distances: Uint16Array): void {
|
|
95
|
+
const lengths = new Uint8Array(320)
|
|
96
|
+
for (let i = 0; i < 144; i++) lengths[i] = 8
|
|
97
|
+
for (let i = 144; i < 256; i++) lengths[i] = 9
|
|
98
|
+
for (let i = 256; i < 280; i++) lengths[i] = 7
|
|
99
|
+
for (let i = 280; i < 288; i++) lengths[i] = 8
|
|
100
|
+
for (let i = 288; i < 320; i++) lengths[i] = 5
|
|
101
|
+
buildHuffman(literalCounts, literals, lengths, 0, 288)
|
|
102
|
+
buildHuffman(distanceCounts, distances, lengths, 288, 30)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function dynamicCodes(
|
|
106
|
+
state: Int32Array,
|
|
107
|
+
source: Uint8Array,
|
|
108
|
+
literalCounts: Uint16Array,
|
|
109
|
+
literals: Uint16Array,
|
|
110
|
+
distanceCounts: Uint16Array,
|
|
111
|
+
distances: Uint16Array
|
|
112
|
+
): void {
|
|
113
|
+
const literalCount = bits(state, source, 5) + 257
|
|
114
|
+
const distanceCount = bits(state, source, 5) + 1
|
|
115
|
+
const codeLengthCount = bits(state, source, 4) + 4
|
|
116
|
+
const lengths = new Uint8Array(320)
|
|
117
|
+
for (let i = 0; i < codeLengthCount; i++) lengths[codeLengthOrder[i]] = bits(state, source, 3)
|
|
118
|
+
buildHuffman(literalCounts, literals, lengths, 0, 19)
|
|
119
|
+
lengths.fill(0)
|
|
120
|
+
let index = 0
|
|
121
|
+
while (index < literalCount + distanceCount) {
|
|
122
|
+
const symbol = decode(state, source, literalCounts, literals)
|
|
123
|
+
if (symbol < 16) {
|
|
124
|
+
lengths[index] = symbol
|
|
125
|
+
index += 1
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
let value = 0
|
|
129
|
+
let repeat = 0
|
|
130
|
+
if (symbol === 16) {
|
|
131
|
+
value = lengths[index - 1]
|
|
132
|
+
repeat = 3 + bits(state, source, 2)
|
|
133
|
+
} else if (symbol === 17) {
|
|
134
|
+
repeat = 3 + bits(state, source, 3)
|
|
135
|
+
} else {
|
|
136
|
+
repeat = 11 + bits(state, source, 7)
|
|
137
|
+
}
|
|
138
|
+
for (let i = 0; i < repeat; i++) {
|
|
139
|
+
lengths[index] = value
|
|
140
|
+
index += 1
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
buildHuffman(literalCounts, literals, lengths, 0, literalCount)
|
|
144
|
+
buildHuffman(distanceCounts, distances, lengths, literalCount, distanceCount)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function codes(
|
|
148
|
+
state: Int32Array,
|
|
149
|
+
source: Uint8Array,
|
|
150
|
+
output: Uint8Array,
|
|
151
|
+
literalCounts: Uint16Array,
|
|
152
|
+
literals: Uint16Array,
|
|
153
|
+
distanceCounts: Uint16Array,
|
|
154
|
+
distances: Uint16Array
|
|
155
|
+
): void {
|
|
156
|
+
for (;;) {
|
|
157
|
+
const symbol = decode(state, source, literalCounts, literals)
|
|
158
|
+
if (symbol < 256) {
|
|
159
|
+
const out = state[written]
|
|
160
|
+
output[out] = symbol
|
|
161
|
+
state[written] = out + 1
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
if (symbol === 256) return
|
|
165
|
+
const lengthSymbol = symbol - 257
|
|
166
|
+
const length = lengthBase[lengthSymbol] + bits(state, source, lengthExtra[lengthSymbol])
|
|
167
|
+
const distanceSymbol = decode(state, source, distanceCounts, distances)
|
|
168
|
+
const distance = distanceBase[distanceSymbol] + bits(state, source, distanceExtra[distanceSymbol])
|
|
169
|
+
// A match may overlap its own output, so it copies byte by byte.
|
|
170
|
+
let out = state[written]
|
|
171
|
+
let from = out - distance
|
|
172
|
+
for (let i = 0; i < length; i++) {
|
|
173
|
+
output[out] = output[from]
|
|
174
|
+
out += 1
|
|
175
|
+
from += 1
|
|
176
|
+
}
|
|
177
|
+
state[written] = out
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Inflates a raw DEFLATE stream into exactly `length` bytes. */
|
|
182
|
+
export function inflateRaw(source: Uint8Array, length: number): Uint8Array {
|
|
183
|
+
const output = new Uint8Array(length)
|
|
184
|
+
const state = new Int32Array(4)
|
|
185
|
+
const literalCounts = new Uint16Array(16)
|
|
186
|
+
const literals = new Uint16Array(288)
|
|
187
|
+
const distanceCounts = new Uint16Array(16)
|
|
188
|
+
const distances = new Uint16Array(288)
|
|
189
|
+
let last = 0
|
|
190
|
+
while (last === 0) {
|
|
191
|
+
last = bits(state, source, 1)
|
|
192
|
+
const type = bits(state, source, 2)
|
|
193
|
+
if (type === 0) stored(state, source, output)
|
|
194
|
+
else if (type === 1) {
|
|
195
|
+
fixedCodes(literalCounts, literals, distanceCounts, distances)
|
|
196
|
+
codes(state, source, output, literalCounts, literals, distanceCounts, distances)
|
|
197
|
+
} else if (type === 2) {
|
|
198
|
+
dynamicCodes(state, source, literalCounts, literals, distanceCounts, distances)
|
|
199
|
+
codes(state, source, output, literalCounts, literals, distanceCounts, distances)
|
|
200
|
+
} else throw new Error('inflate: invalid block type')
|
|
201
|
+
}
|
|
202
|
+
if (state[written] !== length) throw new Error('inflate: stream length does not match')
|
|
203
|
+
return output
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function base64Value(code: number): number {
|
|
207
|
+
if (code >= 65 && code <= 90) return code - 65
|
|
208
|
+
if (code >= 97 && code <= 122) return code - 71
|
|
209
|
+
if (code >= 48 && code <= 57) return code + 4
|
|
210
|
+
if (code === 43) return 62
|
|
211
|
+
if (code === 47) return 63
|
|
212
|
+
return 0
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The bytes base64 string chunks spell. Not atob: the native runtime stores
|
|
217
|
+
* strings as UTF-8, so atob's "binary string" would read bytes >= 0x80 back as
|
|
218
|
+
* Unicode through charCodeAt. Every chunk but the last is a multiple of 4.
|
|
219
|
+
*/
|
|
220
|
+
export function base64Bytes(chunks: string[]): Uint8Array {
|
|
221
|
+
let length = 0
|
|
222
|
+
for (let c = 0; c < chunks.length; c++) {
|
|
223
|
+
const chunk = chunks[c]
|
|
224
|
+
length += (chunk.length / 4) * 3
|
|
225
|
+
if (chunk.length > 0 && chunk.charCodeAt(chunk.length - 1) === 61) length -= 1
|
|
226
|
+
if (chunk.length > 1 && chunk.charCodeAt(chunk.length - 2) === 61) length -= 1
|
|
227
|
+
}
|
|
228
|
+
const bytes = new Uint8Array(length)
|
|
229
|
+
let out = 0
|
|
230
|
+
for (let c = 0; c < chunks.length; c++) {
|
|
231
|
+
const chunk = chunks[c]
|
|
232
|
+
for (let i = 0; i < chunk.length; i += 4) {
|
|
233
|
+
const code2 = chunk.charCodeAt(i + 2)
|
|
234
|
+
const code3 = chunk.charCodeAt(i + 3)
|
|
235
|
+
const v0 = base64Value(chunk.charCodeAt(i))
|
|
236
|
+
const v1 = base64Value(chunk.charCodeAt(i + 1))
|
|
237
|
+
const v2 = base64Value(code2)
|
|
238
|
+
const v3 = base64Value(code3)
|
|
239
|
+
bytes[out] = (v0 << 2) | (v1 >> 4)
|
|
240
|
+
out += 1
|
|
241
|
+
if (code2 !== 61) {
|
|
242
|
+
bytes[out] = ((v1 & 15) << 4) | (v2 >> 2)
|
|
243
|
+
out += 1
|
|
244
|
+
}
|
|
245
|
+
if (code3 !== 61) {
|
|
246
|
+
bytes[out] = ((v2 & 3) << 6) | v3
|
|
247
|
+
out += 1
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return bytes
|
|
252
|
+
}
|
package/src/nativeWebGL.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
declare function threeWebGLCopyTexImage2D(target:number,level:number,internalFormat:number,x:number,y:number,width:number,height:number,border:number):void;
|
|
2
|
+
declare function threeWebGLCopyTexSubImage2D(target:number,level:number,xoffset:number,yoffset:number,x:number,y:number,width:number,height:number):void;
|
|
1
3
|
/// <reference path="./gea-native-types.d.ts" />
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -198,6 +200,12 @@ function typedArrayKind(value: BufferSourceLike): number {
|
|
|
198
200
|
return 1
|
|
199
201
|
}
|
|
200
202
|
|
|
203
|
+
declare function threeWebGLBlitFramebuffer(sx0:number, sy0:number, sx1:number, sy1:number, dx0:number, dy0:number, dx1:number, dy1:number, mask:number, filter:number): void
|
|
204
|
+
declare function threeWebGLRenderbufferStorageMultisample(target:number, samples:number, format:number, width:number, height:number): void
|
|
205
|
+
type ReadPixelsDestination = Uint8Array | Uint16Array | Float32Array | Uint32Array | Int32Array | Int16Array | Int8Array
|
|
206
|
+
// Mutates the destination view; deliberately not marked host-inert.
|
|
207
|
+
declare function threeWebGLReadPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset: number): void
|
|
208
|
+
|
|
201
209
|
function zeroF32Array(length: number): f32[] {
|
|
202
210
|
const out: f32[] = []
|
|
203
211
|
for (let index = 0; index < length; index++) out.push(0 as f32)
|
|
@@ -303,6 +311,9 @@ export interface NativeWebGLContextAttributes {
|
|
|
303
311
|
failIfMajorPerformanceCaveat: boolean
|
|
304
312
|
}
|
|
305
313
|
|
|
314
|
+
// Decoded image boundary. A native host never pretends DOM images have pixels.
|
|
315
|
+
export interface NativeTextureImage { width: number; height: number; data: BufferSourceLike }
|
|
316
|
+
|
|
306
317
|
export class NativeWebGLCanvas {
|
|
307
318
|
width: number
|
|
308
319
|
height: number
|
|
@@ -1092,9 +1103,13 @@ export class NativeWebGL2RenderingContext {
|
|
|
1092
1103
|
texParameterf(target: number, pname: number, param: number): void { nativeWebGLTexParameteri(target, pname, param) }
|
|
1093
1104
|
pixelStorei(pname: number, param: number | boolean): void { nativeWebGLPixelStorei(pname, param === true ? 1 : param === false ? 0 : param) }
|
|
1094
1105
|
generateMipmap(target: number): void { nativeWebGLGenerateMipmap(target) }
|
|
1095
|
-
texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number |
|
|
1106
|
+
texImage2D(target: number, level: number, internalFormat: number, widthOrFormat: number, heightOrType: number, borderOrSource?: number | NativeTextureImage | null, format?: number, type?: number, pixels?: BufferSourceLike | null): void {
|
|
1096
1107
|
if (typeof borderOrSource === 'number' && typeof format === 'number' && typeof type === 'number') {
|
|
1097
|
-
threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels
|
|
1108
|
+
threeWebGLTexImage2D(target, level, internalFormat, widthOrFormat, heightOrType, format, type, pixels ?? new Uint8Array(0))
|
|
1109
|
+
} else if (borderOrSource && typeof borderOrSource !== 'number') {
|
|
1110
|
+
threeWebGLTexImage2D(target, level, internalFormat, borderOrSource.width, borderOrSource.height, widthOrFormat, heightOrType, borderOrSource.data)
|
|
1111
|
+
} else {
|
|
1112
|
+
throw new Error('Native texImage2D requires dimensions and typed pixels, or a decoded NativeTextureImage')
|
|
1098
1113
|
}
|
|
1099
1114
|
}
|
|
1100
1115
|
texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: BufferSourceLike): void {
|
|
@@ -1130,7 +1145,7 @@ export class NativeWebGL2RenderingContext {
|
|
|
1130
1145
|
deleteRenderbuffer(renderbuffer: NativeHandle | null): void { nativeWebGLDeleteRenderbuffer(nullableNativeHandle(renderbuffer)) }
|
|
1131
1146
|
renderbufferStorage(target: number, internalFormat: number, width: number, height: number): void { nativeWebGLRenderbufferStorage(target, internalFormat, width, height) }
|
|
1132
1147
|
renderbufferStorageMultisample(target: number, samples: number, internalFormat: number, width: number, height: number): void {
|
|
1133
|
-
|
|
1148
|
+
threeWebGLRenderbufferStorageMultisample(target, samples, internalFormat, width, height)
|
|
1134
1149
|
}
|
|
1135
1150
|
framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: NativeHandle | null): void {
|
|
1136
1151
|
nativeWebGLFramebufferRenderbuffer(target, attachment, renderbuffertarget, nullableNativeHandle(renderbuffer))
|
|
@@ -1176,11 +1191,13 @@ export class NativeWebGL2RenderingContext {
|
|
|
1176
1191
|
flush(): void {}
|
|
1177
1192
|
hint(_target: number, _mode: number): void {}
|
|
1178
1193
|
invalidateFramebuffer(_target: number, _attachments: BufferSourceLike): void {}
|
|
1179
|
-
blitFramebuffer(
|
|
1194
|
+
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) }
|
|
1180
1195
|
readBuffer(_src: number): void {}
|
|
1181
|
-
readPixels(
|
|
1182
|
-
|
|
1183
|
-
|
|
1196
|
+
readPixels(x: number, y: number, width: number, height: number, format: number, type: number, destination: ReadPixelsDestination, offset = 0): void {
|
|
1197
|
+
threeWebGLReadPixels(x, y, width, height, format, type, destination, offset)
|
|
1198
|
+
}
|
|
1199
|
+
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);}
|
|
1200
|
+
copyTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, x: number, y: number, width: number, height: number): void {threeWebGLCopyTexSubImage2D(target,level,xoffset,yoffset,x,y,width,height);}
|
|
1184
1201
|
copyTexSubImage3D(_target: number, _level: number, _xoffset: number, _yoffset: number, _zoffset: number, _x: number, _y: number, _width: number, _height: number): void {}
|
|
1185
1202
|
clearBufferiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
|
|
1186
1203
|
clearBufferuiv(_buffer: number, _drawbuffer: number, _values: BufferSourceLike): void {}
|
package/src/nativeWebGLHost.ts
CHANGED
|
@@ -148,9 +148,16 @@ export function nativeWebGLDrawElements(mode: number, count: number, type: numbe
|
|
|
148
148
|
export function nativeWebGLDrawArrays(mode: number, first: number, count: number): void { threeWebGLDrawArrays(mode, first, count) }
|
|
149
149
|
export function nativeWebGLGetError(): number { return threeWebGLGetError() }
|
|
150
150
|
|
|
151
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* Lifecycle entry: retains/uses the host view and installs native monitors; not an inert call.
|
|
153
|
+
* It writes no property on any script object, which is the narrower contract.
|
|
154
|
+
* @gea-host-no-property-writes
|
|
155
|
+
*/
|
|
152
156
|
declare function threeWebGLAttach(view: NSView, width: number, height: number, devicePixelRatio: number): boolean
|
|
153
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* Lifecycle entry: synchronizes host layout and viewport state; keep outside the inert contract.
|
|
159
|
+
* @gea-host-no-property-writes
|
|
160
|
+
*/
|
|
154
161
|
declare function threeWebGLSyncSize(fallbackAspect: number): number
|
|
155
162
|
/** @gea-host-inert Native state only; retains no script references and invokes no script. */
|
|
156
163
|
declare function threeWebGLWidth(): number
|