From 0136fb619676c5c4e5b4481459bee6b420e68e2e Mon Sep 17 00:00:00 2001 From: The412Banner Date: Mon, 12 Oct 2026 22:11:55 -0400 Subject: [PATCH] gamescope: explicit sync off where the kernel or the stand-in node cannot signal its timelines Adds tools/gamescope/patches/0126 (GAMESCOPE_DISABLE_EXPLICIT_SYNC=1 for the nested backends) and sets it on a GPU that is not an Adreno when the kernel is older than 6.6 (no DRM_IOCTL_SYNCOBJ_EVENTFD) or the guest's render node is Mali's stand-in. The graphics report lists it with the rest of the guest's graphics environment. --- .../launcher/session/GraphicsReport.kt | 2 +- .../launcher/session/SessionService.kt | 21 ++++++++++++++ tools/gamescope/PATCHES.md | 6 ++++ .../patches/0126-explicit-sync-switch.patch | 29 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tools/gamescope/patches/0126-explicit-sync-switch.patch diff --git a/app/src/main/java/com/droiddeck/launcher/session/GraphicsReport.kt b/app/src/main/java/com/droiddeck/launcher/session/GraphicsReport.kt index a0042b72e..beebb78c6 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/GraphicsReport.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/GraphicsReport.kt @@ -35,7 +35,7 @@ object GraphicsReport { /** The guest keys the report repeats: everything that decides which Vulkan driver loads. */ private val ENV_PREFIXES = listOf( "VK_", "BL_VK", "BL_DRM", "BL_VKBRIDGE", "VKBRIDGE", "MESA_", "GALLIUM_", "ZINK", "TU_", "PANVK", - "DROIDDECK_POWERVR", "DROIDDECK_HYBRIS", "BL_GAME_LOGS", "LIBGL_", + "DROIDDECK_POWERVR", "DROIDDECK_HYBRIS", "BL_GAME_LOGS", "LIBGL_", "GAMESCOPE_DISABLE", "XWAYLAND", ) /** Per-game graphics logs and the guest's Vulkan summary: on for a non-Adreno GPU, or by Setup's switch. */ diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt index 95d4a17b4..9fbbf64a1 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt @@ -307,6 +307,13 @@ class SessionService : Service() { return lines } + /** Whether a kernel release ("6.1.157-android14-11") is older than [major].[minor]; false if unreadable. */ + internal fun kernelBefore(release: String, major: Int, minor: Int): Boolean { + val m = Regex("""^(\d+)\.(\d+)""").find(release) ?: return false + val (a, b) = m.destructured + return a.toInt() < major || (a.toInt() == major && b.toInt() < minor) + } + /** Whether the newest finished session's log shows Xwayland itself crashing. */ private fun lastXwaylandCrashed(): Boolean = runCatching { val last = SessionPaths.sessionFolders(this).filter { it != SessionPaths.current() }.lastOrNull() ?: return false @@ -663,6 +670,20 @@ class SessionService : Service() { guest.add("XWAYLAND_NO_GLAMOR=1") Log.w(TAG, "xwayland: the last session's Xwayland crashed; glamor off for this one (XWAYLAND_NO_GLAMOR=1)") } + // gamescope's explicit sync waits on DRM syncobj timelines through + // DRM_IOCTL_SYNCOBJ_EVENTFD, which a kernel before 6.6 does not have (Exynos 2400e on 6.1: + // hundreds of "timeline ... failed" and a game stuck at black), and Mali's stand-in node + // (/dev/mali0) has no syncobjs at all. There clients use implicit sync instead + // (tools/gamescope/patches/0126). Never on an Adreno. + if (!com.droiddeck.launcher.core.DeviceSupport.adreno()) { + val kernel = System.getProperty("os.version").orEmpty() + val old = kernelBefore(kernel, 6, 6) + val maliNode = LinuxRuntime.gpuStandIn(this) == LinuxRuntime.MALI_DEVICE + if (old || maliNode) { + guest.add("GAMESCOPE_DISABLE_EXPLICIT_SYNC=1") + Log.i(TAG, "gamescope: explicit sync off (" + (if (old) "kernel $kernel has no SYNCOBJ_EVENTFD" else "Mali stand-in node has no syncobjs") + ")") + } + } if (!bridge && com.droiddeck.launcher.core.DeviceSupport.openDriverActive(this)) { LinuxRuntime.gpuStandIn(this)?.takeIf { it == LinuxRuntime.MALI_DEVICE }?.let { guest.add("BL_DRM_STANDIN=$it") } } diff --git a/tools/gamescope/PATCHES.md b/tools/gamescope/PATCHES.md index 6c0225074..8f8dfb40e 100644 --- a/tools/gamescope/PATCHES.md +++ b/tools/gamescope/PATCHES.md @@ -62,3 +62,9 @@ The system PowerVR driver uses the existing SDL/Wayland swapchain backend. Its a Sixteen more of Armada's patches are DRM/lease/HDR-on-KMS work for a native display, which this app's Wayland-hosted gamescope never reaches, or need a newer gamescope than the runtime has. +- `0126-explicit-sync-switch.patch`: `GAMESCOPE_DISABLE_EXPLICIT_SYNC=1` makes the nested + (Wayland and SDL) backends report no explicit sync, so gamescope does not offer + wp_linux_drm_syncobj_manager_v1 and clients use implicit sync. The app sets it on a GPU that is + not an Adreno when the kernel is older than 6.6 (no DRM_IOCTL_SYNCOBJ_EVENTFD: an Exynos 2400e + on 6.1 logged hundreds of "timeline: DRM_IOCTL_SYNCOBJ_EVENTFD failed" and a game hung at a + black screen) or when the render node is Mali's stand-in (/dev/mali0 has no DRM syncobjs). diff --git a/tools/gamescope/patches/0126-explicit-sync-switch.patch b/tools/gamescope/patches/0126-explicit-sync-switch.patch new file mode 100644 index 000000000..92d97dead --- /dev/null +++ b/tools/gamescope/patches/0126-explicit-sync-switch.patch @@ -0,0 +1,29 @@ +--- a/src/Backends/WaylandBackend.cpp ++++ b/src/Backends/WaylandBackend.cpp +@@ -2309,7 +2309,10 @@ + + bool CWaylandBackend::SupportsExplicitSync() const + { +- return true; ++ // GAMESCOPE_DISABLE_EXPLICIT_SYNC=1: a kernel without DRM_IOCTL_SYNCOBJ_EVENTFD (before ++ // Linux 6.6) cannot signal the timelines, so clients use implicit sync instead. ++ const char *pszDisable = getenv( "GAMESCOPE_DISABLE_EXPLICIT_SYNC" ); ++ return !( pszDisable && *pszDisable == '1' ); + } + + bool CWaylandBackend::IsPaused() const +--- a/src/Backends/SDLBackend.cpp ++++ b/src/Backends/SDLBackend.cpp +@@ -506,8 +506,10 @@ + + bool CSDLBackend::SupportsExplicitSync() const + { +- // We use a Vulkan swapchain, so yes. +- return true; ++ // We use a Vulkan swapchain, so yes - unless the kernel cannot signal the timelines ++ // (no DRM_IOCTL_SYNCOBJ_EVENTFD before Linux 6.6): GAMESCOPE_DISABLE_EXPLICIT_SYNC=1. ++ const char *pszDisable = getenv( "GAMESCOPE_DISABLE_EXPLICIT_SYNC" ); ++ return !( pszDisable && *pszDisable == '1' ); + } + + bool CSDLBackend::IsPaused() const