mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 18:40:40 -05:00
49c8d42317
When painting, actors rely on semi global state tracked by the state to get various things needed for painting, such as the current draw framebuffer. Having state hidden in such ways can be very deceiving as it's hard to follow changes spread out, and adding more and more state that should be tracked during a paint gets annoying as they will not change in isolation but one by one in their own places. To do this better, introduce a paint context that is passed along in paint calls that contains the necessary state needed during painting. The paint context implements a framebuffer stack just as Cogl works, which is currently needed for offscreen rendering used by clutter. The same context is passed around for paint nodes, contents and effects as well. In this commit, the context is only introduced, but not used. It aims to replace the Cogl framebuffer stack, and will allow actors to know what view it is currently painted on. https://gitlab.gnome.org/GNOME/mutter/merge_requests/935
293 lines
7.9 KiB
C
293 lines
7.9 KiB
C
#define CLUTTER_ENABLE_EXPERIMENTAL_API
|
|
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
|
|
#include <clutter/clutter.h>
|
|
|
|
#include "tests/clutter-test-utils.h"
|
|
|
|
/****************************************************************
|
|
Old style shader effect
|
|
This uses clutter_shader_effect_set_source
|
|
****************************************************************/
|
|
|
|
static const gchar
|
|
old_shader_effect_source[] =
|
|
"uniform vec3 override_color;\n"
|
|
"\n"
|
|
"void\n"
|
|
"main ()\n"
|
|
"{\n"
|
|
" cogl_color_out = vec4 (override_color, 1.0);\n"
|
|
"}";
|
|
|
|
typedef struct _FooOldShaderEffectClass
|
|
{
|
|
ClutterShaderEffectClass parent_class;
|
|
} FooOldShaderEffectClass;
|
|
|
|
typedef struct _FooOldShaderEffect
|
|
{
|
|
ClutterShaderEffect parent;
|
|
} FooOldShaderEffect;
|
|
|
|
GType foo_old_shader_effect_get_type (void);
|
|
|
|
G_DEFINE_TYPE (FooOldShaderEffect,
|
|
foo_old_shader_effect,
|
|
CLUTTER_TYPE_SHADER_EFFECT);
|
|
|
|
static void
|
|
foo_old_shader_effect_paint_target (ClutterOffscreenEffect *effect,
|
|
ClutterPaintContext *paint_context)
|
|
{
|
|
clutter_shader_effect_set_shader_source (CLUTTER_SHADER_EFFECT (effect),
|
|
old_shader_effect_source);
|
|
clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect),
|
|
"override_color",
|
|
G_TYPE_FLOAT, 3,
|
|
1.0f, 0.0f, 0.0f);
|
|
|
|
CLUTTER_OFFSCREEN_EFFECT_CLASS (foo_old_shader_effect_parent_class)->
|
|
paint_target (effect, paint_context);
|
|
}
|
|
|
|
static void
|
|
foo_old_shader_effect_class_init (FooOldShaderEffectClass *klass)
|
|
{
|
|
ClutterOffscreenEffectClass *offscreen_effect_class =
|
|
CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
|
|
|
|
offscreen_effect_class->paint_target = foo_old_shader_effect_paint_target;
|
|
}
|
|
|
|
static void
|
|
foo_old_shader_effect_init (FooOldShaderEffect *self)
|
|
{
|
|
}
|
|
|
|
/****************************************************************
|
|
New style shader effect
|
|
This overrides get_static_shader_source()
|
|
****************************************************************/
|
|
|
|
static const gchar
|
|
new_shader_effect_source[] =
|
|
"uniform vec3 override_color;\n"
|
|
"\n"
|
|
"void\n"
|
|
"main ()\n"
|
|
"{\n"
|
|
" cogl_color_out = (vec4 (override_color, 1.0) +\n"
|
|
" vec4 (0.0, 0.0, 1.0, 0.0));\n"
|
|
"}";
|
|
|
|
typedef struct _FooNewShaderEffectClass
|
|
{
|
|
ClutterShaderEffectClass parent_class;
|
|
} FooNewShaderEffectClass;
|
|
|
|
typedef struct _FooNewShaderEffect
|
|
{
|
|
ClutterShaderEffect parent;
|
|
} FooNewShaderEffect;
|
|
|
|
GType foo_new_shader_effect_get_type (void);
|
|
|
|
G_DEFINE_TYPE (FooNewShaderEffect,
|
|
foo_new_shader_effect,
|
|
CLUTTER_TYPE_SHADER_EFFECT);
|
|
|
|
static gchar *
|
|
foo_new_shader_effect_get_static_source (ClutterShaderEffect *effect)
|
|
{
|
|
static gboolean already_called = FALSE;
|
|
|
|
/* This should only be called once even though we have two actors
|
|
using this effect */
|
|
g_assert (!already_called);
|
|
|
|
already_called = TRUE;
|
|
|
|
return g_strdup (new_shader_effect_source);
|
|
}
|
|
|
|
static void
|
|
foo_new_shader_effect_paint_target (ClutterOffscreenEffect *effect,
|
|
ClutterPaintContext *paint_context)
|
|
{
|
|
clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect),
|
|
"override_color",
|
|
G_TYPE_FLOAT, 3,
|
|
0.0f, 1.0f, 0.0f);
|
|
|
|
CLUTTER_OFFSCREEN_EFFECT_CLASS (foo_new_shader_effect_parent_class)->
|
|
paint_target (effect, paint_context);
|
|
}
|
|
|
|
static void
|
|
foo_new_shader_effect_class_init (FooNewShaderEffectClass *klass)
|
|
{
|
|
ClutterOffscreenEffectClass *offscreen_effect_class =
|
|
CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
|
|
ClutterShaderEffectClass *shader_effect_class =
|
|
CLUTTER_SHADER_EFFECT_CLASS (klass);
|
|
|
|
offscreen_effect_class->paint_target = foo_new_shader_effect_paint_target;
|
|
|
|
shader_effect_class->get_static_shader_source =
|
|
foo_new_shader_effect_get_static_source;
|
|
}
|
|
|
|
static void
|
|
foo_new_shader_effect_init (FooNewShaderEffect *self)
|
|
{
|
|
}
|
|
|
|
/****************************************************************
|
|
Another new style shader effect
|
|
This is the same but with a different shader. This is just
|
|
sanity check that each class gets its own copy of the private
|
|
data
|
|
****************************************************************/
|
|
|
|
static const gchar
|
|
another_new_shader_effect_source[] =
|
|
"\n"
|
|
"void\n"
|
|
"main ()\n"
|
|
"{\n"
|
|
" cogl_color_out = vec4 (1.0, 0.0, 1.0, 1.0);\n"
|
|
"}";
|
|
|
|
typedef struct _FooAnotherNewShaderEffectClass
|
|
{
|
|
ClutterShaderEffectClass parent_class;
|
|
} FooAnotherNewShaderEffectClass;
|
|
|
|
typedef struct _FooAnotherNewShaderEffect
|
|
{
|
|
ClutterShaderEffect parent;
|
|
} FooAnotherNewShaderEffect;
|
|
|
|
GType foo_another_new_shader_effect_get_type (void);
|
|
|
|
G_DEFINE_TYPE (FooAnotherNewShaderEffect,
|
|
foo_another_new_shader_effect,
|
|
CLUTTER_TYPE_SHADER_EFFECT);
|
|
|
|
static gchar *
|
|
foo_another_new_shader_effect_get_static_source (ClutterShaderEffect *effect)
|
|
{
|
|
return g_strdup (another_new_shader_effect_source);
|
|
}
|
|
|
|
static void
|
|
foo_another_new_shader_effect_class_init (FooAnotherNewShaderEffectClass *klass)
|
|
{
|
|
ClutterShaderEffectClass *shader_effect_class =
|
|
CLUTTER_SHADER_EFFECT_CLASS (klass);
|
|
|
|
shader_effect_class->get_static_shader_source =
|
|
foo_another_new_shader_effect_get_static_source;
|
|
}
|
|
|
|
static void
|
|
foo_another_new_shader_effect_init (FooAnotherNewShaderEffect *self)
|
|
{
|
|
}
|
|
|
|
/****************************************************************/
|
|
|
|
static ClutterActor *
|
|
make_actor (GType shader_type)
|
|
{
|
|
ClutterActor *rect;
|
|
const ClutterColor white = { 0xff, 0xff, 0xff, 0xff };
|
|
|
|
rect = clutter_rectangle_new ();
|
|
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &white);
|
|
clutter_actor_set_size (rect, 50, 50);
|
|
|
|
clutter_actor_add_effect (rect, g_object_new (shader_type, NULL));
|
|
|
|
return rect;
|
|
}
|
|
|
|
static guint32
|
|
get_pixel (CoglFramebuffer *fb,
|
|
int x,
|
|
int y)
|
|
{
|
|
guint8 data[4];
|
|
|
|
cogl_framebuffer_read_pixels (fb,
|
|
x, y, 1, 1,
|
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
|
data);
|
|
|
|
return (((guint32) data[0] << 16) |
|
|
((guint32) data[1] << 8) |
|
|
data[2]);
|
|
}
|
|
|
|
static void
|
|
view_painted_cb (ClutterStage *stage,
|
|
ClutterStageView *view,
|
|
gpointer data)
|
|
{
|
|
CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
|
|
gboolean *was_painted = data;
|
|
|
|
/* old shader effect */
|
|
g_assert_cmpint (get_pixel (fb, 0, 25), ==, 0xff0000);
|
|
/* new shader effect */
|
|
g_assert_cmpint (get_pixel (fb, 100, 25), ==, 0x00ffff);
|
|
/* another new shader effect */
|
|
g_assert_cmpint (get_pixel (fb, 200, 25), ==, 0xff00ff);
|
|
/* new shader effect */
|
|
g_assert_cmpint (get_pixel (fb, 300, 25), ==, 0x00ffff);
|
|
|
|
*was_painted = TRUE;
|
|
}
|
|
|
|
static void
|
|
actor_shader_effect (void)
|
|
{
|
|
ClutterActor *stage;
|
|
ClutterActor *rect;
|
|
gboolean was_painted;
|
|
|
|
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
|
|
return;
|
|
|
|
stage = clutter_stage_new ();
|
|
|
|
rect = make_actor (foo_old_shader_effect_get_type ());
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
|
|
|
|
rect = make_actor (foo_new_shader_effect_get_type ());
|
|
clutter_actor_set_x (rect, 100);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
|
|
|
|
rect = make_actor (foo_another_new_shader_effect_get_type ());
|
|
clutter_actor_set_x (rect, 200);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
|
|
|
|
rect = make_actor (foo_new_shader_effect_get_type ());
|
|
clutter_actor_set_x (rect, 300);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
|
|
|
|
clutter_actor_show (stage);
|
|
|
|
was_painted = FALSE;
|
|
g_signal_connect_after (stage, "paint-view",
|
|
G_CALLBACK (view_painted_cb),
|
|
&was_painted);
|
|
|
|
while (!was_painted)
|
|
g_main_context_iteration (NULL, FALSE);
|
|
}
|
|
|
|
CLUTTER_TEST_SUITE (
|
|
CLUTTER_TEST_UNIT ("/actor/shader-effect", actor_shader_effect)
|
|
)
|