b499696d83
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.
367 lines
11 KiB
C
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);
|
|
}
|