Introduce regional stage rendering

Add support for drawing a stage using multiple framebuffers each making
up one part of the stage. This works by the stage backend
(ClutterStageWindow) providing a list of views which will be for
splitting up the stage in different regions.

A view layout, for now, is a set of rectangles. The stage window (i.e.
stage "backend" will use this information when drawing a frame, using
one framebuffer for each view. The scene graph is adapted to explictly
take a view when painting the stage. It will use this view, its
assigned framebuffer and layout to offset and clip the drawing
accordingly.

This effectively removes any notion of "stage framebuffer", since each
stage now may consist of multiple framebuffers. Therefore, API
involving this has been deprecated and made no-ops; namely
clutter_stage_ensure_context(). Callers are now assumed to either
always use a framebuffer reference explicitly, or push/pop the
framebuffer of a given view where the code has not yet changed to use
the explicit-buffer-using cogl API.

Currently only the nested X11 backend supports this mode fully, and the
per view framebuffers are all offscreen. Upon frame completion, it'll
blit each view's framebuffer onto the onscreen framebuffer before
swapping.

Other backends (X11 CM and native/KMS) are adapted to manage a
full-stage view. The X11 CM backend will continue to use this method,
while the native/KMS backend will be adopted to use multiple view
drawing.

https://bugzilla.gnome.org/show_bug.cgi?id=768976
This commit is contained in:
Jonas Ådahl 2016-05-27 11:09:24 +08:00
parent 749237a28e
commit 566c28bdaf
37 changed files with 1430 additions and 440 deletions

View File

@ -114,6 +114,7 @@ source_h = \
clutter-snap-constraint.h \
clutter-stage.h \
clutter-stage-manager.h \
clutter-stage-view.h \
clutter-tap-action.h \
clutter-test-utils.h \
clutter-texture.h \
@ -197,6 +198,7 @@ source_c = \
clutter-snap-constraint.c \
clutter-stage.c \
clutter-stage-manager.c \
clutter-stage-view.c \
clutter-stage-window.c \
clutter-tap-action.c \
clutter-test-utils.c \

View File

@ -86,8 +86,6 @@ struct _ClutterBackendClass
GError **error);
gboolean (* create_context) (ClutterBackend *backend,
GError **error);
void (* ensure_context) (ClutterBackend *backend,
ClutterStage *stage);
ClutterDeviceManager *(* get_device_manager) (ClutterBackend *backend);
void (* copy_event_data) (ClutterBackend *backend,
@ -113,10 +111,6 @@ ClutterBackend * _clutter_create_backend (void);
ClutterStageWindow * _clutter_backend_create_stage (ClutterBackend *backend,
ClutterStage *wrapper,
GError **error);
void _clutter_backend_ensure_context (ClutterBackend *backend,
ClutterStage *stage);
void _clutter_backend_ensure_context_internal (ClutterBackend *backend,
ClutterStage *stage);
gboolean _clutter_backend_create_context (ClutterBackend *backend,
GError **error);
@ -152,6 +146,7 @@ gint32 _clutter_backend_get_units_serial (Clutter
PangoDirection _clutter_backend_get_keymap_direction (ClutterBackend *backend);
CLUTTER_AVAILABLE_IN_MUTTER
void _clutter_backend_reset_cogl_framebuffer (ClutterBackend *backend);
void clutter_set_allowed_drivers (const char *drivers);

View File

@ -413,27 +413,6 @@ clutter_backend_real_create_context (ClutterBackend *backend,
return TRUE;
}
static void
clutter_backend_real_ensure_context (ClutterBackend *backend,
ClutterStage *stage)
{
ClutterStageWindow *stage_impl;
CoglFramebuffer *framebuffer;
if (stage == NULL)
return;
stage_impl = _clutter_stage_get_window (stage);
if (stage_impl == NULL)
return;
framebuffer = _clutter_stage_window_get_active_framebuffer (stage_impl);
if (framebuffer == NULL)
return;
cogl_set_framebuffer (framebuffer);
}
static ClutterFeatureFlags
clutter_backend_real_get_features (ClutterBackend *backend)
{
@ -697,7 +676,6 @@ clutter_backend_class_init (ClutterBackendClass *klass)
klass->get_device_manager = clutter_backend_real_get_device_manager;
klass->translate_event = clutter_backend_real_translate_event;
klass->create_context = clutter_backend_real_create_context;
klass->ensure_context = clutter_backend_real_ensure_context;
klass->get_features = clutter_backend_real_get_features;
}
@ -789,87 +767,6 @@ _clutter_backend_create_context (ClutterBackend *backend,
return klass->create_context (backend, error);
}
void
_clutter_backend_ensure_context_internal (ClutterBackend *backend,
ClutterStage *stage)
{
ClutterBackendClass *klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (G_LIKELY (klass->ensure_context))
klass->ensure_context (backend, stage);
}
void
_clutter_backend_ensure_context (ClutterBackend *backend,
ClutterStage *stage)
{
static ClutterStage *current_context_stage = NULL;
g_assert (CLUTTER_IS_BACKEND (backend));
g_assert (CLUTTER_IS_STAGE (stage));
if (current_context_stage != stage ||
!clutter_actor_is_realized (CLUTTER_ACTOR (stage)))
{
ClutterStage *new_stage = NULL;
if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)))
{
new_stage = NULL;
CLUTTER_NOTE (BACKEND,
"Stage [%p] is not realized, unsetting the stage",
stage);
}
else
{
new_stage = stage;
CLUTTER_NOTE (BACKEND,
"Setting the new stage [%p]",
new_stage);
}
/* XXX: Until Cogl becomes fully responsible for backend windows
* Clutter need to manually keep it informed of the current window size
*
* NB: This must be done after we ensure_context above because Cogl
* always assumes there is a current GL context.
*/
if (new_stage != NULL)
{
float width, height;
_clutter_backend_ensure_context_internal (backend, new_stage);
clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height);
cogl_onscreen_clutter_backend_set_size (width, height);
/* Eventually we will have a separate CoglFramebuffer for
* each stage and each one will track private projection
* matrix and viewport state, but until then we need to make
* sure we update the projection and viewport whenever we
* switch between stages.
*
* This dirty mechanism will ensure they are asserted before
* the next paint...
*/
_clutter_stage_dirty_viewport (stage);
_clutter_stage_dirty_projection (stage);
}
/* FIXME: With a NULL stage and thus no active context it may make more
* sense to clean the context but then re call with the default stage
* so at least there is some kind of context in place (as to avoid
* potential issue of GL calls with no context).
*/
current_context_stage = new_stage;
}
else
CLUTTER_NOTE (BACKEND, "Stage is the same");
}
ClutterFeatureFlags
_clutter_backend_get_features (ClutterBackend *backend)
{

View File

@ -278,6 +278,8 @@
# define CLUTTER_DEPRECATED_IN_1_12_FOR(f) _CLUTTER_EXTERN
#endif
#define CLUTTER_DEPRECATED_IN_MUTTER CLUTTER_DEPRECATED
#if CLUTTER_VERSION_MAX_ALLOWED < CLUTTER_VERSION_1_12
# define CLUTTER_AVAILABLE_IN_1_12 CLUTTER_UNAVAILABLE(1, 12)
#else

View File

@ -27,6 +27,7 @@
#include "clutter-backend.h"
#include "clutter-macros.h"
#include "clutter-stage-view.h"
#include "cogl/clutter-stage-cogl.h"
#include "x11/clutter-stage-x11.h"

View File

@ -244,6 +244,10 @@ void _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2,
cairo_rectangle_int_t *dest);
gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2,
cairo_rectangle_int_t *dest);
struct _ClutterVertex4
{

View File

@ -36,7 +36,8 @@ typedef struct _ClutterStageQueueRedrawEntry ClutterStageQueueRedrawEntry;
/* stage */
ClutterStageWindow *_clutter_stage_get_default_window (void);
void _clutter_stage_do_paint (ClutterStage *stage,
void _clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip);
void _clutter_stage_set_window (ClutterStage *stage,
@ -56,7 +57,8 @@ void _clutter_stage_get_viewport (ClutterStage
float *width,
float *height);
void _clutter_stage_dirty_viewport (ClutterStage *stage);
void _clutter_stage_maybe_setup_viewport (ClutterStage *stage);
void _clutter_stage_maybe_setup_viewport (ClutterStage *stage,
ClutterStageView *view);
void _clutter_stage_maybe_relayout (ClutterActor *stage);
gboolean _clutter_stage_needs_update (ClutterStage *stage);
gboolean _clutter_stage_do_update (ClutterStage *stage);

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "clutter-build-config.h"
#include "clutter/clutter-stage-view.h"
#include <cairo-gobject.h>
enum
{
PROP_0,
PROP_LAYOUT,
PROP_FRAMEBUFFER,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
typedef struct _ClutterStageViewPrivate
{
cairo_rectangle_int_t layout;
CoglFramebuffer *framebuffer;
guint dirty_viewport : 1;
guint dirty_projection : 1;
} ClutterStageViewPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageView, clutter_stage_view, G_TYPE_OBJECT)
void
clutter_stage_view_get_layout (ClutterStageView *view,
cairo_rectangle_int_t *rect)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
*rect = priv->layout;
}
CoglFramebuffer *
clutter_stage_view_get_framebuffer (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->framebuffer;
}
gboolean
clutter_stage_view_is_dirty_viewport (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->dirty_viewport;
}
void
clutter_stage_view_set_dirty_viewport (ClutterStageView *view,
gboolean dirty)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
priv->dirty_viewport = dirty;
}
gboolean
clutter_stage_view_is_dirty_projection (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->dirty_projection;
}
void
clutter_stage_view_set_dirty_projection (ClutterStageView *view,
gboolean dirty)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
priv->dirty_projection = dirty;
}
static void
clutter_stage_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
switch (prop_id)
{
case PROP_LAYOUT:
g_value_set_boxed (value, &priv->layout);
break;
case PROP_FRAMEBUFFER:
g_value_set_boxed (value, priv->framebuffer);
break;
}
}
static void
clutter_stage_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
cairo_rectangle_int_t *layout;
switch (prop_id)
{
case PROP_LAYOUT:
layout = g_value_get_boxed (value);
priv->layout = *layout;
break;
case PROP_FRAMEBUFFER:
priv->framebuffer = g_value_get_boxed (value);
break;
}
}
static void
clutter_stage_view_dispose (GObject *object)
{
ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
g_clear_pointer (&priv->framebuffer, cogl_object_unref);
}
static void
clutter_stage_view_init (ClutterStageView *view)
{
}
static void
clutter_stage_view_class_init (ClutterStageViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = clutter_stage_view_get_property;
object_class->set_property = clutter_stage_view_set_property;
object_class->dispose = clutter_stage_view_dispose;
obj_props[PROP_LAYOUT] =
g_param_spec_boxed ("layout",
"View layout",
"The view layout on the screen",
CAIRO_GOBJECT_TYPE_RECTANGLE_INT,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_FRAMEBUFFER] =
g_param_spec_boxed ("framebuffer",
"View framebuffer",
"The framebuffer of the view",
COGL_TYPE_HANDLE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CLUTTER_STAGE_VIEW_H__
#define __CLUTTER_STAGE_VIEW_H__
#include <cairo.h>
#include <glib-object.h>
#include <cogl/cogl.h>
#include "clutter-macros.h"
#define CLUTTER_TYPE_STAGE_VIEW (clutter_stage_view_get_type ())
CLUTTER_AVAILABLE_IN_MUTTER
G_DECLARE_DERIVABLE_TYPE (ClutterStageView, clutter_stage_view,
CLUTTER, STAGE_VIEW,
GObject)
struct _ClutterStageViewClass
{
GObjectClass parent_class;
};
CLUTTER_AVAILABLE_IN_MUTTER
void clutter_stage_view_get_layout (ClutterStageView *view,
cairo_rectangle_int_t *rect);
CLUTTER_AVAILABLE_IN_MUTTER
CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view);
gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view);
void clutter_stage_view_set_dirty_viewport (ClutterStageView *view,
gboolean dirty);
gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view);
void clutter_stage_view_set_dirty_projection (ClutterStageView *view,
gboolean dirty);
#endif /* __CLUTTER_STAGE_VIEW_H__ */

View File

@ -274,6 +274,7 @@ _clutter_stage_window_redraw (ClutterStageWindow *window)
void
_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
ClutterStageView *view,
int *x, int *y)
{
ClutterStageWindowIface *iface;
@ -285,27 +286,7 @@ _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->get_dirty_pixel)
iface->get_dirty_pixel (window, x, y);
}
/* NB: The presumption shouldn't be that a stage can't be comprised of
* multiple internal framebuffers, so instead of simply naming this
* function _clutter_stage_window_get_framebuffer(), the "active"
* infix is intended to clarify that it gets the framebuffer that is
* currently in use/being painted.
*/
CoglFramebuffer *
_clutter_stage_window_get_active_framebuffer (ClutterStageWindow *window)
{
ClutterStageWindowIface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), NULL);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->get_active_framebuffer)
return iface->get_active_framebuffer (window);
else
return NULL;
iface->get_dirty_pixel (window, view, x, y);
}
gboolean
@ -349,12 +330,12 @@ _clutter_stage_window_get_scale_factor (ClutterStageWindow *window)
return 1;
}
CoglFramebuffer *
_clutter_stage_window_get_legacy_onscreen (ClutterStageWindow *window)
GList *
_clutter_stage_window_get_views (ClutterStageWindow *window)
{
ClutterStageWindowIface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
return iface->get_legacy_onscreen (window);
return iface->get_views (window);
}
CoglFrameClosure *
@ -376,6 +357,15 @@ _clutter_stage_window_remove_frame_callback (ClutterStageWindow *window,
iface->remove_frame_callback (window, closure);
}
void
_clutter_stage_window_finish_frame (ClutterStageWindow *window)
{
ClutterStageWindowIface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->finish_frame)
iface->finish_frame (window);
}
int64_t
_clutter_stage_window_get_frame_counter (ClutterStageWindow *window)
{

View File

@ -3,6 +3,7 @@
#include <cogl/cogl.h>
#include <clutter/clutter-types.h>
#include "clutter/clutter-stage-view.h"
G_BEGIN_DECLS
@ -77,22 +78,22 @@ struct _ClutterStageWindowIface
void (* redraw) (ClutterStageWindow *stage_window);
void (* get_dirty_pixel) (ClutterStageWindow *stage_window,
ClutterStageView *view,
int *x, int *y);
CoglFramebuffer *(* get_active_framebuffer) (ClutterStageWindow *stage_window);
gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
void (* set_scale_factor) (ClutterStageWindow *stage_window,
int factor);
int (* get_scale_factor) (ClutterStageWindow *stage_window);
CoglFramebuffer *(* get_legacy_onscreen) (ClutterStageWindow *stage_window);
GList *(* get_views) (ClutterStageWindow *stage_window);
CoglFrameClosure *(* set_frame_callback) (ClutterStageWindow *stage_window,
CoglFrameCallback callback,
gpointer user_data);
void (* remove_frame_callback) (ClutterStageWindow *stage_window,
CoglFrameClosure *closure);
int64_t (* get_frame_counter) (ClutterStageWindow *stage_window);
void (* finish_frame) (ClutterStageWindow *stage_window);
};
CLUTTER_AVAILABLE_IN_MUTTER
@ -139,17 +140,16 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin
void _clutter_stage_window_redraw (ClutterStageWindow *window);
void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
ClutterStageView *view,
int *x, int *y);
CoglFramebuffer *_clutter_stage_window_get_active_framebuffer (ClutterStageWindow *window);
gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window);
void _clutter_stage_window_set_scale_factor (ClutterStageWindow *window,
int factor);
int _clutter_stage_window_get_scale_factor (ClutterStageWindow *window);
CoglFramebuffer *_clutter_stage_window_get_legacy_onscreen (ClutterStageWindow *stage_window);
GList * _clutter_stage_window_get_views (ClutterStageWindow *window);
CoglFrameClosure *_clutter_stage_window_set_frame_callback (ClutterStageWindow *window,
CoglFrameCallback callback,
@ -158,6 +158,8 @@ CoglFrameClosure *_clutter_stage_window_set_frame_callback (ClutterStageWin
void _clutter_stage_window_remove_frame_callback (ClutterStageWindow *stage_winow,
CoglFrameClosure *closure);
void _clutter_stage_window_finish_frame (ClutterStageWindow *window);
int64_t _clutter_stage_window_get_frame_counter (ClutterStageWindow *window);
G_END_DECLS

View File

@ -158,8 +158,6 @@ struct _ClutterStagePrivate
guint throttle_motion_events : 1;
guint use_alpha : 1;
guint min_size_changed : 1;
guint dirty_viewport : 1;
guint dirty_projection : 1;
guint accept_focus : 1;
guint motion_events_enabled : 1;
guint has_custom_perspective : 1;
@ -602,7 +600,8 @@ _cogl_util_get_eye_planes_for_screen_poly (float *polygon,
}
static void
_clutter_stage_update_active_framebuffer (ClutterStage *stage)
_clutter_stage_update_active_framebuffer (ClutterStage *stage,
CoglFramebuffer *framebuffer)
{
ClutterStagePrivate *priv = stage->priv;
@ -611,34 +610,27 @@ _clutter_stage_update_active_framebuffer (ClutterStage *stage)
* offscreen framebuffer.
*/
priv->active_framebuffer =
_clutter_stage_window_get_active_framebuffer (priv->impl);
if (!priv->active_framebuffer)
priv->active_framebuffer = cogl_get_draw_framebuffer ();
priv->active_framebuffer = framebuffer;
}
/* This provides a common point of entry for painting the scenegraph
* for picking or painting...
*
* XXX: Instead of having a toplevel 2D clip region, it might be
/* XXX: Instead of having a toplevel 2D clip region, it might be
* better to have a clip volume within the view frustum. This could
* allow us to avoid projecting actors into window coordinates to
* be able to cull them.
*/
void
_clutter_stage_do_paint (ClutterStage *stage,
const cairo_rectangle_int_t *clip)
static void
clutter_stage_do_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStagePrivate *priv = stage->priv;
CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
cairo_rectangle_int_t view_layout;
float clip_poly[8];
float viewport[4];
cairo_rectangle_int_t geom;
int window_scale;
if (priv->impl == NULL)
return;
_clutter_stage_window_get_geometry (priv->impl, &geom);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
@ -647,29 +639,26 @@ _clutter_stage_do_paint (ClutterStage *stage,
viewport[2] = priv->viewport[2] * window_scale;
viewport[3] = priv->viewport[3] * window_scale;
if (clip)
if (!clip)
{
clip_poly[0] = MAX (clip->x * window_scale, 0);
clip_poly[1] = MAX (clip->y * window_scale, 0);
clip_poly[2] = MIN ((clip->x + clip->width) * window_scale, geom.width * window_scale);
clip_poly[3] = clip_poly[1];
clip_poly[4] = clip_poly[2];
clip_poly[5] = MIN ((clip->y + clip->height) * window_scale, geom.height * window_scale);
clip_poly[6] = clip_poly[0];
clip_poly[7] = clip_poly[5];
}
else
{
clip_poly[0] = 0;
clip_poly[1] = 0;
clip_poly[2] = geom.width * window_scale;
clip_poly[3] = 0;
clip_poly[4] = geom.width * window_scale;
clip_poly[5] = geom.height * window_scale;
clip_poly[6] = 0;
clip_poly[7] = geom.height * window_scale;
clutter_stage_view_get_layout (view, &view_layout);
clip = &view_layout;
}
clip_poly[0] = MAX (clip->x * window_scale, 0);
clip_poly[1] = MAX (clip->y * window_scale, 0);
clip_poly[2] = MIN ((clip->x + clip->width) * window_scale,
geom.width * window_scale);
clip_poly[3] = clip_poly[1];
clip_poly[4] = clip_poly[2];
clip_poly[5] = MIN ((clip->y + clip->height) * window_scale,
geom.height * window_scale);
clip_poly[6] = clip_poly[0];
clip_poly[7] = clip_poly[5];
CLUTTER_NOTE (CLIPPING, "Setting stage clip too: "
"x=%f, y=%f, width=%f, height=%f",
clip_poly[0], clip_poly[1],
@ -684,9 +673,24 @@ _clutter_stage_do_paint (ClutterStage *stage,
priv->current_clip_planes);
_clutter_stage_paint_volume_stack_free_all (stage);
_clutter_stage_update_active_framebuffer (stage);
_clutter_stage_update_active_framebuffer (stage, framebuffer);
clutter_actor_paint (CLUTTER_ACTOR (stage));
}
/* This provides a common point of entry for painting the scenegraph
* for picking or painting...
*/
void
_clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStagePrivate *priv = stage->priv;
if (!priv->impl)
return;
clutter_stage_do_paint_view (stage, view, clip);
g_signal_emit (stage, stage_signals[AFTER_PAINT], 0);
}
@ -736,31 +740,10 @@ clutter_stage_realize (ClutterActor *self)
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
gboolean is_realized;
/* Make sure the viewport and projection matrix are valid for the
* first paint (which will likely occur before the ConfigureNotify
* is received)
*/
priv->dirty_viewport = TRUE;
priv->dirty_projection = TRUE;
g_assert (priv->impl != NULL);
is_realized = _clutter_stage_window_realize (priv->impl);
/* ensure that the stage is using the context if the
* realization sequence was successful
*/
if (is_realized)
{
ClutterBackend *backend = clutter_get_default_backend ();
/* We want to select the context without calling
clutter_backend_ensure_context so that it doesn't call any
Cogl functions. Otherwise it would create the Cogl context
before we get a chance to check whether the GL version is
valid */
_clutter_backend_ensure_context_internal (backend, CLUTTER_STAGE (self));
}
else
if (!is_realized)
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
@ -774,8 +757,6 @@ clutter_stage_unrealize (ClutterActor *self)
_clutter_stage_window_unrealize (priv->impl);
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
clutter_stage_ensure_current (CLUTTER_STAGE (self));
}
static void
@ -1106,7 +1087,6 @@ _clutter_stage_maybe_relayout (ClutterActor *actor)
static void
clutter_stage_do_redraw (ClutterStage *stage)
{
ClutterBackend *backend = clutter_get_default_backend ();
ClutterActor *actor = CLUTTER_ACTOR (stage);
ClutterStagePrivate *priv = stage->priv;
@ -1120,16 +1100,12 @@ clutter_stage_do_redraw (ClutterStage *stage)
_clutter_actor_get_debug_name (actor),
stage);
_clutter_backend_ensure_context (backend, stage);
if (_clutter_context_get_show_fps ())
{
if (priv->fps_timer == NULL)
priv->fps_timer = g_timer_new ();
}
_clutter_stage_maybe_setup_viewport (stage);
_clutter_stage_window_redraw (priv->impl);
if (_clutter_context_get_show_fps ())
@ -1371,67 +1347,82 @@ read_pixels_to_file (char *filename_stem,
read_count++;
}
ClutterActor *
_clutter_stage_do_pick (ClutterStage *stage,
gint x,
gint y,
ClutterPickMode mode)
static ClutterActor *
_clutter_stage_do_pick_on_view (ClutterStage *stage,
gint x,
gint y,
ClutterPickMode mode,
ClutterStageView *view)
{
ClutterActor *actor = CLUTTER_ACTOR (stage);
ClutterStagePrivate *priv = stage->priv;
CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
cairo_rectangle_int_t view_layout;
ClutterMainContext *context;
guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
CoglColor stage_pick_id;
gboolean dither_enabled_save;
ClutterActor *retval;
CoglFramebuffer *fb;
gint dirty_x;
gint dirty_y;
gint read_x;
gint read_y;
float stage_width, stage_height;
int window_scale;
float fb_width, fb_height;
int viewport_offset_x;
int viewport_offset_y;
priv = stage->priv;
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
return actor;
if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
return actor;
if (G_UNLIKELY (priv->impl == NULL))
return actor;
clutter_actor_get_size (CLUTTER_ACTOR (stage), &stage_width, &stage_height);
if (x < 0 || x >= stage_width || y < 0 || y >= stage_height)
return actor;
context = _clutter_context_get_default ();
clutter_stage_ensure_current (stage);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
clutter_stage_view_get_layout (view, &view_layout);
fb = cogl_get_draw_framebuffer ();
_clutter_backend_ensure_context (context->backend, stage);
fb_width = view_layout.width;
fb_height = view_layout.height;
cogl_push_framebuffer (fb);
/* needed for when a context switch happens */
_clutter_stage_maybe_setup_viewport (stage);
_clutter_stage_maybe_setup_viewport (stage, view);
_clutter_stage_window_get_dirty_pixel (priv->impl, &dirty_x, &dirty_y);
/* FIXME: For some reason leaving the cogl clip stack empty causes the
* picking to not work at all, so setting it the whole framebuffer content
* for now. */
cogl_framebuffer_push_scissor_clip (fb, 0, 0,
view_layout.width,
view_layout.height);
_clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y);
dirty_x -= view_layout.x;
dirty_y -= view_layout.y;
if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_framebuffer_push_scissor_clip (fb, dirty_x * window_scale, dirty_y * window_scale, 1, 1);
{
CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1",
dirty_x * window_scale,
dirty_y * window_scale);
cogl_framebuffer_push_scissor_clip (fb, dirty_x * window_scale, dirty_y * window_scale, 1, 1);
}
cogl_set_viewport (priv->viewport[0] * window_scale - x * window_scale + dirty_x * window_scale,
priv->viewport[1] * window_scale - y * window_scale + dirty_y * window_scale,
viewport_offset_x = x * window_scale - dirty_x * window_scale;
viewport_offset_y = y * window_scale - dirty_y * window_scale;
CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f",
priv->viewport[0] * window_scale - viewport_offset_x,
priv->viewport[1] * window_scale - viewport_offset_y,
priv->viewport[2] * window_scale,
priv->viewport[3] * window_scale);
cogl_set_viewport (priv->viewport[0] * window_scale - viewport_offset_x,
priv->viewport[1] * window_scale - viewport_offset_y,
priv->viewport[2] * window_scale,
priv->viewport[3] * window_scale);
read_x = dirty_x * window_scale;
read_y = dirty_y * window_scale;
CLUTTER_NOTE (PICK, "Performing pick at %i,%i", x, y);
CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d",
x, y,
view_layout.width, view_layout.height,
view_layout.x, view_layout.y);
cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
cogl_clear (&stage_pick_id, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH);
@ -1444,7 +1435,7 @@ _clutter_stage_do_pick (ClutterStage *stage,
* are drawn offscreen (as we never swap buffers)
*/
context->pick_mode = mode;
_clutter_stage_do_paint (stage, NULL);
_clutter_stage_paint_view (stage, view, NULL);
context->pick_mode = CLUTTER_PICK_NONE;
/* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
@ -1462,11 +1453,11 @@ _clutter_stage_do_pick (ClutterStage *stage,
if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
{
char *file_name =
g_strconcat ("pick-buffer-",
_clutter_actor_get_debug_name (actor),
NULL);
g_strdup_printf ("pick-buffer-%s-view-x-%d",
_clutter_actor_get_debug_name (actor),
view_layout.x);
read_pixels_to_file (file_name, 0, 0, stage_width, stage_height);
read_pixels_to_file (file_name, 0, 0, fb_width, fb_height);
g_free (file_name);
}
@ -1477,6 +1468,8 @@ _clutter_stage_do_pick (ClutterStage *stage,
if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_framebuffer_pop_clip (fb);
cogl_framebuffer_pop_clip (fb);
_clutter_stage_dirty_viewport (stage);
if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
@ -1486,11 +1479,74 @@ _clutter_stage_do_pick (ClutterStage *stage,
guint32 id_ = _clutter_pixel_to_id (pixel);
retval = _clutter_stage_get_actor_by_pick_id (stage, id_);
CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x",
G_OBJECT_TYPE_NAME (retval),
id_,
pixel[0], pixel[1], pixel[2], pixel[3]);
}
cogl_pop_framebuffer ();
return retval;
}
static ClutterStageView *
get_view_at (ClutterStage *stage,
int x,
int y)
{
ClutterStagePrivate *priv = stage->priv;
GList *l;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
clutter_stage_view_get_layout (view, &view_layout);
if (x >= view_layout.x &&
x < view_layout.x + view_layout.width &&
y >= view_layout.y &&
y < view_layout.y + view_layout.height)
return view;
}
return NULL;
}
ClutterActor *
_clutter_stage_do_pick (ClutterStage *stage,
gint x,
gint y,
ClutterPickMode mode)
{
ClutterActor *actor = CLUTTER_ACTOR (stage);
ClutterStagePrivate *priv = stage->priv;
float stage_width, stage_height;
ClutterStageView *view = NULL;
priv = stage->priv;
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
return actor;
if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING))
return actor;
if (G_UNLIKELY (priv->impl == NULL))
return actor;
clutter_actor_get_size (CLUTTER_ACTOR (stage), &stage_width, &stage_height);
if (x < 0 || x >= stage_width || y < 0 || y >= stage_height)
return actor;
view = get_view_at (stage, x, y);
if (view)
return _clutter_stage_do_pick_on_view (stage, x, y, mode, view);
return actor;
}
static gboolean
clutter_stage_real_delete_event (ClutterStage *stage,
ClutterEvent *event)
@ -2339,7 +2395,7 @@ clutter_stage_set_perspective_internal (ClutterStage *stage,
cogl_matrix_get_inverse (&priv->projection,
&priv->inverse_projection);
priv->dirty_projection = TRUE;
_clutter_stage_dirty_projection (stage);
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}
@ -2419,7 +2475,19 @@ _clutter_stage_get_projection_matrix (ClutterStage *stage,
void
_clutter_stage_dirty_projection (ClutterStage *stage)
{
stage->priv->dirty_projection = TRUE;
ClutterStagePrivate *priv;
GList *l;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
{
ClutterStageView *view = l->data;
clutter_stage_view_set_dirty_projection (view, TRUE);
}
}
/*
@ -2484,7 +2552,7 @@ _clutter_stage_set_viewport (ClutterStage *stage,
priv->viewport[2] = width;
priv->viewport[3] = height;
priv->dirty_viewport = TRUE;
_clutter_stage_dirty_viewport (stage);
queue_full_redraw (stage);
}
@ -2496,7 +2564,19 @@ _clutter_stage_set_viewport (ClutterStage *stage,
void
_clutter_stage_dirty_viewport (ClutterStage *stage)
{
stage->priv->dirty_viewport = TRUE;
ClutterStagePrivate *priv;
GList *l;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
{
ClutterStageView *view = l->data;
clutter_stage_view_set_dirty_viewport (view, TRUE);
}
}
/*
@ -2758,14 +2838,18 @@ clutter_stage_read_pixels (ClutterStage *stage,
gint width,
gint height)
{
ClutterStagePrivate *priv;
ClutterActorBox box;
guchar *pixels;
GList *l;
ClutterStageView *view;
cairo_region_t *clip;
cairo_rectangle_int_t clip_rect;
CoglFramebuffer *framebuffer;
uint8_t *pixels;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
/* Force a redraw of the stage before reading back pixels */
clutter_stage_ensure_current (stage);
clutter_actor_paint (CLUTTER_ACTOR (stage));
priv = stage->priv;
clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &box);
@ -2775,12 +2859,42 @@ clutter_stage_read_pixels (ClutterStage *stage,
if (height < 0)
height = ceilf (box.y2 - box.y1);
pixels = g_malloc (height * width * 4);
l = _clutter_stage_window_get_views (priv->impl);
cogl_read_pixels (x, y, width, height,
COGL_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_RGBA_8888,
pixels);
if (!l)
return NULL;
/* XXX: We only read the first view. Needs different API for multi view screen
* capture. */
view = l->data;
clutter_stage_view_get_layout (view, &clip_rect);
clip = cairo_region_create_rectangle (&clip_rect);
cairo_region_intersect_rectangle (clip,
&(cairo_rectangle_int_t) {
.x = x,
.y = y,
.width = width,
.height = height,
});
cairo_region_get_extents (clip, &clip_rect);
cairo_region_destroy (clip);
if (clip_rect.width == 0 || clip_rect.height == 0)
return NULL;
framebuffer = clutter_stage_view_get_framebuffer (view);
cogl_push_framebuffer (framebuffer);
clutter_stage_do_paint_view (stage, view, &clip_rect);
pixels = g_malloc0 (clip_rect.width * clip_rect.height * 4);
cogl_framebuffer_read_pixels (framebuffer,
clip_rect.x, clip_rect.y,
clip_rect.width, clip_rect.height,
COGL_PIXEL_FORMAT_RGBA_8888,
pixels);
cogl_pop_framebuffer ();
return pixels;
}
@ -3241,16 +3355,12 @@ clutter_stage_new (void)
* be used by applications.
*
* Since: 0.8
* Deprecated: mutter: This function does not do anything.
*/
void
clutter_stage_ensure_current (ClutterStage *stage)
{
ClutterBackend *backend;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
backend = clutter_get_default_backend ();
_clutter_backend_ensure_context (backend, stage);
}
/**
@ -3425,14 +3535,18 @@ calculate_z_translation (float z_near)
}
void
_clutter_stage_maybe_setup_viewport (ClutterStage *stage)
_clutter_stage_maybe_setup_viewport (ClutterStage *stage,
ClutterStageView *view)
{
ClutterStagePrivate *priv = stage->priv;
if (priv->dirty_viewport)
if (clutter_stage_view_is_dirty_viewport (view))
{
cairo_rectangle_int_t view_layout;
ClutterPerspective perspective;
int window_scale;
int viewport_offset_x;
int viewport_offset_y;
float z_2d;
CLUTTER_NOTE (PAINT,
@ -3441,9 +3555,12 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
priv->viewport[3]);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
clutter_stage_view_get_layout (view, &view_layout);
cogl_set_viewport (priv->viewport[0] * window_scale,
priv->viewport[1] * window_scale,
viewport_offset_x = view_layout.x * window_scale;
viewport_offset_y = view_layout.y * window_scale;
cogl_set_viewport (priv->viewport[0] * window_scale - viewport_offset_x,
priv->viewport[1] * window_scale - viewport_offset_y,
priv->viewport[2] * window_scale,
priv->viewport[3] * window_scale);
@ -3481,14 +3598,14 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
clutter_stage_apply_scale (stage);
priv->dirty_viewport = FALSE;
clutter_stage_view_set_dirty_viewport (view, FALSE);
}
if (priv->dirty_projection)
if (clutter_stage_view_is_dirty_projection (view))
{
cogl_set_projection_matrix (&priv->projection);
priv->dirty_projection = FALSE;
clutter_stage_view_set_dirty_projection (view, FALSE);
}
}

View File

@ -229,9 +229,6 @@ guchar * clutter_stage_read_pixels (ClutterStage
CLUTTER_AVAILABLE_IN_ALL
void clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
cairo_rectangle_int_t *clip);
CLUTTER_AVAILABLE_IN_ALL
void clutter_stage_ensure_current (ClutterStage *stage);
CLUTTER_AVAILABLE_IN_ALL
void clutter_stage_ensure_viewport (ClutterStage *stage);
CLUTTER_AVAILABLE_IN_ALL

View File

@ -161,6 +161,39 @@ _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1,
dest->y = dest_y;
}
gboolean
_clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2,
cairo_rectangle_int_t *dest)
{
int x1, y1, x2, y2;
x1 = MAX (src1->x, src2->x);
y1 = MAX (src1->y, src2->y);
x2 = MIN (src1->x + (int) src1->width, src2->x + (int) src2->width);
y2 = MIN (src1->y + (int) src1->height, src2->y + (int) src2->height);
if (x1 >= x2 || y1 >= y2)
{
dest->x = 0;
dest->y = 0;
dest->width = 0;
dest->height = 0;
return FALSE;
}
else
{
dest->x = x1;
dest->y = y1;
dest->width = x2 - x1;
dest->height = y2 - y1;
return TRUE;
}
}
float
_clutter_util_matrix_determinant (const ClutterMatrix *matrix)
{

View File

@ -36,6 +36,8 @@
#include "clutter-stage-cogl.h"
#include <stdlib.h>
#include "clutter-actor-private.h"
#include "clutter-backend-private.h"
#include "clutter-debug.h"
@ -46,6 +48,18 @@
#include "clutter-private.h"
#include "clutter-stage-private.h"
typedef struct _ClutterStageViewCoglPrivate
{
/* Stores a list of previous damaged areas in the stage coordinate space */
#define DAMAGE_HISTORY_MAX 16
#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1))
cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX];
unsigned int damage_index;
} ClutterStageViewCoglPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl,
CLUTTER_TYPE_STAGE_VIEW)
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl,
@ -74,8 +88,6 @@ clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
stage_cogl->frame_closure);
stage_cogl->frame_closure = NULL;
}
stage_cogl->pending_swaps = 0;
}
static void
@ -340,51 +352,156 @@ clutter_stage_cogl_get_redraw_clip_bounds (ClutterStageWindow *stage_window,
}
static inline gboolean
valid_buffer_age (ClutterStageCogl *stage_cogl, int age)
valid_buffer_age (ClutterStageViewCogl *view_cogl,
int age)
{
ClutterStageViewCoglPrivate *view_priv =
clutter_stage_view_cogl_get_instance_private (view_cogl);
if (age <= 0)
return FALSE;
return age < MIN (stage_cogl->damage_index, DAMAGE_HISTORY_MAX);
return age < MIN (view_priv->damage_index, DAMAGE_HISTORY_MAX);
}
static gboolean
swap_framebuffer (ClutterStageWindow *stage_window,
ClutterStageView *view,
cairo_rectangle_int_t *swap_region,
gboolean swap_with_damage)
{
CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
int damage[4], ndamage;
damage[0] = swap_region->x;
damage[1] = swap_region->y;
damage[2] = swap_region->width;
damage[3] = swap_region->height;
if (swap_region->width != 0)
ndamage = 1;
else
ndamage = 0;
if (cogl_is_onscreen (framebuffer))
{
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
/* push on the screen */
if (ndamage == 1 && !swap_with_damage)
{
CLUTTER_NOTE (BACKEND,
"cogl_onscreen_swap_region (onscreen: %p, "
"x: %d, y: %d, "
"width: %d, height: %d)",
onscreen,
damage[0], damage[1], damage[2], damage[3]);
cogl_onscreen_swap_region (onscreen,
damage, ndamage);
return FALSE;
}
else
{
CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)",
onscreen);
cogl_onscreen_swap_buffers_with_damage (onscreen,
damage, ndamage);
return TRUE;
}
}
else
{
CLUTTER_NOTE (BACKEND, "cogl_framebuffer_finish (framebuffer: %p)",
framebuffer);
cogl_framebuffer_finish (framebuffer);
return FALSE;
}
}
/* XXX: This is basically identical to clutter_stage_glx_redraw */
static void
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
paint_stage (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStage *stage = stage_cogl->wrapper;
_clutter_stage_maybe_setup_viewport (stage, view);
_clutter_stage_paint_view (stage, view, clip);
}
static void
fill_current_damage_history_and_step (ClutterStageView *view)
{
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
ClutterStageViewCoglPrivate *view_priv =
clutter_stage_view_cogl_get_instance_private (view_cogl);
cairo_rectangle_int_t view_rect;
cairo_rectangle_int_t *current_damage;
current_damage =
&view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)];
clutter_stage_view_get_layout (view, &view_rect);
*current_damage = view_rect;
view_priv->damage_index++;
}
static gboolean
clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
ClutterStageView *view)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
CoglOnscreen *onscreen;
cairo_rectangle_int_t geom;
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
ClutterStageViewCoglPrivate *view_priv =
clutter_stage_view_cogl_get_instance_private (view_cogl);
CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
cairo_rectangle_int_t view_rect;
gboolean have_clip;
gboolean may_use_clipped_redraw;
gboolean use_clipped_redraw;
gboolean can_blit_sub_buffer;
gboolean has_buffer_age;
gboolean do_swap_buffer;
gboolean swap_with_damage;
ClutterActor *wrapper;
cairo_rectangle_int_t *clip_region;
int damage[4], ndamage;
gboolean force_swap;
cairo_rectangle_int_t redraw_clip;
cairo_rectangle_int_t swap_region;
cairo_rectangle_int_t clip_region;
gboolean clip_region_empty;
int window_scale;
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
onscreen = _clutter_stage_window_get_legacy_onscreen (stage_window);
if (!onscreen)
return;
clutter_stage_view_get_layout (view, &view_rect);
can_blit_sub_buffer =
cogl_is_onscreen (fb) &&
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
_clutter_stage_window_get_geometry (stage_window, &geom);
has_buffer_age =
cogl_is_onscreen (fb) &&
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
/* NB: a zero width redraw clip == full stage redraw */
have_clip = (stage_cogl->bounding_redraw_clip.width != 0 &&
!(stage_cogl->bounding_redraw_clip.x == 0 &&
stage_cogl->bounding_redraw_clip.y == 0 &&
stage_cogl->bounding_redraw_clip.width == geom.width &&
stage_cogl->bounding_redraw_clip.height == geom.height));
if (stage_cogl->bounding_redraw_clip.width == 0)
have_clip = FALSE;
else
{
redraw_clip = stage_cogl->bounding_redraw_clip;
_clutter_util_rectangle_intersection (&redraw_clip,
&view_rect,
&redraw_clip);
have_clip = !(redraw_clip.x == view_rect.x &&
redraw_clip.y == view_rect.y &&
redraw_clip.width == view_rect.width &&
redraw_clip.height == view_rect.height);
}
may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
@ -392,13 +509,15 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
have_clip &&
/* some drivers struggle to get going and produce some junk
* frames when starting up... */
stage_cogl->frame_count > 3)
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3)
{
may_use_clipped_redraw = TRUE;
clip_region = &stage_cogl->bounding_redraw_clip;
clip_region = redraw_clip;
}
else
clip_region = NULL;
{
clip_region = (cairo_rectangle_int_t){ 0 };
}
if (may_use_clipped_redraw &&
G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
@ -406,70 +525,87 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
else
use_clipped_redraw = FALSE;
force_swap = FALSE;
clip_region_empty = may_use_clipped_redraw && clip_region.width == 0;
window_scale = _clutter_stage_window_get_scale_factor (stage_window);
swap_with_damage = FALSE;
if (has_buffer_age)
{
cairo_rectangle_int_t *current_damage =
&stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index++)];
if (use_clipped_redraw && !clip_region_empty)
{
int age, i;
cairo_rectangle_int_t *current_damage =
&view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)];
if (use_clipped_redraw)
{
int age = cogl_onscreen_get_buffer_age (onscreen), i;
age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
*current_damage = *clip_region;
if (valid_buffer_age (view_cogl, age))
{
*current_damage = clip_region;
if (valid_buffer_age (stage_cogl, age))
{
for (i = 1; i <= age; i++)
_clutter_util_rectangle_union (clip_region,
&stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index - i - 1)],
clip_region);
for (i = 1; i <= age; i++)
{
cairo_rectangle_int_t *damage =
&view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)];
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
age,
clip_region->x,
clip_region->y,
clip_region->width,
clip_region->height);
force_swap = TRUE;
}
else
{
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
use_clipped_redraw = FALSE;
}
}
else
{
current_damage->x = 0;
current_damage->y = 0;
current_damage->width = geom.width;
current_damage->height = geom.height;
}
_clutter_util_rectangle_union (&clip_region, damage, &clip_region);
}
/* Update the bounding redraw clip state with the extra damage. */
_clutter_util_rectangle_union (&stage_cogl->bounding_redraw_clip,
&clip_region,
&stage_cogl->bounding_redraw_clip);
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
age,
clip_region.x,
clip_region.y,
clip_region.width,
clip_region.height);
swap_with_damage = TRUE;
}
else
{
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
use_clipped_redraw = FALSE;
*current_damage = view_rect;
}
}
else if (!use_clipped_redraw)
{
fill_current_damage_history_and_step (view);
}
}
if (use_clipped_redraw)
cogl_push_framebuffer (fb);
if (use_clipped_redraw && clip_region_empty)
{
CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
CLUTTER_NOTE (CLIPPING, "Empty stage output paint\n");
}
else if (use_clipped_redraw)
{
int scissor_x;
int scissor_y;
CLUTTER_NOTE (CLIPPING,
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
clip_region->x,
clip_region->y,
clip_region->width,
clip_region->height);
clip_region.x,
clip_region.y,
clip_region.width,
clip_region.height);
stage_cogl->using_clipped_redraw = TRUE;
scissor_x = (clip_region.x - view_rect.x) * window_scale;
scissor_y = (clip_region.y - view_rect.y) * window_scale;
cogl_framebuffer_push_scissor_clip (fb,
clip_region->x * window_scale,
clip_region->y * window_scale,
clip_region->width * window_scale,
clip_region->height * window_scale);
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region);
scissor_x,
scissor_y,
clip_region.width * window_scale,
clip_region.height * window_scale);
paint_stage (stage_cogl, view, &clip_region);
cogl_framebuffer_pop_clip (fb);
stage_cogl->using_clipped_redraw = FALSE;
@ -481,26 +617,37 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
/* If we are trying to debug redraw issues then we want to pass
* the bounding_redraw_clip so it can be visualized */
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) &&
may_use_clipped_redraw)
may_use_clipped_redraw &&
!clip_region_empty)
{
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), clip_region);
int scissor_x;
int scissor_y;
scissor_x = (clip_region.x - view_rect.x) * window_scale;;
scissor_y = (clip_region.y - view_rect.y) * window_scale;
cogl_framebuffer_push_scissor_clip (fb,
scissor_x,
scissor_y,
clip_region.width * window_scale,
clip_region.height * window_scale);
paint_stage (stage_cogl, view, &clip_region);
cogl_framebuffer_pop_clip (fb);
}
else
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
paint_stage (stage_cogl, view, &view_rect);
}
cogl_pop_framebuffer ();
if (may_use_clipped_redraw &&
G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
{
CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
CoglContext *ctx = cogl_framebuffer_get_context (fb);
static CoglPipeline *outline = NULL;
cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip;
ClutterActor *actor = CLUTTER_ACTOR (wrapper);
float x_1 = clip->x * window_scale;
float x_2 = clip->x + clip->width * window_scale;
float y_1 = clip->y * window_scale;
float y_2 = clip->y + clip->height * window_scale;
float x_1 = redraw_clip.x;
float x_2 = redraw_clip.x + redraw_clip.width;
float y_1 = redraw_clip.y;
float y_2 = redraw_clip.y + redraw_clip.height;
CoglVertexP2 quad[4] = {
{ x_1, y_1 },
{ x_2, y_1 },
@ -525,9 +672,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
cogl_matrix_init_identity (&modelview);
_clutter_actor_apply_modelview_transform (actor, &modelview);
cogl_framebuffer_set_modelview_matrix (fb, &modelview);
cogl_framebuffer_draw_primitive (COGL_FRAMEBUFFER (onscreen),
outline,
prim);
cogl_framebuffer_draw_primitive (fb, outline, prim);
cogl_framebuffer_pop_matrix (fb);
cogl_object_unref (prim);
}
@ -540,45 +685,77 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
* the resize anyway so it should only exhibit temporary
* artefacts.
*/
if (use_clipped_redraw || force_swap)
if (use_clipped_redraw)
{
damage[0] = clip_region->x * window_scale;
damage[1] = clip_region->y * window_scale;
damage[2] = clip_region->width * window_scale;
damage[3] = clip_region->height * window_scale;
ndamage = 1;
if (use_clipped_redraw && clip_region_empty)
{
do_swap_buffer = FALSE;
}
else if (use_clipped_redraw)
{
swap_region = (cairo_rectangle_int_t) {
.x = (clip_region.x - view_rect.x) * window_scale,
.y = (clip_region.y - view_rect.y) * window_scale,
.width = clip_region.width * window_scale,
.height = clip_region.height * window_scale,
};
g_assert (swap_region.width > 0);
do_swap_buffer = TRUE;
}
else
{
swap_region = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = view_rect.width * window_scale,
.height = view_rect.height * window_scale,
};
do_swap_buffer = TRUE;
}
}
else
{
ndamage = 0;
swap_region = (cairo_rectangle_int_t) { 0 };
do_swap_buffer = TRUE;
}
/* push on the screen */
if (use_clipped_redraw && !force_swap)
if (do_swap_buffer)
{
CLUTTER_NOTE (BACKEND,
"cogl_onscreen_swap_region (onscreen: %p, "
"x: %d, y: %d, "
"width: %d, height: %d)",
onscreen,
damage[0], damage[1], damage[2], damage[3]);
cogl_onscreen_swap_region (onscreen,
damage, ndamage);
return swap_framebuffer (stage_window,
view,
&swap_region,
swap_with_damage);
}
else
{
CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)",
onscreen);
return FALSE;
}
}
static void
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
gboolean swap_event = FALSE;
GList *l;
for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
{
ClutterStageView *view = l->data;
swap_event =
clutter_stage_cogl_redraw_view (stage_window, view) || swap_event;
}
_clutter_stage_window_finish_frame (stage_window);
if (swap_event)
{
/* If we have swap buffer events then cogl_onscreen_swap_buffers
* will return immediately and we need to track that there is a
* swap in progress... */
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
stage_cogl->pending_swaps++;
cogl_onscreen_swap_buffers_with_damage (onscreen,
damage, ndamage);
}
/* reset the redraw clipping for the next paint... */
@ -587,22 +764,15 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
stage_cogl->frame_count++;
}
static CoglFramebuffer *
clutter_stage_cogl_get_active_framebuffer (ClutterStageWindow *stage_window)
{
CoglOnscreen *onscreen =
_clutter_stage_window_get_legacy_onscreen (stage_window);
return COGL_FRAMEBUFFER (onscreen);
}
static void
clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
ClutterStageView *view,
int *x,
int *y)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
gboolean has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
gboolean has_buffer_age =
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
cairo_rectangle_int_t *rect;
if (!has_buffer_age)
{
@ -611,9 +781,11 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
}
else
{
cairo_rectangle_int_t *rect;
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
ClutterStageViewCoglPrivate *view_priv =
clutter_stage_view_cogl_get_instance_private (view_cogl);
rect = &stage_cogl->damage_history[DAMAGE_HISTORY (stage_cogl->damage_index-1)];
rect = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
*x = rect->x;
*y = rect->y;
}
@ -636,7 +808,6 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
iface->redraw = clutter_stage_cogl_redraw;
iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer;
iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
}
@ -683,3 +854,13 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage)
stage->update_time = -1;
}
static void
clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl)
{
}
static void
clutter_stage_view_cogl_class_init (ClutterStageViewCoglClass *klass)
{
}

View File

@ -11,6 +11,8 @@
#include <X11/Xutil.h>
#endif
#include "clutter/clutter-stage-window.h"
G_BEGIN_DECLS
#define CLUTTER_TYPE_STAGE_COGL (_clutter_stage_cogl_get_type ())
@ -25,6 +27,17 @@ typedef struct _ClutterStageCoglClass ClutterStageCoglClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterStageCogl, g_object_unref)
#define CLUTTER_TYPE_STAGE_VIEW_COGL (clutter_stage_view_cogl_get_type ())
CLUTTER_AVAILABLE_IN_MUTTER
G_DECLARE_DERIVABLE_TYPE (ClutterStageViewCogl, clutter_stage_view_cogl,
CLUTTER, STAGE_VIEW_COGL,
ClutterStageView)
struct _ClutterStageViewCoglClass
{
ClutterStageViewClass parent_class;
};
struct _ClutterStageCogl
{
GObject parent_instance;
@ -50,12 +63,6 @@ struct _ClutterStageCogl
cairo_rectangle_int_t bounding_redraw_clip;
/* Stores a list of previous damaged areas */
#define DAMAGE_HISTORY_MAX 16
#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1))
cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX];
unsigned int damage_index;
guint initialized_redraw_clip : 1;
/* TRUE if the current paint cycle has a clipped redraw. In that

View File

@ -97,6 +97,9 @@ CLUTTER_DEPRECATED_IN_1_10_FOR(clutter_actor_get_background_color)
void clutter_stage_get_color (ClutterStage *stage,
ClutterColor *color);
CLUTTER_DEPRECATED_IN_MUTTER
void clutter_stage_ensure_current (ClutterStage *stage);
G_END_DECLS
#endif /* __CLUTTER_STAGE_DEPRECATED_H__ */

View File

@ -445,6 +445,8 @@ clutter_stage_x11_unrealize (ClutterStageWindow *stage_window)
clutter_stage_window_parent_iface->unrealize (stage_window);
g_list_free (stage_x11->legacy_views);
g_clear_object (&stage_x11->legacy_view);
g_clear_pointer (&stage_x11->onscreen, cogl_object_unref);
}
@ -626,6 +628,11 @@ clutter_stage_x11_realize (ClutterStageWindow *stage_window)
stage_x11->onscreen = cogl_onscreen_new (backend->cogl_context, width, height);
if (stage_x11->legacy_view)
g_object_set (G_OBJECT (stage_x11->legacy_view),
"framebuffer", stage_x11->onscreen,
NULL);
/* We just created a window of the size of the actor. No need to fix
the size of the stage, just update it. */
stage_x11->xwin_width = width;
@ -892,12 +899,34 @@ clutter_stage_x11_get_scale_factor (ClutterStageWindow *stage_window)
return stage_x11->scale_factor;
}
static CoglFramebuffer *
clutter_stage_x11_get_legacy_onscreen (ClutterStageWindow *stage_window)
static void
ensure_legacy_view (ClutterStageWindow *stage_window)
{
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
cairo_rectangle_int_t view_layout;
CoglFramebuffer *framebuffer;
if (stage_x11->legacy_view)
return;
_clutter_stage_window_get_geometry (stage_window, &view_layout);
framebuffer = COGL_FRAMEBUFFER (stage_x11->onscreen);
stage_x11->legacy_view = g_object_new (CLUTTER_TYPE_STAGE_VIEW_COGL,
"layout", &view_layout,
"framebuffer", framebuffer,
NULL);
stage_x11->legacy_views = g_list_append (stage_x11->legacy_views,
stage_x11->legacy_view);
}
static GList *
clutter_stage_x11_get_views (ClutterStageWindow *stage_window)
{
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
return stage_x11->onscreen;
ensure_legacy_view (stage_window);
return stage_x11->legacy_views;
}
static CoglFrameClosure *
@ -1007,7 +1036,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
iface->can_clip_redraws = clutter_stage_x11_can_clip_redraws;
iface->set_scale_factor = clutter_stage_x11_set_scale_factor;
iface->get_scale_factor = clutter_stage_x11_get_scale_factor;
iface->get_legacy_onscreen = clutter_stage_x11_get_legacy_onscreen;
iface->get_views = clutter_stage_x11_get_views;
iface->set_frame_callback = clutter_stage_x11_set_frame_callback;
iface->remove_frame_callback = clutter_stage_x11_remove_frame_callback;
iface->get_frame_counter = clutter_stage_x11_get_frame_counter;
@ -1112,6 +1141,8 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
if (!stage_x11->is_foreign_xwin)
{
gboolean size_changed = FALSE;
int stage_width;
int stage_height;
CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)",
(unsigned int) stage_x11->xwin,
@ -1131,9 +1162,9 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
stage_x11->xwin_height = xevent->xconfigure.height;
}
clutter_actor_set_size (CLUTTER_ACTOR (stage),
xevent->xconfigure.width / stage_x11->scale_factor,
xevent->xconfigure.height / stage_x11->scale_factor);
stage_width = xevent->xconfigure.width / stage_x11->scale_factor;
stage_height = xevent->xconfigure.height / stage_x11->scale_factor;
clutter_actor_set_size (CLUTTER_ACTOR (stage), stage_width, stage_height);
if (size_changed)
{
@ -1194,6 +1225,22 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
* to set up the GL viewport with the new size
*/
clutter_stage_ensure_viewport (stage);
/* If this was a result of the Xrandr change when running as a
* X11 compositing manager, we need to reset the legacy
* stage view, now that it has a new size.
*/
if (stage_x11->legacy_view)
{
cairo_rectangle_int_t view_layout = {
.width = stage_width,
.height = stage_height
};
g_object_set (G_OBJECT (stage_x11->legacy_view),
"layout", &view_layout,
NULL);
}
}
}
break;
@ -1463,12 +1510,6 @@ set_foreign_window_callback (ClutterActor *actor,
g_hash_table_insert (clutter_stages_by_xid,
GINT_TO_POINTER (fwd->stage_x11->xwin),
fwd->stage_x11);
/* calling this with the stage unrealized will unset the stage
* from the GL context; once the stage is realized the GL context
* will be set again
*/
clutter_stage_ensure_current (CLUTTER_STAGE (actor));
}
/**

View File

@ -58,6 +58,9 @@ struct _ClutterStageX11
gint xwin_width;
gint xwin_height; /* FIXME target_width / height */
ClutterStageView *legacy_view;
GList *legacy_views;
gchar *title;
guint clipped_redraws_cool_off;

View File

@ -107,6 +107,8 @@ libmutter_la_SOURCES = \
backends/meta-stage.c \
backends/meta-renderer.c \
backends/meta-renderer.h \
backends/meta-renderer-view.c \
backends/meta-renderer-view.h \
backends/edid-parse.c \
backends/edid.h \
backends/x11/meta-backend-x11.c \

View File

@ -106,6 +106,8 @@ struct _MetaBackendClass
void meta_init_backend (MetaBackendType backend_type);
ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend);
MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend,
int device_id);
MetaMonitorManager * meta_backend_get_monitor_manager (MetaBackend *backend);

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "backends/meta-renderer-view.h"
#include "backends/meta-renderer.h"
#include "clutter/clutter-mutter.h"
struct _MetaRendererView
{
ClutterStageView parent;
MetaMonitorInfo *monitor_info;
};
G_DEFINE_TYPE (MetaRendererView, meta_renderer_view,
CLUTTER_TYPE_STAGE_VIEW_COGL)
static void
meta_renderer_view_init (MetaRendererView *view)
{
}
static void
meta_renderer_view_class_init (MetaRendererViewClass *klass)
{
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef META_RENDERER_VIEW_H
#define META_RENDERER_VIEW_H
#include "backends/meta-monitor-manager-private.h"
#include "clutter/clutter-mutter.h"
#define META_TYPE_RENDERER_VIEW (meta_renderer_view_get_type ())
G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view,
META, RENDERER_VIEW,
ClutterStageViewCogl)
#endif /* META_RENDERER_VIEW_H */

View File

@ -26,9 +26,15 @@
#include <glib-object.h>
#include "backends/meta-backend-private.h"
#include "backends/meta-renderer.h"
G_DEFINE_TYPE (MetaRenderer, meta_renderer, G_TYPE_OBJECT)
typedef struct _MetaRendererPrivate
{
GList *views;
} MetaRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaRenderer, meta_renderer, G_TYPE_OBJECT)
CoglRenderer *
meta_renderer_create_cogl_renderer (MetaRenderer *renderer)
@ -36,6 +42,69 @@ meta_renderer_create_cogl_renderer (MetaRenderer *renderer)
return META_RENDERER_GET_CLASS (renderer)->create_cogl_renderer (renderer);
}
static MetaRendererView *
meta_renderer_create_view (MetaRenderer *renderer,
MetaMonitorInfo *monitor_info)
{
return META_RENDERER_GET_CLASS (renderer)->create_view (renderer,
monitor_info);
}
void
meta_renderer_rebuild_views (MetaRenderer *renderer)
{
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorInfo *monitor_infos;
unsigned int num_monitor_infos;
unsigned int i;
g_list_free_full (priv->views, g_object_unref);
priv->views = NULL;
monitor_infos = meta_monitor_manager_get_monitor_infos (monitor_manager,
&num_monitor_infos);
for (i = 0; i < num_monitor_infos; i++)
{
MetaRendererView *view;
view = meta_renderer_create_view (renderer, &monitor_infos[i]);
priv->views = g_list_append (priv->views, view);
}
}
void
meta_renderer_set_legacy_view (MetaRenderer *renderer,
MetaRendererView *legacy_view)
{
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
g_assert (!priv->views);
priv->views = g_list_append (priv->views, legacy_view);
}
GList *
meta_renderer_get_views (MetaRenderer *renderer)
{
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
return priv->views;
}
static void
meta_renderer_finalize (GObject *object)
{
MetaRenderer *renderer = META_RENDERER (object);
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
g_list_free_full (priv->views, g_object_unref);
priv->views = NULL;
}
static void
meta_renderer_init (MetaRenderer *renderer)
{
@ -44,4 +113,7 @@ meta_renderer_init (MetaRenderer *renderer)
static void
meta_renderer_class_init (MetaRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_renderer_finalize;
}

View File

@ -28,6 +28,9 @@
#include <glib-object.h>
#include "cogl/cogl.h"
#include "clutter/clutter-mutter.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/meta-renderer-view.h"
#define META_TYPE_RENDERER (meta_renderer_get_type ())
G_DECLARE_DERIVABLE_TYPE (MetaRenderer, meta_renderer, META, RENDERER, GObject)
@ -37,8 +40,17 @@ struct _MetaRendererClass
GObjectClass parent_class;
CoglRenderer * (* create_cogl_renderer) (MetaRenderer *renderer);
MetaRendererView * (* create_view) (MetaRenderer *renderer,
MetaMonitorInfo *monitor_info);
};
CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer);
void meta_renderer_rebuild_views (MetaRenderer *renderer);
void meta_renderer_set_legacy_view (MetaRenderer *renderer,
MetaRendererView *legacy_view);
GList * meta_renderer_get_views (MetaRenderer *renderer);
#endif /* META_RENDERER_H */

View File

@ -25,7 +25,10 @@
#include "meta-stage.h"
#include <meta/meta-backend.h>
#include <meta/meta-monitor-manager.h>
#include <meta/util.h>
#include "backends/meta-backend-private.h"
#include "clutter/clutter-mutter.h"
struct _MetaOverlay {
gboolean enabled;

View File

@ -63,6 +63,9 @@ void meta_stage_update_cursor_overlay (MetaStage *stage,
void meta_stage_set_active (MetaStage *stage,
gboolean is_active);
void meta_stage_update_view_layout (MetaStage *stage);
G_END_DECLS
#endif /* META_STAGE_H */

View File

@ -38,8 +38,10 @@
#include "meta-launcher.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-pointer-constraint.h"
#include "backends/meta-stage.h"
#include "backends/native/meta-clutter-backend-native.h"
#include "backends/native/meta-renderer-native.h"
#include "backends/native/meta-stage-native.h"
#include <stdlib.h>
@ -383,8 +385,13 @@ static void
meta_backend_native_update_screen_size (MetaBackend *backend,
int width, int height)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
MetaStageNative *stage_native;
ClutterActor *stage = meta_backend_get_stage (backend);
stage_native = meta_clutter_backend_native_get_stage_native (clutter_backend);
meta_stage_native_legacy_set_size (stage_native, width, height);
clutter_actor_set_size (stage, width, height);
}

View File

@ -36,11 +36,22 @@
struct _MetaClutterBackendNative
{
ClutterBackendEglNative parent;
MetaStageNative *stage_native;
};
G_DEFINE_TYPE (MetaClutterBackendNative, meta_clutter_backend_native,
CLUTTER_TYPE_BACKEND_EGL_NATIVE)
MetaStageNative *
meta_clutter_backend_native_get_stage_native (ClutterBackend *backend)
{
MetaClutterBackendNative *clutter_backend_native =
META_CLUTTER_BACKEND_NATIVE (backend);
return clutter_backend_native->stage_native;
}
static CoglRenderer *
meta_clutter_backend_native_get_renderer (ClutterBackend *clutter_backend,
GError **error)
@ -56,10 +67,16 @@ meta_clutter_backend_native_create_stage (ClutterBackend *backend,
ClutterStage *wrapper,
GError **error)
{
return g_object_new (META_TYPE_STAGE_NATIVE,
"backend", backend,
"wrapper", wrapper,
NULL);
MetaClutterBackendNative *clutter_backend_native =
META_CLUTTER_BACKEND_NATIVE (backend);
g_assert (!clutter_backend_native->stage_native);
clutter_backend_native->stage_native = g_object_new (META_TYPE_STAGE_NATIVE,
"backend", backend,
"wrapper", wrapper,
NULL);
return CLUTTER_STAGE_WINDOW (clutter_backend_native->stage_native);
}
static void

View File

@ -29,10 +29,13 @@
#include "clutter/clutter.h"
#include "clutter/egl/clutter-backend-eglnative.h"
#include "backends/native/meta-stage-native.h"
#define META_TYPE_CLUTTER_BACKEND_NATIVE (meta_clutter_backend_native_get_type ())
G_DECLARE_FINAL_TYPE (MetaClutterBackendNative, meta_clutter_backend_native,
META, CLUTTER_BACKEND_NATIVE,
ClutterBackendEglNative)
MetaStageNative * meta_clutter_backend_native_get_stage_native (ClutterBackend *backend);
#endif /* META_CLUTTER_BACKEND_NATIVE_H */

View File

@ -35,7 +35,7 @@ struct _MetaStageNative
{
ClutterStageCogl parent;
CoglOnscreen *onscreen;
CoglOnscreen *pending_onscreen;
};
static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
@ -47,19 +47,79 @@ G_DEFINE_TYPE_WITH_CODE (MetaStageNative, meta_stage_native,
CLUTTER_TYPE_STAGE_COGL,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init))
static MetaRendererView *
get_legacy_view (MetaRenderer *renderer)
{
GList *views;
views = meta_renderer_get_views (renderer);
g_assert (g_list_length (views) <= 1);
if (views)
return views->data;
else
return NULL;
}
static CoglOnscreen *
get_legacy_onscreen (MetaStageNative *stage_native)
{
if (stage_native->pending_onscreen)
{
return stage_native->pending_onscreen;
}
else
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
ClutterStageView *stage_view;
CoglFramebuffer *framebuffer;
stage_view = CLUTTER_STAGE_VIEW (get_legacy_view (renderer));
framebuffer = clutter_stage_view_get_framebuffer (stage_view);
return COGL_ONSCREEN (framebuffer);
}
}
void
meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
int width,
int height)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
MetaRendererView *legacy_view;
cairo_rectangle_int_t view_layout;
legacy_view = get_legacy_view (renderer);
if (!legacy_view)
return;
view_layout = (cairo_rectangle_int_t) {
.width = width,
.height = height
};
g_object_set (G_OBJECT (legacy_view),
"layout", &view_layout,
NULL);
}
static gboolean
meta_stage_native_realize (ClutterStageWindow *stage_window)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
ClutterBackend *clutter_backend = CLUTTER_BACKEND (stage_cogl->backend);
MetaBackend *backend = meta_get_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglFramebuffer *framebuffer;
GError *error = NULL;
stage_native->onscreen = cogl_onscreen_new (clutter_backend->cogl_context,
1, 1);
stage_native->pending_onscreen =
cogl_onscreen_new (clutter_backend->cogl_context, 1, 1);
if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (stage_native->onscreen),
&error))
framebuffer = COGL_FRAMEBUFFER (stage_native->pending_onscreen);
if (!cogl_framebuffer_allocate (framebuffer, &error))
meta_fatal ("Failed to allocate onscreen framebuffer: %s\n",
error->message);
@ -76,7 +136,7 @@ meta_stage_native_unrealize (ClutterStageWindow *stage_window)
clutter_stage_window_parent_iface->unrealize (stage_window);
g_clear_pointer (&stage_native->onscreen, cogl_object_unref);
g_clear_pointer (&stage_native->pending_onscreen, cogl_object_unref);
}
static gboolean
@ -89,15 +149,15 @@ static void
meta_stage_native_get_geometry (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *geometry)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (stage_native->onscreen);
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
MetaRendererView *legacy_view;
if (framebuffer)
legacy_view = get_legacy_view (renderer);
if (legacy_view)
{
*geometry = (cairo_rectangle_int_t) {
.width = cogl_framebuffer_get_width (framebuffer),
.height = cogl_framebuffer_get_height (framebuffer)
};
clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (legacy_view),
geometry);
}
else
{
@ -108,12 +168,44 @@ meta_stage_native_get_geometry (ClutterStageWindow *stage_window,
}
}
static CoglFramebuffer *
meta_stage_native_get_legacy_onscreen (ClutterStageWindow *stage_window)
static void
ensure_legacy_view (ClutterStageWindow *stage_window)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaRendererView *legacy_view;
cairo_rectangle_int_t view_layout = { 0 };
CoglFramebuffer *framebuffer;
return COGL_FRAMEBUFFER (stage_native->onscreen);
legacy_view = get_legacy_view (renderer);
if (legacy_view)
return;
if (!monitor_manager)
return;
meta_monitor_manager_get_screen_size (monitor_manager,
&view_layout.width,
&view_layout.height);
framebuffer = g_steal_pointer (&stage_native->pending_onscreen);
legacy_view = g_object_new (META_TYPE_RENDERER_VIEW,
"layout", &view_layout,
"framebuffer", framebuffer,
NULL);
meta_renderer_set_legacy_view (renderer, legacy_view);
}
static GList *
meta_stage_native_get_views (ClutterStageWindow *stage_window)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
ensure_legacy_view (stage_window);
return meta_renderer_get_views (renderer);
}
static CoglClosure *
@ -122,11 +214,13 @@ meta_stage_native_set_frame_callback (ClutterStageWindow *stage_window,
gpointer user_data)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
CoglOnscreen *legacy_onscreen;
cogl_onscreen_set_swap_throttled (stage_native->onscreen,
legacy_onscreen = get_legacy_onscreen (stage_native);
cogl_onscreen_set_swap_throttled (legacy_onscreen,
_clutter_get_sync_to_vblank ());
return cogl_onscreen_add_frame_callback (stage_native->onscreen,
return cogl_onscreen_add_frame_callback (legacy_onscreen,
callback,
user_data,
NULL);
@ -137,16 +231,22 @@ meta_stage_native_remove_frame_callback (ClutterStageWindow *stage_window,
CoglFrameClosure *closure)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
CoglOnscreen *legacy_onscreen;
cogl_onscreen_remove_frame_callback (stage_native->onscreen, closure);
legacy_onscreen = get_legacy_onscreen (stage_native);
cogl_onscreen_remove_frame_callback (legacy_onscreen, closure);
}
static int64_t
meta_stage_native_get_frame_counter (ClutterStageWindow *stage_window)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
CoglOnscreen *legacy_onscreen;
return cogl_onscreen_get_frame_counter (stage_native->onscreen);
legacy_onscreen = get_legacy_onscreen (stage_native);
return cogl_onscreen_get_frame_counter (legacy_onscreen);
}
static void
@ -168,7 +268,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
iface->unrealize = meta_stage_native_unrealize;
iface->can_clip_redraws = meta_stage_native_can_clip_redraws;
iface->get_geometry = meta_stage_native_get_geometry;
iface->get_legacy_onscreen = meta_stage_native_get_legacy_onscreen;
iface->get_views = meta_stage_native_get_views;
iface->set_frame_callback = meta_stage_native_set_frame_callback;
iface->remove_frame_callback = meta_stage_native_remove_frame_callback;
iface->get_frame_counter = meta_stage_native_get_frame_counter;

View File

@ -31,4 +31,8 @@
G_DECLARE_FINAL_TYPE (MetaStageNative, meta_stage_native,
META, STAGE_NATIVE, ClutterStageCogl)
void meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
int width,
int height);
#endif /* META_STAGE_NATIVE_H */

View File

@ -41,6 +41,7 @@
#include "meta-idle-monitor-xsync.h"
#include "meta-monitor-manager-xrandr.h"
#include "backends/meta-monitor-manager-dummy.h"
#include "backends/meta-stage.h"
#include "backends/x11/nested/meta-cursor-renderer-x11-nested.h"
#include "backends/x11/meta-clutter-backend-x11.h"
#include "backends/x11/meta-renderer-x11.h"
@ -814,7 +815,9 @@ meta_backend_x11_update_screen_size (MetaBackend *backend,
if (priv->mode == META_BACKEND_X11_MODE_NESTED)
{
ClutterActor *stage = meta_backend_get_stage (backend);
MetaRenderer *renderer = meta_backend_get_renderer (backend);
meta_renderer_rebuild_views (renderer);
clutter_actor_set_size (stage, width, height);
}
else

View File

@ -31,8 +31,12 @@
#include "cogl/cogl-xlib.h"
#include "cogl/winsys/cogl-winsys-glx-private.h"
#include "cogl/winsys/cogl-winsys-egl-x11-private.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-renderer.h"
#include "backends/meta-renderer-view.h"
#include "backends/x11/meta-renderer-x11.h"
#include "core/boxes-private.h"
#include "meta/meta-backend.h"
#include "meta/util.h"
struct _MetaRendererX11
@ -64,6 +68,32 @@ meta_renderer_x11_create_cogl_renderer (MetaRenderer *renderer)
return cogl_renderer;
}
static MetaRendererView *
meta_renderer_x11_create_view (MetaRenderer *renderer,
MetaMonitorInfo *monitor_info)
{
MetaBackend *backend = meta_get_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
int width, height;
cairo_rectangle_int_t view_layout;
CoglTexture2D *texture_2d;
CoglOffscreen *offscreen;
g_assert (meta_is_wayland_compositor ());
width = monitor_info->rect.width;
height = monitor_info->rect.height;
texture_2d = cogl_texture_2d_new_with_size (cogl_context, width, height);
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture_2d));
view_layout = meta_rectangle_to_cairo_rectangle (&monitor_info->rect);
return g_object_new (META_TYPE_RENDERER_VIEW,
"layout", &view_layout,
"framebuffer", COGL_FRAMEBUFFER (offscreen),
NULL);
}
static void
meta_renderer_x11_init (MetaRendererX11 *renderer_x11)
{
@ -75,4 +105,5 @@ meta_renderer_x11_class_init (MetaRendererX11Class *klass)
MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass);
renderer_class->create_cogl_renderer = meta_renderer_x11_create_cogl_renderer;
renderer_class->create_view = meta_renderer_x11_create_view;
}

View File

@ -26,13 +26,127 @@
#include "backends/x11/meta-stage-x11-nested.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-renderer.h"
#include "clutter/clutter-mutter.h"
static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
struct _MetaStageX11Nested
{
ClutterStageX11 parent;
CoglPipeline *pipeline;
GList *views;
};
G_DEFINE_TYPE (MetaStageX11Nested, meta_stage_x11_nested,
CLUTTER_TYPE_STAGE_X11)
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (MetaStageX11Nested, meta_stage_x11_nested,
CLUTTER_TYPE_STAGE_X11,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init))
typedef struct _ClutterStageX11View
{
CoglTexture *texture;
ClutterStageViewCogl *view;
} MetaStageX11NestedView;
static gboolean
meta_stage_x11_nested_can_clip_redraws (ClutterStageWindow *stage_window)
{
return FALSE;
}
static GList *
meta_stage_x11_nested_get_views (ClutterStageWindow *stage_window)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
return meta_renderer_get_views (renderer);
}
static void
meta_stage_x11_nested_finish_frame (ClutterStageWindow *stage_window)
{
MetaStageX11Nested *stage_nested = META_STAGE_X11_NESTED (stage_window);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglFramebuffer *onscreen = COGL_FRAMEBUFFER (stage_x11->onscreen);
GList *l;
if (!stage_nested->pipeline)
stage_nested->pipeline = cogl_pipeline_new (clutter_backend->cogl_context);
cogl_framebuffer_clear4f (onscreen,
COGL_BUFFER_BIT_COLOR,
0.0f, 0.0f, 0.0f, 1.0f);
for (l = meta_renderer_get_views (renderer); l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
CoglFramebuffer *framebuffer;
CoglTexture *texture;
clutter_stage_view_get_layout (view, &view_layout);
framebuffer = clutter_stage_view_get_framebuffer (view);
texture = cogl_offscreen_get_texture (COGL_OFFSCREEN (framebuffer));
cogl_framebuffer_set_viewport (onscreen,
view_layout.x,
view_layout.y,
view_layout.width,
view_layout.height);
cogl_pipeline_set_layer_texture (stage_nested->pipeline, 0, texture);
cogl_framebuffer_draw_rectangle (onscreen,
stage_nested->pipeline,
-1, 1, 1, -1);
}
cogl_onscreen_swap_buffers (stage_x11->onscreen);
}
static void
meta_stage_x11_nested_unrealize (ClutterStageWindow *stage_window)
{
MetaStageX11Nested *stage_nested = META_STAGE_X11_NESTED (stage_window);
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
GList *l;
/* Clutter still uses part of the deprecated stateful API of Cogl
* (in particulart cogl_set_framebuffer). It means Cogl can keep an
* internal reference to the onscreen object we rendered to. In the
* case of foreign window, we want to avoid this, as we don't know
* what's going to happen to that window.
*
* The following call sets the current Cogl framebuffer to a dummy
* 1x1 one if we're unrealizing the current one, so Cogl doesn't
* keep any reference to the foreign window.
*/
for (l = meta_renderer_get_views (renderer); l ;l = l->next)
{
ClutterStageView *view = l->data;
CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
if (cogl_get_draw_framebuffer () == framebuffer)
{
_clutter_backend_reset_cogl_framebuffer (stage_cogl->backend);
break;
}
}
g_clear_pointer (&stage_nested->pipeline, cogl_object_unref);
clutter_stage_window_parent_iface->unrealize (stage_window);
}
static void
meta_stage_x11_nested_init (MetaStageX11Nested *stage_x11_nested)
@ -43,3 +157,15 @@ static void
meta_stage_x11_nested_class_init (MetaStageX11NestedClass *klass)
{
}
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
iface->can_clip_redraws = meta_stage_x11_nested_can_clip_redraws;
iface->unrealize = meta_stage_x11_nested_unrealize;
iface->get_views = meta_stage_x11_nested_get_views;
iface->finish_frame = meta_stage_x11_nested_finish_frame;
}

View File

@ -215,4 +215,6 @@ GList* meta_rectangle_find_nonintersected_monitor_edges (
const GList *monitor_rects,
const GSList *all_struts);
cairo_rectangle_int_t meta_rectangle_to_cairo_rectangle (MetaRectangle *rect);
#endif /* META_BOXES_PRIVATE_H */

View File

@ -2013,3 +2013,14 @@ meta_rectangle_find_nonintersected_monitor_edges (
return ret;
}
cairo_rectangle_int_t
meta_rectangle_to_cairo_rectangle (MetaRectangle *rect)
{
return (cairo_rectangle_int_t) {
.x = rect->x,
.y = rect->y,
.width = rect->width,
.height = rect->height
};
}