From 1dda7934ce9665657c6b7235171b3422ca875358 Mon Sep 17 00:00:00 2001 From: IoIxD Date: Thu, 17 Sep 2026 07:25:52 +0900 Subject: [PATCH] wayland flickering/ghosting fix --- CMakeLists.txt | 1 + include/Mw/LowLevel/Cairo.h | 3 ++ include/Mw/LowLevel/Wayland.h | 6 ++++ pl/rules.pl | 1 + src/backend/cairo.c | 27 ++++++++------ src/backend/wayland/buffer.c | 11 ++++-- src/backend/wayland/interfaces.c | 17 +++++++++ src/backend/wayland/wayland.c | 62 +++++++++++++++----------------- 8 files changed, 82 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07f7bb9..f5bb780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,6 +261,7 @@ if(MW_USE_WAYLAND) scan_wayland_protocol("unstable" "primary-selection" "-unstable-v1") scan_wayland_protocol("unstable" "pointer-constraints" "-unstable-v1") scan_wayland_protocol("unstable" "relative-pointer" "-unstable-v1") + scan_wayland_protocol("staging" "fifo" "-v1") scan_wayland_protocol_from_file("wlr-layer-shell" "wlr-layer-shell-unstable-v1.xml") target_sources( diff --git a/include/Mw/LowLevel/Cairo.h b/include/Mw/LowLevel/Cairo.h index 680c651..92304fa 100644 --- a/include/Mw/LowLevel/Cairo.h +++ b/include/Mw/LowLevel/Cairo.h @@ -196,6 +196,9 @@ struct _MwLLCairo { /* The cairo to actually use for draw operations. Typically is front_cairo, but wayland's MwLLBeginDraw can change this to the back_cairo so it can be used to draw window decorations. */ cairo_t* selected_cairo; + cairo_surface_t* frontbuffer_cs; + cairo_t* frontbuffer_cairo; + int x; int y; int width; diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 3b79184..e3b9ff9 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -207,6 +207,7 @@ MwInline int wayland_load_funcs() { #include "Wayland/relative-pointer-client-protocol.h" #include "Wayland/xdg-toplevel-icon-client-protocol.h" #include "Wayland/wlr-layer-shell-client-protocol.h" +#include "Wayland/fifo-client-protocol.h" #endif typedef struct wayland_protocol { @@ -265,6 +266,7 @@ struct _MwLLWaylandShmBuffer { struct wl_buffer* shm_buffer_back; struct wl_surface* surface; struct wl_output* output; + struct wp_fifo_v1* fifo; MwU8* buf; MwU8* buf_back; @@ -426,9 +428,13 @@ struct _MwLLWayland { MwBool force_render; MwBool did_event_loop_early; + MwBool do_cascading_draw; + int cascading_child_num; MwBool dispatching_resize; + MwBool is_toplevel_menu; + struct _MwLLWaylandShmBuffer framebuffer; struct _MwLLWaylandShmBuffer backbuffer; struct _MwLLWaylandShmBuffer cursor; diff --git a/pl/rules.pl b/pl/rules.pl index 551d391..f12c092 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -66,6 +66,7 @@ if (grep(/^wayland$/, @backends)) { scan_wayland_protocol("unstable", "primary-selection", "-unstable-v1"); scan_wayland_protocol("unstable", "pointer-constraints", "-unstable-v1"); scan_wayland_protocol("unstable", "relative-pointer", "-unstable-v1"); + scan_wayland_protocol("staging", "fifo", "-v1"); scan_wayland_protocol_from_file("wlr-layer-shell", "wlr-layer-shell-unstable-v1.xml"); diff --git a/src/backend/cairo.c b/src/backend/cairo.c index 3683ca8..9dab1c1 100644 --- a/src/backend/cairo.c +++ b/src/backend/cairo.c @@ -113,13 +113,16 @@ void MwLLCairoDrawPixmap(struct _MwLLCairo handle, MwRect* rect, MwLLPixmap pixm void MwLLCairoFrontSetup(struct _MwLLCairo* cairo, MwU8* data, MwU32 width, MwU32 height) { if(data) { - cairo->front_cs = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, width * 4); + cairo->front_cs = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, width * 4); + cairo->frontbuffer_cs = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, width * 4); } else { - cairo->front_cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo->front_cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo->frontbuffer_cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); } - cairo->front_cs_back = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo->front_cairo_back = cairo_create(cairo->front_cs_back); - cairo->front_cairo = cairo_create(cairo->front_cs); + cairo->front_cs_back = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo->front_cairo_back = cairo_create(cairo->front_cs_back); + cairo->front_cairo = cairo_create(cairo->front_cs); + cairo->frontbuffer_cairo = cairo_create(cairo->frontbuffer_cs); } void MwLLCairoBackSetup(struct _MwLLCairo* cairo, MwU8* data, MwU32 width, MwU32 height) { @@ -135,6 +138,8 @@ void MwLLCairoFrontDestroy(struct _MwLLCairo* cairo) { cairo_destroy(cairo->front_cairo); cairo_destroy(cairo->front_cairo_back); cairo_surface_destroy(cairo->front_cs); + cairo_surface_destroy(cairo->frontbuffer_cs); + cairo_destroy(cairo->frontbuffer_cairo); }; void MwLLCairoBackDestroy(struct _MwLLCairo* cairo) { cairo_destroy(cairo->back_cairo); @@ -214,8 +219,8 @@ static void MwLLFreeColorImpl(MwLLColor color) {} static MwBool lmao = MwFALSE; static int MwLLPendingImpl(MwLL handle) { - lmao = !lmao; - return lmao; + lmao = !lmao; + return lmao; } static void MwLLNextEventImpl(MwLL handle) { MwLLDispatch(handle, draw, NULL); @@ -259,10 +264,10 @@ static void MwLLRaiseImpl(MwLL handle) {} static void MwLLClipImpl(MwLL handle, MwRect* rect) {} static void MwLLSetupDragAndDropImpl(MwLL handle) {} static int MwLLCairoCallInitImpl(void) { - if(cairo_load_funcs() != 0) { - return 1; - } - return 0; + if(cairo_load_funcs() != 0) { + return 1; + } + return 0; } #include "call.c" CALL(Cairo); diff --git a/src/backend/wayland/buffer.c b/src/backend/wayland/buffer.c index 48071ec..79ce72c 100644 --- a/src/backend/wayland/buffer.c +++ b/src/backend/wayland/buffer.c @@ -10,6 +10,9 @@ void MwLLWaylandFramebufferSetup(struct _MwLLWayland* wayland) { memset(wayland->framebuffer.buf_back, 0, wayland->framebuffer.buf_size); if(wayland->configured) wl_surface_attach(wayland->framebuffer.surface, wayland->framebuffer.shm_buffer, 0, 0); + if(wayland->framebuffer.fifo) + wp_fifo_v1_set_barrier(wayland->framebuffer.fifo); + wl_surface_commit(wayland->framebuffer.surface); MwLLWaylandHangUntilConfigured((MwLL)wayland); @@ -38,6 +41,8 @@ void MwLLWaylandBackbufferSetup(struct _MwLLWayland* wayland) { memset(wayland->backbuffer.buf_back, 255, wayland->backbuffer.buf_size); if(wayland->configured) wl_surface_attach(wayland->backbuffer.surface, wayland->backbuffer.shm_buffer, 0, 0); + // if(wayland->framebuffer.fifo) + // wp_fifo_v1_set_barrier(wayland->framebuffer.fifo); wl_surface_commit(wayland->backbuffer.surface); MwLLWaylandHangUntilConfigured((MwLL)wayland); MwLLWaylandBufferUpdate((MwLL)wayland, &wayland->backbuffer); @@ -119,9 +124,11 @@ void MwLLWaylandBufferUpdate(MwLL self, struct _MwLLWaylandShmBuffer* buffer) { memcpy(buffer->buf, buffer->buf_back, buffer->buf_size); if(buffer->surface) { // Yes this is needed every time, it's how we fix weston. - if(self->wayland.configured) + if(self->wayland.configured) { wl_surface_attach(buffer->surface, buffer->shm_buffer, 0, 0); - + } + if(buffer->fifo) + wp_fifo_v1_wait_barrier(buffer->fifo); wl_surface_commit(buffer->surface); } } diff --git a/src/backend/wayland/interfaces.c b/src/backend/wayland/interfaces.c index dd58f9a..3378d0d 100644 --- a/src/backend/wayland/interfaces.c +++ b/src/backend/wayland/interfaces.c @@ -749,6 +749,7 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time if(self->wayland.backbuffer.surface) { wl_pointer_set_cursor(self->wayland.pointer, self->wayland.pointer_serial, self->wayland.cursor.surface, 0, 0); } + WAYLAND_EVENT_OP_END(self); }; @@ -1511,6 +1512,21 @@ static void zwlr_layer_shell_v1_interface_destroy(struct _MwLLWayland* wayland, free(data); } +static wayland_protocol_t* wp_fifo_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) { + wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); + + proto->context = wl_registry_bind(wayland->registry, name, &wp_fifo_manager_v1_interface, version); + proto->listener = NULL; + + return proto; +} + +static void wp_fifo_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { + (void)wayland; + + free(data); +} + /* Function for setting up the callbacks/structs that will be registered upon the relevant interfaces being found. */ void MwLLWaylandSetupCallbacks(struct _MwLLWayland* wayland) { /* Convience macro for adding the interface functions to the setup map */ @@ -1553,6 +1569,7 @@ void MwLLWaylandSetupCallbacks(struct _MwLLWayland* wayland) { WL_INTERFACE(xdg_toplevel_icon_manager_v1); WL_INTERFACE(wl_subcompositor); WL_INTERFACE(wl_seat); + WL_INTERFACE(wp_fifo_manager_v1); } else if(wayland->type == MwLL_WAYLAND_POPUP) { WL_INTERFACE(wl_seat); } else if(wayland->type == MwLL_WAYLAND_LAYER_SURFACE) { diff --git a/src/backend/wayland/wayland.c b/src/backend/wayland/wayland.c index 9d8d634..ff8fb49 100644 --- a/src/backend/wayland/wayland.c +++ b/src/backend/wayland/wayland.c @@ -265,6 +265,10 @@ static void setup_toplevel(MwLL r, int x, int y) { r->wayland.toplevel->ssurface = wl_subcompositor_get_subsurface(r->wayland.toplevel->scompositor, r->wayland.framebuffer.surface, r->wayland.backbuffer.surface); wl_subsurface_set_desync(r->wayland.toplevel->ssurface); + if(WAYLAND_GET_INTERFACE(r->wayland, wp_fifo_manager_v1) != NULL) { + r->wayland.framebuffer.fifo = wp_fifo_manager_v1_get_fifo(WAYLAND_GET_INTERFACE(r->wayland, wp_fifo_manager_v1)->context, r->wayland.framebuffer.surface); + } + r->wayland.toplevel->xdg_surface = xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.backbuffer.surface); r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface); @@ -407,10 +411,13 @@ static void popup_configure(void* data, if(width < 50) width = 50; if(height < 50) height = 50; - self->wayland.x = x; - self->wayland.y = y; - self->wayland.ww = width; - self->wayland.wh = height; + self->wayland.x = x; + self->wayland.y = y; + + if(!self->wayland.setting_wh) { + self->wayland.ww = width; + self->wayland.wh = height; + } }; static void popup_done(void* data, @@ -439,10 +446,11 @@ struct xdg_popup_listener popup_listener = { static void setup_popup(MwLL r, int x, int y, MwLL parent) { char* mw_force_csd = getenv("MW_FORCE_CSD"); MwLL topmost_parent = r->wayland.parent; - r->wayland.type = MwLL_WAYLAND_POPUP; - r->wayland.x = x; - r->wayland.y = y; - r->wayland.popup = malloc(sizeof(struct _MwLLWaylandPopup)); + + r->wayland.type = MwLL_WAYLAND_POPUP; + r->wayland.x = x; + r->wayland.y = y; + r->wayland.popup = malloc(sizeof(struct _MwLLWaylandPopup)); if(parent) { MwWidget p = parent->common.user; @@ -908,13 +916,8 @@ static void draw_child(MwLL handle, MwLL child) { cairo_t* c; cairo_surface_t* cs; cairo_t* selected_cairo; - MwLL topmost_parent = handle; - if(topmost_parent->wayland.type != MwLL_WAYLAND_POPUP) { - while(topmost_parent->wayland.parent) { - topmost_parent = topmost_parent->wayland.parent; - } - } + draw_children(child); wl_surface_commit(child->wayland.framebuffer.surface); @@ -928,20 +931,18 @@ static void draw_child(MwLL handle, MwLL child) { cairo_paint(c); - // printf("%d %d\n", child->wayland.x, child->wayland.y); - cairo_set_source_surface(handle->wayland.cairo.front_cairo_back, cs, child->wayland.x, child->wayland.y); cairo_paint(handle->wayland.cairo.front_cairo_back); cairo_destroy(c); cairo_surface_destroy(cs); - - draw_children(child); } static void draw_children(MwLL handle) { wl_surface_damage(handle->wayland.framebuffer.surface, 0, 0, handle->wayland.ww, handle->wayland.wh); + cairo_reset_clip(handle->wayland.cairo.front_cairo_back); + MwLLWaylandChildrenIterate(handle, draw_child); if(handle->wayland.configured) MwLLWaylandBufferUpdate(handle, &handle->wayland.framebuffer); @@ -949,24 +950,13 @@ static void draw_children(MwLL handle) { } static void frontbuffer_draw(MwLL handle) { - cairo_t* c; - cairo_surface_t* cs; - - cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, handle->wayland.ww, handle->wayland.wh); - c = cairo_create(cs); + cairo_t* c = handle->wayland.cairo.frontbuffer_cairo; cairo_set_source_surface(c, handle->wayland.cairo.front_cs_back, 0, 0); cairo_pattern_set_filter(cairo_get_source(c), CAIRO_FILTER_NEAREST); - cairo_set_operator(handle->wayland.cairo.front_cairo_back, CAIRO_OPERATOR_OVER); cairo_paint(c); - - cairo_set_source_surface(handle->wayland.cairo.front_cairo, cs, 0, 0); - cairo_paint(handle->wayland.cairo.front_cairo); - - cairo_destroy(c); - cairo_surface_destroy(cs); } static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { @@ -1301,10 +1291,17 @@ static void MwLLBeginDrawImpl(MwLL handle) { } static void MwLLEndDrawImpl(MwLL handle) { + MwLL root = handle; + while(root->wayland.type == MwLL_WAYLAND_SUBLEVEL && root->wayland.parent) { + root = root->wayland.parent; + } + root->wayland.do_cascading_draw = MwTRUE; + if(handle->wayland.configured) { if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { MwLLWaylandBufferUpdate(handle, &handle->wayland.backbuffer); } + MwLLWaylandBufferUpdate(handle, &handle->wayland.framebuffer); } } @@ -1363,9 +1360,10 @@ static int MwLLPendingImpl(MwLL handle) { handle->wayland.resizing = 0; - if(handle->wayland.type != MwLL_WAYLAND_SUBLEVEL) { + if(handle->wayland.do_cascading_draw) { draw_children(handle); frontbuffer_draw(handle); + handle->wayland.do_cascading_draw = MwFALSE; } if(handle->wayland.setting_wh) { @@ -1430,7 +1428,6 @@ static int MwLLPendingImpl(MwLL handle) { } static void MwLLNextEventImpl(MwLL handle) { - if(!MwWaylandVulkan) { if(handle->wayland.did_event_loop_early) { handle->wayland.did_event_loop_early = MwFALSE; @@ -1450,7 +1447,6 @@ static void MwLLNextEventImpl(MwLL handle) { } static void MwLLSetTitleImpl(MwLL handle, const char* title) { - if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); }