rayzee 7.15.2 → 7.17.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/rayzee.es.js +2400 -2215
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +55 -55
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +20 -5
- package/src/PathTracerApp.js +108 -6
- package/src/Processor/PackedRayBuffer.js +49 -23
- package/src/RenderSettings.js +4 -2
- package/src/Stages/ASVGF.js +38 -0
- package/src/Stages/BilateralFilter.js +23 -0
- package/src/Stages/EdgeFilter.js +18 -0
- package/src/Stages/MotionVector.js +23 -0
- package/src/Stages/NormalDepth.js +22 -0
- package/src/Stages/PathTracer.js +310 -166
- package/src/Stages/Variance.js +23 -0
- package/src/TSL/FinalWriteKernel.js +19 -12
- package/src/TSL/GenerateKernel.js +28 -17
- package/src/TSL/ShadeKernel.js +155 -78
- package/src/managers/CameraManager.js +160 -9
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
|
@@ -184,11 +184,26 @@ export const ENGINE_DEFAULTS = {
|
|
|
184
184
|
// only round-trip exactly when both sides agree.
|
|
185
185
|
export const ALBEDO_EPS = 0.01;
|
|
186
186
|
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
// Hard ceiling the engine supports for the reserved (pre-allocated) render size. 4K (3840×2160)
|
|
188
|
+
// fits within 4096². Raising the reserved size to this pins ~1.5 GB of MRT textures — opt-in only.
|
|
189
|
+
export const MAX_RESERVABLE_RENDER_SIZE = 4096;
|
|
190
|
+
|
|
191
|
+
// Reserved render size: every per-resolution compute StorageTexture + aux buffer is pre-allocated at
|
|
192
|
+
// this SQUARE size and never resized at runtime (works around three.js StorageTexture-resize bugs —
|
|
193
|
+
// see TSL/patches history). Render resolution must not exceed it; the engine warns + ignores larger.
|
|
194
|
+
// It is a LIVE binding (mutable) so the host can raise it (e.g. for 4K) BEFORE the pipeline/stages are
|
|
195
|
+
// constructed via setReservedRenderSize(); consumers read it inside their constructors. Default 2048
|
|
196
|
+
// (zero VRAM regression). See docs/internal/specs/wavefront-chunked-pool.md.
|
|
197
|
+
export let MAX_STORAGE_TEXTURE_SIZE = 2048;
|
|
198
|
+
|
|
199
|
+
// Set the reserved render size. MUST be called before pipeline construction (stages pre-allocate at
|
|
200
|
+
// this value and cannot resize). Clamped to [256, MAX_RESERVABLE_RENDER_SIZE]. Returns the applied value.
|
|
201
|
+
export function setReservedRenderSize( px ) {
|
|
202
|
+
|
|
203
|
+
MAX_STORAGE_TEXTURE_SIZE = Math.max( 256, Math.min( MAX_RESERVABLE_RENDER_SIZE, Math.floor( px ) || 256 ) );
|
|
204
|
+
return MAX_STORAGE_TEXTURE_SIZE;
|
|
205
|
+
|
|
206
|
+
}
|
|
192
207
|
|
|
193
208
|
export const ASVGF_QUALITY_PRESETS = {
|
|
194
209
|
// phiColor / phiDepth are RELATIVE tolerances (fractions). Bigger = more
|
package/src/PathTracerApp.js
CHANGED
|
@@ -16,7 +16,7 @@ import { AutoExposure } from './Stages/AutoExposure.js';
|
|
|
16
16
|
import { Compositor } from './Stages/Compositor.js';
|
|
17
17
|
import { RenderPipeline } from './Pipeline/RenderPipeline.js';
|
|
18
18
|
import { CompletionTracker } from './Pipeline/CompletionTracker.js';
|
|
19
|
-
import { ENGINE_DEFAULTS as DEFAULT_STATE, PRODUCTION_RENDER_CONFIG, INTERACTIVE_RENDER_CONFIG, MAX_STORAGE_TEXTURE_SIZE } from './EngineDefaults.js';
|
|
19
|
+
import { ENGINE_DEFAULTS as DEFAULT_STATE, PRODUCTION_RENDER_CONFIG, INTERACTIVE_RENDER_CONFIG, MAX_STORAGE_TEXTURE_SIZE, MAX_RESERVABLE_RENDER_SIZE, setReservedRenderSize } from './EngineDefaults.js';
|
|
20
20
|
import { updateStats, updateLoading, resetLoading, setStatusCallback, getDisplaySamples, disposeObjectFromMemory } from './Processor/utils.js';
|
|
21
21
|
import { BuildTimer } from './Processor/BuildTimer.js';
|
|
22
22
|
import { InteractionManager } from './managers/InteractionManager.js';
|
|
@@ -775,11 +775,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
775
775
|
this.reset();
|
|
776
776
|
this.cameraManager.currentCameraIndex = 0;
|
|
777
777
|
this.dispatchEvent( eventPayload );
|
|
778
|
-
this.
|
|
779
|
-
type: 'CamerasUpdated',
|
|
780
|
-
cameras: this.cameraManager.cameras,
|
|
781
|
-
cameraNames: this.cameraManager.getCameraNames()
|
|
782
|
-
} );
|
|
778
|
+
this._dispatchCamerasUpdated();
|
|
783
779
|
|
|
784
780
|
} finally {
|
|
785
781
|
|
|
@@ -1093,6 +1089,57 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1093
1089
|
|
|
1094
1090
|
}
|
|
1095
1091
|
|
|
1092
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1093
|
+
// Dynamic cameras (add / remove)
|
|
1094
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1095
|
+
|
|
1096
|
+
/** The index of the currently active camera (0 = built-in default). */
|
|
1097
|
+
get currentCameraIndex() {
|
|
1098
|
+
|
|
1099
|
+
return this.cameraManager?.currentCameraIndex ?? 0;
|
|
1100
|
+
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Snapshot the current view as a new named camera and switch to it.
|
|
1105
|
+
* @param {Object} [opts]
|
|
1106
|
+
* @param {string} [opts.name] - Display name (auto-generated otherwise).
|
|
1107
|
+
* @returns {number} The index of the newly added camera.
|
|
1108
|
+
*/
|
|
1109
|
+
addCamera( { name } = {} ) {
|
|
1110
|
+
|
|
1111
|
+
const index = this.cameraManager.addCameraFromView( name );
|
|
1112
|
+
this.cameraManager.switchCamera( index );
|
|
1113
|
+
this._dispatchCamerasUpdated();
|
|
1114
|
+
return index;
|
|
1115
|
+
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Remove a user-added camera by index. Built-in and model-embedded cameras
|
|
1120
|
+
* are protected. Falls back to the default camera if the active one is removed.
|
|
1121
|
+
* @param {number} index
|
|
1122
|
+
* @returns {boolean} true if a camera was removed.
|
|
1123
|
+
*/
|
|
1124
|
+
removeCamera( index ) {
|
|
1125
|
+
|
|
1126
|
+
const removed = this.cameraManager.removeCamera( index );
|
|
1127
|
+
if ( removed ) this._dispatchCamerasUpdated();
|
|
1128
|
+
return removed;
|
|
1129
|
+
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** Notify consumers that the camera list changed (names / count). */
|
|
1133
|
+
_dispatchCamerasUpdated() {
|
|
1134
|
+
|
|
1135
|
+
this.dispatchEvent( {
|
|
1136
|
+
type: 'CamerasUpdated',
|
|
1137
|
+
cameras: this.cameraManager.cameras,
|
|
1138
|
+
cameraNames: this.cameraManager.getCameraNames(),
|
|
1139
|
+
} );
|
|
1140
|
+
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1096
1143
|
// ═══════════════════════════════════════════════════════════════
|
|
1097
1144
|
// BVH Refit (Animation)
|
|
1098
1145
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -1155,6 +1202,56 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1155
1202
|
// Resize
|
|
1156
1203
|
// ═══════════════════════════════════════════════════════════════
|
|
1157
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* Raise (or lower) the reserved render size — the square dimension every compute StorageTexture + aux
|
|
1207
|
+
* buffer is pre-allocated at. Needed to enable resolutions above the 2048 default (e.g. 4K). The request
|
|
1208
|
+
* is device-capped: 4K reservation (~1.5 GB of MRT textures) is only granted on GPUs with ample VRAM +
|
|
1209
|
+
* a large storage-buffer binding limit; weaker devices clamp to 2048.
|
|
1210
|
+
*
|
|
1211
|
+
* MUST be called before the pipeline/stages are constructed (they pre-allocate at this value and cannot
|
|
1212
|
+
* resize). Calling it after init only takes effect on the next full pipeline (re)build.
|
|
1213
|
+
* @param {number} requestedPx desired reserved size (longest edge)
|
|
1214
|
+
* @returns {number} the applied reserved size
|
|
1215
|
+
*/
|
|
1216
|
+
setReservedRenderResolution( requestedPx ) {
|
|
1217
|
+
|
|
1218
|
+
const limits = this.renderer?.backend?.device?.limits;
|
|
1219
|
+
const maxBinding = limits?.maxStorageBufferBindingSize || ( 128 * 1024 * 1024 );
|
|
1220
|
+
const deviceMemGB = ( typeof navigator !== 'undefined' && navigator.deviceMemory ) || 4;
|
|
1221
|
+
// 4K reserve pins the accum MRT (~1.5 GB) + aux; only grant it on clearly-capable GPUs.
|
|
1222
|
+
const deviceSafeMax = ( deviceMemGB >= 8 && maxBinding >= 1024 * 1024 * 1024 )
|
|
1223
|
+
? MAX_RESERVABLE_RENDER_SIZE : 2048;
|
|
1224
|
+
const prev = MAX_STORAGE_TEXTURE_SIZE;
|
|
1225
|
+
const applied = setReservedRenderSize( Math.min( requestedPx, deviceSafeMax ) );
|
|
1226
|
+
|
|
1227
|
+
// If the pipeline is already built and the reserved size changed, re-init the reserved GPU storage in
|
|
1228
|
+
// place: each stage recreates its pre-allocated StorageTextures at the new size + rebuilds its compute
|
|
1229
|
+
// pipelines. Stage OBJECTS, manager refs and event wiring are preserved (so no re-subscription needed);
|
|
1230
|
+
// scene geometry buffers are resolution-independent and reused. Rendering is paused across the swap so
|
|
1231
|
+
// no in-flight dispatch references a disposed texture.
|
|
1232
|
+
if ( applied !== prev && this.stages?.pathTracer?._packedBuffers ) {
|
|
1233
|
+
|
|
1234
|
+
const wasPaused = this.pauseRendering;
|
|
1235
|
+
this.pauseRendering = true;
|
|
1236
|
+
try {
|
|
1237
|
+
|
|
1238
|
+
for ( const stage of Object.values( this.stages ) ) stage?.reallocateReservedStorage?.();
|
|
1239
|
+
|
|
1240
|
+
} finally {
|
|
1241
|
+
|
|
1242
|
+
this.pauseRendering = wasPaused;
|
|
1243
|
+
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
this.reset();
|
|
1247
|
+
this.dispatchEvent( { type: 'reserved_render_size_changed', size: applied } );
|
|
1248
|
+
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return applied;
|
|
1252
|
+
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1158
1255
|
/**
|
|
1159
1256
|
* Guard against render resolutions the compute pipeline can't support.
|
|
1160
1257
|
* Per-resolution StorageTextures are pre-allocated at MAX_STORAGE_TEXTURE_SIZE
|
|
@@ -1292,6 +1389,9 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1292
1389
|
|
|
1293
1390
|
if ( options.canvasWidth && options.canvasHeight ) {
|
|
1294
1391
|
|
|
1392
|
+
// Raise the reserved storage first so a > 2048 final-render resolution (4K) fits (device-capped,
|
|
1393
|
+
// in-place re-init). No-op when the size already fits.
|
|
1394
|
+
this.setReservedRenderResolution( Math.max( options.canvasWidth, options.canvasHeight ) );
|
|
1295
1395
|
this.setCanvasSize( options.canvasWidth, options.canvasHeight );
|
|
1296
1396
|
|
|
1297
1397
|
}
|
|
@@ -1874,6 +1974,8 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1874
1974
|
onResize: () => this.onResize(),
|
|
1875
1975
|
onReset: () => this.reset(),
|
|
1876
1976
|
getSettings: ( k ) => this.settings.get( k ),
|
|
1977
|
+
// Per-camera DOF restore — silent + reset:false so switchCamera's own onReset() is the single reset.
|
|
1978
|
+
applySettings: ( updates ) => this.settings.setMany( updates, { silent: true, reset: false } ),
|
|
1877
1979
|
} );
|
|
1878
1980
|
|
|
1879
1981
|
// Auto-focus context — CameraManager stores it, reads it each frame
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
storage, uintBitsToFloat, floatBitsToUint, vec2, vec3, vec4, uvec4, uint, int,
|
|
7
|
+
storage, uintBitsToFloat, floatBitsToUint, vec2, vec3, vec4, uvec4, uint, int, float, clamp,
|
|
8
8
|
packSnorm2x16, packUnorm2x16, unpackSnorm2x16, unpackUnorm2x16,
|
|
9
9
|
} from 'three/tsl';
|
|
10
10
|
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
@@ -24,7 +24,7 @@ export const RAY = {
|
|
|
24
24
|
THROUGHPUT_PDF: 2, // vec4(throughput.xyz, pdf)
|
|
25
25
|
RADIANCE_ALPHA: 3, // vec4(radiance.xyz, alpha)
|
|
26
26
|
MEDIUM_STACK: 4, // vec4(uintBitsToFloat(stackDepth|transTraversals<<8|wavelength<<16), ior1, ior2, ior3)
|
|
27
|
-
MEDIUM_SIGMA_A: 5, // vec4(sigmaA.xyz,
|
|
27
|
+
MEDIUM_SIGMA_A: 5, // vec4(sigmaA.xyz, featTP.w) — Beer-Lambert absorption (KHR_materials_volume + SSS) + DDFA feature-throughput tint
|
|
28
28
|
SSS_SIGMA_S: 6, // vec4(sigmaS.xyz, g) — SSS scattering coeff + Henyey-Greenstein anisotropy (sigmaS==0 ⇒ glass)
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -50,16 +50,11 @@ export function freeStorageAttribute( renderer, attr ) {
|
|
|
50
50
|
|
|
51
51
|
export class PackedRayBuffer {
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return Math.ceil( maxRays * 1.25 );
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
constructor( maxRays = 0, renderer = null ) {
|
|
53
|
+
// `capacity` is the exact paths-in-flight budget B (Blender-style path pool). It is DECOUPLED from
|
|
54
|
+
// image resolution and fixed for the device — the image is streamed through the pool in row bands
|
|
55
|
+
// (see docs/internal/specs/wavefront-chunked-pool.md), so the SoA stride _cap never changes with
|
|
56
|
+
// resolution and the wavefront kernels are built once (no resize-triggered WGSL regen).
|
|
57
|
+
constructor( capacity = 0, renderer = null ) {
|
|
63
58
|
|
|
64
59
|
this.capacity = 0;
|
|
65
60
|
this._renderer = renderer;
|
|
@@ -70,15 +65,14 @@ export class PackedRayBuffer {
|
|
|
70
65
|
this.rngBuffer = null;
|
|
71
66
|
this.hitBuffer = null;
|
|
72
67
|
|
|
73
|
-
if (
|
|
68
|
+
if ( capacity > 0 ) this.allocate( capacity );
|
|
74
69
|
|
|
75
70
|
}
|
|
76
71
|
|
|
77
|
-
allocate(
|
|
72
|
+
allocate( capacity ) {
|
|
78
73
|
|
|
79
74
|
this.dispose();
|
|
80
75
|
|
|
81
|
-
const capacity = Math.ceil( maxRays * 1.25 );
|
|
82
76
|
this.capacity = capacity;
|
|
83
77
|
_cap = capacity;
|
|
84
78
|
|
|
@@ -117,12 +111,11 @@ export class PackedRayBuffer {
|
|
|
117
111
|
|
|
118
112
|
}
|
|
119
113
|
|
|
120
|
-
// Reallocates only if
|
|
121
|
-
resize(
|
|
114
|
+
// Reallocates only if a larger budget capacity is requested; returns true if it did.
|
|
115
|
+
resize( capacity ) {
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.allocate( maxRays );
|
|
117
|
+
if ( capacity <= this.capacity && this.capacity > 0 ) return false;
|
|
118
|
+
this.allocate( capacity );
|
|
126
119
|
return true;
|
|
127
120
|
|
|
128
121
|
}
|
|
@@ -258,11 +251,44 @@ export const writeMediumStack = ( buf, id, stackDepth, transTraversals, ior1, io
|
|
|
258
251
|
stackDepth.bitOr( transTraversals.shiftLeft( 8 ) ).bitOr( wavelength.shiftLeft( 16 ) )
|
|
259
252
|
), ior1, ior2, ior3 ) );
|
|
260
253
|
|
|
261
|
-
// Region 7: Beer-Lambert sigmaA of the active medium
|
|
254
|
+
// Region 7: Beer-Lambert sigmaA (.xyz) of the active medium + DDFA feature-throughput (.w).
|
|
255
|
+
// .xyz absorption is gated on stackDepth>0; .w carries the OIDN/ASVGF see-through aux tint (see below).
|
|
256
|
+
// Both writers RMW-preserve the other half so sigmaA (live in glass) and featTP never clobber each other.
|
|
262
257
|
export const readMediumSigmaA = ( buf, id ) => buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).xyz;
|
|
263
258
|
|
|
264
|
-
export const writeMediumSigmaA = ( buf, id, sigmaA ) =>
|
|
265
|
-
|
|
259
|
+
export const writeMediumSigmaA = ( buf, id, sigmaA ) => {
|
|
260
|
+
|
|
261
|
+
const keepW = buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).w.toVar();
|
|
262
|
+
buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).assign( vec4( sigmaA, keepW ) );
|
|
263
|
+
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// DDFA (Deferred Denoising-Feature Aux) feature-throughput: a clean per-ray tint carried through smooth
|
|
267
|
+
// glass/mirror, committed into the OIDN/ASVGF albedo guide at the first diffuse-enough surface. Rides the
|
|
268
|
+
// otherwise-dead MEDIUM_SIGMA_A.w, packed 10/10/10 into 30 bits (top 2 bits zero ⇒ the float bit-pattern is
|
|
269
|
+
// always finite — never NaN/Inf). LDR [0,1] per channel; final albedo re-quantizes to unorm16 anyway.
|
|
270
|
+
const pack30 = ( tp ) => {
|
|
271
|
+
|
|
272
|
+
const c = clamp( tp, vec3( 0.0 ), vec3( 1.0 ) ).mul( 1023.0 ).add( 0.5 );
|
|
273
|
+
return uint( c.x ).bitOr( uint( c.y ).shiftLeft( 10 ) ).bitOr( uint( c.z ).shiftLeft( 20 ) );
|
|
274
|
+
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const unpack30 = ( u ) => vec3(
|
|
278
|
+
float( u.bitAnd( uint( 1023 ) ) ),
|
|
279
|
+
float( u.shiftRight( 10 ).bitAnd( uint( 1023 ) ) ),
|
|
280
|
+
float( u.shiftRight( 20 ).bitAnd( uint( 1023 ) ) ),
|
|
281
|
+
).div( 1023.0 );
|
|
282
|
+
|
|
283
|
+
export const readFeatureThroughput = ( buf, id ) =>
|
|
284
|
+
unpack30( floatBitsToUint( buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).w ) );
|
|
285
|
+
|
|
286
|
+
export const writeFeatureThroughput = ( buf, id, tp ) => {
|
|
287
|
+
|
|
288
|
+
const keepXYZ = buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).xyz.toVar();
|
|
289
|
+
buf.element( soa( id, RAY.MEDIUM_SIGMA_A ) ).assign( vec4( keepXYZ, uintBitsToFloat( pack30( tp ) ) ) );
|
|
290
|
+
|
|
291
|
+
};
|
|
266
292
|
|
|
267
293
|
// Per-ray bounce state packed into ORIGIN_META.w (written by writeRayOriginMeta alongside the origin):
|
|
268
294
|
// perRayBounces = bits 0-7 (camera-bounce depth; the loop index can't track it once free bounces decouple it)
|
package/src/RenderSettings.js
CHANGED
|
@@ -252,8 +252,9 @@ export class RenderSettings extends EventDispatcher {
|
|
|
252
252
|
* @param {Object} updates - Key/value pairs
|
|
253
253
|
* @param {Object} [options]
|
|
254
254
|
* @param {boolean} [options.silent] - Suppress settingChanged events
|
|
255
|
+
* @param {boolean} [options.reset] - Override the routes' default reset behavior
|
|
255
256
|
*/
|
|
256
|
-
setMany( updates, { silent } = {} ) {
|
|
257
|
+
setMany( updates, { silent, reset } = {} ) {
|
|
257
258
|
|
|
258
259
|
let needsReset = false;
|
|
259
260
|
|
|
@@ -279,7 +280,8 @@ export class RenderSettings extends EventDispatcher {
|
|
|
279
280
|
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
+
const shouldReset = reset !== undefined ? reset : needsReset;
|
|
284
|
+
if ( shouldReset ) this._resetCallback?.();
|
|
283
285
|
|
|
284
286
|
}
|
|
285
287
|
|
package/src/Stages/ASVGF.js
CHANGED
|
@@ -796,6 +796,44 @@ export class ASVGF extends RenderStage {
|
|
|
796
796
|
|
|
797
797
|
}
|
|
798
798
|
|
|
799
|
+
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
800
|
+
// reserved size + rebuild all compute nodes in place. Same stage object, so refs/wiring stay valid. Fresh
|
|
801
|
+
// build ⇒ the first-render warmup re-runs (no aliasing — the nodes are new, matching initial construction).
|
|
802
|
+
reallocateReservedStorage() {
|
|
803
|
+
|
|
804
|
+
this._gradientNode?.dispose();
|
|
805
|
+
this._temporalNodeA?.dispose();
|
|
806
|
+
this._temporalNodeB?.dispose();
|
|
807
|
+
this._heatmapComputeNode?.dispose();
|
|
808
|
+
this._temporalTexA?.dispose();
|
|
809
|
+
this._temporalTexB?.dispose();
|
|
810
|
+
this._outputModulatedTex?.dispose();
|
|
811
|
+
this._gradientStorageTex?.dispose();
|
|
812
|
+
this._heatmapStorageTex?.dispose();
|
|
813
|
+
|
|
814
|
+
const mk = ( type, filter ) => {
|
|
815
|
+
|
|
816
|
+
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
817
|
+
t.type = type; t.format = RGBAFormat; t.minFilter = filter; t.magFilter = filter;
|
|
818
|
+
return t;
|
|
819
|
+
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
this._temporalTexA = mk( FloatType, LinearFilter );
|
|
823
|
+
this._temporalTexB = mk( FloatType, LinearFilter );
|
|
824
|
+
this._outputModulatedTex = mk( FloatType, LinearFilter );
|
|
825
|
+
this._gradientStorageTex = mk( HalfFloatType, LinearFilter );
|
|
826
|
+
this._heatmapStorageTex = mk( FloatType, NearestFilter );
|
|
827
|
+
|
|
828
|
+
this._buildGradientCompute();
|
|
829
|
+
this._buildTemporalCompute();
|
|
830
|
+
this._buildHeatmapCompute();
|
|
831
|
+
this._compiled = false;
|
|
832
|
+
this.currentMoments = 0;
|
|
833
|
+
this._needsWarmReset = true;
|
|
834
|
+
|
|
835
|
+
}
|
|
836
|
+
|
|
799
837
|
setSize( width, height ) {
|
|
800
838
|
|
|
801
839
|
// StorageTextures stay at max alloc — see resize crash fix (three.js #33061).
|
|
@@ -385,6 +385,29 @@ export class BilateralFilter extends RenderStage {
|
|
|
385
385
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
389
|
+
// reserved size + rebuild the compute nodes in place. Same stage object, so refs/wiring stay valid.
|
|
390
|
+
reallocateReservedStorage() {
|
|
391
|
+
|
|
392
|
+
this._computeNodeA?.dispose();
|
|
393
|
+
this._computeNodeB?.dispose();
|
|
394
|
+
this._storageTexA?.dispose();
|
|
395
|
+
this._storageTexB?.dispose();
|
|
396
|
+
const mk = () => {
|
|
397
|
+
|
|
398
|
+
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
399
|
+
t.type = HalfFloatType; t.format = RGBAFormat; t.minFilter = LinearFilter; t.magFilter = LinearFilter;
|
|
400
|
+
return t;
|
|
401
|
+
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
this._storageTexA = mk();
|
|
405
|
+
this._storageTexB = mk();
|
|
406
|
+
this._buildCompute();
|
|
407
|
+
this._compiled = false;
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
|
|
388
411
|
dispose() {
|
|
389
412
|
|
|
390
413
|
this._computeNodeA?.dispose();
|
package/src/Stages/EdgeFilter.js
CHANGED
|
@@ -454,6 +454,24 @@ export class EdgeFilter extends RenderStage {
|
|
|
454
454
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
458
|
+
// reserved size + rebuild the compute nodes in place. Same stage object, so refs/wiring stay valid.
|
|
459
|
+
reallocateReservedStorage() {
|
|
460
|
+
|
|
461
|
+
this._computeDemod?.dispose();
|
|
462
|
+
this._computeAtrousA?.dispose();
|
|
463
|
+
this._computeAtrousB?.dispose();
|
|
464
|
+
this._demodTex?.dispose();
|
|
465
|
+
this._storageTexA?.dispose();
|
|
466
|
+
this._storageTexB?.dispose();
|
|
467
|
+
this._demodTex = this._makeStorageTex();
|
|
468
|
+
this._storageTexA = this._makeStorageTex();
|
|
469
|
+
this._storageTexB = this._makeStorageTex();
|
|
470
|
+
this._buildCompute();
|
|
471
|
+
this._compiled = false;
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
457
475
|
dispose() {
|
|
458
476
|
|
|
459
477
|
this._computeDemod?.dispose();
|
|
@@ -556,6 +556,29 @@ export class MotionVector extends RenderStage {
|
|
|
556
556
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
560
|
+
// reserved size + rebuild the compute nodes in place. Same stage object, so refs/wiring stay valid.
|
|
561
|
+
reallocateReservedStorage() {
|
|
562
|
+
|
|
563
|
+
this._screenSpaceComputeNode?.dispose();
|
|
564
|
+
this._worldSpaceComputeNode?.dispose();
|
|
565
|
+
this._screenSpaceStorageTex?.dispose();
|
|
566
|
+
this._worldSpaceStorageTex?.dispose();
|
|
567
|
+
const mk = () => {
|
|
568
|
+
|
|
569
|
+
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
570
|
+
t.type = HalfFloatType; t.format = RGBAFormat; t.minFilter = NearestFilter; t.magFilter = NearestFilter;
|
|
571
|
+
return t;
|
|
572
|
+
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
this._screenSpaceStorageTex = mk();
|
|
576
|
+
this._worldSpaceStorageTex = mk();
|
|
577
|
+
this._buildScreenSpaceCompute();
|
|
578
|
+
this._buildWorldSpaceCompute();
|
|
579
|
+
|
|
580
|
+
}
|
|
581
|
+
|
|
559
582
|
dispose() {
|
|
560
583
|
|
|
561
584
|
this._screenSpaceComputeNode?.dispose();
|
|
@@ -405,6 +405,28 @@ export class NormalDepth extends RenderStage {
|
|
|
405
405
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
409
|
+
// reserved size. The compute node rebuilds lazily on the next execute (guarded by _computeBuilt).
|
|
410
|
+
reallocateReservedStorage() {
|
|
411
|
+
|
|
412
|
+
this._computeNode?.dispose();
|
|
413
|
+
this._computeNode = null;
|
|
414
|
+
this._outputStorageTex?.dispose();
|
|
415
|
+
this._shadingStorageTex?.dispose();
|
|
416
|
+
const mk = () => {
|
|
417
|
+
|
|
418
|
+
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
419
|
+
t.type = HalfFloatType; t.format = RGBAFormat; t.minFilter = NearestFilter; t.magFilter = NearestFilter;
|
|
420
|
+
return t;
|
|
421
|
+
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
this._outputStorageTex = mk();
|
|
425
|
+
this._shadingStorageTex = mk();
|
|
426
|
+
this._computeBuilt = false;
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
408
430
|
dispose() {
|
|
409
431
|
|
|
410
432
|
this._computeNode?.dispose();
|