output: limit rate of hardware cursor texture change

A client that generates lots of cursor image changes could cause a
high render load, or deplete the output's cursor swapchain on some
backends.

Limit to two renders per frame, and catch up after output commit if
necessary.
This commit is contained in:
Félix Poisot 2026-03-21 08:45:48 +00:00
parent fd870f6d27
commit c5641f6de5
4 changed files with 22 additions and 1 deletions

View file

@ -17,6 +17,7 @@ bool output_pick_format(struct wlr_output *output,
bool output_ensure_buffer(struct wlr_output *output, bool output_ensure_buffer(struct wlr_output *output,
struct wlr_output_state *state, bool *new_back_buffer); struct wlr_output_state *state, bool *new_back_buffer);
bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor);
bool output_cursor_set_texture(struct wlr_output_cursor *cursor, bool output_cursor_set_texture(struct wlr_output_cursor *cursor,
struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box,
int dst_width, int dst_height, enum wl_output_transform transform, int dst_width, int dst_height, enum wl_output_transform transform,

View file

@ -278,6 +278,8 @@ struct wlr_output {
struct wlr_output_image_description image_description_value; struct wlr_output_image_description image_description_value;
struct wlr_color_transform *color_transform; struct wlr_color_transform *color_transform;
struct wlr_color_primaries default_primaries_value; struct wlr_color_primaries default_primaries_value;
int cursor_uploaded_this_frame;
struct wlr_output_cursor *cursor_pending_upload;
} WLR_PRIVATE; } WLR_PRIVATE;
}; };

View file

@ -14,6 +14,11 @@
#include "types/wlr_buffer.h" #include "types/wlr_buffer.h"
#include "types/wlr_output.h" #include "types/wlr_output.h"
// Maximum of hardware cursor renders per frame.
// Many clients don't regroup the buffer commit and the hotspot change in a
// single message : typical burst of 2 must still be processed without delay
#define MAX_UPLOADS_PER_FRAME 2
static bool output_set_hardware_cursor(struct wlr_output *output, static bool output_set_hardware_cursor(struct wlr_output *output,
struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) {
if (!output->impl->set_cursor) { if (!output->impl->set_cursor) {
@ -55,6 +60,7 @@ static void output_disable_hardware_cursor(struct wlr_output *output) {
output_set_hardware_cursor(output, NULL, 0, 0); output_set_hardware_cursor(output, NULL, 0, 0);
output_cursor_damage_whole(output->hardware_cursor); output_cursor_damage_whole(output->hardware_cursor);
output->hardware_cursor = NULL; output->hardware_cursor = NULL;
output->cursor_pending_upload = NULL;
} }
void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) {
@ -288,7 +294,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor)
return buffer; return buffer;
} }
static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
struct wlr_output *output = cursor->output; struct wlr_output *output = cursor->output;
if (!output->impl->set_cursor || output->software_cursor_locks > 0) { if (!output->impl->set_cursor || output->software_cursor_locks > 0) {
@ -297,6 +303,11 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
struct wlr_texture *texture = cursor->texture; struct wlr_texture *texture = cursor->texture;
if (texture != NULL && output->cursor_uploaded_this_frame >= MAX_UPLOADS_PER_FRAME) {
output->cursor_pending_upload = cursor;
return true;
}
// If the cursor was hidden or was a software cursor, the hardware // If the cursor was hidden or was a software cursor, the hardware
// cursor position is outdated // cursor position is outdated
output_move_hardware_cursor(cursor->output, (int)cursor->x, (int)cursor->y); output_move_hardware_cursor(cursor->output, (int)cursor->x, (int)cursor->y);
@ -308,6 +319,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
wlr_log(WLR_DEBUG, "Failed to render cursor buffer"); wlr_log(WLR_DEBUG, "Failed to render cursor buffer");
return false; return false;
} }
output->cursor_uploaded_this_frame++;
} }
struct wlr_box hotspot = { struct wlr_box hotspot = {

View file

@ -841,6 +841,12 @@ bool wlr_output_commit_state(struct wlr_output *output,
wlr_buffer_unlock(pending.buffer); wlr_buffer_unlock(pending.buffer);
} }
output->cursor_uploaded_this_frame = 0;
if (output->cursor_pending_upload) {
output_cursor_attempt_hardware(output->cursor_pending_upload);
output->cursor_pending_upload = NULL;
}
return true; return true;
} }