Skip to content

Avoid bindable overheads in OsuHitObject.StackHeight implementation#38162

Open
peppy wants to merge 1 commit into
ppy:masterfrom
peppy:avoid-bindable-overhead-diffcalc
Open

Avoid bindable overheads in OsuHitObject.StackHeight implementation#38162
peppy wants to merge 1 commit into
ppy:masterfrom
peppy:avoid-bindable-overhead-diffcalc

Conversation

@peppy

@peppy peppy commented Jun 26, 2026

Copy link
Copy Markdown
Member

We made this HitObjectProperty class quite a while back with the concept of avoiding bindable initialisation when they are never going to be used.

Unfortunately there are multiple cases where bindables are accessed to bind event flows which break this.

This one is easy to fix, so I've done so.

The other remaining case is more egregious and I can't thing of a ten-minute-fix for it. If anyone has ideas, please take a look:

n.ComboIndexBindable.BindTo(hasCombo.ComboIndexBindable);
n.ComboIndexWithOffsetsBindable.BindTo(hasCombo.ComboIndexWithOffsetsBindable);
n.IndexInCurrentComboBindable.BindTo(hasCombo.IndexInCurrentComboBindable);

We made this `HitObjectProperty` class quite a while back with the
concept of avoiding bindable initialisation when they are never going to
be used.

Unfortunately there are multiple cases where bindables are accessed to
bind event flows which break this.

This one is easy to fix, so I've done so.

The other remaining case is more egregious and I can't thing of a
ten-minute-fix for it. If anyone has ideas, please take a look:

https://github.com/ppy/osu/blob/8b542e5442f42ad132af0414a4f7191df9718a99/osu.Game/Rulesets/Objects/HitObject.cs#L123-L125
@peppy peppy added the type/performance Deals with performance regressions or fixes without changing functionality. label Jun 26, 2026
@peppy peppy moved this from Inbox to Pending Review in osu! team task tracker Jun 26, 2026
@peppy

peppy commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

I split this out of diffcalc changes, because I noticed it as a weirdness that really shouldn't be showing in profiling in the first place:

Jump Desktop 2026-06-26 at 09 34 35

@bdach bdach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If anyone has ideas, please take a look:

I have ideas, but they're either not good, or not quick. Sometimes both of those two.

Comment on lines +88 to +100
if (stackHeight.BindableCreated)
return stackHeight.Bindable;

stackHeight.Bindable.BindValueChanged(height =>
{
foreach (var nested in NestedHitObjects)
{
if (nested is OsuHitObject osuHitObject)
osuHitObject.StackHeight = height.NewValue;
}
});

return stackHeight.Bindable;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's hope this doesn't get torpedoed by its lack of thread safety I guess? Closest proxy to knowing that is letting CI run, so I'm going to wait for that before clicking any buttons.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is where we find out that everything was relying on this being in place to give nested objects correct values.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 0bdbce13d3..0039247eac 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -104,7 +104,19 @@ public Bindable<int> StackHeightBindable
         public int StackHeight
         {
             get => stackHeight.Value;
-            set => stackHeight.Value = value;
+            set
+            {
+                stackHeight.Value = value;
+
+                if (!stackHeight.BindableCreated)
+                {
+                    foreach (var nested in NestedHitObjects)
+                    {
+                        if (nested is OsuHitObject osuHitObject)
+                            osuHitObject.StackHeight = value;
+                    }
+                }
+            }
         }
 
         public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);

fixes the direct failures I guess. If a bit ugly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I'm not sure how far we want to go with this.

@peppy peppy added the blocked/don't merge Don't merge this. label Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked/don't merge Don't merge this. size/M type/performance Deals with performance regressions or fixes without changing functionality.

Projects

Status: Pending Review

Development

Successfully merging this pull request may close these issues.

2 participants