@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.
Files changed (410) hide show
  1. package/editor/particles/effect/ParticleEffectDocument.d.ts +21 -6
  2. package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
  3. package/editor/particles/effect/ParticleEffectDocument.js +38 -17
  4. package/editor/view/node-graph/NodeGraphClipboard.d.ts +186 -0
  5. package/editor/view/node-graph/NodeGraphClipboard.d.ts.map +1 -0
  6. package/editor/view/node-graph/NodeGraphClipboard.js +231 -0
  7. package/editor/view/node-graph/NodeGraphEditorView.d.ts +59 -1
  8. package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
  9. package/editor/view/node-graph/NodeGraphEditorView.js +248 -5
  10. package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
  11. package/editor/view/node-graph/NodeGraphView.js +84 -8
  12. package/editor/view/node-graph/actions/connection_bind_actions.d.ts +23 -0
  13. package/editor/view/node-graph/actions/connection_bind_actions.d.ts.map +1 -0
  14. package/editor/view/node-graph/actions/connection_bind_actions.js +39 -0
  15. package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -1
  16. package/editor/view/particles/effect/ParticleCodeEditorView.js +4 -3
  17. package/editor/view/particles/effect/ParticleCurveEditorView.d.ts +20 -0
  18. package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -0
  19. package/editor/view/particles/effect/ParticleCurveEditorView.js +574 -0
  20. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
  21. package/editor/view/particles/effect/ParticleEmitterInspectorView.js +27 -1
  22. package/editor/view/particles/effect/ParticleGradientEditorView.d.ts +24 -0
  23. package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -0
  24. package/editor/view/particles/effect/ParticleGradientEditorView.js +472 -0
  25. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +5 -0
  26. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -1
  27. package/editor/view/particles/effect/ParticleGraphEditorView.js +61 -31
  28. package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
  29. package/editor/view/particles/effect/ParticleNodeParametersView.js +33 -0
  30. package/editor/view/particles/effect/particle-editor.css +123 -35
  31. package/package.json +1 -1
  32. package/src/core/binary/allocator/OffsetAllocator.d.ts +25 -16
  33. package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
  34. package/src/core/binary/allocator/OffsetAllocator.js +10 -1
  35. package/src/core/collection/array/typed/float32_array_hash.d.ts +21 -0
  36. package/src/core/collection/array/typed/float32_array_hash.d.ts.map +1 -0
  37. package/src/core/collection/array/typed/float32_array_hash.js +38 -0
  38. package/src/core/graph/csr/CSRGraph.d.ts +8 -1
  39. package/src/core/graph/csr/CSRGraph.d.ts.map +1 -1
  40. package/src/core/graph/csr/CSRGraph.js +14 -6
  41. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +24 -0
  42. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -1
  43. package/src/core/model/node-graph/visual/layout/LayoutProblem.js +27 -0
  44. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +26 -0
  45. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
  46. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +28 -0
  47. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +92 -3
  48. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -1
  49. package/src/core/model/node-graph/visual/layout/RouteGrid.js +405 -242
  50. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +50 -5
  51. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -1
  52. package/src/core/model/node-graph/visual/layout/RouteSearch.js +160 -24
  53. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +7 -0
  54. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -1
  55. package/src/core/model/node-graph/visual/layout/RouteSet.js +8 -0
  56. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +9 -3
  57. package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -1
  58. package/src/core/model/node-graph/visual/layout/layout_build_problem.js +7 -0
  59. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +9 -3
  60. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -1
  61. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +17 -3
  62. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +9 -6
  63. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -1
  64. package/src/core/model/node-graph/visual/layout/layout_node_graph.js +49 -7
  65. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +11 -1
  66. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -1
  67. package/src/core/model/node-graph/visual/layout/layout_order_layers.js +135 -16
  68. package/src/core/model/node-graph/visual/layout/route_connections.d.ts +29 -0
  69. package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -1
  70. package/src/core/model/node-graph/visual/layout/route_connections.js +716 -309
  71. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +3 -2
  72. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -1
  73. package/src/core/model/node-graph/visual/layout/route_grid_build.js +7 -15
  74. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +6 -0
  75. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -1
  76. package/src/core/model/node-graph/visual/layout/route_nudge.js +42 -13
  77. package/src/engine/animation/AnimationUtils.js +246 -246
  78. package/src/engine/animation/Animations.js +74 -74
  79. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2078 -2078
  80. package/src/engine/ecs/EventType.d.ts +0 -1
  81. package/src/engine/ecs/EventType.js +1 -26
  82. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +1 -1
  83. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -1
  84. package/src/engine/ecs/ik/ik_write_local_rotation.js +3 -7
  85. package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
  86. package/src/engine/ecs/parent/EntityNode.js +4 -9
  87. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  88. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +360 -364
  89. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts +41 -0
  90. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts.map +1 -0
  91. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.js +40 -0
  92. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +8 -0
  93. package/src/engine/ecs/transform/t64_announce_change.d.ts +10 -7
  94. package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -1
  95. package/src/engine/ecs/transform/t64_announce_change.js +12 -12
  96. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts +30 -0
  97. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts.map +1 -0
  98. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js +29 -0
  99. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
  100. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +13 -21
  101. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  102. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +4 -8
  103. package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
  104. package/src/engine/graphics3/LightSystem.js +4 -8
  105. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
  106. package/src/engine/graphics3/MeshSystem.js +9 -19
  107. package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
  108. package/src/engine/graphics3/ParticleEmitterSystem.js +4 -8
  109. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
  110. package/src/engine/graphics3/ShadedGeometrySystem.js +4 -10
  111. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +1 -1
  112. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
  113. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +4 -8
  114. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +218 -218
  115. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  116. package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
  117. package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
  118. package/src/engine/sound/ecs/SoundListenerSystem.js +6 -8
  119. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +0 -4
  120. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
  121. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +5 -11
  122. package/src/shade/playground/particle_editor/README.md +43 -2
  123. package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
  124. package/src/shade/playground/particle_system/particle_prototype.js +4 -5
  125. package/src/shade/playground/particle_system/particle_scene.js +4 -4
  126. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  127. package/src/shade/renderer/Renderer.js +13 -1
  128. package/src/shade/renderer/particles/DESIGN.md +104 -19
  129. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +64 -50
  130. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  131. package/src/shade/renderer/particles/GPUParticleSystem.js +176 -78
  132. package/src/shade/renderer/particles/ParticleConstants.d.ts +59 -23
  133. package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
  134. package/src/shade/renderer/particles/ParticleConstants.js +66 -23
  135. package/src/shade/renderer/particles/ParticleRenderMode.d.ts +19 -0
  136. package/src/shade/renderer/particles/ParticleRenderMode.d.ts.map +1 -0
  137. package/src/shade/renderer/particles/ParticleRenderMode.js +33 -0
  138. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  139. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +167 -166
  140. package/src/shade/renderer/particles/data/particle_emitter_record.d.ts +37 -0
  141. package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -0
  142. package/src/shade/renderer/particles/data/particle_emitter_record.js +109 -0
  143. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +3 -3
  144. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -303
  145. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +18 -0
  146. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
  147. package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +152 -133
  148. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +0 -11
  149. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  150. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +720 -621
  151. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
  152. package/src/shade/renderer/particles/graph/compile_particle_graph.js +8 -3
  153. package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -1
  154. package/src/shade/renderer/particles/graph/flatten_particle_graph.js +244 -213
  155. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
  156. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +6 -0
  157. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
  158. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +294 -319
  159. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -1
  160. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +524 -517
  161. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts +22 -0
  162. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts.map +1 -0
  163. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.js +517 -0
  164. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
  165. package/src/shade/renderer/particles/graph/groups/simplex_reference.js +185 -159
  166. package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -1
  167. package/src/shade/renderer/particles/graph/make_particle_group.js +2 -20
  168. package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts +17 -0
  169. package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts.map +1 -0
  170. package/src/shade/renderer/particles/graph/particle_compare_ops.js +22 -0
  171. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +28 -8
  172. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -1
  173. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +49 -25
  174. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +40 -0
  175. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
  176. package/src/shade/renderer/particles/graph/particle_node_parameters.js +312 -234
  177. package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
  178. package/src/shade/renderer/particles/graph/particle_node_presentation.js +59 -3
  179. package/src/shade/renderer/particles/graph/particle_ramp.d.ts +230 -0
  180. package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -0
  181. package/src/shade/renderer/particles/graph/particle_ramp.js +403 -0
  182. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +7 -1
  183. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -1
  184. package/src/shade/renderer/particles/graph/validate_particle_graph.js +66 -10
  185. package/src/shade/renderer/particles/graph_particles.d.ts +23 -69
  186. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  187. package/src/shade/renderer/particles/graph_particles.js +105 -262
  188. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  189. package/src/shade/renderer/particles/graph_particles_avboit.js +3 -2
  190. package/src/shade/renderer/particles/graph_particles_billboard.d.ts +37 -0
  191. package/src/shade/renderer/particles/graph_particles_billboard.d.ts.map +1 -0
  192. package/src/shade/renderer/particles/graph_particles_billboard.js +93 -0
  193. package/src/shade/renderer/particles/isa/InstructionStream.d.ts +51 -22
  194. package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
  195. package/src/shade/renderer/particles/isa/InstructionStream.js +98 -38
  196. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +49 -8
  197. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
  198. package/src/shade/renderer/particles/isa/ParticleProgram.js +95 -150
  199. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +306 -64
  200. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
  201. package/src/shade/renderer/particles/isa/ParticleVMISA.js +505 -136
  202. package/src/shade/renderer/particles/isa/particle_assembly.d.ts +36 -12
  203. package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
  204. package/src/shade/renderer/particles/isa/particle_assembly.js +354 -139
  205. package/src/shade/renderer/particles/isa/validate_particle_program.d.ts +9 -0
  206. package/src/shade/renderer/particles/isa/validate_particle_program.d.ts.map +1 -0
  207. package/src/shade/renderer/particles/isa/validate_particle_program.js +128 -0
  208. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +23 -0
  209. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
  210. package/src/shade/renderer/particles/layout/ParticleLayout.js +68 -0
  211. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts +77 -0
  212. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts.map +1 -0
  213. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.js +219 -0
  214. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts +83 -0
  215. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts.map +1 -0
  216. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.js +283 -0
  217. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +133 -0
  218. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts.map +1 -0
  219. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +334 -0
  220. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts +117 -0
  221. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts.map +1 -0
  222. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.js +70 -0
  223. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts +97 -0
  224. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts.map +1 -0
  225. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +462 -0
  226. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts +116 -0
  227. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts.map +1 -0
  228. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.js +687 -0
  229. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts +57 -0
  230. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts.map +1 -0
  231. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +860 -0
  232. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts +115 -0
  233. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts.map +1 -0
  234. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.js +264 -0
  235. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +81 -42
  236. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -1
  237. package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +477 -478
  238. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts +100 -0
  239. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts.map +1 -0
  240. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.js +236 -0
  241. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +139 -59
  242. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
  243. package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +764 -647
  244. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +40 -7
  245. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
  246. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +376 -213
  247. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +30 -3
  248. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
  249. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +337 -223
  250. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +24 -38
  251. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
  252. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +166 -206
  253. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts +99 -0
  254. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts.map +1 -0
  255. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.js +313 -0
  256. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +3 -2
  257. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -1
  258. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +198 -197
  259. package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -1
  260. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +19 -0
  261. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
  262. package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +678 -561
  263. package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts +22 -0
  264. package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts.map +1 -0
  265. package/src/shade/renderer/particles/optimizer/pass_inline_constants.js +221 -0
  266. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -1
  267. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +63 -72
  268. package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -1
  269. package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +87 -86
  270. package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -1
  271. package/src/shade/renderer/particles/optimizer/pass_peephole.js +392 -271
  272. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -1
  273. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +361 -268
  274. package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts +4 -0
  275. package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts.map +1 -0
  276. package/src/shade/renderer/particles/optimizer/pass_schedule.js +244 -0
  277. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -1
  278. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +154 -151
  279. package/src/shade/renderer/particles/particle_test_fixtures.d.ts +20 -0
  280. package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -1
  281. package/src/shade/renderer/particles/particle_test_fixtures.js +229 -203
  282. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +99 -25
  283. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
  284. package/src/shade/renderer/particles/runtime/EmitterRegistry.js +438 -286
  285. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +109 -0
  286. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -0
  287. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +127 -0
  288. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +87 -102
  289. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  290. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +184 -163
  291. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +26 -0
  292. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
  293. package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts +130 -0
  294. package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts.map +1 -0
  295. package/src/shade/renderer/particles/runtime/ProgramHeap.js +595 -0
  296. package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts +91 -0
  297. package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts.map +1 -0
  298. package/src/shade/renderer/particles/runtime/ProgramPlacement.js +106 -0
  299. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +5 -2
  300. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
  301. package/src/shade/renderer/particles/runtime/create_particle_effect.js +22 -19
  302. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts +28 -0
  303. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts.map +1 -0
  304. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.js +136 -0
  305. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts +35 -0
  306. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts.map +1 -0
  307. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.js +133 -0
  308. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -9
  309. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  310. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +20 -80
  311. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts +33 -0
  312. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -0
  313. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +114 -0
  314. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts +8 -0
  315. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  316. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +33 -101
  317. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts +45 -0
  318. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -0
  319. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +123 -0
  320. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts +51 -0
  321. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts.map +1 -0
  322. package/src/shade/renderer/particles/sort/graph_particle_sort.js +120 -0
  323. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts +6 -0
  324. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts.map +1 -0
  325. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.js +16 -0
  326. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +4 -1
  327. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  328. package/src/shade/renderer/particles/vm/ParticleVMReference.js +261 -140
  329. package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
  330. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +217 -255
  331. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts +43 -0
  332. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts.map +1 -0
  333. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.js +55 -0
  334. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts +62 -0
  335. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts.map +1 -0
  336. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.js +86 -0
  337. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts +22 -0
  338. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts.map +1 -0
  339. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.js +32 -0
  340. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts +9 -0
  341. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts.map +1 -0
  342. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.js +19 -0
  343. package/src/shade/renderer/shader/chunk/noise/psrdnoise/README.md +65 -0
  344. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts +24 -0
  345. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts.map +1 -0
  346. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.js +83 -0
  347. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts +25 -0
  348. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts.map +1 -0
  349. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.js +42 -0
  350. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts +31 -0
  351. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts.map +1 -0
  352. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.js +80 -0
  353. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts +26 -0
  354. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts.map +1 -0
  355. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.js +109 -0
  356. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts +38 -0
  357. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts.map +1 -0
  358. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.js +129 -0
  359. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts +21 -0
  360. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts.map +1 -0
  361. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.js +35 -0
  362. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts +19 -0
  363. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts.map +1 -0
  364. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.js +28 -0
  365. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts +13 -0
  366. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts.map +1 -0
  367. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.js +22 -0
  368. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts +22 -0
  369. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts.map +1 -0
  370. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.js +59 -0
  371. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts +23 -0
  372. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts.map +1 -0
  373. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.js +74 -0
  374. package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts +34 -1
  375. package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts.map +1 -1
  376. package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +41 -1
  377. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts +3 -2
  378. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  379. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +49 -10
  380. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  381. package/src/view/minimap/dom/MinimapCameraView.js +4 -9
  382. package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts +0 -15
  383. package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts.map +0 -1
  384. package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts +0 -45
  385. package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts.map +0 -1
  386. package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts +0 -24
  387. package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts.map +0 -1
  388. package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts +0 -19
  389. package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts.map +0 -1
  390. package/src/engine/ecs/transform/__probe.d.ts +0 -4
  391. package/src/engine/ecs/transform/__probe.d.ts.map +0 -1
  392. package/src/engine/ecs/util/hideEntityGracefully.d.ts +0 -9
  393. package/src/engine/ecs/util/hideEntityGracefully.d.ts.map +0 -1
  394. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +0 -27
  395. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +0 -1
  396. package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts +0 -14
  397. package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +0 -1
  398. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts +0 -156
  399. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts.map +0 -1
  400. package/src/shade/renderer/particles/runtime/Emitter.d.ts +0 -219
  401. package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +0 -1
  402. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +0 -77
  403. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +0 -1
  404. package/src/shade/renderer/particles/runtime/ProgramArena.js +0 -155
  405. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +0 -15
  406. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +0 -1
  407. package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts +0 -34
  408. package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts.map +0 -1
  409. package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +0 -32
  410. 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
+ }