From 4dd2269701438a4917286dab565f8e0acb6e9b03 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 5 May 2011 23:34:38 +0100 Subject: Starts porting Cogl conformance tests from Clutter 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. --- .gitignore | 27 ++ Makefile.am | 2 +- cogl/cogl-xlib-renderer.c | 5 + configure.ac | 4 + tests/Makefile.am | 16 + tests/README | 31 ++ tests/conform/Makefile.am | 236 ++++++++++++++ tests/conform/run-tests.sh | 12 + tests/conform/test-atlas-migration.c | 133 ++++++++ tests/conform/test-backface-culling.c | 339 ++++++++++++++++++++ tests/conform/test-blend-strings.c | 434 ++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 170 ++++++++++ tests/conform/test-depth-test.c | 291 +++++++++++++++++ tests/conform/test-fixed.c | 18 ++ tests/conform/test-fixtures.c | 12 + tests/conform/test-just-vertex-shader.c | 137 ++++++++ tests/conform/test-launcher.sh.in | 25 ++ tests/conform/test-materials.c | 285 +++++++++++++++++ tests/conform/test-multitexture.c | 206 ++++++++++++ tests/conform/test-npot-texture.c | 236 ++++++++++++++ tests/conform/test-object.c | 86 +++++ tests/conform/test-offscreen.c | 167 ++++++++++ tests/conform/test-path.c | 234 ++++++++++++++ tests/conform/test-pipeline-user-matrix.c | 144 +++++++++ tests/conform/test-pixel-buffer.c | 330 ++++++++++++++++++++ tests/conform/test-premult.c | 367 ++++++++++++++++++++++ tests/conform/test-primitive.c | 230 ++++++++++++++ tests/conform/test-readpixels.c | 178 +++++++++++ tests/conform/test-sub-texture.c | 371 ++++++++++++++++++++++ tests/conform/test-texture-3d.c | 230 ++++++++++++++ tests/conform/test-texture-get-set-data.c | 166 ++++++++++ tests/conform/test-texture-mipmaps.c | 136 ++++++++ tests/conform/test-texture-pixmap-x11.c | 245 +++++++++++++++ tests/conform/test-texture-rectangle.c | 276 ++++++++++++++++ tests/conform/test-utils.c | 80 +++++ tests/conform/test-utils.h | 41 +++ tests/conform/test-vertex-buffer-contiguous.c | 257 +++++++++++++++ tests/conform/test-vertex-buffer-interleved.c | 162 ++++++++++ tests/conform/test-vertex-buffer-mutability.c | 198 ++++++++++++ tests/conform/test-viewport.c | 416 ++++++++++++++++++++++++ tests/conform/test-wrap-modes.c | 317 +++++++++++++++++++ tests/data/Makefile.am | 3 + tests/data/valgrind.suppressions | 173 ++++++++++ 43 files changed, 7425 insertions(+), 1 deletion(-) create mode 100644 tests/Makefile.am create mode 100644 tests/README create mode 100644 tests/conform/Makefile.am create mode 100755 tests/conform/run-tests.sh create mode 100644 tests/conform/test-atlas-migration.c create mode 100644 tests/conform/test-backface-culling.c create mode 100644 tests/conform/test-blend-strings.c create mode 100644 tests/conform/test-conform-main.c create mode 100644 tests/conform/test-depth-test.c create mode 100644 tests/conform/test-fixed.c create mode 100644 tests/conform/test-fixtures.c create mode 100644 tests/conform/test-just-vertex-shader.c create mode 100755 tests/conform/test-launcher.sh.in create mode 100644 tests/conform/test-materials.c create mode 100644 tests/conform/test-multitexture.c create mode 100644 tests/conform/test-npot-texture.c create mode 100644 tests/conform/test-object.c create mode 100644 tests/conform/test-offscreen.c create mode 100644 tests/conform/test-path.c create mode 100644 tests/conform/test-pipeline-user-matrix.c create mode 100644 tests/conform/test-pixel-buffer.c create mode 100644 tests/conform/test-premult.c create mode 100644 tests/conform/test-primitive.c create mode 100644 tests/conform/test-readpixels.c create mode 100644 tests/conform/test-sub-texture.c create mode 100644 tests/conform/test-texture-3d.c create mode 100644 tests/conform/test-texture-get-set-data.c create mode 100644 tests/conform/test-texture-mipmaps.c create mode 100644 tests/conform/test-texture-pixmap-x11.c create mode 100644 tests/conform/test-texture-rectangle.c create mode 100644 tests/conform/test-utils.c create mode 100644 tests/conform/test-utils.h create mode 100644 tests/conform/test-vertex-buffer-contiguous.c create mode 100644 tests/conform/test-vertex-buffer-interleved.c create mode 100644 tests/conform/test-vertex-buffer-mutability.c create mode 100644 tests/conform/test-viewport.c create mode 100644 tests/conform/test-wrap-modes.c create mode 100644 tests/data/Makefile.am create mode 100644 tests/data/valgrind.suppressions 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 #include +#include + 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 ; \ + echo '' >> $@.xml ; \ + echo '' >> $@.xml ; \ + echo ' $(PACKAGE)' >> $@.xml ; \ + echo ' $(VERSION)' >> $@.xml ; \ + echo " $$REVISION" >> $@.xml ; \ + echo " $$TIMESTAMP" >> $@.xml ; \ + echo '' >> $@.xml ; \ + for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ + sed '1,1s/^?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ + done ; \ + echo >> $@.xml ; \ + echo '' >> $@.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 ; \ + echo '' >> $@.xml ; \ + echo '' >> $@.xml ; \ + echo ' $(PACKAGE)' >> $@.xml ; \ + echo ' $(VERSION)' >> $@.xml ; \ + echo " $$REVISION" >> $@.xml ; \ + echo " $$TIMESTAMP" >> $@.xml ; \ + echo '' >> $@.xml ; \ + for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ + sed '1,1s/^?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ + done ; \ + echo >> $@.xml ; \ + echo '' >> $@.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 + +#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 +#include +#include + +#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 +#include +#include + +#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 + +#include +#include +#include + +#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 + +#include + +#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 +#include + +#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 +#include + +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 +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; + +#ifdef COGL_HAS_XLIB + +#include +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 +} -- cgit v1.2.1