Skip to content

fix: not update starDifficultyBindable immediately in computeStarRating() to avoid flicker#37204

Open
hesitling wants to merge 1 commit intoppy:masterfrom
hesitling:fix/issue-36964
Open

fix: not update starDifficultyBindable immediately in computeStarRating() to avoid flicker#37204
hesitling wants to merge 1 commit intoppy:masterfrom
hesitling:fix/issue-36964

Conversation

@hesitling
Copy link
Copy Markdown
Contributor

@hesitling hesitling commented Apr 5, 2026

  • When modifying Mod/Ruleset, the difficulty cache update causes the beatmap difficulty value to change. At this point, the updated value is calculated with Mod and Ruleset applied (the correct new value).
  • During PrepareForUse(), it binds to the difficulty cache update, and because runOnceImmediately = true, it immediately triggers an update. At this moment, both the old value and the updated value are the beatmap's base difficulty values (no Mod, no Ruleset).
  • The difficulty cache updates again. The old value is the base value, and the new value is the difficulty value calculated with Mod and Ruleset applied.

The flickering occurs because the intermediate PrepareForUse() reverts the difficulty display value back to the beatmap's base difficulty value. Setting runOnceImmediately = false resolves the difficulty value flickering issue.

Resolves #36964.

Comment on lines 243 to 244
starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, SongSelect.DIFFICULTY_CALCULATION_DEBOUNCE);
starDifficultyBindable.BindValueChanged(starDifficulty =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels as though there could be a threading edge case where the value is updated between these two lines, but the threaded retrieval does schedule back to Update thread for the actual updates, so it should be okay.

I'd kinda hope to see test coverage though.

…ating()` to avoid flicker

- When modifying Mod/Ruleset, the difficulty cache update causes the beatmap difficulty value to change. At this point, the updated value is calculated with Mod and Ruleset applied (the correct new value).
- During `PrepareForUse()`, it binds to the difficulty cache update, and because `runOnceImmediately = true`, it immediately triggers an update.
  At this moment, both the old value and the updated value are the beatmap's base difficulty values (no Mod, no Ruleset).
- The difficulty cache updates again. The old value is the base value, and the new value is the difficulty value calculated with Mod and Ruleset applied.

The flickering occurs because the intermediate `PrepareForUse()` reverts the difficulty display value back to the beatmap's base difficulty value. Setting `runOnceImmediately = false` resolves the difficulty value flickering issue.
@hesitling
Copy link
Copy Markdown
Contributor Author

Maybe another solution.

--- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -131,10 +131,20 @@ public void Invalidate(IBeatmapInfo oldBeatmap, IBeatmapInfo newBeatmap)
         /// <returns>A bindable that is updated to contain the star difficulty when it becomes available. May be an approximation while in an initial calculating state.</returns>
         public IBindable<StarDifficulty> GetBindableDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default, int computationDelay = 0)
         {
+            var difficulty = new StarDifficulty(beatmapInfo.StarRating, 0);
+
+            var localBeatmapInfo = beatmapInfo as BeatmapInfo;
+
+            if (localBeatmapInfo != null)
+            {
+                var lookup = new DifficultyCacheLookup(localBeatmapInfo, currentRuleset.Value, currentMods.Value);
+                if (CheckExists(lookup, out var cached) && cached != null)
+                    difficulty = cached.Value;
+            }
             var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken)
             {
                 // Start with an approximate known value instead of zero.
-                Value = new StarDifficulty(beatmapInfo.StarRating, 0)
+                Value = difficulty
             };

             updateBindable(bindable, currentRuleset.Value, currentMods.Value, cancellationToken, computationDelay);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Beatmap flicker when using mods & importing new maps

2 participants