@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,860 @@
|
|
|
1
|
+
import { assert } from "../../../../../core/assert.js";
|
|
2
|
+
import { VM_ISA, VM_MAX_WIDTH, VM_OP, swizzle_selector } from "../../isa/ParticleVMISA.js";
|
|
3
|
+
import { vm_semantics_of } from "../particle_vm_semantics.js";
|
|
4
|
+
import { PV_GROUP } from "./particle_vector_groups.js";
|
|
5
|
+
import { PV } from "./particle_vector_graph.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Turn the chosen values into the groups {@link ./particle_vector_lower.js} schedules, colours and
|
|
9
|
+
* emits.
|
|
10
|
+
*
|
|
11
|
+
* There is less here than the job sounds like, and that is the point. A packer over a graph of lanes
|
|
12
|
+
* needs superword grouping, lane-order search and a demand solver, all of it re-deriving a
|
|
13
|
+
* vectorisation that its own lift threw away — that was measured at eight hundred lines and it still
|
|
14
|
+
* lost. Here a value IS a vector, one value is one instruction, and every operand reads lanes of ONE
|
|
15
|
+
* value, so it is a base and a swizzle, always, with nothing to arrange and nothing to copy.
|
|
16
|
+
*
|
|
17
|
+
* The copies that remain are the ones the program really has: a `BUILD`, which is a vector assembled
|
|
18
|
+
* out of lanes of several others. Those come from a frontend's `vec4` constructor and from a load of
|
|
19
|
+
* record words that different stores wrote, and each is one `MOV` per lane until something coalesces
|
|
20
|
+
* them away.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorGraph} graph
|
|
25
|
+
* @param {import("./particle_vector_lift.js").ParticleLiftedVectorSection} section
|
|
26
|
+
* @param {import("./particle_vector_extract.js").ParticleValueExtraction} extraction
|
|
27
|
+
* @returns {import("./particle_vector_groups.js").ParticleValuePacking}
|
|
28
|
+
*/
|
|
29
|
+
export function pack_particle_vectors(graph, section, extraction) {
|
|
30
|
+
const demand = measure_demand(graph, section, extraction);
|
|
31
|
+
/** @type {import("./particle_vector_groups.js").ParticleValueGroup[]} */
|
|
32
|
+
const groups = [];
|
|
33
|
+
/** @type {Map<number, number>} per class, the group that computes it */
|
|
34
|
+
const of = new Map();
|
|
35
|
+
/** @type {Map<number, {group: number, lane: number}>} */
|
|
36
|
+
const placement = new Map();
|
|
37
|
+
|
|
38
|
+
const add = (shape) => {
|
|
39
|
+
const group = {
|
|
40
|
+
index: groups.length,
|
|
41
|
+
kind: PV_GROUP.COMPUTE,
|
|
42
|
+
op: VM_OP.NOP,
|
|
43
|
+
width: 1,
|
|
44
|
+
span: 0,
|
|
45
|
+
values: [],
|
|
46
|
+
raw: [-1, -1, -1],
|
|
47
|
+
operands: [],
|
|
48
|
+
gather: [],
|
|
49
|
+
constants: [],
|
|
50
|
+
draw: -1,
|
|
51
|
+
coalition: -1,
|
|
52
|
+
sequence: 0,
|
|
53
|
+
...shape,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
groups.push(group);
|
|
57
|
+
|
|
58
|
+
return group;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A source operand: lanes of one value, read where that value lives.
|
|
63
|
+
*
|
|
64
|
+
* @param {number} slot
|
|
65
|
+
* @param {number} value
|
|
66
|
+
* @param {number} pattern
|
|
67
|
+
* @param {number} lanes how many lanes the instruction reads of it
|
|
68
|
+
* @param {number} where the reading instruction's place in the section
|
|
69
|
+
* @returns {import("./particle_vector_groups.js").ParticleGroupOperand}
|
|
70
|
+
*/
|
|
71
|
+
const operand = (slot, value, pattern, lanes, where) => {
|
|
72
|
+
const literals = graph.constants_of(value);
|
|
73
|
+
|
|
74
|
+
if (literals !== null) {
|
|
75
|
+
const read = [];
|
|
76
|
+
|
|
77
|
+
for (let lane = 0; lane < lanes; lane++) {
|
|
78
|
+
read.push(literals[Math.min(swizzle_selector(pattern, lane), literals.length - 1)]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Every lane the same literal is an operand with no instruction and no slot behind it,
|
|
82
|
+
// which is what most literals in an effect are.
|
|
83
|
+
if (read.every(one => Object.is(one, read[0]))) {
|
|
84
|
+
return { slot, group: -1, lanes: [], reads: [], value: read[0] };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// And one that is not uniform is a pool run made HERE, for this operand: sequenced at the
|
|
88
|
+
// instruction that reads it, not where the literal first appeared. Those are the same
|
|
89
|
+
// place for a literal used once near the top and thousands of instructions apart for a
|
|
90
|
+
// noise gradient, and on `curl4` the difference is the whole register file — sixty
|
|
91
|
+
// three-lane gradients loaded at the front and held to their single distant use are 185
|
|
92
|
+
// of its 210 slots. A literal is the cheapest thing there is to recompute; it should
|
|
93
|
+
// never be the thing that is kept.
|
|
94
|
+
const run = add({
|
|
95
|
+
kind: PV_GROUP.CONSTANT,
|
|
96
|
+
op: VM_OP.LOAD_CONST,
|
|
97
|
+
width: read.length,
|
|
98
|
+
span: read.length,
|
|
99
|
+
values: read.map(() => -1),
|
|
100
|
+
constants: read,
|
|
101
|
+
sequence: where,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
slot,
|
|
106
|
+
group: run.index,
|
|
107
|
+
lanes: read.map((unused, lane) => lane),
|
|
108
|
+
reads: read.map((unused, lane) => ({ group: run.index, lane })),
|
|
109
|
+
value: 0,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const place = of.get(graph.find(value));
|
|
114
|
+
|
|
115
|
+
assert.defined(place, `particle vector packing: class ${value} was never computed`);
|
|
116
|
+
|
|
117
|
+
const read = [];
|
|
118
|
+
|
|
119
|
+
for (let lane = 0; lane < lanes; lane++) {
|
|
120
|
+
// A lane past the value's own width is one the instruction does not really read — a
|
|
121
|
+
// `CROSS` asked for four lanes reads three — so it is clamped rather than invented.
|
|
122
|
+
read.push(place.base + Math.min(swizzle_selector(pattern, lane), place.width - 1));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const group = place.group;
|
|
126
|
+
|
|
127
|
+
return { slot, group, lanes: read, reads: read.map(lane => ({ group, lane })), value: 0 };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
merge_draws(graph, extraction, demand, of, add);
|
|
131
|
+
|
|
132
|
+
for (const id of extraction.order) {
|
|
133
|
+
if (of.has(id)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const node = graph.node(extraction.choice.get(id));
|
|
138
|
+
const sequence = id;
|
|
139
|
+
const width = demand.get(id) ?? 0;
|
|
140
|
+
|
|
141
|
+
// Nothing reads it: an instruction that need not be written at all.
|
|
142
|
+
if (width === 0) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// A literal is not an instruction until an operand needs it in a register, and most never
|
|
147
|
+
// do — see `operand` above.
|
|
148
|
+
if (node.op === PV.CONST) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (node.op === PV.ATTR) {
|
|
153
|
+
const group = add({
|
|
154
|
+
op: VM_OP.LOAD_ATTR,
|
|
155
|
+
width,
|
|
156
|
+
span: width,
|
|
157
|
+
raw: [node.imm[0], width, -1],
|
|
158
|
+
sequence,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
of.set(id, { group: group.index, base: 0, width: group.span });
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (node.op === PV.BUILD) {
|
|
166
|
+
const group = add({
|
|
167
|
+
kind: PV_GROUP.GATHER,
|
|
168
|
+
op: VM_OP.MOV,
|
|
169
|
+
width: 1,
|
|
170
|
+
span: width,
|
|
171
|
+
gather: node.children.slice(0, width).map((child, lane) => {
|
|
172
|
+
const literals = graph.constants_of(child);
|
|
173
|
+
|
|
174
|
+
if (literals !== null) {
|
|
175
|
+
return { group: -1, lane: 0, value: literals[Math.min(node.patterns[lane], literals.length - 1)] };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const from = of.get(graph.find(child));
|
|
179
|
+
|
|
180
|
+
assert.defined(from, `particle vector packing: class ${child} was never computed`);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
group: from.group,
|
|
184
|
+
lane: from.base + Math.min(node.patterns[lane], from.width - 1),
|
|
185
|
+
value: 0,
|
|
186
|
+
};
|
|
187
|
+
}),
|
|
188
|
+
sequence,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
of.set(id, { group: group.index, base: 0, width: group.span });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const reads = narrowable(node) ? width : node.operand_width;
|
|
196
|
+
const group = add({
|
|
197
|
+
op: node.op,
|
|
198
|
+
width: Math.max(reads, VM_ISA.get(node.op).min_width),
|
|
199
|
+
span: narrowable(node) ? width : node.width,
|
|
200
|
+
raw: raw_fields(node),
|
|
201
|
+
draw: node.op === VM_OP.RANDOM ? node.imm[0] : -1,
|
|
202
|
+
sequence,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
for (let source = 0; source < node.children.length; source++) {
|
|
206
|
+
group.operands.push(operand(
|
|
207
|
+
slot_of(node, source),
|
|
208
|
+
node.children[source],
|
|
209
|
+
node.patterns[source],
|
|
210
|
+
node.op === VM_OP.CURVE ? 1 : group.width,
|
|
211
|
+
sequence
|
|
212
|
+
));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
of.set(id, { group: group.index, base: 0, width: group.span });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const id of extraction.order) {
|
|
219
|
+
if (of.has(id)) {
|
|
220
|
+
placement.set(id, { group: of.get(id).group, lane: of.get(id).base });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
for (const chunk of chunk_stores(section.stores)) {
|
|
225
|
+
const group = add({
|
|
226
|
+
kind: PV_GROUP.STORE,
|
|
227
|
+
op: VM_OP.STORE_ATTR,
|
|
228
|
+
width: chunk.length,
|
|
229
|
+
span: 0,
|
|
230
|
+
raw: [-1, chunk[0].word, chunk.length],
|
|
231
|
+
sequence: Math.max(...chunk.map(store => graph.find(store.place.value))),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
group.operands.push(store_operand(graph, of, groups, chunk, add, group.sequence));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const kill of section.kills) {
|
|
238
|
+
const group = add({
|
|
239
|
+
kind: PV_GROUP.KILL,
|
|
240
|
+
op: VM_OP.KILL,
|
|
241
|
+
width: 1,
|
|
242
|
+
span: 0,
|
|
243
|
+
sequence: graph.find(kill.value),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const literals = graph.constants_of(kill.value);
|
|
247
|
+
|
|
248
|
+
if (literals !== null) {
|
|
249
|
+
group.operands.push({ slot: 0, group: -1, lanes: [], reads: [], value: literals[kill.lane] });
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const from = of.get(graph.find(kill.value));
|
|
254
|
+
|
|
255
|
+
group.operands.push({
|
|
256
|
+
slot: 0,
|
|
257
|
+
group: from.group,
|
|
258
|
+
lanes: [from.base + kill.lane],
|
|
259
|
+
reads: [{ group: from.group, lane: from.base + kill.lane }],
|
|
260
|
+
value: 0,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return dissolve_builds({ groups, placement, coalitions: [] }, add);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* The widest stretch of the file a coalition may claim — the runs it lays end to end, added up.
|
|
269
|
+
*
|
|
270
|
+
* A ceiling rather than a tuned number: swept over the library's effects, 4 costs `integrate` four
|
|
271
|
+
* instructions (a four-lane run plus a one-lane pool run is five slots) and everything from 6 upwards
|
|
272
|
+
* is identical. What actually limits a coalition is that the lanes one operand wants must fall inside
|
|
273
|
+
* ONE window of four, which no amount of room relaxes.
|
|
274
|
+
*
|
|
275
|
+
* @type {number}
|
|
276
|
+
*/
|
|
277
|
+
export const PARTICLE_VECTOR_COALITION_SLOTS = 8;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get rid of the copies by laying the runs where the copy was going to put them.
|
|
281
|
+
*
|
|
282
|
+
* A `BUILD` is a vector assembled from lanes of several values, and it is the only thing in this
|
|
283
|
+
* optimizer that costs an instruction without computing anything. But an operand can read any four
|
|
284
|
+
* consecutive slots: so if the runs it draws from are laid END TO END, and the lanes it wants happen
|
|
285
|
+
* to fall inside one window of four, whoever reads the `BUILD` can read those slots directly and the
|
|
286
|
+
* copies are simply not needed. Nothing about a destination says where it has to be — this optimizer
|
|
287
|
+
* chooses.
|
|
288
|
+
*
|
|
289
|
+
* That is the same thing a pass pipeline achieves by RETARGETING the instruction that feeds a copy,
|
|
290
|
+
* which is what `../pass_propagate_copies.js` does to the 1,227 `MOV`s a frontend emits for its
|
|
291
|
+
* vector constructors. Said in terms of layout rather than of rewriting, because here the layout is
|
|
292
|
+
* not yet decided and there is nothing to rewrite.
|
|
293
|
+
*
|
|
294
|
+
* A literal lane is a run too. Most of what a frontend's vector constructors build is three computed
|
|
295
|
+
* lanes and a constant `w` — `[x, y, z, 0]`, `[x, y, z, 1]` — and laying one pool run of the literals
|
|
296
|
+
* beside the computed ones turns four copies into a single `LOAD_CONST`. That is where the whole of
|
|
297
|
+
* `simplex4`'s remaining thirty copies were.
|
|
298
|
+
*
|
|
299
|
+
* Heaviest first, since a `BUILD` of four lanes is worth four copies and one of two is worth two, and
|
|
300
|
+
* a run may only be in one coalition.
|
|
301
|
+
*
|
|
302
|
+
* @param {import("./particle_vector_groups.js").ParticleValuePacking} packing
|
|
303
|
+
* @param {function(object): import("./particle_vector_groups.js").ParticleValueGroup} add
|
|
304
|
+
* @returns {import("./particle_vector_groups.js").ParticleValuePacking}
|
|
305
|
+
*/
|
|
306
|
+
function dissolve_builds(packing, add) {
|
|
307
|
+
const { groups } = packing;
|
|
308
|
+
/** @type {number[][]} */
|
|
309
|
+
const coalitions = [];
|
|
310
|
+
/** @type {Map<number, {anchor: number, slots: number[], reads: Array<{group: number, lane: number}>}>} */
|
|
311
|
+
const dissolved = new Map();
|
|
312
|
+
|
|
313
|
+
const builds = groups
|
|
314
|
+
.filter(group => group.kind === PV_GROUP.GATHER)
|
|
315
|
+
.sort((a, b) => b.gather.length - a.gather.length || a.index - b.index);
|
|
316
|
+
|
|
317
|
+
for (const build of builds) {
|
|
318
|
+
// Where this build's lanes come from: one entry per run it reads, and one more for its
|
|
319
|
+
// literal lanes, whose run does not exist yet and is only made if the layout works out.
|
|
320
|
+
/** @type {Array<{group: number, span: number}>} */
|
|
321
|
+
const plan = [];
|
|
322
|
+
/** @type {number[]} */
|
|
323
|
+
const literals = [];
|
|
324
|
+
const wanted = build.gather.map(source => {
|
|
325
|
+
if (source.group < 0) {
|
|
326
|
+
let lane = literals.findIndex(one => Object.is(one, source.value));
|
|
327
|
+
|
|
328
|
+
if (lane < 0) {
|
|
329
|
+
lane = literals.push(source.value) - 1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return { member: -1, lane };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let member = plan.findIndex(one => one.group === source.group);
|
|
336
|
+
|
|
337
|
+
if (member < 0) {
|
|
338
|
+
member = plan.push({ group: source.group, span: groups[source.group].span }) - 1;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return { member, lane: source.lane };
|
|
342
|
+
});
|
|
343
|
+
const pool = literals.length > 0 ? plan.push({ group: -1, span: literals.length }) - 1 : -1;
|
|
344
|
+
|
|
345
|
+
for (const source of wanted) {
|
|
346
|
+
if (source.member < 0) {
|
|
347
|
+
source.member = pool;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (plan.length === 0 || plan.length > VM_MAX_WIDTH) {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// A build feeding a build is not something the rewrite below can follow — the reader it
|
|
356
|
+
// would have to retarget is another gather.
|
|
357
|
+
if (plan.some(one => one.group >= 0 && groups[one.group].kind === PV_GROUP.GATHER)) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// A run may only be in one coalition — but a build reading a run that is already in one is
|
|
362
|
+
// not thereby lost. Its lanes may already lie where they need to: `simplex4` builds
|
|
363
|
+
// `[x, y, z, 0]`, `[y, z, 0]` and `[z, 0, 0]` from the same vector, and one layout serves all
|
|
364
|
+
// three. So being coalesced is a reason to JOIN, not a reason to refuse.
|
|
365
|
+
if (plan.some(one => one.group >= 0 && groups[one.group].coalition >= 0)) {
|
|
366
|
+
const joined = join_coalition(groups, coalitions, plan, wanted, literals, pool);
|
|
367
|
+
|
|
368
|
+
if (joined !== null) {
|
|
369
|
+
dissolved.set(build.index, joined);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// The literals' run is an instruction of its own, so it has to pay for itself. Joining an
|
|
376
|
+
// existing coalition costs nothing, which is why this is asked only of a fresh one.
|
|
377
|
+
if (build.gather.length <= (pool < 0 ? 0 : 1)) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (plan.reduce((slots, one) => slots + one.span, 0) > PARTICLE_VECTOR_COALITION_SLOTS) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const chosen = arrangements(plan.map((unused, member) => member))
|
|
386
|
+
.map(arrangement => ({ arrangement, slots: window_of(plan, arrangement, wanted) }))
|
|
387
|
+
.find(candidate => candidate.slots !== null);
|
|
388
|
+
|
|
389
|
+
if (chosen === undefined) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (pool >= 0) {
|
|
394
|
+
plan[pool].group = add({
|
|
395
|
+
kind: PV_GROUP.CONSTANT,
|
|
396
|
+
op: VM_OP.LOAD_CONST,
|
|
397
|
+
width: literals.length,
|
|
398
|
+
span: literals.length,
|
|
399
|
+
values: literals.map(() => -1),
|
|
400
|
+
constants: literals,
|
|
401
|
+
sequence: build.sequence,
|
|
402
|
+
}).index;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const arrangement = chosen.arrangement.map(member => plan[member].group);
|
|
406
|
+
const index = coalitions.length;
|
|
407
|
+
|
|
408
|
+
coalitions.push(arrangement);
|
|
409
|
+
|
|
410
|
+
for (const group of arrangement) {
|
|
411
|
+
groups[group].coalition = index;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
dissolved.set(build.index, {
|
|
415
|
+
anchor: arrangement[0],
|
|
416
|
+
slots: chosen.slots,
|
|
417
|
+
reads: wanted.map(source => ({ group: plan[source.member].group, lane: source.lane })),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (dissolved.size === 0) {
|
|
422
|
+
return packing;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Whoever read the `BUILD` now reads the runs where they lie.
|
|
426
|
+
for (const group of groups) {
|
|
427
|
+
for (const operand of group.operands) {
|
|
428
|
+
const gone = dissolved.get(operand.group);
|
|
429
|
+
|
|
430
|
+
if (gone === undefined) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
operand.reads = operand.lanes.map(lane => gone.reads[Math.min(lane, gone.reads.length - 1)]);
|
|
435
|
+
operand.lanes = operand.lanes.map(lane => gone.slots[Math.min(lane, gone.slots.length - 1)]);
|
|
436
|
+
operand.group = gone.anchor;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// A build may read a build — a four-wide load of a three-component attribute is a build, and
|
|
440
|
+
// anything assembled from it reads one. Those lanes are copies still to be made, so they take
|
|
441
|
+
// the run each lane ended up in rather than a window into it.
|
|
442
|
+
group.gather = group.gather.map(source => {
|
|
443
|
+
const gone = dissolved.get(source.group);
|
|
444
|
+
|
|
445
|
+
return gone === undefined
|
|
446
|
+
? source
|
|
447
|
+
: { ...source, ...gone.reads[Math.min(source.lane, gone.reads.length - 1)] };
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return renumber(groups, coalitions, dissolved);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Whether a `BUILD` can read the layout an existing coalition already has, without changing it.
|
|
456
|
+
*
|
|
457
|
+
* Nothing is laid out here and nothing is added: the question is only whether the lanes this build
|
|
458
|
+
* wants happen to fall inside one window of four of a layout that has already been decided. Its
|
|
459
|
+
* literal lanes have to be found in a pool run that coalition already holds — extending one would
|
|
460
|
+
* move everything after it, and the coalition's other readers have been told where their slots are.
|
|
461
|
+
*
|
|
462
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
463
|
+
* @param {number[][]} coalitions
|
|
464
|
+
* @param {Array<{group: number, span: number}>} plan
|
|
465
|
+
* @param {Array<{member: number, lane: number}>} wanted
|
|
466
|
+
* @param {number[]} literals
|
|
467
|
+
* @param {number} pool which member of `plan` holds the literal lanes, or -1
|
|
468
|
+
* @returns {{anchor: number, slots: number[], reads: Array<{group: number, lane: number}>}|null}
|
|
469
|
+
*/
|
|
470
|
+
function join_coalition(groups, coalitions, plan, wanted, literals, pool) {
|
|
471
|
+
const anchored = plan.find(one => one.group >= 0 && groups[one.group].coalition >= 0);
|
|
472
|
+
const index = groups[anchored.group].coalition;
|
|
473
|
+
const members = coalitions[index];
|
|
474
|
+
|
|
475
|
+
// Every run it reads has to be in that same coalition, or the slots are not one stretch at all.
|
|
476
|
+
if (plan.some(one => one.group >= 0 && groups[one.group].coalition !== index)) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const offset = new Map();
|
|
481
|
+
|
|
482
|
+
let at = 0;
|
|
483
|
+
|
|
484
|
+
for (const group of members) {
|
|
485
|
+
offset.set(group, at);
|
|
486
|
+
at += groups[group].span;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** @type {Array<{group: number, lane: number}>} where each literal already sits, if it does */
|
|
490
|
+
const found = literals.map(value => {
|
|
491
|
+
for (const group of members) {
|
|
492
|
+
if (groups[group].kind !== PV_GROUP.CONSTANT) {
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const lane = groups[group].constants.findIndex(one => Object.is(one, value));
|
|
497
|
+
|
|
498
|
+
if (lane >= 0) {
|
|
499
|
+
return { group, lane };
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return null;
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
if (pool >= 0 && found.some(one => one === null)) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const reads = wanted.map(source => (source.member === pool
|
|
511
|
+
? found[source.lane]
|
|
512
|
+
: { group: plan[source.member].group, lane: source.lane }));
|
|
513
|
+
const slots = reads.map(read => offset.get(read.group) + read.lane);
|
|
514
|
+
|
|
515
|
+
if (Math.max(...slots) - Math.min(...slots) >= VM_MAX_WIDTH) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return { anchor: members[0], slots, reads };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Where a `BUILD`'s lanes land if its runs are laid out in this order, and whether that is somewhere
|
|
524
|
+
* one operand can reach.
|
|
525
|
+
*
|
|
526
|
+
* @param {Array<{group: number, span: number}>} plan
|
|
527
|
+
* @param {number[]} arrangement the members of `plan`, in the order they would be laid out
|
|
528
|
+
* @param {Array<{member: number, lane: number}>} wanted
|
|
529
|
+
* @returns {number[]|null} the slot each lane sits at, measured from the first run's base
|
|
530
|
+
*/
|
|
531
|
+
function window_of(plan, arrangement, wanted) {
|
|
532
|
+
const offset = new Map();
|
|
533
|
+
|
|
534
|
+
let at = 0;
|
|
535
|
+
|
|
536
|
+
for (const member of arrangement) {
|
|
537
|
+
offset.set(member, at);
|
|
538
|
+
at += plan[member].span;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const slots = wanted.map(source => offset.get(source.member) + source.lane);
|
|
542
|
+
|
|
543
|
+
return Math.max(...slots) - Math.min(...slots) < VM_MAX_WIDTH ? slots : null;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Drop the groups that dissolved and close the gap in the numbering.
|
|
548
|
+
*
|
|
549
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
550
|
+
* @param {number[][]} coalitions
|
|
551
|
+
* @param {Map<number, object>} dissolved
|
|
552
|
+
* @returns {import("./particle_vector_groups.js").ParticleValuePacking}
|
|
553
|
+
*/
|
|
554
|
+
function renumber(groups, coalitions, dissolved) {
|
|
555
|
+
const kept = groups.filter(group => !dissolved.has(group.index));
|
|
556
|
+
const moved = new Map();
|
|
557
|
+
|
|
558
|
+
kept.forEach((group, index) => moved.set(group.index, index));
|
|
559
|
+
|
|
560
|
+
for (const group of kept) {
|
|
561
|
+
group.index = moved.get(group.index);
|
|
562
|
+
|
|
563
|
+
for (const operand of group.operands) {
|
|
564
|
+
if (operand.group >= 0) {
|
|
565
|
+
operand.group = moved.get(operand.group);
|
|
566
|
+
operand.reads = operand.reads.map(read => ({ group: moved.get(read.group), lane: read.lane }));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
group.gather = group.gather.map(source => (source.group >= 0
|
|
571
|
+
? { ...source, group: moved.get(source.group) }
|
|
572
|
+
: source));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
groups: kept,
|
|
577
|
+
placement: new Map(),
|
|
578
|
+
coalitions: coalitions.map(members => members.map(group => moved.get(group))),
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Every order a handful of runs could be laid out in. At most four of them, so at most twenty-four.
|
|
584
|
+
*
|
|
585
|
+
* @param {number[]} members
|
|
586
|
+
* @returns {number[][]}
|
|
587
|
+
*/
|
|
588
|
+
function arrangements(members) {
|
|
589
|
+
if (members.length <= 1) {
|
|
590
|
+
return [members];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const out = [];
|
|
594
|
+
|
|
595
|
+
for (let i = 0; i < members.length; i++) {
|
|
596
|
+
const rest = members.filter((unused, j) => j !== i);
|
|
597
|
+
|
|
598
|
+
for (const tail of arrangements(rest)) {
|
|
599
|
+
out.push([members[i], ...tail]);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return out;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* The value a store writes, as one operand — or, where its words come from lanes of several values,
|
|
608
|
+
* the run a `BUILD` has to make first.
|
|
609
|
+
*
|
|
610
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorGraph} graph
|
|
611
|
+
* @param {Map<number, number>} of
|
|
612
|
+
* @param {import("./particle_vector_groups.js").ParticleValueGroup[]} groups
|
|
613
|
+
* @param {Array<{word: number, place: {value: number, lane: number}}>} chunk
|
|
614
|
+
* @param {function(object): import("./particle_vector_groups.js").ParticleValueGroup} add
|
|
615
|
+
* @param {number} where the storing instruction's place in the section
|
|
616
|
+
* @returns {import("./particle_vector_groups.js").ParticleGroupOperand}
|
|
617
|
+
*/
|
|
618
|
+
function store_operand(graph, of, groups, chunk, add, where) {
|
|
619
|
+
const first = graph.find(chunk[0].place.value);
|
|
620
|
+
|
|
621
|
+
assert.ok(
|
|
622
|
+
chunk.every(store => graph.find(store.place.value) === first),
|
|
623
|
+
"particle vector packing: a store chunk should have been cut where its value changes"
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
const literals = graph.constants_of(first);
|
|
627
|
+
|
|
628
|
+
if (literals !== null) {
|
|
629
|
+
const read = chunk.map(store => literals[Math.min(store.place.lane, literals.length - 1)]);
|
|
630
|
+
|
|
631
|
+
if (read.every(one => Object.is(one, read[0]))) {
|
|
632
|
+
return { slot: 0, group: -1, lanes: [], reads: [], value: read[0] };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const run = add({
|
|
636
|
+
kind: PV_GROUP.CONSTANT,
|
|
637
|
+
op: VM_OP.LOAD_CONST,
|
|
638
|
+
width: read.length,
|
|
639
|
+
span: read.length,
|
|
640
|
+
values: read.map(() => -1),
|
|
641
|
+
constants: read,
|
|
642
|
+
sequence: where,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
slot: 0,
|
|
647
|
+
group: run.index,
|
|
648
|
+
lanes: read.map((unused, lane) => lane),
|
|
649
|
+
reads: read.map((unused, lane) => ({ group: run.index, lane })),
|
|
650
|
+
value: 0,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const place = of.get(first);
|
|
655
|
+
const group = place.group;
|
|
656
|
+
const lanes = chunk.map(store => place.base + store.place.lane);
|
|
657
|
+
|
|
658
|
+
return { slot: 0, group, lanes, reads: lanes.map(lane => ({ group, lane })), value: 0 };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Which record words go in one `STORE_ATTR`: consecutive words, taken from one value, no more than
|
|
663
|
+
* the four an operand spans — and cut where the original stores cut, so that a `vec3` position and a
|
|
664
|
+
* `vec4` colour in adjacent words stay two stores.
|
|
665
|
+
*
|
|
666
|
+
* @param {Array<{word: number, place: {value: number, lane: number}, origin: number}>} stores
|
|
667
|
+
* @returns {Array<Array<{word: number, place: {value: number, lane: number}, origin: number}>>}
|
|
668
|
+
*/
|
|
669
|
+
export function chunk_stores(stores) {
|
|
670
|
+
const chunks = [];
|
|
671
|
+
|
|
672
|
+
for (const store of stores) {
|
|
673
|
+
const chunk = chunks[chunks.length - 1];
|
|
674
|
+
const last = chunk?.[chunk.length - 1];
|
|
675
|
+
|
|
676
|
+
if (chunk !== undefined
|
|
677
|
+
&& chunk.length < VM_MAX_WIDTH
|
|
678
|
+
&& last.word + 1 === store.word
|
|
679
|
+
&& last.origin === store.origin
|
|
680
|
+
&& last.place.value === store.place.value) {
|
|
681
|
+
chunk.push(store);
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
chunks.push([store]);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return chunks;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorNode} node
|
|
693
|
+
* @returns {number[]} the static operand words the opcode reads for itself
|
|
694
|
+
*/
|
|
695
|
+
function raw_fields(node) {
|
|
696
|
+
switch (node.op) {
|
|
697
|
+
case VM_OP.LOAD_BUILTIN:
|
|
698
|
+
case VM_OP.CURVE:
|
|
699
|
+
return [node.imm[0], -1, -1];
|
|
700
|
+
|
|
701
|
+
default:
|
|
702
|
+
return [-1, -1, -1];
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorNode} node
|
|
708
|
+
* @param {number} source which of the node's sources
|
|
709
|
+
* @returns {number} the instruction source slot it goes in
|
|
710
|
+
*/
|
|
711
|
+
function slot_of(node, source) {
|
|
712
|
+
// `CURVE` takes its handle in slot 0 and the value it samples in slot 1; everything else fills
|
|
713
|
+
// its slots in order.
|
|
714
|
+
return node.op === VM_OP.CURVE ? source + 1 : source;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* How many lanes of each value anything actually reads.
|
|
719
|
+
*
|
|
720
|
+
* Walked backwards from what the section is obliged to produce, so a value nothing reads gets zero —
|
|
721
|
+
* which is dead-code elimination — and one whose upper lanes nothing reads gets fewer than it has,
|
|
722
|
+
* which is narrowing. Both fall out of the same number, and both are worth having twice over here:
|
|
723
|
+
* a narrower instruction is a smaller run as well as, sometimes, a cheaper one.
|
|
724
|
+
*
|
|
725
|
+
* Only the opcodes the ISA says may drop their trailing lanes are narrowed. A `RANDOM` may not — its
|
|
726
|
+
* width IS how many draws it takes — and neither may a reduction, which reads every lane to write
|
|
727
|
+
* one.
|
|
728
|
+
*
|
|
729
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorGraph} graph
|
|
730
|
+
* @param {import("./particle_vector_lift.js").ParticleLiftedVectorSection} section
|
|
731
|
+
* @param {import("./particle_vector_extract.js").ParticleValueExtraction} extraction
|
|
732
|
+
* @returns {Map<number, number>} per class, the lanes of it that are read
|
|
733
|
+
*/
|
|
734
|
+
function measure_demand(graph, section, extraction) {
|
|
735
|
+
/** @type {Map<number, number>} */
|
|
736
|
+
const demand = new Map();
|
|
737
|
+
const want = (value, lanes) => {
|
|
738
|
+
const id = graph.find(value);
|
|
739
|
+
|
|
740
|
+
demand.set(id, Math.max(demand.get(id) ?? 0, lanes));
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
for (const store of section.stores) {
|
|
744
|
+
want(store.place.value, store.place.lane + 1);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
for (const kill of section.kills) {
|
|
748
|
+
want(kill.value, kill.lane + 1);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// A draw is taken whether or not it is read, so a `RANDOM` is wanted whole.
|
|
752
|
+
for (const draw of section.draws) {
|
|
753
|
+
want(draw, graph.width_of(draw));
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
for (let i = extraction.order.length - 1; i >= 0; i--) {
|
|
757
|
+
const id = extraction.order[i];
|
|
758
|
+
const wanted = demand.get(id) ?? 0;
|
|
759
|
+
|
|
760
|
+
if (wanted === 0) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const node = graph.node(extraction.choice.get(id));
|
|
765
|
+
const width = narrowable(node) ? Math.min(wanted, node.width) : node.width;
|
|
766
|
+
|
|
767
|
+
if (node.op === PV.BUILD) {
|
|
768
|
+
for (let lane = 0; lane < width; lane++) {
|
|
769
|
+
want(node.children[lane], node.patterns[lane] + 1);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const reads = node.op === VM_OP.CURVE
|
|
776
|
+
? 1
|
|
777
|
+
: Math.max(narrowable(node) ? width : node.operand_width, VM_ISA.get(node.op)?.min_width ?? 1);
|
|
778
|
+
|
|
779
|
+
for (let source = 0; source < node.children.length; source++) {
|
|
780
|
+
let highest = 0;
|
|
781
|
+
|
|
782
|
+
for (let lane = 0; lane < reads; lane++) {
|
|
783
|
+
highest = Math.max(highest, swizzle_selector(node.patterns[source], lane) + 1);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
want(node.children[source], highest);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return demand;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorNode} node
|
|
795
|
+
* @returns {boolean} whether dropping its trailing lanes leaves the rest unchanged
|
|
796
|
+
*/
|
|
797
|
+
function narrowable(node) {
|
|
798
|
+
if (node.op === PV.ATTR || node.op === PV.BUILD) {
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return vm_semantics_of(node.op).narrowable && node.width === node.operand_width;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Take consecutive draws in one instruction.
|
|
807
|
+
*
|
|
808
|
+
* A `RANDOM` of width n advances the generator n times and writes n slots, so two `RANDOM.1`s taking
|
|
809
|
+
* draws 4 and 5 compute exactly what one `RANDOM.2` taking draws 4 and 5 computes — the same numbers,
|
|
810
|
+
* in the same order, in two adjacent slots. The graph cannot see this, because a draw is identified by
|
|
811
|
+
* its index and two indices are two values; it is a fact about the instruction rather than about the
|
|
812
|
+
* values, so it belongs here, where instructions are chosen.
|
|
813
|
+
*
|
|
814
|
+
* Only runs that are consecutive with no gap are merged: draws 4 and 6 in one instruction would
|
|
815
|
+
* silently take draw 5 as well and hand the reader the wrong number.
|
|
816
|
+
*
|
|
817
|
+
* @param {import("./particle_vector_graph.js").ParticleVectorGraph} graph
|
|
818
|
+
* @param {import("./particle_vector_extract.js").ParticleValueExtraction} extraction
|
|
819
|
+
* @param {Map<number, number>} demand
|
|
820
|
+
* @param {Map<number, {group: number, base: number, width: number}>} of
|
|
821
|
+
* @param {function(object): import("./particle_vector_groups.js").ParticleValueGroup} add
|
|
822
|
+
*/
|
|
823
|
+
function merge_draws(graph, extraction, demand, of, add) {
|
|
824
|
+
const draws = extraction.order
|
|
825
|
+
.filter(id => (demand.get(id) ?? 0) > 0 && graph.node(extraction.choice.get(id)).op === VM_OP.RANDOM)
|
|
826
|
+
.map(id => ({ id, node: graph.node(extraction.choice.get(id)) }))
|
|
827
|
+
.sort((a, b) => a.node.imm[0] - b.node.imm[0]);
|
|
828
|
+
|
|
829
|
+
for (let first = 0; first < draws.length;) {
|
|
830
|
+
let last = first;
|
|
831
|
+
let width = draws[first].node.width;
|
|
832
|
+
|
|
833
|
+
while (last + 1 < draws.length
|
|
834
|
+
&& draws[last + 1].node.imm[0] === draws[first].node.imm[0] + width
|
|
835
|
+
&& width + draws[last + 1].node.width <= VM_MAX_WIDTH) {
|
|
836
|
+
width += draws[last + 1].node.width;
|
|
837
|
+
last += 1;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (last > first) {
|
|
841
|
+
const group = add({
|
|
842
|
+
op: VM_OP.RANDOM,
|
|
843
|
+
width,
|
|
844
|
+
span: width,
|
|
845
|
+
raw: [-1, -1, -1],
|
|
846
|
+
draw: draws[first].node.imm[0],
|
|
847
|
+
sequence: Math.min(...draws.slice(first, last + 1).map(one => one.id)),
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
let base = 0;
|
|
851
|
+
|
|
852
|
+
for (const one of draws.slice(first, last + 1)) {
|
|
853
|
+
of.set(one.id, { group: group.index, base, width: one.node.width });
|
|
854
|
+
base += one.node.width;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
first = last + 1;
|
|
859
|
+
}
|
|
860
|
+
}
|