Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions osu.Android/OsuGameAndroid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,16 @@ private void updateOrientation()
/// 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,13 @@ private void applyAndroidFrameSyncMigrationOnce(FrameworkConfigManager framework
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV2MigrationApplied, true);
}

// v3 migration: FrameSync.Limit1x was removed from the framework enum in
// osu-framework 2026.504.3 — any stored Limit1x config value is now treated
// as unknown and will fall back to the framework default on next load.
// Nothing to do here; just stamp the flag so the guard is not re-evaluated.
if (!LocalConfig.Get<bool>(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied))
LocalConfig.SetValue(OsuSetting.AndroidStartupFrameSyncV3MigrationApplied, true);

return;
}

Expand All @@ -2237,6 +2254,7 @@ private void applyAndroidFrameSyncMigrationOnce(FrameworkConfigManager framework

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