~gnome3-team/mutter/trunk

« back to all changes in this revision

Viewing changes to clutter/clutter/cogl/clutter-stage-cogl.c

  • Committer: Rui Matos
  • Date: 2016-04-27 16:34:03 UTC
  • mfrom: (0.1.7560)
  • Revision ID: git-v1:a7b5d790ac66477ad9e3d940527c198332a03695
Merge clutter's master branch into mutter

https://bugzilla.gnome.org/show_bug.cgi?id=760439

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
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
 
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
 
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
 
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
 
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
 
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 *
 
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
 
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
 
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
 
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
 
296
clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
 
297
                           gint                width,
 
298
                           gint                height)
 
299
{
 
300
}
 
301
 
 
302
static gboolean
 
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
 
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
 
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
 
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
 
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
 
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 *
 
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
 
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
 
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
 
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
 
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
 
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
 
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
}