Only things missing here from upstream are translations and .gitignore changes
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 33214ab..80d3b09 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -349,6 +349,8 @@ cogl_sources_c = \
$(srcdir)/cogl-pipeline-snippet.c \
$(srcdir)/cogl-pipeline-cache.h \
$(srcdir)/cogl-pipeline-cache.c \
+ $(srcdir)/cogl-pipeline-hash-table.h \
+ $(srcdir)/cogl-pipeline-hash-table.c \
$(srcdir)/cogl-material-compat.c \
$(srcdir)/cogl-program.c \
$(srcdir)/cogl-program-private.h \
@@ -552,7 +554,7 @@ include $(top_srcdir)/build/autotools/Makefile.am.enums
lib_LTLIBRARIES += libcogl.la
-libcogl_la_LIBADD = -lm $(COGL_DEP_LIBS) $(COGL_EXTRA_LDFLAGS)
+libcogl_la_LIBADD = $(LIBM) $(COGL_DEP_LIBS) $(COGL_EXTRA_LDFLAGS)
if !USE_GLIB
libcogl_la_LIBADD += $(top_builddir)/deps/glib/libglib.la
libcogl_la_LIBADD += $(top_builddir)/deps/gmodule/libgmodule.la
diff --git a/cogl/cogl-bitmap-pixbuf.c b/cogl/cogl-bitmap-pixbuf.c
index a02b253..ad34234 100644
--- a/cogl/cogl-bitmap-pixbuf.c
+++ b/cogl/cogl-bitmap-pixbuf.c
@@ -125,11 +125,24 @@ _cogl_bitmap_from_file (CoglContext *ctx,
/* allocate buffer big enough to hold pixel data */
bmp = _cogl_bitmap_new_with_malloc_buffer (ctx,
width, height,
- COGL_PIXEL_FORMAT_ARGB_8888);
+ COGL_PIXEL_FORMAT_ARGB_8888,
+ error);
+ if (bmp == NULL)
+ {
+ CFRelease (image);
+ return NULL;
+ }
rowstride = cogl_bitmap_get_rowstride (bmp);
out_data = _cogl_bitmap_map (bmp,
COGL_BUFFER_ACCESS_WRITE,
- COGL_BUFFER_MAP_HINT_DISCARD);
+ COGL_BUFFER_MAP_HINT_DISCARD,
+ error);
+ if (out_data == NULL)
+ {
+ cogl_object_unref (bmp);
+ CFRelease (image);
+ return NULL;
+ }
/* render to buffer */
color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
diff --git a/cogl/cogl-matrix.h b/cogl/cogl-matrix.h
index 90f3ea9..a136ea0 100644
--- a/cogl/cogl-matrix.h
+++ b/cogl/cogl-matrix.h
@@ -27,6 +27,8 @@
#ifndef __COGL_MATRIX_H
#define __COGL_MATRIX_H
+#include <cogl/cogl-defines.h>
+
#ifdef COGL_HAS_GTYPE_SUPPORT
#include <glib-object.h>
#endif /* COGL_HAS_GTYPE_SUPPORT */
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
index fab3614..df4c433 100644
--- a/cogl/cogl-pipeline-cache.c
+++ b/cogl/cogl-pipeline-cache.c
@@ -3,7 +3,7 @@
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
- * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011, 2013 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -32,133 +32,47 @@
#include "cogl-context-private.h"
#include "cogl-pipeline-private.h"
#include "cogl-pipeline-cache.h"
+#include "cogl-pipeline-hash-table.h"
struct _CoglPipelineCache
{
- GHashTable *fragment_hash;
- GHashTable *vertex_hash;
- GHashTable *combined_hash;
+ CoglPipelineHashTable fragment_hash;
+ CoglPipelineHashTable vertex_hash;
+ CoglPipelineHashTable combined_hash;
};
-static unsigned int
-pipeline_fragment_hash (const void *data)
-{
- unsigned int fragment_state;
- unsigned int layer_fragment_state;
-
- _COGL_GET_CONTEXT (ctx, 0);
-
- fragment_state =
- _cogl_pipeline_get_state_for_fragment_codegen (ctx);
- layer_fragment_state =
- _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
-
- return _cogl_pipeline_hash ((CoglPipeline *)data,
- fragment_state, layer_fragment_state,
- 0);
-}
-
-static CoglBool
-pipeline_fragment_equal (const void *a, const void *b)
+CoglPipelineCache *
+_cogl_pipeline_cache_new (void)
{
+ CoglPipelineCache *cache = g_new (CoglPipelineCache, 1);
+ unsigned long vertex_state;
+ unsigned long layer_vertex_state;
unsigned int fragment_state;
unsigned int layer_fragment_state;
_COGL_GET_CONTEXT (ctx, 0);
+ vertex_state =
+ COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
+ layer_vertex_state =
+ COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
fragment_state =
_cogl_pipeline_get_state_for_fragment_codegen (ctx);
layer_fragment_state =
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
- return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b,
- fragment_state, layer_fragment_state,
- 0);
-}
-
-static unsigned int
-pipeline_vertex_hash (const void *data)
-{
- unsigned long vertex_state =
- COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
- unsigned long layer_vertex_state =
- COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
-
- return _cogl_pipeline_hash ((CoglPipeline *)data,
- vertex_state, layer_vertex_state,
- 0);
-}
-
-static CoglBool
-pipeline_vertex_equal (const void *a, const void *b)
-{
- unsigned long vertex_state =
- COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
- unsigned long layer_vertex_state =
- COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
-
- return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b,
- vertex_state, layer_vertex_state,
- 0);
-}
-
-static unsigned int
-pipeline_combined_hash (const void *data)
-{
- unsigned int combined_state;
- unsigned int layer_combined_state;
-
- _COGL_GET_CONTEXT (ctx, 0);
-
- combined_state =
- _cogl_pipeline_get_state_for_fragment_codegen (ctx) |
- COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
- layer_combined_state =
- _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) |
- COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
-
- return _cogl_pipeline_hash ((CoglPipeline *)data,
- combined_state, layer_combined_state,
- 0);
-}
-
-static CoglBool
-pipeline_combined_equal (const void *a, const void *b)
-{
- unsigned int combined_state;
- unsigned int layer_combined_state;
-
- _COGL_GET_CONTEXT (ctx, 0);
-
- combined_state =
- _cogl_pipeline_get_state_for_fragment_codegen (ctx) |
- COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
- layer_combined_state =
- _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) |
- COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
-
- return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b,
- combined_state, layer_combined_state,
- 0);
-}
-
-CoglPipelineCache *
-_cogl_pipeline_cache_new (void)
-{
- CoglPipelineCache *cache = g_new (CoglPipelineCache, 1);
-
- cache->fragment_hash = g_hash_table_new_full (pipeline_fragment_hash,
- pipeline_fragment_equal,
- cogl_object_unref,
- cogl_object_unref);
- cache->vertex_hash = g_hash_table_new_full (pipeline_vertex_hash,
- pipeline_vertex_equal,
- cogl_object_unref,
- cogl_object_unref);
- cache->combined_hash = g_hash_table_new_full (pipeline_combined_hash,
- pipeline_combined_equal,
- cogl_object_unref,
- cogl_object_unref);
+ _cogl_pipeline_hash_table_init (&cache->vertex_hash,
+ vertex_state,
+ layer_vertex_state,
+ "vertex shaders");
+ _cogl_pipeline_hash_table_init (&cache->fragment_hash,
+ fragment_state,
+ layer_fragment_state,
+ "fragment shaders");
+ _cogl_pipeline_hash_table_init (&cache->combined_hash,
+ vertex_state | fragment_state,
+ layer_vertex_state | layer_fragment_state,
+ "programs");
return cache;
}
@@ -166,9 +80,9 @@ _cogl_pipeline_cache_new (void)
void
_cogl_pipeline_cache_free (CoglPipelineCache *cache)
{
- g_hash_table_destroy (cache->fragment_hash);
- g_hash_table_destroy (cache->vertex_hash);
- g_hash_table_destroy (cache->combined_hash);
+ _cogl_pipeline_hash_table_destroy (&cache->fragment_hash);
+ _cogl_pipeline_hash_table_destroy (&cache->vertex_hash);
+ _cogl_pipeline_hash_table_destroy (&cache->combined_hash);
g_free (cache);
}
@@ -176,107 +90,22 @@ CoglPipeline *
_cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline)
{
- CoglPipeline *template =
- g_hash_table_lookup (cache->fragment_hash, key_pipeline);
-
- if (template == NULL)
- {
- /* XXX: I wish there was a way to insert into a GHashTable with
- * a pre-calculated hash value since there is a cost to
- * calculating the hash of a CoglPipeline and in this case we
- * know we have already called _cogl_pipeline_hash during the
- * lookup so we could pass the value through to here to avoid
- * hashing it again.
- */
-
- /* XXX: Any keys referenced by the hash table need to remain
- * valid all the while that there are corresponding values,
- * so for now we simply make a copy of the current authority
- * pipeline.
- *
- * FIXME: A problem with this is that our key into the cache may
- * hold references to some arbitrary user textures which will
- * now be kept alive indefinitly which is a shame. A better
- * solution will be to derive a special "key pipeline" from the
- * authority which derives from the base Cogl pipeline (to avoid
- * affecting the lifetime of any other pipelines) and only takes
- * a copy of the state that relates to the fragment shader and
- * references small dummy textures instead of potentially large
- * user textures. */
- template = cogl_pipeline_copy (key_pipeline);
-
- g_hash_table_insert (cache->fragment_hash,
- template,
- cogl_object_ref (template));
-
- if (G_UNLIKELY (g_hash_table_size (cache->fragment_hash) > 50))
- {
- static CoglBool seen = FALSE;
- if (!seen)
- g_warning ("Over 50 separate fragment shaders have been "
- "generated which is very unusual, so something "
- "is probably wrong!\n");
- seen = TRUE;
- }
- }
-
- return template;
+ return _cogl_pipeline_hash_table_get (&cache->fragment_hash,
+ key_pipeline);
}
CoglPipeline *
_cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline)
{
- CoglPipeline *template =
- g_hash_table_lookup (cache->vertex_hash, key_pipeline);
-
- if (template == NULL)
- {
- template = cogl_pipeline_copy (key_pipeline);
-
- g_hash_table_insert (cache->vertex_hash,
- template,
- cogl_object_ref (template));
-
- if (G_UNLIKELY (g_hash_table_size (cache->vertex_hash) > 50))
- {
- static CoglBool seen = FALSE;
- if (!seen)
- g_warning ("Over 50 separate vertex shaders have been "
- "generated which is very unusual, so something "
- "is probably wrong!\n");
- seen = TRUE;
- }
- }
-
- return template;
+ return _cogl_pipeline_hash_table_get (&cache->vertex_hash,
+ key_pipeline);
}
CoglPipeline *
_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline)
{
- CoglPipeline *template =
- g_hash_table_lookup (cache->combined_hash, key_pipeline);
-
- if (template == NULL)
- {
- template = cogl_pipeline_copy (key_pipeline);
-
- g_hash_table_insert (cache->combined_hash,
- template,
- cogl_object_ref (template));
-
- if (G_UNLIKELY (g_hash_table_size (cache->combined_hash) > 50))
- {
- static CoglBool seen = FALSE;
- if (!seen)
- g_warning ("Over 50 separate programs have been "
- "generated which is very unusual, so something "
- "is probably wrong!\n");
- seen = TRUE;
- }
- }
-
- return template;
+ return _cogl_pipeline_hash_table_get (&cache->combined_hash,
+ key_pipeline);
}
diff --git a/cogl/cogl-pipeline-hash-table.c b/cogl/cogl-pipeline-hash-table.c
new file mode 100644
index 0000000..8921efc
--- /dev/null
+++ b/cogl/cogl-pipeline-hash-table.c
@@ -0,0 +1,153 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Authors:
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-context-private.h"
+#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-hash-table.h"
+
+typedef struct
+{
+ /* The template pipeline */
+ CoglPipeline *pipeline;
+
+ /* Calculating the hash is a little bit expensive for pipelines so
+ * we don't want to do it repeatedly for entries that are already in
+ * the hash table. Instead we cache the value here and calculate it
+ * outside of the GHashTable. */
+ unsigned int hash_value;
+
+ /* GHashTable annoyingly doesn't let us pass a user data pointer to
+ * the hash and equal functions so to work around it we have to
+ * store the pointer in every hash table entry. We will use this
+ * entry as both the key and the value */
+ CoglPipelineHashTable *hash;
+} CoglPipelineHashTableEntry;
+
+static void
+value_destroy_cb (void *value)
+{
+ CoglPipelineHashTableEntry *entry = value;
+
+ cogl_object_unref (entry->pipeline);
+
+ g_slice_free (CoglPipelineHashTableEntry, entry);
+}
+
+static unsigned int
+entry_hash (const void *data)
+{
+ const CoglPipelineHashTableEntry *entry = data;
+
+ return entry->hash_value;
+}
+
+static CoglBool
+entry_equal (const void *a,
+ const void *b)
+{
+ const CoglPipelineHashTableEntry *entry_a = a;
+ const CoglPipelineHashTableEntry *entry_b = b;
+ const CoglPipelineHashTable *hash = entry_a->hash;
+
+ return _cogl_pipeline_equal (entry_a->pipeline,
+ entry_b->pipeline,
+ hash->main_state,
+ hash->layer_state,
+ 0);
+}
+
+void
+_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash,
+ unsigned int main_state,
+ unsigned int layer_state,
+ const char *debug_string)
+{
+ hash->n_unique_pipelines = 0;
+ hash->debug_string = debug_string;
+ hash->main_state = main_state;
+ hash->layer_state = layer_state;
+ hash->table = g_hash_table_new_full (entry_hash,
+ entry_equal,
+ NULL, /* key destroy */
+ value_destroy_cb);
+}
+
+void
+_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash)
+{
+ g_hash_table_destroy (hash->table);
+}
+
+CoglPipeline *
+_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash,
+ CoglPipeline *key_pipeline)
+{
+ CoglPipelineHashTableEntry dummy_entry;
+ CoglPipelineHashTableEntry *entry;
+ unsigned int copy_state;
+
+ dummy_entry.pipeline = key_pipeline;
+ dummy_entry.hash = hash;
+ dummy_entry.hash_value = _cogl_pipeline_hash (key_pipeline,
+ hash->main_state,
+ hash->layer_state,
+ 0);
+ entry = g_hash_table_lookup (hash->table, &dummy_entry);
+
+ if (entry)
+ return entry->pipeline;
+
+ if (hash->n_unique_pipelines == 50)
+ g_warning ("Over 50 separate %s have been generated which is very "
+ "unusual, so something is probably wrong!\n",
+ hash->debug_string);
+
+ entry = g_slice_new (CoglPipelineHashTableEntry);
+ entry->hash = hash;
+ entry->hash_value = dummy_entry.hash_value;
+
+ copy_state = hash->main_state;
+ if (hash->layer_state)
+ copy_state |= COGL_PIPELINE_STATE_LAYERS;
+
+ /* Create a new pipeline that is a child of the root pipeline
+ * instead of a normal copy so that the template pipeline won't hold
+ * a reference to the original pipeline */
+ entry->pipeline = _cogl_pipeline_deep_copy (key_pipeline,
+ copy_state,
+ hash->layer_state);
+
+ g_hash_table_insert (hash->table, entry, entry);
+
+ hash->n_unique_pipelines++;
+
+ return entry->pipeline;
+}
diff --git a/cogl/cogl-pipeline-hash-table.h b/cogl/cogl-pipeline-hash-table.h
new file mode 100644
index 0000000..1b0a0d9
--- /dev/null
+++ b/cogl/cogl-pipeline-hash-table.h
@@ -0,0 +1,69 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef __COGL_PIPELINE_HASH_H__
+#define __COGL_PIPELINE_HASH_H__
+
+#include "cogl-pipeline.h"
+
+typedef struct
+{
+ /* Total number of pipelines that were ever added to the hash. This
+ * is not decremented when a pipeline is removed. It is only used to
+ * generate a warning if an unusually high number of pipelines are
+ * generated */
+ int n_unique_pipelines;
+
+ /* String that will be used to describe the usage of this hash table
+ * in the debug warning when too many pipelines are generated. This
+ * must be a static string because it won't be copied or freed */
+ const char *debug_string;
+
+ unsigned int main_state;
+ unsigned int layer_state;
+
+ GHashTable *table;
+} CoglPipelineHashTable;
+
+void
+_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash,
+ unsigned int main_state,
+ unsigned int layer_state,
+ const char *debug_string);
+
+void
+_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash);
+
+/*
+ * Gets a pipeline from the hash that has the same state as
+ * @key_pipeline according to the limited state bits passed to
+ * _cogl_pipeline_hash_table_init(). If there is no matching pipelines
+ * already then a copy of key_pipeline is stored in the hash so that
+ * it will be used next time the function is called with a similar
+ * pipeline. In that case the copy itself will be returned
+ */
+CoglPipeline *
+_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash,
+ CoglPipeline *key_pipeline);
+
+#endif /* __COGL_PIPELINE_HASH_H__ */
diff --git a/cogl/cogl-pipeline-layer-private.h b/cogl/cogl-pipeline-layer-private.h
index 125b967..7577559 100644
--- a/cogl/cogl-pipeline-layer-private.h
+++ b/cogl/cogl-pipeline-layer-private.h
@@ -358,6 +358,11 @@ _cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer);
CoglPipelineWrapMode
_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer);
+void
+_cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest,
+ CoglPipelineLayer *src,
+ unsigned long differences);
+
unsigned long
_cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0,
CoglPipelineLayer *layer1);
diff --git a/cogl/cogl-pipeline-layer.c b/cogl/cogl-pipeline-layer.c
index d9590c8..9bc26ef 100644
--- a/cogl/cogl-pipeline-layer.c
+++ b/cogl/cogl-pipeline-layer.c
@@ -42,6 +42,8 @@
#include "cogl-context-private.h"
#include "cogl-texture-private.h"
+#include <string.h>
+
static void
_cogl_pipeline_layer_free (CoglPipelineLayer *layer);
@@ -146,6 +148,107 @@ _cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func)
return 0;
}
+void
+_cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest,
+ CoglPipelineLayer *src,
+ unsigned long differences)
+{
+ CoglPipelineLayerBigState *big_dest, *big_src;
+
+ if ((differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) &&
+ !dest->has_big_state)
+ {
+ dest->big_state = g_slice_new (CoglPipelineLayerBigState);
+ dest->has_big_state = TRUE;
+ }
+
+ big_dest = dest->big_state;
+ big_src = src->big_state;
+
+ dest->differences |= differences;
+
+ while (differences)
+ {
+ int index = _cogl_util_ffs (differences) - 1;
+
+ differences &= ~(1 << index);
+
+ /* This convoluted switch statement is just here so that we'll
+ * get a warning if a new state is added without handling it
+ * here */
+ switch (index)
+ {
+ case COGL_PIPELINE_LAYER_STATE_COUNT:
+ case COGL_PIPELINE_LAYER_STATE_UNIT_INDEX:
+ g_warn_if_reached ();
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX:
+ dest->texture_type = src->texture_type;
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX:
+ dest->texture = src->texture;
+ if (dest->texture)
+ cogl_object_ref (dest->texture);
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX:
+ dest->sampler_cache_entry = src->sampler_cache_entry;
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX:
+ {
+ CoglPipelineCombineFunc func;
+ int n_args, i;
+
+ func = big_src->texture_combine_rgb_func;
+ big_dest->texture_combine_rgb_func = func;
+ n_args = _cogl_get_n_args_for_combine_func (func);
+ for (i = 0; i < n_args; i++)
+ {
+ big_dest->texture_combine_rgb_src[i] =
+ big_src->texture_combine_rgb_src[i];
+ big_dest->texture_combine_rgb_op[i] =
+ big_src->texture_combine_rgb_op[i];
+ }
+
+ func = big_src->texture_combine_alpha_func;
+ big_dest->texture_combine_alpha_func = func;
+ n_args = _cogl_get_n_args_for_combine_func (func);
+ for (i = 0; i < n_args; i++)
+ {
+ big_dest->texture_combine_alpha_src[i] =
+ big_src->texture_combine_alpha_src[i];
+ big_dest->texture_combine_alpha_op[i] =
+ big_src->texture_combine_alpha_op[i];
+ }
+ }
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX:
+ memcpy (big_dest->texture_combine_constant,
+ big_src->texture_combine_constant,
+ sizeof (big_dest->texture_combine_constant));
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX:
+ big_dest->point_sprite_coords = big_src->point_sprite_coords;
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX:
+ _cogl_pipeline_snippet_list_copy (&big_dest->vertex_snippets,
+ &big_src->vertex_snippets);
+ break;
+
+ case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX:
+ _cogl_pipeline_snippet_list_copy (&big_dest->fragment_snippets,
+ &big_src->fragment_snippets);
+ break;
+ }
+ }
+}
+
static void
_cogl_pipeline_layer_init_multi_property_sparse_state (
CoglPipelineLayer *layer,
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index 56700b5..acb5653 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -845,6 +845,17 @@ _cogl_pipeline_hash (CoglPipeline *pipeline,
unsigned long layer_differences,
CoglPipelineEvalFlags flags);
+/* Makes a copy of the given pipeline that is a child of the root
+ * pipeline rather than a child of the source pipeline. That way the
+ * new pipeline won't hold a reference to the source pipeline. The
+ * differences specified in @differences and @layer_differences are
+ * copied across and all other state is left with the default
+ * values. */
+CoglPipeline *
+_cogl_pipeline_deep_copy (CoglPipeline *pipeline,
+ unsigned long differences,
+ unsigned long layer_differences);
+
CoglPipeline *
_cogl_pipeline_journal_ref (CoglPipeline *pipeline);
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index c029f45..a91ad25 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -2771,6 +2771,97 @@ _cogl_pipeline_hash (CoglPipeline *pipeline,
typedef struct
{
+ CoglContext *context;
+ CoglPipeline *src_pipeline;
+ CoglPipeline *dst_pipeline;
+ unsigned int layer_differences;
+} DeepCopyData;
+
+static CoglBool
+deep_copy_layer_cb (CoglPipelineLayer *src_layer,
+ void *user_data)
+{
+ DeepCopyData *data = user_data;
+ CoglPipelineLayer *dst_layer;
+ unsigned int differences = data->layer_differences;
+
+ dst_layer = _cogl_pipeline_get_layer (data->dst_pipeline, src_layer->index);
+
+ while (src_layer != data->context->default_layer_n &&
+ src_layer != data->context->default_layer_0 &&
+ differences)
+ {
+ unsigned long to_copy = differences & src_layer->differences;
+
+ if (to_copy)
+ {
+ _cogl_pipeline_layer_copy_differences (dst_layer, src_layer, to_copy);
+ differences ^= to_copy;
+ }
+
+ src_layer = COGL_PIPELINE_LAYER (COGL_NODE (src_layer)->parent);
+ }
+
+ return TRUE;
+}
+
+CoglPipeline *
+_cogl_pipeline_deep_copy (CoglPipeline *pipeline,
+ unsigned long differences,
+ unsigned long layer_differences)
+{
+ CoglPipeline *new, *authority;
+ CoglBool copy_layer_state;
+
+ _COGL_GET_CONTEXT (ctx, NULL);
+
+ if ((differences & COGL_PIPELINE_STATE_LAYERS))
+ {
+ copy_layer_state = TRUE;
+ differences &= ~COGL_PIPELINE_STATE_LAYERS;
+ }
+ else
+ copy_layer_state = FALSE;
+
+ new = cogl_pipeline_new (ctx);
+
+ for (authority = pipeline;
+ authority != ctx->default_pipeline && differences;
+ authority = COGL_PIPELINE (COGL_NODE (authority)->parent))
+ {
+ unsigned long to_copy = differences & authority->differences;
+
+ if (to_copy)
+ {
+ _cogl_pipeline_copy_differences (new, authority, to_copy);
+ differences ^= to_copy;
+ }
+ }
+
+ if (copy_layer_state)
+ {
+ DeepCopyData data;
+
+ /* The unit index doesn't need to be copied because it should
+ * end up with the same values anyway because the new pipeline
+ * will have the same indices as the source pipeline */
+ layer_differences &= ~COGL_PIPELINE_LAYER_STATE_UNIT;
+
+ data.context = ctx;
+ data.src_pipeline = pipeline;
+ data.dst_pipeline = new;
+ data.layer_differences = layer_differences;
+
+ _cogl_pipeline_foreach_layer_internal (pipeline,
+ deep_copy_layer_cb,
+ &data);
+ }
+
+ return new;
+}
+
+typedef struct
+{
int i;
CoglPipelineLayer **layers;
} AddLayersToArrayState;
diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c
index 18c0fe6..eb1f51a 100644
--- a/cogl/cogl-xlib-renderer.c
+++ b/cogl/cogl-xlib-renderer.c
@@ -238,7 +238,7 @@ update_outputs (CoglRenderer *renderer,
_cogl_xlib_renderer_trap_errors (renderer, &state);
- for (i = 0; i < resources->ncrtc && !error; i++)
+ for (i = 0; resources && i < resources->ncrtc && !error; i++)
{
XRRCrtcInfo *crtc_info = NULL;
XRROutputInfo *output_info = NULL;
diff --git a/cogl/cogl-xlib.h b/cogl/cogl-xlib.h
index 7a6bc7e..5dab8ae 100644
--- a/cogl/cogl-xlib.h
+++ b/cogl/cogl-xlib.h
@@ -79,6 +79,8 @@ cogl_xlib_set_display (Display *display);
CoglFilterReturn
cogl_xlib_handle_event (XEvent *xevent);
+COGL_END_DECLS
+
#undef __COGL_XLIB_H_INSIDE__
#endif /* __COGL_XLIB_H__ */
diff --git a/configure.ac b/configure.ac
index 43bf407..4ba85b8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,7 +25,7 @@ m4_define([cogl_version],
dnl Since the core Cogl library has to also maintain support for the
dnl Cogl 1.x API for Clutter then we track the 1.x version separately.
m4_define([cogl_1_minor_version], [14])
-m4_define([cogl_1_micro_version], [0])
+m4_define([cogl_1_micro_version], [1])
m4_define([cogl_1_version], [1.cogl_1_minor_version.cogl_1_micro_version])
dnl ================================================================
@@ -70,7 +70,7 @@ dnl ================================================================
# libtool version info we don't automatically derive this from the
# pretty version number because we want to test the results of
# updating the version number in advance of a release.
-m4_define([cogl_release_status], [release])
+m4_define([cogl_release_status], [git])
AC_INIT(cogl, [cogl_1_version])
AC_CONFIG_SRCDIR(cogl/cogl.h)
@@ -178,6 +178,12 @@ dnl internal glib configure (as-glibconfig.m4)
m4_ifdef([LT_OUTPUT], [LT_OUTPUT])
dnl ================================================================
+dnl Find an appropriate libm, for sin() etc.
+dnl ================================================================
+LT_LIB_M
+AC_SUBST(LIBM)
+
+dnl ================================================================
dnl See what platform we are building for
dnl ================================================================
AC_CANONICAL_HOST
@@ -474,6 +480,7 @@ AS_IF(
EXPERIMENTAL_OPTIONS="$EXPERIMENTAL_OPTIONS Quartz Core Graphics,"
AC_DEFINE([USE_QUARTZ], 1,
[Use Core Graphics (Quartz) for loading image data])
+ COGL_EXTRA_LDFLAGS="$COGL_EXTRA_LDFLAGS -framework ApplicationServices"
COGL_IMAGE_BACKEND="quartz"
],
[
@@ -1173,6 +1180,12 @@ AC_CHECK_FUNCS([ffs])
dnl 'memmem' is a GNU extension but we have a simple fallback
AC_CHECK_FUNCS([memmem])
+dnl This is used in the cogl-gles2-gears example but it is a GNU extension
+save_libs="$LIBS"
+LIBS="$LIBS $LIBM"
+AC_CHECK_FUNCS([sincos])
+LIBS="$save_libs"
+
dnl ================================================================
dnl Platform values
dnl ================================================================
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 86801c6..ae3e5f7 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -20,7 +20,8 @@ endif
common_ldadd = \
$(COGL_DEP_LIBS) \
- $(top_builddir)/cogl/libcogl.la
+ $(top_builddir)/cogl/libcogl.la \
+ $(LIBM)
if !USE_GLIB
common_ldadd += $(top_builddir)/deps/glib/libglib.la
diff --git a/examples/android/hello/jni/main.c b/examples/android/hello/jni/main.c
index 2c5bd9b..c9a8401 100644
--- a/examples/android/hello/jni/main.c
+++ b/examples/android/hello/jni/main.c
@@ -42,7 +42,7 @@ static int test_init (TestData* data)
CoglOnscreen *onscreen;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-gles2-context.c b/examples/cogl-gles2-context.c
index 1cf375f..de66c21 100644
--- a/examples/cogl-gles2-context.c
+++ b/examples/cogl-gles2-context.c
@@ -70,7 +70,7 @@ main (int argc, char **argv)
CoglOnscreen *onscreen;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-gles2-gears.c b/examples/cogl-gles2-gears.c
index d7dd271..c7185b6 100644
--- a/examples/cogl-gles2-gears.c
+++ b/examples/cogl-gles2-gears.c
@@ -35,6 +35,10 @@
* Jul 13, 2010
*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#define GL_GLEXT_PROTOTYPES
#include <math.h>
@@ -110,6 +114,15 @@ static GLfloat ProjectionMatrix[16];
/** The direction of the directional light for the scene */
static const GLfloat LightSourcePosition[4] = { 5.0, 5.0, 10.0, 1.0};
+#ifndef HAVE_SINCOS
+static void
+sincos (double x, double *sinx, double *cosx)
+{
+ *sinx = sin (x);
+ *cosx = cos (x);
+}
+#endif /* HAVE_SINCOS */
+
/**
* Fills a gear vertex.
*
diff --git a/examples/cogl-hello.c b/examples/cogl-hello.c
index 5bda9bf..3ba1e31 100644
--- a/examples/cogl-hello.c
+++ b/examples/cogl-hello.c
@@ -39,7 +39,7 @@ main (int argc, char **argv)
CoglOnscreen *onscreen;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-msaa.c b/examples/cogl-msaa.c
index 73f9c4e..4a388bc 100644
--- a/examples/cogl-msaa.c
+++ b/examples/cogl-msaa.c
@@ -12,7 +12,7 @@ main (int argc, char **argv)
CoglFramebuffer *fb;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-sdl-hello.c b/examples/cogl-sdl-hello.c
index 961137a..acb9125 100644
--- a/examples/cogl-sdl-hello.c
+++ b/examples/cogl-sdl-hello.c
@@ -80,7 +80,7 @@ main (int argc, char **argv)
CoglOnscreen *onscreen;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-sdl2-hello.c b/examples/cogl-sdl2-hello.c
index 405cb92..12e6ced 100644
--- a/examples/cogl-sdl2-hello.c
+++ b/examples/cogl-sdl2-hello.c
@@ -89,7 +89,7 @@ main (int argc, char **argv)
CoglOnscreen *onscreen;
CoglError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogl-x11-foreign.c b/examples/cogl-x11-foreign.c
index ca9e3ed..a60397c 100644
--- a/examples/cogl-x11-foreign.c
+++ b/examples/cogl-x11-foreign.c
@@ -61,7 +61,7 @@ main (int argc, char **argv)
unsigned long mask;
Window xwin;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
diff --git a/examples/cogland.c b/examples/cogland.c
index c18850a..7a02719 100644
--- a/examples/cogland.c
+++ b/examples/cogland.c
@@ -93,7 +93,6 @@ struct _CoglandCompositor
struct wl_display *wayland_display;
struct wl_event_loop *wayland_loop;
- CoglDisplay *cogl_display;
CoglContext *cogl_context;
int virtual_width;
@@ -336,15 +335,16 @@ cogland_queue_redraw (CoglandCompositor *compositor)
}
static void
-shm_buffer_damaged (CoglandSurface *surface,
- int32_t x,
- int32_t y,
- int32_t width,
- int32_t height)
+surface_damaged (CoglandSurface *surface,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height)
{
struct wl_buffer *wayland_buffer = surface->buffer;
- if (surface->texture)
+ if (surface->texture &&
+ wl_buffer_is_shm (surface->buffer))
{
CoglPixelFormat format;
int stride = wl_shm_buffer_get_stride (wayland_buffer);
@@ -381,6 +381,8 @@ shm_buffer_damaged (CoglandSurface *surface,
stride,
data);
}
+
+ cogland_queue_redraw (surface->compositor);
}
static void
@@ -546,16 +548,18 @@ cogland_surface_commit (struct wl_client *client,
wl_signal_add (&surface->buffer->resource.destroy_signal,
&surface->buffer_destroy_listener);
- wl_list_remove (&surface->pending.buffer_destroy_listener.link);
}
}
- surface->pending.buffer = NULL;
+ if (surface->pending.buffer)
+ {
+ wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+ surface->pending.buffer = NULL;
+ }
surface->pending.sx = 0;
surface->pending.sy = 0;
/* wl_surface.damage */
if (surface->buffer &&
- wl_buffer_is_shm (surface->buffer) &&
surface->texture &&
!region_is_empty (&surface->pending.damage))
{
@@ -571,11 +575,11 @@ cogland_surface_commit (struct wl_client *client,
if (region->y1 < 0)
region->y1 = 0;
- shm_buffer_damaged (surface,
- region->x1,
- region->y1,
- region->x2 - region->x1,
- region->y2 - region->y1);
+ surface_damaged (surface,
+ region->x1,
+ region->y1,
+ region->x2 - region->x1,
+ region->y2 - region->y1);
}
region_init (&surface->pending.damage);
@@ -583,8 +587,6 @@ cogland_surface_commit (struct wl_client *client,
wl_list_insert_list (&compositor->frame_callbacks,
&surface->pending.frame_callback_list);
wl_list_init (&surface->pending.frame_callback_list);
-
- cogland_queue_redraw (compositor);
}
static void
@@ -614,6 +616,9 @@ cogland_surface_free (CoglandSurface *surface)
compositor->surfaces = g_list_remove (compositor->surfaces, surface);
cogland_surface_detach_buffer_and_notify (surface);
+ if (surface->pending.buffer)
+ wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+
wl_list_for_each_safe (cb, next,
&surface->pending.frame_callback_list, link)
wl_resource_destroy (&cb->resource);
@@ -970,7 +975,7 @@ get_shell_surface (struct wl_client *client,
struct wl_resource *surface_resource)
{
CoglandSurface *surface = surface_resource->data;
- CoglandShellSurface *shell_surface = g_new0 (CoglandShellSurface, 1);
+ CoglandShellSurface *shell_surface;
if (surface->has_shell_surface)
{
@@ -980,6 +985,7 @@ get_shell_surface (struct wl_client *client,
return;
}
+ shell_surface = g_new0 (CoglandShellSurface, 1);
shell_surface->resource.destroy = destroy_shell_surface;
shell_surface->resource.object.id = id;
shell_surface->resource.object.interface = &wl_shell_surface_interface;
@@ -1012,6 +1018,36 @@ bind_shell (struct wl_client *client,
&cogland_shell_interface, id, data);
}
+static CoglContext *
+create_cogl_context (CoglandCompositor *compositor,
+ CoglBool use_egl_constraint,
+ CoglError **error)
+{
+ CoglRenderer *renderer = renderer = cogl_renderer_new ();
+ CoglDisplay *display;
+ CoglContext *context;
+
+ if (use_egl_constraint)
+ cogl_renderer_add_constraint (renderer, COGL_RENDERER_CONSTRAINT_USES_EGL);
+
+ if (!cogl_renderer_connect (renderer, error))
+ {
+ cogl_object_unref (renderer);
+ return NULL;
+ }
+
+ display = cogl_display_new (renderer, NULL);
+ cogl_wayland_display_set_compositor_display (display,
+ compositor->wayland_display);
+
+ context = cogl_context_new (display, error);
+
+ cogl_object_unref (renderer);
+ cogl_object_unref (display);
+
+ return context;
+}
+
int
main (int argc, char **argv)
{
@@ -1020,7 +1056,7 @@ main (int argc, char **argv)
CoglError *error = NULL;
GError *gerror = NULL;
CoglVertexP2C4 triangle_vertices[] = {
- {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+ {0, 0.7, 0xff, 0x00, 0x00, 0xff},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
@@ -1055,13 +1091,30 @@ main (int argc, char **argv)
wayland_event_source_new (compositor.wayland_display);
g_source_attach (compositor.wayland_event_source, NULL);
- compositor.cogl_display = cogl_display_new (NULL, NULL);
- cogl_wayland_display_set_compositor_display (compositor.cogl_display,
- compositor.wayland_display);
-
- compositor.cogl_context = cogl_context_new (compositor.cogl_display, &error);
- if (!compositor.cogl_context)
- g_error ("Failed to create a Cogl context: %s\n", error->message);
+ /* We want Cogl to use an EGL renderer because otherwise it won't
+ * set up the wl_drm object and only SHM buffers will work. */
+ compositor.cogl_context =
+ create_cogl_context (&compositor,
+ TRUE /* use EGL constraint */,
+ &error);
+ if (compositor.cogl_context == NULL)
+ {
+ /* If we couldn't get an EGL context then try any type of
+ * context */
+ cogl_error_free (error);
+ error = NULL;
+
+ compositor.cogl_context =
+ create_cogl_context (&compositor,
+ FALSE, /* don't set EGL constraint */
+ &error);
+
+ if (compositor.cogl_context)
+ g_warning ("Failed to create context with EGL constraint, "
+ "falling back");
+ else
+ g_error ("Failed to create a Cogl context: %s\n", error->message);
+ }
compositor.virtual_width = 800;
compositor.virtual_height = 600;
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index 69a460d..9782755 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -65,6 +65,7 @@ test_sources = \
test-framebuffer-get-bits.c \
test-primitive-and-journal.c \
test-copy-replace-texture.c \
+ test-pipeline-cache-unrefs-texture.c \
$(NULL)
test_conformance_SOURCES = $(common_sources) $(test_sources)
@@ -131,7 +132,10 @@ AM_CPPFLAGS += \
-DCOGL_COMPILATION
test_conformance_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
-test_conformance_LDADD = $(COGL_DEP_LIBS) $(top_builddir)/cogl/libcogl.la
+test_conformance_LDADD = \
+ $(COGL_DEP_LIBS) \
+ $(top_builddir)/cogl/libcogl.la \
+ $(LIBM)
if !USE_GLIB
test_conformance_LDADD += $(top_builddir)/deps/glib/libglib.la
endif
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
index 0b55db6..1d1447e 100644
--- a/tests/conform/test-conform-main.c
+++ b/tests/conform/test-conform-main.c
@@ -120,6 +120,8 @@ main (int argc, char **argv)
ADD_TEST (test_copy_replace_texture, 0, 0);
+ ADD_TEST (test_pipeline_cache_unrefs_texture, 0, 0);
+
UNPORTED_TEST (test_viewport);
ADD_TEST (test_gles2_context, TEST_REQUIREMENT_GLES2_CONTEXT, 0);
diff --git a/tests/conform/test-pipeline-cache-unrefs-texture.c b/tests/conform/test-pipeline-cache-unrefs-texture.c
new file mode 100644
index 0000000..ccd02e7
--- /dev/null
+++ b/tests/conform/test-pipeline-cache-unrefs-texture.c
@@ -0,0 +1,92 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+/* Keep track of the number of textures that we've created and are
+ * still alive */
+static int destroyed_texture_count = 0;
+
+#define N_TEXTURES 3
+
+static void
+free_texture_cb (void *user_data)
+{
+ destroyed_texture_count++;
+}
+
+static CoglTexture *
+create_texture (void)
+{
+ static const guint8 data[] =
+ { 0xff, 0xff, 0xff, 0xff };
+ static CoglUserDataKey texture_data_key;
+ CoglTexture2D *tex_2d;
+
+ tex_2d = cogl_texture_2d_new_from_data (test_ctx,
+ 1, 1, /* width / height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ 4, /* rowstride */
+ data,
+ NULL);
+
+ /* Set some user data on the texture so we can track when it has
+ * been destroyed */
+ cogl_object_set_user_data (COGL_OBJECT (tex_2d),
+ &texture_data_key,
+ GINT_TO_POINTER (1),
+ free_texture_cb);
+
+ return COGL_TEXTURE (tex_2d);
+}
+
+void
+test_pipeline_cache_unrefs_texture (void)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ CoglPipeline *simple_pipeline;
+ int i;
+
+ /* Create a pipeline with three texture layers. That way we can be
+ * pretty sure the pipeline will cause a unique shader to be
+ * generated in the cache */
+ for (i = 0; i < N_TEXTURES; i++)
+ {
+ CoglTexture *tex = create_texture ();
+ cogl_pipeline_set_layer_texture (pipeline, i, tex);
+ cogl_object_unref (tex);
+ }
+
+ /* Draw something with the pipeline to ensure it gets into the
+ * pipeline cache */
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 0, 0, 10, 10);
+ cogl_framebuffer_finish (test_fb);
+
+ /* Draw something else so that it is no longer the current flushed
+ * pipeline, and the units have a different texture bound */
+ simple_pipeline = cogl_pipeline_new (test_ctx);
+ for (i = 0; i < N_TEXTURES; i++)
+ {
+ CoglColor combine_constant;
+ cogl_color_init_from_4ub (&combine_constant, i, 0, 0, 255);
+ cogl_pipeline_set_layer_combine_constant (simple_pipeline,
+ i,
+ &combine_constant);
+ }
+ cogl_framebuffer_draw_rectangle (test_fb, simple_pipeline, 0, 0, 10, 10);
+ cogl_framebuffer_finish (test_fb);
+ cogl_object_unref (simple_pipeline);
+
+ g_assert_cmpint (destroyed_texture_count, ==, 0);
+
+ /* Destroy the pipeline. This should immediately cause the textures
+ * to be freed */
+ cogl_object_unref (pipeline);
+
+ g_assert_cmpint (destroyed_texture_count, ==, N_TEXTURES);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/micro-perf/Makefile.am b/tests/micro-perf/Makefile.am
index c221dd6..5c5f69d 100644
--- a/tests/micro-perf/Makefile.am
+++ b/tests/micro-perf/Makefile.am
@@ -19,5 +19,10 @@ endif
AM_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+common_ldadd = \
+ $(COGL_DEP_LIBS) \
+ $(top_builddir)/cogl/libcogl.la \
+ $(LIBM)
+
test_journal_SOURCES = test-journal.c
-test_journal_LDADD = $(COGL_DEP_LIBS) $(top_builddir)/cogl/libcogl.la
+test_journal_LDADD = $(common_ldadd)