rayzee 11.2.1 → 11.4.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 +91 -34
- package/dist/ArchiveImporter-Cgyx5AF7.js +7860 -0
- package/dist/BidirectionalIntegrator-BKF-cwH2.js +954 -0
- package/dist/{BufferLayout-D_IBo2ci.js → BufferLayout-xPRH9jYD.js} +41 -5
- package/dist/{NeuralRetouch-Cr-otSW8.js → NeuralRetouch-C3Q2XqbF.js} +4 -3
- package/dist/{NeuralSuperRes-B7E3_M0t.js → NeuralSuperRes-DV5TtURP.js} +11 -5
- package/dist/{PathTracer-D6nbknj5.js → PathTracer-D3MvEaZK.js} +4384 -4618
- package/dist/{PhysicalSky-Dq5VSiCQ.js → PhysicalSky-CY4jiz6a.js} +2 -2
- package/dist/{RayzeeRenderer-DbjqO6v-.js → RayzeeRenderer-D23m6NqG.js} +486 -391
- package/dist/SolarLimb-bZE6y63S.js +762 -0
- package/dist/{ToneMapGPU-BOjpul7A.js → ToneMapGPU-CTDBvmmS.js} +31 -22
- 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 +1327 -838
- package/dist/rayzee.umd.js +146 -134
- package/dist/sceneCachePolicy-AKOEu2lf.js +1602 -0
- package/package.json +1 -1
- package/src/EngineDefaults.js +2 -2
- package/src/Passes/AIUpscaler.js +2 -0
- package/src/PathTracerApp.js +126 -19
- package/src/Pipeline/CompletionTracker.js +7 -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/ToneMapCPU.js +16 -4
- package/src/Processor/ToneMapGPU.js +30 -14
- 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 +221 -33
- package/src/RenderSettings.js +4 -2
- package/src/Stages/AutoExposure.js +484 -494
- package/src/Stages/Compositor.js +27 -6
- package/src/Stages/LocalExposure.js +546 -0
- package/src/Stages/NormalDepth.js +7 -12
- package/src/Stages/PathTracer.js +18 -21
- package/src/Stages/PathTracerStage.js +282 -103
- 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/index.js +1 -0
- package/src/integrators/BidirectionalIntegrator.js +20 -2
- package/src/managers/DenoisingManager.js +15 -19
- package/src/managers/InteractionManager.js +19 -6
- package/src/managers/MaterialDataManager.js +22 -5
- package/src/managers/UniformManager.js +3 -0
- package/src/managers/VideoRenderManager.js +30 -4
- package/src/neural/NeuralRetouch.js +1 -0
- package/src/neural/NeuralSuperRes.js +6 -1
- package/dist/ArchiveImporter-D_x863yu.js +0 -5198
- package/dist/BidirectionalIntegrator-DmkxxEOR.js +0 -946
- package/dist/SolarLimb-CPaeZVwz.js +0 -721
- package/dist/sceneCachePolicy-C4-4qwt6.js +0 -485
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USD scene loader for multi-file scenes in a folder or archive: layers are read only as composition reaches them, and
|
|
3
|
+
* the stage is built by the pbrt importer's builder, so instancing, budgets and curve tessellation are shared.
|
|
4
|
+
*
|
|
5
|
+
* const files = new USDFiles( paths, read );
|
|
6
|
+
* const { group, environment, cameras } = await loadUSDScene( { files, entryPath, resolveImage, resolveEnvironment } );
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { PBRTSceneBuilder } from '../PBRT/PBRTSceneBuilder.js';
|
|
10
|
+
import { USDStage } from './USDStage.js';
|
|
11
|
+
import { USDSceneReader } from './USDScene.js';
|
|
12
|
+
|
|
13
|
+
export { USDFiles, USDStage } from './USDStage.js';
|
|
14
|
+
|
|
15
|
+
export const USD_LAYER = /\.(usd|usda|usdc)$/i;
|
|
16
|
+
|
|
17
|
+
/** USD layers that could be a scene's root, best first: the shallowest, then the shortest name. */
|
|
18
|
+
export function listUSDRootLayers( paths ) {
|
|
19
|
+
|
|
20
|
+
const layers = paths.filter( p => USD_LAYER.test( p ) && ! p.split( '/' ).pop().startsWith( '._' ) );
|
|
21
|
+
if ( layers.length === 0 ) return [];
|
|
22
|
+
const depth = p => p.split( '/' ).length;
|
|
23
|
+
const top = Math.min( ...layers.map( depth ) );
|
|
24
|
+
return layers.filter( p => depth( p ) === top ).sort( ( a, b ) => {
|
|
25
|
+
|
|
26
|
+
const na = a.split( '/' ).pop(), nb = b.split( '/' ).pop();
|
|
27
|
+
return na.length - nb.length || ( na < nb ? - 1 : na > nb ? 1 : 0 );
|
|
28
|
+
|
|
29
|
+
} );
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The parts a large scene loads by: prims under the root's top prims that bring in sibling directories (Moana's elements/). */
|
|
34
|
+
export async function listUSDParts( stage, listing ) {
|
|
35
|
+
|
|
36
|
+
const parts = new Map();
|
|
37
|
+
const root = stage.pseudoRoot;
|
|
38
|
+
const rootDir = stage.root.id.includes( '/' ) ? stage.root.id.slice( 0, stage.root.id.lastIndexOf( '/' ) ) : '';
|
|
39
|
+
for ( const top of root.childNames() ) {
|
|
40
|
+
|
|
41
|
+
for ( const [ , spec, layer ] of ( await stage.child( root, top ) )?.sites ?? [] ) {
|
|
42
|
+
|
|
43
|
+
for ( const [ name, child ] of spec.children ?? [] ) {
|
|
44
|
+
|
|
45
|
+
for ( const key of [ 'references', 'payload' ] ) {
|
|
46
|
+
|
|
47
|
+
const op = child.arcs?.[ key ];
|
|
48
|
+
for ( const ref of [ ...( op?.explicit ?? [] ), ...( op?.prepend ?? [] ), ...( op?.append ?? [] ), ...( op?.add ?? [] ) ] ) {
|
|
49
|
+
|
|
50
|
+
const file = stage.files.resolve( ref.asset, layer.path );
|
|
51
|
+
if ( ! file?.includes( '/' ) ) continue;
|
|
52
|
+
const prefix = file.slice( 0, file.lastIndexOf( '/' ) );
|
|
53
|
+
if ( prefix === rootDir || ! prefix.startsWith( rootDir ) ) continue;
|
|
54
|
+
if ( ! parts.has( prefix ) ) parts.set( prefix, { name, prefix, files: 0, bytes: 0, scenes: 1 } );
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A library referenced on its own beside the parts is not one.
|
|
67
|
+
const byParent = new Map();
|
|
68
|
+
for ( const part of parts.values() ) {
|
|
69
|
+
|
|
70
|
+
const parent = part.prefix.slice( 0, part.prefix.lastIndexOf( '/' ) );
|
|
71
|
+
if ( ! byParent.has( parent ) ) byParent.set( parent, [] );
|
|
72
|
+
byParent.get( parent ).push( part );
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const siblings = [ ...byParent.values() ].sort( ( a, b ) => b.length - a.length )[ 0 ] ?? [];
|
|
77
|
+
for ( const part of siblings ) {
|
|
78
|
+
|
|
79
|
+
for ( const entry of listing ) {
|
|
80
|
+
|
|
81
|
+
if ( ! entry.path.startsWith( `${part.prefix}/` ) || ! USD_LAYER.test( entry.path ) ) continue;
|
|
82
|
+
part.files ++;
|
|
83
|
+
part.bytes += entry.size;
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return siblings.sort( ( a, b ) => a.bytes - b.bytes );
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function loadUSDScene( args ) {
|
|
94
|
+
|
|
95
|
+
const warnings = [];
|
|
96
|
+
const warn = message => warnings.push( message );
|
|
97
|
+
const builder = new PBRTSceneBuilder( {
|
|
98
|
+
maxTriangles: args.maxTriangles, maxPlacements: args.maxPlacements, mergeShapesAbove: args.mergeShapesAbove,
|
|
99
|
+
curveSteps: args.curveSteps, curveSides: args.curveSides, curveTolerance: args.curveTolerance,
|
|
100
|
+
} );
|
|
101
|
+
|
|
102
|
+
const parseStart = performance.now();
|
|
103
|
+
const stage = new USDStage( args.files, { warn } );
|
|
104
|
+
await stage.open( args.entryPath );
|
|
105
|
+
const reader = new USDSceneReader( stage, {
|
|
106
|
+
maxTriangles: builder.maxTriangles, maxPlacements: builder.maxPlacements, curveSteps: builder.curveSteps,
|
|
107
|
+
resolveImage: args.resolveImage, warn,
|
|
108
|
+
} );
|
|
109
|
+
const read = await reader.read();
|
|
110
|
+
if ( stage.omittedArcs > 0 ) warn( `${stage.omittedArcs} reference(s) into the parts left out of this load skipped` );
|
|
111
|
+
const parseMs = performance.now() - parseStart;
|
|
112
|
+
|
|
113
|
+
const buildStart = performance.now();
|
|
114
|
+
const built = await builder.build( read.ir );
|
|
115
|
+
for ( const object of [ ...read.cameras, ...read.lights ] ) built.group.add( object );
|
|
116
|
+
|
|
117
|
+
let environment = null;
|
|
118
|
+
if ( read.dome ) {
|
|
119
|
+
|
|
120
|
+
const texture = await args.resolveEnvironment( read.dome.file );
|
|
121
|
+
if ( texture ) environment = { texture, intensity: read.dome.intensity, rotation: read.dome.rotation };
|
|
122
|
+
else warn( `USD dome light texture "${read.dome.file}" could not be read` );
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
...built,
|
|
128
|
+
environment,
|
|
129
|
+
cameras: read.cameras,
|
|
130
|
+
lights: read.lights,
|
|
131
|
+
cost: read.cost,
|
|
132
|
+
fitNote: reader.fitNote,
|
|
133
|
+
warnings: [ ...warnings, ...built.warnings ],
|
|
134
|
+
parseMs,
|
|
135
|
+
buildMs: performance.now() - buildStart,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
}
|
|
@@ -46,9 +46,9 @@ self.onmessage = function ( e ) {
|
|
|
46
46
|
|
|
47
47
|
const startTime = performance.now();
|
|
48
48
|
|
|
49
|
-
const { blasRanges, tlasNodeCount } = e.data;
|
|
50
|
-
if ( blasRanges ) refitter.refitPartial( bvhData, triData, blasRanges, tlasNodeCount );
|
|
51
|
-
else refitter.refit( bvhData, triData, nodeCount );
|
|
49
|
+
const { blasRanges, tlasNodeCount, groupRange, treeNodeCount, recordNodeStart = - 1 } = e.data;
|
|
50
|
+
if ( blasRanges ) refitter.refitPartial( bvhData, triData, blasRanges, tlasNodeCount, groupRange, recordNodeStart );
|
|
51
|
+
else refitter.refit( bvhData, triData, treeNodeCount ?? nodeCount, recordNodeStart );
|
|
52
52
|
|
|
53
53
|
self.postMessage( {
|
|
54
54
|
type: 'refitComplete',
|
package/src/RayzeeRenderer.js
CHANGED
|
@@ -11,6 +11,7 @@ import { RenderPipeline } from './Pipeline/RenderPipeline.js';
|
|
|
11
11
|
import { CompletionTracker } from './Pipeline/CompletionTracker.js';
|
|
12
12
|
import { ENGINE_DEFAULTS as DEFAULT_STATE } from './EngineDefaults.js';
|
|
13
13
|
import { TRIANGLE_DATA_LAYOUT, BVH_LEAF_MARKERS } from './Processor/BufferLayout.js';
|
|
14
|
+
import { usesTextureCoordinates } from './Processor/GeometryExtractor.js';
|
|
14
15
|
import { MAX_STORAGE_TEXTURE_SIZE, MAX_RESERVABLE_RENDER_SIZE, setReservedRenderSize } from './Processor/StorageTexturePool.js';
|
|
15
16
|
import { updateStats, updateLoading, resetLoading, setStatusCallback, getDisplaySamples, disposeObjectFromMemory, disposeRenderer } from './Processor/utils.js';
|
|
16
17
|
import { BuildTimer } from './Processor/BuildTimer.js';
|
|
@@ -28,9 +29,9 @@ import { PackedToneMapper } from './Processor/ToneMapGPU.js';
|
|
|
28
29
|
import { BasicColor } from './Color/BasicColor.js';
|
|
29
30
|
import { setActiveColorManagement } from './Color/ActiveColor.js';
|
|
30
31
|
import { getViewTransform, DEFAULT_VIEW } from './Color/ViewTransforms.js';
|
|
31
|
-
import { AssetLoader } from './Processor/AssetLoader.js';
|
|
32
|
+
import { AssetLoader, limitEnvironmentWidth } from './Processor/AssetLoader.js';
|
|
32
33
|
import { SceneProcessor } from './Processor/SceneProcessor.js';
|
|
33
|
-
import { deviceMemoryGB, SPILL_TRIANGLE_BUDGET, SPILL_PLACEMENT_BUDGET } from './Processor/HostMemory.js';
|
|
34
|
+
import { deviceMemoryGB, SPILL_TRIANGLE_BUDGET, SPILL_PLACEMENT_BUDGET, SPILL_ENVIRONMENT_WIDTH } from './Processor/HostMemory.js';
|
|
34
35
|
import { createHeadlessCanvas } from './HeadlessCanvas.js';
|
|
35
36
|
|
|
36
37
|
// Managers
|
|
@@ -52,6 +53,8 @@ const partOf = ( { element, pbrtEntry } = {} ) => ( {
|
|
|
52
53
|
// One app per canvas — auto-dispose a prior owner if the caller double-
|
|
53
54
|
// instantiates (StrictMode, HMR, etc.) so its rAF loop can't burn CPU.
|
|
54
55
|
const _appsByCanvas = new WeakMap();
|
|
56
|
+
// Quiet time after an edit before memory spill puts back on disk what the edit read back.
|
|
57
|
+
const RESPILL_AFTER_MS = 30_000;
|
|
55
58
|
|
|
56
59
|
const KNOWN_EVENTS = new Set( [ ...Object.values( EngineEvents ), ...Object.values( LEGACY_EVENT_NAMES ) ] );
|
|
57
60
|
const _warnedEvents = new Set();
|
|
@@ -165,9 +168,11 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
165
168
|
* @param {false|'auto'|Object} [options.storage] - on-disk storage; defaults to
|
|
166
169
|
* `configureAssets( { storage } )`, or off under `strict` unless that was set. 'auto' opens it through
|
|
167
170
|
* {@link setStorageOpener} (rayzee/addons/storage); a host-supplied StorageManager stays the host's to dispose.
|
|
168
|
-
* @param {boolean} [options.memorySpill=
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
+
* @param {'auto'|boolean} [options.memorySpill='auto'] - build a static scene through disk: its triangle records,
|
|
172
|
+
* BLAS nodes and three.js geometry leave memory as they are built (see {@link ensureSceneResident}), scene archives
|
|
173
|
+
* load past the default budgets, and an environment wider than SPILL_ENVIRONMENT_WIDTH is box-filtered down.
|
|
174
|
+
* 'auto' does so only for a scene whose in-memory estimate passes SAFE_SCENE_BYTES; true for every scene, false
|
|
175
|
+
* never. Needs storage; skipped for animated scenes. See {@link setMemorySpill}.
|
|
171
176
|
*
|
|
172
177
|
* The engine dispatches `EngineEvents.FRAME` after each animate() iteration so hosts can
|
|
173
178
|
* tick external instrumentation (e.g. a stats panel) without coupling the engine to it.
|
|
@@ -198,7 +203,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
198
203
|
this._hostMemoryGB = options.hostMemoryGB;
|
|
199
204
|
this._storageOption = options.storage;
|
|
200
205
|
this._storageOpener = null;
|
|
201
|
-
this._memorySpill = options.memorySpill === true;
|
|
206
|
+
this._memorySpill = options.memorySpill === true || options.memorySpill === false ? options.memorySpill : 'auto';
|
|
207
|
+
this._spillPlan = null;
|
|
202
208
|
/** @type {?Object} on-disk storage (a StorageManager), null when off or unavailable */
|
|
203
209
|
this.storage = null;
|
|
204
210
|
this._storageRelease = null;
|
|
@@ -526,6 +532,13 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
526
532
|
|
|
527
533
|
}
|
|
528
534
|
|
|
535
|
+
if ( this._settling() ) {
|
|
536
|
+
|
|
537
|
+
this.dispatchEvent( { type: EngineEvents.FRAME } );
|
|
538
|
+
return;
|
|
539
|
+
|
|
540
|
+
}
|
|
541
|
+
|
|
529
542
|
// Stop the loop to avoid constant CPU usage while idle
|
|
530
543
|
this.stopAnimation();
|
|
531
544
|
return;
|
|
@@ -552,12 +565,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
552
565
|
|
|
553
566
|
this._traceFrame( { liveDenoise: true } );
|
|
554
567
|
|
|
555
|
-
if ( this.stages.pathTracer.isComplete && this.completion.markComplete() )
|
|
556
|
-
|
|
557
|
-
this._renderCompleted();
|
|
558
|
-
this.dispatchEvent( { type: EngineEvents.RENDER_COMPLETE, ...this._completionInfo() } );
|
|
559
|
-
|
|
560
|
-
}
|
|
568
|
+
if ( this.stages.pathTracer.isComplete && this.completion.markComplete() ) this._announceComplete();
|
|
561
569
|
|
|
562
570
|
}
|
|
563
571
|
|
|
@@ -566,6 +574,25 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
566
574
|
|
|
567
575
|
}
|
|
568
576
|
|
|
577
|
+
// The closing passes and RENDER_COMPLETE wait for _finishImage(), so both see the finished image's exposure.
|
|
578
|
+
_announceComplete() {
|
|
579
|
+
|
|
580
|
+
const info = this._completionInfo();
|
|
581
|
+
const restarts = this.stages.pathTracer.resetCount;
|
|
582
|
+
const announce = () => {
|
|
583
|
+
|
|
584
|
+
if ( ! this.completion.renderCompleteDispatched || this.stages.pathTracer?.resetCount !== restarts ) return;
|
|
585
|
+
this._renderCompleted();
|
|
586
|
+
this.dispatchEvent( { type: EngineEvents.RENDER_COMPLETE, ...info } );
|
|
587
|
+
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const finishing = this._finishImage();
|
|
591
|
+
if ( finishing ) finishing.then( announce, announce );
|
|
592
|
+
else announce();
|
|
593
|
+
|
|
594
|
+
}
|
|
595
|
+
|
|
569
596
|
// One frame of a render and its bookkeeping, shared by animate() and renderUntilComplete().
|
|
570
597
|
_traceFrame( { liveDenoise = false } = {} ) {
|
|
571
598
|
|
|
@@ -708,6 +735,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
708
735
|
this.stopAnimation();
|
|
709
736
|
clearTimeout( this._resizeDebounceTimer );
|
|
710
737
|
this._resizeDebounceTimer = null;
|
|
738
|
+
clearTimeout( this._respillTimer );
|
|
739
|
+
this._respillTimer = null;
|
|
711
740
|
|
|
712
741
|
this._removeTrackedListeners();
|
|
713
742
|
setStatusCallback( null );
|
|
@@ -1175,6 +1204,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1175
1204
|
if ( this._loadingInProgress ) throw this._busyError( 'PathTracerApp' );
|
|
1176
1205
|
|
|
1177
1206
|
this._loadingInProgress = true;
|
|
1207
|
+
// The previous scene's sky width must not narrow this one's while it loads.
|
|
1208
|
+
if ( this.assetLoader ) this.assetLoader.maxEnvironmentWidth = this._memorySpill === true ? SPILL_ENVIRONMENT_WIDTH : 0;
|
|
1178
1209
|
|
|
1179
1210
|
try {
|
|
1180
1211
|
|
|
@@ -1189,6 +1220,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1189
1220
|
// (The old primary was already released by releaseTargetModel() in loadFn.)
|
|
1190
1221
|
this._clearAppendedModels();
|
|
1191
1222
|
this._modelReplaced();
|
|
1223
|
+
// Before the scene's own environment starts loading: a spilling scene's is narrowed as it decodes.
|
|
1224
|
+
this._planSpill();
|
|
1192
1225
|
await this.loadSceneData( { pendingEnvironment: this._beginSceneMetadataEnvironment() } );
|
|
1193
1226
|
this._maybeSpill();
|
|
1194
1227
|
this.pipeline?.eventBus.emit( 'pipeline:lightingChanged' );
|
|
@@ -1215,6 +1248,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1215
1248
|
} finally {
|
|
1216
1249
|
|
|
1217
1250
|
this._loadingInProgress = false;
|
|
1251
|
+
this._spillPlan = null;
|
|
1218
1252
|
|
|
1219
1253
|
}
|
|
1220
1254
|
|
|
@@ -1445,11 +1479,22 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1445
1479
|
updateLoading( { status: "Transferring data to GPU...", progress: 86 } );
|
|
1446
1480
|
await new Promise( r => setTimeout( r, 0 ) );
|
|
1447
1481
|
timer.start( 'GPU data transfer' );
|
|
1482
|
+
await this._sdf.uploadChunks( this._chunkUploader() );
|
|
1448
1483
|
|
|
1449
1484
|
// Re-read rather than reusing the snapshot above: a pendingEnvironment may have landed
|
|
1450
1485
|
// during the BVH build, and the snapshot is then a disposed texture.
|
|
1451
1486
|
if ( ! this._sdf.uploadToPathTracer( this.stages.pathTracer, this.lightManager, this.meshScene, hdri(), { keepUserLights } ) ) return false;
|
|
1452
1487
|
|
|
1488
|
+
// A bucket at a time, each awaited (PathTracerStage#drainUploads): left to the first frame, three.js sends every
|
|
1489
|
+
// layer at once, and Chrome keeps a pool that size (724 MB for 24155522.glb).
|
|
1490
|
+
for ( const texture of [ ...( this._sdf.srgbBucketTextures ?? [] ), ...( this._sdf.linearBucketTextures ?? [] ) ] ) {
|
|
1491
|
+
|
|
1492
|
+
if ( ! texture ) continue;
|
|
1493
|
+
this.renderer.initTexture( texture );
|
|
1494
|
+
await this.stages.pathTracer.drainUploads();
|
|
1495
|
+
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1453
1498
|
// Patch per-mesh visibility into the TLAS leaves we just uploaded
|
|
1454
1499
|
this.stages.pathTracer._meshRefs = this.stages.pathTracer._collectMeshRefs( this.meshScene );
|
|
1455
1500
|
this.stages.pathTracer.setMeshVisibilityData( this.stages.pathTracer._meshRefs );
|
|
@@ -1724,43 +1769,124 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1724
1769
|
// Dynamic cameras (add / remove)
|
|
1725
1770
|
// ═══════════════════════════════════════════════════════════════
|
|
1726
1771
|
|
|
1727
|
-
/**
|
|
1772
|
+
/** How scenes use the disk: 'auto', true or false (see the constructor's `memorySpill`). */
|
|
1773
|
+
get memorySpill() {
|
|
1774
|
+
|
|
1775
|
+
return this._memorySpill;
|
|
1776
|
+
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
/** Sets {@link memorySpill}; the next load or rebuild follows it. */
|
|
1780
|
+
setMemorySpill( mode ) {
|
|
1781
|
+
|
|
1782
|
+
this._memorySpill = mode === true || mode === false ? mode : 'auto';
|
|
1783
|
+
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/** Whether the current scene was built through disk. */
|
|
1787
|
+
get sceneSpilled() {
|
|
1788
|
+
|
|
1789
|
+
return !! this._sdf?.spilling;
|
|
1790
|
+
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
/** Whether the model's three.js geometry is on disk, so picking skips it until {@link ensureSceneResident}. */
|
|
1794
|
+
get geometryOnDisk() {
|
|
1795
|
+
|
|
1796
|
+
return this._sdf?.geometryOnDisk === true;
|
|
1797
|
+
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* Triangle and placement budgets for a scene archive: raised whenever a build could spill, since the preflight
|
|
1802
|
+
* refuses what cannot fit either way. @private
|
|
1803
|
+
*/
|
|
1728
1804
|
_sceneBudgets( options ) {
|
|
1729
1805
|
|
|
1730
|
-
if (
|
|
1806
|
+
if ( this._memorySpill === false || ! this.storage ) return options;
|
|
1731
1807
|
return { maxTriangles: SPILL_TRIANGLE_BUDGET, maxPlacements: SPILL_PLACEMENT_BUDGET, ...options };
|
|
1732
1808
|
|
|
1733
1809
|
}
|
|
1734
1810
|
|
|
1735
1811
|
/**
|
|
1736
|
-
*
|
|
1737
|
-
*
|
|
1812
|
+
* Decides whether the scene about to be built spills: static scenes only, since a deforming clip refits every
|
|
1813
|
+
* frame. Held for the build that follows ({@link _progressiveSpill}). @private
|
|
1738
1814
|
*/
|
|
1815
|
+
_planSpill() {
|
|
1816
|
+
|
|
1817
|
+
const offered = this._memorySpill !== false && !! this.storage && ( this.assetLoader?.animations?.length ?? 0 ) === 0;
|
|
1818
|
+
const spill = offered && ( this._memorySpill === true || this._sdf.needsSpill( this.meshScene ) );
|
|
1819
|
+
if ( this.assetLoader ) this.assetLoader.maxEnvironmentWidth = spill ? SPILL_ENVIRONMENT_WIDTH : 0;
|
|
1820
|
+
// An importer installs its own sky at the end of its read, before this.
|
|
1821
|
+
const sky = spill ? this.meshScene?.environment : null;
|
|
1822
|
+
const image = sky?.image;
|
|
1823
|
+
if ( image ) limitEnvironmentWidth( sky, SPILL_ENVIRONMENT_WIDTH );
|
|
1824
|
+
if ( sky && sky.image !== image ) {
|
|
1825
|
+
|
|
1826
|
+
// three.js keeps an uploaded texture's size, so the old one goes.
|
|
1827
|
+
sky.dispose();
|
|
1828
|
+
sky.needsUpdate = true;
|
|
1829
|
+
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
if ( spill && this._memorySpill === 'auto' ) log.info( 'scene is past the in-memory line: building it through disk' );
|
|
1833
|
+
this._spillPlan = spill;
|
|
1834
|
+
return spill;
|
|
1835
|
+
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/** @private */
|
|
1739
1839
|
_progressiveSpill() {
|
|
1740
1840
|
|
|
1741
|
-
|
|
1841
|
+
const spill = this._spillPlan ?? this._planSpill();
|
|
1842
|
+
this._spillPlan = null;
|
|
1843
|
+
return spill ? { storage: this.storage, uploader: this._chunkUploader() } : null;
|
|
1844
|
+
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/** @private */
|
|
1848
|
+
_chunkUploader() {
|
|
1849
|
+
|
|
1742
1850
|
const stage = this.stages.pathTracer;
|
|
1743
|
-
return
|
|
1851
|
+
return ( records, kind, options ) => kind === 'triangles' ? stage.createTriangleChunkUploader( records, options ) : stage.createChunkUploader( records );
|
|
1744
1852
|
|
|
1745
1853
|
}
|
|
1746
1854
|
|
|
1747
|
-
/**
|
|
1855
|
+
/** The rest of a spilling scene's spill, once it is on the GPU, in the background. @private */
|
|
1748
1856
|
_maybeSpill() {
|
|
1749
1857
|
|
|
1750
|
-
if ( ! this.
|
|
1751
|
-
this._sdf
|
|
1858
|
+
if ( ! this._sdf?.spilling || ! this.storage ) return;
|
|
1859
|
+
this._sdf.spillAfterLoad().catch( error => this._issues.warn( ISSUE_CODES.STORAGE_WRITE_FAILED, `memory spill failed: ${error.message}` ) );
|
|
1860
|
+
this._sdf.spillToDisk( this.storage )
|
|
1752
1861
|
.then( result => result && this.dispatchEvent( { type: EngineEvents.SCENE_SPILLED, ...result } ) )
|
|
1753
1862
|
.catch( error => this._issues.warn( ISSUE_CODES.STORAGE_WRITE_FAILED, `memory spill failed: ${error.message}` ) );
|
|
1754
1863
|
|
|
1755
1864
|
}
|
|
1756
1865
|
|
|
1866
|
+
/** Once edits have been quiet for {@link RESPILL_AFTER_MS}, puts back on disk what they read back. @private */
|
|
1867
|
+
_scheduleRespill() {
|
|
1868
|
+
|
|
1869
|
+
if ( ! this._sdf?.spilling ) return;
|
|
1870
|
+
clearTimeout( this._respillTimer );
|
|
1871
|
+
this._respillTimer = setTimeout( () => {
|
|
1872
|
+
|
|
1873
|
+
this._respillTimer = null;
|
|
1874
|
+
this._sdf?.respill().catch( error => this._issues.warn( ISSUE_CODES.STORAGE_WRITE_FAILED, `memory spill failed: ${error.message}` ) );
|
|
1875
|
+
|
|
1876
|
+
}, RESPILL_AFTER_MS );
|
|
1877
|
+
this._respillTimer.unref?.();
|
|
1878
|
+
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1757
1881
|
/**
|
|
1758
|
-
* Reads back what {@link options.memorySpill} moved to disk
|
|
1759
|
-
*
|
|
1882
|
+
* Reads back what {@link options.memorySpill} moved to disk: triangle records, BLAS nodes and the three.js geometry
|
|
1883
|
+
* (whose arrays are empty until then). The engine does this itself before its own readers; a host calling
|
|
1884
|
+
* {@link refitBLASes}, or reading the model's geometry, on a spilled scene awaits it first.
|
|
1760
1885
|
*/
|
|
1761
1886
|
async ensureSceneResident() {
|
|
1762
1887
|
|
|
1763
1888
|
if ( this._sdf?.spilled ) await this._sdf.ensureResident();
|
|
1889
|
+
await this._sdf?.ensureGeometryResident();
|
|
1764
1890
|
|
|
1765
1891
|
}
|
|
1766
1892
|
|
|
@@ -1885,15 +2011,26 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1885
2011
|
* drags along any other object sharing the same geometry.
|
|
1886
2012
|
*
|
|
1887
2013
|
* @param {number[]} meshIndices - indices into {@link sceneMeshes}
|
|
1888
|
-
* @returns {{ refitTimeMs: number, placements: number }}
|
|
2014
|
+
* @returns {?{ refitTimeMs: number, placements: number }} null when the move waits for a spilled scene's data
|
|
1889
2015
|
*/
|
|
1890
2016
|
updateMeshTransforms( meshIndices ) {
|
|
1891
2017
|
|
|
2018
|
+
// A spilled scene's matrix lists, TLAS or copy records are on disk: the move lands once they are back.
|
|
2019
|
+
if ( this._sdf?.needsPageInForMove( meshIndices ) ) {
|
|
2020
|
+
|
|
2021
|
+
this._sdf.ensureMovable( meshIndices )
|
|
2022
|
+
.then( () => this.updateMeshTransforms( meshIndices ) )
|
|
2023
|
+
.catch( error => this._issues.warn( ISSUE_CODES.STORAGE_READ_FAILED, `reading instance matrices back failed: ${error.message}` ) );
|
|
2024
|
+
return null;
|
|
2025
|
+
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1892
2028
|
this._notePlacementsMoving( meshIndices );
|
|
1893
2029
|
const result = this._sdf.updateMeshTransforms( meshIndices );
|
|
1894
2030
|
|
|
1895
|
-
this.stages.pathTracer.updateBufferRanges( [],
|
|
2031
|
+
this.stages.pathTracer.updateBufferRanges( [], this._sdf.takeMoveRanges() );
|
|
1896
2032
|
this.reset( false, { motion: true } );
|
|
2033
|
+
this._scheduleRespill();
|
|
1897
2034
|
|
|
1898
2035
|
return result;
|
|
1899
2036
|
|
|
@@ -2383,6 +2520,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
2383
2520
|
|
|
2384
2521
|
}
|
|
2385
2522
|
|
|
2523
|
+
await this._finishImage();
|
|
2386
2524
|
return stage.frameCount;
|
|
2387
2525
|
|
|
2388
2526
|
}
|
|
@@ -2460,6 +2598,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
2460
2598
|
}
|
|
2461
2599
|
|
|
2462
2600
|
const info = this._completionInfo();
|
|
2601
|
+
// Before it is announced: a listener reading pixels sees the finished image's exposure.
|
|
2602
|
+
await this._finishImage();
|
|
2463
2603
|
if ( this.completion.markComplete() ) this.dispatchEvent( { type: EngineEvents.RENDER_COMPLETE, ...info } );
|
|
2464
2604
|
|
|
2465
2605
|
const denoised = denoise ? await this._finalDenoise() : false;
|
|
@@ -2619,12 +2759,14 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
2619
2759
|
toneMapping: this.renderer.toneMapping,
|
|
2620
2760
|
saturation: this.settings.get( 'saturation' ) ?? 1,
|
|
2621
2761
|
preserveAlpha,
|
|
2762
|
+
gain: this._displayGain(),
|
|
2622
2763
|
};
|
|
2623
2764
|
|
|
2624
2765
|
const gpu = await this._toneMapOnGPU( shown?.texture ?? null, target, width, height, tone );
|
|
2766
|
+
const fn = ! gpu && tone.gain ? await tone.gain.cpu() : null;
|
|
2625
2767
|
const data = gpu ?? toneMapToRGBA8( shown
|
|
2626
2768
|
? await this._readTexture( shown.texture, width, height )
|
|
2627
|
-
: await this.renderer.readRenderTargetPixelsAsync( target, 0, 0, width, height, 0 ), tone );
|
|
2769
|
+
: await this.renderer.readRenderTargetPixelsAsync( target, 0, 0, width, height, 0 ), { ...tone, pixelGain: fn && { fn, width, height } } );
|
|
2628
2770
|
return { data, width, height, colorSpace, source: shown?.source ?? 'accumulation', toneMappedOn: gpu ? 'gpu' : 'cpu' };
|
|
2629
2771
|
|
|
2630
2772
|
}
|
|
@@ -3198,9 +3340,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3198
3340
|
*/
|
|
3199
3341
|
setMeshVisibility( meshIndex, visible ) {
|
|
3200
3342
|
|
|
3201
|
-
this.stages.pathTracer?.updateMeshVisibility( meshIndex, visible );
|
|
3202
|
-
this._refreshEmissiveForVisibility();
|
|
3203
|
-
this.reset();
|
|
3343
|
+
this._whenTLASEditable( () => this.stages.pathTracer?.updateMeshVisibility( meshIndex, visible ) );
|
|
3204
3344
|
|
|
3205
3345
|
}
|
|
3206
3346
|
|
|
@@ -3210,9 +3350,25 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3210
3350
|
*/
|
|
3211
3351
|
updateAllMeshVisibility() {
|
|
3212
3352
|
|
|
3213
|
-
this.stages.pathTracer?.updateAllMeshVisibility();
|
|
3214
|
-
|
|
3215
|
-
|
|
3353
|
+
this._whenTLASEditable( () => this.stages.pathTracer?.updateAllMeshVisibility() );
|
|
3354
|
+
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
/** Applies a visibility edit, then resets — once a spilled TLAS is back in memory. @private */
|
|
3358
|
+
_whenTLASEditable( edit ) {
|
|
3359
|
+
|
|
3360
|
+
const apply = () => {
|
|
3361
|
+
|
|
3362
|
+
edit();
|
|
3363
|
+
this._refreshEmissiveForVisibility();
|
|
3364
|
+
this.reset();
|
|
3365
|
+
this._scheduleRespill();
|
|
3366
|
+
|
|
3367
|
+
};
|
|
3368
|
+
|
|
3369
|
+
const ready = this._sdf?.whenTLASEditable();
|
|
3370
|
+
if ( ! ready ) return apply();
|
|
3371
|
+
ready.then( apply ).catch( error => this._issues.warn( ISSUE_CODES.STORAGE_READ_FAILED, `reading the TLAS back failed: ${error.message}` ) );
|
|
3216
3372
|
|
|
3217
3373
|
}
|
|
3218
3374
|
|
|
@@ -3472,7 +3628,18 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3472
3628
|
async rebuildMaterials( scene ) {
|
|
3473
3629
|
|
|
3474
3630
|
await this.ensureSceneResident();
|
|
3475
|
-
|
|
3631
|
+
const stage = this.stages.pathTracer;
|
|
3632
|
+
await stage?.rebuildMaterials( scene || this.meshScene );
|
|
3633
|
+
|
|
3634
|
+
// Loaded without texture coordinates, and a material has a texture now: they go up again from the records.
|
|
3635
|
+
if ( stage?.triangleStorageNode?.withoutUV && this._sdf?.triangles && usesTextureCoordinates( scene || this.meshScene ) ) {
|
|
3636
|
+
|
|
3637
|
+
this._sdf.textureCoordinates = true;
|
|
3638
|
+
stage.setTriangleData( this._sdf.triangles, this._sdf.triangleCount, { textureCoordinates: true } );
|
|
3639
|
+
stage.setupMaterial();
|
|
3640
|
+
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3476
3643
|
this.reset();
|
|
3477
3644
|
|
|
3478
3645
|
}
|
|
@@ -3841,6 +4008,20 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3841
4008
|
/** Once, on the loop frame the render completes. */
|
|
3842
4009
|
_renderCompleted() {}
|
|
3843
4010
|
|
|
4011
|
+
/** True keeps the loop running over a finished render, to redraw what is still changing. */
|
|
4012
|
+
_settling() {
|
|
4013
|
+
|
|
4014
|
+
return false;
|
|
4015
|
+
|
|
4016
|
+
}
|
|
4017
|
+
|
|
4018
|
+
/** A render has its samples, before any closing pass or announcement: a promise to wait for, or null. */
|
|
4019
|
+
_finishImage() {
|
|
4020
|
+
|
|
4021
|
+
return null;
|
|
4022
|
+
|
|
4023
|
+
}
|
|
4024
|
+
|
|
3844
4025
|
/** Draws what sits over the image. Here only brings the light scene's matrices up to date. */
|
|
3845
4026
|
_renderHelperOverlay() {
|
|
3846
4027
|
|
|
@@ -3891,6 +4072,13 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3891
4072
|
|
|
3892
4073
|
}
|
|
3893
4074
|
|
|
4075
|
+
/** A per-pixel gain the shown picture carries before the view (the compositor's), for tone-mapped readbacks; or null. */
|
|
4076
|
+
_displayGain() {
|
|
4077
|
+
|
|
4078
|
+
return null;
|
|
4079
|
+
|
|
4080
|
+
}
|
|
4081
|
+
|
|
3894
4082
|
/** Whether a 'display' read should find a processed picture. */
|
|
3895
4083
|
_denoiserInUse() {
|
|
3896
4084
|
|
package/src/RenderSettings.js
CHANGED
|
@@ -258,7 +258,7 @@ export class RenderSettings extends EventDispatcher {
|
|
|
258
258
|
|
|
259
259
|
handleRenderLimitMode: ( value ) => {
|
|
260
260
|
|
|
261
|
-
stages.pathTracer?.setRenderLimitMode?.( value );
|
|
261
|
+
stages.pathTracer?.setRenderLimitMode?.( value, this.get( 'renderTimeLimit' ) );
|
|
262
262
|
reconcileCompletion?.();
|
|
263
263
|
|
|
264
264
|
},
|
|
@@ -271,8 +271,10 @@ export class RenderSettings extends EventDispatcher {
|
|
|
271
271
|
|
|
272
272
|
},
|
|
273
273
|
|
|
274
|
-
|
|
274
|
+
// Arming or disarming the deadline lifts or restores the ceiling in 'timeOnly'.
|
|
275
|
+
handleRenderTimeLimit: ( value ) => {
|
|
275
276
|
|
|
277
|
+
stages.pathTracer?.setRenderLimitMode?.( this.get( 'renderLimitMode' ), value );
|
|
276
278
|
reconcileCompletion?.();
|
|
277
279
|
|
|
278
280
|
},
|