@zephyr3d/scene 0.9.26 → 0.10.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 (506) hide show
  1. package/README.md +60 -27
  2. package/dist/animation/animation_bank.js +634 -0
  3. package/dist/animation/animation_bank.js.map +1 -0
  4. package/dist/animation/animation_bank_gpu.js +256 -0
  5. package/dist/animation/animation_bank_gpu.js.map +1 -0
  6. package/dist/animation/animationset.js +146 -30
  7. package/dist/animation/animationset.js.map +1 -1
  8. package/dist/animation/cloth/gpu_cloth_component.js +406 -0
  9. package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
  10. package/dist/animation/cloth/gpu_cloth_system.js +332 -202
  11. package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
  12. package/dist/animation/gpu_animation_driver.js +289 -0
  13. package/dist/animation/gpu_animation_driver.js.map +1 -0
  14. package/dist/animation/joint_dynamics/collision.js +112 -1
  15. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  16. package/dist/animation/joint_dynamics/controller.js +6 -0
  17. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  18. package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
  19. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  20. package/dist/animation/joint_dynamics/solver.js +50 -12
  21. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  22. package/dist/animation/joint_dynamics/types.js.map +1 -1
  23. package/dist/animation/skeleton.js +300 -56
  24. package/dist/animation/skeleton.js.map +1 -1
  25. package/dist/animation/skin_palette_atlas.js +252 -0
  26. package/dist/animation/skin_palette_atlas.js.map +1 -0
  27. package/dist/animation/spring/multi_chain_spring_system.js +298 -38
  28. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  29. package/dist/animation/spring/spring_chain.js +2 -0
  30. package/dist/animation/spring/spring_chain.js.map +1 -1
  31. package/dist/animation/spring/spring_collider.js +159 -21
  32. package/dist/animation/spring/spring_collider.js.map +1 -1
  33. package/dist/animation/spring/spring_particle.js +1 -0
  34. package/dist/animation/spring/spring_particle.js.map +1 -1
  35. package/dist/animation/spring/spring_solver.js +173 -0
  36. package/dist/animation/spring/spring_solver.js.map +1 -0
  37. package/dist/animation/spring/spring_system.js +249 -24
  38. package/dist/animation/spring/spring_system.js.map +1 -1
  39. package/dist/animation/spring_modifier.js +32 -1
  40. package/dist/animation/spring_modifier.js.map +1 -1
  41. package/dist/app/app.js +26 -0
  42. package/dist/app/app.js.map +1 -1
  43. package/dist/app/engine.js +86 -15
  44. package/dist/app/engine.js.map +1 -1
  45. package/dist/app/gamepad.js +191 -0
  46. package/dist/app/gamepad.js.map +1 -0
  47. package/dist/app/inputactions.js +271 -0
  48. package/dist/app/inputactions.js.map +1 -0
  49. package/dist/app/inputmgr.js +331 -22
  50. package/dist/app/inputmgr.js.map +1 -1
  51. package/dist/app/loading.js +138 -0
  52. package/dist/app/loading.js.map +1 -0
  53. package/dist/app/pipelinecache.js +209 -0
  54. package/dist/app/pipelinecache.js.map +1 -0
  55. package/dist/app/runtimescript.js +25 -0
  56. package/dist/app/runtimescript.js.map +1 -1
  57. package/dist/app/scriptingsystem.js +54 -0
  58. package/dist/app/scriptingsystem.js.map +1 -1
  59. package/dist/app/scriptregistry.js +7 -2
  60. package/dist/app/scriptregistry.js.map +1 -1
  61. package/dist/asset/asset_manifest.js +59 -0
  62. package/dist/asset/asset_manifest.js.map +1 -0
  63. package/dist/asset/asset_preload.js +258 -0
  64. package/dist/asset/asset_preload.js.map +1 -0
  65. package/dist/asset/assetmanager.js +117 -65
  66. package/dist/asset/assetmanager.js.map +1 -1
  67. package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
  68. package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  69. package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  70. package/dist/asset/loaders/ktx2/basis_location.js +17 -0
  71. package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
  72. package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
  73. package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
  74. package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
  75. package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
  76. package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
  77. package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
  78. package/dist/asset/mesh_lod_builder.js +104 -0
  79. package/dist/asset/mesh_lod_builder.js.map +1 -0
  80. package/dist/asset/mesh_settings.js +111 -0
  81. package/dist/asset/mesh_settings.js.map +1 -0
  82. package/dist/asset/meshopt_decoder.js +18 -0
  83. package/dist/asset/meshopt_decoder.js.map +1 -0
  84. package/dist/asset/meshopt_simplifier.js +26 -0
  85. package/dist/asset/meshopt_simplifier.js.map +1 -0
  86. package/dist/asset/model.js +177 -3
  87. package/dist/asset/model.js.map +1 -1
  88. package/dist/asset/texture_settings.js +231 -0
  89. package/dist/asset/texture_settings.js.map +1 -0
  90. package/dist/asset/zmsh_binary.js +131 -0
  91. package/dist/asset/zmsh_binary.js.map +1 -0
  92. package/dist/asset/zsdf_binary.js +111 -0
  93. package/dist/asset/zsdf_binary.js.map +1 -0
  94. package/dist/blitter/bilateralblur.js +1 -2
  95. package/dist/blitter/bilateralblur.js.map +1 -1
  96. package/dist/blitter/gaussianblur.js +1 -6
  97. package/dist/blitter/gaussianblur.js.map +1 -1
  98. package/dist/camera/base.js +31 -10
  99. package/dist/camera/base.js.map +1 -1
  100. package/dist/camera/camera.js +178 -200
  101. package/dist/camera/camera.js.map +1 -1
  102. package/dist/camera/follow.js +342 -0
  103. package/dist/camera/follow.js.map +1 -0
  104. package/dist/camera/fps.js +9 -7
  105. package/dist/camera/fps.js.map +1 -1
  106. package/dist/camera/orbit.js +46 -19
  107. package/dist/camera/orbit.js.map +1 -1
  108. package/dist/index.d.ts +30475 -19865
  109. package/dist/index.js +67 -12
  110. package/dist/index.js.map +1 -1
  111. package/dist/material/blinn.js +3 -0
  112. package/dist/material/blinn.js.map +1 -1
  113. package/dist/material/eye.js +1 -1
  114. package/dist/material/eye.js.map +1 -1
  115. package/dist/material/hair.js +2 -3
  116. package/dist/material/hair.js.map +1 -1
  117. package/dist/material/hairstrand.js +235 -235
  118. package/dist/material/hairstrand.js.map +1 -1
  119. package/dist/material/lambert.js +4 -1
  120. package/dist/material/lambert.js.map +1 -1
  121. package/dist/material/material.js +362 -300
  122. package/dist/material/material.js.map +1 -1
  123. package/dist/material/meshmaterial.js +118 -45
  124. package/dist/material/meshmaterial.js.map +1 -1
  125. package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
  126. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  127. package/dist/material/mixins/lightmodel/lambert.js +1 -1
  128. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  129. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
  130. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  131. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
  132. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  133. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
  134. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  135. package/dist/material/mixins/lit.js +223 -83
  136. package/dist/material/mixins/lit.js.map +1 -1
  137. package/dist/material/mixins/pbr/common.js +31 -206
  138. package/dist/material/mixins/pbr/common.js.map +1 -1
  139. package/dist/material/mtoon.js +1 -1
  140. package/dist/material/mtoon.js.map +1 -1
  141. package/dist/material/pbrmr.js +3 -0
  142. package/dist/material/pbrmr.js.map +1 -1
  143. package/dist/material/pbrsg.js +3 -0
  144. package/dist/material/pbrsg.js.map +1 -1
  145. package/dist/material/sdf_debug.js +78 -0
  146. package/dist/material/sdf_debug.js.map +1 -0
  147. package/dist/material/shader/helper.js +733 -190
  148. package/dist/material/shader/helper.js.map +1 -1
  149. package/dist/material/shader/terrain_blend.js +204 -43
  150. package/dist/material/shader/terrain_blend.js.map +1 -1
  151. package/dist/material/skin.js +331 -297
  152. package/dist/material/skin.js.map +1 -1
  153. package/dist/material/skinprofile.js +1058 -0
  154. package/dist/material/skinprofile.js.map +1 -0
  155. package/dist/material/sss.js +353 -0
  156. package/dist/material/sss.js.map +1 -0
  157. package/dist/material/sss_kernel.js +265 -0
  158. package/dist/material/sss_kernel.js.map +1 -0
  159. package/dist/material/sssprofile.js +879 -0
  160. package/dist/material/sssprofile.js.map +1 -0
  161. package/dist/material/subsurfaceprofile.js +4 -8
  162. package/dist/material/subsurfaceprofile.js.map +1 -1
  163. package/dist/material/terrain-cm.js +464 -188
  164. package/dist/material/terrain-cm.js.map +1 -1
  165. package/dist/material/unlit.js +3 -0
  166. package/dist/material/unlit.js.map +1 -1
  167. package/dist/material/water.js +2009 -201
  168. package/dist/material/water.js.map +1 -1
  169. package/dist/node_modules/meshoptimizer/LICENSE +21 -0
  170. package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
  171. package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
  172. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
  173. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
  174. package/dist/physics/character.js +226 -0
  175. package/dist/physics/character.js.map +1 -0
  176. package/dist/physics/collider.js +209 -0
  177. package/dist/physics/collider.js.map +1 -0
  178. package/dist/physics/events.js +79 -0
  179. package/dist/physics/events.js.map +1 -0
  180. package/dist/physics/joint.js +272 -0
  181. package/dist/physics/joint.js.map +1 -0
  182. package/dist/physics/node_physics.js +234 -0
  183. package/dist/physics/node_physics.js.map +1 -0
  184. package/dist/physics/part.js +91 -0
  185. package/dist/physics/part.js.map +1 -0
  186. package/dist/physics/rigid_body.js +379 -0
  187. package/dist/physics/rigid_body.js.map +1 -0
  188. package/dist/physics/vehicle.js +335 -0
  189. package/dist/physics/vehicle.js.map +1 -0
  190. package/dist/physics/world.js +25 -0
  191. package/dist/physics/world.js.map +1 -0
  192. package/dist/posteffect/bloom.js +49 -14
  193. package/dist/posteffect/bloom.js.map +1 -1
  194. package/dist/posteffect/motionblur.js +668 -63
  195. package/dist/posteffect/motionblur.js.map +1 -1
  196. package/dist/posteffect/posteffect.js +12 -1
  197. package/dist/posteffect/posteffect.js.map +1 -1
  198. package/dist/posteffect/postsss.js +572 -0
  199. package/dist/posteffect/postsss.js.map +1 -0
  200. package/dist/posteffect/sao.js +48 -50
  201. package/dist/posteffect/sao.js.map +1 -1
  202. package/dist/posteffect/skinsss.js +605 -355
  203. package/dist/posteffect/skinsss.js.map +1 -1
  204. package/dist/posteffect/ssgi.js +91 -162
  205. package/dist/posteffect/ssgi.js.map +1 -1
  206. package/dist/posteffect/ssr.js +3 -11
  207. package/dist/posteffect/ssr.js.map +1 -1
  208. package/dist/posteffect/sss.js +34 -15
  209. package/dist/posteffect/sss.js.map +1 -1
  210. package/dist/posteffect/taa.js +1 -0
  211. package/dist/posteffect/taa.js.map +1 -1
  212. package/dist/render/abuffer_oit.js +40 -39
  213. package/dist/render/abuffer_oit.js.map +1 -1
  214. package/dist/render/clipmap.js +328 -62
  215. package/dist/render/clipmap.js.map +1 -1
  216. package/dist/render/cluster_light.js +958 -224
  217. package/dist/render/cluster_light.js.map +1 -1
  218. package/dist/render/cull_visitor.js +26 -5
  219. package/dist/render/cull_visitor.js.map +1 -1
  220. package/dist/render/debug_views.js +37 -0
  221. package/dist/render/debug_views.js.map +1 -0
  222. package/dist/render/depthpass.js +5 -1
  223. package/dist/render/depthpass.js.map +1 -1
  224. package/dist/render/drawable_mixin.js +53 -6
  225. package/dist/render/drawable_mixin.js.map +1 -1
  226. package/dist/render/dualdepthpeeling_oit.js +53 -53
  227. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  228. package/dist/render/envlight.js +196 -113
  229. package/dist/render/envlight.js.map +1 -1
  230. package/dist/render/fbm_wavegenerator.js +8 -13
  231. package/dist/render/fbm_wavegenerator.js.map +1 -1
  232. package/dist/render/fft_wavegenerator.js +268 -136
  233. package/dist/render/fft_wavegenerator.js.map +1 -1
  234. package/dist/render/gerstner_wavegenerator.js +113 -28
  235. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  236. package/dist/render/gi/probe_volume.js +1334 -0
  237. package/dist/render/gi/probe_volume.js.map +1 -0
  238. package/dist/render/gi/scene_gi.js +282 -0
  239. package/dist/render/gi/scene_gi.js.map +1 -0
  240. package/dist/render/gi/voxel_atlas.js +198 -0
  241. package/dist/render/gi/voxel_atlas.js.map +1 -0
  242. package/dist/render/gi/voxel_lighting.js +652 -0
  243. package/dist/render/gi/voxel_lighting.js.map +1 -0
  244. package/dist/render/gi/voxel_scene.js +642 -0
  245. package/dist/render/gi/voxel_scene.js.map +1 -0
  246. package/dist/render/gi/voxelize_pass.js +223 -0
  247. package/dist/render/gi/voxelize_pass.js.map +1 -0
  248. package/dist/render/globalbindgroup_allocator.js +3 -2
  249. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  250. package/dist/render/gpu_picking.js +9 -36
  251. package/dist/render/gpu_picking.js.map +1 -1
  252. package/dist/render/hzb.js +80 -22
  253. package/dist/render/hzb.js.map +1 -1
  254. package/dist/render/instance_culling.js +499 -0
  255. package/dist/render/instance_culling.js.map +1 -0
  256. package/dist/render/lightpass.js +23 -9
  257. package/dist/render/lightpass.js.map +1 -1
  258. package/dist/render/lod.js +46 -0
  259. package/dist/render/lod.js.map +1 -0
  260. package/dist/render/oit.js +11 -11
  261. package/dist/render/oit.js.map +1 -1
  262. package/dist/render/primitive.js +301 -9
  263. package/dist/render/primitive.js.map +1 -1
  264. package/dist/render/primitive_readback.js +209 -0
  265. package/dist/render/primitive_readback.js.map +1 -0
  266. package/dist/render/render_queue.js +198 -11
  267. package/dist/render/render_queue.js.map +1 -1
  268. package/dist/render/renderer.js +14 -7
  269. package/dist/render/renderer.js.map +1 -1
  270. package/dist/render/rendergraph/blackboard.js +15 -1
  271. package/dist/render/rendergraph/blackboard.js.map +1 -1
  272. package/dist/render/rendergraph/executor.js +136 -0
  273. package/dist/render/rendergraph/executor.js.map +1 -1
  274. package/dist/render/rendergraph/forward_plus_builder.js +585 -59
  275. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  276. package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
  277. package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
  278. package/dist/render/rendergraph/history_resources.js +8 -1
  279. package/dist/render/rendergraph/history_resources.js.map +1 -1
  280. package/dist/render/rendergraph/scene_render_context.js +31 -2
  281. package/dist/render/rendergraph/scene_render_context.js.map +1 -1
  282. package/dist/render/rendergraph/types.js.map +1 -1
  283. package/dist/render/renderpass.js +28 -5
  284. package/dist/render/renderpass.js.map +1 -1
  285. package/dist/render/sdf/box_allocator.js +98 -0
  286. package/dist/render/sdf/box_allocator.js.map +1 -0
  287. package/dist/render/sdf/global_sdf.js +865 -0
  288. package/dist/render/sdf/global_sdf.js.map +1 -0
  289. package/dist/render/sdf/scene_sdf.js +562 -0
  290. package/dist/render/sdf/scene_sdf.js.map +1 -0
  291. package/dist/render/sdf/sdf_atlas.js +227 -0
  292. package/dist/render/sdf/sdf_atlas.js.map +1 -0
  293. package/dist/render/sdf/sdf_programs.js +42 -0
  294. package/dist/render/sdf/sdf_programs.js.map +1 -0
  295. package/dist/render/sdf/sdf_view.js +199 -0
  296. package/dist/render/sdf/sdf_view.js.map +1 -0
  297. package/dist/render/sdf/waterline.js +610 -0
  298. package/dist/render/sdf/waterline.js.map +1 -0
  299. package/dist/render/shadow_mask_pass.js +142 -25
  300. package/dist/render/shadow_mask_pass.js.map +1 -1
  301. package/dist/render/shore_wave_generator.js +1193 -0
  302. package/dist/render/shore_wave_generator.js.map +1 -0
  303. package/dist/render/sky.js +516 -86
  304. package/dist/render/sky.js.map +1 -1
  305. package/dist/render/transmission_thickness_pass.js +477 -0
  306. package/dist/render/transmission_thickness_pass.js.map +1 -0
  307. package/dist/render/underwater.js +524 -0
  308. package/dist/render/underwater.js.map +1 -0
  309. package/dist/render/virtualtexture/virtual_texture.js +1214 -0
  310. package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
  311. package/dist/render/volumetric_cloud/cloud_common.js +472 -0
  312. package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
  313. package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
  314. package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
  315. package/dist/render/volumetric_cloud/debug_view.js +131 -0
  316. package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
  317. package/dist/render/volumetric_cloud/resources.js +380 -0
  318. package/dist/render/volumetric_cloud/resources.js.map +1 -0
  319. package/dist/render/volumetric_cloud/sky_cache.js +380 -0
  320. package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
  321. package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
  322. package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
  323. package/dist/render/water_caustics.js +1195 -0
  324. package/dist/render/water_caustics.js.map +1 -0
  325. package/dist/render/water_foam.js +627 -0
  326. package/dist/render/water_foam.js.map +1 -0
  327. package/dist/render/water_interaction.js +1795 -0
  328. package/dist/render/water_interaction.js.map +1 -0
  329. package/dist/render/weightedblended_oit.js +36 -36
  330. package/dist/render/weightedblended_oit.js.map +1 -1
  331. package/dist/scene/basesprite.js +9 -4
  332. package/dist/scene/basesprite.js.map +1 -1
  333. package/dist/scene/batchgroup.js +108 -12
  334. package/dist/scene/batchgroup.js.map +1 -1
  335. package/dist/scene/environment.js +28 -7
  336. package/dist/scene/environment.js.map +1 -1
  337. package/dist/scene/foliage.js +1020 -0
  338. package/dist/scene/foliage.js.map +1 -0
  339. package/dist/scene/foliage_buckets.js +147 -0
  340. package/dist/scene/foliage_buckets.js.map +1 -0
  341. package/dist/scene/foliage_mask.js +358 -0
  342. package/dist/scene/foliage_mask.js.map +1 -0
  343. package/dist/scene/hair.js +15 -4
  344. package/dist/scene/hair.js.map +1 -1
  345. package/dist/scene/light.js +179 -13
  346. package/dist/scene/light.js.map +1 -1
  347. package/dist/scene/mesh.js +327 -45
  348. package/dist/scene/mesh.js.map +1 -1
  349. package/dist/scene/meshdrawable.js +6 -0
  350. package/dist/scene/meshdrawable.js.map +1 -1
  351. package/dist/scene/msdftext.js +9 -3
  352. package/dist/scene/msdftext.js.map +1 -1
  353. package/dist/scene/msdftextsprite.js +9 -3
  354. package/dist/scene/msdftextsprite.js.map +1 -1
  355. package/dist/scene/particlesys.js +8 -2
  356. package/dist/scene/particlesys.js.map +1 -1
  357. package/dist/scene/physics_settings.js +157 -0
  358. package/dist/scene/physics_settings.js.map +1 -0
  359. package/dist/scene/scene.js +190 -44
  360. package/dist/scene/scene.js.map +1 -1
  361. package/dist/scene/scene_node.js +147 -6
  362. package/dist/scene/scene_node.js.map +1 -1
  363. package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
  364. package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
  365. package/dist/scene/terrain-cm/grass.js +881 -42
  366. package/dist/scene/terrain-cm/grass.js.map +1 -1
  367. package/dist/scene/terrain-cm/grass_gpu.js +929 -0
  368. package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
  369. package/dist/scene/terrain-cm/grassmaterial.js +180 -9
  370. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  371. package/dist/scene/terrain-cm/terrain-cm.js +465 -36
  372. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  373. package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
  374. package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
  375. package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
  376. package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
  377. package/dist/scene/update_queue.js +54 -0
  378. package/dist/scene/update_queue.js.map +1 -0
  379. package/dist/scene/water.js +997 -96
  380. package/dist/scene/water.js.map +1 -1
  381. package/dist/scene/water_surface_sampler.js +324 -0
  382. package/dist/scene/water_surface_sampler.js.map +1 -0
  383. package/dist/scene/wind.js +317 -0
  384. package/dist/scene/wind.js.map +1 -0
  385. package/dist/shaders/atmosphere.js +388 -166
  386. package/dist/shaders/atmosphere.js.map +1 -1
  387. package/dist/shaders/fog.js +100 -32
  388. package/dist/shaders/fog.js.map +1 -1
  389. package/dist/shaders/gi.js +365 -0
  390. package/dist/shaders/gi.js.map +1 -0
  391. package/dist/shaders/ltc_rect.js +202 -0
  392. package/dist/shaders/ltc_rect.js.map +1 -0
  393. package/dist/shaders/misc.js +1 -10
  394. package/dist/shaders/misc.js.map +1 -1
  395. package/dist/shaders/noise.js +5 -1
  396. package/dist/shaders/noise.js.map +1 -1
  397. package/dist/shaders/probes.js +309 -0
  398. package/dist/shaders/probes.js.map +1 -0
  399. package/dist/shaders/sdf.js +537 -0
  400. package/dist/shaders/sdf.js.map +1 -0
  401. package/dist/shaders/shadow.js +104 -70
  402. package/dist/shaders/shadow.js.map +1 -1
  403. package/dist/shaders/skin_brdf.js +243 -0
  404. package/dist/shaders/skin_brdf.js.map +1 -0
  405. package/dist/shaders/ssr.js +6 -159
  406. package/dist/shaders/ssr.js.map +1 -1
  407. package/dist/shaders/temporal.js +38 -27
  408. package/dist/shaders/temporal.js.map +1 -1
  409. package/dist/shaders/water.js +40 -87
  410. package/dist/shaders/water.js.map +1 -1
  411. package/dist/shaders/water_medium.js +42 -0
  412. package/dist/shaders/water_medium.js.map +1 -0
  413. package/dist/shadow/dom.js +110 -19
  414. package/dist/shadow/dom.js.map +1 -1
  415. package/dist/shadow/esm.js +3 -3
  416. package/dist/shadow/esm.js.map +1 -1
  417. package/dist/shadow/pcf_opt.js +15 -48
  418. package/dist/shadow/pcf_opt.js.map +1 -1
  419. package/dist/shadow/pcf_pd.js +15 -53
  420. package/dist/shadow/pcf_pd.js.map +1 -1
  421. package/dist/shadow/pcss.js +34 -9
  422. package/dist/shadow/pcss.js.map +1 -1
  423. package/dist/shadow/shadow_impl.js +3 -8
  424. package/dist/shadow/shadow_impl.js.map +1 -1
  425. package/dist/shadow/shadow_region.js +87 -49
  426. package/dist/shadow/shadow_region.js.map +1 -1
  427. package/dist/shadow/shadowmapper.js +93 -53
  428. package/dist/shadow/shadowmapper.js.map +1 -1
  429. package/dist/shadow/ssm.js +18 -72
  430. package/dist/shadow/ssm.js.map +1 -1
  431. package/dist/shadow/vsm.js +3 -3
  432. package/dist/shadow/vsm.js.map +1 -1
  433. package/dist/shapes/shape.js +44 -1
  434. package/dist/shapes/shape.js.map +1 -1
  435. package/dist/utility/buoyancy.js +287 -0
  436. package/dist/utility/buoyancy.js.map +1 -0
  437. package/dist/utility/physical.js +10 -1
  438. package/dist/utility/physical.js.map +1 -1
  439. package/dist/utility/pmrem.js +17 -43
  440. package/dist/utility/pmrem.js.map +1 -1
  441. package/dist/utility/sdf/bake.js +435 -0
  442. package/dist/utility/sdf/bake.js.map +1 -0
  443. package/dist/utility/sdf/bvh.js +576 -0
  444. package/dist/utility/sdf/bvh.js.map +1 -0
  445. package/dist/utility/serialization/manager.js +36 -6
  446. package/dist/utility/serialization/manager.js.map +1 -1
  447. package/dist/utility/serialization/scene/animation.js +77 -7
  448. package/dist/utility/serialization/scene/animation.js.map +1 -1
  449. package/dist/utility/serialization/scene/batch.js +11 -4
  450. package/dist/utility/serialization/scene/batch.js.map +1 -1
  451. package/dist/utility/serialization/scene/camera.js +140 -279
  452. package/dist/utility/serialization/scene/camera.js.map +1 -1
  453. package/dist/utility/serialization/scene/cloth.js +25 -0
  454. package/dist/utility/serialization/scene/cloth.js.map +1 -0
  455. package/dist/utility/serialization/scene/common.js +14 -6
  456. package/dist/utility/serialization/scene/common.js.map +1 -1
  457. package/dist/utility/serialization/scene/foliage.js +821 -0
  458. package/dist/utility/serialization/scene/foliage.js.map +1 -0
  459. package/dist/utility/serialization/scene/hair.js +11 -4
  460. package/dist/utility/serialization/scene/hair.js.map +1 -1
  461. package/dist/utility/serialization/scene/light.js +115 -7
  462. package/dist/utility/serialization/scene/light.js.map +1 -1
  463. package/dist/utility/serialization/scene/material.js +388 -220
  464. package/dist/utility/serialization/scene/material.js.map +1 -1
  465. package/dist/utility/serialization/scene/mesh.js +69 -4
  466. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  467. package/dist/utility/serialization/scene/node.js +140 -5
  468. package/dist/utility/serialization/scene/node.js.map +1 -1
  469. package/dist/utility/serialization/scene/node_physics.js +1318 -0
  470. package/dist/utility/serialization/scene/node_physics.js.map +1 -0
  471. package/dist/utility/serialization/scene/particle.js +11 -4
  472. package/dist/utility/serialization/scene/particle.js.map +1 -1
  473. package/dist/utility/serialization/scene/physics.js +120 -0
  474. package/dist/utility/serialization/scene/physics.js.map +1 -0
  475. package/dist/utility/serialization/scene/primitive.js +124 -10
  476. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  477. package/dist/utility/serialization/scene/scene.js +1687 -51
  478. package/dist/utility/serialization/scene/scene.js.map +1 -1
  479. package/dist/utility/serialization/scene/sprite.js +11 -4
  480. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  481. package/dist/utility/serialization/scene/terrain.js +163 -33
  482. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  483. package/dist/utility/serialization/scene/text.js +11 -4
  484. package/dist/utility/serialization/scene/text.js.map +1 -1
  485. package/dist/utility/serialization/scene/water.js +2473 -114
  486. package/dist/utility/serialization/scene/water.js.map +1 -1
  487. package/dist/utility/serialization/types.js.map +1 -1
  488. package/dist/utility/shprojector.js +97 -109
  489. package/dist/utility/shprojector.js.map +1 -1
  490. package/dist/utility/textures/foam.js +170 -0
  491. package/dist/utility/textures/foam.js.map +1 -0
  492. package/dist/utility/textures/ggxlut.js +17 -43
  493. package/dist/utility/textures/ggxlut.js.map +1 -1
  494. package/dist/utility/textures/ltcdata.js +4 -4
  495. package/dist/utility/textures/ltcdata.js.map +1 -1
  496. package/dist/utility/textures/ltclut.js +16 -6
  497. package/dist/utility/textures/ltclut.js.map +1 -1
  498. package/dist/utility/unsupported.js +24 -0
  499. package/dist/utility/unsupported.js.map +1 -0
  500. package/dist/values.js +13 -1
  501. package/dist/values.js.map +1 -1
  502. package/package.json +25 -12
  503. package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
  504. package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  505. package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  506. package/src/asset/meshoptimizer/LICENSE +21 -0
@@ -0,0 +1,1214 @@
1
+ import { Disposable } from '@zephyr3d/base';
2
+
3
+ function virtualMipChain(a, b, c) {
4
+ const [width, height, pageSize] = c === undefined ? [
5
+ a,
6
+ a,
7
+ b
8
+ ] : [
9
+ a,
10
+ b,
11
+ c
12
+ ];
13
+ if (!isPowerOf2(width) || !isPowerOf2(height) || !isPowerOf2(pageSize) || pageSize > Math.min(width, height)) {
14
+ throw new Error('virtualMipChain(): sizes must be powers of two with pageSize <= virtual size');
15
+ }
16
+ const levels = [];
17
+ for(let x = width / pageSize, y = height / pageSize;; x = Math.max(1, x >> 1), y = Math.max(1, y >> 1)){
18
+ levels.push({
19
+ pagesX: x,
20
+ pagesY: y
21
+ });
22
+ if (x === 1 && y === 1) {
23
+ break;
24
+ }
25
+ }
26
+ return levels;
27
+ }
28
+ const INDEX_NONE = 0xffffffff;
29
+ const MAX_LEVELS = 16;
30
+ const MAX_RECTS = 8;
31
+ const LIST_LRU = 0;
32
+ const LIST_AVAILABLE = 1;
33
+ const LIST_EMPTY = 2;
34
+ const LIST_REQUESTED = 3;
35
+ // Page table encoding
36
+ const PT_PHYS_MASK = 0xffff;
37
+ const PT_LOD_SHIFT = 20;
38
+ const PT_LOD_MASK = 0x3f;
39
+ const PT_THIS_LOD = 0x40000000;
40
+ const PT_ANY_LOD = 0x80000000;
41
+ /** Resident at this level; JS bitwise results are signed, hence the unsigned shift */ const PT_MAPPED = (PT_ANY_LOD | PT_THIS_LOD) >>> 0;
42
+ // Physical page metadata flags
43
+ const META_ALLOCATED = 1;
44
+ const META_DIRTY = 2;
45
+ const META_PINNED = 4;
46
+ // Counter slots
47
+ const C_LOAD = 0;
48
+ const C_FILL = 1;
49
+ const C_CUT = 2;
50
+ const C_REMAIN = 3;
51
+ const C_TIE = 4;
52
+ const C_HIST = 8;
53
+ const C_STAT_REQUESTED = 40;
54
+ const C_STAT_ALLOCATED = 41;
55
+ const C_STAT_EVICTED = 42;
56
+ const C_STAT_REFILLED = 43;
57
+ const C_STAT_DROPPED = 44;
58
+ const C_STAT_EXPIRED = 45;
59
+ const NUM_COUNTERS = 64;
60
+ // Residency mip bias
61
+ const RESIDENCY_ADJUSTMENT_RATE = 0.2;
62
+ const RESIDENCY_MAX_MIP_BIAS = 4;
63
+ const RESIDENCY_LOCKED_UPPER_BOUND = 0.65;
64
+ const HIST_PINNED_BUCKET = 31;
65
+ const GROUP = 64;
66
+ const PACK_GROUP = 256;
67
+ const FILL_GROUP = 8;
68
+ function isPowerOf2(n) {
69
+ return n > 0 && (n & n - 1) === 0;
70
+ }
71
+ /**
72
+ * GPU driven sparse virtual texture.
73
+ *
74
+ * Per frame: shaders mark pages with {@link VirtualTexture.request}, then {@link VirtualTexture.update}
75
+ * maps pages, {@link VirtualTexture.fill} lets the owner write the new pages, and shaders read
76
+ * through {@link VirtualTexture.resolve}. All of it runs on the GPU in the same frame.
77
+ *
78
+ * @public
79
+ */ class VirtualTexture extends Disposable {
80
+ _device;
81
+ _levels;
82
+ _levelOffsets;
83
+ _totalPages;
84
+ _pageSize;
85
+ _border;
86
+ _slotSize;
87
+ _atlasSize;
88
+ _slotsPerRow;
89
+ _physicalPages;
90
+ _loadCapacity;
91
+ _allocBudget;
92
+ _maxPageAge;
93
+ _freeThreshold;
94
+ _pinnedFromLevel;
95
+ _mipChain;
96
+ _planes;
97
+ _planeTextures;
98
+ _params;
99
+ _paramData;
100
+ _rects;
101
+ _rectData;
102
+ _pendingRects;
103
+ _pageTable;
104
+ _pageRequest;
105
+ _meta;
106
+ _lists;
107
+ _listCounts;
108
+ _loadList;
109
+ _fillList;
110
+ _counters;
111
+ _dispatchArgs;
112
+ /** Persistent across updates: (mip bias, upper bound or -1 when disabled, lower bound, locked page residency) */ _state;
113
+ /**
114
+ * Readback copy of the counters, the list counts and the state, in that order. Kept rather
115
+ * than created per read: a single read is in flight at a time (see _statsPending).
116
+ */ _statsReadback;
117
+ _programs;
118
+ _bindGroups;
119
+ _parity;
120
+ _stats;
121
+ _statsPending;
122
+ _updateCount;
123
+ _debugTexture;
124
+ constructor(device, options){
125
+ super();
126
+ if (device.type !== 'webgpu' || !device.getDeviceCaps().miscCaps.supportDispatchIndirect) {
127
+ throw new Error('VirtualTexture requires WebGPU');
128
+ }
129
+ this._device = device;
130
+ this._levels = options.levels.map((l)=>({
131
+ pagesX: l.pagesX,
132
+ pagesY: l.pagesY
133
+ }));
134
+ if (this._levels.length === 0 || this._levels.length > MAX_LEVELS) {
135
+ throw new Error(`VirtualTexture: 1 to ${MAX_LEVELS} levels are supported`);
136
+ }
137
+ this._levelOffsets = [];
138
+ let total = 0;
139
+ for (const l of this._levels){
140
+ this._levelOffsets.push(total);
141
+ total += l.pagesX * l.pagesY;
142
+ }
143
+ this._totalPages = total;
144
+ this._mipChain = this._levels.every((l, i)=>l.pagesX === Math.max(1, this._levels[0].pagesX >> i) && l.pagesY === Math.max(1, this._levels[0].pagesY >> i));
145
+ this._pageSize = options.pageSize ?? 128;
146
+ this._border = options.border ?? 4;
147
+ this._slotSize = this._pageSize + 2 * this._border;
148
+ this._atlasSize = options.atlasSize ?? 4096;
149
+ this._slotsPerRow = Math.floor(this._atlasSize / this._slotSize);
150
+ this._physicalPages = this._slotsPerRow * this._slotsPerRow;
151
+ if (this._physicalPages < 1 || this._physicalPages > PT_PHYS_MASK) {
152
+ throw new Error('VirtualTexture: invalid physical page count');
153
+ }
154
+ this._loadCapacity = options.loadCapacity ?? 16384;
155
+ this._allocBudget = Math.max(1, Math.min(options.allocBudget ?? 16, this._physicalPages));
156
+ this._maxPageAge = options.maxPageAge ?? 1000;
157
+ this._freeThreshold = Math.max(0, options.freeThreshold ?? 15);
158
+ let pinnedFrom = this._levels.length - 1;
159
+ let pinnedPages = this.levelPages(pinnedFrom);
160
+ while(pinnedFrom > 0 && pinnedPages + this.levelPages(pinnedFrom - 1) <= this._physicalPages / 8){
161
+ pinnedFrom--;
162
+ pinnedPages += this.levelPages(pinnedFrom);
163
+ }
164
+ if (pinnedPages > this._physicalPages) {
165
+ throw new Error('VirtualTexture: physical pool too small for the coarsest level');
166
+ }
167
+ this._pinnedFromLevel = pinnedFrom;
168
+ this._planes = options.planes.slice();
169
+ this._planeTextures = this._planes.map((plane)=>{
170
+ const tex = device.createTexture2D(plane.format, this._atlasSize, this._atlasSize, {
171
+ writable: true,
172
+ mipmapping: false
173
+ });
174
+ tex.name = `VirtualTexture.${plane.name}`;
175
+ return tex;
176
+ });
177
+ const storage = (bytes)=>device.createBuffer(Math.max(16, bytes), {
178
+ usage: 'uniform',
179
+ storage: true
180
+ });
181
+ this._params = device.createBuffer((4 + MAX_LEVELS) * 16, {
182
+ usage: 'uniform'
183
+ });
184
+ this._paramData = new Uint32Array((4 + MAX_LEVELS) * 4);
185
+ this._rects = device.createBuffer(MAX_RECTS * 16, {
186
+ usage: 'uniform'
187
+ });
188
+ this._rectData = new Float32Array(MAX_RECTS * 4);
189
+ this._pendingRects = [];
190
+ this._pageTable = storage(total * 4);
191
+ this._pageRequest = storage(total * 4);
192
+ this._meta = storage(this._physicalPages * 16);
193
+ this._lists = [
194
+ storage(this._physicalPages * 16),
195
+ storage(this._physicalPages * 16)
196
+ ];
197
+ this._listCounts = storage(16);
198
+ this._loadList = storage(this._loadCapacity * 12);
199
+ this._fillList = storage(this._allocBudget * 8);
200
+ this._counters = storage(NUM_COUNTERS * 4);
201
+ this._dispatchArgs = device.createBuffer(6 * 4, {
202
+ usage: 'indirect',
203
+ storage: true
204
+ });
205
+ this._state = storage(16);
206
+ this._statsReadback = device.createBuffer(NUM_COUNTERS * 4 + 32, {
207
+ usage: 'read'
208
+ });
209
+ const upper = options.residencyUpperBound ?? 0.95;
210
+ const lower = Math.min(upper, options.residencyLowerBound ?? 0.8);
211
+ // x: mip bias, y: upper bound (negative when disabled), z: lower bound, w: locked residency
212
+ this._state.bufferSubData(0, new Float32Array([
213
+ 0,
214
+ options.residencyMipBias ? upper : -1,
215
+ lower,
216
+ pinnedPages / this._physicalPages
217
+ ]));
218
+ // The first update reads the previous REQUESTED list as its LRU order: every page, in order
219
+ const initialLists = new Uint32Array(this._physicalPages * 4);
220
+ for(let i = 0; i < this._physicalPages; i++){
221
+ initialLists[LIST_REQUESTED * this._physicalPages + i] = i;
222
+ }
223
+ // update() flips the parity before running, so the first one writes _lists[1] and reads its
224
+ // previous lists from _lists[0]
225
+ this._parity = 0;
226
+ this._lists[this._parity].bufferSubData(0, initialLists);
227
+ this._programs = new Map();
228
+ this._bindGroups = new Map();
229
+ this._stats = null;
230
+ this._statsPending = false;
231
+ this._updateCount = 0;
232
+ this._debugTexture = null;
233
+ this.writeParams(0);
234
+ }
235
+ /** Levels of the virtual space */ get levels() {
236
+ return this._levels;
237
+ }
238
+ get pageSize() {
239
+ return this._pageSize;
240
+ }
241
+ get border() {
242
+ return this._border;
243
+ }
244
+ get atlasSize() {
245
+ return this._atlasSize;
246
+ }
247
+ get physicalPageCount() {
248
+ return this._physicalPages;
249
+ }
250
+ /** First level kept resident at all times */ get pinnedFromLevel() {
251
+ return this._pinnedFromLevel;
252
+ }
253
+ get allocBudget() {
254
+ return this._allocBudget;
255
+ }
256
+ /**
257
+ * Last counters read back from the GPU, null until the first read back completes. The
258
+ * residency mip bias there is the value the shaders add in {@link VirtualTexture.computeLevel}.
259
+ */ get stats() {
260
+ return this._stats;
261
+ }
262
+ getPlaneTexture(name) {
263
+ const index = this._planes.findIndex((p)=>p.name === name);
264
+ return index >= 0 ? this._planeTextures[index] : null;
265
+ }
266
+ /** Marks every resident page for refilling */ invalidate() {
267
+ this.invalidateRegion(0, 0, 1, 1);
268
+ }
269
+ /**
270
+ * Marks the resident pages overlapping a region, in normalized virtual coordinates, for
271
+ * refilling. Pages in use (requested within freeThreshold, or pinned) keep their mapping and
272
+ * old content until refilled, ahead of new allocations; cached pages no longer in use are
273
+ * unmapped. A page counts with its border, so pages next to the region are refilled too.
274
+ */ invalidateRegion(u0, v0, u1, v1) {
275
+ this._pendingRects.push([
276
+ u0,
277
+ v0,
278
+ u1,
279
+ v1
280
+ ]);
281
+ }
282
+ /**
283
+ * Maps the pages requested since the last update. Runs compute passes, so it must be called
284
+ * outside of render passes. `stamp` must increase with every frame (not every camera).
285
+ */ update(stamp) {
286
+ const device = this._device;
287
+ this._parity ^= 1;
288
+ this.writeParams(stamp);
289
+ const N = this._physicalPages;
290
+ const groups = (n)=>Math.max(1, Math.ceil(n / GROUP));
291
+ this.dispatch('clear', groups(Math.max(this._totalPages, NUM_COUNTERS)));
292
+ while(this._pendingRects.length > 0){
293
+ const rects = this._pendingRects.splice(0, MAX_RECTS);
294
+ this._rectData.fill(0);
295
+ rects.forEach((r, i)=>this._rectData.set(r, i * 4));
296
+ this._rects.bufferSubData(0, this._rectData);
297
+ this._paramData[13] = rects.length;
298
+ this._params.bufferSubData(0, this._paramData);
299
+ this.dispatch('invalidate', groups(N));
300
+ }
301
+ this.dispatch(`updatePhysical${this._parity}`, groups(N));
302
+ this.dispatch(`pack${this._parity}`, 1);
303
+ this.dispatch(`appendEmpty${this._parity}`, groups(N));
304
+ this.dispatch(`appendEmptyCount${this._parity}`, 1);
305
+ this.dispatch('gather', groups(this._totalPages));
306
+ this.dispatch('select', 1);
307
+ const allocate = this.getProgram(`allocate${this._parity}`);
308
+ device.setProgram(allocate);
309
+ device.setBindGroup(0, this.getBindGroup(`allocate${this._parity}`));
310
+ device.computeIndirect(this._dispatchArgs, 0);
311
+ this.dispatch('finalize', 1);
312
+ this.dispatch(`appendAvailable${this._parity}`, groups(N));
313
+ this.dispatch(`appendAvailableCount${this._parity}`, 1);
314
+ if (this._mipChain) {
315
+ const l0 = this._levels[0];
316
+ this.dispatch('propagate', Math.ceil(l0.pagesX / FILL_GROUP), Math.ceil(l0.pagesY / FILL_GROUP));
317
+ }
318
+ this._updateCount++;
319
+ if (!this._statsPending && this._updateCount % 30 === 1) {
320
+ this.readStats();
321
+ }
322
+ }
323
+ /**
324
+ * Builds a compute program filling the pages mapped by the last update. Each workgroup fills
325
+ * one page, border included, calling `fillTexel` once per texel. The program belongs to the
326
+ * caller, who disposes it.
327
+ *
328
+ * @param label - Program label
329
+ * @param setup - Declares the owner's own bindings (group 0)
330
+ * @param fillTexel - Returns one value per plane for a texel. `texel` is the texel centre in
331
+ * the level's texel space; border texels lie outside the page and must be computed the same
332
+ * way, which is what keeps filtering continuous across pages.
333
+ */ createFillProgram(label, setup, fillTexel) {
334
+ const that = this;
335
+ const S = this._slotSize;
336
+ const iterations = Math.ceil(S / FILL_GROUP);
337
+ return this._device.buildComputeProgram({
338
+ label,
339
+ workgroupSize: [
340
+ FILL_GROUP,
341
+ FILL_GROUP,
342
+ 1
343
+ ],
344
+ compute (pb) {
345
+ that.declareParams(pb, this);
346
+ this.zVT_fillList = pb.uint[0]().storageBufferReadonly(0);
347
+ that._planes.forEach((plane, i)=>{
348
+ this[`zVT_plane${i}`] = pb.texStorage2D[plane.format]().storage(0);
349
+ });
350
+ setup?.(this);
351
+ that.defineLevelOf(pb);
352
+ pb.main(function() {
353
+ this.$l.entry = this.$builtins.workGroupId.x;
354
+ this.$l.pageId = this.zVT_fillList.at(pb.mul(this.entry, 2));
355
+ this.$if(pb.equal(this.pageId, INDEX_NONE), function() {
356
+ this.$return();
357
+ });
358
+ this.$l.phys = this.zVT_fillList.at(pb.add(pb.mul(this.entry, 2), 1));
359
+ this.$l.level = this.zVT_levelOf(this.pageId);
360
+ this.$l.info = this.zVT_params.at(pb.add(this.level, 4));
361
+ this.$l.local = pb.sub(this.pageId, this.info.x);
362
+ this.$l.page = pb.uvec2(pb.sub(this.local, pb.mul(pb.div(this.local, this.info.y), this.info.y)), pb.div(this.local, this.info.y));
363
+ this.$l.origin = pb.mul(pb.uvec2(pb.sub(this.phys, pb.mul(pb.div(this.phys, that._slotsPerRow), that._slotsPerRow)), pb.div(this.phys, that._slotsPerRow)), S);
364
+ this.$for(pb.uint('iy'), 0, iterations, function() {
365
+ this.$l.ty = pb.add(pb.mul(this.iy, FILL_GROUP), this.$builtins.localInvocationId.y);
366
+ this.$for(pb.uint('ix'), 0, iterations, function() {
367
+ this.$l.tx = pb.add(pb.mul(this.ix, FILL_GROUP), this.$builtins.localInvocationId.x);
368
+ this.$if(pb.and(pb.lessThan(this.tx, S), pb.lessThan(this.ty, S)), function() {
369
+ this.$l.texel = pb.add(pb.sub(pb.mul(pb.vec2(pb.float(this.page.x), pb.float(this.page.y)), that._pageSize), pb.vec2(that._border)), pb.add(pb.vec2(pb.float(this.tx), pb.float(this.ty)), pb.vec2(0.5)));
370
+ const values = fillTexel(this, this.level, this.texel);
371
+ values.forEach((v, i)=>{
372
+ pb.textureStore(this[`zVT_plane${i}`], pb.add(this.origin, pb.uvec2(this.tx, this.ty)), v);
373
+ });
374
+ });
375
+ });
376
+ });
377
+ });
378
+ }
379
+ });
380
+ }
381
+ /**
382
+ * Fills the pages mapped by the last update. `bindGroup` comes from the fill program's layout
383
+ * with the owner's own bindings set; the core ones are set here.
384
+ */ fill(program, bindGroup) {
385
+ bindGroup.setBuffer('zVT_params', this._params);
386
+ bindGroup.setBuffer('zVT_fillList', this._fillList);
387
+ this._planeTextures.forEach((tex, i)=>bindGroup.setTexture(`zVT_plane${i}`, tex));
388
+ this._device.setProgram(program);
389
+ this._device.setBindGroup(0, bindGroup);
390
+ this._device.computeIndirect(this._dispatchArgs, 12);
391
+ }
392
+ /**
393
+ * Declares the bindings used by {@link VirtualTexture.request}, {@link VirtualTexture.resolve}
394
+ * and {@link VirtualTexture.computeLevel} in a render program.
395
+ */ declareBindings(scope, group, requests = scope.$builder.shaderKind !== 'vertex') {
396
+ const pb = scope.$builder;
397
+ this.declareParams(pb, scope, group);
398
+ scope.zVT_pageTable = pb.uint[0]().storageBufferReadonly(group);
399
+ scope.zVT_state = pb.vec4[0]().storageBufferReadonly(group);
400
+ if (requests) {
401
+ scope.zVT_pageRequest = pb.atomic_uint[0]().storageBuffer(group);
402
+ }
403
+ }
404
+ applyBindings(bindGroup, withRequests = true) {
405
+ bindGroup.setBuffer('zVT_params', this._params);
406
+ bindGroup.setBuffer('zVT_pageTable', this._pageTable);
407
+ bindGroup.setBuffer('zVT_state', this._state);
408
+ if (withRequests) {
409
+ bindGroup.setBuffer('zVT_pageRequest', this._pageRequest);
410
+ }
411
+ }
412
+ /**
413
+ * Size in texels of a level, as a vec2. Usable wherever the core bindings are declared,
414
+ * including the `fillTexel` callback of {@link VirtualTexture.createFillProgram}, where
415
+ * `texel / levelTexels(level)` is the virtual uv of the texel.
416
+ */ levelTexels(scope, level) {
417
+ const pb = scope.$builder;
418
+ const info = scope.zVT_params.at(pb.add(pb.uint(level), 4));
419
+ return pb.mul(pb.vec2(pb.float(info.y), pb.float(info.z)), this._pageSize);
420
+ }
421
+ /** Mip level from the screen derivatives of a virtual uv, fragment shaders only */ computeLevel(scope, uv) {
422
+ const pb = scope.$builder;
423
+ return this.levelFromFootprint(scope, pb.dpdx(uv), pb.dpdy(uv));
424
+ }
425
+ /**
426
+ * Mip level from the change of the virtual uv across one pixel in x and in y, for marking in
427
+ * compute shaders where there are no screen derivatives (e.g. from reconstructed neighbours).
428
+ * Includes the residency mip bias, like {@link VirtualTexture.computeLevel}.
429
+ */ levelFromFootprint(scope, duvdx, duvdy) {
430
+ const pb = scope.$builder;
431
+ const size = [
432
+ this._levels[0].pagesX * this._pageSize,
433
+ this._levels[0].pagesY * this._pageSize
434
+ ];
435
+ pb.func('zVT_levelFromFootprint', [
436
+ pb.vec2('duvdx'),
437
+ pb.vec2('duvdy')
438
+ ], function() {
439
+ this.$l.dx = pb.mul(this.duvdx, pb.vec2(size[0], size[1]));
440
+ this.$l.dy = pb.mul(this.duvdy, pb.vec2(size[0], size[1]));
441
+ this.$l.d = pb.max(pb.dot(this.dx, this.dx), pb.dot(this.dy, this.dy));
442
+ this.$return(pb.clamp(pb.add(pb.mul(pb.log2(pb.max(this.d, 1e-8)), 0.5), this.zVT_state.at(0).x), 0, pb.float(pb.sub(this.zVT_params.at(1).x, 1))));
443
+ });
444
+ return pb.getGlobalScope().zVT_levelFromFootprint(duvdx, duvdy);
445
+ }
446
+ /**
447
+ * Requests the page covering `uv` at `level`. With `prefetch`, also requests the ancestor at
448
+ * most two levels finer than what is resident now, so the texture refines gradually.
449
+ */ request(scope, level, uv, prefetch = true) {
450
+ const pb = scope.$builder;
451
+ this.definePageId(pb);
452
+ pb.func(`zVT_request${prefetch ? 'P' : ''}`, [
453
+ pb.uint('level'),
454
+ pb.vec2('uv')
455
+ ], function() {
456
+ this.$l.id = this.zVT_pageId(this.level, this.uv);
457
+ pb.atomicAdd(this.zVT_pageRequest.at(this.id), 1);
458
+ if (prefetch) {
459
+ this.$l.e = this.zVT_pageTable.at(this.id);
460
+ this.$if(pb.and(pb.equal(pb.compAnd(this.e, PT_THIS_LOD), 0), pb.notEqual(pb.compAnd(this.e, PT_ANY_LOD), 0)), function() {
461
+ this.$l.hit = pb.add(this.level, pb.compAnd(pb.sar(this.e, PT_LOD_SHIFT), PT_LOD_MASK));
462
+ this.$l.pre = pb.sub(this.hit, pb.min(this.hit, 2));
463
+ this.$if(pb.greaterThan(this.pre, this.level), function() {
464
+ pb.atomicAdd(this.zVT_pageRequest.at(this.zVT_pageId(this.pre, this.uv)), 1);
465
+ });
466
+ });
467
+ }
468
+ });
469
+ pb.getGlobalScope()[`zVT_request${prefetch ? 'P' : ''}`](pb.uint(level), uv);
470
+ }
471
+ /**
472
+ * Physical location of `uv` at `level`: (atlas u, atlas v, level actually hit, 1 if any page
473
+ * is mapped else 0). One page table read; unmapped pages already point at their nearest
474
+ * resident ancestor.
475
+ */ resolve(scope, level, uv) {
476
+ const pb = scope.$builder;
477
+ const that = this;
478
+ this.definePageId(pb);
479
+ pb.func('zVT_resolve', [
480
+ pb.uint('level'),
481
+ pb.vec2('uv')
482
+ ], function() {
483
+ this.$l.e = this.zVT_pageTable.at(this.zVT_pageId(this.level, this.uv));
484
+ this.$l.hit = pb.add(this.level, pb.compAnd(pb.sar(this.e, PT_LOD_SHIFT), PT_LOD_MASK));
485
+ this.$l.phys = pb.compAnd(this.e, PT_PHYS_MASK);
486
+ this.$l.info = this.zVT_params.at(pb.add(pb.min(this.hit, pb.sub(this.zVT_params.at(1).x, 1)), 4));
487
+ this.$l.inPage = pb.fract(pb.mul(this.uv, pb.vec2(pb.float(this.info.y), pb.float(this.info.z))));
488
+ this.$l.origin = pb.mul(pb.vec2(pb.float(pb.sub(this.phys, pb.mul(pb.div(this.phys, that._slotsPerRow), that._slotsPerRow))), pb.float(pb.div(this.phys, that._slotsPerRow))), that._slotSize);
489
+ this.$return(pb.vec4(pb.div(pb.add(pb.add(this.origin, pb.vec2(that._border)), pb.mul(this.inPage, that._pageSize)), that._atlasSize), pb.float(this.hit), pb.float(pb.notEqual(pb.compAnd(this.e, PT_ANY_LOD), 0))));
490
+ });
491
+ return pb.getGlobalScope().zVT_resolve(pb.uint(level), uv);
492
+ }
493
+ /**
494
+ * An rgba8 view of the page table for one level, magnified to the finest level's page grid:
495
+ * bright green = resident and requested this update, dim green = resident, cyan = pinned,
496
+ * blue = pointing at an ancestor (darker the further up), black = nothing mapped.
497
+ */ renderDebugTexture(level) {
498
+ const l0 = this._levels[0];
499
+ if (!this._debugTexture) {
500
+ this._debugTexture = this._device.createTexture2D('rgba8unorm', l0.pagesX, l0.pagesY, {
501
+ writable: true,
502
+ mipmapping: false,
503
+ samplerOptions: {
504
+ magFilter: 'nearest',
505
+ minFilter: 'nearest',
506
+ mipFilter: 'none'
507
+ }
508
+ });
509
+ this._debugTexture.name = 'VirtualTexture.debug';
510
+ }
511
+ const bindGroup = this.getBindGroup('debug');
512
+ bindGroup.setValue('zVT_debugLevel', Math.min(Math.max(0, level | 0), this._levels.length - 1));
513
+ bindGroup.setTexture('zVT_debug', this._debugTexture);
514
+ this._device.setProgram(this.getProgram('debug'));
515
+ this._device.setBindGroup(0, bindGroup);
516
+ this._device.compute(Math.ceil(l0.pagesX / FILL_GROUP), Math.ceil(l0.pagesY / FILL_GROUP), 1);
517
+ return this._debugTexture;
518
+ }
519
+ /**
520
+ * Reads the page table, the physical page metadata and lists back and checks the invariants
521
+ * of the page management. Debugging aid: the read back costs a GPU sync and a few hundred KB.
522
+ * Must be called after an update and before the next one.
523
+ */ async validate() {
524
+ const N = this._physicalPages;
525
+ const table = new Uint8Array(this._totalPages * 4);
526
+ const meta = new Uint8Array(N * 16);
527
+ const lists = new Uint8Array(N * 16);
528
+ const counts = new Uint8Array(16);
529
+ await Promise.all([
530
+ this._pageTable.getBufferSubData(table),
531
+ this._meta.getBufferSubData(meta),
532
+ this._lists[this._parity].getBufferSubData(lists),
533
+ this._listCounts.getBufferSubData(counts)
534
+ ]);
535
+ const pt = new Uint32Array(table.buffer);
536
+ const m = new Uint32Array(meta.buffer);
537
+ const l = new Uint32Array(lists.buffer);
538
+ const requestedCount = new Int32Array(counts.buffer)[LIST_REQUESTED];
539
+ const result = {
540
+ lostPages: 0,
541
+ duplicatePages: 0,
542
+ wrongOwner: 0,
543
+ sharedPages: 0,
544
+ pinnedMissing: 0,
545
+ unmapped: 0
546
+ };
547
+ const seen = new Uint8Array(N);
548
+ for(let i = 0; i < Math.min(requestedCount, N); i++){
549
+ const p = l[LIST_REQUESTED * N + i];
550
+ if (p >= N || seen[p]) {
551
+ result.duplicatePages++;
552
+ } else {
553
+ seen[p] = 1;
554
+ }
555
+ }
556
+ result.lostPages = seen.reduce((n, v)=>n + (v ? 0 : 1), 0);
557
+ const users = new Uint32Array(N);
558
+ for(let id = 0; id < this._totalPages; id++){
559
+ const e = pt[id];
560
+ if (e & PT_THIS_LOD) {
561
+ const p = e & PT_PHYS_MASK;
562
+ if (p >= N || m[p * 4] !== id || !(m[p * 4 + 3] & META_ALLOCATED)) {
563
+ result.wrongOwner++;
564
+ } else if (++users[p] === 2) {
565
+ result.sharedPages++;
566
+ }
567
+ } else if (!(e & PT_ANY_LOD)) {
568
+ result.unmapped++;
569
+ }
570
+ }
571
+ for(let level = this._pinnedFromLevel; level < this._levels.length; level++){
572
+ for(let i = 0; i < this.levelPages(level); i++){
573
+ if (!(pt[this._levelOffsets[level] + i] & PT_THIS_LOD)) {
574
+ result.pinnedMissing++;
575
+ }
576
+ }
577
+ }
578
+ if (!this._mipChain) {
579
+ // Without ancestors to fall back on, unmapped entries are expected
580
+ result.unmapped = 0;
581
+ }
582
+ return result;
583
+ }
584
+ levelPages(level) {
585
+ return this._levels[level].pagesX * this._levels[level].pagesY;
586
+ }
587
+ writeParams(stamp) {
588
+ const d = this._paramData;
589
+ d.set([
590
+ this._totalPages,
591
+ this._physicalPages,
592
+ stamp,
593
+ this._maxPageAge
594
+ ], 0);
595
+ d.set([
596
+ this._levels.length,
597
+ this._allocBudget,
598
+ this._loadCapacity,
599
+ this._pinnedFromLevel
600
+ ], 4);
601
+ d.set([
602
+ this._slotsPerRow,
603
+ this._slotSize,
604
+ this._border,
605
+ this._pageSize
606
+ ], 8);
607
+ d.set([
608
+ this._atlasSize,
609
+ 0,
610
+ this._mipChain ? 1 : 0,
611
+ this._freeThreshold
612
+ ], 12);
613
+ this._levels.forEach((l, i)=>d.set([
614
+ this._levelOffsets[i],
615
+ l.pagesX,
616
+ l.pagesY,
617
+ 0
618
+ ], 16 + i * 4));
619
+ this._params.bufferSubData(0, d);
620
+ }
621
+ readStats() {
622
+ this._statsPending = true;
623
+ const countersBytes = NUM_COUNTERS * 4;
624
+ this._device.copyBuffer(this._counters, this._statsReadback, 0, 0, countersBytes);
625
+ this._device.copyBuffer(this._listCounts, this._statsReadback, 0, countersBytes, 16);
626
+ this._device.copyBuffer(this._state, this._statsReadback, 0, countersBytes + 16, 16);
627
+ this._statsReadback.getBufferSubData().then((data)=>{
628
+ const c = new Uint32Array(data.buffer, data.byteOffset, NUM_COUNTERS);
629
+ const l = new Int32Array(data.buffer, data.byteOffset + countersBytes, 4);
630
+ const state = new Float32Array(data.buffer, data.byteOffset + countersBytes + 16, 4);
631
+ this._stats = {
632
+ requested: c[C_STAT_REQUESTED],
633
+ allocated: c[C_STAT_ALLOCATED],
634
+ evicted: c[C_STAT_EVICTED],
635
+ refilled: c[C_STAT_REFILLED],
636
+ expired: c[C_STAT_EXPIRED],
637
+ loads: Math.min(c[C_LOAD], this._loadCapacity),
638
+ dropped: c[C_STAT_DROPPED],
639
+ available: Math.max(0, l[LIST_AVAILABLE]),
640
+ lruSize: l[LIST_REQUESTED],
641
+ mipBias: state[0]
642
+ };
643
+ }).catch(()=>{}).finally(()=>{
644
+ this._statsPending = false;
645
+ });
646
+ }
647
+ dispatch(name, x, y = 1) {
648
+ this._device.setProgram(this.getProgram(name));
649
+ this._device.setBindGroup(0, this.getBindGroup(name));
650
+ this._device.compute(x, y, 1);
651
+ }
652
+ getProgram(name) {
653
+ let program = this._programs.get(name);
654
+ if (!program) {
655
+ program = this.buildProgram(name);
656
+ this._programs.set(name, program);
657
+ }
658
+ return program;
659
+ }
660
+ getBindGroup(name) {
661
+ let bindGroup = this._bindGroups.get(name);
662
+ if (!bindGroup) {
663
+ const program = this.getProgram(name);
664
+ bindGroup = this._device.createBindGroup(program.bindGroupLayouts[0]);
665
+ const parity = Number(name[name.length - 1]);
666
+ const resources = {
667
+ zVT_params: this._params,
668
+ zVT_rects: this._rects,
669
+ zVT_pageTable: this._pageTable,
670
+ zVT_pageRequest: this._pageRequest,
671
+ zVT_meta: this._meta,
672
+ zVT_listCounts: this._listCounts,
673
+ zVT_loadList: this._loadList,
674
+ zVT_fillList: this._fillList,
675
+ zVT_counters: this._counters,
676
+ zVT_dispatchArgs: this._dispatchArgs,
677
+ zVT_state: this._state
678
+ };
679
+ if (parity === 0 || parity === 1) {
680
+ resources.zVT_lists = this._lists[parity];
681
+ resources.zVT_prevLists = this._lists[parity ^ 1];
682
+ }
683
+ // Layout entries carry the builder's block names (zUBC_*); the variable names map to them
684
+ // through nameMap, which setBuffer resolves
685
+ const layout = program.bindGroupLayouts[0];
686
+ for (const [resName, res] of Object.entries(resources)){
687
+ if (layout.nameMap?.[resName] || layout.entries.some((e)=>e.name === resName)) {
688
+ bindGroup.setBuffer(resName, res);
689
+ }
690
+ }
691
+ this._bindGroups.set(name, bindGroup);
692
+ }
693
+ return bindGroup;
694
+ }
695
+ // ---------------------------------------------------------------- shader building blocks
696
+ declareParams(pb, scope, group = 0) {
697
+ // [0] (totalPages, physicalPages, stamp, maxPageAge)
698
+ // [1] (numLevels, allocBudget, loadCapacity, pinnedFromLevel)
699
+ // [2] (slotsPerRow, slotSize, border, pageSize)
700
+ // [3] (atlasSize, numRects, mipChain, freeThreshold)
701
+ // [4 + l] (first page id, pagesX, pagesY, 0) of level l
702
+ scope.zVT_params = pb.uvec4[4 + MAX_LEVELS]().uniformBuffer(group);
703
+ }
704
+ defineLevelOf(pb) {
705
+ pb.func('zVT_levelOf', [
706
+ pb.uint('id')
707
+ ], function() {
708
+ this.$l.level = pb.uint(0);
709
+ this.$for(pb.uint('l'), 1, MAX_LEVELS, function() {
710
+ this.$if(pb.or(pb.greaterThanEqual(this.l, this.zVT_params.at(1).x), pb.lessThan(this.id, this.zVT_params.at(pb.add(this.l, 4)).x)), function() {
711
+ this.$break();
712
+ });
713
+ this.level = this.l;
714
+ });
715
+ this.$return(this.level);
716
+ });
717
+ }
718
+ definePageId(pb) {
719
+ pb.func('zVT_pageId', [
720
+ pb.uint('level'),
721
+ pb.vec2('uv')
722
+ ], function() {
723
+ this.$l.info = this.zVT_params.at(pb.add(pb.min(this.level, pb.sub(this.zVT_params.at(1).x, 1)), 4));
724
+ this.$l.pages = pb.vec2(pb.float(this.info.y), pb.float(this.info.z));
725
+ this.$l.pc = pb.uvec2(pb.clamp(pb.floor(pb.mul(this.uv, this.pages)), pb.vec2(0), pb.sub(this.pages, pb.vec2(1))));
726
+ this.$return(pb.add(this.info.x, pb.add(pb.mul(this.pc.y, this.info.y), this.pc.x)));
727
+ });
728
+ }
729
+ definePush(pb) {
730
+ pb.func('zVT_push', [
731
+ pb.uint('list'),
732
+ pb.uint('page')
733
+ ], function() {
734
+ this.$l.offset = pb.atomicAdd(this.zVT_listCounts.at(this.list), 1);
735
+ this.$if(pb.lessThan(this.offset, pb.int(this.zVT_params.at(0).y)), function() {
736
+ this.zVT_lists.setAt(pb.add(pb.mul(this.list, this.zVT_params.at(0).y), pb.uint(this.offset)), this.page);
737
+ });
738
+ });
739
+ }
740
+ buildProgram(name) {
741
+ const that = this;
742
+ const base = name.replace(/[01]$/, '');
743
+ const common = (pb, scope)=>{
744
+ that.declareParams(pb, scope);
745
+ };
746
+ const lists = (pb, scope)=>{
747
+ scope.zVT_lists = pb.uint[0]().storageBuffer(0);
748
+ scope.zVT_listCounts = pb.atomic_int[0]().storageBuffer(0);
749
+ };
750
+ const program = (workgroupSize, body)=>this._device.buildComputeProgram({
751
+ label: `VirtualTexture.${name}`,
752
+ workgroupSize,
753
+ compute (pb) {
754
+ common(pb, this);
755
+ body(pb, this);
756
+ }
757
+ });
758
+ const loadCount = (pb, scope)=>pb.atomicAdd(scope.zVT_listCounts.at(LIST_AVAILABLE), 0);
759
+ switch(base){
760
+ case 'clear':
761
+ return program([
762
+ GROUP,
763
+ 1,
764
+ 1
765
+ ], (pb, scope)=>{
766
+ scope.zVT_pageTable = pb.uint[0]().storageBuffer(0);
767
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
768
+ scope.zVT_listCounts = pb.atomic_int[0]().storageBuffer(0);
769
+ pb.main(function() {
770
+ this.$l.i = this.$builtins.globalInvocationId.x;
771
+ this.$if(pb.lessThan(this.i, this.zVT_params.at(0).x), function() {
772
+ this.zVT_pageTable.setAt(this.i, pb.uint(0));
773
+ });
774
+ this.$if(pb.lessThan(this.i, NUM_COUNTERS), function() {
775
+ pb.atomicExchange(this.zVT_counters.at(this.i), 0);
776
+ });
777
+ this.$if(pb.lessThan(this.i, 4), function() {
778
+ pb.atomicExchange(this.zVT_listCounts.at(this.i), 0);
779
+ });
780
+ });
781
+ });
782
+ // Marks the resident pages inside the queued regions dirty
783
+ case 'invalidate':
784
+ return program([
785
+ GROUP,
786
+ 1,
787
+ 1
788
+ ], (pb, scope)=>{
789
+ scope.zVT_rects = pb.vec4[MAX_RECTS]().uniformBuffer(0);
790
+ scope.zVT_meta = pb.uint[0]().storageBuffer(0);
791
+ that.defineLevelOf(pb);
792
+ pb.main(function() {
793
+ this.$l.p = this.$builtins.globalInvocationId.x;
794
+ this.$if(pb.lessThan(this.p, this.zVT_params.at(0).y), function() {
795
+ this.$l.flags = this.zVT_meta.at(pb.add(pb.mul(this.p, 4), 3));
796
+ this.$if(pb.notEqual(pb.compAnd(this.flags, META_ALLOCATED), 0), function() {
797
+ this.$l.owner = this.zVT_meta.at(pb.mul(this.p, 4));
798
+ this.$l.info = this.zVT_params.at(pb.add(this.zVT_levelOf(this.owner), 4));
799
+ this.$l.local = pb.sub(this.owner, this.info.x);
800
+ this.$l.page = pb.vec2(pb.float(pb.sub(this.local, pb.mul(pb.div(this.local, this.info.y), this.info.y))), pb.float(pb.div(this.local, this.info.y)));
801
+ // The page with its border: a border texel is sampled when filtering across the
802
+ // page edge, so an edit next to a page changes it too. One more texel covers the
803
+ // bilinear footprint of the outermost border texel.
804
+ this.$l.margin = pb.div(pb.float(pb.add(this.zVT_params.at(2).z, 1)), pb.float(this.zVT_params.at(2).w));
805
+ this.$l.lo = pb.div(pb.sub(this.page, pb.vec2(this.margin)), pb.vec2(pb.float(this.info.y), pb.float(this.info.z)));
806
+ this.$l.hi = pb.div(pb.add(this.page, pb.vec2(pb.add(1, this.margin))), pb.vec2(pb.float(this.info.y), pb.float(this.info.z)));
807
+ this.$for(pb.uint('r'), 0, this.zVT_params.at(3).y, function() {
808
+ this.$l.rect = this.zVT_rects.at(this.r);
809
+ this.$if(pb.and(pb.all(pb.lessThan(this.lo, this.rect.zw)), pb.all(pb.greaterThan(this.hi, this.rect.xy))), function() {
810
+ this.flags = pb.compOr(this.flags, META_DIRTY);
811
+ });
812
+ });
813
+ this.zVT_meta.setAt(pb.add(pb.mul(this.p, 4), 3), this.flags);
814
+ });
815
+ });
816
+ });
817
+ });
818
+ case 'updatePhysical':
819
+ return program([
820
+ GROUP,
821
+ 1,
822
+ 1
823
+ ], (pb, scope)=>{
824
+ lists(pb, scope);
825
+ scope.zVT_prevLists = pb.uint[0]().storageBufferReadonly(0);
826
+ scope.zVT_pageTable = pb.uint[0]().storageBuffer(0);
827
+ scope.zVT_pageRequest = pb.atomic_uint[0]().storageBuffer(0);
828
+ scope.zVT_meta = pb.uint[0]().storageBuffer(0);
829
+ scope.zVT_fillList = pb.uint[0]().storageBuffer(0);
830
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
831
+ that.definePush(pb);
832
+ pb.main(function() {
833
+ this.$l.i = this.$builtins.globalInvocationId.x;
834
+ this.$l.N = this.zVT_params.at(0).y;
835
+ this.$if(pb.lessThan(this.i, this.N), function() {
836
+ this.$l.p = this.zVT_prevLists.at(pb.add(pb.mul(this.N, LIST_REQUESTED), this.i));
837
+ this.$l.m = pb.mul(this.p, 4);
838
+ this.$l.flags = this.zVT_meta.at(pb.add(this.m, 3));
839
+ this.$l.next = pb.uint(0);
840
+ this.$l.removed = false;
841
+ this.$if(pb.notEqual(pb.compAnd(this.flags, META_ALLOCATED), 0), function() {
842
+ this.$l.owner = this.zVT_meta.at(this.m);
843
+ this.$l.requestedNow = pb.greaterThan(pb.atomicAdd(this.zVT_pageRequest.at(this.owner), 0), 0);
844
+ this.$l.age = pb.sub(this.zVT_params.at(0).z, this.zVT_meta.at(pb.add(this.m, 2)));
845
+ // In use: requested now, pinned, or requested within the free threshold
846
+ this.$l.requested = pb.or(pb.or(this.requestedNow, pb.notEqual(pb.compAnd(this.flags, META_PINNED), 0)), pb.lessThanEqual(this.age, this.zVT_params.at(3).w));
847
+ this.$if(pb.or(this.requested, pb.and(pb.lessThanEqual(this.age, this.zVT_params.at(0).w), pb.equal(pb.compAnd(this.flags, META_DIRTY), 0))), function() {
848
+ this.next = this.flags;
849
+ this.zVT_pageTable.setAt(this.owner, pb.compOr(pb.uint(PT_MAPPED), this.p));
850
+ this.$if(this.requested, function() {
851
+ this.zVT_push(LIST_REQUESTED, this.p);
852
+ this.removed = true;
853
+ this.$if(this.requestedNow, function() {
854
+ this.zVT_meta.setAt(pb.add(this.m, 2), this.zVT_params.at(0).z);
855
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_REQUESTED), 1);
856
+ });
857
+ this.$if(pb.notEqual(pb.compAnd(this.flags, META_DIRTY), 0), function() {
858
+ this.$l.slot = pb.atomicAdd(this.zVT_counters.at(C_FILL), 1);
859
+ this.$if(pb.lessThan(this.slot, this.zVT_params.at(1).y), function() {
860
+ this.zVT_fillList.setAt(pb.mul(this.slot, 2), this.owner);
861
+ this.zVT_fillList.setAt(pb.add(pb.mul(this.slot, 2), 1), this.p);
862
+ this.next = pb.compAnd(this.next, pb.uint(-3 >>> 0));
863
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_REFILLED), 1);
864
+ });
865
+ });
866
+ });
867
+ }).$else(function() {
868
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_EXPIRED), 1);
869
+ });
870
+ });
871
+ this.$if(pb.equal(this.next, 0), function() {
872
+ this.zVT_push(LIST_EMPTY, this.p);
873
+ this.removed = true;
874
+ });
875
+ this.zVT_meta.setAt(pb.add(this.m, 3), this.next);
876
+ this.zVT_lists.setAt(pb.add(pb.mul(this.N, LIST_LRU), this.i), pb.select(this.p, pb.uint(INDEX_NONE), this.removed));
877
+ });
878
+ });
879
+ });
880
+ case 'pack':
881
+ return program([
882
+ PACK_GROUP,
883
+ 1,
884
+ 1
885
+ ], (pb, scope)=>{
886
+ lists(pb, scope);
887
+ scope.zVT_scan = pb.uint[PACK_GROUP]().workgroup();
888
+ pb.main(function() {
889
+ this.$l.lid = this.$builtins.localInvocationId.x;
890
+ this.$l.N = this.zVT_params.at(0).y;
891
+ this.$l.total = pb.uint(0);
892
+ this.$l.chunks = pb.div(pb.add(this.N, PACK_GROUP - 1), PACK_GROUP);
893
+ this.$for(pb.uint('c'), 0, this.chunks, function() {
894
+ this.$l.idx = pb.add(pb.mul(this.c, PACK_GROUP), this.lid);
895
+ this.$l.v = pb.uint(INDEX_NONE);
896
+ this.$if(pb.lessThan(this.idx, this.N), function() {
897
+ this.v = this.zVT_lists.at(pb.add(pb.mul(this.N, LIST_LRU), this.idx));
898
+ });
899
+ this.$l.valid = pb.select(pb.uint(0), pb.uint(1), pb.notEqual(this.v, INDEX_NONE));
900
+ this.zVT_scan.setAt(this.lid, this.valid);
901
+ pb.workgroupBarrier();
902
+ for(let offset = 1; offset < PACK_GROUP; offset *= 2){
903
+ this.$l[`t${offset}`] = pb.uint(0);
904
+ this.$if(pb.greaterThanEqual(this.lid, offset), function() {
905
+ this[`t${offset}`] = this.zVT_scan.at(pb.sub(this.lid, offset));
906
+ });
907
+ pb.workgroupBarrier();
908
+ this.zVT_scan.setAt(this.lid, pb.add(this.zVT_scan.at(this.lid), this[`t${offset}`]));
909
+ pb.workgroupBarrier();
910
+ }
911
+ this.$if(pb.notEqual(this.valid, 0), function() {
912
+ this.zVT_lists.setAt(pb.add(pb.mul(this.N, LIST_AVAILABLE), pb.add(this.total, pb.sub(this.zVT_scan.at(this.lid), 1))), this.v);
913
+ });
914
+ this.total = pb.add(this.total, this.zVT_scan.at(PACK_GROUP - 1));
915
+ pb.workgroupBarrier();
916
+ });
917
+ this.$if(pb.equal(this.lid, 0), function() {
918
+ pb.atomicExchange(this.zVT_listCounts.at(LIST_AVAILABLE), pb.int(this.total));
919
+ });
920
+ });
921
+ });
922
+ case 'appendEmpty':
923
+ case 'appendAvailable':
924
+ case 'appendEmptyCount':
925
+ case 'appendAvailableCount':
926
+ {
927
+ const input = base.startsWith('appendEmpty') ? LIST_EMPTY : LIST_AVAILABLE;
928
+ const output = base.startsWith('appendEmpty') ? LIST_AVAILABLE : LIST_REQUESTED;
929
+ const counts = base.endsWith('Count');
930
+ return program([
931
+ counts ? 1 : GROUP,
932
+ 1,
933
+ 1
934
+ ], (pb, scope)=>{
935
+ lists(pb, scope);
936
+ pb.main(function() {
937
+ this.$l.N = pb.int(this.zVT_params.at(0).y);
938
+ this.$l.inCount = pb.max(pb.atomicAdd(this.zVT_listCounts.at(input), 0), 0);
939
+ this.$l.outCount = pb.max(pb.atomicAdd(this.zVT_listCounts.at(output), 0), 0);
940
+ this.$l.copy = pb.max(0, pb.min(this.inCount, pb.sub(this.N, this.outCount)));
941
+ if (counts) {
942
+ pb.atomicExchange(this.zVT_listCounts.at(output), pb.add(this.outCount, this.copy));
943
+ pb.atomicExchange(this.zVT_listCounts.at(input), 0);
944
+ } else {
945
+ this.$l.i = pb.int(this.$builtins.globalInvocationId.x);
946
+ this.$if(pb.lessThan(this.i, this.copy), function() {
947
+ this.zVT_lists.setAt(pb.uint(pb.add(pb.mul(this.N, output), pb.add(this.outCount, this.i))), this.zVT_lists.at(pb.uint(pb.add(pb.mul(this.N, input), this.i))));
948
+ });
949
+ }
950
+ });
951
+ });
952
+ }
953
+ case 'gather':
954
+ return program([
955
+ GROUP,
956
+ 1,
957
+ 1
958
+ ], (pb, scope)=>{
959
+ scope.zVT_pageTable = pb.uint[0]().storageBufferReadonly(0);
960
+ scope.zVT_pageRequest = pb.atomic_uint[0]().storageBuffer(0);
961
+ scope.zVT_loadList = pb.uint[0]().storageBuffer(0);
962
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
963
+ that.defineLevelOf(pb);
964
+ pb.main(function() {
965
+ this.$l.i = this.$builtins.globalInvocationId.x;
966
+ this.$if(pb.lessThan(this.i, this.zVT_params.at(0).x), function() {
967
+ this.$l.count = pb.atomicExchange(this.zVT_pageRequest.at(this.i), 0);
968
+ this.$l.level = this.zVT_levelOf(this.i);
969
+ this.$l.pinned = pb.greaterThanEqual(this.level, this.zVT_params.at(1).w);
970
+ this.$if(this.pinned, function() {
971
+ this.count = pb.max(this.count, 1);
972
+ });
973
+ this.$if(pb.and(pb.greaterThan(this.count, 0), pb.equal(pb.compAnd(this.zVT_pageTable.at(this.i), PT_THIS_LOD), 0)), function() {
974
+ this.$l.priority = pb.mul(this.count, pb.add(this.level, 1));
975
+ this.$l.bucket = pb.select(pb.min(pb.uint(pb.floor(pb.log2(pb.float(this.priority)))), 30), pb.uint(HIST_PINNED_BUCKET), this.pinned);
976
+ this.$l.slot = pb.atomicAdd(this.zVT_counters.at(C_LOAD), 1);
977
+ this.$if(pb.lessThan(this.slot, this.zVT_params.at(1).z), function() {
978
+ this.zVT_loadList.setAt(pb.mul(this.slot, 3), this.i);
979
+ this.zVT_loadList.setAt(pb.add(pb.mul(this.slot, 3), 1), this.bucket);
980
+ this.zVT_loadList.setAt(pb.add(pb.mul(this.slot, 3), 2), this.level);
981
+ pb.atomicAdd(this.zVT_counters.at(pb.add(this.bucket, C_HIST)), 1);
982
+ }).$else(function() {
983
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_DROPPED), 1);
984
+ });
985
+ });
986
+ });
987
+ });
988
+ });
989
+ case 'select':
990
+ return program([
991
+ 1,
992
+ 1,
993
+ 1
994
+ ], (pb, scope)=>{
995
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
996
+ scope.zVT_listCounts = pb.atomic_int[0]().storageBuffer(0);
997
+ scope.zVT_dispatchArgs = pb.uint[0]().storageBuffer(0);
998
+ pb.main(function() {
999
+ this.$l.available = pb.uint(pb.max(loadCount(pb, this), 0));
1000
+ this.$l.budget = this.zVT_params.at(1).y;
1001
+ this.$l.refills = pb.min(pb.atomicAdd(this.zVT_counters.at(C_FILL), 0), this.budget);
1002
+ this.$l.quota = pb.min(pb.sub(this.budget, this.refills), this.available);
1003
+ this.$l.taken = pb.uint(0);
1004
+ this.$l.cut = pb.uint(INDEX_NONE);
1005
+ this.$for(pb.int('k'), 0, 32, function() {
1006
+ this.$l.b = pb.uint(pb.sub(31, this.k));
1007
+ this.$l.h = pb.atomicAdd(this.zVT_counters.at(pb.add(this.b, C_HIST)), 0);
1008
+ this.$if(pb.lessThanEqual(pb.add(this.taken, this.h), this.quota), function() {
1009
+ this.taken = pb.add(this.taken, this.h);
1010
+ }).$else(function() {
1011
+ this.cut = this.b;
1012
+ this.$break();
1013
+ });
1014
+ });
1015
+ pb.atomicExchange(this.zVT_counters.at(C_CUT), this.cut);
1016
+ pb.atomicExchange(this.zVT_counters.at(C_REMAIN), pb.sub(this.quota, this.taken));
1017
+ this.$l.loads = pb.min(pb.atomicAdd(this.zVT_counters.at(C_LOAD), 0), this.zVT_params.at(1).z);
1018
+ this.zVT_dispatchArgs.setAt(0, pb.div(pb.add(this.loads, GROUP - 1), GROUP));
1019
+ this.zVT_dispatchArgs.setAt(1, pb.uint(1));
1020
+ this.zVT_dispatchArgs.setAt(2, pb.uint(1));
1021
+ });
1022
+ });
1023
+ case 'allocate':
1024
+ return program([
1025
+ GROUP,
1026
+ 1,
1027
+ 1
1028
+ ], (pb, scope)=>{
1029
+ lists(pb, scope);
1030
+ scope.zVT_pageTable = pb.uint[0]().storageBuffer(0);
1031
+ scope.zVT_meta = pb.uint[0]().storageBuffer(0);
1032
+ scope.zVT_loadList = pb.uint[0]().storageBufferReadonly(0);
1033
+ scope.zVT_fillList = pb.uint[0]().storageBuffer(0);
1034
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
1035
+ that.definePush(pb);
1036
+ pb.main(function() {
1037
+ this.$l.k = this.$builtins.globalInvocationId.x;
1038
+ this.$l.loads = pb.min(pb.atomicAdd(this.zVT_counters.at(C_LOAD), 0), this.zVT_params.at(1).z);
1039
+ this.$if(pb.lessThan(this.k, this.loads), function() {
1040
+ this.$l.id = this.zVT_loadList.at(pb.mul(this.k, 3));
1041
+ this.$l.bucket = this.zVT_loadList.at(pb.add(pb.mul(this.k, 3), 1));
1042
+ this.$l.level = this.zVT_loadList.at(pb.add(pb.mul(this.k, 3), 2));
1043
+ this.$l.cut = pb.atomicAdd(this.zVT_counters.at(C_CUT), 0);
1044
+ this.$l.take = pb.or(pb.equal(this.cut, INDEX_NONE), pb.greaterThan(this.bucket, this.cut));
1045
+ this.$if(pb.equal(this.bucket, this.cut), function() {
1046
+ this.take = pb.lessThan(pb.atomicAdd(this.zVT_counters.at(C_TIE), 1), pb.atomicAdd(this.zVT_counters.at(C_REMAIN), 0));
1047
+ });
1048
+ this.$l.slot = pb.uint(INDEX_NONE);
1049
+ this.$if(this.take, function() {
1050
+ // A fill slot first: a page mapped without one would show another page's texels
1051
+ this.slot = pb.atomicAdd(this.zVT_counters.at(C_FILL), 1);
1052
+ this.take = pb.lessThan(this.slot, this.zVT_params.at(1).y);
1053
+ });
1054
+ this.$if(this.take, function() {
1055
+ this.$l.top = pb.atomicSub(this.zVT_listCounts.at(LIST_AVAILABLE), 1);
1056
+ // Out of pages: the slot stays reserved but empty, the fill pass skips it
1057
+ this.$if(pb.lessThanEqual(this.top, 0), function() {
1058
+ this.zVT_fillList.setAt(pb.mul(this.slot, 2), pb.uint(INDEX_NONE));
1059
+ });
1060
+ this.$if(pb.greaterThan(this.top, 0), function() {
1061
+ this.$l.N = this.zVT_params.at(0).y;
1062
+ this.$l.p = this.zVT_lists.at(pb.add(pb.mul(this.N, LIST_AVAILABLE), pb.uint(pb.sub(this.top, 1))));
1063
+ this.$l.m = pb.mul(this.p, 4);
1064
+ this.$if(pb.notEqual(pb.compAnd(this.zVT_meta.at(pb.add(this.m, 3)), META_ALLOCATED), 0), function() {
1065
+ this.zVT_pageTable.setAt(this.zVT_meta.at(this.m), pb.uint(0));
1066
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_EVICTED), 1);
1067
+ });
1068
+ this.zVT_pageTable.setAt(this.id, pb.compOr(pb.uint(PT_MAPPED), this.p));
1069
+ this.zVT_meta.setAt(this.m, this.id);
1070
+ this.zVT_meta.setAt(pb.add(this.m, 1), this.level);
1071
+ this.zVT_meta.setAt(pb.add(this.m, 2), this.zVT_params.at(0).z);
1072
+ this.zVT_meta.setAt(pb.add(this.m, 3), pb.select(pb.uint(META_ALLOCATED), pb.uint(META_ALLOCATED | META_PINNED), pb.greaterThanEqual(this.level, this.zVT_params.at(1).w)));
1073
+ this.zVT_push(LIST_REQUESTED, this.p);
1074
+ this.zVT_fillList.setAt(pb.mul(this.slot, 2), this.id);
1075
+ this.zVT_fillList.setAt(pb.add(pb.mul(this.slot, 2), 1), this.p);
1076
+ pb.atomicAdd(this.zVT_counters.at(C_STAT_ALLOCATED), 1);
1077
+ });
1078
+ });
1079
+ });
1080
+ });
1081
+ });
1082
+ case 'finalize':
1083
+ return program([
1084
+ 1,
1085
+ 1,
1086
+ 1
1087
+ ], (pb, scope)=>{
1088
+ scope.zVT_counters = pb.atomic_uint[0]().storageBuffer(0);
1089
+ scope.zVT_dispatchArgs = pb.uint[0]().storageBuffer(0);
1090
+ scope.zVT_listCounts = pb.atomic_int[0]().storageBuffer(0);
1091
+ scope.zVT_state = pb.vec4[0]().storageBuffer(0);
1092
+ pb.main(function() {
1093
+ this.$l.state = this.zVT_state.at(0);
1094
+ this.$l.residency = pb.div(pb.float(pb.max(pb.atomicAdd(this.zVT_listCounts.at(LIST_REQUESTED), 0), 0)), pb.float(this.zVT_params.at(0).y));
1095
+ this.$l.bias = this.state.x;
1096
+ this.$if(pb.and(pb.greaterThan(this.bias, 0), pb.lessThan(this.residency, this.state.z)), function() {
1097
+ this.bias = pb.sub(this.bias, pb.mul(RESIDENCY_ADJUSTMENT_RATE, pb.sub(this.state.z, this.residency)));
1098
+ }).$elseif(pb.greaterThan(this.residency, this.state.y), function() {
1099
+ this.bias = pb.add(this.bias, pb.mul(RESIDENCY_ADJUSTMENT_RATE, pb.sub(this.residency, this.state.y)));
1100
+ });
1101
+ this.bias = pb.clamp(this.bias, 0, RESIDENCY_MAX_MIP_BIAS);
1102
+ this.$if(pb.or(pb.lessThan(this.state.y, 0), pb.greaterThan(this.state.w, RESIDENCY_LOCKED_UPPER_BOUND)), function() {
1103
+ this.bias = pb.float(0);
1104
+ });
1105
+ this.zVT_state.setAt(0, pb.vec4(this.bias, this.state.yzw));
1106
+ this.zVT_dispatchArgs.setAt(3, pb.min(pb.atomicAdd(this.zVT_counters.at(C_FILL), 0), this.zVT_params.at(1).y));
1107
+ this.zVT_dispatchArgs.setAt(4, pb.uint(1));
1108
+ this.zVT_dispatchArgs.setAt(5, pb.uint(1));
1109
+ });
1110
+ });
1111
+ case 'propagate':
1112
+ return program([
1113
+ FILL_GROUP,
1114
+ FILL_GROUP,
1115
+ 1
1116
+ ], (pb, scope)=>{
1117
+ scope.zVT_pageTable = pb.uint[0]().storageBuffer(0);
1118
+ pb.main(function() {
1119
+ this.$l.xy = this.$builtins.globalInvocationId.xy;
1120
+ this.$l.info0 = this.zVT_params.at(4);
1121
+ this.$if(pb.and(pb.lessThan(this.xy.x, this.info0.y), pb.lessThan(this.xy.y, this.info0.z)), function() {
1122
+ this.$l.numLevels = this.zVT_params.at(1).x;
1123
+ this.$l.mapped = false;
1124
+ this.$l.mappedLevel = pb.uint(0);
1125
+ this.$l.mappedPhys = pb.uint(0);
1126
+ this.$for(pb.uint('k'), 0, this.numLevels, function() {
1127
+ this.$l.l = pb.sub(pb.sub(this.numLevels, 1), this.k);
1128
+ this.$l.info = this.zVT_params.at(pb.add(this.l, 4));
1129
+ this.$l.v = pb.uvec2(pb.sar(this.xy.x, this.l), pb.sar(this.xy.y, this.l));
1130
+ this.$l.id = pb.add(this.info.x, pb.add(pb.mul(this.v.y, this.info.y), this.v.x));
1131
+ this.$l.e = this.zVT_pageTable.at(this.id);
1132
+ this.$if(pb.notEqual(pb.compAnd(this.e, PT_THIS_LOD), 0), function() {
1133
+ this.mapped = true;
1134
+ this.mappedLevel = this.l;
1135
+ this.mappedPhys = pb.compAnd(this.e, PT_PHYS_MASK);
1136
+ }).$elseif(pb.and(this.mapped, pb.equal(pb.uvec2(pb.sal(this.v.x, this.l), pb.sal(this.v.y, this.l)), this.xy)), function() {
1137
+ this.zVT_pageTable.setAt(this.id, pb.compOr(pb.compOr(pb.uint(PT_ANY_LOD), pb.sal(pb.sub(this.mappedLevel, this.l), PT_LOD_SHIFT)), this.mappedPhys));
1138
+ });
1139
+ });
1140
+ });
1141
+ });
1142
+ });
1143
+ case 'debug':
1144
+ return program([
1145
+ FILL_GROUP,
1146
+ FILL_GROUP,
1147
+ 1
1148
+ ], (pb, scope)=>{
1149
+ scope.zVT_pageTable = pb.uint[0]().storageBufferReadonly(0);
1150
+ scope.zVT_meta = pb.uint[0]().storageBufferReadonly(0);
1151
+ scope.zVT_debugLevel = pb.uint().uniform(0);
1152
+ scope.zVT_debug = pb.texStorage2D.rgba8unorm().storage(0);
1153
+ pb.main(function() {
1154
+ this.$l.xy = this.$builtins.globalInvocationId.xy;
1155
+ this.$l.info0 = this.zVT_params.at(4);
1156
+ this.$if(pb.and(pb.lessThan(this.xy.x, this.info0.y), pb.lessThan(this.xy.y, this.info0.z)), function() {
1157
+ this.$l.l = this.zVT_debugLevel;
1158
+ this.$l.info = this.zVT_params.at(pb.add(this.l, 4));
1159
+ this.$l.v = pb.uvec2(pb.sar(this.xy.x, this.l), pb.sar(this.xy.y, this.l));
1160
+ this.$l.e = this.zVT_pageTable.at(pb.add(this.info.x, pb.add(pb.mul(this.v.y, this.info.y), this.v.x)));
1161
+ this.$l.color = pb.vec4(0, 0, 0, 1);
1162
+ this.$if(pb.notEqual(pb.compAnd(this.e, PT_THIS_LOD), 0), function() {
1163
+ this.$l.m = pb.mul(pb.compAnd(this.e, PT_PHYS_MASK), 4);
1164
+ this.$l.fresh = pb.equal(this.zVT_meta.at(pb.add(this.m, 2)), this.zVT_params.at(0).z);
1165
+ this.color = pb.vec4(0, pb.select(0.45, 1, this.fresh), 0, 1);
1166
+ this.$if(pb.notEqual(pb.compAnd(this.zVT_meta.at(pb.add(this.m, 3)), META_PINNED), 0), function() {
1167
+ this.color = pb.vec4(0, 0.8, 0.8, 1);
1168
+ });
1169
+ }).$elseif(pb.notEqual(pb.compAnd(this.e, PT_ANY_LOD), 0), function() {
1170
+ this.$l.lod = pb.float(pb.compAnd(pb.sar(this.e, PT_LOD_SHIFT), PT_LOD_MASK));
1171
+ this.color = pb.vec4(0.1, 0.1, pb.max(0.25, pb.sub(1, pb.mul(this.lod, 0.12))), 1);
1172
+ });
1173
+ pb.textureStore(this.zVT_debug, this.xy, this.color);
1174
+ });
1175
+ });
1176
+ });
1177
+ default:
1178
+ throw new Error(`VirtualTexture: unknown program ${name}`);
1179
+ }
1180
+ }
1181
+ onDispose() {
1182
+ super.onDispose();
1183
+ for (const tex of this._planeTextures){
1184
+ tex.dispose();
1185
+ }
1186
+ for (const buffer of [
1187
+ this._params,
1188
+ this._rects,
1189
+ this._pageTable,
1190
+ this._pageRequest,
1191
+ this._meta,
1192
+ ...this._lists,
1193
+ this._listCounts,
1194
+ this._loadList,
1195
+ this._fillList,
1196
+ this._counters,
1197
+ this._dispatchArgs,
1198
+ this._state,
1199
+ this._statsReadback
1200
+ ]){
1201
+ buffer.dispose();
1202
+ }
1203
+ for (const bindGroup of this._bindGroups.values()){
1204
+ bindGroup.dispose();
1205
+ }
1206
+ for (const program of this._programs.values()){
1207
+ program.dispose();
1208
+ }
1209
+ this._debugTexture?.dispose();
1210
+ }
1211
+ }
1212
+
1213
+ export { VirtualTexture, virtualMipChain };
1214
+ //# sourceMappingURL=virtual_texture.js.map