@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
@@ -0,0 +1,74 @@
1
+ # Tile scheduling
2
+
3
+ A full-frame path-tracing sample can take longer than a display frame and, on slow
4
+ scenes, long enough to stall the browser or trigger a GPU timeout. Renders therefore
5
+ trace each sample as a grid of tiles, sized so that one tile fits `timeBudgetMs`.
6
+ Usage and the related options are in
7
+ [sample batching](../progressive-rendering.md#sample-batching); this note explains how
8
+ the tile grid is chosen. The code is `src/renderer/TileRender.ts`.
9
+
10
+ ## Two predictors
11
+
12
+ - **WebGPU with timestamp queries** uses `makeTileRenderWithPredict`, which reads
13
+ the GPU time of each submitted grid.
14
+ - **WebGL 2, and WebGPU without timestamp queries,** use `makeTileRender`, adapted
15
+ from ray-tracing-renderer. It adjusts a pixels-per-tile count from wall-clock
16
+ feedback, never below 8192 pixels. WebGL timer queries are not reliable enough to
17
+ replace it; see [WebGL 2 backend](./webgl.md).
18
+
19
+ The rest of this note describes the WebGPU predictor.
20
+
21
+ ## Prediction
22
+
23
+ The first sample uses a probe tile shaped like the frame, large enough that dispatch
24
+ overhead does not dominate it. Each later measurement scales the tile area by the
25
+ ratio of the budget to the measured tile time, limited to 4× (2× per dimension) per
26
+ update so the grid converges without jumping from a small probe to a blocking
27
+ full-frame tile. Tiles are never narrower than one compute workgroup (8 pixels):
28
+ a smaller dispatch occupies the same workgroups and repeats their fixed cost.
29
+
30
+ The grid is interleaved, so each sample still covers every pixel once. When the
31
+ predicted tile exceeds the frame, several samples are traced per frame instead.
32
+
33
+ ## Why area-proportional prediction is not enough
34
+
35
+ Tile time is not proportional to tile area. A tile pays a fixed cost for dispatch
36
+ and for its longest path, and small frames leave most of the GPU idle. On a heavy
37
+ scene at a small resolution, 16 tiles can each take more than half the time of the
38
+ whole frame. An area-proportional predictor keeps subdividing toward the budget and
39
+ multiplies the cost of every sample.
40
+
41
+ Two rules handle this:
42
+
43
+ - **Refinement check.** When the predictor subdivides, it compares the next
44
+ measurement with the grid that motivated the change. If the new tile time
45
+ improves by less than half of the area ratio, and still misses the budget by more
46
+ than 10%, the predictor returns to the previous grid and uses its tile size as a
47
+ floor until prediction is invalidated.
48
+ - **Unattainable budgets.** From two measurements at sufficiently different tile
49
+ areas, the predictor fits tile time as `fixed + perPixel × area`. If the fixed
50
+ part alone exceeds the budget by more than 10%, no grid can meet it. The predictor then picks the
51
+ fewest tiles that keep a tile below 350 ms (over 5× below a 2 s GPU timeout) and,
52
+ where possible, keep the repeated fixed cost below one fifth of the sample. This
53
+ decision is retained until prediction is invalidated, because refitting on its
54
+ own grids lets path-cost noise alternate between tile counts.
55
+
56
+ `maxTiles` still caps the grid in both cases. A long tile is a deliberate trade:
57
+ fewer updates while interacting, in exchange for not multiplying the cost of
58
+ every sample. The guide documents the resulting pauses.
59
+
60
+ ## Measurements
61
+
62
+ Several samples may be submitted before their timestamps resolve. Each measurement
63
+ is matched to the grid it was submitted with and consumed once, so stale results
64
+ cannot shrink tiles repeatedly. Scene, view and setting changes invalidate the
65
+ prediction (`needsUpdatePredicting`). Photon and light-tracing batches run with the
66
+ first and last tile and are timed separately.
67
+
68
+ ## Changing this code
69
+
70
+ - Scheduling changes must update `tests/fixtures/display-schedule.json` and the
71
+ schedule tests, and pass `npm run test:parity`; see `AGENTS.md`.
72
+ - Judge changes by total sample time and by tile time, at both small and normal
73
+ resolutions on a heavy scene. Fixed-seed HDR must stay identical, because tiling
74
+ changes only the order of work.
@@ -0,0 +1,53 @@
1
+ # WebGL 2 backend
2
+
3
+ The WebGL 2 backend runs the same path tracer as fragment shaders. It is the
4
+ fallback where WebGPU is unavailable, so it favors working everywhere over speed.
5
+ This note lists the constraints that shape it and the open issues known to
6
+ contributors. Feature differences visible to users are documented with each
7
+ feature.
8
+
9
+ ## Constraints
10
+
11
+ - **Texture units.** WebGL 2 guarantees 16 fragment texture units. Per-vertex
12
+ attributes (normals, UVs, tangents, colors, material indices) share one
13
+ interleaved RGBA32F texture, positions keep their own for triangle tests, and
14
+ instance records follow the BVH in its texture. `webglSamplerBudget.test.ts`
15
+ keeps every trace pass at or below 14 referenced samplers, leaving two for new
16
+ features. Add a sampler only after checking that budget.
17
+ - **Uniforms.** Large scenes can exceed `MAX_FRAGMENT_UNIFORM_VECTORS` (1024 on
18
+ common devices) and fail to render. The renderer does not yet fail soft there.
19
+ - **Path length.** `bounces` and path events stop at 20 on WebGL, and Intel Macs use
20
+ a shorter event limit. WebGPU allows 50.
21
+ - **Compilation.** ANGLE defers native compilation, so a first draw can block for
22
+ seconds, much longer than WebGPU. Trace programs request parallel compilation
23
+ and are polled before use. Per-program timings do not measure that program's
24
+ compile time.
25
+ - **Timing.** Timer queries can report totals longer than the wall-clock time and
26
+ cannot drive scheduling or performance claims. Tiles are sized from wall-clock
27
+ feedback; see [tile scheduling](./tiling.md). Measure WebGL performance by wall
28
+ time until the HDR readback completes.
29
+ - **WebGPU-only features.** Caustic estimators, display post-processing,
30
+ display-resolution AOVs and custom denoisers fail soft on WebGL. Caustic paths
31
+ fall back to path tracing, so the expected image is unchanged.
32
+
33
+ ## Open issues
34
+
35
+ - **Background in some scenes.** On at least one scene, WebGL terminates many
36
+ paths at depth 0 or on a miss where WebGPU continues, and part of the background
37
+ renders black. Primary-miss and environment behavior should be aligned before
38
+ WebGL is used as a cross-backend reference for such scenes.
39
+ - **Long single draws.** With dispersion, one fragment invocation traces all three
40
+ channel suffixes ([shared prefixes](./continuous-spectrum.md#shared-prefixes)).
41
+ Large renders with a single tile have lost the context, likely from a GPU
42
+ timeout. Options are to size tiles for three times the work or to fall back to
43
+ three draws on large images.
44
+ - **Context loss.** Some driver failures lose the context and later reads return
45
+ zeros. Treat an all-black WebGL capture as a failed run, not a result.
46
+
47
+ ## Changing this code
48
+
49
+ - Implement material, light and sampling changes in both WGSL and GLSL; see
50
+ `AGENTS.md`.
51
+ - Run GPU comparisons headless, one context at a time. When a WebGL capture fails
52
+ with context loss, rerun it before drawing conclusions, and do not compare its
53
+ output.
package/docs/updates.md CHANGED
@@ -154,7 +154,9 @@ do not recompile the path tracer. Neither do changes to `bounces`,
154
154
  bevel switch, sample count or seed. Data uploads, atlas/BVH work and accumulation
155
155
  restarts may still be needed.
156
156
 
157
- Returning to a cached compatible combination reuses its pipeline. Prepared
157
+ A render waiting for a shader keeps its samples and image, and other renders keep
158
+ sampling. Returning to a cached compatible combination reuses its pipeline, even
159
+ while another combination is still compiling. Prepared
158
160
  combinations may be discarded after many different edits; recreating the Engine
159
161
  or recovering a lost device also requires preparing shaders again. Compilation time depends
160
162
  on the feature combination, BVH layout, browser and GPU driver. Denoising and
@@ -0,0 +1,65 @@
1
+ # Viewer feature checklist
2
+
3
+ Use this checklist with the [Web viewer](https://mutracer.meimu-studio.com/viewer/)
4
+ and [native viewer](../native/viewer/README.md). Start with material spheres,
5
+ then use a glTF with nested objects, cameras, lights and animation. A checked
6
+ item means you exercised the behavior in the version being reviewed; it does
7
+ not promise identical rendered pixels between devices or backends.
8
+
9
+ ## Workflow
10
+
11
+ | Check | Action and expected result |
12
+ | --- | --- |
13
+ | [ ] Open | Open a GLB, a glTF with external buffers/images, a folder and a model URL; geometry and textures load. Drop files on the viewport. On Web, a fresh load uses the collection's curated view or the file's first camera, including orthographic cameras; restoring a saved view takes precedence. Failed or cancelled loads leave the previous scene usable. |
14
+ | [ ] Examples | Open **Examples** in the toolbar; switch between MuTracer collection and glTF Research Scenes, load a thumbnail and check its camera and authored lighting. |
15
+ | [ ] Scene | Search objects or materials and expand nested groups and meshes; select a material child to open its editor. Select an object from the tree or viewport; selection reveals its ancestors. Hide/show a mesh and a group. Select an authored camera. |
16
+ | [ ] Navigation | Drag to orbit, right-drag to pan, scroll to zoom, **F** to fit. Moving an authored camera continues in Orbit. |
17
+ | [ ] History | Undo/redo material, visibility, lighting and render edits; one continuous drag is one step. Reset all restores defaults. On Web, it also restores the example's render choices and initial camera, or fits the model when no camera is provided. |
18
+ | [ ] Panels | **1–4** opens Lighting, Material, Camera, Render respectively. **[**, **]** toggle scene/inspector; click the active rail tab to close it. **H** hides the interface. Text input must not trigger shortcuts. |
19
+ | [ ] Playback | **Space** plays/pauses animation when present, otherwise pauses/resumes rendering. Choose another clip and seek the timeline. |
20
+ | [ ] Render dock | Change sample target, viewport resolution and denoising; pause/resume and inspect sample progress, elapsed time and completion. **D** toggles denoising. |
21
+ | [ ] Export | **E** opens a preview; save the displayed viewport PNG. Render Viewport/1080p/2K/4K/Square PNG with a separate sample budget, denoising and optional transparency. Cancel an export and continue using the original image. |
22
+ | [ ] Code/help | Open Code and copy a setup for the corresponding SDK; open keyboard help. **⌘O / Ctrl+O** opens files. |
23
+
24
+ ## Lighting, materials, camera and rendering
25
+
26
+ | Check | Controls and expected result |
27
+ | --- | --- |
28
+ | [ ] Environment | Switch HDRI / Studio and model lighting where available. Load a local HDR or browse Poly Haven; change HDR resolution, intensity and rotation. Failed downloads keep the previous lighting. |
29
+ | [ ] Studio | Choose each rig; adjust intensity, rotation, size, ambient strength and color. |
30
+ | [ ] Background | Switch environment, color, transparent and grounded modes where offered. Adjust projection height/radius; verify auto values and transparency in exported PNG. |
31
+ | [ ] Ground | Enable ground shadow and change opacity; inspect contact with the model. |
32
+ | [ ] Material selection | Pick a mesh/material, check shared-material edits, apply a preset and reset to loaded values. |
33
+ | [ ] Material layers | Exercise base color/weight, metalness, roughness, diffuse roughness, IOR/specular, anisotropy, transmission/volume/thin wall, subsurface, coat, fuzz, thin film, emission, opacity/alpha and double-sided/unlit controls where offered. |
34
+ | [ ] Textures | Upload, replace and remove each exposed slot; change Wrap S/T and Repeat S/T, including negative repeats. Apply a Poly Haven set with optional displacement; cancel/replace downloads and switch materials during download. |
35
+ | [ ] Displacement | Change scale, bias, subdivision and smoothing; verify silhouettes and return to zero displacement. |
36
+ | [ ] MaterialX (Web) | Import a .zip package or .mtlx with images; check converted layers and textures. |
37
+ | [ ] Camera | Switch Perspective/Orthographic, adjust FOV, auto-rotate and Fit. Authored cameras keep their projection/FOV until Orbit resumes. |
38
+ | [ ] Depth of field | Enable DOF, adjust f-number, area autofocus and focus frame; move the frame, manually edit focus or pick a point; choose automatic or explicit scene units. Orthographic views disable DOF. |
39
+ | [ ] Quality | Change max samples, bounces, resolution, sampling pattern and clamping. Web also offers a BVH builder. |
40
+ | [ ] Caustics | Toggle light tracing and photon mapping; compare each alone and together on a glass scene. Web's master Caustics switch gates both. |
41
+ | [ ] Denoiser | Toggle JNLM/OIDN; adjust JNLM strength, search/patch radius, albedo/normal tolerance and passes; choose OIDN Standard/Small/Large and tile size. Reset selected parameters. |
42
+ | [ ] Color | Change AgX/ACES/Cineon/Reinhard/Linear, exposure, contrast, saturation, temperature and tint; verify exported colors. |
43
+ | [ ] Effects | Enable bloom, threshold/radius and vignette; turn them off and verify the image returns. |
44
+ | [ ] Debug | Expand/collapse Debug; inspect Final, Albedo, Normal, Depth, Position, Roughness, Diffuse/Specular reflectance, ID and Motion. Adjust channel ranges and ID component; toggle interactive preview and max preview scale. Overlay gestures must not orbit/select the scene. |
45
+ | [ ] Window size | Check wide and narrow windows, hidden panels and long names; controls remain readable and the viewport remains usable. Web also provides the mobile bottom sheet. |
46
+
47
+ ## Platform options and limits
48
+
49
+ | Area | Web | Native |
50
+ | --- | --- | --- |
51
+ | Rendering device | WebGPU; WebGL 2 fallback reloads the page | HWRT / Software BVH; device availability is reported in the UI |
52
+ | Denoising | WebGL 2 supports JNLM; OIDN needs WebGPU | JNLM/OIDN; optional Windows DLSS RR build |
53
+ | HDR display | WebGPU plus compatible browser/display; falls back to SDR | Viewer window is SDR; HDR toggle is disabled |
54
+ | BVH builder | tinybvh / builtin | No builder selector |
55
+ | MaterialX import | .zip or .mtlx with images | No MaterialX import UI |
56
+ | Caustics controls | Master switch plus two methods; methods require WebGPU | Independent method switches; both off disables caustics |
57
+ | File access | Multi-file/folder picker; URLs need CORS | File/folder picker; folder Open selects a top-level glTF/GLB; resources resolve beside the model |
58
+ | Code | JavaScript setup | Rust SDK setup |
59
+ | Examples browser | Popover with optional pinned panel | Toolbar opens a docked panel |
60
+ | Extra native controls | — | Fixed-pixel render sizes, directional key light |
61
+ | Small screens | Mobile sheet and responsive toolbar | Desktop panels; scene list hides when space is limited |
62
+
63
+ Native and Web use different UI toolkits and fonts. Check icon meaning,
64
+ label order, alignment and control availability; file dialogs, window controls,
65
+ font rasterization and responsive behavior follow each platform.
package/docs/viewer.md CHANGED
@@ -21,20 +21,30 @@ resampled to at most 2K width. If a source map is missing, the model still opens
21
21
  and the Lighting panel explains which map is unavailable; choose an environment
22
22
  there to replace it.
23
23
 
24
- glTF Research Scenes automatically use their first camera when available.
25
- For other glTF/GLB imports with cameras, a toast offers **Apply camera** to
26
- use the first camera; dismiss it to keep the Orbit view. You can select any
24
+ MuTracer collection examples open with a curated view, under the procedural
25
+ sky or a Poly Haven HDRI. A few also set their own tone mapping and bloom, which
26
+ return to the defaults when you leave them. Other models with cameras open in their
27
+ first camera. You can select any
27
28
  imported camera in the separate camera toolbar at the bottom right; it appears
28
29
  only for models with cameras. Drag, pan or zoom after selecting a camera to
29
30
  continue in **Orbit** from that view.
30
31
  Hover over a camera in the scene tree and click its camera action to apply it
31
32
  directly. **Fit to model** in the bottom toolbar (or **F**) returns to Orbit
32
33
  and frames the model.
33
- Reloading the current model restores your saved camera selection without
34
- showing the prompt again.
34
+ Reloading the current model restores your saved camera selection.
35
35
 
36
36
  Picking an object in the viewport reveals it in the scene tree and scrolls it
37
- into view. Material rows start collapsed; expand a mesh to see its materials.
37
+ into view. Selection follows the rendered geometry, including
38
+ [displacement on WebGPU](materials.md#displacement) and alpha-cutout holes;
39
+ it becomes available once the scene is ready to render.
40
+ Material rows start collapsed; expand a mesh to see its materials.
41
+
42
+ The viewer caches prepared BVHs to speed up reopening unchanged models.
43
+ Models still need to be loaded and decoded. The cache has a 4 GiB browser-storage
44
+ budget; entries are removed as space is needed. Cached data is reused only when
45
+ geometry, displacement and BVH settings match. Loading still works if storage
46
+ is unavailable or full.
47
+ Clear the site's browser storage to remove the cache.
38
48
 
39
49
  ## Sharing viewer settings
40
50
 
@@ -54,6 +64,8 @@ https://mutracer.meimu-studio.com/viewer/?hdrOutputEnabled=true&maxSamples=128#m
54
64
  The viewer updates the URL as lighting, render and display settings change.
55
65
  Copy the address bar to share those settings and the selected example model.
56
66
  Settings also work in the hash, such as `#model=dragon&hdrOutputEnabled=true`.
67
+ A link with only `#model=…` opens the example with its own render settings, as
68
+ the Examples menu does. Settings in a link win over the example's.
57
69
 
58
70
  ### Options
59
71
 
package/lib/Display.d.ts CHANGED
@@ -36,11 +36,25 @@ export interface PostProcessContext {
36
36
  /** Engine device; may change after recovery. */ device: GPUDevice;
37
37
  /** Record commands here; the library finishes and submits it. */ encoder: GPUCommandEncoder;
38
38
  /** Read-only composed display image, top row first; no additional color conversion. */ input: GPUTexture;
39
- /** Fully write this image. Same format and dimensions as input; supports render, storage and copy writes. */ output: GPUTexture;
40
- /** Actual render width. */ width: number;
41
- /** Actual render height. */ height: number;
39
+ /** Fully write this image. Same format as input; dimensions follow outputSize; supports render, storage and copy writes. */ output: GPUTexture;
40
+ /** Actual render width; pass dimensions are input.width and output.width. */ width: number;
41
+ /** Actual render height; pass dimensions are input.height and output.height. */ height: number;
42
42
  /** Selected complete render, including a selected fallback. */ render: Render;
43
43
  }
44
+ /** Optional dimensions for a display pass. */
45
+ export interface PostProcessOptions {
46
+ /** Output dimensions per invocation, bounded by maxTextureDimension2D. Default undefined preserves input size. Display is the current canvas size, including during export. */
47
+ outputSize?: (input: {
48
+ width: number;
49
+ height: number;
50
+ }, display: {
51
+ width: number;
52
+ height: number;
53
+ }) => {
54
+ width: number;
55
+ height: number;
56
+ };
57
+ }
44
58
  /** Synchronous display pass. Images and encoder are borrowed only during the call; do not retain or destroy them. */
45
59
  export type PostProcess = (context: PostProcessContext) => void;
46
60
  /** Canvas presentation and shared display settings; obtained from engine.display. */
@@ -73,11 +87,11 @@ export default class Display {
73
87
  private readonly _postProcesses;
74
88
  private _warnedPostProcess;
75
89
  /** Registers a display pass in order and returns an idempotent removal function. Default no passes. WebGPU only; WebGL skips passes with one warning. Runs after composition on every present/toBlob, without tracing or invalidating denoising. A thrown error discards the whole chain, presents the original image and emits engine error. */
76
- addPostProcess(pass: PostProcess): () => void;
90
+ addPostProcess(pass: PostProcess, options?: PostProcessOptions): () => void;
77
91
  private warnPostProcess;
78
92
  private compose;
79
93
  /** Presents a complete image or fallback. Keeps fallback visible until the first intermediate filter finishes. Does not trace. */
80
94
  present(render: Render): Render | undefined;
81
- /** Encodes this render at its actual dimensions using these settings; leaves the display canvas unchanged. */
95
+ /** Encodes the display chain at its final dimensions (default render dimensions), cropping fixed renders before passes using these settings; leaves the display canvas unchanged. */
82
96
  toBlob(render: Render, options?: BlobOptions): Promise<Blob>;
83
97
  }
package/lib/Display.js CHANGED
@@ -68,8 +68,8 @@ export default class Display {
68
68
  _postProcesses = [];
69
69
  _warnedPostProcess = false;
70
70
  /** Registers a display pass in order and returns an idempotent removal function. Default no passes. WebGPU only; WebGL skips passes with one warning. Runs after composition on every present/toBlob, without tracing or invalidating denoising. A thrown error discards the whole chain, presents the original image and emits engine error. */
71
- addPostProcess(pass) {
72
- const entry = { pass };
71
+ addPostProcess(pass, options = {}) {
72
+ const entry = { pass, options: { ...options } };
73
73
  this._postProcesses.push(entry);
74
74
  if (this.engine.backend === 'webgl')
75
75
  this.warnPostProcess();
@@ -82,7 +82,7 @@ export default class Display {
82
82
  console.warn('Display post-processing requires WebGPU; passes are ignored on WebGL.');
83
83
  }
84
84
  }
85
- compose(render) {
85
+ compose(render, exporting = false) {
86
86
  if (render._engine !== this.engine)
87
87
  throw new Error('Render belongs to another engine.');
88
88
  const target = render._output;
@@ -100,9 +100,11 @@ export default class Display {
100
100
  if (this._postProcesses.length && this.engine.backend === 'webgl')
101
101
  this.warnPostProcess();
102
102
  try {
103
- target.applyPostProcesses?.(this._postProcesses.map(entry => entry.pass), render);
103
+ target.applyPostProcesses?.(this._postProcesses.map(entry => entry.pass), render, this._postProcesses.map(entry => entry.options), exporting ? render._exportCrop : undefined);
104
104
  }
105
105
  catch (error) {
106
+ // Preserve the pre-chain export crop even when a callback or size fails.
107
+ target.applyPostProcesses?.([], render, [], exporting ? render._exportCrop : undefined);
106
108
  this.engine._notifyError(error);
107
109
  }
108
110
  return target;
@@ -122,12 +124,13 @@ export default class Display {
122
124
  this.engine._present(target);
123
125
  return selected;
124
126
  }
125
- /** Encodes this render at its actual dimensions using these settings; leaves the display canvas unchanged. */
127
+ /** Encodes the display chain at its final dimensions (default render dimensions), cropping fixed renders before passes using these settings; leaves the display canvas unchanged. */
126
128
  async toBlob(render, options = {}) {
127
- const target = this.compose(render);
129
+ const target = this.compose(render, true);
128
130
  if (!target)
129
131
  throw new Error('Render has no complete image.');
130
- const image = await target.readPresentation();
132
+ const presentation = await target.readPresentation();
133
+ const image = this.engine.backend === 'webgpu' ? presentation : render._cropOutput(presentation);
131
134
  const canvas = createCanvas(image.width, image.height, 'convertToBlob' in this.canvas);
132
135
  const context = canvas.getContext('2d');
133
136
  if (!context)
@@ -1 +1 @@
1
- {"version":3,"file":"Display.js","sourceRoot":"","sources":["../src/Display.ts"],"names":[],"mappings":"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","sourcesContent":["import {displayAovKind, validateAovDisplay, type AovIdComponent} from './renderer/aovDisplay.js';\nexport type {AovIdComponent} from './renderer/aovDisplay.js';\nimport {createCanvas} from './util/canvas.js';\nimport type Engine from './Engine.js';\nimport type Render from './Render.js';\nimport type { ToneMapping, DisplayChannel } from './renderer/RenderingPipeline.js';\nimport type { ColorRGB } from './types.js';\nimport { displaySettingsKey, displayFilterSettingsKey, applyDisplaySettings } from './settings/display.js';\n/** Display image channels. Default final. */\nexport type {DisplayChannel} from './renderer/RenderingPipeline.js';\n/** Image encoding options for Display.toBlob. */\nexport interface BlobOptions {\n /** Browser image MIME type. Default image/png. */ type?: string;\n /** Encoder quality in 0–1. Default undefined uses the browser default. */ quality?: number;\n}\n/** Optional shared display settings; defaults match Display. */\nexport interface DisplaySettings {\n /** Tone mapping transform. Default agx. */ toneMapping?: ToneMapping;\n /** Linear exposure multiplier. Default 1. */ toneMappingExposure?: number;\n /** Contrast multiplier. Default 1. */ contrast?: number;\n /** Saturation multiplier. Default 1. */ saturation?: number;\n /** White balance temperature shift in -1–1. Default 0. */ temperature?: number;\n /** Green/magenta tint in -1–1. Default 0. */ tint?: number;\n /** Vignette strength in 0–1. Default 0. */ vignette?: number;\n /** Bloom intensity; 0 disables it. Default 0. */ bloomIntensity?: number;\n /** Bloom luminance threshold. Default 1. */ bloomThreshold?: number;\n /** Bloom radius in 0–1. Default 0.6. */ bloomRadius?: number;\n /** Extended-range WebGPU presentation, ignored on WebGL. Default false. */ hdrOutputEnabled?: boolean;\n /** Display image channel. Default final. AOV channels require explicit Render.aovs. */ channel?: DisplayChannel;\n /** Finite float32 scene-unit depths mapped to black/white; 0 <= near < far. Default [0, 10]. */ aovDepthRange?: readonly [number, number];\n /** Finite positive float32 scene-unit half-range around the origin for position RGB. Default 1. */ aovPositionScale?: number;\n /** Finite positive float32 input-pixel displacement mapped to full motion brightness. Default 16. */ aovMotionScale?: number;\n /** Stable ID lane to colorize. Default object. */ aovIdComponent?: AovIdComponent;\n /** Linear shadow-catcher color. Default black. */ shadowCatcherColor?: ColorRGB;\n /** Shadow-catcher opacity multiplier. Default 1. */ shadowCatcherAlpha?: number;\n}\n/** Borrowed WebGPU images for one synchronous display pass. */\nexport interface PostProcessContext {\n /** Engine device; may change after recovery. */ device: GPUDevice;\n /** Record commands here; the library finishes and submits it. */ encoder: GPUCommandEncoder;\n /** Read-only composed display image, top row first; no additional color conversion. */ input: GPUTexture;\n /** Fully write this image. Same format and dimensions as input; supports render, storage and copy writes. */ output: GPUTexture;\n /** Actual render width. */ width: number;\n /** Actual render height. */ height: number;\n /** Selected complete render, including a selected fallback. */ render: Render;\n}\n/** Synchronous display pass. Images and encoder are borrowed only during the call; do not retain or destroy them. */\nexport type PostProcess = (context: PostProcessContext) => void;\n/** Canvas presentation and shared display settings; obtained from engine.display. */\nexport default class Display {\n /** Display canvas. Default the supplied Engine canvas, or a new canvas (OffscreenCanvas without document). */ readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n /** Tone mapping transform. Default agx. */ toneMapping: ToneMapping = 'agx';\n /** Linear exposure multiplier. Default 1. */ toneMappingExposure = 1;\n /** Contrast multiplier. Default 1. */ contrast = 1;\n /** Saturation multiplier. Default 1. */ saturation = 1;\n /** White balance temperature shift in -1–1. Default 0. */ temperature = 0;\n /** Green/magenta tint in -1–1. Default 0. */ tint = 0;\n /** Vignette strength in 0–1. Default 0. */ vignette = 0;\n /** Bloom intensity; 0 disables it. Default 0. */ bloomIntensity = 0;\n /** Bloom luminance threshold. Default 1. */ bloomThreshold = 1;\n /** Bloom radius in 0–1. Default 0.6. */ bloomRadius = 0.6;\n /** Extended-range WebGPU presentation, ignored on WebGL. Default false. */ hdrOutputEnabled = false;\n /** Display image channel. Default final. AOV channels require explicit Render.aovs. */ channel: DisplayChannel = 'final';\n /** Finite float32 scene-unit depths mapped to black/white; 0 <= near < far. Default [0, 10]. */ aovDepthRange: readonly [number, number] = [0, 10];\n /** Finite positive float32 scene-unit half-range around the origin for position RGB. Default 1. */ aovPositionScale = 1;\n /** Finite positive float32 input-pixel displacement mapped to full motion brightness. Default 16. */ aovMotionScale = 16;\n /** Stable ID lane to colorize. Default object. */ aovIdComponent: AovIdComponent = 'object';\n /** Linear shadow-catcher color. Default black. */ shadowCatcherColor: ColorRGB = [0, 0, 0];\n /** Shadow-catcher opacity multiplier. Default 1. */ shadowCatcherAlpha = 1;\n private constructor(private readonly engine: Engine, canvas: HTMLCanvasElement | OffscreenCanvas) { this.canvas = canvas; }\n /** @internal */ static _create(engine: Engine, canvas: HTMLCanvasElement | OffscreenCanvas) { return new Display(engine, canvas); }\n /** Resizes the drawing buffer; scale-based renders resize at their next refine. */\n setSize(width: number, height: number) {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) throw new Error('Display dimensions must be positive.');\n width = Math.round(width); height = Math.round(height);\n if (this.canvas.width !== width) this.canvas.width = width;\n if (this.canvas.height !== height) this.canvas.height = height;\n }\n private readonly _filterSettings = new WeakMap<Render, string>();\n private readonly settings = new WeakMap<Render, string>();\n /** @internal */ _configure(render: Render) {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const target = render._output;\n if (!target) return;\n const kind = displayAovKind(this.channel);\n if (!kind && !['final','normal','albedo','shadow','shadowCatcher'].includes(this.channel)) throw new RangeError('Unknown display channel');\n if (kind && !render.aovs.includes(kind)) throw new Error(`AOV ${kind} is not enabled on the selected render`);\n validateAovDisplay({depthRange: this.aovDepthRange, positionScale: this.aovPositionScale, motionScale: this.aovMotionScale, idComponent: this.aovIdComponent});\n const key = displaySettingsKey(this);\n const filterKey = displayFilterSettingsKey(this);\n const previous = this.settings.get(render);\n if (previous !== key) {\n this.settings.set(render, key);\n if (previous !== undefined && this._filterSettings.get(render) !== filterKey) render._displayChanged();\n this._filterSettings.set(render, filterKey);\n }\n target.activate();\n applyDisplaySettings(this, target);\n }\n private readonly _postProcesses: {pass: PostProcess}[] = [];\n private _warnedPostProcess = false;\n /** Registers a display pass in order and returns an idempotent removal function. Default no passes. WebGPU only; WebGL skips passes with one warning. Runs after composition on every present/toBlob, without tracing or invalidating denoising. A thrown error discards the whole chain, presents the original image and emits engine error. */\n addPostProcess(pass: PostProcess): () => void {\n const entry = {pass};\n this._postProcesses.push(entry);\n if (this.engine.backend === 'webgl') this.warnPostProcess();\n return () => { const index = this._postProcesses.indexOf(entry); if (index >= 0) this._postProcesses.splice(index, 1); };\n }\n private warnPostProcess() {\n if (!this._warnedPostProcess) { this._warnedPostProcess = true; console.warn('Display post-processing requires WebGPU; passes are ignored on WebGL.'); }\n }\n private compose(render: Render) {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const target = render._output;\n if (!target) return;\n const kind = displayAovKind(this.channel);\n if (!kind && !target.presentationComplete) return;\n if (kind && !render.aovs.includes(kind)) throw new Error(`AOV ${kind} is not enabled on the selected render`);\n if (kind && target.aovReady && !target.aovReady(kind)) return;\n this._configure(render);\n target.drawPostProcess();\n if (this._postProcesses.length && this.engine.backend === 'webgl') this.warnPostProcess();\n try { target.applyPostProcesses?.(this._postProcesses.map(entry => entry.pass), render); }\n catch (error) { this.engine._notifyError(error); }\n return target;\n }\n /** Presents a complete image or fallback. Keeps fallback visible until the first intermediate filter finishes. Does not trace. */\n present(render: Render): Render | undefined {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const ready = this.channel === 'final' && render.fallback ? render._presentable : render._completeFor(this.channel);\n const fallback = render.fallback;\n const selected = ready ? render : fallback && (this.channel === 'final' ? fallback._presentable : fallback._completeFor(this.channel)) ? fallback : undefined;\n if (!selected) return;\n const target = this.compose(selected);\n if (!target) return;\n this.engine._present(target);\n return selected;\n }\n /** Encodes this render at its actual dimensions using these settings; leaves the display canvas unchanged. */\n async toBlob(render: Render, options: BlobOptions = {}): Promise<Blob> {\n const target = this.compose(render);\n if (!target) throw new Error('Render has no complete image.');\n const image = await target.readPresentation();\n const canvas = createCanvas(image.width, image.height, 'convertToBlob' in this.canvas);\n const context = canvas.getContext('2d') as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;\n if (!context) throw new Error('The image encoding context is not available.');\n const bytes = new Uint8ClampedArray(image.data.length);\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.round(Math.min(1, Math.max(0, image.data[i])) * 255);\n context.putImageData(new ImageData(bytes, image.width, image.height), 0, 0);\n if ('convertToBlob' in canvas) return canvas.convertToBlob({type: options.type ?? 'image/png', quality: options.quality});\n return new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('The image could not be encoded')), options.type ?? 'image/png', options.quality));\n }\n}\n"]}
1
+ {"version":3,"file":"Display.js","sourceRoot":"","sources":["../src/Display.ts"],"names":[],"mappings":"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","sourcesContent":["import {displayAovKind, validateAovDisplay, type AovIdComponent} from './renderer/aovDisplay.js';\nexport type {AovIdComponent} from './renderer/aovDisplay.js';\nimport {createCanvas} from './util/canvas.js';\nimport type Engine from './Engine.js';\nimport type Render from './Render.js';\nimport type { ToneMapping, DisplayChannel } from './renderer/RenderingPipeline.js';\nimport type { ColorRGB } from './types.js';\nimport { displaySettingsKey, displayFilterSettingsKey, applyDisplaySettings } from './settings/display.js';\n/** Display image channels. Default final. */\nexport type {DisplayChannel} from './renderer/RenderingPipeline.js';\n/** Image encoding options for Display.toBlob. */\nexport interface BlobOptions {\n /** Browser image MIME type. Default image/png. */ type?: string;\n /** Encoder quality in 0–1. Default undefined uses the browser default. */ quality?: number;\n}\n/** Optional shared display settings; defaults match Display. */\nexport interface DisplaySettings {\n /** Tone mapping transform. Default agx. */ toneMapping?: ToneMapping;\n /** Linear exposure multiplier. Default 1. */ toneMappingExposure?: number;\n /** Contrast multiplier. Default 1. */ contrast?: number;\n /** Saturation multiplier. Default 1. */ saturation?: number;\n /** White balance temperature shift in -1–1. Default 0. */ temperature?: number;\n /** Green/magenta tint in -1–1. Default 0. */ tint?: number;\n /** Vignette strength in 0–1. Default 0. */ vignette?: number;\n /** Bloom intensity; 0 disables it. Default 0. */ bloomIntensity?: number;\n /** Bloom luminance threshold. Default 1. */ bloomThreshold?: number;\n /** Bloom radius in 0–1. Default 0.6. */ bloomRadius?: number;\n /** Extended-range WebGPU presentation, ignored on WebGL. Default false. */ hdrOutputEnabled?: boolean;\n /** Display image channel. Default final. AOV channels require explicit Render.aovs. */ channel?: DisplayChannel;\n /** Finite float32 scene-unit depths mapped to black/white; 0 <= near < far. Default [0, 10]. */ aovDepthRange?: readonly [number, number];\n /** Finite positive float32 scene-unit half-range around the origin for position RGB. Default 1. */ aovPositionScale?: number;\n /** Finite positive float32 input-pixel displacement mapped to full motion brightness. Default 16. */ aovMotionScale?: number;\n /** Stable ID lane to colorize. Default object. */ aovIdComponent?: AovIdComponent;\n /** Linear shadow-catcher color. Default black. */ shadowCatcherColor?: ColorRGB;\n /** Shadow-catcher opacity multiplier. Default 1. */ shadowCatcherAlpha?: number;\n}\n/** Borrowed WebGPU images for one synchronous display pass. */\nexport interface PostProcessContext {\n /** Engine device; may change after recovery. */ device: GPUDevice;\n /** Record commands here; the library finishes and submits it. */ encoder: GPUCommandEncoder;\n /** Read-only composed display image, top row first; no additional color conversion. */ input: GPUTexture;\n /** Fully write this image. Same format as input; dimensions follow outputSize; supports render, storage and copy writes. */ output: GPUTexture;\n /** Actual render width; pass dimensions are input.width and output.width. */ width: number;\n /** Actual render height; pass dimensions are input.height and output.height. */ height: number;\n /** Selected complete render, including a selected fallback. */ render: Render;\n}\n/** Optional dimensions for a display pass. */\nexport interface PostProcessOptions {\n /** Output dimensions per invocation, bounded by maxTextureDimension2D. Default undefined preserves input size. Display is the current canvas size, including during export. */\n outputSize?: (input: {width: number; height: number}, display: {width: number; height: number}) => {width: number; height: number};\n}\n/** Synchronous display pass. Images and encoder are borrowed only during the call; do not retain or destroy them. */\nexport type PostProcess = (context: PostProcessContext) => void;\n/** Canvas presentation and shared display settings; obtained from engine.display. */\nexport default class Display {\n /** Display canvas. Default the supplied Engine canvas, or a new canvas (OffscreenCanvas without document). */ readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n /** Tone mapping transform. Default agx. */ toneMapping: ToneMapping = 'agx';\n /** Linear exposure multiplier. Default 1. */ toneMappingExposure = 1;\n /** Contrast multiplier. Default 1. */ contrast = 1;\n /** Saturation multiplier. Default 1. */ saturation = 1;\n /** White balance temperature shift in -1–1. Default 0. */ temperature = 0;\n /** Green/magenta tint in -1–1. Default 0. */ tint = 0;\n /** Vignette strength in 0–1. Default 0. */ vignette = 0;\n /** Bloom intensity; 0 disables it. Default 0. */ bloomIntensity = 0;\n /** Bloom luminance threshold. Default 1. */ bloomThreshold = 1;\n /** Bloom radius in 0–1. Default 0.6. */ bloomRadius = 0.6;\n /** Extended-range WebGPU presentation, ignored on WebGL. Default false. */ hdrOutputEnabled = false;\n /** Display image channel. Default final. AOV channels require explicit Render.aovs. */ channel: DisplayChannel = 'final';\n /** Finite float32 scene-unit depths mapped to black/white; 0 <= near < far. Default [0, 10]. */ aovDepthRange: readonly [number, number] = [0, 10];\n /** Finite positive float32 scene-unit half-range around the origin for position RGB. Default 1. */ aovPositionScale = 1;\n /** Finite positive float32 input-pixel displacement mapped to full motion brightness. Default 16. */ aovMotionScale = 16;\n /** Stable ID lane to colorize. Default object. */ aovIdComponent: AovIdComponent = 'object';\n /** Linear shadow-catcher color. Default black. */ shadowCatcherColor: ColorRGB = [0, 0, 0];\n /** Shadow-catcher opacity multiplier. Default 1. */ shadowCatcherAlpha = 1;\n private constructor(private readonly engine: Engine, canvas: HTMLCanvasElement | OffscreenCanvas) { this.canvas = canvas; }\n /** @internal */ static _create(engine: Engine, canvas: HTMLCanvasElement | OffscreenCanvas) { return new Display(engine, canvas); }\n /** Resizes the drawing buffer; scale-based renders resize at their next refine. */\n setSize(width: number, height: number) {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) throw new Error('Display dimensions must be positive.');\n width = Math.round(width); height = Math.round(height);\n if (this.canvas.width !== width) this.canvas.width = width;\n if (this.canvas.height !== height) this.canvas.height = height;\n }\n private readonly _filterSettings = new WeakMap<Render, string>();\n private readonly settings = new WeakMap<Render, string>();\n /** @internal */ _configure(render: Render) {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const target = render._output;\n if (!target) return;\n const kind = displayAovKind(this.channel);\n if (!kind && !['final','normal','albedo','shadow','shadowCatcher'].includes(this.channel)) throw new RangeError('Unknown display channel');\n if (kind && !render.aovs.includes(kind)) throw new Error(`AOV ${kind} is not enabled on the selected render`);\n validateAovDisplay({depthRange: this.aovDepthRange, positionScale: this.aovPositionScale, motionScale: this.aovMotionScale, idComponent: this.aovIdComponent});\n const key = displaySettingsKey(this);\n const filterKey = displayFilterSettingsKey(this);\n const previous = this.settings.get(render);\n if (previous !== key) {\n this.settings.set(render, key);\n if (previous !== undefined && this._filterSettings.get(render) !== filterKey) render._displayChanged();\n this._filterSettings.set(render, filterKey);\n }\n target.activate();\n applyDisplaySettings(this, target);\n }\n private readonly _postProcesses: {pass: PostProcess; options: PostProcessOptions}[] = [];\n private _warnedPostProcess = false;\n /** Registers a display pass in order and returns an idempotent removal function. Default no passes. WebGPU only; WebGL skips passes with one warning. Runs after composition on every present/toBlob, without tracing or invalidating denoising. A thrown error discards the whole chain, presents the original image and emits engine error. */\n addPostProcess(pass: PostProcess, options: PostProcessOptions = {}): () => void {\n const entry = {pass, options: {...options}};\n this._postProcesses.push(entry);\n if (this.engine.backend === 'webgl') this.warnPostProcess();\n return () => { const index = this._postProcesses.indexOf(entry); if (index >= 0) this._postProcesses.splice(index, 1); };\n }\n private warnPostProcess() {\n if (!this._warnedPostProcess) { this._warnedPostProcess = true; console.warn('Display post-processing requires WebGPU; passes are ignored on WebGL.'); }\n }\n private compose(render: Render, exporting = false) {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const target = render._output;\n if (!target) return;\n const kind = displayAovKind(this.channel);\n if (!kind && !target.presentationComplete) return;\n if (kind && !render.aovs.includes(kind)) throw new Error(`AOV ${kind} is not enabled on the selected render`);\n if (kind && target.aovReady && !target.aovReady(kind)) return;\n this._configure(render);\n target.drawPostProcess();\n if (this._postProcesses.length && this.engine.backend === 'webgl') this.warnPostProcess();\n try { target.applyPostProcesses?.(this._postProcesses.map(entry => entry.pass), render, this._postProcesses.map(entry => entry.options), exporting ? render._exportCrop : undefined); }\n catch (error) {\n // Preserve the pre-chain export crop even when a callback or size fails.\n target.applyPostProcesses?.([], render, [], exporting ? render._exportCrop : undefined);\n this.engine._notifyError(error);\n }\n return target;\n }\n /** Presents a complete image or fallback. Keeps fallback visible until the first intermediate filter finishes. Does not trace. */\n present(render: Render): Render | undefined {\n if (render._engine !== this.engine) throw new Error('Render belongs to another engine.');\n const ready = this.channel === 'final' && render.fallback ? render._presentable : render._completeFor(this.channel);\n const fallback = render.fallback;\n const selected = ready ? render : fallback && (this.channel === 'final' ? fallback._presentable : fallback._completeFor(this.channel)) ? fallback : undefined;\n if (!selected) return;\n const target = this.compose(selected);\n if (!target) return;\n this.engine._present(target);\n return selected;\n }\n /** Encodes the display chain at its final dimensions (default render dimensions), cropping fixed renders before passes using these settings; leaves the display canvas unchanged. */\n async toBlob(render: Render, options: BlobOptions = {}): Promise<Blob> {\n const target = this.compose(render, true);\n if (!target) throw new Error('Render has no complete image.');\n const presentation = await target.readPresentation();\n const image = this.engine.backend === 'webgpu' ? presentation : render._cropOutput(presentation);\n const canvas = createCanvas(image.width, image.height, 'convertToBlob' in this.canvas);\n const context = canvas.getContext('2d') as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;\n if (!context) throw new Error('The image encoding context is not available.');\n const bytes = new Uint8ClampedArray(image.data.length);\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.round(Math.min(1, Math.max(0, image.data[i])) * 255);\n context.putImageData(new ImageData(bytes, image.width, image.height), 0, 0);\n if ('convertToBlob' in canvas) return canvas.convertToBlob({type: options.type ?? 'image/png', quality: options.quality});\n return new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('The image could not be encoded')), options.type ?? 'image/png', options.quality));\n }\n}\n"]}
package/lib/Engine.d.ts CHANGED
@@ -67,6 +67,8 @@ export default class Engine {
67
67
  private _bvhWorkerPool?;
68
68
  private _bvhPreparationRevision;
69
69
  private _bvhPreparation?;
70
+ private _bvhPreparationController?;
71
+ private _bvhFailedRevision;
70
72
  private _bvhPreparationTail;
71
73
  private _tinyBvhUnavailable;
72
74
  private _bvhWorkerUnavailable;
@@ -116,6 +118,7 @@ export default class Engine {
116
118
  private _pendingBackground;
117
119
  private _disposed;
118
120
  private _readingDepth;
121
+ private _surfaceQuery?;
119
122
  private _renders;
120
123
  private _targetCounter;
121
124
  private _presented?;
@@ -132,7 +135,7 @@ export default class Engine {
132
135
  /** Initializes once; concurrent/repeated calls share the result. */
133
136
  init(): Promise<EngineInfo>;
134
137
  /** Initialization info. Default undefined until init resolves. */ get info(): EngineInfo | undefined;
135
- /** Active backend. Default undefined until initialization completes. */ get backend(): "webgpu" | "webgl" | undefined;
138
+ /** Active backend. Default undefined until initialization completes. */ get backend(): "webgl" | "webgpu" | undefined;
136
139
  /** GPU device. Default undefined before initialization and on WebGL. */ get device(): GPUDevice | undefined;
137
140
  /** Subscribes to shared progress/errors/device loss; returns unsubscribe. */
138
141
  on<K extends keyof EngineEvents>(event: K, listener: (value: EngineEvents[K]) => void): () => void;