diff --git a/ChangeLog b/ChangeLog index ac9c3f496..1087eda80 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2007-05-16 Matthew Allum + + * 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 * clutter/clutter-actor.c: diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 221192b24..f0510d0eb 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -143,16 +143,18 @@ _clutter_backend_init_stage (ClutterBackend *backend, return TRUE; } -void -_clutter_backend_init_features (ClutterBackend *backend) +ClutterFeatureFlags +_clutter_backend_get_features (ClutterBackend *backend) { ClutterBackendClass *klass; g_return_if_fail (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); - if (klass->init_features) - klass->init_features (backend); + if (klass->get_features) + return klass->get_features (backend); + + return 0; } void diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 6a75d482f..320d63a2f 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -29,6 +29,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -66,6 +67,7 @@ struct _ClutterBackendClass ClutterActor *(* get_stage) (ClutterBackend *backend); void (* add_options) (ClutterBackend *backend, GOptionGroup *group); + ClutterFeatureFlags (* get_features) (ClutterBackend *backend); }; GType clutter_backend_get_type (void) G_GNUC_CONST; diff --git a/clutter/clutter-feature.c b/clutter/clutter-feature.c index bdf04eb12..015d6ab58 100644 --- a/clutter/clutter-feature.c +++ b/clutter/clutter-feature.c @@ -35,311 +35,48 @@ #include #include #include -#include -#include -#include -#include - -#include -#include - -#include #include "clutter-feature.h" #include "clutter-main.h" #include "clutter-private.h" #include "clutter-debug.h" -#ifdef HAVE_CLUTTER_GLX -#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; +#include "cogl.h" typedef struct ClutterFeatures { ClutterFeatureFlags flags; - ClutterFeatureFuncs funcs; - gint dri_fd; - ClutterVBlankType vblank_type; - guint features_set : 1; } ClutterFeatures; static ClutterFeatures* __features = NULL; G_LOCK_DEFINE_STATIC (__features); - -#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) +void +_clutter_feature_init (void) { - 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 - - -/* 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; + ClutterMainContext *context; CLUTTER_NOTE (MISC, "checking features"); if (!__features) { CLUTTER_NOTE (MISC, "allocating features data"); - __features = g_new0 (ClutterFeatures, 1); - memset(&__features->funcs, 0, sizeof (ClutterFeatureFuncs)); __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) return; -#ifdef HAVE_CLUTTER_GLX - glx_extensions - = glXQueryExtensionsString (clutter_glx_get_default_display (), - clutter_glx_get_default_screen ()); -#endif + context = clutter_context_get_default (); - 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)) - { - __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->flags = cogl_get_features() + |_clutter_backend_get_features (context->backend); __features->features_set = TRUE; -} -static inline void -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); - } + CLUTTER_NOTE (MISC, "features checked"); } /** @@ -356,9 +93,7 @@ clutter_feature_do_init (void) gboolean 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 clutter_feature_get_all (void) { - clutter_feature_do_init (); - 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; - } -} diff --git a/clutter/clutter-feature.h b/clutter/clutter-feature.h index 784cac6b1..be15107ef 100644 --- a/clutter/clutter-feature.h +++ b/clutter/clutter-feature.h @@ -39,13 +39,13 @@ G_BEGIN_DECLS typedef enum { - CLUTTER_FEATURE_TEXTURE_RECTANGLE = (1 << 1), - CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 2) + CLUTTER_FEATURE_TEXTURE_RECTANGLE = (1 << 1), + CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 2), + CLUTTER_FEATURE_TEXTURE_READ_PIXELS = (1 << 3) } ClutterFeatureFlags; gboolean clutter_feature_available (ClutterFeatureFlags feature); ClutterFeatureFlags clutter_feature_get_all (void); -void clutter_feature_wait_for_vblank (void); G_END_DECLS diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index bb601fec7..bfe3e9df2 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -51,7 +51,6 @@ static ClutterMainContext *ClutterCntx = NULL; static gboolean clutter_is_initialized = FALSE; static gboolean clutter_show_fps = FALSE; static gboolean clutter_fatal_warnings = FALSE; -static gchar *clutter_vblank_name = NULL; guint clutter_debug_flags = 0; /* global clutter debug flag */ @@ -85,20 +84,6 @@ clutter_get_show_fps (void) 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: @@ -330,8 +315,6 @@ clutter_arg_no_debug_cb (const char *key, static GOptionEntry clutter_args[] = { { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps, "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, "Make all warnings fatal", NULL }, #ifdef CLUTTER_ENABLE_DEBUG @@ -382,12 +365,6 @@ pre_parse_hook (GOptionContext *context, } #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"); if (env_string) @@ -555,6 +532,8 @@ clutter_init_with_args (int *argc, _clutter_backend_init_events (clutter_context->backend); + _clutter_feature_init (); + return CLUTTER_INIT_SUCCESS; } @@ -615,14 +594,21 @@ clutter_init (int *argc, g_type_init (); + /* parse_args will trigger backend creation and things like + * DISPLAY connection etc. + */ if (clutter_parse_args (argc, argv) == FALSE) { CLUTTER_NOTE (MISC, "failed to parse arguments."); 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 (); + /* Stage will give us a GL Context etc */ stage_error = NULL; if (!_clutter_backend_init_stage (context->backend, &stage_error)) { @@ -632,8 +618,12 @@ clutter_init (int *argc, return CLUTTER_INIT_ERROR_INTERNAL; } + /* Initiate event collection */ _clutter_backend_init_events (context->backend); + /* finally features - will call to backend and cogl */ + _clutter_feature_init (); + return CLUTTER_INIT_SUCCESS; } diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 003eecede..1b771fbaf 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -62,7 +62,6 @@ void clutter_redraw (void); gboolean clutter_get_debug_enabled (void); gboolean clutter_get_show_fps (void); -G_CONST_RETURN gchar *clutter_get_vblank_method (void); void clutter_threads_enter (void); void clutter_threads_leave (void); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 47d3fed2b..a1d8d4c48 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -39,6 +39,7 @@ #include "clutter-event.h" #include "clutter-backend.h" #include "clutter-stage.h" +#include "clutter-feature.h" G_BEGIN_DECLS @@ -103,12 +104,14 @@ gboolean _clutter_backend_init_stage (ClutterBackend *backend, GError **error); void _clutter_backend_init_events (ClutterBackend *backend); -void _clutter_backend_init_features (ClutterBackend *backend); +ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend); /* backend helpers */ void _clutter_event_button_generate (ClutterBackend *backend, ClutterEvent *event); +void _clutter_feature_init (void); + /* FIXME: move elsewhere via ClutterAudience */ void _clutter_stage_sync_viewport (ClutterStage *stage); diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 868a496b5..dc2a79033 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -151,9 +151,9 @@ clutter_stage_set_property (GObject *object, static void clutter_stage_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { ClutterStage *stage; ClutterStagePrivate *priv; diff --git a/clutter/cogl/cogl.h b/clutter/cogl/cogl.h index f787835aa..44b9d14a4 100644 --- a/clutter/cogl/cogl.h +++ b/clutter/cogl/cogl.h @@ -178,6 +178,8 @@ void cogl_alpha_func (COGLenum func, ClutterFixed ref); +ClutterFeatureFlags +cogl_get_features (); G_END_DECLS diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index abaa6b8dc..70ab95566 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -84,11 +84,56 @@ error_string(GLenum errorCode) #define GE(x) (x); #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 cogl_get_proc_address (const gchar* name) { - return NULL; + /* FIXME */ } gboolean @@ -527,3 +572,24 @@ cogl_setup_viewport (guint width, 1.0f / width) ); 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; +} + diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index 67483c3f5..0739a7b57 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -515,5 +515,11 @@ cogl_setup_viewport (guint w, CFX_ONE / width)); GE( glTranslatex (0, -CFX_ONE * height, 0) ); - +} + +ClutterFeatureFlags +cogl_get_features () +{ + /* Suck */ + return 0; } diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 2d8e0fc91..f9d991bce 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -29,16 +29,31 @@ #include #endif +#include +#include +#include +#include + +#include +#include + +#include + #include "clutter-backend-glx.h" #include "clutter-stage-glx.h" +#include "clutter-glx.h" #include "../clutter-event.h" #include "../clutter-main.h" #include "../clutter-debug.h" #include "../clutter-private.h" +#include "cogl.h" + G_DEFINE_TYPE (ClutterBackendGlx, clutter_backend_glx, CLUTTER_TYPE_BACKEND); +typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName); + /* singleton object */ static ClutterBackendGlx *backend_singleton = NULL; @@ -50,6 +65,56 @@ static gint clutter_screen = 0; static int TrappedErrorCode = 0; 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 clutter_backend_glx_pre_parse (ClutterBackend *backend, GError **error) @@ -66,6 +131,13 @@ clutter_backend_glx_pre_parse (ClutterBackend *backend, env_string = NULL; } + env_string = g_getenv ("CLUTTER_VBLANK"); + if (env_string) + { + clutter_vblank_name = g_strdup (env_string); + env_string = NULL; + } + return TRUE; } @@ -120,13 +192,6 @@ clutter_backend_glx_post_parse (ClutterBackend *backend, 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 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->xwin_root = backend_glx->xwin_root; stage_glx->xscreen = backend_glx->xscreen_num; + stage_glx->backend = backend_glx; CLUTTER_NOTE (MISC, "GLX stage created (display:%p, screen:%d, root:%u)", stage_glx->xdpy, @@ -166,16 +232,6 @@ clutter_backend_glx_init_stage (ClutterBackend *backend, 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; } @@ -209,6 +265,11 @@ static const GOptionEntry entries[] = G_OPTION_ARG_INT, &clutter_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 } }; @@ -274,6 +335,115 @@ clutter_backend_glx_constructor (GType gtype, 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 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->get_stage = clutter_backend_glx_get_stage; backend_class->add_options = clutter_backend_glx_add_options; + backend_class->get_features = clutter_backend_glx_get_features; } static void @@ -317,6 +488,38 @@ error_handler(Display *xdpy, 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: * @@ -378,16 +581,16 @@ clutter_glx_get_default_display (void) * * Since: 0.4 */ -Screen * +int clutter_glx_get_default_screen (void) { if (!backend_singleton) { g_critical ("GLX backend has not been initialised"); - return NULL; + return 0; } - return backend_singleton->xscreen; + return backend_singleton->xscreen_num; } /** diff --git a/clutter/glx/clutter-backend-glx.h b/clutter/glx/clutter-backend-glx.h index 947667578..707dc8791 100644 --- a/clutter/glx/clutter-backend-glx.h +++ b/clutter/glx/clutter-backend-glx.h @@ -42,6 +42,19 @@ G_BEGIN_DECLS typedef struct _ClutterBackendGlx ClutterBackendGlx; 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 { ClutterBackend parent_instance; @@ -57,6 +70,12 @@ struct _ClutterBackendGlx /* event source */ GSource *event_source; + + /* Vblank stuff */ + GetVideoSyncProc get_video_sync; + WaitVideoSyncProc wait_video_sync; + gint dri_fd; + ClutterGLXVBlankType vblank_type; }; struct _ClutterBackendGlxClass @@ -67,6 +86,8 @@ struct _ClutterBackendGlxClass void _clutter_backend_glx_events_init (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; G_END_DECLS diff --git a/clutter/glx/clutter-glx.h b/clutter/glx/clutter-glx.h index 64ceb0d15..a6e983d91 100644 --- a/clutter/glx/clutter-glx.h +++ b/clutter/glx/clutter-glx.h @@ -37,7 +37,7 @@ void clutter_glx_trap_x_errors (void); gint clutter_glx_untrap_x_errors (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_stage_window (ClutterStage *stage); @@ -46,6 +46,7 @@ XVisualInfo *clutter_glx_get_stage_visual (ClutterStage *stage); gboolean clutter_glx_set_stage_foreign (ClutterStage *stage, Window xwindow); + G_END_DECLS #endif /* __CLUTTER_GLX_H__ */ diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index b026406a3..c00de086c 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -331,7 +331,7 @@ clutter_stage_glx_paint (ClutterActor *self) */ 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); } else @@ -373,7 +373,6 @@ clutter_stage_glx_request_coords (ClutterActor *self, ClutterStageGlx *stage_glx = CLUTTER_STAGE_GLX (self); gint new_width, new_height; - /* FIXME: some how have X configure_notfiys call this ? */ new_width = ABS (box->x2 - box->x1); new_height = ABS (box->y2 - box->y1); diff --git a/clutter/glx/clutter-stage-glx.h b/clutter/glx/clutter-stage-glx.h index 44a1feafd..490e96929 100644 --- a/clutter/glx/clutter-stage-glx.h +++ b/clutter/glx/clutter-stage-glx.h @@ -29,6 +29,8 @@ #include #include +#include "clutter-backend-glx.h" + G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE_GLX (clutter_stage_glx_get_type ()) @@ -60,6 +62,9 @@ struct _ClutterStageGlx GLXContext gl_context; guint is_foreign_xwin : 1; + + ClutterBackendGlx *backend; + }; struct _ClutterStageGlxClass