rayzee 11.2.0 → 11.3.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 +62 -33
- package/dist/ArchiveImporter-D735xX-4.js +7860 -0
- package/dist/BidirectionalIntegrator-M89c0AlP.js +954 -0
- package/dist/{BufferLayout-D_IBo2ci.js → BufferLayout-xPRH9jYD.js} +41 -5
- package/dist/{NeuralRetouch-DFGbnmLb.js → NeuralRetouch-DiBH63kz.js} +1 -1
- package/dist/{NeuralSuperRes-BtXvyJQl.js → NeuralSuperRes-CZNQP13W.js} +1 -1
- package/dist/{PathTracer-BHXGJtPN.js → PathTracer-BXCCVrPK.js} +4371 -4612
- package/dist/{PhysicalSky-DN6u8UX4.js → PhysicalSky-DqmaS0lb.js} +2 -2
- package/dist/{RayzeeRenderer-Dw1x8mVL.js → RayzeeRenderer-DUSMFhmc.js} +426 -363
- package/dist/SolarLimb-bZE6y63S.js +762 -0
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +472 -468
- package/dist/rayzee.umd.js +106 -105
- package/dist/sceneCachePolicy-BAib5fCp.js +1602 -0
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -1
- package/src/PathTracerApp.js +4 -3
- package/src/Processor/ArchiveImporter.js +173 -94
- package/src/Processor/ArchiveReader.js +20 -23
- package/src/Processor/AssetLoader.js +48 -1
- package/src/Processor/BVHRefitter.js +109 -13
- package/src/Processor/BufferLayout.js +115 -0
- package/src/Processor/ChunkedRecords.js +3 -2
- package/src/Processor/EmissiveTriangleBuilder.js +6 -6
- package/src/Processor/GeometryExtractor.js +189 -79
- package/src/Processor/HostMemory.js +39 -9
- package/src/Processor/InstanceTable.js +714 -42
- package/src/Processor/PBRT/PBRTCurves.js +224 -6
- package/src/Processor/PBRT/PBRTMaterials.js +3 -0
- package/src/Processor/PBRT/PBRTSceneBuilder.js +82 -25
- package/src/Processor/PackedRayBuffer.js +5 -2
- package/src/Processor/SceneProcessor.js +747 -141
- package/src/Processor/TLASBuilder.js +32 -17
- package/src/Processor/USD/USDLayer.js +1149 -0
- package/src/Processor/USD/USDScene.js +1655 -0
- package/src/Processor/USD/USDStage.js +682 -0
- package/src/Processor/USD/USDText.js +674 -0
- package/src/Processor/USD/index.js +138 -0
- package/src/Processor/Workers/BVHRefitWorker.js +3 -3
- package/src/RayzeeRenderer.js +168 -27
- package/src/Stages/NormalDepth.js +7 -12
- package/src/Stages/PathTracer.js +12 -17
- package/src/Stages/PathTracerStage.js +273 -96
- package/src/Storage/GeometrySpill.js +46 -14
- package/src/Storage/SceneGraphCodec.js +17 -3
- package/src/Storage/SpillStore.js +9 -0
- package/src/TSL/BVHTraversal.js +183 -7
- package/src/TSL/Common.js +57 -13
- package/src/TSL/ShadeKernel.js +3 -2
- package/src/TSL/TextureSampling.js +11 -8
- package/src/TSL/patches.js +84 -0
- package/src/integrators/BidirectionalIntegrator.js +20 -2
- package/src/managers/DenoisingManager.js +4 -4
- package/src/managers/InteractionManager.js +19 -6
- package/src/managers/MaterialDataManager.js +22 -5
- package/src/managers/UniformManager.js +3 -0
- package/dist/ArchiveImporter-eNlu0_6F.js +0 -5198
- package/dist/BidirectionalIntegrator-D1-6UySB.js +0 -946
- package/dist/SolarLimb-CPaeZVwz.js +0 -721
- package/dist/sceneCachePolicy-DA7iGoL4.js +0 -485
|
@@ -148,6 +148,26 @@ export function geometryBytesOf( object ) {
|
|
|
148
148
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/** Bytes of InstancedMesh matrix lists under `object`, each list once however many meshes share it. */
|
|
152
|
+
export function instanceBytesOf( object ) {
|
|
153
|
+
|
|
154
|
+
let bytes = 0;
|
|
155
|
+
const counted = new Set();
|
|
156
|
+
object.traverse?.( node => {
|
|
157
|
+
|
|
158
|
+
const array = node.isInstancedMesh ? node.instanceMatrix?.array : null;
|
|
159
|
+
if ( array && ! counted.has( array ) ) {
|
|
160
|
+
|
|
161
|
+
counted.add( array );
|
|
162
|
+
bytes += array.byteLength;
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
} );
|
|
167
|
+
return bytes;
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
151
171
|
// Affine point and linear direction transforms straight off the matrix elements: no perspective
|
|
152
172
|
// divide, no method dispatch, on the hottest per-vertex path of a baked mesh.
|
|
153
173
|
function affinePoint( v, e ) {
|
|
@@ -187,6 +207,54 @@ function determinant3( e ) {
|
|
|
187
207
|
|
|
188
208
|
}
|
|
189
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Whether any material under `object` holds a texture, so the GPU needs the triangles' texture coordinates. A scene
|
|
212
|
+
* with none (Moana's USD: its Ptex is baked into colours) is uploaded without them.
|
|
213
|
+
*/
|
|
214
|
+
export function usesTextureCoordinates( object ) {
|
|
215
|
+
|
|
216
|
+
let uses = false;
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
object.traverse( node => {
|
|
219
|
+
|
|
220
|
+
if ( uses || ! node.isMesh || ! node.material ) return;
|
|
221
|
+
for ( const material of Array.isArray( node.material ) ? node.material : [ node.material ] ) {
|
|
222
|
+
|
|
223
|
+
if ( seen.has( material ) ) continue;
|
|
224
|
+
seen.add( material );
|
|
225
|
+
for ( const key in material ) if ( material[ key ]?.isTexture ) uses = true;
|
|
226
|
+
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
} );
|
|
230
|
+
return uses;
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Stores a geometry's float attribute in [−1, 1] as normalized 16-bit integers, which three.js reads back as floats.
|
|
236
|
+
* Left as floats when out of that range (or NaN) rather than silently clamped, or when morph targets use it.
|
|
237
|
+
*/
|
|
238
|
+
export function packUnitAttribute( g, name ) {
|
|
239
|
+
|
|
240
|
+
const attr = g.getAttribute( name );
|
|
241
|
+
if ( ! attr || attr.normalized || ! ( attr.array instanceof Float32Array ) ) return;
|
|
242
|
+
if ( g.morphAttributes?.[ name ]?.length ) return;
|
|
243
|
+
|
|
244
|
+
const src = attr.array;
|
|
245
|
+
for ( let i = 0; i < src.length; i ++ ) {
|
|
246
|
+
|
|
247
|
+
const v = src[ i ];
|
|
248
|
+
if ( ! ( v >= - 1.0001 && v <= 1.0001 ) ) return;
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const packed = new Int16Array( src.length );
|
|
253
|
+
for ( let i = 0; i < src.length; i ++ ) packed[ i ] = Math.round( Math.max( - 1, Math.min( 1, src[ i ] ) ) * 32767 );
|
|
254
|
+
g.setAttribute( name, new BufferAttribute( packed, attr.itemSize, true ) );
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
190
258
|
export class GeometryExtractor {
|
|
191
259
|
|
|
192
260
|
/** @param {{issues?: import('../EngineIssues.js').IssueLog}} [options] */
|
|
@@ -252,7 +320,6 @@ export class GeometryExtractor {
|
|
|
252
320
|
|
|
253
321
|
// Single traversal: extract geometry, materials, lights, and cameras
|
|
254
322
|
this.traverseObject( object );
|
|
255
|
-
this._shareInstanceMatrices();
|
|
256
323
|
this._compressAttributes( object );
|
|
257
324
|
|
|
258
325
|
this.logStats();
|
|
@@ -307,7 +374,6 @@ export class GeometryExtractor {
|
|
|
307
374
|
|
|
308
375
|
}
|
|
309
376
|
|
|
310
|
-
this._shareInstanceMatrices();
|
|
311
377
|
this._compressAttributes( object );
|
|
312
378
|
this.triangles.trimInPlace( this.currentTriangleIndex );
|
|
313
379
|
this._streamedStore = true;
|
|
@@ -448,6 +514,7 @@ export class GeometryExtractor {
|
|
|
448
514
|
placements: this._countPlacements( object ),
|
|
449
515
|
meshes,
|
|
450
516
|
geometryBytes: geometryBytesOf( object ),
|
|
517
|
+
instanceBytes: instanceBytesOf( object ),
|
|
451
518
|
};
|
|
452
519
|
|
|
453
520
|
}
|
|
@@ -502,9 +569,71 @@ export class GeometryExtractor {
|
|
|
502
569
|
this.instanceCount = 0;
|
|
503
570
|
this.instanceCapacity = count;
|
|
504
571
|
this.instanceSource = new Int32Array( count );
|
|
505
|
-
|
|
506
|
-
|
|
572
|
+
// Placement transforms as runs of rows (InstanceTable): an InstancedMesh's own matrix list read where it is,
|
|
573
|
+
// or rows of the extractor's own. A copy of every list was 3 GB beside them on the Moana island.
|
|
574
|
+
this._runs = { first: [], arrays: [], arrayOf: [], base: [], owned: [] };
|
|
575
|
+
this._runArrayIndex = new Map();
|
|
576
|
+
this._singles = null;
|
|
577
|
+
this._singlesUsed = 0;
|
|
578
|
+
this._singlesRun = null;
|
|
507
579
|
this._compressed = new Set();
|
|
580
|
+
this._matrixLists = new Map();
|
|
581
|
+
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** Starts a run of rows at the next placement. @private */
|
|
585
|
+
_beginRun( array, base, owned ) {
|
|
586
|
+
|
|
587
|
+
const runs = this._runs;
|
|
588
|
+
let a = this._runArrayIndex.get( array );
|
|
589
|
+
if ( a === undefined ) this._runArrayIndex.set( array, a = runs.arrays.push( array ) - 1 );
|
|
590
|
+
runs.first.push( this.instanceCount );
|
|
591
|
+
runs.arrayOf.push( a );
|
|
592
|
+
runs.base.push( base );
|
|
593
|
+
runs.owned.push( owned );
|
|
594
|
+
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** The row for a placement of a mesh without instances, appended to the extractor's own array. @private */
|
|
598
|
+
_singleRow() {
|
|
599
|
+
|
|
600
|
+
const runs = this._runs;
|
|
601
|
+
if ( ! this._singles ) {
|
|
602
|
+
|
|
603
|
+
this._singles = new Float32Array( 1024 * 16 );
|
|
604
|
+
this._singlesIndex = runs.arrays.push( this._singles ) - 1;
|
|
605
|
+
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if ( this._singlesUsed + 16 > this._singles.length ) {
|
|
609
|
+
|
|
610
|
+
const grown = new Float32Array( this._singles.length * 2 );
|
|
611
|
+
grown.set( this._singles );
|
|
612
|
+
this._singles = runs.arrays[ this._singlesIndex ] = grown;
|
|
613
|
+
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if ( this._singlesRun !== runs.first.length - 1 ) {
|
|
617
|
+
|
|
618
|
+
runs.first.push( this.instanceCount );
|
|
619
|
+
runs.arrayOf.push( this._singlesIndex );
|
|
620
|
+
runs.base.push( this._singlesUsed );
|
|
621
|
+
runs.owned.push( 1 );
|
|
622
|
+
this._singlesRun = runs.first.length - 1;
|
|
623
|
+
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const o = this._singlesUsed;
|
|
627
|
+
this._singlesUsed += 16;
|
|
628
|
+
return o;
|
|
629
|
+
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** The runs, closed at the placement count (InstanceTable.allocate). @private */
|
|
633
|
+
_matrixRuns() {
|
|
634
|
+
|
|
635
|
+
const runs = this._runs;
|
|
636
|
+
return { first: [ ...runs.first, this.instanceCount ], arrays: runs.arrays, arrayOf: runs.arrayOf, base: runs.base, owned: runs.owned };
|
|
508
637
|
|
|
509
638
|
}
|
|
510
639
|
|
|
@@ -543,59 +672,7 @@ export class GeometryExtractor {
|
|
|
543
672
|
if ( this._compressed.has( g.uuid ) ) return;
|
|
544
673
|
this._compressed.add( g.uuid );
|
|
545
674
|
|
|
546
|
-
for ( const name of [ 'normal', 'tangent', 'color' ] )
|
|
547
|
-
|
|
548
|
-
const attr = g.getAttribute( name );
|
|
549
|
-
if ( ! attr || attr.normalized || ! ( attr.array instanceof Float32Array ) ) continue;
|
|
550
|
-
if ( g.morphAttributes?.[ name ]?.length ) continue;
|
|
551
|
-
|
|
552
|
-
const src = attr.array;
|
|
553
|
-
let ok = true;
|
|
554
|
-
for ( let i = 0; i < src.length; i ++ ) {
|
|
555
|
-
|
|
556
|
-
const v = src[ i ];
|
|
557
|
-
if ( ! ( v >= - 1.0001 && v <= 1.0001 ) ) {
|
|
558
|
-
|
|
559
|
-
ok = false; break;
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Out of snorm range (or NaN) — leave it as floats rather than silently clamp.
|
|
566
|
-
if ( ! ok ) continue;
|
|
567
|
-
|
|
568
|
-
const packed = new Int16Array( src.length );
|
|
569
|
-
for ( let i = 0; i < src.length; i ++ ) {
|
|
570
|
-
|
|
571
|
-
packed[ i ] = Math.round( Math.max( - 1, Math.min( 1, src[ i ] ) ) * 32767 );
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
g.setAttribute( name, new BufferAttribute( packed, attr.itemSize, true ) );
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Point each InstancedMesh's matrix attribute at the placement pool that duplicates it.
|
|
583
|
-
*
|
|
584
|
-
* With the host at the origin the two hold identical bytes — 366 MB apart at 6M instances.
|
|
585
|
-
* Runs after the traversal, since growth reallocates the pool.
|
|
586
|
-
* @private
|
|
587
|
-
*/
|
|
588
|
-
_shareInstanceMatrices() {
|
|
589
|
-
|
|
590
|
-
for ( const { mesh, start, count } of this._shareable ) {
|
|
591
|
-
|
|
592
|
-
const attr = mesh.instanceMatrix;
|
|
593
|
-
if ( ! attr || attr.array.length !== count * 16 ) continue;
|
|
594
|
-
attr.array = this.instanceMatrices.subarray( start * 16, ( start + count ) * 16 );
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
this._shareable = [];
|
|
675
|
+
for ( const name of [ 'normal', 'tangent', 'color' ] ) packUnitAttribute( g, name );
|
|
599
676
|
|
|
600
677
|
}
|
|
601
678
|
|
|
@@ -755,7 +832,7 @@ export class GeometryExtractor {
|
|
|
755
832
|
};
|
|
756
833
|
this.expandedTriangleCount += shared.count;
|
|
757
834
|
this.meshTriangleRanges.push( range );
|
|
758
|
-
this._recordPlacements( mesh, meshIndex );
|
|
835
|
+
this._recordPlacements( mesh, meshIndex, false, false, shared.count > 0 );
|
|
759
836
|
return;
|
|
760
837
|
|
|
761
838
|
}
|
|
@@ -785,7 +862,7 @@ export class GeometryExtractor {
|
|
|
785
862
|
this.expandedTriangleCount += range.count;
|
|
786
863
|
this.meshTriangleRanges.push( range );
|
|
787
864
|
if ( range.count > 0 && ! bake && shareable ) this._geometryRanges.set( key, { ...range, meshIndex } );
|
|
788
|
-
this._recordPlacements( mesh, meshIndex, bake, expand );
|
|
865
|
+
this._recordPlacements( mesh, meshIndex, bake, expand, range.count > 0 && shareable );
|
|
789
866
|
if ( range.count > 0 ) this._onRange?.( meshIndex, range );
|
|
790
867
|
|
|
791
868
|
}
|
|
@@ -796,10 +873,9 @@ export class GeometryExtractor {
|
|
|
796
873
|
* a matrix each rather than a million Object3Ds.
|
|
797
874
|
* @private
|
|
798
875
|
*/
|
|
799
|
-
_recordPlacements( mesh, meshIndex, baked = false, expanded = false ) {
|
|
876
|
+
_recordPlacements( mesh, meshIndex, baked = false, expanded = false, groupable = false ) {
|
|
800
877
|
|
|
801
878
|
const world = mesh.matrixWorld.elements;
|
|
802
|
-
const dst = this.instanceMatrices;
|
|
803
879
|
const src = this.instanceSource;
|
|
804
880
|
|
|
805
881
|
// Expanded instances live in the triangles, so the mesh keeps a single placement. The
|
|
@@ -807,6 +883,8 @@ export class GeometryExtractor {
|
|
|
807
883
|
// every instance equally, which is exactly what composing against it produces.
|
|
808
884
|
if ( ! mesh.isInstancedMesh || ! mesh.instanceMatrix || expanded ) {
|
|
809
885
|
|
|
886
|
+
const d = this._singleRow();
|
|
887
|
+
const dst = this._singles;
|
|
810
888
|
const p = this._nextPlacement();
|
|
811
889
|
src[ p ] = meshIndex;
|
|
812
890
|
|
|
@@ -814,12 +892,12 @@ export class GeometryExtractor {
|
|
|
814
892
|
|
|
815
893
|
// The triangles already carry this pose, so the placement starts at identity. A
|
|
816
894
|
// later move composes against the inverse of what was baked in.
|
|
817
|
-
for ( let k = 0; k < 16; k ++ ) dst[
|
|
895
|
+
for ( let k = 0; k < 16; k ++ ) dst[ d + k ] = k % 5 === 0 ? 1 : 0;
|
|
818
896
|
this.bakeInverse.set( meshIndex, Float32Array.from( this._matrixPool.mat4.copy( mesh.matrixWorld ).invert().elements ) );
|
|
819
897
|
|
|
820
898
|
} else {
|
|
821
899
|
|
|
822
|
-
for ( let k = 0; k < 16; k ++ ) dst[
|
|
900
|
+
for ( let k = 0; k < 16; k ++ ) dst[ d + k ] = world[ k ];
|
|
823
901
|
|
|
824
902
|
}
|
|
825
903
|
|
|
@@ -829,27 +907,28 @@ export class GeometryExtractor {
|
|
|
829
907
|
|
|
830
908
|
const arr = mesh.instanceMatrix.array;
|
|
831
909
|
const count = mesh.count ?? ( arr.length / 16 );
|
|
832
|
-
|
|
833
|
-
const identityHost = isIdentityElements( world );
|
|
834
|
-
if ( identityHost && arr.length === count * 16 ) {
|
|
910
|
+
if ( groupable && count > 0 ) {
|
|
835
911
|
|
|
836
|
-
this.
|
|
912
|
+
let list = this._matrixLists.get( mesh.instanceMatrix );
|
|
913
|
+
if ( ! list ) this._matrixLists.set( mesh.instanceMatrix, list = [] );
|
|
914
|
+
list.push( { meshIndex, start: this.instanceCount, count, world: Float32Array.from( world ) } );
|
|
837
915
|
|
|
838
916
|
}
|
|
839
917
|
|
|
918
|
+
if ( count <= 0 ) return;
|
|
919
|
+
|
|
920
|
+
// A pbrt archive bakes the CTM into every instance, so the host needs no multiply and the list is read in place.
|
|
921
|
+
const identityHost = isIdentityElements( world ) && arr.length >= count * 16;
|
|
922
|
+
const dst = identityHost ? arr : new Float32Array( count * 16 );
|
|
923
|
+
this._beginRun( identityHost ? mesh.instanceMatrix : dst, 0, identityHost ? 0 : 1 );
|
|
924
|
+
|
|
840
925
|
for ( let i = 0; i < count; i ++ ) {
|
|
841
926
|
|
|
842
927
|
const o = i * 16;
|
|
843
928
|
const p = this._nextPlacement();
|
|
844
|
-
const d =
|
|
929
|
+
const d = i * 16;
|
|
845
930
|
src[ p ] = meshIndex;
|
|
846
|
-
|
|
847
|
-
if ( identityHost ) {
|
|
848
|
-
|
|
849
|
-
for ( let k = 0; k < 16; k ++ ) dst[ d + k ] = arr[ o + k ];
|
|
850
|
-
continue;
|
|
851
|
-
|
|
852
|
-
}
|
|
931
|
+
if ( identityHost ) continue;
|
|
853
932
|
|
|
854
933
|
// world = mesh.matrixWorld * instanceMatrix, both column-major.
|
|
855
934
|
for ( let c = 0; c < 4; c ++ ) {
|
|
@@ -869,6 +948,39 @@ export class GeometryExtractor {
|
|
|
869
948
|
|
|
870
949
|
}
|
|
871
950
|
|
|
951
|
+
/**
|
|
952
|
+
* Instanced meshes sharing one matrix attribute under the same host transform: one object traced once a copy
|
|
953
|
+
* (InstanceTable.setGroups). A scene builder shares the attribute between a template's shapes for this.
|
|
954
|
+
* @returns {Array<{members: number[], starts: number[], count: number}>}
|
|
955
|
+
* @private
|
|
956
|
+
*/
|
|
957
|
+
_instanceGroups() {
|
|
958
|
+
|
|
959
|
+
const groups = [];
|
|
960
|
+
for ( const list of this._matrixLists.values() ) {
|
|
961
|
+
|
|
962
|
+
const open = [];
|
|
963
|
+
for ( const member of list ) {
|
|
964
|
+
|
|
965
|
+
const group = open.find( g => g.count === member.count && g.world.every( ( v, k ) => v === member.world[ k ] ) );
|
|
966
|
+
if ( group ) {
|
|
967
|
+
|
|
968
|
+
group.members.push( member.meshIndex );
|
|
969
|
+
group.starts.push( member.start );
|
|
970
|
+
|
|
971
|
+
} else open.push( { world: member.world, count: member.count, members: [ member.meshIndex ], starts: [ member.start ] } );
|
|
972
|
+
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
for ( const g of open ) if ( g.members.length >= 2 ) groups.push( { members: g.members, starts: g.starts, count: g.count } );
|
|
976
|
+
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
this._matrixLists.clear();
|
|
980
|
+
return groups;
|
|
981
|
+
|
|
982
|
+
}
|
|
983
|
+
|
|
872
984
|
/** Next free placement slot, growing only if the pre-count was short. @private */
|
|
873
985
|
_nextPlacement() {
|
|
874
986
|
|
|
@@ -876,11 +988,8 @@ export class GeometryExtractor {
|
|
|
876
988
|
|
|
877
989
|
const grown = Math.max( 16, this.instanceCapacity * 2 );
|
|
878
990
|
const srcCol = new Int32Array( grown );
|
|
879
|
-
const matCol = new Float32Array( grown * 16 );
|
|
880
991
|
srcCol.set( this.instanceSource );
|
|
881
|
-
matCol.set( this.instanceMatrices );
|
|
882
992
|
this.instanceSource = srcCol;
|
|
883
|
-
this.instanceMatrices = matCol;
|
|
884
993
|
this.instanceCapacity = grown;
|
|
885
994
|
|
|
886
995
|
}
|
|
@@ -1566,8 +1675,9 @@ export class GeometryExtractor {
|
|
|
1566
1675
|
meshTriangleRanges: this.meshTriangleRanges, // Per-mesh { start, count } for TLAS/BLAS
|
|
1567
1676
|
expandedTriangleCount: this.expandedTriangleCount,
|
|
1568
1677
|
instanceSource: this.instanceSource.subarray( 0, this.instanceCount ),
|
|
1569
|
-
|
|
1678
|
+
matrixRuns: this._matrixRuns(),
|
|
1570
1679
|
instanceCount: this.instanceCount,
|
|
1680
|
+
instanceGroups: this._instanceGroups(),
|
|
1571
1681
|
// meshIndex -> inverse of the pose baked into its triangles; only baked meshes appear.
|
|
1572
1682
|
bakeInverse: this.bakeInverse,
|
|
1573
1683
|
maps: this.maps,
|
|
@@ -29,8 +29,17 @@ const BVH_NODE_BYTES = 64;
|
|
|
29
29
|
*/
|
|
30
30
|
const NODES_PER_TRIANGLE = 0.9;
|
|
31
31
|
|
|
32
|
-
/**
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Bytes per placement in the instance table's own columns, measured on the Moana island (1,415 MB for 51.0M). Its
|
|
34
|
+
* transforms are read where the scene keeps them (an InstancedMesh's matrix list, `instanceBytes`), not copied.
|
|
35
|
+
*/
|
|
36
|
+
const PLACEMENT_BYTES = 28;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Bytes per placement while a spilling build makes its TLAS: the tree, a quarter of it per copy once copies are
|
|
40
|
+
* clustered, plus the boxes it is built from. Measured on the island: ~2.3 GB at 51.0M.
|
|
41
|
+
*/
|
|
42
|
+
const TLAS_PHASE_BYTES = 45;
|
|
34
43
|
|
|
35
44
|
/** Bytes per triangle of per-template reorder map, one Uint32 each. */
|
|
36
45
|
const ORDER_MAP_BYTES = 4;
|
|
@@ -59,11 +68,14 @@ export const SAFE_SCENE_BYTES = 7040 * 1024 * 1024;
|
|
|
59
68
|
*/
|
|
60
69
|
export const MAX_SCENE_BYTES = 9216 * 1024 * 1024;
|
|
61
70
|
|
|
62
|
-
// Scene-archive budgets with the memory spill on: the triangles and
|
|
63
|
-
// passes these instead of the importer's own. The whole
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
export const
|
|
71
|
+
// Scene-archive budgets with the memory spill on: the triangles, BVH and geometry leave the heap as the build goes, so
|
|
72
|
+
// the host passes these instead of the importer's own. The whole USD island (111.6M triangles, 51.0M placements) loads
|
|
73
|
+
// under them in Chrome on a 24 GB M-series: 304 s, tab peak ~13 GB, 13 GB of GPU buffers. The preflight, not the
|
|
74
|
+
// budget, refuses anything larger.
|
|
75
|
+
export const SPILL_TRIANGLE_BUDGET = 120_000_000;
|
|
76
|
+
export const SPILL_PLACEMENT_BUDGET = 60_000_000;
|
|
77
|
+
/** Widest environment kept with memory spill on; a wider one is box-filtered down (AssetLoader). */
|
|
78
|
+
export const SPILL_ENVIRONMENT_WIDTH = 8192;
|
|
67
79
|
|
|
68
80
|
/**
|
|
69
81
|
* The host's memory in GB: the host's own figure when it gives one, else Chrome's
|
|
@@ -96,21 +108,39 @@ const PROBE_CEILING_BYTES = 12 * 1024 * 1024 * 1024;
|
|
|
96
108
|
* @returns {{total: number, triangles: number, bvh: number, geometry: number,
|
|
97
109
|
* placements: number, orderMaps: number}} bytes
|
|
98
110
|
*/
|
|
99
|
-
export function estimateSceneBytes( { triangles, placements, geometryBytes = 0 } ) {
|
|
111
|
+
export function estimateSceneBytes( { triangles, placements, geometryBytes = 0, instanceBytes = 0 } ) {
|
|
100
112
|
|
|
101
113
|
const parts = {
|
|
102
114
|
triangles: triangles * TRIANGLE_BYTES,
|
|
103
115
|
bvh: Math.ceil( triangles * NODES_PER_TRIANGLE ) * BVH_NODE_BYTES,
|
|
104
116
|
geometry: geometryBytes,
|
|
117
|
+
instances: instanceBytes,
|
|
105
118
|
placements: placements * PLACEMENT_BYTES,
|
|
106
119
|
orderMaps: triangles * ORDER_MAP_BYTES,
|
|
107
120
|
};
|
|
108
121
|
|
|
109
|
-
parts.total = parts.triangles + parts.bvh + parts.geometry + parts.placements + parts.orderMaps;
|
|
122
|
+
parts.total = parts.triangles + parts.bvh + parts.geometry + parts.instances + parts.placements + parts.orderMaps;
|
|
110
123
|
return parts;
|
|
111
124
|
|
|
112
125
|
}
|
|
113
126
|
|
|
127
|
+
/**
|
|
128
|
+
* A spilling, streamed build's peak: the larger of its two phases, since neither the triangles, the BLASes nor the
|
|
129
|
+
* three.js geometry are resident once each has been read. Extraction holds the geometry, the matrix lists and
|
|
130
|
+
* `residentTriangleBytes` of records; the TLAS phase the lists, the table, the tree being built and the order maps.
|
|
131
|
+
* @param {ReturnType<typeof estimateSceneBytes>} parts
|
|
132
|
+
* @param {number} placements
|
|
133
|
+
* @param {number} residentTriangleBytes
|
|
134
|
+
* @returns {number} bytes
|
|
135
|
+
*/
|
|
136
|
+
export function spillingPeakBytes( parts, placements, residentTriangleBytes ) {
|
|
137
|
+
|
|
138
|
+
const extraction = parts.geometry + parts.instances + placements * 4 + residentTriangleBytes;
|
|
139
|
+
const tlas = parts.instances + parts.placements + placements * TLAS_PHASE_BYTES + parts.orderMaps;
|
|
140
|
+
return Math.max( extraction, tlas );
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
114
144
|
/**
|
|
115
145
|
* How many bytes this process can still place in chunk-sized pieces, right now.
|
|
116
146
|
*
|