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