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