@woosh/meep-engine 3.16.0 → 3.17.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/particles/effect/ParticleEffectDocument.d.ts +21 -6
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
- package/editor/particles/effect/ParticleEffectDocument.js +38 -17
- package/editor/view/node-graph/NodeGraphClipboard.d.ts +186 -0
- package/editor/view/node-graph/NodeGraphClipboard.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphClipboard.js +231 -0
- package/editor/view/node-graph/NodeGraphEditorView.d.ts +59 -1
- package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphEditorView.js +248 -5
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.js +84 -8
- package/editor/view/node-graph/actions/connection_bind_actions.d.ts +23 -0
- package/editor/view/node-graph/actions/connection_bind_actions.d.ts.map +1 -0
- package/editor/view/node-graph/actions/connection_bind_actions.js +39 -0
- package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleCodeEditorView.js +4 -3
- package/editor/view/particles/effect/ParticleCurveEditorView.d.ts +20 -0
- package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleCurveEditorView.js +574 -0
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +27 -1
- package/editor/view/particles/effect/ParticleGradientEditorView.d.ts +24 -0
- package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleGradientEditorView.js +472 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +5 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleGraphEditorView.js +61 -31
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleNodeParametersView.js +33 -0
- package/editor/view/particles/effect/particle-editor.css +123 -35
- package/package.json +1 -1
- package/src/core/binary/allocator/OffsetAllocator.d.ts +25 -16
- package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
- package/src/core/binary/allocator/OffsetAllocator.js +10 -1
- package/src/core/collection/array/typed/float32_array_hash.d.ts +21 -0
- package/src/core/collection/array/typed/float32_array_hash.d.ts.map +1 -0
- package/src/core/collection/array/typed/float32_array_hash.js +38 -0
- package/src/core/graph/csr/CSRGraph.d.ts +8 -1
- package/src/core/graph/csr/CSRGraph.d.ts.map +1 -1
- package/src/core/graph/csr/CSRGraph.js +14 -6
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +24 -0
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/LayoutProblem.js +27 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +26 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +28 -0
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +92 -3
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteGrid.js +405 -242
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +50 -5
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteSearch.js +160 -24
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +7 -0
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteSet.js +8 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +9 -3
- package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_build_problem.js +7 -0
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +9 -3
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +17 -3
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +9 -6
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_node_graph.js +49 -7
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +11 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.js +135 -16
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts +29 -0
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_connections.js +716 -309
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +3 -2
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_grid_build.js +7 -15
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +6 -0
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_nudge.js +42 -13
- package/src/engine/animation/AnimationUtils.js +246 -246
- package/src/engine/animation/Animations.js +74 -74
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2078 -2078
- package/src/engine/ecs/EventType.d.ts +0 -1
- package/src/engine/ecs/EventType.js +1 -26
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +1 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.js +3 -7
- package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
- package/src/engine/ecs/parent/EntityNode.js +4 -9
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +360 -364
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts +41 -0
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts.map +1 -0
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.js +40 -0
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +8 -0
- package/src/engine/ecs/transform/t64_announce_change.d.ts +10 -7
- package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_announce_change.js +12 -12
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts +30 -0
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts.map +1 -0
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js +29 -0
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +13 -21
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +4 -8
- package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
- package/src/engine/graphics3/LightSystem.js +4 -8
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem.js +9 -19
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/ParticleEmitterSystem.js +4 -8
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem.js +4 -10
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +1 -1
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +4 -8
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +218 -218
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
- package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/SoundListenerSystem.js +6 -8
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +0 -4
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +5 -11
- package/src/shade/playground/particle_editor/README.md +43 -2
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +4 -5
- package/src/shade/playground/particle_system/particle_scene.js +4 -4
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +13 -1
- package/src/shade/renderer/particles/DESIGN.md +104 -19
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +64 -50
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +176 -78
- package/src/shade/renderer/particles/ParticleConstants.d.ts +59 -23
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +66 -23
- package/src/shade/renderer/particles/ParticleRenderMode.d.ts +19 -0
- package/src/shade/renderer/particles/ParticleRenderMode.d.ts.map +1 -0
- package/src/shade/renderer/particles/ParticleRenderMode.js +33 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +167 -166
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts +37 -0
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/particle_emitter_record.js +109 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +3 -3
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -303
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +18 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +152 -133
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +0 -11
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +720 -621
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +8 -3
- package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/flatten_particle_graph.js +244 -213
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +6 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +294 -319
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +524 -517
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts +22 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.js +517 -0
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +185 -159
- package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/make_particle_group.js +2 -20
- package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts +17 -0
- package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_compare_ops.js +22 -0
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +28 -8
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +49 -25
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +40 -0
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_parameters.js +312 -234
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_presentation.js +59 -3
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts +230 -0
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_ramp.js +403 -0
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +7 -1
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/validate_particle_graph.js +66 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +23 -69
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +105 -262
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +3 -2
- package/src/shade/renderer/particles/graph_particles_billboard.d.ts +37 -0
- package/src/shade/renderer/particles/graph_particles_billboard.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph_particles_billboard.js +93 -0
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +51 -22
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/InstructionStream.js +98 -38
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +49 -8
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleProgram.js +95 -150
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +306 -64
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleVMISA.js +505 -136
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts +36 -12
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/particle_assembly.js +354 -139
- package/src/shade/renderer/particles/isa/validate_particle_program.d.ts +9 -0
- package/src/shade/renderer/particles/isa/validate_particle_program.d.ts.map +1 -0
- package/src/shade/renderer/particles/isa/validate_particle_program.js +128 -0
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +23 -0
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
- package/src/shade/renderer/particles/layout/ParticleLayout.js +68 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts +77 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.js +219 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts +83 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.js +283 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +133 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +334 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts +117 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.js +70 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts +97 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +462 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts +116 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.js +687 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts +57 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +860 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts +115 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.js +264 -0
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +81 -42
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +477 -478
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts +100 -0
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.js +236 -0
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +139 -59
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +764 -647
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +40 -7
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +376 -213
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +30 -3
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +337 -223
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +24 -38
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +166 -206
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts +99 -0
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.js +313 -0
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +3 -2
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +198 -197
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +19 -0
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +678 -561
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts +22 -0
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.js +221 -0
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +63 -72
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +87 -86
- package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_peephole.js +392 -271
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +361 -268
- package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_schedule.js +244 -0
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +154 -151
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts +20 -0
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -1
- package/src/shade/renderer/particles/particle_test_fixtures.js +229 -203
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +99 -25
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +438 -286
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +109 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +127 -0
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +87 -102
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +184 -163
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +26 -0
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts +130 -0
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ProgramHeap.js +595 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts +91 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.js +106 -0
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +5 -2
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +22 -19
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts +28 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.js +136 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts +35 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.js +133 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -9
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +20 -80
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts +33 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +114 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts +8 -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 +33 -101
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts +45 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +123 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts +51 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts.map +1 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.js +120 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts +6 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.js +16 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +4 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +261 -140
- package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +217 -255
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts +43 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.js +55 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts +62 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.js +86 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.js +32 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts +9 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.js +19 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/README.md +65 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts +24 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.js +83 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts +25 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.js +42 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts +31 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.js +80 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts +26 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.js +109 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts +38 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.js +129 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts +21 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.js +35 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts +19 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.js +28 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.js +22 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.js +59 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.js +74 -0
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts +34 -1
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +41 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts +3 -2
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +49 -10
- package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapCameraView.js +4 -9
- package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts +0 -15
- package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts +0 -45
- package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts +0 -24
- package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts +0 -19
- package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts.map +0 -1
- package/src/engine/ecs/transform/__probe.d.ts +0 -4
- package/src/engine/ecs/transform/__probe.d.ts.map +0 -1
- package/src/engine/ecs/util/hideEntityGracefully.d.ts +0 -9
- package/src/engine/ecs/util/hideEntityGracefully.d.ts.map +0 -1
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +0 -27
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +0 -1
- package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts +0 -14
- package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +0 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts +0 -156
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/Emitter.d.ts +0 -219
- package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +0 -77
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/ProgramArena.js +0 -155
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +0 -15
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts +0 -34
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +0 -32
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts.map +0 -1
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import { assert } from "../../../../../core/assert.js";
|
|
2
|
+
import { InstructionStream } from "../../isa/InstructionStream.js";
|
|
3
|
+
import { VM_MAX_WIDTH, VM_OP, pack_swizzle, vm_immediate, vm_register } from "../../isa/ParticleVMISA.js";
|
|
4
|
+
import { PV_GROUP } from "./particle_vector_groups.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Give the groups an order and a register file, and write the instructions.
|
|
8
|
+
*
|
|
9
|
+
* By the time this runs there is nothing left to decide about WHAT the program computes — only when
|
|
10
|
+
* each instruction runs and which slots it uses. Those are the two halves of the register-count
|
|
11
|
+
* problem, and the notes' pipeline is exactly this: schedule for the fewest values alive at once,
|
|
12
|
+
* then colour, which for straight-line code is an interval graph and so optimal for a fixed order.
|
|
13
|
+
*
|
|
14
|
+
* ## Ordering
|
|
15
|
+
*
|
|
16
|
+
* The value graph left almost nothing to obey. There are no anti-dependencies at all — a value is
|
|
17
|
+
* not a register until this module makes it one, so the write-after-read and write-after-write
|
|
18
|
+
* hazards that constrain `../pass_schedule.js` do not exist here. What remains is the true data
|
|
19
|
+
* dependencies and ONE chain: the RNG draws come off a sequence, so the `RANDOM` groups keep their
|
|
20
|
+
* order among themselves. Stores and kills are free to go anywhere after the values they read, which
|
|
21
|
+
* is the practical difference between the two optimizers' schedules — over there a store is a
|
|
22
|
+
* barrier nothing crosses.
|
|
23
|
+
*
|
|
24
|
+
* The order is chosen by the same backward greedy `../pass_schedule.js` uses: place last the
|
|
25
|
+
* instruction that leaves fewest slots alive above it. It is a greedy, and a greedy can be fooled;
|
|
26
|
+
* an exact minimum-register schedule is a constraint-programming problem, which is where the notes
|
|
27
|
+
* point next and where a solver would be needed.
|
|
28
|
+
*
|
|
29
|
+
* ## Colouring
|
|
30
|
+
*
|
|
31
|
+
* A group's run is live from where it is written to the last instruction that reads it, so the live
|
|
32
|
+
* ranges are intervals and the lowest-base first fit is the classic optimal left-edge colouring —
|
|
33
|
+
* for SCALARS. A run of two or more is a shape rather than a point and first fit is then a
|
|
34
|
+
* heuristic; the same caveat `../pass_allocate_registers.js` carries, and the reason it is worth
|
|
35
|
+
* running that pass over the result afterwards.
|
|
36
|
+
*
|
|
37
|
+
* One exception to "sources are read before the destination is written", which is otherwise true of
|
|
38
|
+
* every instruction in this ISA and is what lets a destination reuse a slot that dies at the same
|
|
39
|
+
* instruction: a GATHER is several `MOV`s, and the first of them would clobber a slot a later one
|
|
40
|
+
* still has to read. So a gather's run is allocated before its operands are released and never
|
|
41
|
+
* overlaps them.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {object} ParticleLoweredSection
|
|
46
|
+
* @property {InstructionStream} stream
|
|
47
|
+
* @property {number} peak the most slots alive at once, which is what the file has to hold
|
|
48
|
+
* @property {number[]} order the group indices, in the order they run
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {import("./particle_vector_groups.js").ParticleValuePacking} packing
|
|
53
|
+
* @param {object} params
|
|
54
|
+
* @param {number} params.register_slots
|
|
55
|
+
* @param {function(number[]): number} params.intern_constant adds a run to the shared pool and
|
|
56
|
+
* returns its first word
|
|
57
|
+
* @returns {ParticleLoweredSection|null} `null` when the program does not fit the register file
|
|
58
|
+
*/
|
|
59
|
+
export function lower_particle_values(packing, { register_slots, intern_constant }) {
|
|
60
|
+
const order = schedule_groups(packing.groups);
|
|
61
|
+
const bases = allocate_groups(packing.groups, order, register_slots, packing.coalitions);
|
|
62
|
+
|
|
63
|
+
if (bases === null) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const stream = new InstructionStream();
|
|
68
|
+
|
|
69
|
+
for (const index of order) {
|
|
70
|
+
emit_group(stream, packing.groups[index], bases, intern_constant);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { stream, peak: stream.register_count, order };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Per group, the groups it must come after.
|
|
78
|
+
*
|
|
79
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
80
|
+
* @returns {Set<number>[]}
|
|
81
|
+
*/
|
|
82
|
+
export function build_group_dependencies(groups) {
|
|
83
|
+
const predecessors = groups.map(() => new Set());
|
|
84
|
+
const loads = groups.filter(group => group.op === VM_OP.LOAD_ATTR);
|
|
85
|
+
|
|
86
|
+
for (const group of groups) {
|
|
87
|
+
for (const operand of group.operands) {
|
|
88
|
+
for (const read of operand.reads) {
|
|
89
|
+
predecessors[group.index].add(read.group);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (const source of group.gather) {
|
|
94
|
+
if (source.group >= 0) {
|
|
95
|
+
predecessors[group.index].add(source.group);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The one ordering the value graph did not already discharge. Lifting forwarded every load of
|
|
100
|
+
// a word that had been stored, so what is left is loads of the word AS THE SECTION FOUND IT —
|
|
101
|
+
// and those are only true until something stores over that word. The value is a fact about
|
|
102
|
+
// the record before the section ran; the INSTRUCTION that fetches it is not.
|
|
103
|
+
if (group.op === VM_OP.STORE_ATTR) {
|
|
104
|
+
for (const load of loads) {
|
|
105
|
+
if (words_overlap(load.raw[0], load.raw[1], group.raw[1], group.raw[2])) {
|
|
106
|
+
predecessors[group.index].add(load.index);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The draws are a sequence: the state after `n` of them is what the section is compared on, so
|
|
113
|
+
// they may move anywhere as a block but never past one another. Which one comes first is what
|
|
114
|
+
// `draw` says and not where the group happens to sit in the list.
|
|
115
|
+
const draws = groups.filter(group => group.op === VM_OP.RANDOM).sort((a, b) => a.draw - b.draw);
|
|
116
|
+
|
|
117
|
+
for (let i = 1; i < draws.length; i++) {
|
|
118
|
+
predecessors[draws[i].index].add(draws[i - 1].index);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return predecessors;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {number} base_a
|
|
126
|
+
* @param {number} span_a
|
|
127
|
+
* @param {number} base_b
|
|
128
|
+
* @param {number} span_b
|
|
129
|
+
* @returns {boolean} whether the two record-word ranges share a word
|
|
130
|
+
*/
|
|
131
|
+
function words_overlap(base_a, span_a, base_b, span_b) {
|
|
132
|
+
return base_a < base_b + span_b && base_b < base_a + span_a;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The order the groups run in: whichever of two candidate schedules holds fewer slots at once.
|
|
137
|
+
*
|
|
138
|
+
* The first is the section's own order, rebuilt — a forward topological walk that always takes the
|
|
139
|
+
* ready group whose work came earliest. It is not clever and it is not meant to be; it is the
|
|
140
|
+
* schedule the program arrived with, which is a schedule somebody's frontend already thought about,
|
|
141
|
+
* and it is what stops this stage from ever being the reason a program stopped fitting.
|
|
142
|
+
*
|
|
143
|
+
* The second is the backward greedy `../pass_schedule.js` uses: place last the group that leaves
|
|
144
|
+
* fewest slots alive above it. It wins on programs whose values were computed a long way from where
|
|
145
|
+
* they are used — which is most hand-lowered code — and loses on programs made of many independent
|
|
146
|
+
* chains, where the liveness measure is flat and it interleaves twelve noise samples that should
|
|
147
|
+
* have been done one at a time.
|
|
148
|
+
*
|
|
149
|
+
* Neither is the minimum. An exact minimum-register schedule of a DAG is NP-complete and the answer
|
|
150
|
+
* at this size is a constraint solver, which is the next thing the notes point at; measuring two
|
|
151
|
+
* cheap orders and keeping the better is what is here instead, and it is at least never worse than
|
|
152
|
+
* the program's own.
|
|
153
|
+
*
|
|
154
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
155
|
+
* @returns {number[]} group indices, in the order they run
|
|
156
|
+
*/
|
|
157
|
+
export function schedule_groups(groups) {
|
|
158
|
+
const predecessors = build_group_dependencies(groups);
|
|
159
|
+
const sequence = groups.map(group => group.sequence);
|
|
160
|
+
const original = schedule_in_priority_order(groups, predecessors, sequence);
|
|
161
|
+
// Three orders, and each of them wins on programs the other two lose on. Over 1,600 generated
|
|
162
|
+
// programs the section's own order won 1,376, the greedy 214 and the sink 10 — the greedy is
|
|
163
|
+
// worth its quadratic ready set on the small ones and hopeless on the large, where it interleaves
|
|
164
|
+
// every independent chain there is; the sink is worth having for the shapes where a value was
|
|
165
|
+
// computed a long way from where it is read. Iterating the sink over POSITIONS rather than the
|
|
166
|
+
// section's numbering was tried and won nothing at all, on any program measured.
|
|
167
|
+
const candidates = [
|
|
168
|
+
original,
|
|
169
|
+
schedule_in_priority_order(
|
|
170
|
+
groups,
|
|
171
|
+
predecessors,
|
|
172
|
+
as_late_as_possible(groups, predecessors, original, sequence),
|
|
173
|
+
sequence
|
|
174
|
+
),
|
|
175
|
+
schedule_by_liveness(groups, predecessors, sequence),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
let best = candidates[0];
|
|
179
|
+
let best_peak = peak_slots(groups, best);
|
|
180
|
+
|
|
181
|
+
for (const candidate of candidates.slice(1)) {
|
|
182
|
+
const peak = peak_slots(groups, candidate);
|
|
183
|
+
|
|
184
|
+
if (peak < best_peak) {
|
|
185
|
+
best = candidate;
|
|
186
|
+
best_peak = peak;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return best;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The latest place in the section's own order each group could have gone: just before whatever reads
|
|
195
|
+
* it first.
|
|
196
|
+
*
|
|
197
|
+
* A group that is placed where it was WRITTEN holds its run from there until its reader; a group
|
|
198
|
+
* placed just before its reader holds it for an instruction. That is the whole of the difference
|
|
199
|
+
* between a peak of a hundred and a peak of thirty on a program made of a dozen noise samples, where
|
|
200
|
+
* grouping lanes into vectors has pulled a value's readers away from where the value was computed.
|
|
201
|
+
*
|
|
202
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
203
|
+
* @param {Set<number>[]} predecessors
|
|
204
|
+
* @param {number[]} order any valid order, walked backwards so a group is pulled down only once
|
|
205
|
+
* every reader of it already has been
|
|
206
|
+
* @param {number[]} base per group, the time it would run at if nothing pulled it
|
|
207
|
+
* @returns {number[]} per group, its priority — smaller runs earlier
|
|
208
|
+
*/
|
|
209
|
+
function as_late_as_possible(groups, predecessors, order, base) {
|
|
210
|
+
// A group with no reader has nowhere later to go, so it stays where it was; everything else is
|
|
211
|
+
// pulled down to just above the first thing that reads it.
|
|
212
|
+
const latest = base.slice();
|
|
213
|
+
|
|
214
|
+
for (let i = order.length - 1; i >= 0; i--) {
|
|
215
|
+
for (const predecessor of predecessors[order[i]]) {
|
|
216
|
+
latest[predecessor] = Math.min(latest[predecessor], latest[order[i]]);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return latest;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* A forward topological walk that always takes the ready group with the smallest priority.
|
|
225
|
+
*
|
|
226
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
227
|
+
* @param {Set<number>[]} predecessors
|
|
228
|
+
* @param {number[]} priority per group
|
|
229
|
+
* @param {number[]} [tiebreak] per group, consulted where the priority is equal — which it very often
|
|
230
|
+
* is once groups have been pulled down onto the same reader
|
|
231
|
+
* @returns {number[]}
|
|
232
|
+
*/
|
|
233
|
+
function schedule_in_priority_order(groups, predecessors, priority, tiebreak = priority) {
|
|
234
|
+
const pending = predecessors.map(set => set.size);
|
|
235
|
+
/** @type {Set<number>} */
|
|
236
|
+
const ready = new Set();
|
|
237
|
+
/** @type {number[][]} per group, what becomes ready once it is placed */
|
|
238
|
+
const successors = groups.map(() => []);
|
|
239
|
+
|
|
240
|
+
for (let i = 0; i < groups.length; i++) {
|
|
241
|
+
for (const predecessor of predecessors[i]) {
|
|
242
|
+
successors[predecessor].push(i);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (pending[i] === 0) {
|
|
246
|
+
ready.add(i);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** @type {number[]} */
|
|
251
|
+
const order = [];
|
|
252
|
+
|
|
253
|
+
while (order.length < groups.length) {
|
|
254
|
+
let best = -1;
|
|
255
|
+
|
|
256
|
+
for (const candidate of ready) {
|
|
257
|
+
if (best < 0 || earlier(priority, tiebreak, candidate, best)) {
|
|
258
|
+
best = candidate;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
assert.ok(best >= 0, "particle value lowering: the group graph has a cycle");
|
|
263
|
+
|
|
264
|
+
order.push(best);
|
|
265
|
+
ready.delete(best);
|
|
266
|
+
|
|
267
|
+
for (const successor of successors[best]) {
|
|
268
|
+
if (--pending[successor] === 0) {
|
|
269
|
+
ready.add(successor);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return order;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {number[]} priority
|
|
279
|
+
* @param {number[]} tiebreak
|
|
280
|
+
* @param {number} candidate
|
|
281
|
+
* @param {number} best
|
|
282
|
+
* @returns {boolean}
|
|
283
|
+
*/
|
|
284
|
+
function earlier(priority, tiebreak, candidate, best) {
|
|
285
|
+
if (priority[candidate] !== priority[best]) {
|
|
286
|
+
return priority[candidate] < priority[best];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return tiebreak[candidate] !== tiebreak[best] ? tiebreak[candidate] < tiebreak[best] : candidate < best;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The most slots an order holds at once — what the register file has to be.
|
|
294
|
+
*
|
|
295
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
296
|
+
* @param {number[]} order
|
|
297
|
+
* @returns {number}
|
|
298
|
+
*/
|
|
299
|
+
export function peak_slots(groups, order) {
|
|
300
|
+
const position = new Int32Array(groups.length).fill(-1);
|
|
301
|
+
|
|
302
|
+
for (let i = 0; i < order.length; i++) {
|
|
303
|
+
position[order[i]] = i;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const last_use = new Int32Array(groups.length).fill(-1);
|
|
307
|
+
|
|
308
|
+
for (const group of groups) {
|
|
309
|
+
for (const source of sources_of(group)) {
|
|
310
|
+
last_use[source] = Math.max(last_use[source], position[group.index]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** @type {number[][]} */
|
|
315
|
+
const dying = order.map(() => []);
|
|
316
|
+
|
|
317
|
+
let live = 0;
|
|
318
|
+
let peak = 0;
|
|
319
|
+
|
|
320
|
+
for (let i = 0; i < order.length; i++) {
|
|
321
|
+
const group = groups[order[i]];
|
|
322
|
+
|
|
323
|
+
live += group.span;
|
|
324
|
+
peak = Math.max(peak, live);
|
|
325
|
+
dying[Math.max(i, last_use[group.index])].push(group.span);
|
|
326
|
+
|
|
327
|
+
for (const span of dying[i]) {
|
|
328
|
+
live -= span;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return peak;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Backwards, one group at a time, from those whose consumers are all already placed: the one that
|
|
337
|
+
* leaves fewest slots alive above it.
|
|
338
|
+
*
|
|
339
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
340
|
+
* @param {Set<number>[]} predecessors
|
|
341
|
+
* @param {number[]} priority per group; ties go to the one that should run later
|
|
342
|
+
* @returns {number[]} group indices, in the order they run
|
|
343
|
+
*/
|
|
344
|
+
function schedule_by_liveness(groups, predecessors, priority) {
|
|
345
|
+
const pending = new Int32Array(groups.length);
|
|
346
|
+
|
|
347
|
+
for (const set of predecessors) {
|
|
348
|
+
for (const predecessor of set) {
|
|
349
|
+
pending[predecessor]++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** @type {Set<number>} groups every consumer of which is placed */
|
|
354
|
+
const ready = new Set();
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < groups.length; i++) {
|
|
357
|
+
if (pending[i] === 0) {
|
|
358
|
+
ready.add(i);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** @type {Set<number>} groups whose runs are alive just above the ones placed so far */
|
|
363
|
+
const live = new Set();
|
|
364
|
+
/** @type {number[]} */
|
|
365
|
+
const reversed = [];
|
|
366
|
+
|
|
367
|
+
while (reversed.length < groups.length) {
|
|
368
|
+
let best = -1;
|
|
369
|
+
let best_live = Infinity;
|
|
370
|
+
|
|
371
|
+
for (const candidate of ready) {
|
|
372
|
+
const alive = live_above(groups, candidate, live);
|
|
373
|
+
|
|
374
|
+
// Ties go to the group whose work came LATEST in the section as it was written, so that
|
|
375
|
+
// a stretch this measure is indifferent about comes back in the order it arrived.
|
|
376
|
+
if (alive < best_live || (alive === best_live && later(priority, candidate, best))) {
|
|
377
|
+
best = candidate;
|
|
378
|
+
best_live = alive;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
assert.ok(best >= 0, "particle value lowering: the group graph has a cycle");
|
|
383
|
+
|
|
384
|
+
live.delete(best);
|
|
385
|
+
|
|
386
|
+
for (const source of sources_of(groups[best])) {
|
|
387
|
+
live.add(source);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
reversed.push(best);
|
|
391
|
+
ready.delete(best);
|
|
392
|
+
|
|
393
|
+
for (const predecessor of predecessors[best]) {
|
|
394
|
+
if (--pending[predecessor] === 0) {
|
|
395
|
+
ready.add(predecessor);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return reversed.reverse();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @param {number[]} priority
|
|
405
|
+
* @param {number} candidate
|
|
406
|
+
* @param {number} best the best so far, or `-1`
|
|
407
|
+
* @returns {boolean}
|
|
408
|
+
*/
|
|
409
|
+
function later(priority, candidate, best) {
|
|
410
|
+
if (best < 0) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return priority[candidate] !== priority[best] ? priority[candidate] > priority[best] : candidate > best;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup} group
|
|
419
|
+
* @returns {number[]} the groups it reads, with repeats
|
|
420
|
+
*/
|
|
421
|
+
function sources_of(group) {
|
|
422
|
+
const sources = [];
|
|
423
|
+
|
|
424
|
+
for (const operand of group.operands) {
|
|
425
|
+
for (const read of operand.reads) {
|
|
426
|
+
sources.push(read.group);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
for (const source of group.gather) {
|
|
431
|
+
if (source.group >= 0) {
|
|
432
|
+
sources.push(source.group);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return sources;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
441
|
+
* @param {number} candidate
|
|
442
|
+
* @param {Set<number>} below the groups alive just under it
|
|
443
|
+
* @returns {number} slots alive just above it, were it placed here
|
|
444
|
+
*/
|
|
445
|
+
function live_above(groups, candidate, below) {
|
|
446
|
+
const above = new Set(below);
|
|
447
|
+
|
|
448
|
+
above.delete(candidate);
|
|
449
|
+
|
|
450
|
+
for (const source of sources_of(groups[candidate])) {
|
|
451
|
+
above.add(source);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
let slots = 0;
|
|
455
|
+
|
|
456
|
+
for (const group of above) {
|
|
457
|
+
slots += groups[group].span;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return slots;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Give each group's run the lowest base that is free for as long as it lives.
|
|
465
|
+
*
|
|
466
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
467
|
+
* @param {number[]} order
|
|
468
|
+
* @param {number} register_slots
|
|
469
|
+
* @param {number[][]} [coalitions] runs that have to end up end to end, in that order
|
|
470
|
+
* @returns {Int32Array|null} per group, the slot its run starts at; `null` when the file is too small
|
|
471
|
+
*/
|
|
472
|
+
export function allocate_groups(groups, order, register_slots, coalitions = []) {
|
|
473
|
+
const position = positions_of(groups, order);
|
|
474
|
+
const { last, gathered } = lane_uses(groups, position);
|
|
475
|
+
const bases = new Int32Array(groups.length).fill(-1);
|
|
476
|
+
const free = new Uint8Array(register_slots).fill(1);
|
|
477
|
+
/** @type {number[][]} per position, the slots that come free once it has run */
|
|
478
|
+
const release = order.map(() => []);
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Hand each slot of a run back at its own time.
|
|
482
|
+
*
|
|
483
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup} group
|
|
484
|
+
* @param {number} base
|
|
485
|
+
*/
|
|
486
|
+
const release_lanes = (group, base) => {
|
|
487
|
+
for (let lane = 0; lane < group.span; lane++) {
|
|
488
|
+
const key = `${group.index}:${lane}`;
|
|
489
|
+
// A slot is free where its last reader runs, since an instruction reads every source
|
|
490
|
+
// before it writes — so the reader may have it. One nothing reads comes free one position
|
|
491
|
+
// later, this being where it is written; and so does one a GATHER reads, since a gather
|
|
492
|
+
// fills its run one `MOV` at a time and must not clobber a lane it has yet to read.
|
|
493
|
+
const end = Math.max(
|
|
494
|
+
position[group.index] + 1,
|
|
495
|
+
(last.get(key) ?? -1) + (gathered.has(key) ? 1 : 0)
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
if (end < order.length) {
|
|
499
|
+
release[end].push(base + lane);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
for (let i = 0; i < order.length; i++) {
|
|
505
|
+
const group = groups[order[i]];
|
|
506
|
+
|
|
507
|
+
for (const slot of release[i]) {
|
|
508
|
+
free[slot] = 1;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// A coalition member gets its slot when the coalition is claimed, which is before it runs.
|
|
512
|
+
if (group.span === 0 || bases[group.index] >= 0) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// A coalition is claimed whole, by whichever of its runs is written first: they have to end
|
|
517
|
+
// up adjacent, and the only moment that can be arranged is before any of them has a slot.
|
|
518
|
+
const members = group.coalition >= 0 ? coalitions[group.coalition] : [group.index];
|
|
519
|
+
const width = members.reduce((slots, member) => slots + groups[member].span, 0);
|
|
520
|
+
const base = first_fit(free, width);
|
|
521
|
+
|
|
522
|
+
if (base < 0) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
free.fill(0, base, base + width);
|
|
527
|
+
|
|
528
|
+
let at = base;
|
|
529
|
+
|
|
530
|
+
for (const member of members) {
|
|
531
|
+
bases[member] = at;
|
|
532
|
+
release_lanes(groups[member], at);
|
|
533
|
+
at += groups[member].span;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return bases;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Per SLOT of each group's run, the last position that reads it — and which of those slots a gather
|
|
542
|
+
* reads.
|
|
543
|
+
*
|
|
544
|
+
* Per slot and not per run, which is the whole point: a `RANDOM.4` whose fourth draw nothing wants,
|
|
545
|
+
* a builtin loaded three lanes wide for its third, a `CROSS` two lanes of which are read — all of
|
|
546
|
+
* those hold slots that died the moment they were written, and freeing the run as a unit would keep
|
|
547
|
+
* them to the end of the last lane anybody wanted.
|
|
548
|
+
*
|
|
549
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
550
|
+
* @param {number[]} position
|
|
551
|
+
* @returns {{last: Map<string, number>, gathered: Set<string>}} keyed `group:lane`
|
|
552
|
+
*/
|
|
553
|
+
function lane_uses(groups, position) {
|
|
554
|
+
/** @type {Map<string, number>} */
|
|
555
|
+
const last = new Map();
|
|
556
|
+
/** @type {Set<string>} */
|
|
557
|
+
const gathered = new Set();
|
|
558
|
+
|
|
559
|
+
const touch = (group, lane, at) => {
|
|
560
|
+
const key = `${group}:${lane}`;
|
|
561
|
+
|
|
562
|
+
last.set(key, Math.max(last.get(key) ?? -1, at));
|
|
563
|
+
|
|
564
|
+
return key;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
for (const group of groups) {
|
|
568
|
+
for (const operand of group.operands) {
|
|
569
|
+
// `reads` and not `lanes`: a coalition operand's lanes are slots of the whole coalition,
|
|
570
|
+
// and what is being kept alive is a lane of one of the runs inside it.
|
|
571
|
+
for (const read of operand.reads) {
|
|
572
|
+
touch(read.group, read.lane, position[group.index]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
for (const source of group.gather) {
|
|
577
|
+
if (source.group >= 0) {
|
|
578
|
+
gathered.add(touch(source.group, source.lane, position[group.index]));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return { last, gathered };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
588
|
+
* @param {number[]} order
|
|
589
|
+
* @returns {number[]} per group, where the order put it
|
|
590
|
+
*/
|
|
591
|
+
function positions_of(groups, order) {
|
|
592
|
+
const positions = new Array(groups.length).fill(-1);
|
|
593
|
+
|
|
594
|
+
for (let i = 0; i < order.length; i++) {
|
|
595
|
+
positions[order[i]] = i;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return positions;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* @param {Uint8Array} free
|
|
603
|
+
* @param {number} span
|
|
604
|
+
* @returns {number} the lowest base with `span` free slots above it, or `-1`
|
|
605
|
+
*/
|
|
606
|
+
function first_fit(free, span) {
|
|
607
|
+
let run = 0;
|
|
608
|
+
|
|
609
|
+
for (let slot = 0; slot < free.length; slot++) {
|
|
610
|
+
run = free[slot] === 1 ? run + 1 : 0;
|
|
611
|
+
|
|
612
|
+
if (run === span) {
|
|
613
|
+
return slot - span + 1;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return -1;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* @param {InstructionStream} stream
|
|
622
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup} group
|
|
623
|
+
* @param {Int32Array} bases
|
|
624
|
+
* @param {function(number[]): number} intern_constant
|
|
625
|
+
*/
|
|
626
|
+
|
|
627
|
+
function emit_group(stream, group, bases, intern_constant) {
|
|
628
|
+
const base = bases[group.index];
|
|
629
|
+
|
|
630
|
+
switch (group.kind) {
|
|
631
|
+
case PV_GROUP.GATHER:
|
|
632
|
+
for (let lane = 0; lane < group.gather.length; lane++) {
|
|
633
|
+
const source = group.gather[lane];
|
|
634
|
+
const operand = source.group >= 0
|
|
635
|
+
? vm_register(bases[source.group] + source.lane, pack_swizzle(0, 1, 2, 3))
|
|
636
|
+
: vm_immediate(source.value);
|
|
637
|
+
|
|
638
|
+
stream.emit(VM_OP.MOV, base + lane, operand, 0, 0, 1);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return;
|
|
642
|
+
|
|
643
|
+
case PV_GROUP.CONSTANT:
|
|
644
|
+
stream.emit(VM_OP.LOAD_CONST, base, intern_constant(group.constants), 0, 0, group.width);
|
|
645
|
+
|
|
646
|
+
return;
|
|
647
|
+
|
|
648
|
+
default: {
|
|
649
|
+
const sources = [0, 0, 0];
|
|
650
|
+
|
|
651
|
+
for (let slot = 0; slot < 3; slot++) {
|
|
652
|
+
if (group.raw[slot] >= 0) {
|
|
653
|
+
sources[slot] = group.raw[slot];
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
for (const operand of group.operands) {
|
|
658
|
+
sources[operand.slot] = operand.group < 0
|
|
659
|
+
? vm_immediate(operand.value)
|
|
660
|
+
: register_operand(bases[operand.group], operand.lanes);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
stream.emit(group.op, group.span === 0 ? 0 : base, sources[0], sources[1], sources[2], group.width);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* A register operand reading given lanes of a run: the base is the lowest of them and the selectors
|
|
670
|
+
* say the rest. Every lane of an operand comes from ONE group, whose run is at most four slots, so
|
|
671
|
+
* the selectors always fit the two bits they have.
|
|
672
|
+
*
|
|
673
|
+
* @param {number} group_base
|
|
674
|
+
* @param {number[]} lanes which lane of the run each lane of the operand reads
|
|
675
|
+
* @returns {number} a register operand word
|
|
676
|
+
*/
|
|
677
|
+
function register_operand(group_base, lanes) {
|
|
678
|
+
const lowest = Math.min(...lanes);
|
|
679
|
+
const selector = lane => (lane < lanes.length ? lanes[lane] - lowest : lane);
|
|
680
|
+
|
|
681
|
+
assert.ok(
|
|
682
|
+
lanes.every(lane => lane - lowest < VM_MAX_WIDTH),
|
|
683
|
+
`particle value lowering: lanes ${lanes.join(",")} do not fit one operand`
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
return vm_register(group_base + lowest, pack_swizzle(selector(0), selector(1), selector(2), selector(3)));
|
|
687
|
+
}
|