Skip to content

Commit 3cb83d0

Browse files
authored
Merge pull request #351 from winnerspiros/copilot/fix-black-screen-issue-81afa2e2-8602-4671-849b-80662d737660
Android startup black-screen: force safe-mode fallback on persistent Vulkan RGB565 surface + always feed native watchdog heartbeat
2 parents 8e4f508 + 79b7219 commit 3cb83d0

3 files changed

Lines changed: 56 additions & 8 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ jobs:
227227
- name: Setup JDK 11
228228
uses: actions/setup-java@v5
229229
with:
230-
distribution: microsoft
230+
distribution: temurin
231231
java-version: 11
232232

233233
- name: Install .NET 10.0.x
@@ -294,9 +294,13 @@ jobs:
294294
[ -d "$sdk_dir" ] || continue
295295
for sdk in $(ls "$sdk_dir" 2>/dev/null | grep '^MacOSX[0-9].*\.sdk$' | sort -rV); do
296296
cand="$sdk_dir/$sdk"
297-
hdr_count=$(ls "$cand/usr/include" 2>/dev/null | wc -l | tr -d ' ')
297+
cand_real=$(python3 -c "import os, sys; print(os.path.realpath(sys.argv[1]))" "$cand" 2>/dev/null || echo "$cand")
298+
# Skip candidates that resolve back to the unversioned MacOSX.sdk alias.
299+
# Re-linking MacOSX.sdk to itself creates a broken self-referential symlink.
300+
[ "$(basename "$cand_real")" = "MacOSX.sdk" ] && continue
301+
hdr_count=$(ls "$cand_real/usr/include" 2>/dev/null | wc -l | tr -d ' ')
298302
if [ "${hdr_count:-0}" -gt 5 ]; then
299-
REAL_SDK="$cand"
303+
REAL_SDK="$cand_real"
300304
REAL_XCODE="$xapp"
301305
break 2
302306
fi
@@ -306,9 +310,11 @@ jobs:
306310
if [ -z "$REAL_SDK" ]; then
307311
for sdk in $(ls /Library/Developer/CommandLineTools/SDKs 2>/dev/null | grep '^MacOSX[0-9].*\.sdk$' | sort -rV); do
308312
cand="/Library/Developer/CommandLineTools/SDKs/$sdk"
309-
hdr_count=$(ls "$cand/usr/include" 2>/dev/null | wc -l | tr -d ' ')
313+
cand_real=$(python3 -c "import os, sys; print(os.path.realpath(sys.argv[1]))" "$cand" 2>/dev/null || echo "$cand")
314+
[ "$(basename "$cand_real")" = "MacOSX.sdk" ] && continue
315+
hdr_count=$(ls "$cand_real/usr/include" 2>/dev/null | wc -l | tr -d ' ')
310316
if [ "${hdr_count:-0}" -gt 5 ]; then
311-
REAL_SDK="$cand"
317+
REAL_SDK="$cand_real"
312318
break
313319
fi
314320
done
@@ -327,9 +333,13 @@ jobs:
327333
MACOS_SDK="$ACTIVE_SDKS_DIR/MacOSX.sdk"
328334
# Resolve REAL_SDK to its canonical (symlink-free) path to prevent ELOOP errors.
329335
REAL_SDK_CANON=$(python3 -c "import os, sys; print(os.path.realpath(sys.argv[1]))" "$REAL_SDK" 2>/dev/null || echo "$REAL_SDK")
330-
sudo rm -rf "$MACOS_SDK"
331-
sudo ln -sfn "$REAL_SDK_CANON" "$MACOS_SDK"
332-
echo "Created MacOSX.sdk symlink -> $REAL_SDK_CANON"
336+
if [ "$REAL_SDK_CANON" = "$MACOS_SDK" ]; then
337+
echo "WARNING: resolved SDK points to active MacOSX.sdk alias; skipping relink to avoid self-referential symlink"
338+
else
339+
sudo rm -rf "$MACOS_SDK"
340+
sudo ln -sfn "$REAL_SDK_CANON" "$MACOS_SDK"
341+
echo "Created MacOSX.sdk symlink -> $REAL_SDK_CANON"
342+
fi
333343
else
334344
echo "WARNING: no valid macOS SDK found; build may fail"
335345
fi

osu.Android/OsuGameActivity.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ await Task.WhenAll(uris.Select(async uri =>
763763
// check-then-set in SurfaceChanged (lines ~732-734) is not a concurrency concern
764764
// because no two SurfaceChanged calls can overlap on the single UI thread.
765765
private volatile bool setFormatPending;
766+
private volatile int setFormatAttempts;
766767

767768
public IntPtr GetSurfaceGlobalRef()
768769
{
@@ -889,6 +890,7 @@ public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Forma
889890
&& !AndroidStartupSafeMode.IsActive)
890891
{
891892
setFormatPending = true;
893+
setFormatAttempts = 1;
892894

893895
// Log to Runtime so the mid-session RGB565 reset is visible in the main log
894896
// (and therefore in the notification overlay). Performance log gets the same
@@ -924,13 +926,37 @@ public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Forma
924926
return;
925927
}
926928

929+
if (format == global::Android.Graphics.Format.Rgb565
930+
&& LogManagement.IsVulkanConfigured()
931+
&& setFormatPending
932+
&& !AndroidStartupSafeMode.IsActive)
933+
{
934+
setFormatAttempts++;
935+
936+
if (setFormatAttempts >= 2)
937+
{
938+
Logger.Log(
939+
"[osu!] Surface remained RGB565 after RGBA8888 request; restarting to enter safe-mode OpenGL fallback.",
940+
LoggingTarget.Runtime,
941+
LogLevel.Important);
942+
Logger.Log(
943+
"[osu!] Vulkan surface-format recovery failed (RGB565 persisted); killing process for safe-mode restart.",
944+
LoggingTarget.Performance,
945+
LogLevel.Important);
946+
947+
try { global::Android.OS.Process.KillProcess(global::Android.OS.Process.MyPid()); }
948+
catch (Exception e) { Debug.WriteLine($"[osu!] Failed to kill process after persistent RGB565 detection: {e.Message}"); }
949+
}
950+
}
951+
927952
// Release the pending-format guard once the surface is confirmed RGBA8888.
928953
// This allows future RGB565 detection (e.g. after a display-mode change that
929954
// would legitimately reset the format) while still blocking a second spurious
930955
// fire during the immediate teardown+recreate that follows our own SetFormat call.
931956
if (format == global::Android.Graphics.Format.Rgba8888 && setFormatPending)
932957
{
933958
setFormatPending = false;
959+
setFormatAttempts = 0;
934960
Debug.WriteLine("[osu!] Surface format confirmed RGBA8888 — pending-format guard released.");
935961
}
936962

osu.Android/OsuGameAndroid.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,18 @@ public override void SetHost(GameHost host)
22472247
// still make the enqueue side spin on its lock.
22482248
CrashDiagnostics.WriteAliveMarker("OsuGameAndroid.SetHost (about to start HangWatchdog)");
22492249

2250+
// Keep the native pthread watchdog fed even when verbose diagnostics are off.
2251+
// The native watchdog is always armed from Activity.OnCreate, but HangWatchdog
2252+
// (which normally forwards heartbeats) is verbose-gated below.
2253+
try
2254+
{
2255+
host.UpdateThread.Scheduler.AddDelayed(static () => NativeWatchdog.Heartbeat(), 1_000, true);
2256+
}
2257+
catch (Exception e)
2258+
{
2259+
Debug.WriteLine($"[osu!] Failed to schedule native watchdog heartbeat: {e.Message}");
2260+
}
2261+
22502262
// Start the hang watchdog only when verbose diagnostics are enabled.
22512263
// It writes /proc/self/task snapshots to native_crash.log on stalls — valuable
22522264
// during debugging but adds a dedicated background thread and periodic file I/O

0 commit comments

Comments
 (0)