4 * An object oriented GL/GLES Abstraction/Utility Layer
6 * Copyright (C) 2009,2010 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
24 * Havoc Pennington <hp@pobox.com> for litl
25 * Robert Bragg <robert@linux.intel.com>
32 #include "cogl-context-private.h"
33 #include "cogl-internal.h"
34 #include "cogl-matrix-stack.h"
35 #include "cogl-framebuffer-private.h"
36 #include "cogl-object-private.h"
37 #include "cogl-offscreen.h"
42 /* count of pushes with no changes; when a change is
43 * requested, we create a new state and decrement this
51 * Stores a cogl-side matrix stack, which we use as a cache
52 * so we can get the matrix efficiently when using indirect
55 struct _CoglMatrixStack
64 static void _cogl_matrix_stack_free (CoglMatrixStack *stack);
66 COGL_OBJECT_INTERNAL_DEFINE (MatrixStack, matrix_stack);
68 /* XXX: this doesn't initialize the matrix! */
70 _cogl_matrix_state_init (CoglMatrixState *state)
72 state->push_count = 0;
73 state->is_identity = FALSE;
76 static CoglMatrixState *
77 _cogl_matrix_stack_top (CoglMatrixStack *stack)
79 return &g_array_index (stack->stack, CoglMatrixState, stack->stack->len - 1);
83 * Operations like scale, translate, rotate etc need to have an
84 * initialized state->matrix to work with, so they will pass
87 * _cogl_matrix_stack_load_identity and _cogl_matrix_stack_set on the
88 * other hand don't so they will pass initialize = FALSE
90 * NB: Identity matrices are represented by setting
91 * state->is_identity=TRUE in which case state->matrix will be
94 static CoglMatrixState *
95 _cogl_matrix_stack_top_mutable (CoglMatrixStack *stack,
98 CoglMatrixState *state;
99 CoglMatrixState *new_top;
101 state = _cogl_matrix_stack_top (stack);
103 if (state->push_count == 0)
105 if (state->is_identity && initialize)
106 cogl_matrix_init_identity (&state->matrix);
110 state->push_count -= 1;
112 g_array_set_size (stack->stack, stack->stack->len + 1);
113 /* if g_array_set_size reallocs we need to get state
115 state = &g_array_index (stack->stack, CoglMatrixState,
116 stack->stack->len - 2);
117 new_top = _cogl_matrix_stack_top(stack);
118 _cogl_matrix_state_init (new_top);
122 if (state->is_identity)
123 cogl_matrix_init_identity (&new_top->matrix);
125 new_top->matrix = state->matrix;
132 _cogl_matrix_stack_new (void)
134 CoglMatrixStack *stack;
135 CoglMatrixState *state;
137 stack = g_slice_new0 (CoglMatrixStack);
139 stack->stack = g_array_sized_new (FALSE, FALSE,
140 sizeof (CoglMatrixState), 10);
141 g_array_set_size (stack->stack, 1);
142 state = &g_array_index (stack->stack, CoglMatrixState, 0);
143 _cogl_matrix_state_init (state);
144 state->is_identity = TRUE;
148 return _cogl_matrix_stack_object_new (stack);
152 _cogl_matrix_stack_free (CoglMatrixStack *stack)
154 g_array_free (stack->stack, TRUE);
155 g_slice_free (CoglMatrixStack, stack);
159 _cogl_matrix_stack_push (CoglMatrixStack *stack)
161 CoglMatrixState *state;
163 state = _cogl_matrix_stack_top (stack);
165 /* we lazily create a new stack top if someone changes the matrix
166 * while push_count > 0
168 state->push_count += 1;
172 _cogl_matrix_stack_pop (CoglMatrixStack *stack)
174 CoglMatrixState *state;
176 state = _cogl_matrix_stack_top (stack);
178 if (state->push_count > 0)
180 state->push_count -= 1;
184 if (stack->stack->len == 1)
186 g_warning ("Too many matrix pops");
191 g_array_set_size (stack->stack, stack->stack->len - 1);
196 _cogl_matrix_stack_load_identity (CoglMatrixStack *stack)
198 CoglMatrixState *state;
200 state = _cogl_matrix_stack_top_mutable (stack, FALSE);
202 /* NB: Identity matrices are represented by setting
203 * state->is_identity = TRUE and leaving state->matrix
206 * This is done to optimize the heavy usage of
207 * _cogl_matrix_stack_load_identity by the Cogl Journal.
209 if (!state->is_identity)
211 state->is_identity = TRUE;
217 _cogl_matrix_stack_scale (CoglMatrixStack *stack,
222 CoglMatrixState *state;
224 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
225 cogl_matrix_scale (&state->matrix, x, y, z);
226 state->is_identity = FALSE;
231 _cogl_matrix_stack_translate (CoglMatrixStack *stack,
236 CoglMatrixState *state;
238 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
239 cogl_matrix_translate (&state->matrix, x, y, z);
240 state->is_identity = FALSE;
245 _cogl_matrix_stack_rotate (CoglMatrixStack *stack,
251 CoglMatrixState *state;
253 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
254 cogl_matrix_rotate (&state->matrix, angle, x, y, z);
255 state->is_identity = FALSE;
260 _cogl_matrix_stack_multiply (CoglMatrixStack *stack,
261 const CoglMatrix *matrix)
263 CoglMatrixState *state;
265 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
266 cogl_matrix_multiply (&state->matrix, &state->matrix, matrix);
267 state->is_identity = FALSE;
272 _cogl_matrix_stack_frustum (CoglMatrixStack *stack,
280 CoglMatrixState *state;
282 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
283 cogl_matrix_frustum (&state->matrix,
284 left, right, bottom, top,
286 state->is_identity = FALSE;
291 _cogl_matrix_stack_perspective (CoglMatrixStack *stack,
297 CoglMatrixState *state;
299 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
300 cogl_matrix_perspective (&state->matrix,
301 fov_y, aspect, z_near, z_far);
302 state->is_identity = FALSE;
307 _cogl_matrix_stack_ortho (CoglMatrixStack *stack,
315 CoglMatrixState *state;
317 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
318 cogl_matrix_ortho (&state->matrix,
319 left, right, bottom, top, z_near, z_far);
320 state->is_identity = FALSE;
325 _cogl_matrix_stack_get_inverse (CoglMatrixStack *stack,
328 CoglMatrixState *state;
330 state = _cogl_matrix_stack_top_mutable (stack, TRUE);
332 return cogl_matrix_get_inverse (&state->matrix, inverse);
336 _cogl_matrix_stack_get (CoglMatrixStack *stack,
339 CoglMatrixState *state;
341 state = _cogl_matrix_stack_top (stack);
343 /* NB: identity matrices are lazily initialized because we can often avoid
344 * initializing them at all if nothing is pushed on top of them since we
345 * load them using glLoadIdentity()
347 * The Cogl journal typically loads an identiy matrix because it performs
348 * software transformations, which is why we have optimized this case.
350 if (state->is_identity)
351 cogl_matrix_init_identity (matrix);
353 *matrix = state->matrix;
357 _cogl_matrix_stack_set (CoglMatrixStack *stack,
358 const CoglMatrix *matrix)
360 CoglMatrixState *state;
362 state = _cogl_matrix_stack_top_mutable (stack, FALSE);
363 state->matrix = *matrix;
364 state->is_identity = FALSE;
369 _cogl_matrix_stack_flush_matrix_to_gl_builtin (CoglContext *ctx,
370 gboolean is_identity,
374 g_assert (ctx->driver == COGL_DRIVER_GL ||
375 ctx->driver == COGL_DRIVER_GLES1);
377 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
378 if (ctx->flushed_matrix_mode != mode)
384 case COGL_MATRIX_MODELVIEW:
385 gl_mode = GL_MODELVIEW;
388 case COGL_MATRIX_PROJECTION:
389 gl_mode = GL_PROJECTION;
392 case COGL_MATRIX_TEXTURE:
393 gl_mode = GL_TEXTURE;
397 GE (ctx, glMatrixMode (gl_mode));
398 ctx->flushed_matrix_mode = mode;
402 GE (ctx, glLoadIdentity ());
404 GE (ctx, glLoadMatrixf (cogl_matrix_get_array (matrix)));
409 _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx,
410 CoglMatrixStack *stack,
412 gboolean disable_flip)
414 g_assert (ctx->driver == COGL_DRIVER_GL ||
415 ctx->driver == COGL_DRIVER_GLES1);
417 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
420 CoglMatrixState *state;
421 CoglMatrixStackCache *cache;
423 state = _cogl_matrix_stack_top (stack);
425 if (mode == COGL_MATRIX_PROJECTION)
427 /* Because Cogl defines texture coordinates to have a top left
428 * origin and because offscreen framebuffers may be used for
429 * rendering to textures we always render upside down to
430 * offscreen buffers. Also for some backends we need to render
431 * onscreen buffers upside-down too.
436 needs_flip = cogl_is_offscreen (ctx->current_draw_buffer);
438 cache = &ctx->builtin_flushed_projection;
444 if (mode == COGL_MATRIX_MODELVIEW)
445 cache = &ctx->builtin_flushed_modelview;
450 /* We don't need to do anything if the state is the same */
452 _cogl_matrix_stack_check_and_update_cache (stack, cache, needs_flip))
454 gboolean is_identity = state->is_identity && !needs_flip;
458 CoglMatrix flipped_matrix;
460 cogl_matrix_multiply (&flipped_matrix,
463 &ctx->identity_matrix :
466 _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
473 _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
483 _cogl_matrix_stack_get_age (CoglMatrixStack *stack)
489 _cogl_matrix_stack_has_identity_flag (CoglMatrixStack *stack)
491 return _cogl_matrix_stack_top (stack)->is_identity;
495 _cogl_matrix_stack_equal (CoglMatrixStack *stack0,
496 CoglMatrixStack *stack1)
498 CoglMatrixState *state0 = _cogl_matrix_stack_top (stack0);
499 CoglMatrixState *state1 = _cogl_matrix_stack_top (stack1);
501 if (state0->is_identity != state1->is_identity)
504 if (state0->is_identity)
507 return cogl_matrix_equal (&state0->matrix, &state1->matrix);
511 _cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack,
512 CoglMatrixStackCache *cache,
515 gboolean is_identity =
516 _cogl_matrix_stack_has_identity_flag (stack) && !flip;
519 if (is_identity && cache->flushed_identity)
521 else if (cache->stack == NULL ||
522 cache->stack->age != cache->age ||
523 flip != cache->flipped)
526 is_dirty = (cache->stack != stack &&
527 !_cogl_matrix_stack_equal (cache->stack, stack));
529 /* We'll update the cache values even if the stack isn't dirty in
530 case the reason it wasn't dirty is because we compared the
531 matrices and found them to be the same. In that case updating the
532 cache values will avoid the comparison next time */
533 cache->age = stack->age;
534 cogl_object_ref (stack);
536 cogl_object_unref (cache->stack);
537 cache->stack = stack;
538 cache->flushed_identity = is_identity;
539 cache->flipped = flip;
545 _cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache)
548 cache->flushed_identity = FALSE;
552 _cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache)
555 cogl_object_unref (cache->stack);