Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion osu.Android.props
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.504.2" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.504.3" />
<!-- `ppy.osu.Framework.NativeLibs` is a transitive dependency of `ppy.osu.Framework`
that ships desktop-only natives (Linux/macOS/Windows) under `runtimes/<rid>/native/`
— including a bare Linux `libbass.so`/`libbass_fx.so`/`libbassmix.so` for linux-arm64.
Expand Down
34 changes: 34 additions & 0 deletions osu.Android/OsuGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ namespace osu.Android
"application/x-osu-replay",
})]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
// Samsung Game Launcher / Game Booster discovery for sideloaded APKs.
// Apps installed via Play Store/Galaxy Store are auto-discovered as games via the
// PACKAGE_ADDED broadcast + server-side category database. Sideloaded APKs bypass
// this path entirely. Adding the Samsung game category to an intent-filter on the
// main activity is the supported way to signal to Samsung's Game Launcher package
// scanner that this activity is a game entry point — it scans for activities with
// this category during app install and on periodic rescans.
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { "com.samsung.intent.category.GAME" })]
public class OsuGameActivity : AndroidGameActivity, ISurfaceHolderCallback
{
private static readonly string[] osu_url_schemes = { "osu", "osump" };
Expand Down Expand Up @@ -458,6 +466,32 @@ protected override void OnCreate(Bundle? savedInstanceState)
catch (Exception e) { Debug.WriteLine($"[osu!] Failed to load ruleset assembly {asm}: {e.Message}"); }
}

// Samsung Game Launcher self-registration for sideloaded APKs.
//
// Play Store / Galaxy Store installs are auto-discovered by Samsung Game Launcher
// through the PACKAGE_ADDED broadcast it receives at install time, plus its server-side
// game database. Sideloaded APKs bypass both paths entirely — Game Launcher may never
// add the app unless the user manually taps "+" in the Game Launcher UI.
//
// Sending a targeted broadcast to com.samsung.android.game.gameLauncher on every
// launch requests an immediate rescan of our package. Since Android 8.0 implicit
// broadcasts are blocked, we target the package explicitly via setPackage() — the
// broadcast is silently dropped on non-Samsung devices where Game Launcher is absent.
//
// This is a best-effort signal; Game Launcher may still require one manual "Add"
// on very old One UI builds that pre-date the REQUEST_ADD_PACKAGE handler.
try
{
var gameLauncherIntent = new Intent("com.samsung.android.game.gameLauncher.REQUEST_ADD_PACKAGE");
gameLauncherIntent.SetPackage("com.samsung.android.game.gameLauncher");
gameLauncherIntent.PutExtra("packageName", PackageName);
SendBroadcast(gameLauncherIntent);
}
catch
{
// Samsung Game Launcher not present (non-Samsung device) or broadcast failed — not an error.
}

CrashDiagnostics.WriteAliveMarker("Activity.OnCreate exit");
}

Expand Down
28 changes: 28 additions & 0 deletions osu.Android/OsuGameAndroid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,16 @@
/// so a user who later prefers a different mode from Settings → Graphics → Renderer
/// is not fought on every launch.
/// </para>
///
/// <para>
/// Field observations: 27fps at 9ms CPU frame time and 30fps at 1.7ms CPU frame time are
/// both present-queue stall patterns — the CPU is finishing frames quickly but the Vulkan
/// FIFO present queue cannot drain fast enough. IMMEDIATE mode resolves this by bypassing
/// the vblank synchronisation barrier entirely, so every finished frame goes straight to
/// the display engine. VSync or Limit1x (FIFO) amplify the stall during texture-upload
/// storms (100-300 queued uploads logged during song select), making them the wrong choice
/// for this rendering workload.
/// </para>
/// </summary>
private void applyAndroidFrameSyncMigrationOnce(FrameworkConfigManager frameworkConfig)
{
Expand Down Expand Up @@ -2221,6 +2231,23 @@
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV2MigrationApplied, true);
}

// v3 migration: restore users who were incorrectly moved to Limit1x (FIFO) by a
// previous build back to ActualUnlimited (IMMEDIATE present mode). Limit1x
// amplifies vkQueuePresentKHR stalls during texture-upload storms (100-300 items
// queued during song-select), producing the 27fps-at-9ms-frame-time pattern
// observed in field logs. We only apply if the user is currently on Limit1x
// (if they manually changed to any other mode, leave them alone).
if (!LocalConfig.Get<bool>(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied))
{
var frameSync = frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync);
if (frameSync.Value == FrameSync.Limit1x)

Check failure on line 2243 in osu.Android/OsuGameAndroid.cs

View workflow job for this annotation

GitHub Actions / Build only (Android)

'FrameSync' does not contain a definition for 'Limit1x'

Check failure on line 2243 in osu.Android/OsuGameAndroid.cs

View workflow job for this annotation

GitHub Actions / Build only (Android)

'FrameSync' does not contain a definition for 'Limit1x'

Check failure on line 2243 in osu.Android/OsuGameAndroid.cs

View workflow job for this annotation

GitHub Actions / Build only (Android)

'FrameSync' does not contain a definition for 'Limit1x'

Check failure on line 2243 in osu.Android/OsuGameAndroid.cs

View workflow job for this annotation

GitHub Actions / Build only (Android)

'FrameSync' does not contain a definition for 'Limit1x'
{
frameSync.Value = FrameSync.ActualUnlimited;
Logger.Log("[osu!] Android FrameSync v3 migration: Limit1x → ActualUnlimited (IMMEDIATE present, fixes vkQueuePresentKHR stall during texture-upload storms)", LoggingTarget.Performance);
}
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied, true);
}

return;
}

Expand All @@ -2237,6 +2264,7 @@

LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncMigrationApplied, true);
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV2MigrationApplied, true);
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied, true);
}
catch (Exception e)
{
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Configuration/OsuConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ protected override void InitialiseDefaults()
SetDefault(OsuSetting.AndroidVulkanProbe, false);
SetDefault(OsuSetting.AndroidStartupFrameSyncMigrationApplied, false);
SetDefault(OsuSetting.AndroidStartupFrameSyncV2MigrationApplied, false);
SetDefault(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied, false);

// --- Android startup-safety toggles ---
//
Expand Down Expand Up @@ -582,6 +583,7 @@ public enum OsuSetting
AndroidVulkanProbe,
AndroidStartupFrameSyncMigrationApplied,
AndroidStartupFrameSyncV2MigrationApplied,
AndroidStartupFrameSyncV3MigrationApplied,
AndroidCleanupStaleRealmFifos,
AndroidDeferStartupNativeInit,
AndroidStartupFrameSyncMigrationEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,26 @@ private void load(OsuConfigManager config, OsuGame? game)
//
// After a resync the restore button below becomes active so users can undo
// if the hardware measurement doesn't match their perception.
//
// IMPORTANT — Bluetooth speakers/headphones:
// The AAudio measurement captures the device's internal audio pipeline
// latency (DAC + driver buffer). It does NOT include Bluetooth A2DP
// transmission time, which adds a further ~100–300 ms of device-to-device
// wireless delay that AAudio cannot observe. For BT output, resync will
// give a partially-correct value; you must further adjust the audio offset
// manually (positive = audio arrives later than visuals; negative = earlier)
// until hit sounds and music land where they feel right in your ears.
//
// Note: AudioOffset shifts the ENTIRE gameplay clock — audio track, hit
// object visual timing, and hit sound effects all move together. This keeps
// everything internally consistent regardless of the offset value you choose.
Comment on lines +72 to +74
new SettingsButtonV2
{
Text = "Resync hardware audio offset",
TooltipText = "Measures the device's reported hardware output latency over a 2 s window and applies the median to the audio offset above. Previous offset is saved and can be restored.",
TooltipText = "Measures the device's AAudio pipeline latency over 2 s and applies the median to the audio offset. "
+ "NOTE: does NOT include Bluetooth transmission delay (~100–300 ms extra). "
+ "For Bluetooth speakers/headphones, resync first, then fine-tune the offset manually until music and hit sounds feel right. "
+ "The offset shifts the entire game clock — audio, hit objects, and effects all move together.",
Action = () => game?.ResyncHardwareAudioOffset(),
Keywords = new[] { @"resync", @"recalibrate", @"offset", @"hardware", @"latency", @"calibration" },
},
Expand Down
4 changes: 2 additions & 2 deletions osu.Game/osu.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="20.1.0" />
<PackageReference Include="ppy.osu.Framework" Version="2026.504.2" />
<PackageReference Include="ppy.osu.Framework" Version="2026.504.3" />
<!--
Explicitly pin `ppy.Veldrid.SPIRV` to the winnerspiros fork build that
`ppy.osu.Framework 2026.504.2` was compiled against. This version is the only
`ppy.osu.Framework 2026.504.3` was compiled against. This version is the only
Comment on lines +41 to +44
one whose `runtimes/android-arm64/native/libveldrid-spirv.so` is aligned to 16 KB
pages (required by Android 16+). It lives only as a release asset on
<https://github.com/winnerspiros/veldrid-spirv/releases/tag/1.0> and is vendored
Expand Down
2 changes: 1 addition & 1 deletion osu.iOS.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.iOS" Version="2026.504.2" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2026.504.3" />
</ItemGroup>
</Project>
Loading