Wayland: throttle resize operations also for top-level GL windows

Member cb of struct wld_buffer is replaced by member frame_cb of struct wld_window.
This allows frame_cb to be used both for non-GL and for top-level GL windows.
This commit is contained in:
ManoloFLTK 2024-01-18 08:10:33 +01:00
parent 554bccbecd
commit b7fba465ce
6 changed files with 37 additions and 20 deletions

View File

@ -583,7 +583,7 @@ more frequently than the system can process it.
This 2-step mechanism works as follows: This 2-step mechanism works as follows:
- Fl_Wayland_Graphics_Driver::buffer_commit() first calls function \c wl_surface_frame() to - Fl_Wayland_Graphics_Driver::buffer_commit() first calls function \c wl_surface_frame() to
obtain a pointer to a <tt>struct wl_callback</tt> object and stores it as member obtain a pointer to a <tt>struct wl_callback</tt> object and stores it as member
\c cb of the surface's \ref wld_buffer. \c frame_cb of the surface's \ref wld_window.
Then it calls \c wl_callback_add_listener() to associate this object to the Then it calls \c wl_callback_add_listener() to associate this object to the
FLTK-defined, callback function \c surface_frame_done(). FLTK-defined, callback function \c surface_frame_done().
It next calls \c wl_surface_commit(). It next calls \c wl_surface_commit().
@ -591,7 +591,7 @@ Together, these 3 calls instruct Wayland to start mapping the buffer content to
display and to call \c surface_frame_done() later, when it will have become ready display and to call \c surface_frame_done() later, when it will have become ready
for another mapping operation. for another mapping operation.
- Later, \c surface_frame_done() runs and destroys the \c wl_callback object by - Later, \c surface_frame_done() runs and destroys the \c wl_callback object by
function \c wl_callback_destroy() and sets member \c cb to NULL. function \c wl_callback_destroy() and sets member \c frame_cb to NULL.
Member variable \c draw_buffer_needs_commit of the \ref wld_buffer is also Member variable \c draw_buffer_needs_commit of the \ref wld_buffer is also
important in this mechanism : it informs FLTK that the graphics buffer has important in this mechanism : it informs FLTK that the graphics buffer has
@ -603,14 +603,14 @@ This procedure ensures that FLTK never changes the surface's Wayland buffer
while it's being used by the compositor and never calls \c wl_surface_commit() while it's being used by the compositor and never calls \c wl_surface_commit()
before Wayland gets ready for a new commit because before Wayland gets ready for a new commit because
\c Fl_Wayland_Window_Driver::flush() calls \c Fl_Wayland_Window_Driver::flush() calls
\c Fl_Wayland_Graphics_Driver::buffer_commit() only if \c cb is NULL. \c Fl_Wayland_Graphics_Driver::buffer_commit() only if \c frame_cb is NULL.
If it's not NULL, the exact content of function \c surface_frame_done() : If it's not NULL, the exact content of function \c surface_frame_done() :
\code \code
static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
struct wld_window *window = (struct wld_window *)data; struct wld_window *window = (struct wld_window *)data;
wl_callback_destroy(cb); wl_callback_destroy(cb);
if (window->buffer) { if (window->buffer) {
window->buffer->cb = NULL; window->frame_cb = NULL;
if (window->buffer->draw_buffer_needs_commit) { if (window->buffer->draw_buffer_needs_commit) {
Fl_Wayland_Graphics_Driver::buffer_commit(window); Fl_Wayland_Graphics_Driver::buffer_commit(window);
} }
@ -629,14 +629,14 @@ resize is being performed and sends window resize commands at high rate (~60 Hz)
client via the socket. Libdecor turns on flag \c LIBDECOR_WINDOW_STATE_RESIZING client via the socket. Libdecor turns on flag \c LIBDECOR_WINDOW_STATE_RESIZING
to inform the client, and runs function \c handle_configure() for each received resize to inform the client, and runs function \c handle_configure() for each received resize
command. Before calling Fl_Group::resize() and later Fl_Window::draw(), command. Before calling Fl_Group::resize() and later Fl_Window::draw(),
\c handle_configure() tests whether \c window->buffer->cb is NULL. When it's not \c handle_configure() tests whether \c window->frame_cb is NULL. When it's not
because a previous resize operation is being performed, the current resize command is because a previous resize operation is being performed, the current resize command is
skipped. At the end of the interactive resize, flag \c LIBDECOR_WINDOW_STATE_RESIZING skipped. At the end of the interactive resize, flag \c LIBDECOR_WINDOW_STATE_RESIZING
is off and Wayland sends a final resize command which is not skipped. Overall, this is off and Wayland sends a final resize command which is not skipped. Overall, this
ensures the client program resizes its window as frequently as it can without ensures the client program resizes its window as frequently as it can without
falling behind resize commands sent by the compositor. falling behind resize commands sent by the compositor.
To account for a bug in Mutter (issue #878), the \c window->buffer->cb object is To account for a bug in Mutter (issue #878), the \c window->frame_cb object is
not created when a toplevel window is being resized and is entirely covered by not created when a toplevel window is being resized and is entirely covered by
one subwindow. one subwindow.
@ -646,16 +646,16 @@ at any time and then calls the FLTK drawing API. This is made possible
in function \c Fl_Wayland_Window_Driver::make_current() with in function \c Fl_Wayland_Window_Driver::make_current() with
\code \code
// to support progressive drawing // to support progressive drawing
if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->buffer->cb) if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb)
&& window->buffer->draw_buffer_needs_commit && (!wait_for_expose_value) ) { && window->buffer->draw_buffer_needs_commit && (!wait_for_expose_value) ) {
Fl_Wayland_Graphics_Driver::buffer_commit(window); Fl_Wayland_Graphics_Driver::buffer_commit(window);
} }
\endcode \endcode
Thus, \c buffer_commit() runs only when \c cb is NULL. If an app rapidly performs calls Thus, \c buffer_commit() runs only when \c frame_cb is NULL. If an app rapidly performs calls
to \c Fl_Window::make_current() and to drawing functions, FLTK will copy \c draw_buffer to to \c Fl_Window::make_current() and to drawing functions, FLTK will copy \c draw_buffer to
the Wayland buffer and instruct Wayland to map it to the display when \c cb is NULL the Wayland buffer and instruct Wayland to map it to the display when \c frame_cb is NULL
which means that the compositor is ready to start performing a mapping operation. which means that the compositor is ready to start performing a mapping operation.
This occurs when the progressive drawing operation begins. Later, \c cb is generally found This occurs when the progressive drawing operation begins. Later, \c frame_cb is generally found
non NULL when \c Fl_Wayland_Window_Driver::make_current() runs because the compositor non NULL when \c Fl_Wayland_Window_Driver::make_current() runs because the compositor
is busy processing the previous Wayland buffer. When the compositor has completed is busy processing the previous Wayland buffer. When the compositor has completed
this processing, the client app runs \c surface_frame_done() this processing, the client app runs \c surface_frame_done()
@ -1265,6 +1265,7 @@ struct wld_window {
Fl_Window *fl_win; Fl_Window *fl_win;
struct wl_list outputs; // linked list of displays where part or whole of window maps struct wl_list outputs; // linked list of displays where part or whole of window maps
struct wl_surface *wl_surface; // the window's surface struct wl_surface *wl_surface; // the window's surface
struct wl_callback *frame_cb; // non-NULL until Wayland can process new surface commit
struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; // see \ref wld_buffer struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; // see \ref wld_buffer
struct xdg_surface *xdg_surface; struct xdg_surface *xdg_surface;
enum Fl_Wayland_Window_Driver::kind kind; // DECORATED or POPUP or SUBWINDOW or UNFRAMED enum Fl_Wayland_Window_Driver::kind kind; // DECORATED or POPUP or SUBWINDOW or UNFRAMED
@ -1323,7 +1324,6 @@ struct Fl_Wayland_Graphics_Driver::wld_buffer {
struct wl_list link; // links all buffers from the same wl_shm_pool struct wl_list link; // links all buffers from the same wl_shm_pool
struct wl_buffer *wl_buffer; // the Wayland buffer struct wl_buffer *wl_buffer; // the Wayland buffer
void *data; // address of the beginning of the Wayland buffer's byte array void *data; // address of the beginning of the Wayland buffer's byte array
struct wl_callback *cb; // non-NULL until Wayland can process new buffer commit
struct wl_shm_pool *shm_pool; // pter to wl_shm_pool from which this wl_buffer comes struct wl_shm_pool *shm_pool; // pter to wl_shm_pool from which this wl_buffer comes
bool draw_buffer_needs_commit; // true when draw_buffer has been modified but not yet committed bool draw_buffer_needs_commit; // true when draw_buffer has been modified but not yet committed
bool in_use; // true while being committed bool in_use; // true while being committed

View File

@ -397,6 +397,18 @@ static void delayed_scissor(Fl_Wayland_Gl_Window_Driver *dr) {
}*/ }*/
static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
struct wld_window *xid = (struct wld_window *)data;
wl_callback_destroy(cb);
xid->frame_cb = NULL;
}
static const struct wl_callback_listener surface_frame_listener = {
.done = surface_frame_done,
};
void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) { void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) {
if (!egl_window) return; if (!egl_window) return;
float f = Fl::screen_scale(pWindow->screen_num()); float f = Fl::screen_scale(pWindow->screen_num());
@ -406,6 +418,11 @@ void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) {
int W2, H2; int W2, H2;
wl_egl_window_get_attached_size(egl_window, &W2, &H2); wl_egl_window_get_attached_size(egl_window, &W2, &H2);
if (W2 != W || H2 != H) { if (W2 != W || H2 != H) {
struct wld_window *xid = fl_wl_xid(pWindow);
if (xid->kind == Fl_Wayland_Window_Driver::DECORATED && !xid->frame_cb) {
xid->frame_cb = wl_surface_frame(xid->wl_surface);
wl_callback_add_listener(xid->frame_cb, &surface_frame_listener, xid);
}
wl_egl_window_resize(egl_window, W, H, 0, 0); wl_egl_window_resize(egl_window, W, H, 0, 0);
} }
/* CONTROL_LEAKING_SUB_GL_WINDOWS /* CONTROL_LEAKING_SUB_GL_WINDOWS

View File

@ -41,7 +41,6 @@ public:
struct wl_list link; // links all buffers from the same wl_shm_pool struct wl_list link; // links all buffers from the same wl_shm_pool
struct wl_buffer *wl_buffer; struct wl_buffer *wl_buffer;
void *data; void *data;
struct wl_callback *cb;
struct wl_shm_pool *shm_pool; struct wl_shm_pool *shm_pool;
bool draw_buffer_needs_commit; bool draw_buffer_needs_commit;
bool in_use; // true while being committed bool in_use; // true while being committed

View File

@ -123,7 +123,7 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time
struct wld_window *window = (struct wld_window *)data; struct wld_window *window = (struct wld_window *)data;
wl_callback_destroy(cb); wl_callback_destroy(cb);
if (window->buffer) { // fix for issue #712 if (window->buffer) { // fix for issue #712
window->buffer->cb = NULL; window->frame_cb = NULL;
if (window->buffer->draw_buffer_needs_commit) { if (window->buffer->draw_buffer_needs_commit) {
Fl_Wayland_Graphics_Driver::buffer_commit(window); Fl_Wayland_Graphics_Driver::buffer_commit(window);
} }
@ -177,8 +177,8 @@ void Fl_Wayland_Graphics_Driver::buffer_commit(struct wld_window *window, struct
wl_surface_set_buffer_scale( window->wl_surface, wl_surface_set_buffer_scale( window->wl_surface,
Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale() ); Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale() );
if (!window->covered) { // see issue #878 if (!window->covered) { // see issue #878
window->buffer->cb = wl_surface_frame(window->wl_surface); window->frame_cb = wl_surface_frame(window->wl_surface);
wl_callback_add_listener(window->buffer->cb, &surface_frame_listener, window); wl_callback_add_listener(window->frame_cb, &surface_frame_listener, window);
} }
wl_surface_commit(window->wl_surface); wl_surface_commit(window->wl_surface);
window->buffer->draw_buffer_needs_commit = false; window->buffer->draw_buffer_needs_commit = false;
@ -237,7 +237,7 @@ void Fl_Wayland_Graphics_Driver::buffer_release(struct wld_window *window)
{ {
if (window->buffer && !window->buffer->released) { if (window->buffer && !window->buffer->released) {
window->buffer->released = true; window->buffer->released = true;
if (window->buffer->cb) wl_callback_destroy(window->buffer->cb); if (window->frame_cb) { wl_callback_destroy(window->frame_cb); window->frame_cb = NULL; }
delete[] window->buffer->draw_buffer.buffer; delete[] window->buffer->draw_buffer.buffer;
window->buffer->draw_buffer.buffer = NULL; window->buffer->draw_buffer.buffer = NULL;
cairo_destroy(window->buffer->draw_buffer.cairo_); cairo_destroy(window->buffer->draw_buffer.cairo_);

View File

@ -144,6 +144,7 @@ struct wld_window {
Fl_Window *fl_win; Fl_Window *fl_win;
struct wl_list outputs; // linked list of displays where part or whole of window maps struct wl_list outputs; // linked list of displays where part or whole of window maps
struct wl_surface *wl_surface; struct wl_surface *wl_surface;
struct wl_callback *frame_cb;
struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer;
struct xdg_surface *xdg_surface; struct xdg_surface *xdg_surface;
enum Fl_Wayland_Window_Driver::kind kind; enum Fl_Wayland_Window_Driver::kind kind;

View File

@ -352,7 +352,7 @@ void Fl_Wayland_Window_Driver::make_current() {
} }
// to support progressive drawing // to support progressive drawing
if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->buffer->cb) if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb)
&& window->buffer->draw_buffer_needs_commit && (!wait_for_expose_value) ) { && window->buffer->draw_buffer_needs_commit && (!wait_for_expose_value) ) {
Fl_Wayland_Graphics_Driver::buffer_commit(window); Fl_Wayland_Graphics_Driver::buffer_commit(window);
} }
@ -415,7 +415,7 @@ void Fl_Wayland_Window_Driver::flush() {
Fl_Wayland_Window_Driver::in_flush_ = true; Fl_Wayland_Window_Driver::in_flush_ = true;
Fl_Window_Driver::flush(); Fl_Window_Driver::flush();
Fl_Wayland_Window_Driver::in_flush_ = false; Fl_Wayland_Window_Driver::in_flush_ = false;
if (!window->buffer->cb) Fl_Wayland_Graphics_Driver::buffer_commit(window, r); if (!window->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(window, r);
} }
@ -909,9 +909,9 @@ static void handle_configure(struct libdecor_frame *frame,
#ifndef LIBDECOR_MR131 #ifndef LIBDECOR_MR131
bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING); bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING);
#endif #endif
bool condition = in_decorated_window_resizing && window->buffer; bool condition = in_decorated_window_resizing;
if (condition) { // see issue #878 if (condition) { // see issue #878
condition = (window->covered ? window->buffer->in_use : (window->buffer->cb != NULL)); condition = (window->covered ? (window->buffer && window->buffer->in_use) : (window->frame_cb != NULL));
} }
if (condition) { if (condition) {
// Skip resizing & redrawing. The last resize request won't be skipped because // Skip resizing & redrawing. The last resize request won't be skipped because