mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 16:40:41 -05:00
355 lines
9.7 KiB
C
355 lines
9.7 KiB
C
|
|
||
|
#include <clutter/clutter.h>
|
||
|
|
||
|
#ifndef COGL_ENABLE_EXPERIMENTAL_API
|
||
|
#define COGL_ENABLE_EXPERIMENTAL_API
|
||
|
#endif
|
||
|
#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
|
||
|
{
|
||
|
guint frame;
|
||
|
ClutterGeometry stage_geom;
|
||
|
} TestState;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
guint32 color;
|
||
|
float depth;
|
||
|
gboolean test_enable;
|
||
|
CoglDepthTestFunction test_function;
|
||
|
gboolean write_enable;
|
||
|
float range_near;
|
||
|
float range_far;
|
||
|
} TestDepthState;
|
||
|
|
||
|
static void
|
||
|
check_pixel (GLubyte *pixel, guint32 color)
|
||
|
{
|
||
|
guint8 r = MASK_RED (color);
|
||
|
guint8 g = MASK_GREEN (color);
|
||
|
guint8 b = MASK_BLUE (color);
|
||
|
guint8 a = MASK_ALPHA (color);
|
||
|
|
||
|
if (g_test_verbose ())
|
||
|
g_print (" expected = %x, %x, %x, %x\n",
|
||
|
r, g, b, a);
|
||
|
/* FIXME - allow for hardware in-precision */
|
||
|
g_assert_cmpint (pixel[RED], ==, r);
|
||
|
g_assert_cmpint (pixel[GREEN], ==, g);
|
||
|
g_assert_cmpint (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 gboolean
|
||
|
draw_rectangle (TestState *state,
|
||
|
int x,
|
||
|
int y,
|
||
|
TestDepthState *rect_state)
|
||
|
{
|
||
|
guint8 Cr = MASK_RED (rect_state->color);
|
||
|
guint8 Cg = MASK_GREEN (rect_state->color);
|
||
|
guint8 Cb = MASK_BLUE (rect_state->color);
|
||
|
guint8 Ca = MASK_ALPHA (rect_state->color);
|
||
|
CoglHandle material;
|
||
|
|
||
|
material = cogl_material_new ();
|
||
|
cogl_material_set_depth_test_enabled (material, rect_state->test_enable);
|
||
|
cogl_material_set_depth_test_function (material, rect_state->test_function);
|
||
|
cogl_material_set_depth_writing_enabled (material, rect_state->write_enable);
|
||
|
if (!cogl_material_set_depth_range (material,
|
||
|
rect_state->range_near,
|
||
|
rect_state->range_far,
|
||
|
NULL))
|
||
|
{
|
||
|
cogl_handle_unref (material);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
cogl_material_set_color4ub (material, Cr, Cg, Cb, Ca);
|
||
|
|
||
|
cogl_set_source (material);
|
||
|
|
||
|
cogl_push_matrix ();
|
||
|
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_handle_unref (material);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_depth (TestState *state,
|
||
|
int x,
|
||
|
int y,
|
||
|
TestDepthState *rect0_state,
|
||
|
TestDepthState *rect1_state,
|
||
|
TestDepthState *rect2_state,
|
||
|
guint32 expected_result)
|
||
|
{
|
||
|
GLubyte pixel[4];
|
||
|
GLint y_off;
|
||
|
GLint x_off;
|
||
|
gboolean missing_feature = FALSE;
|
||
|
|
||
|
if (rect0_state)
|
||
|
missing_feature |= !draw_rectangle (state, x, y, rect0_state);
|
||
|
if (rect1_state)
|
||
|
missing_feature |= !draw_rectangle (state, x, y, rect1_state);
|
||
|
if (rect2_state)
|
||
|
missing_feature |= !draw_rectangle (state, x, y, rect2_state);
|
||
|
|
||
|
/* We don't consider it an error that we can't test something
|
||
|
* the driver doesn't support. */
|
||
|
if (missing_feature)
|
||
|
return;
|
||
|
|
||
|
/* See what we got... */
|
||
|
|
||
|
y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
|
||
|
x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
|
||
|
|
||
|
/* XXX:
|
||
|
* We haven't always had good luck with GL drivers implementing glReadPixels
|
||
|
* reliably and skipping the first two frames improves our chances... */
|
||
|
if (state->frame <= 2)
|
||
|
return;
|
||
|
|
||
|
cogl_read_pixels (x_off, y_off, 1, 1,
|
||
|
COGL_READ_PIXELS_COLOR_BUFFER,
|
||
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
||
|
pixel);
|
||
|
|
||
|
check_pixel (pixel, expected_result);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_paint (ClutterActor *actor, TestState *state)
|
||
|
{
|
||
|
int frame_num;
|
||
|
CoglMatrix projection_save;
|
||
|
CoglMatrix identity;
|
||
|
|
||
|
/* We don't want the effects of perspective division to interfere
|
||
|
* with the positions of our test rectangles on the x and y axis
|
||
|
* so we use an orthographic projection...
|
||
|
*/
|
||
|
|
||
|
cogl_get_projection_matrix (&projection_save);
|
||
|
|
||
|
cogl_ortho (0, state->stage_geom.width, /* left, right */
|
||
|
state->stage_geom.height, 0, /* bottom, top */
|
||
|
-1, 100 /* z near, far */);
|
||
|
|
||
|
cogl_push_matrix ();
|
||
|
cogl_matrix_init_identity (&identity);
|
||
|
cogl_set_modelview_matrix (&identity);
|
||
|
|
||
|
/* 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 */
|
||
|
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 */
|
||
|
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 */
|
||
|
0, 1 /* depth range */
|
||
|
};
|
||
|
|
||
|
test_depth (state, 0, 0, /* position */
|
||
|
&rect0_state, &rect1_state, &rect2_state,
|
||
|
0x00ff00ff); /* expected */
|
||
|
|
||
|
rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS;
|
||
|
test_depth (state, 1, 0, /* position */
|
||
|
&rect0_state, &rect1_state, &rect2_state,
|
||
|
0x0000ffff); /* expected */
|
||
|
|
||
|
rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS;
|
||
|
test_depth (state, 2, 0, /* position */
|
||
|
&rect0_state, &rect1_state, &rect2_state,
|
||
|
0x0000ffff); /* expected */
|
||
|
|
||
|
rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER;
|
||
|
test_depth (state, 3, 0, /* position */
|
||
|
&rect0_state, &rect1_state, &rect2_state,
|
||
|
0x00ff00ff); /* expected */
|
||
|
|
||
|
rect0_state.test_enable = TRUE;
|
||
|
rect1_state.write_enable = FALSE;
|
||
|
test_depth (state, 4, 0, /* position */
|
||
|
&rect0_state, &rect1_state, &rect2_state,
|
||
|
0x0000ffff); /* 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 */
|
||
|
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 */
|
||
|
0, 0.5 /* depth range */
|
||
|
};
|
||
|
|
||
|
test_depth (state, 0, 1, /* position */
|
||
|
&rect0_state, &rect1_state, NULL,
|
||
|
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 */
|
||
|
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 */
|
||
|
0, 1 /* depth range */
|
||
|
};
|
||
|
|
||
|
cogl_set_depth_test_enabled (TRUE);
|
||
|
test_depth (state, 0, 2, /* position */
|
||
|
&rect0_state, &rect1_state, NULL,
|
||
|
0xff0000ff); /* expected */
|
||
|
cogl_set_depth_test_enabled (FALSE);
|
||
|
test_depth (state, 1, 2, /* position */
|
||
|
&rect0_state, &rect1_state, NULL,
|
||
|
0x00ff00ff); /* expected */
|
||
|
}
|
||
|
|
||
|
cogl_pop_matrix ();
|
||
|
cogl_set_projection_matrix (&projection_save);
|
||
|
|
||
|
/* 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: */
|
||
|
frame_num = state->frame++;
|
||
|
if (frame_num < 2)
|
||
|
g_usleep (G_USEC_PER_SEC);
|
||
|
|
||
|
/* Comment this out if you want visual feedback for what this test paints */
|
||
|
#if 1
|
||
|
if (frame_num == 3)
|
||
|
clutter_main_quit ();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
queue_redraw (gpointer stage)
|
||
|
{
|
||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
test_cogl_depth_test (TestConformSimpleFixture *fixture,
|
||
|
gconstpointer data)
|
||
|
{
|
||
|
TestState state;
|
||
|
ClutterActor *stage;
|
||
|
ClutterActor *group;
|
||
|
guint idle_source;
|
||
|
|
||
|
state.frame = 0;
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
|