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.
This commit is contained in:
The412Banner committed 2026-10-12 22:11:55 -04:00
1 parent 13e712c849
commit 0136fb6196
4 files changed
+57 -1

No files matched your search

@@ -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. */
@@ -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") }
}
+6
View File
@@ -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).
@@ -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