@@ -379,7 +379,7 @@ protected override void LoadComplete()
379379 //
380380 // Pinning Update + Draw + Input to a 5-core subset (mask 0xF8 on SD8G2) is the
381381 // ONLY unconditional Android-specific synchronous mutation we still perform
382- // during the cold-start window — every other customisation (SustainedPerformanceMode,
382+ // during the cold-start window — every other customisation (
383383 // RequestUnbufferedDispatch, refresh-rate selection, Oboe / Vulkan-probe init,
384384 // performance-mode GC-latency flip) is already deferred behind the
385385 // refreshRateDelayMs scheduler below. Field logs.zip on v2026.423.176 show both
@@ -569,21 +569,28 @@ protected override void LoadComplete()
569569 Debug . WriteLine ( $ "[osu!] TameBackgroundThreads (initial) failed: { e . Message } ") ;
570570 }
571571
572- // Sustained performance mode is applied LATER, together with the deferred
573- // display-mode / GC-latency work below. See the Scheduler.AddDelayed block
574- // further down (after base.LoadComplete()) that schedules the first apply
575- // on a refreshRateDelayMs timer. Running
576- // Window.SetSustainedPerformanceMode(true) synchronously here — during the
577- // Toolbar cold-start texture-upload burst and the Vulkan swapchain bring-up —
578- // has been observed to race the Draw thread on Samsung One UI / Adreno panels:
579- // the window-flag mutation round-trips through ViewRootImpl.setPrivateFlags
580- // and can partially reconfigure the Surface while vkAcquireNextImageKHR is in
581- // flight, stalling the present queue. Update keeps ticking (so neither the
582- // managed nor the native watchdog ever dumps), the screen never updates, and
583- // ~10 s later Android raises a MotionEvent input-dispatch ANR — the exact
584- // cold-start "black screen → no touch → ANR" fingerprint reported across
585- // multiple v174 launches in logs.zip. Deferring to the same window used by
586- // SelectHighestRefreshRate moves the mutation behind the texture-upload burst.
572+ // Window.SetSustainedPerformanceMode is intentionally NOT called anywhere.
573+ //
574+ // On Samsung One UI / Adreno devices, calling SetSustainedPerformanceMode(true)
575+ // triggers a non-seamless display-mode transition (even when deferred behind the
576+ // texture-upload burst). The transition momentarily destroys the SurfaceView,
577+ // which resets the surface pixel format back to the Android default (RGB565 on
578+ // high-density Samsung panels). Our SurfaceChanged reactive guard then calls
579+ // SurfaceHolder.SetFormat(RGBA8888), causing a second surface-destroy/recreate
580+ // cycle. During this second cycle the ANativeWindow transiently reports the
581+ // display's scaled (dp) dimensions — 1029×480 on a 3088×1440 3×-density panel —
582+ // instead of the physical pixel dimensions. Veldrid reads those dimensions from
583+ // vkGetPhysicalDeviceSurfaceCapabilitiesKHR during its VkSurfaceKHR-loss
584+ // recovery, creates a permanent swapchain at 1029×480, and SurfaceFlinger tiles
585+ // that sub-screen image 3×3 to fill the display. The result is the "9 screens"
586+ // artifact, blurry/flashing textures, and a sustained FPS drop observed on
587+ // Galaxy S24 Ultra (Adreno 740, One UI 7, Android 15) with Vulkan enabled.
588+ //
589+ // Removing the call eliminates the mid-session surface teardown. ADPF performance
590+ // hinting is already provided by Oboe's setPerformanceHintEnabled(true) (set
591+ // during stream open in oboe_bridge.cpp), and GC low-latency is handled by
592+ // AndroidHighPerformanceSessionManager (SustainedLowLatency GCSettings) which
593+ // covers the same thermal/responsiveness goals without touching the Surface.
587594
588595 base . LoadComplete ( ) ;
589596
@@ -688,27 +695,6 @@ protected override void LoadComplete()
688695 Debug . WriteLine ( $ "[osu!] Deferred SelectHighestRefreshRate failed: { ex . Message } ") ;
689696 }
690697
691- // Deferred sustained-performance-mode apply. See the comment block
692- // before base.LoadComplete() above for the rationale (Samsung One UI /
693- // Adreno Surface reconfigure race with vkAcquireNextImageKHR during the
694- // cold-start texture-upload burst). By the time this fires the
695- // swapchain has long since stabilised.
696- try
697- {
698- gameActivity . RunOnUiThread ( ( ) =>
699- {
700- try { gameActivity . Window ? . SetSustainedPerformanceMode ( true ) ; }
701- catch ( Exception e )
702- {
703- Debug . WriteLine ( $ "[osu!] Failed to enable sustained performance mode: { e . Message } ") ;
704- }
705- } ) ;
706- }
707- catch ( Exception e )
708- {
709- Debug . WriteLine ( $ "[osu!] Failed to dispatch sustained performance mode toggle to UI thread: { e . Message } ") ;
710- }
711-
712698 // Deferred initial application of the user's performance-mode setting.
713699 // The BindValueChanged registration below is WITHOUT the immediate-fire
714700 // flag, so the very first apply (which may flip GCSettings.LatencyMode
@@ -1033,7 +1019,7 @@ protected override void LoadComplete()
10331019 // the BDL load thread, in the silent cold-start window — exactly
10341020 // when we are debugging a startup hang. Deferring the initial
10351021 // fire via Scheduler.AddDelayed onto the same refreshRateDelayMs
1036- // timer that gates SustainedPerformanceMode / the initial refresh-
1022+ // timer that gates the initial refresh-
10371023 // rate apply / the initial performance-mode apply keeps the cold-
10381024 // start path free of synchronous native init even when a saved-
10391025 // true setting would otherwise force it, AND ensures the native
@@ -1149,8 +1135,9 @@ private void applyPerformanceOptimizations(bool enabled)
11491135 {
11501136 try
11511137 {
1152- // Sustained performance mode is always on (set in LoadComplete).
11531138 // The performance toggle controls the high-perf GC session only.
1139+ // (Window.SetSustainedPerformanceMode is intentionally not called —
1140+ // see the comment before base.LoadComplete() for the full rationale.)
11541141 if ( enabled )
11551142 {
11561143 highPerformanceSession ??= highPerformanceSessionManager . BeginSession ( ) ;
0 commit comments