From d7b574232a51cf855befee26880b371c9cb04a7f Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Sun, 8 Mar 2026 23:25:14 +0000 Subject: [PATCH] thirdparty,sokol: fix frame pacing issues on xwayland sessions, prevent sending some keycode events on wayland (#26706) --- .github/workflows/gg_regressions_ci.yml | 2 +- .github/workflows/module_docs_ci.yml | 2 +- .github/workflows/other_ci.yml | 2 +- .../v_apps_and_modules_compile_ci.yml | 4 ++-- thirdparty/sokol/gen_wayland_protocols.vsh | 1 + thirdparty/sokol/sokol_app.h | 22 +++++++++++++++++++ vlib/gg/gg.c.v | 4 +++- vlib/sokol/c/declaration.c.v | 9 +++++++- 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gg_regressions_ci.yml b/.github/workflows/gg_regressions_ci.yml index 84e379967..33701af97 100644 --- a/.github/workflows/gg_regressions_ci.yml +++ b/.github/workflows/gg_regressions_ci.yml @@ -50,7 +50,7 @@ jobs: # libx11-dev : X11 headers for clipboard and sokol (default backend on Linux) .github/workflows/disable_azure_mirror.sh ./v retry -- sudo apt update - ./v retry -- sudo apt install imagemagick openimageio-tools libgl1-mesa-dri xvfb libxcursor-dev libxi-dev libxrandr-dev freeglut3-dev xsel xclip libx11-dev + ./v retry -- sudo apt install imagemagick openimageio-tools libgl1-mesa-dri xvfb libxcursor-dev libxi-dev libxrandr-dev freeglut3-dev xsel xclip libx11-dev libegl-dev ./v retry -- ./v download https://raw.githubusercontent.com/tremby/imgur.sh/c98345d/imgur.sh ./v retry -- git clone https://github.com/Larpon/gg-regression-images gg-regression-images chmod +x ./imgur.sh diff --git a/.github/workflows/module_docs_ci.yml b/.github/workflows/module_docs_ci.yml index 1578d97a5..57e816d48 100644 --- a/.github/workflows/module_docs_ci.yml +++ b/.github/workflows/module_docs_ci.yml @@ -43,7 +43,7 @@ jobs: libsodium-dev libasound2-dev libssl-dev \ sqlite3 libsqlite3-dev libfreetype6-dev \ libx11-dev libxi-dev freeglut3-dev \ - libgl1-mesa-dri libxcursor-dev libgl-dev libxrandr-dev + libgl1-mesa-dri libxcursor-dev libgl-dev libxrandr-dev libegl-dev - name: Install markdown from vpm run: v retry -- v install markdown - name: Test v doc diff --git a/.github/workflows/other_ci.yml b/.github/workflows/other_ci.yml index 3c44d0f6d..9d1db0be8 100644 --- a/.github/workflows/other_ci.yml +++ b/.github/workflows/other_ci.yml @@ -122,7 +122,7 @@ jobs: .github/workflows/disable_azure_mirror.sh ./v retry -- sudo apt update ./v retry -- sudo apt install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev postgresql libpq-dev valgrind - ./v retry -- sudo apt install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev libxrandr-dev libasound2-dev xfonts-75dpi xfonts-base + ./v retry -- sudo apt install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev libxrandr-dev libasound2-dev xfonts-75dpi xfonts-base libegl-dev ./v retry -- sudo apt install --quiet -y g++-9 g++-10 - name: g++-9 version diff --git a/.github/workflows/v_apps_and_modules_compile_ci.yml b/.github/workflows/v_apps_and_modules_compile_ci.yml index f6dfa0a09..0a9deb023 100644 --- a/.github/workflows/v_apps_and_modules_compile_ci.yml +++ b/.github/workflows/v_apps_and_modules_compile_ci.yml @@ -38,10 +38,10 @@ jobs: - name: Install dependencies run: | if [ "$RUNNER_OS" == 'Linux' ]; then - .github/workflows/disable_azure_mirror.sh + .github/workflows/disable_azure_mirror.sh v retry -- sudo apt -qq update v retry -- sudo apt -qq install libgc-dev libsodium-dev libssl-dev sqlite3 libsqlite3-dev - v retry -- sudo apt -qq install libfreetype6-dev libxi-dev libxcursor-dev libgl-dev libxrandr-dev libasound2-dev xfonts-75dpi xfonts-base + v retry -- sudo apt -qq install libfreetype6-dev libxi-dev libxcursor-dev libgl-dev libxrandr-dev libasound2-dev xfonts-75dpi xfonts-base libegl-dev v retry -- sudo apt -qq install sassc libgit2-dev # needed by gitly else v retry brew install sassc libgit2 diff --git a/thirdparty/sokol/gen_wayland_protocols.vsh b/thirdparty/sokol/gen_wayland_protocols.vsh index 881634e0b..4097bb2da 100644 --- a/thirdparty/sokol/gen_wayland_protocols.vsh +++ b/thirdparty/sokol/gen_wayland_protocols.vsh @@ -1,4 +1,5 @@ #!/usr/bin/env -S v + import log fn sh(cmd string) { diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h index d162a978a..be610d837 100644 --- a/thirdparty/sokol/sokol_app.h +++ b/thirdparty/sokol/sokol_app.h @@ -15138,8 +15138,30 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc *desc) { XNextEvent(_sapp.x11.display, &event); _sapp_x11_process_event(&event); } + /* Record the time before swap so we can measure how long the frame took. + If eglSwapBuffers / glXSwapBuffers blocks for vsync (native X11 with a + real GPU driver) the elapsed time will be close to a full frame period + and the poll below will time out immediately, adding no extra delay. + If swap returns without blocking (XWayland / wlroots where vblank + forwarding for X11 clients is unreliable) the elapsed time will be near + zero and poll will sleep for the remainder of the frame budget, keeping + CPU use low without busy-spinning. */ + struct timespec frame_start_ts; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &frame_start_ts); _sapp_linux_frame(); XFlush(_sapp.x11.display); + if (XPending(_sapp.x11.display) == 0) { + struct timespec frame_end_ts; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &frame_end_ts); + const long elapsed_ms = (frame_end_ts.tv_sec - frame_start_ts.tv_sec) * 1000L + + (frame_end_ts.tv_nsec - frame_start_ts.tv_nsec) / 1000000L; + const long frame_ms = (long)((1000.0 / 60.0) * _sapp.swap_interval); + const long remaining_ms = frame_ms - elapsed_ms; + if (remaining_ms > 0) { + struct pollfd x11_fd = { ConnectionNumber(_sapp.x11.display), POLLIN, 0 }; + poll(&x11_fd, 1, (int)remaining_ms); + } + } // handle quit-requested, either from window or from sapp_request_quit() if (_sapp.quit_requested && !_sapp.quit_ordered) { // give user code a chance to intervene diff --git a/vlib/gg/gg.c.v b/vlib/gg/gg.c.v index f00499c36..7246b84ca 100644 --- a/vlib/gg/gg.c.v +++ b/vlib/gg/gg.c.v @@ -481,9 +481,11 @@ fn gg_event_fn(ce voidptr, user_data voidptr) { // dump(e) } } - $if linux { + $if linux && !sokol_wayland ? { if e.typ == .key_down && e.key_code in [.backspace, .delete, .enter, .tab] { // with X11, sokol does not send .char events for some keys; we will emulate them for consistency here: + // NOTE: on Wayland (sokol_wayland), the backend already sends real CHAR events for these + // keys via xkb_state_key_get_utf8, so this block must not run there or events double-fire. e.char_code = match e.key_code { .backspace { u32(8) } .tab { 9 } diff --git a/vlib/sokol/c/declaration.c.v b/vlib/sokol/c/declaration.c.v index 71d2d3ad2..cc169cf5c 100644 --- a/vlib/sokol/c/declaration.c.v +++ b/vlib/sokol/c/declaration.c.v @@ -14,7 +14,11 @@ import sokol.memory as _ $if sokol_wayland ? { #flag linux -lwayland-client -lwayland-egl -lxkbcommon -lxkbcommon-x11 -lEGL -lGL -lpthread -lm -ldl -lX11 -lXi -lXcursor } $else { - #flag linux -lX11 -lXi -lXcursor -lGL -lpthread -lm -ldl + // EGL is used instead of GLX on X11 to get consistent vsync under XWayland. + // GLX vsync is often ignored by XWayland compositors, causing frame pacing + // jitter. EGL honours the swap interval reliably on both native X11 and + // XWayland sessions. + #flag linux -lX11 -lXi -lXcursor -lEGL -lGL -lpthread -lm -ldl } #flag freebsd -DSOKOL_GLCORE #flag freebsd -L/usr/local/lib -lX11 -lGL -lXcursor -lXi @@ -99,6 +103,9 @@ $if !no_sokol_app ? { } $else $if linux { // Explicitly disable Wayland on Linux when not using sokol_wayland #define SOKOL_DISABLE_WAYLAND + // Force EGL instead of GLX so that eglSwapBuffers provides consistent + // vsync pacing on both native X11 and XWayland sessions. + #define SOKOL_FORCE_EGL } @[use_once] -- 2.39.5