c0d5af5de5
A new (internal only currently) API, _clutter_actor_queue_clipped_redraw can be used to queue a redraw along with a clip rectangle in actor coordinates. This clip rectangle propagates up to the stage and clutter backend which may optionally use the information to optimize stage redraws. The GLX backend in particular may scissor the next redraw to the clip rectangle and use GLX_MESA_copy_sub_buffer to present the stage subregion. The intention is that any actors that can naturally determine the bounds of updates should queue clipped redraws to reduce the cost of updating small regions of the screen. Notes: » If GLX_MESA_copy_sub_buffer isn't available then the GLX backend ignores any clip rectangles. » queuing multiple clipped redraws will result in the bounding box of each clip rectangle being used. » If a clipped redraw has a height > 300 pixels then it's promoted into a full stage redraw, so that the GPU doesn't end up blocking too long waiting for the vsync to reach the optimal position to avoid tearing. » Note: no empirical data was used to come up with this threshold so we may need to tune this. » Currently only ClutterX11TexturePixmap makes use of this new API. This is done via a new "queue-damage-redraw" signal that is emitted when the pixmap is updated. The default handler queues a clipped redraw with the assumption that the pixmap is being painted as a rectangle covering the actors transformed allocation. If you subclass ClutterX11TexturePixmap and change how it's painted you now also need to override the signal handler and queue your own redraw. Technically this is a semantic break, but it's assumed that no one is currently doing this. This still leaves a few unsolved issues with regards to optimizing sub stage redraws that need to be addressed in further work so this can only be considered a stepping stone a this point: » Because we have no reliable way to determine if the painting of any given actor is being modified any optimizations implemented using _clutter_actor_queue_redraw_with_clip must be overridable by a subclass, and technically must be opt-in for existing classes to avoid a change in semantics. E.g. consider that a user connects to the paint signal for ClutterTexture and paints a circle instead of a rectangle. In this case any original logic to queue clipped redraws would be incorrect. » Currently only the implementation of an actor has enough information with which to queue clipped redraws. E.g. It is not possible for generic code in clutter-actor.c to queue a clipped redraw when hiding an actor because actors have no way to report a "paint box". (remember actors can draw outside their allocation and actors with depth may also be projected outside of their allocation) » The current plan is to add a actor_class->get_paint_cuboid() virtual so actors can report a bounding cube for everything they would draw in their current state and use that to queue clipped redraws against the stage by projecting the paint cube into stage coordinates. » Our heuristics for promoting clipped redraws into full redraws to avoid blocking the GPU while we wait for the vsync need improving: » vsync issues aren't relevant for redirected/composited applications so they should use different heuristics. In this case we instead need to trade off the cost of blitting when using glXCopySubBuffer vs promoting to a full redraw and flipping instead.
153 lines
4.1 KiB
C
153 lines
4.1 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <glib-object.h>
|
|
|
|
#include "clutter-actor.h"
|
|
#include "clutter-stage-window.h"
|
|
#include "clutter-private.h"
|
|
|
|
GType
|
|
clutter_stage_window_get_type (void)
|
|
{
|
|
static GType stage_window_type = 0;
|
|
|
|
if (G_UNLIKELY (stage_window_type == 0))
|
|
{
|
|
const GTypeInfo stage_window_info = {
|
|
sizeof (ClutterStageWindowIface),
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
stage_window_type =
|
|
g_type_register_static (G_TYPE_INTERFACE, I_("ClutterStageWindow"),
|
|
&stage_window_info, 0);
|
|
|
|
g_type_interface_add_prerequisite (stage_window_type,
|
|
G_TYPE_OBJECT);
|
|
}
|
|
|
|
return stage_window_type;
|
|
}
|
|
|
|
ClutterActor *
|
|
_clutter_stage_window_get_wrapper (ClutterStageWindow *window)
|
|
{
|
|
return CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_wrapper (window);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_set_title (ClutterStageWindow *window,
|
|
const gchar *title)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->set_title (window, title);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_set_fullscreen (ClutterStageWindow *window,
|
|
gboolean is_fullscreen)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->set_fullscreen (window,
|
|
is_fullscreen);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_set_cursor_visible (ClutterStageWindow *window,
|
|
gboolean is_visible)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->set_cursor_visible (window,
|
|
is_visible);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_set_user_resizable (ClutterStageWindow *window,
|
|
gboolean is_resizable)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->set_user_resizable (window,
|
|
is_resizable);
|
|
}
|
|
|
|
gboolean
|
|
_clutter_stage_window_realize (ClutterStageWindow *window)
|
|
{
|
|
return CLUTTER_STAGE_WINDOW_GET_IFACE (window)->realize (window);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_unrealize (ClutterStageWindow *window)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->unrealize (window);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_show (ClutterStageWindow *window,
|
|
gboolean do_raise)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->show (window, do_raise);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_hide (ClutterStageWindow *window)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->hide (window);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_resize (ClutterStageWindow *window,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->resize (window, width, height);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
|
ClutterGeometry *geometry)
|
|
{
|
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_geometry (window, geometry);
|
|
}
|
|
|
|
int
|
|
_clutter_stage_window_get_pending_swaps (ClutterStageWindow *window)
|
|
{
|
|
if (!CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_pending_swaps)
|
|
{
|
|
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
|
return 0;
|
|
}
|
|
|
|
return CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_pending_swaps (window);
|
|
}
|
|
|
|
void
|
|
_clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
|
|
ClutterGeometry *stage_clip)
|
|
{
|
|
ClutterStageWindowIface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
|
if (iface->add_redraw_clip)
|
|
iface->add_redraw_clip (window, stage_clip);
|
|
}
|
|
|
|
gboolean
|
|
_clutter_stage_window_has_redraw_clips (ClutterStageWindow *window)
|
|
{
|
|
ClutterStageWindowIface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
|
if (iface->has_redraw_clips)
|
|
return iface->has_redraw_clips (window);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_clutter_stage_window_ignoring_redraw_clips (ClutterStageWindow *window)
|
|
{
|
|
ClutterStageWindowIface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
|
if (iface->ignoring_redraw_clips)
|
|
return iface->ignoring_redraw_clips (window);
|
|
else
|
|
return TRUE;
|
|
}
|
|
|