clutter: Add infrastructure to render ClutterStageViews to offscreen

The offscreen is given through the ::back-buffer property, the ClutterStageView
will set up the the CoglPipeline used to render it back to the "onscreen"
framebuffer.

The pipeline can be altered through the setup_pipeline() vfunc, so ClutterStageView
implementations can alter the default behavior of blitting from offscreen to
onscreen with no transformations.

https://bugzilla.gnome.org/show_bug.cgi?id=745079
This commit is contained in:
Carlos Garnacho 2016-08-01 02:44:57 +02:00
parent acc2ad9658
commit 6302ffe8ac
5 changed files with 191 additions and 7 deletions

View File

@ -27,6 +27,7 @@ enum
PROP_LAYOUT, PROP_LAYOUT,
PROP_FRAMEBUFFER, PROP_FRAMEBUFFER,
PROP_OFFSCREEN,
PROP_LAST PROP_LAST
}; };
@ -37,6 +38,10 @@ typedef struct _ClutterStageViewPrivate
{ {
cairo_rectangle_int_t layout; cairo_rectangle_int_t layout;
CoglFramebuffer *framebuffer; CoglFramebuffer *framebuffer;
CoglOffscreen *offscreen;
CoglPipeline *pipeline;
guint dirty_viewport : 1; guint dirty_viewport : 1;
guint dirty_projection : 1; guint dirty_projection : 1;
} ClutterStageViewPrivate; } ClutterStageViewPrivate;
@ -55,6 +60,18 @@ clutter_stage_view_get_layout (ClutterStageView *view,
CoglFramebuffer * CoglFramebuffer *
clutter_stage_view_get_framebuffer (ClutterStageView *view) clutter_stage_view_get_framebuffer (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
if (priv->offscreen)
return priv->offscreen;
else
return priv->framebuffer;
}
CoglFramebuffer *
clutter_stage_view_get_onscreen (ClutterStageView *view)
{ {
ClutterStageViewPrivate *priv = ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view); clutter_stage_view_get_instance_private (view);
@ -62,6 +79,108 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view)
return priv->framebuffer; return priv->framebuffer;
} }
static void
clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
ClutterStageViewClass *view_class =
CLUTTER_STAGE_VIEW_GET_CLASS (view);
g_assert (priv->offscreen != NULL);
if (priv->pipeline)
return;
priv->pipeline =
cogl_pipeline_new (cogl_framebuffer_get_context (priv->offscreen));
cogl_pipeline_set_layer_filters (priv->pipeline, 0,
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
cogl_pipeline_set_layer_texture (priv->pipeline, 0,
cogl_offscreen_get_texture (priv->offscreen));
cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0,
COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
if (view_class->setup_offscreen_blit_pipeline)
view_class->setup_offscreen_blit_pipeline (view, priv->pipeline);
}
void
clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
g_clear_pointer (&priv->pipeline, cogl_object_unref);
}
static void
transform_rect_to_onscreen (ClutterStageView *view,
const cairo_rectangle_t *rect,
cairo_rectangle_t *rect_out)
{
float x1, y1, x2, y2;
x1 = rect->x;
y1 = rect->y;
clutter_stage_view_transform_to_onscreen (view, &x1, &y1);
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
clutter_stage_view_transform_to_onscreen (view, &x2, &y2);
*rect_out = (cairo_rectangle_t) {
.x = MIN (x1, x2),
.y = MIN (y1, y2),
.width = ABS (x2 - x1),
.height = ABS (y2 - y1)
};
}
void
clutter_stage_view_blit_offscreen (ClutterStageView *view,
const cairo_rectangle_int_t *rect)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
cairo_rectangle_t texture_rect, onscreen_rect;
CoglMatrix matrix;
clutter_stage_view_ensure_offscreen_blit_pipeline (view);
cogl_framebuffer_push_matrix (priv->framebuffer);
/* Set transform so 0,0 is on the top left corner and 1,1 on
* the bottom right corner.
*/
cogl_matrix_init_identity (&matrix);
cogl_matrix_translate (&matrix, -1, 1, 0);
cogl_matrix_scale (&matrix, 2, -2, 0);
cogl_framebuffer_set_projection_matrix (priv->framebuffer, &matrix);
texture_rect = (cairo_rectangle_t) {
.x = (double) rect->x / cogl_framebuffer_get_width (priv->offscreen),
.y = (double) rect->y / cogl_framebuffer_get_height (priv->offscreen),
.width = (double) rect->width / cogl_framebuffer_get_width (priv->offscreen),
.height = (double) rect->height / cogl_framebuffer_get_height (priv->offscreen)
};
transform_rect_to_onscreen (view, &texture_rect, &onscreen_rect);
cogl_framebuffer_draw_textured_rectangle (priv->framebuffer,
priv->pipeline,
onscreen_rect.x,
onscreen_rect.y,
onscreen_rect.x + onscreen_rect.width,
onscreen_rect.y + onscreen_rect.height,
texture_rect.x,
texture_rect.y,
texture_rect.x + texture_rect.width,
texture_rect.y + texture_rect.height);
cogl_framebuffer_pop_matrix (priv->framebuffer);
}
gboolean gboolean
clutter_stage_view_is_dirty_viewport (ClutterStageView *view) clutter_stage_view_is_dirty_viewport (ClutterStageView *view)
{ {
@ -100,6 +219,27 @@ clutter_stage_view_set_dirty_projection (ClutterStageView *view,
priv->dirty_projection = dirty; priv->dirty_projection = dirty;
} }
void
clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
gfloat *x,
gfloat *y)
{
ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view);
gfloat z = 0, w = 1;
CoglMatrix matrix;
view_class->get_offscreen_transformation_matrix (view, &matrix);
cogl_matrix_get_inverse (&matrix, &matrix);
cogl_matrix_transform_point (&matrix, x, y, &z, &w);
}
static void
clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view,
CoglMatrix *matrix)
{
cogl_matrix_init_identity (matrix);
}
static void static void
clutter_stage_view_get_property (GObject *object, clutter_stage_view_get_property (GObject *object,
guint prop_id, guint prop_id,
@ -118,6 +258,11 @@ clutter_stage_view_get_property (GObject *object,
case PROP_FRAMEBUFFER: case PROP_FRAMEBUFFER:
g_value_set_boxed (value, priv->framebuffer); g_value_set_boxed (value, priv->framebuffer);
break; break;
case PROP_OFFSCREEN:
g_value_set_boxed (value, priv->offscreen);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
} }
@ -141,6 +286,11 @@ clutter_stage_view_set_property (GObject *object,
case PROP_FRAMEBUFFER: case PROP_FRAMEBUFFER:
priv->framebuffer = g_value_dup_boxed (value); priv->framebuffer = g_value_dup_boxed (value);
break; break;
case PROP_OFFSCREEN:
priv->offscreen = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
} }
@ -152,6 +302,10 @@ clutter_stage_view_dispose (GObject *object)
clutter_stage_view_get_instance_private (view); clutter_stage_view_get_instance_private (view);
g_clear_pointer (&priv->framebuffer, cogl_object_unref); g_clear_pointer (&priv->framebuffer, cogl_object_unref);
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->pipeline, cogl_object_unref);
G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
} }
static void static void
@ -169,6 +323,9 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
klass->get_offscreen_transformation_matrix =
clutter_stage_default_get_offscreen_transformation_matrix;
object_class->get_property = clutter_stage_view_get_property; object_class->get_property = clutter_stage_view_get_property;
object_class->set_property = clutter_stage_view_set_property; object_class->set_property = clutter_stage_view_set_property;
object_class->dispose = clutter_stage_view_dispose; object_class->dispose = clutter_stage_view_dispose;
@ -184,10 +341,19 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass)
obj_props[PROP_FRAMEBUFFER] = obj_props[PROP_FRAMEBUFFER] =
g_param_spec_boxed ("framebuffer", g_param_spec_boxed ("framebuffer",
"View framebuffer", "View framebuffer",
"The framebuffer of the view", "The front buffer of the view",
COGL_TYPE_HANDLE, COGL_TYPE_HANDLE,
G_PARAM_READWRITE | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS); G_PARAM_STATIC_STRINGS);
obj_props[PROP_OFFSCREEN] =
g_param_spec_boxed ("offscreen",
"Offscreen buffer",
"Framebuffer used as intermediate buffer",
COGL_TYPE_HANDLE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props); g_object_class_install_properties (object_class, PROP_LAST, obj_props);
} }

View File

@ -33,6 +33,12 @@ G_DECLARE_DERIVABLE_TYPE (ClutterStageView, clutter_stage_view,
struct _ClutterStageViewClass struct _ClutterStageViewClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
void (* setup_offscreen_blit_pipeline) (ClutterStageView *view,
CoglPipeline *pipeline);
void (* get_offscreen_transformation_matrix) (ClutterStageView *view,
CoglMatrix *matrix);
}; };
CLUTTER_AVAILABLE_IN_MUTTER CLUTTER_AVAILABLE_IN_MUTTER
@ -41,6 +47,18 @@ void clutter_stage_view_get_layout (ClutterStageView *view,
CLUTTER_AVAILABLE_IN_MUTTER CLUTTER_AVAILABLE_IN_MUTTER
CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view); CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view);
CLUTTER_AVAILABLE_IN_MUTTER
CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view);
CLUTTER_AVAILABLE_IN_MUTTER
void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view);
CLUTTER_AVAILABLE_IN_MUTTER
void clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
gfloat *x,
gfloat *y);
void clutter_stage_view_blit_offscreen (ClutterStageView *view,
const cairo_rectangle_int_t *clip);
gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view);

View File

@ -357,7 +357,7 @@ swap_framebuffer (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *swap_region, cairo_rectangle_int_t *swap_region,
gboolean swap_with_damage) gboolean swap_with_damage)
{ {
CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
int damage[4], ndamage; int damage[4], ndamage;
damage[0] = swap_region->x; damage[0] = swap_region->x;

View File

@ -379,7 +379,7 @@ on_crtc_flipped (GClosure *closure,
{ {
ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
CoglFramebuffer *framebuffer = CoglFramebuffer *framebuffer =
clutter_stage_view_get_framebuffer (stage_view); clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
@ -397,7 +397,7 @@ flip_closure_destroyed (MetaRendererView *view)
{ {
ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
CoglFramebuffer *framebuffer = CoglFramebuffer *framebuffer =
clutter_stage_view_get_framebuffer (stage_view); clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
@ -828,7 +828,7 @@ meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
{ {
ClutterStageView *stage_view = l->data; ClutterStageView *stage_view = l->data;
CoglFramebuffer *framebuffer = CoglFramebuffer *framebuffer =
clutter_stage_view_get_framebuffer (stage_view); clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
@ -861,7 +861,7 @@ meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native,
MetaMonitorManagerKms *monitor_manager_kms = MetaMonitorManagerKms *monitor_manager_kms =
META_MONITOR_MANAGER_KMS (monitor_manager); META_MONITOR_MANAGER_KMS (monitor_manager);
CoglFramebuffer *framebuffer = CoglFramebuffer *framebuffer =
clutter_stage_view_get_framebuffer (stage_view); clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaOnscreenNative *onscreen_native = egl_onscreen->platform;

View File

@ -127,7 +127,7 @@ ensure_frame_callback (MetaStageNative *stage_native,
if (closure) if (closure)
return; return;
framebuffer = clutter_stage_view_get_framebuffer (stage_view); framebuffer = clutter_stage_view_get_onscreen (stage_view);
onscreen = COGL_ONSCREEN (framebuffer); onscreen = COGL_ONSCREEN (framebuffer);
closure = cogl_onscreen_add_frame_callback (onscreen, closure = cogl_onscreen_add_frame_callback (onscreen,
frame_cb, frame_cb,