@geastack/native-webgl-angle 0.1.10 → 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.
@@ -147,11 +147,13 @@ export function transformAutomaticInstancing(text, fileName, replaceOne, renderI
147
147
  }
148
148
 
149
149
  export function transformCanonicalInstanceHooks(text, fileName) {
150
- if (fileName.endsWith('/three/src/core/Object3D.js')) {
150
+ // Windows paths arrive with backslashes.
151
+ const normalized = fileName.replaceAll('\\', '/');
152
+ if (normalized.endsWith('/three/src/core/Object3D.js')) {
151
153
  return text + `\nexport const nativeInstanceBeforeRender = Object3D.prototype.onBeforeRender;
152
154
  export const nativeInstanceAfterRender = Object3D.prototype.onAfterRender;\n`;
153
155
  }
154
- if (fileName.endsWith('/three/src/materials/Material.js')) {
156
+ if (normalized.endsWith('/three/src/materials/Material.js')) {
155
157
  return text + `\nexport const nativeInstanceMaterialBeforeRender = Material.prototype.onBeforeRender;
156
158
  export const nativeInstanceBeforeCompile = Material.prototype.onBeforeCompile;
157
159
  export const nativeInstanceProgramCacheKey = Material.prototype.customProgramCacheKey;\n`;
@@ -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",
@@ -2010,7 +2016,8 @@ const transformSource = ({ fileName, text }) => {
2010
2016
  }
2011
2017
  if (normalized.endsWith("/three/src/renderers/WebGLRenderer.js")) {
2012
2018
  transformed =
2013
- `import { nativeProfilePhase } from '${fileURLToPath(new URL("./src/nativeWebGLHost.ts", import.meta.url))}';\n` +
2019
+ // Forward slashes: a Windows path's backslashes would be escapes in this literal.
2020
+ `import { nativeProfilePhase } from '${fileURLToPath(new URL("./src/nativeWebGLHost.ts", import.meta.url)).replaceAll("\\", "/")}';\n` +
2014
2021
  transformed;
2015
2022
  transformed = replaceOne(
2016
2023
  transformed,
@@ -10467,7 +10474,7 @@ export default {
10467
10474
  embeddedHostNoThrowFunctions,
10468
10475
  hostNativeArrayFunctions: hostFunctions
10469
10476
  .filter(([, , , declaration]) =>
10470
- declaration.includes("HostNumericArgument<"),
10477
+ declaration.includes("HostNumericArgument<") || declaration.includes("void* bytes"),
10471
10478
  )
10472
10479
  .map(([, cppName]) => cppName),
10473
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.10",
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.14"
58
+ "@geastack/apple": "^0.2.15"
57
59
  },
58
60
  "peerDependencies": {
59
61
  "three": "^0.185.0",
60
- "@geastack/compiler": "^1.0.22"
62
+ "@geastack/compiler": "^1.0.29"
61
63
  },
62
64
  "devDependencies": {
63
- "@geastack/compiler": "^1.0.22",
64
- "@geastack/core": "^0.1.37",
65
- "@geastack/engine": "^0.1.13",
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
+ }
@@ -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 | TexImageSource | null, format?: number, type?: number, pixels?: BufferSourceLike | TexImageSource | null): void {
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 as BufferSourceLike)
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
- this.renderbufferStorage(target, internalFormat, width, height)
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(_srcX0: number, _srcY0: number, _srcX1: number, _srcY1: number, _dstX0: number, _dstY0: number, _dstX1: number, _dstY1: number, _mask: number, _filter: number): void {}
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(_x: number, _y: number, _width: number, _height: number, _format: number, _type: number, _destination: unknown): void {}
1182
- copyTexImage2D(_target: number, _level: number, _internalFormat: number, _x: number, _y: number, _width: number, _height: number, _border: number): void {}
1183
- copyTexSubImage2D(_target: number, _level: number, _xoffset: number, _yoffset: number, _x: number, _y: number, _width: number, _height: number): void {}
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 {}
@@ -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
- /** Lifecycle entry: retains/uses the host view and installs native monitors; not an inert call. */
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
- /** Lifecycle entry: synchronizes host layout and viewport state; keep outside the inert contract. */
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