@meimu/mutracer 0.2.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 (372) 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/README.md +3 -0
  5. package/THIRD_PARTY_NOTICES.md +18 -4
  6. package/dist/assets/bvh.worker.js +1 -1
  7. package/dist/mutracer.umd.js +9328 -7614
  8. package/docs/api.md +83 -35
  9. package/docs/caustics.md +80 -0
  10. package/docs/materials.md +9 -6
  11. package/docs/performance.md +7 -0
  12. package/docs/post-processing.md +62 -9
  13. package/docs/progressive-rendering.md +53 -9
  14. package/docs/sampling.md +4 -0
  15. package/docs/technical/acceleration.md +52 -0
  16. package/docs/technical/aovs.md +79 -0
  17. package/docs/technical/caustics.md +109 -0
  18. package/docs/technical/continuous-spectrum.md +122 -0
  19. package/docs/technical/display-upscale.md +82 -0
  20. package/docs/technical/openpbr.md +99 -0
  21. package/docs/technical/pbrt-import.md +168 -0
  22. package/docs/technical/tiling.md +74 -0
  23. package/docs/technical/webgl.md +53 -0
  24. package/docs/updates.md +3 -1
  25. package/docs/viewer-checklist.md +65 -0
  26. package/docs/viewer.md +18 -6
  27. package/lib/Display.d.ts +19 -5
  28. package/lib/Display.js +10 -7
  29. package/lib/Display.js.map +1 -1
  30. package/lib/Engine.d.ts +4 -1
  31. package/lib/Engine.js +77 -14
  32. package/lib/Engine.js.map +1 -1
  33. package/lib/Render.d.ts +81 -24
  34. package/lib/Render.js +201 -26
  35. package/lib/Render.js.map +1 -1
  36. package/lib/RenderLoop.d.ts +7 -3
  37. package/lib/RenderLoop.js +68 -44
  38. package/lib/RenderLoop.js.map +1 -1
  39. package/lib/main.d.ts +4 -2
  40. package/lib/main.js +1 -0
  41. package/lib/main.js.map +1 -1
  42. package/lib/oidn.d.ts +1 -1
  43. package/lib/renderer/AovPrimitiveCache.js +39 -8
  44. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  45. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  46. package/lib/renderer/DisplayPostProcess.js +51 -18
  47. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  48. package/lib/renderer/GPUTraceQueue.d.ts +2 -1
  49. package/lib/renderer/GPUTraceQueue.js +3 -1
  50. package/lib/renderer/GPUTraceQueue.js.map +1 -1
  51. package/lib/renderer/MaterialData.js +7 -2
  52. package/lib/renderer/MaterialData.js.map +1 -1
  53. package/lib/renderer/RenderingPipeline.d.ts +11 -1
  54. package/lib/renderer/RenderingPipeline.js.map +1 -1
  55. package/lib/renderer/TileRender.d.ts +2 -2
  56. package/lib/renderer/TileRender.js +128 -7
  57. package/lib/renderer/TileRender.js.map +1 -1
  58. package/lib/renderer/WGPUAov.d.ts +6 -1
  59. package/lib/renderer/WGPUAov.js +108 -14
  60. package/lib/renderer/WGPUAov.js.map +1 -1
  61. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  62. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  63. package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
  64. package/lib/renderer/WGPUCausticLightPass.js +217 -0
  65. package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
  66. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  67. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  68. package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
  69. package/lib/renderer/WGPUPhotonMap.js +191 -0
  70. package/lib/renderer/WGPUPhotonMap.js.map +1 -0
  71. package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
  72. package/lib/renderer/WGPURayTraceOutput.js +122 -28
  73. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  74. package/lib/renderer/WGPURayTracePass.d.ts +65 -21
  75. package/lib/renderer/WGPURayTracePass.js +730 -409
  76. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  77. package/lib/renderer/WGPURenderingPipeline.js +110 -68
  78. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  79. package/lib/renderer/WebGLAov.js +3 -3
  80. package/lib/renderer/WebGLAov.js.map +1 -1
  81. package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
  82. package/lib/renderer/WebGLFullQuadPass.js +10 -0
  83. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  84. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  85. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  86. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  87. package/lib/renderer/WebGLRayTracePass.d.ts +20 -11
  88. package/lib/renderer/WebGLRayTracePass.js +157 -116
  89. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  90. package/lib/renderer/WebGLRenderingPipeline.js +17 -14
  91. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  92. package/lib/renderer/WebGLSkyBaker.js +60 -60
  93. package/lib/renderer/accumulationTarget.d.ts +2 -1
  94. package/lib/renderer/accumulationTarget.js.map +1 -1
  95. package/lib/renderer/aov.d.ts +13 -1
  96. package/lib/renderer/aov.js +21 -2
  97. package/lib/renderer/aov.js.map +1 -1
  98. package/lib/renderer/aovDisplay.js +2 -2
  99. package/lib/renderer/aovDisplay.js.map +1 -1
  100. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  101. package/lib/renderer/binnedSahBvh.js +24 -17
  102. package/lib/renderer/binnedSahBvh.js.map +1 -1
  103. package/lib/renderer/bvhAccel.d.ts +3 -1
  104. package/lib/renderer/bvhAccel.js +36 -6
  105. package/lib/renderer/bvhAccel.js.map +1 -1
  106. package/lib/renderer/cameraUniform.d.ts +1 -1
  107. package/lib/renderer/cameraUniform.js +6 -2
  108. package/lib/renderer/cameraUniform.js.map +1 -1
  109. package/lib/renderer/causticDiagnostics.d.ts +31 -0
  110. package/lib/renderer/causticDiagnostics.js +5 -0
  111. package/lib/renderer/causticDiagnostics.js.map +1 -0
  112. package/lib/renderer/causticEmission.d.ts +10 -0
  113. package/lib/renderer/causticEmission.js +40 -0
  114. package/lib/renderer/causticEmission.js.map +1 -0
  115. package/lib/renderer/causticUniform.d.ts +12 -0
  116. package/lib/renderer/causticUniform.js +22 -0
  117. package/lib/renderer/causticUniform.js.map +1 -0
  118. package/lib/renderer/caustics.d.ts +16 -0
  119. package/lib/renderer/caustics.js +93 -0
  120. package/lib/renderer/caustics.js.map +1 -0
  121. package/lib/renderer/compactVertices.d.ts +14 -0
  122. package/lib/renderer/compactVertices.js +124 -0
  123. package/lib/renderer/compactVertices.js.map +1 -0
  124. package/lib/renderer/easu.d.ts +11 -0
  125. package/lib/renderer/easu.js +156 -0
  126. package/lib/renderer/easu.js.map +1 -0
  127. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  128. package/lib/renderer/geometryUpdateState.js.map +1 -1
  129. package/lib/renderer/geometryUpdates.js +3 -2
  130. package/lib/renderer/geometryUpdates.js.map +1 -1
  131. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  132. package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
  133. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  134. package/lib/renderer/glsl/aovTrace.frag.js +45 -37
  135. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  136. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  137. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  138. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  139. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  140. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  141. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  142. package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
  143. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
  144. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
  145. package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
  146. package/lib/renderer/glsl/chunks/intersect.glsl.js +478 -465
  147. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  148. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
  149. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
  150. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  151. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
  152. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  153. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  154. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
  155. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +70 -63
  156. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  157. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +89 -75
  158. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  159. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
  160. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +160 -118
  161. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  162. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
  163. package/lib/renderer/glsl/chunks/sampling.glsl.js +88 -91
  164. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
  165. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  166. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  167. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  168. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  169. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  170. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  171. package/lib/renderer/glsl/depthQuery.frag.js +13 -7
  172. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  173. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  174. package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
  175. package/lib/renderer/glsl/rayTrace.frag.js +338 -252
  176. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  177. package/lib/renderer/glsl/shadowCatcher.frag.js +27 -26
  178. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  179. package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
  180. package/lib/renderer/gpuMemory.js +1 -1
  181. package/lib/renderer/gpuMemory.js.map +1 -1
  182. package/lib/renderer/lightSampling.d.ts +3 -2
  183. package/lib/renderer/lightSampling.js +14 -8
  184. package/lib/renderer/lightSampling.js.map +1 -1
  185. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  186. package/lib/renderer/openPbrEnergyLut.js +18 -1
  187. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  188. package/lib/renderer/pathLimits.d.ts +5 -4
  189. package/lib/renderer/pathLimits.js +6 -5
  190. package/lib/renderer/pathLimits.js.map +1 -1
  191. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  192. package/lib/renderer/prepareMeshBVH.js +5 -3
  193. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  194. package/lib/renderer/progressiveScheduler.js +5 -4
  195. package/lib/renderer/progressiveScheduler.js.map +1 -1
  196. package/lib/renderer/renderPipeline.d.ts +5 -0
  197. package/lib/renderer/renderPipeline.js +1 -0
  198. package/lib/renderer/renderPipeline.js.map +1 -1
  199. package/lib/renderer/researchTransport.d.ts +31 -0
  200. package/lib/renderer/researchTransport.js +5 -0
  201. package/lib/renderer/researchTransport.js.map +1 -0
  202. package/lib/renderer/sceneAssembly.d.ts +1 -1
  203. package/lib/renderer/sceneAssembly.js +4 -3
  204. package/lib/renderer/sceneAssembly.js.map +1 -1
  205. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  206. package/lib/renderer/sceneChangeDetector.js +3 -1
  207. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  208. package/lib/renderer/sceneLayoutSchema.d.ts +2 -0
  209. package/lib/renderer/sceneLayoutSchema.js +3 -3
  210. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  211. package/lib/renderer/scenePreparation.d.ts +3 -2
  212. package/lib/renderer/scenePreparation.js +18 -2
  213. package/lib/renderer/scenePreparation.js.map +1 -1
  214. package/lib/renderer/scratchSampling.js +6 -6
  215. package/lib/renderer/spectrum.d.ts +21 -0
  216. package/lib/renderer/spectrum.js +154 -0
  217. package/lib/renderer/spectrum.js.map +1 -0
  218. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  219. package/lib/renderer/staticBvhPreparation.js +8 -2
  220. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  221. package/lib/renderer/surfaceHit.d.ts +10 -0
  222. package/lib/renderer/surfaceHit.js +22 -0
  223. package/lib/renderer/surfaceHit.js.map +1 -0
  224. package/lib/renderer/tangentRequirements.d.ts +2 -0
  225. package/lib/renderer/tangentRequirements.js +7 -0
  226. package/lib/renderer/tangentRequirements.js.map +1 -0
  227. package/lib/renderer/textureAtlas.d.ts +4 -0
  228. package/lib/renderer/textureAtlas.js +48 -2
  229. package/lib/renderer/textureAtlas.js.map +1 -1
  230. package/lib/renderer/webgl.js +4 -0
  231. package/lib/renderer/webgl.js.map +1 -1
  232. package/lib/renderer/webglLightSampling.d.ts +39 -0
  233. package/lib/renderer/webglLightSampling.js +15 -0
  234. package/lib/renderer/webglLightSampling.js.map +1 -0
  235. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  236. package/lib/renderer/webglVertexAttributes.js +44 -0
  237. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  238. package/lib/renderer/wgpuBindlessSource.js +3 -3
  239. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  240. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  241. package/lib/renderer/wgpuSceneUniform.js +2 -2
  242. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  243. package/lib/renderer/wgpuShaderSource.d.ts +22 -0
  244. package/lib/renderer/wgpuShaderSource.js +3 -1
  245. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  246. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  247. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  248. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  249. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  250. package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
  251. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  252. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  253. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  254. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  256. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  257. package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
  258. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  259. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  260. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  261. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  262. package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
  263. package/lib/renderer/wgsl/causticLight.compute.js +539 -0
  264. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
  265. package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
  266. package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
  267. package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
  268. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
  269. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
  270. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
  271. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
  272. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
  273. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
  274. package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
  275. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
  276. package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
  277. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  278. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
  279. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  280. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +634 -620
  281. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  282. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  283. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +23 -21
  284. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  285. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +3 -1
  286. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +77 -74
  287. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  288. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +795 -795
  289. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  290. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  291. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +5 -1
  292. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +45 -29
  293. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  294. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  295. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
  296. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  297. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  298. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +228 -189
  299. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  300. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  301. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
  302. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  303. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
  304. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +88 -92
  305. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
  306. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  307. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  308. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  309. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  310. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  311. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  312. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  313. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  314. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  315. package/lib/renderer/wgsl/compose.frag.wgsl.js +100 -98
  316. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  317. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  318. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  319. package/lib/renderer/wgsl/depthQuery.wgsl.js +12 -3
  320. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  321. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  322. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  323. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  324. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  325. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  326. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  327. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  328. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  329. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  330. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  331. package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
  332. package/lib/renderer/wgsl/photonMap.compute.js +352 -0
  333. package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
  334. package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
  335. package/lib/renderer/wgsl/rayTrace.compute.d.ts +23 -1
  336. package/lib/renderer/wgsl/rayTrace.compute.js +531 -543
  337. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  338. package/lib/scene/MaterialState.d.ts +2 -2
  339. package/lib/scene/MaterialState.js +2 -2
  340. package/lib/scene/MaterialState.js.map +1 -1
  341. package/lib/scene/Scene.js.map +1 -1
  342. package/lib/settings/render.d.ts +22 -0
  343. package/lib/settings/render.js +2 -0
  344. package/lib/settings/render.js.map +1 -1
  345. package/lib/util/fromTHREE.js +23 -16
  346. package/lib/util/fromTHREE.js.map +1 -1
  347. package/lib/version.d.ts +1 -1
  348. package/lib/version.js +1 -1
  349. package/lib/version.js.map +1 -1
  350. package/llms.txt +2 -1
  351. package/package.json +7 -4
  352. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  353. package/lib/oidnBundledWeights.cdn.js +0 -5
  354. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  355. package/lib/renderer/bvh.worker.d.ts +0 -1
  356. package/lib/renderer/bvh.worker.js +0 -16
  357. package/lib/renderer/bvh.worker.js.map +0 -1
  358. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  359. package/lib/renderer/bvhWorkerBuild.js +0 -17
  360. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  361. package/lib/renderer/lightCore.d.ts +0 -12
  362. package/lib/renderer/lightCore.js +0 -13
  363. package/lib/renderer/lightCore.js.map +0 -1
  364. package/lib/renderer/materialCore.d.ts +0 -8
  365. package/lib/renderer/materialCore.js +0 -9
  366. package/lib/renderer/materialCore.js.map +0 -1
  367. package/lib/scene/PixelTexture.d.ts +0 -6
  368. package/lib/scene/PixelTexture.js +0 -10
  369. package/lib/scene/PixelTexture.js.map +0 -1
  370. package/lib/util/studioLighting.d.ts +0 -18
  371. package/lib/util/studioLighting.js +0 -36
  372. package/lib/util/studioLighting.js.map +0 -1
@@ -1,41 +1,74 @@
1
1
  import { textureBytes } from './gpuMemory.js';
2
- /** Target-owned ping-pong images. A failed chain never replaces the composed image. */
2
+ /** Target-owned images. A failed chain never replaces the composed image. */
3
3
  export class DisplayPostProcess {
4
4
  device;
5
- textures = [];
5
+ textures = new Map();
6
6
  image;
7
7
  constructor(device) {
8
8
  this.device = device;
9
9
  }
10
10
  get output() { return this.image; }
11
11
  reset() { this.image = undefined; }
12
- getMemoryBytes() { return this.textures.reduce((sum, texture) => sum + textureBytes(texture), 0); }
12
+ getMemoryBytes() { return [...this.textures.values()].flat().reduce((sum, texture) => sum + textureBytes(texture), 0); }
13
13
  retire(textures) {
14
14
  if (!textures.length)
15
15
  return;
16
16
  void this.device.queue.onSubmittedWorkDone().catch(() => { }).then(() => textures.forEach(texture => texture.destroy()));
17
17
  }
18
- dispose() { this.reset(); this.retire(this.textures); this.textures = []; }
19
- run(input, passes, render) {
18
+ dispose() { this.reset(); this.retire([...this.textures.values()].flat()); this.textures.clear(); }
19
+ run(input, passes, render, display = input, options = [], crop) {
20
20
  this.reset();
21
- if (!passes.length)
21
+ const cropped = crop && (crop.width !== input.width || crop.height !== input.height);
22
+ if (!passes.length && !cropped)
22
23
  return;
23
- if (this.textures.length === 0 || this.textures[0].width !== input.width || this.textures[0].height !== input.height || this.textures[0].format !== input.format) {
24
- this.retire(this.textures);
25
- this.textures = Array.from({ length: 2 }, (_, index) => this.device.createTexture({
26
- label: `Display post-process ${index}`, size: { width: input.width, height: input.height }, format: input.format,
24
+ const validate = (size) => {
25
+ const limit = this.device.limits.maxTextureDimension2D;
26
+ if (!size || !Number.isInteger(size.width) || !Number.isInteger(size.height) || size.width < 1 || size.height < 1 || size.width > limit || size.height > limit) {
27
+ throw new RangeError('Post-process output dimensions must be positive integers within maxTextureDimension2D.');
28
+ }
29
+ return { width: size.width, height: size.height };
30
+ };
31
+ // Evaluate the entire size chain before recording user commands.
32
+ let size = cropped ? validate(crop) : { width: input.width, height: input.height };
33
+ const sizes = passes.map((_, index) => size = validate(options[index]?.outputSize ? options[index].outputSize({ ...size }, { width: display.width, height: display.height }) : size));
34
+ const active = new Set();
35
+ const acquire = (size, previous) => {
36
+ const key = `${size.width}:${size.height}:${input.format}`;
37
+ active.add(key);
38
+ // Allocate the second image of a size only when consecutive passes need to ping-pong.
39
+ const pair = this.textures.get(key) ?? [];
40
+ this.textures.set(key, pair);
41
+ const index = pair[0] === previous ? 1 : 0;
42
+ pair[index] ??= this.device.createTexture({
43
+ label: `Display post-process ${index}`, size, format: input.format,
27
44
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST
28
- }));
29
- }
45
+ });
46
+ return pair[index];
47
+ };
48
+ const actual = render._output?.getMemory() ?? input;
30
49
  const encoder = this.device.createCommandEncoder();
31
50
  let previous = input;
32
- for (let index = 0; index < passes.length; index++) {
33
- const output = this.textures[index % 2];
34
- passes[index]({ device: this.device, encoder, input: previous, output, width: input.width, height: input.height, render });
35
- previous = output;
51
+ try {
52
+ if (cropped) {
53
+ const output = acquire(crop, previous);
54
+ encoder.copyTextureToTexture({ texture: input, origin: { x: Math.floor((input.width - crop.width) / 2), y: Math.floor((input.height - crop.height) / 2) } }, { texture: output }, crop);
55
+ previous = output;
56
+ }
57
+ for (let index = 0; index < passes.length; index++) {
58
+ const output = acquire(sizes[index], previous);
59
+ passes[index]({ device: this.device, encoder, input: previous, output, width: actual.width, height: actual.height, render });
60
+ previous = output;
61
+ }
62
+ this.device.queue.submit([encoder.finish()]);
63
+ this.image = previous;
64
+ }
65
+ finally {
66
+ for (const [key, pair] of this.textures)
67
+ if (!active.has(key)) {
68
+ this.textures.delete(key);
69
+ this.retire(pair);
70
+ }
36
71
  }
37
- this.device.queue.submit([encoder.finish()]);
38
- this.image = previous;
39
72
  }
40
73
  }
41
74
  //# sourceMappingURL=DisplayPostProcess.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DisplayPostProcess.js","sourceRoot":"","sources":["../../src/renderer/DisplayPostProcess.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PostProcess, PostProcessContext } from '../Display.js';\nimport {textureBytes} from './gpuMemory.js';\n\n/** Target-owned ping-pong images. A failed chain never replaces the composed image. */\nexport class DisplayPostProcess {\n private textures: GPUTexture[] = [];\n private image?: GPUTexture;\n constructor(private readonly device: GPUDevice) {}\n get output() { return this.image; }\n reset() { this.image = undefined; }\n getMemoryBytes() { return this.textures.reduce((sum, texture) => sum + textureBytes(texture), 0); }\n private retire(textures: GPUTexture[]) {\n if (!textures.length) return;\n void this.device.queue.onSubmittedWorkDone().catch(() => {}).then(() => textures.forEach(texture => texture.destroy()));\n }\n dispose() { this.reset(); this.retire(this.textures); this.textures = []; }\n run(input: GPUTexture, passes: readonly PostProcess[], render: PostProcessContext['render']) {\n this.reset();\n if (!passes.length) return;\n if (this.textures.length === 0 || this.textures[0].width !== input.width || this.textures[0].height !== input.height || this.textures[0].format !== input.format) {\n this.retire(this.textures);\n this.textures = Array.from({length: 2}, (_, index) => this.device.createTexture({\n label: `Display post-process ${index}`, size: {width: input.width, height: input.height}, format: input.format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST\n }));\n }\n const encoder = this.device.createCommandEncoder();\n let previous = input;\n for (let index = 0; index < passes.length; index++) {\n const output = this.textures[index % 2];\n passes[index]({device: this.device, encoder, input: previous, output, width: input.width, height: input.height, render});\n previous = output;\n }\n this.device.queue.submit([encoder.finish()]);\n this.image = previous;\n }\n}\n"]}
1
+ {"version":3,"file":"DisplayPostProcess.js","sourceRoot":"","sources":["../../src/renderer/DisplayPostProcess.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PostProcess, PostProcessContext, PostProcessOptions } from '../Display.js';\nimport {textureBytes} from './gpuMemory.js';\n\ntype Size = {width: number; height: number};\n/** Target-owned images. A failed chain never replaces the composed image. */\nexport class DisplayPostProcess {\n private textures = new Map<string, GPUTexture[]>();\n private image?: GPUTexture;\n constructor(private readonly device: GPUDevice) {}\n get output() { return this.image; }\n reset() { this.image = undefined; }\n getMemoryBytes() { return [...this.textures.values()].flat().reduce((sum, texture) => sum + textureBytes(texture), 0); }\n private retire(textures: GPUTexture[]) {\n if (!textures.length) return;\n void this.device.queue.onSubmittedWorkDone().catch(() => {}).then(() => textures.forEach(texture => texture.destroy()));\n }\n dispose() { this.reset(); this.retire([...this.textures.values()].flat()); this.textures.clear(); }\n run(input: GPUTexture, passes: readonly PostProcess[], render: PostProcessContext['render'], display: Size = input,\n options: readonly PostProcessOptions[] = [], crop?: Size) {\n this.reset();\n const cropped = crop && (crop.width !== input.width || crop.height !== input.height);\n if (!passes.length && !cropped) return;\n const validate = (size: Size) => {\n const limit = this.device.limits.maxTextureDimension2D;\n if (!size || !Number.isInteger(size.width) || !Number.isInteger(size.height) || size.width < 1 || size.height < 1 || size.width > limit || size.height > limit) {\n throw new RangeError('Post-process output dimensions must be positive integers within maxTextureDimension2D.');\n }\n return {width: size.width, height: size.height};\n };\n // Evaluate the entire size chain before recording user commands.\n let size: Size = cropped ? validate(crop) : {width: input.width, height: input.height};\n const sizes = passes.map((_, index) => size = validate(options[index]?.outputSize ? options[index].outputSize!({...size}, {width: display.width, height: display.height}) : size));\n const active = new Set<string>();\n const acquire = (size: Size, previous: GPUTexture) => {\n const key = `${size.width}:${size.height}:${input.format}`;\n active.add(key);\n // Allocate the second image of a size only when consecutive passes need to ping-pong.\n const pair = this.textures.get(key) ?? [];\n this.textures.set(key, pair);\n const index = pair[0] === previous ? 1 : 0;\n pair[index] ??= this.device.createTexture({\n label: `Display post-process ${index}`, size, format: input.format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST\n });\n return pair[index];\n };\n const actual = render._output?.getMemory() ?? input;\n const encoder = this.device.createCommandEncoder();\n let previous = input;\n try {\n if (cropped) {\n const output = acquire(crop, previous);\n encoder.copyTextureToTexture({texture: input, origin: {x: Math.floor((input.width-crop.width)/2), y: Math.floor((input.height-crop.height)/2)}}, {texture: output}, crop);\n previous = output;\n }\n for (let index = 0; index < passes.length; index++) {\n const output = acquire(sizes[index], previous);\n passes[index]({device: this.device, encoder, input: previous, output, width: actual.width, height: actual.height, render});\n previous = output;\n }\n this.device.queue.submit([encoder.finish()]);\n this.image = previous;\n } finally {\n for (const [key, pair] of this.textures) if (!active.has(key)) { this.textures.delete(key); this.retire(pair); }\n }\n }\n}\n"]}
@@ -4,7 +4,8 @@ export declare class GPUTraceQueue {
4
4
  private pendingMs;
5
5
  private pending;
6
6
  constructor(queue: GPUQueue);
7
+ get hasPending(): boolean;
7
8
  available(budgetMs: number): number;
8
- whenAvailable(): Promise<void>;
9
+ whenAvailable(): Promise<boolean>;
9
10
  track(estimatedMs: number): void;
10
11
  }
@@ -6,11 +6,13 @@ export class GPUTraceQueue {
6
6
  constructor(queue) {
7
7
  this.queue = queue;
8
8
  }
9
+ get hasPending() { return this.pending.size > 0; }
9
10
  available(budgetMs) {
10
11
  return Math.max(0, budgetMs - this.pendingMs);
11
12
  }
12
13
  whenAvailable() {
13
- return this.pending.values().next().value ?? Promise.resolve();
14
+ const pending = this.pending.values().next().value;
15
+ return pending ? pending.then(() => true) : Promise.resolve(false);
14
16
  }
15
17
  track(estimatedMs) {
16
18
  this.pendingMs += estimatedMs;
@@ -1 +1 @@
1
- {"version":3,"file":"GPUTraceQueue.js","sourceRoot":"","sources":["../../src/renderer/GPUTraceQueue.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,OAAO,aAAa;IAIJ;IAHZ,SAAS,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE3C,YAAoB,KAAe;QAAf,UAAK,GAAL,KAAK,CAAU;IAAG,CAAC;IAEvC,SAAS,CAAC,QAAgB;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,WAAmB;QACvB,IAAI,CAAC,SAAS,IAAI,WAAW,CAAC;QAC9B,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAChC,qEAAqE;YACrE,wEAAwE;YACxE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;gBAAE,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QAC7C,CAAC,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;CACF","sourcesContent":["/** Shared by resident targets so switching images cannot reset queued work. */\nexport class GPUTraceQueue {\n private pendingMs = 0;\n private pending = new Set<Promise<void>>();\n\n constructor(private queue: GPUQueue) {}\n\n available(budgetMs: number): number {\n return Math.max(0, budgetMs - this.pendingMs);\n }\n\n whenAvailable(): Promise<void> {\n return this.pending.values().next().value ?? Promise.resolve();\n }\n\n track(estimatedMs: number): void {\n this.pendingMs += estimatedMs;\n // Fence each sample, rather than the entire batch: a later display tick can\n // replace completed samples while the tail of the previous batch still runs.\n const retire = () => {\n this.pendingMs = Math.max(0, this.pendingMs - estimatedMs);\n this.pending.delete(completion);\n // Subtracting estimates leaves rounding residue. An empty queue must\n // admit a sample as long as the budget, or nothing would wake it again.\n if (!this.pending.size) this.pendingMs = 0;\n };\n const completion = this.queue.onSubmittedWorkDone().then(retire, retire);\n this.pending.add(completion);\n }\n}\n"]}
1
+ {"version":3,"file":"GPUTraceQueue.js","sourceRoot":"","sources":["../../src/renderer/GPUTraceQueue.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,OAAO,aAAa;IAIJ;IAHZ,SAAS,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE3C,YAAoB,KAAe;QAAf,UAAK,GAAL,KAAK,CAAU;IAAG,CAAC;IAEvC,IAAI,UAAU,KAAc,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3D,SAAS,CAAC,QAAgB;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;IAED,aAAa;QACX,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACnD,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,WAAmB;QACvB,IAAI,CAAC,SAAS,IAAI,WAAW,CAAC;QAC9B,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAChC,qEAAqE;YACrE,wEAAwE;YACxE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;gBAAE,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QAC7C,CAAC,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;CACF","sourcesContent":["/** Shared by resident targets so switching images cannot reset queued work. */\nexport class GPUTraceQueue {\n private pendingMs = 0;\n private pending = new Set<Promise<void>>();\n\n constructor(private queue: GPUQueue) {}\n\n get hasPending(): boolean { return this.pending.size > 0; }\n\n available(budgetMs: number): number {\n return Math.max(0, budgetMs - this.pendingMs);\n }\n\n whenAvailable(): Promise<boolean> {\n const pending = this.pending.values().next().value;\n return pending ? pending.then(() => true) : Promise.resolve(false);\n }\n\n track(estimatedMs: number): void {\n this.pendingMs += estimatedMs;\n // Fence each sample, rather than the entire batch: a later display tick can\n // replace completed samples while the tail of the previous batch still runs.\n const retire = () => {\n this.pendingMs = Math.max(0, this.pendingMs - estimatedMs);\n this.pending.delete(completion);\n // Subtracting estimates leaves rounding residue. An empty queue must\n // admit a sample as long as the budget, or nothing would wake it again.\n if (!this.pending.size) this.pendingMs = 0;\n };\n const completion = this.queue.onSubmittedWorkDone().then(retire, retire);\n this.pending.add(completion);\n }\n}\n"]}
@@ -18,8 +18,13 @@ export default class MaterialData {
18
18
  this.textures = maps.textures.slice();
19
19
  for (const texture of lightTextures) {
20
20
  if (texture) {
21
- this.lightTexturesIndices.push(this.textures.length);
22
- this.textures.push(texture);
21
+ const existing = options.independentTextures ? this.textures.indexOf(texture) : -1;
22
+ if (existing >= 0)
23
+ this.lightTexturesIndices.push(existing);
24
+ else {
25
+ this.lightTexturesIndices.push(this.textures.length);
26
+ this.textures.push(texture);
27
+ }
23
28
  }
24
29
  else
25
30
  this.lightTexturesIndices.push(-1);
@@ -1 +1 @@
1
- {"version":3,"file":"MaterialData.js","sourceRoot":"","sources":["../../src/renderer/MaterialData.ts"],"names":[],"mappings":"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","sourcesContent":["import type MaterialState from '../scene/MaterialState.js';\nimport type TextureState from '../scene/TextureState.js';\nimport { MATERIAL_TEXTURE_NAMES, MATERIAL_TEXTURE_SLOTS } from './materialTextureSlots.js';\nimport { mergeTexturesFromMaterials } from './materialTextures.js';\nimport { createMaterialBufferData, repackMaterialParams } from './materialParams.js';\nimport { packMaterialBuffer } from './materialLayout.js';\nimport { planTextureAtlas, type MaterialTableOptions, type TextureAtlasPlan } from './textureAtlas.js';\nimport type { BufferData } from './materialBufferData.js';\n\n// CPU material preparation shared by browser uploads and native command generation.\n// The adapter describes source encoding; the core never decodes DOM/GPU resources.\nexport default class MaterialData {\n bufferData: BufferData;\n textures: TextureState[];\n displacementMaps: TextureState[];\n lightTexturesIndices: number[] = [];\n defines: Record<string, number> = {};\n atlas: TextureAtlasPlan;\n constructor(materials: MaterialState[], lightTextures: (TextureState | undefined)[],\n colorLinear: (texture: TextureState) => number, options: MaterialTableOptions = {},\n floatStorage?: (texture: TextureState) => boolean) {\n const maps = mergeTexturesFromMaterials(materials, MATERIAL_TEXTURE_NAMES);\n this.displacementMaps = maps.indices.displacementMap.map(index => maps.textures[index]).filter(texture => !!texture);\n this.textures = maps.textures.slice();\n for (const texture of lightTextures) {\n if (texture) { this.lightTexturesIndices.push(this.textures.length); this.textures.push(texture); }\n else this.lightTexturesIndices.push(-1);\n }\n this.bufferData = createMaterialBufferData(materials);\n this.bufferData.diffuseMapIndex = maps.indices.map;\n this.bufferData.alphaMapIndex = maps.indices.alphaMap;\n this.bufferData.normalMapIndex = maps.indices.normalMap;\n this.bufferData.emissiveMapIndex = maps.indices.emissiveMap;\n this.bufferData.metallicRoughnessMapIndex = maps.indices.metallicRoughnessMap;\n this.bufferData.roughnessMapIndex = maps.indices.roughnessMap;\n this.bufferData.metalnessMapIndex = maps.indices.metalnessMap;\n this.bufferData.transmissionMapIndex = maps.indices.transmissionMap;\n this.bufferData.thinFilmMapIndex = maps.indices.thinFilmMap;\n this.bufferData.displacementMapIndex = maps.indices.displacementMap;\n this.bufferData.coatWeightMapIndex = maps.indices.coatWeightMap;\n this.bufferData.coatRoughnessMapIndex = maps.indices.coatRoughnessMap;\n this.bufferData.fuzzColorMapIndex = maps.indices.fuzzColorMap;\n this.bufferData.fuzzRoughnessMapIndex = maps.indices.fuzzRoughnessMap;\n this.bufferData.specularWeightMapIndex = maps.indices.specularWeightMap;\n this.bufferData.specularColorMapIndex = maps.indices.specularColorMap;\n this.bufferData.anisotropyMapIndex = maps.indices.anisotropyMap;\n this.bufferData.coatNormalMapIndex = maps.indices.coatNormalMap;\n this.atlas = planTextureAtlas(this.textures, this.textures.map(colorLinear), options,\n floatStorage ? this.textures.some(floatStorage) : undefined);\n this.bufferData.mapInfo = this.atlas.mapInfo; this.bufferData.mapTransform = this.atlas.mapTransform;\n this.bufferData.mapWrap = this.atlas.mapWrap;\n this.bufferData.mapUvChannel = this.atlas.mapUvChannel; this.bufferData.mapColorLinear = this.atlas.mapColorLinear;\n this.defines = {NUM_MAPS: this.textures.length};\n for (const slot of MATERIAL_TEXTURE_SLOTS) {\n if (slot.define) this.defines[slot.define] = maps.indices[slot.property].filter(index => index >= 0).length;\n }\n }\n repackParams(materials: MaterialState[]) { repackMaterialParams(this.bufferData, materials); }\n getBufferArray() { return packMaterialBuffer(this.bufferData); }\n}\n"]}
1
+ {"version":3,"file":"MaterialData.js","sourceRoot":"","sources":["../../src/renderer/MaterialData.ts"],"names":[],"mappings":"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","sourcesContent":["import type MaterialState from '../scene/MaterialState.js';\nimport type TextureState from '../scene/TextureState.js';\nimport { MATERIAL_TEXTURE_NAMES, MATERIAL_TEXTURE_SLOTS } from './materialTextureSlots.js';\nimport { mergeTexturesFromMaterials } from './materialTextures.js';\nimport { createMaterialBufferData, repackMaterialParams } from './materialParams.js';\nimport { packMaterialBuffer } from './materialLayout.js';\nimport { planTextureAtlas, type MaterialTableOptions, type TextureAtlasPlan } from './textureAtlas.js';\nimport type { BufferData } from './materialBufferData.js';\n\n// CPU material preparation shared by browser uploads and native command generation.\n// The adapter describes source encoding; the core never decodes DOM/GPU resources.\nexport default class MaterialData {\n bufferData: BufferData;\n textures: TextureState[];\n displacementMaps: TextureState[];\n lightTexturesIndices: number[] = [];\n defines: Record<string, number> = {};\n atlas: TextureAtlasPlan;\n constructor(materials: MaterialState[], lightTextures: (TextureState | undefined)[],\n colorLinear: (texture: TextureState) => number, options: MaterialTableOptions = {},\n floatStorage?: (texture: TextureState) => boolean) {\n const maps = mergeTexturesFromMaterials(materials, MATERIAL_TEXTURE_NAMES);\n this.displacementMaps = maps.indices.displacementMap.map(index => maps.textures[index]).filter(texture => !!texture);\n this.textures = maps.textures.slice();\n for (const texture of lightTextures) {\n if (texture) {\n const existing = options.independentTextures ? this.textures.indexOf(texture) : -1;\n if (existing >= 0) this.lightTexturesIndices.push(existing);\n else { this.lightTexturesIndices.push(this.textures.length); this.textures.push(texture); }\n }\n else this.lightTexturesIndices.push(-1);\n }\n this.bufferData = createMaterialBufferData(materials);\n this.bufferData.diffuseMapIndex = maps.indices.map;\n this.bufferData.alphaMapIndex = maps.indices.alphaMap;\n this.bufferData.normalMapIndex = maps.indices.normalMap;\n this.bufferData.emissiveMapIndex = maps.indices.emissiveMap;\n this.bufferData.metallicRoughnessMapIndex = maps.indices.metallicRoughnessMap;\n this.bufferData.roughnessMapIndex = maps.indices.roughnessMap;\n this.bufferData.metalnessMapIndex = maps.indices.metalnessMap;\n this.bufferData.transmissionMapIndex = maps.indices.transmissionMap;\n this.bufferData.thinFilmMapIndex = maps.indices.thinFilmMap;\n this.bufferData.displacementMapIndex = maps.indices.displacementMap;\n this.bufferData.coatWeightMapIndex = maps.indices.coatWeightMap;\n this.bufferData.coatRoughnessMapIndex = maps.indices.coatRoughnessMap;\n this.bufferData.fuzzColorMapIndex = maps.indices.fuzzColorMap;\n this.bufferData.fuzzRoughnessMapIndex = maps.indices.fuzzRoughnessMap;\n this.bufferData.specularWeightMapIndex = maps.indices.specularWeightMap;\n this.bufferData.specularColorMapIndex = maps.indices.specularColorMap;\n this.bufferData.anisotropyMapIndex = maps.indices.anisotropyMap;\n this.bufferData.coatNormalMapIndex = maps.indices.coatNormalMap;\n this.atlas = planTextureAtlas(this.textures, this.textures.map(colorLinear), options,\n floatStorage ? this.textures.some(floatStorage) : undefined);\n this.bufferData.mapInfo = this.atlas.mapInfo; this.bufferData.mapTransform = this.atlas.mapTransform;\n this.bufferData.mapWrap = this.atlas.mapWrap;\n this.bufferData.mapUvChannel = this.atlas.mapUvChannel; this.bufferData.mapColorLinear = this.atlas.mapColorLinear;\n this.defines = {NUM_MAPS: this.textures.length};\n for (const slot of MATERIAL_TEXTURE_SLOTS) {\n if (slot.define) this.defines[slot.define] = maps.indices[slot.property].filter(index => index >= 0).length;\n }\n }\n repackParams(materials: MaterialState[]) { repackMaterialParams(this.bufferData, materials); }\n getBufferArray() { return packMaterialBuffer(this.bufferData); }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { SurfaceHit } from './surfaceHit.js';
1
2
  import type { PostProcess } from '../Display.js';
2
3
  import type Render from '../Render.js';
3
4
  import type { DenoiserHandle, OIDNWeb } from './denoiser.js';
@@ -157,6 +158,10 @@ export interface RenderingDiagnostics {
157
158
  };
158
159
  };
159
160
  gpuTraceTiming?: {
161
+ /** Last light-tracing batch time in milliseconds. Default undefined until measured. */ causticTimeMs?: number;
162
+ /** Last photon-map batch time in milliseconds. Default undefined until measured. */ photonMappingTimeMs?: number;
163
+ /** Photon emission, count, block scan, sum scan and scatter times in milliseconds. Default undefined until measured. */ photonStagesMs?: number[];
164
+ /** Camera path kernel time in milliseconds. Default undefined until measured. */ pathTracingTimeMs?: number;
160
165
  tileTimeMs: number;
161
166
  tileTimePerSampleMs?: number;
162
167
  estimatedFullFrameTimeMs: number;
@@ -180,6 +185,7 @@ export interface SceneResources {
180
185
  getDiagnostics(): RenderingDiagnostics;
181
186
  dispose(): void;
182
187
  readDepthSamples(area: DepthSampleArea): Promise<Float32Array | undefined>;
188
+ pickSurface?(x: number, y: number, aspect?: number): Promise<SurfaceHit | null | undefined>;
183
189
  }
184
190
  /** Explicit execution and composition for one accumulation. */
185
191
  export interface RenderPasses {
@@ -189,7 +195,11 @@ export interface RenderPasses {
189
195
  trace(camera: Camera, maxSamples: number): boolean;
190
196
  drawPostProcess(): void;
191
197
  /** Apply display-only passes to the last composed image; absent on WebGL. */
192
- applyPostProcesses?(passes: readonly PostProcess[], render: Render): void;
198
+ applyPostProcesses?(passes: readonly PostProcess[], render: Render, options?: readonly import('../Display.js').PostProcessOptions[], crop?: {
199
+ width: number;
200
+ height: number;
201
+ }): void;
202
+ getAovTexture?(kind: 'diffuseColor' | 'specularColor' | 'depth'): GPUTexture | undefined;
193
203
  getOutputTexture?(kind: 'hdr' | 'display'): GPUTexture | undefined;
194
204
  /** Configure deterministic headless batches used by offline frame export. */
195
205
  setOfflineRenderOptions(options: {
@@ -1 +1 @@
1
- {"version":3,"file":"RenderingPipeline.js","sourceRoot":"","sources":["../../src/renderer/RenderingPipeline.ts"],"names":[],"mappings":"AAqLA,OAAO,EAAC,qBAAqB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC","sourcesContent":["import type {PostProcess} from '../Display.js';\nimport type Render from '../Render.js';\nimport type {DenoiserHandle, OIDNWeb} from './denoiser.js';\nimport type {DenoiseSchedule} from '../Render.js';\nimport type { DepthSampleArea } from './depthQuery.js';\nimport Camera from '../scene/Camera.js';\nimport Mesh from '../scene/Mesh.js';\nimport Geometry from '../scene/Geometry.js';\nimport OpenPBRMaterial from '../scene/OpenPBRMaterial.js';\nimport type { SceneBackground, SceneLight } from '../scene/Scene.js';\nimport type { ColorRGB } from '../types.js';\nimport type { TextureAtlasStats } from './MaterialTable.js';\nimport type {\n OfflineAdaptiveSamplingOptions,\n OfflineAdaptiveSamplingResult\n} from './OfflineAdaptiveSampling.js';\n\nexport type ToneMapping =\n | 'agx'\n | 'aces'\n | 'cineon'\n | 'reinhard'\n | 'linear';\n\nexport interface ToneMappingParams {\n /** Linear exposure multiplier. Default `1`. */\n exposure?: number;\n /** Display tone mapper. Default `agx`. */\n toneMapping?: ToneMapping;\n /** Contrast multiplier. Default `1`. */\n contrast?: number;\n /** Saturation multiplier. Default `1`. */\n saturation?: number;\n /** Color temperature adjustment in [-1, 1]. Default `0`. */\n temperature?: number;\n /** Green/magenta adjustment in [-1, 1]. Default `0`. */\n tint?: number;\n /** Vignette strength in [0, 1]. Default `0`. */\n vignette?: number;\n}\n\nexport type DisplayChannel =\n | 'final'\n | 'shadow'\n | 'albedo'\n | 'normal'\n | 'shadowCatcher'\n | import('./aov.js').AovKind;\n\n/** Primary-ray policy for glTF BLEND materials. */\nexport type AlphaBlendStrategy =\n | 'stochastic'\n | 'deterministic-source-over';\n\n/** Generated source measurements for one shader module. */\nexport interface ShaderModuleCompilationDiagnostics {\n /** Module or pipeline diagnostic label. */\n label: string;\n /** Generated shader source size in bytes. */\n sourceBytes: number;\n /** Generated shader source line count. */\n sourceLines: number;\n /** Stable source-level fingerprint for diagnosing cache-key churn. */\n sourceFingerprint: string;\n}\n\n/** Compilation measurements for one shader pipeline/program. */\nexport interface ShaderPipelineCompilationDiagnostics {\n /** Module or pipeline diagnostic label. */\n label: string;\n /** Compute entry-point name. */\n entryPoint: string;\n /**\n * Whether this pipeline belongs to the minimum set needed to present a\n * usable first image. Optional reuse and quality stages are false even\n * while the current eager compiler still waits for them.\n */\n firstFrameRequired: boolean;\n /** Current compilation state. */\n status: 'queued' | 'compiling' | 'ready' | 'failed';\n /** Milliseconds from the start of this pipeline compilation batch. */\n startedAtMs?: number;\n /** Milliseconds from the start of this pipeline compilation batch. */\n endedAtMs?: number;\n /** Pipeline compilation duration in milliseconds; undefined until completed. */\n durationMs?: number;\n /** Number of pipelines compiling when this one started; undefined before start. */\n concurrentCompilationCount?: number;\n /** Failure message; undefined without a compilation error. */\n error?: string;\n}\n\n/** Source generation and asynchronous pipeline compilation measurements. */\nexport interface ShaderCompilationDiagnostics {\n /** Current compilation state. */\n status: 'compiling' | 'ready' | 'failed';\n /** Generated shader source size in bytes. */\n sourceBytes: number;\n /** Generated shader source line count. */\n sourceLines: number;\n /** Total pipelines in the batch. */\n pipelineCount: number;\n /** Number of pipelines successfully completed. */\n completedPipelineCount: number;\n /** CPU shader source generation time in milliseconds. */\n sourceGenerationTimeMs: number;\n /** Shader module creation time in milliseconds. */\n moduleCreationTimeMs: number;\n /** Per-module source measurements. */\n modules: ShaderModuleCompilationDiagnostics[];\n /** Per-pipeline timing and status measurements. */\n pipelines: ShaderPipelineCompilationDiagnostics[];\n /** Configured pool size, capped to the number of pipelines in this batch. */\n maximumConcurrentPipelineCount: number;\n /** Largest number of createComputePipelineAsync calls active at once. */\n peakConcurrentPipelineCount: number;\n /** Batch pipeline compilation time in milliseconds; undefined until completed. */\n pipelineCompilationTimeMs?: number;\n /** Total batch preparation time in milliseconds; undefined until completed. */\n totalTimeMs?: number;\n /** Failure message; undefined without a compilation error. */\n error?: string;\n}\n\nexport interface RenderingDiagnostics {\n /** Label of the last complete target presented; undefined before the first image. */\n presentedTarget?: string;\n /** Whether the displayed target contains a complete image. Default false. */\n presentedComplete?: boolean;\n /** Resident target dimensions and allocated GPU bytes, including denoise resources. */\n targets?: { label: string; width: number; height: number; bytes: number }[];\n hdrPresentation?: {\n requested: boolean;\n active: boolean;\n /** Largest encoded RGB component sent to the HDR presentation pass. */\n peakDisplayValue?: number;\n };\n textureAtlas?: TextureAtlasStats;\n /** Material texture storage selection and estimated payload; driver allocation overhead is excluded. */\n materialTextures?: {\n requested: 'atlas' | 'auto';\n active: 'atlas' | 'bindless';\n /** Reason auto selected atlas; undefined when no fallback occurred. */\n fallbackReason?: string;\n textureCount: number;\n estimatedBytes: number;\n scale: number;\n /** Metadata buffer bytes; zero for atlas. */\n metadataBytes: number;\n /** Resource-table slots; zero for atlas. */\n resourceTableCapacity: number;\n /** Payload and metadata waiting for previous GPU submissions; zero when none. */\n retiredBytes: number;\n };\n /** Shader compilation measurements: current WebGPU batch or cumulative WebGL programs. */\n shaderCompilation?: ShaderCompilationDiagnostics;\n /** Light-selection table summary (WebGPU). */\n lightSampling?: {\n emissiveTriangles: number;\n activeChoices: number;\n totalWeight: number;\n domains: Record<\n 'environment' | 'infinite' | 'localAndEmissive',\n { count: number; weight: number; pmf: number }\n >;\n };\n /** Traversal stack capacities compiled into the current WebGPU shader. */\n bvh?: {\n tlasTraversalStackSize?: number;\n blasTraversalStackSize?: number;\n /** Last static preparation statistics, excluding queue and WASM loading time. */\n lastBuild?: { geometryCount: number; bvhTimeMs: number };\n };\n gpuTraceTiming?: {\n tileTimeMs: number;\n tileTimePerSampleMs?: number;\n estimatedFullFrameTimeMs: number;\n tileCount: number;\n };\n}\n\nexport {DEFAULT_BEVEL_SAMPLES, MIN_BEVEL_SAMPLES, MAX_BEVEL_SAMPLES, DEFAULT_DIRECT_LIGHT_CANDIDATES, MIN_DIRECT_LIGHT_CANDIDATES, MAX_DIRECT_LIGHT_CANDIDATES, normalizeDirectLightCandidates, normalizeBevelSamples} from './renderingLimits.js';\n\n/** Shared scene resources; uploaded once per committed revision. */\nexport interface SceneResources {\n /** Returns true when scene geometry, materials, or instance transforms changed. */\n setMeshes(meshes: Mesh[], update?: {\n transforms?: readonly Mesh[];\n colors?: readonly Mesh[];\n geometries?: readonly Geometry[];\n materials?: readonly OpenPBRMaterial[];\n }): boolean;\n /** Changes the software memory guard used when packing material textures. */\n setTextureAtlasBudget(maxBytes: number): boolean;\n setLights(lights: SceneLight[]): void;\n setBackground(background?: SceneBackground): void;\n getDiagnostics(): RenderingDiagnostics;\n dispose(): void;\n readDepthSamples(area: DepthSampleArea): Promise<Float32Array | undefined>;\n}\n/** Explicit execution and composition for one accumulation. */\nexport interface RenderPasses {\n isFirstFrame(): boolean;\n /** Discard accumulated samples and any asynchronous denoise output. */\n restartSamples(): void;\n trace(camera: Camera, maxSamples: number): boolean;\n drawPostProcess(): void;\n /** Apply display-only passes to the last composed image; absent on WebGL. */\n applyPostProcesses?(passes: readonly PostProcess[], render: Render): void;\n getOutputTexture?(kind: 'hdr' | 'display'): GPUTexture | undefined;\n /** Configure deterministic headless batches used by offline frame export. */\n setOfflineRenderOptions(options: {\n samplesPerBatch: number;\n deferPostProcess: boolean;\n sampleSequenceOffset: number;\n }): void;\n /** Store luminance second moments in the normal output during a pilot pass. */\n setVarianceMoments(enabled: boolean): void;\n /** Skip pixels once their independent final-pass target has been reached. */\n setAdaptiveSamplingTargets(targets?: {\n width: number;\n height: number;\n data: Float32Array;\n }): void;\n /** Generate final sample targets on GPU; WebGL returns undefined. */\n prepareOfflineAdaptiveSampling(\n options: OfflineAdaptiveSamplingOptions\n ): Promise<OfflineAdaptiveSamplingResult | undefined>;\n /** Enables extended-range float canvas presentation when the host supports it. */\n setHDROutputEnabled?(enabled: boolean): boolean;\n setToneMappingParams(params: ToneMappingParams): boolean;\n setSize(width: number, height: number): void;\n /**\n * Denoises the current image with an explicitly selected built-in or custom filter. Resolves whether the result is now displayed.\n */\n performDenoise?(\n denoiser: DenoiserHandle,\n alphaDenoiser?: OIDNWeb,\n schedule?: DenoiseSchedule,\n signal?: AbortSignal\n ): Promise<boolean>;\n /** Drops a denoise in flight; the next draw invalidates its output. */\n cancelDenoise?(): void;\n /** Keep the last completed OIDN image visible while static while-converging batches accumulate. */\n setPreserveDenoisedOutput?(preserve: boolean): void;\n getTotalSamplesRendered(): number;\n getDiagnostics(): RenderingDiagnostics;\n /** Waits for asynchronous timestamp diagnostics without changing samples. */\n flushDiagnostics(): Promise<void>;\n setShadowCatcherParams(color: ColorRGB, alpha: number): boolean;\n setBloomParams(opts: {\n intensity: number;\n threshold: number;\n radius: number;\n }): boolean;\n setOnSampleRendered(\n cb: (sampleCount: number, isFirstFrame: boolean, frameTime: number) => void\n ): void;\n setOutput(output: DisplayChannel): boolean;\n setAovDisplayOptions?(options: import('./aovDisplay.js').AovDisplayOptions): void;\n dispose(): void;\n aovReady?(kind: import('./aov.js').AovKind): boolean;\n /** Updates optional surface outputs without tracing beauty; false while compiling. */\n refineAovs?(camera: Camera, samples?: number): boolean;\n restartAovs?(): void;\n getAovSamples?(): number;\n readAov?(kind: import('./aov.js').AovKind): Promise<{width: number; height: number; data: Float32Array | Uint32Array}>;\n readOutput(kind: 'hdr' | 'denoised' | 'albedo' | 'normal'): Promise<{\n data: Float32Array;\n width: number;\n height: number;\n }>;\n}\n"]}
1
+ {"version":3,"file":"RenderingPipeline.js","sourceRoot":"","sources":["../../src/renderer/RenderingPipeline.ts"],"names":[],"mappings":"AA0LA,OAAO,EAAC,qBAAqB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC","sourcesContent":["import type {SurfaceHit} from './surfaceHit.js';\nimport type {PostProcess} from '../Display.js';\nimport type Render from '../Render.js';\nimport type {DenoiserHandle, OIDNWeb} from './denoiser.js';\nimport type {DenoiseSchedule} from '../Render.js';\nimport type { DepthSampleArea } from './depthQuery.js';\nimport Camera from '../scene/Camera.js';\nimport Mesh from '../scene/Mesh.js';\nimport Geometry from '../scene/Geometry.js';\nimport OpenPBRMaterial from '../scene/OpenPBRMaterial.js';\nimport type { SceneBackground, SceneLight } from '../scene/Scene.js';\nimport type { ColorRGB } from '../types.js';\nimport type { TextureAtlasStats } from './MaterialTable.js';\nimport type {\n OfflineAdaptiveSamplingOptions,\n OfflineAdaptiveSamplingResult\n} from './OfflineAdaptiveSampling.js';\n\nexport type ToneMapping =\n | 'agx'\n | 'aces'\n | 'cineon'\n | 'reinhard'\n | 'linear';\n\nexport interface ToneMappingParams {\n /** Linear exposure multiplier. Default `1`. */\n exposure?: number;\n /** Display tone mapper. Default `agx`. */\n toneMapping?: ToneMapping;\n /** Contrast multiplier. Default `1`. */\n contrast?: number;\n /** Saturation multiplier. Default `1`. */\n saturation?: number;\n /** Color temperature adjustment in [-1, 1]. Default `0`. */\n temperature?: number;\n /** Green/magenta adjustment in [-1, 1]. Default `0`. */\n tint?: number;\n /** Vignette strength in [0, 1]. Default `0`. */\n vignette?: number;\n}\n\nexport type DisplayChannel =\n | 'final'\n | 'shadow'\n | 'albedo'\n | 'normal'\n | 'shadowCatcher'\n | import('./aov.js').AovKind;\n\n/** Primary-ray policy for glTF BLEND materials. */\nexport type AlphaBlendStrategy =\n | 'stochastic'\n | 'deterministic-source-over';\n\n/** Generated source measurements for one shader module. */\nexport interface ShaderModuleCompilationDiagnostics {\n /** Module or pipeline diagnostic label. */\n label: string;\n /** Generated shader source size in bytes. */\n sourceBytes: number;\n /** Generated shader source line count. */\n sourceLines: number;\n /** Stable source-level fingerprint for diagnosing cache-key churn. */\n sourceFingerprint: string;\n}\n\n/** Compilation measurements for one shader pipeline/program. */\nexport interface ShaderPipelineCompilationDiagnostics {\n /** Module or pipeline diagnostic label. */\n label: string;\n /** Compute entry-point name. */\n entryPoint: string;\n /**\n * Whether this pipeline belongs to the minimum set needed to present a\n * usable first image. Optional reuse and quality stages are false even\n * while the current eager compiler still waits for them.\n */\n firstFrameRequired: boolean;\n /** Current compilation state. */\n status: 'queued' | 'compiling' | 'ready' | 'failed';\n /** Milliseconds from the start of this pipeline compilation batch. */\n startedAtMs?: number;\n /** Milliseconds from the start of this pipeline compilation batch. */\n endedAtMs?: number;\n /** Pipeline compilation duration in milliseconds; undefined until completed. */\n durationMs?: number;\n /** Number of pipelines compiling when this one started; undefined before start. */\n concurrentCompilationCount?: number;\n /** Failure message; undefined without a compilation error. */\n error?: string;\n}\n\n/** Source generation and asynchronous pipeline compilation measurements. */\nexport interface ShaderCompilationDiagnostics {\n /** Current compilation state. */\n status: 'compiling' | 'ready' | 'failed';\n /** Generated shader source size in bytes. */\n sourceBytes: number;\n /** Generated shader source line count. */\n sourceLines: number;\n /** Total pipelines in the batch. */\n pipelineCount: number;\n /** Number of pipelines successfully completed. */\n completedPipelineCount: number;\n /** CPU shader source generation time in milliseconds. */\n sourceGenerationTimeMs: number;\n /** Shader module creation time in milliseconds. */\n moduleCreationTimeMs: number;\n /** Per-module source measurements. */\n modules: ShaderModuleCompilationDiagnostics[];\n /** Per-pipeline timing and status measurements. */\n pipelines: ShaderPipelineCompilationDiagnostics[];\n /** Configured pool size, capped to the number of pipelines in this batch. */\n maximumConcurrentPipelineCount: number;\n /** Largest number of createComputePipelineAsync calls active at once. */\n peakConcurrentPipelineCount: number;\n /** Batch pipeline compilation time in milliseconds; undefined until completed. */\n pipelineCompilationTimeMs?: number;\n /** Total batch preparation time in milliseconds; undefined until completed. */\n totalTimeMs?: number;\n /** Failure message; undefined without a compilation error. */\n error?: string;\n}\n\nexport interface RenderingDiagnostics {\n /** Label of the last complete target presented; undefined before the first image. */\n presentedTarget?: string;\n /** Whether the displayed target contains a complete image. Default false. */\n presentedComplete?: boolean;\n /** Resident target dimensions and allocated GPU bytes, including denoise resources. */\n targets?: { label: string; width: number; height: number; bytes: number }[];\n hdrPresentation?: {\n requested: boolean;\n active: boolean;\n /** Largest encoded RGB component sent to the HDR presentation pass. */\n peakDisplayValue?: number;\n };\n textureAtlas?: TextureAtlasStats;\n /** Material texture storage selection and estimated payload; driver allocation overhead is excluded. */\n materialTextures?: {\n requested: 'atlas' | 'auto';\n active: 'atlas' | 'bindless';\n /** Reason auto selected atlas; undefined when no fallback occurred. */\n fallbackReason?: string;\n textureCount: number;\n estimatedBytes: number;\n scale: number;\n /** Metadata buffer bytes; zero for atlas. */\n metadataBytes: number;\n /** Resource-table slots; zero for atlas. */\n resourceTableCapacity: number;\n /** Payload and metadata waiting for previous GPU submissions; zero when none. */\n retiredBytes: number;\n };\n /** Shader compilation measurements: current WebGPU batch or cumulative WebGL programs. */\n shaderCompilation?: ShaderCompilationDiagnostics;\n /** Light-selection table summary (WebGPU). */\n lightSampling?: {\n emissiveTriangles: number;\n activeChoices: number;\n totalWeight: number;\n domains: Record<\n 'environment' | 'infinite' | 'localAndEmissive',\n { count: number; weight: number; pmf: number }\n >;\n };\n /** Traversal stack capacities compiled into the current WebGPU shader. */\n bvh?: {\n tlasTraversalStackSize?: number;\n blasTraversalStackSize?: number;\n /** Last static preparation statistics, excluding queue and WASM loading time. */\n lastBuild?: { geometryCount: number; bvhTimeMs: number };\n };\n gpuTraceTiming?: {\n /** Last light-tracing batch time in milliseconds. Default undefined until measured. */ causticTimeMs?:number;\n /** Last photon-map batch time in milliseconds. Default undefined until measured. */ photonMappingTimeMs?:number;\n /** Photon emission, count, block scan, sum scan and scatter times in milliseconds. Default undefined until measured. */ photonStagesMs?:number[];\n /** Camera path kernel time in milliseconds. Default undefined until measured. */ pathTracingTimeMs?:number;\n tileTimeMs: number;\n tileTimePerSampleMs?: number;\n estimatedFullFrameTimeMs: number;\n tileCount: number;\n };\n}\n\nexport {DEFAULT_BEVEL_SAMPLES, MIN_BEVEL_SAMPLES, MAX_BEVEL_SAMPLES, DEFAULT_DIRECT_LIGHT_CANDIDATES, MIN_DIRECT_LIGHT_CANDIDATES, MAX_DIRECT_LIGHT_CANDIDATES, normalizeDirectLightCandidates, normalizeBevelSamples} from './renderingLimits.js';\n\n/** Shared scene resources; uploaded once per committed revision. */\nexport interface SceneResources {\n /** Returns true when scene geometry, materials, or instance transforms changed. */\n setMeshes(meshes: Mesh[], update?: {\n transforms?: readonly Mesh[];\n colors?: readonly Mesh[];\n geometries?: readonly Geometry[];\n materials?: readonly OpenPBRMaterial[];\n }): boolean;\n /** Changes the software memory guard used when packing material textures. */\n setTextureAtlasBudget(maxBytes: number): boolean;\n setLights(lights: SceneLight[]): void;\n setBackground(background?: SceneBackground): void;\n getDiagnostics(): RenderingDiagnostics;\n dispose(): void;\n readDepthSamples(area: DepthSampleArea): Promise<Float32Array | undefined>;\n pickSurface?(x: number, y: number, aspect?: number): Promise<SurfaceHit | null | undefined>;\n}\n/** Explicit execution and composition for one accumulation. */\nexport interface RenderPasses {\n isFirstFrame(): boolean;\n /** Discard accumulated samples and any asynchronous denoise output. */\n restartSamples(): void;\n trace(camera: Camera, maxSamples: number): boolean;\n drawPostProcess(): void;\n /** Apply display-only passes to the last composed image; absent on WebGL. */\n applyPostProcesses?(passes: readonly PostProcess[], render: Render, options?: readonly import('../Display.js').PostProcessOptions[], crop?: {width: number; height: number}): void;\n getAovTexture?(kind: 'diffuseColor' | 'specularColor' | 'depth'): GPUTexture | undefined;\n getOutputTexture?(kind: 'hdr' | 'display'): GPUTexture | undefined;\n /** Configure deterministic headless batches used by offline frame export. */\n setOfflineRenderOptions(options: {\n samplesPerBatch: number;\n deferPostProcess: boolean;\n sampleSequenceOffset: number;\n }): void;\n /** Store luminance second moments in the normal output during a pilot pass. */\n setVarianceMoments(enabled: boolean): void;\n /** Skip pixels once their independent final-pass target has been reached. */\n setAdaptiveSamplingTargets(targets?: {\n width: number;\n height: number;\n data: Float32Array;\n }): void;\n /** Generate final sample targets on GPU; WebGL returns undefined. */\n prepareOfflineAdaptiveSampling(\n options: OfflineAdaptiveSamplingOptions\n ): Promise<OfflineAdaptiveSamplingResult | undefined>;\n /** Enables extended-range float canvas presentation when the host supports it. */\n setHDROutputEnabled?(enabled: boolean): boolean;\n setToneMappingParams(params: ToneMappingParams): boolean;\n setSize(width: number, height: number): void;\n /**\n * Denoises the current image with an explicitly selected built-in or custom filter. Resolves whether the result is now displayed.\n */\n performDenoise?(\n denoiser: DenoiserHandle,\n alphaDenoiser?: OIDNWeb,\n schedule?: DenoiseSchedule,\n signal?: AbortSignal\n ): Promise<boolean>;\n /** Drops a denoise in flight; the next draw invalidates its output. */\n cancelDenoise?(): void;\n /** Keep the last completed OIDN image visible while static while-converging batches accumulate. */\n setPreserveDenoisedOutput?(preserve: boolean): void;\n getTotalSamplesRendered(): number;\n getDiagnostics(): RenderingDiagnostics;\n /** Waits for asynchronous timestamp diagnostics without changing samples. */\n flushDiagnostics(): Promise<void>;\n setShadowCatcherParams(color: ColorRGB, alpha: number): boolean;\n setBloomParams(opts: {\n intensity: number;\n threshold: number;\n radius: number;\n }): boolean;\n setOnSampleRendered(\n cb: (sampleCount: number, isFirstFrame: boolean, frameTime: number) => void\n ): void;\n setOutput(output: DisplayChannel): boolean;\n setAovDisplayOptions?(options: import('./aovDisplay.js').AovDisplayOptions): void;\n dispose(): void;\n aovReady?(kind: import('./aov.js').AovKind): boolean;\n /** Updates optional surface outputs without tracing beauty; false while compiling. */\n refineAovs?(camera: Camera, samples?: number): boolean;\n restartAovs?(): void;\n getAovSamples?(): number;\n readAov?(kind: import('./aov.js').AovKind): Promise<{width: number; height: number; data: Float32Array | Uint32Array}>;\n readOutput(kind: 'hdr' | 'denoised' | 'albedo' | 'normal'): Promise<{\n data: Float32Array;\n width: number;\n height: number;\n }>;\n}\n"]}
@@ -18,7 +18,7 @@ export declare function makeTileRender(gl?: WebGL2RenderingContext): {
18
18
  setDesiredMsPerTile(time: number): void;
19
19
  getDesiredMsPerTile(): number;
20
20
  };
21
- export declare function makeTileRenderWithPredict(): {
21
+ export declare function makeTileRenderWithPredict(minimumTileSize?: number): {
22
22
  nextTile: () => {
23
23
  xIndex: number;
24
24
  yIndex: number;
@@ -37,7 +37,7 @@ export declare function makeTileRenderWithPredict(): {
37
37
  setMaxTiles(maxTiles: number): void;
38
38
  setDesiredMsPerTile(time: number): void;
39
39
  needsUpdatePredicting(): void;
40
- setLastTraceTime(time: number): void;
40
+ setLastTraceTime(time: number, timedColumns?: number, timedRows?: number): void;
41
41
  getLastTraceTime(): number;
42
42
  getDesiredMsPerTile(): number;
43
43
  };
@@ -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++;