@gnsx/three 0.184.18 → 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/build/three.cjs +88 -1
- package/build/three.core.js +88 -1
- package/build/three.core.min.js +1 -1
- package/package.json +2 -2
- package/src/profiler/ProfilerService.js +88 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gnsx/three",
|
|
3
|
-
"version": "0.184.
|
|
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": "^
|
|
111
|
+
"puppeteer": "^25.1.0",
|
|
112
112
|
"qunit": "^2.19.4",
|
|
113
113
|
"rollup": "^4.6.0",
|
|
114
114
|
"three": "file:.",
|
|
@@ -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
|
/**
|