@meimu/mutracer 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (536) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/LICENSES/GLSL-PathTracer-MIT.txt +21 -0
  4. package/LICENSES/GlMatrix-MIT.txt +19 -0
  5. package/LICENSES/KinoBloom-MIT.txt +21 -0
  6. package/LICENSES/OIDNWeb-MIT.txt +21 -0
  7. package/LICENSES/Octicons-MIT.txt +21 -0
  8. package/LICENSES/RayTracingRenderer-MIT.txt +21 -0
  9. package/LICENSES/TinyBVH-MIT.txt +21 -0
  10. package/LICENSES/WebGPUUtils-MIT.txt +21 -0
  11. package/LICENSES/ZetaRay-MIT.txt +21 -0
  12. package/README.md +195 -2
  13. package/THIRD_PARTY_NOTICES.md +160 -0
  14. package/dist/assets/bvh.worker.js +1 -0
  15. package/dist/mutracer.umd.js +14352 -0
  16. package/docs/api.md +394 -0
  17. package/docs/bvh.md +141 -0
  18. package/docs/denoising.md +301 -0
  19. package/docs/getting-started.md +308 -0
  20. package/docs/materials.md +281 -0
  21. package/docs/performance.md +85 -0
  22. package/docs/post-processing.md +87 -0
  23. package/docs/progressive-rendering.md +144 -0
  24. package/docs/sampling.md +40 -0
  25. package/docs/threejs.md +273 -0
  26. package/docs/todo.md +61 -0
  27. package/docs/updates.md +208 -0
  28. package/lib/Display.d.ts +72 -0
  29. package/lib/Display.js +121 -0
  30. package/lib/Display.js.map +1 -0
  31. package/lib/Engine.d.ts +161 -0
  32. package/lib/Engine.js +681 -0
  33. package/lib/Engine.js.map +1 -0
  34. package/lib/Render.d.ts +256 -0
  35. package/lib/Render.js +671 -0
  36. package/lib/Render.js.map +1 -0
  37. package/lib/RenderLoop.d.ts +76 -0
  38. package/lib/RenderLoop.js +204 -0
  39. package/lib/RenderLoop.js.map +1 -0
  40. package/lib/Sync.d.ts +19 -0
  41. package/lib/Sync.js +19 -0
  42. package/lib/Sync.js.map +1 -0
  43. package/lib/constants.d.ts +2 -0
  44. package/lib/constants.js +3 -0
  45. package/lib/constants.js.map +1 -0
  46. package/lib/main.d.ts +56 -0
  47. package/lib/main.js +43 -0
  48. package/lib/main.js.map +1 -0
  49. package/lib/math.d.ts +23 -0
  50. package/lib/math.js +56 -0
  51. package/lib/math.js.map +1 -0
  52. package/lib/oidn.d.ts +45 -0
  53. package/lib/oidn.js +42 -0
  54. package/lib/oidn.js.map +1 -0
  55. package/lib/oidnBundledWeights.cdn.d.ts +2 -0
  56. package/lib/oidnBundledWeights.cdn.js +5 -0
  57. package/lib/oidnBundledWeights.cdn.js.map +1 -0
  58. package/lib/oidnBundledWeights.d.ts +2 -0
  59. package/lib/oidnBundledWeights.js +4 -0
  60. package/lib/oidnBundledWeights.js.map +1 -0
  61. package/lib/renderer/DisplayPostProcess.d.ts +14 -0
  62. package/lib/renderer/DisplayPostProcess.js +41 -0
  63. package/lib/renderer/DisplayPostProcess.js.map +1 -0
  64. package/lib/renderer/MaterialTable.d.ts +119 -0
  65. package/lib/renderer/MaterialTable.js +550 -0
  66. package/lib/renderer/MaterialTable.js.map +1 -0
  67. package/lib/renderer/OfflineAdaptiveSampling.d.ts +47 -0
  68. package/lib/renderer/OfflineAdaptiveSampling.js +36 -0
  69. package/lib/renderer/OfflineAdaptiveSampling.js.map +1 -0
  70. package/lib/renderer/RenderingPipeline.d.ts +232 -0
  71. package/lib/renderer/RenderingPipeline.js +18 -0
  72. package/lib/renderer/RenderingPipeline.js.map +1 -0
  73. package/lib/renderer/ScratchPool.d.ts +13 -0
  74. package/lib/renderer/ScratchPool.js +21 -0
  75. package/lib/renderer/ScratchPool.js.map +1 -0
  76. package/lib/renderer/TileRender.d.ts +44 -0
  77. package/lib/renderer/TileRender.js +283 -0
  78. package/lib/renderer/TileRender.js.map +1 -0
  79. package/lib/renderer/WGPUBloomPass.d.ts +17 -0
  80. package/lib/renderer/WGPUBloomPass.js +243 -0
  81. package/lib/renderer/WGPUBloomPass.js.map +1 -0
  82. package/lib/renderer/WGPUCanvasSurface.d.ts +21 -0
  83. package/lib/renderer/WGPUCanvasSurface.js +48 -0
  84. package/lib/renderer/WGPUCanvasSurface.js.map +1 -0
  85. package/lib/renderer/WGPUComputePass.d.ts +73 -0
  86. package/lib/renderer/WGPUComputePass.js +317 -0
  87. package/lib/renderer/WGPUComputePass.js.map +1 -0
  88. package/lib/renderer/WGPUFullQuadPass.d.ts +73 -0
  89. package/lib/renderer/WGPUFullQuadPass.js +389 -0
  90. package/lib/renderer/WGPUFullQuadPass.js.map +1 -0
  91. package/lib/renderer/WGPUHDRPeakMeter.d.ts +19 -0
  92. package/lib/renderer/WGPUHDRPeakMeter.js +112 -0
  93. package/lib/renderer/WGPUHDRPeakMeter.js.map +1 -0
  94. package/lib/renderer/WGPUJNLMDenoiser.d.ts +61 -0
  95. package/lib/renderer/WGPUJNLMDenoiser.js +412 -0
  96. package/lib/renderer/WGPUJNLMDenoiser.js.map +1 -0
  97. package/lib/renderer/WGPURayTraceOutput.d.ts +44 -0
  98. package/lib/renderer/WGPURayTraceOutput.js +368 -0
  99. package/lib/renderer/WGPURayTraceOutput.js.map +1 -0
  100. package/lib/renderer/WGPURayTracePass.d.ts +284 -0
  101. package/lib/renderer/WGPURayTracePass.js +2323 -0
  102. package/lib/renderer/WGPURayTracePass.js.map +1 -0
  103. package/lib/renderer/WGPURenderingPipeline.d.ts +9 -0
  104. package/lib/renderer/WGPURenderingPipeline.js +1446 -0
  105. package/lib/renderer/WGPURenderingPipeline.js.map +1 -0
  106. package/lib/renderer/WGPUSceneFeatures.d.ts +3 -0
  107. package/lib/renderer/WGPUSceneFeatures.js +20 -0
  108. package/lib/renderer/WGPUSceneFeatures.js.map +1 -0
  109. package/lib/renderer/WGPUScratchBuffer.d.ts +9 -0
  110. package/lib/renderer/WGPUScratchBuffer.js +21 -0
  111. package/lib/renderer/WGPUScratchBuffer.js.map +1 -0
  112. package/lib/renderer/WGPUScratchTexture.d.ts +10 -0
  113. package/lib/renderer/WGPUScratchTexture.js +27 -0
  114. package/lib/renderer/WGPUScratchTexture.js.map +1 -0
  115. package/lib/renderer/WGPUShaderCache.d.ts +26 -0
  116. package/lib/renderer/WGPUShaderCache.js +103 -0
  117. package/lib/renderer/WGPUShaderCache.js.map +1 -0
  118. package/lib/renderer/WebGLBloomPass.d.ts +18 -0
  119. package/lib/renderer/WebGLBloomPass.js +113 -0
  120. package/lib/renderer/WebGLBloomPass.js.map +1 -0
  121. package/lib/renderer/WebGLFullQuadPass.d.ts +65 -0
  122. package/lib/renderer/WebGLFullQuadPass.js +208 -0
  123. package/lib/renderer/WebGLFullQuadPass.js.map +1 -0
  124. package/lib/renderer/WebGLJNLMDenoiser.d.ts +32 -0
  125. package/lib/renderer/WebGLJNLMDenoiser.js +328 -0
  126. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -0
  127. package/lib/renderer/WebGLRayTraceOutputPass.d.ts +20 -0
  128. package/lib/renderer/WebGLRayTraceOutputPass.js +106 -0
  129. package/lib/renderer/WebGLRayTraceOutputPass.js.map +1 -0
  130. package/lib/renderer/WebGLRayTracePass.d.ts +142 -0
  131. package/lib/renderer/WebGLRayTracePass.js +845 -0
  132. package/lib/renderer/WebGLRayTracePass.js.map +1 -0
  133. package/lib/renderer/WebGLReadback.d.ts +4 -0
  134. package/lib/renderer/WebGLReadback.js +75 -0
  135. package/lib/renderer/WebGLReadback.js.map +1 -0
  136. package/lib/renderer/WebGLRenderingPipeline.d.ts +4 -0
  137. package/lib/renderer/WebGLRenderingPipeline.js +510 -0
  138. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -0
  139. package/lib/renderer/WebGLScratchCopy.d.ts +10 -0
  140. package/lib/renderer/WebGLScratchCopy.js +27 -0
  141. package/lib/renderer/WebGLScratchCopy.js.map +1 -0
  142. package/lib/renderer/accumulationTarget.d.ts +26 -0
  143. package/lib/renderer/accumulationTarget.js +2 -0
  144. package/lib/renderer/accumulationTarget.js.map +1 -0
  145. package/lib/renderer/binnedSahBvh.d.ts +31 -0
  146. package/lib/renderer/binnedSahBvh.js +263 -0
  147. package/lib/renderer/binnedSahBvh.js.map +1 -0
  148. package/lib/renderer/bvh.worker.d.ts +1 -0
  149. package/lib/renderer/bvh.worker.js +16 -0
  150. package/lib/renderer/bvh.worker.js.map +1 -0
  151. package/lib/renderer/bvhAccel.d.ts +81 -0
  152. package/lib/renderer/bvhAccel.js +621 -0
  153. package/lib/renderer/bvhAccel.js.map +1 -0
  154. package/lib/renderer/bvhWorkerBuild.d.ts +12 -0
  155. package/lib/renderer/bvhWorkerBuild.js +17 -0
  156. package/lib/renderer/bvhWorkerBuild.js.map +1 -0
  157. package/lib/renderer/bvhWorkerClient.d.ts +57 -0
  158. package/lib/renderer/bvhWorkerClient.js +166 -0
  159. package/lib/renderer/bvhWorkerClient.js.map +1 -0
  160. package/lib/renderer/cameraLens.d.ts +5 -0
  161. package/lib/renderer/cameraLens.js +15 -0
  162. package/lib/renderer/cameraLens.js.map +1 -0
  163. package/lib/renderer/cameraSnapshot.d.ts +3 -0
  164. package/lib/renderer/cameraSnapshot.js +7 -0
  165. package/lib/renderer/cameraSnapshot.js.map +1 -0
  166. package/lib/renderer/committedScene.d.ts +13 -0
  167. package/lib/renderer/committedScene.js +121 -0
  168. package/lib/renderer/committedScene.js.map +1 -0
  169. package/lib/renderer/dataTextureUtil.d.ts +32 -0
  170. package/lib/renderer/dataTextureUtil.js +46 -0
  171. package/lib/renderer/dataTextureUtil.js.map +1 -0
  172. package/lib/renderer/denoiser.d.ts +65 -0
  173. package/lib/renderer/denoiser.js +2 -0
  174. package/lib/renderer/denoiser.js.map +1 -0
  175. package/lib/renderer/depthQuery.d.ts +14 -0
  176. package/lib/renderer/depthQuery.js +14 -0
  177. package/lib/renderer/depthQuery.js.map +1 -0
  178. package/lib/renderer/deviceRequirements.d.ts +23 -0
  179. package/lib/renderer/deviceRequirements.js +50 -0
  180. package/lib/renderer/deviceRequirements.js.map +1 -0
  181. package/lib/renderer/envMapCreation.d.ts +10 -0
  182. package/lib/renderer/envMapCreation.js +96 -0
  183. package/lib/renderer/envMapCreation.js.map +1 -0
  184. package/lib/renderer/envMapDistribution.d.ts +9 -0
  185. package/lib/renderer/envMapDistribution.js +107 -0
  186. package/lib/renderer/envMapDistribution.js.map +1 -0
  187. package/lib/renderer/float16.d.ts +1 -0
  188. package/lib/renderer/float16.js +19 -0
  189. package/lib/renderer/float16.js.map +1 -0
  190. package/lib/renderer/geometryBvh.d.ts +11 -0
  191. package/lib/renderer/geometryBvh.js +24 -0
  192. package/lib/renderer/geometryBvh.js.map +1 -0
  193. package/lib/renderer/geometryUpdates.d.ts +28 -0
  194. package/lib/renderer/geometryUpdates.js +131 -0
  195. package/lib/renderer/geometryUpdates.js.map +1 -0
  196. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +2 -0
  197. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +37 -0
  198. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -0
  199. package/lib/renderer/glsl/chunks/common.glsl.d.ts +2 -0
  200. package/lib/renderer/glsl/chunks/common.glsl.js +17 -0
  201. package/lib/renderer/glsl/chunks/common.glsl.js.map +1 -0
  202. package/lib/renderer/glsl/chunks/envMap.glsl.d.ts +2 -0
  203. package/lib/renderer/glsl/chunks/envMap.glsl.js +192 -0
  204. package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -0
  205. package/lib/renderer/glsl/chunks/intersect.glsl.d.ts +2 -0
  206. package/lib/renderer/glsl/chunks/intersect.glsl.js +1091 -0
  207. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -0
  208. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +2 -0
  209. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +371 -0
  210. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -0
  211. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.d.ts +2 -0
  212. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +1767 -0
  213. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js.map +1 -0
  214. package/lib/renderer/glsl/chunks/rand.glsl.d.ts +2 -0
  215. package/lib/renderer/glsl/chunks/rand.glsl.js +26 -0
  216. package/lib/renderer/glsl/chunks/rand.glsl.js.map +1 -0
  217. package/lib/renderer/glsl/chunks/random.glsl.d.ts +2 -0
  218. package/lib/renderer/glsl/chunks/random.glsl.js +27 -0
  219. package/lib/renderer/glsl/chunks/random.glsl.js.map +1 -0
  220. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +2 -0
  221. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +287 -0
  222. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -0
  223. package/lib/renderer/glsl/chunks/sampleLight.glsl.d.ts +2 -0
  224. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +254 -0
  225. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -0
  226. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +2 -0
  227. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +279 -0
  228. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -0
  229. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.d.ts +2 -0
  230. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +48 -0
  231. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js.map +1 -0
  232. package/lib/renderer/glsl/chunks/sampling.glsl.d.ts +2 -0
  233. package/lib/renderer/glsl/chunks/sampling.glsl.js +194 -0
  234. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -0
  235. package/lib/renderer/glsl/chunks/sobol.glsl.d.ts +2 -0
  236. package/lib/renderer/glsl/chunks/sobol.glsl.js +170 -0
  237. package/lib/renderer/glsl/chunks/sobol.glsl.js.map +1 -0
  238. package/lib/renderer/glsl/chunks/textureLinear.glsl.d.ts +2 -0
  239. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +27 -0
  240. package/lib/renderer/glsl/chunks/textureLinear.glsl.js.map +1 -0
  241. package/lib/renderer/glsl/chunks/uvProjection.glsl.d.ts +2 -0
  242. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +72 -0
  243. package/lib/renderer/glsl/chunks/uvProjection.glsl.js.map +1 -0
  244. package/lib/renderer/glsl/depthQuery.frag.d.ts +3 -0
  245. package/lib/renderer/glsl/depthQuery.frag.js +55 -0
  246. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -0
  247. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.d.ts +6 -0
  248. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +46 -0
  249. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js.map +1 -0
  250. package/lib/renderer/glsl/rayTrace.frag.d.ts +2 -0
  251. package/lib/renderer/glsl/rayTrace.frag.js +619 -0
  252. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -0
  253. package/lib/renderer/glsl/shadowCatcher.frag.d.ts +3 -0
  254. package/lib/renderer/glsl/shadowCatcher.frag.js +119 -0
  255. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -0
  256. package/lib/renderer/glsl/webglPipeline.frag.d.ts +4 -0
  257. package/lib/renderer/glsl/webglPipeline.frag.js +208 -0
  258. package/lib/renderer/glsl/webglPipeline.frag.js.map +1 -0
  259. package/lib/renderer/gpuMemory.d.ts +2 -0
  260. package/lib/renderer/gpuMemory.js +10 -0
  261. package/lib/renderer/gpuMemory.js.map +1 -0
  262. package/lib/renderer/halton.d.ts +1 -0
  263. package/lib/renderer/halton.js +15 -0
  264. package/lib/renderer/halton.js.map +1 -0
  265. package/lib/renderer/jnlm.d.ts +85 -0
  266. package/lib/renderer/jnlm.js +87 -0
  267. package/lib/renderer/jnlm.js.map +1 -0
  268. package/lib/renderer/lightSampling.d.ts +75 -0
  269. package/lib/renderer/lightSampling.js +305 -0
  270. package/lib/renderer/lightSampling.js.map +1 -0
  271. package/lib/renderer/lightsUtil.d.ts +26 -0
  272. package/lib/renderer/lightsUtil.js +154 -0
  273. package/lib/renderer/lightsUtil.js.map +1 -0
  274. package/lib/renderer/materialChanges.d.ts +13 -0
  275. package/lib/renderer/materialChanges.js +59 -0
  276. package/lib/renderer/materialChanges.js.map +1 -0
  277. package/lib/renderer/materialComparison.generated.d.ts +5 -0
  278. package/lib/renderer/materialComparison.generated.js +655 -0
  279. package/lib/renderer/materialComparison.generated.js.map +1 -0
  280. package/lib/renderer/materialConstants.d.ts +6 -0
  281. package/lib/renderer/materialConstants.js +3 -0
  282. package/lib/renderer/materialConstants.js.map +1 -0
  283. package/lib/renderer/materialEmission.d.ts +9 -0
  284. package/lib/renderer/materialEmission.js +21 -0
  285. package/lib/renderer/materialEmission.js.map +1 -0
  286. package/lib/renderer/materialIndices.d.ts +7 -0
  287. package/lib/renderer/materialIndices.js +34 -0
  288. package/lib/renderer/materialIndices.js.map +1 -0
  289. package/lib/renderer/materialLayout.d.ts +102 -0
  290. package/lib/renderer/materialLayout.js +189 -0
  291. package/lib/renderer/materialLayout.js.map +1 -0
  292. package/lib/renderer/materialTextureSlots.d.ts +537 -0
  293. package/lib/renderer/materialTextureSlots.js +219 -0
  294. package/lib/renderer/materialTextureSlots.js.map +1 -0
  295. package/lib/renderer/mergeMeshesToGeometry.d.ts +8 -0
  296. package/lib/renderer/mergeMeshesToGeometry.js +236 -0
  297. package/lib/renderer/mergeMeshesToGeometry.js.map +1 -0
  298. package/lib/renderer/meshInstances.d.ts +23 -0
  299. package/lib/renderer/meshInstances.js +66 -0
  300. package/lib/renderer/meshInstances.js.map +1 -0
  301. package/lib/renderer/meshMembership.d.ts +10 -0
  302. package/lib/renderer/meshMembership.js +72 -0
  303. package/lib/renderer/meshMembership.js.map +1 -0
  304. package/lib/renderer/oidnNetwork.d.ts +20 -0
  305. package/lib/renderer/oidnNetwork.js +123 -0
  306. package/lib/renderer/oidnNetwork.js.map +1 -0
  307. package/lib/renderer/openPbrEnergyLut.d.ts +32 -0
  308. package/lib/renderer/openPbrEnergyLut.js +965 -0
  309. package/lib/renderer/openPbrEnergyLut.js.map +1 -0
  310. package/lib/renderer/pathLimits.d.ts +4 -0
  311. package/lib/renderer/pathLimits.js +7 -0
  312. package/lib/renderer/pathLimits.js.map +1 -0
  313. package/lib/renderer/prepareMeshBVH.d.ts +110 -0
  314. package/lib/renderer/prepareMeshBVH.js +857 -0
  315. package/lib/renderer/prepareMeshBVH.js.map +1 -0
  316. package/lib/renderer/progressiveScheduler.d.ts +11 -0
  317. package/lib/renderer/progressiveScheduler.js +19 -0
  318. package/lib/renderer/progressiveScheduler.js.map +1 -0
  319. package/lib/renderer/renderPipeline.d.ts +57 -0
  320. package/lib/renderer/renderPipeline.js +49 -0
  321. package/lib/renderer/renderPipeline.js.map +1 -0
  322. package/lib/renderer/sampling.d.ts +10 -0
  323. package/lib/renderer/sampling.js +28 -0
  324. package/lib/renderer/sampling.js.map +1 -0
  325. package/lib/renderer/sceneChangeDetector.d.ts +29 -0
  326. package/lib/renderer/sceneChangeDetector.js +232 -0
  327. package/lib/renderer/sceneChangeDetector.js.map +1 -0
  328. package/lib/renderer/sceneLightChanges.d.ts +14 -0
  329. package/lib/renderer/sceneLightChanges.js +34 -0
  330. package/lib/renderer/sceneLightChanges.js.map +1 -0
  331. package/lib/renderer/scratchSampling.d.ts +3 -0
  332. package/lib/renderer/scratchSampling.js +39 -0
  333. package/lib/renderer/scratchSampling.js.map +1 -0
  334. package/lib/renderer/shader.d.ts +37 -0
  335. package/lib/renderer/shader.js +41 -0
  336. package/lib/renderer/shader.js.map +1 -0
  337. package/lib/renderer/staticBvhPreparation.d.ts +24 -0
  338. package/lib/renderer/staticBvhPreparation.js +116 -0
  339. package/lib/renderer/staticBvhPreparation.js.map +1 -0
  340. package/lib/renderer/textureRegion.d.ts +8 -0
  341. package/lib/renderer/textureRegion.js +9 -0
  342. package/lib/renderer/textureRegion.js.map +1 -0
  343. package/lib/renderer/texturesFromMaterials.d.ts +6 -0
  344. package/lib/renderer/texturesFromMaterials.js +37 -0
  345. package/lib/renderer/texturesFromMaterials.js.map +1 -0
  346. package/lib/renderer/util.d.ts +17 -0
  347. package/lib/renderer/util.js +56 -0
  348. package/lib/renderer/util.js.map +1 -0
  349. package/lib/renderer/webgl.d.ts +131 -0
  350. package/lib/renderer/webgl.js +586 -0
  351. package/lib/renderer/webgl.js.map +1 -0
  352. package/lib/renderer/webglShaders.d.ts +4 -0
  353. package/lib/renderer/webglShaders.js +41 -0
  354. package/lib/renderer/webglShaders.js.map +1 -0
  355. package/lib/renderer/wgsl/chunks/common.wgsl.d.ts +2 -0
  356. package/lib/renderer/wgsl/chunks/common.wgsl.js +23 -0
  357. package/lib/renderer/wgsl/chunks/common.wgsl.js.map +1 -0
  358. package/lib/renderer/wgsl/chunks/envMap.wgsl.d.ts +2 -0
  359. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +128 -0
  360. package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -0
  361. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -0
  362. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +1441 -0
  363. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -0
  364. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +8 -0
  365. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +85 -0
  366. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -0
  367. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +7 -0
  368. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +315 -0
  369. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -0
  370. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.d.ts +3 -0
  371. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +1737 -0
  372. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js.map +1 -0
  373. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +4 -0
  374. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +352 -0
  375. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -0
  376. package/lib/renderer/wgsl/chunks/rand.wgsl.d.ts +2 -0
  377. package/lib/renderer/wgsl/chunks/rand.wgsl.js +25 -0
  378. package/lib/renderer/wgsl/chunks/rand.wgsl.js.map +1 -0
  379. package/lib/renderer/wgsl/chunks/random.wgsl.d.ts +1 -0
  380. package/lib/renderer/wgsl/chunks/random.wgsl.js +2 -0
  381. package/lib/renderer/wgsl/chunks/random.wgsl.js.map +1 -0
  382. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +5 -0
  383. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +328 -0
  384. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -0
  385. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +2 -0
  386. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +400 -0
  387. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -0
  388. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +3 -0
  389. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +439 -0
  390. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -0
  391. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +2 -0
  392. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +40 -0
  393. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -0
  394. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +2 -0
  395. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +198 -0
  396. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -0
  397. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.d.ts +8 -0
  398. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js +16 -0
  399. package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js.map +1 -0
  400. package/lib/renderer/wgsl/chunks/sobol.wgsl.d.ts +2 -0
  401. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +170 -0
  402. package/lib/renderer/wgsl/chunks/sobol.wgsl.js.map +1 -0
  403. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.d.ts +2 -0
  404. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +42 -0
  405. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js.map +1 -0
  406. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +5 -0
  407. package/lib/renderer/wgsl/compose.frag.wgsl.js +218 -0
  408. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -0
  409. package/lib/renderer/wgsl/customDenoise.wgsl.d.ts +4 -0
  410. package/lib/renderer/wgsl/customDenoise.wgsl.js +28 -0
  411. package/lib/renderer/wgsl/customDenoise.wgsl.js.map +1 -0
  412. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +3 -0
  413. package/lib/renderer/wgsl/depthQuery.wgsl.js +34 -0
  414. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -0
  415. package/lib/renderer/wgsl/offlineAdaptive.compute.d.ts +2 -0
  416. package/lib/renderer/wgsl/offlineAdaptive.compute.js +108 -0
  417. package/lib/renderer/wgsl/offlineAdaptive.compute.js.map +1 -0
  418. package/lib/renderer/wgsl/rayTrace.compute.d.ts +16 -0
  419. package/lib/renderer/wgsl/rayTrace.compute.js +923 -0
  420. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -0
  421. package/lib/scene/AmbientLight.d.ts +17 -0
  422. package/lib/scene/AmbientLight.js +15 -0
  423. package/lib/scene/AmbientLight.js.map +1 -0
  424. package/lib/scene/Camera.d.ts +78 -0
  425. package/lib/scene/Camera.js +111 -0
  426. package/lib/scene/Camera.js.map +1 -0
  427. package/lib/scene/DirectionalLight.d.ts +18 -0
  428. package/lib/scene/DirectionalLight.js +17 -0
  429. package/lib/scene/DirectionalLight.js.map +1 -0
  430. package/lib/scene/EnvironmentLight.d.ts +14 -0
  431. package/lib/scene/EnvironmentLight.js +14 -0
  432. package/lib/scene/EnvironmentLight.js.map +1 -0
  433. package/lib/scene/Geometry.d.ts +87 -0
  434. package/lib/scene/Geometry.js +108 -0
  435. package/lib/scene/Geometry.js.map +1 -0
  436. package/lib/scene/IESProfile.d.ts +19 -0
  437. package/lib/scene/IESProfile.js +165 -0
  438. package/lib/scene/IESProfile.js.map +1 -0
  439. package/lib/scene/InstancedMesh.d.ts +43 -0
  440. package/lib/scene/InstancedMesh.js +79 -0
  441. package/lib/scene/InstancedMesh.js.map +1 -0
  442. package/lib/scene/Mesh.d.ts +14 -0
  443. package/lib/scene/Mesh.js +17 -0
  444. package/lib/scene/Mesh.js.map +1 -0
  445. package/lib/scene/OpenPBRMaterial.d.ts +224 -0
  446. package/lib/scene/OpenPBRMaterial.js +240 -0
  447. package/lib/scene/OpenPBRMaterial.js.map +1 -0
  448. package/lib/scene/RectLight.d.ts +31 -0
  449. package/lib/scene/RectLight.js +26 -0
  450. package/lib/scene/RectLight.js.map +1 -0
  451. package/lib/scene/Scene.d.ts +55 -0
  452. package/lib/scene/Scene.js +2 -0
  453. package/lib/scene/Scene.js.map +1 -0
  454. package/lib/scene/SceneNode.d.ts +16 -0
  455. package/lib/scene/SceneNode.js +33 -0
  456. package/lib/scene/SceneNode.js.map +1 -0
  457. package/lib/scene/SphereLight.d.ts +22 -0
  458. package/lib/scene/SphereLight.js +20 -0
  459. package/lib/scene/SphereLight.js.map +1 -0
  460. package/lib/scene/SpotLight.d.ts +35 -0
  461. package/lib/scene/SpotLight.js +29 -0
  462. package/lib/scene/SpotLight.js.map +1 -0
  463. package/lib/scene/Texture.d.ts +38 -0
  464. package/lib/scene/Texture.js +37 -0
  465. package/lib/scene/Texture.js.map +1 -0
  466. package/lib/scene/anisotropyParameters.d.ts +1 -0
  467. package/lib/scene/anisotropyParameters.js +13 -0
  468. package/lib/scene/anisotropyParameters.js.map +1 -0
  469. package/lib/scene/geometryChanges.d.ts +5 -0
  470. package/lib/scene/geometryChanges.js +18 -0
  471. package/lib/scene/geometryChanges.js.map +1 -0
  472. package/lib/scene/materialTextureNames.d.ts +2 -0
  473. package/lib/scene/materialTextureNames.js +9 -0
  474. package/lib/scene/materialTextureNames.js.map +1 -0
  475. package/lib/scene/openPbrSchema.d.ts +400 -0
  476. package/lib/scene/openPbrSchema.js +149 -0
  477. package/lib/scene/openPbrSchema.js.map +1 -0
  478. package/lib/scene/thinFilmParameters.d.ts +4 -0
  479. package/lib/scene/thinFilmParameters.js +12 -0
  480. package/lib/scene/thinFilmParameters.js.map +1 -0
  481. package/lib/settings/display.d.ts +118 -0
  482. package/lib/settings/display.js +34 -0
  483. package/lib/settings/display.js.map +1 -0
  484. package/lib/settings/engine.d.ts +37 -0
  485. package/lib/settings/engine.js +26 -0
  486. package/lib/settings/engine.js.map +1 -0
  487. package/lib/settings/render.d.ts +178 -0
  488. package/lib/settings/render.js +36 -0
  489. package/lib/settings/render.js.map +1 -0
  490. package/lib/tinybvh/LICENSE +21 -0
  491. package/lib/tinybvh/TinyBvhBuilder.d.ts +17 -0
  492. package/lib/tinybvh/TinyBvhBuilder.js +211 -0
  493. package/lib/tinybvh/TinyBvhBuilder.js.map +1 -0
  494. package/lib/tinybvh/tinybvh.wasm +0 -0
  495. package/lib/types.d.ts +13 -0
  496. package/lib/types.js +2 -0
  497. package/lib/types.js.map +1 -0
  498. package/lib/util/canvas.d.ts +2 -0
  499. package/lib/util/canvas.js +13 -0
  500. package/lib/util/canvas.js.map +1 -0
  501. package/lib/util/commonGeometry.d.ts +15 -0
  502. package/lib/util/commonGeometry.js +240 -0
  503. package/lib/util/commonGeometry.js.map +1 -0
  504. package/lib/util/events.d.ts +7 -0
  505. package/lib/util/events.js +23 -0
  506. package/lib/util/events.js.map +1 -0
  507. package/lib/util/frame.d.ts +2 -0
  508. package/lib/util/frame.js +10 -0
  509. package/lib/util/frame.js.map +1 -0
  510. package/lib/util/fromTHREE.d.ts +100 -0
  511. package/lib/util/fromTHREE.js +1063 -0
  512. package/lib/util/fromTHREE.js.map +1 -0
  513. package/lib/util/geometry.d.ts +13 -0
  514. package/lib/util/geometry.js +292 -0
  515. package/lib/util/geometry.js.map +1 -0
  516. package/lib/util/loadRGBE.d.ts +7 -0
  517. package/lib/util/loadRGBE.js +168 -0
  518. package/lib/util/loadRGBE.js.map +1 -0
  519. package/lib/util/math.d.ts +8 -0
  520. package/lib/util/math.js +64 -0
  521. package/lib/util/math.js.map +1 -0
  522. package/lib/util/mesh.d.ts +7 -0
  523. package/lib/util/mesh.js +20 -0
  524. package/lib/util/mesh.js.map +1 -0
  525. package/lib/util/threeDeformation.d.ts +20 -0
  526. package/lib/util/threeDeformation.js +218 -0
  527. package/lib/util/threeDeformation.js.map +1 -0
  528. package/lib/version.d.ts +2 -0
  529. package/lib/version.js +3 -0
  530. package/lib/version.js.map +1 -0
  531. package/llms.txt +62 -0
  532. package/oidn/weights/LICENSE.txt +202 -0
  533. package/oidn/weights/README.md +43 -0
  534. package/oidn/weights/rt_hdr_alb_nrm.tza +0 -0
  535. package/oidn/weights/rt_hdr_alb_nrm_small.tza +0 -0
  536. package/package.json +124 -4
@@ -0,0 +1,1446 @@
1
+ import { DisplayPostProcess } from './DisplayPostProcess.js';
2
+ import { customDenoiseLayersMain, customDenoiseCoverageMain } from './wgsl/customDenoise.wgsl.js';
3
+ import { WGPUCanvasSurface } from './WGPUCanvasSurface.js';
4
+ import { WGPUScratchBuffer } from './WGPUScratchBuffer.js';
5
+ import { WGPUScratchTexture } from './WGPUScratchTexture.js';
6
+ import { ScratchPool } from './ScratchPool.js';
7
+ import { makeRenderPipeline } from './renderPipeline.js';
8
+ import { textureBytes } from './gpuMemory.js';
9
+ import { WGPUFullQuadPass } from './WGPUFullQuadPass.js';
10
+ import WGPURayTracePass from './WGPURayTracePass.js';
11
+ import { composeMain, composeDefine, compositeDenoiseOutputMain, compositeDenoiseDisplayMain, linearToSrgbDefine } from './wgsl/compose.frag.wgsl.js';
12
+ import halton from './halton.js';
13
+ import { makeTileRender, makeTileRenderWithPredict } from './TileRender.js';
14
+ import { WGPURayTraceOutputPass } from './WGPURayTraceOutput.js';
15
+ import { vec3 } from '../math.js';
16
+ import WGPUBloomPass from './WGPUBloomPass.js';
17
+ import { WGPUComputePass } from './WGPUComputePass.js';
18
+ import WGPUHDRPeakMeter from './WGPUHDRPeakMeter.js';
19
+ import { WGPUJNLMDenoisePass } from './WGPUJNLMDenoiser.js';
20
+ export const oidnAlbedoSrgbEncodeMain = /* wgsl */ `
21
+ if (globalId.x >= u32(imageSize.x) || globalId.y >= u32(imageSize.y)) { return; }
22
+ let index = globalId.x + globalId.y * u32(imageSize.x);
23
+ let raw = textureLoad(in_albedo, vec2u(globalId.xy), 0);
24
+ let linear = clamp(raw.rgb, vec3f(0.0), vec3f(1.0));
25
+ let encoded = select(
26
+ 1.055 * pow(linear, vec3f(1.0 / 2.4)) - 0.055,
27
+ 12.92 * linear,
28
+ linear <= vec3f(0.0031308)
29
+ );
30
+ out_albedo[index] = vec4f(encoded, raw.a);
31
+ `;
32
+ export const oidnAlbedoSrgbDecodeMain = /* wgsl */ `
33
+ if (globalId.x >= u32(imageSize.x) || globalId.y >= u32(imageSize.y)) { return; }
34
+ let index = globalId.x + globalId.y * u32(imageSize.x);
35
+ let filtered = in_albedo[index];
36
+ let encoded = clamp(filtered.rgb, vec3f(0.0), vec3f(1.0));
37
+ let linear = select(
38
+ pow((encoded + 0.055) / 1.055, vec3f(2.4)),
39
+ encoded / 12.92,
40
+ encoded <= vec3f(0.04045)
41
+ );
42
+ var result = linear;
43
+ let ja = textureLoad(in_rawAlbedo, vec2i(globalId.xy), 0).a;
44
+ if (preserveSurfaceTexture != 0u) {
45
+
46
+
47
+
48
+ let fM = vec2i(globalId.xy);
49
+ let center = textureLoad(in_rawNormal, fM, 0);
50
+ let n = normalize(center.rgb * 2.0 - 1.0 + vec3f(1e-8));
51
+ var mP = 0.0;
52
+ for (var y = -1; y <= 1; y++) {
53
+ for (var x = -1; x <= 1; x++) {
54
+ let tJ = textureLoad(in_rawNormal,
55
+ clamp(fM + vec2i(x, y), vec2i(0), imageSize - 1), 0);
56
+ let nn = normalize(tJ.rgb * 2.0 - 1.0 + vec3f(1e-8));
57
+ mP = max(mP,
58
+ max(1.0 - dot(n, nn), abs(center.a - tJ.a)));
59
+ }
60
+ }
61
+ let ua = smoothstep(0.02, 0.1, mP);
62
+ let raw = clamp(textureLoad(in_rawAlbedo, fM, 0).rgb,
63
+ vec3f(0.0), vec3f(1.0));
64
+ result = mix(raw, linear, ua);
65
+ }
66
+ out_albedo[index] = vec4f(result, ja);
67
+ `;
68
+ export const oidnNormalSanitizeMain = /* wgsl */ `
69
+ if (globalId.x >= u32(imageSize.x) || globalId.y >= u32(imageSize.y)) { return; }
70
+ let index = globalId.x + globalId.y * u32(imageSize.x);
71
+ out_normal[index] = vec4f(clamp(in_normal[index].rgb, vec3f(0.0), vec3f(1.0)), 1.0);
72
+ `;
73
+ function createTimeQuery(device) {
74
+ try {
75
+ const querySet = device.createQuerySet({
76
+ type: 'timestamp',
77
+ count: 2
78
+ });
79
+ const resolveBuffer = device.createBuffer({
80
+ size: querySet.count * 8,
81
+ usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC
82
+ });
83
+ const resultBuffer = device.createBuffer({
84
+ size: resolveBuffer.size,
85
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
86
+ });
87
+ return {
88
+ querySet,
89
+ resolveBuffer,
90
+ resultBuffer
91
+ };
92
+ }
93
+ catch (e) {
94
+ console.error(e);
95
+ return undefined;
96
+ }
97
+ }
98
+ function makeWGPUAccumulationTarget(canvas, device, rayTracePass, surface, scratch, customJobs) {
99
+ let sampleRenderedCallback = () => { };
100
+ let allowMultipleSamplesPerFrame = false;
101
+ let offlineSamplesPerBatch = 0;
102
+ let deferPostProcess = false;
103
+ let sampleSequenceOffset = 0;
104
+ let sampleCount = 0;
105
+ let elapsedFrameTime = 32;
106
+ let elapsedTileTime;
107
+ let tileTimingGeneration = 0;
108
+ let lastGpuTraceTiming;
109
+ let diagnosticsReadbackPromise = Promise.resolve();
110
+ let isFirstFrame = true;
111
+ let disposed = false;
112
+ let needsUpdatePredicting = true;
113
+ let isPredictingFrame = false;
114
+ let renderWidth = 0;
115
+ let renderHeight = 0;
116
+ let screenWidth = 0;
117
+ let screenHeight = 0;
118
+ const targetKey = {};
119
+ let execution;
120
+ let resolvedSamples = 0;
121
+ let resolvedHasShadowCatcher = false;
122
+ let pendingFlatBackground = [0, 0, 0, 0];
123
+ let resolvedFlatBackground = [0, 0, 0, 0];
124
+ let presentationTexture;
125
+ let presentationDenoised = false;
126
+ const displayPostProcess = new DisplayPostProcess(device);
127
+ let denoiseValid = false;
128
+ let maxTimePerTile = 21;
129
+ let bloomIntensity = 0;
130
+ let bloomThreshold = 1;
131
+ let bloomRadius = 0.6;
132
+ let exposure = 1;
133
+ let toneMappingMode = 0;
134
+ let contrast = 1;
135
+ let saturation = 1;
136
+ let temperature = 0;
137
+ let tint = 0;
138
+ let vignette = 0;
139
+ let hdrOutputRequested = false;
140
+ let hdrOutputActive = false;
141
+ let output = 'final';
142
+ let shadowCatcherColor = vec3.create();
143
+ let shadowCatcherAlpha = 1;
144
+ let pageLeaveTime = performance.now();
145
+ let pageInvisibleTime = 0;
146
+ let denoisedBuffer;
147
+ let denoisedTexture;
148
+ let denoisedDisplayLinear = false;
149
+ let denoisedHDR = false;
150
+ let activeCustomController;
151
+ let jnlmPass;
152
+ let denoiseGeneration = 0;
153
+ let preserveDenoisedOutput = false;
154
+ let activeOidnPhase;
155
+ function cancelDenoise() {
156
+ activeCustomController?.abort();
157
+ activeCustomController = undefined;
158
+ denoiseGeneration++;
159
+ const activePhase = activeOidnPhase;
160
+ activeOidnPhase = undefined;
161
+ if (activePhase) {
162
+ try {
163
+ activePhase.abort();
164
+ }
165
+ finally {
166
+ activePhase.finish();
167
+ }
168
+ }
169
+ }
170
+ function runOIDNPhase(generation, execute) {
171
+ if (generation !== denoiseGeneration)
172
+ return Promise.resolve();
173
+ return new Promise((resolve, reject) => {
174
+ let settled = false;
175
+ let phase;
176
+ const finish = () => {
177
+ if (settled)
178
+ return;
179
+ settled = true;
180
+ if (activeOidnPhase === phase)
181
+ activeOidnPhase = undefined;
182
+ resolve();
183
+ };
184
+ const fail = (reason) => {
185
+ if (settled)
186
+ return;
187
+ settled = true;
188
+ if (activeOidnPhase === phase)
189
+ activeOidnPhase = undefined;
190
+ reject(reason);
191
+ };
192
+ try {
193
+ const abort = execute(finish, fail);
194
+ phase = { abort, finish };
195
+ if (settled)
196
+ return;
197
+ if (generation !== denoiseGeneration) {
198
+ abort();
199
+ finish();
200
+ return;
201
+ }
202
+ activeOidnPhase = phase;
203
+ }
204
+ catch (error) {
205
+ if (activeOidnPhase === phase)
206
+ activeOidnPhase = undefined;
207
+ settled = true;
208
+ reject(error);
209
+ }
210
+ });
211
+ }
212
+ const ctx = surface.context;
213
+ const timeQuery = device.features.has('timestamp-query')
214
+ ? scratch.get('trace timing', () => {
215
+ const query = createTimeQuery(device);
216
+ return {
217
+ query, getMemoryBytes: () => query ? query.resolveBuffer.size + query.resultBuffer.size : 0,
218
+ dispose() { query?.querySet.destroy(); query?.resolveBuffer.destroy(); query?.resultBuffer.destroy(); }
219
+ };
220
+ }).query
221
+ : undefined;
222
+ const tileRender = makeTileRender();
223
+ const tileRenderWithPredict = timeQuery ? makeTileRenderWithPredict() : undefined;
224
+ tileRender.setDesiredMsPerTile(maxTimePerTile);
225
+ tileRenderWithPredict?.setDesiredMsPerTile(maxTimePerTile);
226
+ function invalidateTilePrediction() {
227
+ tileTimingGeneration++;
228
+ tileRenderWithPredict?.needsUpdatePredicting();
229
+ lastGpuTraceTiming = undefined;
230
+ // A changed render configuration needs fresh timing. Refresh
231
+ // its estimator without submitting a hidden probe in that mode.
232
+ needsUpdatePredicting = offlineSamplesPerBatch === 0;
233
+ isPredictingFrame = false;
234
+ }
235
+ const zero = scratch.get('empty texture', () => {
236
+ const texture = device.createTexture({ label: 'Shared empty image', size: { width: 1, height: 1 }, format: 'rgba8unorm', usage: GPUTextureUsage.TEXTURE_BINDING });
237
+ return { texture, getMemoryBytes: () => 4, dispose: () => texture.destroy() };
238
+ }).texture;
239
+ const rayTraceOutputPass = new WGPURayTraceOutputPass(device, zero);
240
+ const getFrameInput = (label) => rayTraceOutputPass.getOutput(label);
241
+ let bloomPass;
242
+ // Primary misses on a flat colour background are blank: they add the
243
+ // background radiance but no surface hit, and the normalized colour divides
244
+ // by hits only. Raw readback restores the per-sample average, as on WebGL.
245
+ // Fully blank pixels are already divided by the sample count.
246
+ const rawOutputPass = scratch.get('rawOutputPass', () => new WGPUFullQuadPass('raw output', device, {
247
+ textureWillBeRead: true,
248
+ inputs: ['colorTex', 'coverageTex'],
249
+ outputs: ['color'],
250
+ uniforms: [{ label: 'flatBackgroundColor', type: 'vec4f', data: [0, 0, 0, 0] }],
251
+ fsMain: /* wgsl */ `
252
+ var color = load_colorTex(uv, 0);
253
+ let coverage = clamp(load_coverageTex(uv, 0).a, 0.0, 1.0);
254
+ if (flatBackgroundColor.a > 0.5 && coverage > 0.0) {
255
+ color = vec4f(color.rgb * coverage, color.a);
256
+ }
257
+ output.color = color;
258
+ `
259
+ }));
260
+ const readbackPass = scratch.get('readbackPass', () => new WGPUFullQuadPass('output readback', device, {
261
+ textureWillBeRead: true, inputs: ['inputTex'], outputs: ['color'],
262
+ fsMain: 'output.color = load_inputTex(uv, 0);'
263
+ }));
264
+ readbackPass.setOutputParams({ color: { format: 'rgba32float' } });
265
+ let hdrPeakMeter;
266
+ const composePass = scratch.get('composePass', () => new WGPUFullQuadPass('compose', device, {
267
+ textureWillBeRead: true,
268
+ fsMain: composeMain + compositeDenoiseOutputMain,
269
+ fsDefine: composeDefine,
270
+ uniforms: [
271
+ {
272
+ label: 'exposure',
273
+ type: 'f32',
274
+ data: exposure
275
+ },
276
+ {
277
+ label: 'toneMappingParams',
278
+ type: 'vec2f',
279
+ data: [toneMappingMode, vignette]
280
+ },
281
+ {
282
+ label: 'hdrOutputEnabled',
283
+ type: 'f32',
284
+ data: 0
285
+ },
286
+ {
287
+ label: 'colorGradingParams',
288
+ type: 'vec4f',
289
+ data: [contrast, saturation, temperature, tint]
290
+ },
291
+ {
292
+ label: 'flatBackgroundColor',
293
+ type: 'vec4f',
294
+ data: [0, 0, 0, 0]
295
+ },
296
+ {
297
+ label: 'shadowCatcherColor',
298
+ type: 'vec3f',
299
+ data: shadowCatcherColor
300
+ },
301
+ {
302
+ label: 'shadowCatcherAlpha',
303
+ type: 'f32',
304
+ data: shadowCatcherAlpha
305
+ },
306
+ {
307
+ label: 'shadowCatcherEnabled',
308
+ type: 'f32',
309
+ data: 0
310
+ },
311
+ {
312
+ label: 'bloomIntensity',
313
+ type: 'f32',
314
+ data: 0.25
315
+ }
316
+ ],
317
+ outputs: ['color', 'denoiseColor'],
318
+ inputs: [
319
+ 'colorTex',
320
+ 'foregroundTex',
321
+ 'catcherBackgroundTex',
322
+ 'flatBackgroundMaskTex',
323
+ 'shadowCatcherTex',
324
+ 'bloomTex'
325
+ ]
326
+ }));
327
+ let composeColorFormat = 'rgba8unorm';
328
+ composePass.setOutputParams({
329
+ color: {
330
+ format: composeColorFormat
331
+ },
332
+ denoiseColor: { format: 'rgba32float' }
333
+ });
334
+ const compositeDenoiseDisplayPass = scratch.get('compositeDenoiseDisplayPass', () => new WGPUFullQuadPass('OIDN composed display', device, {
335
+ textureWillBeRead: true,
336
+ fsMain: compositeDenoiseDisplayMain,
337
+ fsDefine: linearToSrgbDefine,
338
+ uniforms: [
339
+ { label: 'hdrOutputEnabled', type: 'f32', data: 0 },
340
+ { label: 'denoisedAlpha', type: 'f32', data: 0 }
341
+ ],
342
+ outputs: ['color'], inputs: ['colorTex', 'composedTex']
343
+ }));
344
+ compositeDenoiseDisplayPass.setOutputParams({ color: { format: 'rgba8unorm' } });
345
+ const auxiliaryPassUniforms = [
346
+ {
347
+ label: 'imageSize',
348
+ type: 'vec2i',
349
+ data: new Int32Array([1, 1])
350
+ }
351
+ ];
352
+ const oidnAlbedoEncodePass = scratch.get('oidnAlbedoEncodePass', () => new WGPUComputePass('OIDN albedo sRGB encode', device, {
353
+ inputs: ['albedo'],
354
+ outputs: ['albedo'],
355
+ uniforms: auxiliaryPassUniforms,
356
+ csMain: oidnAlbedoSrgbEncodeMain
357
+ }));
358
+ const oidnAlbedoDecodePass = scratch.get('oidnAlbedoDecodePass', () => new WGPUComputePass('OIDN albedo sRGB decode', device, {
359
+ inputs: ['albedo', 'rawAlbedo', 'rawNormal'],
360
+ outputs: ['albedo'],
361
+ uniforms: [...auxiliaryPassUniforms, {
362
+ label: 'preserveSurfaceTexture', type: 'u32', data: 0
363
+ }],
364
+ csMain: oidnAlbedoSrgbDecodeMain
365
+ }));
366
+ const oidnNormalSanitizePass = scratch.get('oidnNormalSanitizePass', () => new WGPUComputePass('OIDN normal sanitize', device, {
367
+ inputs: ['normal'],
368
+ outputs: ['normal'],
369
+ uniforms: auxiliaryPassUniforms,
370
+ csMain: oidnNormalSanitizeMain
371
+ }));
372
+ oidnAlbedoEncodePass.setOutputParams({ albedo: { channels: 4 } });
373
+ oidnAlbedoDecodePass.setOutputParams({ albedo: { channels: 4 } });
374
+ oidnNormalSanitizePass.setOutputParams({ normal: { channels: 4 } });
375
+ const outputPass = scratch.get('outputPass', () => new WGPUFullQuadPass('output', device, {
376
+ fsMain: /* wgsl */ `
377
+ let vy = min(
378
+ vec2i(uv2 * vec2f(colorTexRegion())),
379
+ vec2i(colorTexRegion()) - vec2i(1)
380
+ );
381
+ var color = load_colorTex(vy, 0);
382
+ color = vec4(color.rgb * color.a, color.a);
383
+ output.color = color;
384
+ `,
385
+ outputs: ['color'],
386
+ inputs: ['colorTex']
387
+ }));
388
+ outputPass.setOutputParams({
389
+ color: {
390
+ format: 'rgba8unorm'
391
+ }
392
+ });
393
+ function _present(encoder, texture) {
394
+ outputPass.setRenderToScreen(ctx.getCurrentTexture(), surface.format);
395
+ outputPass.createPass(encoder, {
396
+ colorTex: texture
397
+ });
398
+ }
399
+ function _updateSeed(width, height) {
400
+ const sequenceSample = sampleCount + sampleSequenceOffset;
401
+ // Each ray includes a .5 pixel center, so its Halton jitter in [-.5,.5)
402
+ // becomes a sub-pixel offset in [0,1).
403
+ const jitterX = halton(sequenceSample, 2) - 0.5;
404
+ const jitterY = halton(sequenceSample, 3) - 0.5;
405
+ rayTracePass.setJitter(jitterX / width, jitterY / height);
406
+ }
407
+ function sample(clear, tiles) {
408
+ const encoder = device.createCommandEncoder();
409
+ rayTracePass.setSampleCount(sampleCount + sampleSequenceOffset);
410
+ rayTracePass.setSampleSequenceOffset(sampleSequenceOffset);
411
+ _updateSeed(renderWidth, renderHeight);
412
+ const activeTimeQuery = timeQuery;
413
+ const isUnmapped = activeTimeQuery?.resultBuffer.mapState === 'unmapped';
414
+ rayTracePass.createPass(encoder, clear, tiles, activeTimeQuery ? { querySet: activeTimeQuery.querySet } : undefined);
415
+ if (activeTimeQuery) {
416
+ encoder.resolveQuerySet(activeTimeQuery.querySet, 0, activeTimeQuery.querySet.count, activeTimeQuery.resolveBuffer, 0);
417
+ if (isUnmapped) {
418
+ encoder.copyBufferToBuffer(activeTimeQuery.resolveBuffer, 0, activeTimeQuery.resultBuffer, 0, activeTimeQuery.resultBuffer.size);
419
+ }
420
+ }
421
+ // TODO if encoder is not submitted. The uniform buffer will be replaced
422
+ device.queue.submit([encoder.finish()]);
423
+ const submittedTileRenderWithPredict = tileRenderWithPredict;
424
+ const submittedTimingGeneration = tileTimingGeneration;
425
+ if (activeTimeQuery && isUnmapped && submittedTileRenderWithPredict) {
426
+ const submittedTileCount = tiles[0] * tiles[1];
427
+ diagnosticsReadbackPromise = activeTimeQuery.resultBuffer
428
+ .mapAsync(GPUMapMode.READ).then(() => {
429
+ const mappedRange = activeTimeQuery.resultBuffer.getMappedRange();
430
+ const times = new BigInt64Array(mappedRange, 0, 2);
431
+ const traceTime = Math.max(Number(times[1] - times[0]) / 1_000_000, 0.1);
432
+ // A readback may finish after scene preparation or a mode switch.
433
+ // It must not overwrite throughput learned for the current scene.
434
+ const timingIsCurrent = !disposed && submittedTimingGeneration === tileTimingGeneration &&
435
+ !isFirstFrame;
436
+ if (timingIsCurrent) {
437
+ lastGpuTraceTiming = {
438
+ tileTimeMs: traceTime,
439
+ tileTimePerSampleMs: traceTime,
440
+ estimatedFullFrameTimeMs: traceTime * submittedTileCount,
441
+ tileCount: submittedTileCount
442
+ };
443
+ submittedTileRenderWithPredict.setLastTraceTime(traceTime);
444
+ }
445
+ activeTimeQuery.resultBuffer.unmap();
446
+ }).catch(() => {
447
+ if (activeTimeQuery.resultBuffer.mapState === 'mapped') {
448
+ activeTimeQuery.resultBuffer.unmap();
449
+ }
450
+ });
451
+ }
452
+ }
453
+ function clearDenoisedOutput() { denoiseValid = false; }
454
+ function invalidateCompositeDenoise() {
455
+ if (!denoisedDisplayLinear)
456
+ return;
457
+ cancelDenoise();
458
+ denoiseValid = false;
459
+ }
460
+ function prepareDenoisedTexture(encoder, buffer, texture) {
461
+ if (!buffer) {
462
+ return undefined;
463
+ }
464
+ const conversion = scratch.get('denoised conversion', () => new WGPUScratchTexture(device));
465
+ texture = conversion.get(renderWidth, renderHeight);
466
+ conversion.setRegion(renderWidth, renderHeight);
467
+ encoder.copyBufferToTexture({ buffer: buffer, bytesPerRow: 16 * renderWidth }, {
468
+ texture: texture
469
+ }, {
470
+ width: renderWidth,
471
+ height: renderHeight,
472
+ depthOrArrayLayers: 1
473
+ });
474
+ return texture;
475
+ }
476
+ function copyDenoisedOutput(encoder, output, target) {
477
+ const byteLength = output.width * output.height * 16;
478
+ if (!target || target.size !== byteLength) {
479
+ target?.destroy();
480
+ target = device.createBuffer({
481
+ label: 'Persistent denoised output',
482
+ size: byteLength,
483
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
484
+ });
485
+ }
486
+ encoder.copyBufferToBuffer(output.data, 0, target, 0, byteLength);
487
+ return target;
488
+ }
489
+ function prepareComposition() {
490
+ composePass.setSize(renderWidth, renderHeight);
491
+ composePass.setOutputParams({ color: { format: composeColorFormat }, denoiseColor: { format: 'rgba32float' } });
492
+ composePass.setUniform('exposure', exposure);
493
+ composePass.setUniform('toneMappingParams', [toneMappingMode, vignette]);
494
+ composePass.setUniform('colorGradingParams', [contrast, saturation, temperature, tint]);
495
+ composePass.setUniform('hdrOutputEnabled', +hdrOutputActive);
496
+ composePass.setUniform('shadowCatcherColor', shadowCatcherColor);
497
+ composePass.setUniform('shadowCatcherAlpha', shadowCatcherAlpha);
498
+ compositeDenoiseDisplayPass.setSize(renderWidth, renderHeight);
499
+ compositeDenoiseDisplayPass.setOutputParams({ color: { format: composeColorFormat } });
500
+ compositeDenoiseDisplayPass.setUniform('hdrOutputEnabled', +hdrOutputActive);
501
+ compositeDenoiseDisplayPass.setUniform('denoisedAlpha', denoiseValid && denoisedDisplayLinear ? +rayTracePass.needsDenoiseAlpha() : 0);
502
+ }
503
+ function snapshotInput(encoder, key, texture, exact = false) {
504
+ const buffer = scratch.get(key, () => new WGPUScratchBuffer(device)).get(renderWidth * renderHeight * 16, exact);
505
+ encoder.copyTextureToBuffer({ texture }, { buffer, bytesPerRow: renderWidth * 16 }, { width: renderWidth, height: renderHeight });
506
+ return buffer;
507
+ }
508
+ function copyGuide(key, input, exact = false) {
509
+ const pass = scratch.get(key, () => new WGPUComputePass(`${key} copy`, device, {
510
+ inputs: ['guide'], outputs: ['color'], uniforms: auxiliaryPassUniforms,
511
+ csMain: 'if (globalId.x >= u32(imageSize.x) || globalId.y >= u32(imageSize.y)) {return;} out_color[globalId.x + globalId.y * u32(imageSize.x)] = textureLoad(in_guide, vec2i(globalId.xy), 0);'
512
+ }));
513
+ pass.setOutputParams({ color: { channels: 4 } });
514
+ pass.setSize(renderWidth, renderHeight);
515
+ pass.setUniform('imageSize', [renderWidth, renderHeight]);
516
+ const output = exact ? scratch.get(`${key} buffer`, () => new WGPUScratchBuffer(device)).get(renderWidth * renderHeight * 16, true) : undefined;
517
+ const encoder = device.createCommandEncoder();
518
+ pass.createPass(encoder, { guide: { texture: input, channels: 4 } }, output ? { color: output } : undefined);
519
+ device.queue.submit([encoder.finish()]);
520
+ return output ?? pass.getOutput('color');
521
+ }
522
+ function drawPostProcess(encoder, _forceNeedsDenoise, resolve = false) {
523
+ displayPostProcess.reset();
524
+ if (resolve) {
525
+ resolvedSamples = sampleCount;
526
+ resolvedHasShadowCatcher = rayTracePass.hasShadowCatcher();
527
+ resolvedFlatBackground = pendingFlatBackground.slice();
528
+ }
529
+ composePass.setUniform('flatBackgroundColor', resolvedFlatBackground);
530
+ rawOutputPass.setUniform('flatBackgroundColor', resolvedFlatBackground);
531
+ if (resolvedSamples < 1)
532
+ return;
533
+ prepareComposition();
534
+ if (denoiseValid)
535
+ denoisedTexture = prepareDenoisedTexture(encoder, denoisedBuffer, denoisedTexture);
536
+ if (resolve) {
537
+ rayTraceOutputPass.setSampleCount(sampleCount);
538
+ rayTraceOutputPass.createPass(encoder, rayTracePass.getOutputBuffer(), resolvedHasShadowCatcher);
539
+ }
540
+ composePass.setUniform('shadowCatcherEnabled', +resolvedHasShadowCatcher);
541
+ let foregroundTexture = getFrameInput(resolvedHasShadowCatcher ? 'foreground' : 'color');
542
+ const colorTexture = denoiseValid && denoisedHDR && denoisedTexture ? denoisedTexture : getFrameInput('color');
543
+ let catcherBackgroundTexture = getFrameInput('catcherBackground');
544
+ if (denoiseValid && denoisedHDR && denoisedTexture && resolvedHasShadowCatcher) {
545
+ const layers = scratch.get('custom HDR layers', () => new WGPUFullQuadPass('Custom HDR layers', device, {
546
+ textureWillBeRead: true,
547
+ inputs: ['originalTex', 'filteredTex', 'foregroundTex', 'backgroundTex'],
548
+ outputs: ['foreground', 'background'], fsMain: customDenoiseLayersMain
549
+ }));
550
+ layers.setOutputParams({ foreground: { format: 'rgba32float' }, background: { format: 'rgba32float' } });
551
+ layers.setSize(renderWidth, renderHeight);
552
+ layers.createPass(encoder, { originalTex: getFrameInput('color'), filteredTex: denoisedTexture,
553
+ foregroundTex: foregroundTexture, backgroundTex: catcherBackgroundTexture });
554
+ foregroundTexture = layers.getOutput('foreground');
555
+ catcherBackgroundTexture = layers.getOutput('background');
556
+ }
557
+ const bloomEnabled = bloomIntensity > 0.0;
558
+ // Progressive accumulation refreshes bloom every ten samples.
559
+ const samplesSkipped = sampleCount < 10 || sampleCount % 10 > 0;
560
+ const bloomState = scratch.get('bloom state', () => ({ owner: undefined, settings: '', dispose() { } }));
561
+ const bloomSettings = `${bloomThreshold}/${bloomRadius}/${+(denoiseValid && denoisedHDR)}`;
562
+ const needsUpdateBloom = bloomEnabled && sampleCount >= 10 && (!samplesSkipped || bloomState.owner !== targetKey || bloomState.settings !== bloomSettings);
563
+ let bloomIntensityToUpdate = bloomIntensity;
564
+ if (needsUpdateBloom) {
565
+ if (!bloomPass) {
566
+ bloomPass = scratch.get('bloom', () => new WGPUBloomPass(device));
567
+ bloomPass.setSize(renderWidth, renderHeight);
568
+ }
569
+ bloomPass.setSize(renderWidth, renderHeight);
570
+ bloomState.owner = targetKey;
571
+ bloomState.settings = bloomSettings;
572
+ bloomPass.setBrightThreshold(bloomThreshold);
573
+ bloomPass.setRadius(bloomRadius);
574
+ bloomPass.createPass(encoder, colorTexture);
575
+ }
576
+ else if (bloomEnabled && sampleCount < 10) {
577
+ bloomIntensityToUpdate = 0;
578
+ }
579
+ composePass.setUniform('bloomIntensity', bloomIntensityToUpdate);
580
+ composePass.createPass(encoder, {
581
+ colorTex: colorTexture,
582
+ foregroundTex: foregroundTexture,
583
+ catcherBackgroundTex: catcherBackgroundTexture,
584
+ flatBackgroundMaskTex: getFrameInput('albedo'),
585
+ shadowCatcherTex: getFrameInput('shadowCatcher'),
586
+ // Use color texture as placeholder
587
+ bloomTex: bloomEnabled && bloomPass ? bloomPass.getOutput() : colorTexture
588
+ });
589
+ if (denoiseValid && !denoisedHDR && denoisedDisplayLinear && denoisedTexture) {
590
+ compositeDenoiseDisplayPass.createPass(encoder, {
591
+ colorTex: denoisedTexture,
592
+ composedTex: composePass.getOutput('denoiseColor')
593
+ });
594
+ }
595
+ let displayTexture = (denoiseValid && denoisedBuffer ? (denoisedHDR ? composePass.getOutput('color') : denoisedDisplayLinear
596
+ ? compositeDenoiseDisplayPass.getOutput('color') : denoisedTexture) : undefined) ||
597
+ composePass.getOutput('color');
598
+ const activeHDRPeakMeter = hdrOutputActive
599
+ ? (hdrPeakMeter ??= scratch.get('hdrPeak', () => new WGPUHDRPeakMeter(device)))
600
+ : undefined;
601
+ const hdrPeakReadbackSubmitted = activeHDRPeakMeter?.encode(encoder, displayTexture, renderWidth, renderHeight) ?? false;
602
+ if (output === 'normal' || output === 'albedo') {
603
+ displayTexture = getFrameInput(output);
604
+ }
605
+ else if (output === 'shadowCatcher') {
606
+ displayTexture = getFrameInput('shadowCatcher');
607
+ }
608
+ if (displayTexture.width < renderWidth || displayTexture.height < renderHeight) {
609
+ readbackPass.setSize(renderWidth, renderHeight);
610
+ readbackPass.createPass(encoder, { inputTex: displayTexture });
611
+ displayTexture = readbackPass.getOutput('color');
612
+ }
613
+ // Keep the completed denoise visible until its replacement is ready.
614
+ if (!(preserveDenoisedOutput && output === 'final' && !denoiseValid && presentationDenoised)) {
615
+ const width = renderWidth, height = renderHeight;
616
+ if (!presentationTexture || presentationTexture.width !== width || presentationTexture.height !== height || presentationTexture.format !== displayTexture.format) {
617
+ const previous = presentationTexture;
618
+ presentationTexture = device.createTexture({
619
+ label: 'Render complete image', size: { width, height }, format: displayTexture.format,
620
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC
621
+ });
622
+ if (previous)
623
+ void device.queue.onSubmittedWorkDone().catch(() => { }).then(() => previous.destroy());
624
+ }
625
+ encoder.copyTextureToTexture({ texture: displayTexture }, { texture: presentationTexture }, { width, height });
626
+ presentationDenoised = output === 'final' && denoiseValid;
627
+ }
628
+ const commandBuffer = encoder.finish();
629
+ device.queue.submit([commandBuffer]);
630
+ if (hdrPeakReadbackSubmitted) {
631
+ void activeHDRPeakMeter?.resolve();
632
+ }
633
+ }
634
+ function trace(camera, maxSamples) {
635
+ // A call to draw means accumulation has been invalidated. Cancel OIDN
636
+ // before checking shader compilation: pipeline-affecting parameter changes
637
+ // otherwise leave the previous asynchronous denoise alive while resources
638
+ // and bind groups are being replaced.
639
+ cancelDenoise();
640
+ if (rayTracePass.isCompilingShader()) {
641
+ isFirstFrame = true;
642
+ sampleCount = 0;
643
+ return true;
644
+ }
645
+ // Update the camera before compiling and sampling.
646
+ rayTracePass.setCamera(camera);
647
+ const resetAccumulation = isFirstFrame;
648
+ if (resetAccumulation) {
649
+ denoiseValid = false;
650
+ tileTimingGeneration++;
651
+ elapsedTileTime = undefined;
652
+ rayTracePass.setCamera(camera);
653
+ (tileRenderWithPredict || tileRender).reset();
654
+ // Reset
655
+ isFirstFrame = false;
656
+ sampleCount = 0;
657
+ }
658
+ rayTracePass.updatePipeline();
659
+ if (rayTracePass.isCompilingShader() || !rayTracePass.isPipelineReady()) {
660
+ isFirstFrame = true;
661
+ sampleCount = 0;
662
+ return true;
663
+ }
664
+ // Keep the last completed denoise visible while a static while-converging batch
665
+ // accumulates, including the incomplete first sample after a scene edit.
666
+ if (!preserveDenoisedOutput || isFirstFrame) {
667
+ clearDenoisedOutput();
668
+ }
669
+ if (isPredictingFrame) {
670
+ (tileRenderWithPredict || tileRender).reset();
671
+ isPredictingFrame = false;
672
+ }
673
+ // Never enter a waiting prediction state while the shared timestamp
674
+ // buffer is still being mapped. Render normally and retry the probe on a
675
+ // later frame. The probe itself is also non-blocking: if readback fails or
676
+ // is superseded, the renderer keeps using the conservative tile seed.
677
+ if (offlineSamplesPerBatch === 0 &&
678
+ tileRenderWithPredict &&
679
+ needsUpdatePredicting &&
680
+ timeQuery?.resultBuffer.mapState === 'unmapped') {
681
+ (tileRenderWithPredict || tileRender).reset();
682
+ isPredictingFrame = true;
683
+ tileRenderWithPredict.needsUpdatePredicting();
684
+ needsUpdatePredicting = false;
685
+ }
686
+ const frameSampleCount = sampleCount;
687
+ _startRecordFrameTime(!resetAccumulation);
688
+ const renderSampleTarget = maxSamples;
689
+ const batchTarget = Math.min(renderSampleTarget, sampleCount + Math.max(offlineSamplesPerBatch, 1));
690
+ do {
691
+ let { xIndex, yIndex, columns, rows, isFirstTile, isLastTile, samplesPerFrame } = (tileRenderWithPredict || tileRender).nextTile(elapsedTileTime);
692
+ rayTraceOutputPass.setSampleTileGrid(columns, rows);
693
+ if (offlineSamplesPerBatch > 0 && columns * rows === 1) {
694
+ samplesPerFrame = batchTarget - sampleCount;
695
+ }
696
+ else if (!allowMultipleSamplesPerFrame) {
697
+ samplesPerFrame = 1;
698
+ }
699
+ if (columns * rows === 1) {
700
+ rayTracePass.setSampleTiles(1, 1, 0, 0);
701
+ let samplesSubmitted = 0;
702
+ while (samplesSubmitted < samplesPerFrame &&
703
+ sampleCount < renderSampleTarget &&
704
+ sampleCount < batchTarget) {
705
+ sample(sampleCount === 0, [1, 1]);
706
+ sampleCount++;
707
+ samplesSubmitted++;
708
+ }
709
+ }
710
+ else {
711
+ rayTracePass.setSampleTiles(columns, rows, xIndex, yIndex);
712
+ sample(sampleCount === 0 && isFirstTile, [columns, rows]);
713
+ if (isLastTile)
714
+ sampleCount++;
715
+ }
716
+ } while (offlineSamplesPerBatch > 0 &&
717
+ sampleCount < batchTarget &&
718
+ sampleCount < renderSampleTarget);
719
+ // Not displaying on screen for prediciting frame.
720
+ if (isPredictingFrame) {
721
+ // This hidden timing probe has not resolved the OIDN input textures.
722
+ // Exclude it from progressive samples so a 1spp target cannot denoise
723
+ // stale inputs, and clear its accumulation on the next visible sample.
724
+ sampleCount = 0;
725
+ return false;
726
+ }
727
+ if (sampleCount !== frameSampleCount && (!deferPostProcess || sampleCount >= renderSampleTarget)) {
728
+ const encoder = device.createCommandEncoder();
729
+ drawPostProcess(encoder, sampleCount >= renderSampleTarget, true);
730
+ }
731
+ if (sampleCount !== frameSampleCount) {
732
+ sampleRenderedCallback(sampleCount, frameSampleCount === 0, elapsedFrameTime);
733
+ }
734
+ return false;
735
+ }
736
+ function roundUp(a, b) {
737
+ return Math.ceil(a / b) * b;
738
+ }
739
+ function _applyRenderResolution() {
740
+ if (!screenWidth || !screenHeight)
741
+ return;
742
+ rayTracePass.useTarget(targetKey);
743
+ const scale = 1;
744
+ const nextRenderWidth = roundUp(Math.max(1, Math.round(screenWidth * scale)), 16);
745
+ const nextRenderHeight = roundUp(Math.max(1, Math.round(screenHeight * scale)), 16);
746
+ if (nextRenderWidth === renderWidth && nextRenderHeight === renderHeight) {
747
+ rayTracePass.setSize(nextRenderWidth, nextRenderHeight, screenWidth / screenHeight);
748
+ return;
749
+ }
750
+ // OIDN retains the ray-trace output textures supplied at the start of a
751
+ // tile sequence. Stop every phase (including Promise-chained follow-ups)
752
+ // before those textures are replaced below.
753
+ cancelDenoise();
754
+ // Completed OIDN buffers use the previous image dimensions. Scene edits
755
+ // may preserve them, but a resize must not copy them with the new stride.
756
+ // The target retains its complete presentation texture through the resize.
757
+ clearDenoisedOutput();
758
+ tileTimingGeneration++;
759
+ renderWidth = nextRenderWidth;
760
+ renderHeight = nextRenderHeight;
761
+ (tileRenderWithPredict || tileRender).setSize(renderWidth, renderHeight);
762
+ // Projection follows the displayed canvas, independent of preview/texture alignment.
763
+ rayTracePass.setSize(renderWidth, renderHeight, screenWidth / screenHeight);
764
+ rayTraceOutputPass.setSize(renderWidth, renderHeight);
765
+ composePass.setSize(renderWidth, renderHeight);
766
+ compositeDenoiseDisplayPass.setSize(renderWidth, renderHeight);
767
+ bloomPass?.setSize(renderWidth, renderHeight);
768
+ resolvedSamples = 0;
769
+ // Resizing starts a fresh accumulation while retaining the previous presentation.
770
+ sampleCount = 0;
771
+ resolvedSamples = 0;
772
+ isFirstFrame = true;
773
+ }
774
+ function setSize(width, height) {
775
+ if (width === screenWidth && height === screenHeight) {
776
+ return;
777
+ }
778
+ screenWidth = width;
779
+ screenHeight = height;
780
+ invalidateTilePrediction();
781
+ _applyRenderResolution();
782
+ outputPass.setSize(screenWidth, screenHeight);
783
+ isFirstFrame = true;
784
+ }
785
+ function synchronizeBackground(background) {
786
+ const flatColor = Array.isArray(background) && background[3] >= 0.99999
787
+ ? background
788
+ : undefined;
789
+ const flatBackgroundColor = flatColor
790
+ ? [flatColor[0], flatColor[1], flatColor[2], 1]
791
+ : [0, 0, 0, 0];
792
+ pendingFlatBackground = flatBackgroundColor;
793
+ }
794
+ function setToneMappingParams(params) {
795
+ const newExposure = params.exposure ?? 1;
796
+ const newToneMappingMode = {
797
+ agx: 0,
798
+ aces: 1,
799
+ cineon: 2,
800
+ reinhard: 3,
801
+ linear: 4
802
+ }[params.toneMapping ?? 'agx'] ?? 0;
803
+ const newContrast = params.contrast ?? 1;
804
+ const newSaturation = params.saturation ?? 1;
805
+ const newTemperature = params.temperature ?? 0;
806
+ const newTint = params.tint ?? 0;
807
+ const newVignette = params.vignette ?? 0;
808
+ if (newExposure === exposure &&
809
+ newToneMappingMode === toneMappingMode &&
810
+ newContrast === contrast &&
811
+ newSaturation === saturation &&
812
+ newTemperature === temperature &&
813
+ newTint === tint &&
814
+ newVignette === vignette)
815
+ return false;
816
+ invalidateCompositeDenoise();
817
+ exposure = newExposure;
818
+ toneMappingMode = newToneMappingMode;
819
+ contrast = newContrast;
820
+ saturation = newSaturation;
821
+ temperature = newTemperature;
822
+ tint = newTint;
823
+ vignette = newVignette;
824
+ composePass.setUniform('exposure', exposure);
825
+ composePass.setUniform('toneMappingParams', [toneMappingMode, vignette]);
826
+ composePass.setUniform('colorGradingParams', [
827
+ contrast,
828
+ saturation,
829
+ temperature,
830
+ tint
831
+ ]);
832
+ return true;
833
+ }
834
+ function setHDROutputEnabled(enabled) {
835
+ if (enabled === hdrOutputRequested)
836
+ return false;
837
+ invalidateCompositeDenoise();
838
+ hdrOutputRequested = enabled;
839
+ const wasActive = hdrOutputActive;
840
+ hdrOutputActive = enabled && surface.supportsHDR;
841
+ const nextComposeColorFormat = hdrOutputActive
842
+ ? 'rgba16float'
843
+ : 'rgba8unorm';
844
+ if (nextComposeColorFormat !== composeColorFormat) {
845
+ composeColorFormat = nextComposeColorFormat;
846
+ composePass.setOutputParams({
847
+ color: { format: composeColorFormat },
848
+ denoiseColor: { format: 'rgba32float' }
849
+ });
850
+ }
851
+ if (wasActive !== hdrOutputActive) {
852
+ hdrPeakMeter?.reset();
853
+ }
854
+ compositeDenoiseDisplayPass.setOutputParams({ color: { format: composeColorFormat } });
855
+ compositeDenoiseDisplayPass.setUniform('hdrOutputEnabled', hdrOutputActive ? 1 : 0);
856
+ composePass.setUniform('hdrOutputEnabled', hdrOutputActive ? 1 : 0);
857
+ return wasActive !== hdrOutputActive;
858
+ }
859
+ function _startRecordFrameTime(collectTileTiming) {
860
+ const start = performance.now();
861
+ const generation = tileTimingGeneration;
862
+ const timedTileRender = tileRender;
863
+ const scheduleFrame = typeof requestAnimationFrame === 'function'
864
+ ? requestAnimationFrame
865
+ : (callback) => setTimeout(() => callback(performance.now()), 0);
866
+ scheduleFrame(() => {
867
+ // TODO use timestamp from requestAnimationFrame may have negative value.
868
+ elapsedFrameTime = Math.max(performance.now() - start - pageInvisibleTime, pageInvisibleTime > 0 ? 16 : 8);
869
+ pageInvisibleTime = 0;
870
+ if (generation === tileTimingGeneration &&
871
+ timedTileRender === tileRender && !isFirstFrame) {
872
+ // Without GPU timestamps, discard the draw that follows a scene
873
+ // update instead of feeding its preparation/compilation time back.
874
+ elapsedTileTime = collectTileTiming ? elapsedFrameTime : undefined;
875
+ }
876
+ });
877
+ }
878
+ function _onPageVisiblilityChange() {
879
+ if (typeof document !== 'undefined' && document.hidden) {
880
+ pageLeaveTime = performance.now();
881
+ }
882
+ else {
883
+ pageInvisibleTime = performance.now() - pageLeaveTime;
884
+ }
885
+ }
886
+ /** Runs the built-in JNLM filter on the current composed image. */
887
+ function performJNLMDenoise(denoiser) {
888
+ const generation = denoiseGeneration;
889
+ jnlmPass ??= scratch.get('jnlm', () => new WGPUJNLMDenoisePass(device));
890
+ const rayBuffers = rayTracePass.getOutputBuffer();
891
+ const encoder = device.createCommandEncoder({ label: 'JNLM denoise' });
892
+ const output = jnlmPass.encode(encoder, {
893
+ width: renderWidth,
894
+ height: renderHeight,
895
+ accumulation: rayBuffers.shadowCatcher,
896
+ normalAccumulation: rayBuffers.normal,
897
+ color: composePass.getOutput('denoiseColor'),
898
+ albedo: getFrameInput('albedo'),
899
+ normal: getFrameInput('normal'),
900
+ shadowCatcher: getFrameInput('shadowCatcher'),
901
+ strength: denoiser.strength,
902
+ shape: denoiser.shape
903
+ });
904
+ denoisedBuffer = copyDenoisedOutput(encoder, { data: output, width: renderWidth, height: renderHeight }, denoisedBuffer);
905
+ denoisedDisplayLinear = true;
906
+ denoiseValid = true;
907
+ // The filter also smooths coverage, which carries catcher shadows and
908
+ // transmissive alpha.
909
+ compositeDenoiseDisplayPass.setUniform('denoisedAlpha', +rayTracePass.needsDenoiseAlpha());
910
+ drawPostProcess(encoder, false);
911
+ return device.queue.onSubmittedWorkDone().then(() => {
912
+ const completed = generation === denoiseGeneration;
913
+ return completed;
914
+ });
915
+ }
916
+ function performCustomDenoise(denoiser, schedule, signal) {
917
+ const generation = denoiseGeneration;
918
+ const controller = activeCustomController = new AbortController();
919
+ const abort = () => controller.abort();
920
+ signal?.addEventListener('abort', abort, { once: true });
921
+ if (signal?.aborted)
922
+ abort();
923
+ const task = (async () => {
924
+ if (controller.signal.aborted)
925
+ return false;
926
+ const color = copyGuide('Custom HDR input', getFrameInput('color'), true);
927
+ const width = renderWidth, height = renderHeight;
928
+ const albedo = copyGuide('Custom albedo', getFrameInput('albedo'), true);
929
+ const normal = copyGuide('Custom normal', getFrameInput('normal'), true);
930
+ const output = scratch.get('Custom output', () => new WGPUScratchBuffer(device)).get(width * height * 16, true);
931
+ await denoiser.denoise({ device, width, height, color, albedo, normal, output }, { signal: controller.signal, schedule });
932
+ if (controller.signal.aborted || disposed || generation !== denoiseGeneration)
933
+ return false;
934
+ const coverage = scratch.get('custom coverage', () => new WGPUComputePass('Custom coverage', device, {
935
+ inputs: ['filtered', 'original'], outputs: ['color'], uniforms: auxiliaryPassUniforms,
936
+ csMain: customDenoiseCoverageMain
937
+ }));
938
+ coverage.setOutputParams({ color: { channels: 4 } });
939
+ coverage.setSize(width, height);
940
+ coverage.setUniform('imageSize', [width, height]);
941
+ const finish = device.createCommandEncoder({ label: 'Custom HDR result' });
942
+ coverage.createPass(finish, { filtered: { buffer: output, channels: 4 }, original: { buffer: color, channels: 4 } });
943
+ denoisedBuffer = copyDenoisedOutput(finish, { data: coverage.getOutput('color'), width, height }, denoisedBuffer);
944
+ denoisedHDR = true;
945
+ denoisedDisplayLinear = false;
946
+ denoiseValid = true;
947
+ drawPostProcess(finish, false);
948
+ await device.queue.onSubmittedWorkDone();
949
+ return !controller.signal.aborted && !disposed && generation === denoiseGeneration;
950
+ })();
951
+ customJobs.add(task);
952
+ void task.finally(() => {
953
+ customJobs.delete(task);
954
+ signal?.removeEventListener('abort', abort);
955
+ if (activeCustomController === controller)
956
+ activeCustomController = undefined;
957
+ }).catch(() => { });
958
+ return task;
959
+ }
960
+ function performDenoise(attachedDenoiser, alphaDenoiser, denoiseSchedule = 'converged', signal) {
961
+ cancelDenoise();
962
+ if (isFirstFrame || sampleCount < 1)
963
+ return Promise.resolve(false);
964
+ if (attachedDenoiser.kind === 'custom')
965
+ return performCustomDenoise(attachedDenoiser.denoiser, denoiseSchedule, signal);
966
+ denoisedHDR = false;
967
+ if (attachedDenoiser.kind === 'jnlm')
968
+ return performJNLMDenoise(attachedDenoiser.denoiser);
969
+ const denoiser = attachedDenoiser.denoiser;
970
+ compositeDenoiseDisplayPass.setUniform('denoisedAlpha', 0);
971
+ const generation = denoiseGeneration;
972
+ const size = { width: renderWidth, height: renderHeight };
973
+ const snapshotEncoder = device.createCommandEncoder();
974
+ const colorInput = snapshotInput(snapshotEncoder, 'OIDN color input', composePass.getOutput('denoiseColor'));
975
+ device.queue.submit([snapshotEncoder.finish()]);
976
+ for (const pass of [oidnAlbedoEncodePass, oidnAlbedoDecodePass, oidnNormalSanitizePass]) {
977
+ pass.setSize(renderWidth, renderHeight);
978
+ pass.setUniform('imageSize', [renderWidth, renderHeight]);
979
+ }
980
+ const convergingTileOptions = {
981
+ ...(denoiseSchedule !== 'converged' ? {
982
+ tileOverlap: 80,
983
+ // Low-resolution interaction must finish as one low-latency frame;
984
+ // otherwise continuous camera updates outrun a multi-tile denoise.
985
+ // The settled full-resolution image uses bounded dynamic tiles so
986
+ // 4K inference cannot allocate a whole-image U-Net activation set.
987
+ wholeImage: denoiseSchedule === 'every-sample'
988
+ } : {}),
989
+ // A preview frame is one whole-image pass. Settled images can take many
990
+ // tiles; oidn-web 0.5 runs those back to back by default, which starves
991
+ // the page at 4K, so pace them to display frames instead.
992
+ scheduling: denoiseSchedule === 'every-sample' ? 'event-loop' : 'animation-frame'
993
+ };
994
+ async function denoiseAuxiliaryGuides() {
995
+ const auxiliaryDenoisers = denoiser.auxiliaryDenoisers;
996
+ // Prefilter sampled texture/normal detail for clean-aux weights. The
997
+ // accumulated guides alone are not antialiased at silhouettes and
998
+ // subpixel texture boundaries.
999
+ if (!auxiliaryDenoisers) {
1000
+ return {
1001
+ albedo: {
1002
+ data: copyGuide('OIDN raw albedo', getFrameInput('albedo')),
1003
+ ...size
1004
+ },
1005
+ normal: {
1006
+ data: copyGuide('OIDN raw normal', getFrameInput('normal')),
1007
+ ...size
1008
+ }
1009
+ };
1010
+ }
1011
+ if (generation !== denoiseGeneration)
1012
+ return undefined;
1013
+ const encodeEncoder = device.createCommandEncoder();
1014
+ oidnAlbedoEncodePass.createPass(encodeEncoder, {
1015
+ albedo: {
1016
+ texture: getFrameInput('albedo'),
1017
+ channels: 4
1018
+ }
1019
+ });
1020
+ device.queue.submit([encodeEncoder.finish()]);
1021
+ let albedoOutput;
1022
+ await runOIDNPhase(generation, (finish, fail) => auxiliaryDenoisers.albedo.tileExecute({
1023
+ ...convergingTileOptions,
1024
+ color: {
1025
+ data: oidnAlbedoEncodePass.getOutput('albedo'),
1026
+ ...size
1027
+ },
1028
+ done(output) {
1029
+ albedoOutput = output;
1030
+ finish();
1031
+ },
1032
+ error: fail
1033
+ }));
1034
+ if (generation !== denoiseGeneration || !albedoOutput)
1035
+ return undefined;
1036
+ const decodeEncoder = device.createCommandEncoder();
1037
+ oidnAlbedoDecodePass.setUniform('preserveSurfaceTexture', +false);
1038
+ oidnAlbedoDecodePass.createPass(decodeEncoder, {
1039
+ albedo: { buffer: albedoOutput.data, channels: 4 },
1040
+ rawAlbedo: { texture: getFrameInput('albedo'), channels: 4 },
1041
+ rawNormal: { texture: getFrameInput('normal'), channels: 4 }
1042
+ });
1043
+ device.queue.submit([decodeEncoder.finish()]);
1044
+ let normalOutput;
1045
+ await runOIDNPhase(generation, (finish, fail) => auxiliaryDenoisers.normal.tileExecute({
1046
+ ...convergingTileOptions,
1047
+ color: {
1048
+ data: copyGuide('OIDN raw normal', getFrameInput('normal')),
1049
+ ...size
1050
+ },
1051
+ done(output) {
1052
+ normalOutput = output;
1053
+ finish();
1054
+ },
1055
+ error: fail
1056
+ }));
1057
+ if (generation !== denoiseGeneration || !normalOutput)
1058
+ return undefined;
1059
+ const normalEncoder = device.createCommandEncoder();
1060
+ oidnNormalSanitizePass.createPass(normalEncoder, {
1061
+ normal: { buffer: normalOutput.data, channels: 4 }
1062
+ });
1063
+ device.queue.submit([normalEncoder.finish()]);
1064
+ return {
1065
+ albedo: {
1066
+ data: oidnAlbedoDecodePass.getOutput('albedo'),
1067
+ ...size
1068
+ },
1069
+ normal: {
1070
+ data: oidnNormalSanitizePass.getOutput('normal'),
1071
+ ...size
1072
+ }
1073
+ };
1074
+ }
1075
+ function denoiseComposite(guides) {
1076
+ if (generation !== denoiseGeneration)
1077
+ return Promise.resolve();
1078
+ // Snapshot the complete raw composition; never feed a previous denoise
1079
+ // back into the network during progressive updates.
1080
+ return runOIDNPhase(generation, (finish, fail) => denoiser.tileExecute({
1081
+ ...convergingTileOptions,
1082
+ color: { data: colorInput, ...size },
1083
+ albedo: guides.albedo,
1084
+ normal: guides.normal,
1085
+ progress(output, _tileData, _tile, currentIdx, totalIdx) {
1086
+ if (generation !== denoiseGeneration)
1087
+ return;
1088
+ if (denoiseSchedule !== 'converged' && currentIdx + 1 < totalIdx)
1089
+ return;
1090
+ const encoder = device.createCommandEncoder();
1091
+ denoisedBuffer = copyDenoisedOutput(encoder, output, denoisedBuffer);
1092
+ denoisedDisplayLinear = true;
1093
+ denoiseValid = true;
1094
+ drawPostProcess(encoder, false);
1095
+ },
1096
+ done(output) {
1097
+ if (generation === denoiseGeneration) {
1098
+ const encoder = device.createCommandEncoder();
1099
+ denoisedBuffer = copyDenoisedOutput(encoder, output, denoisedBuffer);
1100
+ denoisedDisplayLinear = true;
1101
+ denoiseValid = true;
1102
+ drawPostProcess(encoder, false);
1103
+ }
1104
+ finish();
1105
+ },
1106
+ error: fail
1107
+ }));
1108
+ }
1109
+ // Denoise alpha channel
1110
+ function denoiseComposed() {
1111
+ if (!alphaDenoiser || generation !== denoiseGeneration) {
1112
+ return Promise.resolve();
1113
+ }
1114
+ const encoder = device.createCommandEncoder();
1115
+ readbackPass.setSize(renderWidth, renderHeight);
1116
+ readbackPass.createPass(encoder, { inputTex: denoisedDisplayLinear ? compositeDenoiseDisplayPass.getOutput('color') : composePass.getOutput('color') });
1117
+ const alphaInput = snapshotInput(encoder, 'OIDN alpha input', readbackPass.getOutput('color'));
1118
+ device.queue.submit([encoder.finish()]);
1119
+ return runOIDNPhase(generation, (finish, fail) => alphaDenoiser.tileExecute({
1120
+ ...convergingTileOptions,
1121
+ color: { data: alphaInput, ...size },
1122
+ denoiseAlpha: true,
1123
+ progress(output, _tileData, _tile, currentIdx, totalIdx) {
1124
+ if (generation !== denoiseGeneration)
1125
+ return;
1126
+ // While-converging denoising keeps the previous complete frame (or the
1127
+ // fallback) visible while tiled inference is
1128
+ // running. Publishing the shared output buffer before its final
1129
+ // tile would expose uninitialized regions as black rectangles.
1130
+ if (denoiseSchedule !== 'converged' && currentIdx + 1 < totalIdx)
1131
+ return;
1132
+ const encoder = device.createCommandEncoder();
1133
+ denoisedBuffer = copyDenoisedOutput(encoder, output, denoisedBuffer);
1134
+ denoisedDisplayLinear = false;
1135
+ denoiseValid = true;
1136
+ drawPostProcess(encoder, false);
1137
+ },
1138
+ done(output) {
1139
+ if (generation === denoiseGeneration) {
1140
+ const encoder = device.createCommandEncoder();
1141
+ denoisedBuffer = copyDenoisedOutput(encoder, output, denoisedBuffer);
1142
+ denoisedDisplayLinear = false;
1143
+ denoiseValid = true;
1144
+ drawPostProcess(encoder, false);
1145
+ }
1146
+ finish();
1147
+ },
1148
+ error: fail
1149
+ }));
1150
+ }
1151
+ return denoiseAuxiliaryGuides()
1152
+ .then(async (guides) => {
1153
+ if (guides && generation === denoiseGeneration) {
1154
+ await denoiseComposite(guides);
1155
+ }
1156
+ })
1157
+ .then(() => {
1158
+ if (generation === denoiseGeneration &&
1159
+ rayTracePass.needsDenoiseAlpha()) {
1160
+ return denoiseComposed();
1161
+ }
1162
+ })
1163
+ .then(() => {
1164
+ const completed = generation === denoiseGeneration;
1165
+ return completed;
1166
+ });
1167
+ }
1168
+ if (typeof document !== 'undefined') {
1169
+ document.addEventListener('visibilitychange', _onPageVisiblilityChange);
1170
+ }
1171
+ function dispose() {
1172
+ if (disposed)
1173
+ return;
1174
+ if (typeof document !== 'undefined') {
1175
+ document.removeEventListener('visibilitychange', _onPageVisiblilityChange);
1176
+ }
1177
+ disposed = true;
1178
+ tileTimingGeneration++;
1179
+ cancelDenoise();
1180
+ // Target outputs and timestamp readbacks can still be referenced by the
1181
+ // queue. Cancel inference now; retire all GPU-owned target resources after
1182
+ // both submitted work and the pending timestamp mapping have completed.
1183
+ void Promise.all([
1184
+ device.queue.onSubmittedWorkDone().catch(() => undefined),
1185
+ diagnosticsReadbackPromise
1186
+ ]).then(() => {
1187
+ rayTracePass.releaseTarget(targetKey);
1188
+ denoisedBuffer?.destroy();
1189
+ presentationTexture?.destroy();
1190
+ displayPostProcess.dispose();
1191
+ rayTraceOutputPass.dispose();
1192
+ });
1193
+ }
1194
+ function activate() {
1195
+ rayTracePass.useTarget(targetKey);
1196
+ if (renderWidth && renderHeight)
1197
+ rayTracePass.setSize(renderWidth, renderHeight, screenWidth / screenHeight);
1198
+ if (execution) {
1199
+ rayTracePass.setQuality(execution);
1200
+ rayTracePass.setClamping(execution.clamping);
1201
+ rayTracePass.setTransmissiveOutputAlphaMin(execution.transmissiveOutputAlphaMin);
1202
+ rayTracePass.setBevelSamples(execution.bevelSamples);
1203
+ rayTracePass.setAlphaBlendStrategy(execution.alphaBlendStrategy);
1204
+ rayTracePass.setSamplingPattern(execution.samplingPattern, execution.maxSamples);
1205
+ }
1206
+ }
1207
+ return {
1208
+ invalidateTiming: invalidateTilePrediction,
1209
+ invalidateDenoisedOutput() { cancelDenoise(); denoiseValid = false; preserveDenoisedOutput = false; },
1210
+ configure(settings) {
1211
+ const timingKeys = ['bounces', 'bevel', 'bevelSamples', 'directLightCandidates', 'dispersionWavelengths', 'alphaBlendStrategy', 'samplingPattern', 'clamping', 'transmissiveOutputAlphaMin'];
1212
+ if (!execution || timingKeys.some(key => execution[key] !== settings[key]))
1213
+ invalidateTilePrediction();
1214
+ execution = { ...settings };
1215
+ maxTimePerTile = Math.max(1, settings.timeBudgetMs);
1216
+ allowMultipleSamplesPerFrame = settings.allowMultipleSamplesPerFrame;
1217
+ tileRender.setDesiredMsPerTile(maxTimePerTile);
1218
+ tileRender.setMaxTiles(settings.maxTiles);
1219
+ tileRenderWithPredict?.setDesiredMsPerTile(maxTimePerTile);
1220
+ tileRenderWithPredict?.setMaxTiles(settings.maxTiles);
1221
+ },
1222
+ synchronizeBackground,
1223
+ activate,
1224
+ canCompleteSampleWithin(timeBudgetMs) {
1225
+ return (lastGpuTraceTiming?.estimatedFullFrameTimeMs ?? elapsedFrameTime) <= timeBudgetMs;
1226
+ },
1227
+ get presentationComplete() { return !!presentationTexture; },
1228
+ get presentationDenoised() { return presentationDenoised; },
1229
+ present() {
1230
+ if (!presentationTexture)
1231
+ return;
1232
+ surface.configure(hdrOutputActive);
1233
+ const encoder = device.createCommandEncoder();
1234
+ outputPass.setSize(canvas.width, canvas.height);
1235
+ _present(encoder, displayPostProcess.output ?? presentationTexture);
1236
+ device.queue.submit([encoder.finish()]);
1237
+ },
1238
+ getMemory() {
1239
+ return {
1240
+ width: renderWidth, height: renderHeight,
1241
+ bytes: rayTracePass.getTargetMemoryBytes(targetKey) + rayTraceOutputPass.getMemoryBytes() + textureBytes(presentationTexture) + displayPostProcess.getMemoryBytes() + (denoisedBuffer?.size ?? 0)
1242
+ };
1243
+ },
1244
+ isFirstFrame() {
1245
+ return isFirstFrame;
1246
+ },
1247
+ restartSamples() {
1248
+ // Input edits reset accumulation, not learned throughput. Re-arming a
1249
+ // hidden probe here starves continuously changing scenes of visible samples.
1250
+ tileTimingGeneration++;
1251
+ cancelDenoise();
1252
+ preserveDenoisedOutput = false;
1253
+ denoiseValid = false;
1254
+ presentationDenoised = false;
1255
+ resolvedSamples = 0;
1256
+ (tileRenderWithPredict || tileRender).reset();
1257
+ isPredictingFrame = false;
1258
+ isFirstFrame = true;
1259
+ sampleCount = 0;
1260
+ },
1261
+ getOutputTexture(kind) {
1262
+ if (resolvedSamples < 1)
1263
+ return;
1264
+ if (kind === 'hdr')
1265
+ return getFrameInput('color');
1266
+ return displayPostProcess.output ?? presentationTexture;
1267
+ },
1268
+ applyPostProcesses(passes, render) {
1269
+ if (presentationTexture)
1270
+ displayPostProcess.run(presentationTexture, passes, render);
1271
+ },
1272
+ trace,
1273
+ drawPostProcess() {
1274
+ const encoder = device.createCommandEncoder();
1275
+ drawPostProcess(encoder, true);
1276
+ },
1277
+ performDenoise,
1278
+ cancelDenoise,
1279
+ setSize,
1280
+ setHDROutputEnabled,
1281
+ setToneMappingParams,
1282
+ async readPresentation() {
1283
+ if (!presentationTexture)
1284
+ throw new Error('Render has no complete image.');
1285
+ const image = displayPostProcess.output ?? presentationTexture;
1286
+ const width = image.width, height = image.height;
1287
+ const bytesPerRow = Math.ceil(width * 16 / 256) * 256;
1288
+ const buffer = device.createBuffer({ size: bytesPerRow * height, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ });
1289
+ const encoder = device.createCommandEncoder();
1290
+ readbackPass.setSize(width, height);
1291
+ readbackPass.createPass(encoder, { inputTex: image });
1292
+ encoder.copyTextureToBuffer({ texture: readbackPass.getOutput('color') }, { buffer, bytesPerRow }, { width, height });
1293
+ device.queue.submit([encoder.finish()]);
1294
+ try {
1295
+ await buffer.mapAsync(GPUMapMode.READ);
1296
+ const mapped = new Float32Array(buffer.getMappedRange());
1297
+ const data = new Float32Array(width * height * 4);
1298
+ for (let y = 0; y < height; y++)
1299
+ data.set(mapped.subarray(y * bytesPerRow / 4, y * bytesPerRow / 4 + width * 4), y * width * 4);
1300
+ return { width, height, data };
1301
+ }
1302
+ finally {
1303
+ buffer.unmap();
1304
+ buffer.destroy();
1305
+ }
1306
+ },
1307
+ async readOutput(kind) {
1308
+ rawOutputPass.setUniform('flatBackgroundColor', resolvedFlatBackground);
1309
+ const width = renderWidth, height = renderHeight;
1310
+ const bytesPerRow = Math.ceil(width * 16 / 256) * 256;
1311
+ const buffer = device.createBuffer({
1312
+ size: bytesPerRow * height, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
1313
+ });
1314
+ const encoder = device.createCommandEncoder();
1315
+ const denoised = kind === 'denoised' && denoiseValid ? denoisedBuffer : undefined;
1316
+ if (kind === 'denoised' && !denoised)
1317
+ kind = 'hdr';
1318
+ const pass = kind === 'hdr' ? rawOutputPass : readbackPass;
1319
+ pass.setOutputParams({ color: { format: 'rgba32float' } });
1320
+ pass.setSize(width, height);
1321
+ if (denoised) {
1322
+ encoder.copyBufferToBuffer(denoised, 0, buffer, 0, width * height * 16);
1323
+ }
1324
+ else if (kind === 'hdr') {
1325
+ rawOutputPass.createPass(encoder, { colorTex: getFrameInput('color'), coverageTex: getFrameInput('albedo') });
1326
+ }
1327
+ else {
1328
+ readbackPass.createPass(encoder, { inputTex: getFrameInput(kind) });
1329
+ }
1330
+ if (!denoised)
1331
+ encoder.copyTextureToBuffer({ texture: pass.getOutput('color') }, { buffer, bytesPerRow }, { width, height, depthOrArrayLayers: 1 });
1332
+ device.queue.submit([encoder.finish()]);
1333
+ try {
1334
+ await buffer.mapAsync(GPUMapMode.READ);
1335
+ const mapped = new Float32Array(buffer.getMappedRange());
1336
+ const data = new Float32Array(width * height * 4);
1337
+ const stride = denoised ? width * 4 : bytesPerRow / 4;
1338
+ for (let y = 0; y < height; y++)
1339
+ data.set(mapped.subarray(y * stride, y * stride + width * 4), y * width * 4);
1340
+ return { data, width, height };
1341
+ }
1342
+ finally {
1343
+ buffer.unmap();
1344
+ buffer.destroy();
1345
+ }
1346
+ },
1347
+ setPreserveDenoisedOutput(preserve) {
1348
+ preserveDenoisedOutput = preserve;
1349
+ },
1350
+ setOfflineRenderOptions(options) {
1351
+ const wasOffline = offlineSamplesPerBatch > 0;
1352
+ offlineSamplesPerBatch = Math.max(0, Math.round(options.samplesPerBatch));
1353
+ deferPostProcess = options.deferPostProcess;
1354
+ sampleSequenceOffset = Math.max(0, Math.round(options.sampleSequenceOffset));
1355
+ if (offlineSamplesPerBatch > 0 && isPredictingFrame) {
1356
+ (tileRenderWithPredict || tileRender).reset();
1357
+ isPredictingFrame = false;
1358
+ }
1359
+ if (offlineSamplesPerBatch > 0)
1360
+ needsUpdatePredicting = false;
1361
+ else if (wasOffline)
1362
+ invalidateTilePrediction();
1363
+ },
1364
+ setVarianceMoments(enabled) {
1365
+ rayTracePass.setVarianceMoments(enabled);
1366
+ },
1367
+ setAdaptiveSamplingTargets(targets) {
1368
+ rayTracePass.setAdaptiveSamplingTargets(targets);
1369
+ },
1370
+ prepareOfflineAdaptiveSampling(options) {
1371
+ return rayTracePass.prepareOfflineAdaptiveSampling(options);
1372
+ },
1373
+ getTotalSamplesRendered() {
1374
+ return sampleCount;
1375
+ },
1376
+ getDiagnostics() {
1377
+ return {
1378
+ ...rayTracePass.getDiagnostics(),
1379
+ hdrPresentation: {
1380
+ requested: hdrOutputRequested,
1381
+ active: surface.hdr,
1382
+ peakDisplayValue: surface.hdr
1383
+ ? hdrPeakMeter?.getPeakDisplayValue()
1384
+ : undefined
1385
+ },
1386
+ gpuTraceTiming: lastGpuTraceTiming
1387
+ };
1388
+ },
1389
+ async flushDiagnostics() {
1390
+ await device.queue.onSubmittedWorkDone();
1391
+ await diagnosticsReadbackPromise;
1392
+ },
1393
+ setBloomParams({ intensity, threshold, radius }) {
1394
+ const changed = threshold !== bloomThreshold ||
1395
+ intensity !== bloomIntensity ||
1396
+ radius !== bloomRadius;
1397
+ if (changed)
1398
+ invalidateCompositeDenoise();
1399
+ bloomThreshold = threshold;
1400
+ bloomIntensity = intensity;
1401
+ bloomRadius = radius;
1402
+ return changed;
1403
+ },
1404
+ setShadowCatcherParams(color, alpha) {
1405
+ const changed = color[0] !== shadowCatcherColor[0] ||
1406
+ color[1] !== shadowCatcherColor[1] ||
1407
+ color[2] !== shadowCatcherColor[2] ||
1408
+ alpha !== shadowCatcherAlpha;
1409
+ if (changed) {
1410
+ vec3.copy(shadowCatcherColor, color);
1411
+ shadowCatcherAlpha = alpha;
1412
+ composePass.setUniform('shadowCatcherColor', color);
1413
+ composePass.setUniform('shadowCatcherAlpha', alpha);
1414
+ }
1415
+ return changed;
1416
+ },
1417
+ setOnSampleRendered(cb) {
1418
+ sampleRenderedCallback = cb;
1419
+ },
1420
+ setOutput(nextOutput) {
1421
+ if (output === nextOutput)
1422
+ return false;
1423
+ output = nextOutput;
1424
+ return true;
1425
+ },
1426
+ dispose
1427
+ };
1428
+ }
1429
+ /** Independent render targets, without preview or presentation policy. */
1430
+ export function makeWGPURenderTargets(canvas, device, options = {}) {
1431
+ const rayTracePass = new WGPURayTracePass(device);
1432
+ const customJobs = new Set();
1433
+ const scratch = new ScratchPool(work => {
1434
+ // A cancelled custom filter can keep submitting GPU work until its promise settles.
1435
+ // A custom callback can dispose synchronously, before its promise is recorded.
1436
+ // Observe leases after that callback returns so its borrowed inputs stay alive.
1437
+ void Promise.resolve().then(() => Promise.allSettled([...customJobs]))
1438
+ .then(() => device.queue.onSubmittedWorkDone()).catch(() => { }).then(work);
1439
+ });
1440
+ const surface = scratch.get('canvas', () => new WGPUCanvasSurface(canvas, device, options.presentationFormat ?? navigator.gpu.getPreferredCanvasFormat()));
1441
+ return makeRenderPipeline(rayTracePass, () => {
1442
+ const target = makeWGPUAccumulationTarget(canvas, device, rayTracePass, surface, scratch, customJobs);
1443
+ return target;
1444
+ }, false, scratch);
1445
+ }
1446
+ //# sourceMappingURL=WGPURenderingPipeline.js.map