1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 13:22:55 +08:00

Make ScrollingHitObjectContainer respond to defaults applied events

This commit is contained in:
smoogipoo 2020-05-08 18:49:58 +09:00
parent efff2bf15d
commit 22dda3fe02
2 changed files with 51 additions and 21 deletions

View File

@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
[Cached(typeof(DrawableHitObject))] [Cached(typeof(DrawableHitObject))]
public abstract class DrawableHitObject : SkinReloadableDrawable public abstract class DrawableHitObject : SkinReloadableDrawable
{ {
public event Action<DrawableHitObject> DefaultsApplied;
public readonly HitObject HitObject; public readonly HitObject HitObject;
/// <summary> /// <summary>
@ -178,6 +180,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
private void onDefaultsApplied(HitObject hitObject) private void onDefaultsApplied(HitObject hitObject)
{ {
apply(hitObject); apply(hitObject);
DefaultsApplied?.Invoke(this);
} }
private void apply(HitObject hitObject) private void apply(HitObject hitObject)

View File

@ -16,17 +16,23 @@ 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, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
[Resolved] [Resolved]
private IScrollingInfo scrollingInfo { get; set; } private IScrollingInfo scrollingInfo { get; set; }
private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); // Responds to changes in the layout. When the layout is changes, all hit object states must be recomputed.
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;
AddLayout(initialStateCache); AddLayout(layoutCache);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -35,13 +41,14 @@ namespace osu.Game.Rulesets.UI.Scrolling
direction.BindTo(scrollingInfo.Direction); direction.BindTo(scrollingInfo.Direction);
timeRange.BindTo(scrollingInfo.TimeRange); timeRange.BindTo(scrollingInfo.TimeRange);
direction.ValueChanged += _ => initialStateCache.Invalidate(); direction.ValueChanged += _ => layoutCache.Invalidate();
timeRange.ValueChanged += _ => initialStateCache.Invalidate(); timeRange.ValueChanged += _ => layoutCache.Invalidate();
} }
public override void Add(DrawableHitObject hitObject) public override void Add(DrawableHitObject hitObject)
{ {
initialStateCache.Invalidate(); combinedObjCache.Invalidate();
hitObject.DefaultsApplied += onDefaultsApplied;
base.Add(hitObject); base.Add(hitObject);
} }
@ -51,8 +58,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
if (result) if (result)
{ {
initialStateCache.Invalidate(); combinedObjCache.Invalidate();
hitObjectInitialStateCache.Remove(hitObject); hitObjectInitialStateCache.Remove(hitObject);
hitObject.DefaultsApplied -= onDefaultsApplied;
} }
return result; return result;
@ -60,23 +69,45 @@ namespace osu.Game.Rulesets.UI.Scrolling
public override void Clear(bool disposeChildren = true) public override void Clear(bool disposeChildren = true)
{ {
foreach (var h in Objects)
h.DefaultsApplied -= onDefaultsApplied;
base.Clear(disposeChildren); base.Clear(disposeChildren);
initialStateCache.Invalidate(); combinedObjCache.Invalidate();
hitObjectInitialStateCache.Clear(); hitObjectInitialStateCache.Clear();
} }
private void onDefaultsApplied(DrawableHitObject drawableObject)
{
// The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame).
// In such a case, combinedObjCache will take care of updating the hitobject.
if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var objCache))
{
combinedObjCache.Invalidate();
objCache.Invalidate();
}
}
private float scrollLength; private float scrollLength;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
if (!initialStateCache.IsValid) if (!layoutCache.IsValid)
{ {
foreach (var cached in hitObjectInitialStateCache.Values) foreach (var cached in hitObjectInitialStateCache.Values)
cached.Invalidate(); cached.Invalidate();
combinedObjCache.Invalidate();
scrollingInfo.Algorithm.Reset();
layoutCache.Validate();
}
if (!combinedObjCache.IsValid)
{
switch (direction.Value) switch (direction.Value)
{ {
case ScrollingDirection.Up: case ScrollingDirection.Up:
@ -89,15 +120,21 @@ namespace osu.Game.Rulesets.UI.Scrolling
break; break;
} }
scrollingInfo.Algorithm.Reset();
foreach (var obj in Objects) foreach (var obj in Objects)
{ {
if (!hitObjectInitialStateCache.TryGetValue(obj, out var objCache))
objCache = hitObjectInitialStateCache[obj] = new Cached();
if (objCache.IsValid)
return;
computeLifetimeStartRecursive(obj); computeLifetimeStartRecursive(obj);
computeInitialStateRecursive(obj); computeInitialStateRecursive(obj);
objCache.Validate();
} }
initialStateCache.Validate(); combinedObjCache.Validate();
} }
} }
@ -109,8 +146,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
computeLifetimeStartRecursive(obj); computeLifetimeStartRecursive(obj);
} }
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
{ {
float originAdjustment = 0.0f; float originAdjustment = 0.0f;
@ -142,12 +177,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Cant use AddOnce() since the delegate is re-constructed every invocation // Cant use AddOnce() since the delegate is re-constructed every invocation
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
{ {
if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached))
cached = hitObjectInitialStateCache[hitObject] = new Cached();
if (cached.IsValid)
return;
if (hitObject.HitObject is IHasEndTime e) if (hitObject.HitObject is IHasEndTime e)
{ {
switch (direction.Value) switch (direction.Value)
@ -171,8 +200,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
// 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);
} }
cached.Validate();
}); });
protected override void UpdateAfterChildrenLife() protected override void UpdateAfterChildrenLife()