2021-04-20 16:55:01 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable enable
2021-05-31 13:37:28 +08:00
using System ;
2021-04-20 16:55:01 +08:00
using System.Diagnostics ;
using osu.Framework.Graphics.Performance ;
using osu.Framework.Graphics.Pooling ;
namespace osu.Game.Rulesets.Objects.Pooling
{
/// <summary>
/// A <see cref="PoolableDrawable"/> that is controlled by <see cref="Entry"/> to implement drawable pooling and replay rewinding.
/// </summary>
/// <typeparam name="TEntry">The <see cref="LifetimeEntry"/> type storing state and controlling this drawable.</typeparam>
2021-04-26 11:06:21 +08:00
public abstract class PoolableDrawableWithLifetime < TEntry > : PoolableDrawable where TEntry : LifetimeEntry
2021-04-20 16:55:01 +08:00
{
/// <summary>
2021-04-26 11:06:21 +08:00
/// The entry holding essential state of this <see cref="PoolableDrawableWithLifetime{TEntry}"/>.
2021-04-20 16:55:01 +08:00
/// </summary>
2021-04-28 17:27:40 +08:00
public TEntry ? Entry { get ; private set ; }
2021-04-20 16:55:01 +08:00
/// <summary>
2021-04-26 11:06:21 +08:00
/// Whether <see cref="Entry"/> is applied to this <see cref="PoolableDrawableWithLifetime{TEntry}"/>.
2021-04-20 16:55:01 +08:00
/// When an initial entry is specified in the constructor, <see cref="Entry"/> is set but not applied until loading is completed.
/// </summary>
protected bool HasEntryApplied { get ; private set ; }
public override double LifetimeStart
{
2021-05-20 13:53:33 +08:00
get = > base . LifetimeStart ;
2021-04-28 19:23:52 +08:00
set
{
2021-05-31 13:37:28 +08:00
if ( Entry = = null & & LifetimeStart ! = value )
throw new InvalidOperationException ( $"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime<TEntry>)} when entry is not set" ) ;
2021-05-04 15:04:58 +08:00
2021-05-31 13:37:28 +08:00
if ( Entry = = null ) return ;
2021-06-01 13:22:12 +08:00
// Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true).
Entry . LifetimeStart = value ;
2021-06-01 12:17:30 +08:00
base . LifetimeStart = Entry . LifetimeStart = value ;
2021-04-28 19:23:52 +08:00
}
2021-04-20 16:55:01 +08:00
}
public override double LifetimeEnd
{
2021-05-20 13:53:33 +08:00
get = > base . LifetimeEnd ;
2021-04-28 19:23:52 +08:00
set
{
2021-05-31 13:37:28 +08:00
if ( Entry = = null & & LifetimeEnd ! = value )
throw new InvalidOperationException ( $"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime<TEntry>)} when entry is not set" ) ;
if ( Entry = = null ) return ;
2021-05-04 15:04:58 +08:00
2021-06-01 13:22:12 +08:00
Entry . LifetimeEnd = value ;
base . LifetimeEnd = Entry . LifetimeEnd ;
2021-04-28 19:23:52 +08:00
}
2021-04-20 16:55:01 +08:00
}
public override bool RemoveWhenNotAlive = > false ;
public override bool RemoveCompletedTransforms = > false ;
2021-04-26 11:06:21 +08:00
protected PoolableDrawableWithLifetime ( TEntry ? initialEntry = null )
2021-04-20 16:55:01 +08:00
{
Entry = initialEntry ;
}
protected override void LoadAsyncComplete ( )
{
base . LoadAsyncComplete ( ) ;
2021-04-26 11:04:59 +08:00
// Apply the initial entry given in the constructor.
2021-04-20 16:55:01 +08:00
if ( Entry ! = null & & ! HasEntryApplied )
Apply ( Entry ) ;
}
/// <summary>
/// Applies a new entry to be represented by this drawable.
/// If there is an existing entry applied, the entry will be replaced.
/// </summary>
public void Apply ( TEntry entry )
{
2021-04-26 11:04:59 +08:00
if ( HasEntryApplied )
free ( ) ;
2021-04-20 16:55:01 +08:00
Entry = entry ;
2021-06-01 12:17:30 +08:00
entry . LifetimeChanged + = setLifetimeFromEntry ;
setLifetimeFromEntry ( entry ) ;
2021-05-20 13:53:33 +08:00
2021-04-20 16:55:01 +08:00
OnApply ( entry ) ;
2021-05-20 13:53:33 +08:00
2021-04-20 16:55:01 +08:00
HasEntryApplied = true ;
}
protected sealed override void FreeAfterUse ( )
{
base . FreeAfterUse ( ) ;
2021-04-26 11:04:59 +08:00
// We preserve the existing entry in case we want to move a non-pooled drawable between different parent drawables.
if ( HasEntryApplied & & IsInPool )
free ( ) ;
2021-04-20 16:55:01 +08:00
}
/// <summary>
/// Invoked to apply a new entry to this drawable.
/// </summary>
protected virtual void OnApply ( TEntry entry )
{
}
/// <summary>
/// Invoked to revert application of the entry to this drawable.
/// </summary>
protected virtual void OnFree ( TEntry entry )
{
}
2021-04-26 11:04:59 +08:00
private void free ( )
2021-04-20 16:55:01 +08:00
{
2021-04-26 11:04:59 +08:00
Debug . Assert ( Entry ! = null & & HasEntryApplied ) ;
2021-04-20 16:55:01 +08:00
OnFree ( Entry ) ;
2021-05-20 13:53:33 +08:00
2021-06-01 12:17:30 +08:00
Entry . LifetimeChanged - = setLifetimeFromEntry ;
2021-04-20 16:55:01 +08:00
Entry = null ;
2021-06-01 12:17:30 +08:00
base . LifetimeStart = double . MinValue ;
base . LifetimeEnd = double . MaxValue ;
2021-05-20 13:53:33 +08:00
2021-04-20 16:55:01 +08:00
HasEntryApplied = false ;
}
2021-05-31 13:37:28 +08:00
2021-06-01 12:17:30 +08:00
private void setLifetimeFromEntry ( LifetimeEntry entry )
2021-05-31 13:37:28 +08:00
{
Debug . Assert ( entry = = Entry ) ;
2021-06-01 12:17:30 +08:00
base . LifetimeStart = entry . LifetimeStart ;
base . LifetimeEnd = entry . LifetimeEnd ;
2021-05-31 13:37:28 +08:00
}
2021-04-20 16:55:01 +08:00
}
}