Fix invalid swapBuffer calls & outdated window decorations on Wayland

Swap-buffers was being deferred (to prevent it being called
from the event handling thread) however when it was called the
OpenGL context might not be active (especially with multiple windows).

Moving the cursor between windows made eglSwapBuffers report:
EGL Error (0x300D): EGL_BAD_SURFACE.

Resolve this by removing swapBuffer calls and instead add a
GHOST_kEventWindowUpdateDecor event intended for redrawing
client-side-decoration.

Besides the warning, this results an error with LIBDECOR window frames
not redrawing when a window became inactive.
This commit is contained in:
Campbell Barton 2023-01-20 17:58:02 +11:00
parent bbc35fef25
commit 721bd5e6cf
4 changed files with 51 additions and 29 deletions

View File

@ -198,6 +198,8 @@ typedef enum {
GHOST_kEventWindowActivate,
GHOST_kEventWindowDeactivate,
GHOST_kEventWindowUpdate,
/** Client side window decorations have changed and need to be redrawn. */
GHOST_kEventWindowUpdateDecor,
GHOST_kEventWindowSize,
GHOST_kEventWindowMove,
GHOST_kEventWindowDPIHintChanged,

View File

@ -311,10 +311,9 @@ enum eGWL_PendingWindowActions {
# ifdef GHOST_OPENGL_ALPHA
PENDING_OPAQUE_SET,
# endif
PENDING_SWAP_BUFFERS,
PENDING_SCALE_UPDATE,
};
# define PENDING_NUM (PENDING_SWAP_BUFFERS + 1)
# define PENDING_NUM (PENDING_SCALE_UPDATE + 1)
static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWindowActions type)
{
@ -338,9 +337,6 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
if (win->pending_actions[PENDING_SCALE_UPDATE].exchange(false)) {
win->ghost_window->outputs_changed_update_scale();
}
if (win->pending_actions[PENDING_SWAP_BUFFERS].exchange(false)) {
win->ghost_window->swapBuffers();
}
}
#endif /* USE_EVENT_BACKGROUND_THREAD */
@ -356,8 +352,6 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
#endif
bool do_redraw = false;
if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
if ((win->frame.size[0] != win->frame_pending.size[0]) ||
(win->frame.size[1] != win->frame_pending.size[1])) {
@ -365,9 +359,6 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
}
}
bool is_active_ghost = (win->ghost_window ==
win->ghost_system->getWindowManager()->getActiveWindow());
if (win->frame_pending.is_active) {
win->ghost_window->activate();
}
@ -375,10 +366,6 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
win->ghost_window->deactivate();
}
if (is_active_ghost != win->frame_pending.is_active) {
do_redraw = true;
}
win->frame_pending.size[0] = win->frame.size[0];
win->frame_pending.size[1] = win->frame.size[1];
@ -387,15 +374,6 @@ static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
/* Signal not to apply the scale unless it's configured. */
win->frame_pending.size[0] = 0;
win->frame_pending.size[1] = 0;
if (do_redraw) {
#ifdef USE_EVENT_BACKGROUND_THREAD
/* Could swap buffers, use pending to a redundant call in some cases. */
gwl_window_pending_actions_tag(win, PENDING_SWAP_BUFFERS);
#else
win->ghost_window->swapBuffers();
#endif
}
}
static void gwl_window_frame_update_from_pending(GWL_Window *win)
@ -621,12 +599,11 @@ static void frame_handle_commit(struct libdecor_frame * /*frame*/, void *data)
{
CLOG_INFO(LOG, 2, "commit");
# if 0
GWL_Window *win = static_cast<GWL_Window *>(data);
# ifdef USE_EVENT_BACKGROUND_THREAD
gwl_window_pending_actions_tag(win, PENDING_SWAP_BUFFERS);
win->ghost_window->notify_decor_redraw();
# else
win->ghost_window->swapBuffers();
(void)data;
# endif
}
@ -1321,8 +1298,17 @@ GHOST_TSuccess GHOST_WindowWayland::activate()
return GHOST_kFailure;
}
}
return system_->pushEvent_maybe_pending(
const GHOST_TSuccess success = system_->pushEvent_maybe_pending(
new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowActivate, this));
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (success == GHOST_kSuccess) {
if (use_libdecor) {
/* Ensure there is a swap-buffers, needed for the updated window borders to refresh. */
notify_decor_redraw();
}
}
#endif
return success;
}
GHOST_TSuccess GHOST_WindowWayland::deactivate()
@ -1335,8 +1321,17 @@ GHOST_TSuccess GHOST_WindowWayland::deactivate()
{
system_->getWindowManager()->setWindowInactive(this);
}
return system_->pushEvent_maybe_pending(
const GHOST_TSuccess success = system_->pushEvent_maybe_pending(
new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowDeactivate, this));
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (success == GHOST_kSuccess) {
if (use_libdecor) {
/* Ensure there is a swap-buffers, needed for the updated window borders to refresh. */
notify_decor_redraw();
}
}
#endif
return success;
}
GHOST_TSuccess GHOST_WindowWayland::notify_size()
@ -1358,6 +1353,14 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowSize, this));
}
GHOST_TSuccess GHOST_WindowWayland::notify_decor_redraw()
{
/* NOTE: we want to `swapBuffers`, however this may run from a thread and
* when this windows OpenGL context is not active, so send and update event instead. */
return system_->pushEvent_maybe_pending(
new GHOST_Event(system_->getMilliSeconds(), GHOST_kEventWindowUpdateDecor, this));
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -150,6 +150,7 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TSuccess activate();
GHOST_TSuccess deactivate();
GHOST_TSuccess notify_size();
GHOST_TSuccess notify_decor_redraw();
/* WAYLAND utility functions. */

View File

@ -1229,6 +1229,22 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
break;
}
case GHOST_kEventWindowUpdateDecor: {
if (G.debug & G_DEBUG_EVENTS) {
printf("%s: ghost redraw decor %d\n", __func__, win->winid);
}
wm_window_make_drawable(wm, win);
#if 0
/* NOTE(@campbellbarton): Ideally we could swap-buffers to avoid a full redraw.
* however this causes window flickering on resize with LIBDECOR under WAYLAND. */
wm_window_swap_buffers(win);
#else
WM_event_add_notifier(C, NC_WINDOW, NULL);
#endif
break;
}
case GHOST_kEventWindowSize:
case GHOST_kEventWindowMove: {
GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);