@woosh/meep-engine 3.28.0 → 3.30.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/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.map +1 -1
- package/src/core/binary/BinaryBuffer.js +14 -2
- package/src/core/binary/BitSet.d.ts +16 -0
- package/src/core/binary/BitSet.d.ts.map +1 -1
- package/src/core/binary/BitSet.js +159 -49
- package/src/core/binary/allocator/OffsetAllocator.d.ts +48 -0
- package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
- package/src/core/binary/allocator/OffsetAllocator.js +208 -9
- package/src/core/binary/compression/compress_bytes.d.ts +20 -0
- package/src/core/binary/compression/compress_bytes.d.ts.map +1 -0
- package/src/core/binary/compression/compress_bytes.js +35 -0
- package/src/core/binary/compression/decompress_bytes_into.d.ts +30 -0
- package/src/core/binary/compression/decompress_bytes_into.d.ts.map +1 -0
- package/src/core/binary/compression/decompress_bytes_into.js +87 -0
- package/src/core/binary/compression/deflate_decompress_bound.d.ts +27 -0
- package/src/core/binary/compression/deflate_decompress_bound.d.ts.map +1 -0
- package/src/core/binary/compression/deflate_decompress_bound.js +32 -0
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +14 -1
- package/src/core/cache/FrequencySketch.d.ts +3 -1
- package/src/core/cache/FrequencySketch.d.ts.map +1 -1
- package/src/core/cache/FrequencySketch.js +10 -4
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
- package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts +24 -0
- package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_add.js +31 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts +46 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.js +104 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts +62 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.js +158 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts +21 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.js +24 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts +34 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.js +55 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts +28 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.js +35 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts +15 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +35 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts +93 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.js +128 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts +38 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +113 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +93 -17
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +930 -69
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.js +46 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.js +49 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
- package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts +63 -0
- package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts.map +1 -0
- package/src/core/graph/layout/layered/BoxLayoutOptions.js +80 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.d.ts +35 -0
- package/src/core/graph/layout/layered/LayoutBoxGraph.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.js +53 -1
- package/src/core/graph/layout/layered/layout_assign_coordinates.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.js +72 -68
- package/src/core/graph/layout/layered/layout_assign_layers.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.js +4 -2
- package/src/core/graph/layout/layered/layout_break_cycles.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_insert_lanes.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_order_layers.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_orient_links.d.ts.map +1 -0
- package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
- package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
- package/src/core/math/lookup/ParameterLookupTable.js +10 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +16 -10
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +7 -51
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +8 -69
- package/src/core/model/node-graph/visual/layout/layout_build_problem.js +1 -1
- package/src/core/model/node-graph/visual/layout/layout_node_graph.js +6 -6
- package/src/core/model/node-graph/visual/layout/route_grid_build.js +1 -1
- package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
- package/src/core/model/object/ImmutableObjectPool.js +17 -7
- package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +95 -1
- package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
- package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
- package/src/core/process/task/task_find_dependency_cycle.js +81 -0
- package/src/core/wfc/WFCCellChoice.d.ts +17 -0
- package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
- package/src/core/wfc/WFCCellChoice.js +36 -0
- package/src/core/wfc/WFCLattice.d.ts +105 -0
- package/src/core/wfc/WFCLattice.d.ts.map +1 -0
- package/src/core/wfc/WFCLattice.js +235 -0
- package/src/core/wfc/WFCRuleTable.d.ts +172 -0
- package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
- package/src/core/wfc/WFCRuleTable.js +334 -0
- package/src/core/wfc/WFCState.d.ts +273 -0
- package/src/core/wfc/WFCState.d.ts.map +1 -0
- package/src/core/wfc/WFCState.js +552 -0
- package/src/core/wfc/WFCStatus.d.ts +11 -0
- package/src/core/wfc/WFCStatus.d.ts.map +1 -0
- package/src/core/wfc/WFCStatus.js +28 -0
- package/src/core/wfc/WFCTileWeights.d.ts +48 -0
- package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
- package/src/core/wfc/WFCTileWeights.js +86 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
- package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
- package/src/core/wfc/wfc_estimate_bytes.js +47 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
- package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
- package/src/core/wfc/wfc_solve.d.ts +21 -0
- package/src/core/wfc/wfc_solve.d.ts.map +1 -0
- package/src/core/wfc/wfc_solve.js +154 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
- package/src/core/wfc/wfc_state_assign.d.ts +11 -0
- package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_assign.js +36 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_ban_bits.js +81 -0
- package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
- package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_collapse.js +73 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
- package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
- package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_propagate.js +155 -0
- package/src/core/wfc/wfc_state_read.d.ts +11 -0
- package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_read.js +38 -0
- package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
- package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_restrict.js +39 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_set_eligible.js +49 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_undo_to.js +84 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
- package/src/core/wfc/wfc_words_per_cell.js +16 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +37 -14
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
- package/src/engine/ecs/guid/UUID.js +45 -17
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
- package/src/engine/graphics3/DebugDrawSystem.d.ts +7 -5
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem.js +206 -203
- package/src/engine/graphics3/DecalSystem.d.ts +4 -5
- package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DecalSystem.js +5 -5
- package/src/engine/graphics3/FogOfWarSystem.d.ts +7 -12
- package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -1
- package/src/engine/graphics3/FogOfWarSystem.js +8 -12
- package/src/engine/graphics3/prefab/prefab_instantiate.js +230 -230
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
- package/src/engine/interpolation/Interpoland.d.ts +7 -1
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/Interpoland.js +8 -1
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +3 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/pose_interpoland.js +2 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
- package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
- package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
- package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
- package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
- package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
- package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
- package/src/format/image/avif/av1/util/round2.d.ts +19 -2
- package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
- package/src/format/image/avif/av1/util/round2.js +51 -27
- package/src/format/image/avif/heif/parse_avif_file.js +24 -0
- package/src/shade/RENDERER_CONTRACT.md +12 -7
- package/src/shade/playground/caldera/README.md +529 -0
- package/src/shade/playground/caldera/caldera_format.d.ts +151 -0
- package/src/shade/playground/caldera/caldera_format.d.ts.map +1 -0
- package/src/shade/playground/caldera/caldera_format.js +241 -0
- package/src/shade/playground/caldera/index.html +126 -0
- package/src/shade/playground/caldera/main.d.ts.map +1 -0
- package/src/shade/playground/caldera/main.js +1565 -0
- package/src/shade/playground/mesh_row_ceiling/README.md +85 -0
- package/src/shade/playground/mesh_row_ceiling/index.html +31 -0
- package/src/shade/playground/mesh_row_ceiling/main.d.ts +2 -0
- package/src/shade/playground/mesh_row_ceiling/main.d.ts.map +1 -0
- package/src/shade/playground/mesh_row_ceiling/main.js +389 -0
- package/src/shade/playground/vgeo_runtime/README.md +5 -3
- package/src/shade/playground/vgeo_runtime/main.js +3 -2
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +3 -3
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -1
- package/src/shade/playground/vgeo_runtime/sample_asset.js +3 -3
- package/src/shade/playground/vgeo_scene/README.md +103 -24
- package/src/shade/playground/vgeo_scene/main.js +525 -24
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +6 -0
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +23 -0
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +100 -8
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +69 -33
- package/src/shade/renderer/extension/RenderExtension.d.ts +15 -18
- package/src/shade/renderer/extension/RenderExtension.d.ts.map +1 -1
- package/src/shade/renderer/extension/RenderExtension.js +15 -19
- package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts +5 -5
- package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts.map +1 -1
- package/src/shade/renderer/extension/RenderExtensionRegistry.js +29 -166
- package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GPUGeometryManager.js +73 -16
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +18 -3
- package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +663 -0
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +100 -5
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +688 -81
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +540 -0
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +155 -64
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +76 -16
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +3 -3
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +448 -0
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +44 -7
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +48 -9
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts +40 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.js +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts +91 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.js +91 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.js +25 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts +77 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.js +84 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +19 -6
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.js +20 -7
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.js +58 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.js +116 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +545 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.js +110 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +2 -27
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.js +120 -1564
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts +26 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.js +123 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts +26 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.js +78 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.js +337 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.js +147 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts +37 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.js +243 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.js +117 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts +46 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +74 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +78 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +12 -7
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.js +33 -11
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts +58 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.js +79 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts +39 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.js +68 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts +53 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.js +243 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +32 -7
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +243 -24
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +12 -8
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.js +58 -50
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +5 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +13 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts +38 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.js +81 -0
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +23 -5
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.js +28 -21
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.js +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.js +224 -60
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.js +17 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.js +19 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +10 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.js +103 -9
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +14 -2
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.js +38 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts +6 -2
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +27 -4
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts +32 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.js +93 -0
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +20 -6
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +22 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/{vgeo_layers_copy_vertex.js → vgeo_layers_copy_corner.js} +8 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts +18 -7
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +19 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.d.ts +3 -3
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +3 -3
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts +27 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.js +97 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts +7 -3
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +21 -11
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts +21 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.js +41 -0
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +19 -1
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +20 -1
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts +25 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.js +24 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts +10 -3
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +37 -3
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +21 -13
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +37 -13
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts +8 -2
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +14 -2
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +19 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +6 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts +21 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.js +20 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +10 -1
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +1 -2
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +10 -3
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.js +43 -0
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +4 -2
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +8 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +2 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts +6 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.js +6 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +13 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +13 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +5 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +14 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts +4 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.js +53 -11
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +7 -3
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts +12 -2
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +111 -33
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts +6 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +112 -42
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts +2 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +51 -21
- package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +385 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +34 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +18 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +47 -19
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +22 -4
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +7 -2
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +8 -3
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +10 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +13 -11
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +13 -11
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +4 -0
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +3 -4
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +6 -2
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +90 -12
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts +22 -0
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js +21 -0
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
- package/src/shade/renderer/particles/DESIGN.md +14 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +8 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +13 -8
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -6
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +4 -12
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +4 -5
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +5 -3
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.js +11 -4
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +4 -3
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +3 -0
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +94 -90
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +13 -5
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +96 -89
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +3 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.js +4 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +100 -89
- package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.js +31 -7
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +1 -7
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +161 -153
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +2 -7
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +4 -8
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +5 -8
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +25 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +24 -0
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +10 -0
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +30 -21
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
- package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
- package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipManager.js +22 -0
- package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipObserver.js +13 -3
- package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts +0 -61
- package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/main.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/sample_asset.d.ts +0 -18
- package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/select_cut.d.ts +0 -173
- package/src/shade/playground/vgeo_viewer/select_cut.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts +0 -52
- package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts +0 -24
- package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts +0 -32
- package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts +0 -59
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts +0 -44
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts +0 -20
- package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts +0 -73
- package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts +0 -36
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts +0 -19
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts +0 -29
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts +0 -31
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts +0 -18
- package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts +0 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts +0 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts +0 -11
- package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts +0 -46
- package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +0 -22
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts +0 -29
- package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts +0 -9
- package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts +0 -10
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts +0 -15
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts.map +0 -1
- package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts +0 -10
- package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts +0 -17
- package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts +0 -3
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts +0 -8
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts.map +0 -1
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.js +0 -0
- /package/src/shade/playground/{vgeo_viewer → caldera}/main.d.ts +0 -0
|
@@ -0,0 +1,1565 @@
|
|
|
1
|
+
import { Color } from "../../../core/color/Color.js";
|
|
2
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
3
|
+
import { GIGABYTE } from "../../../core/science/units/memory/GIGABYTE.js";
|
|
4
|
+
import { EntityComponentDataset } from "../../../engine/ecs/EntityComponentDataset.js";
|
|
5
|
+
import { Transform64 } from "../../../engine/ecs/transform/Transform64.js";
|
|
6
|
+
import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
|
|
7
|
+
import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
|
|
8
|
+
import { FramePhase } from "../../renderer/extension/FramePhase.js";
|
|
9
|
+
import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
|
|
10
|
+
import { meshlet_geometry_from_bytes } from "../../renderer/geometry/meshlet_geometry_transfer.js";
|
|
11
|
+
import {
|
|
12
|
+
vgeo_byte_source_over_url
|
|
13
|
+
} from "../../renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js";
|
|
14
|
+
import {
|
|
15
|
+
VGeoContainerReader
|
|
16
|
+
} from "../../renderer/geometry/virtual/format/read/VGeoContainerReader.js";
|
|
17
|
+
import {
|
|
18
|
+
VGeoReadOptions
|
|
19
|
+
} from "../../renderer/geometry/virtual/format/read/VGeoReadOptions.js";
|
|
20
|
+
import { VGEO_CUT_STAT } from "../../renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js";
|
|
21
|
+
import {
|
|
22
|
+
VGeoResidencyOptions
|
|
23
|
+
} from "../../renderer/geometry/virtual/runtime/VGeoResidencyOptions.js";
|
|
24
|
+
import { VirtualGeometry } from "../../renderer/geometry/virtual/runtime/VirtualGeometry.js";
|
|
25
|
+
import { make_sunlight } from "../../renderer/light/make_sunlight.js";
|
|
26
|
+
import { place_light } from "../../renderer/light/place_light.js";
|
|
27
|
+
import { ShadeDrawSide } from "../../renderer/material/ShadeDrawSide.js";
|
|
28
|
+
import { StandardShadeMaterial } from "../../renderer/material/StandardShadeMaterial.js";
|
|
29
|
+
import {
|
|
30
|
+
RasterizationCollectionType
|
|
31
|
+
} from "../../renderer/rasterize/expand/RasterizationCollectionType.js";
|
|
32
|
+
import { Renderer } from "../../renderer/Renderer.js";
|
|
33
|
+
import { place_primitive } from "../../renderer/scene/rows/place_primitive.js";
|
|
34
|
+
import { Scene } from "../../renderer/scene/Scene.js";
|
|
35
|
+
import { load_default_environment } from "../load_default_environment.js";
|
|
36
|
+
import { install_profile_hotkey } from "../profile_hotkey.js";
|
|
37
|
+
import {
|
|
38
|
+
CALDERA_TABLE_ROW,
|
|
39
|
+
caldera_coalesce_ranges,
|
|
40
|
+
caldera_read_geometry_table,
|
|
41
|
+
caldera_read_placements,
|
|
42
|
+
} from "./caldera_format.js";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Caldera, drawn by meep: 45 districts, millions of placements, and every triangle of it standing
|
|
46
|
+
* where Activision's map put it.
|
|
47
|
+
*
|
|
48
|
+
* [`../vgeo_scene/`](../vgeo_scene/README.md) already puts a converted scene of this order on the
|
|
49
|
+
* screen, and this is deliberately **not** that page. `vgeo_scene` asks one question — what a
|
|
50
|
+
* virtual-geometry cut costs across a whole scene — and answers it with every asset on the same
|
|
51
|
+
* backing, because the point is the cut. This page asks the opposite: what the **ordinary** engine
|
|
52
|
+
* does when a real production map is handed to it whole. So the default backing here is the plain
|
|
53
|
+
* meshlet path every game asset takes, the rows are ordinary mesh rows, and virtual geometry is a
|
|
54
|
+
* *promotion* that a few hundred of the largest geometries get — which is how a game would actually
|
|
55
|
+
* use it, and which makes the two paths comparable inside one frame instead of across two pages.
|
|
56
|
+
*
|
|
57
|
+
* ## What it is looking at
|
|
58
|
+
*
|
|
59
|
+
* Caldera is the Call of Duty: Warzone map, released by Activision as OpenUSD. Its own README calls
|
|
60
|
+
* it 17.5 million primitives and two billion points across two miles square, and it is the largest
|
|
61
|
+
* honest test of a renderer's *scene* — not of one hero asset — that is freely available.
|
|
62
|
+
* `tools/caldera/` converts it; the README beside this file has the numbers that came out.
|
|
63
|
+
*
|
|
64
|
+
* ## What it is measuring
|
|
65
|
+
*
|
|
66
|
+
* Load time first, because that is what the conversion is for. The set holds **serialized
|
|
67
|
+
* `MeshletGeometry`** — {@link MeshletGeometrySerializationAdapter}, the format whose own docblock
|
|
68
|
+
* says building meshlets at load costs 1,246 ms per 199k triangles — so opening a district is a
|
|
69
|
+
* range request, a deserialize and an upload, with no geometry processing anywhere on the path. The
|
|
70
|
+
* panel's `open` row is that claim, per district, in milliseconds.
|
|
71
|
+
*
|
|
72
|
+
* Then everything the scale breaks. Four million mesh rows is two orders of magnitude past what any
|
|
73
|
+
* meep page has asked for, and the things that give way first — a rasterization collection's limit,
|
|
74
|
+
* the geometry manager's arenas, VRAM against half a million BLAS — are exactly what a page like
|
|
75
|
+
* this exists to find. Every one of them is a counter in the panel rather than a stall you have to
|
|
76
|
+
* guess at.
|
|
77
|
+
*
|
|
78
|
+
* ## Running it
|
|
79
|
+
*
|
|
80
|
+
* The dev server in front of the page, and an asset server in front of the converted set — Vite
|
|
81
|
+
* refuses to serve a path outside the workspace, and a converted Caldera has no business inside
|
|
82
|
+
* one:
|
|
83
|
+
*
|
|
84
|
+
* ```
|
|
85
|
+
* npm run dev --workspace @woosh/meep-engine
|
|
86
|
+
* node tools/geometry/virtual/vgeo_serve.js H:/git/caldera/meep
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* Then `/src/shade/playground/caldera/index.html`. `?districts=` takes what to open,
|
|
90
|
+
* `?placements=` caps how much of it, and the rest are in the README.
|
|
91
|
+
*
|
|
92
|
+
* @author Alex Goldring
|
|
93
|
+
* @copyright Company Named Limited (c) 2026
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
const element = id => document.getElementById(id);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Where the asset server is, when the page is not told otherwise. The same default port
|
|
100
|
+
* `vgeo_serve.js` binds, because it is the same server.
|
|
101
|
+
*
|
|
102
|
+
* @type {string}
|
|
103
|
+
*/
|
|
104
|
+
const DEFAULT_ORIGIN = 'http://localhost:5200';
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The manifest `caldera_build.js` writes, at the asset origin's root.
|
|
108
|
+
*
|
|
109
|
+
* @type {string}
|
|
110
|
+
*/
|
|
111
|
+
const DEFAULT_MANIFEST = 'caldera.meep.json';
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* How long a placement pass may hold the thread before it yields.
|
|
115
|
+
*
|
|
116
|
+
* Rows are created one at a time and there are millions of them, so the question is not whether to
|
|
117
|
+
* break the work up but where. A frame's worth is the wrong budget — it makes the page *smooth*
|
|
118
|
+
* while it loads and makes loading take four times as long. Four frames' worth is the compromise:
|
|
119
|
+
* the page still answers a drag, and the scene arrives at something like the rate the machine can
|
|
120
|
+
* actually build it.
|
|
121
|
+
*
|
|
122
|
+
* @type {number}
|
|
123
|
+
*/
|
|
124
|
+
const PLACEMENT_BUDGET_MS = 64;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* How long the deserialize loop may hold the thread before it yields.
|
|
128
|
+
*
|
|
129
|
+
* The same reasoning as {@link PLACEMENT_BUDGET_MS} and a different loop, and it was worth splitting
|
|
130
|
+
* because the range was the wrong unit: yielding once per fetched range is fine over a district of
|
|
131
|
+
* four-triangle props and starves the frame completely over the terrain, where one range is one
|
|
132
|
+
* two-million-triangle geometry and four of them are being read at once. The frame counter sitting
|
|
133
|
+
* still for minutes on the `st_*` districts is what this is for.
|
|
134
|
+
*
|
|
135
|
+
* @type {number}
|
|
136
|
+
*/
|
|
137
|
+
const DESERIALIZE_BUDGET_MS = 32;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* `.vgeo` containers opened at once. The same figure `vgeo_scene` settled on: enough that the
|
|
141
|
+
* round trips overlap, few enough that a browser does not queue them behind each other anyway.
|
|
142
|
+
*
|
|
143
|
+
* @type {number}
|
|
144
|
+
*/
|
|
145
|
+
const OPEN_CONCURRENCY = 24;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The row count past which this set has not been held to be safe.
|
|
149
|
+
*
|
|
150
|
+
* It used to be 2²⁰, where the picture stopped being correct — and that was never a row count at
|
|
151
|
+
* all: the dispatch that broke was over *meshlets*, and 2²⁰ rows is where this scene's meshlet
|
|
152
|
+
* count crossed it. Fixed in `962e79183` / `53deaa9b5`, so a scene of any row count draws what it
|
|
153
|
+
* is given now.
|
|
154
|
+
*
|
|
155
|
+
* What is left is memory, which is the wall the README calls the second one: this set ran out of
|
|
156
|
+
* VRAM on a 24 GB card somewhere between 2.2 and 3.7 million rows, once saying so out of `render`
|
|
157
|
+
* and once by thrashing until the main thread stopped answering. 2.2 million is where it said so,
|
|
158
|
+
* so it is the last figure this page can stand behind.
|
|
159
|
+
*
|
|
160
|
+
* The page does not stop here, because stopping would hide it. It says so in the panel instead.
|
|
161
|
+
*
|
|
162
|
+
* @type {number}
|
|
163
|
+
*/
|
|
164
|
+
const ROWS_LAST_KNOWN_GOOD = 2_200_000;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* What a container's always-resident set costs at the median across a converted set — header, level
|
|
168
|
+
* table, root frame. Asking for this much on the first read opens the container in **one** round
|
|
169
|
+
* trip instead of the reader's default 64 KiB of over-fetch.
|
|
170
|
+
*
|
|
171
|
+
* @type {number}
|
|
172
|
+
*/
|
|
173
|
+
const HEAD_PREFIX_BYTES = 8 * 1024;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @type {Record<string, ShadeDrawSide>}
|
|
177
|
+
*/
|
|
178
|
+
const DRAW_SIDES = Object.freeze({
|
|
179
|
+
front: ShadeDrawSide.Front,
|
|
180
|
+
back: ShadeDrawSide.Back,
|
|
181
|
+
double: ShadeDrawSide.Double,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Opens the VG frame before anything records a rasterization pass, because the seam inside those
|
|
187
|
+
* passes writes into the feedback buffer this creates.
|
|
188
|
+
*/
|
|
189
|
+
class VGeoFrameStart extends RenderExtension {
|
|
190
|
+
name = "caldera/vgeo frame start";
|
|
191
|
+
|
|
192
|
+
phase = FramePhase.FrameStart;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {CalderaPlayground} page
|
|
196
|
+
*/
|
|
197
|
+
constructor(page) {
|
|
198
|
+
super();
|
|
199
|
+
|
|
200
|
+
this.page = page;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
record(frame) {
|
|
204
|
+
this.page.runtime.begin_frame({ graph: frame.graph });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Schedules the readback and closes the frame, after everything that could have cut has cut.
|
|
211
|
+
*/
|
|
212
|
+
class VGeoFrameEnd extends RenderExtension {
|
|
213
|
+
name = "caldera/vgeo frame end";
|
|
214
|
+
|
|
215
|
+
phase = FramePhase.BeforePresent;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {CalderaPlayground} page
|
|
219
|
+
*/
|
|
220
|
+
constructor(page) {
|
|
221
|
+
super();
|
|
222
|
+
|
|
223
|
+
this.page = page;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
record(frame) {
|
|
227
|
+
this.page.feedback.read(frame.graph);
|
|
228
|
+
this.page.runtime.end_frame();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class CalderaPlayground {
|
|
234
|
+
|
|
235
|
+
/** @type {Renderer} */
|
|
236
|
+
renderer;
|
|
237
|
+
|
|
238
|
+
/** @type {HTMLCanvasElement} */
|
|
239
|
+
canvas;
|
|
240
|
+
|
|
241
|
+
/** @type {Scene} */
|
|
242
|
+
scene;
|
|
243
|
+
|
|
244
|
+
/** @type {GPUSceneContext} */
|
|
245
|
+
scene_context;
|
|
246
|
+
|
|
247
|
+
/** @type {PerspectiveCamera} */
|
|
248
|
+
camera;
|
|
249
|
+
|
|
250
|
+
/** @type {OrbitalCameraController} */
|
|
251
|
+
controller;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The one material the whole map is drawn with.
|
|
255
|
+
*
|
|
256
|
+
* Caldera ships without materials or textures — its own README says so, and says why: the set
|
|
257
|
+
* is about the relationships between spatial elements rather than about how they look. So there
|
|
258
|
+
* is nothing to shade this with but geometry, and one material is the honest representation of
|
|
259
|
+
* that rather than a simplification this page is making.
|
|
260
|
+
*
|
|
261
|
+
* @type {StandardShadeMaterial}
|
|
262
|
+
*/
|
|
263
|
+
material;
|
|
264
|
+
|
|
265
|
+
/** @type {object|undefined} */
|
|
266
|
+
manifest;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The geometry table, as column arrays.
|
|
270
|
+
* @type {ReturnType<typeof caldera_read_geometry_table>|undefined}
|
|
271
|
+
*/
|
|
272
|
+
table;
|
|
273
|
+
|
|
274
|
+
/** @type {string} where the manifest came from; every other URL resolves against it */
|
|
275
|
+
base = '';
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Meshlet geometries that have been deserialized, by geometry id.
|
|
279
|
+
*
|
|
280
|
+
* Held because a geometry is shared between districts — that is the whole reason the set has a
|
|
281
|
+
* geometry table rather than a scene per district — and because dropping one would drop the
|
|
282
|
+
* rows that draw it.
|
|
283
|
+
*
|
|
284
|
+
* @type {Map<number, MeshletGeometry>}
|
|
285
|
+
*/
|
|
286
|
+
geometries = new Map();
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Geometries placed on the virtual backing instead, by geometry id, with the reader kept beside
|
|
290
|
+
* each so nothing collects it out from under the residency.
|
|
291
|
+
*
|
|
292
|
+
* @type {Map<number, {geometry: VirtualGeometry, reader: VGeoContainerReader}>}
|
|
293
|
+
*/
|
|
294
|
+
virtual = new Map();
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The merged assets — one container each, holding what the source had cut into pieces.
|
|
298
|
+
*
|
|
299
|
+
* Caldera's ground arrives this way: 728 geometries placed once each on an 832 m grid, baked by
|
|
300
|
+
* `caldera_build.js --merge` into a single `.vgeo`. They are held for the same reason the other
|
|
301
|
+
* readers are, and they are placed at the identity because the baking already put every vertex
|
|
302
|
+
* in world space.
|
|
303
|
+
*
|
|
304
|
+
* @type {Array<{record: object, geometry: VirtualGeometry, reader: VGeoContainerReader}>}
|
|
305
|
+
*/
|
|
306
|
+
merged = [];
|
|
307
|
+
|
|
308
|
+
/** @type {number} placements turned into rows */
|
|
309
|
+
placed = 0;
|
|
310
|
+
|
|
311
|
+
/** @type {number} how many of those were drawn from the virtual backing */
|
|
312
|
+
placed_virtual = 0;
|
|
313
|
+
|
|
314
|
+
/** @type {number} instanced triangles standing in the scene */
|
|
315
|
+
instanced_triangles = 0;
|
|
316
|
+
|
|
317
|
+
/** @type {number} distinct source triangles behind them */
|
|
318
|
+
source_triangles = 0;
|
|
319
|
+
|
|
320
|
+
/** @type {number} bytes of pack, table, placements and container heads fetched */
|
|
321
|
+
bytes_fetched = 0;
|
|
322
|
+
|
|
323
|
+
/** @type {number} geometries the set declared but this page could not open */
|
|
324
|
+
refused = 0;
|
|
325
|
+
|
|
326
|
+
/** @type {number} milliseconds from the first district's first byte to its last row */
|
|
327
|
+
open_ms = 0;
|
|
328
|
+
|
|
329
|
+
/** @type {number} */
|
|
330
|
+
frames = 0;
|
|
331
|
+
|
|
332
|
+
/** @type {number} */
|
|
333
|
+
frame_ms = 0;
|
|
334
|
+
|
|
335
|
+
/** @type {number} what the last frame cost on the CPU, which at this scale is the interesting half */
|
|
336
|
+
cpu_ms = 0;
|
|
337
|
+
|
|
338
|
+
/** @type {string} */
|
|
339
|
+
status_text = '';
|
|
340
|
+
|
|
341
|
+
/** @type {number} */
|
|
342
|
+
screen_width = 1;
|
|
343
|
+
|
|
344
|
+
/** @type {number} */
|
|
345
|
+
screen_height = 1;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Districts as the manifest declares them, with what this page has done about each.
|
|
349
|
+
* @type {Array<object & {state: string}>}
|
|
350
|
+
*/
|
|
351
|
+
districts = [];
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Whether the promoted geometries are drawn from their `.vgeo` rather than their meshlet blob.
|
|
355
|
+
* @type {boolean}
|
|
356
|
+
*/
|
|
357
|
+
virtual_enabled = false;
|
|
358
|
+
|
|
359
|
+
/** @type {GPUVirtualGeometryRuntime|undefined} */
|
|
360
|
+
runtime;
|
|
361
|
+
|
|
362
|
+
/** @type {VGeoResidency|undefined} */
|
|
363
|
+
residency;
|
|
364
|
+
|
|
365
|
+
/** @type {GPUVirtualGeometryFeedback|undefined} */
|
|
366
|
+
feedback;
|
|
367
|
+
|
|
368
|
+
/** @type {number} half the diagonal of the whole map, which sets the far plane */
|
|
369
|
+
world_radius = 1000;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* What the virtual-geometry tables were sized to, kept so a session can read back the numbers
|
|
373
|
+
* the page chose rather than infer them.
|
|
374
|
+
*
|
|
375
|
+
* @type {object|undefined}
|
|
376
|
+
*/
|
|
377
|
+
capacities;
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* What killed the frame loop, if anything did.
|
|
381
|
+
*
|
|
382
|
+
* A scene this size runs out of somewhere — VRAM first — and the engine says so, out of
|
|
383
|
+
* `render`: *Failed to allocate GPU memory for meshlet batch*. The loop catches that and stops,
|
|
384
|
+
* and without this flag the loader carries on behind it for another twenty minutes, fetching
|
|
385
|
+
* geometry for a renderer that is never going to draw again and burying the one message that
|
|
386
|
+
* explained what happened. Whatever is on the screen when this is set is what the machine could
|
|
387
|
+
* hold, which is the reading the page exists to take.
|
|
388
|
+
*
|
|
389
|
+
* @type {Error|undefined}
|
|
390
|
+
*/
|
|
391
|
+
fatal;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Put the camera at `eye` looking at `target`, and rescale everything that is a function of how
|
|
395
|
+
* far away it is.
|
|
396
|
+
*
|
|
397
|
+
* The near and far planes are the part that matters here: Caldera is three kilometres across and
|
|
398
|
+
* a viewer stands centimetres from a wall inside it, so a fixed pair of planes either z-fights
|
|
399
|
+
* everything close or clips everything far. Deriving them from the distance is what lets one
|
|
400
|
+
* page do both.
|
|
401
|
+
*
|
|
402
|
+
* @param {ArrayLike<number>} eye
|
|
403
|
+
* @param {ArrayLike<number>} target
|
|
404
|
+
*/
|
|
405
|
+
look(eye, target) {
|
|
406
|
+
const from = new Vector3(eye[0], eye[1], eye[2]);
|
|
407
|
+
const to = new Vector3(target[0], target[1], target[2]);
|
|
408
|
+
|
|
409
|
+
const distance = Math.max(Vector3.distance(from, to), 1e-3);
|
|
410
|
+
|
|
411
|
+
this.camera.near = distance * 0.002;
|
|
412
|
+
this.camera.far = Math.max(distance * 2000, this.world_radius * 4);
|
|
413
|
+
|
|
414
|
+
this.controller.distanceLimits.min = distance * 0.01;
|
|
415
|
+
this.controller.distanceLimits.max = Math.max(distance * 2000, this.world_radius * 4);
|
|
416
|
+
this.controller.movement_speed_scale = distance;
|
|
417
|
+
|
|
418
|
+
this.controller.look(from, to);
|
|
419
|
+
|
|
420
|
+
// the pixels the camera is about to cover have no correspondence in a TAA history written
|
|
421
|
+
// from where it was standing
|
|
422
|
+
this.renderer.indicate_view_change();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* @param {string} text
|
|
427
|
+
* @param {boolean} [error]
|
|
428
|
+
*/
|
|
429
|
+
status(text, error = false) {
|
|
430
|
+
this.status_text = text;
|
|
431
|
+
|
|
432
|
+
const node = element('status');
|
|
433
|
+
|
|
434
|
+
node.textContent = text;
|
|
435
|
+
node.classList.toggle('error', error);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Size the canvas to the document.
|
|
440
|
+
*
|
|
441
|
+
* The **document**, not the canvas: `Renderer` writes an inline `style.width` and `style.height`
|
|
442
|
+
* every time it configures the context, so measuring the canvas measures what the renderer last
|
|
443
|
+
* decided rather than what the window now is, and the two ratchet against each other.
|
|
444
|
+
*/
|
|
445
|
+
fit() {
|
|
446
|
+
const renderer = this.renderer;
|
|
447
|
+
|
|
448
|
+
const viewport = document.documentElement;
|
|
449
|
+
|
|
450
|
+
renderer.pixel_ratio = globalThis.devicePixelRatio || 1;
|
|
451
|
+
|
|
452
|
+
renderer.resize(
|
|
453
|
+
Math.max(1, Math.floor(viewport.clientWidth)),
|
|
454
|
+
Math.max(1, Math.floor(viewport.clientHeight))
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
this.camera.aspect = renderer.aspect_ratio;
|
|
458
|
+
|
|
459
|
+
const resolution = renderer.output_resolution;
|
|
460
|
+
const scale = renderer.internal_resolution_scale;
|
|
461
|
+
|
|
462
|
+
this.screen_width = Math.max(1, Math.floor(resolution.x * scale));
|
|
463
|
+
this.screen_height = Math.max(1, Math.floor(resolution.y * scale));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* @param {string} path relative to the manifest
|
|
468
|
+
* @param {{start: number, end: number}} [range] inclusive-exclusive, as the table reports one
|
|
469
|
+
* @returns {Promise<ArrayBuffer>}
|
|
470
|
+
*/
|
|
471
|
+
async fetch_bytes(path, range) {
|
|
472
|
+
const url = new URL(path, this.base).href;
|
|
473
|
+
|
|
474
|
+
const options = range === undefined
|
|
475
|
+
? undefined
|
|
476
|
+
: { headers: { Range: `bytes=${range.start}-${range.end - 1}` } };
|
|
477
|
+
|
|
478
|
+
const response = await fetch(url, options);
|
|
479
|
+
|
|
480
|
+
if (!response.ok) {
|
|
481
|
+
throw new Error(`${path} answered HTTP ${response.status}`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/*
|
|
485
|
+
A `200` where a range was asked for is a server that ignored the request and sent the head
|
|
486
|
+
of the file — plausible bytes at the wrong offset, which downstream is a meshlet geometry
|
|
487
|
+
built out of another geometry's middle. The same check `vgeo_fetch_byte_source` makes, for
|
|
488
|
+
the same reason, and it is the one thing a static server in front of this set has to get
|
|
489
|
+
right.
|
|
490
|
+
*/
|
|
491
|
+
if (range !== undefined && response.status !== 206) {
|
|
492
|
+
throw new Error(
|
|
493
|
+
`${path} answered HTTP ${response.status} to a Range request — the asset server`
|
|
494
|
+
+ ` must support ranges; vgeo_serve.js does`
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const bytes = await response.arrayBuffer();
|
|
499
|
+
|
|
500
|
+
this.bytes_fetched += bytes.byteLength;
|
|
501
|
+
|
|
502
|
+
return bytes;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Open every district that was asked for, one after another, placing each as it lands.
|
|
507
|
+
*
|
|
508
|
+
* Sequential rather than concurrent, and that is the shape of the thing rather than a
|
|
509
|
+
* simplification: placing rows is main-thread work that does not overlap with itself, so
|
|
510
|
+
* fetching a second district while the first is still being placed buys nothing but memory. The
|
|
511
|
+
* fetch inside a district is concurrent, which is where the overlap actually is.
|
|
512
|
+
*
|
|
513
|
+
* @param {number} placement_budget stop once this many rows exist
|
|
514
|
+
* @returns {Promise<void>}
|
|
515
|
+
*/
|
|
516
|
+
async open_districts(placement_budget) {
|
|
517
|
+
const started = performance.now();
|
|
518
|
+
|
|
519
|
+
/*
|
|
520
|
+
The merged assets first, and not because they are large — because they are the *ground*. A
|
|
521
|
+
scene that fills in district by district over four minutes reads as a map being built if
|
|
522
|
+
the terrain is under it from the first second, and as debris floating in the sea if it is
|
|
523
|
+
not.
|
|
524
|
+
*/
|
|
525
|
+
await this.open_merged();
|
|
526
|
+
|
|
527
|
+
for (const district of this.districts) {
|
|
528
|
+
if (this.fatal !== undefined || this.placed >= placement_budget) {
|
|
529
|
+
district.state = 'skipped';
|
|
530
|
+
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
district.state = 'loading';
|
|
535
|
+
|
|
536
|
+
this.paint_districts();
|
|
537
|
+
|
|
538
|
+
try {
|
|
539
|
+
await this.open_district(district, placement_budget);
|
|
540
|
+
|
|
541
|
+
district.state = 'done';
|
|
542
|
+
} catch (error) {
|
|
543
|
+
district.state = 'failed';
|
|
544
|
+
|
|
545
|
+
console.warn(`${district.name} — ${error.message ?? error}`);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
this.paint_districts();
|
|
549
|
+
|
|
550
|
+
this.status(
|
|
551
|
+
`${this.placed.toLocaleString()} placements`
|
|
552
|
+
+ `, ${(this.geometries.size + this.virtual.size).toLocaleString()} geometries`
|
|
553
|
+
+ `, ${((performance.now() - started) / 1000).toFixed(1)} s`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
this.open_ms = performance.now() - started;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Open every merged asset the manifest names and place each at the identity.
|
|
562
|
+
*
|
|
563
|
+
* One row apiece, and that is the point of them: the ground is 24 million triangles that would
|
|
564
|
+
* otherwise be 728 containers and 728 rows, each with its own cluster DAG over a surface it
|
|
565
|
+
* shares with its neighbours. As one asset the cut sees one surface, and a coarse level may
|
|
566
|
+
* simplify across a seam that is no longer there.
|
|
567
|
+
*
|
|
568
|
+
* These have no meshlet alternative — the pieces they were baked from have no blobs of their
|
|
569
|
+
* own — so `?vgeo=off` does not apply to them, and a page that could not open one is missing
|
|
570
|
+
* its terrain rather than drawing it the other way.
|
|
571
|
+
*
|
|
572
|
+
* @returns {Promise<void>}
|
|
573
|
+
*/
|
|
574
|
+
async open_merged() {
|
|
575
|
+
const records = this.manifest.merged ?? [];
|
|
576
|
+
|
|
577
|
+
if (records.length === 0 || this.runtime === undefined) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const options = new VGeoReadOptions();
|
|
582
|
+
|
|
583
|
+
options.verify_frames = true;
|
|
584
|
+
options.track_selectability = false;
|
|
585
|
+
options.head_prefix_bytes = HEAD_PREFIX_BYTES;
|
|
586
|
+
|
|
587
|
+
const transform = new Transform64();
|
|
588
|
+
|
|
589
|
+
for (const record of records) {
|
|
590
|
+
try {
|
|
591
|
+
const source = vgeo_byte_source_over_url(
|
|
592
|
+
new URL(record.file, this.base).href, record.bytes
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
const reader = await VGeoContainerReader.open(source, options);
|
|
596
|
+
|
|
597
|
+
const geometry = new VirtualGeometry(reader);
|
|
598
|
+
|
|
599
|
+
geometry.vg_row = this.residency.register(reader);
|
|
600
|
+
|
|
601
|
+
place_primitive(this.scene_context, geometry, this.material, transform);
|
|
602
|
+
|
|
603
|
+
this.merged.push({ record, geometry, reader });
|
|
604
|
+
|
|
605
|
+
this.placed++;
|
|
606
|
+
this.placed_virtual++;
|
|
607
|
+
this.source_triangles += record.triangles;
|
|
608
|
+
this.instanced_triangles += record.triangles;
|
|
609
|
+
} catch (error) {
|
|
610
|
+
this.refused++;
|
|
611
|
+
|
|
612
|
+
console.warn(`${record.file} — ${error.message ?? error}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
this.status(
|
|
617
|
+
`${this.merged.length} merged asset${this.merged.length === 1 ? '' : 's'}`
|
|
618
|
+
+ `, ${this.merged.reduce((t, m) => t + m.record.triangles, 0).toLocaleString()} triangles`
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* @param {object} district
|
|
624
|
+
* @param {number} placement_budget
|
|
625
|
+
* @returns {Promise<void>}
|
|
626
|
+
*/
|
|
627
|
+
async open_district(district, placement_budget) {
|
|
628
|
+
const placements = caldera_read_placements(await this.fetch_bytes(district.file));
|
|
629
|
+
|
|
630
|
+
/*
|
|
631
|
+
What this district needs that is not already here. A geometry shared with a district that
|
|
632
|
+
opened earlier costs nothing the second time, which is most of why the whole map fits at
|
|
633
|
+
all: the set's 3.8 million placements stand on a far smaller number of geometries, and the
|
|
634
|
+
sharing is between districts as much as inside them.
|
|
635
|
+
*/
|
|
636
|
+
const wanted_meshlets = [];
|
|
637
|
+
const wanted_virtual = [];
|
|
638
|
+
|
|
639
|
+
const seen = new Set();
|
|
640
|
+
|
|
641
|
+
for (let i = 0; i < placements.count; i++) {
|
|
642
|
+
const id = placements.geometry[i];
|
|
643
|
+
|
|
644
|
+
if (seen.has(id) || this.geometries.has(id) || this.virtual.has(id)) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
seen.add(id);
|
|
649
|
+
|
|
650
|
+
if (this.use_virtual(id)) {
|
|
651
|
+
wanted_virtual.push(id);
|
|
652
|
+
} else {
|
|
653
|
+
wanted_meshlets.push(id);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
await Promise.all([
|
|
658
|
+
this.open_virtual(wanted_virtual),
|
|
659
|
+
this.open_meshlets(wanted_meshlets),
|
|
660
|
+
]);
|
|
661
|
+
|
|
662
|
+
await this.place(placements, placement_budget);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Whether this geometry is drawn from a `.vgeo` rather than from its meshlet blob.
|
|
667
|
+
*
|
|
668
|
+
* Both were built for it — `caldera_build.js` promotes by triangle count and keeps the meshlet
|
|
669
|
+
* blob anyway — so this is a runtime choice and `?vgeo=off` unmakes it without rebuilding
|
|
670
|
+
* anything. What it costs is not symmetric: the meshlet blob of a 200k-triangle geometry is
|
|
671
|
+
* fetched and uploaded whole, and the `.vgeo` of one is a 2.5 KiB head and whatever the cut
|
|
672
|
+
* asks for afterwards.
|
|
673
|
+
*
|
|
674
|
+
* @param {number} id
|
|
675
|
+
* @returns {boolean}
|
|
676
|
+
*/
|
|
677
|
+
use_virtual(id) {
|
|
678
|
+
return this.virtual_enabled && this.table.vgeo[id] !== CALDERA_TABLE_ROW.NO_VGEO;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Fetch and deserialize a set of meshlet geometries.
|
|
683
|
+
*
|
|
684
|
+
* The fetch is by **coalesced range**, which is the one thing about this path that had to be
|
|
685
|
+
* designed rather than written: a district wants tens of thousands of blobs, a request each is
|
|
686
|
+
* tens of thousands of round trips, and the blobs are laid down in geometry-id order precisely
|
|
687
|
+
* so that a district's own geometry is a handful of long runs. What comes back is sliced apart
|
|
688
|
+
* here.
|
|
689
|
+
*
|
|
690
|
+
* @param {number[]} ids
|
|
691
|
+
* @returns {Promise<void>}
|
|
692
|
+
*/
|
|
693
|
+
async open_meshlets(ids) {
|
|
694
|
+
if (ids.length === 0) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const table = this.table;
|
|
699
|
+
const packs = this.manifest.packs;
|
|
700
|
+
|
|
701
|
+
const ranges = caldera_coalesce_ranges(ids, table);
|
|
702
|
+
|
|
703
|
+
let next = 0;
|
|
704
|
+
|
|
705
|
+
const worker = async () => {
|
|
706
|
+
while (next < ranges.length && this.fatal === undefined) {
|
|
707
|
+
const range = ranges[next++];
|
|
708
|
+
|
|
709
|
+
const bytes = await this.fetch_bytes(packs[range.pack].file, range);
|
|
710
|
+
|
|
711
|
+
let deadline = performance.now() + DESERIALIZE_BUDGET_MS;
|
|
712
|
+
|
|
713
|
+
for (const id of range.ids) {
|
|
714
|
+
const at = table.offset[id] - range.start;
|
|
715
|
+
|
|
716
|
+
try {
|
|
717
|
+
const geometry = meshlet_geometry_from_bytes(
|
|
718
|
+
bytes.slice(at, at + table.bytes[id])
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
this.geometries.set(id, geometry);
|
|
722
|
+
|
|
723
|
+
this.source_triangles += table.triangles[id];
|
|
724
|
+
} catch (error) {
|
|
725
|
+
this.refused++;
|
|
726
|
+
|
|
727
|
+
console.warn(`geometry ${id} — ${error.message ?? error}`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (performance.now() >= deadline) {
|
|
731
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
732
|
+
|
|
733
|
+
deadline = performance.now() + DESERIALIZE_BUDGET_MS;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// a pack range is tens of megabytes and the geometries out of it are already
|
|
738
|
+
// copies; yielding here is what lets the collector have it back
|
|
739
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
const workers = [];
|
|
744
|
+
|
|
745
|
+
for (let i = 0; i < Math.min(4, ranges.length); i++) {
|
|
746
|
+
workers.push(worker());
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
await Promise.all(workers);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Open the `.vgeo` containers for a set of geometries and register each with the one residency.
|
|
754
|
+
*
|
|
755
|
+
* @param {number[]} ids
|
|
756
|
+
* @returns {Promise<void>}
|
|
757
|
+
*/
|
|
758
|
+
async open_virtual(ids) {
|
|
759
|
+
if (ids.length === 0) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const options = new VGeoReadOptions();
|
|
764
|
+
|
|
765
|
+
/*
|
|
766
|
+
Frame verification on. These are bytes off a network rather than bytes a tool just wrote,
|
|
767
|
+
which is the case the check exists for and the reason §10 leaves it off by default.
|
|
768
|
+
*/
|
|
769
|
+
options.verify_frames = true;
|
|
770
|
+
|
|
771
|
+
// the runtime answers activation out of its arena; the reader's own counters would be
|
|
772
|
+
// megabytes at this instance count and are read by nothing
|
|
773
|
+
options.track_selectability = false;
|
|
774
|
+
|
|
775
|
+
options.head_prefix_bytes = HEAD_PREFIX_BYTES;
|
|
776
|
+
|
|
777
|
+
let next = 0;
|
|
778
|
+
|
|
779
|
+
const worker = async () => {
|
|
780
|
+
while (next < ids.length && this.fatal === undefined) {
|
|
781
|
+
const id = ids[next++];
|
|
782
|
+
|
|
783
|
+
const record = this.manifest.vgeo[this.table.vgeo[id]];
|
|
784
|
+
|
|
785
|
+
try {
|
|
786
|
+
const source = vgeo_byte_source_over_url(
|
|
787
|
+
new URL(record.file, this.base).href, record.bytes
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
const reader = await VGeoContainerReader.open(source, options);
|
|
791
|
+
|
|
792
|
+
const geometry = new VirtualGeometry(reader);
|
|
793
|
+
|
|
794
|
+
geometry.vg_row = this.residency.register(reader);
|
|
795
|
+
|
|
796
|
+
this.virtual.set(id, { geometry, reader });
|
|
797
|
+
|
|
798
|
+
this.source_triangles += this.table.triangles[id];
|
|
799
|
+
} catch (error) {
|
|
800
|
+
this.refused++;
|
|
801
|
+
|
|
802
|
+
console.warn(`${record.file} — ${error.message ?? error}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const workers = [];
|
|
808
|
+
|
|
809
|
+
for (let i = 0; i < Math.min(OPEN_CONCURRENCY, ids.length); i++) {
|
|
810
|
+
workers.push(worker());
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
await Promise.all(workers);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Turn placements into rows, yielding often enough that the page stays alive.
|
|
818
|
+
*
|
|
819
|
+
* A row is an entity, three components and a mesh row, and there are millions of them; this is
|
|
820
|
+
* the loop that decides whether the page loads in a minute or in five, and the only knob it has
|
|
821
|
+
* is how long it holds the thread between yields. It is also the loop that will break first
|
|
822
|
+
* when this set is scaled up, which is the point of the page.
|
|
823
|
+
*
|
|
824
|
+
* @param {{count: number, geometry: Uint32Array, matrices: Float32Array}} placements
|
|
825
|
+
* @param {number} budget stop once this many rows exist
|
|
826
|
+
* @returns {Promise<void>}
|
|
827
|
+
*/
|
|
828
|
+
async place(placements, budget) {
|
|
829
|
+
const transform = new Transform64();
|
|
830
|
+
|
|
831
|
+
const table = this.table;
|
|
832
|
+
|
|
833
|
+
let deadline = performance.now() + PLACEMENT_BUDGET_MS;
|
|
834
|
+
|
|
835
|
+
for (let i = 0; i < placements.count; i++) {
|
|
836
|
+
if (this.fatal !== undefined || this.placed >= budget) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const id = placements.geometry[i];
|
|
841
|
+
|
|
842
|
+
const virtual = this.virtual.get(id);
|
|
843
|
+
const geometry = virtual?.geometry ?? this.geometries.get(id);
|
|
844
|
+
|
|
845
|
+
if (geometry === undefined) {
|
|
846
|
+
// a geometry that refused; its placements are holes rather than a failure
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/*
|
|
851
|
+
The source matrix, decomposed. A transform row carries a TRS and the GPU composes the
|
|
852
|
+
world from it, so an instance carrying **shear** cannot be represented exactly and is
|
|
853
|
+
placed at the closest rigid-plus-scale fit. The engine reverses a reflected instance's
|
|
854
|
+
winding in the vertex shader off the row's own flag, so a negative determinant needs
|
|
855
|
+
nothing done to it here for the picture to be right.
|
|
856
|
+
*/
|
|
857
|
+
transform.fromMatrix(placements.matrices.subarray(i * 16, i * 16 + 16));
|
|
858
|
+
|
|
859
|
+
place_primitive(this.scene_context, geometry, this.material, transform);
|
|
860
|
+
|
|
861
|
+
this.placed++;
|
|
862
|
+
|
|
863
|
+
if (virtual !== undefined) {
|
|
864
|
+
this.placed_virtual++;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
this.instanced_triangles += table.triangles[id];
|
|
868
|
+
|
|
869
|
+
if (performance.now() >= deadline) {
|
|
870
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
871
|
+
|
|
872
|
+
deadline = performance.now() + PLACEMENT_BUDGET_MS;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
paint_districts() {
|
|
878
|
+
const done = this.districts.reduce(
|
|
879
|
+
(total, district) => total + (district.state === 'done' ? district.placements : 0), 0
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
const declared = this.districts.reduce(
|
|
883
|
+
(total, district) => total + district.placements, 0
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
element('progress').firstElementChild.style.width =
|
|
887
|
+
`${declared === 0 ? 0 : Math.round(100 * done / declared)}%`;
|
|
888
|
+
|
|
889
|
+
element('districts').innerHTML = this.districts.map(district => (
|
|
890
|
+
`<div class="row ${district.state}"><span>${escape_html(district.name)}</span>`
|
|
891
|
+
+ `<span>${district.state === 'done'
|
|
892
|
+
? district.placements.toLocaleString()
|
|
893
|
+
: district.state}</span></div>`
|
|
894
|
+
)).join('');
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Everything the panel shows and a profile capture records, in one object.
|
|
899
|
+
*
|
|
900
|
+
* @returns {object}
|
|
901
|
+
*/
|
|
902
|
+
state() {
|
|
903
|
+
const stats = this.feedback?.stats;
|
|
904
|
+
const pool = this.runtime?.pool;
|
|
905
|
+
|
|
906
|
+
const limits = this.renderer.graphics.collection_limits;
|
|
907
|
+
|
|
908
|
+
return {
|
|
909
|
+
frames: this.frames,
|
|
910
|
+
frame_ms: this.frame_ms,
|
|
911
|
+
cpu_ms: this.cpu_ms,
|
|
912
|
+
status: this.status_text,
|
|
913
|
+
fatal: this.fatal === undefined ? null : (this.fatal.message ?? String(this.fatal)),
|
|
914
|
+
screen_width: this.screen_width,
|
|
915
|
+
screen_height: this.screen_height,
|
|
916
|
+
scene: {
|
|
917
|
+
source: this.manifest?.source ?? '',
|
|
918
|
+
districts: this.districts.filter(d => d.state === 'done').length,
|
|
919
|
+
districts_declared: this.districts.length,
|
|
920
|
+
placements: this.placed,
|
|
921
|
+
placements_virtual: this.placed_virtual,
|
|
922
|
+
geometries: this.geometries.size,
|
|
923
|
+
geometries_virtual: this.virtual.size + this.merged.length,
|
|
924
|
+
merged: this.merged.length,
|
|
925
|
+
merged_triangles: this.merged.reduce((t, m) => t + m.record.triangles, 0),
|
|
926
|
+
source_triangles: this.source_triangles,
|
|
927
|
+
instanced_triangles: this.instanced_triangles,
|
|
928
|
+
bytes_fetched: this.bytes_fetched,
|
|
929
|
+
refused: this.refused,
|
|
930
|
+
open_ms: this.open_ms,
|
|
931
|
+
},
|
|
932
|
+
/*
|
|
933
|
+
The **limits**, not the counts: what the rasterization collections have had to grow to
|
|
934
|
+
in order to hold what the frame handed them. At this scale that is the number worth
|
|
935
|
+
watching — a collection that stops growing while placements keep arriving is one
|
|
936
|
+
dropping the difference, and `dropped` beside it is where that shows up.
|
|
937
|
+
*/
|
|
938
|
+
collections: {
|
|
939
|
+
meshes: limits.get_limit(RasterizationCollectionType.Meshes),
|
|
940
|
+
meshlets: limits.get_limit(RasterizationCollectionType.Meshlets),
|
|
941
|
+
},
|
|
942
|
+
capacities: this.capacities,
|
|
943
|
+
cut: stats === undefined ? undefined : {
|
|
944
|
+
tau: this.runtime.tau,
|
|
945
|
+
// the runtime's own, not what the page asked for — it is the one that was used
|
|
946
|
+
queue_capacity: this.runtime.queue_capacity,
|
|
947
|
+
drawn: stats[VGEO_CUT_STAT.DRAWN],
|
|
948
|
+
clamped: stats[VGEO_CUT_STAT.CLAMPED],
|
|
949
|
+
pages: stats[VGEO_CUT_STAT.PAGES],
|
|
950
|
+
clusters_tested: stats[VGEO_CUT_STAT.CLUSTERS_TESTED],
|
|
951
|
+
meshlets_dropped: stats[VGEO_CUT_STAT.MESHLETS_DROPPED],
|
|
952
|
+
instances_dropped: stats[VGEO_CUT_STAT.INSTANCES_DROPPED],
|
|
953
|
+
queue_dropped: stats[VGEO_CUT_STAT.QUEUE_DROPPED],
|
|
954
|
+
queue_remaining: stats[VGEO_CUT_STAT.QUEUE_REMAINING],
|
|
955
|
+
max_iterations: this.runtime.max_iterations,
|
|
956
|
+
},
|
|
957
|
+
vram: pool === undefined ? undefined : {
|
|
958
|
+
pages: pool.resident_page_count,
|
|
959
|
+
bytes: pool.resident_bytes,
|
|
960
|
+
capacity: pool.tables.page_capacity,
|
|
961
|
+
evicted: this.residency.evicted_vram_count,
|
|
962
|
+
refused: this.residency.refused_install_count,
|
|
963
|
+
},
|
|
964
|
+
camera: {
|
|
965
|
+
eye: Array.from(this.camera.transform.translation),
|
|
966
|
+
target: this.controller.target.toJSON(),
|
|
967
|
+
distance: this.controller.distance,
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
function human_bytes(bytes) {
|
|
975
|
+
if (bytes < 1024) {
|
|
976
|
+
return `${bytes} B`;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (bytes < 1024 * 1024) {
|
|
980
|
+
return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (bytes < 1024 * 1024 * 1024) {
|
|
984
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
function escape_html(text) {
|
|
992
|
+
return String(text).replace(/[&<>"]/g, c => (
|
|
993
|
+
{ '&': '&', '<': '<', '>': '>', '"': '"' }[c]
|
|
994
|
+
));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* How large the virtual-geometry tables have to be for the containers this set holds.
|
|
1000
|
+
*
|
|
1001
|
+
* These are the capacities a *scene* reaches first, and two of them degrade silently — a lookup
|
|
1002
|
+
* arena too small refuses containers, and a claim arena too small drops instances. Both are
|
|
1003
|
+
* functions of the set rather than of the frame, so they come off the manifest rather than out of a
|
|
1004
|
+
* constant.
|
|
1005
|
+
*
|
|
1006
|
+
* @param {object} manifest
|
|
1007
|
+
* @param {number} page_capacity
|
|
1008
|
+
* @returns {{geometry_capacity: number, lookup_words: number, claim_word_capacity: number}}
|
|
1009
|
+
*/
|
|
1010
|
+
function virtual_capacities(manifest, page_capacity, requested_queue) {
|
|
1011
|
+
const merged = manifest.merged ?? [];
|
|
1012
|
+
|
|
1013
|
+
/*
|
|
1014
|
+
Both kinds, because the tables are per *container* and the residency does not care which list a
|
|
1015
|
+
container came from. A merged asset is one container and a large share of the pages — Caldera's
|
|
1016
|
+
ground is one of 228 and a third of them.
|
|
1017
|
+
*/
|
|
1018
|
+
const containers = Math.max(1, (manifest.vgeo?.length ?? 0) + merged.length);
|
|
1019
|
+
|
|
1020
|
+
const pages = Math.max(
|
|
1021
|
+
1, (manifest.vgeo_pages ?? 0) + merged.reduce((total, m) => total + (m.pages ?? 0), 0)
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
/*
|
|
1025
|
+
Every instance of every promoted container, plus one apiece for the merged assets. The builder
|
|
1026
|
+
counts them while it writes the district files, because the count is a property of the
|
|
1027
|
+
placements and nothing else on either side of the conversion knows it.
|
|
1028
|
+
*/
|
|
1029
|
+
const instances = (manifest.vgeo ?? []).reduce((total, e) => total + (e.instances ?? 0), 0)
|
|
1030
|
+
+ merged.length;
|
|
1031
|
+
|
|
1032
|
+
const queue = requested_queue > 0
|
|
1033
|
+
? requested_queue
|
|
1034
|
+
: Math.min(
|
|
1035
|
+
1 << 22,
|
|
1036
|
+
1 << Math.ceil(Math.log2(Math.max(1 << 18, Math.max(1, instances) * 16)))
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
return {
|
|
1040
|
+
geometry_capacity: containers + 64,
|
|
1041
|
+
lookup_words: 1 << Math.ceil(Math.log2(Math.max(1 << 16, pages * 1.5))),
|
|
1042
|
+
claim_word_capacity: Math.ceil(
|
|
1043
|
+
Math.max(1, manifest.max_vgeo_instances ?? 1) * page_capacity / 32
|
|
1044
|
+
) * 2,
|
|
1045
|
+
queue_capacity: queue,
|
|
1046
|
+
instances,
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Where the converted set is.
|
|
1053
|
+
*
|
|
1054
|
+
* `?scene=` names a manifest outright; otherwise the asset server's root is asked, which is the
|
|
1055
|
+
* common case and saves typing a URL the server already knows.
|
|
1056
|
+
*
|
|
1057
|
+
* @returns {Promise<string>}
|
|
1058
|
+
*/
|
|
1059
|
+
async function resolve_manifest_url() {
|
|
1060
|
+
const parameters = new URL(globalThis.location.href).searchParams;
|
|
1061
|
+
|
|
1062
|
+
const named = parameters.get('scene');
|
|
1063
|
+
|
|
1064
|
+
if (named !== null) {
|
|
1065
|
+
return new URL(named, globalThis.location.href).href;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const origin = parameters.get('origin') ?? DEFAULT_ORIGIN;
|
|
1069
|
+
|
|
1070
|
+
return new URL(DEFAULT_MANIFEST, origin.endsWith('/') ? origin : `${origin}/`).href;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
async function start() {
|
|
1075
|
+
const canvas = element('view');
|
|
1076
|
+
|
|
1077
|
+
const page = new CalderaPlayground();
|
|
1078
|
+
|
|
1079
|
+
page.canvas = canvas;
|
|
1080
|
+
|
|
1081
|
+
const parameters = new URL(globalThis.location.href).searchParams;
|
|
1082
|
+
|
|
1083
|
+
page.status('reading the manifest…');
|
|
1084
|
+
|
|
1085
|
+
const manifest_url = await resolve_manifest_url();
|
|
1086
|
+
|
|
1087
|
+
const response = await fetch(manifest_url);
|
|
1088
|
+
|
|
1089
|
+
if (!response.ok) {
|
|
1090
|
+
throw new Error(
|
|
1091
|
+
`${manifest_url} answered HTTP ${response.status}`
|
|
1092
|
+
+ ` — is \`node tools/geometry/virtual/vgeo_serve.js <converted set>\` running?`
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
const manifest = await response.json();
|
|
1097
|
+
|
|
1098
|
+
if (manifest.format !== 1) {
|
|
1099
|
+
throw new Error(
|
|
1100
|
+
`${manifest_url} is a format-${manifest.format} set and this page reads format 1`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
page.manifest = manifest;
|
|
1105
|
+
page.base = manifest_url;
|
|
1106
|
+
|
|
1107
|
+
page.status('reading the geometry table…');
|
|
1108
|
+
|
|
1109
|
+
page.table = caldera_read_geometry_table(
|
|
1110
|
+
await page.fetch_bytes(manifest.geometry_table)
|
|
1111
|
+
);
|
|
1112
|
+
|
|
1113
|
+
if (page.table.count !== manifest.geometry_count) {
|
|
1114
|
+
throw new Error(
|
|
1115
|
+
`the manifest names ${manifest.geometry_count} geometries and the table holds`
|
|
1116
|
+
+ ` ${page.table.count}`
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/*
|
|
1121
|
+
Which districts, and how much of them. Both default to everything, because the whole point of
|
|
1122
|
+
the page is the whole map — but a machine that cannot hold it should find out by watching a
|
|
1123
|
+
counter stop rather than by waiting for a tab to die, and these are how you come back down.
|
|
1124
|
+
*/
|
|
1125
|
+
const wanted = parameters.get('districts');
|
|
1126
|
+
|
|
1127
|
+
page.districts = manifest.districts
|
|
1128
|
+
.filter(district => wanted === null || wanted.split(',').includes(district.name))
|
|
1129
|
+
.map(district => ({ ...district, state: 'pending' }));
|
|
1130
|
+
|
|
1131
|
+
if (page.districts.length === 0) {
|
|
1132
|
+
throw new Error(`no district matched ?districts=${wanted}`);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
const placement_budget = Number(parameters.get('placements') ?? Number.POSITIVE_INFINITY);
|
|
1136
|
+
|
|
1137
|
+
/*
|
|
1138
|
+
`?vgeo=off` moves the *promoted* geometries back onto their meshlet blobs. It cannot move the
|
|
1139
|
+
merged assets, which have no blobs to move to — so the runtime is enabled whenever the set holds
|
|
1140
|
+
either kind, and only the promotion is a choice.
|
|
1141
|
+
*/
|
|
1142
|
+
page.virtual_enabled = parameters.get('vgeo') !== 'off' && (manifest.vgeo?.length ?? 0) > 0;
|
|
1143
|
+
|
|
1144
|
+
const has_virtual = page.virtual_enabled || (manifest.merged?.length ?? 0) > 0;
|
|
1145
|
+
|
|
1146
|
+
const box = manifest.world_bounds;
|
|
1147
|
+
|
|
1148
|
+
page.world_radius = 0.5 * Math.hypot(box[3] - box[0], box[4] - box[1], box[5] - box[2]);
|
|
1149
|
+
|
|
1150
|
+
const renderer = new Renderer();
|
|
1151
|
+
|
|
1152
|
+
page.renderer = renderer;
|
|
1153
|
+
|
|
1154
|
+
await renderer.initialize({
|
|
1155
|
+
context: canvas.getContext('webgpu'),
|
|
1156
|
+
pixelRatio: globalThis.devicePixelRatio || 1,
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
/*
|
|
1160
|
+
TAA on, SSAO and bloom off. The jitter is what keeps a scene made almost entirely of small
|
|
1161
|
+
hard-edged geometry from crawling; the other two are picture rather than geometry and this page
|
|
1162
|
+
is about geometry, so they are frame time spent on the wrong question.
|
|
1163
|
+
*/
|
|
1164
|
+
renderer.feature_taa_enabled = true;
|
|
1165
|
+
renderer.feature_ssao_enabled = false;
|
|
1166
|
+
renderer.feature_bloom_enabled = false;
|
|
1167
|
+
|
|
1168
|
+
const scene = new Scene();
|
|
1169
|
+
|
|
1170
|
+
scene.dataset = new EntityComponentDataset();
|
|
1171
|
+
|
|
1172
|
+
page.scene = scene;
|
|
1173
|
+
|
|
1174
|
+
/*
|
|
1175
|
+
A sun roughly overhead and from the south-west, which is where Caldera's own lighting layer
|
|
1176
|
+
puts it. The set carries no lights — the conversion takes geometry and the source has no
|
|
1177
|
+
materials to begin with — so which way this points decides whether half the map is in ambient.
|
|
1178
|
+
*/
|
|
1179
|
+
place_light(scene, make_sunlight({ direction: new Vector3(0.4, -0.82, 0.41) }));
|
|
1180
|
+
|
|
1181
|
+
/*
|
|
1182
|
+
Image-based lighting, for the geometry rather than for the picture: a sun alone puts every
|
|
1183
|
+
surface facing away from it at ambient zero, and those are the silhouettes worth looking at.
|
|
1184
|
+
Not awaited — `test_assets/` is gitignored, so a checkout without it gets the sun alone.
|
|
1185
|
+
*/
|
|
1186
|
+
load_default_environment(scene);
|
|
1187
|
+
|
|
1188
|
+
const camera = new PerspectiveCamera();
|
|
1189
|
+
|
|
1190
|
+
camera.fov_degrees = 60;
|
|
1191
|
+
|
|
1192
|
+
page.camera = camera;
|
|
1193
|
+
page.controller = new OrbitalCameraController(camera, document.body);
|
|
1194
|
+
|
|
1195
|
+
/*
|
|
1196
|
+
The camera opens on the first district asked for rather than on the whole map. Caldera is two
|
|
1197
|
+
miles across and almost all of it is terrain; framed on the world box, a viewer opens three
|
|
1198
|
+
kilometres up looking at a grey lozenge, which is a true picture of the extent and tells you
|
|
1199
|
+
nothing about the geometry.
|
|
1200
|
+
*/
|
|
1201
|
+
const framed = page.districts[0];
|
|
1202
|
+
|
|
1203
|
+
frame_on(page, framed.placements > 0 ? framed.bounds : box);
|
|
1204
|
+
|
|
1205
|
+
page.fit();
|
|
1206
|
+
|
|
1207
|
+
globalThis.addEventListener('resize', () => page.fit());
|
|
1208
|
+
|
|
1209
|
+
// one frame to let the renderer build its scene context, which is what owns the geometry manager
|
|
1210
|
+
renderer.render(camera, scene, 1 / 60);
|
|
1211
|
+
|
|
1212
|
+
const scene_context = renderer.scenes.obtain(scene);
|
|
1213
|
+
|
|
1214
|
+
page.scene_context = scene_context;
|
|
1215
|
+
|
|
1216
|
+
if (has_virtual) {
|
|
1217
|
+
enable_virtual_geometry(page, manifest, parameters);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/*
|
|
1221
|
+
What the rasterization collections are primed to expect. Both grow on their own from what the
|
|
1222
|
+
frame reports, so this is about the first seconds rather than the steady state — but the first
|
|
1223
|
+
seconds are when the map is being placed, and a `Meshes` limit of 1e4 against four million rows
|
|
1224
|
+
counts the difference as `instances_dropped` for as long as it takes to catch up.
|
|
1225
|
+
*/
|
|
1226
|
+
const limits = renderer.graphics.collection_limits;
|
|
1227
|
+
|
|
1228
|
+
limits.prime(
|
|
1229
|
+
RasterizationCollectionType.Meshes,
|
|
1230
|
+
Math.max(1e4, Math.ceil(Math.min(manifest.placements, placement_budget) * 0.25))
|
|
1231
|
+
);
|
|
1232
|
+
|
|
1233
|
+
limits.prime(RasterizationCollectionType.Meshlets, 1e6);
|
|
1234
|
+
|
|
1235
|
+
const material = new StandardShadeMaterial();
|
|
1236
|
+
|
|
1237
|
+
material.diffuse_color.copy(new Color(0.70, 0.69, 0.66));
|
|
1238
|
+
material.roughness_factor = 0.62;
|
|
1239
|
+
material.metallic_factor = 0;
|
|
1240
|
+
|
|
1241
|
+
/*
|
|
1242
|
+
`?side=front|back|double` sets the whole map's draw side, which is the cheapest way to ask
|
|
1243
|
+
whether what you are looking at is a winding problem: under `double` nothing is culled, so a
|
|
1244
|
+
surface that appears there and not under `front` was being culled rather than being absent.
|
|
1245
|
+
*/
|
|
1246
|
+
material.draw_side = DRAW_SIDES[parameters.get('side')] ?? ShadeDrawSide.Front;
|
|
1247
|
+
|
|
1248
|
+
page.material = material;
|
|
1249
|
+
|
|
1250
|
+
install_profile_hotkey({
|
|
1251
|
+
renderer,
|
|
1252
|
+
name: "caldera",
|
|
1253
|
+
status: element('profile-status'),
|
|
1254
|
+
settings: () => {
|
|
1255
|
+
const state = page.state();
|
|
1256
|
+
|
|
1257
|
+
return [
|
|
1258
|
+
state.scene.districts_declared === state.scene.districts
|
|
1259
|
+
? `${state.scene.districts} districts`
|
|
1260
|
+
: `${state.scene.districts} of ${state.scene.districts_declared} districts`,
|
|
1261
|
+
`${state.scene.placements} placements`,
|
|
1262
|
+
`${state.scene.geometries + state.scene.geometries_virtual} geometries`,
|
|
1263
|
+
page.virtual_enabled ? `vgeo on, τ=${state.cut?.tau}px` : 'vgeo off',
|
|
1264
|
+
`${state.screen_width}×${state.screen_height}`,
|
|
1265
|
+
];
|
|
1266
|
+
},
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
// a drag on a control in the panel is a drag on the body, and the camera controller listens there
|
|
1270
|
+
element('panel').addEventListener('pointerdown', event => event.stopPropagation());
|
|
1271
|
+
|
|
1272
|
+
page.paint_districts();
|
|
1273
|
+
|
|
1274
|
+
/*
|
|
1275
|
+
What a headless session reads. `__state` is the panel's own numbers, so a screenshot and a probe
|
|
1276
|
+
can never disagree about what the frame did; `__look` is how a scripted tour moves.
|
|
1277
|
+
*/
|
|
1278
|
+
globalThis.__page = page;
|
|
1279
|
+
globalThis.__state = () => page.state();
|
|
1280
|
+
globalThis.__look = (eye, target) => page.look(eye, target);
|
|
1281
|
+
|
|
1282
|
+
let stopped = false;
|
|
1283
|
+
let last_time;
|
|
1284
|
+
|
|
1285
|
+
const tick = time => {
|
|
1286
|
+
if (stopped) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
const dt = last_time === undefined ? 1 / 60 : Math.min((time - last_time) / 1000, 0.05);
|
|
1291
|
+
|
|
1292
|
+
if (last_time !== undefined) {
|
|
1293
|
+
page.frame_ms += ((time - last_time) - page.frame_ms) * 0.1;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
last_time = time;
|
|
1297
|
+
|
|
1298
|
+
const cpu_started = performance.now();
|
|
1299
|
+
|
|
1300
|
+
try {
|
|
1301
|
+
page.controller.update(dt);
|
|
1302
|
+
|
|
1303
|
+
renderer.render(camera, scene, dt);
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
stopped = true;
|
|
1306
|
+
|
|
1307
|
+
page.fatal = error;
|
|
1308
|
+
|
|
1309
|
+
page.status(
|
|
1310
|
+
`${error.message ?? String(error)}`
|
|
1311
|
+
+ ` — stopped at ${page.placed.toLocaleString()} placements`
|
|
1312
|
+
+ ` and ${(page.geometries.size + page.virtual.size).toLocaleString()} geometries`,
|
|
1313
|
+
true
|
|
1314
|
+
);
|
|
1315
|
+
|
|
1316
|
+
console.error(error);
|
|
1317
|
+
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
page.cpu_ms += ((performance.now() - cpu_started) - page.cpu_ms) * 0.1;
|
|
1322
|
+
page.frames++;
|
|
1323
|
+
|
|
1324
|
+
paint(page);
|
|
1325
|
+
|
|
1326
|
+
requestAnimationFrame(tick);
|
|
1327
|
+
};
|
|
1328
|
+
|
|
1329
|
+
requestAnimationFrame(tick);
|
|
1330
|
+
|
|
1331
|
+
page.status(
|
|
1332
|
+
`opening ${page.districts.length} district${page.districts.length === 1 ? '' : 's'}…`
|
|
1333
|
+
);
|
|
1334
|
+
|
|
1335
|
+
/*
|
|
1336
|
+
Awaited, unlike the frame loop above it, because there is nothing after it but the summary. The
|
|
1337
|
+
scene arrives under a camera that is already drawing, which is the behaviour worth watching: a
|
|
1338
|
+
district that has not landed is simply not in the frame, and the frame does not wait for it.
|
|
1339
|
+
*/
|
|
1340
|
+
await page.open_districts(placement_budget);
|
|
1341
|
+
|
|
1342
|
+
if (page.fatal === undefined) {
|
|
1343
|
+
page.status(
|
|
1344
|
+
`${page.placed.toLocaleString()} placements`
|
|
1345
|
+
+ `, ${(page.geometries.size + page.virtual.size).toLocaleString()} geometries`
|
|
1346
|
+
+ `, ${human_bytes(page.bytes_fetched)} fetched`
|
|
1347
|
+
+ `, opened in ${(page.open_ms / 1000).toFixed(1)} s`
|
|
1348
|
+
+ (page.refused > 0 ? ` — ${page.refused} refused` : '')
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
globalThis.__opened = true;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* Point the camera at a box, from far enough out to see all of it.
|
|
1358
|
+
*
|
|
1359
|
+
* Down and along rather than straight down: a map is read from an angle, and a plan view of one made
|
|
1360
|
+
* of buildings is a picture of their roofs.
|
|
1361
|
+
*
|
|
1362
|
+
* @param {CalderaPlayground} page
|
|
1363
|
+
* @param {number[]} box
|
|
1364
|
+
*/
|
|
1365
|
+
function frame_on(page, box) {
|
|
1366
|
+
const centre = [
|
|
1367
|
+
(box[0] + box[3]) / 2, (box[1] + box[4]) / 2, (box[2] + box[5]) / 2,
|
|
1368
|
+
];
|
|
1369
|
+
|
|
1370
|
+
const radius = Math.max(
|
|
1371
|
+
10, 0.5 * Math.hypot(box[3] - box[0], box[4] - box[1], box[5] - box[2])
|
|
1372
|
+
);
|
|
1373
|
+
|
|
1374
|
+
page.look(
|
|
1375
|
+
[centre[0] + radius * 0.9, centre[1] + radius * 0.55, centre[2] + radius * 0.9],
|
|
1376
|
+
centre
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Turn on virtual geometry for the containers the set promoted, and size its tables for them.
|
|
1383
|
+
*
|
|
1384
|
+
* @param {CalderaPlayground} page
|
|
1385
|
+
* @param {object} manifest
|
|
1386
|
+
* @param {URLSearchParams} parameters
|
|
1387
|
+
*/
|
|
1388
|
+
function enable_virtual_geometry(page, manifest, parameters) {
|
|
1389
|
+
const page_capacity = Number(parameters.get('pages') ?? 16384);
|
|
1390
|
+
|
|
1391
|
+
const capacities = virtual_capacities(
|
|
1392
|
+
manifest, page_capacity, Number(parameters.get('queue') ?? 0)
|
|
1393
|
+
);
|
|
1394
|
+
|
|
1395
|
+
page.capacities = capacities;
|
|
1396
|
+
|
|
1397
|
+
const residency = new VGeoResidencyOptions();
|
|
1398
|
+
|
|
1399
|
+
residency.ram_budget_bytes = 2 * GIGABYTE;
|
|
1400
|
+
|
|
1401
|
+
/*
|
|
1402
|
+
VRAM is a strict subset of RAM, so the RAM tier has to hold the whole VRAM tier and every
|
|
1403
|
+
container's pinned root besides.
|
|
1404
|
+
*/
|
|
1405
|
+
residency.ram_slot_capacity = Math.max(
|
|
1406
|
+
1 << 14,
|
|
1407
|
+
(page_capacity + (manifest.vgeo?.length ?? 0) + (manifest.merged?.length ?? 0)) * 2
|
|
1408
|
+
);
|
|
1409
|
+
|
|
1410
|
+
residency.max_fetches_per_reading = 64;
|
|
1411
|
+
residency.max_concurrent_fetches = 24;
|
|
1412
|
+
residency.max_promotions_per_reading = 128;
|
|
1413
|
+
|
|
1414
|
+
page.runtime = page.scene_context.enable_virtual_geometry({
|
|
1415
|
+
page_size: manifest.vgeo_page_size ?? manifest.merged?.[0]?.page_size,
|
|
1416
|
+
table_options: {
|
|
1417
|
+
page_capacity,
|
|
1418
|
+
geometry_capacity: capacities.geometry_capacity,
|
|
1419
|
+
lookup_words: capacities.lookup_words,
|
|
1420
|
+
},
|
|
1421
|
+
claim_word_capacity: capacities.claim_word_capacity,
|
|
1422
|
+
want_capacity: 8192,
|
|
1423
|
+
/*
|
|
1424
|
+
Traversal items one wavefront may hold, over the engine's 1 << 16. This is the capacity a
|
|
1425
|
+
scene reaches first and its overflow is invisible without looking: the frontier is simply
|
|
1426
|
+
thrown away, and what you see is the drawn cluster count wandering between frames as
|
|
1427
|
+
different subtrees lose the race.
|
|
1428
|
+
|
|
1429
|
+
It is derived from the instance count rather than fixed, because the descent is seeded with
|
|
1430
|
+
one item per visible virtual instance and that number is a property of the *set*: at the
|
|
1431
|
+
default 20,000-triangle promotion Caldera has 12,147 virtual instances, and at 4,096 it has
|
|
1432
|
+
172,764. A constant that is ample for one is a fourteenth of the other. Zorah settled at
|
|
1433
|
+
1 << 18 for 17,424 instances, which is where the factor of sixteen comes from.
|
|
1434
|
+
*/
|
|
1435
|
+
queue_capacity: capacities.queue_capacity,
|
|
1436
|
+
residency,
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
page.residency = page.scene_context.virtual_geometry_residency;
|
|
1440
|
+
page.feedback = page.scene_context.virtual_geometry_feedback;
|
|
1441
|
+
|
|
1442
|
+
globalThis.addEventListener('pagehide', () => page.feedback.destroy());
|
|
1443
|
+
|
|
1444
|
+
page.renderer.add_extension(new VGeoFrameStart(page));
|
|
1445
|
+
page.renderer.add_extension(new VGeoFrameEnd(page));
|
|
1446
|
+
|
|
1447
|
+
const tau = element('tau');
|
|
1448
|
+
|
|
1449
|
+
tau.addEventListener('input', event => {
|
|
1450
|
+
page.runtime.tau = Number(event.target.value);
|
|
1451
|
+
|
|
1452
|
+
element('tau-value').textContent = page.runtime.tau.toFixed(2);
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
const requested = Number(parameters.get('tau'));
|
|
1456
|
+
|
|
1457
|
+
if (Number.isFinite(requested) && requested > 0) {
|
|
1458
|
+
page.runtime.tau = requested;
|
|
1459
|
+
|
|
1460
|
+
tau.value = String(requested);
|
|
1461
|
+
element('tau-value').textContent = requested.toFixed(2);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* @type {number}
|
|
1468
|
+
*/
|
|
1469
|
+
let painted_at = 0;
|
|
1470
|
+
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* @param {CalderaPlayground} page
|
|
1474
|
+
*/
|
|
1475
|
+
function paint(page) {
|
|
1476
|
+
const now = performance.now();
|
|
1477
|
+
|
|
1478
|
+
// the panel is DOM and the scene is four million rows; repainting it every frame is a
|
|
1479
|
+
// measurable share of a frame that this page is trying to measure
|
|
1480
|
+
if (now - painted_at < 250) {
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
painted_at = now;
|
|
1485
|
+
|
|
1486
|
+
const state = page.state();
|
|
1487
|
+
const scene = state.scene;
|
|
1488
|
+
|
|
1489
|
+
set('s-districts', `${scene.districts} / ${scene.districts_declared}`);
|
|
1490
|
+
set(
|
|
1491
|
+
's-placements',
|
|
1492
|
+
scene.placements.toLocaleString()
|
|
1493
|
+
+ (scene.placements > ROWS_LAST_KNOWN_GOOD ? ' ⚠ past measured VRAM' : ''),
|
|
1494
|
+
scene.placements > ROWS_LAST_KNOWN_GOOD ? 'bad' : ''
|
|
1495
|
+
);
|
|
1496
|
+
set('s-geometries', scene.geometries_virtual > 0
|
|
1497
|
+
? `${scene.geometries.toLocaleString()} + ${scene.geometries_virtual.toLocaleString()} vg`
|
|
1498
|
+
: scene.geometries.toLocaleString());
|
|
1499
|
+
set('s-source', scene.source_triangles.toLocaleString());
|
|
1500
|
+
set('s-instanced', scene.instanced_triangles.toLocaleString());
|
|
1501
|
+
set('s-fetched', human_bytes(scene.bytes_fetched));
|
|
1502
|
+
set('s-open', scene.open_ms > 0 ? `${(scene.open_ms / 1000).toFixed(1)} s` : '—');
|
|
1503
|
+
|
|
1504
|
+
set('f-frame', `${state.frame_ms.toFixed(1)} ms`);
|
|
1505
|
+
set('f-cpu', `${state.cpu_ms.toFixed(1)} ms`);
|
|
1506
|
+
set('f-resolution', `${state.screen_width}×${state.screen_height}`);
|
|
1507
|
+
set('f-meshes', state.collections.meshes.toLocaleString());
|
|
1508
|
+
set('f-meshlets', state.collections.meshlets.toLocaleString());
|
|
1509
|
+
|
|
1510
|
+
/*
|
|
1511
|
+
Virtual geometry's drops only — the ordinary meshlet path publishes none, which is exactly why
|
|
1512
|
+
the 2^20 failure above was invisible. A zero here is not a statement that the frame is complete.
|
|
1513
|
+
|
|
1514
|
+
`queue_dropped` is in the sum because it is the one that degrades *quietly*: an overflowed
|
|
1515
|
+
frontier is thrown away rather than refused, and what you see is the drawn cluster count
|
|
1516
|
+
wandering between frames as different subtrees lose the race. It reads as instability in the
|
|
1517
|
+
cut rather than as a capacity that was too small.
|
|
1518
|
+
*/
|
|
1519
|
+
const dropped = (state.cut?.instances_dropped ?? 0)
|
|
1520
|
+
+ (state.cut?.meshlets_dropped ?? 0)
|
|
1521
|
+
+ (state.cut?.queue_dropped ?? 0);
|
|
1522
|
+
|
|
1523
|
+
set('f-dropped', dropped.toLocaleString(), dropped > 0 ? 'bad' : 'good');
|
|
1524
|
+
|
|
1525
|
+
if (state.cut === undefined) {
|
|
1526
|
+
set('v-containers', 'off', 'muted');
|
|
1527
|
+
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
set('v-containers', scene.merged > 0
|
|
1532
|
+
? `${scene.geometries_virtual.toLocaleString()} (${scene.merged} merged)`
|
|
1533
|
+
: scene.geometries_virtual.toLocaleString());
|
|
1534
|
+
set('v-instances', scene.placements_virtual.toLocaleString());
|
|
1535
|
+
set('v-drawn', state.cut.drawn.toLocaleString());
|
|
1536
|
+
set('v-pages', `${state.vram.pages.toLocaleString()} / ${state.vram.capacity.toLocaleString()}`);
|
|
1537
|
+
|
|
1538
|
+
const strained = state.vram.refused + state.cut.clamped;
|
|
1539
|
+
|
|
1540
|
+
set('v-refused', `${state.vram.refused.toLocaleString()} / ${state.cut.clamped.toLocaleString()}`,
|
|
1541
|
+
strained > 0 ? 'warn' : 'good');
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* @param {string} id
|
|
1547
|
+
* @param {string} text
|
|
1548
|
+
* @param {string} [tone]
|
|
1549
|
+
*/
|
|
1550
|
+
function set(id, text, tone) {
|
|
1551
|
+
const node = element(id);
|
|
1552
|
+
|
|
1553
|
+
node.textContent = text;
|
|
1554
|
+
node.className = tone ?? '';
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
|
|
1558
|
+
start().catch(error => {
|
|
1559
|
+
console.error(error);
|
|
1560
|
+
|
|
1561
|
+
const node = element('status');
|
|
1562
|
+
|
|
1563
|
+
node.textContent = error.message ?? String(error);
|
|
1564
|
+
node.classList.add('error');
|
|
1565
|
+
});
|