@woosh/meep-engine 3.23.0 → 3.24.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/editor/entity_world_bounds.d.ts +9 -3
- package/editor/entity_world_bounds.d.ts.map +1 -1
- package/editor/entity_world_bounds.js +61 -4
- package/package.json +1 -1
- package/src/core/binary/allocator/OffsetAllocator.d.ts +40 -2
- package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
- package/src/core/binary/allocator/OffsetAllocator.js +124 -29
- package/src/core/collection/map/u32_index.d.ts +83 -0
- package/src/core/collection/map/u32_index.d.ts.map +1 -0
- package/src/core/collection/map/u32_index.js +247 -0
- package/src/engine/asset/loaders/GLTFSceneBundleAssetLoader.d.ts +2 -2
- package/src/engine/asset/loaders/GLTFSceneBundleAssetLoader.js +2 -2
- package/src/engine/asset/loaders/USDSceneBundleAssetLoader.d.ts +1 -2
- package/src/engine/asset/loaders/USDSceneBundleAssetLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/USDSceneBundleAssetLoader.js +4 -12
- package/src/engine/ecs/gpu/GPUStateAuthority.d.ts +55 -0
- package/src/engine/ecs/gpu/GPUStateAuthority.d.ts.map +1 -0
- package/src/engine/ecs/gpu/GPUStateAuthority.js +72 -0
- package/src/engine/ecs/gpu/GPUStateAuthorityFlags.d.ts +40 -0
- package/src/engine/ecs/gpu/GPUStateAuthorityFlags.d.ts.map +1 -0
- package/src/engine/ecs/gpu/GPUStateAuthorityFlags.js +79 -0
- package/src/engine/ecs/gpu/gpu_authority_assert_cpu.d.ts +18 -0
- package/src/engine/ecs/gpu/gpu_authority_assert_cpu.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_assert_cpu.js +27 -0
- package/src/engine/ecs/gpu/gpu_authority_declare.d.ts +23 -0
- package/src/engine/ecs/gpu/gpu_authority_declare.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_declare.js +39 -0
- package/src/engine/ecs/gpu/gpu_authority_grant.d.ts +15 -0
- package/src/engine/ecs/gpu/gpu_authority_grant.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_grant.js +38 -0
- package/src/engine/ecs/gpu/gpu_authority_has.d.ts +18 -0
- package/src/engine/ecs/gpu/gpu_authority_has.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_has.js +28 -0
- package/src/engine/ecs/gpu/gpu_authority_revoke.d.ts +24 -0
- package/src/engine/ecs/gpu/gpu_authority_revoke.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_revoke.js +49 -0
- package/src/engine/ecs/ik/IKProblem.d.ts +14 -0
- package/src/engine/ecs/ik/IKProblem.d.ts.map +1 -1
- package/src/engine/ecs/ik/IKProblem.js +16 -0
- package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts.map +1 -1
- package/src/engine/ecs/ik/InverseKinematicsSystem.js +19 -0
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +2 -2
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +2 -2
- package/src/engine/ecs/ik/ik_bone_chain.d.ts +11 -2
- package/src/engine/ecs/ik/ik_bone_chain.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_bone_chain.js +47 -2
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.js +6 -0
- package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
- package/src/engine/ecs/speaker/VoiceSystem.js +6 -0
- package/src/engine/ecs/transform/T64WorldCache.d.ts +50 -0
- package/src/engine/ecs/transform/T64WorldCache.d.ts.map +1 -0
- package/src/engine/ecs/transform/T64WorldCache.js +139 -0
- package/src/engine/ecs/transform/t64_evaluate_world.d.ts +31 -0
- package/src/engine/ecs/transform/t64_evaluate_world.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_evaluate_world.js +126 -0
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts +8 -0
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +59 -30
- package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.js +4 -11
- package/src/engine/graphics/ecs/mesh-v2/SHADED_GEOMETRY_EVENT_CHANGE.d.ts +14 -0
- package/src/engine/graphics/ecs/mesh-v2/SHADED_GEOMETRY_EVENT_CHANGE.d.ts.map +1 -0
- package/src/engine/graphics/ecs/mesh-v2/SHADED_GEOMETRY_EVENT_CHANGE.js +13 -0
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +2 -14
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +11 -21
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.d.ts +1 -0
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +7 -0
- package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.d.ts +5 -6
- package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.js +13 -11
- package/src/engine/graphics/ecs/mesh-v2/shaded_geometry_announce_change.d.ts +14 -0
- package/src/engine/graphics/ecs/mesh-v2/shaded_geometry_announce_change.d.ts.map +1 -0
- package/src/engine/graphics/ecs/mesh-v2/shaded_geometry_announce_change.js +21 -0
- package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +1 -1
- package/src/engine/graphics/ecs/particles/ParticleEffect.js +1 -1
- package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.d.ts +1 -1
- package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +1 -1
- package/src/engine/graphics3/AnimationGraphSystem.js +3 -3
- package/src/engine/graphics3/AnimationSystem.js +2 -2
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem.js +2 -4
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +34 -27
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +185 -85
- package/src/engine/graphics3/HighlightOutlineSystem.d.ts +4 -3
- package/src/engine/graphics3/HighlightOutlineSystem.d.ts.map +1 -1
- package/src/engine/graphics3/HighlightOutlineSystem.js +7 -9
- package/src/engine/graphics3/LightSystem.d.ts +7 -5
- package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
- package/src/engine/graphics3/LightSystem.js +36 -22
- package/src/engine/graphics3/MeshSystem.d.ts +43 -39
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem.js +324 -331
- package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -1
- package/src/engine/graphics3/PathDisplaySystem.js +2 -4
- package/src/engine/graphics3/PickingQuery.d.ts +15 -21
- package/src/engine/graphics3/PickingQuery.d.ts.map +1 -1
- package/src/engine/graphics3/PickingQuery.js +21 -25
- package/src/engine/graphics3/PickingSystem.d.ts +60 -31
- package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
- package/src/engine/graphics3/PickingSystem.js +416 -273
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts +22 -12
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem.js +309 -174
- package/src/engine/graphics3/TerrainSystem.d.ts +19 -11
- package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -1
- package/src/engine/graphics3/TerrainSystem.js +102 -53
- package/src/engine/graphics3/TooltipComponentSystem.d.ts.map +1 -1
- package/src/engine/graphics3/TooltipComponentSystem.js +2 -14
- package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics3/Trail3DSystem.js +2 -4
- package/src/engine/graphics3/animation/ClipListPlayer.d.ts +4 -4
- package/src/engine/graphics3/animation/ClipListPlayer.d.ts.map +1 -1
- package/src/engine/graphics3/animation/ClipListPlayer.js +2 -2
- package/src/engine/graphics3/highlight/pack_highlight_table.d.ts +9 -11
- package/src/engine/graphics3/highlight/pack_highlight_table.d.ts.map +1 -1
- package/src/engine/graphics3/highlight/pack_highlight_table.js +11 -18
- package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +4 -3
- package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
- package/src/engine/graphics3/pose/query_entity_node_world_pose.js +75 -74
- package/src/engine/graphics3/prefab/Prefab.d.ts +130 -0
- package/src/engine/graphics3/prefab/Prefab.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/Prefab.js +162 -0
- package/src/engine/graphics3/prefab/PrefabInstance.d.ts +72 -0
- package/src/engine/graphics3/prefab/PrefabInstance.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/PrefabInstance.js +80 -0
- package/src/engine/graphics3/prefab/prefab_compile.d.ts +23 -0
- package/src/engine/graphics3/prefab/prefab_compile.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_compile.js +334 -0
- package/src/engine/graphics3/prefab/prefab_instantiate.d.ts +46 -0
- package/src/engine/graphics3/prefab/prefab_instantiate.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_instantiate.js +230 -0
- package/src/engine/graphics3/prefab/prefab_normalize_skin_frames.d.ts +70 -0
- package/src/engine/graphics3/prefab/prefab_normalize_skin_frames.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_normalize_skin_frames.js +210 -0
- package/src/engine/graphics3/prefab/prefab_place.d.ts +60 -0
- package/src/engine/graphics3/prefab/prefab_place.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_place.js +130 -0
- package/src/engine/graphics3/prefab/prefab_skin_clip_scale_conflicts.d.ts +60 -0
- package/src/engine/graphics3/prefab/prefab_skin_clip_scale_conflicts.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_skin_clip_scale_conflicts.js +128 -0
- package/src/engine/graphics3/prefab/prefab_skin_frame.d.ts +40 -0
- package/src/engine/graphics3/prefab/prefab_skin_frame.d.ts.map +1 -0
- package/src/engine/graphics3/prefab/prefab_skin_frame.js +116 -0
- package/src/engine/graphics3/preview/make_model_preview_scene.d.ts +16 -14
- package/src/engine/graphics3/preview/make_model_preview_scene.d.ts.map +1 -1
- package/src/engine/graphics3/preview/make_model_preview_scene.js +126 -154
- package/src/engine/graphics3/preview/make_model_thumbnail.js +1 -1
- package/src/engine/graphics3/register_instance_animation.d.ts +6 -6
- package/src/engine/graphics3/register_instance_animation.d.ts.map +1 -1
- package/src/engine/graphics3/register_instance_animation.js +4 -4
- package/src/engine/graphics3/shade_bundle_bounds.d.ts +2 -6
- package/src/engine/graphics3/shade_bundle_bounds.d.ts.map +1 -1
- package/src/engine/graphics3/shade_bundle_bounds.js +47 -55
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts +11 -18
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts.map +1 -1
- package/src/engine/graphics3/terrain/pack_terrain_row_table.js +48 -53
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +17 -2
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -1
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +284 -256
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +13 -15
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +12 -14
- package/src/engine/physics/cloth/ecs/ClothRig.d.ts +7 -8
- package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothRig.js +7 -8
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +5 -4
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +11 -12
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +19 -23
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +201 -199
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +8 -0
- package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts +18 -34
- package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +58 -115
- package/src/engine/physics/cloth/playground/README.md +11 -11
- package/src/engine/physics/cloth/playground/garment_build.d.ts +0 -1
- package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -1
- package/src/engine/physics/cloth/playground/garment_build.js +64 -58
- package/src/engine/physics/cloth/playground/garment_main.js +59 -33
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/RENDERER_CONTRACT.md +85 -53
- package/src/shade/device/ShadeGPUCommandContext.d.ts +23 -0
- package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
- package/src/shade/device/ShadeGPUCommandContext.js +36 -0
- package/src/shade/playground/add_random_meshes.d.ts +4 -4
- package/src/shade/playground/add_random_meshes.d.ts.map +1 -1
- package/src/shade/playground/add_random_meshes.js +20 -16
- package/src/shade/playground/avboit/main.d.ts +1 -0
- package/src/shade/playground/avboit/main.d.ts.map +1 -1
- package/src/shade/playground/avboit/main.js +916 -875
- package/src/shade/playground/basis_textures/main.d.ts.map +1 -1
- package/src/shade/playground/basis_textures/main.js +22 -8
- package/src/shade/playground/basis_textures/texture_inventory.d.ts +2 -2
- package/src/shade/playground/basis_textures/texture_inventory.d.ts.map +1 -1
- package/src/shade/playground/basis_textures/texture_inventory.js +6 -7
- package/src/shade/playground/ground_seam/main.d.ts.map +1 -1
- package/src/shade/playground/ground_seam/main.js +13 -4
- package/src/shade/playground/ground_seam/make_ground.d.ts +13 -4
- package/src/shade/playground/ground_seam/make_ground.d.ts.map +1 -1
- package/src/shade/playground/ground_seam/make_ground.js +21 -16
- package/src/shade/playground/particle_ecs/bonfire_editor.js +3 -3
- package/src/shade/playground/particle_system/main.js +11 -10
- package/src/shade/playground/particle_system/particle_prototype.d.ts +15 -10
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +56 -15
- package/src/shade/playground/particle_system/particle_scene.d.ts +68 -28
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_scene.js +149 -53
- package/src/shade/playground/readback_crossover/index.html +41 -0
- package/src/shade/playground/readback_crossover/main.d.ts +2 -0
- package/src/shade/playground/readback_crossover/main.d.ts.map +1 -0
- package/src/shade/playground/readback_crossover/main.js +372 -0
- package/src/shade/playground/skinned_blas_refit/README.md +11 -7
- package/src/shade/playground/skinned_blas_refit/add_floor.d.ts +13 -44
- package/src/shade/playground/skinned_blas_refit/add_floor.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/add_floor.js +19 -59
- package/src/shade/playground/skinned_blas_refit/main.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/main.js +67 -58
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts +40 -54
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/place_character.js +47 -74
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts +3 -3
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/sweep_views.js +4 -3
- package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.d.ts +3 -3
- package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.js +11 -4
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.js +38 -9
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts +3 -3
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.js +9 -6
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts +3 -3
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +3 -3
- package/src/shade/playground/skinned_mesh_soup/README.md +6 -6
- package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -1
- package/src/shade/playground/skinned_mesh_soup/main.js +44 -102
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts +5 -5
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -1
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +42 -33
- package/src/shade/playground/sponza_path_trace/main.d.ts +1 -0
- package/src/shade/playground/sponza_path_trace/main.d.ts.map +1 -1
- package/src/shade/playground/sponza_path_trace/main.js +393 -363
- package/src/shade/playground/ssr_reprojection/main.js +11 -9
- package/src/shade/playground/ssr_reprojection/scene.d.ts +15 -4
- package/src/shade/playground/ssr_reprojection/scene.d.ts.map +1 -1
- package/src/shade/playground/ssr_reprojection/scene.js +32 -11
- package/src/shade/playground/ssr_variance/main.js +1 -1
- package/src/shade/playground/ssr_variance/scene.d.ts +10 -4
- package/src/shade/playground/ssr_variance/scene.d.ts.map +1 -1
- package/src/shade/playground/ssr_variance/scene.js +39 -15
- package/src/shade/playground/vgeo_viewer/main.js +6 -2
- package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
- package/src/shade/playground/volumetrics_froxel/main.js +936 -922
- package/src/shade/renderer/GraphicsContext.d.ts +7 -0
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +11 -0
- package/src/shade/renderer/Renderer.d.ts +3 -2
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +3 -2
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +39 -47
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +319 -256
- package/src/shade/renderer/animation/ShadeAnimationChannel.d.ts +26 -22
- package/src/shade/renderer/animation/ShadeAnimationChannel.d.ts.map +1 -1
- package/src/shade/renderer/animation/ShadeAnimationChannel.js +28 -23
- package/src/shade/renderer/animation/ShadeAnimationClip.d.ts +14 -6
- package/src/shade/renderer/animation/ShadeAnimationClip.d.ts.map +1 -1
- package/src/shade/renderer/animation/ShadeAnimationClip.js +20 -8
- package/src/shade/renderer/animation/Skin.d.ts +19 -19
- package/src/shade/renderer/animation/Skin.d.ts.map +1 -1
- package/src/shade/renderer/animation/Skin.js +15 -13
- package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts +6 -5
- package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts.map +1 -1
- package/src/shade/renderer/animation/pose/pose_evaluate_local.js +9 -9
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts +5 -59
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts.map +1 -1
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.js +8 -132
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +65 -62
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +148 -194
- package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +4 -7
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts +2 -2
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +2 -2
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts +74 -0
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts.map +1 -0
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.js +240 -0
- package/src/shade/renderer/buffer/table/GPUDatabase.d.ts +14 -0
- package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUDatabase.js +64 -6
- package/src/shade/renderer/buffer/table/GPUDatabaseReadback.d.ts +190 -0
- package/src/shade/renderer/buffer/table/GPUDatabaseReadback.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/GPUDatabaseReadback.js +1323 -0
- package/src/shade/renderer/buffer/table/GPUPageMetadata.d.ts +10 -0
- package/src/shade/renderer/buffer/table/GPUPageMetadata.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUPageMetadata.js +12 -0
- package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts +96 -0
- package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/GPUReadbackStatus.d.ts +15 -0
- package/src/shade/renderer/buffer/table/GPUReadbackStatus.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/GPUReadbackStatus.js +34 -0
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts +75 -7
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +518 -135
- package/src/shade/renderer/buffer/table/GPU_TABLE_GATHER_DESCRIPTOR_WORDS.d.ts +19 -0
- package/src/shade/renderer/buffer/table/GPU_TABLE_GATHER_DESCRIPTOR_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/GPU_TABLE_GATHER_DESCRIPTOR_WORDS.js +18 -0
- package/src/shade/renderer/buffer/table/WordViews.d.ts +39 -0
- package/src/shade/renderer/buffer/table/WordViews.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/WordViews.js +62 -0
- package/src/shade/renderer/buffer/table/gpu_table_gather_shader.d.ts +27 -0
- package/src/shade/renderer/buffer/table/gpu_table_gather_shader.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/gpu_table_gather_shader.js +117 -0
- package/src/shade/renderer/buffer/table/serde/generate_record_writer.d.ts +121 -0
- package/src/shade/renderer/buffer/table/serde/generate_record_writer.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/serde/generate_record_writer.js +395 -0
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +70 -20
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +6 -5
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +3 -3
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +15 -11
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_bake_for_scene.d.ts +1 -1
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_bake_for_scene.js +4 -4
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.d.ts +1 -3
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.js +5 -4
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +1 -1
- package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +1 -4
- package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts +8 -3
- package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.js +19 -14
- package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +9 -1
- package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.d.ts +3 -3
- package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.js +12 -5
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +1 -1
- package/src/shade/renderer/light/GPULightCollection.d.ts +29 -0
- package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
- package/src/shade/renderer/light/GPULightCollection.js +444 -329
- package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.d.ts.map +1 -1
- package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.js +7 -1
- package/src/shade/renderer/light/LightCollection.d.ts +29 -1
- package/src/shade/renderer/light/LightCollection.d.ts.map +1 -1
- package/src/shade/renderer/light/LightCollection.js +50 -1
- package/src/shade/renderer/light/make_sunlight.js +2 -2
- package/src/shade/renderer/light/model/Light.d.ts +29 -2
- package/src/shade/renderer/light/model/Light.d.ts.map +1 -1
- package/src/shade/renderer/light/model/Light.js +30 -2
- package/src/shade/renderer/light/model/io/light_record_directional.d.ts +3 -1
- package/src/shade/renderer/light/model/io/light_record_directional.d.ts.map +1 -1
- package/src/shade/renderer/light/model/io/light_record_directional.js +8 -5
- package/src/shade/renderer/light/model/io/light_record_point.d.ts +3 -1
- package/src/shade/renderer/light/model/io/light_record_point.d.ts.map +1 -1
- package/src/shade/renderer/light/model/io/light_record_point.js +5 -2
- package/src/shade/renderer/light/model/io/light_record_spot.d.ts +4 -1
- package/src/shade/renderer/light/model/io/light_record_spot.d.ts.map +1 -1
- package/src/shade/renderer/light/model/io/light_record_spot.js +10 -6
- package/src/shade/renderer/light/place_light.d.ts +16 -0
- package/src/shade/renderer/light/place_light.d.ts.map +1 -0
- package/src/shade/renderer/light/place_light.js +36 -0
- package/src/shade/renderer/light/shader_refresh_light_poses.d.ts +44 -0
- package/src/shade/renderer/light/shader_refresh_light_poses.d.ts.map +1 -0
- package/src/shade/renderer/light/shader_refresh_light_poses.js +154 -0
- package/src/shade/renderer/lightmap/GPULightMap.js +1 -1
- package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.js +12 -14
- package/src/shade/renderer/loader/SceneBundle.d.ts +53 -28
- package/src/shade/renderer/loader/SceneBundle.d.ts.map +1 -1
- package/src/shade/renderer/loader/SceneBundle.js +111 -56
- package/src/shade/renderer/loader/SceneNode.d.ts +81 -0
- package/src/shade/renderer/loader/SceneNode.d.ts.map +1 -0
- package/src/shade/renderer/loader/SceneNode.js +114 -0
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts +3 -2
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +110 -142
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/loader/usd/UsdExtensionRegistry.d.ts +4 -4
- package/src/shade/renderer/loader/usd/UsdExtensionRegistry.d.ts.map +1 -1
- package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +2 -2
- package/src/shade/renderer/loader/usd/load_usd.d.ts +3 -2
- package/src/shade/renderer/loader/usd/load_usd.d.ts.map +1 -1
- package/src/shade/renderer/loader/usd/load_usd.js +4 -5
- package/src/shade/renderer/loader/usd/usd_build_scene.d.ts +3 -3
- package/src/shade/renderer/loader/usd/usd_build_scene.d.ts.map +1 -1
- package/src/shade/renderer/loader/usd/usd_build_scene.js +47 -55
- package/src/shade/renderer/material/GPUMaterialManager.js +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +5 -4
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +8 -15
- package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.d.ts +15 -13
- package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.d.ts.map +1 -1
- package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.js +40 -21
- package/src/shade/renderer/particles/DESIGN.md +53 -50
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +31 -17
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +54 -91
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +12 -11
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +12 -11
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +13 -19
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +19 -25
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +34 -24
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +53 -40
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +1 -1
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.js +2 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_scene_meshes_cull_and_classify_buckets.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_scene_meshes_cull_and_classify_buckets.js +5 -2
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +1 -1
- package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +1 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +1 -1
- package/src/shade/renderer/scene/DynamicMesh.d.ts +13 -4
- package/src/shade/renderer/scene/DynamicMesh.d.ts.map +1 -1
- package/src/shade/renderer/scene/DynamicMesh.js +14 -4
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +38 -18
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +112 -60
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +55 -48
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +594 -928
- package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +5 -5
- package/src/shade/renderer/scene/NODE3D_STRUCT.js +5 -5
- package/src/shade/renderer/scene/Node3D.d.ts +27 -8
- package/src/shade/renderer/scene/Node3D.d.ts.map +1 -1
- package/src/shade/renderer/scene/Node3DDirtyList.d.ts +59 -0
- package/src/shade/renderer/scene/Node3DDirtyList.d.ts.map +1 -0
- package/src/shade/renderer/scene/Scene.d.ts +18 -60
- package/src/shade/renderer/scene/Scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/Scene.js +53 -146
- package/src/shade/renderer/scene/SkinnedMesh.d.ts +10 -93
- package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
- package/src/shade/renderer/scene/bvh/StaticSceneBVH.d.ts +24 -3
- package/src/shade/renderer/scene/bvh/StaticSceneBVH.d.ts.map +1 -1
- package/src/shade/renderer/scene/bvh/StaticSceneBVH.js +53 -13
- package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts +5 -6
- package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts.map +1 -1
- package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.js +5 -6
- package/src/shade/renderer/scene/optimization/scene_optimize.d.ts +10 -2
- package/src/shade/renderer/scene/optimization/scene_optimize.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/scene_optimize.js +266 -230
- package/src/shade/renderer/scene/rows/GPUEntityBoundsReadback.d.ts +53 -0
- package/src/shade/renderer/scene/rows/GPUEntityBoundsReadback.d.ts.map +1 -0
- package/src/shade/renderer/scene/rows/GPUEntityBoundsReadback.js +144 -0
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts +227 -0
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -0
- package/src/shade/renderer/scene/rows/GPUSceneRows.js +1136 -0
- package/src/shade/renderer/scene/rows/place_primitive.d.ts +48 -0
- package/src/shade/renderer/scene/rows/place_primitive.d.ts.map +1 -0
- package/src/shade/renderer/scene/rows/place_primitive.js +98 -0
- package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts +10 -8
- package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/deserialize_scene.js +24 -27
- package/src/shade/renderer/scene/serialization/serialize_scene.d.ts +9 -2
- package/src/shade/renderer/scene/serialization/serialize_scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/serialize_scene.js +25 -4
- package/src/shade/renderer/scene/verify_gpu_tlas_build.js +2 -2
- package/src/shade/renderer/shader/chunk/light/DIRECTIONAL_LIGHT_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/DIRECTIONAL_LIGHT_STRUCT.js +5 -0
- package/src/shade/renderer/shader/chunk/light/POINT_LIGHT_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/POINT_LIGHT_STRUCT.js +5 -0
- package/src/shade/renderer/shader/chunk/light/SPOT_LIGHT_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/SPOT_LIGHT_STRUCT.js +5 -0
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts +39 -0
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +112 -64
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +7 -0
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +1541 -1352
- package/src/shade/renderer/shadow/map/PointLightShadowmap.js +1 -1
- package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +1 -1
- package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +1 -1
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts +29 -0
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.js +43 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASTER_RANGE_MARGIN_FRACTION.d.ts +13 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASTER_RANGE_MARGIN_FRACTION.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASTER_RANGE_MARGIN_FRACTION.js +12 -0
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts +49 -0
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +194 -0
- package/src/shade/renderer/shadow/map/csm_debug_draw_preview.js +1 -1
- package/src/shade/renderer/shadow/map/shadowmap_update_score.js +1 -1
- package/src/shade/renderer/shadow/ray/RTXShadows.js +1 -1
- package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.js +1 -1
- package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.d.ts.map +1 -1
- package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.js +3 -1
- package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.js +8 -8
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +4 -3
- package/src/engine/graphics3/instantiate_scene_bundle.js +0 -164
- package/src/engine/graphics3/shade_node_to_entity_composition.js +0 -88
- package/src/shade/renderer/animation/compute_skin_world_bounds.js +0 -146
- package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +0 -531
- package/src/shade/renderer/animation/pose/pose_evaluate_world.js +0 -76
- package/src/shade/renderer/animation/skin_bind_transform.js +0 -33
- package/src/shade/renderer/animation/skin_frame_defect.js +0 -132
- package/src/shade/renderer/animation/skin_joint_matrix.js +0 -46
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.js +0 -216
- package/src/shade/renderer/scene/InstanceBatch.js +0 -235
- package/src/shade/renderer/scene/Mesh.js +0 -168
- package/src/shade/renderer/scene/Node3D.js +0 -263
- package/src/shade/renderer/scene/SceneNodeBinding.js +0 -56
- package/src/shade/renderer/scene/SkinnedMesh.js +0 -170
- package/src/shade/renderer/scene/TransformAuthority.js +0 -33
|
@@ -2,6 +2,13 @@ import { assert } from "../../../../core/assert.js";
|
|
|
2
2
|
import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
|
|
3
3
|
import { BitSet } from "../../../../core/binary/BitSet.js";
|
|
4
4
|
import { platform_compute_endianness } from "../../../../core/binary/platform_compute_endianness.js";
|
|
5
|
+
import {
|
|
6
|
+
U32_INDEX_MISS,
|
|
7
|
+
u32_index_clear,
|
|
8
|
+
u32_index_count,
|
|
9
|
+
u32_index_get,
|
|
10
|
+
u32_index_set,
|
|
11
|
+
} from "../../../../core/collection/map/u32_index.js";
|
|
5
12
|
import { read_wgsl_type_value } from "../../shader/type/serde/read_gpu_typed_buffer.js";
|
|
6
13
|
import { write_wgsl_type_value } from "../../shader/type/serde/write_gpu_typed_buffer.js";
|
|
7
14
|
import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
@@ -9,11 +16,13 @@ import { GPU_DATABASE_BUFFER_ELEMENT_TYPE } from "./GPU_DATABASE_BUFFER_ELEMENT_
|
|
|
9
16
|
import { GPU_DATABASE_UPLOAD_BUFFER_LIMIT } from "./GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js";
|
|
10
17
|
import { GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS } from "./GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.js";
|
|
11
18
|
import { GPUPageMetadata } from "./GPUPageMetadata.js";
|
|
19
|
+
import { WordViews } from "./WordViews.js";
|
|
12
20
|
|
|
13
21
|
/**
|
|
14
22
|
* @typedef {import("./GPUTypedTableDescriptor.js").GPUTypedTableDescriptor} GPUTypedTableDescriptor
|
|
15
23
|
* @typedef {import("./PageBufferPool.js").PageBufferPool} PageBufferPool
|
|
16
24
|
* @typedef {import("../../shader/type/WebGPUType.js").WebGPUType} WebGPUType
|
|
25
|
+
* @typedef {import("./serde/generate_record_writer.js").GPURecordWriter} GPURecordWriter
|
|
17
26
|
*/
|
|
18
27
|
|
|
19
28
|
const SCRATCH_BUFFER = new BinaryBuffer();
|
|
@@ -27,11 +36,18 @@ const READ_STAGE_BUFFER = BinaryBuffer.fromEndianness(platform_compute_endiannes
|
|
|
27
36
|
// top-level alignment round-up is a no-op.
|
|
28
37
|
const WRITE_STAGE_BUFFER = BinaryBuffer.fromEndianness(platform_compute_endianness());
|
|
29
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Byte size of one {@link GPU_DATABASE_BUFFER_ELEMENT_TYPE} — the width every offset into the
|
|
41
|
+
* database, and into either upload stream, is counted in.
|
|
42
|
+
* @type {number}
|
|
43
|
+
*/
|
|
44
|
+
const WORD_SIZE = GPU_DATABASE_BUFFER_ELEMENT_TYPE.size;
|
|
45
|
+
|
|
30
46
|
/**
|
|
31
47
|
* Byte stride of one entry in {@link GPUTypedTable.partial_upload_descriptors}.
|
|
32
48
|
* @type {number}
|
|
33
49
|
*/
|
|
34
|
-
const PARTIAL_DESCRIPTOR_BYTES = GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS *
|
|
50
|
+
const PARTIAL_DESCRIPTOR_BYTES = GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS * WORD_SIZE;
|
|
35
51
|
|
|
36
52
|
/**
|
|
37
53
|
* Index of the `word_count` word within one partial-upload descriptor — the one word retiring a
|
|
@@ -54,34 +70,6 @@ function empty_upload_buffer() {
|
|
|
54
70
|
return buffer;
|
|
55
71
|
}
|
|
56
72
|
|
|
57
|
-
/**
|
|
58
|
-
* How far {@link GPUTypedTable.set} walks back through
|
|
59
|
-
* {@link GPUTypedTable.element_upload_buffer} looking for an existing
|
|
60
|
-
* record targeting the same element id.
|
|
61
|
-
*
|
|
62
|
-
* Two records aimed at the same row race in the upload compute shader —
|
|
63
|
-
* shader threads run concurrently within a workgroup and the GPU is free
|
|
64
|
-
* to retire them in any order, so "last writer wins" depends on schedule,
|
|
65
|
-
* not buffer order. Scanning back 256 records covers a full 256-thread
|
|
66
|
-
* workgroup of accumulated writes, which is enough to keep duplicates
|
|
67
|
-
* within one dispatch unit out of the buffer.
|
|
68
|
-
*
|
|
69
|
-
* Cross-workgroup duplicates are still possible (a hot row touched once
|
|
70
|
-
* per frame for many frames could in theory have stale entries spaced
|
|
71
|
-
* further apart than this window). For our current usage that doesn't
|
|
72
|
-
* happen in practice; if it ever does, swap this linear scan for a tiny
|
|
73
|
-
* bloom filter or a sparse `id → record_offset` map.
|
|
74
|
-
*
|
|
75
|
-
* The partial-write stream took the map route from the start — see
|
|
76
|
-
* {@link GPUTypedTable#set_bytes} — not because its hazard is worse but
|
|
77
|
-
* because a row can own several ranges at once and every write has to
|
|
78
|
-
* find all of them, so a scan deep enough to be safe would have cost
|
|
79
|
-
* more than the feature saves.
|
|
80
|
-
*
|
|
81
|
-
* @type {number}
|
|
82
|
-
*/
|
|
83
|
-
const UPLOAD_BUFFER_DEDUP_LOOKBACK = 256;
|
|
84
|
-
|
|
85
73
|
/**
|
|
86
74
|
* see "Rendering 'Assassin's Creed Shadows'" GDC 2025 by Nicolas Lopez
|
|
87
75
|
* @template T
|
|
@@ -133,26 +121,80 @@ export class GPUTypedTable {
|
|
|
133
121
|
* Index of the most recent pending descriptor for a row — the head of that row's chain
|
|
134
122
|
* through {@link #partial_descriptor_previous}.
|
|
135
123
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
124
|
+
* A row can own several descriptors at once — that is the point, a caller updating three
|
|
125
|
+
* scattered fields should not pay for the span between them — and every write has to find all
|
|
126
|
+
* of them, which is why this was an index from the start rather than the backwards scan
|
|
127
|
+
* {@link set} used to run. {@link #pending_records} has since gone the same way.
|
|
140
128
|
*
|
|
141
|
-
*
|
|
129
|
+
* A row whose descriptors have all been retired holds `-1` rather than losing its slot, which
|
|
130
|
+
* {@link u32_index_get} reports as {@link U32_INDEX_MISS} — the same thing a row that never
|
|
131
|
+
* took a partial write reads as, and every site below wants them to mean the same thing.
|
|
132
|
+
*
|
|
133
|
+
* `null` for a table that never takes a partial write, which is the only thing {@link set} and
|
|
142
134
|
* {@link remove} have to check.
|
|
143
135
|
*
|
|
144
|
-
* @type {
|
|
136
|
+
* @type {Uint32Array|null}
|
|
145
137
|
*/
|
|
146
|
-
#partial_descriptor_head =
|
|
138
|
+
#partial_descriptor_head = null;
|
|
147
139
|
|
|
148
140
|
/**
|
|
149
141
|
* For descriptor `i`, the index of the previous pending descriptor targeting the same row, or
|
|
150
142
|
* -1 at the end of the chain. Parallel to {@link partial_upload_descriptors}, and kept on this
|
|
151
143
|
* side rather than in a fifth descriptor word so the GPU-side stride stays at four.
|
|
152
144
|
*
|
|
153
|
-
*
|
|
145
|
+
* Never cleared, and does not need to be: descriptor indices restart at zero each time the
|
|
146
|
+
* stream is dispatched, every descriptor writes its own entry as it is appended, and a chain
|
|
147
|
+
* is only ever walked from a head recorded during the frame doing the walking. A stale entry
|
|
148
|
+
* is therefore overwritten before anything can read it.
|
|
149
|
+
*
|
|
150
|
+
* @type {Int32Array}
|
|
154
151
|
*/
|
|
155
|
-
#partial_descriptor_previous =
|
|
152
|
+
#partial_descriptor_previous = new Int32Array(0);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Byte offset in {@link element_upload_buffer} of the whole record pending for a row, for
|
|
156
|
+
* every row that has one.
|
|
157
|
+
*
|
|
158
|
+
* Two records aimed at the same row race in the upload shader — each is consumed by its own
|
|
159
|
+
* thread within a single dispatch and the GPU retires threads in whatever order it likes — so
|
|
160
|
+
* the buffer must hold at most one record per row, and every write has to find the one already
|
|
161
|
+
* there. This was a backwards scan over the last 256 records, which is a window: deep enough
|
|
162
|
+
* to be safe and it costs more than the write it protects, shallow enough to be cheap and a
|
|
163
|
+
* hot row can slip a stale record past it. At a few hundred rows a frame the scan was most of
|
|
164
|
+
* what {@link set} did.
|
|
165
|
+
*
|
|
166
|
+
* An index is both cheaper and exact, and the exactness is worth more. {@link set_bytes} had
|
|
167
|
+
* one interleaving it could only resolve through that window — a range landing on a whole
|
|
168
|
+
* record queued further back than the scan reached — and it is gone: a row is now never live
|
|
169
|
+
* in both streams at once.
|
|
170
|
+
*
|
|
171
|
+
* A `u32_index` rather than a `Map`, because this one is cleared and refilled every frame and
|
|
172
|
+
* `Map.clear` drops its backing store each time. Over a frame of 300 rows that is worth about
|
|
173
|
+
* 9% of {@link set_record} and 19% at 3000, and rather more of the garbage: 105 collections
|
|
174
|
+
* against 43 across the same run. It is invisible in {@link set}, whose cost is the reflective
|
|
175
|
+
* type walk and not the bookkeeping around it.
|
|
176
|
+
*
|
|
177
|
+
* `null` until the table takes its first write.
|
|
178
|
+
*
|
|
179
|
+
* @type {Uint32Array|null}
|
|
180
|
+
*/
|
|
181
|
+
#pending_records = null;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Word views over {@link element_upload_buffer}'s storage, for the generated record writers.
|
|
185
|
+
* Rebuilt by {@link WordViews} itself whenever the buffer grows out from under them.
|
|
186
|
+
*
|
|
187
|
+
* @type {WordViews}
|
|
188
|
+
*/
|
|
189
|
+
#upload_words = new WordViews();
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Word views over {@link partial_upload_data}'s storage, on the same terms as
|
|
193
|
+
* {@link #upload_words}.
|
|
194
|
+
*
|
|
195
|
+
* @type {WordViews}
|
|
196
|
+
*/
|
|
197
|
+
#partial_words = new WordViews();
|
|
156
198
|
|
|
157
199
|
/**
|
|
158
200
|
* When a page is updated - it is marked as dirty.
|
|
@@ -169,6 +211,23 @@ export class GPUTypedTable {
|
|
|
169
211
|
*/
|
|
170
212
|
occupancy = new BitSet();
|
|
171
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Per row, how many times it has been freed — `null` until {@link track_generations}, so a
|
|
216
|
+
* table nothing reads back pays one null check per {@link remove} and no memory.
|
|
217
|
+
*
|
|
218
|
+
* Rows are reused the moment they are freed: {@link add} takes the lowest clear bit, exactly as
|
|
219
|
+
* `EntityComponentDataset.createEntity` does, so a free and an add in one frame leave occupancy
|
|
220
|
+
* as it was and put a stranger where an in-flight readback was aimed. The generation is what
|
|
221
|
+
* tells the two apart, and it lives here rather than with the reader because the table is the
|
|
222
|
+
* thing that reuses rows: every path that frees one moves it, and no caller has to remember to.
|
|
223
|
+
*
|
|
224
|
+
* The plan puts a real generation in the row's flags word once entities are rows; until then
|
|
225
|
+
* this is it.
|
|
226
|
+
*
|
|
227
|
+
* @type {Uint32Array|null}
|
|
228
|
+
*/
|
|
229
|
+
#generations = null;
|
|
230
|
+
|
|
172
231
|
/**
|
|
173
232
|
* Pages whose GPU-side header (live_count + occupancy bitmap) is stale and
|
|
174
233
|
* needs to be re-uploaded. Set when {@link occupancy} or
|
|
@@ -211,6 +270,22 @@ export class GPUTypedTable {
|
|
|
211
270
|
return id;
|
|
212
271
|
}
|
|
213
272
|
|
|
273
|
+
/**
|
|
274
|
+
* {@link add} through a generated writer — {@link set_record} is to {@link set} what this is to
|
|
275
|
+
* {@link add}.
|
|
276
|
+
*
|
|
277
|
+
* @param {T} value
|
|
278
|
+
* @param {GPURecordWriter} writer generated against this table's type
|
|
279
|
+
* @returns {number} element index
|
|
280
|
+
*/
|
|
281
|
+
add_record(value, writer) {
|
|
282
|
+
const id = this.occupancy.nextClearBit(0);
|
|
283
|
+
|
|
284
|
+
this.set_record(id, value, writer);
|
|
285
|
+
|
|
286
|
+
return id;
|
|
287
|
+
}
|
|
288
|
+
|
|
214
289
|
/**
|
|
215
290
|
* Remove an element from the table, freeing its slot.
|
|
216
291
|
* The slot will be zero-filled in the CPU shadow and an upload will be queued.
|
|
@@ -227,6 +302,10 @@ export class GPUTypedTable {
|
|
|
227
302
|
|
|
228
303
|
this.occupancy.set(element_index, false);
|
|
229
304
|
|
|
305
|
+
if (this.#generations !== null) {
|
|
306
|
+
this.#bump_generation(element_index);
|
|
307
|
+
}
|
|
308
|
+
|
|
230
309
|
const page_index = Math.floor(element_index / this.descriptor.elements_per_page);
|
|
231
310
|
const page = this.pages.get(page_index);
|
|
232
311
|
|
|
@@ -251,6 +330,54 @@ export class GPUTypedTable {
|
|
|
251
330
|
}
|
|
252
331
|
}
|
|
253
332
|
|
|
333
|
+
/**
|
|
334
|
+
* Start counting how many times each row is freed. Idempotent. Called by whoever reads rows
|
|
335
|
+
* back and needs to know whether the row it asked about is still the row that answered — see
|
|
336
|
+
* {@link generation_of}.
|
|
337
|
+
*/
|
|
338
|
+
track_generations() {
|
|
339
|
+
if (this.#generations === null) {
|
|
340
|
+
this.#generations = new Uint32Array(Math.max(64, this.element_capacity));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* How many times a row has been freed since {@link track_generations}. Zero for a table that
|
|
346
|
+
* is not tracking, and for a row never freed since.
|
|
347
|
+
*
|
|
348
|
+
* @param {number} row
|
|
349
|
+
* @returns {number}
|
|
350
|
+
*/
|
|
351
|
+
generation_of(row) {
|
|
352
|
+
const generations = this.#generations;
|
|
353
|
+
|
|
354
|
+
if (generations === null || row >= generations.length) {
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return generations[row];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {number} row
|
|
363
|
+
*/
|
|
364
|
+
#bump_generation(row) {
|
|
365
|
+
let generations = this.#generations;
|
|
366
|
+
|
|
367
|
+
if (row >= generations.length) {
|
|
368
|
+
const grown = new Uint32Array(Math.max(generations.length * 2, row + 1));
|
|
369
|
+
|
|
370
|
+
grown.set(generations);
|
|
371
|
+
|
|
372
|
+
generations = grown;
|
|
373
|
+
this.#generations = generations;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// wraps at 2^32, which needs a row to be freed four billion times before a reader of
|
|
377
|
+
// exactly that age could be fooled
|
|
378
|
+
generations[row] = (generations[row] + 1) >>> 0;
|
|
379
|
+
}
|
|
380
|
+
|
|
254
381
|
/**
|
|
255
382
|
* Reset every slot to free in one shot. Cheaper than calling
|
|
256
383
|
* {@link remove} per element when the caller knows it's about to
|
|
@@ -268,6 +395,15 @@ export class GPUTypedTable {
|
|
|
268
395
|
return;
|
|
269
396
|
}
|
|
270
397
|
|
|
398
|
+
if (this.#generations !== null) {
|
|
399
|
+
// every occupied row is being freed, and each one will be reused
|
|
400
|
+
const occupancy = this.occupancy;
|
|
401
|
+
|
|
402
|
+
for (let row = occupancy.nextSetBit(0); row !== -1; row = occupancy.nextSetBit(row + 1)) {
|
|
403
|
+
this.#bump_generation(row);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
271
407
|
this.occupancy.reset();
|
|
272
408
|
|
|
273
409
|
for (const [page_index, page] of this.pages) {
|
|
@@ -279,39 +415,61 @@ export class GPUTypedTable {
|
|
|
279
415
|
}
|
|
280
416
|
|
|
281
417
|
/**
|
|
282
|
-
*
|
|
283
|
-
*
|
|
418
|
+
* Byte offset of the whole record already pending for a row, or -1. One record per id is the
|
|
419
|
+
* invariant that keeps the upload shader race-free — see {@link #pending_records}.
|
|
284
420
|
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
421
|
+
* Also where a stream dispatched since the last write gets noticed. `GPUDatabase` rewinds
|
|
422
|
+
* {@link element_upload_buffer} by moving its position back to zero and tells nobody, so the
|
|
423
|
+
* index can outlive the records it points at. Every mutator consults this before appending —
|
|
424
|
+
* that is the invariant the staleness check rests on, and why it lives here rather than being
|
|
425
|
+
* repeated at each of the appends.
|
|
290
426
|
*
|
|
291
427
|
* @param {number} element_index
|
|
292
|
-
* @returns {number} byte offset of the
|
|
428
|
+
* @returns {number} byte offset of the pending record, or -1
|
|
293
429
|
*/
|
|
294
|
-
#
|
|
295
|
-
const
|
|
430
|
+
#pending_record_offset(element_index) {
|
|
431
|
+
const pending = this.#pending_records;
|
|
296
432
|
|
|
297
|
-
|
|
298
|
-
|
|
433
|
+
if (pending === null || u32_index_count(pending) === 0) {
|
|
434
|
+
return -1;
|
|
435
|
+
}
|
|
299
436
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
);
|
|
437
|
+
if (this.element_upload_buffer.position === 0) {
|
|
438
|
+
// the stream was dispatched and rewound since these were recorded
|
|
439
|
+
u32_index_clear(pending);
|
|
304
440
|
|
|
305
|
-
|
|
306
|
-
const candidate_start = tail - i * step_size;
|
|
307
|
-
upload_buffer.position = candidate_start;
|
|
308
|
-
if (upload_buffer.readUint32() === element_index) {
|
|
309
|
-
return candidate_start;
|
|
310
|
-
}
|
|
441
|
+
return -1;
|
|
311
442
|
}
|
|
312
443
|
|
|
313
|
-
|
|
314
|
-
|
|
444
|
+
return u32_index_get(pending, element_index);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* The page holding a row, ready to be written through: allocated if the table has never
|
|
449
|
+
* reached that far, and given a CPU shadow if it was handed one without.
|
|
450
|
+
*
|
|
451
|
+
* @param {number} element_index
|
|
452
|
+
* @returns {GPUPageMetadata}
|
|
453
|
+
*/
|
|
454
|
+
#page_for_write(element_index) {
|
|
455
|
+
const page_index = Math.floor(element_index / this.descriptor.elements_per_page);
|
|
456
|
+
|
|
457
|
+
let page = this.pages.get(page_index);
|
|
458
|
+
|
|
459
|
+
if (page === undefined) {
|
|
460
|
+
page = this.#allocate_page(page_index);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (page.cpu_data_buffer === undefined) {
|
|
464
|
+
// Materialize CPU buffer on demand (e.g. pre-allocated stub from GPUDatabase)
|
|
465
|
+
const pool = this.page_buffer_pool;
|
|
466
|
+
|
|
467
|
+
page.cpu_data_buffer = pool !== undefined
|
|
468
|
+
? pool.acquire().buffer
|
|
469
|
+
: new ArrayBuffer(this.descriptor.page_byte_size);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return page;
|
|
315
473
|
}
|
|
316
474
|
|
|
317
475
|
/**
|
|
@@ -331,13 +489,16 @@ export class GPUTypedTable {
|
|
|
331
489
|
// must not leave both the old value record and this zero record alive in one
|
|
332
490
|
// dispatch, or the GPU row becomes schedule-dependent.
|
|
333
491
|
const tail = upload_buffer.position;
|
|
334
|
-
const existing_record = this.#
|
|
492
|
+
const existing_record = this.#pending_record_offset(element_index);
|
|
335
493
|
|
|
336
494
|
if (existing_record >= 0) {
|
|
337
|
-
//
|
|
495
|
+
// zero the matched record's value bytes, leaving its id alone
|
|
496
|
+
upload_buffer.position = existing_record + u32.size;
|
|
497
|
+
|
|
338
498
|
for (let i = 0; i < word_count; i++) {
|
|
339
499
|
upload_buffer.writeUint32(0);
|
|
340
500
|
}
|
|
501
|
+
|
|
341
502
|
upload_buffer.position = tail;
|
|
342
503
|
return;
|
|
343
504
|
}
|
|
@@ -346,6 +507,8 @@ export class GPUTypedTable {
|
|
|
346
507
|
for (let i = 0; i < word_count; i++) {
|
|
347
508
|
upload_buffer.writeUint32(0);
|
|
348
509
|
}
|
|
510
|
+
|
|
511
|
+
this.#pending_records = u32_index_set(this.#pending_records, element_index, tail);
|
|
349
512
|
}
|
|
350
513
|
|
|
351
514
|
/**
|
|
@@ -494,29 +657,12 @@ export class GPUTypedTable {
|
|
|
494
657
|
const type = this.descriptor.type;
|
|
495
658
|
|
|
496
659
|
// write to the local page if resident
|
|
497
|
-
const
|
|
498
|
-
let page = this.pages.get(page_index);
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (page === undefined) {
|
|
502
|
-
// allocate page
|
|
503
|
-
page = this.#allocate_page(page_index);
|
|
504
|
-
}
|
|
660
|
+
const page = this.#page_for_write(element_index);
|
|
505
661
|
|
|
506
662
|
// Track occupancy: only increment if slot was previously free
|
|
507
663
|
const was_occupied = this.occupancy.get(element_index);
|
|
508
664
|
this.occupancy.set(element_index, true);
|
|
509
665
|
|
|
510
|
-
if (page.cpu_data_buffer === undefined) {
|
|
511
|
-
// Materialize CPU buffer on demand (e.g. pre-allocated stub from GPUDatabase)
|
|
512
|
-
const pool = this.page_buffer_pool;
|
|
513
|
-
if (pool !== undefined) {
|
|
514
|
-
page.cpu_data_buffer = pool.acquire().buffer;
|
|
515
|
-
} else {
|
|
516
|
-
page.cpu_data_buffer = new ArrayBuffer(this.descriptor.page_byte_size);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
666
|
// page is resident, write to the CPU data
|
|
521
667
|
const record_size = this.descriptor.packed_element_size_bytes;
|
|
522
668
|
SCRATCH_BUFFER.fromArrayBuffer(page.cpu_data_buffer);
|
|
@@ -528,7 +674,7 @@ export class GPUTypedTable {
|
|
|
528
674
|
|
|
529
675
|
if (!was_occupied) {
|
|
530
676
|
page.occupancy_count++;
|
|
531
|
-
this.header_dirty_pages.set(
|
|
677
|
+
this.header_dirty_pages.set(page.index, true);
|
|
532
678
|
}
|
|
533
679
|
|
|
534
680
|
// write to the upload buffer.
|
|
@@ -537,14 +683,14 @@ export class GPUTypedTable {
|
|
|
537
683
|
// the full stride after writing, since write_wgsl_type_value does
|
|
538
684
|
// not pad to type.size when the last field ends earlier.
|
|
539
685
|
//
|
|
540
|
-
// Before appending,
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
//
|
|
546
|
-
//
|
|
547
|
-
//
|
|
686
|
+
// Before appending, ask the index whether this row already has a
|
|
687
|
+
// record queued. If it does, overwrite its value bytes in place and
|
|
688
|
+
// leave the tail where it was — that way the upload shader sees only
|
|
689
|
+
// the latest value per row, which matters because two records
|
|
690
|
+
// targeting the same slot race within a workgroup (last-writer-wins
|
|
691
|
+
// is non-deterministic). The CPU shadow above is already idempotent
|
|
692
|
+
// w.r.t. repeated set() calls, so the GPU side is the only thing that
|
|
693
|
+
// needed the dedupe.
|
|
548
694
|
//
|
|
549
695
|
// A whole record also supersedes any sub-record range queued for this row earlier in the
|
|
550
696
|
// frame — see set_bytes for the ordering rules across the two streams.
|
|
@@ -555,14 +701,15 @@ export class GPUTypedTable {
|
|
|
555
701
|
const step_size = value_offset + record_size;
|
|
556
702
|
const tail = upload_buffer.position;
|
|
557
703
|
|
|
558
|
-
const dedup_record_start = this.#
|
|
704
|
+
const dedup_record_start = this.#pending_record_offset(element_index);
|
|
559
705
|
|
|
560
706
|
if (dedup_record_start >= 0) {
|
|
561
|
-
// Overwrite the existing record's value section
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
|
|
707
|
+
// Overwrite the existing record's value section, then restore the tail so subsequent
|
|
708
|
+
// writes still append at the end.
|
|
709
|
+
upload_buffer.position = dedup_record_start + value_offset;
|
|
710
|
+
|
|
565
711
|
write_wgsl_type_value(value, upload_buffer, type);
|
|
712
|
+
|
|
566
713
|
upload_buffer.position = tail;
|
|
567
714
|
} else {
|
|
568
715
|
// Reserve the whole stride up front. The writes below only reach the last
|
|
@@ -574,9 +721,91 @@ export class GPUTypedTable {
|
|
|
574
721
|
upload_buffer.writeUint32(element_index);
|
|
575
722
|
write_wgsl_type_value(value, upload_buffer, type);
|
|
576
723
|
upload_buffer.position = tail + step_size;
|
|
724
|
+
|
|
725
|
+
this.#pending_records = u32_index_set(this.#pending_records, element_index, tail);
|
|
577
726
|
}
|
|
578
727
|
}
|
|
579
728
|
|
|
729
|
+
/**
|
|
730
|
+
* Write a whole record through a writer generated for its type, instead of walking the type
|
|
731
|
+
* on every call.
|
|
732
|
+
*
|
|
733
|
+
* Same contract as {@link set} in every other respect — the row is created if the table has
|
|
734
|
+
* never held it, the CPU shadow is patched, and the record joins the whole-record upload
|
|
735
|
+
* stream — and the bytes it leaves behind are the same ones. What changes is the cost: the
|
|
736
|
+
* field offsets are constants inside {@link GPURecordWriter.write_record} rather than
|
|
737
|
+
* something rediscovered per field per row, and the shadow and the queued record are written
|
|
738
|
+
* through views rather than a `BinaryBuffer` cursor.
|
|
739
|
+
*
|
|
740
|
+
* A writer naming only some of the type's fields is legal here and means what it says: the
|
|
741
|
+
* rest of the record keeps whatever the shadow held, and so does the queued record. That is
|
|
742
|
+
* rarely what a caller wants from *this* method, though — a partial writer belongs in
|
|
743
|
+
* {@link set_fields}, which uploads only the bytes it wrote.
|
|
744
|
+
*
|
|
745
|
+
* @param {number} element_index
|
|
746
|
+
* @param {T} value
|
|
747
|
+
* @param {GPURecordWriter} writer generated against this table's type
|
|
748
|
+
*/
|
|
749
|
+
set_record(element_index, value, writer) {
|
|
750
|
+
assert.isNonNegativeInteger(element_index, 'element_index');
|
|
751
|
+
assert.defined(writer, 'writer');
|
|
752
|
+
// The message names no type: `WebGPUType.toString` rebuilds the struct's whole WGSL
|
|
753
|
+
// declaration, and an interpolation is evaluated whether the assertion holds or not — which
|
|
754
|
+
// put a code generator on the per-row write path, where a writer exists to take one off.
|
|
755
|
+
assert.equal(writer.type, this.descriptor.type, `writer was generated against a type other than '${this.descriptor.name}''s — its field offsets would land anywhere in a record of this one`);
|
|
756
|
+
|
|
757
|
+
const descriptor = this.descriptor;
|
|
758
|
+
const record_size = descriptor.packed_element_size_bytes;
|
|
759
|
+
|
|
760
|
+
const page = this.#page_for_write(element_index);
|
|
761
|
+
|
|
762
|
+
const was_occupied = this.occupancy.get(element_index);
|
|
763
|
+
this.occupancy.set(element_index, true);
|
|
764
|
+
|
|
765
|
+
const in_page_index = element_index % descriptor.elements_per_page;
|
|
766
|
+
const shadow = page.words.over(page.cpu_data_buffer);
|
|
767
|
+
|
|
768
|
+
writer.write_record(
|
|
769
|
+
shadow.f32,
|
|
770
|
+
shadow.u32,
|
|
771
|
+
(page.cpu_data_address + in_page_index * record_size) / WORD_SIZE,
|
|
772
|
+
value
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
page.cpu_version++;
|
|
776
|
+
|
|
777
|
+
if (!was_occupied) {
|
|
778
|
+
page.occupancy_count++;
|
|
779
|
+
this.header_dirty_pages.set(page.index, true);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// A whole record supersedes any range queued for the row earlier in the frame.
|
|
783
|
+
this.#retire_partial_records(element_index);
|
|
784
|
+
|
|
785
|
+
const upload_buffer = this.element_upload_buffer;
|
|
786
|
+
const step_size = u32.size + record_size;
|
|
787
|
+
const tail = upload_buffer.position;
|
|
788
|
+
|
|
789
|
+
let record_start = this.#pending_record_offset(element_index);
|
|
790
|
+
|
|
791
|
+
if (record_start < 0) {
|
|
792
|
+
// Reserve the whole stride before taking views over the buffer — this is the call
|
|
793
|
+
// that can move the storage they would be over.
|
|
794
|
+
upload_buffer.ensureCapacity(tail + step_size);
|
|
795
|
+
|
|
796
|
+
upload_buffer.writeUint32(element_index);
|
|
797
|
+
upload_buffer.position = tail + step_size;
|
|
798
|
+
|
|
799
|
+
this.#pending_records = u32_index_set(this.#pending_records, element_index, tail);
|
|
800
|
+
|
|
801
|
+
record_start = tail;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const upload = this.#upload_words.over(upload_buffer.data);
|
|
805
|
+
|
|
806
|
+
writer.write_record(upload.f32, upload.u32, (record_start + u32.size) / WORD_SIZE, value);
|
|
807
|
+
}
|
|
808
|
+
|
|
580
809
|
/**
|
|
581
810
|
* Overwrite one field (or sub-struct) of an existing record, leaving the rest of it untouched
|
|
582
811
|
* — on the CPU shadow and on the GPU alike.
|
|
@@ -638,11 +867,8 @@ export class GPUTypedTable {
|
|
|
638
867
|
* the GPU is free to retire threads in any order. In append order:
|
|
639
868
|
*
|
|
640
869
|
* - **partial after whole-record** — the pending whole record is patched in place and nothing
|
|
641
|
-
* is appended, so the row is not in both streams at once.
|
|
642
|
-
* {@link
|
|
643
|
-
* whole record further back than the window is missed and the range is queued beside it —
|
|
644
|
-
* still correct, because {@link GPUDatabase.update} dispatches a table's whole records
|
|
645
|
-
* before its ranges, so the range lands on top of the record exactly as append order said.
|
|
870
|
+
* is appended, so the row is not in both streams at once. The record is found through
|
|
871
|
+
* {@link #pending_records}, so it is found however long ago it was queued.
|
|
646
872
|
* - **whole-record after partial** — {@link set} retires the row's pending descriptors (blanks
|
|
647
873
|
* their word count, making those threads no-ops); the whole record supersedes them.
|
|
648
874
|
* - **partial after partial, the same range or a sub-range of a pending one** — the pending
|
|
@@ -653,8 +879,8 @@ export class GPUTypedTable {
|
|
|
653
879
|
* are retired and one covering their union is appended, its words read back out of the CPU
|
|
654
880
|
* shadow, which is already correct by then.
|
|
655
881
|
*
|
|
656
|
-
* Every
|
|
657
|
-
*
|
|
882
|
+
* Every one of those is exact rather than windowed: both streams index a row's pending
|
|
883
|
+
* entries by id, so they are found however long either stream has grown.
|
|
658
884
|
*
|
|
659
885
|
* @param {number} element_index row to patch; must already be occupied
|
|
660
886
|
* @param {number} byte_offset offset of the range within the record, 4-byte aligned
|
|
@@ -670,10 +896,8 @@ export class GPUTypedTable {
|
|
|
670
896
|
|
|
671
897
|
const descriptor = this.descriptor;
|
|
672
898
|
const record_size = descriptor.packed_element_size_bytes;
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
assert.equal(byte_offset % word_size, 0, `byte_offset (${byte_offset}) must be a multiple of ${word_size} — the database buffer is an array<u32> and a partial write is not masked`);
|
|
676
|
-
assert.equal(byte_length % word_size, 0, `byte_length (${byte_length}) must be a multiple of ${word_size} — the database buffer is an array<u32> and a partial write is not masked`);
|
|
899
|
+
assert.equal(byte_offset % WORD_SIZE, 0, `byte_offset (${byte_offset}) must be a multiple of ${WORD_SIZE} — the database buffer is an array<u32> and a partial write is not masked`);
|
|
900
|
+
assert.equal(byte_length % WORD_SIZE, 0, `byte_length (${byte_length}) must be a multiple of ${WORD_SIZE} — the database buffer is an array<u32> and a partial write is not masked`);
|
|
677
901
|
assert.lessThanOrEqual(byte_offset + byte_length, record_size, `range [${byte_offset}, ${byte_offset + byte_length}) reaches past the end of a ${record_size} byte record of '${descriptor.name}'`);
|
|
678
902
|
|
|
679
903
|
assert.ok(this.occupancy.get(element_index), `element ${element_index} of '${descriptor.name}' is not occupied — a partial write patches a record, it does not create one`);
|
|
@@ -715,10 +939,7 @@ export class GPUTypedTable {
|
|
|
715
939
|
// A pending whole record for this row wins the frame outright: patch its bytes and append
|
|
716
940
|
// nothing, so the row stays in exactly one stream.
|
|
717
941
|
const upload_buffer = this.element_upload_buffer;
|
|
718
|
-
const
|
|
719
|
-
const pending_record = this.#find_upload_record(element_index);
|
|
720
|
-
|
|
721
|
-
upload_buffer.position = upload_tail;
|
|
942
|
+
const pending_record = this.#pending_record_offset(element_index);
|
|
722
943
|
|
|
723
944
|
if (pending_record >= 0) {
|
|
724
945
|
new Uint8Array(upload_buffer.data, pending_record + u32.size + byte_offset, byte_length)
|
|
@@ -727,7 +948,163 @@ export class GPUTypedTable {
|
|
|
727
948
|
return;
|
|
728
949
|
}
|
|
729
950
|
|
|
730
|
-
this.#stage_partial_range(element_index, byte_offset /
|
|
951
|
+
this.#stage_partial_range(element_index, byte_offset / WORD_SIZE, byte_length / WORD_SIZE, record_bytes);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Patch the fields a generated writer owns, on the CPU shadow and on the GPU, and leave the
|
|
956
|
+
* rest of the record alone — {@link set_bytes} for a struct whose owned fields are known at
|
|
957
|
+
* generation time rather than named per call.
|
|
958
|
+
*
|
|
959
|
+
* The point is the bookkeeping, not the stores. `set_bytes` has to treat every range as an
|
|
960
|
+
* event of its own: a page lookup, a record view, a pending-record check, a chain walk, a
|
|
961
|
+
* descriptor append. A caller patching a struct's two CPU-owned blocks through it pays all of
|
|
962
|
+
* that twice, which measured at roughly double one range's cost rather than the few extra
|
|
963
|
+
* words the second range actually is. A writer knows both ranges belong to one row's update
|
|
964
|
+
* before the call starts, so this pays for the row once: the fields reach the shadow and the
|
|
965
|
+
* stream's data blob in a single pass over the value, and the descriptors go out together.
|
|
966
|
+
*
|
|
967
|
+
* The ordering rules across the two streams are {@link set_bytes}'s and are followed exactly
|
|
968
|
+
* — a row's update lands on a pending whole record if there is one, and joins ranges already
|
|
969
|
+
* queued for the row through the same merge, one range at a time, because what to do with an
|
|
970
|
+
* overlap is a property of what is pending and not of the writer.
|
|
971
|
+
*
|
|
972
|
+
* The row must already be occupied, for the reason a partial write always must: it patches a
|
|
973
|
+
* record, it does not create one.
|
|
974
|
+
*
|
|
975
|
+
* @param {number} element_index
|
|
976
|
+
* @param {T} value
|
|
977
|
+
* @param {GPURecordWriter} writer generated against this table's type
|
|
978
|
+
*/
|
|
979
|
+
set_fields(element_index, value, writer) {
|
|
980
|
+
assert.isNonNegativeInteger(element_index, 'element_index');
|
|
981
|
+
assert.defined(writer, 'writer');
|
|
982
|
+
// Naming no type in the message is deliberate — see `set_record`.
|
|
983
|
+
assert.equal(writer.type, this.descriptor.type, `writer was generated against a type other than '${this.descriptor.name}''s — its field offsets would land anywhere in a record of this one`);
|
|
984
|
+
|
|
985
|
+
const descriptor = this.descriptor;
|
|
986
|
+
const record_size = descriptor.packed_element_size_bytes;
|
|
987
|
+
const elements_per_page = descriptor.elements_per_page;
|
|
988
|
+
|
|
989
|
+
const page = this.pages.get(Math.floor(element_index / elements_per_page));
|
|
990
|
+
|
|
991
|
+
assert.ok(this.occupancy.get(element_index), `element ${element_index} of '${descriptor.name}' is not occupied — a partial write patches a record, it does not create one`);
|
|
992
|
+
assert.defined(page, `page of element ${element_index} of '${descriptor.name}'`);
|
|
993
|
+
assert.defined(page.cpu_data_buffer, `page ${page.index} of '${descriptor.name}' has no CPU shadow — a partial write cannot run against an unmapped page`);
|
|
994
|
+
|
|
995
|
+
const record_byte_base = page.cpu_data_address + (element_index % elements_per_page) * record_size;
|
|
996
|
+
const record_word_base = record_byte_base / WORD_SIZE;
|
|
997
|
+
|
|
998
|
+
const shadow = page.words.over(page.cpu_data_buffer);
|
|
999
|
+
|
|
1000
|
+
const pending_record = this.#pending_record_offset(element_index);
|
|
1001
|
+
|
|
1002
|
+
if (pending_record >= 0) {
|
|
1003
|
+
// A whole record for this row is already queued: patch it where it lies, append
|
|
1004
|
+
// nothing, and the row stays in exactly one stream.
|
|
1005
|
+
writer.write_record(shadow.f32, shadow.u32, record_word_base, value);
|
|
1006
|
+
|
|
1007
|
+
const upload = this.#upload_words.over(this.element_upload_buffer.data);
|
|
1008
|
+
|
|
1009
|
+
writer.write_record(upload.f32, upload.u32, (pending_record + u32.size) / WORD_SIZE, value);
|
|
1010
|
+
|
|
1011
|
+
page.cpu_version++;
|
|
1012
|
+
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const ranges = writer.ranges;
|
|
1017
|
+
|
|
1018
|
+
if (this.#partial_descriptor_head !== null && u32_index_get(this.#partial_descriptor_head, element_index) !== U32_INDEX_MISS) {
|
|
1019
|
+
// The row already owns ranges this frame. Whether each of ours patches one in place or
|
|
1020
|
+
// has to absorb it into a union depends on what is pending, not on the writer, so the
|
|
1021
|
+
// ranges go through the general path one at a time.
|
|
1022
|
+
writer.write_record(shadow.f32, shadow.u32, record_word_base, value);
|
|
1023
|
+
|
|
1024
|
+
page.cpu_version++;
|
|
1025
|
+
|
|
1026
|
+
const record_bytes = new Uint8Array(page.cpu_data_buffer, record_byte_base, record_size);
|
|
1027
|
+
|
|
1028
|
+
for (let i = 0; i < ranges.length; i += 2) {
|
|
1029
|
+
this.#stage_partial_range(element_index, ranges[i] / WORD_SIZE, ranges[i + 1] / WORD_SIZE, record_bytes);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const descriptors = this.partial_upload_descriptors;
|
|
1036
|
+
const data = this.partial_upload_data;
|
|
1037
|
+
|
|
1038
|
+
const descriptor_tail = descriptors.position;
|
|
1039
|
+
const data_tail = data.position;
|
|
1040
|
+
const data_byte_count = writer.range_word_count * WORD_SIZE;
|
|
1041
|
+
|
|
1042
|
+
// Both reservations happen before the views are taken: growing is what moves the storage
|
|
1043
|
+
// they would otherwise be over.
|
|
1044
|
+
descriptors.ensureCapacity(descriptor_tail + (ranges.length / 2) * PARTIAL_DESCRIPTOR_BYTES);
|
|
1045
|
+
data.ensureCapacity(data_tail + data_byte_count);
|
|
1046
|
+
|
|
1047
|
+
const blob = this.#partial_words.over(data.data);
|
|
1048
|
+
const data_word_base = data_tail / WORD_SIZE;
|
|
1049
|
+
|
|
1050
|
+
writer.write_record_and_ranges(
|
|
1051
|
+
shadow.f32, shadow.u32, record_word_base,
|
|
1052
|
+
blob.f32, blob.u32, data_word_base,
|
|
1053
|
+
value
|
|
1054
|
+
);
|
|
1055
|
+
|
|
1056
|
+
page.cpu_version++;
|
|
1057
|
+
|
|
1058
|
+
data.position = data_tail + data_byte_count;
|
|
1059
|
+
|
|
1060
|
+
const previous = this.#previous_capacity(
|
|
1061
|
+
descriptor_tail / PARTIAL_DESCRIPTOR_BYTES + ranges.length / 2
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
let head = -1;
|
|
1065
|
+
let range_word = data_word_base;
|
|
1066
|
+
|
|
1067
|
+
descriptors.position = descriptor_tail;
|
|
1068
|
+
|
|
1069
|
+
for (let i = 0; i < ranges.length; i += 2) {
|
|
1070
|
+
const word_count = ranges[i + 1] / WORD_SIZE;
|
|
1071
|
+
|
|
1072
|
+
descriptors.writeUint32(element_index);
|
|
1073
|
+
descriptors.writeUint32(ranges[i] / WORD_SIZE);
|
|
1074
|
+
descriptors.writeUint32(word_count);
|
|
1075
|
+
descriptors.writeUint32(range_word);
|
|
1076
|
+
|
|
1077
|
+
const index = descriptor_tail / PARTIAL_DESCRIPTOR_BYTES + i / 2;
|
|
1078
|
+
|
|
1079
|
+
previous[index] = head;
|
|
1080
|
+
head = index;
|
|
1081
|
+
|
|
1082
|
+
range_word += word_count;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
this.#partial_descriptor_head = u32_index_set(this.#partial_descriptor_head, element_index, head);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* {@link #partial_descriptor_previous}, guaranteed to hold at least `count` entries.
|
|
1090
|
+
*
|
|
1091
|
+
* @param {number} count
|
|
1092
|
+
* @returns {Int32Array}
|
|
1093
|
+
*/
|
|
1094
|
+
#previous_capacity(count) {
|
|
1095
|
+
const previous = this.#partial_descriptor_previous;
|
|
1096
|
+
|
|
1097
|
+
if (count <= previous.length) {
|
|
1098
|
+
return previous;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const grown = new Int32Array(Math.max(64, previous.length * 2, count));
|
|
1102
|
+
|
|
1103
|
+
grown.set(previous);
|
|
1104
|
+
|
|
1105
|
+
this.#partial_descriptor_previous = grown;
|
|
1106
|
+
|
|
1107
|
+
return grown;
|
|
731
1108
|
}
|
|
732
1109
|
|
|
733
1110
|
/**
|
|
@@ -743,16 +1120,17 @@ export class GPUTypedTable {
|
|
|
743
1120
|
const descriptors = this.partial_upload_descriptors;
|
|
744
1121
|
const data = this.partial_upload_data;
|
|
745
1122
|
const previous = this.#partial_descriptor_previous;
|
|
746
|
-
const word_size = GPU_DATABASE_BUFFER_ELEMENT_TYPE.size;
|
|
747
|
-
|
|
748
1123
|
const tail = descriptors.position;
|
|
749
|
-
|
|
1124
|
+
// -1 whether the row never had a chain or had one retired — see #partial_descriptor_head
|
|
1125
|
+
const head = this.#partial_descriptor_head === null
|
|
1126
|
+
? U32_INDEX_MISS
|
|
1127
|
+
: u32_index_get(this.#partial_descriptor_head, element_index);
|
|
750
1128
|
|
|
751
1129
|
let lo = word_offset;
|
|
752
1130
|
let hi = word_offset + word_count;
|
|
753
1131
|
let overlaps = false;
|
|
754
1132
|
|
|
755
|
-
for (let i = head
|
|
1133
|
+
for (let i = head; i >= 0; i = previous[i]) {
|
|
756
1134
|
descriptors.position = i * PARTIAL_DESCRIPTOR_BYTES + u32.size;
|
|
757
1135
|
|
|
758
1136
|
const candidate_lo = descriptors.readUint32();
|
|
@@ -773,8 +1151,8 @@ export class GPUTypedTable {
|
|
|
773
1151
|
|
|
774
1152
|
descriptors.position = tail;
|
|
775
1153
|
|
|
776
|
-
new Uint8Array(data.data, (candidate_data + (lo - candidate_lo)) *
|
|
777
|
-
.set(record_bytes.subarray(lo *
|
|
1154
|
+
new Uint8Array(data.data, (candidate_data + (lo - candidate_lo)) * WORD_SIZE, word_count * WORD_SIZE)
|
|
1155
|
+
.set(record_bytes.subarray(lo * WORD_SIZE, hi * WORD_SIZE));
|
|
778
1156
|
|
|
779
1157
|
return;
|
|
780
1158
|
}
|
|
@@ -791,7 +1169,7 @@ export class GPUTypedTable {
|
|
|
791
1169
|
for (let absorbing = overlaps; absorbing;) {
|
|
792
1170
|
absorbing = false;
|
|
793
1171
|
|
|
794
|
-
for (let i = head
|
|
1172
|
+
for (let i = head; i >= 0; i = previous[i]) {
|
|
795
1173
|
descriptors.position = i * PARTIAL_DESCRIPTOR_BYTES + u32.size;
|
|
796
1174
|
|
|
797
1175
|
const candidate_lo = descriptors.readUint32();
|
|
@@ -820,11 +1198,11 @@ export class GPUTypedTable {
|
|
|
820
1198
|
|
|
821
1199
|
descriptors.position = tail;
|
|
822
1200
|
|
|
823
|
-
const data_offset = data.position /
|
|
1201
|
+
const data_offset = data.position / WORD_SIZE;
|
|
824
1202
|
|
|
825
1203
|
// The words come out of the shadow rather than out of the caller's `source`, so a union
|
|
826
1204
|
// wider than what this call supplied still carries the right values.
|
|
827
|
-
data.writeBytes(record_bytes, lo *
|
|
1205
|
+
data.writeBytes(record_bytes, lo * WORD_SIZE, (hi - lo) * WORD_SIZE);
|
|
828
1206
|
|
|
829
1207
|
descriptors.writeUint32(element_index);
|
|
830
1208
|
descriptors.writeUint32(lo);
|
|
@@ -833,8 +1211,8 @@ export class GPUTypedTable {
|
|
|
833
1211
|
|
|
834
1212
|
const index = tail / PARTIAL_DESCRIPTOR_BYTES;
|
|
835
1213
|
|
|
836
|
-
|
|
837
|
-
this.#partial_descriptor_head
|
|
1214
|
+
this.#previous_capacity(index + 1)[index] = head;
|
|
1215
|
+
this.#partial_descriptor_head = u32_index_set(this.#partial_descriptor_head, element_index, index);
|
|
838
1216
|
}
|
|
839
1217
|
|
|
840
1218
|
/**
|
|
@@ -849,13 +1227,13 @@ export class GPUTypedTable {
|
|
|
849
1227
|
#retire_partial_records(element_index) {
|
|
850
1228
|
const heads = this.#partial_descriptor_head;
|
|
851
1229
|
|
|
852
|
-
if (heads
|
|
1230
|
+
if (heads === null) {
|
|
853
1231
|
return;
|
|
854
1232
|
}
|
|
855
1233
|
|
|
856
|
-
const head = heads
|
|
1234
|
+
const head = u32_index_get(heads, element_index);
|
|
857
1235
|
|
|
858
|
-
if (head ===
|
|
1236
|
+
if (head === U32_INDEX_MISS) {
|
|
859
1237
|
return;
|
|
860
1238
|
}
|
|
861
1239
|
|
|
@@ -870,7 +1248,9 @@ export class GPUTypedTable {
|
|
|
870
1248
|
|
|
871
1249
|
descriptors.position = tail;
|
|
872
1250
|
|
|
873
|
-
|
|
1251
|
+
// retired rather than removed: blanking the slot would cut the probe chains running
|
|
1252
|
+
// through it, and -1 already reads back as absent
|
|
1253
|
+
this.#partial_descriptor_head = u32_index_set(heads, element_index, -1);
|
|
874
1254
|
}
|
|
875
1255
|
|
|
876
1256
|
/**
|
|
@@ -881,8 +1261,11 @@ export class GPUTypedTable {
|
|
|
881
1261
|
this.partial_upload_descriptors.position = 0;
|
|
882
1262
|
this.partial_upload_data.position = 0;
|
|
883
1263
|
|
|
884
|
-
this.#partial_descriptor_head
|
|
885
|
-
|
|
1264
|
+
if (this.#partial_descriptor_head !== null) {
|
|
1265
|
+
u32_index_clear(this.#partial_descriptor_head);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// #partial_descriptor_previous needs no clearing — see its declaration
|
|
886
1269
|
}
|
|
887
1270
|
|
|
888
1271
|
/**
|
|
@@ -940,15 +1323,15 @@ export class GPUTypedTable {
|
|
|
940
1323
|
assert.isNonNegativeInteger(offset, 'offset');
|
|
941
1324
|
|
|
942
1325
|
const occupancy = this.occupancy;
|
|
943
|
-
const capacity = this.element_capacity;
|
|
944
1326
|
|
|
945
1327
|
let written = 0;
|
|
946
1328
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1329
|
+
// Word by word rather than bit by bit: rows can sit a long way above the live ones — an
|
|
1330
|
+
// entity's row is its id, and a band of node rows sits above every entity — and the
|
|
1331
|
+
// stretch between costs a word read per thirty-two rows rather than a read per row.
|
|
1332
|
+
for (let i = occupancy.nextSetBit(0); i !== -1; i = occupancy.nextSetBit(i + 1)) {
|
|
1333
|
+
target[offset + written] = i;
|
|
1334
|
+
written++;
|
|
952
1335
|
}
|
|
953
1336
|
|
|
954
1337
|
assert.equal(written, this.count, 'occupied slot scan disagreed with the table count');
|