2013-12-12 09:51:00 -05:00
|
|
|
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
|
2008-11-07 14:32:28 -05:00
|
|
|
#include <clutter/clutter.h>
|
|
|
|
|
2019-07-03 07:43:25 -04:00
|
|
|
#include "tests/clutter-test-utils.h"
|
|
|
|
|
2010-02-16 07:15:23 -05:00
|
|
|
#define STAGE_WIDTH 640
|
|
|
|
#define STAGE_HEIGHT 480
|
2008-11-07 14:32:28 -05:00
|
|
|
#define ACTORS_X 12
|
|
|
|
#define ACTORS_Y 16
|
|
|
|
|
|
|
|
typedef struct _State State;
|
|
|
|
|
|
|
|
struct _State
|
|
|
|
{
|
|
|
|
ClutterActor *stage;
|
|
|
|
int y, x;
|
2011-04-11 09:11:39 -04:00
|
|
|
ClutterActor *actors[ACTORS_X * ACTORS_Y];
|
2008-11-07 14:32:28 -05:00
|
|
|
guint actor_width, actor_height;
|
2015-08-19 12:34:44 -04:00
|
|
|
guint failed_pass;
|
|
|
|
guint failed_idx;
|
2008-11-10 06:48:00 -05:00
|
|
|
gboolean pass;
|
2008-11-07 14:32:28 -05:00
|
|
|
};
|
|
|
|
|
2015-08-19 12:34:44 -04:00
|
|
|
static const char *test_passes[] = {
|
|
|
|
"No covering actor",
|
|
|
|
"Invisible covering actor",
|
|
|
|
"Clipped covering actor",
|
|
|
|
"Blur effect",
|
|
|
|
};
|
|
|
|
|
2008-11-07 14:32:28 -05:00
|
|
|
static gboolean
|
2012-01-25 18:09:38 -05:00
|
|
|
on_timeout (gpointer data)
|
2008-11-07 14:32:28 -05:00
|
|
|
{
|
2012-01-25 18:09:38 -05:00
|
|
|
State *state = data;
|
2009-03-27 17:55:40 -04:00
|
|
|
int test_num = 0;
|
2008-11-07 14:32:28 -05:00
|
|
|
int y, x;
|
2009-03-27 17:55:40 -04:00
|
|
|
ClutterActor *over_actor = NULL;
|
2008-11-07 14:32:28 -05:00
|
|
|
|
2010-10-27 13:02:47 -04:00
|
|
|
/* This will cause an unclipped pick redraw that will get buffered.
|
|
|
|
We'll check below that this buffer is discarded because we also need
|
|
|
|
to pick non-reactive actors */
|
|
|
|
clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
|
|
|
|
CLUTTER_PICK_REACTIVE, 10, 10);
|
|
|
|
|
|
|
|
clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
|
|
|
|
CLUTTER_PICK_REACTIVE, 10, 10);
|
|
|
|
|
2015-08-19 12:34:44 -04:00
|
|
|
for (test_num = 0; test_num < G_N_ELEMENTS (test_passes); test_num++)
|
2009-03-27 17:55:40 -04:00
|
|
|
{
|
|
|
|
if (test_num == 0)
|
|
|
|
{
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("No covering actor:\n");
|
|
|
|
}
|
|
|
|
if (test_num == 1)
|
|
|
|
{
|
|
|
|
static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff };
|
|
|
|
/* Create an actor that covers the whole stage but that
|
|
|
|
isn't visible so it shouldn't affect the picking */
|
|
|
|
over_actor = clutter_rectangle_new_with_color (&red);
|
|
|
|
clutter_actor_set_size (over_actor, STAGE_WIDTH, STAGE_HEIGHT);
|
2013-12-12 09:51:00 -05:00
|
|
|
clutter_actor_add_child (state->stage, over_actor);
|
2009-03-27 17:55:40 -04:00
|
|
|
clutter_actor_hide (over_actor);
|
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("Invisible covering actor:\n");
|
|
|
|
}
|
|
|
|
else if (test_num == 2)
|
|
|
|
{
|
|
|
|
/* Make the actor visible but set a clip so that only some
|
|
|
|
of the actors are accessible */
|
|
|
|
clutter_actor_show (over_actor);
|
|
|
|
clutter_actor_set_clip (over_actor,
|
|
|
|
state->actor_width * 2,
|
|
|
|
state->actor_height * 2,
|
|
|
|
state->actor_width * (ACTORS_X - 4),
|
|
|
|
state->actor_height * (ACTORS_Y - 4));
|
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("Clipped covering actor:\n");
|
|
|
|
}
|
2011-06-02 08:16:23 -04:00
|
|
|
else if (test_num == 3)
|
|
|
|
{
|
2011-11-21 20:00:58 -05:00
|
|
|
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
|
|
|
|
continue;
|
|
|
|
|
2011-06-02 08:16:23 -04:00
|
|
|
clutter_actor_hide (over_actor);
|
|
|
|
|
|
|
|
clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage),
|
|
|
|
"blur",
|
|
|
|
clutter_blur_effect_new ());
|
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("With blur effect:\n");
|
|
|
|
}
|
2009-03-27 17:55:40 -04:00
|
|
|
|
|
|
|
for (y = 0; y < ACTORS_Y; y++)
|
2011-06-02 08:16:23 -04:00
|
|
|
{
|
clutter: Introduce geometric picking
Currently, Clutter does picking by drawing with Cogl and reading
the pixel that's beneath the given point. Since Cogl has a journal
that records drawing operations, and has optimizations to read a
single pixel from a list of rectangle, it would be expected that
we would hit this fast path and not flush the journal while picking.
However, that's not the case: dithering, clipping with scissors, etc,
can all flush the journal, issuing commands to the GPU and making
picking slow. On NVidia-based systems, this glReadPixels() call is
extremely costly.
Introduce geometric picking, and avoid using the Cogl journal entirely.
Do this by introducing a stack of actors in ClutterStage. This stack
is cached, but for now, don't use the cache as much as possible.
The picking routines are still tied to painting.
When projecting the actor vertexes, do it manually and take the modelview
matrix of the framebuffer into account as well.
CPU usage on an Intel i7-7700, tested with two different GPUs/drivers:
| | Intel | Nvidia |
| ------: | --------: | -----: |
| Moving the mouse: |
| Before | 10% | 10% |
| After | 6% | 6% |
| Moving a window: |
| Before | 23% | 81% |
| After | 19% | 40% |
Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154,
https://gitlab.gnome.org/GNOME/mutter/issues/691
Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283,
https://gitlab.gnome.org/GNOME/mutter/issues/590,
https://gitlab.gnome.org/GNOME/mutter/issues/700
v2: Fix code style issues
Simplify quadrilateral checks
Remove the 0.5f hack
Differentiate axis-aligned rectangles
https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
2018-08-02 07:03:30 -04:00
|
|
|
x = 0;
|
2011-06-02 08:16:23 -04:00
|
|
|
|
|
|
|
for (; x < ACTORS_X; x++)
|
|
|
|
{
|
|
|
|
gboolean pass = FALSE;
|
|
|
|
gfloat pick_x;
|
|
|
|
ClutterActor *actor;
|
|
|
|
|
|
|
|
pick_x = x * state->actor_width + state->actor_width / 2;
|
|
|
|
|
2013-12-12 09:51:00 -05:00
|
|
|
actor =
|
|
|
|
clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
|
|
|
|
CLUTTER_PICK_ALL,
|
|
|
|
pick_x,
|
|
|
|
y * state->actor_height
|
|
|
|
+ state->actor_height / 2);
|
2011-06-02 08:16:23 -04:00
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("% 3i,% 3i / %p -> ",
|
|
|
|
x, y, state->actors[y * ACTORS_X + x]);
|
|
|
|
|
|
|
|
if (actor == NULL)
|
|
|
|
{
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("NULL: FAIL\n");
|
|
|
|
}
|
|
|
|
else if (actor == over_actor)
|
|
|
|
{
|
|
|
|
if (test_num == 2
|
|
|
|
&& x >= 2 && x < ACTORS_X - 2
|
|
|
|
&& y >= 2 && y < ACTORS_Y - 2)
|
|
|
|
pass = TRUE;
|
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("over_actor: %s\n", pass ? "pass" : "FAIL");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (actor == state->actors[y * ACTORS_X + x]
|
|
|
|
&& (test_num != 2
|
|
|
|
|| x < 2 || x >= ACTORS_X - 2
|
|
|
|
|| y < 2 || y >= ACTORS_Y - 2))
|
|
|
|
pass = TRUE;
|
|
|
|
|
|
|
|
if (g_test_verbose ())
|
|
|
|
g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pass)
|
2015-08-19 12:34:44 -04:00
|
|
|
{
|
|
|
|
state->failed_pass = test_num;
|
|
|
|
state->failed_idx = y * ACTORS_X + x;
|
|
|
|
state->pass = FALSE;
|
|
|
|
}
|
2011-06-02 08:16:23 -04:00
|
|
|
}
|
|
|
|
}
|
2009-03-27 17:55:40 -04:00
|
|
|
}
|
2008-11-07 14:32:28 -05:00
|
|
|
|
|
|
|
clutter_main_quit ();
|
|
|
|
|
2012-01-25 18:09:38 -05:00
|
|
|
return G_SOURCE_REMOVE;
|
2008-11-07 14:32:28 -05:00
|
|
|
}
|
|
|
|
|
2013-12-12 09:51:00 -05:00
|
|
|
static void
|
2012-02-27 08:08:31 -05:00
|
|
|
actor_pick (void)
|
2008-11-07 14:32:28 -05:00
|
|
|
{
|
|
|
|
int y, x;
|
|
|
|
State state;
|
|
|
|
|
2008-11-10 06:48:00 -05:00
|
|
|
state.pass = TRUE;
|
2008-11-07 14:32:28 -05:00
|
|
|
|
2013-12-12 09:51:00 -05:00
|
|
|
state.stage = clutter_test_get_stage ();
|
2008-11-07 14:32:28 -05:00
|
|
|
|
|
|
|
state.actor_width = STAGE_WIDTH / ACTORS_X;
|
|
|
|
state.actor_height = STAGE_HEIGHT / ACTORS_Y;
|
|
|
|
|
|
|
|
for (y = 0; y < ACTORS_Y; y++)
|
|
|
|
for (x = 0; x < ACTORS_X; x++)
|
|
|
|
{
|
2020-03-31 17:58:44 -04:00
|
|
|
ClutterColor color = { x * 255 / (ACTORS_X - 1),
|
|
|
|
y * 255 / (ACTORS_Y - 1),
|
|
|
|
128, 255 };
|
|
|
|
ClutterActor *rect = clutter_rectangle_new_with_color (&color);
|
2008-11-07 14:32:28 -05:00
|
|
|
|
2011-11-08 09:46:16 -05:00
|
|
|
clutter_actor_set_position (rect,
|
|
|
|
x * state.actor_width,
|
|
|
|
y * state.actor_height);
|
|
|
|
clutter_actor_set_size (rect,
|
|
|
|
state.actor_width,
|
|
|
|
state.actor_height);
|
2008-11-07 14:32:28 -05:00
|
|
|
|
2020-03-31 17:58:44 -04:00
|
|
|
clutter_actor_add_child (state.stage, rect);
|
2008-11-07 14:32:28 -05:00
|
|
|
|
2020-03-31 17:58:44 -04:00
|
|
|
state.actors[y * ACTORS_X + x] = rect;
|
2008-11-07 14:32:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
clutter_actor_show (state.stage);
|
|
|
|
|
2012-01-25 18:09:38 -05:00
|
|
|
clutter_threads_add_idle (on_timeout, &state);
|
2008-11-07 14:32:28 -05:00
|
|
|
|
|
|
|
clutter_main ();
|
|
|
|
|
2015-08-19 12:34:44 -04:00
|
|
|
if (g_test_verbose ())
|
|
|
|
{
|
|
|
|
if (!state.pass)
|
|
|
|
g_test_message ("Failed pass: %s[%d], actor index: %d [%p]\n",
|
|
|
|
test_passes[state.failed_pass],
|
|
|
|
state.failed_pass,
|
|
|
|
state.failed_idx,
|
|
|
|
state.actors[state.failed_idx]);
|
|
|
|
}
|
|
|
|
|
2008-11-10 07:28:42 -05:00
|
|
|
g_assert (state.pass);
|
2008-11-07 14:32:28 -05:00
|
|
|
}
|
2013-12-12 09:51:00 -05:00
|
|
|
|
|
|
|
CLUTTER_TEST_SUITE (
|
|
|
|
CLUTTER_TEST_UNIT ("/actor/pick", actor_pick)
|
|
|
|
)
|