summaryrefslogtreecommitdiff
path: root/cogl/cogl/cogl-primitives.c
diff options
context:
space:
mode:
Diffstat (limited to 'cogl/cogl/cogl-primitives.c')
-rw-r--r--cogl/cogl/cogl-primitives.c1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/cogl/cogl/cogl-primitives.c b/cogl/cogl/cogl-primitives.c
new file mode 100644
index 000000000..bfe773588
--- /dev/null
+++ b/cogl/cogl/cogl-primitives.c
@@ -0,0 +1,1148 @@
+/*
+ * Cogl
+ *
+ * A Low Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 2007,2008,2009,2010 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-debug.h"
+#include "cogl-context-private.h"
+#include "cogl-journal-private.h"
+#include "cogl-texture-private.h"
+#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-opengl-private.h"
+#include "cogl-vertex-buffer-private.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl-attribute-private.h"
+#include "cogl-private.h"
+#include "cogl-meta-texture.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl1-context.h"
+#include "cogl-primitives-private.h"
+
+#include <string.h>
+#include <math.h>
+
+#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
+
+typedef struct _TextureSlicedQuadState
+{
+ CoglFramebuffer *framebuffer;
+ CoglPipeline *pipeline;
+ CoglTexture *main_texture;
+ float tex_virtual_origin_x;
+ float tex_virtual_origin_y;
+ float quad_origin_x;
+ float quad_origin_y;
+ float v_to_q_scale_x;
+ float v_to_q_scale_y;
+ float quad_len_x;
+ float quad_len_y;
+ CoglBool flipped_x;
+ CoglBool flipped_y;
+} TextureSlicedQuadState;
+
+typedef struct _TextureSlicedPolygonState
+{
+ const CoglTextureVertex *vertices;
+ int n_vertices;
+ int stride;
+ CoglAttribute **attributes;
+} TextureSlicedPolygonState;
+
+static void
+log_quad_sub_textures_cb (CoglTexture *texture,
+ const float *subtexture_coords,
+ const float *virtual_coords,
+ void *user_data)
+{
+ TextureSlicedQuadState *state = user_data;
+ CoglFramebuffer *framebuffer = state->framebuffer;
+ CoglTexture *texture_override;
+ float quad_coords[4];
+
+#define TEX_VIRTUAL_TO_QUAD(V, Q, AXIS) \
+ do { \
+ Q = V - state->tex_virtual_origin_##AXIS; \
+ Q *= state->v_to_q_scale_##AXIS; \
+ if (state->flipped_##AXIS) \
+ Q = state->quad_len_##AXIS - Q; \
+ Q += state->quad_origin_##AXIS; \
+ } while (0);
+
+ TEX_VIRTUAL_TO_QUAD (virtual_coords[0], quad_coords[0], x);
+ TEX_VIRTUAL_TO_QUAD (virtual_coords[1], quad_coords[1], y);
+
+ TEX_VIRTUAL_TO_QUAD (virtual_coords[2], quad_coords[2], x);
+ TEX_VIRTUAL_TO_QUAD (virtual_coords[3], quad_coords[3], y);
+
+#undef TEX_VIRTUAL_TO_QUAD
+
+ COGL_NOTE (DRAW,
+ "~~~~~ slice\n"
+ "qx1: %f\t"
+ "qy1: %f\n"
+ "qx2: %f\t"
+ "qy2: %f\n"
+ "tx1: %f\t"
+ "ty1: %f\n"
+ "tx2: %f\t"
+ "ty2: %f\n",
+ quad_coords[0], quad_coords[1],
+ quad_coords[2], quad_coords[3],
+ subtexture_coords[0], subtexture_coords[1],
+ subtexture_coords[2], subtexture_coords[3]);
+
+ /* We only need to override the texture if it's different from the
+ main texture */
+ if (texture == state->main_texture)
+ texture_override = NULL;
+ else
+ texture_override = texture;
+
+ _cogl_journal_log_quad (framebuffer->journal,
+ quad_coords,
+ state->pipeline,
+ 1, /* one layer */
+ texture_override, /* replace the layer0 texture */
+ subtexture_coords,
+ 4);
+}
+
+typedef struct _ValidateFirstLayerState
+{
+ CoglPipeline *override_pipeline;
+} ValidateFirstLayerState;
+
+static CoglBool
+validate_first_layer_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ ValidateFirstLayerState *state = user_data;
+ CoglPipelineWrapMode clamp_to_edge =
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ CoglPipelineWrapMode wrap_s;
+ CoglPipelineWrapMode wrap_t;
+
+ /* 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. By
+ * default WRAP_MODE_AUTOMATIC becomes CLAMP_TO_EDGE so we only need
+ * to override if the wrap mode isn't already automatic or
+ * clamp_to_edge.
+ */
+ wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index);
+ if (wrap_s != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE &&
+ wrap_s != COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (!state->override_pipeline)
+ state->override_pipeline = cogl_pipeline_copy (pipeline);
+ cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline,
+ layer_index, clamp_to_edge);
+ }
+
+ wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index);
+ if (wrap_t != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE &&
+ wrap_t != COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (!state->override_pipeline)
+ state->override_pipeline = cogl_pipeline_copy (pipeline);
+ cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline,
+ layer_index, clamp_to_edge);
+ }
+
+ return FALSE;
+}
+
+/* This path doesn't currently support multitexturing but is used for
+ * CoglTextures that don't support repeating using the GPU so we need to
+ * manually emit extra geometry to fake the repeating. This includes:
+ *
+ * - CoglTexture2DSliced: when made of > 1 slice or if the users given
+ * texture coordinates require repeating,
+ * - CoglTexture2DAtlas: if the users given texture coordinates require
+ * repeating,
+ * - CoglTextureRectangle: if the users given texture coordinates require
+ * repeating,
+ * - CoglTexturePixmap: if the users given texture coordinates require
+ * repeating
+ */
+/* TODO: support multitexturing */
+static void
+_cogl_texture_quad_multiple_primitives (CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ CoglTexture *texture,
+ int layer_index,
+ const float *position,
+ float tx_1,
+ float ty_1,
+ float tx_2,
+ float ty_2)
+{
+ TextureSlicedQuadState state;
+ CoglBool tex_virtual_flipped_x;
+ CoglBool tex_virtual_flipped_y;
+ CoglBool quad_flipped_x;
+ CoglBool quad_flipped_y;
+ ValidateFirstLayerState validate_first_layer_state;
+ CoglPipelineWrapMode wrap_s, wrap_t;
+
+ wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index);
+ wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index);
+
+ validate_first_layer_state.override_pipeline = NULL;
+ cogl_pipeline_foreach_layer (pipeline,
+ validate_first_layer_cb,
+ &validate_first_layer_state);
+
+ state.framebuffer = framebuffer;
+ state.main_texture = texture;
+
+ if (validate_first_layer_state.override_pipeline)
+ state.pipeline = validate_first_layer_state.override_pipeline;
+ else
+ state.pipeline = pipeline;
+
+ /* Get together the data we need to transform the virtual texture
+ * coordinates of each slice into quad coordinates...
+ *
+ * NB: We need to consider that the quad coordinates and the texture
+ * coordinates may be inverted along the x or y axis, and must preserve the
+ * inversions when we emit the final geometry.
+ */
+
+#define X0 0
+#define Y0 1
+#define X1 2
+#define Y1 3
+
+ tex_virtual_flipped_x = (tx_1 > tx_2) ? TRUE : FALSE;
+ tex_virtual_flipped_y = (ty_1 > ty_2) ? TRUE : FALSE;
+ state.tex_virtual_origin_x = tex_virtual_flipped_x ? tx_2 : tx_1;
+ state.tex_virtual_origin_y = tex_virtual_flipped_y ? ty_2 : ty_1;
+
+ quad_flipped_x = (position[X0] > position[X1]) ? TRUE : FALSE;
+ quad_flipped_y = (position[Y0] > position[Y1]) ? TRUE : FALSE;
+ state.quad_origin_x = quad_flipped_x ? position[X1] : position[X0];
+ state.quad_origin_y = quad_flipped_y ? position[Y1] : position[Y0];
+
+ /* flatten the two forms of coordinate inversion into one... */
+ state.flipped_x = tex_virtual_flipped_x ^ quad_flipped_x;
+ state.flipped_y = tex_virtual_flipped_y ^ quad_flipped_y;
+
+ /* We use the _len_AXIS naming here instead of _width and _height because
+ * log_quad_slice_cb uses a macro with symbol concatenation to handle both
+ * axis, so this is more convenient... */
+ state.quad_len_x = fabs (position[X1] - position[X0]);
+ state.quad_len_y = fabs (position[Y1] - position[Y0]);
+
+#undef X0
+#undef Y0
+#undef X1
+#undef Y1
+
+ state.v_to_q_scale_x = fabs (state.quad_len_x / (tx_2 - tx_1));
+ state.v_to_q_scale_y = fabs (state.quad_len_y / (ty_2 - ty_1));
+
+ /* For backwards compatablity the default wrap mode for cogl_rectangle() is
+ * _REPEAT... */
+ if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT;
+ if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT;
+
+ cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture),
+ tx_1, ty_1, tx_2, ty_2,
+ wrap_s,
+ wrap_t,
+ log_quad_sub_textures_cb,
+ &state);
+
+ if (validate_first_layer_state.override_pipeline)
+ cogl_object_unref (validate_first_layer_state.override_pipeline);
+}
+
+typedef struct _ValidateTexCoordsState
+{
+ int i;
+ int n_layers;
+ const float *user_tex_coords;
+ int user_tex_coords_len;
+ float *final_tex_coords;
+ CoglPipeline *override_pipeline;
+ CoglBool needs_multiple_primitives;
+} ValidateTexCoordsState;
+
+/*
+ * Validate the texture coordinates for this rectangle.
+ */
+static CoglBool
+validate_tex_coords_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ ValidateTexCoordsState *state = user_data;
+ CoglTexture *texture;
+ const float *in_tex_coords;
+ float *out_tex_coords;
+ float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
+ CoglTransformResult transform_result;
+
+ state->i++;
+
+ /* FIXME: we should be able to avoid this copying when no
+ * transform is required by the texture backend and the user
+ * has supplied enough coordinates for all the layers.
+ */
+
+ /* If the user didn't supply texture coordinates for this layer
+ then use the default coords */
+ if (state->i >= state->user_tex_coords_len / 4)
+ in_tex_coords = default_tex_coords;
+ else
+ in_tex_coords = &state->user_tex_coords[state->i * 4];
+
+ out_tex_coords = &state->final_tex_coords[state->i * 4];
+
+ memcpy (out_tex_coords, in_tex_coords, sizeof (float) * 4);
+
+ texture = cogl_pipeline_get_layer_texture (pipeline, layer_index);
+
+ /* NB: NULL textures are handled by _cogl_pipeline_flush_gl_state */
+ if (!texture)
+ return TRUE;
+
+ /* Convert the texture coordinates to GL.
+ */
+ transform_result =
+ _cogl_texture_transform_quad_coords_to_gl (texture,
+ out_tex_coords);
+ /* If the texture has waste or we are using GL_TEXTURE_RECT we
+ * can't handle texture repeating so we can't use the layer if
+ * repeating is required.
+ *
+ * NB: We already know that no texture matrix is being used if the
+ * texture doesn't support hardware repeat.
+ */
+ if (transform_result == COGL_TRANSFORM_SOFTWARE_REPEAT)
+ {
+ if (state->i == 0)
+ {
+ if (state->n_layers > 1)
+ {
+ static CoglBool warning_seen = FALSE;
+ if (!warning_seen)
+ g_warning ("Skipping layers 1..n of your material since "
+ "the first layer doesn't support hardware "
+ "repeat (e.g. because of waste or use of "
+ "GL_TEXTURE_RECTANGLE_ARB) and you supplied "
+ "texture coordinates outside the range [0,1]."
+ "Falling back to software repeat assuming "
+ "layer 0 is the most important one keep");
+ warning_seen = TRUE;
+ }
+
+ if (state->override_pipeline)
+ cogl_object_unref (state->override_pipeline);
+ state->needs_multiple_primitives = TRUE;
+ return FALSE;
+ }
+ else
+ {
+ static CoglBool warning_seen = FALSE;
+ if (!warning_seen)
+ g_warning ("Skipping layer %d of your material "
+ "since you have supplied texture coords "
+ "outside the range [0,1] but the texture "
+ "doesn't support hardware repeat (e.g. "
+ "because of waste or use of "
+ "GL_TEXTURE_RECTANGLE_ARB). This isn't "
+ "supported with multi-texturing.", state->i);
+ warning_seen = TRUE;
+
+ cogl_pipeline_set_layer_texture (pipeline, layer_index, NULL);
+ }
+ }
+
+ /* By default WRAP_MODE_AUTOMATIC becomes to CLAMP_TO_EDGE. If
+ the texture coordinates need repeating then we'll override
+ this to GL_REPEAT. Otherwise we'll leave it at CLAMP_TO_EDGE
+ so that it won't blend in pixels from the opposite side when
+ the full texture is drawn with GL_LINEAR filter mode */
+ if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT)
+ {
+ if (cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) ==
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (!state->override_pipeline)
+ state->override_pipeline = cogl_pipeline_copy (pipeline);
+ cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline,
+ layer_index,
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+ }
+ if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) ==
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (!state->override_pipeline)
+ state->override_pipeline = cogl_pipeline_copy (pipeline);
+ cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline,
+ layer_index,
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+ }
+ }
+
+ return TRUE;
+}
+
+/* This path supports multitexturing but only when each of the layers is
+ * handled with a single GL texture. Also if repeating is necessary then
+ * _cogl_texture_can_hardware_repeat() must return TRUE.
+ * This includes layers made from:
+ *
+ * - CoglTexture2DSliced: if only comprised of a single slice with optional
+ * waste, assuming the users given texture coordinates don't require
+ * repeating.
+ * - CoglTexture{1D,2D,3D}: always.
+ * - CoglTexture2DAtlas: assuming the users given texture coordinates don't
+ * require repeating.
+ * - CoglTextureRectangle: assuming the users given texture coordinates don't
+ * require repeating.
+ * - CoglTexturePixmap: assuming the users given texture coordinates don't
+ * require repeating.
+ */
+static CoglBool
+_cogl_multitexture_quad_single_primitive (CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ const float *position,
+ const float *user_tex_coords,
+ int user_tex_coords_len)
+{
+ int n_layers = cogl_pipeline_get_n_layers (pipeline);
+ ValidateTexCoordsState state;
+ float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers);
+
+ state.i = -1;
+ state.n_layers = n_layers;
+ state.user_tex_coords = user_tex_coords;
+ state.user_tex_coords_len = user_tex_coords_len;
+ state.final_tex_coords = final_tex_coords;
+ state.override_pipeline = NULL;
+ state.needs_multiple_primitives = FALSE;
+
+ cogl_pipeline_foreach_layer (pipeline,
+ validate_tex_coords_cb,
+ &state);
+
+ if (state.needs_multiple_primitives)
+ return FALSE;
+
+ if (state.override_pipeline)
+ pipeline = state.override_pipeline;
+
+ _cogl_journal_log_quad (framebuffer->journal,
+ position,
+ pipeline,
+ n_layers,
+ NULL, /* no texture override */
+ final_tex_coords,
+ n_layers * 4);
+
+ if (state.override_pipeline)
+ cogl_object_unref (state.override_pipeline);
+
+ return TRUE;
+}
+
+typedef struct _ValidateLayerState
+{
+ CoglContext *ctx;
+ int i;
+ int first_layer;
+ CoglPipeline *override_source;
+ CoglBool all_use_sliced_quad_fallback;
+} ValidateLayerState;
+
+static CoglBool
+_cogl_rectangles_validate_layer_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ ValidateLayerState *state = user_data;
+ CoglTexture *texture;
+
+ state->i++;
+
+ /* We need to ensure the mipmaps are ready before deciding
+ * anything else about the texture because the texture storage
+ * could completely change if it needs to be migrated out of the
+ * atlas and will affect how we validate the layer.
+ *
+ * FIXME: this needs to be generalized. There could be any
+ * number of things that might require a shuffling of the
+ * underlying texture storage. We could add two mechanisms to
+ * generalize this a bit...
+ *
+ * 1) add a _cogl_pipeline_layer_update_storage() function that
+ * would for instance consider if mipmapping is necessary and
+ * potentially migrate the texture from an atlas.
+ *
+ * 2) allow setting of transient primitive-flags on a pipeline
+ * that may affect the outcome of _update_storage(). One flag
+ * could indicate that we expect to sample beyond the bounds of
+ * the texture border.
+ *
+ * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS;
+ * _cogl_pipeline_layer_assert_primitive_flags (layer, flags)
+ * _cogl_pipeline_layer_update_storage (layer)
+ * enqueue primitive in journal
+ *
+ * when the primitive is dequeued and drawn we should:
+ * _cogl_pipeline_flush_gl_state (pipeline)
+ * draw primitive
+ * _cogl_pipeline_unassert_primitive_flags (layer, flags);
+ *
+ * _cogl_pipeline_layer_update_storage should take into
+ * consideration all the asserted primitive requirements. (E.g.
+ * there could be multiple primitives in the journal - or in a
+ * renderlist in the future - that need mipmaps or that need
+ * valid contents beyond their borders (for cogl_polygon)
+ * meaning they can't work with textures in an atas, so
+ * _cogl_pipeline_layer_update_storage would pass on these
+ * requirements to the texture atlas backend which would make
+ * sure the referenced texture is migrated out of the atlas and
+ * mipmaps are generated.)
+ */
+ _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index);
+
+ texture = cogl_pipeline_get_layer_texture (pipeline, layer_index);
+
+ /* NULL textures are handled by
+ * _cogl_pipeline_flush_gl_state */
+ if (texture == NULL)
+ return TRUE;
+
+ if (state->i == 0)
+ state->first_layer = layer_index;
+
+ /* 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 (texture))
+ {
+ if (state->i == 0)
+ {
+ if (cogl_pipeline_get_n_layers (pipeline) > 1)
+ {
+ static CoglBool warning_seen = FALSE;
+
+ if (!state->override_source)
+ state->override_source = cogl_pipeline_copy (pipeline);
+ _cogl_pipeline_prune_to_n_layers (state->override_source, 1);
+
+ if (!warning_seen)
+ g_warning ("Skipping layers 1..n of your pipeline 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");
+ warning_seen = TRUE;
+ }
+
+ state->all_use_sliced_quad_fallback = TRUE;
+
+ return FALSE;
+ }
+ else
+ {
+ static CoglBool warning_seen = FALSE;
+ CoglTexture2D *tex_2d;
+
+ if (!warning_seen)
+ g_warning ("Skipping layer %d of your pipeline consisting of "
+ "a sliced texture (unsuported for multi texturing)",
+ state->i);
+ warning_seen = TRUE;
+
+ /* Note: currently only 2D textures can be sliced. */
+ tex_2d = state->ctx->default_gl_texture_2d_tex;
+ cogl_pipeline_set_layer_texture (pipeline, layer_index,
+ COGL_TEXTURE (tex_2d));
+ return TRUE;
+ }
+ }
+
+#ifdef COGL_ENABLE_DEBUG
+ /* If the texture can't be repeated with the GPU (e.g. because it has
+ * waste or if using GL_TEXTURE_RECTANGLE_ARB) then if a texture matrix
+ * is also in use we don't know if the result will end up trying
+ * to texture from the waste area.
+ *
+ * Note: we check can_hardware_repeat() first since it's cheaper.
+ *
+ * Note: cases where the texture coordinates will require repeating
+ * will be caught by later validation.
+ */
+ if (!_cogl_texture_can_hardware_repeat (texture) &&
+ _cogl_pipeline_layer_has_user_matrix (pipeline, layer_index))
+ {
+ static CoglBool warning_seen = FALSE;
+ if (!warning_seen)
+ g_warning ("layer %d of your pipeline uses a custom "
+ "texture matrix but because the texture doesn't "
+ "support hardware repeating you may see artefacts "
+ "due to sampling beyond the texture's bounds.",
+ state->i);
+ warning_seen = TRUE;
+ }
+#endif
+
+ return TRUE;
+}
+
+void
+_cogl_framebuffer_draw_multitextured_rectangles (
+ CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ CoglMultiTexturedRect *rects,
+ int n_rects,
+ CoglBool disable_legacy_state)
+{
+ CoglContext *ctx = framebuffer->context;
+ CoglPipeline *original_pipeline;
+ ValidateLayerState state;
+ int i;
+
+ original_pipeline = pipeline;
+
+ /*
+ * Validate all the layers of the current source pipeline...
+ */
+ state.ctx = ctx;
+ state.i = -1;
+ state.first_layer = 0;
+ state.override_source = NULL;
+ state.all_use_sliced_quad_fallback = FALSE;
+ cogl_pipeline_foreach_layer (pipeline,
+ _cogl_rectangles_validate_layer_cb,
+ &state);
+
+ if (state.override_source)
+ pipeline = state.override_source;
+
+ if (!disable_legacy_state)
+ {
+ if (G_UNLIKELY (ctx->legacy_state_set) &&
+ _cogl_get_enable_legacy_state ())
+ {
+ /* If we haven't already made a pipeline copy */
+ if (pipeline == original_pipeline)
+ pipeline = cogl_pipeline_copy (pipeline);
+ _cogl_pipeline_apply_legacy_state (pipeline);
+ }
+ }
+
+ /*
+ * Emit geometry for each of the rectangles...
+ */
+
+ for (i = 0; i < n_rects; i++)
+ {
+ CoglTexture *texture;
+ const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
+ const float *tex_coords;
+
+ if (!state.all_use_sliced_quad_fallback)
+ {
+ CoglBool success =
+ _cogl_multitexture_quad_single_primitive (framebuffer,
+ pipeline,
+ rects[i].position,
+ rects[i].tex_coords,
+ rects[i].tex_coords_len);
+
+ /* NB: If _cogl_multitexture_quad_single_primitive fails then it
+ * means the user tried to use texture repeat with a texture that
+ * can't be repeated by the GPU (e.g. due to waste or use of
+ * GL_TEXTURE_RECTANGLE_ARB) */
+ if (success)
+ continue;
+ }
+
+ /* If multitexturing failed or we are drawing with a sliced texture
+ * then we only support a single layer so we pluck out the texture
+ * from the first pipeline layer... */
+ texture = cogl_pipeline_get_layer_texture (pipeline, state.first_layer);
+
+ if (rects[i].tex_coords)
+ tex_coords = rects[i].tex_coords;
+ else
+ tex_coords = default_tex_coords;
+
+ COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)");
+
+ _cogl_texture_quad_multiple_primitives (framebuffer,
+ pipeline,
+ texture,
+ state.first_layer,
+ rects[i].position,
+ tex_coords[0],
+ tex_coords[1],
+ tex_coords[2],
+ tex_coords[3]);
+ }
+
+ if (pipeline != original_pipeline)
+ cogl_object_unref (pipeline);
+}
+
+static void
+_cogl_rectangles_with_multitexture_coords (
+ CoglMultiTexturedRect *rects,
+ int n_rects)
+{
+ _cogl_framebuffer_draw_multitextured_rectangles (cogl_get_draw_framebuffer (),
+ cogl_get_source (),
+ rects,
+ n_rects,
+ FALSE);
+}
+
+void
+cogl_rectangles (const float *verts,
+ unsigned int n_rects)
+{
+ CoglMultiTexturedRect *rects;
+ int i;
+
+ /* XXX: All the cogl_rectangle* APIs normalize their input into an array of
+ * CoglMultiTexturedRect rectangles and pass these on to our work horse;
+ * _cogl_rectangles_with_multitexture_coords.
+ */
+
+ rects = g_alloca (n_rects * sizeof (CoglMultiTexturedRect));
+
+ for (i = 0; i < n_rects; i++)
+ {
+ rects[i].position = &verts[i * 4];
+ rects[i].tex_coords = NULL;
+ rects[i].tex_coords_len = 0;
+ }
+
+ _cogl_rectangles_with_multitexture_coords (rects, n_rects);
+}
+
+void
+cogl_rectangles_with_texture_coords (const float *verts,
+ unsigned int n_rects)
+{
+ CoglMultiTexturedRect *rects;
+ int i;
+
+ /* XXX: All the cogl_rectangle* APIs normalize their input into an array of
+ * CoglMultiTexturedRect rectangles and pass these on to our work horse;
+ * _cogl_rectangles_with_multitexture_coords.
+ */
+
+ rects = g_alloca (n_rects * sizeof (CoglMultiTexturedRect));
+
+ for (i = 0; i < n_rects; i++)
+ {
+ rects[i].position = &verts[i * 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 x_1,
+ float y_1,
+ float x_2,
+ float y_2,
+ float tx_1,
+ float ty_1,
+ float tx_2,
+ float ty_2)
+{
+ const float position[4] = {x_1, y_1, x_2, y_2};
+ const float tex_coords[4] = {tx_1, ty_1, tx_2, ty_2};
+ CoglMultiTexturedRect rect;
+
+ /* XXX: All the cogl_rectangle* APIs normalize their input into an array of
+ * CoglMultiTexturedRect rectangles and pass these on to our work horse;
+ * _cogl_rectangles_with_multitexture_coords.
+ */
+
+ rect.position = position;
+ rect.tex_coords = tex_coords;
+ rect.tex_coords_len = 4;
+
+ _cogl_rectangles_with_multitexture_coords (&rect, 1);
+}
+
+void
+cogl_rectangle_with_multitexture_coords (float x_1,
+ float y_1,
+ float x_2,
+ float y_2,
+ const float *user_tex_coords,
+ int user_tex_coords_len)
+{
+ const float position[4] = {x_1, y_1, x_2, y_2};
+ CoglMultiTexturedRect rect;
+
+ /* XXX: All the cogl_rectangle* APIs normalize their input into an array of
+ * CoglMultiTexturedRect rectangles and pass these on to our work horse;
+ * _cogl_rectangles_with_multitexture_coords.
+ */
+
+ rect.position = position;
+ rect.tex_coords = user_tex_coords;
+ rect.tex_coords_len = user_tex_coords_len;
+
+ _cogl_rectangles_with_multitexture_coords (&rect, 1);
+}
+
+void
+cogl_rectangle (float x_1,
+ float y_1,
+ float x_2,
+ float y_2)
+{
+ const float position[4] = {x_1, y_1, x_2, y_2};
+ CoglMultiTexturedRect rect;
+
+ /* XXX: All the cogl_rectangle* APIs normalize their input into an array of
+ * CoglMultiTexturedRect rectangles and pass these on to our work horse;
+ * _cogl_rectangles_with_multitexture_coords.
+ */
+
+ rect.position = position;
+ rect.tex_coords = NULL;
+ rect.tex_coords_len = 0;
+
+ _cogl_rectangles_with_multitexture_coords (&rect, 1);
+}
+
+void
+_cogl_rectangle_immediate (CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ float x_1,
+ float y_1,
+ float x_2,
+ float y_2)
+{
+ /* Draw a rectangle using the vertex array API to avoid going
+ through the journal. This should only be used in cases where the
+ code might be called while the journal is already being flushed
+ such as when flushing the clip state */
+ CoglContext *ctx = framebuffer->context;
+ float vertices[8] =
+ {
+ x_1, y_1,
+ x_1, y_2,
+ x_2, y_1,
+ x_2, y_2
+ };
+ CoglAttributeBuffer *attribute_buffer;
+ CoglAttribute *attributes[1];
+
+ attribute_buffer =
+ cogl_attribute_buffer_new (ctx, sizeof (vertices), vertices);
+ attributes[0] = cogl_attribute_new (attribute_buffer,
+ "cogl_position_in",
+ sizeof (float) * 2, /* stride */
+ 0, /* offset */
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ _cogl_framebuffer_draw_attributes (framebuffer,
+ pipeline,
+ COGL_VERTICES_MODE_TRIANGLE_STRIP,
+ 0, /* first_index */
+ 4, /* n_vertices */
+ attributes,
+ 1,
+ COGL_DRAW_SKIP_JOURNAL_FLUSH |
+ COGL_DRAW_SKIP_PIPELINE_VALIDATION |
+ COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH |
+ COGL_DRAW_SKIP_LEGACY_STATE);
+
+
+ cogl_object_unref (attributes[0]);
+ cogl_object_unref (attribute_buffer);
+}
+
+typedef struct _AppendTexCoordsState
+{
+ const CoglTextureVertex *vertices_in;
+ int vertex;
+ int layer;
+ float *vertices_out;
+} AppendTexCoordsState;
+
+static CoglBool
+append_tex_coord_attributes_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ AppendTexCoordsState *state = user_data;
+ CoglTexture *texture;
+ float tx, ty;
+ float *t;
+
+ tx = state->vertices_in[state->vertex].tx;
+ ty = state->vertices_in[state->vertex].ty;
+
+ /* NULL textures will be handled in
+ * _cogl_pipeline_flush_layers_gl_state but there is no need to worry
+ * about scaling texture coordinates in this case */
+ texture = cogl_pipeline_get_layer_texture (pipeline, layer_index);
+ if (texture != NULL)
+ _cogl_texture_transform_coords_to_gl (texture, &tx, &ty);
+
+ /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
+ t = state->vertices_out + 3 + 2 * state->layer;
+ t[0] = tx;
+ t[1] = ty;
+
+ state->layer++;
+
+ return TRUE;
+}
+
+typedef struct _ValidateState
+{
+ CoglPipeline *original_pipeline;
+ CoglPipeline *pipeline;
+} ValidateState;
+
+static CoglBool
+_cogl_polygon_validate_layer_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ ValidateState *state = user_data;
+
+ /* By default COGL_PIPELINE_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_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) ==
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (state->original_pipeline == state->pipeline)
+ state->pipeline = cogl_pipeline_copy (pipeline);
+
+ cogl_pipeline_set_layer_wrap_mode_s (state->pipeline, layer_index,
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+ }
+
+ if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) ==
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ {
+ if (state->original_pipeline == state->pipeline)
+ state->pipeline = cogl_pipeline_copy (pipeline);
+
+ cogl_pipeline_set_layer_wrap_mode_t (state->pipeline, layer_index,
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+ }
+
+ return TRUE;
+}
+
+void
+cogl_polygon (const CoglTextureVertex *vertices,
+ unsigned int n_vertices,
+ CoglBool use_color)
+{
+ CoglPipeline *pipeline;
+ ValidateState validate_state;
+ int n_layers;
+ int n_attributes;
+ CoglAttribute **attributes;
+ int i;
+ unsigned int stride;
+ size_t stride_bytes;
+ CoglAttributeBuffer *attribute_buffer;
+ float *v;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ pipeline = cogl_get_source ();
+
+ validate_state.original_pipeline = pipeline;
+ validate_state.pipeline = pipeline;
+ cogl_pipeline_foreach_layer (pipeline,
+ _cogl_polygon_validate_layer_cb,
+ &validate_state);
+ pipeline = validate_state.pipeline;
+
+ n_layers = cogl_pipeline_get_n_layers (pipeline);
+
+ n_attributes = 1 + n_layers + (use_color ? 1 : 0);
+ attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes);
+
+ /* 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);
+
+ attribute_buffer =
+ cogl_attribute_buffer_new (ctx, n_vertices * stride_bytes, NULL);
+
+ attributes[0] = cogl_attribute_new (attribute_buffer,
+ "cogl_position_in",
+ stride_bytes,
+ 0,
+ 3,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ for (i = 0; i < n_layers; i++)
+ {
+ static 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 *allocated_name = NULL;
+ const char *name;
+
+ if (i < 8)
+ name = names[i];
+ else
+ name = allocated_name = g_strdup_printf ("cogl_tex_coord%d_in", i);
+
+ attributes[i + 1] = cogl_attribute_new (attribute_buffer,
+ name,
+ stride_bytes,
+ /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
+ 12 + 8 * i,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ g_free (allocated_name);
+ }
+
+ if (use_color)
+ {
+ attributes[n_attributes - 1] =
+ cogl_attribute_new (attribute_buffer,
+ "cogl_color_in",
+ stride_bytes,
+ /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
+ 12 + 8 * n_layers,
+ 4,
+ COGL_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;
+ uint8_t *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_pipeline_foreach_layer (pipeline,
+ append_tex_coord_attributes_cb,
+ &append_tex_coords_state);
+
+ if (use_color)
+ {
+ /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
+ c = (uint8_t *) (v + 3 + 2 * n_layers);
+ 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;
+ }
+
+ v = (float *)ctx->polygon_vertices->data;
+ cogl_buffer_set_data (COGL_BUFFER (attribute_buffer),
+ 0,
+ v,
+ ctx->polygon_vertices->len * sizeof (float));
+
+ /* XXX: although this may seem redundant, we need to do this since
+ * cogl_polygon() can be used with legacy state and its the source stack
+ * which track whether legacy state is enabled.
+ *
+ * (We only have a CoglDrawFlag to disable legacy state not one
+ * to enable it) */
+ cogl_push_source (pipeline);
+
+ _cogl_framebuffer_draw_attributes (cogl_get_draw_framebuffer (),
+ pipeline,
+ COGL_VERTICES_MODE_TRIANGLE_FAN,
+ 0, n_vertices,
+ attributes,
+ n_attributes,
+ 0 /* no draw flags */);
+
+ cogl_pop_source ();
+
+ if (pipeline != validate_state.original_pipeline)
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (attribute_buffer);
+
+ for (i = 0; i < n_attributes; i++)
+ cogl_object_unref (attributes[i]);
+}