@meimu/mutracer 0.3.0 → 0.3.1
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/CHANGELOG.md +222 -0
- package/LICENSES/FidelityFX-FSR.txt +19 -0
- package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
- package/THIRD_PARTY_NOTICES.md +18 -4
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +6260 -5580
- package/docs/api.md +45 -18
- package/docs/materials.md +4 -2
- package/docs/performance.md +7 -0
- package/docs/post-processing.md +62 -9
- package/docs/progressive-rendering.md +3 -0
- package/docs/sampling.md +4 -0
- package/docs/technical/acceleration.md +52 -0
- package/docs/technical/aovs.md +79 -0
- package/docs/technical/caustics.md +109 -0
- package/docs/technical/continuous-spectrum.md +29 -0
- package/docs/technical/display-upscale.md +82 -0
- package/docs/technical/openpbr.md +99 -0
- package/docs/technical/pbrt-import.md +168 -0
- package/docs/technical/tiling.md +74 -0
- package/docs/technical/webgl.md +53 -0
- package/docs/viewer.md +11 -1
- package/lib/Display.d.ts +19 -5
- package/lib/Display.js +10 -7
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +2 -1
- package/lib/Engine.js +30 -2
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +20 -11
- package/lib/Render.js +32 -7
- package/lib/Render.js.map +1 -1
- package/lib/main.d.ts +3 -1
- package/lib/main.js +1 -0
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/renderer/AovPrimitiveCache.js +39 -8
- package/lib/renderer/AovPrimitiveCache.js.map +1 -1
- package/lib/renderer/DisplayPostProcess.d.ts +8 -3
- package/lib/renderer/DisplayPostProcess.js +51 -18
- package/lib/renderer/DisplayPostProcess.js.map +1 -1
- package/lib/renderer/MaterialData.js +7 -2
- package/lib/renderer/MaterialData.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +7 -1
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/TileRender.d.ts +2 -2
- package/lib/renderer/TileRender.js +128 -7
- package/lib/renderer/TileRender.js.map +1 -1
- package/lib/renderer/WGPUAov.d.ts +6 -1
- package/lib/renderer/WGPUAov.js +108 -14
- package/lib/renderer/WGPUAov.js.map +1 -1
- package/lib/renderer/WGPUCanvasSurface.js +12 -1
- package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
- package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPURayTraceOutput.js +12 -12
- package/lib/renderer/WGPURayTracePass.d.ts +17 -4
- package/lib/renderer/WGPURayTracePass.js +194 -71
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +46 -40
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLAov.js +3 -3
- package/lib/renderer/WebGLAov.js.map +1 -1
- package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
- package/lib/renderer/WebGLFullQuadPass.js +8 -2
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
- package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
- package/lib/renderer/WebGLRayTracePass.js +115 -111
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +10 -3
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.js +57 -57
- package/lib/renderer/aov.d.ts +13 -1
- package/lib/renderer/aov.js +21 -2
- package/lib/renderer/aov.js.map +1 -1
- package/lib/renderer/aovDisplay.js +2 -2
- package/lib/renderer/aovDisplay.js.map +1 -1
- package/lib/renderer/binnedSahBvh.d.ts +2 -1
- package/lib/renderer/binnedSahBvh.js +24 -17
- package/lib/renderer/binnedSahBvh.js.map +1 -1
- package/lib/renderer/bvhAccel.d.ts +3 -1
- package/lib/renderer/bvhAccel.js +36 -6
- package/lib/renderer/bvhAccel.js.map +1 -1
- package/lib/renderer/cameraUniform.d.ts +1 -1
- package/lib/renderer/cameraUniform.js +6 -2
- package/lib/renderer/cameraUniform.js.map +1 -1
- package/lib/renderer/compactVertices.d.ts +14 -0
- package/lib/renderer/compactVertices.js +124 -0
- package/lib/renderer/compactVertices.js.map +1 -0
- package/lib/renderer/easu.d.ts +11 -0
- package/lib/renderer/easu.js +156 -0
- package/lib/renderer/easu.js.map +1 -0
- package/lib/renderer/geometryUpdateState.d.ts +1 -0
- package/lib/renderer/geometryUpdateState.js.map +1 -1
- package/lib/renderer/geometryUpdates.js +3 -2
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
- package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
- package/lib/renderer/glsl/aovTrace.frag.js +44 -36
- package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
- package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
- package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
- package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
- package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
- 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 +12 -6
- package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
- package/lib/renderer/glsl/rayTrace.frag.js +233 -170
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
- package/lib/renderer/gpuMemory.js +1 -1
- package/lib/renderer/gpuMemory.js.map +1 -1
- package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
- package/lib/renderer/openPbrEnergyLut.js +18 -1
- package/lib/renderer/openPbrEnergyLut.js.map +1 -1
- package/lib/renderer/pathLimits.d.ts +5 -4
- package/lib/renderer/pathLimits.js +6 -5
- package/lib/renderer/pathLimits.js.map +1 -1
- package/lib/renderer/prepareMeshBVH.d.ts +2 -0
- package/lib/renderer/prepareMeshBVH.js +5 -3
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/progressiveScheduler.js +1 -1
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +4 -0
- package/lib/renderer/renderPipeline.js +1 -0
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/researchTransport.d.ts +1 -1
- package/lib/renderer/researchTransport.js.map +1 -1
- package/lib/renderer/sceneAssembly.d.ts +1 -1
- package/lib/renderer/sceneAssembly.js +4 -3
- package/lib/renderer/sceneAssembly.js.map +1 -1
- package/lib/renderer/sceneChangeDetector.d.ts +1 -1
- package/lib/renderer/sceneChangeDetector.js +3 -1
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- 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/scenePreparation.d.ts +3 -2
- package/lib/renderer/scenePreparation.js +18 -2
- package/lib/renderer/scenePreparation.js.map +1 -1
- package/lib/renderer/scratchSampling.js +6 -6
- package/lib/renderer/spectrum.js +2 -2
- package/lib/renderer/staticBvhPreparation.d.ts +9 -0
- package/lib/renderer/staticBvhPreparation.js +8 -2
- package/lib/renderer/staticBvhPreparation.js.map +1 -1
- package/lib/renderer/surfaceHit.d.ts +10 -0
- package/lib/renderer/surfaceHit.js +22 -0
- package/lib/renderer/surfaceHit.js.map +1 -0
- package/lib/renderer/tangentRequirements.d.ts +2 -0
- package/lib/renderer/tangentRequirements.js +7 -0
- package/lib/renderer/tangentRequirements.js.map +1 -0
- package/lib/renderer/textureAtlas.d.ts +4 -0
- package/lib/renderer/textureAtlas.js +48 -2
- package/lib/renderer/textureAtlas.js.map +1 -1
- package/lib/renderer/webgl.js +4 -0
- package/lib/renderer/webgl.js.map +1 -1
- package/lib/renderer/webglVertexAttributes.d.ts +17 -0
- package/lib/renderer/webglVertexAttributes.js +44 -0
- package/lib/renderer/webglVertexAttributes.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.js +2 -2
- package/lib/renderer/wgpuMaterialConstants.js +4 -3
- package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
- package/lib/renderer/wgpuSceneUniform.js +2 -2
- package/lib/renderer/wgpuSceneUniform.js.map +1 -1
- package/lib/renderer/wgpuShaderSource.d.ts +15 -0
- package/lib/renderer/wgpuShaderSource.js +1 -1
- package/lib/renderer/wgpuShaderSource.js.map +1 -1
- package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
- package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
- package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
- package/lib/renderer/wgsl/causticLight.compute.js +121 -119
- package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
- package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
- package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
- package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
- package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/easu.wgsl.js +80 -0
- package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/jnlm.compute.js +74 -74
- package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
- package/lib/renderer/wgsl/photonMap.compute.js +89 -89
- package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
- package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/Scene.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 +21 -14
- 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/package.json +5 -2
- package/lib/oidnBundledWeights.cdn.d.ts +0 -2
- package/lib/oidnBundledWeights.cdn.js +0 -5
- package/lib/oidnBundledWeights.cdn.js.map +0 -1
- package/lib/renderer/bvh.worker.d.ts +0 -1
- package/lib/renderer/bvh.worker.js +0 -16
- package/lib/renderer/bvh.worker.js.map +0 -1
- package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
- package/lib/renderer/bvhWorkerBuild.js +0 -17
- package/lib/renderer/bvhWorkerBuild.js.map +0 -1
- package/lib/renderer/lightCore.d.ts +0 -12
- package/lib/renderer/lightCore.js +0 -13
- package/lib/renderer/lightCore.js.map +0 -1
- package/lib/renderer/materialCore.d.ts +0 -8
- package/lib/renderer/materialCore.js +0 -9
- package/lib/renderer/materialCore.js.map +0 -1
- package/lib/scene/PixelTexture.d.ts +0 -6
- package/lib/scene/PixelTexture.js +0 -10
- package/lib/scene/PixelTexture.js.map +0 -1
- package/lib/util/studioLighting.d.ts +0 -18
- package/lib/util/studioLighting.js +0 -36
- package/lib/util/studioLighting.js.map +0 -1
|
@@ -127,7 +127,7 @@ export function makeTileRender(gl) {
|
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
-
function getPredictResult(width, height, lastTraceTime, expectFrameTime, tilePixels, maxNumTiles) {
|
|
130
|
+
function getPredictResult(width, height, lastTraceTime, expectFrameTime, tilePixels, maxNumTiles, minimumTileSize, minimumPredictionSize) {
|
|
131
131
|
const maxTileSize = [width, height];
|
|
132
132
|
const rawCount = lastTraceTime > 0 ? expectFrameTime / lastTraceTime : 1;
|
|
133
133
|
// GPU cost is not perfectly linear with tile area: tiny probes include fixed
|
|
@@ -148,6 +148,11 @@ function getPredictResult(width, height, lastTraceTime, expectFrameTime, tilePix
|
|
|
148
148
|
samplesPerFrame *= Math.floor(newTileHeight / maxTileSize[1]);
|
|
149
149
|
}
|
|
150
150
|
newTileHeight = Math.min(newTileHeight, maxTileSize[1]);
|
|
151
|
+
// A compute dispatch still occupies a whole workgroup when fewer lanes
|
|
152
|
+
// are active. Splitting below that footprint repeats the fixed kernel cost
|
|
153
|
+
// and can make timestamp feedback progressively slow down a tiny render.
|
|
154
|
+
newTileWidth = Math.max(newTileWidth, Math.min(width, Math.max(minimumTileSize, minimumPredictionSize[0])));
|
|
155
|
+
newTileHeight = Math.max(newTileHeight, Math.min(height, Math.max(minimumTileSize, minimumPredictionSize[1])));
|
|
151
156
|
let newColumns = Math.round(width / newTileWidth);
|
|
152
157
|
let newRows = Math.round(height / newTileHeight);
|
|
153
158
|
newTileWidth = Math.ceil(width / newColumns);
|
|
@@ -181,7 +186,56 @@ function getInitialProbeSize(width, height) {
|
|
|
181
186
|
Math.min(height, Math.max(1, Math.round(Math.sqrt(initialProbePixels / aspectRatio))))
|
|
182
187
|
];
|
|
183
188
|
}
|
|
184
|
-
|
|
189
|
+
// Keep repeated kernel/longest-path cost below one fifth of the sample where
|
|
190
|
+
// possible. If that conflicts with responsiveness, prioritize the tile ceiling.
|
|
191
|
+
const maximumFixedCostShare = 0.2;
|
|
192
|
+
// A throughput-oriented ceiling still leaves over 5x margin below a 2s TDR.
|
|
193
|
+
// This is a prediction, not a guarantee for unusually expensive sample paths.
|
|
194
|
+
const maximumUnattainableTileMs = 350;
|
|
195
|
+
const unattainableBudgetTolerance = 1.1;
|
|
196
|
+
// Nearby grids amplify timestamp noise when extrapolating to zero pixels.
|
|
197
|
+
const minimumAffineAreaChange = 0.2;
|
|
198
|
+
function getUnattainableBudgetGrid(width, height, fixedMs, msPerPixel, minimumTileSize, maxNumTiles, samplesPerFrame) {
|
|
199
|
+
const areaCost = msPerPixel * width * height;
|
|
200
|
+
const maxColumns = Math.max(1, Math.floor(width / minimumTileSize));
|
|
201
|
+
const maxRows = Math.max(1, Math.floor(height / minimumTileSize));
|
|
202
|
+
// If the fixed cost alone exceeds the ceiling, no subdivision can meet it.
|
|
203
|
+
// Avoid multiplying an unavoidable stall by an ever-growing tile count.
|
|
204
|
+
const required = fixedMs < maximumUnattainableTileMs
|
|
205
|
+
? Math.ceil(areaCost / (maximumUnattainableTileMs - fixedMs)) : 1;
|
|
206
|
+
const minimumTiles = clamp(required, 1, maxColumns * maxRows);
|
|
207
|
+
let fewest;
|
|
208
|
+
let withinBoth;
|
|
209
|
+
const better = (candidate, best) => !best || candidate.count < best.count ||
|
|
210
|
+
(candidate.count === best.count &&
|
|
211
|
+
Math.abs(candidate.columns - candidate.rows) < Math.abs(best.columns - best.rows));
|
|
212
|
+
for (let columns = 1; columns <= Math.min(maxColumns, minimumTiles); columns++) {
|
|
213
|
+
const rows = Math.ceil(minimumTiles / columns);
|
|
214
|
+
if (rows > maxRows)
|
|
215
|
+
continue;
|
|
216
|
+
const count = columns * rows, candidate = { columns, rows, count };
|
|
217
|
+
if (better(candidate, fewest))
|
|
218
|
+
fewest = candidate;
|
|
219
|
+
const tileMs = fixedMs + areaCost / count;
|
|
220
|
+
const fixedShare = count * fixedMs / (count * fixedMs + areaCost);
|
|
221
|
+
if (tileMs <= maximumUnattainableTileMs && fixedShare <= maximumFixedCostShare &&
|
|
222
|
+
better(candidate, withinBoth))
|
|
223
|
+
withinBoth = candidate;
|
|
224
|
+
}
|
|
225
|
+
// Fewer tiles also minimize fixed cost. If even the fewest grid that meets
|
|
226
|
+
// the ceiling exceeds the share limit, no grid can satisfy both constraints.
|
|
227
|
+
let { columns, rows, count: numTiles } = (withinBoth ?? fewest);
|
|
228
|
+
if (numTiles > maxNumTiles) {
|
|
229
|
+
// Preserve the existing rectangular rounding of the public maxTiles cap.
|
|
230
|
+
numTiles = maxNumTiles;
|
|
231
|
+
columns = Math.ceil(Math.sqrt(numTiles));
|
|
232
|
+
rows = Math.ceil(numTiles / columns);
|
|
233
|
+
numTiles = columns * rows;
|
|
234
|
+
}
|
|
235
|
+
return { columns, rows, numTiles, tileWidth: Math.ceil(width / columns),
|
|
236
|
+
tileHeight: Math.ceil(height / rows), samplesPerFrame };
|
|
237
|
+
}
|
|
238
|
+
export function makeTileRenderWithPredict(minimumTileSize = 1) {
|
|
185
239
|
let numTiles = 1;
|
|
186
240
|
let currentTile = -1;
|
|
187
241
|
let lastTraceTime = -1;
|
|
@@ -202,6 +256,18 @@ export function makeTileRenderWithPredict() {
|
|
|
202
256
|
let columns = 0;
|
|
203
257
|
let rows = 0;
|
|
204
258
|
let maxNumTiles = Infinity;
|
|
259
|
+
// Validate a refinement against the grid that motivated it. A smaller
|
|
260
|
+
// dispatch can retain most of the longest path / fixed kernel cost.
|
|
261
|
+
let refinement;
|
|
262
|
+
let minimumPredictionSize = [0, 0];
|
|
263
|
+
let lastMeasurement;
|
|
264
|
+
let unattainableGrid;
|
|
265
|
+
function clearPredictionEvidence() {
|
|
266
|
+
refinement = undefined;
|
|
267
|
+
minimumPredictionSize = [0, 0];
|
|
268
|
+
lastMeasurement = undefined;
|
|
269
|
+
unattainableGrid = undefined;
|
|
270
|
+
}
|
|
205
271
|
function nextTile() {
|
|
206
272
|
currentTile++;
|
|
207
273
|
const avgTileTime = lastFrameTraceTimeValidCount > 0
|
|
@@ -212,7 +278,48 @@ export function makeTileRenderWithPredict() {
|
|
|
212
278
|
}
|
|
213
279
|
if (currentTile === 0 && timingRevision !== appliedTimingRevision) {
|
|
214
280
|
appliedTimingRevision = timingRevision;
|
|
215
|
-
const
|
|
281
|
+
const measured = lastFrameTraceTimeValidCount > 0;
|
|
282
|
+
let rollback;
|
|
283
|
+
if (measured && refinement) {
|
|
284
|
+
const areaRatio = refinement.grid.numTiles / numTiles;
|
|
285
|
+
// Require at least half of the area-proportional improvement, unless
|
|
286
|
+
// the new tile already fits the budget (with 10% timing tolerance).
|
|
287
|
+
if (avgTileTime > expectFrameTime * 1.1 &&
|
|
288
|
+
avgTileTime > refinement.time * (1 - (1 - areaRatio) * 0.5)) {
|
|
289
|
+
rollback = refinement;
|
|
290
|
+
minimumPredictionSize = [rollback.grid.tileWidth, rollback.grid.tileHeight];
|
|
291
|
+
}
|
|
292
|
+
refinement = undefined;
|
|
293
|
+
}
|
|
294
|
+
let result = rollback?.grid ?? getPredictResult(width, height, avgTileTime, expectFrameTime, lastTraceTime >= 0 ? tilePixels : getInitialProbeSize(width, height), maxNumTiles, minimumTileSize, minimumPredictionSize);
|
|
295
|
+
if (measured && columns > 0 && rows > 0 && !unattainableGrid) {
|
|
296
|
+
// Use nominal covered area, rather than ceil-rounded dispatch bounds:
|
|
297
|
+
// the interleaved grid covers every pixel exactly once per sample.
|
|
298
|
+
const point = { area: width * height / numTiles, time: avgTileTime };
|
|
299
|
+
if (lastMeasurement && Math.abs(point.area - lastMeasurement.area) >=
|
|
300
|
+
minimumAffineAreaChange * Math.max(point.area, lastMeasurement.area)) {
|
|
301
|
+
const slope = (point.time - lastMeasurement.time) / (point.area - lastMeasurement.area);
|
|
302
|
+
const fixed = point.time - slope * point.area;
|
|
303
|
+
if (Number.isFinite(slope) && slope > 0 && Number.isFinite(fixed) &&
|
|
304
|
+
fixed >= expectFrameTime * unattainableBudgetTolerance) {
|
|
305
|
+
unattainableGrid = getUnattainableBudgetGrid(width, height, fixed, slope, minimumTileSize, maxNumTiles, result.samplesPerFrame);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
lastMeasurement = point;
|
|
309
|
+
}
|
|
310
|
+
if (unattainableGrid) {
|
|
311
|
+
// Retain the decision until explicit invalidation. Re-fitting on its
|
|
312
|
+
// own probe/target grids can alternate counts as path costs fluctuate.
|
|
313
|
+
// Keep the original samplesPerFrame calculation independent of this policy.
|
|
314
|
+
result = { ...unattainableGrid, samplesPerFrame: result.samplesPerFrame };
|
|
315
|
+
refinement = undefined;
|
|
316
|
+
}
|
|
317
|
+
if (!unattainableGrid && !rollback && measured && result.numTiles > numTiles) {
|
|
318
|
+
refinement = {
|
|
319
|
+
grid: { tileWidth, tileHeight, columns, rows, numTiles, samplesPerFrame },
|
|
320
|
+
time: avgTileTime
|
|
321
|
+
};
|
|
322
|
+
}
|
|
216
323
|
lastFrameTotalTraceTime = 0;
|
|
217
324
|
lastFrameTraceTimeValidCount = 0;
|
|
218
325
|
tileWidth = result.tileWidth;
|
|
@@ -246,28 +353,42 @@ export function makeTileRenderWithPredict() {
|
|
|
246
353
|
currentTile = -1;
|
|
247
354
|
},
|
|
248
355
|
setSize(w, h) {
|
|
249
|
-
if (width !== w || height !== h)
|
|
356
|
+
if (width !== w || height !== h) {
|
|
250
357
|
timingRevision++;
|
|
358
|
+
clearPredictionEvidence();
|
|
359
|
+
lastTraceTime = -1;
|
|
360
|
+
lastFrameTotalTraceTime = 0;
|
|
361
|
+
lastFrameTraceTimeValidCount = 0;
|
|
362
|
+
}
|
|
251
363
|
width = w;
|
|
252
364
|
height = h;
|
|
253
365
|
},
|
|
254
366
|
setMaxTiles(maxTiles) {
|
|
255
|
-
if (maxNumTiles !== maxTiles)
|
|
367
|
+
if (maxNumTiles !== maxTiles) {
|
|
256
368
|
timingRevision++;
|
|
369
|
+
clearPredictionEvidence();
|
|
370
|
+
}
|
|
257
371
|
maxNumTiles = maxTiles;
|
|
258
372
|
},
|
|
259
373
|
setDesiredMsPerTile(time) {
|
|
260
|
-
if (expectFrameTime !== time)
|
|
374
|
+
if (expectFrameTime !== time) {
|
|
261
375
|
timingRevision++;
|
|
376
|
+
clearPredictionEvidence();
|
|
377
|
+
}
|
|
262
378
|
expectFrameTime = time;
|
|
263
379
|
},
|
|
264
380
|
needsUpdatePredicting() {
|
|
381
|
+
clearPredictionEvidence();
|
|
265
382
|
timingRevision++;
|
|
266
383
|
lastTraceTime = -1;
|
|
267
384
|
lastFrameTotalTraceTime = 0;
|
|
268
385
|
lastFrameTraceTimeValidCount = 0;
|
|
269
386
|
},
|
|
270
|
-
setLastTraceTime(time) {
|
|
387
|
+
setLastTraceTime(time, timedColumns = columns, timedRows = rows) {
|
|
388
|
+
// A timestamp can arrive after the next sample changed the grid. Only
|
|
389
|
+
// compare measurements of the grid currently being evaluated.
|
|
390
|
+
if (timedColumns !== columns || timedRows !== rows || !Number.isFinite(time) || time <= 0)
|
|
391
|
+
return;
|
|
271
392
|
timingRevision++;
|
|
272
393
|
lastTraceTime = time;
|
|
273
394
|
lastFrameTraceTimeValidCount++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TileRender.js","sourceRoot":"","sources":["../../src/renderer/TileRender.ts"],"names":[],"mappings":"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","sourcesContent":["// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { clamp } from './util.js';\n\n// TileRender is based on the concept of a compute shader's work group.\n\n// Path tracing the scene can be very slow (<1 fps).\n// This overworks the GPU and tends to lock up the OS, making it unresponsive.\n\n// To fix this, we can split the screen into smaller tiles, and sample the scene one tile at a time\n// The tile size is set such that each tile takes approximately a constant amount of time to render.\n\n// Since the render time of a tile is dependent on the device, we find the desired tile dimensions by measuring\n// the time it takes to render an arbitrarily-set tile size and adjusting the size according to the benchmark.\n\nexport function makeTileRender(gl?: WebGL2RenderingContext) {\n let desiredMsPerTile = 21;\n let maxNumTiles = Infinity;\n\n let currentTile = -1;\n let numTiles = 1;\n let numSamples = 1;\n\n let tileWidth: number;\n let tileHeight: number;\n\n let columns: number;\n let rows: number;\n\n let width = 0;\n let height = 0;\n\n let totalElapsedMs = 0;\n let timedTiles = 0;\n\n // initial number of pixels per rendered tile\n // based on correlation between system performance and max supported render buffer size\n // adjusted dynamically according to system performance\n let pixelsPerTile = gl ? pixelsPerTileEstimate(gl) : 600000;\n\n function reset() {\n currentTile = -1;\n totalElapsedMs = 0;\n timedTiles = 0;\n }\n\n function setSize(w: number, h: number) {\n width = w;\n height = h;\n reset();\n calcTileDimensions();\n }\n\n function calcTileDimensions() {\n const maxWidth = 3000;\n const aspectRatio = width / height;\n\n // quantize the width of the tile so that it evenly divides the entire window\n tileWidth = Math.ceil(\n maxWidth /\n Math.max(\n Math.round(maxWidth / Math.sqrt(pixelsPerTile * aspectRatio)),\n 1\n )\n );\n tileHeight = Math.ceil(tileWidth / aspectRatio);\n\n if (tileWidth > width && tileHeight > height) {\n columns = 1;\n rows = 1;\n numTiles = 1;\n\n numSamples =\n Math.round(tileWidth / width) * Math.round(tileHeight / height);\n } else {\n numSamples = 1;\n\n columns = Math.ceil(width / tileWidth);\n rows = Math.ceil(height / tileHeight);\n numTiles = columns * rows;\n }\n\n if (numTiles > maxNumTiles) {\n // TODO maxNumTiles is not exact value.\n numTiles = maxNumTiles;\n columns = Math.ceil(Math.sqrt(numTiles));\n rows = Math.ceil(numTiles / columns);\n numTiles = columns * rows;\n tileWidth = Math.ceil(width / columns);\n tileHeight = Math.ceil(height / rows);\n }\n }\n\n function updatePixelsPerTile() {\n const msPerTile = totalElapsedMs / timedTiles;\n\n const error = desiredMsPerTile - msPerTile;\n\n // tweak to find balance. higher = faster convergence, lower = less fluctuations to microstutters\n const strength = 5000;\n\n // sqrt prevents massive fluctuations in pixelsPerTile for the occasional stutter\n pixelsPerTile += strength * Math.sign(error) * Math.sqrt(Math.abs(error));\n pixelsPerTile = Math.max(pixelsPerTile, 8192);\n }\n\n function nextTile(elapsedFrameMs?: number) {\n // The supplied time belongs to the previous tile. After a reset it may\n // instead describe scene preparation, compilation, or the previous view.\n if (\n currentTile >= 0 && elapsedFrameMs != null &&\n Number.isFinite(elapsedFrameMs) && elapsedFrameMs > 0\n ) {\n totalElapsedMs += elapsedFrameMs;\n timedTiles++;\n }\n currentTile++;\n\n if (currentTile % numTiles === 0) {\n if (timedTiles) {\n updatePixelsPerTile();\n calcTileDimensions();\n }\n\n totalElapsedMs = 0;\n timedTiles = 0;\n currentTile = 0;\n }\n\n const isLastTile = currentTile === numTiles - 1;\n\n const x = currentTile % columns;\n const y = Math.floor(currentTile / columns) % rows;\n\n return {\n xIndex: x,\n yIndex: y,\n columns,\n rows,\n x: x * tileWidth,\n y: y * tileHeight,\n tileWidth,\n tileHeight,\n isFirstTile: currentTile === 0,\n isLastTile,\n samplesPerFrame: numSamples\n };\n }\n\n return {\n nextTile,\n reset,\n setSize,\n setMaxTiles(maxTiles: number) {\n maxNumTiles = maxTiles;\n },\n setDesiredMsPerTile(time: number) {\n desiredMsPerTile = time;\n },\n getDesiredMsPerTile() {\n return desiredMsPerTile;\n }\n };\n}\n\nfunction getPredictResult(\n width: number,\n height: number,\n lastTraceTime: number,\n expectFrameTime: number,\n tilePixels: number[],\n maxNumTiles: number\n) {\n const maxTileSize = [width, height];\n const rawCount = lastTraceTime > 0 ? expectFrameTime / lastTraceTime : 1;\n // GPU cost is not perfectly linear with tile area: tiny probes include fixed\n // dispatch overhead, while very large tiles can hit cache and divergence\n // cliffs. Limit each update to 2x per dimension so prediction converges in a\n // few samples without jumping from the probe straight to a blocking 4K tile.\n const count = clamp(rawCount, 0.25, 4);\n\n const wScale = Math.sqrt(count);\n const hScale = count / wScale;\n\n let newTileWidth = Math.ceil(wScale * tilePixels[0]);\n let newTileHeight = Math.ceil(hScale * tilePixels[1]);\n\n let samplesPerFrame = 1;\n if (newTileWidth > maxTileSize[0] * 2) {\n samplesPerFrame *= Math.floor(newTileWidth / maxTileSize[0]);\n }\n newTileWidth = Math.min(newTileWidth, maxTileSize[0]);\n if (newTileHeight > maxTileSize[1] * 2) {\n samplesPerFrame *= Math.floor(newTileHeight / maxTileSize[1]);\n }\n newTileHeight = Math.min(newTileHeight, maxTileSize[1]);\n\n let newColumns = Math.round(width / newTileWidth);\n let newRows = Math.round(height / newTileHeight);\n\n newTileWidth = Math.ceil(width / newColumns);\n newTileHeight = Math.ceil(height / newRows);\n\n let newNumTiles = newRows * newColumns;\n\n if (newNumTiles > maxNumTiles) {\n newNumTiles = maxNumTiles;\n newColumns = Math.ceil(Math.sqrt(newNumTiles));\n newRows = Math.ceil(newNumTiles / newColumns);\n newNumTiles = newColumns * newRows;\n newTileWidth = Math.ceil(width / newColumns);\n newTileHeight = Math.ceil(height / newRows);\n }\n\n return {\n tileWidth: newTileWidth,\n tileHeight: newTileHeight,\n columns: newColumns,\n rows: newRows,\n numTiles: newNumTiles,\n samplesPerFrame\n };\n}\n\n// A 64x64 probe is dominated by dispatch and timestamp overhead on modern\n// GPUs. At 4K it also leads to an extremely conservative first prediction.\n// Use a still-small but representative amount of work, shaped like the frame.\nconst initialProbePixels = 262144;\n\nfunction getInitialProbeSize(width: number, height: number) {\n const aspectRatio = width > 0 && height > 0 ? width / height : 1;\n return [\n Math.min(width, Math.max(1, Math.round(Math.sqrt(initialProbePixels * aspectRatio)))),\n Math.min(height, Math.max(1, Math.round(Math.sqrt(initialProbePixels / aspectRatio))))\n ];\n}\n\nexport function makeTileRenderWithPredict() {\n let numTiles = 1;\n let currentTile = -1;\n let lastTraceTime = -1;\n // Several offline samples can be submitted before a timing callback runs.\n // Consume each measurement once so stale feedback cannot repeatedly shrink tiles.\n let timingRevision = 0;\n let appliedTimingRevision = -1;\n let lastFrameTotalTraceTime = 0;\n let lastFrameTraceTimeValidCount = 0;\n let tilePixels = [64, 64];\n let width = 0;\n let height = 0;\n let expectFrameTime = 0;\n\n // Calcualate\n let tileWidth = 0;\n let tileHeight = 0;\n let samplesPerFrame = 1;\n let columns = 0;\n let rows = 0;\n\n let maxNumTiles = Infinity;\n\n function nextTile() {\n currentTile++;\n const avgTileTime =\n lastFrameTraceTimeValidCount > 0\n ? lastFrameTotalTraceTime / lastFrameTraceTimeValidCount\n : lastTraceTime;\n\n if (currentTile % numTiles === 0) {\n currentTile = 0;\n }\n\n if (currentTile === 0 && timingRevision !== appliedTimingRevision) {\n appliedTimingRevision = timingRevision;\n const result = getPredictResult(\n width,\n height,\n avgTileTime,\n expectFrameTime,\n lastTraceTime >= 0 ? tilePixels : getInitialProbeSize(width, height),\n maxNumTiles\n );\n\n lastFrameTotalTraceTime = 0;\n lastFrameTraceTimeValidCount = 0;\n tileWidth = result.tileWidth;\n tileHeight = result.tileHeight;\n columns = result.columns;\n rows = result.rows;\n numTiles = result.numTiles;\n samplesPerFrame = result.samplesPerFrame;\n }\n\n const x = currentTile % columns;\n const y = Math.floor(currentTile / columns) % rows;\n const isLastTile = currentTile === numTiles - 1;\n\n tilePixels = [tileWidth, tileHeight];\n\n return {\n xIndex: x,\n yIndex: y,\n columns,\n rows,\n x: x * tileWidth,\n y: y * tileHeight,\n tileWidth,\n tileHeight,\n isFirstTile: currentTile === 0,\n isLastTile,\n samplesPerFrame\n };\n }\n\n return {\n nextTile,\n reset() {\n currentTile = -1;\n },\n setSize(w: number, h: number) {\n if (width !== w || height !== h) timingRevision++;\n width = w;\n height = h;\n },\n setMaxTiles(maxTiles: number) {\n if (maxNumTiles !== maxTiles) timingRevision++;\n maxNumTiles = maxTiles;\n },\n setDesiredMsPerTile(time: number) {\n if (expectFrameTime !== time) timingRevision++;\n expectFrameTime = time;\n },\n needsUpdatePredicting() {\n timingRevision++;\n lastTraceTime = -1;\n lastFrameTotalTraceTime = 0;\n lastFrameTraceTimeValidCount = 0;\n },\n setLastTraceTime(time: number) {\n timingRevision++;\n lastTraceTime = time;\n lastFrameTraceTimeValidCount++;\n lastFrameTotalTraceTime += time;\n },\n getLastTraceTime() {\n return lastTraceTime;\n },\n getDesiredMsPerTile() {\n return expectFrameTime;\n }\n };\n}\n\n\nfunction pixelsPerTileEstimate(gl: WebGL2RenderingContext) {\n const maxRenderbufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);\n\n if (maxRenderbufferSize <= 8192) {\n return 200000;\n } else {\n // 16K render targets are common on current desktop/integrated GPUs. The\n // old 400K seed unnecessarily split even 720p into several tiles and took\n // dozens of samples to grow to a full-frame tile.\n return 1000000;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"TileRender.js","sourceRoot":"","sources":["../../src/renderer/TileRender.ts"],"names":[],"mappings":"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","sourcesContent":["// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport { clamp } from './util.js';\n\n// TileRender is based on the concept of a compute shader's work group.\n\n// Path tracing the scene can be very slow (<1 fps).\n// This overworks the GPU and tends to lock up the OS, making it unresponsive.\n\n// To fix this, we can split the screen into smaller tiles, and sample the scene one tile at a time\n// The tile size is set such that each tile takes approximately a constant amount of time to render.\n\n// Since the render time of a tile is dependent on the device, we find the desired tile dimensions by measuring\n// the time it takes to render an arbitrarily-set tile size and adjusting the size according to the benchmark.\n\nexport function makeTileRender(gl?: WebGL2RenderingContext) {\n let desiredMsPerTile = 21;\n let maxNumTiles = Infinity;\n\n let currentTile = -1;\n let numTiles = 1;\n let numSamples = 1;\n\n let tileWidth: number;\n let tileHeight: number;\n\n let columns: number;\n let rows: number;\n\n let width = 0;\n let height = 0;\n\n let totalElapsedMs = 0;\n let timedTiles = 0;\n\n // initial number of pixels per rendered tile\n // based on correlation between system performance and max supported render buffer size\n // adjusted dynamically according to system performance\n let pixelsPerTile = gl ? pixelsPerTileEstimate(gl) : 600000;\n\n function reset() {\n currentTile = -1;\n totalElapsedMs = 0;\n timedTiles = 0;\n }\n\n function setSize(w: number, h: number) {\n width = w;\n height = h;\n reset();\n calcTileDimensions();\n }\n\n function calcTileDimensions() {\n const maxWidth = 3000;\n const aspectRatio = width / height;\n\n // quantize the width of the tile so that it evenly divides the entire window\n tileWidth = Math.ceil(\n maxWidth /\n Math.max(\n Math.round(maxWidth / Math.sqrt(pixelsPerTile * aspectRatio)),\n 1\n )\n );\n tileHeight = Math.ceil(tileWidth / aspectRatio);\n\n if (tileWidth > width && tileHeight > height) {\n columns = 1;\n rows = 1;\n numTiles = 1;\n\n numSamples =\n Math.round(tileWidth / width) * Math.round(tileHeight / height);\n } else {\n numSamples = 1;\n\n columns = Math.ceil(width / tileWidth);\n rows = Math.ceil(height / tileHeight);\n numTiles = columns * rows;\n }\n\n if (numTiles > maxNumTiles) {\n // TODO maxNumTiles is not exact value.\n numTiles = maxNumTiles;\n columns = Math.ceil(Math.sqrt(numTiles));\n rows = Math.ceil(numTiles / columns);\n numTiles = columns * rows;\n tileWidth = Math.ceil(width / columns);\n tileHeight = Math.ceil(height / rows);\n }\n }\n\n function updatePixelsPerTile() {\n const msPerTile = totalElapsedMs / timedTiles;\n\n const error = desiredMsPerTile - msPerTile;\n\n // tweak to find balance. higher = faster convergence, lower = less fluctuations to microstutters\n const strength = 5000;\n\n // sqrt prevents massive fluctuations in pixelsPerTile for the occasional stutter\n pixelsPerTile += strength * Math.sign(error) * Math.sqrt(Math.abs(error));\n pixelsPerTile = Math.max(pixelsPerTile, 8192);\n }\n\n function nextTile(elapsedFrameMs?: number) {\n // The supplied time belongs to the previous tile. After a reset it may\n // instead describe scene preparation, compilation, or the previous view.\n if (\n currentTile >= 0 && elapsedFrameMs != null &&\n Number.isFinite(elapsedFrameMs) && elapsedFrameMs > 0\n ) {\n totalElapsedMs += elapsedFrameMs;\n timedTiles++;\n }\n currentTile++;\n\n if (currentTile % numTiles === 0) {\n if (timedTiles) {\n updatePixelsPerTile();\n calcTileDimensions();\n }\n\n totalElapsedMs = 0;\n timedTiles = 0;\n currentTile = 0;\n }\n\n const isLastTile = currentTile === numTiles - 1;\n\n const x = currentTile % columns;\n const y = Math.floor(currentTile / columns) % rows;\n\n return {\n xIndex: x,\n yIndex: y,\n columns,\n rows,\n x: x * tileWidth,\n y: y * tileHeight,\n tileWidth,\n tileHeight,\n isFirstTile: currentTile === 0,\n isLastTile,\n samplesPerFrame: numSamples\n };\n }\n\n return {\n nextTile,\n reset,\n setSize,\n setMaxTiles(maxTiles: number) {\n maxNumTiles = maxTiles;\n },\n setDesiredMsPerTile(time: number) {\n desiredMsPerTile = time;\n },\n getDesiredMsPerTile() {\n return desiredMsPerTile;\n }\n };\n}\n\nfunction getPredictResult(\n width: number,\n height: number,\n lastTraceTime: number,\n expectFrameTime: number,\n tilePixels: number[],\n maxNumTiles: number,\n minimumTileSize: number,\n minimumPredictionSize: number[]\n) {\n const maxTileSize = [width, height];\n const rawCount = lastTraceTime > 0 ? expectFrameTime / lastTraceTime : 1;\n // GPU cost is not perfectly linear with tile area: tiny probes include fixed\n // dispatch overhead, while very large tiles can hit cache and divergence\n // cliffs. Limit each update to 2x per dimension so prediction converges in a\n // few samples without jumping from the probe straight to a blocking 4K tile.\n const count = clamp(rawCount, 0.25, 4);\n\n const wScale = Math.sqrt(count);\n const hScale = count / wScale;\n\n let newTileWidth = Math.ceil(wScale * tilePixels[0]);\n let newTileHeight = Math.ceil(hScale * tilePixels[1]);\n\n let samplesPerFrame = 1;\n if (newTileWidth > maxTileSize[0] * 2) {\n samplesPerFrame *= Math.floor(newTileWidth / maxTileSize[0]);\n }\n newTileWidth = Math.min(newTileWidth, maxTileSize[0]);\n if (newTileHeight > maxTileSize[1] * 2) {\n samplesPerFrame *= Math.floor(newTileHeight / maxTileSize[1]);\n }\n newTileHeight = Math.min(newTileHeight, maxTileSize[1]);\n\n // A compute dispatch still occupies a whole workgroup when fewer lanes\n // are active. Splitting below that footprint repeats the fixed kernel cost\n // and can make timestamp feedback progressively slow down a tiny render.\n newTileWidth = Math.max(newTileWidth,\n Math.min(width, Math.max(minimumTileSize, minimumPredictionSize[0])));\n newTileHeight = Math.max(newTileHeight,\n Math.min(height, Math.max(minimumTileSize, minimumPredictionSize[1])));\n\n let newColumns = Math.round(width / newTileWidth);\n let newRows = Math.round(height / newTileHeight);\n\n newTileWidth = Math.ceil(width / newColumns);\n newTileHeight = Math.ceil(height / newRows);\n\n let newNumTiles = newRows * newColumns;\n\n if (newNumTiles > maxNumTiles) {\n newNumTiles = maxNumTiles;\n newColumns = Math.ceil(Math.sqrt(newNumTiles));\n newRows = Math.ceil(newNumTiles / newColumns);\n newNumTiles = newColumns * newRows;\n newTileWidth = Math.ceil(width / newColumns);\n newTileHeight = Math.ceil(height / newRows);\n }\n\n return {\n tileWidth: newTileWidth,\n tileHeight: newTileHeight,\n columns: newColumns,\n rows: newRows,\n numTiles: newNumTiles,\n samplesPerFrame\n };\n}\n\n// A 64x64 probe is dominated by dispatch and timestamp overhead on modern\n// GPUs. At 4K it also leads to an extremely conservative first prediction.\n// Use a still-small but representative amount of work, shaped like the frame.\nconst initialProbePixels = 262144;\n\nfunction getInitialProbeSize(width: number, height: number) {\n const aspectRatio = width > 0 && height > 0 ? width / height : 1;\n return [\n Math.min(width, Math.max(1, Math.round(Math.sqrt(initialProbePixels * aspectRatio)))),\n Math.min(height, Math.max(1, Math.round(Math.sqrt(initialProbePixels / aspectRatio))))\n ];\n}\n\n// Keep repeated kernel/longest-path cost below one fifth of the sample where\n// possible. If that conflicts with responsiveness, prioritize the tile ceiling.\nconst maximumFixedCostShare = 0.2;\n// A throughput-oriented ceiling still leaves over 5x margin below a 2s TDR.\n// This is a prediction, not a guarantee for unusually expensive sample paths.\nconst maximumUnattainableTileMs = 350;\nconst unattainableBudgetTolerance = 1.1;\n// Nearby grids amplify timestamp noise when extrapolating to zero pixels.\nconst minimumAffineAreaChange = 0.2;\n\nfunction getUnattainableBudgetGrid(\n width: number, height: number, fixedMs: number, msPerPixel: number,\n minimumTileSize: number, maxNumTiles: number, samplesPerFrame: number\n) {\n const areaCost = msPerPixel * width * height;\n const maxColumns = Math.max(1, Math.floor(width / minimumTileSize));\n const maxRows = Math.max(1, Math.floor(height / minimumTileSize));\n // If the fixed cost alone exceeds the ceiling, no subdivision can meet it.\n // Avoid multiplying an unavoidable stall by an ever-growing tile count.\n const required = fixedMs < maximumUnattainableTileMs\n ? Math.ceil(areaCost / (maximumUnattainableTileMs - fixedMs)) : 1;\n const minimumTiles = clamp(required, 1, maxColumns * maxRows);\n let fewest: {columns: number; rows: number; count: number} | undefined;\n let withinBoth: typeof fewest;\n const better = (candidate: NonNullable<typeof fewest>, best: typeof fewest) =>\n !best || candidate.count < best.count ||\n (candidate.count === best.count &&\n Math.abs(candidate.columns - candidate.rows) < Math.abs(best.columns - best.rows));\n for (let columns = 1; columns <= Math.min(maxColumns, minimumTiles); columns++) {\n const rows = Math.ceil(minimumTiles / columns);\n if (rows > maxRows) continue;\n const count = columns * rows, candidate = {columns, rows, count};\n if (better(candidate, fewest)) fewest = candidate;\n const tileMs = fixedMs + areaCost / count;\n const fixedShare = count * fixedMs / (count * fixedMs + areaCost);\n if (tileMs <= maximumUnattainableTileMs && fixedShare <= maximumFixedCostShare &&\n better(candidate, withinBoth)) withinBoth = candidate;\n }\n // Fewer tiles also minimize fixed cost. If even the fewest grid that meets\n // the ceiling exceeds the share limit, no grid can satisfy both constraints.\n let {columns, rows, count: numTiles} = (withinBoth ?? fewest)!;\n if (numTiles > maxNumTiles) {\n // Preserve the existing rectangular rounding of the public maxTiles cap.\n numTiles = maxNumTiles;\n columns = Math.ceil(Math.sqrt(numTiles));\n rows = Math.ceil(numTiles / columns);\n numTiles = columns * rows;\n }\n return {columns, rows, numTiles, tileWidth: Math.ceil(width / columns),\n tileHeight: Math.ceil(height / rows), samplesPerFrame};\n}\n\nexport function makeTileRenderWithPredict(minimumTileSize = 1) {\n let numTiles = 1;\n let currentTile = -1;\n let lastTraceTime = -1;\n // Several offline samples can be submitted before a timing callback runs.\n // Consume each measurement once so stale feedback cannot repeatedly shrink tiles.\n let timingRevision = 0;\n let appliedTimingRevision = -1;\n let lastFrameTotalTraceTime = 0;\n let lastFrameTraceTimeValidCount = 0;\n let tilePixels = [64, 64];\n let width = 0;\n let height = 0;\n let expectFrameTime = 0;\n\n // Calcualate\n let tileWidth = 0;\n let tileHeight = 0;\n let samplesPerFrame = 1;\n let columns = 0;\n let rows = 0;\n\n let maxNumTiles = Infinity;\n // Validate a refinement against the grid that motivated it. A smaller\n // dispatch can retain most of the longest path / fixed kernel cost.\n let refinement: { grid: ReturnType<typeof getPredictResult>; time: number } | undefined;\n let minimumPredictionSize = [0, 0];\n let lastMeasurement: {area: number; time: number} | undefined;\n let unattainableGrid: ReturnType<typeof getPredictResult> | undefined;\n\n function clearPredictionEvidence() {\n refinement = undefined;\n minimumPredictionSize = [0, 0];\n lastMeasurement = undefined;\n unattainableGrid = undefined;\n }\n\n function nextTile() {\n currentTile++;\n const avgTileTime =\n lastFrameTraceTimeValidCount > 0\n ? lastFrameTotalTraceTime / lastFrameTraceTimeValidCount\n : lastTraceTime;\n\n if (currentTile % numTiles === 0) {\n currentTile = 0;\n }\n\n if (currentTile === 0 && timingRevision !== appliedTimingRevision) {\n appliedTimingRevision = timingRevision;\n const measured = lastFrameTraceTimeValidCount > 0;\n let rollback: typeof refinement;\n if (measured && refinement) {\n const areaRatio = refinement.grid.numTiles / numTiles;\n // Require at least half of the area-proportional improvement, unless\n // the new tile already fits the budget (with 10% timing tolerance).\n if (avgTileTime > expectFrameTime * 1.1 &&\n avgTileTime > refinement.time * (1 - (1 - areaRatio) * 0.5)) {\n rollback = refinement;\n minimumPredictionSize = [rollback.grid.tileWidth, rollback.grid.tileHeight];\n }\n refinement = undefined;\n }\n let result = rollback?.grid ?? getPredictResult(\n width,\n height,\n avgTileTime,\n expectFrameTime,\n lastTraceTime >= 0 ? tilePixels : getInitialProbeSize(width, height),\n maxNumTiles,\n minimumTileSize,\n minimumPredictionSize\n );\n if (measured && columns > 0 && rows > 0 && !unattainableGrid) {\n // Use nominal covered area, rather than ceil-rounded dispatch bounds:\n // the interleaved grid covers every pixel exactly once per sample.\n const point = {area: width * height / numTiles, time: avgTileTime};\n if (lastMeasurement && Math.abs(point.area - lastMeasurement.area) >=\n minimumAffineAreaChange * Math.max(point.area, lastMeasurement.area)) {\n const slope = (point.time - lastMeasurement.time) / (point.area - lastMeasurement.area);\n const fixed = point.time - slope * point.area;\n if (Number.isFinite(slope) && slope > 0 && Number.isFinite(fixed) &&\n fixed >= expectFrameTime * unattainableBudgetTolerance) {\n unattainableGrid = getUnattainableBudgetGrid(width, height, fixed, slope,\n minimumTileSize, maxNumTiles, result.samplesPerFrame);\n }\n }\n lastMeasurement = point;\n }\n if (unattainableGrid) {\n // Retain the decision until explicit invalidation. Re-fitting on its\n // own probe/target grids can alternate counts as path costs fluctuate.\n // Keep the original samplesPerFrame calculation independent of this policy.\n result = {...unattainableGrid, samplesPerFrame: result.samplesPerFrame};\n refinement = undefined;\n }\n if (!unattainableGrid && !rollback && measured && result.numTiles > numTiles) {\n refinement = {\n grid: {tileWidth, tileHeight, columns, rows, numTiles, samplesPerFrame},\n time: avgTileTime\n };\n }\n\n lastFrameTotalTraceTime = 0;\n lastFrameTraceTimeValidCount = 0;\n tileWidth = result.tileWidth;\n tileHeight = result.tileHeight;\n columns = result.columns;\n rows = result.rows;\n numTiles = result.numTiles;\n samplesPerFrame = result.samplesPerFrame;\n }\n\n const x = currentTile % columns;\n const y = Math.floor(currentTile / columns) % rows;\n const isLastTile = currentTile === numTiles - 1;\n\n tilePixels = [tileWidth, tileHeight];\n\n return {\n xIndex: x,\n yIndex: y,\n columns,\n rows,\n x: x * tileWidth,\n y: y * tileHeight,\n tileWidth,\n tileHeight,\n isFirstTile: currentTile === 0,\n isLastTile,\n samplesPerFrame\n };\n }\n\n return {\n nextTile,\n reset() {\n currentTile = -1;\n },\n setSize(w: number, h: number) {\n if (width !== w || height !== h) {\n timingRevision++;\n clearPredictionEvidence();\n lastTraceTime = -1;\n lastFrameTotalTraceTime = 0;\n lastFrameTraceTimeValidCount = 0;\n }\n width = w;\n height = h;\n },\n setMaxTiles(maxTiles: number) {\n if (maxNumTiles !== maxTiles) {\n timingRevision++;\n clearPredictionEvidence();\n }\n maxNumTiles = maxTiles;\n },\n setDesiredMsPerTile(time: number) {\n if (expectFrameTime !== time) {\n timingRevision++;\n clearPredictionEvidence();\n }\n expectFrameTime = time;\n },\n needsUpdatePredicting() {\n clearPredictionEvidence();\n timingRevision++;\n lastTraceTime = -1;\n lastFrameTotalTraceTime = 0;\n lastFrameTraceTimeValidCount = 0;\n },\n setLastTraceTime(time: number, timedColumns = columns, timedRows = rows) {\n // A timestamp can arrive after the next sample changed the grid. Only\n // compare measurements of the grid currently being evaluated.\n if (timedColumns !== columns || timedRows !== rows || !Number.isFinite(time) || time <= 0) return;\n timingRevision++;\n lastTraceTime = time;\n lastFrameTraceTimeValidCount++;\n lastFrameTotalTraceTime += time;\n },\n getLastTraceTime() {\n return lastTraceTime;\n },\n getDesiredMsPerTile() {\n return expectFrameTime;\n }\n };\n}\n\n\nfunction pixelsPerTileEstimate(gl: WebGL2RenderingContext) {\n const maxRenderbufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);\n\n if (maxRenderbufferSize <= 8192) {\n return 200000;\n } else {\n // 16K render targets are common on current desktop/integrated GPUs. The\n // old 400K seed unnecessarily split even 720p into several tiles and took\n // dozens of samples to grow to a full-frame tile.\n return 1000000;\n }\n}\n"]}
|
|
@@ -8,6 +8,9 @@ export declare class WGPUAov {
|
|
|
8
8
|
height: number;
|
|
9
9
|
samples: number;
|
|
10
10
|
centerReady: boolean;
|
|
11
|
+
compact: boolean;
|
|
12
|
+
private revision;
|
|
13
|
+
private readonly borrowed;
|
|
11
14
|
accumulation?: GPUBuffer;
|
|
12
15
|
center?: GPUBuffer;
|
|
13
16
|
previousCenter?: GPUBuffer;
|
|
@@ -23,13 +26,15 @@ export declare class WGPUAov {
|
|
|
23
26
|
private readonly snapshotLeases;
|
|
24
27
|
private readonly snapshotBuffers;
|
|
25
28
|
constructor(device: GPUDevice);
|
|
29
|
+
private resolveCode;
|
|
26
30
|
reset(): void;
|
|
27
|
-
resize(width: number, height: number): void;
|
|
31
|
+
resize(width: number, height: number, compact?: boolean): void;
|
|
28
32
|
setScene(words: Uint32Array, primitiveIds?: Uint32Array): void;
|
|
29
33
|
get bytes(): number;
|
|
30
34
|
snapshot(kinds: readonly AovKind[]): Partial<Record<AovKind, GPUBuffer>>;
|
|
31
35
|
releaseSnapshot(buffers: Partial<Record<AovKind, GPUBuffer>>): void;
|
|
32
36
|
visualize(kind: AovKind, options: AovDisplayOptions, linear: boolean): GPUTexture;
|
|
37
|
+
texture(kind: 'depth' | 'diffuseColor' | 'specularColor'): GPUTexture | undefined;
|
|
33
38
|
read(kind: AovKind): Promise<{
|
|
34
39
|
width: number;
|
|
35
40
|
height: number;
|
package/lib/renderer/WGPUAov.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { textureBytes } from './gpuMemory.js';
|
|
2
|
+
import { compactAovResolveCode } from './wgsl/aovCompact.wgsl.js';
|
|
3
|
+
import { aovBorrowDepthShader, aovBorrowReflectanceShader } from './wgsl/aovResolve.wgsl.js';
|
|
1
4
|
import { aovDisplayShader } from './wgsl/aovDisplay.wgsl.js';
|
|
2
5
|
import { packAovDisplay } from './aovDisplay.js';
|
|
3
6
|
import { aovSnapshotShader, aovSnapshotBinding, AOV_SNAPSHOT_BATCH_SIZE } from './wgsl/aovSnapshot.wgsl.js';
|
|
4
|
-
import { AovHistory, AOV_KINDS, AOV_LAYOUT, aovMask } from './aov.js';
|
|
7
|
+
import { AovHistory, AOV_KINDS, AOV_LAYOUT, aovLaneKind, aovMask } from './aov.js';
|
|
8
|
+
/** @internal Content revision of each borrowed AOV texture; changes whenever it is re-resolved. */
|
|
9
|
+
export const borrowedAovRevision = new WeakMap();
|
|
5
10
|
/** One target's optional AOV allocations and accepted frame history. */
|
|
6
11
|
export class WGPUAov {
|
|
7
12
|
device;
|
|
@@ -10,6 +15,9 @@ export class WGPUAov {
|
|
|
10
15
|
height = 0;
|
|
11
16
|
samples = 0;
|
|
12
17
|
centerReady = false;
|
|
18
|
+
compact = false;
|
|
19
|
+
revision = 0;
|
|
20
|
+
borrowed = new Map();
|
|
13
21
|
accumulation;
|
|
14
22
|
center;
|
|
15
23
|
previousCenter;
|
|
@@ -27,8 +35,12 @@ export class WGPUAov {
|
|
|
27
35
|
constructor(device) {
|
|
28
36
|
this.device = device;
|
|
29
37
|
}
|
|
38
|
+
resolveCode(code) {
|
|
39
|
+
return this.compact ? compactAovResolveCode(code) : code;
|
|
40
|
+
}
|
|
30
41
|
reset() {
|
|
31
|
-
|
|
42
|
+
this.revision++;
|
|
43
|
+
if (!this.compact && this.centerReady && this.center && this.previousCenter) {
|
|
32
44
|
const encoder = this.device.createCommandEncoder();
|
|
33
45
|
encoder.copyBufferToBuffer(this.center, 0, this.previousCenter, 0, this.center.size);
|
|
34
46
|
this.device.queue.submit([encoder.finish()]);
|
|
@@ -37,20 +49,23 @@ export class WGPUAov {
|
|
|
37
49
|
this.centerReady = false;
|
|
38
50
|
this.frame = undefined;
|
|
39
51
|
}
|
|
40
|
-
resize(width, height) {
|
|
41
|
-
if (width === this.width && height === this.height)
|
|
52
|
+
resize(width, height, compact = false) {
|
|
53
|
+
if (width === this.width && height === this.height && compact === this.compact)
|
|
42
54
|
return;
|
|
43
|
-
|
|
55
|
+
const stride = compact ? 24 : AOV_LAYOUT.accumulatedBytes;
|
|
56
|
+
if (width * height * stride > this.device.limits.maxStorageBufferBindingSize || width * height * stride > this.device.limits.maxBufferSize)
|
|
44
57
|
throw new RangeError('AOV accumulation exceeds device buffer limits');
|
|
45
58
|
this.dispose();
|
|
46
59
|
this.history.clear();
|
|
47
60
|
this.reset();
|
|
48
61
|
this.width = width;
|
|
49
62
|
this.height = height;
|
|
63
|
+
this.compact = compact;
|
|
64
|
+
this.displayPipeline = undefined;
|
|
50
65
|
const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC;
|
|
51
|
-
this.accumulation = this.device.createBuffer({ label: 'AOV accumulation', size: width * height *
|
|
52
|
-
this.previousCenter = this.device.createBuffer({ label: 'Previous AOV center', size: width * height * AOV_LAYOUT.centerBytes, usage });
|
|
53
|
-
this.center = this.device.createBuffer({ label: 'AOV center', size: width * height * AOV_LAYOUT.centerBytes, usage });
|
|
66
|
+
this.accumulation = this.device.createBuffer({ label: 'AOV accumulation', size: width * height * stride, usage });
|
|
67
|
+
this.previousCenter = this.device.createBuffer({ label: 'Previous AOV center', size: compact ? 32 : width * height * AOV_LAYOUT.centerBytes, usage });
|
|
68
|
+
this.center = this.device.createBuffer({ label: 'AOV center', size: compact ? 32 : width * height * AOV_LAYOUT.centerBytes, usage });
|
|
54
69
|
}
|
|
55
70
|
setScene(words, primitiveIds) {
|
|
56
71
|
const bytes = words.byteLength + (primitiveIds?.byteLength ?? 0);
|
|
@@ -72,7 +87,7 @@ export class WGPUAov {
|
|
|
72
87
|
this.scenePrimitives = primitiveIds;
|
|
73
88
|
this.scenePrimitiveOffset = words.byteLength;
|
|
74
89
|
}
|
|
75
|
-
get bytes() { return (this.accumulation?.size ?? 0) + (this.center?.size ?? 0) + (this.previousCenter?.size ?? 0) + (this.scene?.size ?? 0) + (this.displayTexture ? this.width * this.height * 8 : 0) + [...this.snapshotBuffers].reduce((bytes, buffer) => bytes + buffer.size, 0); }
|
|
90
|
+
get bytes() { return (this.accumulation?.size ?? 0) + (this.center?.size ?? 0) + (this.previousCenter?.size ?? 0) + (this.scene?.size ?? 0) + (this.displayTexture ? this.width * this.height * 8 : 0) + [...this.borrowed.values()].reduce((bytes, entry) => bytes + textureBytes(entry.image), 0) + [...this.snapshotBuffers].reduce((bytes, buffer) => bytes + buffer.size, 0); }
|
|
76
91
|
snapshot(kinds) {
|
|
77
92
|
if (!this.accumulation || !this.center || !this.samples)
|
|
78
93
|
throw new Error('AOV output is not ready');
|
|
@@ -81,11 +96,11 @@ export class WGPUAov {
|
|
|
81
96
|
const unique = AOV_KINDS.filter(kind => kinds.includes(kind));
|
|
82
97
|
const batches = [];
|
|
83
98
|
for (let offset = 0; offset < unique.length; offset += AOV_SNAPSHOT_BATCH_SIZE) {
|
|
84
|
-
const batch = unique.slice(offset, offset + AOV_SNAPSHOT_BATCH_SIZE), key = batch.join(',')
|
|
99
|
+
const batch = unique.slice(offset, offset + AOV_SNAPSHOT_BATCH_SIZE), key = `${this.compact}:${batch.join(',')}`;
|
|
85
100
|
let pipeline = this.resolvePipelines.get(key);
|
|
86
101
|
if (!pipeline) {
|
|
87
102
|
pipeline = device.createComputePipeline({ label: 'AOV snapshot', layout: 'auto',
|
|
88
|
-
compute: { module: device.createShaderModule({ code: aovSnapshotShader(batch) }), entryPoint: 'main' } });
|
|
103
|
+
compute: { module: device.createShaderModule({ code: this.resolveCode(aovSnapshotShader(batch)) }), entryPoint: 'main', ...(this.compact ? { constants: { AOV_COMPACT: 1 } } : {}) } });
|
|
89
104
|
this.resolvePipelines.set(key, pipeline);
|
|
90
105
|
}
|
|
91
106
|
batches.push({ kinds: batch, pipeline });
|
|
@@ -160,7 +175,7 @@ export class WGPUAov {
|
|
|
160
175
|
if (!this.accumulation || !this.center || !this.samples)
|
|
161
176
|
throw new Error('AOV output is not ready');
|
|
162
177
|
const device = this.device;
|
|
163
|
-
this.displayPipeline ??= device.createComputePipeline({ label: 'AOV visualization', layout: 'auto', compute: { module: device.createShaderModule({ code: aovDisplayShader('rgba16float') }), entryPoint: 'main' } });
|
|
178
|
+
this.displayPipeline ??= device.createComputePipeline({ label: 'AOV visualization', layout: 'auto', compute: { module: device.createShaderModule({ code: this.resolveCode(aovDisplayShader('rgba16float')) }), entryPoint: 'main', ...(this.compact ? { constants: { AOV_COMPACT: 1 } } : {}) } });
|
|
164
179
|
if (!this.displayTexture || this.displayTexture.width !== this.width || this.displayTexture.height !== this.height) {
|
|
165
180
|
this.displayTexture?.destroy();
|
|
166
181
|
this.displayTexture = device.createTexture({ label: 'AOV display image', size: [this.width, this.height], format: 'rgba16float', usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC });
|
|
@@ -179,7 +194,84 @@ export class WGPUAov {
|
|
|
179
194
|
parameters.destroy();
|
|
180
195
|
return this.displayTexture;
|
|
181
196
|
}
|
|
197
|
+
texture(kind) {
|
|
198
|
+
if (!this.samples || !this.accumulation || !this.center)
|
|
199
|
+
return;
|
|
200
|
+
const device = this.device;
|
|
201
|
+
let entry = this.borrowed.get(kind);
|
|
202
|
+
if (!entry) {
|
|
203
|
+
// Reflectance in [0,1] is stored sRGB-encoded in 8 bits (decoded on load), halving its memory
|
|
204
|
+
// with fine steps for dark albedo; depth keeps float32 precision.
|
|
205
|
+
const depth = kind === 'depth', format = depth ? 'rg32float' : 'rgba8unorm-srgb';
|
|
206
|
+
const constants = this.compact ? { constants: { AOV_COMPACT: 1 } } : {};
|
|
207
|
+
let pipeline;
|
|
208
|
+
if (depth) {
|
|
209
|
+
pipeline = device.createComputePipeline({ label: `Borrow ${kind}`, layout: 'auto', compute: {
|
|
210
|
+
module: device.createShaderModule({ code: this.resolveCode(aovBorrowDepthShader) }), entryPoint: 'main', ...constants
|
|
211
|
+
} });
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const module = device.createShaderModule({ code: this.resolveCode(aovBorrowReflectanceShader) });
|
|
215
|
+
pipeline = device.createRenderPipeline({ label: `Borrow ${kind}`, layout: 'auto', vertex: { module, entryPoint: 'vertex', ...constants },
|
|
216
|
+
fragment: { module, entryPoint: 'fragment', targets: [{ format }], ...constants } });
|
|
217
|
+
}
|
|
218
|
+
const image = device.createTexture({ label: `Borrow ${kind}`, size: [this.width, this.height], format,
|
|
219
|
+
usage: (depth ? GPUTextureUsage.STORAGE_BINDING : GPUTextureUsage.RENDER_ATTACHMENT) | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC });
|
|
220
|
+
entry = { image, pipeline, samples: -1, revision: -1 };
|
|
221
|
+
this.borrowed.set(kind, entry);
|
|
222
|
+
}
|
|
223
|
+
if (entry.samples !== this.samples || entry.revision !== this.revision) {
|
|
224
|
+
const parameters = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM, mappedAtCreation: true });
|
|
225
|
+
new Uint32Array(parameters.getMappedRange()).set([this.width, this.height, this.samples, aovLaneKind(kind)]);
|
|
226
|
+
parameters.unmap();
|
|
227
|
+
const encoder = device.createCommandEncoder();
|
|
228
|
+
const entries = [{ binding: 0, resource: { buffer: this.accumulation } }, { binding: 1, resource: { buffer: this.center } }, { binding: 3, resource: { buffer: parameters } }];
|
|
229
|
+
if (kind === 'depth') {
|
|
230
|
+
const pipeline = entry.pipeline;
|
|
231
|
+
const group = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [...entries, { binding: 2, resource: entry.image.createView() }] });
|
|
232
|
+
const pass = encoder.beginComputePass({ label: `Resolve ${kind}` });
|
|
233
|
+
pass.setPipeline(pipeline);
|
|
234
|
+
pass.setBindGroup(0, group);
|
|
235
|
+
pass.dispatchWorkgroups(Math.ceil(this.width / 8), Math.ceil(this.height / 8));
|
|
236
|
+
pass.end();
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const pipeline = entry.pipeline;
|
|
240
|
+
const group = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries });
|
|
241
|
+
const pass = encoder.beginRenderPass({ label: `Resolve ${kind}`, colorAttachments: [{ view: entry.image.createView(), loadOp: 'clear', storeOp: 'store' }] });
|
|
242
|
+
pass.setPipeline(pipeline);
|
|
243
|
+
pass.setBindGroup(0, group);
|
|
244
|
+
pass.draw(3);
|
|
245
|
+
pass.end();
|
|
246
|
+
}
|
|
247
|
+
device.queue.submit([encoder.finish()]);
|
|
248
|
+
parameters.destroy();
|
|
249
|
+
entry.samples = this.samples;
|
|
250
|
+
entry.revision = this.revision;
|
|
251
|
+
borrowedAovRevision.set(entry.image, (borrowedAovRevision.get(entry.image) ?? 0) + 1);
|
|
252
|
+
}
|
|
253
|
+
return entry.image;
|
|
254
|
+
}
|
|
182
255
|
async read(kind) {
|
|
256
|
+
if (this.compact) {
|
|
257
|
+
const buffers = this.snapshot([kind]), source = buffers[kind];
|
|
258
|
+
const staging = this.device.createBuffer({ size: source.size, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
|
259
|
+
try {
|
|
260
|
+
const encoder = this.device.createCommandEncoder();
|
|
261
|
+
encoder.copyBufferToBuffer(source, 0, staging, 0, source.size);
|
|
262
|
+
this.device.queue.submit([encoder.finish()]);
|
|
263
|
+
await staging.mapAsync(GPUMapMode.READ);
|
|
264
|
+
return { width: this.width, height: this.height, data: new Float32Array(staging.getMappedRange().slice(0)) };
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
if (staging.mapState === 'mapped')
|
|
268
|
+
staging.unmap();
|
|
269
|
+
staging.destroy();
|
|
270
|
+
this.snapshotLeases.delete(source);
|
|
271
|
+
this.snapshotBuffers.delete(source);
|
|
272
|
+
source.destroy();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
183
275
|
const center = kind === 'id' || kind === 'motion';
|
|
184
276
|
const source = center ? this.center : this.accumulation;
|
|
185
277
|
const staging = this.device.createBuffer({ size: source.size, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
|
@@ -208,7 +300,7 @@ export class WGPUAov {
|
|
|
208
300
|
else if (kind === 'roughness')
|
|
209
301
|
data.set([f[o + 1] * inv, f[o + 2] * inv, f[o + 3] * inv, coverage], target);
|
|
210
302
|
else {
|
|
211
|
-
const offset = kind ===
|
|
303
|
+
const offset = aovLaneKind(kind) === 3 ? o + 8 : o + 12, valid = f[offset + 3];
|
|
212
304
|
const scale = valid > 0 ? 1 / valid : 0;
|
|
213
305
|
data.set([f[offset] * scale, f[offset + 1] * scale, f[offset + 2] * scale, valid / Math.max(1, this.samples)], target);
|
|
214
306
|
}
|
|
@@ -223,6 +315,8 @@ export class WGPUAov {
|
|
|
223
315
|
}
|
|
224
316
|
}
|
|
225
317
|
dispose() {
|
|
318
|
+
const borrowed = [...this.borrowed.values()];
|
|
319
|
+
this.borrowed.clear();
|
|
226
320
|
const texture = this.displayTexture;
|
|
227
321
|
this.displayTexture = undefined;
|
|
228
322
|
this.scenePrimitives = undefined;
|
|
@@ -233,7 +327,7 @@ export class WGPUAov {
|
|
|
233
327
|
pooled.forEach(buffer => this.snapshotBuffers.delete(buffer));
|
|
234
328
|
const buffers = [this.accumulation, this.center, this.scene, this.previousCenter, ...pooled];
|
|
235
329
|
this.accumulation = this.center = this.scene = this.previousCenter = undefined;
|
|
236
|
-
void this.device.queue.onSubmittedWorkDone().catch(() => undefined).then(() => { texture?.destroy(); buffers.forEach(buffer => buffer?.destroy()); });
|
|
330
|
+
void this.device.queue.onSubmittedWorkDone().catch(() => undefined).then(() => { borrowed.forEach(entry => entry.image.destroy()); texture?.destroy(); buffers.forEach(buffer => buffer?.destroy()); });
|
|
237
331
|
}
|
|
238
332
|
}
|
|
239
333
|
//# sourceMappingURL=WGPUAov.js.map
|