mutter/clutter/cogl/common/cogl-current-matrix.c
Robert Bragg df4429462f [cogl matrix stack] Create a client side matrix stack for the projection matrix
The cost of glGetFloatv with Mesa is still representing a majority of our
time in OpenGL for some applications, and the last thing left using this is
the current-matrix API when getting the projection matrix.

This adds a matrix stack for the projection matrix, so all getting, setting
and modification of the projection matrix is now managed by Cogl and it's only
when we come to draw that we flush changes to the matrix to OpenGL.

This also brings us closer to being able to drop internal use of the
deprecated OpenGL matrix functions, re: commit 54159f5a1d
2009-07-07 10:32:56 +01:00

550 lines
14 KiB
C

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors:
* Havoc Pennington <hp@pobox.com> for litl
* Robert Bragg <robert@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-context.h"
#include "cogl-internal.h"
#include "cogl-current-matrix.h"
#include "cogl-matrix-stack.h"
#ifdef HAVE_COGL_GLES2
#include "cogl-gles2-wrapper.h"
#define glFrustum(L,R,B,T,N,F) \
cogl_wrap_glFrustumf((GLfloat)L, (GLfloat)R, (GLfloat)B, \
(GLfloat)T, (GLfloat)N, (GLfloat)F)
#elif defined (HAVE_COGL_GLES)
#define glFrustum(L,R,B,T,N,F) \
glFrustumf((GLfloat)L, (GLfloat)R, (GLfloat)B, \
(GLfloat)T, (GLfloat)N, (GLfloat)F)
#define glOrtho glOrthof
#endif
#include <string.h>
#include <math.h>
static void
_cogl_get_client_stack (CoglContext *ctx,
CoglMatrixMode mode,
CoglMatrixStack **current_stack_p)
{
if (ctx->modelview_stack &&
mode == COGL_MATRIX_MODELVIEW)
*current_stack_p = ctx->modelview_stack;
else if (ctx->projection_stack &&
mode == COGL_MATRIX_PROJECTION)
*current_stack_p = ctx->projection_stack;
else
*current_stack_p = NULL;
}
#define _COGL_GET_CONTEXT_AND_STACK(contextvar, stackvar, rval) \
CoglMatrixStack *stackvar; \
_COGL_GET_CONTEXT (contextvar, rval); \
_cogl_get_client_stack (contextvar, ctx->matrix_mode, &stackvar)
void
_cogl_set_current_matrix (CoglMatrixMode mode)
{
GLenum gl_mode;
CoglMatrixStack *current_stack; \
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (mode == ctx->matrix_mode)
return;
ctx->matrix_mode = mode;
/* If we have a client side stack then then the GL matrix mode only needs
* changing when we come to flush it to OpenGL */
_cogl_get_client_stack (ctx, mode, &current_stack);
if (current_stack)
return;
gl_mode = 0; /* silence compiler warning */
switch (mode)
{
case COGL_MATRIX_MODELVIEW:
gl_mode = GL_MODELVIEW;
break;
case COGL_MATRIX_PROJECTION:
gl_mode = GL_PROJECTION;
break;
case COGL_MATRIX_TEXTURE:
gl_mode = GL_TEXTURE;
break;
}
GE (glMatrixMode (gl_mode));
}
void
_cogl_current_matrix_push (void)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_push (current_stack);
else
GE (glPushMatrix ());
}
void
_cogl_current_matrix_pop (void)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_pop (current_stack);
else
GE (glPopMatrix ());
}
void
_cogl_current_matrix_identity (void)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_load_identity (current_stack);
else
GE (glLoadIdentity ());
}
void
_cogl_current_matrix_load (const CoglMatrix *matrix)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_set (current_stack, matrix);
else
GE (glLoadMatrixf (cogl_matrix_get_array (matrix)));
}
void
_cogl_current_matrix_multiply (const CoglMatrix *matrix)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_multiply (current_stack, matrix);
else
GE (glMultMatrixf (cogl_matrix_get_array (matrix)));
}
void
_cogl_current_matrix_rotate (float angle,
float x,
float y,
float z)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_rotate (current_stack, angle, x, y, z);
else
GE (glRotatef (angle, x, y, z));
}
void
_cogl_current_matrix_scale (float x,
float y,
float z)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_scale (current_stack, x, y, z);
else
GE (glScalef (x, y, z));
}
void
_cogl_current_matrix_translate (float x,
float y,
float z)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_translate (current_stack, x, y, z);
else
GE (glTranslatef (x, y, z));
}
void
_cogl_current_matrix_frustum (float left,
float right,
float bottom,
float top,
float near_val,
float far_val)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_frustum (current_stack,
left, right,
bottom, top,
near_val,
far_val);
else
GE (glFrustum (left, right, bottom, top, near_val, far_val));
}
void
_cogl_current_matrix_perspective (float fov_y,
float aspect,
float z_near,
float z_far)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_perspective (current_stack,
fov_y, aspect, z_near, z_far);
else
{
/* NB: There is no glPerspective() (only gluPerspective()) so we use
* cogl_matrix_perspective: */
CoglMatrix matrix;
_cogl_get_matrix (ctx->matrix_mode, &matrix);
cogl_matrix_perspective (&matrix,
fov_y, aspect, z_near, z_far);
_cogl_current_matrix_load (&matrix);
}
}
void
_cogl_current_matrix_ortho (float left,
float right,
float bottom,
float top,
float near_val,
float far_val)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack != NULL)
_cogl_matrix_stack_ortho (current_stack,
left, right,
bottom, top,
near_val,
far_val);
else
{
#ifdef HAVE_COGL_GLES2
/* NB: GLES 2 has no glOrtho(): */
CoglMatrix matrix;
_cogl_get_matrix (ctx->matrix_mode, &matrix);
cogl_matrix_ortho (&matrix,
left, right, bottom, top, near_val, far_val);
_cogl_current_matrix_load (&matrix);
#else
GE (glOrtho (left, right, bottom, top, near_val, far_val));
#endif
}
}
void
_cogl_get_matrix (CoglMatrixMode mode,
CoglMatrix *matrix)
{
CoglMatrixStack *current_stack;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_get_client_stack (ctx, mode, &current_stack);
if (current_stack)
_cogl_matrix_stack_get (current_stack, matrix);
else
{
GLenum gl_mode;
GLfloat gl_matrix[16];
gl_mode = 0; /* silence compiler warning */
switch (mode)
{
case COGL_MATRIX_MODELVIEW:
gl_mode = GL_MODELVIEW_MATRIX;
break;
case COGL_MATRIX_PROJECTION:
gl_mode = GL_PROJECTION_MATRIX;
break;
case COGL_MATRIX_TEXTURE:
gl_mode = GL_TEXTURE_MATRIX;
break;
}
/* Note: we have a redundant copy happening here. If that turns out to be
* a problem then, since this is internal to Cogl, we could pass the
* CoglMatrix pointer directly to glGetFloatv; the only problem with that
* is that if we later add internal flags to CoglMatrix they will need to
* be initialized seperatly.
*/
GE (glGetFloatv (gl_mode, gl_matrix));
cogl_matrix_init_from_array (matrix, gl_matrix);
}
}
void
_cogl_set_matrix (const CoglMatrix *matrix)
{
_cogl_current_matrix_load (matrix);
}
void
_cogl_current_matrix_state_init (void)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
ctx->matrix_mode = COGL_MATRIX_MODELVIEW;
ctx->modelview_stack = NULL;
ctx->projection_stack = NULL;
#if 0
if (ctx->indirect ||
cogl_debug_flags & COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES)
#endif
{
ctx->modelview_stack = _cogl_matrix_stack_new ();
ctx->projection_stack = _cogl_matrix_stack_new ();
}
}
void
_cogl_current_matrix_state_destroy (void)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack)
_cogl_matrix_stack_destroy (current_stack);
}
void
_cogl_current_matrix_state_flush (void)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->matrix_mode != COGL_MATRIX_MODELVIEW &&
ctx->matrix_mode != COGL_MATRIX_PROJECTION)
{
g_warning ("matrix state must be flushed in "
"MODELVIEW or PROJECTION mode");
return;
}
if (ctx->modelview_stack &&
ctx->matrix_mode == COGL_MATRIX_MODELVIEW)
{
_cogl_matrix_stack_flush_to_gl (ctx->modelview_stack,
GL_MODELVIEW);
}
else if (ctx->projection_stack &&
ctx->matrix_mode == COGL_MATRIX_PROJECTION)
{
_cogl_matrix_stack_flush_to_gl (ctx->projection_stack,
GL_PROJECTION);
}
}
void
_cogl_current_matrix_state_dirty (void)
{
_COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL);
if (current_stack)
_cogl_matrix_stack_dirty (current_stack);
}
void
cogl_push_matrix (void)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_push ();
}
void
cogl_pop_matrix (void)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_pop ();
}
void
cogl_scale (float x, float y, float z)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_scale (x, y, z);
}
void
cogl_translate (float x, float y, float z)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_translate (x, y, z);
}
void
cogl_rotate (float angle, float x, float y, float z)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_rotate (angle, x, y, z);
}
void
cogl_perspective (float fov_y,
float aspect,
float z_near,
float z_far)
{
float ymax = z_near * tanf (fov_y * G_PI / 360.0);
cogl_frustum (-ymax * aspect, /* left */
ymax * aspect, /* right */
-ymax, /* bottom */
ymax, /* top */
z_near,
z_far);
}
void
cogl_frustum (float left,
float right,
float bottom,
float top,
float z_near,
float z_far)
{
float c, d;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
_cogl_current_matrix_identity ();
_cogl_current_matrix_frustum (left,
right,
bottom,
top,
z_near,
z_far);
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (float) * 16);
c = - (z_far + z_near) / (z_far - z_near);
d = - (2 * (z_far * z_near)) / (z_far - z_near);
#define M(row,col) ctx->inverse_projection[col*4+row]
M(0,0) = (right - left) / (2 * z_near);
M(0,3) = (right + left) / (2 * z_near);
M(1,1) = (top - bottom) / (2 * z_near);
M(1,3) = (top + bottom) / (2 * z_near);
M(2,3) = -1.0;
M(3,2) = 1.0 / d;
M(3,3) = c / d;
#undef M
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
}
void
cogl_ortho (float left,
float right,
float bottom,
float top,
float z_near,
float z_far)
{
CoglMatrix ortho;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_matrix_init_identity (&ortho);
cogl_matrix_ortho (&ortho, left, right, bottom, top, z_near, z_far);
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
_cogl_current_matrix_load (&ortho);
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (float) * 16);
#define M(row,col) ctx->inverse_projection[col*4+row]
M(0,0) = 1.0 / ortho.xx;
M(0,3) = -ortho.xw;
M(1,1) = 1.0 / ortho.yy;
M(1,3) = -ortho.yw;
M(2,2) = 1.0 / ortho.zz;
M(2,3) = -ortho.zw;
M(3,3) = 1.0;
#undef M
}
void
cogl_get_modelview_matrix (CoglMatrix *matrix)
{
_cogl_get_matrix (COGL_MATRIX_MODELVIEW,
matrix);
}
void
cogl_set_modelview_matrix (CoglMatrix *matrix)
{
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_load (matrix);
}
void
cogl_get_projection_matrix (CoglMatrix *matrix)
{
_cogl_get_matrix (COGL_MATRIX_PROJECTION,
matrix);
}
void
cogl_set_projection_matrix (CoglMatrix *matrix)
{
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
_cogl_current_matrix_load (matrix);
}
void
_cogl_flush_matrix_stacks (void)
{
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
_cogl_current_matrix_state_flush ();
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
_cogl_current_matrix_state_flush ();
}