@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.
Files changed (304) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/LICENSES/FidelityFX-FSR.txt +19 -0
  3. package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
  4. package/THIRD_PARTY_NOTICES.md +18 -4
  5. package/dist/assets/bvh.worker.js +1 -1
  6. package/dist/mutracer.umd.js +6260 -5580
  7. package/docs/api.md +45 -18
  8. package/docs/materials.md +4 -2
  9. package/docs/performance.md +7 -0
  10. package/docs/post-processing.md +62 -9
  11. package/docs/progressive-rendering.md +3 -0
  12. package/docs/sampling.md +4 -0
  13. package/docs/technical/acceleration.md +52 -0
  14. package/docs/technical/aovs.md +79 -0
  15. package/docs/technical/caustics.md +109 -0
  16. package/docs/technical/continuous-spectrum.md +29 -0
  17. package/docs/technical/display-upscale.md +82 -0
  18. package/docs/technical/openpbr.md +99 -0
  19. package/docs/technical/pbrt-import.md +168 -0
  20. package/docs/technical/tiling.md +74 -0
  21. package/docs/technical/webgl.md +53 -0
  22. package/docs/viewer.md +11 -1
  23. package/lib/Display.d.ts +19 -5
  24. package/lib/Display.js +10 -7
  25. package/lib/Display.js.map +1 -1
  26. package/lib/Engine.d.ts +2 -1
  27. package/lib/Engine.js +30 -2
  28. package/lib/Engine.js.map +1 -1
  29. package/lib/Render.d.ts +20 -11
  30. package/lib/Render.js +32 -7
  31. package/lib/Render.js.map +1 -1
  32. package/lib/main.d.ts +3 -1
  33. package/lib/main.js +1 -0
  34. package/lib/main.js.map +1 -1
  35. package/lib/oidn.d.ts +1 -1
  36. package/lib/renderer/AovPrimitiveCache.js +39 -8
  37. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  38. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  39. package/lib/renderer/DisplayPostProcess.js +51 -18
  40. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  41. package/lib/renderer/MaterialData.js +7 -2
  42. package/lib/renderer/MaterialData.js.map +1 -1
  43. package/lib/renderer/RenderingPipeline.d.ts +7 -1
  44. package/lib/renderer/RenderingPipeline.js.map +1 -1
  45. package/lib/renderer/TileRender.d.ts +2 -2
  46. package/lib/renderer/TileRender.js +128 -7
  47. package/lib/renderer/TileRender.js.map +1 -1
  48. package/lib/renderer/WGPUAov.d.ts +6 -1
  49. package/lib/renderer/WGPUAov.js +108 -14
  50. package/lib/renderer/WGPUAov.js.map +1 -1
  51. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  52. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  53. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  54. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  55. package/lib/renderer/WGPURayTraceOutput.js +12 -12
  56. package/lib/renderer/WGPURayTracePass.d.ts +17 -4
  57. package/lib/renderer/WGPURayTracePass.js +194 -71
  58. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  59. package/lib/renderer/WGPURenderingPipeline.js +46 -40
  60. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  61. package/lib/renderer/WebGLAov.js +3 -3
  62. package/lib/renderer/WebGLAov.js.map +1 -1
  63. package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
  64. package/lib/renderer/WebGLFullQuadPass.js +8 -2
  65. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  66. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  67. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  68. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  69. package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
  70. package/lib/renderer/WebGLRayTracePass.js +115 -111
  71. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  72. package/lib/renderer/WebGLRenderingPipeline.js +10 -3
  73. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  74. package/lib/renderer/WebGLSkyBaker.js +57 -57
  75. package/lib/renderer/aov.d.ts +13 -1
  76. package/lib/renderer/aov.js +21 -2
  77. package/lib/renderer/aov.js.map +1 -1
  78. package/lib/renderer/aovDisplay.js +2 -2
  79. package/lib/renderer/aovDisplay.js.map +1 -1
  80. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  81. package/lib/renderer/binnedSahBvh.js +24 -17
  82. package/lib/renderer/binnedSahBvh.js.map +1 -1
  83. package/lib/renderer/bvhAccel.d.ts +3 -1
  84. package/lib/renderer/bvhAccel.js +36 -6
  85. package/lib/renderer/bvhAccel.js.map +1 -1
  86. package/lib/renderer/cameraUniform.d.ts +1 -1
  87. package/lib/renderer/cameraUniform.js +6 -2
  88. package/lib/renderer/cameraUniform.js.map +1 -1
  89. package/lib/renderer/compactVertices.d.ts +14 -0
  90. package/lib/renderer/compactVertices.js +124 -0
  91. package/lib/renderer/compactVertices.js.map +1 -0
  92. package/lib/renderer/easu.d.ts +11 -0
  93. package/lib/renderer/easu.js +156 -0
  94. package/lib/renderer/easu.js.map +1 -0
  95. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  96. package/lib/renderer/geometryUpdateState.js.map +1 -1
  97. package/lib/renderer/geometryUpdates.js +3 -2
  98. package/lib/renderer/geometryUpdates.js.map +1 -1
  99. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  100. package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
  101. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  102. package/lib/renderer/glsl/aovTrace.frag.js +44 -36
  103. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  104. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  105. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  106. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  107. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  108. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  109. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  110. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
  111. package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
  112. package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
  113. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  114. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
  115. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
  116. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  117. package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
  118. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
  119. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  120. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
  121. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  122. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
  123. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
  124. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  125. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
  126. package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
  127. package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
  128. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  129. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  130. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  131. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  132. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  133. package/lib/renderer/glsl/depthQuery.frag.js +12 -6
  134. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  135. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  136. package/lib/renderer/glsl/rayTrace.frag.js +233 -170
  137. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  138. package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
  139. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  140. package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
  141. package/lib/renderer/gpuMemory.js +1 -1
  142. package/lib/renderer/gpuMemory.js.map +1 -1
  143. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  144. package/lib/renderer/openPbrEnergyLut.js +18 -1
  145. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  146. package/lib/renderer/pathLimits.d.ts +5 -4
  147. package/lib/renderer/pathLimits.js +6 -5
  148. package/lib/renderer/pathLimits.js.map +1 -1
  149. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  150. package/lib/renderer/prepareMeshBVH.js +5 -3
  151. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  152. package/lib/renderer/progressiveScheduler.js +1 -1
  153. package/lib/renderer/progressiveScheduler.js.map +1 -1
  154. package/lib/renderer/renderPipeline.d.ts +4 -0
  155. package/lib/renderer/renderPipeline.js +1 -0
  156. package/lib/renderer/renderPipeline.js.map +1 -1
  157. package/lib/renderer/researchTransport.d.ts +1 -1
  158. package/lib/renderer/researchTransport.js.map +1 -1
  159. package/lib/renderer/sceneAssembly.d.ts +1 -1
  160. package/lib/renderer/sceneAssembly.js +4 -3
  161. package/lib/renderer/sceneAssembly.js.map +1 -1
  162. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  163. package/lib/renderer/sceneChangeDetector.js +3 -1
  164. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  165. package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
  166. package/lib/renderer/sceneLayoutSchema.js +2 -2
  167. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  168. package/lib/renderer/scenePreparation.d.ts +3 -2
  169. package/lib/renderer/scenePreparation.js +18 -2
  170. package/lib/renderer/scenePreparation.js.map +1 -1
  171. package/lib/renderer/scratchSampling.js +6 -6
  172. package/lib/renderer/spectrum.js +2 -2
  173. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  174. package/lib/renderer/staticBvhPreparation.js +8 -2
  175. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  176. package/lib/renderer/surfaceHit.d.ts +10 -0
  177. package/lib/renderer/surfaceHit.js +22 -0
  178. package/lib/renderer/surfaceHit.js.map +1 -0
  179. package/lib/renderer/tangentRequirements.d.ts +2 -0
  180. package/lib/renderer/tangentRequirements.js +7 -0
  181. package/lib/renderer/tangentRequirements.js.map +1 -0
  182. package/lib/renderer/textureAtlas.d.ts +4 -0
  183. package/lib/renderer/textureAtlas.js +48 -2
  184. package/lib/renderer/textureAtlas.js.map +1 -1
  185. package/lib/renderer/webgl.js +4 -0
  186. package/lib/renderer/webgl.js.map +1 -1
  187. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  188. package/lib/renderer/webglVertexAttributes.js +44 -0
  189. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  190. package/lib/renderer/wgpuBindlessSource.js +2 -2
  191. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  192. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  193. package/lib/renderer/wgpuSceneUniform.js +2 -2
  194. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  195. package/lib/renderer/wgpuShaderSource.d.ts +15 -0
  196. package/lib/renderer/wgpuShaderSource.js +1 -1
  197. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  198. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  199. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  200. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  201. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  202. package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
  203. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  204. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  205. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  206. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  207. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  208. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  209. package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
  210. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  211. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  212. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  213. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  214. package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
  215. package/lib/renderer/wgsl/causticLight.compute.js +121 -119
  216. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
  217. package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
  218. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
  219. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
  220. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
  221. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  222. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
  223. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  224. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
  225. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  226. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  227. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
  228. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  229. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
  230. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
  231. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  232. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
  233. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
  234. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  235. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
  236. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
  237. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  238. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
  239. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  240. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
  241. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  242. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
  243. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
  244. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
  245. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  246. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  247. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  248. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  249. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  250. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  251. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  252. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  253. package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
  254. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  256. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  257. package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
  258. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  259. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  260. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  261. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  262. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  263. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  264. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  265. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  266. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  267. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  268. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  269. package/lib/renderer/wgsl/photonMap.compute.js +89 -89
  270. package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
  271. package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
  272. package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
  273. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  274. package/lib/scene/Scene.js.map +1 -1
  275. package/lib/settings/render.d.ts +11 -0
  276. package/lib/settings/render.js +1 -0
  277. package/lib/settings/render.js.map +1 -1
  278. package/lib/util/fromTHREE.js +21 -14
  279. package/lib/util/fromTHREE.js.map +1 -1
  280. package/lib/version.d.ts +1 -1
  281. package/lib/version.js +1 -1
  282. package/lib/version.js.map +1 -1
  283. package/package.json +5 -2
  284. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  285. package/lib/oidnBundledWeights.cdn.js +0 -5
  286. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  287. package/lib/renderer/bvh.worker.d.ts +0 -1
  288. package/lib/renderer/bvh.worker.js +0 -16
  289. package/lib/renderer/bvh.worker.js.map +0 -1
  290. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  291. package/lib/renderer/bvhWorkerBuild.js +0 -17
  292. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  293. package/lib/renderer/lightCore.d.ts +0 -12
  294. package/lib/renderer/lightCore.js +0 -13
  295. package/lib/renderer/lightCore.js.map +0 -1
  296. package/lib/renderer/materialCore.d.ts +0 -8
  297. package/lib/renderer/materialCore.js +0 -9
  298. package/lib/renderer/materialCore.js.map +0 -1
  299. package/lib/scene/PixelTexture.d.ts +0 -6
  300. package/lib/scene/PixelTexture.js +0 -10
  301. package/lib/scene/PixelTexture.js.map +0 -1
  302. package/lib/util/studioLighting.d.ts +0 -18
  303. package/lib/util/studioLighting.js +0 -36
  304. 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
- export function makeTileRenderWithPredict() {
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 result = getPredictResult(width, height, avgTileTime, expectFrameTime, lastTraceTime >= 0 ? tilePixels : getInitialProbeSize(width, height), maxNumTiles);
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;
@@ -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
- if (this.centerReady && this.center && this.previousCenter) {
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
- if (width * height * AOV_LAYOUT.accumulatedBytes > this.device.limits.maxStorageBufferBindingSize || width * height * AOV_LAYOUT.accumulatedBytes > this.device.limits.maxBufferSize)
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 * AOV_LAYOUT.accumulatedBytes, usage });
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 === 'diffuseAlbedo' ? o + 8 : o + 12, valid = f[offset + 3];
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