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