Skip to content
Merged
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
20 changes: 9 additions & 11 deletions osu.Android/OsuGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -906,11 +906,6 @@ public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Forma
// to send a heartbeat (especially during startup when the runtime is under
// heavy load), and the native watchdog (10s default) would fire, killing the
// process and producing a black screen.
//
// Calling Heartbeat() here resets the watchdog timer from the UI thread,
// giving SetFormat time to complete without triggering a false positive.
// The native watchdog checks g_lastHeartbeatMonotonicSec which is updated
// by a simple atomic store — safe to call from any thread.
try
{
NativeWatchdog.Heartbeat();
Expand All @@ -921,6 +916,8 @@ public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Forma
Debug.WriteLine($"[osu!] NativeWatchdog.Heartbeat failed (non-fatal): {e.Message}");
}

Debug.WriteLine("[osu!] Native surface format change requested (RGB565→RGBA8888)");

try
{
holder.SetFormat(global::Android.Graphics.Format.Rgba8888);
Expand All @@ -930,12 +927,13 @@ public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Forma
Debug.WriteLine($"[osu!] Failed to request RGBA8888 format change for Vulkan: {e.Message}");
}

Debug.WriteLine("[osu!] Native surface format change requested (RGB565→RGBA8888)");

// Reset the surface event so GetSurfaceGlobalRef() does NOT unblock with the
// old (about-to-be-invalidated) surface handle. The event will be re-set when
// SurfaceChanged fires again for the new RGBA8888 surface.
surfaceEvent.Reset();
// After SetFormat returns, the synchronous SurfaceDestroyed→SurfaceCreated→SurfaceChanged
// cycle has already completed and surfaceGlobalRef points to the new RGBA8888 surface.
// Set the event now so GetSurfaceGlobalRef() returns the new valid surface.
// This preserves the invariant that surfaceEvent is only set when surfaceGlobalRef
// points to a safe-to-render surface, while the watchdog heartbeat above prevents
// a false positive during the blocking SetFormat call.
surfaceEvent.Set();

return;
}
Expand Down
Loading