diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index f19776622..63425d684 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -26,6 +26,7 @@ test_conformance_SOURCES = \ test-cogl-readpixels.c \ test-cogl-multitexture.c \ test-cogl-texture-mipmaps.c \ + test-cogl-texture-rectangle.c \ test-cogl-pixel-buffer.c \ test-path.c \ test-pick.c \ diff --git a/tests/conform/test-cogl-texture-rectangle.c b/tests/conform/test-cogl-texture-rectangle.c new file mode 100644 index 000000000..81b555876 --- /dev/null +++ b/tests/conform/test-cogl-texture-rectangle.c @@ -0,0 +1,268 @@ +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; + guint frame; +} TestState; + +static CoglHandle +create_source_rect (void) +{ +#ifdef GL_TEXTURE_RECTANGLE_ARB + + int x, y; + guint8 *data = g_malloc (256 * 256 * 4), *p = data; + CoglHandle tex; + GLuint gl_tex; + + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = x; + *(p++) = y; + *(p++) = 0; + *(p++) = 255; + } + + 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); + + g_assert (glGetError () == GL_NO_ERROR); + + g_free (data); + + tex = cogl_texture_new_from_foreign (gl_tex, + GL_TEXTURE_RECTANGLE_ARB, + 256, 256, 0, 0, + COGL_PIXEL_FORMAT_RGBA_8888); + + return tex; + +#else /* GL_TEXTURE_RECTANGLE_ARB */ + + return COGL_INVALID_HANDLE; + +#endif /* GL_TEXTURE_RECTANGLE_ARB */ +} + +static CoglHandle +create_source_2d (void) +{ + int x, y; + guint8 *data = g_malloc (256 * 256 * 4), *p = data; + CoglHandle tex; + + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = 0; + *(p++) = x; + *(p++) = y; + *(p++) = 255; + } + + tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + 256 * 4, + data); + + g_free (data); + + return tex; +} + +static void +draw_frame (TestState *state) +{ + GLuint gl_tex; + CoglHandle tex_rect = create_source_rect (); + CoglHandle material_rect = cogl_material_new (); + CoglHandle tex_2d = create_source_2d (); + CoglHandle material_2d = cogl_material_new (); + + g_assert (tex_rect != COGL_INVALID_HANDLE); + + cogl_material_set_layer (material_rect, 0, tex_rect); + cogl_material_set_layer_filters (material_rect, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + + cogl_material_set_layer (material_2d, 0, tex_2d); + cogl_material_set_layer_filters (material_2d, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + + cogl_set_source (material_rect); + + /* Render the texture repeated horizontally twice */ + cogl_rectangle_with_texture_coords (0.0f, 0.0f, 512.0f, 256.0f, + 0.0f, 0.0f, 2.0f, 1.0f); + /* Render the top half of the texture to test without repeating */ + cogl_rectangle_with_texture_coords (0.0f, 256.0f, 256.0f, 384.0f, + 0.0f, 0.0f, 1.0f, 0.5f); + + cogl_set_source (material_2d); + + /* Render the top half of a regular 2D texture */ + cogl_rectangle_with_texture_coords (256.0f, 256.0f, 512.0f, 384.0f, + 0.0f, 0.0f, 1.0f, 0.5f); + + /* Flush the rendering now so we can safely delete the texture */ + cogl_flush (); + + cogl_handle_unref (material_rect); + + /* Cogl doesn't destroy foreign textures so we have to do it manually */ + cogl_texture_get_gl_texture (tex_rect, &gl_tex, NULL); + glDeleteTextures (1, &gl_tex); + cogl_handle_unref (tex_rect); +} + +static void +validate_result (TestState *state) +{ + guint8 *data, *p; + int x, y; + + p = data = g_malloc (512 * 256 * 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) +{ + int frame_num; + + draw_frame (state); + + /* 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; +} + +static gboolean +check_rectangle_extension (void) +{ + static const char rect_extension[] = "GL_ARB_texture_rectangle"; + const char *extensions = (const char *) glGetString (GL_EXTENSIONS); + const char *extensions_end; + + extensions_end = extensions + strlen (extensions); + + while (extensions < extensions_end) + { + const char *end = strchr (extensions, ' '); + + if (end == NULL) + end = extensions_end; + + if (end - extensions == sizeof (rect_extension) - 1 && + !memcmp (extensions, rect_extension, sizeof (rect_extension) - 1)) + return TRUE; + + extensions = end + 1; + } + + return FALSE; +} + +void +test_cogl_texture_rectangle (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + guint idle_source; + guint paint_handler; + + state.frame = 0; + + state.stage = clutter_stage_get_default (); + + /* Check whether GL supports the rectangle extension. If not we'll + just assume the test passes */ + if (check_rectangle_extension ()) + { + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, state.stage); + + paint_handler = g_signal_connect_after (state.stage, "paint", + G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_source_remove (idle_source); + g_signal_handler_disconnect (state.stage, paint_handler); + + if (g_test_verbose ()) + g_print ("OK\n"); + } + else if (g_test_verbose ()) + g_print ("Skipping\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 54c4fd979..5a0801e8e 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -198,6 +198,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_texture_mipmaps); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_sub_texture); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_pixel_buffer); + TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_texture_rectangle); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved);