summaryrefslogtreecommitdiff
path: root/cogl/cogl/cogl-attribute.c
diff options
context:
space:
mode:
Diffstat (limited to 'cogl/cogl/cogl-attribute.c')
-rw-r--r--cogl/cogl/cogl-attribute.c687
1 files changed, 687 insertions, 0 deletions
diff --git a/cogl/cogl/cogl-attribute.c b/cogl/cogl/cogl-attribute.c
new file mode 100644
index 000000000..bcfbf78c6
--- /dev/null
+++ b/cogl/cogl/cogl-attribute.c
@@ -0,0 +1,687 @@
+/*
+ * Cogl
+ *
+ * A Low Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 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.
+ *
+ *
+ *
+ * Authors:
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-util.h"
+#include "cogl-context-private.h"
+#include "cogl-object-private.h"
+#include "cogl-journal-private.h"
+#include "cogl-attribute.h"
+#include "cogl-attribute-private.h"
+#include "cogl-pipeline.h"
+#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-opengl-private.h"
+#include "cogl-texture-private.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl-indices-private.h"
+#ifdef COGL_PIPELINE_PROGEND_GLSL
+#include "cogl-pipeline-progend-glsl-private.h"
+#endif
+#include "cogl-private.h"
+#include "cogl-gtype-private.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* This isn't defined in the GLES headers */
+#ifndef GL_UNSIGNED_INT
+#define GL_UNSIGNED_INT 0x1405
+#endif
+
+static void _cogl_attribute_free (CoglAttribute *attribute);
+
+COGL_OBJECT_DEFINE (Attribute, attribute);
+COGL_GTYPE_DEFINE_CLASS (Attribute, attribute);
+
+static CoglBool
+validate_cogl_attribute_name (const char *name,
+ char **real_attribute_name,
+ CoglAttributeNameID *name_id,
+ CoglBool *normalized,
+ int *layer_number)
+{
+ name = name + 5; /* skip "cogl_" */
+
+ *normalized = FALSE;
+ *layer_number = 0;
+
+ if (strcmp (name, "position_in") == 0)
+ *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY;
+ else if (strcmp (name, "color_in") == 0)
+ {
+ *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY;
+ *normalized = TRUE;
+ }
+ else if (strcmp (name, "tex_coord_in") == 0)
+ {
+ *real_attribute_name = "cogl_tex_coord0_in";
+ *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
+ }
+ else if (strncmp (name, "tex_coord", strlen ("tex_coord")) == 0)
+ {
+ char *endptr;
+ *layer_number = strtoul (name + 9, &endptr, 10);
+ if (strcmp (endptr, "_in") != 0)
+ {
+ g_warning ("Texture coordinate attributes should either be named "
+ "\"cogl_tex_coord_in\" or named with a texture unit index "
+ "like \"cogl_tex_coord2_in\"\n");
+ return FALSE;
+ }
+ *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
+ }
+ else if (strcmp (name, "normal_in") == 0)
+ {
+ *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY;
+ *normalized = TRUE;
+ }
+ else if (strcmp (name, "point_size_in") == 0)
+ *name_id = COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY;
+ else
+ {
+ g_warning ("Unknown cogl_* attribute name cogl_%s\n", name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+CoglAttributeNameState *
+_cogl_attribute_register_attribute_name (CoglContext *context,
+ const char *name)
+{
+ CoglAttributeNameState *name_state = g_new (CoglAttributeNameState, 1);
+ int name_index = context->n_attribute_names++;
+ char *name_copy = g_strdup (name);
+
+ name_state->name = NULL;
+ name_state->name_index = name_index;
+ if (strncmp (name, "cogl_", 5) == 0)
+ {
+ if (!validate_cogl_attribute_name (name,
+ &name_state->name,
+ &name_state->name_id,
+ &name_state->normalized_default,
+ &name_state->layer_number))
+ goto error;
+ }
+ else
+ {
+ name_state->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY;
+ name_state->normalized_default = FALSE;
+ name_state->layer_number = 0;
+ }
+
+ if (name_state->name == NULL)
+ name_state->name = name_copy;
+
+ g_hash_table_insert (context->attribute_name_states_hash,
+ name_copy, name_state);
+
+ if (G_UNLIKELY (context->attribute_name_index_map == NULL))
+ context->attribute_name_index_map =
+ g_array_new (FALSE, FALSE, sizeof (void *));
+
+ g_array_set_size (context->attribute_name_index_map, name_index + 1);
+
+ g_array_index (context->attribute_name_index_map,
+ CoglAttributeNameState *, name_index) = name_state;
+
+ return name_state;
+
+error:
+ g_free (name_state);
+ return NULL;
+}
+
+static CoglBool
+validate_n_components (const CoglAttributeNameState *name_state,
+ int n_components)
+{
+ switch (name_state->name_id)
+ {
+ case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
+ if (G_UNLIKELY (n_components == 1))
+ {
+ g_critical ("glVertexPointer doesn't allow 1 component vertex "
+ "positions so we currently only support \"cogl_vertex\" "
+ "attributes where n_components == 2, 3 or 4");
+ return FALSE;
+ }
+ break;
+ case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
+ if (G_UNLIKELY (n_components != 3 && n_components != 4))
+ {
+ g_critical ("glColorPointer expects 3 or 4 component colors so we "
+ "currently only support \"cogl_color\" attributes where "
+ "n_components == 3 or 4");
+ return FALSE;
+ }
+ break;
+ case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY:
+ break;
+ case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
+ if (G_UNLIKELY (n_components != 3))
+ {
+ g_critical ("glNormalPointer expects 3 component normals so we "
+ "currently only support \"cogl_normal\" attributes "
+ "where n_components == 3");
+ return FALSE;
+ }
+ break;
+ case COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY:
+ if (G_UNLIKELY (n_components != 1))
+ {
+ g_critical ("The point size attribute can only have one "
+ "component");
+ return FALSE;
+ }
+ break;
+ case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY:
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+CoglAttribute *
+cogl_attribute_new (CoglAttributeBuffer *attribute_buffer,
+ const char *name,
+ size_t stride,
+ size_t offset,
+ int n_components,
+ CoglAttributeType type)
+{
+ CoglAttribute *attribute = g_slice_new (CoglAttribute);
+ CoglBuffer *buffer = COGL_BUFFER (attribute_buffer);
+ CoglContext *ctx = buffer->context;
+
+ attribute->is_buffered = TRUE;
+
+ attribute->name_state =
+ g_hash_table_lookup (ctx->attribute_name_states_hash, name);
+ if (!attribute->name_state)
+ {
+ CoglAttributeNameState *name_state =
+ _cogl_attribute_register_attribute_name (ctx, name);
+ if (!name_state)
+ goto error;
+ attribute->name_state = name_state;
+ }
+
+ attribute->d.buffered.attribute_buffer = cogl_object_ref (attribute_buffer);
+ attribute->d.buffered.stride = stride;
+ attribute->d.buffered.offset = offset;
+ attribute->d.buffered.n_components = n_components;
+ attribute->d.buffered.type = type;
+
+ attribute->immutable_ref = 0;
+
+ if (attribute->name_state->name_id != COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY)
+ {
+ if (!validate_n_components (attribute->name_state, n_components))
+ return NULL;
+ attribute->normalized =
+ attribute->name_state->normalized_default;
+ }
+ else
+ attribute->normalized = FALSE;
+
+ return _cogl_attribute_object_new (attribute);
+
+error:
+ _cogl_attribute_free (attribute);
+ return NULL;
+}
+
+static CoglAttribute *
+_cogl_attribute_new_const (CoglContext *context,
+ const char *name,
+ int n_components,
+ int n_columns,
+ CoglBool transpose,
+ const float *value)
+{
+ CoglAttribute *attribute = g_slice_new (CoglAttribute);
+
+ attribute->name_state =
+ g_hash_table_lookup (context->attribute_name_states_hash, name);
+ if (!attribute->name_state)
+ {
+ CoglAttributeNameState *name_state =
+ _cogl_attribute_register_attribute_name (context, name);
+ if (!name_state)
+ goto error;
+ attribute->name_state = name_state;
+ }
+
+ if (!validate_n_components (attribute->name_state, n_components))
+ goto error;
+
+ attribute->is_buffered = FALSE;
+ attribute->normalized = FALSE;
+
+ attribute->d.constant.context = cogl_object_ref (context);
+
+ attribute->d.constant.boxed.v.array = NULL;
+
+ if (n_columns == 1)
+ {
+ _cogl_boxed_value_set_float (&attribute->d.constant.boxed,
+ n_components,
+ 1,
+ value);
+ }
+ else
+ {
+ /* FIXME: Up until GL[ES] 3 only square matrices were supported
+ * and we don't currently expose non-square matrices in Cogl.
+ */
+ _COGL_RETURN_VAL_IF_FAIL (n_columns == n_components, NULL);
+ _cogl_boxed_value_set_matrix (&attribute->d.constant.boxed,
+ n_columns,
+ 1,
+ transpose,
+ value);
+ }
+
+ return _cogl_attribute_object_new (attribute);
+
+error:
+ _cogl_attribute_free (attribute);
+ return NULL;
+}
+
+CoglAttribute *
+cogl_attribute_new_const_1f (CoglContext *context,
+ const char *name,
+ float value)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 1, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ &value);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_2fv (CoglContext *context,
+ const char *name,
+ const float *value)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 2, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ value);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_3fv (CoglContext *context,
+ const char *name,
+ const float *value)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 3, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ value);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_4fv (CoglContext *context,
+ const char *name,
+ const float *value)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 4, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ value);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_2f (CoglContext *context,
+ const char *name,
+ float component0,
+ float component1)
+{
+ float vec2[2] = { component0, component1 };
+ return _cogl_attribute_new_const (context,
+ name,
+ 2, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ vec2);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_3f (CoglContext *context,
+ const char *name,
+ float component0,
+ float component1,
+ float component2)
+{
+ float vec3[3] = { component0, component1, component2 };
+ return _cogl_attribute_new_const (context,
+ name,
+ 3, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ vec3);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_4f (CoglContext *context,
+ const char *name,
+ float component0,
+ float component1,
+ float component2,
+ float component3)
+{
+ float vec4[4] = { component0, component1, component2, component3 };
+ return _cogl_attribute_new_const (context,
+ name,
+ 4, /* n_components */
+ 1, /* 1 column vector */
+ FALSE, /* no transpose */
+ vec4);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_2x2fv (CoglContext *context,
+ const char *name,
+ const float *matrix2x2,
+ CoglBool transpose)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 2, /* n_components */
+ 2, /* 2 column vector */
+ FALSE, /* no transpose */
+ matrix2x2);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_3x3fv (CoglContext *context,
+ const char *name,
+ const float *matrix3x3,
+ CoglBool transpose)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 3, /* n_components */
+ 3, /* 3 column vector */
+ FALSE, /* no transpose */
+ matrix3x3);
+}
+
+CoglAttribute *
+cogl_attribute_new_const_4x4fv (CoglContext *context,
+ const char *name,
+ const float *matrix4x4,
+ CoglBool transpose)
+{
+ return _cogl_attribute_new_const (context,
+ name,
+ 4, /* n_components */
+ 4, /* 4 column vector */
+ FALSE, /* no transpose */
+ matrix4x4);
+}
+
+CoglBool
+cogl_attribute_get_normalized (CoglAttribute *attribute)
+{
+ _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), FALSE);
+
+ return attribute->normalized;
+}
+
+static void
+warn_about_midscene_changes (void)
+{
+ static CoglBool seen = FALSE;
+ if (!seen)
+ {
+ g_warning ("Mid-scene modification of attributes has "
+ "undefined results\n");
+ seen = TRUE;
+ }
+}
+
+void
+cogl_attribute_set_normalized (CoglAttribute *attribute,
+ CoglBool normalized)
+{
+ _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
+
+ if (G_UNLIKELY (attribute->immutable_ref))
+ warn_about_midscene_changes ();
+
+ attribute->normalized = normalized;
+}
+
+CoglAttributeBuffer *
+cogl_attribute_get_buffer (CoglAttribute *attribute)
+{
+ _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL);
+ _COGL_RETURN_VAL_IF_FAIL (attribute->is_buffered, NULL);
+
+ return attribute->d.buffered.attribute_buffer;
+}
+
+void
+cogl_attribute_set_buffer (CoglAttribute *attribute,
+ CoglAttributeBuffer *attribute_buffer)
+{
+ _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
+ _COGL_RETURN_IF_FAIL (attribute->is_buffered);
+
+ if (G_UNLIKELY (attribute->immutable_ref))
+ warn_about_midscene_changes ();
+
+ cogl_object_ref (attribute_buffer);
+
+ cogl_object_unref (attribute->d.buffered.attribute_buffer);
+ attribute->d.buffered.attribute_buffer = attribute_buffer;
+}
+
+CoglAttribute *
+_cogl_attribute_immutable_ref (CoglAttribute *attribute)
+{
+ CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer);
+
+ _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL);
+
+ attribute->immutable_ref++;
+ _cogl_buffer_immutable_ref (buffer);
+ return attribute;
+}
+
+void
+_cogl_attribute_immutable_unref (CoglAttribute *attribute)
+{
+ CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer);
+
+ _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
+ _COGL_RETURN_IF_FAIL (attribute->immutable_ref > 0);
+
+ attribute->immutable_ref--;
+ _cogl_buffer_immutable_unref (buffer);
+}
+
+static void
+_cogl_attribute_free (CoglAttribute *attribute)
+{
+ if (attribute->is_buffered)
+ cogl_object_unref (attribute->d.buffered.attribute_buffer);
+ else
+ _cogl_boxed_value_destroy (&attribute->d.constant.boxed);
+
+ g_slice_free (CoglAttribute, attribute);
+}
+
+static CoglBool
+validate_layer_cb (CoglPipeline *pipeline,
+ int layer_index,
+ void *user_data)
+{
+ CoglTexture *texture =
+ cogl_pipeline_get_layer_texture (pipeline, layer_index);
+ CoglFlushLayerState *state = user_data;
+ CoglBool status = TRUE;
+
+ /* invalid textures will be handled correctly in
+ * _cogl_pipeline_flush_layers_gl_state */
+ if (texture == NULL)
+ goto validated;
+
+ _cogl_texture_flush_journal_rendering (texture);
+
+ /* 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 */
+ _cogl_texture_ensure_non_quad_rendering (texture);
+
+ /* 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_pipeline_pre_paint_for_layer (pipeline, layer_index);
+
+ if (!_cogl_texture_can_hardware_repeat (texture))
+ {
+ 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", layer_index);
+
+ /* XXX: maybe we can add a mechanism for users to forcibly use
+ * textures with waste where it would be their responsability to use
+ * texture coords in the range [0,1] such that sampling outside isn't
+ * required. We can then use a texture matrix (or a modification of
+ * the users own matrix) to map 1 to the edge of the texture data.
+ *
+ * Potentially, given the same guarantee as above we could also
+ * support a single sliced layer too. We would have to redraw the
+ * vertices once for each layer, each time with a fiddled texture
+ * matrix.
+ */
+ state->fallback_layers |= (1 << state->unit);
+ state->options.flags |= COGL_PIPELINE_FLUSH_FALLBACK_MASK;
+ }
+
+validated:
+ state->unit++;
+ return status;
+}
+
+void
+_cogl_flush_attributes_state (CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ CoglDrawFlags flags,
+ CoglAttribute **attributes,
+ int n_attributes)
+{
+ CoglContext *ctx = framebuffer->context;
+ CoglFlushLayerState layers_state;
+ CoglPipeline *copy = NULL;
+
+ if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
+ _cogl_journal_flush (framebuffer->journal);
+
+ layers_state.unit = 0;
+ layers_state.options.flags = 0;
+ layers_state.fallback_layers = 0;
+
+ if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION))
+ cogl_pipeline_foreach_layer (pipeline,
+ validate_layer_cb,
+ &layers_state);
+
+ /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
+ * as the pipeline state) when flushing the clip stack, so should
+ * always be done first when preparing to draw. We need to do this
+ * before setting up the array pointers because setting up the clip
+ * stack can cause some drawing which would change the array
+ * pointers. */
+ if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH))
+ _cogl_framebuffer_flush_state (framebuffer,
+ framebuffer,
+ COGL_FRAMEBUFFER_STATE_ALL);
+
+ /* In cogl_read_pixels we have a fast-path when reading a single
+ * pixel and the scene is just comprised of simple rectangles still
+ * in the journal. For this optimization to work we need to track
+ * when the framebuffer really does get drawn to. */
+ _cogl_framebuffer_mark_mid_scene (framebuffer);
+ _cogl_framebuffer_mark_clear_clip_dirty (framebuffer);
+
+ if (G_UNLIKELY (!(flags & COGL_DRAW_SKIP_LEGACY_STATE)) &&
+ G_UNLIKELY (ctx->legacy_state_set) &&
+ _cogl_get_enable_legacy_state ())
+ {
+ copy = cogl_pipeline_copy (pipeline);
+ pipeline = copy;
+ _cogl_pipeline_apply_legacy_state (pipeline);
+ }
+
+ ctx->driver_vtable->flush_attributes_state (framebuffer,
+ pipeline,
+ &layers_state,
+ flags,
+ attributes,
+ n_attributes);
+
+ if (copy)
+ cogl_object_unref (copy);
+}
+
+int
+_cogl_attribute_get_n_components (CoglAttribute *attribute)
+{
+ if (attribute->is_buffered)
+ return attribute->d.buffered.n_components;
+ else
+ return attribute->d.constant.boxed.size;
+}