Open driver pairs: vkd3d-proton reports the driver's caps; buffer attach/release tracing; Mesa log

- With a PanVK or RADV pair (BL_OPEN_DRIVER=1), droiddeck-game-env drops the app-wide
  VKD3D_FEATURE_LEVEL=12_2 and VKD3D_SHADER_MODEL=6_9 so vkd3d-proton reports what the driver has
  (an Unreal DX12 game crashed at start on PanVK with the overrides); a game's own setting still
  wins, and launch.txt records DROIDDECK_VKD3D_CAPS=driver.
- The compositor traces, on a GPU that is not an Adreno, the first 400 dma-buf attaches and buffer
  releases by wl_buffer id and sending line, and logs any release of a buffer already released
  and not attached again (gamescope's "Compositor released us but we were not acquired" on G925).
- Per-game logs no longer set MESA_DEBUG=silent, which kept mesa.log empty.
This commit is contained in:
The412Banner committed 2026-10-12 23:01:47 -04:00
1 parent 0e44b3a032
commit d16ca4d8a8
5 files changed
+64 -5

No files matched your search

+34 -2
View File
@@ -385,8 +385,39 @@ static struct dmabuf_buffer *get_dmabuf(struct wl_resource *buffer);
static int buffer_release_blocked(struct wl_resource *buffer) {
return vkp_completion_unknown() && get_dmabuf(buffer) != NULL;
}
static void send_buffer_release(struct wl_resource *buffer) {
if (!buffer_release_blocked(buffer)) wl_buffer_send_release(buffer);
/* Buffer tracing on a GPU that is not an Adreno (no KGSL node): every attach and release of the
* first 400 buffer events, by wl_buffer id and the line that sent it, and any release of a buffer
* already released and not attached again since (gamescope: "Compositor released us but we were
* not acquired"). Off on an Adreno. */
static int g_buf_trace = -1;
static int g_buf_trace_left = 400;
static uint32_t g_released_ids[128];
static int buf_trace_on(void) {
if (g_buf_trace < 0) g_buf_trace = access("/dev/kgsl-3d0", F_OK) != 0;
return g_buf_trace;
}
static void buf_trace_attach(struct wl_resource *buffer) {
if (!buffer || !buf_trace_on()) return;
uint32_t id = wl_resource_get_id(buffer);
for (int i = 0; i < 128; i++) if (g_released_ids[i] == id) g_released_ids[i] = 0;
if (g_buf_trace_left > 0) { g_buf_trace_left--; droiddeck_log("buffer", "attach id %u", id); }
}
static void buf_trace_release(struct wl_resource *buffer, int line) {
if (!buffer || !buf_trace_on()) return;
uint32_t id = wl_resource_get_id(buffer);
int twice = 0, slot = -1;
for (int i = 0; i < 128; i++) {
if (g_released_ids[i] == id) twice = 1;
if (slot < 0 && g_released_ids[i] == 0) slot = i;
}
if (twice) droiddeck_log("buffer", "DOUBLE release id %u (line %d): released again without an attach", id, line);
else if (slot >= 0) g_released_ids[slot] = id;
if (g_buf_trace_left > 0) { g_buf_trace_left--; droiddeck_log("buffer", "release id %u (line %d)", id, line); }
}
#define send_buffer_release(b) send_buffer_release_at((b), __LINE__)
static void send_buffer_release_at(struct wl_resource *buffer, int line) {
if (!buffer_release_blocked(buffer)) { buf_trace_release(buffer, line); wl_buffer_send_release(buffer); }
}
static void pending_release_free(struct pending_release *pr) {
@@ -1147,6 +1178,7 @@ static void surface_commit(struct wl_client *c, struct wl_resource *r) {
/* Role-less surface: never drawn, but its buffer is kept for a set_cursor to come. */
if (buffer) surface_hold_idle(s, buffer);
} else if (db) {
buf_trace_attach(buffer);
take_dmabuf(s, db, buffer);
} else if (shm) {
drop_dmabuf(s, 1);
@@ -688,6 +688,8 @@ class SessionService : Service() {
// CEF's accelerated shared-texture output showed stale, half-written tiles on RADV
// (Xclipse), as #570 found on PowerVR: web compositing in software, GPU raster kept.
guest.add("BL_STEAM_SOFT_COMPOSITING=1")
// droiddeck-game-env leaves vkd3d-proton to report the driver's own caps on this path.
guest.add("BL_OPEN_DRIVER=1")
Log.i(TAG, "steam: software web compositing for the open driver pair (GPU raster kept)")
LinuxRuntime.gpuStandIn(this)?.takeIf { it == LinuxRuntime.MALI_DEVICE }?.let { guest.add("BL_DRM_STANDIN=$it") }
}
@@ -178,6 +178,15 @@ def apply_config(env, config, app_id, fixes=None):
if dxvk:
own = result.get("DXVK_CONFIG", "").strip().rstrip(";").strip()
result["DXVK_CONFIG"] = own + "; " + dxvk if own else dxvk
# An open driver pair (PanVK, RADV) on a GPU that is not an Adreno: vkd3d-proton reports the
# driver's own feature level and shader model. The app-wide 12_2 / 6_9 over-promised what PanVK
# has, and an Unreal DX12 game took paths that crashed. A game's own setting still wins.
if env.get("BL_OPEN_DRIVER") == "1":
game = config.get("games", {}).get(app_id, {}) or {}
for name in ("VKD3D_FEATURE_LEVEL", "VKD3D_SHADER_MODEL"):
if name not in game and name in result:
result.pop(name)
result["DROIDDECK_VKD3D_CAPS"] = "driver"
return result
@@ -379,7 +388,6 @@ def game_logs(command, env, app_id, now=None):
"VKD3D_DEBUG": env.get("VKD3D_DEBUG", "warn"),
"VKD3D_SHADER_DEBUG": env.get("VKD3D_SHADER_DEBUG", "none"),
"MESA_LOG_FILE": str(folder / "mesa.log"),
"MESA_DEBUG": env.get("MESA_DEBUG", "silent"),
"PANVK_DEBUG": env.get("PANVK_DEBUG", "startup"),
})
try:
@@ -389,7 +397,7 @@ def game_logs(command, env, app_id, now=None):
"proton: %s" % command[0],
"started: %s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now)),
] + ["%s=%s" % (key, env[key]) for key in sorted(env)
if key.startswith(("VK_", "BL_VK", "BL_DRM", "MESA_", "PANVK", "DXVK_", "VKD3D", "ZINK", "TU_"))]) + "\n")
if key.startswith(("VK_", "BL_VK", "BL_DRM", "BL_OPEN", "MESA_", "PANVK", "DXVK_", "VKD3D", "ZINK", "TU_", "DROIDDECK_VKD3D"))]) + "\n")
except OSError:
pass
# Whether the game's sound reaches PulseAudio: the streams 20 s and 60 s in, without waiting.
+14
View File
@@ -393,6 +393,20 @@ class GameEnvironmentTest(unittest.TestCase):
named = game_logs(command, on, None, now=0)
self.assertTrue(Path(named["PROTON_LOG_DIR"]).name.endswith("-Hades"))
def test_open_driver_pairs_report_the_drivers_own_vkd3d_caps(self):
apply = MODULE["apply_config"]
config = {"version": 1, "shared": {"VKD3D_FEATURE_LEVEL": "12_2", "VKD3D_SHADER_MODEL": "6_9"},
"games": {"42": {"VKD3D_SHADER_MODEL": "6_6"}}}
adreno = apply({}, config, "1")
self.assertEqual(adreno["VKD3D_FEATURE_LEVEL"], "12_2")
pair = apply({"BL_OPEN_DRIVER": "1"}, config, "1")
self.assertNotIn("VKD3D_FEATURE_LEVEL", pair)
self.assertNotIn("VKD3D_SHADER_MODEL", pair)
self.assertEqual(pair["DROIDDECK_VKD3D_CAPS"], "driver")
own = apply({"BL_OPEN_DRIVER": "1"}, config, "42")
self.assertEqual(own["VKD3D_SHADER_MODEL"], "6_6")
self.assertNotIn("VKD3D_FEATURE_LEVEL", own)
def test_game_ids_and_probes(self):
for prefix in ("", "/compatdata/0", "/compatdata/0-123", "/compatdata/nope", "/compatdata/4294967296"):
self.assertIsNone(MODULE["game_id"]({"STEAM_COMPAT_DATA_PATH": prefix}))
+4 -1
View File
@@ -73,7 +73,10 @@ int __android_log_print(int, const char *, const char *, ...);
''')
(tmp / 'wayland-server.h').write_text('#pragma once\nstruct wl_display; struct wl_resource; struct wl_client;\n')
compositor = (SOURCE.parent / 'compositor.c').read_text()
callers = '\n'.join(function(compositor, n) for n in ('buffer_release_blocked', 'send_buffer_release', 'take_shm', 'take_pixel'))
# send_buffer_release is a macro over send_buffer_release_at (with buffer tracing).
callers = ('static void buf_trace_release(struct wl_resource *b, int line) { (void)b; (void)line; }\n'
+ '#define send_buffer_release(b) send_buffer_release_at((b), __LINE__)\n'
+ '\n'.join(function(compositor, n) for n in ('buffer_release_blocked', 'send_buffer_release_at', 'take_shm', 'take_pixel')))
harness = (Path(__file__).with_name('vk_terminal_callers_test.c')).read_text()
probe = tmp / 'probe.c'
probe.write_text(harness.replace('/* COMPOSITOR_CALLERS */', callers))