@woosh/meep-engine 3.64.0 → 3.65.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.
- package/CHANGELOG.md +85 -0
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts +5 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +39 -5
- package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts +65 -6
- package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +451 -101
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +4 -2
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +4 -2
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts +8 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +45 -10
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +237 -119
- package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts +143 -0
- package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts.map +1 -0
- package/src/engine/graphics/ecs/grass/GrassSpecies.js +189 -0
- package/src/engine/graphics/ecs/grass/GrassSurface.d.ts +25 -0
- package/src/engine/graphics/ecs/grass/GrassSurface.d.ts.map +1 -0
- package/src/engine/graphics/ecs/grass/GrassSurface.js +32 -0
- package/src/engine/graphics/ecs/grass/GroundCover.d.ts +68 -0
- package/src/engine/graphics/ecs/grass/GroundCover.d.ts.map +1 -0
- package/src/engine/graphics/ecs/grass/GroundCover.js +85 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts +27 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.js +91 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts +23 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.js +64 -0
- package/src/engine/graphics3/GrassSurfaceSystem.d.ts +153 -0
- package/src/engine/graphics3/GrassSurfaceSystem.d.ts.map +1 -0
- package/src/engine/graphics3/GrassSurfaceSystem.js +1076 -0
- package/src/engine/graphics3/GroundCoverSystem.d.ts +33 -0
- package/src/engine/graphics3/GroundCoverSystem.d.ts.map +1 -0
- package/src/engine/graphics3/GroundCoverSystem.js +58 -0
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +372 -110
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +6 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +9 -2
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +325 -140
- package/src/engine/network/sim/SimActionExecutor.d.ts +4 -1
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +65 -9
- package/src/engine/network/state/PredictionHistory.d.ts.map +1 -1
- package/src/engine/network/state/PredictionHistory.js +6 -1
- package/src/engine/network/time/AdaptiveRenderDelay.d.ts +25 -14
- package/src/engine/network/time/AdaptiveRenderDelay.d.ts.map +1 -1
- package/src/engine/network/time/AdaptiveRenderDelay.js +38 -20
- package/src/engine/network/time/RenderPlayout.d.ts +23 -3
- package/src/engine/network/time/RenderPlayout.d.ts.map +1 -1
- package/src/engine/network/time/RenderPlayout.js +40 -4
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts +5 -1
- package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts.map +1 -1
- package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.js +27 -1
- package/src/format/image/exr/ExrCompression.d.ts +22 -0
- package/src/format/image/exr/ExrCompression.d.ts.map +1 -0
- package/src/format/image/exr/ExrCompression.js +27 -0
- package/src/format/image/exr/ExrImage.d.ts +32 -0
- package/src/format/image/exr/ExrImage.d.ts.map +1 -0
- package/src/format/image/exr/ExrImage.js +32 -0
- package/src/format/image/exr/ExrPixelType.d.ts +13 -0
- package/src/format/image/exr/ExrPixelType.d.ts.map +1 -0
- package/src/format/image/exr/ExrPixelType.js +18 -0
- package/src/format/image/exr/exr_compare_names.d.ts +16 -0
- package/src/format/image/exr/exr_compare_names.d.ts.map +1 -0
- package/src/format/image/exr/exr_compare_names.js +25 -0
- package/src/format/image/exr/exr_constants.d.ts +49 -0
- package/src/format/image/exr/exr_constants.d.ts.map +1 -0
- package/src/format/image/exr/exr_constants.js +56 -0
- package/src/format/image/exr/exr_read.d.ts +32 -0
- package/src/format/image/exr/exr_read.d.ts.map +1 -0
- package/src/format/image/exr/exr_read.js +531 -0
- package/src/format/image/exr/exr_sample_bits.d.ts +16 -0
- package/src/format/image/exr/exr_sample_bits.d.ts.map +1 -0
- package/src/format/image/exr/exr_sample_bits.js +21 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts +17 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts.map +1 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.js +31 -0
- package/src/format/image/exr/exr_write.d.ts +27 -0
- package/src/format/image/exr/exr_write.d.ts.map +1 -0
- package/src/format/image/exr/exr_write.js +378 -0
- package/src/shade/device/shader/ShaderModuleCompiler.d.ts +2 -0
- package/src/shade/device/shader/ShaderModuleCompiler.d.ts.map +1 -1
- package/src/shade/device/shader/ShaderModuleCompiler.js +25 -2
- package/src/shade/playground/grass_surfaces/index.html +75 -0
- package/src/shade/playground/grass_surfaces/main.d.ts +2 -0
- package/src/shade/playground/grass_surfaces/main.d.ts.map +1 -0
- package/src/shade/playground/grass_surfaces/main.js +593 -0
- package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts +62 -0
- package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts.map +1 -0
- package/src/shade/playground/grass_surfaces/read_grass_surfaces.js +188 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +34 -1
- package/src/shade/playground/probe_clipmap_box/README.md +5 -1
- package/src/shade/playground/probe_clipmap_box/index.html +12 -0
- package/src/shade/playground/probe_clipmap_box/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap_box/main.js +15 -0
- package/src/shade/playground/probe_clipmap_box/scene.d.ts +8 -1
- package/src/shade/playground/probe_clipmap_box/scene.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap_box/scene.js +74 -6
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +47 -43
- package/src/shade/renderer/extension/ViewTextures.d.ts +12 -5
- package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
- package/src/shade/renderer/extension/ViewTextures.js +19 -5
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +3 -1
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts.map +1 -1
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.js +6 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +7 -0
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +10 -0
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +7 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +46 -15
- package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts +3 -2
- package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.js +3 -2
- package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts +22 -0
- package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts.map +1 -0
- package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.js +247 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +50 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +101 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +8 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +12 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts +12 -6
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +35 -29
- package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.js +4 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.js +6 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.js +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts +6 -5
- package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.js +15 -14
- package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.js +4 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +19 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +128 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +19 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +27 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts +19 -11
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.js +35 -11
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +3 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +18 -14
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts +40 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.js +174 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.d.ts +4 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.js +10 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts +31 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.js +54 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +8 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +10 -21
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +7 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +33 -29
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts +28 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.js +138 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +7 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +28 -17
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +13 -9
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +18 -11
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +18 -18
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +44 -48
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +4 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +2 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts +5 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +15 -30
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +4 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +11 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +29 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts +40 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.js +126 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts +36 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.js +221 -0
- package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts +40 -0
- package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.js +86 -0
- package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts +135 -0
- package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/GPUGrassBlades.js +676 -0
- package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts +145 -0
- package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/GRASS_BLADE.js +179 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts +63 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade.js +244 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts +58 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.js +174 -0
- package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts +25 -0
- package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/grass_blade_id_draw.js +133 -0
- package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts +80 -0
- package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/grass_blade_mechanics.js +92 -0
- package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts +29 -0
- package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/grass_blade_resolve.js +175 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts +14 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.js +45 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts +61 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/shader_grass_blade_expand.js +340 -0
- package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts +46 -0
- package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts.map +1 -0
- package/src/shade/renderer/grass/blade/shader_grass_cell_count.js +280 -0
- package/src/shade/renderer/grass/population/chunk_grass_population.d.ts +60 -0
- package/src/shade/renderer/grass/population/chunk_grass_population.d.ts.map +1 -0
- package/src/shade/renderer/grass/population/chunk_grass_population.js +171 -0
- package/src/shade/renderer/grass/press/GPUGrassPress.d.ts +99 -0
- package/src/shade/renderer/grass/press/GPUGrassPress.d.ts.map +1 -0
- package/src/shade/renderer/grass/press/GPUGrassPress.js +436 -0
- package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts +179 -0
- package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts.map +1 -0
- package/src/shade/renderer/grass/press/GRASS_PRESS.js +217 -0
- package/src/shade/renderer/grass/press/chunk_grass_press.d.ts +65 -0
- package/src/shade/renderer/grass/press/chunk_grass_press.d.ts.map +1 -0
- package/src/shade/renderer/grass/press/chunk_grass_press.js +296 -0
- package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +31 -0
- package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -0
- package/src/shade/renderer/grass/press/shader_grass_press_mark.js +169 -0
- package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts +27 -0
- package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts.map +1 -0
- package/src/shade/renderer/grass/press/shader_grass_press_stamp.js +183 -0
- package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts +193 -0
- package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/GPUGrassSurfaces.js +1253 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts +211 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE.js +241 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts +82 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.js +111 -0
- package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts +39 -0
- package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_column_cross.js +85 -0
- package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts +29 -0
- package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_directory.js +112 -0
- package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts +27 -0
- package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_slot.js +50 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts +21 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.js +41 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts +32 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surface_window.js +136 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts +37 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_surfel.js +85 -0
- package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts +25 -0
- package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/chunk_grass_tile.js +52 -0
- package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts +23 -0
- package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_directory_fill.js +87 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts +43 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake.js +265 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts +13 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.js +49 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts +62 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.js +162 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts +40 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_retire.js +155 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts +19 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.js +58 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts +30 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_want.js +251 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts +21 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts.map +1 -0
- package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.js +75 -0
- package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +4 -23
- package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts +12 -0
- package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.js +29 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +22 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +3 -0
- package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts +27 -0
- package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.js +38 -0
- package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts +28 -0
- package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.js +58 -0
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +8 -0
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +8 -0
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +21 -3
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts +37 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.js +148 -0
- package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts +20 -0
- package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.js +169 -0
- package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts +18 -0
- package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts.map +1 -1
- package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.js +28 -0
- package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts +13 -0
- package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts.map +1 -0
- package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.js +22 -0
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +4 -0
- package/src/shade/renderer/wind/GPUSceneWind.d.ts +52 -5
- package/src/shade/renderer/wind/GPUSceneWind.d.ts.map +1 -1
- package/src/shade/renderer/wind/GPUSceneWind.js +164 -8
- package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts +3 -1
- package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts.map +1 -1
- package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.js +3 -1
- package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +2 -1
- package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
- package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +18 -13
- package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts +13 -0
- package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts.map +1 -0
- package/src/shade/renderer/wind/chunk_wind_field_sample_previous.js +23 -0
- package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts +1 -0
- package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts.map +1 -1
- package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.js +6 -1
- package/src/shade/renderer/wind/wind_field_add_resources.d.ts +11 -0
- package/src/shade/renderer/wind/wind_field_add_resources.d.ts.map +1 -1
- package/src/shade/renderer/wind/wind_field_add_resources.js +15 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
|
@@ -0,0 +1,1253 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { graph_read_buffer } from "../../buffer/graph_read_buffer.js";
|
|
3
|
+
import { BufferResourceDescriptor } from "../../../device/graph/BufferResourceDescriptor.js";
|
|
4
|
+
import { HASH_TABLE_PROBE_LIMIT } from "../../gpu_primitive/hash_table/cpu_build_hash_table.js";
|
|
5
|
+
import { INDIRECT_COMPUTE_COMMAND_DESCRIPTOR } from "../../gpu_primitive/indirect/IndirectComputeCommandBuildPass.js";
|
|
6
|
+
import {
|
|
7
|
+
WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION
|
|
8
|
+
} from "../../gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js";
|
|
9
|
+
import {
|
|
10
|
+
graph_prefix_scan_csdldf,
|
|
11
|
+
prefix_scan_csdldf_output_descriptor
|
|
12
|
+
} from "../../gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js";
|
|
13
|
+
import {
|
|
14
|
+
shader_slot_stack_fill,
|
|
15
|
+
SLOT_STACK_FILL_WORKGROUP_SIZE
|
|
16
|
+
} from "../../gpu_primitive/slot_stack/shader_slot_stack_fill.js";
|
|
17
|
+
import {
|
|
18
|
+
shader_slot_stack_push_range,
|
|
19
|
+
SLOT_STACK_PUSH_RANGE_WORKGROUP_SIZE
|
|
20
|
+
} from "../../gpu_primitive/slot_stack/shader_slot_stack_push_range.js";
|
|
21
|
+
import { shader_prefix_sum_to_command } from "../../rasterize/expand/mesh/shader_prefix_sum_to_command.js";
|
|
22
|
+
import { graph_create_buffer } from "../../shader/graph/graph_create_buffer.js";
|
|
23
|
+
import { graph_import_buffer } from "../../shader/graph/graph_import_buffer.js";
|
|
24
|
+
import { GPUTileAtlas2D } from "../../texture/atlas/GPUTileAtlas2D.js";
|
|
25
|
+
import { GRASS_SURFEL_FORMAT } from "./chunk_grass_surfel.js";
|
|
26
|
+
import {
|
|
27
|
+
GRASS_POOL_FREE_COUNT,
|
|
28
|
+
GRASS_POOL_HEADER_WORDS,
|
|
29
|
+
GRASS_POOL_REMOVED,
|
|
30
|
+
GRASS_POOL_STARVED,
|
|
31
|
+
GRASS_SLOT_WORDS,
|
|
32
|
+
GRASS_TILE_COLUMNS
|
|
33
|
+
} from "./GRASS_SURFACE.js";
|
|
34
|
+
import {
|
|
35
|
+
grass_surface_level_count,
|
|
36
|
+
GRASS_SURFACE_RECORD_BLADE_LENGTH,
|
|
37
|
+
GRASS_SURFACE_RECORD_HIGH,
|
|
38
|
+
GRASS_SURFACE_RECORD_LOW,
|
|
39
|
+
GRASS_SURFACE_RECORD_NODE,
|
|
40
|
+
GRASS_SURFACE_RECORD_WORDS
|
|
41
|
+
} from "./GRASS_SURFACE_VIEW.js";
|
|
42
|
+
import { GRASS_DIRECTORY_FILL_WORKGROUP_SIZE, shader_grass_directory_fill } from "./shader_grass_directory_fill.js";
|
|
43
|
+
import { shader_grass_surface_bake } from "./shader_grass_surface_bake.js";
|
|
44
|
+
import { shader_grass_surface_bake_command } from "./shader_grass_surface_bake_command.js";
|
|
45
|
+
import {
|
|
46
|
+
GRASS_INVALIDATION_WORDS,
|
|
47
|
+
GRASS_SURFACE_INVALIDATE_RUN,
|
|
48
|
+
GRASS_SURFACE_INVALIDATE_WORKGROUP_SIZE,
|
|
49
|
+
shader_grass_surface_invalidate
|
|
50
|
+
} from "./shader_grass_surface_invalidate.js";
|
|
51
|
+
import { GRASS_SURFACE_RETIRE_WORKGROUP_SIZE, shader_grass_surface_retire } from "./shader_grass_surface_retire.js";
|
|
52
|
+
import { shader_grass_surface_schedule } from "./shader_grass_surface_schedule.js";
|
|
53
|
+
import { shader_grass_surface_want } from "./shader_grass_surface_want.js";
|
|
54
|
+
import {
|
|
55
|
+
GRASS_SURFACE_WINDOW_COUNT_WORKGROUP_SIZE,
|
|
56
|
+
shader_grass_surface_window_count
|
|
57
|
+
} from "./shader_grass_surface_window_count.js";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @typedef {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} FrameGraph
|
|
61
|
+
* @typedef {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} PipelineEnquiry
|
|
62
|
+
* @typedef {import("../../GraphicsContext.js").GraphicsContext} GraphicsContext
|
|
63
|
+
* @typedef {import("../../view/GPUViewContext.js").GPUViewContext} GPUViewContext
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Bytes a `u32` collection's count takes ahead of its elements, which sit on a 16-byte boundary.
|
|
68
|
+
* @type {number}
|
|
69
|
+
*/
|
|
70
|
+
const COLLECTION_HEADER_BYTES = 16;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Slots a pool is made with. A pool that turns asks away grows.
|
|
74
|
+
* @type {number}
|
|
75
|
+
*/
|
|
76
|
+
const INITIAL_SLOT_CAPACITY = 1024;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Surface records the list is made with room for.
|
|
80
|
+
* @type {number}
|
|
81
|
+
*/
|
|
82
|
+
const INITIAL_SURFACE_CAPACITY = 16;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Changes of ground one frame's marking reads: what its dispatch can count runs of. The rest wait a frame.
|
|
86
|
+
* @type {number}
|
|
87
|
+
*/
|
|
88
|
+
const INVALIDATIONS_PER_FRAME = WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION * GRASS_SURFACE_INVALIDATE_RUN;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Numbers a queued change of ground takes: the surface's row, and a rectangle on its frame's x and z.
|
|
92
|
+
* @type {number}
|
|
93
|
+
*/
|
|
94
|
+
const INVALIDATION_QUEUE_STRIDE = 5;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Bytes of a `u32` collection with room for `count` elements.
|
|
98
|
+
*
|
|
99
|
+
* @param {number} count
|
|
100
|
+
* @returns {number}
|
|
101
|
+
*/
|
|
102
|
+
function collection_size(count) {
|
|
103
|
+
return COLLECTION_HEADER_BYTES + Math.max(1, Math.ceil(count / 4)) * 16;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Records in the directory of a pool of `slot_capacity` slots: a power of two at least twice the slots,
|
|
108
|
+
* so the table is never more than half full, and no fewer than its probe limit.
|
|
109
|
+
*
|
|
110
|
+
* @param {number} slot_capacity
|
|
111
|
+
* @returns {number}
|
|
112
|
+
*/
|
|
113
|
+
export function grass_directory_table_size(slot_capacity) {
|
|
114
|
+
let size = HASH_TABLE_PROBE_LIMIT;
|
|
115
|
+
|
|
116
|
+
while (size < 2 * slot_capacity) {
|
|
117
|
+
size *= 2;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return size;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* How finely a scene's grass surfaces are sampled, and how fast.
|
|
125
|
+
*/
|
|
126
|
+
export class GrassSurfaceSettings {
|
|
127
|
+
/**
|
|
128
|
+
* The distance between neighbouring columns of a surface's finest level, in world units: how exactly a
|
|
129
|
+
* blade's root follows the ground, against the rays that find it and the memory that keeps it.
|
|
130
|
+
*
|
|
131
|
+
* @type {number}
|
|
132
|
+
*/
|
|
133
|
+
finest_spacing = 0.125;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The least width a blade is drawn at, in pixels of the view's render resolution. A surface's level
|
|
137
|
+
* serves the distances at which its spacing spans no more than this.
|
|
138
|
+
*
|
|
139
|
+
* @type {number}
|
|
140
|
+
*/
|
|
141
|
+
narrowest_blade = 1.5;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The least length a blade is drawn at, in pixels of the view's render resolution. No tile is asked for
|
|
145
|
+
* past the distance where its surface's tallest blade spans less.
|
|
146
|
+
*
|
|
147
|
+
* @type {number}
|
|
148
|
+
*/
|
|
149
|
+
shortest_blade = 2;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The most layers of tiles baked a view a frame, each a workgroup of {@link GRASS_TILE_SURFELS} columns:
|
|
153
|
+
* how fast ground a view turns towards gets its grass. A frame bakes what it was asked and no more, so
|
|
154
|
+
* this is the cost of the frame that asks most.
|
|
155
|
+
*
|
|
156
|
+
* @type {number}
|
|
157
|
+
*/
|
|
158
|
+
bake_capacity = 64;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* How many blades a view ray may cross before the grass behind them is left out: the grass's density
|
|
162
|
+
* where it is seen at a low angle. Lower lets more ground show through.
|
|
163
|
+
* @type {number}
|
|
164
|
+
*/
|
|
165
|
+
canopy_opacity = 6;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The grass surfaces of one scene on the device: which surfaces there are, the pool their tiles' surfels
|
|
170
|
+
* are kept in, and the passes that ask for and bake the tiles a view wants (`GRASS_SURFACE.js`).
|
|
171
|
+
*
|
|
172
|
+
* A surface is named by its `transforms` row. Its members are the mesh rows whose `mesh_ground_covers`
|
|
173
|
+
* record names that row; this class is told only the surface's box and tallest blade.
|
|
174
|
+
*
|
|
175
|
+
* Each frame: {@link update} once, then {@link graph_update} for every view that draws grass.
|
|
176
|
+
*/
|
|
177
|
+
export class GPUGrassSurfaces {
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Offer the pipelines the surfaces' passes run — see {@link PipelineEnquiry}.
|
|
181
|
+
*
|
|
182
|
+
* @param {PipelineEnquiry} enquiry
|
|
183
|
+
*/
|
|
184
|
+
static enquirePipelines(enquiry) {
|
|
185
|
+
enquiry.compute.push(shader_slot_stack_fill.pipeline);
|
|
186
|
+
enquiry.compute.push(shader_slot_stack_push_range.pipeline);
|
|
187
|
+
enquiry.compute.push(shader_grass_directory_fill.pipeline);
|
|
188
|
+
enquiry.compute.push(shader_grass_surface_invalidate.pipeline);
|
|
189
|
+
enquiry.compute.push(shader_grass_surface_retire.pipeline);
|
|
190
|
+
enquiry.compute.push(shader_grass_surface_schedule.pipeline);
|
|
191
|
+
enquiry.compute.push(shader_grass_surface_window_count.pipeline);
|
|
192
|
+
enquiry.compute.push(shader_grass_surface_want.pipeline);
|
|
193
|
+
enquiry.compute.push(shader_grass_surface_bake_command.pipeline);
|
|
194
|
+
enquiry.compute.push(shader_grass_surface_bake.pipeline);
|
|
195
|
+
enquiry.compute.push(shader_prefix_sum_to_command.pipeline);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @type {GrassSurfaceSettings}
|
|
200
|
+
*/
|
|
201
|
+
settings = new GrassSurfaceSettings();
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @type {GPUDevice}
|
|
205
|
+
*/
|
|
206
|
+
#device;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Surface records, packed from 0, as words.
|
|
210
|
+
* @type {Uint32Array}
|
|
211
|
+
*/
|
|
212
|
+
#records = new Uint32Array(INITIAL_SURFACE_CAPACITY * GRASS_SURFACE_RECORD_WORDS);
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The same memory as floats.
|
|
216
|
+
* @type {Float32Array}
|
|
217
|
+
*/
|
|
218
|
+
#records_f32 = new Float32Array(this.#records.buffer);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Each surface's place in {@link #records}, by its `transforms` row.
|
|
222
|
+
* @type {Map<number, number>}
|
|
223
|
+
*/
|
|
224
|
+
#index_of = new Map();
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @type {number}
|
|
228
|
+
*/
|
|
229
|
+
#surface_count = 0;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The device's copy of {@link #records}; null until a surface is added.
|
|
233
|
+
* @type {GPUBuffer|null}
|
|
234
|
+
*/
|
|
235
|
+
#surfaces_buffer = null;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The records the device's copy is behind on, as a range of records; empty when `from >= to`.
|
|
239
|
+
*/
|
|
240
|
+
#dirty_from = Infinity;
|
|
241
|
+
#dirty_to = 0;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The tallest blade of any surface, kept as surfaces come, go and change.
|
|
245
|
+
* @type {number}
|
|
246
|
+
*/
|
|
247
|
+
#blade_length_max = 0;
|
|
248
|
+
#blade_length_stale = false;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Slots the pool has; 0 until the pool is made.
|
|
252
|
+
* @type {number}
|
|
253
|
+
*/
|
|
254
|
+
#slot_capacity = 0;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Slots the next pool is made with, when it replaces one that had grown; 0 for the least.
|
|
258
|
+
* @type {number}
|
|
259
|
+
*/
|
|
260
|
+
#slot_capacity_wanted = 0;
|
|
261
|
+
|
|
262
|
+
/** @type {GPUBuffer|null} the pool's arena */
|
|
263
|
+
#pool = null;
|
|
264
|
+
|
|
265
|
+
/** @type {GPUBuffer|null} the free stack's slots */
|
|
266
|
+
#free = null;
|
|
267
|
+
|
|
268
|
+
/** @type {GPUBuffer|null} the directory's table */
|
|
269
|
+
#directory = null;
|
|
270
|
+
|
|
271
|
+
/** @type {GPUTileAtlas2D|null} the surfel atlas, a tile a slot */
|
|
272
|
+
#atlas = null;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Slots the pool has gained and the free stack does not hold yet: `[first, count]` each.
|
|
276
|
+
* @type {number[]}
|
|
277
|
+
*/
|
|
278
|
+
#pushes = [];
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The pool's count of starved asks, as last read off the device.
|
|
282
|
+
* @type {number}
|
|
283
|
+
*/
|
|
284
|
+
#starved = 0;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Whether an ask has starved since the pool last grew.
|
|
288
|
+
* @type {boolean}
|
|
289
|
+
*/
|
|
290
|
+
#grow_pending = false;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The frame the pool last grew in: a reading recorded before it says nothing of the pool as it is.
|
|
294
|
+
* @type {number}
|
|
295
|
+
*/
|
|
296
|
+
#grown_frame = 0;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* The pool's count of directory removals, as last read off the device, and as it stood when the
|
|
300
|
+
* directory was last made.
|
|
301
|
+
* @type {number}
|
|
302
|
+
*/
|
|
303
|
+
#removed = 0;
|
|
304
|
+
|
|
305
|
+
/** @type {number} */
|
|
306
|
+
#removed_at_fill = 0;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Whether the directory is to be made again from the slots: it is empty, or holds too many removed records.
|
|
310
|
+
* @type {boolean}
|
|
311
|
+
*/
|
|
312
|
+
#directory_stale = false;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The frame whose once-a-frame passes are recorded, and how many views it and the frame before recorded.
|
|
316
|
+
* @type {number}
|
|
317
|
+
*/
|
|
318
|
+
#lifetime_frame = -1;
|
|
319
|
+
|
|
320
|
+
/** @type {number} */
|
|
321
|
+
#views = 0;
|
|
322
|
+
|
|
323
|
+
/** @type {number} */
|
|
324
|
+
#views_before = 1;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Raised when the pool is let go of: a reading of a pool that is gone is dropped.
|
|
328
|
+
* @type {number}
|
|
329
|
+
*/
|
|
330
|
+
#generation = 0;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* The finest spacing the pool's tiles are cut by: a tile of another spacing is another piece of ground.
|
|
334
|
+
* @type {number}
|
|
335
|
+
*/
|
|
336
|
+
#pool_spacing = 0;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Whether the free stack is still to be filled: a fresh one holds zeros, which is one slot many times over.
|
|
340
|
+
* @type {boolean}
|
|
341
|
+
*/
|
|
342
|
+
#fill_pending = false;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Frames {@link update} has begun.
|
|
346
|
+
* @type {number}
|
|
347
|
+
*/
|
|
348
|
+
#frame = 0;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Changes of ground not yet marked on the device: `[node, low_x, low_z, high_x, high_z]` each.
|
|
352
|
+
* @type {number[]}
|
|
353
|
+
*/
|
|
354
|
+
#invalidations = [];
|
|
355
|
+
|
|
356
|
+
/** @type {GPUBuffer|null} the frame's changes of ground */
|
|
357
|
+
#invalidation_buffer = null;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* The frame's changes of ground as the device reads them, kept between frames.
|
|
361
|
+
* @type {Uint32Array}
|
|
362
|
+
*/
|
|
363
|
+
#invalidation_words = new Uint32Array(64 * GRASS_INVALIDATION_WORDS);
|
|
364
|
+
|
|
365
|
+
/** @type {Float32Array} */
|
|
366
|
+
#invalidation_floats = new Float32Array(this.#invalidation_words.buffer);
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @param {GraphicsContext} graphics
|
|
370
|
+
*/
|
|
371
|
+
constructor(graphics) {
|
|
372
|
+
assert.defined(graphics, 'graphics');
|
|
373
|
+
assert.equal(graphics.isGraphicsContext, true, 'graphics.isGraphicsContext !== true');
|
|
374
|
+
|
|
375
|
+
this.#device = graphics.device;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @returns {number} how many surfaces there are
|
|
380
|
+
*/
|
|
381
|
+
get surface_count() {
|
|
382
|
+
return this.#surface_count;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* @returns {number} how many slots the pool has; 0 before the first surface
|
|
387
|
+
*/
|
|
388
|
+
get slot_capacity() {
|
|
389
|
+
return this.#slot_capacity;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* The pool's arena: its counters, then a record a slot (`GRASS_SURFACE.js`). Null before the first
|
|
394
|
+
* surface, and replaced when the pool grows: read it when a pass runs.
|
|
395
|
+
*
|
|
396
|
+
* @returns {GPUBuffer|null}
|
|
397
|
+
*/
|
|
398
|
+
get pool_buffer() {
|
|
399
|
+
return this.#pool;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The pool's directory, a `chunk_hash_table_atomic` table. Null before the first surface, and replaced
|
|
404
|
+
* when the pool grows.
|
|
405
|
+
*
|
|
406
|
+
* @returns {GPUBuffer|null}
|
|
407
|
+
*/
|
|
408
|
+
get directory_buffer() {
|
|
409
|
+
return this.#directory;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* The surfel atlas, a texel a column of a slot (`chunk_grass_surfel`, `grass_slot_texel`). Null before
|
|
414
|
+
* the first surface, and replaced when the pool grows.
|
|
415
|
+
*
|
|
416
|
+
* @returns {GPUTexture|null}
|
|
417
|
+
*/
|
|
418
|
+
get atlas() {
|
|
419
|
+
return this.#atlas === null ? null : this.#atlas.texture_at(0).gpu_texture;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* A view of the surfel atlas for a pass to bind. Null before the first surface; ask again each frame.
|
|
424
|
+
*
|
|
425
|
+
* @returns {GPUTextureView|null}
|
|
426
|
+
*/
|
|
427
|
+
get atlas_view() {
|
|
428
|
+
return this.#atlas === null ? null : this.#atlas.texture_at(0).obtainView();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* The most slots the pool can grow to on this device: what the surfel atlas can hold. 0 before the
|
|
433
|
+
* first surface.
|
|
434
|
+
*
|
|
435
|
+
* @returns {number}
|
|
436
|
+
*/
|
|
437
|
+
get slot_capacity_limit() {
|
|
438
|
+
return this.#atlas === null ? 0 : this.#atlas.capacity_limit;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* The list of surface records (`GRASS_SURFACE_RECORD_STRUCT`), {@link surface_count} of them. Null
|
|
443
|
+
* before the first surface, and replaced when the list grows.
|
|
444
|
+
*
|
|
445
|
+
* @returns {GPUBuffer|null}
|
|
446
|
+
*/
|
|
447
|
+
get surfaces_buffer() {
|
|
448
|
+
return this.#surfaces_buffer;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Whether `node` is a surface here.
|
|
453
|
+
*
|
|
454
|
+
* @param {number} node a `transforms` row
|
|
455
|
+
* @returns {boolean}
|
|
456
|
+
*/
|
|
457
|
+
has(node) {
|
|
458
|
+
return this.#index_of.has(node);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Make a surface of a `transforms` row, or change the one it is.
|
|
463
|
+
*
|
|
464
|
+
* @param {number} node the surface's `transforms` row; not the scene's root
|
|
465
|
+
* @param {ArrayLike<number>} low the least corner of a box, in the surface's frame, that holds every member
|
|
466
|
+
* @param {ArrayLike<number>} high the box's greatest corner
|
|
467
|
+
* @param {number} blade_length the tallest blade that grows on any member, world units; 0 when none grows
|
|
468
|
+
*/
|
|
469
|
+
set(node, low, high, blade_length) {
|
|
470
|
+
assert.isPositiveInteger(node, 'node');
|
|
471
|
+
assert.isFinite(blade_length, 'blade_length');
|
|
472
|
+
assert.greaterThanOrEqual(blade_length, 0, 'blade_length');
|
|
473
|
+
|
|
474
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
475
|
+
assert.isFinite(low[axis], `low[${axis}]`);
|
|
476
|
+
assert.isFinite(high[axis], `high[${axis}]`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
let index = this.#index_of.get(node);
|
|
480
|
+
|
|
481
|
+
if (index === undefined) {
|
|
482
|
+
index = this.#surface_count++;
|
|
483
|
+
|
|
484
|
+
this.#index_of.set(node, index);
|
|
485
|
+
|
|
486
|
+
if (this.#records.length < this.#surface_count * GRASS_SURFACE_RECORD_WORDS) {
|
|
487
|
+
const grown = new Uint32Array(2 * this.#records.length);
|
|
488
|
+
|
|
489
|
+
grown.set(this.#records);
|
|
490
|
+
|
|
491
|
+
this.#records = grown;
|
|
492
|
+
this.#records_f32 = new Float32Array(grown.buffer);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const at = index * GRASS_SURFACE_RECORD_WORDS;
|
|
497
|
+
const words = this.#records;
|
|
498
|
+
const floats = this.#records_f32;
|
|
499
|
+
|
|
500
|
+
if (floats[at + GRASS_SURFACE_RECORD_BLADE_LENGTH] === this.#blade_length_max && blade_length < this.#blade_length_max) {
|
|
501
|
+
this.#blade_length_stale = true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
words[at + GRASS_SURFACE_RECORD_NODE] = node;
|
|
505
|
+
floats[at + GRASS_SURFACE_RECORD_BLADE_LENGTH] = blade_length;
|
|
506
|
+
|
|
507
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
508
|
+
floats[at + GRASS_SURFACE_RECORD_LOW + axis] = low[axis];
|
|
509
|
+
floats[at + GRASS_SURFACE_RECORD_HIGH + axis] = high[axis];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
this.#blade_length_max = Math.max(this.#blade_length_max, blade_length);
|
|
513
|
+
|
|
514
|
+
this.#mark(index);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* A row stops being a surface. The slots that hold its tiles stay taken until the pool has need of them.
|
|
519
|
+
*
|
|
520
|
+
* @param {number} node a `transforms` row; one that is no surface is left alone
|
|
521
|
+
*/
|
|
522
|
+
remove(node) {
|
|
523
|
+
const index = this.#index_of.get(node);
|
|
524
|
+
|
|
525
|
+
if (index === undefined) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
this.#index_of.delete(node);
|
|
530
|
+
|
|
531
|
+
const last = --this.#surface_count;
|
|
532
|
+
|
|
533
|
+
const at = index * GRASS_SURFACE_RECORD_WORDS;
|
|
534
|
+
const words = this.#records;
|
|
535
|
+
|
|
536
|
+
if (this.#records_f32[at + GRASS_SURFACE_RECORD_BLADE_LENGTH] === this.#blade_length_max) {
|
|
537
|
+
this.#blade_length_stale = true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (index !== last) {
|
|
541
|
+
// the last record takes the place left
|
|
542
|
+
const from = last * GRASS_SURFACE_RECORD_WORDS;
|
|
543
|
+
|
|
544
|
+
words.copyWithin(at, from, from + GRASS_SURFACE_RECORD_WORDS);
|
|
545
|
+
|
|
546
|
+
this.#index_of.set(words[at + GRASS_SURFACE_RECORD_NODE], index);
|
|
547
|
+
|
|
548
|
+
this.#mark(index);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* The ground of a surface has changed inside a rectangle: a member has come, gone or moved there. Every
|
|
554
|
+
* layer of every tile the rectangle touches, of every level, is baked again, where it is, when a view
|
|
555
|
+
* next asks for it. What it costs does not depend on the rectangle's size.
|
|
556
|
+
*
|
|
557
|
+
* @param {number} node the surface's `transforms` row
|
|
558
|
+
* @param {number} low_x the rectangle, in the surface's frame
|
|
559
|
+
* @param {number} low_z
|
|
560
|
+
* @param {number} high_x
|
|
561
|
+
* @param {number} high_z
|
|
562
|
+
*/
|
|
563
|
+
invalidate(node, low_x, low_z, high_x, high_z) {
|
|
564
|
+
assert.isPositiveInteger(node, 'node');
|
|
565
|
+
assert.isFinite(low_x, 'low_x');
|
|
566
|
+
assert.isFinite(low_z, 'low_z');
|
|
567
|
+
assert.isFinite(high_x, 'high_x');
|
|
568
|
+
assert.isFinite(high_z, 'high_z');
|
|
569
|
+
assert.lessThanOrEqual(low_x, high_x, 'low_x');
|
|
570
|
+
assert.lessThanOrEqual(low_z, high_z, 'low_z');
|
|
571
|
+
|
|
572
|
+
this.#invalidations.push(node, low_x, low_z, high_x, high_z);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Record the pass that marks the queued changes of ground.
|
|
577
|
+
*
|
|
578
|
+
* @param {FrameGraph} graph
|
|
579
|
+
*/
|
|
580
|
+
#graph_invalidate(graph) {
|
|
581
|
+
const queue = this.#invalidations;
|
|
582
|
+
|
|
583
|
+
if (queue.length === 0) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const record_count = Math.min(queue.length / INVALIDATION_QUEUE_STRIDE, INVALIDATIONS_PER_FRAME);
|
|
588
|
+
const word_count = record_count * GRASS_INVALIDATION_WORDS;
|
|
589
|
+
|
|
590
|
+
if (this.#invalidation_words.length < word_count) {
|
|
591
|
+
this.#invalidation_words = new Uint32Array(Math.max(word_count, this.#invalidation_words.length * 2));
|
|
592
|
+
this.#invalidation_floats = new Float32Array(this.#invalidation_words.buffer);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const words = this.#invalidation_words;
|
|
596
|
+
const floats = this.#invalidation_floats;
|
|
597
|
+
|
|
598
|
+
for (let i = 0; i < record_count; i++) {
|
|
599
|
+
const from = i * INVALIDATION_QUEUE_STRIDE;
|
|
600
|
+
const to = i * GRASS_INVALIDATION_WORDS;
|
|
601
|
+
|
|
602
|
+
words[to] = queue[from];
|
|
603
|
+
floats[to + 1] = queue[from + 1];
|
|
604
|
+
floats[to + 2] = queue[from + 2];
|
|
605
|
+
floats[to + 3] = queue[from + 3];
|
|
606
|
+
floats[to + 4] = queue[from + 4];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// what was not taken moves to the queue's front
|
|
610
|
+
const taken = record_count * INVALIDATION_QUEUE_STRIDE;
|
|
611
|
+
|
|
612
|
+
queue.copyWithin(0, taken);
|
|
613
|
+
queue.length -= taken;
|
|
614
|
+
|
|
615
|
+
const device = this.#device;
|
|
616
|
+
const bytes = word_count * 4;
|
|
617
|
+
|
|
618
|
+
if (this.#invalidation_buffer === null || this.#invalidation_buffer.size < bytes) {
|
|
619
|
+
this.#invalidation_buffer?.destroy();
|
|
620
|
+
|
|
621
|
+
this.#invalidation_buffer = device.createBuffer({
|
|
622
|
+
label: "grass / invalidations",
|
|
623
|
+
size: words.byteLength,
|
|
624
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
device.queue.writeBuffer(this.#invalidation_buffer, 0, words, 0, word_count);
|
|
629
|
+
|
|
630
|
+
const buffer = this.#invalidation_buffer;
|
|
631
|
+
const slot_capacity = this.#slot_capacity;
|
|
632
|
+
const finest_spacing = this.settings.finest_spacing;
|
|
633
|
+
|
|
634
|
+
const pass = graph.add("invalidate tiles", {}, (data, resources, context) => {
|
|
635
|
+
shader_grass_surface_invalidate.dispatch({
|
|
636
|
+
encoder: context.encoder,
|
|
637
|
+
group_count_x: Math.min(Math.ceil(slot_capacity / GRASS_SURFACE_INVALIDATE_WORKGROUP_SIZE), WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION),
|
|
638
|
+
group_count_y: Math.ceil(record_count / GRASS_SURFACE_INVALIDATE_RUN),
|
|
639
|
+
bindings: {
|
|
640
|
+
settings: { finest_spacing, record_count, slot_capacity },
|
|
641
|
+
grass_invalidations: buffer,
|
|
642
|
+
grass_pool: this.#pool,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// it writes the pool, which the graph does not hold
|
|
648
|
+
pass.make_side_effect();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* @param {number} index a record the device's copy is now behind on
|
|
653
|
+
*/
|
|
654
|
+
#mark(index) {
|
|
655
|
+
this.#dirty_from = Math.min(this.#dirty_from, index);
|
|
656
|
+
this.#dirty_to = Math.max(this.#dirty_to, index + 1);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* @returns {number} the tallest blade of any surface
|
|
661
|
+
*/
|
|
662
|
+
#tallest_blade() {
|
|
663
|
+
if (this.#blade_length_stale) {
|
|
664
|
+
this.#blade_length_stale = false;
|
|
665
|
+
|
|
666
|
+
let tallest = 0;
|
|
667
|
+
|
|
668
|
+
for (let i = 0; i < this.#surface_count; i++) {
|
|
669
|
+
tallest = Math.max(tallest, this.#records_f32[i * GRASS_SURFACE_RECORD_WORDS + GRASS_SURFACE_RECORD_BLADE_LENGTH]);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
this.#blade_length_max = tallest;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return this.#blade_length_max;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Begin a frame: bring the device's list of surfaces up to date, and make the pool if there is a
|
|
680
|
+
* surface and no pool yet. Call once a frame, before any view's {@link graph_update}.
|
|
681
|
+
*/
|
|
682
|
+
update() {
|
|
683
|
+
this.#frame++;
|
|
684
|
+
|
|
685
|
+
this.#views_before = Math.max(1, this.#views);
|
|
686
|
+
this.#views = 0;
|
|
687
|
+
|
|
688
|
+
if (this.#pool !== null && this.#pool_spacing !== this.settings.finest_spacing) {
|
|
689
|
+
// every tile it holds is of a lattice that is no more: a fresh pool of its size, when a surface next needs one
|
|
690
|
+
const slot_capacity = this.#slot_capacity;
|
|
691
|
+
|
|
692
|
+
this.#drop_pool();
|
|
693
|
+
|
|
694
|
+
this.#slot_capacity_wanted = slot_capacity;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (this.#pool !== null) {
|
|
698
|
+
if (this.#grow_pending) {
|
|
699
|
+
this.#grow_pending = false;
|
|
700
|
+
this.#grown_frame = this.#frame;
|
|
701
|
+
|
|
702
|
+
this.#grow();
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// a quarter of the table removed: with the half the slots can fill, a miss still ends within four probes or so
|
|
706
|
+
if (this.#removed - this.#removed_at_fill >= grass_directory_table_size(this.#slot_capacity) / 4) {
|
|
707
|
+
this.#directory_stale = true;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (this.#surface_count === 0) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const device = this.#device;
|
|
716
|
+
|
|
717
|
+
const needed_bytes = this.#surface_count * GRASS_SURFACE_RECORD_WORDS * 4;
|
|
718
|
+
|
|
719
|
+
if (this.#surfaces_buffer === null || this.#surfaces_buffer.size < needed_bytes) {
|
|
720
|
+
this.#surfaces_buffer?.destroy();
|
|
721
|
+
|
|
722
|
+
this.#surfaces_buffer = device.createBuffer({
|
|
723
|
+
label: "grass / surfaces",
|
|
724
|
+
size: this.#records.byteLength,
|
|
725
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
this.#dirty_from = 0;
|
|
729
|
+
this.#dirty_to = this.#surface_count;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (this.#dirty_from < this.#dirty_to) {
|
|
733
|
+
// a record that has since left the list's end needs no write
|
|
734
|
+
const to = Math.min(this.#dirty_to, this.#surface_count);
|
|
735
|
+
const from = Math.min(this.#dirty_from, to);
|
|
736
|
+
|
|
737
|
+
if (from < to) {
|
|
738
|
+
device.queue.writeBuffer(
|
|
739
|
+
this.#surfaces_buffer,
|
|
740
|
+
from * GRASS_SURFACE_RECORD_WORDS * 4,
|
|
741
|
+
this.#records,
|
|
742
|
+
from * GRASS_SURFACE_RECORD_WORDS,
|
|
743
|
+
(to - from) * GRASS_SURFACE_RECORD_WORDS,
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
this.#dirty_from = Infinity;
|
|
748
|
+
this.#dirty_to = 0;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (this.#pool === null) {
|
|
752
|
+
this.#make_pool(Math.max(INITIAL_SLOT_CAPACITY, this.#slot_capacity_wanted));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* @param {number} slot_capacity
|
|
758
|
+
*/
|
|
759
|
+
#make_pool(slot_capacity) {
|
|
760
|
+
const device = this.#device;
|
|
761
|
+
|
|
762
|
+
this.#slot_capacity = slot_capacity;
|
|
763
|
+
this.#pool_spacing = this.settings.finest_spacing;
|
|
764
|
+
|
|
765
|
+
this.#pool = device.createBuffer({
|
|
766
|
+
label: "grass / pool",
|
|
767
|
+
size: (GRASS_POOL_HEADER_WORDS + slot_capacity * GRASS_SLOT_WORDS) * 4,
|
|
768
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// every slot is free
|
|
772
|
+
const header = new Uint32Array(GRASS_POOL_HEADER_WORDS);
|
|
773
|
+
|
|
774
|
+
header[GRASS_POOL_FREE_COUNT] = slot_capacity;
|
|
775
|
+
|
|
776
|
+
device.queue.writeBuffer(this.#pool, 0, header);
|
|
777
|
+
|
|
778
|
+
this.#free = device.createBuffer({
|
|
779
|
+
label: "grass / free slots",
|
|
780
|
+
size: slot_capacity * 4,
|
|
781
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
this.#fill_pending = true;
|
|
785
|
+
|
|
786
|
+
const table_size = grass_directory_table_size(slot_capacity);
|
|
787
|
+
|
|
788
|
+
this.#directory = device.createBuffer({
|
|
789
|
+
label: "grass / directory",
|
|
790
|
+
size: (2 + 2 * table_size) * 4,
|
|
791
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// [records held, table size]; the records themselves start empty
|
|
795
|
+
device.queue.writeBuffer(this.#directory, 0, Uint32Array.of(0, table_size));
|
|
796
|
+
|
|
797
|
+
this.#atlas = new GPUTileAtlas2D({
|
|
798
|
+
device,
|
|
799
|
+
formats: [GRASS_SURFEL_FORMAT],
|
|
800
|
+
tile_size: [GRASS_TILE_COLUMNS, GRASS_TILE_COLUMNS],
|
|
801
|
+
usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
|
|
802
|
+
label: "grass / surfels",
|
|
803
|
+
capacity: slot_capacity,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Double the pool: the atlas adds rows, the arena and the free stack grow with a copy, the new slots
|
|
809
|
+
* are pushed by the next frame, and the directory is made again in a table of the new size.
|
|
810
|
+
*/
|
|
811
|
+
#grow() {
|
|
812
|
+
const old_capacity = this.#slot_capacity;
|
|
813
|
+
const capacity = Math.min(2 * old_capacity, this.#atlas.capacity_limit);
|
|
814
|
+
|
|
815
|
+
if (capacity <= old_capacity) {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const device = this.#device;
|
|
820
|
+
|
|
821
|
+
this.#atlas.ensure_capacity(capacity);
|
|
822
|
+
|
|
823
|
+
this.#pool = this.#regrow(this.#pool, (GRASS_POOL_HEADER_WORDS + capacity * GRASS_SLOT_WORDS) * 4);
|
|
824
|
+
this.#free = this.#regrow(this.#free, capacity * 4);
|
|
825
|
+
|
|
826
|
+
const table_size = grass_directory_table_size(capacity);
|
|
827
|
+
|
|
828
|
+
this.#directory.destroy();
|
|
829
|
+
|
|
830
|
+
this.#directory = device.createBuffer({
|
|
831
|
+
label: "grass / directory",
|
|
832
|
+
size: (2 + 2 * table_size) * 4,
|
|
833
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// [records held, table size]; the next frame lists the slots in it
|
|
837
|
+
device.queue.writeBuffer(this.#directory, 0, Uint32Array.of(0, table_size));
|
|
838
|
+
|
|
839
|
+
this.#directory_stale = true;
|
|
840
|
+
|
|
841
|
+
this.#slot_capacity = capacity;
|
|
842
|
+
this.#pushes.push(old_capacity, capacity - old_capacity);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* A larger buffer with the old one's bytes, copied ahead of the frame's commands; the old one is
|
|
847
|
+
* destroyed after the copy is submitted.
|
|
848
|
+
*
|
|
849
|
+
* @param {GPUBuffer} old
|
|
850
|
+
* @param {number} size bytes
|
|
851
|
+
* @returns {GPUBuffer}
|
|
852
|
+
*/
|
|
853
|
+
#regrow(old, size) {
|
|
854
|
+
const device = this.#device;
|
|
855
|
+
|
|
856
|
+
const buffer = device.createBuffer({ label: old.label, size, usage: old.usage });
|
|
857
|
+
|
|
858
|
+
const encoder = device.createCommandEncoder({ label: `${old.label} / growth` });
|
|
859
|
+
|
|
860
|
+
encoder.copyBufferToBuffer(old, 0, buffer, 0, old.size);
|
|
861
|
+
device.queue.submit([encoder.finish()]);
|
|
862
|
+
|
|
863
|
+
old.destroy();
|
|
864
|
+
|
|
865
|
+
return buffer;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Record the passes that run once a frame, ahead of any view's asks: the pool's new slots onto the
|
|
870
|
+
* free stack, the directory made again if it is due, the changes of ground, and the retire.
|
|
871
|
+
*
|
|
872
|
+
* @param {FrameGraph} graph
|
|
873
|
+
*/
|
|
874
|
+
#graph_begin_frame(graph) {
|
|
875
|
+
const slot_capacity = this.#slot_capacity;
|
|
876
|
+
const frame = this.#frame;
|
|
877
|
+
|
|
878
|
+
const fill_stack = this.#fill_pending;
|
|
879
|
+
|
|
880
|
+
this.#fill_pending = false;
|
|
881
|
+
|
|
882
|
+
const pushes = this.#pushes.slice();
|
|
883
|
+
|
|
884
|
+
this.#pushes.length = 0;
|
|
885
|
+
|
|
886
|
+
const fill_directory = this.#directory_stale;
|
|
887
|
+
|
|
888
|
+
if (fill_directory) {
|
|
889
|
+
this.#directory_stale = false;
|
|
890
|
+
this.#removed_at_fill = this.#removed;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const table_size = grass_directory_table_size(slot_capacity);
|
|
894
|
+
|
|
895
|
+
const pass = graph.add("retire tiles", {}, (data, resources, context) => {
|
|
896
|
+
const encoder = context.encoder;
|
|
897
|
+
|
|
898
|
+
if (fill_stack) {
|
|
899
|
+
shader_slot_stack_fill.dispatch({
|
|
900
|
+
encoder,
|
|
901
|
+
group_count_x: Math.min(Math.ceil(slot_capacity / SLOT_STACK_FILL_WORKGROUP_SIZE), WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION),
|
|
902
|
+
bindings: { capacity: slot_capacity, slots: this.#free },
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
for (let i = 0; i < pushes.length; i += 2) {
|
|
907
|
+
shader_slot_stack_push_range.dispatch({
|
|
908
|
+
encoder,
|
|
909
|
+
group_count_x: Math.min(Math.ceil(pushes[i + 1] / SLOT_STACK_PUSH_RANGE_WORKGROUP_SIZE), WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION),
|
|
910
|
+
bindings: {
|
|
911
|
+
settings: { first: pushes[i], count: pushes[i + 1], counter: GRASS_POOL_FREE_COUNT },
|
|
912
|
+
counts: this.#pool,
|
|
913
|
+
slots: this.#free,
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (fill_directory) {
|
|
919
|
+
// no record, in a table of its size: the count, then every record
|
|
920
|
+
encoder.clearBuffer(this.#directory, 0, 4);
|
|
921
|
+
encoder.clearBuffer(this.#directory, 8, table_size * 8);
|
|
922
|
+
|
|
923
|
+
shader_grass_directory_fill.dispatch({
|
|
924
|
+
encoder,
|
|
925
|
+
group_count_x: Math.ceil(slot_capacity / GRASS_DIRECTORY_FILL_WORKGROUP_SIZE),
|
|
926
|
+
bindings: { slot_capacity, grass_pool: this.#pool, grass_directory: this.#directory },
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
shader_grass_surface_retire.dispatch({
|
|
931
|
+
encoder,
|
|
932
|
+
group_count_x: Math.ceil(slot_capacity / GRASS_SURFACE_RETIRE_WORKGROUP_SIZE),
|
|
933
|
+
bindings: {
|
|
934
|
+
settings: { frame, slot_capacity },
|
|
935
|
+
grass_pool: this.#pool,
|
|
936
|
+
grass_directory: this.#directory,
|
|
937
|
+
grass_free: this.#free,
|
|
938
|
+
},
|
|
939
|
+
});
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
// it writes the pool, which the graph does not hold
|
|
943
|
+
pass.make_side_effect();
|
|
944
|
+
|
|
945
|
+
this.#graph_invalidate(graph);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Record the pass that plans the next frame's eviction, and the reading of the pool's counters that
|
|
950
|
+
* growth and the directory's upkeep follow.
|
|
951
|
+
*
|
|
952
|
+
* @param {FrameGraph} graph
|
|
953
|
+
*/
|
|
954
|
+
#graph_end_frame(graph) {
|
|
955
|
+
const slack = this.settings.bake_capacity * this.#views_before;
|
|
956
|
+
|
|
957
|
+
const pass = graph.add("schedule eviction", {}, (data, resources, context) => {
|
|
958
|
+
shader_grass_surface_schedule.dispatch({
|
|
959
|
+
encoder: context.encoder,
|
|
960
|
+
bindings: { slack, grass_pool: this.#pool },
|
|
961
|
+
});
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
pass.make_side_effect();
|
|
965
|
+
|
|
966
|
+
const read_frame = this.#frame;
|
|
967
|
+
const generation = this.#generation;
|
|
968
|
+
|
|
969
|
+
graph_read_buffer({
|
|
970
|
+
graph,
|
|
971
|
+
buffer: graph_import_buffer(graph, this.#pool, "grass / pool"),
|
|
972
|
+
offset: 0,
|
|
973
|
+
size: 4 * (Math.max(GRASS_POOL_STARVED, GRASS_POOL_REMOVED) + 1),
|
|
974
|
+
})
|
|
975
|
+
.then(reference => {
|
|
976
|
+
try {
|
|
977
|
+
if (generation !== this.#generation) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const words = new Uint32Array(reference.getValue());
|
|
982
|
+
|
|
983
|
+
if (words[GRASS_POOL_STARVED] !== this.#starved) {
|
|
984
|
+
this.#starved = words[GRASS_POOL_STARVED];
|
|
985
|
+
this.#grow_pending ||= read_frame >= this.#grown_frame;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
this.#removed = words[GRASS_POOL_REMOVED];
|
|
989
|
+
} finally {
|
|
990
|
+
reference.release();
|
|
991
|
+
}
|
|
992
|
+
})
|
|
993
|
+
.catch(() => {
|
|
994
|
+
// a reading lost to a timeout or a lost device: the counts run on, and a later reading carries them
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* What a view asks of the surfaces this frame, as the passes' immediate (`GRASS_SURFACE_VIEW_SETTINGS`):
|
|
1000
|
+
* the settings, the view's focal length in pixels, the frame and the counts. `blade_capacity` is left
|
|
1001
|
+
* at zero for whoever keeps a blade list.
|
|
1002
|
+
*
|
|
1003
|
+
* @param {GPUViewContext} view
|
|
1004
|
+
* @returns {object|null} null while there is no pool, no surface, or nothing grows on any
|
|
1005
|
+
*/
|
|
1006
|
+
view_settings(view) {
|
|
1007
|
+
const tallest = this.#tallest_blade();
|
|
1008
|
+
|
|
1009
|
+
if (this.#pool === null || this.#surface_count === 0 || tallest <= 0) {
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const settings = this.settings;
|
|
1014
|
+
|
|
1015
|
+
return {
|
|
1016
|
+
finest_spacing: settings.finest_spacing,
|
|
1017
|
+
// what a unit a unit away spans, in pixels: the projection's vertical scale over half the view's height
|
|
1018
|
+
focal: view.camera.camera.projection_matrix[5] * view.resolution[1] * 0.5,
|
|
1019
|
+
narrowest_blade: settings.narrowest_blade,
|
|
1020
|
+
shortest_blade: settings.shortest_blade,
|
|
1021
|
+
frame: this.#frame,
|
|
1022
|
+
level_count: grass_surface_level_count(tallest, settings.finest_spacing, settings.narrowest_blade, settings.shortest_blade),
|
|
1023
|
+
surface_count: this.#surface_count,
|
|
1024
|
+
job_capacity: settings.bake_capacity,
|
|
1025
|
+
slot_capacity: this.#slot_capacity,
|
|
1026
|
+
canopy_opacity: settings.canopy_opacity,
|
|
1027
|
+
blade_capacity: 0,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Record a view's asks for tiles, and the bake of those taken: the passes of `GRASS_SURFACE.js`'s
|
|
1033
|
+
* lifetime, in the frame graph. Records nothing while there is no surface.
|
|
1034
|
+
*
|
|
1035
|
+
* @param {object} options
|
|
1036
|
+
* @param {FrameGraph} options.graph
|
|
1037
|
+
* @param {GPUViewContext} options.view the view; its scene is the surfaces' scene
|
|
1038
|
+
*/
|
|
1039
|
+
graph_update({ graph, view }) {
|
|
1040
|
+
assert.defined(graph, 'graph');
|
|
1041
|
+
assert.defined(view, 'view');
|
|
1042
|
+
|
|
1043
|
+
if (this.#pool === null) {
|
|
1044
|
+
// no pool holds a tile to mark
|
|
1045
|
+
this.#invalidations.length = 0;
|
|
1046
|
+
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
graph.push_scope("grass surfaces");
|
|
1051
|
+
|
|
1052
|
+
// the first view a frame records carries the passes that run once a frame
|
|
1053
|
+
const first_view = this.#lifetime_frame !== this.#frame;
|
|
1054
|
+
|
|
1055
|
+
this.#lifetime_frame = this.#frame;
|
|
1056
|
+
this.#views++;
|
|
1057
|
+
|
|
1058
|
+
if (first_view) {
|
|
1059
|
+
this.#graph_begin_frame(graph);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const settings = this.settings;
|
|
1063
|
+
|
|
1064
|
+
const tallest = this.#tallest_blade();
|
|
1065
|
+
|
|
1066
|
+
if (this.#surface_count === 0 || tallest <= 0) {
|
|
1067
|
+
// no surface, or nothing grows on any: no view wants a tile
|
|
1068
|
+
if (first_view) {
|
|
1069
|
+
this.#graph_end_frame(graph);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
graph.pop_scope();
|
|
1073
|
+
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const view_settings = this.view_settings(view);
|
|
1078
|
+
|
|
1079
|
+
const level_count = view_settings.level_count;
|
|
1080
|
+
|
|
1081
|
+
const window_total = this.#surface_count * level_count;
|
|
1082
|
+
|
|
1083
|
+
const camera = view.camera;
|
|
1084
|
+
|
|
1085
|
+
const scene = view.scene;
|
|
1086
|
+
|
|
1087
|
+
const count_data = {};
|
|
1088
|
+
|
|
1089
|
+
const count_pass = graph.add("count windows", count_data, (data, resources, context) => {
|
|
1090
|
+
shader_grass_surface_window_count.dispatch({
|
|
1091
|
+
encoder: context.encoder,
|
|
1092
|
+
group_count_x: Math.ceil(window_total / GRASS_SURFACE_WINDOW_COUNT_WORKGROUP_SIZE),
|
|
1093
|
+
bindings: {
|
|
1094
|
+
settings: view_settings,
|
|
1095
|
+
camera: camera.gpu_buffer,
|
|
1096
|
+
scene_database: scene.scene_database_buffer,
|
|
1097
|
+
grass_surfaces: this.#surfaces_buffer,
|
|
1098
|
+
grass_window_counts: resources.get(data.counts),
|
|
1099
|
+
},
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
count_data.counts = count_pass.create("window counts", BufferResourceDescriptor.from(
|
|
1104
|
+
collection_size(window_total),
|
|
1105
|
+
GPUBufferUsage.STORAGE,
|
|
1106
|
+
// the count, which thread 0 writes
|
|
1107
|
+
0, COLLECTION_HEADER_BYTES,
|
|
1108
|
+
));
|
|
1109
|
+
|
|
1110
|
+
const sums = graph_prefix_scan_csdldf({
|
|
1111
|
+
graph,
|
|
1112
|
+
input: count_data.counts,
|
|
1113
|
+
output: graph_create_buffer(graph, "window offsets", prefix_scan_csdldf_output_descriptor(window_total)),
|
|
1114
|
+
element_max_count: window_total,
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
const command_data = {};
|
|
1118
|
+
|
|
1119
|
+
const command_pass = graph.add("make want command", command_data, (data, resources, context) => {
|
|
1120
|
+
shader_prefix_sum_to_command.dispatch({
|
|
1121
|
+
encoder: context.encoder,
|
|
1122
|
+
bindings: {
|
|
1123
|
+
prefix_sums: resources.get(data.prefix_sums),
|
|
1124
|
+
command: resources.get(data.command),
|
|
1125
|
+
},
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
command_data.prefix_sums = command_pass.read(sums);
|
|
1130
|
+
command_data.command = command_pass.create("want command", INDIRECT_COMPUTE_COMMAND_DESCRIPTOR);
|
|
1131
|
+
|
|
1132
|
+
const want_data = {};
|
|
1133
|
+
|
|
1134
|
+
const want_pass = graph.add("want tiles", want_data, (data, resources, context) => {
|
|
1135
|
+
shader_grass_surface_want.dispatchIndirect({
|
|
1136
|
+
encoder: context.encoder,
|
|
1137
|
+
command: resources.get(data.command),
|
|
1138
|
+
bindings: {
|
|
1139
|
+
settings: view_settings,
|
|
1140
|
+
camera: camera.gpu_buffer,
|
|
1141
|
+
scene_database: scene.scene_database_buffer,
|
|
1142
|
+
grass_surfaces: this.#surfaces_buffer,
|
|
1143
|
+
prefix_sums: resources.get(data.prefix_sums),
|
|
1144
|
+
grass_pool: this.#pool,
|
|
1145
|
+
grass_directory: this.#directory,
|
|
1146
|
+
grass_free: this.#free,
|
|
1147
|
+
grass_jobs: resources.get(data.jobs),
|
|
1148
|
+
},
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
want_data.command = want_pass.read(command_data.command);
|
|
1153
|
+
want_data.prefix_sums = want_pass.read(sums);
|
|
1154
|
+
want_data.jobs = want_pass.create("bake list", BufferResourceDescriptor.from(
|
|
1155
|
+
collection_size(settings.bake_capacity),
|
|
1156
|
+
GPUBufferUsage.STORAGE,
|
|
1157
|
+
// the count, which the asks raise from zero
|
|
1158
|
+
0, COLLECTION_HEADER_BYTES,
|
|
1159
|
+
));
|
|
1160
|
+
|
|
1161
|
+
const bake_command_data = {};
|
|
1162
|
+
|
|
1163
|
+
const bake_command_pass = graph.add("make bake command", bake_command_data, (data, resources, context) => {
|
|
1164
|
+
shader_grass_surface_bake_command.dispatch({
|
|
1165
|
+
encoder: context.encoder,
|
|
1166
|
+
bindings: {
|
|
1167
|
+
job_capacity: settings.bake_capacity,
|
|
1168
|
+
grass_jobs: resources.get(data.jobs),
|
|
1169
|
+
command: resources.get(data.command),
|
|
1170
|
+
},
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
bake_command_data.jobs = bake_command_pass.write(bake_command_pass.read(want_data.jobs));
|
|
1175
|
+
bake_command_data.command = bake_command_pass.create("bake command", INDIRECT_COMPUTE_COMMAND_DESCRIPTOR);
|
|
1176
|
+
|
|
1177
|
+
const bake_data = {};
|
|
1178
|
+
|
|
1179
|
+
const bake_pass = graph.add("bake tiles", bake_data, (data, resources, context) => {
|
|
1180
|
+
const geometries = scene.geometries;
|
|
1181
|
+
|
|
1182
|
+
shader_grass_surface_bake.dispatchIndirect({
|
|
1183
|
+
encoder: context.encoder,
|
|
1184
|
+
command: resources.get(data.command),
|
|
1185
|
+
bindings: {
|
|
1186
|
+
settings: { finest_spacing: settings.finest_spacing },
|
|
1187
|
+
scene_database: scene.scene_database_buffer,
|
|
1188
|
+
tlas: scene.tlas.buffer,
|
|
1189
|
+
blas_lookup: geometries.blas.buffer_metadata,
|
|
1190
|
+
blas_data: geometries.blas.buffer_data,
|
|
1191
|
+
geometries: geometries.buffer_metadata,
|
|
1192
|
+
meshlet_metadata: geometries.meshlets.buffer_metadata,
|
|
1193
|
+
meshlet_data: geometries.meshlets.buffer_data,
|
|
1194
|
+
grass_jobs: resources.get(data.jobs),
|
|
1195
|
+
grass_pool: this.#pool,
|
|
1196
|
+
grass_surfels: this.#atlas.texture_at(0).obtainView(),
|
|
1197
|
+
},
|
|
1198
|
+
});
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
bake_data.command = bake_pass.read(bake_command_data.command);
|
|
1202
|
+
bake_data.jobs = bake_pass.read(bake_command_data.jobs);
|
|
1203
|
+
|
|
1204
|
+
// the bake writes the pool and the atlas, which the graph does not hold
|
|
1205
|
+
bake_pass.make_side_effect();
|
|
1206
|
+
|
|
1207
|
+
if (first_view) {
|
|
1208
|
+
this.#graph_end_frame(graph);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
graph.pop_scope();
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Let go of everything on the device. The surfaces stay listed: the next {@link update} makes a fresh
|
|
1216
|
+
* pool for them.
|
|
1217
|
+
*/
|
|
1218
|
+
destroy() {
|
|
1219
|
+
this.#surfaces_buffer?.destroy();
|
|
1220
|
+
this.#invalidation_buffer?.destroy();
|
|
1221
|
+
|
|
1222
|
+
this.#surfaces_buffer = null;
|
|
1223
|
+
this.#invalidation_buffer = null;
|
|
1224
|
+
|
|
1225
|
+
this.#drop_pool();
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Let go of the pool and everything it holds.
|
|
1230
|
+
*/
|
|
1231
|
+
#drop_pool() {
|
|
1232
|
+
this.#pool?.destroy();
|
|
1233
|
+
this.#free?.destroy();
|
|
1234
|
+
this.#directory?.destroy();
|
|
1235
|
+
this.#atlas?.destroy();
|
|
1236
|
+
|
|
1237
|
+
this.#pool = null;
|
|
1238
|
+
this.#free = null;
|
|
1239
|
+
this.#directory = null;
|
|
1240
|
+
this.#atlas = null;
|
|
1241
|
+
this.#invalidations.length = 0;
|
|
1242
|
+
this.#slot_capacity = 0;
|
|
1243
|
+
this.#slot_capacity_wanted = 0;
|
|
1244
|
+
this.#fill_pending = false;
|
|
1245
|
+
this.#pushes.length = 0;
|
|
1246
|
+
this.#starved = 0;
|
|
1247
|
+
this.#removed = 0;
|
|
1248
|
+
this.#removed_at_fill = 0;
|
|
1249
|
+
this.#grow_pending = false;
|
|
1250
|
+
this.#directory_stale = false;
|
|
1251
|
+
this.#generation++;
|
|
1252
|
+
}
|
|
1253
|
+
}
|