@meimu/mutracer 0.2.0 → 0.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 +3 -0
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +8949 -7915
- package/docs/api.md +42 -21
- package/docs/caustics.md +80 -0
- package/docs/materials.md +5 -4
- package/docs/progressive-rendering.md +50 -9
- package/docs/technical/continuous-spectrum.md +93 -0
- package/docs/updates.md +3 -1
- package/docs/viewer-checklist.md +65 -0
- package/docs/viewer.md +7 -5
- package/lib/Display.js +1 -1
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +2 -0
- package/lib/Engine.js +47 -12
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +61 -13
- package/lib/Render.js +169 -19
- package/lib/Render.js.map +1 -1
- package/lib/RenderLoop.d.ts +7 -3
- package/lib/RenderLoop.js +68 -44
- package/lib/RenderLoop.js.map +1 -1
- package/lib/main.d.ts +1 -1
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/GPUTraceQueue.d.ts +2 -1
- package/lib/renderer/GPUTraceQueue.js +3 -1
- package/lib/renderer/GPUTraceQueue.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +4 -0
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
- package/lib/renderer/WGPUCausticLightPass.js +217 -0
- package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
- package/lib/renderer/WGPUEnvMapDistribution.js +18 -18
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
- package/lib/renderer/WGPUPhotonMap.js +191 -0
- package/lib/renderer/WGPUPhotonMap.js.map +1 -0
- package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
- package/lib/renderer/WGPURayTraceOutput.js +122 -28
- package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
- package/lib/renderer/WGPURayTracePass.d.ts +48 -17
- package/lib/renderer/WGPURayTracePass.js +538 -340
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +83 -47
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
- package/lib/renderer/WebGLFullQuadPass.js +4 -0
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +6 -1
- package/lib/renderer/WebGLRayTracePass.js +43 -6
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +10 -14
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.js +60 -60
- package/lib/renderer/accumulationTarget.d.ts +2 -1
- package/lib/renderer/accumulationTarget.js.map +1 -1
- package/lib/renderer/causticDiagnostics.d.ts +31 -0
- package/lib/renderer/causticDiagnostics.js +5 -0
- package/lib/renderer/causticDiagnostics.js.map +1 -0
- package/lib/renderer/causticEmission.d.ts +10 -0
- package/lib/renderer/causticEmission.js +40 -0
- package/lib/renderer/causticEmission.js.map +1 -0
- package/lib/renderer/causticUniform.d.ts +12 -0
- package/lib/renderer/causticUniform.js +22 -0
- package/lib/renderer/causticUniform.js.map +1 -0
- package/lib/renderer/caustics.d.ts +16 -0
- package/lib/renderer/caustics.js +93 -0
- package/lib/renderer/caustics.js.map +1 -0
- package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
- package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
- package/lib/renderer/glsl/aovTrace.frag.js +33 -33
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -9
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
- package/lib/renderer/glsl/chunks/intersect.glsl.js +426 -428
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +68 -63
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +94 -76
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +140 -117
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
- package/lib/renderer/glsl/chunks/sampling.glsl.js +89 -92
- package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +2 -2
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
- package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
- package/lib/renderer/glsl/rayTrace.frag.js +260 -237
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +26 -26
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
- package/lib/renderer/lightSampling.d.ts +3 -2
- package/lib/renderer/lightSampling.js +14 -8
- package/lib/renderer/lightSampling.js.map +1 -1
- package/lib/renderer/progressiveScheduler.js +4 -3
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +1 -0
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/researchTransport.d.ts +31 -0
- package/lib/renderer/researchTransport.js +5 -0
- package/lib/renderer/researchTransport.js.map +1 -0
- package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
- package/lib/renderer/sceneLayoutSchema.js +2 -2
- package/lib/renderer/sceneLayoutSchema.js.map +1 -1
- package/lib/renderer/scratchSampling.js +6 -6
- package/lib/renderer/spectrum.d.ts +21 -0
- package/lib/renderer/spectrum.js +154 -0
- package/lib/renderer/spectrum.js.map +1 -0
- package/lib/renderer/webglLightSampling.d.ts +39 -0
- package/lib/renderer/webglLightSampling.js +15 -0
- package/lib/renderer/webglLightSampling.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.js +3 -3
- package/lib/renderer/wgpuShaderSource.d.ts +7 -0
- package/lib/renderer/wgpuShaderSource.js +2 -0
- package/lib/renderer/wgpuShaderSource.js.map +1 -1
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovResolve.wgsl.js +2 -2
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
- package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
- package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
- package/lib/renderer/wgsl/causticLight.compute.js +537 -0
- package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +598 -592
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +14 -14
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +75 -73
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +794 -794
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +35 -25
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +204 -187
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +89 -93
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/compose.frag.wgsl.js +98 -98
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.js +2 -2
- package/lib/renderer/wgsl/jnlm.compute.js +74 -74
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +25 -25
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
- package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
- package/lib/renderer/wgsl/photonMap.compute.js +352 -0
- package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
- package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +9 -1
- package/lib/renderer/wgsl/rayTrace.compute.js +448 -522
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/MaterialState.d.ts +2 -2
- package/lib/scene/MaterialState.js +2 -2
- package/lib/scene/MaterialState.js.map +1 -1
- package/lib/settings/render.d.ts +11 -0
- package/lib/settings/render.js +1 -0
- package/lib/settings/render.js.map +1 -1
- package/lib/util/fromTHREE.js +2 -2
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/llms.txt +2 -1
- package/package.json +3 -3
package/lib/Render.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export type RenderStatus = 'preparing' | 'compiling' | 'converging' | 'denoising
|
|
|
17
17
|
/** Execution overrides for one refine call; accumulated samples are retained. */
|
|
18
18
|
export interface RefineOptions {
|
|
19
19
|
/** Batch several samples in this call. Default this render's batchSamples. */ batchSamples?: boolean;
|
|
20
|
-
/** Positive finite WebGPU queue budget in milliseconds; capped by timeBudgetMs, ignored on WebGL. Default timeBudgetMs. */ targetBatchTimeMs?: number;
|
|
21
|
-
/** Leave the displayed image unchanged, for calls between display frames; the next presentation or pixel read updates it once with every new sample. Ignored on WebGL. Default false. */ deferResolve?: boolean;
|
|
20
|
+
/** @deprecated Use refineUntil; this option will be removed in a later release. Positive finite WebGPU queue budget in milliseconds; capped by timeBudgetMs, ignored on WebGL. Default timeBudgetMs. */ targetBatchTimeMs?: number;
|
|
21
|
+
/** @deprecated Use refineUntil; this option will be removed in a later release. Leave the displayed image unchanged, for calls between display frames; the next presentation or pixel read updates it once with every new sample. Ignored on WebGL. Default false. */ deferResolve?: boolean;
|
|
22
22
|
}
|
|
23
23
|
/** When a render filters its complete samples. Default converged. */
|
|
24
24
|
export type DenoiseSchedule = 'converged' | 'progressive' | 'every-sample';
|
|
@@ -30,7 +30,7 @@ export interface Denoiser {
|
|
|
30
30
|
width: number;
|
|
31
31
|
height: number;
|
|
32
32
|
}): Promise<void>;
|
|
33
|
-
/** Write all output RGB, then resolve after submitting GPU work. Abort on input changes, restart, replacement or disposal; aborted results are discarded even if this resolves. Buffers are borrowed until settlement; do not retain or destroy them. */
|
|
33
|
+
/** Write all output RGB, then resolve after submitting GPU work. Abort on input changes, restart, replacement or disposal; aborted results are discarded even if this resolves. Settle within 10 seconds of an abort, or later denoising continues without waiting. Buffers are borrowed until settlement; do not retain or destroy them. */
|
|
34
34
|
denoise(input: DenoiseInput, context: {
|
|
35
35
|
signal: AbortSignal;
|
|
36
36
|
schedule: DenoiseSchedule;
|
|
@@ -88,6 +88,21 @@ export type OIDNDenoiserOptions = OIDNSettings & ({
|
|
|
88
88
|
});
|
|
89
89
|
/** Assign a complete configuration, or null to disable denoising. The type selector is required. */
|
|
90
90
|
export type DenoiserOptions = JNLMDenoiserOptions | OIDNDenoiserOptions | CustomDenoiserOptions;
|
|
91
|
+
/** Optional caustic estimators. All are disabled by default. Both fall back to path tracing when a smooth caster has stochastic blend coverage. */
|
|
92
|
+
export interface CausticSettings {
|
|
93
|
+
/** WebGPU progressive photon mapping; ignored on WebGL. Used alone, it can reduce denoising blotches on directly visible receivers, with noisier raw output. Default `false`. */
|
|
94
|
+
photonMapping?: boolean | {
|
|
95
|
+
/** Emitted photons per batch; 28 bytes per photon plus up to 13 MiB of overhead. Positive finite values are rounded to at least 1 and reduced to fit device limits and a 128 MiB batch budget; other values use the default. Default pixel count / 4, at most 262144. */ photonsPerSample?: number;
|
|
96
|
+
/** Positive first-batch gather radius in scene units, independent of photonsPerSample; shrinks as batches accumulate. Default 3% of the caster bounding sphere radius. */ initialRadius?: number;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* WebGPU light tracing for directly visible caustic paths; ignored on WebGL.
|
|
100
|
+
* Default `false`.
|
|
101
|
+
*/ lightTracing?: boolean | {
|
|
102
|
+
/** Emitted photons per sample, rounded to a positive integer and capped by the device dispatch limit. Default pixel count / 4. */
|
|
103
|
+
photonsPerSample?: number;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
91
106
|
/** Optional per-render execution and image-quality settings. */
|
|
92
107
|
export interface RenderSettings {
|
|
93
108
|
/** Optional surface outputs. Default []; disabled outputs allocate no AOV resources. */ aovs?: readonly AovKind[];
|
|
@@ -95,6 +110,7 @@ export interface RenderSettings {
|
|
|
95
110
|
/** Maximum complete samples. Default 2048. */ maxSamples?: number;
|
|
96
111
|
/**
|
|
97
112
|
* Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
|
|
113
|
+
* Complete caustic light-to-camera paths can be longer than this depth.
|
|
98
114
|
* Total path events, including transmission and alpha skips, are capped at 20
|
|
99
115
|
* and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
|
|
100
116
|
* for scenes with transmission or alpha, with its existing early termination.
|
|
@@ -102,15 +118,16 @@ export interface RenderSettings {
|
|
|
102
118
|
*/ bounces?: number;
|
|
103
119
|
/** Radiance clamp; 0 disables it. Default 0. */ clamping?: number;
|
|
104
120
|
/** Sampling sequence. Default sobol. */ samplingPattern?: SamplingPattern;
|
|
121
|
+
/** Caustic estimators. All are disabled by default. */ caustics?: CausticSettings;
|
|
105
122
|
/** Light candidates per vertex; WebGPU only, ignored on WebGL. Default 4. */ directLightCandidates?: number;
|
|
106
123
|
/** Rounded material edges. Default true. */ bevel?: boolean;
|
|
107
124
|
/** Bevel neighborhood probes, clamped to 1–32. Default 8. */ bevelSamples?: number;
|
|
108
|
-
/**
|
|
125
|
+
/** Wavelength paths per dispersion sample, each carrying one RGB channel. Default 3; 1 selects one random channel. */ dispersionWavelengths?: 1 | 3;
|
|
109
126
|
/** Primary-ray alpha blend strategy; WebGPU only. Default stochastic. */ alphaBlendStrategy?: AlphaBlendStrategy;
|
|
110
127
|
/** Minimum transmissive path alpha, using path depth. Default 1. Changes restart this accumulation. */ transmissiveOutputAlphaMin?: number;
|
|
111
128
|
/** Tile execution budget in milliseconds. Default 21. */ timeBudgetMs?: number;
|
|
112
|
-
/** Maximum tiles per frame. Default Infinity. */ maxTiles?: number;
|
|
113
|
-
/** Let one refine submit several samples
|
|
129
|
+
/** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles?: number;
|
|
130
|
+
/** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. RenderLoop also batches between display frames when individual tiles fit the budget. Default false. */ batchSamples?: boolean;
|
|
114
131
|
/** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */ allowMultipleSamplesPerFrame?: boolean;
|
|
115
132
|
/** Adapt scale to frame timing; minScale must not exceed resolutionScale. Default false. */ adaptiveResolution?: {
|
|
116
133
|
targetFrameMs: number;
|
|
@@ -161,6 +178,7 @@ export default class Render {
|
|
|
161
178
|
/** Maximum complete samples. Default 2048. */ maxSamples: number;
|
|
162
179
|
/**
|
|
163
180
|
* Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
|
|
181
|
+
* Complete caustic light-to-camera paths can be longer than this depth.
|
|
164
182
|
* Total path events, including transmission and alpha skips, are capped at 20
|
|
165
183
|
* and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
|
|
166
184
|
* for scenes with transmission or alpha, with its existing early termination.
|
|
@@ -168,6 +186,7 @@ export default class Render {
|
|
|
168
186
|
*/ bounces: number;
|
|
169
187
|
/** Per-sample radiance clamp; 0 disables it. Default 0. */ clamping: number;
|
|
170
188
|
/** Sampling sequence. Default sobol. */ samplingPattern: SamplingPattern;
|
|
189
|
+
/** Caustic estimators. Default undefined (all disabled). */ caustics?: CausticSettings;
|
|
171
190
|
/** WebGPU light candidates per vertex; ignored on WebGL. Default 4. */ directLightCandidates: number;
|
|
172
191
|
/** Rounded material edges. Default true. */ bevel: boolean;
|
|
173
192
|
/** Bevel probes, clamped to 1–32. Default 8. */ bevelSamples: number;
|
|
@@ -175,8 +194,8 @@ export default class Render {
|
|
|
175
194
|
/** WebGPU primary-ray alpha policy; ignored on WebGL. Default stochastic. */ alphaBlendStrategy: AlphaBlendStrategy;
|
|
176
195
|
/** Minimum transmissive path alpha. Default 1. Changing it restarts this render. */ transmissiveOutputAlphaMin: number;
|
|
177
196
|
/** Execution budget in milliseconds. Default 21. */ timeBudgetMs: number;
|
|
178
|
-
/** Maximum tiles per frame. Default Infinity. */ maxTiles: number;
|
|
179
|
-
/** Let one refine submit several samples within timeBudgetMs; WebGPU requires GPU timestamps. Default false. */ batchSamples: boolean;
|
|
197
|
+
/** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles: number;
|
|
198
|
+
/** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. Default false. */ batchSamples: boolean;
|
|
180
199
|
/** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */
|
|
181
200
|
get allowMultipleSamplesPerFrame(): boolean;
|
|
182
201
|
set allowMultipleSamplesPerFrame(value: boolean);
|
|
@@ -210,6 +229,7 @@ export default class Render {
|
|
|
210
229
|
private _disposed;
|
|
211
230
|
private _busy;
|
|
212
231
|
private _refineOptions?;
|
|
232
|
+
private _refillController?;
|
|
213
233
|
private _offlineCamera;
|
|
214
234
|
private _input;
|
|
215
235
|
private _adaptiveScale;
|
|
@@ -238,22 +258,33 @@ export default class Render {
|
|
|
238
258
|
private _lastDenoiseTime;
|
|
239
259
|
private _warnedDenoisedReadback;
|
|
240
260
|
constructor(engine: Engine, options?: RenderOptions);
|
|
241
|
-
/**
|
|
242
|
-
/**
|
|
261
|
+
/** Output width: exact for fixed renders; otherwise the target width, or the requested width before allocation. */ get width(): number;
|
|
262
|
+
/** Output height: exact for fixed renders; otherwise the target height, or the requested height before allocation. */ get height(): number;
|
|
243
263
|
/** Complete beauty samples since the last restart; stale engine inputs report 0. Default 0. */
|
|
244
264
|
get samples(): number;
|
|
245
265
|
/** Whether complete beauty samples reached maxSamples. Default false. */ get converged(): boolean;
|
|
246
266
|
/** Current execution state. Default preparing. */ get status(): RenderStatus;
|
|
247
267
|
private get _current();
|
|
248
|
-
/** Latest WebGPU full beauty sample estimate in milliseconds. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
268
|
+
/** @deprecated Use refineUntil for custom frame loops; this will be removed in a later release. Latest WebGPU full beauty sample estimate in milliseconds, extrapolated from tile GPU time when tiled. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
249
269
|
get gpuSampleTimeMs(): number | undefined;
|
|
250
|
-
/**
|
|
251
|
-
|
|
270
|
+
/**
|
|
271
|
+
* @deprecated Use refineUntil; this will be removed in a later release. Waits for the
|
|
272
|
+
* next release of WebGPU sampling queue capacity and returns true when sampling was
|
|
273
|
+
* queued. Returns false immediately with no queued sampling, on WebGL, or for only
|
|
274
|
+
* denoising, display or offline convergence work. Does not submit work. Throws after
|
|
275
|
+
* disposal.
|
|
276
|
+
*/
|
|
277
|
+
whenReadyToRefine(): Promise<boolean>;
|
|
252
278
|
/** Complete surface-output samples since restart; stale inputs report 0. Independent of beauty samples. Default 0. */
|
|
253
279
|
get aovSamplesRendered(): number;
|
|
254
280
|
private _refineAov;
|
|
255
281
|
private _validate;
|
|
256
282
|
private _size;
|
|
283
|
+
/**
|
|
284
|
+
* Backends may round targets up. A fixed render widens the view over the extra
|
|
285
|
+
* pixels so the requested centered rectangle keeps its framing and square pixels.
|
|
286
|
+
*/
|
|
287
|
+
private _targetCamera;
|
|
257
288
|
private _settings;
|
|
258
289
|
private _synchronize;
|
|
259
290
|
private _trace;
|
|
@@ -261,6 +292,23 @@ export default class Render {
|
|
|
261
292
|
private _cancelAdaptiveFrames;
|
|
262
293
|
/** Accumulates within this frame's budget. Default undefined traces beauty and requested AOVs. Pass an enabled AOV kind to refine surface outputs only, without beauty or denoising; progress is aovSamplesRendered. Options default to this render's execution settings. Rejects calls during converge or after disposal. */
|
|
263
294
|
refine(channel?: AovKind, options?: RefineOptions): RenderStatus;
|
|
295
|
+
/**
|
|
296
|
+
* Calls refine() once synchronously, then keeps sampling on WebGPU until deadline.
|
|
297
|
+
* Call present() right after it; later samples appear at the next presentation.
|
|
298
|
+
* Sampling continues only with batchSamples on, the final channel displayed, a
|
|
299
|
+
* visible page and tiles that fit timeBudgetMs. It stops on abort or convergence.
|
|
300
|
+
* Preparing, compiling, denoising and the first incomplete sample advance only
|
|
301
|
+
* through the synchronous call, which is all WebGL runs. A new call ends the
|
|
302
|
+
* previous one. Asynchronous errors set status to error and resolve; synchronous
|
|
303
|
+
* errors follow refine().
|
|
304
|
+
* @param deadline End time in performance.now() milliseconds.
|
|
305
|
+
* @param options.signal Optional application cancellation; default none. An
|
|
306
|
+
* already aborted signal still permits the synchronous refine().
|
|
307
|
+
*/
|
|
308
|
+
refineUntil(deadline: number, options?: {
|
|
309
|
+
signal?: AbortSignal;
|
|
310
|
+
}): Promise<void>;
|
|
311
|
+
private _refineToDeadline;
|
|
264
312
|
private _refine;
|
|
265
313
|
private _refineOne;
|
|
266
314
|
private _schedule;
|
package/lib/Render.js
CHANGED
|
@@ -104,6 +104,7 @@ export default class Render {
|
|
|
104
104
|
/** Maximum complete samples. Default 2048. */ maxSamples = 2048;
|
|
105
105
|
/**
|
|
106
106
|
* Maximum scattering depth, rounded and clamped to [1, 20] (NaN becomes 1).
|
|
107
|
+
* Complete caustic light-to-camera paths can be longer than this depth.
|
|
107
108
|
* Total path events, including transmission and alpha skips, are capped at 20
|
|
108
109
|
* and at three times this depth. Intel Mac WebGL uses min(depth, 3), doubled
|
|
109
110
|
* for scenes with transmission or alpha, with its existing early termination.
|
|
@@ -111,6 +112,7 @@ export default class Render {
|
|
|
111
112
|
*/ bounces = 3;
|
|
112
113
|
/** Per-sample radiance clamp; 0 disables it. Default 0. */ clamping = 0;
|
|
113
114
|
/** Sampling sequence. Default sobol. */ samplingPattern = 'sobol';
|
|
115
|
+
/** Caustic estimators. Default undefined (all disabled). */ caustics;
|
|
114
116
|
/** WebGPU light candidates per vertex; ignored on WebGL. Default 4. */ directLightCandidates = 4;
|
|
115
117
|
/** Rounded material edges. Default true. */ bevel = true;
|
|
116
118
|
/** Bevel probes, clamped to 1–32. Default 8. */ bevelSamples = 8;
|
|
@@ -118,8 +120,8 @@ export default class Render {
|
|
|
118
120
|
/** WebGPU primary-ray alpha policy; ignored on WebGL. Default stochastic. */ alphaBlendStrategy = 'stochastic';
|
|
119
121
|
/** Minimum transmissive path alpha. Default 1. Changing it restarts this render. */ transmissiveOutputAlphaMin = 1;
|
|
120
122
|
/** Execution budget in milliseconds. Default 21. */ timeBudgetMs = 21;
|
|
121
|
-
/** Maximum tiles per frame. Default Infinity. */ maxTiles = Infinity;
|
|
122
|
-
/** Let one refine submit several samples within timeBudgetMs; WebGPU requires GPU timestamps. Default false. */ batchSamples = false;
|
|
123
|
+
/** Maximum tile grid size per sample, rounded up to a rectangular grid. Fewer tiles make each tile larger; this does not cap work per frame or refine call. Default Infinity. */ maxTiles = Infinity;
|
|
124
|
+
/** Let one refine submit several samples within an estimated timeBudgetMs; one sample or tile can exceed it. WebGPU requires GPU timestamps. Default false. */ batchSamples = false;
|
|
123
125
|
/** @deprecated Use batchSamples; this alias will be removed in a later release. Default false. */
|
|
124
126
|
get allowMultipleSamplesPerFrame() { return this.batchSamples; }
|
|
125
127
|
set allowMultipleSamplesPerFrame(value) { this.batchSamples = value; }
|
|
@@ -180,6 +182,7 @@ export default class Render {
|
|
|
180
182
|
_disposed = false;
|
|
181
183
|
_busy = false;
|
|
182
184
|
_refineOptions;
|
|
185
|
+
_refillController;
|
|
183
186
|
_offlineCamera = false;
|
|
184
187
|
_input = { scene: -1, camera: -1, settings: '' };
|
|
185
188
|
_adaptiveScale = 1;
|
|
@@ -231,8 +234,8 @@ export default class Render {
|
|
|
231
234
|
if (denoiser)
|
|
232
235
|
void this.setDenoiser(denoiser).catch(error => this._engine._notifyError(error));
|
|
233
236
|
}
|
|
234
|
-
/**
|
|
235
|
-
/**
|
|
237
|
+
/** Output width: exact for fixed renders; otherwise the target width, or the requested width before allocation. */ get width() { return this._fixed?.width ?? (this._target?.getMemory().width || this._size().width); }
|
|
238
|
+
/** Output height: exact for fixed renders; otherwise the target height, or the requested height before allocation. */ get height() { return this._fixed?.height ?? (this._target?.getMemory().height || this._size().height); }
|
|
236
239
|
/** Complete beauty samples since the last restart; stale engine inputs report 0. Default 0. */
|
|
237
240
|
get samples() { return this._current ? this._target?.getTotalSamplesRendered() ?? 0 : 0; }
|
|
238
241
|
/** Whether complete beauty samples reached maxSamples. Default false. */ get converged() { return this.samples >= this.maxSamples; }
|
|
@@ -246,13 +249,29 @@ export default class Render {
|
|
|
246
249
|
/** @internal */ get _presentable() { return this._complete && (!this._denoisePending || this._denoisedSamples > 0 || (this._schedule() === 'converged' && this.maxSamples > 1)); }
|
|
247
250
|
/** @internal */ get _output() { if (this._disposed)
|
|
248
251
|
throw new Error('Render is disposed.'); return this._target; }
|
|
249
|
-
/** Latest WebGPU full beauty sample estimate in milliseconds. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
250
|
-
get gpuSampleTimeMs() { return
|
|
251
|
-
/**
|
|
252
|
-
|
|
252
|
+
/** @deprecated Use refineUntil for custom frame loops; this will be removed in a later release. Latest WebGPU full beauty sample estimate in milliseconds, extrapolated from tile GPU time when tiled. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
253
|
+
get gpuSampleTimeMs() { return this._gpuSampleTimeMs; }
|
|
254
|
+
/** @internal Latest WebGPU full beauty sample estimate in milliseconds; with tiling, extrapolated from tile GPU time. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
255
|
+
get _gpuSampleTimeMs() { return !this._busy && this._current ? this._target?.getRealtimeSampleTime?.() : undefined; }
|
|
256
|
+
/** @internal Latest WebGPU beauty tile estimate in milliseconds, including amortized caustic work; equal to the sample estimate without tiling. Default undefined; also undefined for stale inputs, during converge or without GPU timestamps. */
|
|
257
|
+
get gpuTileTimeMs() { return !this._busy && this._current ? this._target?.getRealtimeTileTime?.() : undefined; }
|
|
258
|
+
/**
|
|
259
|
+
* @deprecated Use refineUntil; this will be removed in a later release. Waits for the
|
|
260
|
+
* next release of WebGPU sampling queue capacity and returns true when sampling was
|
|
261
|
+
* queued. Returns false immediately with no queued sampling, on WebGL, or for only
|
|
262
|
+
* denoising, display or offline convergence work. Does not submit work. Throws after
|
|
263
|
+
* disposal.
|
|
264
|
+
*/
|
|
265
|
+
whenReadyToRefine() { return this._whenReadyToRefine(); }
|
|
266
|
+
/** @internal Waits for the next release of WebGPU sampling queue capacity and returns true
|
|
267
|
+
* when sampling was queued. Returns false immediately with no queued sampling,
|
|
268
|
+
* on WebGL, or for only denoising, display or offline convergence work.
|
|
269
|
+
* Does not submit work. Throws after disposal.
|
|
270
|
+
*/
|
|
271
|
+
_whenReadyToRefine() {
|
|
253
272
|
if (this._disposed)
|
|
254
273
|
throw new Error('Render is disposed.');
|
|
255
|
-
return this._target?.whenTraceAvailable?.() ?? Promise.resolve();
|
|
274
|
+
return !this._busy ? this._target?.whenTraceAvailable?.() ?? Promise.resolve(false) : Promise.resolve(false);
|
|
256
275
|
}
|
|
257
276
|
/** Complete surface-output samples since restart; stale inputs report 0. Independent of beauty samples. Default 0. */
|
|
258
277
|
get aovSamplesRendered() { return this._current ? this._target?.getAovSamples?.() ?? 0 : 0; }
|
|
@@ -289,7 +308,7 @@ export default class Render {
|
|
|
289
308
|
return this._status = 'preparing';
|
|
290
309
|
const before = target.getAovSamples?.() ?? 0;
|
|
291
310
|
const limit = kind === 'id' || kind === 'motion' ? 1 : Math.min(this.maxSamples, this.aovSamples);
|
|
292
|
-
if (!target.refineAovs?.(this._engine._inputs.value, Math.min(limit, (target.getAovSamples?.() ?? 0) + 1)))
|
|
311
|
+
if (!target.refineAovs?.(this._targetCamera(this._engine._inputs.value), Math.min(limit, (target.getAovSamples?.() ?? 0) + 1)))
|
|
293
312
|
return this._status = 'compiling';
|
|
294
313
|
if ((target.getAovSamples?.() ?? 0) > before) {
|
|
295
314
|
this._frameTime = Math.max(.001, performance.now() - started);
|
|
@@ -317,6 +336,45 @@ export default class Render {
|
|
|
317
336
|
const scale = this.adaptiveResolution ? Math.min(this._adaptiveScale, this._resolutionScale) : this._resolutionScale;
|
|
318
337
|
return { width: Math.max(1, Math.round(this._engine.display.canvas.width * scale)), height: Math.max(1, Math.round(this._engine.display.canvas.height * scale)) };
|
|
319
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Backends may round targets up. A fixed render widens the view over the extra
|
|
341
|
+
* pixels so the requested centered rectangle keeps its framing and square pixels.
|
|
342
|
+
*/
|
|
343
|
+
_targetCamera(camera) {
|
|
344
|
+
const memory = this._target?.getMemory(), fixed = this._fixed;
|
|
345
|
+
if (!memory || !fixed || (memory.width === fixed.width && memory.height === fixed.height))
|
|
346
|
+
return camera;
|
|
347
|
+
const scaleX = memory.width / fixed.width, scaleY = memory.height / fixed.height;
|
|
348
|
+
const widened = camera.clone();
|
|
349
|
+
widened.transform = camera.transform;
|
|
350
|
+
if (camera.projection === 'perspective') {
|
|
351
|
+
widened.fov = Math.atan(Math.tan(camera.fov * Math.PI / 360) * scaleY) * 360 / Math.PI;
|
|
352
|
+
widened.aspect = (camera.aspect ?? fixed.width / fixed.height) * scaleX / scaleY;
|
|
353
|
+
// Keeps the lens focal length, and with it depth of field.
|
|
354
|
+
widened.sensorHeight = camera.sensorHeight * scaleY;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
const x = (camera.left + camera.right) / 2, y = (camera.bottom + camera.top) / 2;
|
|
358
|
+
widened.left = x + (camera.left - x) * scaleX;
|
|
359
|
+
widened.right = x + (camera.right - x) * scaleX;
|
|
360
|
+
widened.bottom = y + (camera.bottom - y) * scaleY;
|
|
361
|
+
widened.top = y + (camera.top - y) * scaleY;
|
|
362
|
+
}
|
|
363
|
+
return widened;
|
|
364
|
+
}
|
|
365
|
+
/** @internal Crops a target-sized RGBA image to a fixed render's requested rectangle. */
|
|
366
|
+
_cropOutput(image) {
|
|
367
|
+
const fixed = this._fixed;
|
|
368
|
+
if (!fixed || !image.width || (image.width === fixed.width && image.height === fixed.height))
|
|
369
|
+
return image;
|
|
370
|
+
const { width, height } = fixed, data = new image.data.constructor(width * height * 4);
|
|
371
|
+
const left = (image.width - width) >> 1, top = (image.height - height) >> 1;
|
|
372
|
+
for (let y = 0; y < height; y++) {
|
|
373
|
+
const start = ((y + top) * image.width + left) * 4;
|
|
374
|
+
data.set(image.data.subarray(start, start + width * 4), y * width * 4);
|
|
375
|
+
}
|
|
376
|
+
return { width, height, data };
|
|
377
|
+
}
|
|
320
378
|
_settings(sampleCap = this.maxSamples) {
|
|
321
379
|
const settings = collectTraceSettings(this, sampleCap);
|
|
322
380
|
settings.batchSamples = this._refineOptions?.batchSamples ?? settings.batchSamples;
|
|
@@ -365,7 +423,7 @@ export default class Render {
|
|
|
365
423
|
this._sampleStart = performance.now();
|
|
366
424
|
this._sampleScale = this._adaptiveScale;
|
|
367
425
|
}
|
|
368
|
-
const compiling = target.trace(this._engine._inputs.value, limit);
|
|
426
|
+
const compiling = target.trace(this._targetCamera(this._engine._inputs.value), limit);
|
|
369
427
|
if (compiling)
|
|
370
428
|
this._sampleStart = undefined;
|
|
371
429
|
return compiling;
|
|
@@ -427,6 +485,95 @@ export default class Render {
|
|
|
427
485
|
this._engine._reportShaderProgress();
|
|
428
486
|
}
|
|
429
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Calls refine() once synchronously, then keeps sampling on WebGPU until deadline.
|
|
490
|
+
* Call present() right after it; later samples appear at the next presentation.
|
|
491
|
+
* Sampling continues only with batchSamples on, the final channel displayed, a
|
|
492
|
+
* visible page and tiles that fit timeBudgetMs. It stops on abort or convergence.
|
|
493
|
+
* Preparing, compiling, denoising and the first incomplete sample advance only
|
|
494
|
+
* through the synchronous call, which is all WebGL runs. A new call ends the
|
|
495
|
+
* previous one. Asynchronous errors set status to error and resolve; synchronous
|
|
496
|
+
* errors follow refine().
|
|
497
|
+
* @param deadline End time in performance.now() milliseconds.
|
|
498
|
+
* @param options.signal Optional application cancellation; default none. An
|
|
499
|
+
* already aborted signal still permits the synchronous refine().
|
|
500
|
+
*/
|
|
501
|
+
refineUntil(deadline, options) {
|
|
502
|
+
return this._refineToDeadline(deadline, options?.signal);
|
|
503
|
+
}
|
|
504
|
+
_refineToDeadline(deadline, signal, options, beforeRefill) {
|
|
505
|
+
this._refillController?.abort();
|
|
506
|
+
const controller = this._refillController = new AbortController();
|
|
507
|
+
// Keep lifecycle exceptions synchronous, just like refine(). Install the
|
|
508
|
+
// controller first so a synchronous sample listener can revoke this call.
|
|
509
|
+
try {
|
|
510
|
+
this.refine(undefined, options);
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
controller.abort();
|
|
514
|
+
if (this._refillController === controller)
|
|
515
|
+
this._refillController = undefined;
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
const device = this._engine.device;
|
|
519
|
+
const canContinue = (afterWait = false) => !controller.signal.aborted && !signal?.aborted &&
|
|
520
|
+
!this._disposed && !this._busy && device !== undefined && device === this._engine.device &&
|
|
521
|
+
(typeof document === 'undefined' || !document.hidden) && performance.now() < deadline &&
|
|
522
|
+
this.batchSamples && this.status === 'converging' && !this.denoising && this.samples > 0 &&
|
|
523
|
+
this.gpuTileTimeMs !== undefined && this.gpuTileTimeMs <= this.timeBudgetMs &&
|
|
524
|
+
this._engine.display.channel === 'final' && (!afterWait || this._engine._isPresented(this._target));
|
|
525
|
+
if (!canContinue()) {
|
|
526
|
+
if (this._refillController === controller)
|
|
527
|
+
this._refillController = undefined;
|
|
528
|
+
return Promise.resolve();
|
|
529
|
+
}
|
|
530
|
+
return (async () => {
|
|
531
|
+
let timer;
|
|
532
|
+
const abort = () => controller.abort();
|
|
533
|
+
const visibility = () => { if (document.hidden)
|
|
534
|
+
abort(); };
|
|
535
|
+
let stopped;
|
|
536
|
+
const cancelled = new Promise(resolve => { stopped = () => resolve(false); });
|
|
537
|
+
controller.signal.addEventListener('abort', stopped, { once: true });
|
|
538
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
539
|
+
if (typeof document !== 'undefined')
|
|
540
|
+
document.addEventListener('visibilitychange', visibility);
|
|
541
|
+
if (Number.isFinite(deadline))
|
|
542
|
+
timer = setTimeout(abort, Math.min(2147483647, Math.max(0, deadline - performance.now())));
|
|
543
|
+
try {
|
|
544
|
+
while (canContinue()) {
|
|
545
|
+
if (!await Promise.race([this._whenReadyToRefine(), cancelled]))
|
|
546
|
+
break;
|
|
547
|
+
beforeRefill?.();
|
|
548
|
+
if (!canContinue(true))
|
|
549
|
+
break;
|
|
550
|
+
this.refine(undefined, { targetBatchTimeMs: 4, deferResolve: true });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
if (!controller.signal.aborted && !this._disposed) {
|
|
555
|
+
this._engine._setRenderError(this._engine._notifyError(error));
|
|
556
|
+
this._status = 'error';
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
finally {
|
|
560
|
+
if (timer !== undefined)
|
|
561
|
+
clearTimeout(timer);
|
|
562
|
+
signal?.removeEventListener('abort', abort);
|
|
563
|
+
controller.signal.removeEventListener('abort', stopped);
|
|
564
|
+
if (typeof document !== 'undefined')
|
|
565
|
+
document.removeEventListener('visibilitychange', visibility);
|
|
566
|
+
if (this._refillController === controller)
|
|
567
|
+
this._refillController = undefined;
|
|
568
|
+
}
|
|
569
|
+
})();
|
|
570
|
+
}
|
|
571
|
+
/** @internal Whether this render currently has a between-frame lease. */
|
|
572
|
+
get _refilling() { return !!this._refillController && !this._refillController.signal.aborted; }
|
|
573
|
+
/** @internal Preserve loop-specific synchronous overrides while using refineUntil. */
|
|
574
|
+
_refineUntil(deadline, signal, options, beforeRefill) {
|
|
575
|
+
return this._refineToDeadline(deadline, signal, options, beforeRefill);
|
|
576
|
+
}
|
|
430
577
|
_refine() {
|
|
431
578
|
if (this._engine._initializationError)
|
|
432
579
|
throw this._engine._initializationError;
|
|
@@ -455,7 +602,7 @@ export default class Render {
|
|
|
455
602
|
if (this._trace(target, limit))
|
|
456
603
|
return this._status = 'compiling';
|
|
457
604
|
}
|
|
458
|
-
else if (target.refineAovs && !target.refineAovs(this._engine._inputs.value)) {
|
|
605
|
+
else if (target.refineAovs && !target.refineAovs(this._targetCamera(this._engine._inputs.value))) {
|
|
459
606
|
return this._status = 'compiling';
|
|
460
607
|
}
|
|
461
608
|
this._maybeDenoise(false, limit);
|
|
@@ -572,9 +719,10 @@ export default class Render {
|
|
|
572
719
|
});
|
|
573
720
|
}
|
|
574
721
|
_cancelDenoise() { this._denoiseController?.abort(); this._denoiseController = undefined; this._denoiseRevision++; this._target?.cancelDenoise?.(); this._denoisePending = undefined; this._denoisedSamples = 0; }
|
|
575
|
-
/** @internal */ _inputsChanged(resetTiming = false) { this._cancelDenoise(); if (resetTiming)
|
|
722
|
+
/** @internal */ _inputsChanged(resetTiming = false) { this._refillController?.abort(); this._cancelDenoise(); if (resetTiming)
|
|
576
723
|
this._target?.invalidateTiming?.(); }
|
|
577
724
|
/** @internal */ _deviceLost(recovering) {
|
|
725
|
+
this._refillController?.abort();
|
|
578
726
|
this._cancelAdaptiveFrames();
|
|
579
727
|
this._cancelDenoise();
|
|
580
728
|
this._filterRevision++;
|
|
@@ -585,7 +733,7 @@ export default class Render {
|
|
|
585
733
|
this._input = { scene: -1, camera: -1, settings: '' };
|
|
586
734
|
this._status = recovering ? 'preparing' : 'error';
|
|
587
735
|
}
|
|
588
|
-
/** @internal */ _displayChanged() { this._cancelDenoise(); this._target?.invalidateDenoisedOutput(); }
|
|
736
|
+
/** @internal */ _displayChanged() { this._refillController?.abort(); this._cancelDenoise(); this._target?.invalidateDenoisedOutput(); }
|
|
589
737
|
_maybeDenoise(force = false, sampleTarget = this._nextSampleTarget()) {
|
|
590
738
|
const target = this._target, filter = this._filter;
|
|
591
739
|
if (!target || !filter || this._denoisePending || this.samples < 1 || this._denoisedSamples === this.samples)
|
|
@@ -624,7 +772,7 @@ export default class Render {
|
|
|
624
772
|
if (frameStart !== undefined)
|
|
625
773
|
this._frameTime = Math.max(this._frameTime, this._lastDenoiseTime - frameStart);
|
|
626
774
|
}
|
|
627
|
-
});
|
|
775
|
+
}, controller.signal);
|
|
628
776
|
this._denoisePending = task;
|
|
629
777
|
void task.catch(error => { if (revision === this._denoiseRevision && !this._disposed)
|
|
630
778
|
this._engine._notifyError(error); }).finally(() => {
|
|
@@ -648,7 +796,7 @@ export default class Render {
|
|
|
648
796
|
if (!target?.readAov)
|
|
649
797
|
throw new Error('AOV output is not ready');
|
|
650
798
|
target.activate();
|
|
651
|
-
return target.readAov(aov);
|
|
799
|
+
return this._cropOutput(await target.readAov(aov));
|
|
652
800
|
}
|
|
653
801
|
if (!target || this.samples === 0)
|
|
654
802
|
return { width: 0, height: 0, data: new Float32Array(0) };
|
|
@@ -660,7 +808,7 @@ export default class Render {
|
|
|
660
808
|
kind = 'hdr';
|
|
661
809
|
}
|
|
662
810
|
target.activate();
|
|
663
|
-
return target.readOutput(kind);
|
|
811
|
+
return this._cropOutput(await target.readOutput(kind));
|
|
664
812
|
}
|
|
665
813
|
/** Borrows a read-only WebGPU texture; undefined on WebGL or before a complete sample. Kind is required. HDR is native resolved linear ray color before denoising/display composition, with DenoiseInput.color normalization and coverage. Display is the latest compose, including filters and display passes. Valid only until this render's next refine/converge, display present/setSize, restart/dispose or device loss; never retain, write or destroy it. */
|
|
666
814
|
getOutputTexture(kind) {
|
|
@@ -670,11 +818,12 @@ export default class Render {
|
|
|
670
818
|
}
|
|
671
819
|
/** Discards only this render's accumulation and pending denoising. */
|
|
672
820
|
restart() { if (this._disposed)
|
|
673
|
-
throw new Error('Render is disposed.'); this._cancelDenoise(); this._cancelAdaptiveFrames(); this._sampleStart = this._frameStart = undefined; this._frameTime = 0; this._target?.restartSamples(); this._status = 'converging'; }
|
|
821
|
+
throw new Error('Render is disposed.'); this._refillController?.abort(); this._cancelDenoise(); this._cancelAdaptiveFrames(); this._sampleStart = this._frameStart = undefined; this._frameTime = 0; this._target?.restartSamples(); this._status = 'converging'; }
|
|
674
822
|
/** Releases only this render's image resources and denoiser. */
|
|
675
823
|
dispose() {
|
|
676
824
|
if (this._disposed)
|
|
677
825
|
return;
|
|
826
|
+
this._refillController?.abort();
|
|
678
827
|
this._disposed = true;
|
|
679
828
|
this._events.clear();
|
|
680
829
|
this._filterRevision++;
|
|
@@ -720,6 +869,7 @@ export default class Render {
|
|
|
720
869
|
if (current.scene !== inputs.scene || current.camera !== inputs.camera)
|
|
721
870
|
throw new Error('Engine inputs changed during converge.');
|
|
722
871
|
};
|
|
872
|
+
this._refillController?.abort();
|
|
723
873
|
this._busy = true;
|
|
724
874
|
const abortConverge = () => this._cancelDenoise();
|
|
725
875
|
options.signal?.addEventListener('abort', abortConverge, { once: true });
|
|
@@ -759,7 +909,7 @@ export default class Render {
|
|
|
759
909
|
target.activate();
|
|
760
910
|
target.setOfflineRenderOptions({ samplesPerBatch: batch, deferPostProcess: true, sampleSequenceOffset: offset });
|
|
761
911
|
const before = target.getTotalSamplesRendered(), batchStarted = performance.now();
|
|
762
|
-
this._status = target.trace(camera, cap) ? 'compiling' : 'converging';
|
|
912
|
+
this._status = target.trace(this._targetCamera(camera), cap) ? 'compiling' : 'converging';
|
|
763
913
|
const count = target.getTotalSamplesRendered();
|
|
764
914
|
if (count !== previous) {
|
|
765
915
|
previous = count;
|