Skip to content

Commit

Permalink
Merge branch 'master' into discord-dnd-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
jvyden committed Feb 27, 2024
2 parents e053c08 + 2889cf3 commit e2a3643
Show file tree
Hide file tree
Showing 34 changed files with 417 additions and 174 deletions.
6 changes: 6 additions & 0 deletions osu.Desktop/OsuGameDesktop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Reflection;
using System.Runtime.Versioning;
using Microsoft.Win32;
using osu.Desktop.Performance;
using osu.Desktop.Security;
using osu.Framework.Platform;
using osu.Game;
Expand All @@ -15,9 +16,11 @@
using osu.Framework.Logging;
using osu.Game.Updater;
using osu.Desktop.Windows;
using osu.Framework.Allocation;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Online.Multiplayer;
using osu.Game.Performance;
using osu.Game.Utils;
using SDL2;

Expand All @@ -28,6 +31,9 @@ internal partial class OsuGameDesktop : OsuGame
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
private ArchiveImportIPCChannel? archiveImportIPCChannel;

[Cached(typeof(IHighPerformanceSessionManager))]
private readonly HighPerformanceSessionManager highPerformanceSessionManager = new HighPerformanceSessionManager();

public OsuGameDesktop(string[]? args = null)
: base(args)
{
Expand Down
43 changes: 43 additions & 0 deletions osu.Desktop/Performance/HighPerformanceSessionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

using System;
using System.Runtime;
using osu.Framework.Allocation;
using osu.Framework.Logging;
using osu.Game.Performance;

namespace osu.Desktop.Performance
{
public class HighPerformanceSessionManager : IHighPerformanceSessionManager
{
private GCLatencyMode originalGCMode;

public IDisposable BeginSession()
{
enableHighPerformanceSession();
return new InvokeOnDisposal<HighPerformanceSessionManager>(this, static m => m.disableHighPerformanceSession());
}

private void enableHighPerformanceSession()
{
Logger.Log("Starting high performance session");

originalGCMode = GCSettings.LatencyMode;
GCSettings.LatencyMode = GCLatencyMode.LowLatency;

// Without doing this, the new GC mode won't kick in until the next GC, which could be at a more noticeable point in time.
GC.Collect(0);
}

private void disableHighPerformanceSession()
{
Logger.Log("Ending high performance session");

if (GCSettings.LatencyMode == GCLatencyMode.LowLatency)
GCSettings.LatencyMode = originalGCMode;

// No GC.Collect() as we were already collecting at a higher frequency in the old mode.
}
}
}
22 changes: 22 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
Expand Down Expand Up @@ -32,6 +33,27 @@ public partial class TestSceneOsuModFlashlight : OsuModTestScene
[Test]
public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true });

[Test]
public void TestPlayfieldBasedSize()
{
ModFlashlight mod = new OsuModFlashlight();
CreateModTest(new ModTestData
{
Mod = mod,
PassCondition = () =>
{
var flashlightOverlay = Player.DrawableRuleset.Overlays
.OfType<ModFlashlight<OsuHitObject>.Flashlight>()
.First();

return Precision.AlmostEquals(mod.DefaultFlashlightSize * .5f, flashlightOverlay.GetSize());
}
});

AddStep("adjust playfield scale", () =>
Player.DrawableRuleset.Playfield.Scale = new Vector2(.5f));
}

[Test]
public void TestSliderDimsOnlyAfterStartTime()
{
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public partial class DrawableSlider : DrawableOsuHitObject

protected override IEnumerable<Drawable> DimmablePieces => new Drawable[]
{
HeadCircle,
// HeadCircle should not be added to this list, as it handles dimming itself
TailCircle,
repeatContainer,
Body,
Expand Down
32 changes: 27 additions & 5 deletions osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,9 @@ protected override void Update()
if (HandleUserInput)
{
bool isValidSpinningTime = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime;
bool correctButtonPressed = (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);

RotationTracker.Tracking = !Result.HasResult
&& correctButtonPressed
&& correctButtonPressed()
&& isValidSpinningTime;
}

Expand All @@ -292,11 +291,34 @@ protected override void Update()
// Ticks can theoretically be judged at any point in the spinner's duration.
// A tick must be alive to correctly play back samples,
// but for performance reasons, we only want to keep the next tick alive.
var next = NestedHitObjects.FirstOrDefault(h => !h.Judged);
DrawableHitObject nextTick = null;

foreach (var nested in NestedHitObjects)
{
if (!nested.Judged)
{
nextTick = nested;
break;
}
}

// See default `LifetimeStart` as set in `DrawableSpinnerTick`.
if (next?.LifetimeStart == double.MaxValue)
next.LifetimeStart = HitObject.StartTime;
if (nextTick?.LifetimeStart == double.MaxValue)
nextTick.LifetimeStart = HitObject.StartTime;
}

private bool correctButtonPressed()
{
if (OsuActionInputManager == null)
return false;

foreach (var action in OsuActionInputManager.PressedActions)
{
if (action == OsuAction.LeftButton || action == OsuAction.RightButton)
return true;
}

return false;
}

protected override void UpdateAfterChildren()
Expand Down
32 changes: 5 additions & 27 deletions osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
Expand Down Expand Up @@ -111,42 +110,21 @@ protected override void LoadComplete()
{
spmCounter.Text = Math.Truncate(spm.NewValue).ToString(@"#0");
}, true);

drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
}

protected override void Update()
{
base.Update();

if (!spmContainer.IsPresent && drawableSpinner.Result?.TimeStarted != null)
fadeCounterOnTimeStart();
}

private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
if (!(drawableHitObject is DrawableSpinner))
return;

fadeCounterOnTimeStart();
updateSpmAlpha();
}

private void fadeCounterOnTimeStart()
private void updateSpmAlpha()
{
if (drawableSpinner.Result?.TimeStarted is double startTime)
{
using (BeginAbsoluteSequence(startTime))
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
}
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

if (drawableSpinner.IsNotNull())
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
spmContainer.Alpha = (float)Math.Clamp((Clock.CurrentTime - startTime) / drawableSpinner.HitObject.TimeFadeIn, 0, 1);
else
spmContainer.Alpha = 0;
}
}
}
32 changes: 5 additions & 27 deletions osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
Expand Down Expand Up @@ -117,42 +116,21 @@ protected override void LoadComplete()
{
spmCounter.Text = Math.Truncate(spm.NewValue).ToString(@"#0");
}, true);

drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
}

protected override void Update()
{
base.Update();

if (!spmContainer.IsPresent && drawableSpinner.Result?.TimeStarted != null)
fadeCounterOnTimeStart();
}

private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
if (!(drawableHitObject is DrawableSpinner))
return;

fadeCounterOnTimeStart();
updateSpmAlpha();
}

private void fadeCounterOnTimeStart()
private void updateSpmAlpha()
{
if (drawableSpinner.Result?.TimeStarted is double startTime)
{
using (BeginAbsoluteSequence(startTime))
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
}
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

if (drawableSpinner.IsNotNull())
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
spmContainer.Alpha = (float)Math.Clamp((Clock.CurrentTime - startTime) / drawableSpinner.HitObject.TimeFadeIn, 0, 1);
else
spmContainer.Alpha = 0;
}
}
}
10 changes: 6 additions & 4 deletions osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
Expand Down Expand Up @@ -33,14 +32,16 @@ protected override void LoadComplete()
drawableSpinner.HitObjectApplied += resetState;
}

private RotationRecord lastRecord;

public void SetRotation(float currentRotation)
{
// If we've gone back in time, it's fine to work with a fresh set of records for now
if (records.Count > 0 && Time.Current < records.Last().Time)
if (records.Count > 0 && Time.Current < lastRecord.Time)
records.Clear();

// Never calculate SPM by same time of record to avoid 0 / 0 = NaN or X / 0 = Infinity result.
if (records.Count > 0 && Precision.AlmostEquals(Time.Current, records.Last().Time))
if (records.Count > 0 && Precision.AlmostEquals(Time.Current, lastRecord.Time))
return;

if (records.Count > 0)
Expand All @@ -52,11 +53,12 @@ record = records.Dequeue();
result.Value = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
}

records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current });
records.Enqueue(lastRecord = new RotationRecord { Rotation = currentRotation, Time = Time.Current });
}

private void resetState(DrawableHitObject hitObject)
{
lastRecord = default;
result.Value = 0;
records.Clear();
}
Expand Down
5 changes: 3 additions & 2 deletions osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
Expand Down Expand Up @@ -228,7 +227,9 @@ public override void ApplyState()
int futurePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer());

points.Clear();
points.AddRange(Source.SmokePoints.Skip(firstVisiblePointIndex).Take(futurePointIndex - firstVisiblePointIndex));

for (int i = firstVisiblePointIndex; i < futurePointIndex; i++)
points.Add(Source.SmokePoints[i]);
}

protected sealed override void Draw(IRenderer renderer)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.

using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
Expand All @@ -17,6 +18,7 @@
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Tests.Visual;
using osuTK;

namespace osu.Game.Tests.Editing
{
Expand Down Expand Up @@ -228,6 +230,28 @@ public void GetSnappedDistanceFromDistance()
assertSnappedDistance(400, 400);
}

[Test]
public void TestUseCurrentSnap()
{
AddStep("add objects to beatmap", () =>
{
editorBeatmap.Add(new HitCircle { StartTime = 1000 });
editorBeatmap.Add(new HitCircle { Position = new Vector2(100), StartTime = 2000 });
});

AddStep("hover use current snap button", () => InputManager.MoveMouseTo(composer.ChildrenOfType<ExpandableButton>().Single()));
AddUntilStep("use current snap expanded", () => composer.ChildrenOfType<ExpandableButton>().Single().Expanded.Value, () => Is.True);

AddStep("seek before first object", () => EditorClock.Seek(0));
AddUntilStep("use current snap not available", () => composer.ChildrenOfType<ExpandableButton>().Single().Enabled.Value, () => Is.False);

AddStep("seek to between objects", () => EditorClock.Seek(1500));
AddUntilStep("use current snap available", () => composer.ChildrenOfType<ExpandableButton>().Single().Enabled.Value, () => Is.True);

AddStep("seek after last object", () => EditorClock.Seek(2500));
AddUntilStep("use current snap not available", () => composer.ChildrenOfType<ExpandableButton>().Single().Enabled.Value, () => Is.False);
}

private void assertSnapDistance(float expectedDistance, HitObject? referenceObject, bool includeSliderVelocity)
=> AddAssert($"distance is {expectedDistance}", () => composer.DistanceSnapProvider.GetBeatSnapDistanceAt(referenceObject ?? new HitObject(), includeSliderVelocity), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON));

Expand Down
Loading

0 comments on commit e2a3643

Please sign in to comment.