diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 7ebadff66..12cf8eade 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -987,10 +987,9 @@ is_paint_node_solid_opaque_untransformed(struct weston_paint_node *pnode) } static bool -lower_solid_views_to_background_region(struct drm_output *output, - struct wl_array *visible_pnodes, - struct weston_paint_node **last_visible_pnode, - pixman_region32_t *background_region) +try_solid_pnodes_to_background(struct drm_output *output, struct wl_array *visible_pnodes, + struct weston_paint_node **last_visible_pnode, + pixman_region32_t *background_region) { struct drm_device *device = output->device; struct drm_backend *b = device->backend; @@ -1275,6 +1274,291 @@ drm_output_propose_state_try_reuse(struct weston_output *output_base, return state; } +static bool +paint_node_has_direct_display_buffer(struct weston_paint_node *pnode) +{ + struct weston_buffer *buffer = pnode->surface->buffer_ref.buffer; + + if (buffer->type == WESTON_BUFFER_DMABUF) { + if (buffer->direct_display) + return true; + } + + return false; +} + + +static void +filter_pnodes_with_direct_display(struct drm_output *output, + struct wl_array *visible_pnodes, + struct wl_array *pnodes_with_direct_display, + struct wl_array *pnodes_remaining) +{ + struct weston_paint_node **pnode; + + wl_array_for_each(pnode, visible_pnodes) { + if (paint_node_has_direct_display_buffer(*pnode)) { + struct weston_paint_node **pnode_new = + wl_array_add(pnodes_with_direct_display, sizeof(*pnode)); + *pnode_new = *pnode; + } else { + struct weston_paint_node **pnode_new = + wl_array_add(pnodes_remaining, sizeof(*pnode)); + *pnode_new = *pnode; + } + } +} + +static void +find_pnodes_above_direct_display(struct drm_output *output, + struct weston_paint_node *pnode_dd, + struct wl_array *result) +{ + struct weston_paint_node *pnode; + + /* all the paint nodes until pnode_dd being stacked on top should be + * above pnode_dd; we just need to test for occlusion */ + wl_list_for_each(pnode, &output->base.paint_node_z_order_list, + z_order_link) { + bool pnode_above = false; + + if (pnode == pnode_dd) + break; + + pixman_region32_t overlap; + pixman_region32_init(&overlap); + + if (pnode->is_fully_opaque) + pixman_region32_intersect(&overlap, + weston_paint_node_get_opaque_region(pnode), + weston_paint_node_get_opaque_region(pnode_dd)); + else + pixman_region32_intersect(&overlap, &pnode->visible, &pnode_dd->visible); + + if (pixman_region32_not_empty(&overlap)) + pnode_above = true; + + if (pnode_above) { + struct weston_paint_node **node_above = + wl_array_add(result, sizeof(*node_above)); + *node_above = pnode; + } + + pixman_region32_fini(&overlap); + } +} + +static void +filter_pnodes_above_direct_display(struct drm_output *output, struct wl_array *visible_pnodes, + struct wl_array *pnodes_with_direct_display, struct wl_array *result) +{ + struct weston_paint_node **pn; + + wl_array_for_each(pn, pnodes_with_direct_display) + find_pnodes_above_direct_display(output, *pn, result); +} + +static void +filter_pnodes_duplicates(struct drm_output *output, struct wl_array *a, struct wl_array *b) +{ + struct weston_paint_node **pa; + struct wl_array tmp_dup; + + wl_array_init(&tmp_dup); + + wl_array_for_each(pa, a) { + struct weston_paint_node **pb; + bool found_dup = false; + + wl_array_for_each(pb, b) { + if (*pa == *pb) { + found_dup = true; + break; + } + } + + if (!found_dup) { + struct weston_paint_node **n = + wl_array_add(&tmp_dup, sizeof(*n)); + *n = *pa; + } + } + + wl_array_copy(a, &tmp_dup); + wl_array_release(&tmp_dup); +} + +static void +paint_node_print_array(struct drm_backend *b, struct wl_array *array) +{ + struct weston_paint_node **pn; + + wl_array_for_each(pn, array) { + struct weston_paint_node *p = *pn; + drm_debug(b, "\t\t\t\t[paint node] paint node %s\n", p->internal_name); + } +} + +static int +walk_paint_nodes(pixman_region32_t *renderer_region, + pixman_region32_t *obscured_region, + pixman_region32_t *background_region, + struct drm_plane_state *scanout_state, + struct drm_output_state *state, + enum drm_output_propose_state_mode mode, + uint64_t current_lowest_zpos_underlay, + uint64_t current_lowest_zpos_overlay, + struct drm_output *output, + struct wl_array *pnodes, + struct weston_paint_node *last_visible_pnode) +{ + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; + struct weston_paint_node **visible_pnode; + + /* Assign paint nodes to planes. */ + wl_array_for_each(visible_pnode, pnodes) { + struct weston_paint_node *pnode = *visible_pnode; + struct drm_plane_state *ps = NULL; + bool need_underlay = false; + pixman_region32_t tmp; + bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); + + drm_debug(b, "\t\t\t[paint node] evaluating paint node %s for plane " + "assignment on output %s (%lu)\n", + pnode->internal_name, output->base.name, + (unsigned long) output->base.id); + + if (!b->gbm) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_NO_GBM; + + if (!weston_paint_node_has_valid_buffer(pnode)) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_NO_BUFFER; + + if (pnode->draw_solid) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_SOLID_SURFACE; + + if (pnode->output->color_effect) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_OUTPUT_COLOR_EFFECT; + + if (pnode->surf_xform.transform && (!device->color_pipeline_supported || + !pnode->output->from_blend_to_output_by_backend)) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_NO_COLOR_TRANSFORM; + + /* Since we process views from top to bottom, we know that if + * the view intersects the calculated renderer region, it must + * be part of, or occluded by, it, and cannot go on an overlay + * plane. */ + pixman_region32_init(&tmp); + pixman_region32_intersect(&tmp, renderer_region, &pnode->clipped_view); + if (pixman_region32_not_empty(&tmp)) { + if (output->has_underlay) { + need_underlay = true; + } else { + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_OCCLUDED_BY_RENDERER; + drm_debug(b, "\t\t\t\t[paint node] not assigning paint node %s to a " + "plane (occluded by renderer views), current lowest " + "zpos change to %"PRIu64"\n", pnode->internal_name, + current_lowest_zpos_underlay); + } + } + pixman_region32_fini(&tmp); + + /* If need_underlay, but view contains alpha, then it needs to + * be rendered. Only fully-opaque views can go on an underlay. + */ + if (need_underlay && !pnode->is_fully_opaque) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_OCCLUDED_BY_RENDERER; + + /* In case of enforced mode of content-protection do not + * assign planes for a protected surface on an unsecured output. + */ + if (pnode->censored) + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION; + + if (pnode->surface->tear_control) + state->tear &= pnode->surface->tear_control->may_tear; + else + state->tear = 0; + + /* Now try to place it on a plane if we can. */ + if (!pnode->try_view_on_plane_failure_reasons) { + pixman_region32_t obscured_or_background_region; + + drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n", + need_underlay ? current_lowest_zpos_underlay : + current_lowest_zpos_overlay); + + pixman_region32_init(&obscured_or_background_region); + if (pnode == last_visible_pnode) { + pixman_region32_union(&obscured_or_background_region, + background_region, + obscured_region); + if (pixman_region32_not_empty(&obscured_or_background_region)) + drm_debug(b, "\t\t\t[plane] adding background region\n"); + } + + ps = drm_output_find_plane_for_paint_node(state, pnode, mode, + scanout_state, + &obscured_or_background_region, + current_lowest_zpos_overlay, + current_lowest_zpos_underlay, + need_underlay); + + pixman_region32_fini(&obscured_or_background_region); + } + + if (ps) { + if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY && + ps->plane->type == WDRM_PLANE_TYPE_OVERLAY) { + pixman_region32_union(obscured_region, + obscured_region, + weston_paint_node_get_opaque_region (pnode)); + } + + if (drm_mixed_mode_check_underlay(mode, scanout_state, ps->zpos)) + current_lowest_zpos_underlay = ps->zpos; + else + current_lowest_zpos_overlay = ps->zpos; + drm_debug(b, "\t\t\t[plane] next overlay zpos to use %"PRIu64"," + " next underlay zpos to use %"PRIu64"\n", + current_lowest_zpos_overlay, + current_lowest_zpos_underlay); + } else if (!ps && !renderer_ok) { + drm_debug(b, "\t\t[paint node] failing state generation: " + "placing paint node %s to renderer not allowed\n", + pnode->internal_name); + return -1; + } else if (!ps) { + FILE *dbg = weston_log_scope_stream(b->debug); + + if (dbg) { + fprintf(dbg, "\t\t\t\t[paint node] paint node %s will be placed on the renderer: ", + pnode->internal_name); + bits_to_str_stream(pnode->try_view_on_plane_failure_reasons, + weston_plane_failure_reasons_to_str, dbg); + fputs("\n", dbg); + } + } + + if (!ps || drm_mixed_mode_check_underlay(mode, scanout_state, ps->zpos)) { + /* visible contains the area that's going to be visible + * on screen; add this to the renderer region */ + pixman_region32_union(renderer_region, renderer_region, &pnode->visible); + } + } + + return 0; +} + static struct drm_output_state * drm_output_propose_state(struct weston_output *output_base, struct drm_pending_state *pending_state, @@ -1295,7 +1579,7 @@ drm_output_propose_state(struct weston_output *output_base, pixman_region32_t background_region; pixman_region32_t obscured_region; - int ret; + int ret = 0; /* Record the current lowest zpos of the overlay planes */ uint64_t current_lowest_zpos_overlay = DRM_PLANE_ZPOS_INVALID_PLANE; /* Record the current lowest zpos of the underlay plane */ @@ -1421,7 +1705,7 @@ drm_output_propose_state(struct weston_output *output_base, pixman_region32_init(&obscured_region); /* background_region contains the area that is covered by opaque - * solid views. If they are black this area can be fully ignored in + * solid paint nodes. If they are black this area can be fully ignored in * PLANES_ONLY mode according to the DRM spec: * * "Unless explicitly specified (via CRTC property or otherwise), the @@ -1435,160 +1719,123 @@ drm_output_propose_state(struct weston_output *output_base, * For other colors the same applies if the BACKGROUND_COLOR DRM * property is supported. * - * All said views can thus be ignored during plane assignment. + * All said paint nodes can thus be ignored during plane assignment. */ pixman_region32_init(&background_region); - if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY && - !lower_solid_views_to_background_region(output, - &visible_pnodes, - &last_visible_pnode, - &background_region)) - goto err_region; - - /* Assign paint nodes to planes. */ - wl_array_for_each(visible_pnode, &visible_pnodes) { - struct weston_paint_node *pnode = *visible_pnode; - struct drm_plane_state *ps = NULL; - bool need_underlay = false; - pixman_region32_t tmp; - bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - - drm_debug(b, "\t\t\t[paint node] evaluating paint node %s for plane " - "assignment on output %s (%lu)\n", - pnode->internal_name, output->base.name, - (unsigned long) output->base.id); - - if (!b->gbm) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_NO_GBM; - - if (!weston_paint_node_has_valid_buffer(pnode)) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_NO_BUFFER; - - if (pnode->draw_solid) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_SOLID_SURFACE; - - if (pnode->output->color_effect) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_OUTPUT_COLOR_EFFECT; - - if (pnode->surf_xform.transform && (!device->color_pipeline_supported || - !pnode->output->from_blend_to_output_by_backend)) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_NO_COLOR_TRANSFORM; - - /* Since we process views from top to bottom, we know that if - * the view intersects the calculated renderer region, it must - * be part of, or occluded by, it, and cannot go on an overlay - * plane. */ - pixman_region32_init(&tmp); - pixman_region32_intersect(&tmp, &renderer_region, - &pnode->clipped_view); - if (pixman_region32_not_empty(&tmp)) { - if (output->has_underlay) { - need_underlay = true; - } else { - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_OCCLUDED_BY_RENDERER; - drm_debug(b, "\t\t\t\t[paint node] not assigning paint node %s to a " - "plane (occluded by renderer views), current lowest " - "zpos change to %"PRIu64"\n", pnode->internal_name, - current_lowest_zpos_underlay); - } - } - pixman_region32_fini(&tmp); - - /* If need_underlay, but view contains alpha, then it needs to - * be rendered. Only fully-opaque views can go on an underlay. - */ - if (need_underlay && !pnode->is_fully_opaque) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_OCCLUDED_BY_RENDERER; - - /* In case of enforced mode of content-protection do not - * assign planes for a protected surface on an unsecured output. - */ - if (pnode->censored) - pnode->try_view_on_plane_failure_reasons |= - FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION; - - if (pnode->surface->tear_control) - state->tear &= pnode->surface->tear_control->may_tear; - else - state->tear = 0; - - /* Now try to place it on a plane if we can. */ - if (!pnode->try_view_on_plane_failure_reasons) { - pixman_region32_t obscured_or_background_region; - - drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n", - need_underlay ? current_lowest_zpos_underlay : - current_lowest_zpos_overlay); - - pixman_region32_init(&obscured_or_background_region); - if (pnode == last_visible_pnode) { - pixman_region32_union(&obscured_or_background_region, - &background_region, - &obscured_region); - if (pixman_region32_not_empty (&obscured_or_background_region)) - drm_debug(b, "\t\t\t[plane] adding background region\n"); - } - - ps = drm_output_find_plane_for_paint_node(state, pnode, mode, - scanout_state, - &obscured_or_background_region, - current_lowest_zpos_overlay, - current_lowest_zpos_underlay, - need_underlay); - - pixman_region32_fini(&obscured_or_background_region); - } - - if (ps) { - if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY && - ps->plane->type == WDRM_PLANE_TYPE_OVERLAY) { - pixman_region32_union(&obscured_region, - &obscured_region, - weston_paint_node_get_opaque_region (pnode)); - } - - if (drm_mixed_mode_check_underlay(mode, scanout_state, ps->zpos)) - current_lowest_zpos_underlay = ps->zpos; - else - current_lowest_zpos_overlay = ps->zpos; - drm_debug(b, "\t\t\t[plane] next overlay zpos to use %"PRIu64"," - " next underlay zpos to use %"PRIu64"\n", - current_lowest_zpos_overlay, - current_lowest_zpos_underlay); - } else if (!ps && !renderer_ok) { - drm_debug(b, "\t\t[paint node] failing state generation: " - "placing paint node %s to renderer not allowed\n", - pnode->internal_name); + if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { + bool solid_pnodes_to_bg = + try_solid_pnodes_to_background(output, &visible_pnodes, + &last_visible_pnode, &background_region); + if (!solid_pnodes_to_bg) goto err_region; - } else if (!ps) { - FILE *dbg = weston_log_scope_stream(b->debug); - - if (dbg) { - fprintf(dbg, "\t\t\t\t[paint node] paint node %s will be placed on the renderer: ", - pnode->internal_name); - bits_to_str_stream(pnode->try_view_on_plane_failure_reasons, - weston_plane_failure_reasons_to_str, dbg); - fputs("\n", dbg); - } - } - - if (!ps || drm_mixed_mode_check_underlay(mode, scanout_state, ps->zpos)) { - /* visible contains the area that's going to be visible - * on screen; add this to the renderer region */ - pixman_region32_union(&renderer_region, - &renderer_region, - &pnode->visible); - } } + if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) { + struct wl_array pnodes_with_direct_display; + struct wl_array pnodes_remaining; + struct wl_array pnodes_above_direct_display; + + unsigned int count_pnodes_dd = 0; + unsigned int count_pnodes_above_dd = 0; + + wl_array_init(&pnodes_with_direct_display); + wl_array_init(&pnodes_remaining); + wl_array_init(&pnodes_above_direct_display); + + filter_pnodes_with_direct_display(output, &visible_pnodes, + &pnodes_with_direct_display, + &pnodes_remaining); + + filter_pnodes_above_direct_display(output, &visible_pnodes, + &pnodes_with_direct_display, + &pnodes_above_direct_display); + + /* we might have the same paint nodes in + * pnodes_above_direct_display and in pnodes_remaining as we + * are searching for paint nodes above the direct display ones + * in all visible_pnodes so a paint node above direct-display + * would might also be in pnodes_remaining */ + filter_pnodes_duplicates(output, &pnodes_remaining, + &pnodes_above_direct_display); + + count_pnodes_dd = pnodes_with_direct_display.size / + sizeof(struct weston_paint_node *); + count_pnodes_above_dd = pnodes_above_direct_display.size / + sizeof(struct weston_paint_node *); + + drm_debug(b, "\t\t\t[paint node] visible paint nodes\n"); + paint_node_print_array(b, &visible_pnodes); + + if (count_pnodes_dd) { + drm_debug(b, "\t\t\t[paint node] pnodes direct display\n"); + paint_node_print_array(b, &pnodes_with_direct_display); + + if (count_pnodes_above_dd && !output->has_underlay) { + drm_debug(b, "\t\t\t[paint node] pnodes above " + "direct display\n"); + paint_node_print_array(b, &pnodes_above_direct_display); + + drm_debug(b, "\t\t\t[paint node] walking above " + "direct display paint node list\n"); + ret = walk_paint_nodes(&renderer_region, &obscured_region, + &background_region, scanout_state, + state, mode, + current_lowest_zpos_underlay, + current_lowest_zpos_overlay, + output, + &pnodes_above_direct_display, + last_visible_pnode); + if (ret) { + wl_array_release(&pnodes_with_direct_display); + wl_array_release(&pnodes_above_direct_display); + wl_array_release(&pnodes_remaining); + goto err_region; + } + } + + drm_debug(b, "\t\t\t[paint node] walking direct display " + "paint node list\n"); + + ret = walk_paint_nodes(&renderer_region, &obscured_region, + &background_region, scanout_state, + state, mode, + current_lowest_zpos_underlay, + current_lowest_zpos_overlay, + output, + &pnodes_with_direct_display, + last_visible_pnode); + if (ret) { + wl_array_release(&pnodes_with_direct_display); + wl_array_release(&pnodes_above_direct_display); + wl_array_release(&pnodes_remaining); + goto err_region; + } + + /* replace visible pnodes with remaining pnodes to + * avoid going over them again */ + wl_array_copy(&visible_pnodes, &pnodes_remaining); + + drm_debug(b, "\t\t\t[paint node] remaining visible paint nodes\n"); + paint_node_print_array(b, &visible_pnodes); + } + + wl_array_release(&pnodes_with_direct_display); + wl_array_release(&pnodes_above_direct_display); + wl_array_release(&pnodes_remaining); + } + + drm_debug(b, "\t\t\t[paint node] walking visible paint node list\n"); + ret = walk_paint_nodes(&renderer_region, &obscured_region, + &background_region, scanout_state, state, + mode, + current_lowest_zpos_underlay, + current_lowest_zpos_overlay, output, + &visible_pnodes, + last_visible_pnode); + if (ret) + goto err_region; + pixman_region32_fini(&renderer_region); pixman_region32_fini(&obscured_region); pixman_region32_fini(&background_region);