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