4
* An OpenGL based 'interactive canvas' library.
6
* Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation.
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
33
#define CLUTTER_ENABLE_EXPERIMENTAL_API
35
#include "clutter-config.h"
37
#include "clutter-stage-cogl.h"
39
#include "clutter-actor-private.h"
40
#include "clutter-backend-private.h"
41
#include "clutter-debug.h"
42
#include "clutter-event.h"
43
#include "clutter-enum-types.h"
44
#include "clutter-feature.h"
45
#include "clutter-main.h"
46
#include "clutter-private.h"
47
#include "clutter-stage-private.h"
49
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
51
G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl,
54
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
55
clutter_stage_window_iface_init));
65
clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
67
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
69
CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_cogl);
71
if (stage_cogl->onscreen != NULL)
73
cogl_onscreen_remove_frame_callback (stage_cogl->onscreen,
74
stage_cogl->frame_closure);
75
stage_cogl->frame_closure = NULL;
77
cogl_object_unref (stage_cogl->onscreen);
78
stage_cogl->onscreen = NULL;
81
stage_cogl->pending_swaps = 0;
85
frame_cb (CoglOnscreen *onscreen,
90
ClutterStageCogl *stage_cogl = user_data;
92
if (event == COGL_FRAME_EVENT_SYNC)
94
/* Early versions of the swap_event implementation in Mesa
95
* deliver BufferSwapComplete event when not selected for,
96
* so if we get a swap event we aren't expecting, just ignore it.
98
* https://bugs.freedesktop.org/show_bug.cgi?id=27962
100
* FIXME: This issue can be hidden inside Cogl so we shouldn't
101
* need to care about this bug here.
103
if (stage_cogl->pending_swaps > 0)
104
stage_cogl->pending_swaps--;
106
else if (event == COGL_FRAME_EVENT_COMPLETE)
108
gint64 presentation_time_cogl = cogl_frame_info_get_presentation_time (info);
110
if (presentation_time_cogl != 0)
112
CoglContext *context = cogl_framebuffer_get_context (COGL_FRAMEBUFFER (onscreen));
113
gint64 current_time_cogl = cogl_get_clock_time (context);
114
gint64 now = g_get_monotonic_time ();
116
stage_cogl->last_presentation_time =
117
now + (presentation_time_cogl - current_time_cogl) / 1000;
120
stage_cogl->refresh_rate = cogl_frame_info_get_refresh_rate (info);
125
clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
127
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
128
ClutterBackend *backend;
129
CoglFramebuffer *framebuffer;
130
GError *error = NULL;
134
CLUTTER_NOTE (BACKEND, "Realizing stage '%s' [%p]",
135
G_OBJECT_TYPE_NAME (stage_cogl),
138
backend = clutter_get_default_backend ();
140
if (backend->cogl_context == NULL)
142
g_warning ("Failed to realize stage: missing Cogl context");
146
if (stage_cogl->onscreen == NULL)
148
stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
152
cogl_onscreen_set_swap_throttled (stage_cogl->onscreen,
153
_clutter_get_sync_to_vblank ());
155
framebuffer = COGL_FRAMEBUFFER (stage_cogl->onscreen);
156
if (!cogl_framebuffer_allocate (framebuffer, &error))
158
g_warning ("Failed to allocate stage: %s", error->message);
159
g_error_free (error);
160
cogl_object_unref (stage_cogl->onscreen);
161
stage_cogl->onscreen = NULL;
165
/* FIXME: for fullscreen Cogl platforms then the size we gave
166
* will be ignored, so we need to make sure the stage size is
167
* updated to this size. */
169
stage_cogl->frame_closure =
170
cogl_onscreen_add_frame_callback (stage_cogl->onscreen,
178
clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
181
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
184
gint64 refresh_interval;
186
if (stage_cogl->update_time != -1)
189
now = g_get_monotonic_time ();
193
stage_cogl->update_time = now;
197
/* We only extrapolate presentation times for 150ms - this is somewhat
198
* arbitrary. The reasons it might not be accurate for larger times are
199
* that the refresh interval might be wrong or the vertical refresh
200
* might be downclocked if nothing is going on onscreen.
202
if (stage_cogl->last_presentation_time == 0||
203
stage_cogl->last_presentation_time < now - 150000)
205
stage_cogl->update_time = now;
209
refresh_rate = stage_cogl->refresh_rate;
210
if (refresh_rate == 0.0)
213
refresh_interval = (gint64) (0.5 + 1000000 / refresh_rate);
214
if (refresh_interval == 0)
215
refresh_interval = 16667; /* 1/60th second */
217
stage_cogl->update_time = stage_cogl->last_presentation_time + 1000 * sync_delay;
219
while (stage_cogl->update_time < now)
220
stage_cogl->update_time += refresh_interval;
224
clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window)
226
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
228
if (stage_cogl->pending_swaps)
229
return -1; /* in the future, indefinite */
231
return stage_cogl->update_time;
235
clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window)
237
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
239
stage_cogl->update_time = -1;
242
static ClutterActor *
243
clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window)
245
return CLUTTER_ACTOR (CLUTTER_STAGE_COGL (stage_window)->wrapper);
249
clutter_stage_cogl_show (ClutterStageWindow *stage_window,
252
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
254
clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
258
clutter_stage_cogl_hide (ClutterStageWindow *stage_window)
260
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
262
clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
266
clutter_stage_cogl_get_geometry (ClutterStageWindow *stage_window,
267
cairo_rectangle_int_t *geometry)
269
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
272
window_scale = _clutter_stage_window_get_scale_factor (stage_window);
274
if (geometry != NULL)
276
if (stage_cogl->onscreen)
278
CoglFramebuffer *framebuffer =
279
COGL_FRAMEBUFFER (stage_cogl->onscreen);
281
geometry->x = geometry->y = 0;
283
geometry->width = cogl_framebuffer_get_width (framebuffer) / window_scale;
284
geometry->height = cogl_framebuffer_get_height (framebuffer) / window_scale;
288
geometry->x = geometry->y = 0;
289
geometry->width = 800;
290
geometry->height = 600;
296
clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
303
clutter_stage_cogl_has_redraw_clips (ClutterStageWindow *stage_window)
305
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
307
/* NB: at the start of each new frame there is an implied clip that
308
* clips everything (i.e. nothing would be drawn) so we need to make
309
* sure we return True in the un-initialized case here.
311
* NB: a clip width of 0 means a full stage redraw has been queued
312
* so we effectively don't have any redraw clips in that case.
314
if (!stage_cogl->initialized_redraw_clip ||
315
(stage_cogl->initialized_redraw_clip &&
316
stage_cogl->bounding_redraw_clip.width != 0))
323
clutter_stage_cogl_ignoring_redraw_clips (ClutterStageWindow *stage_window)
325
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
327
/* NB: a clip width of 0 means a full stage redraw is required */
328
if (stage_cogl->initialized_redraw_clip &&
329
stage_cogl->bounding_redraw_clip.width == 0)
335
/* A redraw clip represents (in stage coordinates) the bounding box of
336
* something that needs to be redraw. Typically they are added to the
337
* StageWindow as a result of clutter_actor_queue_clipped_redraw() by
338
* actors such as ClutterGLXTexturePixmap. All redraw clips are
339
* discarded after the next paint.
341
* A NULL stage_clip means the whole stage needs to be redrawn.
343
* What we do with this information:
344
* - we keep track of the bounding box for all redraw clips
345
* - when we come to redraw; we scissor the redraw to that box and use
346
* glBlitFramebuffer to present the redraw to the front
350
clutter_stage_cogl_add_redraw_clip (ClutterStageWindow *stage_window,
351
cairo_rectangle_int_t *stage_clip)
353
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
355
/* If we are already forced to do a full stage redraw then bail early */
356
if (clutter_stage_cogl_ignoring_redraw_clips (stage_window))
359
/* A NULL stage clip means a full stage redraw has been queued and
360
* we keep track of this by setting a zero width
361
* stage_cogl->bounding_redraw_clip */
362
if (stage_clip == NULL)
364
stage_cogl->bounding_redraw_clip.width = 0;
365
stage_cogl->initialized_redraw_clip = TRUE;
369
/* Ignore requests to add degenerate/empty clip rectangles */
370
if (stage_clip->width == 0 || stage_clip->height == 0)
373
if (!stage_cogl->initialized_redraw_clip)
375
stage_cogl->bounding_redraw_clip = *stage_clip;
377
else if (stage_cogl->bounding_redraw_clip.width > 0)
379
_clutter_util_rectangle_union (&stage_cogl->bounding_redraw_clip,
381
&stage_cogl->bounding_redraw_clip);
384
stage_cogl->initialized_redraw_clip = TRUE;
388
clutter_stage_cogl_get_redraw_clip_bounds (ClutterStageWindow *stage_window,
389
cairo_rectangle_int_t *stage_clip)
391
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
393
if (stage_cogl->using_clipped_redraw)
395
*stage_clip = stage_cogl->bounding_redraw_clip;
403
static inline gboolean
404
valid_buffer_age (ClutterStageCogl *stage_cogl, int age)
406
if (age <= 0 || stage_cogl->dirty_backbuffer)
409
return age < MIN (stage_cogl->damage_index, DAMAGE_HISTORY_MAX);
412
/* XXX: This is basically identical to clutter_stage_glx_redraw */
414
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
416
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
417
cairo_rectangle_int_t geom;
419
gboolean may_use_clipped_redraw;
420
gboolean use_clipped_redraw;
421
gboolean can_blit_sub_buffer;
422
gboolean has_buffer_age;
423
ClutterActor *wrapper;
424
cairo_rectangle_int_t *clip_region;
425
int damage[4], ndamage;
429
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
431
if (!stage_cogl->onscreen)
434
can_blit_sub_buffer =
435
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
437
has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
439
_clutter_stage_window_get_geometry (stage_window, &geom);
441
/* NB: a zero width redraw clip == full stage redraw */
442
have_clip = (stage_cogl->bounding_redraw_clip.width != 0 &&
443
!(stage_cogl->bounding_redraw_clip.x == 0 &&
444
stage_cogl->bounding_redraw_clip.y == 0 &&
445
stage_cogl->bounding_redraw_clip.width == geom.width &&
446
stage_cogl->bounding_redraw_clip.height == geom.height));
448
may_use_clipped_redraw = FALSE;
449
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
450
(can_blit_sub_buffer || has_buffer_age) &&
452
/* some drivers struggle to get going and produce some junk
453
* frames when starting up... */
454
stage_cogl->frame_count > 3)
456
may_use_clipped_redraw = TRUE;
457
clip_region = &stage_cogl->bounding_redraw_clip;
462
if (may_use_clipped_redraw &&
463
G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
464
use_clipped_redraw = TRUE;
466
use_clipped_redraw = FALSE;
470
window_scale = _clutter_stage_window_get_scale_factor (stage_window);
474
cairo_rectangle_int_t *current_damage =
475
&stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index++)];
477
if (use_clipped_redraw)
479
int age = cogl_onscreen_get_buffer_age (stage_cogl->onscreen), i;
481
*current_damage = *clip_region;
483
if (valid_buffer_age (stage_cogl, age))
485
for (i = 1; i <= age; i++)
486
_clutter_util_rectangle_union (clip_region,
487
&stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index - i - 1)],
490
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
495
clip_region->height);
500
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
501
use_clipped_redraw = FALSE;
506
current_damage->x = 0;
507
current_damage->y = 0;
508
current_damage->width = geom.width;
509
current_damage->height = geom.height;
513
if (use_clipped_redraw)
515
CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen);
517
CLUTTER_NOTE (CLIPPING,
518
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
522
clip_region->height);
524
stage_cogl->using_clipped_redraw = TRUE;
526
cogl_framebuffer_push_scissor_clip (fb,
527
clip_region->x * window_scale,
528
clip_region->y * window_scale,
529
clip_region->width * window_scale,
530
clip_region->height * window_scale);
531
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region);
532
cogl_framebuffer_pop_clip (fb);
534
stage_cogl->using_clipped_redraw = FALSE;
538
CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n");
540
/* If we are trying to debug redraw issues then we want to pass
541
* the bounding_redraw_clip so it can be visualized */
542
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) &&
543
may_use_clipped_redraw)
545
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region);
548
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
551
if (may_use_clipped_redraw &&
552
G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
554
CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen);
555
CoglContext *ctx = cogl_framebuffer_get_context (fb);
556
static CoglPipeline *outline = NULL;
557
cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip;
558
ClutterActor *actor = CLUTTER_ACTOR (wrapper);
559
float x_1 = clip->x * window_scale;
560
float x_2 = clip->x + clip->width * window_scale;
561
float y_1 = clip->y * window_scale;
562
float y_2 = clip->y + clip->height * window_scale;
563
CoglVertexP2 quad[4] = {
570
CoglMatrix modelview;
574
outline = cogl_pipeline_new (ctx);
575
cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
578
prim = cogl_primitive_new_p2 (ctx,
579
COGL_VERTICES_MODE_LINE_LOOP,
583
cogl_framebuffer_push_matrix (fb);
584
cogl_matrix_init_identity (&modelview);
585
_clutter_actor_apply_modelview_transform (actor, &modelview);
586
cogl_framebuffer_set_modelview_matrix (fb, &modelview);
587
cogl_framebuffer_draw_primitive (COGL_FRAMEBUFFER (stage_cogl->onscreen),
590
cogl_framebuffer_pop_matrix (fb);
591
cogl_object_unref (prim);
594
/* XXX: It seems there will be a race here in that the stage
595
* window may be resized before the cogl_onscreen_swap_region
596
* is handled and so we may copy the wrong region. I can't
597
* really see how we can handle this with the current state of X
598
* but at least in this case a full redraw should be queued by
599
* the resize anyway so it should only exhibit temporary
602
if (use_clipped_redraw || force_swap)
604
damage[0] = clip_region->x * window_scale;
605
damage[1] = clip_region->y * window_scale;
606
damage[2] = clip_region->width * window_scale;
607
damage[3] = clip_region->height * window_scale;
615
/* push on the screen */
616
if (use_clipped_redraw && !force_swap)
618
CLUTTER_NOTE (BACKEND,
619
"cogl_onscreen_swap_region (onscreen: %p, "
621
"width: %d, height: %d)",
622
stage_cogl->onscreen,
623
damage[0], damage[1], damage[2], damage[3]);
625
cogl_onscreen_swap_region (stage_cogl->onscreen,
630
CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)",
631
stage_cogl->onscreen);
633
/* If we have swap buffer events then cogl_onscreen_swap_buffers
634
* will return immediately and we need to track that there is a
635
* swap in progress... */
636
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
637
stage_cogl->pending_swaps++;
639
cogl_onscreen_swap_buffers_with_damage (stage_cogl->onscreen,
643
/* reset the redraw clipping for the next paint... */
644
stage_cogl->initialized_redraw_clip = FALSE;
646
/* We have repaired the backbuffer */
647
stage_cogl->dirty_backbuffer = FALSE;
649
stage_cogl->frame_count++;
652
static CoglFramebuffer *
653
clutter_stage_cogl_get_active_framebuffer (ClutterStageWindow *stage_window)
655
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
657
return COGL_FRAMEBUFFER (stage_cogl->onscreen);
661
clutter_stage_cogl_dirty_back_buffer (ClutterStageWindow *stage_window)
663
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
665
stage_cogl->dirty_backbuffer = TRUE;
669
clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
673
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
674
gboolean has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
683
cairo_rectangle_int_t *rect;
685
rect = &stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index-1)];
692
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
694
iface->realize = clutter_stage_cogl_realize;
695
iface->unrealize = clutter_stage_cogl_unrealize;
696
iface->get_wrapper = clutter_stage_cogl_get_wrapper;
697
iface->get_geometry = clutter_stage_cogl_get_geometry;
698
iface->resize = clutter_stage_cogl_resize;
699
iface->show = clutter_stage_cogl_show;
700
iface->hide = clutter_stage_cogl_hide;
701
iface->schedule_update = clutter_stage_cogl_schedule_update;
702
iface->get_update_time = clutter_stage_cogl_get_update_time;
703
iface->clear_update_time = clutter_stage_cogl_clear_update_time;
704
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
705
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
706
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
707
iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
708
iface->redraw = clutter_stage_cogl_redraw;
709
iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer;
710
iface->dirty_back_buffer = clutter_stage_cogl_dirty_back_buffer;
711
iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
715
clutter_stage_cogl_set_property (GObject *gobject,
720
ClutterStageCogl *self = CLUTTER_STAGE_COGL (gobject);
725
self->wrapper = g_value_get_object (value);
729
self->backend = g_value_get_object (value);
733
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
739
_clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
741
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
743
gobject_class->set_property = clutter_stage_cogl_set_property;
745
g_object_class_override_property (gobject_class, PROP_WRAPPER, "wrapper");
746
g_object_class_override_property (gobject_class, PROP_BACKEND, "backend");
750
_clutter_stage_cogl_init (ClutterStageCogl *stage)
752
stage->last_presentation_time = 0;
753
stage->refresh_rate = 0.0;
755
stage->update_time = -1;