"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-matrix-stack.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2009,2010 Intel Corporation.
7  *
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.
12  *
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.
17  *
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/>.
20  *
21  *
22  *
23  * Authors:
24  *   Havoc Pennington <hp@pobox.com> for litl
25  *   Robert Bragg <robert@linux.intel.com>
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
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"
38
39 typedef struct {
40   CoglMatrix matrix;
41   gboolean is_identity;
42   /* count of pushes with no changes; when a change is
43    * requested, we create a new state and decrement this
44    */
45   int push_count;
46 } CoglMatrixState;
47
48 /**
49  * CoglMatrixStack:
50  *
51  * Stores a cogl-side matrix stack, which we use as a cache
52  * so we can get the matrix efficiently when using indirect
53  * rendering.
54  */
55 struct _CoglMatrixStack
56 {
57   CoglObject _parent;
58
59   GArray *stack;
60
61   unsigned int age;
62 };
63
64 static void _cogl_matrix_stack_free (CoglMatrixStack *stack);
65
66 COGL_OBJECT_INTERNAL_DEFINE (MatrixStack, matrix_stack);
67
68 /* XXX: this doesn't initialize the matrix! */
69 static void
70 _cogl_matrix_state_init (CoglMatrixState *state)
71 {
72   state->push_count = 0;
73   state->is_identity = FALSE;
74 }
75
76 static CoglMatrixState *
77 _cogl_matrix_stack_top (CoglMatrixStack *stack)
78 {
79   return &g_array_index (stack->stack, CoglMatrixState, stack->stack->len - 1);
80 }
81
82 /* XXX:
83  * Operations like scale, translate, rotate etc need to have an
84  * initialized state->matrix to work with, so they will pass
85  * initialize = TRUE.
86  *
87  * _cogl_matrix_stack_load_identity and _cogl_matrix_stack_set on the
88  * other hand don't so they will pass initialize = FALSE
89  *
90  * NB: Identity matrices are represented by setting
91  * state->is_identity=TRUE in which case state->matrix will be
92  * uninitialized.
93  */
94 static CoglMatrixState *
95 _cogl_matrix_stack_top_mutable (CoglMatrixStack *stack,
96                                 gboolean initialize)
97 {
98   CoglMatrixState *state;
99   CoglMatrixState *new_top;
100
101   state = _cogl_matrix_stack_top (stack);
102
103   if (state->push_count == 0)
104     {
105       if (state->is_identity && initialize)
106         cogl_matrix_init_identity (&state->matrix);
107       return state;
108     }
109
110   state->push_count -= 1;
111
112   g_array_set_size (stack->stack, stack->stack->len + 1);
113   /* if g_array_set_size reallocs we need to get state
114    * pointer again */
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);
119
120   if (initialize)
121     {
122       if (state->is_identity)
123         cogl_matrix_init_identity (&new_top->matrix);
124       else
125         new_top->matrix = state->matrix;
126     }
127
128   return new_top;
129 }
130
131 CoglMatrixStack*
132 _cogl_matrix_stack_new (void)
133 {
134   CoglMatrixStack *stack;
135   CoglMatrixState *state;
136
137   stack = g_slice_new0 (CoglMatrixStack);
138
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;
145
146   stack->age = 0;
147
148   return _cogl_matrix_stack_object_new (stack);
149 }
150
151 static void
152 _cogl_matrix_stack_free (CoglMatrixStack *stack)
153 {
154   g_array_free (stack->stack, TRUE);
155   g_slice_free (CoglMatrixStack, stack);
156 }
157
158 void
159 _cogl_matrix_stack_push (CoglMatrixStack *stack)
160 {
161   CoglMatrixState *state;
162
163   state = _cogl_matrix_stack_top (stack);
164
165   /* we lazily create a new stack top if someone changes the matrix
166    * while push_count > 0
167    */
168   state->push_count += 1;
169 }
170
171 void
172 _cogl_matrix_stack_pop (CoglMatrixStack *stack)
173 {
174   CoglMatrixState *state;
175
176   state = _cogl_matrix_stack_top (stack);
177
178   if (state->push_count > 0)
179     {
180       state->push_count -= 1;
181     }
182   else
183     {
184       if (stack->stack->len == 1)
185         {
186           g_warning ("Too many matrix pops");
187           return;
188         }
189
190       stack->age++;
191       g_array_set_size (stack->stack, stack->stack->len - 1);
192     }
193 }
194
195 void
196 _cogl_matrix_stack_load_identity (CoglMatrixStack *stack)
197 {
198   CoglMatrixState *state;
199
200   state = _cogl_matrix_stack_top_mutable (stack, FALSE);
201
202   /* NB: Identity matrices are represented by setting
203    * state->is_identity = TRUE and leaving state->matrix
204    * uninitialized.
205    *
206    * This is done to optimize the heavy usage of
207    * _cogl_matrix_stack_load_identity by the Cogl Journal.
208    */
209   if (!state->is_identity)
210     {
211       state->is_identity = TRUE;
212       stack->age++;
213     }
214 }
215
216 void
217 _cogl_matrix_stack_scale (CoglMatrixStack *stack,
218                           float            x,
219                           float            y,
220                           float            z)
221 {
222   CoglMatrixState *state;
223
224   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
225   cogl_matrix_scale (&state->matrix, x, y, z);
226   state->is_identity = FALSE;
227   stack->age++;
228 }
229
230 void
231 _cogl_matrix_stack_translate (CoglMatrixStack *stack,
232                               float            x,
233                               float            y,
234                               float            z)
235 {
236   CoglMatrixState *state;
237
238   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
239   cogl_matrix_translate (&state->matrix, x, y, z);
240   state->is_identity = FALSE;
241   stack->age++;
242 }
243
244 void
245 _cogl_matrix_stack_rotate (CoglMatrixStack *stack,
246                            float            angle,
247                            float            x,
248                            float            y,
249                            float            z)
250 {
251   CoglMatrixState *state;
252
253   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
254   cogl_matrix_rotate (&state->matrix, angle, x, y, z);
255   state->is_identity = FALSE;
256   stack->age++;
257 }
258
259 void
260 _cogl_matrix_stack_multiply (CoglMatrixStack  *stack,
261                              const CoglMatrix *matrix)
262 {
263   CoglMatrixState *state;
264
265   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
266   cogl_matrix_multiply (&state->matrix, &state->matrix, matrix);
267   state->is_identity = FALSE;
268   stack->age++;
269 }
270
271 void
272 _cogl_matrix_stack_frustum (CoglMatrixStack *stack,
273                             float            left,
274                             float            right,
275                             float            bottom,
276                             float            top,
277                             float            z_near,
278                             float            z_far)
279 {
280   CoglMatrixState *state;
281
282   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
283   cogl_matrix_frustum (&state->matrix,
284                        left, right, bottom, top,
285                        z_near, z_far);
286   state->is_identity = FALSE;
287   stack->age++;
288 }
289
290 void
291 _cogl_matrix_stack_perspective (CoglMatrixStack *stack,
292                                 float            fov_y,
293                                 float            aspect,
294                                 float            z_near,
295                                 float            z_far)
296 {
297   CoglMatrixState *state;
298
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;
303   stack->age++;
304 }
305
306 void
307 _cogl_matrix_stack_ortho (CoglMatrixStack *stack,
308                           float            left,
309                           float            right,
310                           float            bottom,
311                           float            top,
312                           float            z_near,
313                           float            z_far)
314 {
315   CoglMatrixState *state;
316
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;
321   stack->age++;
322 }
323
324 gboolean
325 _cogl_matrix_stack_get_inverse (CoglMatrixStack *stack,
326                                 CoglMatrix      *inverse)
327 {
328   CoglMatrixState *state;
329
330   state = _cogl_matrix_stack_top_mutable (stack, TRUE);
331
332   return cogl_matrix_get_inverse (&state->matrix, inverse);
333 }
334
335 void
336 _cogl_matrix_stack_get (CoglMatrixStack *stack,
337                         CoglMatrix      *matrix)
338 {
339   CoglMatrixState *state;
340
341   state = _cogl_matrix_stack_top (stack);
342
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()
346    *
347    * The Cogl journal typically loads an identiy matrix because it performs
348    * software transformations, which is why we have optimized this case.
349    */
350   if (state->is_identity)
351     cogl_matrix_init_identity (matrix);
352   else
353     *matrix = state->matrix;
354 }
355
356 void
357 _cogl_matrix_stack_set (CoglMatrixStack  *stack,
358                         const CoglMatrix *matrix)
359 {
360   CoglMatrixState *state;
361
362   state = _cogl_matrix_stack_top_mutable (stack, FALSE);
363   state->matrix = *matrix;
364   state->is_identity = FALSE;
365   stack->age++;
366 }
367
368 static void
369 _cogl_matrix_stack_flush_matrix_to_gl_builtin (CoglContext *ctx,
370                                                gboolean is_identity,
371                                                CoglMatrix *matrix,
372                                                CoglMatrixMode mode)
373 {
374   g_assert (ctx->driver == COGL_DRIVER_GL ||
375             ctx->driver == COGL_DRIVER_GLES1);
376
377 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
378   if (ctx->flushed_matrix_mode != mode)
379     {
380       GLenum gl_mode = 0;
381
382       switch (mode)
383         {
384         case COGL_MATRIX_MODELVIEW:
385           gl_mode = GL_MODELVIEW;
386           break;
387
388         case COGL_MATRIX_PROJECTION:
389           gl_mode = GL_PROJECTION;
390           break;
391
392         case COGL_MATRIX_TEXTURE:
393           gl_mode = GL_TEXTURE;
394           break;
395         }
396
397       GE (ctx, glMatrixMode (gl_mode));
398       ctx->flushed_matrix_mode = mode;
399     }
400
401   if (is_identity)
402     GE (ctx, glLoadIdentity ());
403   else
404     GE (ctx, glLoadMatrixf (cogl_matrix_get_array (matrix)));
405 #endif
406 }
407
408 void
409 _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx,
410                                          CoglMatrixStack *stack,
411                                          CoglMatrixMode mode,
412                                          gboolean disable_flip)
413 {
414   g_assert (ctx->driver == COGL_DRIVER_GL ||
415             ctx->driver == COGL_DRIVER_GLES1);
416
417 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
418   {
419     gboolean needs_flip;
420     CoglMatrixState *state;
421     CoglMatrixStackCache *cache;
422
423     state = _cogl_matrix_stack_top (stack);
424
425     if (mode == COGL_MATRIX_PROJECTION)
426       {
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.
432          */
433         if (disable_flip)
434           needs_flip = FALSE;
435         else
436           needs_flip = cogl_is_offscreen (ctx->current_draw_buffer);
437
438         cache = &ctx->builtin_flushed_projection;
439       }
440     else
441       {
442         needs_flip = FALSE;
443
444         if (mode == COGL_MATRIX_MODELVIEW)
445           cache = &ctx->builtin_flushed_modelview;
446         else
447           cache = NULL;
448       }
449
450     /* We don't need to do anything if the state is the same */
451     if (!cache ||
452         _cogl_matrix_stack_check_and_update_cache (stack, cache, needs_flip))
453       {
454         gboolean is_identity = state->is_identity && !needs_flip;
455
456         if (needs_flip)
457           {
458             CoglMatrix flipped_matrix;
459
460             cogl_matrix_multiply (&flipped_matrix,
461                                   &ctx->y_flip_matrix,
462                                   state->is_identity ?
463                                   &ctx->identity_matrix :
464                                   &state->matrix);
465
466             _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
467                                                            /* not identity */
468                                                            FALSE,
469                                                            &flipped_matrix,
470                                                            mode);
471           }
472         else
473           _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
474                                                          is_identity,
475                                                          &state->matrix,
476                                                          mode);
477       }
478   }
479 #endif
480 }
481
482 unsigned int
483 _cogl_matrix_stack_get_age (CoglMatrixStack *stack)
484 {
485   return stack->age;
486 }
487
488 gboolean
489 _cogl_matrix_stack_has_identity_flag (CoglMatrixStack *stack)
490 {
491   return _cogl_matrix_stack_top (stack)->is_identity;
492 }
493
494 gboolean
495 _cogl_matrix_stack_equal (CoglMatrixStack *stack0,
496                           CoglMatrixStack *stack1)
497 {
498   CoglMatrixState *state0 = _cogl_matrix_stack_top (stack0);
499   CoglMatrixState *state1 = _cogl_matrix_stack_top (stack1);
500
501   if (state0->is_identity != state1->is_identity)
502     return FALSE;
503
504   if (state0->is_identity)
505     return TRUE;
506   else
507     return cogl_matrix_equal (&state0->matrix, &state1->matrix);
508 }
509
510 gboolean
511 _cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack,
512                                            CoglMatrixStackCache *cache,
513                                            gboolean flip)
514 {
515   gboolean is_identity =
516     _cogl_matrix_stack_has_identity_flag (stack) && !flip;
517   gboolean is_dirty;
518
519   if (is_identity && cache->flushed_identity)
520     is_dirty = FALSE;
521   else if (cache->stack == NULL ||
522            cache->stack->age != cache->age ||
523            flip != cache->flipped)
524     is_dirty = TRUE;
525   else
526     is_dirty = (cache->stack != stack &&
527                 !_cogl_matrix_stack_equal (cache->stack, stack));
528
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);
535   if (cache->stack)
536     cogl_object_unref (cache->stack);
537   cache->stack = stack;
538   cache->flushed_identity = is_identity;
539   cache->flipped = flip;
540
541   return is_dirty;
542 }
543
544 void
545 _cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache)
546 {
547   cache->stack = NULL;
548   cache->flushed_identity = FALSE;
549 }
550
551 void
552 _cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache)
553 {
554   if (cache->stack)
555     cogl_object_unref (cache->stack);
556 }