Hi Daniel, On Tue, 26 Sep 2017 18:16:09 +0100, Daniel Stone wrote: > The atomic API can allow us to test state before we apply it, to see if > it will be valid. Add support for this, which we will later use in > assign_planes to ensure our plane configuration is valid. > > Signed-off-by: Daniel Stone <dani...@collabora.com> > --- > libweston/compositor-drm.c | 180 > +++++++++++++++++++++++++++++++++++---------- > 1 file changed, 141 insertions(+), 39 deletions(-) > > diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c > index 7a720c34..e678fa7d 100644 > --- a/libweston/compositor-drm.c > +++ b/libweston/compositor-drm.c > @@ -1619,6 +1619,7 @@ drm_pending_state_get_output(struct drm_pending_state > *pending_state, > } > > static int drm_pending_state_apply(struct drm_pending_state *state); > +static int drm_pending_state_test(struct drm_pending_state *state); > > /** > * Mark a drm_output_state (the output's last state) as complete. This > handles > @@ -1728,12 +1729,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) > @@ -1746,7 +1750,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; > } > @@ -1765,10 +1778,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_pending_state_test(output_state->pending_state); > + 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; > } > > @@ -1843,7 +1867,9 @@ drm_output_render(struct drm_output_state *state, > pixman_region32_t *damage) > * want to render. */ > scanout_state = drm_output_state_get_plane(state, > 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) && > @@ -1866,6 +1892,7 @@ drm_output_render(struct drm_output_state *state, > pixman_region32_t *damage) > return; > } > > + drm_fb_unref(scanout_state->fb); > scanout_state->fb = fb; > scanout_state->output = output; > > @@ -2245,8 +2272,14 @@ err: > return -1; > } > > +enum drm_pending_state_mode { > + DRM_PENDING_STATE_TEST, > + DRM_PENDING_STATE_APPLY, > +}; > + > static int > -drm_pending_state_apply_atomic(struct drm_pending_state *pending_state) > +drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, > + enum drm_pending_state_mode mode) > { > struct drm_backend *b = pending_state->backend; > struct drm_output_state *output_state, *tmp; > @@ -2386,8 +2419,15 @@ drm_pending_state_apply_atomic(struct > drm_pending_state *pending_state) > goto out; > } > > - flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; > + if (mode == DRM_PENDING_STATE_TEST) > + flags |= DRM_MODE_ATOMIC_TEST_ONLY; > + else > + flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; > ret = drmModeAtomicCommit(b->drm.fd, req, flags, b); > + > + if (mode == DRM_PENDING_STATE_TEST) > + return ret;
Leaks req and should drmModeAtomicFree(req) before returning or goto out. Michael > + > if (ret != 0) { > weston_log("atomic: couldn't commit new state: %m\n"); > goto out; > @@ -2409,6 +2449,22 @@ out: > } > #endif > > +static int > +drm_pending_state_test(struct drm_pending_state *pending_state) > +{ > +#ifdef HAVE_DRM_ATOMIC > + struct drm_backend *b = pending_state->backend; > + > + if (b->atomic_modeset) > + return drm_pending_state_apply_atomic(pending_state, > + DRM_PENDING_STATE_TEST); > +#endif > + > + /* We have no way to test state before application on the legacy > + * modesetting API, so just claim it succeeded. */ > + return 0; > +} > + > static int > drm_pending_state_apply(struct drm_pending_state *pending_state) > { > @@ -2418,7 +2474,8 @@ drm_pending_state_apply(struct drm_pending_state > *pending_state) > > #ifdef HAVE_DRM_ATOMIC > if (b->atomic_modeset) > - return drm_pending_state_apply_atomic(pending_state); > + return drm_pending_state_apply_atomic(pending_state, > + DRM_PENDING_STATE_APPLY); > #endif > > if (b->state_invalid) { > @@ -2720,7 +2777,8 @@ atomic_flip_handler(int fd, unsigned int frame, > unsigned int sec, > > 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; > @@ -2729,9 +2787,7 @@ drm_output_prepare_overlay_view(struct drm_output_state > *output_state, > 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) > @@ -2763,29 +2819,37 @@ drm_output_prepare_overlay_view(struct > drm_output_state *output_state, > continue; > } > > - break; > - } > + state->ev = ev; > + state->output = output; > + 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; > + } > > - /* No sprites available */ > - if (!state) { > - drm_fb_unref(fb); > - return NULL; > - } > + /* We hold one reference for the lifetime of this function; > + * from calling drm_fb_get_from_view, to the out label where > + * we unconditionally drop the reference. So, we take another > + * reference here to live within the state. */ > + state->fb = drm_fb_ref(fb); > > - 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) > + goto out; > > - 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_pending_state_test(output_state->pending_state); > + if (ret == 0) > + goto out; > > - return state; > + drm_plane_state_put_back(state); > + state = NULL; > + } > > -err: > - drm_plane_state_put_back(state); > - return NULL; > +out: > + drm_fb_unref(fb); > + return state; > } > > /** > @@ -2985,12 +3049,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); > state = drm_output_state_duplicate(output->state_cur, > pending_state, > 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: > @@ -3024,6 +3118,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(&clipped_view); > pixman_region32_intersect(&clipped_view, > @@ -3059,17 +3156,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 || !drm_view_is_opaque(ev)) > force_renderer = true; > > - if (!drm_view_is_opaque(ev)) > - 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) { > pixman_region32_union(&occluded_region, > @@ -3077,8 +3170,6 @@ drm_output_propose_state(struct weston_output > *output_base, > &clipped_view); > pixman_region32_fini(&clipped_view); > continue; > - } > - > if (!renderer_ok) { > pixman_region32_fini(&clipped_view); > goto err; > @@ -3092,6 +3183,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_pending_state_test(state->pending_state); > + if (ret != 0) > + goto err; > + > return state; > > err: > @@ -3112,8 +3208,14 @@ drm_assign_planes(struct weston_output *output_base, > void *repaint_data) > struct weston_view *ev; > struct weston_plane *primary = &output_base->compositor->primary_plane; > > + > state = drm_output_propose_state(output_base, pending_state, > - DRM_OUTPUT_PROPOSE_STATE_MIXED); > + DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); > + if (!state) > + state = drm_output_propose_state(output_base, pending_state, > + > DRM_OUTPUT_PROPOSE_STATE_MIXED); > + > + assert(state); > > wl_list_for_each(ev, &output_base->compositor->view_list, link) { > struct drm_plane *target_plane = NULL; _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel