From 4a7fa6d0fa30b6510e997ed3e2fd0ec7ec1a6354 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Jan 2009 11:07:35 +0000 Subject: [PATCH] Updates GLES1 support for CoglMaterial This updates cogl/gles in line with the integration of CoglMaterial throughout Cogl that has been done for cogl/gl. Note: This is still buggy, but at least it builds again and test-actors works. Some GLES2 specific changes were made, but these haven't been tested yet. --- clutter/cogl/common/cogl-material.c | 35 +- clutter/cogl/gl/cogl-texture.c | 25 +- clutter/cogl/gles/cogl-context.c | 130 +- clutter/cogl/gles/cogl-context.h | 94 +- clutter/cogl/gles/cogl-gles2-wrapper.h | 5 + clutter/cogl/gles/cogl-internal.h | 11 +- clutter/cogl/gles/cogl-primitives.c | 75 +- clutter/cogl/gles/cogl-texture-private.h | 39 +- clutter/cogl/gles/cogl-texture.c | 1680 ++++++++++++++-------- clutter/cogl/gles/cogl.c | 49 +- 10 files changed, 1333 insertions(+), 810 deletions(-) diff --git a/clutter/cogl/common/cogl-material.c b/clutter/cogl/common/cogl-material.c index 5b3c7490e..925e77af6 100644 --- a/clutter/cogl/common/cogl-material.c +++ b/clutter/cogl/common/cogl-material.c @@ -886,8 +886,12 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, _cogl_material_layer_pointer_from_handle (layer_handle); CoglLayerInfo *gl_layer_info = NULL; CoglLayerInfo new_gl_layer_info; + CoglHandle tex_handle; GLuint gl_texture; GLenum gl_target; +#ifdef HAVE_COGL_GLES2 + GLenum gl_internal_format; +#endif new_gl_layer_info.layer0_overridden = layer0_override_texture ? TRUE : FALSE; @@ -912,18 +916,19 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, continue; } - cogl_texture_get_gl_texture (layer->texture, &gl_texture, &gl_target); + tex_handle = layer->texture; + cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); if (new_gl_layer_info.layer0_overridden) gl_texture = layer0_override_texture; else if (new_gl_layer_info.fallback) { - CoglHandle tex_handle; - if (gl_target == GL_TEXTURE_2D) tex_handle = ctx->default_gl_texture_2d_tex; +#ifdef HAVE_COGL_GL else if (gl_target == GL_TEXTURE_RECTANGLE_ARB) tex_handle = ctx->default_gl_texture_rect_tex; +#endif else { g_warning ("We don't have a default texture we can use to fill " @@ -935,12 +940,28 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, cogl_texture_get_gl_texture (tex_handle, &gl_texture, NULL); } +#ifdef HAVE_COGL_GLES2 + { + CoglTexture *tex = + _cogl_texture_pointer_from_handle (tex_handle); + gl_internal_format = tex->gl_intformat; + } +#endif + GE (glActiveTexture (GL_TEXTURE0 + i)); if (!gl_layer_info || gl_layer_info->gl_target != gl_target || gl_layer_info->gl_texture != gl_texture) - GE (glBindTexture (gl_target, gl_texture)); + { +#ifdef HAVE_COGL_GLES2 + cogl_gles2_wrapper_bind_texture (gl_target, + gl_texture, + gl_internal_format); +#else + GE (glBindTexture (gl_target, gl_texture)); +#endif + } /* Disable the previous target if it was different */ if (gl_layer_info && @@ -1007,7 +1028,11 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material) if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR && material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR)) { - GE (glColor4fv (material->unlit)); + /* GLES doesn't have glColor4fv... */ + GE (glColor4f (material->unlit[0], + material->unlit[1], + material->unlit[2], + material->unlit[3])); } if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 0a84457c5..fba290dd3 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -51,6 +51,13 @@ printf("err: 0x%x\n", err); \ } */ +#ifdef HAVE_COGL_GL +#ifdef glDrawRangeElements +#undef glDrawRangeElements +#endif +#define glDrawRangeElements ctx->pf_glDrawRangeElements +#endif + static void _cogl_journal_flush (void); static void _cogl_texture_free (CoglTexture *tex); @@ -2040,10 +2047,10 @@ _cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start, GE (glVertexPointer (2, GL_FLOAT, stride, vertex_pointer)); GE (ctx->pf_glDrawRangeElements (GL_TRIANGLES, - 0, ctx->static_indices->len - 1, - 6 * batch_len, - GL_UNSIGNED_SHORT, - ctx->static_indices->data)); + 0, ctx->static_indices->len - 1, + 6 * batch_len, + GL_UNSIGNED_SHORT, + ctx->static_indices->data)); } static void @@ -2970,6 +2977,16 @@ cogl_polygon (CoglTextureVertex *vertices, if (i == 0 && cogl_texture_is_sliced (tex_handle)) { +#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) + { + static gboolean shown_gles_slicing_warning = FALSE; + if (!shown_gles_slicing_warning) + g_warning ("cogl_polygon does not work for sliced textures " + "on GL ES"); + shown_gles_slicing_warning = TRUE; + return; + } +#endif if (n_layers > 1) { static gboolean shown_slicing_warning = FALSE; diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c index 995068817..9d874d78f 100644 --- a/clutter/cogl/gles/cogl-context.c +++ b/clutter/cogl/gles/cogl-context.c @@ -28,69 +28,108 @@ #endif #include "cogl.h" - -#include - #include "cogl-internal.h" #include "cogl-util.h" #include "cogl-context.h" +#include "cogl-texture-private.h" +#include "cogl-material-private.h" #include "cogl-gles2-wrapper.h" +#include + static CoglContext *_context = NULL; gboolean cogl_create_context () { + GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 }; + gulong enable_flags = 0; + if (_context != NULL) return FALSE; - + /* Allocate context memory */ _context = (CoglContext*) g_malloc (sizeof (CoglContext)); - + /* Init default values */ _context->feature_flags = 0; _context->features_cached = FALSE; - + _context->enable_flags = 0; _context->color_alpha = 255; - - _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); - _context->last_path = 0; - - _context->texture_handles = NULL; - _context->texture_vertices = g_array_new (FALSE, FALSE, - sizeof (CoglTextureGLVertex)); - _context->texture_indices = g_array_new (FALSE, FALSE, - sizeof (GLushort)); _context->material_handles = NULL; _context->material_layer_handles = NULL; - _context->source_material = COGL_INVALID_HANDLE; + _context->default_material = cogl_material_new (); + _context->source_material = NULL; + + _context->texture_handles = NULL; + _context->default_gl_texture_2d_tex = COGL_INVALID_HANDLE; + _context->default_gl_texture_rect_tex = COGL_INVALID_HANDLE; + _context->texture_download_material = COGL_INVALID_HANDLE; + + _context->journal = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry)); + _context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat)); + _context->static_indices = g_array_new (FALSE, FALSE, sizeof (GLushort)); + _context->polygon_vertices = g_array_new (FALSE, FALSE, + sizeof (CoglTextureGLVertex)); + + _context->current_material = NULL; + _context->current_material_flags = 0; + _context->current_layers = g_array_new (FALSE, FALSE, + sizeof (CoglLayerInfo)); + _context->n_texcoord_arrays_enabled = 0; _context->fbo_handles = NULL; - _context->program_handles = NULL; - _context->shader_handles = NULL; _context->draw_buffer = COGL_WINDOW_BUFFER; + _context->shader_handles = NULL; + + _context->program_handles = NULL; + _context->vertex_buffer_handles = NULL; - - _context->blend_src_factor = CGL_SRC_ALPHA; - _context->blend_dst_factor = CGL_ONE_MINUS_SRC_ALPHA; + + _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); + _context->last_path = 0; + _context->stencil_material = cogl_material_new (); /* Init the GLES2 wrapper */ #ifdef HAVE_COGL_GLES2 cogl_gles2_wrapper_init (&_context->gles2); #endif - - /* Init OpenGL state */ - GE( glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) ); - GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); - GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); - cogl_enable (0); + /* Initialise the clip stack */ _cogl_clip_stack_state_init (); - + + /* Create default textures used for fall backs */ + _context->default_gl_texture_2d_tex = + cogl_texture_new_from_data (1, /* width */ + 1, /* height */ + -1, /* max waste */ + FALSE, /* auto mipmap */ + COGL_PIXEL_FORMAT_RGBA_8888, /* data format */ + /* internal format */ + COGL_PIXEL_FORMAT_RGBA_8888, + 0, /* auto calc row stride */ + &default_texture_data); + _context->default_gl_texture_rect_tex = + cogl_texture_new_from_data (1, /* width */ + 1, /* height */ + -1, /* max waste */ + FALSE, /* auto mipmap */ + COGL_PIXEL_FORMAT_RGBA_8888, /* data format */ + /* internal format */ + COGL_PIXEL_FORMAT_RGBA_8888, + 0, /* auto calc row stride */ + &default_texture_data); + + cogl_set_source (_context->default_material); + cogl_material_flush_gl_state (_context->source_material, NULL); + enable_flags = + cogl_material_get_cogl_enable_flags (_context->source_material); + cogl_enable (enable_flags); + return TRUE; } @@ -105,15 +144,6 @@ cogl_destroy_context () if (_context->path_nodes) g_array_free (_context->path_nodes, TRUE); -#ifdef HAVE_COGL_GLES2 - cogl_gles2_wrapper_deinit (&_context->gles2); -#endif - - if (_context->texture_vertices) - g_array_free (_context->texture_vertices, TRUE); - if (_context->texture_indices) - g_array_free (_context->texture_indices, TRUE); - if (_context->texture_handles) g_array_free (_context->texture_handles, TRUE); if (_context->fbo_handles) @@ -122,16 +152,36 @@ cogl_destroy_context () g_array_free (_context->shader_handles, TRUE); if (_context->program_handles) g_array_free (_context->program_handles, TRUE); - + + if (_context->default_gl_texture_2d_tex) + cogl_texture_unref (_context->default_gl_texture_2d_tex); + if (_context->default_gl_texture_rect_tex) + cogl_texture_unref (_context->default_gl_texture_rect_tex); + + if (_context->default_material) + cogl_material_unref (_context->default_material); + + if (_context->journal) + g_array_free (_context->journal, TRUE); + if (_context->logged_vertices) + g_array_free (_context->logged_vertices, TRUE); + + if (_context->static_indices) + g_array_free (_context->static_indices, TRUE); + if (_context->polygon_vertices) + g_array_free (_context->polygon_vertices, TRUE); + if (_context->current_layers) + g_array_free (_context->current_layers, TRUE); + g_free (_context); } CoglContext * _cogl_context_get_default () { - /* Create if doesn't exists yet */ + /* Create if doesn't exist yet */ if (_context == NULL) cogl_create_context (); - + return _context; } diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index c1d107270..041587c56 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -41,57 +41,69 @@ typedef struct typedef struct { /* Features cache */ - CoglFeatureFlags feature_flags; - gboolean features_cached; - - /* Enable cache */ - gulong enable_flags; - guint8 color_alpha; - COGLenum blend_src_factor; - COGLenum blend_dst_factor; + CoglFeatureFlags feature_flags; + gboolean features_cached; + + /* Enable cache */ + gulong enable_flags; + guint8 color_alpha; + + gboolean enable_backface_culling; - gboolean enable_backface_culling; - - /* Primitives */ - floatVec2 path_start; - floatVec2 path_pen; - GArray *path_nodes; - guint last_path; - floatVec2 path_nodes_min; - floatVec2 path_nodes_max; - /* Cache of inverse projection matrix */ float inverse_projection[16]; - /* Textures */ - GArray *texture_handles; - GArray *texture_vertices; - GArray *texture_indices; - /* The gl texture number that the above vertices apply to. This to - detect when a different slice is encountered so that the vertices - can be flushed */ - GLuint texture_current; - GLenum texture_target; - GLenum texture_format; - /* Materials */ - GArray *material_handles; - GArray *material_layer_handles; - CoglHandle source_material; + GArray *material_handles; + GArray *material_layer_handles; + CoglHandle default_material; + CoglHandle source_material; + + /* Textures */ + GArray *texture_handles; + CoglHandle default_gl_texture_2d_tex; + CoglHandle default_gl_texture_rect_tex; + CoglHandle texture_download_material; + + /* Batching geometry... */ + /* We journal the texture rectangles we want to submit to OpenGL so + * we have an oppertunity to optimise the final order so that we + * can batch things together. */ + GArray *journal; + GArray *logged_vertices; + GArray *static_indices; + GArray *polygon_vertices; + + /* Some simple caching, to minimize state changes... */ + CoglHandle current_material; + gulong current_material_flags; + GArray *current_layers; + guint n_texcoord_arrays_enabled; /* Framebuffer objects */ - GArray *fbo_handles; - CoglBufferTarget draw_buffer; + GArray *fbo_handles; + CoglBufferTarget draw_buffer; /* Shaders */ - GArray *program_handles; - GArray *shader_handles; + GArray *shader_handles; - /* Vertex buffers */ - GArray *vertex_buffer_handles; + /* Programs */ + GArray *program_handles; /* Clip stack */ - CoglClipStackState clip; + CoglClipStackState clip; + + /* Vertex buffers */ + GArray *vertex_buffer_handles; + + /* Primitives */ + floatVec2 path_start; + floatVec2 path_pen; + GArray *path_nodes; + guint last_path; + floatVec2 path_nodes_min; + floatVec2 path_nodes_max; + CoglHandle stencil_material; #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; @@ -100,7 +112,7 @@ typedef struct supported */ GLint viewport_store[4]; #endif - + } CoglContext; CoglContext * @@ -111,6 +123,6 @@ _cogl_context_get_default (); CoglContext *ctxvar = _cogl_context_get_default (); \ if (ctxvar == NULL) return retval; -#define NO_RETVAL +#define NO_RETVAL #endif /* __COGL_CONTEXT_H */ diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.h b/clutter/cogl/gles/cogl-gles2-wrapper.h index 5cbad279d..f45b8e635 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.h +++ b/clutter/cogl/gles/cogl-gles2-wrapper.h @@ -369,6 +369,11 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program); glGenerateMipmap doesn't need to do anything */ #define cogl_wrap_glGenerateMipmap(x) ((void) 0) +/* GLES doesn't have glDrawRangeElements, so we simply pretend it does + * but that it makes no use of the start, end constraints: */ +#define glDrawRangeElements(mode, start, end, count, type, indices) \ + glDrawElements (mode, count, type, indices) + #endif /* HAVE_COGL_GLES2 */ G_END_DECLS diff --git a/clutter/cogl/gles/cogl-internal.h b/clutter/cogl/gles/cogl-internal.h index c17c5e24f..786e80f84 100644 --- a/clutter/cogl/gles/cogl-internal.h +++ b/clutter/cogl/gles/cogl-internal.h @@ -75,13 +75,10 @@ const char *_cogl_error_string(GLenum errorCode); #endif /* COGL_DEBUG */ #define COGL_ENABLE_BLEND (1<<1) -#define COGL_ENABLE_TEXTURE_2D (1<<2) -#define COGL_ENABLE_ALPHA_TEST (1<<3) -#define COGL_ENABLE_TEXTURE_RECT (1<<4) -#define COGL_ENABLE_VERTEX_ARRAY (1<<5) -#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6) -#define COGL_ENABLE_COLOR_ARRAY (1<<7) -#define COGL_ENABLE_BACKFACE_CULLING (1<<8) +#define COGL_ENABLE_ALPHA_TEST (1<<2) +#define COGL_ENABLE_VERTEX_ARRAY (1<<3) +#define COGL_ENABLE_COLOR_ARRAY (1<<4) +#define COGL_ENABLE_BACKFACE_CULLING (1<<5) gint _cogl_get_format_bpp (CoglPixelFormat format); diff --git a/clutter/cogl/gles/cogl-primitives.c b/clutter/cogl/gles/cogl-primitives.c index dad130f53..b17cd389d 100644 --- a/clutter/cogl/gles/cogl-primitives.c +++ b/clutter/cogl/gles/cogl-primitives.c @@ -38,30 +38,9 @@ #define _COGL_MAX_BEZ_RECURSE_DEPTH 16 -void -_cogl_rectangle (float x, - float y, - float width, - float height) -{ - GLfloat rect_verts[8] = { - (GLfloat) x, (GLfloat) y, - (GLfloat) (x + width), (GLfloat) y, - (GLfloat) x, (GLfloat) (y + height), - (GLfloat) (x + width), (GLfloat) (y + height) - }; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - GE ( glVertexPointer (2, GL_FLOAT, 0, rect_verts ) ); - GE ( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); -} - void _cogl_path_add_node (gboolean new_sub_path, - float x, + float x, float y) { CoglPathNode new_node; @@ -96,22 +75,27 @@ _cogl_path_add_node (gboolean new_sub_path, void _cogl_path_stroke_nodes () { - guint path_start = 0; + guint path_start = 0; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 - ? COGL_ENABLE_BLEND : 0)); + enable_flags |= cogl_material_get_cogl_enable_flags (ctx->source_material); + cogl_enable (enable_flags); + + cogl_material_flush_gl_state (ctx->source_material, + COGL_MATERIAL_FLUSH_DISABLE_MASK, + (guint32)~0, /* disable all texture layers */ + NULL); while (path_start < ctx->path_nodes->len) { CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, path_start); - GE( glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode), - (guchar *) path - + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); GE( glDrawArrays (GL_LINE_STRIP, 0, path->path_size) ); path_start += path->path_size; @@ -145,12 +129,22 @@ _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; + 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; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Just setup a simple material that doesn't use texturing... */ + cogl_material_flush_gl_state (ctx->stencil_material, NULL); + + enable_flags |= + cogl_material_get_cogl_enable_flags (ctx->source_material); + cogl_enable (enable_flags); _cogl_path_get_bounds (nodes_min, nodes_max, &bounds_x, &bounds_y, &bounds_w, &bounds_h); @@ -175,11 +169,9 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, while (path_start < path_size) { - cogl_enable (COGL_ENABLE_VERTEX_ARRAY); - - GE( glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode), - (guchar *) path - + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); GE( glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size) ); if (sub_path_num > 0) @@ -255,7 +247,7 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* clear scanline intersection lists */ - for (i=0; i < bounds_h; i++) + for (i=0; i < bounds_h; i++) scanlines[i]=NULL; first_x = prev_x = (path->x); @@ -444,4 +436,3 @@ _cogl_path_fill_nodes () } } } - diff --git a/clutter/cogl/gles/cogl-texture-private.h b/clutter/cogl/gles/cogl-texture-private.h index 5174c3cde..59bcad108 100644 --- a/clutter/cogl/gles/cogl-texture-private.h +++ b/clutter/cogl/gles/cogl-texture-private.h @@ -28,11 +28,9 @@ #include "cogl-bitmap.h" -typedef struct _CoglTexture CoglTexture; -typedef struct _CoglTexSliceSpan CoglTexSliceSpan; -typedef struct _CoglSpanIter CoglSpanIter; -typedef struct _CoglMultiTexture CoglMultiTexture; -typedef struct _CoglMultiTextureLayer CoglMultiTextureLayer; +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; struct _CoglTexSliceSpan { @@ -61,26 +59,23 @@ struct _CoglTexture gboolean auto_mipmap; }; -struct _CoglMultiTextureLayer +/* To improve batching of geometry when submitting vertices to OpenGL we + * log the texture rectangles we want to draw to a journal, so when we + * later flush the journal we aim to batch data, and gl draw calls. */ +typedef struct _CoglJournalEntry { - guint ref_count; - guint index; /*!< lowest index is blended first then others - on top */ - CoglTexture *tex; /*!< The texture for this layer, or NULL - for an empty layer */ - - /* TODO: Add more control over the texture environment for each texture - * unit. For example we should support dot3 normal mapping. */ -}; - -struct _CoglMultiTexture -{ - guint ref_count; - GList *layers; -}; + CoglHandle material; + gint n_layers; + guint32 fallback_mask; + GLuint layer0_override_texture; +} CoglJournalEntry; CoglTexture* _cogl_texture_pointer_from_handle (CoglHandle handle); -#endif /* __COGL_TEXTURE_H */ +gboolean +_cogl_texture_span_has_waste (CoglTexture *tex, + gint x_span_index, + gint y_span_index); +#endif /* __COGL_TEXTURE_H */ diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index e461dbcf0..ec51c6382 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -53,6 +53,25 @@ printf("err: 0x%x\n", err); \ } */ +#ifdef HAVE_COGL_GL + +#define glDrawRangeElements ctx->pf_glDrawRangeElements + +#else + +/* GLES doesn't have glDrawRangeElements, so we simply pretend it does + * but that it makes no use of the start, end constraints: */ +#define glDrawRangeElements(mode, start, end, count, type, indices) \ + glDrawElements (mode, count, type, indices) + +#endif + +static void _cogl_journal_flush (void); + +static void _cogl_texture_free (CoglTexture *tex); + +COGL_HANDLE_DEFINE (Texture, texture, texture_handles); + struct _CoglSpanIter { gint index; @@ -70,10 +89,6 @@ struct _CoglSpanIter gboolean intersects; }; -static void _cogl_texture_free (CoglTexture *tex); - -COGL_HANDLE_DEFINE (Texture, texture, texture_handles); - static void _cogl_texture_bitmap_free (CoglTexture *tex) { @@ -383,119 +398,81 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) static void _cogl_texture_draw_and_read (CoglTexture *tex, CoglBitmap *target_bmp, - CoglColor *back_color, GLint *viewport) { - gint bpp; + gint bpp; float rx1, ry1; float rx2, ry2; float tx1, ty1; float tx2, ty2; - int bw, bh; - CoglBitmap rect_bmp; - CoglHandle handle; + int bw, bh; + CoglBitmap rect_bmp; + CoglHandle handle; handle = _cogl_texture_handle_from_pointer (tex); bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); - /* If whole image fits into the viewport and target buffer - has got no special rowstride, we can do it in one pass */ - if (tex->bitmap.width < viewport[2] - viewport[0] && - tex->bitmap.height < viewport[3] - viewport[1] && - tex->bitmap.rowstride == bpp * tex->bitmap.width) + ry1 = 0; ry2 = 0; + ty1 = 0; ty2 = 0; + + /* Walk Y axis until whole bitmap height consumed */ + for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) { - /* Clear buffer with transparent black, draw with white - for direct copy to framebuffer */ - cogl_paint_init (back_color); + /* Rectangle Y coords */ + ry1 = ry2; + ry2 += (bh < viewport[3]) ? bh : viewport[3]; - /* Draw the texture image */ - cogl_texture_rectangle (handle, - 0, 0, - (float)(tex->bitmap.width), - (float)(tex->bitmap.height), - 0, 0, 1.0, 1.0); + /* Normalized texture Y coords */ + ty1 = ty2; + ty2 = (ry2 / (float)tex->bitmap.height); - /* Read into target bitmap */ - prep_for_gl_pixels_download (tex->bitmap.rowstride); - GE( glReadPixels (viewport[0], viewport[1], - tex->bitmap.width, - tex->bitmap.height, - GL_RGBA, GL_UNSIGNED_BYTE, - target_bmp->data) ); - } - else - { - ry1 = 0; ry2 = 0; - ty1 = 0; ty2 = 0; + rx1 = 0; rx2 = 0; + tx1 = 0; tx2 = 0; -#define CFIX (float) - - /* Walk Y axis until whole bitmap height consumed */ - for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) + /* Walk X axis until whole bitmap width consumed */ + for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) { - /* Rectangle Y coords */ - ry1 = ry2; - ry2 += (bh < viewport[3]) ? bh : viewport[3]; + /* Rectangle X coords */ + rx1 = rx2; + rx2 += (bw < viewport[2]) ? bw : viewport[2]; - /* Normalized texture Y coords */ - ty1 = ty2; - ty2 = (CFIX (ry2) / CFIX (tex->bitmap.height)); + /* Normalized texture X coords */ + tx1 = tx2; + tx2 = (rx2 / (float)tex->bitmap.width); - rx1 = 0; rx2 = 0; - tx1 = 0; tx2 = 0; + /* Draw a portion of texture */ + cogl_rectangle_with_texture_coords (0, 0, + rx2 - rx1, + ry2 - ry1, + tx1, ty1, + tx2, ty2); - /* Walk X axis until whole bitmap width consumed */ - for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) - { - /* Rectangle X coords */ - rx1 = rx2; - rx2 += (bw < viewport[2]) ? bw : viewport[2]; + /* Read into a temporary bitmap */ + rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; + rect_bmp.width = rx2 - rx1; + rect_bmp.height = ry2 - ry1; + rect_bmp.rowstride = bpp * rect_bmp.width; + rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * + rect_bmp.height); - /* Normalized texture X coords */ - tx1 = tx2; - tx2 = (CFIX (rx2) / CFIX (tex->bitmap.width)); + prep_for_gl_pixels_download (rect_bmp.rowstride); + GE( glReadPixels (viewport[0], viewport[1], + rect_bmp.width, + rect_bmp.height, + GL_RGBA, GL_UNSIGNED_BYTE, + rect_bmp.data) ); - /* Clear buffer with transparent black, draw with white - for direct copy to framebuffer */ - cogl_paint_init (back_color); + /* Copy to target bitmap */ + _cogl_bitmap_copy_subregion (&rect_bmp, + target_bmp, + 0,0, + rx1,ry1, + rect_bmp.width, + rect_bmp.height); - /* Draw a portion of texture */ - cogl_texture_rectangle (handle, - 0, 0, - CFIX (rx2 - rx1), - CFIX (ry2 - ry1), - tx1, ty1, - tx2, ty2); - - /* Read into a temporary bitmap */ - rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; - rect_bmp.width = rx2 - rx1; - rect_bmp.height = ry2 - ry1; - rect_bmp.rowstride = bpp * rect_bmp.width; - rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * - rect_bmp.height); - - prep_for_gl_pixels_download (rect_bmp.rowstride); - GE( glReadPixels (viewport[0], viewport[1], - rect_bmp.width, - rect_bmp.height, - GL_RGBA, GL_UNSIGNED_BYTE, - rect_bmp.data) ); - - /* Copy to target bitmap */ - _cogl_bitmap_copy_subregion (&rect_bmp, - target_bmp, - 0,0, - rx1,ry1, - rect_bmp.width, - rect_bmp.height); - - /* Free temp bitmap */ - g_free (rect_bmp.data); - } + /* Free temp bitmap */ + g_free (rect_bmp.data); } - -#undef CFIX } } @@ -507,14 +484,10 @@ _cogl_texture_download_from_gl (CoglTexture *tex, { gint bpp; GLint viewport[4]; - CoglColor cwhite; CoglBitmap alpha_bmp; - COGLenum old_src_factor; - COGLenum old_dst_factor; _COGL_GET_CONTEXT (ctx, FALSE); - cogl_color_set_from_4ub (&cwhite, 0xff, 0xff, 0xff, 0xff); bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); @@ -545,15 +518,37 @@ _cogl_texture_download_from_gl (CoglTexture *tex, /* Draw to all channels */ cogl_draw_buffer (COGL_WINDOW_BUFFER | COGL_MASK_BUFFER, 0); - /* Store old blending factors */ - old_src_factor = ctx->blend_src_factor; - old_dst_factor = ctx->blend_dst_factor; - /* Direct copy operation */ - cogl_set_source_color (&cwhite); - cogl_blend_func (CGL_ONE, CGL_ZERO); - _cogl_texture_draw_and_read (tex, target_bmp, - &cwhite, viewport); + + if (ctx->texture_download_material == COGL_INVALID_HANDLE) + { + ctx->texture_download_material = cogl_material_new (); + cogl_material_set_layer_combine_function ( + ctx->texture_download_material, + 0, /* layer */ + COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, + COGL_MATERIAL_LAYER_COMBINE_FUNC_REPLACE); + cogl_material_set_layer_combine_arg_src ( + ctx->texture_download_material, + 0, /* layer */ + 0, /* arg */ + COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, + COGL_MATERIAL_LAYER_COMBINE_SRC_TEXTURE); + cogl_material_set_blend_factors (ctx->texture_download_material, + COGL_MATERIAL_BLEND_FACTOR_ONE, + COGL_MATERIAL_BLEND_FACTOR_ZERO); + } + + cogl_material_set_layer (ctx->texture_download_material, 0, tex); + + cogl_material_set_layer_combine_arg_op ( + ctx->texture_download_material, + 0, /* layer */ + 0, /* arg */ + COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, + COGL_MATERIAL_LAYER_COMBINE_OP_SRC_COLOR); + cogl_material_flush_gl_state (ctx->texture_download_material, NULL); + _cogl_texture_draw_and_read (tex, target_bmp, viewport); /* Check whether texture has alpha and framebuffer not */ /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer @@ -585,9 +580,14 @@ _cogl_texture_download_from_gl (CoglTexture *tex, alpha_bmp.height); /* Draw alpha values into RGB channels */ - cogl_blend_func (CGL_ZERO, CGL_SRC_ALPHA); - _cogl_texture_draw_and_read (tex, &alpha_bmp, - &cwhite, viewport); + cogl_material_set_layer_combine_arg_op ( + ctx->texture_download_material, + 0, /* layer */ + 0, /* arg */ + COGL_MATERIAL_LAYER_COMBINE_CHANNELS_RGB, + COGL_MATERIAL_LAYER_COMBINE_OP_SRC_ALPHA); + cogl_material_flush_gl_state (ctx->texture_download_material, NULL); + _cogl_texture_draw_and_read (tex, &alpha_bmp, viewport); /* Copy temp R to target A */ srcdata = alpha_bmp.data; @@ -615,7 +615,6 @@ _cogl_texture_download_from_gl (CoglTexture *tex, glPopMatrix (); cogl_draw_buffer (COGL_WINDOW_BUFFER, 0); - cogl_blend_func (old_src_factor, old_dst_factor); return TRUE; } @@ -952,6 +951,33 @@ _cogl_texture_size_supported (GLenum gl_target, return TRUE; } +static void +_cogl_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex->wrap_mode != wrap_mode) + { + int i; + + /* Any queued texture rectangles may be depending on the previous + * wrap mode... */ + _cogl_journal_flush (); + + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GLuint texnum = g_array_index (tex->slice_gl_handles, GLuint, i); + + GE( glBindTexture (tex->gl_target, texnum) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); + } + + tex->wrap_mode = wrap_mode; + } +} + static gboolean _cogl_texture_slices_create (CoglTexture *tex) { @@ -1079,13 +1105,8 @@ _cogl_texture_slices_create (CoglTexture *tex) g_array_set_size (tex->slice_gl_handles, n_slices); - - /* Hardware repeated tiling if supported, else tile in software*/ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && n_slices == 1) - tex->wrap_mode = GL_REPEAT; - else - tex->wrap_mode = GL_CLAMP_TO_EDGE; + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; /* Generate a "working set" of GL texture objects * (some implementations might supported faster @@ -1159,6 +1180,20 @@ _cogl_texture_slices_free (CoglTexture *tex) } } +gboolean +_cogl_texture_span_has_waste (CoglTexture *tex, + gint x_span_index, + gint y_span_index) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x_span_index); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y_span_index); + + return (x_span->waste || y_span->waste) ? TRUE : FALSE; +} + static gboolean _cogl_pixel_format_from_gl_internal (GLenum gl_int_format, CoglPixelFormat *out_format) @@ -1301,11 +1336,11 @@ _cogl_texture_free (CoglTexture *tex) } CoglHandle -cogl_texture_new_with_size (guint width, - guint height, - gint max_waste, - CoglTextureFlags flags, - CoglPixelFormat internal_format) +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglTextureFlags flags, + CoglPixelFormat internal_format) { CoglTexture *tex; gint bpp; @@ -1497,10 +1532,10 @@ cogl_texture_new_from_bitmap (CoglBitmap *bmp, CoglHandle cogl_texture_new_from_file (const gchar *filename, - gint max_waste, + gint max_waste, CoglTextureFlags flags, - CoglPixelFormat internal_format, - GError **error) + CoglPixelFormat internal_format, + GError **error) { CoglBitmap *bmp; CoglHandle handle; @@ -1647,6 +1682,9 @@ cogl_texture_new_from_foreign (GLuint gl_handle, tex->mag_filter = gl_mag_filter; tex->max_waste = 0; + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + /* Create slice arrays */ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, @@ -1673,23 +1711,6 @@ cogl_texture_new_from_foreign (GLuint gl_handle, g_array_append_val (tex->slice_gl_handles, gl_handle); - /* Force appropriate wrap parameter */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && - gl_target == GL_TEXTURE_2D) - { - /* Hardware repeated tiling */ - tex->wrap_mode = GL_REPEAT; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) ); - } - else - { - /* Any tiling will be done in software */ - tex->wrap_mode = GL_CLAMP_TO_EDGE; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); - } - return _cogl_texture_handle_new (tex); } @@ -2045,124 +2066,313 @@ cogl_texture_get_data (CoglHandle handle, return byte_size; } + +/****************************************************************************** + * XXX: Here ends the code that strictly implements "CoglTextures". + * + * The following consists of code for rendering rectangles and polygons. It + * might be neater to move this code somewhere else. I think everything below + * here should be implementable without access to CoglTexture internals, but + * that will at least mean exposing the cogl_span_iter_* funcs. + */ + static void -_cogl_texture_flush_vertices (void) +_cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start, + gint batch_len, + GLfloat *vertex_pointer) { + int needed_indices; + gsize stride; + int i; + gulong enable_flags = 0; + guint32 disable_mask; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (ctx->texture_vertices->len > 0) + /* The indices are always the same sequence regardless of the vertices so we + * only need to change it if there are more vertices than ever before. */ + needed_indices = batch_len * 6; + if (needed_indices > ctx->static_indices->len) { - int needed_indices; - CoglTextureGLVertex *p - = (CoglTextureGLVertex *) ctx->texture_vertices->data; + int old_len = ctx->static_indices->len; + int vert_num = old_len / 6 * 4; + GLushort *q; - /* The indices are always the same sequence regardless of the - vertices so we only need to change it if there are more - vertices than ever before */ - needed_indices = ctx->texture_vertices->len / 4 * 6; - if (needed_indices > ctx->texture_indices->len) + /* Add two triangles for each quad to the list of + indices. That makes six new indices but two of the + vertices in the triangles are shared. */ + g_array_set_size (ctx->static_indices, needed_indices); + q = &g_array_index (ctx->static_indices, GLushort, old_len); + + for (i = old_len; + i < ctx->static_indices->len; + i += 6, vert_num += 4) { - int old_len = ctx->texture_indices->len; - int vert_num = old_len / 6 * 4; - int i; - GLushort *q; + *(q++) = vert_num + 0; + *(q++) = vert_num + 1; + *(q++) = vert_num + 3; - /* Add two triangles for each quad to the list of - indices. That makes six new indices but two of the - vertices in the triangles are shared. */ - g_array_set_size (ctx->texture_indices, needed_indices); - q = &g_array_index (ctx->texture_indices, GLushort, old_len); - - for (i = old_len; - i < ctx->texture_indices->len; - i += 6, vert_num += 4) - { - *(q++) = vert_num + 0; - *(q++) = vert_num + 1; - *(q++) = vert_num + 3; - - *(q++) = vert_num + 1; - *(q++) = vert_num + 2; - *(q++) = vert_num + 3; - } + *(q++) = vert_num + 1; + *(q++) = vert_num + 2; + *(q++) = vert_num + 3; } - - GE( glVertexPointer (2, GL_FLOAT, - sizeof (CoglTextureGLVertex), p->v ) ); - GE( glTexCoordPointer (2, GL_FLOAT, - sizeof (CoglTextureGLVertex), p->t ) ); - - GE( cogl_gles2_wrapper_bind_texture (ctx->texture_target, - ctx->texture_current, - ctx->texture_format) ); - GE( glDrawElements (GL_TRIANGLES, - needed_indices, - GL_UNSIGNED_SHORT, - ctx->texture_indices->data) ); - - g_array_set_size (ctx->texture_vertices, 0); } + + /* XXX NB: + * Our vertex data is arranged as follows: + * 4 vertices per quad: 2 GLfloats per position, + * 2 GLfloats per tex coord * n_layers + */ + stride = 2 + 2 * batch_start->n_layers; + stride *= sizeof (GLfloat); + + disable_mask = (1 << batch_start->n_layers) - 1; + disable_mask = ~disable_mask; + + cogl_material_flush_gl_state (ctx->source_material, + COGL_MATERIAL_FLUSH_FALLBACK_MASK, + batch_start->fallback_mask, + COGL_MATERIAL_FLUSH_DISABLE_MASK, + disable_mask, + /* Redundant when dealing with unsliced + * textures but does no harm... */ + COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, + batch_start->layer0_override_texture, + NULL); + + for (i = 0; i < batch_start->n_layers; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); + GE (glTexCoordPointer (2, GL_FLOAT, stride, vertex_pointer + 2 + 2 * i)); + } + /* XXX: Without this we get a segfault with the PVR SDK. + * We should probably be doing this for cogl/gl too though. */ + for (; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = i + 1; + + /* FIXME: This api is a bit yukky, ideally it will be removed if we + * re-work the cogl_enable mechanism */ + enable_flags |= cogl_material_get_cogl_enable_flags (ctx->source_material); + + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + enable_flags |= COGL_ENABLE_VERTEX_ARRAY; + cogl_enable (enable_flags); + + GE (glVertexPointer (2, GL_FLOAT, stride, vertex_pointer)); + + GE (glDrawRangeElements (GL_TRIANGLES, + 0, ctx->static_indices->len - 1, + 6 * batch_len, + GL_UNSIGNED_SHORT, + ctx->static_indices->data)); } static void -_cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - GLfloat tx1, GLfloat ty1, - GLfloat tx2, GLfloat ty2) +_cogl_journal_flush (void) { - CoglTextureGLVertex *p; - GLushort first_vert; + GLfloat *current_vertex_pointer; + GLfloat *batch_vertex_pointer; + CoglJournalEntry *batch_start; + guint batch_len; + int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Add the four vertices of the quad to the list of queued - vertices */ - first_vert = ctx->texture_vertices->len; - g_array_set_size (ctx->texture_vertices, first_vert + 4); - p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, first_vert); + if (ctx->journal->len == 0) + return; - p->v[0] = x1; p->v[1] = y1; - p->t[0] = tx1; p->t[1] = ty1; - p++; - p->v[0] = x1; p->v[1] = y2; - p->t[0] = tx1; p->t[1] = ty2; - p++; - p->v[0] = x2; p->v[1] = y2; - p->t[0] = tx2; p->t[1] = ty2; - p++; - p->v[0] = x2; p->v[1] = y1; - p->t[0] = tx2; p->t[1] = ty1; - p++; + /* Current non-variables / constraints: + * + * - We don't have to worry about much GL state changing between journal + * entries since currently the journal never out lasts a single call to + * _cogl_multitexture_multiple_rectangles. So the user doesn't get the + * chance to fiddle with anything. (XXX: later this will be extended at + * which point we can start logging certain state changes) + * + * - Implied from above: all entries will refer to the same material. + * + * - Although _cogl_multitexture_multiple_rectangles can cause the wrap mode + * of textures to be modified, the journal is flushed if a wrap mode is + * changed so we don't currently have to log wrap mode changes. + * + * - XXX - others? + */ + + /* TODO: "compile" the journal to find ways of batching draw calls and vertex + * data. + * + * Simple E.g. given current constraints... + * pass 0 - load all data into a single CoglVertexBuffer + * pass 1 - batch gl draw calls according to entries that use the same + * textures. + * + * We will be able to do cooler stuff here when we extend the life of + * journals beyond _cogl_multitexture_multiple_rectangles. + */ + + batch_vertex_pointer = (GLfloat *)ctx->logged_vertices->data; + batch_start = (CoglJournalEntry *)ctx->journal->data; + batch_len = 1; + + current_vertex_pointer = batch_vertex_pointer; + + for (i = 1; i < ctx->journal->len; i++) + { + CoglJournalEntry *prev_entry = + &g_array_index (ctx->journal, CoglJournalEntry, i - 1); + CoglJournalEntry *current_entry = prev_entry + 1; + gsize stride; + + /* Progress the vertex pointer */ + stride = 2 + current_entry->n_layers * 2; + current_vertex_pointer += stride; + +#warning "NB: re-enable batching" +#if 1 + /* batch rectangles using the same textures */ + if (current_entry->material == prev_entry->material && + current_entry->n_layers == prev_entry->n_layers && + current_entry->fallback_mask == prev_entry->fallback_mask && + current_entry->layer0_override_texture + == prev_entry->layer0_override_texture) + { + batch_len++; + continue; + } +#endif + + _cogl_journal_flush_quad_batch (batch_start, + batch_len, + batch_vertex_pointer); + + batch_start = current_entry; + batch_len = 1; + batch_vertex_pointer = current_vertex_pointer; + } + + /* The last batch... */ + _cogl_journal_flush_quad_batch (batch_start, + batch_len, + batch_vertex_pointer); + + + g_array_set_size (ctx->journal, 0); + g_array_set_size (ctx->logged_vertices, 0); } static void -_cogl_texture_quad_sw (CoglTexture *tex, - float x1, - float y1, - float x2, - float y2, - float tx1, - float ty1, - float tx2, - float ty2) +_cogl_journal_log_quad (float x1, + float y1, + float x2, + float y2, + CoglHandle material, + gint n_layers, + guint32 fallback_mask, + GLuint layer0_override_texture, + float *tex_coords, + guint tex_coords_len) { - CoglSpanIter iter_x , iter_y; - float tw , th; - float tqx , tqy; - float first_tx , first_ty; - float first_qx , first_qy; - float slice_tx1 , slice_ty1; - float slice_tx2 , slice_ty2; - float slice_qx1 , slice_qy1; - float slice_qx2 , slice_qy2; - GLuint gl_handle; + int stride; + int next_vert; + GLfloat *v; + int i; + int next_entry; + CoglJournalEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* The vertex data is logged into a seperate array in a layout that can be + * directly passed to OpenGL + */ + + /* We pack the vertex data as 2 (x,y) GLfloats folowed by 2 (tx,ty) GLfloats + * for each texture being used, E.g.: + * [X, Y, TX0, TY0, TX1, TY1, X, Y, TX0, TY0, X, Y, ...] + */ + stride = 2 + n_layers * 2; + + next_vert = ctx->logged_vertices->len; + g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride); + v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); + + /* XXX: All the jumping around to fill in this strided buffer doesn't + * seem ideal. */ + + /* XXX: we could defer expanding the vertex data for GL until we come + * to flushing the journal. */ + + v[0] = x1; v[1] = y1; + v += stride; + v[0] = x1; v[1] = y2; + v += stride; + v[0] = x2; v[1] = y2; + v += stride; + v[0] = x2; v[1] = y1; + + for (i = 0; i < n_layers; i++) + { + GLfloat *t = + &g_array_index (ctx->logged_vertices, GLfloat, next_vert + 2 + 2 * i); + + t[0] = tex_coords[0]; t[1] = tex_coords[1]; + t += stride; + t[0] = tex_coords[0]; t[1] = tex_coords[3]; + t += stride; + t[0] = tex_coords[2]; t[1] = tex_coords[3]; + t += stride; + t[0] = tex_coords[2]; t[1] = tex_coords[1]; + } + + next_entry = ctx->journal->len; + g_array_set_size (ctx->journal, next_entry + 1); + entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry); + + entry->material = material; + entry->n_layers = n_layers; + entry->fallback_mask = fallback_mask; + entry->layer0_override_texture = layer0_override_texture; +} + +static void +_cogl_texture_sliced_quad (CoglTexture *tex, + CoglHandle material, + float x1, + float y1, + float x2, + float y2, + float tx1, + float ty1, + float tx2, + float ty2) +{ + CoglSpanIter iter_x , iter_y; + float tw , th; + float tqx , tqy; + float first_tx , first_ty; + float first_qx , first_qy; + float slice_tx1 , slice_ty1; + float slice_tx2 , slice_ty2; + float slice_qx1 , slice_qy1; + float slice_qx2 , slice_qy2; + GLuint gl_handle; _COGL_GET_CONTEXT (ctx, NO_RETVAL); #if COGL_DEBUG - printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); + printf("=== Drawing Tex Quad (Sliced Mode) ===\n"); #endif + /* We can't use hardware repeat so we need to set clamp to edge + otherwise it might pull in edge pixels from the other side */ + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be flipped but we can still use the same algorithm to iterate the @@ -2216,6 +2426,8 @@ _cogl_texture_quad_sw (CoglTexture *tex, !_cogl_span_iter_end (&iter_y) ; _cogl_span_iter_next (&iter_y) ) { + float tex_coords[4]; + /* Discard slices out of quad early */ if (!iter_y.intersects) continue; @@ -2228,8 +2440,6 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - /* Normalize texture coordinates to current slice - (rectangle texture targets take denormalized) */ slice_ty1 /= iter_y.span->size; slice_ty2 /= iter_y.span->size; @@ -2273,156 +2483,350 @@ _cogl_texture_quad_sw (CoglTexture *tex, iter_y.index * iter_x.array->len + iter_x.index); - /* If we're using a different texture from the one already queued - then flush the vertices */ - if (ctx->texture_vertices->len > 0 - && gl_handle != ctx->texture_current) - _cogl_texture_flush_vertices (); - ctx->texture_target = tex->gl_target; - ctx->texture_current = gl_handle; - ctx->texture_format = tex->gl_intformat; - - _cogl_texture_add_quad_vertices ( (slice_qx1), - (slice_qy1), - (slice_qx2), - (slice_qy2), - (slice_tx1), - (slice_ty1), - (slice_tx2), - (slice_ty2)); + tex_coords[0] = slice_tx1; + tex_coords[1] = slice_ty1; + tex_coords[2] = slice_tx2; + tex_coords[3] = slice_ty2; + _cogl_journal_log_quad (slice_qx1, + slice_qy1, + slice_qx2, + slice_qy2, + material, + 1, /* one layer */ + 0, /* don't need to use fallbacks */ + gl_handle, /* replace the layer0 texture */ + tex_coords, + 4); } } } -static void -_cogl_texture_quad_hw (CoglTexture *tex, - float x1, - float y1, - float x2, - float y2, - float tx1, - float ty1, - float tx2, - float ty2) +static gboolean +_cogl_multitexture_unsliced_quad (float x1, + float y1, + float x2, + float y2, + CoglHandle material, + gint n_layers, + guint32 fallback_mask, + const float *user_tex_coords, + gint user_tex_coords_len) { - GLuint gl_handle; - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; + float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); + const GList *layers; + GList *tmp; + int i; -#if COGL_DEBUG - printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); -#endif + _COGL_GET_CONTEXT (ctx, FALSE); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* + * Validate the texture coordinates for this rectangle. + */ + layers = cogl_material_get_layers (material); + for (tmp = (GList *)layers, i = 0; tmp != NULL; tmp = tmp->next, i++) + { + CoglHandle layer = (CoglHandle)tmp->data; + /* CoglLayerInfo *layer_info; */ + CoglHandle tex_handle; + CoglTexture *tex; + const float *in_tex_coords; + float *out_tex_coords; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; - /* Pick and bind opengl texture object */ - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + /* layer_info = &layers[i]; */ - /* If we're using a different texture from the one already queued - then flush the vertices */ - if (ctx->texture_vertices->len > 0 - && gl_handle != ctx->texture_current) - _cogl_texture_flush_vertices (); - ctx->texture_target = tex->gl_target; - ctx->texture_current = gl_handle; - ctx->texture_format = tex->gl_intformat; + /* FIXME - we shouldn't be checking this stuff if layer_info->gl_texture + * already == 0 */ - /* Don't include the waste in the texture coordinates */ - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tex_handle = cogl_material_layer_get_texture (layer); + tex = _cogl_texture_pointer_from_handle (tex_handle); - /* Don't include the waste in the texture coordinates */ - tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size; - tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size; - ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; - ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + in_tex_coords = &user_tex_coords[i * 4]; + out_tex_coords = &final_tex_coords[i * 4]; - _cogl_texture_add_quad_vertices ( (x1), - (y1), - (x2), - (y2), - (tx1), - (ty1), - (tx2), - (ty2)); + + /* If the texture has waste or we are using GL_TEXTURE_RECT we + * can't handle texture repeating so we check that the texture + * coords lie in the range [0,1]. + * + * NB: We already know that no texture matrix is being used + * if the texture has waste since we validated that early on. + * TODO: check for a texture matrix in the GL_TEXTURE_RECT + * case. + */ + if (_cogl_texture_span_has_waste (tex, 0, 0) + && (in_tex_coords[0] < 0 || in_tex_coords[0] > 1.0 + || in_tex_coords[1] < 0 || in_tex_coords[1] > 1.0 + || in_tex_coords[2] < 0 || in_tex_coords[2] > 1.0 + || in_tex_coords[3] < 0 || in_tex_coords[3] > 1.0)) + { + if (i == 0) + { + if (n_layers > 1) + { + g_warning ("Skipping layers 1..n of your material since the " + "first layer has waste and you supplied texture " + "coordinates outside the range [0,1]. We don't " + "currently support any multi-texturing using " + "textures with waste when repeating is " + "necissary so we are falling back to sliced " + "textures assuming layer 0 is the most " + "important one keep"); + } + return FALSE; + } + else + { + g_warning ("Skipping layer %d of your material " + "consisting of a texture with waste since " + "you have supplied texture coords outside " + "the range [0,1] (unsupported when " + "multi-texturing)", i); + + /* NB: marking for fallback will replace the layer with + * a default transparent texture */ + fallback_mask |= (1 << i); + } + } + + + /* + * Setup the texture unit... + */ + + /* NB: The user might not have supplied texture coordinates for all + * layers... */ + if (i < (user_tex_coords_len / 4)) + { + GLenum wrap_mode; + + /* If the texture coords are all in the range [0,1] then we want to + clamp the coords to the edge otherwise it can pull in edge pixels + from the wrong side when scaled */ + if (in_tex_coords[0] >= 0 && in_tex_coords[0] <= 1.0 + && in_tex_coords[1] >= 0 && in_tex_coords[1] <= 1.0 + && in_tex_coords[2] >= 0 && in_tex_coords[2] <= 1.0 + && in_tex_coords[3] >= 0 && in_tex_coords[3] <= 1.0) + wrap_mode = GL_CLAMP_TO_EDGE; + else + wrap_mode = GL_REPEAT; + + _cogl_texture_set_wrap_mode_parameter (tex, wrap_mode); + } + else + { + out_tex_coords[0] = 0; /* tx1 */ + out_tex_coords[1] = 0; /* ty1 */ + out_tex_coords[2] = 1.0; /* tx2 */ + out_tex_coords[3] = 1.0; /* ty2 */ + + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + } + + /* Don't include the waste in the texture coordinates */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + + out_tex_coords[0] = + in_tex_coords[0] * (x_span->size - x_span->waste) / x_span->size; + out_tex_coords[1] = + in_tex_coords[1] * (x_span->size - x_span->waste) / x_span->size; + out_tex_coords[2] = + in_tex_coords[2] * (y_span->size - y_span->waste) / y_span->size; + out_tex_coords[3] = + in_tex_coords[3] * (y_span->size - y_span->waste) / y_span->size; + } + + _cogl_journal_log_quad (x1, + y1, + x2, + y2, + material, + n_layers, + fallback_mask, + 0, /* don't replace the layer0 texture */ + final_tex_coords, + n_layers * 4); + + return TRUE; } -void -cogl_texture_multiple_rectangles (CoglHandle handle, - const float *verts, - guint n_rects) +struct _CoglMutiTexturedRect { - CoglTexture *tex; - gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY - | COGL_ENABLE_TEXTURE_2D); + float x1; + float y1; + float x2; + float y2; + const float *tex_coords; + gint tex_coords_len; +}; + +static void +_cogl_rectangles_with_multitexture_coords ( + struct _CoglMutiTexturedRect *rects, + gint n_rects) +{ + CoglHandle material; + const GList *layers; + int n_layers; + const GList *tmp; + guint32 fallback_mask = 0; + gboolean all_use_sliced_quad_fallback = FALSE; + int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Check if valid texture */ - if (!cogl_is_texture (handle)) - return; - cogl_clip_ensure (); - tex = _cogl_texture_pointer_from_handle (handle); + material = ctx->source_material; - /* Make sure we got stuff to draw */ - if (tex->slice_gl_handles == NULL) - return; + layers = cogl_material_get_layers (material); + n_layers = g_list_length ((GList *)layers); - if (tex->slice_gl_handles->len == 0) - return; + /* + * Validate all the layers of the current source material... + */ - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - enable_flags |= COGL_ENABLE_BLEND; - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); - - g_array_set_size (ctx->texture_vertices, 0); - - while (n_rects-- > 0) + for (tmp = layers, i = 0; tmp != NULL; tmp = tmp->next, i++) { - if (verts[4] != verts[6] && verts[5] != verts[7]) - { - /* If there is only one GL texture and either the texture is - NPOT (no waste) or all of the coordinates are in the - range [0,1] then we can use hardware tiling */ - if (tex->slice_gl_handles->len == 1 - && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && tex->gl_target == GL_TEXTURE_2D) - || (verts[4] >= 0 && verts[4] <= 1.0 - && verts[6] >= 0 && verts[6] <= 1.0 - && verts[5] >= 0 && verts[5] <= 1.0 - && verts[7] >= 0 && verts[7] <= 1.0))) - _cogl_texture_quad_hw (tex, verts[0],verts[1], verts[2],verts[3], - verts[4],verts[5], verts[6],verts[7]); - else - _cogl_texture_quad_sw (tex, verts[0],verts[1], verts[2],verts[3], - verts[4],verts[5], verts[6],verts[7]); - } + CoglHandle layer = tmp->data; + CoglHandle tex_handle = cogl_material_layer_get_texture (layer); + CoglTexture *texture = _cogl_texture_pointer_from_handle (tex_handle); + gulong flags; - verts += 8; + if (cogl_material_layer_get_type (layer) + != COGL_MATERIAL_LAYER_TYPE_TEXTURE) + continue; + + /* XXX: + * For now, if the first layer is sliced then all other layers are + * ignored since we currently don't support multi-texturing with + * sliced textures. If the first layer is not sliced then any other + * layers found to be sliced will be skipped. (with a warning) + * + * TODO: Add support for multi-texturing rectangles with sliced + * textures if no texture matrices are in use. + */ + if (cogl_texture_is_sliced (tex_handle)) + { + if (i == 0) + { + fallback_mask = ~1; /* fallback all except the first layer */ + all_use_sliced_quad_fallback = TRUE; + if (tmp->next) + { + g_warning ("Skipping layers 1..n of your material since the " + "first layer is sliced. We don't currently " + "support any multi-texturing with sliced " + "textures but assume layer 0 is the most " + "important to keep"); + } + break; + } + else + { + g_warning ("Skipping layer %d of your material consisting of a " + "sliced texture (unsuported for multi texturing)", + i); + + /* NB: marking for fallback will replace the layer with + * a default transparent texture */ + fallback_mask |= (1 << i); + continue; + } + } + + /* We don't support multi texturing using textures with any waste if the + * user has supplied a custom texture matrix, since we don't know if + * the result will end up trying to texture from the waste area. */ + flags = cogl_material_layer_get_flags (layer); + if (flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX + && _cogl_texture_span_has_waste (texture, 0, 0)) + { + static gboolean shown_warning = FALSE; + if (!shown_warning) + g_warning ("Skipping layer %d of your material consisting of a " + "texture with waste since you have supplied a custom " + "texture matrix and the result may try to sample from " + "the waste area of your texture.", i); + shown_warning = TRUE; + /* NB: marking for fallback will replace the layer with + * a default transparent texture */ + fallback_mask |= (1 << i); + continue; + } } - _cogl_texture_flush_vertices (); + /* + * Emit geometry for each of the rectangles... + */ + + for (i = 0; i < n_rects; i++) + { + if (all_use_sliced_quad_fallback + || !_cogl_multitexture_unsliced_quad (rects[i].x1, rects[i].y1, + rects[i].x2, rects[i].y2, + material, + n_layers, + fallback_mask, + rects[i].tex_coords, + rects[i].tex_coords_len)) + { + const GList *layers; + CoglHandle tex_handle; + CoglTexture *texture; + + layers = cogl_material_get_layers (material); + tex_handle = + cogl_material_layer_get_texture ((CoglHandle)layers->data); + texture = _cogl_texture_pointer_from_handle (tex_handle); + _cogl_texture_sliced_quad (texture, + material, + rects[i].x1, rects[i].y1, + rects[i].x2, rects[i].y2, + rects[i].tex_coords[0], + rects[i].tex_coords[1], + rects[i].tex_coords[2], + rects[i].tex_coords[3]); + } + } + + _cogl_journal_flush (); } void -cogl_texture_rectangle (CoglHandle handle, - float x1, - float y1, - float x2, - float y2, - float tx1, - float ty1, - float tx2, - float ty2) +cogl_rectangles_with_texture_coords (const float *verts, + guint n_rects) +{ + struct _CoglMutiTexturedRect rects[n_rects]; + int i; + + for (i = 0; i < n_rects; i++) + { + rects[i].x1 = verts[i * 8]; + rects[i].y1 = verts[i * 8 + 1]; + rects[i].x2 = verts[i * 8 + 2]; + rects[i].y2 = verts[i * 8 + 3]; + /* FIXME: rect should be defined to have a const float *geom; + * instead, to avoid this copy + * rect[i].geom = &verts[n_rects * 8]; */ + rects[i].tex_coords = &verts[i * 8 + 4]; + rects[i].tex_coords_len = 4; + } + + _cogl_rectangles_with_multitexture_coords (rects, n_rects); +} + +void +cogl_rectangle_with_texture_coords (float x1, + float y1, + float x2, + float y2, + float tx1, + float ty1, + float tx2, + float ty2) { float verts[8]; @@ -2435,73 +2839,312 @@ cogl_texture_rectangle (CoglHandle handle, verts[6] = tx2; verts[7] = ty2; - cogl_texture_multiple_rectangles (handle, verts, 1); + cogl_rectangles_with_texture_coords (verts, 1); } void -cogl_texture_polygon (CoglHandle handle, - guint n_vertices, - CoglTextureVertex *vertices, - gboolean use_color) +cogl_rectangle_with_multitexture_coords (float x1, + float y1, + float x2, + float y2, + const float *user_tex_coords, + gint user_tex_coords_len) { - CoglTexture *tex; - int i; - GLuint gl_handle; - CoglTexSliceSpan *y_span, *x_span; - gulong enable_flags; - CoglTextureGLVertex *p; + struct _CoglMutiTexturedRect rect; + + rect.x1 = x1; + rect.y1 = y1; + rect.x2 = x2; + rect.y2 = y2; + rect.tex_coords = user_tex_coords; + rect.tex_coords_len = user_tex_coords_len; + + _cogl_rectangles_with_multitexture_coords (&rect, 1); +} + +static void +_cogl_texture_sliced_polygon (CoglTextureVertex *vertices, + guint n_vertices, + guint stride, + gboolean use_color) +{ + const GList *layers; + CoglHandle layer0; + CoglHandle tex_handle; + CoglTexture *tex; + CoglTexSliceSpan *y_span, *x_span; + int x, y, tex_num, i; + GLuint gl_handle; + GLfloat *v; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Check if valid texture */ - if (!cogl_is_texture (handle)) - return; + /* We can assume in this case that we have at least one layer in the + * material that corresponds to a sliced cogl texture */ + layers = cogl_material_get_layers (ctx->source_material); + layer0 = (CoglHandle)layers->data; + tex_handle = cogl_material_layer_get_texture (layer0); + tex = _cogl_texture_pointer_from_handle (tex_handle); + + v = (GLfloat *)ctx->logged_vertices->data; + for (i = 0; i < n_vertices; i++) + { + GLfloat *c; + + v[0] = vertices[i].x; + v[1] = vertices[i].y; + v[2] = vertices[i].z; + + /* NB: [X,Y,Z,TX,TY,R,G,B,A,...] */ + c = v + 5; + c[0] = cogl_color_get_red_byte (&vertices[i].color); + c[1] = cogl_color_get_green_byte (&vertices[i].color); + c[2] = cogl_color_get_blue_byte (&vertices[i].color); + c[3] = cogl_color_get_alpha_byte (&vertices[i].color); + + v += stride; + } + + /* Render all of the slices with the full geometry but use a + transparent border color so that any part of the texture not + covered by the slice will be ignored */ + tex_num = 0; + for (y = 0; y < tex->slice_y_spans->len; y++) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < tex->slice_x_spans->len; x++) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, tex_num++); + + /* Convert the vertices into an array of GLfloats ready to pass to + OpenGL */ + v = (GLfloat *)ctx->logged_vertices->data; + for (i = 0; i < n_vertices; i++) + { + GLfloat *t; + float tx, ty; + + tx = ((vertices[i].tx + - ((float)(x_span->start) + / tex->bitmap.width)) + * tex->bitmap.width / x_span->size); + ty = ((vertices[i].ty + - ((float)(y_span->start) + / tex->bitmap.height)) + * tex->bitmap.height / y_span->size); + + /* NB: [X,Y,Z,TX,TY,R,G,B,A,...] */ + t = v + 3; + t[0] = tx; + t[1] = ty; + + v += stride; + } + + cogl_material_flush_gl_state (ctx->source_material, + COGL_MATERIAL_FLUSH_DISABLE_MASK, + (guint32)~1, /* disable all except the + first layer */ + COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE, + gl_handle, + NULL); + + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); + } + } +} + + +static void +_cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices, + guint n_vertices, + guint n_layers, + guint stride, + gboolean use_color, + guint32 fallback_mask) +{ + CoglHandle material; + const GList *layers; + int i; + GList *tmp; + CoglTexSliceSpan *y_span, *x_span; + GLfloat *v; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + v = (GLfloat *)ctx->logged_vertices->data; + + material = ctx->source_material; + layers = cogl_material_get_layers (material); + + /* Convert the vertices into an array of GLfloats ready to pass to + OpenGL */ + for (v = (GLfloat *)ctx->logged_vertices->data, i = 0; + i < n_vertices; + v += stride, i++) + { + GLfloat *c; + int j; + + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + v[0] = vertices[i].x; + v[1] = vertices[i].y; + v[2] = vertices[i].z; + + for (tmp = (GList *)layers, j = 0; tmp != NULL; tmp = tmp->next, j++) + { + CoglHandle layer = (CoglHandle)tmp->data; + CoglHandle tex_handle; + CoglTexture *tex; + GLfloat *t; + float tx, ty; + + tex_handle = cogl_material_layer_get_texture (layer); + tex = _cogl_texture_pointer_from_handle (tex_handle); + + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + + tx = ((vertices[i].tx + - ((float)(x_span->start) + / tex->bitmap.width)) + * tex->bitmap.width / x_span->size); + ty = ((vertices[i].ty + - ((float)(y_span->start) + / tex->bitmap.height)) + * tex->bitmap.height / y_span->size); + + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + t = v + 3 + 2 * j; + t[0] = tx; + t[1] = ty; + } + + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + c = v + 3 + 2 * n_layers; + c[0] = cogl_color_get_red_float (&vertices[i].color); + c[1] = cogl_color_get_green_float (&vertices[i].color); + c[2] = cogl_color_get_blue_float (&vertices[i].color); + c[3] = cogl_color_get_alpha_float (&vertices[i].color); + } + + cogl_material_flush_gl_state (ctx->source_material, + COGL_MATERIAL_FLUSH_FALLBACK_MASK, + fallback_mask, + NULL); + + GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices)); +} + +void +cogl_polygon (CoglTextureVertex *vertices, + guint n_vertices, + gboolean use_color) +{ + CoglHandle material; + const GList *layers; + int n_layers; + GList *tmp; + gboolean use_sliced_polygon_fallback = FALSE; + guint32 fallback_mask = 0; + int i; + gulong enable_flags; + guint stride; + gsize stride_bytes; + GLfloat *v; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); cogl_clip_ensure (); - tex = _cogl_texture_pointer_from_handle (handle); + material = ctx->source_material; + layers = cogl_material_get_layers (ctx->source_material); + n_layers = g_list_length ((GList *)layers); - /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to - render sliced textures in the GL backend will not work. Therefore - cogl_texture_polygon is only supported if the texture is not - sliced */ - if (tex->slice_gl_handles->len != 1) + for (tmp = (GList *)layers, i = 0; tmp != NULL; tmp = tmp->next, i++) { - static gboolean shown_warning = FALSE; + CoglHandle layer = (CoglHandle)tmp->data; + CoglHandle tex_handle = cogl_material_layer_get_texture (layer); + CoglTexture *tex = _cogl_texture_pointer_from_handle (tex_handle); - if (!shown_warning) - { - g_warning ("cogl_texture_polygon does not work for " - "sliced textures on GL ES"); - shown_warning = TRUE; - } - return; + if (i == 0 && cogl_texture_is_sliced (tex_handle)) + { +#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) + { + static gboolean shown_gles_slicing_warning = FALSE; + if (!shown_gles_slicing_warning) + g_warning ("cogl_polygon does not work for sliced textures " + "on GL ES"); + shown_gles_slicing_warning = TRUE; + return; + } +#endif + if (n_layers > 1) + { + static gboolean shown_slicing_warning = FALSE; + if (!shown_slicing_warning) + { + g_warning ("Disabling layers 1..n since multi-texturing with " + "cogl_polygon isn't supported when using sliced " + "textures\n"); + shown_slicing_warning = TRUE; + } + } + use_sliced_polygon_fallback = TRUE; + n_layers = 1; + + if (tex->min_filter != GL_NEAREST || tex->mag_filter != GL_NEAREST) + { + static gboolean shown_filter_warning = FALSE; + if (!shown_filter_warning) + { + g_warning ("cogl_texture_polygon does not work for sliced textures " + "when the minification and magnification filters are not " + "CGL_NEAREST"); + shown_filter_warning = TRUE; + } + return; + } + +#ifdef HAVE_COGL_GL + /* Temporarily change the wrapping mode on all of the slices to use + * a transparent border + * XXX: it's doesn't look like we save/restore this, like the comment + * implies? */ + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_BORDER); +#endif + break; + } + + if (cogl_texture_is_sliced (tex_handle)) + { + g_warning ("Disabling layer %d of the current source material, " + "because texturing with the vertex buffer API is not " + "currently supported using sliced textures, or textures " + "with waste\n", i); + + fallback_mask |= (1 << i); + continue; + } } - /* Make sure there is enough space in the global texture vertex + /* Our data is arranged like: + * [X, Y, Z, TX0, TY0, TX1, TY1..., R, G, B, A,...] */ + stride = 3 + (2 * n_layers) + (use_color ? 4 : 0); + stride_bytes = stride * sizeof (GLfloat); + + /* Make sure there is enough space in the global vertex array. This is used so we can render the polygon with a single call to OpenGL but still support any number of vertices */ - g_array_set_size (ctx->texture_vertices, n_vertices); - p = (CoglTextureGLVertex *) ctx->texture_vertices->data; + g_array_set_size (ctx->logged_vertices, n_vertices * stride); + v = (GLfloat *)ctx->logged_vertices->data; /* Prepare GL state */ - enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); - - if ((tex->bitmap.format & COGL_A_BIT)) - enable_flags |= COGL_ENABLE_BLEND; - else if (use_color) - { - for (i = 0; i < n_vertices; i++) - if (cogl_color_get_alpha_byte(&vertices[i].color) < 255) - { - enable_flags |= COGL_ENABLE_BLEND; - break; - } - } - else if (ctx->color_alpha < 255) - enable_flags |= COGL_ENABLE_BLEND; + enable_flags = COGL_ENABLE_VERTEX_ARRAY; + enable_flags |= cogl_material_get_cogl_enable_flags (ctx->source_material); if (ctx->enable_backface_culling) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; @@ -2509,219 +3152,36 @@ cogl_texture_polygon (CoglHandle handle, if (use_color) { enable_flags |= COGL_ENABLE_COLOR_ARRAY; - GE( glColorPointer (4, GL_UNSIGNED_BYTE, - sizeof (CoglTextureGLVertex), p->c) ); - } - - GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); - GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->t ) ); + GE( glColorPointer (4, GL_FLOAT, + stride_bytes, + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + v + 3 + 2 * n_layers) ); + } cogl_enable (enable_flags); - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + GE (glVertexPointer (3, GL_FLOAT, stride_bytes, v)); - /* Convert the vertices into an array of GLfloats ready to pass to - OpenGL */ - for (i = 0; i < n_vertices; i++, p++) + for (i = 0; i < n_layers; i++) { -#define CFX_F - - p->v[0] = CFX_F(vertices[i].x); - p->v[1] = CFX_F(vertices[i].y); - p->v[2] = CFX_F(vertices[i].z); - p->t[0] = CFX_F(vertices[i].tx - * (x_span->size - x_span->waste) / x_span->size); - p->t[1] = CFX_F(vertices[i].ty - * (y_span->size - y_span->waste) / y_span->size); - p->c[0] = cogl_color_get_red_byte(&vertices[i].color); - p->c[1] = cogl_color_get_green_byte(&vertices[i].color); - p->c[2] = cogl_color_get_blue_byte(&vertices[i].color); - p->c[3] = cogl_color_get_alpha_byte(&vertices[i].color); - -#undef CFX_F + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glTexCoordPointer (2, GL_FLOAT, + stride_bytes, + /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ + v + 3 + 2 * i)); } - GE( cogl_gles2_wrapper_bind_texture (tex->gl_target, gl_handle, - tex->gl_intformat) ); - - GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); - - /* Set the last color so that the cache of the alpha value will work - properly */ - if (use_color && n_vertices > 0) - cogl_set_source_color (&vertices[n_vertices - 1].color); + if (use_sliced_polygon_fallback) + _cogl_texture_sliced_polygon (vertices, + n_vertices, + stride, + use_color); + else + _cogl_multitexture_unsliced_polygon (vertices, + n_vertices, + n_layers, + stride, + use_color, + fallback_mask); } -void -cogl_material_rectangle (CoglFixed x1, - CoglFixed y1, - CoglFixed x2, - CoglFixed y2, - const CoglFixed *user_tex_coords) -{ - CoglHandle material; - const GList *layers; - int n_layers; - const GList *tmp; - CoglHandle *valid_layers = NULL; - int n_valid_layers = 0; - gboolean handle_slicing = FALSE; - int i; - GLfloat *tex_coords_buff; - GLfloat quad_coords[8]; - gulong enable_flags = 0; - GLfloat values[4]; - - /* FIXME - currently cogl deals with enabling texturing via enable flags, - * but that can't scale to n texture units. Currently we have to be carefull - * how we leave the environment so we don't break things. See the cleanup - * notes at the end of this function */ - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - material = ctx->source_material; - - layers = cogl_material_get_layers (material); - n_layers = g_list_length ((GList *)layers); - valid_layers = alloca (sizeof (CoglHandle) * n_layers); - - for (tmp = layers; tmp != NULL; tmp = tmp->next) - { - CoglHandle layer = tmp->data; - CoglHandle texture = cogl_material_layer_get_texture (layer); - - if (cogl_material_layer_get_type (layer) - != COGL_MATERIAL_LAYER_TYPE_TEXTURE) - continue; - - /* FIXME: support sliced textures. For now if the first layer is - * sliced then all other layers are ignored, or if the first layer - * is not sliced, we ignore sliced textures in other layers. */ - if (cogl_texture_is_sliced (texture)) - { - if (n_valid_layers == 0) - { - valid_layers[n_valid_layers++] = layer; - handle_slicing = TRUE; - break; - } - continue; - } - valid_layers[n_valid_layers++] = tmp->data; - - if (n_valid_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) - break; - } - - /* NB: It could be that no valid texture layers were found, but - * we will still submit a non-textured rectangle in that case. */ - if (n_valid_layers) - tex_coords_buff = alloca (sizeof(GLfloat) * 8 * n_valid_layers); - - for (i = 0; i < n_valid_layers; i++) - { - CoglHandle layer = valid_layers[i]; - CoglHandle texture_handle = cogl_material_layer_get_texture (layer); - CoglTexture *texture = _cogl_texture_pointer_from_handle (texture_handle); - const CoglFixed *in_tex_coords = &user_tex_coords[i * 4]; - GLfloat *out_tex_coords = &tex_coords_buff[i * 8]; - GLuint gl_tex_handle; - GLenum gl_target; - -#define CFX_F COGL_FIXED_TO_FLOAT - /* IN LAYOUT: [ tx1:0, ty1:1, tx2:2, ty2:3 ] */ - out_tex_coords[0] = CFX_F (in_tex_coords[0]); /* tx1 */ - out_tex_coords[1] = CFX_F (in_tex_coords[1]); /* ty1 */ - out_tex_coords[2] = CFX_F (in_tex_coords[2]); /* tx2 */ - out_tex_coords[3] = CFX_F (in_tex_coords[1]); /* ty1 */ - out_tex_coords[4] = CFX_F (in_tex_coords[0]); /* tx1 */ - out_tex_coords[5] = CFX_F (in_tex_coords[3]); /* ty2 */ - out_tex_coords[6] = CFX_F (in_tex_coords[2]); /* tx2 */ - out_tex_coords[7] = CFX_F (in_tex_coords[3]); /* ty2 */ -#undef CFX_F - - /* TODO - support sliced textures */ - cogl_texture_get_gl_texture (texture, &gl_tex_handle, &gl_target); - - GE (glActiveTexture (GL_TEXTURE0 + i)); - cogl_material_layer_flush_gl_sampler_state (layer); - GE( cogl_gles2_wrapper_bind_texture (gl_target, - gl_tex_handle, - texure->gl_intformat)); - /* GE (glEnable (GL_TEXTURE_2D)); */ - - GE (glClientActiveTexture (GL_TEXTURE0 + i)); - GE (glTexCoordPointer (2, GL_FLOAT, 0, out_tex_coords)); - /* GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); */ - - /* FIXME - cogl only knows about one texture unit a.t.m - * (Also see cleanup note below) */ - if (i == 0) - enable_flags |= COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_TEXCOORD_ARRAY; - else - { - GE (glEnable (GL_TEXTURE_2D)); - GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); - } - } - -#define CFX_F COGL_FIXED_TO_FLOAT - quad_coords[0] = CFX_F (x1); - quad_coords[1] = CFX_F (y1); - quad_coords[2] = CFX_F (x2); - quad_coords[3] = CFX_F (y1); - quad_coords[4] = CFX_F (x1); - quad_coords[5] = CFX_F (y2); - quad_coords[6] = CFX_F (x2); - quad_coords[7] = CFX_F (y2); -#undef CFX_F - - enable_flags |= COGL_ENABLE_VERTEX_ARRAY; - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords)); - - /* Setup the remaining GL state according to this material... */ - cogl_material_flush_gl_material_state (material); - cogl_material_flush_gl_alpha_func (material); - cogl_material_flush_gl_blend_func (material); - /* FIXME: This api is a bit yukky, ideally it will be removed if we - * re-work the cogl_enable mechanism */ - enable_flags |= cogl_material_get_cogl_enable_flags (material); - - /* FIXME - cogl only knows about one texture unit so assumes that unit 0 - * is always active...*/ - GE (glActiveTexture (GL_TEXTURE0)); - GE (glClientActiveTexture (GL_TEXTURE0)); - cogl_enable (enable_flags); - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); - - /* FIXME - cogl doesn't currently have a way of caching the - * enable states for more than one texture unit so for now, - * we just disable anything relating to additional units once - * we are done with them. */ - for (i = 1; i < n_valid_layers; i++) - { - GE (glActiveTexture (GL_TEXTURE0 + i)); - GE (glClientActiveTexture (GL_TEXTURE0 + i)); - - GE (glDisable (GL_TEXTURE_2D)); - GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); - } - - /* FIXME - CoglMaterials aren't yet used pervasively throughout - * the cogl API, so we currently need to cleanup material state - * that will confuse other parts of the API. - * Other places to tweak, include the primitives API and lite - * GL wrappers like cogl_rectangle */ - values[0] = 0.2; values[1] = 0.2; values[2] = 0.2; values[3] = 1.0; - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, values)); - values[0] = 0.8; values[1] = 0.8; values[2] = 0.8; values[3] = 1.0; - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, values)); - values[0] = 0; values[1] = 0; values[2] = 0; values[3] = 1.0; - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, values)); - values[0] = 0; values[1] = 0; values[2] = 0; values[3] = 1.0; - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, values)); - values[0] = 0; - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, values)); -} diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index bcdd18cfb..32f4c71dd 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -92,10 +92,10 @@ cogl_paint_init (const CoglColor *color) fprintf(stderr, "\n ============== Paint Start ================ \n"); #endif - glClearColor (cogl_color_get_red (color), - cogl_color_get_green (color), - cogl_color_get_blue (color), - 0); + GE( glClearColor (cogl_color_get_red_float (color), + cogl_color_get_green_float (color), + cogl_color_get_blue_float (color), + 0.0) ); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDisable (GL_LIGHTING); @@ -213,10 +213,6 @@ cogl_enable (gulong flags) COGL_ENABLE_BLEND, GL_BLEND); - cogl_toggle_flag (ctx, flags, - COGL_ENABLE_TEXTURE_2D, - GL_TEXTURE_2D); - cogl_toggle_flag (ctx, flags, COGL_ENABLE_BACKFACE_CULLING, GL_CULL_FACE); @@ -225,10 +221,6 @@ cogl_enable (gulong flags) COGL_ENABLE_VERTEX_ARRAY, GL_VERTEX_ARRAY); - cogl_toggle_client_flag (ctx, flags, - COGL_ENABLE_TEXCOORD_ARRAY, - GL_TEXTURE_COORD_ARRAY); - cogl_toggle_client_flag (ctx, flags, COGL_ENABLE_COLOR_ARRAY, GL_COLOR_ARRAY); @@ -272,34 +264,11 @@ cogl_set_source_color (const CoglColor *color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); -#if 0 /*HAVE_GLES_COLOR4UB*/ + /* In case cogl_set_source_texture was previously used... */ + cogl_material_remove_layer (ctx->default_material, 0); - /* NOTE: seems SDK_OGLES-1.1_LINUX_PCEMULATION_2.02.22.0756 has this call - * but is broken - see #857. Therefor disabling. - */ - - /* - * GLES 1.1 does actually have this function, it's in the header file but - * missing in the reference manual (and SDK): - * - * http://www.khronos.org/egl/headers/1_1/gl.h - */ - GE( glColor4ub (color->red, - color->green, - color->blue, - color->alpha) ); - - -#else - /* conversion can cause issues with picking on some gles implementations */ - GE( glColor4f (cogl_color_get_red (color), - cogl_color_get_green (color), - cogl_color_get_blue (color), - cogl_color_get_alpha (color)) ); -#endif - - /* Store alpha for proper blending enables */ - ctx->color_alpha = cogl_color_get_alpha_byte (color); + cogl_material_set_color (ctx->default_material, color); + cogl_set_source (ctx->default_material); } static void @@ -421,6 +390,8 @@ _cogl_add_stencil_clip (float x_offset, { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + cogl_material_flush_gl_state (ctx->stencil_material); + if (first) { GE( glEnable (GL_STENCIL_TEST) );