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