Skip to content

perf: update framework submodule + optimize gameplay hot paths - #216

Merged
winnerspiros merged 9 commits into
masterfrom
copilot/fix-osu-android-crash-again
Apr 20, 2026
Merged

perf: update framework submodule + optimize gameplay hot paths#216
winnerspiros merged 9 commits into
masterfrom
copilot/fix-osu-android-crash-again

Conversation

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown
  • Diagnose CI failures: previous NBGV shallow-clone issue already fixed (fetch-depth: 0)
  • Identify InspectCode warnings: apiState?., multiplayerState?., spectatorState?. use null-conditional on non-nullable fields
  • Fix: change ?.. for all three UnbindAll() calls in Dispose
  • Validate changes

Summary by Gitar

  • Code cleanup:
    • Removed unnecessary null-conditional operators in OnlineStatusNotifier.Dispose to resolve InspectCode warnings.

This will update automatically on new commits.

Copilot AI and others added 3 commits April 20, 2026 11:40
…th optimizations and bug fixes

Updates include:
- Fix NullReferenceException in GraphicsPipeline.cs (ResourceLayouts?.Length)
- Fix null safety in VeldridExtensions.Clone() (ResourceLayouts null check)
- Fix null-forgiving operators in shader compilation (ShaderCompilationStore/VeldridShader)
- Performance optimizations: HashSet for hover tracking, LazyList struct enumerator
- Performance optimizations round 2: hot-path LINQ removal, array stats, single-pass trim
- Fix 30 CS1591 warnings from veldrid submodule + IL trimmer fixes
- Fix NuGet pack warnings for iOS project (NU5100, NU5118)
- Fix workflow failures: update action versions, fix shallow clone
- Remove redundant nullable operators per InspectCode warnings

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…aySampleTriggerSource, HitEventExtensions

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/adbddea2-3da1-47fd-b556-24d1a5d61ad5

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Copilot AI and others added 2 commits April 20, 2026 12:44
…ObjectContainer; replace Single/SingleOrDefault with First/FirstOrDefault; fix OnlineStatusNotifier bindable leak

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/b0878070-74cc-41e1-af2d-af6fb8954594

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…olourHitErrorMeter hot paths

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/b0878070-74cc-41e1-af2d-af6fb8954594

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…kage caching for all jobs

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/ef1cac07-0e5c-4e78-80ca-6affd0c0fd6b

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
… version computation

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/1eaec755-4862-4a45-a4e9-3ab598e3840d

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Comment thread osu.Game/Online/OnlineStatusNotifier.cs Outdated
{
base.Dispose(isDisposing);

apiState?.UnbindAll();
Comment thread osu.Game/Online/OnlineStatusNotifier.cs Outdated
base.Dispose(isDisposing);

apiState?.UnbindAll();
multiplayerState?.UnbindAll();
Comment thread osu.Game/Online/OnlineStatusNotifier.cs Outdated

apiState?.UnbindAll();
multiplayerState?.UnbindAll();
spectatorState?.UnbindAll();
…ifier.Dispose (InspectCode warning)

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/78598192-f2c2-49e8-9b77-ba2c9587efa7

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
@winnerspiros
winnerspiros marked this pull request as ready for review April 20, 2026 15:56
Copilot AI review requested due to automatic review settings April 20, 2026 15:56
@winnerspiros
winnerspiros merged commit 4d60d00 into master Apr 20, 2026
@gitar-bot

gitar-bot Bot commented Apr 20, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR focuses on reducing allocations and avoiding repeated LINQ enumeration in gameplay/editor hot paths, alongside a small CI improvement and a cleanup to address an InspectCode warning.

Changes:

  • Optimize several per-frame/per-judgement paths by replacing LINQ chains with single-pass loops or cheaper enumerations.
  • Improve HitObjectContainer ordering accessors to avoid sorting on every Objects/AliveObjects access.
  • Add NuGet package caching to CI jobs and adjust OnlineStatusNotifier.Dispose() unbinding.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs Avoids repeated LINQ re-enumeration when trimming excess judgements.
osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondController.cs Reworks rewind trimming/counting to reduce per-element removals.
osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs Uses FirstOrDefault() instead of SingleOrDefault() to reduce enumeration cost.
osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs Uses First() instead of Single() to reduce enumeration cost.
osu.Game/Rulesets/UI/HitObjectContainer.cs Avoids OrderBy() on every access; uses reverse enumeration + in-place sort for alive objects.
osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs Removes LINQ-heavy selection logic in favour of single-pass scans.
osu.Game/Rulesets/UI/DrawableRuleset.cs Uses FirstOrDefault() instead of SingleOrDefault() to reduce enumeration cost.
osu.Game/Rulesets/Scoring/HitEventExtensions.cs Computes average hit error via running sum to avoid intermediate array allocation.
osu.Game/Online/OnlineStatusNotifier.cs Adjusts bindable unbinding in Dispose() (per InspectCode warning).
.github/workflows/ci.yml Adds NuGet package caching to speed up restores across CI jobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +51
// Timestamps are added in chronological order (from clock.CurrentTime),
// so we can use binary-search-style trimming instead of per-element RemoveAt.

// Trim future timestamps caused by rewinding (remove from the end in one batch).
// RemoveRange from the end is a single operation vs repeated RemoveAt calls.
int trimStart = timestamps.Count;

while (trimStart > 0 && timestamps[trimStart - 1] > latestValidTime)
trimStart--;

if (trimStart < timestamps.Count)
timestamps.RemoveRange(trimStart, timestamps.Count - trimStart);

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

The new trimming/counting logic assumes timestamps is always in non-decreasing order and that any "future" timestamps (from rewinds/seeks) are contiguous at the end. If an input timestamp is added after a seek/rewind but before the next Update() (or if replay input is re-applied after rewinding), the list can become out-of-order and this RemoveRange-from-end pass will leave future timestamps earlier in the list, inflating the CPS count. Consider either maintaining sorted order on insertion (e.g. insert at the correct position) or falling back to removing future timestamps during the backwards scan when an out-of-order condition is detected (or clearing the list on rewind/seek).

Copilot uses AI. Check for mistakes.
Comment on lines +164 to +166
apiState.UnbindAll();
multiplayerState.UnbindAll();
spectatorState.UnbindAll();

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

These bindables are initialised in load() via GetBoundCopy(), but Dispose() can run even if load() never executed (note the existing IsNotNull() guards for clients). Calling UnbindAll() unconditionally here can therefore throw if any of these fields are still null at disposal time. Consider initialising them at declaration (e.g. new Bindable<...>() + BindTo(...) in load()), or making them nullable and using null-conditional unbinds.

Suggested change
apiState.UnbindAll();
multiplayerState.UnbindAll();
spectatorState.UnbindAll();
apiState?.UnbindAll();
multiplayerState?.UnbindAll();
spectatorState?.UnbindAll();

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants