2007-05-16 Matthew Allum <mallum@openedhand.com>

* clutter/clutter-backend.c:
        * clutter/clutter-backend.h:
        * clutter/clutter-feature.c:
        * clutter/clutter-feature.h:
        * clutter/clutter-main.c:
        * clutter/clutter-main.h:
        * clutter/clutter-private.h:
        * clutter/clutter-stage.c:
        * clutter/cogl/cogl.h:
        * clutter/cogl/gl/cogl.c:
        * clutter/cogl/gles/cogl.c:
        * clutter/glx/clutter-backend-glx.c:
        * clutter/glx/clutter-backend-glx.h:
        * clutter/glx/clutter-glx.h:
        * clutter/glx/clutter-stage-glx.c:
        * clutter/glx/clutter-stage-glx.h:
        Rejig the features() foo, moving mostly into backends/cogl.
This commit is contained in:
Matthew Allum 2007-05-16 09:08:30 +00:00
parent aab511d319
commit ee928107cb
17 changed files with 388 additions and 377 deletions

View File

@ -1,3 +1,23 @@
2007-05-16 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-backend.c:
* clutter/clutter-backend.h:
* clutter/clutter-feature.c:
* clutter/clutter-feature.h:
* clutter/clutter-main.c:
* clutter/clutter-main.h:
* clutter/clutter-private.h:
* clutter/clutter-stage.c:
* clutter/cogl/cogl.h:
* clutter/cogl/gl/cogl.c:
* clutter/cogl/gles/cogl.c:
* clutter/glx/clutter-backend-glx.c:
* clutter/glx/clutter-backend-glx.h:
* clutter/glx/clutter-glx.h:
* clutter/glx/clutter-stage-glx.c:
* clutter/glx/clutter-stage-glx.h:
Rejig the features() foo, moving mostly into backends/cogl.
2007-05-15 Tomas Frydrych <tf@openedhand.com> 2007-05-15 Tomas Frydrych <tf@openedhand.com>
* clutter/clutter-actor.c: * clutter/clutter-actor.c:

View File

@ -143,16 +143,18 @@ _clutter_backend_init_stage (ClutterBackend *backend,
return TRUE; return TRUE;
} }
void ClutterFeatureFlags
_clutter_backend_init_features (ClutterBackend *backend) _clutter_backend_get_features (ClutterBackend *backend)
{ {
ClutterBackendClass *klass; ClutterBackendClass *klass;
g_return_if_fail (CLUTTER_IS_BACKEND (backend)); g_return_if_fail (CLUTTER_IS_BACKEND (backend));
klass = CLUTTER_BACKEND_GET_CLASS (backend); klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (klass->init_features) if (klass->get_features)
klass->init_features (backend); return klass->get_features (backend);
return 0;
} }
void void

View File

@ -29,6 +29,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <clutter/clutter-actor.h> #include <clutter/clutter-actor.h>
#include <clutter/clutter-event.h> #include <clutter/clutter-event.h>
#include <clutter/clutter-feature.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -66,6 +67,7 @@ struct _ClutterBackendClass
ClutterActor *(* get_stage) (ClutterBackend *backend); ClutterActor *(* get_stage) (ClutterBackend *backend);
void (* add_options) (ClutterBackend *backend, void (* add_options) (ClutterBackend *backend,
GOptionGroup *group); GOptionGroup *group);
ClutterFeatureFlags (* get_features) (ClutterBackend *backend);
}; };
GType clutter_backend_get_type (void) G_GNUC_CONST; GType clutter_backend_get_type (void) G_GNUC_CONST;

View File

@ -35,311 +35,48 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <dlfcn.h>
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-main.h" #include "clutter-main.h"
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-debug.h" #include "clutter-debug.h"
#ifdef HAVE_CLUTTER_GLX #include "cogl.h"
#include "glx/clutter-glx.h"
#endif
typedef void (*FuncPtr) (void);
typedef int (*GetVideoSyncProc) (unsigned int *count);
typedef int (*WaitVideoSyncProc) (int divisor,
int remainder,
unsigned int *count);
typedef FuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
typedef struct ClutterFeatureFuncs
{
GetVideoSyncProc get_video_sync;
WaitVideoSyncProc wait_video_sync;
} ClutterFeatureFuncs;
typedef enum ClutterVBlankType
{
CLUTTER_VBLANK_NONE = 0,
CLUTTER_VBLANK_GLX,
CLUTTER_VBLANK_DRI
} ClutterVBlankType;
typedef struct ClutterFeatures typedef struct ClutterFeatures
{ {
ClutterFeatureFlags flags; ClutterFeatureFlags flags;
ClutterFeatureFuncs funcs;
gint dri_fd;
ClutterVBlankType vblank_type;
guint features_set : 1; guint features_set : 1;
} ClutterFeatures; } ClutterFeatures;
static ClutterFeatures* __features = NULL; static ClutterFeatures* __features = NULL;
G_LOCK_DEFINE_STATIC (__features); G_LOCK_DEFINE_STATIC (__features);
void
#ifdef __linux__ _clutter_feature_init (void)
#define DRM_VBLANK_RELATIVE 0x1;
struct drm_wait_vblank_request {
int type;
unsigned int sequence;
unsigned long signal;
};
struct drm_wait_vblank_reply {
int type;
unsigned int sequence;
long tval_sec;
long tval_usec;
};
typedef union drm_wait_vblank {
struct drm_wait_vblank_request request;
struct drm_wait_vblank_reply reply;
} drm_wait_vblank_t;
#define DRM_IOCTL_BASE 'd'
#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t)
static int drm_wait_vblank(int fd, drm_wait_vblank_t *vbl)
{ {
int ret, rc; ClutterMainContext *context;
do
{
ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
vbl->request.type &= ~DRM_VBLANK_RELATIVE;
rc = errno;
}
while (ret && rc == EINTR);
return rc;
}
#endif
/* Note must be called after context created */
static gboolean
check_gl_extension (const gchar *name,
const gchar *ext)
{
/* FIXME: move to cogl */
gchar *end;
gint name_len, n;
if (name == NULL || ext == NULL)
return FALSE;
end = (gchar*)(ext + strlen(ext));
name_len = strlen(name);
while (ext < end)
{
n = strcspn(ext, " ");
if ((name_len == n) && (!strncmp(name, ext, n)))
return TRUE;
ext += (n + 1);
}
return FALSE;
}
/* FIXME: move to cogl */
static FuncPtr
get_proc_address (const gchar *name)
{
static GLXGetProcAddressProc get_proc_func = NULL;
static void *dlhand = NULL;
if (get_proc_func == NULL && dlhand == NULL)
{
dlhand = dlopen (NULL, RTLD_LAZY);
if (dlhand)
{
dlerror ();
get_proc_func =
(GLXGetProcAddressProc) dlsym (dlhand, "glXGetProcAddress");
if (dlerror () != NULL)
{
get_proc_func =
(GLXGetProcAddressProc) dlsym (dlhand, "glXGetProcAddressARB");
}
if (dlerror () != NULL)
{
get_proc_func = NULL;
g_warning ("failed to bind GLXGetProcAddress "
"or GLXGetProcAddressARB");
}
}
}
if (get_proc_func)
return get_proc_func ((unsigned char*) name);
return NULL;
}
static gboolean
check_vblank_env (const char *name)
{
const char *val;
val = clutter_get_vblank_method ();
if (val && !strcasecmp(val, name))
return TRUE;
return FALSE;
}
/* clutter_feature_init:
* must be called with the static lock on __features held, to keep it
* mt-safe.
*
* XXX - here we need a bit of weird machinery in place. the features
* are checked at run time, each time we try to access them - unless
* we already checked them once. unfortunately, we need an open
* X display if we want to check them, so ideally we'd need to call
* clutter_init() before checking for any feature. the generator for
* the api documentation, and more in general every tool relying on the
* introspection API provided by GObject, may well not be able to call
* clutter_init() (and neither they should be, as we might be running
* them on a headless build box). so, we need a way to get the features
* without explicitely calling clutter_feature_init() inside clutter_init()
* and we also need to have an open X display when we test for the features.
* __features is dynamically allocated, and applications tend to badly
* crash when trying to access __features components if we did not allocate
* it; so when can't use a NULL check to know whether we already invoked
* clutter_feature_init() once; hence, we must allocate it anyway, and have
* a flag to let us know when the features have been set - that is when
* clutter_feature_init() has been successfully completed with an open
* X display.
*/
static void
clutter_feature_init (void)
{
const gchar *gl_extensions, *glx_extensions = NULL;
CLUTTER_NOTE (MISC, "checking features"); CLUTTER_NOTE (MISC, "checking features");
if (!__features) if (!__features)
{ {
CLUTTER_NOTE (MISC, "allocating features data"); CLUTTER_NOTE (MISC, "allocating features data");
__features = g_new0 (ClutterFeatures, 1); __features = g_new0 (ClutterFeatures, 1);
memset(&__features->funcs, 0, sizeof (ClutterFeatureFuncs));
__features->features_set = FALSE; /* don't rely on zero-ing */ __features->features_set = FALSE; /* don't rely on zero-ing */
} }
#ifdef HAVE_CLUTTER_GLX
if (!clutter_glx_get_default_display ())
return;
#endif
if (__features->features_set) if (__features->features_set)
return; return;
#ifdef HAVE_CLUTTER_GLX context = clutter_context_get_default ();
glx_extensions
= glXQueryExtensionsString (clutter_glx_get_default_display (),
clutter_glx_get_default_screen ());
#endif
gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS); __features->flags = cogl_get_features()
|_clutter_backend_get_features (context->backend);
if (check_gl_extension ("GL_ARB_texture_rectangle", gl_extensions) ||
check_gl_extension ("GL_EXT_texture_rectangle", gl_extensions))
{
__features->flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE;
}
/* vblank */
__features->vblank_type = CLUTTER_VBLANK_NONE;
if (getenv("__GL_SYNC_TO_VBLANK") || check_vblank_env ("none"))
{
CLUTTER_NOTE (MISC, "vblank sync: disabled at user request");
}
else
{
#ifdef HAVE_CLUTTER_GLX
if (!check_vblank_env ("dri") &&
check_gl_extension ("GLX_SGI_video_sync", glx_extensions))
{
__features->funcs.get_video_sync =
(GetVideoSyncProc) get_proc_address ("glXGetVideoSyncSGI");
__features->funcs.wait_video_sync =
(WaitVideoSyncProc) get_proc_address ("glXWaitVideoSyncSGI");
if ((__features->funcs.get_video_sync != NULL) &&
(__features->funcs.wait_video_sync != NULL))
{
CLUTTER_NOTE (MISC, "vblank sync: using glx");
__features->vblank_type = CLUTTER_VBLANK_GLX;
__features->flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
}
}
#endif
#ifdef __linux__
if (!(__features->flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
{
__features->dri_fd = open("/dev/dri/card0", O_RDWR);
if (__features->dri_fd >= 0)
{
CLUTTER_NOTE (MISC, "vblank sync: using dri");
__features->vblank_type = CLUTTER_VBLANK_DRI;
__features->flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
}
}
#endif
if (!(__features->flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
{
CLUTTER_NOTE (MISC,
"vblank sync: no use-able mechanism found");
}
}
CLUTTER_NOTE (MISC, "features checked");
__features->features_set = TRUE; __features->features_set = TRUE;
}
static inline void CLUTTER_NOTE (MISC, "features checked");
clutter_feature_do_init (void)
{
if (G_UNLIKELY (__features == NULL) ||
G_UNLIKELY (__features->features_set == FALSE))
{
G_LOCK (__features);
clutter_feature_init ();
G_UNLOCK (__features);
}
} }
/** /**
@ -356,8 +93,6 @@ clutter_feature_do_init (void)
gboolean gboolean
clutter_feature_available (ClutterFeatureFlags feature) clutter_feature_available (ClutterFeatureFlags feature)
{ {
clutter_feature_do_init ();
return (__features->flags & feature); return (__features->flags & feature);
} }
@ -373,49 +108,6 @@ clutter_feature_available (ClutterFeatureFlags feature)
ClutterFeatureFlags ClutterFeatureFlags
clutter_feature_get_all (void) clutter_feature_get_all (void)
{ {
clutter_feature_do_init ();
return __features->flags; return __features->flags;
} }
/**
* clutter_feature_wait_for_vblank:
*
* FIXME
*
* Since: 0.2
*/
void
clutter_feature_wait_for_vblank (void)
{
clutter_feature_do_init ();
switch (__features->vblank_type)
{
case CLUTTER_VBLANK_GLX:
#ifdef HAVE_CLUTTER_GLX
{
unsigned int retraceCount;
__features->funcs.get_video_sync (&retraceCount);
__features->funcs.wait_video_sync (2,
(retraceCount + 1) % 2,
&retraceCount);
}
#endif
break;
case CLUTTER_VBLANK_DRI:
#ifdef __linux__
{
drm_wait_vblank_t blank;
blank.request.type = DRM_VBLANK_RELATIVE;
blank.request.sequence = 1;
blank.request.signal = 0;
drm_wait_vblank (__features->dri_fd, &blank);
}
#endif
break;
case CLUTTER_VBLANK_NONE:
default:
break;
}
}

View File

@ -40,12 +40,12 @@ G_BEGIN_DECLS
typedef enum typedef enum
{ {
CLUTTER_FEATURE_TEXTURE_RECTANGLE = (1 << 1), CLUTTER_FEATURE_TEXTURE_RECTANGLE = (1 << 1),
CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 2) CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 2),
CLUTTER_FEATURE_TEXTURE_READ_PIXELS = (1 << 3)
} ClutterFeatureFlags; } ClutterFeatureFlags;
gboolean clutter_feature_available (ClutterFeatureFlags feature); gboolean clutter_feature_available (ClutterFeatureFlags feature);
ClutterFeatureFlags clutter_feature_get_all (void); ClutterFeatureFlags clutter_feature_get_all (void);
void clutter_feature_wait_for_vblank (void);
G_END_DECLS G_END_DECLS

View File

@ -51,7 +51,6 @@ static ClutterMainContext *ClutterCntx = NULL;
static gboolean clutter_is_initialized = FALSE; static gboolean clutter_is_initialized = FALSE;
static gboolean clutter_show_fps = FALSE; static gboolean clutter_show_fps = FALSE;
static gboolean clutter_fatal_warnings = FALSE; static gboolean clutter_fatal_warnings = FALSE;
static gchar *clutter_vblank_name = NULL;
guint clutter_debug_flags = 0; /* global clutter debug flag */ guint clutter_debug_flags = 0; /* global clutter debug flag */
@ -85,20 +84,6 @@ clutter_get_show_fps (void)
return clutter_show_fps; return clutter_show_fps;
} }
/**
* clutter_get_vblank_method:
*
* FIXME
*
* Return value: FIXME
*
* Since: 0.4
*/
G_CONST_RETURN gchar *
clutter_get_vblank_method (void)
{
return clutter_vblank_name;
}
/** /**
* clutter_redraw: * clutter_redraw:
@ -330,8 +315,6 @@ clutter_arg_no_debug_cb (const char *key,
static GOptionEntry clutter_args[] = { static GOptionEntry clutter_args[] = {
{ "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps, { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps,
"Show frames per second", NULL }, "Show frames per second", NULL },
{ "clutter-vblank", 0, 0, G_OPTION_ARG_STRING, &clutter_vblank_name,
"VBlank method to be used (none, dri or glx)", "METHOD" },
{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings,
"Make all warnings fatal", NULL }, "Make all warnings fatal", NULL },
#ifdef CLUTTER_ENABLE_DEBUG #ifdef CLUTTER_ENABLE_DEBUG
@ -382,12 +365,6 @@ pre_parse_hook (GOptionContext *context,
} }
#endif /* CLUTTER_ENABLE_DEBUG */ #endif /* CLUTTER_ENABLE_DEBUG */
env_string = g_getenv ("CLUTTER_VBLANK");
if (env_string)
{
clutter_vblank_name = g_strdup (env_string);
env_string = NULL;
}
env_string = g_getenv ("CLUTTER_SHOW_FPS"); env_string = g_getenv ("CLUTTER_SHOW_FPS");
if (env_string) if (env_string)
@ -555,6 +532,8 @@ clutter_init_with_args (int *argc,
_clutter_backend_init_events (clutter_context->backend); _clutter_backend_init_events (clutter_context->backend);
_clutter_feature_init ();
return CLUTTER_INIT_SUCCESS; return CLUTTER_INIT_SUCCESS;
} }
@ -615,14 +594,21 @@ clutter_init (int *argc,
g_type_init (); g_type_init ();
/* parse_args will trigger backend creation and things like
* DISPLAY connection etc.
*/
if (clutter_parse_args (argc, argv) == FALSE) if (clutter_parse_args (argc, argv) == FALSE)
{ {
CLUTTER_NOTE (MISC, "failed to parse arguments."); CLUTTER_NOTE (MISC, "failed to parse arguments.");
return CLUTTER_INIT_ERROR_INTERNAL; return CLUTTER_INIT_ERROR_INTERNAL;
} }
/* Note, creates backend if not already existing (though parse args will
* have likely created it)
*/
context = clutter_context_get_default (); context = clutter_context_get_default ();
/* Stage will give us a GL Context etc */
stage_error = NULL; stage_error = NULL;
if (!_clutter_backend_init_stage (context->backend, &stage_error)) if (!_clutter_backend_init_stage (context->backend, &stage_error))
{ {
@ -632,8 +618,12 @@ clutter_init (int *argc,
return CLUTTER_INIT_ERROR_INTERNAL; return CLUTTER_INIT_ERROR_INTERNAL;
} }
/* Initiate event collection */
_clutter_backend_init_events (context->backend); _clutter_backend_init_events (context->backend);
/* finally features - will call to backend and cogl */
_clutter_feature_init ();
return CLUTTER_INIT_SUCCESS; return CLUTTER_INIT_SUCCESS;
} }

View File

@ -62,7 +62,6 @@ void clutter_redraw (void);
gboolean clutter_get_debug_enabled (void); gboolean clutter_get_debug_enabled (void);
gboolean clutter_get_show_fps (void); gboolean clutter_get_show_fps (void);
G_CONST_RETURN gchar *clutter_get_vblank_method (void);
void clutter_threads_enter (void); void clutter_threads_enter (void);
void clutter_threads_leave (void); void clutter_threads_leave (void);

View File

@ -39,6 +39,7 @@
#include "clutter-event.h" #include "clutter-event.h"
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-stage.h" #include "clutter-stage.h"
#include "clutter-feature.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -103,12 +104,14 @@ gboolean _clutter_backend_init_stage (ClutterBackend *backend,
GError **error); GError **error);
void _clutter_backend_init_events (ClutterBackend *backend); void _clutter_backend_init_events (ClutterBackend *backend);
void _clutter_backend_init_features (ClutterBackend *backend); ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend);
/* backend helpers */ /* backend helpers */
void _clutter_event_button_generate (ClutterBackend *backend, void _clutter_event_button_generate (ClutterBackend *backend,
ClutterEvent *event); ClutterEvent *event);
void _clutter_feature_init (void);
/* FIXME: move elsewhere via ClutterAudience */ /* FIXME: move elsewhere via ClutterAudience */
void _clutter_stage_sync_viewport (ClutterStage *stage); void _clutter_stage_sync_viewport (ClutterStage *stage);

View File

@ -178,6 +178,8 @@ void
cogl_alpha_func (COGLenum func, cogl_alpha_func (COGLenum func,
ClutterFixed ref); ClutterFixed ref);
ClutterFeatureFlags
cogl_get_features ();
G_END_DECLS G_END_DECLS

View File

@ -84,11 +84,56 @@ error_string(GLenum errorCode)
#define GE(x) (x); #define GE(x) (x);
#endif #endif
static gboolean
check_gl_extension (const gchar *name,
const gchar *ext)
{
gchar *end;
gint name_len, n;
if (name == NULL || ext == NULL)
return FALSE;
end = (gchar*)(ext + strlen(ext));
name_len = strlen(name);
while (ext < end)
{
n = strcspn(ext, " ");
if ((name_len == n) && (!strncmp(name, ext, n)))
return TRUE;
ext += (n + 1);
}
return FALSE;
}
static gboolean
is_gl_version_at_least_12 (void)
{
/* FIXME: This likely needs to live elsewhere in features or cogl */
return
(g_ascii_strtod ((const gchar*) glGetString (GL_VERSION), NULL) >= 1.2);
#if 0
/* At least GL 1.2 is needed for CLAMP_TO_EDGE */
/* FIXME: move to cogl... */
if (!is_gl_version_at_least_12 ())
{
g_set_error (error, CLUTTER_INIT_ERROR,
CLUTTER_INIT_ERROR_BACKEND,
"Clutter needs at least version 1.2 of OpenGL");
return FALSE;
}
#endif
}
CoglFuncPtr CoglFuncPtr
cogl_get_proc_address (const gchar* name) cogl_get_proc_address (const gchar* name)
{ {
return NULL; /* FIXME */
} }
gboolean gboolean
@ -527,3 +572,24 @@ cogl_setup_viewport (guint width,
1.0f / width) ); 1.0f / width) );
GE( glTranslatef (0.0f, -1.0 * height, 0.0f) ); GE( glTranslatef (0.0f, -1.0 * height, 0.0f) );
} }
ClutterFeatureFlags
cogl_get_features ()
{
ClutterFeatureFlags flags = 0;
const gchar *gl_extensions;
flags = CLUTTER_FEATURE_TEXTURE_READ_PIXELS;
gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS);
if (check_gl_extension ("GL_ARB_texture_rectangle", gl_extensions) ||
check_gl_extension ("GL_EXT_texture_rectangle", gl_extensions))
{
flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE;
}
return flags;
}

View File

@ -515,5 +515,11 @@ cogl_setup_viewport (guint w,
CFX_ONE / width)); CFX_ONE / width));
GE( glTranslatex (0, -CFX_ONE * height, 0) ); GE( glTranslatex (0, -CFX_ONE * height, 0) );
}
ClutterFeatureFlags
cogl_get_features ()
{
/* Suck */
return 0;
} }

View File

@ -29,16 +29,31 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <dlfcn.h>
#include "clutter-backend-glx.h" #include "clutter-backend-glx.h"
#include "clutter-stage-glx.h" #include "clutter-stage-glx.h"
#include "clutter-glx.h"
#include "../clutter-event.h" #include "../clutter-event.h"
#include "../clutter-main.h" #include "../clutter-main.h"
#include "../clutter-debug.h" #include "../clutter-debug.h"
#include "../clutter-private.h" #include "../clutter-private.h"
#include "cogl.h"
G_DEFINE_TYPE (ClutterBackendGlx, clutter_backend_glx, CLUTTER_TYPE_BACKEND); G_DEFINE_TYPE (ClutterBackendGlx, clutter_backend_glx, CLUTTER_TYPE_BACKEND);
typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
/* singleton object */ /* singleton object */
static ClutterBackendGlx *backend_singleton = NULL; static ClutterBackendGlx *backend_singleton = NULL;
@ -50,6 +65,56 @@ static gint clutter_screen = 0;
static int TrappedErrorCode = 0; static int TrappedErrorCode = 0;
static int (* old_error_handler) (Display *, XErrorEvent *); static int (* old_error_handler) (Display *, XErrorEvent *);
static gchar *clutter_vblank_name = NULL;
#ifdef __linux__
#define DRM_VBLANK_RELATIVE 0x1;
struct drm_wait_vblank_request {
int type;
unsigned int sequence;
unsigned long signal;
};
struct drm_wait_vblank_reply {
int type;
unsigned int sequence;
long tval_sec;
long tval_usec;
};
typedef union drm_wait_vblank {
struct drm_wait_vblank_request request;
struct drm_wait_vblank_reply reply;
} drm_wait_vblank_t;
#define DRM_IOCTL_BASE 'd'
#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t)
static int drm_wait_vblank(int fd, drm_wait_vblank_t *vbl)
{
int ret, rc;
do
{
ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
vbl->request.type &= ~DRM_VBLANK_RELATIVE;
rc = errno;
}
while (ret && rc == EINTR);
return rc;
}
#endif
G_CONST_RETURN gchar*
clutter_backend_glx_get_vblank_method (void)
{
return clutter_vblank_name;
}
static gboolean static gboolean
clutter_backend_glx_pre_parse (ClutterBackend *backend, clutter_backend_glx_pre_parse (ClutterBackend *backend,
GError **error) GError **error)
@ -66,6 +131,13 @@ clutter_backend_glx_pre_parse (ClutterBackend *backend,
env_string = NULL; env_string = NULL;
} }
env_string = g_getenv ("CLUTTER_VBLANK");
if (env_string)
{
clutter_vblank_name = g_strdup (env_string);
env_string = NULL;
}
return TRUE; return TRUE;
} }
@ -120,13 +192,6 @@ clutter_backend_glx_post_parse (ClutterBackend *backend,
return TRUE; return TRUE;
} }
static gboolean
is_gl_version_at_least_12 (void)
{
/* FIXME: This likely needs to live elsewhere in features or cogl */
return
(g_ascii_strtod ((const gchar*) glGetString (GL_VERSION), NULL) >= 1.2);
}
static gboolean static gboolean
clutter_backend_glx_init_stage (ClutterBackend *backend, clutter_backend_glx_init_stage (ClutterBackend *backend,
@ -146,6 +211,7 @@ clutter_backend_glx_init_stage (ClutterBackend *backend,
stage_glx->xdpy = backend_glx->xdpy; stage_glx->xdpy = backend_glx->xdpy;
stage_glx->xwin_root = backend_glx->xwin_root; stage_glx->xwin_root = backend_glx->xwin_root;
stage_glx->xscreen = backend_glx->xscreen_num; stage_glx->xscreen = backend_glx->xscreen_num;
stage_glx->backend = backend_glx;
CLUTTER_NOTE (MISC, "GLX stage created (display:%p, screen:%d, root:%u)", CLUTTER_NOTE (MISC, "GLX stage created (display:%p, screen:%d, root:%u)",
stage_glx->xdpy, stage_glx->xdpy,
@ -166,16 +232,6 @@ clutter_backend_glx_init_stage (ClutterBackend *backend,
return FALSE; return FALSE;
} }
/* At least GL 1.2 is needed for CLAMP_TO_EDGE */
/* FIXME: move to cogl... */
if (!is_gl_version_at_least_12 ())
{
g_set_error (error, CLUTTER_INIT_ERROR,
CLUTTER_INIT_ERROR_BACKEND,
"Clutter needs at least version 1.2 of OpenGL");
return FALSE;
}
return TRUE; return TRUE;
} }
@ -209,6 +265,11 @@ static const GOptionEntry entries[] =
G_OPTION_ARG_INT, &clutter_screen, G_OPTION_ARG_INT, &clutter_screen,
"X screen to use", "SCREEN" "X screen to use", "SCREEN"
}, },
{ "vblank", 0,
G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_STRING, &clutter_vblank_name,
"VBlank method to be used (none, dri or glx)", "METHOD"
},
{ NULL } { NULL }
}; };
@ -274,6 +335,115 @@ clutter_backend_glx_constructor (GType gtype,
return g_object_ref (backend_singleton); return g_object_ref (backend_singleton);
} }
static gboolean
check_vblank_env (const char *name)
{
if (clutter_vblank_name && !strcasecmp(clutter_vblank_name, name))
return TRUE;
return FALSE;
}
static CoglFuncPtr
get_proc_address (const gchar* name)
{
static GLXGetProcAddressProc get_proc_func = NULL;
static void *dlhand = NULL;
if (get_proc_func == NULL && dlhand == NULL)
{
dlhand = dlopen (NULL, RTLD_LAZY);
if (dlhand)
{
dlerror ();
get_proc_func =
(GLXGetProcAddressProc) dlsym (dlhand, "glXGetProcAddress");
if (dlerror () != NULL)
{
get_proc_func =
(GLXGetProcAddressProc) dlsym (dlhand, "glXGetProcAddressARB");
}
if (dlerror () != NULL)
{
get_proc_func = NULL;
g_warning ("failed to bind GLXGetProcAddress "
"or GLXGetProcAddressARB");
}
}
}
if (get_proc_func)
return get_proc_func ((unsigned char*) name);
return NULL;
}
static ClutterFeatureFlags
clutter_backend_glx_get_features (ClutterBackend *backend)
{
ClutterBackendGlx *backend_glx = CLUTTER_BACKEND_GLX (backend);
const gchar *glx_extensions = NULL;
ClutterFeatureFlags flags = 0;
/* FIXME: we really need to check if gl context is set */
glx_extensions
= glXQueryExtensionsString (clutter_glx_get_default_display (),
clutter_glx_get_default_screen ());
if (getenv("__GL_SYNC_TO_VBLANK") || check_vblank_env ("none"))
{
CLUTTER_NOTE (MISC, "vblank sync: disabled at user request");
}
else
{
if (!check_vblank_env ("dri") &&
cogl_check_extension ("GLX_SGI_video_sync", glx_extensions))
{
backend_glx->get_video_sync =
(GetVideoSyncProc) get_proc_address ("glXGetVideoSyncSGI");
backend_glx->wait_video_sync =
(WaitVideoSyncProc) get_proc_address ("glXWaitVideoSyncSGI");
if ((backend_glx->get_video_sync != NULL) &&
(backend_glx->wait_video_sync != NULL))
{
CLUTTER_NOTE (MISC, "vblank sync: using glx");
backend_glx->vblank_type = CLUTTER_VBLANK_GLX;
flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
}
}
#ifdef __linux__
if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
{
backend_glx->dri_fd = open("/dev/dri/card0", O_RDWR);
if (backend_glx->dri_fd >= 0)
{
CLUTTER_NOTE (MISC, "vblank sync: using dri");
backend_glx->vblank_type = CLUTTER_VBLANK_DRI;
flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
}
}
#endif
if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
{
CLUTTER_NOTE (MISC,
"vblank sync: no use-able mechanism found");
}
}
CLUTTER_NOTE (MISC, "backend features checked");
return flags;
}
static void static void
clutter_backend_glx_class_init (ClutterBackendGlxClass *klass) clutter_backend_glx_class_init (ClutterBackendGlxClass *klass)
{ {
@ -290,6 +460,7 @@ clutter_backend_glx_class_init (ClutterBackendGlxClass *klass)
backend_class->init_events = clutter_backend_glx_init_events; backend_class->init_events = clutter_backend_glx_init_events;
backend_class->get_stage = clutter_backend_glx_get_stage; backend_class->get_stage = clutter_backend_glx_get_stage;
backend_class->add_options = clutter_backend_glx_add_options; backend_class->add_options = clutter_backend_glx_add_options;
backend_class->get_features = clutter_backend_glx_get_features;
} }
static void static void
@ -317,6 +488,38 @@ error_handler(Display *xdpy,
return 0; return 0;
} }
void
clutter_backend_glx_wait_for_vblank (ClutterBackendGlx *backend_glx)
{
switch (backend_glx->vblank_type)
{
case CLUTTER_VBLANK_GLX:
{
unsigned int retraceCount;
backend_glx->get_video_sync (&retraceCount);
backend_glx->wait_video_sync (2,
(retraceCount + 1) % 2,
&retraceCount);
}
break;
case CLUTTER_VBLANK_DRI:
#ifdef __linux__
{
drm_wait_vblank_t blank;
blank.request.type = DRM_VBLANK_RELATIVE;
blank.request.sequence = 1;
blank.request.signal = 0;
drm_wait_vblank (backend_glx->dri_fd, &blank);
}
#endif
break;
case CLUTTER_VBLANK_NONE:
default:
break;
}
}
/** /**
* clutter_glx_trap_x_errors: * clutter_glx_trap_x_errors:
* *
@ -378,16 +581,16 @@ clutter_glx_get_default_display (void)
* *
* Since: 0.4 * Since: 0.4
*/ */
Screen * int
clutter_glx_get_default_screen (void) clutter_glx_get_default_screen (void)
{ {
if (!backend_singleton) if (!backend_singleton)
{ {
g_critical ("GLX backend has not been initialised"); g_critical ("GLX backend has not been initialised");
return NULL; return 0;
} }
return backend_singleton->xscreen; return backend_singleton->xscreen_num;
} }
/** /**

View File

@ -42,6 +42,19 @@ G_BEGIN_DECLS
typedef struct _ClutterBackendGlx ClutterBackendGlx; typedef struct _ClutterBackendGlx ClutterBackendGlx;
typedef struct _ClutterBackendGlxClass ClutterBackendGlxClass; typedef struct _ClutterBackendGlxClass ClutterBackendGlxClass;
typedef enum ClutterGLXVBlankType
{
CLUTTER_VBLANK_NONE = 0,
CLUTTER_VBLANK_GLX,
CLUTTER_VBLANK_DRI
} ClutterGLXVBlankType;
typedef int (*GetVideoSyncProc) (unsigned int *count);
typedef int (*WaitVideoSyncProc) (int divisor,
int remainder,
unsigned int *count);
struct _ClutterBackendGlx struct _ClutterBackendGlx
{ {
ClutterBackend parent_instance; ClutterBackend parent_instance;
@ -57,6 +70,12 @@ struct _ClutterBackendGlx
/* event source */ /* event source */
GSource *event_source; GSource *event_source;
/* Vblank stuff */
GetVideoSyncProc get_video_sync;
WaitVideoSyncProc wait_video_sync;
gint dri_fd;
ClutterGLXVBlankType vblank_type;
}; };
struct _ClutterBackendGlxClass struct _ClutterBackendGlxClass
@ -67,6 +86,8 @@ struct _ClutterBackendGlxClass
void _clutter_backend_glx_events_init (ClutterBackend *backend); void _clutter_backend_glx_events_init (ClutterBackend *backend);
void _clutter_backend_glx_events_uninit (ClutterBackend *backend); void _clutter_backend_glx_events_uninit (ClutterBackend *backend);
void clutter_backend_glx_wait_for_vblank (ClutterBackendGlx *backend_glx);
GType clutter_backend_glx_get_type (void) G_GNUC_CONST; GType clutter_backend_glx_get_type (void) G_GNUC_CONST;
G_END_DECLS G_END_DECLS

View File

@ -37,7 +37,7 @@ void clutter_glx_trap_x_errors (void);
gint clutter_glx_untrap_x_errors (void); gint clutter_glx_untrap_x_errors (void);
Display *clutter_glx_get_default_display (void); Display *clutter_glx_get_default_display (void);
Screen * clutter_glx_get_default_screen (void); int clutter_glx_get_default_screen (void);
Window clutter_glx_get_root_window (void); Window clutter_glx_get_root_window (void);
Window clutter_glx_get_stage_window (ClutterStage *stage); Window clutter_glx_get_stage_window (ClutterStage *stage);
@ -46,6 +46,7 @@ XVisualInfo *clutter_glx_get_stage_visual (ClutterStage *stage);
gboolean clutter_glx_set_stage_foreign (ClutterStage *stage, gboolean clutter_glx_set_stage_foreign (ClutterStage *stage,
Window xwindow); Window xwindow);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_GLX_H__ */ #endif /* __CLUTTER_GLX_H__ */

View File

@ -331,7 +331,7 @@ clutter_stage_glx_paint (ClutterActor *self)
*/ */
if (stage_glx->xwin) if (stage_glx->xwin)
{ {
clutter_feature_wait_for_vblank (); clutter_backend_glx_wait_for_vblank (stage_glx->backend);
glXSwapBuffers (stage_glx->xdpy, stage_glx->xwin); glXSwapBuffers (stage_glx->xdpy, stage_glx->xwin);
} }
else else
@ -373,7 +373,6 @@ clutter_stage_glx_request_coords (ClutterActor *self,
ClutterStageGlx *stage_glx = CLUTTER_STAGE_GLX (self); ClutterStageGlx *stage_glx = CLUTTER_STAGE_GLX (self);
gint new_width, new_height; gint new_width, new_height;
/* FIXME: some how have X configure_notfiys call this ? */
new_width = ABS (box->x2 - box->x1); new_width = ABS (box->x2 - box->x1);
new_height = ABS (box->y2 - box->y1); new_height = ABS (box->y2 - box->y1);

View File

@ -29,6 +29,8 @@
#include <GL/glx.h> #include <GL/glx.h>
#include <GL/gl.h> #include <GL/gl.h>
#include "clutter-backend-glx.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define CLUTTER_TYPE_STAGE_GLX (clutter_stage_glx_get_type ()) #define CLUTTER_TYPE_STAGE_GLX (clutter_stage_glx_get_type ())
@ -60,6 +62,9 @@ struct _ClutterStageGlx
GLXContext gl_context; GLXContext gl_context;
guint is_foreign_xwin : 1; guint is_foreign_xwin : 1;
ClutterBackendGlx *backend;
}; };
struct _ClutterStageGlxClass struct _ClutterStageGlxClass