1 /*
2 * Copyright (C) 2016 Red Hat Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "clutter-build-config.h"
19
20 #include "clutter/clutter-stage-view.h"
21 #include "clutter/clutter-stage-view-private.h"
22
23 #include <cairo-gobject.h>
24 #include <math.h>
25
26 #include "clutter/clutter-damage-history.h"
27 #include "clutter/clutter-frame-clock.h"
28 #include "clutter/clutter-frame-private.h"
29 #include "clutter/clutter-private.h"
30 #include "clutter/clutter-mutter.h"
31 #include "clutter/clutter-stage-private.h"
32 #include "cogl/cogl.h"
33
34 enum
35 {
36 PROP_0,
37
38 PROP_NAME,
39 PROP_STAGE,
40 PROP_LAYOUT,
41 PROP_FRAMEBUFFER,
42 PROP_OFFSCREEN,
43 PROP_USE_SHADOWFB,
44 PROP_SCALE,
45 PROP_REFRESH_RATE,
46 PROP_VBLANK_DURATION_US,
47
48 PROP_LAST
49 };
50
51 static GParamSpec *obj_props[PROP_LAST];
52
53 typedef struct _ClutterStageViewPrivate
54 {
55 char *name;
56
57 ClutterStage *stage;
58
59 cairo_rectangle_int_t layout;
60 float scale;
61 CoglFramebuffer *framebuffer;
62
63 CoglOffscreen *offscreen;
64 CoglPipeline *offscreen_pipeline;
65
66 gboolean use_shadowfb;
67 struct {
68 struct {
69 CoglDmaBufHandle *handles[2];
70 int current_idx;
71 ClutterDamageHistory *damage_history;
72 } dma_buf;
73
74 CoglOffscreen *framebuffer;
75 } shadow;
76
77 CoglScanout *next_scanout;
78
79 gboolean has_redraw_clip;
80 cairo_region_t *redraw_clip;
81
82 float refresh_rate;
83 int64_t vblank_duration_us;
84 ClutterFrameClock *frame_clock;
85
86 struct {
87 int frame_count;
88 int64_t last_print_time_us;
89 int64_t cumulative_draw_time_us;
90 int64_t began_draw_time_us;
91 int64_t worst_draw_time_us;
92 } frame_timings;
93
94 guint dirty_viewport : 1;
95 guint dirty_projection : 1;
96 } ClutterStageViewPrivate;
97
G_DEFINE_TYPE_WITH_PRIVATE(ClutterStageView,clutter_stage_view,G_TYPE_OBJECT)98 G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageView, clutter_stage_view, G_TYPE_OBJECT)
99
100 void
101 clutter_stage_view_destroy (ClutterStageView *view)
102 {
103 g_object_run_dispose (G_OBJECT (view));
104 g_object_unref (view);
105 }
106
107 void
clutter_stage_view_get_layout(ClutterStageView * view,cairo_rectangle_int_t * rect)108 clutter_stage_view_get_layout (ClutterStageView *view,
109 cairo_rectangle_int_t *rect)
110 {
111 ClutterStageViewPrivate *priv =
112 clutter_stage_view_get_instance_private (view);
113
114 *rect = priv->layout;
115 }
116
117 /**
118 * clutter_stage_view_get_framebuffer:
119 * @view: a #ClutterStageView
120 *
121 * Retrieves the framebuffer of @view to draw to.
122 *
123 * Returns: (transfer none): a #CoglFramebuffer
124 */
125 CoglFramebuffer *
clutter_stage_view_get_framebuffer(ClutterStageView * view)126 clutter_stage_view_get_framebuffer (ClutterStageView *view)
127 {
128 ClutterStageViewPrivate *priv =
129 clutter_stage_view_get_instance_private (view);
130
131 if (priv->offscreen)
132 return COGL_FRAMEBUFFER (priv->offscreen);
133 else if (priv->shadow.framebuffer)
134 return COGL_FRAMEBUFFER (priv->shadow.framebuffer);
135 else
136 return priv->framebuffer;
137 }
138
139 /**
140 * clutter_stage_view_get_onscreen:
141 * @view: a #ClutterStageView
142 *
143 * Retrieves the onscreen framebuffer of @view if available.
144 *
145 * Returns: (transfer none): a #CoglFramebuffer
146 */
147 CoglFramebuffer *
clutter_stage_view_get_onscreen(ClutterStageView * view)148 clutter_stage_view_get_onscreen (ClutterStageView *view)
149 {
150 ClutterStageViewPrivate *priv =
151 clutter_stage_view_get_instance_private (view);
152
153 return priv->framebuffer;
154 }
155
156 static CoglPipeline *
clutter_stage_view_create_offscreen_pipeline(CoglOffscreen * offscreen)157 clutter_stage_view_create_offscreen_pipeline (CoglOffscreen *offscreen)
158 {
159 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen);
160 CoglPipeline *pipeline;
161
162 pipeline = cogl_pipeline_new (cogl_framebuffer_get_context (framebuffer));
163
164 cogl_pipeline_set_layer_filters (pipeline, 0,
165 COGL_PIPELINE_FILTER_NEAREST,
166 COGL_PIPELINE_FILTER_NEAREST);
167 cogl_pipeline_set_layer_texture (pipeline, 0,
168 cogl_offscreen_get_texture (offscreen));
169 cogl_pipeline_set_layer_wrap_mode (pipeline, 0,
170 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
171
172 return pipeline;
173 }
174
175 static void
clutter_stage_view_ensure_offscreen_blit_pipeline(ClutterStageView * view)176 clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view)
177 {
178 ClutterStageViewPrivate *priv =
179 clutter_stage_view_get_instance_private (view);
180 ClutterStageViewClass *view_class =
181 CLUTTER_STAGE_VIEW_GET_CLASS (view);
182
183 g_assert (priv->offscreen != NULL);
184
185 if (priv->offscreen_pipeline)
186 return;
187
188 priv->offscreen_pipeline =
189 clutter_stage_view_create_offscreen_pipeline (priv->offscreen);
190
191 if (view_class->setup_offscreen_blit_pipeline)
192 view_class->setup_offscreen_blit_pipeline (view, priv->offscreen_pipeline);
193 }
194
195 void
clutter_stage_view_invalidate_offscreen_blit_pipeline(ClutterStageView * view)196 clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view)
197 {
198 ClutterStageViewPrivate *priv =
199 clutter_stage_view_get_instance_private (view);
200
201 g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref);
202 }
203
204 void
clutter_stage_view_transform_rect_to_onscreen(ClutterStageView * view,const cairo_rectangle_int_t * src_rect,int dst_width,int dst_height,cairo_rectangle_int_t * dst_rect)205 clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view,
206 const cairo_rectangle_int_t *src_rect,
207 int dst_width,
208 int dst_height,
209 cairo_rectangle_int_t *dst_rect)
210 {
211 ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view);
212
213 return view_class->transform_rect_to_onscreen (view,
214 src_rect,
215 dst_width,
216 dst_height,
217 dst_rect);
218 }
219
220 static void
paint_transformed_framebuffer(ClutterStageView * view,CoglPipeline * pipeline,CoglOffscreen * src_framebuffer,CoglFramebuffer * dst_framebuffer,const cairo_region_t * redraw_clip)221 paint_transformed_framebuffer (ClutterStageView *view,
222 CoglPipeline *pipeline,
223 CoglOffscreen *src_framebuffer,
224 CoglFramebuffer *dst_framebuffer,
225 const cairo_region_t *redraw_clip)
226 {
227 graphene_matrix_t matrix;
228 unsigned int n_rectangles, i;
229 int dst_width, dst_height;
230 cairo_rectangle_int_t view_layout;
231 cairo_rectangle_int_t onscreen_layout;
232 float view_scale;
233 float *coordinates;
234
235 dst_width = cogl_framebuffer_get_width (dst_framebuffer);
236 dst_height = cogl_framebuffer_get_height (dst_framebuffer);
237 clutter_stage_view_get_layout (view, &view_layout);
238 clutter_stage_view_transform_rect_to_onscreen (view,
239 &(cairo_rectangle_int_t) {
240 .width = view_layout.width,
241 .height = view_layout.height,
242 },
243 view_layout.width,
244 view_layout.height,
245 &onscreen_layout);
246 view_scale = clutter_stage_view_get_scale (view);
247
248 cogl_framebuffer_push_matrix (dst_framebuffer);
249
250 graphene_matrix_init_translate (&matrix,
251 &GRAPHENE_POINT3D_INIT (-dst_width / 2.0,
252 -dst_height / 2.0,
253 0.f));
254 graphene_matrix_scale (&matrix,
255 1.0 / (dst_width / 2.0),
256 -1.0 / (dst_height / 2.0),
257 0.f);
258 cogl_framebuffer_set_projection_matrix (dst_framebuffer, &matrix);
259 cogl_framebuffer_set_viewport (dst_framebuffer,
260 0, 0, dst_width, dst_height);
261
262 n_rectangles = cairo_region_num_rectangles (redraw_clip);
263 coordinates = g_newa (float, 2 * 4 * n_rectangles);
264
265 for (i = 0; i < n_rectangles; i++)
266 {
267 cairo_rectangle_int_t src_rect;
268 cairo_rectangle_int_t dst_rect;
269
270 cairo_region_get_rectangle (redraw_clip, i, &src_rect);
271 _clutter_util_rectangle_offset (&src_rect,
272 -view_layout.x,
273 -view_layout.y,
274 &src_rect);
275
276 clutter_stage_view_transform_rect_to_onscreen (view,
277 &src_rect,
278 onscreen_layout.width,
279 onscreen_layout.height,
280 &dst_rect);
281
282 coordinates[i * 8 + 0] = (float) dst_rect.x * view_scale;
283 coordinates[i * 8 + 1] = (float) dst_rect.y * view_scale;
284 coordinates[i * 8 + 2] = ((float) (dst_rect.x + dst_rect.width) *
285 view_scale);
286 coordinates[i * 8 + 3] = ((float) (dst_rect.y + dst_rect.height) *
287 view_scale);
288
289 coordinates[i * 8 + 4] = (((float) dst_rect.x / (float) dst_width) *
290 view_scale);
291 coordinates[i * 8 + 5] = (((float) dst_rect.y / (float) dst_height) *
292 view_scale);
293 coordinates[i * 8 + 6] = ((float) (dst_rect.x + dst_rect.width) /
294 (float) dst_width) * view_scale;
295 coordinates[i * 8 + 7] = ((float) (dst_rect.y + dst_rect.height) /
296 (float) dst_height) * view_scale;
297 }
298
299 cogl_framebuffer_draw_textured_rectangles (dst_framebuffer,
300 pipeline,
301 coordinates,
302 n_rectangles);
303
304 cogl_framebuffer_pop_matrix (dst_framebuffer);
305 }
306
307 static gboolean
is_shadowfb_double_buffered(ClutterStageView * view)308 is_shadowfb_double_buffered (ClutterStageView *view)
309 {
310 ClutterStageViewPrivate *priv =
311 clutter_stage_view_get_instance_private (view);
312
313 return priv->shadow.dma_buf.handles[0] && priv->shadow.dma_buf.handles[1];
314 }
315
316 static gboolean
init_dma_buf_shadowfbs(ClutterStageView * view,CoglContext * cogl_context,int width,int height,GError ** error)317 init_dma_buf_shadowfbs (ClutterStageView *view,
318 CoglContext *cogl_context,
319 int width,
320 int height,
321 GError **error)
322 {
323 ClutterStageViewPrivate *priv =
324 clutter_stage_view_get_instance_private (view);
325 CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context);
326 CoglFramebuffer *initial_shadowfb;
327
328 if (!cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
329 {
330 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
331 "Buffer age not supported");
332 return FALSE;
333 }
334
335 if (!COGL_IS_ONSCREEN (priv->framebuffer))
336 {
337 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
338 "Tried to use shadow buffer without onscreen");
339 return FALSE;
340 }
341
342 priv->shadow.dma_buf.handles[0] = cogl_renderer_create_dma_buf (cogl_renderer,
343 width, height,
344 error);
345 if (!priv->shadow.dma_buf.handles[0])
346 return FALSE;
347
348 priv->shadow.dma_buf.handles[1] = cogl_renderer_create_dma_buf (cogl_renderer,
349 width, height,
350 error);
351 if (!priv->shadow.dma_buf.handles[1])
352 {
353 g_clear_pointer (&priv->shadow.dma_buf.handles[0],
354 cogl_dma_buf_handle_free);
355 return FALSE;
356 }
357
358 priv->shadow.dma_buf.damage_history = clutter_damage_history_new ();
359
360 initial_shadowfb =
361 cogl_dma_buf_handle_get_framebuffer (priv->shadow.dma_buf.handles[0]);
362 priv->shadow.framebuffer = COGL_OFFSCREEN (g_object_ref (initial_shadowfb));
363
364 return TRUE;
365 }
366
367 static CoglOffscreen *
create_offscreen_framebuffer(CoglContext * context,int width,int height,GError ** error)368 create_offscreen_framebuffer (CoglContext *context,
369 int width,
370 int height,
371 GError **error)
372 {
373 CoglOffscreen *framebuffer;
374 CoglTexture2D *texture;
375
376 texture = cogl_texture_2d_new_with_size (context, width, height);
377 cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture),
378 FALSE);
379
380 if (!cogl_texture_allocate (COGL_TEXTURE (texture), error))
381 {
382 cogl_object_unref (texture);
383 return FALSE;
384 }
385
386 framebuffer = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
387 cogl_object_unref (texture);
388 if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (framebuffer), error))
389 {
390 g_object_unref (framebuffer);
391 return FALSE;
392 }
393
394 return framebuffer;
395 }
396
397 static gboolean
init_fallback_shadowfb(ClutterStageView * view,CoglContext * cogl_context,int width,int height,GError ** error)398 init_fallback_shadowfb (ClutterStageView *view,
399 CoglContext *cogl_context,
400 int width,
401 int height,
402 GError **error)
403 {
404 ClutterStageViewPrivate *priv =
405 clutter_stage_view_get_instance_private (view);
406 CoglOffscreen *offscreen;
407
408 offscreen = create_offscreen_framebuffer (cogl_context, width, height, error);
409 if (!offscreen)
410 return FALSE;
411
412 priv->shadow.framebuffer = offscreen;
413 return TRUE;
414 }
415
416 static void
init_shadowfb(ClutterStageView * view)417 init_shadowfb (ClutterStageView *view)
418 {
419 ClutterStageViewPrivate *priv =
420 clutter_stage_view_get_instance_private (view);
421 g_autoptr (GError) error = NULL;
422 int width;
423 int height;
424 CoglContext *cogl_context;
425
426 width = cogl_framebuffer_get_width (priv->framebuffer);
427 height = cogl_framebuffer_get_height (priv->framebuffer);
428 cogl_context = cogl_framebuffer_get_context (priv->framebuffer);
429
430 if (g_strcmp0 (g_getenv ("MUTTER_DEBUG_ENABLE_DOUBLE_SHADOWFB"), "1") == 0)
431 {
432 if (init_dma_buf_shadowfbs (view, cogl_context, width, height, &error))
433 {
434 g_message ("Initialized double buffered shadow fb for %s",
435 priv->name);
436 return;
437 }
438
439 g_warning ("Failed to initialize double buffered shadow fb for %s: %s",
440 priv->name, error->message);
441 g_clear_error (&error);
442 }
443
444 if (!init_fallback_shadowfb (view, cogl_context, width, height, &error))
445 {
446 g_warning ("Failed to initialize single buffered shadow fb for %s: %s",
447 priv->name, error->message);
448 }
449 else
450 {
451 g_message ("Initialized single buffered shadow fb for %s", priv->name);
452 }
453 }
454
455 void
clutter_stage_view_after_paint(ClutterStageView * view,cairo_region_t * redraw_clip)456 clutter_stage_view_after_paint (ClutterStageView *view,
457 cairo_region_t *redraw_clip)
458 {
459 ClutterStageViewPrivate *priv =
460 clutter_stage_view_get_instance_private (view);
461
462 if (priv->offscreen)
463 {
464 clutter_stage_view_ensure_offscreen_blit_pipeline (view);
465
466 if (priv->shadow.framebuffer)
467 {
468 CoglFramebuffer *shadowfb =
469 COGL_FRAMEBUFFER (priv->shadow.framebuffer);
470
471 paint_transformed_framebuffer (view,
472 priv->offscreen_pipeline,
473 priv->offscreen,
474 shadowfb,
475 redraw_clip);
476 }
477 else
478 {
479 paint_transformed_framebuffer (view,
480 priv->offscreen_pipeline,
481 priv->offscreen,
482 priv->framebuffer,
483 redraw_clip);
484 }
485 }
486 }
487
488 static gboolean
is_tile_dirty(cairo_rectangle_int_t * tile,uint8_t * current_data,uint8_t * prev_data,int bpp,int stride)489 is_tile_dirty (cairo_rectangle_int_t *tile,
490 uint8_t *current_data,
491 uint8_t *prev_data,
492 int bpp,
493 int stride)
494 {
495 int y;
496
497 for (y = tile->y; y < tile->y + tile->height; y++)
498 {
499 if (memcmp (prev_data + y * stride + tile->x * bpp,
500 current_data + y * stride + tile->x * bpp,
501 tile->width * bpp) != 0)
502 return TRUE;
503 }
504
505 return FALSE;
506 }
507
508 static int
flip_dma_buf_idx(int idx)509 flip_dma_buf_idx (int idx)
510 {
511 return (idx + 1) % 2;
512 }
513
514 static cairo_region_t *
find_damaged_tiles(ClutterStageView * view,const cairo_region_t * damage_region,GError ** error)515 find_damaged_tiles (ClutterStageView *view,
516 const cairo_region_t *damage_region,
517 GError **error)
518 {
519 ClutterStageViewPrivate *priv =
520 clutter_stage_view_get_instance_private (view);
521 cairo_region_t *tile_damage_region;
522 cairo_rectangle_int_t damage_extents;
523 cairo_rectangle_int_t fb_rect;
524 int prev_dma_buf_idx;
525 CoglDmaBufHandle *prev_dma_buf_handle;
526 uint8_t *prev_data;
527 int current_dma_buf_idx;
528 CoglDmaBufHandle *current_dma_buf_handle;
529 uint8_t *current_data;
530 int width, height, stride, bpp;
531 int tile_x_min, tile_x_max;
532 int tile_y_min, tile_y_max;
533 int tile_x, tile_y;
534 const int tile_size = 16;
535
536 prev_dma_buf_idx = flip_dma_buf_idx (priv->shadow.dma_buf.current_idx);
537 prev_dma_buf_handle = priv->shadow.dma_buf.handles[prev_dma_buf_idx];
538
539 current_dma_buf_idx = priv->shadow.dma_buf.current_idx;
540 current_dma_buf_handle = priv->shadow.dma_buf.handles[current_dma_buf_idx];
541
542 width = cogl_dma_buf_handle_get_width (current_dma_buf_handle);
543 height = cogl_dma_buf_handle_get_height (current_dma_buf_handle);
544 stride = cogl_dma_buf_handle_get_stride (current_dma_buf_handle);
545 bpp = cogl_dma_buf_handle_get_bpp (current_dma_buf_handle);
546
547 cogl_framebuffer_finish (COGL_FRAMEBUFFER (priv->shadow.framebuffer));
548 #ifdef __linux__
549 if (!cogl_dma_buf_handle_sync_read_start (prev_dma_buf_handle, error))
550 return NULL;
551
552 if (!cogl_dma_buf_handle_sync_read_start (current_dma_buf_handle, error))
553 goto err_sync_read_current;
554 #endif
555 prev_data = cogl_dma_buf_handle_mmap (prev_dma_buf_handle, error);
556 if (!prev_data)
557 goto err_mmap_prev;
558 current_data = cogl_dma_buf_handle_mmap (current_dma_buf_handle, error);
559 if (!current_data)
560 goto err_mmap_current;
561
562 fb_rect = (cairo_rectangle_int_t) {
563 .width = width,
564 .height = height,
565 };
566
567 cairo_region_get_extents (damage_region, &damage_extents);
568
569 tile_x_min = damage_extents.x / tile_size;
570 tile_x_max = ((damage_extents.x + damage_extents.width + tile_size - 1) /
571 tile_size);
572 tile_y_min = damage_extents.y / tile_size;
573 tile_y_max = ((damage_extents.y + damage_extents.height + tile_size - 1) /
574 tile_size);
575
576 tile_damage_region = cairo_region_create ();
577
578 for (tile_y = tile_y_min; tile_y <= tile_y_max; tile_y++)
579 {
580 for (tile_x = tile_x_min; tile_x <= tile_x_max; tile_x++)
581 {
582 cairo_rectangle_int_t tile = {
583 .x = tile_x * tile_size,
584 .y = tile_y * tile_size,
585 .width = tile_size,
586 .height = tile_size,
587 };
588
589 if (cairo_region_contains_rectangle (damage_region, &tile) ==
590 CAIRO_REGION_OVERLAP_OUT)
591 continue;
592
593 _clutter_util_rectangle_intersection (&tile, &fb_rect, &tile);
594
595 if (is_tile_dirty (&tile, current_data, prev_data, bpp, stride))
596 cairo_region_union_rectangle (tile_damage_region, &tile);
597 }
598 }
599 #ifdef __linux__
600 if (!cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, error))
601 {
602 g_warning ("Failed to end DMA buffer read synchronization: %s",
603 (*error)->message);
604 g_clear_error (error);
605 }
606
607 if (!cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, error))
608 {
609 g_warning ("Failed to end DMA buffer read synchronization: %s",
610 (*error)->message);
611 g_clear_error (error);
612 }
613 #endif
614 cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL);
615 cogl_dma_buf_handle_munmap (current_dma_buf_handle, current_data, NULL);
616
617 cairo_region_intersect (tile_damage_region, damage_region);
618
619 return tile_damage_region;
620
621 err_mmap_current:
622 cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL);
623 #ifdef __linux__
624 err_mmap_prev:
625 cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, NULL);
626
627 err_sync_read_current:
628 cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, NULL);
629 #else
630 err_mmap_prev:
631 err_sync_read_current:
632 #endif
633 return NULL;
634 }
635
636 static void
swap_dma_buf_framebuffer(ClutterStageView * view)637 swap_dma_buf_framebuffer (ClutterStageView *view)
638 {
639 ClutterStageViewPrivate *priv =
640 clutter_stage_view_get_instance_private (view);
641 int next_idx;
642 CoglDmaBufHandle *next_dma_buf_handle;
643 CoglFramebuffer *next_framebuffer;
644
645 next_idx = ((priv->shadow.dma_buf.current_idx + 1) %
646 G_N_ELEMENTS (priv->shadow.dma_buf.handles));
647 priv->shadow.dma_buf.current_idx = next_idx;
648
649 next_dma_buf_handle = priv->shadow.dma_buf.handles[next_idx];
650 next_framebuffer =
651 cogl_dma_buf_handle_get_framebuffer (next_dma_buf_handle);
652 g_clear_object (&priv->shadow.framebuffer);
653 priv->shadow.framebuffer = COGL_OFFSCREEN (g_object_ref (next_framebuffer));
654 }
655
656 static void
copy_shadowfb_to_onscreen(ClutterStageView * view,const cairo_region_t * swap_region)657 copy_shadowfb_to_onscreen (ClutterStageView *view,
658 const cairo_region_t *swap_region)
659 {
660 ClutterStageViewPrivate *priv =
661 clutter_stage_view_get_instance_private (view);
662 ClutterDamageHistory *damage_history = priv->shadow.dma_buf.damage_history;
663 cairo_region_t *damage_region;
664 int age;
665 int i;
666
667 if (cairo_region_is_empty (swap_region))
668 {
669 cairo_rectangle_int_t full_damage = {
670 .width = cogl_framebuffer_get_width (priv->framebuffer),
671 .height = cogl_framebuffer_get_height (priv->framebuffer),
672 };
673 damage_region = cairo_region_create_rectangle (&full_damage);
674 }
675 else
676 {
677 damage_region = cairo_region_copy (swap_region);
678 }
679
680 if (is_shadowfb_double_buffered (view))
681 {
682 CoglOnscreen *onscreen = COGL_ONSCREEN (priv->framebuffer);
683 cairo_region_t *changed_region;
684
685 if (cogl_onscreen_get_frame_counter (onscreen) >= 1)
686 {
687 g_autoptr (GError) error = NULL;
688
689 changed_region = find_damaged_tiles (view, damage_region, &error);
690 if (!changed_region)
691 {
692 int other_dma_buf_idx;
693
694 g_warning ("Disabling actual damage detection: %s",
695 error->message);
696
697 other_dma_buf_idx =
698 flip_dma_buf_idx (priv->shadow.dma_buf.current_idx);
699 g_clear_pointer (&priv->shadow.dma_buf.handles[other_dma_buf_idx],
700 cogl_dma_buf_handle_free);
701 }
702 }
703 else
704 {
705 changed_region = cairo_region_copy (damage_region);
706 }
707
708 if (changed_region)
709 {
710 int buffer_age;
711
712 clutter_damage_history_record (damage_history, changed_region);
713
714 buffer_age = cogl_onscreen_get_buffer_age (onscreen);
715 if (clutter_damage_history_is_age_valid (damage_history, buffer_age))
716 {
717 for (age = 1; age <= buffer_age; age++)
718 {
719 const cairo_region_t *old_damage;
720
721 old_damage = clutter_damage_history_lookup (damage_history, age);
722 cairo_region_union (changed_region, old_damage);
723 }
724
725 cairo_region_destroy (damage_region);
726 damage_region = g_steal_pointer (&changed_region);
727 }
728 else
729 {
730 cairo_region_destroy (changed_region);
731 }
732
733 clutter_damage_history_step (damage_history);
734 }
735 }
736
737 for (i = 0; i < cairo_region_num_rectangles (damage_region); i++)
738 {
739 CoglFramebuffer *shadowfb = COGL_FRAMEBUFFER (priv->shadow.framebuffer);
740 g_autoptr (GError) error = NULL;
741 cairo_rectangle_int_t rect;
742
743 cairo_region_get_rectangle (damage_region, i, &rect);
744
745 if (!cogl_blit_framebuffer (shadowfb,
746 priv->framebuffer,
747 rect.x, rect.y,
748 rect.x, rect.y,
749 rect.width, rect.height,
750 &error))
751 {
752 g_warning ("Failed to blit shadow buffer: %s", error->message);
753 cairo_region_destroy (damage_region);
754 return;
755 }
756 }
757
758 cairo_region_destroy (damage_region);
759
760 if (is_shadowfb_double_buffered (view))
761 swap_dma_buf_framebuffer (view);
762 }
763
764 void
clutter_stage_view_before_swap_buffer(ClutterStageView * view,const cairo_region_t * swap_region)765 clutter_stage_view_before_swap_buffer (ClutterStageView *view,
766 const cairo_region_t *swap_region)
767 {
768 ClutterStageViewPrivate *priv =
769 clutter_stage_view_get_instance_private (view);
770
771 if (priv->shadow.framebuffer)
772 copy_shadowfb_to_onscreen (view, swap_region);
773 }
774
775 float
clutter_stage_view_get_scale(ClutterStageView * view)776 clutter_stage_view_get_scale (ClutterStageView *view)
777 {
778 ClutterStageViewPrivate *priv =
779 clutter_stage_view_get_instance_private (view);
780
781 return priv->scale;
782 }
783
784 typedef void (*FrontBufferCallback) (CoglFramebuffer *framebuffer,
785 gconstpointer user_data);
786
787 static void
clutter_stage_view_foreach_front_buffer(ClutterStageView * view,FrontBufferCallback callback,gconstpointer user_data)788 clutter_stage_view_foreach_front_buffer (ClutterStageView *view,
789 FrontBufferCallback callback,
790 gconstpointer user_data)
791 {
792 ClutterStageViewPrivate *priv =
793 clutter_stage_view_get_instance_private (view);
794
795 if (priv->offscreen)
796 {
797 callback (COGL_FRAMEBUFFER (priv->offscreen), user_data);
798 }
799 else if (priv->shadow.framebuffer)
800 {
801 if (is_shadowfb_double_buffered (view))
802 {
803 int i;
804
805 for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++)
806 {
807 CoglDmaBufHandle *handle = priv->shadow.dma_buf.handles[i];
808 CoglFramebuffer *framebuffer =
809 cogl_dma_buf_handle_get_framebuffer (handle);
810
811 callback (framebuffer, user_data);
812 }
813 }
814 else
815 {
816 callback (COGL_FRAMEBUFFER (priv->shadow.framebuffer), user_data);
817 }
818 }
819 else
820 {
821 callback (priv->framebuffer, user_data);
822 }
823 }
824
825 gboolean
clutter_stage_view_is_dirty_viewport(ClutterStageView * view)826 clutter_stage_view_is_dirty_viewport (ClutterStageView *view)
827 {
828 ClutterStageViewPrivate *priv =
829 clutter_stage_view_get_instance_private (view);
830
831 return priv->dirty_viewport;
832 }
833
834 void
clutter_stage_view_invalidate_viewport(ClutterStageView * view)835 clutter_stage_view_invalidate_viewport (ClutterStageView *view)
836 {
837 ClutterStageViewPrivate *priv =
838 clutter_stage_view_get_instance_private (view);
839
840 priv->dirty_viewport = TRUE;
841 }
842
843 static void
set_framebuffer_viewport(CoglFramebuffer * framebuffer,gconstpointer user_data)844 set_framebuffer_viewport (CoglFramebuffer *framebuffer,
845 gconstpointer user_data)
846 {
847 const graphene_rect_t *rect = user_data;
848
849 cogl_framebuffer_set_viewport (framebuffer,
850 rect->origin.x,
851 rect->origin.y,
852 rect->size.width,
853 rect->size.height);
854 }
855
856 void
clutter_stage_view_set_viewport(ClutterStageView * view,float x,float y,float width,float height)857 clutter_stage_view_set_viewport (ClutterStageView *view,
858 float x,
859 float y,
860 float width,
861 float height)
862 {
863 ClutterStageViewPrivate *priv =
864 clutter_stage_view_get_instance_private (view);
865 graphene_rect_t rect;
866
867 priv->dirty_viewport = FALSE;
868
869 rect = (graphene_rect_t) {
870 .origin = { .x = x, .y = y },
871 .size = { .width = width, .height = height },
872 };
873 clutter_stage_view_foreach_front_buffer (view,
874 set_framebuffer_viewport,
875 &rect);
876 }
877
878 gboolean
clutter_stage_view_is_dirty_projection(ClutterStageView * view)879 clutter_stage_view_is_dirty_projection (ClutterStageView *view)
880 {
881 ClutterStageViewPrivate *priv =
882 clutter_stage_view_get_instance_private (view);
883
884 return priv->dirty_projection;
885 }
886
887 static void
set_framebuffer_projection_matrix(CoglFramebuffer * framebuffer,gconstpointer user_data)888 set_framebuffer_projection_matrix (CoglFramebuffer *framebuffer,
889 gconstpointer user_data)
890 {
891 cogl_framebuffer_set_projection_matrix (framebuffer, user_data);
892 }
893
894 void
clutter_stage_view_invalidate_projection(ClutterStageView * view)895 clutter_stage_view_invalidate_projection (ClutterStageView *view)
896 {
897 ClutterStageViewPrivate *priv =
898 clutter_stage_view_get_instance_private (view);
899
900 priv->dirty_projection = TRUE;
901 }
902
903 void
clutter_stage_view_set_projection(ClutterStageView * view,const graphene_matrix_t * matrix)904 clutter_stage_view_set_projection (ClutterStageView *view,
905 const graphene_matrix_t *matrix)
906 {
907 ClutterStageViewPrivate *priv =
908 clutter_stage_view_get_instance_private (view);
909
910 priv->dirty_projection = FALSE;
911 clutter_stage_view_foreach_front_buffer (view,
912 set_framebuffer_projection_matrix,
913 matrix);
914 }
915
916 void
clutter_stage_view_get_offscreen_transformation_matrix(ClutterStageView * view,graphene_matrix_t * matrix)917 clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view,
918 graphene_matrix_t *matrix)
919 {
920 ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view);
921
922 view_class->get_offscreen_transformation_matrix (view, matrix);
923 }
924
925 void
clutter_stage_view_add_redraw_clip(ClutterStageView * view,const cairo_rectangle_int_t * clip)926 clutter_stage_view_add_redraw_clip (ClutterStageView *view,
927 const cairo_rectangle_int_t *clip)
928 {
929 ClutterStageViewPrivate *priv =
930 clutter_stage_view_get_instance_private (view);
931
932 if (priv->has_redraw_clip && !priv->redraw_clip)
933 return;
934
935 if (!clip)
936 {
937 g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
938 priv->has_redraw_clip = TRUE;
939 return;
940 }
941
942 if (clip->width == 0 || clip->height == 0)
943 return;
944
945 if (!priv->redraw_clip)
946 {
947 if (!clutter_util_rectangle_equal (&priv->layout, clip))
948 priv->redraw_clip = cairo_region_create_rectangle (clip);
949 }
950 else
951 {
952 cairo_region_union_rectangle (priv->redraw_clip, clip);
953
954 if (cairo_region_num_rectangles (priv->redraw_clip) == 1)
955 {
956 cairo_rectangle_int_t redraw_clip_extents;
957
958 cairo_region_get_extents (priv->redraw_clip, &redraw_clip_extents);
959 if (clutter_util_rectangle_equal (&priv->layout, &redraw_clip_extents))
960 g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
961 }
962 }
963
964 priv->has_redraw_clip = TRUE;
965 }
966
967 gboolean
clutter_stage_view_has_redraw_clip(ClutterStageView * view)968 clutter_stage_view_has_redraw_clip (ClutterStageView *view)
969 {
970 ClutterStageViewPrivate *priv =
971 clutter_stage_view_get_instance_private (view);
972
973 return priv->has_redraw_clip;
974 }
975
976 gboolean
clutter_stage_view_has_full_redraw_clip(ClutterStageView * view)977 clutter_stage_view_has_full_redraw_clip (ClutterStageView *view)
978 {
979 ClutterStageViewPrivate *priv =
980 clutter_stage_view_get_instance_private (view);
981
982 return priv->has_redraw_clip && !priv->redraw_clip;
983 }
984
985 const cairo_region_t *
clutter_stage_view_peek_redraw_clip(ClutterStageView * view)986 clutter_stage_view_peek_redraw_clip (ClutterStageView *view)
987 {
988 ClutterStageViewPrivate *priv =
989 clutter_stage_view_get_instance_private (view);
990
991 return priv->redraw_clip;
992 }
993
994 cairo_region_t *
clutter_stage_view_take_redraw_clip(ClutterStageView * view)995 clutter_stage_view_take_redraw_clip (ClutterStageView *view)
996 {
997 ClutterStageViewPrivate *priv =
998 clutter_stage_view_get_instance_private (view);
999
1000 priv->has_redraw_clip = FALSE;
1001
1002 return g_steal_pointer (&priv->redraw_clip);
1003 }
1004
1005 static void
clutter_stage_default_get_offscreen_transformation_matrix(ClutterStageView * view,graphene_matrix_t * matrix)1006 clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view,
1007 graphene_matrix_t *matrix)
1008 {
1009 graphene_matrix_init_identity (matrix);
1010 }
1011
1012 void
clutter_stage_view_assign_next_scanout(ClutterStageView * view,CoglScanout * scanout)1013 clutter_stage_view_assign_next_scanout (ClutterStageView *view,
1014 CoglScanout *scanout)
1015 {
1016 ClutterStageViewPrivate *priv =
1017 clutter_stage_view_get_instance_private (view);
1018
1019 g_set_object (&priv->next_scanout, scanout);
1020 }
1021
1022 CoglScanout *
clutter_stage_view_take_scanout(ClutterStageView * view)1023 clutter_stage_view_take_scanout (ClutterStageView *view)
1024 {
1025 ClutterStageViewPrivate *priv =
1026 clutter_stage_view_get_instance_private (view);
1027
1028 return g_steal_pointer (&priv->next_scanout);
1029 }
1030
1031 /**
1032 * clutter_stage_view_peek_scanout: (skip)
1033 */
1034 CoglScanout *
clutter_stage_view_peek_scanout(ClutterStageView * view)1035 clutter_stage_view_peek_scanout (ClutterStageView *view)
1036 {
1037 ClutterStageViewPrivate *priv =
1038 clutter_stage_view_get_instance_private (view);
1039
1040 return priv->next_scanout;
1041 }
1042
1043 void
clutter_stage_view_schedule_update(ClutterStageView * view)1044 clutter_stage_view_schedule_update (ClutterStageView *view)
1045 {
1046 ClutterStageViewPrivate *priv =
1047 clutter_stage_view_get_instance_private (view);
1048
1049 clutter_frame_clock_schedule_update (priv->frame_clock);
1050 }
1051
1052 float
clutter_stage_view_get_refresh_rate(ClutterStageView * view)1053 clutter_stage_view_get_refresh_rate (ClutterStageView *view)
1054 {
1055 ClutterStageViewPrivate *priv =
1056 clutter_stage_view_get_instance_private (view);
1057
1058 return priv->refresh_rate;
1059 }
1060
1061 /**
1062 * clutter_stage_view_get_frame_clock: (skip)
1063 */
1064 ClutterFrameClock *
clutter_stage_view_get_frame_clock(ClutterStageView * view)1065 clutter_stage_view_get_frame_clock (ClutterStageView *view)
1066 {
1067 ClutterStageViewPrivate *priv =
1068 clutter_stage_view_get_instance_private (view);
1069
1070 return priv->frame_clock;
1071 }
1072
1073 static void
handle_frame_clock_before_frame(ClutterFrameClock * frame_clock,int64_t frame_count,gpointer user_data)1074 handle_frame_clock_before_frame (ClutterFrameClock *frame_clock,
1075 int64_t frame_count,
1076 gpointer user_data)
1077 {
1078 ClutterStageView *view = user_data;
1079 ClutterStageViewPrivate *priv =
1080 clutter_stage_view_get_instance_private (view);
1081
1082 _clutter_stage_process_queued_events (priv->stage);
1083 }
1084
1085 static void
begin_frame_timing_measurement(ClutterStageView * view)1086 begin_frame_timing_measurement (ClutterStageView *view)
1087 {
1088 ClutterStageViewPrivate *priv =
1089 clutter_stage_view_get_instance_private (view);
1090
1091 priv->frame_timings.began_draw_time_us = g_get_monotonic_time ();
1092 }
1093
1094 static void
end_frame_timing_measurement(ClutterStageView * view)1095 end_frame_timing_measurement (ClutterStageView *view)
1096 {
1097 ClutterStageViewPrivate *priv =
1098 clutter_stage_view_get_instance_private (view);
1099 int64_t now_us = g_get_monotonic_time ();
1100 int64_t draw_time_us;
1101
1102 draw_time_us = now_us - priv->frame_timings.began_draw_time_us;
1103
1104 priv->frame_timings.frame_count++;
1105 priv->frame_timings.cumulative_draw_time_us += draw_time_us;
1106 if (draw_time_us > priv->frame_timings.worst_draw_time_us)
1107 priv->frame_timings.worst_draw_time_us = draw_time_us;
1108
1109 if (priv->frame_timings.frame_count && priv->frame_timings.last_print_time_us)
1110 {
1111 float time_since_last_print_s;
1112
1113 time_since_last_print_s =
1114 (now_us - priv->frame_timings.last_print_time_us) /
1115 (float) G_USEC_PER_SEC;
1116
1117 if (time_since_last_print_s >= 1.0)
1118 {
1119 float avg_fps, avg_draw_time_ms, worst_draw_time_ms;
1120
1121 avg_fps = priv->frame_timings.frame_count / time_since_last_print_s;
1122
1123 avg_draw_time_ms =
1124 (priv->frame_timings.cumulative_draw_time_us / 1000.0) /
1125 priv->frame_timings.frame_count;
1126
1127 worst_draw_time_ms = priv->frame_timings.worst_draw_time_us / 1000.0;
1128
1129 g_print ("*** %s frame timings over %.01fs: "
1130 "%.02f FPS, average: %.01fms, peak: %.01fms\n",
1131 priv->name,
1132 time_since_last_print_s,
1133 avg_fps,
1134 avg_draw_time_ms,
1135 worst_draw_time_ms);
1136
1137 priv->frame_timings.frame_count = 0;
1138 priv->frame_timings.cumulative_draw_time_us = 0;
1139 priv->frame_timings.worst_draw_time_us = 0;
1140 priv->frame_timings.last_print_time_us = now_us;
1141 }
1142 }
1143 else if (!priv->frame_timings.last_print_time_us)
1144 {
1145 priv->frame_timings.last_print_time_us = now_us;
1146 }
1147 }
1148
1149 static ClutterFrameResult
handle_frame_clock_frame(ClutterFrameClock * frame_clock,int64_t frame_count,int64_t time_us,gpointer user_data)1150 handle_frame_clock_frame (ClutterFrameClock *frame_clock,
1151 int64_t frame_count,
1152 int64_t time_us,
1153 gpointer user_data)
1154 {
1155 ClutterStageView *view = user_data;
1156 ClutterStageViewPrivate *priv =
1157 clutter_stage_view_get_instance_private (view);
1158 ClutterStage *stage = priv->stage;
1159 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
1160 g_autoptr (GSList) devices = NULL;
1161 ClutterFrame frame;
1162
1163 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
1164 return CLUTTER_FRAME_RESULT_IDLE;
1165
1166 if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)))
1167 return CLUTTER_FRAME_RESULT_IDLE;
1168
1169 if (!clutter_actor_is_mapped (CLUTTER_ACTOR (stage)))
1170 return CLUTTER_FRAME_RESULT_IDLE;
1171
1172 if (_clutter_context_get_show_fps ())
1173 begin_frame_timing_measurement (view);
1174
1175 _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT);
1176 clutter_stage_emit_before_update (stage, view);
1177
1178 clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
1179 clutter_stage_maybe_finish_queue_redraws (stage);
1180
1181 clutter_stage_finish_layout (stage);
1182
1183 devices = clutter_stage_find_updated_devices (stage);
1184
1185 frame = CLUTTER_FRAME_INIT;
1186
1187 _clutter_stage_window_prepare_frame (stage_window, view, &frame);
1188
1189 if (clutter_stage_view_has_redraw_clip (view))
1190 {
1191 clutter_stage_emit_before_paint (stage, view);
1192
1193 _clutter_stage_window_redraw_view (stage_window, view, &frame);
1194
1195 clutter_frame_clock_record_flip_time (frame_clock,
1196 g_get_monotonic_time ());
1197
1198 clutter_stage_emit_after_paint (stage, view);
1199
1200 if (_clutter_context_get_show_fps ())
1201 end_frame_timing_measurement (view);
1202 }
1203
1204 _clutter_stage_window_finish_frame (stage_window, view, &frame);
1205
1206 clutter_stage_update_devices (stage, devices);
1207
1208 _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
1209 clutter_stage_emit_after_update (stage, view);
1210
1211 return clutter_frame_get_result (&frame);
1212 }
1213
1214 static const ClutterFrameListenerIface frame_clock_listener_iface = {
1215 .before_frame = handle_frame_clock_before_frame,
1216 .frame = handle_frame_clock_frame,
1217 };
1218
1219 void
clutter_stage_view_notify_presented(ClutterStageView * view,ClutterFrameInfo * frame_info)1220 clutter_stage_view_notify_presented (ClutterStageView *view,
1221 ClutterFrameInfo *frame_info)
1222 {
1223 ClutterStageViewPrivate *priv =
1224 clutter_stage_view_get_instance_private (view);
1225
1226 clutter_stage_presented (priv->stage, view, frame_info);
1227 clutter_frame_clock_notify_presented (priv->frame_clock, frame_info);
1228 }
1229
1230 void
clutter_stage_view_notify_ready(ClutterStageView * view)1231 clutter_stage_view_notify_ready (ClutterStageView *view)
1232 {
1233 ClutterStageViewPrivate *priv =
1234 clutter_stage_view_get_instance_private (view);
1235
1236 clutter_frame_clock_notify_ready (priv->frame_clock);
1237 }
1238
1239 static void
sanity_check_framebuffer(ClutterStageView * view)1240 sanity_check_framebuffer (ClutterStageView *view)
1241 {
1242 ClutterStageViewPrivate *priv =
1243 clutter_stage_view_get_instance_private (view);
1244 G_GNUC_UNUSED int fb_width, fb_height;
1245
1246 fb_width = cogl_framebuffer_get_width (priv->framebuffer);
1247 fb_height = cogl_framebuffer_get_height (priv->framebuffer);
1248
1249 g_warn_if_fail (fabsf (roundf (fb_width / priv->scale) -
1250 fb_width / priv->scale) < FLT_EPSILON);
1251 g_warn_if_fail (fabsf (roundf (fb_height / priv->scale) -
1252 fb_height / priv->scale) < FLT_EPSILON);
1253 }
1254
1255 static void
clutter_stage_view_set_framebuffer(ClutterStageView * view,CoglFramebuffer * framebuffer)1256 clutter_stage_view_set_framebuffer (ClutterStageView *view,
1257 CoglFramebuffer *framebuffer)
1258 {
1259 ClutterStageViewPrivate *priv =
1260 clutter_stage_view_get_instance_private (view);
1261
1262 g_warn_if_fail (!priv->framebuffer);
1263 if (framebuffer)
1264 {
1265 priv->framebuffer = g_object_ref (framebuffer);
1266 sanity_check_framebuffer (view);
1267 }
1268 }
1269
1270 static void
clutter_stage_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1271 clutter_stage_view_get_property (GObject *object,
1272 guint prop_id,
1273 GValue *value,
1274 GParamSpec *pspec)
1275 {
1276 ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
1277 ClutterStageViewPrivate *priv =
1278 clutter_stage_view_get_instance_private (view);
1279
1280 switch (prop_id)
1281 {
1282 case PROP_NAME:
1283 g_value_set_string (value, priv->name);
1284 break;
1285 case PROP_STAGE:
1286 g_value_set_boxed (value, &priv->stage);
1287 break;
1288 case PROP_LAYOUT:
1289 g_value_set_boxed (value, &priv->layout);
1290 break;
1291 case PROP_FRAMEBUFFER:
1292 g_value_set_object (value, priv->framebuffer);
1293 break;
1294 case PROP_OFFSCREEN:
1295 g_value_set_object (value, priv->offscreen);
1296 break;
1297 case PROP_USE_SHADOWFB:
1298 g_value_set_boolean (value, priv->use_shadowfb);
1299 break;
1300 case PROP_SCALE:
1301 g_value_set_float (value, priv->scale);
1302 break;
1303 case PROP_REFRESH_RATE:
1304 g_value_set_float (value, priv->refresh_rate);
1305 break;
1306 case PROP_VBLANK_DURATION_US:
1307 g_value_set_int64 (value, priv->vblank_duration_us);
1308 break;
1309 default:
1310 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1311 }
1312 }
1313
1314 static void
clutter_stage_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1315 clutter_stage_view_set_property (GObject *object,
1316 guint prop_id,
1317 const GValue *value,
1318 GParamSpec *pspec)
1319 {
1320 ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
1321 ClutterStageViewPrivate *priv =
1322 clutter_stage_view_get_instance_private (view);
1323 cairo_rectangle_int_t *layout;
1324
1325 switch (prop_id)
1326 {
1327 case PROP_NAME:
1328 priv->name = g_value_dup_string (value);
1329 break;
1330 case PROP_STAGE:
1331 priv->stage = g_value_get_object (value);
1332 break;
1333 case PROP_LAYOUT:
1334 layout = g_value_get_boxed (value);
1335 priv->layout = *layout;
1336 break;
1337 case PROP_FRAMEBUFFER:
1338 clutter_stage_view_set_framebuffer (view, g_value_get_object (value));
1339 break;
1340 case PROP_OFFSCREEN:
1341 priv->offscreen = g_value_dup_object (value);
1342 break;
1343 case PROP_USE_SHADOWFB:
1344 priv->use_shadowfb = g_value_get_boolean (value);
1345 break;
1346 case PROP_SCALE:
1347 priv->scale = g_value_get_float (value);
1348 break;
1349 case PROP_REFRESH_RATE:
1350 priv->refresh_rate = g_value_get_float (value);
1351 break;
1352 case PROP_VBLANK_DURATION_US:
1353 priv->vblank_duration_us = g_value_get_int64 (value);
1354 break;
1355 default:
1356 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1357 }
1358 }
1359
1360 static void
clutter_stage_view_constructed(GObject * object)1361 clutter_stage_view_constructed (GObject *object)
1362 {
1363 ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
1364 ClutterStageViewPrivate *priv =
1365 clutter_stage_view_get_instance_private (view);
1366
1367 if (priv->use_shadowfb)
1368 init_shadowfb (view);
1369
1370 priv->frame_clock = clutter_frame_clock_new (priv->refresh_rate,
1371 priv->vblank_duration_us,
1372 &frame_clock_listener_iface,
1373 view);
1374
1375 clutter_stage_view_add_redraw_clip (view, NULL);
1376 clutter_stage_view_schedule_update (view);
1377
1378 G_OBJECT_CLASS (clutter_stage_view_parent_class)->constructed (object);
1379 }
1380
1381 static void
clutter_stage_view_dispose(GObject * object)1382 clutter_stage_view_dispose (GObject *object)
1383 {
1384 ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
1385 ClutterStageViewPrivate *priv =
1386 clutter_stage_view_get_instance_private (view);
1387 int i;
1388
1389 g_clear_pointer (&priv->name, g_free);
1390
1391 g_clear_object (&priv->shadow.framebuffer);
1392 for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++)
1393 {
1394 g_clear_pointer (&priv->shadow.dma_buf.handles[i],
1395 cogl_dma_buf_handle_free);
1396 }
1397 g_clear_pointer (&priv->shadow.dma_buf.damage_history,
1398 clutter_damage_history_free);
1399
1400 g_clear_object (&priv->offscreen);
1401 g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref);
1402 g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
1403 g_clear_pointer (&priv->frame_clock, clutter_frame_clock_destroy);
1404
1405 G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
1406 }
1407
1408 static void
clutter_stage_view_finalize(GObject * object)1409 clutter_stage_view_finalize (GObject *object)
1410 {
1411 ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
1412 ClutterStageViewPrivate *priv =
1413 clutter_stage_view_get_instance_private (view);
1414
1415 g_clear_object (&priv->framebuffer);
1416
1417 G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
1418 }
1419
1420 static void
clutter_stage_view_init(ClutterStageView * view)1421 clutter_stage_view_init (ClutterStageView *view)
1422 {
1423 ClutterStageViewPrivate *priv =
1424 clutter_stage_view_get_instance_private (view);
1425
1426 priv->dirty_viewport = TRUE;
1427 priv->dirty_projection = TRUE;
1428 priv->scale = 1.0;
1429 priv->refresh_rate = 60.0;
1430 }
1431
1432 static void
clutter_stage_view_class_init(ClutterStageViewClass * klass)1433 clutter_stage_view_class_init (ClutterStageViewClass *klass)
1434 {
1435 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1436
1437 klass->get_offscreen_transformation_matrix =
1438 clutter_stage_default_get_offscreen_transformation_matrix;
1439
1440 object_class->get_property = clutter_stage_view_get_property;
1441 object_class->set_property = clutter_stage_view_set_property;
1442 object_class->constructed = clutter_stage_view_constructed;
1443 object_class->dispose = clutter_stage_view_dispose;
1444 object_class->finalize = clutter_stage_view_finalize;
1445
1446 obj_props[PROP_NAME] =
1447 g_param_spec_string ("name",
1448 "Name",
1449 "Name of view",
1450 NULL,
1451 G_PARAM_READWRITE |
1452 G_PARAM_CONSTRUCT_ONLY |
1453 G_PARAM_STATIC_STRINGS);
1454
1455 obj_props[PROP_STAGE] =
1456 g_param_spec_object ("stage",
1457 "The stage",
1458 "The ClutterStage",
1459 CLUTTER_TYPE_STAGE,
1460 G_PARAM_READWRITE |
1461 G_PARAM_CONSTRUCT_ONLY |
1462 G_PARAM_STATIC_STRINGS);
1463
1464 obj_props[PROP_LAYOUT] =
1465 g_param_spec_boxed ("layout",
1466 "View layout",
1467 "The view layout on the screen",
1468 CAIRO_GOBJECT_TYPE_RECTANGLE_INT,
1469 G_PARAM_READWRITE |
1470 G_PARAM_CONSTRUCT |
1471 G_PARAM_STATIC_STRINGS);
1472
1473 obj_props[PROP_FRAMEBUFFER] =
1474 g_param_spec_object ("framebuffer",
1475 "View framebuffer",
1476 "The front buffer of the view",
1477 COGL_TYPE_FRAMEBUFFER,
1478 G_PARAM_READWRITE |
1479 G_PARAM_CONSTRUCT |
1480 G_PARAM_STATIC_STRINGS);
1481
1482 obj_props[PROP_OFFSCREEN] =
1483 g_param_spec_object ("offscreen",
1484 "Offscreen buffer",
1485 "Framebuffer used as intermediate buffer",
1486 COGL_TYPE_OFFSCREEN,
1487 G_PARAM_READWRITE |
1488 G_PARAM_CONSTRUCT_ONLY |
1489 G_PARAM_STATIC_STRINGS);
1490
1491 obj_props[PROP_USE_SHADOWFB] =
1492 g_param_spec_boolean ("use-shadowfb",
1493 "Use shadowfb",
1494 "Whether to use one or more shadow framebuffers",
1495 FALSE,
1496 G_PARAM_READWRITE |
1497 G_PARAM_CONSTRUCT_ONLY |
1498 G_PARAM_STATIC_STRINGS);
1499
1500 obj_props[PROP_SCALE] =
1501 g_param_spec_float ("scale",
1502 "View scale",
1503 "The view scale",
1504 0.5, G_MAXFLOAT, 1.0,
1505 G_PARAM_READWRITE |
1506 G_PARAM_CONSTRUCT |
1507 G_PARAM_STATIC_STRINGS);
1508
1509 obj_props[PROP_REFRESH_RATE] =
1510 g_param_spec_float ("refresh-rate",
1511 "Refresh rate",
1512 "Update refresh rate",
1513 1.0, G_MAXFLOAT, 60.0,
1514 G_PARAM_READWRITE |
1515 G_PARAM_CONSTRUCT |
1516 G_PARAM_STATIC_STRINGS);
1517
1518 obj_props[PROP_VBLANK_DURATION_US] =
1519 g_param_spec_int64 ("vblank-duration-us",
1520 "Vblank duration (µs)",
1521 "The vblank duration",
1522 0, G_MAXINT64, 0,
1523 G_PARAM_READWRITE |
1524 G_PARAM_CONSTRUCT_ONLY |
1525 G_PARAM_STATIC_STRINGS);
1526
1527 g_object_class_install_properties (object_class, PROP_LAST, obj_props);
1528 }
1529