Пример #1
0
static void
clutter_backend_glx_dispose (GObject *gobject)
{
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (gobject);
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (gobject);

  /* Unrealize all shaders, since the GL context is going away */
  _clutter_shader_release_all ();

  if (backend_glx->gl_context)
    {
      glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL);
      glXDestroyContext (backend_x11->xdpy, backend_glx->gl_context);
      backend_glx->gl_context = None;
    }

  if (backend_glx->dummy_glxwin)
    {
      glXDestroyWindow (backend_x11->xdpy, backend_glx->dummy_glxwin);
      backend_glx->dummy_glxwin = None;
    }

  if (backend_glx->dummy_xwin)
    {
      XDestroyWindow (backend_x11->xdpy, backend_glx->dummy_xwin);
      backend_glx->dummy_xwin = None;
    }

  G_OBJECT_CLASS (clutter_backend_glx_parent_class)->dispose (gobject);
}
Пример #2
0
static gboolean
clutter_stage_glx_realize (ClutterStageWindow *stage_window)
{
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
  ClutterBackendX11 *backend_x11;
  ClutterBackendGLX *backend_glx;

  CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]",
                G_OBJECT_TYPE_NAME (stage_window),
                stage_window);

  if (!_clutter_stage_x11_create_window (stage_x11))
    return FALSE;

  backend_x11 = stage_x11->backend;
  backend_glx = CLUTTER_BACKEND_GLX (backend_x11);

  if (stage_glx->glxwin == None)
    {
      int major;
      int minor;
      GLXFBConfig config;

      /* Try and create a GLXWindow to use with extensions dependent on
       * GLX versions >= 1.3 that don't accept regular X Windows as GLX
       * drawables.
       */
      if (glXQueryVersion (backend_x11->xdpy, &major, &minor) &&
          major == 1 && minor >= 3 &&
          _clutter_backend_glx_get_fbconfig (backend_glx, &config))
        {
          stage_glx->glxwin = glXCreateWindow (backend_x11->xdpy,
                                               config,
                                               stage_x11->xwin,
                                               NULL);
        }
    }

#ifdef GLX_INTEL_swap_event
  if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
    {
      GLXDrawable drawable = stage_glx->glxwin
                           ? stage_glx->glxwin
                           : stage_x11->xwin;

      /* we unconditionally select this event because we rely on it to
       * advance the master clock, and drive redraw/relayout, animations
       * and event handling.
       */
      glXSelectEvent (backend_x11->xdpy,
                      drawable,
                      GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
    }
#endif /* GLX_INTEL_swap_event */

  /* chain up to the StageX11 implementation */
  return clutter_stage_window_parent_iface->realize (stage_window);
}
Пример #3
0
static XVisualInfo *
clutter_backend_glx_get_visual_info (ClutterBackendX11 *backend_x11)
{
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend_x11);
  GLXFBConfig config;

  if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config))
    return NULL;

  return glXGetVisualFromFBConfig (backend_x11->xdpy, config);
}
Пример #4
0
static gboolean
clutter_backend_glx_create_context (ClutterBackend  *backend,
                                    gboolean         is_offscreen,
                                    GError         **error)
{
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend);
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);

  if (backend_glx->gl_context == None)
    {
      XVisualInfo *xvisinfo;

      xvisinfo =
        clutter_backend_x11_get_visual_info (backend_x11, is_offscreen);

      CLUTTER_NOTE (GL, "Creating GL Context (display: %p, %s)",
                    backend_x11->xdpy,
                    is_offscreen ? "offscreen" : "onscreen");

      backend_glx->gl_context = glXCreateContext (backend_x11->xdpy,
                                                  xvisinfo,
                                                  0,
                                                  is_offscreen ? False : True);

      XFree (xvisinfo);

      if (backend_glx->gl_context == None)
        {
          g_set_error (error, CLUTTER_INIT_ERROR,
                       CLUTTER_INIT_ERROR_BACKEND,
                       "Unable to create suitable %s GL context",
                       is_offscreen ? "offscreen" : "onscreen");
          return FALSE;
        }

      if (!is_offscreen)
        {
          gboolean is_direct;

          is_direct = glXIsDirect (backend_x11->xdpy,
                                   backend_glx->gl_context);

          CLUTTER_NOTE (GL, "Setting %s context",
                        is_direct ? "direct" : "indirect");
          _cogl_set_indirect_context (!is_direct);
        }
    }

  return TRUE;
}
Пример #5
0
static void
clutter_backend_glx_redraw (ClutterBackend *backend,
                            ClutterStage   *stage)
{
  ClutterStageGLX *stage_glx;
  ClutterStageX11 *stage_x11;
  ClutterStageWindow *impl;

  impl = _clutter_stage_get_window (stage);
  if (G_UNLIKELY (impl == NULL))
    {
      CLUTTER_NOTE (BACKEND, "Stage [%p] has no implementation", stage);
      return;
    }

  g_assert (CLUTTER_IS_STAGE_GLX (impl));

  stage_x11 = CLUTTER_STAGE_X11 (impl);
  stage_glx = CLUTTER_STAGE_GLX (impl);

  /* this will cause the stage implementation to be painted */
  clutter_actor_paint (CLUTTER_ACTOR (stage));
  cogl_flush ();

  if (stage_x11->xwin != None)
    {
      /* wait for the next vblank */
      CLUTTER_NOTE (BACKEND, "Waiting for vblank");
      glx_wait_for_vblank (CLUTTER_BACKEND_GLX (backend));

      /* push on the screen */
      CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)",
                    stage_x11->xdpy,
                    (unsigned long) stage_x11->xwin);
      glXSwapBuffers (stage_x11->xdpy, stage_x11->xwin);
    }
  else
    {
      /* offscreen */
      glXWaitGL ();

      CLUTTER_GLERR ();
    }
}
Пример #6
0
static ClutterTranslateReturn
clutter_stage_glx_translate_event (ClutterEventTranslator *translator,
                                   gpointer                native,
                                   ClutterEvent           *event)
{
#ifdef GLX_INTEL_swap_event
  ClutterBackendGLX *backend_glx;
  XEvent *xevent = native;

  backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ());

  if (xevent->type == (backend_glx->event_base + GLX_BufferSwapComplete))
    {
      ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (translator);
      ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (translator);
      GLXBufferSwapComplete *swap_complete_event;

      swap_complete_event = (GLXBufferSwapComplete *) xevent;

      if (stage_x11->xwin == swap_complete_event->drawable)
        {
	  /* Early versions of the swap_event implementation in Mesa
	   * deliver BufferSwapComplete event when not selected for,
	   * so if we get a swap event we aren't expecting, just ignore it.
	   *
	   * https://bugs.freedesktop.org/show_bug.cgi?id=27962
	   */
          if (stage_glx->pending_swaps > 0)
            stage_glx->pending_swaps--;

          return CLUTTER_TRANSLATE_REMOVE;
        }
    }
#endif

  /* chain up to the common X11 implementation */
  return clutter_event_translator_parent_iface->translate_event (translator,
                                                                 native,
                                                                 event);
}
Пример #7
0
static gboolean
clutter_backend_glx_post_parse (ClutterBackend  *backend,
                                GError         **error)
{
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend);
  ClutterBackendClass *backend_class =
    CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class);
  int glx_major, glx_minor;

  if (!backend_class->post_parse (backend, error))
    return FALSE;

  if (!glXQueryExtension (backend_x11->xdpy,
                          &backend_glx->error_base,
                          &backend_glx->event_base))
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "XServer appears to lack required GLX support");

      return FALSE;
    }

  /* XXX: Technically we should require >= GLX 1.3 support but for a long
   * time Mesa has exported a hybrid GLX, exporting extensions specified
   * to require GLX 1.3, but still reporting 1.2 via glXQueryVersion. */
  if (!glXQueryVersion (backend_x11->xdpy, &glx_major, &glx_minor)
      || !(glx_major == 1 && glx_minor >= 2))
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "XServer appears to lack required GLX 1.2 support");
      return FALSE;
    }

  return TRUE;
}
Пример #8
0
static GObject *
clutter_backend_glx_constructor (GType                  gtype,
                                 guint                  n_params,
                                 GObjectConstructParam *params)
{
  GObjectClass *parent_class;
  GObject *retval;

  if (!backend_singleton)
    {
      parent_class = G_OBJECT_CLASS (clutter_backend_glx_parent_class);
      retval = parent_class->constructor (gtype, n_params, params);

      backend_singleton = CLUTTER_BACKEND_GLX (retval);

      return retval;
    }

  g_warning ("Attempting to create a new backend object. This should "
             "never happen, so we return the singleton instance.");

  return g_object_ref (backend_singleton);
}
Пример #9
0
/* TODO: remove this interface in favour of
 * _clutter_stage_window_make_current () */
static void
clutter_backend_glx_ensure_context (ClutterBackend *backend,
                                    ClutterStage   *stage)
{
  ClutterStageWindow *impl;

  /* if there is no stage, the stage is being destroyed or it has no
   * implementation attached to it then we clear the GL context
   */
  if (stage == NULL ||
      (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) ||
      ((impl = _clutter_stage_get_window (stage)) == NULL))
    {
      ClutterBackendX11 *backend_x11;

      backend_x11 = CLUTTER_BACKEND_X11 (backend);
      CLUTTER_NOTE (MULTISTAGE, "Clearing all context");

      glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL);
    }
  else
    {
      ClutterBackendGLX *backend_glx;
      ClutterBackendX11 *backend_x11;
      ClutterStageGLX   *stage_glx;
      ClutterStageX11   *stage_x11;
      GLXDrawable        drawable;

      g_assert (impl != NULL);

      stage_glx = CLUTTER_STAGE_GLX (impl);
      stage_x11 = CLUTTER_STAGE_X11 (impl);
      backend_glx = CLUTTER_BACKEND_GLX (backend);
      backend_x11 = CLUTTER_BACKEND_X11 (backend);

      drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin;

      CLUTTER_NOTE (BACKEND,
                    "Setting context for stage of type %s, window: 0x%x",
                    G_OBJECT_TYPE_NAME (impl),
                    (unsigned int) drawable);

      /* no GL context to set */
      if (backend_glx->gl_context == None)
        return;

      clutter_x11_trap_x_errors ();

      /* we might get here inside the final dispose cycle, so we
       * need to handle this gracefully
       */
      if (drawable == None)
        {
          GLXDrawable dummy_drawable;

          CLUTTER_NOTE (BACKEND,
                        "Received a stale stage, clearing all context");

          if (backend_glx->dummy_glxwin)
            dummy_drawable = backend_glx->dummy_glxwin;
          else
            dummy_drawable = backend_glx->dummy_xwin;

          if (dummy_drawable == None)
            glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL);
          else
            {
              glXMakeContextCurrent (backend_x11->xdpy,
                                     dummy_drawable,
                                     dummy_drawable,
                                     backend_glx->gl_context);
            }
        }
      else
        {
          CLUTTER_NOTE (BACKEND,
                        "MakeContextCurrent dpy: %p, window: 0x%x (%s), context: %p",
                        backend_x11->xdpy,
                        (unsigned int) drawable,
                        stage_x11->is_foreign_xwin ? "foreign" : "native",
                        backend_glx->gl_context);

          glXMakeContextCurrent (backend_x11->xdpy,
                                 drawable,
                                 drawable,
                                 backend_glx->gl_context);
          /*
           * In case we are using GLX_SGI_swap_control for vblank syncing we need call
           * glXSwapIntervalSGI here to make sure that it affects the current drawable.
           */
          if (backend_glx->vblank_type == CLUTTER_VBLANK_GLX_SWAP && backend_glx->swap_interval != NULL)
            backend_glx->swap_interval (1);
        }

      if (clutter_x11_untrap_x_errors ())
        g_critical ("Unable to make the stage window 0x%x the current "
                    "GLX drawable",
                    (unsigned int) drawable);
    }
}
Пример #10
0
static gboolean
clutter_backend_glx_create_context (ClutterBackend  *backend,
                                    GError         **error)
{
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend);
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
  GLXFBConfig config;
  gboolean is_direct;
  Window root_xwin;
  XSetWindowAttributes attrs;
  XVisualInfo *xvisinfo;
  Display *xdisplay;
  int major;
  int minor;
  GLXDrawable dummy_drawable;

  if (backend_glx->gl_context != None)
    return TRUE;

  xdisplay = clutter_x11_get_default_display ();
  root_xwin = clutter_x11_get_root_window ();

  if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config))
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "Unable to find suitable fbconfig for the GLX context");
      return FALSE;
    }

  CLUTTER_NOTE (BACKEND, "Creating GLX Context (display: %p)", xdisplay);

  backend_glx->gl_context = glXCreateNewContext (xdisplay,
                                                 config,
                                                 GLX_RGBA_TYPE,
                                                 NULL,
                                                 True);
  if (backend_glx->gl_context == None)
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "Unable to create suitable GL context");
      return FALSE;
    }

  is_direct = glXIsDirect (xdisplay, backend_glx->gl_context);

  CLUTTER_NOTE (GL, "Setting %s context",
                is_direct ? "direct"
                          : "indirect");
  _cogl_set_indirect_context (!is_direct);

  /* COGL assumes that there is always a GL context selected; in order
   * to make sure that a GLX context exists and is made current, we use
   * a dummy, offscreen override-redirect window to which we can always
   * fall back if no stage is available
   *
   * XXX - we need to do this dance because GLX does not allow creating
   * a context and querying it for basic information (even the function
   * pointers) unless it's made current to a real Drawable. it should be
   * possible to avoid this in future releases of Mesa and X11, but right
   * now this is the best solution available.
   */
  xvisinfo = glXGetVisualFromFBConfig (xdisplay, config);
  if (xvisinfo == None)
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "Unable to retrieve the X11 visual");
      return FALSE;
    }

  clutter_x11_trap_x_errors ();

  attrs.override_redirect = True;
  attrs.colormap = XCreateColormap (xdisplay,
                                    root_xwin,
                                    xvisinfo->visual,
                                    AllocNone);
  attrs.border_pixel = 0;

  backend_glx->dummy_xwin = XCreateWindow (xdisplay, root_xwin,
                                           -100, -100, 1, 1,
                                           0,
                                           xvisinfo->depth,
                                           CopyFromParent,
                                           xvisinfo->visual,
                                           CWOverrideRedirect | CWColormap | CWBorderPixel,
                                           &attrs);

  /* Try and create a GLXWindow to use with extensions dependent on
   * GLX versions >= 1.3 that don't accept regular X Windows as GLX
   * drawables. */
  if (glXQueryVersion (backend_x11->xdpy, &major, &minor) &&
      major == 1 && minor >= 3)
    {
      backend_glx->dummy_glxwin = glXCreateWindow (backend_x11->xdpy,
                                                   config,
                                                   backend_glx->dummy_xwin,
                                                   NULL);
    }

  if (backend_glx->dummy_glxwin)
    dummy_drawable = backend_glx->dummy_glxwin;
  else
    dummy_drawable = backend_glx->dummy_xwin;

  CLUTTER_NOTE (BACKEND, "Selecting dummy 0x%x for the GLX context",
                (unsigned int) dummy_drawable);

  glXMakeContextCurrent (xdisplay,
                         dummy_drawable,
                         dummy_drawable,
                         backend_glx->gl_context);

  XFree (xvisinfo);

  if (clutter_x11_untrap_x_errors ())
    {
      g_set_error (error, CLUTTER_INIT_ERROR,
                   CLUTTER_INIT_ERROR_BACKEND,
                   "Unable to select the newly created GLX context");
      return FALSE;
    }

  return TRUE;
}
Пример #11
0
static ClutterFeatureFlags
clutter_backend_glx_get_features (ClutterBackend *backend)
{
  ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend);
  const gchar *glx_extensions = NULL;
  const gchar *gl_extensions = NULL;
  ClutterFeatureFlags flags;
  gboolean use_dri = FALSE;

  flags = clutter_backend_x11_get_features (backend);
  flags |= CLUTTER_FEATURE_STAGE_MULTIPLE;

  /* this will make sure that the GL context exists */
  g_assert (backend_glx->gl_context != None);
  g_assert (glXGetCurrentDrawable () != None);

  CLUTTER_NOTE (BACKEND,
                "Checking features\n"
                "  GL_VENDOR: %s\n"
                "  GL_RENDERER: %s\n"
                "  GL_VERSION: %s\n"
                "  GL_EXTENSIONS: %s",
                glGetString (GL_VENDOR),
                glGetString (GL_RENDERER),
                glGetString (GL_VERSION),
                glGetString (GL_EXTENSIONS));

  glx_extensions =
    glXQueryExtensionsString (clutter_x11_get_default_display (),
                              clutter_x11_get_default_screen ());

  CLUTTER_NOTE (BACKEND, "  GLX Extensions: %s", glx_extensions);

  gl_extensions = (const gchar *)glGetString (GL_EXTENSIONS);

  /* When using glBlitFramebuffer or glXCopySubBufferMESA for sub stage
   * redraws, we cannot rely on glXSwapIntervalSGI to throttle the blits
   * so we need to resort to manually synchronizing with the vblank so we
   * always check for the video_sync extension...
   */
  if (_cogl_check_extension ("GLX_SGI_video_sync", glx_extensions) &&
      /* Note: the GLX_SGI_video_sync spec explicitly states this extension
       * only works for direct contexts. */
      glXIsDirect (clutter_x11_get_default_display (),
                   backend_glx->gl_context))
    {
      backend_glx->get_video_sync =
        (GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI");

      backend_glx->wait_video_sync =
        (WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI");
    }

  use_dri = check_vblank_env ("dri");

  /* First check for explicit disabling or it set elsewhere (eg NVIDIA) */
  if (check_vblank_env ("none"))
    {
      CLUTTER_NOTE (BACKEND, "vblank sync: disabled at user request");
      goto vblank_setup_done;
    }

  if (g_getenv ("__GL_SYNC_TO_VBLANK") != NULL)
    {
      backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP;
      flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;

      CLUTTER_NOTE (BACKEND, "Using __GL_SYNC_TO_VBLANK hint");
      goto vblank_setup_done;
    }

  /* We try two GL vblank syncing mechanisms.
   * glXSwapIntervalSGI is tried first, then glXGetVideoSyncSGI.
   *
   * glXSwapIntervalSGI is known to work with Mesa and in particular
   * the Intel drivers. glXGetVideoSyncSGI has serious problems with
   * Intel drivers causing terrible frame rate so it only tried as a
   * fallback.
   *
   * How well glXGetVideoSyncSGI works with other driver (ATI etc) needs
   * to be investigated. glXGetVideoSyncSGI on ATI at least seems to have
   * no effect.
   */
  if (!use_dri &&
      _cogl_check_extension ("GLX_SGI_swap_control", glx_extensions))
    {
      backend_glx->swap_interval =
        (SwapIntervalProc) cogl_get_proc_address ("glXSwapIntervalSGI");

      CLUTTER_NOTE (BACKEND, "attempting glXSwapIntervalSGI vblank setup");

      if (backend_glx->swap_interval != NULL &&
          backend_glx->swap_interval (1) == 0)
        {
          backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP;
          flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;

          CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI setup success");

#ifdef GLX_INTEL_swap_event
          /* GLX_INTEL_swap_event allows us to avoid blocking the CPU
           * while we wait for glXSwapBuffers to complete, and instead
           * we get an X event notifying us of completion...
           */
          if (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_SWAP_EVENTS) &&
              _cogl_check_extension ("GLX_INTEL_swap_event", glx_extensions))
            {
              flags |= CLUTTER_FEATURE_SWAP_EVENTS;
            }
#endif /* GLX_INTEL_swap_event */

          goto vblank_setup_done;
        }

      CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI vblank setup failed");
    }

  if (!use_dri &&
      !(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK) &&
      _cogl_check_extension ("GLX_SGI_video_sync", glx_extensions))
    {
      CLUTTER_NOTE (BACKEND, "attempting glXGetVideoSyncSGI vblank setup");

      if ((backend_glx->get_video_sync != NULL) &&
          (backend_glx->wait_video_sync != NULL))
        {
          CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup success");

          backend_glx->vblank_type = CLUTTER_VBLANK_GLX;
          flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;

          goto vblank_setup_done;
        }

      CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup failed");
    }

#ifdef __linux__
  /*
   * DRI is really an extreme fallback -rumoured to work with Via chipsets
   */
  if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
    {
      CLUTTER_NOTE (BACKEND, "attempting DRI vblank setup");

      backend_glx->dri_fd = open("/dev/dri/card0", O_RDWR);
      if (backend_glx->dri_fd >= 0)
        {
          CLUTTER_NOTE (BACKEND, "DRI vblank setup success");

          backend_glx->vblank_type = CLUTTER_VBLANK_DRI;
          flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;

          goto vblank_setup_done;
        }

      CLUTTER_NOTE (BACKEND, "DRI vblank setup failed");
    }
#endif /* __linux__ */

  CLUTTER_NOTE (BACKEND, "no use-able vblank mechanism found");

vblank_setup_done:

  if (_cogl_check_extension ("GLX_MESA_copy_sub_buffer", glx_extensions))
    {
      backend_glx->copy_sub_buffer =
        (CopySubBufferProc) cogl_get_proc_address ("glXCopySubBufferMESA");
      backend_glx->can_blit_sub_buffer = TRUE;
      backend_glx->blit_sub_buffer_is_synchronized = TRUE;
    }
  else if (_cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions))
    {
      CLUTTER_NOTE (BACKEND,
                    "Using glBlitFramebuffer fallback for sub_buffer copies");
      backend_glx->blit_framebuffer =
        (BlitFramebufferProc) cogl_get_proc_address ("glBlitFramebuffer");
      backend_glx->can_blit_sub_buffer = TRUE;
      backend_glx->blit_sub_buffer_is_synchronized = FALSE;
    }

  CLUTTER_NOTE (BACKEND, "backend features checked");

  return flags;
}
Пример #12
0
static void
clutter_stage_glx_redraw (ClutterStageWindow *stage_window)
{
  ClutterBackendX11 *backend_x11;
  ClutterBackendGLX *backend_glx;
  ClutterStageX11 *stage_x11;
  ClutterStageGLX *stage_glx;
  GLXDrawable drawable;
  unsigned int video_sync_count;
  gboolean may_use_clipped_redraw;
  gboolean use_clipped_redraw;

  CLUTTER_STATIC_TIMER (painting_timer,
                        "Redrawing", /* parent */
                        "Painting actors",
                        "The time spent painting actors",
                        0 /* no application private data */);
  CLUTTER_STATIC_TIMER (swapbuffers_timer,
                        "Redrawing", /* parent */
                        "glXSwapBuffers",
                        "The time spent blocked by glXSwapBuffers",
                        0 /* no application private data */);
  CLUTTER_STATIC_TIMER (blit_sub_buffer_timer,
                        "Redrawing", /* parent */
                        "glx_blit_sub_buffer",
                        "The time spent in _glx_blit_sub_buffer",
                        0 /* no application private data */);

  stage_x11 = CLUTTER_STAGE_X11 (stage_window);
  if (stage_x11->xwin == None)
    return;

  stage_glx = CLUTTER_STAGE_GLX (stage_window);

  backend_x11 = stage_x11->backend;
  backend_glx = CLUTTER_BACKEND_GLX (backend_x11);

  CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer);

  if (G_LIKELY (backend_glx->can_blit_sub_buffer) &&
      /* NB: a zero width redraw clip == full stage redraw */
      stage_glx->bounding_redraw_clip.width != 0 &&
      /* some drivers struggle to get going and produce some junk
       * frames when starting up... */
      G_LIKELY (stage_glx->frame_count > 3) &&
      /* While resizing a window clipped redraws are disabled to avoid
       * artefacts. See clutter-event-x11.c:event_translate for a
       * detailed explanation */
      G_LIKELY (stage_x11->clipped_redraws_cool_off == 0))
    {
      may_use_clipped_redraw = TRUE;
    }
  else
    may_use_clipped_redraw = FALSE;

  if (may_use_clipped_redraw &&
      G_LIKELY (!(clutter_paint_debug_flags &
                  CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
    use_clipped_redraw = TRUE;
  else
    use_clipped_redraw = FALSE;

  if (use_clipped_redraw)
    {
      CLUTTER_NOTE (CLIPPING,
                    "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
                    stage_glx->bounding_redraw_clip.x,
                    stage_glx->bounding_redraw_clip.y,
                    stage_glx->bounding_redraw_clip.width,
                    stage_glx->bounding_redraw_clip.height);
      cogl_clip_push_window_rectangle (stage_glx->bounding_redraw_clip.x,
                                       stage_glx->bounding_redraw_clip.y,
                                       stage_glx->bounding_redraw_clip.width,
                                       stage_glx->bounding_redraw_clip.height);
      _clutter_stage_do_paint (stage_x11->wrapper,
                               &stage_glx->bounding_redraw_clip);
      cogl_clip_pop ();
    }
  else
    {
      CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n");
      _clutter_stage_do_paint (stage_x11->wrapper, NULL);
    }

  if (may_use_clipped_redraw &&
      G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
    {
      static CoglMaterial *outline = NULL;
      ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
      ClutterActor *actor = CLUTTER_ACTOR (stage_x11->wrapper);
      CoglHandle vbo;
      float x_1 = clip->x;
      float x_2 = clip->x + clip->width;
      float y_1 = clip->y;
      float y_2 = clip->y + clip->height;
      float quad[8] = {
        x_1, y_1,
        x_2, y_1,
        x_2, y_2,
        x_1, y_2
      };
      CoglMatrix modelview;

      if (outline == NULL)
        {
          outline = cogl_material_new ();
          cogl_material_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
        }

      vbo = cogl_vertex_buffer_new (4);
      cogl_vertex_buffer_add (vbo,
                              "gl_Vertex",
                              2, /* n_components */
                              COGL_ATTRIBUTE_TYPE_FLOAT,
                              FALSE, /* normalized */
                              0, /* stride */
                              quad);
      cogl_vertex_buffer_submit (vbo);

      cogl_push_matrix ();
      cogl_matrix_init_identity (&modelview);
      _clutter_actor_apply_modelview_transform (actor, &modelview);
      cogl_set_modelview_matrix (&modelview);
      cogl_set_source (outline);
      cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINE_LOOP,
                               0 , 4);
      cogl_pop_matrix ();
      cogl_object_unref (vbo);
    }

  cogl_flush ();
  CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);

  drawable = stage_glx->glxwin
           ? stage_glx->glxwin
           : stage_x11->xwin;

  /* If we might ever use _clutter_backend_glx_blit_sub_buffer then we
   * always need to keep track of the video_sync_count so that we can
   * throttle blits.
   *
   * Note: we get the count *before* we issue any glXCopySubBuffer or
   * blit_sub_buffer request in case the count would go up before
   * returning control to us.
   */
  if (backend_glx->can_blit_sub_buffer && backend_glx->get_video_sync)
    backend_glx->get_video_sync (&video_sync_count);

  /* push on the screen */
  if (use_clipped_redraw)
    {
      ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
      ClutterGeometry copy_area;
      ClutterActor *actor;

      CLUTTER_NOTE (BACKEND,
                    "_glx_blit_sub_buffer (window: 0x%lx, "
                                          "x: %d, y: %d, "
                                          "width: %d, height: %d)",
                    (unsigned long) drawable,
                    stage_glx->bounding_redraw_clip.x,
                    stage_glx->bounding_redraw_clip.y,
                    stage_glx->bounding_redraw_clip.width,
                    stage_glx->bounding_redraw_clip.height);

      /* XXX: It seems there will be a race here in that the stage
       * window may be resized before glXCopySubBufferMESA is handled
       * and so we may copy the wrong region. I can't really see how
       * we can handle this with the current state of X but at least
       * in this case a full redraw should be queued by the resize
       * anyway so it should only exhibit temporary artefacts.
       */
      actor = CLUTTER_ACTOR (stage_x11->wrapper);
      copy_area.y = clutter_actor_get_height (actor)
                  - clip->y
                  - clip->height;
      copy_area.x = clip->x;
      copy_area.width = clip->width;
      copy_area.height = clip->height;

      /* glXCopySubBufferMESA and glBlitFramebuffer are not integrated
       * with the glXSwapIntervalSGI mechanism which we usually use to
       * throttle the Clutter framerate to the vertical refresh and so
       * we have to manually wait for the vblank period...
       */

      /* Here 'is_synchronized' only means that the blit won't cause a
       * tear, ie it won't prevent multiple blits per retrace if they
       * can all be performed in the blanking period. If that's the
       * case then we still want to use the vblank sync menchanism but
       * we only need it to throttle redraws.
       */
      if (!backend_glx->blit_sub_buffer_is_synchronized)
        {
          /* XXX: note that glXCopySubBuffer, at least for Intel, is
           * synchronized with the vblank but glBlitFramebuffer may
           * not be so we use the same scheme we do when calling
           * glXSwapBuffers without the swap_control extension and
           * call glFinish () before waiting for the vblank period.
           *
           * See where we call glXSwapBuffers for more details.
           */
          glFinish ();
          wait_for_vblank (backend_glx);
        }
      else if (backend_glx->get_video_sync)
        {
          /* If we have the GLX_SGI_video_sync extension then we can
           * be a bit smarter about how we throttle blits by avoiding
           * any waits if we can see that the video sync count has
           * already progressed. */
          if (backend_glx->last_video_sync_count == video_sync_count)
            wait_for_vblank (backend_glx);
        }
      else
        wait_for_vblank (backend_glx);

      CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer);
      _clutter_backend_glx_blit_sub_buffer (backend_glx,
                                            drawable,
                                            copy_area.x,
                                            copy_area.y,
                                            copy_area.width,
                                            copy_area.height);
      CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer);
    }
  else
    {
      CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)",
                    backend_x11->xdpy,
                    (unsigned long) drawable);

      /* If we have GLX swap buffer events then glXSwapBuffers will return
       * immediately and we need to track that there is a swap in
       * progress... */
      if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
        stage_glx->pending_swaps++;

      if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP &&
          backend_glx->vblank_type != CLUTTER_VBLANK_NONE)
        {
          /* If we are going to wait for VBLANK manually, we not only
           * need to flush out pending drawing to the GPU before we
           * sleep, we need to wait for it to finish. Otherwise, we
           * may end up with the situation:
           *
           *        - We finish drawing      - GPU drawing continues
           *        - We go to sleep         - GPU drawing continues
           * VBLANK - We call glXSwapBuffers - GPU drawing continues
           *                                 - GPU drawing continues
           *                                 - Swap buffers happens
           *
           * Producing a tear. Calling glFinish() first will cause us
           * to properly wait for the next VBLANK before we swap. This
           * obviously does not happen when we use _GLX_SWAP and let
           * the driver do the right thing
           */
          glFinish ();

          wait_for_vblank (backend_glx);
        }

      CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
      glXSwapBuffers (backend_x11->xdpy, drawable);
      CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);

      _cogl_swap_buffers_notify ();
    }

  backend_glx->last_video_sync_count = video_sync_count;

  /* reset the redraw clipping for the next paint... */
  stage_glx->initialized_redraw_clip = FALSE;

  stage_glx->frame_count++;
}
Пример #13
0
static gboolean
clutter_stage_glx_realize (ClutterStageWindow *stage_window)
{
  ClutterStageX11   *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
  ClutterStageGLX   *stage_glx = CLUTTER_STAGE_GLX (stage_window);
  ClutterBackend    *backend;
  ClutterBackendGLX *backend_glx;
  ClutterBackendX11 *backend_x11;
  GError            *error;

  CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]",
                G_OBJECT_TYPE_NAME (stage_window),
                stage_window);

  backend     = clutter_get_default_backend ();
  backend_glx = CLUTTER_BACKEND_GLX (backend);
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

  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_glx->glxwin == None)
    {
      int major;
      int minor;
      GLXFBConfig config;

      /* Try and create a GLXWindow to use with extensions dependent on
       * GLX versions >= 1.3 that don't accept regular X Windows as GLX
       * drawables. */
      if (glXQueryVersion (backend_x11->xdpy, &major, &minor) &&
          major == 1 && minor >= 3 &&
          _clutter_backend_glx_get_fbconfig (backend_glx, &config))
        {
          stage_glx->glxwin = glXCreateWindow (backend_x11->xdpy,
                                               config,
                                               stage_x11->xwin,
                                               NULL);
        }
    }

  if (clutter_x11_has_event_retrieval ())
    {
      if (clutter_x11_has_xinput ())
        {
          XSelectInput (backend_x11->xdpy, stage_x11->xwin,
                        StructureNotifyMask |
                        FocusChangeMask |
                        ExposureMask |
                        KeyPressMask | KeyReleaseMask |
                        EnterWindowMask | LeaveWindowMask |
                        PropertyChangeMask);
#ifdef HAVE_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);

#ifdef GLX_INTEL_swap_event
      if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
        {
          GLXDrawable drawable =
            stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin;
          glXSelectEvent (backend_x11->xdpy,
                          drawable,
                          GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
        }
#endif /* GLX_INTEL_swap_event */
    }

  /* 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);

  /* ask for a context; a no-op, if a context already exists */
  error = NULL;
  _clutter_backend_create_context (backend, &error);
  if (error)
    {
      g_critical ("Unable to realize stage: %s", error->message);
      g_error_free (error);
      return FALSE;
    }

  CLUTTER_NOTE (BACKEND, "Successfully realized stage");

  /* chain up to the StageX11 implementation */
  return clutter_stage_glx_parent_iface->realize (stage_window);
}
static void
clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture)
{
  ClutterGLXTexturePixmapPrivate *priv = texture->priv;
  GLXPixmap                       glx_pixmap = None;
  int                             attribs[7], i = 0, mipmap = 0;
  GLXFBConfig                    *fbconfig;
  Display                        *dpy;
  guint                           depth;
  Pixmap                          pixmap;
  guint				  pixmap_width, pixmap_height;
  ClutterBackendGLX              *backend_glx;
  ClutterTextureQuality           quality;

  CLUTTER_NOTE (TEXTURE, "Creating GLXPixmap");

  backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend ());

  dpy = clutter_x11_get_default_display ();

  if (priv->use_fallback == TRUE
      || !clutter_glx_texture_pixmap_using_extension (texture))
    goto cleanup;

  priv->use_fallback = FALSE;

  g_object_get (texture,
                "pixmap-width",  &pixmap_width,
                "pixmap-height", &pixmap_height,
                "pixmap-depth",  &depth,
                "pixmap",        &pixmap,
                NULL);

  if (!pixmap)
    {
      goto cleanup;
    }

  fbconfig = get_fbconfig_for_depth (depth);

  if (!fbconfig)
    {
      g_warning ("Could not find an FBConfig for selected pixmap");
      goto cleanup;
    }

  attribs[i++] = GLX_TEXTURE_FORMAT_EXT;

  if (depth == 24)
    {
      attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT;
    }
  else if (depth == 32)
    {
      attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
    }
  else
    {
      g_warning ("Pixmap with depth bellow 24 are not supported");
      goto cleanup;
    }

  quality = clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture));

  if (quality == CLUTTER_TEXTURE_QUALITY_HIGH)
    mipmap = 1;

  attribs[i++] = GLX_MIPMAP_TEXTURE_EXT;
  attribs[i++] = mipmap;

  attribs[i++] = GLX_TEXTURE_TARGET_EXT;

  attribs[i++] = GLX_TEXTURE_2D_EXT;

  attribs[i++] = None;

  clutter_x11_trap_x_errors ();
  glx_pixmap = glXCreatePixmap (dpy,
                                *fbconfig,
                                pixmap,
                                attribs);
  XSync (dpy, FALSE);
  if (clutter_x11_untrap_x_errors ())
    {
      CLUTTER_NOTE (TEXTURE, "Failed to create GLXPixmap");

      /* Make sure we don't think the call actually succeeded */
      glx_pixmap = None;
    }

  g_free (fbconfig);

 cleanup:

  if (priv->glx_pixmap)
    clutter_glx_texture_pixmap_free_glx_pixmap (texture);

  if (glx_pixmap != None)
    {
      priv->glx_pixmap = glx_pixmap;

      create_cogl_texture (CLUTTER_TEXTURE (texture), pixmap_width, pixmap_height);

      CLUTTER_NOTE (TEXTURE, "Created GLXPixmap");

      return;
    }
  else
    {
      priv->use_fallback = TRUE;
      priv->glx_pixmap   = None;

      /* Some fucky logic here - we've fallen back and need to make sure
       * we realize here..
      */
      clutter_actor_realize (CLUTTER_ACTOR (texture));
    }
}
Пример #15
0
static void
clutter_backend_glx_ensure_context (ClutterBackend *backend, 
                                    ClutterStage   *stage)
{
  ClutterStageWindow *impl;

  /* if there is no stage, the stage is being destroyed or it has no
   * implementation attached to it then we clear the GL context
   */
  if (stage == NULL ||
      (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) ||
      ((impl = _clutter_stage_get_window (stage)) == NULL))
    {
      ClutterBackendX11 *backend_x11;

      backend_x11 = CLUTTER_BACKEND_X11 (backend);
      CLUTTER_NOTE (MULTISTAGE, "Clearing all context");

      glXMakeCurrent (backend_x11->xdpy, None, NULL);
    }
  else
    {
      ClutterBackendGLX *backend_glx;
      ClutterStageGLX   *stage_glx;
      ClutterStageX11   *stage_x11;

      g_assert (impl != NULL);

      CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]",
                    g_type_name (G_OBJECT_TYPE (impl)),
                    impl);

      stage_glx = CLUTTER_STAGE_GLX (impl);
      stage_x11 = CLUTTER_STAGE_X11 (impl);
      backend_glx = CLUTTER_BACKEND_GLX (backend);

      /* no GL context to set */
      if (backend_glx->gl_context == None)
        return;

      clutter_x11_trap_x_errors ();

      /* we might get here inside the final dispose cycle, so we
       * need to handle this gracefully
       */
      if (stage_x11->xwin == None)
        {
          ClutterBackendX11 *backend_x11;

          backend_x11 = CLUTTER_BACKEND_X11 (backend);
          CLUTTER_NOTE (MULTISTAGE,
                        "Received a stale stage, clearing all context");

          glXMakeCurrent (backend_x11->xdpy, None, NULL);
        }
      else
        {
          CLUTTER_NOTE (BACKEND,
                        "MakeCurrent dpy: %p, window: 0x%x (%s), context: %p",
                        stage_x11->xdpy,
                        (int) stage_x11->xwin,
                        stage_x11->is_foreign_xwin ? "foreign" : "native",
                        backend_glx->gl_context);

          glXMakeCurrent (stage_x11->xdpy,
                          stage_x11->xwin,
                          backend_glx->gl_context);
        }

      if (clutter_x11_untrap_x_errors ())
        g_critical ("Unable to make the stage window 0x%x the current "
                    "GLX drawable",
                    (int) stage_x11->xwin);
    }
}
Пример #16
0
static ClutterFeatureFlags
clutter_backend_glx_get_features (ClutterBackend *backend)
{
  ClutterBackendGLX  *backend_glx = CLUTTER_BACKEND_GLX (backend);
  const gchar        *glx_extensions = NULL;
  ClutterFeatureFlags flags;
  
  flags = clutter_backend_x11_get_features (backend);
  flags |= CLUTTER_FEATURE_STAGE_MULTIPLE;

  /* this will make sure that the GL context exists and
   * it's bound to a drawable
   */
  g_assert (backend_glx->gl_context != None);
  g_assert (glXGetCurrentDrawable () != None);

  CLUTTER_NOTE (BACKEND, "Checking features\n"
                "GL_VENDOR: %s\n"
                "GL_RENDERER: %s\n"
                "GL_VERSION: %s\n"
                "GL_EXTENSIONS: %s\n",
                glGetString (GL_VENDOR),
                glGetString (GL_RENDERER),
                glGetString (GL_VERSION),
                glGetString (GL_EXTENSIONS));

  glx_extensions = 
    glXQueryExtensionsString (clutter_x11_get_default_display (),
			      clutter_x11_get_default_screen ());

  CLUTTER_NOTE (BACKEND, "GLX Extensions: %s", glx_extensions);

  /* First check for explicit disabling or it set elsewhere (eg NVIDIA) */
  if (getenv("__GL_SYNC_TO_VBLANK") || check_vblank_env ("none"))
    {
      CLUTTER_NOTE (BACKEND, "vblank sync: disabled at user request");
    }
  else
    {
      /* We try two GL vblank syncing mechanisms.  
       * glXSwapIntervalSGI is tried first, then glXGetVideoSyncSGI.
       *
       * glXSwapIntervalSGI is known to work with Mesa and in particular
       * the Intel drivers. glXGetVideoSyncSGI has serious problems with
       * Intel drivers causing terrible frame rate so it only tried as a
       * fallback.
       *
       * How well glXGetVideoSyncSGI works with other driver (ATI etc) needs
       * to be investigated. glXGetVideoSyncSGI on ATI at least seems to have
       * no effect.
      */
      if (!check_vblank_env ("dri") && 
          cogl_check_extension ("GLX_SGI_swap_control", glx_extensions))
        {
          backend_glx->swap_interval = 
            (SwapIntervalProc) cogl_get_proc_address ("glXSwapIntervalSGI");

          CLUTTER_NOTE (BACKEND, "attempting glXSwapIntervalSGI vblank setup");

          if (backend_glx->swap_interval != NULL)
            {
              if (backend_glx->swap_interval (1) == 0)
                {
                  backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP;
                  flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
                  CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI setup success");
                }
            }

          if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
            CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI vblank setup failed");
        }

      if (!check_vblank_env ("dri") && 
          !(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK) &&
          cogl_check_extension ("GLX_SGI_video_sync", glx_extensions))
        {
          CLUTTER_NOTE (BACKEND, "attempting glXGetVideoSyncSGI vblank setup");

          backend_glx->get_video_sync = 
            (GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI");

          backend_glx->wait_video_sync = 
            (WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI");

          if ((backend_glx->get_video_sync != NULL) &&
              (backend_glx->wait_video_sync != NULL))
            {
              CLUTTER_NOTE (BACKEND, 
                            "glXGetVideoSyncSGI vblank setup success");

              backend_glx->vblank_type = CLUTTER_VBLANK_GLX;
              flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
            }

          if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
            CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup failed");
        }
#ifdef __linux__
      /* 
       * DRI is really an extreme fallback -rumoured to work with Via chipsets
      */
      if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
        {
          CLUTTER_NOTE (BACKEND, "attempting DRI vblank setup");
          backend_glx->dri_fd = open("/dev/dri/card0", O_RDWR);
          if (backend_glx->dri_fd >= 0)
            {
              CLUTTER_NOTE (BACKEND, "DRI vblank setup success");
              backend_glx->vblank_type = CLUTTER_VBLANK_DRI;
              flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
            }

          if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
            CLUTTER_NOTE (BACKEND, "DRI vblank setup failed");
        }
#endif
      if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK))
        {
          CLUTTER_NOTE (BACKEND,
                        "no use-able vblank mechanism found");
        }
    }

  CLUTTER_NOTE (MISC, "backend features checked");

  return flags;
}