cbe1e8321b
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.
351 lines
10 KiB
C
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");
|
|
}
|
|
|