mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 08:03:52 +09:00
Merge pull request #10443 from peppy/editor-timeline-tick-display-performance
Improve TimelineTickDisplay performance
This commit is contained in:
@ -5,7 +5,6 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTimelineTickDisplay : TimelineTestScene
|
public class TestSceneTimelineTickDisplay : TimelineTestScene
|
||||||
{
|
{
|
||||||
public override Drawable CreateTestComponent() => new TimelineTickDisplay();
|
public override Drawable CreateTestComponent() => Empty(); // tick display is implicitly inside the timeline.
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osuTK;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
||||||
{
|
{
|
||||||
@ -12,16 +12,23 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PointVisualisation : Box
|
public class PointVisualisation : Box
|
||||||
{
|
{
|
||||||
|
public const float WIDTH = 1;
|
||||||
|
|
||||||
public PointVisualisation(double startTime)
|
public PointVisualisation(double startTime)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
X = (float)startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointVisualisation()
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
|
||||||
Width = 1;
|
|
||||||
EdgeSmoothness = new Vector2(1, 0);
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
X = (float)startTime;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Width = WIDTH;
|
||||||
|
EdgeSmoothness = new Vector2(WIDTH, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
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.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -12,7 +14,7 @@ using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class TimelineTickDisplay : TimelinePart
|
public class TimelineTickDisplay : TimelinePart<PointVisualisation>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap beatmap { get; set; }
|
private EditorBeatmap beatmap { get; set; }
|
||||||
@ -31,15 +33,63 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Cached tickCache = new Cached();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
beatDivisor.BindValueChanged(_ => createLines(), true);
|
beatDivisor.BindValueChanged(_ => tickCache.Invalidate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLines()
|
/// <summary>
|
||||||
|
/// The visible time/position range of the timeline.
|
||||||
|
/// </summary>
|
||||||
|
private (float min, float max) visibleRange = (float.MinValue, float.MaxValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next time/position value to the left of the display when tick regeneration needs to be run.
|
||||||
|
/// </summary>
|
||||||
|
private float? nextMinTick;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next time/position value to the right of the display when tick regeneration needs to be run.
|
||||||
|
/// </summary>
|
||||||
|
private float? nextMaxTick;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Timeline timeline { get; set; }
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
{
|
{
|
||||||
Clear();
|
base.Update();
|
||||||
|
|
||||||
|
if (timeline != null)
|
||||||
|
{
|
||||||
|
var newRange = (
|
||||||
|
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X,
|
||||||
|
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X);
|
||||||
|
|
||||||
|
if (visibleRange != newRange)
|
||||||
|
{
|
||||||
|
visibleRange = newRange;
|
||||||
|
|
||||||
|
// actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries.
|
||||||
|
if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick))
|
||||||
|
tickCache.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tickCache.IsValid)
|
||||||
|
createTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTicks()
|
||||||
|
{
|
||||||
|
int drawableIndex = 0;
|
||||||
|
int highestDivisor = BindableBeatDivisor.VALID_DIVISORS.Last();
|
||||||
|
|
||||||
|
nextMinTick = null;
|
||||||
|
nextMaxTick = null;
|
||||||
|
|
||||||
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
|
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
|
||||||
{
|
{
|
||||||
@ -50,41 +100,70 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value)
|
for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value)
|
||||||
{
|
{
|
||||||
var indexInBeat = beat % beatDivisor.Value;
|
float xPos = (float)t;
|
||||||
|
|
||||||
if (indexInBeat == 0)
|
if (t < visibleRange.min)
|
||||||
{
|
nextMinTick = xPos;
|
||||||
Add(new PointVisualisation(t)
|
else if (t > visibleRange.max)
|
||||||
{
|
nextMaxTick ??= xPos;
|
||||||
Colour = BindableBeatDivisor.GetColourFor(1, colours),
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// if this is the first beat in the beatmap, there is no next min tick
|
||||||
|
if (beat == 0 && i == 0)
|
||||||
|
nextMinTick = float.MinValue;
|
||||||
|
|
||||||
|
var indexInBeat = beat % beatDivisor.Value;
|
||||||
|
|
||||||
var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
|
var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
|
||||||
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);
|
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);
|
||||||
var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f;
|
|
||||||
|
|
||||||
Add(new PointVisualisation(t)
|
// even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn.
|
||||||
{
|
var height = indexInBeat == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f;
|
||||||
Colour = colour,
|
|
||||||
Height = height,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(new PointVisualisation(t)
|
var topPoint = getNextUsablePoint();
|
||||||
{
|
topPoint.X = xPos;
|
||||||
Colour = colour,
|
topPoint.Colour = colour;
|
||||||
Anchor = Anchor.BottomLeft,
|
topPoint.Height = height;
|
||||||
Origin = Anchor.BottomCentre,
|
topPoint.Anchor = Anchor.TopLeft;
|
||||||
Height = height,
|
topPoint.Origin = Anchor.TopCentre;
|
||||||
});
|
|
||||||
|
var bottomPoint = getNextUsablePoint();
|
||||||
|
bottomPoint.X = xPos;
|
||||||
|
bottomPoint.Colour = colour;
|
||||||
|
bottomPoint.Anchor = Anchor.BottomLeft;
|
||||||
|
bottomPoint.Origin = Anchor.BottomCentre;
|
||||||
|
bottomPoint.Height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
beat++;
|
beat++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int usedDrawables = drawableIndex;
|
||||||
|
|
||||||
|
// save a few drawables beyond the currently used for edge cases.
|
||||||
|
while (drawableIndex < Math.Min(usedDrawables + 16, Count))
|
||||||
|
Children[drawableIndex++].Hide();
|
||||||
|
|
||||||
|
// expire any excess
|
||||||
|
while (drawableIndex < Count)
|
||||||
|
Children[drawableIndex++].Expire();
|
||||||
|
|
||||||
|
tickCache.Validate();
|
||||||
|
|
||||||
|
Drawable getNextUsablePoint()
|
||||||
|
{
|
||||||
|
PointVisualisation point;
|
||||||
|
if (drawableIndex >= Count)
|
||||||
|
Add(point = new PointVisualisation());
|
||||||
|
else
|
||||||
|
point = Children[drawableIndex];
|
||||||
|
|
||||||
|
drawableIndex++;
|
||||||
|
point.Show();
|
||||||
|
|
||||||
|
return point;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user