1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2007,2008,2009,2010,2011  Intel Corporation.
7  *
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.
12  *
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.
17  *
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/>.
20 
21  * Authors:
22  *  Matthew Allum
23  *  Robert Bragg
24  *  Neil Roberts
25  *  Emmanuele Bassi
26  */
27 
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #define CLUTTER_ENABLE_EXPERIMENTAL_API
34 
35 #include "clutter-config.h"
36 
37 #include "clutter-stage-cogl.h"
38 
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"
48 
49 static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
50 
51 G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl,
52                          _clutter_stage_cogl,
53                          G_TYPE_OBJECT,
54                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
55                                                 clutter_stage_window_iface_init));
56 
57 enum {
58   PROP_0,
59   PROP_WRAPPER,
60   PROP_BACKEND,
61   PROP_LAST
62 };
63 
64 static void
clutter_stage_cogl_unrealize(ClutterStageWindow * stage_window)65 clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
66 {
67   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
68 
69   CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_cogl);
70 
71   if (stage_cogl->onscreen != NULL)
72     {
73       cogl_onscreen_remove_frame_callback (stage_cogl->onscreen,
74                                            stage_cogl->frame_closure);
75       stage_cogl->frame_closure = NULL;
76 
77       cogl_object_unref (stage_cogl->onscreen);
78       stage_cogl->onscreen = NULL;
79     }
80 
81   stage_cogl->pending_swaps = 0;
82 }
83 
84 static void
frame_cb(CoglOnscreen * onscreen,CoglFrameEvent event,CoglFrameInfo * info,void * user_data)85 frame_cb (CoglOnscreen  *onscreen,
86           CoglFrameEvent event,
87           CoglFrameInfo *info,
88           void          *user_data)
89 {
90   ClutterStageCogl *stage_cogl = user_data;
91 
92   if (event == COGL_FRAME_EVENT_SYNC)
93     {
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.
97        *
98        * https://bugs.freedesktop.org/show_bug.cgi?id=27962
99        *
100        * FIXME: This issue can be hidden inside Cogl so we shouldn't
101        * need to care about this bug here.
102        */
103       if (stage_cogl->pending_swaps > 0)
104         stage_cogl->pending_swaps--;
105     }
106   else if (event == COGL_FRAME_EVENT_COMPLETE)
107     {
108       gint64 presentation_time_cogl = cogl_frame_info_get_presentation_time (info);
109 
110       if (presentation_time_cogl != 0)
111         {
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 ();
115 
116           stage_cogl->last_presentation_time =
117             now + (presentation_time_cogl - current_time_cogl) / 1000;
118         }
119 
120       stage_cogl->refresh_rate = cogl_frame_info_get_refresh_rate (info);
121     }
122 }
123 
124 static gboolean
clutter_stage_cogl_realize(ClutterStageWindow * stage_window)125 clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
126 {
127   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
128   ClutterBackend *backend;
129   CoglFramebuffer *framebuffer;
130   GError *error = NULL;
131   gfloat width = 800;
132   gfloat height = 600;
133 
134   CLUTTER_NOTE (BACKEND, "Realizing stage '%s' [%p]",
135                 G_OBJECT_TYPE_NAME (stage_cogl),
136                 stage_cogl);
137 
138   backend = clutter_get_default_backend ();
139 
140   if (backend->cogl_context == NULL)
141     {
142       g_warning ("Failed to realize stage: missing Cogl context");
143       return FALSE;
144     }
145 
146   if (stage_cogl->onscreen == NULL)
147     {
148       stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
149 						width, height);
150     }
151 
152   cogl_onscreen_set_swap_throttled (stage_cogl->onscreen,
153                                     _clutter_get_sync_to_vblank ());
154 
155   framebuffer = COGL_FRAMEBUFFER (stage_cogl->onscreen);
156   if (!cogl_framebuffer_allocate (framebuffer, &error))
157     {
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;
162       return FALSE;
163     }
164 
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. */
168 
169   stage_cogl->frame_closure =
170     cogl_onscreen_add_frame_callback (stage_cogl->onscreen,
171                                       frame_cb,
172                                       stage_cogl,
173                                       NULL);
174   return TRUE;
175 }
176 
177 static void
clutter_stage_cogl_schedule_update(ClutterStageWindow * stage_window,gint sync_delay)178 clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
179                                     gint                sync_delay)
180 {
181   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
182   gint64 now;
183   float refresh_rate;
184   gint64 refresh_interval;
185 
186   if (stage_cogl->update_time != -1)
187     return;
188 
189   now = g_get_monotonic_time ();
190 
191   if (sync_delay < 0)
192     {
193       stage_cogl->update_time = now;
194       return;
195     }
196 
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.
201    */
202   if (stage_cogl->last_presentation_time == 0||
203       stage_cogl->last_presentation_time < now - 150000)
204     {
205       stage_cogl->update_time = now;
206       return;
207     }
208 
209   refresh_rate = stage_cogl->refresh_rate;
210   if (refresh_rate == 0.0)
211     refresh_rate = 60.0;
212 
213   refresh_interval = (gint64) (0.5 + 1000000 / refresh_rate);
214   if (refresh_interval == 0)
215     refresh_interval = 16667; /* 1/60th second */
216 
217   stage_cogl->update_time = stage_cogl->last_presentation_time + 1000 * sync_delay;
218 
219   while (stage_cogl->update_time < now)
220     stage_cogl->update_time += refresh_interval;
221 }
222 
223 static gint64
clutter_stage_cogl_get_update_time(ClutterStageWindow * stage_window)224 clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window)
225 {
226   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
227 
228   if (stage_cogl->pending_swaps)
229     return -1; /* in the future, indefinite */
230 
231   return stage_cogl->update_time;
232 }
233 
234 static void
clutter_stage_cogl_clear_update_time(ClutterStageWindow * stage_window)235 clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window)
236 {
237   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
238 
239   stage_cogl->update_time = -1;
240 }
241 
242 static ClutterActor *
clutter_stage_cogl_get_wrapper(ClutterStageWindow * stage_window)243 clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window)
244 {
245   return CLUTTER_ACTOR (CLUTTER_STAGE_COGL (stage_window)->wrapper);
246 }
247 
248 static void
clutter_stage_cogl_show(ClutterStageWindow * stage_window,gboolean do_raise)249 clutter_stage_cogl_show (ClutterStageWindow *stage_window,
250 			 gboolean            do_raise)
251 {
252   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
253 
254   clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
255 }
256 
257 static void
clutter_stage_cogl_hide(ClutterStageWindow * stage_window)258 clutter_stage_cogl_hide (ClutterStageWindow *stage_window)
259 {
260   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
261 
262   clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
263 }
264 
265 static void
clutter_stage_cogl_get_geometry(ClutterStageWindow * stage_window,cairo_rectangle_int_t * geometry)266 clutter_stage_cogl_get_geometry (ClutterStageWindow    *stage_window,
267                                  cairo_rectangle_int_t *geometry)
268 {
269   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
270   int window_scale;
271 
272   window_scale = _clutter_stage_window_get_scale_factor (stage_window);
273 
274   if (geometry != NULL)
275     {
276       if (stage_cogl->onscreen)
277         {
278           CoglFramebuffer *framebuffer =
279             COGL_FRAMEBUFFER (stage_cogl->onscreen);
280 
281           geometry->x = geometry->y = 0;
282 
283           geometry->width = cogl_framebuffer_get_width (framebuffer) / window_scale;
284           geometry->height = cogl_framebuffer_get_height (framebuffer) / window_scale;
285         }
286       else
287         {
288           geometry->x = geometry->y = 0;
289           geometry->width = 800;
290           geometry->height = 600;
291         }
292     }
293 }
294 
295 static void
clutter_stage_cogl_resize(ClutterStageWindow * stage_window,gint width,gint height)296 clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
297                            gint                width,
298                            gint                height)
299 {
300 }
301 
302 static gboolean
clutter_stage_cogl_has_redraw_clips(ClutterStageWindow * stage_window)303 clutter_stage_cogl_has_redraw_clips (ClutterStageWindow *stage_window)
304 {
305   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
306 
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.
310    *
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.
313    */
314   if (!stage_cogl->initialized_redraw_clip ||
315       (stage_cogl->initialized_redraw_clip &&
316        stage_cogl->bounding_redraw_clip.width != 0))
317     return TRUE;
318   else
319     return FALSE;
320 }
321 
322 static gboolean
clutter_stage_cogl_ignoring_redraw_clips(ClutterStageWindow * stage_window)323 clutter_stage_cogl_ignoring_redraw_clips (ClutterStageWindow *stage_window)
324 {
325   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
326 
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)
330     return TRUE;
331   else
332     return FALSE;
333 }
334 
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.
340  *
341  * A NULL stage_clip means the whole stage needs to be redrawn.
342  *
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
347  *   buffer.
348  */
349 static void
clutter_stage_cogl_add_redraw_clip(ClutterStageWindow * stage_window,cairo_rectangle_int_t * stage_clip)350 clutter_stage_cogl_add_redraw_clip (ClutterStageWindow    *stage_window,
351                                     cairo_rectangle_int_t *stage_clip)
352 {
353   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
354 
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))
357     return;
358 
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)
363     {
364       stage_cogl->bounding_redraw_clip.width = 0;
365       stage_cogl->initialized_redraw_clip = TRUE;
366       return;
367     }
368 
369   /* Ignore requests to add degenerate/empty clip rectangles */
370   if (stage_clip->width == 0 || stage_clip->height == 0)
371     return;
372 
373   if (!stage_cogl->initialized_redraw_clip)
374     {
375       stage_cogl->bounding_redraw_clip = *stage_clip;
376     }
377   else if (stage_cogl->bounding_redraw_clip.width > 0)
378     {
379       _clutter_util_rectangle_union (&stage_cogl->bounding_redraw_clip,
380                                      stage_clip,
381                                      &stage_cogl->bounding_redraw_clip);
382     }
383 
384   stage_cogl->initialized_redraw_clip = TRUE;
385 }
386 
387 static gboolean
clutter_stage_cogl_get_redraw_clip_bounds(ClutterStageWindow * stage_window,cairo_rectangle_int_t * stage_clip)388 clutter_stage_cogl_get_redraw_clip_bounds (ClutterStageWindow    *stage_window,
389                                            cairo_rectangle_int_t *stage_clip)
390 {
391   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
392 
393   if (stage_cogl->using_clipped_redraw)
394     {
395       *stage_clip = stage_cogl->bounding_redraw_clip;
396 
397       return TRUE;
398     }
399 
400   return FALSE;
401 }
402 
403 static inline gboolean
valid_buffer_age(ClutterStageCogl * stage_cogl,int age)404 valid_buffer_age (ClutterStageCogl *stage_cogl, int age)
405 {
406   if (age <= 0 || stage_cogl->dirty_backbuffer)
407     return FALSE;
408 
409   return age < MIN (stage_cogl->damage_index, DAMAGE_HISTORY_MAX);
410 }
411 
412 /* XXX: This is basically identical to clutter_stage_glx_redraw */
413 static void
clutter_stage_cogl_redraw(ClutterStageWindow * stage_window)414 clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
415 {
416   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
417   cairo_rectangle_int_t geom;
418   gboolean have_clip;
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;
426   gboolean force_swap;
427   int window_scale;
428 
429   wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
430 
431   if (!stage_cogl->onscreen)
432     return;
433 
434   can_blit_sub_buffer =
435     cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
436 
437   has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
438 
439   _clutter_stage_window_get_geometry (stage_window, &geom);
440 
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));
447 
448   may_use_clipped_redraw = FALSE;
449   if (_clutter_stage_window_can_clip_redraws (stage_window) &&
450       (can_blit_sub_buffer || has_buffer_age) &&
451       have_clip &&
452       /* some drivers struggle to get going and produce some junk
453        * frames when starting up... */
454       stage_cogl->frame_count > 3)
455     {
456       may_use_clipped_redraw = TRUE;
457       clip_region = &stage_cogl->bounding_redraw_clip;
458     }
459   else
460     clip_region = NULL;
461 
462   if (may_use_clipped_redraw &&
463       G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
464     use_clipped_redraw = TRUE;
465   else
466     use_clipped_redraw = FALSE;
467 
468   force_swap = FALSE;
469 
470   window_scale = _clutter_stage_window_get_scale_factor (stage_window);
471 
472   if (has_buffer_age)
473     {
474       cairo_rectangle_int_t *current_damage =
475 	&stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index++)];
476 
477       if (use_clipped_redraw)
478 	{
479 	  int age = cogl_onscreen_get_buffer_age (stage_cogl->onscreen), i;
480 
481 	  *current_damage = *clip_region;
482 
483 	  if (valid_buffer_age (stage_cogl, age))
484 	    {
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)],
488 					       clip_region);
489 
490 	      CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
491 			    age,
492 			    clip_region->x,
493 			    clip_region->y,
494 			    clip_region->width,
495 			    clip_region->height);
496 	      force_swap = TRUE;
497 	    }
498 	  else
499 	    {
500 	      CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
501 	      use_clipped_redraw = FALSE;
502 	    }
503 	}
504       else
505 	{
506 	  current_damage->x = 0;
507 	  current_damage->y = 0;
508 	  current_damage->width  = geom.width;
509 	  current_damage->height = geom.height;
510 	}
511     }
512 
513   if (use_clipped_redraw)
514     {
515       CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen);
516 
517       CLUTTER_NOTE (CLIPPING,
518                     "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
519                     clip_region->x,
520                     clip_region->y,
521                     clip_region->width,
522                     clip_region->height);
523 
524       stage_cogl->using_clipped_redraw = TRUE;
525 
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);
533 
534       stage_cogl->using_clipped_redraw = FALSE;
535     }
536   else
537     {
538       CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n");
539 
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)
544         {
545           _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region);
546         }
547       else
548         _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
549     }
550 
551   if (may_use_clipped_redraw &&
552       G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
553     {
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] = {
564         { x_1, y_1 },
565         { x_2, y_1 },
566         { x_2, y_2 },
567         { x_1, y_2 }
568       };
569       CoglPrimitive *prim;
570       CoglMatrix modelview;
571 
572       if (outline == NULL)
573         {
574           outline = cogl_pipeline_new (ctx);
575           cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
576         }
577 
578       prim = cogl_primitive_new_p2 (ctx,
579                                     COGL_VERTICES_MODE_LINE_LOOP,
580                                     4, /* n_vertices */
581                                     quad);
582 
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),
588                                        outline,
589                                        prim);
590       cogl_framebuffer_pop_matrix (fb);
591       cogl_object_unref (prim);
592     }
593 
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
600    * artefacts.
601    */
602   if (use_clipped_redraw || force_swap)
603     {
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;
608       ndamage = 1;
609     }
610   else
611     {
612       ndamage = 0;
613     }
614 
615   /* push on the screen */
616   if (use_clipped_redraw && !force_swap)
617     {
618       CLUTTER_NOTE (BACKEND,
619                     "cogl_onscreen_swap_region (onscreen: %p, "
620                                                 "x: %d, y: %d, "
621                                                 "width: %d, height: %d)",
622                     stage_cogl->onscreen,
623                     damage[0], damage[1], damage[2], damage[3]);
624 
625       cogl_onscreen_swap_region (stage_cogl->onscreen,
626 				 damage, ndamage);
627     }
628   else
629     {
630       CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)",
631                     stage_cogl->onscreen);
632 
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++;
638 
639       cogl_onscreen_swap_buffers_with_damage (stage_cogl->onscreen,
640 					      damage, ndamage);
641     }
642 
643   /* reset the redraw clipping for the next paint... */
644   stage_cogl->initialized_redraw_clip = FALSE;
645 
646   /* We have repaired the backbuffer */
647   stage_cogl->dirty_backbuffer = FALSE;
648 
649   stage_cogl->frame_count++;
650 }
651 
652 static CoglFramebuffer *
clutter_stage_cogl_get_active_framebuffer(ClutterStageWindow * stage_window)653 clutter_stage_cogl_get_active_framebuffer (ClutterStageWindow *stage_window)
654 {
655   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
656 
657   return COGL_FRAMEBUFFER (stage_cogl->onscreen);
658 }
659 
660 static void
clutter_stage_cogl_dirty_back_buffer(ClutterStageWindow * stage_window)661 clutter_stage_cogl_dirty_back_buffer (ClutterStageWindow *stage_window)
662 {
663  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
664 
665  stage_cogl->dirty_backbuffer = TRUE;
666 }
667 
668 static void
clutter_stage_cogl_get_dirty_pixel(ClutterStageWindow * stage_window,int * x,int * y)669 clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
670                                     int                *x,
671                                     int                *y)
672 {
673   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
674   gboolean has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
675 
676   if (!has_buffer_age)
677     {
678       *x = 0;
679       *y = 0;
680     }
681   else
682     {
683       cairo_rectangle_int_t *rect;
684 
685       rect = &stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index-1)];
686       *x = rect->x;
687       *y = rect->y;
688     }
689 }
690 
691 static void
clutter_stage_window_iface_init(ClutterStageWindowIface * iface)692 clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
693 {
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;
712 }
713 
714 static void
clutter_stage_cogl_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)715 clutter_stage_cogl_set_property (GObject      *gobject,
716 				 guint         prop_id,
717 				 const GValue *value,
718 				 GParamSpec   *pspec)
719 {
720   ClutterStageCogl *self = CLUTTER_STAGE_COGL (gobject);
721 
722   switch (prop_id)
723     {
724     case PROP_WRAPPER:
725       self->wrapper = g_value_get_object (value);
726       break;
727 
728     case PROP_BACKEND:
729       self->backend = g_value_get_object (value);
730       break;
731 
732     default:
733       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
734       break;
735     }
736 }
737 
738 static void
_clutter_stage_cogl_class_init(ClutterStageCoglClass * klass)739 _clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
740 {
741   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
742 
743   gobject_class->set_property = clutter_stage_cogl_set_property;
744 
745   g_object_class_override_property (gobject_class, PROP_WRAPPER, "wrapper");
746   g_object_class_override_property (gobject_class, PROP_BACKEND, "backend");
747 }
748 
749 static void
_clutter_stage_cogl_init(ClutterStageCogl * stage)750 _clutter_stage_cogl_init (ClutterStageCogl *stage)
751 {
752   stage->last_presentation_time = 0;
753   stage->refresh_rate = 0.0;
754 
755   stage->update_time = -1;
756 }
757