backend-drm: skip building atomic state and logging for "empty" repaints

With multiple backends or devices repaint_begin/repaint_flush may be called even
if no outout of a device will be repainted. This results in an "empty" drm state
without any output.
The actual commit to the kernel is already skipped but the drm-backend log is
still filled with "Beginning repaint"/"repaint-flush" messages and the scene
graph.

Use the new prepare_repaint() callback to determine if a backend needs to be
repainted and only create the pending_state if necessary.
Exit early in repaint_flush()/repaint_cancel() when no pending_state was created.

Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
This commit is contained in:
Michael Olbrich 2024-01-02 16:39:26 +01:00
parent a1f8c49d5b
commit e8166e854d
2 changed files with 75 additions and 47 deletions

View file

@ -203,6 +203,8 @@ struct drm_device {
/* drm_writeback::link */
struct wl_list writeback_connector_list;
bool will_repaint;
bool state_invalid;
bool atomic_modeset;

View file

@ -684,6 +684,14 @@ cursor_bo_update(struct drm_output *output, struct weston_view *ev)
}
#endif
static void
drm_output_prepare_repaint(struct weston_output *output_base)
{
struct drm_output *output = to_drm_output(output_base);
output->device->will_repaint = true;
}
static int
drm_output_repaint(struct weston_output *output_base)
{
@ -699,6 +707,7 @@ drm_output_repaint(struct weston_output *output_base)
device = output->device;
pending_state = device->repaint_data;
assert(pending_state);
if (output->disable_pending || output->destroy_pending)
goto err;
@ -889,6 +898,21 @@ finish_frame:
return 0;
}
static void
drm_repaint_begin_device(struct drm_device *device)
{
struct drm_backend *b = device->backend;
struct drm_pending_state *pending_state;
device->will_repaint = false;
pending_state = drm_pending_state_alloc(device);
device->repaint_data = pending_state;
if (weston_log_scope_is_enabled(b->debug))
drm_debug(b, "[repaint] Beginning repaint (%s); pending_state %p\n",
device->drm.filename, device->repaint_data);
}
/**
* Begin a new repaint cycle
*
@ -901,32 +925,42 @@ drm_repaint_begin(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device;
struct drm_pending_state *pending_state;
device = b->drm;
pending_state = drm_pending_state_alloc(device);
device->repaint_data = pending_state;
if (b->drm->will_repaint)
drm_repaint_begin_device(b->drm);
wl_list_for_each(device, &b->kms_list, link) {
if (device->will_repaint)
drm_repaint_begin_device(device);
}
if (weston_log_scope_is_enabled(b->debug)) {
char *dbg = weston_compositor_print_scene_graph(b->compositor);
drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n",
device->repaint_data);
drm_debug(b, "%s", dbg);
free(dbg);
}
}
wl_list_for_each(device, &b->kms_list, link) {
pending_state = drm_pending_state_alloc(device);
device->repaint_data = pending_state;
static int
drm_repaint_flush_device(struct drm_device *device)
{
struct drm_backend *b = device->backend;
struct drm_pending_state *pending_state;
int ret;
if (weston_log_scope_is_enabled(b->debug)) {
char *dbg = weston_compositor_print_scene_graph(b->compositor);
drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n",
pending_state);
drm_debug(b, "%s", dbg);
free(dbg);
}
}
pending_state = device->repaint_data;
if (!pending_state)
return 0;
ret = drm_pending_state_apply(pending_state);
if (ret != 0)
weston_log("repaint-flush failed: %s\n", strerror(errno));
drm_debug(b, "[repaint] flushed (%s) pending_state %p\n",
device->drm.filename, pending_state);
device->repaint_data = NULL;
return (ret == -EACCES || ret == -EBUSY) ? ret : 0;
}
/**
@ -943,29 +977,29 @@ drm_repaint_flush(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device;
struct drm_pending_state *pending_state;
int ret;
device = b->drm;
ret= drm_repaint_flush_device(b->drm);
wl_list_for_each(device, &b->kms_list, link)
ret = drm_repaint_flush_device(device);
return ret;
}
static void
drm_repaint_cancel_device(struct drm_device *device)
{
struct drm_backend *b = device->backend;
struct drm_pending_state *pending_state;
device->will_repaint = false;
pending_state = device->repaint_data;
ret = drm_pending_state_apply(pending_state);
if (ret != 0)
weston_log("repaint-flush failed: %s\n", strerror(errno));
drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state);
device->repaint_data = NULL;
wl_list_for_each(device, &b->kms_list, link) {
pending_state = device->repaint_data;
ret = drm_pending_state_apply(pending_state);
if (ret != 0)
weston_log("repaint-flush failed: %s\n", strerror(errno));
drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state);
if (pending_state) {
drm_pending_state_free(pending_state);
drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state);
device->repaint_data = NULL;
}
return (ret == -EACCES || ret == -EBUSY) ? ret : 0;
}
/**
@ -979,20 +1013,11 @@ drm_repaint_cancel(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device;
struct drm_pending_state *pending_state;
device = b->drm;
pending_state = device->repaint_data;
drm_pending_state_free(pending_state);
drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state);
device->repaint_data = NULL;
drm_repaint_cancel_device(b->drm);
wl_list_for_each(device, &b->kms_list, link) {
pending_state = device->repaint_data;
drm_pending_state_free(pending_state);
drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state);
device->repaint_data = NULL;
}
wl_list_for_each(device, &b->kms_list, link)
drm_repaint_cancel_device(b->drm);
}
static int
@ -2290,6 +2315,7 @@ drm_output_enable(struct weston_output *base)
drm_output_init_backlight(output);
output->base.start_repaint_loop = drm_output_start_repaint_loop;
output->base.prepare_repaint = drm_output_prepare_repaint;
output->base.repaint = drm_output_repaint;
output->base.assign_planes = drm_assign_planes;
output->base.set_dpms = drm_set_dpms;