From 0e3fcec08fe8ddb103a062bfe8e000b7eca1718d Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 11:37:00 +0200 Subject: [PATCH 01/57] add stupid helper. add missing return Signed-off-by: h8d13 --- compbench.sh | 22 ++++++++++++++++++++++ src/backends/wayland/wayland.c | 1 + 2 files changed, 23 insertions(+) create mode 100755 compbench.sh diff --git a/compbench.sh b/compbench.sh new file mode 100755 index 0000000..c90649e --- /dev/null +++ b/compbench.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# stupid helper to compile versionned + +set -euo pipefail + +# git rev parse branch +BRANCH="$(git rev-parse --abbrev-ref HEAD)" +# git rev parse commit +COMMIT="$(git rev-parse --short HEAD)" +BUILD="build-$BRANCH-$COMMIT" + +# meson conf +meson setup --reconfigure "$BUILD" +# meson comp +meson compile -C "$BUILD" + +# run nested smoke test +# -b only scans installed backends, build tree needs explicit path +# wl is picked from WAYLAND_DISPLAY +WL_BACKEND="$BUILD/src/backends/wayland/libwl-backend.so" +timeout 5 "$BUILD/vortex" --verbose --backend-path "$WL_BACKEND" \ + || [[ $? -eq 124 ]] diff --git a/src/backends/wayland/wayland.c b/src/backends/wayland/wayland.c index ce248fb..9edc754 100644 --- a/src/backends/wayland/wayland.c +++ b/src/backends/wayland/wayland.c @@ -922,6 +922,7 @@ bool backend_implement_wl(struct vt_compositor_t *comp) { // No session in Wayland nested memset(&comp->session->impl, 0, sizeof(comp->session->impl)); + return true; } bool backend_handle_frame_wl(struct vt_backend_t *backend, From 6845e192df22a6f5de85f616a917e4f4c4afb306 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:14:40 +0200 Subject: [PATCH 02/57] feat(bench): glmark2 bench on nested and real DRM --- compbench.sh | 26 +++++++++++++----- scripts/bench-drm.sh | 44 ++++++++++++++++++++++++++++++ scripts/bench-run.sh | 65 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 7 deletions(-) create mode 100755 scripts/bench-drm.sh create mode 100755 scripts/bench-run.sh diff --git a/compbench.sh b/compbench.sh index c90649e..8730425 100755 --- a/compbench.sh +++ b/compbench.sh @@ -1,8 +1,12 @@ #!/usr/bin/env bash -# stupid helper to compile versionned +# stupid helper to compile versionned, then bench with glmark2-wayland +# usage: compbench.sh [wl|drm] [glmark2 args...] set -euo pipefail +MODE="${1:-wl}" +shift || true + # git rev parse branch BRANCH="$(git rev-parse --abbrev-ref HEAD)" # git rev parse commit @@ -14,9 +18,17 @@ meson setup --reconfigure "$BUILD" # meson comp meson compile -C "$BUILD" -# run nested smoke test -# -b only scans installed backends, build tree needs explicit path -# wl is picked from WAYLAND_DISPLAY -WL_BACKEND="$BUILD/src/backends/wayland/libwl-backend.so" -timeout 5 "$BUILD/vortex" --verbose --backend-path "$WL_BACKEND" \ - || [[ $? -eq 124 ]] +# -b only scans installed backends, scripts pass the build tree .so +case "$MODE" in +wl) + scripts/bench-run.sh "$BUILD" wl "$@" + ;; +drm) + # build stays user-owned, only the VT session needs root + sudo scripts/bench-drm.sh "$BUILD" "$@" + ;; +*) + echo "unknown mode '$MODE' (wl|drm)" >&2 + exit 1 + ;; +esac diff --git a/scripts/bench-drm.sh b/scripts/bench-drm.sh new file mode 100755 index 0000000..0ee7aed --- /dev/null +++ b/scripts/bench-drm.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# run bench-run.sh in drm mode on a spare VT, from any terminal or ssh. +# logind only hands out DRM master to an active session bound to a VT, +# PAMName=login + TTYPath create exactly that for the transient unit. +# usage: sudo bench-drm.sh [glmark2 args...] + +set -euo pipefail + +if [[ "$EUID" -ne 0 || -z "${SUDO_USER:-}" ]]; then + echo "run through sudo: sudo $0 [glmark2 args...]" >&2 + exit 1 +fi + +BUILD="$(realpath "$1")" +shift +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VT="${VT:-8}" +TIMEOUT="${TIMEOUT:-600}" +UNIT="vortex-bench" + +ORIG_VT="$(fgconsole)" +trap 'chvt "$ORIG_VT"' EXIT + +echo "switching to VT $VT, back to VT $ORIG_VT when done" +chvt "$VT" + +# RuntimeMaxSec backs up the vortex watchdog: the unit dying frees the VT +status=0 +systemd-run --unit="$UNIT" --wait --collect \ + -p PAMName=login -p User="$SUDO_USER" \ + -p TTYPath="/dev/tty$VT" -p StandardInput=tty-force \ + -p StandardOutput=journal -p StandardError=journal \ + -p TTYReset=yes -p TTYVTDisallocate=yes \ + -p RuntimeMaxSec="$((TIMEOUT + 30))" \ + -p WorkingDirectory="$ROOT" \ + --setenv=TIMEOUT="$TIMEOUT" \ + "$ROOT/scripts/bench-run.sh" "$BUILD" drm "$@" || status=$? + +# unit stdout did not reach the journal in testing, read results back +if [[ -f "$BUILD/glmark2-drm.txt" ]]; then + cat "$BUILD/glmark2-drm.txt" +fi +echo "unit exited with status $status. vortex log: $BUILD/vortex-drm.log" +exit "$status" diff --git a/scripts/bench-run.sh b/scripts/bench-run.sh new file mode 100755 index 0000000..6206386 --- /dev/null +++ b/scripts/bench-run.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# run vortex from a build dir, attach glmark2-wayland, save score. +# usage: bench-run.sh [glmark2 args...] + +set -euo pipefail + +BUILD="$1" +MODE="$2" +shift 2 + +# watchdog: a hung vortex on drm keeps the VT. SIGABRT leaves a core. +TIMEOUT="${TIMEOUT:-600}" +LOG="$BUILD/vortex-$MODE.log" +OUT="$BUILD/glmark2-$MODE.txt" + +case "$MODE" in +wl) + BACKEND="$BUILD/src/backends/wayland/libwl-backend.so" + ;; +drm) + BACKEND="$BUILD/src/backends/drm/libdrm-backend.so" + if [[ "$(tty)" != /dev/tty[0-9]* ]]; then + echo "drm mode needs a VT, got $(tty). Use bench-drm.sh" >&2 + exit 1 + fi + # vortex picks the wl backend whenever this is set + unset WAYLAND_DISPLAY + ;; +*) + echo "unknown mode '$MODE' (wl|drm)" >&2 + exit 1 + ;; +esac + +# line-buffered so the readiness lines show up while vortex runs +timeout -s ABRT -k 5 "$TIMEOUT" stdbuf -oL -eL \ + "$BUILD/vortex" --verbose --backend-path "$BACKEND" >"$LOG" 2>&1 & +VPID=$! +trap 'kill "$VPID" || true; wait "$VPID" || true' EXIT + +# socket is bound before session/backend init, seat init comes last +SOCK="" +for _ in $(seq 100); do + if ! kill -0 "$VPID"; then + echo "vortex exited early, log: $LOG" >&2 + cat "$LOG" + exit 1 + fi + if grep -q "Initialized wayland seat." "$LOG"; then + SOCK="$(grep -oP "ready on socket '\K[^']+" "$LOG")" + break + fi + sleep 0.1 +done + +if [[ -z "$SOCK" ]]; then + echo "vortex not ready after 10s, log: $LOG" >&2 + cat "$LOG" + exit 1 +fi + +echo "vortex ready on $SOCK ($MODE), running glmark2" +# stderr too: a glmark2 init failure is otherwise invisible on drm +WAYLAND_DISPLAY="$SOCK" glmark2-wayland "$@" 2>&1 | tee "$OUT" +echo "score saved: $OUT, vortex log: $LOG" From 31d8f121ac77b6bb88cc4511ba368e5366c1d4b0 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:15:46 +0200 Subject: [PATCH 03/57] fix(input): wait for nested keymap, bind keybinds once --- src/input/backends/wayland/wayland_input.c | 11 +++++++++-- src/input/wl_seat.c | 5 ++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/input/backends/wayland/wayland_input.c b/src/input/backends/wayland/wayland_input.c index dfc68dd..8c7c66a 100644 --- a/src/input/backends/wayland/wayland_input.c +++ b/src/input/backends/wayland/wayland_input.c @@ -152,8 +152,6 @@ void _wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_LOGO); ibackend->mods.caps = _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_CAPS); - - vt_seat_bind_global_keybinds(wl->comp->seat); } void _wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, @@ -388,6 +386,15 @@ bool input_backend_init_wl(struct vt_input_backend_t *backend, wl_keyboard_add_listener(wl->wl_keyboard, &keyboard_listener, wl); wl_pointer_add_listener(wl->wl_pointer, &pointer_listener, wl); + // keymap arrives async from the parent, clients binding wl_keyboard + // before it lands would get a NULL keymap + struct wl_display *parent = + wl_proxy_get_display((struct wl_proxy *)wl->wl_seat); + if (wl_display_roundtrip(parent) < 0 || !backend->keymap) { + VT_ERROR(backend->comp->log, "No keymap received from parent seat."); + return false; + } + VT_TRACE(backend->comp->log, "Initialized Wayland nested keyboard backend."); return true; } diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 08feb5f..e507ee8 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -499,9 +499,8 @@ bool vt_seat_init(struct vt_seat_t *seat) { return false; } - if (seat->comp->input_backend->platform == VT_INPUT_LIBINPUT) { - vt_seat_bind_global_keybinds(seat); - } + // every input backend has its keymap (and mod masks) by now + vt_seat_bind_global_keybinds(seat); return true; } From 95184003ab6c3c31822353ea78bdd104ddb445c7 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:15:59 +0200 Subject: [PATCH 04/57] fix(input): drop leaked keymap ref per keyboard bind --- src/input/wl_seat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index e507ee8..5c817b2 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -148,7 +148,6 @@ void _wl_seat_bind(struct wl_client *client, void *data, uint32_t version, int _wl_create_keymap_fd(struct vt_keyboard_t *kbd) { struct xkb_keymap *keymap = kbd->seat->comp->input_backend->keymap; - xkb_keymap_ref(keymap); char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); From 6d79527b4cd274daf96bbe928726911659a3da74 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:00 +0200 Subject: [PATCH 05/57] fix(input): honor XKB_DEFAULT_* env on DRM --- src/input/backends/libinput/libinput.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/input/backends/libinput/libinput.c b/src/input/backends/libinput/libinput.c index 3bee371..e5d97c9 100644 --- a/src/input/backends/libinput/libinput.c +++ b/src/input/backends/libinput/libinput.c @@ -286,11 +286,8 @@ bool _vt_li_input_handle_pointer_scroll_event( } bool _vt_li_init_xkb(struct vt_input_backend_li_t *li) { - struct xkb_rule_names rules = {.rules = "evdev", - .model = "pc105", - .layout = "us", - .variant = NULL, - .options = NULL}; + // NULL fields make xkbcommon read XKB_DEFAULT_*, then evdev/pc105/us + struct xkb_rule_names rules = {0}; struct vt_input_backend_t *backend = li->comp->input_backend; From 4131107378bb58b758791481fb3b90f4a2eb2bea Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:00 +0200 Subject: [PATCH 06/57] fix(input): return status from libinput suspend/resume --- src/input/backends/libinput/libinput.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/input/backends/libinput/libinput.c b/src/input/backends/libinput/libinput.c index e5d97c9..41d700f 100644 --- a/src/input/backends/libinput/libinput.c +++ b/src/input/backends/libinput/libinput.c @@ -461,6 +461,7 @@ bool input_backend_suspend_li(struct vt_input_backend_t *backend) { BACKEND_DATA(backend, struct vt_input_backend_li_t); libinput_suspend(li->input); VT_TRACE(backend->comp->log, "Suspended libinput."); + return true; } bool input_backend_resume_li(struct vt_input_backend_t *backend) { @@ -468,6 +469,10 @@ bool input_backend_resume_li(struct vt_input_backend_t *backend) { return false; struct vt_input_backend_li_t *li = BACKEND_DATA(backend, struct vt_input_backend_li_t); - libinput_resume(li->input); + if (libinput_resume(li->input) != 0) { + VT_ERROR(backend->comp->log, "Failed to resume libinput."); + return false; + } VT_TRACE(backend->comp->log, "Resumed libinput."); + return true; } From c689bccb969702b4bb6833ea85452874a4783c69 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:13 +0200 Subject: [PATCH 07/57] fix(input): send modifiers after late keyboard enter --- src/input/wl_seat.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 5c817b2..0954d78 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -175,7 +175,9 @@ void _wl_keyboard_send_initial_state(struct vt_keyboard_t *kbd) { uint32_t group = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_EFFECTIVE); - wl_keyboard_send_modifiers(kbd->res, 0, depressed, latched, locked, group); + wl_keyboard_send_modifiers(kbd->res, + wl_display_next_serial(kbd->seat->comp->wl.dsp), + depressed, latched, locked, group); } bool _wl_handle_global_keybind(struct vt_seat_t *seat, uint32_t keycode, @@ -346,6 +348,9 @@ void _wl_seat_get_keyboard(struct wl_client *client, wl_keyboard_send_enter(kbd->res, serial, surf->res, &keys); wl_array_release(&keys); + + // protocol: modifiers must follow enter + _wl_keyboard_send_initial_state(kbd); } } From 87a34d20a39c099ac3fe9140e9976da79d2988bd Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:38 +0200 Subject: [PATCH 08/57] fix(drm): one liftoff device per CRTC --- src/backends/drm/drm_backend.c | 81 +++++++++++++++++++++++----------- src/backends/drm/drm_types.h | 6 ++- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 1147097..eb4a7ab 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -873,19 +873,6 @@ static bool _drm_init_for_device(struct vt_compositor_t *comp, _drm_update_tearing_cap(drm); - int liftoff_fd = dup(drm->drm_fd); - if (liftoff_fd < 0) { - VT_ERROR(comp->log, "Failed to dup DRM fd for libliftoff."); - return false; - } - - drm->liftoff_dev = liftoff_device_create(liftoff_fd); - if (!drm->liftoff_dev) { - close(liftoff_fd); - VT_ERROR(comp->log, "Failed to create liftoff device."); - return false; - } - /* Renderer is only initialized on the main DRM device */ if (!drm_master->main_drm && comp->renderer->impl.is_handle_renderable( comp->renderer, drm->native_handle)) { @@ -1109,17 +1096,56 @@ static bool _drm_plane_init(struct vt_drm_backend_state_t *drm, return false; } - if (drm->impl->atomic && plane->type != DRM_PLANE_TYPE_CURSOR) { - plane->liftoff_plane = liftoff_plane_create(drm->liftoff_dev, plane->id); - if (!plane->liftoff_plane) { - VT_ERROR(comp->log, - "Failed to create liftoff plane for plane %p (plane ID: %d)", - plane, plane->id); - return false; + return true; +} + +static void _drm_liftoff_finish(struct vt_drm_backend_state_t *drm) { + struct vt_drm_crtc_t *crtc; + wl_array_for_each(crtc, &drm->crtcs) { + // also destroys the device's planes + liftoff_device_destroy(crtc->liftoff_dev); + crtc->liftoff_dev = NULL; + } + + struct vt_drm_plane_t *plane; + wl_array_for_each(plane, &drm->planes) { + plane->liftoff_plane = NULL; + } +} + +static bool _drm_liftoff_init(struct vt_drm_backend_state_t *drm) { + struct vt_drm_crtc_t *crtc; + wl_array_for_each(crtc, &drm->crtcs) { + // liftoff dups the fd itself + crtc->liftoff_dev = liftoff_device_create(drm->drm_fd); + if (!crtc->liftoff_dev) { + VT_ERROR(drm->comp->log, "Failed to create liftoff device for CRTC %u.", + crtc->id); + goto fail; + } + + struct vt_drm_plane_t *plane; + wl_array_for_each(plane, &drm->planes) { + // a plane shared between CRTCs goes to the first one only + if (plane->type == DRM_PLANE_TYPE_CURSOR || plane->liftoff_plane || + !(plane->possible_crtcs & (1u << crtc->index))) + continue; + + plane->liftoff_plane = liftoff_plane_create(crtc->liftoff_dev, plane->id); + if (!plane->liftoff_plane) { + VT_ERROR(drm->comp->log, + "Failed to create liftoff plane %u for CRTC %u.", plane->id, + crtc->id); + goto fail; + } } } return true; + +fail: + _drm_liftoff_finish(drm); + return false; } static bool _drm_planes_init(struct vt_drm_backend_state_t *drm) { @@ -1263,6 +1289,12 @@ static bool _drm_init_crtcs_and_planes(struct vt_drm_backend_state_t *drm) { } } else { _drm_assign_crtc_planes(drm); + + if (drm->impl->atomic && !_drm_liftoff_init(drm)) { + VT_WARN(comp->log, "Failed to initialize libliftoff, using legacy KMS."); + drm->impl = &drm_kms_legacy_impl; + _drm_update_tearing_cap(drm); + } } drmModeFreeResources(res); @@ -1694,7 +1726,8 @@ static bool _drm_create_output_for_device(struct vt_drm_backend_state_t *drm, if (drm->impl->atomic) { if (!(drm_output->liftoff_output = - liftoff_output_create(drm->liftoff_dev, drm_output->crtc->id))) { + liftoff_output_create(drm_output->crtc->liftoff_dev, + drm_output->crtc->id))) { VT_ERROR(comp->log, "Failed to create liftoff output for output."); return false; } @@ -2324,15 +2357,11 @@ static bool _drm_terminate_for_device(struct vt_drm_backend_state_t *drm) { _drm_destroy_output_for_device(drm, output); } - if (drm->impl->atomic) - liftoff_device_destroy(drm->liftoff_dev); + _drm_liftoff_finish(drm); struct vt_drm_plane_t *plane; wl_array_for_each(plane, &drm->planes) { vt_drm_format_array_free(&plane->formats); - if (plane->liftoff_plane) { - liftoff_plane_destroy(plane->liftoff_plane); - } free(plane->cursor_sizes); } wl_array_release(&drm->planes); diff --git a/src/backends/drm/drm_types.h b/src/backends/drm/drm_types.h index a6ee79c..a1711c5 100644 --- a/src/backends/drm/drm_types.h +++ b/src/backends/drm/drm_types.h @@ -74,6 +74,10 @@ struct vt_drm_crtc_t { struct vt_drm_plane_t *plane_cursor; struct vt_drm_plane_t *plane_primary; + /* per CRTC: liftoff writes every plane of its device into each request, + * a shared device would drag other CRTCs into per-output commits */ + struct liftoff_device *liftoff_dev; + bool in_use; }; @@ -173,8 +177,6 @@ struct vt_drm_backend_state_t { struct wl_array sampling_formats; drmModeRes *res; - - struct liftoff_device *liftoff_dev; }; struct vt_drm_backend_master_state_t { From 40efde376409812ce10a18e2a92f4cd7fe150b0d Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:44 +0200 Subject: [PATCH 09/57] fix(drm): zero CRTC state on init --- src/backends/drm/drm_backend.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index eb4a7ab..0e3a71c 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -1214,8 +1214,8 @@ static bool _drm_init_crtc(struct vt_compositor_t *comp, if (!crtc) return false; - crtc->id = id; - crtc->index = index; + // wl_array_add memory is uninitialized + *crtc = (struct vt_drm_crtc_t){.id = id, .index = index}; if (!drm_kms_props_get_crtc(drm_fd, id, crtc->props)) { memset(crtc->props, 0, sizeof(crtc->props)); From 350ccc114dcab68eb33ae1322d106c1900f234da Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:16:44 +0200 Subject: [PATCH 10/57] fix(drm): check device path content, not array address --- src/backends/drm/drm_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 0e3a71c..3ab23ce 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -1903,7 +1903,7 @@ static void _drm_on_drm_change(struct wl_listener *listener, void *data) { struct vt_drm_backend_state_t *drm; wl_list_for_each(drm, &master->backends, link) { - if (!drm->dev || !drm->dev->path || !event->device_node_name) + if (!drm->dev || !drm->dev->path[0] || !event->device_node_name) continue; const char *node = strrchr(drm->dev->path, '/'); From 3a3a6dcded561406b0d9d50ac3c12c362b13446c Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:14 +0200 Subject: [PATCH 11/57] fix(dmabuf): close params before freeing them --- src/protocols/linux_dmabuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index c8d11e0..b06f2de 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -752,8 +752,8 @@ void _linux_dmabuf_params_create(struct wl_resource *resource, uint32_t buf_id, buf->res = wl_resource_create(client, &wl_buffer_interface, 1, buf_id); if (!buf->res) { free(buf); - free(params); _linux_dmabuf_close_params(params); + free(params); VT_WL_OUT_OF_MEMORY(_proto->comp, client); return; } From b3836b23a28c0815185dcb0dc532dd3fa4ce4863 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:14 +0200 Subject: [PATCH 12/57] fix(dmabuf): free packed feedback on error paths --- src/protocols/linux_dmabuf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index b06f2de..6ef9682 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -1182,7 +1182,7 @@ bool _linux_dmabuf_pack_feedback( if (!wl_array_add(&tranche_packed->indices, n_entries * sizeof(uint16_t))) { VT_ERROR(feedback->comp->log, "Failed to allocate tranche indices array"); - goto fail_ro_fd; + goto fail_packed; } n = 0; @@ -1198,7 +1198,7 @@ bool _linux_dmabuf_pack_feedback( "Format 0x%" PRIX32 " modifier 0x%" PRIX64 " in tranche %u is missing from the global table", fmt->format, mod->mod, i); - goto fail_ro_fd; + goto fail_packed; } indices[n] = (uint16_t)index; n++; @@ -1215,10 +1215,13 @@ bool _linux_dmabuf_pack_feedback( return true; +fail_packed: + for (size_t i = 0; i < n_tranches; i++) + wl_array_release(&packed->tranches[i].indices); + free(packed); fail_ro_fd: if (ro_fd >= 0) close(ro_fd); -fail_rw_fd: if (rw_fd >= 0) close(rw_fd); fail_all_formats: From 79f0beba4f82cbeba33f7b73338720adafeb01bf Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:14 +0200 Subject: [PATCH 13/57] fix(dmabuf): log feedback before freeing it --- src/protocols/linux_dmabuf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index 6ef9682..71523e8 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -1243,12 +1243,13 @@ void _linux_dmabuf_free_feedback( for (uint32_t i = 0; i < packed->n_tranches; i++) wl_array_release(&packed->tranches[i].indices); + VT_TRACE(_proto->comp->log, + "linux_dmabuf.free_feedback: freeing packed feedback %p.", + (void *)packed); + /* 3. Close FD and free structure */ close(packed->entries_fd); free(packed); - - VT_TRACE(_proto->comp->log, - "linux_dmabuf.free_feedback: freed packed feedback %p.", packed); } static struct vt_linux_dmabuf_v1_output_feedback_t * From fa05c2b1e7586900b0db5d821d8ac69278aa3e1c Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:49 +0200 Subject: [PATCH 14/57] fix(session): check udev devnode instead of action twice --- src/backends/drm/session_drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 7965982..838c298 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -241,7 +241,7 @@ int32_t _vt_session_drm_udev_dispatch(int fd, uint32_t mask, void *data) { VT_TRACE(session->comp->log, "Received udev event for device %s (%s)", name, action); - if (!_vt_session_drm_card_valid(name) || !action || !action) { + if (!_vt_session_drm_card_valid(name) || !node || !action) { udev_device_unref(udev_dev); return 1; } From eaf09daad3ff643a5cc76fe99782d42d786a29c0 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:49 +0200 Subject: [PATCH 15/57] fix(session): unref udev device after handled event --- src/backends/drm/session_drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 838c298..75ad399 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -283,6 +283,7 @@ int32_t _vt_session_drm_udev_dispatch(int fd, uint32_t mask, void *data) { VT_WARN(session->comp->log, "Got unknown udev action for device %s", name); } + udev_device_unref(udev_dev); return 0; } From d4e7d071ddac47bcf9cf088a391a551b3faaee48 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:17:57 +0200 Subject: [PATCH 16/57] fix(log): use correct specifiers for size_t and dev_t --- src/backends/drm/session_drm.c | 4 ++-- src/protocols/linux_dmabuf.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 75ad399..6ba33bc 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -441,8 +441,8 @@ bool vt_session_open_device_drm(struct vt_session_t *session, dev->device_id = device_id; dev->dev = st.st_rdev; - VT_TRACE(session->comp->log, "Opening device %s, device ID: %i", path, - dev->dev); + VT_TRACE(session->comp->log, "Opening device %s, device ID: %ju", path, + (uintmax_t)dev->dev); return true; } diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index 71523e8..1dd123f 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -1137,7 +1137,8 @@ bool _linux_dmabuf_pack_feedback( close(rw_fd); rw_fd = -1; - VT_TRACE(feedback->comp->log, "Building packed feedback for %i tranche(s)...", + VT_TRACE(feedback->comp->log, + "Building packed feedback for %zu tranche(s)...", feedback->tranches.size / sizeof(struct vt_dmabuf_tranche_t)); size_t n_tranches = @@ -1164,7 +1165,7 @@ bool _linux_dmabuf_pack_feedback( packed->entries_fd = ro_fd; VT_TRACE(feedback->comp->log, - "Building packed feedback tranches (Total Tranches: %i)...", + "Building packed feedback tranches (Total Tranches: %zu)...", packed->n_tranches); size_t n = 0; @@ -1421,7 +1422,7 @@ void _linux_dmabuf_send_feedback( return; VT_TRACE(_proto->comp->log, - "linux_dmabuf.send_feedback: sending feedback (%i tranches).", + "linux_dmabuf.send_feedback: sending feedback (%zu tranches).", feedback->n_tranches); /* 2. Prepare main device descriptor */ @@ -1443,8 +1444,8 @@ void _linux_dmabuf_send_feedback( zwp_linux_dmabuf_feedback_v1_send_main_device(res, &device); VT_TRACE(_proto->comp->log, - "linux_dmabuf.send_feedback: sent main device %i.", - feedback->dev_main); + "linux_dmabuf.send_feedback: sent main device %ju.", + (uintmax_t)feedback->dev_main); /* 4. Send tranche data */ for (size_t i = 0; i < feedback->n_tranches; i++) { @@ -1453,8 +1454,8 @@ void _linux_dmabuf_send_feedback( VT_TRACE( _proto->comp->log, - "linux_dmabuf.send_feedback: sending tranche %zu (dev=%u, flags=%u).", - i, tranche->dev_target, tranche->flags); + "linux_dmabuf.send_feedback: sending tranche %zu (dev=%ju, flags=%u).", + i, (uintmax_t)tranche->dev_target, tranche->flags); *dev = tranche->dev_target; zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(res, &device); From 106acec59bab77641696a74726876841570940e0 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:28 +0200 Subject: [PATCH 17/57] fix(log): keep log stream when logfile cannot be opened --- src/core/compositor.c | 9 ++++++--- src/core/util.c | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/core/compositor.c b/src/core/compositor.c index d851c20..b4c4434 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -262,9 +262,12 @@ const char *_vt_comp_handle_cmd_flags(struct vt_compositor_t *c, int argc, if (!flag) continue; if (_flag_cmp(flag, "--logfile", "-lf")) { - c->log.stream = fopen(vt_util_log_get_filepath(), "a"); - if (c->log.stream) { - setvbuf(c->log.stream, NULL, _IONBF, 0); + // keep the current stream on failure, log macros never check NULL + const char *logpath = vt_util_log_get_filepath(); + FILE *stream = logpath ? fopen(logpath, "a") : NULL; + if (stream) { + setvbuf(stream, NULL, _IONBF, 0); + c->log.stream = stream; } else { perror("log fopen"); } diff --git a/src/core/util.c b/src/core/util.c index 3a3a643..e4b2bbb 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -87,14 +87,22 @@ char *vt_util_log_get_filepath() { snprintf(path, sizeof(path), "%s/.local/state", home); state_home = path; } + if (!state_home) + return NULL; + // a truncated path would create and log into the wrong directory char log_dir[PATH_MAX]; - snprintf(log_dir, sizeof(log_dir), "%s/%s/logs", state_home, _BRAND_NAME); + int n = + snprintf(log_dir, sizeof(log_dir), "%s/%s/logs", state_home, _BRAND_NAME); + if (n < 0 || (size_t)n >= sizeof(log_dir)) + return NULL; // create directories if they don't exist mkdir(state_home, 0755); + // shorter than log_dir, so it cannot truncate char app_dir[PATH_MAX]; - snprintf(app_dir, sizeof(app_dir), "%s/%s", state_home, _BRAND_NAME); + if (snprintf(app_dir, sizeof(app_dir), "%s/%s", state_home, _BRAND_NAME) < 0) + return NULL; mkdir(app_dir, 0755); mkdir(log_dir, 0755); @@ -104,8 +112,10 @@ char *vt_util_log_get_filepath() { // scheme: /--.log static char logfile[PATH_MAX]; - snprintf(logfile, sizeof(logfile), "%s/%s-%s-%d.log", log_dir, _BRAND_NAME, - timestamp, pid); + n = snprintf(logfile, sizeof(logfile), "%s/%s-%s-%d.log", log_dir, + _BRAND_NAME, timestamp, pid); + if (n < 0 || (size_t)n >= sizeof(logfile)) + return NULL; return logfile; } From 305cf84b83a679363502927d18070384ec667828 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:29 +0200 Subject: [PATCH 18/57] fix(core): parenthesize VT_MAX and VT_MIN --- src/core/util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/util.h b/src/core/util.h index 6edda35..db9c44e 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -140,8 +140,8 @@ wl_client_post_no_memory((client)); \ } while (0) -#define VT_MAX(a, b) a > b ? a : b -#define VT_MIN(a, b) a < b ? a : b +#define VT_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define VT_MIN(a, b) ((a) < (b) ? (a) : (b)) typedef enum { VT_LL_TRACE = 0, From 37b4e2af997ae246716dffc57092a49a97a8aabb Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:29 +0200 Subject: [PATCH 19/57] fix(scene): use signed math for output overlap --- src/core/scene.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/scene.c b/src/core/scene.c index 4c1943b..119af4f 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -613,9 +613,12 @@ struct vt_output_t *vt_scene_node_primary_output(struct vt_compositor_t *comp, int32_t x1 = VT_MAX(rect->x, output->x); int32_t y1 = VT_MAX(rect->y, output->y); - int32_t x2 = VT_MIN(rect->x + rect->width, output->x + output->width); + // signed math: rects may start left/above an output + int32_t x2 = + VT_MIN(rect->x + rect->width, output->x + (int32_t)output->width); - int32_t y2 = VT_MIN(rect->y + rect->height, output->y + output->height); + int32_t y2 = + VT_MIN(rect->y + rect->height, output->y + (int32_t)output->height); if (x2 <= x1 || y2 <= y1) continue; From dcb6f3e31fa1c35c21d847344f4ba26d44edafab Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:29 +0200 Subject: [PATCH 20/57] fix(core): forward-declare structs used in prototypes --- src/core/buffer.h | 1 + src/core/surface_addon.h | 1 + src/core/util.h | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/buffer.h b/src/core/buffer.h index a1b5329..bef1e53 100644 --- a/src/core/buffer.h +++ b/src/core/buffer.h @@ -29,6 +29,7 @@ #include "core_types.h" struct vt_buffer_t; +struct vt_buffer_release_t; struct vt_buffer_implementation_t { bool (*get_dmabuf)(struct vt_buffer_t *buf, struct vt_dmabuf_attr_t *o_attr); diff --git a/src/core/surface_addon.h b/src/core/surface_addon.h index 806491d..ef352c2 100644 --- a/src/core/surface_addon.h +++ b/src/core/surface_addon.h @@ -25,6 +25,7 @@ #include #include +struct vt_surface_t; struct vt_surface_addon_t; struct vt_content_update_t; diff --git a/src/core/util.h b/src/core/util.h index db9c44e..66b0acf 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -180,9 +180,11 @@ void vt_util_arena_destroy(struct vt_arena_t *a); void vt_util_emit_signal(struct wl_signal *signal, void *data); +struct vt_compositor_t; +struct vt_output_t; + int vt_util_allocate_shm_file(struct vt_compositor_t *comp, size_t size); -struct vt_compositor_t; bool vt_util_allocate_shm_rwro_pair(struct vt_compositor_t *comp, size_t size, int *rw_fd, int *ro_fd); From cd2438a0fa9bc8c267e029debfa2e5870ed5ec7b Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:29 +0200 Subject: [PATCH 21/57] fix(wl): end session when nested window closes --- src/backends/wayland/wayland.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backends/wayland/wayland.c b/src/backends/wayland/wayland.c index 9edc754..08cb1d1 100644 --- a/src/backends/wayland/wayland.c +++ b/src/backends/wayland/wayland.c @@ -344,8 +344,11 @@ void _wl_parent_xdg_surface_configure(void *data, struct xdg_surface *surf, } void _wl_parent_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) { + (void)toplevel; struct vt_output_t *output = data; - (output->backend, output); + // destroying the output here leaves its pending frame callbacks dangling, + // closing any nested window ends the session instead + output->backend->comp->running = false; } int _wl_parent_dispatch(int fd, uint32_t mask, void *data) { From eaf9f059834d78dd3f43a2bc1b9eef7d77de9858 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:18:29 +0200 Subject: [PATCH 22/57] fix(render): return early when begin_scene has no output --- src/render/gl/egl_gl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index a3a9056..cde6e58 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -1387,7 +1387,7 @@ void renderer_composite_pass_egl(struct vt_renderer_t *r, void renderer_begin_scene_egl(struct vt_renderer_t *r, struct vt_output_t *output) { if (!output) - ; + return; if (!r || !r->impl.begin_scene || !r->user_data) { VT_ERROR(r->comp->log, "Renderer backend not initialized before beginning frame."); From eb2baaf69737143acb097305d428eee63bd8c3ad Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:00 +0200 Subject: [PATCH 23/57] fix(drm): reject negative shm cursor dimensions --- src/backends/drm/drm_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 3ab23ce..8d3a720 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -2114,7 +2114,7 @@ static bool _drm_import_cursor_image(struct vt_drm_output_state_t *drm_output, return false; } - if (!shm.data || shm.width == 0 || shm.height == 0 || shm.stride == 0) + if (!shm.data || shm.width <= 0 || shm.height <= 0 || shm.stride <= 0) return false; /* Pick viable supported hardware cursor size */ From 0b92325fc6a78b283a89a0ac7c6496443a5c36e6 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:15 +0200 Subject: [PATCH 24/57] chore: cast %p log arguments to void * --- src/backends/drm/atomic.c | 2 +- src/backends/drm/drm_backend.c | 34 ++++++++++++------------ src/core/buffer.c | 32 ++++++++++++----------- src/core/compositor.c | 15 ++++++----- src/core/content_update.c | 24 +++++++++-------- src/core/scene.c | 18 +++++++------ src/core/surface.c | 2 +- src/input/wl_seat.c | 2 +- src/protocols/linux_dmabuf.c | 27 +++++++++---------- src/protocols/linux_explicit_sync.c | 8 +++--- src/protocols/wl_surface.c | 25 ++++++++++-------- src/protocols/xdg_shell.c | 40 ++++++++++++++--------------- src/render/gl/egl_gl.c | 38 +++++++++++++++------------ 13 files changed, 142 insertions(+), 125 deletions(-) diff --git a/src/backends/drm/atomic.c b/src/backends/drm/atomic.c index 09ecc2e..9340836 100644 --- a/src/backends/drm/atomic.c +++ b/src/backends/drm/atomic.c @@ -219,7 +219,7 @@ static bool _atomic_prepare_liftoff(struct vt_drm_backend_state_t *drm, if (!liftoff_layer) { VT_ERROR(drm->comp->log, "Layer %p has no retained Liftoff layer.", - layer); + (void *)layer); return false; } diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 8d3a720..35e0e9a 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -1636,19 +1636,19 @@ static bool _drm_create_output_for_device(struct vt_drm_backend_state_t *drm, VT_WARN(comp->log, "CRTC %" PRIu32 " does not have a cursor plane. Using software cursor on output %p", - drm_output->crtc->id, output); + drm_output->crtc->id, (void *)output); output->cursor_mode = VT_CURSOR_MODE_SOFTWARE; } else if (getenv("VT_DRM_NO_HARDWARE_CURSOR")) { VT_TRACE(comp->log, "Hardware cursor was disabled by the environment. Using software " "cursor on output %p", - output); + (void *)output); output->cursor_mode = VT_CURSOR_MODE_SOFTWARE; } else { VT_TRACE(comp->log, "CRTC %" PRIu32 " has a cursor plane. Using hardware cursor on output %p", - drm_output->crtc->id, output); + drm_output->crtc->id, (void *)output); output->cursor_mode = VT_CURSOR_MODE_HARDWARE; } @@ -2693,17 +2693,17 @@ static bool _drm_handle_frame_for_device(struct vt_drm_backend_state_t *drm, if (!vt_output_track_presented_surface(output, layer->surface)) { VT_ERROR(comp->log, "Failed to track directly scanned-out surface %p.", - layer->surface); + (void *)layer->surface); continue; } VT_TRACE(comp->log, "OUTPUT: tracking presented surface=%p output=%p", - layer->surface, output); + (void *)layer->surface, (void *)output); } VT_TRACE( comp->log, "real commit succeeded output=%p event_pending=%d layers=%zu", - output, commit.event_pending, + (void *)output, commit.event_pending, drm_output->pending_layers.size / sizeof(struct vt_drm_layer_state_t)); drm_output->pending_valid = true; @@ -3599,7 +3599,7 @@ bool backend_build_surface_feedback(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Cannot build surface feedback for surface %p, output %p; CRTC or" "primary plane is not available", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3613,7 +3613,7 @@ bool backend_build_surface_feedback(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Failed to create direct-scanout tranche for surface feedback of " "surface %p, output %p", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3623,7 +3623,7 @@ bool backend_build_surface_feedback(struct vt_backend_t *backend, VT_TRACE(drm_master->comp->log, "Surface feedback for surface=%p output=%p: direct scanout tranche", - surface, output); + (void *)surface, (void *)output); _log_dmabuf_tranche(drm_master->comp, scanout_tranche, drm_master->main_drm->dev->path); @@ -3635,7 +3635,7 @@ bool backend_build_surface_feedback(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Failed to create fallback tranche for surface feedback of " "surface %p, output %p", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3648,7 +3648,7 @@ bool backend_build_surface_feedback(struct vt_backend_t *backend, VT_TRACE(drm_master->comp->log, "Surface feedback for surface=%p output=%p: composite tranche", - surface, output); + (void *)surface, (void *)output); _log_dmabuf_tranche(drm_master->comp, fallback_tranche, drm_master->main_drm->dev->path); @@ -3714,7 +3714,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Cannot build surface feedback for surface %p, output %p; CRTC or" "primary plane is not available", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3728,7 +3728,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Failed to create direct-scanout tranche for surface feedback of " "surface %p, output %p", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3738,7 +3738,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, if (!_drm_output_scanout_formats(drm_output->drm_backend, drm_output, &scanout_formats)) { VT_ERROR(drm_master->comp->log, - "Failed to collect scanout formats for output %p", output); + "Failed to collect scanout formats for output %p", (void *)output); return false; } @@ -3753,7 +3753,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, VT_TRACE(drm_master->comp->log, "Surface feedback for surface=%p output=%p: direct scanout tranche", - surface, output); + (void *)surface, (void *)output); _log_dmabuf_tranche(drm_master->comp, scanout_tranche, drm_master->main_drm->dev->path); @@ -3765,7 +3765,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, VT_ERROR(drm_master->comp->log, "Failed to create fallback tranche for surface feedback of " "surface %p, output %p", - surface, output); + (void *)surface, (void *)output); return false; } @@ -3778,7 +3778,7 @@ bool backend_build_surface_feedback_drm(struct vt_backend_t *backend, VT_TRACE(drm_master->comp->log, "Surface feedback for surface=%p output=%p: composite tranche", - surface, output); + (void *)surface, (void *)output); _log_dmabuf_tranche(drm_master->comp, fallback_tranche, drm_master->main_drm->dev->path); diff --git a/src/core/buffer.c b/src/core/buffer.c index 7372b65..33ab10c 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -65,7 +65,7 @@ static void _buffer_destroy(struct vt_buffer_t *buf) { return; } - VT_TRACE(buf->comp->log, "Destroying buffer %p", buf); + VT_TRACE(buf->comp->log, "Destroying buffer %p", (void *)buf); struct vt_buffer_attachment_t *attachment, *tmp; @@ -73,7 +73,7 @@ static void _buffer_destroy(struct vt_buffer_t *buf) { _buffer_remove_attachment(attachment); } - VT_TRACE(buf->comp->log, "Destroyed buffer %p", buf); + VT_TRACE(buf->comp->log, "Destroyed buffer %p", (void *)buf); free(buf); } @@ -113,7 +113,7 @@ struct vt_buffer_t *vt_buffer_ref(struct vt_buffer_t *buf) { buf->refcount++; - VT_TRACE(buf->comp->log, "Referenced buffer: buf=%p refs=%u", buf, + VT_TRACE(buf->comp->log, "Referenced buffer: buf=%p refs=%u", (void *)buf, buf->refcount); return buf; @@ -130,7 +130,7 @@ void vt_buffer_unref(struct vt_buffer_t **buf_ptr) { assert(buf->refcount > 0); - VT_TRACE(buf->comp->log, "Unreferenced buffer: buf=%p refs=%u", buf, + VT_TRACE(buf->comp->log, "Unreferenced buffer: buf=%p refs=%u", (void *)buf, buf->refcount); if (--buf->refcount != 0) @@ -162,7 +162,8 @@ void vt_buffer_begin_use(struct vt_buffer_t *buf) { buf->uses++; - VT_TRACE(buf->comp->log, "Began buffer use: buf=%p uses=%u", buf, buf->uses); + VT_TRACE(buf->comp->log, "Began buffer use: buf=%p uses=%u", (void *)buf, + buf->uses); } void vt_buffer_end_use(struct vt_buffer_t *buf) { @@ -173,7 +174,8 @@ void vt_buffer_end_use(struct vt_buffer_t *buf) { assert(buf->uses > 0); - VT_TRACE(buf->comp->log, "Ending buffer use: buf=%p uses=%u", buf, buf->uses); + VT_TRACE(buf->comp->log, "Ending buffer use: buf=%p uses=%u", (void *)buf, + buf->uses); if (--buf->uses != 0) return; @@ -258,7 +260,7 @@ vt_buffer_release_ref(struct vt_buffer_release_t *release) { release->refcount++; VT_TRACE(release->comp->log, "Referenced buffer release: release=%p refs=%u", - release, release->refcount); + (void *)release, release->refcount); return release; } @@ -275,13 +277,13 @@ void vt_buffer_release_unref(struct vt_buffer_release_t **release_ptr) { assert(release->refcount > 0); VT_TRACE(release->comp->log, - "Unreferenced buffer release: release=%p refs=%u", release, + "Unreferenced buffer release: release=%p refs=%u", (void *)release, release->refcount); if (--release->refcount != 0) return; - VT_TRACE(release->comp->log, "Destroying buffer release %p", release); + VT_TRACE(release->comp->log, "Destroying buffer release %p", (void *)release); assert(release->impl->destroy); release->impl->destroy(release); @@ -320,7 +322,7 @@ struct vt_buffer_use_t *vt_buffer_use_ref(struct vt_buffer_use_t *use) { use->refcount++; - VT_TRACE(use->comp->log, "Referenced buffer use: use=%p refs=%u", use, + VT_TRACE(use->comp->log, "Referenced buffer use: use=%p refs=%u", (void *)use, use->refcount); return use; @@ -333,8 +335,8 @@ static void _buffer_use_destroy(struct vt_buffer_use_t *use) { } VT_TRACE(use->comp->log, - "Buffer use destroy: use=%p release=%p acquire_fence_fd=%i", use, - use->release, use->acquire_fence_fd); + "Buffer use destroy: use=%p release=%p acquire_fence_fd=%i", + (void *)use, (void *)use->release, use->acquire_fence_fd); if (use->release) vt_buffer_release_finish(use->release, use->release_fence_fd); @@ -413,7 +415,7 @@ struct vt_buffer_use_t *vt_buffer_use_create_take( use->release_fence_fd = -1; VT_TRACE(use->comp->log, "Buffer use create: use=%p taking buf=%p release=%p", - use, use->buf, use->release); + (void *)use, (void *)use->buf, (void *)use->release); return use; } @@ -435,8 +437,8 @@ bool vt_buffer_use_set_release_fence_fd(struct vt_buffer_use_t *use, int fd) { use->release_fence_fd = owned_fd; - VT_TRACE(use->comp->log, "Set buffer use=%p release_fence_fd to %i", use, - use->release_fence_fd); + VT_TRACE(use->comp->log, "Set buffer use=%p release_fence_fd to %i", + (void *)use, use->release_fence_fd); return true; } diff --git a/src/core/compositor.c b/src/core/compositor.c index b4c4434..f91f95a 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -130,7 +130,8 @@ void _vt_comp_commit_handler(void *data) { return; } - VT_TRACE(c->log, "Pending repaint on output %p got satisfied.", output); + VT_TRACE(c->log, "Pending repaint on output %p got satisfied.", + (void *)output); } if (output->cursor_dirty) { @@ -160,7 +161,7 @@ void vt_comp_frame_done(struct vt_compositor_t *c, struct vt_output_t *output, free(entry); } - VT_TRACE(c->log, "Sent frame callbacks for output %p.", output); + VT_TRACE(c->log, "Sent frame callbacks for output %p.", (void *)output); } void vt_comp_frame_done_all(struct vt_compositor_t *c, uint32_t t) { @@ -525,7 +526,8 @@ void _vt_comp_wl_region_handle_resource_destroy(struct wl_resource *resource) { if (!r) return; - VT_TRACE(r->comp->log, "region.destroy_resoure: destroying region %p", r); + VT_TRACE(r->comp->log, "region.destroy_resoure: destroying region %p", + (void *)r); pixman_region32_fini(&r->region); free(r); } @@ -566,7 +568,8 @@ void _vt_comp_wl_surface_create_region(struct wl_client *client, wl_resource_set_implementation(res, ®ion_impl, region, _vt_comp_wl_region_handle_resource_destroy); - VT_TRACE(comp->log, "compositor.create_region: created region %p", region); + VT_TRACE(comp->log, "compositor.create_region: created region %p", + (void *)region); } void _vt_comp_wl_region_destroy(struct wl_client *client, @@ -979,7 +982,7 @@ void vt_comp_schedule_repaint(struct vt_compositor_t *c, _vt_comp_schedule_output_commit(c, output); if (output->needs_repaint != old) - VT_TRACE(c->log, "Scheduling repaint on output %p.", output); + VT_TRACE(c->log, "Scheduling repaint on output %p.", (void *)output); } void vt_comp_schedule_cursor_commit(struct vt_compositor_t *c, @@ -993,7 +996,7 @@ void vt_comp_schedule_cursor_commit(struct vt_compositor_t *c, _vt_comp_schedule_output_commit(c, output); if (output->cursor_dirty != old) - VT_TRACE(c->log, "Scheduling cursor commit on output %p.", output); + VT_TRACE(c->log, "Scheduling cursor commit on output %p.", (void *)output); } void vt_comp_repaint_scene(struct vt_compositor_t *c, diff --git a/src/core/content_update.c b/src/core/content_update.c index 7620cd9..7e638e3 100644 --- a/src/core/content_update.c +++ b/src/core/content_update.c @@ -329,7 +329,7 @@ vt_content_update_create(struct vt_surface_t *surf, VT_TRACE(surf->comp->log, "Creating content update for surface %p with pending buffer=%p, " "buffer_release=%p, buffer_attached=%s", - surf, state->buf, state->buffer_release, + (void *)surf, (void *)state->buf, (void *)state->buffer_release, state->buffer_attached ? "true" : "false"); vt_surface_pending_state_move(&update->state, state); @@ -365,7 +365,7 @@ bool vt_content_update_finish_create(struct vt_content_update_t *cu) { "Finished creating content update cu=%p for " "surface=%p with " "buffer use=%p", - cu, cu->surf, cu->buffer_use); + (void *)cu, (void *)cu->surf, (void *)cu->buffer_use); return true; } @@ -427,41 +427,43 @@ bool vt_content_update_apply_dag(struct vt_content_update_t *root) { struct vt_compositor_t *comp = root->surf->comp; - VT_TRACE(comp->log, "Content update apply DAG: root=%p type=%d", root, + VT_TRACE(comp->log, "Content update apply DAG: root=%p type=%d", (void *)root, root->type); if (!_content_update_is_candidate(root)) { VT_TRACE(comp->log, "Content update apply DAG: root=%p NOT CANDIDATE", - root); + (void *)root); return false; } - VT_TRACE(comp->log, "Content update apply DAG: root=%p is candidate", root); + VT_TRACE(comp->log, "Content update apply DAG: root=%p is candidate", + (void *)root); if (!_content_update_is_ready(root)) { - VT_TRACE(comp->log, "Content update apply DAG: root=%p not ready", root); + VT_TRACE(comp->log, "Content update apply DAG: root=%p not ready", + (void *)root); return false; } - VT_TRACE(comp->log, "Content update DAG root=%p is ready", root); + VT_TRACE(comp->log, "Content update DAG root=%p is ready", (void *)root); if (!_content_update_prepare_dag_recursive(root)) { VT_WARN(comp->log, "Content update apply DAG: root=%p prepare failed", - root); + (void *)root); return false; } - VT_TRACE(comp->log, "Content update DAG: root=%p prepared", root); + VT_TRACE(comp->log, "Content update DAG: root=%p prepared", (void *)root); if (!_content_update_apply_dag_recursive(root)) { log_fatal(comp->log, "Content update DAG: root=%p apply failed with valid prepare", - root); + (void *)root); return false; } VT_TRACE(comp->log, "Content update apply DAG: root=%p applied, retiring", - root); + (void *)root); _content_update_retire_applied_dag(root); diff --git a/src/core/scene.c b/src/core/scene.c index 119af4f..47aea3f 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -101,7 +101,8 @@ static bool _scene_node_on_output(struct vt_scene_node_t *node, struct vt_box_t *box = vt_scene_node_get_global_bounds(node); if (!box) { VT_TRACE(output->backend->comp->log, - "SCENE: node_on_output: node=%p has no global bounds", node); + "SCENE: node_on_output: node=%p has no global bounds", + (void *)node); return false; } @@ -216,7 +217,7 @@ static void _scene_render_layers(struct vt_scene_t *scene, VT_TRACE( renderer->comp->log, "Surface=%p accepted as direct-scanout layer; Will not be rendered.", - layer->surface); + (void *)layer->surface); continue; } @@ -353,8 +354,8 @@ struct vt_scene_node_t *vt_scene_node_create(struct vt_compositor_t *c, surf->scene_node = n; VT_TRACE(c->log, - "Created scene node %p for surface %p at (%d,%d), geom_dirty=%d", n, - surf, n->x, n->y, n->geom_dirty); + "Created scene node %p for surface %p at (%d,%d), geom_dirty=%d", + (void *)n, (void *)surf, n->x, n->y, n->geom_dirty); return n; } @@ -371,11 +372,12 @@ bool vt_scene_node_damage_whole(struct vt_compositor_t *comp, struct vt_scene_node_t *node) { if (!node || !comp) return false; - VT_TRACE(comp->log, "DEBUG: node %p damaged; repainting all outputs", node); + VT_TRACE(comp->log, "DEBUG: node %p damaged; repainting all outputs", + (void *)node); struct vt_output_t *it; wl_list_for_each(it, &comp->outputs, link_global) { - VT_TRACE(comp->log, "DEBUG: scheduling output %p", it); + VT_TRACE(comp->log, "DEBUG: scheduling output %p", (void *)it); vt_comp_schedule_repaint(comp, it); } @@ -599,7 +601,7 @@ struct vt_output_t *vt_scene_node_primary_output(struct vt_compositor_t *comp, VT_ERROR(comp->log, "Failed to pick primary output for scene node %p. Node has " "invalid global bounds", - node); + (void *)node); return NULL; } @@ -635,7 +637,7 @@ struct vt_output_t *vt_scene_node_primary_output(struct vt_compositor_t *comp, VT_ERROR(comp->log, "Failed to pick primary output for scene node %p. [x: %i, y: %i, " "w: %u, h: %u]", - node, rect->x, rect->y, rect->width, rect->height); + (void *)node, rect->x, rect->y, rect->width, rect->height); } return best; } diff --git a/src/core/surface.c b/src/core/surface.c index a00985b..53a546d 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -463,7 +463,7 @@ void vt_surface_frame_done(struct vt_surface_t *surf, wl_callback_send_done(cb->res, frame_time_msec); VT_TRACE(surf->comp->log, "Sent wl_callback done for surf=%p callback=%p", - surf, cb); + (void *)surf, (void *)cb); wl_resource_destroy(cb->res); } diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 0954d78..5478c52 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -295,7 +295,7 @@ static void _wl_seat_pointer_set_cursor(struct wl_client *client, wl_resource_post_error(resource, WL_POINTER_ERROR_ROLE, "wl_surface already has another role"); VT_ERROR(surf->comp->log, "Failed to set cursor role for surface %p", - surf); + (void *)surf); return; } } diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index 1dd123f..2b8680e 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -307,7 +307,8 @@ void _proto_linux_dmabuf_v1_bind(struct wl_client *client, void *data, VT_TRACE(_proto->comp->log, "linux_dmabuf.bind: sending legacy formats for client %p (%zu " "formats).", - client, vt_drm_format_array_count(&_proto->default_formats)); + (void *)client, + vt_drm_format_array_count(&_proto->default_formats)); } } @@ -408,7 +409,7 @@ void _linux_dmabuf_v1_create_params(struct wl_client *client, VT_TRACE(_proto->comp->log, "linux_dmabuf.create_params: created buffer params resource %p.", - params->res); + (void *)params->res); } void _linux_dmabuf_v1_get_default_feedback(struct wl_client *client, @@ -437,7 +438,7 @@ void _linux_dmabuf_v1_get_default_feedback(struct wl_client *client, VT_TRACE( _proto->comp->log, "linux_dmabuf.get_default_feedback: sent default feedback to client %p.", - client); + (void *)client); } void _linux_dmabuf_v1_get_surface_feedback(struct wl_client *client, @@ -485,7 +486,7 @@ void _linux_dmabuf_v1_get_surface_feedback(struct wl_client *client, VT_WARN(_proto->comp->log, "Failed to update DMABUF feedback for surface %p; using default " "feedback.", - surf); + (void *)surf); } /* 6. Track resource in DMA-BUF surface list */ @@ -499,7 +500,7 @@ void _linux_dmabuf_v1_get_surface_feedback(struct wl_client *client, VT_TRACE(_proto->comp->log, "linux_dmabuf.get_surface_feedback: sent feedback to surface %p " "(version %i).", - surface, wl_resource_get_version(surface)); + (void *)surface, wl_resource_get_version(surface)); } void _linux_dmabuf_v1_params_destroy(struct wl_client *client, @@ -781,7 +782,7 @@ void _linux_dmabuf_params_create(struct wl_resource *resource, uint32_t buf_id, VT_TRACE(_proto->comp->log, "linux_dmabuf.params_create: successfully created wl_buffer %p " "(%ix%i, format=%u).", - buf->res, width, height, format); + (void *)buf->res, width, height, format); } void _linux_dmabuf_v1_params_create(struct wl_client *client, @@ -882,7 +883,7 @@ static bool _accumulate_tranche_formats(struct vt_compositor_t *comp, vt_drm_format_fini(&new_fmt); VT_ERROR(comp->log, "Failed to push format %p (n mods: %zu) into format array.", - fmt, vt_drm_format_mod_count(fmt)); + (void *)fmt, vt_drm_format_mod_count(fmt)); return false; } @@ -1410,7 +1411,7 @@ _linux_dmabuf_surface_from_surf(struct vt_surface_t *surf) { VT_TRACE(_proto->comp->log, "linux_dmabuf.surface_from_surf: created dmabuf surface %p for %p.", - dmabuf_surf, surf); + (void *)dmabuf_surf, (void *)surf); return dmabuf_surf; } @@ -1533,7 +1534,7 @@ static bool _dmabuf_buffer_get_dmabuf(struct vt_buffer_t *buf, VT_ERROR(buf->comp->log, "Requested dmabuf backing of buffer %p but cannot find dmabuf " "attachment", - buf) + (void *)buf) return false; } @@ -1624,15 +1625,15 @@ vt_proto_linux_dmabuf_v1_from_buffer_res(struct wl_resource *res) { &_dmabuf_wl_buffer_impl) || wl_resource_get_user_data(res) == NULL) { VT_TRACE(_proto->comp->log, - "linux_dmabuf.from_buffer_res: invalid resource %p.", res); + "linux_dmabuf.from_buffer_res: invalid resource %p.", (void *)res); return NULL; } /* 2. Retrieve internal DMABUF buffer handle */ struct vt_linux_dmabuf_v1_buffer_t *buf = wl_resource_get_user_data(res); VT_TRACE(_proto->comp->log, - "linux_dmabuf.from_buffer_res: resolved wl_buffer %p to %p.", res, - buf); + "linux_dmabuf.from_buffer_res: resolved wl_buffer %p to %p.", + (void *)res, (void *)buf); return buf; } @@ -1678,7 +1679,7 @@ bool vt_proto_linux_dmabuf_v1_update_surface_feedback( VT_WARN(surf->comp->log, "Failed to build cached DMABUF feedback for output %p; using " "default feedback.", - primary_output); + (void *)primary_output); } generation = cache->generation; diff --git a/src/protocols/linux_explicit_sync.c b/src/protocols/linux_explicit_sync.c index da91969..333522c 100644 --- a/src/protocols/linux_explicit_sync.c +++ b/src/protocols/linux_explicit_sync.c @@ -117,7 +117,7 @@ static bool _linux_explicit_sync_addon_commit(struct vt_surface_t *surf, VT_TRACE( surf->comp->log, "Moved acquire_fence_fd=%i owned by explicit sync to content update %p", - cu->acquire_fence_fd, cu); + cu->acquire_fence_fd, (void *)cu); return true; } @@ -155,7 +155,7 @@ _linux_explicit_sync_v1_from_surf(struct vt_surface_t *surf) { VT_TRACE(_proto.comp->log, "Created linux-explicit-synchronization-v1 surface state %p for %p.", - state, surf); + (void *)state, (void *)surf); return state; } @@ -207,7 +207,7 @@ void _linux_explicit_sync_v1_get_synchronization( VT_TRACE(surf->comp->log, "linux_explicit_sync.get_synchronization: created synchronization " "object for surface %p.", - surf); + (void *)surf); } void _linux_explicit_sync_v1_destroy(struct wl_client *client, @@ -274,7 +274,7 @@ void _linux_surface_sync_v1_set_acquire_fence(struct wl_client *client, VT_TRACE(state->surf->comp->log, "linux_surface_sync.set_acquire_fence: set acquire fence FD=%i for " "surface %p.", - fd, state->surf); + fd, (void *)state->surf); } static struct vt_wayland_buffer_release_t * diff --git a/src/protocols/wl_surface.c b/src/protocols/wl_surface.c index 3649a69..3a88d99 100644 --- a/src/protocols/wl_surface.c +++ b/src/protocols/wl_surface.c @@ -159,7 +159,8 @@ void _wl_surface_attach(struct wl_client *client, struct wl_resource *resource, new_buf = vt_buffer_ref(new_buf); VT_TRACE(surf->comp->log, "attach: buffer_res=%p id=%u wrapper=%p refs=%u", - buffer, wl_resource_get_id(buffer), new_buf, new_buf->refcount); + (void *)buffer, wl_resource_get_id(buffer), (void *)new_buf, + new_buf->refcount); if (!_wayland_buffer_attachment_get_or_create(new_buf, buffer)) { VT_WL_OUT_OF_MEMORY(surf->comp, client); @@ -191,7 +192,8 @@ void _wl_surface_commit(struct wl_client *client, return; } - VT_TRACE(surf->comp->log, "Got wl_surface.commit for surface %p", surf) + VT_TRACE(surf->comp->log, "Got wl_surface.commit for surface %p", + (void *)surf) if (!vt_surface_validate_commit(surf)) { VT_ERROR(surf->comp->log, "wl_surface.commit: Commit validation failed"); @@ -253,7 +255,8 @@ void _wl_surface_frame(struct wl_client *client, struct wl_resource *resource, wl_list_insert(surf->pending.frame_callbacks.prev, &cb->link); VT_TRACE(surf->comp->log, - "wl_surface.frame: Queued callback %p for surface %p.", cb, surf); + "wl_surface.frame: Queued callback %p for surface %p.", (void *)cb, + (void *)surf); } void _wl_surface_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { @@ -274,7 +277,7 @@ void _wl_surface_damage(struct wl_client *client, struct wl_resource *resource, VT_TRACE(surf->comp->log, "wl_surface.damage: Accumulated damage [x: %i, y: %i, w: %i, " "h: %i] into pending surface damage of surface %p", - x, y, width, height, surf); + x, y, width, height, (void *)surf); } void _wl_surface_damage_buffer(struct wl_client *client, @@ -296,7 +299,7 @@ void _wl_surface_damage_buffer(struct wl_client *client, VT_TRACE(surf->comp->log, "wl_surface.damage_buffer: Accumulated damage [x: %i, y: %i, w: %i, " "h: %i] into pending buffer damage of surface %p", - x, y, width, height, surf); + x, y, width, height, (void *)surf); } void _wl_surface_set_opaque_region(struct wl_client *client, @@ -327,7 +330,7 @@ void _wl_surface_set_opaque_region(struct wl_client *client, VT_TRACE(surf->comp->log, "wl_surface.set_opaque_region: updated pending opaque region for " "surface %p", - surf); + (void *)surf); } void _wl_surface_set_input_region(struct wl_client *client, @@ -359,7 +362,7 @@ void _wl_surface_set_input_region(struct wl_client *client, VT_TRACE(surf->comp->log, "wl_surface.set_input_region: updated pending input region for " "surface %p", - surf); + (void *)surf); } void _wl_surface_set_buffer_transform(struct wl_client *client, @@ -376,7 +379,7 @@ void _wl_surface_set_buffer_transform(struct wl_client *client, transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, "invalid transform %d", transform); - VT_WARN(surf->comp->log, "%p", surf); + VT_WARN(surf->comp->log, "%p", (void *)surf); return; } @@ -387,7 +390,7 @@ void _wl_surface_set_buffer_transform(struct wl_client *client, VT_TRACE(surf->comp->log, "wl_surface.set_buffer_transform: Set pending transform=%d for " "surface %p", - transform, surf); + transform, (void *)surf); } void _wl_surface_set_buffer_scale(struct wl_client *client, @@ -415,7 +418,7 @@ void _wl_surface_set_buffer_scale(struct wl_client *client, VT_TRACE(surf->comp->log, "wl_surface.set_buffer_scale: Set pending scale=%d for surface %p", - scale, surf); + scale, (void *)surf); } void _wl_surface_offset(struct wl_client *client, struct wl_resource *resource, @@ -432,7 +435,7 @@ void _wl_surface_offset(struct wl_client *client, struct wl_resource *resource, VT_TRACE(surf->comp->log, "wl_surface.offset: Set pending offset=[%d, %d] for surface %p", x, - y, surf); + y, (void *)surf); } void _wl_surface_destroy(struct wl_client *client, diff --git a/src/protocols/xdg_shell.c b/src/protocols/xdg_shell.c index 106e7ca..7a6eccb 100644 --- a/src/protocols/xdg_shell.c +++ b/src/protocols/xdg_shell.c @@ -334,7 +334,7 @@ void _xdg_wm_base_create_positioner(struct wl_client *client, VT_TRACE( pos->comp->log, "xdg_wm_base.create_positioner: created positioner with resource %p.", - res); + (void *)res); } void _xdg_wm_base_positioner_handle_resource_destroy( @@ -349,7 +349,7 @@ void _xdg_wm_base_positioner_handle_resource_destroy( VT_TRACE( pos->comp->log, "xdg_positioner.resource_destroy: destroyed positioner with resource %p.", - resource); + (void *)resource); /* 1. Unlink pointers within internal handle */ pos->res = NULL; @@ -601,7 +601,7 @@ void _xdg_wm_base_get_xdg_surface(struct wl_client *client, _xdg_surface_handle_resource_destroy); VT_TRACE(surf->comp->log, "xdg_wm_base.get_xdg_surface with resource %p.", - res); + (void *)res); } void _xdg_wm_base_pong(struct wl_client *client, struct wl_resource *resource, @@ -633,7 +633,7 @@ void _xdg_positioner_set_size(struct wl_client *client, VT_WARN(pos->comp->log, "xdg_positioner.set_size: Trying to set invalid width (%i) or " "height (%i) for positioner %p.", - width, height, pos); + width, height, (void *)pos); return; } @@ -643,7 +643,7 @@ void _xdg_positioner_set_size(struct wl_client *client, VT_TRACE(_proto.comp->log, "xdg_positioner.set_size: Size %ix%i for positioner %p.", width, - height, pos); + height, (void *)pos); } void _xdg_positioner_set_anchor_rect(struct wl_client *client, @@ -668,7 +668,7 @@ void _xdg_positioner_set_anchor_rect(struct wl_client *client, VT_WARN(pos->comp->log, "xdg_positioner.set_anchor: Trying to set invalid width (%i) or " "height (%i) for positioner %p.", - width, height, pos); + width, height, (void *)pos); return; } @@ -681,7 +681,7 @@ void _xdg_positioner_set_anchor_rect(struct wl_client *client, VT_TRACE(pos->comp->log, "xdg_positioner.set_anchor_rect: Pos %ix%i, size %ix%i for " "positioner %p.", - x, y, width, height, pos); + x, y, width, height, (void *)pos); } void _xdg_positioner_set_anchor(struct wl_client *client, @@ -703,7 +703,7 @@ void _xdg_positioner_set_anchor(struct wl_client *client, VT_WARN(pos->comp->log, "xdg_positioner.set_anchor: Trying to set invalid anchor %i for " "positioner %p.", - anchor, pos); + anchor, (void *)pos); return; } @@ -712,8 +712,8 @@ void _xdg_positioner_set_anchor(struct wl_client *client, pos->anchor = anchor; VT_TRACE(pos->comp->log, - "xdg_positioner.set_anchor: Set anchor of positioner %p to %i.", pos, - anchor); + "xdg_positioner.set_anchor: Set anchor of positioner %p to %i.", + (void *)pos, anchor); } void _xdg_positioner_set_gravity(struct wl_client *client, @@ -736,7 +736,7 @@ void _xdg_positioner_set_gravity(struct wl_client *client, VT_WARN(pos->comp->log, "xdg_positioner.set_gravity: Trying to set invalid gravity %i for " "positioner %p.", - gravity, pos); + gravity, (void *)pos); return; } @@ -746,7 +746,7 @@ void _xdg_positioner_set_gravity(struct wl_client *client, VT_TRACE(pos->comp->log, "xdg_positioner.set_gravity: Set gravity of positioner %p to %i.", - pos, gravity); + (void *)pos, gravity); } void _xdg_positioner_set_constraint_adjustment(struct wl_client *client, @@ -770,7 +770,7 @@ void _xdg_positioner_set_constraint_adjustment(struct wl_client *client, VT_WARN(pos->comp->log, "xdg_positioner.set_constraint_adjustment: Trying to set invalid " "constraint adjustment %i for positioner %p.", - constraint_adjustment, pos); + constraint_adjustment, (void *)pos); return; } @@ -781,7 +781,7 @@ void _xdg_positioner_set_constraint_adjustment(struct wl_client *client, VT_TRACE(pos->comp->log, "xdg_positioner.set_constraint_adjustment: Set constraint " "adjustment of positioner %p to %i.", - pos, constraint_adjustment); + (void *)pos, constraint_adjustment); } void _xdg_positioner_set_offset(struct wl_client *client, @@ -801,7 +801,7 @@ void _xdg_positioner_set_offset(struct wl_client *client, VT_TRACE(pos->comp->log, "xdg_positioner.set_offset: Set offset of positioner %p to %ix%i.", - pos, x, y); + (void *)pos, x, y); } void _xdg_surface_destroy(struct wl_client *client, @@ -837,7 +837,7 @@ void _xdg_surface_get_toplevel(struct wl_client *client, "xdg_surface already has a role"); VT_WARN(_proto.comp->log, "XDG surface %p already has another role.", - xdg_surf); + (void *)xdg_surf); return; } @@ -922,7 +922,7 @@ void _xdg_surface_get_popup(struct wl_client *client, "xdg_surface already has a role"); VT_WARN(_proto.comp->log, "XDG surface %p already has another role.", - popup_xdg_surf); + (void *)popup_xdg_surf); return; } @@ -1067,7 +1067,7 @@ void _xdg_surface_set_window_geometry(struct wl_client *client, VT_WARN(xdg_surf->surf->comp->log, "xdg_positioner.set_size: Trying to set invalid width (%i) or " "height (%i) for XDG surface %p.", - width, height, xdg_surf); + width, height, (void *)xdg_surf); return; } @@ -1081,7 +1081,7 @@ void _xdg_surface_set_window_geometry(struct wl_client *client, VT_TRACE(xdg_surf->surf->comp->log, "xdg_surface.set_window_geometry: Set window window geometry of " "surface %p to (%ix%i, %ix%i).", - xdg_surf->surf, x, y, width, height); + (void *)xdg_surf->surf, x, y, width, height); } void _xdg_toplevel_destroy(struct wl_client *client, @@ -1123,7 +1123,7 @@ void _xdg_toplevel_set_parent(struct wl_client *client, VT_TRACE(xdg_toplevel->xdg_surf->surf->comp->log, "xdg_toplevel.set_parent: Set parent of toplevel %p to %p.", - xdg_toplevel, parent); + (void *)xdg_toplevel, (void *)parent); } void _xdg_toplevel_set_title(struct wl_client *client, diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index cde6e58..48550f1 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -638,7 +638,8 @@ bool _egl_gl_create_output_fbo(struct vt_output_t *output, uint32_t w, if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { VT_ERROR(output->backend->comp->log, - "FBO creation for output %p (%u%u) failed.\n", output, w, h); + "FBO creation for output %p (%u%u) failed.\n", (void *)output, w, + h); glBindFramebuffer(GL_FRAMEBUFFER, 0); return false; } @@ -743,7 +744,7 @@ static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, if (egl_buf->tex.id) glDeleteTextures(1, &egl_buf->tex.id); - VT_TRACE(r->comp->log, "Deleted OpenGL texture for buffer %p", buf); + VT_TRACE(r->comp->log, "Deleted OpenGL texture for buffer %p", (void *)buf); if (egl_buf->egl_img != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR_ptr(egl->egl_dsp, (EGLImageKHR)egl_buf->egl_img); @@ -754,7 +755,8 @@ static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, egl_buf->tex.width = 0; egl_buf->tex.height = 0; - VT_TRACE(r->comp->log, "Destroyed EGL Image handle for buffer %p", buf); + VT_TRACE(r->comp->log, "Destroyed EGL Image handle for buffer %p", + (void *)buf); } static bool _egl_track_rendered_buffer_use(struct vt_output_t *output, @@ -1176,7 +1178,7 @@ bool renderer_setup_renderable_output_egl(struct vt_renderer_t *r, vt_comp_schedule_repaint(r->comp, output); VT_TRACE(r->comp->log, "Created EGL render surface %p for output %p (%ux%u)", - egl_surf, output, output->width, output->height); + egl_surf, (void *)output, output->width, output->height); return true; } @@ -1293,7 +1295,8 @@ static bool _egl_import_buffer_by_deduced_type(struct vt_renderer_t *r, return _egl_gl_import_buffer_shm(r, &shm_attr, buf, egl_buf, damage); } - VT_WARN(r->comp->log, "Unknown buffer import type for buffer %p", buf); + VT_WARN(r->comp->log, "Unknown buffer import type for buffer %p", + (void *)buf); return false; } @@ -1305,7 +1308,7 @@ bool renderer_import_buffer_egl(struct vt_renderer_t *r, struct egl_backend_state_t *egl = BACKEND_DATA(r, struct egl_backend_state_t); - VT_TRACE(r->comp->log, "Importing buffer to handle %p", buf); + VT_TRACE(r->comp->log, "Importing buffer to handle %p", (void *)buf); struct vt_egl_buffer_t *egl_buf = _egl_get_or_create_egl_buffer(r, buf); @@ -1453,7 +1456,7 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, if (!surface->current_buf_use) { VT_TRACE(r->comp->log, "Not rendering surface %p, no active buffer use on surface.", - surface); + (void *)surface); return; } @@ -1473,7 +1476,7 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, if (!renderer_import_buffer_egl(r, use->buf, &surface->applied.damage)) { VT_ERROR(r->comp->log, "Failed to import buffer %p for surface %p.", - use->buf, surface); + (void *)use->buf, (void *)surface); return; } @@ -1512,12 +1515,12 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, if (!vt_output_track_presented_surface(output, surface)) { VT_ERROR(r->comp->log, "Failed to track presented surface %p on output %p.", - surface, output); + (void *)surface, (void *)output); } if (!_egl_track_rendered_buffer_use(output, use)) { VT_ERROR(r->comp->log, "Failed to track EGL buffer use %p (buffer: %p).", - use, buf); + (void *)use, (void *)buf); } surface->damaged = false; @@ -1526,8 +1529,9 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, "Presented surface %p " "src=[x:%d y:%d w:%u h:%u] " "dst=[x:%d y:%d w:%u h:%u].", - surface, src_box->x, src_box->y, src_box->width, src_box->height, - dst_box->x, dst_box->y, dst_box->width, dst_box->height); + (void *)surface, src_box->x, src_box->y, src_box->width, + src_box->height, dst_box->x, dst_box->y, dst_box->width, + dst_box->height); } void renderer_draw_surface_simple_egl(struct vt_renderer_t *r, @@ -1613,21 +1617,21 @@ void renderer_end_frame_egl(struct vt_renderer_t *r, struct vt_output_t *output, GLenum gl_err = glGetError(); if (gl_err != GL_NO_ERROR) { VT_ERROR(r->comp->log, - "glBlitFramebuffer failed for output %p: GL error 0x%x", output, - gl_err); + "glBlitFramebuffer failed for output %p: GL error 0x%x", + (void *)output, gl_err); return; } if (!_egl_record_surface_release_fences(r, output)) { VT_ERROR(r->comp->log, "Cannot record surface release fences after render for output %p.", - output); + (void *)output); } if (!eglSwapBuffers(egl->egl_dsp, output->render_surface)) { VT_ERROR(r->comp->log, - "eglSwapBuffers failed for output %p: EGL error 0x%x", output, - eglGetError()); + "eglSwapBuffers failed for output %p: EGL error 0x%x", + (void *)output, eglGetError()); return; } From 29bc2ce844740de186c9cb86ed3f29fa0cebbe22 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:16 +0200 Subject: [PATCH 25/57] chore: mark unused callback parameters --- src/backends/drm/session_drm.c | 6 ++++++ src/backends/wayland/wayland.c | 6 ++++++ src/core/compositor.c | 3 +++ src/core/scene.c | 1 + src/input/backends/libinput/libinput.c | 3 +++ src/input/backends/wayland/wayland_input.c | 5 +++++ src/input/wl_seat.c | 5 +++++ src/protocols/linux_dmabuf.c | 7 +++++++ src/protocols/linux_explicit_sync.c | 2 ++ src/protocols/wl_data_device.c | 5 +++++ src/protocols/wl_output.c | 1 + src/protocols/wl_subcompositor.c | 2 ++ src/protocols/wl_surface.c | 9 +++++++++ src/protocols/xdg_shell.c | 21 +++++++++++++++++++++ src/render/gl/egl_gl.c | 5 +++++ 15 files changed, 81 insertions(+) diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 6ba33bc..7ed3cad 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -113,6 +113,7 @@ void _vt_session_seat_log_func(enum libseat_log_level level, const char *format, } void _vt_session_seat_enable(struct libseat *seat, void *userdata) { + (void)seat; if (!userdata) return; struct vt_session_t *session = (struct vt_session_t *)userdata; @@ -146,6 +147,8 @@ void _vt_session_seat_disable(struct libseat *seat, void *userdata) { } int32_t _vt_session_seat_dispatch(int fd, uint32_t mask, void *data) { + (void)fd; + (void)mask; if (!data) return 0; struct vt_session_t *session = data; @@ -224,6 +227,8 @@ _vt_session_drm_open_device_if_kms(struct vt_session_t *session, } int32_t _vt_session_drm_udev_dispatch(int fd, uint32_t mask, void *data) { + (void)fd; + (void)mask; if (!data) return 1; struct vt_session_t *session = data; @@ -539,6 +544,7 @@ struct vt_device_t *vt_session_device_from_fd_drm(struct vt_session_t *session, } static void on_found_gpu(struct wl_listener *listener, void *data) { + (void)data; struct wait_for_gpu_handler_t *handler = wl_container_of(listener, handler, listener); handler->found = true; diff --git a/src/backends/wayland/wayland.c b/src/backends/wayland/wayland.c index 08cb1d1..3fbe5b0 100644 --- a/src/backends/wayland/wayland.c +++ b/src/backends/wayland/wayland.c @@ -201,6 +201,7 @@ static const struct wl_output_listener output_listener = { static void output_sync_callback(void *data, struct wl_callback *callback, uint32_t unused) { + (void)unused; parent_physical_output_t *output = data; assert(output->sync_cb == callback); @@ -292,6 +293,8 @@ static void _output_handle_render_resize(struct vt_output_t *output, int32_t w, void _wl_parent_xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, struct wl_array *states) { + (void)toplevel; + (void)states; struct vt_output_t *output = data; if (!output || !output->user_data) return; @@ -548,6 +551,7 @@ static void _wl_parent_output_mode(void *data, struct wl_output *wl_output_proxy, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)wl_output_proxy; parent_physical_output_t *output = data; struct vt_output_mode_t *mode = @@ -560,6 +564,7 @@ static void _wl_parent_output_mode(void *data, bool _wl_backend_create_output(struct vt_backend_t *backend, struct vt_output_t *output, void *data) { + (void)data; if (!backend || !output) return false; @@ -969,6 +974,7 @@ bool backend_terminate_wl(struct vt_backend_t *backend) { } bool backend_prepare_output_frame_wl(struct vt_backend_t *backend, struct vt_output_t *output) { + (void)backend; wayland_output_state_t *wl_output = BACKEND_DATA(output, wayland_output_state_t); diff --git a/src/core/compositor.c b/src/core/compositor.c index f91f95a..0a90003 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -574,12 +574,14 @@ void _vt_comp_wl_surface_create_region(struct wl_client *client, void _vt_comp_wl_region_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } void _vt_comp_wl_region_add(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; struct vt_region_t *r = resource ? wl_resource_get_user_data(resource) : NULL; if (!r) return; @@ -591,6 +593,7 @@ void _vt_comp_wl_region_add(struct wl_client *client, void _vt_comp_wl_region_subtract(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; struct vt_region_t *r = resource ? wl_resource_get_user_data(resource) : NULL; if (!r) return; diff --git a/src/core/scene.c b/src/core/scene.c index 47aea3f..ff78992 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -362,6 +362,7 @@ struct vt_scene_node_t *vt_scene_node_create(struct vt_compositor_t *c, bool vt_scene_node_destroy(struct vt_compositor_t *c, struct vt_scene_node_t *node) { + (void)c; if (node->parent) { return vt_scene_node_remove_child(node->parent, node); } diff --git a/src/input/backends/libinput/libinput.c b/src/input/backends/libinput/libinput.c index 41d700f..adcd46b 100644 --- a/src/input/backends/libinput/libinput.c +++ b/src/input/backends/libinput/libinput.c @@ -74,6 +74,7 @@ static const struct libinput_interface input_interface = { int32_t _vt_li_open_restricted(const char *path, int32_t flags, void *user_data) { + (void)flags; struct vt_session_t *session = (struct vt_session_t *)user_data; struct vt_device_t *dev = VT_ALLOC(session->comp, sizeof(*dev)); @@ -101,6 +102,8 @@ void _vt_li_close_restricted(int32_t fd, void *user_data) { } int32_t _vt_li_input_dispatch(int fd, uint32_t mask, void *data) { + (void)fd; + (void)mask; struct vt_input_backend_li_t *li = (struct vt_input_backend_li_t *)data; libinput_dispatch(li->input); diff --git a/src/input/backends/wayland/wayland_input.c b/src/input/backends/wayland/wayland_input.c index 8c7c66a..15911b3 100644 --- a/src/input/backends/wayland/wayland_input.c +++ b/src/input/backends/wayland/wayland_input.c @@ -111,6 +111,7 @@ static void _wl_pointer_axis_relative_direction(void *data, void _wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int fd, uint32_t size) { + (void)wl_keyboard; struct vt_input_backend_wl_t *wl = data; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) return; @@ -175,6 +176,8 @@ void _wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, void _wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + (void)wl_keyboard; + (void)serial; struct vt_input_backend_wl_t *wl = data; uint32_t key_xkb = key + 8; @@ -189,6 +192,8 @@ void _wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + (void)wl_keyboard; + (void)serial; struct vt_input_backend_wl_t *wl = data; struct vt_input_backend_t *ibackend = wl->comp->input_backend; if (!ibackend->kb_state) diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 5478c52..9095aa3 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -105,6 +105,7 @@ _wl_kb_get_mod_states(struct xkb_state *state); static void _wl_seat_release(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } @@ -204,6 +205,7 @@ void _wl_handle_keybind_exit(struct vt_compositor_t *comp, void *user_data) { vt_comp_terminate(comp); } void _wl_handle_keybind_term(struct vt_compositor_t *comp, void *user_data) { + (void)user_data; char buf[128]; // env scoped to the child: our own WAYLAND_DISPLAY may point at a parent snprintf(buf, sizeof(buf), @@ -273,6 +275,7 @@ static void _wl_seat_pointer_set_cursor(struct wl_client *client, uint32_t serial, struct wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) { + (void)serial; struct vt_pointer_t *ptr = wl_resource_get_user_data(resource); if (!ptr || !ptr->seat) @@ -356,11 +359,13 @@ void _wl_seat_get_keyboard(struct wl_client *client, void _wl_seat_release_kb(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } void _wl_seat_release_pointer(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index 2b8680e..e0b85e5 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -352,6 +352,7 @@ void _proto_linux_dmabuf_v1_destroy(struct vt_proto_linux_dmabuf_v1_t *dmabuf) { void _proto_linux_dmabuf_v1_handle_dsp_destroy(struct wl_listener *listener, void *data) { + (void)data; /* 1. Retrieve protocol handle from listener */ struct vt_proto_linux_dmabuf_v1_t *dmabuf = wl_container_of(listener, dmabuf, dsp_destroy); @@ -369,6 +370,7 @@ void _proto_linux_dmabuf_v1_handle_dsp_destroy(struct wl_listener *listener, void _linux_dmabuf_v1_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; /* Destroy the linux-dmabuf global resource */ wl_resource_destroy(resource); } @@ -505,6 +507,7 @@ void _linux_dmabuf_v1_get_surface_feedback(struct wl_client *client, void _linux_dmabuf_v1_params_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; /* Destroy the linux_dmabuf buffer params resource */ wl_resource_destroy(resource); } @@ -514,6 +517,7 @@ void _linux_dmabuf_v1_params_add(struct wl_client *client, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) { + (void)client; VT_TRACE(_proto->comp->log, "linux_dmabuf.params_add: request received."); /* 1. Retrieve internal params handle */ @@ -789,6 +793,7 @@ void _linux_dmabuf_v1_params_create(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) { + (void)client; VT_TRACE(_proto->comp->log, "_linux_dmabuf_v1_params_create"); _linux_dmabuf_params_create(resource, 0, width, height, format, flags); } @@ -798,6 +803,7 @@ void _linux_dmabuf_v1_params_create_immed(struct wl_client *client, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { + (void)client; VT_TRACE(_proto->comp->log, "_linux_dmabuf_v1_params_create_immed"); _linux_dmabuf_params_create(resource, buffer_id, width, height, format, flags); @@ -850,6 +856,7 @@ void _linux_dmabuf_v1_feedback_destroy(struct wl_client *client, void _linux_dmabuf_v1_buffer_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; VT_TRACE(_proto->comp->log, "_linux_dmabuf_v1_buffer_destroy()"); wl_resource_destroy(resource); } diff --git a/src/protocols/linux_explicit_sync.c b/src/protocols/linux_explicit_sync.c index 333522c..71a2c23 100644 --- a/src/protocols/linux_explicit_sync.c +++ b/src/protocols/linux_explicit_sync.c @@ -212,6 +212,7 @@ void _linux_explicit_sync_v1_get_synchronization( void _linux_explicit_sync_v1_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; /* Destroy the global explicit sync interface resource */ wl_resource_destroy(resource); } @@ -243,6 +244,7 @@ void _linux_surface_sync_v1_destroy(struct wl_client *client, void _linux_surface_sync_v1_set_acquire_fence(struct wl_client *client, struct wl_resource *resource, int32_t fd) { + (void)client; struct vt_linux_explicit_sync_v1_surface_state_t *state = resource ? wl_resource_get_user_data(resource) : NULL; if (!state) { diff --git a/src/protocols/wl_data_device.c b/src/protocols/wl_data_device.c index 07f3ec5..058a703 100644 --- a/src/protocols/wl_data_device.c +++ b/src/protocols/wl_data_device.c @@ -176,6 +176,7 @@ static void _get_data_device(struct wl_client *client, static void _client_source_accept(struct vt_data_source_t *source, uint32_t serial, const char *mime_type) { + (void)serial; wl_data_source_send_target(source->resource, mime_type); } @@ -191,6 +192,7 @@ static void _client_source_cancel(struct vt_data_source_t *source) { static void _data_source_offer(struct wl_client *client, struct wl_resource *resource, const char *type) { + (void)client; struct vt_data_source_t *source = wl_resource_get_user_data(resource); char **p; @@ -203,12 +205,14 @@ static void _data_source_offer(struct wl_client *client, static void _data_source_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } static void _data_source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions) { + (void)client; struct vt_data_source_t *source = wl_resource_get_user_data(resource); if (source->actions_set) { @@ -239,6 +243,7 @@ static void _data_source_set_actions(struct wl_client *client, static void _manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + (void)data; struct wl_resource *resource; resource = wl_resource_create(client, &wl_data_device_manager_interface, diff --git a/src/protocols/wl_output.c b/src/protocols/wl_output.c index 7070f57..e079185 100644 --- a/src/protocols/wl_output.c +++ b/src/protocols/wl_output.c @@ -27,6 +27,7 @@ static void output_release(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } diff --git a/src/protocols/wl_subcompositor.c b/src/protocols/wl_subcompositor.c index 0028364..a663714 100644 --- a/src/protocols/wl_subcompositor.c +++ b/src/protocols/wl_subcompositor.c @@ -77,6 +77,7 @@ static const struct vt_surface_role_impl_t subsurface_role_impl = { static void _subcompositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + (void)version; struct vt_compositor_t *comp = data; @@ -92,6 +93,7 @@ static void _subcompositor_bind(struct wl_client *client, void *data, static void _subcompositor_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } diff --git a/src/protocols/wl_surface.c b/src/protocols/wl_surface.c index 3a88d99..6d104f5 100644 --- a/src/protocols/wl_surface.c +++ b/src/protocols/wl_surface.c @@ -186,6 +186,7 @@ void _wl_surface_attach(struct wl_client *client, struct wl_resource *resource, void _wl_surface_commit(struct wl_client *client, struct wl_resource *resource) { + (void)client; struct vt_surface_t *surf = wl_resource_get_user_data(resource); if (!surf) { VT_PARAM_CHECK_FAIL(_proto.comp); @@ -260,6 +261,7 @@ void _wl_surface_frame(struct wl_client *client, struct wl_resource *resource, } void _wl_surface_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; struct vt_surface_t *surf = resource ? wl_resource_get_user_data(resource) : NULL; @@ -283,6 +285,7 @@ void _wl_surface_damage(struct wl_client *client, struct wl_resource *resource, void _wl_surface_damage_buffer(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; struct vt_surface_t *surf = resource ? wl_resource_get_user_data(resource) : NULL; @@ -305,6 +308,7 @@ void _wl_surface_damage_buffer(struct wl_client *client, void _wl_surface_set_opaque_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region) { + (void)client; struct vt_surface_t *surf = wl_resource_get_user_data(resource); if (!surf) { VT_PARAM_CHECK_FAIL(_proto.comp); @@ -336,6 +340,7 @@ void _wl_surface_set_opaque_region(struct wl_client *client, void _wl_surface_set_input_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region) { + (void)client; struct vt_surface_t *surf = wl_resource_get_user_data(resource); if (!surf) { VT_PARAM_CHECK_FAIL(_proto.comp); @@ -368,6 +373,7 @@ void _wl_surface_set_input_region(struct wl_client *client, void _wl_surface_set_buffer_transform(struct wl_client *client, struct wl_resource *resource, int32_t transform) { + (void)client; struct vt_surface_t *surf = resource ? wl_resource_get_user_data(resource) : NULL; if (!surf) { @@ -395,6 +401,7 @@ void _wl_surface_set_buffer_transform(struct wl_client *client, void _wl_surface_set_buffer_scale(struct wl_client *client, struct wl_resource *resource, int32_t scale) { + (void)client; /* [0]: Sets buffer scale for HiDPi displays. This needs to be * applied when rendering. */ struct vt_surface_t *surf = @@ -423,6 +430,7 @@ void _wl_surface_set_buffer_scale(struct wl_client *client, void _wl_surface_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { + (void)client; struct vt_surface_t *surf = wl_resource_get_user_data(resource); if (!surf) { VT_PARAM_CHECK_FAIL(_proto.comp); @@ -440,6 +448,7 @@ void _wl_surface_offset(struct wl_client *client, struct wl_resource *resource, void _wl_surface_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; struct vt_surface_t *surf = ((struct vt_surface_t *)wl_resource_get_user_data(resource)); diff --git a/src/protocols/xdg_shell.c b/src/protocols/xdg_shell.c index 7a6eccb..43a04d3 100644 --- a/src/protocols/xdg_shell.c +++ b/src/protocols/xdg_shell.c @@ -295,6 +295,7 @@ void _xdg_wm_base_bind(struct wl_client *client, void *data, uint32_t version, void _xdg_wm_base_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; /* Destroy the XDG WM-base interface resource */ wl_resource_destroy(resource); } @@ -451,6 +452,7 @@ void _xdg_popup_handle_resource_destroy(struct wl_resource *resource) { static bool _xdg_surface_commit(struct vt_surface_t *surf, struct vt_content_update_t *cu) { + (void)cu; struct vt_xdg_surface_t *xdg = surf->role.data; if (!xdg) @@ -609,12 +611,14 @@ void _xdg_wm_base_pong(struct wl_client *client, struct wl_resource *resource, void _xdg_positioner_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } void _xdg_positioner_set_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -649,6 +653,7 @@ void _xdg_positioner_set_size(struct wl_client *client, void _xdg_positioner_set_anchor_rect(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -686,6 +691,7 @@ void _xdg_positioner_set_anchor_rect(struct wl_client *client, void _xdg_positioner_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -719,6 +725,7 @@ void _xdg_positioner_set_anchor(struct wl_client *client, void _xdg_positioner_set_gravity(struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -752,6 +759,7 @@ void _xdg_positioner_set_gravity(struct wl_client *client, void _xdg_positioner_set_constraint_adjustment(struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -787,6 +795,7 @@ void _xdg_positioner_set_constraint_adjustment(struct wl_client *client, void _xdg_positioner_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { + (void)client; /* 1. Retrieve internal positioner handle */ struct vt_xdg_positioner_t *pos = resource ? wl_resource_get_user_data(resource) : NULL; @@ -806,6 +815,7 @@ void _xdg_positioner_set_offset(struct wl_client *client, void _xdg_surface_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } @@ -1024,6 +1034,7 @@ void _xdg_surface_get_popup(struct wl_client *client, void _xdg_surface_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { + (void)client; struct vt_xdg_surface_t *xdg = wl_resource_get_user_data(resource); if (!xdg) @@ -1048,6 +1059,7 @@ void _xdg_surface_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + (void)client; /* 1. Retrieve internal XDG-Surface handle */ struct vt_xdg_surface_t *xdg_surf = resource ? wl_resource_get_user_data(resource) : NULL; @@ -1086,11 +1098,13 @@ void _xdg_surface_set_window_geometry(struct wl_client *client, void _xdg_toplevel_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } void _xdg_toplevel_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { + (void)client; /* 1. Retrieve internal XDG-Toplevel handle */ struct vt_xdg_toplevel_t *xdg_toplevel = resource ? wl_resource_get_user_data(resource) : NULL; @@ -1128,6 +1142,7 @@ void _xdg_toplevel_set_parent(struct wl_client *client, void _xdg_toplevel_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { + (void)client; /* Deallocate the old App Title and strdup() the requested ID */ struct vt_xdg_toplevel_t *top = wl_resource_get_user_data(resource); if (top->title) @@ -1138,6 +1153,7 @@ void _xdg_toplevel_set_title(struct wl_client *client, void _xdg_toplevel_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { + (void)client; struct vt_xdg_toplevel_t *top = wl_resource_get_user_data(resource); /* Deallocate the old App ID and strdup() the requested ID */ if (top->app_id) @@ -1168,6 +1184,8 @@ void _xdg_toplevel_set_min_size(struct wl_client *client, void _xdg_toplevel_set_maximized(struct wl_client *client, struct wl_resource *resource) { + (void)client; + (void)resource; // optional: ignore } @@ -1233,11 +1251,13 @@ void _xdg_toplevel_set_minimized(struct wl_client *client, void _xdg_popup_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; wl_resource_destroy(resource); } void _xdg_popup_grab(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial) { + (void)client; /* 1. Retrieve internal XDG-Popup handle */ struct vt_xdg_popup_t *popup = resource ? wl_resource_get_user_data(resource) : NULL; @@ -1553,6 +1573,7 @@ static bool _popup_resolve_pos(struct vt_xdg_popup_t *popup, void _xdg_popup_reposition(struct wl_client *client, struct wl_resource *resource, struct wl_resource *positioner, uint32_t token) { + (void)client; struct vt_xdg_popup_t *popup = resource ? wl_resource_get_user_data(resource) : NULL; diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index 48550f1..3a0e503 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -183,6 +183,7 @@ bool _egl_gl_import_buffer_shm(struct vt_renderer_t *r, struct vt_shm_attr_t *a, struct vt_buffer_t *buf, struct vt_egl_buffer_t *egl_buf, const pixman_region32_t *damage) { + (void)damage; if (!buf || !a || !r || !egl_buf || !a->data) return false; @@ -251,6 +252,7 @@ bool _egl_gl_import_buffer_dmabuf(struct vt_renderer_t *r, struct vt_dmabuf_attr_t *a, struct vt_buffer_t *buf, struct vt_egl_buffer_t *egl_buf) { + (void)buf; struct egl_backend_state_t *egl = BACKEND_DATA(r, struct egl_backend_state_t); @@ -1347,6 +1349,8 @@ void renderer_set_vsync_egl(struct vt_renderer_t *r, bool vsync) { void renderer_set_clear_color_egl(struct vt_renderer_t *r, struct vt_output_t *output, uint32_t col) { + (void)r; + (void)output; vec4s zto = rn_color_to_zto(rn_color_from_hex(col)); glClearColor(zto.r, zto.g, zto.b, zto.a); glClear(GL_COLOR_BUFFER_BIT); @@ -1575,6 +1579,7 @@ void renderer_draw_rect_egl(struct vt_renderer_t *r, float x, float y, float w, void renderer_end_scene_egl(struct vt_renderer_t *r, struct vt_output_t *output) { + (void)output; if (!r || !r->impl.end_scene || !r->user_data) { VT_ERROR(r->comp->log, "Renderer backend not initialized before ending frame."); From f4cf1b71ad1b8ac04c2e170b4efae7aff00a5e78 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:28 +0200 Subject: [PATCH 26/57] chore(input): replace xkb mod mask macro with inline helper --- src/input/backends/libinput/libinput.c | 16 +++++----------- src/input/backends/wayland/wayland_input.c | 17 ++++++----------- src/input/input.h | 6 ++++++ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/input/backends/libinput/libinput.c b/src/input/backends/libinput/libinput.c index adcd46b..862ae36 100644 --- a/src/input/backends/libinput/libinput.c +++ b/src/input/backends/libinput/libinput.c @@ -38,12 +38,6 @@ #define _SUBSYS_NAME "INPUT/LIBINPUT" -#define _VT_XKB_GET_MOD_MASK(keymap, name) \ - ({ \ - int idx = xkb_keymap_mod_get_index((keymap), (name)); \ - idx == XKB_MOD_INVALID ? 0 : (1u << idx); \ - }) - struct vt_input_backend_li_t { struct libinput *input; struct vt_compositor_t *comp; @@ -314,15 +308,15 @@ bool _vt_li_init_xkb(struct vt_input_backend_li_t *li) { } li->comp->input_backend->mods.shift = - _VT_XKB_GET_MOD_MASK(backend->keymap, XKB_MOD_NAME_SHIFT); + vt_input_xkb_mod_mask(backend->keymap, XKB_MOD_NAME_SHIFT); li->comp->input_backend->mods.ctrl = - _VT_XKB_GET_MOD_MASK(backend->keymap, XKB_MOD_NAME_CTRL); + vt_input_xkb_mod_mask(backend->keymap, XKB_MOD_NAME_CTRL); li->comp->input_backend->mods.alt = - _VT_XKB_GET_MOD_MASK(backend->keymap, XKB_MOD_NAME_ALT); + vt_input_xkb_mod_mask(backend->keymap, XKB_MOD_NAME_ALT); li->comp->input_backend->mods.super = - _VT_XKB_GET_MOD_MASK(backend->keymap, XKB_MOD_NAME_LOGO); + vt_input_xkb_mod_mask(backend->keymap, XKB_MOD_NAME_LOGO); li->comp->input_backend->mods.caps = - _VT_XKB_GET_MOD_MASK(backend->keymap, XKB_MOD_NAME_CAPS); + vt_input_xkb_mod_mask(backend->keymap, XKB_MOD_NAME_CAPS); return true; } diff --git a/src/input/backends/wayland/wayland_input.c b/src/input/backends/wayland/wayland_input.c index 15911b3..95c14d8 100644 --- a/src/input/backends/wayland/wayland_input.c +++ b/src/input/backends/wayland/wayland_input.c @@ -34,12 +34,6 @@ #define _SUBSYS_NAME "INPUT/WL" -#define _VT_XKB_GET_MOD_MASK(keymap, name) \ - ({ \ - int idx = xkb_keymap_mod_get_index((keymap), (name)); \ - idx == XKB_MOD_INVALID ? 0 : (1u << idx); \ - }) - struct vt_input_backend_wl_t { struct wl_seat *wl_seat; struct wl_keyboard *wl_keyboard; @@ -145,14 +139,15 @@ void _wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, ibackend->kb_state = state; ibackend->mods.shift = - _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_SHIFT); + vt_input_xkb_mod_mask(ibackend->keymap, XKB_MOD_NAME_SHIFT); ibackend->mods.ctrl = - _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_CTRL); - ibackend->mods.alt = _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_ALT); + vt_input_xkb_mod_mask(ibackend->keymap, XKB_MOD_NAME_CTRL); + ibackend->mods.alt = + vt_input_xkb_mod_mask(ibackend->keymap, XKB_MOD_NAME_ALT); ibackend->mods.super = - _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_LOGO); + vt_input_xkb_mod_mask(ibackend->keymap, XKB_MOD_NAME_LOGO); ibackend->mods.caps = - _VT_XKB_GET_MOD_MASK(ibackend->keymap, XKB_MOD_NAME_CAPS); + vt_input_xkb_mod_mask(ibackend->keymap, XKB_MOD_NAME_CAPS); } void _wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, diff --git a/src/input/input.h b/src/input/input.h index 16049bf..a62be05 100644 --- a/src/input/input.h +++ b/src/input/input.h @@ -27,6 +27,12 @@ #include "../core/core_types.h" +static inline uint32_t vt_input_xkb_mod_mask(struct xkb_keymap *keymap, + const char *name) { + xkb_mod_index_t idx = xkb_keymap_mod_get_index(keymap, name); + return idx == XKB_MOD_INVALID ? 0 : (1u << idx); +} + enum vt_input_backend_platform_t { VT_INPUT_LIBINPUT = 0, VT_INPUT_WAYLAND, From 2a12363a726320eaf7479a273f40f57c54c286ac Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:36 +0200 Subject: [PATCH 27/57] chore: remove dead code and unused locals --- src/backends/drm/drm_backend.c | 12 -------- src/backends/drm/session_drm.c | 2 -- src/backends/wayland/wayland.c | 9 ------ src/core/compositor.c | 32 ---------------------- src/core/content_update.c | 2 -- src/core/scene.c | 2 -- src/core/surface.c | 8 ------ src/input/backends/wayland/wayland_input.c | 2 -- src/input/wl_seat.c | 2 -- src/protocols/xdg_shell.c | 2 -- src/render/gl/egl_gl.c | 12 -------- src/render/gl/egl_gl.h | 3 -- 12 files changed, 88 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 35e0e9a..fd0be4e 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -3125,18 +3125,6 @@ bool backend_is_dmabuf_importable_drm(struct vt_backend_t *backend, return drm_prime_test_import(master->main_drm->drm_fd, attr); } -static struct vt_drm_plane_t * -_drm_plane_from_id(struct vt_drm_backend_state_t *drm, uint32_t plane_id) { - struct vt_drm_plane_t *plane; - - wl_array_for_each(plane, &drm->planes) { - if (plane->id == plane_id) - return plane; - } - - return NULL; -} - static bool _drm_output_can_scanout_dmabuf(struct vt_drm_backend_state_t *drm, struct vt_drm_output_state_t *output, diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 7ed3cad..68e702d 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -486,8 +486,6 @@ bool vt_session_unmanage_device_drm(struct vt_session_t *session, struct vt_device_t *dev) { if (!dev || !session || !session->user_data) return false; - struct vt_session_drm_t *session_drm = - BACKEND_DATA(session, struct vt_session_drm_t); wl_list_remove(&dev->link); return true; } diff --git a/src/backends/wayland/wayland.c b/src/backends/wayland/wayland.c index 3fbe5b0..800a413 100644 --- a/src/backends/wayland/wayland.c +++ b/src/backends/wayland/wayland.c @@ -495,7 +495,6 @@ static void _wl_parent_output_geometry( const char *make, const char *model, int32_t transform) { parent_physical_output_t *output = data; - static int32_t x_ptr = 0; output->x = x; output->y = y; @@ -656,9 +655,6 @@ bool _wl_backend_destroy_output(struct vt_backend_t *backend, if (!output || !output->user_data) return false; - wayland_backend_state_t *wl_backend = - BACKEND_DATA(backend, wayland_backend_state_t); - wayland_output_state_t *wl_output = BACKEND_DATA(output, wayland_output_state_t); if (!wl_output) @@ -843,8 +839,6 @@ bool backend_init_wl(struct vt_backend_t *backend) { free(shm_formats); - const uint8_t dmabuf_ver = 4, dmabuf_explicit_sync_ver = 2; - if (backend->comp->have_proto_dmabuf) { // initialize the dmabuf protocol with default feedback struct vt_dmabuf_feedback_t *default_feedback = @@ -940,9 +934,6 @@ bool backend_handle_frame_wl(struct vt_backend_t *backend, wayland_backend_state_t *wl = BACKEND_DATA(backend, wayland_backend_state_t); - wayland_output_state_t *wl_output = - BACKEND_DATA(output, wayland_output_state_t); - if (!_wl_parent_flush(wl)) return false; diff --git a/src/core/compositor.c b/src/core/compositor.c index 0a90003..1691429 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -616,38 +616,6 @@ static void _vt_comp_schedule_output_commit(struct vt_compositor_t *comp, wl_event_loop_add_idle(comp->wl.evloop, _vt_comp_commit_handler, output); } -static void _wl_region_add(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y, - int32_t width, int32_t height) { - struct vt_region_t *r = resource ? wl_resource_get_user_data(resource) : NULL; - if (!r) - return; - if (width <= 0 || height <= 0) - return; - pixman_region32_union_rect(&r->region, &r->region, x, y, width, height); -} - -static void _wl_region_subtract(struct wl_client *client, - struct wl_resource *resource, int32_t x, - int32_t y, int32_t width, int32_t height) { - struct vt_region_t *r = resource ? wl_resource_get_user_data(resource) : NULL; - if (!r) - return; - if (width <= 0 || height <= 0) - return; - pixman_region32_t rect; - pixman_region32_init_rect(&rect, x, y, width, height); - pixman_region32_subtract(&r->region, &r->region, &rect); - pixman_region32_fini(&rect); -} - -static void _wl_region_handle_destroy(struct wl_resource *resource) { - struct vt_region_t *r = resource ? wl_resource_get_user_data(resource) : NULL; - if (!r) - return; - pixman_region32_fini(&r->region); - free(r); -} bool _vt_comp_wl_init(struct vt_compositor_t *c) { if (!c) return false; diff --git a/src/core/content_update.c b/src/core/content_update.c index 7e638e3..312a4ce 100644 --- a/src/core/content_update.c +++ b/src/core/content_update.c @@ -71,8 +71,6 @@ static void _content_update_dependency_destroy( static bool _content_update_prepare_buffer(struct vt_content_update_t *cu) { assert(cu && cu->surf && cu->surf->comp && cu->surf->comp->renderer); - struct vt_renderer_t *r = cu->surf->comp->renderer; - if (!cu->state.buffer_attached) return true; diff --git a/src/core/scene.c b/src/core/scene.c index ff78992..2ec3f2d 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -33,8 +33,6 @@ #define _SUBSYS_NAME "SCENE" -static void sceneprintindent(int indent); - static struct vt_output_layer_state_t * _scene_push_layer(struct vt_scene_t *scene); diff --git a/src/core/surface.c b/src/core/surface.c index 53a546d..579e457 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -35,14 +35,6 @@ #define _SUBSYS_NAME "SURFACE" -static void _surface_update_mapping(struct vt_surface_t *surf); - -static void _surface_update_mapping(struct vt_surface_t *surf) { - bool mapped = surf->current_buf_use != NULL; - - vt_surface_set_mapped(surf, mapped); -} - bool vt_surface_init(struct vt_surface_t *surf) { if (!surf) return false; diff --git a/src/input/backends/wayland/wayland_input.c b/src/input/backends/wayland/wayland_input.c index 95c14d8..fafe14c 100644 --- a/src/input/backends/wayland/wayland_input.c +++ b/src/input/backends/wayland/wayland_input.c @@ -20,7 +20,6 @@ * SOFTWARE. */ -#pragma once #include "src/core/output.h" #include "src/input/input.h" #include "src/input/wl_seat.h" @@ -402,7 +401,6 @@ bool input_backend_init_wl(struct vt_input_backend_t *backend, bool input_backend_terminate_wl(struct vt_input_backend_t *backend) { if (!backend || !backend->user_data) return false; - struct vt_input_backend_wl_t *wl = backend->user_data; VT_TRACE(backend->comp->log, "Terminated Wayland nested backend."); return true; } diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 9095aa3..7093333 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -1178,8 +1178,6 @@ void vt_seat_set_pointer_focus(struct vt_seat_t *seat, } if (surf && surf->res && new_client) { - uint32_t serial = wl_display_next_serial(seat->comp->wl.dsp); - struct vt_pointer_t *ptr; wl_list_for_each(ptr, &seat->pointers, link) { if (!ptr->res) diff --git a/src/protocols/xdg_shell.c b/src/protocols/xdg_shell.c index 43a04d3..1afe8bf 100644 --- a/src/protocols/xdg_shell.c +++ b/src/protocols/xdg_shell.c @@ -1233,8 +1233,6 @@ static void _xdg_toplevel_set_fullscreen(struct wl_client *client, wl_array_release(&states); - uint32_t serial = wl_display_next_serial(wl_client_get_display(client)); - top->fullscreen = true; top->fullscreen_output = output; diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index 3a0e503..fc29626 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -418,9 +418,6 @@ bool _egl_pick_config_from_format(struct vt_compositor_t *c, bool _egl_pick_config(struct vt_compositor_t *comp, struct egl_backend_state_t *egl, struct vt_backend_t *backend) { - EGLint attribs[32]; - int i = 0; - switch (backend->platform) { case VT_BACKEND_DRM_GBM: { // DRM/GBM backend: use DRM fourcc to pick config @@ -1318,9 +1315,6 @@ bool renderer_import_buffer_egl(struct vt_renderer_t *r, egl->has_dmabuf_support); } -bool renderer_destroy_buffer_texture_egl(struct vt_renderer_t *r, - struct vt_buffer_t *buf) {} - bool renderer_drop_context_egl(struct vt_renderer_t *r) { if (!r || !r->impl.drop_context || !r->user_data) { VT_ERROR(r->comp->log, @@ -1361,9 +1355,6 @@ void renderer_stencil_damage_pass_egl(struct vt_renderer_t *r, if (!r || !output || !output->user_data_render) return; - struct egl_output_state_t *egl_output = - (struct egl_output_state_t *)output->user_data_render; - glEnable(GL_STENCIL_TEST); glStencilMask(0xFF); @@ -1381,9 +1372,6 @@ void renderer_composite_pass_egl(struct vt_renderer_t *r, if (!r || !output || !output->user_data_render) return; - struct egl_output_state_t *egl_output = - (struct egl_output_state_t *)output->user_data_render; - // ========== PASS 2: SCENE ========== glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilMask(0x00); diff --git a/src/render/gl/egl_gl.h b/src/render/gl/egl_gl.h index dfb311b..6747536 100644 --- a/src/render/gl/egl_gl.h +++ b/src/render/gl/egl_gl.h @@ -62,9 +62,6 @@ bool renderer_import_buffer_egl(struct vt_renderer_t *r, struct vt_buffer_t *buf, const pixman_region32_t *damage); -bool renderer_destroy_buffer_texture_egl(struct vt_renderer_t *r, - struct vt_buffer_t *buf); - bool renderer_drop_context_egl(struct vt_renderer_t *r); void renderer_set_vsync_egl(struct vt_renderer_t *r, bool vsync); From a518f8a93881cbc0a5fd41ca7b99151740e1945b Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:36 +0200 Subject: [PATCH 28/57] chore: const-correct attachment and release callbacks --- src/core/buffer.c | 2 +- src/core/buffer.h | 6 +++--- src/core/surface.c | 2 +- src/core/surface.h | 4 ++-- src/input/root_cursor.c | 4 ++-- src/protocols/linux_dmabuf.c | 4 ++-- src/protocols/wl_shm.c | 8 ++++---- src/protocols/wl_surface.c | 8 ++++---- src/render/drm_format.c | 2 +- src/render/drm_format.h | 3 ++- src/render/gl/egl_gl.c | 8 ++++---- 11 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/core/buffer.c b/src/core/buffer.c index 33ab10c..e1fb68b 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -240,7 +240,7 @@ struct vt_buffer_attachment_t *vt_buffer_find_attachment( void vt_buffer_release_init_and_ref( struct vt_buffer_release_t *release, struct vt_compositor_t *comp, - struct vt_buffer_release_implementation_t *impl) { + const struct vt_buffer_release_implementation_t *impl) { if (!release) return; diff --git a/src/core/buffer.h b/src/core/buffer.h index bef1e53..12394fc 100644 --- a/src/core/buffer.h +++ b/src/core/buffer.h @@ -44,8 +44,8 @@ struct vt_buffer_release_implementation_t { }; struct vt_buffer_attachment_implementation_t { - void (*destroy)(struct vt_buffer_t *buf, void *owner, void *data); - void (*end_use)(struct vt_buffer_t *buf, void *owner, void *data); + void (*destroy)(struct vt_buffer_t *buf, const void *owner, void *data); + void (*end_use)(struct vt_buffer_t *buf, const void *owner, void *data); }; struct vt_buffer_attachment_t { @@ -121,7 +121,7 @@ struct vt_buffer_attachment_t *vt_buffer_find_attachment( void vt_buffer_release_init_and_ref( struct vt_buffer_release_t *release, struct vt_compositor_t *comp, - struct vt_buffer_release_implementation_t *impl); + const struct vt_buffer_release_implementation_t *impl); struct vt_buffer_release_t * vt_buffer_release_ref(struct vt_buffer_release_t *release); diff --git a/src/core/surface.c b/src/core/surface.c index 579e457..326639f 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -536,7 +536,7 @@ bool vt_surface_has_role(struct vt_surface_t *surf, return surf->role.impl && surf->role.impl->type == type; } -struct vt_buffer_t *vt_surface_get_buffer(struct vt_surface_t *surf) { +struct vt_buffer_t *vt_surface_get_buffer(const struct vt_surface_t *surf) { if (!surf) return NULL; return surf->current_buf_use ? surf->current_buf_use->buf : NULL; diff --git a/src/core/surface.h b/src/core/surface.h index fa5eda5..7c8f80a 100644 --- a/src/core/surface.h +++ b/src/core/surface.h @@ -120,7 +120,7 @@ struct vt_surface_role_impl_t { }; struct vt_surface_role_t { - struct vt_surface_role_impl_t *impl; + const struct vt_surface_role_impl_t *impl; void *data; }; @@ -215,4 +215,4 @@ bool vt_surface_set_role(struct vt_surface_t *surf, bool vt_surface_has_role(struct vt_surface_t *surf, enum vt_surface_role_type_t type); -struct vt_buffer_t *vt_surface_get_buffer(struct vt_surface_t *surf); +struct vt_buffer_t *vt_surface_get_buffer(const struct vt_surface_t *surf); diff --git a/src/input/root_cursor.c b/src/input/root_cursor.c index 6fd6f50..7715ee5 100644 --- a/src/input/root_cursor.c +++ b/src/input/root_cursor.c @@ -33,8 +33,8 @@ #define _SUBSYS_NAME "CURSOR" -static void _root_cursor_pixels_destroy(struct vt_buffer_t *buf, void *owner, - void *data) { +static void _root_cursor_pixels_destroy(struct vt_buffer_t *buf, + const void *owner, void *data) { (void)buf; (void)owner; XcursorImageDestroy(data); diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index e0b85e5..15aa949 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -208,7 +208,7 @@ static bool _dmabuf_buffer_get_dmabuf(struct vt_buffer_t *buf, struct vt_dmabuf_attr_t *o_attr); static void _dmabuf_buffer_attachment_destroy(struct vt_buffer_t *buf, - void *owner, void *data); + const void *owner, void *data); static struct vt_proto_linux_dmabuf_v1_t *_proto; @@ -1557,7 +1557,7 @@ static bool _dmabuf_buffer_get_dmabuf(struct vt_buffer_t *buf, } static void _dmabuf_buffer_attachment_destroy(struct vt_buffer_t *buf, - void *owner, void *data) { + const void *owner, void *data) { (void)buf; (void)owner; if (!data) { diff --git a/src/protocols/wl_shm.c b/src/protocols/wl_shm.c index 46b31ee..8ffdd5f 100644 --- a/src/protocols/wl_shm.c +++ b/src/protocols/wl_shm.c @@ -107,8 +107,8 @@ static void _wl_shm_handle_display_destroy(struct wl_listener *listener, static void _wl_shm_buffer_destroy(struct wl_client *client, struct wl_resource *resource); -static void _shm_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, - void *data); +static void _shm_buffer_attachment_destroy(struct vt_buffer_t *buf, + const void *owner, void *data); bool _wl_shm_buffer_get_attr(struct vt_buffer_t *buf, struct vt_shm_attr_t *o_attr); @@ -483,8 +483,8 @@ static void _wl_shm_buffer_handle_res_destroy(struct wl_resource *resource) { free(shm); } -static void _shm_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, - void *data) { +static void _shm_buffer_attachment_destroy(struct vt_buffer_t *buf, + const void *owner, void *data) { (void)buf; (void)owner; diff --git a/src/protocols/wl_surface.c b/src/protocols/wl_surface.c index 6d104f5..1479148 100644 --- a/src/protocols/wl_surface.c +++ b/src/protocols/wl_surface.c @@ -88,9 +88,9 @@ _wayland_buffer_attachment_get_or_create(struct vt_buffer_t *buf, struct wl_resource *res); static void _wayland_buffer_attachment_destroy(struct vt_buffer_t *buf, - void *owner, void *data); + const void *owner, void *data); static void _wayland_buffer_attachment_end_use(struct vt_buffer_t *buf, - void *owner, void *data); + const void *owner, void *data); static void _wayland_buffer_handle_res_destroy(struct wl_listener *listener, void *data); @@ -557,7 +557,7 @@ _wayland_buffer_attachment_get_or_create(struct vt_buffer_t *buf, } static void _wayland_buffer_attachment_destroy(struct vt_buffer_t *buf, - void *owner, void *data) { + const void *owner, void *data) { (void)buf; (void)owner; @@ -569,7 +569,7 @@ static void _wayland_buffer_attachment_destroy(struct vt_buffer_t *buf, } static void _wayland_buffer_attachment_end_use(struct vt_buffer_t *buf, - void *owner, void *data) { + const void *owner, void *data) { (void)buf; (void)owner; diff --git a/src/render/drm_format.c b/src/render/drm_format.c index 051b2e0..34e5b2c 100644 --- a/src/render/drm_format.c +++ b/src/render/drm_format.c @@ -71,7 +71,7 @@ void vt_drm_format_fini(struct vt_drm_format_t *fmt) { } bool vt_drm_format_add_mods(struct vt_drm_format_t *fmt, - struct wl_array *mods) { + const struct wl_array *mods) { assert(fmt && mods); struct vt_drm_format_modifier_t *it; diff --git a/src/render/drm_format.h b/src/render/drm_format.h index ba59b82..0c2c1e1 100644 --- a/src/render/drm_format.h +++ b/src/render/drm_format.h @@ -46,7 +46,8 @@ vt_drm_format_add_mod(struct vt_drm_format_t *fmt, uint64_t mod); void vt_drm_format_fini(struct vt_drm_format_t *fmt); -bool vt_drm_format_add_mods(struct vt_drm_format_t *fmt, struct wl_array *mods); +bool vt_drm_format_add_mods(struct vt_drm_format_t *fmt, + const struct wl_array *mods); size_t vt_drm_format_mod_count(const struct vt_drm_format_t *fmt); diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index fc29626..eb4721c 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -133,8 +133,8 @@ static bool _egl_create_renderer(struct vt_renderer_t *renderer, enum vt_backend_platform_t platform, void *native_handle, bool log_error); -static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, - void *data); +static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, + const void *owner, void *data); static bool _egl_track_rendered_buffer_use(struct vt_output_t *output, struct vt_buffer_use_t *use); @@ -718,8 +718,8 @@ bool _egl_create_renderer(struct vt_renderer_t *renderer, return true; } -static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, void *owner, - void *data) { +static void _egl_buffer_attachment_destroy(struct vt_buffer_t *buf, + const void *owner, void *data) { (void)owner; (void)buf; From 573010c10af1f45aa4da151cee3d00c6939fc44a Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:44 +0200 Subject: [PATCH 29/57] chore: fix sign-compare and pedantic warnings --- src/backends/drm/drm_backend.c | 15 +++++---- src/backends/drm/session_drm.c | 2 +- src/backends/drm/session_drm.h | 2 +- src/backends/wayland/wayland.c | 7 ++-- src/core/compositor.c | 7 +++- src/core/content_update.c | 2 +- src/core/scene.c | 8 ++--- src/core/session.h | 2 +- src/core/surface.c | 4 +-- src/core/surface.h | 4 +-- src/input/wl_seat.c | 9 +++--- src/protocols/xdg_shell.c | 59 ++++++++++++++++++++++++++++------ src/render/gl/egl_gl.c | 39 +++++++++++++--------- 13 files changed, 108 insertions(+), 52 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index fd0be4e..5feb755 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -1258,7 +1258,7 @@ static bool _drm_init_crtcs_and_planes(struct vt_drm_backend_state_t *drm) { goto fail_resources; } - assert(drm->crtcs.size / sizeof(*crtcs) == res->count_crtcs); + assert(drm->crtcs.size / sizeof(*crtcs) == (size_t)res->count_crtcs); memset(crtcs, 0, crtc_bytes); @@ -2124,7 +2124,8 @@ static bool _drm_import_cursor_image(struct vt_drm_output_state_t *drm_output, uint32_t w = plane->cursor_sizes[i].width; uint32_t h = plane->cursor_sizes[i].height; - if (w < shm.width || h < shm.height) + // shm dims checked positive above + if (w < (uint32_t)shm.width || h < (uint32_t)shm.height) continue; if (bo_width == 0 || (uint64_t)w * h < (uint64_t)bo_width * bo_height) { @@ -2171,7 +2172,7 @@ static bool _drm_import_cursor_image(struct vt_drm_output_state_t *drm_output, switch (shm.format) { case WL_SHM_FORMAT_ARGB8888: - for (uint32_t y = 0; y < shm.height; y++) { + for (int32_t y = 0; y < shm.height; y++) { memcpy(dst + (size_t)y * dst_stride, src + (size_t)y * shm.stride, (size_t)shm.width * 4); } @@ -2179,13 +2180,13 @@ static bool _drm_import_cursor_image(struct vt_drm_output_state_t *drm_output, case WL_SHM_FORMAT_XRGB8888: /* XRGB -> ARGB. Cursor planes require meaningful alpha, so we force * fully opaque alpha */ - for (uint32_t y = 0; y < shm.height; y++) { + for (int32_t y = 0; y < shm.height; y++) { const uint32_t *src_row = (const uint32_t *)(src + (size_t)y * shm.stride); uint32_t *dst_row = (uint32_t *)(dst + (size_t)y * dst_stride); - for (uint32_t x = 0; x < shm.width; x++) + for (int32_t x = 0; x < shm.width; x++) dst_row[x] = src_row[x] | 0xff000000u; } break; @@ -2525,8 +2526,8 @@ static bool _drm_output_sync_refresh(struct vt_drm_output_state_t *drm_output) { struct vt_output_mode_t *mode; wl_list_for_each(mode, &output->physical.modes, link) { mode->flags &= ~WL_OUTPUT_MODE_CURRENT; - if (mode->width == output->width && mode->height == output->height && - mode->refresh == refresh) { + if ((uint32_t)mode->width == output->width && + (uint32_t)mode->height == output->height && mode->refresh == refresh) { mode->flags |= WL_OUTPUT_MODE_CURRENT; } } diff --git a/src/backends/drm/session_drm.c b/src/backends/drm/session_drm.c index 68e702d..b09683e 100644 --- a/src/backends/drm/session_drm.c +++ b/src/backends/drm/session_drm.c @@ -527,7 +527,7 @@ vt_session_get_native_handle_render_node(struct vt_session_t *session, } struct vt_device_t *vt_session_device_from_fd_drm(struct vt_session_t *session, - uint32_t fd) { + int32_t fd) { if (!session || !session->user_data) return NULL; struct vt_session_drm_t *session_drm = diff --git a/src/backends/drm/session_drm.h b/src/backends/drm/session_drm.h index 5d336ec..1c5487b 100644 --- a/src/backends/drm/session_drm.h +++ b/src/backends/drm/session_drm.h @@ -77,7 +77,7 @@ vt_session_get_native_handle_render_node(struct vt_session_t *session, // ====== DRM ONLY ======= // ======================= struct vt_device_t *vt_session_device_from_fd_drm(struct vt_session_t *session, - uint32_t fd); + int32_t fd); uint32_t vt_session_enumerate_cards_drm(struct vt_session_t *session, struct vt_device_t **devs, diff --git a/src/backends/wayland/wayland.c b/src/backends/wayland/wayland.c index 800a413..47913b1 100644 --- a/src/backends/wayland/wayland.c +++ b/src/backends/wayland/wayland.c @@ -196,8 +196,9 @@ static const struct xdg_toplevel_listener parent_toplevel_listener = { .close = _wl_parent_xdg_toplevel_close, }; +// bound at version 1: done/scale/name/description are never sent static const struct wl_output_listener output_listener = { - _wl_parent_output_geometry, _wl_parent_output_mode}; + .geometry = _wl_parent_output_geometry, .mode = _wl_parent_output_mode}; static void output_sync_callback(void *data, struct wl_callback *callback, uint32_t unused) { @@ -304,7 +305,7 @@ void _wl_parent_xdg_toplevel_configure(void *data, return; // Handle resize output in renderer - if (w != output->width || h != output->height) { + if (w != (int32_t)output->width || h != (int32_t)output->height) { _output_handle_render_resize(output, w, h); } } @@ -341,7 +342,7 @@ void _wl_parent_xdg_surface_configure(void *data, struct xdg_surface *surf, int h = output->height > 0 ? output->height : _WL_DEFAULT_OUTPUT_HEIGHT; // Handle resize output in renderer - if (w != output->width || h != output->height) { + if (w != (int32_t)output->width || h != (int32_t)output->height) { _output_handle_render_resize(output, w, h); } } diff --git a/src/core/compositor.c b/src/core/compositor.c index 1691429..eb8d732 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -462,7 +462,9 @@ void _vt_comp_load_backend(struct vt_compositor_t *c, const char *backend_name, VT_ERROR(c->log, "Backend name is too long."); return; } - backend_implement_func_t sym_ptr = dlsym(_vt_comp_dl_handle, sym); + // POSIX-sanctioned way to turn dlsym's void * into a function pointer + backend_implement_func_t sym_ptr; + *(void **)&sym_ptr = dlsym(_vt_comp_dl_handle, sym); if (!sym_ptr) { dlclose(_vt_comp_dl_handle); @@ -815,6 +817,9 @@ bool vt_comp_init(struct vt_compositor_t *c, int argc, char **argv) { case VT_BACKEND_WAYLAND: input_backend = VT_INPUT_WAYLAND; break; + case VT_BACKEND_SURFACELESS: + // headless: no input devices to drive + break; } vt_input_implement(c->input_backend, input_backend); diff --git a/src/core/content_update.c b/src/core/content_update.c index 312a4ce..ac0e9a2 100644 --- a/src/core/content_update.c +++ b/src/core/content_update.c @@ -145,7 +145,7 @@ _content_update_prepare_dag_recursive(struct vt_content_update_t *cu) { /* If a content update fails any part of preperation, it must not be applied. */ { - uint32_t _w, _h; + int32_t _w, _h; if (!vt_surface_compute_final_size(cu->surf, cu->state.buffer_scale, cu->state.buffer_transform, &_w, &_h)) { return false; diff --git a/src/core/scene.c b/src/core/scene.c index 2ec3f2d..6119516 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -48,8 +48,8 @@ static void _scene_render_layers(struct vt_scene_t *scene, struct vt_output_t *output); static void _composite_pass(struct vt_scene_t *scene, struct vt_output_t *output); -static void _scene_node_get_size(struct vt_scene_node_t *node, uint32_t *o_w, - uint32_t *o_h); +static void _scene_node_get_size(struct vt_scene_node_t *node, int32_t *o_w, + int32_t *o_h); static void _scene_node_update_surface_feedback(struct vt_scene_node_t *node); @@ -275,8 +275,8 @@ static void _composite_pass(struct vt_scene_t *scene, r->impl.end_scene(r, output); } -static void _scene_node_get_size(struct vt_scene_node_t *node, uint32_t *o_w, - uint32_t *o_h) { +static void _scene_node_get_size(struct vt_scene_node_t *node, int32_t *o_w, + int32_t *o_h) { if (!node || !o_w || !o_h) return; diff --git a/src/core/session.h b/src/core/session.h index 5fb7297..f348581 100644 --- a/src/core/session.h +++ b/src/core/session.h @@ -51,7 +51,7 @@ struct vt_session_interface_t { const char *(*get_native_handle_render_node)(struct vt_session_t *session, void *handle); struct vt_device_t *(*device_from_fd)(struct vt_session_t *session, - uint32_t fd); + int32_t fd); }; struct vt_session_t { diff --git a/src/core/surface.c b/src/core/surface.c index 326639f..e8fc690 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -463,8 +463,8 @@ void vt_surface_frame_done(struct vt_surface_t *surf, bool vt_surface_compute_final_size(const struct vt_surface_t *surf, uint32_t buffer_scale, - uint32_t buffer_transform, uint32_t *o_w, - uint32_t *o_h) { + uint32_t buffer_transform, int32_t *o_w, + int32_t *o_h) { if (!surf || !o_w || !o_h) return false; diff --git a/src/core/surface.h b/src/core/surface.h index 7c8f80a..74b1432 100644 --- a/src/core/surface.h +++ b/src/core/surface.h @@ -206,8 +206,8 @@ void vt_surface_frame_done(struct vt_surface_t *surf, uint32_t frame_time_msec); bool vt_surface_compute_final_size(const struct vt_surface_t *surf, uint32_t buffer_scale, - uint32_t buffer_transform, uint32_t *o_w, - uint32_t *o_h); + uint32_t buffer_transform, int32_t *o_w, + int32_t *o_h); bool vt_surface_set_role(struct vt_surface_t *surf, const struct vt_surface_role_impl_t *impl, void *data); diff --git a/src/input/wl_seat.c b/src/input/wl_seat.c index 7093333..c07282c 100644 --- a/src/input/wl_seat.c +++ b/src/input/wl_seat.c @@ -491,10 +491,10 @@ bool vt_seat_init(struct vt_seat_t *seat) { if (first) { seat->pointer_x += seat->cursor.root_hotspot_x < (int32_t)first->width ? seat->cursor.root_hotspot_x - : first->width - 1; + : (int32_t)first->width - 1; seat->pointer_y += seat->cursor.root_hotspot_y < (int32_t)first->height ? seat->cursor.root_hotspot_y - : first->height - 1; + : (int32_t)first->height - 1; } /* Outputs already exist when the seat is initialized. */ @@ -901,8 +901,9 @@ void vt_seat_handle_pointer_button(struct vt_seat_t *seat, uint32_t button, uint32_t serial = wl_display_next_serial(seat->comp->wl.dsp); VT_TRACE(seat->comp->log, "BUTTON: ptr_focus=%p picked=%p button=%u", - seat->ptr_focus.surf, - vt_comp_pick_surface(seat->comp, seat->pointer_x, seat->pointer_y), + (void *)seat->ptr_focus.surf, + (void *)(vt_comp_pick_surface(seat->comp, seat->pointer_x, + seat->pointer_y)), button); if (pressed && surf) { diff --git a/src/protocols/xdg_shell.c b/src/protocols/xdg_shell.c index 1afe8bf..8fd1648 100644 --- a/src/protocols/xdg_shell.c +++ b/src/protocols/xdg_shell.c @@ -607,7 +607,11 @@ void _xdg_wm_base_get_xdg_surface(struct wl_client *client, } void _xdg_wm_base_pong(struct wl_client *client, struct wl_resource *resource, - uint32_t serial) {} + uint32_t serial) { + (void)client; + (void)resource; + (void)serial; +} void _xdg_positioner_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -1164,23 +1168,51 @@ void _xdg_toplevel_set_app_id(struct wl_client *client, void _xdg_toplevel_show_window_menu(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, - int32_t x, int32_t y) {} + int32_t x, int32_t y) { + (void)client; + (void)resource; + (void)seat; + (void)serial; + (void)x; + (void)y; +} void _xdg_toplevel_move(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *seat, uint32_t serial) {} + struct wl_resource *seat, uint32_t serial) { + (void)client; + (void)resource; + (void)seat; + (void)serial; +} void _xdg_toplevel_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, - uint32_t edges) {} + uint32_t edges) { + (void)client; + (void)resource; + (void)seat; + (void)serial; + (void)edges; +} void _xdg_toplevel_set_max_size(struct wl_client *client, struct wl_resource *resource, int32_t width, - int32_t height) {} + int32_t height) { + (void)client; + (void)resource; + (void)width; + (void)height; +} void _xdg_toplevel_set_min_size(struct wl_client *client, struct wl_resource *resource, int32_t width, - int32_t height) {} + int32_t height) { + (void)client; + (void)resource; + (void)width; + (void)height; +} void _xdg_toplevel_set_maximized(struct wl_client *client, struct wl_resource *resource) { @@ -1190,7 +1222,10 @@ void _xdg_toplevel_set_maximized(struct wl_client *client, } void _xdg_toplevel_unset_maximized(struct wl_client *client, - struct wl_resource *resource) {} + struct wl_resource *resource) { + (void)client; + (void)resource; +} static void _xdg_toplevel_set_fullscreen(struct wl_client *client, struct wl_resource *resource, @@ -1242,10 +1277,16 @@ static void _xdg_toplevel_set_fullscreen(struct wl_client *client, } void _xdg_toplevel_unset_fullscreen(struct wl_client *client, - struct wl_resource *resource) {} + struct wl_resource *resource) { + (void)client; + (void)resource; +} void _xdg_toplevel_set_minimized(struct wl_client *client, - struct wl_resource *resource) {} + struct wl_resource *resource) { + (void)client; + (void)resource; +} void _xdg_popup_destroy(struct wl_client *client, struct wl_resource *resource) { diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index eb4721c..b51259f 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -106,7 +106,7 @@ struct egl_backend_state_t { }; struct egl_output_state_t { - GLint fbo_id, fbo_tex_id, rbo_tex_depth; + GLuint fbo_id, fbo_tex_id, rbo_tex_depth; struct wl_list rendered_buffer_uses; }; @@ -210,8 +210,10 @@ bool _egl_gl_import_buffer_shm(struct vt_renderer_t *r, struct vt_shm_attr_t *a, void *data = a->data; - bool need_buf_regen = egl_buf->tex.id == 0 || egl_buf->tex.width != width || - egl_buf->tex.height != height; + // width/height checked positive above + bool need_buf_regen = egl_buf->tex.id == 0 || + egl_buf->tex.width != (uint32_t)width || + egl_buf->tex.height != (uint32_t)height; if (egl_buf->tex.id == 0) glGenTextures(1, &egl_buf->tex.id); @@ -925,10 +927,12 @@ bool renderer_is_handle_renderable_egl(struct vt_renderer_t *renderer, static bool _egl_load_dmabuf_query_procs(void) { if (!eglQueryDmaBufFormatsEXT_ptr) eglQueryDmaBufFormatsEXT_ptr = - (void *)eglGetProcAddress("eglQueryDmaBufFormatsEXT"); + (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress( + "eglQueryDmaBufFormatsEXT"); if (!eglQueryDmaBufModifiersEXT_ptr) eglQueryDmaBufModifiersEXT_ptr = - (void *)eglGetProcAddress("eglQueryDmaBufModifiersEXT"); + (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress( + "eglQueryDmaBufModifiersEXT"); return eglQueryDmaBufFormatsEXT_ptr && eglQueryDmaBufModifiersEXT_ptr; } @@ -943,7 +947,7 @@ static bool _egl_query_dmabuf_formats(struct vt_compositor_t *comp, // Query the available DMABUF formats EGLint n_formats = 0; /* - /* we set max_formats to 0 to count the formats without retrieving them: + we set max_formats to 0 to count the formats without retrieving them: * If is 0, no formats are returned, but the total number of formats is returned in , and no error is generated. */ @@ -1438,7 +1442,7 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, struct vt_box_t *dst_box) { VT_TRACE(r->comp->log, "DRAW ENTER: surf=%p current_use=%p mapped=%d effective_mapped=%d", - surface, surface ? surface->current_buf_use : NULL, + (void *)surface, (void *)(surface ? surface->current_buf_use : NULL), surface ? surface->mapped : 0, surface ? vt_surface_effectively_mapped(surface) : 0); @@ -1495,15 +1499,17 @@ void renderer_draw_surface_egl(struct vt_renderer_t *r, return; vec4s uv_rect = (vec4s){ - (float)src_box->x / (float)surface->applied.width, - (float)src_box->y / (float)surface->applied.height, - (float)(src_box->x + src_box->width) / (float)surface->applied.width, - (float)(src_box->y + src_box->height) / (float)surface->applied.height, + .x = (float)src_box->x / (float)surface->applied.width, + .y = (float)src_box->y / (float)surface->applied.height, + .z = (float)(src_box->x + src_box->width) / (float)surface->applied.width, + .w = (float)(src_box->y + src_box->height) / + (float)surface->applied.height, }; - rn_image_render_adv(egl->render, (vec2s){dst_box->x, dst_box->y}, - (vec2s){dst_box->width, dst_box->height}, 0.0f, RN_WHITE, - egl_buf->tex, uv_rect, false, RN_NO_COLOR, 0.0f, 0.0f); + rn_image_render_adv(egl->render, (vec2s){.x = dst_box->x, .y = dst_box->y}, + (vec2s){.x = dst_box->width, .y = dst_box->height}, 0.0f, + RN_WHITE, egl_buf->tex, uv_rect, false, RN_NO_COLOR, 0.0f, + 0.0f); if (!vt_output_track_presented_surface(output, surface)) { VT_ERROR(r->comp->log, "Failed to track presented surface %p on output %p.", @@ -1547,9 +1553,10 @@ void renderer_draw_image_egl(struct vt_renderer_t *r, struct vt_output_t *output, uint32_t tex_id, uint32_t width, uint32_t height, float x, float y) { + (void)output; struct egl_backend_state_t *egl = BACKEND_DATA(r, struct egl_backend_state_t); - rn_image_render(egl->render, (vec2s){x, y}, RN_WHITE, + rn_image_render(egl->render, (vec2s){.x = x, .y = y}, RN_WHITE, (RnTexture){.id = tex_id, .width = width, .height = height}); } void renderer_draw_rect_egl(struct vt_renderer_t *r, float x, float y, float w, @@ -1561,7 +1568,7 @@ void renderer_draw_rect_egl(struct vt_renderer_t *r, float x, float y, float w, } struct egl_backend_state_t *egl = BACKEND_DATA(r, struct egl_backend_state_t); - rn_rect_render(egl->render, (vec2s){x, y}, (vec2s){w, h}, + rn_rect_render(egl->render, (vec2s){.x = x, .y = y}, (vec2s){.x = w, .y = h}, rn_color_from_hex(col)); } From 25053786e21ae2f62abf58e72fa1d2ccd513a3a2 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:19:44 +0200 Subject: [PATCH 30/57] chore(ci): build with -Werror --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b66525..38a7737 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,8 +23,9 @@ jobs: - uses: actions/checkout@v4 + # vortex must stay warning-free, vendored runara still has upstream ones - name: Configure - run: meson setup build + run: meson setup build -Dwerror=true -Drunara:werror=false - name: Compile run: meson compile -C build From 89e1e9eb110fb3bc6a88fcb217b155a1a9c76a74 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:28:24 +0200 Subject: [PATCH 31/57] fix(build): include headers for used libc functions --- src/backends/drm/props.h | 1 + src/core/scene.c | 1 + src/core/surface.c | 1 + src/core/util.h | 1 + src/protocols/linux_dmabuf.c | 1 + src/render/dmabuf.c | 2 ++ src/render/gl/egl_gl.c | 2 ++ 7 files changed, 9 insertions(+) diff --git a/src/backends/drm/props.h b/src/backends/drm/props.h index 535f25d..7aa2b19 100644 --- a/src/backends/drm/props.h +++ b/src/backends/drm/props.h @@ -24,6 +24,7 @@ #include #include +#include /* Needs to be sorted according to strcmp() for bsearch() */ enum vt_drm_connector_property_t { diff --git a/src/core/scene.c b/src/core/scene.c index 6119516..51a6362 100644 --- a/src/core/scene.c +++ b/src/core/scene.c @@ -27,6 +27,7 @@ #include "src/core/surface.h" #include "src/core/util.h" #include "src/render/renderer.h" +#include #include #define _SCENE_CHILD_CAP_INIT 4 diff --git a/src/core/surface.c b/src/core/surface.c index e8fc690..e2b8395 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -31,6 +31,7 @@ #include "src/core/util.h" #include "src/protocols/wl_subcompositor.h" #include +#include #include #define _SUBSYS_NAME "SURFACE" diff --git a/src/core/util.h b/src/core/util.h index 66b0acf..1a582da 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include diff --git a/src/protocols/linux_dmabuf.c b/src/protocols/linux_dmabuf.c index 15aa949..4c7df0b 100644 --- a/src/protocols/linux_dmabuf.c +++ b/src/protocols/linux_dmabuf.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/src/render/dmabuf.c b/src/render/dmabuf.c index e1e96fb..d1bff3e 100644 --- a/src/render/dmabuf.c +++ b/src/render/dmabuf.c @@ -22,6 +22,8 @@ #include "dmabuf.h" #include "src/render/drm_format.h" +#include +#include #define _SUBSYS_NAME "DMABUF" struct vt_dmabuf_feedback_t * diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index b51259f..d005799 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -22,7 +22,9 @@ #include #include +#include #include +#include #include #include From eac45651804a9e81a03701397826014608aeefc9 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:34:34 +0200 Subject: [PATCH 32/57] fix(core): shut down cleanly on SIGTERM and SIGINT --- src/core/compositor.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/compositor.c b/src/core/compositor.c index eb8d732..4e407ff 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -875,10 +875,24 @@ bool vt_comp_init(struct vt_compositor_t *c, int argc, char **argv) { return true; } +static int _vt_comp_handle_quit_signal(int sig, void *data) { + struct vt_compositor_t *c = data; + VT_TRACE(c->log, "Caught signal %d (%s), shutting down.", sig, + strsignal(sig)); + c->running = false; + return 0; +} + void vt_comp_run(struct vt_compositor_t *c) { if (!c || !c->wl.evloop || !c->wl.dsp) return; + // leave through vt_comp_terminate so the socket and lock get removed + struct wl_event_source *sig_term = wl_event_loop_add_signal( + c->wl.evloop, SIGTERM, _vt_comp_handle_quit_signal, c); + struct wl_event_source *sig_int = wl_event_loop_add_signal( + c->wl.evloop, SIGINT, _vt_comp_handle_quit_signal, c); + c->running = true; VT_TRACE(c->log, "Entering main event loop..."); while (c->running) { @@ -890,6 +904,11 @@ void vt_comp_run(struct vt_compositor_t *c) { } wl_display_flush_clients(c->wl.dsp); } + + if (sig_term) + wl_event_source_remove(sig_term); + if (sig_int) + wl_event_source_remove(sig_int); } bool vt_comp_terminate(struct vt_compositor_t *c) { From 081e26fb75fc3a8231588a51d275abc4bc6985b3 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:34:34 +0200 Subject: [PATCH 33/57] fix(bench): detect socket by mtime, make trace logging opt-in --- scripts/bench-run.sh | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/scripts/bench-run.sh b/scripts/bench-run.sh index 6206386..55a87c7 100755 --- a/scripts/bench-run.sh +++ b/scripts/bench-run.sh @@ -12,6 +12,8 @@ shift 2 TIMEOUT="${TIMEOUT:-600}" LOG="$BUILD/vortex-$MODE.log" OUT="$BUILD/glmark2-$MODE.txt" +# a failed run must not leave the previous score looking current +rm -f "$OUT" case "$MODE" in wl) @@ -32,13 +34,29 @@ drm) ;; esac -# line-buffered so the readiness lines show up while vortex runs +# trace logging costs frames, only turn it on when debugging +LOG_ARGS=() +if [[ "${VERBOSE:-0}" == 1 ]]; then + LOG_ARGS=(--verbose) +fi + +# a killed vortex leaves its socket behind and the next one reuses the +# name, so match on creation after launch rather than on a new name +MARK="$(mktemp)" +new_socket() { + find "$XDG_RUNTIME_DIR" -maxdepth 1 -type s -name 'wayland-*' \ + -newer "$MARK" -printf '%f\n' +} + +# line-buffered so the last lines before a hang/kill reach the log timeout -s ABRT -k 5 "$TIMEOUT" stdbuf -oL -eL \ - "$BUILD/vortex" --verbose --backend-path "$BACKEND" >"$LOG" 2>&1 & + "$BUILD/vortex" "${LOG_ARGS[@]}" --backend-path "$BACKEND" \ + >"$LOG" 2>&1 & VPID=$! -trap 'kill "$VPID" || true; wait "$VPID" || true' EXIT +trap 'kill "$VPID" || true; wait "$VPID" || true; rm -f "$MARK"' EXIT -# socket is bound before session/backend init, seat init comes last +# the new socket is vortex's. connecting before init finishes is fine: +# the first roundtrip is only answered once the event loop runs. SOCK="" for _ in $(seq 100); do if ! kill -0 "$VPID"; then @@ -46,8 +64,8 @@ for _ in $(seq 100); do cat "$LOG" exit 1 fi - if grep -q "Initialized wayland seat." "$LOG"; then - SOCK="$(grep -oP "ready on socket '\K[^']+" "$LOG")" + SOCK="$(new_socket)" + if [[ -n "$SOCK" ]]; then break fi sleep 0.1 From 9fa33bd3c2f64b4547a0fe31a2eb8770b3c60f2a Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:37:44 +0200 Subject: [PATCH 34/57] fix(drm): poll for flip events before draining on suspend/teardown --- src/backends/drm/drm_backend.c | 78 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 5feb755..89778f0 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -695,6 +696,38 @@ _drm_build_dmabuf_feedback(struct vt_drm_backend_master_state_t *master, return false; } +static bool _drm_any_flip_inflight(struct vt_drm_backend_state_t *drm) { + struct vt_output_t *output; + wl_list_for_each(output, &drm->outputs, link_local) { + if (output->user_data && + BACKEND_DATA(output, struct vt_drm_output_state_t)->flip_inflight) + return true; + } + return false; +} + +/* libseat hands out a non-blocking fd: reading before the flip event + * arrives fails with EAGAIN, so wait for it instead */ +static bool _drm_drain_flips(struct vt_drm_backend_state_t *drm) { + while (_drm_any_flip_inflight(drm)) { + struct pollfd pfd = {.fd = drm->drm_fd, .events = POLLIN}; + int ret = poll(&pfd, 1, 1000); + if (ret < 0 && errno == EINTR) + continue; + if (ret <= 0) { + VT_WARN(drm->comp->log, "Waiting for DRM flips failed: %s", + ret == 0 ? "timed out" : strerror(errno)); + return false; + } + if (drmHandleEvent(drm->drm_fd, &drm->evctx) != 0 && errno != EAGAIN) { + VT_WARN(drm->comp->log, "Failed to drain DRM events: %s", + strerror(errno)); + return false; + } + } + return true; +} + static bool _drm_suspend(struct vt_drm_backend_state_t *backend) { if (!backend || !backend->comp || backend->drm_fd < 0 || !backend->impl) return false; @@ -706,27 +739,8 @@ static bool _drm_suspend(struct vt_drm_backend_state_t *backend) { output->needs_repaint = false; } - bool any_inflight; - do { - any_inflight = false; - wl_list_for_each(output, &backend->outputs, link_local) { - if (!output->user_data) - continue; - - struct vt_drm_output_state_t *drm_output = - BACKEND_DATA(output, struct vt_drm_output_state_t); - if (drm_output->flip_inflight) { - any_inflight = true; - break; - } - } - - if (any_inflight && drmHandleEvent(backend->drm_fd, &backend->evctx) != 0) { - VT_ERROR(backend->comp->log, "Failed to drain DRM events: %s", - strerror(errno)); - return false; - } - } while (any_inflight); + if (!_drm_drain_flips(backend)) + return false; wl_list_for_each(output, &backend->outputs, link_local) { if (!output->user_data) @@ -2329,25 +2343,9 @@ static bool _drm_terminate_for_device(struct vt_drm_backend_state_t *drm) { return false; struct vt_compositor_t *comp = drm->comp; - if (!comp->suspended && drm->drm_fd >= 0) { - bool any_inflight; - do { - any_inflight = false; - struct vt_output_t *output; - wl_list_for_each(output, &drm->outputs, link_local) { - if (output->user_data && - BACKEND_DATA(output, struct vt_drm_output_state_t)->flip_inflight) { - any_inflight = true; - break; - } - } - if (any_inflight && drmHandleEvent(drm->drm_fd, &drm->evctx) != 0) { - VT_WARN(comp->log, "Failed to drain DRM events during teardown: %s", - strerror(errno)); - break; - } - } while (any_inflight); - } + // teardown goes on regardless, the warning is the only signal + if (!comp->suspended && drm->drm_fd >= 0) + _drm_drain_flips(drm); if (drm->res) drmModeFreeResources(drm->res); From cedf721f96a359074862057785e5f4206feb4284 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 12:59:08 +0200 Subject: [PATCH 35/57] match pattern to build Signed-off-by: h8d13 --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cc4a0b8..67f64b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -./build +./build* .cache *.json subprojects/cglm-*/ From 0c80ee4af3761e79d01c74026bececd0a216271b Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:00:54 +0200 Subject: [PATCH 36/57] feat(core): content update constraints with in-order apply --- src/core/content_update.c | 86 ++++++++++++++++++++++++++++++++++----- src/core/content_update.h | 19 +++++++++ src/core/surface.c | 4 ++ 3 files changed, 98 insertions(+), 11 deletions(-) diff --git a/src/core/content_update.c b/src/core/content_update.c index ac0e9a2..47e9065 100644 --- a/src/core/content_update.c +++ b/src/core/content_update.c @@ -113,19 +113,14 @@ static bool _content_update_is_candidate(const struct vt_content_update_t *cu) { if (!cu) return false; - if (cu->type != VT_CU_DESYNC) + if (cu->type != VT_CU_DESYNC || wl_list_empty(&cu->surf->content_updates)) return false; - const struct vt_content_update_t *it; - wl_list_for_each(it, &cu->surf->content_updates, queue_link) { - if (it == cu) - return true; - - if (it->type == VT_CU_SYNC) - return false; - } - - return false; + // anything queued ahead is unapplied (sync, or held by a constraint), and + // applying past it would show frames out of order + const struct vt_content_update_t *head = + wl_container_of(cu->surf->content_updates.next, head, queue_link); + return head == cu; } static bool @@ -381,6 +376,13 @@ void vt_content_update_destroy(struct vt_content_update_t *cu) { _content_update_dependency_destroy(it); } + struct vt_content_update_constraint_t *c, *c_tmp; + wl_list_for_each_safe(c, c_tmp, &cu->constraints, link) { + wl_list_remove(&c->link); + wl_list_init(&c->link); + c->destroy(c); + } + vt_surface_pending_state_fini(&cu->state); if (cu->queued) { @@ -468,6 +470,68 @@ bool vt_content_update_apply_dag(struct vt_content_update_t *root) { return true; } +bool vt_content_update_waits(const struct vt_content_update_t *cu) { + assert(cu && cu->surf); + + const struct vt_content_update_t *it; + wl_list_for_each(it, &cu->surf->content_updates, queue_link) { + if (!_content_update_is_ready(it)) + return true; + if (it == cu) + break; + } + return false; +} + +void vt_content_update_add_constraint( + struct vt_content_update_t *cu, struct vt_content_update_constraint_t *c) { + assert(cu && c && c->destroy); + wl_list_insert(&cu->constraints, &c->link); +} + +static void _content_update_collect_dependant_surfaces( + struct vt_content_update_t *cu, struct wl_array *surfs) { + struct vt_content_update_dependency_t *edge; + wl_list_for_each(edge, &cu->dependants, dependant_link) { + struct vt_surface_t **slot = wl_array_add(surfs, sizeof(*slot)); + if (slot) + *slot = edge->cu->surf; + _content_update_collect_dependant_surfaces(edge->cu, surfs); + } +} + +// applies queue heads in order until one is not ready yet +static void _content_update_flush_surface(struct vt_surface_t *surf) { + while (!wl_list_empty(&surf->content_updates)) { + struct vt_content_update_t *head = + wl_container_of(surf->content_updates.next, head, queue_link); + if (!vt_content_update_apply_dag(head)) + return; + } +} + +void vt_content_update_resolve_constraint( + struct vt_content_update_t *cu, struct vt_content_update_constraint_t *c) { + assert(cu && c); + + wl_list_remove(&c->link); + wl_list_init(&c->link); + + // applying retires updates, so gather the affected surfaces up front + struct wl_array surfs; + wl_array_init(&surfs); + struct vt_surface_t **slot = wl_array_add(&surfs, sizeof(*slot)); + if (slot) + *slot = cu->surf; + _content_update_collect_dependant_surfaces(cu, &surfs); + + wl_array_for_each(slot, &surfs) { + _content_update_flush_surface(*slot); + } + + wl_array_release(&surfs); +} + bool vt_content_update_reaches(struct vt_content_update_t *from, struct vt_content_update_t *target) { if (!from || !target) diff --git a/src/core/content_update.h b/src/core/content_update.h index 8270fb2..bbc17e5 100644 --- a/src/core/content_update.h +++ b/src/core/content_update.h @@ -48,6 +48,14 @@ struct vt_content_update_t { int acquire_fence_fd; }; +/* Holds a content update back until resolved, e.g. while a client's GPU work + * for the attached buffer has not been submitted yet. destroy runs if the + * update dies first. */ +struct vt_content_update_constraint_t { + struct wl_list link; + void (*destroy)(struct vt_content_update_constraint_t *constraint); +}; + struct vt_content_update_dependency_t { struct vt_content_update_t *cu; struct vt_content_update_t *dependency; @@ -70,5 +78,16 @@ bool vt_content_update_add_dependency(struct vt_content_update_t *cu, bool vt_content_update_apply_dag(struct vt_content_update_t *root); +// true while cu or an update queued ahead of it is held by a constraint +bool vt_content_update_waits(const struct vt_content_update_t *cu); + +void vt_content_update_add_constraint( + struct vt_content_update_t *cu, struct vt_content_update_constraint_t *c); + +/* Detaches the constraint and applies whatever became ready. cu may be + * destroyed by the time this returns. */ +void vt_content_update_resolve_constraint( + struct vt_content_update_t *cu, struct vt_content_update_constraint_t *c); + bool vt_content_update_reaches(struct vt_content_update_t *from, struct vt_content_update_t *target); diff --git a/src/core/surface.c b/src/core/surface.c index e2b8395..a05e94d 100644 --- a/src/core/surface.c +++ b/src/core/surface.c @@ -415,6 +415,10 @@ bool vt_surface_emit_content_update(struct vt_surface_t *surf) { if (cu->type == VT_CU_SYNC) return true; + // stays queued, resolving the constraint applies it in order + if (vt_content_update_waits(cu)) + return true; + if (!vt_content_update_apply_dag(cu)) goto fail; From 297e6d43c98a3b41adf39ebcef118f39e30c167c Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:06:07 +0200 Subject: [PATCH 37/57] feat(proto): implement linux-drm-syncobj-v1 --- meson.build | 27 +- src/backends/drm/drm_backend.c | 9 + src/core/surface.h | 1 + src/protocols/linux_drm_syncobj.c | 595 ++++++++++++++++++++++++++++++ src/protocols/linux_drm_syncobj.h | 44 +++ src/render/gl/egl_gl.c | 8 + src/render/gl/egl_gl.h | 2 + src/render/renderer.c | 1 + src/render/renderer.h | 2 + 9 files changed, 687 insertions(+), 2 deletions(-) create mode 100644 src/protocols/linux_drm_syncobj.c create mode 100644 src/protocols/linux_drm_syncobj.h diff --git a/meson.build b/meson.build index 6ac7157..d28ed39 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,8 @@ dep_wayland_egl = dependency('wayland-egl', required: true) dep_wayland_protos = dependency('wayland-protocols', required: true) dep_egl = dependency('egl', required: true) dep_udev = dependency('libudev', required: true) +# syncobj timelines are imported in core protocol code +dep_libdrm = dependency('libdrm', required: true) dep_gl = dependency('gl', required: true) dep_pixman = dependency('pixman-1', required: true) dep_libinput = dependency('libinput', required: true) @@ -37,6 +39,7 @@ protocols_dir = dep_wayland_protos.get_variable(pkgconfig: 'pkgdatadir') xdg_shell_xml = join_paths(protocols_dir, 'stable', 'xdg-shell', 'xdg-shell.xml') dma_buf_xml = join_paths(protocols_dir, 'stable', 'linux-dmabuf', 'linux-dmabuf-v1.xml') explitic_sync_xml = join_paths(protocols_dir, 'unstable', 'linux-explicit-synchronization', 'linux-explicit-synchronization-unstable-v1.xml') +drm_syncobj_xml = join_paths(protocols_dir, 'staging', 'linux-drm-syncobj', 'linux-drm-syncobj-v1.xml') xdg_server_header = custom_target( 'xdg-shell-server-header', @@ -85,6 +88,22 @@ explitic_sync_code = custom_target( install: false ) +drm_syncobj_header = custom_target( + 'linux-drm-syncobj-header', + input: drm_syncobj_xml, + output: 'linux-drm-syncobj-v1-server-protocol.h', + command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], + install: false +) + +drm_syncobj_code = custom_target( + 'linux-drm-syncobj-code', + input: drm_syncobj_xml, + output: 'linux-drm-syncobj-v1-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + install: false +) + vortex_src = files('src/vortex.c', 'src/vortex.c', 'src/render/renderer.c', @@ -101,6 +120,8 @@ vortex_src = files('src/vortex.c', 'src/protocols/linux_dmabuf.h', 'src/protocols/linux_explicit_sync.c', 'src/protocols/linux_explicit_sync.h', +'src/protocols/linux_drm_syncobj.c', +'src/protocols/linux_drm_syncobj.h', 'src/protocols/wl_surface.h', 'src/protocols/wl_surface.c', 'src/protocols/wl_output.c', @@ -145,7 +166,7 @@ vortex_inc = include_directories('include', '/usr/include/pixman-1/') deps = [ dep_wayland_server, dep_wayland_egl, dep_egl, dep_libinput, dep_xcursor, dep_udev, dep_gl, - runara_dep, dep_pixman, + runara_dep, dep_pixman, dep_libdrm, ] if dep_xkbcommon.found() deps += dep_xkbcommon @@ -171,7 +192,9 @@ vortex_exe = executable( dma_buf_header, dma_buf_code, explitic_sync_header, - explitic_sync_code + explitic_sync_code, + drm_syncobj_header, + drm_syncobj_code ], include_directories: vortex_inc, link_args: ['-Wl,-E'], diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 89778f0..335cf30 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -59,6 +59,7 @@ #include "core/util.h" #include "input/wl_seat.h" #include "protocols/linux_dmabuf.h" +#include "protocols/linux_drm_syncobj.h" #include "protocols/linux_explicit_sync.h" #include "protocols/wl_output.h" #include "protocols/wl_shm.h" @@ -2996,6 +2997,14 @@ bool backend_init_drm(struct vt_backend_t *backend) { vt_dmabuf_feedback_fini(default_feedback); } + // timelines import into the main device, the one the renderer waits on + struct vt_renderer_t *r = backend->comp->renderer; + if (drm_master->main_drm && r && r->impl.supports_explicit_sync && + r->impl.supports_explicit_sync(r)) { + vt_proto_linux_drm_syncobj_v1_init(backend->comp, + drm_master->main_drm->drm_fd); + } + if (backend->comp->have_proto_dmabuf_explicit_sync) { const uint32_t dmabuf_explicit_sync_ver = 2; if (!vt_proto_linux_explicit_sync_v1_init(backend->comp, diff --git a/src/core/surface.h b/src/core/surface.h index 74b1432..12376e0 100644 --- a/src/core/surface.h +++ b/src/core/surface.h @@ -149,6 +149,7 @@ struct vt_surface_t { struct { struct vt_linux_dmabuf_v1_surface_state_t *linux_dmabuf_v1; struct vt_linux_explicit_sync_v1_surface_state_t *linux_explicit_sync_v1; + struct vt_linux_drm_syncobj_v1_surface_state_t *linux_drm_syncobj_v1; } proto_state; struct wl_list link, link_focus; diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c new file mode 100644 index 0000000..0c91e55 --- /dev/null +++ b/src/protocols/linux_drm_syncobj.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2026 Luca Machiedo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* linux-drm-syncobj-v1: timeline points are converted to and from sync_files + * so the existing acquire/release fence paths (EGL wait, KMS IN_FENCE_FD, + * render/scanout release fences) do the actual synchronization. */ + +#include "linux_drm_syncobj.h" +#include "src/core/buffer.h" +#include "src/core/content_update.h" +#include "src/core/surface.h" +#include "src/core/util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _SUBSYS_NAME "VT_PROTO_LINUX_DRM_SYNCOBJ" + +struct vt_drm_syncobj_timeline_t { + int drm_fd; + uint32_t handle; + uint32_t refcount; +}; + +struct vt_drm_syncobj_release_t { + struct vt_buffer_release_t base; + struct vt_drm_syncobj_timeline_t *timeline; + uint64_t point; + bool signaled; +}; + +struct vt_drm_syncobj_acquire_waiter_t { + struct vt_content_update_constraint_t constraint; + struct vt_content_update_t *cu; + struct vt_drm_syncobj_timeline_t *timeline; + uint64_t point; + int event_fd; + struct wl_event_source *source; +}; + +static struct { + struct vt_compositor_t *comp; + int drm_fd; +} _proto = {.drm_fd = -1}; + +/* =================================================== + * ==================== TIMELINES ==================== + * =================================================== */ + +static struct vt_drm_syncobj_timeline_t * +_timeline_ref(struct vt_drm_syncobj_timeline_t *tl) { + tl->refcount++; + return tl; +} + +static void _timeline_unref(struct vt_drm_syncobj_timeline_t **tl_ptr) { + struct vt_drm_syncobj_timeline_t *tl = *tl_ptr; + *tl_ptr = NULL; + if (!tl || --tl->refcount != 0) + return; + + drmSyncobjDestroy(tl->drm_fd, tl->handle); + free(tl); +} + +static bool _timeline_point_available(struct vt_drm_syncobj_timeline_t *tl, + uint64_t point) { + uint32_t handle = tl->handle; + return drmSyncobjTimelineWait(tl->drm_fd, &handle, &point, 1, 0, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, + NULL) == 0; +} + +// sync_files are what the EGL and KMS fence paths consume +static int _timeline_export_sync_file(struct vt_drm_syncobj_timeline_t *tl, + uint64_t point) { + uint32_t tmp; + if (drmSyncobjCreate(tl->drm_fd, 0, &tmp) != 0) + return -1; + + int fd = -1; + if (drmSyncobjTransfer(tl->drm_fd, tmp, 0, tl->handle, point, 0) != 0 || + drmSyncobjExportSyncFile(tl->drm_fd, tmp, &fd) != 0) + fd = -1; + + drmSyncobjDestroy(tl->drm_fd, tmp); + return fd; +} + +static bool _timeline_import_sync_file(struct vt_drm_syncobj_timeline_t *tl, + uint64_t point, int sync_file_fd) { + uint32_t tmp; + if (drmSyncobjCreate(tl->drm_fd, 0, &tmp) != 0) + return false; + + bool ok = + drmSyncobjImportSyncFile(tl->drm_fd, tmp, sync_file_fd) == 0 && + drmSyncobjTransfer(tl->drm_fd, tl->handle, point, tmp, 0, 0) == 0; + + drmSyncobjDestroy(tl->drm_fd, tmp); + return ok; +} + +static void _timeline_signal(struct vt_drm_syncobj_timeline_t *tl, + uint64_t point) { + uint32_t handle = tl->handle; + if (drmSyncobjTimelineSignal(tl->drm_fd, &handle, &point, 1) != 0) + VT_ERROR(_proto.comp->log, "Failed to signal timeline point %" PRIu64 ": %s", + point, strerror(errno)); +} + +/* =================================================== + * ===================== RELEASE ===================== + * =================================================== */ + +static bool _release_needs_fence(struct vt_buffer_release_t *base) { + (void)base; + return true; +} + +static void _release_finish(struct vt_buffer_release_t *base, + int release_fence_fd) { + struct vt_drm_syncobj_release_t *rel = wl_container_of(base, rel, base); + + rel->signaled = true; + + if (release_fence_fd < 0) { + _timeline_signal(rel->timeline, rel->point); + return; + } + + if (_timeline_import_sync_file(rel->timeline, rel->point, release_fence_fd)) + return; + + // signaling early would let the client overwrite a buffer still being read + VT_WARN(_proto.comp->log, + "Failed to attach release fence to point %" PRIu64 + ", waiting on the CPU.", + rel->point); + struct pollfd pfd = {.fd = release_fence_fd, .events = POLLIN}; + poll(&pfd, 1, 1000); + _timeline_signal(rel->timeline, rel->point); +} + +static void _release_destroy(struct vt_buffer_release_t *base) { + struct vt_drm_syncobj_release_t *rel = wl_container_of(base, rel, base); + + // dropped before ever being used, the buffer was never read + if (!rel->signaled) + _timeline_signal(rel->timeline, rel->point); + + _timeline_unref(&rel->timeline); + free(rel); +} + +static const struct vt_buffer_release_implementation_t _release_impl = { + .finish = _release_finish, + .destroy = _release_destroy, + .needs_release_fence = _release_needs_fence, +}; + +/* =================================================== + * ===================== ACQUIRE ===================== + * =================================================== */ + +static void _waiter_finish(struct vt_drm_syncobj_acquire_waiter_t *w) { + if (w->source) + wl_event_source_remove(w->source); + if (w->event_fd >= 0) + close(w->event_fd); + _timeline_unref(&w->timeline); + free(w); +} + +static void _waiter_constraint_destroy(struct vt_content_update_constraint_t *c) { + struct vt_drm_syncobj_acquire_waiter_t *w = + wl_container_of(c, w, constraint); + _waiter_finish(w); +} + +static int _waiter_handle_ready(int fd, uint32_t mask, void *data) { + (void)fd; + (void)mask; + struct vt_drm_syncobj_acquire_waiter_t *w = data; + struct vt_content_update_t *cu = w->cu; + + int fence_fd = _timeline_export_sync_file(w->timeline, w->point); + if (fence_fd < 0) { + VT_ERROR(_proto.comp->log, + "Failed to export acquire point %" PRIu64 " as sync_file.", + w->point); + } else if (cu->buffer_use) { + if (cu->buffer_use->acquire_fence_fd >= 0) + close(cu->buffer_use->acquire_fence_fd); + cu->buffer_use->acquire_fence_fd = fence_fd; + } else { + close(fence_fd); + } + + // detached before resolving, so cu teardown no longer frees the waiter + vt_content_update_resolve_constraint(cu, &w->constraint); + _waiter_finish(w); + return 0; +} + +/* The point may not have a fence yet when the client commits before + * submitting its GPU work. Hold the update until it does, then let the + * GPU-side fence wait cover the actual rendering. */ +static bool _acquire_attach(struct vt_content_update_t *cu, + struct vt_drm_syncobj_timeline_t *tl, + uint64_t point) { + if (_timeline_point_available(tl, point)) { + int fence_fd = _timeline_export_sync_file(tl, point); + if (fence_fd < 0) + return false; + if (cu->acquire_fence_fd >= 0) + close(cu->acquire_fence_fd); + cu->acquire_fence_fd = fence_fd; + return true; + } + + struct vt_drm_syncobj_acquire_waiter_t *w = calloc(1, sizeof(*w)); + if (!w) + return false; + + w->cu = cu; + w->point = point; + w->timeline = _timeline_ref(tl); + w->constraint.destroy = _waiter_constraint_destroy; + w->event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + + if (w->event_fd < 0 || + drmSyncobjEventfd(tl->drm_fd, tl->handle, point, w->event_fd, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) != 0) { + VT_ERROR(_proto.comp->log, "Failed to wait on acquire point: %s", + strerror(errno)); + _waiter_finish(w); + return false; + } + + w->source = wl_event_loop_add_fd(_proto.comp->wl.evloop, w->event_fd, + WL_EVENT_READABLE, _waiter_handle_ready, w); + if (!w->source) { + _waiter_finish(w); + return false; + } + + vt_content_update_add_constraint(cu, &w->constraint); + return true; +} + +/* =================================================== + * ================== SURFACE ADDON ================== + * =================================================== */ + +static void _state_clear_points(struct vt_linux_drm_syncobj_v1_surface_state_t *s) { + _timeline_unref(&s->acquire_timeline); + _timeline_unref(&s->release_timeline); +} + +static bool _post_surface_error(struct vt_linux_drm_syncobj_v1_surface_state_t *s, + uint32_t code, const char *msg) { + wl_resource_post_error(s->res, code, "%s", msg); + return false; +} + +static bool _addon_validate_commit(struct vt_surface_t *surf) { + struct vt_linux_drm_syncobj_v1_surface_state_t *s = + surf->proto_state.linux_drm_syncobj_v1; + if (!s || !s->res) + return true; + + bool has_buffer = surf->pending.buffer_attached && surf->pending.buf; + bool has_points = s->acquire_timeline || s->release_timeline; + + if (!has_buffer) { + if (has_points) + return _post_surface_error(s, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, + "timeline points set without a buffer"); + return true; + } + + if (!s->acquire_timeline) + return _post_surface_error( + s, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, + "buffer attached without an acquire point"); + if (!s->release_timeline) + return _post_surface_error( + s, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, + "buffer attached without a release point"); + + if (s->acquire_timeline == s->release_timeline && + s->acquire_point >= s->release_point) + return _post_surface_error( + s, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, + "acquire point must be lower than release point on one timeline"); + + struct vt_dmabuf_attr_t attr; + if (!vt_buffer_get_dmabuf(surf->pending.buf, &attr)) + return _post_surface_error( + s, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_UNSUPPORTED_BUFFER, + "explicit sync needs a dmabuf buffer"); + + return true; +} + +static bool _addon_commit(struct vt_surface_t *surf, + struct vt_content_update_t *cu) { + struct vt_linux_drm_syncobj_v1_surface_state_t *s = + surf->proto_state.linux_drm_syncobj_v1; + if (!s || !s->acquire_timeline) + return true; + + // validated: both points are set and a dmabuf is attached + struct vt_drm_syncobj_release_t *rel = calloc(1, sizeof(*rel)); + if (!rel) { + _state_clear_points(s); + return false; + } + + vt_buffer_release_init_and_ref(&rel->base, surf->comp, &_release_impl); + rel->timeline = _timeline_ref(s->release_timeline); + rel->point = s->release_point; + + if (cu->state.buffer_release) + vt_buffer_release_unref(&cu->state.buffer_release); + cu->state.buffer_release = &rel->base; + + bool ok = _acquire_attach(cu, s->acquire_timeline, s->acquire_point); + if (!ok) + VT_ERROR(surf->comp->log, "Failed to attach acquire point to commit."); + + _state_clear_points(s); + return ok; +} + +static void _addon_destroy(struct vt_surface_addon_t *addon) { + struct vt_linux_drm_syncobj_v1_surface_state_t *s = + wl_container_of(addon, s, addon); + + _state_clear_points(s); + + if (s->res) + wl_resource_set_user_data(s->res, NULL); + + if (s->surf) + s->surf->proto_state.linux_drm_syncobj_v1 = NULL; + + free(s); +} + +static const struct vt_surface_addon_impl_t _addon_impl = { + .name = "linux-drm-syncobj-v1", + .destroy = _addon_destroy, + .validate_commit = _addon_validate_commit, + .commit = _addon_commit, +}; + +/* =================================================== + * =================== REQUESTS ====================== + * =================================================== */ + +static void _surface_destroy(struct wl_client *client, + struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void _surface_set_point(struct wl_resource *resource, + struct wl_resource *timeline_res, uint32_t hi, + uint32_t lo, bool acquire) { + struct vt_linux_drm_syncobj_v1_surface_state_t *s = + wl_resource_get_user_data(resource); + if (!s) { + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, + "the wl_surface was destroyed"); + return; + } + + struct vt_drm_syncobj_timeline_t *tl = wl_resource_get_user_data(timeline_res); + struct vt_drm_syncobj_timeline_t **slot = + acquire ? &s->acquire_timeline : &s->release_timeline; + + _timeline_unref(slot); + *slot = _timeline_ref(tl); + + uint64_t point = ((uint64_t)hi << 32) | lo; + if (acquire) + s->acquire_point = point; + else + s->release_point = point; +} + +static void _surface_set_acquire_point(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *timeline, + uint32_t point_hi, uint32_t point_lo) { + (void)client; + _surface_set_point(resource, timeline, point_hi, point_lo, true); +} + +static void _surface_set_release_point(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *timeline, + uint32_t point_hi, uint32_t point_lo) { + (void)client; + _surface_set_point(resource, timeline, point_hi, point_lo, false); +} + +static const struct wp_linux_drm_syncobj_surface_v1_interface _surface_impl = { + .destroy = _surface_destroy, + .set_acquire_point = _surface_set_acquire_point, + .set_release_point = _surface_set_release_point, +}; + +static void _surface_handle_resource_destroy(struct wl_resource *resource) { + struct vt_linux_drm_syncobj_v1_surface_state_t *s = + wl_resource_get_user_data(resource); + if (!s) + return; + + // points set since the last commit may be discarded + s->res = NULL; + vt_surface_addon_destroy(&s->addon); +} + +static void _timeline_destroy(struct wl_client *client, + struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static const struct wp_linux_drm_syncobj_timeline_v1_interface _timeline_impl = + { + .destroy = _timeline_destroy, +}; + +// points already set keep their own reference +static void _timeline_handle_resource_destroy(struct wl_resource *resource) { + struct vt_drm_syncobj_timeline_t *tl = wl_resource_get_user_data(resource); + _timeline_unref(&tl); +} + +static void _manager_destroy(struct wl_client *client, + struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void _manager_get_surface(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_res) { + struct vt_surface_t *surf = wl_resource_get_user_data(surface_res); + + if (surf->proto_state.linux_drm_syncobj_v1) { + wl_resource_post_error( + resource, WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, + "wl_surface@%" PRIu32 " already has a syncobj surface", + wl_resource_get_id(surface_res)); + return; + } + + struct vt_linux_drm_syncobj_v1_surface_state_t *s = calloc(1, sizeof(*s)); + struct wl_resource *res = wl_resource_create( + client, &wp_linux_drm_syncobj_surface_v1_interface, + wl_resource_get_version(resource), id); + if (!s || !res) { + free(s); + if (res) + wl_resource_destroy(res); + VT_WL_OUT_OF_MEMORY(_proto.comp, client); + return; + } + + s->surf = surf; + s->res = res; + s->addon.impl = _addon_impl; + wl_list_insert(&surf->addons, &s->addon.link); + surf->proto_state.linux_drm_syncobj_v1 = s; + + wl_resource_set_implementation(res, &_surface_impl, s, + _surface_handle_resource_destroy); +} + +static void _manager_import_timeline(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + int32_t fd) { + struct vt_drm_syncobj_timeline_t *tl = calloc(1, sizeof(*tl)); + if (!tl) { + close(fd); + VT_WL_OUT_OF_MEMORY(_proto.comp, client); + return; + } + + tl->drm_fd = _proto.drm_fd; + tl->refcount = 1; + int ret = drmSyncobjFDToHandle(tl->drm_fd, fd, &tl->handle); + close(fd); + + if (ret != 0) { + free(tl); + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, + "cannot import DRM syncobj: %s", strerror(errno)); + return; + } + + struct wl_resource *res = + wl_resource_create(client, &wp_linux_drm_syncobj_timeline_v1_interface, + wl_resource_get_version(resource), id); + if (!res) { + _timeline_unref(&tl); + VT_WL_OUT_OF_MEMORY(_proto.comp, client); + return; + } + + wl_resource_set_implementation(res, &_timeline_impl, tl, + _timeline_handle_resource_destroy); +} + +static const struct wp_linux_drm_syncobj_manager_v1_interface _manager_impl = { + .destroy = _manager_destroy, + .get_surface = _manager_get_surface, + .import_timeline = _manager_import_timeline, +}; + +static void _manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + (void)data; + struct wl_resource *res = wl_resource_create( + client, &wp_linux_drm_syncobj_manager_v1_interface, version, id); + if (!res) { + VT_WL_OUT_OF_MEMORY(_proto.comp, client); + return; + } + + wl_resource_set_implementation(res, &_manager_impl, NULL, NULL); + VT_TRACE(_proto.comp->log, "linux_drm_syncobj.bind: client bound version %u.", + version); +} + +/* =================================================== + * =================== PUBLIC API ==================== + * =================================================== */ + +bool vt_proto_linux_drm_syncobj_v1_init(struct vt_compositor_t *comp, + int drm_fd) { + uint64_t cap = 0; + if (drmGetCap(drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap) != 0 || !cap) { + VT_TRACE(comp->log, "DRM device lacks timeline syncobjs, not advertising " + "linux-drm-syncobj-v1."); + return false; + } + + _proto.comp = comp; + _proto.drm_fd = drm_fd; + + if (!wl_global_create(comp->wl.dsp, &wp_linux_drm_syncobj_manager_v1_interface, + 1, NULL, _manager_bind)) { + VT_ERROR(comp->log, "Cannot create linux-drm-syncobj-v1 global."); + return false; + } + + VT_TRACE(comp->log, "Initialized linux-drm-syncobj-v1."); + return true; +} diff --git a/src/protocols/linux_drm_syncobj.h b/src/protocols/linux_drm_syncobj.h new file mode 100644 index 0000000..1d5e026 --- /dev/null +++ b/src/protocols/linux_drm_syncobj.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Luca Machiedo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../core/core_types.h" +#include "../core/surface_addon.h" + +struct vt_drm_syncobj_timeline_t; + +struct vt_linux_drm_syncobj_v1_surface_state_t { + struct vt_surface_addon_t addon; + struct vt_surface_t *surf; + + struct wl_resource *res; + + // double-buffered, consumed by the next commit + struct vt_drm_syncobj_timeline_t *acquire_timeline, *release_timeline; + uint64_t acquire_point, release_point; +}; + +/* drm_fd must support DRM_CAP_SYNCOBJ_TIMELINE and outlive the global, it is + * the device timelines are imported into. */ +bool vt_proto_linux_drm_syncobj_v1_init(struct vt_compositor_t *comp, + int drm_fd); diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index d005799..0b8727e 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -1321,6 +1321,14 @@ bool renderer_import_buffer_egl(struct vt_renderer_t *r, egl->has_dmabuf_support); } +bool renderer_supports_explicit_sync_egl(struct vt_renderer_t *r) { + if (!r || !r->user_data) + return false; + struct egl_backend_state_t *egl = BACKEND_DATA(r, struct egl_backend_state_t); + return r->comp->have_proto_dmabuf_explicit_sync && + egl->has_explicit_sync_support; +} + bool renderer_drop_context_egl(struct vt_renderer_t *r) { if (!r || !r->impl.drop_context || !r->user_data) { VT_ERROR(r->comp->log, diff --git a/src/render/gl/egl_gl.h b/src/render/gl/egl_gl.h index 6747536..8305eba 100644 --- a/src/render/gl/egl_gl.h +++ b/src/render/gl/egl_gl.h @@ -64,6 +64,8 @@ bool renderer_import_buffer_egl(struct vt_renderer_t *r, bool renderer_drop_context_egl(struct vt_renderer_t *r); +bool renderer_supports_explicit_sync_egl(struct vt_renderer_t *r); + void renderer_set_vsync_egl(struct vt_renderer_t *r, bool vsync); void renderer_set_clear_color_egl(struct vt_renderer_t *r, diff --git a/src/render/renderer.c b/src/render/renderer.c index ff0ff12..a110a36 100644 --- a/src/render/renderer.c +++ b/src/render/renderer.c @@ -38,6 +38,7 @@ void vt_renderer_implement(struct vt_renderer_t *renderer, .destroy_renderable_output = renderer_destroy_renderable_output_egl, .import_buffer = renderer_import_buffer_egl, .drop_context = renderer_drop_context_egl, + .supports_explicit_sync = renderer_supports_explicit_sync_egl, .set_vsync = renderer_set_vsync_egl, .composite_pass = renderer_composite_pass_egl, .stencil_damage_pass = renderer_stencil_damage_pass_egl, diff --git a/src/render/renderer.h b/src/render/renderer.h index b4109d7..4d3fa92 100644 --- a/src/render/renderer.h +++ b/src/render/renderer.h @@ -51,6 +51,8 @@ struct vt_renderer_interface_t { bool (*import_buffer)(struct vt_renderer_t *r, struct vt_buffer_t *buf, const pixman_region32_t *damage); bool (*drop_context)(struct vt_renderer_t *r); + // true when acquire fences are waited on and release fences produced + bool (*supports_explicit_sync)(struct vt_renderer_t *r); void (*set_vsync)(struct vt_renderer_t *r, bool vsync); void (*set_clear_color)(struct vt_renderer_t *r, struct vt_output_t *output, uint32_t col); From 18572070819fd4ca5da75f52bc49b4baa3cd12db Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:06:15 +0200 Subject: [PATCH 38/57] fix(bench): forward VERBOSE, TIMEOUT, VT through sudo --- compbench.sh | 2 +- scripts/bench-drm.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compbench.sh b/compbench.sh index 8730425..5049658 100755 --- a/compbench.sh +++ b/compbench.sh @@ -25,7 +25,7 @@ wl) ;; drm) # build stays user-owned, only the VT session needs root - sudo scripts/bench-drm.sh "$BUILD" "$@" + sudo --preserve-env=VERBOSE,TIMEOUT,VT scripts/bench-drm.sh "$BUILD" "$@" ;; *) echo "unknown mode '$MODE' (wl|drm)" >&2 diff --git a/scripts/bench-drm.sh b/scripts/bench-drm.sh index 0ee7aed..698dd9c 100755 --- a/scripts/bench-drm.sh +++ b/scripts/bench-drm.sh @@ -33,7 +33,7 @@ systemd-run --unit="$UNIT" --wait --collect \ -p TTYReset=yes -p TTYVTDisallocate=yes \ -p RuntimeMaxSec="$((TIMEOUT + 30))" \ -p WorkingDirectory="$ROOT" \ - --setenv=TIMEOUT="$TIMEOUT" \ + --setenv=TIMEOUT="$TIMEOUT" --setenv=VERBOSE="${VERBOSE:-0}" \ "$ROOT/scripts/bench-run.sh" "$BUILD" drm "$@" || status=$? # unit stdout did not reach the journal in testing, read results back From 67a504474a1669fbad6630b2169fb1339e7a7036 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:17:56 +0200 Subject: [PATCH 39/57] feat(drm): count missed vblanks per output, report at teardown --- src/backends/drm/drm_backend.c | 47 ++++++++++++++++++++++++++++++++-- src/backends/drm/drm_types.h | 7 +++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 335cf30..9a95df6 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -288,12 +288,48 @@ static void _drm_complete_pending(struct vt_drm_output_state_t *drm_output) { } } +static void _drm_frame_stats_record(struct vt_drm_output_state_t *drm_output, + unsigned int sec, unsigned int usec, + bool busy) { + uint64_t now = (uint64_t)sec * 1000000 + usec; + uint32_t hz = drm_output->mode.vrefresh; + + // only a frame wanted right after the last one can miss a vblank + if (drm_output->frame_stats.busy && drm_output->frame_stats.last_us && hz) { + uint64_t interval = now - drm_output->frame_stats.last_us; + uint64_t expected = 1000000 / hz; + if (interval > expected + expected / 2) + drm_output->frame_stats.missed++; + if (interval > drm_output->frame_stats.worst_us) + drm_output->frame_stats.worst_us = interval; + } + + drm_output->frame_stats.frames++; + drm_output->frame_stats.last_us = now; + drm_output->frame_stats.busy = busy; +} + +static void _drm_frame_stats_log(struct vt_drm_output_state_t *drm_output) { + struct vt_compositor_t *comp = drm_output->base->backend->comp; + + if (drm_output->frame_stats.missed == 0) { + VT_TRACE(comp->log, "CRTC %u: %" PRIu64 " frames, no missed vblanks.", + drm_output->crtc->id, drm_output->frame_stats.frames); + return; + } + + VT_WARN(comp->log, + "CRTC %u (%u Hz): %" PRIu64 " of %" PRIu64 + " frames missed a vblank, worst interval %.2f ms.", + drm_output->crtc->id, drm_output->mode.vrefresh, + drm_output->frame_stats.missed, drm_output->frame_stats.frames, + drm_output->frame_stats.worst_us / 1000.0); +} + static void _drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { (void)fd; (void)frame; - (void)sec; - (void)usec; struct vt_output_t *output = (struct vt_output_t *)data; if (!output || !output->user_data || !output->backend || @@ -326,6 +362,9 @@ static void _drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec, vt_comp_frame_done(output->backend->comp, output, t); } + if (frame_commit) + _drm_frame_stats_record(drm_output, sec, usec, output->needs_repaint); + if (output->needs_repaint) { vt_comp_schedule_repaint(output->backend->comp, output); } else if (output->cursor_dirty) { @@ -759,6 +798,8 @@ static bool _drm_suspend(struct vt_drm_backend_state_t *backend) { _drm_release_all_layers(output); drm_output->needs_modeset = true; drm_output->flip_inflight = false; + // time spent switched away is not a missed vblank + drm_output->frame_stats.busy = false; } return true; @@ -2354,6 +2395,8 @@ static bool _drm_terminate_for_device(struct vt_drm_backend_state_t *drm) { struct vt_output_t *output, *tmp; wl_list_for_each_safe(output, tmp, &drm->outputs, link_local) { + if (output->user_data) + _drm_frame_stats_log(BACKEND_DATA(output, struct vt_drm_output_state_t)); _drm_destroy_output_for_device(drm, output); } diff --git a/src/backends/drm/drm_types.h b/src/backends/drm/drm_types.h index a1711c5..b3809a8 100644 --- a/src/backends/drm/drm_types.h +++ b/src/backends/drm/drm_types.h @@ -233,6 +233,13 @@ struct vt_drm_output_state_t { bool needs_modeset; bool flip_inflight; + + // kernel flip timestamps, summarized at teardown + struct { + uint64_t last_us, worst_us; + uint64_t frames, missed; + bool busy; + } frame_stats; bool modeset_bootstrapped; bool renderable_setup; bool connector_seen; From 5543b97db5d0ebec841d07372f04ba83bcfef6f4 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:21:17 +0200 Subject: [PATCH 40/57] fix(core): stop warning per repaint request while suspended --- src/core/compositor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/compositor.c b/src/core/compositor.c index 4e407ff..7032a70 100644 --- a/src/core/compositor.c +++ b/src/core/compositor.c @@ -965,9 +965,9 @@ void vt_comp_schedule_repaint(struct vt_compositor_t *c, if (!c || !output || !c->backend || !c->wl.evloop) return; + // clients keep committing while switched away, resume repaints everything if (c->suspended) { - VT_WARN(c->log, - "Trying to schedule repaint while compositor is suspended."); + VT_TRACE(c->log, "Dropping repaint request while suspended."); return; } From f408a62794d0aef0a1ab59a43deb4c3aaea7a70c Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:21:17 +0200 Subject: [PATCH 41/57] fix(drm): ignore EAGAIN when dispatching DRM events --- src/backends/drm/drm_backend.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 9a95df6..72fd702 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -854,7 +854,9 @@ static int _drm_dispatch(int fd, uint32_t mask, void *data) { VT_ERROR(drm->comp->log, "DRM event source failed."); return -1; } - if ((mask & WL_EVENT_READABLE) && drmHandleEvent(fd, &drm->evctx) != 0) { + // the libseat fd is non-blocking: EAGAIN only means nothing to read yet + if ((mask & WL_EVENT_READABLE) && drmHandleEvent(fd, &drm->evctx) != 0 && + errno != EAGAIN) { VT_ERROR(drm->comp->log, "Failed to handle DRM event: %s", strerror(errno)); return -1; } From 8878346b631b512cc93b154a2709ce0ba261e428 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:21:51 +0200 Subject: [PATCH 42/57] feat(bench): pass VORTEX_ARGS through to vortex for A/B runs --- compbench.sh | 2 +- scripts/bench-drm.sh | 1 + scripts/bench-run.sh | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compbench.sh b/compbench.sh index 5049658..7c0dcce 100755 --- a/compbench.sh +++ b/compbench.sh @@ -25,7 +25,7 @@ wl) ;; drm) # build stays user-owned, only the VT session needs root - sudo --preserve-env=VERBOSE,TIMEOUT,VT scripts/bench-drm.sh "$BUILD" "$@" + sudo --preserve-env=VERBOSE,TIMEOUT,VT,VORTEX_ARGS scripts/bench-drm.sh "$BUILD" "$@" ;; *) echo "unknown mode '$MODE' (wl|drm)" >&2 diff --git a/scripts/bench-drm.sh b/scripts/bench-drm.sh index 698dd9c..5c15329 100755 --- a/scripts/bench-drm.sh +++ b/scripts/bench-drm.sh @@ -34,6 +34,7 @@ systemd-run --unit="$UNIT" --wait --collect \ -p RuntimeMaxSec="$((TIMEOUT + 30))" \ -p WorkingDirectory="$ROOT" \ --setenv=TIMEOUT="$TIMEOUT" --setenv=VERBOSE="${VERBOSE:-0}" \ + --setenv=VORTEX_ARGS="${VORTEX_ARGS:-}" \ "$ROOT/scripts/bench-run.sh" "$BUILD" drm "$@" || status=$? # unit stdout did not reach the journal in testing, read results back diff --git a/scripts/bench-run.sh b/scripts/bench-run.sh index 55a87c7..db63401 100755 --- a/scripts/bench-run.sh +++ b/scripts/bench-run.sh @@ -39,6 +39,8 @@ LOG_ARGS=() if [[ "${VERBOSE:-0}" == 1 ]]; then LOG_ARGS=(--verbose) fi +# extra vortex flags for A/B runs, e.g. "-expt linux-dmabuf-explicit-sync" +read -ra VT_ARGS <<<"${VORTEX_ARGS:-}" # a killed vortex leaves its socket behind and the next one reuses the # name, so match on creation after launch rather than on a new name @@ -50,7 +52,7 @@ new_socket() { # line-buffered so the last lines before a hang/kill reach the log timeout -s ABRT -k 5 "$TIMEOUT" stdbuf -oL -eL \ - "$BUILD/vortex" "${LOG_ARGS[@]}" --backend-path "$BACKEND" \ + "$BUILD/vortex" "${LOG_ARGS[@]}" "${VT_ARGS[@]}" --backend-path "$BACKEND" \ >"$LOG" 2>&1 & VPID=$! trap 'kill "$VPID" || true; wait "$VPID" || true; rm -f "$MARK"' EXIT From 10b5bdd03279bde77b0f34716b6033590e21470f Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:26:42 +0200 Subject: [PATCH 43/57] feat(core): add INFO log level for summaries --- src/core/util.c | 5 +++-- src/core/util.h | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/core/util.c b/src/core/util.c index e4b2bbb..3219e6d 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -121,10 +121,11 @@ char *vt_util_log_get_filepath() { } void vt_util_log_header(FILE *stream, log_level_t lvl) { - static const char *lvl_str[VT_LL_COUNT] = {"TRACE", "WARNING", "ERROR", - "FATAL"}; + static const char *lvl_str[VT_LL_COUNT] = {"TRACE", "INFO", "WARNING", + "ERROR", "FATAL"}; static const char *lvl_clr[VT_LL_COUNT] = { "\033[1;32m", // TRACE - bright green + "\033[1;36m", // INFO - bright cyan "\033[1;33m", // WARNING - yellow "\033[1;31m", // ERROR - bright red "\033[38;5;88m" // FATAL - dark red diff --git a/src/core/util.h b/src/core/util.h index 1a582da..53ff700 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -51,6 +51,17 @@ } \ } +// shown without --verbose, for summaries rather than problems +#define VT_INFO(logstate, ...) \ + if (!(logstate).quiet) { \ + do { \ + vt_util_log_header((logstate).stream, VT_LL_INFO); \ + fprintf((logstate).stream, "%s: ", _SUBSYS_NAME); \ + fprintf((logstate).stream, __VA_ARGS__); \ + fprintf((logstate).stream, "\n"); \ + } while (0); \ + } + #define VT_WARN(logstate, ...) \ if (!(logstate).quiet) { \ do { \ @@ -146,6 +157,7 @@ typedef enum { VT_LL_TRACE = 0, + VT_LL_INFO, VT_LL_WARN, VT_LL_ERR, VT_LL_FATAL, From bcad6d0833523902a58410a0defb1c2c34a47021 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:26:42 +0200 Subject: [PATCH 44/57] feat(drm): report CPU time and syncobj counters at teardown --- src/backends/drm/drm_backend.c | 17 ++++++++++------- src/protocols/linux_drm_syncobj.c | 22 ++++++++++++++++++++++ src/protocols/linux_drm_syncobj.h | 2 ++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 72fd702..800e771 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -312,13 +313,7 @@ static void _drm_frame_stats_record(struct vt_drm_output_state_t *drm_output, static void _drm_frame_stats_log(struct vt_drm_output_state_t *drm_output) { struct vt_compositor_t *comp = drm_output->base->backend->comp; - if (drm_output->frame_stats.missed == 0) { - VT_TRACE(comp->log, "CRTC %u: %" PRIu64 " frames, no missed vblanks.", - drm_output->crtc->id, drm_output->frame_stats.frames); - return; - } - - VT_WARN(comp->log, + VT_INFO(comp->log, "CRTC %u (%u Hz): %" PRIu64 " of %" PRIu64 " frames missed a vblank, worst interval %.2f ms.", drm_output->crtc->id, drm_output->mode.vrefresh, @@ -2402,6 +2397,14 @@ static bool _drm_terminate_for_device(struct vt_drm_backend_state_t *drm) { _drm_destroy_output_for_device(drm, output); } + // main thread load next to the vblank stats tells CPU from GPU stalls + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) == 0) + VT_INFO(comp->log, "CPU time: user %.2fs, sys %.2fs.", + ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6, + ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1e6); + vt_proto_linux_drm_syncobj_v1_log_stats(); + _drm_liftoff_finish(drm); struct vt_drm_plane_t *plane; diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c index 0c91e55..4670e81 100644 --- a/src/protocols/linux_drm_syncobj.c +++ b/src/protocols/linux_drm_syncobj.c @@ -69,6 +69,11 @@ struct vt_drm_syncobj_acquire_waiter_t { static struct { struct vt_compositor_t *comp; int drm_fd; + + // per-run counters, reported at teardown + struct { + uint64_t commits, waited, exported, release_imported, release_signaled; + } stats; } _proto = {.drm_fd = -1}; /* =================================================== @@ -106,6 +111,7 @@ static int _timeline_export_sync_file(struct vt_drm_syncobj_timeline_t *tl, if (drmSyncobjCreate(tl->drm_fd, 0, &tmp) != 0) return -1; + _proto.stats.exported++; int fd = -1; if (drmSyncobjTransfer(tl->drm_fd, tmp, 0, tl->handle, point, 0) != 0 || drmSyncobjExportSyncFile(tl->drm_fd, tmp, &fd) != 0) @@ -121,6 +127,7 @@ static bool _timeline_import_sync_file(struct vt_drm_syncobj_timeline_t *tl, if (drmSyncobjCreate(tl->drm_fd, 0, &tmp) != 0) return false; + _proto.stats.release_imported++; bool ok = drmSyncobjImportSyncFile(tl->drm_fd, tmp, sync_file_fd) == 0 && drmSyncobjTransfer(tl->drm_fd, tl->handle, point, tmp, 0, 0) == 0; @@ -131,6 +138,7 @@ static bool _timeline_import_sync_file(struct vt_drm_syncobj_timeline_t *tl, static void _timeline_signal(struct vt_drm_syncobj_timeline_t *tl, uint64_t point) { + _proto.stats.release_signaled++; uint32_t handle = tl->handle; if (drmSyncobjTimelineSignal(tl->drm_fd, &handle, &point, 1) != 0) VT_ERROR(_proto.comp->log, "Failed to signal timeline point %" PRIu64 ": %s", @@ -251,6 +259,7 @@ static bool _acquire_attach(struct vt_content_update_t *cu, if (!w) return false; + _proto.stats.waited++; w->cu = cu; w->point = point; w->timeline = _timeline_ref(tl); @@ -340,6 +349,7 @@ static bool _addon_commit(struct vt_surface_t *surf, return true; // validated: both points are set and a dmabuf is attached + _proto.stats.commits++; struct vt_drm_syncobj_release_t *rel = calloc(1, sizeof(*rel)); if (!rel) { _state_clear_points(s); @@ -593,3 +603,15 @@ bool vt_proto_linux_drm_syncobj_v1_init(struct vt_compositor_t *comp, VT_TRACE(comp->log, "Initialized linux-drm-syncobj-v1."); return true; } + +void vt_proto_linux_drm_syncobj_v1_log_stats(void) { + if (!_proto.comp) + return; + + VT_INFO(_proto.comp->log, + "syncobj: %" PRIu64 " commits, %" PRIu64 " waited for submit, %" PRIu64 + " acquire exports, %" PRIu64 " release imports, %" PRIu64 + " release signals.", + _proto.stats.commits, _proto.stats.waited, _proto.stats.exported, + _proto.stats.release_imported, _proto.stats.release_signaled); +} diff --git a/src/protocols/linux_drm_syncobj.h b/src/protocols/linux_drm_syncobj.h index 1d5e026..1eefd32 100644 --- a/src/protocols/linux_drm_syncobj.h +++ b/src/protocols/linux_drm_syncobj.h @@ -42,3 +42,5 @@ struct vt_linux_drm_syncobj_v1_surface_state_t { * the device timelines are imported into. */ bool vt_proto_linux_drm_syncobj_v1_init(struct vt_compositor_t *comp, int drm_fd); + +void vt_proto_linux_drm_syncobj_v1_log_stats(void); From 2cfc0b96cec1b49c8da735263a690b1abcfd53fa Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:28:26 +0200 Subject: [PATCH 45/57] feat(core): defer acquire fence export until a buffer is used --- src/backends/drm/drm_backend.c | 4 ++-- src/core/buffer.c | 17 +++++++++++++++++ src/core/buffer.h | 12 ++++++++++++ src/core/content_update.c | 6 ++++++ src/core/content_update.h | 2 ++ src/render/gl/egl_gl.c | 2 +- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 800e771..8549b89 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -3439,7 +3439,7 @@ bool backend_test_output_layers_drm(struct vt_backend_t *backend, } if (!drm_liftoff_set_layer_props(test->liftoff_layer, layer, test->fb.id, - use->acquire_fence_fd, i)) { + vt_buffer_use_acquire_fence(use), i)) { VT_TRACE(backend->comp->log, "layer %zu surface=%p rejected from liftoff test: " "failed to set liftoff layer properties.", @@ -3561,7 +3561,7 @@ bool backend_test_output_layers_drm(struct vt_backend_t *backend, if (!planned->use) goto fail_plan; - planned->acquire_fence_fd = test->use->acquire_fence_fd; + planned->acquire_fence_fd = vt_buffer_use_acquire_fence(test->use); } else { /* * the renderer is going to draw this surface into the compositor FB. diff --git a/src/core/buffer.c b/src/core/buffer.c index e1fb68b..c5361ae 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -351,6 +351,9 @@ static void _buffer_use_destroy(struct vt_buffer_use_t *use) { use->acquire_fence_fd = -1; } + if (use->acquire_source) + use->acquire_source->destroy(use->acquire_source); + vt_buffer_end_use(use->buf); if (use->release) @@ -420,6 +423,20 @@ struct vt_buffer_use_t *vt_buffer_use_create_take( return use; } +int vt_buffer_use_acquire_fence(struct vt_buffer_use_t *use) { + if (!use) + return -1; + + if (use->acquire_fence_fd < 0 && use->acquire_source) { + struct vt_buffer_acquire_source_t *src = use->acquire_source; + use->acquire_source = NULL; + use->acquire_fence_fd = src->export_fence(src); + src->destroy(src); + } + + return use->acquire_fence_fd; +} + bool vt_buffer_use_set_release_fence_fd(struct vt_buffer_use_t *use, int fd) { if (!use || !use->comp || fd < 0) { VT_PARAM_CHECK_FAIL_HEADLESS(); diff --git a/src/core/buffer.h b/src/core/buffer.h index 12394fc..05e7e9e 100644 --- a/src/core/buffer.h +++ b/src/core/buffer.h @@ -80,6 +80,13 @@ struct vt_buffer_release_t { const struct vt_buffer_release_implementation_t *impl; }; +/* Deferred acquire fence: only built when the buffer is actually drawn or + * scanned out. A fast client replaces most buffers before either happens. */ +struct vt_buffer_acquire_source_t { + int (*export_fence)(struct vt_buffer_acquire_source_t *src); + void (*destroy)(struct vt_buffer_acquire_source_t *src); +}; + struct vt_buffer_use_t { struct vt_compositor_t *comp; @@ -92,6 +99,8 @@ struct vt_buffer_use_t { int acquire_fence_fd; int release_fence_fd; + + struct vt_buffer_acquire_source_t *acquire_source; }; struct vt_buffer_t * @@ -141,5 +150,8 @@ struct vt_buffer_use_t *vt_buffer_use_create_take( struct vt_compositor_t *comp, const void *owner, struct vt_buffer_t **buf, struct vt_buffer_release_t **release, int *acquire_fence_fd); +// borrowed fd, materialized from acquire_source on first call, -1 if none +int vt_buffer_use_acquire_fence(struct vt_buffer_use_t *use); + bool vt_buffer_use_set_release_fence_fd(struct vt_buffer_use_t *use, int release_fd); diff --git a/src/core/content_update.c b/src/core/content_update.c index 47e9065..be8af6c 100644 --- a/src/core/content_update.c +++ b/src/core/content_update.c @@ -352,6 +352,9 @@ bool vt_content_update_finish_create(struct vt_content_update_t *cu) { if (!cu->buffer_use) return false; + + cu->buffer_use->acquire_source = cu->acquire_source; + cu->acquire_source = NULL; } VT_TRACE(cu->surf->comp->log, @@ -394,6 +397,9 @@ void vt_content_update_destroy(struct vt_content_update_t *cu) { cu->acquire_fence_fd = -1; } + if (cu->acquire_source) + cu->acquire_source->destroy(cu->acquire_source); + if (cu->buffer_use) vt_buffer_use_unref(&cu->buffer_use); diff --git a/src/core/content_update.h b/src/core/content_update.h index bbc17e5..d841ca0 100644 --- a/src/core/content_update.h +++ b/src/core/content_update.h @@ -46,6 +46,8 @@ struct vt_content_update_t { struct vt_buffer_use_t *buffer_use; int acquire_fence_fd; + + struct vt_buffer_acquire_source_t *acquire_source; }; /* Holds a content update back until resolved, e.g. while a client's GPU work diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index 0b8727e..94ec557 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -486,7 +486,7 @@ bool _egl_buffer_use_is_ready(struct vt_renderer_t *renderer, if (!renderer || !use) return false; - if (use->acquire_fence_fd < 0) + if (vt_buffer_use_acquire_fence(use) < 0) return true; struct egl_backend_state_t *egl = From 2f4474f960b01faa954787c24e526c4fb1b8126d Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:28:27 +0200 Subject: [PATCH 46/57] fix(proto): export syncobj acquire fences only for drawn buffers --- src/protocols/linux_drm_syncobj.c | 73 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c index 4670e81..3f0b341 100644 --- a/src/protocols/linux_drm_syncobj.c +++ b/src/protocols/linux_drm_syncobj.c @@ -57,11 +57,16 @@ struct vt_drm_syncobj_release_t { bool signaled; }; +struct vt_drm_syncobj_acquire_t { + struct vt_buffer_acquire_source_t base; + struct vt_drm_syncobj_timeline_t *timeline; + uint64_t point; +}; + +// holds a content update until its acquire point has been submitted struct vt_drm_syncobj_acquire_waiter_t { struct vt_content_update_constraint_t constraint; struct vt_content_update_t *cu; - struct vt_drm_syncobj_timeline_t *timeline; - uint64_t point; int event_fd; struct wl_event_source *source; }; @@ -199,12 +204,28 @@ static const struct vt_buffer_release_implementation_t _release_impl = { * ===================== ACQUIRE ===================== * =================================================== */ +static int _acquire_export(struct vt_buffer_acquire_source_t *base) { + struct vt_drm_syncobj_acquire_t *acq = wl_container_of(base, acq, base); + + int fd = _timeline_export_sync_file(acq->timeline, acq->point); + if (fd < 0) + VT_ERROR(_proto.comp->log, + "Failed to export acquire point %" PRIu64 " as sync_file.", + acq->point); + return fd; +} + +static void _acquire_destroy(struct vt_buffer_acquire_source_t *base) { + struct vt_drm_syncobj_acquire_t *acq = wl_container_of(base, acq, base); + _timeline_unref(&acq->timeline); + free(acq); +} + static void _waiter_finish(struct vt_drm_syncobj_acquire_waiter_t *w) { if (w->source) wl_event_source_remove(w->source); if (w->event_fd >= 0) close(w->event_fd); - _timeline_unref(&w->timeline); free(w); } @@ -218,42 +239,34 @@ static int _waiter_handle_ready(int fd, uint32_t mask, void *data) { (void)fd; (void)mask; struct vt_drm_syncobj_acquire_waiter_t *w = data; - struct vt_content_update_t *cu = w->cu; - - int fence_fd = _timeline_export_sync_file(w->timeline, w->point); - if (fence_fd < 0) { - VT_ERROR(_proto.comp->log, - "Failed to export acquire point %" PRIu64 " as sync_file.", - w->point); - } else if (cu->buffer_use) { - if (cu->buffer_use->acquire_fence_fd >= 0) - close(cu->buffer_use->acquire_fence_fd); - cu->buffer_use->acquire_fence_fd = fence_fd; - } else { - close(fence_fd); - } // detached before resolving, so cu teardown no longer frees the waiter - vt_content_update_resolve_constraint(cu, &w->constraint); + vt_content_update_resolve_constraint(w->cu, &w->constraint); _waiter_finish(w); return 0; } -/* The point may not have a fence yet when the client commits before - * submitting its GPU work. Hold the update until it does, then let the - * GPU-side fence wait cover the actual rendering. */ +/* The fence is only exported if the buffer is drawn or scanned out. The + * point may not exist yet when the client commits before submitting its GPU + * work: hold the update until it does. */ static bool _acquire_attach(struct vt_content_update_t *cu, struct vt_drm_syncobj_timeline_t *tl, uint64_t point) { - if (_timeline_point_available(tl, point)) { - int fence_fd = _timeline_export_sync_file(tl, point); - if (fence_fd < 0) - return false; - if (cu->acquire_fence_fd >= 0) - close(cu->acquire_fence_fd); - cu->acquire_fence_fd = fence_fd; + struct vt_drm_syncobj_acquire_t *acq = calloc(1, sizeof(*acq)); + if (!acq) + return false; + + acq->base.export_fence = _acquire_export; + acq->base.destroy = _acquire_destroy; + acq->timeline = _timeline_ref(tl); + acq->point = point; + + if (cu->acquire_source) + cu->acquire_source->destroy(cu->acquire_source); + cu->acquire_source = &acq->base; + + if (_timeline_point_available(tl, point)) return true; - } struct vt_drm_syncobj_acquire_waiter_t *w = calloc(1, sizeof(*w)); if (!w) @@ -261,8 +274,6 @@ static bool _acquire_attach(struct vt_content_update_t *cu, _proto.stats.waited++; w->cu = cu; - w->point = point; - w->timeline = _timeline_ref(tl); w->constraint.destroy = _waiter_constraint_destroy; w->event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); From f2c3933b9e6a2290540ca544ec71973312c4c988 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:32:08 +0200 Subject: [PATCH 47/57] fix(render): stop reading past format arrays on lookup misses --- src/render/drm_format.c | 67 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/render/drm_format.c b/src/render/drm_format.c index 34e5b2c..23c5688 100644 --- a/src/render/drm_format.c +++ b/src/render/drm_format.c @@ -179,6 +179,28 @@ void vt_drm_format_array_free(struct wl_array *formats) { wl_array_release(formats); } +/* wl_array_for_each leaves its cursor one past the end on a miss, so + * lookups must return NULL instead of the cursor */ +static const struct vt_drm_format_t * +_drm_format_array_find(const struct wl_array *formats, uint32_t format) { + const struct vt_drm_format_t *fmt; + wl_array_for_each(fmt, formats) { + if (fmt->format == format) + return fmt; + } + return NULL; +} + +static const struct vt_drm_format_modifier_t * +_drm_format_find_mod(const struct vt_drm_format_t *fmt, uint64_t mod) { + const struct vt_drm_format_modifier_t *it; + wl_array_for_each(it, &fmt->mods) { + if (it->mod == mod) + return it; + } + return NULL; +} + bool vt_drm_format_array_intersect(struct wl_array *dst, const struct wl_array *a, const struct wl_array *b) { @@ -187,17 +209,10 @@ bool vt_drm_format_array_intersect(struct wl_array *dst, const struct vt_drm_format_t *afmt; wl_array_for_each(afmt, a) { - const struct vt_drm_format_t *bfmt = NULL; - - wl_array_for_each(bfmt, b) { - if (afmt->format == bfmt->format) { - break; - } - } - - if (bfmt == NULL || afmt->format != bfmt->format) { + const struct vt_drm_format_t *bfmt = + _drm_format_array_find(b, afmt->format); + if (!bfmt) continue; - } struct vt_drm_format_t *ofmt = wl_array_add(&out, sizeof(*ofmt)); if (ofmt == NULL) { @@ -209,17 +224,10 @@ bool vt_drm_format_array_intersect(struct wl_array *dst, const struct vt_drm_format_modifier_t *amod; wl_array_for_each(amod, &afmt->mods) { - const struct vt_drm_format_modifier_t *bmod = NULL; - - wl_array_for_each(bmod, &bfmt->mods) { - if (amod->mod == bmod->mod) { - break; - } - } - - if (bmod == NULL || amod->mod != bmod->mod) { + const struct vt_drm_format_modifier_t *bmod = + _drm_format_find_mod(bfmt, amod->mod); + if (!bmod) continue; - } struct vt_drm_format_modifier_t *omod = vt_drm_format_add_mod(ofmt, amod->mod); @@ -289,14 +297,9 @@ bool vt_drm_format_array_union(struct wl_array *dst, const struct wl_array *a, } wl_array_for_each(fmt, b) { - struct vt_drm_format_t *ofmt = NULL; - - wl_array_for_each(ofmt, &out) { - if (ofmt->format == fmt->format) - break; - } + struct vt_drm_format_t *ofmt = vt_drm_format_array_get_format(&out, fmt); - if (ofmt == NULL || ofmt->format != fmt->format) { + if (!ofmt) { if (!vt_drm_format_array_push(&out, fmt)) { vt_drm_format_array_free(&out); return false; @@ -307,14 +310,10 @@ bool vt_drm_format_array_union(struct wl_array *dst, const struct wl_array *a, const struct vt_drm_format_modifier_t *mod; wl_array_for_each(mod, &fmt->mods) { - struct vt_drm_format_modifier_t *omod = NULL; - - wl_array_for_each(omod, &ofmt->mods) { - if (omod->mod == mod->mod) - break; - } + struct vt_drm_format_modifier_t *omod = + vt_drm_format_get_mod(ofmt, mod->mod); - if (omod != NULL && omod->mod == mod->mod) { + if (omod) { /* * It is extension-only only if neither side has a normal * representation of this modifier. From 2c0c374203cc21fa916f2bf51cf391c2ce62d678 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:33:03 +0200 Subject: [PATCH 48/57] feat(render): count acquire fences still pending at draw time --- src/render/gl/egl_gl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/render/gl/egl_gl.c b/src/render/gl/egl_gl.c index 94ec557..8bfd439 100644 --- a/src/render/gl/egl_gl.c +++ b/src/render/gl/egl_gl.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,9 @@ struct egl_backend_state_t { bool has_swap_buffers_with_damage_khr; bool has_swap_buffers_with_damage_ext; + // acquire fences seen at draw time, and how many had not signaled yet + uint64_t fenced_draws, fence_pending_draws; + RnState *render; struct wl_array formats; @@ -496,6 +500,12 @@ bool _egl_buffer_use_is_ready(struct vt_renderer_t *renderer, !egl->has_explicit_sync_support) return true; + // a pending fence here makes the composite wait on the client's GPU work + struct pollfd pfd = {.fd = use->acquire_fence_fd, .events = POLLIN}; + egl->fenced_draws++; + if (poll(&pfd, 1, 0) == 0) + egl->fence_pending_draws++; + EGLint attribs[] = { EGL_SYNC_NATIVE_FENCE_FD_ANDROID, use->acquire_fence_fd, @@ -1658,6 +1668,11 @@ bool renderer_destroy_egl(struct vt_renderer_t *r) { vt_drm_format_array_free(&egl->formats); + VT_INFO(r->comp->log, + "%" PRIu64 " draws waited on an acquire fence, %" PRIu64 + " of them not yet signaled.", + egl->fenced_draws, egl->fence_pending_draws); + rn_terminate(egl->render); eglMakeCurrent(egl->egl_dsp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); From 1d5789d42123aecd111a3ed7fa6b73064bf90fcc Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 13:58:59 +0200 Subject: [PATCH 49/57] feat(drm): report syncobj pending exports, log stats on suspend --- src/backends/drm/drm_backend.c | 5 +++++ src/protocols/linux_drm_syncobj.c | 24 +++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 8549b89..663848a 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -767,6 +767,8 @@ static bool _drm_suspend(struct vt_drm_backend_state_t *backend) { if (!backend || !backend->comp || backend->drm_fd < 0 || !backend->impl) return false; + vt_proto_linux_drm_syncobj_v1_log_stats(); + VT_TRACE(backend->comp->log, "Suspending seat session (VT switch away)..."); struct vt_output_t *output; @@ -784,6 +786,9 @@ static bool _drm_suspend(struct vt_drm_backend_state_t *backend) { struct vt_drm_output_state_t *drm_output = BACKEND_DATA(output, struct vt_drm_output_state_t); + // a VT switch often ends a run, report what was measured so far + _drm_frame_stats_log(drm_output); + if (!backend->impl->disable(backend, drm_output)) { VT_WARN(backend->comp->log, "Failed to disable connector %u while suspending.", diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c index 3f0b341..8bca7db 100644 --- a/src/protocols/linux_drm_syncobj.c +++ b/src/protocols/linux_drm_syncobj.c @@ -77,7 +77,8 @@ static struct { // per-run counters, reported at teardown struct { - uint64_t commits, waited, exported, release_imported, release_signaled; + uint64_t commits, waited, exported, exported_pending; + uint64_t release_imported, release_signaled; } stats; } _proto = {.drm_fd = -1}; @@ -109,6 +110,15 @@ static bool _timeline_point_available(struct vt_drm_syncobj_timeline_t *tl, NULL) == 0; } +// absolute timeout of 0 makes this a non-blocking check +static bool _timeline_point_signaled(struct vt_drm_syncobj_timeline_t *tl, + uint64_t point) { + uint32_t handle = tl->handle; + return drmSyncobjTimelineWait(tl->drm_fd, &handle, &point, 1, 0, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, + NULL) == 0; +} + // sync_files are what the EGL and KMS fence paths consume static int _timeline_export_sync_file(struct vt_drm_syncobj_timeline_t *tl, uint64_t point) { @@ -116,7 +126,10 @@ static int _timeline_export_sync_file(struct vt_drm_syncobj_timeline_t *tl, if (drmSyncobjCreate(tl->drm_fd, 0, &tmp) != 0) return -1; + // exports happen at draw time: a pending point stalls the composite _proto.stats.exported++; + if (!_timeline_point_signaled(tl, point)) + _proto.stats.exported_pending++; int fd = -1; if (drmSyncobjTransfer(tl->drm_fd, tmp, 0, tl->handle, point, 0) != 0 || drmSyncobjExportSyncFile(tl->drm_fd, tmp, &fd) != 0) @@ -620,9 +633,10 @@ void vt_proto_linux_drm_syncobj_v1_log_stats(void) { return; VT_INFO(_proto.comp->log, - "syncobj: %" PRIu64 " commits, %" PRIu64 " waited for submit, %" PRIu64 - " acquire exports, %" PRIu64 " release imports, %" PRIu64 - " release signals.", + "syncobj: %" PRIu64 " commits, %" PRIu64 " held, %" PRIu64 + " acquire exports (%" PRIu64 " still pending), %" PRIu64 + " release imports, %" PRIu64 " release signals.", _proto.stats.commits, _proto.stats.waited, _proto.stats.exported, - _proto.stats.release_imported, _proto.stats.release_signaled); + _proto.stats.exported_pending, _proto.stats.release_imported, + _proto.stats.release_signaled); } From 66f45390941162d8af2855d6adcc37b3858662e2 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 14:09:25 +0200 Subject: [PATCH 50/57] feat(proto): count syncobj releases signaled past a lower pending point --- src/protocols/linux_drm_syncobj.c | 36 +++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c index 8bca7db..e32ffd5 100644 --- a/src/protocols/linux_drm_syncobj.c +++ b/src/protocols/linux_drm_syncobj.c @@ -48,6 +48,9 @@ struct vt_drm_syncobj_timeline_t { int drm_fd; uint32_t handle; uint32_t refcount; + + // release points on this timeline not signaled yet + struct wl_list releases; }; struct vt_drm_syncobj_release_t { @@ -55,6 +58,7 @@ struct vt_drm_syncobj_release_t { struct vt_drm_syncobj_timeline_t *timeline; uint64_t point; bool signaled; + struct wl_list link; }; struct vt_drm_syncobj_acquire_t { @@ -79,6 +83,7 @@ static struct { struct { uint64_t commits, waited, exported, exported_pending; uint64_t release_imported, release_signaled; + uint64_t timelines, release_early; } stats; } _proto = {.drm_fd = -1}; @@ -172,11 +177,27 @@ static bool _release_needs_fence(struct vt_buffer_release_t *base) { return true; } +/* Signaling a point signals every earlier one on its timeline. With a + * timeline shared between buffers, that frees buffers still being read. */ +static void _release_mark_signaled(struct vt_drm_syncobj_release_t *rel) { + struct vt_drm_syncobj_release_t *other; + wl_list_for_each(other, &rel->timeline->releases, link) { + if (other != rel && other->point < rel->point) { + _proto.stats.release_early++; + break; + } + } + + rel->signaled = true; + wl_list_remove(&rel->link); + wl_list_init(&rel->link); +} + static void _release_finish(struct vt_buffer_release_t *base, int release_fence_fd) { struct vt_drm_syncobj_release_t *rel = wl_container_of(base, rel, base); - rel->signaled = true; + _release_mark_signaled(rel); if (release_fence_fd < 0) { _timeline_signal(rel->timeline, rel->point); @@ -200,8 +221,10 @@ static void _release_destroy(struct vt_buffer_release_t *base) { struct vt_drm_syncobj_release_t *rel = wl_container_of(base, rel, base); // dropped before ever being used, the buffer was never read - if (!rel->signaled) + if (!rel->signaled) { + _release_mark_signaled(rel); _timeline_signal(rel->timeline, rel->point); + } _timeline_unref(&rel->timeline); free(rel); @@ -383,6 +406,7 @@ static bool _addon_commit(struct vt_surface_t *surf, vt_buffer_release_init_and_ref(&rel->base, surf->comp, &_release_impl); rel->timeline = _timeline_ref(s->release_timeline); rel->point = s->release_point; + wl_list_insert(&rel->timeline->releases, &rel->link); if (cu->state.buffer_release) vt_buffer_release_unref(&cu->state.buffer_release); @@ -557,6 +581,8 @@ static void _manager_import_timeline(struct wl_client *client, tl->drm_fd = _proto.drm_fd; tl->refcount = 1; + wl_list_init(&tl->releases); + _proto.stats.timelines++; int ret = drmSyncobjFDToHandle(tl->drm_fd, fd, &tl->handle); close(fd); @@ -635,8 +661,10 @@ void vt_proto_linux_drm_syncobj_v1_log_stats(void) { VT_INFO(_proto.comp->log, "syncobj: %" PRIu64 " commits, %" PRIu64 " held, %" PRIu64 " acquire exports (%" PRIu64 " still pending), %" PRIu64 - " release imports, %" PRIu64 " release signals.", + " release imports, %" PRIu64 " release signals, %" PRIu64 + " timelines, %" PRIu64 " releases past a lower pending point.", _proto.stats.commits, _proto.stats.waited, _proto.stats.exported, _proto.stats.exported_pending, _proto.stats.release_imported, - _proto.stats.release_signaled); + _proto.stats.release_signaled, _proto.stats.timelines, + _proto.stats.release_early); } From 80e1f44d0b10f2adfbae35732377a70cc67d43e7 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 14:13:44 +0200 Subject: [PATCH 51/57] feat(drm): VT_DRM_NO_DIRECT_SCANOUT switch, count direct scanout layers --- compbench.sh | 2 +- scripts/bench-drm.sh | 1 + src/backends/drm/drm_backend.c | 17 +++++++++++++++-- src/backends/drm/drm_types.h | 4 ++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/compbench.sh b/compbench.sh index 7c0dcce..09bb721 100755 --- a/compbench.sh +++ b/compbench.sh @@ -25,7 +25,7 @@ wl) ;; drm) # build stays user-owned, only the VT session needs root - sudo --preserve-env=VERBOSE,TIMEOUT,VT,VORTEX_ARGS scripts/bench-drm.sh "$BUILD" "$@" + sudo --preserve-env=VERBOSE,TIMEOUT,VT,VORTEX_ARGS,VT_DRM_NO_DIRECT_SCANOUT scripts/bench-drm.sh "$BUILD" "$@" ;; *) echo "unknown mode '$MODE' (wl|drm)" >&2 diff --git a/scripts/bench-drm.sh b/scripts/bench-drm.sh index 5c15329..7f897fc 100755 --- a/scripts/bench-drm.sh +++ b/scripts/bench-drm.sh @@ -35,6 +35,7 @@ systemd-run --unit="$UNIT" --wait --collect \ -p WorkingDirectory="$ROOT" \ --setenv=TIMEOUT="$TIMEOUT" --setenv=VERBOSE="${VERBOSE:-0}" \ --setenv=VORTEX_ARGS="${VORTEX_ARGS:-}" \ + --setenv=VT_DRM_NO_DIRECT_SCANOUT="${VT_DRM_NO_DIRECT_SCANOUT:-}" \ "$ROOT/scripts/bench-run.sh" "$BUILD" drm "$@" || status=$? # unit stdout did not reach the journal in testing, read results back diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 663848a..798a66f 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -315,10 +315,13 @@ static void _drm_frame_stats_log(struct vt_drm_output_state_t *drm_output) { VT_INFO(comp->log, "CRTC %u (%u Hz): %" PRIu64 " of %" PRIu64 - " frames missed a vblank, worst interval %.2f ms.", + " frames missed a vblank, worst interval %.2f ms, %" PRIu64 + " direct scanout layers (%" PRIu64 " with IN_FENCE_FD).", drm_output->crtc->id, drm_output->mode.vrefresh, drm_output->frame_stats.missed, drm_output->frame_stats.frames, - drm_output->frame_stats.worst_us / 1000.0); + drm_output->frame_stats.worst_us / 1000.0, + drm_output->frame_stats.direct_layers, + drm_output->frame_stats.direct_fenced); } static void _drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec, @@ -927,6 +930,9 @@ static bool _drm_init_for_device(struct vt_compositor_t *comp, bool use_atomic = getenv("VT_DRM_NO_ATOMIC") ? false : drm->caps[VT_DRM_CAP_ATOMIC_MODESET]; + // the bench forwards unset variables as empty strings + const char *no_direct = getenv("VT_DRM_NO_DIRECT_SCANOUT"); + drm->no_direct_scanout = no_direct && *no_direct; drm->impl = use_atomic ? &drm_kms_atomic_impl : &drm_kms_legacy_impl; _drm_update_tearing_cap(drm); @@ -3358,6 +3364,9 @@ bool backend_test_output_layers_drm(struct vt_backend_t *backend, struct vt_dmabuf_attr_t attr = {0}; + if (drm->no_direct_scanout) + goto composited; + /* 1. Prove layer has a DMABUF */ if (!vt_buffer_get_dmabuf(use->buf, &attr)) { VT_TRACE(backend->comp->log, @@ -3567,6 +3576,10 @@ bool backend_test_output_layers_drm(struct vt_backend_t *backend, goto fail_plan; planned->acquire_fence_fd = vt_buffer_use_acquire_fence(test->use); + + drm_output->frame_stats.direct_layers++; + if (planned->acquire_fence_fd >= 0) + drm_output->frame_stats.direct_fenced++; } else { /* * the renderer is going to draw this surface into the compositor FB. diff --git a/src/backends/drm/drm_types.h b/src/backends/drm/drm_types.h index b3809a8..e1ca76b 100644 --- a/src/backends/drm/drm_types.h +++ b/src/backends/drm/drm_types.h @@ -174,6 +174,9 @@ struct vt_drm_backend_state_t { bool caps[VT_DRM_CAP__COUNT]; + // VT_DRM_NO_DIRECT_SCANOUT: composite every surface + bool no_direct_scanout; + struct wl_array sampling_formats; drmModeRes *res; @@ -238,6 +241,7 @@ struct vt_drm_output_state_t { struct { uint64_t last_us, worst_us; uint64_t frames, missed; + uint64_t direct_layers, direct_fenced; bool busy; } frame_stats; bool modeset_bootstrapped; From 0eeb1e9adb4cfe403bd79555df8ee2a9c3d4aa6c Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 21:14:13 +0200 Subject: [PATCH 52/57] chore(proto): document why syncobj holds only until submitted holding until signaled starved glmark2 of buffers (8.8k vs 31.7k) --- src/protocols/linux_drm_syncobj.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/protocols/linux_drm_syncobj.c b/src/protocols/linux_drm_syncobj.c index e32ffd5..729790f 100644 --- a/src/protocols/linux_drm_syncobj.c +++ b/src/protocols/linux_drm_syncobj.c @@ -282,9 +282,10 @@ static int _waiter_handle_ready(int fd, uint32_t mask, void *data) { return 0; } -/* The fence is only exported if the buffer is drawn or scanned out. The - * point may not exist yet when the client commits before submitting its GPU - * work: hold the update until it does. */ +/* Hold the update only until the client has submitted its GPU work, the + * fence wait (EGL or KMS IN_FENCE_FD) covers the rest. Holding until + * signaled starves fast clients of buffers: releases queue behind the held + * commits. The fence itself is only exported if the buffer gets used. */ static bool _acquire_attach(struct vt_content_update_t *cu, struct vt_drm_syncobj_timeline_t *tl, uint64_t point) { From c807c19a979f97cb8d682928009891fbf5ca0aa8 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 14:28:28 +0200 Subject: [PATCH 53/57] fix(drm): scan out directly only once the acquire fence signaled * KMS holds the whole flip on IN_FENCE_FD, an uncapped client stalled it up to 206 ms * late surfaces stay composited for 60 frames to avoid plane flapping --- src/backends/drm/drm_backend.c | 27 +++++++++++++++++++++++++++ src/core/surface.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/backends/drm/drm_backend.c b/src/backends/drm/drm_backend.c index 798a66f..cd12422 100644 --- a/src/backends/drm/drm_backend.c +++ b/src/backends/drm/drm_backend.c @@ -734,6 +734,26 @@ _drm_build_dmabuf_feedback(struct vt_drm_backend_master_state_t *master, return false; } +#define VT_DRM_SCANOUT_BACKOFF_FRAMES 60 + +static bool _drm_scanout_fence_ready(struct vt_surface_t *surface, + struct vt_buffer_use_t *use) { + int fd = vt_buffer_use_acquire_fence(use); + if (fd >= 0) { + struct pollfd pfd = {.fd = fd, .events = POLLIN}; + if (poll(&pfd, 1, 0) <= 0) { + surface->scanout_backoff = VT_DRM_SCANOUT_BACKOFF_FRAMES; + return false; + } + } + + if (surface->scanout_backoff > 0) { + surface->scanout_backoff--; + return false; + } + return true; +} + static bool _drm_any_flip_inflight(struct vt_drm_backend_state_t *drm) { struct vt_output_t *output; wl_list_for_each(output, &drm->outputs, link_local) { @@ -3367,6 +3387,13 @@ bool backend_test_output_layers_drm(struct vt_backend_t *backend, if (drm->no_direct_scanout) goto composited; + /* A KMS flip holds on IN_FENCE_FD, so an unfinished client frame would + * stall the whole output. Compositing waits on the GPU instead. Back off + * for a while so a client that is late half the time does not flap + * between plane and composition every frame. */ + if (!_drm_scanout_fence_ready(layer->surface, use)) + goto composited; + /* 1. Prove layer has a DMABUF */ if (!vt_buffer_get_dmabuf(use->buf, &attr)) { VT_TRACE(backend->comp->log, diff --git a/src/core/surface.h b/src/core/surface.h index 12376e0..d47955e 100644 --- a/src/core/surface.h +++ b/src/core/surface.h @@ -140,6 +140,9 @@ struct vt_surface_t { struct vt_buffer_use_t *current_buf_use; + // frames to stay composited after a late acquire fence + uint32_t scanout_backoff; + struct wl_list content_updates; struct vt_scene_node_t *scene_node; From 039f7a987de7fce0741ff38c2a507069686616c8 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 14:34:53 +0200 Subject: [PATCH 54/57] chore(docs): document VT_DRM_NO_DIRECT_SCANOUT --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7c86ed2..88c6f08 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ Vortex handles the following environment variables: |--------|--------------| | `VT_DRM_NO_ATOMIC` | Force legacy DRM/KMS implementation | | `VT_DRM_NO_HARDWARE_CURSOR` | Force cursor compositing | +| `VT_DRM_NO_DIRECT_SCANOUT` | Composite every surface, never put client buffers on hardware planes. Surfaces whose frame is not finished are already composited automatically, this forces it always | ## Notes - [`runara`](https://github.com/cococry/runara) is currently vendored into `subprojects/` but might not be in the future. From a6a31167415421c2dffb8ac365662503365b75ee Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 14:35:09 +0200 Subject: [PATCH 55/57] document knob Signed-off-by: h8d13 --- README.md | 2 +- references/sway | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 references/sway diff --git a/README.md b/README.md index 88c6f08..42f646c 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ vortex -bp /usr/lib/vortex/backends/libcustom.so ### Environment Variables Vortex handles the following environment variables: -| Env | Description | +| Env | Description | |--------|--------------| | `VT_DRM_NO_ATOMIC` | Force legacy DRM/KMS implementation | | `VT_DRM_NO_HARDWARE_CURSOR` | Force cursor compositing | diff --git a/references/sway b/references/sway new file mode 160000 index 0000000..1652c54 --- /dev/null +++ b/references/sway @@ -0,0 +1 @@ +Subproject commit 1652c54b73f67df17b7b4ab0b0f7048204aa8104 From 714000533b915572445a5c9fdfea8a3f37673ae2 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 15:02:41 +0200 Subject: [PATCH 56/57] pin CPUs for reproducible tests Signed-off-by: h8d13 --- compbench.sh | 2 +- scripts/bench-drm.sh | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compbench.sh b/compbench.sh index 09bb721..49dabfc 100755 --- a/compbench.sh +++ b/compbench.sh @@ -25,7 +25,7 @@ wl) ;; drm) # build stays user-owned, only the VT session needs root - sudo --preserve-env=VERBOSE,TIMEOUT,VT,VORTEX_ARGS,VT_DRM_NO_DIRECT_SCANOUT scripts/bench-drm.sh "$BUILD" "$@" + sudo --preserve-env=VERBOSE,TIMEOUT,VT,VORTEX_ARGS,VT_DRM_NO_DIRECT_SCANOUT,CPUS scripts/bench-drm.sh "$BUILD" "$@" ;; *) echo "unknown mode '$MODE' (wl|drm)" >&2 diff --git a/scripts/bench-drm.sh b/scripts/bench-drm.sh index 7f897fc..ef987a7 100755 --- a/scripts/bench-drm.sh +++ b/scripts/bench-drm.sh @@ -24,9 +24,16 @@ trap 'chvt "$ORIG_VT"' EXIT echo "switching to VT $VT, back to VT $ORIG_VT when done" chvt "$VT" +# hybrid CPUs: P/E core placement moves scores, CPUS=0-3 pins a run. +# the unit is spawned by PID 1, so taskset on this script would not carry +AFFINITY=() +if [[ -n "${CPUS:-}" ]]; then + AFFINITY=(-p CPUAffinity="$CPUS") +fi + # RuntimeMaxSec backs up the vortex watchdog: the unit dying frees the VT status=0 -systemd-run --unit="$UNIT" --wait --collect \ +systemd-run --unit="$UNIT" --wait --collect "${AFFINITY[@]}" \ -p PAMName=login -p User="$SUDO_USER" \ -p TTYPath="/dev/tty$VT" -p StandardInput=tty-force \ -p StandardOutput=journal -p StandardError=journal \ From 9a9b9daa4fa8899c82dcc1cfb04ebad6d7b5341d Mon Sep 17 00:00:00 2001 From: h8d13 Date: Fri, 16 Oct 2026 21:28:39 +0200 Subject: [PATCH 57/57] rem useless comment Signed-off-by: h8d13 --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index d28ed39..b4e5170 100644 --- a/meson.build +++ b/meson.build @@ -26,7 +26,6 @@ dep_wayland_egl = dependency('wayland-egl', required: true) dep_wayland_protos = dependency('wayland-protocols', required: true) dep_egl = dependency('egl', required: true) dep_udev = dependency('libudev', required: true) -# syncobj timelines are imported in core protocol code dep_libdrm = dependency('libdrm', required: true) dep_gl = dependency('gl', required: true) dep_pixman = dependency('pixman-1', required: true)