git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit0f0348fc65c51b606766812c159eef8aa6fbea91
parent92cde23169
authorLucas Galante <[email protected]>
date2026-08-12 23:12
debug: report listener + pending-callback state in CCE_FRAME_DEBUG

Extends the frame-delivery trace with listeners= (handlers attached to the
scene buffer's frame_done signal), committed_cb= and pending_cb= (frame
callbacks on the surface's applied and pending state). Read at the instant of
the emit via wlr_scene_surface_try_from_buffer, so no listener lifecycle to
manage.

These readings redirected the investigation and are worth keeping: listeners is
always 1 (the scene->surface wiring is attached, so the partial scenefx
vendoring is not implicated) and committed_cb is 0 on 77% of emits — which
turned out to be a consequence, not a cause. Emits track output frames exactly
1:1, so the scene layer faithfully mirrors the frame loop; the real fault is
that the output frame rate collapses from 58fps to 3fps during pointer motion,
with gaps up to 970ms, while render_and_commit stays at ~2.2ms.

Co-Authored-By: Claude Opus 5 <[email protected]>

 scenefx/types/scene/wlr_scene.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index aa83cc6..33ce240 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -1711,9 +1711,24 @@ void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
 	bool visible = !pixman_region32_empty(&scene_buffer->node.visible);
 	if (cce_frame_debug_match(scene_buffer->dst_width, scene_buffer->dst_height)) {
 		pixman_box32_t *extents = pixman_region32_extents(&scene_buffer->node.visible);
-		wlr_log(WLR_INFO, "[cce-frame] t=%lld send_frame_done dst=%dx%d sent=%d visible_extents=(%d,%d %dx%d)",
+		// listeners: is anything wired to turn this signal into a client callback?
+		// (wlr_scene_surface registers one; zero means the emit goes nowhere.)
+		// committed_cb / pending_cb: does the surface actually hold a frame callback
+		// for wlr_surface_send_frame_done to fire? A request stuck in `pending` means
+		// the client asked but the compositor has not applied that commit yet — which
+		// separates "compositor drops it" from "client's request never landed".
+		int listeners = wl_list_length(&scene_buffer->events.frame_done.listener_list);
+		int committed_cb = -1, pending_cb = -1;
+		struct wlr_scene_surface *scene_surface =
+			wlr_scene_surface_try_from_buffer(scene_buffer);
+		if (scene_surface != NULL && scene_surface->surface != NULL) {
+			committed_cb = wl_list_length(&scene_surface->surface->current.frame_callback_list);
+			pending_cb = wl_list_length(&scene_surface->surface->pending.frame_callback_list);
+		}
+		wlr_log(WLR_INFO, "[cce-frame] t=%lld send_frame_done dst=%dx%d sent=%d listeners=%d committed_cb=%d pending_cb=%d visible_extents=(%d,%d %dx%d)",
 				(long long)cce_now_ms(), scene_buffer->dst_width, scene_buffer->dst_height,
-				visible, extents->x1, extents->y1,
+				visible, listeners, committed_cb, pending_cb,
+				extents->x1, extents->y1,
 				extents->x2 - extents->x1, extents->y2 - extents->y1);
 	}
 	if (visible) {