@geastack/native-webgl-angle 0.1.8 → 0.1.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Native WebGL ANGLE
2
2
 
3
3
  `@geastack/native-webgl-angle` owns the native WebGL compatibility layer used by
4
- the real Three.js macOS path.
4
+ the Three.js macOS path.
5
5
 
6
6
  ## Installation
7
7
 
@@ -48,7 +48,7 @@ native/angle_webgl_host.mm
48
48
  Objective-C++ ANGLE/Metal host implementation
49
49
  ```
50
50
 
51
- ## Current Compromise
51
+ ## Native sources in apps
52
52
 
53
53
  Gea app manifests currently accept only app-relative `nativeSources`. Until
54
54
  that resolver can consume package native sources directly, app consumers keep a
@@ -63,7 +63,6 @@ opcode-style host functions. The intended end state is direct typed native
63
63
  methods and typed WebGL handles, with no `gea_cpp_value`/`gea_cpp_key` in the
64
64
  bridge hot path.
65
65
 
66
-
67
66
  ### Synchronous numeric uploads
68
67
 
69
68
  GL upload functions opt into `hostShims.hostNativeArrayFunctions`. The compiler
@@ -74,7 +73,7 @@ format and buffer/texture dispatch in `geatsc-plugin-uploads.mjs`.
74
73
  float/integer format, using inline storage through 16 elements and one heap
75
74
  allocation beyond that. Matching typed arrays retain and borrow their backing
76
75
  storage, preserving view offsets. Shared views use synchronized reads into an
77
- owned copy. Uniform arrays and buffer subranges no longer pass through a
76
+ owned copy. Uniform arrays and buffer subranges are passed without a
78
77
  JavaScript number-array temporary. Sparse arrays fail closed.
79
78
 
80
79
  These hosts must consume the input synchronously without reentering JavaScript
@@ -85,8 +84,8 @@ when changing this ABI; unmarked hosts retain their original vector ABI.
85
84
  Profiling builds (`GEA_PROFILE_ALLOCATIONS` or `GEA_PROFILE_NATIVE_FRAMES`)
86
85
  aggregate native GL call scopes and scene-transform markers. The frame-only
87
86
  option requires defining the macro only for `angle_webgl_host.mm` and avoids
88
- allocation instrumentation. Their elapsed times include instrumentation and
89
- any driver wait; they are attribution data, not uninstrumented CPU speedups.
87
+ allocation instrumentation. Elapsed times include instrumentation overhead and
88
+ driver waits, so use them to attribute time, not as CPU timings.
90
89
 
91
90
  `GEA_WEBGL_TRAP`, `GEA_WEBGL_BLEND_DEBUG`, and `GEA_WEBGL_SHADOW_COLOR_AUDIT`
92
91
  are launch settings, cached on first use to avoid environment lookups in draw
@@ -105,10 +104,9 @@ storage. Removing a scalar or typed-array contract must restore conservative
105
104
  prototype taint in the test.
106
105
 
107
106
  View attachment, size synchronization and presentation remain outside that
108
- contract pending a separate lifecycle/reentry audit. A native function's name
109
- or numeric return type alone does not establish its effects. These contracts
110
- remove false opaque-call edges; they do not establish zero boxing for an
111
- application as a whole.
107
+ contract pending a separate lifecycle/reentry audit. These contracts remove
108
+ false opaque-call edges; they do not make an application as a whole free of
109
+ boxing.
112
110
 
113
111
  `npm test` includes the native void-call regression. Three.js comes from this
114
112
  package's own `three` devDependency, so `npm install` here is enough; the
@@ -117,14 +115,12 @@ compiler comes from a checkout of
117
115
  `clang++` (or `CXX`) supplies ASan/UBSan. `GEATSC_COMPILER_ROOT` and
118
116
  `GEASTACK_ROOT` point at that checkout when it is not beside this repo.
119
117
 
120
- The test extracts Three.js's actual `bindVertexArrayObject` function before
121
- and after the plugin transform, then compiles each against the real native
122
- WebGL facade and host bridge. It checks certified direct calls, physical void
118
+ The test extracts Three.js's `bindVertexArrayObject` function before and
119
+ after the plugin transform, then compiles each against the native WebGL facade and host bridge. It checks certified direct calls, physical void
123
120
  results, absence of executable boxing, nullable handle forwarding, and the
124
121
  observable `undefined` return. Only the external GL functions are mocked for
125
122
  execution. Source fixtures stay in memory; the test executable uses the
126
- compiler's existing ignored build output. No archived compiler APIs or scratch
127
- directories are needed.
123
+ compiler's existing ignored build output.
128
124
 
129
125
  ### Streaming diagnostics and uniform names
130
126
 
@@ -153,23 +149,21 @@ is cached; replacement, deletion and `needsUpdate` remain observable.
153
149
  `GEA_WEBGL_UNIFORM_BINDINGS=baseline` disables the new name classification and
154
150
  shadow setters for comparison with the previous implementation.
155
151
  `GEA_WEBGL_UNIFORM_NAMES=baseline` disables fixed-name renderer setters.
156
- An earlier explicit local ID snapshot increased allocations by discarding
157
- reusable native string capacity; this implementation does not repeat it.
158
152
  `npm run test:allocation-contracts` covers streaming, fixed uniforms and live
159
153
  uniform bindings. `node test/uniform-binding-native.test.mjs` additionally
160
- executes the actual lookup helpers under ASan/UBSan and requires no emitted
154
+ executes the lookup helpers under ASan/UBSan and requires no emitted
161
155
  boxing.
162
156
 
163
157
  The experimental BatchedMesh adaptation remains separate from the standard
164
158
  plugin. `node test/batched-mesh-contract.test.mjs` checks its generic geometry,
165
159
  transform, color, resize and clone behavior. Clones receive independent texture
166
- sources and arrays. Native callback, clone and visibility checks now pass;
167
- the full ANGLE comparison also matches ordinary mesh pixels exactly. Without
160
+ sources and arrays. Native callback, clone and visibility checks pass, and
161
+ the full ANGLE comparison matches ordinary mesh pixels exactly. Without
168
162
  `WEBGL_multi_draw`, its fallback retains separate draw calls. See
169
163
  [the native validation record](test/BATCHED-MESH-NATIVE.md) for evidence.
170
164
 
171
165
  The separate `GEA_WEBGL_AUTO_INSTANCE=1` experiment groups adjacent compatible
172
- opaque meshes into actual instanced draws. It is disabled by default while
166
+ opaque meshes into instanced draws. It is disabled by default while
173
167
  end-to-end validation and platform measurements continue. Its eligibility rules,
174
168
  fallbacks and validation commands are documented in
175
169
  [the instancing contract](test/INSTANCING.md). Both experiments are generic;
@@ -177,8 +171,8 @@ the plugin contains no application-specific batching logic.
177
171
 
178
172
  ## License
179
173
 
180
- Apache-2.0 (see `LICENSE`). Use it, change it, ship closed-source products on
181
- it, no strings attached. The only GeaStack code under a different license is
174
+ Apache-2.0 (see `LICENSE`). You can ship closed-source products
175
+ built on it. The only GeaStack code under a different license is
182
176
  the embedded board support (`targets` and `@geastack/chips`, GPL-3.0-only):
183
177
  shipping closed-source firmware through those needs a commercial license.
184
178
  Contact [contact@geastack.com](mailto:contact@geastack.com) for commercial terms, support and hosted builds.
package/geatsc-plugin.mjs CHANGED
@@ -1719,6 +1719,31 @@ function ${name}( template, input ) {
1719
1719
 
1720
1720
  }`;
1721
1721
 
1722
+ /**
1723
+ * `array.constructor.name` over the nine typed-array kinds, spelled as the
1724
+ * `instanceof` dispatch the native typed-array carriers lower. A `.constructor`
1725
+ * read off a sum of typed arrays names a class evaluation no carrier holds.
1726
+ */
1727
+ export const nativeTypedArrayName = `/**
1728
+ * @param {${nativeTypedArrayType}} array
1729
+ * @return {string}
1730
+ */
1731
+ function typedArrayTypeName( array ) {
1732
+
1733
+ \tif ( array instanceof Int8Array ) return 'Int8Array';
1734
+ \tif ( array instanceof Uint8Array ) return 'Uint8Array';
1735
+ \tif ( array instanceof Uint8ClampedArray ) return 'Uint8ClampedArray';
1736
+ \tif ( array instanceof Int16Array ) return 'Int16Array';
1737
+ \tif ( array instanceof Uint16Array ) return 'Uint16Array';
1738
+ \tif ( array instanceof Int32Array ) return 'Int32Array';
1739
+ \tif ( array instanceof Uint32Array ) return 'Uint32Array';
1740
+ \tif ( array instanceof Float32Array ) return 'Float32Array';
1741
+ \tif ( array instanceof Float64Array ) return 'Float64Array';
1742
+
1743
+ \tthrow new Error( 'Unsupported typed array.' );
1744
+
1745
+ }`;
1746
+
1722
1747
  const replaceOne = (text, before, after, fileName, description) => {
1723
1748
  if (!text.includes(before))
1724
1749
  throw new Error(
@@ -3292,6 +3317,17 @@ function createColorManagement() {`,
3292
3317
  fileName,
3293
3318
  "ColorManagement typed built-in definitions close",
3294
3319
  );
3320
+ // `define` merges a table of definitions into `spaces`. An untyped
3321
+ // parameter is `any`, which made `Object.assign( this.spaces, colorSpaces )`
3322
+ // copy a dynamic object into a typed dictionary; stating the table's type
3323
+ // is what every caller already passes.
3324
+ transformed = replaceOne(
3325
+ transformed,
3326
+ "\t\tdefine: function ( colorSpaces ) {",
3327
+ "\t\t/** @param {Record<string, NativeColorSpaceDefinition>} colorSpaces */\n\t\tdefine: function ( colorSpaces ) {",
3328
+ fileName,
3329
+ "ColorManagement.define parameter type",
3330
+ );
3295
3331
  const propertyFunction = /^(\s*)([A-Za-z_$][A-Za-z0-9_$]*): function \(/gm;
3296
3332
  const matches = transformed.match(propertyFunction)?.length ?? 0;
3297
3333
  if (matches !== 13)
@@ -3427,6 +3463,8 @@ function createColorManagement() {`,
3427
3463
  "class BufferGeometry extends EventDispatcher {",
3428
3464
  `${nativeTypedArrayFactory("createTypedArrayWithLength", "number")}
3429
3465
 
3466
+ ${nativeTypedArrayName}
3467
+
3430
3468
  /** @param {BufferAttribute|InterleavedBufferAttribute} attribute @param {number} index @param {number} x @param {number} y @param {number} z */
3431
3469
  function setNativeAttributeXYZ( attribute, index, x, y, z ) {
3432
3470
 
@@ -3445,6 +3483,13 @@ class BufferGeometry extends EventDispatcher {`,
3445
3483
  fileName,
3446
3484
  "BufferGeometry typed-array factory",
3447
3485
  );
3486
+ transformed = replaceOne(
3487
+ transformed,
3488
+ 'type: index.array.constructor.name,',
3489
+ 'type: typedArrayTypeName( index.array ),',
3490
+ fileName,
3491
+ 'BufferGeometry typed-array name',
3492
+ );
3448
3493
  for (const attributeName of [
3449
3494
  "positionAttribute",
3450
3495
  "normalAttribute",
@@ -3537,10 +3582,17 @@ class BufferGeometry extends EventDispatcher {`,
3537
3582
  transformed = replaceOne(
3538
3583
  transformed,
3539
3584
  "class InterleavedBuffer {",
3540
- `${nativeTypedArrayFactory("createTypedArrayFromBuffer", "ArrayBuffer")}\n\nclass InterleavedBuffer {`,
3585
+ `${nativeTypedArrayFactory("createTypedArrayFromBuffer", "ArrayBuffer")}\n\n${nativeTypedArrayName}\n\nclass InterleavedBuffer {`,
3541
3586
  fileName,
3542
3587
  "InterleavedBuffer typed-array factory",
3543
3588
  );
3589
+ transformed = replaceOne(
3590
+ transformed,
3591
+ 'type: this.array.constructor.name,',
3592
+ 'type: typedArrayTypeName( this.array ),',
3593
+ fileName,
3594
+ 'InterleavedBuffer typed-array name',
3595
+ );
3544
3596
  transformed = replaceOne(
3545
3597
  transformed,
3546
3598
  "\t\tconst array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] );",
@@ -3567,10 +3619,17 @@ class BufferGeometry extends EventDispatcher {`,
3567
3619
  transformed = replaceOne(
3568
3620
  transformed,
3569
3621
  "class InterleavedBufferAttribute {",
3570
- `${nativeTypedArrayFactory("createTypedArrayFromValues", "number[]")}\n\nclass InterleavedBufferAttribute {`,
3622
+ `${nativeTypedArrayFactory("createTypedArrayFromValues", "number[]")}\n\n${nativeTypedArrayName}\n\nclass InterleavedBufferAttribute {`,
3571
3623
  fileName,
3572
3624
  "InterleavedBufferAttribute typed-array factory",
3573
3625
  );
3626
+ transformed = replaceOne(
3627
+ transformed,
3628
+ 'type: this.array.constructor.name,',
3629
+ 'type: typedArrayTypeName( this.array ),',
3630
+ fileName,
3631
+ 'InterleavedBufferAttribute typed-array name',
3632
+ );
3574
3633
  transformed = replaceOne(
3575
3634
  transformed,
3576
3635
  "\t\t\tconst array = [];",
@@ -4377,6 +4436,37 @@ import { Euler } from '../math/Euler.js';`,
4377
4436
  fileName,
4378
4437
  "Material exact setValues value imports",
4379
4438
  );
4439
+ // `toJSON` serializes whichever colour fields a subclass declared, behind
4440
+ // the duck test `x.isColor`. The field census sums every writer of a
4441
+ // colour field (`Color`, and the number/string `setValues` stores), so the
4442
+ // duck test leaves the sum un-narrowed and `getHex` is called on it.
4443
+ // `instanceof Color` is the same test over the one class that has the
4444
+ // method, and narrows the sum to that arm.
4445
+ for (const field of ["color", "sheenColor", "emissive", "specular", "specularColor"]) {
4446
+ transformed = replaceOne(
4447
+ transformed,
4448
+ `if ( this.${field} && this.${field}.isColor ) data.${field} = this.${field}.getHex();`,
4449
+ `if ( this.${field} instanceof Color ) data.${field} = this.${field}.getHex();`,
4450
+ fileName,
4451
+ `Material.toJSON ${field} narrowing`,
4452
+ );
4453
+ }
4454
+ for (const field of ["blendColor"]) {
4455
+ transformed = replaceOne(
4456
+ transformed,
4457
+ `if ( this.${field} && this.${field}.isColor ) data.${field} = this.${field}.getHex();`,
4458
+ `if ( this.${field} instanceof Color ) data.${field} = this.${field}.getHex();`,
4459
+ fileName,
4460
+ `Material.toJSON ${field} narrowing`,
4461
+ );
4462
+ }
4463
+ transformed = replaceOne(
4464
+ transformed,
4465
+ "if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex();",
4466
+ "if ( this.attenuationColor instanceof Color ) data.attenuationColor = this.attenuationColor.getHex();",
4467
+ fileName,
4468
+ "Material.toJSON attenuationColor narrowing",
4469
+ );
4380
4470
  transformed = replaceOne(
4381
4471
  transformed,
4382
4472
  `\t\t\tif ( currentValue && currentValue.isColor ) {
@@ -9718,6 +9808,20 @@ class PMREMGenerator {`,
9718
9808
  }
9719
9809
  }
9720
9810
  if (normalized.endsWith("/three/src/core/BufferAttribute.js")) {
9811
+ transformed = replaceOne(
9812
+ transformed,
9813
+ "class BufferAttribute extends EventDispatcher {",
9814
+ `${nativeTypedArrayName}\n\nclass BufferAttribute extends EventDispatcher {`,
9815
+ fileName,
9816
+ "BufferAttribute typed-array type name helper",
9817
+ );
9818
+ transformed = replaceOne(
9819
+ transformed,
9820
+ "type: this.array.constructor.name,",
9821
+ "type: typedArrayTypeName( this.array ),",
9822
+ fileName,
9823
+ "BufferAttribute toJSON typed-array type name",
9824
+ );
9721
9825
  const rawCopyArrayParameter =
9722
9826
  "@param {(TypedArray|Array)} array - The array to copy.";
9723
9827
  const realizedCopyArrayParameter =
@@ -9979,6 +10083,19 @@ class Texture extends EventDispatcher {`,
9979
10083
  "Plane normal-matrix input",
9980
10084
  );
9981
10085
  }
10086
+ if (normalized.endsWith("/three/src/math/Matrix3.js")) {
10087
+ // `x.isVector2` is a duck test on a `number|Vector2` parameter. The checker
10088
+ // does not narrow a union by the presence of a property one arm lacks, so
10089
+ // the `else` arm kept the whole union where `set` takes numbers. The
10090
+ // `typeof` form accepts exactly the same two inputs and narrows both arms.
10091
+ transformed = replaceOne(
10092
+ transformed,
10093
+ "\tmakeTranslation( x, y ) {\n\n\t\tif ( x.isVector2 ) {",
10094
+ "\tmakeTranslation( x, y ) {\n\n\t\tif ( typeof x !== 'number' ) {",
10095
+ fileName,
10096
+ "Matrix3.makeTranslation vector narrowing",
10097
+ );
10098
+ }
9982
10099
  if (normalized.endsWith("/three/src/math/Matrix4.js")) {
9983
10100
  transformed = replaceOne(
9984
10101
  transformed,
@@ -9987,6 +10104,20 @@ class Texture extends EventDispatcher {`,
9987
10104
  fileName,
9988
10105
  "Matrix4.fromArray native array input",
9989
10106
  );
10107
+ transformed = replaceOne(
10108
+ transformed,
10109
+ "\tsetPosition( x, y, z ) {\n\n\t\tconst te = this.elements;\n\n\t\tif ( x.isVector3 ) {",
10110
+ "\tsetPosition( x, y, z ) {\n\n\t\tconst te = this.elements;\n\n\t\tif ( typeof x !== 'number' ) {",
10111
+ fileName,
10112
+ "Matrix4.setPosition vector narrowing",
10113
+ );
10114
+ transformed = replaceOne(
10115
+ transformed,
10116
+ "\tmakeTranslation( x, y, z ) {\n\n\t\tif ( x.isVector3 ) {",
10117
+ "\tmakeTranslation( x, y, z ) {\n\n\t\tif ( typeof x !== 'number' ) {",
10118
+ fileName,
10119
+ "Matrix4.makeTranslation vector narrowing",
10120
+ );
9990
10121
  transformed = replaceOne(
9991
10122
  transformed,
9992
10123
  "@param {Array<number>} [array=[]] - The target array holding the matrix elements in column-major order.\n\t * @param {number} [offset=0] - Index of the first element in the array.\n\t * @return {Array<number>} The matrix elements in column-major order.",
@@ -10198,7 +10329,9 @@ class Texture extends EventDispatcher {`,
10198
10329
  transformed = replaceOne(
10199
10330
  transformed,
10200
10331
  "class Source {",
10201
- `/** @typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} NativeTextureData */
10332
+ `${nativeTypedArrayName}
10333
+
10334
+ /** @typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} NativeTextureData */
10202
10335
  /** @typedef {{ data?: NativeTextureData, width?: number, height?: number, depth?: number }} NativeTextureImage */
10203
10336
  /** @typedef {NativeTextureImage|NativeTextureImage[]|null} NativeTextureSourceData */
10204
10337
  class Source {
@@ -10208,6 +10341,13 @@ class Source {
10208
10341
  fileName,
10209
10342
  "Source native image types",
10210
10343
  );
10344
+ transformed = replaceOne(
10345
+ transformed,
10346
+ "type: image.data.constructor.name",
10347
+ "type: typedArrayTypeName( image.data )",
10348
+ fileName,
10349
+ "Source serializeImage typed-array type name",
10350
+ );
10211
10351
  transformed = transformed.replace(
10212
10352
  "@param {any} [data=null] - The data definition of a texture.",
10213
10353
  "@param {NativeTextureSourceData} data - The data definition of a texture.",
@@ -10249,6 +10389,16 @@ class Source {
10249
10389
  fileName,
10250
10390
  "Source image serialization input",
10251
10391
  );
10392
+ // `let url;` declares an evolving type: the checker reads each branch's
10393
+ // assignment, but the cube branch's `url = []` is an evolving `any[]` the
10394
+ // program cannot type. Name the union the two branches produce.
10395
+ transformed = replaceOne(
10396
+ transformed,
10397
+ "\t\t\tlet url;\n",
10398
+ "\t\t\t/** @type {ReturnType<typeof serializeImage>|Array<ReturnType<typeof serializeImage>>} */\n\t\t\tlet url;\n",
10399
+ fileName,
10400
+ "Source.toJSON serialized url type",
10401
+ );
10252
10402
  }
10253
10403
  // An unannotated override receives a precise declaration-overlay parameter,
10254
10404
  // while Object3D's source explicitly accepts either the metadata object or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geastack/native-webgl-angle",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -53,18 +53,20 @@
53
53
  "test:package": "node --experimental-vm-modules test/package.test.mjs"
54
54
  },
55
55
  "dependencies": {
56
- "@geastack/apple": "^0.2.13"
56
+ "@geastack/apple": "^0.2.14"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "three": "^0.185.0",
60
- "@geastack/compiler": "^1.0.17"
60
+ "@geastack/compiler": "^1.0.22"
61
61
  },
62
62
  "devDependencies": {
63
- "@geastack/compiler": "^1.0.17",
64
- "@geastack/core": "^0.1.23",
65
- "@geastack/engine": "^0.1.5",
63
+ "@geastack/compiler": "^1.0.22",
64
+ "@geastack/core": "^0.1.37",
65
+ "@geastack/engine": "^0.1.13",
66
66
  "@types/three": "^0.185.0",
67
67
  "three": "^0.185.0",
68
- "typescript": "^5.9.3"
68
+ "troika-three-text": "^0.52.4",
69
+ "typescript": "^5.9.3",
70
+ "vite": "^8.0.0"
69
71
  }
70
72
  }