Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
0e3fcec
add stupid helper.
h8d13 Oct 16, 2026
6845e19
feat(bench): glmark2 bench on nested and real DRM
h8d13 Oct 16, 2026
31d8f12
fix(input): wait for nested keymap, bind keybinds once
h8d13 Oct 16, 2026
9518400
fix(input): drop leaked keymap ref per keyboard bind
h8d13 Oct 16, 2026
6d79527
fix(input): honor XKB_DEFAULT_* env on DRM
h8d13 Oct 16, 2026
4131107
fix(input): return status from libinput suspend/resume
h8d13 Oct 16, 2026
c689bcc
fix(input): send modifiers after late keyboard enter
h8d13 Oct 16, 2026
87a34d2
fix(drm): one liftoff device per CRTC
h8d13 Oct 16, 2026
40efde3
fix(drm): zero CRTC state on init
h8d13 Oct 16, 2026
350ccc1
fix(drm): check device path content, not array address
h8d13 Oct 16, 2026
3a3a6dc
fix(dmabuf): close params before freeing them
h8d13 Oct 16, 2026
b3836b2
fix(dmabuf): free packed feedback on error paths
h8d13 Oct 16, 2026
79f0beb
fix(dmabuf): log feedback before freeing it
h8d13 Oct 16, 2026
fa05c2b
fix(session): check udev devnode instead of action twice
h8d13 Oct 16, 2026
eaf09da
fix(session): unref udev device after handled event
h8d13 Oct 16, 2026
d4e7d07
fix(log): use correct specifiers for size_t and dev_t
h8d13 Oct 16, 2026
106acec
fix(log): keep log stream when logfile cannot be opened
h8d13 Oct 16, 2026
305cf84
fix(core): parenthesize VT_MAX and VT_MIN
h8d13 Oct 16, 2026
37b4e2a
fix(scene): use signed math for output overlap
h8d13 Oct 16, 2026
dcb6f3e
fix(core): forward-declare structs used in prototypes
h8d13 Oct 16, 2026
cd2438a
fix(wl): end session when nested window closes
h8d13 Oct 16, 2026
eaf9f05
fix(render): return early when begin_scene has no output
h8d13 Oct 16, 2026
eb2baaf
fix(drm): reject negative shm cursor dimensions
h8d13 Oct 16, 2026
0b92325
chore: cast %p log arguments to void *
h8d13 Oct 16, 2026
29bc2ce
chore: mark unused callback parameters
h8d13 Oct 16, 2026
f4cf1b7
chore(input): replace xkb mod mask macro with inline helper
h8d13 Oct 16, 2026
2a12363
chore: remove dead code and unused locals
h8d13 Oct 16, 2026
a518f8a
chore: const-correct attachment and release callbacks
h8d13 Oct 16, 2026
573010c
chore: fix sign-compare and pedantic warnings
h8d13 Oct 16, 2026
2505378
chore(ci): build with -Werror
h8d13 Oct 16, 2026
89e1e9e
fix(build): include headers for used libc functions
h8d13 Oct 16, 2026
eac4565
fix(core): shut down cleanly on SIGTERM and SIGINT
h8d13 Oct 16, 2026
081e26f
fix(bench): detect socket by mtime, make trace logging opt-in
h8d13 Oct 16, 2026
9fa33bd
fix(drm): poll for flip events before draining on suspend/teardown
h8d13 Oct 16, 2026
cedf721
match pattern to build
h8d13 Oct 16, 2026
0c80ee4
feat(core): content update constraints with in-order apply
h8d13 Oct 16, 2026
297e6d4
feat(proto): implement linux-drm-syncobj-v1
h8d13 Oct 16, 2026
1857207
fix(bench): forward VERBOSE, TIMEOUT, VT through sudo
h8d13 Oct 16, 2026
67a5044
feat(drm): count missed vblanks per output, report at teardown
h8d13 Oct 16, 2026
5543b97
fix(core): stop warning per repaint request while suspended
h8d13 Oct 16, 2026
f408a62
fix(drm): ignore EAGAIN when dispatching DRM events
h8d13 Oct 16, 2026
8878346
feat(bench): pass VORTEX_ARGS through to vortex for A/B runs
h8d13 Oct 16, 2026
10b5bdd
feat(core): add INFO log level for summaries
h8d13 Oct 16, 2026
bcad6d0
feat(drm): report CPU time and syncobj counters at teardown
h8d13 Oct 16, 2026
2cfc0b9
feat(core): defer acquire fence export until a buffer is used
h8d13 Oct 16, 2026
2f4474f
fix(proto): export syncobj acquire fences only for drawn buffers
h8d13 Oct 16, 2026
f2c3933
fix(render): stop reading past format arrays on lookup misses
h8d13 Oct 16, 2026
2c0c374
feat(render): count acquire fences still pending at draw time
h8d13 Oct 16, 2026
1d5789d
feat(drm): report syncobj pending exports, log stats on suspend
h8d13 Oct 16, 2026
66f4539
feat(proto): count syncobj releases signaled past a lower pending point
h8d13 Oct 16, 2026
80e1f44
feat(drm): VT_DRM_NO_DIRECT_SCANOUT switch, count direct scanout layers
h8d13 Oct 16, 2026
0eeb1e9
chore(proto): document why syncobj holds only until submitted
h8d13 Oct 16, 2026
c807c19
fix(drm): scan out directly only once the acquire fence signaled
h8d13 Oct 16, 2026
039f7a9
chore(docs): document VT_DRM_NO_DIRECT_SCANOUT
h8d13 Oct 16, 2026
a6a3116
document knob
h8d13 Oct 16, 2026
7140005
pin CPUs for reproducible tests
h8d13 Oct 16, 2026
9a9b9da
rem useless comment
h8d13 Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
./build
./build*
.cache
*.json
subprojects/cglm-*/
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,11 @@ 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 |
| `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.
Expand Down
34 changes: 34 additions & 0 deletions compbench.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# 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
COMMIT="$(git rev-parse --short HEAD)"
BUILD="build-$BRANCH-$COMMIT"

# meson conf
meson setup --reconfigure "$BUILD"
# meson comp
meson compile -C "$BUILD"

# -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 --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
exit 1
;;
esac
26 changes: 24 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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)
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)
Expand All @@ -37,6 +38,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',
Expand Down Expand Up @@ -85,6 +87,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',
Expand All @@ -101,6 +119,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',
Expand Down Expand Up @@ -145,7 +165,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
Expand All @@ -171,7 +191,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'],
Expand Down
1 change: 1 addition & 0 deletions references/sway
Submodule sway added at 1652c5
53 changes: 53 additions & 0 deletions scripts/bench-drm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/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 <build-dir> [glmark2 args...]

set -euo pipefail

if [[ "$EUID" -ne 0 || -z "${SUDO_USER:-}" ]]; then
echo "run through sudo: sudo $0 <build-dir> [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"

# 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 "${AFFINITY[@]}" \
-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" --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
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"
85 changes: 85 additions & 0 deletions scripts/bench-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# run vortex from a build dir, attach glmark2-wayland, save score.
# usage: bench-run.sh <build-dir> <wl|drm> [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"
# a failed run must not leave the previous score looking current
rm -f "$OUT"

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

# trace logging costs frames, only turn it on when debugging
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
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" "${LOG_ARGS[@]}" "${VT_ARGS[@]}" --backend-path "$BACKEND" \
>"$LOG" 2>&1 &
VPID=$!
trap 'kill "$VPID" || true; wait "$VPID" || true; rm -f "$MARK"' EXIT

# 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
echo "vortex exited early, log: $LOG" >&2
cat "$LOG"
exit 1
fi
SOCK="$(new_socket)"
if [[ -n "$SOCK" ]]; then
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"
2 changes: 1 addition & 1 deletion src/backends/drm/atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading
Loading