rayzee 7.17.0 → 7.18.0
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 +1 -1
- package/dist/assets/BVHSubtreeWorker-D7KMbrQ-.js +2 -0
- package/dist/assets/BVHSubtreeWorker-D7KMbrQ-.js.map +1 -0
- package/dist/assets/BVHWorker-DOaGn6uN.js +2 -0
- package/dist/assets/BVHWorker-DOaGn6uN.js.map +1 -0
- package/dist/rayzee.es.js +2773 -2615
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +48 -48
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +7 -6
- package/src/Passes/OIDNDenoiser.js +17 -14
- package/src/PathTracerApp.js +32 -12
- package/src/Processor/BVHBuilder.js +11 -7
- package/src/Processor/BuildTimer.js +20 -6
- package/src/Processor/EmissiveTriangleBuilder.js +11 -7
- package/src/Processor/GeometryExtractor.js +11 -5
- package/src/Processor/KernelManager.js +48 -1
- package/src/Processor/PackedRayBuffer.js +20 -12
- package/src/Processor/ParallelBVHBuilder.js +9 -6
- package/src/Processor/QueueManager.js +6 -8
- package/src/Processor/SceneProcessor.js +47 -21
- package/src/Processor/ShaderBuilder.js +4 -1
- package/src/Processor/StorageTexturePool.js +4 -1
- package/src/Processor/Workers/BVHWorker.js +12 -6
- package/src/RenderSettings.js +1 -1
- package/src/Stages/PathTracer.js +15 -4
- package/src/Stages/PathTracerStage.js +17 -14
- package/src/TSL/FinalWriteKernel.js +11 -6
- package/src/TSL/ShadeKernel.js +46 -24
- package/src/TSL/TextureSampling.js +12 -2
- package/src/index.js +3 -0
- package/src/managers/EnvironmentManager.js +12 -8
- package/src/managers/MaterialDataManager.js +12 -9
- package/src/managers/UniformManager.js +1 -1
- package/src/utils/Logger.js +291 -0
- package/dist/assets/BVHSubtreeWorker-sNzvxn66.js +0 -2
- package/dist/assets/BVHSubtreeWorker-sNzvxn66.js.map +0 -1
- package/dist/assets/BVHWorker-CiVdFrwe.js +0 -2
- package/dist/assets/BVHWorker-CiVdFrwe.js.map +0 -1
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
packSnorm2x16, packUnorm2x16, unpackSnorm2x16, unpackUnorm2x16,
|
|
9
9
|
} from 'three/tsl';
|
|
10
10
|
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
11
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
12
|
+
|
|
13
|
+
const log = createLogger( 'gpu' );
|
|
11
14
|
|
|
12
15
|
export const RAY_STRIDE = 7;
|
|
13
16
|
export const HIT_STRIDE = 2;
|
|
@@ -19,7 +22,7 @@ export const HIT_STRIDE = 2;
|
|
|
19
22
|
export const GBUFFER_STRIDE = 1;
|
|
20
23
|
|
|
21
24
|
export const RAY = {
|
|
22
|
-
ORIGIN_META: 0, // vec4(origin.xyz, uintBitsToFloat(perRayBounces | sssSteps<<8)); pixelIndex == rayID
|
|
25
|
+
ORIGIN_META: 0, // vec4(origin.xyz, uintBitsToFloat(perRayBounces | sssSteps<<8 | lobeCounts<<16)); pixelIndex == rayID
|
|
23
26
|
DIR_FLAGS: 1, // vec4(direction.xyz, uintBitsToFloat(bounceFlags))
|
|
24
27
|
THROUGHPUT_PDF: 2, // vec4(throughput.xyz, pdf)
|
|
25
28
|
RADIANCE_ALPHA: 3, // vec4(radiance.xyz, alpha)
|
|
@@ -100,14 +103,12 @@ export class PackedRayBuffer {
|
|
|
100
103
|
ro: storage( hitAttr, 'vec4' ).toReadOnly(),
|
|
101
104
|
};
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
// Kept for the [gpu] startup summary in PathTracer._buildWavefrontKernels.
|
|
107
|
+
this.rayBytes = rayCount * 16 + capacity * 4;
|
|
108
|
+
this.hitBytes = hitCount * 16;
|
|
109
|
+
this.totalBytes = this.rayBytes + this.hitBytes;
|
|
106
110
|
|
|
107
|
-
|
|
108
|
-
`PackedRayBuffer: capacity=${capacity}, total=${totalMB.toFixed( 1 )} MB ` +
|
|
109
|
-
`(ray=${( rayCount * 16 / 1048576 ).toFixed( 0 )}MB hit=${( hitCount * 16 / 1048576 ).toFixed( 0 )}MB) [SoA ray/hit]`
|
|
110
|
-
);
|
|
111
|
+
log.debug( `ray pool capacity ${fmt.n( capacity )} · ${fmt.mb( this.totalBytes )} (rays ${fmt.mb( this.rayBytes )} · hits ${fmt.mb( this.hitBytes )})` );
|
|
111
112
|
|
|
112
113
|
}
|
|
113
114
|
|
|
@@ -182,12 +183,15 @@ export const gbDecodeNormalDepth = ( packed ) => {
|
|
|
182
183
|
export const gbDecodeAlbedo = ( packed ) =>
|
|
183
184
|
vec3( unpackUnorm2x16( packed.z ), unpackUnorm2x16( packed.w ).x );
|
|
184
185
|
|
|
185
|
-
// .w packs per-ray bounce state: perRayBounces (bits 0-7) | sssSteps (bits 8-15)
|
|
186
|
-
//
|
|
187
|
-
|
|
186
|
+
// .w packs per-ray bounce state: perRayBounces (bits 0-7) | sssSteps (bits 8-15) | transparentCount
|
|
187
|
+
// (bits 16-21), each masked so an overrun can't bleed into its neighbour. pixelIndex is NOT stored —
|
|
188
|
+
// it equals rayID (one ray per pixel).
|
|
189
|
+
export const writeRayOriginMeta = ( buf, id, origin, bounces, sssSteps, transparentCount = uint( 0 ) ) =>
|
|
188
190
|
buf.element( soa( id, RAY.ORIGIN_META ) )
|
|
189
191
|
.assign( vec4( origin, uintBitsToFloat(
|
|
190
|
-
uint( bounces ).
|
|
192
|
+
uint( bounces ).bitAnd( uint( 0xFF ) )
|
|
193
|
+
.bitOr( uint( sssSteps ).bitAnd( uint( 0xFF ) ).shiftLeft( 8 ) )
|
|
194
|
+
.bitOr( uint( transparentCount ).bitAnd( uint( 0x3F ) ).shiftLeft( 16 ) )
|
|
191
195
|
) ) );
|
|
192
196
|
|
|
193
197
|
export const writeRayDirFlags = ( buf, id, direction, bounceFlags ) =>
|
|
@@ -293,11 +297,15 @@ export const writeFeatureThroughput = ( buf, id, tp ) => {
|
|
|
293
297
|
// Per-ray bounce state packed into ORIGIN_META.w (written by writeRayOriginMeta alongside the origin):
|
|
294
298
|
// perRayBounces = bits 0-7 (camera-bounce depth; the loop index can't track it once free bounces decouple it)
|
|
295
299
|
// sssSteps = bits 8-15 (SSS random-walk step counter)
|
|
300
|
+
// transparentCount = bits 16-21 (alpha-passthrough counter, max 63; see maxTransparentBounces)
|
|
296
301
|
export const readPathBounces = ( buf, id ) =>
|
|
297
302
|
int( floatBitsToUint( buf.element( soa( id, RAY.ORIGIN_META ) ).w ).bitAnd( 0xFF ) );
|
|
298
303
|
export const readSssSteps = ( buf, id ) =>
|
|
299
304
|
int( floatBitsToUint( buf.element( soa( id, RAY.ORIGIN_META ) ).w ).shiftRight( 8 ).bitAnd( 0xFF ) );
|
|
300
305
|
|
|
306
|
+
export const readTransparentCount = ( buf, id ) =>
|
|
307
|
+
int( floatBitsToUint( buf.element( soa( id, RAY.ORIGIN_META ) ).w ).shiftRight( 16 ).bitAnd( uint( 0x3F ) ) );
|
|
308
|
+
|
|
301
309
|
// Region 9: SSS sigmaS + Henyey-Greenstein g. sigmaS==0 marks glass (Beer-Lambert path, not random walk).
|
|
302
310
|
export const readSSSMedium = ( buf, id ) => {
|
|
303
311
|
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
11
11
|
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
12
12
|
import BVH_SUBTREE_WORKER_URL from './Workers/BVHSubtreeWorker.js?worker&url';
|
|
13
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
14
|
+
|
|
15
|
+
const log = createLogger( 'bvh' );
|
|
13
16
|
|
|
14
17
|
const FPT = 32; // FLOATS_PER_TRIANGLE
|
|
15
18
|
const PARALLEL_THRESHOLD = 50000;
|
|
@@ -34,7 +37,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
34
37
|
const numWorkers = Math.min( navigator.hardwareConcurrency || 4, MAX_PARALLEL_WORKERS );
|
|
35
38
|
const parallelDepth = Math.ceil( Math.log2( numWorkers * 2.5 + 1 ) );
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
log.debug( `parallel build ${fmt.n( triangleCount )} triangles · ${numWorkers} workers · parallelDepth ${parallelDepth}` );
|
|
38
41
|
|
|
39
42
|
return new Promise( ( resolve, reject ) => {
|
|
40
43
|
|
|
@@ -98,7 +101,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
98
101
|
|
|
99
102
|
if ( settled ) return;
|
|
100
103
|
settled = true;
|
|
101
|
-
|
|
104
|
+
log.warn( `parallel build failed (${reason}), falling back to single worker` );
|
|
102
105
|
cleanup();
|
|
103
106
|
// Copy from SharedArrayBuffer to regular ArrayBuffer for transfer
|
|
104
107
|
const restoredBuffer = new ArrayBuffer( sharedTriangleData.byteLength );
|
|
@@ -182,7 +185,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
182
185
|
|
|
183
186
|
} )().catch( ( error ) => {
|
|
184
187
|
|
|
185
|
-
|
|
188
|
+
log.warn( 'parallel build setup failed:', error );
|
|
186
189
|
reject( error );
|
|
187
190
|
|
|
188
191
|
} );
|
|
@@ -209,7 +212,7 @@ async function handlePhase2(
|
|
|
209
212
|
|
|
210
213
|
// No frontier tasks — top-level tree is the complete BVH
|
|
211
214
|
// Still need to reorder triangles via coordinator
|
|
212
|
-
|
|
215
|
+
log.debug( 'no frontier tasks, assembling with top-level tree only' );
|
|
213
216
|
coordinatorWorker.postMessage( {
|
|
214
217
|
type: 'assemble',
|
|
215
218
|
topFlatData,
|
|
@@ -225,7 +228,7 @@ async function handlePhase2(
|
|
|
225
228
|
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
log.debug( `phase 2 distributing ${frontierTasks.length} tasks across ${numWorkers} workers` );
|
|
229
232
|
|
|
230
233
|
// Distribute tasks using greedy least-loaded assignment
|
|
231
234
|
const sortedTasks = [ ...frontierTasks ].sort( ( a, b ) => ( b.end - b.start ) - ( a.end - a.start ) );
|
|
@@ -493,7 +496,7 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
493
496
|
|
|
494
497
|
} )().catch( ( error ) => {
|
|
495
498
|
|
|
496
|
-
|
|
499
|
+
log.warn( 'single worker fallback failed:', error );
|
|
497
500
|
reject( error );
|
|
498
501
|
|
|
499
502
|
} );
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
import { storage } from 'three/tsl';
|
|
6
6
|
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
7
7
|
import { freeStorageAttribute } from './PackedRayBuffer.js';
|
|
8
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
9
|
+
|
|
10
|
+
const log = createLogger( 'gpu' );
|
|
8
11
|
|
|
9
12
|
/** Counter indices — must match ResetCounters kernel */
|
|
10
13
|
export const COUNTER = {
|
|
@@ -109,15 +112,10 @@ export class QueueManager {
|
|
|
109
112
|
|
|
110
113
|
this.pingPong = 0;
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
capacity * 4 * 3
|
|
115
|
-
);
|
|
115
|
+
// Kept for the [gpu] startup summary in PathTracer._buildWavefrontKernels.
|
|
116
|
+
this.totalBytes = COUNTER.COUNT * 4 + capacity * 4 * 3;
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
`QueueManager: Allocated capacity=${capacity}, ` +
|
|
119
|
-
`total=${( totalBytes / ( 1024 * 1024 ) ).toFixed( 1 )} MB`
|
|
120
|
-
);
|
|
118
|
+
log.debug( `queues capacity ${fmt.n( capacity )} · ${fmt.mb( this.totalBytes )}` );
|
|
121
119
|
|
|
122
120
|
}
|
|
123
121
|
|
|
@@ -9,12 +9,15 @@ import { GeometryExtractor } from './GeometryExtractor.js';
|
|
|
9
9
|
import { EmissiveTriangleBuilder } from './EmissiveTriangleBuilder.js';
|
|
10
10
|
import { updateLoading } from '../Processor/utils.js';
|
|
11
11
|
import { BuildTimer } from './BuildTimer.js';
|
|
12
|
+
import { createLogger, fmt, workerLogLevel } from '../utils/Logger.js';
|
|
12
13
|
import { SRGBColorSpace } from 'three';
|
|
13
14
|
import { TRIANGLE_DATA_LAYOUT, TEXTURE_CONSTANTS, getTextureBucketId, packTextureIndex } from '../EngineDefaults.js';
|
|
14
15
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
15
16
|
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
16
17
|
import BVH_REFIT_WORKER_URL from './Workers/BVHRefitWorker.js?worker&url';
|
|
17
18
|
|
|
19
|
+
const log = createLogger( 'scene' );
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* SceneProcessor - Processes scene geometry into GPU-ready data:
|
|
20
23
|
* BVH acceleration, texture atlas, material buffers.
|
|
@@ -170,11 +173,9 @@ export class SceneProcessor {
|
|
|
170
173
|
*/
|
|
171
174
|
_log( message, data ) {
|
|
172
175
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
176
|
+
// Visibility is the log level's job now; config.verbose no longer gates this.
|
|
177
|
+
if ( data !== undefined ) log.debug( message, data );
|
|
178
|
+
else log.debug( message );
|
|
178
179
|
|
|
179
180
|
}
|
|
180
181
|
|
|
@@ -194,7 +195,7 @@ export class SceneProcessor {
|
|
|
194
195
|
this.isProcessing = true;
|
|
195
196
|
this.processingStage = 'init';
|
|
196
197
|
|
|
197
|
-
const timer = new BuildTimer(
|
|
198
|
+
const timer = new BuildTimer( object.name ?? '', { namespace: 'scene' } );
|
|
198
199
|
|
|
199
200
|
try {
|
|
200
201
|
|
|
@@ -273,7 +274,7 @@ export class SceneProcessor {
|
|
|
273
274
|
} catch ( error ) {
|
|
274
275
|
|
|
275
276
|
this.processingStage = 'error';
|
|
276
|
-
|
|
277
|
+
log.error( 'processing failed:', error );
|
|
277
278
|
updateLoading( {
|
|
278
279
|
status: `Error: ${error.message}`,
|
|
279
280
|
progress: 100
|
|
@@ -357,7 +358,7 @@ export class SceneProcessor {
|
|
|
357
358
|
|
|
358
359
|
} catch ( error ) {
|
|
359
360
|
|
|
360
|
-
|
|
361
|
+
log.error( 'geometry extraction failed:', error );
|
|
361
362
|
updateLoading( {
|
|
362
363
|
status: `Extraction error: ${error.message}`,
|
|
363
364
|
progress: 25
|
|
@@ -449,6 +450,7 @@ export class SceneProcessor {
|
|
|
449
450
|
numBins: this.bvhBuilder.numBins,
|
|
450
451
|
maxBins: this.bvhBuilder.maxBins,
|
|
451
452
|
minBins: this.bvhBuilder.minBins,
|
|
453
|
+
logLevel: workerLogLevel(),
|
|
452
454
|
};
|
|
453
455
|
|
|
454
456
|
const totalTasks = poolTasks.length + parallelTasks.length;
|
|
@@ -487,7 +489,9 @@ export class SceneProcessor {
|
|
|
487
489
|
Promise.all( parallelPromises )
|
|
488
490
|
] );
|
|
489
491
|
|
|
490
|
-
// Store all results
|
|
492
|
+
// Store all results, summing per-mesh split stats for one aggregate BVH line
|
|
493
|
+
const blasStats = { sah: 0, objMed: 0, spatMed: 0, failed: 0, treeletsImproved: 0, treeletsProcessed: 0 };
|
|
494
|
+
|
|
491
495
|
for ( const { m, range, result } of [ ...poolResults, ...parallelResults ] ) {
|
|
492
496
|
|
|
493
497
|
if ( result.reorderedTriangles ) {
|
|
@@ -496,6 +500,18 @@ export class SceneProcessor {
|
|
|
496
500
|
|
|
497
501
|
}
|
|
498
502
|
|
|
503
|
+
const st = result.splitStats;
|
|
504
|
+
if ( st ) {
|
|
505
|
+
|
|
506
|
+
blasStats.sah += st.sahSplits ?? 0;
|
|
507
|
+
blasStats.objMed += st.objectMedianSplits ?? 0;
|
|
508
|
+
blasStats.spatMed += st.spatialMedianSplits ?? 0;
|
|
509
|
+
blasStats.failed += st.failedSplits ?? 0;
|
|
510
|
+
blasStats.treeletsImproved += st.treeletsImproved ?? 0;
|
|
511
|
+
blasStats.treeletsProcessed += st.treeletsProcessed ?? 0;
|
|
512
|
+
|
|
513
|
+
}
|
|
514
|
+
|
|
499
515
|
this.instanceTable.setEntry( {
|
|
500
516
|
meshIndex: m,
|
|
501
517
|
blasNodeCount: result.bvhData.length / 16,
|
|
@@ -552,7 +568,15 @@ export class SceneProcessor {
|
|
|
552
568
|
this._disposeRefitWorker();
|
|
553
569
|
|
|
554
570
|
const duration = performance.now() - startTime;
|
|
555
|
-
|
|
571
|
+
// One aggregate line for the whole two-level build; the workers' per-mesh
|
|
572
|
+
// detail sits a level below at `debug`.
|
|
573
|
+
log.debug( fmt.list( [
|
|
574
|
+
`${fmt.n( validEntries.length )} BLASes + TLAS`,
|
|
575
|
+
`${fmt.n( this.bvhData.length / 16 )} nodes`,
|
|
576
|
+
`SAH ${fmt.n( blasStats.sah )} · objMed ${blasStats.objMed} · spatMed ${blasStats.spatMed} · failed ${blasStats.failed}`,
|
|
577
|
+
blasStats.treeletsProcessed ? `treelets ${blasStats.treeletsImproved}/${blasStats.treeletsProcessed} improved` : null,
|
|
578
|
+
fmt.ms( duration ),
|
|
579
|
+
] ) );
|
|
556
580
|
|
|
557
581
|
updateLoading( {
|
|
558
582
|
status: "BVH construction complete",
|
|
@@ -561,7 +585,7 @@ export class SceneProcessor {
|
|
|
561
585
|
|
|
562
586
|
} catch ( error ) {
|
|
563
587
|
|
|
564
|
-
|
|
588
|
+
log.error( 'BVH build failed:', error );
|
|
565
589
|
updateLoading( {
|
|
566
590
|
status: `BVH error: ${error.message}`,
|
|
567
591
|
progress: 75
|
|
@@ -660,6 +684,7 @@ export class SceneProcessor {
|
|
|
660
684
|
numBins: opts.numBins,
|
|
661
685
|
maxBins: opts.maxBins,
|
|
662
686
|
minBins: opts.minBins,
|
|
687
|
+
logLevel: opts.logLevel,
|
|
663
688
|
}, [ meshTriData.buffer ] );
|
|
664
689
|
|
|
665
690
|
};
|
|
@@ -686,6 +711,7 @@ export class SceneProcessor {
|
|
|
686
711
|
bvhData: data.bvhData,
|
|
687
712
|
reorderedTriangles: data.triangles || null,
|
|
688
713
|
originalToBvh: data.originalToBvh || null,
|
|
714
|
+
splitStats: data.treeletStats || null,
|
|
689
715
|
}
|
|
690
716
|
} );
|
|
691
717
|
|
|
@@ -835,7 +861,7 @@ export class SceneProcessor {
|
|
|
835
861
|
|
|
836
862
|
} catch ( error ) {
|
|
837
863
|
|
|
838
|
-
|
|
864
|
+
log.error( 'texture creation failed:', error );
|
|
839
865
|
throw error;
|
|
840
866
|
|
|
841
867
|
}
|
|
@@ -877,7 +903,7 @@ export class SceneProcessor {
|
|
|
877
903
|
if ( seen !== undefined ) return packTextureIndex( bucket, seen );
|
|
878
904
|
if ( lists[ bucket ].length >= STRIDE ) {
|
|
879
905
|
|
|
880
|
-
|
|
906
|
+
log.warn( `texture bucket ${bucket} full (${STRIDE}); dropping a map` );
|
|
881
907
|
return - 1;
|
|
882
908
|
|
|
883
909
|
}
|
|
@@ -1089,7 +1115,7 @@ export class SceneProcessor {
|
|
|
1089
1115
|
|
|
1090
1116
|
} catch ( error ) {
|
|
1091
1117
|
|
|
1092
|
-
|
|
1118
|
+
log.warn( `error disposing ${prop}:`, error );
|
|
1093
1119
|
|
|
1094
1120
|
}
|
|
1095
1121
|
|
|
@@ -1157,7 +1183,7 @@ export class SceneProcessor {
|
|
|
1157
1183
|
|
|
1158
1184
|
} catch ( error ) {
|
|
1159
1185
|
|
|
1160
|
-
|
|
1186
|
+
log.error( 'material rebuild failed:', error );
|
|
1161
1187
|
throw error;
|
|
1162
1188
|
|
|
1163
1189
|
} finally {
|
|
@@ -1492,7 +1518,7 @@ export class SceneProcessor {
|
|
|
1492
1518
|
|
|
1493
1519
|
if ( ! this.triangleData ) {
|
|
1494
1520
|
|
|
1495
|
-
|
|
1521
|
+
log.error( 'failed to get triangle data' );
|
|
1496
1522
|
return false;
|
|
1497
1523
|
|
|
1498
1524
|
}
|
|
@@ -1501,7 +1527,7 @@ export class SceneProcessor {
|
|
|
1501
1527
|
|
|
1502
1528
|
if ( ! this.bvhData ) {
|
|
1503
1529
|
|
|
1504
|
-
|
|
1530
|
+
log.error( 'failed to get BVH data' );
|
|
1505
1531
|
return false;
|
|
1506
1532
|
|
|
1507
1533
|
}
|
|
@@ -1515,7 +1541,7 @@ export class SceneProcessor {
|
|
|
1515
1541
|
|
|
1516
1542
|
} else {
|
|
1517
1543
|
|
|
1518
|
-
|
|
1544
|
+
log.warn( 'no material data, using defaults' );
|
|
1519
1545
|
|
|
1520
1546
|
}
|
|
1521
1547
|
|
|
@@ -1862,7 +1888,7 @@ export class SceneProcessor {
|
|
|
1862
1888
|
|
|
1863
1889
|
if ( data.error ) {
|
|
1864
1890
|
|
|
1865
|
-
|
|
1891
|
+
log.error( `background BLAS rebuild failed (mesh ${meshIdx}):`, data.error );
|
|
1866
1892
|
return;
|
|
1867
1893
|
|
|
1868
1894
|
}
|
|
@@ -1876,7 +1902,7 @@ export class SceneProcessor {
|
|
|
1876
1902
|
|
|
1877
1903
|
worker.onerror = ( err ) => {
|
|
1878
1904
|
|
|
1879
|
-
|
|
1905
|
+
log.error( `background BLAS rebuild worker failed (mesh ${meshIdx}):`, err );
|
|
1880
1906
|
worker.terminate();
|
|
1881
1907
|
this._pendingRebuilds.delete( meshIdx );
|
|
1882
1908
|
|
|
@@ -1948,7 +1974,7 @@ export class SceneProcessor {
|
|
|
1948
1974
|
// for the same triangle set. If it differs, the buffer layout is invalid.
|
|
1949
1975
|
if ( newNodeCount !== entry.blasNodeCount ) {
|
|
1950
1976
|
|
|
1951
|
-
|
|
1977
|
+
log.warn( `background rebuild node count mismatch for mesh ${meshIdx} (${newNodeCount} vs ${entry.blasNodeCount}), skipping swap` );
|
|
1952
1978
|
return;
|
|
1953
1979
|
|
|
1954
1980
|
}
|
|
@@ -11,6 +11,9 @@ import { texture } from 'three/tsl';
|
|
|
11
11
|
import { LinearFilter, DataArrayTexture } from 'three';
|
|
12
12
|
import { setAlphaShadowsUniform } from '../TSL/LightsDirect.js';
|
|
13
13
|
import { setGoboMapsTexture, setIESProfilesTexture } from '../TSL/LightsCore.js';
|
|
14
|
+
import { createLogger } from '../utils/Logger.js';
|
|
15
|
+
|
|
16
|
+
const log = createLogger( 'shader' );
|
|
14
17
|
|
|
15
18
|
export class ShaderBuilder {
|
|
16
19
|
|
|
@@ -43,7 +46,7 @@ export class ShaderBuilder {
|
|
|
43
46
|
if ( stage.goboMaps && nodes.goboMapsTex ) nodes.goboMapsTex.value = stage.goboMaps;
|
|
44
47
|
if ( stage.iesProfiles && nodes.iesProfilesTex ) nodes.iesProfilesTex.value = stage.iesProfiles;
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
log.debug( 'scene textures updated in-place' );
|
|
47
50
|
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import { StorageTexture, RenderTarget } from 'three/webgpu';
|
|
16
16
|
import { RGBAFormat, FloatType, LinearFilter, NearestFilter, Box2, Vector2 } from 'three';
|
|
17
17
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
18
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
19
|
+
|
|
20
|
+
const log = createLogger( 'gpu' );
|
|
18
21
|
|
|
19
22
|
function createWriteStorageTex() {
|
|
20
23
|
|
|
@@ -83,7 +86,7 @@ export class StorageTexturePool {
|
|
|
83
86
|
this.readTarget.textures[ 1 ].name = 'gNormalDepth';
|
|
84
87
|
this.readTarget.textures[ 2 ].name = 'gAlbedo';
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
log.debug( `storage texture pool ${fmt.px( width, height )} — 3 write StorageTextures + 1 MRT RenderTarget` );
|
|
87
90
|
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { BVHBuilder } from '../BVHBuilder.js';
|
|
2
|
+
import { createLogger, fmt, applyWorkerLogLevel } from '../../utils/Logger.js';
|
|
3
|
+
|
|
4
|
+
const log = createLogger( 'bvh' );
|
|
2
5
|
|
|
3
6
|
const FPT = 32; // FLOATS_PER_TRIANGLE
|
|
4
7
|
|
|
@@ -9,6 +12,9 @@ self.onmessage = function ( e ) {
|
|
|
9
12
|
const data = e.data;
|
|
10
13
|
const type = data.type;
|
|
11
14
|
|
|
15
|
+
// Workers get their own globals, so the main thread forwards its level per message.
|
|
16
|
+
applyWorkerLogLevel( data.logLevel );
|
|
17
|
+
|
|
12
18
|
if ( type === 'buildPhase1' ) {
|
|
13
19
|
|
|
14
20
|
handlePhase1( data );
|
|
@@ -100,7 +106,7 @@ function handlePhase1( data ) {
|
|
|
100
106
|
const flattenTime = performance.now() - flattenStart;
|
|
101
107
|
|
|
102
108
|
const totalTime = performance.now() - startTime;
|
|
103
|
-
|
|
109
|
+
log.debug( `phase 1 ${fmt.ms( totalTime )} (init ${fmt.ms( builder.splitStats.initTime )} · morton ${fmt.ms( builder.splitStats.mortonSortTime )} · SAH ${fmt.ms( builder.splitStats.sahBuildTime )} · flatten ${fmt.ms( flattenTime )}) · ${builder.frontierTasks.length} frontier tasks` );
|
|
104
110
|
|
|
105
111
|
self.postMessage( {
|
|
106
112
|
type: 'phase1Result',
|
|
@@ -113,7 +119,7 @@ function handlePhase1( data ) {
|
|
|
113
119
|
|
|
114
120
|
} catch ( error ) {
|
|
115
121
|
|
|
116
|
-
|
|
122
|
+
log.error( 'phase 1 failed:', error );
|
|
117
123
|
self.postMessage( { type: 'error', error: error.message } );
|
|
118
124
|
|
|
119
125
|
}
|
|
@@ -162,7 +168,7 @@ function handleAssemble( data ) {
|
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
const totalTime = performance.now() - startTime;
|
|
165
|
-
|
|
171
|
+
log.debug( `phase 3 assemble + reorder ${fmt.ms( totalTime )} (${fmt.mb( bvhData.byteLength )} BVH)` );
|
|
166
172
|
|
|
167
173
|
self.postMessage( {
|
|
168
174
|
type: 'assembleResult',
|
|
@@ -173,7 +179,7 @@ function handleAssemble( data ) {
|
|
|
173
179
|
|
|
174
180
|
} catch ( error ) {
|
|
175
181
|
|
|
176
|
-
|
|
182
|
+
log.error( 'assembly failed:', error );
|
|
177
183
|
self.postMessage( { type: 'error', error: error.message } );
|
|
178
184
|
|
|
179
185
|
}
|
|
@@ -225,7 +231,7 @@ function handleFullBuild( data ) {
|
|
|
225
231
|
const flattenStart = performance.now();
|
|
226
232
|
const bvhData = builder.flattenBVH( bvhRoot );
|
|
227
233
|
const flattenTime = performance.now() - flattenStart;
|
|
228
|
-
|
|
234
|
+
log.debug( `flatten ${fmt.ms( flattenTime )} (${fmt.mb( bvhData.byteLength )})` );
|
|
229
235
|
|
|
230
236
|
const originalToBvh = builder.originalToBvhMap || null;
|
|
231
237
|
|
|
@@ -261,7 +267,7 @@ function handleFullBuild( data ) {
|
|
|
261
267
|
|
|
262
268
|
} catch ( error ) {
|
|
263
269
|
|
|
264
|
-
|
|
270
|
+
log.error( 'build failed:', error );
|
|
265
271
|
self.postMessage( { error: error.message } );
|
|
266
272
|
|
|
267
273
|
}
|
package/src/RenderSettings.js
CHANGED
|
@@ -18,6 +18,7 @@ const SETTING_ROUTES = {
|
|
|
18
18
|
maxBounces: { uniform: 'maxBounces', reset: true },
|
|
19
19
|
transmissiveBounces: { uniform: 'transmissiveBounces', reset: true },
|
|
20
20
|
maxSubsurfaceSteps: { uniform: 'maxSubsurfaceSteps', reset: true },
|
|
21
|
+
maxTransparentBounces: { uniform: 'maxTransparentBounces', reset: true },
|
|
21
22
|
environmentIntensity: { uniform: 'environmentIntensity', reset: true },
|
|
22
23
|
backgroundIntensity: { uniform: 'backgroundIntensity', reset: true },
|
|
23
24
|
backgroundBlurriness: { uniform: 'backgroundBlurriness', reset: true },
|
|
@@ -43,7 +44,6 @@ const SETTING_ROUTES = {
|
|
|
43
44
|
// Adaptive sampling — whole-frame early-stop (useAdaptiveSampling) + per-pixel freeze (usePixelFreeze)
|
|
44
45
|
useAdaptiveSampling: { uniform: 'useAdaptiveSampling', reset: true },
|
|
45
46
|
noiseThreshold: { uniform: 'noiseThreshold', reset: true },
|
|
46
|
-
darkNoiseFloor: { uniform: 'darkNoiseFloor', reset: true },
|
|
47
47
|
adaptiveMinSamples: { uniform: 'adaptiveMinSamples', reset: true },
|
|
48
48
|
adaptiveStopFraction: { uniform: 'adaptiveStopFraction', reset: true },
|
|
49
49
|
usePixelFreeze: { uniform: 'usePixelFreeze', reset: true },
|
package/src/Stages/PathTracer.js
CHANGED
|
@@ -24,10 +24,13 @@ import {
|
|
|
24
24
|
SORT_GLOBAL_WG_SIZE, SORT_GLOBAL_MAX_BINS,
|
|
25
25
|
} from '../TSL/SortGlobalKernels.js';
|
|
26
26
|
import { ENGINE_DEFAULTS, MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
27
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
27
28
|
import {
|
|
28
29
|
Fn, uint, int, atomicStore, atomicLoad, atomicAdd, instanceIndex, If, Return,
|
|
29
30
|
} from 'three/tsl';
|
|
30
31
|
|
|
32
|
+
const log = createLogger( 'wavefront' );
|
|
33
|
+
|
|
31
34
|
export class PathTracer extends PathTracerStage {
|
|
32
35
|
|
|
33
36
|
constructor( renderer, scene, camera, options = {} ) {
|
|
@@ -117,7 +120,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
117
120
|
this.vramTracker = new VRAMTracker( this.renderer );
|
|
118
121
|
this._registerVRAMProviders();
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
log.debug( 'initialized (wavefront)' );
|
|
121
124
|
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -596,7 +599,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
596
599
|
|
|
597
600
|
} ).catch( ( e ) => {
|
|
598
601
|
|
|
599
|
-
|
|
602
|
+
log.warn( 'bounceCounts readback failed:', e );
|
|
600
603
|
this._readbackPending = false;
|
|
601
604
|
|
|
602
605
|
} );
|
|
@@ -1193,6 +1196,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
1193
1196
|
numSpotLights: this.numSpotLights,
|
|
1194
1197
|
maxBounceCount: this.maxBounces,
|
|
1195
1198
|
maxSubsurfaceSteps: this.maxSubsurfaceSteps,
|
|
1199
|
+
maxTransparentBounces: this.maxTransparentBounces,
|
|
1196
1200
|
transparentBackground: this.transparentBackground,
|
|
1197
1201
|
backgroundIntensity: this.backgroundIntensity,
|
|
1198
1202
|
backgroundColor: this.backgroundColor,
|
|
@@ -1309,7 +1313,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
1309
1313
|
m2BufferRW: m2RW,
|
|
1310
1314
|
useAdaptiveSampling: this.useAdaptiveSampling,
|
|
1311
1315
|
noiseThreshold: this.noiseThreshold,
|
|
1312
|
-
darkNoiseFloor: this.darkNoiseFloor,
|
|
1313
1316
|
adaptiveMinSamples: this.adaptiveMinSamples,
|
|
1314
1317
|
// Tier-2 freeze (stamp + pass-through)
|
|
1315
1318
|
usePixelFreeze: this.usePixelFreeze,
|
|
@@ -1367,7 +1370,15 @@ export class PathTracer extends PathTracerStage {
|
|
|
1367
1370
|
);
|
|
1368
1371
|
|
|
1369
1372
|
this._wavefrontReady = true;
|
|
1370
|
-
|
|
1373
|
+
|
|
1374
|
+
const bufferBytes = ( this._packedBuffers?.totalBytes ?? 0 ) + ( this._queueManager?.totalBytes ?? 0 );
|
|
1375
|
+
|
|
1376
|
+
log.info( fmt.list( [
|
|
1377
|
+
fmt.px( w, h ),
|
|
1378
|
+
`${fmt.mb( bufferBytes )} wavefront buffers`,
|
|
1379
|
+
`budget ${fmt.n( B )} paths`,
|
|
1380
|
+
this._numChunks > 1 ? `${this._numChunks} chunks of ≤${this._chunkRows} rows` : null,
|
|
1381
|
+
] ) );
|
|
1371
1382
|
|
|
1372
1383
|
}
|
|
1373
1384
|
|