mirror of
https://github.com/ppy/osu.git
synced 2025-02-19 07:42:58 +08:00
Separate Lifetime computation and layout update
This commit is contained in:
parent
ec92545d7a
commit
ce57e8ddfb
@ -2,13 +2,10 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Layout;
|
using osu.Framework.Layout;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -19,7 +16,12 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
{
|
{
|
||||||
private readonly IBindable<double> timeRange = new BindableDouble();
|
private readonly IBindable<double> timeRange = new BindableDouble();
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
private readonly Dictionary<DrawableHitObject, InitialState> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, InitialState>();
|
|
||||||
|
// If a hit object is not in this set, the position and the size should be updated when the hit object becomes alive.
|
||||||
|
private readonly HashSet<DrawableHitObject> layoutComputedHitObjects = new HashSet<DrawableHitObject>();
|
||||||
|
|
||||||
|
// Used to recompute all lifetime when `layoutCache` becomes invalid
|
||||||
|
private readonly HashSet<DrawableHitObject> lifetimeComputedHitObjects = new HashSet<DrawableHitObject>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
@ -27,10 +29,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
// Responds to changes in the layout. When the layout changes, all hit object states must be recomputed.
|
// Responds to changes in the layout. When the layout changes, all hit object states must be recomputed.
|
||||||
private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
|
private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
|
||||||
|
|
||||||
// A combined cache across all hit object states to reduce per-update iterations.
|
|
||||||
// When invalidated, one or more (but not necessarily all) hitobject states must be re-validated.
|
|
||||||
private readonly Cached combinedObjCache = new Cached();
|
|
||||||
|
|
||||||
public ScrollingHitObjectContainer()
|
public ScrollingHitObjectContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -52,8 +50,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
{
|
{
|
||||||
base.Clear(disposeChildren);
|
base.Clear(disposeChildren);
|
||||||
|
|
||||||
combinedObjCache.Invalidate();
|
layoutComputedHitObjects.Clear();
|
||||||
hitObjectInitialStateCache.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -150,13 +147,15 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalidate the cache of the layout of this hit object.
|
/// Invalidate the cache of the layout of this hit object.
|
||||||
|
/// A hit object should be invalidated after all its nested hit objects are invalidated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void InvalidateDrawableHitObject(DrawableHitObject drawableObject)
|
public void InvalidateDrawableHitObject(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var state))
|
// lifetime is computed before update
|
||||||
state.Cache.Invalidate();
|
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
||||||
|
|
||||||
combinedObjCache.Invalidate();
|
lifetimeComputedHitObjects.Add(hitObject);
|
||||||
|
layoutComputedHitObjects.Remove(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a nonzero value to prevent infinite results
|
// Use a nonzero value to prevent infinite results
|
||||||
@ -168,17 +167,14 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
if (!layoutCache.IsValid)
|
if (!layoutCache.IsValid)
|
||||||
{
|
{
|
||||||
foreach (var state in hitObjectInitialStateCache.Values)
|
// this.Objects cannot be used as it doesn't contain nested objects
|
||||||
state.Cache.Invalidate();
|
foreach (var hitObject in lifetimeComputedHitObjects)
|
||||||
combinedObjCache.Invalidate();
|
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
||||||
|
|
||||||
|
layoutComputedHitObjects.Clear();
|
||||||
|
|
||||||
scrollingInfo.Algorithm.Reset();
|
scrollingInfo.Algorithm.Reset();
|
||||||
|
|
||||||
layoutCache.Validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!combinedObjCache.IsValid)
|
|
||||||
{
|
|
||||||
switch (direction.Value)
|
switch (direction.Value)
|
||||||
{
|
{
|
||||||
case ScrollingDirection.Up:
|
case ScrollingDirection.Up:
|
||||||
@ -191,32 +187,18 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var obj in Objects)
|
layoutCache.Validate();
|
||||||
{
|
|
||||||
if (!hitObjectInitialStateCache.TryGetValue(obj, out var state))
|
|
||||||
state = hitObjectInitialStateCache[obj] = new InitialState(new Cached());
|
|
||||||
|
|
||||||
if (state.Cache.IsValid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
state.ScheduledComputation?.Cancel();
|
|
||||||
state.ScheduledComputation = computeInitialStateRecursive(obj);
|
|
||||||
|
|
||||||
computeLifetimeStartRecursive(obj);
|
|
||||||
|
|
||||||
state.Cache.Validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
combinedObjCache.Validate();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void computeLifetimeStartRecursive(DrawableHitObject hitObject)
|
foreach (var obj in AliveObjects)
|
||||||
{
|
{
|
||||||
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
if (layoutComputedHitObjects.Contains(obj))
|
||||||
|
continue;
|
||||||
|
|
||||||
foreach (var obj in hitObject.NestedHitObjects)
|
updateLayoutRecursive(obj);
|
||||||
computeLifetimeStartRecursive(obj);
|
|
||||||
|
layoutComputedHitObjects.Add(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
|
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
|
||||||
@ -247,7 +229,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength);
|
return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
|
private void updateLayoutRecursive(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
if (hitObject.HitObject is IHasDuration e)
|
if (hitObject.HitObject is IHasDuration e)
|
||||||
{
|
{
|
||||||
@ -267,12 +249,12 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
foreach (var obj in hitObject.NestedHitObjects)
|
foreach (var obj in hitObject.NestedHitObjects)
|
||||||
{
|
{
|
||||||
computeInitialStateRecursive(obj);
|
updateLayoutRecursive(obj);
|
||||||
|
|
||||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||||
updatePosition(obj, hitObject.HitObject.StartTime);
|
updatePosition(obj, hitObject.HitObject.StartTime);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildrenLife()
|
protected override void UpdateAfterChildrenLife()
|
||||||
{
|
{
|
||||||
@ -304,19 +286,5 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InitialState
|
|
||||||
{
|
|
||||||
[NotNull]
|
|
||||||
public readonly Cached Cache;
|
|
||||||
|
|
||||||
[CanBeNull]
|
|
||||||
public ScheduledDelegate ScheduledComputation;
|
|
||||||
|
|
||||||
public InitialState(Cached cache)
|
|
||||||
{
|
|
||||||
Cache = cache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user