mirror of
https://github.com/brl/mutter.git
synced 2025-01-09 19:22:22 +00:00
wayland: Implement direct scanout for cropped and scaled surfaces
Until now we only supported direct scanout to the primary plane if the buffer size perfectly matched the display size. Since display controllers usually support scaling and cropping buffers highly efficiently, try to let them do the job. This is usually helpful if wp_viewporter is used by the client or Mutter uses fractional scaling. This has several advantages: - Games (e.g. SDL2 based ones) can almost always hit direct scanout paths in fullscreen mode. Notably when fractional scaling is used or the game renders in a non-native resolution (or both). - Video players using YUV buffer formats and wp_viewporter can easily hit direct scanout paths, making displaying video very power efficient as the 3D engine is not used at all. Note that this still only uses the primary plane, no overlay or underlay planes, making this change comparatively low risk. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3177>
This commit is contained in:
parent
ed50cbbfe4
commit
bd6196f4ca
@ -92,7 +92,6 @@ find_scanout_candidate (MetaCompositorView *compositor_view,
|
||||
MetaSurfaceActor *surface_actor;
|
||||
MetaSurfaceActorWayland *surface_actor_wayland;
|
||||
MetaWaylandSurface *surface;
|
||||
int geometry_scale;
|
||||
|
||||
if (meta_compositor_is_unredirect_inhibited (compositor))
|
||||
{
|
||||
@ -191,26 +190,11 @@ find_scanout_candidate (MetaCompositorView *compositor_view,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
surface_actor = meta_window_actor_get_scanout_candidate (window_actor);
|
||||
if (!surface_actor)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: window-actor has no scanout candidate");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (meta_surface_actor_is_effectively_obscured (surface_actor))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: surface-actor is obscured");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!clutter_actor_get_paint_box (CLUTTER_ACTOR (surface_actor),
|
||||
if (!clutter_actor_get_paint_box (CLUTTER_ACTOR (window_actor),
|
||||
&actor_box))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: no actor paint-box");
|
||||
"No direct scanout candidate: no window actor paint-box");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -232,6 +216,22 @@ find_scanout_candidate (MetaCompositorView *compositor_view,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
surface_actor = meta_window_actor_get_scanout_candidate (window_actor);
|
||||
if (!surface_actor)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: window-actor has no scanout "
|
||||
"candidate");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (meta_surface_actor_is_effectively_obscured (surface_actor))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: surface-actor is obscured");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
surface_actor_wayland = META_SURFACE_ACTOR_WAYLAND (surface_actor);
|
||||
surface = meta_surface_actor_wayland_get_surface (surface_actor_wayland);
|
||||
if (!surface)
|
||||
@ -241,18 +241,6 @@ find_scanout_candidate (MetaCompositorView *compositor_view,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
|
||||
|
||||
if (!meta_wayland_surface_can_scanout_untransformed (surface,
|
||||
renderer_view,
|
||||
geometry_scale))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"No direct scanout candidate: surface can not be scanned out "
|
||||
"untransformed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*crtc_out = crtc;
|
||||
*onscreen_out = COGL_ONSCREEN (framebuffer);
|
||||
*surface_out = surface;
|
||||
@ -268,8 +256,10 @@ try_assign_next_scanout (MetaCompositorView *compositor_view,
|
||||
ClutterStageView *stage_view;
|
||||
g_autoptr (CoglScanout) scanout = NULL;
|
||||
|
||||
stage_view = meta_compositor_view_get_stage_view (compositor_view);
|
||||
scanout = meta_wayland_surface_try_acquire_scanout (surface,
|
||||
onscreen);
|
||||
onscreen,
|
||||
stage_view);
|
||||
if (!scanout)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
@ -277,8 +267,6 @@ try_assign_next_scanout (MetaCompositorView *compositor_view,
|
||||
return;
|
||||
}
|
||||
|
||||
stage_view = meta_compositor_view_get_stage_view (compositor_view);
|
||||
|
||||
clutter_stage_view_assign_next_scanout (stage_view, scanout);
|
||||
}
|
||||
|
||||
|
@ -384,18 +384,14 @@ META_EXPORT_TEST
|
||||
int meta_wayland_surface_get_buffer_height (MetaWaylandSurface *surface);
|
||||
|
||||
CoglScanout * meta_wayland_surface_try_acquire_scanout (MetaWaylandSurface *surface,
|
||||
CoglOnscreen *onscreen);
|
||||
CoglOnscreen *onscreen,
|
||||
ClutterStageView *stage_view);
|
||||
|
||||
MetaCrtc * meta_wayland_surface_get_scanout_candidate (MetaWaylandSurface *surface);
|
||||
|
||||
void meta_wayland_surface_set_scanout_candidate (MetaWaylandSurface *surface,
|
||||
MetaCrtc *crtc);
|
||||
|
||||
gboolean
|
||||
meta_wayland_surface_can_scanout_untransformed (MetaWaylandSurface *surface,
|
||||
MetaRendererView *view,
|
||||
int geometry_scale);
|
||||
|
||||
int meta_wayland_surface_get_geometry_scale (MetaWaylandSurface *surface);
|
||||
|
||||
META_EXPORT_TEST
|
||||
|
@ -2255,18 +2255,87 @@ meta_wayland_surface_get_buffer_height (MetaWaylandSurface *surface)
|
||||
|
||||
CoglScanout *
|
||||
meta_wayland_surface_try_acquire_scanout (MetaWaylandSurface *surface,
|
||||
CoglOnscreen *onscreen)
|
||||
CoglOnscreen *onscreen,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaRendererView *renderer_view;
|
||||
MetaSurfaceActor *surface_actor;
|
||||
MetaMonitorTransform view_transform;
|
||||
ClutterActorBox actor_box;
|
||||
MtkRectangle *dst_rect_ptr = NULL;
|
||||
MtkRectangle dst_rect;
|
||||
graphene_rect_t *src_rect_ptr = NULL;
|
||||
graphene_rect_t src_rect;
|
||||
MtkRectangle view_rect;
|
||||
float view_scale;
|
||||
int untransformed_view_width;
|
||||
int untransformed_view_height;
|
||||
|
||||
if (!surface->buffer)
|
||||
return NULL;
|
||||
|
||||
if (surface->buffer->use_count == 0)
|
||||
return NULL;
|
||||
|
||||
renderer_view = META_RENDERER_VIEW (stage_view);
|
||||
view_transform = meta_renderer_view_get_transform (renderer_view);
|
||||
if (view_transform != surface->buffer_transform)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out: buffer transform does not "
|
||||
"match renderer-view transform");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface_actor = meta_wayland_surface_get_actor (surface);
|
||||
if (!surface_actor ||
|
||||
!clutter_actor_get_paint_box (CLUTTER_ACTOR (surface_actor), &actor_box))
|
||||
return NULL;
|
||||
|
||||
clutter_stage_view_get_layout (stage_view, &view_rect);
|
||||
view_scale = clutter_stage_view_get_scale (stage_view);
|
||||
|
||||
dst_rect = (MtkRectangle) {
|
||||
.x = roundf ((actor_box.x1 - view_rect.x) * view_scale),
|
||||
.y = roundf ((actor_box.y1 - view_rect.y) * view_scale),
|
||||
.width = roundf ((actor_box.x2 - actor_box.x1) * view_scale),
|
||||
.height = roundf ((actor_box.y2 - actor_box.y1) * view_scale),
|
||||
};
|
||||
|
||||
if (meta_monitor_transform_is_rotated (view_transform))
|
||||
{
|
||||
untransformed_view_width = view_rect.height;
|
||||
untransformed_view_height = view_rect.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
untransformed_view_width = view_rect.width;
|
||||
untransformed_view_height = view_rect.height;
|
||||
}
|
||||
|
||||
meta_rectangle_transform (&dst_rect,
|
||||
view_transform,
|
||||
untransformed_view_width,
|
||||
untransformed_view_height,
|
||||
&dst_rect);
|
||||
|
||||
/* Use an implicit destination rect when possible */
|
||||
if (surface->viewport.has_dst_size ||
|
||||
dst_rect.x != 0 || dst_rect.y != 0 ||
|
||||
dst_rect.width != untransformed_view_width ||
|
||||
dst_rect.height != untransformed_view_height)
|
||||
dst_rect_ptr = &dst_rect;
|
||||
|
||||
if (surface->viewport.has_src_rect)
|
||||
{
|
||||
src_rect = surface->viewport.src_rect;
|
||||
src_rect_ptr = &src_rect;
|
||||
}
|
||||
|
||||
return meta_wayland_buffer_try_acquire_scanout (surface->buffer,
|
||||
onscreen,
|
||||
NULL,
|
||||
NULL);
|
||||
src_rect_ptr,
|
||||
dst_rect_ptr);
|
||||
}
|
||||
|
||||
MetaCrtc *
|
||||
@ -2287,120 +2356,6 @@ meta_wayland_surface_set_scanout_candidate (MetaWaylandSurface *surface,
|
||||
obj_props[PROP_SCANOUT_CANDIDATE]);
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_wayland_surface_can_scanout_untransformed (MetaWaylandSurface *surface,
|
||||
MetaRendererView *view,
|
||||
int geometry_scale)
|
||||
{
|
||||
int surface_scale = surface->applied_state.scale;
|
||||
|
||||
if (meta_renderer_view_get_transform (view) != surface->buffer_transform)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out untransformed: buffer "
|
||||
"transform does not match renderer-view transform");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (surface->viewport.has_dst_size)
|
||||
{
|
||||
MtkRectangle view_layout;
|
||||
float view_scale;
|
||||
float untransformed_layout_width;
|
||||
float untransformed_layout_height;
|
||||
|
||||
clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
|
||||
view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
|
||||
|
||||
if (meta_monitor_transform_is_rotated (meta_renderer_view_get_transform (view)))
|
||||
{
|
||||
untransformed_layout_width = view_layout.height * view_scale;
|
||||
untransformed_layout_height = view_layout.width * view_scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
untransformed_layout_width = view_layout.width * view_scale;
|
||||
untransformed_layout_height = view_layout.height * view_scale;
|
||||
}
|
||||
|
||||
if ((view_layout.width / geometry_scale) != surface->viewport.dst_width ||
|
||||
(view_layout.height / geometry_scale) != surface->viewport.dst_height ||
|
||||
!G_APPROX_VALUE (untransformed_layout_width,
|
||||
meta_wayland_surface_get_buffer_width (surface),
|
||||
FLT_EPSILON) ||
|
||||
!G_APPROX_VALUE (untransformed_layout_height,
|
||||
meta_wayland_surface_get_buffer_height (surface),
|
||||
FLT_EPSILON))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out untransformed: viewport "
|
||||
"destination or buffer size does not match stage-view "
|
||||
"layout. (%d/%d != %d || %d/%d != %d || %f != %d %f != %d)",
|
||||
view_layout.width, geometry_scale, surface->viewport.dst_width,
|
||||
view_layout.height, geometry_scale, surface->viewport.dst_height,
|
||||
untransformed_layout_width,
|
||||
meta_wayland_surface_get_buffer_width (surface),
|
||||
untransformed_layout_height,
|
||||
meta_wayland_surface_get_buffer_height (surface));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MetaContext *context =
|
||||
meta_wayland_compositor_get_context (surface->compositor);
|
||||
MetaBackend *backend = meta_context_get_backend (context);
|
||||
|
||||
if (meta_backend_is_stage_views_scaled (backend))
|
||||
{
|
||||
float view_scale;
|
||||
|
||||
view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
|
||||
if (!G_APPROX_VALUE (view_scale, surface_scale, FLT_EPSILON))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out untransformed: "
|
||||
"buffer scale does not match stage-view scale");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (geometry_scale != surface_scale)
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out untransformed: "
|
||||
"buffer scale does not match actor geometry scale");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (surface->viewport.has_src_rect)
|
||||
{
|
||||
if (!G_APPROX_VALUE (surface->viewport.src_rect.origin.x, 0.0,
|
||||
FLT_EPSILON) ||
|
||||
!G_APPROX_VALUE (surface->viewport.src_rect.origin.y, 0.0,
|
||||
FLT_EPSILON) ||
|
||||
!G_APPROX_VALUE (surface->viewport.src_rect.size.width *
|
||||
surface_scale,
|
||||
meta_wayland_surface_get_buffer_width (surface),
|
||||
FLT_EPSILON) ||
|
||||
!G_APPROX_VALUE (surface->viewport.src_rect.size.height *
|
||||
surface_scale,
|
||||
meta_wayland_surface_get_buffer_height (surface),
|
||||
FLT_EPSILON))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Surface can not be scanned out untransformed: viewport "
|
||||
"source rect does not cover the whole buffer");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
meta_wayland_surface_get_geometry_scale (MetaWaylandSurface *surface)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user