backend-drm: delay mode switches until the last commit is completed

Changing the mode will destoy the GBM surface for the output. As a result all
corresponding BOs are deleted regardless of the drm_fb refcount.

While a commit is pending, the last_state may contain a reference to such a BO.
So delay the mode switch until the commit is finished and the reference is
release.

Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
This commit is contained in:
Michael Olbrich 2020-07-16 15:59:21 +02:00 committed by Daniel Stone
parent a2684005b6
commit 6275a0fb32
2 changed files with 23 additions and 2 deletions

View File

@ -545,6 +545,7 @@ struct drm_output {
bool destroy_pending;
bool disable_pending;
bool dpms_off_pending;
bool mode_switch_pending;
uint32_t gbm_cursor_handle[2];
struct drm_fb *gbm_cursor_fb[2];

View File

@ -259,6 +259,8 @@ drm_output_get_disable_state(struct drm_pending_state *pending_state,
return output_state;
}
static int
drm_output_apply_mode(struct drm_output *output);
/**
* Mark a drm_output_state (the output's last state) as complete. This handles
@ -287,18 +289,24 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
output->destroy_pending = false;
output->disable_pending = false;
output->dpms_off_pending = false;
output->mode_switch_pending = false;
drm_output_destroy(&output->base);
return;
} else if (output->disable_pending) {
output->disable_pending = false;
output->dpms_off_pending = false;
output->mode_switch_pending = false;
weston_output_disable(&output->base);
return;
} else if (output->dpms_off_pending) {
struct drm_pending_state *pending = drm_pending_state_alloc(device);
output->dpms_off_pending = false;
output->mode_switch_pending = false;
drm_output_get_disable_state(pending, output);
drm_pending_state_apply_sync(pending);
} else if (output->mode_switch_pending) {
output->mode_switch_pending = false;
drm_output_apply_mode(output);
}
if (output->state_cur->dpms == WESTON_DPMS_OFF &&
output->base.repaint_status != REPAINT_AWAITING_COMPLETION) {
@ -687,8 +695,6 @@ static int
drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
{
struct drm_output *output = to_drm_output(output_base);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_mode *drm_mode;
assert(output);
@ -709,6 +715,20 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
output->base.current_mode->flags =
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
if (output->page_flip_pending || output->atomic_complete_pending) {
output->mode_switch_pending = true;
return 0;
}
return drm_output_apply_mode(output);
}
static int
drm_output_apply_mode(struct drm_output *output)
{
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
/* XXX: This drops our current buffer too early, before we've started
* displaying it. Ideally this should be much more atomic and
* integrated with a full repaint cycle, rather than doing a