From b41a81fb0868e51bf03d25446f8cc9748fcd6bc7 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 17 Sep 2009 14:25:29 +0100 Subject: [PATCH] Add a conformance test for clutter_texture_new_from_actor This contains four tests :- - A regular onscreen source with a clone next to it - An offscreen source with a clone. This is currently commented out because it no longer works. - An onscreen source with a rectangular clip and a clone. - An onscreen source with a clip from a path and a clone. The sources are all a 2x2 grid of colors. Each clone is tested that it either contains the color that should be at that grid position or that the stage color is showing through if the source is clipped. --- tests/conform/Makefile.am | 1 + tests/conform/test-conform-main.c | 1 + tests/conform/test-texture-fbo.c | 255 ++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 tests/conform/test-texture-fbo.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 7a5b6f971..442800d63 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -35,6 +35,7 @@ test_conformance_SOURCES = \ test-materials.c \ test-group.c \ test-actor-size.c \ + test-texture-fbo.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 2b2624434..3379fbabf 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -147,6 +147,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/texture", test_backface_culling); TEST_CONFORM_SIMPLE ("/texture", test_npot_texture); TEST_CONFORM_SIMPLE ("/texture", test_premult); + TEST_CONFORM_SIMPLE ("/texture", test_texture_fbo); TEST_CONFORM_SIMPLE ("/path", test_path); diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c new file mode 100644 index 000000000..b11bde080 --- /dev/null +++ b/tests/conform/test-texture-fbo.c @@ -0,0 +1,255 @@ + +#include +#include + +#include "test-conform-common.h" + +#define SOURCE_SIZE 32 +#define SOURCE_DIVISIONS_X 2 +#define SOURCE_DIVISIONS_Y 2 +#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) +#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) + +static const ClutterColor +corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = + { + { 0xff, 0x00, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0x00, 0xff, 0xff } + }; + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; + guint frame; +} TestState; + +static ClutterActor * +create_source (void) +{ + int x, y; + ClutterActor *group = clutter_group_new (); + + /* Create a group with a different coloured rectangle at each + corner */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + ClutterActor *rect = clutter_rectangle_new (); + clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT); + clutter_actor_set_position (rect, + DIVISION_WIDTH * x, + DIVISION_HEIGHT * y); + clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), + corner_colors + + (y * SOURCE_DIVISIONS_X + x)); + clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL); + } + + return group; +} + +static void +pre_paint_clip_cb (void) +{ + /* Generate a clip path that clips out the top left division */ + cogl_path_move_to (DIVISION_WIDTH, 0); + cogl_path_line_to (SOURCE_SIZE, 0); + cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE); + cogl_path_line_to (0, SOURCE_SIZE); + cogl_path_line_to (0, DIVISION_HEIGHT); + cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT); + cogl_path_close (); + cogl_clip_push_from_path (); +} + +static void +post_paint_clip_cb (void) +{ + cogl_clip_pop (); +} + +static gboolean +validate_part (TestState *state, + int xpos, int ypos, + int clip_flags) +{ + int x, y; + gboolean pass = TRUE; + + /* Check whether the center of each division is the right color */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + guchar *pixels; + const ClutterColor *correct_color; + + /* Read the center pixels of this division */ + pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), + x * DIVISION_WIDTH + + DIVISION_WIDTH / 2 + xpos, + y * DIVISION_HEIGHT + + DIVISION_HEIGHT / 2 + ypos, + 1, 1); + + /* If this division is clipped then it should be the stage + color */ + if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x)))) + correct_color = &stage_color; + else + /* Otherwise it should be the color for this division */ + correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x; + + if (pixels == NULL || + pixels[0] != correct_color->red || + pixels[1] != correct_color->green || + pixels[2] != correct_color->blue) + pass = FALSE; + + g_free (pixels); + } + + return pass; +} + +static void +validate_result (TestState *state) +{ + int ypos = 0; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); + ypos++; + +#if 0 /* this doesn't work */ + if (g_test_verbose ()) + g_print ("Testing offscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); +#endif + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with rectangular clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1)); + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with path clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1)); + ypos++; + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int frame_num; + + /* XXX: Experiments have shown that for some buggy drivers, when using + * glReadPixels there is some kind of race, so we delay our test for a + * few frames and a few seconds: + */ + /* Need to increment frame first because clutter_stage_read_pixels + fires a redraw */ + frame_num = state->frame++; + if (frame_num == 2) + validate_result (state); + else if (frame_num < 2) + g_usleep (G_USEC_PER_SEC); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_texture_fbo (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + guint idle_source; + ClutterActor *actor; + int ypos = 0; + + state.frame = 0; + + state.stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* Onscreen source with clone next to it */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Offscreen source with clone */ +#if 0 /* this doesn't work */ + actor = create_source (); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); +#endif + ypos++; + + /* Source clipped to the top left division */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Source clipped to everything but top left division using a + path */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + g_signal_connect (actor, "paint", + G_CALLBACK (pre_paint_clip_cb), NULL); + g_signal_connect_after (actor, "paint", + G_CALLBACK (post_paint_clip_cb), NULL); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* 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); + + 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); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (state.stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} +