mutter/tests/conform/test-cogl-materials.c
Neil Roberts cbe1e8321b tests: Dynamically resolve GL symbols
Some of the tests are making direct GL calls. Eventually we want
Clutter not to link directly against any GL library so that it can
leave Cogl to load it dynamically. As a step towards getting this to
work this patch changes the tests to resolve the symbols using
cogl_get_proc_address instead of linking directly.
2011-07-19 16:06:06 +01:00

351 lines
10 KiB
C

#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 };
static TestConformGLFunctions gl_functions;
#define QUAD_WIDTH 20
#define RED 0
#define GREEN 1
#define BLUE 2
#define ALPHA 3
#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
#define MASK_ALPHA(COLOR) (COLOR & 0xff)
typedef struct _TestState
{
ClutterGeometry stage_geom;
} TestState;
static void
check_pixel (TestState *state, int x, int y, guint32 color)
{
GLint y_off;
GLint x_off;
GLubyte pixel[4];
guint8 r = MASK_RED (color);
guint8 g = MASK_GREEN (color);
guint8 b = MASK_BLUE (color);
guint8 a = MASK_ALPHA (color);
/* See what we got... */
y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
cogl_read_pixels (x_off, y_off, 1, 1,
COGL_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
pixel);
if (g_test_verbose ())
g_print (" result = %02x, %02x, %02x, %02x\n",
pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
if (g_test_verbose ())
g_print (" expected = %x, %x, %x, %x\n",
r, g, b, a);
/* FIXME - allow for hardware in-precision */
g_assert (pixel[RED] == r);
g_assert (pixel[GREEN] == g);
g_assert (pixel[BLUE] == b);
/* FIXME
* We ignore the alpha, since we don't know if our render target is
* RGB or RGBA */
/* g_assert (pixel[ALPHA] == a); */
}
static void
test_material_with_primitives (TestState *state,
int x, int y,
guint32 color)
{
CoglTextureVertex verts[4] = {
{ .x = 0, .y = 0, .z = 0 },
{ .x = 0, .y = QUAD_WIDTH, .z = 0 },
{ .x = QUAD_WIDTH, .y = QUAD_WIDTH, .z = 0 },
{ .x = QUAD_WIDTH, .y = 0, .z = 0 },
};
CoglHandle vbo;
cogl_push_matrix ();
cogl_translate (x * QUAD_WIDTH, y * QUAD_WIDTH, 0);
cogl_rectangle (0, 0, QUAD_WIDTH, QUAD_WIDTH);
cogl_translate (0, QUAD_WIDTH, 0);
cogl_polygon (verts, 4, FALSE);
cogl_translate (0, QUAD_WIDTH, 0);
vbo = cogl_vertex_buffer_new (4);
cogl_vertex_buffer_add (vbo,
"gl_Vertex",
2, /* n components */
COGL_ATTRIBUTE_TYPE_FLOAT,
FALSE, /* normalized */
sizeof (CoglTextureVertex), /* stride */
verts);
cogl_vertex_buffer_draw (vbo,
COGL_VERTICES_MODE_TRIANGLE_FAN,
0, /* first */
4); /* count */
cogl_handle_unref (vbo);
cogl_pop_matrix ();
check_pixel (state, x, y, color);
check_pixel (state, x, y+1, color);
check_pixel (state, x, y+2, color);
}
static void
test_invalid_texture_layers (TestState *state, int x, int y)
{
CoglHandle material = cogl_material_new ();
/* explicitly create a layer with an invalid handle. This may be desireable
* if the user also sets a texture combine string that e.g. refers to a
* constant color. */
cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE);
cogl_set_source (material);
cogl_handle_unref (material);
/* We expect a white fallback material to be used */
test_material_with_primitives (state, x, y, 0xffffffff);
}
static gboolean
using_gles2_driver (void)
{
/* FIXME: This should probably be replaced with some way to query
the driver from Cogl */
return g_str_has_prefix ((const char *) gl_functions.glGetString (GL_VERSION),
"OpenGL ES 2");
}
static void
test_using_all_layers (TestState *state, int x, int y)
{
CoglHandle material = cogl_material_new ();
guint8 white_pixel[] = { 0xff, 0xff, 0xff, 0xff };
guint8 red_pixel[] = { 0xff, 0x00, 0x00, 0xff };
CoglHandle white_texture;
CoglHandle red_texture;
GLint n_layers;
int i;
/* Create a material that uses the maximum number of layers. All but
the last layer will use a solid white texture. The last layer
will use a red texture. The layers will all be modulated together
so the final fragment should be red. */
white_texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
COGL_PIXEL_FORMAT_ANY,
4, white_pixel);
red_texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
COGL_PIXEL_FORMAT_ANY,
4, red_pixel);
/* FIXME: Cogl doesn't provide a way to query the maximum number of
texture layers so for now we'll just ask GL directly. */
#ifdef COGL_HAS_GLES2
if (using_gles2_driver ())
{
GLint n_image_units, n_attribs;
/* GLES 2 doesn't have GL_MAX_TEXTURE_UNITS and it uses
GL_MAX_TEXTURE_IMAGE_UNITS instead */
gl_functions.glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n_image_units);
/* Cogl needs a vertex attrib for each layer to upload the texture
coordinates */
gl_functions.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
#endif
{
#if defined(COGL_HAS_GLES1) || defined(COGL_HAS_GL)
gl_functions.glGetIntegerv (GL_MAX_TEXTURE_UNITS, &n_layers);
#endif
}
/* FIXME: is this still true? */
/* Cogl currently can't cope with more than 32 layers so we'll also
limit the maximum to that. */
if (n_layers > 32)
n_layers = 32;
for (i = 0; i < n_layers; i++)
{
cogl_material_set_layer_filters (material, i,
COGL_MATERIAL_FILTER_NEAREST,
COGL_MATERIAL_FILTER_NEAREST);
cogl_material_set_layer (material, i,
i == n_layers - 1 ? red_texture : white_texture);
}
cogl_set_source (material);
cogl_handle_unref (material);
cogl_handle_unref (white_texture);
cogl_handle_unref (red_texture);
/* We expect the final fragment to be red */
test_material_with_primitives (state, x, y, 0xff0000ff);
}
static void
test_invalid_texture_layers_with_constant_colors (TestState *state,
int x, int y)
{
CoglHandle material = cogl_material_new ();
CoglColor constant_color;
/* explicitly create a layer with an invalid handle */
cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE);
/* ignore the fallback texture on the layer and use a constant color
instead */
cogl_color_init_from_4ub (&constant_color, 0, 0, 255, 255);
cogl_material_set_layer_combine (material, 0,
"RGBA=REPLACE(CONSTANT)",
NULL);
cogl_material_set_layer_combine_constant (material, 0, &constant_color);
cogl_set_source (material);
cogl_handle_unref (material);
/* We expect the final fragments to be green */
test_material_with_primitives (state, x, y, 0x0000ffff);
}
static void
basic_ref_counting_destroy_cb (void *user_data)
{
gboolean *destroyed_flag = user_data;
g_assert (*destroyed_flag == FALSE);
*destroyed_flag = TRUE;
}
static void
test_basic_ref_counting (void)
{
CoglMaterial *material_parent;
gboolean parent_destroyed = FALSE;
CoglMaterial *material_child;
gboolean child_destroyed = FALSE;
static CoglUserDataKey user_data_key;
/* This creates a material with a copy and then just unrefs them
both without setting them as a source. They should immediately be
freed. We can test whether they were freed or not by registering
a destroy callback with some user data */
material_parent = cogl_material_new ();
/* Set some user data so we can detect when the material is
destroyed */
cogl_object_set_user_data (COGL_OBJECT (material_parent),
&user_data_key,
&parent_destroyed,
basic_ref_counting_destroy_cb);
material_child = cogl_material_copy (material_parent);
cogl_object_set_user_data (COGL_OBJECT (material_child),
&user_data_key,
&child_destroyed,
basic_ref_counting_destroy_cb);
cogl_object_unref (material_child);
cogl_object_unref (material_parent);
g_assert (parent_destroyed);
g_assert (child_destroyed);
}
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 */
);
test_basic_ref_counting ();
/* Comment this out if you want visual feedback for what this test paints */
#if 1
clutter_main_quit ();
#endif
}
static gboolean
queue_redraw (gpointer stage)
{
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
return TRUE;
}
void
test_cogl_materials (TestConformSimpleFixture *fixture,
gconstpointer data)
{
TestState state;
ClutterActor *stage;
ClutterActor *group;
guint idle_source;
test_conform_get_gl_functions (&gl_functions);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
clutter_actor_get_geometry (stage, &state.stage_geom);
group = clutter_group_new ();
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
/* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that
* will trigger redrawing. */
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
clutter_actor_show_all (stage);
clutter_main ();
g_source_remove (idle_source);
if (g_test_verbose ())
g_print ("OK\n");
}