// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable enable using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; namespace osu.Game.Rulesets.Objects.Pooling { /// /// A that is controlled by to implement drawable pooling and replay rewinding. /// /// The type storing state and controlling this drawable. public abstract class PoolableDrawableWithLifetime : PoolableDrawable where TEntry : LifetimeEntry { /// /// The entry holding essential state of this . /// protected TEntry? Entry { get; private set; } /// /// Whether is applied to this . /// When an initial entry is specified in the constructor, is set but not applied until loading is completed. /// protected bool HasEntryApplied { get; private set; } public override double LifetimeStart { get => base.LifetimeStart; set => setLifetime(value, LifetimeEnd); } public override double LifetimeEnd { get => base.LifetimeEnd; set => setLifetime(LifetimeStart, value); } public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; protected PoolableDrawableWithLifetime(TEntry? initialEntry = null) { Entry = initialEntry; } protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); // Apply the initial entry given in the constructor. if (Entry != null && !HasEntryApplied) Apply(Entry); } /// /// Applies a new entry to be represented by this drawable. /// If there is an existing entry applied, the entry will be replaced. /// public void Apply(TEntry entry) { if (HasEntryApplied) free(); setLifetime(entry.LifetimeStart, entry.LifetimeEnd); Entry = entry; OnApply(entry); HasEntryApplied = true; } protected sealed override void FreeAfterUse() { base.FreeAfterUse(); // We preserve the existing entry in case we want to move a non-pooled drawable between different parent drawables. if (HasEntryApplied && IsInPool) free(); } /// /// Invoked to apply a new entry to this drawable. /// protected virtual void OnApply(TEntry entry) { } /// /// Invoked to revert application of the entry to this drawable. /// protected virtual void OnFree(TEntry entry) { } private void setLifetime(double start, double end) { base.LifetimeStart = start; base.LifetimeEnd = end; if (Entry != null) { Entry.LifetimeStart = start; Entry.LifetimeEnd = end; } } private void free() { Debug.Assert(Entry != null && HasEntryApplied); OnFree(Entry); Entry = null; setLifetime(double.MaxValue, double.MaxValue); HasEntryApplied = false; } } }