diff --git a/README b/README index 668296055..464ebe9ee 100644 --- a/README +++ b/README @@ -212,6 +212,12 @@ Release Notes for Clutter 1.2 * Clutter now depends on the system copy of JSON-GLib, and will fall back to the internal copy only if JSON-GLib is not installed. +Cogl API changes for Clutter 1.2 +-------------------------------- + +* cogl_viewport is now deprecated in favour of cogl_set_viewport which + accepts a viewport offset. + Release Notes for Clutter 1.0 ------------------------------- diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index b0a7c279e..ca4fd3c26 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -47,6 +47,8 @@ #include "clutter-marshal.h" #include "clutter-private.h" +#include + G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); #define DEFAULT_FONT_NAME "Sans 10" @@ -355,7 +357,22 @@ _clutter_backend_ensure_context (ClutterBackend *backend, klass = CLUTTER_BACKEND_GET_CLASS (backend); if (G_LIKELY (klass->ensure_context)) klass->ensure_context (backend, new_stage); - + + /* XXX: Until Cogl becomes fully responsible for backend windows + * Clutter need to manually keep it informed of the current window size + * + * NB: This must be done after we ensure_context above because Cogl + * always assumes there is a current GL context. + */ + if (new_stage) + { + float width, height; + + clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height); + + _cogl_onscreen_clutter_backend_set_size (width, height); + } + /* FIXME: With a NULL stage and thus no active context it may make more * sense to clean the context but then re call with the default stage * so at least there is some kind of context in place (as to avoid diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index f62d6bd89..b6bb287e0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -425,10 +425,12 @@ read_pixels_to_file (char *filename_stem, GLubyte *data; GdkPixbuf *pixbuf; static int read_count = 0; - GdkPixbuf *flipped; data = g_malloc (4 * width * height); - glReadPixels (0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, /* has alpha */ @@ -439,24 +441,18 @@ read_pixels_to_file (char *filename_stem, pixbuf_free, /* callback to free data */ NULL); /* callback data */ if (pixbuf) - { - flipped = gdk_pixbuf_flip (pixbuf, FALSE); - g_object_unref (pixbuf); - } - - if (flipped) { char *filename = g_strdup_printf ("%s-%05d.png", filename_stem, read_count); GError *error = NULL; - if (!gdk_pixbuf_save (flipped, filename, "png", &error, NULL)) + if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) { g_warning ("Failed to save pick buffer to file %s: %s", filename, error->message); g_error_free (error); } g_free (filename); - g_object_unref (flipped); + g_object_unref (pixbuf); read_count++; } #else @@ -478,7 +474,6 @@ _clutter_do_pick (ClutterStage *stage, { ClutterMainContext *context; guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; - GLint viewport[4]; CoglColor white; guint32 id; GLboolean dither_was_on; @@ -517,17 +512,21 @@ _clutter_do_pick (ClutterStage *stage, if (G_LIKELY (!(clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) cogl_clip_pop (); - /* Calls should work under both GL and GLES, note GLES needs RGBA */ - glGetIntegerv(GL_VIEWPORT, viewport); - /* Make sure Cogl flushes any batched geometry to the GPU driver */ cogl_flush (); /* Read the color of the screen co-ords pixel */ - glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + cogl_read_pixels (x, y, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) - read_pixels_to_file ("pick-buffer", 0, 0, viewport[2], viewport[3]); + { + read_pixels_to_file ("pick-buffer", 0, 0, + clutter_actor_get_width (CLUTTER_ACTOR (stage)), + clutter_actor_get_height (CLUTTER_ACTOR (stage))); + } /* Restore whether GL_DITHER was enabled */ if (dither_was_on) @@ -1338,6 +1337,8 @@ clutter_init_real (GError **error) /* Now we can safely assume we have a valid GL context and can * start issueing cogl commands */ + /* - will call to backend and cogl */ + _clutter_feature_init (); /* * Resolution requires display to be open, so can only be queried after @@ -1374,9 +1375,6 @@ clutter_init_real (GError **error) /* Initiate event collection */ _clutter_backend_init_events (ctx->backend); - /* finally features - will call to backend and cogl */ - _clutter_feature_init (); - clutter_is_initialized = TRUE; ctx->is_initialized = TRUE; diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 9fe5cd347..be2941217 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -192,6 +192,10 @@ clutter_stage_allocate (ClutterActor *self, { ClutterActorClass *klass; + /* XXX: Until Cogl becomes fully responsible for backend windows Clutter + * need to manually keep it informed of the current window size */ + _cogl_onscreen_clutter_backend_set_size (width, height); + CLUTTER_NOTE (LAYOUT, "Following allocation to %dx%d (origin %s)", width, height, @@ -210,6 +214,10 @@ clutter_stage_allocate (ClutterActor *self, _clutter_stage_window_get_geometry (priv->impl, &geom); + /* XXX: Until Cogl becomes fully responsible for backend windows Clutter + * need to manually keep it informed of the current window size */ + _cogl_onscreen_clutter_backend_set_size (geom.width, geom.height); + override.x1 = 0; override.y1 = 0; override.x2 = geom.width; diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 363c99179..abe0f8485 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -251,7 +251,7 @@ clutter_texture_unrealize (ClutterActor *actor) CLUTTER_MARK(); - if (priv->fbo_source != COGL_INVALID_HANDLE) + if (priv->fbo_source != NULL) { /* Free up our fbo handle and texture resources, realize will recreate */ cogl_handle_unref (priv->fbo_handle); @@ -449,66 +449,128 @@ clutter_texture_allocate (ClutterActor *self, } static void -clutter_texture_set_fbo_projection (ClutterActor *self) +set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source, + int viewport_width, + int viewport_height) { - ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv; ClutterVertex verts[4]; - gfloat viewport[4]; - gfloat x_min, x_max, y_min, y_max; - gfloat tx_min, tx_max, ty_min, ty_max; - gfloat tan_angle, near_size; - ClutterPerspective perspective; - ClutterStage *stage; + float x_min = G_MAXFLOAT, y_min = G_MAXFLOAT; + int x_offset, y_offset; int i; - /* Get the bounding rectangle of the source as drawn in screen - coordinates */ - clutter_actor_get_abs_allocation_vertices (priv->fbo_source, verts); + /* Get the actors allocation transformed into screen coordinates. + * + * XXX: Note: this may not be a bounding box for the actor, since an + * actor with depth may escape the box due to its perspective + * projection. */ + clutter_actor_get_abs_allocation_vertices (fbo_source, verts); - x_min = x_max = verts[0].x; - y_min = y_max = verts[0].y; - - for (i = 1; i < G_N_ELEMENTS (verts); ++i) + for (i = 0; i < G_N_ELEMENTS (verts); ++i) { if (verts[i].x < x_min) x_min = verts[i].x; - - if (verts[i].x > x_max) - x_max = verts[i].x; - if (verts[i].y < y_min) y_min = verts[i].y; - - if (verts[i].y > y_max) - y_max = verts[i].y; } - stage = CLUTTER_STAGE (clutter_actor_get_stage (self)); - clutter_stage_get_perspective (stage, &perspective); + /* XXX: It's not good enough to round by simply truncating the fraction here + * via a cast, as it results in offscreen rendering being offset by 1 pixel + * in many cases... */ +#define ROUND(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5)) - /* Convert the coordinates back to [-1,1] range */ - cogl_get_viewport (viewport); + x_offset = ROUND (-x_min); + y_offset = ROUND (-y_min); - tx_min = (x_min / viewport[2]) - * 2 - 1.0; - tx_max = (x_max / viewport[2]) - * 2 - 1.0; - ty_min = (y_min / viewport[3]) - * 2 - 1.0; - ty_max = (y_max / viewport[3]) - * 2 - 1.0; +#undef ROUND - /* Set up a projection matrix so that the actor will be projected as - if it was drawn at its original location */ - tan_angle = tanf ((perspective.fovy / 2) * (G_PI / 180.0)); - near_size = perspective.z_near * tan_angle; + /* translate the viewport so that the source actor lands on the + * sub-region backed by the offscreen draw buffer... */ + cogl_set_viewport (x_offset, y_offset, viewport_width, viewport_height); +} - cogl_frustum ((tx_min * near_size), - (tx_max * near_size), - (-ty_min * near_size), - (-ty_max * near_size), - perspective.z_near, - perspective.z_far); +static void +update_fbo (ClutterActor *self) +{ + ClutterTexture *texture = CLUTTER_TEXTURE (self); + ClutterTexturePrivate *priv = texture->priv; + ClutterMainContext *context; + ClutterShader *shader = NULL; + ClutterActor *stage = NULL; + ClutterPerspective perspective; + CoglColor transparent_col; + + context = _clutter_context_get_default (); + + if (context->shaders) + shader = clutter_actor_get_shader (context->shaders->data); + + /* Temporarily turn off the shader on the top of the context's shader stack, + * to restore the GL pipeline to it's natural state. + */ + if (shader) + clutter_shader_set_is_enabled (shader, FALSE); + + /* Redirect drawing to the fbo */ + cogl_push_draw_buffer (); + cogl_set_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); + + if ((stage = clutter_actor_get_stage (self))) + { + gfloat stage_width, stage_height; + ClutterActor *source_parent; + + /* We copy the projection and modelview matrices from the stage to + * the offscreen draw buffer and create a viewport larger than the + * offscreen draw buffer - the same size as the stage. + * + * The fbo source actor gets rendered into this stage size viewport at the + * same position it normally would after applying all it's usual parent + * transforms and it's own scale and rotate transforms etc. + * + * The viewport is offset such that the offscreen buffer will be positioned + * under the actor. + */ + + clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); + clutter_actor_get_size (stage, &stage_width, &stage_height); + + /* Set the projection matrix modelview matrix and viewport size as + * they are for the stage... */ + _cogl_setup_viewport (stage_width, stage_height, + perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); + + /* Negatively offset the viewport so that the offscreen draw buffer is + * position underneath the fbo_source actor... */ + set_viewport_with_buffer_under_fbo_source (priv->fbo_source, + stage_width, + stage_height); + + /* Reapply the source's parent transformations */ + if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) + _clutter_actor_apply_modelview_transform_recursive (source_parent, + NULL); + } + + + /* cogl_clear is called to clear the buffers */ + cogl_color_set_from_4ub (&transparent_col, 0, 0, 0, 0); + cogl_clear (&transparent_col, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH); + cogl_disable_fog (); + + /* Render the actor to the fbo */ + clutter_actor_paint (priv->fbo_source); + + /* Restore drawing to the previous draw buffer */ + cogl_pop_draw_buffer (); + + /* If there is a shader on top of the shader stack, turn it back on. */ + if (shader) + clutter_shader_set_is_enabled (shader, TRUE); } static void @@ -517,7 +579,6 @@ clutter_texture_paint (ClutterActor *self) ClutterTexture *texture = CLUTTER_TEXTURE (self); ClutterTexturePrivate *priv = texture->priv; ClutterActorBox box = { 0, }; - CoglColor transparent_col; gfloat t_w, t_h; guint8 paint_opacity = clutter_actor_get_paint_opacity (self); @@ -530,85 +591,7 @@ clutter_texture_paint (ClutterActor *self) } if (priv->fbo_handle != COGL_INVALID_HANDLE) - { - ClutterMainContext *context; - ClutterShader *shader = NULL; - ClutterActor *stage = NULL; - ClutterPerspective perspective; - - context = _clutter_context_get_default (); - - if (context->shaders) - shader = clutter_actor_get_shader (context->shaders->data); - - /* Temporarily turn of the shader on the top of the context's - * shader stack, to restore the GL pipeline to it's natural state. - */ - if (shader) - clutter_shader_set_is_enabled (shader, FALSE); - - /* Redirect drawing to the fbo */ - cogl_set_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); - - if ((stage = clutter_actor_get_stage (self))) - { - gfloat stage_width, stage_height; - ClutterActor *source_parent; - - clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); - clutter_actor_get_size (stage, &stage_width, &stage_height); - - /* Use below to set the modelview matrix as if the viewport - was still the same size as the stage */ - _cogl_setup_viewport (stage_width, stage_height, - perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - /* Use a projection matrix that makes the actor appear as it - would if it was rendered at its normal screen location */ - clutter_texture_set_fbo_projection (self); - - /* Reset the viewport to the size of the FBO */ - cogl_viewport (priv->image_width, priv->image_height); - - /* Reapply the source's parent transformations */ - if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) - _clutter_actor_apply_modelview_transform_recursive (source_parent, - NULL); - } - - /* cogl_clear is called to clear the buffers */ - cogl_color_set_from_4ub (&transparent_col, 0, 0, 0, 0); - cogl_clear (&transparent_col, - COGL_BUFFER_BIT_COLOR | - COGL_BUFFER_BIT_DEPTH); - cogl_disable_fog (); - - /* Clear the clipping stack so that if the FBO actor is being - clipped then it won't affect drawing the source */ - cogl_clip_stack_save (); - - /* Render out actor scene to fbo */ - clutter_actor_paint (priv->fbo_source); - - cogl_clip_stack_restore (); - - /* Restore drawing to the frame buffer */ - cogl_set_draw_buffer (COGL_WINDOW_BUFFER, COGL_INVALID_HANDLE); - - /* Restore the perspective matrix using cogl_perspective so that - the inverse matrix will be right */ - cogl_perspective (perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - /* If there is a shader on top of the shader stack, turn it back on. */ - if (shader) - clutter_shader_set_is_enabled (shader, TRUE); - } + update_fbo (self); CLUTTER_NOTE (PAINT, "painting texture '%s'", diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 81c5186d2..4e0c2a1e7 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -120,6 +120,10 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-spans.c \ $(srcdir)/cogl-journal-private.h \ $(srcdir)/cogl-journal.c \ + $(srcdir)/cogl-draw-buffer-private.h \ + $(srcdir)/cogl-draw-buffer.c \ + $(srcdir)/cogl-matrix-mesa.h \ + $(srcdir)/cogl-matrix-mesa.c \ $(BUILT_SOURCES) \ $(NULL) diff --git a/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index a0dc3bc49..93900338e 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/cogl/cogl-clip-stack.c @@ -34,6 +34,7 @@ #include "cogl-primitives.h" #include "cogl-context.h" #include "cogl-internal.h" +#include "cogl-draw-buffer-private.h" /* These are defined in the particular backend (float in GL vs fixed in GL ES) */ @@ -113,45 +114,79 @@ struct _CoglClipStackEntryPath CoglPathNode path[1]; }; +/* FIXME: deprecate and replace with: + * void + * cogl_clip_push_window_rectangle (int x_offset, + * int y_offset, + * int width, + * int height); + */ void cogl_clip_push_window_rect (float x_offset, float y_offset, float width, float height) { - CoglClipStackEntryWindowRect *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; - float v[4]; + int draw_buffer_height; + CoglClipStackEntryWindowRect *entry; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); - cogl_get_viewport (v); + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; + + draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); entry = g_slice_new (CoglClipStackEntryWindowRect); - /* We convert from coords with (0,0) at top left to coords - * with (0,0) at bottom left. */ + /* We store the entry coordinates in OpenGL window coordinate space and so + * because Cogl defines the window origin to be top left but OpenGL defines + * it as bottom left we may need to convert the incoming coordinates. + * + * NB: Cogl forces all offscreen rendering to be done upside down so in this + * case no conversion is needed. + */ entry->type = COGL_CLIP_STACK_WINDOW_RECT; entry->x0 = x_offset; - entry->y0 = v[3] - y_offset - height; entry->x1 = x_offset + width; - entry->y1 = v[3] - y_offset; + if (cogl_is_offscreen (draw_buffer)) + { + entry->y0 = y_offset; + entry->y1 = y_offset + height; + } + else + { + entry->y0 = draw_buffer_height - y_offset - height; + entry->y1 = draw_buffer_height - y_offset; + } /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } -/* Scale from OpenGL <-1,1> coordinates system to window coordinates - * <0,window-size> with (0,0) being top left. */ -#define VIEWPORT_SCALE_X(x, w, width, origin) \ - ((((((x) / (w)) + 1.0) / 2) * (width)) + (origin)) -#define VIEWPORT_SCALE_Y(y, w, height, origin) \ - ((height) - (((((y) / (w)) + 1.0) / 2) * (height)) + (origin)) +/* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) + * to Cogl window/draw-buffer coordinates (ranging from 0 to buffer-size) with + * (0,0) being top left. */ +#define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ + ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) +/* Note: for Y we first flip all coordinates around the X axis while in + * normalized device coodinates */ +#define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ + ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) +/* Transform a homogeneous vertex position from model space to Cogl + * window coordinates (with 0,0 being top left) */ static void transform_point (CoglMatrix *matrix_mv, CoglMatrix *matrix_p, @@ -162,14 +197,19 @@ transform_point (CoglMatrix *matrix_mv, float z = 0; float w = 1; - /* Apply the model view matrix */ + /* Apply the modelview matrix transform */ cogl_matrix_transform_point (matrix_mv, x, y, &z, &w); - /* Apply the projection matrix */ + /* Apply the projection matrix transform */ cogl_matrix_transform_point (matrix_p, x, y, &z, &w); + + /* Perform perspective division */ + *x /= w; + *y /= w; + /* Apply viewport transform */ - *x = VIEWPORT_SCALE_X (*x, w, viewport[2], viewport[0]); - *y = VIEWPORT_SCALE_Y (*y, w, viewport[3], viewport[1]); + *x = VIEWPORT_TRANSFORM_X (*x, viewport[0], viewport[2]); + *y = VIEWPORT_TRANSFORM_Y (*y, viewport[1], viewport[3]); } #undef VIEWPORT_SCALE_X @@ -193,6 +233,15 @@ try_pushing_rect_as_window_rect (float x_offset, cogl_get_modelview_matrix (&matrix); + /* If the modelview meets these constraints then a transformed rectangle + * should still be a rectangle when it reaches screen coordinates. + * + * 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. + */ if (matrix.xy != 0 || matrix.xz != 0 || matrix.yx != 0 || matrix.yz != 0 || matrix.zx != 0 || matrix.zy != 0) @@ -204,6 +253,15 @@ try_pushing_rect_as_window_rect (float x_offset, transform_point (&matrix, &matrix_p, v, &_x0, &_y0); transform_point (&matrix, &matrix_p, v, &_x1, &_y1); + /* Consider that the modelview matrix may flip the rectangle + * along the x or y axis... */ +#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) + if (_x0 > _x1) + SWAP (_x0, _x1); + if (_y0 > _y1) + SWAP (_y0, _y1); +#undef SWAP + cogl_clip_push_window_rect (_x0, _y0, _x1 - _x0, _y1 - _y0); return TRUE; } @@ -214,17 +272,26 @@ cogl_clip_push (float x_offset, float width, float height) { - CoglClipStackEntryRect *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; + CoglClipStackEntryRect *entry; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + /* Try and catch window space rectangles so we can redirect to * cogl_clip_push_window_rect which will use scissoring. */ if (try_pushing_rect_as_window_rect (x_offset, y_offset, width, height)) return; - stack = (CoglClipStack *) ctx->clip.stacks->data; + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; entry = g_slice_new (CoglClipStackEntryRect); @@ -240,18 +307,27 @@ cogl_clip_push (float x_offset, /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void cogl_clip_push_from_path_preserve (void) { - CoglClipStackEntryPath *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; + CoglClipStackEntryPath *entry; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; entry = g_malloc (sizeof (CoglClipStackEntryPath) + sizeof (CoglPathNode) * (ctx->path_nodes->len - 1)); @@ -268,7 +344,7 @@ cogl_clip_push_from_path_preserve (void) /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void @@ -279,16 +355,18 @@ cogl_clip_push_from_path (void) cogl_path_new (); } -void -cogl_clip_pop (void) +static void +_cogl_clip_pop_real (CoglClipStackState *clip_state) { - gpointer entry; CoglClipStack *stack; + gpointer entry; CoglClipStackEntryType type; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); - stack = (CoglClipStack *) ctx->clip.stacks->data; + stack = clip_state->stacks->data; g_return_if_fail (stack->stack_top != NULL); @@ -306,35 +384,57 @@ cogl_clip_pop (void) stack->stack_top = g_list_delete_link (stack->stack_top, stack->stack_top); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void -_cogl_clip_stack_rebuild (void) +cogl_clip_pop (void) { - int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + CoglHandle draw_buffer; + CoglClipStackState *clip_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + _cogl_clip_pop_real (clip_state); +} + +void +_cogl_flush_clip_state (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + int has_clip_planes; gboolean using_clip_planes = FALSE; gboolean using_stencil_buffer = FALSE; GList *node; - CoglClipStack *stack; gint scissor_x0 = 0; gint scissor_y0 = 0; gint scissor_x1 = G_MAXINT; gint scissor_y1 = G_MAXINT; - CoglMatrixStack *modelview_stack; + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - modelview_stack = ctx->modelview_stack; + if (!clip_state->stack_dirty) + return; /* The current primitive journal does not support tracking changes to the * clip stack... */ _cogl_journal_flush (); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* XXX: the handling of clipping is quite complex. It may involve use of + * the Cogl Journal or other Cogl APIs which may end up recursively + * wanting to ensure the clip state is flushed. We need to ensure we + * don't recurse infinitely... + */ + clip_state->stack_dirty = FALSE; - ctx->clip.stack_dirty = FALSE; - ctx->clip.stencil_used = FALSE; + has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + + stack = clip_state->stacks->data; + + clip_state->stencil_used = FALSE; _cogl_disable_clip_planes (); _cogl_disable_stencil_buffer (); @@ -433,74 +533,111 @@ _cogl_clip_stack_rebuild (void) scissor_y1 - scissor_y0)); } - ctx->clip.stencil_used = using_stencil_buffer; + clip_state->stencil_used = using_stencil_buffer; } +/* XXX: This should never have been made public API! */ void cogl_clip_ensure (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + CoglClipStackState *clip_state; - if (ctx->clip.stack_dirty) - _cogl_clip_stack_rebuild (); + clip_state = _cogl_draw_buffer_get_clip_state (_cogl_get_draw_buffer ()); + _cogl_flush_clip_state (clip_state); +} + +static void +_cogl_clip_stack_save_real (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + stack = g_slice_new (CoglClipStack); + stack->stack_top = NULL; + + clip_state->stacks = g_slist_prepend (clip_state->stacks, stack); + clip_state->stack_dirty = TRUE; } void cogl_clip_stack_save (void) { - CoglClipStack *stack; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = g_slice_new (CoglClipStack); - stack->stack_top = NULL; + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); - ctx->clip.stacks = g_slist_prepend (ctx->clip.stacks, stack); + _cogl_clip_stack_save_real (clip_state); +} - ctx->clip.stack_dirty = TRUE; +static void +_cogl_clip_stack_restore_real (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + + g_return_if_fail (clip_state->stacks != NULL); + + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + stack = clip_state->stacks->data; + + /* Empty the current stack */ + while (stack->stack_top) + _cogl_clip_pop_real (clip_state); + + /* Revert to an old stack */ + g_slice_free (CoglClipStack, stack); + clip_state->stacks = g_slist_delete_link (clip_state->stacks, + clip_state->stacks); + + clip_state->stack_dirty = TRUE; } void cogl_clip_stack_restore (void) { - CoglClipStack *stack; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - g_return_if_fail (ctx->clip.stacks != NULL); + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); - stack = (CoglClipStack *) ctx->clip.stacks->data; - - /* Empty the current stack */ - while (stack->stack_top) - cogl_clip_pop (); - - /* Revert to an old stack */ - g_slice_free (CoglClipStack, stack); - ctx->clip.stacks = g_slist_delete_link (ctx->clip.stacks, - ctx->clip.stacks); - - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_restore_real (clip_state); } void -_cogl_clip_stack_state_init (void) +_cogl_clip_stack_state_init (CoglClipStackState *clip_state) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - ctx->clip.stacks = NULL; - ctx->clip.stack_dirty = TRUE; + clip_state->stacks = NULL; + clip_state->stack_dirty = TRUE; /* Add an intial stack */ - cogl_clip_stack_save (); + _cogl_clip_stack_save_real (clip_state); } void -_cogl_clip_stack_state_destroy (void) +_cogl_clip_stack_state_destroy (CoglClipStackState *clip_state) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Destroy all of the stacks */ - while (ctx->clip.stacks) - cogl_clip_stack_restore (); + while (clip_state->stacks) + _cogl_clip_stack_restore_real (clip_state); } + +void +_cogl_clip_stack_state_dirty (CoglClipStackState *clip_state) +{ + clip_state->stack_dirty = TRUE; +} + diff --git a/clutter/cogl/cogl/cogl-clip-stack.h b/clutter/cogl/cogl/cogl-clip-stack.h index 324097b17..0ca041ec4 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.h +++ b/clutter/cogl/cogl/cogl-clip-stack.h @@ -35,9 +35,10 @@ struct _CoglClipStackState gboolean stencil_used; }; -void _cogl_clip_stack_state_init (void); -void _cogl_clip_stack_state_destroy (void); -void _cogl_clip_stack_rebuild (void); -void _cogl_clip_stack_merge (void); +void _cogl_clip_stack_state_init (CoglClipStackState *state); +void _cogl_clip_stack_state_destroy (CoglClipStackState *state); +void _cogl_clip_stack_state_dirty (CoglClipStackState *state); + +void _cogl_flush_clip_state (CoglClipStackState *clip_state); #endif /* __COGL_CLIP_STACK_H */ diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 6cbc7b7c2..34c65d08c 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/cogl/cogl-context.c @@ -32,6 +32,7 @@ #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-draw-buffer-private.h" #include @@ -46,7 +47,7 @@ cogl_create_context (void) { GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 }; gulong enable_flags = 0; - CoglDrawBufferState *draw_buffer; + CoglHandle window_buffer; if (_context != NULL) return FALSE; @@ -58,16 +59,24 @@ cogl_create_context (void) _context->feature_flags = 0; _context->features_cached = FALSE; + /* Initialise the driver specific state */ + /* TODO: combine these two into one function */ + _cogl_create_context_driver (_context); + _cogl_features_init (); + _context->enable_flags = 0; _context->color_alpha = 0; _context->enable_backface_culling = FALSE; + _context->flushed_front_winding = COGL_FRONT_WINDING_COUNTER_CLOCKWISE; _context->indirect = gl_is_indirect; + cogl_matrix_init_identity (&_context->identity_matrix); + cogl_matrix_init_identity (&_context->y_flip_matrix); + cogl_matrix_scale (&_context->y_flip_matrix, 1, -1, 1); + _context->flushed_matrix_mode = COGL_MATRIX_MODELVIEW; - _context->modelview_stack = _cogl_matrix_stack_new (); - _context->projection_stack = _cogl_matrix_stack_new (); _context->texture_units = NULL; _context->default_material = cogl_material_new (); @@ -87,11 +96,14 @@ cogl_create_context (void) sizeof (CoglLayerInfo)); _context->n_texcoord_arrays_enabled = 0; - draw_buffer = g_slice_new0 (CoglDrawBufferState); - draw_buffer->target = COGL_WINDOW_BUFFER; - draw_buffer->offscreen = COGL_INVALID_HANDLE; - _context->draw_buffer_stack = - g_slist_prepend (NULL, draw_buffer); + _context->draw_buffer_stack = _cogl_create_draw_buffer_stack (); + window_buffer = _cogl_onscreen_new (); + /* XXX: When setting up the window buffer, cogl_set_draw_buffer + * assumes that the handle can be found in ctx->window_buffer */ + _context->window_buffer = window_buffer; + cogl_set_draw_buffer (COGL_WINDOW_BUFFER, 0/* ignored */); + _context->dirty_bound_framebuffer = TRUE; + _context->dirty_gl_viewport = TRUE; _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); _context->last_path = 0; @@ -99,21 +111,12 @@ cogl_create_context (void) _context->in_begin_gl_block = FALSE; - _context->viewport_width = 0; - _context->viewport_height = 0; - _context->quad_indices_byte = COGL_INVALID_HANDLE; _context->quad_indices_short = COGL_INVALID_HANDLE; _context->quad_indices_short_len = 0; _context->texture_download_material = COGL_INVALID_HANDLE; - /* Initialise the clip stack */ - _cogl_clip_stack_state_init (); - - /* Initialise the driver specific state */ - _cogl_create_context_driver (_context); - /* Create default textures used for fall backs */ _context->default_gl_texture_2d_tex = cogl_texture_new_from_data (1, /* width */ @@ -139,6 +142,7 @@ cogl_create_context (void) enable_flags = _cogl_material_get_cogl_enable_flags (_context->source_material); cogl_enable (enable_flags); + _cogl_flush_face_winding (); return TRUE; } @@ -146,16 +150,14 @@ cogl_create_context (void) void _cogl_destroy_context () { + if (_context == NULL) return; - _cogl_clip_stack_state_destroy (); - - _cogl_matrix_stack_destroy (_context->modelview_stack); - _cogl_matrix_stack_destroy (_context->projection_stack); - _cogl_destroy_texture_units (); + _cogl_free_draw_buffer_stack (_context->draw_buffer_stack); + if (_context->path_nodes) g_array_free (_context->path_nodes, TRUE); diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index cf8e3ad84..d10664f30 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -38,12 +38,6 @@ typedef struct GLubyte c[4]; } CoglTextureGLVertex; -typedef struct -{ - CoglBufferTarget target; - CoglHandle offscreen; -} CoglDrawBufferState; - typedef struct { /* Features cache */ @@ -55,19 +49,18 @@ typedef struct guint8 color_alpha; gboolean enable_backface_culling; + CoglFrontWinding flushed_front_winding; gboolean indirect; + /* A few handy matrix constants */ + CoglMatrix identity_matrix; + CoglMatrix y_flip_matrix; + /* Client-side matrix stack or NULL if none */ CoglMatrixMode flushed_matrix_mode; - CoglMatrixStack *projection_stack; - CoglMatrixStack *modelview_stack; - GList *texture_units; - /* Cache of inverse projection matrix */ - float inverse_projection[16]; - /* Materials */ CoglHandle default_material; CoglHandle source_material; @@ -91,11 +84,11 @@ typedef struct GArray *current_layers; guint n_texcoord_arrays_enabled; - /* Framebuffer objects */ + /* Draw Buffers */ GSList *draw_buffer_stack; - - /* Clip stack */ - CoglClipStackState clip; + CoglHandle window_buffer; + gboolean dirty_bound_framebuffer; + gboolean dirty_gl_viewport; /* Primitives */ floatVec2 path_start; @@ -114,9 +107,6 @@ typedef struct gboolean in_begin_gl_block; - guint viewport_width; - guint viewport_height; - CoglHandle texture_download_material; CoglContextDriver drv; diff --git a/clutter/cogl/cogl/cogl-debug.c b/clutter/cogl/cogl/cogl-debug.c index 6ca99c5b3..efc0146a3 100644 --- a/clutter/cogl/cogl/cogl-debug.c +++ b/clutter/cogl/cogl/cogl-debug.c @@ -46,7 +46,8 @@ static const GDebugKey cogl_debug_keys[] = { { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, { "journal", COGL_DEBUG_JOURNAL }, { "batching", COGL_DEBUG_BATCHING }, - { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM } + { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, + { "matrices", COGL_DEBUG_MATRICES } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/clutter/cogl/cogl/cogl-debug.h b/clutter/cogl/cogl/cogl-debug.h index a1e5a358c..86d3b57db 100644 --- a/clutter/cogl/cogl/cogl-debug.h +++ b/clutter/cogl/cogl/cogl-debug.h @@ -44,7 +44,8 @@ typedef enum { COGL_DEBUG_DISABLE_VBOS = 1 << 12, COGL_DEBUG_JOURNAL = 1 << 13, COGL_DEBUG_BATCHING = 1 << 14, - COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 15 + COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 15, + COGL_DEBUG_MATRICES = 1 << 16 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/clutter/cogl/cogl/cogl-draw-buffer-private.h b/clutter/cogl/cogl/cogl-draw-buffer-private.h new file mode 100644 index 000000000..3490cd26c --- /dev/null +++ b/clutter/cogl/cogl/cogl-draw-buffer-private.h @@ -0,0 +1,133 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_DRAW_BUFFER_PRIVATE_H +#define __COGL_DRAW_BUFFER_PRIVATE_H + +#include "cogl-handle.h" +#include "cogl-matrix-stack.h" +#include "cogl-clip-stack.h" + +typedef enum _CoglDrawBufferType { + COGL_DRAW_BUFFER_TYPE_ONSCREEN, + COGL_DRAW_BUFFER_TYPE_OFFSCREEN +} CoglDrawBufferType; + +typedef struct +{ + CoglHandleObject _parent; + CoglDrawBufferType type; + int width; + int height; + + CoglMatrixStack *modelview_stack; + CoglMatrixStack *projection_stack; + int viewport_x; + int viewport_y; + int viewport_width; + int viewport_height; + + CoglClipStackState clip_state; +} CoglDrawBuffer; + +#define COGL_DRAW_BUFFER(X) ((CoglDrawBuffer *)(X)) + +typedef struct _CoglDrawBufferStackEntry +{ + CoglBufferTarget target; + CoglHandle draw_buffer; +} CoglDrawBufferStackEntry; + +typedef struct _CoglOffscreen +{ + CoglDrawBuffer _parent; + GLuint fbo_handle; + GLuint gl_stencil_handle; +} CoglOffscreen; + +#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X)) + +typedef struct _CoglOnscreen +{ + CoglDrawBuffer _parent; +} CoglOnscreen; + +#define COGL_ONSCREEN(X) ((CoglOnscreen *)(X)) + +void +_cogl_draw_buffer_state_init (void); +int +_cogl_draw_buffer_get_width (CoglHandle handle); +int +_cogl_draw_buffer_get_height (CoglHandle handle); +CoglClipStackState * +_cogl_draw_buffer_get_clip_state (CoglHandle handle); +void +_cogl_draw_buffer_set_viewport (CoglHandle handle, + int x, + int y, + int width, + int height); +int +_cogl_draw_buffer_get_viewport_x (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_y (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_width (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_height (CoglHandle handle); +void +_cogl_draw_buffer_get_viewport4fv (CoglHandle handle, int *viewport); +CoglMatrixStack * +_cogl_draw_buffer_get_modelview_stack (CoglHandle handle); +CoglMatrixStack * +_cogl_draw_buffer_get_projection_stack (CoglHandle handle); + +typedef enum _CoglDrawBufferFlushFlags +{ + /* XXX: When using this, that imples you are going to manually load the + * modelview matrix (via glLoadMatrix). _cogl_matrix_stack_flush_to_gl wont + * be called for draw_buffer->modelview_stack, and the modelview_stack will + * also be marked as dirty. */ + COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW = 1L<<0, +} CoglDrawBufferFlushFlags; + +void +_cogl_draw_buffer_flush_state (CoglHandle handle, + CoglDrawBufferFlushFlags flags); + +CoglHandle +_cogl_onscreen_new (void); + +gboolean +cogl_is_offscreen (CoglHandle handle); + +CoglHandle +_cogl_get_draw_buffer (void); +GSList * +_cogl_create_draw_buffer_stack (void); +void +_cogl_free_draw_buffer_stack (GSList *stack); + +#endif /* __COGL_DRAW_BUFFER_PRIVATE_H */ + diff --git a/clutter/cogl/cogl/cogl-draw-buffer.c b/clutter/cogl/cogl/cogl-draw-buffer.c new file mode 100644 index 000000000..2a7b75784 --- /dev/null +++ b/clutter/cogl/cogl/cogl-draw-buffer.c @@ -0,0 +1,602 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-draw-buffer-private.h" +#include "cogl-clip-stack.h" + +#ifdef HAVE_COGL_GLES2 + +#include "../gles/cogl-gles2-wrapper.h" + +#else + +#define glGenRenderbuffers ctx->drv.pf_glGenRenderbuffers +#define glDeleteRenderbuffers ctx->drv.pf_glDeleteRenderbuffers +#define glBindRenderbuffer ctx->drv.pf_glBindRenderbuffer +#define glRenderbufferStorage ctx->drv.pf_glRenderbufferStorage +#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers +#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer +#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D +#define glFramebufferRenderbuffer ctx->drv.pf_glFramebufferRenderbuffer +#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus +#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers + +#endif + +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D00 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif + +static void _cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer); +static void _cogl_onscreen_free (CoglOnscreen *onscreen); +static void _cogl_offscreen_free (CoglOffscreen *offscreen); + +COGL_HANDLE_DEFINE (Onscreen, onscreen); +COGL_HANDLE_DEFINE (Offscreen, offscreen); + +/* XXX: + * The CoglHandle macros don't support any form of inheritance, so for + * now we implement the CoglHandle support for the CoglDrawBuffer + * abstract class manually. + */ + +gboolean +cogl_is_draw_buffer (CoglHandle handle) +{ + CoglHandleObject *obj = (CoglHandleObject *)handle; + + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return obj->klass->type == _cogl_handle_onscreen_get_type () + || obj->klass->type == _cogl_handle_offscreen_get_type (); +} + +static void +_cogl_draw_buffer_init (CoglDrawBuffer *draw_buffer, + CoglDrawBufferType type, + int width, + int height) +{ + draw_buffer->type = type; + draw_buffer->width = width; + draw_buffer->height = height; + draw_buffer->viewport_x = 0; + draw_buffer->viewport_y = 0; + draw_buffer->viewport_width = width; + draw_buffer->viewport_height = height; + + draw_buffer->modelview_stack = _cogl_matrix_stack_new (); + draw_buffer->projection_stack = _cogl_matrix_stack_new (); + + /* Initialise the clip stack */ + _cogl_clip_stack_state_init (&draw_buffer->clip_state); +} + +void +_cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer) +{ + _cogl_clip_stack_state_destroy (&draw_buffer->clip_state); + + _cogl_matrix_stack_destroy (draw_buffer->modelview_stack); + draw_buffer->modelview_stack = NULL; + + _cogl_matrix_stack_destroy (draw_buffer->projection_stack); + draw_buffer->projection_stack = NULL; +} + +int +_cogl_draw_buffer_get_width (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->width; +} + +int +_cogl_draw_buffer_get_height (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->height; +} + +CoglClipStackState * +_cogl_draw_buffer_get_clip_state (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + + return &draw_buffer->clip_state; +} + +void +_cogl_draw_buffer_set_viewport (CoglHandle handle, + int x, + int y, + int width, + int height) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (draw_buffer->viewport_x == x && + draw_buffer->viewport_y == y && + draw_buffer->viewport_width == width && + draw_buffer->viewport_height == height) + return; + + _cogl_journal_flush (); + + draw_buffer->viewport_x = x; + draw_buffer->viewport_y = y; + draw_buffer->viewport_width = width; + draw_buffer->viewport_height = height; + + if (_cogl_get_draw_buffer () == draw_buffer) + ctx->dirty_gl_viewport = TRUE; +} + +int +_cogl_draw_buffer_get_viewport_x (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_x; +} + +int +_cogl_draw_buffer_get_viewport_y (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_y; +} + +int +_cogl_draw_buffer_get_viewport_width (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_width; +} + +int +_cogl_draw_buffer_get_viewport_height (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_height; +} + +void +_cogl_draw_buffer_get_viewport4fv (CoglHandle handle, int *viewport) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + viewport[0] = draw_buffer->viewport_x; + viewport[1] = draw_buffer->viewport_y; + viewport[2] = draw_buffer->viewport_width; + viewport[3] = draw_buffer->viewport_height; +} + +CoglMatrixStack * +_cogl_draw_buffer_get_modelview_stack (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->modelview_stack; +} + +CoglMatrixStack * +_cogl_draw_buffer_get_projection_stack (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->projection_stack; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + CoglOffscreen *offscreen; + int width; + int height; + GLuint tex_gl_handle; + GLenum tex_gl_target; + GLuint fbo_gl_handle; + GLuint gl_stencil_handle; + GLenum status; + + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return COGL_INVALID_HANDLE; + + /* Make texhandle is a valid texture object */ + if (!cogl_is_texture (texhandle)) + return COGL_INVALID_HANDLE; + + /* The texture must not be sliced */ + if (cogl_texture_is_sliced (texhandle)) + return COGL_INVALID_HANDLE; + + /* Pick the single texture slice width, height and GL id */ + + width = cogl_texture_get_width (texhandle); + height = cogl_texture_get_height (texhandle); + + if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target)) + return COGL_INVALID_HANDLE; + + if (tex_gl_target != GL_TEXTURE_2D) + return COGL_INVALID_HANDLE; + + /* Create a renderbuffer for stenciling */ + GE (glGenRenderbuffers (1, &gl_stencil_handle)); + GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle)); + GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, + cogl_texture_get_width (texhandle), + cogl_texture_get_height (texhandle))); + GE (glBindRenderbuffer (GL_RENDERBUFFER, 0)); + + /* We are about to generate and bind a new fbo, so when next flushing the + * journal, we will need to rebind the current draw buffer... */ + ctx->dirty_bound_framebuffer = 1; + + /* Generate framebuffer */ + glGenFramebuffers (1, &fbo_gl_handle); + GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle)); + GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, 0)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_stencil_handle)); + + /* XXX: The framebuffer_object spec isn't clear in defining whether attaching + * a texture as a renderbuffer with mipmap filtering enabled while the + * mipmaps have not been uploaded should result in an incomplete framebuffer + * object. (different drivers make different decisions) + * + * To avoid an error with drivers that do consider this a problem we + * explicitly set non mipmapped filters here. These will later be reset when + * the texture is actually used for rendering according to the filters set on + * the corresponding CoglMaterial. + */ + _cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST); + + /* Make sure it's complete */ + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + /* Stencil renderbuffers aren't always supported. Try again + without the stencil buffer */ + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + 0)); + GE (glDeleteRenderbuffers (1, &gl_stencil_handle)); + gl_stencil_handle = 0; + + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + /* Still failing, so give up */ + GE (glDeleteFramebuffers (1, &fbo_gl_handle)); + GE (glBindFramebuffer (GL_FRAMEBUFFER, 0)); + return COGL_INVALID_HANDLE; + } + } + + offscreen = g_new0 (CoglOffscreen, 1); + + _cogl_draw_buffer_init (COGL_DRAW_BUFFER (offscreen), + COGL_DRAW_BUFFER_TYPE_OFFSCREEN, + width, + height); + + offscreen->fbo_handle = fbo_gl_handle; + offscreen->gl_stencil_handle = gl_stencil_handle; + + /* XXX: Can we get a away with removing this? It wasn't documented, and most + * users of the API are hopefully setting up the modelview from scratch + * anyway */ +#if 0 + cogl_matrix_translate (&draw_buffer->modelview, -1.0f, -1.0f, 0.0f); + cogl_matrix_scale (&draw_buffer->modelview, + 2.0f / draw_buffer->width, 2.0f / draw_buffer->height, 1.0f); +#endif + + return _cogl_offscreen_handle_new (offscreen); +} + +static void +_cogl_offscreen_free (CoglOffscreen *offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Chain up to parent */ + _cogl_draw_buffer_free (COGL_DRAW_BUFFER (offscreen)); + + if (offscreen->gl_stencil_handle) + GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle)); + GE (glDeleteFramebuffers (1, &offscreen->fbo_handle)); + g_free (offscreen); +} + +CoglHandle +_cogl_onscreen_new (void) +{ + CoglOnscreen *onscreen; + + /* XXX: Until we have full winsys support in Cogl then we can't fully + * implement CoglOnscreen draw buffers, since we can't, e.g. keep track of + * the window size. */ + + onscreen = g_new0 (CoglOnscreen, 1); + _cogl_draw_buffer_init (COGL_DRAW_BUFFER (onscreen), + COGL_DRAW_BUFFER_TYPE_ONSCREEN, + 0xdeadbeef, /* width */ + 0xdeadbeef); /* height */ + + return _cogl_onscreen_handle_new (onscreen); +} + +static void +_cogl_onscreen_free (CoglOnscreen *onscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Chain up to parent */ + _cogl_draw_buffer_free (COGL_DRAW_BUFFER (onscreen)); + + g_free (onscreen); +} + +void +_cogl_onscreen_clutter_backend_set_size (int width, int height) +{ + CoglDrawBuffer *draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = COGL_DRAW_BUFFER (ctx->window_buffer); + + if (draw_buffer->width == width && draw_buffer->height == height) + return; + + draw_buffer->width = width; + draw_buffer->height = height; + + /* We'll need to recalculate the GL viewport state derived + * from the Cogl viewport */ + ctx->dirty_gl_viewport = 1; +} + +GSList * +_cogl_create_draw_buffer_stack (void) +{ + GSList *stack = NULL; + CoglDrawBufferStackEntry *entry; + + entry = g_slice_new0 (CoglDrawBufferStackEntry); + entry->target = COGL_WINDOW_BUFFER; + entry->draw_buffer = COGL_INVALID_HANDLE; + + return g_slist_prepend (stack, entry); +} + +void +_cogl_free_draw_buffer_stack (GSList *stack) +{ + GSList *l; + + for (l = stack; l != NULL; l = l->next) + { + CoglDrawBufferStackEntry *entry = l->data; + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (entry->draw_buffer); + if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN) + _cogl_offscreen_free (COGL_OFFSCREEN (draw_buffer)); + else + _cogl_onscreen_free (COGL_ONSCREEN (draw_buffer)); + } + g_slist_free (stack); +} + +/* XXX: The target argument is redundant; when we break API, we should + * remove it! */ +void +cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = NULL; + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_journal_flush (); + + g_assert (ctx->draw_buffer_stack != NULL); + entry = ctx->draw_buffer_stack->data; + + if (target == COGL_WINDOW_BUFFER) + handle = ctx->window_buffer; + else if (!cogl_is_draw_buffer (handle)) + return; + + draw_buffer = COGL_DRAW_BUFFER (handle); + + if (entry->draw_buffer != draw_buffer) + { + entry->target = target; + + ctx->dirty_bound_framebuffer = 1; + ctx->dirty_gl_viewport = 1; + + if (draw_buffer != COGL_INVALID_HANDLE) + cogl_handle_ref (draw_buffer); + if (entry->draw_buffer != COGL_INVALID_HANDLE) + cogl_handle_unref (entry->draw_buffer); + entry->draw_buffer = draw_buffer; + + /* We've effectively just switched the current modelview and + * projection matrix stacks and clip state so we need to dirty + * them to ensure they get flushed for the next batch of geometry + * we flush */ + _cogl_matrix_stack_dirty (draw_buffer->modelview_stack); + _cogl_matrix_stack_dirty (draw_buffer->projection_stack); + _cogl_clip_stack_state_dirty (&draw_buffer->clip_state); + } +} + +CoglHandle +_cogl_get_draw_buffer (void) +{ + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_assert (ctx->draw_buffer_stack); + entry = ctx->draw_buffer_stack->data; + + return entry->draw_buffer; +} + +void +cogl_push_draw_buffer (void) +{ + CoglDrawBufferStackEntry *old; + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + entry = g_slice_new0 (CoglDrawBufferStackEntry); + + g_assert (ctx->draw_buffer_stack); + + old = ctx->draw_buffer_stack->data; + *entry = *old; + + cogl_handle_ref (entry->draw_buffer); + + ctx->draw_buffer_stack = + g_slist_prepend (ctx->draw_buffer_stack, entry); +} + +void +cogl_pop_draw_buffer (void) +{ + CoglDrawBufferStackEntry *to_pop; + CoglDrawBufferStackEntry *to_restore; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_assert (ctx->draw_buffer_stack != NULL); + + to_pop = ctx->draw_buffer_stack->data; + to_restore = ctx->draw_buffer_stack->next->data; + + cogl_set_draw_buffer (to_restore->target, to_restore->draw_buffer); + + cogl_handle_unref (to_pop->draw_buffer); + ctx->draw_buffer_stack = + g_slist_remove_link (ctx->draw_buffer_stack, + ctx->draw_buffer_stack); + g_slice_free (CoglDrawBufferStackEntry, to_pop); +} + +void +_cogl_draw_buffer_flush_state (CoglHandle handle, + CoglDrawBufferFlushFlags flags) +{ + CoglDrawBuffer *draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = COGL_DRAW_BUFFER (handle); + + if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && + ctx->dirty_bound_framebuffer) + { + if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN) + { + GE (glBindFramebuffer (GL_FRAMEBUFFER, + COGL_OFFSCREEN (draw_buffer)->fbo_handle)); + } + else + GE (glBindFramebuffer (GL_FRAMEBUFFER, 0)); + ctx->dirty_bound_framebuffer = FALSE; + } + + if (ctx->dirty_gl_viewport) + { + int gl_viewport_y; + + /* Convert the Cogl viewport y offset to an OpenGL viewport y offset + * NB: OpenGL defines its window and viewport origins to be bottom + * left, while Cogl defines them to be top left. + * NB: We render upside down to offscreen draw buffers so we don't + * need to convert the y offset in this case. */ + if (cogl_is_offscreen (draw_buffer)) + gl_viewport_y = draw_buffer->viewport_y; + else + gl_viewport_y = draw_buffer->height - + (draw_buffer->viewport_y + draw_buffer->viewport_height); + + GE (glViewport (draw_buffer->viewport_x, + gl_viewport_y, + draw_buffer->viewport_width, + draw_buffer->viewport_height)); + ctx->dirty_gl_viewport = FALSE; + } + + /* XXX: Flushing clip state may trash the modelview and projection + * matrices so we must do it before flushing the matrices... + */ + _cogl_flush_clip_state (&draw_buffer->clip_state); + + if (!(flags & COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW)) + _cogl_matrix_stack_flush_to_gl (draw_buffer->modelview_stack, + COGL_MATRIX_MODELVIEW); + + _cogl_matrix_stack_flush_to_gl (draw_buffer->projection_stack, + COGL_MATRIX_PROJECTION); +} + diff --git a/clutter/cogl/cogl/cogl-internal.h b/clutter/cogl/cogl/cogl-internal.h index 1f84e0d05..a920a9785 100644 --- a/clutter/cogl/cogl/cogl-internal.h +++ b/clutter/cogl/cogl/cogl-internal.h @@ -34,6 +34,12 @@ typedef enum COGL_MATRIX_TEXTURE } CoglMatrixMode; +typedef enum +{ + COGL_FRONT_WINDING_CLOCKWISE, + COGL_FRONT_WINDING_COUNTER_CLOCKWISE +} CoglFrontWinding; + #ifdef HAVE_COGL_GLES2 typedef enum { COGL_BOXED_NONE, @@ -105,4 +111,7 @@ _cogl_destroy_texture_units (void); void _cogl_flush_matrix_stacks (void); +void +_cogl_flush_face_winding (void); + #endif /* __COGL_INTERNAL_H */ diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index aa0d5892e..bfa92c6be 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/cogl/cogl-journal.c @@ -32,6 +32,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-vertex-buffer-private.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -301,6 +302,7 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, enable_flags |= COGL_ENABLE_VERTEX_ARRAY; enable_flags |= COGL_ENABLE_COLOR_ARRAY; cogl_enable (enable_flags); + _cogl_flush_face_winding (); /* If we haven't transformed the quads in software then we need to also break * up batches according to changes in the modelview matrix... */ @@ -531,6 +533,8 @@ _cogl_journal_flush (void) GLuint journal_vbo; gboolean vbo_fallback = (cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE; + CoglHandle draw_buffer; + CoglMatrixStack *modelview_stack; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -547,20 +551,19 @@ _cogl_journal_flush (void) else state.vbo_offset = (char *)ctx->logged_vertices->data; - _cogl_matrix_stack_flush_to_gl (ctx->projection_stack, - COGL_MATRIX_PROJECTION); + draw_buffer = _cogl_get_draw_buffer (); + modelview_stack = _cogl_draw_buffer_get_modelview_stack (draw_buffer); + state.modelview_stack = modelview_stack; - state.modelview_stack = ctx->modelview_stack; - _cogl_matrix_stack_push (ctx->modelview_stack); + _cogl_matrix_stack_push (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... */ if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) { - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, - COGL_MATRIX_MODELVIEW); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW); } /* batch_and_call() batches a list of journal entries according to some @@ -591,7 +594,7 @@ _cogl_journal_flush (void) _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ &state); /* data */ - _cogl_matrix_stack_pop (ctx->modelview_stack); + _cogl_matrix_stack_pop (modelview_stack); for (i = 0; i < ctx->journal->len; i++) { @@ -607,6 +610,18 @@ _cogl_journal_flush (void) g_array_set_size (ctx->logged_vertices, 0); } +static void +_cogl_journal_init (void) +{ + /* Here we flush anything that we know must remain constant until the + * next the the journal is flushed. Note: This lets up flush things + * that themselves depend on the journal, such as clip state. */ + + /* NB: the journal deals with flushing the modelview stack manually */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), + COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW); +} + void _cogl_journal_log_quad (float x_1, float y_1, @@ -632,6 +647,9 @@ _cogl_journal_log_quad (float x_1, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + if (ctx->logged_vertices->len == 0) + _cogl_journal_init (); + /* The vertex data is logged into a separate array in a layout that can be * directly passed to OpenGL */ diff --git a/clutter/cogl/cogl/cogl-matrix-mesa.c b/clutter/cogl/cogl/cogl-matrix-mesa.c new file mode 100644 index 000000000..0057180bd --- /dev/null +++ b/clutter/cogl/cogl/cogl-matrix-mesa.c @@ -0,0 +1,1698 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file cogl-matrix-mesa.c + * Matrix operations. + * + * \note + * -# 4x4 transformation matrices are stored in memory in column major order. + * -# Points/vertices are to be thought of as column vectors. + * -# Transformation of a point p by a matrix M is: p' = M * p + */ + +/* + * Changes compared to the original code from Mesa: + * + * - instead of allocating matrix->m and matrix->inv using malloc, our + * public CoglMatrix typedef is large enough to directly contain the + * matrix, its inverse, a type and a set of flags. + * - instead of having a _math_matrix_analyse which updates the type, + * flags and inverse, we have _math_matrix_update_inverse which + * essentially does the same thing (internally making use of + * _math_matrix_update_type_and_flags()) but with additional guards in + * place to bail out when the inverse matrix is still valid. + * - when initializing a matrix with the identity matrix we don't + * immediately initialize the inverse matrix; rather we just set the + * dirty flag for the inverse (since it's likely the user won't request + * the inverse of the identity matrix) + */ + +#include "cogl-matrix-mesa.h" + +#include +#include + + +#define DEG2RAD (G_PI/180.0) + +/** Dot product of two 2-element vectors */ +#define DOT2(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] ) + +/** Dot product of two 3-element vectors */ +#define DOT3(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] ) + +#define CROSS3(N, U, V) \ +do { \ + (N)[0] = (U)[1]*(V)[2] - (U)[2]*(V)[1]; \ + (N)[1] = (U)[2]*(V)[0] - (U)[0]*(V)[2]; \ + (N)[2] = (U)[0]*(V)[1] - (U)[1]*(V)[0]; \ +} while (0) + +#define SUB_3V(DST, SRCA, SRCB) \ +do { \ + (DST)[0] = (SRCA)[0] - (SRCB)[0]; \ + (DST)[1] = (SRCA)[1] - (SRCB)[1]; \ + (DST)[2] = (SRCA)[2] - (SRCB)[2]; \ +} while (0) + +#define LEN_SQUARED_3FV( V ) ((V)[0]*(V)[0]+(V)[1]*(V)[1]+(V)[2]*(V)[2]) + +/** + * \defgroup MatFlags MAT_FLAG_XXX-flags + * + * Bitmasks to indicate different kinds of 4x4 matrices in CoglMatrix::flags + */ +/*@{*/ +#define MAT_FLAG_IDENTITY 0 /**< is an identity matrix flag. + * (Not actually used - the identity + * matrix is identified by the absense + * of all other flags.) + */ +#define MAT_FLAG_GENERAL 0x1 /**< is a general matrix flag */ +#define MAT_FLAG_ROTATION 0x2 /**< is a rotation matrix flag */ +#define MAT_FLAG_TRANSLATION 0x4 /**< is a translation matrix flag */ +#define MAT_FLAG_UNIFORM_SCALE 0x8 /**< is an uniform scaling matrix flag */ +#define MAT_FLAG_GENERAL_SCALE 0x10 /**< is a general scaling matrix flag */ +#define MAT_FLAG_GENERAL_3D 0x20 /**< general 3D matrix flag */ +#define MAT_FLAG_PERSPECTIVE 0x40 /**< is a perspective proj matrix flag */ +#define MAT_FLAG_SINGULAR 0x80 /**< is a singular matrix flag */ +#define MAT_DIRTY_TYPE 0x100 /**< matrix type is dirty */ +#define MAT_DIRTY_FLAGS 0x200 /**< matrix flags are dirty */ +#define MAT_DIRTY_INVERSE 0x400 /**< matrix inverse is dirty */ + +/** angle preserving matrix flags mask */ +#define MAT_FLAGS_ANGLE_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE) + +/** geometry related matrix flags mask */ +#define MAT_FLAGS_GEOMETRY (MAT_FLAG_GENERAL | \ + MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D | \ + MAT_FLAG_PERSPECTIVE | \ + MAT_FLAG_SINGULAR) + +/** length preserving matrix flags mask */ +#define MAT_FLAGS_LENGTH_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION) + + +/** 3D (non-perspective) matrix flags mask */ +#define MAT_FLAGS_3D (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D) + +/** dirty matrix flags mask */ +#define MAT_DIRTY_ALL (MAT_DIRTY_TYPE | \ + MAT_DIRTY_FLAGS | \ + MAT_DIRTY_INVERSE) + +/*@}*/ + + +/** + * Test geometry related matrix flags. + * + * \param mat a pointer to a CoglMatrix structure. + * \param a flags mask. + * + * \returns non-zero if all geometry related matrix flags are contained within + * the mask, or zero otherwise. + */ +#define TEST_MAT_FLAGS(mat, a) \ + ((MAT_FLAGS_GEOMETRY & (~(a)) & ((mat)->flags) ) == 0) + + + +/** + * Names of the corresponding CoglMatrixType values. + */ +static const char *types[] = { + "COGL_MATRIX_TYPE_GENERAL", + "COGL_MATRIX_TYPE_IDENTITY", + "COGL_MATRIX_TYPE_3D_NO_ROT", + "COGL_MATRIX_TYPE_PERSPECTIVE", + "COGL_MATRIX_TYPE_2D", + "COGL_MATRIX_TYPE_2D_NO_ROT", + "COGL_MATRIX_TYPE_3D" +}; + + +/** + * Identity matrix. + */ +static float identity[16] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +}; + + + +/**********************************************************************/ +/** \name Matrix multiplication */ +/*@{*/ + +#define A(row,col) a[(col<<2)+row] +#define B(row,col) b[(col<<2)+row] +#define R(row,col) result[(col<<2)+row] + +/** + * Perform a full 4x4 matrix multiplication. + * + * \param a matrix. + * \param b matrix. + * \param product will receive the product of \p a and \p b. + * + * \warning Is assumed that \p product != \p b. \p product == \p a is allowed. + * + * \note KW: 4*16 = 64 multiplications + * + * \author This \c matmul was contributed by Thomas Malik + */ +static void +matrix_multiply4x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 4; i++) + { + const float ai0 = A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); + } +} + +/** + * Multiply two matrices known to occupy only the top three rows, such + * as typical model matrices, and orthogonal matrices. + * + * \param a matrix. + * \param b matrix. + * \param product will receive the product of \p a and \p b. + */ +static void +matrix_multiply3x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 3; i++) + { + const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3; + } + R(3,0) = 0; + R(3,1) = 0; + R(3,2) = 0; + R(3,3) = 1; +} + +#undef A +#undef B +#undef R + +/** + * Multiply a matrix by an array of floats with known properties. + * + * \param mat pointer to a CoglMatrix structure containing the left multiplication + * matrix, and that will receive the product result. + * \param m right multiplication matrix array. + * \param flags flags of the matrix \p m. + * + * Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +static void +matrix_multiply_array_with_flags (CoglMatrix *result, + const float *array, + unsigned int flags) +{ + result->flags |= (flags | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS (result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)result, array); + else + matrix_multiply4x4 ((float *)result, (float *)result, array); +} + +/** + * Matrix multiplication. + * + * \param dest destination matrix. + * \param a left matrix. + * \param b right matrix. + * + * Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +void +_math_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b) +{ + result->flags = (a->flags | + b->flags | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS(result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)a, (float *)b); + else + matrix_multiply4x4 ((float *)result, (float *)a, (float *)b); +} + +/** + * Matrix multiplication. + * + * \param dest left and destination matrix. + * \param m right matrix array. + * + * Marks the matrix flags with general flag, and type and inverse dirty flags. + * Calls matrix_multiply4x4() for the multiplication. + */ +void +_math_matrix_multiply_array (CoglMatrix *result, const float *array) +{ + result->flags |= (MAT_FLAG_GENERAL | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE | + MAT_DIRTY_FLAGS); + + matrix_multiply4x4 ((float *)result, (float *)result, (float *)array); +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix output */ +/*@{*/ + +/** + * Print a matrix array. + * + * \param m matrix array. + * + * Called by _math_matrix_print() to print a matrix or its inverse. + */ +static void +print_matrix_floats (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] ); +} + +/** + * Dumps the contents of a CoglMatrix structure. + * + * \param m pointer to the CoglMatrix structure. + */ +void +_math_matrix_print (const CoglMatrix *matrix) +{ + g_print ("Matrix type: %s, flags: %x\n", + types[matrix->type], (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"); +} + +/*@}*/ + + +/** + * References an element of 4x4 matrix. + * + * \param m matrix array. + * \param c column of the desired element. + * \param r row of the desired element. + * + * \return value of the desired element. + * + * Calculate the linear storage index of the element and references it. + */ +#define MAT(m,r,c) (m)[(c)*4+(r)] + + +/**********************************************************************/ +/** \name Matrix inversion */ +/*@{*/ + +/** + * Swaps the values of two floating pointer variables. + * + * Used by invert_matrix_general() to swap the row pointers. + */ +#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } + +/** + * Compute inverse of 4x4 transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * \author + * Code contributed by Jacques Leroy jle@star.be + * + * Calculates the inverse matrix by performing the gaussian matrix reduction + * with partial pivoting followed by back/substitution with the loops manually + * unrolled. + */ +static gboolean +invert_matrix_general (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + float *out = matrix->inv; + float wtmp[4][8]; + float m0, m1, m2, m3, s; + float *r0, *r1, *r2, *r3; + + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + + r0[0] = MAT (m, 0, 0), r0[1] = MAT (m, 0, 1), + r0[2] = MAT (m, 0, 2), r0[3] = MAT (m, 0, 3), + r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, + + r1[0] = MAT (m, 1, 0), r1[1] = MAT (m, 1, 1), + r1[2] = MAT (m, 1, 2), r1[3] = MAT (m, 1, 3), + r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, + + r2[0] = MAT (m, 2, 0), r2[1] = MAT (m, 2, 1), + r2[2] = MAT (m, 2, 2), r2[3] = MAT (m, 2, 3), + r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, + + r3[0] = MAT (m, 3, 0), r3[1] = MAT (m, 3, 1), + r3[2] = MAT (m, 3, 2), r3[3] = MAT (m, 3, 3), + r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; + + /* choose pivot - or die */ + if (fabsf (r3[0]) > fabsf (r2[0])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[0]) > fabsf (r1[0])) + SWAP_ROWS (r2, r1); + if (fabsf (r1[0]) > fabsf (r0[0])) + SWAP_ROWS (r1, r0); + if (0.0 == r0[0]) + return FALSE; + + /* eliminate first variable */ + m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0]; + s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s; + s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s; + s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r0[5]; + if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r0[6]; + if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r0[7]; + if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[1]) > fabsf (r2[1])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[1]) > fabsf (r1[1])) + SWAP_ROWS (r2, r1); + if (0.0 == r1[1]) + return FALSE; + + /* eliminate second variable */ + m2 = r2[1] / r1[1]; m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; + s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[2]) > fabsf (r2[2])) + SWAP_ROWS (r3, r2); + if (0.0 == r2[2]) + return FALSE; + + /* eliminate third variable */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], + r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], + r3[7] -= m3 * r2[7]; + + /* last check */ + if (0.0 == r3[3]) + return FALSE; + + s = 1.0f / r3[3]; /* now back substitute row 3 */ + r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s; + + m2 = r2[3]; /* now back substitute row 2 */ + s = 1.0f / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), + r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, + r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, + r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; + + m1 = r1[2]; /* now back substitute row 1 */ + s = 1.0f / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), + r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, + r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; + + m0 = r0[1]; /* now back substitute row 0 */ + s = 1.0f / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), + r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); + + MAT (out, 0, 0) = r0[4]; MAT (out, 0, 1) = r0[5], + MAT (out, 0, 2) = r0[6]; MAT (out, 0, 3) = r0[7], + MAT (out, 1, 0) = r1[4]; MAT (out, 1, 1) = r1[5], + MAT (out, 1, 2) = r1[6]; MAT (out, 1, 3) = r1[7], + MAT (out, 2, 0) = r2[4]; MAT (out, 2, 1) = r2[5], + MAT (out, 2, 2) = r2[6]; MAT (out, 2, 3) = r2[7], + MAT (out, 3, 0) = r3[4]; MAT (out, 3, 1) = r3[5], + MAT (out, 3, 2) = r3[6]; MAT (out, 3, 3) = r3[7]; + + return TRUE; +} +#undef SWAP_ROWS + +/** + * Compute inverse of a general 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * \author Adapted from graphics gems II. + * + * Calculates the inverse of the upper left by first calculating its + * determinant and multiplying it to the symmetric adjust matrix of each + * element. Finally deals with the translation part by transforming the + * original translation vector using by the calculated submatrix inverse. + */ +static gboolean +invert_matrix_3d_general (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + float pos, neg, t; + float det; + + /* Calculate the determinant of upper left 3x3 submatrix and + * determine if the matrix is singular. + */ + pos = neg = 0.0; + t = MAT (in,0,0) * MAT (in,1,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,1,0) * MAT (in,2,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,2,0) * MAT (in,0,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,2,0) * MAT (in,1,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,1,0) * MAT (in,0,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,0,0) * MAT (in,2,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + det = pos + neg; + + if (det*det < 1e-25) + return FALSE; + + det = 1.0f / det; + MAT (out,0,0) = + ( (MAT (in, 1, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 1, 2) )*det); + MAT (out,0,1) = + (- (MAT (in, 0, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 0, 2) )*det); + MAT (out,0,2) = + ( (MAT (in, 0, 1)*MAT (in, 1, 2) - MAT (in, 1, 1)*MAT (in, 0, 2) )*det); + MAT (out,1,0) = + (- (MAT (in,1,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,1,2) )*det); + MAT (out,1,1) = + ( (MAT (in,0,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,0,2) )*det); + MAT (out,1,2) = + (- (MAT (in,0,0)*MAT (in,1,2) - MAT (in,1,0)*MAT (in,0,2) )*det); + MAT (out,2,0) = + ( (MAT (in,1,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,1,1) )*det); + MAT (out,2,1) = + (- (MAT (in,0,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,0,1) )*det); + MAT (out,2,2) = + ( (MAT (in,0,0)*MAT (in,1,1) - MAT (in,1,0)*MAT (in,0,1) )*det); + + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2 ,0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + + return TRUE; +} + +/** + * Compute inverse of a 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * If the matrix is not an angle preserving matrix then calls + * invert_matrix_3d_general for the actual calculation. Otherwise calculates + * the inverse matrix analyzing and inverting each of the scaling, rotation and + * translation parts. + */ +static gboolean +invert_matrix_3d (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (!TEST_MAT_FLAGS(matrix, MAT_FLAGS_ANGLE_PRESERVING)) + return invert_matrix_3d_general (matrix); + + if (matrix->flags & MAT_FLAG_UNIFORM_SCALE) + { + float scale = (MAT (in, 0, 0) * MAT (in, 0, 0) + + MAT (in, 0, 1) * MAT (in, 0, 1) + + MAT (in, 0, 2) * MAT (in, 0, 2)); + + if (scale == 0.0) + return FALSE; + + scale = 1.0f / scale; + + /* Transpose and scale the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = scale * MAT (in, 0, 0); + MAT (out, 1, 0) = scale * MAT (in, 0, 1); + MAT (out, 2, 0) = scale * MAT (in, 0, 2); + MAT (out, 0, 1) = scale * MAT (in, 1, 0); + MAT (out, 1, 1) = scale * MAT (in, 1, 1); + MAT (out, 2, 1) = scale * MAT (in, 1, 2); + MAT (out, 0, 2) = scale * MAT (in, 2, 0); + MAT (out, 1, 2) = scale * MAT (in, 2, 1); + MAT (out, 2, 2) = scale * MAT (in, 2, 2); + } + else if (matrix->flags & MAT_FLAG_ROTATION) + { + /* Transpose the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = MAT (in, 0, 0); + MAT (out, 1, 0) = MAT (in, 0, 1); + MAT (out, 2, 0) = MAT (in, 0, 2); + MAT (out, 0, 1) = MAT (in, 1, 0); + MAT (out, 1, 1) = MAT (in, 1, 1); + MAT (out, 2, 1) = MAT (in, 1, 2); + MAT (out, 0, 2) = MAT (in, 2, 0); + MAT (out, 1, 2) = MAT (in, 2, 1); + MAT (out, 2, 2) = MAT (in, 2, 2); + } + else + { + /* pure translation */ + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 3) = - MAT (in, 0, 3); + MAT (out, 1, 3) = - MAT (in, 1, 3); + MAT (out, 2, 3) = - MAT (in, 2, 3); + return TRUE; + } + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2, 0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + } + else + MAT (out, 0, 3) = MAT (out, 1, 3) = MAT (out, 2, 3) = 0.0; + + return TRUE; +} + +/** + * Compute inverse of an identity transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return always TRUE. + * + * Simply copies identity into CoglMatrix::inv. + */ +static gboolean +invert_matrix_identity (CoglMatrix *matrix) +{ + memcpy (matrix->inv, identity, 16 * sizeof (float)); + return TRUE; +} + +/** + * Compute inverse of a no-rotation 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calculates the + */ +static gboolean +invert_matrix_3d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in,0,0) == 0 || MAT (in,1,1) == 0 || MAT (in,2,2) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out,0,0) = 1.0f / MAT (in,0,0); + MAT (out,1,1) = 1.0f / MAT (in,1,1); + MAT (out,2,2) = 1.0f / MAT (in,2,2); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out,0,3) = - (MAT (in,0,3) * MAT (out,0,0)); + MAT (out,1,3) = - (MAT (in,1,3) * MAT (out,1,1)); + MAT (out,2,3) = - (MAT (in,2,3) * MAT (out,2,2)); + } + + return TRUE; +} + +/** + * Compute inverse of a no-rotation 2d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calculates the inverse matrix by applying the inverse scaling and + * translation to the identity matrix. + */ +static gboolean +invert_matrix_2d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in, 0, 0) == 0 || MAT (in, 1, 1) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out, 0, 3) = - (MAT (in, 0, 3) * MAT (out, 0, 0)); + MAT (out, 1, 3) = - (MAT (in, 1, 3) * MAT (out, 1, 1)); + } + + return TRUE; +} + +#if 0 +/* broken */ +static gboolean +invert_matrix_perspective (CoglMatrix *matrix) +{ + const float *in = matrix; + float *out = matrix->inv; + + if (MAT (in,2,3) == 0) + return FALSE; + + memcpy( out, identity, 16 * sizeof(float) ); + + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + MAT (out, 0, 3) = MAT (in, 0, 2); + MAT (out, 1, 3) = MAT (in, 1, 2); + + MAT (out,2,2) = 0; + MAT (out,2,3) = -1; + + MAT (out,3,2) = 1.0f / MAT (in,2,3); + MAT (out,3,3) = MAT (in,2,2) * MAT (out,3,2); + + return TRUE; +} +#endif + +/** + * Matrix inversion function pointer type. + */ +typedef gboolean (*inv_mat_func)(CoglMatrix *matrix); + +/** + * Table of the matrix inversion functions according to the matrix type. + */ +static inv_mat_func inv_mat_tab[7] = { + invert_matrix_general, + invert_matrix_identity, + invert_matrix_3d_no_rotation, +#if 0 + /* Don't use this function for now - it fails when the projection matrix + * is premultiplied by a translation (ala Chromium's tilesort SPU). + */ + invert_matrix_perspective, +#else + invert_matrix_general, +#endif + invert_matrix_3d, /* lazy! */ + invert_matrix_2d_no_rotation, + invert_matrix_3d +}; + +/** + * Compute inverse of a transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calls the matrix inversion function in inv_mat_tab corresponding to the + * given matrix type. In case of failure, updates the MAT_FLAG_SINGULAR flag, + * and copies the identity matrix into CoglMatrix::inv. + */ +gboolean +_math_matrix_update_inverse (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_FLAGS || + matrix->flags & MAT_DIRTY_INVERSE) + { + _math_matrix_update_type_and_flags (matrix); + + if (inv_mat_tab[matrix->type](matrix)) + matrix->flags &= ~MAT_FLAG_SINGULAR; + else + { + matrix->flags |= MAT_FLAG_SINGULAR; + memcpy (matrix->inv, identity, 16 * sizeof (float)); + } + + matrix->flags &= ~MAT_DIRTY_INVERSE; + } + + if (matrix->flags & MAT_FLAG_SINGULAR) + return FALSE; + else + return TRUE; +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix generation */ +/*@{*/ + +/** + * Generate a 4x4 transformation matrix from glRotate parameters, and + * post-multiply the input matrix by it. + * + * \author + * This function was contributed by Erich Boleyn (erich@uruk.org). + * Optimizations contributed by Rudolf Opalla (rudi@khm.de). + */ +void +_math_matrix_rotate (CoglMatrix *matrix, + float angle, + float x, + float y, + float z) +{ + float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; + float m[16]; + gboolean optimized; + + s = sinf (angle * DEG2RAD); + c = cosf (angle * DEG2RAD); + + memcpy (m, identity, 16 * sizeof (float)); + optimized = FALSE; + +#define M(row,col) m[col*4+row] + + if (x == 0.0f) + { + if (y == 0.0f) + { + if (z != 0.0f) + { + optimized = TRUE; + /* rotate only around z-axis */ + M (0,0) = c; + M (1,1) = c; + if (z < 0.0f) + { + M (0,1) = s; + M (1,0) = -s; + } + else + { + M (0,1) = -s; + M (1,0) = s; + } + } + } + else if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around y-axis */ + M (0,0) = c; + M (2,2) = c; + if (y < 0.0f) + { + M (0,2) = -s; + M (2,0) = s; + } + else + { + M (0,2) = s; + M (2,0) = -s; + } + } + } + else if (y == 0.0f) + { + if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around x-axis */ + M (1,1) = c; + M (2,2) = c; + if (x < 0.0f) + { + M (1,2) = s; + M (2,1) = -s; + } + else + { + M (1,2) = -s; + M (2,1) = s; + } + } + } + + if (!optimized) + { + const float mag = sqrtf (x * x + y * y + z * z); + + if (mag <= 1.0e-4) + { + /* no rotation, leave mat as-is */ + return; + } + + x /= mag; + y /= mag; + z /= mag; + + + /* + * Arbitrary axis rotation matrix. + * + * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied + * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation + * (which is about the X-axis), and the two composite transforms + * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary + * from the arbitrary axis to the X-axis then back. They are + * all elementary rotations. + * + * Rz' is a rotation about the Z-axis, to bring the axis vector + * into the x-z plane. Then Ry' is applied, rotating about the + * Y-axis to bring the axis vector parallel with the X-axis. The + * rotation about the X-axis is then performed. Ry and Rz are + * simply the respective inverse transforms to bring the arbitrary + * axis back to it's original orientation. The first transforms + * Rz' and Ry' are considered inverses, since the data from the + * arbitrary axis gives you info on how to get to it, not how + * to get away from it, and an inverse must be applied. + * + * The basic calculation used is to recognize that the arbitrary + * axis vector (x, y, z), since it is of unit length, actually + * represents the sines and cosines of the angles to rotate the + * X-axis to the same orientation, with theta being the angle about + * Z and phi the angle about Y (in the order described above) + * as follows: + * + * cos ( theta ) = x / sqrt ( 1 - z^2 ) + * sin ( theta ) = y / sqrt ( 1 - z^2 ) + * + * cos ( phi ) = sqrt ( 1 - z^2 ) + * sin ( phi ) = z + * + * Note that cos ( phi ) can further be inserted to the above + * formulas: + * + * cos ( theta ) = x / cos ( phi ) + * sin ( theta ) = y / sin ( phi ) + * + * ...etc. Because of those relations and the standard trigonometric + * relations, it is pssible to reduce the transforms down to what + * is used below. It may be that any primary axis chosen will give the + * same results (modulo a sign convention) using thie method. + * + * Particularly nice is to notice that all divisions that might + * have caused trouble when parallel to certain planes or + * axis go away with care paid to reducing the expressions. + * After checking, it does perform correctly under all cases, since + * in all the cases of division where the denominator would have + * been zero, the numerator would have been zero as well, giving + * the expected result. + */ + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * s; + ys = y * s; + zs = z * s; + one_c = 1.0f - c; + + /* We already hold the identity-matrix so we can skip some statements */ + M (0,0) = (one_c * xx) + c; + M (0,1) = (one_c * xy) - zs; + M (0,2) = (one_c * zx) + ys; + /* M (0,3) = 0.0f; */ + + M (1,0) = (one_c * xy) + zs; + M (1,1) = (one_c * yy) + c; + M (1,2) = (one_c * yz) - xs; + /* M (1,3) = 0.0f; */ + + M (2,0) = (one_c * zx) - ys; + M (2,1) = (one_c * yz) + xs; + M (2,2) = (one_c * zz) + c; + /* M (2,3) = 0.0f; */ + + /* + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; + */ + } +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_ROTATION); +} + +/** + * Apply a perspective projection matrix. + * + * \param mat matrix to apply the projection. + * \param left left clipping plane coordinate. + * \param right right clipping plane coordinate. + * \param bottom bottom clipping plane coordinate. + * \param top top clipping plane coordinate. + * \param nearval distance to the near clipping plane. + * \param farval distance to the far clipping plane. + * + * Creates the projection matrix and multiplies it with \p mat, marking the + * MAT_FLAG_PERSPECTIVE flag. + */ +void +_math_matrix_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float nearval, + float farval) +{ + float x, y, a, b, c, d; + float m[16]; + + x = (2.0f * nearval) / (right - left); + y = (2.0f * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0f * farval * nearval) / (farval - nearval); /* error? */ + +#define M(row,col) m[col*4+row] + M (0,0) = x; M (0,1) = 0.0f; M (0,2) = a; M (0,3) = 0.0f; + M (1,0) = 0.0f; M (1,1) = y; M (1,2) = b; M (1,3) = 0.0f; + M (2,0) = 0.0f; M (2,1) = 0.0f; M (2,2) = c; M (2,3) = d; + M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = -1.0f; M (3,3) = 0.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_PERSPECTIVE); +} + +/** + * Apply an orthographic projection matrix. + * + * \param mat matrix to apply the projection. + * \param left left clipping plane coordinate. + * \param right right clipping plane coordinate. + * \param bottom bottom clipping plane coordinate. + * \param top top clipping plane coordinate. + * \param nearval distance to the near clipping plane. + * \param farval distance to the far clipping plane. + * + * Creates the projection matrix and multiplies it with \p mat, marking the + * MAT_FLAG_GENERAL_SCALE and MAT_FLAG_TRANSLATION flags. + */ +void +_math_matrix_ortho (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float nearval, + float farval) +{ + float m[16]; + +#define M(row,col) m[col*4+row] + M (0,0) = 2.0f / (right-left); + M (0,1) = 0.0f; + M (0,2) = 0.0f; + M (0,3) = -(right+left) / (right-left); + + M (1,0) = 0.0f; + M (1,1) = 2.0f / (top-bottom); + M (1,2) = 0.0f; + M (1,3) = -(top+bottom) / (top-bottom); + + M (2,0) = 0.0f; + M (2,1) = 0.0f; + M (2,2) = -2.0f / (farval-nearval); + M (2,3) = -(farval+nearval) / (farval-nearval); + + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, + (MAT_FLAG_GENERAL_SCALE | + MAT_FLAG_TRANSLATION)); +} + +/** + * Multiply a matrix with a general scaling matrix. + * + * \param mat matrix. + * \param x x axis scale factor. + * \param y y axis scale factor. + * \param z z axis scale factor. + * + * Multiplies in-place the elements of \p mat by the scale factors. Checks if + * the scales factors are roughly the same, marking the MAT_FLAG_UNIFORM_SCALE + * flag, or MAT_FLAG_GENERAL_SCALE. Marks the MAT_DIRTY_TYPE and + * MAT_DIRTY_INVERSE dirty flags. + */ +void +_math_matrix_scale (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[0] *= x; m[4] *= y; m[8] *= z; + m[1] *= x; m[5] *= y; m[9] *= z; + m[2] *= x; m[6] *= y; m[10] *= z; + m[3] *= x; m[7] *= y; m[11] *= z; + + if (fabsf (x - y) < 1e-8 && fabsf (x - z) < 1e-8) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + matrix->flags |= (MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); +} + +/** + * Multiply a matrix with a translation matrix. + * + * \param mat matrix. + * \param x translation vector x coordinate. + * \param y translation vector y coordinate. + * \param z translation vector z coordinate. + * + * Adds the translation coordinates to the elements of \p mat in-place. Marks + * the MAT_FLAG_TRANSLATION flag, and the MAT_DIRTY_TYPE and MAT_DIRTY_INVERSE + * dirty flags. + */ +void +_math_matrix_translate (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[12] = m[0] * x + m[4] * y + m[8] * z + m[12]; + m[13] = m[1] * x + m[5] * y + m[9] * z + m[13]; + m[14] = m[2] * x + m[6] * y + m[10] * z + m[14]; + m[15] = m[3] * x + m[7] * y + m[11] * z + m[15]; + + matrix->flags |= (MAT_FLAG_TRANSLATION | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); +} + + +/** + * Set matrix to do viewport and depthrange mapping. + * Transforms Normalized Device Coords to window/Z values. + */ +void +_math_matrix_viewport (CoglMatrix *matrix, int x, int y, int width, int height, + float zNear, float zFar, float depthMax) +{ + float *m = (float *)matrix; + m[MAT_SX] = (float)width / 2.0f; + m[MAT_TX] = m[MAT_SX] + x; + m[MAT_SY] = (float) height / 2.0f; + m[MAT_TY] = m[MAT_SY] + y; + m[MAT_SZ] = depthMax * ((zFar - zNear) / 2.0f); + m[MAT_TZ] = depthMax * ((zFar - zNear) / 2.0f + zNear); + matrix->flags = MAT_FLAG_GENERAL_SCALE | MAT_FLAG_TRANSLATION; + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; +} + + +/** + * Set a matrix to the identity matrix. + * + * \param mat matrix. + * + * Copies ::identity into \p CoglMatrix::m, and into CoglMatrix::inv if + * not NULL. Sets the matrix type to identity, resets the flags. It + * doesn't initialize the inverse matrix, it just marks it dirty. + */ +void +_math_matrix_init_identity (CoglMatrix *matrix) +{ + memcpy (matrix, identity, 16 * sizeof (float)); + + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + matrix->flags = MAT_DIRTY_INVERSE; +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix analysis */ +/*@{*/ + +#define ZERO(x) (1<flags &= ~MAT_FLAGS_GEOMETRY; + + /* Check for translation - no-one really cares + */ + if ((mask & MASK_NO_TRX) != MASK_NO_TRX) + matrix->flags |= MAT_FLAG_TRANSLATION; + + /* Do the real work + */ + if (mask == (unsigned int) MASK_IDENTITY) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if ((mask & MASK_2D_NO_ROT) == (unsigned int) MASK_2D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + + if ((mask & MASK_NO_2D_SCALE) != MASK_NO_2D_SCALE) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_2D) == (unsigned int) MASK_2D) + { + float mm = DOT2 (m, m); + float m4m4 = DOT2 (m+4,m+4); + float mm4 = DOT2 (m,m+4); + + matrix->type = COGL_MATRIX_TYPE_2D; + + /* Check for scale */ + if (SQ (mm-1) > SQ (1e-6) || + SQ (m4m4-1) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (mm4) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_3D; + else + matrix->flags |= MAT_FLAG_ROTATION; + + } + else if ((mask & MASK_3D_NO_ROT) == (unsigned int) MASK_3D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + + /* Check for scale */ + if (SQ (m[0]-m[5]) < SQ (1e-6) && + SQ (m[0]-m[10]) < SQ (1e-6)) + { + if (SQ (m[0]-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_3D) == (unsigned int) MASK_3D) + { + float c1 = DOT3 (m,m); + float c2 = DOT3 (m+4,m+4); + float c3 = DOT3 (m+8,m+8); + float d1 = DOT3 (m, m+4); + float cp[3]; + + matrix->type = COGL_MATRIX_TYPE_3D; + + /* Check for scale */ + if (SQ (c1-c2) < SQ (1e-6) && SQ (c1-c3) < SQ (1e-6)) + { + if (SQ (c1-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + /* else no scale at all */ + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (d1) < SQ (1e-6)) + { + CROSS3 ( cp, m, m+4); + SUB_3V ( cp, cp, (m+8)); + if (LEN_SQUARED_3FV(cp) < SQ(1e-6)) + matrix->flags |= MAT_FLAG_ROTATION; + else + matrix->flags |= MAT_FLAG_GENERAL_3D; + } + else + matrix->flags |= MAT_FLAG_GENERAL_3D; /* shear, etc */ + } + else if ((mask & MASK_PERSPECTIVE) == MASK_PERSPECTIVE && m[11]==-1.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + matrix->flags |= MAT_FLAG_GENERAL; + } + else + { + matrix->type = COGL_MATRIX_TYPE_GENERAL; + matrix->flags |= MAT_FLAG_GENERAL; + } +} + +/** + * Analyze a matrix given that its flags are accurate. + * + * This is the more common operation, hopefully. + */ +static void +analyse_from_flags (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + + if (TEST_MAT_FLAGS(matrix, 0)) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if (TEST_MAT_FLAGS(matrix, (MAT_FLAG_TRANSLATION | + MAT_FLAG_UNIFORM_SCALE | + MAT_FLAG_GENERAL_SCALE))) + { + if ( m[10] == 1.0f && m[14] == 0.0f ) + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + else + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + } + else if (TEST_MAT_FLAGS (matrix, MAT_FLAGS_3D)) + { + if ( m[ 8]==0.0f + && m[ 9]==0.0f + && m[2]==0.0f && m[6]==0.0f && m[10]==1.0f && m[14]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_2D; + } + else + matrix->type = COGL_MATRIX_TYPE_3D; + } + else if ( m[4]==0.0f && m[12]==0.0f + && m[1]==0.0f && m[13]==0.0f + && m[2]==0.0f && m[6]==0.0f + && m[3]==0.0f && m[7]==0.0f && m[11]==-1.0f && m[15]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + } + else + matrix->type = COGL_MATRIX_TYPE_GENERAL; +} + +/** + * Analyze and update the type and flags of a matrix. + * + * \param mat matrix. + * + * If the matrix type is dirty then calls either analyse_from_scratch() or + * analyse_from_flags() to determine its type, according to whether the flags + * are dirty or not, respectively. If the matrix has an inverse and it's dirty + * then calls matrix_invert(). Finally clears the dirty flags. + */ +void +_math_matrix_update_type_and_flags (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_TYPE) + { + if (matrix->flags & MAT_DIRTY_FLAGS) + analyse_from_scratch (matrix); + else + analyse_from_flags (matrix); + } + + matrix->flags &= ~(MAT_DIRTY_FLAGS | MAT_DIRTY_TYPE); +} + +/*@}*/ + + +/** + * Test if the given matrix preserves vector lengths. + */ +gboolean +_math_matrix_is_length_preserving (const CoglMatrix *m) +{ + return TEST_MAT_FLAGS (m, MAT_FLAGS_LENGTH_PRESERVING); +} + + +/** + * Test if the given matrix does any rotation. + * (or perhaps if the upper-left 3x3 is non-identity) + */ +gboolean +_math_matrix_has_rotation (const CoglMatrix *matrix) +{ + if (matrix->flags & (MAT_FLAG_GENERAL | + MAT_FLAG_ROTATION | + MAT_FLAG_GENERAL_3D | + MAT_FLAG_PERSPECTIVE)) + return TRUE; + else + return FALSE; +} + + +gboolean +_math_matrix_is_general_scale (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_FLAG_GENERAL_SCALE) ? TRUE : FALSE; +} + + +gboolean +_math_matrix_is_dirty (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_DIRTY_ALL) ? TRUE : FALSE; +} + + +/**********************************************************************/ +/** \name Matrix setup */ +/*@{*/ + +/** + * Loads a matrix array into CoglMatrix. + * + * \param m matrix array. + * \param mat matrix. + * + * Copies \p m into CoglMatrix::m and marks the MAT_FLAG_GENERAL and + * MAT_DIRTY_ALL + * flags. + */ +void +_math_matrix_init_from_array (CoglMatrix *matrix, const float *array) +{ + memcpy (matrix, array, 16 * sizeof (float)); + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix transpose */ +/*@{*/ + +/** + * Transpose a float matrix. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposef (float to[16], const float from[16]) +{ + to[0] = from[0]; + to[1] = from[4]; + to[2] = from[8]; + to[3] = from[12]; + to[4] = from[1]; + to[5] = from[5]; + to[6] = from[9]; + to[7] = from[13]; + to[8] = from[2]; + to[9] = from[6]; + to[10] = from[10]; + to[11] = from[14]; + to[12] = from[3]; + to[13] = from[7]; + to[14] = from[11]; + to[15] = from[15]; +} + +/** + * Transpose a double matrix. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposed (double to[16], const double from[16]) +{ + to[0] = from[0]; + to[1] = from[4]; + to[2] = from[8]; + to[3] = from[12]; + to[4] = from[1]; + to[5] = from[5]; + to[6] = from[9]; + to[7] = from[13]; + to[8] = from[2]; + to[9] = from[6]; + to[10] = from[10]; + to[11] = from[14]; + to[12] = from[3]; + to[13] = from[7]; + to[14] = from[11]; + to[15] = from[15]; +} + +/** + * Transpose a double matrix and convert to float. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposefd (float to[16], const double from[16]) +{ + to[0] = (float)from[0]; + to[1] = (float)from[4]; + to[2] = (float)from[8]; + to[3] = (float)from[12]; + to[4] = (float)from[1]; + to[5] = (float)from[5]; + to[6] = (float)from[9]; + to[7] = (float)from[13]; + to[8] = (float)from[2]; + to[9] = (float)from[6]; + to[10] = (float)from[10]; + to[11] = (float)from[14]; + to[12] = (float)from[3]; + to[13] = (float)from[7]; + to[14] = (float)from[11]; + to[15] = (float)from[15]; +} + +/*@}*/ + + +/** + * Transform a 4-element row vector (1x4 matrix) by a 4x4 matrix. This + * function is used for transforming clipping plane equations and spotlight + * directions. + * Mathematically, u = v * m. + * Input: v - input vector + * m - transformation matrix + * Output: u - transformed vector + */ +void +_mesa_transform_vector (float u[4], const float v[4], const float m[16]) +{ + const float v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; +#define M(row,col) m[row + col*4] + u[0] = v0 * M (0,0) + v1 * M (1,0) + v2 * M (2,0) + v3 * M (3,0); + u[1] = v0 * M (0,1) + v1 * M (1,1) + v2 * M (2,1) + v3 * M (3,1); + u[2] = v0 * M (0,2) + v1 * M (1,2) + v2 * M (2,2) + v3 * M (3,2); + u[3] = v0 * M (0,3) + v1 * M (1,3) + v2 * M (2,3) + v3 * M (3,3); +#undef M +} + diff --git a/clutter/cogl/cogl/cogl-matrix-mesa.h b/clutter/cogl/cogl/cogl-matrix-mesa.h new file mode 100644 index 000000000..1babce7f4 --- /dev/null +++ b/clutter/cogl/cogl/cogl-matrix-mesa.h @@ -0,0 +1,226 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file math/m_matrix.h + * Defines basic structures for matrix-handling. + */ + +#ifndef _M_MATRIX_H +#define _M_MATRIX_H + +#include + +#include + +/** + * \name Symbolic names to some of the entries in the matrix + * + * These are handy for the viewport mapping, which is expressed as a matrix. + */ +/*@{*/ +#define MAT_SX 0 +#define MAT_SY 5 +#define MAT_SZ 10 +#define MAT_TX 12 +#define MAT_TY 13 +#define MAT_TZ 14 +/*@}*/ + + +/** + * Different kinds of 4x4 transformation matrices. + * We use these to select specific optimized vertex transformation routines. + */ +enum CoglMatrixType { + COGL_MATRIX_TYPE_GENERAL, /**< general 4x4 matrix */ + COGL_MATRIX_TYPE_IDENTITY, /**< identity matrix */ + COGL_MATRIX_TYPE_3D_NO_ROT, /**< orthogonal projection and others... */ + COGL_MATRIX_TYPE_PERSPECTIVE, /**< perspective projection matrix */ + COGL_MATRIX_TYPE_2D, /**< 2-D transformation */ + COGL_MATRIX_TYPE_2D_NO_ROT, /**< 2-D scale & translate only */ + COGL_MATRIX_TYPE_3D /**< 3-D transformation */ +} ; + + +#if 0 +/** + * Matrix type to represent 4x4 transformation matrices. + */ +typedef struct { + float *m; /**< 16 matrix elements (16-byte aligned) */ + float *inv; /**< optional 16-element inverse (16-byte aligned) */ + unsigned int flags; /**< possible values determined by (of \link + * MatFlags MAT_FLAG_* flags\endlink) + */ + enum CoglMatrixType type; +} CoglMatrix; +#endif + + +void +_math_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b); + +void +_math_matrix_multiply_array (CoglMatrix *result, const float *b); + +void +_math_matrix_init_from_array (CoglMatrix *matrix, const float *array); + +void +_math_matrix_translate (CoglMatrix *matrix, float x, float y, float z); + +void +_math_matrix_rotate (CoglMatrix *matrix, float angle, + float x, float y, float z); + +void +_math_matrix_scale (CoglMatrix *matrix, float x, float y, float z); + +void +_math_matrix_ortho (CoglMatrix *matrix, + float left, float right, + float bottom, float top, + float nearval, float farval); + +void +_math_matrix_frustum (CoglMatrix *matrix, + float left, float right, + float bottom, float top, + float nearval, float farval); + +void +_math_matrix_viewport (CoglMatrix *matrix, + int x, int y, int width, int height, + float z_near, float z_far, float depth_max); + +void +_math_matrix_init_identity (CoglMatrix *matrix); + +gboolean +_math_matrix_update_inverse (CoglMatrix *matrix); + +void +_math_matrix_update_type_and_flags (CoglMatrix *matrix); + +void +_math_matrix_print (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_length_preserving (const CoglMatrix *matrix); + +gboolean +_math_matrix_has_rotation (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_general_scale (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_dirty (const CoglMatrix *matrix); + + +/** + * \name Related functions that don't actually operate on CoglMatrix structs + */ +/*@{*/ + +void +_math_transposef ( float to[16], const float from[16]); + +void +_math_transposed (double to[16], const double from[16]); + +void +_math_transposefd (float to[16], const double from[16]); + + +/* + * Transform a point (column vector) by a matrix: Q = M * P + */ +#define TRANSFORM_POINT( Q, M, P ) \ + Q[0] = M[0] * P[0] + M[4] * P[1] + M[8] * P[2] + M[12] * P[3]; \ + Q[1] = M[1] * P[0] + M[5] * P[1] + M[9] * P[2] + M[13] * P[3]; \ + Q[2] = M[2] * P[0] + M[6] * P[1] + M[10] * P[2] + M[14] * P[3]; \ + Q[3] = M[3] * P[0] + M[7] * P[1] + M[11] * P[2] + M[15] * P[3]; + + +#define TRANSFORM_POINT3( Q, M, P ) \ + Q[0] = M[0] * P[0] + M[4] * P[1] + M[8] * P[2] + M[12]; \ + Q[1] = M[1] * P[0] + M[5] * P[1] + M[9] * P[2] + M[13]; \ + Q[2] = M[2] * P[0] + M[6] * P[1] + M[10] * P[2] + M[14]; \ + Q[3] = M[3] * P[0] + M[7] * P[1] + M[11] * P[2] + M[15]; + + +/* + * Transform a normal (row vector) by a matrix: [NX NY NZ] = N * MAT + */ +#define TRANSFORM_NORMAL( TO, N, MAT ) \ +do { \ + TO[0] = N[0] * MAT[0] + N[1] * MAT[1] + N[2] * MAT[2]; \ + TO[1] = N[0] * MAT[4] + N[1] * MAT[5] + N[2] * MAT[6]; \ + TO[2] = N[0] * MAT[8] + N[1] * MAT[9] + N[2] * MAT[10]; \ +} while (0) + + +/** + * Transform a direction by a matrix. + */ +#define TRANSFORM_DIRECTION( TO, DIR, MAT ) \ +do { \ + TO[0] = DIR[0] * MAT[0] + DIR[1] * MAT[4] + DIR[2] * MAT[8]; \ + TO[1] = DIR[0] * MAT[1] + DIR[1] * MAT[5] + DIR[2] * MAT[9]; \ + TO[2] = DIR[0] * MAT[2] + DIR[1] * MAT[6] + DIR[2] * MAT[10];\ +} while (0) + + +void +_mesa_transform_vector (float u[4], const float v[4], const float m[16]); + + +/*@}*/ + + +#endif diff --git a/clutter/cogl/cogl/driver/gl/cogl-fbo.h b/clutter/cogl/cogl/cogl-matrix-private.h similarity index 62% rename from clutter/cogl/cogl/driver/gl/cogl-fbo.h rename to clutter/cogl/cogl/cogl-matrix-private.h index 8499f8fb0..4323815e9 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-fbo.h +++ b/clutter/cogl/cogl/cogl-matrix-private.h @@ -3,7 +3,7 @@ * * An object oriented GL/GLES Abstraction/Utility Layer * - * Copyright (C) 2007,2008,2009 Intel Corporation. + * Copyright (C) 2008,2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,21 +19,29 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg */ -#ifndef __COGL_FBO_H -#define __COGL_FBO_H +#ifndef __COGL_MATRIX_PRIVATE_H +#define __COGL_MATRIX_PRIVATE_H -#include "cogl-handle.h" +#include -typedef struct -{ - CoglHandleObject _parent; - int width; - int height; - GLuint gl_handle; - GLuint gl_stencil_handle; +G_BEGIN_DECLS -} CoglFbo; +#define _COGL_MATRIX_DEBUG_PRINT(MATRIX) \ + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_MATRICES)) \ + { \ + g_print ("%s:\n", G_STRFUNC); \ + _cogl_matrix_print (MATRIX); \ + } + +void +_cogl_matrix_print (CoglMatrix *matrix); + +G_END_DECLS + +#endif /* __COGL_MATRIX_PRIVATE_H */ -#endif /* __COGL_FBO_H */ diff --git a/clutter/cogl/cogl/cogl-matrix-stack.c b/clutter/cogl/cogl/cogl-matrix-stack.c index b1f21c946..c6236efd3 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.c +++ b/clutter/cogl/cogl/cogl-matrix-stack.c @@ -22,6 +22,7 @@ * * Authors: * Havoc Pennington for litl + * Robert Bragg */ #ifdef HAVE_CONFIG_H @@ -32,6 +33,7 @@ #include "cogl-context.h" #include "cogl-internal.h" #include "cogl-matrix-stack.h" +#include "cogl-draw-buffer-private.h" typedef struct { CoglMatrix matrix; @@ -346,6 +348,17 @@ _cogl_matrix_stack_ortho (CoglMatrixStack *stack, state->is_identity = FALSE; } +gboolean +_cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse) +{ + CoglMatrixState *state; + + 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) @@ -412,21 +425,35 @@ _cogl_matrix_stack_flush_to_gl (CoglMatrixStack *stack, ctx->flushed_matrix_mode = mode; } - /* In theory it might help the GL implementation if we used our - * local analysis of the matrix and called Translate/Scale rather - * than LoadMatrix to send a 2D matrix + /* Because Cogl defines texture coordinates to have a top left origin and + * because offscreen draw buffers may be used for rendering to textures we + * always render upside down to offscreen buffers. */ - - if (state->is_identity) + if (mode == COGL_MATRIX_PROJECTION && + cogl_is_offscreen (_cogl_get_draw_buffer ())) { - if (!stack->flushed_identity) - GE (glLoadIdentity ()); - stack->flushed_identity = TRUE; + CoglMatrix flipped_projection; + CoglMatrix *projection = + state->is_identity ? &ctx->identity_matrix : &state->matrix; + + cogl_matrix_multiply (&flipped_projection, + &ctx->y_flip_matrix, projection); + GE (glLoadMatrixf (cogl_matrix_get_array (&flipped_projection))); + stack->flushed_identity = FALSE; } else { - GE (glLoadMatrixf (cogl_matrix_get_array (&state->matrix))); - stack->flushed_identity = FALSE; + if (state->is_identity) + { + if (!stack->flushed_identity) + GE (glLoadIdentity ()); + stack->flushed_identity = TRUE; + } + else + { + GE (glLoadMatrixf (cogl_matrix_get_array (&state->matrix))); + stack->flushed_identity = FALSE; + } } stack->flushed_state = state; } diff --git a/clutter/cogl/cogl/cogl-matrix-stack.h b/clutter/cogl/cogl/cogl-matrix-stack.h index 1762eda95..648eace84 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.h +++ b/clutter/cogl/cogl/cogl-matrix-stack.h @@ -71,6 +71,9 @@ void _cogl_matrix_stack_ortho (CoglMatrixStack *stack, float top, float z_near, float z_far); + +gboolean _cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse); void _cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix); void _cogl_matrix_stack_set (CoglMatrixStack *stack, diff --git a/clutter/cogl/cogl/cogl-matrix.c b/clutter/cogl/cogl/cogl-matrix.c index 4cc635985..5eb0f9782 100644 --- a/clutter/cogl/cogl/cogl-matrix.c +++ b/clutter/cogl/cogl/cogl-matrix.c @@ -24,20 +24,41 @@ * Robert Bragg */ +#define USE_MESA_MATRIX_API + #include #include +#include +#ifdef USE_MESA_MATRIX_API +#include +#endif #include #include #include +void +_cogl_matrix_print (CoglMatrix *matrix) +{ + float *m = (float *)matrix; + int y; + + for (y = 0; y < 4; y++) + g_print ("\t%6.4f %6.4f %6.4f %6.4f\n", m[y], m[4+y], m[8+y], m[12+y]); +} + void cogl_matrix_init_identity (CoglMatrix *matrix) { +#ifndef USE_MESA_MATRIX_API matrix->xx = 1; matrix->xy = 0; matrix->xz = 0; matrix->xw = 0; matrix->yx = 0; matrix->yy = 1; matrix->yz = 0; matrix->yw = 0; matrix->zx = 0; matrix->zy = 0; matrix->zz = 1; matrix->zw = 0; matrix->wx = 0; matrix->wy = 0; matrix->wz = 0; matrix->ww = 1; +#else + _math_matrix_init_identity (matrix); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -45,6 +66,7 @@ cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, const CoglMatrix *b) { +#ifndef USE_MESA_MATRIX_API CoglMatrix r; /* row 0 */ @@ -73,10 +95,14 @@ cogl_matrix_multiply (CoglMatrix *result, /* The idea was that having this unrolled; it might be easier for the * compiler to vectorize, but that's probably not true. Mesa does it - * using a single for (i=0; i<4; i++) approach, may that's better... + * using a single for (i=0; i<4; i++) approach, maybe that's better... */ *result = r; +#else + _math_matrix_multiply (result, a, b); +#endif + _COGL_MATRIX_DEBUG_PRINT (result); } void @@ -86,6 +112,7 @@ cogl_matrix_rotate (CoglMatrix *matrix, float y, float z) { +#ifndef USE_MESA_MATRIX_API CoglMatrix rotation; CoglMatrix result; float c, s; @@ -116,6 +143,10 @@ cogl_matrix_rotate (CoglMatrix *matrix, cogl_matrix_multiply (&result, matrix, &rotation); *matrix = result; +#else + _math_matrix_rotate (matrix, angle, x, y, z); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -124,10 +155,15 @@ cogl_matrix_translate (CoglMatrix *matrix, float y, float z) { +#ifndef USE_MESA_MATRIX_API matrix->xw = matrix->xx * x + matrix->xy * y + matrix->xz * z + matrix->xw; matrix->yw = matrix->yx * x + matrix->yy * y + matrix->yz * z + matrix->yw; matrix->zw = matrix->zx * x + matrix->zy * y + matrix->zz * z + matrix->zw; matrix->ww = matrix->wx * x + matrix->wy * y + matrix->wz * z + matrix->ww; +#else + _math_matrix_translate (matrix, x, y, z); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -136,23 +172,16 @@ cogl_matrix_scale (CoglMatrix *matrix, float sy, float sz) { +#ifndef USE_MESA_MATRIX_API matrix->xx *= sx; matrix->xy *= sy; matrix->xz *= sz; matrix->yx *= sx; matrix->yy *= sy; matrix->yz *= sz; matrix->zx *= sx; matrix->zy *= sy; matrix->zz *= sz; matrix->wx *= sx; matrix->wy *= sy; matrix->wz *= sz; -} - -#if 0 -gboolean -cogl_matrix_invert (CoglMatrix *matrix) -{ - /* TODO */ - /* Note: It might be nice to also use the flag based tricks that mesa does - * to alow it to track the type of transformations a matrix represents - * so it can use various assumptions to optimise the inversion. - */ -} +#else + _math_matrix_scale (matrix, sx, sy, sz); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); +} void cogl_matrix_frustum (CoglMatrix *matrix, @@ -163,6 +192,7 @@ cogl_matrix_frustum (CoglMatrix *matrix, float z_near, float z_far) { +#ifndef USE_MESA_MATRIX_API float x, y, a, b, c, d; CoglMatrix frustum; @@ -194,6 +224,10 @@ cogl_matrix_frustum (CoglMatrix *matrix, frustum.ww = 0.0f; cogl_matrix_multiply (matrix, matrix, &frustum); +#else + _math_matrix_frustum (matrix, left, right, bottom, top, z_near, z_far); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -212,6 +246,7 @@ cogl_matrix_perspective (CoglMatrix *matrix, ymax, /* top */ z_near, z_far); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -223,6 +258,7 @@ cogl_matrix_ortho (CoglMatrix *matrix, float near_val, float far_val) { +#ifndef USE_MESA_MATRIX_API CoglMatrix ortho; /* column 0 */ @@ -250,12 +286,21 @@ cogl_matrix_ortho (CoglMatrix *matrix, ortho.ww = 1.0; cogl_matrix_multiply (matrix, matrix, &ortho); +#else + _math_matrix_ortho (matrix, left, right, bottom, top, near_val, far_val); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) { +#ifndef USE_MESA_MATRIX_API memcpy (matrix, array, sizeof (float) * 16); +#else + _math_matrix_init_from_array (matrix, array); +#endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } const float * @@ -264,6 +309,27 @@ cogl_matrix_get_array (const CoglMatrix *matrix) return (float *)matrix; } +gboolean +cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse) +{ +#ifndef USE_MESA_MATRIX_API +#warning "cogl_matrix_get_inverse not supported without Mesa matrix API" + cogl_matrix_init_identity (inverse); + return FALSE; +#else + if (_math_matrix_update_inverse ((CoglMatrix *)matrix)) + { + cogl_matrix_init_from_array (inverse, matrix->inv); + return TRUE; + } + else + { + cogl_matrix_init_identity (inverse); + return FALSE; + } +#endif +} + void cogl_matrix_transform_point (const CoglMatrix *matrix, float *x, diff --git a/clutter/cogl/cogl/cogl-matrix.h b/clutter/cogl/cogl/cogl-matrix.h index e2c07f5c8..19e3e133b 100644 --- a/clutter/cogl/cogl/cogl-matrix.h +++ b/clutter/cogl/cogl/cogl-matrix.h @@ -101,9 +101,9 @@ struct _CoglMatrix /* Note: we may want to extend this later with private flags * and a cache of the inverse transform matrix. */ - float _padding0[16]; - gulong _padding1; - gulong _padding2; + float inv[16]; + gulong type; + gulong flags; gulong _padding3; }; @@ -266,6 +266,27 @@ void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array); */ G_CONST_RETURN float *cogl_matrix_get_array (const CoglMatrix *matrix); +/** + * cogl_matrix_get_inverse: + * @matrix: A 4x4 transformation matrix + * @inverse: The destination for a 4x4 inverse transformation matrix + * + * This gets the inverse transform of a given matrix and uses it to initialize + * a new CoglMatrix. + * + * Note: that although the first parameter is annotated as const to indicate + * that the transform it represents isn't modified this function may + * technically save a copy of the inverse transform within the given CoglMatrix + * so that subsequent requests for the inverse transform may avoid costly + * inversion calculations. + * + * Returns TRUE if the inverse was successfully calculated or FALSE for + * degenerate transformations that can't be inverted (in this case the matrix + * will simply be initialized with the identity matrix) + */ +gboolean +cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse); + /** * cogl_matrix_transform_point: * @matrix: A 4x4 transformation matrix diff --git a/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index 016833ad3..af32264d7 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/cogl/cogl-primitives.c @@ -32,6 +32,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-vertex-buffer-private.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -396,8 +397,6 @@ _cogl_rectangles_with_multitexture_coords ( _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_clip_ensure (); - material = ctx->source_material; layers = cogl_material_get_layers (material); @@ -706,7 +705,6 @@ draw_polygon_sub_texture_cb (CoglHandle tex_handle, options.layer0_override_texture = gl_handle; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices)); } @@ -840,7 +838,6 @@ _cogl_multitexture_polygon_single_primitive (CoglTextureVertex *vertices, options.flags |= COGL_MATERIAL_FLUSH_SKIP_GL_COLOR; options.fallback_layers = fallback_layers; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices)); } @@ -866,7 +863,11 @@ cogl_polygon (CoglTextureVertex *vertices, _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_journal_flush (); - cogl_clip_ensure (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); material = ctx->source_material; layers = cogl_material_get_layers (ctx->source_material); @@ -978,6 +979,7 @@ cogl_polygon (CoglTextureVertex *vertices, } cogl_enable (enable_flags); + _cogl_flush_face_winding (); GE (glVertexPointer (3, GL_FLOAT, stride_bytes, v)); @@ -1030,9 +1032,6 @@ cogl_path_fill_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_journal_flush (); - cogl_clip_ensure (); - if (ctx->path_nodes->len == 0) return; @@ -1055,10 +1054,7 @@ cogl_path_stroke_preserve (void) if (ctx->path_nodes->len == 0) return; - _cogl_journal_flush (); - cogl_clip_ensure (); - - _cogl_path_stroke_nodes(); + _cogl_path_stroke_nodes (); } void diff --git a/clutter/cogl/cogl/cogl-texture-2d-sliced.c b/clutter/cogl/cogl/cogl-texture-2d-sliced.c index 9b70860e8..52eec49fe 100644 --- a/clutter/cogl/cogl/cogl-texture-2d-sliced.c +++ b/clutter/cogl/cogl/cogl-texture-2d-sliced.c @@ -907,7 +907,7 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; /* Rowstride from width */ bpp = _cogl_get_format_bpp (internal_format); diff --git a/clutter/cogl/cogl/cogl-texture.c b/clutter/cogl/cogl/cogl-texture.c index fa33f047d..763edbc2e 100644 --- a/clutter/cogl/cogl/cogl-texture.c +++ b/clutter/cogl/cogl/cogl-texture.c @@ -42,6 +42,7 @@ #include "cogl-context.h" #include "cogl-handle.h" #include "cogl-primitives.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -667,7 +668,8 @@ _cogl_texture_draw_and_read (CoglTexture *tex, GLuint target_gl_type) { gint bpp; - GLint viewport[4]; + CoglHandle draw_buffer; + int viewport[4]; CoglBitmap alpha_bmp; CoglHandle prev_source; CoglMatrixStack *projection_stack; @@ -677,8 +679,9 @@ _cogl_texture_draw_and_read (CoglTexture *tex, bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + draw_buffer = _cogl_get_draw_buffer (); /* Viewport needs to have some size and be inside the window for this */ - GE( glGetIntegerv (GL_VIEWPORT, viewport)); + _cogl_draw_buffer_get_viewport4fv (draw_buffer, viewport); if (viewport[0] < 0 || viewport[1] < 0 || viewport[2] <= 0 || viewport[3] <= 0) return FALSE; @@ -688,16 +691,18 @@ _cogl_texture_draw_and_read (CoglTexture *tex, * works) */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - _cogl_matrix_stack_ortho (ctx->projection_stack, + projection_stack = _cogl_draw_buffer_get_projection_stack (draw_buffer); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_ortho (projection_stack, 0, (float)(viewport[2]), 0, (float)(viewport[3]), (float)(0), (float)(100)); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + modelview_stack = _cogl_draw_buffer_get_modelview_stack (draw_buffer); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); /* Direct copy operation */ diff --git a/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c index c1aa172f4..2ded6eeb4 100644 --- a/clutter/cogl/cogl/cogl-vertex-buffer.c +++ b/clutter/cogl/cogl/cogl-vertex-buffer.c @@ -138,6 +138,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-primitives.h" +#include "cogl-draw-buffer-private.h" #define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \ (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1))) @@ -1664,6 +1665,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) } ctx->n_texcoord_arrays_enabled = max_texcoord_attrib_unit + 1; + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + options.flags = COGL_MATERIAL_FLUSH_FALLBACK_MASK | COGL_MATERIAL_FLUSH_DISABLE_MASK; @@ -1677,6 +1683,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; cogl_enable (enable_flags); + _cogl_flush_face_winding (); } static void @@ -1753,15 +1760,11 @@ cogl_vertex_buffer_draw (CoglHandle handle, return; _cogl_journal_flush (); - cogl_clip_ensure (); buffer = _cogl_vertex_buffer_pointer_from_handle (handle); - cogl_clip_ensure (); - _cogl_flush_matrix_stacks (); enable_state_for_drawing_buffer (buffer); - /* FIXME: flush cogl cache */ GE (glDrawArrays (mode, first, count)); disable_state_for_drawing_buffer (buffer); @@ -1885,7 +1888,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, return; _cogl_journal_flush (); - cogl_clip_ensure (); buffer = _cogl_vertex_buffer_pointer_from_handle (handle); @@ -1894,8 +1896,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle); - cogl_clip_ensure (); - _cogl_flush_matrix_stacks (); enable_state_for_drawing_buffer (buffer); byte_offset = indices_offset * get_indices_type_size (indices->type); @@ -1905,7 +1905,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, GPOINTER_TO_UINT (indices->vbo_name))); - /* FIXME: flush cogl cache */ GE (glDrawRangeElements (mode, min_index, max_index, count, indices->type, (void *)byte_offset)); diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 4fb1c7e67..7631eee5b 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/cogl/cogl.c @@ -38,6 +38,8 @@ #include "cogl-context.h" #include "cogl-material-private.h" #include "cogl-winsys.h" +#include "cogl-draw-buffer-private.h" +#include "cogl-matrix-private.h" #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) #include "cogl-gles2-wrapper.h" @@ -116,7 +118,12 @@ cogl_clear (const CoglColor *color, gulong buffers) COGL_NOTE (DRAW, "Clear begin"); - cogl_clip_ensure (); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); if (buffers & COGL_BUFFER_BIT_COLOR) { @@ -264,6 +271,9 @@ cogl_set_backface_culling_enabled (gboolean setting) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + if (ctx->enable_backface_culling == setting) + return; + /* Currently the journal can't track changes to backface culling state... */ _cogl_journal_flush (); @@ -278,6 +288,38 @@ cogl_get_backface_culling_enabled (void) return ctx->enable_backface_culling; } +void +_cogl_flush_face_winding (void) +{ + CoglFrontWinding winding; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* The front face winding doesn't matter if we aren't performing any + * backface culling... */ + if (!ctx->enable_backface_culling) + return; + + /* NB: We use a clockwise face winding order when drawing offscreen because + * all offscreen rendering is done upside down resulting in reversed winding + * for all triangles. + */ + if (cogl_is_offscreen (_cogl_get_draw_buffer ())) + winding = COGL_FRONT_WINDING_CLOCKWISE; + else + winding = COGL_FRONT_WINDING_COUNTER_CLOCKWISE; + + if (winding != ctx->flushed_front_winding) + { + + if (winding == COGL_FRONT_WINDING_CLOCKWISE) + GE (glFrontFace (GL_CW)); + else + GE (glFrontFace (GL_CCW)); + ctx->flushed_front_winding = winding; + } +} + void cogl_set_source_color (const CoglColor *color) { @@ -326,32 +368,38 @@ set_clip_plane (GLint plane_num, GLdouble plane[4]; #endif GLfloat angle; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); CoglMatrix inverse_projection; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection); + /* Calculate the angle between the axes and the line crossing the two points */ angle = atan2f (vertex_b[1] - vertex_a[1], vertex_b[0] - vertex_a[0]) * (180.0/G_PI); - _cogl_matrix_stack_push (ctx->modelview_stack); - /* Load the identity matrix and multiply by the reverse of the - projection matrix so we can specify the plane in screen - coordinates */ - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - cogl_matrix_init_from_array (&inverse_projection, - ctx->inverse_projection); - _cogl_matrix_stack_multiply (ctx->modelview_stack, &inverse_projection); + _cogl_matrix_stack_push (modelview_stack); + + /* Load the inverse of the projection matrix so we can specify the plane + * in screen coordinates */ + _cogl_matrix_stack_set (modelview_stack, &inverse_projection); + /* Rotate about point a */ - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_translate (modelview_stack, vertex_a[0], vertex_a[1], vertex_a[2]); /* Rotate the plane by the calculated angle so that it will connect the two points */ - _cogl_matrix_stack_rotate (ctx->modelview_stack, angle, 0.0f, 0.0f, 1.0f); - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f); + _cogl_matrix_stack_translate (modelview_stack, -vertex_a[0], -vertex_a[1], -vertex_a[2]); - _cogl_flush_matrix_stacks (); + _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW); plane[0] = 0; plane[1] = -1.0; @@ -363,7 +411,7 @@ set_clip_plane (GLint plane_num, GE( glClipPlane (plane_num, plane) ); #endif - _cogl_matrix_stack_pop (ctx->modelview_stack); + _cogl_matrix_stack_pop (modelview_stack); } void @@ -372,7 +420,12 @@ _cogl_set_clip_planes (float x_offset, float width, float height) { + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); CoglMatrix modelview_matrix; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); CoglMatrix projection_matrix; float vertex_tl[4] = { x_offset, y_offset, 0, 1.0 }; @@ -381,10 +434,8 @@ _cogl_set_clip_planes (float x_offset, float vertex_br[4] = { x_offset + width, y_offset + height, 0, 1.0 }; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_matrix_stack_get (ctx->projection_stack, &projection_matrix); - _cogl_matrix_stack_get (ctx->modelview_stack, &modelview_matrix); + _cogl_matrix_stack_get (projection_stack, &projection_matrix); + _cogl_matrix_stack_get (modelview_stack, &modelview_matrix); project_vertex (&modelview_matrix, &projection_matrix, vertex_tl); project_vertex (&modelview_matrix, &projection_matrix, vertex_tr); @@ -422,6 +473,7 @@ _cogl_add_stencil_clip (float x_offset, gboolean first) { CoglHandle current_source; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -429,6 +481,8 @@ _cogl_add_stencil_clip (float x_offset, * batched geometry before we start... */ _cogl_journal_flush (); + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* temporarily swap in our special stenciling material */ current_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->stencil_material); @@ -450,6 +504,11 @@ _cogl_add_stencil_clip (float x_offset, } else { + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + /* Add one to every pixel of the stencil buffer in the rectangle */ GE( glStencilFunc (GL_NEVER, 0x1, 0x3) ); @@ -466,16 +525,16 @@ _cogl_add_stencil_clip (float x_offset, rectangle are set will be valid */ GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } /* make sure our rectangles hit the stencil buffer before we restore @@ -515,19 +574,32 @@ _cogl_disable_clip_planes (void) GE( glDisable (GL_CLIP_PLANE0) ); } -/* XXX: This should be deprecated and Cogl should be left to manage - * the glViewport automatically when switching draw buffers. */ +void +cogl_set_viewport (int x, + int y, + int width, + int height) +{ + CoglHandle draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = _cogl_get_draw_buffer (); + + _cogl_draw_buffer_set_viewport (draw_buffer, + x, + y, + width, + height); +} + +/* XXX: This should be deprecated, and we should expose a way to also + * specify an x and y viewport offset */ void cogl_viewport (guint width, guint height) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - COGL_NOTE (MISC, "glViewport(0, 0, %u, %u)", width, height); - GE( glViewport (0, 0, width, height) ); - - ctx->viewport_width = width; - ctx->viewport_height = height; + cogl_set_viewport (0, 0, width, height); } void @@ -540,10 +612,11 @@ _cogl_setup_viewport (guint width, { float z_camera; CoglMatrix projection_matrix; + CoglMatrixStack *modelview_stack; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_viewport (width, height); + cogl_set_viewport (0, 0, width, height); /* For Ortho projection. * _cogl_matrix_stack_ortho (projection_stack, 0, width, 0, height, -1, 1); @@ -593,11 +666,13 @@ _cogl_setup_viewport (guint width, cogl_get_projection_matrix (&projection_matrix); z_camera = 0.5 * projection_matrix.xx; - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - _cogl_matrix_stack_translate (ctx->modelview_stack, -0.5f, -0.5f, -z_camera); - _cogl_matrix_stack_scale (ctx->modelview_stack, + modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_translate (modelview_stack, -0.5f, -0.5f, -z_camera); + _cogl_matrix_stack_scale (modelview_stack, 1.0f / width, -1.0f / height, 1.0f / width); - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_translate (modelview_stack, 0.0f, -1.0 * height, 0.0f); } @@ -606,9 +681,6 @@ cogl_get_features (void) { _COGL_GET_CONTEXT (ctx, 0); - if (!ctx->features_cached) - _cogl_features_init (); - if (cogl_debug_flags & COGL_DEBUG_DISABLE_VBOS) ctx->feature_flags &= ~COGL_FEATURE_VBOS; @@ -626,18 +698,24 @@ cogl_features_available (CoglFeatureFlags features) return (ctx->feature_flags & features) == features; } -/* XXX: This function should be deprecated, and replaced with a - * cogl_draw_buffer_get_size() API instead. We don't support offset - * viewports, and you can't have floating point viewport sizes. */ +/* XXX: This function should either be replaced with one returning + * integers, or removed/deprecated and make the + * _cogl_draw_buffer_get_viewport* functions public. + */ void cogl_get_viewport (float v[4]) { + CoglHandle draw_buffer; + int viewport[4]; + int i; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - v[0] = 0; - v[1] = 0; - v[2] = ctx->viewport_width; - v[3] = ctx->viewport_height; + draw_buffer = _cogl_get_draw_buffer (); + _cogl_draw_buffer_get_viewport4fv (draw_buffer, viewport); + + for (i = 0; i < 4; i++) + v[i] = viewport[i]; } void @@ -733,7 +811,7 @@ cogl_disable_fog (void) void cogl_flush_gl_state (int flags) { - _cogl_flush_matrix_stacks (); + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); } #endif @@ -752,37 +830,52 @@ cogl_read_pixels (int x, CoglPixelFormat format, guint8 *pixels) { - GLint viewport[4]; - GLint viewport_height; - int rowstride = width * 4; - guint8 *temprow; + CoglHandle draw_buffer; + int draw_buffer_height; + int rowstride = width * 4; + guint8 *temprow; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (format == COGL_PIXEL_FORMAT_RGBA_8888); g_return_if_fail (source == COGL_READ_PIXELS_COLOR_BUFFER); temprow = g_alloca (rowstride * sizeof (guint8)); - glGetIntegerv (GL_VIEWPORT, viewport); - viewport_height = viewport[3]; + draw_buffer = _cogl_get_draw_buffer (); + + _cogl_draw_buffer_flush_state (draw_buffer, 0); + + draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); /* The y co-ordinate should be given in OpenGL's coordinate system - so 0 is the bottom row */ - y = viewport_height - y - height; - - /* Setup the pixel store parameters that may have been changed by - Cogl */ - glPixelStorei (GL_PACK_ALIGNMENT, 4); -#ifdef HAVE_COGL_GL - glPixelStorei (GL_PACK_ROW_LENGTH, 0); - glPixelStorei (GL_PACK_SKIP_PIXELS, 0); - glPixelStorei (GL_PACK_SKIP_ROWS, 0); -#endif /* HAVE_COGL_GL */ + * so 0 is the bottom row + * + * NB: all offscreen rendering is done upside down so no conversion + * is necissary in this case. + */ + if (!cogl_is_offscreen (draw_buffer)) + y = draw_buffer_height - y - height; /* make sure any batched primitives get emitted to the GL driver before * issuing our read pixels... */ cogl_flush (); - glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + /* Setup the pixel store parameters that may have been changed by + Cogl */ + GE (glPixelStorei (GL_PACK_ALIGNMENT, 1)); +#ifdef HAVE_COGL_GL + GE (glPixelStorei (GL_PACK_ROW_LENGTH, 0)); + GE (glPixelStorei (GL_PACK_SKIP_PIXELS, 0)); + GE (glPixelStorei (GL_PACK_SKIP_ROWS, 0)); +#endif /* HAVE_COGL_GL */ + + GE (glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + + /* NB: All offscreen rendering is done upside down so there is no need + * to flip in this case... */ + if (cogl_is_offscreen (draw_buffer)) + return; /* TODO: consider using the GL_MESA_pack_invert extension in the future * to avoid this flip... */ @@ -825,12 +918,13 @@ cogl_begin_gl (void) /* Flush all batched primitives */ cogl_flush (); - /* Flush our clipping state to GL */ - cogl_clip_ensure (); - - /* Flush any client side matrix state */ - _cogl_flush_matrix_stacks (); - + /* Flush framebuffer state, including clip state, modelview and + * projection matrix state + * + * NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); /* Setup the state for the current material */ @@ -852,6 +946,7 @@ cogl_begin_gl (void) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; cogl_enable (enable_flags); + _cogl_flush_face_winding (); /* Disable all client texture coordinate arrays */ for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) @@ -940,36 +1035,41 @@ _cogl_destroy_texture_units (void) void cogl_push_matrix (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_push (ctx->modelview_stack); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_push (modelview_stack); } void cogl_pop_matrix (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_pop (ctx->modelview_stack); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_pop (modelview_stack); } void cogl_scale (float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_scale (ctx->modelview_stack, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_scale (modelview_stack, x, y, z); } void cogl_translate (float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_translate (ctx->modelview_stack, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_translate (modelview_stack, x, y, z); } void cogl_rotate (float angle, float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_rotate (ctx->modelview_stack, angle, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z); } void @@ -996,35 +1096,20 @@ cogl_frustum (float left, float z_near, float z_far) { - float c, d; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); - _cogl_matrix_stack_frustum (ctx->projection_stack, + _cogl_matrix_stack_frustum (projection_stack, left, right, bottom, top, z_near, z_far); - - /* Calculate and store the inverse of the matrix */ - memset (ctx->inverse_projection, 0, sizeof (float) * 16); - - c = - (z_far + z_near) / (z_far - z_near); - d = - (2 * (z_far * z_near)) / (z_far - z_near); - -#define M(row,col) ctx->inverse_projection[col*4+row] - M(0,0) = (right - left) / (2 * z_near); - M(0,3) = (right + left) / (2 * z_near); - M(1,1) = (top - bottom) / (2 * z_near); - M(1,3) = (top + bottom) / (2 * z_near); - M(2,3) = -1.0; - M(3,2) = 1.0 / d; - M(3,3) = c / d; -#undef M } void @@ -1036,65 +1121,61 @@ cogl_ortho (float left, float z_far) { CoglMatrix ortho; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); _COGL_GET_CONTEXT (ctx, NO_RETVAL); cogl_matrix_init_identity (&ortho); cogl_matrix_ortho (&ortho, left, right, bottom, top, z_near, z_far); - _cogl_matrix_stack_set (ctx->projection_stack, &ortho); - - /* Calculate and store the inverse of the matrix */ - memset (ctx->inverse_projection, 0, sizeof (float) * 16); - -#define M(row,col) ctx->inverse_projection[col*4+row] - M(0,0) = 1.0 / ortho.xx; - M(0,3) = -ortho.xw; - M(1,1) = 1.0 / ortho.yy; - M(1,3) = -ortho.yw; - M(2,2) = 1.0 / ortho.zz; - M(2,3) = -ortho.zw; - M(3,3) = 1.0; -#undef M + _cogl_matrix_stack_set (projection_stack, &ortho); } void cogl_get_modelview_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_get (ctx->modelview_stack, matrix); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_get (modelview_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_set_modelview_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_set (ctx->modelview_stack, matrix); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_set (modelview_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_get_projection_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_get (ctx->projection_stack, matrix); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_get (projection_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_set_projection_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_set (ctx->projection_stack, matrix); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_set (projection_stack, matrix); /* FIXME: Update the inverse projection matrix!! Presumably use * of clip planes must currently be broken if this API is used. */ + _COGL_MATRIX_DEBUG_PRINT (matrix); } -void -_cogl_flush_matrix_stacks (void) +CoglClipStackState * +_cogl_get_clip_state (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_flush_to_gl (ctx->projection_stack, - COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, - COGL_MATRIX_MODELVIEW); + CoglHandle draw_buffer; + + draw_buffer = _cogl_get_draw_buffer (); + return _cogl_draw_buffer_get_clip_state (draw_buffer); } diff --git a/clutter/cogl/cogl/cogl.h.in b/clutter/cogl/cogl/cogl.h.in index 524aec71d..03ab052d9 100644 --- a/clutter/cogl/cogl/cogl.h.in +++ b/clutter/cogl/cogl/cogl.h.in @@ -219,6 +219,8 @@ void _cogl_setup_viewport (guint width, float z_near, float z_far); +#ifndef COGL_DISABLE_DEPRECATED + /** * cogl_viewport: * @width: Width of the viewport @@ -229,7 +231,25 @@ void _cogl_setup_viewport (guint width, * Since: 0.8.2 */ void cogl_viewport (guint width, - guint height); + guint height) G_GNUC_DEPRECATED; + +#endif + +/** + * cogl_set_viewport: + * @x: viewport X offset + * @x: viewport Y offset + * @width: Width of the viewport + * @height: Height of the viewport + * + * Replace the current viewport with the given values. + * + * Since: 1.2 + */ +void cogl_set_viewport (int x, + int y, + int width, + int height); /** * cogl_push_matrix: @@ -867,6 +887,10 @@ void cogl_flush_gl_state (int flags); /* private */ void _cogl_set_indirect_context (gboolean indirect); +void _cogl_set_viewport (int x, int y, int width, int height); + +void +_cogl_onscreen_clutter_backend_set_size (int width, int height); G_END_DECLS diff --git a/clutter/cogl/cogl/driver/gl/Makefile.am b/clutter/cogl/cogl/driver/gl/Makefile.am index 81a21c90e..afe961780 100644 --- a/clutter/cogl/cogl/driver/gl/Makefile.am +++ b/clutter/cogl/cogl/driver/gl/Makefile.am @@ -25,8 +25,6 @@ libclutter_cogl_driver_la_SOURCES = \ cogl.c \ cogl-primitives.c \ cogl-texture-driver.c \ - cogl-fbo.h \ - cogl-fbo.c \ cogl-shader-private.h \ cogl-shader.c \ cogl-program.h \ diff --git a/clutter/cogl/cogl/driver/gl/cogl-context-driver.c b/clutter/cogl/cogl/driver/gl/cogl-context-driver.c index dcf65ef26..c27721fd0 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-context-driver.c +++ b/clutter/cogl/cogl/driver/gl/cogl-context-driver.c @@ -30,15 +30,16 @@ void _cogl_create_context_driver (CoglContext *_context) { - _context->drv.pf_glGenRenderbuffersEXT = NULL; - _context->drv.pf_glBindRenderbufferEXT = NULL; - _context->drv.pf_glRenderbufferStorageEXT = NULL; - _context->drv.pf_glGenFramebuffersEXT = NULL; - _context->drv.pf_glBindFramebufferEXT = NULL; - _context->drv.pf_glFramebufferTexture2DEXT = NULL; - _context->drv.pf_glFramebufferRenderbufferEXT = NULL; - _context->drv.pf_glCheckFramebufferStatusEXT = NULL; - _context->drv.pf_glDeleteFramebuffersEXT = NULL; + _context->drv.pf_glGenRenderbuffers = NULL; + _context->drv.pf_glBindRenderbuffer = NULL; + _context->drv.pf_glRenderbufferStorage = NULL; + _context->drv.pf_glGenFramebuffers = NULL; + _context->drv.pf_glBindFramebuffer = NULL; + _context->drv.pf_glFramebufferTexture2D = NULL; + _context->drv.pf_glFramebufferRenderbuffer = NULL; + _context->drv.pf_glCheckFramebufferStatus = NULL; + _context->drv.pf_glDeleteFramebuffers = NULL; + _context->drv.pf_glBlitFramebufferEXT = NULL; _context->drv.pf_glRenderbufferStorageMultisampleEXT = NULL; diff --git a/clutter/cogl/cogl/driver/gl/cogl-context-driver.h b/clutter/cogl/cogl/driver/gl/cogl-context-driver.h index ac5e61621..ff266d663 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-context-driver.h +++ b/clutter/cogl/cogl/driver/gl/cogl-context-driver.h @@ -29,19 +29,20 @@ typedef struct _CoglContextDriver { /* Relying on glext.h to define these */ - COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; - COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT; - COGL_PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT; - COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT; - COGL_PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT; - COGL_PFNGLBINDFRAMEBUFFEREXTPROC pf_glBindFramebufferEXT; - COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pf_glFramebufferTexture2DEXT; - COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC pf_glFramebufferRenderbufferEXT; - COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pf_glCheckFramebufferStatusEXT; - COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT; + COGL_PFNGLGENRENDERBUFFERSPROC pf_glGenRenderbuffers; + COGL_PFNGLDELETERENDERBUFFERSPROC pf_glDeleteRenderbuffers; + COGL_PFNGLBINDRENDERBUFFERPROC pf_glBindRenderbuffer; + COGL_PFNGLRENDERBUFFERSTORAGEPROC pf_glRenderbufferStorage; + COGL_PFNGLGENFRAMEBUFFERSPROC pf_glGenFramebuffers; + COGL_PFNGLBINDFRAMEBUFFERPROC pf_glBindFramebuffer; + COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC pf_glFramebufferTexture2D; + COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC pf_glFramebufferRenderbuffer; + COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC pf_glCheckFramebufferStatus; + COGL_PFNGLDELETEFRAMEBUFFERSPROC pf_glDeleteFramebuffers; + COGL_PFNGLGENERATEMIPMAPPROC pf_glGenerateMipmap; + COGL_PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT; COGL_PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT; - COGL_PFNGLGENERATEMIPMAPEXTPROC pf_glGenerateMipmapEXT; COGL_PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB; COGL_PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB; diff --git a/clutter/cogl/cogl/driver/gl/cogl-defines.h.in b/clutter/cogl/cogl/driver/gl/cogl-defines.h.in index 1c79bedfa..9d04be9af 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-defines.h.in +++ b/clutter/cogl/cogl/driver/gl/cogl-defines.h.in @@ -698,34 +698,34 @@ G_BEGIN_DECLS #endif typedef void - (APIENTRYP COGL_PFNGLGENRENDERBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); typedef void - (APIENTRYP COGL_PFNGLBINDRENDERBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); typedef void - (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC) + (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); typedef void - (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); typedef void - (APIENTRYP COGL_PFNGLBINDFRAMEBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); typedef void - (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) + (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, @@ -733,26 +733,30 @@ typedef void GLint level); typedef void - (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef GLenum - (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) + (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); typedef void - (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); typedef void - (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void + (APIENTRYP COGL_PFNGLGENERATEMIPMAPPROC) + (GLenum target); + typedef void (APIENTRYP COGL_PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, @@ -774,10 +778,6 @@ typedef void GLsizei width, GLsizei height); -typedef void - (APIENTRYP COGL_PFNGLGENERATEMIPMAPEXTPROC) - (GLenum target); - typedef GLhandleARB (APIENTRYP COGL_PFNGLCREATEPROGRAMOBJECTARBPROC) (void); diff --git a/clutter/cogl/cogl/driver/gl/cogl-fbo.c b/clutter/cogl/cogl/driver/gl/cogl-fbo.c deleted file mode 100644 index 1bd88aeea..000000000 --- a/clutter/cogl/cogl/driver/gl/cogl-fbo.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Cogl - * - * An object oriented GL/GLES Abstraction/Utility Layer - * - * Copyright (C) 2007,2008,2009 Intel Corporation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "cogl.h" -#include "cogl-internal.h" -#include "cogl-util.h" -#include "cogl-texture-private.h" -#include "cogl-fbo.h" -#include "cogl-context.h" -#include "cogl-handle.h" - -/* Expecting EXT functions not to be defined - redirect to pointers in context */ -#define glGenRenderbuffersEXT ctx->drv.pf_glGenRenderbuffersEXT -#define glDeleteRenderbuffersEXT ctx->drv.pf_glDeleteRenderbuffersEXT -#define glBindRenderbufferEXT ctx->drv.pf_glBindRenderbufferEXT -#define glRenderbufferStorageEXT ctx->drv.pf_glRenderbufferStorageEXT -#define glGenFramebuffersEXT ctx->drv.pf_glGenFramebuffersEXT -#define glBindFramebufferEXT ctx->drv.pf_glBindFramebufferEXT -#define glFramebufferTexture2DEXT ctx->drv.pf_glFramebufferTexture2DEXT -#define glFramebufferRenderbufferEXT ctx->drv.pf_glFramebufferRenderbufferEXT -#define glCheckFramebufferStatusEXT ctx->drv.pf_glCheckFramebufferStatusEXT -#define glDeleteFramebuffersEXT ctx->drv.pf_glDeleteFramebuffersEXT -#define glBlitFramebufferEXT ctx->drv.pf_glBlitFramebufferEXT -#define glRenderbufferStorageMultisampleEXT ctx->drv.pf_glRenderbufferStorageMultisampleEXT - -#ifndef GL_READ_FRAMEBUFFER_EXT -#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 -#endif -#ifndef GL_DRAW_FRAMEBUFFER_EXT -#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 -#endif - -static void _cogl_offscreen_free (CoglFbo *fbo); - -COGL_HANDLE_DEFINE (Fbo, offscreen); - -CoglHandle -cogl_offscreen_new_to_texture (CoglHandle texhandle) -{ - CoglFbo *fbo; - int width; - int height; - GLuint tex_gl_handle; - GLenum tex_gl_target; - GLuint fbo_gl_handle; - GLuint gl_stencil_handle; - GLenum status; - - _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); - - if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) - return COGL_INVALID_HANDLE; - - /* Make texhandle is a valid texture object */ - if (!cogl_is_texture (texhandle)) - return COGL_INVALID_HANDLE; - - /* The texture must not be sliced */ - if (cogl_texture_is_sliced (texhandle)) - return COGL_INVALID_HANDLE; - - /* Pick the single texture slice width, height and GL id */ - - width = cogl_texture_get_width (texhandle); - height = cogl_texture_get_height (texhandle); - - if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target)) - return COGL_INVALID_HANDLE; - - if (tex_gl_target != GL_TEXTURE_2D) - return COGL_INVALID_HANDLE; - - /* Create a renderbuffer for stenciling */ - GE( glGenRenderbuffersEXT (1, &gl_stencil_handle) ); - GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, gl_stencil_handle) ); - GE( glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, - cogl_texture_get_width (texhandle), - cogl_texture_get_height (texhandle)) ); - GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, 0) ); - - /* Generate framebuffer */ - glGenFramebuffersEXT (1, &fbo_gl_handle); - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) ); - GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - tex_gl_target, tex_gl_handle, 0) ); - GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, gl_stencil_handle) ); - - /* XXX: The framebuffer_object spec isn't clear in defining whether attaching - * a texture as a renderbuffer with mipmap filtering enabled while the - * mipmaps have not been uploaded should result in an incomplete framebuffer - * object. (different drivers make different decisions) - * - * To avoid an error with drivers that do consider this a problem we - * explicitly set non mipmapped filters here. These will later be reset when - * the texture is actually used for rendering according to the filters set on - * the corresponding CoglMaterial. - */ - _cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST); - - /* Make sure it's complete */ - status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - /* Stencil renderbuffers aren't always supported. Try again - without the stencil buffer */ - GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, - 0) ); - GE( glDeleteRenderbuffersEXT (1, &gl_stencil_handle) ); - gl_stencil_handle = 0; - - status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - /* Still failing, so give up */ - GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) ); - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); - return COGL_INVALID_HANDLE; - } - } - - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); - - /* Allocate and init a CoglFbo object (store non-wasted size - for subsequent blits and viewport setup) */ - fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo)); - fbo->width = width; - fbo->height = height; - fbo->gl_handle = fbo_gl_handle; - fbo->gl_stencil_handle = gl_stencil_handle; - - return _cogl_offscreen_handle_new (fbo); -} - -static void -_cogl_offscreen_free (CoglFbo *fbo) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Frees FBO resources but its handle is not - released! Do that separately before this! */ - if (fbo->gl_stencil_handle) - GE( glDeleteRenderbuffersEXT (1, &fbo->gl_stencil_handle) ); - GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) ); - g_free (fbo); -} - -void -cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ - CoglFbo *fbo = NULL; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_journal_flush (); - - g_assert (ctx->draw_buffer_stack != NULL); - draw_buffer = ctx->draw_buffer_stack->data; - - if (target == COGL_OFFSCREEN_BUFFER) - { - /* Make sure it is a valid fbo handle */ - if (!cogl_is_offscreen (offscreen)) - return; - - fbo = _cogl_offscreen_pointer_from_handle (offscreen); - - /* Check current draw buffer target */ - if (draw_buffer->target != COGL_OFFSCREEN_BUFFER) - { - /* Push the viewport and matrix setup if redirecting - from a non-screen buffer */ - GE( glPushAttrib (GL_VIEWPORT_BIT) ); - - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - else - { - /* Override viewport and matrix setup if redirecting - from another offscreen buffer */ - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - - /* Setup new viewport and matrices */ - cogl_viewport (fbo->width, fbo->height); - _cogl_matrix_stack_translate (ctx->modelview_stack, -1.0f, -1.0f, 0.0f); - _cogl_matrix_stack_scale (ctx->modelview_stack, 2.0f / fbo->width, 2.0f / fbo->height, 1.0f); - - /* Bind offscreen framebuffer object */ - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo->gl_handle) ); - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); - - /* Some implementation require a clear before drawing - to an fbo. Luckily it is affected by scissor test. */ - /* FIXME: test where exactly this is needed end whether - a glClear with 0 argument is enough */ - GE( glPushAttrib (GL_SCISSOR_BIT) ); - GE( glScissor (0,0,0,0) ); - GE( glEnable (GL_SCISSOR_TEST) ); - GE( glClear (GL_COLOR_BUFFER_BIT) ); - GE( glPopAttrib () ); - } - else if (target & COGL_WINDOW_BUFFER) - { - /* Check current draw buffer target */ - if (draw_buffer->target == COGL_OFFSCREEN_BUFFER) - { - /* Pop viewport and matrices if redirecting back - from an offscreen buffer */ - GE( glPopAttrib () ); - - _cogl_matrix_stack_pop (ctx->projection_stack); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - } - - /* Bind window framebuffer object */ - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); - } - - /* Store new target */ - draw_buffer->target = target; - if (draw_buffer->offscreen != offscreen) - { - if (draw_buffer->offscreen != COGL_INVALID_HANDLE) - cogl_handle_unref (draw_buffer->offscreen); - if (offscreen != COGL_INVALID_HANDLE) - cogl_handle_ref (offscreen); - draw_buffer->offscreen = offscreen; - } -} - -void -cogl_push_draw_buffer(void) -{ - CoglDrawBufferState *old; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - g_assert (ctx->draw_buffer_stack != NULL); - old = ctx->draw_buffer_stack->data; - - draw_buffer = g_slice_new0 (CoglDrawBufferState); - *draw_buffer = *old; - - ctx->draw_buffer_stack = - g_slist_prepend (ctx->draw_buffer_stack, draw_buffer); -} - -void -cogl_pop_draw_buffer(void) -{ - CoglDrawBufferState *to_pop; - CoglDrawBufferState *to_restore; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - g_assert (ctx->draw_buffer_stack != NULL); - if (ctx->draw_buffer_stack->next == NULL) - { - g_warning ("1 more cogl_pop_draw_buffer() than cogl_push_draw_buffer()"); - return; - } - - to_pop = ctx->draw_buffer_stack->data; - to_restore = ctx->draw_buffer_stack->next->data; - - /* the logic in cogl_set_draw_buffer() only works if - * to_pop is still on top of the stack, because - * cogl_set_draw_buffer() needs to know the previous - * state. - */ - cogl_set_draw_buffer (to_restore->target, to_restore->offscreen); - - /* cogl_set_draw_buffer() should have set top of stack - * to to_restore - */ - g_assert (to_restore->target == to_pop->target); - g_assert (to_restore->offscreen == to_pop->offscreen); - - g_assert (ctx->draw_buffer_stack->data == to_pop); - ctx->draw_buffer_stack = - g_slist_remove_link (ctx->draw_buffer_stack, - ctx->draw_buffer_stack); - - g_slice_free (CoglDrawBufferState, to_pop); -} - diff --git a/clutter/cogl/cogl/driver/gl/cogl-primitives.c b/clutter/cogl/cogl/driver/gl/cogl-primitives.c index 3c1c9e656..d2bc19335 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-primitives.c +++ b/clutter/cogl/cogl/driver/gl/cogl-primitives.c @@ -30,6 +30,8 @@ #include "cogl-context.h" #include "cogl-clip-stack.h" #include "cogl-material-private.h" +#include "cogl-clip-stack.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -74,7 +76,7 @@ _cogl_path_add_node (gboolean new_sub_path, } void -_cogl_path_stroke_nodes () +_cogl_path_stroke_nodes (void) { guint path_start = 0; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; @@ -82,6 +84,13 @@ _cogl_path_stroke_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); cogl_enable (enable_flags); @@ -90,7 +99,6 @@ _cogl_path_stroke_nodes () options.disable_layers = (guint32)~0; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); while (path_start < ctx->path_nodes->len) { @@ -127,20 +135,33 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, CoglPathNode *path, gboolean merge) { - guint path_start = 0; - guint sub_path_num = 0; - float bounds_x; - float bounds_y; - float bounds_w; - float bounds_h; - gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; - CoglHandle prev_source; - int i; + guint path_start = 0; + guint sub_path_num = 0; + float bounds_x; + float bounds_y; + float bounds_w; + float bounds_h; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglHandle prev_source; + int i; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't track changes to the stencil buffer in the journal + * so we need to flush any batched geometry first */ _cogl_journal_flush (); + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* Just setup a simple material that doesn't use texturing... */ prev_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->stencil_material); @@ -161,7 +182,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } else { - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + cogl_clear (NULL, COGL_BUFFER_BIT_STENCIL); GE( glStencilMask (1) ); GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); } @@ -179,8 +200,6 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } ctx->n_texcoord_arrays_enabled = 0; - _cogl_flush_matrix_stacks (); - while (path_start < path_size) { GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), @@ -194,8 +213,17 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, significant bit */ GE( glStencilMask (merge ? 6 : 3) ); GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); - glRectf (bounds_x, bounds_y, - bounds_x + bounds_w, bounds_y + bounds_h); + cogl_rectangle (bounds_x, bounds_y, + bounds_x + bounds_w, bounds_y + bounds_h); + /* Make sure the rectangle hits the stencil buffer before + * directly changing other GL state. */ + _cogl_journal_flush (); + /* NB: The journal flushing may trash the modelview state and + * enable flags */ + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); + cogl_enable (enable_flags); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -216,19 +244,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_flush_to_gl (projection_stack, + COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); - _cogl_flush_matrix_stacks (); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + /* Make sure these rectangles hit the stencil buffer before we + * restore the stencil op/func. */ + _cogl_journal_flush (); - glRectf (-1.0, -1.0, 1.0, 1.0); - glRectf (-1.0, -1.0, 1.0, 1.0); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } GE( glStencilMask (~(GLuint) 0) ); @@ -244,8 +277,10 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } void -_cogl_path_fill_nodes () +_cogl_path_fill_nodes (void) { + CoglHandle draw_buffer; + CoglClipStackState *clip_state; float bounds_x; float bounds_y; float bounds_w; @@ -253,6 +288,11 @@ _cogl_path_fill_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max, &bounds_x, &bounds_y, &bounds_w, &bounds_h); @@ -261,12 +301,13 @@ _cogl_path_fill_nodes () ctx->path_nodes->len, &g_array_index (ctx->path_nodes, CoglPathNode, 0), - ctx->clip.stencil_used); + clip_state->stencil_used); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); /* The stencil buffer now contains garbage so the clip area needs to be rebuilt */ - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_state_dirty (clip_state); } + diff --git a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c index e4c868dbd..742fab807 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c +++ b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c @@ -45,7 +45,7 @@ #include #include -#define glGenerateMipmap ctx->drv.pf_glGenerateMipmapEXT +#define glGenerateMipmap ctx->drv.pf_glGenerateMipmap void _cogl_texture_driver_bind (GLenum gl_target, diff --git a/clutter/cogl/cogl/driver/gl/cogl.c b/clutter/cogl/cogl/driver/gl/cogl.c index 04d3b750a..e81ec377c 100644 --- a/clutter/cogl/cogl/driver/gl/cogl.c +++ b/clutter/cogl/cogl/driver/gl/cogl.c @@ -32,6 +32,12 @@ #include "cogl-internal.h" #include "cogl-context.h" +typedef struct _CoglGLSymbolTableEntry +{ + const char *name; + void *ptr; +} CoglGLSymbolTableEntry; + gboolean cogl_check_extension (const gchar *name, const gchar *ext) { @@ -57,6 +63,26 @@ cogl_check_extension (const gchar *name, const gchar *ext) return FALSE; } +gboolean +_cogl_resolve_gl_symbols (CoglGLSymbolTableEntry *symbol_table, + const char *suffix) +{ + int i; + gboolean status = TRUE; + for (i = 0; symbol_table[i].name; i++) + { + char *full_name = g_strdup_printf ("%s%s", symbol_table[i].name, suffix); + *((CoglFuncPtr *)symbol_table[i].ptr) = cogl_get_proc_address (full_name); + g_free (full_name); + if (!*((CoglFuncPtr *)symbol_table[i].ptr)) + { + status = FALSE; + break; + } + } + return status; +} + #ifdef HAVE_CLUTTER_OSX static gboolean really_enable_npot (void) @@ -90,6 +116,9 @@ _cogl_features_init (void) const gchar *gl_extensions; GLint max_clip_planes = 0; GLint num_stencil_bits = 0; + gboolean fbo_ARB = FALSE; + gboolean fbo_EXT = FALSE; + const char *suffix; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -284,64 +313,35 @@ _cogl_features_init (void) flags |= COGL_FEATURE_SHADERS_GLSL; } - if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) || - cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions)) + fbo_ARB = cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions); + if (fbo_ARB) + suffix = ""; + else { - ctx->drv.pf_glGenRenderbuffersEXT = - (COGL_PFNGLGENRENDERBUFFERSEXTPROC) - cogl_get_proc_address ("glGenRenderbuffersEXT"); + fbo_EXT = cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions); + if (fbo_EXT) + suffix = "EXT"; + } - ctx->drv.pf_glDeleteRenderbuffersEXT = - (COGL_PFNGLDELETERENDERBUFFERSEXTPROC) - cogl_get_proc_address ("glDeleteRenderbuffersEXT"); + if (fbo_ARB || fbo_EXT) + { + CoglGLSymbolTableEntry symbol_table[] = { + {"glGenRenderbuffers", &ctx->drv.pf_glGenRenderbuffers}, + {"glDeleteRenderbuffers", &ctx->drv.pf_glDeleteRenderbuffers}, + {"glBindRenderbuffer", &ctx->drv.pf_glBindRenderbuffer}, + {"glRenderbufferStorage", &ctx->drv.pf_glRenderbufferStorage}, + {"glGenFramebuffers", &ctx->drv.pf_glGenFramebuffers}, + {"glBindFramebuffer", &ctx->drv.pf_glBindFramebuffer}, + {"glFramebufferTexture2D", &ctx->drv.pf_glFramebufferTexture2D}, + {"glFramebufferRenderbuffer", &ctx->drv.pf_glFramebufferRenderbuffer}, + {"glCheckFramebufferStatus", &ctx->drv.pf_glCheckFramebufferStatus}, + {"glDeleteFramebuffers", &ctx->drv.pf_glDeleteFramebuffers}, + {"glGenerateMipmap", &ctx->drv.pf_glGenerateMipmap}, + {NULL, NULL} + }; - ctx->drv.pf_glBindRenderbufferEXT = - (COGL_PFNGLBINDRENDERBUFFEREXTPROC) - cogl_get_proc_address ("glBindRenderbufferEXT"); - - ctx->drv.pf_glRenderbufferStorageEXT = - (COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC) - cogl_get_proc_address ("glRenderbufferStorageEXT"); - - ctx->drv.pf_glGenFramebuffersEXT = - (COGL_PFNGLGENFRAMEBUFFERSEXTPROC) - cogl_get_proc_address ("glGenFramebuffersEXT"); - - ctx->drv.pf_glBindFramebufferEXT = - (COGL_PFNGLBINDFRAMEBUFFEREXTPROC) - cogl_get_proc_address ("glBindFramebufferEXT"); - - ctx->drv.pf_glFramebufferTexture2DEXT = - (COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) - cogl_get_proc_address ("glFramebufferTexture2DEXT"); - - ctx->drv.pf_glFramebufferRenderbufferEXT = - (COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) - cogl_get_proc_address ("glFramebufferRenderbufferEXT"); - - ctx->drv.pf_glCheckFramebufferStatusEXT = - (COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) - cogl_get_proc_address ("glCheckFramebufferStatusEXT"); - - ctx->drv.pf_glDeleteFramebuffersEXT = - (COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC) - cogl_get_proc_address ("glDeleteFramebuffersEXT"); - - ctx->drv.pf_glGenerateMipmapEXT = - (COGL_PFNGLGENERATEMIPMAPEXTPROC) - cogl_get_proc_address ("glGenerateMipmapEXT"); - - if (ctx->drv.pf_glGenRenderbuffersEXT && - ctx->drv.pf_glBindRenderbufferEXT && - ctx->drv.pf_glRenderbufferStorageEXT && - ctx->drv.pf_glGenFramebuffersEXT && - ctx->drv.pf_glBindFramebufferEXT && - ctx->drv.pf_glFramebufferTexture2DEXT && - ctx->drv.pf_glFramebufferRenderbufferEXT && - ctx->drv.pf_glCheckFramebufferStatusEXT && - ctx->drv.pf_glDeleteFramebuffersEXT && - ctx->drv.pf_glGenerateMipmapEXT) - flags |= COGL_FEATURE_OFFSCREEN; + if (_cogl_resolve_gl_symbols (symbol_table, suffix)) + flags |= COGL_FEATURE_OFFSCREEN; } if (cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions)) diff --git a/clutter/cogl/cogl/driver/gles/Makefile.am b/clutter/cogl/cogl/driver/gles/Makefile.am index 0a44fb465..182b29b84 100644 --- a/clutter/cogl/cogl/driver/gles/Makefile.am +++ b/clutter/cogl/cogl/driver/gles/Makefile.am @@ -22,11 +22,9 @@ libclutter_cogl_driver_la_CPPFLAGS = \ $(CLUTTER_DEBUG_CFLAGS) \ $(MAINTAINER_CFLAGS) libclutter_cogl_driver_la_SOURCES = \ - cogl-fbo.h \ cogl.c \ cogl-primitives.c \ cogl-texture-driver.c \ - cogl-fbo.c \ cogl-context-driver.c \ cogl-context-driver.h \ cogl-gles2-wrapper.h \ diff --git a/clutter/cogl/cogl/driver/gles/cogl-context-driver.c b/clutter/cogl/cogl/driver/gles/cogl-context-driver.c index 6660db1f3..ae2a06477 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.c +++ b/clutter/cogl/cogl/driver/gles/cogl-context-driver.c @@ -31,6 +31,16 @@ void _cogl_create_context_driver (CoglContext *context) { + context->drv.pf_glGenRenderbuffers = NULL; + context->drv.pf_glBindRenderbuffer = NULL; + context->drv.pf_glRenderbufferStorage = NULL; + context->drv.pf_glGenFramebuffers = NULL; + context->drv.pf_glBindFramebuffer = NULL; + context->drv.pf_glFramebufferTexture2D = NULL; + context->drv.pf_glFramebufferRenderbuffer = NULL; + context->drv.pf_glCheckFramebufferStatus = NULL; + context->drv.pf_glDeleteFramebuffers = NULL; + /* Init the GLES2 wrapper */ #ifdef HAVE_COGL_GLES2 cogl_gles2_wrapper_init (&context->drv.gles2); diff --git a/clutter/cogl/cogl/driver/gles/cogl-context-driver.h b/clutter/cogl/cogl/driver/gles/cogl-context-driver.h index 9b5996e74..de24eb775 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.h +++ b/clutter/cogl/cogl/driver/gles/cogl-context-driver.h @@ -24,17 +24,26 @@ #ifndef __COGL_CONTEXT_DRIVER_H #define __COGL_CONTEXT_DRIVER_H +#include "cogl.h" #include "cogl-gles2-wrapper.h" typedef struct _CoglContextDriver { + COGL_PFNGLGENRENDERBUFFERSPROC pf_glGenRenderbuffers; + COGL_PFNGLDELETERENDERBUFFERSPROC pf_glDeleteRenderbuffers; + COGL_PFNGLBINDRENDERBUFFERPROC pf_glBindRenderbuffer; + COGL_PFNGLRENDERBUFFERSTORAGEPROC pf_glRenderbufferStorage; + COGL_PFNGLGENFRAMEBUFFERSPROC pf_glGenFramebuffers; + COGL_PFNGLBINDFRAMEBUFFERPROC pf_glBindFramebuffer; + COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC pf_glFramebufferTexture2D; + COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC pf_glFramebufferRenderbuffer; + COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC pf_glCheckFramebufferStatus; + COGL_PFNGLDELETEFRAMEBUFFERSPROC pf_glDeleteFramebuffers; + COGL_PFNGLGENERATEMIPMAPPROC pf_glGenerateMipmap; + #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; - - /* Viewport store for FBOs. Needed because glPushAttrib() isn't - supported */ - GLint viewport_store[4]; #endif } CoglContextDriver; diff --git a/clutter/cogl/cogl/driver/gles/cogl-defines.h.in b/clutter/cogl/cogl/driver/gles/cogl-defines.h.in index 97045df94..8f0cff1da 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-defines.h.in +++ b/clutter/cogl/cogl/driver/gles/cogl-defines.h.in @@ -633,6 +633,76 @@ G_BEGIN_DECLS #define CGL_SHININESS 0x1601 #endif +/* Extension function prototypes */ + +#ifndef APIENTRY +#define APIENTRY +#endif + +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +typedef void + (APIENTRYP COGL_PFNGLGENRENDERBUFFERSPROC) + (GLsizei n, + GLuint *renderbuffers); + +typedef void + (APIENTRYP COGL_PFNGLBINDRENDERBUFFERPROC) + (GLenum target, + GLuint renderbuffer); + +typedef void + (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEPROC) + (GLenum target, + GLenum internalformat, + GLsizei width, + GLsizei height); + +typedef void + (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSPROC) + (GLsizei n, + GLuint *framebuffers); + +typedef void + (APIENTRYP COGL_PFNGLBINDFRAMEBUFFERPROC) + (GLenum target, + GLuint framebuffer); + +typedef void + (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC) + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level); + +typedef void + (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC) + (GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer); + +typedef GLenum + (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC) + (GLenum target); + +typedef void + (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSPROC) + (GLsizei n, + const GLuint *framebuffers); + +typedef void + (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSPROC) + (GLsizei n, + const GLuint *renderbuffers); + +typedef void + (APIENTRYP COGL_PFNGLGENERATEMIPMAPPROC) + (GLenum target); + G_END_DECLS #endif diff --git a/clutter/cogl/cogl/driver/gles/cogl-fbo.c b/clutter/cogl/cogl/driver/gles/cogl-fbo.c deleted file mode 100644 index 0b1006970..000000000 --- a/clutter/cogl/cogl/driver/gles/cogl-fbo.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Cogl - * - * An object oriented GL/GLES Abstraction/Utility Layer - * - * Copyright (C) 2008,2009 Intel Corporation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "cogl.h" -#include "cogl-internal.h" -#include "cogl-util.h" -#include "cogl-texture-private.h" -#include "cogl-fbo.h" -#include "cogl-context.h" -#include "cogl-handle.h" -#include "cogl-gles2-wrapper.h" - -#ifdef HAVE_COGL_GLES2 - -static void _cogl_offscreen_free (CoglFbo *fbo); - -COGL_HANDLE_DEFINE (Fbo, offscreen); - -CoglHandle -cogl_offscreen_new_to_texture (CoglHandle texhandle) -{ - CoglFbo *fbo; - int width; - int height; - GLuint tex_gl_handle; - GLenum tex_gl_target; - GLuint fbo_gl_handle; - GLuint gl_stencil_handle; - GLenum status; - - _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); - - if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) - return COGL_INVALID_HANDLE; - - /* Make texhandle is a valid texture object */ - if (!cogl_is_texture (texhandle)) - return COGL_INVALID_HANDLE; - - /* The texture must not be sliced */ - if (cogl_texture_is_sliced (texhandle)) - return COGL_INVALID_HANDLE; - - /* Pick the single texture slice width, height and GL id */ - - width = cogl_texture_get_width (texhandle); - height = cogl_texture_get_height (texhandle); - - if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target)) - return COGL_INVALID_HANDLE; - - if (tex_gl_target != GL_TEXTURE_2D) - return COGL_INVALID_HANDLE; - - /* Create a renderbuffer for stenciling */ - GE( glGenRenderbuffers (1, &gl_stencil_handle) ); - GE( glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle) ); - GE( glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, - cogl_texture_get_width (texhandle), - cogl_texture_get_height (texhandle)) ); - GE( glBindRenderbuffer (GL_RENDERBUFFER, 0) ); - - /* Generate framebuffer */ - glGenFramebuffers (1, &fbo_gl_handle); - GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle) ); - GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - tex_gl_target, tex_gl_handle, 0) ); - GE( glFramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, gl_stencil_handle) ); - - /* Make sure it's complete */ - status = glCheckFramebufferStatus (GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - /* Stencil renderbuffers aren't always supported. Try again - without the stencil buffer */ - GE( glFramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, - 0) ); - GE( glDeleteRenderbuffers (1, &gl_stencil_handle) ); - gl_stencil_handle = 0; - - status = glCheckFramebufferStatus (GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - /* Still failing, so give up */ - GE( glDeleteFramebuffers (1, &fbo_gl_handle) ); - GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); - return COGL_INVALID_HANDLE; - } - } - - GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); - - /* Allocate and init a CoglFbo object (store non-wasted size - for subsequent blits and viewport setup) */ - fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo)); - fbo->width = width; - fbo->height = height; - fbo->gl_handle = fbo_gl_handle; - fbo->gl_stencil_handle = gl_stencil_handle; - - return _cogl_offscreen_handle_new (fbo); -} - -static void -_cogl_offscreen_free (CoglFbo *fbo) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Frees FBO resources but its handle is not - released! Do that separately before this! */ - if (fbo->gl_stencil_handle) - GE( glDeleteRenderbuffers (1, &fbo->gl_stencil_handle) ); - GE( glDeleteFramebuffers (1, &fbo->gl_handle) ); - g_free (fbo); -} - -void -cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ - CoglFbo *fbo = NULL; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - g_assert (ctx->draw_buffer_stack != NULL); - draw_buffer = ctx->draw_buffer_stack->data; - - if (target == COGL_OFFSCREEN_BUFFER) - { - GLboolean scissor_enabled; - GLint scissor_box[4]; - - /* Make sure it is a valid fbo handle */ - if (!cogl_is_offscreen (offscreen)) - return; - - fbo = _cogl_offscreen_pointer_from_handle (offscreen); - - /* Check current draw buffer target */ - if (draw_buffer->target != COGL_OFFSCREEN_BUFFER) - { - /* Push the viewport and matrix setup if redirecting - from a non-screen buffer */ - GE( glGetIntegerv (GL_VIEWPORT, ctx->drv.viewport_store) ); - - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - else - { - /* Override viewport and matrix setup if redirecting - from another offscreen buffer */ - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - - /* Setup new viewport and matrices */ - GE( glViewport (0, 0, fbo->width, fbo->height) ); - _cogl_matrix_stack_translate (ctx->modelview_stack, -1.0f, -1.0f, 0.0f); - _cogl_matrix_stack_scale (ctx->modelview_stack, - 2.0f / fbo->width, 2.0f / fbo->height, 1.0f); - - /* Bind offscreen framebuffer object */ - GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo->gl_handle) ); - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); - - /* Some implementation require a clear before drawing - to an fbo. Luckily it is affected by scissor test. */ - /* FIXME: test where exactly this is needed end whether - a glClear with 0 argument is enough */ - - scissor_enabled = glIsEnabled (GL_SCISSOR_TEST); - GE( glGetIntegerv (GL_SCISSOR_BOX, scissor_box) ); - GE( glScissor (0, 0, 0, 0) ); - GE( glEnable (GL_SCISSOR_TEST) ); - GE( glClear (GL_COLOR_BUFFER_BIT) ); - if (!scissor_enabled) - glDisable (GL_SCISSOR_TEST); - glScissor (scissor_box[0], scissor_box[1], - scissor_box[2], scissor_box[3]); - - } - else if (target & COGL_WINDOW_BUFFER) - { - /* Check current draw buffer target */ - if (draw_buffer->target == COGL_OFFSCREEN_BUFFER) - { - /* Pop viewport and matrices if redirecting back - from an offscreen buffer */ - GE( glViewport (ctx->drv.viewport_store[0], - ctx->drv.viewport_store[1], - ctx->drv.viewport_store[2], - ctx->drv.viewport_store[3]) ); - - _cogl_matrix_stack_pop (ctx->projection_stack); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - } - - /* Bind window framebuffer object */ - GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); - } - - /* Store new target */ - draw_buffer->target = target; - if (draw_buffer->offscreen != offscreen) - { - if (draw_buffer->offscreen != COGL_INVALID_HANDLE) - cogl_handle_unref (draw_buffer->offscreen); - if (offscreen != COGL_INVALID_HANDLE) - cogl_handle_ref (offscreen); - draw_buffer->offscreen = offscreen; - } -} - -void -cogl_push_draw_buffer(void) -{ - CoglDrawBufferState *old; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - g_assert (ctx->draw_buffer_stack != NULL); - old = ctx->draw_buffer_stack->data; - - draw_buffer = g_slice_new0 (CoglDrawBufferState); - *draw_buffer = *old; - - ctx->draw_buffer_stack = - g_slist_prepend (ctx->draw_buffer_stack, draw_buffer); -} - -void -cogl_pop_draw_buffer(void) -{ - CoglDrawBufferState *to_pop; - CoglDrawBufferState *to_restore; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - g_assert (ctx->draw_buffer_stack != NULL); - if (ctx->draw_buffer_stack->next == NULL) - { - g_warning ("1 more cogl_pop_draw_buffer() than cogl_push_draw_buffer()"); - return; - } - - to_pop = ctx->draw_buffer_stack->data; - to_restore = ctx->draw_buffer_stack->next->data; - - /* the logic in cogl_set_draw_buffer() only works if - * to_pop is still on top of the stack, because - * cogl_set_draw_buffer() needs to know the previous - * state. - */ - cogl_set_draw_buffer (to_restore->target, to_restore->offscreen); - - /* cogl_set_draw_buffer() should have set top of stack - * to to_restore - */ - g_assert (to_restore->target == to_pop->target); - g_assert (to_restore->offscreen == to_pop->offscreen); - - g_assert (ctx->draw_buffer_stack->data == to_pop); - ctx->draw_buffer_stack = - g_slist_remove_link (ctx->draw_buffer_stack, - ctx->draw_buffer_stack); - - g_slice_free (CoglDrawBufferState, to_pop); -} - -#else /* HAVE_COGL_GLES2 */ - -/* No support on regular OpenGL 1.1 */ - -gboolean -cogl_is_offscreen (CoglHandle handle) -{ - return FALSE; -} - -CoglHandle -cogl_offscreen_new_to_texture (CoglHandle texhandle) -{ - return COGL_INVALID_HANDLE; -} - -CoglHandle -cogl_offscreen_ref (CoglHandle handle) -{ - return COGL_INVALID_HANDLE; -} - -void -cogl_offscreen_unref (CoglHandle handle) -{ -} - -void -cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ -} - -#endif /* HAVE_COGL_GLES2 */ diff --git a/clutter/cogl/cogl/driver/gles/cogl-fbo.h b/clutter/cogl/cogl/driver/gles/cogl-fbo.h deleted file mode 100644 index 8499f8fb0..000000000 --- a/clutter/cogl/cogl/driver/gles/cogl-fbo.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Cogl - * - * An object oriented GL/GLES Abstraction/Utility Layer - * - * Copyright (C) 2007,2008,2009 Intel Corporation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __COGL_FBO_H -#define __COGL_FBO_H - -#include "cogl-handle.h" - -typedef struct -{ - CoglHandleObject _parent; - int width; - int height; - GLuint gl_handle; - GLuint gl_stencil_handle; - -} CoglFbo; - -#endif /* __COGL_FBO_H */ diff --git a/clutter/cogl/cogl/driver/gles/cogl-primitives.c b/clutter/cogl/cogl/driver/gles/cogl-primitives.c index f31bd2143..09d637e7d 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-primitives.c +++ b/clutter/cogl/cogl/driver/gles/cogl-primitives.c @@ -30,6 +30,9 @@ #include "cogl-context.h" #include "cogl-clip-stack.h" #include "cogl-material-private.h" +#include "cogl-clip-stack.h" +#include "cogl-draw-buffer-private.h" +#include "cogl-clip-stack.h" #include #include @@ -72,7 +75,7 @@ _cogl_path_add_node (gboolean new_sub_path, } void -_cogl_path_stroke_nodes () +_cogl_path_stroke_nodes (void) { guint path_start = 0; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; @@ -80,13 +83,21 @@ _cogl_path_stroke_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); cogl_enable (enable_flags); options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK; + /* disable all texture layers */ options.disable_layers = (guint32)~0; - _cogl_material_flush_gl_state (ctx->source_material,&options); - _cogl_flush_matrix_stacks(); + + _cogl_material_flush_gl_state (ctx->source_material, &options); while (path_start < ctx->path_nodes->len) { @@ -129,18 +140,38 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, CoglPathNode *path, gboolean merge) { - guint path_start = 0; - guint sub_path_num = 0; - float bounds_x; - float bounds_y; - float bounds_w; - float bounds_h; - gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + guint path_start = 0; + guint sub_path_num = 0; + float bounds_x; + float bounds_y; + float bounds_w; + float bounds_h; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglHandle prev_source; + int i; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't track changes to the stencil buffer in the journal + * so we need to flush any batched geometry first */ + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* Just setup a simple material that doesn't use texturing... */ - _cogl_material_flush_gl_state (ctx->stencil_material, NULL); + prev_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->stencil_material); + + _cogl_material_flush_gl_state (ctx->source_material, NULL); enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); @@ -156,7 +187,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } else { - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + cogl_clear (NULL, COGL_BUFFER_BIT_STENCIL); GE( glStencilMask (1) ); GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); } @@ -167,7 +198,13 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( glDepthMask (FALSE) ); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, COGL_MATRIX_MODELVIEW); + for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = 0; + while (path_start < path_size) { GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), @@ -183,6 +220,14 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); + /* Make sure the rectangle hits the stencil buffer before + * directly changing other GL state. */ + _cogl_journal_flush (); + /* NB: The journal flushing may trash the modelview state and + * enable flags */ + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); + cogl_enable (enable_flags); GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -204,17 +249,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_flush_to_gl (projection_stack, + COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + /* Make sure these rectangles hit the stencil buffer before we + * restore the stencil op/func. */ + _cogl_journal_flush (); - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } GE( glStencilMask (~(GLuint) 0) ); @@ -250,6 +302,21 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We are going to use GL to draw directly so make sure any + * previously batched geometry gets to GL before we start... + */ + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + + _cogl_material_flush_gl_state (ctx->source_material, NULL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); + /* clear scanline intersection lists */ for (i=0; i < bounds_h; i++) scanlines[i]=NULL; @@ -386,8 +453,6 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, } /* render triangles */ - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); GE ( glVertexPointer (2, GL_FLOAT, 0, coords ) ); GE ( glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3)); g_free (coords); @@ -395,7 +460,7 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, } void -_cogl_path_fill_nodes () +_cogl_path_fill_nodes (void) { float bounds_x; float bounds_y; @@ -409,19 +474,27 @@ _cogl_path_fill_nodes () if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { + CoglHandle draw_buffer; + CoglClipStackState *clip_state; + + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min, ctx->path_nodes_max, ctx->path_nodes->len, &g_array_index (ctx->path_nodes, CoglPathNode, 0), - ctx->clip.stencil_used); + clip_state->stencil_used); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); /* The stencil buffer now contains garbage so the clip area needs to be rebuilt */ - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_state_dirty (clip_state); } else { diff --git a/clutter/cogl/cogl/driver/gles/cogl.c b/clutter/cogl/cogl/driver/gles/cogl.c index aa7da6ef2..565ff0fa7 100644 --- a/clutter/cogl/cogl/driver/gles/cogl.c +++ b/clutter/cogl/cogl/driver/gles/cogl.c @@ -31,6 +31,56 @@ #include "cogl-internal.h" #include "cogl-context.h" +typedef struct _CoglGLSymbolTableEntry +{ + const char *name; + void *ptr; +} CoglGLSymbolTableEntry; + +gboolean +cogl_check_extension (const gchar *name, const gchar *ext) +{ + gchar *end; + gint name_len, n; + + if (name == NULL || ext == NULL) + return FALSE; + + end = (gchar*)(ext + strlen(ext)); + + name_len = strlen(name); + + while (ext < end) + { + n = strcspn(ext, " "); + + if ((name_len == n) && (!strncmp(name, ext, n))) + return TRUE; + ext += (n + 1); + } + + return FALSE; +} + +gboolean +_cogl_resolve_gl_symbols (CoglGLSymbolTableEntry *symbol_table, + const char *suffix) +{ + int i; + gboolean status = TRUE; + for (i = 0; symbol_table[i].name; i++) + { + char *full_name = g_strdup_printf ("%s%s", symbol_table[i].name, suffix); + *((CoglFuncPtr *)symbol_table[i].ptr) = cogl_get_proc_address (full_name); + g_free (full_name); + if (!*((CoglFuncPtr *)symbol_table[i].ptr)) + { + status = FALSE; + break; + } + } + return status; +} void @@ -39,9 +89,34 @@ _cogl_features_init (void) CoglFeatureFlags flags = 0; int max_clip_planes = 0; GLint num_stencil_bits = 0; + const char *gl_extensions; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + gl_extensions = (const char*) glGetString (GL_EXTENSIONS); + + if (cogl_check_extension ("GL_OES_framebuffer_object", gl_extensions)) + { + g_assert (0); + CoglGLSymbolTableEntry symbol_table[] = { + {"glGenRenderbuffers", &ctx->drv.pf_glGenRenderbuffers}, + {"glDeleteRenderbuffers", &ctx->drv.pf_glDeleteRenderbuffers}, + {"glBindRenderbuffer", &ctx->drv.pf_glBindRenderbuffer}, + {"glRenderbufferStorage", &ctx->drv.pf_glRenderbufferStorage}, + {"glGenFramebuffers", &ctx->drv.pf_glGenFramebuffers}, + {"glBindFramebuffer", &ctx->drv.pf_glBindFramebuffer}, + {"glFramebufferTexture2D", &ctx->drv.pf_glFramebufferTexture2D}, + {"glFramebufferRenderbuffer", &ctx->drv.pf_glFramebufferRenderbuffer}, + {"glCheckFramebufferStatus", &ctx->drv.pf_glCheckFramebufferStatus}, + {"glDeleteFramebuffers", &ctx->drv.pf_glDeleteFramebuffers}, + {"glGenerateMipmap", &ctx->drv.pf_glGenerateMipmap}, + {NULL, NULL} + }; + + if (_cogl_resolve_gl_symbols (symbol_table, "OES")) + flags |= COGL_FEATURE_OFFSCREEN; + } + GE( glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) ); /* We need at least three stencil bits to combine clips */ if (num_stencil_bits > 2) diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 074595f9f..678bbde2f 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -210,6 +210,7 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); + ClutterStage *stage = stage_x11->wrapper; gboolean resize; resize = clutter_stage_get_user_resizable (stage_x11->wrapper); @@ -250,6 +251,22 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, stage_x11->xwin, stage_x11->xwin_width, stage_x11->xwin_height); + + /* If the viewport hasn't previously been initialized then even + * though we can't guarantee that the server will honour our request + * we need to ensure a valid viewport is set before our first paint. + */ + if (G_UNLIKELY (!stage_x11->viewport_initialized)) + { + ClutterPerspective perspective; + clutter_stage_get_perspective (stage, &perspective); + _cogl_setup_viewport (stage_x11->xwin_width, + stage_x11->xwin_height, + perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); + } } if (!resize) @@ -654,6 +671,7 @@ clutter_stage_x11_init (ClutterStageX11 *stage) stage->is_foreign_xwin = FALSE; stage->fullscreen_on_map = FALSE; stage->is_cursor_visible = TRUE; + stage->viewport_initialized = FALSE; stage->title = NULL; diff --git a/clutter/x11/clutter-stage-x11.h b/clutter/x11/clutter-stage-x11.h index 0929094bc..71b66e348 100644 --- a/clutter/x11/clutter-stage-x11.h +++ b/clutter/x11/clutter-stage-x11.h @@ -50,9 +50,10 @@ struct _ClutterStageX11 { ClutterGroup parent_instance; - guint is_foreign_xwin : 1; - guint fullscreen_on_map : 1; - guint is_cursor_visible : 1; + guint is_foreign_xwin : 1; + guint fullscreen_on_map : 1; + guint is_cursor_visible : 1; + guint viewport_initialized : 1; Window xwin; gint xwin_width; diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 7a5b6f971..a2cb54d27 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -35,6 +35,10 @@ test_conformance_SOURCES = \ test-materials.c \ test-group.c \ test-actor-size.c \ + test-texture-fbo.c \ + test-cogl-viewport.c \ + test-cogl-offscreen.c \ + test-cogl-readpixels.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c index 56cb8c45a..af45d3c9c 100644 --- a/tests/conform/test-backface-culling.c +++ b/tests/conform/test-backface-culling.c @@ -5,8 +5,6 @@ #include "test-conform-common.h" -static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; - #ifdef CLUTTER_COGL_HAS_GL /* Size the texture so that it is just off a power of two to enourage @@ -31,24 +29,31 @@ static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; typedef struct _TestState { - guint frame; CoglHandle texture; + CoglHandle offscreen; + CoglHandle offscreen_tex; } TestState; +#include + static gboolean validate_part (int xnum, int ynum, gboolean shown) { guchar *pixels, *p; - ClutterActor *stage = clutter_stage_get_default (); gboolean ret = TRUE; + pixels = g_malloc0 ((TEXTURE_RENDER_SIZE - TEST_INSET * 2) + * (TEXTURE_RENDER_SIZE - TEST_INSET * 2) * 4); + /* Read the appropriate part but skip out a few pixels around the edges */ - pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage), - xnum * TEXTURE_RENDER_SIZE + TEST_INSET, - ynum * TEXTURE_RENDER_SIZE + TEST_INSET, - TEXTURE_RENDER_SIZE - TEST_INSET * 2, - TEXTURE_RENDER_SIZE - TEST_INSET * 2); + cogl_read_pixels (xnum * TEXTURE_RENDER_SIZE + TEST_INSET, + ynum * TEXTURE_RENDER_SIZE + TEST_INSET, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixels); /* Make sure every pixels is the appropriate color */ for (p = pixels; @@ -70,47 +75,9 @@ validate_part (int xnum, int ynum, gboolean shown) } static void -validate_result (TestState *state) -{ - /* Front-facing texture */ - g_assert (validate_part (0, 0, TRUE)); - /* Front-facing texture with flipped tex coords */ - g_assert (validate_part (1, 0, TRUE)); - /* Back-facing texture */ - g_assert (validate_part (2, 0, FALSE)); - /* Front-facing texture polygon */ - g_assert (validate_part (3, 0, TRUE)); - /* Back-facing texture polygon */ - g_assert (validate_part (4, 0, FALSE)); - /* Regular rectangle */ - g_assert (validate_part (5, 0, TRUE)); - - /* Backface culling disabled - everything should be shown */ - - /* Front-facing texture */ - g_assert (validate_part (0, 1, TRUE)); - /* Front-facing texture with flipped tex coords */ - g_assert (validate_part (1, 1, TRUE)); - /* Back-facing texture */ - g_assert (validate_part (2, 1, TRUE)); - /* Front-facing texture polygon */ - g_assert (validate_part (3, 1, TRUE)); - /* Back-facing texture polygon */ - g_assert (validate_part (4, 1, TRUE)); - /* Regular rectangle */ - g_assert (validate_part (5, 1, TRUE)); - - /* Comment this out if you want visual feedback of what this test - * paints. - */ - clutter_main_quit (); -} - -static void -on_paint (ClutterActor *actor, TestState *state) +do_test_backface_culling (TestState *state) { int i; - int frame_num; CoglHandle material = cogl_material_new (); cogl_material_set_layer_filters (material, 0, @@ -197,21 +164,91 @@ on_paint (ClutterActor *actor, TestState *state) cogl_set_backface_culling_enabled (FALSE); } - cogl_pop_matrix (); - cogl_handle_unref (material); - /* XXX: Experiments have shown that for some buggy drivers, when using - * glReadPixels there is some kind of race, so we delay our test for a - * few frames and a few seconds: + cogl_pop_matrix (); + + /* Front-facing texture */ + g_assert (validate_part (0, 0, TRUE)); + /* Front-facing texture with flipped tex coords */ + g_assert (validate_part (1, 0, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (2, 0, FALSE)); + /* Front-facing texture polygon */ + g_assert (validate_part (3, 0, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (4, 0, FALSE)); + /* Regular rectangle */ + g_assert (validate_part (5, 0, TRUE)); + + /* Backface culling disabled - everything should be shown */ + + /* Front-facing texture */ + g_assert (validate_part (0, 1, TRUE)); + /* Front-facing texture with flipped tex coords */ + g_assert (validate_part (1, 1, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (2, 1, TRUE)); + /* Front-facing texture polygon */ + g_assert (validate_part (3, 1, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (4, 1, TRUE)); + /* Regular rectangle */ + g_assert (validate_part (5, 1, TRUE)); + +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglColor clr; + float stage_viewport[4]; + CoglMatrix stage_projection; + CoglMatrix stage_modelview; + + cogl_color_set_from_4ub (&clr, 0x00, 0x00, 0x00, 0xff); + + do_test_backface_culling (state); + + /* Since we are going to repeat the test rendering offscreen we clear the + * stage, just to minimize the chance of a some other bug causing us + * mistakenly reading back the results from the stage and giving a false + * posistive. */ + cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL); + + /* + * Now repeat the test but rendered to an offscreen draw buffer... */ - /* Need to increment frame first because clutter_stage_read_pixels - fires a redraw */ - frame_num = state->frame++; - if (frame_num == 2) - validate_result (state); - else if (frame_num < 2) - g_usleep (G_USEC_PER_SEC); + + cogl_get_viewport (stage_viewport); + cogl_get_projection_matrix (&stage_projection); + cogl_get_modelview_matrix (&stage_modelview); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, state->offscreen); + + cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL); + + cogl_set_viewport (stage_viewport[0], + stage_viewport[1], + stage_viewport[2], + stage_viewport[3]); + cogl_set_projection_matrix (&stage_projection); + cogl_set_modelview_matrix (&stage_modelview); + + do_test_backface_culling (state); + + cogl_pop_draw_buffer (); + + /* Incase we want feedback of what was drawn offscreen we draw it + * to the stage... */ + cogl_set_source_texture (state->offscreen_tex); + cogl_rectangle (0, 0, stage_viewport[2], stage_viewport[3]); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); } static gboolean @@ -256,15 +293,26 @@ test_backface_culling (TestConformSimpleFixture *fixture, gconstpointer data) { TestState state; + CoglHandle tex; ClutterActor *stage; + float stage_width; + float stage_height; + const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; ClutterActor *group; guint idle_source; - state.frame = 0; + stage = clutter_stage_get_default (); + clutter_actor_get_size (stage, &stage_width, &stage_height); + + state.offscreen = COGL_INVALID_HANDLE; state.texture = make_texture (); - stage = clutter_stage_get_default (); + tex = cogl_texture_new_with_size (stage_width, stage_height, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_ANY); /* internal fmt */ + state.offscreen = cogl_offscreen_new_to_texture (tex); + state.offscreen_tex = tex; clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); @@ -284,6 +332,8 @@ test_backface_culling (TestConformSimpleFixture *fixture, g_source_remove (idle_source); + cogl_handle_unref (state.offscreen); + cogl_handle_unref (state.offscreen_tex); cogl_handle_unref (state.texture); if (g_test_verbose ()) diff --git a/tests/conform/test-cogl-offscreen.c b/tests/conform/test-cogl-offscreen.c new file mode 100644 index 000000000..f6f4a972a --- /dev/null +++ b/tests/conform/test-cogl-offscreen.c @@ -0,0 +1,169 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + guint8 pixel[4]; + + /* Save the Clutter viewport/matrices and load identity matrices */ + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + /* Set a scale and translate transform on the window draw buffer before + * switching to the offscreen draw buffer so we can verify it gets restored + * when we switch back + * + * The test is going to draw a grid of 4 colors to a texture which we + * subsequently draw to the window with a fullscreen rectangle. This + * transform will flip the texture left to right, scale it to a quater of the + * window size and slide it to the top right of the window. + */ + cogl_translate (0.5, 0.5, 0); + cogl_scale (-0.5, 0.5, 1); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, offscreen); + + /* Cogl should release the last reference when we call cogl_pop_draw_buffer() + */ + cogl_handle_unref (offscreen); + + /* Setup something other than the identity matrix for the modelview so we can + * verify it gets restored when we call cogl_pop_draw_buffer () */ + cogl_scale (2, 2, 1); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-0.5, 0.5, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 0.5, 0.5, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-0.5, 0, 0, -0.5); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 0.5, -0.5); + + cogl_pop_draw_buffer (); + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + cogl_handle_unref (tex); + + /* NB: The texture is drawn flipped horizontally and scaled to fit in the + * top right corner of the window. */ + + /* red, top right */ + cogl_read_pixels (DRAW_BUFFER_WIDTH - 1, 0, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0x00 && pixel[BLUE] == 0x00); + + /* green, top left */ + cogl_read_pixels ((DRAW_BUFFER_WIDTH/2), 0, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0xff && pixel[BLUE] == 0x00); + + /* blue, bottom right */ + cogl_read_pixels (DRAW_BUFFER_WIDTH - 1, (DRAW_BUFFER_HEIGHT/2) - 1, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0x00 && pixel[BLUE] == 0xff); + + /* white, bottom left */ + cogl_read_pixels ((DRAW_BUFFER_WIDTH/2), (DRAW_BUFFER_HEIGHT/2) - 1, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0xff && pixel[BLUE] == 0xff); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_offscreen (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-cogl-readpixels.c b/tests/conform/test-cogl-readpixels.c new file mode 100644 index 000000000..563efdf60 --- /dev/null +++ b/tests/conform/test-cogl-readpixels.c @@ -0,0 +1,153 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + guint32 *pixels; + + /* Save the Clutter viewport/matrices and load identity matrices */ + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* All offscreen rendering is done upside down so the first thing we + * verify is reading back grid of colors from a CoglOffscreen draw buffer + */ + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, offscreen); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-1, 1, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 1, 1, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 0, 0, -1); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 1, -1); + + pixels = g_malloc0 (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + cogl_read_pixels (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + (guchar *)pixels); + + g_assert (pixels[0] == 0xff0000ff); + g_assert (pixels[DRAW_BUFFER_WIDTH - 1] == 0xff00ff00); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH] == 0xffff0000); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH + + DRAW_BUFFER_WIDTH - 1] == 0xffffffff); + g_free (pixels); + + cogl_pop_draw_buffer (); + cogl_handle_unref (offscreen); + + /* Now verify reading back from an onscreen draw buffer... + */ + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + pixels = g_malloc0 (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + cogl_read_pixels (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + (guchar *)pixels); + + g_assert (pixels[0] == 0xff0000ff); + g_assert (pixels[DRAW_BUFFER_WIDTH - 1] == 0xff00ff00); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH] == 0xffff0000); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH + + DRAW_BUFFER_WIDTH - 1] == 0xffffffff); + g_free (pixels); + + cogl_handle_unref (tex); + + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_readpixels (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-cogl-viewport.c b/tests/conform/test-cogl-viewport.c new file mode 100644 index 000000000..1188b3bd2 --- /dev/null +++ b/tests/conform/test-cogl-viewport.c @@ -0,0 +1,417 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +static void +assert_region_color (int x, + int y, + int width, + int height, + guint8 red, + guint8 green, + guint8 blue, + guint8 alpha) +{ + guint8 *data = g_malloc0 (width * height * 4); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + guint8 *pixel = &data[y*width*4 + x*4]; +#if 1 + g_assert (pixel[RED] == red && + pixel[GREEN] == green && + pixel[BLUE] == blue && + pixel[ALPHA] == alpha); +#endif + } + g_free (data); +} + +static void +assert_rectangle_color_and_black_border (int x, + int y, + int width, + int height, + guint8 red, + guint8 green, + guint8 blue) +{ + /* check the rectangle itself... */ + assert_region_color (x, y, width, height, red, green, blue, 0xff); + /* black to left of the rectangle */ + assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black to right of the rectangle */ + assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black above the rectangle */ + assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff); + /* and black below the rectangle */ + assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff); +} + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + CoglColor black; + float x0; + float y0; + float width; + float height; + + /* for clearing the offscreen draw buffer to black... */ + cogl_color_set_from_4ub (&black, 0x00, 0x00, 0x00, 0xff); + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* - Create a 100x200 viewport (i.e. smaller than the onscreen draw buffer) + * and position it a (20, 10) inside the draw buffer. + * - Fill the whole viewport with a purple rectangle + * - Verify that the draw buffer is black with a 100x200 purple rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with purple.. */ + cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0xff); + + + /* - Create a viewport twice the size of the onscreen draw buffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which + * is (20, 10) within the draw buffer) + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + DRAW_BUFFER_WIDTH * 2, /* width */ + DRAW_BUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 green rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / DRAW_BUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / DRAW_BUFFER_HEIGHT) * 20.0f; + width = (1.0f / DRAW_BUFFER_WIDTH) * 100; + height = (1.0f / DRAW_BUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0xff, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rect (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the draw buffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push (-(width/2.0), -(height/2.0), width, height); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something specific so we can verify that it gets + * restored after we are done testing with an offscreen draw buffer... */ + cogl_set_viewport (20, 10, 100, 200); + + /* + * Next test offscreen drawing... + */ + cogl_push_draw_buffer (); + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + cogl_set_draw_buffer (0 /* unused */, offscreen); + + + /* - Create a 100x200 viewport (i.e. smaller than the offscreen draw buffer) + * and position it a (20, 10) inside the draw buffer. + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a viewport twice the size of the offscreen draw buffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which + * is (20, 10) within the draw buffer) + * - Verify that the draw buffer is black with a 100x200 red rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + DRAW_BUFFER_WIDTH * 2, /* width */ + DRAW_BUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 red rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / DRAW_BUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / DRAW_BUFFER_HEIGHT) * 20.0f; + width = (1.0f / DRAW_BUFFER_WIDTH) * 100; + height = (1.0f / DRAW_BUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rect (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the draw buffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push (-(width/2.0), -(height/2.0), width, height); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something obscure to verify that it gets + * replace when we switch back to the onscreen draw buffer... */ + cogl_set_viewport (0, 0, 10, 10); + + cogl_pop_draw_buffer (); + cogl_handle_unref (offscreen); + + /* + * Verify that the previous onscreen draw buffer's viewport was restored + * by drawing a white rectangle across the whole viewport. This should + * draw a 100x200 rectangle at (20,10) relative to the onscreen draw + * buffer... + */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0xff, 0xff); + + + /* Uncomment to display the last contents of the offscreen draw buffer */ +#if 1 + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + cogl_set_viewport (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); +#endif + + cogl_handle_unref (tex); + + /* Finally restore the stage's original state... */ + cogl_pop_matrix (); + cogl_set_projection_matrix (&saved_projection); + cogl_set_viewport (saved_viewport[0], saved_viewport[1], + saved_viewport[2], saved_viewport[3]); + + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_viewport (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 2b2624434..88b7abfc5 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -147,6 +147,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/texture", test_backface_culling); TEST_CONFORM_SIMPLE ("/texture", test_npot_texture); TEST_CONFORM_SIMPLE ("/texture", test_premult); + TEST_CONFORM_SIMPLE ("/texture", test_texture_fbo); TEST_CONFORM_SIMPLE ("/path", test_path); @@ -174,5 +175,9 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/sizing", test_fixed_size); TEST_CONFORM_SIMPLE ("/sizing", test_preferred_size); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_viewport); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_offscreen); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_readpixels); + return g_test_run (); } diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c new file mode 100644 index 000000000..958fc2932 --- /dev/null +++ b/tests/conform/test-texture-fbo.c @@ -0,0 +1,255 @@ + +#include +#include + +#include "test-conform-common.h" + +#define SOURCE_SIZE 32 +#define SOURCE_DIVISIONS_X 2 +#define SOURCE_DIVISIONS_Y 2 +#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) +#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) + +static const ClutterColor +corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = + { + { 0xff, 0x00, 0x00, 0xff }, /* red top left */ + { 0x00, 0xff, 0x00, 0xff }, /* green top right */ + { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */ + { 0xff, 0x00, 0xff, 0xff } /* purple bottom right */ + }; + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; + guint frame; +} TestState; + +static ClutterActor * +create_source (void) +{ + int x, y; + ClutterActor *group = clutter_group_new (); + + /* Create a group with a different coloured rectangle at each + corner */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + ClutterActor *rect = clutter_rectangle_new (); + clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT); + clutter_actor_set_position (rect, + DIVISION_WIDTH * x, + DIVISION_HEIGHT * y); + clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), + corner_colors + + (y * SOURCE_DIVISIONS_X + x)); + clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL); + } + + return group; +} + +static void +pre_paint_clip_cb (void) +{ + /* Generate a clip path that clips out the top left division */ + cogl_path_move_to (DIVISION_WIDTH, 0); + cogl_path_line_to (SOURCE_SIZE, 0); + cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE); + cogl_path_line_to (0, SOURCE_SIZE); + cogl_path_line_to (0, DIVISION_HEIGHT); + cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT); + cogl_path_close (); + cogl_clip_push_from_path (); +} + +static void +post_paint_clip_cb (void) +{ + cogl_clip_pop (); +} + +static gboolean +validate_part (TestState *state, + int xpos, int ypos, + int clip_flags) +{ + int x, y; + gboolean pass = TRUE; + + /* Check whether the center of each division is the right color */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + guchar *pixels; + const ClutterColor *correct_color; + + /* Read the center pixels of this division */ + pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), + x * DIVISION_WIDTH + + DIVISION_WIDTH / 2 + xpos, + y * DIVISION_HEIGHT + + DIVISION_HEIGHT / 2 + ypos, + 1, 1); + + /* If this division is clipped then it should be the stage + color */ + if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x)))) + correct_color = &stage_color; + else + /* Otherwise it should be the color for this division */ + correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x; + + if (pixels == NULL || + pixels[0] != correct_color->red || + pixels[1] != correct_color->green || + pixels[2] != correct_color->blue) + pass = FALSE; + + g_free (pixels); + } + + return pass; +} + +static void +validate_result (TestState *state) +{ + int ypos = 0; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); + ypos++; + +#if 0 /* this doesn't work */ + if (g_test_verbose ()) + g_print ("Testing offscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); +#endif + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with rectangular clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1)); + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with path clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1)); + ypos++; + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int frame_num; + + /* XXX: Experiments have shown that for some buggy drivers, when using + * glReadPixels there is some kind of race, so we delay our test for a + * few frames and a few seconds: + */ + /* Need to increment frame first because clutter_stage_read_pixels + fires a redraw */ + frame_num = state->frame++; + if (frame_num == 2) + validate_result (state); + else if (frame_num < 2) + g_usleep (G_USEC_PER_SEC); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_texture_fbo (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + guint idle_source; + ClutterActor *actor; + int ypos = 0; + + state.frame = 0; + + state.stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* Onscreen source with clone next to it */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Offscreen source with clone */ +#if 0 /* this doesn't work */ + actor = create_source (); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); +#endif + ypos++; + + /* Source clipped to the top left division */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Source clipped to everything but top left division using a + path */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + g_signal_connect (actor, "paint", + G_CALLBACK (pre_paint_clip_cb), NULL); + g_signal_connect_after (actor, "paint", + G_CALLBACK (post_paint_clip_cb), NULL); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, state.stage); + + g_signal_connect_after (state.stage, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (state.stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/interactive/test-fbo.c b/tests/interactive/test-fbo.c index 3d82b9b44..fdf8b83a9 100644 --- a/tests/interactive/test-fbo.c +++ b/tests/interactive/test-fbo.c @@ -1,25 +1,4 @@ -/*#define TEST_GROUP */ - -/* These variables are used instead of the standard GLSL variables on - GLES 2 */ -#ifdef HAVE_COGL_GLES2 - -#define GLES2_VARS \ - "precision mediump float;\n" \ - "varying vec2 tex_coord;\n" \ - "varying vec4 frag_color;\n" -#define TEX_COORD "tex_coord" -#define COLOR_VAR "frag_color" - -#else /* HAVE_COGL_GLES2 */ - -#define GLES2_VARS "" -#define TEX_COORD "gl_TexCoord[0]" -#define COLOR_VAR "gl_Color" - -#endif /* HAVE_COGL_GLES2 */ - #include #include @@ -27,13 +6,16 @@ #include #include -ClutterActor* -make_source(void) -{ - ClutterActor *source, *actor; - GError *error = NULL; +#define STAGE_WIDTH 800 +#define STAGE_HEIGHT 600 - ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; +ClutterActor * +make_source (void) +{ + ClutterActor *source, *actor; + GError *error = NULL; + + ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; source = clutter_group_new(); actor = clutter_texture_new_from_file ("redhand.png", &error); @@ -51,65 +33,17 @@ make_source(void) return source; } -ClutterShader* -make_shader(void) -{ - ClutterShader *shader; - GError *error = NULL; - - shader = clutter_shader_new (); - clutter_shader_set_fragment_source (shader, - - GLES2_VARS - "uniform float radius ;" - "uniform sampler2D rectTexture;" - "uniform float x_step, y_step;" - "" - "void main()" - "{" - " vec4 color = texture2D(rectTexture, " TEX_COORD ".st);" - " float u;" - " float v;" - " int count = 1;" - " for (u=-radius;umessage); - g_error_free (error); - } - - return shader; -} - -G_MODULE_EXPORT gint -test_fbo_main (gint argc, gchar *argv[]) +G_MODULE_EXPORT int +test_fbo_main (int argc, char *argv[]) { ClutterColor blue = {0x33, 0x44, 0x55, 0xff}; ClutterActor *fbo; - ClutterActor *onscreen_source, *offscreen_source, *trans_source; - ClutterActor *foo_source; + ClutterActor *onscreen_source; ClutterActor *stage; - ClutterActor *clone; - ClutterShader *shader; - gint padx, pady; - gint fbo_width, fbo_height; + ClutterAnimation *animation; + int x_pos = 200; + int y_pos = 100; clutter_init (&argc, &argv); @@ -117,6 +51,7 @@ test_fbo_main (gint argc, gchar *argv[]) g_error("This test requires CLUTTER_FEATURE_OFFSCREEN"); stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), &blue); /* Create the first source */ @@ -124,93 +59,34 @@ test_fbo_main (gint argc, gchar *argv[]) clutter_actor_show_all (onscreen_source); clutter_group_add (stage, onscreen_source); - /* Basic sizing for alignment */ - fbo_width = clutter_actor_get_width (onscreen_source); - fbo_height = clutter_actor_get_height (onscreen_source); - padx = fbo_width + 10; - pady = fbo_height + 10; - clutter_actor_set_size (stage, padx*4, pady*2); + y_pos = (STAGE_HEIGHT/2.0) - + (clutter_actor_get_height (onscreen_source)/2.0); + clutter_actor_set_position (onscreen_source, x_pos, y_pos); + x_pos += clutter_actor_get_width (onscreen_source); - /* Second hand from fbo onscreen */ + animation = clutter_actor_animate (onscreen_source, + CLUTTER_LINEAR, + 5000, /* 1 second duration */ + "rotation-angle-y", 360.0f, + NULL); + clutter_animation_set_loop (animation, TRUE); + + /* Second hand = actor from onscreen_source */ if ((fbo = clutter_texture_new_from_actor (onscreen_source)) == NULL) g_error("onscreen fbo creation failed"); - clutter_actor_set_position (fbo, padx, 0); + clutter_actor_set_position (fbo, x_pos, y_pos); + x_pos += clutter_actor_get_width (fbo); clutter_group_add (stage, fbo); - /* apply a shader to it */ - shader = make_shader(); - clutter_actor_set_shader (fbo, shader); - clutter_actor_set_shader_param_float (fbo, "radius", 2.0); - clutter_actor_set_shader_param_float (fbo, "x_step", - 1.0f / clutter_util_next_p2 (fbo_width)); - clutter_actor_set_shader_param_float (fbo, "y_step", - 1.0f / clutter_util_next_p2 (fbo_height)); + /* Third hand = actor from Second hand */ + if ((fbo = clutter_texture_new_from_actor (fbo)) == NULL) + g_error("fbo from fbo creation failed"); - /* Third from cloning the fbo texture */ - clone = clutter_clone_new (fbo); - clutter_container_add_actor (CLUTTER_CONTAINER (stage), clone); - clutter_actor_set_position (clone, padx*2, 0); - - - /* Forth - an offscreen source */ - offscreen_source = make_source(); - clutter_actor_show_all (offscreen_source); /* need to show() offscreen */ - if ((fbo = clutter_texture_new_from_actor (offscreen_source)) == NULL) - g_error("offscreen fbo creation failed"); - - clutter_actor_set_position (fbo, padx*3, 0); + clutter_actor_set_position (fbo, x_pos, y_pos); + x_pos += clutter_actor_get_width (fbo); clutter_group_add (stage, fbo); - - /* 5th transformed */ - trans_source = make_source(); - clutter_actor_show_all (trans_source); /* need to show() offscreen */ - - clutter_actor_set_scale (trans_source, 2.5, 2.5); - - if ((fbo = clutter_texture_new_from_actor (trans_source)) == NULL) - g_error("transformed fbo creation failed"); - - clutter_actor_set_position (fbo, 0, pady); - clutter_group_add (stage, fbo); - - - /* 6th resized bigger, but after fbo creation */ - trans_source = make_source(); - clutter_actor_show_all (trans_source); /* need to show() offscreen */ - - if ((fbo = clutter_texture_new_from_actor (trans_source)) == NULL) - g_error("transformed fbo creation failed"); - - /* rotate after */ - clutter_actor_move_anchor_point_from_gravity (trans_source, - CLUTTER_GRAVITY_CENTER); - clutter_actor_set_rotation (trans_source, CLUTTER_Z_AXIS, 90.0, 0, 0, 0); - - clutter_actor_set_position (fbo, padx, pady); - clutter_group_add (stage, fbo); - - - /* non visual breaks */ - foo_source = make_source(); - g_object_ref_sink (foo_source); - - clutter_actor_show_all (foo_source); - if ((fbo = clutter_texture_new_from_actor (foo_source)) == NULL) - g_error("foo fbo creation failed"); - - g_object_unref (foo_source); /* fbo should keep it around */ - - clutter_actor_set_position (fbo, padx*3, pady); - clutter_group_add (stage, fbo); - - /* TODO: - * Check realize/unrealize - * get_pixbuf() - * set_rgba on fbo texture. - */ - clutter_actor_show_all (stage); clutter_main ();