@gnsx/three 0.185.9 → 0.185.11

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.
@@ -67,6 +67,15 @@ class Backend {
67
67
  [ TimestampQuery.COMPUTE ]: null
68
68
  };
69
69
 
70
+ // WITH_GENESYS
71
+ /**
72
+ * Listeners notified after a timestamp query is successfully allocated.
73
+ *
74
+ * @type {Set<function(string, string, ?string): void>}
75
+ */
76
+ this.timestampQueryListeners = new Set();
77
+ // !WITH_GENESYS
78
+
70
79
  /**
71
80
  * Whether to track timestamps with a Timestamp Query API or not.
72
81
  *
@@ -505,6 +514,41 @@ class Backend {
505
514
 
506
515
  }
507
516
 
517
+ // WITH_GENESYS
518
+ /**
519
+ * @param {function(string, string, ?string): void} listener
520
+ */
521
+ addTimestampQueryListener( listener ) {
522
+
523
+ this.timestampQueryListeners.add( listener );
524
+
525
+ }
526
+
527
+ /**
528
+ * @param {function(string, string, ?string): void} listener
529
+ */
530
+ removeTimestampQueryListener( listener ) {
531
+
532
+ this.timestampQueryListeners.delete( listener );
533
+
534
+ }
535
+
536
+ /**
537
+ * @param {string} type
538
+ * @param {string} uid
539
+ * @param {?string} [label=null]
540
+ */
541
+ notifyTimestampQuery( type, uid, label = null ) {
542
+
543
+ for ( const listener of this.timestampQueryListeners ) {
544
+
545
+ listener( type, uid, label );
546
+
547
+ }
548
+
549
+ }
550
+ // !WITH_GENESYS
551
+
508
552
  /**
509
553
  * Returns all timestamp frames for the given type.
510
554
  *
@@ -548,6 +592,22 @@ class Backend {
548
592
 
549
593
  }
550
594
 
595
+ // WITH_GENESYS
596
+ /**
597
+ * Returns the raw GPU timestamp range for the given uid when supported.
598
+ *
599
+ * @param {string} uid - The unique identifier.
600
+ * @return {?{start: bigint, end: bigint}} The raw timestamp range.
601
+ */
602
+ getTimestampRange( uid ) {
603
+
604
+ const queryPool = this._getQueryPool( uid );
605
+
606
+ return queryPool.getTimestampRange( uid );
607
+
608
+ }
609
+ // !WITH_GENESYS
610
+
551
611
  /**
552
612
  * Whether the backend supports query timestamps or not.
553
613
  *
@@ -230,6 +230,16 @@ class RenderContext {
230
230
  */
231
231
  this.camera = null;
232
232
 
233
+ // WITH_GENESYS
234
+ /**
235
+ * An optional label used to identify this render pass in GPU profiler output.
236
+ *
237
+ * @type {?string}
238
+ * @default null
239
+ */
240
+ this.gpuProfilerLabel = null;
241
+ // !WITH_GENESYS
242
+
233
243
  /**
234
244
  * This flag can be used for type testing.
235
245
  *
@@ -1617,6 +1617,25 @@ class Renderer {
1617
1617
 
1618
1618
  const renderContext = this._renderContexts.get( renderTarget, this._mrt, this._callDepth );
1619
1619
 
1620
+ // WITH_GENESYS
1621
+ // Prefer an explicit scene/pass name; otherwise fall back to the render
1622
+ // target texture name so depth/shadow/RTT work is not an invisible hole.
1623
+ let gpuProfilerLabel = scene.name !== '' ? scene.name : null;
1624
+ if ( gpuProfilerLabel === null && renderTarget !== null ) {
1625
+
1626
+ const textureName = renderTarget.texture?.name;
1627
+ if ( textureName !== undefined && textureName !== null && textureName !== '' ) {
1628
+
1629
+ gpuProfilerLabel = textureName;
1630
+
1631
+ }
1632
+
1633
+ }
1634
+
1635
+ renderContext.gpuProfilerLabel = gpuProfilerLabel;
1636
+ // !WITH_GENESYS
1637
+ // renderContext.gpuProfilerLabel = scene.name !== '' ? scene.name : null;
1638
+
1620
1639
  this._currentRenderContext = renderContext;
1621
1640
  this._currentRenderObjectFunction = this._renderObjectFunction || this.renderObject;
1622
1641
  this._handleObjectFunction = this._renderObjectDirect;
@@ -2616,7 +2635,7 @@ class Renderer {
2616
2635
 
2617
2636
  // WITH_GENESYS
2618
2637
  // Drop the strong reference to the active `RenderContext` so module-level
2619
- // `WeakMap<RenderContext, �?>` caches can release entries once
2638
+ // `WeakMap<RenderContext, ...>` caches can release entries once
2620
2639
  // `_renderContexts.dispose()` has cleared its dictionary.
2621
2640
  this._currentRenderContext = null;
2622
2641
  // !WITH_GENESYS
@@ -85,6 +85,15 @@ class TimestampQueryPool {
85
85
  */
86
86
  this.timestamps = new Map();
87
87
 
88
+ // WITH_GENESYS
89
+ /**
90
+ * Stores raw GPU timestamp ranges for each render context when supported.
91
+ *
92
+ * @type {Map<string, {start: bigint, end: bigint}>}
93
+ */
94
+ this.timestampRanges = new Map();
95
+ // !WITH_GENESYS
96
+
88
97
  }
89
98
 
90
99
  /**
@@ -120,6 +129,20 @@ class TimestampQueryPool {
120
129
 
121
130
  }
122
131
 
132
+ // WITH_GENESYS
133
+ /**
134
+ * Returns the raw GPU timestamp range for a render context.
135
+ *
136
+ * @param {string} uid - A unique identifier for the render context.
137
+ * @return {?{start: bigint, end: bigint}} The raw timestamp range, or null when unsupported.
138
+ */
139
+ getTimestampRange( uid ) {
140
+
141
+ return this.timestampRanges.get( uid ) ?? null;
142
+
143
+ }
144
+ // !WITH_GENESYS
145
+
123
146
  /**
124
147
  * Returns whether a timestamp is available for a given render context.
125
148
  *
@@ -407,7 +407,11 @@ class WebGLBackend extends Backend {
407
407
  * @param {string} type - The type of the timestamp query.
408
408
  * @param {string} uid - A unique identifier for the timestamp query.
409
409
  */
410
- initTimestampQuery( type, uid ) {
410
+ // WITH_GENESYS
411
+ initTimestampQuery( type, uid, label = null ) {
412
+
413
+ // !WITH_GENESYS
414
+ // initTimestampQuery( type, uid ) {
411
415
 
412
416
  if ( ! this.disjoint || ! this.trackTimestamp ) return;
413
417
 
@@ -424,7 +428,11 @@ class WebGLBackend extends Backend {
424
428
 
425
429
  if ( baseOffset !== null ) {
426
430
 
427
- timestampQueryPool.beginQuery( uid );
431
+ const started = timestampQueryPool.beginQuery( uid );
432
+
433
+ // WITH_GENESYS
434
+ if ( started ) this.notifyTimestampQuery( type, uid, label );
435
+ // !WITH_GENESYS
428
436
 
429
437
  }
430
438
 
@@ -497,7 +505,10 @@ class WebGLBackend extends Backend {
497
505
 
498
506
  //
499
507
 
500
- this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ) );
508
+ // WITH_GENESYS
509
+ this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ), renderContext.gpuProfilerLabel );
510
+ // !WITH_GENESYS
511
+ // this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ) );
501
512
 
502
513
  renderContextData.previousContext = this._currentContext;
503
514
  this._currentContext = renderContext;
@@ -81,33 +81,34 @@ class WebGLTimestampQueryPool extends TimestampQueryPool {
81
81
  * Begins a timestamp query for the specified render context.
82
82
  *
83
83
  * @param {string} uid - A unique identifier for the render context.
84
+ * @return {boolean} Whether the query was started.
84
85
  */
85
86
  beginQuery( uid ) {
86
87
 
87
88
  if ( ! this.trackTimestamp || this.isDisposed ) {
88
89
 
89
- return;
90
+ return false;
90
91
 
91
92
  }
92
93
 
93
94
  const baseOffset = this.queryOffsets.get( uid );
94
95
  if ( baseOffset == null ) {
95
96
 
96
- return;
97
+ return false;
97
98
 
98
99
  }
99
100
 
100
101
  // Don't start a new query if there's an active one
101
102
  if ( this.activeQuery !== null ) {
102
103
 
103
- return;
104
+ return false;
104
105
 
105
106
  }
106
107
 
107
108
  const query = this.queries[ baseOffset ];
108
109
  if ( ! query ) {
109
110
 
110
- return;
111
+ return false;
111
112
 
112
113
  }
113
114
 
@@ -119,6 +120,7 @@ class WebGLTimestampQueryPool extends TimestampQueryPool {
119
120
  this.gl.beginQuery( this.ext.TIME_ELAPSED_EXT, query );
120
121
  this.activeQuery = baseOffset;
121
122
  this.queryStates.set( baseOffset, 'started' );
123
+ return true;
122
124
 
123
125
  }
124
126
 
@@ -130,6 +132,8 @@ class WebGLTimestampQueryPool extends TimestampQueryPool {
130
132
 
131
133
  }
132
134
 
135
+ return false;
136
+
133
137
  }
134
138
 
135
139
  /**
@@ -25,7 +25,7 @@ import GPUShaderModuleDescriptor from './descriptors/GPUShaderModuleDescriptor.j
25
25
  import GPURenderPassColorAttachment from './descriptors/GPURenderPassColorAttachment.js';
26
26
  import GPURenderPassDepthStencilAttachment from './descriptors/GPURenderPassDepthStencilAttachment.js';
27
27
  import GPURenderPassDescriptor from './descriptors/GPURenderPassDescriptor.js';
28
- import GPURenderPassTimestampWrites from './descriptors/GPURenderPassTimestampWrites.js';
28
+ // import GPURenderPassTimestampWrites from './descriptors/GPURenderPassTimestampWrites.js';
29
29
  import GPUTexelCopyTextureInfo from './descriptors/GPUTexelCopyTextureInfo.js';
30
30
  import GPUTextureViewDescriptor from './descriptors/GPUTextureViewDescriptor.js';
31
31
  import GPUExtent3D from './descriptors/GPUExtent3D.js';
@@ -36,7 +36,7 @@ const _commandEncoderDescriptor = new GPUCommandEncoderDescriptor();
36
36
  const _computePassDescriptor = new GPUComputePassDescriptor();
37
37
  const _querySetDescriptor = new GPUQuerySetDescriptor();
38
38
  const _shaderModuleDescriptor = new GPUShaderModuleDescriptor();
39
- const _renderPassTimestampWrites = new GPURenderPassTimestampWrites();
39
+ // const _renderPassTimestampWrites = new GPURenderPassTimestampWrites();
40
40
  const _texelCopyTextureInfoSrc = new GPUTexelCopyTextureInfo();
41
41
  const _texelCopyTextureInfoDst = new GPUTexelCopyTextureInfo();
42
42
  const _viewDescriptor = new GPUTextureViewDescriptor();
@@ -836,7 +836,10 @@ class WebGPUBackend extends Backend {
836
836
 
837
837
  }
838
838
 
839
- this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ), descriptor );
839
+ // WITH_GENESYS
840
+ this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ), descriptor, renderContext.gpuProfilerLabel );
841
+ // !WITH_GENESYS
842
+ // this.initTimestampQuery( TimestampQuery.RENDER, this.getTimestampUID( renderContext ), descriptor );
840
843
 
841
844
  descriptor.occlusionQuerySet = occlusionQuerySet;
842
845
 
@@ -2252,7 +2255,11 @@ class WebGPUBackend extends Backend {
2252
2255
  * @param {number} uid - Unique id for the context (e.g. render context id).
2253
2256
  * @param {Object} descriptor - The query descriptor.
2254
2257
  */
2255
- initTimestampQuery( type, uid, descriptor ) {
2258
+ // WITH_GENESYS
2259
+ initTimestampQuery( type, uid, descriptor, label = null ) {
2260
+
2261
+ // !WITH_GENESYS
2262
+ // initTimestampQuery( type, uid, descriptor ) {
2256
2263
 
2257
2264
  if ( ! this.trackTimestamp ) return;
2258
2265
 
@@ -2272,11 +2279,22 @@ class WebGPUBackend extends Backend {
2272
2279
  if ( baseOffset === null || timestampQueryPool.querySet === null ) return;
2273
2280
  // !WITH_GENESYS
2274
2281
 
2275
- _renderPassTimestampWrites.querySet = timestampQueryPool.querySet;
2276
- _renderPassTimestampWrites.beginningOfPassWriteIndex = baseOffset;
2277
- _renderPassTimestampWrites.endOfPassWriteIndex = baseOffset + 1;
2282
+ // WITH_GENESYS
2283
+ // Per-pass object: a shared descriptor is mutated by nested beginRender /
2284
+ // copyFramebuffer restarts and can make unrelated passes write the same
2285
+ // query indices (shadow maps collapsing to ~1µs is the common symptom).
2286
+ descriptor.timestampWrites = {
2287
+ querySet: timestampQueryPool.querySet,
2288
+ beginningOfPassWriteIndex: baseOffset,
2289
+ endOfPassWriteIndex: baseOffset + 1,
2290
+ };
2278
2291
 
2279
- descriptor.timestampWrites = _renderPassTimestampWrites;
2292
+ this.notifyTimestampQuery( type, uid, label );
2293
+ // !WITH_GENESYS
2294
+ // _renderPassTimestampWrites.querySet = timestampQueryPool.querySet;
2295
+ // _renderPassTimestampWrites.beginningOfPassWriteIndex = baseOffset;
2296
+ // _renderPassTimestampWrites.endOfPassWriteIndex = baseOffset + 1;
2297
+ // descriptor.timestampWrites = _renderPassTimestampWrites;
2280
2298
 
2281
2299
  }
2282
2300
 
@@ -2837,6 +2855,17 @@ class WebGPUBackend extends Backend {
2837
2855
  if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
2838
2856
  if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
2839
2857
 
2858
+ // WITH_GENESYS
2859
+ // Restarted passes need fresh timestamp slots; reusing the previous
2860
+ // timestampWrites overwrites the first segment's query results.
2861
+ this.initTimestampQuery(
2862
+ TimestampQuery.RENDER,
2863
+ this.getTimestampUID( renderContext ),
2864
+ descriptor,
2865
+ renderContext.gpuProfilerLabel
2866
+ );
2867
+ // !WITH_GENESYS
2868
+
2840
2869
  renderContextData.currentPass = encoder.beginRenderPass( descriptor );
2841
2870
  renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null };
2842
2871
 
@@ -78,7 +78,20 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool {
78
78
  const baseOffset = this.currentQueryIndex;
79
79
  this.currentQueryIndex += 2;
80
80
 
81
- this.queryOffsets.set( uid, baseOffset );
81
+ // WITH_GENESYS
82
+ // One logical uid can own multiple GPU passes (nested interrupt /
83
+ // copyFramebuffer restart). Keep every offset; resolve sums them.
84
+ let offsets = this.queryOffsets.get( uid );
85
+ if ( offsets === undefined ) {
86
+
87
+ offsets = [];
88
+ this.queryOffsets.set( uid, offsets );
89
+
90
+ }
91
+
92
+ offsets.push( baseOffset );
93
+ // !WITH_GENESYS
94
+ // this.queryOffsets.set( uid, baseOffset );
82
95
 
83
96
  return baseOffset;
84
97
 
@@ -200,7 +213,7 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool {
200
213
 
201
214
  const frames = [];
202
215
 
203
- for ( const [ uid, baseOffset ] of currentOffsets ) {
216
+ for ( const [ uid, offsetOrOffsets ] of currentOffsets ) {
204
217
 
205
218
  const match = uid.match( /^(.*):f(\d+)$/ );
206
219
  const frame = parseInt( match[ 2 ] );
@@ -213,11 +226,34 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool {
213
226
 
214
227
  if ( framesDuration[ frame ] === undefined ) framesDuration[ frame ] = 0;
215
228
 
216
- const startTime = times[ baseOffset ];
217
- const endTime = times[ baseOffset + 1 ];
218
- const duration = Number( endTime - startTime ) / 1e6;
229
+ // WITH_GENESYS
230
+ const offsets = Array.isArray( offsetOrOffsets ) ? offsetOrOffsets : [ offsetOrOffsets ];
231
+ let duration = 0;
232
+ let rangeStart = null;
233
+ let rangeEnd = null;
234
+
235
+ for ( const baseOffset of offsets ) {
236
+
237
+ const startTime = times[ baseOffset ];
238
+ const endTime = times[ baseOffset + 1 ];
239
+ duration += Number( endTime - startTime ) / 1e6;
240
+
241
+ if ( rangeStart === null || startTime < rangeStart ) rangeStart = startTime;
242
+ if ( rangeEnd === null || endTime > rangeEnd ) rangeEnd = endTime;
243
+
244
+ }
219
245
 
220
246
  this.timestamps.set( uid, duration );
247
+ if ( rangeStart !== null && rangeEnd !== null ) {
248
+
249
+ this.timestampRanges.set( uid, { start: rangeStart, end: rangeEnd } );
250
+
251
+ }
252
+ // !WITH_GENESYS
253
+ // const startTime = times[ baseOffset ];
254
+ // const endTime = times[ baseOffset + 1 ];
255
+ // const duration = Number( endTime - startTime ) / 1e6;
256
+ // this.timestamps.set( uid, duration );
221
257
 
222
258
  framesDuration[ frame ] += duration;
223
259
 
@@ -316,6 +352,9 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool {
316
352
  }
317
353
 
318
354
  this.queryOffsets.clear();
355
+ // WITH_GENESYS
356
+ this.timestampRanges.clear();
357
+ // !WITH_GENESYS
319
358
  this.pendingResolve = null;
320
359
 
321
360
  }