mutter/clutter/egl/clutter-stage-egl.c
Robert Bragg b499696d83 Use paint volumes to do automatic culling
This uses actor paint volumes to perform culling during
clutter_actor_paint.

When performing a clipped redraw (because only a few localized actors
changed) then as we traverse the scenegraph painting the actors we can
now ignore actors that don't intersect the clip region. Early testing
shows this can have a big performance benefit; e.g. 100% fps improvement
for test-state with culling enabled and we hope that there are even much
more compelling examples than that in the real world,

Most Clutter applications are 2Dish interfaces and have quite a lot of
actors that get continuously painted when anything is animated. The
dynamic actors are often localized to an area of user focus though so
with culling we can completely avoid painting any of the static actors
outside the current clip region.

Obviously the cost of culling has to be offset against the cost of
painting to determine if it's a win, but our (limited) testing suggests
it should be a win for most applications.

Note: we hope we will be able to also bring another performance bump
from culling with another iteration - hopefully in the 1.6 cycle - to
avoid doing the culling in screen space and instead do it in the stage's
model space. This will hopefully let us minimize the cost of
transforming the actor volumes for culling.
2010-09-29 15:12:58 +01:00

367 lines
11 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-stage-egl.h"
#include "clutter-egl.h"
#include "clutter-backend-egl.h"
#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"
#include "../clutter-stage.h"
#include "../clutter-stage-window.h"
#ifdef COGL_HAS_X11_SUPPORT
static ClutterStageWindowIface *clutter_stage_egl_parent_iface = NULL;
#endif
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL,
_clutter_stage_egl,
#ifdef COGL_HAS_X11_SUPPORT
CLUTTER_TYPE_STAGE_X11,
#else
G_TYPE_OBJECT,
#endif
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init));
#ifdef COGL_HAS_XLIB_SUPPORT
static void
clutter_stage_egl_unrealize (ClutterStageWindow *stage_window)
{
ClutterBackend *backend = clutter_get_default_backend ();
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
CLUTTER_NOTE (BACKEND, "Unrealizing stage");
clutter_x11_trap_x_errors ();
if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None)
{
XDestroyWindow (backend_x11->xdpy, stage_x11->xwin);
stage_x11->xwin = None;
}
else
stage_x11->xwin = None;
if (stage_egl->egl_surface != EGL_NO_SURFACE)
{
eglDestroySurface (clutter_eglx_display (), stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
XSync (backend_x11->xdpy, False);
clutter_x11_untrap_x_errors ();
}
static gboolean
clutter_stage_egl_realize (ClutterStageWindow *stage_window)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
ClutterBackend *backend;
ClutterBackendEGL *backend_egl;
ClutterBackendX11 *backend_x11;
EGLDisplay edpy;
CLUTTER_NOTE (BACKEND, "Realizing main stage");
backend = clutter_get_default_backend ();
backend_egl = CLUTTER_BACKEND_EGL (backend);
backend_x11 = CLUTTER_BACKEND_X11 (backend);
edpy = clutter_eglx_display ();
if (stage_x11->xwin == None)
{
XSetWindowAttributes xattr;
unsigned long mask;
XVisualInfo *xvisinfo;
gfloat width, height;
CLUTTER_NOTE (MISC, "Creating stage X window");
xvisinfo = clutter_backend_x11_get_visual_info (backend_x11);
if (xvisinfo == NULL)
{
g_critical ("Unable to find suitable GL visual.");
return FALSE;
}
/* window attributes */
xattr.background_pixel = WhitePixel (backend_x11->xdpy,
backend_x11->xscreen_num);
xattr.border_pixel = 0;
xattr.colormap = XCreateColormap (backend_x11->xdpy,
backend_x11->xwin_root,
xvisinfo->visual,
AllocNone);
mask = CWBorderPixel | CWColormap;
/* Call get_size - this will either get the geometry size (which
* before we create the window is set to 640x480), or if a size
* is set, it will get that. This lets you set a size on the
* stage before it's realized.
*/
clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper),
&width,
&height);
stage_x11->xwin_width = (gint)width;
stage_x11->xwin_height = (gint)height;
stage_x11->xwin = XCreateWindow (backend_x11->xdpy,
backend_x11->xwin_root,
0, 0,
stage_x11->xwin_width,
stage_x11->xwin_height,
0,
xvisinfo->depth,
InputOutput,
xvisinfo->visual,
mask, &xattr);
CLUTTER_NOTE (BACKEND, "Stage [%p], window: 0x%x, size: %dx%d",
stage_window,
(unsigned int) stage_x11->xwin,
stage_x11->xwin_width,
stage_x11->xwin_height);
XFree (xvisinfo);
}
if (stage_egl->egl_surface == EGL_NO_SURFACE)
{
stage_egl->egl_surface =
eglCreateWindowSurface (edpy,
backend_egl->egl_config,
(NativeWindowType) stage_x11->xwin,
NULL);
}
if (stage_egl->egl_surface == EGL_NO_SURFACE)
g_warning ("Unable to create an EGL surface");
if (clutter_x11_has_event_retrieval ())
{
if (clutter_x11_has_xinput ())
{
XSelectInput (backend_x11->xdpy, stage_x11->xwin,
StructureNotifyMask |
FocusChangeMask |
ExposureMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask);
#ifdef USE_XINPUT
_clutter_x11_select_events (stage_x11->xwin);
#endif
}
else
XSelectInput (backend_x11->xdpy, stage_x11->xwin,
StructureNotifyMask |
FocusChangeMask |
ExposureMask |
PointerMotionMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask);
}
/* no user resize... */
clutter_stage_x11_fix_window_size (stage_x11,
stage_x11->xwin_width,
stage_x11->xwin_height);
clutter_stage_x11_set_wm_protocols (stage_x11);
return clutter_stage_egl_parent_iface->realize (stage_window);
}
#else /* COGL_HAS_XLIB_SUPPORT */
static void
clutter_stage_egl_unrealize (ClutterStageWindow *stage_window)
{
}
static gboolean
clutter_stage_egl_realize (ClutterStageWindow *stage_window)
{
/* the EGL surface is created by the backend */
return TRUE;
}
static void
clutter_stage_egl_set_fullscreen (ClutterStageWindow *stage_window,
gboolean fullscreen)
{
g_warning ("Stage of type '%s' do not support ClutterStage::set_fullscreen",
G_OBJECT_TYPE_NAME (stage_window));
}
static void
clutter_stage_egl_set_title (ClutterStageWindow *stage_window,
const gchar *title)
{
g_warning ("Stage of type '%s' do not support ClutterStage::set_title",
G_OBJECT_TYPE_NAME (stage_window));
}
static void
clutter_stage_egl_set_cursor_visible (ClutterStageWindow *stage_window,
gboolean cursor_visible)
{
g_warning ("Stage of type '%s' do not support ClutterStage::set_cursor_visible",
G_OBJECT_TYPE_NAME (stage_window));
}
static ClutterActor *
clutter_stage_egl_get_wrapper (ClutterStageWindow *stage_window)
{
return CLUTTER_ACTOR (CLUTTER_STAGE_EGL (stage_window)->wrapper);
}
static void
clutter_stage_egl_show (ClutterStageWindow *stage_window,
gboolean do_raise)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
clutter_actor_map (CLUTTER_ACTOR (stage_egl->wrapper));
}
static void
clutter_stage_egl_hide (ClutterStageWindow *stage_window)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
clutter_actor_unmap (CLUTTER_ACTOR (stage_egl->wrapper));
}
static void
clutter_stage_egl_get_geometry (ClutterStageWindow *stage_window,
ClutterGeometry *geometry)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
ClutterBackendEGL *backend_egl = stage_egl->backend;
if (geometry)
{
geometry->x = geometry->y = 0;
geometry->width = backend_egl->surface_width;
geometry->height = backend_egl->surface_height;
}
}
static void
clutter_stage_egl_resize (ClutterStageWindow *stage_window,
gint width,
gint height)
{
}
#endif /* COGL_HAS_XLIB_SUPPORT */
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
#ifdef COGL_HAS_X11_SUPPORT
clutter_stage_egl_parent_iface = g_type_interface_peek_parent (iface);
iface->realize = clutter_stage_egl_realize;
iface->unrealize = clutter_stage_egl_unrealize;
/* the rest is inherited from ClutterStageX11 */
#else /* COGL_HAS_X11_SUPPORT */
iface->realize = clutter_stage_egl_realize;
iface->unrealize = clutter_stage_egl_unrealize;
iface->set_fullscreen = clutter_stage_egl_set_fullscreen;
iface->set_title = clutter_stage_egl_set_title;
iface->set_cursor_visible = clutter_stage_egl_set_cursor_visible;
iface->get_wrapper = clutter_stage_egl_get_wrapper;
iface->get_geometry = clutter_stage_egl_get_geometry;
iface->resize = clutter_stage_egl_resize;
iface->show = clutter_stage_egl_show;
iface->hide = clutter_stage_egl_hide;
#endif /* COGL_HAS_X11_SUPPORT */
}
#ifdef COGL_HAS_X11_SUPPORT
static void
clutter_stage_egl_dispose (GObject *gobject)
{
G_OBJECT_CLASS (_clutter_stage_egl_parent_class)->dispose (gobject);
}
static void
_clutter_stage_egl_class_init (ClutterStageEGLClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = clutter_stage_egl_dispose;
}
static void
_clutter_stage_egl_init (ClutterStageEGL *stage)
{
stage->egl_surface = EGL_NO_SURFACE;
}
#else /* COGL_HAS_X11_SUPPORT */
static void
_clutter_stage_egl_class_init (ClutterStageEGLClass *klass)
{
}
static void
_clutter_stage_egl_init (ClutterStageEGL *stage)
{
/* Without X we only support one surface and that is associated
* with the backend directly instead of the stage */
}
#endif /* COGL_HAS_X11_SUPPORT */
void
_clutter_stage_egl_redraw (ClutterStageEGL *stage_egl,
ClutterStage *stage)
{
ClutterBackend *backend = clutter_get_default_backend ();
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
ClutterActor *wrapper;
EGLSurface egl_surface;
#ifdef COGL_HAS_X11_SUPPORT
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_egl);
wrapper = CLUTTER_ACTOR (stage_x11->wrapper);
egl_surface = stage_egl->egl_surface;
#else
wrapper = CLUTTER_ACTOR (stage_egl->wrapper);
/* Without X we only support one surface and that is associated
* with the backend directly instead of the stage */
egl_surface = backend_egl->egl_surface;
#endif
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
cogl_flush ();
eglSwapBuffers (backend_egl->edpy, egl_surface);
}