summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Bragg <robert@linux.intel.com>2011-05-05 23:34:38 +0100
committerRobert Bragg <robert@linux.intel.com>2011-09-08 12:50:04 +0100
commit4dd2269701438a4917286dab565f8e0acb6e9b03 (patch)
treec8e564b4a11e673010b8e45da350a4e169bb438d
parent6228c9f53c78b09abd90575b8e2ab6e3cf50868d (diff)
downloadcogl-wip/port-clutter-tests.tar.gz
Starts porting Cogl conformance tests from Clutterwip/port-clutter-tests
This makes a start on porting the Cogl conformance tests that use to live in the Clutter repository to be standalone Cogl tests that no longer require a ClutterStage. The main thing is that this commit brings in the basic testing infrastructure we need, so no we can port more and more tests incrementally.
-rw-r--r--.gitignore27
-rw-r--r--Makefile.am2
-rw-r--r--cogl/cogl-xlib-renderer.c5
-rw-r--r--configure.ac4
-rw-r--r--tests/Makefile.am16
-rw-r--r--tests/README31
-rw-r--r--tests/conform/Makefile.am236
-rwxr-xr-xtests/conform/run-tests.sh12
-rw-r--r--tests/conform/test-atlas-migration.c133
-rw-r--r--tests/conform/test-backface-culling.c339
-rw-r--r--tests/conform/test-blend-strings.c434
-rw-r--r--tests/conform/test-conform-main.c170
-rw-r--r--tests/conform/test-depth-test.c291
-rw-r--r--tests/conform/test-fixed.c18
-rw-r--r--tests/conform/test-fixtures.c12
-rw-r--r--tests/conform/test-just-vertex-shader.c137
-rwxr-xr-xtests/conform/test-launcher.sh.in25
-rw-r--r--tests/conform/test-materials.c285
-rw-r--r--tests/conform/test-multitexture.c206
-rw-r--r--tests/conform/test-npot-texture.c236
-rw-r--r--tests/conform/test-object.c86
-rw-r--r--tests/conform/test-offscreen.c167
-rw-r--r--tests/conform/test-path.c234
-rw-r--r--tests/conform/test-pipeline-user-matrix.c144
-rw-r--r--tests/conform/test-pixel-buffer.c330
-rw-r--r--tests/conform/test-premult.c367
-rw-r--r--tests/conform/test-primitive.c230
-rw-r--r--tests/conform/test-readpixels.c178
-rw-r--r--tests/conform/test-sub-texture.c371
-rw-r--r--tests/conform/test-texture-3d.c230
-rw-r--r--tests/conform/test-texture-get-set-data.c166
-rw-r--r--tests/conform/test-texture-mipmaps.c136
-rw-r--r--tests/conform/test-texture-pixmap-x11.c245
-rw-r--r--tests/conform/test-texture-rectangle.c276
-rw-r--r--tests/conform/test-utils.c80
-rw-r--r--tests/conform/test-utils.h41
-rw-r--r--tests/conform/test-vertex-buffer-contiguous.c257
-rw-r--r--tests/conform/test-vertex-buffer-interleved.c162
-rw-r--r--tests/conform/test-vertex-buffer-mutability.c198
-rw-r--r--tests/conform/test-viewport.c416
-rw-r--r--tests/conform/test-wrap-modes.c317
-rw-r--r--tests/data/Makefile.am3
-rw-r--r--tests/data/valgrind.suppressions173
43 files changed, 7425 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 5a17a47b..18ddd29b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,3 +90,30 @@ TAGS
*.rej
.DS_Store
.testlogs-*
+tests/conform/test-cogl-atlas-migration
+tests/conform/test-cogl-backface-culling
+tests/conform/test-cogl-blend-strings
+tests/conform/test-cogl-fixed
+tests/conform/test-cogl-just-vertex-shader
+tests/conform/test-cogl-materials
+tests/conform/test-cogl-multitexture
+tests/conform/test-cogl-npot-texture
+tests/conform/test-cogl-object
+tests/conform/test-cogl-offscreen
+tests/conform/test-cogl-path
+tests/conform/test-cogl-pipeline-user-matrix
+tests/conform/test-cogl-pixel-array
+tests/conform/test-cogl-premult
+tests/conform/test-cogl-primitive
+tests/conform/test-cogl-readpixels
+tests/conform/test-cogl-sub-texture
+tests/conform/test-cogl-texture-3d
+tests/conform/test-cogl-texture-get-set-data
+tests/conform/test-cogl-texture-mipmaps
+tests/conform/test-cogl-texture-pixmap-x11
+tests/conform/test-cogl-texture-rectangle
+tests/conform/test-cogl-vertex-buffer-contiguous
+tests/conform/test-cogl-vertex-buffer-interleved
+tests/conform/test-cogl-vertex-buffer-mutability
+tests/conform/test-cogl-viewport
+tests/conform/test-cogl-wrap-modes
diff --git a/Makefile.am b/Makefile.am
index 2806a54e..dd34ca28 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = cogl
+SUBDIRS = cogl tests
if BUILD_COGL_PANGO
SUBDIRS += cogl-pango
diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c
index 8b4d3ccc..92fe5ff5 100644
--- a/cogl/cogl-xlib-renderer.c
+++ b/cogl/cogl-xlib-renderer.c
@@ -40,6 +40,8 @@
#include <X11/Xlib.h>
#include <X11/extensions/Xdamage.h>
+#include <stdlib.h>
+
static char *_cogl_x11_display_name = NULL;
static GList *_cogl_xlib_renderers = NULL;
@@ -163,6 +165,9 @@ _cogl_xlib_renderer_connect (CoglRenderer *renderer, GError **error)
if (!assert_xlib_display (renderer, error))
return FALSE;
+ if (getenv ("COGL_X11_SYNC"))
+ XSynchronize (xlib_renderer->xdpy, TRUE);
+
/* Check whether damage events are supported on this display */
if (!XDamageQueryExtension (xlib_renderer->xdpy,
&x11_renderer->damage_base,
diff --git a/configure.ac b/configure.ac
index bc7110d6..c230638e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -968,6 +968,10 @@ doc/reference/cogl/cogl-docs.xml
doc/reference/cogl-2.0-experimental/Makefile
doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml
examples/Makefile
+tests/Makefile
+tests/conform/Makefile
+tests/conform/test-launcher.sh
+tests/data/Makefile
po/Makefile.in
)
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 00000000..8bf6ba23
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = conform data
+
+DIST_SUBDIRS = conform data
+
+EXTRA_DIST = README
+
+test conform:
+ ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+
+test-report full-report:
+ ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+
+.PHONY: test conform test-report full-report
+
+# run make test as part of make check
+check-local: test
diff --git a/tests/README b/tests/README
new file mode 100644
index 00000000..a8c01e7f
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,31 @@
+Outline of test categories:
+
+The conform/ tests should be non-interactive unit-tests that verify a single
+feature is behaving as documented. See conform/ADDING_NEW_TESTS for more
+details.
+
+The micro-bench/ tests should be focused performance test, 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 contains optional data (like images) that can be
+referenced by a test.
+
+Other 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/tests/conform/Makefile.am b/tests/conform/Makefile.am
new file mode 100644
index 00000000..54a48923
--- /dev/null
+++ b/tests/conform/Makefile.am
@@ -0,0 +1,236 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+NULL =
+
+noinst_PROGRAMS = test-conformance
+
+common_sources = \
+ test-utils.h \
+ test-utils.c \
+ test-conform-main.c \
+ $(NULL)
+
+unported_test_sources = \
+ test-backface-culling.c \
+ test-blend-strings.c \
+ test-fixed.c \
+ test-materials.c \
+ test-pipeline-user-matrix.c \
+ test-viewport.c \
+ test-multitexture.c \
+ test-npot-texture.c \
+ test-object.c \
+ test-offscreen.c \
+ test-path.c \
+ test-pixel-buffer.c \
+ test-premult.c \
+ test-readpixels.c \
+ test-sub-texture.c \
+ test-texture-3d.c \
+ test-texture-get-set-data.c \
+ test-texture-mipmaps.c \
+ test-texture-pixmap-x11.c \
+ test-texture-rectangle.c \
+ test-atlas-migration.c \
+ test-vertex-buffer-contiguous.c \
+ test-vertex-buffer-interleved.c \
+ test-vertex-buffer-mutability.c \
+ test-wrap-modes.c \
+ test-primitive.c \
+ test-just-vertex-shader.c \
+ $(NULL)
+
+test_sources = \
+ test-depth-test.c \
+ $(NULL)
+
+test_conformance_SOURCES = $(common_sources) $(test_sources)
+
+if OS_WIN32
+SHEXT =
+else
+SHEXT = $(EXEEXT)
+endif
+
+# For convenience, this provides a way to easily run individual unit tests:
+.PHONY: wrappers clean-wrappers
+
+#UNIT_TESTS = `./test-conformance -l -m thorough | $(GREP) '^/'`
+
+wrappers: stamp-test-conformance
+ @true
+stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c
+ @mkdir -p wrappers
+ @sed -n \
+ -e 's/^ \{1,\}ADD_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+ -e 's/^ \{1,\}ADD_CONDITIONAL_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+ -e 's/^ \{1,\}ADD_TODO_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+ $(srcdir)/test-conform-main.c > unit-tests
+ @chmod +x test-launcher.sh
+ @( echo "/stamp-test-conformance" ; \
+ echo "/test-conformance" ; \
+ echo "*.o" ; \
+ echo "*.xml" ; \
+ echo "*.html" ; \
+ echo ".gitignore" ; \
+ echo "unit-tests" ; \
+ echo "/wrappers/" ) > .gitignore
+ @for i in `cat unit-tests`; \
+ do \
+ unit=`basename $$i | sed -e s/_/-/g`; \
+ echo " GEN $$unit"; \
+ ( echo "#!/bin/sh" ; echo "$(abs_builddir)/test-launcher.sh '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
+ ( echo "#!/bin/sh" ; echo "exec $(abs_builddir)/test-conformance$(EXEEXT) -p $$i \"\$$@\"" ) > wrappers/$$unit$(SHEXT) ; \
+ chmod +x $$unit$(SHEXT); \
+ chmod +x wrappers/$$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) ; \
+ rm -f wrappers/$$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
+
+INCLUDES = -I$(top_srcdir)
+
+test_conformance_CPPFLAGS = \
+ -DG_DISABLE_SINGLE_INCLUDES \
+ -DCOGL_ENABLE_EXPERIMENTAL_API \
+ -DCOGL_DISABLE_DEPRECATED \
+ -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/libcogl.la
+test_conformance_LDFLAGS = -export-dynamic
+
+test: wrappers
+ @$(top_srcdir)/tests/conform/run-tests.sh \
+ ./test-conformance$(EXEEXT) -o test-report.xml
+
+test-verbose: wrappers
+ @$(top_srcdir)/tests/conform/run-tests.sh \
+ ./test-conformance$(EXEEXT) -o test-report.xml --verbose
+
+GTESTER = gtester
+GTESTER_REPORT = gtester-report
+
+# XXX: we could prevent the conformance test suite from running
+# by simply defining this variable conditionally
+TEST_PROGS = test-conformance
+
+.PHONY: test
+.PHONY: test-report perf-report full-report
+.PHONY: test-report-npot perf-report-npot full-report-npot
+
+# test-report: run tests and generate report
+# perf-report: run tests with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report: ${TEST_PROGS}
+ @test -z "${TEST_PROGS}" || { \
+ export GTESTER_LOGDIR=`mktemp -d "$(srcdir)/.testlogs-XXXXXX"` ; \
+ if test -d "$(top_srcdir)/.git"; then \
+ export REVISION="`git describe`" ; \
+ else \
+ export REVISION="$(VERSION) $(CLUTTER_RELEASE_STATUS)" ; \
+ fi ; \
+ export TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S%z` ; \
+ case $@ in \
+ test-report) test_options="-k";; \
+ perf-report) test_options="-k -m=perf";; \
+ full-report) test_options="-k -m=perf -m=slow";; \
+ esac ; \
+ $(top_srcdir)/tests/conform/run-tests.sh \
+ ./test-conformance$(EXEEXT) \
+ --verbose \
+ $$test_options \
+ -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ; \
+ echo '<?xml version="1.0"?>' > $@.xml ; \
+ echo '<report-collection>' >> $@.xml ; \
+ echo '<info>' >> $@.xml ; \
+ echo ' <package>$(PACKAGE)</package>' >> $@.xml ; \
+ echo ' <version>$(VERSION)</version>' >> $@.xml ; \
+ echo " <revision>$$REVISION</revision>" >> $@.xml ; \
+ echo " <date>$$TIMESTAMP</date>" >> $@.xml ; \
+ echo '</info>' >> $@.xml ; \
+ for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+ sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \
+ done ; \
+ echo >> $@.xml ; \
+ echo '</report-collection>' >> $@.xml ; \
+ ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \
+ rm -rf "$$GTESTER_LOGDIR" ; \
+ }
+
+# same as above, but with a wrapper that forcibly disables non-power of
+# two textures
+test-report-npot perf-report-npot full-report-npot: ${TEST_PROGS}
+ @test -z "${TEST_PROGS}" || { \
+ export COGL_DEBUG="$COGL_DEBUG,disable-npot-textures"; \
+ export GTESTER_LOGDIR=`mktemp -d "$(srcdir)/.testlogs-XXXXXX"` ; \
+ if test -d "$(top_srcdir)/.git"; then \
+ export REVISION="`git describe`" ; \
+ else \
+ export REVISION="$(VERSION) $(CLUTTER_RELEASE_STATUS)" ; \
+ fi ; \
+ export TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S%z` ; \
+ case $@ in \
+ test-report-npot) test_options="-k";; \
+ perf-report-npot) test_options="-k -m=perf";; \
+ full-report-npot) test_options="-k -m=perf -m=slow";; \
+ esac ; \
+ $(top_srcdir)/tests/conform/run-tests.sh \
+ ./test-conformance$(EXEEXT) \
+ --verbose \
+ $$test_options \
+ -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ; \
+ echo '<?xml version="1.0"?>' > $@.xml ; \
+ echo '<report-collection>' >> $@.xml ; \
+ echo '<info>' >> $@.xml ; \
+ echo ' <package>$(PACKAGE)</package>' >> $@.xml ; \
+ echo ' <version>$(VERSION)</version>' >> $@.xml ; \
+ echo " <revision>$$REVISION</revision>" >> $@.xml ; \
+ echo " <date>$$TIMESTAMP</date>" >> $@.xml ; \
+ echo '</info>' >> $@.xml ; \
+ for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+ sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \
+ done ; \
+ echo >> $@.xml ; \
+ echo '</report-collection>' >> $@.xml ; \
+ ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \
+ rm -rf "$$GTESTER_LOGDIR" ; \
+ }
+
+XML_REPORTS = \
+ test-report.xml \
+ perf-report.xml \
+ full-report.xml \
+ test-report-npot.xml \
+ perf-report-npot.xml \
+ full-report-npot.xml
+
+HTML_REPORTS = \
+ test-report.html \
+ perf-report.html \
+ full-report.html \
+ test-report-npot.html \
+ perf-report-npot.html \
+ full-report-npot.html
+
+EXTRA_DIST = ADDING_NEW_TESTS test-launcher.sh.in run-tests.sh
+DISTCLEANFILES = test-launcher.sh .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 $(XML_REPORTS) $(HTML_REPORTS)
diff --git a/tests/conform/run-tests.sh b/tests/conform/run-tests.sh
new file mode 100755
index 00000000..1104953a
--- /dev/null
+++ b/tests/conform/run-tests.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+BINARY=$1
+shift
+
+TMP=`./$BINARY -l -m thorough | grep '^/' | sed -e s/_/-/g`
+for i in $TMP
+do
+ TESTS="$TESTS wrappers/`basename $i`"
+done
+
+exec gtester "$@" $TESTS
diff --git a/tests/conform/test-atlas-migration.c b/tests/conform/test-atlas-migration.c
new file mode 100644
index 00000000..8b6cd985
--- /dev/null
+++ b/tests/conform/test-atlas-migration.c
@@ -0,0 +1,133 @@
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+#define N_TEXTURES 128
+
+#define OPACITY_FOR_ROW(y) \
+ (0xff - ((y) & 0xf) * 0x10)
+
+#define COLOR_FOR_SIZE(size) \
+ (colors + (size) % 3)
+
+static const ClutterColor colors[] =
+ { { 0xff, 0x00, 0x00, 0xff },
+ { 0x00, 0xff, 0x00, 0xff },
+ { 0x00, 0x00, 0xff, 0xff } };
+
+static CoglHandle
+create_texture (int size)
+{
+ CoglHandle texture;
+ const ClutterColor *color;
+ guint8 *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 = cogl_texture_new_from_data (size, /* width */
+ size, /* height */
+ COGL_TEXTURE_NONE, /* flags */
+ /* format */
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ /* internal format */
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ /* rowstride */
+ size * 4,
+ data);
+
+ g_free (data);
+
+ return texture;
+}
+
+static void
+verify_texture (CoglHandle texture, int size)
+{
+ guint8 *data, *p;
+ int x, y;
+ const ClutterColor *color;
+
+ color = COLOR_FOR_SIZE (size);
+
+ p = data = g_malloc (size * size * 4);
+
+ cogl_texture_get_data (texture,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ size * 4,
+ data);
+
+ for (y = 0; y < size; y++)
+ {
+ int opacity = OPACITY_FOR_ROW (y);
+
+ for (x = 0; x < size; x++)
+ {
+ g_assert_cmpint (p[0], ==, color->red * opacity / 255);
+ g_assert_cmpint (p[1], ==, color->green * opacity / 255);
+ g_assert_cmpint (p[2], ==, color->blue * opacity / 255);
+ g_assert_cmpint (p[3], ==, opacity);
+
+ p += 4;
+ }
+ }
+
+ g_free (data);
+}
+
+void
+test_cogl_atlas_migration (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ CoglHandle 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 (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c
new file mode 100644
index 00000000..d4f3e0d9
--- /dev/null
+++ b/tests/conform/test-backface-culling.c
@@ -0,0 +1,339 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#ifdef CLUTTER_COGL_HAS_GL
+
+/* Size the texture so that it is just off a power of two to enourage
+ it so use software tiling when NPOTs aren't available */
+#define TEXTURE_SIZE 257
+
+#else /* CLUTTER_COGL_HAS_GL */
+
+/* We can't use the funny-sized texture on GL ES because it will break
+ cogl_texture_polygon. However there is only one code path for
+ rendering quads so there is no need */
+#define TEXTURE_SIZE 32
+
+#endif /* CLUTTER_COGL_HAS_GL */
+
+/* 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 32
+
+typedef struct _TestState
+{
+ CoglHandle texture;
+ CoglHandle offscreen;
+ CoglHandle offscreen_tex;
+} TestState;
+
+static gboolean
+validate_part (int xnum, int ynum, gboolean shown)
+{
+ guchar *pixels, *p;
+ gboolean ret = TRUE;
+
+ pixels = g_malloc0 ((TEXTURE_RENDER_SIZE - TEST_INSET * 2)
+ * (TEXTURE_RENDER_SIZE - TEST_INSET * 2) * 4);
+
+ /* Read the appropriate part but skip out a few pixels around the
+ edges */
+ cogl_read_pixels (xnum * TEXTURE_RENDER_SIZE + TEST_INSET,
+ ynum * TEXTURE_RENDER_SIZE + TEST_INSET,
+ TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+ TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixels);
+
+ /* Make sure every pixels is the appropriate color */
+ for (p = pixels;
+ p < pixels + ((TEXTURE_RENDER_SIZE - TEST_INSET * 2)
+ * (TEXTURE_RENDER_SIZE - TEST_INSET * 2));
+ p += 4)
+ {
+ if (p[0] != (shown ? 255 : 0))
+ ret = FALSE;
+ if (p[1] != 0)
+ ret = FALSE;
+ if (p[2] != 0)
+ ret = FALSE;
+ }
+
+ g_free (pixels);
+
+ return ret;
+}
+
+static void
+do_test_backface_culling (TestState *state)
+{
+ int i;
+ CoglHandle material = cogl_material_new ();
+
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+
+ cogl_set_backface_culling_enabled (TRUE);
+
+ cogl_push_matrix ();
+
+ /* Render the scene twice - once with backface culling enabled and
+ once without. The second time is translated so that it is below
+ the first */
+ for (i = 0; i < 2; i++)
+ {
+ float x1 = 0, x2, y1 = 0, y2 = (float)(TEXTURE_RENDER_SIZE);
+ CoglTextureVertex verts[4];
+
+ cogl_set_source (material);
+
+ memset (verts, 0, sizeof (verts));
+
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a front-facing texture */
+ cogl_material_set_layer (material, 0, state->texture);
+ cogl_rectangle (x1, y1, x2, y2);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a front-facing texture with flipped texcoords */
+ cogl_material_set_layer (material, 0, state->texture);
+ 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_material_set_layer (material, 0, state->texture);
+ cogl_rectangle (x2, y1, x1, y2);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a front-facing texture 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_material_set_layer (material, 0, state->texture);
+ cogl_polygon (verts, 4, FALSE);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a back-facing texture 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_material_set_layer (material, 0, state->texture);
+ cogl_polygon (verts, 4, FALSE);
+
+ x1 = x2;
+ x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+ /* Draw a regular rectangle (this should always show) */
+ cogl_set_source_color4f (1.0, 0, 0, 1.0);
+ cogl_rectangle (x1, y1, x2, y2);
+
+ /* The second time round draw beneath the first with backface
+ culling disabled */
+ cogl_translate (0, TEXTURE_RENDER_SIZE, 0);
+ cogl_set_backface_culling_enabled (FALSE);
+ }
+
+ cogl_handle_unref (material);
+
+ cogl_pop_matrix ();
+
+ /* Front-facing texture */
+ g_assert (validate_part (0, 0, TRUE));
+ /* Front-facing texture with flipped tex coords */
+ g_assert (validate_part (1, 0, TRUE));
+ /* Back-facing texture */
+ g_assert (validate_part (2, 0, FALSE));
+ /* Front-facing texture polygon */
+ g_assert (validate_part (3, 0, TRUE));
+ /* Back-facing texture polygon */
+ g_assert (validate_part (4, 0, FALSE));
+ /* Regular rectangle */
+ g_assert (validate_part (5, 0, TRUE));
+
+ /* Backface culling disabled - everything should be shown */
+
+ /* Front-facing texture */
+ g_assert (validate_part (0, 1, TRUE));
+ /* Front-facing texture with flipped tex coords */
+ g_assert (validate_part (1, 1, TRUE));
+ /* Back-facing texture */
+ g_assert (validate_part (2, 1, TRUE));
+ /* Front-facing texture polygon */
+ g_assert (validate_part (3, 1, TRUE));
+ /* Back-facing texture polygon */
+ g_assert (validate_part (4, 1, TRUE));
+ /* Regular rectangle */
+ g_assert (validate_part (5, 1, TRUE));
+
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglColor clr;
+ float stage_viewport[4];
+ CoglMatrix stage_projection;
+ CoglMatrix stage_modelview;
+
+ cogl_color_init_from_4ub (&clr, 0x00, 0x00, 0x00, 0xff);
+
+ do_test_backface_culling (state);
+
+ /* Since we are going to repeat the test rendering offscreen we clear the
+ * stage, just to minimize the chance of a some other bug causing us
+ * mistakenly reading back the results from the stage and giving a false
+ * posistive. */
+ cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL);
+
+ /*
+ * Now repeat the test but rendered to an offscreen framebuffer...
+ */
+
+ cogl_get_viewport (stage_viewport);
+ cogl_get_projection_matrix (&stage_projection);
+ cogl_get_modelview_matrix (&stage_modelview);
+
+ cogl_push_framebuffer (state->offscreen);
+
+ cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL);
+
+ cogl_set_viewport (stage_viewport[0],
+ stage_viewport[1],
+ stage_viewport[2],
+ stage_viewport[3]);
+ cogl_set_projection_matrix (&stage_projection);
+ cogl_set_modelview_matrix (&stage_modelview);
+
+ do_test_backface_culling (state);
+
+ cogl_pop_framebuffer ();
+
+ /* Incase we want feedback of what was drawn offscreen we draw it
+ * to the stage... */
+ cogl_set_source_texture (state->offscreen_tex);
+ cogl_rectangle (0, 0, stage_viewport[2], stage_viewport[3]);
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+static CoglHandle
+make_texture (void)
+{
+ guchar *tex_data, *p;
+ CoglHandle 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 = cogl_texture_new_from_data (TEXTURE_SIZE,
+ TEXTURE_SIZE,
+ COGL_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ TEXTURE_SIZE * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+void
+test_cogl_backface_culling (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ CoglHandle tex;
+ ClutterActor *stage;
+ float stage_width;
+ float stage_height;
+ const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ stage = clutter_stage_get_default ();
+ clutter_actor_get_size (stage, &stage_width, &stage_height);
+
+ state.offscreen = COGL_INVALID_HANDLE;
+
+ state.texture = make_texture ();
+
+ tex = cogl_texture_new_with_size (stage_width, stage_height,
+ COGL_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_ANY); /* internal fmt */
+ state.offscreen = cogl_offscreen_new_to_texture (tex);
+ state.offscreen_tex = tex;
+
+ 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);
+
+ cogl_handle_unref (state.offscreen);
+ cogl_handle_unref (state.offscreen_tex);
+ cogl_handle_unref (state.texture);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-blend-strings.c b/tests/conform/test-blend-strings.c
new file mode 100644
index 00000000..77d1dd67
--- /dev/null
+++ b/tests/conform/test-blend-strings.c
@@ -0,0 +1,434 @@
+
+#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)
+
+#define BLEND_CONSTANT_UNUSED 0xDEADBEEF
+#define TEX_CONSTANT_UNUSED 0xDEADBEEF
+
+typedef struct _TestState
+{
+ ClutterGeometry stage_geom;
+} TestState;
+
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 a = MASK_ALPHA (color);
+
+ if (g_test_verbose ())
+ g_print (" expected = %x, %x, %x, %x\n",
+ r, g, b, a);
+ /* FIXME - allow for hardware in-precision */
+ g_assert_cmpint (pixel[RED], ==, r);
+ g_assert_cmpint (pixel[GREEN], ==, g);
+ g_assert_cmpint (pixel[BLUE], ==, b);
+
+ /* FIXME
+ * We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+ /* g_assert (pixel[ALPHA] == a); */
+}
+
+static void
+test_blend (TestState *state,
+ int x,
+ int y,
+ guint32 src_color,
+ guint32 dst_color,
+ const char *blend_string,
+ guint32 blend_constant,
+ guint32 expected_result)
+{
+ /* src color */
+ guint8 Sr = MASK_RED (src_color);
+ guint8 Sg = MASK_GREEN (src_color);
+ guint8 Sb = MASK_BLUE (src_color);
+ guint8 Sa = MASK_ALPHA (src_color);
+ /* dest color */
+ guint8 Dr = MASK_RED (dst_color);
+ guint8 Dg = MASK_GREEN (dst_color);
+ guint8 Db = MASK_BLUE (dst_color);
+ guint8 Da = MASK_ALPHA (dst_color);
+ /* blend constant - when applicable */
+ guint8 Br = MASK_RED (blend_constant);
+ guint8 Bg = MASK_GREEN (blend_constant);
+ guint8 Bb = MASK_BLUE (blend_constant);
+ guint8 Ba = MASK_ALPHA (blend_constant);
+ CoglColor blend_const_color;
+
+ CoglHandle material;
+ gboolean status;
+ GError *error = NULL;
+ GLubyte pixel[4];
+ GLint y_off;
+ GLint x_off;
+
+ /* 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)
+ {
+ /* It's not strictly a test failure; you need a more capable GPU or
+ * driver to test this blend string. */
+ g_debug ("Failed to test blend string %s: %s",
+ blend_string, error->message);
+ }
+
+ 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... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ cogl_read_pixels (x_off, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (g_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");
+ g_print (" result = %x, %x, %x, %x\n",
+ pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+ }
+
+ check_pixel (pixel, expected_result);
+}
+
+static CoglHandle
+make_texture (guint32 color)
+{
+ guchar *tex_data, *p;
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 a = MASK_ALPHA (color);
+ CoglHandle 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 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 = cogl_texture_new_from_data (QUAD_WIDTH,
+ QUAD_WIDTH,
+ COGL_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ QUAD_WIDTH * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+static void
+test_tex_combine (TestState *state,
+ int x,
+ int y,
+ guint32 tex0_color,
+ guint32 tex1_color,
+ guint32 combine_constant,
+ const char *combine_string,
+ guint32 expected_result)
+{
+ CoglHandle tex0, tex1;
+
+ /* combine constant - when applicable */
+ guint8 Cr = MASK_RED (combine_constant);
+ guint8 Cg = MASK_GREEN (combine_constant);
+ guint8 Cb = MASK_BLUE (combine_constant);
+ guint8 Ca = MASK_ALPHA (combine_constant);
+ CoglColor combine_const_color;
+
+ CoglHandle material;
+ gboolean status;
+ GError *error = NULL;
+ GLubyte pixel[4];
+ GLint y_off;
+ GLint 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_handle_unref (tex0);
+ cogl_handle_unref (tex1);
+
+ /* See what we got... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ cogl_read_pixels (x_off, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (g_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");
+ g_print (" result = %02x, %02x, %02x, %02x\n",
+ pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+ }
+
+ check_pixel (pixel, expected_result);
+}
+
+static void
+on_paint (ClutterActor *actor, 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 */
+
+ /* Comment this out if you want visual feedback for what this test paints */
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_blend_strings (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 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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
new file mode 100644
index 00000000..1dd9b68d
--- /dev/null
+++ b/tests/conform/test-conform-main.c
@@ -0,0 +1,170 @@
+#include "config.h"
+
+#include <cogl/cogl.h>
+
+#include <glib.h>
+#include <locale.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+#if 0
+void
+skip_init (TestUtilsGTestFixture *fixture,
+ const void *data)
+{
+ /* void */
+}
+
+static void
+skip_test (TestUtilsGTestFixture *fixture,
+ const void *data)
+{
+ /* void */
+}
+
+void
+skip_fini (TestUtilsGTestFixture *fixture,
+ const void *data)
+{
+ /* void */
+}
+#endif
+
+static void
+run_todo_test (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+#ifdef G_OS_UNIX
+ TestUtilsSharedState *state = data;
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
+ {
+ state->todo_func (fixture, data);
+ exit (0);
+ }
+
+ g_test_trap_assert_failed ();
+#endif
+}
+
+void
+verify_failure (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ g_assert (FALSE);
+}
+
+static TestUtilsSharedState *shared_state = NULL;
+
+/* This is a bit of sugar for adding new conformance tests:
+ *
+ * - It adds an extern function definition just to save maintaining a header
+ * that lists test entry points.
+ * - It sets up callbacks for a fixture, which lets us share initialization
+ * code between tests. (see test-utils.c)
+ * - It passes in a shared data pointer that is initialised once in main(),
+ * that gets passed to the fixture setup and test functions. (See the
+ * definition in test-utils.h)
+ */
+#define ADD_TEST(NAMESPACE, FUNC) G_STMT_START { \
+ extern void FUNC (TestUtilsGTestFixture *, void *); \
+ g_test_add ("/conform" NAMESPACE "/" #FUNC, \
+ TestUtilsGTestFixture, \
+ shared_state, /* data argument for test */ \
+ test_utils_init, \
+ (void *)(FUNC), \
+ test_utils_fini); } G_STMT_END
+
+/* this is a macro that conditionally executes a test if CONDITION
+ * evaluates to TRUE; otherwise, it will put the test under the
+ * "/skip" namespace and execute a dummy function that will always
+ * pass.
+ */
+#define ADD_CONDITIONAL_TEST(CONDITION, NAMESPACE, FUNC) G_STMT_START { \
+ if (!(CONDITION)) { \
+ g_test_add ("/skipped" NAMESPACE "/" #FUNC, \
+ TestUtilsGTestFixture, \
+ shared_state, /* data argument for test */ \
+ skip_init, \
+ skip_test, \
+ skip_fini); \
+ } else { ADD_TEST (NAMESPACE, FUNC); } } G_STMT_END
+
+#define ADD_TODO_TEST(NAMESPACE, FUNC) G_STMT_START { \
+ extern void FUNC (TestUtilsGTestFixture *, void *); \
+ shared_state->todo_func = FUNC; \
+ g_test_add ("/todo" NAMESPACE "/" #FUNC, \
+ TestUtilsGTestFixture, \
+ shared_state, \
+ test_utils_init, \
+ (void *)(run_todo_test), \
+ test_utils_fini); } G_STMT_END
+
+#define UNPORTED_TEST(NAMESPACE, FUNC)
+
+gchar *
+clutter_test_get_data_file (const gchar *filename)
+{
+ return g_build_filename (TESTS_DATADIR, filename, NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
+
+ /* Initialise the state you need to share with everything.
+ */
+ shared_state = g_new0 (TestUtilsSharedState, 1);
+ shared_state->argc_addr = &argc;
+ shared_state->argv_addr = &argv;
+
+ /* 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. To comment out a test use the SKIP or TODO macros. Using
+ * #if 0 would break the script. */
+
+ /* sanity check for the test suite itself */
+ ADD_TODO_TEST ("/suite", verify_failure);
+
+ UNPORTED_TEST ("/cogl", test_cogl_object);
+ UNPORTED_TEST ("/cogl", test_cogl_fixed);
+ UNPORTED_TEST ("/cogl", test_cogl_backface_culling);
+ UNPORTED_TEST ("/cogl", test_cogl_materials);
+ UNPORTED_TEST ("/cogl", test_cogl_pipeline_user_matrix);
+ UNPORTED_TEST ("/cogl", test_cogl_blend_strings);
+ UNPORTED_TEST ("/cogl", test_cogl_premult);
+ UNPORTED_TEST ("/cogl", test_cogl_readpixels);
+ UNPORTED_TEST ("/cogl", test_cogl_path);
+ ADD_TEST ("/cogl", test_cogl_depth_test);
+
+ UNPORTED_TEST ("/cogl/texture", test_cogl_npot_texture);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_multitexture);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_texture_mipmaps);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_sub_texture);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_pixel_array);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_texture_rectangle);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_texture_3d);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_wrap_modes);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_texture_pixmap_x11);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_texture_get_set_data);
+ UNPORTED_TEST ("/cogl/texture", test_cogl_atlas_migration);
+
+ UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous);
+ UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved);
+ UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_mutability);
+
+ UNPORTED_TEST ("/cogl/vertex-array", test_cogl_primitive);
+
+ UNPORTED_TEST ("/cogl/shaders", test_cogl_just_vertex_shader);
+
+ /* left to the end because they aren't currently very orthogonal and tend to
+ * break subsequent tests! */
+ UNPORTED_TEST ("/cogl", test_cogl_viewport);
+ UNPORTED_TEST ("/cogl", test_cogl_offscreen);
+
+ return g_test_run ();
+}
diff --git a/tests/conform/test-depth-test.c b/tests/conform/test-depth-test.c
new file mode 100644
index 00000000..1cbfd967
--- /dev/null
+++ b/tests/conform/test-depth-test.c
@@ -0,0 +1,291 @@
+#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 width;
+ int height;
+} TestState;
+
+typedef struct
+{
+ guint32 color;
+ float depth;
+ gboolean test_enable;
+ CoglDepthTestFunction test_function;
+ gboolean write_enable;
+ float range_near;
+ float range_far;
+} TestDepthState;
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 a = MASK_ALPHA (color);
+
+ if (g_test_verbose ())
+ g_print (" expected = %x, %x, %x, %x\n",
+ r, g, b, a);
+ /* FIXME - allow for hardware in-precision */
+ g_assert_cmpint (pixel[RED], ==, r);
+ g_assert_cmpint (pixel[GREEN], ==, g);
+ g_assert_cmpint (pixel[BLUE], ==, b);
+
+ /* FIXME
+ * We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+ /* g_assert (pixel[ALPHA] == a); */
+}
+
+static gboolean
+draw_rectangle (TestState *state,
+ int x,
+ int y,
+ TestDepthState *rect_state)
+{
+ guint8 Cr = MASK_RED (rect_state->color);
+ guint8 Cg = MASK_GREEN (rect_state->color);
+ guint8 Cb = MASK_BLUE (rect_state->color);
+ guint8 Ca = MASK_ALPHA (rect_state->color);
+ CoglHandle 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 ();
+ if (!cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL))
+ {
+ cogl_object_unref (pipeline);
+ return FALSE;
+ }
+
+ cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca);
+
+ cogl_set_source (pipeline);
+
+ cogl_push_matrix ();
+ 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_object_unref (pipeline);
+
+ return TRUE;
+}
+
+static void
+test_depth (TestState *state,
+ int x,
+ int y,
+ TestDepthState *rect0_state,
+ TestDepthState *rect1_state,
+ TestDepthState *rect2_state,
+ guint32 expected_result)
+{
+ GLubyte pixel[4];
+ int y_off;
+ int x_off;
+ gboolean missing_feature = FALSE;
+
+ if (rect0_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect0_state);
+ if (rect1_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect1_state);
+ if (rect2_state)
+ missing_feature |= !draw_rectangle (state, x, y, rect2_state);
+
+ /* We don't consider it an error that we can't test something
+ * the driver doesn't support. */
+ if (missing_feature)
+ return;
+
+ /* See what we got... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ cogl_read_pixels (x_off, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+
+ check_pixel (pixel, expected_result);
+}
+
+static void
+paint (TestState *state)
+{
+ CoglMatrix identity;
+
+ cogl_ortho (0, state->width, /* left, right */
+ state->height, 0, /* bottom, top */
+ -1, 100 /* z near, far */);
+
+ cogl_push_matrix ();
+ cogl_matrix_init_identity (&identity);
+ cogl_set_modelview_matrix (&identity);
+
+ /* 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 */
+ 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 */
+ 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 */
+ 0, 1 /* depth range */
+ };
+
+ test_depth (state, 0, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ 0x00ff00ff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS;
+ test_depth (state, 1, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ 0x0000ffff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS;
+ test_depth (state, 2, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ 0x0000ffff); /* expected */
+
+ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER;
+ test_depth (state, 3, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ 0x00ff00ff); /* expected */
+
+ rect0_state.test_enable = TRUE;
+ rect1_state.write_enable = FALSE;
+ test_depth (state, 4, 0, /* position */
+ &rect0_state, &rect1_state, &rect2_state,
+ 0x0000ffff); /* 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 */
+ 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 */
+ 0, 0.5 /* depth range */
+ };
+
+ test_depth (state, 0, 1, /* position */
+ &rect0_state, &rect1_state, NULL,
+ 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 */
+ 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 */
+ 0, 1 /* depth range */
+ };
+
+ cogl_set_depth_test_enabled (TRUE);
+ test_depth (state, 0, 2, /* position */
+ &rect0_state, &rect1_state, NULL,
+ 0xff0000ff); /* expected */
+ cogl_set_depth_test_enabled (FALSE);
+ test_depth (state, 1, 2, /* position */
+ &rect0_state, &rect1_state, NULL,
+ 0x00ff00ff); /* expected */
+ }
+
+ cogl_pop_matrix ();
+}
+
+void
+test_cogl_depth_test (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestUtilsSharedState *shared_state = data;
+ TestState state;
+
+ state.width = cogl_framebuffer_get_width (shared_state->fb);
+ state.height = cogl_framebuffer_get_height (shared_state->fb);
+ paint (&state);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-fixed.c b/tests/conform/test-fixed.c
new file mode 100644
index 00000000..78c34453
--- /dev/null
+++ b/tests/conform/test-fixed.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+void
+test_cogl_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/tests/conform/test-fixtures.c b/tests/conform/test-fixtures.c
new file mode 100644
index 00000000..24178d28
--- /dev/null
+++ b/tests/conform/test-fixtures.c
@@ -0,0 +1,12 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+void
+test_cogl_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/tests/conform/test-just-vertex-shader.c b/tests/conform/test-just-vertex-shader.c
new file mode 100644
index 00000000..886f354e
--- /dev/null
+++ b/tests/conform/test-just-vertex-shader.c
@@ -0,0 +1,137 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0xff, 0xff };
+
+static void
+draw_frame (void)
+{
+ CoglHandle material = cogl_material_new ();
+ CoglColor color;
+ GError *error = NULL;
+ CoglHandle shader, program;
+
+ /* 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 */
+ cogl_color_set_from_4ub (&color, 0x00, 0xff, 0x00, 0xff);
+ cogl_material_set_layer_combine_constant (material, 0, &color);
+ if (!cogl_material_set_layer_combine (material, 0,
+ "RGBA=REPLACE(CONSTANT)",
+ &error))
+ {
+ g_warning ("Error setting blend constant: %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"
+ "}\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
+validate_pixel (int x, int y)
+{
+ guint8 pixels[4];
+
+ cogl_read_pixels (x, y, 1, 1, COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE, pixels);
+
+ /* The final color should be green. If it's blue then the layer
+ state is being ignored. If it's green then the stage is showing
+ through */
+ g_assert_cmpint (pixels[0], ==, 0x00);
+ g_assert_cmpint (pixels[1], ==, 0xff);
+ g_assert_cmpint (pixels[2], ==, 0x00);
+}
+
+static void
+validate_result (void)
+{
+ /* Non-shader version */
+ validate_pixel (25, 25);
+ /* Shader version */
+ validate_pixel (75, 25);
+}
+
+static void
+on_paint (void)
+{
+ draw_frame ();
+
+ validate_result ();
+
+ /* Comment this out to see what the test paints */
+ clutter_main_quit ();
+}
+
+void
+test_cogl_just_vertex_shader (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ ClutterActor *stage;
+ unsigned int paint_handler;
+
+ stage = clutter_stage_get_default ();
+
+ /* If shaders aren't supported then we can't run the test */
+ if (cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
+ {
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ paint_handler = g_signal_connect_after (stage, "paint",
+ G_CALLBACK (on_paint), NULL);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_signal_handler_disconnect (stage, paint_handler);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+ }
+ else if (g_test_verbose ())
+ g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-launcher.sh.in b/tests/conform/test-launcher.sh.in
new file mode 100755
index 00000000..0be13bda
--- /dev/null
+++ b/tests/conform/test-launcher.sh.in
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+UNIT_TEST_PATH=$1
+shift
+
+test -z ${UNIT_TEST_PATH} && {
+ echo "Usage: $0 /path/to/unit_test"
+ exit 1
+}
+
+UNIT_TEST=`basename ${UNIT_TEST_PATH}`
+
+echo "Running: ./test-conformance -p ${UNIT_TEST_PATH} $@"
+echo ""
+@abs_builddir@/test-conformance -p ${UNIT_TEST_PATH} "$@"
+
+echo ""
+echo "NOTE: For debugging purposes, you can run this single test as follows:"
+echo "$ libtool --mode=execute \\"
+echo " gdb --eval-command=\"b ${UNIT_TEST}\" \\"
+echo " --args ./test-conformance -p ${UNIT_TEST_PATH}"
+echo "or:"
+echo "$ env G_SLICE=always-malloc \\"
+echo " libtool --mode=execute \\"
+echo " valgrind ./test-conformance -p ${UNIT_TEST_PATH}"
diff --git a/tests/conform/test-materials.c b/tests/conform/test-materials.c
new file mode 100644
index 00000000..5dbb30c0
--- /dev/null
+++ b/tests/conform/test-materials.c
@@ -0,0 +1,285 @@
+#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_pixel (TestState *state, int x, int y, guint32 color)
+{
+ GLint y_off;
+ GLint x_off;
+ GLubyte pixel[4];
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 a = MASK_ALPHA (color);
+
+ /* See what we got... */
+
+ /* NB: glReadPixels is done in GL screen space so y = 0 is at the bottom */
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ cogl_read_pixels (x_off, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (g_test_verbose ())
+ g_print (" result = %02x, %02x, %02x, %02x\n",
+ pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+
+ if (g_test_verbose ())
+ g_print (" expected = %x, %x, %x, %x\n",
+ r, g, b, a);
+ /* FIXME - allow for hardware in-precision */
+ g_assert (pixel[RED] == r);
+ g_assert (pixel[GREEN] == g);
+ g_assert (pixel[BLUE] == b);
+
+ /* FIXME
+ * We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+ /* g_assert (pixel[ALPHA] == a); */
+}
+
+static void
+test_material_with_primitives (TestState *state,
+ int x, int y,
+ guint32 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_pixel (state, x, y, color);
+ check_pixel (state, x, y+1, color);
+ check_pixel (state, 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, COGL_INVALID_HANDLE);
+
+ 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 ();
+ guint8 white_pixel[] = { 0xff, 0xff, 0xff, 0xff };
+ guint8 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 = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ 4, white_pixel);
+ red_texture = cogl_texture_new_from_data (1, 1, COGL_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, COGL_INVALID_HANDLE);
+
+ /* 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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-multitexture.c b/tests/conform/test-multitexture.c
new file mode 100644
index 00000000..79717bca
--- /dev/null
+++ b/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,
+ guint8 red,
+ guint8 green,
+ guint8 blue,
+ guint8 alpha)
+{
+ guint8 *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++)
+ {
+ guint8 *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 = cogl_texture_new_from_data (QUAD_WIDTH * 2,
+ QUAD_WIDTH * 2,
+ COGL_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;
+ gboolean status;
+ GError *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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-npot-texture.c b/tests/conform/test-npot-texture.c
new file mode 100644
index 00000000..49db94f8
--- /dev/null
+++ b/tests/conform/test-npot-texture.c
@@ -0,0 +1,236 @@
+#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 };
+
+/* 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 ClutterColor corner_colors[PARTS * PARTS] =
+ {
+ /* Top left - red */ { 255, 0, 0, 255 },
+ /* Top right - green */ { 0, 255, 0, 255 },
+ /* Bottom left - blue */ { 0, 0, 255, 255 },
+ /* Bottom right - yellow */ { 255, 255, 0, 255 }
+ };
+
+typedef struct _TestState
+{
+ unsigned int frame;
+ CoglHandle texture;
+} TestState;
+
+static gboolean
+validate_part (int xnum, int ynum, const ClutterColor *color)
+{
+ guchar *pixels, *p;
+ ClutterActor *stage = clutter_stage_get_default ();
+ gboolean ret = TRUE;
+
+ /* Read the appropriate part but skip out a few pixels around the
+ edges */
+ pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage),
+ xnum * PART_RENDER_SIZE + TEST_INSET,
+ ynum * PART_RENDER_SIZE + TEST_INSET,
+ PART_RENDER_SIZE - TEST_INSET * 2,
+ PART_RENDER_SIZE - TEST_INSET * 2);
+
+ /* Make sure every pixels is the appropriate color */
+ for (p = pixels;
+ p < pixels + ((PART_RENDER_SIZE - TEST_INSET * 2)
+ * (PART_RENDER_SIZE - TEST_INSET * 2));
+ p += 4)
+ {
+ if (p[0] != color->red)
+ ret = FALSE;
+ if (p[1] != color->green)
+ ret = FALSE;
+ if (p[2] != color->blue)
+ ret = FALSE;
+ }
+
+ g_free (pixels);
+
+ return ret;
+}
+
+static void
+validate_result (TestState *state)
+{
+ /* Validate that all four corners of the texture are drawn in the
+ right color */
+ g_assert (validate_part (0, 0, corner_colors + 0));
+ g_assert (validate_part (1, 0, corner_colors + 1));
+ g_assert (validate_part (0, 1, corner_colors + 2));
+ g_assert (validate_part (1, 1, corner_colors + 3));
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ int frame_num;
+ int y, x;
+
+ /* Just render the texture in the top left corner */
+ cogl_set_source_texture (state->texture);
+ /* Render the texture using four separate rectangles */
+ for (y = 0; y < 2; y++)
+ for (x = 0; x < 2; x++)
+ cogl_rectangle_with_texture_coords (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);
+
+ /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+ * another paint run so to avoid infinite recursion we only aim to validate
+ * the first frame. */
+ frame_num = state->frame++;
+ if (frame_num == 1)
+ validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+static CoglHandle
+make_texture (void)
+{
+ guchar *tex_data, *p;
+ CoglHandle 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++)
+ {
+ const ClutterColor *color = corner_colors + party * PARTS + partx;
+ width = (partx < PARTS - 1
+ ? PART_SIZE
+ : TEXTURE_SIZE - PART_SIZE * (PARTS - 1));
+
+ while (width-- > 0)
+ {
+ *(p++) = color->red;
+ *(p++) = color->green;
+ *(p++) = color->blue;
+ *(p++) = color->alpha;
+ }
+ }
+
+ while (--height > 0)
+ {
+ memcpy (p, p - TEXTURE_SIZE * 4, TEXTURE_SIZE * 4);
+ p += TEXTURE_SIZE * 4;
+ }
+ }
+
+ tex = cogl_texture_new_from_data (TEXTURE_SIZE,
+ TEXTURE_SIZE,
+ COGL_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ TEXTURE_SIZE * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ if (g_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_features_available (COGL_FEATURE_TEXTURE_NPOT)
+ ? !cogl_texture_is_sliced (tex)
+ : cogl_texture_is_sliced (tex));
+
+ return tex;
+}
+
+void
+test_cogl_npot_texture (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ if (g_test_verbose ())
+ {
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT))
+ g_print ("NPOT textures are supported\n");
+ else
+ g_print ("NPOT textures are not supported\n");
+ }
+
+ state.frame = 0;
+
+ state.texture = make_texture ();
+
+ 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);
+
+ cogl_handle_unref (state.texture);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-object.c b/tests/conform/test-object.c
new file mode 100644
index 00000000..b66b83e6
--- /dev/null
+++ b/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_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-offscreen.c b/tests/conform/test-offscreen.c
new file mode 100644
index 00000000..07ab6d63
--- /dev/null
+++ b/tests/conform/test-offscreen.c
@@ -0,0 +1,167 @@
+
+#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;
+ guint8 pixel[4];
+
+ /* 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);
+
+ data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+ tex = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_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_to_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 quater of the
+ * window size and slide it to the top right of the window.
+ */
+ 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_handle_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_handle_unref (tex);
+
+ /* NB: The texture is drawn flipped horizontally and scaled to fit in the
+ * top right corner of the window. */
+
+ /* red, top right */
+ cogl_read_pixels (FRAMEBUFFER_WIDTH - 1, 0, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0x00 && pixel[BLUE] == 0x00);
+
+ /* green, top left */
+ cogl_read_pixels ((FRAMEBUFFER_WIDTH/2), 0, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0xff && pixel[BLUE] == 0x00);
+
+ /* blue, bottom right */
+ cogl_read_pixels (FRAMEBUFFER_WIDTH - 1, (FRAMEBUFFER_HEIGHT/2) - 1, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0x00 && pixel[BLUE] == 0xff);
+
+ /* white, bottom left */
+ cogl_read_pixels ((FRAMEBUFFER_WIDTH/2), (FRAMEBUFFER_HEIGHT/2) - 1, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0xff && pixel[BLUE] == 0xff);
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_offscreen (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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-path.c b/tests/conform/test-path.c
new file mode 100644
index 00000000..363482fe
--- /dev/null
+++ b/tests/conform/test-path.c
@@ -0,0 +1,234 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define BLOCK_SIZE 16
+
+/* Number of pixels at the border of a block quadrant to skip when verifying */
+#define TEST_INSET 1
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+static const ClutterColor block_color = { 0xff, 0xff, 0xff, 0xff };
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+ unsigned int frame;
+} TestState;
+
+static void
+draw_path_at (int x, int y)
+{
+ cogl_push_matrix ();
+ cogl_translate (x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f);
+ cogl_path_fill ();
+ cogl_pop_matrix ();
+}
+
+static void
+verify_block (int block_x, int block_y, int block_mask)
+{
+ guint8 data[BLOCK_SIZE * BLOCK_SIZE * 4];
+ 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_read_pixels (block_x * BLOCK_SIZE,
+ block_y * BLOCK_SIZE,
+ BLOCK_SIZE, BLOCK_SIZE,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ data);
+
+ for (qy = 0; qy < 2; qy++)
+ for (qx = 0; qx < 2; qx++)
+ {
+ int bit = qx | (qy << 1);
+ const ClutterColor *color =
+ ((block_mask & (1 << bit)) ? &block_color : &stage_color);
+ 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 guint8 *p = data + (qx * BLOCK_SIZE / 2 * 4 +
+ qy * BLOCK_SIZE * 4 * BLOCK_SIZE / 2 +
+ (x + TEST_INSET) * 4 +
+ (y + TEST_INSET) * BLOCK_SIZE * 4);
+ g_assert_cmpint (p[0], ==, color->red);
+ g_assert_cmpint (p[1], ==, color->green);
+ g_assert_cmpint (p[2], ==, color->blue);
+ }
+ }
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglHandle path_a, path_b, path_c;
+
+ if (state->frame++ < 2)
+ return;
+
+ cogl_set_source_color4ub (255, 255, 255, 255);
+
+ /* 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 */
+ cogl_path_new ();
+ cogl_path_rectangle (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2,
+ BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2,
+ BLOCK_SIZE * 3 / 4, BLOCK_SIZE);
+ path_a = cogl_handle_ref (cogl_get_path ());
+ draw_path_at (0, 0);
+
+ /* Create another path filling the whole block */
+ cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ path_b = cogl_handle_ref (cogl_get_path ());
+ draw_path_at (1, 0);
+
+ /* Draw the first path again */
+ cogl_set_path (path_a);
+ draw_path_at (2, 0);
+
+ /* Draw a copy of path a */
+ path_c = cogl_path_copy (path_a);
+ cogl_set_path (path_c);
+ draw_path_at (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_set_path (path_a);
+ cogl_path_line_to (0, BLOCK_SIZE / 2);
+ cogl_path_line_to (0, 0);
+ cogl_path_line_to (BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ draw_path_at (4, 0);
+
+ /* Draw the copy again. It should not have changed */
+ cogl_set_path (path_c);
+ draw_path_at (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_set_path (path_c);
+ cogl_path_line_to (BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (BLOCK_SIZE * 3 / 4, 0);
+ cogl_path_line_to (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2);
+ cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_rectangle (BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2);
+ draw_path_at (6, 0);
+
+ /* Draw the original path again. It should not have changed */
+ cogl_set_path (path_a);
+ draw_path_at (7, 0);
+
+ cogl_handle_unref (path_a);
+ cogl_handle_unref (path_b);
+ cogl_handle_unref (path_c);
+
+ /* Draw a self-intersecting path. The part that intersects should be
+ inverted */
+ cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_line_to (0, BLOCK_SIZE / 2);
+ cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (BLOCK_SIZE / 2, 0);
+ cogl_path_close ();
+ draw_path_at (8, 0);
+
+ /* Draw two sub paths. Where the paths intersect it should be
+ inverted */
+ cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE);
+ draw_path_at (9, 0);
+
+ /* Draw a clockwise outer path */
+ cogl_path_move_to (0, 0);
+ cogl_path_line_to (BLOCK_SIZE, 0);
+ cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE);
+ cogl_path_line_to (0, BLOCK_SIZE);
+ cogl_path_close ();
+ /* Add a clockwise sub path in the upper left quadrant */
+ cogl_path_move_to (0, 0);
+ cogl_path_line_to (BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (0, BLOCK_SIZE / 2);
+ cogl_path_close ();
+ /* Add a counter-clockwise sub path in the upper right quadrant */
+ cogl_path_move_to (BLOCK_SIZE / 2, 0);
+ cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+ cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE / 2);
+ cogl_path_line_to (BLOCK_SIZE, 0);
+ cogl_path_close ();
+ /* Retain the path for the next test */
+ path_a = cogl_handle_ref (cogl_get_path ());
+ draw_path_at (10, 0);
+
+ /* Draw the same path again with the other fill rule */
+ cogl_set_path (path_a);
+ cogl_path_set_fill_rule (COGL_PATH_FILL_RULE_NON_ZERO);
+ draw_path_at (11, 0);
+
+ cogl_handle_unref (path_a);
+
+ verify_block (0, 0, 0x8 /* bottom right */);
+ verify_block (1, 0, 0xf /* all of them */);
+ verify_block (2, 0, 0x8 /* bottom right */);
+ verify_block (3, 0, 0x8 /* bottom right */);
+ verify_block (4, 0, 0x9 /* top left and bottom right */);
+ verify_block (5, 0, 0x8 /* bottom right */);
+ verify_block (6, 0, 0xa /* bottom right and top right */);
+ verify_block (7, 0, 0x9 /* top_left and bottom right */);
+ verify_block (8, 0, 0xe /* all but top left */);
+ verify_block (9, 0, 0x7 /* all but bottom right */);
+ verify_block (10, 0, 0xc /* bottom two */);
+ verify_block (11, 0, 0xd /* all but top right */);
+
+ /* Comment this out if you want visual feedback of what this test
+ * paints.
+ */
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_path (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ unsigned int idle_source;
+ unsigned int paint_handler;
+
+ state.frame = 0;
+ state.stage = clutter_stage_get_default ();
+ 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 (state.stage);
+ clutter_main ();
+
+ g_signal_handler_disconnect (state.stage, paint_handler);
+ g_source_remove (idle_source);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-pipeline-user-matrix.c b/tests/conform/test-pipeline-user-matrix.c
new file mode 100644
index 00000000..8e48fd2c
--- /dev/null
+++ b/tests/conform/test-pipeline-user-matrix.c
@@ -0,0 +1,144 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+static void
+paint_cb (ClutterActor *stage)
+{
+ /* This texture is painted mirrored around the x-axis */
+ guint8 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 */
+ guint8 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 */
+ };
+ CoglHandle tex0, tex1;
+ CoglPipeline *pipeline;
+ CoglMatrix matrix;
+ int width, height;
+ guint8 *pixels, *p;
+
+ width = clutter_actor_get_width (stage);
+ height = clutter_actor_get_height (stage);
+
+ 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 ();
+
+ /* 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 */
+ cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGBA=ADD(PREVIOUS, TEXTURE)",
+ NULL);
+
+ /* 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_set_source (pipeline);
+ cogl_rectangle (0, 0, width, height);
+
+ cogl_handle_unref (tex1);
+ cogl_handle_unref (tex0);
+ cogl_object_unref (pipeline);
+
+ /* 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 (width * height * 4);
+
+ cogl_read_pixels (0, 0, width, height,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixels);
+
+ for (p = pixels + width * height * 4; p > pixels;)
+ {
+ p -= 4;
+ g_assert_cmpint (p[0], ==, 0xff);
+ g_assert_cmpint (p[1], ==, 0xff);
+ g_assert_cmpint (p[2], ==, 0xff);
+ }
+
+ g_free (pixels);
+
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_pipeline_user_matrix (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ ClutterActor *stage;
+ unsigned int idle_source;
+ unsigned int paint_handler;
+
+ 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);
+
+ paint_handler = g_signal_connect_after (stage, "paint",
+ G_CALLBACK (paint_cb),
+ NULL);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_source_remove (idle_source);
+ g_signal_handler_disconnect (stage, paint_handler);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-pixel-buffer.c b/tests/conform/test-pixel-buffer.c
new file mode 100644
index 00000000..05e84335
--- /dev/null
+++ b/tests/conform/test-pixel-buffer.c
@@ -0,0 +1,330 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#define TILE_SIZE 32.0f
+
+enum
+{
+ TILE_MAP,
+ TILE_SET_DATA,
+ NB_TILES,
+ TILE_SET_REGION,
+};
+
+typedef struct test_tile
+{
+ ClutterColor color;
+ gfloat x, y;
+ CoglHandle buffer;
+ CoglHandle texture;
+} TestTile;
+
+static const ClutterColor
+buffer_colors[] =
+ {
+ };
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+ unsigned int frame;
+
+ TestTile *tiles;
+
+} TestState;
+
+static CoglHandle
+create_texture_from_buffer (CoglHandle buffer)
+{
+ CoglHandle texture;
+
+ texture = cogl_texture_new_from_buffer (buffer,
+ TILE_SIZE, TILE_SIZE,
+ COGL_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ TILE_SIZE * 4,
+ 0);
+
+ g_assert (texture != COGL_INVALID_HANDLE);
+
+ return texture;
+}
+
+static void
+create_map_tile (TestTile *tile)
+{
+ CoglHandle buffer;
+ guchar *map;
+ unsigned int i;
+ unsigned int stride = 0;
+ guint8 *line;
+
+ buffer = cogl_pixel_array_new_with_size (TILE_SIZE,
+ TILE_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ &stride);
+
+ g_assert (cogl_is_pixel_array (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);
+
+ map = cogl_buffer_map (buffer,
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD);
+ g_assert (map);
+
+ line = g_alloca (TILE_SIZE * 4);
+ for (i = 0; i < TILE_SIZE * 4; i += 4)
+ memcpy (line + i, &tile->color, 4);
+
+ for (i = 0; i < TILE_SIZE; i++)
+ memcpy (map + stride * i, line, TILE_SIZE * 4);
+
+ cogl_buffer_unmap (buffer);
+
+ tile->buffer = buffer;
+ tile->texture = create_texture_from_buffer (tile->buffer);
+}
+
+#if 0
+static void
+create_set_region_tile (TestTile *tile)
+{
+ CoglHandle buffer;
+ ClutterColor bottom_color;
+ unsigned int rowstride = 0;
+ guchar *data;
+ unsigned int i;
+
+ buffer = cogl_pixel_array_with_size (TILE_SIZE,
+ TILE_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ &rowstride);
+
+ g_assert (cogl_is_pixel_array (buffer));
+ g_assert (cogl_is_buffer (buffer));
+
+ /* while at it, set/get the hint */
+ cogl_buffer_set_hint (buffer, COGL_BUFFER_HINT_STATIC_TEXTURE);
+ g_assert (cogl_buffer_get_hint (buffer) == COGL_BUFFER_HINT_STATIC_TEXTURE);
+
+ data = g_malloc (TILE_SIZE * TILE_SIZE * 4);
+ /* create a buffer with the data we want to copy to the buffer */
+ for (i = 0; i < TILE_SIZE * TILE_SIZE * 4; i += 4)
+ memcpy (data + i, &tile->color, 4);
+
+ cogl_pixel_array_set_region (buffer,
+ data,
+ TILE_SIZE, TILE_SIZE,
+ TILE_SIZE,
+ 0, 0);
+
+ bottom_color.red = tile->color.red;
+ bottom_color.green = tile->color.blue;
+ bottom_color.blue = tile->color.green;
+ bottom_color.alpha = tile->color.alpha;
+ for (i = 0; i < TILE_SIZE / 2; i++)
+ memcpy (data + i, &bottom_color, 4);
+
+ cogl_buffer_set_data (buffer, data, 0, TILE_SIZE * TILE_SIZE * 4 / 2);
+
+ g_free (data);
+
+ tile->buffer = buffer;
+ tile->texture = create_texture_from_buffer (tile->buffer);
+}
+#endif
+
+static void
+create_set_data_tile (TestTile *tile)
+{
+ CoglHandle buffer;
+ unsigned int rowstride = 0;
+ gboolean res;
+ guchar *data;
+ unsigned int i;
+
+ buffer = cogl_pixel_array_new_with_size (TILE_SIZE,
+ TILE_SIZE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ &rowstride);
+
+ g_assert (cogl_is_pixel_array (buffer));
+ g_assert (cogl_is_buffer (buffer));
+ g_assert_cmpint (cogl_buffer_get_size (buffer), ==, rowstride * TILE_SIZE);
+
+ /* create a buffer with the data we want to copy to the buffer */
+ data = g_malloc (TILE_SIZE * TILE_SIZE * 4);
+ for (i = 0; i < TILE_SIZE * TILE_SIZE * 4; i += 4)
+ memcpy (data + i, &tile->color, 4);
+
+ /* FIXME: this doesn't consider the rowstride */
+ res = cogl_buffer_set_data (buffer, 0, data, TILE_SIZE * TILE_SIZE * 4);
+ g_assert (res);
+
+ g_free (data);
+
+ tile->buffer = buffer;
+ tile->texture = create_texture_from_buffer (tile->buffer);
+}
+
+static void
+draw_frame (TestState *state)
+{
+ unsigned int i;
+
+ /* Paint the textures */
+ for (i = 0; i < NB_TILES; i++)
+ {
+ cogl_set_source_texture (state->tiles[i].texture);
+ cogl_rectangle (state->tiles[i].x,
+ state->tiles[i].y,
+ state->tiles[i].x + TILE_SIZE,
+ state->tiles[i].y + TILE_SIZE);
+ }
+
+}
+
+static gboolean
+validate_tile (TestState *state,
+ TestTile *tile)
+{
+ int x, y;
+ guchar *pixels, *p;
+
+ p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+ tile->x,
+ tile->y,
+ TILE_SIZE,
+ TILE_SIZE);
+
+ /* Check whether the center of each division is the right color */
+ for (y = 0; y < TILE_SIZE; y++)
+ for (x = 0; x < TILE_SIZE; x++)
+ {
+ if (p[0] != tile->color.red ||
+ p[1] != tile->color.green ||
+ p[2] != tile->color.blue ||
+ p[3] != tile->color.alpha)
+ {
+ return FALSE;
+ }
+
+ p += 4;
+ }
+
+ return TRUE;
+}
+
+static void
+validate_result (TestState *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < NB_TILES; i++)
+ g_assert (validate_tile (state, &state->tiles[i]));
+
+ /* comment this if you want to see what's being drawn */
+#if 1
+ clutter_main_quit ();
+#endif
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ int frame_num;
+
+ draw_frame (state);
+
+ /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+ * another paint run so to avoid infinite recursion we only aim to validate
+ * the first frame. */
+ frame_num = state->frame++;
+ if (frame_num == 1)
+ validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_pixel_array (TestUtilsGTestFixture *fixture,
+ void * data)
+{
+ TestState state;
+ unsigned int idle_source;
+ unsigned int paint_handler, i;
+ static TestTile tiles[NB_TILES] =
+ {
+ /* color x y buffer tex */
+
+ /* MAP */
+ { { 0xff, 0x00, 0x00, 0xff }, 0.0f, 0.0f, NULL, NULL },
+#if 0
+ /* SET_REGION */
+ { { 0x7e, 0x7e, 0xff, 0x7e }, 0.0f, TILE_SIZE, NULL, NULL },
+#endif
+ /* SET_DATA */
+ { { 0x7e, 0xff, 0x7e, 0xff }, 0.0f, TILE_SIZE, NULL, NULL }
+ };
+
+ state.frame = 0;
+
+ state.stage = clutter_stage_get_default ();
+
+ create_map_tile (&tiles[TILE_MAP]);
+#if 0
+ create_set_region_tile (&tiles[TILE_SET_REGION]);
+#endif
+ create_set_data_tile (&tiles[TILE_SET_DATA]);
+
+ state.tiles = tiles;
+
+ 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);
+
+ for (i = 0; i < NB_TILES; i++)
+ {
+ cogl_handle_unref (state.tiles[i].buffer);
+ cogl_handle_unref (state.tiles[i].texture);
+ }
+
+ /* Remove all of the actors from the stage */
+ clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
+ (ClutterCallback) clutter_actor_destroy,
+ NULL);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-premult.c b/tests/conform/test-premult.c
new file mode 100644
index 00000000..6b73aca5
--- /dev/null
+++ b/tests/conform/test-premult.c
@@ -0,0 +1,367 @@
+
+#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;
+ CoglHandle passthrough_material;
+} TestState;
+
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 a = MASK_ALPHA (color);
+
+ if (g_test_verbose ())
+ g_print (" expected = %x, %x, %x, %x\n",
+ r, g, b, a);
+ /* FIXME - allow for hardware in-precision */
+ g_assert (pixel[RED] == r);
+ g_assert (pixel[GREEN] == g);
+ g_assert (pixel[BLUE] == b);
+
+ /* FIXME
+ * We ignore the alpha, since we don't know if our render target is
+ * RGB or RGBA */
+ /* g_assert (pixel[ALPHA] == a); */
+}
+
+static guchar *
+gen_tex_data (guint32 color)
+{
+ guchar *tex_data, *p;
+ guint8 r = MASK_RED (color);
+ guint8 g = MASK_GREEN (color);
+ guint8 b = MASK_BLUE (color);
+ guint8 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 CoglHandle
+make_texture (guint32 color,
+ CoglPixelFormat src_format,
+ CoglPixelFormat internal_format)
+{
+ CoglHandle tex;
+ guchar *tex_data = gen_tex_data (color);
+
+ tex = cogl_texture_new_from_data (QUAD_WIDTH,
+ QUAD_WIDTH,
+ COGL_TEXTURE_NONE,
+ src_format,
+ internal_format,
+ QUAD_WIDTH * 4,
+ tex_data);
+
+ g_free (tex_data);
+
+ return tex;
+}
+
+static void
+check_texture (TestState *state,
+ int x,
+ int y,
+ CoglHandle tex,
+ guint32 expected_result)
+{
+ guchar pixel[4];
+ int y_off;
+ int x_off;
+
+ cogl_material_set_layer (state->passthrough_material, 0, tex);
+
+ cogl_set_source (state->passthrough_material);
+ cogl_rectangle (x * QUAD_WIDTH,
+ y * QUAD_WIDTH,
+ x * QUAD_WIDTH + QUAD_WIDTH,
+ y * QUAD_WIDTH + QUAD_WIDTH);
+
+ /* See what we got... */
+
+ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+ x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+ cogl_read_pixels (x_off, y_off, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+ if (g_test_verbose ())
+ {
+ g_print ("check texture (%d, %d):\n", x, y);
+ g_print (" result = %02x, %02x, %02x, %02x\n",
+ pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+ }
+
+ check_pixel (pixel, expected_result);
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ CoglHandle tex;
+ guchar *tex_data;
+
+ /* If the user explicitly specifies an unmultiplied internal format then
+ * Cogl shouldn't automatically premultiply the given texture data... */
+ if (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+ check_texture (state, 1, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /* If the user gives COGL_PIXEL_FORMAT_ANY for the internal format then
+ * by default Cogl should premultiply the given texture data...
+ * (In the future there will be additional Cogl API to control this
+ * behaviour) */
+ if (g_test_verbose ())
+ g_print ("make_texture (0xff00ff80, "
+ "src = RGBA_8888, internal = ANY)\n");
+ tex = make_texture (0xff00ff80,
+ COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+ COGL_PIXEL_FORMAT_ANY); /* internal format */
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_ANY); /* internal format */
+ check_texture (state, 5, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /*
+ * Test cogl_texture_set_region() ....
+ */
+
+ if (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+ if (g_test_verbose ())
+ g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+ tex_data = gen_tex_data (0xff00ff80);
+ 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 */
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ 0, /* auto compute row stride */
+ tex_data);
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+ if (g_test_verbose ())
+ g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+ tex_data = gen_tex_data (0x80008080);
+ 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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 0, /* auto compute row stride */
+ tex_data);
+ check_texture (state, 7, 0, /* position */
+ tex,
+ 0xff00ff80); /* expected */
+
+
+ if (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+ if (g_test_verbose ())
+ g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+ tex_data = gen_tex_data (0x80008080);
+ 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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ 0, /* auto compute row stride */
+ tex_data);
+ check_texture (state, 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 (g_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 */
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+ if (g_test_verbose ())
+ g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+ tex_data = gen_tex_data (0xff00ff80);
+ 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 */
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ 0, /* auto compute row stride */
+ tex_data);
+ check_texture (state, 9, 0, /* position */
+ tex,
+ 0x80008080); /* expected */
+
+ /* Comment this out if you want visual feedback for what this test paints */
+ clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_premult (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ ClutterActor *stage;
+ ClutterActor *group;
+ unsigned int idle_source;
+
+ state.passthrough_material = cogl_material_new ();
+ cogl_material_set_blend (state.passthrough_material,
+ "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ cogl_material_set_layer_combine (state.passthrough_material, 0,
+ "RGBA = REPLACE (TEXTURE)", NULL);
+
+ 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 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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-primitive.c b/tests/conform/test-primitive.c
new file mode 100644
index 00000000..cffbfd32
--- /dev/null
+++ b/tests/conform/test-primitive.c
@@ -0,0 +1,230 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0xff, 0x00, 0xff };
+static const ClutterColor prim_color = { 0xff, 0x00, 0xff, 0xff };
+static const ClutterColor tex_color = { 0x00, 0x00, 0xff, 0xff };
+
+typedef CoglPrimitive * (* TestPrimFunc) (ClutterColor *expected_color);
+
+static CoglPrimitive *
+test_prim_p2 (ClutterColor *expected_color)
+{
+ static const CoglVertexP2 verts[] =
+ { { 0, 0 }, { 0, 10 }, { 10, 0 } };
+
+ return cogl_primitive_new_p2 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3 (ClutterColor *expected_color)
+{
+ static const CoglVertexP3 verts[] =
+ { { 0, 0, 0 }, { 0, 10, 0 }, { 10, 0, 0 } };
+
+ return cogl_primitive_new_p3 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2c4 (ClutterColor *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->red = 255;
+ expected_color->green = 255;
+ expected_color->blue = 0;
+
+ return cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3c4 (ClutterColor *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->red = 255;
+ expected_color->green = 255;
+ expected_color->blue = 0;
+
+ return cogl_primitive_new_p3c4 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2 (ClutterColor *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 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2 (ClutterColor *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 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2c4 (ClutterColor *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 } };
+
+ *expected_color = tex_color;
+ expected_color->blue = 0xf0;
+
+ return cogl_primitive_new_p2t2c4 (COGL_VERTICES_MODE_TRIANGLES,
+ 3, /* n_vertices */
+ verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2c4 (ClutterColor *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 } };
+
+ *expected_color = tex_color;
+ expected_color->blue = 0xf0;
+
+ return cogl_primitive_new_p3t2c4 (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
+paint_cb (void)
+{
+ CoglPipeline *pipeline;
+ CoglHandle tex;
+ guint8 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.red;
+ tex_data[4] = tex_color.green;
+ tex_data[5] = tex_color.blue;
+ tex = cogl_texture_new_from_data (2, 1, /* size */
+ COGL_TEXTURE_NO_ATLAS,
+ COGL_PIXEL_FORMAT_RGB_888,
+ COGL_PIXEL_FORMAT_ANY,
+ 6, /* rowstride */
+ tex_data);
+ pipeline = cogl_pipeline_new ();
+ cogl_pipeline_set_color4ub (pipeline,
+ prim_color.red,
+ prim_color.green,
+ prim_color.blue,
+ prim_color.alpha);
+ cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+ cogl_handle_unref (tex);
+ cogl_set_source (pipeline);
+ cogl_object_unref (pipeline);
+
+ for (i = 0; i < G_N_ELEMENTS (test_prim_funcs); i++)
+ {
+ CoglPrimitive *prim;
+ ClutterColor expected_color = prim_color;
+ guint8 pixel[4];
+
+ prim = test_prim_funcs[i] (&expected_color);
+
+ cogl_push_matrix ();
+ cogl_translate (i * 10, 0, 0);
+ cogl_primitive_draw (prim);
+ cogl_pop_matrix ();
+
+ cogl_read_pixels (i * 10 + 2, 2, 1, 1,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ pixel);
+
+ g_assert_cmpint (pixel[0], ==, expected_color.red);
+ g_assert_cmpint (pixel[1], ==, expected_color.green);
+ g_assert_cmpint (pixel[2], ==, expected_color.blue);
+
+ cogl_object_unref (prim);
+ }
+
+ /* Comment this out to see what the test paints */
+ clutter_main_quit ();
+}
+
+void
+test_cogl_primitive (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ ClutterActor *stage;
+ unsigned int paint_handler;
+
+ stage = clutter_stage_get_default ();
+
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ paint_handler = g_signal_connect_after (stage, "paint",
+ G_CALLBACK (paint_cb), NULL);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_signal_handler_disconnect (stage, paint_handler);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-readpixels.c b/tests/conform/test-readpixels.c
new file mode 100644
index 00000000..0890f9a7
--- /dev/null
+++ b/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;
+ guint32 *pixels;
+ guint8 *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 = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_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_to_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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-sub-texture.c b/tests/conform/test-sub-texture.c
new file mode 100644
index 00000000..038b4ac8
--- /dev/null
+++ b/tests/conform/test-sub-texture.c
@@ -0,0 +1,371 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.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 ClutterColor
+corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] =
+ {
+ { 0xff, 0x00, 0x00, 0xff }, /* red top left */
+ { 0x00, 0xff, 0x00, 0xff }, /* green top right */
+ { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */
+ { 0xff, 0x00, 0xff, 0xff } /* purple bottom right */
+ };
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+ unsigned int frame;
+
+ CoglHandle tex;
+} TestState;
+
+static CoglHandle
+create_source (void)
+{
+ int dx, dy;
+ guchar *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4);
+
+ /* 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++)
+ {
+ guchar *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++)
+ {
+ memcpy (p, corner_colors + dx + dy * SOURCE_DIVISIONS_X, 4);
+ p += 4;
+ }
+
+ p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4;
+ }
+ }
+
+ return cogl_texture_new_from_data (SOURCE_SIZE, SOURCE_SIZE,
+ COGL_TEXTURE_NONE,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ SOURCE_SIZE * 4,
+ data);
+}
+
+static CoglHandle
+create_test_texture (void)
+{
+ CoglHandle tex;
+ guint8 *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_new_from_data (256, 256, COGL_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)
+{
+ CoglHandle full_texture, sub_texture, sub_sub_texture;
+
+ /* Create a sub texture of the bottom right quarter of the texture */
+ sub_texture = cogl_texture_new_from_sub_texture (state->tex,
+ DIVISION_WIDTH,
+ DIVISION_HEIGHT,
+ DIVISION_WIDTH,
+ DIVISION_HEIGHT);
+
+ /* Paint it */
+ cogl_set_source_texture (sub_texture);
+ cogl_rectangle (0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT);
+
+ cogl_handle_unref (sub_texture);
+
+ /* 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_texture_new_from_sub_texture (state->tex,
+ 0, 0,
+ SOURCE_SIZE,
+ DIVISION_HEIGHT);
+ cogl_set_source_texture (sub_texture);
+ cogl_rectangle_with_texture_coords (0.0f, SOURCE_SIZE,
+ SOURCE_SIZE * 2.0f, SOURCE_SIZE * 1.5f,
+ 0.0f, 0.0f,
+ 2.0f, 1.0f);
+ cogl_handle_unref (sub_texture);
+
+ /* Create a sub texture of a sub texture */
+ full_texture = create_test_texture ();
+ sub_texture = cogl_texture_new_from_sub_texture (full_texture,
+ 20, 10, 30, 20);
+ sub_sub_texture = cogl_texture_new_from_sub_texture (sub_texture,
+ 20, 10, 10, 10);
+ cogl_set_source_texture (sub_sub_texture);
+ cogl_rectangle (0.0f, SOURCE_SIZE * 2.0f,
+ 10.0f, SOURCE_SIZE * 2.0f + 10.0f);
+ cogl_handle_unref (sub_sub_texture);
+ cogl_handle_unref (sub_texture);
+ cogl_handle_unref (full_texture);
+}
+
+static gboolean
+validate_part (TestState *state,
+ int xpos, int ypos,
+ int width, int height,
+ const ClutterColor *color)
+{
+ int x, y;
+ gboolean pass = TRUE;
+ guchar *pixels, *p;
+
+ p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+ xpos + TEST_INSET,
+ ypos + TEST_INSET,
+ width - TEST_INSET - 2,
+ height - TEST_INSET - 2);
+
+ /* Check whether the center of each division is the right color */
+ for (y = 0; y < height - TEST_INSET - 2; y++)
+ for (x = 0; x < width - TEST_INSET - 2; x++)
+ {
+ if (p[0] != color->red ||
+ p[1] != color->green ||
+ p[2] != color->blue)
+ pass = FALSE;
+
+ p += 4;
+ }
+
+ return pass;
+}
+
+static guint8 *
+create_update_data (void)
+{
+ guint8 *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;
+ CoglHandle sub_texture, test_tex;
+ guchar *texture_data, *p;
+ int tex_width, tex_height;
+
+ /* Sub texture of the bottom right corner of the texture */
+ g_assert (validate_part (state, 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++)
+ g_assert (validate_part (state,
+ i * SOURCE_SIZE + division_num * DIVISION_WIDTH,
+ SOURCE_SIZE,
+ DIVISION_WIDTH, DIVISION_HEIGHT,
+ corner_colors + division_num));
+
+ /* Sub sub texture */
+ p = texture_data = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+ 0, SOURCE_SIZE * 2, 10, 10);
+ 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_texture_new_from_sub_texture (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);
+ const ClutterColor *color = (corner_colors + div_x +
+ div_y * SOURCE_DIVISIONS_X);
+ g_assert (p[0] == color->red);
+ g_assert (p[1] == color->green);
+ g_assert (p[2] == color->blue);
+ p += 4;
+ }
+ g_free (texture_data);
+ cogl_handle_unref (sub_texture);
+
+ /* Create a 256x256 test texture */
+ test_tex = create_test_texture ();
+ /* Create a sub texture the views the center half of the texture */
+ sub_texture = cogl_texture_new_from_sub_texture (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_handle_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,
+ 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_handle_unref (test_tex);
+
+ /* Comment this out to see what the test paints */
+ clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+ int frame_num;
+
+ draw_frame (state);
+
+ /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+ * another paint run so to avoid infinite recursion we only aim to validate
+ * the first frame. */
+ frame_num = state->frame++;
+ if (frame_num == 1)
+ validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_sub_texture (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ unsigned int idle_source;
+ unsigned int paint_handler;
+
+ state.frame = 0;
+
+ state.stage = clutter_stage_get_default ();
+ state.tex = create_source ();
+
+ 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);
+
+ cogl_handle_unref (state.tex);
+
+ /* Remove all of the actors from the stage */
+ clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
+ (ClutterCallback) clutter_actor_destroy,
+ NULL);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-texture-3d.c b/tests/conform/test-texture-3d.c
new file mode 100644
index 00000000..d8a5ee4b
--- /dev/null
+++ b/tests/conform/test-texture-3d.c
@@ -0,0 +1,230 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0xff, 0x0, 0xff };
+
+#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)
+
+static CoglHandle
+create_texture_3d (void)
+{
+ int x, y, z;
+ guint8 *data = g_malloc (TEX_IMAGE_STRIDE * TEX_DEPTH);
+ guint8 *p = data;
+ CoglHandle tex;
+ GError *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 (TEX_WIDTH, TEX_HEIGHT, TEX_DEPTH,
+ COGL_TEXTURE_NO_AUTO_MIPMAP,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_ANY,
+ TEX_ROWSTRIDE,
+ TEX_IMAGE_STRIDE,
+ data,
+ &error);
+
+ if (tex == COGL_INVALID_HANDLE)
+ {
+ 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 (void)
+{
+ CoglHandle tex = create_texture_3d ();
+ CoglHandle material = cogl_material_new ();
+ typedef struct { float x, y, s, t, r; } Vert;
+ CoglHandle vbo, indices;
+ Vert *verts, *v;
+ int i;
+
+ cogl_material_set_layer (material, 0, tex);
+ cogl_handle_unref (tex);
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_set_source (material);
+ cogl_handle_unref (material);
+
+ /* Render the texture repeated horizontally twice using a regular
+ cogl rectangle. This should end up with the r texture coordinates
+ as zero */
+ cogl_rectangle_with_texture_coords (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 VBO */
+ 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++;
+ }
+
+ vbo = cogl_vertex_buffer_new (4 * TEX_DEPTH);
+ cogl_vertex_buffer_add (vbo, "gl_Vertex",
+ 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (Vert),
+ &verts->x);
+ cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0",
+ 3, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+ sizeof (Vert),
+ &verts->s);
+ cogl_vertex_buffer_submit (vbo);
+
+ g_free (verts);
+
+ indices = cogl_vertex_buffer_indices_get_for_quads (6 * TEX_DEPTH);
+
+ cogl_vertex_buffer_draw_elements (vbo,
+ COGL_VERTICES_MODE_TRIANGLES,
+ indices,
+ 0, TEX_DEPTH * 4 - 1,
+ 0, TEX_DEPTH * 6);
+
+ cogl_handle_unref (vbo);
+}
+
+static void
+validate_block (int block_x, int block_y, int z)
+{
+ guint8 *data, *p;
+ int x, y;
+
+ p = data = g_malloc (TEX_WIDTH * TEX_HEIGHT * 4);
+
+ cogl_read_pixels (block_x * TEX_WIDTH, block_y * TEX_HEIGHT,
+ TEX_WIDTH, TEX_HEIGHT,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ data);
+
+ for (y = 0; y < TEX_HEIGHT; y++)
+ for (x = 0; x < TEX_WIDTH; x++)
+ {
+ g_assert_cmpint (p[0], ==, 255 - x * 8);
+ g_assert_cmpint (p[1], ==, y * 8);
+ g_assert_cmpint (p[2], ==, 255 - z * 8);
+ p += 4;
+ }
+
+ g_free (data);
+}
+
+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
+on_paint (void)
+{
+ draw_frame ();
+
+ validate_result ();
+
+ /* Comment this out to see what the test paints */
+ clutter_main_quit ();
+}
+
+void
+test_cogl_texture_3d (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ ClutterActor *stage;
+ unsigned int paint_handler;
+
+ stage = clutter_stage_get_default ();
+
+ /* Check whether GL supports the rectangle extension. If not we'll
+ just assume the test passes */
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_3D))
+ {
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ paint_handler = g_signal_connect_after (stage, "paint",
+ G_CALLBACK (on_paint), NULL);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_signal_handler_disconnect (stage, paint_handler);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+ }
+ else if (g_test_verbose ())
+ g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-texture-get-set-data.c b/tests/conform/test-texture-get-set-data.c
new file mode 100644
index 00000000..78e302c6
--- /dev/null
+++ b/tests/conform/test-texture-get-set-data.c
@@ -0,0 +1,166 @@
+#include <clutter/clutter.h>
+#include <glib.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static void
+check_texture (int width, int height, CoglTextureFlags flags)
+{
+ CoglHandle tex;
+ guint8 *data, *p;
+ int y, x;
+
+ 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);
+ }
+
+ tex = cogl_texture_new_from_data (width, height,
+ flags,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ width * 4,
+ data);
+
+ /* Replace the bottom right quarter of the data with negated data to
+ test set_region */
+ p = data + (height + 1) * width * 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, /* src_x */
+ height / 2, /* src_y */
+ width / 2, /* dst_x */
+ height / 2, /* dst_y */
+ width / 2, /* dst_width */
+ height / 2, /* dst_height */
+ width,
+ height,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ width * 4, /* 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_handle_unref (tex);
+ g_free (data);
+}
+
+static void
+paint_cb (void)
+{
+ /* First try without atlasing */
+ check_texture (256, 256, COGL_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, COGL_TEXTURE_NO_ATLAS);
+ /* And in the other direction. */
+ check_texture (5128, 4, COGL_TEXTURE_NO_ATLAS);
+
+ clutter_main_quit ();
+}
+
+void
+test_cogl_texture_get_set_data (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ ClutterActor *stage;
+ unsigned int paint_handler;
+
+ /* We create a stage even though we don't usually need it so that if
+ the draw-and-read texture fallback is needed then it will have
+ something to draw to */
+ stage = clutter_stage_get_default ();
+
+ paint_handler = g_signal_connect_after (stage, "paint",
+ G_CALLBACK (paint_cb), NULL);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_signal_handler_disconnect (stage, paint_handler);
+
+ if (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-texture-mipmaps.c b/tests/conform/test-texture-mipmaps.c
new file mode 100644
index 00000000..22e27b74
--- /dev/null
+++ b/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 = cogl_texture_new_from_data (TEX_SIZE, TEX_SIZE, COGL_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;
+ guint8 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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-texture-pixmap-x11.c b/tests/conform/test-texture-pixmap-x11.c
new file mode 100644
index 00000000..69c58a34
--- /dev/null
+++ b/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 gboolean
+check_paint (TestState *state, int x, int y, int scale)
+{
+ guint8 *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
+ {
+ guint8 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)
+ {
+ gboolean 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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+#endif /* COGL_HAS_XLIB */
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+
+#else /* COGL_HAS_XLIB */
+
+ if (g_test_verbose ())
+ g_print ("Skipping\n");
+
+#endif /* COGL_HAS_XLIB */
+}
+
diff --git a/tests/conform/test-texture-rectangle.c b/tests/conform/test-texture-rectangle.c
new file mode 100644
index 00000000..26b8e284
--- /dev/null
+++ b/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;
+ guint8 *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 = cogl_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 COGL_INVALID_HANDLE;
+
+#endif /* GL_TEXTURE_RECTANGLE_ARB */
+}
+
+static CoglHandle
+create_source_2d (void)
+{
+ int x, y;
+ guint8 *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 = cogl_texture_new_from_data (256, 256, COGL_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 != COGL_INVALID_HANDLE);
+
+ 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)
+{
+ guint8 *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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+static gboolean
+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_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+ }
+ else if (g_test_verbose ())
+ g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-utils.c b/tests/conform/test-utils.c
new file mode 100644
index 00000000..651e4ef1
--- /dev/null
+++ b/tests/conform/test-utils.c
@@ -0,0 +1,80 @@
+#include <cogl/cogl.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+#define FB_WIDTH 640
+#define FB_HEIGHT 480
+
+void
+test_utils_init (TestUtilsGTestFixture *fixture,
+ const void *data)
+{
+ TestUtilsSharedState *state = (TestUtilsSharedState *)data;
+ static int counter = 0;
+ GError *error = NULL;
+ CoglOnscreen *onscreen = NULL;
+
+ if (counter != 0)
+ g_critical ("We don't support running more than one test at a time\n"
+ "in a single test run due to the state leakage that can\n"
+ "cause subsequent tests to fail.\n"
+ "\n"
+ "If you want to run all the tests you should run\n"
+ "$ make test-report");
+ counter++;
+
+ setenv ("COGL_X11_SYNC", "1", 0);
+
+ state->ctx = cogl_context_new (NULL, &error);
+ if (!state->ctx)
+ g_critical ("Failed to create a CoglContext: %s", error->message);
+
+ if (getenv ("COGL_TEST_ONSCREEN"))
+ {
+ onscreen = cogl_onscreen_new (state->ctx, 640, 480);
+ state->fb = COGL_FRAMEBUFFER (onscreen);
+ }
+ else
+ {
+ CoglHandle offscreen;
+ CoglHandle tex = cogl_texture_2d_new_with_size (state->ctx,
+ FB_WIDTH, FB_HEIGHT,
+ COGL_PIXEL_FORMAT_ANY,
+ &error);
+ if (!tex)
+ g_critical ("Failed to allocate texture: %s", error->message);
+
+ offscreen = cogl_offscreen_new_to_texture (tex);
+ state->fb = COGL_FRAMEBUFFER (offscreen);
+ }
+
+ if (!cogl_framebuffer_allocate (state->fb, &error))
+ g_critical ("Failed to allocate framebuffer: %s", error->message);
+
+ if (onscreen)
+ cogl_onscreen_show (onscreen);
+
+ cogl_framebuffer_clear4f (state->fb,
+ COGL_BUFFER_BIT_COLOR |
+ COGL_BUFFER_BIT_DEPTH |
+ COGL_BUFFER_BIT_STENCIL,
+ 0, 0, 0, 1);
+
+ cogl_push_framebuffer (state->fb);
+}
+
+void
+test_utils_fini (TestUtilsGTestFixture *fixture,
+ const void *data)
+{
+ const TestUtilsSharedState *state = (TestUtilsSharedState *)data;
+
+ cogl_pop_framebuffer ();
+
+ if (state->fb)
+ cogl_object_unref (state->fb);
+
+ if (state->ctx)
+ cogl_object_unref (state->ctx);
+}
diff --git a/tests/conform/test-utils.h b/tests/conform/test-utils.h
new file mode 100644
index 00000000..93250518
--- /dev/null
+++ b/tests/conform/test-utils.h
@@ -0,0 +1,41 @@
+#ifndef _TEST_UTILS_H_
+#define _TEST_UTILS_H_
+
+/* This fixture structure is allocated by glib, and before running
+ * each test we get a callback to initialize it.
+ *
+ * Actually we don't use this currently, we instead manage our own
+ * TestUtilsSharedState structure which also gets passed as a private
+ * data argument to the same initialization callback. The advantage of
+ * allocating our own shared state structure is that we can put data
+ * in it before we start running anything.
+ */
+typedef struct _TestUtilsGTestFixture
+{
+ /**/
+ int dummy;
+} TestUtilsGTestFixture;
+
+/* Stuff you put in here is setup once in main() and gets passed around to
+ * all test functions and fixture setup/teardown functions in the data
+ * argument */
+typedef struct _TestUtilsSharedState
+{
+ int *argc_addr;
+ char ***argv_addr;
+
+ void (* todo_func) (TestUtilsGTestFixture *, void *data);
+
+ CoglContext *ctx;
+ CoglFramebuffer *fb;
+} TestUtilsSharedState;
+
+void
+test_utils_init (TestUtilsGTestFixture *fixture,
+ const void *data);
+
+void
+test_utils_fini (TestUtilsGTestFixture *fixture,
+ const void *data);
+
+#endif /* _TEST_UTILS_H_ */
diff --git a/tests/conform/test-vertex-buffer-contiguous.c b/tests/conform/test-vertex-buffer-contiguous.c
new file mode 100644
index 00000000..d1890595
--- /dev/null
+++ b/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 (g_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 (g_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 (g_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 (g_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 (g_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 (g_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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-vertex-buffer-interleved.c b/tests/conform/test-vertex-buffer-interleved.c
new file mode 100644
index 00000000..e548506a
--- /dev/null
+++ b/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 (g_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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-vertex-buffer-mutability.c b/tests/conform/test-vertex-buffer-mutability.c
new file mode 100644
index 00000000..c55ee560
--- /dev/null
+++ b/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 (g_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 (g_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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-viewport.c b/tests/conform/test-viewport.c
new file mode 100644
index 00000000..b247a594
--- /dev/null
+++ b/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,
+ guint8 red,
+ guint8 green,
+ guint8 blue,
+ guint8 alpha)
+{
+ guint8 *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++)
+ {
+ guint8 *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,
+ guint8 red,
+ guint8 green,
+ guint8 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 = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+ COGL_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_to_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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_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 (g_test_verbose ())
+ g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-wrap-modes.c b/tests/conform/test-wrap-modes.c
new file mode 100644
index 00000000..94045966
--- /dev/null
+++ b/tests/conform/test-wrap-modes.c
@@ -0,0 +1,317 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#define TEX_SIZE 4
+
+static const ClutterColor stage_color = { 0x80, 0x80, 0x80, 0xff };
+
+typedef struct _TestState
+{
+ ClutterActor *stage;
+ CoglHandle texture;
+} TestState;
+
+static CoglHandle
+create_texture (CoglTextureFlags flags)
+{
+ guint8 *data = g_malloc (TEX_SIZE * TEX_SIZE * 4), *p = data;
+ CoglHandle 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 = cogl_texture_new_from_data (TEX_SIZE, TEX_SIZE, flags,
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+ COGL_PIXEL_FORMAT_ANY,
+ TEX_SIZE * 4,
+ data);
+
+ g_free (data);
+
+ return tex;
+}
+
+static CoglHandle
+create_material (TestState *state,
+ CoglMaterialWrapMode wrap_mode_s,
+ CoglMaterialWrapMode wrap_mode_t)
+{
+ CoglHandle material;
+
+ material = cogl_material_new ();
+ cogl_material_set_layer (material, 0, state->texture);
+ cogl_material_set_layer_filters (material, 0,
+ COGL_MATERIAL_FILTER_NEAREST,
+ COGL_MATERIAL_FILTER_NEAREST);
+ cogl_material_set_layer_wrap_mode_s (material, 0, wrap_mode_s);
+ cogl_material_set_layer_wrap_mode_t (material, 0, wrap_mode_t);
+
+ return material;
+}
+
+static CoglMaterialWrapMode
+test_wrap_modes[] =
+ {
+ COGL_MATERIAL_WRAP_MODE_REPEAT,
+ COGL_MATERIAL_WRAP_MODE_REPEAT,
+
+ COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+ COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+
+ COGL_MATERIAL_WRAP_MODE_REPEAT,
+ COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+
+ COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+ COGL_MATERIAL_WRAP_MODE_REPEAT,
+
+ COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+ COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+
+ COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+ COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE
+ };
+
+static void
+draw_tests (TestState *state)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+ {
+ CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+ CoglHandle material;
+
+ /* Create a separate material for each pair of wrap modes so
+ that we can verify whether the batch splitting works */
+ wrap_mode_s = test_wrap_modes[i];
+ wrap_mode_t = test_wrap_modes[i + 1];
+ material = create_material (state, wrap_mode_s, wrap_mode_t);
+ cogl_set_source (material);
+ cogl_handle_unref (material);
+ /* Render the material at four times the size of the texture */
+ cogl_rectangle_with_texture_coords (i * TEX_SIZE, 0,
+ (i + 2) * TEX_SIZE, TEX_SIZE * 2,
+ 0, 0, 2, 2);
+ }
+}
+
+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 (test_wrap_modes); i += 2)
+ {
+ CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+ CoglHandle material;
+
+ wrap_mode_s = test_wrap_modes[i];
+ wrap_mode_t = test_wrap_modes[i + 1];
+ material = create_material (state, wrap_mode_s, wrap_mode_t);
+ cogl_set_source (material);
+ cogl_handle_unref (material);
+ cogl_push_matrix ();
+ cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+ /* Render the material 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 (test_wrap_modes); i += 2)
+ {
+ CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+ CoglHandle material;
+
+ wrap_mode_s = test_wrap_modes[i];
+ wrap_mode_t = test_wrap_modes[i + 1];
+ material = create_material (state, wrap_mode_s, wrap_mode_t);
+ cogl_set_source (material);
+ cogl_handle_unref (material);
+ cogl_push_matrix ();
+ cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+ /* Render the material 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
+draw_frame (TestState *state)
+{
+ /* Draw the tests first with a non atlased texture */
+ state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+ draw_tests (state);
+ cogl_handle_unref (state->texture);
+
+ /* Draw the tests again with a possible atlased texture. This should
+ end up testing software repeats */
+ state->texture = create_texture (COGL_TEXTURE_NONE);
+ cogl_push_matrix ();
+ cogl_translate (0.0f, TEX_SIZE * 2.0f, 0.0f);
+ draw_tests (state);
+ cogl_pop_matrix ();
+ cogl_handle_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_handle_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_handle_unref (state->texture);
+}
+
+static void
+validate_set (TestState *state, int offset)
+{
+ guint8 data[TEX_SIZE * 2 * TEX_SIZE * 2 * 4], *p;
+ int x, y, i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+ {
+ CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+
+ wrap_mode_s = test_wrap_modes[i];
+ wrap_mode_t = test_wrap_modes[i + 1];
+
+ cogl_read_pixels (i * TEX_SIZE, offset * TEX_SIZE * 2,
+ TEX_SIZE * 2, TEX_SIZE * 2,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ COGL_PIXEL_FORMAT_RGBA_8888,
+ data);
+
+ p = data;
+
+ for (y = 0; y < TEX_SIZE * 2; y++)
+ for (x = 0; x < TEX_SIZE * 2; x++)
+ {
+ guint8 green, blue;
+
+ if (x < TEX_SIZE ||
+ wrap_mode_s == COGL_MATERIAL_WRAP_MODE_REPEAT ||
+ wrap_mode_s == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
+ green = (x & 1) * 255;
+ else
+ green = ((TEX_SIZE - 1) & 1) * 255;
+
+ if (y < TEX_SIZE ||
+ wrap_mode_t == COGL_MATERIAL_WRAP_MODE_REPEAT ||
+ wrap_mode_t == COGL_MATERIAL_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 */
+
+ /* 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 gboolean
+queue_redraw (gpointer stage)
+{
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+ return TRUE;
+}
+
+void
+test_cogl_wrap_modes (TestUtilsGTestFixture *fixture,
+ void *data)
+{
+ TestState state;
+ unsigned int idle_source;
+ unsigned int paint_handler;
+
+ state.stage = clutter_stage_get_default ();
+
+ 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 (g_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
new file mode 100644
index 00000000..3a2030a7
--- /dev/null
+++ b/tests/data/Makefile.am
@@ -0,0 +1,3 @@
+NULL =
+
+EXTRA_DIST = valgrind.suppressions
diff --git a/tests/data/valgrind.suppressions b/tests/data/valgrind.suppressions
new file mode 100644
index 00000000..f47498d1
--- /dev/null
+++ b/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
+}