diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2016-02-10 14:36:23 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2016-02-10 14:36:23 +0100 |
commit | 05f3de9a0826ac5a32a9a01d171eb2745438822a (patch) | |
tree | a1b1847f10140f3e88fa0242bd5e29af56d610c4 | |
parent | 98439323dd6f1ce563754cd9bd85863f35ad7a2e (diff) | |
download | clutter-05f3de9a0826ac5a32a9a01d171eb2745438822a.tar.gz |
cogl: Add support for multiple views of the stage
Those have a rectangle and a transform, at the time of rendering
each view will be painted separately, while applying the corresponding
transform to the whole stage.
Also, implement get_geometry/get_hw_geometry() based on the views
when we have any.
-rw-r--r-- | clutter/cogl/clutter-stage-cogl.c | 449 | ||||
-rw-r--r-- | clutter/cogl/clutter-stage-cogl.h | 55 |
2 files changed, 427 insertions, 77 deletions
diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c index a94e08243..16d5ebdb2 100644 --- a/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/cogl/clutter-stage-cogl.c @@ -263,8 +263,8 @@ clutter_stage_cogl_hide (ClutterStageWindow *stage_window) } static void -clutter_stage_cogl_get_geometry (ClutterStageWindow *stage_window, - cairo_rectangle_int_t *geometry) +clutter_stage_cogl_get_hw_geometry (ClutterStageWindow *stage_window, + cairo_rectangle_int_t *geometry) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); int window_scale; @@ -273,7 +273,13 @@ clutter_stage_cogl_get_geometry (ClutterStageWindow *stage_window, if (geometry != NULL) { - if (stage_cogl->onscreen) + if (stage_cogl->views) + { + geometry->x = geometry->y = 0; + geometry->width = stage_cogl->fb_width / window_scale; + geometry->height = stage_cogl->fb_height / window_scale; + } + else if (stage_cogl->onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (stage_cogl->onscreen); @@ -293,6 +299,28 @@ clutter_stage_cogl_get_geometry (ClutterStageWindow *stage_window, } static void +clutter_stage_cogl_get_geometry (ClutterStageWindow *stage_window, + cairo_rectangle_int_t *geometry) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + int window_scale; + + window_scale = _clutter_stage_window_get_scale_factor (stage_window); + + if (geometry != NULL) + { + if (stage_cogl->views) + { + geometry->x = geometry->y = 0; + geometry->width = stage_cogl->geometry_width / window_scale; + geometry->height = stage_cogl->geometry_height/ window_scale; + } + else + clutter_stage_cogl_get_hw_geometry (stage_window, geometry); + } +} + +static void clutter_stage_cogl_resize (ClutterStageWindow *stage_window, gint width, gint height) @@ -409,6 +437,101 @@ valid_buffer_age (ClutterStageCogl *stage_cogl, int age) return age < MIN (stage_cogl->damage_index, DAMAGE_HISTORY_MAX); } +static void +clutter_stage_cogl_paint_stage (ClutterStageWindow *stage_window, + cairo_rectangle_int_t *clip_region, + gboolean use_clipped_redraw, + gboolean may_use_clipped_redraw) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + ClutterActor *wrapper; + gint window_scale; + + window_scale = _clutter_stage_window_get_scale_factor (stage_window); + wrapper = CLUTTER_ACTOR (stage_cogl->wrapper); + + if (use_clipped_redraw) + { + CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen); + + CLUTTER_NOTE (CLIPPING, + "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n", + clip_region->x, + clip_region->y, + clip_region->width, + clip_region->height); + + stage_cogl->using_clipped_redraw = TRUE; + + cogl_framebuffer_push_scissor_clip (fb, + clip_region->x * window_scale, + clip_region->y * window_scale, + clip_region->width * window_scale, + clip_region->height * window_scale); + _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region); + cogl_framebuffer_pop_clip (fb); + + stage_cogl->using_clipped_redraw = FALSE; + } + else + { + CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n"); + + /* If we are trying to debug redraw issues then we want to pass + * the bounding_redraw_clip so it can be visualized */ + if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) && + may_use_clipped_redraw) + { + _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region); + } + else + _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL); + } + + if (may_use_clipped_redraw && + G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))) + { + CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen); + CoglContext *ctx = cogl_framebuffer_get_context (fb); + static CoglPipeline *outline = NULL; + cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip; + ClutterActor *actor = CLUTTER_ACTOR (wrapper); + float x_1 = clip->x * window_scale; + float x_2 = clip->x + clip->width * window_scale; + float y_1 = clip->y * window_scale; + float y_2 = clip->y + clip->height * window_scale; + CoglVertexP2 quad[4] = { + { x_1, y_1 }, + { x_2, y_1 }, + { x_2, y_2 }, + { x_1, y_2 } + }; + CoglPrimitive *prim; + CoglMatrix modelview; + + if (outline == NULL) + { + outline = cogl_pipeline_new (ctx); + cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff); + } + + prim = cogl_primitive_new_p2 (ctx, + COGL_VERTICES_MODE_LINE_LOOP, + 4, /* n_vertices */ + quad); + + cogl_framebuffer_push_matrix (fb); + cogl_matrix_init_identity (&modelview); + _clutter_actor_apply_modelview_transform (actor, &modelview); + cogl_framebuffer_set_modelview_matrix (fb, &modelview); + cogl_framebuffer_draw_primitive (COGL_FRAMEBUFFER (stage_cogl->onscreen), + outline, + prim); + cogl_framebuffer_pop_matrix (fb); + cogl_object_unref (prim); + } +} + /* XXX: This is basically identical to clutter_stage_glx_redraw */ static void clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) @@ -420,14 +543,11 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) gboolean use_clipped_redraw; gboolean can_blit_sub_buffer; gboolean has_buffer_age; - ClutterActor *wrapper; cairo_rectangle_int_t *clip_region; int damage[4], ndamage; gboolean force_swap; int window_scale; - wrapper = CLUTTER_ACTOR (stage_cogl->wrapper); - if (!stage_cogl->onscreen) return; @@ -510,85 +630,35 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) } } - if (use_clipped_redraw) + if (!stage_cogl->views) { - CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen); - - CLUTTER_NOTE (CLIPPING, - "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n", - clip_region->x, - clip_region->y, - clip_region->width, - clip_region->height); - - stage_cogl->using_clipped_redraw = TRUE; - - cogl_framebuffer_push_scissor_clip (fb, - clip_region->x * window_scale, - clip_region->y * window_scale, - clip_region->width * window_scale, - clip_region->height * window_scale); - _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region); - cogl_framebuffer_pop_clip (fb); - - stage_cogl->using_clipped_redraw = FALSE; + clutter_stage_cogl_paint_stage (stage_window, + clip_region, + use_clipped_redraw, + may_use_clipped_redraw); } else { - CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n"); - - /* If we are trying to debug redraw issues then we want to pass - * the bounding_redraw_clip so it can be visualized */ - if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) && - may_use_clipped_redraw) - { - _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region); - } - else - _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL); - } - - if (may_use_clipped_redraw && - G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))) - { CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen); - CoglContext *ctx = cogl_framebuffer_get_context (fb); - static CoglPipeline *outline = NULL; - cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip; - ClutterActor *actor = CLUTTER_ACTOR (wrapper); - float x_1 = clip->x * window_scale; - float x_2 = clip->x + clip->width * window_scale; - float y_1 = clip->y * window_scale; - float y_2 = clip->y + clip->height * window_scale; - CoglVertexP2 quad[4] = { - { x_1, y_1 }, - { x_2, y_1 }, - { x_2, y_2 }, - { x_1, y_2 } - }; - CoglPrimitive *prim; - CoglMatrix modelview; + GList *l; - if (outline == NULL) + for (l = stage_cogl->views; l; l = l->next) { - outline = cogl_pipeline_new (ctx); - cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff); + ClutterStageCoglView *view = l->data; + + stage_cogl->current_view = view; + cogl_framebuffer_push_scissor_clip (fb, + view->rect.x, + view->rect.y, + view->rect.width, + view->rect.height); + + clutter_stage_cogl_paint_stage (stage_window, + clip_region, + FALSE, FALSE); + cogl_framebuffer_pop_clip (fb); + stage_cogl->current_view = NULL; } - - prim = cogl_primitive_new_p2 (ctx, - COGL_VERTICES_MODE_LINE_LOOP, - 4, /* n_vertices */ - quad); - - cogl_framebuffer_push_matrix (fb); - cogl_matrix_init_identity (&modelview); - _clutter_actor_apply_modelview_transform (actor, &modelview); - cogl_framebuffer_set_modelview_matrix (fb, &modelview); - cogl_framebuffer_draw_primitive (COGL_FRAMEBUFFER (stage_cogl->onscreen), - outline, - prim); - cogl_framebuffer_pop_matrix (fb); - cogl_object_unref (prim); } /* XXX: It seems there will be a race here in that the stage @@ -689,6 +759,79 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, } static void +clutter_stage_cogl_transform_to_view (ClutterStageWindow *stage_window, + CoglMatrix *matrix) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + ClutterStageCoglView *current = stage_cogl->current_view; + + if (!current || cogl_get_draw_framebuffer() != stage_cogl->onscreen) + return; + + switch (current->transform) + { + case CLUTTER_STAGE_COGL_TRANSFORM_90: + cogl_matrix_translate (matrix, current->rect.x, + current->rect.y + current->rect.height, 0); + cogl_matrix_rotate (matrix, -90, 0, 0, 1); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_180: + cogl_matrix_translate (matrix, + current->rect.x + current->rect.width, + current->rect.y + current->rect.height, 0); + cogl_matrix_rotate (matrix, 180, 0, 0, 1); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_270: + cogl_matrix_translate (matrix, + current->rect.x + current->rect.width, 0, 0); + cogl_matrix_rotate (matrix, 90, 0, 0, 1); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_0: + cogl_matrix_translate (matrix, + current->rect.x + current->rect.width, + current->rect.y, 0); + cogl_matrix_scale (matrix, -1, 1, 0); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_90: + cogl_matrix_translate (matrix, + current->rect.x, current->rect.y, 0); + cogl_matrix_rotate (matrix, -90, 0, 0, 1); + cogl_matrix_scale (matrix, -1, 1, 0); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_180: + cogl_matrix_translate (matrix, + current->rect.x + current->rect.width, + current->rect.y + current->rect.height, 0); + cogl_matrix_scale (matrix, -1, 1, 0); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_270: + cogl_matrix_translate (matrix, + current->rect.x + current->rect.width, + current->rect.y + current->rect.height, 0); + cogl_matrix_rotate (matrix, 90, 0, 0, 1); + cogl_matrix_scale (matrix, -1, 1, 0); + cogl_matrix_translate (matrix, -current->rect.x, + -current->rect.y, 0); + break; + case CLUTTER_STAGE_COGL_TRANSFORM_0: + default: + return; + } +} + +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { iface->realize = clutter_stage_cogl_realize; @@ -709,6 +852,8 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface) iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer; iface->dirty_back_buffer = clutter_stage_cogl_dirty_back_buffer; iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; + iface->transform_to_view = clutter_stage_cogl_transform_to_view; + iface->get_hw_geometry = clutter_stage_cogl_get_hw_geometry; } static void @@ -754,3 +899,153 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage) stage->update_time = -1; } + +static inline gboolean +view_is_rotated (ClutterStageCoglView *view) +{ + switch (view->transform) + { + case CLUTTER_STAGE_COGL_TRANSFORM_0: + case CLUTTER_STAGE_COGL_TRANSFORM_180: + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_0: + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_180: + return FALSE; + case CLUTTER_STAGE_COGL_TRANSFORM_90: + case CLUTTER_STAGE_COGL_TRANSFORM_270: + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_90: + case CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_270: + return TRUE; + } + + return FALSE; +} + +static void +stage_view_changed_cb (ClutterStageCoglView *view, + ClutterStageCogl *stage) +{ + gint fb_width = 0, fb_height = 0; + gint width = 0, height = 0; + GList *l; + + for (l = stage->views; l; l = l->next) + { + ClutterStageCoglView *cur = l->data; + + if (view_is_rotated (cur)) + { + width = MAX (width, cur->rect.x + cur->rect.height); + height = MAX (height, cur->rect.y + cur->rect.width); + } + else + { + width = MAX (width, cur->rect.x + cur->rect.width); + height = MAX (height, cur->rect.y + cur->rect.height); + } + + fb_width = MAX (fb_width, cur->rect.x + cur->rect.width); + fb_height = MAX (fb_height, cur->rect.y + cur->rect.height); + } + + stage->geometry_width = width; + stage->geometry_height = height; + stage->fb_width = fb_width; + stage->fb_height = fb_height; + clutter_actor_queue_relayout (CLUTTER_ACTOR (stage->wrapper)); +} + +void +clutter_stage_cogl_add_view (ClutterStageCogl *stage, + ClutterStageCoglView *view) +{ + g_return_if_fail (CLUTTER_IS_STAGE_COGL (stage)); + g_return_if_fail (CLUTTER_IS_STAGE_COGL_VIEW (view)); + + if (g_list_find (stage->views, view)) + return; + + g_signal_connect (view, "changed", + G_CALLBACK (stage_view_changed_cb), + stage); + stage->views = g_list_prepend (stage->views, + g_object_ref (view)); +} + +void +clutter_stage_cogl_remove_view (ClutterStageCogl *stage, + ClutterStageCoglView *view) +{ + GList *l; + + g_return_if_fail (CLUTTER_IS_STAGE_COGL (stage)); + g_return_if_fail (CLUTTER_IS_STAGE_COGL_VIEW (view)); + + l = g_list_find (stage->views, view); + if (!l) + return; + + g_signal_handlers_disconnect_by_func (view, + stage_view_changed_cb, + stage); + stage->views = g_list_delete_link (stage->views, l); + g_object_unref (view); +} + +enum { + CHANGED, + N_VIEW_SIGNALS +}; + +static guint view_signals[N_VIEW_SIGNALS] = { 0 }; + +G_DEFINE_ABSTRACT_TYPE (ClutterStageCoglView, clutter_stage_cogl_view, + G_TYPE_OBJECT) + +static void +clutter_stage_cogl_view_init (ClutterStageCoglView *view) +{ +} + +static void +clutter_stage_cogl_view_class_init (ClutterStageCoglViewClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + view_signals[CHANGED] = + g_signal_new (I_("changed"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + _clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +void +clutter_stage_cogl_view_set_rect (ClutterStageCoglView *view, + cairo_rectangle_int_t *rect) +{ + g_return_if_fail (CLUTTER_IS_STAGE_COGL_VIEW (view)); + g_return_if_fail (rect != NULL); + + if (view->rect.x == rect->x && + view->rect.y == rect->y && + view->rect.width == rect->width && + view->rect.height == rect->height) + return; + + view->rect = *rect; + g_signal_emit (view, view_signals[CHANGED], 0); +} + +void +clutter_stage_cogl_view_set_transform (ClutterStageCoglView *view, + ClutterStageCoglViewTransform transform) +{ + g_return_if_fail (CLUTTER_IS_STAGE_COGL_VIEW (view)); + + if (view->transform == transform) + return; + + view->transform = transform; + g_signal_emit (view, view_signals[CHANGED], 0); +} diff --git a/clutter/cogl/clutter-stage-cogl.h b/clutter/cogl/clutter-stage-cogl.h index 8455405e3..500541959 100644 --- a/clutter/cogl/clutter-stage-cogl.h +++ b/clutter/cogl/clutter-stage-cogl.h @@ -11,6 +11,8 @@ #include <X11/Xutil.h> #endif +#include "clutter-marshal.h" + G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE_COGL (_clutter_stage_cogl_get_type ()) @@ -20,9 +22,19 @@ G_BEGIN_DECLS #define CLUTTER_IS_STAGE_COGL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_COGL)) #define CLUTTER_STAGE_COGL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_COGL, ClutterStageCoglClass)) +#define CLUTTER_TYPE_STAGE_COGL_VIEW (clutter_stage_cogl_view_get_type ()) +#define CLUTTER_STAGE_COGL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_COGL_VIEW, ClutterStageCoglView)) +#define CLUTTER_IS_STAGE_COGL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_COGL_VIEW)) +#define CLUTTER_STAGE_COGL_CLASS_VIEW(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_COGL_VIEW, ClutterStageCoglViewClass)) +#define CLUTTER_IS_STAGE_COGL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_COGL_VIEW)) +#define CLUTTER_STAGE_COGL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_COGL_VIEW, ClutterStageCoglViewClass)) + typedef struct _ClutterStageCogl ClutterStageCogl; typedef struct _ClutterStageCoglClass ClutterStageCoglClass; +typedef struct _ClutterStageCoglView ClutterStageCoglView; +typedef struct _ClutterStageCoglViewClass ClutterStageCoglViewClass; + struct _ClutterStageCogl { GObject parent_instance; @@ -50,6 +62,13 @@ struct _ClutterStageCogl cairo_rectangle_int_t bounding_redraw_clip; + ClutterStageCoglView *current_view; + GList *views; + gint geometry_width; + gint geometry_height; + gint fb_width; + gint fb_height; + /* Stores a list of previous damaged areas */ #define DAMAGE_HISTORY_MAX 16 #define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1)) @@ -65,13 +84,49 @@ struct _ClutterStageCogl guint dirty_backbuffer : 1; }; +typedef enum { + CLUTTER_STAGE_COGL_TRANSFORM_0, + CLUTTER_STAGE_COGL_TRANSFORM_90, + CLUTTER_STAGE_COGL_TRANSFORM_180, + CLUTTER_STAGE_COGL_TRANSFORM_270, + CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_0, + CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_90, + CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_180, + CLUTTER_STAGE_COGL_TRANSFORM_FLIPPED_270, +} ClutterStageCoglViewTransform; + struct _ClutterStageCoglClass { GObjectClass parent_class; }; +struct _ClutterStageCoglView +{ + GObject parent_intance; + cairo_rectangle_int_t rect; + ClutterStageCoglViewTransform transform; +}; + +struct _ClutterStageCoglViewClass +{ + GObjectClass parent_class; +}; + GType _clutter_stage_cogl_get_type (void) G_GNUC_CONST; +GType clutter_stage_cogl_view_get_type (void) G_GNUC_CONST; + + +void clutter_stage_cogl_add_view (ClutterStageCogl *stage, + ClutterStageCoglView *view); +void clutter_stage_cogl_remove_view (ClutterStageCogl *stage, + ClutterStageCoglView *output); + +void clutter_stage_cogl_view_set_rect (ClutterStageCoglView *view, + cairo_rectangle_int_t *rect); +void clutter_stage_cogl_view_set_transform (ClutterStageCoglView *view, + ClutterStageCoglViewTransform transform); + G_END_DECLS #endif /* __CLUTTER_STAGE_COGL_H__ */ |