primitives: implements cogl_polygon on vertex_attributes

This updates the implementation of cogl_polygon so it sits on the new
CoglVertexArray and CoglVertexAttribute apis. This lets us minimize the
number of different drawing paths we have to maintain in Cogl.

Since the sliced texture support for cogl_polygon has been broken for a
long time now and no one has complained this patch also greatly
simplifies the code by not doing any special material validation so
cogl_polygon will be restricted in the same way as
cogl_draw_vertex_attributes. (i.e. sliced textures not supported).
This commit is contained in:
Robert Bragg 2010-10-18 17:17:22 +01:00
parent 37657a5dd8
commit 44644d0a9e
3 changed files with 191 additions and 444 deletions

View File

@ -144,6 +144,8 @@ cogl_create_context (void)
_context->journal_flush_attributes_array =
g_array_new (TRUE, FALSE, sizeof (CoglVertexAttribute *));
_context->polygon_vertices = g_array_new (FALSE, FALSE, sizeof (float));
_context->current_material = NULL;
_context->current_material_changes_since_flush = 0;
_context->current_material_skip_gl_color = FALSE;
@ -285,6 +287,9 @@ _cogl_destroy_context (void)
if (_context->journal_flush_attributes_array)
g_array_free (_context->journal_flush_attributes_array, TRUE);
if (_context->polygon_vertices)
g_array_free (_context->polygon_vertices, TRUE);
if (_context->quad_buffer_indices_byte)
cogl_handle_unref (_context->quad_buffer_indices_byte);
if (_context->quad_buffer_indices)

View File

@ -100,9 +100,10 @@ typedef struct
* can batch things together. */
GArray *journal;
GArray *logged_vertices;
GArray *polygon_vertices;
GArray *journal_flush_attributes_array;
GArray *polygon_vertices;
/* Some simple caching, to minimize state changes... */
CoglMaterial *current_material;
unsigned long current_material_changes_since_flush;

View File

@ -35,6 +35,7 @@
#include "cogl-material-opengl-private.h"
#include "cogl-vertex-buffer-private.h"
#include "cogl-framebuffer-private.h"
#include "cogl-vertex-attribute-private.h"
#include <string.h>
#include <math.h>
@ -67,6 +68,7 @@ typedef struct _TextureSlicedPolygonState
const CoglTextureVertex *vertices;
int n_vertices;
int stride;
CoglVertexAttribute **attributes;
} TextureSlicedPolygonState;
static void
@ -821,212 +823,195 @@ cogl_rectangle (float x_1,
_cogl_rectangles_with_multitexture_coords (&rect, 1);
}
void
draw_polygon_sub_texture_cb (CoglHandle tex_handle,
GLuint gl_handle,
GLenum gl_target,
const float *subtexture_coords,
const float *virtual_coords,
typedef struct _AppendTexCoordsState
{
const CoglTextureVertex *vertices_in;
int vertex;
int layer;
float *vertices_out;
} AppendTexCoordsState;
gboolean
append_tex_coord_attributes_cb (CoglMaterial *material,
int layer_index,
void *user_data)
{
TextureSlicedPolygonState *state = user_data;
GLfloat *v;
int i;
CoglMaterialFlushOptions options;
float slice_origin_x;
float slice_origin_y;
float virtual_origin_x;
float virtual_origin_y;
float v_to_s_scale_x;
float v_to_s_scale_y;
CoglMaterial *source;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
slice_origin_x = subtexture_coords[0];
slice_origin_y = subtexture_coords[1];
virtual_origin_x = virtual_coords[0];
virtual_origin_y = virtual_coords[1];
v_to_s_scale_x = ((virtual_coords[2] - virtual_coords[0]) /
(subtexture_coords[2] - subtexture_coords[0]));
v_to_s_scale_y = ((virtual_coords[3] - virtual_coords[1]) /
(subtexture_coords[3] - subtexture_coords[1]));
/* Convert the vertices into an array of GLfloats ready to pass to
* OpenGL */
v = (GLfloat *)ctx->logged_vertices->data;
for (i = 0; i < state->n_vertices; i++)
{
/* NB: layout = [X,Y,Z,TX,TY,R,G,B,A,...] */
GLfloat *t = v + 3;
t[0] = ((state->vertices[i].tx - virtual_origin_x) * v_to_s_scale_x
+ slice_origin_x);
t[1] = ((state->vertices[i].ty - virtual_origin_y) * v_to_s_scale_y
+ slice_origin_y);
v += state->stride;
}
source = cogl_material_copy (cogl_get_source ());
if (G_UNLIKELY (ctx->legacy_state_set))
_cogl_material_apply_legacy_state (source);
options.flags =
COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE |
COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES;
options.layer0_override_texture = gl_handle;
/* Override the wrapping mode on all of the slices to use a
transparent border so that we can draw the full polygon for
each slice. Coordinates outside the texture will be transparent
so only the part of the polygon that intersects the slice will
be visible. This is a fairly hacky fallback and it relies on
the blending function working correctly */
memset (&options.wrap_mode_overrides, 0,
sizeof (options.wrap_mode_overrides));
options.wrap_mode_overrides.values[0].s =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER;
options.wrap_mode_overrides.values[0].t =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER;
if (cogl_material_get_n_layers (source) != 1)
{
/* disable all except the first layer */
options.disable_layers = (guint32)~1;
options.flags |= COGL_MATERIAL_FLUSH_DISABLE_MASK;
}
/* If we haven't already created a derived material... */
_cogl_material_apply_overrides (source, &options);
_cogl_material_flush_gl_state (source, FALSE);
GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices));
cogl_handle_unref (source);
}
/* handles 2d-sliced textures with > 1 slice */
static void
_cogl_texture_polygon_multiple_primitives (const CoglTextureVertex *vertices,
unsigned int n_vertices,
unsigned int stride,
gboolean use_color)
{
const GList *layers;
CoglHandle layer0;
AppendTexCoordsState *state = user_data;
CoglHandle tex_handle;
GLfloat *v;
int i;
TextureSlicedPolygonState state;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* 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 (cogl_get_source ());
layer0 = (CoglHandle)layers->data;
tex_handle = cogl_material_layer_get_texture (layer0);
v = (GLfloat *)ctx->logged_vertices->data;
for (i = 0; i < n_vertices; i++)
{
guint8 *c;
v[0] = vertices[i].x;
v[1] = vertices[i].y;
v[2] = vertices[i].z;
if (use_color)
{
/* NB: [X,Y,Z,TX,TY,R,G,B,A,...] */
c = (guint8 *) (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;
}
state.stride = stride;
state.vertices = vertices;
state.n_vertices = n_vertices;
_cogl_texture_foreach_sub_texture_in_region (tex_handle,
0, 0, 1, 1,
draw_polygon_sub_texture_cb,
&state);
}
static void
_cogl_multitexture_polygon_single_primitive (const CoglTextureVertex *vertices,
unsigned int n_vertices,
unsigned int n_layers,
unsigned int stride,
gboolean use_color,
guint32 fallback_layers,
CoglMaterialWrapModeOverrides *
wrap_mode_overrides)
{
CoglHandle material;
const GList *layers;
int i;
GList *tmp;
GLfloat *v;
CoglMaterialFlushOptions options;
CoglMaterial *copy = NULL;
CoglMaterial *source;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
material = cogl_get_source ();
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++)
{
guint8 *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;
GLfloat *t;
float tx, ty;
float *t;
tex_handle = cogl_material_layer_get_texture (layer);
tx = state->vertices_in[state->vertex].tx;
ty = state->vertices_in[state->vertex].ty;
/* COGL_INVALID_HANDLE textures will be handled in
* _cogl_material_flush_layers_gl_state but there is no need to worry
* about scaling texture coordinates in this case */
if (tex_handle == COGL_INVALID_HANDLE)
continue;
tx = vertices[i].tx;
ty = vertices[i].ty;
tex_handle = _cogl_material_get_layer_texture (material, layer_index);
if (tex_handle != COGL_INVALID_HANDLE)
_cogl_texture_transform_coords_to_gl (tex_handle, &tx, &ty);
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
t = v + 3 + 2 * j;
t = state->vertices_out + 3 + 2 * state->layer;
t[0] = tx;
t[1] = ty;
state->layer++;
return TRUE;
}
typedef struct _ValidateState
{
CoglMaterial *original_material;
CoglMaterial *material;
} ValidateState;
gboolean
validate_layer_cb (CoglMaterial *material,
int layer_index,
void *user_data)
{
ValidateState *state = user_data;
/* By default COGL_MATERIAL_WRAP_MODE_AUTOMATIC becomes
* GL_CLAMP_TO_EDGE but we want the polygon API to use GL_REPEAT to
* maintain compatibility with previous releases
*/
if (cogl_material_get_layer_wrap_mode_s (material, layer_index) ==
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
{
if (state->original_material == state->material)
state->material = cogl_material_copy (material);
cogl_material_set_layer_wrap_mode_s (state->material, layer_index,
COGL_MATERIAL_WRAP_MODE_REPEAT);
}
if (cogl_material_get_layer_wrap_mode_t (material, layer_index) ==
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
{
if (state->original_material == state->material)
state->material = cogl_material_copy (material);
cogl_material_set_layer_wrap_mode_t (state->material, layer_index,
COGL_MATERIAL_WRAP_MODE_REPEAT);
}
return TRUE;
}
void
cogl_polygon (const CoglTextureVertex *vertices,
unsigned int n_vertices,
gboolean use_color)
{
CoglMaterial *material;
ValidateState validate_state;
int n_layers;
int n_attributes;
CoglVertexAttribute **attributes;
int i;
unsigned int stride;
gsize stride_bytes;
CoglVertexArray *vertex_array;
float *v;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
material = cogl_get_source ();
validate_state.original_material = material;
validate_state.material = material;
cogl_material_foreach_layer (material,
validate_layer_cb,
&validate_state);
material = validate_state.material;
n_layers = cogl_material_get_n_layers (material);
n_attributes = 1 + n_layers + (use_color ? 1 : 0);
attributes = g_alloca (sizeof (CoglVertexAttribute *) * (n_attributes + 1));
attributes[n_attributes] = NULL;
/* Our data is arranged like:
* [X, Y, Z, TX0, TY0, TX1, TY1..., R, G, B, A,...] */
stride = 3 + (2 * n_layers) + (use_color ? 1 : 0);
stride_bytes = stride * sizeof (float);
/* 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->polygon_vertices, n_vertices * stride);
vertex_array = cogl_vertex_array_new (n_vertices * stride_bytes);
attributes[0] =
cogl_vertex_attribute_new (vertex_array,
"cogl_position_in",
stride_bytes,
0,
3,
COGL_VERTEX_ATTRIBUTE_TYPE_FLOAT);
for (i = 0; i < n_layers; i++)
{
const char *names[] = {
"cogl_tex_coord0_in",
"cogl_tex_coord1_in",
"cogl_tex_coord2_in",
"cogl_tex_coord3_in",
"cogl_tex_coord4_in",
"cogl_tex_coord5_in",
"cogl_tex_coord6_in",
"cogl_tex_coord7_in"
};
char *name = i < 8 ? (char *)names[i] :
g_strdup_printf ("cogl_tex_coord%d_in", i);
attributes[i + 1] =
cogl_vertex_attribute_new (vertex_array,
name,
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
12 + 8 * i,
2,
COGL_VERTEX_ATTRIBUTE_TYPE_FLOAT);
}
if (use_color)
{
attributes[n_attributes - 1] =
cogl_vertex_attribute_new (vertex_array,
"cogl_color_in",
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
12 + 8 * n_layers,
4,
COGL_VERTEX_ATTRIBUTE_TYPE_UNSIGNED_BYTE);
}
/* Convert the vertices into an array of float vertex attributes */
v = (float *)ctx->polygon_vertices->data;
for (i = 0; i < n_vertices; i++)
{
AppendTexCoordsState append_tex_coords_state;
guint8 *c;
/* 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;
append_tex_coords_state.vertices_in = vertices;
append_tex_coords_state.vertex = i;
append_tex_coords_state.layer = 0;
append_tex_coords_state.vertices_out = v;
cogl_material_foreach_layer (material,
append_tex_coord_attributes_cb,
&append_tex_coords_state);
if (use_color)
{
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
@ -1036,266 +1021,22 @@ _cogl_multitexture_polygon_single_primitive (const CoglTextureVertex *vertices,
c[2] = cogl_color_get_blue_byte (&vertices[i].color);
c[3] = cogl_color_get_alpha_byte (&vertices[i].color);
}
v += stride;
}
if (G_UNLIKELY (ctx->legacy_state_set))
{
copy = cogl_material_copy (cogl_get_source ());
_cogl_material_apply_legacy_state (copy);
source = copy;
}
else
source = cogl_get_source ();
options.flags = 0;
if (G_UNLIKELY (fallback_layers))
{
options.flags |= COGL_MATERIAL_FLUSH_FALLBACK_MASK;
options.fallback_layers = fallback_layers;
}
if (wrap_mode_overrides)
{
options.flags |= COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES;
options.wrap_mode_overrides = *wrap_mode_overrides;
}
if (options.flags)
{
/* If we haven't already created a derived material... */
if (!copy)
{
copy = cogl_material_copy (source);
source = copy;
}
_cogl_material_apply_overrides (copy, &options);
}
_cogl_material_flush_gl_state (source, use_color);
GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices));
if (G_UNLIKELY (copy))
cogl_handle_unref (copy);
}
void
cogl_polygon (const CoglTextureVertex *vertices,
unsigned int n_vertices,
gboolean use_color)
{
CoglMaterial *material;
CoglMaterial *copy = NULL;
const GList *layers, *tmp;
int n_layers;
gboolean use_sliced_polygon_fallback = FALSE;
guint32 fallback_layers = 0;
int i;
unsigned long enable_flags;
unsigned int stride;
gsize stride_bytes;
GLfloat *v;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
CoglMaterialWrapModeOverrides *wrap_mode_overrides_p = NULL;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_journal_flush ();
/* NB: _cogl_framebuffer_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_framebuffer_flush_state (_cogl_get_framebuffer (), 0);
material = cogl_get_source ();
layers = cogl_material_get_layers (material);
n_layers = g_list_length ((GList *)layers);
memset (&wrap_mode_overrides, 0, sizeof (wrap_mode_overrides));
for (tmp = layers, i = 0; tmp != NULL; tmp = tmp->next, i++)
{
CoglHandle layer = tmp->data;
CoglHandle tex_handle = cogl_material_layer_get_texture (layer);
/* COGL_INVALID_HANDLE textures will be handled in
* _cogl_material_flush_layers_gl_state */
if (tex_handle == COGL_INVALID_HANDLE)
continue;
/* Give the texture a chance to know that we're rendering
* non-quad shaped primitives. If the texture is in an atlas it
* will be migrated
*
* FIXME: this needs to be generalized. There could be any
* number of things that might require a shuffling of the
* underlying texture storage.
*/
_cogl_texture_ensure_non_quad_rendering (tex_handle);
/* We need to ensure the mipmaps are ready before deciding
* anything else about the texture because the texture storate
* could completely change if it needs to be migrated out of the
* atlas and will affect how we validate the layer.
*/
_cogl_material_layer_pre_paint (layer);
if (i == 0 && cogl_texture_is_sliced (tex_handle))
{
#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2)
{
static gboolean warning_seen = FALSE;
if (!warning_seen)
g_warning ("cogl_polygon does not work for sliced textures "
"on GL ES");
warning_seen = TRUE;
return;
}
#endif
if (n_layers > 1)
{
static gboolean warning_seen = FALSE;
if (!warning_seen)
{
g_warning ("Disabling layers 1..n since multi-texturing with "
"cogl_polygon isn't supported when using sliced "
"textures\n");
warning_seen = TRUE;
}
}
use_sliced_polygon_fallback = TRUE;
n_layers = 1;
if (cogl_material_layer_get_min_filter (layer) != GL_NEAREST
|| cogl_material_layer_get_mag_filter (layer) != GL_NEAREST)
{
static gboolean warning_seen = FALSE;
if (!warning_seen)
{
g_warning ("cogl_texture_polygon does not work for sliced textures "
"when the minification and magnification filters are not "
"COGL_MATERIAL_FILTER_NEAREST");
warning_seen = TRUE;
}
return;
}
break;
}
if (cogl_texture_is_sliced (tex_handle))
{
static gboolean warning_seen = FALSE;
if (!warning_seen)
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);
warning_seen = TRUE;
fallback_layers |= (1 << i);
continue;
}
/* By default COGL_MATERIAL_WRAP_MODE_AUTOMATIC becomes
GL_CLAMP_TO_EDGE but we want the polygon API to use GL_REPEAT
to maintain compatibility with previous releases */
if (cogl_material_layer_get_wrap_mode_s (layer) ==
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
{
wrap_mode_overrides.values[i].s =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT;
wrap_mode_overrides_p = &wrap_mode_overrides;
}
if (cogl_material_layer_get_wrap_mode_t (layer) ==
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
{
wrap_mode_overrides.values[i].t =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT;
wrap_mode_overrides_p = &wrap_mode_overrides;
}
}
/* Our data is arranged like:
* [X, Y, Z, TX0, TY0, TX1, TY1..., R, G, B, A,...] */
stride = 3 + (2 * n_layers) + (use_color ? 1 : 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->logged_vertices, n_vertices * stride);
v = (GLfloat *)ctx->logged_vertices->data;
/* Prepare GL state */
enable_flags = COGL_ENABLE_VERTEX_ARRAY;
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
if (use_color)
{
enable_flags |= COGL_ENABLE_COLOR_ARRAY;
GE( glColorPointer (4, GL_UNSIGNED_BYTE,
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
v + 3 + 2 * n_layers) );
if (!_cogl_material_get_real_blend_enabled (material))
{
CoglMaterialBlendEnable blend_enabled =
COGL_MATERIAL_BLEND_ENABLE_ENABLED;
copy = cogl_material_copy (material);
_cogl_material_set_blend_enabled (copy, blend_enabled);
material = copy;
}
}
_cogl_enable (enable_flags);
_cogl_flush_face_winding ();
GE (glVertexPointer (3, GL_FLOAT, stride_bytes, v));
for (i = 0; i < n_layers; i++)
{
GE (glClientActiveTexture (GL_TEXTURE0 + i));
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
GE (glTexCoordPointer (2, GL_FLOAT,
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
v + 3 + 2 * i));
}
_cogl_bitmask_clear_all (&ctx->temp_bitmask);
_cogl_bitmask_set_range (&ctx->temp_bitmask, n_layers, TRUE);
_cogl_disable_other_texcoord_arrays (&ctx->temp_bitmask);
v = (float *)ctx->polygon_vertices->data;
cogl_buffer_set_data (COGL_BUFFER (vertex_array),
0,
(const guint8 *)v,
ctx->polygon_vertices->len * sizeof (float));
cogl_push_source (material);
if (use_sliced_polygon_fallback)
_cogl_texture_polygon_multiple_primitives (vertices,
n_vertices,
stride,
use_color);
else
_cogl_multitexture_polygon_single_primitive (vertices,
n_vertices,
n_layers,
stride,
use_color,
fallback_layers,
wrap_mode_overrides_p);
cogl_draw_vertex_attributes_array (COGL_VERTICES_MODE_TRIANGLE_FAN,
0, n_vertices,
attributes);
cogl_pop_source ();
if (copy)
cogl_object_unref (copy);
/* XXX: when we have weak materials then any override material
* should get associated with the original material so we don't
* create lots of one-shot materials! */
/* Reset the size of the logged vertex array because rendering
rectangles expects it to start at 0 */
g_array_set_size (ctx->logged_vertices, 0);
}