diff options
author | Daniel Stone <daniels@collabora.com> | 2016-12-09 15:57:53 +0000 |
---|---|---|
committer | Daniel Stone <daniels@collabora.com> | 2017-02-07 19:16:34 +0000 |
commit | 70d8424132558ae6b30bb15b7f8623bf970538fe (patch) | |
tree | c9e815e4234af51dfa3592c5a12a8c5f14a7b12f | |
parent | 2a651c30864f966738f698111be24743f817c49a (diff) |
compositor-drm: Test plane states before application
Generate an output state in two stages. Firstly, attempt to run through
and generate a configuration with all views in planes. If this succeeds,
we can bypass the renderer completely.
If this fails, we know we need to have the renderer output used as a
base on the scanout plane, and can incrementally attempt to construct a
working state, by trying the combination of our old renderer state with
planes added one-by-one. If they pass the test commit stage, we can use
it, so we stash that state and continue on.
Signed-off-by: Daniel Stone <daniels@collabora.com>
Differential Revision: https://phabricator.freedesktop.org/D1535
-rw-r--r-- | libweston/compositor-drm.c | 136 |
1 files changed, 102 insertions, 34 deletions
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 79b937d4..58d434ff 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -1664,12 +1664,15 @@ enum drm_output_propose_state_mode { static struct drm_plane_state * drm_output_prepare_scanout_view(struct drm_output_state *output_state, - struct weston_view *ev) + struct weston_view *ev, + enum drm_output_propose_state_mode mode) { struct drm_output *output = output_state->output; struct drm_plane *scanout_plane = output->scanout_plane; struct drm_plane_state *state; + struct drm_plane_state *state_old = NULL; struct drm_fb *fb; + int ret; fb = drm_fb_get_from_view(output_state, ev); if (!fb) @@ -1682,7 +1685,16 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, } state = drm_output_state_get_plane(output_state, scanout_plane); - if (state->fb) { + + /* Check if we've already placed a buffer on this plane; if there's a + * buffer there but it comes from GBM, then it's the result of + * drm_output_propose_state placing it here for testing purposes. */ + if (state->fb && + (state->fb->type == BUFFER_GBM_SURFACE || + state->fb->type == BUFFER_PIXMAN_DUMB)) { + state_old = calloc(1, sizeof(*state_old)); + memcpy(state_old, state, sizeof(*state_old)); + } else if (state->fb) { drm_fb_unref(fb); return NULL; } @@ -1701,10 +1713,21 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, state->dest_h != (unsigned) output->base.current_mode->height) goto err; + if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) + return state; + + ret = drm_output_apply_state(output_state, DRM_OUTPUT_APPLY_STATE_TEST); + if (ret != 0) + goto err; + return state; err: - drm_plane_state_put_back(state); + drm_plane_state_free(state, false); + if (state_old) { + wl_list_insert(&output_state->plane_list, &state_old->link); + state_old->output_state = output_state; + } return NULL; } @@ -1775,7 +1798,9 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage) * want to render. */ scanout_state = drm_output_state_get_plane(output->state_pending, output->scanout_plane); - if (scanout_state->fb) + if (scanout_state->fb && + scanout_state->fb->type != BUFFER_GBM_SURFACE && + scanout_state->fb->type != BUFFER_PIXMAN_DUMB) return; if (!pixman_region32_not_empty(damage) && @@ -1798,6 +1823,7 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage) return; } + drm_fb_unref(scanout_state->fb); scanout_state->fb = fb; scanout_state->output = output; @@ -2396,24 +2422,24 @@ atomic_flip_handler(int fd, unsigned int frame, static struct drm_plane_state * drm_output_prepare_overlay_view(struct drm_output_state *output_state, - struct weston_view *ev) + struct weston_view *ev, + enum drm_output_propose_state_mode mode) { struct drm_output *output = output_state->output; struct weston_compositor *ec = output->base.compositor; struct drm_backend *b = to_drm_backend(ec); struct drm_plane *p; - struct drm_plane_state *state = NULL; struct drm_fb *fb; unsigned int i; - - if (b->sprites_are_broken) - return NULL; + int ret; fb = drm_fb_get_from_view(output_state, ev); if (!fb) return NULL; wl_list_for_each(p, &b->plane_list, link) { + struct drm_plane_state *state = NULL; + if (p->type != WDRM_PLANE_TYPE_OVERLAY) continue; @@ -2439,28 +2465,31 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state, continue; } - break; - } + state->fb = fb; + state->ev = ev; + state->output = output; - /* No sprites available */ - if (!state) { - drm_fb_unref(fb); - return NULL; - } + drm_plane_state_coords_for_view(state, ev); + if (state->src_w != state->dest_w << 16 || + state->src_h != state->dest_h << 16) { + drm_plane_state_put_back(state); + continue; + } - state->fb = fb; - state->ev = ev; - state->output = output; + /* In planes-only mode, we don't have an incremental state to + * test against, so we just hope it'll work. */ + if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) + return state; - drm_plane_state_coords_for_view(state, ev); - if (state->src_w != state->dest_w << 16 || - state->src_h != state->dest_h << 16) - goto err; + ret = drm_output_apply_state(output_state, + DRM_OUTPUT_APPLY_STATE_TEST); + if (ret == 0) + return state; - return state; + drm_plane_state_put_back(state); + } -err: - drm_plane_state_put_back(state); + drm_fb_unref(fb); return NULL; } @@ -2660,12 +2689,42 @@ drm_output_propose_state(struct weston_output *output_base, struct weston_view *ev; pixman_region32_t surface_overlap, renderer_region, occluded_region; bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - bool planes_ok = !b->sprites_are_broken; + bool planes_ok = false; + bool duplicate_renderer = false; + int ret; + + /* We can only propose planes if we're in plane-only mode (i.e. just + * get as much as possible into planes and test at the end), or mixed + * mode, where we have our previous renderer buffer (and it's broadly + * compatible) to test with. If we don't have these things, then we + * don't know whether or not the planes will work, so we conservatively + * fall back to just using the renderer. */ + if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { + if (b->sprites_are_broken) + return NULL; + planes_ok = true; + } else if (output->scanout_plane->state_cur->fb) { + struct drm_fb *scanout_fb = + output->scanout_plane->state_cur->fb; + if ((scanout_fb->type == BUFFER_GBM_SURFACE || + scanout_fb->type == BUFFER_PIXMAN_DUMB) && + scanout_fb->width == output_base->current_mode->width && + scanout_fb->height == output_base->current_mode->height) { + planes_ok = true; + duplicate_renderer = true; + } + } assert(!output->state_last); assert(!output->state_pending); state = drm_output_state_duplicate(output->state_cur, DRM_OUTPUT_STATE_CLEAR_PLANES); + if (!planes_ok) + return state; + + if (duplicate_renderer) + drm_plane_state_duplicate(state, + output->scanout_plane->state_cur); /* * Find a surface for each sprite in the output using some heuristics: @@ -2699,6 +2758,9 @@ drm_output_propose_state(struct weston_output *output_base, if (ev->output_mask != (1u << output->base.id)) force_renderer = true; + if (!ev->surface->buffer_ref.buffer) + force_renderer = true; + /* Ignore views we know to be totally occluded. */ pixman_region32_init(&surface_overlap); pixman_region32_subtract(&surface_overlap, @@ -2732,17 +2794,13 @@ drm_output_propose_state(struct weston_output *output_base, * cursors_are_broken flag. */ if (!force_renderer && !b->cursors_are_broken) ps = drm_output_prepare_cursor_view(state, ev); - if (!planes_ok) - force_renderer = true; if (!force_renderer && !ps) - ps = drm_output_prepare_scanout_view(state, ev); + ps = drm_output_prepare_scanout_view(state, ev, mode); if (!force_renderer && !ps) - ps = drm_output_prepare_overlay_view(state, ev); + ps = drm_output_prepare_overlay_view(state, ev, mode); if (ps) continue; - if (!renderer_ok) - goto err; pixman_region32_union(&renderer_region, &renderer_region, @@ -2751,6 +2809,11 @@ drm_output_propose_state(struct weston_output *output_base, pixman_region32_fini(&renderer_region); pixman_region32_fini(&occluded_region); + /* Check to see if this state will actually work. */ + ret = drm_output_apply_state(state, DRM_OUTPUT_APPLY_STATE_TEST); + if (ret != 0) + goto err; + return state; err: @@ -2771,7 +2834,12 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data) struct weston_plane *primary = &output_base->compositor->primary_plane; state = drm_output_propose_state(output_base, - DRM_OUTPUT_PROPOSE_STATE_MIXED); + DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); + if (!state) + state = drm_output_propose_state(output_base, + DRM_OUTPUT_PROPOSE_STATE_MIXED); + + assert(state); wl_list_for_each(ev, &output_base->compositor->view_list, link) { struct drm_plane *target_plane = NULL; |