From 4ebfd1eea2a11b88d6772409eaf33ca5b7d21ec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 13:02:23 +0000 Subject: [PATCH 1/4] Initial plan From a13d616c8b76b680118443499705916953573236 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 13:14:15 +0000 Subject: [PATCH 2/4] osu.Android: force RGBA8888 SurfaceHolder format for Vulkan black-screen fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root cause of the Vulkan black-screen + Draw-thread native crash on Android is that SDL3 only calls setFormat(RGBA8888) for OpenGL, not Vulkan. Android's default SurfaceView pixel format on many high-density displays is RGB565. An RGB565 ANativeWindow causes Veldrid to negotiate a R5G6B5_UNORM Vulkan swapchain format which is incompatible with the 8-bit-per-channel rendering pipeline, producing a black screen and eventual native crash on Adreno GPUs. Two-layer fix in OsuGameActivity: 1. Proactive: Call holder.SetFormat(RGBA8888) in the DecorView.Post lambda before AddCallback, ensuring the Surface is created with the correct format in the normal case. 2. Reactive: In SurfaceChanged, detect RGB565 + Vulkan configured → log a loud warning (LogLevel.Important) and call SetFormat(RGBA8888) to trigger a surface recreate. Veldrid's VkSurfaceKHR-loss recovery picks up the new RGBA8888 ANativeWindow on the next pass. Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/0bf6f340-f206-4105-88e4-30e371bcfd12 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com> --- osu.Android/OsuGameActivity.cs | 65 +++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 3053776cfc84..d53e204e750b 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -249,7 +249,33 @@ protected override void OnCreate(Bundle? savedInstanceState) { try { - GetSurface()?.Holder?.AddCallback(this); + var holder = GetSurface()?.Holder; + + if (holder != null) + { + // Request RGBA8888 on the Android SurfaceHolder unconditionally BEFORE + // registering our callback. Without this, Android defaults to RGB565 + // for the SurfaceView when no renderer explicitly requests a different + // format — SDL3 only calls setFormat(RGBA8888) for OpenGL, not Vulkan, + // so Vulkan sessions receive an RGB565 ANativeWindow. An RGB565 swapchain + // is incompatible with our 8-bit-per-channel rendering pipeline and + // causes a black screen followed by a native Draw-thread crash on Adreno + // GPUs (evidenced by SDL_PIXELFORMAT_RGB565 + "drawable size 3088×1440" + // in the runtime log for every Vulkan crash session). RGBA8888 is what + // OpenGL already uses and is the correct baseline for all renderers. + // Calling SetFormat before AddCallback ensures the format is stamped + // on the SurfaceHolder before SDL creates the VkAndroidSurfaceKHR. + try + { + holder.SetFormat(Android.Graphics.Format.Rgba8888); + } + catch (Exception fe) + { + Debug.WriteLine($"[osu!] Failed to request RGBA8888 surface format: {fe.Message}"); + } + + holder.AddCallback(this); + } } catch (Exception e) { @@ -586,6 +612,43 @@ public void SurfaceCreated(ISurfaceHolder holder) public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height) { + // Guard: if the Android surface materialised with a 16-bit pixel format (RGB565) + // while Vulkan is configured, request a format change to RGBA8888 immediately. + // + // Root cause: SDL3 only calls setFormat(RGBA8888) for OpenGL, not Vulkan. + // Android's default SurfaceView pixel format on many displays (especially high- + // density landscape panels) is RGB565. An RGB565 ANativeWindow means the Vulkan + // WSI can only negotiate R5G6B5_UNORM as the swapchain format, which is + // incompatible with our 8-bit-per-channel pipeline and produces a black screen + // followed by a native Draw-thread crash on Adreno GPUs. + // + // The proactive SetFormat(RGBA8888) call in the DecorView.Post lambda above is + // the primary fix (runs before the Surface is typically created). This reactive + // guard is the belt-and-braces fallback for timing windows where the Surface is + // already created when the Post fires (e.g. rapid cold-starts, system-restored + // windows). Calling SetFormat here triggers SurfaceDestroyed + SurfaceCreated + + // SurfaceChanged with the corrected format; Veldrid's VkSurfaceKHR-loss recovery + // picks up the new ANativeWindow and negotiates a proper BGRA/RGBA 8-bit swapchain. + if (format == global::Android.Graphics.Format.Rgb565 && LogManagement.IsVulkanConfigured()) + { + Logger.Log( + "[osu!] Android surface pixel format RGB565 is incompatible with the Vulkan rendering pipeline " + + "— requesting RGBA8888 and triggering a surface recreate. " + + "This is the root cause of the Vulkan black-screen crash on Adreno (SDL_PIXELFORMAT_RGB565 in runtime log). " + + "The next SurfaceChanged will carry the corrected format.", + LoggingTarget.Performance, + LogLevel.Important); + + try + { + holder.SetFormat(global::Android.Graphics.Format.Rgba8888); + } + catch (Exception e) + { + Debug.WriteLine($"[osu!] Failed to request RGBA8888 format change for Vulkan: {e.Message}"); + } + } + if (width > 0 && height > 0) { surfaceEvent.Set(); From 1e351b68b094b0ec73ebf87170333b2e8ff3a26e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 13:15:59 +0000 Subject: [PATCH 3/4] Fix exception variable naming: rename 'fe' to 'e' for consistency Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/0bf6f340-f206-4105-88e4-30e371bcfd12 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com> --- osu.Android/OsuGameActivity.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index d53e204e750b..4b75db000927 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -269,9 +269,9 @@ protected override void OnCreate(Bundle? savedInstanceState) { holder.SetFormat(Android.Graphics.Format.Rgba8888); } - catch (Exception fe) + catch (Exception e) { - Debug.WriteLine($"[osu!] Failed to request RGBA8888 surface format: {fe.Message}"); + Debug.WriteLine($"[osu!] Failed to request RGBA8888 surface format: {e.Message}"); } holder.AddCallback(this); From 793f85603889c83452da4f35128220eb54450f49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 13:42:43 +0000 Subject: [PATCH 4/4] Fix CS0234: use global:: qualifier for Android.Graphics.Format in OsuGameActivity Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/a24b8feb-25ae-4d67-afd0-495430d3e5c3 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com> --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 4b75db000927..3cacc0d47b44 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -267,7 +267,7 @@ protected override void OnCreate(Bundle? savedInstanceState) // on the SurfaceHolder before SDL creates the VkAndroidSurfaceKHR. try { - holder.SetFormat(Android.Graphics.Format.Rgba8888); + holder.SetFormat(global::Android.Graphics.Format.Rgba8888); } catch (Exception e) {