return drm_output_propose_state_mode_as_string[mode];
}
-static struct drm_plane_state *
-drm_output_prepare_overlay_view(struct drm_output_state *output_state,
- struct weston_view *ev,
- enum drm_output_propose_state_mode mode)
+static void
+drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
{
- 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;
- int ret;
- enum {
- NO_PLANES,
- NO_PLANES_WITH_FORMAT,
- NO_PLANES_ACCEPTED,
- PLACED_ON_PLANE,
- } availability = NO_PLANES;
+ struct drm_backend *b = plane->backend;
+ struct drm_plane_zpos *h_plane;
+ struct drm_plane_zpos *plane_zpos;
- assert(!b->sprites_are_broken);
- assert(b->atomic_modeset);
+ plane_zpos = zalloc(sizeof(*plane_zpos));
+ if (!plane_zpos)
+ return;
- fb = drm_fb_get_from_view(output_state, ev);
- if (!fb) {
- drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
- " couldn't get fb\n", ev);
- return NULL;
+ plane_zpos->plane = plane;
+
+ drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
+ plane->plane_id);
+
+ if (wl_list_empty(planes)) {
+ wl_list_insert(planes, &plane_zpos->link);
+ return;
}
- wl_list_for_each(p, &b->plane_list, link) {
- if (p->type != WDRM_PLANE_TYPE_OVERLAY)
- continue;
+ h_plane = wl_container_of(planes->next, h_plane, link);
+ if (h_plane->plane->zpos_max >= plane->zpos_max) {
+ wl_list_insert(planes->prev, &plane_zpos->link);
+ } else {
+ struct drm_plane_zpos *p_zpos = NULL;
- if (!drm_plane_is_available(p, output))
- continue;
+ if (wl_list_length(planes) == 1) {
+ wl_list_insert(planes->prev, &plane_zpos->link);
+ return;
+ }
- state = drm_output_state_get_plane(output_state, p);
- if (state->fb) {
- state = NULL;
- continue;
+ wl_list_for_each(p_zpos, planes, link) {
+ if (p_zpos->plane->zpos_max >
+ plane_zpos->plane->zpos_max)
+ break;
}
- if (availability == NO_PLANES)
- availability = NO_PLANES_WITH_FORMAT;
+ wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
+ }
+}
+
+static void
+drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
+{
+ wl_list_remove(&plane_zpos->link);
+ free(plane_zpos);
+}
+
+static bool
+drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
+ struct drm_output_state *output_state)
+{
+ struct drm_plane_state *ps;
+ wl_list_for_each(ps, &output_state->plane_list, link) {
+ if (ps->plane == plane && ps->fb)
+ return true;
+ }
+ return false;
+}
- /* Check whether the format is supported */
- for (i = 0; i < p->count_formats; i++) {
- unsigned int j;
+static bool
+drm_output_plane_has_valid_format(struct drm_plane *plane,
+ struct drm_output_state *state,
+ struct drm_fb *fb)
+{
+ struct drm_backend *b = plane->backend;
+ unsigned int i;
- if (p->formats[i].format != fb->format->format)
- continue;
+ if (!fb)
+ return false;
- if (fb->modifier == DRM_FORMAT_MOD_INVALID)
- break;
+ /* Check whether the format is supported */
+ for (i = 0; i < plane->count_formats; i++) {
+ unsigned int j;
- for (j = 0; j < p->formats[i].count_modifiers; j++) {
- if (p->formats[i].modifiers[j] == fb->modifier)
- break;
- }
- if (j != p->formats[i].count_modifiers)
- break;
- }
- if (i == p->count_formats) {
- drm_plane_state_put_back(state);
- state = NULL;
+ if (plane->formats[i].format != fb->format->format)
continue;
- }
- if (availability == NO_PLANES_WITH_FORMAT)
- availability = NO_PLANES_ACCEPTED;
+ if (fb->modifier == DRM_FORMAT_MOD_INVALID)
+ return true;
- state->ev = ev;
- state->output = output;
- if (!drm_plane_state_coords_for_view(state, ev)) {
- drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
- "unsuitable transform\n", ev);
- drm_plane_state_put_back(state);
- state = NULL;
- continue;
+ for (j = 0; j < plane->formats[i].count_modifiers; j++) {
+ if (plane->formats[i].modifiers[j] == fb->modifier)
+ return true;
}
+ }
- /* If the surface buffer has an in-fence fd, but the plane
- * doesn't support fences, we can't place the buffer on this
- * plane. */
- if (ev->surface->acquire_fence_fd >= 0 &&
- p->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
- drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
- "no in-fence support\n", ev);
- drm_plane_state_put_back(state);
- state = NULL;
- continue;
- }
+ drm_debug(b, "\t\t\t\t[%s] not placing view on %s: "
+ "no free %s planes matching format %s (0x%lx) "
+ "modifier 0x%llx\n",
+ drm_output_get_plane_type_name(plane),
+ drm_output_get_plane_type_name(plane),
+ drm_output_get_plane_type_name(plane),
+ fb->format->drm_format_name,
+ (unsigned long) fb->format,
+ (unsigned long long) fb->modifier);
+
+ return false;
+}
- /* 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);
+static bool
+drm_output_plane_cursor_has_valid_format(struct weston_view *ev)
+{
+ struct wl_shm_buffer *shmbuf =
+ wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
- state->in_fence_fd = ev->surface->acquire_fence_fd;
+ if (shmbuf && wl_shm_buffer_get_format(shmbuf) == WL_SHM_FORMAT_ARGB8888)
+ return true;
- /* 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) {
- drm_debug(b, "\t\t\t\t[overlay] provisionally placing "
- "view %p on overlay %lu in planes-only mode\n",
- ev, (unsigned long) p->plane_id);
- availability = PLACED_ON_PLANE;
- goto out;
- }
+ return false;
+}
- ret = drm_pending_state_test(output_state->pending_state);
- if (ret == 0) {
- drm_debug(b, "\t\t\t\t[overlay] provisionally placing "
- "view %p on overlay %d in mixed mode\n",
- ev, p->plane_id);
- availability = PLACED_ON_PLANE;
- goto out;
- }
+static struct drm_plane_state *
+drm_output_prepare_overlay_view(struct drm_plane *plane,
+ struct drm_output_state *output_state,
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode,
+ struct drm_fb *fb, uint64_t zpos)
+{
+ 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_state *state = NULL;
+ int ret;
+
+ assert(!b->sprites_are_broken);
+ assert(b->atomic_modeset);
- drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay %lu "
- "in mixed mode: kernel test failed\n",
- ev, (unsigned long) p->plane_id);
+ if (!fb) {
+ drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
+ " couldn't get fb\n", ev);
+ return NULL;
+ }
+
+ state = drm_output_state_get_plane(output_state, plane);
+ /* we can't have a 'pending' framebuffer as never set one before reaching here */
+ assert(!state->fb);
+ state->ev = ev;
+ state->output = output;
+
+ if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
+ drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
+ "unsuitable transform\n", ev);
drm_plane_state_put_back(state);
state = NULL;
+ goto out;
}
- switch (availability) {
- case NO_PLANES:
- drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
- "no free overlay planes\n", ev);
- break;
- case NO_PLANES_WITH_FORMAT:
+ /* If the surface buffer has an in-fence fd, but the plane
+ * doesn't support fences, we can't place the buffer on this
+ * plane. */
+ if (ev->surface->acquire_fence_fd >= 0 &&
+ plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
- "no free overlay planes matching format %s (0x%lx) "
- "modifier 0x%llx\n",
- ev, fb->format->drm_format_name,
- (unsigned long) fb->format,
- (unsigned long long) fb->modifier);
- break;
- case NO_PLANES_ACCEPTED:
- case PLACED_ON_PLANE:
- break;
+ "no in-fence support\n", ev);
+ drm_plane_state_put_back(state);
+ state = NULL;
+ goto out;
+ }
+
+ /* We hold one reference for the lifetime of this function; from
+ * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
+ * so, we take another reference here to live within the state. */
+ state->fb = drm_fb_ref(fb);
+
+ state->in_fence_fd = ev->surface->acquire_fence_fd;
+
+ /* 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) {
+ drm_debug(b, "\t\t\t[overlay] provisionally placing "
+ "view %p on overlay %lu in planes-only mode\n",
+ ev, (unsigned long) plane->plane_id);
+ goto out;
}
+ ret = drm_pending_state_test(output_state->pending_state);
+ if (ret == 0) {
+ drm_debug(b, "\t\t\t[overlay] provisionally placing "
+ "view %p on overlay %d in mixed mode\n",
+ ev, plane->plane_id);
+ goto out;
+ }
+
+ drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
+ "in mixed mode: kernel test failed\n",
+ ev, (unsigned long) plane->plane_id);
+
+ drm_plane_state_put_back(state);
+ state = NULL;
+
out:
- drm_fb_unref(fb);
return state;
}
static struct drm_plane_state *
drm_output_prepare_cursor_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev, uint64_t zpos)
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *plane = output->cursor_plane;
struct drm_plane_state *plane_state;
- struct wl_shm_buffer *shmbuf;
bool needs_update = false;
+ const char *p_name = drm_output_get_plane_type_name(plane);
assert(!b->cursors_are_broken);
if (b->gbm == NULL)
return NULL;
- if (ev->surface->buffer_ref.buffer == NULL) {
- drm_debug(b, "\t\t\t\t[cursor] not assigning view %p to cursor plane "
- "(no buffer available)\n", ev);
- return NULL;
- }
- shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
- if (!shmbuf) {
- drm_debug(b, "\t\t\t\t[cursor] not assigning view %p to cursor plane "
- "(buffer isn't SHM)\n", ev);
- return NULL;
- }
- if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) {
- drm_debug(b, "\t\t\t\t[cursor] not assigning view %p to cursor plane "
- "(format 0x%lx unsuitable)\n",
- ev, (unsigned long) wl_shm_buffer_get_format(shmbuf));
- return NULL;
- }
-
plane_state =
drm_output_state_get_plane(output_state, output->cursor_plane);
/* We can't scale with the legacy API, and we don't try to account for
* simple cropping/translation in cursor_bo_update. */
plane_state->output = output;
- if (!drm_plane_state_coords_for_view(plane_state, ev))
+ if (!drm_plane_state_coords_for_view(plane_state, ev, zpos)) {
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ "unsuitable transform\n", p_name, ev, p_name);
goto err;
+ }
if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
plane_state->src_w > (unsigned) b->cursor_width << 16 ||
plane_state->src_h > (unsigned) b->cursor_height << 16 ||
plane_state->src_w != plane_state->dest_w << 16 ||
plane_state->src_h != plane_state->dest_h << 16) {
- drm_debug(b, "\t\t\t\t[cursor] not assigning view %p to cursor plane "
- "(positioning requires cropping or scaling)\n", ev);
+ drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
+ "(positioning requires cropping or scaling)\n",
+ p_name, ev, p_name);
goto err;
}
drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
if (needs_update) {
- drm_debug(b, "\t\t\t\t[cursor] copying new content to cursor BO\n");
+ drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name);
cursor_bo_update(plane_state, ev);
}
plane_state->dest_w = b->cursor_width;
plane_state->dest_h = b->cursor_height;
- drm_debug(b, "\t\t\t\t[cursor] provisionally assigned view %p to cursor\n",
- ev);
+ drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
+ p_name, ev);
return plane_state;
#else
static struct drm_plane_state *
drm_output_prepare_cursor_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev, uint64_t zpos)
{
return NULL;
}
static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev,
- enum drm_output_propose_state_mode mode)
+ enum drm_output_propose_state_mode mode,
+ struct drm_fb *fb, uint64_t zpos)
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
- struct drm_fb *fb;
- pixman_box32_t *extents;
+ const char *p_name = drm_output_get_plane_type_name(scanout_plane);
assert(!b->sprites_are_broken);
assert(b->atomic_modeset);
/* Check the view spans exactly the output size, calculated in the
* logical co-ordinate space. */
- extents = pixman_region32_extents(&ev->transform.boundingbox);
- if (extents->x1 != output->base.x ||
- extents->y1 != output->base.y ||
- extents->x2 != output->base.x + output->base.width ||
- extents->y2 != output->base.y + output->base.height)
+ if (!weston_view_matches_output_entirely(ev, &output->base)) {
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ " view does not match output entirely\n",
+ p_name, ev, p_name);
return NULL;
+ }
/* If the surface buffer has an in-fence fd, but the plane doesn't
* support fences, we can't place the buffer on this plane. */
if (ev->surface->acquire_fence_fd >= 0 &&
- scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0)
+ scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ "no in-fence support\n", p_name, ev, p_name);
return NULL;
+ }
- fb = drm_fb_get_from_view(output_state, ev);
if (!fb) {
- drm_debug(b, "\t\t\t\t[scanout] not placing view %p on scanout: "
- " couldn't get fb\n", ev);
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ " couldn't get fb\n", p_name, ev, p_name);
return NULL;
}
* and in the latter case, the view must have been marked as occluded,
* meaning we should never have ended up here. */
assert(!state->fb);
- state->fb = fb;
+
+ /* take another reference here to live within the state */
+ state->fb = drm_fb_ref(fb);
state->ev = ev;
state->output = output;
- if (!drm_plane_state_coords_for_view(state, ev))
+ if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ "unsuitable transform\n", p_name, ev, p_name);
goto err;
+ }
if (state->dest_x != 0 || state->dest_y != 0 ||
state->dest_w != (unsigned) output->base.current_mode->width ||
- state->dest_h != (unsigned) output->base.current_mode->height)
+ state->dest_h != (unsigned) output->base.current_mode->height) {
+ drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
+ " invalid plane state\n", p_name, ev, p_name);
goto err;
+ }
state->in_fence_fd = ev->surface->acquire_fence_fd;
return NULL;
}
+static bool
+drm_output_plane_view_has_valid_format(struct drm_plane *plane,
+ struct drm_output_state *state,
+ struct weston_view *ev,
+ struct drm_fb *fb)
+{
+ /* depending on the type of the plane we have different requirements */
+ switch (plane->type) {
+ case WDRM_PLANE_TYPE_CURSOR:
+ return drm_output_plane_cursor_has_valid_format(ev);
+ case WDRM_PLANE_TYPE_OVERLAY:
+ return drm_output_plane_has_valid_format(plane, state, fb);
+ case WDRM_PLANE_TYPE_PRIMARY:
+ return drm_output_plane_has_valid_format(plane, state, fb);
+ default:
+ assert(0);
+ return false;
+ }
+
+ return false;
+}
+
+static struct drm_plane_state *
+drm_output_try_view_on_plane(struct drm_plane *plane,
+ struct drm_output_state *state,
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode,
+ struct drm_fb *fb, uint64_t zpos)
+{
+ struct drm_backend *b = state->pending_state->backend;
+ struct weston_output *wet_output = &state->output->base;
+ bool view_matches_entire_output, scanout_has_view_assigned;
+ struct drm_plane *scanout_plane = state->output->scanout_plane;
+ struct drm_plane_state *ps = NULL;
+ const char *p_name = drm_output_get_plane_type_name(plane);
+ enum {
+ NO_PLANES, /* generic err-handle */
+ NO_PLANES_ACCEPTED,
+ PLACED_ON_PLANE,
+ } availability = NO_PLANES;
+
+ /* sanity checks in case we over/underflow zpos or pass incorrect
+ * values */
+ assert(zpos <= plane->zpos_max ||
+ zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
+
+ switch (plane->type) {
+ case WDRM_PLANE_TYPE_CURSOR:
+ if (b->cursors_are_broken) {
+ availability = NO_PLANES_ACCEPTED;
+ goto out;
+ }
+
+ ps = drm_output_prepare_cursor_view(state, ev, zpos);
+ if (ps)
+ availability = PLACED_ON_PLANE;
+ break;
+ case WDRM_PLANE_TYPE_OVERLAY:
+ /* do not attempt to place it in the overlay if we don't have
+ * anything in the scanout/primary and the view doesn't cover
+ * the entire output */
+ view_matches_entire_output =
+ weston_view_matches_output_entirely(ev, wet_output);
+ scanout_has_view_assigned =
+ drm_output_check_plane_has_view_assigned(scanout_plane,
+ state);
+
+ if (view_matches_entire_output && !scanout_has_view_assigned) {
+ availability = NO_PLANES_ACCEPTED;
+ goto out;
+ }
+
+ ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
+ fb, zpos);
+ if (ps)
+ availability = PLACED_ON_PLANE;
+ break;
+ case WDRM_PLANE_TYPE_PRIMARY:
+ if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
+ availability = NO_PLANES_ACCEPTED;
+ goto out;
+ }
+
+ ps = drm_output_prepare_scanout_view(state, ev, mode,
+ fb, zpos);
+ if (ps)
+ availability = PLACED_ON_PLANE;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+out:
+ switch (availability) {
+ case NO_PLANES:
+ /* set initial to this catch-all case, such that
+ * prepare_cursor/overlay/scanout() should have/contain the
+ * reason for failling */
+ break;
+ case NO_PLANES_ACCEPTED:
+ drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
+ "place view %p in %s\n",
+ plane->plane_id, ev, p_name);
+ break;
+ case PLACED_ON_PLANE:
+ break;
+ }
+
+
+ return ps;
+}
+
+static int
+drm_output_check_zpos_plane_states(struct drm_output_state *state)
+{
+ struct drm_backend *b = state->pending_state->backend;
+ struct drm_plane_state *ps;
+ int ret = 0;
+
+ wl_list_for_each(ps, &state->plane_list, link) {
+ struct wl_list *next_node = ps->link.next;
+ bool found_dup = false;
+
+ /* find another plane with the same zpos value */
+ if (next_node == &state->plane_list)
+ break;
+
+ while (next_node && next_node != &state->plane_list) {
+ struct drm_plane_state *ps_next;
+
+ ps_next = container_of(next_node,
+ struct drm_plane_state,
+ link);
+
+ if (ps->zpos == ps_next->zpos) {
+ found_dup = true;
+ break;
+ }
+ next_node = next_node->next;
+ }
+
+ if (found_dup) {
+ ret = 1;
+ drm_debug(b, "\t\t\t[plane] found duplicate zpos values\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct drm_plane_state *
+drm_output_prepare_plane_view(struct drm_output_state *state,
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode,
+ uint64_t current_lowest_zpos)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+
+ struct drm_plane_state *ps = NULL;
+ struct drm_plane *plane;
+ struct drm_plane_zpos *p_zpos, *p_zpos_next;
+ struct wl_list zpos_candidate_list;
+
+ struct drm_fb *fb;
+
+ wl_list_init(&zpos_candidate_list);
+
+ /* check view for valid buffer, doesn't make sense to even try */
+ if (!weston_view_has_valid_buffer(ev))
+ return ps;
+
+ fb = drm_fb_get_from_view(state, ev);
+
+ /* assemble a list with possible candidates */
+ wl_list_for_each(plane, &b->plane_list, link) {
+ if (!drm_plane_is_available(plane, output))
+ continue;
+
+ if (drm_output_check_plane_has_view_assigned(plane, state)) {
+ drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
+ " candidate list: view already assigned "
+ "to a plane\n", plane->plane_id);
+ continue;
+ }
+
+ if (plane->zpos_min >= current_lowest_zpos) {
+ drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
+ "candidate list: minium zpos (%"PRIu64") "
+ "plane's above current lowest zpos "
+ "(%"PRIu64")\n", plane->plane_id,
+ plane->zpos_min, current_lowest_zpos);
+ continue;
+ }
+
+ if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) {
+ drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
+ "candidate list: invalid pixel format\n",
+ plane->plane_id);
+ continue;
+ }
+
+ drm_output_add_zpos_plane(plane, &zpos_candidate_list);
+ }
+
+ /* go over the potential candidate list and try to find a possible
+ * plane suitable for \c ev; start with the highest zpos value of a
+ * plane to maximize our chances, but do note we pass the zpos value
+ * based on current tracked value by \c current_lowest_zpos_in_use */
+ while (!wl_list_empty(&zpos_candidate_list)) {
+ struct drm_plane_zpos *head_p_zpos =
+ wl_container_of(zpos_candidate_list.next,
+ head_p_zpos, link);
+ struct drm_plane *plane = head_p_zpos->plane;
+ const char *p_name = drm_output_get_plane_type_name(plane);
+ uint64_t zpos;
+
+ if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
+ zpos = plane->zpos_max;
+ else
+ zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
+
+ drm_debug(b, "\t\t\t\t[plane] plane %d picked "
+ "from candidate list, type: %s\n",
+ plane->plane_id, p_name);
+
+ ps = drm_output_try_view_on_plane(plane, state, ev,
+ mode, fb, zpos);
+ drm_output_destroy_zpos_plane(head_p_zpos);
+ if (ps) {
+ drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
+ "%s plane with computed zpos %"PRIu64"\n",
+ ev, p_name, zpos);
+ break;
+ }
+ }
+
+ wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
+ drm_output_destroy_zpos_plane(p_zpos);
+
+ drm_fb_unref(fb);
+ return ps;
+}
+
static struct drm_output_state *
drm_output_propose_state(struct weston_output *output_base,
struct drm_pending_state *pending_state,
struct drm_output_state *state;
struct drm_plane_state *scanout_state = NULL;
struct weston_view *ev;
- pixman_region32_t surface_overlap, renderer_region, occluded_region;
- bool planes_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
+
+ pixman_region32_t surface_overlap, renderer_region, planes_region;
+ pixman_region32_t occluded_region;
+
bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
int ret;
+ uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
(unsigned long) output->base.id);
}
- /*
- * Find a surface for each sprite in the output using some heuristics:
- * 1) size
- * 2) frequency of update
- * 3) opacity (though some hw might support alpha blending)
- * 4) clipping (this can be fixed with color keys)
- *
- * The idea is to save on blitting since this should save power.
- * If we can get a large video surface on the sprite for example,
- * the main display surface may not need to update at all, and
- * the client buffer can be used directly for the sprite surface
- * as we do for flipping full screen surfaces.
+ /* - renderer_region contains the total region which which will be
+ * covered by the renderer
+ * - planes_region contains the total region which has been covered by
+ * hardware planes
+ * - occluded_region contains the total region which which will be
+ * covered by the renderer and hardware planes, where the view's
+ * visible-and-opaque region is added in both cases (the view's
+ * opaque region accumulates there for each view); it is being used
+ * to skip the view, if it is completely occluded; includes the
+ * situation where occluded_region covers entire output's region.
*/
pixman_region32_init(&renderer_region);
+ pixman_region32_init(&planes_region);
pixman_region32_init(&occluded_region);
wl_list_for_each(ev, &output_base->compositor->view_list, link) {
bool force_renderer = false;
pixman_region32_t clipped_view;
bool totally_occluded = false;
- bool overlay_occluded = false;
drm_debug(b, "\t\t\t[view] evaluating view %p for "
"output %s (%lu)\n",
force_renderer = true;
}
- if (!ev->surface->buffer_ref.buffer) {
+ if (!weston_view_has_valid_buffer(ev)) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(no buffer available)\n", ev);
force_renderer = true;
pixman_region32_init(&surface_overlap);
pixman_region32_subtract(&surface_overlap, &clipped_view,
&occluded_region);
+ /* if the view is completely occluded then ignore that
+ * view; includes the case where occluded_region covers
+ * the entire output */
totally_occluded = !pixman_region32_not_empty(&surface_overlap);
if (totally_occluded) {
drm_debug(b, "\t\t\t\t[view] ignoring view %p "
"(occluded by renderer views)\n", ev);
force_renderer = true;
}
+ pixman_region32_fini(&surface_overlap);
/* In case of enforced mode of content-protection do not
* assign planes for a protected surface on an unsecured output.
force_renderer = true;
}
- /* We do not control the stacking order of overlay planes;
- * the scanout plane is strictly stacked bottom and the cursor
- * plane top, but the ordering of overlay planes with respect
- * to each other is undefined. Make sure we do not have two
- * planes overlapping each other. */
- pixman_region32_intersect(&surface_overlap, &occluded_region,
- &clipped_view);
- if (pixman_region32_not_empty(&surface_overlap)) {
- drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
- "(occluded by other overlay planes)\n", ev);
- overlay_occluded = true;
+ if (!force_renderer) {
+ drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
+ current_lowest_zpos);
+ ps = drm_output_prepare_plane_view(state, ev, mode,
+ current_lowest_zpos);
}
- pixman_region32_fini(&surface_overlap);
-
- /* The cursor plane is 'special' in the sense that we can still
- * place it in the legacy API, and we gate that with a separate
- * cursors_are_broken flag. */
- if (!force_renderer && !overlay_occluded && !b->cursors_are_broken)
- ps = drm_output_prepare_cursor_view(state, ev);
-
- /* If sprites are disabled or the view is not fully opaque, we
- * must put the view into the renderer - unless it has already
- * been placed in the cursor plane, which can handle alpha. */
- if (!ps && !planes_ok) {
- drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
- "(precluded by mode)\n", ev);
- force_renderer = true;
- }
- if (!ps && !weston_view_is_opaque(ev, &clipped_view)) {
- drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
- "(view not fully opaque)\n", ev);
- force_renderer = true;
- }
-
- /* Only try to place scanout surfaces in planes-only mode; in
- * mixed mode, we have already failed to place a view on the
- * scanout surface, forcing usage of the renderer on the
- * scanout plane. */
- if (!ps && !force_renderer && !renderer_ok)
- ps = drm_output_prepare_scanout_view(state, ev, mode);
-
- if (!ps && !overlay_occluded && !force_renderer)
- ps = drm_output_prepare_overlay_view(state, ev, mode);
if (ps) {
+ current_lowest_zpos = ps->zpos;
+ drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n",
+ current_lowest_zpos);
+
/* If we have been assigned to an overlay or scanout
* plane, add this area to the occluded region, so
* other views are known to be behind it. The cursor
* be added to the renderer region nor the occluded
* region. */
if (ps->plane->type != WDRM_PLANE_TYPE_CURSOR) {
+ pixman_region32_union(&planes_region,
+ &planes_region,
+ &clipped_view);
+
+ if (!weston_view_is_opaque(ev, &clipped_view))
+ pixman_region32_intersect(&clipped_view,
+ &clipped_view,
+ &ev->transform.opaque);
+ /* the visible-and-opaque region of this view
+ * will occlude views underneath it */
pixman_region32_union(&occluded_region,
&occluded_region,
&clipped_view);
+
pixman_region32_fini(&clipped_view);
+
}
continue;
}
pixman_region32_union(&renderer_region,
&renderer_region,
&clipped_view);
+
+ if (!weston_view_is_opaque(ev, &clipped_view))
+ pixman_region32_intersect(&clipped_view,
+ &clipped_view,
+ &ev->transform.opaque);
+
+ pixman_region32_union(&occluded_region,
+ &occluded_region,
+ &clipped_view);
+
pixman_region32_fini(&clipped_view);
+
+ drm_debug(b, "\t\t\t\t[view] view %p will be placed "
+ "on the renderer\n", ev);
}
+
pixman_region32_fini(&renderer_region);
+ pixman_region32_fini(&planes_region);
pixman_region32_fini(&occluded_region);
/* In renderer-only mode, we can't test the state as we don't have a
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY)
return state;
+ /* check if we have invalid zpos values, like duplicate(s) */
+ ret = drm_output_check_zpos_plane_states(state);
+ if (ret != 0) {
+ drm_debug(b, "\t\t[view] failing state generation: "
+ "zpos values are in-consistent\n");
+ goto err;
+ }
+
/* Check to see if this state will actually work. */
ret = drm_pending_state_test(state->pending_state);
if (ret != 0) {
* to the buffer anyway, there is no side effects.
*/
if (b->use_pixman ||
- (ev->surface->buffer_ref.buffer &&
+ (weston_view_has_valid_buffer(ev) &&
(!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
(ev->surface->width <= b->cursor_width &&
ev->surface->height <= b->cursor_height))))