diff --git a/cogl/cogl-attribute.c b/cogl/cogl-attribute.c index 2b1a986f4..57e285dbc 100644 --- a/cogl/cogl-attribute.c +++ b/cogl/cogl-attribute.c @@ -674,7 +674,10 @@ _cogl_flush_attributes_state (CoglFramebuffer *framebuffer, _cogl_pipeline_apply_legacy_state (pipeline); } - _cogl_pipeline_flush_gl_state (pipeline, skip_gl_color, n_tex_coord_attribs); + _cogl_pipeline_flush_gl_state (pipeline, + framebuffer, + skip_gl_color, + n_tex_coord_attribs); _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp); _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp); diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index e5703c251..eb3c38be9 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -46,6 +46,7 @@ #include "cogl-primitive-private.h" #include "cogl1-context.h" #include "cogl-offscreen.h" +#include "cogl-matrix-stack.h" #ifndef GL_CLIP_PLANE0 #define GL_CLIP_PLANE0 0x3000 @@ -113,9 +114,10 @@ set_clip_plane (CoglFramebuffer *framebuffer, /* Clip planes can only be used when a fixed function backend is in use so we know we can directly push this matrix to the builtin state */ - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - modelview_stack, + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + modelview_stack->last_entry, COGL_MATRIX_MODELVIEW, + framebuffer, FALSE /* don't disable flip */); planef[0] = 0; @@ -147,13 +149,12 @@ set_clip_plane (CoglFramebuffer *framebuffer, static void set_clip_planes (CoglFramebuffer *framebuffer, + CoglMatrixEntry *modelview_entry, float x_1, - float y_1, - float x_2, - float y_2) + float y_1, + float x_2, + float y_2) { - CoglMatrixStack *modelview_stack = - _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrix modelview_matrix; CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); @@ -167,7 +168,7 @@ set_clip_planes (CoglFramebuffer *framebuffer, float vertex_br[4] = { x_2, y_2, 0, 1.0 }; _cogl_matrix_stack_get (projection_stack, &projection_matrix); - _cogl_matrix_stack_get (modelview_stack, &modelview_matrix); + _cogl_matrix_entry_get (modelview_entry, &modelview_matrix); cogl_matrix_multiply (&modelview_projection, &projection_matrix, @@ -207,23 +208,24 @@ set_clip_planes (CoglFramebuffer *framebuffer, static void add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, + CoglMatrixEntry *modelview_entry, float x_1, float y_1, float x_2, float y_2, CoglBool first) { - CoglMatrixStack *modelview_stack = - _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); - /* This can be called from the journal code which doesn't flush - the matrix stacks between calls so we need to ensure they're - flushed now */ - _cogl_context_set_current_projection (ctx, projection_stack); - _cogl_context_set_current_modelview (ctx, modelview_stack); + /* NB: This can be called while flushing the journal so we need + * to be very conservative with what state we change. + */ + + _cogl_context_set_current_projection_entry (ctx, + projection_stack->last_entry); + _cogl_context_set_current_modelview_entry (ctx, modelview_entry); if (first) { @@ -256,21 +258,12 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, rectangle are set will be valid */ GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); - _cogl_matrix_stack_push (projection_stack); - _cogl_matrix_stack_load_identity (projection_stack); - - _cogl_matrix_stack_push (modelview_stack); - _cogl_matrix_stack_load_identity (modelview_stack); - - _cogl_context_set_current_projection (ctx, projection_stack); - _cogl_context_set_current_modelview (ctx, modelview_stack); + _cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry); + _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); - - _cogl_matrix_stack_pop (modelview_stack); - _cogl_matrix_stack_pop (projection_stack); } /* Restore the stencil mode */ @@ -285,6 +278,7 @@ typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer, static void add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, SilhouettePaintCallback silhouette_callback, + CoglMatrixEntry *modelview_entry, float bounds_x1, float bounds_y1, float bounds_x2, @@ -293,19 +287,19 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, CoglBool need_clear, void *user_data) { - CoglMatrixStack *modelview_stack = - _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); - /* This can be called from the clip stack code which doesn't flush - the matrix stacks between calls so we need to ensure they're - flushed now */ - _cogl_context_set_current_projection (ctx, projection_stack); - _cogl_context_set_current_modelview (ctx, modelview_stack); + /* NB: This can be called while flushing the journal so we need + * to be very conservative with what state we change. + */ - _cogl_pipeline_flush_gl_state (ctx->stencil_pipeline, FALSE, 0); + _cogl_context_set_current_projection_entry (ctx, + projection_stack->last_entry); + _cogl_context_set_current_modelview_entry (ctx, modelview_entry); + + _cogl_pipeline_flush_gl_state (ctx->stencil_pipeline, framebuffer, FALSE, 0); GE( ctx, glEnable (GL_STENCIL_TEST) ); @@ -360,19 +354,13 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ - _cogl_matrix_stack_push (projection_stack); - _cogl_matrix_stack_load_identity (projection_stack); - - _cogl_matrix_stack_push (modelview_stack); - _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry); + _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); - - _cogl_matrix_stack_pop (modelview_stack); - _cogl_matrix_stack_pop (projection_stack); } GE (ctx, glStencilMask (~(GLuint) 0)); @@ -400,6 +388,7 @@ paint_path_silhouette (CoglFramebuffer *framebuffer, static void add_stencil_clip_path (CoglFramebuffer *framebuffer, + CoglMatrixEntry *modelview_entry, CoglPath *path, CoglBool merge, CoglBool need_clear) @@ -407,6 +396,7 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer, CoglPathData *data = path->data; add_stencil_clip_silhouette (framebuffer, paint_path_silhouette, + modelview_entry, data->path_nodes_min.x, data->path_nodes_min.y, data->path_nodes_max.x, @@ -432,6 +422,7 @@ paint_primitive_silhouette (CoglFramebuffer *framebuffer, static void add_stencil_clip_primitive (CoglFramebuffer *framebuffer, + CoglMatrixEntry *modelview_entry, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, @@ -442,6 +433,7 @@ add_stencil_clip_primitive (CoglFramebuffer *framebuffer, { add_stencil_clip_silhouette (framebuffer, paint_primitive_silhouette, + modelview_entry, bounds_x1, bounds_y1, bounds_x2, @@ -500,32 +492,49 @@ _cogl_clip_stack_push_entry (CoglClipStack *clip_stack, return entry; } +static void +get_transformed_corners (float x_1, + float y_1, + float x_2, + float y_2, + CoglMatrix *modelview, + CoglMatrix *projection, + const float *viewport, + float *transformed_corners) +{ + int i; + + transformed_corners[0] = x_1; + transformed_corners[1] = y_1; + transformed_corners[2] = x_2; + transformed_corners[3] = y_1; + transformed_corners[4] = x_2; + transformed_corners[5] = y_2; + transformed_corners[6] = x_1; + transformed_corners[7] = y_2; + + + /* Project the coordinates to window space coordinates */ + for (i = 0; i < 4; i++) + { + float *v = transformed_corners + i * 2; + _cogl_transform_point (modelview, projection, viewport, v, v + 1); + } +} + /* Sets the window-space bounds of the entry based on the projected coordinates of the given rectangle */ static void _cogl_clip_stack_entry_set_bounds (CoglClipStack *entry, - float x_1, - float y_1, - float x_2, - float y_2, - const CoglMatrix *modelview) + float *transformed_corners) { - CoglMatrix projection; - float viewport[4]; - float verts[4 * 2] = { x_1, y_1, x_2, y_1, x_2, y_2, x_1, y_2 }; float min_x = G_MAXFLOAT, min_y = G_MAXFLOAT; float max_x = -G_MAXFLOAT, max_y = -G_MAXFLOAT; int i; - cogl_get_projection_matrix (&projection); - cogl_get_viewport (viewport); - for (i = 0; i < 4; i++) { - float *v = verts + i * 2; - - /* Project the coordinates to window space coordinates */ - _cogl_transform_point (modelview, &projection, viewport, v, v + 1); + float *v = transformed_corners + i * 2; if (v[0] > max_x) max_x = v[0]; @@ -570,11 +579,28 @@ _cogl_clip_stack_push_rectangle (CoglClipStack *stack, float y_1, float x_2, float y_2, - const CoglMatrix *modelview_matrix) + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) { CoglClipStackRect *entry; - CoglMatrix matrix_p; - float v[4]; + CoglMatrix modelview; + CoglMatrix projection; + CoglMatrix modelview_projection; + + /* Corners of the given rectangle in an clockwise order: + * (0, 1) (2, 3) + * + * + * + * (6, 7) (4, 5) + */ + float rect[] = { + x_1, y_1, + x_2, y_1, + x_2, y_2, + x_1, y_2 + }; /* Make a new entry */ entry = _cogl_clip_stack_push_entry (stack, @@ -586,34 +612,46 @@ _cogl_clip_stack_push_rectangle (CoglClipStack *stack, entry->x1 = x_2; entry->y1 = y_2; - entry->matrix = *modelview_matrix; + entry->matrix_entry = _cogl_matrix_entry_ref (modelview_entry); - /* If the modelview meets these constraints then a transformed rectangle - * should still be a rectangle when it reaches screen coordinates. + _cogl_matrix_entry_get (modelview_entry, &modelview); + _cogl_matrix_entry_get (projection_entry, &projection); + + cogl_matrix_multiply (&modelview_projection, + &projection, + &modelview); + + /* Technically we could avoid the viewport transform at this point + * if we want to make this a bit faster. */ + _cogl_transform_point (&modelview, &projection, viewport, &rect[0], &rect[1]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[2], &rect[3]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[4], &rect[5]); + _cogl_transform_point (&modelview, &projection, viewport, &rect[6], &rect[7]); + + /* If the fully transformed rectangle isn't still axis aligned we + * can't handle it using a scissor. * - * FIXME: we are are making certain assumptions about the projection - * matrix a.t.m and should really be looking at the combined modelview - * and projection matrix. - * FIXME: we don't consider rotations that are a multiple of 90 degrees - * which could be quite common. + * We don't use an epsilon here since we only really aim to catch + * simple cases where the transform doesn't leave the rectangle screen + * aligned and don't mind some false positives. */ - if (modelview_matrix->xy != 0 || modelview_matrix->xz != 0 || - modelview_matrix->yx != 0 || modelview_matrix->yz != 0 || - modelview_matrix->zx != 0 || modelview_matrix->zy != 0) + if (rect[0] != rect[6] || + rect[1] != rect[3] || + rect[2] != rect[4] || + rect[7] != rect[5]) { entry->can_be_scissor = FALSE; + _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, - x_1, y_1, x_2, y_2, modelview_matrix); + rect); } else { CoglClipStack *base_entry = (CoglClipStack *) entry; - - cogl_get_projection_matrix (&matrix_p); - cogl_get_viewport (v); - - _cogl_transform_point (modelview_matrix, &matrix_p, v, &x_1, &y_1); - _cogl_transform_point (modelview_matrix, &matrix_p, v, &x_2, &y_2); + x_1 = rect[0]; + y_1 = rect[1]; + x_2 = rect[4]; + y_2 = rect[5]; /* Consider that the modelview matrix may flip the rectangle * along the x or y axis... */ @@ -637,7 +675,9 @@ _cogl_clip_stack_push_rectangle (CoglClipStack *stack, CoglClipStack * _cogl_clip_stack_push_from_path (CoglClipStack *stack, CoglPath *path, - const CoglMatrix *modelview_matrix) + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) { float x_1, y_1, x_2, y_2; @@ -650,10 +690,15 @@ _cogl_clip_stack_push_from_path (CoglClipStack *stack, return _cogl_clip_stack_push_rectangle (stack, x_1, y_1, x_2, y_2, - modelview_matrix); + modelview_entry, + projection_entry, + viewport); else { CoglClipStackPath *entry; + CoglMatrix modelview; + CoglMatrix projection; + float transformed_corners[8]; entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackPath), @@ -661,10 +706,18 @@ _cogl_clip_stack_push_from_path (CoglClipStack *stack, entry->path = cogl_path_copy (path); - entry->matrix = *modelview_matrix; + entry->matrix_entry = _cogl_matrix_entry_ref (modelview_entry); + _cogl_matrix_entry_get (modelview_entry, &modelview); + _cogl_matrix_entry_get (projection_entry, &projection); + + get_transformed_corners (x_1, y_1, x_2, y_2, + &modelview, + &projection, + viewport, + transformed_corners); _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, - x_1, y_1, x_2, y_2, modelview_matrix); + transformed_corners); return (CoglClipStack *) entry; } @@ -677,9 +730,14 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack, float bounds_y1, float bounds_x2, float bounds_y2, - const CoglMatrix *modelview_matrix) + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport) { CoglClipStackPrimitive *entry; + CoglMatrix modelview; + CoglMatrix projection; + float transformed_corners[8]; entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackPrimitive), @@ -687,18 +745,26 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack, entry->primitive = cogl_object_ref (primitive); - entry->matrix = *modelview_matrix; + entry->matrix_entry = _cogl_matrix_entry_ref (modelview_entry); entry->bounds_x1 = bounds_x1; entry->bounds_y1 = bounds_y1; entry->bounds_x2 = bounds_x2; entry->bounds_y2 = bounds_y2; + _cogl_matrix_entry_get (modelview_entry, &modelview); + _cogl_matrix_entry_get (modelview_entry, &projection); + + get_transformed_corners (bounds_x1, bounds_y1, bounds_x2, bounds_y2, + &modelview, + &projection, + viewport, + transformed_corners); + /* NB: this is referring to the bounds in window coordinates as opposed * to the bounds above in primitive local coordinates. */ _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, - bounds_x1, bounds_y1, bounds_x2, bounds_y2, - modelview_matrix); + transformed_corners); return (CoglClipStack *) entry; } @@ -726,23 +792,33 @@ _cogl_clip_stack_unref (CoglClipStack *entry) switch (entry->type) { case COGL_CLIP_STACK_RECT: - g_slice_free1 (sizeof (CoglClipStackRect), entry); - break; - + { + CoglClipStackRect *rect = (CoglClipStackRect *) entry; + _cogl_matrix_entry_unref (rect->matrix_entry); + g_slice_free1 (sizeof (CoglClipStackRect), entry); + break; + } case COGL_CLIP_STACK_WINDOW_RECT: g_slice_free1 (sizeof (CoglClipStackWindowRect), entry); break; case COGL_CLIP_STACK_PATH: - cogl_object_unref (((CoglClipStackPath *) entry)->path); - g_slice_free1 (sizeof (CoglClipStackPath), entry); - break; - + { + CoglClipStackPath *path_entry = (CoglClipStackPath *) entry; + _cogl_matrix_entry_unref (path_entry->matrix_entry); + cogl_object_unref (path_entry->path); + g_slice_free1 (sizeof (CoglClipStackPath), entry); + break; + } case COGL_CLIP_STACK_PRIMITIVE: - cogl_object_unref (((CoglClipStackPrimitive *) entry)->primitive); - g_slice_free1 (sizeof (CoglClipStackPrimitive), entry); - break; - + { + CoglClipStackPrimitive *primitive_entry = + (CoglClipStackPrimitive *) entry; + _cogl_matrix_entry_unref (primitive_entry->matrix_entry); + cogl_object_unref (primitive_entry->primitive); + g_slice_free1 (sizeof (CoglClipStackPrimitive), entry); + break; + } default: g_assert_not_reached (); } @@ -804,6 +880,7 @@ void _cogl_clip_stack_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer) { + CoglContext *ctx = framebuffer->context; int has_clip_planes; CoglBool using_clip_planes = FALSE; CoglBool using_stencil_buffer = FALSE; @@ -811,12 +888,9 @@ _cogl_clip_stack_flush (CoglClipStack *stack, int scissor_y0; int scissor_x1; int scissor_y1; - CoglMatrixStack *modelview_stack; CoglClipStack *entry; int scissor_y_start; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* If we have already flushed this state then we don't need to do anything */ if (ctx->current_clip_stack_valid) @@ -830,9 +904,6 @@ _cogl_clip_stack_flush (CoglClipStack *stack, ctx->current_clip_stack_valid = TRUE; ctx->current_clip_stack = _cogl_clip_stack_ref (stack); - modelview_stack = - _cogl_framebuffer_get_modelview_stack (framebuffer); - has_clip_planes = ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES; @@ -905,16 +976,12 @@ _cogl_clip_stack_flush (CoglClipStack *stack, COGL_NOTE (CLIPPING, "Adding stencil clip for path"); - _cogl_matrix_stack_push (modelview_stack); - _cogl_matrix_stack_set (modelview_stack, &path_entry->matrix); - add_stencil_clip_path (framebuffer, + path_entry->matrix_entry, path_entry->path, using_stencil_buffer, TRUE); - _cogl_matrix_stack_pop (modelview_stack); - using_stencil_buffer = TRUE; break; } @@ -925,10 +992,8 @@ _cogl_clip_stack_flush (CoglClipStack *stack, COGL_NOTE (CLIPPING, "Adding stencil clip for primitive"); - _cogl_matrix_stack_push (modelview_stack); - _cogl_matrix_stack_set (modelview_stack, &primitive_entry->matrix); - add_stencil_clip_primitive (framebuffer, + primitive_entry->matrix_entry, primitive_entry->primitive, primitive_entry->bounds_x1, primitive_entry->bounds_y1, @@ -937,8 +1002,6 @@ _cogl_clip_stack_flush (CoglClipStack *stack, using_stencil_buffer, TRUE); - _cogl_matrix_stack_pop (modelview_stack); - using_stencil_buffer = TRUE; break; } @@ -950,9 +1013,6 @@ _cogl_clip_stack_flush (CoglClipStack *stack, rectangle was entirely described by its scissor bounds */ if (!rect->can_be_scissor) { - _cogl_matrix_stack_push (modelview_stack); - _cogl_matrix_stack_set (modelview_stack, &rect->matrix); - /* If we support clip planes and we haven't already used them then use that instead */ if (has_clip_planes) @@ -961,6 +1021,7 @@ _cogl_clip_stack_flush (CoglClipStack *stack, "Adding clip planes clip for rectangle"); set_clip_planes (framebuffer, + rect->matrix_entry, rect->x0, rect->y0, rect->x1, @@ -974,6 +1035,7 @@ _cogl_clip_stack_flush (CoglClipStack *stack, COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle"); add_stencil_clip_rectangle (framebuffer, + rect->matrix_entry, rect->x0, rect->y0, rect->x1, @@ -981,8 +1043,6 @@ _cogl_clip_stack_flush (CoglClipStack *stack, !using_stencil_buffer); using_stencil_buffer = TRUE; } - - _cogl_matrix_stack_pop (modelview_stack); } break; } diff --git a/cogl/cogl-clip-stack.h b/cogl/cogl-clip-stack.h index a9673d93a..f89a9c34d 100644 --- a/cogl/cogl-clip-stack.h +++ b/cogl/cogl-clip-stack.h @@ -28,6 +28,7 @@ #include "cogl-matrix.h" #include "cogl-primitive.h" #include "cogl-framebuffer.h" +#include "cogl-matrix-stack.h" /* The clip stack works like a GSList where only a pointer to the top of the stack is stored. The empty clip stack is represented simply @@ -112,13 +113,13 @@ struct _CoglClipStack struct _CoglClipStackRect { - CoglClipStack _parent_data; + CoglClipStack _parent_data; /* The rectangle for this clip */ - float x0; - float y0; - float x1; - float y1; + float x0; + float y0; + float x1; + float y1; /* If this is true then the clip for this rectangle is entirely described by the scissor bounds. This implies that the rectangle @@ -128,15 +129,15 @@ struct _CoglClipStackRect modelview matrix is that same as when a rectangle is added to the journal. In that case we can use the original clip coordinates and modify the rectangle instead. */ - CoglBool can_be_scissor; + CoglBool can_be_scissor; /* The matrix that was current when the clip was set */ - CoglMatrix matrix; + CoglMatrixEntry *matrix_entry; }; struct _CoglClipStackWindowRect { - CoglClipStack _parent_data; + CoglClipStack _parent_data; /* The window rect clip doesn't need any specific data because it just adds to the scissor clip */ @@ -144,12 +145,12 @@ struct _CoglClipStackWindowRect struct _CoglClipStackPath { - CoglClipStack _parent_data; + CoglClipStack _parent_data; /* The matrix that was current when the clip was set */ - CoglMatrix matrix; + CoglMatrixEntry *matrix_entry; - CoglPath *path; + CoglPath *path; }; struct _CoglClipStackPrimitive @@ -157,7 +158,7 @@ struct _CoglClipStackPrimitive CoglClipStack _parent_data; /* The matrix that was current when the clip was set */ - CoglMatrix matrix; + CoglMatrixEntry *matrix_entry; CoglPrimitive *primitive; @@ -180,12 +181,16 @@ _cogl_clip_stack_push_rectangle (CoglClipStack *stack, float y_1, float x_2, float y_2, - const CoglMatrix *modelview_matrix); + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport); CoglClipStack * _cogl_clip_stack_push_from_path (CoglClipStack *stack, CoglPath *path, - const CoglMatrix *modelview_matrix); + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport); CoglClipStack * _cogl_clip_stack_push_primitive (CoglClipStack *stack, @@ -194,7 +199,9 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack, float bounds_y1, float bounds_x2, float bounds_y2, - const CoglMatrix *modelview_matrix); + CoglMatrixEntry *modelview_entry, + CoglMatrixEntry *projection_entry, + const float *viewport); CoglClipStack * _cogl_clip_stack_pop (CoglClipStack *stack); diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h index 35ee09009..40f157330 100644 --- a/cogl/cogl-context-private.h +++ b/cogl/cogl-context-private.h @@ -110,14 +110,17 @@ struct _CoglContext calling it multiple times */ CoglMatrixMode flushed_matrix_mode; - /* The matrix stack that should be used for the next render */ - CoglMatrixStack *current_projection_stack; - CoglMatrixStack *current_modelview_stack; + /* The matrix stack entries that should be flushed during the next + * pipeline state flush */ + CoglMatrixEntry *current_projection_entry; + CoglMatrixEntry *current_modelview_entry; - /* The last matrix stack with age that was flushed to the GL matrix - builtins */ - CoglMatrixStackCache builtin_flushed_projection; - CoglMatrixStackCache builtin_flushed_modelview; + CoglMatrixEntry identity_entry; + + /* A cache of the last (immutable) matrix stack entries that were + * flushed to the GL matrix builtins */ + CoglMatrixEntryCache builtin_flushed_projection; + CoglMatrixEntryCache builtin_flushed_modelview; GArray *texture_units; int active_texture_unit; @@ -336,11 +339,11 @@ if (ctxvar == NULL) return retval; #define NO_RETVAL void -_cogl_context_set_current_projection (CoglContext *context, - CoglMatrixStack *stack); +_cogl_context_set_current_projection_entry (CoglContext *context, + CoglMatrixEntry *entry); void -_cogl_context_set_current_modelview (CoglContext *context, - CoglMatrixStack *stack); +_cogl_context_set_current_modelview_entry (CoglContext *context, + CoglMatrixEntry *entry); #endif /* __COGL_CONTEXT_PRIVATE_H */ diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 3d386bc17..bdfd8a5dc 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -395,10 +395,11 @@ cogl_context_new (CoglDisplay *display, GE (context, glEnable (GL_ALPHA_TEST)); #endif - _context->current_modelview_stack = NULL; - _context->current_projection_stack = NULL; - _cogl_matrix_stack_init_cache (&_context->builtin_flushed_projection); - _cogl_matrix_stack_init_cache (&_context->builtin_flushed_modelview); + _context->current_modelview_entry = NULL; + _context->current_projection_entry = NULL; + _cogl_matrix_entry_identity_init (&_context->identity_entry); + _cogl_matrix_entry_cache_init (&_context->builtin_flushed_projection); + _cogl_matrix_entry_cache_init (&_context->builtin_flushed_modelview); default_texture_bitmap = cogl_bitmap_new_for_data (_context, @@ -429,7 +430,6 @@ cogl_context_new (CoglDisplay *display, cogl_object_unref (default_texture_bitmap); cogl_push_source (context->opaque_color_pipeline); - _cogl_pipeline_flush_gl_state (context->opaque_color_pipeline, FALSE, 0); context->atlases = NULL; g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook)); @@ -525,12 +525,12 @@ _cogl_context_free (CoglContext *context) g_slist_free (context->texture_types); g_slist_free (context->buffer_types); - if (_context->current_modelview_stack) - cogl_object_unref (_context->current_modelview_stack); - if (_context->current_projection_stack) - cogl_object_unref (_context->current_projection_stack); - _cogl_matrix_stack_destroy_cache (&context->builtin_flushed_projection); - _cogl_matrix_stack_destroy_cache (&context->builtin_flushed_modelview); + if (_context->current_modelview_entry) + _cogl_matrix_entry_unref (_context->current_modelview_entry); + if (_context->current_projection_entry) + _cogl_matrix_entry_unref (_context->current_projection_entry); + _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_projection); + _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_modelview); cogl_pipeline_cache_free (context->pipeline_cache); @@ -597,21 +597,21 @@ _cogl_context_update_features (CoglContext *context, } void -_cogl_context_set_current_projection (CoglContext *context, - CoglMatrixStack *stack) +_cogl_context_set_current_projection_entry (CoglContext *context, + CoglMatrixEntry *entry) { - cogl_object_ref (stack); - if (context->current_projection_stack) - cogl_object_unref (context->current_projection_stack); - context->current_projection_stack = stack; + _cogl_matrix_entry_ref (entry); + if (context->current_projection_entry) + _cogl_matrix_entry_unref (context->current_projection_entry); + context->current_projection_entry = entry; } void -_cogl_context_set_current_modelview (CoglContext *context, - CoglMatrixStack *stack) +_cogl_context_set_current_modelview_entry (CoglContext *context, + CoglMatrixEntry *entry) { - cogl_object_ref (stack); - if (context->current_modelview_stack) - cogl_object_unref (context->current_modelview_stack); - context->current_modelview_stack = stack; + _cogl_matrix_entry_ref (entry); + if (context->current_modelview_entry) + _cogl_matrix_entry_unref (context->current_modelview_entry); + context->current_modelview_entry = entry; } diff --git a/cogl/cogl-debug-options.h b/cogl/cogl-debug-options.h index 4b177aedc..76d7232bb 100644 --- a/cogl/cogl-debug-options.h +++ b/cogl/cogl-debug-options.h @@ -185,3 +185,8 @@ OPT (CLIPPING, "clipping", N_("Trace clipping"), N_("Logs information about how Cogl is implementing clipping")) +OPT (PERFORMANCE, + N_("Cogl Tracing"), + "performance", + N_("Trace performance concerns"), + N_("Tries to highlight sub-optimal Cogl usage.")) diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index f3cb46f52..63856d4fd 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -56,7 +56,8 @@ static const GDebugKey cogl_log_debug_keys[] = { { "texture-pixmap", COGL_DEBUG_TEXTURE_PIXMAP }, { "bitmap", COGL_DEBUG_BITMAP }, { "clipping", COGL_DEBUG_CLIPPING }, - { "winsys", COGL_DEBUG_WINSYS } + { "winsys", COGL_DEBUG_WINSYS }, + { "performance", COGL_DEBUG_PERFORMANCE } }; static const int n_cogl_log_debug_keys = G_N_ELEMENTS (cogl_log_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index aa34ea86f..efb60e1ca 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -67,6 +67,7 @@ typedef enum { COGL_DEBUG_DISABLE_FAST_READ_PIXEL, COGL_DEBUG_CLIPPING, COGL_DEBUG_WINSYS, + COGL_DEBUG_PERFORMANCE, COGL_DEBUG_N_FLAGS } CoglDebugFlags; diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index 4e24e0387..1cc7ca0a4 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -1533,18 +1533,38 @@ _cogl_framebuffer_flush_dither_state (CoglFramebuffer *framebuffer) } } +static CoglMatrixEntry * +_cogl_framebuffer_get_modelview_entry (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + return modelview_stack->last_entry; +} + static void _cogl_framebuffer_flush_modelview_state (CoglFramebuffer *framebuffer) { - _cogl_context_set_current_modelview (framebuffer->context, - framebuffer->modelview_stack); + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + _cogl_context_set_current_modelview_entry (framebuffer->context, + modelview_entry); +} + +static CoglMatrixEntry * +_cogl_framebuffer_get_projection_entry (CoglFramebuffer *framebuffer) +{ + CoglMatrixStack *projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + return projection_stack->last_entry; } static void _cogl_framebuffer_flush_projection_state (CoglFramebuffer *framebuffer) { - _cogl_context_set_current_projection (framebuffer->context, - framebuffer->projection_stack); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + _cogl_context_set_current_projection_entry (framebuffer->context, + projection_entry); } static void @@ -2647,9 +2667,9 @@ void cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix) { - CoglMatrixStack *modelview_stack = - _cogl_framebuffer_get_modelview_stack (framebuffer); - _cogl_matrix_stack_get (modelview_stack, matrix); + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + _cogl_matrix_entry_get (modelview_entry, matrix); _COGL_MATRIX_DEBUG_PRINT (matrix); } @@ -2672,9 +2692,9 @@ void cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix) { - CoglMatrixStack *projection_stack = - _cogl_framebuffer_get_projection_stack (framebuffer); - _cogl_matrix_stack_get (projection_stack, matrix); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + _cogl_matrix_entry_get (projection_entry, matrix); _COGL_MATRIX_DEBUG_PRINT (matrix); } @@ -2724,14 +2744,25 @@ cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer, float y_2) { CoglClipState *clip_state = _cogl_framebuffer_get_clip_state (framebuffer); - CoglMatrix modelview_matrix; - - cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview_matrix); + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; clip_state->stacks->data = _cogl_clip_stack_push_rectangle (clip_state->stacks->data, x_1, y_1, x_2, y_2, - &modelview_matrix); + modelview_entry, + projection_entry, + viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= @@ -2743,14 +2774,25 @@ cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, CoglPath *path) { CoglClipState *clip_state = _cogl_framebuffer_get_clip_state (framebuffer); - CoglMatrix modelview_matrix; - - cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview_matrix); + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; clip_state->stacks->data = _cogl_clip_stack_push_from_path (clip_state->stacks->data, path, - &modelview_matrix); + modelview_entry, + projection_entry, + viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= @@ -2766,16 +2808,27 @@ cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, float bounds_y2) { CoglClipState *clip_state = _cogl_framebuffer_get_clip_state (framebuffer); - CoglMatrix modelview_matrix; - - cogl_get_modelview_matrix (&modelview_matrix); + CoglMatrixEntry *modelview_entry = + _cogl_framebuffer_get_modelview_entry (framebuffer); + CoglMatrixEntry *projection_entry = + _cogl_framebuffer_get_projection_entry (framebuffer); + /* XXX: It would be nicer if we stored the private viewport as a + * vec4 so we could avoid this redundant copy. */ + float viewport[] = { + framebuffer->viewport_x, + framebuffer->viewport_y, + framebuffer->viewport_width, + framebuffer->viewport_height + }; clip_state->stacks->data = _cogl_clip_stack_push_primitive (clip_state->stacks->data, primitive, bounds_x1, bounds_y1, bounds_x2, bounds_y2, - &modelview_matrix); + modelview_entry, + projection_entry, + viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h index e6e20153f..f5190f655 100644 --- a/cogl/cogl-journal-private.h +++ b/cogl/cogl-journal-private.h @@ -67,13 +67,10 @@ typedef struct _CoglJournalEntry { CoglPipeline *pipeline; int n_layers; - CoglMatrix model_view; + CoglMatrixEntry *modelview_entry; CoglClipStack *clip_stack; /* Offset into ctx->logged_vertices */ size_t array_offset; - /* XXX: These entries are pretty big now considering the padding in - * CoglPipelineFlushOptions and CoglMatrix, so we might need to optimize this - * later. */ } CoglJournalEntry; CoglJournal * diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index 4ae1eb020..5d2a09b5d 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -107,9 +107,6 @@ typedef struct _CoglJournalFlushState CoglIndices *indices; size_t indices_type_size; - CoglMatrixStack *modelview_stack; - CoglMatrixStack *projection_stack; - CoglPipeline *pipeline; } CoglJournalFlushState; @@ -297,11 +294,8 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, g_print ("BATCHING: modelview batch len = %d\n", batch_len); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) - { - _cogl_matrix_stack_set (state->modelview_stack, - &batch_start->model_view); - _cogl_context_set_current_modelview (ctx, state->modelview_stack); - } + _cogl_context_set_current_modelview_entry (ctx, + batch_start->modelview_entry); attributes = (CoglAttribute **)state->attributes->data; @@ -413,21 +407,7 @@ compare_entry_modelviews (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { /* Batch together quads with the same model view matrix */ - - /* FIXME: this is nasty, there are much nicer ways to track this - * (at the add_quad_vertices level) without resorting to a memcmp! - * - * E.g. If the cogl-current-matrix code maintained an "age" for - * the modelview matrix we could simply check in add_quad_vertices - * if the age has increased, and if so record the change as a - * boolean in the journal. - */ - - if (memcmp (&entry0->model_view, &entry1->model_view, - sizeof (GLfloat) * 16) == 0) - return TRUE; - else - return FALSE; + return entry0->modelview_entry == entry1->modelview_entry; } /* At this point we have a run of quads that we know have compatible @@ -702,6 +682,9 @@ _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, void *data) { CoglJournalFlushState *state = data; + CoglFramebuffer *framebuffer = state->journal->framebuffer; + CoglContext *ctx = framebuffer->context; + CoglMatrixStack *projection_stack; COGL_STATIC_TIMER (time_flush_clip_stack_pipeline_entries, "Journal Flush", /* parent */ @@ -710,15 +693,13 @@ _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, "pipeline + entries", 0 /* no application private data */); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - COGL_TIMER_START (_cogl_uprof_context, time_flush_clip_stack_pipeline_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: clip stack batch len = %d\n", batch_len); - _cogl_clip_stack_flush (batch_start->clip_stack, state->journal->framebuffer); + _cogl_clip_stack_flush (batch_start->clip_stack, framebuffer); /* XXX: Because we are manually flushing clip state here we need to * make sure that the clip state gets updated the next time we flush @@ -726,22 +707,21 @@ _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, * as changed. */ ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; - _cogl_matrix_stack_push (state->modelview_stack); - /* If we have transformed all our quads at log time then we ensure * no further model transform is applied by loading the identity * matrix here. We need to do this after flushing the clip stack - * because the clip stack flushing code can modify the matrix */ + * because the clip stack flushing code can modify the current + * modelview matrix entry */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))) - { - _cogl_matrix_stack_load_identity (state->modelview_stack); - _cogl_context_set_current_modelview (ctx, state->modelview_stack); - } + _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); - /* Setting up the clip state can sometimes also flush the projection - matrix so we should flush it again. This will be a no-op if the - clip code didn't modify the projection */ - _cogl_context_set_current_projection (ctx, state->projection_stack); + /* Setting up the clip state can sometimes also update the current + * projection matrix entry so we should update it again. This will have + * no affect if the clip code didn't modify the projection */ + projection_stack = + _cogl_framebuffer_get_projection_stack (framebuffer); + _cogl_context_set_current_projection_entry (ctx, + projection_stack->last_entry); batch_and_call (batch_start, batch_len, @@ -749,97 +729,10 @@ _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ data); - _cogl_matrix_stack_pop (state->modelview_stack); - COGL_TIMER_STOP (_cogl_uprof_context, time_flush_clip_stack_pipeline_entries); } -static CoglBool -calculate_translation (const CoglMatrix *a, - const CoglMatrix *b, - float *tx_p, - float *ty_p) -{ - float tx, ty; - int x, y; - - /* Assuming we had the original matrix in this form: - * - * [ a₁₁, a₁₂, a₁₃, a₁₄ ] - * [ a₂₁, a₂₂, a₂₃, a₂₄ ] - * a = [ a₃₁, a₃₂, a₃₃, a₃₄ ] - * [ a₄₁, a₄₂, a₄₃, a₄₄ ] - * - * then a translation of that matrix would be a multiplication by a - * matrix of this form: - * - * [ 1, 0, 0, x ] - * [ 0, 1, 0, y ] - * t = [ 0, 0, 1, 0 ] - * [ 0, 0, 0, 1 ] - * - * That would give us a matrix of this form. - * - * [ a₁₁, a₁₂, a₁₃, a₁₁ x + a₁₂ y + a₁₄ ] - * [ a₂₁, a₂₂, a₂₃, a₂₁ x + a₂₂ y + a₂₄ ] - * b = a ⋅ t = [ a₃₁, a₃₂, a₃₃, a₃₁ x + a₃₂ y + a₃₄ ] - * [ a₄₁, a₄₂, a₄₃, a₄₁ x + a₄₂ y + a₄₄ ] - * - * We can use the two equations from the top left of the matrix to - * work out the x and y translation given the two matrices: - * - * b₁₄ = a₁₁x + a₁₂y + a₁₄ - * b₂₄ = a₂₁x + a₂₂y + a₂₄ - * - * Rearranging gives us: - * - * a₁₂ b₂₄ - a₂₄ a₁₂ - * ----------------- + a₁₄ - b₁₄ - * a₂₂ - * x = --------------------------------- - * a₁₂ a₂₁ - * ------- - a₁₁ - * a₂₂ - * - * b₂₄ - a₂₁x - a₂₄ - * y = ---------------- - * a₂₂ - * - * Once we've worked out what x and y would be if this was a valid - * translation then we can simply verify that the rest of the matrix - * matches up. - */ - - /* The leftmost 3x4 part of the matrix shouldn't change by a - translation so we can just compare it directly */ - for (y = 0; y < 4; y++) - for (x = 0; x < 3; x++) - if ((&a->xx)[x * 4 + y] != (&b->xx)[x * 4 + y]) - return FALSE; - - tx = (((a->xy * b->yw - a->yw * a->xy) / a->yy + a->xw - b->xw) / - ((a->xy * a->yx) / a->yy - a->xx)); - ty = (b->yw - a->yx * tx - a->yw) / a->yy; - -#define APPROX_EQUAL(a, b) (fabsf ((a) - (b)) < 1e-6f) - - /* Check whether the 4th column of the matrices match up to the - calculation */ - if (!APPROX_EQUAL (b->xw, a->xx * tx + a->xy * ty + a->xw) || - !APPROX_EQUAL (b->yw, a->yx * tx + a->yy * ty + a->yw) || - !APPROX_EQUAL (b->zw, a->zx * tx + a->zy * ty + a->zw) || - !APPROX_EQUAL (b->ww, a->wx * tx + a->wy * ty + a->ww)) - return FALSE; - -#undef APPROX_EQUAL - - *tx_p = tx; - *ty_p = ty; - - return TRUE; -} - typedef struct { float x_1, y_1; @@ -887,13 +780,15 @@ can_software_clip_entry (CoglJournalEntry *journal_entry, { float rect_x1, rect_y1, rect_x2, rect_y2; CoglClipStackRect *clip_rect; - float tx, ty; + float tx, ty, tz; + CoglMatrixEntry *modelview_entry; clip_rect = (CoglClipStackRect *) clip_entry; - if (!calculate_translation (&clip_rect->matrix, - &journal_entry->model_view, - &tx, &ty)) + modelview_entry = journal_entry->modelview_entry; + if (!_cogl_matrix_entry_calculate_translation (clip_rect->matrix_entry, + modelview_entry, + &tx, &ty, &tz)) return FALSE; if (clip_rect->x0 < clip_rect->x1) @@ -1212,6 +1107,7 @@ upload_vertices (CoglJournal *journal, else { float v[8]; + CoglMatrix modelview; v[0] = vin[0]; v[1] = vin[1]; @@ -1222,7 +1118,8 @@ upload_vertices (CoglJournal *journal, v[6] = vin[array_stride]; v[7] = vin[1]; - cogl_matrix_transform_points (&entry->model_view, + _cogl_matrix_entry_get (entry->modelview_entry, &modelview); + cogl_matrix_transform_points (&modelview, 2, /* n_components */ sizeof (float) * 2, /* stride_in */ v, /* points_in */ @@ -1269,6 +1166,7 @@ _cogl_journal_discard (CoglJournal *journal) CoglJournalEntry *entry = &g_array_index (journal->entries, CoglJournalEntry, i); _cogl_pipeline_journal_unref (entry->pipeline); + _cogl_matrix_entry_unref (entry->modelview_entry); _cogl_clip_stack_unref (entry->clip_stack); } @@ -1357,25 +1255,27 @@ _cogl_journal_all_entries_within_bounds (CoglJournal *journal, void _cogl_journal_flush (CoglJournal *journal) { + CoglFramebuffer *framebuffer; + CoglContext *ctx; CoglJournalFlushState state; - int i; - CoglMatrixStack *modelview_stack; + int i; COGL_STATIC_TIMER (flush_timer, "Mainloop", /* parent */ "Journal Flush", "The time spent flushing the Cogl journal", 0 /* no application private data */); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (journal->entries->len == 0) return; + framebuffer = journal->framebuffer; + ctx = framebuffer->context; + /* The entries in this journal may depend on images in other * framebuffers which may require that we flush the journals * associated with those framebuffers before we can flush * this journal... */ - _cogl_framebuffer_flush_dependency_journals (journal->framebuffer); + _cogl_framebuffer_flush_dependency_journals (framebuffer); /* Note: we start the timer after flushing dependency journals so * that the timer isn't started recursively. */ @@ -1386,8 +1286,8 @@ _cogl_journal_flush (CoglJournal *journal) /* NB: the journal deals with flushing the modelview stack and clip state manually */ - _cogl_framebuffer_flush_state (journal->framebuffer, - journal->framebuffer, + _cogl_framebuffer_flush_state (framebuffer, + framebuffer, COGL_FRAMEBUFFER_STATE_ALL & ~(COGL_FRAMEBUFFER_STATE_MODELVIEW | COGL_FRAMEBUFFER_STATE_CLIP)); @@ -1396,12 +1296,6 @@ _cogl_journal_flush (CoglJournal *journal) state.attributes = ctx->journal_flush_attributes_array; - modelview_stack = - _cogl_framebuffer_get_modelview_stack (journal->framebuffer); - state.modelview_stack = modelview_stack; - state.projection_stack = - _cogl_framebuffer_get_projection_stack (journal->framebuffer); - if (G_UNLIKELY ((COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_CLIP)) == 0)) { /* We do an initial walk of the journal to analyse the clip stack @@ -1502,6 +1396,7 @@ _cogl_journal_log_quad (CoglJournal *journal, CoglPipeline *final_pipeline; CoglClipStack *clip_stack; CoglPipelineFlushOptions flush_options; + CoglMatrixStack *modelview_stack; COGL_STATIC_TIMER (log_timer, "Mainloop", /* parent */ "Journal Log", @@ -1599,8 +1494,9 @@ _cogl_journal_log_quad (CoglJournal *journal, if (G_UNLIKELY (final_pipeline != pipeline)) cogl_object_unref (final_pipeline); - cogl_framebuffer_get_modelview_matrix (framebuffer, - &entry->model_view); + modelview_stack = + _cogl_framebuffer_get_modelview_stack (framebuffer); + entry->modelview_entry = _cogl_matrix_entry_ref (modelview_stack->last_entry); _cogl_pipeline_foreach_layer_internal (pipeline, add_framebuffer_deps_cb, @@ -1622,6 +1518,7 @@ entry_to_screen_polygon (CoglFramebuffer *framebuffer, GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers); CoglMatrixStack *projection_stack; CoglMatrix projection; + CoglMatrix modelview; int i; float viewport[4]; @@ -1649,7 +1546,8 @@ entry_to_screen_polygon (CoglFramebuffer *framebuffer, * _cogl_transform_points utility... */ - cogl_matrix_transform_points (&entry->model_view, + _cogl_matrix_entry_get (entry->modelview_entry, &modelview); + cogl_matrix_transform_points (&modelview, 2, /* n_components */ sizeof (float) * 4, /* stride_in */ poly, /* points_in */ diff --git a/cogl/cogl-matrix-private.h b/cogl/cogl-matrix-private.h index 1bdea7bb0..ef39e84e5 100644 --- a/cogl/cogl-matrix-private.h +++ b/cogl/cogl-matrix-private.h @@ -41,6 +41,13 @@ G_BEGIN_DECLS void _cogl_matrix_print (const CoglMatrix *matrix); +void +_cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix); + +void +_cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, + const CoglMatrix *src); + G_END_DECLS #endif /* __COGL_MATRIX_PRIVATE_H */ diff --git a/cogl/cogl-matrix-stack.c b/cogl/cogl-matrix-stack.c index 64ff803b4..32b0cd7a0 100644 --- a/cogl/cogl-matrix-stack.c +++ b/cogl/cogl-matrix-stack.c @@ -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 @@ -16,13 +16,13 @@ * 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, see . - * - * + * License along with this library. If not, see + * . * * Authors: * Havoc Pennington for litl * Robert Bragg + * Neil Roberts */ #ifdef HAVE_CONFIG_H @@ -35,341 +35,674 @@ #include "cogl-framebuffer-private.h" #include "cogl-object-private.h" #include "cogl-offscreen.h" - -typedef struct { - CoglMatrix matrix; - CoglBool is_identity; - /* count of pushes with no changes; when a change is - * requested, we create a new state and decrement this - */ - int push_count; -} CoglMatrixState; - -/** - * CoglMatrixStack: - * - * Stores a cogl-side matrix stack, which we use as a cache - * so we can get the matrix efficiently when using indirect - * rendering. - */ -struct _CoglMatrixStack -{ - CoglObject _parent; - - GArray *stack; - - unsigned int age; -}; +#include "cogl-matrix-private.h" +#include "cogl-magazine-private.h" static void _cogl_matrix_stack_free (CoglMatrixStack *stack); COGL_OBJECT_INTERNAL_DEFINE (MatrixStack, matrix_stack); -/* XXX: this doesn't initialize the matrix! */ -static void -_cogl_matrix_state_init (CoglMatrixState *state) +static CoglMagazine *_cogl_matrix_stack_magazine; +static CoglMagazine *_cogl_matrix_stack_matrices_magazine; + +static void * +_cogl_matrix_stack_push_entry (CoglMatrixStack *stack, + size_t size, + CoglMatrixOp operation) { - state->push_count = 0; - state->is_identity = FALSE; -} + CoglMatrixEntry *entry = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_magazine); -static CoglMatrixState * -_cogl_matrix_stack_top (CoglMatrixStack *stack) -{ - return &g_array_index (stack->stack, CoglMatrixState, stack->stack->len - 1); -} + /* The new entry starts with a ref count of 1 because the stack + holds a reference to it as it is the top entry */ + entry->ref_count = 1; + entry->op = operation; + entry->parent = stack->last_entry; -/* XXX: - * Operations like scale, translate, rotate etc need to have an - * initialized state->matrix to work with, so they will pass - * initialize = TRUE. - * - * _cogl_matrix_stack_load_identity and _cogl_matrix_stack_set on the - * other hand don't so they will pass initialize = FALSE - * - * NB: Identity matrices are represented by setting - * state->is_identity=TRUE in which case state->matrix will be - * uninitialized. - */ -static CoglMatrixState * -_cogl_matrix_stack_top_mutable (CoglMatrixStack *stack, - CoglBool initialize) -{ - CoglMatrixState *state; - CoglMatrixState *new_top; + entry->composite_gets = 0; - state = _cogl_matrix_stack_top (stack); + stack->last_entry = entry; - if (state->push_count == 0) - { - if (state->is_identity && initialize) - cogl_matrix_init_identity (&state->matrix); - return state; - } + /* We don't need to take a reference to the parent from the entry + because the we are stealing the ref in the new stack top */ - state->push_count -= 1; - - g_array_set_size (stack->stack, stack->stack->len + 1); - /* if g_array_set_size reallocs we need to get state - * pointer again */ - state = &g_array_index (stack->stack, CoglMatrixState, - stack->stack->len - 2); - new_top = _cogl_matrix_stack_top(stack); - _cogl_matrix_state_init (new_top); - - if (initialize) - { - if (state->is_identity) - cogl_matrix_init_identity (&new_top->matrix); - else - new_top->matrix = state->matrix; - } - - return new_top; -} - -CoglMatrixStack* -_cogl_matrix_stack_new (void) -{ - CoglMatrixStack *stack; - CoglMatrixState *state; - - stack = g_slice_new0 (CoglMatrixStack); - - stack->stack = g_array_sized_new (FALSE, FALSE, - sizeof (CoglMatrixState), 10); - g_array_set_size (stack->stack, 1); - state = &g_array_index (stack->stack, CoglMatrixState, 0); - _cogl_matrix_state_init (state); - state->is_identity = TRUE; - - stack->age = 0; - - return _cogl_matrix_stack_object_new (stack); -} - -static void -_cogl_matrix_stack_free (CoglMatrixStack *stack) -{ - g_array_free (stack->stack, TRUE); - g_slice_free (CoglMatrixStack, stack); + return entry; } void -_cogl_matrix_stack_push (CoglMatrixStack *stack) +_cogl_matrix_entry_identity_init (CoglMatrixEntry *entry) { - CoglMatrixState *state; - - state = _cogl_matrix_stack_top (stack); - - /* we lazily create a new stack top if someone changes the matrix - * while push_count > 0 - */ - state->push_count += 1; -} - -void -_cogl_matrix_stack_pop (CoglMatrixStack *stack) -{ - CoglMatrixState *state; - - state = _cogl_matrix_stack_top (stack); - - if (state->push_count > 0) - { - state->push_count -= 1; - } - else - { - if (stack->stack->len == 1) - { - g_warning ("Too many matrix pops"); - return; - } - - stack->age++; - g_array_set_size (stack->stack, stack->stack->len - 1); - } + entry->ref_count = 1; + entry->op = COGL_MATRIX_OP_LOAD_IDENTITY; + entry->parent = NULL; + entry->composite_gets = 0; } void _cogl_matrix_stack_load_identity (CoglMatrixStack *stack) { - CoglMatrixState *state; - - state = _cogl_matrix_stack_top_mutable (stack, FALSE); - - /* NB: Identity matrices are represented by setting - * state->is_identity = TRUE and leaving state->matrix - * uninitialized. - * - * This is done to optimize the heavy usage of - * _cogl_matrix_stack_load_identity by the Cogl Journal. - */ - if (!state->is_identity) - { - state->is_identity = TRUE; - stack->age++; - } -} - -void -_cogl_matrix_stack_scale (CoglMatrixStack *stack, - float x, - float y, - float z) -{ - CoglMatrixState *state; - - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_scale (&state->matrix, x, y, z); - state->is_identity = FALSE; - stack->age++; + _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntry), + COGL_MATRIX_OP_LOAD_IDENTITY); } void _cogl_matrix_stack_translate (CoglMatrixStack *stack, - float x, - float y, - float z) + float x, + float y, + float z) { - CoglMatrixState *state; + CoglMatrixEntryTranslate *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_translate (&state->matrix, x, y, z); - state->is_identity = FALSE; - stack->age++; + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryTranslate), + COGL_MATRIX_OP_TRANSLATE); + + entry->x = x; + entry->y = y; + entry->z = z; } void _cogl_matrix_stack_rotate (CoglMatrixStack *stack, - float angle, - float x, - float y, - float z) + float angle, + float x, + float y, + float z) { - CoglMatrixState *state; + CoglMatrixEntryRotate *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_rotate (&state->matrix, angle, x, y, z); - state->is_identity = FALSE; - stack->age++; + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryRotate), + COGL_MATRIX_OP_ROTATE); + + entry->angle = angle; + entry->x = x; + entry->y = y; + entry->z = z; } void -_cogl_matrix_stack_multiply (CoglMatrixStack *stack, +_cogl_matrix_stack_scale (CoglMatrixStack *stack, + float x, + float y, + float z) +{ + CoglMatrixEntryScale *entry; + + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryScale), + COGL_MATRIX_OP_SCALE); + + entry->x = x; + entry->y = y; + entry->z = z; +} + +void +_cogl_matrix_stack_multiply (CoglMatrixStack *stack, const CoglMatrix *matrix) { - CoglMatrixState *state; + CoglMatrixEntryMultiply *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_multiply (&state->matrix, &state->matrix, matrix); - state->is_identity = FALSE; - stack->age++; + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryMultiply), + COGL_MATRIX_OP_MULTIPLY); + + entry->matrix = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_from_array (entry->matrix, (float *)matrix); +} + +void +_cogl_matrix_stack_set (CoglMatrixStack *stack, + const CoglMatrix *matrix) +{ + CoglMatrixEntryLoad *entry; + + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryLoad), + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_from_array (entry->matrix, (float *)matrix); } void _cogl_matrix_stack_frustum (CoglMatrixStack *stack, - float left, - float right, - float bottom, - float top, - float z_near, - float z_far) + float left, + float right, + float bottom, + float top, + float z_near, + float z_far) { - CoglMatrixState *state; + CoglMatrixEntryLoad *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_frustum (&state->matrix, + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryLoad), + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_frustum (entry->matrix, left, right, bottom, top, z_near, z_far); - state->is_identity = FALSE; - stack->age++; } void _cogl_matrix_stack_perspective (CoglMatrixStack *stack, - float fov_y, - float aspect, - float z_near, - float z_far) + float fov_y, + float aspect, + float z_near, + float z_far) { - CoglMatrixState *state; + CoglMatrixEntryLoad *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_perspective (&state->matrix, + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryLoad), + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_perspective (entry->matrix, fov_y, aspect, z_near, z_far); - state->is_identity = FALSE; - stack->age++; } 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) { - CoglMatrixState *state; + CoglMatrixEntryLoad *entry; - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - cogl_matrix_ortho (&state->matrix, - left, right, bottom, top, z_near, z_far); - state->is_identity = FALSE; - stack->age++; + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntryLoad), + COGL_MATRIX_OP_LOAD); + + entry->matrix = + _cogl_magazine_chunk_alloc (_cogl_matrix_stack_matrices_magazine); + + cogl_matrix_init_identity (entry->matrix); + cogl_matrix_orthographic (entry->matrix, + x_1, y_1, x_2, y_2, near, far); +} + +void +_cogl_matrix_stack_push (CoglMatrixStack *stack) +{ + CoglMatrixEntrySave *entry; + + entry = _cogl_matrix_stack_push_entry (stack, + sizeof (CoglMatrixEntrySave), + COGL_MATRIX_OP_SAVE); + + entry->cache_valid = FALSE; +} + +CoglMatrixEntry * +_cogl_matrix_entry_ref (CoglMatrixEntry *entry) +{ + /* A NULL pointer is considered a valid stack so we should accept + that as an argument */ + if (entry) + entry->ref_count++; + + return entry; +} + +void +_cogl_matrix_entry_unref (CoglMatrixEntry *entry) +{ + for (; entry && --entry->ref_count <= 0; entry = entry->parent) + { + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_TRANSLATE: + case COGL_MATRIX_OP_ROTATE: + case COGL_MATRIX_OP_SCALE: + break; + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *multiply = + (CoglMatrixEntryMultiply *)entry; + _cogl_magazine_chunk_free (_cogl_matrix_stack_matrices_magazine, + multiply->matrix); + break; + } + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + _cogl_magazine_chunk_free (_cogl_matrix_stack_matrices_magazine, + load->matrix); + break; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; + if (save->cache_valid) + _cogl_magazine_chunk_free (_cogl_matrix_stack_matrices_magazine, + save->cache); + break; + } + } + + _cogl_magazine_chunk_free (_cogl_matrix_stack_magazine, entry); + } +} + +void +_cogl_matrix_stack_pop (CoglMatrixStack *stack) +{ + CoglMatrixEntry *old_top; + CoglMatrixEntry *new_top; + + _COGL_RETURN_IF_FAIL (stack != NULL); + + old_top = stack->last_entry; + _COGL_RETURN_IF_FAIL (old_top != NULL); + + /* To pop we are moving the top of the stack to the old top's parent + * node. The stack always needs to have a reference to the top entry + * so we must take a reference to the new top. The stack would have + * previously had a reference to the old top so we need to decrease + * the ref count on that. We need to ref the new head first in case + * this stack was the only thing referencing the old top. In that + * case the call to _cogl_matrix_entry_unref will unref the parent. + */ + + /* Find the last save operation and remove it */ + + /* XXX: it would be an error to pop to the very beginning of the + * stack so we don't need to check for NULL pointer dereferencing. */ + for (new_top = old_top; + new_top->op != COGL_MATRIX_OP_SAVE; + new_top = new_top->parent) + ; + + new_top = new_top->parent; + _cogl_matrix_entry_ref (new_top); + + _cogl_matrix_entry_unref (old_top); + + stack->last_entry = new_top; } CoglBool _cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, - CoglMatrix *inverse) + CoglMatrix *inverse) { - CoglMatrixState *state; + CoglMatrix matrix; + CoglMatrix *internal = _cogl_matrix_stack_get (stack, &matrix); - state = _cogl_matrix_stack_top_mutable (stack, TRUE); - - return cogl_matrix_get_inverse (&state->matrix, inverse); -} - -void -_cogl_matrix_stack_get (CoglMatrixStack *stack, - CoglMatrix *matrix) -{ - CoglMatrixState *state; - - state = _cogl_matrix_stack_top (stack); - - /* NB: identity matrices are lazily initialized because we can often avoid - * initializing them at all if nothing is pushed on top of them since we - * load them using glLoadIdentity() - * - * The Cogl journal typically loads an identiy matrix because it performs - * software transformations, which is why we have optimized this case. - */ - if (state->is_identity) - cogl_matrix_init_identity (matrix); + if (internal) + return cogl_matrix_get_inverse (internal, inverse); else - *matrix = state->matrix; + return cogl_matrix_get_inverse (&matrix, inverse); } -void -_cogl_matrix_stack_set (CoglMatrixStack *stack, - const CoglMatrix *matrix) +/* In addition to writing the stack matrix into the give @matrix + * argument this function *may* sometimes also return a pointer + * to a matrix too so if we are querying the inverse matrix we + * should query from the return matrix so that the result can + * be cached within the stack. */ +CoglMatrix * +_cogl_matrix_entry_get (CoglMatrixEntry *entry, + CoglMatrix *matrix) { - CoglMatrixState *state; + int depth; + CoglMatrixEntry *current; + CoglMatrixEntry **children; + int i; - state = _cogl_matrix_stack_top_mutable (stack, FALSE); - state->matrix = *matrix; - state->is_identity = FALSE; - stack->age++; + for (depth = 0, current = entry; + current; + current = current->parent, depth++) + { + switch (current->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + cogl_matrix_init_identity (matrix); + goto initialized; + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current; + _cogl_matrix_init_from_matrix_without_inverse (matrix, + load->matrix); + goto initialized; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current; + if (!save->cache_valid) + { + CoglMagazine *matrices_magazine = + _cogl_matrix_stack_matrices_magazine; + save->cache = _cogl_magazine_chunk_alloc (matrices_magazine); + _cogl_matrix_entry_get (current->parent, save->cache); + save->cache_valid = TRUE; + } + _cogl_matrix_init_from_matrix_without_inverse (matrix, save->cache); + goto initialized; + } + default: + continue; + } + } + +initialized: + + if (depth == 0) + { + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_TRANSLATE: + case COGL_MATRIX_OP_ROTATE: + case COGL_MATRIX_OP_SCALE: + case COGL_MATRIX_OP_MULTIPLY: + return NULL; + + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + return load->matrix; + } + case COGL_MATRIX_OP_SAVE: + { + CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; + return save->cache; + } + } + g_warn_if_reached (); + return NULL; + } + +#ifdef COGL_ENABLE_DEBUG + if (!current) + { + g_warning ("Inconsistent matrix stack"); + return NULL; + } +#endif + + entry->composite_gets++; + + children = g_alloca (sizeof (CoglMatrixEntry) * depth); + + /* We need walk the list of entries from the init/load/save entry + * back towards the leaf node but the nodes don't link to their + * children so we need to re-walk them here to add to a separate + * array. */ + for (i = depth - 1, current = entry; + i >= 0 && current; + i--, current = current->parent) + { + children[i] = current; + } + + if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) && + entry->composite_gets >= 2) + { + COGL_NOTE (PERFORMANCE, + "Re-composing a matrix stack entry multiple times"); + } + + for (i = 0; i < depth; i++) + { + switch (children[i]->op) + { + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate = + (CoglMatrixEntryTranslate *)children[i]; + cogl_matrix_translate (matrix, + translate->x, + translate->y, + translate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate= + (CoglMatrixEntryRotate *)children[i]; + cogl_matrix_rotate (matrix, + rotate->angle, + rotate->x, + rotate->y, + rotate->z); + continue; + } + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale = + (CoglMatrixEntryScale *)children[i]; + cogl_matrix_scale (matrix, + scale->x, + scale->y, + scale->z); + continue; + } + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *multiply = + (CoglMatrixEntryMultiply *)children[i]; + cogl_matrix_multiply (matrix, matrix, multiply->matrix); + continue; + } + + case COGL_MATRIX_OP_LOAD_IDENTITY: + case COGL_MATRIX_OP_LOAD: + case COGL_MATRIX_OP_SAVE: + g_warn_if_reached (); + continue; + } + } + + return NULL; +} + +/* In addition to writing the stack matrix into the give @matrix + * argument this function *may* sometimes also return a pointer + * to a matrix too so if we are querying the inverse matrix we + * should query from the return matrix so that the result can + * be cached within the stack. */ +CoglMatrix * +_cogl_matrix_stack_get (CoglMatrixStack *stack, + CoglMatrix *matrix) +{ + return _cogl_matrix_entry_get (stack->last_entry, matrix); } static void -_cogl_matrix_stack_flush_matrix_to_gl_builtin (CoglContext *ctx, - CoglBool is_identity, - CoglMatrix *matrix, - CoglMatrixMode mode) +_cogl_matrix_stack_free (CoglMatrixStack *stack) +{ + _cogl_matrix_entry_unref (stack->last_entry); + g_slice_free (CoglMatrixStack, stack); +} + +CoglMatrixStack * +_cogl_matrix_stack_new (void) +{ + CoglMatrixStack *stack = g_slice_new (CoglMatrixStack); + + if (G_UNLIKELY (_cogl_matrix_stack_magazine == NULL)) + { + _cogl_matrix_stack_magazine = + _cogl_magazine_new (sizeof (CoglMatrixEntryFull), 20); + _cogl_matrix_stack_matrices_magazine = + _cogl_magazine_new (sizeof (CoglMatrix), 20); + } + + stack->last_entry = NULL; + + _cogl_matrix_stack_load_identity (stack); + + return _cogl_matrix_stack_object_new (stack); +} + +static CoglMatrixEntry * +_cogl_matrix_entry_skip_saves (CoglMatrixEntry *entry) +{ + /* We currently assume that every stack starts with an + * _OP_LOAD_IDENTITY so we don't need to worry about + * NULL pointer dereferencing here. */ + while (entry->op == COGL_MATRIX_OP_SAVE) + entry = entry->parent; + + return entry; +} + +CoglBool +_cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1, + float *x, + float *y, + float *z) +{ + GSList *head0 = NULL; + GSList *head1 = NULL; + CoglMatrixEntry *node0; + CoglMatrixEntry *node1; + int len0 = 0; + int len1 = 0; + int count; + GSList *common_ancestor0; + GSList *common_ancestor1; + + /* Algorithm: + * + * 1) Ignoring _OP_SAVE entries walk the ancestors of each entry to + * the root node or any non-translation node, adding a pointer to + * each ancestor node to two linked lists. + * + * 2) Compare the lists to find the nodes where they start to + * differ marking the common_ancestor node for each list. + * + * 3) For the list corresponding to entry0, start iterating after + * the common ancestor applying the negative of all translations + * to x, y and z. + * + * 4) For the list corresponding to entry1, start iterating after + * the common ancestor applying the positive of all translations + * to x, y and z. + * + * If we come across any non-translation operations during 3) or 4) + * then bail out returning FALSE. + */ + + for (node0 = entry0; node0; node0 = node0->parent) + { + GSList *link; + + if (node0->op == COGL_MATRIX_OP_SAVE) + continue; + + link = alloca (sizeof (GSList)); + link->next = head0; + link->data = node0; + head0 = link; + len0++; + + if (node0->op != COGL_MATRIX_OP_TRANSLATE) + break; + } + for (node1 = entry1; node1; node1 = node1->parent) + { + GSList *link; + + if (node1->op == COGL_MATRIX_OP_SAVE) + continue; + + link = alloca (sizeof (GSList)); + link->next = head1; + link->data = node1; + head1 = link; + len1++; + + if (node1->op != COGL_MATRIX_OP_TRANSLATE) + break; + } + + if (head0->data != head1->data) + return FALSE; + + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + count = MIN (len0, len1) - 1; + while (count--) + { + if (head0->data != head1->data) + break; + common_ancestor0 = head0; + common_ancestor1 = head1; + head0 = head0->next; + head1 = head1->next; + } + + *x = 0; + *y = 0; + *z = 0; + + for (head0 = common_ancestor0->next; head0; head0 = head0->next) + { + CoglMatrixEntryTranslate *translate; + + node0 = head0->data; + + if (node0->op != COGL_MATRIX_OP_TRANSLATE) + return FALSE; + + translate = (CoglMatrixEntryTranslate *)node0; + + *x = *x - translate->x; + *y = *y - translate->y; + *z = *z - translate->z; + } + for (head1 = common_ancestor1->next; head1; head1 = head1->next) + { + CoglMatrixEntryTranslate *translate; + + node1 = head1->data; + + if (node1->op != COGL_MATRIX_OP_TRANSLATE) + return FALSE; + + translate = (CoglMatrixEntryTranslate *)node1; + + *x = *x + translate->x; + *y = *y + translate->y; + *z = *z + translate->z; + } + + return TRUE; +} + +CoglBool +_cogl_matrix_entry_has_identity_flag (CoglMatrixEntry *entry) +{ + return entry ? entry->op == COGL_MATRIX_OP_LOAD_IDENTITY : FALSE; +} + +static void +_cogl_matrix_flush_to_gl_builtin (CoglContext *ctx, + CoglBool is_identity, + CoglMatrix *matrix, + CoglMatrixMode mode) { g_assert (ctx->driver == COGL_DRIVER_GL || ctx->driver == COGL_DRIVER_GLES1); @@ -406,9 +739,10 @@ _cogl_matrix_stack_flush_matrix_to_gl_builtin (CoglContext *ctx, } void -_cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx, - CoglMatrixStack *stack, +_cogl_matrix_entry_flush_to_gl_builtins (CoglContext *ctx, + CoglMatrixEntry *entry, CoglMatrixMode mode, + CoglFramebuffer *framebuffer, CoglBool disable_flip) { g_assert (ctx->driver == COGL_DRIVER_GL || @@ -417,10 +751,7 @@ _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx, #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES) { CoglBool needs_flip; - CoglMatrixState *state; - CoglMatrixStackCache *cache; - - state = _cogl_matrix_stack_top (stack); + CoglMatrixEntryCache *cache; if (mode == COGL_MATRIX_PROJECTION) { @@ -433,7 +764,7 @@ _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx, if (disable_flip) needs_flip = FALSE; else - needs_flip = cogl_is_offscreen (ctx->current_draw_buffer); + needs_flip = cogl_is_offscreen (framebuffer); cache = &ctx->builtin_flushed_projection; } @@ -449,9 +780,18 @@ _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx, /* We don't need to do anything if the state is the same */ if (!cache || - _cogl_matrix_stack_check_and_update_cache (stack, cache, needs_flip)) + _cogl_matrix_entry_cache_maybe_update (cache, entry, needs_flip)) { - CoglBool is_identity = state->is_identity && !needs_flip; + CoglBool is_identity; + CoglMatrix matrix; + + if (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY) + is_identity = TRUE; + else + { + is_identity = FALSE; + _cogl_matrix_entry_get (entry, &matrix); + } if (needs_flip) { @@ -459,98 +799,248 @@ _cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx, cogl_matrix_multiply (&flipped_matrix, &ctx->y_flip_matrix, - state->is_identity ? + is_identity ? &ctx->identity_matrix : - &state->matrix); + &matrix); - _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx, - /* not identity */ - FALSE, - &flipped_matrix, - mode); + _cogl_matrix_flush_to_gl_builtin (ctx, + /* not identity */ + FALSE, + &flipped_matrix, + mode); } else - _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx, - is_identity, - &state->matrix, - mode); + { + _cogl_matrix_flush_to_gl_builtin (ctx, + is_identity, + &matrix, + mode); + } } } #endif } -unsigned int -_cogl_matrix_stack_get_age (CoglMatrixStack *stack) +CoglBool +_cogl_matrix_entry_fast_equal (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1) { - return stack->age; + return entry0 == entry1; } CoglBool -_cogl_matrix_stack_has_identity_flag (CoglMatrixStack *stack) +_cogl_matrix_entry_equal (CoglMatrixEntry *entry0, + CoglMatrixEntry *entry1) { - return _cogl_matrix_stack_top (stack)->is_identity; -} + for (; + entry0 && entry1; + entry0 = entry0->parent, entry1 = entry1->parent) + { + entry0 = _cogl_matrix_entry_skip_saves (entry0); + entry1 = _cogl_matrix_entry_skip_saves (entry1); -CoglBool -_cogl_matrix_stack_equal (CoglMatrixStack *stack0, - CoglMatrixStack *stack1) -{ - CoglMatrixState *state0 = _cogl_matrix_stack_top (stack0); - CoglMatrixState *state1 = _cogl_matrix_stack_top (stack1); + if (entry0 == entry1) + return TRUE; - if (state0->is_identity != state1->is_identity) - return FALSE; + if (entry0->op != entry1->op) + return FALSE; - if (state0->is_identity) - return TRUE; - else - return cogl_matrix_equal (&state0->matrix, &state1->matrix); -} + switch (entry0->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + return TRUE; + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate0 = + (CoglMatrixEntryTranslate *)entry0; + CoglMatrixEntryTranslate *translate1 = + (CoglMatrixEntryTranslate *)entry1; + /* We could perhaps use an epsilon to compare here? + * I expect the false negatives are probaly never going to + * be a problem and this is a bit cheaper. */ + if (translate0->x != translate1->x || + translate0->y != translate1->y || + translate0->z != translate1->z) + return FALSE; + } + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate0 = + (CoglMatrixEntryRotate *)entry0; + CoglMatrixEntryRotate *rotate1 = + (CoglMatrixEntryRotate *)entry1; + if (rotate0->angle != rotate1->angle || + rotate0->x != rotate1->x || + rotate0->y != rotate1->y || + rotate0->z != rotate1->z) + return FALSE; + } + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale0 = (CoglMatrixEntryScale *)entry0; + CoglMatrixEntryScale *scale1 = (CoglMatrixEntryScale *)entry1; + if (scale0->x != scale1->x || + scale0->y != scale1->y || + scale0->z != scale1->z) + return FALSE; + } + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *mult0 = (CoglMatrixEntryMultiply *)entry0; + CoglMatrixEntryMultiply *mult1 = (CoglMatrixEntryMultiply *)entry1; + if (!cogl_matrix_equal (mult0->matrix, mult1->matrix)) + return FALSE; + } + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load0 = (CoglMatrixEntryLoad *)entry0; + CoglMatrixEntryLoad *load1 = (CoglMatrixEntryLoad *)entry1; + /* There's no need to check any further since an + * _OP_LOAD makes all the ancestors redundant as far as + * the final matrix value is concerned. */ + return cogl_matrix_equal (load0->matrix, load1->matrix); + } + case COGL_MATRIX_OP_SAVE: + /* We skip over saves above so we shouldn't see save entries */ + g_warn_if_reached (); + } + } -CoglBool -_cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack, - CoglMatrixStackCache *cache, - CoglBool flip) -{ - CoglBool is_identity = - _cogl_matrix_stack_has_identity_flag (stack) && !flip; - CoglBool is_dirty; - - if (is_identity && cache->flushed_identity) - is_dirty = FALSE; - else if (cache->stack == NULL || - cache->stack->age != cache->age || - flip != cache->flipped) - is_dirty = TRUE; - else - is_dirty = (cache->stack != stack && - !_cogl_matrix_stack_equal (cache->stack, stack)); - - /* We'll update the cache values even if the stack isn't dirty in - case the reason it wasn't dirty is because we compared the - matrices and found them to be the same. In that case updating the - cache values will avoid the comparison next time */ - cache->age = stack->age; - cogl_object_ref (stack); - if (cache->stack) - cogl_object_unref (cache->stack); - cache->stack = stack; - cache->flushed_identity = is_identity; - cache->flipped = flip; - - return is_dirty; + return FALSE; } void -_cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache) +_cogl_matrix_entry_print (CoglMatrixEntry *entry) { - cache->stack = NULL; + int depth; + CoglMatrixEntry *e; + CoglMatrixEntry **children; + int i; + + for (depth = 0, e = entry; e; e = e->parent) + depth++; + + children = g_alloca (sizeof (CoglMatrixEntry) * depth); + + for (i = depth - 1, e = entry; + i >= 0 && e; + i--, e = e->parent) + { + children[i] = e; + } + + g_print ("MatrixEntry %p =\n", entry); + + for (i = 0; i < depth; i++) + { + entry = children[i]; + + switch (entry->op) + { + case COGL_MATRIX_OP_LOAD_IDENTITY: + g_print (" LOAD IDENTITY\n"); + continue; + case COGL_MATRIX_OP_TRANSLATE: + { + CoglMatrixEntryTranslate *translate = + (CoglMatrixEntryTranslate *)entry; + g_print (" TRANSLATE X=%f Y=%f Z=%f\n", + translate->x, + translate->y, + translate->z); + continue; + } + case COGL_MATRIX_OP_ROTATE: + { + CoglMatrixEntryRotate *rotate = + (CoglMatrixEntryRotate *)entry; + g_print (" ROTATE ANGLE=%f X=%f Y=%f Z=%f\n", + rotate->angle, + rotate->x, + rotate->y, + rotate->z); + continue; + } + case COGL_MATRIX_OP_SCALE: + { + CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)entry; + g_print (" SCALE X=%f Y=%f Z=%f\n", + scale->x, + scale->y, + scale->z); + continue; + } + case COGL_MATRIX_OP_MULTIPLY: + { + CoglMatrixEntryMultiply *mult = (CoglMatrixEntryMultiply *)entry; + g_print (" MULT:\n"); + _cogl_matrix_prefix_print (" ", mult->matrix); + continue; + } + case COGL_MATRIX_OP_LOAD: + { + CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; + g_print (" LOAD:\n"); + _cogl_matrix_prefix_print (" ", load->matrix); + continue; + } + case COGL_MATRIX_OP_SAVE: + g_print (" SAVE\n"); + } + } +} + +void +_cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache) +{ + cache->entry = NULL; cache->flushed_identity = FALSE; } -void -_cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache) +/* NB: This function can report false negatives since it never does a + * deep comparison of the stack matrices. */ +CoglBool +_cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache, + CoglMatrixEntry *entry, + CoglBool flip) { - if (cache->stack) - cogl_object_unref (cache->stack); + CoglBool is_identity; + CoglBool updated = FALSE; + + if (cache->flipped != flip) + { + cache->flipped = flip; + updated = TRUE; + } + + is_identity = (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY); + if (cache->flushed_identity != is_identity) + { + cache->flushed_identity = is_identity; + updated = TRUE; + } + + if (cache->entry != entry) + { + _cogl_matrix_entry_ref (entry); + if (cache->entry) + _cogl_matrix_entry_unref (cache->entry); + cache->entry = entry; + + /* We want to make sure here that if the cache->entry and the + * given @entry are both identity matrices then even though they + * are different entries we don't want to consider this an + * update... + */ + updated |= !is_identity; + } + + return updated; +} + +void +_cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache) +{ + if (cache->entry) + _cogl_matrix_entry_unref (cache->entry); } diff --git a/cogl/cogl-matrix-stack.h b/cogl/cogl-matrix-stack.h index 49cd03e71..db957d6ca 100644 --- a/cogl/cogl-matrix-stack.h +++ b/cogl/cogl-matrix-stack.h @@ -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); diff --git a/cogl/cogl-matrix.c b/cogl/cogl-matrix.c index d22293e75..142cd65d8 100644 --- a/cogl/cogl-matrix.c +++ b/cogl/cogl-matrix.c @@ -360,11 +360,38 @@ _cogl_matrix_multiply_array (CoglMatrix *result, const float *array) * Called by _cogl_matrix_print() to print a matrix or its inverse. */ static void -print_matrix_floats (const float m[16]) +print_matrix_floats (const char *prefix, const float m[16]) { int i; for (i = 0;i < 4; i++) - g_print ("\t%f %f %f %f\n", m[i], m[4+i], m[8+i], m[12+i] ); + g_print ("%s\t%f %f %f %f\n", prefix, m[i], m[4+i], m[8+i], m[12+i] ); +} + +void +_cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix) +{ + if (!(matrix->flags & MAT_DIRTY_TYPE)) + { + _COGL_RETURN_IF_FAIL (matrix->type < COGL_MATRIX_N_TYPES); + g_print ("%sMatrix type: %s, flags: %x\n", + prefix, types[matrix->type], (int)matrix->flags); + } + else + g_print ("%sMatrix type: DIRTY, flags: %x\n", + prefix, (int)matrix->flags); + + print_matrix_floats (prefix, (float *)matrix); + g_print ("%sInverse: \n", prefix); + if (!(matrix->flags & MAT_DIRTY_INVERSE)) + { + float prod[16]; + print_matrix_floats (prefix, matrix->inv); + matrix_multiply4x4 (prod, (float *)matrix, matrix->inv); + g_print ("%sMat * Inverse:\n", prefix); + print_matrix_floats (prefix, prod); + } + else + g_print ("%s - not available\n", prefix); } /* @@ -373,27 +400,7 @@ print_matrix_floats (const float m[16]) void _cogl_matrix_print (const CoglMatrix *matrix) { - if (!(matrix->flags & MAT_DIRTY_TYPE)) - { - _COGL_RETURN_IF_FAIL (matrix->type < COGL_MATRIX_N_TYPES); - g_print ("Matrix type: %s, flags: %x\n", - types[matrix->type], (int)matrix->flags); - } - else - g_print ("Matrix type: DIRTY, flags: %x\n", (int)matrix->flags); - - print_matrix_floats ((float *)matrix); - g_print ("Inverse: \n"); - if (!(matrix->flags & MAT_DIRTY_INVERSE)) - { - float prod[16]; - print_matrix_floats (matrix->inv); - matrix_multiply4x4 (prod, (float *)matrix, matrix->inv); - g_print ("Mat * Inverse:\n"); - print_matrix_floats (prod); - } - else - g_print (" - not available\n"); + _cogl_matrix_prefix_print ("", matrix); } /* @@ -1659,6 +1666,15 @@ cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) _COGL_MATRIX_DEBUG_PRINT (matrix); } +void +_cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, + const CoglMatrix *src) +{ + memcpy (matrix, src, 16 * sizeof (float)); + matrix->type = src->type; + matrix->flags = src->flags | MAT_DIRTY_INVERSE; +} + static void _cogl_matrix_init_from_quaternion (CoglMatrix *matrix, CoglQuaternion *quaternion) diff --git a/cogl/cogl-pipeline-opengl-private.h b/cogl/cogl-pipeline-opengl-private.h index 5742b4aff..c9f56fa92 100644 --- a/cogl/cogl-pipeline-opengl-private.h +++ b/cogl/cogl-pipeline-opengl-private.h @@ -143,6 +143,7 @@ _cogl_delete_gl_texture (GLuint gl_texture); void _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer, CoglBool skip_gl_state, int n_tex_coord_attribs); diff --git a/cogl/cogl-pipeline-opengl.c b/cogl/cogl-pipeline-opengl.c index d947a5370..5341803fc 100644 --- a/cogl/cogl-pipeline-opengl.c +++ b/cogl/cogl-pipeline-opengl.c @@ -1056,8 +1056,6 @@ fragend_add_layer_cb (CoglPipelineLayer *layer, CoglPipeline *pipeline = state->pipeline; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); - _COGL_GET_CONTEXT (ctx, FALSE); - /* Either generate per layer code snippets or setup the * fixed function glTexEnv for each layer... */ if (G_LIKELY (fragend->add_layer (pipeline, @@ -1075,6 +1073,7 @@ fragend_add_layer_cb (CoglPipelineLayer *layer, typedef struct { + CoglFramebuffer *framebuffer; const CoglPipelineVertend *vertend; CoglPipeline *pipeline; unsigned long *layer_differences; @@ -1092,13 +1091,12 @@ vertend_add_layer_cb (CoglPipelineLayer *layer, CoglPipeline *pipeline = state->pipeline; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); - _COGL_GET_CONTEXT (ctx, FALSE); - /* Either enerate per layer code snippets or setup the * fixed function matrix uniforms for each layer... */ if (G_LIKELY (vertend->add_layer (pipeline, layer, - state->layer_differences[unit_index]))) + state->layer_differences[unit_index], + state->framebuffer))) state->added_layer = TRUE; else { @@ -1161,6 +1159,7 @@ vertend_add_layer_cb (CoglPipelineLayer *layer, */ void _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer, CoglBool skip_gl_color, int n_tex_coord_attribs) { @@ -1330,6 +1329,7 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline, n_tex_coord_attribs))) continue; + state.framebuffer = framebuffer; state.vertend = vertend; state.pipeline = pipeline; state.layer_differences = layer_differences; @@ -1407,7 +1407,7 @@ done: matrices */ for (i = 0; i < COGL_PIPELINE_N_PROGENDS; i++) if (_cogl_pipeline_progends[i]->pre_paint) - _cogl_pipeline_progends[i]->pre_paint (pipeline); + _cogl_pipeline_progends[i]->pre_paint (pipeline, framebuffer); /* Handle the fact that OpenGL associates texture filter and wrap * modes with the texture objects not the texture units... */ diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h index a7f677dbe..1b3bef1f4 100644 --- a/cogl/cogl-pipeline-private.h +++ b/cogl/cogl-pipeline-private.h @@ -39,6 +39,7 @@ #include "cogl-boxed-value.h" #include "cogl-pipeline-snippet-private.h" #include "cogl-pipeline-state.h" +#include "cogl-framebuffer.h" #include @@ -583,7 +584,8 @@ typedef struct _CoglPipelineVertend int n_tex_coord_attribs); CoglBool (*add_layer) (CoglPipeline *pipeline, CoglPipelineLayer *layer, - unsigned long layers_difference); + unsigned long layers_difference, + CoglFramebuffer *framebuffer); CoglBool (*end) (CoglPipeline *pipeline, unsigned long pipelines_difference); @@ -609,7 +611,7 @@ typedef struct /* This is called after all of the other functions whenever the pipeline is flushed, even if the pipeline hasn't changed since the last flush */ - void (* pre_paint) (CoglPipeline *pipeline); + void (* pre_paint) (CoglPipeline *pipeline, CoglFramebuffer *framebuffer); } CoglPipelineProgend; typedef enum diff --git a/cogl/cogl-pipeline-progend-fixed.c b/cogl/cogl-pipeline-progend-fixed.c index d71543939..c1051faa4 100644 --- a/cogl/cogl-pipeline-progend-fixed.c +++ b/cogl/cogl-pipeline-progend-fixed.c @@ -37,24 +37,28 @@ #include "cogl-context.h" #include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" static void -_cogl_pipeline_progend_fixed_pre_paint (CoglPipeline *pipeline) +_cogl_pipeline_progend_fixed_pre_paint (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + CoglContext *ctx = framebuffer->context; if (pipeline->vertend != COGL_PIPELINE_VERTEND_FIXED) return; - if (ctx->current_projection_stack) - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - ctx->current_projection_stack, + if (ctx->current_projection_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_projection_entry, COGL_MATRIX_PROJECTION, + framebuffer, FALSE /* enable flip */); - if (ctx->current_modelview_stack) - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - ctx->current_modelview_stack, + if (ctx->current_modelview_entry) + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + ctx->current_modelview_entry, COGL_MATRIX_MODELVIEW, + framebuffer, FALSE /* enable flip */); } diff --git a/cogl/cogl-pipeline-progend-glsl.c b/cogl/cogl-pipeline-progend-glsl.c index f89e49463..fdecb7b1e 100644 --- a/cogl/cogl-pipeline-progend-glsl.c +++ b/cogl/cogl-pipeline-progend-glsl.c @@ -123,8 +123,8 @@ typedef struct GLint projection_uniform; GLint mvp_uniform; - CoglMatrixStackCache projection_cache; - CoglMatrixStackCache modelview_cache; + CoglMatrixEntryCache projection_cache; + CoglMatrixEntryCache modelview_cache; #endif /* We need to track the last pipeline that the program was used with @@ -231,10 +231,10 @@ clear_attribute_cache (CoglPipelineProgramState *program_state) static void clear_flushed_matrix_stacks (CoglPipelineProgramState *program_state) { - _cogl_matrix_stack_destroy_cache (&program_state->projection_cache); - _cogl_matrix_stack_init_cache (&program_state->projection_cache); - _cogl_matrix_stack_destroy_cache (&program_state->modelview_cache); - _cogl_matrix_stack_init_cache (&program_state->modelview_cache); + _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); + _cogl_matrix_entry_cache_init (&program_state->projection_cache); + _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); + _cogl_matrix_entry_cache_init (&program_state->modelview_cache); } #endif /* HAVE_COGL_GLES2 */ @@ -252,8 +252,8 @@ program_state_new (int n_layers) program_state->uniform_locations = NULL; program_state->attribute_locations = NULL; #ifdef HAVE_COGL_GLES2 - _cogl_matrix_stack_init_cache (&program_state->modelview_cache); - _cogl_matrix_stack_init_cache (&program_state->projection_cache); + _cogl_matrix_entry_cache_init (&program_state->modelview_cache); + _cogl_matrix_entry_cache_init (&program_state->projection_cache); #endif return program_state; @@ -281,8 +281,8 @@ destroy_program_state (void *user_data, #ifdef HAVE_COGL_GLES2 if (ctx->driver == COGL_DRIVER_GLES2) { - _cogl_matrix_stack_destroy_cache (&program_state->projection_cache); - _cogl_matrix_stack_destroy_cache (&program_state->modelview_cache); + _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); + _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); } #endif @@ -929,11 +929,12 @@ _cogl_pipeline_progend_glsl_layer_pre_change_notify ( } static void -_cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) +_cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline, + CoglFramebuffer *framebuffer) { CoglBool needs_flip; - CoglMatrixStack *projection_stack; - CoglMatrixStack *modelview_stack; + CoglMatrixEntry *projection_entry; + CoglMatrixEntry *modelview_entry; CoglPipelineProgramState *program_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -943,13 +944,13 @@ _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) program_state = get_program_state (pipeline); - projection_stack = ctx->current_projection_stack; - modelview_stack = ctx->current_modelview_stack; + projection_entry = ctx->current_projection_entry; + modelview_entry = ctx->current_modelview_entry; /* An initial pipeline is flushed while creating the context. At this point there are no matrices selected so we can't do anything */ - if (modelview_stack == NULL || projection_stack == NULL) + if (modelview_entry == NULL || projection_entry == NULL) return; needs_flip = cogl_is_offscreen (ctx->current_draw_buffer); @@ -964,19 +965,17 @@ _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) CoglMatrix modelview, projection; projection_changed = - _cogl_matrix_stack_check_and_update_cache (projection_stack, - &program_state-> - projection_cache, - needs_flip && - program_state-> - flip_uniform == -1); + _cogl_matrix_entry_cache_maybe_update (&program_state->projection_cache, + projection_entry, + (needs_flip && + program_state->flip_uniform == + -1)); modelview_changed = - _cogl_matrix_stack_check_and_update_cache (modelview_stack, - &program_state-> - modelview_cache, - /* never flip modelview */ - FALSE); + _cogl_matrix_entry_cache_maybe_update (&program_state->modelview_cache, + modelview_entry, + /* never flip modelview */ + FALSE); if (modelview_changed || projection_changed) { @@ -991,19 +990,19 @@ _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) } if (need_modelview) - _cogl_matrix_stack_get (modelview_stack, &modelview); + _cogl_matrix_entry_get (modelview_entry, &modelview); if (need_projection) { if (needs_flip && program_state->flip_uniform == -1) { CoglMatrix tmp_matrix; - _cogl_matrix_stack_get (projection_stack, &tmp_matrix); + _cogl_matrix_entry_get (projection_entry, &tmp_matrix); cogl_matrix_multiply (&projection, &ctx->y_flip_matrix, &tmp_matrix); } else - _cogl_matrix_stack_get (projection_stack, &projection); + _cogl_matrix_entry_get (projection_entry, &projection); } if (projection_changed && program_state->projection_uniform != -1) @@ -1023,7 +1022,7 @@ _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) /* The journal usually uses an identity matrix for the modelview so we can optimise this common case by avoiding the matrix multiplication */ - if (_cogl_matrix_stack_has_identity_flag (modelview_stack)) + if (_cogl_matrix_entry_has_identity_flag (modelview_entry)) { GE (ctx, glUniformMatrix4fv (program_state->mvp_uniform, @@ -1056,13 +1055,15 @@ _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline) geometry via the matrix and use the flip vertex instead */ disable_flip = program_state->flip_uniform != -1; - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - projection_stack, + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + projection_entry, COGL_MATRIX_PROJECTION, + framebuffer, disable_flip); - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - modelview_stack, + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + modelview_entry, COGL_MATRIX_MODELVIEW, + framebuffer, disable_flip); } diff --git a/cogl/cogl-pipeline-vertend-fixed.c b/cogl/cogl-pipeline-vertend-fixed.c index f87f4624a..219090e35 100644 --- a/cogl/cogl-pipeline-vertend-fixed.c +++ b/cogl/cogl-pipeline-vertend-fixed.c @@ -33,6 +33,7 @@ #include "cogl-pipeline-private.h" #include "cogl-pipeline-state-private.h" #include "cogl-pipeline-opengl-private.h" +#include "cogl-framebuffer-private.h" #ifdef COGL_PIPELINE_VERTEND_FIXED @@ -80,26 +81,29 @@ _cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline, static CoglBool _cogl_pipeline_vertend_fixed_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *layer, - unsigned long layers_difference) + unsigned long layers_difference, + CoglFramebuffer *framebuffer) { + CoglContext *ctx = framebuffer->context; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); - _COGL_GET_CONTEXT (ctx, FALSE); - if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) { CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, state); + CoglMatrixEntry *matrix_entry; _cogl_matrix_stack_set (unit->matrix_stack, &authority->big_state->matrix); _cogl_set_active_texture_unit (unit_index); - _cogl_matrix_stack_flush_to_gl_builtins (ctx, unit->matrix_stack, + matrix_entry = unit->matrix_stack->last_entry; + _cogl_matrix_entry_flush_to_gl_builtins (ctx, matrix_entry, COGL_MATRIX_TEXTURE, + framebuffer, FALSE /* enable flip */); } diff --git a/cogl/cogl-pipeline-vertend-glsl.c b/cogl/cogl-pipeline-vertend-glsl.c index 757cc7eb6..d8b7e6dc1 100644 --- a/cogl/cogl-pipeline-vertend-glsl.c +++ b/cogl/cogl-pipeline-vertend-glsl.c @@ -287,7 +287,8 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, static CoglBool _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *layer, - unsigned long layers_difference) + unsigned long layers_difference, + CoglFramebuffer *framebuffer) { CoglPipelineShaderState *shader_state; CoglPipelineSnippetData snippet_data; @@ -310,15 +311,18 @@ _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, state); CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + CoglMatrixEntry *matrix_entry; _cogl_matrix_stack_set (unit->matrix_stack, &authority->big_state->matrix); _cogl_set_active_texture_unit (unit_index); - _cogl_matrix_stack_flush_to_gl_builtins (ctx, - unit->matrix_stack, + matrix_entry = unit->matrix_stack->last_entry; + _cogl_matrix_entry_flush_to_gl_builtins (ctx, + matrix_entry, COGL_MATRIX_TEXTURE, + framebuffer, FALSE /* do flip */); } } diff --git a/cogl/cogl.c b/cogl/cogl.c index 7608f53e0..a1fa5a724 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -441,6 +441,7 @@ cogl_begin_gl (void) */ pipeline = cogl_get_source (); _cogl_pipeline_flush_gl_state (pipeline, + cogl_get_draw_framebuffer (), FALSE, cogl_pipeline_get_n_layers (pipeline));