diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39596d15ebbd..d36849248797 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: # macOS runner performance has gotten unbearably slow so let's turn them off temporarily. # - { prettyname: macOS, fullname: macos-latest } - { prettyname: Linux, fullname: ubuntu-latest } - threadingMode: ['SingleThread', 'MultiThreaded'] + threadingMode: ['MultiThreaded'] timeout-minutes: 120 steps: - name: Checkout diff --git a/README.md b/README.md index 4f7396181fa5..c1383a14ad1b 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,6 @@ Settings → Graphics → Renderer now exposes the full set of fork-added option | **Renderer** | Picks the GPU backend. On Windows you get Metal / Vulkan / D3D11 / **D3D12 (new)** / OpenGL plus their `Deferred_*` experimental variants. On Android you get Vulkan (if supported) and OpenGL ES. | | **Frame limiter** | VSync, **VSync Unbuffered (new)** — ideal for G-Sync / FreeSync / VRR displays, 2×/4×/8× refresh, Unlimited, or **Custom (new)**. | | **Custom draw rate limit** | Slider 0–1000 Hz, only visible when the frame limiter is set to Custom. `0` = unlimited draw thread. Useful for benchmarking or VRR-specific tuning. | -| **Threading mode** | Single / MultiThreaded / MultiThreadedDrawing. | | **Low latency** | `Off` / `On` / `Boost` — drives the fork's generic `ILowLatencyProvider` (NVIDIA Reflex / LatencyFlex-ready on D3D11 & D3D12; no-op on other backends until a provider plugin is supplied). `Boost` also sleeps at the start of each update frame for lower input-to-photon latency. | --- @@ -184,6 +183,7 @@ Settings → Graphics → Renderer now exposes the full set of fork-added option This fork includes several hardening fixes on top of upstream: +- **Multi-threaded execution lock-in (v145+)** — the framework's `ExecutionMode = SingleThread` is force-set to `MultiThreaded` on every startup and the threading-mode toggle is removed from Settings → Graphics → Renderer. SingleThread on Android collapsed the SDL/Vulkan thread onto the same thread that delivers the `SurfaceHolder.Callback`, so `VeldridDevice`'s 5-second `SurfaceHandle` poll deadlocked and Vulkan device creation crashed on a null function pointer (`SDLThread` `SI_TKILL` ~5 s into launch). The same risk applies to iOS Metal drawable attach; on desktop SingleThread is strictly slower with no UX benefit, so the lock-in is unconditional across all platforms. - **Sentry-safe init** — the app gracefully handles a missing/placeholder Sentry DSN instead of failing on startup - **Graceful native library loading** — if the Oboe or Vulkan native libraries are missing, the app continues without them - **JNI surface safety** — proper lifecycle management with atomic swaps and timeouts to prevent race conditions between Android surface creation and destruction diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index f3568732201d..a0026fb3d0c1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -8,9 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Configuration; using osu.Framework.Graphics.Textures; -using osu.Framework.Platform; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -104,13 +102,6 @@ public void TestNullRulesetHandled() AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); } - [Test] - public void TestSwitchThreadExecutionMode() - { - AddStep("Change thread mode to multi threaded", () => { Game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); }); - AddStep("Change thread mode to single thread", () => { Game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); }); - } - [Test] public void TestUnavailableRulesetHandled() { diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 312063c72083..f5ea9e30dc87 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -29,11 +29,6 @@ public static class GraphicsSettingsStrings /// public static LocalisableString FrameLimiter => new TranslatableString(getKey(@"frame_limiter"), @"Frame limiter"); - /// - /// "Threading mode" - /// - public static LocalisableString ThreadingMode => new TranslatableString(getKey(@"threading_mode"), @"Threading mode"); - /// /// "Show FPS" /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 21b46947dd96..225fd7b5e740 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -329,6 +329,21 @@ private void load(ReadableKeyCombinationProvider keyCombinationProvider, Framewo MessageFormatter.WebsiteRootUrl = endpoints.WebsiteUrl; + // Force the framework execution mode to MultiThreaded on every startup. + // + // SingleThread used to be exposed in the graphics settings, but it is incompatible + // with our Android Vulkan-surface readiness path: VeldridDevice / AndroidGameHost + // poll AndroidGameWindow.SurfaceHandle for up to 5s, and in SingleThread mode that + // poll runs on the same thread that publishes the surface handle, deadlocking + // initialisation until the timeout fires and Vulkan device creation crashes with a + // null function pointer (see PR history around the SDLThread/SI_TKILL crash). + // + // The same risk applies to iOS Metal layer attach (drawable surfaces are also + // delivered via the main loop), and on desktop SingleThread is strictly slower with + // no UX benefit. Upstream ppy/osu defaults to MultiThreaded; we now enforce it + // unconditionally so a stale `framework.ini` cannot pin a user into the broken mode. + frameworkConfig.SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); + // Initialise localisation frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); frameworkLocale.BindValueChanged(_ => updateLanguage()); diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 491a1d7b08a8..43c676719fba 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -83,11 +83,6 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi Keywords = new[] { @"fps", @"framerate" }, }, customDrawLimitItem, - new SettingsItemV2(new FormEnumDropdown - { - Caption = GraphicsSettingsStrings.ThreadingMode, - Current = config.GetBindable(FrameworkSetting.ExecutionMode) - }), new SettingsItemV2(new FormCheckBox { Caption = GraphicsSettingsStrings.ShowFPS,