@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
@@ -6,17 +6,28 @@ import {
6
6
  } from "../ParticleConstants.js";
7
7
  import { ParticleProgram } from "./ParticleProgram.js";
8
8
  import {
9
+ pack_swizzle,
10
+ swizzle_selector,
9
11
  VM_BUILTIN,
10
12
  VM_ISA,
13
+ VM_KIND,
11
14
  VM_MAX_WIDTH,
12
15
  VM_OP,
13
16
  VM_OPERAND,
17
+ VM_SWIZZLE_BROADCAST,
14
18
  VM_WIDTH,
19
+ vm_decode_dst,
20
+ vm_decode_kind,
15
21
  vm_decode_op,
16
22
  vm_decode_width,
17
- vm_encode_word0,
23
+ vm_f32_bits,
24
+ vm_f32_value,
25
+ vm_operand_base,
26
+ vm_operand_pattern,
18
27
  vm_operation_width,
19
- vm_visit_register_operands,
28
+ vm_pack_instruction,
29
+ vm_register,
30
+ vm_visit_register_runs,
20
31
  } from "./ParticleVMISA.js";
21
32
 
22
33
  /**
@@ -42,12 +53,13 @@ import {
42
53
  * .init ; section — everything until the next section directive
43
54
  * LOAD_BUILTIN.3 r0, EMITTER_POSITION
44
55
  * RANDOM.2 r1
45
- * SCALE.2 r1, r1, r2
56
+ * MUL.2 r1, r1, #0.5 ; a literal where a register could stand
46
57
  * STORE_ATTR r1, @1, 3 ; store r1 into the 3-component attribute at record word 1
47
58
  *
48
59
  * .update
49
60
  * LOAD_ATTR.1 r0, @7, 1
50
61
  * CMP_GE.1 r0, r0, r1
62
+ * MOV.3 r2, r0.xxx ; a register read through a swizzle: lane 0 three times
51
63
  * KILL r0
52
64
  * ```
53
65
  *
@@ -62,16 +74,24 @@ import {
62
74
  *
63
75
  * | role | spelling | example |
64
76
  * |---|---|---|
65
- * | register / destination | `r<slot>` | `r3` |
77
+ * | destination | `r<slot>` | `r3` |
78
+ * | register source | `r<slot>`, optionally `.<selectors>` | `r3`, `r3.yyy`, `r0.zyx` |
79
+ * | literal source | `#<float>` | `#1.5` |
66
80
  * | constant-pool word | `c<word>` | `c0` |
67
81
  * | attribute word offset | `@<word>` | `@7` |
68
82
  * | attribute component count | a bare integer `1..4` | `3` |
69
83
  * | builtin | a {@link VM_BUILTIN} name | `EMITTER_POSITION` |
70
84
  * | curve handle | `k<handle>` | `k2` |
71
- * | swizzle pattern | `.<four of xyzw>` | `.xxyz` |
72
85
  *
73
- * `STORE_ATTR` and `KILL` carry a *source* register in the destination slot — the ISA's own
74
- * encoding, spelled here the way it is encoded rather than reordered to read like a store.
86
+ * A register source's selectors say which lane of the value each lane of the instruction reads, one
87
+ * letter of `xyzw` per lane the instruction spans; left off, lane `c` reads lane `c`. A literal is
88
+ * the same value in every lane. The destination is only ever a plain register.
89
+ *
90
+ * ## What older text still means
91
+ *
92
+ * Two mnemonics that were opcodes are now spellings of an operand, and documents that carry them read
93
+ * as the instruction they meant: `SWIZZLE.n rd, rs, .xyzw` is `MOV.n rd, rs.xyzw`, and `SCALE.n rd,
94
+ * rv, rs` is `MUL.n rd, rv, rs.xxxx`. The disassembler never writes either.
75
95
  */
76
96
 
77
97
  /** Section directive introducing the INIT stream. @type {string} */
@@ -86,11 +106,12 @@ export const PARTICLE_ASSEMBLY_DIRECTIVE_REGISTERS = ".registers";
86
106
  /** Directive declaring one constant-pool entry. @type {string} */
87
107
  export const PARTICLE_ASSEMBLY_DIRECTIVE_CONST = ".const";
88
108
 
109
+ /** Opens a literal in a register slot: `#1.5`. @type {string} */
110
+ export const PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX = "#";
111
+
89
112
  /**
90
- * Separates a mnemonic from the lane count it spans: `ADD.3`.
91
- *
92
- * The same character opens a directive and a swizzle pattern, but never in this position — a line
93
- * starts with either a directive or a mnemonic, and a swizzle is an operand.
113
+ * Separates a mnemonic from the lane count it spans: `ADD.3`. Also separates a register from its
114
+ * selectors: `r3.yyy`. The same character opens a directive, but never in either of those positions.
94
115
  *
95
116
  * @type {string}
96
117
  */
@@ -104,18 +125,21 @@ export const PARTICLE_ASSEMBLY_WIDTH_SEPARATOR = ".";
104
125
  * a bound the parser tries to allocate that pool and takes the page with it, which is a worse answer
105
126
  * to a typo than a marker on the line.
106
127
  *
107
- * Generous against any real program: an effect's constants are its literals, and the whole
108
- * instruction budget is {@link PARTICLE_VM_MAX_INSTRUCTIONS} per section.
109
- *
110
128
  * @type {number}
111
129
  */
112
130
  export const PARTICLE_ASSEMBLY_MAX_CONSTANTS = 4096;
113
131
 
132
+ /** Lane selector characters, indexed by the 2-bit selector value a swizzle packs. @type {string} */
133
+ const SWIZZLE_LANES = "xyzw";
134
+
114
135
  /**
115
- * Lane selector characters, indexed by the 2-bit selector value the `SWIZZLE` opcode packs.
116
- * @type {string}
136
+ * Mnemonics that were opcodes and are now operand spellings, read for the documents that carry them.
137
+ * @type {Map<string, {op: number, form: string}>}
117
138
  */
118
- const SWIZZLE_LANES = "xyzw";
139
+ const LEGACY_MNEMONICS = new Map([
140
+ ["SWIZZLE", { op: VM_OP.MOV, form: "swizzle" }],
141
+ ["SCALE", { op: VM_OP.MUL, form: "scale" }],
142
+ ]);
119
143
 
120
144
  /**
121
145
  * Mnemonic -> opcode, derived from {@link VM_ISA} so the two cannot drift.
@@ -154,6 +178,10 @@ const BUILTIN_NAME_BY_ID = invert(VM_BUILTIN);
154
178
  * @property {string} message
155
179
  */
156
180
 
181
+ // ---------------------------------------------------------------------------------------------
182
+ // Disassembly
183
+ // ---------------------------------------------------------------------------------------------
184
+
157
185
  /**
158
186
  * Render a compiled program as assembly text.
159
187
  *
@@ -216,13 +244,14 @@ function disassemble_instruction(words, base) {
216
244
  if (meta === undefined) {
217
245
  // Not reachable from a validated program, but a disassembler that throws is useless for
218
246
  // looking at the thing that went wrong.
219
- return `; unknown opcode ${op} [${words[base + 1]}, ${words[base + 2]}, ${words[base + 3]}, ${words[base + 4]}]`;
247
+ return `; unknown opcode ${op} [${words[base + 1]}, ${words[base + 2]}, ${words[base + 3]}]`;
220
248
  }
221
249
 
250
+ const operation_width = vm_operation_width(op, width);
222
251
  const operands = [];
223
252
 
224
253
  if (meta.dst) {
225
- operands.push(`r${words[base + 1]}`);
254
+ operands.push(`r${vm_decode_dst(word0)}`);
226
255
  }
227
256
 
228
257
  for (let slot = 0; slot < 3; slot++) {
@@ -232,7 +261,15 @@ function disassemble_instruction(words, base) {
232
261
  continue;
233
262
  }
234
263
 
235
- operands.push(format_operand(role, words[base + 2 + slot]));
264
+ const word = words[base + 1 + slot];
265
+
266
+ if (role !== VM_OPERAND.REG) {
267
+ operands.push(format_operand(role, word));
268
+ } else if (vm_decode_kind(word0, slot) === VM_KIND.IMM) {
269
+ operands.push(`${PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX}${format_float(vm_f32_value(word))}`);
270
+ } else {
271
+ operands.push(format_register(word, meta.scalar_operands.includes(slot) ? 1 : operation_width));
272
+ }
236
273
  }
237
274
 
238
275
  const mnemonic = meta.name + format_width_suffix(op, meta, width);
@@ -262,15 +299,36 @@ function format_width_suffix(op, meta, width) {
262
299
  }
263
300
 
264
301
  /**
265
- * @param {string} role a {@link VM_OPERAND} value
302
+ * A register source: its slot, and the selectors of the lanes read wherever they are not the lane
303
+ * itself.
304
+ *
305
+ * @param {number} word a register operand
306
+ * @param {number} lanes lanes the instruction reads of it
307
+ * @returns {string}
308
+ */
309
+ function format_register(word, lanes) {
310
+ const pattern = vm_operand_pattern(word);
311
+
312
+ let identity = true;
313
+ let selectors = "";
314
+
315
+ for (let lane = 0; lane < lanes; lane++) {
316
+ const selector = swizzle_selector(pattern, lane);
317
+
318
+ identity = identity && selector === lane;
319
+ selectors += SWIZZLE_LANES.charAt(selector);
320
+ }
321
+
322
+ return `r${vm_operand_base(word)}${identity ? "" : PARTICLE_ASSEMBLY_WIDTH_SEPARATOR + selectors}`;
323
+ }
324
+
325
+ /**
326
+ * @param {string} role a {@link VM_OPERAND} value other than the register one
266
327
  * @param {number} value
267
328
  * @returns {string}
268
329
  */
269
330
  function format_operand(role, value) {
270
331
  switch (role) {
271
- case VM_OPERAND.REG:
272
- case VM_OPERAND.DST:
273
- return `r${value}`;
274
332
  case VM_OPERAND.CONST:
275
333
  return `c${value}`;
276
334
  case VM_OPERAND.ATTR_OFFSET:
@@ -281,27 +339,11 @@ function format_operand(role, value) {
281
339
  return BUILTIN_NAME_BY_ID.get(value) ?? String(value);
282
340
  case VM_OPERAND.CURVE:
283
341
  return `k${value}`;
284
- case VM_OPERAND.SWIZZLE:
285
- return format_swizzle(value);
286
342
  default:
287
343
  return String(value);
288
344
  }
289
345
  }
290
346
 
291
- /**
292
- * @param {number} packed four 2-bit lane selectors
293
- * @returns {string} `.xyzw` form
294
- */
295
- function format_swizzle(packed) {
296
- let result = ".";
297
-
298
- for (let lane = 0; lane < 4; lane++) {
299
- result += SWIZZLE_LANES.charAt((packed >>> (lane * 2)) & 3);
300
- }
301
-
302
- return result;
303
- }
304
-
305
347
  /**
306
348
  * A float spelled so it reads back as itself.
307
349
  *
@@ -335,6 +377,10 @@ function format_float(value) {
335
377
  return String(value);
336
378
  }
337
379
 
380
+ // ---------------------------------------------------------------------------------------------
381
+ // Parsing
382
+ // ---------------------------------------------------------------------------------------------
383
+
338
384
  /**
339
385
  * Read assembly text into a program, reporting every problem rather than stopping at the first.
340
386
  *
@@ -353,7 +399,7 @@ export function parse_particle_assembly(text) {
353
399
 
354
400
  const report = (line, message) => diagnostics.push({ line, message });
355
401
 
356
- /** @type {number[]} flat constant pool, 4 floats per entry */
402
+ /** @type {number[]} flat constant pool, one float per word */
357
403
  const constants = [];
358
404
  /** @type {Set<number>} constant indices a `.const` directive has already claimed */
359
405
  const declared_constants = new Set();
@@ -446,7 +492,7 @@ export function parse_particle_assembly(text) {
446
492
  }
447
493
 
448
494
  // dense constant indices: c0..cN-1 with nothing missing, so a pool index means the same thing
449
- // here as it does in the arena
495
+ // here as it does in the heap
450
496
  for (let i = 0; i < constants.length; i++) {
451
497
  if (!declared_constants.has(i)) {
452
498
  report(0, `Constant c${i} is referenced by the pool's length but never declared`);
@@ -456,7 +502,7 @@ export function parse_particle_assembly(text) {
456
502
  const register_count = declared_register_count === -1 ? register_high_water : declared_register_count;
457
503
 
458
504
  if (register_count > PARTICLE_VM_REGISTER_SLOTS) {
459
- report(0, `Register file is ${PARTICLE_VM_REGISTER_SLOTS} slots, the program asks for ${register_count}`);
505
+ report(0, `A VM instruction can address ${PARTICLE_VM_REGISTER_SLOTS} slots, the program asks for ${register_count}`);
460
506
  }
461
507
 
462
508
  const constant_word_count = constants.length;
@@ -532,11 +578,12 @@ function check_stream_references(stream, register_count, constant_word_count, re
532
578
  for (let i = 0; i < instruction_count; i++) {
533
579
  const base = i * PARTICLE_VM_INSTRUCTION_WORDS;
534
580
  const line = stream.lines[i];
535
- const meta = VM_ISA.get(vm_decode_op(words[base]));
581
+ const op = vm_decode_op(words[base]);
582
+ const meta = VM_ISA.get(op);
536
583
 
537
584
  // An operand of width w occupies w consecutive slots, so it is the END of the run that has
538
585
  // to be inside the file: `MOV.4 r30` starts inside a 32-slot file and finishes outside one.
539
- vm_visit_register_operands(words, base, (index, span) => {
586
+ vm_visit_register_runs(words, base, (index, span) => {
540
587
  if (index + span <= register_count) {
541
588
  return;
542
589
  }
@@ -550,29 +597,27 @@ function check_stream_references(stream, register_count, constant_word_count, re
550
597
  );
551
598
  });
552
599
 
553
- for (let slot = 0; slot < 3; slot++) {
554
- const role = meta.operands[slot];
555
- const value = words[base + 2 + slot];
556
-
557
- if (role === VM_OPERAND.CONST) {
558
- // The load reads one word per lane, so the whole run has to be inside the pool.
559
- const span = vm_operation_width(vm_decode_op(words[base]), vm_decode_width(words[base]));
600
+ if (op === VM_OP.LOAD_CONST) {
601
+ // The load reads one word per lane, so the whole run has to be inside the pool.
602
+ const first = words[base + 1];
603
+ const span = vm_operation_width(op, vm_decode_width(words[base]));
560
604
 
561
- if (value + span > constant_word_count) {
562
- report(
563
- line,
564
- `c${value} reads ${span} word${span === 1 ? "" : "s"}, past the end of the `
565
- + `${constant_word_count}-word constant pool`
566
- );
567
- }
605
+ if (first + span > constant_word_count) {
606
+ report(
607
+ line,
608
+ `c${first} reads ${span} word${span === 1 ? "" : "s"}, past the end of the `
609
+ + `${constant_word_count}-word constant pool`
610
+ );
568
611
  }
569
612
  }
570
613
 
571
614
  // An attribute access past the end of a record is not caught by GPU robustness: the address
572
615
  // still lands inside the pool buffer, and reads or corrupts the NEXT particle's record.
573
- if (meta.operands[0] === VM_OPERAND.ATTR_OFFSET) {
574
- const offset = words[base + 2];
575
- const components = words[base + 3];
616
+ const offset_slot = meta.operands.indexOf(VM_OPERAND.ATTR_OFFSET);
617
+
618
+ if (offset_slot !== -1) {
619
+ const offset = words[base + 1 + offset_slot];
620
+ const components = words[base + 2 + offset_slot];
576
621
 
577
622
  if (offset + components > PARTICLE_RECORD_WORD_COUNT) {
578
623
  report(
@@ -671,6 +716,14 @@ function parse_directive(line, line_number, report) {
671
716
  return null;
672
717
  }
673
718
 
719
+ /**
720
+ * @typedef {object} ParsedOperand
721
+ * @property {number} kind a {@link VM_KIND}: `REG` or `IMM` for a register-role slot, `NONE` for any other
722
+ * @property {number} word the operand word
723
+ * @property {number} [selectors] for a register written with selectors, how many the text spelled
724
+ * @property {string} [token] the text the operand was read from, for messages
725
+ */
726
+
674
727
  /**
675
728
  * @param {string} line already stripped of comments and trimmed
676
729
  * @param {number} line_number
@@ -688,7 +741,8 @@ function parse_instruction(line, line_number, report) {
688
741
  const mnemonic = separator === -1 ? head : head.slice(0, separator);
689
742
  const width_text = separator === -1 ? "" : head.slice(separator + 1);
690
743
 
691
- const opcode = OPCODE_BY_NAME.get(mnemonic);
744
+ const legacy = LEGACY_MNEMONICS.get(mnemonic) ?? null;
745
+ const opcode = legacy === null ? OPCODE_BY_NAME.get(mnemonic) : legacy.op;
692
746
 
693
747
  if (opcode === undefined) {
694
748
  report(line_number, `Unknown instruction '${mnemonic}'`);
@@ -723,11 +777,88 @@ function parse_instruction(line, line_number, report) {
723
777
 
724
778
  const tokens = rest.split(",").map(t => t.trim()).filter(t => t !== "");
725
779
 
726
- /** roles the operands fill, in the order they are written */
780
+ const parsed = legacy === null
781
+ ? parse_operands(meta, tokens, line_number, report)
782
+ : parse_legacy_operands(legacy.form, mnemonic, tokens, line_number, report);
783
+
784
+ if (parsed === null) {
785
+ return null;
786
+ }
787
+
788
+ const { dst, sources } = parsed;
789
+
790
+ // The attribute ops measure themselves in components unless the text says otherwise: a load may
791
+ // be widened past its attribute (the lanes beyond read zero), a store may not — it writes one
792
+ // record word per lane, so its width IS the component count.
793
+ if (opcode === VM_OP.LOAD_ATTR && width_text === "") {
794
+ width = sources[1].word;
795
+ }
796
+
797
+ if (opcode === VM_OP.STORE_ATTR) {
798
+ if (width_text !== "" && width !== sources[2].word) {
799
+ report(line_number, `STORE_ATTR width ${width} must equal its component count ${sources[2].word}`);
800
+ return null;
801
+ }
802
+
803
+ width = sources[2].word;
804
+ }
805
+
806
+ // Selectors are written one per lane read, and the lane count is known only now that the width
807
+ // is: `r6.xx` on a three-lane read would otherwise quietly be `r6.xxz`.
808
+ const operation_width = vm_operation_width(opcode, width);
809
+
810
+ for (let slot = 0; slot < sources.length; slot++) {
811
+ const source = sources[slot];
812
+ const lanes = meta.scalar_operands.includes(slot) ? 1 : operation_width;
813
+
814
+ if (source.kind === VM_KIND.REG && source.selectors !== undefined && source.selectors > 0 && source.selectors !== lanes) {
815
+ report(
816
+ line_number,
817
+ `${meta.name} reads ${lanes} lane${lanes === 1 ? "" : "s"} of '${source.token}', `
818
+ + `which names ${source.selectors} selector${source.selectors === 1 ? "" : "s"}; write one per lane`
819
+ );
820
+ return null;
821
+ }
822
+ }
823
+
824
+ const words = vm_pack_instruction(
825
+ opcode,
826
+ width,
827
+ dst,
828
+ sources.map(source => source.kind),
829
+ sources.map(source => source.word)
830
+ );
831
+
832
+ // Registers are scalar slots, so what the file has to hold is where the widest operand ENDS.
833
+ // Sizing it by the largest index written down leaves a `RANDOM.3 r0` writing r1 and r2 outside
834
+ // the file it derived, which the interpreter reads back as zero and the reference as nothing.
835
+ let register_reach = 0;
836
+
837
+ vm_visit_register_runs(words, 0, (slot, span) => {
838
+ if (slot + span > register_reach) {
839
+ register_reach = slot + span;
840
+ }
841
+ });
842
+
843
+ return { words, register_reach };
844
+ }
845
+
846
+ /**
847
+ * The operands of an ordinary instruction, in the order they are written: the destination when the
848
+ * opcode has one, then every source whose role is not `NONE`.
849
+ *
850
+ * @param {object} meta the opcode's {@link VM_ISA} entry
851
+ * @param {string[]} tokens
852
+ * @param {number} line_number
853
+ * @param {function(number, string):void} report
854
+ * @returns {{dst: number, sources: ParsedOperand[]}|null}
855
+ */
856
+ function parse_operands(meta, tokens, line_number, report) {
857
+ /** roles the written operands fill, in order */
727
858
  const roles = [];
728
859
 
729
860
  if (meta.dst) {
730
- roles.push(VM_OPERAND.DST);
861
+ roles.push("dst");
731
862
  }
732
863
 
733
864
  for (let slot = 0; slot < 3; slot++) {
@@ -745,86 +876,181 @@ function parse_instruction(line, line_number, report) {
745
876
  return null;
746
877
  }
747
878
 
748
- // Word 0 is filled in at the end, once the operands are known: the attribute ops take their
749
- // width from their component count.
750
- const words = [0, 0, 0, 0, 0];
751
-
879
+ let dst = 0;
880
+ const sources = [none(), none(), none()];
752
881
  let slot_cursor = 0;
753
882
 
754
883
  for (let i = 0; i < roles.length; i++) {
755
- const role = roles[i];
756
- const value = parse_operand(role, tokens[i], line_number, report);
757
-
758
- if (value === null) {
759
- return null;
760
- }
884
+ if (roles[i] === "dst") {
885
+ dst = parse_destination(tokens[i], line_number, report);
761
886
 
762
- if (i === 0 && meta.dst) {
763
- words[1] = value;
764
- } else {
765
- // source slots are written in ISA order, skipping the ones marked NONE
766
- while (meta.operands[slot_cursor] === VM_OPERAND.NONE) {
767
- slot_cursor++;
887
+ if (dst === null) {
888
+ return null;
768
889
  }
769
890
 
770
- words[2 + slot_cursor] = value;
891
+ continue;
892
+ }
893
+
894
+ // source slots are written in ISA order, skipping the ones marked NONE
895
+ while (meta.operands[slot_cursor] === VM_OPERAND.NONE) {
771
896
  slot_cursor++;
772
897
  }
898
+
899
+ const operand = roles[i] === VM_OPERAND.REG
900
+ ? parse_register_operand(tokens[i], line_number, report)
901
+ : parse_plain_operand(roles[i], tokens[i], line_number, report);
902
+
903
+ if (operand === null) {
904
+ return null;
905
+ }
906
+
907
+ sources[slot_cursor] = operand;
908
+ slot_cursor++;
773
909
  }
774
910
 
775
- // The attribute ops measure themselves in components unless the text says otherwise: a load may
776
- // be widened past its attribute (the lanes beyond read zero), a store may not — it writes one
777
- // record word per lane, so its width IS the component count.
778
- if (opcode === VM_OP.LOAD_ATTR && width_text === "") {
779
- width = words[3];
911
+ return { dst, sources };
912
+ }
913
+
914
+ /**
915
+ * The operands of a legacy mnemonic, rewritten as the instruction it now spells.
916
+ *
917
+ * `SWIZZLE rd, rs, .xyzw` reads its pattern into `rs`'s swizzle; `SCALE rd, rv, rs` reads `rs`
918
+ * through `.xxxx`. A literal in either source position is what it is — one value in every lane —
919
+ * and the pattern or broadcast changes nothing about it.
920
+ *
921
+ * @param {string} form `"swizzle"` or `"scale"`
922
+ * @param {string} mnemonic as written, for messages
923
+ * @param {string[]} tokens
924
+ * @param {number} line_number
925
+ * @param {function(number, string):void} report
926
+ * @returns {{dst: number, sources: ParsedOperand[]}|null}
927
+ */
928
+ function parse_legacy_operands(form, mnemonic, tokens, line_number, report) {
929
+ if (tokens.length !== 3) {
930
+ report(line_number, `${mnemonic} takes 3 operands, got ${tokens.length}`);
931
+ return null;
780
932
  }
781
933
 
782
- if (opcode === VM_OP.STORE_ATTR) {
783
- if (width_text !== "" && width !== words[3]) {
784
- report(line_number, `STORE_ATTR width ${width} must equal its component count ${words[3]}`);
934
+ const dst = parse_destination(tokens[0], line_number, report);
935
+
936
+ if (dst === null) {
937
+ return null;
938
+ }
939
+
940
+ if (form === "swizzle") {
941
+ const source = parse_register_operand(tokens[1], line_number, report);
942
+ const pattern = parse_swizzle_pattern(tokens[2]);
943
+
944
+ if (source === null) {
785
945
  return null;
786
946
  }
787
947
 
788
- width = words[3];
948
+ if (pattern === null) {
949
+ report(line_number, `Expected a swizzle like '.xyzw', got '${tokens[2]}'`);
950
+ return null;
951
+ }
952
+
953
+ return { dst, sources: [with_pattern(source, pattern), none(), none()] };
789
954
  }
790
955
 
791
- words[0] = vm_encode_word0(opcode, width);
956
+ const vector = parse_register_operand(tokens[1], line_number, report);
957
+ const scalar = parse_register_operand(tokens[2], line_number, report);
792
958
 
793
- // Registers are scalar slots, so what the file has to hold is where the widest operand ENDS.
794
- // Sizing it by the largest index written down leaves a `RANDOM.3 r0` writing r1 and r2 outside
795
- // the file it derived, which the interpreter reads back as zero and the reference as nothing.
796
- let register_reach = 0;
959
+ if (vector === null || scalar === null) {
960
+ return null;
961
+ }
797
962
 
798
- vm_visit_register_operands(words, 0, (slot, span) => {
799
- if (slot + span > register_reach) {
800
- register_reach = slot + span;
801
- }
802
- });
963
+ return { dst, sources: [vector, with_pattern(scalar, VM_SWIZZLE_BROADCAST), none()] };
964
+ }
803
965
 
804
- return { words, register_reach };
966
+ /** @returns {ParsedOperand} the operand of an unused slot */
967
+ function none() {
968
+ return { kind: VM_KIND.NONE, word: 0 };
969
+ }
970
+
971
+ /**
972
+ * @param {ParsedOperand} operand
973
+ * @param {number} pattern
974
+ * @returns {ParsedOperand} a register operand read through `pattern`; a literal, unchanged
975
+ */
976
+ function with_pattern(operand, pattern) {
977
+ return operand.kind === VM_KIND.REG
978
+ ? { kind: VM_KIND.REG, word: vm_register(vm_operand_base(operand.word), pattern), token: operand.token }
979
+ : operand;
805
980
  }
806
981
 
807
982
  /**
808
- * @param {string} role
809
983
  * @param {string} token
810
984
  * @param {number} line_number
811
985
  * @param {function(number, string):void} report
812
- * @returns {number|null}
986
+ * @returns {number|null} the destination register
813
987
  */
814
- function parse_operand(role, token, line_number, report) {
815
- switch (role) {
816
- case VM_OPERAND.DST:
817
- case VM_OPERAND.REG: {
818
- const index = parse_prefixed(token, "r");
988
+ function parse_destination(token, line_number, report) {
989
+ const index = parse_prefixed(token, "r");
819
990
 
820
- if (index === null) {
821
- report(line_number, `Expected a register like 'r0', got '${token}'`);
822
- return null;
823
- }
991
+ if (index === null) {
992
+ report(line_number, `A destination is a register like 'r0', got '${token}'`);
993
+ return null;
994
+ }
995
+
996
+ return index;
997
+ }
998
+
999
+ /**
1000
+ * An operand in a register slot: a register, optionally read through selectors, or a literal.
1001
+ *
1002
+ * @param {string} token
1003
+ * @param {number} line_number
1004
+ * @param {function(number, string):void} report
1005
+ * @returns {ParsedOperand|null}
1006
+ */
1007
+ function parse_register_operand(token, line_number, report) {
1008
+ const register = /^r(\d+)(?:\.([xyzw]{1,4}))?$/i.exec(token);
1009
+
1010
+ if (register !== null) {
1011
+ const selectors = register[2] ?? "";
1012
+ const lanes = [0, 1, 2, 3].map(lane => (lane < selectors.length
1013
+ ? SWIZZLE_LANES.indexOf(selectors.charAt(lane).toLowerCase())
1014
+ : lane));
1015
+
1016
+ return {
1017
+ kind: VM_KIND.REG,
1018
+ word: vm_register(Number.parseInt(register[1], 10), pack_swizzle(lanes[0], lanes[1], lanes[2], lanes[3])),
1019
+ selectors: selectors.length,
1020
+ token,
1021
+ };
1022
+ }
824
1023
 
825
- return index;
1024
+ if (token.startsWith(PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX)) {
1025
+ const value = parse_float(token.slice(PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX.length));
1026
+
1027
+ if (value === null) {
1028
+ report(line_number, `Expected a literal like '${PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX}1.5', got '${token}'`);
1029
+ return null;
826
1030
  }
827
1031
 
1032
+ // The word IS the f32: what a Float32Array would hold for this value, read back as bits.
1033
+ return { kind: VM_KIND.IMM, word: vm_f32_bits(value) };
1034
+ }
1035
+
1036
+ report(
1037
+ line_number,
1038
+ `Expected a register like 'r0' or 'r0.xxy', or a literal like `
1039
+ + `'${PARTICLE_ASSEMBLY_IMMEDIATE_PREFIX}1.5', got '${token}'`
1040
+ );
1041
+
1042
+ return null;
1043
+ }
1044
+
1045
+ /**
1046
+ * @param {string} role a role other than the register one
1047
+ * @param {string} token
1048
+ * @param {number} line_number
1049
+ * @param {function(number, string):void} report
1050
+ * @returns {ParsedOperand|null}
1051
+ */
1052
+ function parse_plain_operand(role, token, line_number, report) {
1053
+ switch (role) {
828
1054
  case VM_OPERAND.CONST: {
829
1055
  const index = parse_prefixed(token, "c");
830
1056
 
@@ -833,23 +1059,18 @@ function parse_operand(role, token, line_number, report) {
833
1059
  return null;
834
1060
  }
835
1061
 
836
- return index;
1062
+ return { kind: VM_KIND.NONE, word: index };
837
1063
  }
838
1064
 
839
1065
  case VM_OPERAND.ATTR_OFFSET: {
840
- if (!token.startsWith("@")) {
841
- report(line_number, `Expected an attribute offset like '@1', got '${token}'`);
842
- return null;
843
- }
844
-
845
- const offset = parse_unsigned(token.slice(1));
1066
+ const offset = token.startsWith("@") ? parse_unsigned(token.slice(1)) : null;
846
1067
 
847
1068
  if (offset === null) {
848
1069
  report(line_number, `Expected an attribute offset like '@1', got '${token}'`);
849
1070
  return null;
850
1071
  }
851
1072
 
852
- return offset;
1073
+ return { kind: VM_KIND.NONE, word: offset };
853
1074
  }
854
1075
 
855
1076
  case VM_OPERAND.COMP: {
@@ -860,11 +1081,14 @@ function parse_operand(role, token, line_number, report) {
860
1081
  return null;
861
1082
  }
862
1083
 
863
- return components;
1084
+ return { kind: VM_KIND.NONE, word: components };
864
1085
  }
865
1086
 
866
- case VM_OPERAND.BUILTIN:
867
- return parse_enum_operand(token, VM_BUILTIN, "builtin", line_number, report);
1087
+ case VM_OPERAND.BUILTIN: {
1088
+ const id = parse_enum_operand(token, VM_BUILTIN, "builtin", line_number, report);
1089
+
1090
+ return id === null ? null : { kind: VM_KIND.NONE, word: id };
1091
+ }
868
1092
 
869
1093
  case VM_OPERAND.CURVE: {
870
1094
  const handle = parse_prefixed(token, "k");
@@ -874,18 +1098,7 @@ function parse_operand(role, token, line_number, report) {
874
1098
  return null;
875
1099
  }
876
1100
 
877
- return handle;
878
- }
879
-
880
- case VM_OPERAND.SWIZZLE: {
881
- const packed = parse_swizzle(token);
882
-
883
- if (packed === null) {
884
- report(line_number, `Expected a swizzle like '.xyzw', got '${token}'`);
885
- return null;
886
- }
887
-
888
- return packed;
1101
+ return { kind: VM_KIND.NONE, word: handle };
889
1102
  }
890
1103
 
891
1104
  default:
@@ -924,10 +1137,12 @@ function parse_enum_operand(token, enumeration, label, line_number, report) {
924
1137
  }
925
1138
 
926
1139
  /**
1140
+ * A legacy `SWIZZLE` pattern: exactly four selectors behind a dot.
1141
+ *
927
1142
  * @param {string} token
928
- * @returns {number|null} the packed 8-bit pattern
1143
+ * @returns {number|null} the packed pattern
929
1144
  */
930
- function parse_swizzle(token) {
1145
+ function parse_swizzle_pattern(token) {
931
1146
  if (token.length !== 5 || !token.startsWith(".")) {
932
1147
  return null;
933
1148
  }