summaryrefslogtreecommitdiff
path: root/cogl/tests
diff options
context:
space:
mode:
authorRui Matos <tiagomatos@gmail.com>2016-04-22 17:19:45 +0200
committerRui Matos <tiagomatos@gmail.com>2016-04-22 17:19:45 +0200
commit44a07fd74b8896556c719bd466eb9c6eb52ed2f4 (patch)
tree6d903af001ec7b994ca2261bfdf5a631a53d0ace /cogl/tests
parentd148b445e447d04bb56b77adaa9dbd5c89d7696f (diff)
parent2d2835f02a80cc34feb5c4384a6a75c2406d4381 (diff)
downloadmutter-44a07fd74b8896556c719bd466eb9c6eb52ed2f4.tar.gz
Merge cogl's cogl-1.22 branch into mutter
Diffstat (limited to 'cogl/tests')
-rw-r--r--cogl/tests/Makefile.am31
-rw-r--r--cogl/tests/README63
-rw-r--r--cogl/tests/config.env.in3
-rw-r--r--cogl/tests/conform/Makefile.am176
-rw-r--r--cogl/tests/conform/test-alpha-test.c73
-rw-r--r--cogl/tests/conform/test-alpha-textures.c123
-rw-r--r--cogl/tests/conform/test-atlas-migration.c145
-rw-r--r--cogl/tests/conform/test-backface-culling.c311
-rw-r--r--cogl/tests/conform/test-blend-strings.c430
-rw-r--r--cogl/tests/conform/test-blend.c64
-rw-r--r--cogl/tests/conform/test-color-hsl.c45
-rw-r--r--cogl/tests/conform/test-color-mask.c110
-rw-r--r--cogl/tests/conform/test-conform-main.c157
-rw-r--r--cogl/tests/conform/test-copy-replace-texture.c120
-rw-r--r--cogl/tests/conform/test-custom-attributes.c301
-rw-r--r--cogl/tests/conform/test-depth-test.c301
-rw-r--r--cogl/tests/conform/test-euler-quaternion.c81
-rw-r--r--cogl/tests/conform/test-fence.c63
-rw-r--r--cogl/tests/conform/test-fixed.c18
-rw-r--r--cogl/tests/conform/test-fixtures.c12
-rw-r--r--cogl/tests/conform/test-framebuffer-get-bits.c40
-rw-r--r--cogl/tests/conform/test-gles2-context.c962
-rw-r--r--cogl/tests/conform/test-just-vertex-shader.c205
-rw-r--r--cogl/tests/conform/test-layer-remove.c145
-rw-r--r--cogl/tests/conform/test-map-buffer-range.c123
-rw-r--r--cogl/tests/conform/test-materials.c253
-rw-r--r--cogl/tests/conform/test-multitexture.c206
-rw-r--r--cogl/tests/conform/test-no-gl-header.c16
-rw-r--r--cogl/tests/conform/test-npot-texture.c170
-rw-r--r--cogl/tests/conform/test-object.c86
-rw-r--r--cogl/tests/conform/test-offscreen.c199
-rw-r--r--cogl/tests/conform/test-path-clip.c68
-rw-r--r--cogl/tests/conform/test-path.c215
-rw-r--r--cogl/tests/conform/test-pipeline-cache-unrefs-texture.c91
-rw-r--r--cogl/tests/conform/test-pipeline-shader-state.c93
-rw-r--r--cogl/tests/conform/test-pipeline-uniforms.c415
-rw-r--r--cogl/tests/conform/test-pipeline-user-matrix.c140
-rw-r--r--cogl/tests/conform/test-pixel-buffer.c269
-rw-r--r--cogl/tests/conform/test-point-size-attribute.c166
-rw-r--r--cogl/tests/conform/test-point-size.c99
-rw-r--r--cogl/tests/conform/test-point-sprite.c194
-rw-r--r--cogl/tests/conform/test-premult.c301
-rw-r--r--cogl/tests/conform/test-primitive-and-journal.c122
-rw-r--r--cogl/tests/conform/test-primitive.c334
-rw-r--r--cogl/tests/conform/test-read-texture-formats.c222
-rw-r--r--cogl/tests/conform/test-readpixels.c178
-rw-r--r--cogl/tests/conform/test-snippets.c815
-rw-r--r--cogl/tests/conform/test-sparse-pipeline.c62
-rw-r--r--cogl/tests/conform/test-sub-texture.c325
-rw-r--r--cogl/tests/conform/test-texture-3d.c274
-rw-r--r--cogl/tests/conform/test-texture-get-set-data.c144
-rw-r--r--cogl/tests/conform/test-texture-mipmaps.c136
-rw-r--r--cogl/tests/conform/test-texture-no-allocate.c80
-rw-r--r--cogl/tests/conform/test-texture-pixmap-x11.c245
-rw-r--r--cogl/tests/conform/test-texture-rectangle.c276
-rw-r--r--cogl/tests/conform/test-texture-rg.c74
-rw-r--r--cogl/tests/conform/test-version.c85
-rw-r--r--cogl/tests/conform/test-vertex-buffer-contiguous.c257
-rw-r--r--cogl/tests/conform/test-vertex-buffer-interleved.c162
-rw-r--r--cogl/tests/conform/test-vertex-buffer-mutability.c198
-rw-r--r--cogl/tests/conform/test-viewport.c416
-rw-r--r--cogl/tests/conform/test-wrap-modes.c296
-rw-r--r--cogl/tests/conform/test-wrap-rectangle-textures.c175
-rw-r--r--cogl/tests/conform/test-write-texture-formats.c184
-rw-r--r--cogl/tests/data/Makefile.am3
-rw-r--r--cogl/tests/data/valgrind.suppressions173
-rw-r--r--cogl/tests/micro-perf/Makefile.am24
-rw-r--r--cogl/tests/micro-perf/test-journal.c190
-rwxr-xr-xcogl/tests/run-tests.sh157
-rwxr-xr-xcogl/tests/test-launcher.sh39
-rw-r--r--cogl/tests/unit/Makefile.am83
-rw-r--r--cogl/tests/unit/test-unit-main.c45
72 files changed, 12857 insertions, 0 deletions
diff --git a/cogl/tests/Makefile.am b/cogl/tests/Makefile.am
new file mode 100644
index 000000000..94ba34a6f
--- /dev/null
+++ b/cogl/tests/Makefile.am
@@ -0,0 +1,31 @@
+SUBDIRS = conform
+
+if UNIT_TESTS
+SUBDIRS += unit
+endif
+
+SUBDIRS += micro-perf data
+
+DIST_SUBDIRS = conform unit micro-perf data
+
+EXTRA_DIST = README test-launcher.sh run-tests.sh
+
+if UNIT_TESTS
+test conform:
+ ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+ ( cd ./unit && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+else
+test conform:
+ ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+endif
+
+.PHONY: test conform
+
+# run make test as part of make check
+check-local: test
+
+if ENABLE_INSTALLED_TESTS
+insttestdir = $(libexecdir)/installed-tests/$(PACKAGE)
+insttest_SCRIPTS = run-tests.sh
+insttest_DATA = config.env
+endif
diff --git a/cogl/tests/README b/cogl/tests/README
new file mode 100644
index 000000000..cc6dbb97a
--- /dev/null
+++ b/cogl/tests/README
@@ -0,0 +1,63 @@
+Outline of test categories:
+
+The conform/ tests:
+-------------------
+These tests should be non-interactive unit-tests that verify a single
+feature is behaving as documented. See conform/ADDING_NEW_TESTS for more
+details.
+
+Although it may seem a bit awkward; all the tests are built into a
+single binary because it makes building the tests *much* faster by avoiding
+lots of linking.
+
+Each test has a wrapper script generated though so running the individual tests
+should be convenient enough. Running the wrapper script will also print out for
+convenience how you could run the test under gdb or valgrind like this for
+example:
+
+ NOTE: For debugging purposes, you can run this single test as follows:
+ $ libtool --mode=execute \
+ gdb --eval-command="b test_cogl_depth_test" \
+ --args ./test-conformance -p /conform/cogl/test_cogl_depth_test
+ or:
+ $ env G_SLICE=always-malloc \
+ libtool --mode=execute \
+ valgrind ./test-conformance -p /conform/cogl/test_cogl_depth_test
+
+By default the conformance tests are run offscreen. This makes the tests run
+much faster and they also don't interfere with other work you may want to do by
+constantly stealing focus. CoglOnscreen framebuffers obviously don't get tested
+this way so it's important that the tests also get run onscreen every once in a
+while, especially if changes are being made to CoglFramebuffer related code.
+Onscreen testing can be enabled by setting COGL_TEST_ONSCREEN=1 in your
+environment.
+
+The micro-bench/ tests:
+-----------------------
+These should be focused performance tests, ideally testing a
+single metric. Please never forget that these tests are synthetic and if you
+are using them then you understand what metric is being tested. They probably
+don't reflect any real world application loads and the intention is that you
+use these tests once you have already determined the crux of your problem and
+need focused feedback that your changes are indeed improving matters. There is
+no exit status requirements for these tests, but they should give clear
+feedback as to their performance. If the framerate is the feedback metric, then
+the test should forcibly enable FPS debugging.
+
+The data/ directory:
+--------------------
+This contains optional data (like images) that can be referenced by a test.
+
+
+Misc notes:
+-----------
+• All tests should ideally include a detailed description in the source
+explaining exactly what the test is for, how the test was designed to work,
+and possibly a rationale for the approach taken for testing.
+
+• When running tests under Valgrind, you should follow the instructions
+available here:
+
+ http://live.gnome.org/Valgrind
+
+and also use the suppression file available inside the data/ directory.
diff --git a/cogl/tests/config.env.in b/cogl/tests/config.env.in
new file mode 100644
index 000000000..d3777565d
--- /dev/null
+++ b/cogl/tests/config.env.in
@@ -0,0 +1,3 @@
+HAVE_GL=@HAVE_GL@
+HAVE_GLES1=@HAVE_GLES1@
+HAVE_GLES2=@HAVE_GLES2@
diff --git a/cogl/tests/conform/Makefile.am b/cogl/tests/conform/Makefile.am
new file mode 100644
index 000000000..f1c4d6fa5
--- /dev/null
+++ b/cogl/tests/conform/Makefile.am
@@ -0,0 +1,176 @@
+NULL =
+
+noinst_PROGRAMS = test-conformance
+
+common_sources = \
+ test-conform-main.c \
+ $(NULL)
+
+unported_test_sources = \
+ test-fixed.c \
+ test-materials.c \
+ test-viewport.c \
+ test-multitexture.c \
+ test-npot-texture.c \
+ test-object.c \
+ test-readpixels.c \
+ test-texture-mipmaps.c \
+ test-texture-pixmap-x11.c \
+ test-texture-rectangle.c \
+ test-vertex-buffer-contiguous.c \
+ test-vertex-buffer-interleved.c \
+ test-vertex-buffer-mutability.c \
+ $(NULL)
+
+test_sources = \
+ test-atlas-migration.c \
+ test-blend-strings.c \
+ test-blend.c \
+ test-depth-test.c \
+ test-color-hsl.c \
+ test-color-mask.c \
+ test-backface-culling.c \
+ test-just-vertex-shader.c \
+ test-pipeline-user-matrix.c \
+ test-pipeline-uniforms.c \
+ test-pixel-buffer.c \
+ test-premult.c \
+ test-snippets.c \
+ test-wrap-modes.c \
+ test-sub-texture.c \
+ test-custom-attributes.c \
+ test-offscreen.c \
+ test-primitive.c \
+ test-texture-3d.c \
+ test-sparse-pipeline.c \
+ test-read-texture-formats.c \
+ test-write-texture-formats.c \
+ test-point-size.c \
+ test-point-size-attribute.c \
+ test-point-sprite.c \
+ test-no-gl-header.c \
+ test-version.c \
+ test-gles2-context.c \
+ test-euler-quaternion.c \
+ test-layer-remove.c \
+ test-alpha-test.c \
+ test-map-buffer-range.c \
+ test-npot-texture.c \
+ test-alpha-textures.c \
+ test-wrap-rectangle-textures.c \
+ test-texture-get-set-data.c \
+ test-framebuffer-get-bits.c \
+ test-primitive-and-journal.c \
+ test-copy-replace-texture.c \
+ test-pipeline-cache-unrefs-texture.c \
+ test-texture-no-allocate.c \
+ test-pipeline-shader-state.c \
+ test-texture-rg.c \
+ test-fence.c \
+ $(NULL)
+
+if BUILD_COGL_PATH
+test_sources += \
+ test-path.c \
+ test-path-clip.c
+endif
+
+test_conformance_SOURCES = $(common_sources) $(test_sources)
+
+SHEXT = $(EXEEXT)
+
+# For convenience, this provides a way to easily run individual unit tests:
+.PHONY: wrappers clean-wrappers
+
+wrappers: stamp-test-conformance
+ @true
+stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c
+ @mkdir -p wrappers
+ @sed -n -e 's/^ \{1,\}ADD_TEST *( *\([a-zA-Z0-9_]\{1,\}\).*/\1/p' $(srcdir)/test-conform-main.c > unit-tests
+ @chmod +x $(top_srcdir)/tests/test-launcher.sh
+ @( echo "/stamp-test-conformance" ; \
+ echo "/test-conformance$(EXEEXT)" ; \
+ echo "*.o" ; \
+ echo ".gitignore" ; \
+ echo "unit-tests" ; ) > .gitignore
+ @for i in `cat unit-tests`; \
+ do \
+ unit=`basename $$i | sed -e s/_/-/g`; \
+ echo " GEN $$unit"; \
+ ( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-conformance$(EXEEXT) '' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
+ chmod +x $$unit$(SHEXT); \
+ echo "/$$unit$(SHEXT)" >> .gitignore; \
+ done \
+ && echo timestamp > $(@F)
+
+clean-wrappers:
+ @for i in `cat unit-tests`; \
+ do \
+ unit=`basename $$i | sed -e s/_/-/g`; \
+ echo " RM $$unit"; \
+ rm -f $$unit$(SHEXT) ; \
+ done \
+ && rm -f unit-tests \
+ && rm -f stamp-test-conformance
+
+# NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting
+# a phony rule that will generate symlink scripts for running individual tests
+BUILT_SOURCES = wrappers
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/cogl \
+ -I$(top_srcdir)/test-fixtures
+
+AM_CPPFLAGS += \
+ -DCOGL_ENABLE_EXPERIMENTAL_API \
+ -DCOGL_DISABLE_DEPRECATED \
+ -DCOGL_DISABLE_DEPRECATION_WARNINGS \
+ -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\"
+
+test_conformance_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+test_conformance_LDADD = \
+ $(COGL_DEP_LIBS) \
+ $(top_builddir)/cogl/libmutter-cogl.la \
+ $(LIBM)
+if BUILD_COGL_PATH
+test_conformance_LDADD += $(top_builddir)/cogl-path/libmutter-cogl-path.la
+endif
+test_conformance_LDFLAGS = -export-dynamic
+
+test: wrappers
+ @$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-conformance$(EXEEXT)
+
+# XXX: we could prevent the conformance test suite from running
+# by simply defining this variable conditionally
+TEST_PROGS = test-conformance
+
+.PHONY: test
+
+DISTCLEANFILES = .gitignore
+
+# we override the clean-generic target to clean up the wrappers so
+# we cannot use CLEANFILES
+clean-generic: clean-wrappers
+ $(QUIET_RM)rm -f .log
+
+
+if ENABLE_INSTALLED_TESTS
+
+insttestdir = $(libexecdir)/installed-tests/$(PACKAGE)/conform
+insttest_PROGRAMS = test-conformance
+insttest_DATA = unit-tests
+
+testmetadir = $(datadir)/installed-tests/$(PACKAGE)
+testmeta_DATA = conform.test
+
+conform.test:
+ echo " GEN $@"; \
+ echo "[Test]" > $@.tmp; \
+ echo "Type=session" >> $@.tmp; \
+ echo "Exec=sh -c \"cd $(libexecdir)/installed-tests/$(PACKAGE)/conform; ../run-tests.sh ../config.env ./test-conformance\"" >> $@.tmp; \
+ mv $@.tmp $@
+
+CLEANFILES = conform.test
+
+endif
diff --git a/cogl/tests/conform/test-alpha-test.c b/cogl/tests/conform/test-alpha-test.c
new file mode 100644
index 000000000..e74f6d8e0
--- /dev/null
+++ b/cogl/tests/conform/test-alpha-test.c
@@ -0,0 +1,73 @@
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+static CoglTexture2D *
+create_texture (CoglContext *context)
+{
+ static const uint8_t data[] =
+ {
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xfa, 0x00, 0xfa
+ };
+
+ return cogl_texture_2d_new_from_data (context,
+ 2, 1, /* width/height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 4, /* rowstride */
+ data,
+ NULL /* error */);
+}
+
+void
+test_alpha_test (void)
+{
+ CoglTexture *tex = create_texture (test_ctx);
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglColor clear_color;
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_pipeline_set_layer_filters (pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_alpha_test_function (pipeline,
+ COGL_PIPELINE_ALPHA_FUNC_GEQUAL,
+ 254 / 255.0f /* alpha reference */);
+
+ cogl_color_init_from_4ub (&clear_color, 0x00, 0x00, 0xff, 0xff);
+ cogl_framebuffer_clear (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ &clear_color);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1, -1,
+ 1, 1);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (tex);
+
+ /* The left side of the framebuffer should use the first pixel from
+ * the texture which is red */
+ test_utils_check_region (test_fb,
+ 2, 2,
+ fb_width / 2 - 4,
+ fb_height - 4,
+ 0xff0000ff);
+ /* The right side of the framebuffer should use the clear color
+ * because the second pixel from the texture is clipped from the
+ * alpha test */
+ test_utils_check_region (test_fb,
+ fb_width / 2 + 2,
+ 2,
+ fb_width / 2 - 4,
+ fb_height - 4,
+ 0x0000ffff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-alpha-textures.c b/cogl/tests/conform/test-alpha-textures.c
new file mode 100644
index 000000000..dccd30e11
--- /dev/null
+++ b/cogl/tests/conform/test-alpha-textures.c
@@ -0,0 +1,123 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+static void
+create_pipeline (CoglTexture **tex_out,
+ CoglPipeline **pipeline_out)
+{
+ CoglTexture2D *tex;
+ CoglPipeline *pipeline;
+ static const uint8_t tex_data[] =
+ { 0x00, 0x44, 0x88, 0xcc };
+
+ tex = cogl_texture_2d_new_from_data (test_ctx,
+ 2, 2, /* width/height */
+ COGL_PIXEL_FORMAT_A_8, /* format */
+ 2, /* rowstride */
+ tex_data,
+ NULL);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_layer_wrap_mode (pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+ /* This is the layer combine used by cogl-pango */
+ cogl_pipeline_set_layer_combine (pipeline,
+ 0, /* layer */
+ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+ NULL);
+
+ cogl_pipeline_set_layer_texture (pipeline,
+ 0, /* layer */
+ tex);
+
+ *pipeline_out = pipeline;
+ *tex_out = tex;
+}
+
+void
+test_alpha_textures (void)
+{
+ CoglTexture *tex1, *tex2;
+ CoglPipeline *pipeline1, *pipeline2;
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ uint8_t replacement_data[1] = { 0xff };
+
+ create_pipeline (&tex1, &pipeline1);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline1,
+ -1.0f, 1.0f, /* x1/y1 */
+ 1.0f, 0.0f /* x2/y2 */);
+
+ create_pipeline (&tex2, &pipeline2);
+
+ cogl_texture_set_region (tex2,
+ 0, 0, /* src_x/y */
+ 1, 1, /* dst_x/y */
+ 1, 1, /* dst_width / dst_height */
+ 1, 1, /* width / height */
+ COGL_PIXEL_FORMAT_A_8,
+ 1, /* rowstride */
+ replacement_data);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline2,
+ -1.0f, 0.0f, /* x1/y1 */
+ 1.0f, -1.0f /* x2/y2 */);
+
+ cogl_object_unref (tex1);
+ cogl_object_unref (tex2);
+ cogl_object_unref (pipeline1);
+ cogl_object_unref (pipeline2);
+
+ /* Unmodified texture */
+ test_utils_check_pixel (test_fb,
+ fb_width / 4,
+ fb_height / 8,
+ 0x000000ff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4,
+ fb_height / 8,
+ 0x444444ff);
+ test_utils_check_pixel (test_fb,
+ fb_width / 4,
+ fb_height * 3 / 8,
+ 0x888888ff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4,
+ fb_height * 3 / 8,
+ 0xccccccff);
+
+ /* Modified texture */
+ test_utils_check_pixel (test_fb,
+ fb_width / 4,
+ fb_height * 5 / 8,
+ 0x000000ff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4,
+ fb_height * 5 / 8,
+ 0x444444ff);
+ test_utils_check_pixel (test_fb,
+ fb_width / 4,
+ fb_height * 7 / 8,
+ 0x888888ff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4,
+ fb_height * 7 / 8,
+ 0xffffffff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-atlas-migration.c b/cogl/tests/conform/test-atlas-migration.c
new file mode 100644
index 000000000..39e8a3c1f
--- /dev/null
+++ b/cogl/tests/conform/test-atlas-migration.c
@@ -0,0 +1,145 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+#define N_TEXTURES 128
+
+#define OPACITY_FOR_ROW(y) \
+ (0xff - ((y) & 0xf) * 0x10)
+
+#define COLOR_FOR_SIZE(size) \
+ (colors + (size) % 3)
+
+typedef struct
+{
+ uint8_t red, green, blue, alpha;
+} TestColor;
+
+static const TestColor colors[] =
+ { { 0xff, 0x00, 0x00, 0xff },
+ { 0x00, 0xff, 0x00, 0xff },
+ { 0x00, 0x00, 0xff, 0xff } };
+
+static CoglTexture *
+create_texture (int size)
+{
+ CoglTexture *texture;
+ const TestColor *color;
+ uint8_t *data, *p;
+ int x, y;
+
+ /* Create a red, green or blue texture depending on the size */
+ color = COLOR_FOR_SIZE (size);
+
+ p = data = g_malloc (size * size * 4);
+
+ /* Fill the data with the color but fade the opacity out with
+ increasing y coordinates so that we can see the blending it the
+ atlas migration accidentally blends with garbage in the
+ texture */
+ for (y = 0; y < size; y++)
+ {
+ int opacity = OPACITY_FOR_ROW (y);
+
+ for (x = 0; x < size; x++)
+ {
+ /* Store the colors premultiplied */
+ p[0] = color->red * opacity / 255;
+ p[1] = color->green * opacity / 255;
+ p[2] = color->blue * opacity / 255;
+ p[3] = opacity;
+
+ p += 4;
+ }
+ }
+
+ texture = test_utils_texture_new_from_data (test_ctx,
+ size, /* width */
+ size, /* height */
+ TEST_UTILS_TEXTURE_NONE, /* flags */
+ /* format */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ /* rowstride */
+ size * 4,
+ data);
+
+ g_free (data);
+
+ return texture;
+}
+
+static void
+verify_texture (CoglTexture *texture, int size)
+{
+ uint8_t *data, *p;
+ int x, y;
+ const TestColor *color;
+
+ color = COLOR_FOR_SIZE (size);
+
+ p = data = g_malloc (size * size * 4);
+
+ cogl_texture_get_data (texture,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ size * 4,
+ data);
+
+ for (y = 0; y < size; y++)
+ {
+ int opacity = OPACITY_FOR_ROW (y);
+
+ for (x = 0; x < size; x++)
+ {
+ TestColor real_color =
+ {
+ color->red * opacity / 255,
+ color->green * opacity / 255,
+ color->blue * opacity / 255
+ };
+
+ test_utils_compare_pixel (p,
+ (real_color.red << 24) |
+ (real_color.green << 16) |
+ (real_color.blue << 8) |
+ opacity);
+ g_assert_cmpint (p[3], ==, opacity);
+
+ p += 4;
+ }
+ }
+
+ g_free (data);
+}
+
+void
+test_atlas_migration (void)
+{
+ CoglTexture *textures[N_TEXTURES];
+ int i, tex_num;
+
+ /* Create and destroy all of the textures a few times to increase
+ the chances that we'll end up reusing the buffers for previously
+ discarded atlases */
+ for (i = 0; i < 5; i++)
+ {
+ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+ textures[tex_num] = create_texture (tex_num + 1);
+ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+ cogl_object_unref (textures[tex_num]);
+ }
+
+ /* Create all the textures again */
+ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+ textures[tex_num] = create_texture (tex_num + 1);
+
+ /* Verify that they all still have the right data */
+ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+ verify_texture (textures[tex_num], tex_num + 1);
+
+ /* Destroy them all */
+ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+ cogl_object_unref (textures[tex_num]);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-backface-culling.c b/cogl/tests/conform/test-backface-culling.c
new file mode 100644
index 000000000..e90c2f5ec
--- /dev/null
+++ b/cogl/tests/conform/test-backface-culling.c
@@ -0,0 +1,311 @@
+#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0
+
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+/* Size the texture so that it is just off a power of two to encourage
+ it so use software tiling when NPOTs aren't available */
+#define TEXTURE_SIZE 257
+
+/* Amount of pixels to skip off the top, bottom, left and right of the
+ texture when reading back the stage */
+#define TEST_INSET 2
+
+/* Size to actually render the texture at */
+#define TEXTURE_RENDER_SIZE 8
+
+typedef struct _TestState
+{
+ CoglTexture *texture;
+ CoglFramebuffer *offscreen;
+ CoglTexture *offscreen_tex;
+ int width, height;
+} TestState;
+
+static void
+validate_part (CoglFramebuffer *framebuffer,
+ int xnum, int ynum, CoglBool shown)
+{
+ test_utils_check_region (framebuffer,
+ xnum * TEXTURE_RENDER_SIZE + TEST_INSET,
+ ynum * TEXTURE_RENDER_SIZE + TEST_INSET,
+ TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+ TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+ shown ? 0xff0000ff : 0x000000ff);
+}
+
+/* We draw everything 16 times. The draw number is used as a bitmask
+ to test all of the combinations of enabling legacy state, both
+ winding orders and all four culling modes */
+
+#define USE_LEGACY_STATE(draw_num) (((draw_num) & 0x01) >> 0)
+#define FRONT_WINDING(draw_num) (((draw_num) & 0x02) >> 1)
+#define CULL_FACE_MODE(draw_num) (((draw_num) & 0x0c) >> 2)
+
+static void
+paint_test_backface_culling (TestState *state,
+ CoglFramebuffer *framebuffer)
+{
+ int draw_num;
+ CoglPipeline *base_pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_framebuffer_orthographic (framebuffer,
+ 0, 0,
+ state->width,
+ state->height,
+ -1,
+ 100);
+
+ cogl_framebuffer_clear4f (framebuffer,
+ COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_STENCIL,
+ 0, 0, 0, 1);
+
+ cogl_pipeline_set_layer_texture (base_pipeline, 0, state->texture);
+
+ cogl_pipeline_set_layer_filters (base_pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ cogl_push_framebuffer (framebuffer);
+
+ /* Render the scene sixteen times to test all of the combinations of
+ cull face mode, legacy state and winding orders */
+ for (draw_num = 0; draw_num < 16; draw_num++)
+ {
+ float x1 = 0, x2, y1 = 0, y2 = (float)(TEXTURE_RENDER_SIZE);
+ CoglTextureVertex verts[4];
+ CoglPipeline *pipeline;
+
+ cogl_push_matrix ();
+ cogl_translate (0, TEXTURE_RENDER_SIZE * draw_num, 0);
+
+ pipeline = cogl_pipeline_copy (base_pipeline);
+
+ cogl_set_backface_culling_enabled (USE_LEGACY_STATE (draw_num));
+ cogl_pipeline_set_front_face_winding (pipeline, FRONT_WINDING (draw_num));
+ cogl_pipeline_set_cull_face_mode (pipeline, CULL_FACE_MODE (draw_num));
+
+ cogl_push_source (pipeline);
+
+ memset (verts, 0, sizeof (verts));
+
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a front-facing texture */
+ cogl_rectangle (x1, y1, x2, y2);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a front-facing texture with flipped texcoords */
+ cogl_rectangle_with_texture_coords (x1, y1, x2, y2,
+ 1.0, 0.0, 0.0, 1.0);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a back-facing texture */
+ cogl_rectangle (x2, y1, x1, y2);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* If the texture is sliced then cogl_polygon doesn't work so
+ we'll just use a solid color instead */
+ if (cogl_texture_is_sliced (state->texture))
+ cogl_set_source_color4ub (255, 0, 0, 255);
+
+ /* Draw a front-facing polygon */
+ verts[0].x = x1; verts[0].y = y2;
+ verts[1].x = x2; verts[1].y = y2;
+ verts[2].x = x2; verts[2].y = y1;
+ verts[3].x = x1; verts[3].y = y1;
+ verts[0].tx = 0; verts[0].ty = 0;
+ verts[1].tx = 1.0; verts[1].ty = 0;
+ verts[2].tx = 1.0; verts[2].ty = 1.0;
+ verts[3].tx = 0; verts[3].ty = 1.0;
+ cogl_polygon (verts, 4, FALSE);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a back-facing polygon */
+ verts[0].x = x1; verts[0].y = y1;
+ verts[1].x = x2; verts[1].y = y1;
+ verts[2].x = x2; verts[2].y = y2;
+ verts[3].x = x1; verts[3].y = y2;
+ verts[0].tx = 0; verts[0].ty = 0;
+ verts[1].tx = 1.0; verts[1].ty = 0;
+ verts[2].tx = 1.0; verts[2].ty = 1.0;
+ verts[3].tx = 0; verts[3].ty = 1.0;
+ cogl_polygon (verts, 4, FALSE);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ cogl_pop_matrix ();
+
+ cogl_pop_source ();
+ cogl_object_unref (pipeline);
+ }
+
+ cogl_pop_framebuffer ();
+
+ cogl_object_unref (base_pipeline);
+}
+
+static void
+validate_result (CoglFramebuffer *framebuffer, int y_offset)
+{
+ int draw_num;
+
+ for (draw_num = 0; draw_num < 16; draw_num++)
+ {
+ CoglBool cull_front, cull_back;
+ CoglPipelineCullFaceMode cull_mode;
+
+ if (USE_LEGACY_STATE (draw_num))
+ cull_mode = COGL_PIPELINE_CULL_FACE_MODE_BACK;
+ else
+ cull_mode = CULL_FACE_MODE (draw_num);
+
+ switch (cull_mode)
+ {
+ case COGL_PIPELINE_CULL_FACE_MODE_NONE:
+ cull_front = FALSE;
+ cull_back = FALSE;
+ break;
+
+ case COGL_PIPELINE_CULL_FACE_MODE_FRONT:
+ cull_front = TRUE;
+ cull_back = FALSE;
+ break;
+
+ case COGL_PIPELINE_CULL_FACE_MODE_BACK:
+ cull_front = FALSE;
+ cull_back = TRUE;
+ break;
+
+ case COGL_PIPELINE_CULL_FACE_MODE_BOTH:
+ cull_front = TRUE;
+ cull_back = TRUE;
+ break;
+ }
+
+ if (FRONT_WINDING (draw_num) == COGL_WINDING_CLOCKWISE)
+ {
+ CoglBool tmp = cull_front;
+ cull_front = cull_back;
+ cull_back = tmp;
+ }
+
+ /* Front-facing texture */
+ validate_part (framebuffer,
+ 0, y_offset + draw_num, !cull_front);
+ /* Front-facing texture with flipped tex coords */
+ validate_part (framebuffer,
+ 1, y_offset + draw_num, !cull_front);
+ /* Back-facing texture */
+ validate_part (framebuffer,
+ 2, y_offset + draw_num, !cull_back);
+ /* Front-facing texture polygon */
+ validate_part (framebuffer,
+ 3, y_offset + draw_num, !cull_front);
+ /* Back-facing texture polygon */
+ validate_part (framebuffer,
+ 4, y_offset + draw_num, !cull_back);
+ }
+}
+
+static void
+paint (TestState *state)
+{
+ CoglPipeline *pipeline;
+
+ paint_test_backface_culling (state, test_fb);
+
+ /*
+ * Now repeat the test but rendered to an offscreen
+ * framebuffer. Note that by default the conformance tests are
+ * always run to an offscreen buffer but we might as well have this
+ * check anyway in case it is being run with COGL_TEST_ONSCREEN=1
+ */
+ paint_test_backface_culling (state, state->offscreen);
+
+ /* Copy the result of the offscreen rendering for validation and
+ * also so we can have visual feedback. */
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, state->offscreen_tex);
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 0, TEXTURE_RENDER_SIZE * 16,
+ state->width,
+ state->height + TEXTURE_RENDER_SIZE * 16);
+ cogl_object_unref (pipeline);
+
+ validate_result (test_fb, 0);
+ validate_result (test_fb, 16);
+}
+
+static CoglTexture *
+make_texture (void)
+{
+ guchar *tex_data, *p;
+ CoglTexture *tex;
+
+ tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
+
+ for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;)
+ {
+ *(--p) = 255;
+ *(--p) = 0;
+ *(--p) = 0;
+ *(--p) = 255;
+ }
+
+ tex = test_utils_texture_new_from_data (test_ctx,
+ TEXTURE_SIZE,
+ TEXTURE_SIZE,
+ TEST_UTILS_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ TEXTURE_SIZE * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+void
+test_backface_culling (void)
+{
+ TestState state;
+ CoglTexture *tex;
+
+ state.width = cogl_framebuffer_get_width (test_fb);
+ state.height = cogl_framebuffer_get_height (test_fb);
+
+ state.offscreen = NULL;
+
+ state.texture = make_texture ();
+
+ tex = test_utils_texture_new_with_size (test_ctx,
+ state.width, state.height,
+ TEST_UTILS_TEXTURE_NO_SLICING,
+ COGL_TEXTURE_COMPONENTS_RGBA);
+ state.offscreen = cogl_offscreen_new_with_texture (tex);
+ state.offscreen_tex = tex;
+
+ paint (&state);
+
+ cogl_object_unref (state.offscreen);
+ cogl_object_unref (state.offscreen_tex);
+ cogl_object_unref (state.texture);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-blend-strings.c b/cogl/tests/conform/test-blend-strings.c
new file mode 100644
index 000000000..f49c8603b
--- /dev/null
+++ b/cogl/tests/conform/test-blend-strings.c
@@ -0,0 +1,430 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+#define BLEND_CONSTANT_UNUSED 0xDEADBEEF
+#define TEX_CONSTANT_UNUSED 0xDEADBEEF
+
+typedef struct _TestState
+{
+ CoglContext *ctx;
+} TestState;
+
+
+static void
+test_blend (TestState *state,
+ int x,
+ int y,
+ uint32_t src_color,
+ uint32_t dst_color,
+ const char *blend_string,
+ uint32_t blend_constant,
+ uint32_t expected_result)
+{
+ /* src color */
+ uint8_t Sr = MASK_RED (src_color);
+ uint8_t Sg = MASK_GREEN (src_color);
+ uint8_t Sb = MASK_BLUE (src_color);
+ uint8_t Sa = MASK_ALPHA (src_color);
+ /* dest color */
+ uint8_t Dr = MASK_RED (dst_color);
+ uint8_t Dg = MASK_GREEN (dst_color);
+ uint8_t Db = MASK_BLUE (dst_color);
+ uint8_t Da = MASK_ALPHA (dst_color);
+ /* blend constant - when applicable */
+ uint8_t Br = MASK_RED (blend_constant);
+ uint8_t Bg = MASK_GREEN (blend_constant);
+ uint8_t Bb = MASK_BLUE (blend_constant);
+ uint8_t Ba = MASK_ALPHA (blend_constant);
+ CoglColor blend_const_color;
+
+ CoglHandle material;
+ CoglPipeline *pipeline;
+ CoglBool status;
+ CoglError *error = NULL;
+ int y_off;
+ int x_off;
+
+ /* First write out the destination color without any blending... */
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_color4ub (pipeline, Dr, Dg, Db, Da);
+ cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ cogl_set_source (pipeline);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_object_unref (pipeline);
+
+ /*
+ * Now blend a rectangle over our well defined destination:
+ */
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_color4ub (pipeline, Sr, Sg, Sb, Sa);
+
+ status = cogl_pipeline_set_blend (pipeline, blend_string, &error);
+ if (!status)
+ {
+ /* It's not strictly a test failure; you need a more capable GPU or
+ * driver to test this blend string. */
+ if (cogl_test_verbose ())
+ {
+ g_debug ("Failed to test blend string %s: %s",
+ blend_string, error->message);
+ g_print ("Skipping\n");
+ }
+ return;
+ }
+
+ cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba);
+ cogl_pipeline_set_blend_constant (pipeline, &blend_const_color);
+
+ cogl_set_source (pipeline);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_object_unref (pipeline);
+
+ /* See what we got... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ if (cogl_test_verbose ())
+ {
+ g_print ("test_blend (%d, %d):\n%s\n", x, y, blend_string);
+ g_print (" src color = %02x, %02x, %02x, %02x\n", Sr, Sg, Sb, Sa);
+ g_print (" dst color = %02x, %02x, %02x, %02x\n", Dr, Dg, Db, Da);
+ if (blend_constant != BLEND_CONSTANT_UNUSED)
+ g_print (" blend constant = %02x, %02x, %02x, %02x\n",
+ Br, Bg, Bb, Ba);
+ else
+ g_print (" blend constant = UNUSED\n");
+ }
+
+ test_utils_check_pixel (test_fb, x_off, y_off, expected_result);
+
+
+ /*
+ * Test with legacy API
+ */
+
+ /* Clear previous work */
+ cogl_set_source_color4ub (0, 0, 0, 0xff);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+
+ /* First write out the destination color without any blending... */
+ material = cogl_material_new ();
+ cogl_material_set_color4ub (material, Dr, Dg, Db, Da);
+ cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ cogl_set_source (material);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_handle_unref (material);
+
+ /*
+ * Now blend a rectangle over our well defined destination:
+ */
+
+ material = cogl_material_new ();
+ cogl_material_set_color4ub (material, Sr, Sg, Sb, Sa);
+
+ status = cogl_material_set_blend (material, blend_string, &error);
+ if (!status)
+ {
+ /* This is a failure as it must be equivalent to the new API */
+ g_warning ("Error setting blend string %s: %s",
+ blend_string, error->message);
+ g_assert_not_reached ();
+ }
+
+ cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba);
+ cogl_material_set_blend_constant (material, &blend_const_color);
+
+ cogl_set_source (material);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_handle_unref (material);
+
+ /* See what we got... */
+
+ test_utils_check_pixel (test_fb, x_off, y_off, expected_result);
+}
+
+static CoglTexture *
+make_texture (uint32_t color)
+{
+ guchar *tex_data, *p;
+ uint8_t r = MASK_RED (color);
+ uint8_t g = MASK_GREEN (color);
+ uint8_t b = MASK_BLUE (color);
+ uint8_t a = MASK_ALPHA (color);
+ CoglTexture *tex;
+
+ tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
+
+ for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
+ {
+ *(--p) = a;
+ *(--p) = b;
+ *(--p) = g;
+ *(--p) = r;
+ }
+
+ /* Note: we claim that the data is premultiplied so that Cogl won't
+ * premultiply the data on upload */
+ tex = test_utils_texture_new_from_data (test_ctx,
+ QUAD_WIDTH,
+ QUAD_WIDTH,
+ TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ QUAD_WIDTH * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+static void
+test_tex_combine (TestState *state,
+ int x,
+ int y,
+ uint32_t tex0_color,
+ uint32_t tex1_color,
+ uint32_t combine_constant,
+ const char *combine_string,
+ uint32_t expected_result)
+{
+ CoglTexture *tex0, *tex1;
+
+ /* combine constant - when applicable */
+ uint8_t Cr = MASK_RED (combine_constant);
+ uint8_t Cg = MASK_GREEN (combine_constant);
+ uint8_t Cb = MASK_BLUE (combine_constant);
+ uint8_t Ca = MASK_ALPHA (combine_constant);
+ CoglColor combine_const_color;
+
+ CoglHandle material;
+ CoglBool status;
+ CoglError *error = NULL;
+ int y_off;
+ int x_off;
+
+
+ tex0 = make_texture (tex0_color);
+ tex1 = make_texture (tex1_color);
+
+ material = cogl_material_new ();
+
+ cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80);
+ cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+
+ cogl_material_set_layer (material, 0, tex0);
+ cogl_material_set_layer_combine (material, 0,
+ "RGBA = REPLACE (TEXTURE)", NULL);
+
+ cogl_material_set_layer (material, 1, tex1);
+ status = cogl_material_set_layer_combine (material, 1,
+ combine_string, &error);
+ if (!status)
+ {
+ /* It's not strictly a test failure; you need a more capable GPU or
+ * driver to test this texture combine string. */
+ g_debug ("Failed to test texture combine string %s: %s",
+ combine_string, error->message);
+ }
+
+ cogl_color_init_from_4ub (&combine_const_color, Cr, Cg, Cb, Ca);
+ cogl_material_set_layer_combine_constant (material, 1, &combine_const_color);
+
+ cogl_set_source (material);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+
+ cogl_handle_unref (material);
+ cogl_object_unref (tex0);
+ cogl_object_unref (tex1);
+
+ /* See what we got... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ if (cogl_test_verbose ())
+ {
+ g_print ("test_tex_combine (%d, %d):\n%s\n", x, y, combine_string);
+ g_print (" texture 0 color = 0x%08lX\n", (unsigned long)tex0_color);
+ g_print (" texture 1 color = 0x%08lX\n", (unsigned long)tex1_color);
+ if (combine_constant != TEX_CONSTANT_UNUSED)
+ g_print (" combine constant = %02x, %02x, %02x, %02x\n",
+ Cr, Cg, Cb, Ca);
+ else
+ g_print (" combine constant = UNUSED\n");
+ }
+
+ test_utils_check_pixel (test_fb, x_off, y_off, expected_result);
+}
+
+static void
+paint (TestState *state)
+{
+ test_blend (state, 0, 0, /* position */
+ 0xff0000ff, /* src */
+ 0xffffffff, /* dst */
+ "RGBA = ADD (SRC_COLOR, 0)",
+ BLEND_CONSTANT_UNUSED,
+ 0xff0000ff); /* expected */
+
+ test_blend (state, 1, 0, /* position */
+ 0x11223344, /* src */
+ 0x11223344, /* dst */
+ "RGBA = ADD (SRC_COLOR, DST_COLOR)",
+ BLEND_CONSTANT_UNUSED,
+ 0x22446688); /* expected */
+
+ test_blend (state, 2, 0, /* position */
+ 0x80808080, /* src */
+ 0xffffffff, /* dst */
+ "RGBA = ADD (SRC_COLOR * (CONSTANT), 0)",
+ 0x80808080, /* constant (RGBA all = 0.5 when normalized) */
+ 0x40404040); /* expected */
+
+ test_blend (state, 3, 0, /* position */
+ 0x80000080, /* src (alpha = 0.5 when normalized) */
+ 0x40000000, /* dst */
+ "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A]),"
+ " DST_COLOR * (1-SRC_COLOR[A]))",
+ BLEND_CONSTANT_UNUSED,
+ 0x60000040); /* expected */
+
+ /* XXX:
+ * For all texture combine tests tex0 will use a combine mode of
+ * "RGBA = REPLACE (TEXTURE)"
+ */
+
+ test_tex_combine (state, 4, 0, /* position */
+ 0x11111111, /* texture 0 color */
+ 0x22222222, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = ADD (PREVIOUS, TEXTURE)", /* tex combine */
+ 0x33333333); /* expected */
+
+ test_tex_combine (state, 5, 0, /* position */
+ 0x40404040, /* texture 0 color */
+ 0x80808080, /* texture 1 color (RGBA all = 0.5) */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */
+ 0x20202020); /* expected */
+
+ test_tex_combine (state, 6, 0, /* position */
+ 0xffffff80, /* texture 0 color (alpha = 0.5) */
+ 0xDEADBE40, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGB = REPLACE (PREVIOUS)"
+ "A = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */
+ 0xffffff20); /* expected */
+
+ /* XXX: we are assuming test_tex_combine creates a material with
+ * a color of 0x80808080 (i.e. the "PRIMARY" color) */
+ test_tex_combine (state, 7, 0, /* position */
+ 0xffffff80, /* texture 0 color (alpha = 0.5) */
+ 0xDEADBE20, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGB = REPLACE (PREVIOUS)"
+ "A = MODULATE (PRIMARY, TEXTURE)", /* tex combine */
+ 0xffffff10); /* expected */
+
+ test_tex_combine (state, 8, 0, /* position */
+ 0x11111111, /* texture 0 color */
+ 0x22222222, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = ADD (PREVIOUS, 1-TEXTURE)", /* tex combine */
+ 0xeeeeeeee); /* expected */
+
+ /* this is again assuming a primary color of 0x80808080 */
+ test_tex_combine (state, 9, 0, /* position */
+ 0x10101010, /* texture 0 color */
+ 0x20202020, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = INTERPOLATE (PREVIOUS, TEXTURE, PRIMARY)",
+ 0x18181818); /* expected */
+
+#if 0 /* using TEXTURE_N appears to be broken in cogl-blend-string.c */
+ test_tex_combine (state, 0, 1, /* position */
+ 0xDEADBEEF, /* texture 0 color (not used) */
+ 0x11223344, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = ADD (TEXTURE_1, TEXTURE)", /* tex combine */
+ 0x22446688); /* expected */
+#endif
+
+ test_tex_combine (state, 1, 1, /* position */
+ 0x21314151, /* texture 0 color */
+ 0x99999999, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = ADD_SIGNED (PREVIOUS, TEXTURE)", /* tex combine */
+ 0x3a4a5a6a); /* expected */
+
+ test_tex_combine (state, 2, 1, /* position */
+ 0xfedcba98, /* texture 0 color */
+ 0x11111111, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGBA = SUBTRACT (PREVIOUS, TEXTURE)", /* tex combine */
+ 0xedcba987); /* expected */
+
+ test_tex_combine (state, 3, 1, /* position */
+ 0x8899aabb, /* texture 0 color */
+ 0xbbaa9988, /* texture 1 color */
+ TEX_CONSTANT_UNUSED,
+ "RGB = DOT3_RGBA (PREVIOUS, TEXTURE)"
+ "A = REPLACE (PREVIOUS)",
+ 0x2a2a2abb); /* expected */
+}
+
+void
+test_blend_strings (void)
+{
+ TestState state;
+
+ cogl_framebuffer_orthographic (test_fb, 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ /* XXX: we have to push/pop a framebuffer since this test currently
+ * uses the legacy cogl_rectangle() api. */
+ cogl_push_framebuffer (test_fb);
+ paint (&state);
+ cogl_pop_framebuffer ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-blend.c b/cogl/tests/conform/test-blend.c
new file mode 100644
index 000000000..3c6235b5f
--- /dev/null
+++ b/cogl/tests/conform/test-blend.c
@@ -0,0 +1,64 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+static void
+paint (void)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ int width = cogl_framebuffer_get_width (test_fb);
+ int half_width = width / 2;
+ int height = cogl_framebuffer_get_height (test_fb);
+ CoglVertexP2 tri0_vertices[] = {
+ { 0, 0 },
+ { 0, height },
+ { half_width, height },
+ };
+ CoglVertexP2C4 tri1_vertices[] = {
+ { half_width, 0, 0x80, 0x80, 0x80, 0x80 },
+ { half_width, height, 0x80, 0x80, 0x80, 0x80 },
+ { width, height, 0x80, 0x80, 0x80, 0x80 },
+ };
+ CoglPrimitive *tri0;
+ CoglPrimitive *tri1;
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0);
+
+ tri0 = cogl_primitive_new_p2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES,
+ 3, tri0_vertices);
+ tri1 = cogl_primitive_new_p2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES,
+ 3, tri1_vertices);
+
+ /* Check that cogl correctly handles the case where we draw
+ * different primitives same pipeline and switch from using the
+ * opaque color associated with the pipeline and using a colour
+ * attribute with an alpha component which implies blending is
+ * required.
+ *
+ * If Cogl gets this wrong then then in all likelyhood the second
+ * primitive will be drawn with blending still disabled.
+ */
+
+ cogl_primitive_draw (tri0, test_fb, pipeline);
+ cogl_primitive_draw (tri1, test_fb, pipeline);
+
+ test_utils_check_pixel_and_alpha (test_fb,
+ half_width + 5,
+ height - 5,
+ 0x80808080);
+}
+
+void
+test_blend (void)
+{
+ cogl_framebuffer_orthographic (test_fb, 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint ();
+}
+
diff --git a/cogl/tests/conform/test-color-hsl.c b/cogl/tests/conform/test-color-hsl.c
new file mode 100644
index 000000000..651ce5208
--- /dev/null
+++ b/cogl/tests/conform/test-color-hsl.c
@@ -0,0 +1,45 @@
+#include <math.h>
+#include <string.h>
+
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+#define cogl_assert_float(a, b) \
+ do { \
+ if (fabsf ((a) - (b)) >= 0.0001f) \
+ g_assert_cmpfloat ((a), ==, (b)); \
+ } while (0)
+
+void
+test_color_hsl (void)
+{
+ CoglColor color;
+ float hue, saturation, luminance;
+
+ cogl_color_init_from_4ub(&color, 108, 198, 78, 255);
+ cogl_color_to_hsl(&color, &hue, &saturation, &luminance);
+
+ cogl_assert_float(hue, 105.f);
+ cogl_assert_float(saturation, 0.512821);
+ cogl_assert_float(luminance, 0.541176);
+
+ memset(&color, 0, sizeof (CoglColor));
+ cogl_color_init_from_hsl(&color, hue, saturation, luminance);
+
+ g_assert_cmpint (cogl_color_get_red_byte (&color), ==, 108);
+ g_assert_cmpint (cogl_color_get_green_byte (&color), ==, 198);
+ g_assert_cmpint (cogl_color_get_blue_byte (&color), ==, 78);
+ g_assert_cmpint (cogl_color_get_alpha_byte (&color), ==, 255);
+
+ memset(&color, 0, sizeof (CoglColor));
+ cogl_color_init_from_hsl(&color, hue, 0, luminance);
+
+ cogl_assert_float (cogl_color_get_red_float (&color), luminance);
+ cogl_assert_float (cogl_color_get_green_float (&color), luminance);
+ cogl_assert_float (cogl_color_get_blue_float (&color), luminance);
+ cogl_assert_float (cogl_color_get_alpha_float (&color), 1.0f);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-color-mask.c b/cogl/tests/conform/test-color-mask.c
new file mode 100644
index 000000000..e80f46dae
--- /dev/null
+++ b/cogl/tests/conform/test-color-mask.c
@@ -0,0 +1,110 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+#define TEX_SIZE 128
+
+#define NUM_FBOS 3
+
+typedef struct _TestState
+{
+ int width;
+ int height;
+
+ CoglTexture *tex[NUM_FBOS];
+ CoglFramebuffer *fbo[NUM_FBOS];
+} TestState;
+
+static void
+paint (TestState *state)
+{
+ CoglColor bg;
+ int i;
+
+ cogl_set_source_color4ub (255, 255, 255, 255);
+
+ /* We push the third framebuffer first so that later we can switch
+ back to it by popping to test that that works */
+ cogl_push_framebuffer (state->fbo[2]);
+
+ cogl_push_framebuffer (state->fbo[0]);
+ cogl_rectangle (-1.0, -1.0, 1.0, 1.0);
+ cogl_pop_framebuffer ();
+
+ cogl_push_framebuffer (state->fbo[1]);
+ cogl_rectangle (-1.0, -1.0, 1.0, 1.0);
+ cogl_pop_framebuffer ();
+
+ /* We should now be back on the third framebuffer */
+ cogl_rectangle (-1.0, -1.0, 1.0, 1.0);
+ cogl_pop_framebuffer ();
+
+ cogl_color_init_from_4ub (&bg, 128, 128, 128, 255);
+ cogl_clear (&bg, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH);
+
+ /* Render all of the textures to the screen */
+ for (i = 0; i < NUM_FBOS; i++)
+ {
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, state->tex[i]);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline,
+ 2.0f / NUM_FBOS * i - 1.0f, -1.0f,
+ 2.0f / NUM_FBOS * (i + 1) - 1.0f, 1.0f);
+ cogl_object_unref (pipeline);
+ }
+
+ /* Verify all of the fbos drew the right color */
+ for (i = 0; i < NUM_FBOS; i++)
+ {
+ uint8_t expected_colors[NUM_FBOS][4] =
+ { { 0xff, 0x00, 0x00, 0xff },
+ { 0x00, 0xff, 0x00, 0xff },
+ { 0x00, 0x00, 0xff, 0xff } };
+
+ test_utils_check_pixel_rgb (test_fb,
+ state->width * (i + 0.5f) / NUM_FBOS,
+ state->height / 2,
+ expected_colors[i][0],
+ expected_colors[i][1],
+ expected_colors[i][2]);
+ }
+}
+
+void
+test_color_mask (void)
+{
+ TestState state;
+ int i;
+
+ state.width = cogl_framebuffer_get_width (test_fb);
+ state.height = cogl_framebuffer_get_height (test_fb);
+
+ for (i = 0; i < NUM_FBOS; i++)
+ {
+ state.tex[i] = test_utils_texture_new_with_size (test_ctx, 128, 128,
+ TEST_UTILS_TEXTURE_NO_ATLAS,
+ COGL_TEXTURE_COMPONENTS_RGB);
+
+
+ state.fbo[i] = cogl_offscreen_new_with_texture (state.tex[i]);
+
+ /* Clear the texture color bits */
+ cogl_framebuffer_clear4f (state.fbo[i],
+ COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ cogl_framebuffer_set_color_mask (state.fbo[i],
+ i == 0 ? COGL_COLOR_MASK_RED :
+ i == 1 ? COGL_COLOR_MASK_GREEN :
+ COGL_COLOR_MASK_BLUE);
+ }
+
+ /* XXX: we have to push/pop a framebuffer since this test currently
+ * uses the legacy cogl_rectangle() api. */
+ cogl_push_framebuffer (test_fb);
+ paint (&state);
+ cogl_pop_framebuffer ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-conform-main.c b/cogl/tests/conform/test-conform-main.c
new file mode 100644
index 000000000..9b6573d92
--- /dev/null
+++ b/cogl/tests/conform/test-conform-main.c
@@ -0,0 +1,157 @@
+#include "config.h"
+
+#include <cogl/cogl.h>
+
+#include <glib.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+/* A bit of sugar for adding new conformance tests */
+#define ADD_TEST(FUNC, REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS) \
+ G_STMT_START { \
+ extern void FUNC (void); \
+ if (strcmp (#FUNC, argv[1]) == 0) \
+ { \
+ test_utils_init (REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS); \
+ FUNC (); \
+ test_utils_fini (); \
+ exit (0); \
+ } \
+ } G_STMT_END
+
+#define UNPORTED_TEST(FUNC)
+
+int
+main (int argc, char **argv)
+{
+ int i;
+
+ if (argc != 2)
+ {
+ g_printerr ("usage %s UNIT_TEST\n", argv[0]);
+ exit (1);
+ }
+
+ /* Just for convenience in case people try passing the wrapper
+ * filenames for the UNIT_TEST argument we normalize '-' characters
+ * to '_' characters... */
+ for (i = 0; argv[1][i]; i++)
+ {
+ if (argv[1][i] == '-')
+ argv[1][i] = '_';
+ }
+
+ /* This file is run through a sed script during the make step so the
+ * lines containing the tests need to be formatted on a single line
+ * each.
+ */
+
+ UNPORTED_TEST (test_object);
+ UNPORTED_TEST (test_fixed);
+ UNPORTED_TEST (test_materials);
+ ADD_TEST (test_pipeline_user_matrix, 0, 0);
+ ADD_TEST (test_blend_strings, 0, 0);
+ ADD_TEST (test_blend, 0, 0);
+ ADD_TEST (test_premult, 0, TEST_KNOWN_FAILURE);
+ UNPORTED_TEST (test_readpixels);
+#ifdef COGL_HAS_COGL_PATH_SUPPORT
+ ADD_TEST (test_path, 0, 0);
+ ADD_TEST (test_path_clip, 0, 0);
+#endif
+ ADD_TEST (test_depth_test, 0, 0);
+ ADD_TEST (test_color_mask, 0, 0);
+ ADD_TEST (test_backface_culling, 0, TEST_REQUIREMENT_NPOT);
+ ADD_TEST (test_layer_remove, 0, 0);
+
+ ADD_TEST (test_sparse_pipeline, 0, 0);
+
+ ADD_TEST (test_npot_texture, 0, 0);
+ UNPORTED_TEST (test_multitexture);
+ UNPORTED_TEST (test_texture_mipmaps);
+ ADD_TEST (test_sub_texture, 0, 0);
+ ADD_TEST (test_pixel_buffer_map, 0, 0);
+ ADD_TEST (test_pixel_buffer_set_data, 0, 0);
+ ADD_TEST (test_pixel_buffer_sub_region, 0, 0);
+ UNPORTED_TEST (test_texture_rectangle);
+ ADD_TEST (test_texture_3d, TEST_REQUIREMENT_TEXTURE_3D, 0);
+ ADD_TEST (test_wrap_modes, 0, 0);
+ UNPORTED_TEST (test_texture_pixmap_x11);
+ ADD_TEST (test_texture_get_set_data, 0, 0);
+ ADD_TEST (test_atlas_migration, 0, 0);
+ ADD_TEST (test_read_texture_formats, 0, TEST_KNOWN_FAILURE);
+ ADD_TEST (test_write_texture_formats, 0, 0);
+ ADD_TEST (test_alpha_textures, 0, 0);
+ ADD_TEST (test_wrap_rectangle_textures,
+ TEST_REQUIREMENT_TEXTURE_RECTANGLE,
+ TEST_KNOWN_FAILURE);
+
+ UNPORTED_TEST (test_vertex_buffer_contiguous);
+ UNPORTED_TEST (test_vertex_buffer_interleved);
+ UNPORTED_TEST (test_vertex_buffer_mutability);
+
+ ADD_TEST (test_primitive, 0, 0);
+
+ ADD_TEST (test_just_vertex_shader, TEST_REQUIREMENT_GLSL, 0);
+ ADD_TEST (test_pipeline_uniforms, TEST_REQUIREMENT_GLSL, 0);
+ ADD_TEST (test_snippets, TEST_REQUIREMENT_GLSL, 0);
+ ADD_TEST (test_custom_attributes, TEST_REQUIREMENT_GLSL, 0);
+
+ ADD_TEST (test_offscreen, 0, 0);
+ ADD_TEST (test_framebuffer_get_bits,
+ TEST_REQUIREMENT_OFFSCREEN | TEST_REQUIREMENT_GL,
+ 0);
+
+ ADD_TEST (test_point_size, 0, 0);
+ ADD_TEST (test_point_size_attribute,
+ TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE, 0);
+ ADD_TEST (test_point_size_attribute_snippet,
+ TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE |
+ TEST_REQUIREMENT_GLSL, 0);
+ ADD_TEST (test_point_sprite,
+ TEST_REQUIREMENT_POINT_SPRITE,
+ 0);
+ ADD_TEST (test_point_sprite_orientation,
+ TEST_REQUIREMENT_POINT_SPRITE,
+ TEST_KNOWN_FAILURE);
+ ADD_TEST (test_point_sprite_glsl,
+ TEST_REQUIREMENT_POINT_SPRITE |
+ TEST_REQUIREMENT_GLSL,
+ 0);
+
+ ADD_TEST (test_version, 0, 0);
+
+ ADD_TEST (test_alpha_test, 0, 0);
+
+ ADD_TEST (test_map_buffer_range, TEST_REQUIREMENT_MAP_WRITE, 0);
+
+ ADD_TEST (test_primitive_and_journal, 0, 0);
+
+ ADD_TEST (test_copy_replace_texture, 0, 0);
+
+ ADD_TEST (test_pipeline_cache_unrefs_texture, 0, 0);
+ ADD_TEST (test_pipeline_shader_state, TEST_REQUIREMENT_GLSL, 0);
+
+ UNPORTED_TEST (test_viewport);
+
+ ADD_TEST (test_gles2_context, TEST_REQUIREMENT_GLES2_CONTEXT, 0);
+ ADD_TEST (test_gles2_context_fbo, TEST_REQUIREMENT_GLES2_CONTEXT, 0);
+ ADD_TEST (test_gles2_context_copy_tex_image,
+ TEST_REQUIREMENT_GLES2_CONTEXT,
+ 0);
+
+ ADD_TEST (test_euler_quaternion, 0, 0);
+ ADD_TEST (test_color_hsl, 0, 0);
+
+ ADD_TEST (test_fence, TEST_REQUIREMENT_FENCE, 0);
+
+ ADD_TEST (test_texture_no_allocate, 0, 0);
+
+ ADD_TEST (test_texture_rg, TEST_REQUIREMENT_TEXTURE_RG, 0);
+
+ g_printerr ("Unknown test name \"%s\"\n", argv[1]);
+
+ return 1;
+}
diff --git a/cogl/tests/conform/test-copy-replace-texture.c b/cogl/tests/conform/test-copy-replace-texture.c
new file mode 100644
index 000000000..f11070ee8
--- /dev/null
+++ b/cogl/tests/conform/test-copy-replace-texture.c
@@ -0,0 +1,120 @@
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+/* Keep track of the number of textures that we've created and are
+ * still alive */
+static int alive_texture_mask = 0;
+
+#define N_LAYERS 3
+#define N_PIPELINES 4
+
+#define PIPELINE_LAYER_MASK(pipeline_num) \
+ (((1 << N_LAYERS) - 1) << (N_LAYERS * (pipeline_num) + 1))
+#define LAST_PIPELINE_MASK PIPELINE_LAYER_MASK (N_PIPELINES - 1)
+#define FIRST_PIPELINE_MASK PIPELINE_LAYER_MASK (0)
+
+static void
+free_texture_cb (void *user_data)
+{
+ int texture_num = GPOINTER_TO_INT (user_data);
+
+ alive_texture_mask &= ~(1 << texture_num);
+}
+
+static CoglTexture *
+create_texture (void)
+{
+ static const guint8 data[] =
+ { 0xff, 0xff, 0xff, 0xff };
+ static CoglUserDataKey texture_data_key;
+ CoglTexture2D *tex_2d;
+ static int texture_num = 1;
+
+ alive_texture_mask |= (1 << texture_num);
+
+ tex_2d = cogl_texture_2d_new_from_data (test_ctx,
+ 1, 1, /* width / height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 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 (texture_num),
+ free_texture_cb);
+
+ texture_num++;
+
+ return tex_2d;
+}
+
+void
+test_copy_replace_texture (void)
+{
+ CoglPipeline *pipelines[N_PIPELINES];
+ int pipeline_num;
+
+ /* Create a set of pipeline copies each with three of their own
+ * replacement textures */
+ for (pipeline_num = 0; pipeline_num < N_PIPELINES; pipeline_num++)
+ {
+ int layer_num;
+
+ if (pipeline_num == 0)
+ pipelines[pipeline_num] = cogl_pipeline_new (test_ctx);
+ else
+ pipelines[pipeline_num] =
+ cogl_pipeline_copy (pipelines[pipeline_num - 1]);
+
+ for (layer_num = 0; layer_num < N_LAYERS; layer_num++)
+ {
+ CoglTexture *tex = create_texture ();
+ cogl_pipeline_set_layer_texture (pipelines[pipeline_num],
+ layer_num,
+ tex);
+ cogl_object_unref (tex);
+ }
+ }
+
+ /* Unref everything but the last pipeline */
+ for (pipeline_num = 0; pipeline_num < N_PIPELINES - 1; pipeline_num++)
+ cogl_object_unref (pipelines[pipeline_num]);
+
+ if (alive_texture_mask && cogl_test_verbose ())
+ {
+ int i;
+
+ g_print ("Alive textures:");
+
+ for (i = 0; i < N_PIPELINES * N_LAYERS; i++)
+ if ((alive_texture_mask & (1 << (i + 1))))
+ g_print (" %i", i);
+
+ g_print ("\n");
+ }
+
+ /* Ideally there should only be the textures from the last pipeline
+ * left alive. We also let Cogl keep the textures from the first
+ * texture alive because currently the child of the third layer in
+ * the first pipeline will retain its authority on the unit index
+ * state so that it can set it to 2. If there are more textures then
+ * it means the pipeline isn't correctly pruning redundant
+ * ancestors */
+ g_assert_cmpint (alive_texture_mask & ~FIRST_PIPELINE_MASK,
+ ==,
+ LAST_PIPELINE_MASK);
+
+ /* Clean up the last pipeline */
+ cogl_object_unref (pipelines[N_PIPELINES - 1]);
+
+ /* That should get rid of the last of the textures */
+ g_assert_cmpint (alive_texture_mask, ==, 0);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-custom-attributes.c b/cogl/tests/conform/test-custom-attributes.c
new file mode 100644
index 000000000..633dc2ad8
--- /dev/null
+++ b/cogl/tests/conform/test-custom-attributes.c
@@ -0,0 +1,301 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ CoglPipeline *pipeline;
+} TestState;
+
+typedef struct
+{
+ int16_t x, y;
+ float r, g, b, a;
+} FloatVert;
+
+typedef struct
+{
+ int16_t x, y;
+ uint8_t r, g, b, a;
+} ByteVert;
+
+typedef struct
+{
+ int16_t x, y;
+ int16_t r, g, b, a;
+} ShortVert;
+
+static void
+test_float_verts (TestState *state, int offset_x, int offset_y)
+{
+ CoglAttribute *attributes[2];
+ CoglAttributeBuffer *buffer;
+ CoglPrimitive *primitive;
+
+ static const FloatVert float_verts[] =
+ {
+ { 0, 10, /**/ 1, 0, 0, 1 },
+ { 10, 10, /**/ 1, 0, 0, 1 },
+ { 5, 0, /**/ 1, 0, 0, 1 },
+
+ { 10, 10, /**/ 0, 1, 0, 1 },
+ { 20, 10, /**/ 0, 1, 0, 1 },
+ { 15, 0, /**/ 0, 1, 0, 1 }
+ };
+
+ buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (float_verts), float_verts);
+ attributes[0] = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (FloatVert),
+ G_STRUCT_OFFSET (FloatVert, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_SHORT);
+ attributes[1] = cogl_attribute_new (buffer,
+ "color",
+ sizeof (FloatVert),
+ G_STRUCT_OFFSET (FloatVert, r),
+ 4, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f);
+
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 6, /* n_vertices */
+ attributes,
+ 2); /* n_attributes */
+ cogl_primitive_draw (primitive, test_fb, state->pipeline);
+ cogl_object_unref (primitive);
+
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ cogl_object_unref (attributes[1]);
+ cogl_object_unref (attributes[0]);
+ cogl_object_unref (buffer);
+
+ test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff);
+ test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff);
+}
+
+static void
+test_byte_verts (TestState *state, int offset_x, int offset_y)
+{
+ CoglAttribute *attributes[2];
+ CoglAttributeBuffer *buffer, *unnorm_buffer;
+ CoglPrimitive *primitive;
+
+ static const ByteVert norm_verts[] =
+ {
+ { 0, 10, /**/ 255, 0, 0, 255 },
+ { 10, 10, /**/ 255, 0, 0, 255 },
+ { 5, 0, /**/ 255, 0, 0, 255 },
+
+ { 10, 10, /**/ 0, 255, 0, 255 },
+ { 20, 10, /**/ 0, 255, 0, 255 },
+ { 15, 0, /**/ 0, 255, 0, 255 }
+ };
+
+ static const ByteVert unnorm_verts[] =
+ {
+ { 0, 0, /**/ 0, 0, 1, 1 },
+ { 0, 0, /**/ 0, 0, 1, 1 },
+ { 0, 0, /**/ 0, 0, 1, 1 },
+ };
+
+ buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (norm_verts), norm_verts);
+ attributes[0] = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (ByteVert),
+ G_STRUCT_OFFSET (ByteVert, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_SHORT);
+ attributes[1] = cogl_attribute_new (buffer,
+ "color",
+ sizeof (ByteVert),
+ G_STRUCT_OFFSET (ByteVert, r),
+ 4, /* n_components */
+ COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE);
+ cogl_attribute_set_normalized (attributes[1], TRUE);
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f);
+
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 6, /* n_vertices */
+ attributes,
+ 2); /* n_attributes */
+ cogl_primitive_draw (primitive, test_fb, state->pipeline);
+ cogl_object_unref (primitive);
+
+ cogl_object_unref (attributes[1]);
+
+ /* Test again with unnormalized attributes */
+ unnorm_buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (unnorm_verts),
+ unnorm_verts);
+ attributes[1] = cogl_attribute_new (unnorm_buffer,
+ "color",
+ sizeof (ByteVert),
+ G_STRUCT_OFFSET (ByteVert, r),
+ 4, /* n_components */
+ COGL_ATTRIBUTE_TYPE_BYTE);
+
+ cogl_framebuffer_translate (test_fb, 20, 0, 0);
+
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ attributes,
+ 2); /* n_attributes */
+ cogl_primitive_draw (primitive, test_fb, state->pipeline);
+ cogl_object_unref (primitive);
+
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ cogl_object_unref (attributes[0]);
+ cogl_object_unref (attributes[1]);
+ cogl_object_unref (buffer);
+ cogl_object_unref (unnorm_buffer);
+
+ test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff);
+ test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff);
+ test_utils_check_pixel (test_fb, offset_x + 25, offset_y + 5, 0x0000ffff);
+}
+
+static void
+test_short_verts (TestState *state, int offset_x, int offset_y)
+{
+ CoglAttribute *attributes[2];
+ CoglAttributeBuffer *buffer;
+ CoglPipeline *pipeline, *pipeline2;
+ CoglSnippet *snippet;
+ CoglPrimitive *primitive;
+
+ static const ShortVert short_verts[] =
+ {
+ { -10, -10, /**/ 0xffff, 0, 0, 0xffff },
+ { -1, -10, /**/ 0xffff, 0, 0, 0xffff },
+ { -5, -1, /**/ 0xffff, 0, 0, 0xffff }
+ };
+
+
+ pipeline = cogl_pipeline_copy (state->pipeline);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255);
+
+ buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (short_verts), short_verts);
+ attributes[0] = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (ShortVert),
+ G_STRUCT_OFFSET (ShortVert, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_SHORT);
+ attributes[1] = cogl_attribute_new (buffer,
+ "color",
+ sizeof (ShortVert),
+ G_STRUCT_OFFSET (ShortVert, r),
+ 4, /* n_components */
+ COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT);
+ cogl_attribute_set_normalized (attributes[1], TRUE);
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb,
+ offset_x + 10.0f,
+ offset_y + 10.0f,
+ 0.0f);
+
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ attributes,
+ 2); /* n_attributes */
+ cogl_primitive_draw (primitive, test_fb, pipeline);
+ cogl_object_unref (primitive);
+
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ cogl_object_unref (attributes[0]);
+
+ /* Test again treating the attribute as unsigned */
+ attributes[0] = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (ShortVert),
+ G_STRUCT_OFFSET (ShortVert, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT);
+
+ /* XXX: this is a hack to force the pipeline to use the glsl backend
+ * because we know it's not possible to test short vertex position
+ * components with the legacy GL backend since which might otherwise
+ * be used internally... */
+ pipeline2 = cogl_pipeline_new (test_ctx);
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ "attribute vec4 color;",
+ "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);");
+ cogl_pipeline_add_snippet (pipeline2, snippet);
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb,
+ offset_x + 10.0f - 65525.0f,
+ offset_y - 65525,
+ 0.0f);
+
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ attributes,
+ 1); /* n_attributes */
+ cogl_primitive_draw (primitive, test_fb, pipeline2);
+ cogl_object_unref (primitive);
+
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ cogl_object_unref (attributes[0]);
+
+ cogl_object_unref (pipeline2);
+ cogl_object_unref (pipeline);
+ cogl_object_unref (buffer);
+
+ test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff);
+ test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff);
+}
+
+static void
+paint (TestState *state)
+{
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ test_float_verts (state, 0, 0);
+ test_byte_verts (state, 0, 10);
+ test_short_verts (state, 0, 20);
+}
+
+void
+test_custom_attributes (void)
+{
+ CoglSnippet *snippet;
+ TestState state;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ state.pipeline = cogl_pipeline_new (test_ctx);
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ "attribute vec4 color;",
+ "cogl_color_out = color;");
+ cogl_pipeline_add_snippet (state.pipeline, snippet);
+
+ paint (&state);
+
+ cogl_object_unref (state.pipeline);
+ cogl_object_unref (snippet);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-depth-test.c b/cogl/tests/conform/test-depth-test.c
new file mode 100644
index 000000000..bfa9d0e1f
--- /dev/null
+++ b/cogl/tests/conform/test-depth-test.c
@@ -0,0 +1,301 @@
+#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0
+
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef struct _TestState
+{
+ int padding;
+} TestState;
+
+typedef struct
+{
+ uint32_t color;
+ float depth;
+ CoglBool test_enable;
+ CoglDepthTestFunction test_function;
+ CoglBool write_enable;
+ CoglBool fb_write_enable;
+ float range_near;
+ float range_far;
+} TestDepthState;
+
+static CoglBool
+draw_rectangle (TestState *state,
+ int x,
+ int y,
+ TestDepthState *rect_state,
+ CoglBool legacy_mode)
+{
+ uint8_t Cr = MASK_RED (rect_state->color);
+ uint8_t Cg = MASK_GREEN (rect_state->color);
+ uint8_t Cb = MASK_BLUE (rect_state->color);
+ uint8_t Ca = MASK_ALPHA (rect_state->color);
+ CoglPipeline *pipeline;
+ CoglDepthState depth_state;
+
+ cogl_depth_state_init (&depth_state);
+ cogl_depth_state_set_test_enabled (&depth_state, rect_state->test_enable);
+ cogl_depth_state_set_test_function (&depth_state, rect_state->test_function);
+ cogl_depth_state_set_write_enabled (&depth_state, rect_state->write_enable);
+ cogl_depth_state_set_range (&depth_state,
+ rect_state->range_near,
+ rect_state->range_far);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ if (!cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL))
+ {
+ cogl_object_unref (pipeline);
+ return FALSE;
+ }
+
+ if (!legacy_mode)
+ {
+ cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca);
+
+ cogl_framebuffer_set_depth_write_enabled (test_fb,
+ rect_state->fb_write_enable);
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, 0, 0, rect_state->depth);
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_framebuffer_pop_matrix (test_fb);
+ }
+ else
+ {
+ cogl_push_framebuffer (test_fb);
+ cogl_push_matrix ();
+ cogl_set_source_color4ub (Cr, Cg, Cb, Ca);
+ cogl_translate (0, 0, rect_state->depth);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ cogl_pop_matrix ();
+ cogl_pop_framebuffer ();
+ }
+
+ cogl_object_unref (pipeline);
+
+ return TRUE;
+}
+
+static void
+test_depth (TestState *state,
+ int x,
+ int y,
+ TestDepthState *rect0_state,
+ TestDepthState *rect1_state,
+ TestDepthState *rect2_state,
+ CoglBool legacy_mode,
+ uint32_t expected_result)
+{
+ CoglBool missing_feature = FALSE;
+
+ if (rect0_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect0_state, legacy_mode);
+ if (rect1_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect1_state, legacy_mode);
+ if (rect2_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect2_state, legacy_mode);
+
+ /* We don't consider it an error that we can't test something
+ * the driver doesn't support. */
+ if (missing_feature)
+ return;
+
+ test_utils_check_pixel (test_fb,
+ x * QUAD_WIDTH + (QUAD_WIDTH / 2),
+ y * QUAD_WIDTH + (QUAD_WIDTH / 2),
+ expected_result);
+}
+
+static void
+paint (TestState *state)
+{
+ /* Sanity check a few of the different depth test functions
+ * and that depth writing can be disabled... */
+
+ {
+ /* Closest */
+ TestDepthState rect0_state = {
+ 0xff0000ff, /* rgba color */
+ -10, /* depth */
+ FALSE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 1 /* depth range */
+ };
+ /* Furthest */
+ TestDepthState rect1_state = {
+ 0x00ff00ff, /* rgba color */
+ -70, /* depth */
+ TRUE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 1 /* depth range */
+ };
+ /* In the middle */
+ TestDepthState rect2_state = {
+ 0x0000ffff, /* rgba color */
+ -20, /* depth */
+ TRUE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_NEVER,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 1 /* depth range */
+ };
+
+ test_depth (state, 0, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x00ff00ff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS;
+ test_depth (state, 1, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x0000ffff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS;
+ test_depth (state, 2, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x0000ffff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER;
+ test_depth (state, 3, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x00ff00ff); /* expected */
+
+ rect0_state.test_enable = TRUE;
+ rect1_state.write_enable = FALSE;
+ test_depth (state, 4, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x0000ffff); /* expected */
+
+ rect1_state.write_enable = TRUE;
+ rect1_state.fb_write_enable = FALSE;
+ test_depth (state, 4, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x0000ffff); /* expected */
+
+ /* Re-enable FB depth writing to verify state flush */
+ rect1_state.write_enable = TRUE;
+ rect1_state.fb_write_enable = TRUE;
+ test_depth (state, 4, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ FALSE, /* legacy mode */
+ 0x00ff00ff); /* expected */
+ }
+
+ /* Check that the depth buffer values can be mapped into different
+ * ranges... */
+
+ {
+ /* Closest by depth, furthest by depth range */
+ TestDepthState rect0_state = {
+ 0xff0000ff, /* rgba color */
+ -10, /* depth */
+ TRUE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0.5, 1 /* depth range */
+ };
+ /* Furthest by depth, nearest by depth range */
+ TestDepthState rect1_state = {
+ 0x00ff00ff, /* rgba color */
+ -70, /* depth */
+ TRUE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_GREATER,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 0.5 /* depth range */
+ };
+
+ test_depth (state, 0, 1, /* position */
+ &rect0_state, &rect1_state, NULL,
+ FALSE, /* legacy mode */
+ 0xff0000ff); /* expected */
+ }
+
+ /* Test that the legacy cogl_set_depth_test_enabled() API still
+ * works... */
+
+ {
+ /* Nearest */
+ TestDepthState rect0_state = {
+ 0xff0000ff, /* rgba color */
+ -10, /* depth */
+ FALSE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_LESS,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 1 /* depth range */
+ };
+ /* Furthest */
+ TestDepthState rect1_state = {
+ 0x00ff00ff, /* rgba color */
+ -70, /* depth */
+ FALSE, /* depth test enable */
+ COGL_DEPTH_TEST_FUNCTION_LESS,
+ TRUE, /* depth write enable */
+ TRUE, /* FB depth write enable */
+ 0, 1 /* depth range */
+ };
+
+ cogl_set_depth_test_enabled (TRUE);
+ test_depth (state, 0, 2, /* position */
+ &rect0_state, &rect1_state, NULL,
+ TRUE, /* legacy mode */
+ 0xff0000ff); /* expected */
+ cogl_set_depth_test_enabled (FALSE);
+ test_depth (state, 1, 2, /* position */
+ &rect0_state, &rect1_state, NULL,
+ TRUE, /* legacy mode */
+ 0x00ff00ff); /* expected */
+ }
+}
+
+void
+test_depth_test (void)
+{
+ TestState state;
+
+ cogl_framebuffer_orthographic (test_fb, 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-euler-quaternion.c b/cogl/tests/conform/test-euler-quaternion.c
new file mode 100644
index 000000000..c250bec0b
--- /dev/null
+++ b/cogl/tests/conform/test-euler-quaternion.c
@@ -0,0 +1,81 @@
+#include <cogl/cogl.h>
+#include <math.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+/* Macros are used here instead of functions so that the
+ * g_assert_cmpfloat will give a more interesting message when it
+ * fails */
+
+#define COMPARE_FLOATS(a, b) \
+ do { \
+ if (fabsf ((a) - (b)) >= 0.0001f) \
+ g_assert_cmpfloat ((a), ==, (b)); \
+ } while (0)
+
+#define COMPARE_MATRICES(a, b) \
+ do { \
+ COMPARE_FLOATS ((a)->xx, (b)->xx); \
+ COMPARE_FLOATS ((a)->yx, (b)->yx); \
+ COMPARE_FLOATS ((a)->zx, (b)->zx); \
+ COMPARE_FLOATS ((a)->wx, (b)->wx); \
+ COMPARE_FLOATS ((a)->xy, (b)->xy); \
+ COMPARE_FLOATS ((a)->yy, (b)->yy); \
+ COMPARE_FLOATS ((a)->zy, (b)->zy); \
+ COMPARE_FLOATS ((a)->wy, (b)->wy); \
+ COMPARE_FLOATS ((a)->xz, (b)->xz); \
+ COMPARE_FLOATS ((a)->yz, (b)->yz); \
+ COMPARE_FLOATS ((a)->zz, (b)->zz); \
+ COMPARE_FLOATS ((a)->wz, (b)->wz); \
+ COMPARE_FLOATS ((a)->xw, (b)->xw); \
+ COMPARE_FLOATS ((a)->yw, (b)->yw); \
+ COMPARE_FLOATS ((a)->zw, (b)->zw); \
+ COMPARE_FLOATS ((a)->ww, (b)->ww); \
+ } while (0)
+
+void
+test_euler_quaternion (void)
+{
+ CoglEuler euler;
+ CoglQuaternion quaternion;
+ CoglMatrix matrix_a, matrix_b;
+
+ /* Try doing the rotation with three separate rotations */
+ cogl_matrix_init_identity (&matrix_a);
+ cogl_matrix_rotate (&matrix_a, -30.0f, 0.0f, 1.0f, 0.0f);
+ cogl_matrix_rotate (&matrix_a, 40.0f, 1.0f, 0.0f, 0.0f);
+ cogl_matrix_rotate (&matrix_a, 50.0f, 0.0f, 0.0f, 1.0f);
+
+ /* And try the same rotation with a euler */
+ cogl_euler_init (&euler, -30, 40, 50);
+ cogl_matrix_init_from_euler (&matrix_b, &euler);
+
+ /* Verify that the matrices are approximately the same */
+ COMPARE_MATRICES (&matrix_a, &matrix_b);
+
+ /* Try converting the euler to a matrix via a quaternion */
+ cogl_quaternion_init_from_euler (&quaternion, &euler);
+ memset (&matrix_b, 0, sizeof (matrix_b));
+ cogl_matrix_init_from_quaternion (&matrix_b, &quaternion);
+ COMPARE_MATRICES (&matrix_a, &matrix_b);
+
+ /* Try applying the rotation from a euler to a framebuffer */
+ cogl_framebuffer_identity_matrix (test_fb);
+ cogl_framebuffer_rotate_euler (test_fb, &euler);
+ memset (&matrix_b, 0, sizeof (matrix_b));
+ cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b);
+ COMPARE_MATRICES (&matrix_a, &matrix_b);
+
+ /* And again with a quaternion */
+ cogl_framebuffer_identity_matrix (test_fb);
+ cogl_framebuffer_rotate_quaternion (test_fb, &quaternion);
+ memset (&matrix_b, 0, sizeof (matrix_b));
+ cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b);
+ COMPARE_MATRICES (&matrix_a, &matrix_b);
+
+ /* FIXME: This needs a lot more tests! */
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-fence.c b/cogl/tests/conform/test-fence.c
new file mode 100644
index 000000000..d5e3586f5
--- /dev/null
+++ b/cogl/tests/conform/test-fence.c
@@ -0,0 +1,63 @@
+#include <cogl/cogl.h>
+
+/* These will be redefined in config.h */
+#undef COGL_ENABLE_EXPERIMENTAL_2_0_API
+#undef COGL_ENABLE_EXPERIMENTAL_API
+
+#include "test-utils.h"
+#include "config.h"
+
+/* I'm writing this on the train after having dinner at a churrascuria. */
+#define MAGIC_CHUNK_O_DATA ((void *) 0xdeadbeef)
+
+static GMainLoop *loop;
+
+gboolean
+timeout (void *user_data)
+{
+ g_assert (!"timeout not reached");
+
+ return FALSE;
+}
+
+void
+callback (CoglFence *fence,
+ void *user_data)
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+
+ test_utils_check_pixel (test_fb, fb_width - 1, fb_height - 1, 0x00ff0000);
+ g_assert (user_data == MAGIC_CHUNK_O_DATA && "callback data not mangled");
+
+ g_main_loop_quit (loop);
+}
+
+void
+test_fence (void)
+{
+ GSource *cogl_source;
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglFenceClosure *closure;
+
+ cogl_source = cogl_glib_source_new (test_ctx, G_PRIORITY_DEFAULT);
+ g_source_attach (cogl_source, NULL);
+ loop = g_main_loop_new (NULL, TRUE);
+
+ cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100);
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR,
+ 0.0f, 1.0f, 0.0f, 0.0f);
+
+ closure = cogl_framebuffer_add_fence_callback (test_fb,
+ callback,
+ MAGIC_CHUNK_O_DATA);
+ g_assert (closure != NULL);
+
+ g_timeout_add_seconds (5, timeout, NULL);
+
+ g_main_loop_run (loop);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-fixed.c b/cogl/tests/conform/test-fixed.c
new file mode 100644
index 000000000..175b2d195
--- /dev/null
+++ b/cogl/tests/conform/test-fixed.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+void
+test_fixed (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_FLOAT (1.0));
+ g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_INT (1));
+
+ g_assert_cmpint (COGL_FIXED_0_5, ==, COGL_FIXED_FROM_FLOAT (0.5));
+
+ g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_1), ==, 1.0);
+ g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_0_5), ==, 0.5);
+}
+
diff --git a/cogl/tests/conform/test-fixtures.c b/cogl/tests/conform/test-fixtures.c
new file mode 100644
index 000000000..dfc20437d
--- /dev/null
+++ b/cogl/tests/conform/test-fixtures.c
@@ -0,0 +1,12 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+void
+test_simple_rig (void)
+{
+ ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+}
diff --git a/cogl/tests/conform/test-framebuffer-get-bits.c b/cogl/tests/conform/test-framebuffer-get-bits.c
new file mode 100644
index 000000000..31c220d78
--- /dev/null
+++ b/cogl/tests/conform/test-framebuffer-get-bits.c
@@ -0,0 +1,40 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+void
+test_framebuffer_get_bits (void)
+{
+ CoglTexture2D *tex_a =
+ cogl_texture_2d_new_with_size (test_ctx,
+ 16, 16); /* width/height */
+ CoglOffscreen *offscreen_a =
+ cogl_offscreen_new_with_texture (tex_a);
+ CoglFramebuffer *fb_a = offscreen_a;
+ CoglTexture2D *tex_rgba =
+ cogl_texture_2d_new_with_size (test_ctx,
+ 16, 16); /* width/height */
+ CoglOffscreen *offscreen_rgba =
+ cogl_offscreen_new_with_texture (tex_rgba);
+ CoglFramebuffer *fb_rgba = offscreen_rgba;
+
+ cogl_texture_set_components (tex_a,
+ COGL_TEXTURE_COMPONENTS_A);
+ cogl_framebuffer_allocate (fb_a, NULL);
+ cogl_framebuffer_allocate (fb_rgba, NULL);
+
+ g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_a), ==, 0);
+ g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_a), ==, 0);
+ g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_a), ==, 0);
+ g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_a), >=, 1);
+
+ g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_rgba), >=, 1);
+ g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_rgba), >=, 1);
+ g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_rgba), >=, 1);
+ g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_rgba), >=, 1);
+
+ cogl_object_unref (fb_rgba);
+ cogl_object_unref (tex_rgba);
+ cogl_object_unref (fb_a);
+ cogl_object_unref (tex_a);
+}
diff --git a/cogl/tests/conform/test-gles2-context.c b/cogl/tests/conform/test-gles2-context.c
new file mode 100644
index 000000000..bedc30a02
--- /dev/null
+++ b/cogl/tests/conform/test-gles2-context.c
@@ -0,0 +1,962 @@
+
+#include <cogl/cogl.h>
+#include <cogl/cogl-gles2.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ CoglTexture *offscreen_texture;
+ CoglOffscreen *offscreen;
+ CoglGLES2Context *gles2_ctx;
+ const CoglGLES2Vtable *gles2;
+} TestState;
+
+static void
+test_push_pop_single_context (void)
+{
+ CoglTexture *offscreen_texture;
+ CoglOffscreen *offscreen;
+ CoglPipeline *pipeline;
+ CoglGLES2Context *gles2_ctx;
+ const CoglGLES2Vtable *gles2;
+ CoglError *error = NULL;
+
+ offscreen_texture =
+ cogl_texture_2d_new_with_size (test_ctx,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb));
+ offscreen = cogl_offscreen_new_with_texture (offscreen_texture);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, offscreen_texture);
+
+ gles2_ctx = cogl_gles2_context_new (test_ctx, &error);
+ if (!gles2_ctx)
+ g_error ("Failed to create GLES2 context: %s\n", error->message);
+
+ gles2 = cogl_gles2_context_get_vtable (gles2_ctx);
+
+ /* Clear onscreen to 0xffff00 using GLES2 */
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ test_fb,
+ test_fb,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glClearColor (1, 1, 0, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xffff00ff);
+
+ /* Clear offscreen to 0xff0000 using GLES2 and then copy the result
+ * onscreen.
+ *
+ * If we fail to bind the new context here then we'd probably end up
+ * clearing onscreen to 0xff0000 and copying 0xffff00 to onscreen
+ * instead.
+ */
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ offscreen,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glClearColor (1, 0, 0, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1, 1, 1, -1);
+ /* NB: Cogl doesn't automatically support mid-scene modifications
+ * of textures and so we explicitly flush the drawn rectangle to the
+ * framebuffer now otherwise it may be batched until after the
+ * offscreen texture has been modified again. */
+ cogl_flush ();
+
+ /* Clear the offscreen framebuffer to blue using GLES2 before
+ * reading back from the onscreen framebuffer in case we mistakenly
+ * read from the offscreen framebuffer and get a false positive
+ */
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ offscreen,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glClearColor (0, 0, 1, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xff0000ff);
+
+ /* Now copy the offscreen blue clear to the onscreen framebufer and
+ * check that too */
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1, 1, 1, -1);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0x0000ffff);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ test_fb,
+ test_fb,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glClearColor (1, 0, 1, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xff00ffff);
+
+
+ cogl_object_unref (gles2_ctx);
+
+ cogl_object_unref (pipeline);
+}
+
+static void
+create_gles2_context (CoglTexture **offscreen_texture,
+ CoglOffscreen **offscreen,
+ CoglPipeline **pipeline,
+ CoglGLES2Context **gles2_ctx,
+ const CoglGLES2Vtable **gles2)
+{
+ CoglError *error = NULL;
+
+ *offscreen_texture =
+ cogl_texture_2d_new_with_size (test_ctx,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb));
+ *offscreen = cogl_offscreen_new_with_texture (*offscreen_texture);
+
+ *pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (*pipeline, 0, *offscreen_texture);
+
+ *gles2_ctx = cogl_gles2_context_new (test_ctx, &error);
+ if (!*gles2_ctx)
+ g_error ("Failed to create GLES2 context: %s\n", error->message);
+
+ *gles2 = cogl_gles2_context_get_vtable (*gles2_ctx);
+}
+
+static void
+test_push_pop_multi_context (void)
+{
+ CoglTexture *offscreen_texture0;
+ CoglOffscreen *offscreen0;
+ CoglPipeline *pipeline0;
+ CoglGLES2Context *gles2_ctx0;
+ const CoglGLES2Vtable *gles20;
+ CoglTexture *offscreen_texture1;
+ CoglOffscreen *offscreen1;
+ CoglPipeline *pipeline1;
+ CoglGLES2Context *gles2_ctx1;
+ const CoglGLES2Vtable *gles21;
+ CoglError *error = NULL;
+
+ create_gles2_context (&offscreen_texture0,
+ &offscreen0,
+ &pipeline0,
+ &gles2_ctx0,
+ &gles20);
+
+ create_gles2_context (&offscreen_texture1,
+ &offscreen1,
+ &pipeline1,
+ &gles2_ctx1,
+ &gles21);
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx0,
+ offscreen0,
+ offscreen0,
+ &error))
+ {
+ g_error ("Failed to push gles2 context 0: %s\n", error->message);
+ }
+
+ gles20->glClearColor (1, 0, 0, 1);
+ gles20->glClear (GL_COLOR_BUFFER_BIT);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx1,
+ offscreen1,
+ offscreen1,
+ &error))
+ {
+ g_error ("Failed to push gles2 context 1: %s\n", error->message);
+ }
+
+ gles21->glClearColor (0, 1, 0, 1);
+ gles21->glClear (GL_COLOR_BUFFER_BIT);
+
+ cogl_pop_gles2_context (test_ctx);
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xffffffff);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline0,
+ -1, 1, 1, -1);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xff0000ff);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline1,
+ -1, 1, 1, -1);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0x00ff00ff);
+}
+
+static GLuint
+create_gles2_framebuffer (const CoglGLES2Vtable *gles2,
+ int width,
+ int height)
+{
+ GLuint texture_handle;
+ GLuint fbo_handle;
+ GLenum status;
+
+ gles2->glGenTextures (1, &texture_handle);
+ gles2->glGenFramebuffers (1, &fbo_handle);
+
+ gles2->glBindTexture (GL_TEXTURE_2D, texture_handle);
+ gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gles2->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL);
+ gles2->glBindTexture (GL_TEXTURE_2D, 0);
+
+ gles2->glBindFramebuffer (GL_FRAMEBUFFER, fbo_handle);
+ gles2->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture_handle, 0);
+
+ status = gles2->glCheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (cogl_test_verbose ())
+ g_print ("status for gles2 framebuffer = 0x%x %s\n",
+ status, status == GL_FRAMEBUFFER_COMPLETE ? "(complete)" : "(?)");
+
+ gles2->glBindFramebuffer (GL_FRAMEBUFFER, 0);
+
+ return fbo_handle;
+}
+
+static void
+test_gles2_read_pixels (void)
+{
+ CoglTexture *offscreen_texture;
+ CoglOffscreen *offscreen;
+ CoglPipeline *pipeline;
+ CoglGLES2Context *gles2_ctx;
+ const CoglGLES2Vtable *gles2;
+ CoglError *error = NULL;
+ GLubyte pixel[4];
+ GLuint fbo_handle;
+
+ create_gles2_context (&offscreen_texture,
+ &offscreen,
+ &pipeline,
+ &gles2_ctx,
+ &gles2);
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ offscreen,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glClearColor (1, 0, 0, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+ gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+
+ test_utils_compare_pixel (pixel, 0xff0000ff);
+
+ fbo_handle = create_gles2_framebuffer (gles2, 256, 256);
+
+ gles2->glBindFramebuffer (GL_FRAMEBUFFER, fbo_handle);
+
+ gles2->glClearColor (0, 1, 0, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+ gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+
+ test_utils_compare_pixel (pixel, 0x00ff00ff);
+
+ gles2->glBindFramebuffer (GL_FRAMEBUFFER, 0);
+
+ gles2->glClearColor (0, 1, 1, 1);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+ gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+
+ test_utils_compare_pixel (pixel, 0x00ffffff);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xffffffff);
+
+ /* Bind different read and write buffers */
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ test_fb,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+
+ test_utils_compare_pixel (pixel, 0x00ffffff);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ test_utils_check_pixel (test_fb, 0, 0, 0xffffffff);
+
+ /* Bind different read and write buffers (the other way around from
+ * before so when we test with COGL_TEST_ONSCREEN=1 we will read
+ * from an onscreen framebuffer) */
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ test_fb,
+ offscreen,
+ &error))
+ {
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+ }
+
+ gles2->glReadPixels (0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+
+ test_utils_compare_pixel (pixel, 0xffffffff);
+
+ cogl_pop_gles2_context (test_ctx);
+}
+
+void
+test_gles2_context (void)
+{
+ test_push_pop_single_context ();
+ test_push_pop_multi_context ();
+ test_gles2_read_pixels ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
+static GLuint
+create_shader (const CoglGLES2Vtable *gles2,
+ GLenum type,
+ const char *source)
+{
+ GLuint shader;
+ GLint status;
+ int length = strlen (source);
+
+ shader = gles2->glCreateShader (type);
+ gles2->glShaderSource (shader, 1, &source, &length);
+ gles2->glCompileShader (shader);
+ gles2->glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
+
+ if (!status)
+ {
+ char buf[512];
+
+ gles2->glGetShaderInfoLog (shader, sizeof (buf), NULL, buf);
+
+ g_error ("Shader compilation failed:\n%s", buf);
+ }
+
+ return shader;
+}
+
+static GLuint
+create_program (const CoglGLES2Vtable *gles2,
+ const char *vertex_shader_source,
+ const char *fragment_shader_source)
+{
+ GLuint fragment_shader, vertex_shader, program;
+ GLint status;
+
+ vertex_shader =
+ create_shader (gles2, GL_VERTEX_SHADER, vertex_shader_source);
+ fragment_shader =
+ create_shader (gles2, GL_FRAGMENT_SHADER, fragment_shader_source);
+
+ program = gles2->glCreateProgram ();
+ gles2->glAttachShader (program, vertex_shader);
+ gles2->glAttachShader (program, fragment_shader);
+ gles2->glLinkProgram (program);
+
+ gles2->glGetProgramiv (program, GL_LINK_STATUS, &status);
+
+ if (!status)
+ {
+ char buf[512];
+
+ gles2->glGetProgramInfoLog (program, sizeof (buf), NULL, buf);
+
+ g_error ("Program linking failed:\n%s", buf);
+ }
+
+ return program;
+}
+
+typedef struct
+{
+ const CoglGLES2Vtable *gles2;
+ GLint color_location;
+ GLint pos_location;
+ int fb_width, fb_height;
+} PaintData;
+
+typedef void (* PaintMethod) (PaintData *data);
+
+/* Top vertices are counter-clockwise */
+static const float top_vertices[] =
+ {
+ -1.0f, 0.0f,
+ 1.0f, 0.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+/* Bottom vertices are clockwise */
+static const float bottom_vertices[] =
+ {
+ 1.0f, 0.0f,
+ 1.0f, -1.0f,
+ -1.0f, 0.0f,
+ -1.0f, -1.0f
+ };
+
+static void
+paint_quads (PaintData *data)
+{
+ const CoglGLES2Vtable *gles2 = data->gles2;
+
+ gles2->glEnableVertexAttribArray (data->pos_location);
+
+ /* Paint the top half in red */
+ gles2->glUniform4f (data->color_location,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+ gles2->glVertexAttribPointer (data->pos_location,
+ 2, /* size */
+ GL_FLOAT,
+ GL_FALSE, /* not normalized */
+ sizeof (float) * 2,
+ top_vertices);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ /* Paint the bottom half in blue */
+ gles2->glUniform4f (data->color_location,
+ 0.0f, 0.0f, 1.0f, 1.0f);
+ gles2->glVertexAttribPointer (data->pos_location,
+ 2, /* size */
+ GL_FLOAT,
+ GL_FALSE, /* not normalized */
+ sizeof (float) * 2,
+ bottom_vertices);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+}
+
+static void
+paint_viewport (PaintData *data)
+{
+ const CoglGLES2Vtable *gles2 = data->gles2;
+ int viewport[4];
+
+ /* Vertices to fill the entire framebuffer */
+ static const float vertices[] =
+ {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+
+ gles2->glEnableVertexAttribArray (data->pos_location);
+ gles2->glVertexAttribPointer (data->pos_location,
+ 2, /* size */
+ GL_FLOAT,
+ GL_FALSE, /* not normalized */
+ sizeof (float) * 2,
+ vertices);
+
+ /* Paint the top half in red */
+ gles2->glViewport (0, data->fb_height / 2,
+ data->fb_width, data->fb_height / 2);
+ gles2->glUniform4f (data->color_location,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ /* Paint the bottom half in blue */
+ gles2->glViewport (0, 0, data->fb_width, data->fb_height / 2);
+ gles2->glUniform4f (data->color_location,
+ 0.0f, 0.0f, 1.0f, 1.0f);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ gles2->glGetIntegerv (GL_VIEWPORT, viewport);
+ g_assert_cmpint (viewport[0], ==, 0.0f);
+ g_assert_cmpint (viewport[1], ==, 0.0f);
+ g_assert_cmpint (viewport[2], ==, data->fb_width);
+ g_assert_cmpint (viewport[3], ==, data->fb_height / 2);
+}
+
+static void
+paint_scissor (PaintData *data)
+{
+ const CoglGLES2Vtable *gles2 = data->gles2;
+ float scissor[4];
+
+ gles2->glEnable (GL_SCISSOR_TEST);
+
+ /* Paint the top half in red */
+ gles2->glScissor (0, data->fb_height / 2,
+ data->fb_width, data->fb_height / 2);
+ gles2->glClearColor (1.0, 0.0, 0.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ /* Paint the bottom half in blue */
+ gles2->glScissor (0, 0, data->fb_width, data->fb_height / 2);
+ gles2->glClearColor (0.0, 0.0, 1.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ gles2->glGetFloatv (GL_SCISSOR_BOX, scissor);
+ g_assert_cmpfloat (scissor[0], ==, 0.0f);
+ g_assert_cmpfloat (scissor[1], ==, 0.0f);
+ g_assert_cmpfloat (scissor[2], ==, data->fb_width);
+ g_assert_cmpfloat (scissor[3], ==, data->fb_height / 2);
+}
+
+static void
+paint_cull (PaintData *data)
+{
+ const CoglGLES2Vtable *gles2 = data->gles2;
+ GLint front_face;
+ int i;
+
+ gles2->glEnableVertexAttribArray (data->pos_location);
+ gles2->glEnable (GL_CULL_FACE);
+
+ /* First time round we'll use GL_CCW as the front face so that the
+ * bottom quad will be culled */
+ gles2->glFrontFace (GL_CCW);
+ gles2->glUniform4f (data->color_location,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+
+ gles2->glGetIntegerv (GL_FRONT_FACE, &front_face);
+ g_assert_cmpint (front_face, ==, GL_CCW);
+
+ for (i = 0; i < 2; i++)
+ {
+ /* Paint both quads in the same color. One of these will be
+ * culled */
+ gles2->glVertexAttribPointer (data->pos_location,
+ 2, /* size */
+ GL_FLOAT,
+ GL_FALSE, /* not normalized */
+ sizeof (float) * 2,
+ top_vertices);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ gles2->glVertexAttribPointer (data->pos_location,
+ 2, /* size */
+ GL_FLOAT,
+ GL_FALSE, /* not normalized */
+ sizeof (float) * 2,
+ bottom_vertices);
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ /* Second time round we'll use GL_CW as the front face so that the
+ * top quad will be culled */
+ gles2->glFrontFace (GL_CW);
+ gles2->glUniform4f (data->color_location,
+ 0.0f, 0.0f, 1.0f, 1.0f);
+
+ gles2->glGetIntegerv (GL_FRONT_FACE, &front_face);
+ g_assert_cmpint (front_face, ==, GL_CW);
+ }
+}
+
+static void
+verify_read_pixels (const PaintData *data)
+{
+ int stride = data->fb_width * 4;
+ uint8_t *buf = g_malloc (data->fb_height * stride);
+
+ data->gles2->glReadPixels (0, 0, /* x/y */
+ data->fb_width, data->fb_height,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ buf);
+
+ /* In GL, the lines earlier in the buffer are the bottom */
+ /* Bottom should be blue */
+ test_utils_compare_pixel (buf + data->fb_width / 2 * 4 +
+ data->fb_height / 4 * stride,
+ 0x0000ffff);
+ /* Top should be red */
+ test_utils_compare_pixel (buf + data->fb_width / 2 * 4 +
+ data->fb_height * 3 / 4 * stride,
+ 0xff0000ff);
+
+ g_free (buf);
+}
+
+void
+test_gles2_context_fbo (void)
+{
+ static const char vertex_shader_source[] =
+ "attribute vec2 pos;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " gl_Position = vec4 (pos, 0.0, 1.0);\n"
+ "}\n";
+ static const char fragment_shader_source[] =
+ "precision mediump float;\n"
+ "uniform vec4 color;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ static const PaintMethod paint_methods[] =
+ {
+ paint_quads,
+ paint_viewport,
+ paint_scissor,
+ paint_cull
+ };
+ int i;
+ PaintData data;
+
+ data.fb_width = cogl_framebuffer_get_width (test_fb);
+ data.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ for (i = 0; i < G_N_ELEMENTS (paint_methods); i++)
+ {
+ CoglTexture *offscreen_texture;
+ CoglOffscreen *offscreen;
+ CoglPipeline *pipeline;
+ CoglGLES2Context *gles2_ctx;
+ GLuint program;
+ CoglError *error = NULL;
+
+ create_gles2_context (&offscreen_texture,
+ &offscreen,
+ &pipeline,
+ &gles2_ctx,
+ &data.gles2);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ offscreen,
+ &error))
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+
+ program = create_program (data.gles2,
+ vertex_shader_source,
+ fragment_shader_source);
+
+ data.gles2->glClearColor (1.0, 1.0, 0.0, 1.0);
+ data.gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ data.gles2->glUseProgram (program);
+
+ data.color_location = data.gles2->glGetUniformLocation (program, "color");
+ if (data.color_location == -1)
+ g_error ("Couldn't find ‘color’ uniform");
+
+ data.pos_location = data.gles2->glGetAttribLocation (program, "pos");
+ if (data.pos_location == -1)
+ g_error ("Couldn't find ‘pos’ attribute");
+
+ paint_methods[i] (&data);
+
+ verify_read_pixels (&data);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ cogl_object_unref (offscreen);
+ cogl_object_unref (gles2_ctx);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1.0f, 1.0f,
+ 1.0f, -1.0f);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (offscreen_texture);
+
+ /* Top half of the framebuffer should be red */
+ test_utils_check_pixel (test_fb,
+ data.fb_width / 2, data.fb_height / 4,
+ 0xff0000ff);
+ /* Bottom half should be blue */
+ test_utils_check_pixel (test_fb,
+ data.fb_width / 2, data.fb_height * 3 / 4,
+ 0x0000ffff);
+ }
+}
+
+/* Position to draw a rectangle in. The top half of this rectangle
+ * will be red, and the bottom will be blue */
+#define RECTANGLE_DRAW_X 10
+#define RECTANGLE_DRAW_Y 15
+
+/* Position to copy the rectangle to in the destination texture */
+#define RECTANGLE_COPY_X 110
+#define RECTANGLE_COPY_Y 115
+
+#define RECTANGLE_WIDTH 30
+#define RECTANGLE_HEIGHT 40
+
+static void
+verify_region (const CoglGLES2Vtable *gles2,
+ int x,
+ int y,
+ int width,
+ int height,
+ uint32_t expected_pixel)
+{
+ uint8_t *buf, *p;
+
+ buf = g_malloc (width * height * 4);
+
+ gles2->glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buf);
+
+ for (p = buf + width * height * 4; p > buf; p -= 4)
+ test_utils_compare_pixel (p - 4, expected_pixel);
+
+ g_free (buf);
+}
+
+void
+test_gles2_context_copy_tex_image (void)
+{
+ static const char vertex_shader_source[] =
+ "attribute vec2 pos;\n"
+ "attribute vec2 tex_coord_attrib;\n"
+ "varying vec2 tex_coord_varying;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " gl_Position = vec4 (pos, 0.0, 1.0);\n"
+ " tex_coord_varying = tex_coord_attrib;\n"
+ "}\n";
+ static const char fragment_shader_source[] =
+ "precision mediump float;\n"
+ "varying vec2 tex_coord_varying;\n"
+ "uniform sampler2D tex;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " gl_FragColor = texture2D (tex, tex_coord_varying);\n"
+ "}\n";
+ static const float verts[] =
+ {
+ -1.0f, -1.0f, 0.0f, 0.0f,
+ 1.0f, -1.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f
+ };
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglTexture *offscreen_texture;
+ CoglOffscreen *offscreen;
+ CoglPipeline *pipeline;
+ CoglGLES2Context *gles2_ctx;
+ const CoglGLES2Vtable *gles2;
+ CoglError *error = NULL;
+ GLuint tex;
+ GLint tex_uniform_location;
+ GLint pos_location;
+ GLint tex_coord_location;
+ GLuint program;
+
+ create_gles2_context (&offscreen_texture,
+ &offscreen,
+ &pipeline,
+ &gles2_ctx,
+ &gles2);
+
+ if (!cogl_push_gles2_context (test_ctx,
+ gles2_ctx,
+ offscreen,
+ offscreen,
+ &error))
+ g_error ("Failed to push gles2 context: %s\n", error->message);
+
+ gles2->glClearColor (1.0, 1.0, 0.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ /* Draw a rectangle using clear and the scissor so that we don't
+ * have to create a shader */
+ gles2->glEnable (GL_SCISSOR_TEST);
+
+ /* Top half red */
+ gles2->glScissor (RECTANGLE_DRAW_X,
+ RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2);
+ gles2->glClearColor (1.0, 0.0, 0.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+ /* Bottom half blue */
+ gles2->glScissor (RECTANGLE_DRAW_X,
+ RECTANGLE_DRAW_Y,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2);
+ gles2->glClearColor (0.0, 0.0, 1.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ /* Draw where the rectangle would be if the coordinates were flipped
+ * in white to make it obvious that that is the problem if the
+ * assertion fails */
+ gles2->glScissor (RECTANGLE_DRAW_X,
+ fb_width - (RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT),
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT);
+ gles2->glClearColor (1.0, 1.0, 1.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ gles2->glDisable (GL_SCISSOR_TEST);
+
+ /* Create a texture */
+ gles2->glGenTextures (1, &tex);
+ gles2->glBindTexture (GL_TEXTURE_2D, tex);
+ gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ /* Copy the entire framebuffer into the texture */
+ gles2->glCopyTexImage2D (GL_TEXTURE_2D,
+ 0, /* level */
+ GL_RGBA,
+ 0, 0, /* x/y */
+ fb_width, fb_height,
+ 0 /* border */);
+
+ /* Copy the rectangle into another part of the texture */
+ gles2->glCopyTexSubImage2D (GL_TEXTURE_2D,
+ 0, /* level */
+ RECTANGLE_COPY_X,
+ RECTANGLE_COPY_Y,
+ RECTANGLE_DRAW_X,
+ RECTANGLE_DRAW_Y,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT);
+
+ /* Clear the framebuffer to make the test more thorough */
+ gles2->glClearColor (1.0, 1.0, 0.0, 1.0);
+ gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+ /* Create a program to render the texture */
+ program = create_program (gles2,
+ vertex_shader_source,
+ fragment_shader_source);
+
+ pos_location =
+ gles2->glGetAttribLocation (program, "pos");
+ if (pos_location == -1)
+ g_error ("Couldn't find ‘pos’ attribute");
+
+ tex_coord_location =
+ gles2->glGetAttribLocation (program, "tex_coord_attrib");
+ if (tex_coord_location == -1)
+ g_error ("Couldn't find ‘tex_coord_attrib’ attribute");
+
+ tex_uniform_location =
+ gles2->glGetUniformLocation (program, "tex");
+ if (tex_uniform_location == -1)
+ g_error ("Couldn't find ‘tex’ uniform");
+
+ gles2->glUseProgram (program);
+
+ gles2->glUniform1i (tex_uniform_location, 0);
+
+ /* Render the texture to fill the framebuffer */
+ gles2->glEnableVertexAttribArray (pos_location);
+ gles2->glVertexAttribPointer (pos_location,
+ 2, /* n_components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ sizeof (float) * 4,
+ verts);
+ gles2->glEnableVertexAttribArray (tex_coord_location);
+ gles2->glVertexAttribPointer (tex_coord_location,
+ 2, /* n_components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ sizeof (float) * 4,
+ verts + 2);
+
+ gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ /* Verify top of drawn rectangle is red */
+ verify_region (gles2,
+ RECTANGLE_DRAW_X,
+ RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2,
+ 0xff0000ff);
+ /* Verify bottom of drawn rectangle is blue */
+ verify_region (gles2,
+ RECTANGLE_DRAW_X,
+ RECTANGLE_DRAW_Y,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2,
+ 0x0000ffff);
+ /* Verify top of copied rectangle is red */
+ verify_region (gles2,
+ RECTANGLE_COPY_X,
+ RECTANGLE_COPY_Y + RECTANGLE_HEIGHT / 2,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2,
+ 0xff0000ff);
+ /* Verify bottom of copied rectangle is blue */
+ verify_region (gles2,
+ RECTANGLE_COPY_X,
+ RECTANGLE_COPY_Y,
+ RECTANGLE_WIDTH,
+ RECTANGLE_HEIGHT / 2,
+ 0x0000ffff);
+
+ cogl_pop_gles2_context (test_ctx);
+
+ cogl_object_unref (offscreen);
+ cogl_object_unref (gles2_ctx);
+ cogl_object_unref (pipeline);
+ cogl_object_unref (offscreen_texture);
+}
diff --git a/cogl/tests/conform/test-just-vertex-shader.c b/cogl/tests/conform/test-just-vertex-shader.c
new file mode 100644
index 000000000..60fcaf74c
--- /dev/null
+++ b/cogl/tests/conform/test-just-vertex-shader.c
@@ -0,0 +1,205 @@
+#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0
+
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ int paddiing;
+} TestState;
+
+static CoglTexture *
+create_dummy_texture (void)
+{
+ /* Create a dummy 1x1 green texture to replace the color from the
+ vertex shader */
+ static const uint8_t data[4] = { 0x00, 0xff, 0x00, 0xff };
+
+ return test_utils_texture_new_from_data (test_ctx,
+ 1, 1, /* size */
+ TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGB_888,
+ 4, /* rowstride */
+ data);
+}
+
+static void
+paint_legacy (TestState *state)
+{
+ CoglHandle material = cogl_material_new ();
+ CoglTexture *tex;
+ CoglColor color;
+ CoglError *error = NULL;
+ CoglHandle shader, program;
+
+ cogl_color_init_from_4ub (&color, 0, 0, 0, 255);
+ cogl_clear (&color, COGL_BUFFER_BIT_COLOR);
+
+ /* Set the primary vertex color as red */
+ cogl_color_set_from_4ub (&color, 0xff, 0x00, 0x00, 0xff);
+ cogl_material_set_color (material, &color);
+
+ /* Override the vertex color in the texture environment with a
+ constant green color provided by a texture */
+ tex = create_dummy_texture ();
+ cogl_material_set_layer (material, 0, tex);
+ cogl_object_unref (tex);
+ if (!cogl_material_set_layer_combine (material, 0,
+ "RGBA=REPLACE(TEXTURE)",
+ &error))
+ {
+ g_warning ("Error setting layer combine: %s", error->message);
+ g_assert_not_reached ();
+ }
+
+ /* Set up a dummy vertex shader that does nothing but the usual
+ fixed function transform */
+ shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
+ cogl_shader_source (shader,
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_position_out = "
+ "cogl_modelview_projection_matrix * "
+ "cogl_position_in;\n"
+ " cogl_color_out = cogl_color_in;\n"
+ " cogl_tex_coord_out[0] = cogl_tex_coord_in;\n"
+ "}\n");
+ cogl_shader_compile (shader);
+ if (!cogl_shader_is_compiled (shader))
+ {
+ char *log = cogl_shader_get_info_log (shader);
+ g_warning ("Shader compilation failed:\n%s", log);
+ g_free (log);
+ g_assert_not_reached ();
+ }
+
+ program = cogl_create_program ();
+ cogl_program_attach_shader (program, shader);
+ cogl_program_link (program);
+
+ cogl_handle_unref (shader);
+
+ /* Draw something using the material */
+ cogl_set_source (material);
+ cogl_rectangle (0, 0, 50, 50);
+
+ /* Draw it again using the program. It should look exactly the same */
+ cogl_program_use (program);
+ cogl_rectangle (50, 0, 100, 50);
+ cogl_program_use (COGL_INVALID_HANDLE);
+
+ cogl_handle_unref (material);
+ cogl_handle_unref (program);
+}
+
+static void
+paint (TestState *state)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ CoglTexture *tex;
+ CoglColor color;
+ CoglError *error = NULL;
+ CoglHandle shader, program;
+
+ cogl_color_init_from_4ub (&color, 0, 0, 0, 255);
+ cogl_clear (&color, COGL_BUFFER_BIT_COLOR);
+
+ /* Set the primary vertex color as red */
+ cogl_color_set_from_4ub (&color, 0xff, 0x00, 0x00, 0xff);
+ cogl_pipeline_set_color (pipeline, &color);
+
+ /* Override the vertex color in the texture environment with a
+ constant green color provided by a texture */
+ tex = create_dummy_texture ();
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_object_unref (tex);
+ if (!cogl_pipeline_set_layer_combine (pipeline, 0,
+ "RGBA=REPLACE(TEXTURE)",
+ &error))
+ {
+ g_warning ("Error setting layer combine: %s", error->message);
+ g_assert_not_reached ();
+ }
+
+ /* Set up a dummy vertex shader that does nothing but the usual
+ fixed function transform */
+ shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
+ cogl_shader_source (shader,
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_position_out = "
+ "cogl_modelview_projection_matrix * "
+ "cogl_position_in;\n"
+ " cogl_color_out = cogl_color_in;\n"
+ " cogl_tex_coord_out[0] = cogl_tex_coord_in;\n"
+ "}\n");
+ cogl_shader_compile (shader);
+ if (!cogl_shader_is_compiled (shader))
+ {
+ char *log = cogl_shader_get_info_log (shader);
+ g_warning ("Shader compilation failed:\n%s", log);
+ g_free (log);
+ g_assert_not_reached ();
+ }
+
+ program = cogl_create_program ();
+ cogl_program_attach_shader (program, shader);
+ cogl_program_link (program);
+
+ cogl_handle_unref (shader);
+
+ /* Draw something without the program */
+ cogl_set_source (pipeline);
+ cogl_rectangle (0, 0, 50, 50);
+
+ /* Draw it again using the program. It should look exactly the same */
+ cogl_pipeline_set_user_program (pipeline, program);
+ cogl_handle_unref (program);
+
+ cogl_rectangle (50, 0, 100, 50);
+ cogl_pipeline_set_user_program (pipeline, COGL_INVALID_HANDLE);
+
+ cogl_object_unref (pipeline);
+}
+
+static void
+validate_result (CoglFramebuffer *framebuffer)
+{
+ /* Non-shader version */
+ test_utils_check_pixel (framebuffer, 25, 25, 0x00ff0000);
+ /* Shader version */
+ test_utils_check_pixel (framebuffer, 75, 25, 0x00ff0000);
+}
+
+void
+test_just_vertex_shader (void)
+{
+ TestState state;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ /* XXX: we have to push/pop a framebuffer since this test currently
+ * uses the legacy cogl_rectangle() api. */
+ cogl_push_framebuffer (test_fb);
+
+ paint_legacy (&state);
+ validate_result (test_fb);
+
+ paint (&state);
+ validate_result (test_fb);
+
+ cogl_pop_framebuffer ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-layer-remove.c b/cogl/tests/conform/test-layer-remove.c
new file mode 100644
index 000000000..de1efeccd
--- /dev/null
+++ b/cogl/tests/conform/test-layer-remove.c
@@ -0,0 +1,145 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+#define TEST_SQUARE_SIZE 10
+
+static CoglPipeline *
+create_two_layer_pipeline (void)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ CoglColor color;
+
+ /* The pipeline is initially black */
+ cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255);
+
+ /* The first layer adds a full red component */
+ cogl_color_init_from_4ub (&color, 255, 0, 0, 255);
+ cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+ cogl_pipeline_set_layer_combine (pipeline,
+ 0, /* layer_num */
+ "RGBA=ADD(PREVIOUS,CONSTANT)",
+ NULL);
+
+ /* The second layer adds a full green component */
+ cogl_color_init_from_4ub (&color, 0, 255, 0, 255);
+ cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+ cogl_pipeline_set_layer_combine (pipeline,
+ 1, /* layer_num */
+ "RGBA=ADD(PREVIOUS,CONSTANT)",
+ NULL);
+
+ return pipeline;
+}
+
+static void
+test_color (CoglPipeline *pipeline,
+ uint32_t color,
+ int pos)
+{
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ pos * TEST_SQUARE_SIZE,
+ 0,
+ pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE,
+ TEST_SQUARE_SIZE);
+ test_utils_check_pixel (test_fb,
+ pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE / 2,
+ TEST_SQUARE_SIZE / 2,
+ color);
+}
+
+void
+test_layer_remove (void)
+{
+ CoglPipeline *pipeline0, *pipeline1;
+ CoglColor color;
+ int pos = 0;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ /** TEST 1 **/
+ /* Basic sanity check that the pipeline combines the two colors
+ * together properly */
+ pipeline0 = create_two_layer_pipeline ();
+ test_color (pipeline0, 0xffff00ff, pos++);
+ cogl_object_unref (pipeline0);
+
+ /** TEST 2 **/
+ /* Check that we can remove the second layer */
+ pipeline0 = create_two_layer_pipeline ();
+ cogl_pipeline_remove_layer (pipeline0, 1);
+ test_color (pipeline0, 0xff0000ff, pos++);
+ cogl_object_unref (pipeline0);
+
+ /** TEST 3 **/
+ /* Check that we can remove the first layer */
+ pipeline0 = create_two_layer_pipeline ();
+ cogl_pipeline_remove_layer (pipeline0, 0);
+ test_color (pipeline0, 0x00ff00ff, pos++);
+ cogl_object_unref (pipeline0);
+
+ /** TEST 4 **/
+ /* Check that we can make a copy and remove a layer from the
+ * original pipeline */
+ pipeline0 = create_two_layer_pipeline ();
+ pipeline1 = cogl_pipeline_copy (pipeline0);
+ cogl_pipeline_remove_layer (pipeline0, 1);
+ test_color (pipeline0, 0xff0000ff, pos++);
+ test_color (pipeline1, 0xffff00ff, pos++);
+ cogl_object_unref (pipeline0);
+ cogl_object_unref (pipeline1);
+
+ /** TEST 5 **/
+ /* Check that we can make a copy and remove the second layer from the
+ * new pipeline */
+ pipeline0 = create_two_layer_pipeline ();
+ pipeline1 = cogl_pipeline_copy (pipeline0);
+ cogl_pipeline_remove_layer (pipeline1, 1);
+ test_color (pipeline0, 0xffff00ff, pos++);
+ test_color (pipeline1, 0xff0000ff, pos++);
+ cogl_object_unref (pipeline0);
+ cogl_object_unref (pipeline1);
+
+ /** TEST 6 **/
+ /* Check that we can make a copy and remove the first layer from the
+ * new pipeline */
+ pipeline0 = create_two_layer_pipeline ();
+ pipeline1 = cogl_pipeline_copy (pipeline0);
+ cogl_pipeline_remove_layer (pipeline1, 0);
+ test_color (pipeline0, 0xffff00ff, pos++);
+ test_color (pipeline1, 0x00ff00ff, pos++);
+ cogl_object_unref (pipeline0);
+ cogl_object_unref (pipeline1);
+
+ /** TEST 7 **/
+ /* Check that we can modify a layer in a child pipeline */
+ pipeline0 = create_two_layer_pipeline ();
+ pipeline1 = cogl_pipeline_copy (pipeline0);
+ cogl_color_init_from_4ub (&color, 0, 0, 255, 255);
+ cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color);
+ test_color (pipeline0, 0xffff00ff, pos++);
+ test_color (pipeline1, 0x00ffffff, pos++);
+ cogl_object_unref (pipeline0);
+ cogl_object_unref (pipeline1);
+
+ /** TEST 8 **/
+ /* Check that we can modify a layer in a child pipeline but then remove it */
+ pipeline0 = create_two_layer_pipeline ();
+ pipeline1 = cogl_pipeline_copy (pipeline0);
+ cogl_color_init_from_4ub (&color, 0, 0, 255, 255);
+ cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color);
+ cogl_pipeline_remove_layer (pipeline1, 0);
+ test_color (pipeline0, 0xffff00ff, pos++);
+ test_color (pipeline1, 0x00ff00ff, pos++);
+ cogl_object_unref (pipeline0);
+ cogl_object_unref (pipeline1);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-map-buffer-range.c b/cogl/tests/conform/test-map-buffer-range.c
new file mode 100644
index 000000000..e9792405f
--- /dev/null
+++ b/cogl/tests/conform/test-map-buffer-range.c
@@ -0,0 +1,123 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+static uint8_t
+tex_data[2 * 2 * 4] =
+ {
+ 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff
+ };
+
+/* Vertex data for a quad with all of the texture coordinates set to
+ * the top left (red) pixel */
+static CoglVertexP2T2
+vertex_data[4] =
+ {
+ { -1, -1, 0, 0 },
+ { 1, -1, 0, 0 },
+ { -1, 1, 0, 0 },
+ { 1, 1, 0, 0 }
+ };
+
+void
+test_map_buffer_range (void)
+{
+ CoglTexture2D *tex;
+ CoglPipeline *pipeline;
+ int fb_width, fb_height;
+ CoglAttributeBuffer *buffer;
+ CoglVertexP2T2 *data;
+ CoglAttribute *pos_attribute;
+ CoglAttribute *tex_coord_attribute;
+ CoglPrimitive *primitive;
+
+ tex = cogl_texture_2d_new_from_data (test_ctx,
+ 2, 2, /* width/height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 2 * 4, /* rowstride */
+ tex_data,
+ NULL /* error */);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_layer_wrap_mode (pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+ fb_width = cogl_framebuffer_get_width (test_fb);
+ fb_height = cogl_framebuffer_get_height (test_fb);
+
+ buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (vertex_data),
+ vertex_data);
+
+ /* Replace the texture coordinates of the third vertex with the
+ * coordinates for a green texel */
+ data = cogl_buffer_map_range (buffer,
+ sizeof (vertex_data[0]) * 2,
+ sizeof (vertex_data[0]),
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD_RANGE,
+ NULL); /* don't catch errors */
+ g_assert (data != NULL);
+
+ data->x = vertex_data[2].x;
+ data->y = vertex_data[2].y;
+ data->s = 1.0f;
+ data->t = 0.0f;
+
+ cogl_buffer_unmap (buffer);
+
+ pos_attribute =
+ cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (vertex_data[0]),
+ offsetof (CoglVertexP2T2, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ tex_coord_attribute =
+ cogl_attribute_new (buffer,
+ "cogl_tex_coord_in",
+ sizeof (vertex_data[0]),
+ offsetof (CoglVertexP2T2, s),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 1);
+
+ primitive =
+ cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLE_STRIP,
+ 4, /* n_vertices */
+ pos_attribute,
+ tex_coord_attribute,
+ NULL);
+ cogl_primitive_draw (primitive, test_fb, pipeline);
+ cogl_object_unref (primitive);
+
+ /* Top left pixel should be the one that is replaced to be green */
+ test_utils_check_pixel (test_fb, 1, 1, 0x00ff00ff);
+ /* The other three corners should be left as red */
+ test_utils_check_pixel (test_fb, fb_width - 2, 1, 0xff0000ff);
+ test_utils_check_pixel (test_fb, 1, fb_height - 2, 0xff0000ff);
+ test_utils_check_pixel (test_fb, fb_width - 2, fb_height - 2, 0xff0000ff);
+
+ cogl_object_unref (buffer);
+ cogl_object_unref (pos_attribute);
+ cogl_object_unref (tex_coord_attribute);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (tex);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-materials.c b/cogl/tests/conform/test-materials.c
new file mode 100644
index 000000000..69c9c746f
--- /dev/null
+++ b/cogl/tests/conform/test-materials.c
@@ -0,0 +1,253 @@
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef struct _TestState
+{
+ ClutterGeometry stage_geom;
+} TestState;
+
+static void
+check_quad (int quad_x, int quad_y, uint32_t color)
+{
+ test_utils_check_pixel (x * QUAD_WIDTH + (QUAD_WIDTH / 2),
+ y * QUAD_WIDTH + (QUAD_WIDTH / 2),
+ color);
+}
+
+static void
+test_material_with_primitives (TestState *state,
+ int x, int y,
+ uint32_t color)
+{
+ CoglTextureVertex verts[4] = {
+ { .x = 0, .y = 0, .z = 0 },
+ { .x = 0, .y = QUAD_WIDTH, .z = 0 },
+ { .x = QUAD_WIDTH, .y = QUAD_WIDTH, .z = 0 },
+ { .x = QUAD_WIDTH, .y = 0, .z = 0 },
+ };
+ CoglHandle vbo;
+
+ cogl_push_matrix ();
+
+ cogl_translate (x * QUAD_WIDTH, y * QUAD_WIDTH, 0);
+
+ cogl_rectangle (0, 0, QUAD_WIDTH, QUAD_WIDTH);
+
+ cogl_translate (0, QUAD_WIDTH, 0);
+ cogl_polygon (verts, 4, FALSE);
+
+ cogl_translate (0, QUAD_WIDTH, 0);
+ vbo = cogl_vertex_buffer_new (4);
+ cogl_vertex_buffer_add (vbo,
+ "gl_Vertex",
+ 2, /* n components */
+ COGL_ATTRIBUTE_TYPE_FLOAT,
+ FALSE, /* normalized */
+ sizeof (CoglTextureVertex), /* stride */
+ verts);
+ cogl_vertex_buffer_draw (vbo,
+ COGL_VERTICES_MODE_TRIANGLE_FAN,
+ 0, /* first */
+ 4); /* count */
+ cogl_handle_unref (vbo);
+
+ cogl_pop_matrix ();
+
+ check_quad (x, y, color);
+ check_quad (x, y+1, color);
+ check_quad (x, y+2, color);
+}
+
+static void
+test_invalid_texture_layers (TestState *state, int x, int y)
+{
+ CoglHandle material = cogl_material_new ();
+
+ /* explicitly create a layer with an invalid handle. This may be desireable
+ * if the user also sets a texture combine string that e.g. refers to a
+ * constant color. */
+ cogl_material_set_layer (material, 0, NULL);
+
+ cogl_set_source (material);
+
+ cogl_handle_unref (material);
+
+ /* We expect a white fallback material to be used */
+ test_material_with_primitives (state, x, y, 0xffffffff);
+}
+
+static void
+test_using_all_layers (TestState *state, int x, int y)
+{
+ CoglHandle material = cogl_material_new ();
+ uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff };
+ uint8_t red_pixel[] = { 0xff, 0x00, 0x00, 0xff };
+ CoglHandle white_texture;
+ CoglHandle red_texture;
+ GLint n_layers;
+ int i;
+
+ /* Create a material that uses the maximum number of layers. All but
+ the last layer will use a solid white texture. The last layer
+ will use a red texture. The layers will all be modulated together
+ so the final fragment should be red. */
+
+ white_texture = test_utils_texture_new_from_data (1, 1, TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ 4, white_pixel);
+ red_texture = test_utils_texture_new_from_data (1, 1, TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ 4, red_pixel);
+
+ /* FIXME: Cogl doesn't provide a way to query the maximum number of
+ texture layers so for now we'll just ask GL directly. */
+#ifdef HAVE_COGL_GLES2
+ {
+ GLint n_image_units, n_attribs;
+ /* GLES 2 doesn't have GL_MAX_TEXTURE_UNITS and it uses
+ GL_MAX_TEXTURE_IMAGE_UNITS instead */
+ glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n_image_units);
+ /* Cogl needs a vertex attrib for each layer to upload the texture
+ coordinates */
+ glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, &n_attribs);
+ /* We can't use two of the attribs because they are used by the
+ position and color */
+ n_attribs -= 2;
+ n_layers = MIN (n_attribs, n_image_units);
+ }
+#else
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS, &n_layers);
+#endif
+ /* FIXME: is this still true? */
+ /* Cogl currently can't cope with more than 32 layers so we'll also
+ limit the maximum to that. */
+ if (n_layers > 32)
+ n_layers = 32;
+
+ for (i = 0; i < n_layers; i++)
+ {
+ cogl_material_set_layer_filters (material, i,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_material_set_layer (material, i,
+ i == n_layers - 1 ? red_texture : white_texture);
+ }
+
+ cogl_set_source (material);
+
+ cogl_handle_unref (material);
+ cogl_handle_unref (white_texture);
+ cogl_handle_unref (red_texture);
+
+ /* We expect the final fragment to be red */
+ test_material_with_primitives (state, x, y, 0xff0000ff);
+}
+
+static void
+test_invalid_texture_layers_with_constant_colors (TestState *state,
+ int x, int y)
+{
+ CoglHandle material = cogl_material_new ();
+ CoglColor constant_color;
+
+ /* explicitly create a layer with an invalid handle */
+ cogl_material_set_layer (material, 0, NULL);
+
+ /* ignore the fallback texture on the layer and use a constant color
+ instead */
+ cogl_color_init_from_4ub (&constant_color, 0, 0, 255, 255);
+ cogl_material_set_layer_combine (material, 0,
+ "RGBA=REPLACE(CONSTANT)",
+ NULL);
+ cogl_material_set_layer_combine_constant (material, 0, &constant_color);
+
+ cogl_set_source (material);
+
+ cogl_handle_unref (material);
+
+ /* We expect the final fragments to be green */
+ test_material_with_primitives (state, x, y, 0x0000ffff);
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ test_invalid_texture_layers (state,
+ 0, 0 /* position */
+ );
+ test_invalid_texture_layers_with_constant_colors (state,
+ 1, 0 /* position */
+ );
+ test_using_all_layers (state,
+ 2, 0 /* position */
+ );
+
+ /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+ clutter_main_quit ();
+#endif
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_materials (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+ clutter_actor_get_geometry (stage, &state.stage_geom);
+
+ group = clutter_group_new ();
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing of the stage, since we need to skip
+ * the first few frames, and we wont be doing anything else that
+ * will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-multitexture.c b/cogl/tests/conform/test-multitexture.c
new file mode 100644
index 000000000..da38766aa
--- /dev/null
+++ b/cogl/tests/conform/test-multitexture.c
@@ -0,0 +1,206 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+typedef struct _TestState
+{
+ unsigned int padding;
+} TestState;
+
+static void
+assert_region_color (int x,
+ int y,
+ int width,
+ int height,
+ uint8_t red,
+ uint8_t green,
+ uint8_t blue,
+ uint8_t alpha)
+{
+ uint8_t *data = g_malloc0 (width * height * 4);
+ cogl_read_pixels (x, y, width, height,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ data);
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ uint8_t *pixel = &data[y * width * 4 + x * 4];
+#if 1
+ g_assert (pixel[RED] == red &&
+ pixel[GREEN] == green &&
+ pixel[BLUE] == blue);
+#endif
+ }
+ g_free (data);
+}
+
+/* Creates a texture divided into 4 quads with colors arranged as follows:
+ * (The same value are used in all channels for each texel)
+ *
+ * |-----------|
+ * |0x11 |0x00 |
+ * |+ref | |
+ * |-----------|
+ * |0x00 |0x33 |
+ * | |+ref |
+ * |-----------|
+ *
+ *
+ */
+static CoglHandle
+make_texture (guchar ref)
+{
+ int x;
+ int y;
+ guchar *tex_data, *p;
+ CoglHandle tex;
+ guchar val;
+
+ tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 16);
+
+ for (y = 0; y < QUAD_WIDTH * 2; y++)
+ for (x = 0; x < QUAD_WIDTH * 2; x++)
+ {
+ p = tex_data + (QUAD_WIDTH * 8 * y) + x * 4;
+ if (x < QUAD_WIDTH && y < QUAD_WIDTH)
+ val = 0x11 + ref;
+ else if (x >= QUAD_WIDTH && y >= QUAD_WIDTH)
+ val = 0x33 + ref;
+ else
+ val = 0x00;
+ p[0] = p[1] = p[2] = p[3] = val;
+ }
+
+ /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here
+ * since we don't want to allow Cogl to premultiply our data. */
+ tex = test_utils_texture_new_from_data (QUAD_WIDTH * 2,
+ QUAD_WIDTH * 2,
+ TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ QUAD_WIDTH * 8,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglHandle tex0, tex1;
+ CoglHandle material;
+ CoglBool status;
+ CoglError *error = NULL;
+ float tex_coords[] = {
+ 0, 0, 0.5, 0.5, /* tex0 */
+ 0.5, 0.5, 1, 1 /* tex1 */
+ };
+
+ tex0 = make_texture (0x00);
+ tex1 = make_texture (0x11);
+
+ material = cogl_material_new ();
+
+ /* An arbitrary color which should be replaced by the first texture layer */
+ cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80);
+ cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+
+ cogl_material_set_layer (material, 0, tex0);
+ cogl_material_set_layer_combine (material, 0,
+ "RGBA = REPLACE (TEXTURE)", NULL);
+ /* We'll use nearest filtering mode on the textures, otherwise the
+ edge of the quad can pull in texels from the neighbouring
+ quarters of the texture due to imprecision */
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+
+ cogl_material_set_layer (material, 1, tex1);
+ cogl_material_set_layer_filters (material, 1,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ status = cogl_material_set_layer_combine (material, 1,
+ "RGBA = ADD (PREVIOUS, TEXTURE)",
+ &error);
+ if (!status)
+ {
+ /* It's not strictly a test failure; you need a more capable GPU or
+ * driver to test this texture combine string. */
+ g_debug ("Failed to setup texture combine string "
+ "RGBA = ADD (PREVIOUS, TEXTURE): %s",
+ error->message);
+ }
+
+ cogl_set_source (material);
+ cogl_rectangle_with_multitexture_coords (0, 0, QUAD_WIDTH, QUAD_WIDTH,
+ tex_coords, 8);
+
+ cogl_handle_unref (material);
+ cogl_handle_unref (tex0);
+ cogl_handle_unref (tex1);
+
+ /* See what we got... */
+
+ assert_region_color (0, 0, QUAD_WIDTH, QUAD_WIDTH,
+ 0x55, 0x55, 0x55, 0x55);
+
+ /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+ clutter_main_quit ();
+#endif
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_multitexture (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ group = clutter_group_new ();
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing incase someone comments out the
+ * clutter_main_quit and wants visual feedback for the test since we
+ * wont be doing anything else that will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-no-gl-header.c b/cogl/tests/conform/test-no-gl-header.c
new file mode 100644
index 000000000..9618d840e
--- /dev/null
+++ b/cogl/tests/conform/test-no-gl-header.c
@@ -0,0 +1,16 @@
+#undef COGL_COMPILATION
+#include <cogl/cogl.h>
+
+/* If you just include cogl/cogl.h, you shouldn't end up including any
+ GL headers */
+#ifdef GL_TRUE
+#error "Including cogl.h shouldn't be including any GL headers"
+#endif
+
+void test_no_gl_header (void);
+
+void
+test_no_gl_header (void)
+{
+}
+
diff --git a/cogl/tests/conform/test-npot-texture.c b/cogl/tests/conform/test-npot-texture.c
new file mode 100644
index 000000000..85c16c960
--- /dev/null
+++ b/cogl/tests/conform/test-npot-texture.c
@@ -0,0 +1,170 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+/* Non-power-of-two sized texture that should cause slicing */
+#define TEXTURE_SIZE 384
+/* Number of times to split the texture up on each axis */
+#define PARTS 2
+/* The texture is split into four parts, each with a different colour */
+#define PART_SIZE (TEXTURE_SIZE / PARTS)
+
+/* Amount of pixels to skip off the top, bottom, left and right of the
+ texture when reading back the stage */
+#define TEST_INSET 4
+
+/* Size to actually render the texture at */
+#define TEXTURE_RENDER_SIZE TEXTURE_SIZE
+/* The size of a part once rendered */
+#define PART_RENDER_SIZE (TEXTURE_RENDER_SIZE / PARTS)
+
+static const uint32_t corner_colors[PARTS * PARTS] =
+ {
+ /* Top left - red */ 0xff0000ff,
+ /* Top right - green */ 0x00ff00ff,
+ /* Bottom left - blue */ 0x0000ffff,
+ /* Bottom right - yellow */ 0xffff00ff
+ };
+
+static void
+validate_part (int xnum,
+ int ynum,
+ uint32_t color)
+{
+ test_utils_check_region (test_fb,
+ xnum * PART_RENDER_SIZE + TEST_INSET,
+ ynum * PART_RENDER_SIZE + TEST_INSET,
+ PART_RENDER_SIZE - TEST_INSET * 2,
+ PART_RENDER_SIZE - TEST_INSET * 2,
+ color);
+}
+
+static void
+validate_result (void)
+{
+ /* Validate that all four corners of the texture are drawn in the
+ right color */
+ validate_part (0, 0, corner_colors[0]);
+ validate_part (1, 0, corner_colors[1]);
+ validate_part (0, 1, corner_colors[2]);
+ validate_part (1, 1, corner_colors[3]);
+}
+
+static CoglTexture *
+make_texture (void)
+{
+ void *tex_data;
+ uint32_t *p;
+ CoglTexture *tex;
+ int partx, party, width, height;
+
+ p = tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
+
+ /* Make a texture with a different color for each part */
+ for (party = 0; party < PARTS; party++)
+ {
+ height = (party < PARTS - 1
+ ? PART_SIZE
+ : TEXTURE_SIZE - PART_SIZE * (PARTS - 1));
+
+ for (partx = 0; partx < PARTS; partx++)
+ {
+ uint32_t color = corner_colors[party * PARTS + partx];
+ width = (partx < PARTS - 1
+ ? PART_SIZE
+ : TEXTURE_SIZE - PART_SIZE * (PARTS - 1));
+
+ while (width-- > 0)
+ *(p++) = GUINT32_TO_BE (color);
+ }
+
+ while (--height > 0)
+ {
+ memcpy (p, p - TEXTURE_SIZE, TEXTURE_SIZE * 4);
+ p += TEXTURE_SIZE;
+ }
+ }
+
+ tex = test_utils_texture_new_from_data (test_ctx,
+ TEXTURE_SIZE,
+ TEXTURE_SIZE,
+ TEST_UTILS_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ TEXTURE_SIZE * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ if (cogl_test_verbose ())
+ {
+ if (cogl_texture_is_sliced (tex))
+ g_print ("Texture is sliced\n");
+ else
+ g_print ("Texture is not sliced\n");
+ }
+
+ /* The texture should be sliced unless NPOTs are supported */
+ g_assert (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT)
+ ? !cogl_texture_is_sliced (tex)
+ : cogl_texture_is_sliced (tex));
+
+ return tex;
+}
+
+static void
+paint (void)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ CoglTexture *texture = make_texture ();
+ int y, x;
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture);
+
+ /* Just render the texture in the top left corner */
+ /* Render the texture using four separate rectangles */
+ for (y = 0; y < 2; y++)
+ for (x = 0; x < 2; x++)
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ x * TEXTURE_RENDER_SIZE / 2,
+ y * TEXTURE_RENDER_SIZE / 2,
+ (x + 1) *
+ TEXTURE_RENDER_SIZE / 2,
+ (y + 1) *
+ TEXTURE_RENDER_SIZE / 2,
+ x / 2.0f,
+ y / 2.0f,
+ (x + 1) / 2.0f,
+ (y + 1) / 2.0f);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (texture);
+}
+
+void
+test_npot_texture (void)
+{
+ if (cogl_test_verbose ())
+ {
+ if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT))
+ g_print ("NPOT textures are supported\n");
+ else
+ g_print ("NPOT textures are not supported\n");
+ }
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint ();
+ validate_result ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-object.c b/cogl/tests/conform/test-object.c
new file mode 100644
index 000000000..0a6dcab62
--- /dev/null
+++ b/cogl/tests/conform/test-object.c
@@ -0,0 +1,86 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+CoglUserDataKey private_key0;
+CoglUserDataKey private_key1;
+CoglUserDataKey private_key2;
+
+static int user_data0;
+static int user_data1;
+static int user_data2;
+
+static int destroy0_count = 0;
+static int destroy1_count = 0;
+static int destroy2_count = 0;
+
+static void
+destroy0_cb (void *user_data)
+{
+ g_assert (user_data == &user_data0);
+ destroy0_count++;
+}
+
+static void
+destroy1_cb (void *user_data)
+{
+ g_assert (user_data == &user_data1);
+ destroy1_count++;
+}
+
+static void
+destroy2_cb (void *user_data)
+{
+ g_assert (user_data == &user_data2);
+ destroy2_count++;
+}
+
+void
+test_object (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ CoglPath *path;
+
+ /* Assuming that COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES == 2
+ * test associating 2 pointers to private data with an object */
+ cogl_path_new ();
+ path = cogl_get_path ();
+
+ cogl_object_set_user_data (COGL_OBJECT (path),
+ &private_key0,
+ &user_data0,
+ destroy0_cb);
+
+ cogl_object_set_user_data (COGL_OBJECT (path),
+ &private_key1,
+ &user_data1,
+ destroy1_cb);
+
+ cogl_object_set_user_data (COGL_OBJECT (path),
+ &private_key2,
+ &user_data2,
+ destroy2_cb);
+
+ cogl_object_set_user_data (COGL_OBJECT (path),
+ &private_key1,
+ NULL,
+ destroy1_cb);
+
+ cogl_object_set_user_data (COGL_OBJECT (path),
+ &private_key1,
+ &user_data1,
+ destroy1_cb);
+
+ cogl_object_unref (path);
+
+ g_assert_cmpint (destroy0_count, ==, 1);
+ g_assert_cmpint (destroy1_count, ==, 2);
+ g_assert_cmpint (destroy2_count, ==, 1);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-offscreen.c b/cogl/tests/conform/test-offscreen.c
new file mode 100644
index 000000000..9bc14b7da
--- /dev/null
+++ b/cogl/tests/conform/test-offscreen.c
@@ -0,0 +1,199 @@
+#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0
+
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+typedef struct _TestState
+{
+ int fb_width;
+ int fb_height;
+} TestState;
+
+static void
+check_quadrant (TestState *state,
+ int qx,
+ int qy,
+ uint32_t expected_rgba)
+{
+ /* The quadrants are all stuffed into the top right corner of the
+ framebuffer */
+ int x = state->fb_width * qx / 4 + state->fb_width / 2;
+ int y = state->fb_height * qy / 4;
+ int width = state->fb_width / 4;
+ int height = state->fb_height / 4;
+
+ /* Subtract a two-pixel gap around the edges to allow some rounding
+ differences */
+ x += 2;
+ y += 2;
+ width -= 4;
+ height -= 4;
+
+ test_utils_check_region (test_fb, x, y, width, height, expected_rgba);
+}
+
+static void
+test_paint (TestState *state)
+{
+ CoglTexture2D *tex_2d;
+ CoglTexture *tex;
+ CoglOffscreen *offscreen;
+
+ tex_2d = cogl_texture_2d_new_with_size (test_ctx,
+ state->fb_width,
+ state->fb_height);
+ tex = tex_2d;
+
+ offscreen = cogl_offscreen_new_with_texture (tex);
+
+ /* Set a scale and translate transform on the window framebuffer
+ * before switching to the offscreen framebuffer so we can verify it
+ * gets restored when we switch back
+ *
+ * The test is going to draw a grid of 4 colors to a texture which
+ * we subsequently draw to the window with a fullscreen rectangle.
+ * This transform will flip the texture left to right, scale it to a
+ * quarter of the window size and slide it to the top right of the
+ * window.
+ */
+ cogl_push_matrix ();
+ cogl_translate (0.5, 0.5, 0);
+ cogl_scale (-0.5, 0.5, 1);
+
+ cogl_push_framebuffer (offscreen);
+
+ /* Cogl should release the last reference when we call cogl_pop_framebuffer()
+ */
+ cogl_object_unref (offscreen);
+
+ /* Setup something other than the identity matrix for the modelview so we can
+ * verify it gets restored when we call cogl_pop_framebuffer () */
+ cogl_scale (2, 2, 1);
+
+ /* red, top left */
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_rectangle (-0.5, 0.5, 0, 0);
+ /* green, top right */
+ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+ cogl_rectangle (0, 0.5, 0.5, 0);
+ /* blue, bottom left */
+ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+ cogl_rectangle (-0.5, 0, 0, -0.5);
+ /* white, bottom right */
+ cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+ cogl_rectangle (0, 0, 0.5, -0.5);
+
+ cogl_pop_framebuffer ();
+
+ cogl_set_source_texture (tex);
+ cogl_rectangle (-1, 1, 1, -1);
+
+ cogl_object_unref (tex_2d);
+
+ cogl_pop_matrix ();
+
+ /* NB: The texture is drawn flipped horizontally and scaled to fit in the
+ * top right corner of the window. */
+
+ /* red, top right */
+ check_quadrant (state, 1, 0, 0xff0000ff);
+ /* green, top left */
+ check_quadrant (state, 0, 0, 0x00ff00ff);
+ /* blue, bottom right */
+ check_quadrant (state, 1, 1, 0x0000ffff);
+ /* white, bottom left */
+ check_quadrant (state, 0, 1, 0xffffffff);
+}
+
+static void
+test_flush (TestState *state)
+{
+ CoglTexture2D *tex_2d;
+ CoglTexture *tex;
+ CoglOffscreen *offscreen;
+ CoglColor clear_color;
+ int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ /* This tests that rendering to a framebuffer and then reading back
+ the contents of the texture will automatically flush the
+ journal */
+
+ tex_2d = cogl_texture_2d_new_with_size (test_ctx,
+ 16, 16); /* width/height */
+ tex = tex_2d;
+
+ offscreen = cogl_offscreen_new_with_texture (tex);
+
+ cogl_push_framebuffer (offscreen);
+
+ cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 255);
+ cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
+
+ cogl_set_source_color4ub (255, 0, 0, 255);
+ cogl_rectangle (-1, -1, 1, 1);
+
+ if (i == 0)
+ /* First time check using read pixels on the offscreen */
+ test_utils_check_region (offscreen,
+ 1, 1, 15, 15, 0xff0000ff);
+ else if (i == 1)
+ {
+ uint8_t data[16 * 4 * 16];
+ int x, y;
+
+ /* Second time try reading back the texture contents */
+ cogl_texture_get_data (tex,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 16 * 4, /* rowstride */
+ data);
+
+ for (y = 1; y < 15; y++)
+ for (x = 1; x < 15; x++)
+ test_utils_compare_pixel (data + x * 4 + y * 16 * 4,
+ 0xff0000ff);
+ }
+
+ cogl_pop_framebuffer ();
+
+ if (i == 2)
+ {
+ /* Third time try drawing the texture to the screen */
+ cogl_set_source_texture (tex);
+ cogl_rectangle (-1, -1, 1, 1);
+ test_utils_check_region (test_fb,
+ 2, 2, /* x/y */
+ state->fb_width - 4,
+ state->fb_height - 4,
+ 0xff0000ff);
+ }
+
+ cogl_object_unref (tex_2d);
+ cogl_object_unref (offscreen);
+ }
+}
+
+void
+test_offscreen (void)
+{
+ TestState state;
+
+ state.fb_width = cogl_framebuffer_get_width (test_fb);
+ state.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ /* XXX: we have to push/pop a framebuffer since this test currently
+ * uses the legacy cogl_rectangle() api. */
+ cogl_push_framebuffer (test_fb);
+ test_paint (&state);
+ test_flush (&state);
+ cogl_pop_framebuffer ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-path-clip.c b/cogl/tests/conform/test-path-clip.c
new file mode 100644
index 000000000..95ad00b96
--- /dev/null
+++ b/cogl/tests/conform/test-path-clip.c
@@ -0,0 +1,68 @@
+#define COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <cogl/cogl.h>
+#include <cogl-path/cogl-path.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+void
+test_path_clip (void)
+{
+ CoglPath *path;
+ CoglPipeline *pipeline;
+ int fb_width, fb_height;
+
+ fb_width = cogl_framebuffer_get_width (test_fb);
+ fb_height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, fb_width, fb_height, -1, 100);
+
+ path = cogl_path_new ();
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+
+ /* Make an L-shape with the top right corner left untouched */
+ cogl_path_move_to (path, 0, fb_height);
+ cogl_path_line_to (path, fb_width, fb_height);
+ cogl_path_line_to (path, fb_width, fb_height / 2);
+ cogl_path_line_to (path, fb_width / 2, fb_height / 2);
+ cogl_path_line_to (path, fb_width / 2, 0);
+ cogl_path_line_to (path, 0, 0);
+ cogl_path_close (path);
+
+ cogl_framebuffer_push_path_clip (test_fb, path);
+
+ /* Try to fill the framebuffer with a blue rectangle. This should be
+ * clipped to leave the top right quadrant as is */
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_color4ub (pipeline, 0, 0, 255, 255);
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 0, 0, fb_width, fb_height);
+
+ cogl_framebuffer_pop_clip (test_fb);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (path);
+
+ /* Check each of the four quadrants */
+ test_utils_check_pixel (test_fb,
+ fb_width / 4, fb_height / 4,
+ 0x0000ffff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4, fb_height / 4,
+ 0xff0000ff);
+ test_utils_check_pixel (test_fb,
+ fb_width / 4, fb_height * 3 / 4,
+ 0x0000ffff);
+ test_utils_check_pixel (test_fb,
+ fb_width * 3 / 4, fb_height * 3 / 4,
+ 0x0000ffff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-path.c b/cogl/tests/conform/test-path.c
new file mode 100644
index 000000000..11f7f1583
--- /dev/null
+++ b/cogl/tests/conform/test-path.c
@@ -0,0 +1,215 @@
+#define COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <cogl/cogl.h>
+#include <cogl-path/cogl-path.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define BLOCK_SIZE 16
+
+/* Number of pixels at the border of a block quadrant to skip when verifying */
+#define TEST_INSET 1
+
+typedef struct _TestState
+{
+ int dummy;
+} TestState;
+
+static void
+draw_path_at (CoglPath *path, CoglPipeline *pipeline, int x, int y)
+{
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f);
+
+ cogl_set_framebuffer (test_fb);
+ cogl_set_source (pipeline);
+ cogl_path_fill (path);
+
+ cogl_framebuffer_pop_matrix (test_fb);
+}
+
+static void
+check_block (int block_x, int block_y, int block_mask)
+{
+ uint32_t data[BLOCK_SIZE * BLOCK_SIZE];
+ int qx, qy;
+
+ /* Block mask represents which quarters of the block should be
+ filled. The bits from 0->3 represent the top left, top right,
+ bottom left and bottom right respectively */
+
+ cogl_framebuffer_read_pixels (test_fb,
+ block_x * BLOCK_SIZE,
+ block_y * BLOCK_SIZE,
+ BLOCK_SIZE, BLOCK_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ (uint8_t *)data);
+
+ for (qy = 0; qy < 2; qy++)
+ for (qx = 0; qx < 2; qx++)
+ {
+ int bit = qx | (qy << 1);
+ const char *intended_pixel = ((block_mask & (1 << bit)) ? "#ffffff" : "#000000");
+ int x, y;
+
+ for (x = 0; x < BLOCK_SIZE / 2 - TEST_INSET * 2; x++)
+ for (y = 0; y < BLOCK_SIZE / 2 - TEST_INSET * 2; y++)
+ {
+ const uint32_t *p = data + (qx * BLOCK_SIZE / 2 +
+ qy * BLOCK_SIZE * BLOCK_SIZE / 2 +
+ (x + TEST_INSET) +
+ (y + TEST_INSET) * BLOCK_SIZE);
+ char *screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8);
+ g_assert_cmpstr (screen_pixel, ==, intended_pixel);
+ g_free (screen_pixel);
+ }
+ }
+}
+
+static void
+paint (TestState *state)
+{
+ CoglPath *path_a, *path_b, *path_c;
+ CoglPipeline *white = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4f (white, 1, 1, 1, 1);
+
+ /* Create a path filling just a quarter of a block. It will use two
+ rectangles so that we have a sub path in the path */
+ path_a = cogl_path_new ();
+ cogl_path_rectangle (path_a,
+ BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2,
+ BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_rectangle (path_a,
+ BLOCK_SIZE / 2, BLOCK_SIZE / 2,
+ BLOCK_SIZE * 3 / 4, BLOCK_SIZE);
+ draw_path_at (path_a, white, 0, 0);
+
+ /* Create another path filling the whole block */
+ path_b = cogl_path_new ();
+ cogl_path_rectangle (path_b, 0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ draw_path_at (path_b, white, 1, 0);
+
+ /* Draw the first path again */
+ draw_path_at (path_a, white, 2, 0);
+
+ /* Draw a copy of path a */
+ path_c = cogl_path_copy (path_a);
+ draw_path_at (path_c, white, 3, 0);
+
+ /* Add another rectangle to path a. We'll use line_to's instead of
+ cogl_rectangle so that we don't create another sub-path because
+ that is more likely to break the copy */
+ cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, 0, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ draw_path_at (path_a, white, 4, 0);
+
+ /* Draw the copy again. It should not have changed */
+ draw_path_at (path_c, white, 5, 0);
+
+ /* Add another rectangle to path c. It will be added in two halves,
+ one as an extension of the previous path and the other as a new
+ sub path */
+ cogl_path_line_to (path_c, BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, 0);
+ cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_c, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_rectangle (path_c,
+ BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2);
+ draw_path_at (path_c, white, 6, 0);
+
+ /* Draw the original path again. It should not have changed */
+ draw_path_at (path_a, white, 7, 0);
+
+ cogl_object_unref (path_a);
+ cogl_object_unref (path_b);
+ cogl_object_unref (path_c);
+
+ /* Draw a self-intersecting path. The part that intersects should be
+ inverted */
+ path_a = cogl_path_new ();
+ cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0);
+ cogl_path_close (path_a);
+ draw_path_at (path_a, white, 8, 0);
+ cogl_object_unref (path_a);
+
+ /* Draw two sub paths. Where the paths intersect it should be
+ inverted */
+ path_a = cogl_path_new ();
+ cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_rectangle (path_a,
+ BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE);
+ draw_path_at (path_a, white, 9, 0);
+ cogl_object_unref (path_a);
+
+ /* Draw a clockwise outer path */
+ path_a = cogl_path_new ();
+ cogl_path_move_to (path_a, 0, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_line_to (path_a, 0, BLOCK_SIZE);
+ cogl_path_close (path_a);
+ /* Add a clockwise sub path in the upper left quadrant */
+ cogl_path_move_to (path_a, 0, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2);
+ cogl_path_close (path_a);
+ /* Add a counter-clockwise sub path in the upper right quadrant */
+ cogl_path_move_to (path_a, BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE / 2);
+ cogl_path_line_to (path_a, BLOCK_SIZE, 0);
+ cogl_path_close (path_a);
+ /* Retain the path for the next test */
+ draw_path_at (path_a, white, 10, 0);
+
+ /* Draw the same path again with the other fill rule */
+ cogl_path_set_fill_rule (path_a, COGL_PATH_FILL_RULE_NON_ZERO);
+ draw_path_at (path_a, white, 11, 0);
+
+ cogl_object_unref (path_a);
+}
+
+static void
+validate_result ()
+{
+ check_block (0, 0, 0x8 /* bottom right */);
+ check_block (1, 0, 0xf /* all of them */);
+ check_block (2, 0, 0x8 /* bottom right */);
+ check_block (3, 0, 0x8 /* bottom right */);
+ check_block (4, 0, 0x9 /* top left and bottom right */);
+ check_block (5, 0, 0x8 /* bottom right */);
+ check_block (6, 0, 0xa /* bottom right and top right */);
+ check_block (7, 0, 0x9 /* top_left and bottom right */);
+ check_block (8, 0, 0xe /* all but top left */);
+ check_block (9, 0, 0x7 /* all but bottom right */);
+ check_block (10, 0, 0xc /* bottom two */);
+ check_block (11, 0, 0xd /* all but top right */);
+}
+
+void
+test_path (void)
+{
+ TestState state;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint (&state);
+ validate_result ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c b/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c
new file mode 100644
index 000000000..5d278dcd0
--- /dev/null
+++ b/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c
@@ -0,0 +1,91 @@
+#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,
+ 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 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/cogl/tests/conform/test-pipeline-shader-state.c b/cogl/tests/conform/test-pipeline-shader-state.c
new file mode 100644
index 000000000..4d1e5f2b7
--- /dev/null
+++ b/cogl/tests/conform/test-pipeline-shader-state.c
@@ -0,0 +1,93 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+void
+test_pipeline_shader_state (void)
+{
+ CoglOffscreen *offscreen;
+ CoglFramebuffer *fb;
+ CoglPipeline *base_pipeline;
+ CoglPipeline *draw_pipeline;
+ CoglTexture2D *tex;
+ CoglSnippet *snippet;
+
+ float width = cogl_framebuffer_get_width (test_fb);
+ float height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, width, height,
+ -1,
+ 100);
+
+ tex = cogl_texture_2d_new_with_size (test_ctx, 128, 128);
+ offscreen = cogl_offscreen_new_with_texture (tex);
+ fb = offscreen;
+ cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+ cogl_object_unref (offscreen);
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 0, 1);
+
+
+ /* Setup a template pipeline... */
+
+ base_pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (base_pipeline, 1, tex);
+ cogl_pipeline_set_color4f (base_pipeline, 1, 0, 0, 1);
+
+
+ /* Derive a pipeline from the template, making a change that affects
+ * fragment processing but making sure not to affect vertex
+ * processing... */
+
+ draw_pipeline = cogl_pipeline_copy (base_pipeline);
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL, /* declarations */
+ "cogl_color_out = vec4 (0.0, 1.0, 0.1, 1.1);");
+ cogl_pipeline_add_snippet (draw_pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline,
+ 0, 0, width, height);
+
+ cogl_object_unref (draw_pipeline);
+
+ cogl_framebuffer_finish (test_fb);
+
+
+ /* At this point we should have provoked cogl to cache some vertex
+ * shader state for the draw_pipeline with the base_pipeline because
+ * none of the changes made to the draw_pipeline affected vertex
+ * processing. (NB: cogl will cache shader state with the oldest
+ * ancestor that the state is still valid for to maximize the chance
+ * that it can be used with other derived pipelines)
+ *
+ * Now we make a change to the base_pipeline to make sure that this
+ * cached vertex shader gets invalidated.
+ */
+
+ cogl_pipeline_set_layer_texture (base_pipeline, 0, tex);
+
+
+ /* Now we derive another pipeline from base_pipeline to verify that
+ * it doesn't end up re-using the old cached state
+ */
+
+ draw_pipeline = cogl_pipeline_copy (base_pipeline);
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL, /* declarations */
+ "cogl_color_out = vec4 (0.0, 0.0, 1.1, 1.1);");
+ cogl_pipeline_add_snippet (draw_pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline,
+ 0, 0, width, height);
+
+ cogl_object_unref (draw_pipeline);
+
+
+ test_utils_check_region (test_fb, 0, 0, width, height,
+ 0x0000ffff);
+}
diff --git a/cogl/tests/conform/test-pipeline-uniforms.c b/cogl/tests/conform/test-pipeline-uniforms.c
new file mode 100644
index 000000000..4d27558d2
--- /dev/null
+++ b/cogl/tests/conform/test-pipeline-uniforms.c
@@ -0,0 +1,415 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define LONG_ARRAY_SIZE 128
+
+typedef struct _TestState
+{
+ CoglPipeline *pipeline_red;
+ CoglPipeline *pipeline_green;
+ CoglPipeline *pipeline_blue;
+
+ CoglPipeline *matrix_pipeline;
+ CoglPipeline *vector_pipeline;
+ CoglPipeline *int_pipeline;
+
+ CoglPipeline *long_pipeline;
+ int long_uniform_locations[LONG_ARRAY_SIZE];
+} TestState;
+
+static const char
+color_source[] =
+ "uniform float red, green, blue;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_color_out = vec4 (red, green, blue, 1.0);\n"
+ "}\n";
+
+static const char
+matrix_source[] =
+ "uniform mat4 matrix_array[4];\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " vec4 color = vec4 (0.0, 0.0, 0.0, 1.0);\n"
+ " int i;\n"
+ "\n"
+ " for (i = 0; i < 4; i++)\n"
+ " color = matrix_array[i] * color;\n"
+ "\n"
+ " cogl_color_out = color;\n"
+ "}\n";
+
+static const char
+vector_source[] =
+ "uniform vec4 vector_array[2];\n"
+ "uniform vec3 short_vector;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_color_out = (vector_array[0] +\n"
+ " vector_array[1] +\n"
+ " vec4 (short_vector, 1.0));\n"
+ "}\n";
+
+static const char
+int_source[] =
+ "uniform ivec4 vector_array[2];\n"
+ "uniform int single_value;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_color_out = (vec4 (vector_array[0]) +\n"
+ " vec4 (vector_array[1]) +\n"
+ " vec4 (float (single_value), 0.0, 0.0, 255.0)) / 255.0;\n"
+ "}\n";
+
+static const char
+long_source[] =
+ "uniform int long_array[" G_STRINGIFY (LONG_ARRAY_SIZE) "];\n"
+ "const int last_index = " G_STRINGIFY (LONG_ARRAY_SIZE) " - 1;\n"
+ "\n"
+ "void\n"
+ "main ()\n"
+ "{\n"
+ " cogl_color_out = vec4 (float (long_array[last_index]), 0.0, 0.0, 1.0);\n"
+ "}\n";
+
+static CoglPipeline *
+create_pipeline_for_shader (TestState *state, const char *shader_source)
+{
+ CoglPipeline *pipeline;
+ CoglHandle shader;
+ CoglHandle program;
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
+ cogl_shader_source (shader, shader_source);
+
+ program = cogl_create_program ();
+ cogl_program_attach_shader (program, shader);
+
+ cogl_pipeline_set_user_program (pipeline, program);
+
+ cogl_handle_unref (shader);
+ cogl_handle_unref (program);
+
+ return pipeline;
+}
+
+static void
+init_state (TestState *state)
+{
+ int uniform_location;
+
+ state->pipeline_red = create_pipeline_for_shader (state, color_source);
+
+ uniform_location =
+ cogl_pipeline_get_uniform_location (state->pipeline_red, "red");
+ cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 1.0f);
+ uniform_location =
+ cogl_pipeline_get_uniform_location (state->pipeline_red, "green");
+ cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f);
+ uniform_location =
+ cogl_pipeline_get_uniform_location (state->pipeline_red, "blue");
+ cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f);
+
+ state->pipeline_green = cogl_pipeline_copy (state->pipeline_red);
+ uniform_location =
+ cogl_pipeline_get_uniform_location (state->pipeline_green, "green");
+ cogl_pipeline_set_uniform_1f (state->pipeline_green, uniform_location, 1.0f);
+
+ state->pipeline_blue = cogl_pipeline_copy (state->pipeline_red);
+ uniform_location =
+ cogl_pipeline_get_uniform_location (state->pipeline_blue, "blue");
+ cogl_pipeline_set_uniform_1f (state->pipeline_blue, uniform_location, 1.0f);
+
+ state->matrix_pipeline = create_pipeline_for_shader (state, matrix_source);
+ state->vector_pipeline = create_pipeline_for_shader (state, vector_source);
+ state->int_pipeline = create_pipeline_for_shader (state, int_source);
+
+ state->long_pipeline = NULL;
+}
+
+static void
+init_long_pipeline_state (TestState *state)
+{
+ int i;
+
+ state->long_pipeline = create_pipeline_for_shader (state, long_source);
+
+ /* This tries to lookup a large number of uniform names to make sure
+ that the bitmask of overriden uniforms flows over the size of a
+ single long so that it has to resort to allocating it */
+ for (i = 0; i < LONG_ARRAY_SIZE; i++)
+ {
+ char *uniform_name = g_strdup_printf ("long_array[%i]", i);
+ state->long_uniform_locations[i] =
+ cogl_pipeline_get_uniform_location (state->long_pipeline,
+ uniform_name);
+ g_free (uniform_name);
+ }
+}
+
+static void
+destroy_state (TestState *state)
+{
+ cogl_object_unref (state->pipeline_red);
+ cogl_object_unref (state->pipeline_green);
+ cogl_object_unref (state->pipeline_blue);
+ cogl_object_unref (state->matrix_pipeline);
+ cogl_object_unref (state->vector_pipeline);
+ cogl_object_unref (state->int_pipeline);
+
+ if (state->long_pipeline)
+ cogl_object_unref (state->long_pipeline);
+}
+
+static void
+paint_pipeline (CoglPipeline *pipeline, int pos)
+{
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline,
+ pos * 10, 0, pos * 10 + 10, 10);
+}
+
+static void
+paint_color_pipelines (TestState *state)
+{
+ CoglPipeline *temp_pipeline;
+ int uniform_location;
+ int i;
+
+ /* Paint with the first pipeline that sets the uniforms to bright
+ red */
+ paint_pipeline (state->pipeline_red, 0);
+
+ /* Paint with the two other pipelines. These inherit from the red
+ pipeline and only override one other component. The values for
+ the two other components should be inherited from the red
+ pipeline. */
+ paint_pipeline (state->pipeline_green, 1);
+ paint_pipeline (state->pipeline_blue, 2);
+
+ /* Try modifying a single pipeline for multiple rectangles */
+ temp_pipeline = cogl_pipeline_copy (state->pipeline_green);
+ uniform_location = cogl_pipeline_get_uniform_location (temp_pipeline,
+ "green");
+
+ for (i = 0; i <= 8; i++)
+ {
+ cogl_pipeline_set_uniform_1f (temp_pipeline, uniform_location,
+ i / 8.0f);
+ paint_pipeline (temp_pipeline, i + 3);
+ }
+
+ cogl_object_unref (temp_pipeline);
+}
+
+static void
+paint_matrix_pipeline (CoglPipeline *pipeline)
+{
+ CoglMatrix matrices[4];
+ float matrix_floats[16 * 4];
+ int uniform_location;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ cogl_matrix_init_identity (matrices + i);
+
+ /* Use the first matrix to make the color red */
+ cogl_matrix_translate (matrices + 0, 1.0f, 0.0f, 0.0f);
+
+ /* Rotate the vertex so that it ends up green */
+ cogl_matrix_rotate (matrices + 1, 90.0f, 0.0f, 0.0f, 1.0f);
+
+ /* Scale the vertex so it ends up halved */
+ cogl_matrix_scale (matrices + 2, 0.5f, 0.5f, 0.5f);
+
+ /* Add a blue component in the final matrix. The final matrix is
+ uploaded as transposed so we need to transpose first to cancel
+ that out */
+ cogl_matrix_translate (matrices + 3, 0.0f, 0.0f, 1.0f);
+ cogl_matrix_transpose (matrices + 3);
+
+ for (i = 0; i < 4; i++)
+ memcpy (matrix_floats + i * 16,
+ cogl_matrix_get_array (matrices + i),
+ sizeof (float) * 16);
+
+ /* Set the first three matrices as transposed */
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "matrix_array");
+ cogl_pipeline_set_uniform_matrix (pipeline,
+ uniform_location,
+ 4, /* dimensions */
+ 3, /* count */
+ FALSE, /* not transposed */
+ matrix_floats);
+
+ /* Set the last matrix as untransposed */
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "matrix_array[3]");
+ cogl_pipeline_set_uniform_matrix (pipeline,
+ uniform_location,
+ 4, /* dimensions */
+ 1, /* count */
+ TRUE, /* transposed */
+ matrix_floats + 16 * 3);
+
+ paint_pipeline (pipeline, 12);
+}
+
+static void
+paint_vector_pipeline (CoglPipeline *pipeline)
+{
+ float vector_array_values[] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f };
+ float short_vector_values[] = { 0.0f, 0.0f, 1.0f };
+ int uniform_location;
+
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "vector_array");
+ cogl_pipeline_set_uniform_float (pipeline,
+ uniform_location,
+ 4, /* n_components */
+ 2, /* count */
+ vector_array_values);
+
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "short_vector");
+ cogl_pipeline_set_uniform_float (pipeline,
+ uniform_location,
+ 3, /* n_components */
+ 1, /* count */
+ short_vector_values);
+
+ paint_pipeline (pipeline, 13);
+}
+
+static void
+paint_int_pipeline (CoglPipeline *pipeline)
+{
+ int vector_array_values[] = { 0x00, 0x00, 0xff, 0x00,
+ 0x00, 0xff, 0x00, 0x00 };
+ int single_value = 0x80;
+ int uniform_location;
+
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "vector_array");
+ cogl_pipeline_set_uniform_int (pipeline,
+ uniform_location,
+ 4, /* n_components */
+ 2, /* count */
+ vector_array_values);
+
+ uniform_location =
+ cogl_pipeline_get_uniform_location (pipeline, "single_value");
+ cogl_pipeline_set_uniform_1i (pipeline,
+ uniform_location,
+ single_value);
+
+ paint_pipeline (pipeline, 14);
+}
+
+static void
+paint_long_pipeline (TestState *state)
+{
+ int i;
+
+ for (i = 0; i < LONG_ARRAY_SIZE; i++)
+ {
+ int location = state->long_uniform_locations[i];
+
+ cogl_pipeline_set_uniform_1i (state->long_pipeline,
+ location,
+ i == LONG_ARRAY_SIZE - 1);
+ }
+
+ paint_pipeline (state->long_pipeline, 15);
+}
+
+static void
+paint (TestState *state)
+{
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ paint_color_pipelines (state);
+ paint_matrix_pipeline (state->matrix_pipeline);
+ paint_vector_pipeline (state->vector_pipeline);
+ paint_int_pipeline (state->int_pipeline);
+}
+
+static void
+check_pos (int pos, uint32_t color)
+{
+ test_utils_check_pixel (test_fb, pos * 10 + 5, 5, color);
+}
+
+static void
+validate_result (void)
+{
+ int i;
+
+ check_pos (0, 0xff0000ff);
+ check_pos (1, 0xffff00ff);
+ check_pos (2, 0xff00ffff);
+
+ for (i = 0; i <= 8; i++)
+ {
+ int green_value = i / 8.0f * 255.0f + 0.5f;
+ check_pos (i + 3, 0xff0000ff + (green_value << 16));
+ }
+
+ check_pos (12, 0x0080ffff);
+ check_pos (13, 0xffffffff);
+ check_pos (14, 0x80ffffff);
+}
+
+static void
+validate_long_pipeline_result (void)
+{
+ check_pos (15, 0xff0000ff);
+}
+
+void
+test_pipeline_uniforms (void)
+{
+ TestState state;
+
+ init_state (&state);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint (&state);
+ validate_result ();
+
+ /* Try the test again after querying the location of a large
+ number of uniforms. This should verify that the bitmasks
+ still work even if they have to allocate a separate array to
+ store the bits */
+
+ init_long_pipeline_state (&state);
+ paint (&state);
+ paint_long_pipeline (&state);
+ validate_result ();
+ validate_long_pipeline_result ();
+
+ destroy_state (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-pipeline-user-matrix.c b/cogl/tests/conform/test-pipeline-user-matrix.c
new file mode 100644
index 000000000..f7cdee8c8
--- /dev/null
+++ b/cogl/tests/conform/test-pipeline-user-matrix.c
@@ -0,0 +1,140 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ int width;
+ int height;
+} TestState;
+
+static void
+validate_result (TestState *state)
+{
+ uint32_t *pixels, *p;
+ char *screen_pixel;
+ const char *intended_pixel = "#ffffff";
+
+ /* The textures are setup so that when added together with the
+ correct matrices then all of the pixels should be white. We can
+ verify this by reading back the entire stage */
+ pixels = g_malloc (state->width * state->height * 4);
+
+ cogl_framebuffer_read_pixels (test_fb, 0, 0, state->width, state->height,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ (uint8_t *)pixels);
+
+ for (p = pixels; p < pixels + state->width * state->height; p++)
+ {
+ screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8);
+ g_assert_cmpstr (screen_pixel, ==, intended_pixel);
+ g_free (screen_pixel);
+ }
+}
+
+static void
+paint (TestState *state)
+{
+ /* This texture is painted mirrored around the x-axis */
+ uint8_t data0[] = {
+ 0xff, 0x00, 0x00, /* red -> becomes bottom left */
+ 0x00, 0xff, 0x00, /* green -> becomes bottom right */
+ 0x00, 0x00, 0xff, /* blue -> becomes top left */
+ 0xff, 0x00, 0xff /* magenta -> becomes top right */
+ };
+ /* This texture is painted mirrored about the y-axis */
+ uint8_t data1[] = {
+ 0x00, 0xff, 0x00, /* green -> becomes top right */
+ 0xff, 0xff, 0x00, /* yellow -> becomes top left */
+ 0xff, 0x00, 0xff, /* magenta -> becomes bottom right */
+ 0x00, 0xff, 0xff /* cyan -> becomes bottom left */
+ };
+ CoglTexture *tex0, *tex1;
+ CoglPipeline *pipeline;
+ CoglMatrix matrix;
+ CoglError *error = NULL;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ state->width,
+ state->height,
+ -1,
+ 100);
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ cogl_matrix_init_identity (&matrix);
+ cogl_framebuffer_set_modelview_matrix (test_fb, &matrix);
+
+ tex0 = cogl_texture_new_from_data (2, 2,
+ COGL_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGB_888,
+ COGL_PIXEL_FORMAT_ANY,
+ 6,
+ data0);
+ tex1 = cogl_texture_new_from_data (2, 2,
+ COGL_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGB_888,
+ COGL_PIXEL_FORMAT_ANY,
+ 6,
+ data1);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ /* Set the two textures as layers */
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex0);
+ cogl_pipeline_set_layer_filters (pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_layer_texture (pipeline, 1, tex1);
+ cogl_pipeline_set_layer_filters (pipeline, 1,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ /* Set a combine mode so that the two textures get added together */
+ if (!cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGBA=ADD(PREVIOUS, TEXTURE)",
+ &error))
+ {
+ g_warning ("Error setting blend string: %s", error->message);
+ g_assert_not_reached ();
+ }
+
+ /* Set a matrix on the first layer so that it will mirror about the y-axis */
+ cogl_matrix_init_identity (&matrix);
+ cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
+ cogl_matrix_scale (&matrix, 1.0f, -1.0f, 1.0f);
+ cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+
+ /* Set a matrix on the second layer so that it will mirror about the x-axis */
+ cogl_matrix_init_identity (&matrix);
+ cogl_matrix_translate (&matrix, 1.0f, 0.0f, 0.0f);
+ cogl_matrix_scale (&matrix, -1.0f, 1.0f, 1.0f);
+ cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 0, 0,
+ state->width, state->height);
+
+ cogl_object_unref (tex1);
+ cogl_object_unref (tex0);
+ cogl_object_unref (pipeline);
+}
+
+void
+test_pipeline_user_matrix (void)
+{
+ TestState state;
+
+ state.width = cogl_framebuffer_get_width (test_fb);
+ state.height = cogl_framebuffer_get_height (test_fb);
+
+ paint (&state);
+ validate_result (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-pixel-buffer.c b/cogl/tests/conform/test-pixel-buffer.c
new file mode 100644
index 000000000..a78516d06
--- /dev/null
+++ b/cogl/tests/conform/test-pixel-buffer.c
@@ -0,0 +1,269 @@
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+#define BITMAP_SIZE 256
+
+/*
+ * Creates a 256 x 256 with image data split into four quadrants. The
+ * colours of these in reading order will be: blue, green, cyan,
+ * red */
+static void
+generate_bitmap_data (uint8_t *data,
+ int stride)
+{
+ int y, x;
+
+ for (y = 0; y < BITMAP_SIZE; y++)
+ {
+ for (x = 0; x < BITMAP_SIZE; x++)
+ {
+ int color_num = x / (BITMAP_SIZE / 2) + y / (BITMAP_SIZE / 2) * 2 + 1;
+ *(data++) = (color_num & 4) ? 255 : 0;
+ *(data++) = (color_num & 2) ? 255 : 0;
+ *(data++) = (color_num & 1) ? 255 : 0;
+ *(data++) = 255;
+ }
+ data += stride - BITMAP_SIZE * 4;
+ }
+}
+
+static CoglBitmap *
+create_bitmap (void)
+{
+ CoglBitmap *bitmap;
+ CoglBuffer *buffer;
+
+ bitmap = cogl_bitmap_new_with_size (test_ctx,
+ BITMAP_SIZE,
+ BITMAP_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888);
+ buffer = cogl_bitmap_get_buffer (bitmap);
+
+ g_assert (cogl_is_pixel_buffer (buffer));
+ g_assert (cogl_is_buffer (buffer));
+
+ cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+ g_assert_cmpint (cogl_buffer_get_update_hint (buffer),
+ ==,
+ COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+
+ return bitmap;
+}
+
+static CoglBitmap *
+create_and_fill_bitmap (void)
+{
+ CoglBitmap *bitmap = create_bitmap ();
+ CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap);
+ uint8_t *map;
+ unsigned int stride;
+
+ stride = cogl_bitmap_get_rowstride (bitmap);
+
+ map = cogl_buffer_map (buffer,
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD);
+ g_assert (map);
+
+ generate_bitmap_data (map, stride);
+
+ cogl_buffer_unmap (buffer);
+
+ return bitmap;
+}
+
+static CoglTexture *
+create_texture_from_bitmap (CoglBitmap *bitmap)
+{
+ CoglTexture2D *texture;
+
+ texture = cogl_texture_2d_new_from_bitmap (bitmap);
+
+ g_assert (texture != NULL);
+
+ return texture;
+}
+
+static CoglPipeline *
+create_pipeline_from_texture (CoglTexture *texture)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture);
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0, /* layer_num */
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ return pipeline;
+}
+
+static void
+check_colours (uint32_t color0,
+ uint32_t color1,
+ uint32_t color2,
+ uint32_t color3)
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+
+ test_utils_check_region (test_fb,
+ 1, 1, /* x/y */
+ fb_width / 2 - 2, /* width */
+ fb_height / 2 - 2, /* height */
+ color0);
+ test_utils_check_region (test_fb,
+ fb_width / 2 + 1, /* x */
+ 1, /* y */
+ fb_width / 2 - 2, /* width */
+ fb_height / 2 - 2, /* height */
+ color1);
+ test_utils_check_region (test_fb,
+ 1, /* x */
+ fb_height / 2 + 1, /* y */
+ fb_width / 2 - 2, /* width */
+ fb_height / 2 - 2, /* height */
+ color2);
+ test_utils_check_region (test_fb,
+ fb_width / 2 + 1, /* x */
+ fb_height / 2 + 1, /* y */
+ fb_width / 2 - 2, /* width */
+ fb_height / 2 - 2, /* height */
+ color3);
+}
+
+void
+test_pixel_buffer_map (void)
+{
+ CoglBitmap *bitmap = create_and_fill_bitmap ();
+ CoglPipeline *pipeline;
+ CoglTexture *texture;
+
+ texture = create_texture_from_bitmap (bitmap);
+ pipeline = create_pipeline_from_texture (texture);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1.0f, 1.0f,
+ 1.0f, -1.0f);
+
+ cogl_object_unref (bitmap);
+ cogl_object_unref (texture);
+ cogl_object_unref (pipeline);
+
+ check_colours (0x0000ffff,
+ 0x00ff00ff,
+ 0x00ffffff,
+ 0xff0000ff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
+void
+test_pixel_buffer_set_data (void)
+{
+ CoglBitmap *bitmap = create_bitmap ();
+ CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap);
+ CoglPipeline *pipeline;
+ CoglTexture *texture;
+ uint8_t *data;
+ unsigned int stride;
+
+ stride = cogl_bitmap_get_rowstride (bitmap);
+
+ data = g_malloc (stride * BITMAP_SIZE);
+
+ generate_bitmap_data (data, stride);
+
+ cogl_buffer_set_data (buffer,
+ 0, /* offset */
+ data,
+ stride * (BITMAP_SIZE - 1) +
+ BITMAP_SIZE * 4);
+
+ g_free (data);
+
+ texture = create_texture_from_bitmap (bitmap);
+ pipeline = create_pipeline_from_texture (texture);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1.0f, 1.0f,
+ 1.0f, -1.0f);
+
+ cogl_object_unref (bitmap);
+ cogl_object_unref (texture);
+ cogl_object_unref (pipeline);
+
+ check_colours (0x0000ffff,
+ 0x00ff00ff,
+ 0x00ffffff,
+ 0xff0000ff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
+static CoglTexture *
+create_white_texture (void)
+{
+ CoglTexture2D *texture;
+ uint8_t *data = g_malloc (BITMAP_SIZE * BITMAP_SIZE * 4);
+
+ memset (data, 255, BITMAP_SIZE * BITMAP_SIZE * 4);
+
+ texture = cogl_texture_2d_new_from_data (test_ctx,
+ BITMAP_SIZE,
+ BITMAP_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ BITMAP_SIZE * 4, /* rowstride */
+ data,
+ NULL); /* don't catch errors */
+
+ g_free (data);
+
+ return texture;
+}
+
+void
+test_pixel_buffer_sub_region (void)
+{
+ CoglBitmap *bitmap = create_and_fill_bitmap ();
+ CoglPipeline *pipeline;
+ CoglTexture *texture;
+
+ texture = create_white_texture ();
+
+ /* Replace the top-right quadrant of the texture with the red part
+ * of the bitmap */
+ cogl_texture_set_region_from_bitmap (texture,
+ BITMAP_SIZE / 2, /* src_x */
+ BITMAP_SIZE / 2, /* src_y */
+ BITMAP_SIZE / 2, /* dst_x */
+ 0, /* dst_y */
+ BITMAP_SIZE / 2, /* width */
+ BITMAP_SIZE / 2, /* height */
+ bitmap);
+
+ pipeline = create_pipeline_from_texture (texture);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1.0f, 1.0f,
+ 1.0f, -1.0f);
+
+ cogl_object_unref (bitmap);
+ cogl_object_unref (texture);
+ cogl_object_unref (pipeline);
+
+ check_colours (0xffffffff,
+ 0xff0000ff,
+ 0xffffffff,
+ 0xffffffff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-point-size-attribute.c b/cogl/tests/conform/test-point-size-attribute.c
new file mode 100644
index 000000000..a08d1daa9
--- /dev/null
+++ b/cogl/tests/conform/test-point-size-attribute.c
@@ -0,0 +1,166 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+/* This test assumes the GL driver supports point sizes up to 16
+ pixels. Cogl should probably have some way of querying the size so
+ we start from that instead */
+#define MAX_POINT_SIZE 16
+#define MIN_POINT_SIZE 4
+#define N_POINTS (MAX_POINT_SIZE - MIN_POINT_SIZE + 1)
+/* The size of the area that we'll paint each point in */
+#define POINT_BOX_SIZE (MAX_POINT_SIZE * 2)
+
+typedef struct
+{
+ float x, y;
+ float point_size;
+} PointVertex;
+
+static int
+calc_coord_offset (int pos, int pos_index, int point_size)
+{
+ switch (pos_index)
+ {
+ case 0: return pos - point_size / 2 - 2;
+ case 1: return pos - point_size / 2 + 2;
+ case 2: return pos + point_size / 2 - 2;
+ case 3: return pos + point_size / 2 + 2;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+verify_point_size (CoglFramebuffer *test_fb,
+ int x_pos,
+ int y_pos,
+ int point_size)
+{
+ int y, x;
+
+ for (y = 0; y < 4; y++)
+ for (x = 0; x < 4; x++)
+ {
+ CoglBool in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2;
+ uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff;
+
+ test_utils_check_pixel (test_fb,
+ calc_coord_offset (x_pos, x, point_size),
+ calc_coord_offset (y_pos, y, point_size),
+ expected_pixel);
+ }
+}
+
+static CoglPrimitive *
+create_primitive (const char *attribute_name)
+{
+ PointVertex vertices[N_POINTS];
+ CoglAttributeBuffer *buffer;
+ CoglAttribute *attributes[2];
+ CoglPrimitive *prim;
+ int i;
+
+ for (i = 0; i < N_POINTS; i++)
+ {
+ vertices[i].x = i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2;
+ vertices[i].y = POINT_BOX_SIZE / 2;
+ vertices[i].point_size = MAX_POINT_SIZE - i;
+ }
+
+ buffer = cogl_attribute_buffer_new (test_ctx,
+ sizeof (vertices),
+ vertices);
+
+ attributes[0] = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (PointVertex),
+ G_STRUCT_OFFSET (PointVertex, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ attributes[1] = cogl_attribute_new (buffer,
+ attribute_name,
+ sizeof (PointVertex),
+ G_STRUCT_OFFSET (PointVertex, point_size),
+ 1, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+
+ prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS,
+ N_POINTS,
+ attributes,
+ 2 /* n_attributes */);
+
+ for (i = 0; i < 2; i++)
+ cogl_object_unref (attributes[i]);
+
+ return prim;
+}
+
+static void
+do_test (const char *attribute_name,
+ void (* pipeline_setup_func) (CoglPipeline *pipeline))
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglPrimitive *primitive;
+ CoglPipeline *pipeline;
+ int i;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, /* x_1, y_1 */
+ fb_width, /* x_2 */
+ fb_height /* y_2 */,
+ -1, 100 /* near/far */);
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+
+ primitive = create_primitive (attribute_name);
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_color4ub (pipeline, 0x00, 0xff, 0x00, 0xff);
+ cogl_pipeline_set_per_vertex_point_size (pipeline, TRUE, NULL);
+ if (pipeline_setup_func)
+ pipeline_setup_func (pipeline);
+ cogl_primitive_draw (primitive, test_fb, pipeline);
+ cogl_object_unref (pipeline);
+ cogl_object_unref (primitive);
+
+ /* Verify all of the points where drawn at the right size */
+ for (i = 0; i < N_POINTS; i++)
+ verify_point_size (test_fb,
+ i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2, /* x */
+ POINT_BOX_SIZE / 2, /* y */
+ MAX_POINT_SIZE - i /* point size */);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
+void
+test_point_size_attribute (void)
+{
+ do_test ("cogl_point_size_in", NULL);
+}
+
+static void
+setup_snippet (CoglPipeline *pipeline)
+{
+ CoglSnippet *snippet;
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_POINT_SIZE,
+ "attribute float "
+ "my_super_duper_point_size_attrib;\n",
+ NULL);
+ cogl_snippet_set_replace (snippet,
+ "cogl_point_size_out = "
+ "my_super_duper_point_size_attrib;\n");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+}
+
+void
+test_point_size_attribute_snippet (void)
+{
+ do_test ("my_super_duper_point_size_attrib", setup_snippet);
+}
diff --git a/cogl/tests/conform/test-point-size.c b/cogl/tests/conform/test-point-size.c
new file mode 100644
index 000000000..3c3af0f5e
--- /dev/null
+++ b/cogl/tests/conform/test-point-size.c
@@ -0,0 +1,99 @@
+#include <cogl/cogl2-experimental.h>
+
+#include "test-utils.h"
+
+/* This test assumes the GL driver supports point sizes up to 16
+ pixels. Cogl should probably have some way of querying the size so
+ we start from that instead */
+#define MAX_POINT_SIZE 16
+/* The size of the area that we'll paint each point in */
+#define POINT_BOX_SIZE (MAX_POINT_SIZE * 2)
+
+static int
+calc_coord_offset (int pos, int pos_index, int point_size)
+{
+ switch (pos_index)
+ {
+ case 0: return pos - point_size / 2 - 2;
+ case 1: return pos - point_size / 2 + 2;
+ case 2: return pos + point_size / 2 - 2;
+ case 3: return pos + point_size / 2 + 2;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+verify_point_size (CoglFramebuffer *test_fb,
+ int x_pos,
+ int y_pos,
+ int point_size)
+{
+ int y, x;
+
+ for (y = 0; y < 4; y++)
+ for (x = 0; x < 4; x++)
+ {
+ CoglBool in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2;
+ uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff;
+
+ test_utils_check_pixel (test_fb,
+ calc_coord_offset (x_pos, x, point_size),
+ calc_coord_offset (y_pos, y, point_size),
+ expected_pixel);
+ }
+}
+
+void
+test_point_size (void)
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ int point_size;
+ int x_pos;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, /* x_1, y_1 */
+ fb_width, /* x_2 */
+ fb_height /* y_2 */,
+ -1, 100 /* near/far */);
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 1.0f, 0.0f, 0.0f, 1.0f);
+
+ /* Try a rendering a single point with a few different point
+ sizes */
+ for (x_pos = 0, point_size = MAX_POINT_SIZE;
+ point_size >= 4;
+ x_pos += POINT_BOX_SIZE, point_size /= 2)
+ {
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ CoglVertexP2 point = { x_pos + POINT_BOX_SIZE / 2,
+ POINT_BOX_SIZE / 2 };
+ CoglPrimitive *prim =
+ cogl_primitive_new_p2 (test_ctx,
+ COGL_VERTICES_MODE_POINTS,
+ 1, /* n_vertices */
+ &point);
+
+ cogl_pipeline_set_point_size (pipeline, point_size);
+ cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255);
+ cogl_primitive_draw (prim, test_fb, pipeline);
+
+ cogl_object_unref (prim);
+ cogl_object_unref (pipeline);
+ }
+
+ /* Verify all of the points where drawn at the right size */
+ for (x_pos = 0, point_size = MAX_POINT_SIZE;
+ point_size >= 4;
+ x_pos += POINT_BOX_SIZE, point_size /= 2)
+ verify_point_size (test_fb,
+ x_pos + POINT_BOX_SIZE / 2,
+ POINT_BOX_SIZE / 2,
+ point_size);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-point-sprite.c b/cogl/tests/conform/test-point-sprite.c
new file mode 100644
index 000000000..eb80cfb0a
--- /dev/null
+++ b/cogl/tests/conform/test-point-sprite.c
@@ -0,0 +1,194 @@
+#include <cogl/cogl2-experimental.h>
+
+#include "test-utils.h"
+
+#define POINT_SIZE 8
+
+static const CoglVertexP2T2
+point =
+ {
+ POINT_SIZE, POINT_SIZE,
+ 0.0f, 0.0f
+ };
+
+static const uint8_t
+tex_data[3 * 2 * 2] =
+ {
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00,
+ 0x00, 0xff, 0xff, 0xff, 0x00, 0x00
+ };
+
+static void
+do_test (CoglBool check_orientation,
+ CoglBool use_glsl)
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglPrimitive *prim;
+ CoglError *error = NULL;
+ CoglTexture2D *tex_2d;
+ CoglPipeline *pipeline, *solid_pipeline;
+ int tex_height;
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, /* x_1, y_1 */
+ fb_width, /* x_2 */
+ fb_height /* y_2 */,
+ -1, 100 /* near/far */);
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 1.0f, 1.0f, 1.0f, 1.0f);
+
+ /* If we're not checking the orientation of the point sprite then
+ * we'll set the height of the texture to 1 so that the vertical
+ * orientation does not matter */
+ if (check_orientation)
+ tex_height = 2;
+ else
+ tex_height = 1;
+
+ tex_2d = cogl_texture_2d_new_from_data (test_ctx,
+ 2, tex_height, /* width/height */
+ COGL_PIXEL_FORMAT_RGB_888,
+ 6, /* row stride */
+ tex_data,
+ &error);
+ g_assert (tex_2d != NULL);
+ g_assert (error == NULL);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex_2d);
+
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0, /* layer_index */
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_point_size (pipeline, POINT_SIZE);
+
+ /* If we're using GLSL then we don't need to enable point sprite
+ * coords and we can just directly reference cogl_point_coord in the
+ * snippet */
+ if (use_glsl)
+ {
+ CoglSnippet *snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+ NULL, /* declarations */
+ NULL /* post */);
+ static const char source[] =
+ " cogl_texel = texture2D (cogl_sampler, cogl_point_coord);\n";
+
+ cogl_snippet_set_replace (snippet, source);
+
+ /* Keep a reference to the original pipeline because there is no
+ * way to remove a snippet in order to recreate the solid
+ * pipeline */
+ solid_pipeline = cogl_pipeline_copy (pipeline);
+
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+
+ cogl_object_unref (snippet);
+ }
+ else
+ {
+ CoglBool res =
+ cogl_pipeline_set_layer_point_sprite_coords_enabled (pipeline,
+ /* layer_index */
+ 0,
+ /* enable */
+ TRUE,
+ &error);
+ g_assert (res == TRUE);
+ g_assert (error == NULL);
+
+ solid_pipeline = cogl_pipeline_copy (pipeline);
+
+ res =
+ cogl_pipeline_set_layer_point_sprite_coords_enabled (solid_pipeline,
+ /* layer_index */
+ 0,
+ /* enable */
+ FALSE,
+ &error);
+
+ g_assert (res == TRUE);
+ g_assert (error == NULL);
+ }
+
+ prim = cogl_primitive_new_p2t2 (test_ctx,
+ COGL_VERTICES_MODE_POINTS,
+ 1, /* n_vertices */
+ &point);
+
+ cogl_primitive_draw (prim, test_fb, pipeline);
+
+ /* Render the primitive again without point sprites to make sure
+ disabling it works */
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb,
+ POINT_SIZE * 2, /* x */
+ 0.0f, /* y */
+ 0.0f /* z */);
+ cogl_primitive_draw (prim, test_fb, solid_pipeline);
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ cogl_object_unref (prim);
+ cogl_object_unref (solid_pipeline);
+ cogl_object_unref (pipeline);
+ cogl_object_unref (tex_2d);
+
+ test_utils_check_pixel (test_fb,
+ POINT_SIZE - POINT_SIZE / 4,
+ POINT_SIZE - POINT_SIZE / 4,
+ 0x0000ffff);
+ test_utils_check_pixel (test_fb,
+ POINT_SIZE + POINT_SIZE / 4,
+ POINT_SIZE - POINT_SIZE / 4,
+ 0x00ff00ff);
+ test_utils_check_pixel (test_fb,
+ POINT_SIZE - POINT_SIZE / 4,
+ POINT_SIZE + POINT_SIZE / 4,
+ check_orientation ?
+ 0x00ffffff :
+ 0x0000ffff);
+ test_utils_check_pixel (test_fb,
+ POINT_SIZE + POINT_SIZE / 4,
+ POINT_SIZE + POINT_SIZE / 4,
+ check_orientation ?
+ 0xff0000ff :
+ 0x00ff00ff);
+
+ /* When rendering without the point sprites all of the texture
+ coordinates should be 0,0 so it should get the top-left texel
+ which is blue */
+ test_utils_check_region (test_fb,
+ POINT_SIZE * 3 - POINT_SIZE / 2 + 1,
+ POINT_SIZE - POINT_SIZE / 2 + 1,
+ POINT_SIZE - 2, POINT_SIZE - 2,
+ 0x0000ffff);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
+void
+test_point_sprite (void)
+{
+ do_test (FALSE /* don't check orientation */,
+ FALSE /* don't use GLSL */);
+}
+
+void
+test_point_sprite_orientation (void)
+{
+ do_test (TRUE /* check orientation */,
+ FALSE /* don't use GLSL */);
+}
+
+void
+test_point_sprite_glsl (void)
+{
+ do_test (FALSE /* don't check orientation */,
+ TRUE /* use GLSL */);
+}
diff --git a/cogl/tests/conform/test-premult.c b/cogl/tests/conform/test-premult.c
new file mode 100644
index 000000000..fa60bdf1e
--- /dev/null
+++ b/cogl/tests/conform/test-premult.c
@@ -0,0 +1,301 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define QUAD_WIDTH 32
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef enum _MakeTextureFlags
+{
+ TEXTURE_FLAG_SET_PREMULTIPLIED = 1,
+ TEXTURE_FLAG_SET_UNPREMULTIPLIED = 1<<1,
+} MakeTextureFlags;
+
+static guchar *
+gen_tex_data (uint32_t color)
+{
+ guchar *tex_data, *p;
+ uint8_t r = MASK_RED (color);
+ uint8_t g = MASK_GREEN (color);
+ uint8_t b = MASK_BLUE (color);
+ uint8_t a = MASK_ALPHA (color);
+
+ tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
+
+ for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
+ {
+ *(--p) = a;
+ *(--p) = b;
+ *(--p) = g;
+ *(--p) = r;
+ }
+
+ return tex_data;
+}
+
+static CoglTexture *
+make_texture (uint32_t color,
+ CoglPixelFormat src_format,
+ MakeTextureFlags flags)
+{
+ CoglTexture2D *tex_2d;
+ guchar *tex_data = gen_tex_data (color);
+ CoglBitmap *bmp = cogl_bitmap_new_for_data (test_ctx,
+ QUAD_WIDTH,
+ QUAD_WIDTH,
+ src_format,
+ QUAD_WIDTH * 4,
+ tex_data);
+
+ tex_2d = cogl_texture_2d_new_from_bitmap (bmp);
+
+ if (flags & TEXTURE_FLAG_SET_PREMULTIPLIED)
+ cogl_texture_set_premultiplied (tex_2d, TRUE);
+ else if (flags & TEXTURE_FLAG_SET_UNPREMULTIPLIED)
+ cogl_texture_set_premultiplied (tex_2d, FALSE);
+
+ cogl_object_unref (bmp);
+ g_free (tex_data);
+
+ return tex_2d;
+}
+
+static void
+set_region (CoglTexture *tex,
+ uint32_t color,
+ CoglPixelFormat format)
+{
+ guchar *tex_data = gen_tex_data (color);
+
+ cogl_texture_set_region (tex,
+ 0, 0, /* src x, y */
+ 0, 0, /* dst x, y */
+ QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
+ QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
+ format,
+ 0, /* auto compute row stride */
+ tex_data);
+}
+
+static void
+check_texture (CoglPipeline *pipeline,
+ CoglHandle material,
+ int x,
+ int y,
+ CoglTexture *tex,
+ uint32_t expected_result)
+{
+ /* Legacy */
+ cogl_push_framebuffer (test_fb);
+ cogl_material_set_layer (material, 0, tex);
+ cogl_set_source (material);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result);
+ cogl_pop_framebuffer ();
+
+ /* New API */
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline,
+ x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+ test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result);
+}
+
+void
+test_premult (void)
+{
+ CoglPipeline *pipeline;
+ CoglHandle material;
+ CoglTexture *tex;
+
+ cogl_framebuffer_orthographic (test_fb, 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 1.0f, 1.0f, 1.0f, 1.0f);
+
+ /* Legacy */
+ material = cogl_material_new ();
+ cogl_material_set_blend (material,
+ "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ cogl_material_set_layer_combine (material, 0,
+ "RGBA = REPLACE (TEXTURE)", NULL);
+
+ /* New API */
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_blend (pipeline,
+ "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ cogl_pipeline_set_layer_combine (pipeline, 0,
+ "RGBA = REPLACE (TEXTURE)", NULL);
+
+ /* If the user explicitly specifies an unmultiplied internal format then
+ * Cogl shouldn't automatically premultiply the given texture data... */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xff00ff80, "
+ "src = RGBA_8888, internal = RGBA_8888)\n");
+ tex = make_texture (0xff00ff80,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ TEXTURE_FLAG_SET_UNPREMULTIPLIED);
+ check_texture (pipeline, material, 0, 0, /* position */
+ tex,
+ 0xff00ff80); /* expected */
+
+ /* If the user explicitly requests a premultiplied internal format and
+ * gives unmultiplied src data then Cogl should always premultiply that
+ * for us */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xff00ff80, "
+ "src = RGBA_8888, internal = RGBA_8888_PRE)\n");
+ tex = make_texture (0xff00ff80,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ TEXTURE_FLAG_SET_PREMULTIPLIED);
+ check_texture (pipeline, material, 1, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /* If the user doesn't explicitly declare that the texture is premultiplied
+ * then Cogl should assume it is by default should premultiply
+ * unpremultiplied texture data...
+ */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xff00ff80, "
+ "src = RGBA_8888, internal = ANY)\n");
+ tex = make_texture (0xff00ff80,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ 0); /* default premultiplied status */
+ check_texture (pipeline, material, 2, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /* If the user requests a premultiplied internal texture format and supplies
+ * premultiplied source data, Cogl should never modify that source data...
+ */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0x80008080, "
+ "src = RGBA_8888_PRE, "
+ "internal = RGBA_8888_PRE)\n");
+ tex = make_texture (0x80008080,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+ TEXTURE_FLAG_SET_PREMULTIPLIED);
+ check_texture (pipeline, material, 3, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /* If the user requests an unmultiplied internal texture format, but
+ * supplies premultiplied source data, then Cogl should always
+ * un-premultiply the source data... */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0x80008080, "
+ "src = RGBA_8888_PRE, internal = RGBA_8888)\n");
+ tex = make_texture (0x80008080,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+ TEXTURE_FLAG_SET_UNPREMULTIPLIED);
+ check_texture (pipeline, material, 4, 0, /* position */
+ tex,
+ 0xff00ff80); /* expected */
+
+ /* If the user allows any internal texture format and provides premultipled
+ * source data then by default Cogl shouldn't modify the source data...
+ * (In the future there will be additional Cogl API to control this
+ * behaviour) */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0x80008080, "
+ "src = RGBA_8888_PRE, internal = ANY)\n");
+ tex = make_texture (0x80008080,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+ 0); /* default premultiplied status */
+ check_texture (pipeline, material, 5, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /*
+ * Test cogl_texture_set_region() ....
+ */
+
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xDEADBEEF, "
+ "src = RGBA_8888, internal = RGBA_8888)\n");
+ tex = make_texture (0xDEADBEEF,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ TEXTURE_FLAG_SET_UNPREMULTIPLIED);
+ if (cogl_test_verbose ())
+ g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+ set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888);
+ check_texture (pipeline, material, 6, 0, /* position */
+ tex,
+ 0xff00ff80); /* expected */
+
+ /* Updating a texture region for an unmultiplied texture using premultiplied
+ * region data should result in Cogl unmultiplying the given region data...
+ */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xDEADBEEF, "
+ "src = RGBA_8888, internal = RGBA_8888)\n");
+ tex = make_texture (0xDEADBEEF,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ TEXTURE_FLAG_SET_UNPREMULTIPLIED);
+ if (cogl_test_verbose ())
+ g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+ set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE);
+ check_texture (pipeline, material, 7, 0, /* position */
+ tex,
+ 0xff00ff80); /* expected */
+
+
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xDEADBEEF, "
+ "src = RGBA_8888_PRE, "
+ "internal = RGBA_8888_PRE)\n");
+ tex = make_texture (0xDEADBEEF,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+ TEXTURE_FLAG_SET_PREMULTIPLIED);
+ if (cogl_test_verbose ())
+ g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+ set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE);
+ check_texture (pipeline, material, 8, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+
+ /* Updating a texture region for a premultiplied texture using unmultiplied
+ * region data should result in Cogl premultiplying the given region data...
+ */
+ if (cogl_test_verbose ())
+ g_print ("make_texture (0xDEADBEEF, "
+ "src = RGBA_8888_PRE, "
+ "internal = RGBA_8888_PRE)\n");
+ tex = make_texture (0xDEADBEEF,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+ TEXTURE_FLAG_SET_PREMULTIPLIED);
+ if (cogl_test_verbose ())
+ g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+ set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888);
+ check_texture (pipeline, material, 9, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-primitive-and-journal.c b/cogl/tests/conform/test-primitive-and-journal.c
new file mode 100644
index 000000000..f978cd5ee
--- /dev/null
+++ b/cogl/tests/conform/test-primitive-and-journal.c
@@ -0,0 +1,122 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+typedef CoglVertexP2C4 Vertex;
+
+static void
+setup_orthographic_modelview (void)
+{
+ CoglMatrix matrix;
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+
+ /* Set up a non-identity modelview matrix. When the journal is
+ * flushed it will usually flush the identity matrix. Using the
+ * non-default matrix ensures that we test that Cogl restores the
+ * matrix we asked for. The matrix sets up an orthographic transform
+ * in the modelview matrix */
+
+ cogl_matrix_init_identity (&matrix);
+ cogl_matrix_orthographic (&matrix,
+ 0.0f, 0.0f, /* x_1 y_1 */
+ fb_width,
+ fb_height,
+ -1.0f, /* nearval */
+ 1.0f /* farval */);
+ cogl_framebuffer_set_modelview_matrix (test_fb, &matrix);
+}
+
+static void
+create_primitives (CoglPrimitive *primitives[2])
+{
+ static const Vertex vertex_data[8] =
+ {
+ /* triangle strip 1 */
+ { 0, 0, 255, 0, 0, 255 },
+ { 0, 100, 255, 0, 0, 255 },
+ { 100, 0, 255, 0, 0, 255 },
+ { 100, 100, 255, 0, 0, 255 },
+ /* triangle strip 2 */
+ { 200, 0, 0, 0, 255, 255 },
+ { 200, 100, 0, 0, 255, 255 },
+ { 300, 0, 0, 0, 255, 255 },
+ { 300, 100, 0, 0, 255, 255 },
+ };
+
+ primitives[0] = cogl_primitive_new_p2c4 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLE_STRIP,
+ G_N_ELEMENTS (vertex_data),
+ vertex_data);
+ cogl_primitive_set_n_vertices (primitives[0], 4);
+
+ primitives[1] = cogl_primitive_copy (primitives[0]);
+ cogl_primitive_set_first_vertex (primitives[1], 4);
+ cogl_primitive_set_n_vertices (primitives[1], 4);
+}
+
+static CoglPipeline *
+create_pipeline (void)
+{
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255);
+
+ return pipeline;
+}
+
+void
+test_primitive_and_journal (void)
+{
+ CoglPrimitive *primitives[2];
+ CoglPipeline *pipeline;
+
+ setup_orthographic_modelview ();
+ create_primitives (primitives);
+ pipeline = create_pipeline ();
+
+ /* Set a clip to clip all three rectangles to just the bottom half.
+ * The journal flushes its own clip state so this verifies that the
+ * clip state is correctly restored for the second primitive. */
+ cogl_framebuffer_push_rectangle_clip (test_fb,
+ 0, 50, 300, 100);
+
+ cogl_primitive_draw (primitives[0], test_fb, pipeline);
+
+ /* Draw a rectangle using the journal in-between the two primitives.
+ * This should test that the journal gets flushed correctly and that
+ * the modelview matrix is restored. Half of the rectangle should be
+ * overriden by the second primitive */
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 100, 0, /* x1/y1 */
+ 300, 100 /* x2/y2 */);
+
+ cogl_primitive_draw (primitives[1], test_fb, pipeline);
+
+ /* Check the three rectangles */
+ test_utils_check_region (test_fb,
+ 1, 51,
+ 98, 48,
+ 0xff0000ff);
+ test_utils_check_region (test_fb,
+ 101, 51,
+ 98, 48,
+ 0x00ff00ff);
+ test_utils_check_region (test_fb,
+ 201, 51,
+ 98, 48,
+ 0x0000ffff);
+
+ /* Check that the top half of all of the rectangles was clipped */
+ test_utils_check_region (test_fb,
+ 1, 1,
+ 298, 48,
+ 0x000000ff);
+
+ cogl_framebuffer_pop_clip (test_fb);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-primitive.c b/cogl/tests/conform/test-primitive.c
new file mode 100644
index 000000000..db264fc1c
--- /dev/null
+++ b/cogl/tests/conform/test-primitive.c
@@ -0,0 +1,334 @@
+#include <cogl/cogl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ int fb_width;
+ int fb_height;
+} TestState;
+
+#define PRIM_COLOR 0xff00ffff
+#define TEX_COLOR 0x0000ffff
+
+#define N_ATTRIBS 8
+
+typedef CoglPrimitive * (* TestPrimFunc) (CoglContext *ctx, uint32_t *expected_color);
+
+static CoglPrimitive *
+test_prim_p2 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP2 verts[] =
+ { { 0, 0 }, { 0, 10 }, { 10, 0 } };
+
+ return cogl_primitive_new_p2 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP3 verts[] =
+ { { 0, 0, 0 }, { 0, 10, 0 }, { 10, 0, 0 } };
+
+ return cogl_primitive_new_p3 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2c4 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP2C4 verts[] =
+ { { 0, 0, 255, 255, 0, 255 },
+ { 0, 10, 255, 255, 0, 255 },
+ { 10, 0, 255, 255, 0, 255 } };
+
+ *expected_color = 0xffff00ff;
+
+ return cogl_primitive_new_p2c4 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3c4 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP3C4 verts[] =
+ { { 0, 0, 0, 255, 255, 0, 255 },
+ { 0, 10, 0, 255, 255, 0, 255 },
+ { 10, 0, 0, 255, 255, 0, 255 } };
+
+ *expected_color = 0xffff00ff;
+
+ return cogl_primitive_new_p3c4 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP2T2 verts[] =
+ { { 0, 0, 1, 0 },
+ { 0, 10, 1, 0 },
+ { 10, 0, 1, 0 } };
+
+ *expected_color = TEX_COLOR;
+
+ return cogl_primitive_new_p2t2 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP3T2 verts[] =
+ { { 0, 0, 0, 1, 0 },
+ { 0, 10, 0, 1, 0 },
+ { 10, 0, 0, 1, 0 } };
+
+ *expected_color = TEX_COLOR;
+
+ return cogl_primitive_new_p3t2 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2c4 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP2T2C4 verts[] =
+ { { 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+ { 0, 10, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+ { 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } };
+
+ /* The blue component of the texture color should be replaced with 0xf0 */
+ *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000;
+
+ return cogl_primitive_new_p2t2c4 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2c4 (CoglContext *ctx, uint32_t *expected_color)
+{
+ static const CoglVertexP3T2C4 verts[] =
+ { { 0, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+ { 0, 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+ { 10, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } };
+
+ /* The blue component of the texture color should be replaced with 0xf0 */
+ *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000;
+
+ return cogl_primitive_new_p3t2c4 (test_ctx,
+ COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static const TestPrimFunc
+test_prim_funcs[] =
+ {
+ test_prim_p2,
+ test_prim_p3,
+ test_prim_p2c4,
+ test_prim_p3c4,
+ test_prim_p2t2,
+ test_prim_p3t2,
+ test_prim_p2t2c4,
+ test_prim_p3t2c4
+ };
+
+static void
+test_paint (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglTexture *tex;
+ uint8_t tex_data[6];
+ int i;
+
+ /* Create a two pixel texture. The first pixel is white and the
+ second pixel is tex_color. The assumption is that if no texture
+ coordinates are specified then it will default to 0,0 and get
+ white */
+ tex_data[0] = 255;
+ tex_data[1] = 255;
+ tex_data[2] = 255;
+ tex_data[3] = (TEX_COLOR >> 24) & 0xff;
+ tex_data[4] = (TEX_COLOR >> 16) & 0xff;
+ tex_data[5] = (TEX_COLOR >> 8) & 0xff;
+ tex = test_utils_texture_new_from_data (test_ctx,
+ 2, 1, /* size */
+ TEST_UTILS_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGB_888,
+ 6, /* rowstride */
+ tex_data);
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_color4ub (pipeline,
+ (PRIM_COLOR >> 24) & 0xff,
+ (PRIM_COLOR >> 16) & 0xff,
+ (PRIM_COLOR >> 8) & 0xff,
+ (PRIM_COLOR >> 0) & 0xff);
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_object_unref (tex);
+
+ for (i = 0; i < G_N_ELEMENTS (test_prim_funcs); i++)
+ {
+ CoglPrimitive *prim;
+ uint32_t expected_color = PRIM_COLOR;
+
+ prim = test_prim_funcs[i] (test_ctx, &expected_color);
+
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, i * 10, 0, 0);
+ cogl_primitive_draw (prim, test_fb, pipeline);
+ cogl_framebuffer_pop_matrix (test_fb);
+
+ test_utils_check_pixel (test_fb, i * 10 + 2, 2, expected_color);
+
+ cogl_object_unref (prim);
+ }
+
+ cogl_object_unref (pipeline);
+}
+
+static CoglBool
+get_attributes_cb (CoglPrimitive *prim,
+ CoglAttribute *attrib,
+ void *user_data)
+{
+ CoglAttribute ***p = user_data;
+ *((* p)++) = attrib;
+ return TRUE;
+}
+
+static int
+compare_pointers (const void *a, const void *b)
+{
+ CoglAttribute *pa = *(CoglAttribute **) a;
+ CoglAttribute *pb = *(CoglAttribute **) b;
+
+ if (pa < pb)
+ return -1;
+ else if (pa > pb)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+test_copy (TestState *state)
+{
+ static const uint16_t indices_data[2] = { 1, 2 };
+ CoglAttributeBuffer *buffer =
+ cogl_attribute_buffer_new (test_ctx, 100, NULL);
+ CoglAttribute *attributes[N_ATTRIBS];
+ CoglAttribute *attributes_a[N_ATTRIBS], *attributes_b[N_ATTRIBS];
+ CoglAttribute **p;
+ CoglPrimitive *prim_a, *prim_b;
+ CoglIndices *indices;
+ int i;
+
+ for (i = 0; i < N_ATTRIBS; i++)
+ {
+ char *name = g_strdup_printf ("foo_%i", i);
+ attributes[i] = cogl_attribute_new (buffer,
+ name,
+ 16, /* stride */
+ 16, /* offset */
+ 2, /* components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ g_free (name);
+ }
+
+ prim_a = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 8, /* n_vertices */
+ attributes,
+ N_ATTRIBS);
+
+ indices = cogl_indices_new (test_ctx,
+ COGL_INDICES_TYPE_UNSIGNED_SHORT,
+ indices_data,
+ 2 /* n_indices */);
+
+ cogl_primitive_set_first_vertex (prim_a, 12);
+ cogl_primitive_set_indices (prim_a, indices, 2);
+
+ prim_b = cogl_primitive_copy (prim_a);
+
+ p = attributes_a;
+ cogl_primitive_foreach_attribute (prim_a,
+ get_attributes_cb,
+ &p);
+ g_assert_cmpint (p - attributes_a, ==, N_ATTRIBS);
+
+ p = attributes_b;
+ cogl_primitive_foreach_attribute (prim_b,
+ get_attributes_cb,
+ &p);
+ g_assert_cmpint (p - attributes_b, ==, N_ATTRIBS);
+
+ qsort (attributes_a, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers);
+ qsort (attributes_b, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers);
+
+ g_assert (memcmp (attributes_a, attributes_b, sizeof (attributes_a)) == 0);
+
+ g_assert_cmpint (cogl_primitive_get_first_vertex (prim_a),
+ ==,
+ cogl_primitive_get_first_vertex (prim_b));
+
+ g_assert_cmpint (cogl_primitive_get_n_vertices (prim_a),
+ ==,
+ cogl_primitive_get_n_vertices (prim_b));
+
+ g_assert_cmpint (cogl_primitive_get_mode (prim_a),
+ ==,
+ cogl_primitive_get_mode (prim_b));
+
+ g_assert (cogl_primitive_get_indices (prim_a) ==
+ cogl_primitive_get_indices (prim_b));
+
+ cogl_object_unref (prim_a);
+ cogl_object_unref (prim_b);
+ cogl_object_unref (indices);
+
+ for (i = 0; i < N_ATTRIBS; i++)
+ cogl_object_unref (attributes[i]);
+
+ cogl_object_unref (buffer);
+}
+
+void
+test_primitive (void)
+{
+ TestState state;
+
+ state.fb_width = cogl_framebuffer_get_width (test_fb);
+ state.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ state.fb_width,
+ state.fb_height,
+ -1,
+ 100);
+
+ test_paint (&state);
+ test_copy (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-read-texture-formats.c b/cogl/tests/conform/test-read-texture-formats.c
new file mode 100644
index 000000000..3fa4d8eea
--- /dev/null
+++ b/cogl/tests/conform/test-read-texture-formats.c
@@ -0,0 +1,222 @@
+#include <cogl/cogl2-experimental.h>
+#include <stdarg.h>
+
+#include "test-utils.h"
+
+/*
+ * This tests reading back an RGBA texture in all of the available
+ * pixel formats
+ */
+
+static const uint8_t tex_data[4] = { 0x12, 0x34, 0x56, 0x78 };
+
+static void
+test_read_byte (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ uint8_t expected_byte)
+{
+ uint8_t received_byte;
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 1, /* rowstride */
+ &received_byte);
+
+ g_assert_cmpint (expected_byte, ==, received_byte);
+}
+
+static void
+test_read_short (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ ...)
+{
+ va_list ap;
+ int bits;
+ uint16_t received_value;
+ uint16_t expected_value = 0;
+ char *received_value_str;
+ char *expected_value_str;
+ int bits_sum = 0;
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 2, /* rowstride */
+ (uint8_t *) &received_value);
+
+ va_start (ap, format);
+
+ /* Convert the va args into a single 16-bit expected value */
+ while ((bits = va_arg (ap, int)) != -1)
+ {
+ int value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255;
+
+ bits_sum += bits;
+
+ expected_value |= value << (16 - bits_sum);
+ }
+
+ va_end (ap);
+
+ received_value_str = g_strdup_printf ("0x%04x", received_value);
+ expected_value_str = g_strdup_printf ("0x%04x", expected_value);
+ g_assert_cmpstr (received_value_str, ==, expected_value_str);
+ g_free (received_value_str);
+ g_free (expected_value_str);
+}
+
+static void
+test_read_888 (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ uint32_t expected_pixel)
+{
+ uint8_t pixel[4];
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 4, /* rowstride */
+ pixel);
+
+ test_utils_compare_pixel (pixel, expected_pixel);
+}
+
+static void
+test_read_88 (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ uint32_t expected_pixel)
+{
+ uint8_t pixel[4];
+
+ pixel[2] = 0x00;
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 2, /* rowstride */
+ pixel);
+
+ test_utils_compare_pixel (pixel, expected_pixel);
+}
+
+static void
+test_read_8888 (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ uint32_t expected_pixel)
+{
+ uint32_t received_pixel;
+ char *received_value_str;
+ char *expected_value_str;
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 4, /* rowstride */
+ (uint8_t *) &received_pixel);
+
+ received_pixel = GUINT32_FROM_BE (received_pixel);
+
+ received_value_str = g_strdup_printf ("0x%08x", received_pixel);
+ expected_value_str = g_strdup_printf ("0x%08x", expected_pixel);
+ g_assert_cmpstr (received_value_str, ==, expected_value_str);
+ g_free (received_value_str);
+ g_free (expected_value_str);
+}
+
+static void
+test_read_int (CoglTexture2D *tex_2d,
+ CoglPixelFormat format,
+ ...)
+{
+ va_list ap;
+ int bits;
+ uint32_t received_value;
+ uint32_t expected_value = 0;
+ char *received_value_str;
+ char *expected_value_str;
+ int bits_sum = 0;
+
+ cogl_texture_get_data (tex_2d,
+ format,
+ 4, /* rowstride */
+ (uint8_t *) &received_value);
+
+ va_start (ap, format);
+
+ /* Convert the va args into a single 32-bit expected value */
+ while ((bits = va_arg (ap, int)) != -1)
+ {
+ uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255;
+
+ bits_sum += bits;
+
+ expected_value |= value << (32 - bits_sum);
+ }
+
+ va_end (ap);
+
+ received_value_str = g_strdup_printf ("0x%08x", received_value);
+ expected_value_str = g_strdup_printf ("0x%08x", expected_value);
+ g_assert_cmpstr (received_value_str, ==, expected_value_str);
+ g_free (received_value_str);
+ g_free (expected_value_str);
+}
+
+void
+test_read_texture_formats (void)
+{
+ CoglTexture2D *tex_2d;
+
+ tex_2d = cogl_texture_2d_new_from_data (test_ctx,
+ 1, 1, /* width / height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 4, /* rowstride */
+ tex_data,
+ NULL);
+
+ test_read_byte (tex_2d, COGL_PIXEL_FORMAT_A_8, 0x78);
+
+#if 0
+ /* I'm not sure what's the right value to put here because Nvidia
+ and Mesa seem to behave differently so one of them must be
+ wrong. */
+ test_read_byte (tex_2d, COGL_PIXEL_FORMAT_G_8, 0x9c);
+#endif
+
+ /* We should always be able to read into an RG buffer regardless of
+ * whether RG textures are supported because Cogl will do the
+ * conversion for us */
+ test_read_88 (tex_2d, COGL_PIXEL_FORMAT_RG_88, 0x123400ff);
+
+ test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGB_565,
+ 5, 0x12, 6, 0x34, 5, 0x56,
+ -1);
+ test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_4444_PRE,
+ 4, 0x12, 4, 0x34, 4, 0x56, 4, 0x78,
+ -1);
+ test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_5551_PRE,
+ 5, 0x12, 5, 0x34, 5, 0x56, 1, 0x78,
+ -1);
+
+ test_read_888 (tex_2d, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff);
+ test_read_888 (tex_2d, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff);
+
+ test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 0x12345678);
+ test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_BGRA_8888_PRE, 0x56341278);
+ test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ARGB_8888_PRE, 0x78123456);
+ test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ABGR_8888_PRE, 0x78563412);
+
+ test_read_int (tex_2d, COGL_PIXEL_FORMAT_RGBA_1010102_PRE,
+ 10, 0x12, 10, 0x34, 10, 0x56, 2, 0x78,
+ -1);
+ test_read_int (tex_2d, COGL_PIXEL_FORMAT_BGRA_1010102_PRE,
+ 10, 0x56, 10, 0x34, 10, 0x12, 2, 0x78,
+ -1);
+ test_read_int (tex_2d, COGL_PIXEL_FORMAT_ARGB_2101010_PRE,
+ 2, 0x78, 10, 0x12, 10, 0x34, 10, 0x56,
+ -1);
+ test_read_int (tex_2d, COGL_PIXEL_FORMAT_ABGR_2101010_PRE,
+ 2, 0x78, 10, 0x56, 10, 0x34, 10, 0x12,
+ -1);
+
+ cogl_object_unref (tex_2d);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-readpixels.c b/cogl/tests/conform/test-readpixels.c
new file mode 100644
index 000000000..131b08b23
--- /dev/null
+++ b/cogl/tests/conform/test-readpixels.c
@@ -0,0 +1,178 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+#define FRAMEBUFFER_WIDTH 640
+#define FRAMEBUFFER_HEIGHT 480
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+
+static void
+on_paint (ClutterActor *actor, void *state)
+{
+ float saved_viewport[4];
+ CoglMatrix saved_projection;
+ CoglMatrix projection;
+ CoglMatrix modelview;
+ guchar *data;
+ CoglHandle tex;
+ CoglHandle offscreen;
+ uint32_t *pixels;
+ uint8_t *pixelsc;
+
+ /* Save the Clutter viewport/matrices and load identity matrices */
+
+ cogl_get_viewport (saved_viewport);
+ cogl_get_projection_matrix (&saved_projection);
+ cogl_push_matrix ();
+
+ cogl_matrix_init_identity (&projection);
+ cogl_matrix_init_identity (&modelview);
+
+ cogl_set_projection_matrix (&projection);
+ cogl_set_modelview_matrix (&modelview);
+
+ /* All offscreen rendering is done upside down so the first thing we
+ * verify is reading back grid of colors from a CoglOffscreen framebuffer
+ */
+
+ data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+ tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ TEST_UTILS_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */
+ COGL_PIXEL_FORMAT_ANY, /* internal fmt */
+ FRAMEBUFFER_WIDTH * 4, /* rowstride */
+ data);
+ g_free (data);
+ offscreen = cogl_offscreen_new_with_texture (tex);
+
+ cogl_push_framebuffer (offscreen);
+
+ /* red, top left */
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_rectangle (-1, 1, 0, 0);
+ /* green, top right */
+ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+ cogl_rectangle (0, 1, 1, 0);
+ /* blue, bottom left */
+ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+ cogl_rectangle (-1, 0, 0, -1);
+ /* white, bottom right */
+ cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+ cogl_rectangle (0, 0, 1, -1);
+
+ pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+ cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ (guchar *)pixels);
+
+ g_assert_cmpint (pixels[0], ==, 0xff0000ff);
+ g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00);
+ g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000);
+ g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff);
+ g_free (pixels);
+
+ cogl_pop_framebuffer ();
+ cogl_handle_unref (offscreen);
+
+ /* Now verify reading back from an onscreen framebuffer...
+ */
+
+ cogl_set_source_texture (tex);
+ cogl_rectangle (-1, 1, 1, -1);
+
+ pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+ cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ (guchar *)pixels);
+
+ g_assert_cmpint (pixels[0], ==, 0xff0000ff);
+ g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00);
+ g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000);
+ g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff);
+ g_free (pixels);
+
+ /* Verify using BGR format */
+
+ cogl_set_source_texture (tex);
+ cogl_rectangle (-1, 1, 1, -1);
+
+ pixelsc = g_malloc0 (FRAMEBUFFER_WIDTH * 3 * FRAMEBUFFER_HEIGHT);
+ cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_BGR_888,
+ (guchar *)pixelsc);
+
+ g_assert_cmpint (pixelsc[0], ==, 0x00);
+ g_assert_cmpint (pixelsc[1], ==, 0x00);
+ g_assert_cmpint (pixelsc[2], ==, 0xff);
+
+ g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 0], ==, 0x00);
+ g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 1], ==, 0xff);
+ g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 2], ==, 0x00);
+
+ g_free (pixelsc);
+
+ cogl_handle_unref (tex);
+
+ /* Restore the viewport and matrices state */
+ cogl_set_viewport (saved_viewport[0],
+ saved_viewport[1],
+ saved_viewport[2],
+ saved_viewport[3]);
+ cogl_set_projection_matrix (&saved_projection);
+ cogl_pop_matrix ();
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_readpixels (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ unsigned int idle_source;
+ ClutterActor *stage;
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ /* We force continuous redrawing of the stage, since we need to skip
+ * the first few frames, and we wont be doing anything else that
+ * will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+ g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL);
+
+ clutter_actor_show (stage);
+ clutter_main ();
+
+ g_source_remove (idle_source);
+
+ /* Remove all of the actors from the stage */
+ clutter_container_foreach (CLUTTER_CONTAINER (stage),
+ (ClutterCallback) clutter_actor_destroy,
+ NULL);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-snippets.c b/cogl/tests/conform/test-snippets.c
new file mode 100644
index 000000000..a251fc162
--- /dev/null
+++ b/cogl/tests/conform/test-snippets.c
@@ -0,0 +1,815 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ int fb_width, fb_height;
+} TestState;
+
+typedef void (* SnippetTestFunc) (TestState *state);
+
+static CoglPipeline *
+create_texture_pipeline (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglTexture *tex;
+ static const uint8_t tex_data[] =
+ {
+ 0xff, 0x00, 0x00, 0xff, /* red */ 0x00, 0xff, 0x00, 0xff, /* green */
+ 0x00, 0x00, 0xff, 0xff, /* blue */ 0xff, 0xff, 0x00, 0xff, /* yellow */
+ };
+
+ tex = test_utils_texture_new_from_data (test_ctx,
+ 2, 2, /* width/height */
+ TEST_UTILS_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 8, /* rowstride */
+ tex_data);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+
+ cogl_pipeline_set_layer_filters (pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ cogl_object_unref (tex);
+
+ return pipeline;
+}
+
+static void
+simple_fragment_snippet (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Simple fragment snippet */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL, /* declarations */
+ "cogl_color_out.g += 1.0;");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff);
+}
+
+static void
+simple_vertex_snippet (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Simple vertex snippet */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ NULL,
+ "cogl_color_out.b += 1.0;");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 10, 0, 20, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 15, 5, 0xff00ffff);
+}
+
+static void
+shared_uniform (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ int location;
+
+ /* Snippets sharing a uniform across the vertex and fragment
+ hooks */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ location = cogl_pipeline_get_uniform_location (pipeline, "a_value");
+ cogl_pipeline_set_uniform_1f (pipeline, location, 0.25f);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ "uniform float a_value;",
+ "cogl_color_out.b += a_value;");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ "uniform float a_value;",
+ "cogl_color_out.b += a_value;");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 20, 0, 30, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 25, 5, 0xff0080ff);
+}
+
+static void
+lots_snippets (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ int location;
+ int i;
+
+ /* Lots of snippets on one pipeline */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255);
+
+ for (i = 0; i < 3; i++)
+ {
+ char letter = 'x' + i;
+ char *uniform_name = g_strdup_printf ("%c_value", letter);
+ char *declarations = g_strdup_printf ("uniform float %s;\n",
+ uniform_name);
+ char *code = g_strdup_printf ("cogl_color_out.%c = %s;\n",
+ letter,
+ uniform_name);
+
+ location = cogl_pipeline_get_uniform_location (pipeline, uniform_name);
+ cogl_pipeline_set_uniform_1f (pipeline, location, (i + 1) * 0.1f);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ declarations,
+ code);
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ g_free (code);
+ g_free (uniform_name);
+ g_free (declarations);
+ }
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 30, 0, 40, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 35, 5, 0x19334cff);
+}
+
+static void
+shared_variable_pre_post (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Test that the pre string can declare variables used by the post
+ string */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 255, 255, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL, /* declarations */
+ "cogl_color_out = redvec;");
+ cogl_snippet_set_pre (snippet, "vec4 redvec = vec4 (1.0, 0.0, 0.0, 1.0);");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 40, 0, 50, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 45, 5, 0xff0000ff);
+}
+
+static void
+test_pipeline_caching (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Check that the pipeline caching works when unrelated pipelines
+ share snippets state. It's too hard to actually assert this in
+ the conformance test but at least it should be possible to see by
+ setting COGL_DEBUG=show-source to check whether this shader gets
+ generated twice */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ "/* This comment should only be seen ONCE\n"
+ " when COGL_DEBUG=show-source is TRUE\n"
+ " even though it is used in two different\n"
+ " unrelated pipelines */",
+ "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);\n");
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 50, 0, 60, 10);
+ cogl_object_unref (pipeline);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 60, 0, 70, 10);
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (snippet);
+
+ test_utils_check_pixel (test_fb, 55, 5, 0x00ff00ff);
+ test_utils_check_pixel (test_fb, 65, 5, 0x00ff00ff);
+}
+
+static void
+test_replace_string (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Check the replace string */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL);
+ cogl_snippet_set_pre (snippet,
+ "cogl_color_out = vec4 (0.0, 0.5, 0.0, 1.0);");
+ /* Remove the generated output. If the replace string isn't working
+ then the code from the pre string would get overwritten with
+ white */
+ cogl_snippet_set_replace (snippet, "/* do nothing */");
+ cogl_snippet_set_post (snippet,
+ "cogl_color_out += vec4 (0.5, 0.0, 0.0, 1.0);");
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 70, 0, 80, 10);
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (snippet);
+
+ test_utils_check_pixel (test_fb, 75, 5, 0x808000ff);
+}
+
+static void
+test_texture_lookup_hook (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Check the texture lookup hook */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+ NULL,
+ "cogl_texel.b += 1.0;");
+ /* Flip the texture coordinates around the y axis so that it will
+ get the green texel */
+ cogl_snippet_set_pre (snippet, "cogl_tex_coord.x = 1.0 - cogl_tex_coord.x;");
+
+ pipeline = create_texture_pipeline (state);
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 80, 0, 90, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (snippet);
+
+ test_utils_check_pixel (test_fb, 85, 5, 0x00ffffff);
+}
+
+static void
+test_multiple_samples (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Check that we can use the passed in sampler in the texture lookup
+ to sample multiple times */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+ NULL,
+ NULL);
+ cogl_snippet_set_replace (snippet,
+ "cogl_texel = "
+ "texture2D (cogl_sampler, vec2 (0.25, 0.25)) + "
+ "texture2D (cogl_sampler, vec2 (0.75, 0.25));");
+
+ pipeline = create_texture_pipeline (state);
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10);
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (snippet);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff);
+}
+
+static void
+test_replace_lookup_hook (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Check replacing the texture lookup hook */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, NULL);
+ cogl_snippet_set_replace (snippet, "cogl_texel = vec4 (0.0, 0.0, 1.0, 0.0);");
+
+ pipeline = create_texture_pipeline (state);
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 90, 0, 100, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ cogl_object_unref (snippet);
+
+ test_utils_check_pixel (test_fb, 95, 5, 0x0000ffff);
+}
+
+static void
+test_replace_snippet (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Test replacing a previous snippet */
+ pipeline = create_texture_pipeline (state);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL,
+ "cogl_color_out = vec4 (0.5, 0.5, 0.5, 1.0);");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL);
+ cogl_snippet_set_pre (snippet, "cogl_color_out = vec4 (1.0, 1.0, 1.0, 1.0);");
+ cogl_snippet_set_replace (snippet,
+ "cogl_color_out *= vec4 (1.0, 0.0, 0.0, 1.0);");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 100, 0, 110, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 105, 5, 0xff0000ff);
+}
+
+static void
+test_replace_fragment_layer (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Test replacing the fragment layer code */
+ pipeline = create_texture_pipeline (state);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL);
+ cogl_snippet_set_replace (snippet, "cogl_layer = vec4 (0.0, 0.0, 1.0, 1.0);");
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ /* Add a second layer which samples from the texture in the first
+ layer. The snippet override should cause the first layer not to
+ generate the code for the texture lookup but this second layer
+ should still be able to cause it to be generated */
+ cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGB = ADD(TEXTURE_0, PREVIOUS)"
+ "A = REPLACE(PREVIOUS)",
+ NULL);
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 110, 0, 120, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 115, 5, 0xff00ffff);
+}
+
+static void
+test_modify_fragment_layer (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Test modifying the fragment layer code */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_uniform_1f (pipeline,
+ cogl_pipeline_get_uniform_location (pipeline,
+ "a_value"),
+ 0.5);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
+ "uniform float a_value;",
+ "cogl_layer.g = a_value;");
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 120, 0, 130, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 125, 5, 0xff80ffff);
+}
+
+static void
+test_modify_vertex_layer (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ CoglMatrix matrix;
+
+ /* Test modifying the vertex layer code */
+ pipeline = create_texture_pipeline (state);
+
+ cogl_matrix_init_identity (&matrix);
+ cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
+ cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM,
+ NULL,
+ "cogl_tex_coord.x = 1.0;");
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 130, 0, 140, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 135, 5, 0xffff00ff);
+}
+
+static void
+test_replace_vertex_layer (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ CoglMatrix matrix;
+
+ /* Test replacing the vertex layer code */
+ pipeline = create_texture_pipeline (state);
+
+ cogl_matrix_init_identity (&matrix);
+ cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
+ cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM,
+ NULL,
+ NULL);
+ cogl_snippet_set_replace (snippet, "cogl_tex_coord.x = 1.0;\n");
+ cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ 140, 0, 150, 10,
+ 0, 0, 0, 0);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 145, 5, 0x00ff00ff);
+}
+
+static void
+test_vertex_transform_hook (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ CoglMatrix identity_matrix;
+ CoglMatrix matrix;
+ int location;
+
+ /* Test the vertex transform hook */
+
+ cogl_matrix_init_identity (&identity_matrix);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 255, 0, 255, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_TRANSFORM,
+ "uniform mat4 pmat;",
+ NULL);
+ cogl_snippet_set_replace (snippet, "cogl_position_out = "
+ "pmat * cogl_position_in;");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ /* Copy the current projection matrix to a uniform */
+ cogl_framebuffer_get_projection_matrix (test_fb, &matrix);
+ location = cogl_pipeline_get_uniform_location (pipeline, "pmat");
+ cogl_pipeline_set_uniform_matrix (pipeline,
+ location,
+ 4, /* dimensions */
+ 1, /* count */
+ FALSE, /* don't transpose */
+ cogl_matrix_get_array (&matrix));
+
+ /* Replace the real projection matrix with the identity. This should
+ mess up the drawing unless the snippet replacement is working */
+ cogl_framebuffer_set_projection_matrix (test_fb, &identity_matrix);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 150, 0, 160, 10);
+ cogl_object_unref (pipeline);
+
+ /* Restore the projection matrix */
+ cogl_framebuffer_set_projection_matrix (test_fb, &matrix);
+
+ test_utils_check_pixel (test_fb, 155, 5, 0xff00ffff);
+}
+
+static void
+test_global_vertex_hook (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ /* Creates a function in the global declarations hook which is used
+ * by a subsequent snippet. The subsequent snippets replace any
+ * previous snippets but this shouldn't prevent the global
+ * declarations from being generated */
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
+ /* declarations */
+ "float\n"
+ "multiply_by_two (float number)\n"
+ "{\n"
+ " return number * 2.0;\n"
+ "}\n",
+ /* post */
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_snippet_set_pre (snippet,
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_snippet_set_replace (snippet,
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ NULL, /* declarations */
+ NULL /* replace */);
+ cogl_snippet_set_replace (snippet,
+ "cogl_color_out.r = multiply_by_two (0.5);\n"
+ "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n"
+ "cogl_position_out = cogl_position_in;\n");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1, 1,
+ 10.0f * 2.0f / state->fb_width - 1.0f,
+ 10.0f * 2.0f / state->fb_height - 1.0f);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff);
+}
+
+static void
+test_global_fragment_hook (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ /* Creates a function in the global declarations hook which is used
+ * by a subsequent snippet. The subsequent snippets replace any
+ * previous snippets but this shouldn't prevent the global
+ * declarations from being generated */
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
+ /* declarations */
+ "float\n"
+ "multiply_by_four (float number)\n"
+ "{\n"
+ " return number * 4.0;\n"
+ "}\n",
+ /* post */
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_snippet_set_pre (snippet,
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_snippet_set_replace (snippet,
+ "This string shouldn't be used so "
+ "we can safely put garbage in here.");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL, /* declarations */
+ NULL /* replace */);
+ cogl_snippet_set_replace (snippet,
+ "cogl_color_out.r = multiply_by_four (0.25);\n"
+ "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ 0, 0, 10, 10);
+
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff);
+}
+
+static void
+test_snippet_order (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+
+ /* Verify that the snippets are executed in the right order. We'll
+ replace the r component of the color in the pre sections of the
+ snippets and the g component in the post. The pre sections should
+ be executed in the reverse order they were added and the post
+ sections in the same order as they were added. Therefore the r
+ component should be taken from the the second snippet and the g
+ component from the first */
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL,
+ "cogl_color_out.g = 0.5;\n");
+ cogl_snippet_set_pre (snippet, "cogl_color_out.r = 0.5;\n");
+ cogl_snippet_set_replace (snippet, "cogl_color_out.ba = vec2 (0.0, 1.0);");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL,
+ "cogl_color_out.g = 1.0;\n");
+ cogl_snippet_set_pre (snippet, "cogl_color_out.r = 1.0;\n");
+ cogl_pipeline_add_snippet (pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 160, 0, 170, 10);
+ cogl_object_unref (pipeline);
+
+ test_utils_check_pixel (test_fb, 165, 5, 0x80ff00ff);
+}
+
+static void
+test_naming_texture_units (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglSnippet *snippet;
+ CoglTexture *tex1, *tex2;
+
+ /* Test that we can sample from an arbitrary texture unit by naming
+ its layer number */
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ NULL,
+ NULL);
+ cogl_snippet_set_replace (snippet,
+ "cogl_color_out = "
+ "texture2D (cogl_sampler100, vec2 (0.0, 0.0)) + "
+ "texture2D (cogl_sampler200, vec2 (0.0, 0.0));");
+
+ tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff);
+ tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 100, tex1);
+ cogl_pipeline_set_layer_texture (pipeline, 200, tex2);
+
+ cogl_pipeline_add_snippet (pipeline, snippet);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (snippet);
+ cogl_object_unref (tex1);
+ cogl_object_unref (tex2);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff);
+}
+
+static void
+test_snippet_properties (TestState *state)
+{
+ CoglSnippet *snippet;
+
+ /* Sanity check modifying the snippet */
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "foo", "bar");
+ g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "foo");
+ g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar");
+ g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL);
+ g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL);
+
+ cogl_snippet_set_declarations (snippet, "fu");
+ g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu");
+ g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar");
+ g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL);
+ g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL);
+
+ cogl_snippet_set_post (snippet, "ba");
+ g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu");
+ g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba");
+ g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL);
+ g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL);
+
+ cogl_snippet_set_pre (snippet, "fuba");
+ g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu");
+ g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba");
+ g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL);
+ g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba");
+
+ cogl_snippet_set_replace (snippet, "baba");
+ g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu");
+ g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba");
+ g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, "baba");
+ g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba");
+
+ g_assert_cmpint (cogl_snippet_get_hook (snippet),
+ ==,
+ COGL_SNIPPET_HOOK_FRAGMENT);
+}
+
+static SnippetTestFunc
+tests[] =
+ {
+ simple_fragment_snippet,
+ simple_vertex_snippet,
+ shared_uniform,
+ lots_snippets,
+ shared_variable_pre_post,
+ test_pipeline_caching,
+ test_replace_string,
+ test_texture_lookup_hook,
+ test_multiple_samples,
+ test_replace_lookup_hook,
+ test_replace_snippet,
+ test_replace_fragment_layer,
+ test_modify_fragment_layer,
+ test_modify_vertex_layer,
+ test_replace_vertex_layer,
+ test_vertex_transform_hook,
+ test_global_fragment_hook,
+ test_global_vertex_hook,
+ test_snippet_order,
+ test_naming_texture_units,
+ test_snippet_properties
+ };
+
+static void
+run_tests (TestState *state)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (tests); i++)
+ {
+ cogl_framebuffer_clear4f (test_fb,
+ COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 1);
+
+ tests[i] (state);
+ }
+}
+
+void
+test_snippets (void)
+{
+ TestState state;
+
+ state.fb_width = cogl_framebuffer_get_width (test_fb);
+ state.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ state.fb_width,
+ state.fb_height,
+ -1,
+ 100);
+
+ run_tests (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-sparse-pipeline.c b/cogl/tests/conform/test-sparse-pipeline.c
new file mode 100644
index 000000000..04c81d27c
--- /dev/null
+++ b/cogl/tests/conform/test-sparse-pipeline.c
@@ -0,0 +1,62 @@
+#include <cogl/cogl2-experimental.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+typedef struct _TestState
+{
+ int fb_width;
+ int fb_height;
+} TestState;
+
+static void
+test_sparse_layer_combine (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglTexture *tex1, *tex2;
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ /* This tests that the TEXTURE_* numbers used in the layer combine
+ string refer to the layer number rather than the unit numbers by
+ creating a pipeline with very large layer numbers. This should
+ end up being mapped to much smaller unit numbers */
+
+ tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff);
+ tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 50, tex1);
+ cogl_pipeline_set_layer_texture (pipeline, 100, tex2);
+ cogl_pipeline_set_layer_combine (pipeline, 200,
+ "RGBA = ADD(TEXTURE_50, TEXTURE_100)",
+ NULL);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, -1, 1, 1);
+
+ test_utils_check_pixel (test_fb, 2, 2, 0xffff00ff);
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (tex1);
+ cogl_object_unref (tex2);
+}
+
+void
+test_sparse_pipeline (void)
+{
+ TestState state;
+
+ state.fb_width = cogl_framebuffer_get_width (test_fb);
+ state.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ test_sparse_layer_combine (&state);
+
+ /* FIXME: This should have a lot more tests, for example testing
+ whether using an attribute with sparse texture coordinates will
+ work */
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-sub-texture.c b/cogl/tests/conform/test-sub-texture.c
new file mode 100644
index 000000000..f049f3fae
--- /dev/null
+++ b/cogl/tests/conform/test-sub-texture.c
@@ -0,0 +1,325 @@
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+#define SOURCE_SIZE 32
+#define SOURCE_DIVISIONS_X 2
+#define SOURCE_DIVISIONS_Y 2
+#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X)
+#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y)
+
+#define TEST_INSET 1
+
+static const uint32_t
+corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] =
+ {
+ 0xff0000ff, /* red top left */
+ 0x00ff00ff, /* green top right */
+ 0x0000ffff, /* blue bottom left */
+ 0xff00ffff /* purple bottom right */
+ };
+
+typedef struct _TestState
+{
+ CoglTexture2D *tex;
+} TestState;
+
+static CoglTexture2D *
+create_source (TestState *state)
+{
+ int dx, dy;
+ uint8_t *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4);
+ CoglTexture2D *tex;
+
+ /* Create a texture with a different coloured rectangle at each
+ corner */
+ for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++)
+ for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++)
+ {
+ uint8_t *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 +
+ dx * DIVISION_WIDTH * 4);
+ int x, y;
+
+ for (y = 0; y < DIVISION_HEIGHT; y++)
+ {
+ for (x = 0; x < DIVISION_WIDTH; x++)
+ {
+ uint32_t color = GUINT32_FROM_BE (corner_colors[dx + dy * SOURCE_DIVISIONS_X]);
+ memcpy (p, &color, 4);
+ p += 4;
+ }
+
+ p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4;
+ }
+ }
+
+ tex = cogl_texture_2d_new_from_data (test_ctx,
+ SOURCE_SIZE, SOURCE_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ SOURCE_SIZE * 4,
+ data,
+ NULL);
+ return tex;
+}
+
+static CoglTexture2D *
+create_test_texture (TestState *state)
+{
+ CoglTexture2D *tex;
+ uint8_t *data = g_malloc (256 * 256 * 4), *p = data;
+ int x, y;
+
+ /* Create a texture that is 256x256 where the red component ranges
+ from 0->255 along the x axis and the green component ranges from
+ 0->255 along the y axis. The blue and alpha components are all
+ 255 */
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++)
+ {
+ *(p++) = x;
+ *(p++) = y;
+ *(p++) = 255;
+ *(p++) = 255;
+ }
+
+ tex = cogl_texture_2d_new_from_data (test_ctx,
+ 256, 256,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 256 * 4,
+ data,
+ NULL);
+ g_free (data);
+
+ return tex;
+}
+
+static void
+paint (TestState *state)
+{
+ CoglTexture2D *full_texture;
+ CoglSubTexture *sub_texture, *sub_sub_texture;
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+
+ /* Create a sub texture of the bottom right quarter of the texture */
+ sub_texture = cogl_sub_texture_new (test_ctx,
+ state->tex,
+ DIVISION_WIDTH,
+ DIVISION_HEIGHT,
+ DIVISION_WIDTH,
+ DIVISION_HEIGHT);
+
+ /* Paint it */
+ cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture);
+ cogl_object_unref (sub_texture);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline,
+ 0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT);
+
+
+ /* Repeat a sub texture of the top half of the full texture. This is
+ documented to be undefined so it doesn't technically have to work
+ but it will with the current implementation */
+ sub_texture = cogl_sub_texture_new (test_ctx,
+ state->tex,
+ 0, 0,
+ SOURCE_SIZE,
+ DIVISION_HEIGHT);
+ cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture);
+ cogl_object_unref (sub_texture);
+ cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline,
+ 0.0f,
+ SOURCE_SIZE,
+ SOURCE_SIZE * 2.0f,
+ SOURCE_SIZE * 1.5f,
+ 0.0f, 0.0f,
+ 2.0f, 1.0f);
+
+ /* Create a sub texture of a sub texture */
+ full_texture = create_test_texture (state);
+ sub_texture = cogl_sub_texture_new (test_ctx,
+ full_texture,
+ 20, 10, 30, 20);
+ cogl_object_unref (full_texture);
+ sub_sub_texture = cogl_sub_texture_new (test_ctx,
+ sub_texture,
+ 20, 10, 10, 10);
+ cogl_object_unref (sub_texture);
+ cogl_pipeline_set_layer_texture (pipeline, 0, sub_sub_texture);
+ cogl_object_unref (sub_sub_texture);
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline,
+ 0.0f, SOURCE_SIZE * 2.0f,
+ 10.0f, SOURCE_SIZE * 2.0f + 10.0f);
+
+ cogl_object_unref (pipeline);
+}
+
+static void
+validate_part (int xpos, int ypos,
+ int width, int height,
+ uint32_t color)
+{
+ test_utils_check_region (test_fb,
+ xpos + TEST_INSET,
+ ypos + TEST_INSET,
+ width - TEST_INSET - 2,
+ height - TEST_INSET - 2,
+ color);
+}
+
+static uint8_t *
+create_update_data (void)
+{
+ uint8_t *data = g_malloc (256 * 256 * 4), *p = data;
+ int x, y;
+
+ /* Create some image data that is 256x256 where the blue component
+ ranges from 0->255 along the x axis and the alpha component
+ ranges from 0->255 along the y axis. The red and green components
+ are all zero */
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++)
+ {
+ *(p++) = 0;
+ *(p++) = 0;
+ *(p++) = x;
+ *(p++) = y;
+ }
+
+ return data;
+}
+
+static void
+validate_result (TestState *state)
+{
+ int i, division_num, x, y;
+ CoglTexture2D *test_tex;
+ CoglSubTexture *sub_texture;
+ uint8_t *texture_data, *p;
+ int tex_width, tex_height;
+
+ /* Sub texture of the bottom right corner of the texture */
+ validate_part (0, 0, DIVISION_WIDTH, DIVISION_HEIGHT,
+ corner_colors[
+ (SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X +
+ SOURCE_DIVISIONS_X - 1]);
+
+ /* Sub texture of the top half repeated horizontally */
+ for (i = 0; i < 2; i++)
+ for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++)
+ validate_part (i * SOURCE_SIZE + division_num * DIVISION_WIDTH,
+ SOURCE_SIZE,
+ DIVISION_WIDTH, DIVISION_HEIGHT,
+ corner_colors[division_num]);
+
+ /* Sub sub texture */
+ p = texture_data = g_malloc (10 * 10 * 4);
+ cogl_flush ();
+ cogl_framebuffer_read_pixels (test_fb,
+ 0, SOURCE_SIZE * 2, 10, 10,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ p);
+ for (y = 0; y < 10; y++)
+ for (x = 0; x < 10; x++)
+ {
+ g_assert (*(p++) == x + 40);
+ g_assert (*(p++) == y + 20);
+ p += 2;
+ }
+ g_free (texture_data);
+
+ /* Try reading back the texture data */
+ sub_texture = cogl_sub_texture_new (test_ctx,
+ state->tex,
+ SOURCE_SIZE / 4,
+ SOURCE_SIZE / 4,
+ SOURCE_SIZE / 2,
+ SOURCE_SIZE / 2);
+ tex_width = cogl_texture_get_width (sub_texture);
+ tex_height = cogl_texture_get_height (sub_texture);
+ p = texture_data = g_malloc (tex_width * tex_height * 4);
+ cogl_texture_get_data (sub_texture,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ tex_width * 4,
+ texture_data);
+ for (y = 0; y < tex_height; y++)
+ for (x = 0; x < tex_width; x++)
+ {
+ int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) /
+ DIVISION_WIDTH);
+ int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) /
+ DIVISION_HEIGHT);
+ uint32_t reference = corner_colors[div_x + div_y * SOURCE_DIVISIONS_X] >> 8;
+ uint32_t color = GUINT32_FROM_BE (*((uint32_t *)p)) >> 8;
+ g_assert (color == reference);
+ p += 4;
+ }
+ g_free (texture_data);
+ cogl_object_unref (sub_texture);
+
+ /* Create a 256x256 test texture */
+ test_tex = create_test_texture (state);
+ /* Create a sub texture the views the center half of the texture */
+ sub_texture = cogl_sub_texture_new (test_ctx,
+ test_tex,
+ 64, 64, 128, 128);
+ /* Update the center half of the sub texture */
+ texture_data = create_update_data ();
+ cogl_texture_set_region (sub_texture,
+ 0, 0, 32, 32, 64, 64, 256, 256,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4,
+ texture_data);
+ g_free (texture_data);
+ cogl_object_unref (sub_texture);
+ /* Get the texture data */
+ p = texture_data = g_malloc (256 * 256 * 4);
+ cogl_texture_get_data (test_tex,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 256 * 4, texture_data);
+
+ /* Verify the texture data */
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++)
+ {
+ /* If we're in the center quarter */
+ if (x >= 96 && x < 160 && y >= 96 && y < 160)
+ {
+ g_assert ((*p++) == 0);
+ g_assert ((*p++) == 0);
+ g_assert ((*p++) == x - 96);
+ g_assert ((*p++) == y - 96);
+ }
+ else
+ {
+ g_assert ((*p++) == x);
+ g_assert ((*p++) == y);
+ g_assert ((*p++) == 255);
+ g_assert ((*p++) == 255);
+ }
+ }
+ g_free (texture_data);
+ cogl_object_unref (test_tex);
+}
+
+void
+test_sub_texture (void)
+{
+ TestState state;
+
+ state.tex = create_source (&state);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ cogl_framebuffer_get_width (test_fb),
+ cogl_framebuffer_get_height (test_fb),
+ -1,
+ 100);
+
+ paint (&state);
+ validate_result (&state);
+
+ cogl_object_unref (state.tex);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-texture-3d.c b/cogl/tests/conform/test-texture-3d.c
new file mode 100644
index 000000000..8cc5bb595
--- /dev/null
+++ b/cogl/tests/conform/test-texture-3d.c
@@ -0,0 +1,274 @@
+#include <cogl/cogl2-experimental.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+#define TEX_WIDTH 4
+#define TEX_HEIGHT 8
+#define TEX_DEPTH 16
+/* Leave four bytes of padding between each row */
+#define TEX_ROWSTRIDE (TEX_WIDTH * 4 + 4)
+/* Leave four rows of padding between each image */
+#define TEX_IMAGE_STRIDE ((TEX_HEIGHT + 4) * TEX_ROWSTRIDE)
+
+typedef struct _TestState
+{
+ int fb_width;
+ int fb_height;
+} TestState;
+
+static CoglTexture3D *
+create_texture_3d (CoglContext *context)
+{
+ int x, y, z;
+ uint8_t *data = g_malloc (TEX_IMAGE_STRIDE * TEX_DEPTH);
+ uint8_t *p = data;
+ CoglTexture3D *tex;
+ CoglError *error = NULL;
+
+ for (z = 0; z < TEX_DEPTH; z++)
+ {
+ for (y = 0; y < TEX_HEIGHT; y++)
+ {
+ for (x = 0; x < TEX_WIDTH; x++)
+ {
+ /* Set red, green, blue to values based on x, y, z */
+ *(p++) = 255 - x * 8;
+ *(p++) = y * 8;
+ *(p++) = 255 - z * 8;
+ /* Fully opaque */
+ *(p++) = 0xff;
+ }
+
+ /* Set the padding between rows to 0xde */
+ memset (p, 0xde, TEX_ROWSTRIDE - (TEX_WIDTH * 4));
+ p += TEX_ROWSTRIDE - (TEX_WIDTH * 4);
+ }
+ /* Set the padding between images to 0xad */
+ memset (p, 0xba, TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE));
+ p += TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE);
+ }
+
+ tex = cogl_texture_3d_new_from_data (context,
+ TEX_WIDTH, TEX_HEIGHT, TEX_DEPTH,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ TEX_ROWSTRIDE,
+ TEX_IMAGE_STRIDE,
+ data,
+ &error);
+
+ if (tex == NULL)
+ {
+ g_assert (error != NULL);
+ g_warning ("Failed to create 3D texture: %s", error->message);
+ g_assert_not_reached ();
+ }
+
+ g_free (data);
+
+ return tex;
+}
+
+static void
+draw_frame (TestState *state)
+{
+ CoglTexture *tex = create_texture_3d (test_ctx);
+ CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+ typedef struct { float x, y, s, t, r; } Vert;
+ CoglPrimitive *primitive;
+ CoglAttributeBuffer *attribute_buffer;
+ CoglAttribute *attributes[2];
+ Vert *verts, *v;
+ int i;
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_object_unref (tex);
+ cogl_pipeline_set_layer_filters (pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ /* Render the texture repeated horizontally twice using a regular
+ cogl rectangle. This should end up with the r texture coordinates
+ as zero */
+ cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline,
+ 0.0f, 0.0f, TEX_WIDTH * 2, TEX_HEIGHT,
+ 0.0f, 0.0f, 2.0f, 1.0f);
+
+ /* Render all of the images in the texture using coordinates from a
+ CoglPrimitive */
+ v = verts = g_new (Vert, 4 * TEX_DEPTH);
+ for (i = 0; i < TEX_DEPTH; i++)
+ {
+ float r = (i + 0.5f) / TEX_DEPTH;
+
+ v->x = i * TEX_WIDTH;
+ v->y = TEX_HEIGHT;
+ v->s = 0;
+ v->t = 0;
+ v->r = r;
+ v++;
+
+ v->x = i * TEX_WIDTH;
+ v->y = TEX_HEIGHT * 2;
+ v->s = 0;
+ v->t = 1;
+ v->r = r;
+ v++;
+
+ v->x = i * TEX_WIDTH + TEX_WIDTH;
+ v->y = TEX_HEIGHT * 2;
+ v->s = 1;
+ v->t = 1;
+ v->r = r;
+ v++;
+
+ v->x = i * TEX_WIDTH + TEX_WIDTH;
+ v->y = TEX_HEIGHT;
+ v->s = 1;
+ v->t = 0;
+ v->r = r;
+ v++;
+ }
+
+ attribute_buffer = cogl_attribute_buffer_new (test_ctx,
+ 4 * TEX_DEPTH * sizeof (Vert),
+ verts);
+ attributes[0] = cogl_attribute_new (attribute_buffer,
+ "cogl_position_in",
+ sizeof (Vert),
+ G_STRUCT_OFFSET (Vert, x),
+ 2, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ attributes[1] = cogl_attribute_new (attribute_buffer,
+ "cogl_tex_coord_in",
+ sizeof (Vert),
+ G_STRUCT_OFFSET (Vert, s),
+ 3, /* n_components */
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+ 6 * TEX_DEPTH,
+ attributes,
+ 2 /* n_attributes */);
+
+ cogl_primitive_set_indices (primitive,
+ cogl_get_rectangle_indices (test_ctx,
+ TEX_DEPTH),
+ 6 * TEX_DEPTH);
+
+ cogl_primitive_draw (primitive, test_fb, pipeline);
+
+ g_free (verts);
+
+ cogl_object_unref (primitive);
+ cogl_object_unref (attributes[0]);
+ cogl_object_unref (attributes[1]);
+ cogl_object_unref (attribute_buffer);
+ cogl_object_unref (pipeline);
+}
+
+static void
+validate_block (int block_x, int block_y, int z)
+{
+ int x, y;
+
+ for (y = 0; y < TEX_HEIGHT; y++)
+ for (x = 0; x < TEX_WIDTH; x++)
+ test_utils_check_pixel_rgb (test_fb,
+ block_x * TEX_WIDTH + x,
+ block_y * TEX_HEIGHT + y,
+ 255 - x * 8,
+ y * 8,
+ 255 - z * 8);
+}
+
+static void
+validate_result (void)
+{
+ int i;
+
+ validate_block (0, 0, 0);
+
+ for (i = 0; i < TEX_DEPTH; i++)
+ validate_block (i, 1, i);
+}
+
+static void
+test_multi_texture (TestState *state)
+{
+ CoglPipeline *pipeline;
+ CoglTexture3D *tex_3d;
+ CoglTexture2D *tex_2d;
+ uint8_t tex_data[4];
+
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+
+ /* Tests a pipeline that is using multi-texturing to combine a 3D
+ texture with a 2D texture. The texture from another layer is
+ sampled with TEXTURE_? just to pick up a specific bug that was
+ happening with the ARBfp fragend */
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ tex_data[0] = 0xff;
+ tex_data[1] = 0x00;
+ tex_data[2] = 0x00;
+ tex_data[3] = 0xff;
+ tex_2d = cogl_texture_2d_new_from_data (test_ctx,
+ 1, 1, /* width/height */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 4, /* rowstride */
+ tex_data,
+ NULL);
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex_2d);
+
+ tex_data[0] = 0x00;
+ tex_data[1] = 0xff;
+ tex_data[2] = 0x00;
+ tex_data[3] = 0xff;
+ tex_3d = cogl_texture_3d_new_from_data (test_ctx,
+ 1, 1, 1, /* width/height/depth */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 4, /* rowstride */
+ 4, /* image_stride */
+ tex_data,
+ NULL);
+ cogl_pipeline_set_layer_texture (pipeline, 1, tex_3d);
+
+ cogl_pipeline_set_layer_combine (pipeline, 0,
+ "RGBA = REPLACE(PREVIOUS)",
+ NULL);
+ cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGBA = ADD(TEXTURE_0, TEXTURE_1)",
+ NULL);
+
+ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10);
+
+ test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff);
+
+ cogl_object_unref (tex_2d);
+ cogl_object_unref (tex_3d);
+ cogl_object_unref (pipeline);
+}
+
+void
+test_texture_3d (void)
+{
+ TestState state;
+
+ state.fb_width = cogl_framebuffer_get_width (test_fb);
+ state.fb_height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, /* x_1, y_1 */
+ state.fb_width, /* x_2 */
+ state.fb_height /* y_2 */,
+ -1, 100 /* near/far */);
+
+ draw_frame (&state);
+ validate_result ();
+
+ test_multi_texture (&state);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-texture-get-set-data.c b/cogl/tests/conform/test-texture-get-set-data.c
new file mode 100644
index 000000000..59bd0f635
--- /dev/null
+++ b/cogl/tests/conform/test-texture-get-set-data.c
@@ -0,0 +1,144 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+static void
+check_texture (int width, int height, TestUtilsTextureFlags flags)
+{
+ CoglTexture *tex;
+ uint8_t *data, *p;
+ int y, x;
+ int rowstride;
+ CoglBitmap *bmp;
+
+ p = data = g_malloc (width * height * 4);
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ *(p++) = x;
+ *(p++) = y;
+ *(p++) = 128;
+ *(p++) = (x ^ y);
+ }
+
+ bmp = cogl_bitmap_new_for_data (test_ctx,
+ width, height,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ width * 4,
+ data);
+
+ tex = test_utils_texture_new_from_bitmap (bmp, flags,
+ FALSE);
+
+ /* Replace the bottom right quarter of the data with negated data to
+ test set_region */
+ rowstride = width * 4;
+ p = data + (height / 2) * rowstride + rowstride / 2;
+ for (y = 0; y < height / 2; y++)
+ {
+ for (x = 0; x < width / 2; x++)
+ {
+ p[0] = ~p[0];
+ p[1] = ~p[1];
+ p[2] = ~p[2];
+ p[3] = ~p[3];
+ p += 4;
+ }
+ p += width * 2;
+ }
+ cogl_texture_set_region (tex,
+ width / 2,
+ height / 2,
+ width / 2, /* dest x */
+ height / 2, /* dest y */
+ width / 2, /* region width */
+ height / 2, /* region height */
+ width, /* src width */
+ height, /* src height */
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ rowstride,
+ data);
+
+ /* Check passing a NULL pointer and a zero rowstride. The texture
+ should calculate the needed data size and return it */
+ g_assert_cmpint (cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_ANY, 0, NULL),
+ ==,
+ width * height * 4);
+
+ /* Try first receiving the data as RGB. This should cause a
+ * conversion */
+ memset (data, 0, width * height * 4);
+
+ cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGB_888,
+ width * 3, data);
+
+ p = data;
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ if (x >= width / 2 && y >= height / 2)
+ {
+ g_assert_cmpint (p[0], ==, ~x & 0xff);
+ g_assert_cmpint (p[1], ==, ~y & 0xff);
+ g_assert_cmpint (p[2], ==, ~128 & 0xff);
+ }
+ else
+ {
+ g_assert_cmpint (p[0], ==, x & 0xff);
+ g_assert_cmpint (p[1], ==, y & 0xff);
+ g_assert_cmpint (p[2], ==, 128);
+ }
+ p += 3;
+ }
+
+ /* Now try receiving the data as RGBA. This should not cause a
+ * conversion and no unpremultiplication because we explicitly set
+ * the internal format when we created the texture */
+ memset (data, 0, width * height * 4);
+
+ cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGBA_8888,
+ width * 4, data);
+
+ p = data;
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ if (x >= width / 2 && y >= height / 2)
+ {
+ g_assert_cmpint (p[0], ==, ~x & 0xff);
+ g_assert_cmpint (p[1], ==, ~y & 0xff);
+ g_assert_cmpint (p[2], ==, ~128 & 0xff);
+ g_assert_cmpint (p[3], ==, ~(x ^ y) & 0xff);
+ }
+ else
+ {
+ g_assert_cmpint (p[0], ==, x & 0xff);
+ g_assert_cmpint (p[1], ==, y & 0xff);
+ g_assert_cmpint (p[2], ==, 128);
+ g_assert_cmpint (p[3], ==, (x ^ y) & 0xff);
+ }
+ p += 4;
+ }
+
+ cogl_object_unref (tex);
+ g_free (data);
+}
+
+void
+test_texture_get_set_data (void)
+{
+ /* First try without atlasing */
+ check_texture (256, 256, TEST_UTILS_TEXTURE_NO_ATLAS);
+ /* Try again with atlasing. This should end up testing the atlas
+ backend and the sub texture backend */
+ check_texture (256, 256, 0);
+ /* Try with a really big texture in the hope that it will end up
+ sliced. */
+ check_texture (4, 5128, TEST_UTILS_TEXTURE_NO_ATLAS);
+ /* And in the other direction. */
+ check_texture (5128, 4, TEST_UTILS_TEXTURE_NO_ATLAS);
+}
diff --git a/cogl/tests/conform/test-texture-mipmaps.c b/cogl/tests/conform/test-texture-mipmaps.c
new file mode 100644
index 000000000..3118ba86d
--- /dev/null
+++ b/cogl/tests/conform/test-texture-mipmaps.c
@@ -0,0 +1,136 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+#define TEX_SIZE 64
+
+typedef struct _TestState
+{
+ unsigned int padding;
+} TestState;
+
+/* Creates a texture where the pixels are evenly divided between
+ selecting just one of the R,G and B components */
+static CoglHandle
+make_texture (void)
+{
+ guchar *tex_data = g_malloc (TEX_SIZE * TEX_SIZE * 3), *p = tex_data;
+ CoglHandle tex;
+ int x, y;
+
+ for (y = 0; y < TEX_SIZE; y++)
+ for (x = 0; x < TEX_SIZE; x++)
+ {
+ memset (p, 0, 3);
+ /* Set one of the components to full. The components should be
+ evenly represented so that each gets a third of the
+ texture */
+ p[(p - tex_data) / (TEX_SIZE * TEX_SIZE * 3 / 3)] = 255;
+ p += 3;
+ }
+
+ tex = test_utils_texture_new_from_data (TEX_SIZE, TEX_SIZE, TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGB_888,
+ COGL_PIXEL_FORMAT_ANY,
+ TEX_SIZE * 3,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglHandle tex;
+ CoglHandle material;
+ uint8_t pixels[8];
+
+ tex = make_texture ();
+ material = cogl_material_new ();
+ cogl_material_set_layer (material, 0, tex);
+ cogl_handle_unref (tex);
+
+ /* Render a 1x1 pixel quad without mipmaps */
+ cogl_set_source (material);
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_rectangle (0, 0, 1, 1);
+ /* Then with mipmaps */
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_rectangle (1, 0, 2, 1);
+
+ cogl_handle_unref (material);
+
+ /* Read back the two pixels we rendered */
+ cogl_read_pixels (0, 0, 2, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixels);
+
+ /* The first pixel should be just one of the colors from the
+ texture. It doesn't matter which one */
+ g_assert ((pixels[0] == 255 && pixels[1] == 0 && pixels[2] == 0) ||
+ (pixels[0] == 0 && pixels[1] == 255 && pixels[2] == 0) ||
+ (pixels[0] == 0 && pixels[1] == 0 && pixels[2] == 255));
+ /* The second pixel should be more or less the average of all of the
+ pixels in the texture. Each component gets a third of the image
+ so each component should be approximately 255/3 */
+ g_assert (ABS (pixels[4] - 255 / 3) <= 3 &&
+ ABS (pixels[5] - 255 / 3) <= 3 &&
+ ABS (pixels[6] - 255 / 3) <= 3);
+
+ /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+ clutter_main_quit ();
+#endif
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_texture_mipmaps (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ group = clutter_group_new ();
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing of the stage, since we need to skip
+ * the first few frames, and we wont be doing anything else that
+ * will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-texture-no-allocate.c b/cogl/tests/conform/test-texture-no-allocate.c
new file mode 100644
index 000000000..b0199a988
--- /dev/null
+++ b/cogl/tests/conform/test-texture-no-allocate.c
@@ -0,0 +1,80 @@
+#include <cogl/cogl.h>
+
+#include "test-utils.h"
+
+/* Tests that the various texture types can be freed without being
+ * allocated */
+
+/* Texture size that is probably to big to fit within the texture
+ * limits */
+#define BIG_TEX_WIDTH 16384
+#define BIG_TEX_HEIGHT 128
+
+void
+test_texture_no_allocate (void)
+{
+ uint8_t *tex_data;
+ CoglTexture *texture;
+ CoglTexture2D *texture_2d;
+ GError *error = NULL;
+
+ tex_data = g_malloc (BIG_TEX_WIDTH * BIG_TEX_HEIGHT * 4);
+
+ /* NB: if we make the atlas and sliced texture APIs public then this
+ * could changed to explicitly use that instead of the magic texture
+ * API */
+
+ /* Try to create an atlas texture that is too big so it will
+ * internally be freed without allocating */
+ texture =
+ cogl_atlas_texture_new_from_data (test_ctx,
+ BIG_TEX_WIDTH,
+ BIG_TEX_HEIGHT,
+ /* format */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ /* rowstride */
+ BIG_TEX_WIDTH * 4,
+ tex_data,
+ &error);
+
+ g_free (tex_data);
+
+ /* It's ok if this causes an error, we just don't want it to
+ * crash */
+
+ if (texture == NULL)
+ cogl_error_free (error);
+ else
+ cogl_object_unref (texture);
+
+ /* Try to create a sliced texture without allocating it */
+ texture =
+ cogl_texture_2d_sliced_new_with_size (test_ctx,
+ BIG_TEX_WIDTH,
+ BIG_TEX_HEIGHT,
+ COGL_TEXTURE_MAX_WASTE);
+ cogl_object_unref (texture);
+
+ /* 2D texture */
+ texture_2d = cogl_texture_2d_new_with_size (test_ctx,
+ 64, 64);
+ cogl_object_unref (texture_2d);
+
+ /* 3D texture */
+ if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_3D))
+ {
+ CoglTexture3D *texture_3d =
+ cogl_texture_3d_new_with_size (test_ctx,
+ 64, 64, 64);
+ cogl_object_unref (texture_3d);
+ }
+
+ /* Rectangle texture */
+ if (cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE))
+ {
+ CoglTextureRectangle *texture_rect =
+ cogl_texture_rectangle_new_with_size (test_ctx,
+ 64, 64);
+ cogl_object_unref (texture_rect);
+ }
+}
diff --git a/cogl/tests/conform/test-texture-pixmap-x11.c b/cogl/tests/conform/test-texture-pixmap-x11.c
new file mode 100644
index 000000000..4e8d55059
--- /dev/null
+++ b/cogl/tests/conform/test-texture-pixmap-x11.c
@@ -0,0 +1,245 @@
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+#ifdef COGL_HAS_XLIB
+
+#include <clutter/x11/clutter-x11.h>
+#include <cogl/cogl-texture-pixmap-x11.h>
+
+#define PIXMAP_WIDTH 512
+#define PIXMAP_HEIGHT 256
+#define GRID_SQUARE_SIZE 16
+
+/* Coordinates of a square that we'll update */
+#define PIXMAP_CHANGE_X 1
+#define PIXMAP_CHANGE_Y 1
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+ CoglHandle tfp;
+ Pixmap pixmap;
+ unsigned int frame_count;
+ Display *display;
+} TestState;
+
+static Pixmap
+create_pixmap (TestState *state)
+{
+ Pixmap pixmap;
+ XGCValues gc_values = { 0, };
+ GC black_gc, white_gc;
+ int screen = DefaultScreen (state->display);
+ int x, y;
+
+ pixmap = XCreatePixmap (state->display,
+ DefaultRootWindow (state->display),
+ PIXMAP_WIDTH, PIXMAP_HEIGHT,
+ DefaultDepth (state->display, screen));
+
+ gc_values.foreground = BlackPixel (state->display, screen);
+ black_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values);
+ gc_values.foreground = WhitePixel (state->display, screen);
+ white_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values);
+
+ /* Draw a grid of alternative black and white rectangles to the
+ pixmap */
+ for (y = 0; y < PIXMAP_HEIGHT / GRID_SQUARE_SIZE; y++)
+ for (x = 0; x < PIXMAP_WIDTH / GRID_SQUARE_SIZE; x++)
+ XFillRectangle (state->display, pixmap,
+ ((x ^ y) & 1) ? black_gc : white_gc,
+ x * GRID_SQUARE_SIZE,
+ y * GRID_SQUARE_SIZE,
+ GRID_SQUARE_SIZE,
+ GRID_SQUARE_SIZE);
+
+ XFreeGC (state->display, black_gc);
+ XFreeGC (state->display, white_gc);
+
+ return pixmap;
+}
+
+static void
+update_pixmap (TestState *state)
+{
+ XGCValues gc_values = { 0, };
+ GC black_gc;
+ int screen = DefaultScreen (state->display);
+
+ gc_values.foreground = BlackPixel (state->display, screen);
+ black_gc = XCreateGC (state->display, state->pixmap,
+ GCForeground, &gc_values);
+
+ /* Fill in one the rectangles with black */
+ XFillRectangle (state->display, state->pixmap,
+ black_gc,
+ PIXMAP_CHANGE_X * GRID_SQUARE_SIZE,
+ PIXMAP_CHANGE_Y * GRID_SQUARE_SIZE,
+ GRID_SQUARE_SIZE, GRID_SQUARE_SIZE);
+
+ XFreeGC (state->display, black_gc);
+}
+
+static CoglBool
+check_paint (TestState *state, int x, int y, int scale)
+{
+ uint8_t *data, *p, update_value = 0;
+
+ p = data = g_malloc (PIXMAP_WIDTH * PIXMAP_HEIGHT * 4);
+
+ cogl_read_pixels (x, y, PIXMAP_WIDTH / scale, PIXMAP_HEIGHT / scale,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ data);
+
+ for (y = 0; y < PIXMAP_HEIGHT / scale; y++)
+ for (x = 0; x < PIXMAP_WIDTH / scale; x++)
+ {
+ int grid_x = x * scale / GRID_SQUARE_SIZE;
+ int grid_y = y * scale / GRID_SQUARE_SIZE;
+
+ /* If this is the updatable square then we'll let it be either
+ color but we'll return which one it was */
+ if (grid_x == PIXMAP_CHANGE_X && grid_y == PIXMAP_CHANGE_Y)
+ {
+ if (x % (GRID_SQUARE_SIZE / scale) == 0 &&
+ y % (GRID_SQUARE_SIZE / scale) == 0)
+ update_value = *p;
+ else
+ g_assert_cmpint (p[0], ==, update_value);
+
+ g_assert (p[1] == update_value);
+ g_assert (p[2] == update_value);
+ p += 4;
+ }
+ else
+ {
+ uint8_t value = ((grid_x ^ grid_y) & 1) ? 0x00 : 0xff;
+ g_assert_cmpint (*(p++), ==, value);
+ g_assert_cmpint (*(p++), ==, value);
+ g_assert_cmpint (*(p++), ==, value);
+ p++;
+ }
+ }
+
+ g_free (data);
+
+ return update_value == 0x00;
+}
+
+/* We skip these frames first */
+#define FRAME_COUNT_BASE 5
+/* First paint the tfp with no mipmaps */
+#define FRAME_COUNT_NORMAL 6
+/* Then use mipmaps */
+#define FRAME_COUNT_MIPMAP 7
+/* After this frame will start waiting for the pixmap to change */
+#define FRAME_COUNT_UPDATED 8
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglHandle material;
+
+ material = cogl_material_new ();
+ cogl_material_set_layer (material, 0, state->tfp);
+ if (state->frame_count == FRAME_COUNT_MIPMAP)
+ {
+ const CoglMaterialFilter min_filter =
+ COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST;
+ cogl_material_set_layer_filters (material, 0,
+ min_filter,
+ COGL_MATERIAL_FILTER_NEAREST);
+ }
+ else
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_set_source (material);
+
+ cogl_rectangle (0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
+
+ cogl_rectangle (0, PIXMAP_HEIGHT,
+ PIXMAP_WIDTH / 4, PIXMAP_HEIGHT * 5 / 4);
+
+ if (state->frame_count >= 5)
+ {
+ CoglBool big_updated, small_updated;
+
+ big_updated = check_paint (state, 0, 0, 1);
+ small_updated = check_paint (state, 0, PIXMAP_HEIGHT, 4);
+
+ g_assert (big_updated == small_updated);
+
+ if (state->frame_count < FRAME_COUNT_UPDATED)
+ g_assert (big_updated == FALSE);
+ else if (state->frame_count == FRAME_COUNT_UPDATED)
+ /* Change the pixmap and keep drawing until it updates */
+ update_pixmap (state);
+ else if (big_updated)
+ /* If we successfully got the update then the test is over */
+ clutter_main_quit ();
+ }
+
+ state->frame_count++;
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+#endif /* COGL_HAS_XLIB */
+
+void
+test_texture_pixmap_x11 (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+#ifdef COGL_HAS_XLIB
+
+ TestState state;
+ unsigned int idle_handler;
+ unsigned int paint_handler;
+
+ state.frame_count = 0;
+ state.stage = clutter_stage_get_default ();
+
+ state.display = clutter_x11_get_default_display ();
+
+ state.pixmap = create_pixmap (&state);
+ state.tfp = cogl_texture_pixmap_x11_new (state.pixmap, TRUE);
+
+ clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+ paint_handler = g_signal_connect_after (state.stage, "paint",
+ G_CALLBACK (on_paint), &state);
+
+ idle_handler = g_idle_add (queue_redraw, state.stage);
+
+ clutter_actor_show_all (state.stage);
+
+ clutter_main ();
+
+ g_signal_handler_disconnect (state.stage, paint_handler);
+
+ g_source_remove (idle_handler);
+
+ XFreePixmap (state.display, state.pixmap);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+
+#else /* COGL_HAS_XLIB */
+
+ if (cogl_test_verbose ())
+ g_print ("Skipping\n");
+
+#endif /* COGL_HAS_XLIB */
+}
+
diff --git a/cogl/tests/conform/test-texture-rectangle.c b/cogl/tests/conform/test-texture-rectangle.c
new file mode 100644
index 000000000..3784cdcfe
--- /dev/null
+++ b/cogl/tests/conform/test-texture-rectangle.c
@@ -0,0 +1,276 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+} TestState;
+
+static CoglHandle
+create_source_rect (void)
+{
+#ifdef GL_TEXTURE_RECTANGLE_ARB
+
+ int x, y;
+ GLint prev_unpack_row_length;
+ GLint prev_unpack_alignment;
+ GLint prev_unpack_skip_rows;
+ GLint prev_unpack_skip_pixles;
+ GLint prev_rectangle_binding;
+ uint8_t *data = g_malloc (256 * 256 * 4), *p = data;
+ CoglHandle tex;
+ GLuint gl_tex;
+
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++)
+ {
+ *(p++) = x;
+ *(p++) = y;
+ *(p++) = 0;
+ *(p++) = 255;
+ }
+
+ /* We are about to use OpenGL directly to create a TEXTURE_RECTANGLE
+ * texture so we need to save the state that we modify so we can
+ * restore it afterwards and be sure not to interfere with any state
+ * caching that Cogl may do internally.
+ */
+ glGetIntegerv (GL_UNPACK_ROW_LENGTH, &prev_unpack_row_length);
+ glGetIntegerv (GL_UNPACK_ALIGNMENT, &prev_unpack_alignment);
+ glGetIntegerv (GL_UNPACK_SKIP_ROWS, &prev_unpack_skip_rows);
+ glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &prev_unpack_skip_pixles);
+ glGetIntegerv (GL_TEXTURE_BINDING_RECTANGLE_ARB, &prev_rectangle_binding);
+
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 256);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+
+ glGenTextures (1, &gl_tex);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, gl_tex);
+ glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0,
+ GL_RGBA, 256, 256, 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ data);
+
+ /* Now restore the original GL state as Cogl had left it */
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, prev_unpack_row_length);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, prev_unpack_alignment);
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, prev_unpack_skip_rows);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, prev_unpack_skip_pixles);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, prev_rectangle_binding);
+
+ g_assert (glGetError () == GL_NO_ERROR);
+
+ g_free (data);
+
+ tex = test_utils_texture_new_from_foreign (gl_tex,
+ GL_TEXTURE_RECTANGLE_ARB,
+ 256, 256, 0, 0,
+ COGL_PIXEL_FORMAT_RGBA_8888);
+
+ return tex;
+
+#else /* GL_TEXTURE_RECTANGLE_ARB */
+
+ return NULL;
+
+#endif /* GL_TEXTURE_RECTANGLE_ARB */
+}
+
+static CoglHandle
+create_source_2d (void)
+{
+ int x, y;
+ uint8_t *data = g_malloc (256 * 256 * 4), *p = data;
+ CoglHandle tex;
+
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++)
+ {
+ *(p++) = 0;
+ *(p++) = x;
+ *(p++) = y;
+ *(p++) = 255;
+ }
+
+ tex = test_utils_texture_new_from_data (256, 256, TEST_UTILS_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ 256 * 4,
+ data);
+
+ g_free (data);
+
+ return tex;
+}
+
+static void
+draw_frame (TestState *state)
+{
+ GLuint gl_tex;
+ CoglHandle tex_rect = create_source_rect ();
+ CoglHandle material_rect = cogl_material_new ();
+ CoglHandle tex_2d = create_source_2d ();
+ CoglHandle material_2d = cogl_material_new ();
+
+ g_assert (tex_rect != NULL);
+
+ cogl_material_set_layer (material_rect, 0, tex_rect);
+ cogl_material_set_layer_filters (material_rect, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+
+ cogl_material_set_layer (material_2d, 0, tex_2d);
+ cogl_material_set_layer_filters (material_2d, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+
+ cogl_set_source (material_rect);
+
+ /* Render the texture repeated horizontally twice */
+ cogl_rectangle_with_texture_coords (0.0f, 0.0f, 512.0f, 256.0f,
+ 0.0f, 0.0f, 2.0f, 1.0f);
+ /* Render the top half of the texture to test without repeating */
+ cogl_rectangle_with_texture_coords (0.0f, 256.0f, 256.0f, 384.0f,
+ 0.0f, 0.0f, 1.0f, 0.5f);
+
+ cogl_set_source (material_2d);
+
+ /* Render the top half of a regular 2D texture */
+ cogl_rectangle_with_texture_coords (256.0f, 256.0f, 512.0f, 384.0f,
+ 0.0f, 0.0f, 1.0f, 0.5f);
+
+ /* Flush the rendering now so we can safely delete the texture */
+ cogl_flush ();
+
+ cogl_handle_unref (material_rect);
+
+ /* Cogl doesn't destroy foreign textures so we have to do it manually */
+ cogl_texture_get_gl_texture (tex_rect, &gl_tex, NULL);
+ glDeleteTextures (1, &gl_tex);
+ cogl_handle_unref (tex_rect);
+}
+
+static void
+validate_result (TestState *state)
+{
+ uint8_t *data, *p;
+ int x, y;
+
+ p = data = g_malloc (512 * 384 * 4);
+
+ cogl_read_pixels (0, 0, 512, 384,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ data);
+
+ for (y = 0; y < 384; y++)
+ for (x = 0; x < 512; x++)
+ {
+ if (x >= 256 && y >= 256)
+ {
+ g_assert_cmpint (p[0], ==, 0);
+ g_assert_cmpint (p[1], ==, x & 0xff);
+ g_assert_cmpint (p[2], ==, y & 0xff);
+ }
+ else
+ {
+ g_assert_cmpint (p[0], ==, x & 0xff);
+ g_assert_cmpint (p[1], ==, y & 0xff);
+ g_assert_cmpint (p[2], ==, 0);
+ }
+ p += 4;
+ }
+
+ g_free (data);
+
+ /* Comment this out to see what the test paints */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ draw_frame (state);
+
+ validate_result (state);
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+static CoglBool
+check_rectangle_extension (void)
+{
+ static const char rect_extension[] = "GL_ARB_texture_rectangle";
+ const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
+ const char *extensions_end;
+
+ extensions_end = extensions + strlen (extensions);
+
+ while (extensions < extensions_end)
+ {
+ const char *end = strchr (extensions, ' ');
+
+ if (end == NULL)
+ end = extensions_end;
+
+ if (end - extensions == sizeof (rect_extension) - 1 &&
+ !memcmp (extensions, rect_extension, sizeof (rect_extension) - 1))
+ return TRUE;
+
+ extensions = end + 1;
+ }
+
+ return FALSE;
+}
+
+void
+test_texture_rectangle (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ unsigned int idle_source;
+ unsigned int paint_handler;
+
+ state.stage = clutter_stage_get_default ();
+
+ /* Check whether GL supports the rectangle extension. If not we'll
+ just assume the test passes */
+ if (check_rectangle_extension ())
+ {
+ clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+ /* We force continuous redrawing of the stage, since we need to skip
+ * the first few frames, and we wont be doing anything else that
+ * will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, state.stage);
+
+ paint_handler = g_signal_connect_after (state.stage, "paint",
+ G_CALLBACK (on_paint), &state);
+
+ clutter_actor_show_all (state.stage);
+
+ clutter_main ();
+
+ g_source_remove (idle_source);
+ g_signal_handler_disconnect (state.stage, paint_handler);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+ }
+ else if (cogl_test_verbose ())
+ g_print ("Skipping\n");
+}
+
diff --git a/cogl/tests/conform/test-texture-rg.c b/cogl/tests/conform/test-texture-rg.c
new file mode 100644
index 000000000..72a5ae930
--- /dev/null
+++ b/cogl/tests/conform/test-texture-rg.c
@@ -0,0 +1,74 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define TEX_WIDTH 8
+#define TEX_HEIGHT 8
+
+static CoglTexture2D *
+make_texture (void)
+{
+ uint8_t tex_data[TEX_WIDTH * TEX_HEIGHT * 2], *p = tex_data;
+ int x, y;
+
+ for (y = 0; y < TEX_HEIGHT; y++)
+ for (x = 0; x < TEX_WIDTH; x++)
+ {
+ *(p++) = x * 256 / TEX_WIDTH;
+ *(p++) = y * 256 / TEX_HEIGHT;
+ }
+
+ return cogl_texture_2d_new_from_data (test_ctx,
+ TEX_WIDTH, TEX_HEIGHT,
+ COGL_PIXEL_FORMAT_RG_88,
+ TEX_WIDTH * 2,
+ tex_data,
+ NULL);
+}
+
+void
+test_texture_rg (void)
+{
+ CoglPipeline *pipeline;
+ CoglTexture2D *tex;
+ int fb_width, fb_height;
+ int x, y;
+
+ fb_width = cogl_framebuffer_get_width (test_fb);
+ fb_height = cogl_framebuffer_get_height (test_fb);
+
+ tex = make_texture ();
+
+ g_assert (cogl_texture_get_components (tex) == COGL_TEXTURE_COMPONENTS_RG);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ cogl_framebuffer_draw_rectangle (test_fb,
+ pipeline,
+ -1.0f, 1.0f,
+ 1.0f, -1.0f);
+
+ for (y = 0; y < TEX_HEIGHT; y++)
+ for (x = 0; x < TEX_WIDTH; x++)
+ {
+ test_utils_check_pixel_rgb (test_fb,
+ x * fb_width / TEX_WIDTH +
+ fb_width / (TEX_WIDTH * 2),
+ y * fb_height / TEX_HEIGHT +
+ fb_height / (TEX_HEIGHT * 2),
+ x * 256 / TEX_WIDTH,
+ y * 256 / TEX_HEIGHT,
+ 0);
+ }
+
+ cogl_object_unref (pipeline);
+ cogl_object_unref (tex);
+}
diff --git a/cogl/tests/conform/test-version.c b/cogl/tests/conform/test-version.c
new file mode 100644
index 000000000..b651165e8
--- /dev/null
+++ b/cogl/tests/conform/test-version.c
@@ -0,0 +1,85 @@
+#include <cogl/cogl.h>
+
+/* These will be redefined in config.h */
+#undef COGL_ENABLE_EXPERIMENTAL_2_0_API
+#undef COGL_ENABLE_EXPERIMENTAL_API
+
+#include "test-utils.h"
+#include "config.h"
+
+/* So we can use _COGL_STATIC_ASSERT we include the internal
+ * cogl-util.h header. Since internal headers explicitly guard against
+ * applications including them directly instead of including
+ * <cogl/cogl.h> we define __COGL_H_INSIDE__ here to subvert those
+ * guards in this case... */
+#define __COGL_H_INSIDE__
+#include <cogl/cogl-util.h>
+#undef __COGL_H_INSIDE__
+
+_COGL_STATIC_ASSERT (COGL_VERSION_ENCODE (COGL_VERSION_MAJOR,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO) ==
+ COGL_VERSION,
+ "The pre-encoded Cogl version does not match the version "
+ "encoding macro");
+
+_COGL_STATIC_ASSERT (COGL_VERSION_GET_MAJOR (COGL_VERSION_ENCODE (100,
+ 200,
+ 300)) ==
+ 100,
+ "Getting the major component out of a encoded version "
+ "does not work");
+_COGL_STATIC_ASSERT (COGL_VERSION_GET_MINOR (COGL_VERSION_ENCODE (100,
+ 200,
+ 300)) ==
+ 200,
+ "Getting the minor component out of a encoded version "
+ "does not work");
+_COGL_STATIC_ASSERT (COGL_VERSION_GET_MICRO (COGL_VERSION_ENCODE (100,
+ 200,
+ 300)) ==
+ 300,
+ "Getting the micro component out of a encoded version "
+ "does not work");
+
+_COGL_STATIC_ASSERT (COGL_VERSION_CHECK (COGL_VERSION_MAJOR,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO),
+ "Checking the Cogl version against the current version "
+ "does not pass");
+_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO + 1),
+ "Checking the Cogl version against a later micro version "
+ "should not pass");
+_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR,
+ COGL_VERSION_MINOR + 1,
+ COGL_VERSION_MICRO),
+ "Checking the Cogl version against a later minor version "
+ "should not pass");
+_COGL_STATIC_ASSERT (!COGL_VERSION_CHECK (COGL_VERSION_MAJOR + 1,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO),
+ "Checking the Cogl version against a later major version "
+ "should not pass");
+
+_COGL_STATIC_ASSERT (COGL_VERSION_CHECK (COGL_VERSION_MAJOR - 1,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO),
+ "Checking the Cogl version against a older major version "
+ "should pass");
+
+void
+test_version (void)
+{
+ const char *version = g_strdup_printf ("version = %i.%i.%i",
+ COGL_VERSION_MAJOR,
+ COGL_VERSION_MINOR,
+ COGL_VERSION_MICRO);
+
+ g_assert_cmpstr (version, ==, "version = " COGL_VERSION_STRING);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-vertex-buffer-contiguous.c b/cogl/tests/conform/test-vertex-buffer-contiguous.c
new file mode 100644
index 000000000..1cd7b456b
--- /dev/null
+++ b/cogl/tests/conform/test-vertex-buffer-contiguous.c
@@ -0,0 +1,257 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that the simplest usage of the vertex buffer API,
+ * where we add contiguous (x,y) GLfloat vertices, and RGBA GLubyte color
+ * attributes to a buffer, submit, and draw.
+ *
+ * It also tries to verify that the enable/disable attribute APIs are working
+ * too.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+ CoglHandle buffer;
+ CoglHandle texture;
+ CoglHandle material;
+ ClutterGeometry stage_geom;
+} TestState;
+
+static void
+validate_result (TestState *state)
+{
+ GLubyte pixel[4];
+ GLint y_off = 90;
+
+ if (cogl_test_verbose ())
+ g_print ("y_off = %d\n", y_off);
+
+ /* NB: We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+ /* Should see a blue pixel */
+ cogl_read_pixels (10, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+ /* Should see a red pixel */
+ cogl_read_pixels (110, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0);
+
+ /* Should see a blue pixel */
+ cogl_read_pixels (210, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 2 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+ /* Should see a green pixel, at bottom of 4th triangle */
+ cogl_read_pixels (310, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 3 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[GREEN] > pixel[RED] && pixel[GREEN] > pixel[BLUE]);
+
+ /* Should see a red pixel, at top of 4th triangle */
+ cogl_read_pixels (310, y_off - 70, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 4 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] > pixel[GREEN] && pixel[RED] > pixel[BLUE]);
+
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ /* Draw a faded blue triangle */
+ cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue");
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ /* Draw a red triangle */
+ /* Here we are testing that the disable attribute works; if it doesn't
+ * the triangle will remain faded blue */
+ cogl_translate (100, 0, 0);
+ cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue");
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ /* Draw a faded blue triangle */
+ /* Here we are testing that the re-enable works; if it doesn't
+ * the triangle will remain red */
+ cogl_translate (100, 0, 0);
+ cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue");
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ /* Draw a textured triangle */
+ cogl_translate (100, 0, 0);
+ cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue");
+ cogl_set_source (state->material);
+ cogl_material_set_color4ub (state->material, 0xff, 0xff, 0xff, 0xff);
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ validate_result (state);
+}
+
+static CoglBool
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+
+
+void
+test_vertex_buffer_contiguous (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+ ClutterActor *group;
+ unsigned int idle_source;
+ guchar tex_data[] = {
+ 0xff, 0x00, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff
+ };
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+ clutter_actor_get_geometry (stage, &state.stage_geom);
+
+ group = clutter_group_new ();
+ clutter_actor_set_size (group,
+ state.stage_geom.width,
+ state.stage_geom.height);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing incase someone comments out the
+ * clutter_main_quit and wants visual feedback for the test since we
+ * wont be doing anything else that will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ state.texture = cogl_texture_new_from_data (2, 2,
+ COGL_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ 0, /* auto calc row stride */
+ tex_data);
+
+ state.material = cogl_material_new ();
+ cogl_material_set_color4ub (state.material, 0x00, 0xff, 0x00, 0xff);
+ cogl_material_set_layer (state.material, 0, state.texture);
+
+ {
+ GLfloat triangle_verts[3][2] =
+ {
+ {0.0, 0.0},
+ {100.0, 100.0},
+ {0.0, 100.0}
+ };
+ GLbyte triangle_colors[3][4] =
+ {
+ {0x00, 0x00, 0xff, 0xff}, /* blue */
+ {0x00, 0x00, 0xff, 0x00}, /* transparent blue */
+ {0x00, 0x00, 0xff, 0x00} /* transparent blue */
+ };
+ GLfloat triangle_tex_coords[3][2] =
+ {
+ {0.0, 0.0},
+ {1.0, 1.0},
+ {0.0, 1.0}
+ };
+ state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Vertex",
+ 2, /* n components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_verts);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Color::blue",
+ 4, /* n components */
+ GL_UNSIGNED_BYTE,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_colors);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_MultiTexCoord0",
+ 2, /* n components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_tex_coords);
+
+ cogl_vertex_buffer_submit (state.buffer);
+ }
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ cogl_handle_unref (state.buffer);
+ cogl_handle_unref (state.material);
+ cogl_handle_unref (state.texture);
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-vertex-buffer-interleved.c b/cogl/tests/conform/test-vertex-buffer-interleved.c
new file mode 100644
index 000000000..2d31e364a
--- /dev/null
+++ b/cogl/tests/conform/test-vertex-buffer-interleved.c
@@ -0,0 +1,162 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that interleved attributes work with the vertex buffer
+ * API. We add (x,y) GLfloat vertices, interleved with RGBA GLubyte color
+ * attributes to a buffer, submit and draw.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+ CoglHandle buffer;
+ ClutterGeometry stage_geom;
+} TestState;
+
+typedef struct _InterlevedVertex
+{
+ GLfloat x;
+ GLfloat y;
+
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+} InterlevedVertex;
+
+
+static void
+validate_result (TestState *state)
+{
+ GLubyte pixel[4];
+ GLint y_off = 90;
+
+ /* NB: We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+ /* Should see a blue pixel */
+ cogl_read_pixels (10, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ /* Draw a faded blue triangle */
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ validate_result (state);
+}
+
+static CoglBool
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_vertex_buffer_interleved (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+ clutter_actor_get_geometry (stage, &state.stage_geom);
+
+ group = clutter_group_new ();
+ clutter_actor_set_size (group,
+ state.stage_geom.width,
+ state.stage_geom.height);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing incase someone comments out the
+ * clutter_main_quit and wants visual feedback for the test since we
+ * wont be doing anything else that will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ {
+ InterlevedVertex verts[3] =
+ {
+ { /* .x = */ 0.0, /* .y = */ 0.0,
+ /* blue */
+ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0xff },
+
+ { /* .x = */ 100.0, /* .y = */ 100.0,
+ /* transparent blue */
+ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 },
+
+ { /* .x = */ 0.0, /* .y = */ 100.0,
+ /* transparent blue */
+ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 },
+ };
+
+ /* We assume the compiler is doing no funny struct padding for this test:
+ */
+ g_assert (sizeof (InterlevedVertex) == 12);
+
+ state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Vertex",
+ 2, /* n components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ 12, /* stride */
+ &verts[0].x);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Color",
+ 4, /* n components */
+ GL_UNSIGNED_BYTE,
+ FALSE, /* normalized */
+ 12, /* stride */
+ &verts[0].r);
+ cogl_vertex_buffer_submit (state.buffer);
+ }
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ cogl_handle_unref (state.buffer);
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-vertex-buffer-mutability.c b/cogl/tests/conform/test-vertex-buffer-mutability.c
new file mode 100644
index 000000000..9c8d5da8c
--- /dev/null
+++ b/cogl/tests/conform/test-vertex-buffer-mutability.c
@@ -0,0 +1,198 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that modifying a vertex buffer works, by updating
+ * vertex positions, and deleting and re-adding different color attributes.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+ CoglHandle buffer;
+ ClutterGeometry stage_geom;
+} TestState;
+
+static void
+validate_result (TestState *state)
+{
+ GLubyte pixel[4];
+ GLint y_off = 90;
+
+ /* NB: We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+ /* Should see a red pixel */
+ cogl_read_pixels (110, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0);
+
+ /* Should see a green pixel */
+ cogl_read_pixels (210, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (cogl_test_verbose ())
+ g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+ g_assert (pixel[RED] == 0 && pixel[GREEN] != 0 && pixel[BLUE] == 0);
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ GLfloat triangle_verts[3][2] =
+ {
+ {100.0, 0.0},
+ {200.0, 100.0},
+ {100.0, 100.0}
+ };
+ GLbyte triangle_colors[3][4] =
+ {
+ {0x00, 0xff, 0x00, 0xff}, /* blue */
+ {0x00, 0xff, 0x00, 0x00}, /* transparent blue */
+ {0x00, 0xff, 0x00, 0x00} /* transparent blue */
+ };
+
+ /*
+ * Draw a red triangle
+ */
+
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+
+ cogl_vertex_buffer_add (state->buffer,
+ "gl_Vertex",
+ 2, /* n components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_verts);
+ cogl_vertex_buffer_delete (state->buffer, "gl_Color");
+ cogl_vertex_buffer_submit (state->buffer);
+
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ /*
+ * Draw a faded green triangle
+ */
+
+ cogl_vertex_buffer_add (state->buffer,
+ "gl_Color",
+ 4, /* n components */
+ GL_UNSIGNED_BYTE,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_colors);
+ cogl_vertex_buffer_submit (state->buffer);
+
+ cogl_translate (100, 0, 0);
+ cogl_vertex_buffer_draw (state->buffer,
+ GL_TRIANGLE_STRIP, /* mode */
+ 0, /* first */
+ 3); /* count */
+
+ validate_result (state);
+}
+
+static CoglBool
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_vertex_buffer_mutability (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+ clutter_actor_get_geometry (stage, &state.stage_geom);
+
+ group = clutter_group_new ();
+ clutter_actor_set_size (group,
+ state.stage_geom.width,
+ state.stage_geom.height);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+ /* We force continuous redrawing incase someone comments out the
+ * clutter_main_quit and wants visual feedback for the test since we
+ * wont be doing anything else that will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+
+ g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+ {
+ GLfloat triangle_verts[3][2] =
+ {
+ {0.0, 0.0},
+ {100.0, 100.0},
+ {0.0, 100.0}
+ };
+ GLbyte triangle_colors[3][4] =
+ {
+ {0x00, 0x00, 0xff, 0xff}, /* blue */
+ {0x00, 0x00, 0xff, 0x00}, /* transparent blue */
+ {0x00, 0x00, 0xff, 0x00} /* transparent blue */
+ };
+ state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Vertex",
+ 2, /* n components */
+ GL_FLOAT,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_verts);
+ cogl_vertex_buffer_add (state.buffer,
+ "gl_Color",
+ 4, /* n components */
+ GL_UNSIGNED_BYTE,
+ FALSE, /* normalized */
+ 0, /* stride */
+ triangle_colors);
+ cogl_vertex_buffer_submit (state.buffer);
+ }
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ cogl_handle_unref (state.buffer);
+
+ g_source_remove (idle_source);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-viewport.c b/cogl/tests/conform/test-viewport.c
new file mode 100644
index 000000000..a1937c42a
--- /dev/null
+++ b/cogl/tests/conform/test-viewport.c
@@ -0,0 +1,416 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define FRAMEBUFFER_WIDTH 640
+#define FRAMEBUFFER_HEIGHT 480
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+static void
+assert_region_color (int x,
+ int y,
+ int width,
+ int height,
+ uint8_t red,
+ uint8_t green,
+ uint8_t blue,
+ uint8_t alpha)
+{
+ uint8_t *data = g_malloc0 (width * height * 4);
+ cogl_read_pixels (x, y, width, height,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ data);
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ uint8_t *pixel = &data[y*width*4 + x*4];
+#if 1
+ g_assert (pixel[RED] == red &&
+ pixel[GREEN] == green &&
+ pixel[BLUE] == blue &&
+ pixel[ALPHA] == alpha);
+#endif
+ }
+ g_free (data);
+}
+
+static void
+assert_rectangle_color_and_black_border (int x,
+ int y,
+ int width,
+ int height,
+ uint8_t red,
+ uint8_t green,
+ uint8_t blue)
+{
+ /* check the rectangle itself... */
+ assert_region_color (x, y, width, height, red, green, blue, 0xff);
+ /* black to left of the rectangle */
+ assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff);
+ /* black to right of the rectangle */
+ assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff);
+ /* black above the rectangle */
+ assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff);
+ /* and black below the rectangle */
+ assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff);
+}
+
+
+static void
+on_paint (ClutterActor *actor, void *state)
+{
+ float saved_viewport[4];
+ CoglMatrix saved_projection;
+ CoglMatrix projection;
+ CoglMatrix modelview;
+ guchar *data;
+ CoglHandle tex;
+ CoglHandle offscreen;
+ CoglColor black;
+ float x0;
+ float y0;
+ float width;
+ float height;
+
+ /* for clearing the offscreen framebuffer to black... */
+ cogl_color_init_from_4ub (&black, 0x00, 0x00, 0x00, 0xff);
+
+ cogl_get_viewport (saved_viewport);
+ cogl_get_projection_matrix (&saved_projection);
+ cogl_push_matrix ();
+
+ cogl_matrix_init_identity (&projection);
+ cogl_matrix_init_identity (&modelview);
+
+ cogl_set_projection_matrix (&projection);
+ cogl_set_modelview_matrix (&modelview);
+
+ /* - Create a 100x200 viewport (i.e. smaller than the onscreen framebuffer)
+ * and position it a (20, 10) inside the framebuffer.
+ * - Fill the whole viewport with a purple rectangle
+ * - Verify that the framebuffer is black with a 100x200 purple rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 100, /* width */
+ 200); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* fill the viewport with purple.. */
+ cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0xff, 0x00, 0xff);
+
+
+ /* - Create a viewport twice the size of the onscreen framebuffer with
+ * a negative offset positioning it at (-20, -10) relative to the
+ * buffer itself.
+ * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which
+ * is (20, 10) within the framebuffer)
+ * - Verify that the framebuffer is black with a 100x200 green rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (-20, /* x */
+ -10, /* y */
+ FRAMEBUFFER_WIDTH * 2, /* width */
+ FRAMEBUFFER_HEIGHT * 2); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* draw a 100x200 green rectangle offset into the viewport such that its
+ * top left corner should be found at (20, 10) in the offscreen buffer */
+ /* (offset 40 pixels right from the left of the viewport) */
+ x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f;
+ /* (offset 20 pixels down from the top of the viewport) */
+ y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f;
+ width = (1.0f / FRAMEBUFFER_WIDTH) * 100;
+ height = (1.0f / FRAMEBUFFER_HEIGHT) * 200;
+ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+ cogl_rectangle (x0, y0, x0 + width, y0 - height);
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0x00, 0xff, 0x00);
+
+
+ /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+ * buffer.
+ * - Push a 100x200 window space clip rectangle at (20, 10)
+ * - Fill the whole viewport with a blue rectangle
+ * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 200, /* width */
+ 400); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ cogl_clip_push_window_rectangle (20, 10, 100, 200);
+ /* fill the viewport with blue.. */
+ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ cogl_clip_pop ();
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0x00, 0x00, 0xff);
+
+
+ /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+ * buffer.
+ * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport
+ * (i.e. (40, 20) inside the framebuffer)
+ * - Fill the whole viewport with a green rectangle
+ * - Verify that the framebuffer is black with a 100x200 green rectangle at
+ * (40, 20)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 200, /* width */
+ 400); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* figure out where to position our clip rectangle in model space
+ * coordinates... */
+ /* (offset 40 pixels right from the left of the viewport) */
+ x0 = -1.0f + (2.0f / 200) * 20.f;
+ /* (offset 20 pixels down from the top of the viewport) */
+ y0 = 1.0f - (2.0f / 400) * 10.0f;
+ width = (2.0f / 200) * 100;
+ height = (2.0f / 400) * 200;
+ /* add the clip rectangle... */
+ cogl_push_matrix ();
+ cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0);
+ /* XXX: Rotate just enough to stop Cogl from converting our model space
+ * rectangle into a window space rectangle.. */
+ cogl_rotate (0.1, 0, 0, 1);
+ cogl_clip_push_rectangle (-(width/2.0), -(height/2.0),
+ width/2.0, height/2.0);
+ cogl_pop_matrix ();
+ /* fill the viewport with green.. */
+ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ cogl_clip_pop ();
+ assert_rectangle_color_and_black_border (40, 20, 100, 200,
+ 0x00, 0xff, 0x00);
+
+
+ /* Set the viewport to something specific so we can verify that it gets
+ * restored after we are done testing with an offscreen framebuffer... */
+ cogl_set_viewport (20, 10, 100, 200);
+
+ /*
+ * Next test offscreen drawing...
+ */
+ data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+ tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ TEST_UTILS_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */
+ COGL_PIXEL_FORMAT_ANY, /* internal fmt */
+ FRAMEBUFFER_WIDTH * 4, /* rowstride */
+ data);
+ g_free (data);
+ offscreen = cogl_offscreen_new_with_texture (tex);
+
+ cogl_push_framebuffer (offscreen);
+
+
+ /* - Create a 100x200 viewport (i.e. smaller than the offscreen framebuffer)
+ * and position it a (20, 10) inside the framebuffer.
+ * - Fill the whole viewport with a blue rectangle
+ * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 100, /* width */
+ 200); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* fill the viewport with blue.. */
+ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0x00, 0x00, 0xff);
+
+
+ /* - Create a viewport twice the size of the offscreen framebuffer with
+ * a negative offset positioning it at (-20, -10) relative to the
+ * buffer itself.
+ * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which
+ * is (20, 10) within the framebuffer)
+ * - Verify that the framebuffer is black with a 100x200 red rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (-20, /* x */
+ -10, /* y */
+ FRAMEBUFFER_WIDTH * 2, /* width */
+ FRAMEBUFFER_HEIGHT * 2); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* draw a 100x200 red rectangle offset into the viewport such that its
+ * top left corner should be found at (20, 10) in the offscreen buffer */
+ /* (offset 40 pixels right from the left of the viewport) */
+ x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f;
+ /* (offset 20 pixels down from the top of the viewport) */
+ y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f;
+ width = (1.0f / FRAMEBUFFER_WIDTH) * 100;
+ height = (1.0f / FRAMEBUFFER_HEIGHT) * 200;
+ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+ cogl_rectangle (x0, y0, x0 + width, y0 - height);
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0xff, 0x00, 0x00);
+
+
+ /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+ * buffer.
+ * - Push a 100x200 window space clip rectangle at (20, 10)
+ * - Fill the whole viewport with a blue rectangle
+ * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+ * (20, 10)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 200, /* width */
+ 400); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ cogl_clip_push_window_rectangle (20, 10, 100, 200);
+ /* fill the viewport with blue.. */
+ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ cogl_clip_pop ();
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0x00, 0x00, 0xff);
+
+
+ /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+ * buffer.
+ * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport
+ * (i.e. (40, 20) inside the framebuffer)
+ * - Fill the whole viewport with a green rectangle
+ * - Verify that the framebuffer is black with a 100x200 green rectangle at
+ * (40, 20)
+ */
+ cogl_set_viewport (20, /* x */
+ 10, /* y */
+ 200, /* width */
+ 400); /* height */
+ /* clear everything... */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ /* figure out where to position our clip rectangle in model space
+ * coordinates... */
+ /* (offset 40 pixels right from the left of the viewport) */
+ x0 = -1.0f + (2.0f / 200) * 20.f;
+ /* (offset 20 pixels down from the top of the viewport) */
+ y0 = 1.0f - (2.0f / 400) * 10.0f;
+ width = (2.0f / 200) * 100;
+ height = (2.0f / 400) * 200;
+ /* add the clip rectangle... */
+ cogl_push_matrix ();
+ cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0);
+ /* XXX: Rotate just enough to stop Cogl from converting our model space
+ * rectangle into a window space rectangle.. */
+ cogl_rotate (0.1, 0, 0, 1);
+ cogl_clip_push_rectangle (-(width/2.0), -(height/2.0),
+ width/2, height/2);
+ cogl_pop_matrix ();
+ /* fill the viewport with green.. */
+ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ cogl_clip_pop ();
+ assert_rectangle_color_and_black_border (40, 20, 100, 200,
+ 0x00, 0xff, 0x00);
+
+
+ /* Set the viewport to something obscure to verify that it gets
+ * replace when we switch back to the onscreen framebuffer... */
+ cogl_set_viewport (0, 0, 10, 10);
+
+ cogl_pop_framebuffer ();
+ cogl_handle_unref (offscreen);
+
+ /*
+ * Verify that the previous onscreen framebuffer's viewport was restored
+ * by drawing a white rectangle across the whole viewport. This should
+ * draw a 100x200 rectangle at (20,10) relative to the onscreen draw
+ * buffer...
+ */
+ cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+ cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+ cogl_rectangle (-1, 1, 1, -1);
+ assert_rectangle_color_and_black_border (20, 10, 100, 200,
+ 0xff, 0xff, 0xff);
+
+
+ /* Uncomment to display the last contents of the offscreen framebuffer */
+#if 1
+ cogl_matrix_init_identity (&projection);
+ cogl_matrix_init_identity (&modelview);
+ cogl_set_viewport (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
+ cogl_set_projection_matrix (&projection);
+ cogl_set_modelview_matrix (&modelview);
+ cogl_set_source_texture (tex);
+ cogl_rectangle (-1, 1, 1, -1);
+#endif
+
+ cogl_handle_unref (tex);
+
+ /* Finally restore the stage's original state... */
+ cogl_pop_matrix ();
+ cogl_set_projection_matrix (&saved_projection);
+ cogl_set_viewport (saved_viewport[0], saved_viewport[1],
+ saved_viewport[2], saved_viewport[3]);
+
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static CoglBool
+queue_redraw (void *stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_viewport (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ unsigned int idle_source;
+ ClutterActor *stage;
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ /* We force continuous redrawing of the stage, since we need to skip
+ * the first few frames, and we wont be doing anything else that
+ * will trigger redrawing. */
+ idle_source = g_idle_add (queue_redraw, stage);
+ g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL);
+
+ clutter_actor_show (stage);
+ clutter_main ();
+
+ g_source_remove (idle_source);
+
+ /* Remove all of the actors from the stage */
+ clutter_container_foreach (CLUTTER_CONTAINER (stage),
+ (ClutterCallback) clutter_actor_destroy,
+ NULL);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/cogl/tests/conform/test-wrap-modes.c b/cogl/tests/conform/test-wrap-modes.c
new file mode 100644
index 000000000..e5a2a1a61
--- /dev/null
+++ b/cogl/tests/conform/test-wrap-modes.c
@@ -0,0 +1,296 @@
+#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0
+
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+#define TEX_SIZE 4
+
+typedef struct _TestState
+{
+ int width;
+ int height;
+ CoglTexture *texture;
+} TestState;
+
+static CoglTexture *
+create_texture (TestUtilsTextureFlags flags)
+{
+ uint8_t *data = g_malloc (TEX_SIZE * TEX_SIZE * 4), *p = data;
+ CoglTexture *tex;
+ int x, y;
+
+ for (y = 0; y < TEX_SIZE; y++)
+ for (x = 0; x < TEX_SIZE; x++)
+ {
+ *(p++) = 0;
+ *(p++) = (x & 1) * 255;
+ *(p++) = (y & 1) * 255;
+ *(p++) = 255;
+ }
+
+ tex = test_utils_texture_new_from_data (test_ctx,
+ TEX_SIZE, TEX_SIZE, flags,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ TEX_SIZE * 4,
+ data);
+ g_free (data);
+
+ return tex;
+}
+
+static CoglPipeline *
+create_pipeline (TestState *state,
+ CoglPipelineWrapMode wrap_mode_s,
+ CoglPipelineWrapMode wrap_mode_t)
+{
+ CoglPipeline *pipeline;
+
+ pipeline = cogl_pipeline_new (test_ctx);
+ cogl_pipeline_set_layer_texture (pipeline, 0, state->texture);
+ cogl_pipeline_set_layer_filters (pipeline, 0,
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+ cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0, wrap_mode_s);
+ cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0, wrap_mode_t);
+
+ return pipeline;
+}
+
+static CoglPipelineWrapMode
+wrap_modes[] =
+ {
+ COGL_PIPELINE_WRAP_MODE_REPEAT,
+ COGL_PIPELINE_WRAP_MODE_REPEAT,
+
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
+
+ COGL_PIPELINE_WRAP_MODE_REPEAT,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
+
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
+ COGL_PIPELINE_WRAP_MODE_REPEAT,
+
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC,
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC,
+
+ COGL_PIPELINE_WRAP_MODE_AUTOMATIC,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE
+ };
+
+static void
+draw_tests (TestState *state)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2)
+ {
+ CoglPipelineWrapMode wrap_mode_s, wrap_mode_t;
+ CoglPipeline *pipeline;
+
+ /* Create a separate pipeline for each pair of wrap modes so
+ that we can verify whether the batch splitting works */
+ wrap_mode_s = wrap_modes[i];
+ wrap_mode_t = wrap_modes[i + 1];
+ pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t);
+ /* Render the pipeline at four times the size of the texture */
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ i * TEX_SIZE,
+ 0,
+ (i + 2) * TEX_SIZE,
+ TEX_SIZE * 2,
+ 0, 0, 2, 2);
+ cogl_object_unref (pipeline);
+ }
+}
+
+static const CoglTextureVertex vertices[4] =
+ {
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, TEX_SIZE * 2, 0.0f, 0.0f, 2.0f },
+ { TEX_SIZE * 2, TEX_SIZE * 2, 0.0f, 2.0f, 2.0f },
+ { TEX_SIZE * 2, 0.0f, 0.0f, 2.0f, 0.0f }
+ };
+
+static void
+draw_tests_polygon (TestState *state)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2)
+ {
+ CoglPipelineWrapMode wrap_mode_s, wrap_mode_t;
+ CoglPipeline *pipeline;
+
+ wrap_mode_s = wrap_modes[i];
+ wrap_mode_t = wrap_modes[i + 1];
+ pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t);
+ cogl_set_source (pipeline);
+ cogl_object_unref (pipeline);
+ cogl_push_matrix ();
+ cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+ /* Render the pipeline at four times the size of the texture */
+ cogl_polygon (vertices, G_N_ELEMENTS (vertices), FALSE);
+ cogl_pop_matrix ();
+ }
+}
+
+static void
+draw_tests_vbo (TestState *state)
+{
+ CoglHandle vbo;
+ int i;
+
+ vbo = cogl_vertex_buffer_new (4);
+ cogl_vertex_buffer_add (vbo, "gl_Vertex", 3,
+ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (vertices[0]),
+ &vertices[0].x);
+ cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0", 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (vertices[0]),
+ &vertices[0].tx);
+ cogl_vertex_buffer_submit (vbo);
+
+ for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2)
+ {
+ CoglPipelineWrapMode wrap_mode_s, wrap_mode_t;
+ CoglPipeline *pipeline;
+
+ wrap_mode_s = wrap_modes[i];
+ wrap_mode_t = wrap_modes[i + 1];
+ pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t);
+ cogl_set_source (pipeline);
+ cogl_object_unref (pipeline);
+ cogl_push_matrix ();
+ cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+ /* Render the pipeline at four times the size of the texture */
+ cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_TRIANGLE_FAN, 0, 4);
+ cogl_pop_matrix ();
+ }
+
+ cogl_handle_unref (vbo);
+}
+
+static void
+validate_set (TestState *state, int offset)
+{
+ uint8_t data[TEX_SIZE * 2 * TEX_SIZE * 2 * 4], *p;
+ int x, y, i;
+
+ for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2)
+ {
+ CoglPipelineWrapMode wrap_mode_s, wrap_mode_t;
+
+ wrap_mode_s = wrap_modes[i];
+ wrap_mode_t = wrap_modes[i + 1];
+
+ cogl_framebuffer_read_pixels (test_fb, i * TEX_SIZE, offset * TEX_SIZE * 2,
+ TEX_SIZE * 2, TEX_SIZE * 2,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ data);
+
+ p = data;
+
+ for (y = 0; y < TEX_SIZE * 2; y++)
+ for (x = 0; x < TEX_SIZE * 2; x++)
+ {
+ uint8_t green, blue;
+
+ if (x < TEX_SIZE ||
+ wrap_mode_s == COGL_PIPELINE_WRAP_MODE_REPEAT ||
+ wrap_mode_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ green = (x & 1) * 255;
+ else
+ green = ((TEX_SIZE - 1) & 1) * 255;
+
+ if (y < TEX_SIZE ||
+ wrap_mode_t == COGL_PIPELINE_WRAP_MODE_REPEAT ||
+ wrap_mode_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+ blue = (y & 1) * 255;
+ else
+ blue = ((TEX_SIZE - 1) & 1) * 255;
+
+ g_assert_cmpint (p[0], ==, 0);
+ g_assert_cmpint (p[1], ==, green);
+ g_assert_cmpint (p[2], ==, blue);
+
+ p += 4;
+ }
+ }
+}
+
+static void
+validate_result (TestState *state)
+{
+ validate_set (state, 0); /* non-atlased rectangle */
+#if 0 /* this doesn't currently work */
+ validate_set (state, 1); /* atlased rectangle */
+#endif
+ validate_set (state, 2); /* cogl_polygon */
+ validate_set (state, 3); /* vertex buffer */
+}
+
+static void
+paint (TestState *state)
+{
+ /* Draw the tests first with a non atlased texture */
+ state->texture = create_texture (TEST_UTILS_TEXTURE_NO_ATLAS);
+ draw_tests (state);
+ cogl_object_unref (state->texture);
+
+ /* Draw the tests again with a possible atlased texture. This should
+ end up testing software repeats */
+ state->texture = create_texture (TEST_UTILS_TEXTURE_NONE);
+ cogl_framebuffer_push_matrix (test_fb);
+ cogl_framebuffer_translate (test_fb, 0.0f, TEX_SIZE * 2.0f, 0.0f);
+ draw_tests (state);
+ cogl_pop_matrix ();
+ cogl_object_unref (state->texture);
+
+ /* Draw the tests using cogl_polygon */
+ state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+ cogl_push_matrix ();
+ cogl_translate (0.0f, TEX_SIZE * 4.0f, 0.0f);
+ draw_tests_polygon (state);
+ cogl_pop_matrix ();
+ cogl_object_unref (state->texture);
+
+ /* Draw the tests using a vertex buffer */
+ state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+ cogl_push_matrix ();
+ cogl_translate (0.0f, TEX_SIZE * 6.0f, 0.0f);
+ draw_tests_vbo (state);
+ cogl_pop_matrix ();
+ cogl_object_unref (state->texture);
+
+ validate_result (state);
+}
+
+void
+test_wrap_modes (void)
+{
+ TestState state;
+
+ state.width = cogl_framebuffer_get_width (test_fb);
+ state.height = cogl_framebuffer_get_height (test_fb);
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0,
+ state.width,
+ state.height,
+ -1,
+ 100);
+
+ /* XXX: we have to push/pop a framebuffer since this test currently
+ * uses the legacy cogl_vertex_buffer_draw() api. */
+ cogl_push_framebuffer (test_fb);
+ paint (&state);
+ cogl_pop_framebuffer ();
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/conform/test-wrap-rectangle-textures.c b/cogl/tests/conform/test-wrap-rectangle-textures.c
new file mode 100644
index 000000000..73b357536
--- /dev/null
+++ b/cogl/tests/conform/test-wrap-rectangle-textures.c
@@ -0,0 +1,175 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define DRAW_SIZE 64
+
+static CoglPipeline *
+create_base_pipeline (void)
+{
+ CoglBitmap *bmp;
+ CoglTextureRectangle *tex;
+ CoglPipeline *pipeline;
+ uint8_t tex_data[] =
+ {
+ 0x44, 0x44, 0x44, 0x88, 0x88, 0x88,
+ 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff
+ };
+
+ bmp = cogl_bitmap_new_for_data (test_ctx,
+ 2, 2, /* width/height */
+ COGL_PIXEL_FORMAT_RGB_888,
+ 2 * 3, /* rowstride */
+ tex_data);
+
+ tex = cogl_texture_rectangle_new_from_bitmap (bmp);
+
+ cogl_object_unref (bmp);
+
+ pipeline = cogl_pipeline_new (test_ctx);
+
+ cogl_pipeline_set_layer_filters (pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_FILTER_NEAREST,
+ COGL_PIPELINE_FILTER_NEAREST);
+
+ cogl_pipeline_set_layer_texture (pipeline,
+ 0, /* layer */
+ tex);
+
+ cogl_object_unref (tex);
+
+ return pipeline;
+}
+
+static void
+check_colors (int x_offset,
+ int y_offset,
+ const uint8_t expected_colors[9])
+{
+ int x, y;
+
+ for (y = 0; y < 4; y++)
+ for (x = 0; x < 4; x++)
+ {
+ uint32_t color = expected_colors[x + y * 4];
+ test_utils_check_region (test_fb,
+ x * DRAW_SIZE / 4 + 1 + x_offset,
+ y * DRAW_SIZE / 4 + 1 + y_offset,
+ DRAW_SIZE / 4 - 2,
+ DRAW_SIZE / 4 - 2,
+ 0xff |
+ (color << 8) |
+ (color << 16) |
+ (color << 24));
+ }
+}
+
+static void
+test_pipeline (CoglPipeline *pipeline,
+ int x_offset,
+ int y_offset,
+ const uint8_t expected_colors[9])
+{
+ float x1 = x_offset;
+ float y1 = y_offset;
+ float x2 = x1 + DRAW_SIZE;
+ float y2 = y1 + DRAW_SIZE;
+ int y, x;
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ x1, y1,
+ x2, y2,
+ -0.5f, /* s1 */
+ -0.5f, /* t1 */
+ 1.5f, /* s2 */
+ 1.5f /* t2 */);
+
+ check_colors (x_offset, y_offset, expected_colors);
+
+ /* Also try drawing each quadrant of the rectangle with a small
+ * rectangle */
+
+ for (y = -1; y < 3; y++)
+ for (x = -1; x < 3; x++)
+ {
+ x1 = x_offset + (x + 1) * DRAW_SIZE / 4 + DRAW_SIZE;
+ y1 = y_offset + (y + 1) * DRAW_SIZE / 4;
+ x2 = x1 + DRAW_SIZE / 4;
+ y2 = y1 + DRAW_SIZE / 4;
+
+ cogl_framebuffer_draw_textured_rectangle (test_fb,
+ pipeline,
+ x1, y1,
+ x2, y2,
+ x / 2.0f, /* s1 */
+ y / 2.0f, /* t1 */
+ (x + 1) / 2.0f, /* s2 */
+ (y + 1) / 2.0f /* t2 */);
+ }
+
+ check_colors (x_offset + DRAW_SIZE, y_offset, expected_colors);
+}
+
+void
+test_wrap_rectangle_textures (void)
+{
+ float fb_width = cogl_framebuffer_get_width (test_fb);
+ float fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglPipeline *base_pipeline;
+ CoglPipeline *clamp_pipeline;
+ CoglPipeline *repeat_pipeline;
+ /* The textures are drawn with the texture coordinates from
+ * -0.5→1.5. That means we get one complete copy of the texture and
+ * an extra half of the texture surrounding it. The drawing is
+ * tested against a 4x4 grid of colors. The center 2x2 colours
+ * specify the normal texture colors and the other colours specify
+ * what the wrap mode should generate */
+ static const uint8_t clamp_colors[] =
+ {
+ 0x44, 0x44, 0x88, 0x88,
+ 0x44, 0x44, 0x88, 0x88,
+ 0xcc, 0xcc, 0xff, 0xff,
+ 0xcc, 0xcc, 0xff, 0xff
+ };
+ static const uint8_t repeat_colors[] =
+ {
+ 0xff, 0xcc, 0xff, 0xcc,
+ 0x88, 0x44, 0x88, 0x44,
+ 0xff, 0xcc, 0xff, 0xcc,
+ 0x88, 0x44, 0x88, 0x44
+ };
+
+ cogl_framebuffer_orthographic (test_fb,
+ 0, 0, /* x_1, y_1 */
+ fb_width, /* x_2 */
+ fb_height /* y_2 */,
+ -1, 100 /* near/far */);
+
+ base_pipeline = create_base_pipeline ();
+
+ clamp_pipeline = cogl_pipeline_copy (base_pipeline);
+ cogl_pipeline_set_layer_wrap_mode (clamp_pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+ repeat_pipeline = cogl_pipeline_copy (base_pipeline);
+ cogl_pipeline_set_layer_wrap_mode (repeat_pipeline,
+ 0, /* layer */
+ COGL_PIPELINE_WRAP_MODE_REPEAT);
+
+ test_pipeline (clamp_pipeline,
+ 0, 0, /* x/y offset */
+ clamp_colors);
+
+ test_pipeline (repeat_pipeline,
+ 0, DRAW_SIZE * 2, /* x/y offset */
+ repeat_colors);
+
+ cogl_object_unref (repeat_pipeline);
+ cogl_object_unref (clamp_pipeline);
+ cogl_object_unref (base_pipeline);
+}
diff --git a/cogl/tests/conform/test-write-texture-formats.c b/cogl/tests/conform/test-write-texture-formats.c
new file mode 100644
index 000000000..d415df0a7
--- /dev/null
+++ b/cogl/tests/conform/test-write-texture-formats.c
@@ -0,0 +1,184 @@
+#include <cogl/cogl2-experimental.h>
+#include <stdarg.h>
+
+#include "test-utils.h"
+
+/*
+ * This tests writing data to an RGBA texture in all of the available
+ * pixel formats
+ */
+
+static void
+test_color (CoglTexture *texture,
+ uint32_t expected_pixel)
+{
+ uint8_t received_pixel[4];
+
+ cogl_texture_get_data (texture,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 4, /* rowstride */
+ received_pixel);
+
+ test_utils_compare_pixel_and_alpha (received_pixel, expected_pixel);
+}
+
+static void
+test_write_byte (CoglContext *context,
+ CoglPixelFormat format,
+ uint8_t byte,
+ uint32_t expected_pixel)
+{
+ CoglTexture *texture = test_utils_create_color_texture (context, 0);
+
+ cogl_texture_set_region (texture,
+ 0, 0, /* src_x / src_y */
+ 0, 0, /* dst_x / dst_y */
+ 1, 1, /* dst_w / dst_h */
+ 1, 1, /* width / height */
+ format,
+ 1, /* rowstride */
+ &byte);
+
+ test_color (texture, expected_pixel);
+
+ cogl_object_unref (texture);
+}
+
+static void
+test_write_short (CoglContext *context,
+ CoglPixelFormat format,
+ uint16_t value,
+ uint32_t expected_pixel)
+{
+ CoglTexture *texture = test_utils_create_color_texture (context, 0);
+
+ cogl_texture_set_region (texture,
+ 0, 0, /* src_x / src_y */
+ 0, 0, /* dst_x / dst_y */
+ 1, 1, /* dst_w / dst_h */
+ 1, 1, /* width / height */
+ format,
+ 2, /* rowstride */
+ (uint8_t *) &value);
+
+ test_color (texture, expected_pixel);
+
+ cogl_object_unref (texture);
+}
+
+static void
+test_write_bytes (CoglContext *context,
+ CoglPixelFormat format,
+ uint32_t value,
+ uint32_t expected_pixel)
+{
+ CoglTexture *texture = test_utils_create_color_texture (context, 0);
+
+ value = GUINT32_TO_BE (value);
+
+ cogl_texture_set_region (texture,
+ 0, 0, /* src_x / src_y */
+ 0, 0, /* dst_x / dst_y */
+ 1, 1, /* dst_w / dst_h */
+ 1, 1, /* width / height */
+ format,
+ 4, /* rowstride */
+ (uint8_t *) &value);
+
+ test_color (texture, expected_pixel);
+
+ cogl_object_unref (texture);
+}
+
+static void
+test_write_int (CoglContext *context,
+ CoglPixelFormat format,
+ uint32_t expected_pixel,
+ ...)
+{
+ va_list ap;
+ int bits;
+ uint32_t tex_data = 0;
+ int bits_sum = 0;
+ CoglTexture *texture = test_utils_create_color_texture (context, 0);
+
+ va_start (ap, expected_pixel);
+
+ /* Convert the va args into a single 32-bit value */
+ while ((bits = va_arg (ap, int)) != -1)
+ {
+ uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 127) / 255;
+
+ bits_sum += bits;
+
+ tex_data |= value << (32 - bits_sum);
+ }
+
+ va_end (ap);
+
+ cogl_texture_set_region (texture,
+ 0, 0, /* src_x / src_y */
+ 0, 0, /* dst_x / dst_y */
+ 1, 1, /* dst_w / dst_h */
+ 1, 1, /* width / height */
+ format,
+ 4, /* rowstride */
+ (uint8_t *) &tex_data);
+
+ test_color (texture, expected_pixel);
+
+ cogl_object_unref (texture);
+}
+
+void
+test_write_texture_formats (void)
+{
+ test_write_byte (test_ctx, COGL_PIXEL_FORMAT_A_8, 0x34, 0x00000034);
+#if 0
+ /* I'm not sure what's the right value to put here because Nvidia
+ and Mesa seem to behave differently so one of them must be
+ wrong. */
+ test_write_byte (test_ctx, COGL_PIXEL_FORMAT_G_8, 0x34, 0x340000ff);
+#endif
+
+ /* We should always be able to read from an RG buffer regardless of
+ * whether RG textures are supported because Cogl will do the
+ * conversion for us */
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RG_88, 0x123456ff, 0x123400ff);
+
+ test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGB_565, 0x0843, 0x080819ff);
+ test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_4444_PRE, 0x1234, 0x11223344);
+ test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_5551_PRE, 0x0887, 0x081019ff);
+
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff, 0x123456ff);
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff, 0x123456ff);
+
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 0x12345678, 0x12345678);
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGRA_8888_PRE,
+ 0x56341278, 0x12345678);
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ARGB_8888_PRE,
+ 0x78123456, 0x12345678);
+ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ABGR_8888_PRE,
+ 0x78563412, 0x12345678);
+
+ test_write_int (test_ctx, COGL_PIXEL_FORMAT_RGBA_1010102_PRE,
+ 0x123456ff,
+ 10, 0x12, 10, 0x34, 10, 0x56, 2, 0xff,
+ -1);
+ test_write_int (test_ctx, COGL_PIXEL_FORMAT_BGRA_1010102_PRE,
+ 0x123456ff,
+ 10, 0x56, 10, 0x34, 10, 0x12, 2, 0xff,
+ -1);
+ test_write_int (test_ctx, COGL_PIXEL_FORMAT_ARGB_2101010_PRE,
+ 0x123456ff,
+ 2, 0xff, 10, 0x12, 10, 0x34, 10, 0x56,
+ -1);
+ test_write_int (test_ctx, COGL_PIXEL_FORMAT_ABGR_2101010_PRE,
+ 0x123456ff,
+ 2, 0xff, 10, 0x56, 10, 0x34, 10, 0x12,
+ -1);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/cogl/tests/data/Makefile.am b/cogl/tests/data/Makefile.am
new file mode 100644
index 000000000..3a2030a77
--- /dev/null
+++ b/cogl/tests/data/Makefile.am
@@ -0,0 +1,3 @@
+NULL =
+
+EXTRA_DIST = valgrind.suppressions
diff --git a/cogl/tests/data/valgrind.suppressions b/cogl/tests/data/valgrind.suppressions
new file mode 100644
index 000000000..f47498d16
--- /dev/null
+++ b/cogl/tests/data/valgrind.suppressions
@@ -0,0 +1,173 @@
+{
+ ioctl_1
+ Memcheck:Param
+ ioctl(generic)
+ fun:ioctl
+ fun:driDrawableInitVBlank
+ fun:intelMakeCurrent
+ fun:glXMakeContextCurrent
+}
+
+{
+ ioctl_2
+ Memcheck:Param
+ ioctl(generic)
+ fun:ioctl
+ fun:driDrawableGetMSC32
+ fun:clutter_backend_glx_redraw
+}
+
+{
+ ioctl_3
+ Memcheck:Param
+ ioctl(generic)
+ fun:ioctl
+ fun:driWaitForMSC32
+ fun:clutter_backend_glx_redraw
+}
+
+{
+ mesa_init_context
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:glXCreateNewContext
+}
+
+{
+ type_register
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_register_*
+}
+
+{
+ type_ref
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_class_ref
+}
+
+{
+ type_interface_prereq
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_interface_add_prerequisite
+}
+
+{
+ get_charset
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_get_charset
+}
+
+{
+ cogl_features
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:cogl_get_features
+}
+
+{
+ glx_query_version
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:glXQueryVersion
+}
+
+{
+ glx_create_context
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:glXCreateNewContext
+}
+
+{
+ glx_make_current
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:glXMakeContextCurrent
+}
+
+{
+ gl_draw_arrays
+ Memcheck:Leak
+ fun:*malloc
+ ...
+ fun:glDrawArrays
+}
+
+{
+ cogl_clear
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:cogl_clear
+}
+
+{
+ default_font
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:clutter_backend_get_font_name
+}
+
+{
+ id_pool
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:clutter_id_pool_new
+}
+
+{
+ x_open_display
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:XOpenDisplay
+}
+
+# ... and font descriptions from every "sans 12" type string
+{
+ pango_font_description_from_string
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:pango_font_description_from_string
+}
+
+# other lib init
+{
+ fontconfig_init
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:FcConfigParseAndLoad
+}
+
+{
+ freetype_init
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:FT_Open_Face
+}
+
+{
+ x_init_ext
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:XInitExtension
+}
diff --git a/cogl/tests/micro-perf/Makefile.am b/cogl/tests/micro-perf/Makefile.am
new file mode 100644
index 000000000..75d02b2ce
--- /dev/null
+++ b/cogl/tests/micro-perf/Makefile.am
@@ -0,0 +1,24 @@
+NULL =
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)
+
+test_conformance_CPPFLAGS = \
+ -DCOGL_ENABLE_EXPERIMENTAL_API \
+ -DCOGL_DISABLE_DEPRECATED \
+ -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\"
+
+
+noinst_PROGRAMS =
+
+noinst_PROGRAMS += test-journal
+
+AM_CFLAGS = $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+
+common_ldadd = \
+ $(COGL_DEP_LIBS) \
+ $(top_builddir)/cogl/libmutter-cogl.la \
+ $(LIBM)
+
+test_journal_SOURCES = test-journal.c
+test_journal_LDADD = $(common_ldadd)
diff --git a/cogl/tests/micro-perf/test-journal.c b/cogl/tests/micro-perf/test-journal.c
new file mode 100644
index 000000000..35e3cd5a3
--- /dev/null
+++ b/cogl/tests/micro-perf/test-journal.c
@@ -0,0 +1,190 @@
+#include <glib.h>
+#include <cogl/cogl2-experimental.h>
+#include <math.h>
+
+#include "cogl/cogl-profile.h"
+
+#define FRAMEBUFFER_WIDTH 800
+#define FRAMEBUFFER_HEIGHT 600
+
+CoglBool run_all = FALSE;
+
+typedef struct _Data
+{
+ CoglContext *ctx;
+ CoglFramebuffer *fb;
+ CoglPipeline *pipeline;
+ CoglPipeline *alpha_pipeline;
+ GTimer *timer;
+ int frame;
+} Data;
+
+static void
+test_rectangles (Data *data)
+{
+#define RECT_WIDTH 5
+#define RECT_HEIGHT 5
+ int x;
+ int y;
+
+ cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 1, 1, 1, 1);
+
+ cogl_framebuffer_push_rectangle_clip (data->fb,
+ 10,
+ 10,
+ FRAMEBUFFER_WIDTH - 10,
+ FRAMEBUFFER_HEIGHT - 10);
+
+ /* Should the rectangles be randomly positioned/colored/rotated?
+ *
+ * It could be good to develop equivalent GL and Cairo tests so we can
+ * have a sanity check for our Cogl performance.
+ *
+ * The color should vary to check that we correctly batch color changes
+ * The use of alpha should vary so we have a variation of which rectangles
+ * require blending.
+ * Should this be a random variation?
+ * It could be good to experiment with focibly enabling blending for
+ * rectangles that don't technically need it for the sake of extending
+ * batching. E.g. if you a long run of interleved rectangles with every
+ * other rectangle needing blending then it may be worth enabling blending
+ * for all the rectangles to avoid the state changes.
+ * The modelview should change between rectangles to check the software
+ * transform codepath.
+ * Should we group some rectangles under the same modelview? Potentially
+ * we could avoid software transform for long runs of rectangles with the
+ * same modelview.
+ *
+ */
+ for (y = 0; y < FRAMEBUFFER_HEIGHT; y += RECT_HEIGHT)
+ {
+ for (x = 0; x < FRAMEBUFFER_WIDTH; x += RECT_WIDTH)
+ {
+ cogl_framebuffer_push_matrix (data->fb);
+ cogl_framebuffer_translate (data->fb, x, y, 0);
+ cogl_framebuffer_rotate (data->fb, 45, 0, 0, 1);
+
+ cogl_pipeline_set_color4f (data->pipeline,
+ 1,
+ (1.0f/FRAMEBUFFER_WIDTH)*y,
+ (1.0f/FRAMEBUFFER_HEIGHT)*x,
+ 1);
+ cogl_framebuffer_draw_rectangle (data->fb,
+ data->pipeline,
+ 0, 0, RECT_WIDTH, RECT_HEIGHT);
+
+ cogl_framebuffer_pop_matrix (data->fb);
+ }
+ }
+
+ for (y = 0; y < FRAMEBUFFER_HEIGHT; y += RECT_HEIGHT)
+ {
+ for (x = 0; x < FRAMEBUFFER_WIDTH; x += RECT_WIDTH)
+ {
+ cogl_framebuffer_push_matrix (data->fb);
+ cogl_framebuffer_translate (data->fb, x, y, 0);
+
+ cogl_pipeline_set_color4f (data->alpha_pipeline,
+ 1,
+ (1.0f/FRAMEBUFFER_WIDTH)*x,
+ (1.0f/FRAMEBUFFER_HEIGHT)*y,
+ (1.0f/FRAMEBUFFER_WIDTH)*x);
+ cogl_framebuffer_draw_rectangle (data->fb,
+ data->alpha_pipeline,
+ 0, 0, RECT_WIDTH, RECT_HEIGHT);
+
+ cogl_framebuffer_pop_matrix (data->fb);
+ }
+ }
+
+ cogl_framebuffer_pop_clip (data->fb);
+}
+
+static CoglBool
+paint_cb (void *user_data)
+{
+ Data *data = user_data;
+ double elapsed;
+
+ data->frame++;
+
+ test_rectangles (data);
+
+ cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb));
+
+ elapsed = g_timer_elapsed (data->timer, NULL);
+ if (elapsed > 1.0)
+ {
+ g_print ("fps = %f\n", data->frame / elapsed);
+ g_timer_start (data->timer);
+ data->frame = 0;
+ }
+
+ return FALSE; /* remove the callback */
+}
+
+static void
+frame_event_cb (CoglOnscreen *onscreen,
+ CoglFrameEvent event,
+ CoglFrameInfo *info,
+ void *user_data)
+{
+ if (event == COGL_FRAME_EVENT_SYNC)
+ paint_cb (user_data);
+}
+
+int
+main (int argc, char **argv)
+{
+ Data data;
+ CoglOnscreen *onscreen;
+ GSource *cogl_source;
+ GMainLoop *loop;
+ COGL_STATIC_TIMER (mainloop_timer,
+ NULL, //no parent
+ "Mainloop",
+ "The time spent in the glib mainloop",
+ 0); // no application private data
+
+ data.ctx = cogl_context_new (NULL, NULL);
+
+ onscreen = cogl_onscreen_new (data.ctx,
+ FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
+ cogl_onscreen_set_swap_throttled (onscreen, FALSE);
+ cogl_onscreen_show (onscreen);
+
+ data.fb = onscreen;
+ cogl_framebuffer_orthographic (data.fb,
+ 0, 0,
+ FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ -1,
+ 100);
+
+ data.pipeline = cogl_pipeline_new (data.ctx);
+ cogl_pipeline_set_color4f (data.pipeline, 1, 1, 1, 1);
+ data.alpha_pipeline = cogl_pipeline_new (data.ctx);
+ cogl_pipeline_set_color4f (data.alpha_pipeline, 1, 1, 1, 0.5);
+
+ cogl_source = cogl_glib_source_new (data.ctx, G_PRIORITY_DEFAULT);
+
+ g_source_attach (cogl_source, NULL);
+
+ cogl_onscreen_add_frame_callback (COGL_ONSCREEN (data.fb),
+ frame_event_cb,
+ &data,
+ NULL); /* destroy notify */
+
+ g_idle_add (paint_cb, &data);
+
+ data.frame = 0;
+ data.timer = g_timer_new ();
+ g_timer_start (data.timer);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ COGL_TIMER_START (uprof_get_mainloop_context (), mainloop_timer);
+ g_main_loop_run (loop);
+ COGL_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer);
+
+ return 0;
+}
+
diff --git a/cogl/tests/run-tests.sh b/cogl/tests/run-tests.sh
new file mode 100755
index 000000000..7e62bf0f6
--- /dev/null
+++ b/cogl/tests/run-tests.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+
+if test -z "$G_DEBUG"; then
+ G_DEBUG=fatal-warnings
+else
+ G_DEBUG="$G_DEBUG,fatal-warnings"
+fi
+
+export G_DEBUG
+
+ENVIRONMENT_CONFIG=$1
+shift
+
+TEST_BINARY=$1
+shift
+
+. $ENVIRONMENT_CONFIG
+
+set +m
+
+trap "" ERR
+trap "" SIGABRT
+trap "" SIGFPE
+trap "" SIGSEGV
+
+EXIT=0
+MISSING_FEATURE="WARNING: Missing required feature";
+KNOWN_FAILURE="WARNING: Test is known to fail";
+
+echo "Key:"
+echo "ok = Test passed"
+echo "n/a = Driver is missing a feature required for the test"
+echo "FAIL = Unexpected failure"
+echo "FIXME = Test failed, but it was an expected failure"
+echo "PASS! = Unexpected pass"
+echo ""
+
+get_status()
+{
+ case $1 in
+ # Special value we use to indicate that the test failed
+ # but it was an expected failure so don't fail the
+ # overall test run as a result...
+ 300)
+ echo -n "FIXME";;
+ # Special value we use to indicate that the test passed
+ # but we weren't expecting it to pass‽
+ 400)
+ echo -n 'PASS!';;
+
+ # Special value to indicate the test is missing a required feature
+ 500)
+ echo -n "n/a";;
+
+ 0)
+ echo -n "ok";;
+
+ *)
+ echo -n "FAIL";;
+ esac
+}
+
+run_test()
+{
+ $($TEST_BINARY $1 &>.log)
+ TMP=$?
+ var_name=$2_result
+ eval $var_name=$TMP
+ if grep -q "$MISSING_FEATURE" .log; then
+ if test $TMP -ne 0; then
+ eval $var_name=500
+ else
+ eval $var_name=400
+ fi
+ elif grep -q "$KNOWN_FAILURE" .log; then
+ if test $TMP -ne 0; then
+ eval $var_name=300
+ else
+ eval $var_name=400
+ fi
+ else
+ if test $TMP -ne 0; then EXIT=$TMP; fi
+ fi
+}
+
+TITLE_FORMAT="%35s"
+printf $TITLE_FORMAT "Test"
+
+if test $HAVE_GL -eq 1; then
+ GL_FORMAT=" %6s %8s %7s %6s %6s"
+ printf "$GL_FORMAT" "GL+FF" "GL+ARBFP" "GL+GLSL" "GL-NPT" "GL3"
+fi
+if test $HAVE_GLES2 -eq 1; then
+ GLES2_FORMAT=" %6s %7s"
+ printf "$GLES2_FORMAT" "ES2" "ES2-NPT"
+fi
+
+echo ""
+echo ""
+
+for test in `cat unit-tests`
+do
+ export COGL_DEBUG=
+
+ if test $HAVE_GL -eq 1; then
+ export COGL_DRIVER=gl
+ export COGL_DEBUG=disable-glsl,disable-arbfp
+ run_test $test gl_ff
+
+ export COGL_DRIVER=gl
+ # NB: we can't explicitly disable fixed + glsl in this case since
+ # the arbfp code only supports fragment processing so we need either
+ # the fixed or glsl vertends
+ export COGL_DEBUG=
+ run_test $test gl_arbfp
+
+ export COGL_DRIVER=gl
+ export COGL_DEBUG=disable-fixed,disable-arbfp
+ run_test $test gl_glsl
+
+ export COGL_DRIVER=gl
+ export COGL_DEBUG=disable-npot-textures
+ run_test $test gl_npot
+
+ export COGL_DRIVER=gl3
+ export COGL_DEBUG=
+ run_test $test gl3
+ fi
+
+ if test $HAVE_GLES2 -eq 1; then
+ export COGL_DRIVER=gles2
+ export COGL_DEBUG=
+ run_test $test gles2
+
+ export COGL_DRIVER=gles2
+ export COGL_DEBUG=disable-npot-textures
+ run_test $test gles2_npot
+ fi
+
+ printf $TITLE_FORMAT "$test:"
+ if test $HAVE_GL -eq 1; then
+ printf "$GL_FORMAT" \
+ "`get_status $gl_ff_result`" \
+ "`get_status $gl_arbfp_result`" \
+ "`get_status $gl_glsl_result`" \
+ "`get_status $gl_npot_result`" \
+ "`get_status $gl3_result`"
+ fi
+ if test $HAVE_GLES2 -eq 1; then
+ printf "$GLES2_FORMAT" \
+ "`get_status $gles2_result`" \
+ "`get_status $gles2_npot_result`"
+ fi
+ echo ""
+done
+
+exit $EXIT
diff --git a/cogl/tests/test-launcher.sh b/cogl/tests/test-launcher.sh
new file mode 100755
index 000000000..e159e2e49
--- /dev/null
+++ b/cogl/tests/test-launcher.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+TEST_BINARY=$1
+shift
+
+SYMBOL_PREFIX=$1
+shift
+
+UNIT_TEST=$1
+shift
+
+test -z ${UNIT_TEST} && {
+ echo "Usage: $0 UNIT_TEST"
+ exit 1
+}
+
+BINARY_NAME=`basename $TEST_BINARY`
+UNIT_TEST=`echo $UNIT_TEST|sed 's/-/_/g'`
+
+echo "Running: ./$BINARY_NAME ${UNIT_TEST} $@"
+echo ""
+COGL_TEST_VERBOSE=1 $TEST_BINARY ${UNIT_TEST} "$@"
+exit_val=$?
+
+if test $exit_val -eq 0; then
+ echo "OK"
+fi
+
+echo ""
+echo "NOTE: For debugging purposes, you can run this single test as follows:"
+echo "$ libtool --mode=execute \\"
+echo " gdb --eval-command=\"start\" --eval-command=\"b ${UNIT_TEST#${SYMBOL_PREFIX}}\" \\"
+echo " --args ./$BINARY_NAME ${UNIT_TEST}"
+echo "or:"
+echo "$ env G_SLICE=always-malloc \\"
+echo " libtool --mode=execute \\"
+echo " valgrind ./$BINARY_NAME ${UNIT_TEST}"
+
+exit $exit_val
diff --git a/cogl/tests/unit/Makefile.am b/cogl/tests/unit/Makefile.am
new file mode 100644
index 000000000..5c1c08100
--- /dev/null
+++ b/cogl/tests/unit/Makefile.am
@@ -0,0 +1,83 @@
+NULL =
+
+noinst_PROGRAMS = test-unit
+
+test_unit_SOURCES = test-unit-main.c
+
+SHEXT = $(EXEEXT)
+
+# For convenience, this provides a way to easily run individual unit tests:
+.PHONY: wrappers clean-wrappers
+
+wrappers: stamp-test-unit
+ @true
+stamp-test-unit: Makefile test-unit$(EXEEXT)
+ @mkdir -p wrappers
+ . $(top_builddir)/cogl/libmutter-cogl.la ; \
+ $(NM) $(top_builddir)/cogl/.libs/"$$dlname"| \
+ grep '[DR] _\?unit_test_'|sed 's/.\+ [DR] _\?//' > unit-tests
+ @chmod +x $(top_srcdir)/tests/test-launcher.sh
+ @( echo "/stamp-test-unit" ; \
+ echo "/test-unit$(EXEEXT)" ; \
+ echo "*.o" ; \
+ echo ".gitignore" ; \
+ echo "unit-tests" ; ) > .gitignore
+ @for i in `cat unit-tests`; \
+ do \
+ unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \
+ echo " GEN $$unit"; \
+ ( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-unit$(EXEEXT) 'unit_test_' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
+ chmod +x $$unit$(SHEXT); \
+ echo "/$$unit$(SHEXT)" >> .gitignore; \
+ done \
+ && echo timestamp > $(@F)
+
+clean-wrappers:
+ @for i in `cat unit-tests`; \
+ do \
+ unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \
+ echo " RM $$unit"; \
+ rm -f $$unit$(SHEXT) ; \
+ done \
+ && rm -f unit-tests \
+ && rm -f stamp-test-unit
+
+# NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting
+# a phony rule that will generate symlink scripts for running individual tests
+BUILT_SOURCES = wrappers
+
+# The include of the $(buildir)/cogl directory here is to make it so
+# that tests that directly include Cogl source code for whitebox
+# testing (such as test-bitmask) will still compile
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/test-fixtures \
+ -I$(top_builddir)/cogl
+
+AM_CPPFLAGS += \
+ -DCOGL_DISABLE_DEPRECATED \
+ -DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" \
+ -DCOGL_COMPILATION
+
+test_unit_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+test_unit_LDADD = \
+ $(COGL_DEP_LIBS) \
+ $(top_builddir)/cogl/libmutter-cogl.la \
+ $(LIBM)
+test_unit_LDFLAGS = -export-dynamic
+
+test: wrappers
+ @$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-unit$(EXEEXT)
+
+# XXX: we could prevent the unit test suite from running
+# by simply defining this variable conditionally
+TEST_PROGS = test-unit
+
+.PHONY: test
+
+DISTCLEANFILES = .gitignore
+
+# we override the clean-generic target to clean up the wrappers so
+# we cannot use CLEANFILES
+clean-generic: clean-wrappers
+ $(QUIET_RM)rm -f .log
diff --git a/cogl/tests/unit/test-unit-main.c b/cogl/tests/unit/test-unit-main.c
new file mode 100644
index 000000000..b1f78645e
--- /dev/null
+++ b/cogl/tests/unit/test-unit-main.c
@@ -0,0 +1,45 @@
+#include <config.h>
+
+#include <gmodule.h>
+
+#include <test-fixtures/test-unit.h>
+#include <stdlib.h>
+
+int
+main (int argc, char **argv)
+{
+ GModule *main_module;
+ const CoglUnitTest *unit_test;
+ int i;
+
+ if (argc != 2)
+ {
+ g_printerr ("usage %s UNIT_TEST\n", argv[0]);
+ exit (1);
+ }
+
+ /* Just for convenience in case people try passing the wrapper
+ * filenames for the UNIT_TEST argument we normalize '-' characters
+ * to '_' characters... */
+ for (i = 0; argv[1][i]; i++)
+ {
+ if (argv[1][i] == '-')
+ argv[1][i] = '_';
+ }
+
+ main_module = g_module_open (NULL, /* use main module */
+ 0 /* flags */);
+
+ if (!g_module_symbol (main_module, argv[1], (void **) &unit_test))
+ {
+ g_printerr ("Unknown test name \"%s\"\n", argv[1]);
+ return 1;
+ }
+
+ test_utils_init (unit_test->requirement_flags,
+ unit_test->known_failure_flags);
+ unit_test->run ();
+ test_utils_fini ();
+
+ return 0;
+}