@gnsx/three 0.184.17 → 0.184.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnsx/three",
3
- "version": "0.184.17",
3
+ "version": "0.184.19",
4
4
  "description": "JavaScript 3D library",
5
5
  "type": "module",
6
6
  "main": "./build/three.cjs",
@@ -108,7 +108,7 @@
108
108
  "jsdoc": "^4.0.5",
109
109
  "magic-string": "^0.30.0",
110
110
  "pngjs": "^7.0.0",
111
- "puppeteer": "^24.40.0",
111
+ "puppeteer": "^25.1.0",
112
112
  "qunit": "^2.19.4",
113
113
  "rollup": "^4.6.0",
114
114
  "three": "file:.",
@@ -290,6 +290,17 @@ class Object3D extends EventDispatcher {
290
290
  */
291
291
  this.visible = true;
292
292
 
293
+ // WITH_GENESYS
294
+ /**
295
+ * When set to `true`, this object itself is hidden.
296
+ * Doesn't affect the visibility of the children.
297
+ *
298
+ * @type {boolean}
299
+ * @default false
300
+ */
301
+ this.selfHidden = false;
302
+ // !WITH_GENESYS
303
+
293
304
  /**
294
305
  * When set to `true`, the 3D object gets rendered into shadow maps.
295
306
  *
@@ -1303,6 +1314,9 @@ class Object3D extends EventDispatcher {
1303
1314
  if ( this.castShadow === true ) object.castShadow = true;
1304
1315
  if ( this.receiveShadow === true ) object.receiveShadow = true;
1305
1316
  if ( this.visible === false ) object.visible = false;
1317
+ // WITH_GENESYS
1318
+ if ( this.selfHidden === true ) object.selfHidden = true;
1319
+ // !WITH_GENESYS
1306
1320
  if ( this.frustumCulled === false ) object.frustumCulled = false;
1307
1321
  if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
1308
1322
  if ( this.static !== false ) object.static = this.static;
@@ -1604,6 +1618,9 @@ class Object3D extends EventDispatcher {
1604
1618
 
1605
1619
  this.layers.mask = source.layers.mask;
1606
1620
  this.visible = source.visible;
1621
+ // WITH_GENESYS
1622
+ this.selfHidden = source.selfHidden;
1623
+ // !WITH_GENESYS
1607
1624
 
1608
1625
  this.castShadow = source.castShadow;
1609
1626
  this.receiveShadow = source.receiveShadow;
@@ -1166,6 +1166,9 @@ class ObjectLoader extends Loader {
1166
1166
  }
1167
1167
 
1168
1168
  if ( data.visible !== undefined ) object.visible = data.visible;
1169
+ // WITH_GENESYS
1170
+ if ( data.selfHidden !== undefined ) object.selfHidden = data.selfHidden;
1171
+ // !WITH_GENESYS
1169
1172
  if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;
1170
1173
  if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;
1171
1174
  if ( data.static !== undefined ) object.static = data.static;
@@ -24,7 +24,7 @@ class ProfilerServiceClass {
24
24
  this._profile = 'full';
25
25
  this._enabled = false;
26
26
 
27
- /** @type {Map<string, Float64Array>} */
27
+ /** @type {Map<string, Float64Array>} Inclusive time ring buffers (one per label). */
28
28
  this.buffers = new Map();
29
29
  /** @type {Map<string, number>} */
30
30
  this.cursors = new Map();
@@ -37,6 +37,20 @@ class ProfilerServiceClass {
37
37
  this.traceStartTime = 0;
38
38
  this._markId = 0;
39
39
 
40
+ /** @type {Map<string, Float64Array>} Exclusive (self) time ring buffers (one per label). */
41
+ this.selfBuffers = new Map();
42
+ /** @type {Map<string, number>} */
43
+ this.selfCursors = new Map();
44
+ /** @type {Map<string, number>} */
45
+ this.selfCounts = new Map();
46
+ /**
47
+ * Global call stack tracking nesting across all labels for exclusive-time computation.
48
+ * @type {Array<{ label: string, childTime: number }>}
49
+ */
50
+ this.callStack = [];
51
+ /** @type {Map<string, number>} Total (uncapped) invocation count since last reset, for calls-per-frame. */
52
+ this.invocations = new Map();
53
+
40
54
  if ( this._enabled ) {
41
55
 
42
56
  this.traceStartTime = performance.now();
@@ -143,6 +157,9 @@ class ProfilerServiceClass {
143
157
  performance.mark( startMark );
144
158
  stack.push( { startTime, startMark, traced } );
145
159
 
160
+ // Push onto the global call stack for exclusive-time tracking.
161
+ this.callStack.push( { label, childTime: 0 } );
162
+
146
163
  }
147
164
 
148
165
  /**
@@ -178,6 +195,42 @@ class ProfilerServiceClass {
178
195
  this.cursors.set( label, ( cursor + 1 ) % RING_SIZE );
179
196
  this.counts.set( label, Math.min( ( this.counts.get( label ) + 1 ), RING_SIZE ) );
180
197
 
198
+ // Track total invocations (uncapped) for calls-per-frame computation.
199
+ this.invocations.set( label, ( this.invocations.get( label ) ?? 0 ) + 1 );
200
+
201
+ // Exclusive (self) time via the global call stack.
202
+ const top = this.callStack.length > 0 ? this.callStack[ this.callStack.length - 1 ] : undefined;
203
+ if ( top !== undefined && top.label === label ) {
204
+
205
+ this.callStack.pop();
206
+ const selfTime = Math.max( 0, duration - top.childTime );
207
+
208
+ let selfBuffer = this.selfBuffers.get( label );
209
+ if ( ! selfBuffer ) {
210
+
211
+ selfBuffer = new Float64Array( RING_SIZE );
212
+ this.selfBuffers.set( label, selfBuffer );
213
+ this.selfCursors.set( label, 0 );
214
+ this.selfCounts.set( label, 0 );
215
+
216
+ }
217
+
218
+ const selfCursor = this.selfCursors.get( label );
219
+ selfBuffer[ selfCursor ] = selfTime;
220
+ this.selfCursors.set( label, ( selfCursor + 1 ) % RING_SIZE );
221
+ this.selfCounts.set( label, Math.min( ( this.selfCounts.get( label ) + 1 ), RING_SIZE ) );
222
+
223
+ // Propagate inclusive duration to the parent scope's child accumulator.
224
+ const parent = this.callStack.length > 0 ? this.callStack[ this.callStack.length - 1 ] : undefined;
225
+ if ( parent !== undefined ) parent.childTime += duration;
226
+
227
+ } else {
228
+
229
+ // Label mismatch — likely async interleaving. Reset to avoid corruption.
230
+ this.callStack.length = 0;
231
+
232
+ }
233
+
181
234
  if ( traced ) {
182
235
 
183
236
  this.traceEvents.push( {
@@ -220,6 +273,23 @@ class ProfilerServiceClass {
220
273
  const sorted = [ ...samples ].sort( ( a, b ) => a - b );
221
274
  const avg = sorted.reduce( ( a, b ) => a + b, 0 ) / sorted.length;
222
275
 
276
+ // Exclusive (self) time stats.
277
+ const selfCount = this.selfCounts.get( label ) ?? 0;
278
+ let selfAvg, selfMin, selfMax, selfP95, selfFrameBudget;
279
+ if ( selfCount > 0 ) {
280
+
281
+ const selfBuffer = this.selfBuffers.get( label );
282
+ const selfSamples = [ ...( selfCount < RING_SIZE
283
+ ? selfBuffer.subarray( 0, selfCount )
284
+ : selfBuffer ) ].sort( ( a, b ) => a - b );
285
+ selfAvg = selfSamples.reduce( ( a, b ) => a + b, 0 ) / selfSamples.length;
286
+ selfMin = selfSamples[ 0 ];
287
+ selfMax = selfSamples[ selfSamples.length - 1 ];
288
+ selfP95 = selfSamples[ Math.floor( selfSamples.length * 0.95 ) ];
289
+ selfFrameBudget = ( selfAvg / FRAME_BUDGET_MS ) * 100;
290
+
291
+ }
292
+
223
293
  return {
224
294
  label,
225
295
  samples: samples.length,
@@ -228,6 +298,12 @@ class ProfilerServiceClass {
228
298
  max: sorted[ sorted.length - 1 ],
229
299
  p95: sorted[ Math.floor( sorted.length * 0.95 ) ],
230
300
  frameBudget: ( avg / FRAME_BUDGET_MS ) * 100,
301
+ totalInvocations: this.invocations.get( label ) ?? 0,
302
+ selfAvg,
303
+ selfMin,
304
+ selfMax,
305
+ selfP95,
306
+ selfFrameBudget,
231
307
  };
232
308
 
233
309
  }
@@ -350,6 +426,11 @@ class ProfilerServiceClass {
350
426
  this.traceEvents = [];
351
427
  this.traceStartTime = performance.now();
352
428
  this._markId = 0;
429
+ this.selfBuffers.clear();
430
+ this.selfCursors.clear();
431
+ this.selfCounts.clear();
432
+ this.callStack.length = 0;
433
+ this.invocations.clear();
353
434
 
354
435
  }
355
436
 
@@ -371,6 +452,12 @@ class ProfilerServiceClass {
371
452
  * @property {number} max
372
453
  * @property {number} p95
373
454
  * @property {number} frameBudget Percentage of a 60 fps frame budget (16.67 ms)
455
+ * @property {number} totalInvocations Total (uncapped) call count since last reset, for calls-per-frame computation.
456
+ * @property {number|undefined} selfAvg Exclusive (self) avg ms — inclusive time minus child scope time.
457
+ * @property {number|undefined} selfMin
458
+ * @property {number|undefined} selfMax
459
+ * @property {number|undefined} selfP95
460
+ * @property {number|undefined} selfFrameBudget Exclusive time as percentage of a 60 fps frame budget.
374
461
  */
375
462
 
376
463
  /**
@@ -1895,7 +1895,7 @@ class WebGLRenderer {
1895
1895
 
1896
1896
  const visible = object.layers.test( camera.layers );
1897
1897
 
1898
- if ( visible ) {
1898
+ if ( visible && ! object.selfHidden /* WITH_GENESYS */ ) {
1899
1899
 
1900
1900
  if ( object.isGroup ) {
1901
1901
 
@@ -1682,13 +1682,13 @@ class Renderer {
1682
1682
  renderList.begin();
1683
1683
 
1684
1684
  // WITH_GENESYS
1685
- ProfilerService.begin( '_projectObject' );
1685
+ ProfilerService.begin( 'projectObject' );
1686
1686
  // !WITH_GENESYS
1687
1687
 
1688
1688
  this._projectObject( scene, camera, 0, renderList, renderContext.clippingContext );
1689
1689
 
1690
1690
  // WITH_GENESYS
1691
- ProfilerService.end( '_projectObject' );
1691
+ ProfilerService.end( 'projectObject' );
1692
1692
  // !WITH_GENESYS
1693
1693
 
1694
1694
  renderList.finish();
@@ -3142,7 +3142,7 @@ class Renderer {
3142
3142
 
3143
3143
  const visible = object.layers.test( camera.layers );
3144
3144
 
3145
- if ( visible ) {
3145
+ if ( visible && ! object.selfHidden /* WITH_GENESYS */ ) {
3146
3146
 
3147
3147
  if ( object.isGroup ) {
3148
3148