1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2007,2008,2009,2012 Intel Corporation.
7 * Copyright (C) 2019 DisplayLink (UK) Ltd.
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use, copy,
13 * modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *
29 *
30 */
31
32 #include "cogl-config.h"
33
34 #include <string.h>
35
36 #include "cogl-debug.h"
37 #include "cogl-context-private.h"
38 #include "cogl-display-private.h"
39 #include "cogl-renderer-private.h"
40 #include "cogl-object-private.h"
41 #include "cogl-util.h"
42 #include "cogl-texture-private.h"
43 #include "cogl-framebuffer-private.h"
44 #include "cogl-onscreen-template-private.h"
45 #include "cogl-clip-stack.h"
46 #include "cogl-journal-private.h"
47 #include "cogl-pipeline-state-private.h"
48 #include "cogl-primitive-private.h"
49 #include "cogl-offscreen.h"
50 #include "cogl1-context.h"
51 #include "cogl-private.h"
52 #include "cogl-primitives-private.h"
53 #include "cogl-gtype-private.h"
54 #include "winsys/cogl-winsys-private.h"
55
56 enum
57 {
58 PROP_0,
59
60 PROP_CONTEXT,
61 PROP_DRIVER_CONFIG,
62 PROP_WIDTH,
63 PROP_HEIGHT,
64
65 N_PROPS
66 };
67
68 static GParamSpec *obj_props[N_PROPS];
69
70 enum
71 {
72 DESTROY,
73
74 N_SIGNALS
75 };
76
77 static guint signals[N_SIGNALS];
78
79 #ifdef COGL_ENABLE_DEBUG
80 static CoglUserDataKey wire_pipeline_key;
81 #endif
82
83 typedef struct _CoglFramebufferPrivate
84 {
85 CoglContext *context;
86
87 /* The user configuration before allocation... */
88 CoglFramebufferConfig config;
89
90 CoglFramebufferDriverConfig driver_config;
91 CoglFramebufferDriver *driver;
92
93 int width;
94 int height;
95 /* Format of the pixels in the framebuffer (including the expected
96 premult state) */
97 CoglPixelFormat internal_format;
98 gboolean allocated;
99
100 CoglMatrixStack *modelview_stack;
101 CoglMatrixStack *projection_stack;
102 float viewport_x;
103 float viewport_y;
104 float viewport_width;
105 float viewport_height;
106 int viewport_age;
107 int viewport_age_for_scissor_workaround;
108
109 CoglClipStack *clip_stack;
110
111 gboolean dither_enabled;
112 gboolean depth_writing_enabled;
113 CoglStereoMode stereo_mode;
114
115 /* We journal the textured rectangles we want to submit to OpenGL so
116 * we have an opportunity to batch them together into less draw
117 * calls. */
118 CoglJournal *journal;
119
120 /* The scene of a given framebuffer may depend on images in other
121 * framebuffers... */
122 GList *deps;
123
124 /* As part of an optimization for reading-back single pixels from a
125 * framebuffer in some simple cases where the geometry is still
126 * available in the journal we need to track the bounds of the last
127 * region cleared, its color and we need to track when something
128 * does in fact draw to that region so it is no longer clear.
129 */
130 float clear_color_red;
131 float clear_color_green;
132 float clear_color_blue;
133 float clear_color_alpha;
134 int clear_clip_x0;
135 int clear_clip_y0;
136 int clear_clip_x1;
137 int clear_clip_y1;
138 gboolean clear_clip_dirty;
139
140 int samples_per_pixel;
141
142 /* Whether the depth buffer was enabled for this framebuffer,
143 * usually means it needs to be cleared before being reused next.
144 */
145 gboolean depth_buffer_clear_needed;
146 } CoglFramebufferPrivate;
147
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(CoglFramebuffer,cogl_framebuffer,G_TYPE_OBJECT)148 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (CoglFramebuffer, cogl_framebuffer,
149 G_TYPE_OBJECT)
150
151 uint32_t
152 cogl_framebuffer_error_quark (void)
153 {
154 return g_quark_from_static_string ("cogl-framebuffer-error-quark");
155 }
156
157 gboolean
cogl_is_framebuffer(void * object)158 cogl_is_framebuffer (void *object)
159 {
160 return COGL_IS_FRAMEBUFFER (object);
161 }
162
163 static void
cogl_framebuffer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)164 cogl_framebuffer_get_property (GObject *object,
165 guint prop_id,
166 GValue *value,
167 GParamSpec *pspec)
168 {
169 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (object);
170 CoglFramebufferPrivate *priv =
171 cogl_framebuffer_get_instance_private (framebuffer);
172
173 switch (prop_id)
174 {
175 case PROP_CONTEXT:
176 g_value_set_boxed (value, priv->context);
177 break;
178 case PROP_DRIVER_CONFIG:
179 g_value_set_pointer (value, &priv->driver_config);
180 break;
181 case PROP_WIDTH:
182 g_value_set_int (value, priv->width);
183 break;
184 case PROP_HEIGHT:
185 g_value_set_int (value, priv->height);
186 break;
187 default:
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189 }
190 }
191
192 static void
cogl_framebuffer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)193 cogl_framebuffer_set_property (GObject *object,
194 guint prop_id,
195 const GValue *value,
196 GParamSpec *pspec)
197 {
198 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (object);
199 CoglFramebufferPrivate *priv =
200 cogl_framebuffer_get_instance_private (framebuffer);
201 CoglFramebufferDriverConfig *driver_config;
202
203 switch (prop_id)
204 {
205 case PROP_CONTEXT:
206 priv->context = g_value_get_boxed (value);
207 break;
208 case PROP_DRIVER_CONFIG:
209 driver_config = g_value_get_pointer (value);
210 if (driver_config)
211 priv->driver_config = *driver_config;
212 break;
213 case PROP_WIDTH:
214 priv->width = g_value_get_int (value);
215 break;
216 case PROP_HEIGHT:
217 priv->height = g_value_get_int (value);
218 break;
219 default:
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 }
222 }
223
224 static void
cogl_framebuffer_constructed(GObject * object)225 cogl_framebuffer_constructed (GObject *object)
226 {
227 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (object);
228 CoglFramebufferPrivate *priv =
229 cogl_framebuffer_get_instance_private (framebuffer);
230
231 g_assert (priv->context);
232
233 priv->internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
234 priv->viewport_x = 0;
235 priv->viewport_y = 0;
236 priv->viewport_width = priv->width;
237 priv->viewport_height = priv->height;
238 priv->viewport_age = 0;
239 priv->viewport_age_for_scissor_workaround = -1;
240 priv->dither_enabled = TRUE;
241 priv->depth_writing_enabled = TRUE;
242 priv->depth_buffer_clear_needed = TRUE;
243
244 priv->modelview_stack = cogl_matrix_stack_new (priv->context);
245 priv->projection_stack = cogl_matrix_stack_new (priv->context);
246
247 priv->samples_per_pixel = 0;
248
249 priv->clip_stack = NULL;
250
251 priv->journal = _cogl_journal_new (framebuffer);
252
253 /* Ensure we know the framebuffer->clear_color* members can't be
254 * referenced for our fast-path read-pixel optimization (see
255 * _cogl_journal_try_read_pixel()) until some region of the
256 * framebuffer is initialized.
257 */
258 priv->clear_clip_dirty = TRUE;
259
260 /* XXX: We have to maintain a central list of all framebuffers
261 * because at times we need to be able to flush all known journals.
262 *
263 * Examples where we need to flush all journals are:
264 * - because journal entries can reference OpenGL texture
265 * coordinates that may not survive texture-atlas reorganization
266 * so we need the ability to flush those entries.
267 * - because although we generally advise against modifying
268 * pipelines after construction we have to handle that possibility
269 * and since pipelines may be referenced in journal entries we
270 * need to be able to flush them before allowing the pipelines to
271 * be changed.
272 *
273 * Note we don't maintain a list of journals and associate
274 * framebuffers with journals by e.g. having a journal->framebuffer
275 * reference since that would introduce a circular reference.
276 *
277 * Note: As a future change to try and remove the need to index all
278 * journals it might be possible to defer resolving of OpenGL
279 * texture coordinates for rectangle primitives until we come to
280 * flush a journal. This would mean for instance that a single
281 * rectangle entry in a journal could later be expanded into
282 * multiple quad primitives to handle sliced textures but would mean
283 * we don't have to worry about retaining references to OpenGL
284 * texture coordinates that may later become invalid.
285 */
286 priv->context->framebuffers = g_list_prepend (priv->context->framebuffers,
287 framebuffer);
288 }
289
290 void
_cogl_framebuffer_set_internal_format(CoglFramebuffer * framebuffer,CoglPixelFormat internal_format)291 _cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer,
292 CoglPixelFormat internal_format)
293 {
294 CoglFramebufferPrivate *priv =
295 cogl_framebuffer_get_instance_private (framebuffer);
296
297 priv->internal_format = internal_format;
298 }
299
300 CoglPixelFormat
cogl_framebuffer_get_internal_format(CoglFramebuffer * framebuffer)301 cogl_framebuffer_get_internal_format (CoglFramebuffer *framebuffer)
302 {
303 CoglFramebufferPrivate *priv =
304 cogl_framebuffer_get_instance_private (framebuffer);
305
306 return priv->internal_format;
307 }
308
309 const CoglFramebufferConfig *
cogl_framebuffer_get_config(CoglFramebuffer * framebuffer)310 cogl_framebuffer_get_config (CoglFramebuffer *framebuffer)
311 {
312 CoglFramebufferPrivate *priv =
313 cogl_framebuffer_get_instance_private (framebuffer);
314
315 return &priv->config;
316 }
317
318 void
cogl_framebuffer_init_config(CoglFramebuffer * framebuffer,const CoglFramebufferConfig * config)319 cogl_framebuffer_init_config (CoglFramebuffer *framebuffer,
320 const CoglFramebufferConfig *config)
321 {
322 CoglFramebufferPrivate *priv =
323 cogl_framebuffer_get_instance_private (framebuffer);
324
325 priv->config = *config;
326 cogl_object_ref (priv->config.swap_chain);
327 }
328
329 static void
cogl_framebuffer_dispose(GObject * object)330 cogl_framebuffer_dispose (GObject *object)
331 {
332 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (object);
333 CoglFramebufferPrivate *priv =
334 cogl_framebuffer_get_instance_private (framebuffer);
335 CoglContext *ctx = priv->context;
336
337 if (priv->journal)
338 {
339 _cogl_journal_flush (priv->journal);
340
341 g_signal_emit (framebuffer, signals[DESTROY], 0);
342
343 _cogl_fence_cancel_fences_for_framebuffer (framebuffer);
344 }
345
346 g_clear_pointer (&priv->clip_stack, _cogl_clip_stack_unref);
347 cogl_clear_object (&priv->modelview_stack);
348 cogl_clear_object (&priv->projection_stack);
349 cogl_clear_object (&priv->journal);
350
351 ctx->framebuffers = g_list_remove (ctx->framebuffers, framebuffer);
352
353 if (ctx->current_draw_buffer == framebuffer)
354 ctx->current_draw_buffer = NULL;
355 if (ctx->current_read_buffer == framebuffer)
356 ctx->current_read_buffer = NULL;
357
358 g_clear_object (&priv->driver);
359 }
360
361 static void
cogl_framebuffer_init(CoglFramebuffer * framebuffer)362 cogl_framebuffer_init (CoglFramebuffer *framebuffer)
363 {
364 CoglFramebufferPrivate *priv =
365 cogl_framebuffer_get_instance_private (framebuffer);
366
367 priv->width = -1;
368 priv->height = -1;
369 }
370
371 static void
cogl_framebuffer_class_init(CoglFramebufferClass * klass)372 cogl_framebuffer_class_init (CoglFramebufferClass *klass)
373 {
374 GObjectClass *object_class = G_OBJECT_CLASS (klass);
375
376 object_class->dispose = cogl_framebuffer_dispose;
377 object_class->constructed = cogl_framebuffer_constructed;
378 object_class->get_property = cogl_framebuffer_get_property;
379 object_class->set_property = cogl_framebuffer_set_property;
380
381 obj_props[PROP_CONTEXT] =
382 g_param_spec_boxed ("context",
383 "context",
384 "CoglContext",
385 COGL_TYPE_HANDLE,
386 G_PARAM_READWRITE |
387 G_PARAM_CONSTRUCT_ONLY |
388 G_PARAM_STATIC_STRINGS);
389 obj_props[PROP_DRIVER_CONFIG] =
390 g_param_spec_pointer ("driver-config",
391 "driver-config",
392 "CoglFramebufferDriverConfig",
393 G_PARAM_READWRITE |
394 G_PARAM_CONSTRUCT_ONLY |
395 G_PARAM_STATIC_STRINGS);
396 obj_props[PROP_WIDTH] =
397 g_param_spec_int ("width",
398 "width",
399 "framebuffer width",
400 -1, INT_MAX, -1,
401 G_PARAM_READWRITE |
402 G_PARAM_CONSTRUCT |
403 G_PARAM_STATIC_STRINGS);
404 obj_props[PROP_HEIGHT] =
405 g_param_spec_int ("height",
406 "height",
407 "framebuffer height",
408 -1, INT_MAX, -1,
409 G_PARAM_READWRITE |
410 G_PARAM_CONSTRUCT |
411 G_PARAM_STATIC_STRINGS);
412
413 g_object_class_install_properties (object_class, N_PROPS, obj_props);
414
415 signals[DESTROY] =
416 g_signal_new (I_("destroy"),
417 G_TYPE_FROM_CLASS (object_class),
418 G_SIGNAL_RUN_LAST,
419 0,
420 NULL, NULL, NULL,
421 G_TYPE_NONE,
422 0);
423 }
424
425 const CoglWinsysVtable *
_cogl_framebuffer_get_winsys(CoglFramebuffer * framebuffer)426 _cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer)
427 {
428 CoglFramebufferPrivate *priv =
429 cogl_framebuffer_get_instance_private (framebuffer);
430
431 return priv->context->display->renderer->winsys_vtable;
432 }
433
434 /* This version of cogl_clear can be used internally as an alternative
435 * to avoid flushing the journal or the framebuffer state. This is
436 * needed when doing operations that may be called while flushing
437 * the journal */
438 void
_cogl_framebuffer_clear_without_flush4f(CoglFramebuffer * framebuffer,unsigned long buffers,float red,float green,float blue,float alpha)439 _cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer,
440 unsigned long buffers,
441 float red,
442 float green,
443 float blue,
444 float alpha)
445 {
446 CoglFramebufferPrivate *priv =
447 cogl_framebuffer_get_instance_private (framebuffer);
448
449 if (!buffers)
450 {
451 static gboolean shown = FALSE;
452
453 if (!shown)
454 {
455 g_warning ("You should specify at least one auxiliary buffer "
456 "when calling cogl_framebuffer_clear");
457 }
458
459 return;
460 }
461
462 cogl_framebuffer_driver_clear (priv->driver,
463 buffers,
464 red,
465 green,
466 blue,
467 alpha);
468 }
469
470 void
_cogl_framebuffer_mark_clear_clip_dirty(CoglFramebuffer * framebuffer)471 _cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer)
472 {
473 CoglFramebufferPrivate *priv =
474 cogl_framebuffer_get_instance_private (framebuffer);
475
476 priv->clear_clip_dirty = TRUE;
477 }
478
479 void
cogl_framebuffer_set_depth_buffer_clear_needed(CoglFramebuffer * framebuffer)480 cogl_framebuffer_set_depth_buffer_clear_needed (CoglFramebuffer *framebuffer)
481 {
482 CoglFramebufferPrivate *priv =
483 cogl_framebuffer_get_instance_private (framebuffer);
484
485 priv->depth_buffer_clear_needed = TRUE;
486 }
487
488 void
cogl_framebuffer_clear4f(CoglFramebuffer * framebuffer,unsigned long buffers,float red,float green,float blue,float alpha)489 cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer,
490 unsigned long buffers,
491 float red,
492 float green,
493 float blue,
494 float alpha)
495 {
496 CoglFramebufferPrivate *priv =
497 cogl_framebuffer_get_instance_private (framebuffer);
498 CoglContext *context = cogl_framebuffer_get_context (framebuffer);
499 CoglClipStack *clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer);
500 gboolean had_depth_and_color_buffer_bits;
501 int scissor_x0;
502 int scissor_y0;
503 int scissor_x1;
504 int scissor_y1;
505
506 had_depth_and_color_buffer_bits =
507 (buffers & COGL_BUFFER_BIT_DEPTH) &&
508 (buffers & COGL_BUFFER_BIT_COLOR);
509
510 if (!priv->depth_buffer_clear_needed &&
511 (buffers & COGL_BUFFER_BIT_DEPTH))
512 buffers &= ~(COGL_BUFFER_BIT_DEPTH);
513
514 if (buffers == 0)
515 return;
516
517 _cogl_clip_stack_get_bounds (clip_stack,
518 &scissor_x0, &scissor_y0,
519 &scissor_x1, &scissor_y1);
520
521 /* NB: the previous clear could have had an arbitrary clip.
522 * NB: everything for the last frame might still be in the journal
523 * but we can't assume anything about how each entry was
524 * clipped.
525 * NB: Clutter will scissor its pick renders which would mean all
526 * journal entries have a common ClipStack entry, but without
527 * a layering violation Cogl has to explicitly walk the journal
528 * entries to determine if this is the case.
529 * NB: We have a software only read-pixel optimization in the
530 * journal that determines the color at a given framebuffer
531 * coordinate for simple scenes without rendering with the GPU.
532 * When Clutter is hitting this fast-path we can expect to
533 * receive calls to clear the framebuffer with an un-flushed
534 * journal.
535 * NB: To fully support software based picking for Clutter we
536 * need to be able to reliably detect when the contents of a
537 * journal can be discarded and when we can skip the call to
538 * glClear because it matches the previous clear request.
539 */
540
541 /* Note: we don't check for the stencil buffer being cleared here
542 * since there isn't any public cogl api to manipulate the stencil
543 * buffer.
544 *
545 * Note: we check for an exact clip match here because
546 * 1) a smaller clip could mean existing journal entries may
547 * need to contribute to regions outside the new clear-clip
548 * 2) a larger clip would mean we need to issue a real
549 * glClear and we only care about cases avoiding a
550 * glClear.
551 *
552 * Note: Comparing without an epsilon is considered
553 * appropriate here.
554 */
555 if (had_depth_and_color_buffer_bits &&
556 !priv->clear_clip_dirty &&
557 priv->clear_color_red == red &&
558 priv->clear_color_green == green &&
559 priv->clear_color_blue == blue &&
560 priv->clear_color_alpha == alpha &&
561 scissor_x0 == priv->clear_clip_x0 &&
562 scissor_y0 == priv->clear_clip_y0 &&
563 scissor_x1 == priv->clear_clip_x1 &&
564 scissor_y1 == priv->clear_clip_y1)
565 {
566 /* NB: We only have to consider the clip state of journal
567 * entries if the current clear is clipped since otherwise we
568 * know every pixel of the framebuffer is affected by the clear
569 * and so all journal entries become redundant and can simply be
570 * discarded.
571 */
572 if (clip_stack)
573 {
574 /*
575 * Note: the function for checking the journal entries is
576 * quite strict. It avoids detailed checking of all entry
577 * clip_stacks by only checking the details of the first
578 * entry and then it only verifies that the remaining
579 * entries share the same clip_stack ancestry. This means
580 * it's possible for some false negatives here but that will
581 * just result in us falling back to a real clear.
582 */
583 if (_cogl_journal_all_entries_within_bounds (priv->journal,
584 scissor_x0, scissor_y0,
585 scissor_x1, scissor_y1))
586 {
587 _cogl_journal_discard (priv->journal);
588 goto cleared;
589 }
590 }
591 else
592 {
593 _cogl_journal_discard (priv->journal);
594 goto cleared;
595 }
596 }
597
598 COGL_NOTE (DRAW, "Clear begin");
599
600 _cogl_framebuffer_flush_journal (framebuffer);
601
602 /* NB: cogl_context_flush_framebuffer_state may disrupt various state (such
603 * as the pipeline state) when flushing the clip stack, so should
604 * always be done first when preparing to draw. */
605 cogl_context_flush_framebuffer_state (context,
606 framebuffer, framebuffer,
607 COGL_FRAMEBUFFER_STATE_ALL);
608
609 _cogl_framebuffer_clear_without_flush4f (framebuffer, buffers,
610 red, green, blue, alpha);
611
612 /* This is a debugging variable used to visually display the quad
613 * batches from the journal. It is reset here to increase the
614 * chances of getting the same colours for each frame during an
615 * animation */
616 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES)) &&
617 buffers & COGL_BUFFER_BIT_COLOR)
618 {
619 priv->context->journal_rectangles_color = 1;
620 }
621
622 COGL_NOTE (DRAW, "Clear end");
623
624 cleared:
625
626 _cogl_framebuffer_mark_clear_clip_dirty (framebuffer);
627
628 if (buffers & COGL_BUFFER_BIT_DEPTH)
629 priv->depth_buffer_clear_needed = FALSE;
630
631 if (had_depth_and_color_buffer_bits)
632 {
633 /* For our fast-path for reading back a single pixel of simple
634 * scenes where the whole frame is in the journal we need to
635 * track the cleared color of the framebuffer in case the point
636 * read doesn't intersect any of the journal rectangles. */
637 priv->clear_clip_dirty = FALSE;
638 priv->clear_color_red = red;
639 priv->clear_color_green = green;
640 priv->clear_color_blue = blue;
641 priv->clear_color_alpha = alpha;
642
643 /* NB: A clear may be scissored so we need to track the extents
644 * that the clear is applicable too... */
645 _cogl_clip_stack_get_bounds (clip_stack,
646 &priv->clear_clip_x0,
647 &priv->clear_clip_y0,
648 &priv->clear_clip_x1,
649 &priv->clear_clip_y1);
650 }
651 }
652
653 /* Note: the 'buffers' and 'color' arguments were switched around on
654 * purpose compared to the original cogl_clear API since it was odd
655 * that you would be expected to specify a color before even
656 * necessarily choosing to clear the color buffer.
657 */
658 void
cogl_framebuffer_clear(CoglFramebuffer * framebuffer,unsigned long buffers,const CoglColor * color)659 cogl_framebuffer_clear (CoglFramebuffer *framebuffer,
660 unsigned long buffers,
661 const CoglColor *color)
662 {
663 cogl_framebuffer_clear4f (framebuffer, buffers,
664 cogl_color_get_red_float (color),
665 cogl_color_get_green_float (color),
666 cogl_color_get_blue_float (color),
667 cogl_color_get_alpha_float (color));
668 }
669
670 /* We will lazily allocate framebuffers if necessary when querying
671 * their size/viewport but note we need to be careful in the case of
672 * onscreen framebuffers that are instantiated with an initial request
673 * size that we don't trigger an allocation when this is queried since
674 * that would lead to a recursion when the winsys backend queries this
675 * requested size during allocation. */
676 static void
ensure_size_initialized(CoglFramebuffer * framebuffer)677 ensure_size_initialized (CoglFramebuffer *framebuffer)
678 {
679 CoglFramebufferPrivate *priv =
680 cogl_framebuffer_get_instance_private (framebuffer);
681
682 /* In the case of offscreen framebuffers backed by a texture then
683 * until that texture has been allocated we might not know the size
684 * of the framebuffer */
685 if (priv->width < 0)
686 {
687 /* Currently we assume the size is always initialized for
688 * onscreen framebuffers. */
689 g_return_if_fail (COGL_IS_OFFSCREEN (framebuffer));
690
691 /* We also assume the size would have been initialized if the
692 * framebuffer were allocated. */
693 g_return_if_fail (!priv->allocated);
694
695 cogl_framebuffer_allocate (framebuffer, NULL);
696 }
697 }
698
699 void
cogl_framebuffer_update_size(CoglFramebuffer * framebuffer,int width,int height)700 cogl_framebuffer_update_size (CoglFramebuffer *framebuffer,
701 int width,
702 int height)
703 {
704 CoglFramebufferPrivate *priv =
705 cogl_framebuffer_get_instance_private (framebuffer);
706
707 priv->width = width;
708 priv->height = height;
709
710 cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height);
711 }
712
713 int
cogl_framebuffer_get_width(CoglFramebuffer * framebuffer)714 cogl_framebuffer_get_width (CoglFramebuffer *framebuffer)
715 {
716 CoglFramebufferPrivate *priv =
717 cogl_framebuffer_get_instance_private (framebuffer);
718
719 ensure_size_initialized (framebuffer);
720 return priv->width;
721 }
722
723 int
cogl_framebuffer_get_height(CoglFramebuffer * framebuffer)724 cogl_framebuffer_get_height (CoglFramebuffer *framebuffer)
725 {
726 CoglFramebufferPrivate *priv =
727 cogl_framebuffer_get_instance_private (framebuffer);
728
729 ensure_size_initialized (framebuffer);
730 return priv->height;
731 }
732
733 CoglClipStack *
_cogl_framebuffer_get_clip_stack(CoglFramebuffer * framebuffer)734 _cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer)
735 {
736 CoglFramebufferPrivate *priv =
737 cogl_framebuffer_get_instance_private (framebuffer);
738
739 return priv->clip_stack;
740 }
741
742 void
cogl_framebuffer_set_viewport4fv(CoglFramebuffer * framebuffer,float * viewport)743 cogl_framebuffer_set_viewport4fv (CoglFramebuffer *framebuffer,
744 float *viewport)
745 {
746 CoglFramebufferPrivate *priv =
747 cogl_framebuffer_get_instance_private (framebuffer);
748
749 if (priv->viewport_x == viewport[0] &&
750 priv->viewport_y == viewport[1] &&
751 priv->viewport_width == viewport[2] &&
752 priv->viewport_height == viewport[3])
753 return;
754
755 priv->viewport_x = viewport[0];
756 priv->viewport_y = viewport[1];
757 priv->viewport_width = viewport[2];
758 priv->viewport_height = viewport[3];
759 priv->viewport_age++;
760 }
761
762 void
cogl_framebuffer_set_viewport(CoglFramebuffer * framebuffer,float x,float y,float width,float height)763 cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer,
764 float x,
765 float y,
766 float width,
767 float height)
768 {
769 CoglFramebufferPrivate *priv =
770 cogl_framebuffer_get_instance_private (framebuffer);
771
772 g_return_if_fail (width > 0 && height > 0);
773
774 if (priv->viewport_x == x &&
775 priv->viewport_y == y &&
776 priv->viewport_width == width &&
777 priv->viewport_height == height)
778 return;
779
780 priv->viewport_x = x;
781 priv->viewport_y = y;
782 priv->viewport_width = width;
783 priv->viewport_height = height;
784 }
785
786 float
cogl_framebuffer_get_viewport_x(CoglFramebuffer * framebuffer)787 cogl_framebuffer_get_viewport_x (CoglFramebuffer *framebuffer)
788 {
789 CoglFramebufferPrivate *priv =
790 cogl_framebuffer_get_instance_private (framebuffer);
791
792 return priv->viewport_x;
793 }
794
795 float
cogl_framebuffer_get_viewport_y(CoglFramebuffer * framebuffer)796 cogl_framebuffer_get_viewport_y (CoglFramebuffer *framebuffer)
797 {
798 CoglFramebufferPrivate *priv =
799 cogl_framebuffer_get_instance_private (framebuffer);
800
801 return priv->viewport_y;
802 }
803
804 float
cogl_framebuffer_get_viewport_width(CoglFramebuffer * framebuffer)805 cogl_framebuffer_get_viewport_width (CoglFramebuffer *framebuffer)
806 {
807 CoglFramebufferPrivate *priv =
808 cogl_framebuffer_get_instance_private (framebuffer);
809
810 ensure_size_initialized (framebuffer);
811 return priv->viewport_width;
812 }
813
814 float
cogl_framebuffer_get_viewport_height(CoglFramebuffer * framebuffer)815 cogl_framebuffer_get_viewport_height (CoglFramebuffer *framebuffer)
816 {
817 CoglFramebufferPrivate *priv =
818 cogl_framebuffer_get_instance_private (framebuffer);
819
820 ensure_size_initialized (framebuffer);
821 return priv->viewport_height;
822 }
823
824 void
cogl_framebuffer_get_viewport4f(CoglFramebuffer * framebuffer,float * viewport_x,float * viewport_y,float * viewport_width,float * viewport_height)825 cogl_framebuffer_get_viewport4f (CoglFramebuffer *framebuffer,
826 float *viewport_x,
827 float *viewport_y,
828 float *viewport_width,
829 float *viewport_height)
830 {
831 CoglFramebufferPrivate *priv =
832 cogl_framebuffer_get_instance_private (framebuffer);
833
834 ensure_size_initialized (framebuffer);
835
836 *viewport_x = priv->viewport_x;
837 *viewport_y = priv->viewport_y;
838 *viewport_width = priv->viewport_width;
839 *viewport_height = priv->viewport_height;
840 }
841
842 void
cogl_framebuffer_get_viewport4fv(CoglFramebuffer * framebuffer,float * viewport)843 cogl_framebuffer_get_viewport4fv (CoglFramebuffer *framebuffer,
844 float *viewport)
845 {
846 cogl_framebuffer_get_viewport4f (framebuffer,
847 &viewport[0],
848 &viewport[1],
849 &viewport[2],
850 &viewport[3]);
851 }
852
853 CoglMatrixStack *
_cogl_framebuffer_get_modelview_stack(CoglFramebuffer * framebuffer)854 _cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer)
855 {
856 CoglFramebufferPrivate *priv =
857 cogl_framebuffer_get_instance_private (framebuffer);
858
859 return priv->modelview_stack;
860 }
861
862 CoglMatrixStack *
_cogl_framebuffer_get_projection_stack(CoglFramebuffer * framebuffer)863 _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer)
864 {
865 CoglFramebufferPrivate *priv =
866 cogl_framebuffer_get_instance_private (framebuffer);
867
868 return priv->projection_stack;
869 }
870
871 void
_cogl_framebuffer_add_dependency(CoglFramebuffer * framebuffer,CoglFramebuffer * dependency)872 _cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer,
873 CoglFramebuffer *dependency)
874 {
875 CoglFramebufferPrivate *priv =
876 cogl_framebuffer_get_instance_private (framebuffer);
877 GList *l;
878
879 for (l = priv->deps; l; l = l->next)
880 {
881 CoglFramebuffer *existing_dep = l->data;
882 if (existing_dep == dependency)
883 return;
884 }
885
886 /* TODO: generalize the primed-array type structure we e.g. use for
887 * cogl_object_set_user_data or for pipeline children as a way to
888 * avoid quite a lot of mid-scene micro allocations here... */
889 priv->deps =
890 g_list_prepend (priv->deps, g_object_ref (dependency));
891 }
892
893 void
_cogl_framebuffer_flush_journal(CoglFramebuffer * framebuffer)894 _cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer)
895 {
896 CoglFramebufferPrivate *priv =
897 cogl_framebuffer_get_instance_private (framebuffer);
898
899 _cogl_journal_flush (priv->journal);
900 }
901
902 void
_cogl_framebuffer_flush_dependency_journals(CoglFramebuffer * framebuffer)903 _cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer)
904 {
905 CoglFramebufferPrivate *priv =
906 cogl_framebuffer_get_instance_private (framebuffer);
907
908 g_list_foreach (priv->deps, (GFunc) _cogl_framebuffer_flush_journal, NULL);
909 g_list_free_full (priv->deps, g_object_unref);
910 priv->deps = NULL;
911 }
912
913 gboolean
cogl_framebuffer_is_allocated(CoglFramebuffer * framebuffer)914 cogl_framebuffer_is_allocated (CoglFramebuffer *framebuffer)
915 {
916 CoglFramebufferPrivate *priv =
917 cogl_framebuffer_get_instance_private (framebuffer);
918
919 return priv->allocated;
920 }
921
922 static gboolean
cogl_framebuffer_init_driver(CoglFramebuffer * framebuffer,GError ** error)923 cogl_framebuffer_init_driver (CoglFramebuffer *framebuffer,
924 GError **error)
925
926 {
927 CoglFramebufferPrivate *priv =
928 cogl_framebuffer_get_instance_private (framebuffer);
929 const CoglDriverVtable *driver_vtable = priv->context->driver_vtable;
930 CoglFramebufferDriver *driver;
931
932 driver = driver_vtable->create_framebuffer_driver (priv->context,
933 framebuffer,
934 &priv->driver_config,
935 error);
936 if (!driver)
937 return FALSE;
938
939 priv->driver = driver;
940 return TRUE;
941 }
942
943 gboolean
cogl_framebuffer_allocate(CoglFramebuffer * framebuffer,GError ** error)944 cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
945 GError **error)
946 {
947 CoglFramebufferPrivate *priv =
948 cogl_framebuffer_get_instance_private (framebuffer);
949 CoglFramebufferClass *klass = COGL_FRAMEBUFFER_GET_CLASS (framebuffer);
950
951 if (priv->allocated)
952 return TRUE;
953
954 if (!klass->allocate (framebuffer, error))
955 return FALSE;
956
957 if (!cogl_framebuffer_init_driver (framebuffer, error))
958 return FALSE;
959
960 priv->allocated = TRUE;
961
962 return TRUE;
963 }
964
965 static unsigned long
_cogl_framebuffer_compare_viewport_state(CoglFramebuffer * a,CoglFramebuffer * b)966 _cogl_framebuffer_compare_viewport_state (CoglFramebuffer *a,
967 CoglFramebuffer *b)
968 {
969 CoglFramebufferPrivate *priv_a = cogl_framebuffer_get_instance_private (a);
970 CoglFramebufferPrivate *priv_b = cogl_framebuffer_get_instance_private (b);
971
972 if (priv_a->viewport_x != priv_b->viewport_x ||
973 priv_a->viewport_y != priv_b->viewport_y ||
974 priv_a->viewport_width != priv_b->viewport_width ||
975 priv_a->viewport_height != priv_b->viewport_height ||
976 /* NB: we render upside down to offscreen framebuffers and that
977 * can affect how we setup the GL viewport... */
978 G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
979 return COGL_FRAMEBUFFER_STATE_VIEWPORT;
980 else
981 return 0;
982 }
983
984 static unsigned long
_cogl_framebuffer_compare_clip_state(CoglFramebuffer * a,CoglFramebuffer * b)985 _cogl_framebuffer_compare_clip_state (CoglFramebuffer *a,
986 CoglFramebuffer *b)
987 {
988 CoglFramebufferPrivate *priv_a = cogl_framebuffer_get_instance_private (a);
989 CoglFramebufferPrivate *priv_b = cogl_framebuffer_get_instance_private (b);
990
991 if (priv_a->clip_stack != priv_b->clip_stack)
992 return COGL_FRAMEBUFFER_STATE_CLIP;
993 else
994 return 0;
995 }
996
997 static unsigned long
_cogl_framebuffer_compare_dither_state(CoglFramebuffer * a,CoglFramebuffer * b)998 _cogl_framebuffer_compare_dither_state (CoglFramebuffer *a,
999 CoglFramebuffer *b)
1000 {
1001 CoglFramebufferPrivate *priv_a = cogl_framebuffer_get_instance_private (a);
1002 CoglFramebufferPrivate *priv_b = cogl_framebuffer_get_instance_private (b);
1003
1004 return priv_a->dither_enabled != priv_b->dither_enabled ?
1005 COGL_FRAMEBUFFER_STATE_DITHER : 0;
1006 }
1007
1008 static unsigned long
_cogl_framebuffer_compare_modelview_state(CoglFramebuffer * a,CoglFramebuffer * b)1009 _cogl_framebuffer_compare_modelview_state (CoglFramebuffer *a,
1010 CoglFramebuffer *b)
1011 {
1012 /* We always want to flush the modelview state. All this does is set
1013 the current modelview stack on the context to the framebuffer's
1014 stack. */
1015 return COGL_FRAMEBUFFER_STATE_MODELVIEW;
1016 }
1017
1018 static unsigned long
_cogl_framebuffer_compare_projection_state(CoglFramebuffer * a,CoglFramebuffer * b)1019 _cogl_framebuffer_compare_projection_state (CoglFramebuffer *a,
1020 CoglFramebuffer *b)
1021 {
1022 /* We always want to flush the projection state. All this does is
1023 set the current projection stack on the context to the
1024 framebuffer's stack. */
1025 return COGL_FRAMEBUFFER_STATE_PROJECTION;
1026 }
1027
1028 static unsigned long
_cogl_framebuffer_compare_front_face_winding_state(CoglFramebuffer * a,CoglFramebuffer * b)1029 _cogl_framebuffer_compare_front_face_winding_state (CoglFramebuffer *a,
1030 CoglFramebuffer *b)
1031 {
1032 if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
1033 return COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING;
1034 else
1035 return 0;
1036 }
1037
1038 static unsigned long
_cogl_framebuffer_compare_depth_write_state(CoglFramebuffer * a,CoglFramebuffer * b)1039 _cogl_framebuffer_compare_depth_write_state (CoglFramebuffer *a,
1040 CoglFramebuffer *b)
1041 {
1042 CoglFramebufferPrivate *priv_a = cogl_framebuffer_get_instance_private (a);
1043 CoglFramebufferPrivate *priv_b = cogl_framebuffer_get_instance_private (b);
1044
1045 return priv_a->depth_writing_enabled != priv_b->depth_writing_enabled ?
1046 COGL_FRAMEBUFFER_STATE_DEPTH_WRITE : 0;
1047 }
1048
1049 static unsigned long
_cogl_framebuffer_compare_stereo_mode(CoglFramebuffer * a,CoglFramebuffer * b)1050 _cogl_framebuffer_compare_stereo_mode (CoglFramebuffer *a,
1051 CoglFramebuffer *b)
1052 {
1053 CoglFramebufferPrivate *priv_a = cogl_framebuffer_get_instance_private (a);
1054 CoglFramebufferPrivate *priv_b = cogl_framebuffer_get_instance_private (b);
1055
1056 return priv_a->stereo_mode != priv_b->stereo_mode ?
1057 COGL_FRAMEBUFFER_STATE_STEREO_MODE : 0;
1058 }
1059
1060 unsigned long
_cogl_framebuffer_compare(CoglFramebuffer * a,CoglFramebuffer * b,unsigned long state)1061 _cogl_framebuffer_compare (CoglFramebuffer *a,
1062 CoglFramebuffer *b,
1063 unsigned long state)
1064 {
1065 unsigned long differences = 0;
1066 int bit;
1067
1068 if (state & COGL_FRAMEBUFFER_STATE_BIND)
1069 {
1070 differences |= COGL_FRAMEBUFFER_STATE_BIND;
1071 state &= ~COGL_FRAMEBUFFER_STATE_BIND;
1072 }
1073
1074 COGL_FLAGS_FOREACH_START (&state, 1, bit)
1075 {
1076 /* XXX: We considered having an array of callbacks for each state index
1077 * that we'd call here but decided that this way the compiler is more
1078 * likely going to be able to in-line the comparison functions and use
1079 * the index to jump straight to the required code. */
1080 switch (bit)
1081 {
1082 case COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT:
1083 differences |=
1084 _cogl_framebuffer_compare_viewport_state (a, b);
1085 break;
1086 case COGL_FRAMEBUFFER_STATE_INDEX_CLIP:
1087 differences |= _cogl_framebuffer_compare_clip_state (a, b);
1088 break;
1089 case COGL_FRAMEBUFFER_STATE_INDEX_DITHER:
1090 differences |= _cogl_framebuffer_compare_dither_state (a, b);
1091 break;
1092 case COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW:
1093 differences |=
1094 _cogl_framebuffer_compare_modelview_state (a, b);
1095 break;
1096 case COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION:
1097 differences |=
1098 _cogl_framebuffer_compare_projection_state (a, b);
1099 break;
1100 case COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING:
1101 differences |=
1102 _cogl_framebuffer_compare_front_face_winding_state (a, b);
1103 break;
1104 case COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE:
1105 differences |=
1106 _cogl_framebuffer_compare_depth_write_state (a, b);
1107 break;
1108 case COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE:
1109 differences |=
1110 _cogl_framebuffer_compare_stereo_mode (a, b);
1111 break;
1112 default:
1113 g_warn_if_reached ();
1114 }
1115 }
1116 COGL_FLAGS_FOREACH_END;
1117
1118 return differences;
1119 }
1120
1121 void
cogl_context_flush_framebuffer_state(CoglContext * ctx,CoglFramebuffer * draw_buffer,CoglFramebuffer * read_buffer,CoglFramebufferState state)1122 cogl_context_flush_framebuffer_state (CoglContext *ctx,
1123 CoglFramebuffer *draw_buffer,
1124 CoglFramebuffer *read_buffer,
1125 CoglFramebufferState state)
1126 {
1127 ctx->driver_vtable->flush_framebuffer_state (ctx,
1128 draw_buffer,
1129 read_buffer,
1130 state);
1131 }
1132
1133 static void
cogl_framebuffer_query_bits(CoglFramebuffer * framebuffer,CoglFramebufferBits * bits)1134 cogl_framebuffer_query_bits (CoglFramebuffer *framebuffer,
1135 CoglFramebufferBits *bits)
1136 {
1137 CoglFramebufferPrivate *priv =
1138 cogl_framebuffer_get_instance_private (framebuffer);
1139
1140 g_return_if_fail (priv->driver);
1141
1142 cogl_framebuffer_driver_query_bits (priv->driver, bits);
1143 }
1144
1145 int
cogl_framebuffer_get_red_bits(CoglFramebuffer * framebuffer)1146 cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer)
1147 {
1148 CoglFramebufferBits bits;
1149
1150 cogl_framebuffer_query_bits (framebuffer, &bits);
1151
1152 return bits.red;
1153 }
1154
1155 int
cogl_framebuffer_get_green_bits(CoglFramebuffer * framebuffer)1156 cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer)
1157 {
1158 CoglFramebufferBits bits;
1159
1160 cogl_framebuffer_query_bits (framebuffer, &bits);
1161
1162 return bits.green;
1163 }
1164
1165 int
cogl_framebuffer_get_blue_bits(CoglFramebuffer * framebuffer)1166 cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer)
1167 {
1168 CoglFramebufferBits bits;
1169
1170 cogl_framebuffer_query_bits (framebuffer, &bits);
1171
1172 return bits.blue;
1173 }
1174
1175 int
cogl_framebuffer_get_alpha_bits(CoglFramebuffer * framebuffer)1176 cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer)
1177 {
1178 CoglFramebufferBits bits;
1179
1180 cogl_framebuffer_query_bits (framebuffer, &bits);
1181
1182 return bits.alpha;
1183 }
1184
1185 int
cogl_framebuffer_get_depth_bits(CoglFramebuffer * framebuffer)1186 cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer)
1187 {
1188 CoglFramebufferBits bits;
1189
1190 cogl_framebuffer_query_bits (framebuffer, &bits);
1191
1192 return bits.depth;
1193 }
1194
1195 int
_cogl_framebuffer_get_stencil_bits(CoglFramebuffer * framebuffer)1196 _cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer)
1197 {
1198 CoglFramebufferBits bits;
1199
1200 cogl_framebuffer_query_bits (framebuffer, &bits);
1201
1202 return bits.stencil;
1203 }
1204
1205 gboolean
cogl_framebuffer_get_is_stereo(CoglFramebuffer * framebuffer)1206 cogl_framebuffer_get_is_stereo (CoglFramebuffer *framebuffer)
1207 {
1208 CoglFramebufferPrivate *priv =
1209 cogl_framebuffer_get_instance_private (framebuffer);
1210
1211 return priv->config.stereo_enabled;
1212 }
1213
1214 CoglStereoMode
cogl_framebuffer_get_stereo_mode(CoglFramebuffer * framebuffer)1215 cogl_framebuffer_get_stereo_mode (CoglFramebuffer *framebuffer)
1216 {
1217 CoglFramebufferPrivate *priv =
1218 cogl_framebuffer_get_instance_private (framebuffer);
1219
1220 return priv->stereo_mode;
1221 }
1222
1223 void
cogl_framebuffer_set_stereo_mode(CoglFramebuffer * framebuffer,CoglStereoMode stereo_mode)1224 cogl_framebuffer_set_stereo_mode (CoglFramebuffer *framebuffer,
1225 CoglStereoMode stereo_mode)
1226 {
1227 CoglFramebufferPrivate *priv =
1228 cogl_framebuffer_get_instance_private (framebuffer);
1229
1230 if (priv->stereo_mode == stereo_mode)
1231 return;
1232
1233 /* Stereo mode changes don't go through the journal */
1234 _cogl_framebuffer_flush_journal (framebuffer);
1235
1236 priv->stereo_mode = stereo_mode;
1237
1238 if (priv->context->current_draw_buffer == framebuffer)
1239 priv->context->current_draw_buffer_changes |=
1240 COGL_FRAMEBUFFER_STATE_STEREO_MODE;
1241 }
1242
1243 gboolean
cogl_framebuffer_get_depth_write_enabled(CoglFramebuffer * framebuffer)1244 cogl_framebuffer_get_depth_write_enabled (CoglFramebuffer *framebuffer)
1245 {
1246 CoglFramebufferPrivate *priv =
1247 cogl_framebuffer_get_instance_private (framebuffer);
1248
1249 return priv->depth_writing_enabled;
1250 }
1251
1252 void
cogl_framebuffer_set_depth_write_enabled(CoglFramebuffer * framebuffer,gboolean depth_write_enabled)1253 cogl_framebuffer_set_depth_write_enabled (CoglFramebuffer *framebuffer,
1254 gboolean depth_write_enabled)
1255 {
1256 CoglFramebufferPrivate *priv =
1257 cogl_framebuffer_get_instance_private (framebuffer);
1258
1259 if (priv->depth_writing_enabled == depth_write_enabled)
1260 return;
1261
1262 /* XXX: Currently depth write changes don't go through the journal */
1263 _cogl_framebuffer_flush_journal (framebuffer);
1264
1265 priv->depth_writing_enabled = depth_write_enabled;
1266
1267 if (priv->context->current_draw_buffer == framebuffer)
1268 priv->context->current_draw_buffer_changes |=
1269 COGL_FRAMEBUFFER_STATE_DEPTH_WRITE;
1270 }
1271
1272 gboolean
cogl_framebuffer_get_dither_enabled(CoglFramebuffer * framebuffer)1273 cogl_framebuffer_get_dither_enabled (CoglFramebuffer *framebuffer)
1274 {
1275 CoglFramebufferPrivate *priv =
1276 cogl_framebuffer_get_instance_private (framebuffer);
1277
1278 return priv->dither_enabled;
1279 }
1280
1281 void
cogl_framebuffer_set_dither_enabled(CoglFramebuffer * framebuffer,gboolean dither_enabled)1282 cogl_framebuffer_set_dither_enabled (CoglFramebuffer *framebuffer,
1283 gboolean dither_enabled)
1284 {
1285 CoglFramebufferPrivate *priv =
1286 cogl_framebuffer_get_instance_private (framebuffer);
1287
1288 if (priv->dither_enabled == dither_enabled)
1289 return;
1290
1291 priv->dither_enabled = dither_enabled;
1292 }
1293
1294 int
cogl_framebuffer_get_samples_per_pixel(CoglFramebuffer * framebuffer)1295 cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer)
1296 {
1297 CoglFramebufferPrivate *priv =
1298 cogl_framebuffer_get_instance_private (framebuffer);
1299
1300 if (priv->allocated)
1301 return priv->samples_per_pixel;
1302 else
1303 return priv->config.samples_per_pixel;
1304 }
1305
1306 void
cogl_framebuffer_set_samples_per_pixel(CoglFramebuffer * framebuffer,int samples_per_pixel)1307 cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer,
1308 int samples_per_pixel)
1309 {
1310 CoglFramebufferPrivate *priv =
1311 cogl_framebuffer_get_instance_private (framebuffer);
1312
1313 g_return_if_fail (!priv->allocated);
1314
1315 priv->config.samples_per_pixel = samples_per_pixel;
1316 }
1317
1318 void
cogl_framebuffer_update_samples_per_pixel(CoglFramebuffer * framebuffer,int samples_per_pixel)1319 cogl_framebuffer_update_samples_per_pixel (CoglFramebuffer *framebuffer,
1320 int samples_per_pixel)
1321 {
1322 CoglFramebufferPrivate *priv =
1323 cogl_framebuffer_get_instance_private (framebuffer);
1324
1325 priv->samples_per_pixel = samples_per_pixel;
1326 }
1327
1328 void
cogl_framebuffer_resolve_samples(CoglFramebuffer * framebuffer)1329 cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer)
1330 {
1331 CoglFramebufferPrivate *priv =
1332 cogl_framebuffer_get_instance_private (framebuffer);
1333
1334 cogl_framebuffer_resolve_samples_region (framebuffer,
1335 0, 0,
1336 priv->width,
1337 priv->height);
1338
1339 /* TODO: Make this happen implicitly when the resolve texture next gets used
1340 * as a source, either via cogl_texture_get_data(), via cogl_read_pixels() or
1341 * if used as a source for rendering. We would also implicitly resolve if
1342 * necessary before freeing a CoglFramebuffer.
1343 *
1344 * This API should still be kept but it is optional, only necessary
1345 * if the user wants to explicitly control when the resolve happens e.g.
1346 * to ensure it's done in advance of it being used as a source.
1347 *
1348 * Every texture should have a CoglFramebuffer *needs_resolve member
1349 * internally. When the texture gets validated before being used as a source
1350 * we should first check the needs_resolve pointer and if set we'll
1351 * automatically call cogl_framebuffer_resolve_samples ().
1352 *
1353 * Calling cogl_framebuffer_resolve_samples() or
1354 * cogl_framebuffer_resolve_samples_region() should reset the textures
1355 * needs_resolve pointer to NULL.
1356 *
1357 * Rendering anything to a framebuffer will cause the corresponding
1358 * texture's ->needs_resolve pointer to be set.
1359 *
1360 * XXX: Note: we only need to address this TODO item when adding support for
1361 * EXT_framebuffer_multisample because currently we only support hardware
1362 * that resolves implicitly anyway.
1363 */
1364 }
1365
1366 void
cogl_framebuffer_resolve_samples_region(CoglFramebuffer * framebuffer,int x,int y,int width,int height)1367 cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer,
1368 int x,
1369 int y,
1370 int width,
1371 int height)
1372 {
1373 /* NOP for now since we don't support EXT_framebuffer_multisample yet which
1374 * requires an explicit resolve. */
1375 }
1376
1377 CoglContext *
cogl_framebuffer_get_context(CoglFramebuffer * framebuffer)1378 cogl_framebuffer_get_context (CoglFramebuffer *framebuffer)
1379 {
1380 CoglFramebufferPrivate *priv =
1381 cogl_framebuffer_get_instance_private (framebuffer);
1382
1383 g_return_val_if_fail (framebuffer != NULL, NULL);
1384
1385 return priv->context;
1386 }
1387
1388 CoglJournal *
cogl_framebuffer_get_journal(CoglFramebuffer * framebuffer)1389 cogl_framebuffer_get_journal (CoglFramebuffer *framebuffer)
1390 {
1391 CoglFramebufferPrivate *priv =
1392 cogl_framebuffer_get_instance_private (framebuffer);
1393
1394 return priv->journal;
1395 }
1396
1397 static gboolean
_cogl_framebuffer_try_fast_read_pixel(CoglFramebuffer * framebuffer,int x,int y,CoglReadPixelsFlags source,CoglBitmap * bitmap)1398 _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
1399 int x,
1400 int y,
1401 CoglReadPixelsFlags source,
1402 CoglBitmap *bitmap)
1403 {
1404 CoglFramebufferPrivate *priv =
1405 cogl_framebuffer_get_instance_private (framebuffer);
1406 gboolean found_intersection;
1407 CoglPixelFormat format;
1408
1409 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FAST_READ_PIXEL)))
1410 return FALSE;
1411
1412 if (source != COGL_READ_PIXELS_COLOR_BUFFER)
1413 return FALSE;
1414
1415 format = cogl_bitmap_get_format (bitmap);
1416
1417 if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE &&
1418 format != COGL_PIXEL_FORMAT_RGBA_8888)
1419 return FALSE;
1420
1421 if (!_cogl_journal_try_read_pixel (priv->journal,
1422 x, y, bitmap,
1423 &found_intersection))
1424 return FALSE;
1425
1426 /* If we can't determine the color from the primitives in the
1427 * journal then see if we can use the last recorded clear color
1428 */
1429
1430 /* If _cogl_journal_try_read_pixel() failed even though there was an
1431 * intersection of the given point with a primitive in the journal
1432 * then we can't fallback to the framebuffer's last clear color...
1433 * */
1434 if (found_intersection)
1435 return TRUE;
1436
1437 /* If the framebuffer has been rendered too since it was last
1438 * cleared then we can't return the last known clear color. */
1439 if (priv->clear_clip_dirty)
1440 return FALSE;
1441
1442 if (x >= priv->clear_clip_x0 &&
1443 x < priv->clear_clip_x1 &&
1444 y >= priv->clear_clip_y0 &&
1445 y < priv->clear_clip_y1)
1446 {
1447 uint8_t *pixel;
1448 GError *ignore_error = NULL;
1449
1450 /* we currently only care about cases where the premultiplied or
1451 * unpremultipled colors are equivalent... */
1452 if (priv->clear_color_alpha != 1.0)
1453 return FALSE;
1454
1455 pixel = _cogl_bitmap_map (bitmap,
1456 COGL_BUFFER_ACCESS_WRITE,
1457 COGL_BUFFER_MAP_HINT_DISCARD,
1458 &ignore_error);
1459 if (pixel == NULL)
1460 {
1461 g_error_free (ignore_error);
1462 return FALSE;
1463 }
1464
1465 pixel[0] = priv->clear_color_red * 255.0;
1466 pixel[1] = priv->clear_color_green * 255.0;
1467 pixel[2] = priv->clear_color_blue * 255.0;
1468 pixel[3] = priv->clear_color_alpha * 255.0;
1469
1470 _cogl_bitmap_unmap (bitmap);
1471
1472 return TRUE;
1473 }
1474
1475 return FALSE;
1476 }
1477
1478 gboolean
_cogl_framebuffer_read_pixels_into_bitmap(CoglFramebuffer * framebuffer,int x,int y,CoglReadPixelsFlags source,CoglBitmap * bitmap,GError ** error)1479 _cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
1480 int x,
1481 int y,
1482 CoglReadPixelsFlags source,
1483 CoglBitmap *bitmap,
1484 GError **error)
1485 {
1486 CoglFramebufferPrivate *priv =
1487 cogl_framebuffer_get_instance_private (framebuffer);
1488 int width;
1489 int height;
1490
1491 g_return_val_if_fail (source & COGL_READ_PIXELS_COLOR_BUFFER, FALSE);
1492 g_return_val_if_fail (cogl_is_framebuffer (framebuffer), FALSE);
1493
1494 if (!cogl_framebuffer_allocate (framebuffer, error))
1495 return FALSE;
1496
1497 width = cogl_bitmap_get_width (bitmap);
1498 height = cogl_bitmap_get_height (bitmap);
1499
1500 if (width == 1 && height == 1 && !priv->clear_clip_dirty)
1501 {
1502 /* If everything drawn so far for this frame is still in the
1503 * Journal then if all of the rectangles only have a flat
1504 * opaque color we have a fast-path for reading a single pixel
1505 * that avoids the relatively high cost of flushing primitives
1506 * to be drawn on the GPU (considering how simple the geometry
1507 * is in this case) and then blocking on the long GPU pipelines
1508 * for the result.
1509 */
1510 if (_cogl_framebuffer_try_fast_read_pixel (framebuffer,
1511 x, y, source, bitmap))
1512 return TRUE;
1513 }
1514
1515 /* make sure any batched primitives get emitted to the driver
1516 * before issuing our read pixels...
1517 */
1518 _cogl_framebuffer_flush_journal (framebuffer);
1519
1520 return cogl_framebuffer_driver_read_pixels_into_bitmap (priv->driver,
1521 x, y,
1522 source,
1523 bitmap,
1524 error);
1525 }
1526
1527 gboolean
cogl_framebuffer_read_pixels_into_bitmap(CoglFramebuffer * framebuffer,int x,int y,CoglReadPixelsFlags source,CoglBitmap * bitmap)1528 cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
1529 int x,
1530 int y,
1531 CoglReadPixelsFlags source,
1532 CoglBitmap *bitmap)
1533 {
1534 GError *ignore_error = NULL;
1535 gboolean status =
1536 _cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
1537 x, y, source, bitmap,
1538 &ignore_error);
1539 g_clear_error (&ignore_error);
1540 return status;
1541 }
1542
1543 gboolean
cogl_framebuffer_read_pixels(CoglFramebuffer * framebuffer,int x,int y,int width,int height,CoglPixelFormat format,uint8_t * pixels)1544 cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer,
1545 int x,
1546 int y,
1547 int width,
1548 int height,
1549 CoglPixelFormat format,
1550 uint8_t *pixels)
1551 {
1552 CoglFramebufferPrivate *priv =
1553 cogl_framebuffer_get_instance_private (framebuffer);
1554 int bpp;
1555 CoglBitmap *bitmap;
1556 gboolean ret;
1557
1558 g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE);
1559
1560 bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0);
1561 bitmap = cogl_bitmap_new_for_data (priv->context,
1562 width, height,
1563 format,
1564 bpp * width, /* rowstride */
1565 pixels);
1566
1567 /* Note: we don't try and catch errors here since we created the
1568 * bitmap storage up-front and can assume we won't hit an
1569 * out-of-memory error which should be the only exception
1570 * this api throws.
1571 */
1572 ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
1573 x, y,
1574 COGL_READ_PIXELS_COLOR_BUFFER,
1575 bitmap,
1576 NULL);
1577 cogl_object_unref (bitmap);
1578
1579 return ret;
1580 }
1581
1582 gboolean
cogl_framebuffer_is_y_flipped(CoglFramebuffer * framebuffer)1583 cogl_framebuffer_is_y_flipped (CoglFramebuffer *framebuffer)
1584 {
1585 return COGL_FRAMEBUFFER_GET_CLASS (framebuffer)->is_y_flipped (framebuffer);
1586 }
1587
1588 gboolean
cogl_blit_framebuffer(CoglFramebuffer * framebuffer,CoglFramebuffer * dst,int src_x,int src_y,int dst_x,int dst_y,int width,int height,GError ** error)1589 cogl_blit_framebuffer (CoglFramebuffer *framebuffer,
1590 CoglFramebuffer *dst,
1591 int src_x,
1592 int src_y,
1593 int dst_x,
1594 int dst_y,
1595 int width,
1596 int height,
1597 GError **error)
1598 {
1599 CoglFramebufferPrivate *priv =
1600 cogl_framebuffer_get_instance_private (framebuffer);
1601 CoglFramebufferPrivate *dst_priv =
1602 cogl_framebuffer_get_instance_private (dst);
1603 CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
1604 int src_x1, src_y1, src_x2, src_y2;
1605 int dst_x1, dst_y1, dst_x2, dst_y2;
1606
1607 if (!cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER))
1608 {
1609 g_set_error_literal (error, COGL_SYSTEM_ERROR,
1610 COGL_SYSTEM_ERROR_UNSUPPORTED,
1611 "Cogl BLIT_FRAMEBUFFER is not supported by the system.");
1612 return FALSE;
1613 }
1614
1615 /* The buffers must use the same premult convention */
1616 if ((priv->internal_format & COGL_PREMULT_BIT) !=
1617 (dst_priv->internal_format & COGL_PREMULT_BIT))
1618 {
1619 g_set_error_literal (error, COGL_SYSTEM_ERROR,
1620 COGL_SYSTEM_ERROR_UNSUPPORTED,
1621 "cogl_blit_framebuffer premult mismatch.");
1622 return FALSE;
1623 }
1624
1625 /* Make sure any batched primitives get submitted to the driver
1626 * before blitting
1627 */
1628 _cogl_framebuffer_flush_journal (framebuffer);
1629
1630 /* Make sure the current framebuffers are bound. We explicitly avoid
1631 flushing the clip state so we can bind our own empty state */
1632 cogl_context_flush_framebuffer_state (ctx,
1633 dst,
1634 framebuffer,
1635 (COGL_FRAMEBUFFER_STATE_ALL &
1636 ~COGL_FRAMEBUFFER_STATE_CLIP));
1637
1638 /* Flush any empty clip stack because glBlitFramebuffer is affected
1639 by the scissor and we want to hide this feature for the Cogl API
1640 because it's not obvious to an app how the clip state will affect
1641 the scissor */
1642 _cogl_clip_stack_flush (NULL, dst);
1643
1644 /* XXX: Because we are manually flushing clip state here we need to
1645 * make sure that the clip state gets updated the next time we flush
1646 * framebuffer state by marking the current framebuffer's clip state
1647 * as changed */
1648 ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP;
1649
1650 /* Offscreens we do the normal way, onscreens need an y-flip. Even if
1651 * we consider offscreens to be rendered upside-down, the offscreen
1652 * orientation is in this function's API. */
1653 if (cogl_framebuffer_is_y_flipped (framebuffer))
1654 {
1655 src_x1 = src_x;
1656 src_y1 = src_y;
1657 src_x2 = src_x + width;
1658 src_y2 = src_y + height;
1659 }
1660 else
1661 {
1662 src_x1 = src_x;
1663 src_y1 = cogl_framebuffer_get_height (framebuffer) - src_y;
1664 src_x2 = src_x + width;
1665 src_y2 = src_y1 - height;
1666 }
1667
1668 if (cogl_framebuffer_is_y_flipped (dst))
1669 {
1670 dst_x1 = dst_x;
1671 dst_y1 = dst_y;
1672 dst_x2 = dst_x + width;
1673 dst_y2 = dst_y + height;
1674 }
1675 else
1676 {
1677 dst_x1 = dst_x;
1678 dst_y1 = cogl_framebuffer_get_height (dst) - dst_y;
1679 dst_x2 = dst_x + width;
1680 dst_y2 = dst_y1 - height;
1681 }
1682
1683 ctx->glBlitFramebuffer (src_x1, src_y1, src_x2, src_y2,
1684 dst_x1, dst_y1, dst_x2, dst_y2,
1685 GL_COLOR_BUFFER_BIT,
1686 GL_NEAREST);
1687
1688 return TRUE;
1689 }
1690
1691 void
cogl_framebuffer_discard_buffers(CoglFramebuffer * framebuffer,unsigned long buffers)1692 cogl_framebuffer_discard_buffers (CoglFramebuffer *framebuffer,
1693 unsigned long buffers)
1694 {
1695 CoglFramebufferPrivate *priv =
1696 cogl_framebuffer_get_instance_private (framebuffer);
1697
1698 g_return_if_fail (buffers & COGL_BUFFER_BIT_COLOR);
1699
1700 cogl_framebuffer_driver_discard_buffers (priv->driver, buffers);
1701 }
1702
1703 void
cogl_framebuffer_finish(CoglFramebuffer * framebuffer)1704 cogl_framebuffer_finish (CoglFramebuffer *framebuffer)
1705 {
1706 CoglFramebufferPrivate *priv =
1707 cogl_framebuffer_get_instance_private (framebuffer);
1708
1709 _cogl_framebuffer_flush_journal (framebuffer);
1710
1711 cogl_framebuffer_driver_finish (priv->driver);
1712 }
1713
1714 void
cogl_framebuffer_flush(CoglFramebuffer * framebuffer)1715 cogl_framebuffer_flush (CoglFramebuffer *framebuffer)
1716 {
1717 CoglFramebufferPrivate *priv =
1718 cogl_framebuffer_get_instance_private (framebuffer);
1719
1720 _cogl_framebuffer_flush_journal (framebuffer);
1721
1722 cogl_framebuffer_driver_flush (priv->driver);
1723 }
1724
1725 void
cogl_framebuffer_push_matrix(CoglFramebuffer * framebuffer)1726 cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer)
1727 {
1728 CoglFramebufferPrivate *priv =
1729 cogl_framebuffer_get_instance_private (framebuffer);
1730 CoglMatrixStack *modelview_stack =
1731 _cogl_framebuffer_get_modelview_stack (framebuffer);
1732 cogl_matrix_stack_push (modelview_stack);
1733
1734 if (priv->context->current_draw_buffer == framebuffer)
1735 {
1736 priv->context->current_draw_buffer_changes |=
1737 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1738 }
1739 }
1740
1741 void
cogl_framebuffer_pop_matrix(CoglFramebuffer * framebuffer)1742 cogl_framebuffer_pop_matrix (CoglFramebuffer *framebuffer)
1743 {
1744 CoglFramebufferPrivate *priv =
1745 cogl_framebuffer_get_instance_private (framebuffer);
1746 CoglMatrixStack *modelview_stack =
1747 _cogl_framebuffer_get_modelview_stack (framebuffer);
1748 cogl_matrix_stack_pop (modelview_stack);
1749
1750 if (priv->context->current_draw_buffer == framebuffer)
1751 {
1752 priv->context->current_draw_buffer_changes |=
1753 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1754 }
1755 }
1756
1757 void
cogl_framebuffer_identity_matrix(CoglFramebuffer * framebuffer)1758 cogl_framebuffer_identity_matrix (CoglFramebuffer *framebuffer)
1759 {
1760 CoglFramebufferPrivate *priv =
1761 cogl_framebuffer_get_instance_private (framebuffer);
1762 CoglMatrixStack *modelview_stack =
1763 _cogl_framebuffer_get_modelview_stack (framebuffer);
1764 cogl_matrix_stack_load_identity (modelview_stack);
1765
1766 if (priv->context->current_draw_buffer == framebuffer)
1767 {
1768 priv->context->current_draw_buffer_changes |=
1769 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1770 }
1771 }
1772
1773 void
cogl_framebuffer_scale(CoglFramebuffer * framebuffer,float x,float y,float z)1774 cogl_framebuffer_scale (CoglFramebuffer *framebuffer,
1775 float x,
1776 float y,
1777 float z)
1778 {
1779 CoglFramebufferPrivate *priv =
1780 cogl_framebuffer_get_instance_private (framebuffer);
1781 CoglMatrixStack *modelview_stack =
1782 _cogl_framebuffer_get_modelview_stack (framebuffer);
1783 cogl_matrix_stack_scale (modelview_stack, x, y, z);
1784
1785 if (priv->context->current_draw_buffer == framebuffer)
1786 {
1787 priv->context->current_draw_buffer_changes |=
1788 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1789 }
1790 }
1791
1792 void
cogl_framebuffer_translate(CoglFramebuffer * framebuffer,float x,float y,float z)1793 cogl_framebuffer_translate (CoglFramebuffer *framebuffer,
1794 float x,
1795 float y,
1796 float z)
1797 {
1798 CoglFramebufferPrivate *priv =
1799 cogl_framebuffer_get_instance_private (framebuffer);
1800 CoglMatrixStack *modelview_stack =
1801 _cogl_framebuffer_get_modelview_stack (framebuffer);
1802 cogl_matrix_stack_translate (modelview_stack, x, y, z);
1803
1804 if (priv->context->current_draw_buffer == framebuffer)
1805 {
1806 priv->context->current_draw_buffer_changes |=
1807 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1808 }
1809 }
1810
1811 void
cogl_framebuffer_rotate(CoglFramebuffer * framebuffer,float angle,float x,float y,float z)1812 cogl_framebuffer_rotate (CoglFramebuffer *framebuffer,
1813 float angle,
1814 float x,
1815 float y,
1816 float z)
1817 {
1818 CoglFramebufferPrivate *priv =
1819 cogl_framebuffer_get_instance_private (framebuffer);
1820 CoglMatrixStack *modelview_stack =
1821 _cogl_framebuffer_get_modelview_stack (framebuffer);
1822 cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z);
1823
1824 if (priv->context->current_draw_buffer == framebuffer)
1825 {
1826 priv->context->current_draw_buffer_changes |=
1827 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1828 }
1829 }
1830
1831 void
cogl_framebuffer_rotate_euler(CoglFramebuffer * framebuffer,const graphene_euler_t * euler)1832 cogl_framebuffer_rotate_euler (CoglFramebuffer *framebuffer,
1833 const graphene_euler_t *euler)
1834 {
1835 CoglFramebufferPrivate *priv =
1836 cogl_framebuffer_get_instance_private (framebuffer);
1837 CoglMatrixStack *modelview_stack =
1838 _cogl_framebuffer_get_modelview_stack (framebuffer);
1839 cogl_matrix_stack_rotate_euler (modelview_stack, euler);
1840
1841 if (priv->context->current_draw_buffer == framebuffer)
1842 {
1843 priv->context->current_draw_buffer_changes |=
1844 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1845 }
1846 }
1847
1848 void
cogl_framebuffer_transform(CoglFramebuffer * framebuffer,const graphene_matrix_t * matrix)1849 cogl_framebuffer_transform (CoglFramebuffer *framebuffer,
1850 const graphene_matrix_t *matrix)
1851 {
1852 CoglFramebufferPrivate *priv =
1853 cogl_framebuffer_get_instance_private (framebuffer);
1854 CoglMatrixStack *modelview_stack =
1855 _cogl_framebuffer_get_modelview_stack (framebuffer);
1856 cogl_matrix_stack_multiply (modelview_stack, matrix);
1857
1858 if (priv->context->current_draw_buffer == framebuffer)
1859 {
1860 priv->context->current_draw_buffer_changes |=
1861 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1862 }
1863 }
1864
1865 void
cogl_framebuffer_perspective(CoglFramebuffer * framebuffer,float fov_y,float aspect,float z_near,float z_far)1866 cogl_framebuffer_perspective (CoglFramebuffer *framebuffer,
1867 float fov_y,
1868 float aspect,
1869 float z_near,
1870 float z_far)
1871 {
1872 CoglFramebufferPrivate *priv =
1873 cogl_framebuffer_get_instance_private (framebuffer);
1874 float ymax = z_near * tanf (fov_y * G_PI / 360.0);
1875
1876 cogl_framebuffer_frustum (framebuffer,
1877 -ymax * aspect, /* left */
1878 ymax * aspect, /* right */
1879 -ymax, /* bottom */
1880 ymax, /* top */
1881 z_near,
1882 z_far);
1883
1884 if (priv->context->current_draw_buffer == framebuffer)
1885 {
1886 priv->context->current_draw_buffer_changes |=
1887 COGL_FRAMEBUFFER_STATE_PROJECTION;
1888 }
1889 }
1890
1891 void
cogl_framebuffer_frustum(CoglFramebuffer * framebuffer,float left,float right,float bottom,float top,float z_near,float z_far)1892 cogl_framebuffer_frustum (CoglFramebuffer *framebuffer,
1893 float left,
1894 float right,
1895 float bottom,
1896 float top,
1897 float z_near,
1898 float z_far)
1899 {
1900 CoglFramebufferPrivate *priv =
1901 cogl_framebuffer_get_instance_private (framebuffer);
1902 CoglMatrixStack *projection_stack =
1903 _cogl_framebuffer_get_projection_stack (framebuffer);
1904
1905 /* XXX: The projection matrix isn't currently tracked in the journal
1906 * so we need to flush all journaled primitives first... */
1907 _cogl_framebuffer_flush_journal (framebuffer);
1908
1909 cogl_matrix_stack_load_identity (projection_stack);
1910
1911 cogl_matrix_stack_frustum (projection_stack,
1912 left,
1913 right,
1914 bottom,
1915 top,
1916 z_near,
1917 z_far);
1918
1919 if (priv->context->current_draw_buffer == framebuffer)
1920 {
1921 priv->context->current_draw_buffer_changes |=
1922 COGL_FRAMEBUFFER_STATE_PROJECTION;
1923 }
1924 }
1925
1926 void
cogl_framebuffer_orthographic(CoglFramebuffer * framebuffer,float x_1,float y_1,float x_2,float y_2,float near,float far)1927 cogl_framebuffer_orthographic (CoglFramebuffer *framebuffer,
1928 float x_1,
1929 float y_1,
1930 float x_2,
1931 float y_2,
1932 float near,
1933 float far)
1934 {
1935 CoglFramebufferPrivate *priv =
1936 cogl_framebuffer_get_instance_private (framebuffer);
1937 graphene_matrix_t ortho;
1938 CoglMatrixStack *projection_stack =
1939 _cogl_framebuffer_get_projection_stack (framebuffer);
1940
1941 /* XXX: The projection matrix isn't currently tracked in the journal
1942 * so we need to flush all journaled primitives first... */
1943 _cogl_framebuffer_flush_journal (framebuffer);
1944
1945 graphene_matrix_init_ortho (&ortho, x_1, x_2, y_2, y_1, near, far);
1946 cogl_matrix_stack_set (projection_stack, &ortho);
1947
1948 if (priv->context->current_draw_buffer == framebuffer)
1949 {
1950 priv->context->current_draw_buffer_changes |=
1951 COGL_FRAMEBUFFER_STATE_PROJECTION;
1952 }
1953 }
1954
1955 void
cogl_framebuffer_get_modelview_matrix(CoglFramebuffer * framebuffer,graphene_matrix_t * matrix)1956 cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer,
1957 graphene_matrix_t *matrix)
1958 {
1959 CoglMatrixEntry *modelview_entry =
1960 _cogl_framebuffer_get_modelview_entry (framebuffer);
1961
1962 cogl_matrix_entry_get (modelview_entry, matrix);
1963 }
1964
1965 void
cogl_framebuffer_set_modelview_matrix(CoglFramebuffer * framebuffer,const graphene_matrix_t * matrix)1966 cogl_framebuffer_set_modelview_matrix (CoglFramebuffer *framebuffer,
1967 const graphene_matrix_t *matrix)
1968 {
1969 CoglFramebufferPrivate *priv =
1970 cogl_framebuffer_get_instance_private (framebuffer);
1971 CoglMatrixStack *modelview_stack =
1972 _cogl_framebuffer_get_modelview_stack (framebuffer);
1973 cogl_matrix_stack_set (modelview_stack, matrix);
1974
1975 if (priv->context->current_draw_buffer == framebuffer)
1976 {
1977 priv->context->current_draw_buffer_changes |=
1978 COGL_FRAMEBUFFER_STATE_MODELVIEW;
1979 }
1980 }
1981
1982 void
cogl_framebuffer_get_projection_matrix(CoglFramebuffer * framebuffer,graphene_matrix_t * matrix)1983 cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer,
1984 graphene_matrix_t *matrix)
1985 {
1986 CoglMatrixEntry *projection_entry =
1987 _cogl_framebuffer_get_projection_entry (framebuffer);
1988
1989 cogl_matrix_entry_get (projection_entry, matrix);
1990 }
1991
1992 void
cogl_framebuffer_set_projection_matrix(CoglFramebuffer * framebuffer,const graphene_matrix_t * matrix)1993 cogl_framebuffer_set_projection_matrix (CoglFramebuffer *framebuffer,
1994 const graphene_matrix_t *matrix)
1995 {
1996 CoglFramebufferPrivate *priv =
1997 cogl_framebuffer_get_instance_private (framebuffer);
1998 CoglMatrixStack *projection_stack =
1999 _cogl_framebuffer_get_projection_stack (framebuffer);
2000
2001 /* XXX: The projection matrix isn't currently tracked in the journal
2002 * so we need to flush all journaled primitives first... */
2003 _cogl_framebuffer_flush_journal (framebuffer);
2004
2005 cogl_matrix_stack_set (projection_stack, matrix);
2006
2007 if (priv->context->current_draw_buffer == framebuffer)
2008 {
2009 priv->context->current_draw_buffer_changes |=
2010 COGL_FRAMEBUFFER_STATE_PROJECTION;
2011 }
2012 }
2013
2014 void
cogl_framebuffer_push_scissor_clip(CoglFramebuffer * framebuffer,int x,int y,int width,int height)2015 cogl_framebuffer_push_scissor_clip (CoglFramebuffer *framebuffer,
2016 int x,
2017 int y,
2018 int width,
2019 int height)
2020 {
2021 CoglFramebufferPrivate *priv =
2022 cogl_framebuffer_get_instance_private (framebuffer);
2023
2024 priv->clip_stack =
2025 _cogl_clip_stack_push_window_rectangle (priv->clip_stack,
2026 x, y, width, height);
2027
2028 if (priv->context->current_draw_buffer == framebuffer)
2029 {
2030 priv->context->current_draw_buffer_changes |=
2031 COGL_FRAMEBUFFER_STATE_CLIP;
2032 }
2033 }
2034
2035 void
cogl_framebuffer_push_rectangle_clip(CoglFramebuffer * framebuffer,float x_1,float y_1,float x_2,float y_2)2036 cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer,
2037 float x_1,
2038 float y_1,
2039 float x_2,
2040 float y_2)
2041 {
2042 CoglFramebufferPrivate *priv =
2043 cogl_framebuffer_get_instance_private (framebuffer);
2044 CoglMatrixEntry *modelview_entry =
2045 _cogl_framebuffer_get_modelview_entry (framebuffer);
2046 CoglMatrixEntry *projection_entry =
2047 _cogl_framebuffer_get_projection_entry (framebuffer);
2048 /* XXX: It would be nicer if we stored the private viewport as a
2049 * vec4 so we could avoid this redundant copy. */
2050 float viewport[] = {
2051 priv->viewport_x,
2052 priv->viewport_y,
2053 priv->viewport_width,
2054 priv->viewport_height
2055 };
2056
2057 priv->clip_stack =
2058 _cogl_clip_stack_push_rectangle (priv->clip_stack,
2059 x_1, y_1, x_2, y_2,
2060 modelview_entry,
2061 projection_entry,
2062 viewport);
2063
2064 if (priv->context->current_draw_buffer == framebuffer)
2065 {
2066 priv->context->current_draw_buffer_changes |=
2067 COGL_FRAMEBUFFER_STATE_CLIP;
2068 }
2069 }
2070
2071 void
cogl_framebuffer_push_primitive_clip(CoglFramebuffer * framebuffer,CoglPrimitive * primitive,float bounds_x1,float bounds_y1,float bounds_x2,float bounds_y2)2072 cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer,
2073 CoglPrimitive *primitive,
2074 float bounds_x1,
2075 float bounds_y1,
2076 float bounds_x2,
2077 float bounds_y2)
2078 {
2079 CoglFramebufferPrivate *priv =
2080 cogl_framebuffer_get_instance_private (framebuffer);
2081 CoglMatrixEntry *modelview_entry =
2082 _cogl_framebuffer_get_modelview_entry (framebuffer);
2083 CoglMatrixEntry *projection_entry =
2084 _cogl_framebuffer_get_projection_entry (framebuffer);
2085 /* XXX: It would be nicer if we stored the private viewport as a
2086 * vec4 so we could avoid this redundant copy. */
2087 float viewport[] = {
2088 priv->viewport_x,
2089 priv->viewport_y,
2090 priv->viewport_width,
2091 priv->viewport_height
2092 };
2093
2094 priv->clip_stack =
2095 _cogl_clip_stack_push_primitive (priv->clip_stack,
2096 primitive,
2097 bounds_x1, bounds_y1,
2098 bounds_x2, bounds_y2,
2099 modelview_entry,
2100 projection_entry,
2101 viewport);
2102
2103 if (priv->context->current_draw_buffer == framebuffer)
2104 {
2105 priv->context->current_draw_buffer_changes |=
2106 COGL_FRAMEBUFFER_STATE_CLIP;
2107 }
2108 }
2109
2110 void
cogl_framebuffer_push_region_clip(CoglFramebuffer * framebuffer,cairo_region_t * region)2111 cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer,
2112 cairo_region_t *region)
2113 {
2114 CoglFramebufferPrivate *priv =
2115 cogl_framebuffer_get_instance_private (framebuffer);
2116
2117 priv->clip_stack =
2118 cogl_clip_stack_push_region (priv->clip_stack,
2119 region);
2120
2121 if (priv->context->current_draw_buffer == framebuffer)
2122 {
2123 priv->context->current_draw_buffer_changes |=
2124 COGL_FRAMEBUFFER_STATE_CLIP;
2125 }
2126 }
2127
2128 void
cogl_framebuffer_pop_clip(CoglFramebuffer * framebuffer)2129 cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer)
2130 {
2131 CoglFramebufferPrivate *priv =
2132 cogl_framebuffer_get_instance_private (framebuffer);
2133
2134 priv->clip_stack = _cogl_clip_stack_pop (priv->clip_stack);
2135
2136 if (priv->context->current_draw_buffer == framebuffer)
2137 {
2138 priv->context->current_draw_buffer_changes |=
2139 COGL_FRAMEBUFFER_STATE_CLIP;
2140 }
2141 }
2142
2143 #ifdef COGL_ENABLE_DEBUG
2144 static int
get_index(void * indices,CoglIndicesType type,int _index)2145 get_index (void *indices,
2146 CoglIndicesType type,
2147 int _index)
2148 {
2149 if (!indices)
2150 return _index;
2151
2152 switch (type)
2153 {
2154 case COGL_INDICES_TYPE_UNSIGNED_BYTE:
2155 return ((uint8_t *)indices)[_index];
2156 case COGL_INDICES_TYPE_UNSIGNED_SHORT:
2157 return ((uint16_t *)indices)[_index];
2158 case COGL_INDICES_TYPE_UNSIGNED_INT:
2159 return ((uint32_t *)indices)[_index];
2160 }
2161
2162 g_return_val_if_reached (0);
2163 }
2164
2165 static void
add_line(uint32_t * line_indices,int base,void * user_indices,CoglIndicesType user_indices_type,int index0,int index1,int * pos)2166 add_line (uint32_t *line_indices,
2167 int base,
2168 void *user_indices,
2169 CoglIndicesType user_indices_type,
2170 int index0,
2171 int index1,
2172 int *pos)
2173 {
2174 index0 = get_index (user_indices, user_indices_type, index0);
2175 index1 = get_index (user_indices, user_indices_type, index1);
2176
2177 line_indices[(*pos)++] = base + index0;
2178 line_indices[(*pos)++] = base + index1;
2179 }
2180
2181 static int
get_line_count(CoglVerticesMode mode,int n_vertices)2182 get_line_count (CoglVerticesMode mode, int n_vertices)
2183 {
2184 if (mode == COGL_VERTICES_MODE_TRIANGLES &&
2185 (n_vertices % 3) == 0)
2186 {
2187 return n_vertices;
2188 }
2189 else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN &&
2190 n_vertices >= 3)
2191 {
2192 return 2 * n_vertices - 3;
2193 }
2194 else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP &&
2195 n_vertices >= 3)
2196 {
2197 return 2 * n_vertices - 3;
2198 }
2199 /* In the journal we are a bit sneaky and actually use GL_QUADS
2200 * which isn't actually a valid CoglVerticesMode! */
2201 #ifdef HAVE_COGL_GL
2202 else if (mode == GL_QUADS && (n_vertices % 4) == 0)
2203 {
2204 return n_vertices;
2205 }
2206 #endif
2207
2208 g_return_val_if_reached (0);
2209 }
2210
2211 static CoglIndices *
get_wire_line_indices(CoglContext * ctx,CoglVerticesMode mode,int first_vertex,int n_vertices_in,CoglIndices * user_indices,int * n_indices)2212 get_wire_line_indices (CoglContext *ctx,
2213 CoglVerticesMode mode,
2214 int first_vertex,
2215 int n_vertices_in,
2216 CoglIndices *user_indices,
2217 int *n_indices)
2218 {
2219 int n_lines;
2220 uint32_t *line_indices;
2221 CoglIndexBuffer *index_buffer;
2222 void *indices;
2223 CoglIndicesType indices_type;
2224 int base = first_vertex;
2225 int pos;
2226 int i;
2227 CoglIndices *ret;
2228
2229 if (user_indices)
2230 {
2231 index_buffer = cogl_indices_get_buffer (user_indices);
2232 indices = _cogl_buffer_map (COGL_BUFFER (index_buffer),
2233 COGL_BUFFER_ACCESS_READ, 0,
2234 NULL);
2235 indices_type = cogl_indices_get_type (user_indices);
2236 }
2237 else
2238 {
2239 index_buffer = NULL;
2240 indices = NULL;
2241 indices_type = COGL_INDICES_TYPE_UNSIGNED_BYTE;
2242 }
2243
2244 n_lines = get_line_count (mode, n_vertices_in);
2245
2246 /* Note: we are using COGL_INDICES_TYPE_UNSIGNED_INT so 4 bytes per index. */
2247 line_indices = g_malloc (4 * n_lines * 2);
2248
2249 pos = 0;
2250
2251 if (mode == COGL_VERTICES_MODE_TRIANGLES &&
2252 (n_vertices_in % 3) == 0)
2253 {
2254 for (i = 0; i < n_vertices_in; i += 3)
2255 {
2256 add_line (line_indices, base, indices, indices_type, i, i+1, &pos);
2257 add_line (line_indices, base, indices, indices_type, i+1, i+2, &pos);
2258 add_line (line_indices, base, indices, indices_type, i+2, i, &pos);
2259 }
2260 }
2261 else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN &&
2262 n_vertices_in >= 3)
2263 {
2264 add_line (line_indices, base, indices, indices_type, 0, 1, &pos);
2265 add_line (line_indices, base, indices, indices_type, 1, 2, &pos);
2266 add_line (line_indices, base, indices, indices_type, 0, 2, &pos);
2267
2268 for (i = 3; i < n_vertices_in; i++)
2269 {
2270 add_line (line_indices, base, indices, indices_type, i - 1, i, &pos);
2271 add_line (line_indices, base, indices, indices_type, 0, i, &pos);
2272 }
2273 }
2274 else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP &&
2275 n_vertices_in >= 3)
2276 {
2277 add_line (line_indices, base, indices, indices_type, 0, 1, &pos);
2278 add_line (line_indices, base, indices, indices_type, 1, 2, &pos);
2279 add_line (line_indices, base, indices, indices_type, 0, 2, &pos);
2280
2281 for (i = 3; i < n_vertices_in; i++)
2282 {
2283 add_line (line_indices, base, indices, indices_type, i - 1, i, &pos);
2284 add_line (line_indices, base, indices, indices_type, i - 2, i, &pos);
2285 }
2286 }
2287 /* In the journal we are a bit sneaky and actually use GL_QUADS
2288 * which isn't actually a valid CoglVerticesMode! */
2289 #ifdef HAVE_COGL_GL
2290 else if (mode == GL_QUADS && (n_vertices_in % 4) == 0)
2291 {
2292 for (i = 0; i < n_vertices_in; i += 4)
2293 {
2294 add_line (line_indices,
2295 base, indices, indices_type, i, i + 1, &pos);
2296 add_line (line_indices,
2297 base, indices, indices_type, i + 1, i + 2, &pos);
2298 add_line (line_indices,
2299 base, indices, indices_type, i + 2, i + 3, &pos);
2300 add_line (line_indices,
2301 base, indices, indices_type, i + 3, i, &pos);
2302 }
2303 }
2304 #endif
2305
2306 if (user_indices)
2307 cogl_buffer_unmap (COGL_BUFFER (index_buffer));
2308
2309 *n_indices = n_lines * 2;
2310
2311 ret = cogl_indices_new (ctx,
2312 COGL_INDICES_TYPE_UNSIGNED_INT,
2313 line_indices,
2314 *n_indices);
2315
2316 g_free (line_indices);
2317
2318 return ret;
2319 }
2320
2321 static void
pipeline_destroyed_cb(CoglPipeline * weak_pipeline,void * user_data)2322 pipeline_destroyed_cb (CoglPipeline *weak_pipeline, void *user_data)
2323 {
2324 CoglPipeline *original_pipeline = user_data;
2325
2326 /* XXX: I think we probably need to provide a custom unref function for
2327 * CoglPipeline because it's possible that we will reach this callback
2328 * because original_pipeline is being freed which means cogl_object_unref
2329 * will have already freed any associated user data.
2330 *
2331 * Setting more user data here will *probably* succeed but that may allocate
2332 * a new user-data array which could be leaked.
2333 *
2334 * Potentially we could have a _cogl_object_free_user_data function so
2335 * that a custom unref function could be written that can destroy weak
2336 * pipeline children before removing user data.
2337 */
2338 cogl_object_set_user_data (COGL_OBJECT (original_pipeline),
2339 &wire_pipeline_key, NULL, NULL);
2340
2341 cogl_object_unref (weak_pipeline);
2342 }
2343
2344 static void
draw_wireframe(CoglContext * ctx,CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglVerticesMode mode,int first_vertex,int n_vertices,CoglAttribute ** attributes,int n_attributes,CoglIndices * indices,CoglDrawFlags flags)2345 draw_wireframe (CoglContext *ctx,
2346 CoglFramebuffer *framebuffer,
2347 CoglPipeline *pipeline,
2348 CoglVerticesMode mode,
2349 int first_vertex,
2350 int n_vertices,
2351 CoglAttribute **attributes,
2352 int n_attributes,
2353 CoglIndices *indices,
2354 CoglDrawFlags flags)
2355 {
2356 CoglIndices *wire_indices;
2357 CoglPipeline *wire_pipeline;
2358 int n_indices;
2359
2360 wire_indices = get_wire_line_indices (ctx,
2361 mode,
2362 first_vertex,
2363 n_vertices,
2364 indices,
2365 &n_indices);
2366
2367 wire_pipeline = cogl_object_get_user_data (COGL_OBJECT (pipeline),
2368 &wire_pipeline_key);
2369
2370 if (!wire_pipeline)
2371 {
2372 static CoglSnippet *snippet = NULL;
2373
2374 wire_pipeline =
2375 _cogl_pipeline_weak_copy (pipeline, pipeline_destroyed_cb, NULL);
2376
2377 cogl_object_set_user_data (COGL_OBJECT (pipeline),
2378 &wire_pipeline_key, wire_pipeline,
2379 NULL);
2380
2381 /* If we have glsl then the pipeline may have an associated
2382 * vertex program and since we'd like to see the results of the
2383 * vertex program in the wireframe we just add a final clobber
2384 * of the wire color leaving the rest of the state untouched. */
2385
2386 /* The snippet is cached so that it will reuse the program
2387 * from the pipeline cache if possible */
2388 if (snippet == NULL)
2389 {
2390 snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
2391 NULL,
2392 NULL);
2393 cogl_snippet_set_replace (snippet,
2394 "cogl_color_out = "
2395 "vec4 (0.0, 1.0, 0.0, 1.0);\n");
2396 }
2397
2398 cogl_pipeline_add_snippet (wire_pipeline, snippet);
2399 }
2400
2401 /* temporarily disable the wireframe to avoid recursion! */
2402 flags |= COGL_DRAW_SKIP_DEBUG_WIREFRAME;
2403 _cogl_framebuffer_draw_indexed_attributes (
2404 framebuffer,
2405 wire_pipeline,
2406 COGL_VERTICES_MODE_LINES,
2407 0,
2408 n_indices,
2409 wire_indices,
2410 attributes,
2411 n_attributes,
2412 flags);
2413 COGL_DEBUG_SET_FLAG (COGL_DEBUG_WIREFRAME);
2414
2415 cogl_object_unref (wire_indices);
2416 }
2417 #endif
2418
2419 /* This can be called directly by the CoglJournal to draw attributes
2420 * skipping the implicit journal flush, the framebuffer flush and
2421 * pipeline validation. */
2422 void
_cogl_framebuffer_draw_attributes(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglVerticesMode mode,int first_vertex,int n_vertices,CoglAttribute ** attributes,int n_attributes,CoglDrawFlags flags)2423 _cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer,
2424 CoglPipeline *pipeline,
2425 CoglVerticesMode mode,
2426 int first_vertex,
2427 int n_vertices,
2428 CoglAttribute **attributes,
2429 int n_attributes,
2430 CoglDrawFlags flags)
2431 {
2432 CoglFramebufferPrivate *priv =
2433 cogl_framebuffer_get_instance_private (framebuffer);
2434
2435 #ifdef COGL_ENABLE_DEBUG
2436 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) &&
2437 (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) &&
2438 mode != COGL_VERTICES_MODE_LINES &&
2439 mode != COGL_VERTICES_MODE_LINE_LOOP &&
2440 mode != COGL_VERTICES_MODE_LINE_STRIP)
2441 draw_wireframe (priv->context,
2442 framebuffer, pipeline,
2443 mode, first_vertex, n_vertices,
2444 attributes, n_attributes, NULL,
2445 flags);
2446 else
2447 #endif
2448 {
2449 cogl_framebuffer_driver_draw_attributes (priv->driver,
2450 pipeline,
2451 mode,
2452 first_vertex,
2453 n_vertices,
2454 attributes,
2455 n_attributes,
2456 flags);
2457 }
2458 }
2459
2460 void
_cogl_framebuffer_draw_indexed_attributes(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglVerticesMode mode,int first_vertex,int n_vertices,CoglIndices * indices,CoglAttribute ** attributes,int n_attributes,CoglDrawFlags flags)2461 _cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer,
2462 CoglPipeline *pipeline,
2463 CoglVerticesMode mode,
2464 int first_vertex,
2465 int n_vertices,
2466 CoglIndices *indices,
2467 CoglAttribute **attributes,
2468 int n_attributes,
2469 CoglDrawFlags flags)
2470 {
2471 CoglFramebufferPrivate *priv =
2472 cogl_framebuffer_get_instance_private (framebuffer);
2473
2474 #ifdef COGL_ENABLE_DEBUG
2475 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) &&
2476 (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) &&
2477 mode != COGL_VERTICES_MODE_LINES &&
2478 mode != COGL_VERTICES_MODE_LINE_LOOP &&
2479 mode != COGL_VERTICES_MODE_LINE_STRIP)
2480 draw_wireframe (priv->context,
2481 framebuffer, pipeline,
2482 mode, first_vertex, n_vertices,
2483 attributes, n_attributes, indices,
2484 flags);
2485 else
2486 #endif
2487 {
2488 cogl_framebuffer_driver_draw_indexed_attributes (priv->driver,
2489 pipeline,
2490 mode,
2491 first_vertex,
2492 n_vertices,
2493 indices,
2494 attributes,
2495 n_attributes,
2496 flags);
2497 }
2498 }
2499
2500 void
cogl_framebuffer_draw_primitive(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglPrimitive * primitive)2501 cogl_framebuffer_draw_primitive (CoglFramebuffer *framebuffer,
2502 CoglPipeline *pipeline,
2503 CoglPrimitive *primitive)
2504 {
2505 _cogl_primitive_draw (primitive, framebuffer, pipeline, 0);
2506 }
2507
2508 void
cogl_framebuffer_draw_rectangle(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,float x_1,float y_1,float x_2,float y_2)2509 cogl_framebuffer_draw_rectangle (CoglFramebuffer *framebuffer,
2510 CoglPipeline *pipeline,
2511 float x_1,
2512 float y_1,
2513 float x_2,
2514 float y_2)
2515 {
2516 const float position[4] = {x_1, y_1, x_2, y_2};
2517 CoglMultiTexturedRect rect;
2518
2519 /* XXX: All the _*_rectangle* APIs normalize their input into an array of
2520 * _CoglMultiTexturedRect rectangles and pass these on to our work horse;
2521 * _cogl_framebuffer_draw_multitextured_rectangles.
2522 */
2523
2524 rect.position = position;
2525 rect.tex_coords = NULL;
2526 rect.tex_coords_len = 0;
2527
2528 _cogl_framebuffer_draw_multitextured_rectangles (framebuffer,
2529 pipeline,
2530 &rect,
2531 1);
2532 }
2533
2534 void
cogl_framebuffer_draw_textured_rectangle(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,float x_1,float y_1,float x_2,float y_2,float s_1,float t_1,float s_2,float t_2)2535 cogl_framebuffer_draw_textured_rectangle (CoglFramebuffer *framebuffer,
2536 CoglPipeline *pipeline,
2537 float x_1,
2538 float y_1,
2539 float x_2,
2540 float y_2,
2541 float s_1,
2542 float t_1,
2543 float s_2,
2544 float t_2)
2545 {
2546 const float position[4] = {x_1, y_1, x_2, y_2};
2547 const float tex_coords[4] = {s_1, t_1, s_2, t_2};
2548 CoglMultiTexturedRect rect;
2549
2550 /* XXX: All the _*_rectangle* APIs normalize their input into an array of
2551 * CoglMultiTexturedRect rectangles and pass these on to our work horse;
2552 * _cogl_framebuffer_draw_multitextured_rectangles.
2553 */
2554
2555 rect.position = position;
2556 rect.tex_coords = tex_coords;
2557 rect.tex_coords_len = 4;
2558
2559 _cogl_framebuffer_draw_multitextured_rectangles (framebuffer,
2560 pipeline,
2561 &rect,
2562 1);
2563 }
2564
2565 void
cogl_framebuffer_draw_multitextured_rectangle(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,float x_1,float y_1,float x_2,float y_2,const float * tex_coords,int tex_coords_len)2566 cogl_framebuffer_draw_multitextured_rectangle (CoglFramebuffer *framebuffer,
2567 CoglPipeline *pipeline,
2568 float x_1,
2569 float y_1,
2570 float x_2,
2571 float y_2,
2572 const float *tex_coords,
2573 int tex_coords_len)
2574 {
2575 const float position[4] = {x_1, y_1, x_2, y_2};
2576 CoglMultiTexturedRect rect;
2577
2578 /* XXX: All the _*_rectangle* APIs normalize their input into an array of
2579 * CoglMultiTexturedRect rectangles and pass these on to our work horse;
2580 * _cogl_framebuffer_draw_multitextured_rectangles.
2581 */
2582
2583 rect.position = position;
2584 rect.tex_coords = tex_coords;
2585 rect.tex_coords_len = tex_coords_len;
2586
2587 _cogl_framebuffer_draw_multitextured_rectangles (framebuffer,
2588 pipeline,
2589 &rect,
2590 1);
2591 }
2592
2593 void
cogl_framebuffer_draw_rectangles(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,const float * coordinates,unsigned int n_rectangles)2594 cogl_framebuffer_draw_rectangles (CoglFramebuffer *framebuffer,
2595 CoglPipeline *pipeline,
2596 const float *coordinates,
2597 unsigned int n_rectangles)
2598 {
2599 CoglMultiTexturedRect *rects;
2600 int i;
2601
2602 /* XXX: All the _*_rectangle* APIs normalize their input into an array of
2603 * CoglMultiTexturedRect rectangles and pass these on to our work horse;
2604 * _cogl_framebuffer_draw_multitextured_rectangles.
2605 */
2606
2607 rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect));
2608
2609 for (i = 0; i < n_rectangles; i++)
2610 {
2611 rects[i].position = &coordinates[i * 4];
2612 rects[i].tex_coords = NULL;
2613 rects[i].tex_coords_len = 0;
2614 }
2615
2616 _cogl_framebuffer_draw_multitextured_rectangles (framebuffer,
2617 pipeline,
2618 rects,
2619 n_rectangles);
2620 }
2621
2622 void
cogl_framebuffer_draw_textured_rectangles(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,const float * coordinates,unsigned int n_rectangles)2623 cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer,
2624 CoglPipeline *pipeline,
2625 const float *coordinates,
2626 unsigned int n_rectangles)
2627 {
2628 CoglMultiTexturedRect *rects;
2629 int i;
2630
2631 /* XXX: All the _*_rectangle* APIs normalize their input into an array of
2632 * _CoglMultiTexturedRect rectangles and pass these on to our work horse;
2633 * _cogl_framebuffer_draw_multitextured_rectangles.
2634 */
2635
2636 rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect));
2637
2638 for (i = 0; i < n_rectangles; i++)
2639 {
2640 rects[i].position = &coordinates[i * 8];
2641 rects[i].tex_coords = &coordinates[i * 8 + 4];
2642 rects[i].tex_coords_len = 4;
2643 }
2644
2645 _cogl_framebuffer_draw_multitextured_rectangles (framebuffer,
2646 pipeline,
2647 rects,
2648 n_rectangles);
2649 }
2650
2651 CoglFramebufferDriver *
cogl_framebuffer_get_driver(CoglFramebuffer * framebuffer)2652 cogl_framebuffer_get_driver (CoglFramebuffer *framebuffer)
2653 {
2654 CoglFramebufferPrivate *priv =
2655 cogl_framebuffer_get_instance_private (framebuffer);
2656
2657 return priv->driver;
2658 }
2659
2660 CoglTimestampQuery *
cogl_framebuffer_create_timestamp_query(CoglFramebuffer * framebuffer)2661 cogl_framebuffer_create_timestamp_query (CoglFramebuffer *framebuffer)
2662 {
2663 CoglFramebufferPrivate *priv =
2664 cogl_framebuffer_get_instance_private (framebuffer);
2665 const CoglDriverVtable *driver_vtable = priv->context->driver_vtable;
2666
2667 g_return_val_if_fail (cogl_has_feature (priv->context,
2668 COGL_FEATURE_ID_TIMESTAMP_QUERY),
2669 NULL);
2670
2671 /* The timestamp query completes upon completion of all previously submitted
2672 * GL commands. So make sure those commands are indeed submitted by flushing
2673 * the journal.
2674 */
2675 _cogl_framebuffer_flush_journal (framebuffer);
2676
2677 cogl_context_flush_framebuffer_state (priv->context,
2678 framebuffer,
2679 framebuffer,
2680 COGL_FRAMEBUFFER_STATE_BIND);
2681
2682 return driver_vtable->create_timestamp_query (priv->context);
2683 }
2684