12public sealed class EffectsManager : IDisposable
14 private readonly
object _sync =
new();
15 private readonly List<DisplayEffect> _activeEffects = [];
17 private long? _lastTick;
18 private bool _disposed;
21 _host = host ??
throw new ArgumentNullException(nameof(host));
29 return _activeEffects.ToList().AsReadOnly();
53 ArgumentNullException.ThrowIfNull(effect);
57 removed = _activeEffects.Remove(effect);
62 effect.CancelInternal(restoreState:
true);
63 Invalidate(effect.Target);
73 snapshot = _activeEffects.ToArray();
74 _activeEffects.Clear();
77 foreach (var effect
in snapshot)
79 effect.CancelInternal(restoreState:
true);
80 Invalidate(effect.Target);
84 internal void Update(
long tick)
86 if (!_lastTick.HasValue)
92 float deltaSeconds = HighResTimer.GetDuration(_lastTick.Value, tick);
94 Advance(deltaSeconds);
97 internal void Advance(
float deltaSeconds)
99 DisplayEffect[] snapshot;
102 snapshot = _activeEffects.ToArray();
104 foreach (var effect
in snapshot)
106 if (!OwnsTarget(effect.Target))
108 RemoveAndCancel(effect, restoreState:
false);
112 bool finished = effect.AdvanceInternal(deltaSeconds);
113 Invalidate(effect.Target);
118 _activeEffects.Remove(effect);
123 internal void Invalidate(
object target)
125 if (target is View or SceneLayer)
126 _host.Scene.FullRefreshNeeded =
true;
129 private TEffect RunCore<TEffect>(
object target, TEffect effect)
130 where TEffect : DisplayEffect
132 ObjectDisposedException.ThrowIf(_disposed,
this);
133 ArgumentNullException.ThrowIfNull(target);
134 ArgumentNullException.ThrowIfNull(effect);
136 if (!OwnsTarget(target))
137 throw new ArgumentException(
138 "The effect target is not owned by this render surface host.",
141 if (!effect.SupportsTarget(target))
142 throw new ArgumentException(
143 $
"{effect.GetType().Name} does not support {target.GetType().Name} targets.",
146 DisplayEffect? replaced;
151 throw new InvalidOperationException(
"An effect instance can only be run once.");
153 replaced = _activeEffects.FirstOrDefault(
154 active => ReferenceEquals(active.Target, target)
155 && active.Channel == effect.Channel);
157 if (replaced is not
null)
158 _activeEffects.Remove(replaced);
160 _activeEffects.Add(effect);
165 replaced?.CancelInternal(restoreState:
false);
169 effect.StartInternal(
this, target);
175 _activeEffects.Remove(effect);
183 _activeEffects.Remove(effect);
189 private bool OwnsTarget(
object target) => target
switch
191 View view => _host.ViewManager.Views.Contains(view),
193 && _host.Scene.SceneLayers.Contains(layer),
197 private void RemoveAndCancel(DisplayEffect effect,
bool restoreState)
200 _activeEffects.Remove(effect);
202 effect.CancelInternal(restoreState);