@woosh/meep-engine 2.165.7 → 2.167.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 (146) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/package.json +1 -1
  3. package/src/CORRECTNESS_HUNT_2026_07_24.md +142 -0
  4. package/src/core/binary/BinaryBuffer.js +1 -1
  5. package/src/core/binary/EncodingBinaryBuffer.d.ts.map +1 -1
  6. package/src/core/binary/EncodingBinaryBuffer.js +3 -1
  7. package/src/core/cache/Cache.d.ts.map +1 -1
  8. package/src/core/cache/Cache.js +0 -4
  9. package/src/core/cache/CacheElement.d.ts.map +1 -1
  10. package/src/core/cache/CacheElement.js +4 -0
  11. package/src/core/cache/FrequencySketch.d.ts +0 -21
  12. package/src/core/cache/FrequencySketch.d.ts.map +1 -1
  13. package/src/core/cache/FrequencySketch.js +66 -65
  14. package/src/core/cache/wtinylfu/CacheElementWTinylfu.d.ts +17 -0
  15. package/src/core/cache/wtinylfu/CacheElementWTinylfu.d.ts.map +1 -0
  16. package/src/core/cache/wtinylfu/CacheElementWTinylfu.js +18 -0
  17. package/src/core/cache/wtinylfu/CacheRegionType.d.ts +10 -0
  18. package/src/core/cache/wtinylfu/CacheRegionType.d.ts.map +1 -0
  19. package/src/core/cache/wtinylfu/CacheRegionType.js +10 -0
  20. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts +66 -0
  21. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts.map +1 -0
  22. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.js +183 -0
  23. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts +279 -0
  24. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -0
  25. package/src/core/cache/wtinylfu/CacheWTinylfu.js +807 -0
  26. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +243 -0
  27. package/src/core/cache/wtinylfu/wtinylfuDecisions.d.ts +80 -0
  28. package/src/core/cache/wtinylfu/wtinylfuDecisions.d.ts.map +1 -0
  29. package/src/core/cache/wtinylfu/wtinylfuDecisions.js +102 -0
  30. package/src/core/collection/RingBuffer.d.ts.map +1 -1
  31. package/src/core/collection/RingBuffer.js +15 -3
  32. package/src/core/collection/map/HashMap.d.ts +1 -1
  33. package/src/core/collection/map/HashMap.d.ts.map +1 -1
  34. package/src/core/collection/map/HashMap.js +5 -17
  35. package/src/core/geom/2d/line/line2_compute_segment_point_distance_sqr.d.ts.map +1 -1
  36. package/src/core/geom/2d/line/line2_compute_segment_point_distance_sqr.js +5 -0
  37. package/src/core/geom/ConicRay.d.ts.map +1 -1
  38. package/src/core/geom/ConicRay.js +9 -2
  39. package/src/core/geom/Quaternion.d.ts.map +1 -1
  40. package/src/core/geom/Quaternion.js +7 -3
  41. package/src/core/math/build_gaussian_kernel_1d.d.ts +2 -2
  42. package/src/core/math/build_gaussian_kernel_1d.d.ts.map +1 -1
  43. package/src/core/math/build_gaussian_kernel_1d.js +4 -2
  44. package/src/core/math/build_gaussian_kernel_2d.d.ts +2 -2
  45. package/src/core/math/build_gaussian_kernel_2d.d.ts.map +1 -1
  46. package/src/core/math/build_gaussian_kernel_2d.js +4 -2
  47. package/src/core/math/gaussian.d.ts +2 -2
  48. package/src/core/math/gaussian.js +2 -2
  49. package/src/core/math/spline/spline3_bezier_bounds.d.ts.map +1 -1
  50. package/src/core/math/spline/spline3_bezier_bounds.js +10 -3
  51. package/src/core/math/spline/spline3_bezier_to_hermite.d.ts +8 -7
  52. package/src/core/math/spline/spline3_bezier_to_hermite.d.ts.map +1 -1
  53. package/src/core/math/spline/spline3_bezier_to_hermite.js +13 -12
  54. package/src/core/math/spline/spline3_hermite_bounds.d.ts.map +1 -1
  55. package/src/core/math/spline/spline3_hermite_bounds.js +10 -3
  56. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -1
  57. package/src/core/math/spline/spline3_hermite_bounds_t.js +115 -109
  58. package/src/core/math/spline/spline3_hermite_to_bezier.d.ts +2 -0
  59. package/src/core/math/spline/spline3_hermite_to_bezier.d.ts.map +1 -1
  60. package/src/core/math/spline/spline3_hermite_to_bezier.js +2 -0
  61. package/src/core/math/statistics/computeStatisticalMean.js +1 -1
  62. package/src/core/math/statistics/gaussian_amplitude.d.ts.map +1 -1
  63. package/src/core/math/statistics/gaussian_amplitude.js +3 -1
  64. package/src/engine/animation/clip/ecd_bind_animation_curve.d.ts.map +1 -1
  65. package/src/engine/animation/clip/ecd_bind_animation_curve.js +10 -1
  66. package/src/engine/animation/clip/hemisphere_align_quaternion_values.d.ts +20 -0
  67. package/src/engine/animation/clip/hemisphere_align_quaternion_values.d.ts.map +1 -0
  68. package/src/engine/animation/clip/hemisphere_align_quaternion_values.js +37 -0
  69. package/src/engine/animation/keyed2/AnimationTrackPlayback.d.ts.map +1 -1
  70. package/src/engine/animation/keyed2/AnimationTrackPlayback.js +5 -1
  71. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  72. package/src/engine/ecs/EntityComponentDataset.js +5 -21
  73. package/src/engine/ecs/fow/FogOfWar.d.ts +22 -0
  74. package/src/engine/ecs/fow/FogOfWar.d.ts.map +1 -1
  75. package/src/engine/ecs/fow/FogOfWar.js +202 -54
  76. package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts +16 -0
  77. package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts.map +1 -0
  78. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +39 -0
  79. package/src/engine/ecs/fow/FogOfWarSystem.d.ts.map +1 -1
  80. package/src/engine/ecs/fow/FogOfWarSystem.js +1 -0
  81. package/src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.d.ts.map +1 -1
  82. package/src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.js +3 -0
  83. package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts +7 -0
  84. package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts.map +1 -1
  85. package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +155 -145
  86. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.d.ts.map +1 -1
  87. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +33 -8
  88. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts.map +1 -1
  89. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js +3 -1
  90. package/src/engine/graphics/GraphicsEngine.d.ts +5 -1
  91. package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
  92. package/src/engine/graphics/GraphicsEngine.js +14 -3
  93. package/src/engine/graphics/composit/CompositLayer.d.ts.map +1 -1
  94. package/src/engine/graphics/composit/CompositLayer.js +258 -255
  95. package/src/engine/graphics/composit/LayerCompositer.d.ts +12 -0
  96. package/src/engine/graphics/composit/LayerCompositer.d.ts.map +1 -1
  97. package/src/engine/graphics/composit/LayerCompositer.js +19 -10
  98. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.d.ts +1 -1
  99. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.d.ts.map +1 -1
  100. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +10 -4
  101. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.d.ts +6 -0
  102. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.d.ts.map +1 -1
  103. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js +21 -0
  104. package/src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.d.ts.map +1 -1
  105. package/src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.js +5 -23
  106. package/src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.d.ts.map +1 -1
  107. package/src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js +6 -4
  108. package/src/engine/graphics/ecs/water/WaterSystem.d.ts.map +1 -1
  109. package/src/engine/graphics/ecs/water/WaterSystem.js +9 -3
  110. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.d.ts +12 -0
  111. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.d.ts.map +1 -1
  112. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.js +46 -9
  113. package/src/engine/graphics/particles/particular/engine/ParticularEngine.d.ts +5 -0
  114. package/src/engine/graphics/particles/particular/engine/ParticularEngine.d.ts.map +1 -1
  115. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +8 -0
  116. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.d.ts +7 -0
  117. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.d.ts.map +1 -1
  118. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.js +8 -0
  119. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.d.ts +10 -2
  120. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.d.ts.map +1 -1
  121. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +42 -10
  122. package/src/engine/graphics/render/buffer/FrameBuffer.d.ts.map +1 -1
  123. package/src/engine/graphics/render/buffer/FrameBuffer.js +5 -2
  124. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts +13 -1
  125. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  126. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +40 -7
  127. package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.d.ts.map +1 -1
  128. package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.js +10 -4
  129. package/src/engine/graphics/render/forward_plus/LightManager.d.ts.map +1 -1
  130. package/src/engine/graphics/render/forward_plus/LightManager.js +1235 -1232
  131. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.d.ts +5 -0
  132. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.d.ts.map +1 -1
  133. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.js +11 -10
  134. package/src/engine/graphics/trail/tube/TubeX.d.ts.map +1 -1
  135. package/src/engine/graphics/trail/tube/TubeX.js +557 -555
  136. package/src/engine/graphics/trail/x/RibbonXPlugin.d.ts +12 -1
  137. package/src/engine/graphics/trail/x/RibbonXPlugin.d.ts.map +1 -1
  138. package/src/engine/graphics/trail/x/RibbonXPlugin.js +15 -8
  139. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts.map +1 -1
  140. package/src/engine/input/ecs/systems/InputControllerSystem.js +9 -6
  141. package/src/engine/navigation/ecs/components/Path.d.ts +5 -2
  142. package/src/engine/navigation/ecs/components/Path.d.ts.map +1 -1
  143. package/src/engine/navigation/ecs/components/Path.js +10 -4
  144. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +1 -1
  145. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  146. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"OutlineRenderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js"],"names":[],"mappings":"AA+JA;IAQQ;;;OAGG;IACH,wBAAoB;IAEpB;;;OAGG;IACH,eAAkB;IAElB;;;OAGG;IACH,OAFU,KAAK,CAEE;IAEjB;;;;OAIG;IACH,gCAGE;IAEF,gCAA6D;IAC7D,kBAA6B;IAE7B,kCAAoD;IACpD,kCAA6C;IAC7C,gCAAuC;IAEvC,qDAAsF;IAGtF,kBAA6B;IAK7B;;;OAGG;IACH,eAFU,OAAO,CAEQ;IAEzB,sCAMC;IASD,6CAOE;IAEF,6CAOE;IAEF,6CAOE;IAEF,uCAOE;IAEF,uCAOE;IASF;;;;OAIG;IACH,yBAAyB;IAEzB;;;;OAIG;IACH,2BAA2B;IAG3B;;;;OAIG;IACH,4BAA+B;IAGnC,2BAEC;IAED;;;OAGG;IACH,gDAEC;IAED;;;;OAIG;IACH,UAHW,MAAM,KACN,MAAM,QA2BhB;IAED;;;OAGG;IACH,kBAFW,oBAAkB,kBAAkB,QAI9C;IAED;;;;OAIG;IACH,sCAyBC;IAED;;;;OAIG;IACH,6BAmBC;IAED;;;OAGG;IACH,mDAiDC;IAhDG,sCAA4B;IAkDhC;;;OAGG;IACH,2BAFW,MAAM,QA0ChB;IAED;;;;;OAKG;IACH,8BAyBC;IAED,wBAEC;IAED;;OAEG;IACH,uBAIC;IAED;;;;;OAKG;IACH,0CAgBC;IAED,wBAEC;IAGD;;;OAGG;IACH,0CAqBC;IAED,0BAeC;CACJ;sBAxmBM,OAAO;mCAAP,OAAO;+BAAP,OAAO;oCAAP,OAAO;qBAAP,OAAO;4BAAP,OAAO;kCAAP,OAAO"}
1
+ {"version":3,"file":"OutlineRenderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js"],"names":[],"mappings":"AA+JA;IAQQ;;;OAGG;IACH,wBAAoB;IAEpB;;;OAGG;IACH,eAAkB;IAElB;;;OAGG;IACH,OAFU,KAAK,CAEE;IAEjB;;;;OAIG;IACH,gCAGE;IAEF,gCAA6D;IAC7D,kBAA6B;IAE7B,kCAAoD;IACpD,kCAA6C;IAC7C,gCAAuC;IAEvC,qDAAsF;IAGtF,kBAA6B;IAK7B;;;OAGG;IACH,eAFU,OAAO,CAEQ;IAEzB,sCAMC;IASD,6CAOE;IAEF,6CAOE;IAEF,6CAOE;IAEF,uCAOE;IAEF,uCAOE;IASF;;;;OAIG;IACH,yBAAyB;IAEzB;;;;OAIG;IACH,2BAA2B;IAG3B;;;;OAIG;IACH,4BAA+B;IAGnC,2BAEC;IAED;;;OAGG;IACH,gDAEC;IAED;;;;OAIG;IACH,UAHW,MAAM,KACN,MAAM,QA2BhB;IAED;;;OAGG;IACH,kBAFW,oBAAkB,kBAAkB,QAI9C;IAED;;;;OAIG;IACH,sCAyBC;IAED;;;;OAIG;IACH,6BAmBC;IAED;;;OAGG;IACH,mDAiDC;IAhDG,sCAA4B;IAkDhC;;;OAGG;IACH,2BAFW,MAAM,QA0ChB;IAED;;;;;OAKG;IACH,8BAyBC;IAED,wBAEC;IAED;;OAEG;IACH,uBAIC;IAED;;;;;OAKG;IACH,0CAgBC;IAED,wBAEC;IAGD;;;OAGG;IACH,0CAqBC;IAED,0BAeC;IAED;;;;OAIG;IACH,gBAcC;CACJ;sBA7nBM,OAAO;mCAAP,OAAO;+BAAP,OAAO;oCAAP,OAAO;qBAAP,OAAO;4BAAP,OAAO;kCAAP,OAAO"}
@@ -633,4 +633,25 @@ export class OutlineRenderer {
633
633
 
634
634
  this.isTargetClear = true;
635
635
  }
636
+
637
+ /**
638
+ * Release every GPU resource owned by this renderer. The objects themselves stay usable: three.js
639
+ * re-uploads a disposed texture/target/program on next use, so the renderer can be resized and
640
+ * rendered again afterwards.
641
+ */
642
+ dispose() {
643
+ this.__renderTargetObjectId_t0.dispose();
644
+ this.__renderTargetObjectId_t1.dispose();
645
+ this.__renderTargetObjectId_t2.dispose();
646
+ this.__renderTargetColor.dispose();
647
+ this.__renderTargetFinal.dispose();
648
+
649
+ this.__textureParametersColor.dispose();
650
+
651
+ this.__material_decode.dispose();
652
+ this.__material_dilate.dispose();
653
+ this.__material_blur.dispose();
654
+
655
+ this.__ss_mesh.geometry.dispose();
656
+ }
636
657
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RibbonPathBuilder.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.js"],"names":[],"mappings":"AAaA;IAEQ;;;OAGG;IACH,OAFU,kBAAgB,IAAI,CAEb;IAEjB;;;OAGG;IACH,MAFU,OAAK,IAAI,CAEH;IAEhB;;;OAGG;IACH,QAFU,gBAAc,IAAI,CAEV;IAGtB;;;OAGG;IACH,uCAEC;IAED;;;OAGG;IACH,0BAEC;IAED;;;OAGG;IACH,uCAEC;IAED;;;OAGG;IACH,mBAFW,MAAM,EAAE,QAsFlB;CAEJ;mBAnJkB,2BAA2B"}
1
+ {"version":3,"file":"RibbonPathBuilder.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.js"],"names":[],"mappings":"AAYA;IAEQ;;;OAGG;IACH,OAFU,kBAAgB,IAAI,CAEb;IAEjB;;;OAGG;IACH,MAFU,OAAK,IAAI,CAEH;IAEhB;;;OAGG;IACH,QAFU,gBAAc,IAAI,CAEV;IAGtB;;;OAGG;IACH,uCAEC;IAED;;;OAGG;IACH,0BAEC;IAED;;;OAGG;IACH,uCAEC;IAED;;;OAGG;IACH,mBAFW,MAAM,EAAE,QAqElB;CAEJ;mBAjIkB,2BAA2B"}
@@ -1,6 +1,5 @@
1
1
  import Vector3 from "../../../../../core/geom/Vector3.js";
2
2
  import Entity from "../../../../ecs/Entity.js";
3
- import { EventType } from "../../../../ecs/EventType.js";
4
3
  import Renderable from "../../../../ecs/renderable/Renderable.js";
5
4
  import { RenderableFlags } from "../../../../ecs/renderable/RenderableFlags.js";
6
5
  import { Transform } from "../../../../ecs/transform/Transform.js";
@@ -104,11 +103,13 @@ export class RibbonPathBuilder {
104
103
 
105
104
 
106
105
  const plugin = this.plugin;
107
- const engine = plugin.engine;
108
-
109
- const graphics = engine.graphics;
110
106
 
111
107
  const geometry = ribbon.getGeometry();
108
+
109
+ // the `resolution` uniform on this material is not ours to write: every material the plugin
110
+ // hands out shares one uniform object, which the plugin holds on the render resolution for
111
+ // its whole lifetime. Tracking it here as well meant one extra viewport subscription per
112
+ // ribbon entity, all writing that same object, and left the value to whichever fired last
112
113
  const material = plugin.obtain_material_x(style.material);
113
114
 
114
115
  const mesh = ThreeFactory.createMesh(geometry, material);
@@ -124,25 +125,6 @@ export class RibbonPathBuilder {
124
125
  .add(renderable)
125
126
  .add(new Transform());
126
127
 
127
- function setResolution() {
128
- material.uniforms.resolution.value.copy(graphics.viewport.size);
129
- }
130
-
131
- function handleEntityRemoved() {
132
-
133
- graphics.viewport.size.onChanged.remove(setResolution);
134
-
135
- }
136
-
137
- entityBuilder.on.built.add(() => {
138
- // set resolution
139
- setResolution();
140
-
141
- graphics.viewport.size.onChanged.add(setResolution);
142
-
143
- entityBuilder.addEventListener(EventType.EntityRemoved, handleEntityRemoved);
144
- });
145
-
146
128
  destination.push(entityBuilder);
147
129
  }
148
130
 
@@ -1 +1 @@
1
- {"version":3,"file":"estimatePathViaIterativeIntegral.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,sEAFW,MAAM,UA6BhB"}
1
+ {"version":3,"file":"estimatePathViaIterativeIntegral.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,sEAFW,MAAM,UA+BhB"}
@@ -19,14 +19,16 @@ export function estimatePathViaIterativeIntegral(path, epsilon) {
19
19
  }
20
20
 
21
21
  for (let p = epsilon; ; p += epsilon) {
22
- const ended = path.sample(p1, p);
22
+ // sample returns false once the offset is clamped to the last point (offset >= length)
23
+ const on_path = path.sample(p1, p);
23
24
 
24
- if (ended) {
25
+ // close the current segment either way; the final iteration adds the last piece up to the end point
26
+ sum += p0.distanceTo(p1);
27
+
28
+ if (!on_path) {
25
29
  break;
26
30
  }
27
31
 
28
- sum += p0.distanceTo(p1);
29
-
30
32
  const swap = p1;
31
33
 
32
34
  p1 = p0;
@@ -1 +1 @@
1
- {"version":3,"file":"WaterSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/water/WaterSystem.js"],"names":[],"mappings":";AAaA;IAyBI;;;;OAIG;IACH,sCAmBC;IAhDD,+BAAuB;IAEvB;;;OAGG;IACH,cAFU,GAAG,CAEG;IAGhB,cAAY;IAEZ;;;OAGG;IACH,aAFU,KAAK,EAAE,CAEA;IAEjB;;;;OAIG;IACH,qBAAiB;IAWb,+BAA8B;IAE9B;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEH;IAEvB;;;;OAIG;IACH,wBAA2B;IAK/B,4CASC;IAED,2CAkEC;IAED;;;;OAIG;IACH,gBAHW,KAAK,UACL,MAAM,QAsDhB;IAED;;;;OAIG;IACH,kBAHW,KAAK,UACL,MAAM,QAgBhB;IAED,2BAgCC;IAED;;;;;OAKG;IACH,0BAUC;IAED,6BAcC;CACJ;;;;uBA/RsB,wBAAwB;kBAM7B,YAAY;oBARV,mCAAmC"}
1
+ {"version":3,"file":"WaterSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/water/WaterSystem.js"],"names":[],"mappings":";AAaA;IAyBI;;;;OAIG;IACH,sCAmBC;IAhDD,+BAAuB;IAEvB;;;OAGG;IACH,cAFU,GAAG,CAEG;IAGhB,cAAY;IAEZ;;;OAGG;IACH,aAFU,KAAK,EAAE,CAEA;IAEjB;;;;OAIG;IACH,qBAAiB;IAWb,+BAA8B;IAE9B;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEH;IAEvB;;;;OAIG;IACH,wBAA2B;IAK/B,4CASC;IAED,2CAwEC;IAED;;;;OAIG;IACH,gBAHW,KAAK,UACL,MAAM,QAsDhB;IAED;;;;OAIG;IACH,kBAHW,KAAK,UACL,MAAM,QAgBhB;IAED,2BAgCC;IAED;;;;;OAKG;IACH,0BAUC;IAED,6BAcC;CACJ;;;;uBArSsB,wBAAwB;kBAM7B,YAAY;oBARV,mCAAmC"}
@@ -81,7 +81,12 @@ class WaterSystem extends System {
81
81
 
82
82
  this.renderLayer = graphicsEngine.layers.create(WaterSystem.RENDER_LAYER_NAME);
83
83
 
84
- const viewport_size = graphicsEngine.viewport.size;
84
+ // the water shader reads the shared depth attachment at `gl_FragCoord.xy / vScreenResolution`,
85
+ // so the uniform has to name that attachment's own texel grid. `output_resolution` is the value
86
+ // the frame buffers are sized from, whole texels and all; recomputing it here from the CSS
87
+ // viewport size and a freshly read `window.devicePixelRatio` reproduced it only up to rounding,
88
+ // and only while the DPR had not moved since the last resize
89
+ const output_resolution = graphicsEngine.output_resolution;
85
90
 
86
91
  this.renderLayer.buildVisibleSet = make_bvh_visibility_builder(
87
92
  this.entityManager, this.bvh,
@@ -125,7 +130,8 @@ class WaterSystem extends System {
125
130
  return;
126
131
  }
127
132
 
128
- const pixelRatio = graphicsEngine.computeTotalPixelRatio();
133
+ const resolution_x = output_resolution.x;
134
+ const resolution_y = output_resolution.y;
129
135
 
130
136
  dataset.traverseComponents(Water, function (component, entity) {
131
137
  const shader = component.__shader;
@@ -133,7 +139,7 @@ class WaterSystem extends System {
133
139
  shader.uniforms.fCameraNear.value = camera.near;
134
140
  shader.uniforms.fCameraFar.value = camera.far;
135
141
 
136
- shader.uniforms.vScreenResolution.value.set(viewport_size.x * pixelRatio, viewport_size.y * pixelRatio);
142
+ shader.uniforms.vScreenResolution.value.set(resolution_x, resolution_y);
137
143
 
138
144
  });
139
145
  }
@@ -14,6 +14,18 @@ export class ParticleEmitterSystem extends System<any> {
14
14
  */
15
15
  renderLayer: RenderLayer | null;
16
16
  __handlers: any[];
17
+ /**
18
+ * `output_resolution` observer installed in startup, kept so shutdown can detach it
19
+ * @type {Function|null}
20
+ * @private
21
+ */
22
+ private __viewportSizeHook;
23
+ /**
24
+ * preRender hook installed in startup, kept so shutdown can detach it
25
+ * @type {Function|null}
26
+ * @private
27
+ */
28
+ private __preRenderHook;
17
29
  /**
18
30
  *
19
31
  * @type {GraphicsEngine}
@@ -1 +1 @@
1
- {"version":3,"file":"ParticleEmitterSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/particles/ecs/ParticleEmitterSystem.js"],"names":[],"mappings":"AAaA;IAeI;;;;;OAKG;IACH,4BAwBC;IA5CD,4DAA4C;IAE5C,uEAEE;IAEF;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEP;IAEnB,kBAAgB;IAcZ;;;OAGG;IACH,+BAAqC;IAErC;;;OAGG;IACH,2BAAuC;IAEvC;;;OAGG;IACH,gBAFU,gBAAgB,CAE2E;IAIzG,2CA8HC;IAED,4CAEC;IAED;;;;;OAKG;IACH,cAJW,eAAe,aACf,SAAS,qBAoCnB;IAED;;;;;OAKG;IACH,gBAJW,eAAe,aACf,SAAS,qBAenB;IAED,6BAEC;CACJ;uBA3PsB,wBAAwB;0BACrB,qCAAqC;gCAG/B,iDAAiD;4CANrC,uDAAuD;iCAQlE,0CAA0C"}
1
+ {"version":3,"file":"ParticleEmitterSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/particles/ecs/ParticleEmitterSystem.js"],"names":[],"mappings":"AAaA;IA6BI;;;;;OAKG;IACH,4BAwBC;IA1DD,4DAA4C;IAE5C,uEAEE;IAEF;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEP;IAEnB,kBAAgB;IAEhB;;;;OAIG;IACH,2BAA0B;IAE1B;;;;OAIG;IACH,wBAAuB;IAcnB;;;OAGG;IACH,+BAAqC;IAErC;;;OAGG;IACH,2BAAuC;IAEvC;;;OAGG;IACH,gBAFU,gBAAgB,CAE2E;IAIzG,2CAiIC;IAED,4CAsBC;IAED;;;;;OAKG;IACH,cAJW,eAAe,aACf,SAAS,qBAoCnB;IAED;;;;;OAKG;IACH,gBAJW,eAAe,aACf,SAAS,qBAenB;IAED,6BAEC;CACJ;uBAhSsB,wBAAwB;0BACrB,qCAAqC;gCAG/B,iDAAiD;4CANrC,uDAAuD;iCAQlE,0CAA0C"}
@@ -26,6 +26,20 @@ export class ParticleEmitterSystem extends System {
26
26
 
27
27
  __handlers = [];
28
28
 
29
+ /**
30
+ * `output_resolution` observer installed in startup, kept so shutdown can detach it
31
+ * @type {Function|null}
32
+ * @private
33
+ */
34
+ __viewportSizeHook = null;
35
+
36
+ /**
37
+ * preRender hook installed in startup, kept so shutdown can detach it
38
+ * @type {Function|null}
39
+ * @private
40
+ */
41
+ __preRenderHook = null;
42
+
29
43
  /**
30
44
  *
31
45
  * @extends {System.<ParticleEmitter>}
@@ -124,16 +138,18 @@ export class ParticleEmitterSystem extends System {
124
138
  this.particleEngine.setDepthTexture(depthTexture);
125
139
 
126
140
 
127
- function updateViewportSize() {
128
- const size = graphicsEngine.viewport.size;
129
-
130
- const pixelRatio = graphicsEngine.computeTotalPixelRatio();
131
-
132
- self.particleEngine.setViewportSize(size.x * pixelRatio, size.y * pixelRatio);
141
+ // device pixels, not CSS pixels. The billboard shader spends this value on the render target's
142
+ // own grid twice over: `gl_FragCoord.xy / resolution` reads the depth attachment acquired just
143
+ // above, and `gl_PointSize` is measured in framebuffer pixels. `output_resolution` is what that
144
+ // attachment is sized from -- rebuilding it here from the CSS size and a freshly read
145
+ // `window.devicePixelRatio` matched only up to rounding, and only until the DPR moved without
146
+ // a resize behind it
147
+ function updateViewportSize(x, y) {
148
+ self.particleEngine.setViewportSize(x, y);
133
149
  }
134
150
 
135
- graphicsEngine.viewport.size.process(updateViewportSize);
136
- graphicsEngine.pixelRatio.onChanged.add(updateViewportSize);
151
+ this.__viewportSizeHook = updateViewportSize;
152
+ graphicsEngine.output_resolution.process(updateViewportSize);
137
153
 
138
154
  function preRenderHook(renderer, camera, scene) {
139
155
  //update camera
@@ -183,11 +199,32 @@ export class ParticleEmitterSystem extends System {
183
199
  }
184
200
  }
185
201
 
202
+ this.__preRenderHook = preRenderHook;
186
203
  graphicsEngine.on.preRender.add(preRenderHook);
187
204
  }
188
205
 
189
206
  async shutdown(entityManager) {
190
- this.graphicsEngine.layers.remove(this.renderLayer);
207
+ const graphicsEngine = this.graphicsEngine;
208
+
209
+ if (this.__viewportSizeHook !== null) {
210
+ graphicsEngine.output_resolution.onChanged.remove(this.__viewportSizeHook);
211
+ this.__viewportSizeHook = null;
212
+ }
213
+
214
+ if (this.__preRenderHook !== null) {
215
+ // left attached, the hook keeps driving the particle simulation for a system that is gone, and
216
+ // keeps taking a reference on the ColorAndDepth frame buffer every frame the last visible set
217
+ // was non-empty, so FrameBufferManager goes on rendering that buffer for nobody
218
+ graphicsEngine.on.preRender.remove(this.__preRenderHook);
219
+ this.__preRenderHook = null;
220
+ }
221
+
222
+ graphicsEngine.layers.remove(this.renderLayer);
223
+
224
+ // every emitter has already been unlinked by this point: both teardown paths detach the dataset
225
+ // (EntityManager#shutdown) or remove the system observer (EntityManager#removeSystem) before
226
+ // calling shutdown, so `unlink` has run for each one
227
+ this.particleEngine.dispose();
191
228
  }
192
229
 
193
230
  /**
@@ -45,6 +45,11 @@ export class ParticularEngine {
45
45
  setCamera(camera: THREE.Camera): void;
46
46
  setDepthTexture(texture: any): void;
47
47
  setViewportSize(x: any, y: any): void;
48
+ /**
49
+ * Hand back the resources held on behalf of the managed emitters. Emitters themselves are owned by
50
+ * their components and are disposed by {@link ParticleEmitter#dispose} as they are removed.
51
+ */
52
+ dispose(): void;
48
53
  /**
49
54
  * @private
50
55
  * @param {ParticleEmitter} emitter
@@ -1 +1 @@
1
- {"version":3,"file":"ParticularEngine.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/particles/particular/engine/ParticularEngine.js"],"names":[],"mappings":"AAUA;IA+BI;;;;;OAKG;IACH,2EAEC;IAtCD;;;OAGG;IACH,QAFU,MAAM,MAAM,GAAC,IAAI,CAEb;IAEd;;;OAGG;IACH,UAFU,qBAAsB,CAEV;IAEtB;;;OAGG;IACH,cAFU,GAAG,CAEG;IAGhB;;;OAGG;IACH,OAFU,wBAAwB,CAOhC;IASE,6BAAsE;IAG1E;;;OAGG;IACH,oCAaC;IAED;;;;OAIG;IACH,kCAFa,OAAO,CAenB;IAED;;;OAGG;IACH,kBAFW,MAAM,MAAM,QAKtB;IAED,oCAEC;IAED,sCAEC;IAED;;;;OAIG;IACH,uBAsEC;IAED;;;;OAIG;IACH,sBAuCC;IAED;;;OAGG;IACH,mBAFW,MAAM,QAWhB;CACJ;iBAnPgB,6CAA6C;oBAD1C,sCAAsC;8BAG5B,2BAA2B"}
1
+ {"version":3,"file":"ParticularEngine.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/particles/particular/engine/ParticularEngine.js"],"names":[],"mappings":"AAUA;IA+BI;;;;;OAKG;IACH,2EAEC;IAtCD;;;OAGG;IACH,QAFU,MAAM,MAAM,GAAC,IAAI,CAEb;IAEd;;;OAGG;IACH,UAFU,qBAAsB,CAEV;IAEtB;;;OAGG;IACH,cAFU,GAAG,CAEG;IAGhB;;;OAGG;IACH,OAFU,wBAAwB,CAOhC;IASE,6BAAsE;IAG1E;;;OAGG;IACH,oCAaC;IAED;;;;OAIG;IACH,kCAFa,OAAO,CAenB;IAED;;;OAGG;IACH,kBAFW,MAAM,MAAM,QAKtB;IAED,oCAEC;IAED,sCAEC;IAED;;;OAGG;IACH,gBAEC;IAED;;;;OAIG;IACH,uBAsEC;IAED;;;;OAIG;IACH,sBAuCC;IAED;;;OAGG;IACH,mBAFW,MAAM,QAWhB;CACJ;iBA3PgB,6CAA6C;oBAD1C,sCAAsC;8BAG5B,2BAA2B"}
@@ -105,6 +105,14 @@ export class ParticularEngine {
105
105
  this.shaderManager.setViewportSize(x, y);
106
106
  }
107
107
 
108
+ /**
109
+ * Hand back the resources held on behalf of the managed emitters. Emitters themselves are owned by
110
+ * their components and are disposed by {@link ParticleEmitter#dispose} as they are removed.
111
+ */
112
+ dispose() {
113
+ this.shaderManager.dispose();
114
+ }
115
+
108
116
  /**
109
117
  * @private
110
118
  * @param {ParticleEmitter} emitter
@@ -4,6 +4,13 @@ export class MaterialRecord {
4
4
  * @type {Material}
5
5
  */
6
6
  material: Material;
7
+ /**
8
+ * The reference {@link MaterialManager#obtain} handed out for {@link MaterialRecord#material}. Held so
9
+ * the material can be given back; the manager decides when to actually dispose it, as the compiled
10
+ * material is shared by content hash with every other consumer that asked for the same shader.
11
+ * @type {Reference<Material>|null}
12
+ */
13
+ material_reference: Reference<Material> | null;
7
14
  /**
8
15
  *
9
16
  * @type {number}
@@ -1 +1 @@
1
- {"version":3,"file":"MaterialRecord.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/shader/MaterialRecord.js"],"names":[],"mappings":"AAAA;IAEI;;;OAGG;IACH,mBAAgB;IAEhB;;;OAGG;IACH,OAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,gBAFU,eAAe,CAEL;IAEpB;;;OAGG;IACH,UAFU,iBAAiB,CAEb;IAEd;;;OAGG;IACH,YAFU,eAAa,IAAI,CAET;IAElB;;;OAGG;IACH,gBAFU,MAAM,CAEG;CAEtB"}
1
+ {"version":3,"file":"MaterialRecord.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/shader/MaterialRecord.js"],"names":[],"mappings":"AAAA;IAEI;;;OAGG;IACH,mBAAgB;IAEhB;;;;;OAKG;IACH,oBAFU,sBAAoB,IAAI,CAER;IAE1B;;;OAGG;IACH,OAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,gBAFU,eAAe,CAEL;IAEpB;;;OAGG;IACH,UAFU,iBAAiB,CAEb;IAEd;;;OAGG;IACH,YAFU,eAAa,IAAI,CAET;IAElB;;;OAGG;IACH,gBAFU,MAAM,CAEG;CAEtB"}
@@ -6,6 +6,14 @@ export class MaterialRecord {
6
6
  */
7
7
  material = null;
8
8
 
9
+ /**
10
+ * The reference {@link MaterialManager#obtain} handed out for {@link MaterialRecord#material}. Held so
11
+ * the material can be given back; the manager decides when to actually dispose it, as the compiled
12
+ * material is shared by content hash with every other consumer that asked for the same shader.
13
+ * @type {Reference<Material>|null}
14
+ */
15
+ material_reference = null;
16
+
9
17
  /**
10
18
  *
11
19
  * @type {number}
@@ -65,7 +65,7 @@ export class ShaderManager {
65
65
  private updateSpriteTexture;
66
66
  /**
67
67
  * @private
68
- * @returns {Material}
68
+ * @returns {Reference<Material>}
69
69
  * @param {ParticleEmitter} emitter
70
70
  */
71
71
  private buildMaterial;
@@ -101,7 +101,15 @@ export class ShaderManager {
101
101
  */
102
102
  setViewportSize(x: number, y: number): void;
103
103
  /**
104
- * Dispose of any used resources
104
+ * Hand back every resource this manager acquired.
105
+ *
106
+ * Materials are given back to the {@link MaterialManager} rather than disposed here: the compiled
107
+ * material is shared by content hash with every other consumer that asked for the same shader, so the
108
+ * manager is the only thing that can know when the last user is gone. Releasing is therefore safe
109
+ * whichever way the teardown ordering falls, even for a material a live mesh still points at.
110
+ *
111
+ * The manager is left able to re-acquire, so a system that starts up again gets working materials and
112
+ * a working sprite sheet without being rebuilt.
105
113
  */
106
114
  dispose(): void;
107
115
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"ShaderManager.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/shader/ShaderManager.js"],"names":[],"mappings":"AA0FA;IAOI;;;;;OAKG;IACH,4EAuGC;IAnGG;;;;OAIG;IACH,2BAA0C;IAE1C;;;OAGG;IACH,aAFU,YAAY,CAE4B;IAElD;;;OAGG;IACH,eAFU,WAAW,GAAC,IAAI,CAOxB;IAIF;;;OAGG;IACH,cAFU,UAAQ,IAAI,CAEE;IAExB;;;;;OAKG;IACH,6BAGE;IAEF;;;OAGG;IACH,qBAFU,MAAM,CAEa;IAE7B;;;;OAIG;IACH,iCAGE;IAEF;;;OAGG;IACH,cAFU,OAAO,CAEoB;IAKrC;;;OAGG;IACH,0BAwBC;IAKL;;OAEG;IACH,eAEC;IAGD;;OAEG;IACH,4BAuBC;IAED;;;;OAIG;IACH,sBAmBC;IAED;;;OAGG;IACH,yCA+CC;IAED;;OAEG;IACH,gBA2BC;IAED;;;OAGG;IACH,2CA8CC;IAED;;;OAGG;IACH,gCAiBC;IAhBG,eAAoB;IAkBxB;;;OAGG;IACH,wCAKC;IAED;;;;OAIG;IACH,mBAHW,MAAM,KACN,MAAM,QAMhB;IAED;;OAEG;IACH,gBAEC;;CACJ;6BA7b4B,kDAAkD;4BAJxE,OAAO;oBAGM,wCAAwC"}
1
+ {"version":3,"file":"ShaderManager.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/shader/ShaderManager.js"],"names":[],"mappings":"AA0FA;IAOI;;;;;OAKG;IACH,4EAuGC;IAnGG;;;;OAIG;IACH,2BAA0C;IAE1C;;;OAGG;IACH,aAFU,YAAY,CAE4B;IAElD;;;OAGG;IACH,eAFU,WAAW,GAAC,IAAI,CAOxB;IAIF;;;OAGG;IACH,cAFU,UAAQ,IAAI,CAEE;IAExB;;;;;OAKG;IACH,6BAGE;IAEF;;;OAGG;IACH,qBAFU,MAAM,CAEa;IAE7B;;;;OAIG;IACH,iCAGE;IAEF;;;OAGG;IACH,cAFU,OAAO,CAEoB;IAKrC;;;OAGG;IACH,0BAwBC;IAKL;;OAEG;IACH,eAEC;IAGD;;OAEG;IACH,4BAuBC;IAED;;;;OAIG;IACH,sBAeC;IAED;;;OAGG;IACH,yCAkDC;IAED;;OAEG;IACH,gBAiCC;IAED;;;OAGG;IACH,2CAiDC;IAED;;;OAGG;IACH,gCAiBC;IAhBG,eAAoB;IAkBxB;;;OAGG;IACH,wCAKC;IAED;;;;OAIG;IACH,mBAHW,MAAM,KACN,MAAM,QAMhB;IAED;;;;;;;;;;OAUG;IACH,gBAiBC;;CACJ;6BA5d4B,kDAAkD;4BAJxE,OAAO;oBAGM,wCAAwC"}
@@ -244,7 +244,7 @@ export class ShaderManager {
244
244
 
245
245
  /**
246
246
  * @private
247
- * @returns {Material}
247
+ * @returns {Reference<Material>}
248
248
  * @param {ParticleEmitter} emitter
249
249
  */
250
250
  buildMaterial(emitter) {
@@ -261,11 +261,7 @@ export class ShaderManager {
261
261
  defs.DEPTH_SOFT_ENABLED = !emitter.getFlag(ParticleEmitterFlag.DepthSoftDisabled);
262
262
  defs.ALIGN_DIRECTION_ENABLED = emitter.getFlag(ParticleEmitterFlag.AlignOnVelocity);
263
263
 
264
- const ref = this.__material_manager.obtain(shader);
265
-
266
- // TODO track reference and release it eventually
267
-
268
- return ref.getValue();
264
+ return this.__material_manager.obtain(shader);
269
265
  }
270
266
 
271
267
  /**
@@ -299,6 +295,9 @@ export class ShaderManager {
299
295
  if (this.materialMap.has(emitter)) {
300
296
  entry = this.materialMap.get(emitter);
301
297
 
298
+ // the record is in use again, so it is no longer a candidate for the graveyard eviction in
299
+ // `cleanup`, which releases the material reference the record still hands to live emitters
300
+ this.recordGraveyard.delete(emitter);
302
301
 
303
302
  material = entry.material;
304
303
  } else {
@@ -338,6 +337,8 @@ export class ShaderManager {
338
337
  }
339
338
  });
340
339
 
340
+ const evicted = this.recordGraveyard.get(bestKey);
341
+
341
342
  if (!this.recordGraveyard.delete(bestKey)) {
342
343
  console.error(`Failed to remove record from the graveyard`, bestKey, this.recordGraveyard);
343
344
 
@@ -350,6 +351,10 @@ export class ShaderManager {
350
351
  }
351
352
 
352
353
  this.materialMap.delete(bestKey);
354
+
355
+ // the record is leaving both maps, so this is the last chance to hand the material back;
356
+ // only unused records reach the graveyard, `register` takes a resurrected one back out of it
357
+ evicted.material_reference.release();
353
358
  }
354
359
  }
355
360
 
@@ -381,6 +386,9 @@ export class ShaderManager {
381
386
 
382
387
  if (ref !== undefined) {
383
388
  ref.release();
389
+
390
+ // the reference is spent, and the map is what keeps the layer reachable
391
+ this.#sprite_references.delete(layer);
384
392
  }
385
393
 
386
394
  layer.setAtlasPatch(null); // clear patch
@@ -451,10 +459,33 @@ export class ShaderManager {
451
459
  }
452
460
 
453
461
  /**
454
- * Dispose of any used resources
462
+ * Hand back every resource this manager acquired.
463
+ *
464
+ * Materials are given back to the {@link MaterialManager} rather than disposed here: the compiled
465
+ * material is shared by content hash with every other consumer that asked for the same shader, so the
466
+ * manager is the only thing that can know when the last user is gone. Releasing is therefore safe
467
+ * whichever way the teardown ordering falls, even for a material a live mesh still points at.
468
+ *
469
+ * The manager is left able to re-acquire, so a system that starts up again gets working materials and
470
+ * a working sprite sheet without being rebuilt.
455
471
  */
456
472
  dispose() {
457
- // TODO implement
473
+ // the graveyard is a subset of `materialMap` - `deregister` adds an unused record to the graveyard
474
+ // without taking it out of the material map, and `cleanup` is what drops it from both
475
+ this.materialMap.forEach(record => {
476
+ record.material_reference.release();
477
+ });
478
+
479
+ this.materialMap.clear();
480
+ this.recordGraveyard.clear();
481
+
482
+ for (const reference of this.#sprite_references.values()) {
483
+ reference.release();
484
+ }
485
+
486
+ this.#sprite_references.clear();
487
+
488
+ this.spriteTexture.dispose();
458
489
  }
459
490
  }
460
491
 
@@ -469,9 +500,10 @@ function createShaderEntry(emitter, shaderManager) {
469
500
  entry.count = 0;
470
501
  entry.parameters = emitter.parameters;
471
502
 
472
- const material = shaderManager.buildMaterial(emitter);
503
+ const material_reference = shaderManager.buildMaterial(emitter);
473
504
 
474
- entry.material = material;
505
+ entry.material_reference = material_reference;
506
+ entry.material = material_reference.getValue();
475
507
 
476
508
  //link all signal bindings
477
509
  entry.signalBindings.forEach(function (b) {
@@ -1 +1 @@
1
- {"version":3,"file":"FrameBuffer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/render/buffer/FrameBuffer.js"],"names":[],"mappings":"AAGA;IAyBI;;;;OAIG;IACH,gBAHW,MAAM,EAWhB;IArCD;;;OAGG;IACH,cAFU,iBAAiB,CAEP;IAEpB;;;OAGG;IACH,gBAFU,MAAM,CAEG;IAEnB;;;OAGG;IACH,cAFU,MAAM,EAAE,CAEA;IAElB;;;OAGG;IACH,MAFU,OAAO,CAEQ;IAQrB;;;OAGG;IACH,IAFU,MAAM,CAEJ;IAKhB;;;OAGG;IACH,mBAFa,iBAAiB,+BAA6B,CAI1D;IAED;;;OAGG;IACH,0CAEC;IAED;;;OAGG;IACH,wCAEC;IAED;;;;;;OAMG;IACH,4EAFW,WAAW,EAAE,QAYvB;IAED;;;;OAIG;IACH,WAHW,MAAM,KACN,MAAM,QAgBhB;CACJ;kCA1GiC,OAAO;oBACrB,kCAAkC"}
1
+ {"version":3,"file":"FrameBuffer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/render/buffer/FrameBuffer.js"],"names":[],"mappings":"AAGA;IAyBI;;;;OAIG;IACH,gBAHW,MAAM,EAWhB;IArCD;;;OAGG;IACH,cAFU,iBAAiB,CAEP;IAEpB;;;OAGG;IACH,gBAFU,MAAM,CAEG;IAEnB;;;OAGG;IACH,cAFU,MAAM,EAAE,CAEA;IAElB;;;OAGG;IACH,MAFU,OAAO,CAEQ;IAQrB;;;OAGG;IACH,IAFU,MAAM,CAEJ;IAKhB;;;OAGG;IACH,mBAFa,iBAAiB,+BAA6B,CAI1D;IAED;;;OAGG;IACH,0CAEC;IAED;;;OAGG;IACH,wCAEC;IAED;;;;;;OAMG;IACH,4EAFW,WAAW,EAAE,QAYvB;IAED;;;;OAIG;IACH,WAHW,MAAM,KACN,MAAM,QAmBhB;CACJ;kCA7GiC,OAAO;oBACrB,kCAAkC"}
@@ -92,9 +92,12 @@ export class FrameBuffer {
92
92
  setSize(x, y) {
93
93
  const size = this.size;
94
94
 
95
+ // a render target is a whole number of texels; WebGL truncates fractional dimensions when it
96
+ // allocates, so round here too, otherwise `renderTarget.width` reports a size the texture does
97
+ // not have and any texel math built from it is off by up to a texel
95
98
  // clamp to 1: a 0-sized attachment trips GL_INVALID_FRAMEBUFFER_OPERATION on clear/draw
96
- const w = Math.max(1, x);
97
- const h = Math.max(1, y);
99
+ const w = Math.max(1, Math.floor(x));
100
+ const h = Math.max(1, Math.floor(y));
98
101
 
99
102
  if (w === size.x && h === size.y) {
100
103
  // nothing changed
@@ -60,7 +60,12 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
60
60
  * @private
61
61
  */
62
62
  private __setCamera;
63
- __prepare_uniforms(): void;
63
+ /**
64
+ *
65
+ * @param {GraphicsEngine} graphics
66
+ * @private
67
+ */
68
+ private __prepare_uniforms;
64
69
  __prepare_draw(): void;
65
70
  /**
66
71
  *
@@ -84,6 +89,13 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
84
89
  * @private
85
90
  */
86
91
  private __remove_used_frame_buffer;
92
+ /**
93
+ * Give back every frame buffer reference this plugin holds. `__use_normals` is deliberately left
94
+ * alone, so a subsequent {@link AmbientOcclusionPostProcessEffect#startup} re-acquires the same
95
+ * buffers through {@link AmbientOcclusionPostProcessEffect#setNormalBufferUsage}.
96
+ * @private
97
+ */
98
+ private __release_used_frame_buffers;
87
99
  __update_render_target_size(): void;
88
100
  }
89
101
  import { EnginePlugin } from "../../../../../plugin/EnginePlugin.js";
@@ -1 +1 @@
1
- {"version":3,"file":"AmbientOcclusionPostProcessEffect.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js"],"names":[],"mappings":"AAoBA;IAQQ,2BAQE;IAyBF;;;;OAIG;IACH,sBAA0B;IAE1B;;;;OAIG;IACH,2BAA6B;IAG7B;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,wBAA2B;IAE3B,8CAUE;IAEF;;;;OAIG;IACH,uBAAwB;IAExB;;;;;OAKG;IACH,2BASE;IAKF;;;;OAIG;IACH,0BAAwD;IAK5D;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,wBAEC;IAED;;;;OAIG;IACH,oBAeC;IAMD,2BAoCC;IAED,uBASC;IAED;;;OAGG;IACH,iBAwBC;IAED;;;OAGG;IACH,4BAFW,OAAO,QAiCjB;IAED;;;;OAIG;IACH,gCASC;IAED;;;;OAIG;IACH,mCAUC;IAGD,oCAiBC;CAkEJ;6BApZ4B,uCAAuC;+BAD7D,OAAO;6CAAP,OAAO"}
1
+ {"version":3,"file":"AmbientOcclusionPostProcessEffect.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js"],"names":[],"mappings":"AAoBA;IAQQ,2BAQE;IAyBF;;;;OAIG;IACH,sBAA0B;IAE1B;;;;OAIG;IACH,2BAA6B;IAG7B;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,wBAA2B;IAE3B,8CAUE;IAEF;;;;OAIG;IACH,uBAAwB;IAExB;;;;;OAKG;IACH,2BASE;IAKF;;;;OAIG;IACH,0BAAwD;IAK5D;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,wBAEC;IAED;;;;OAIG;IACH,oBAeC;IAMD;;;;OAIG;IACH,2BA2CC;IAED,uBASC;IAED;;;OAGG;IACH,iBAwBC;IAED;;;OAGG;IACH,4BAFW,OAAO,QAiCjB;IAED;;;;OAIG;IACH,gCASC;IAED;;;;OAIG;IACH,mCAUC;IAED;;;;;OAKG;IACH,qCAQC;IAGD,oCAiBC;CAuEJ;6BArb4B,uCAAuC;+BAD7D,OAAO;6CAAP,OAAO"}
@@ -179,7 +179,12 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
179
179
  super.finalize();
180
180
  }
181
181
 
182
- __prepare_uniforms() {
182
+ /**
183
+ *
184
+ * @param {GraphicsEngine} graphics
185
+ * @private
186
+ */
187
+ __prepare_uniforms(graphics) {
183
188
 
184
189
  const material = this.__material;
185
190
 
@@ -188,8 +193,14 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
188
193
  const camera = this.__render_camera;
189
194
 
190
195
  // AO renders at half-res but samples the full-res depth buffer, so `size` is the full (depth)
191
- // resolution -- all texel math then lands on depth texel centres rather than texel boundaries
192
- uniforms.size.value.set(this.__composit_layer.renderTarget.width, this.__composit_layer.renderTarget.height);
196
+ // resolution -- all texel math then lands on depth texel centres rather than texel boundaries.
197
+ // Read it from `output_resolution`, the value the depth frame buffer is itself sized from:
198
+ // deriving it from any other buffer only holds while that buffer agrees, and a `size` that
199
+ // disagrees with the depth texture collapses the reconstructed normals (neighbour taps land
200
+ // inside the centre texel, so every depth delta reads as zero).
201
+ const output_resolution = graphics.output_resolution;
202
+
203
+ uniforms.size.value.copy(output_resolution);
193
204
 
194
205
  // setup camera
195
206
  const near = camera.near;
@@ -205,12 +216,13 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
205
216
  // trigger uniform update
206
217
  material.uniformsNeedUpdate = true;
207
218
 
208
- // upscale runs at full resolution (the composit layer), reading the half-res AO target
219
+ // upscale runs at full resolution (the composit layer), reading the half-res AO target. `size`
220
+ // must be the same full-res grid the AO pass used, since the upscale reproduces its depth-texel
221
+ // snapping to judge each tap
209
222
  const upscale = this.__upscale_material;
210
223
  const upscaleUniforms = upscale.uniforms;
211
- const output = this.__composit_layer.renderTarget;
212
224
 
213
- upscaleUniforms.size.value.set(output.width, output.height);
225
+ upscaleUniforms.size.value.copy(output_resolution);
214
226
  upscaleUniforms.cameraNear.value = near;
215
227
  upscaleUniforms.cameraFar.value = far;
216
228
 
@@ -224,7 +236,7 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
224
236
 
225
237
  this.__setCamera(camera);
226
238
 
227
- this.__prepare_uniforms();
239
+ this.__prepare_uniforms(graphics);
228
240
 
229
241
  }
230
242
 
@@ -328,6 +340,22 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
328
340
  this.__used_buffers.splice(i, 1);
329
341
  }
330
342
 
343
+ /**
344
+ * Give back every frame buffer reference this plugin holds. `__use_normals` is deliberately left
345
+ * alone, so a subsequent {@link AmbientOcclusionPostProcessEffect#startup} re-acquires the same
346
+ * buffers through {@link AmbientOcclusionPostProcessEffect#setNormalBufferUsage}.
347
+ * @private
348
+ */
349
+ __release_used_frame_buffers() {
350
+ const buffers = this.__used_buffers;
351
+
352
+ for (const buffer of buffers) {
353
+ buffer.referenceCount--;
354
+ }
355
+
356
+ buffers.length = 0;
357
+ }
358
+
331
359
 
332
360
  __update_render_target_size() {
333
361
  const engine = this.engine;
@@ -404,7 +432,12 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
404
432
  this.__composit_layer.on.preRender.remove(this.__render, this);
405
433
  graphics.output_resolution.onChanged.remove(this.__update_render_target_size, this);
406
434
 
435
+ // hand back the frame buffers we pinned, otherwise FrameBufferManager keeps rendering them
436
+ // for a plugin that is no longer there
437
+ this.__release_used_frame_buffers();
438
+
407
439
  // release memory
440
+ this.__material.dispose();
408
441
  this.__upscale_material.dispose();
409
442
  this.__hilbert_texture.dispose();
410
443
  this.__render_target.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"TemporalSupersamplingRenderPlugin.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.js"],"names":[],"mappings":"AAIA;IAIQ;;;;OAIG;IACH,sBAAsB;IAEtB,uBAAwB;IACxB,8BAAkE;IAGlE;QACI;;;;WAIG;2BAFQ,MAAM,EAAE,UACR,MAAM,EAAE;MAkBtB;IAGL,2BAIC;IAED,8BAEC;CAyBJ;6BA3E4B,uCAAuC"}
1
+ {"version":3,"file":"TemporalSupersamplingRenderPlugin.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.js"],"names":[],"mappings":"AAIA;IAIQ;;;;OAIG;IACH,sBAAsB;IAEtB,uBAAwB;IACxB,8BAAkE;IAGlE;QACI;;;;WAIG;2BAFQ,MAAM,EAAE,UACR,MAAM,EAAE;MAwBtB;IAGL,2BAIC;IAED,8BAEC;CAyBJ;6BAjF4B,uCAAuC"}