// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.UI { /// /// A that pools s and allows children to retrieve them via . /// [Cached(typeof(HitObjectPoolProvider))] public class HitObjectPoolProvider : CompositeDrawable { [Resolved] private DrawableRuleset drawableRuleset { get; set; } [Resolved] private IReadOnlyList mods { get; set; } [Resolved(CanBeNull = true)] private HitObjectPoolProvider parentProvider { get; set; } private readonly Dictionary pools = new Dictionary(); /// /// Registers a default pool with this which is to be used whenever /// representations are requested for the given type (via ). /// /// The number of s to be initially stored in the pool. /// /// The maximum number of s that can be stored in the pool. /// If this limit is exceeded, every subsequent will be created anew instead of being retrieved from the pool, /// until some of the existing s are returned to the pool. /// /// The type. /// The receiver for s. protected void RegisterPool(int initialSize, int? maximumSize = null) where TObject : HitObject where TDrawable : DrawableHitObject, new() => RegisterPool(new DrawablePool(initialSize, maximumSize)); /// /// Registers a custom pool with this which is to be used whenever /// representations are requested for the given type (via ). /// /// The to register. /// The type. /// The receiver for s. protected void RegisterPool([NotNull] DrawablePool pool) where TObject : HitObject where TDrawable : DrawableHitObject, new() { pools[typeof(TObject)] = pool; AddInternal(pool); } /// /// Attempts to retrieve the poolable representation of a . /// /// The to retrieve the representation of. /// The representing , or null if no poolable representation exists. [CanBeNull] public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject) { var lookupType = hitObject.GetType(); IDrawablePool pool; // Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists. if (!pools.TryGetValue(lookupType, out pool)) { foreach (var (t, p) in pools) { if (!t.IsInstanceOfType(hitObject)) continue; pools[lookupType] = pool = p; break; } } if (pool == null) return parentProvider?.GetPooledDrawableRepresentation(hitObject); return (DrawableHitObject)pool.Get(d => { var dho = (DrawableHitObject)d; // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. // This is done before Apply() so that the state is updated once when the hitobject is applied. if (!dho.IsLoaded) { foreach (var m in mods.OfType()) m.ApplyToDrawableHitObjects(dho.Yield()); } dho.Apply(hitObject, drawableRuleset.GetLifetimeEntry(hitObject)); }); } } }