Merge branch 'wip/mvlad/fast-direct-display' into 'main'

backend-drm: Prioritize direct-display paint nodes

See merge request wayland/weston!2078
This commit is contained in:
Marius Vlad 2026-06-19 12:10:29 +03:00
commit 3f719bb601

View file

@ -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);