Re-design the matrix stack using a graph of ops

This re-designs the matrix stack so we now keep track of each separate
operation such as rotating, scaling, translating and multiplying as
immutable, ref-counted nodes in a graph.

Being a "graph" here means that different transformations composed of
a sequence of linked operation nodes may share nodes.

The first node in a matrix-stack is always a LOAD_IDENTITY operation.

As an example consider if an application where to draw three rectangles
A, B and C something like this:

cogl_framebuffer_scale (fb, 2, 2, 2);
cogl_framebuffer_push_matrix(fb);

  cogl_framebuffer_translate (fb, 10, 0, 0);

  cogl_framebuffer_push_matrix(fb);

    cogl_framebuffer_rotate (fb, 45, 0, 0, 1);
    cogl_framebuffer_draw_rectangle (...); /* A */

  cogl_framebuffer_pop_matrix(fb);

  cogl_framebuffer_draw_rectangle (...); /* B */

cogl_framebuffer_pop_matrix(fb);

cogl_framebuffer_push_matrix(fb);
  cogl_framebuffer_set_modelview_matrix (fb, &mv);
  cogl_framebuffer_draw_rectangle (...); /* C */
cogl_framebuffer_pop_matrix(fb);

That would result in a graph of nodes like this:

LOAD_IDENTITY
      |
    SCALE
    /     \
SAVE       LOAD
  |           |
TRANSLATE    RECTANGLE(C)
  |     \
SAVE    RECTANGLE(B)
  |
ROTATE
  |
RECTANGLE(A)

Each push adds a SAVE operation which serves as a marker to rewind too
when a corresponding pop is issued and also each SAVE node may also
store a cached matrix representing the composition of all its ancestor
nodes. This means if we repeatedly need to resolve a real CoglMatrix
for a given node then we don't need to repeat the composition.

Some advantages of this design are:
- A single pointer to any node in the graph can now represent a
  complete, immutable transformation that can be logged for example
  into a journal. Previously we were storing a full CoglMatrix in
  each journal entry which is 16 floats for the matrix itself as well
  as space for flags and another 16 floats for possibly storing a
  cache of the inverse. This means that we significantly reduce
  the size of the journal when drawing lots of primitives and we also
  avoid copying over 128 bytes per entry.
- It becomes much cheaper to check for equality. In cases where some
  (unlikely) false negatives are allowed simply comparing the pointers
  of two matrix stack graph entries is enough. Previously we would use
  memcmp() to compare matrices.
- It becomes easier to do comparisons of transformations. By looking
  for the common ancestry between nodes we can determine the operations
  that differentiate the transforms and use those to gain a high level
  understanding of the differences. For example we use this in the
  journal to be able to efficiently determine when two rectangle
  transforms only differ by some translation so that we can perform
  software clipping.

Reviewed-by: Neil Roberts <neil@linux.intel.com>

(cherry picked from commit f75aee93f6b293ca7a7babbd8fcc326ee6bf7aef)
This commit is contained in:
Robert Bragg
2012-02-20 15:59:48 +00:00
parent 882ff5612b
commit e3d6bc36d3
23 changed files with 1495 additions and 813 deletions

View File

@ -3,7 +3,7 @@
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2009,2010 Intel Corporation.
* Copyright (C) 2009,2010,2012 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -29,29 +29,114 @@
#ifndef __COGL_MATRIX_STACK_H
#define __COGL_MATRIX_STACK_H
#include "cogl-object-private.h"
#include "cogl-matrix.h"
#include "cogl-context.h"
#include "cogl-framebuffer.h"
typedef struct _CoglMatrixStack CoglMatrixStack;
typedef struct
typedef enum _CoglMatrixOp
{
CoglMatrixStack *stack;
unsigned int age;
COGL_MATRIX_OP_LOAD_IDENTITY,
COGL_MATRIX_OP_TRANSLATE,
COGL_MATRIX_OP_ROTATE,
COGL_MATRIX_OP_SCALE,
COGL_MATRIX_OP_MULTIPLY,
COGL_MATRIX_OP_LOAD,
COGL_MATRIX_OP_SAVE,
} CoglMatrixOp;
typedef struct _CoglMatrixEntry CoglMatrixEntry;
struct _CoglMatrixEntry
{
CoglMatrixOp op;
CoglMatrixEntry *parent;
unsigned int ref_count;
/* used for performance tracing */
int composite_gets;
};
typedef struct _CoglMatrixEntryTranslate
{
CoglMatrixEntry _parent_data;
float x;
float y;
float z;
} CoglMatrixEntryTranslate;
typedef struct _CoglMatrixEntryRotate
{
CoglMatrixEntry _parent_data;
float angle;
float x;
float y;
float z;
} CoglMatrixEntryRotate;
typedef struct _CoglMatrixEntryScale
{
CoglMatrixEntry _parent_data;
float x;
float y;
float z;
} CoglMatrixEntryScale;
typedef struct _CoglMatrixEntryMultiply
{
CoglMatrixEntry _parent_data;
CoglMatrix *matrix;
} CoglMatrixEntryMultiply;
typedef struct _CoglMatrixEntryLoad
{
CoglMatrixEntry _parent_data;
CoglMatrix *matrix;
} CoglMatrixEntryLoad;
typedef struct _CoglMatrixEntrySave
{
CoglMatrixEntry _parent_data;
CoglBool cache_valid;
CoglMatrix *cache;
} CoglMatrixEntrySave;
typedef union _CoglMatrixEntryFull
{
CoglMatrixEntry any;
CoglMatrixEntryTranslate translate;
CoglMatrixEntryRotate rotae;
CoglMatrixEntryScale scale;
CoglMatrixEntryMultiply multiply;
CoglMatrixEntryLoad load;
CoglMatrixEntrySave save;
} CoglMatrixEntryFull;
typedef struct _CoglMatrixStack
{
CoglObject _parent;
CoglMatrixEntry *last_entry;
} CoglMatrixStack;
typedef struct _CoglMatrixEntryCache
{
CoglMatrixEntry *entry;
CoglBool flushed_identity;
CoglBool flipped;
} CoglMatrixStackCache;
typedef enum {
COGL_MATRIX_MODELVIEW,
COGL_MATRIX_PROJECTION,
COGL_MATRIX_TEXTURE
} CoglMatrixMode;
typedef void (* CoglMatrixStackFlushFunc) (CoglContext *context,
CoglBool is_identity,
const CoglMatrix *matrix,
void *user_data);
} CoglMatrixEntryCache;
CoglMatrixStack *
_cogl_matrix_stack_new (void);
@ -62,6 +147,9 @@ _cogl_matrix_stack_push (CoglMatrixStack *stack);
void
_cogl_matrix_stack_pop (CoglMatrixStack *stack);
void
_cogl_matrix_entry_identity_init (CoglMatrixEntry *entry);
void
_cogl_matrix_stack_load_identity (CoglMatrixStack *stack);
@ -99,53 +187,89 @@ _cogl_matrix_stack_perspective (CoglMatrixStack *stack,
float z_near,
float z_far);
void
_cogl_matrix_stack_ortho (CoglMatrixStack *stack,
float left,
float right,
float bottom,
float top,
float z_near,
float z_far);
_cogl_matrix_stack_orthographic (CoglMatrixStack *stack,
float x_1,
float y_1,
float x_2,
float y_2,
float near,
float far);
CoglBool
_cogl_matrix_stack_get_inverse (CoglMatrixStack *stack,
CoglMatrix *inverse);
void
/* NB: This function only *sometimes* returns a pointer to a matrix if
* the matrix returned didn't need to be composed of multiple
* operations */
CoglMatrix *
_cogl_matrix_stack_get (CoglMatrixStack *stack,
CoglMatrix *matrix);
/* NB: This function only *sometimes* returns a pointer to a matrix if
* the matrix returned didn't need to be composed of multiple
* operations */
CoglMatrix *
_cogl_matrix_entry_get (CoglMatrixEntry *entry,
CoglMatrix *matrix);
void
_cogl_matrix_stack_set (CoglMatrixStack *stack,
const CoglMatrix *matrix);
CoglBool
_cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0,
CoglMatrixEntry *entry1,
float *x,
float *y,
float *z);
/* If this returns TRUE then the entry is definitely the identity
* matrix. If it returns FALSE it may or may not be the identity
* matrix but no expensive comparison is performed to verify it. */
CoglBool
_cogl_matrix_entry_has_identity_flag (CoglMatrixEntry *entry);
CoglBool
_cogl_matrix_entry_fast_equal (CoglMatrixEntry *entry0,
CoglMatrixEntry *entry1);
CoglBool
_cogl_matrix_entry_equal (CoglMatrixEntry *entry0,
CoglMatrixEntry *entry1);
void
_cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx,
CoglMatrixStack *stack,
_cogl_matrix_entry_print (CoglMatrixEntry *entry);
CoglMatrixEntry *
_cogl_matrix_entry_ref (CoglMatrixEntry *entry);
void
_cogl_matrix_entry_unref (CoglMatrixEntry *entry);
typedef enum {
COGL_MATRIX_MODELVIEW,
COGL_MATRIX_PROJECTION,
COGL_MATRIX_TEXTURE
} CoglMatrixMode;
void
_cogl_matrix_entry_flush_to_gl_builtins (CoglContext *ctx,
CoglMatrixEntry *entry,
CoglMatrixMode mode,
CoglFramebuffer *framebuffer,
CoglBool disable_flip);
unsigned int
_cogl_matrix_stack_get_age (CoglMatrixStack *stack);
/* If this returns TRUE then the top of the matrix is definitely the
identity matrix. If it returns FALSE it may or may not be the
identity matrix but no expensive comparison is performed to verify
it. */
CoglBool
_cogl_matrix_stack_has_identity_flag (CoglMatrixStack *stack);
void
_cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache);
CoglBool
_cogl_matrix_stack_equal (CoglMatrixStack *stack0,
CoglMatrixStack *stack1);
_cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache,
CoglMatrixEntry *entry,
CoglBool flip);
void
_cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache);
CoglBool
_cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack,
CoglMatrixStackCache *cache,
CoglBool flip);
void
_cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache);
_cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache);
CoglBool
_cogl_is_matrix_stack (void *object);