From 83fd251c7b1f4d6f2ba4a31f551fc0548e81f4b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 17:44:25 +0900 Subject: [PATCH 01/16] Pass sub-controlpoints as span slices --- .../TestCaseAutoJuiceStream.cs | 3 +- .../Objects/JuiceStream.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs | 14 ++--- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 6 +-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Visual/TestCaseHitObjectComposer.cs | 2 +- osu.Game.props | 2 +- .../Rulesets/Objects/BezierApproximator.cs | 9 ++-- .../Rulesets/Objects/CatmullApproximator.cs | 16 +++--- .../Objects/CircularArcApproximator.cs | 22 ++++---- .../Legacy/Catch/ConvertHitObjectParser.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 24 ++++++--- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../Legacy/Mania/ConvertHitObjectParser.cs | 2 +- .../Legacy/Osu/ConvertHitObjectParser.cs | 2 +- .../Legacy/Taiko/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/SliderCurve.cs | 51 ++++++++++++------- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 3 +- 18 files changed, 93 insertions(+), 73 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index 34e07170bd..5e68acde94 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; @@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { X = 0.5f - width / 2, - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 0344189af5..82e32d24d2 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Objects public SliderCurve Curve { get; } = new SliderCurve(); - public List ControlPoints + public Vector2[] ControlPoints { get { return Curve.ControlPoints; } set { Curve.ControlPoints = value; } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index 3f9464a98f..300ac16155 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(239, 176), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(154, 28), @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-(distance / 2), 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(distance, 0), @@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(200, 200), @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(150, 75), @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Bezier, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(150, 75), @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(0, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(-200, 0), @@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = Time.Current + 1000, Position = new Vector2(-100, 0), CurveType = CurveType.Catmull, - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(50, -50), diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 7a30e6b134..1b3725a15e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; @@ -33,8 +32,9 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var newControlPoints = new List(); - slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y))); + var newControlPoints = new Vector2[slider.ControlPoints.Length]; + for (int i = 0; i < slider.ControlPoints.Length; i++) + newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y); slider.ControlPoints = newControlPoints; slider.Curve?.Calculate(); // Recalculate the slider curve diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 7a0dcc77a6..c284335c98 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects public SliderCurve Curve { get; } = new SliderCurve(); - public List ControlPoints + public Vector2[] ControlPoints { get { return Curve.ControlPoints; } set { Curve.ControlPoints = value; } diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 5df371dd09..a2bc28bc3c 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual new Slider { Position = new Vector2(128, 256), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(216, 0), diff --git a/osu.Game.props b/osu.Game.props index ec859e64a5..4bcac479a0 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,7 +1,7 @@ - 7 + 7.2 ..\app.manifest diff --git a/osu.Game/Rulesets/Objects/BezierApproximator.cs b/osu.Game/Rulesets/Objects/BezierApproximator.cs index 6094934510..a1803e32f7 100644 --- a/osu.Game/Rulesets/Objects/BezierApproximator.cs +++ b/osu.Game/Rulesets/Objects/BezierApproximator.cs @@ -1,25 +1,26 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public class BezierApproximator + public readonly ref struct BezierApproximator { private readonly int count; - private readonly List controlPoints; + private readonly ReadOnlySpan controlPoints; private readonly Vector2[] subdivisionBuffer1; private readonly Vector2[] subdivisionBuffer2; private const float tolerance = 0.25f; private const float tolerance_sq = tolerance * tolerance; - public BezierApproximator(List controlPoints) + public BezierApproximator(ReadOnlySpan controlPoints) { this.controlPoints = controlPoints; - count = controlPoints.Count; + count = controlPoints.Length; subdivisionBuffer1 = new Vector2[count]; subdivisionBuffer2 = new Vector2[count * 2 - 1]; diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs index fabb55e8f6..78f8e471f3 100644 --- a/osu.Game/Rulesets/Objects/CatmullApproximator.cs +++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs @@ -1,40 +1,40 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public class CatmullApproximator + public readonly ref struct CatmullApproximator { /// /// The amount of pieces to calculate for each controlpoint quadruplet. /// private const int detail = 50; - private readonly List controlPoints; + private readonly ReadOnlySpan controlPoints; - public CatmullApproximator(List controlPoints) + public CatmullApproximator(ReadOnlySpan controlPoints) { this.controlPoints = controlPoints; } - /// /// Creates a piecewise-linear approximation of a Catmull-Rom spline. /// /// A list of vectors representing the piecewise-linear approximation. public List CreateCatmull() { - var result = new List(); + var result = new List((controlPoints.Length - 1) * detail * 2); - for (int i = 0; i < controlPoints.Count - 1; i++) + for (int i = 0; i < controlPoints.Length - 1; i++) { var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; var v2 = controlPoints[i]; - var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; - var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; + var v3 = i < controlPoints.Length - 1 ? controlPoints[i + 1] : v2 + v2 - v1; + var v4 = i < controlPoints.Length - 2 ? controlPoints[i + 2] : v3 + v3 - v2; for (int c = 0; c < detail; c++) { diff --git a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs index 7fb8d8a40e..28d7442aaf 100644 --- a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs +++ b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs @@ -8,21 +8,15 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public class CircularArcApproximator + public readonly ref struct CircularArcApproximator { - private readonly Vector2 a; - private readonly Vector2 b; - private readonly Vector2 c; - - private int amountPoints; - private const float tolerance = 0.1f; - public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c) + private readonly ReadOnlySpan controlPoints; + + public CircularArcApproximator(ReadOnlySpan controlPoints) { - this.a = a; - this.b = b; - this.c = c; + this.controlPoints = controlPoints; } /// @@ -31,6 +25,10 @@ namespace osu.Game.Rulesets.Objects /// A list of vectors representing the piecewise-linear approximation. public List CreateArc() { + Vector2 a = controlPoints[0]; + Vector2 b = controlPoints[1]; + Vector2 c = controlPoints[2]; + float aSq = (b - c).LengthSquared; float bSq = (a - c).LengthSquared; float cSq = (a - b).LengthSquared; @@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Objects // is: 2 * Math.Acos(1 - TOLERANCE / r) // The special case is required for extremely short sliders where the radius is smaller than // the tolerance. This is a pathological rather than a realistic case. - amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); + int amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); List output = new List(amountPoints); diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 802080aedb..9c9fc2e742 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 72168a4cd2..965e76d27a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -72,10 +72,18 @@ namespace osu.Game.Rulesets.Objects.Legacy { CurveType curveType = CurveType.Catmull; double length = 0; - var points = new List { Vector2.Zero }; - string[] pointsplit = split[5].Split('|'); - foreach (string t in pointsplit) + string[] pointSplit = split[5].Split('|'); + + int pointCount = 1; + foreach (var t in pointSplit) + if (t.Length > 1) + pointCount++; + + var points = new Vector2[pointCount]; + + int pointIndex = 1; + foreach (string t in pointSplit) { if (t.Length == 1) { @@ -94,16 +102,18 @@ namespace osu.Game.Rulesets.Objects.Legacy curveType = CurveType.PerfectCurve; break; } + continue; } string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); + points[pointIndex++] = new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos; } // osu-stable special-cased colinear perfect curves to a CurveType.Linear - bool isLinear(List p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); - if (points.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) + bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + + if (points.Length == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) curveType = CurveType.Linear; int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); @@ -262,7 +272,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The slider repeat count. /// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider. /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); /// /// Creates a legacy Spinner-type hit object. diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index ef1eecec3d..93c49ea3ce 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// s don't need a curve since they're converted to ruleset-specific hitobjects. /// public SliderCurve Curve { get; } = null; - public List ControlPoints { get; set; } + public Vector2[] ControlPoints { get; set; } public CurveType CurveType { get; set; } public double Distance { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 6f59965e18..68e05f6223 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index acd0de8688..f3c815fc32 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index e5904825c2..985a032640 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko return new ConvertHit(); } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/SliderCurve.cs b/osu.Game/Rulesets/Objects/SliderCurve.cs index 3932d8ed9d..60f7901a48 100644 --- a/osu.Game/Rulesets/Objects/SliderCurve.cs +++ b/osu.Game/Rulesets/Objects/SliderCurve.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.MathUtils; @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Objects { public double Distance; - public List ControlPoints; + public Vector2[] ControlPoints; public CurveType CurveType = CurveType.PerfectCurve; @@ -22,19 +23,17 @@ namespace osu.Game.Rulesets.Objects private readonly List calculatedPath = new List(); private readonly List cumulativeLength = new List(); - private List calculateSubpath(List subControlPoints) + private List calculateSubpath(ReadOnlySpan subControlPoints) { switch (CurveType) { - case CurveType.Linear: - return subControlPoints; case CurveType.PerfectCurve: //we can only use CircularArc iff we have exactly three control points and no dissection. - if (ControlPoints.Count != 3 || subControlPoints.Count != 3) + if (ControlPoints.Length != 3 || subControlPoints.Length != 3) break; // Here we have exactly 3 control points. Attempt to fit a circular arc. - List subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc(); + List subpath = new CircularArcApproximator(subControlPoints).CreateArc(); // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. if (subpath.Count == 0) @@ -55,18 +54,32 @@ namespace osu.Game.Rulesets.Objects // Sliders may consist of various subpaths separated by two consecutive vertices // with the same position. The following loop parses these subpaths and computes // their shape independently, consecutively appending them to calculatedPath. - List subControlPoints = new List(); - for (int i = 0; i < ControlPoints.Count; ++i) - { - subControlPoints.Add(ControlPoints[i]); - if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) - { - List subpath = calculateSubpath(subControlPoints); - foreach (Vector2 t in subpath) - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - subControlPoints.Clear(); + int start = 0; + int end = 0; + + for (int i = 0; i < ControlPoints.Length; ++i) + { + end++; + + if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) + { + ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + + if (CurveType == CurveType.Linear) + { + foreach (var t in cpSpan) + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); + } + else + { + foreach (Vector2 t in calculateSubpath(cpSpan)) + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); + } + + start = end; } } } @@ -166,7 +179,7 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + if (calculatedPath.Count == 0 && ControlPoints.Length > 0) Calculate(); double d0 = progressToDistance(p0); @@ -193,7 +206,7 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + if (calculatedPath.Count == 0 && ControlPoints.Length > 0) Calculate(); double d = progressToDistance(progress); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 54dcb26ae2..69b2f722e7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects.Types @@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The control points that shape the curve. /// - List ControlPoints { get; } + Vector2[] ControlPoints { get; } /// /// The type of curve. From bd99a872988ba09a33ab837c3187abb34e78a3f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 17:48:47 +0900 Subject: [PATCH 02/16] Use ordinal comparison in LegacyBeatmapDecoder --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 5b5bc5d936..d9f72315ee 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -58,7 +58,7 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } - protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"); + protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal); protected override void ParseLine(Beatmap beatmap, Section section, string line) { From 5b544a0c97bf8fe29d93897abe4ba2a38244d3b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 18:09:19 +0900 Subject: [PATCH 03/16] Remove allocation of string.IndexOf() --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index a9e1e4c55d..db1fe6b2ed 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -69,7 +69,9 @@ namespace osu.Game.Beatmaps.Formats protected string StripComments(string line) { - var index = line.IndexOf("//", StringComparison.Ordinal); + const string comment_prefix = "//"; + + var index = line.AsSpan().IndexOf(comment_prefix.AsSpan()); if (index > 0) return line.Substring(0, index); return line; From 10fb1d20d1c4498ec232d91f2ffdc8781ebeae61 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 18:38:04 +0900 Subject: [PATCH 04/16] Remove allocation of control point with every binarysearch --- .../Beatmaps/ControlPoints/ControlPointInfo.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index dad69321a5..2be7f4e16d 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -12,6 +12,11 @@ namespace osu.Game.Beatmaps.ControlPoints [Serializable] public class ControlPointInfo { + private readonly TimingControlPoint timingPointSearch = new TimingControlPoint(); + private readonly DifficultyControlPoint difficultyPointSearch = new DifficultyControlPoint(); + private readonly SampleControlPoint samplePointSearch = new SampleControlPoint(); + private readonly EffectControlPoint effectPointSearch = new EffectControlPoint(); + /// /// All timing points. /// @@ -41,28 +46,28 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the difficulty control point at. /// The difficulty control point. - public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time); + public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time, difficultyPointSearch); /// /// Finds the effect control point that is active at . /// /// The time to find the effect control point at. /// The effect control point. - public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time); + public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time, effectPointSearch); /// /// Finds the sound control point that is active at . /// /// The time to find the sound control point at. /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault()); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, samplePointSearch, SamplePoints.FirstOrDefault()); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingPointSearch, TimingPoints.FirstOrDefault()); /// /// Finds the maximum BPM represented by any timing control point. @@ -92,7 +97,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. If null, a new control point will be constructed. /// The active control point at . - private T binarySearch(SortedList list, double time, T prePoint = null) + private T binarySearch(SortedList list, double time, T searchPoint, T prePoint = null) where T : ControlPoint, new() { if (list == null) @@ -104,7 +109,8 @@ namespace osu.Game.Beatmaps.ControlPoints if (time < list[0].Time) return prePoint ?? new T(); - int index = list.BinarySearch(new T { Time = time }); + searchPoint.Time = time; + int index = list.BinarySearch(searchPoint); // Check if we've found an exact match (t == time) if (index >= 0) From 7b2a30f5b4624481cd5ab23bb3e9b59d6712ac79 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 19:52:03 +0900 Subject: [PATCH 05/16] Reduce some high-profile enumerator allocations --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 2be7f4e16d..db5574e5ce 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -60,14 +60,14 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the sound control point at. /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, samplePointSearch, SamplePoints.FirstOrDefault()); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, samplePointSearch, SamplePoints.Count > 0 ? SamplePoints[0] : null); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingPointSearch, TimingPoints.FirstOrDefault()); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingPointSearch, TimingPoints.Count > 0 ? TimingPoints[0] : null); /// /// Finds the maximum BPM represented by any timing control point. From ec9f23ab73849fff9b626f935b3e2c8ffbbc9b4e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 19:53:07 +0900 Subject: [PATCH 06/16] Make IBeatmap.HitObjects an IReadOnlyList --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Beatmaps/Patterns/Legacy/PatternGenerator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 2 +- osu.Game/Beatmaps/IBeatmap.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index c15b303048..5d8480d969 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps TargetColumns = (int)Math.Max(1, roundedCircleSize); else { - float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count(); + float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) TargetColumns = 7; else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 05ca1d4365..7bd39adb45 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy drainTime = 10000; BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; - conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); return conversionDifficulty.Value; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 028e3acc57..8fc2b69267 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; - int maxCombo = beatmap.HitObjects.Count(); + int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d4a60dd52f..b0887ac72b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); - beatmapMaxCombo = Beatmap.HitObjects.Count(); + beatmapMaxCombo = Beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above) beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); } diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 9aabb434a3..4ef7b28d49 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps [JsonConverter(typeof(TypedListConverter))] public List HitObjects = new List(); - IEnumerable IBeatmap.HitObjects => HitObjects; + IReadOnlyList IBeatmap.HitObjects => HitObjects; public virtual IEnumerable GetStatistics() => Enumerable.Empty(); diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index fe20bce98a..3d8b94dc46 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps /// /// The hitobjects contained by this beatmap. /// - IEnumerable HitObjects { get; } + IReadOnlyList HitObjects { get; } /// /// Returns statistics for the contained in this beatmap. From fb1760f580ca6c6ec90526f6dd3fd7398e9decdb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Oct 2018 20:29:26 +0900 Subject: [PATCH 07/16] Simplify linq from beatmap conversion --- osu.Game/Beatmaps/BeatmapConverter.cs | 43 +++++++++++++-------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index a1bb70135a..6496a84803 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -55,39 +55,38 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo = original.BeatmapInfo; beatmap.ControlPointInfo = original.ControlPointInfo; - beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); + beatmap.HitObjects = convertHitObjects(original.HitObjects, original); beatmap.Breaks = original.Breaks; return beatmap; } - /// - /// Converts a hit object. - /// - /// The hit object to convert. - /// The un-converted Beatmap. - /// The converted hit object. - private IEnumerable convert(HitObject original, IBeatmap beatmap) + private List convertHitObjects(IReadOnlyList hitObjects, IBeatmap beatmap) { - // Check if the hitobject is already the converted type - T tObject = original as T; - if (tObject != null) - { - yield return tObject; - yield break; - } + var result = new List(hitObjects.Count); - var converted = ConvertHitObject(original, beatmap).ToList(); - ObjectConverted?.Invoke(original, converted); - - // Convert the hit object - foreach (var obj in converted) + // ReSharper disable once ForCanBeConvertedToForeach + foreach (var obj in hitObjects) { - if (obj == null) + if (obj is T tObj) + { + result.Add(tObj); continue; + } - yield return obj; + var converted = ConvertHitObject(obj, beatmap); + + // Note: This will not perform .ToList() if ObjectConverted is null + ObjectConverted?.Invoke(obj, converted.ToList()); + + foreach (var c in converted) + { + if (c != null) + result.Add(c); + } } + + return result; } /// From 72aaaa4a7426bb11a9e2bbe5859b4197654cfe9e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Oct 2018 10:29:53 +0900 Subject: [PATCH 08/16] PointSearch -> SearchPoint --- .../Beatmaps/ControlPoints/ControlPointInfo.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index db5574e5ce..4e36a755d1 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -12,10 +12,10 @@ namespace osu.Game.Beatmaps.ControlPoints [Serializable] public class ControlPointInfo { - private readonly TimingControlPoint timingPointSearch = new TimingControlPoint(); - private readonly DifficultyControlPoint difficultyPointSearch = new DifficultyControlPoint(); - private readonly SampleControlPoint samplePointSearch = new SampleControlPoint(); - private readonly EffectControlPoint effectPointSearch = new EffectControlPoint(); + private readonly TimingControlPoint timingSearchPoint = new TimingControlPoint(); + private readonly DifficultyControlPoint difficultySearchPoint = new DifficultyControlPoint(); + private readonly SampleControlPoint sampleSearchPoint = new SampleControlPoint(); + private readonly EffectControlPoint effectSearchPoint = new EffectControlPoint(); /// /// All timing points. @@ -46,28 +46,28 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the difficulty control point at. /// The difficulty control point. - public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time, difficultyPointSearch); + public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time, difficultySearchPoint); /// /// Finds the effect control point that is active at . /// /// The time to find the effect control point at. /// The effect control point. - public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time, effectPointSearch); + public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time, effectSearchPoint); /// /// Finds the sound control point that is active at . /// /// The time to find the sound control point at. /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, samplePointSearch, SamplePoints.Count > 0 ? SamplePoints[0] : null); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, sampleSearchPoint, SamplePoints.Count > 0 ? SamplePoints[0] : null); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingPointSearch, TimingPoints.Count > 0 ? TimingPoints[0] : null); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingSearchPoint, TimingPoints.Count > 0 ? TimingPoints[0] : null); /// /// Finds the maximum BPM represented by any timing control point. From 060cc24dba4fef9e38ab56339aa6386839474bdd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Oct 2018 11:11:04 +0900 Subject: [PATCH 09/16] Cleanup slidercurve calculation --- osu.Game/Rulesets/Objects/SliderCurve.cs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderCurve.cs b/osu.Game/Rulesets/Objects/SliderCurve.cs index 60f7901a48..dfccdf68f2 100644 --- a/osu.Game/Rulesets/Objects/SliderCurve.cs +++ b/osu.Game/Rulesets/Objects/SliderCurve.cs @@ -27,6 +27,12 @@ namespace osu.Game.Rulesets.Objects { switch (CurveType) { + case CurveType.Linear: + var result = new List(subControlPoints.Length); + foreach (var c in subControlPoints) + result.Add(c); + + return result; case CurveType.PerfectCurve: //we can only use CircularArc iff we have exactly three control points and no dissection. if (ControlPoints.Length != 3 || subControlPoints.Length != 3) @@ -66,18 +72,9 @@ namespace osu.Game.Rulesets.Objects { ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); - if (CurveType == CurveType.Linear) - { - foreach (var t in cpSpan) - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - } - else - { - foreach (Vector2 t in calculateSubpath(cpSpan)) - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - } + foreach (Vector2 t in calculateSubpath(cpSpan)) + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); start = end; } From 3ad93d5a07f666ab6f790e324dfd8b29f5a97253 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Oct 2018 11:40:51 +0900 Subject: [PATCH 10/16] Remove redundant type specifications --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 2 +- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 4 ++-- osu.Game/Graphics/UserInterface/RollingCounter.cs | 2 +- osu.Game/Storyboards/CommandTimeline.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 04a7cb64d3..30803d1545 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) accentColour = colours.PinkDarker; updateAccentColour(); } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 24183e07a0..e7d6a87349 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -53,7 +53,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) AccentColour = colours.Blue; } @@ -142,7 +142,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) AccentColour = colours.Blue; } diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index f84404a911..c2162b8a42 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -134,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface /// public virtual void ResetCount() { - SetCountWithoutRolling(default(T)); + SetCountWithoutRolling(default); } /// diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 8a032064d3..64b0a45fcd 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -21,8 +21,8 @@ namespace osu.Game.Storyboards private Cached endTimeBacking; public double EndTime => endTimeBacking.IsValid ? endTimeBacking : endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue; - public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T); - public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T); + public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default; + public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default; public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue) { From 41391a66278494926572fa8d60cf758afa14307a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Oct 2018 12:01:58 +0900 Subject: [PATCH 11/16] Fix beatmap conversion tests failing --- osu.Game/Beatmaps/BeatmapConverter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 6496a84803..a2d7a69b5d 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -76,8 +76,11 @@ namespace osu.Game.Beatmaps var converted = ConvertHitObject(obj, beatmap); - // Note: This will not perform .ToList() if ObjectConverted is null - ObjectConverted?.Invoke(obj, converted.ToList()); + if (ObjectConverted != null) + { + converted = converted.ToList(); + ObjectConverted.Invoke(obj, converted); + } foreach (var c in converted) { From d431dd59a8a12e43ed9c3310d5ebbe9b15acab09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Oct 2018 10:53:21 +0900 Subject: [PATCH 12/16] Cleanups --- osu.Game/Beatmaps/BeatmapConverter.cs | 1 - osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index a2d7a69b5d..3cb7833a12 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -65,7 +65,6 @@ namespace osu.Game.Beatmaps { var result = new List(hitObjects.Count); - // ReSharper disable once ForCanBeConvertedToForeach foreach (var obj in hitObjects) { if (obj is T tObj) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index db1fe6b2ed..2ef7c5de30 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -69,9 +69,7 @@ namespace osu.Game.Beatmaps.Formats protected string StripComments(string line) { - const string comment_prefix = "//"; - - var index = line.AsSpan().IndexOf(comment_prefix.AsSpan()); + var index = line.AsSpan().IndexOf("//".AsSpan()); if (index > 0) return line.Substring(0, index); return line; From 336b6fa38ef95f7047f56c86044d1867064935be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Oct 2018 11:23:18 +0900 Subject: [PATCH 13/16] Perform a custom binary search to remove thread-unsafeness --- .../ControlPoints/ControlPointInfo.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 4e36a755d1..d5826e85b0 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -12,11 +12,6 @@ namespace osu.Game.Beatmaps.ControlPoints [Serializable] public class ControlPointInfo { - private readonly TimingControlPoint timingSearchPoint = new TimingControlPoint(); - private readonly DifficultyControlPoint difficultySearchPoint = new DifficultyControlPoint(); - private readonly SampleControlPoint sampleSearchPoint = new SampleControlPoint(); - private readonly EffectControlPoint effectSearchPoint = new EffectControlPoint(); - /// /// All timing points. /// @@ -46,28 +41,28 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the difficulty control point at. /// The difficulty control point. - public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time, difficultySearchPoint); + public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time); /// /// Finds the effect control point that is active at . /// /// The time to find the effect control point at. /// The effect control point. - public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time, effectSearchPoint); + public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time); /// /// Finds the sound control point that is active at . /// /// The time to find the sound control point at. /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, sampleSearchPoint, SamplePoints.Count > 0 ? SamplePoints[0] : null); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, timingSearchPoint, TimingPoints.Count > 0 ? TimingPoints[0] : null); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null); /// /// Finds the maximum BPM represented by any timing control point. @@ -97,7 +92,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. If null, a new control point will be constructed. /// The active control point at . - private T binarySearch(SortedList list, double time, T searchPoint, T prePoint = null) + private T binarySearch(SortedList list, double time, T prePoint = null) where T : ControlPoint, new() { if (list == null) @@ -109,18 +104,23 @@ namespace osu.Game.Beatmaps.ControlPoints if (time < list[0].Time) return prePoint ?? new T(); - searchPoint.Time = time; - int index = list.BinarySearch(searchPoint); + int l = 0; + int r = list.Count - 1; - // Check if we've found an exact match (t == time) - if (index >= 0) - return list[index]; + while (l <= r) + { + int pivot = l + ((r - l) >> 1); - index = ~index; + if (list[pivot].Time < time) + l = pivot + 1; + else if (list[pivot].Time > time) + r = pivot - 1; + else + return list[pivot]; + } - // BinarySearch will return the index of the first element _greater_ than the search - // This is the inactive point - the active point is the one before it (index - 1) - return list[index - 1]; + // l will be the first control point with Time > time, but we want the one before it + return list[l - 1]; } } } From 1bebceada7084e10f1a1df80f955c0d98cd5ba73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Oct 2018 11:39:29 +0900 Subject: [PATCH 14/16] Unroll loop for the last control point --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index d5826e85b0..f064d53358 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -104,8 +104,11 @@ namespace osu.Game.Beatmaps.ControlPoints if (time < list[0].Time) return prePoint ?? new T(); + if (time >= list[list.Count - 1].Time) + return list[list.Count - 1]; + int l = 0; - int r = list.Count - 1; + int r = list.Count - 2; while (l <= r) { From a6d05febff65539ae34163b6e69c588d9ac2fb74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Oct 2018 16:26:41 +0900 Subject: [PATCH 15/16] Attempt with different key source --- appveyor_deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index 22a4859885..355f0603be 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -10,8 +10,8 @@ before_build: - cmd: nuget restore -verbosity quiet build_script: - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1')) - - appveyor DownloadFile https://puu.sh/BCrS8/7faccf7876.enc # signing certificate - - cmd: appveyor-tools\secure-file -decrypt 7faccf7876.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx + - appveyor DownloadFile https://www.dropbox.com/s/f7hv069mr5tqi2j/deanherbert.pfx.enc?dl=1 # signing certificate + - cmd: appveyor-tools\secure-file -decrypt deanherbert.pfx.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - cd osu-deploy - nuget restore -verbosity quiet From 9aeba149f664806fc15029ad17bbaaa1387d8a3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Oct 2018 19:39:32 +0900 Subject: [PATCH 16/16] Update squirrel for more diagnostics --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 1e8bf05e01..2be352165e 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,7 +27,7 @@ - +