Drop GLcontext typedef and use struct gl_context instead
[mesa.git] / src / mesa / drivers / dri / common / vblank.c
index e91dd65260cd4bb3ad635c8adcc6d7f2c8cc9971..cb98dd0b3a38a8396cb64b0d6652e610c55e700d 100644 (file)
  * Authors:
  *    Ian Romanick <idr@us.ibm.com>
  */
-/* $XFree86:$ */
 
-#include "glheader.h"
+#include "main/glheader.h"
 #include "xf86drm.h"
-#include "mtypes.h"
-#include "macros.h"
-#include "dd.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "main/dd.h"
 #include "vblank.h"
+#include "xmlpool.h"
+
+static unsigned int msc_to_vblank(__DRIdrawable * dPriv, int64_t msc)
+{
+   return (unsigned int)(msc - dPriv->msc_base + dPriv->vblank_base);
+}
+
+static int64_t vblank_to_msc(__DRIdrawable * dPriv, unsigned int vblank)
+{
+   return (int64_t)(vblank - dPriv->vblank_base + dPriv->msc_base);
+}
 
 
 /****************************************************************************/
@@ -41,7 +51,7 @@
  *
  * Stores the 64-bit count of vertical refreshes since some (arbitrary)
  * point in time in \c count.  Unless the value wraps around, which it
- * may, it will never decrease.
+ * may, it will never decrease for a given drawable.
  *
  * \warning This function is called from \c glXGetVideoSyncSGI, which expects
  * a \c count of type \c unsigned (32-bit), and \c glXGetSyncValuesOML, which 
  * currently always returns a \c sequence of type \c unsigned.
  *
  * \param priv   Pointer to the DRI screen private struct.
+ * \param dPriv  Pointer to the DRI drawable private struct
  * \param count  Storage to hold MSC counter.
  * \return       Zero is returned on success.  A negative errno value
  *               is returned on failure.
  */
-int driGetMSC32( __DRIscreenPrivate * priv, int64_t * count )
+int driDrawableGetMSC32( __DRIscreen * priv,
+                        __DRIdrawable * dPriv,
+                        int64_t * count)
 {
    drmVBlank vbl;
    int ret;
@@ -62,14 +75,21 @@ int driGetMSC32( __DRIscreenPrivate * priv, int64_t * count )
 
    vbl.request.type = DRM_VBLANK_RELATIVE;
    vbl.request.sequence = 0;
+   if ( dPriv && dPriv->vblFlags & VBLANK_FLAG_SECONDARY )
+      vbl.request.type |= DRM_VBLANK_SECONDARY;
 
    ret = drmWaitVBlank( priv->fd, &vbl );
-   *count = (int64_t)vbl.reply.sequence;
+
+   if (dPriv) {
+      *count = vblank_to_msc(dPriv, vbl.reply.sequence);
+   } else {
+      /* Old driver (no knowledge of drawable MSC callback) */
+      *count = vbl.reply.sequence;
+   }
 
    return ret;
 }
 
-
 /****************************************************************************/
 /**
  * Wait for a specified refresh count.  This implements most of the
@@ -102,7 +122,7 @@ int driGetMSC32( __DRIscreenPrivate * priv, int64_t * count )
  * \return            Zero on success or \c GLX_BAD_CONTEXT on failure.
  */
 
-int driWaitForMSC32( __DRIdrawablePrivate *priv,
+int driWaitForMSC32( __DRIdrawable *priv,
                     int64_t target_msc, int64_t divisor, int64_t remainder,
                     int64_t * msc )
 {
@@ -110,9 +130,8 @@ int driWaitForMSC32( __DRIdrawablePrivate *priv,
 
 
    if ( divisor != 0 ) {
-      unsigned int target = (unsigned int)target_msc;
-      unsigned int next = target;
-      unsigned int r;
+      int64_t next = target_msc;
+      int64_t r;
       int dont_wait = (target_msc == 0);
 
       do {
@@ -122,21 +141,21 @@ int driWaitForMSC32( __DRIdrawablePrivate *priv,
           */
          vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE :
                                         DRM_VBLANK_ABSOLUTE;
-         vbl.request.sequence = next;
+         vbl.request.sequence = next ? msc_to_vblank(priv, next) : 0;
+        if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
+           vbl.request.type |= DRM_VBLANK_SECONDARY;
 
         if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
            /* FIXME: This doesn't seem like the right thing to return here.
             */
-#ifndef _SOLO
            return GLX_BAD_CONTEXT;
-#else
-           return -1;
-#endif
         }
 
-         dont_wait = 0;
-         if (target_msc != 0 && vbl.reply.sequence == target)
+        *msc = vblank_to_msc(priv, vbl.reply.sequence);
+
+         if (!dont_wait && *msc == next)
             break;
+         dont_wait = 0;
 
          /* Assuming the wait-done test fails, the next refresh to wait for
           * will be one that satisfies (MSC % divisor) == remainder.  The
@@ -145,11 +164,12 @@ int driWaitForMSC32( __DRIdrawablePrivate *priv,
           * If this refresh has already happened, we add divisor to obtain 
           * the next refresh after the current one that will satisfy it.
           */
-         r = (vbl.reply.sequence % (unsigned int)divisor);
-         next = (vbl.reply.sequence - r + (unsigned int)remainder);
-         if (next <= vbl.reply.sequence) next += (unsigned int)divisor;
+         r = ((uint64_t)*msc % divisor);
+         next = (*msc - r + remainder);
+         if (next <= *msc)
+           next += divisor;
 
-      } while ( r != (unsigned int)remainder );
+      } while (r != remainder);
    }
    else {
       /* If the \c divisor is zero, just wait until the MSC is greater
@@ -157,21 +177,20 @@ int driWaitForMSC32( __DRIdrawablePrivate *priv,
        */
 
       vbl.request.type = DRM_VBLANK_ABSOLUTE;
-      vbl.request.sequence = target_msc;
+      vbl.request.sequence = target_msc ? msc_to_vblank(priv, target_msc) : 0;
+
+      if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
+        vbl.request.type |= DRM_VBLANK_SECONDARY;
 
       if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
         /* FIXME: This doesn't seem like the right thing to return here.
          */
-#ifndef _SOLO
-           return GLX_BAD_CONTEXT;
-#else
-           return -1;
-#endif
+        return GLX_BAD_CONTEXT;
       }
    }
 
-   *msc  = (target_msc & 0xffffffff00000000LL);
-   *msc |= vbl.reply.sequence;
+   *msc = vblank_to_msc(priv, vbl.reply.sequence);
+
    if ( *msc < target_msc ) {
       *msc += 0x0000000100000000LL;
    }
@@ -183,20 +202,33 @@ int driWaitForMSC32( __DRIdrawablePrivate *priv,
 /****************************************************************************/
 /**
  * Gets a set of default vertical-blank-wait flags based on the internal GLX
- * API version and several environment variables.
+ * API version and several configuration options.
  */
 
-GLuint driGetDefaultVBlankFlags( void )
+GLuint driGetDefaultVBlankFlags( const driOptionCache *optionCache )
 {
-   GLuint  flags = 0;
-
-
-   flags |= (driCompareGLXAPIVersion( 20030317 ) >= 0) 
-       ? VBLANK_FLAG_INTERVAL : 0;
-   flags |= (getenv("LIBGL_SYNC_REFRESH") != NULL)
-       ? VBLANK_FLAG_SYNC : 0;
-   flags |= (getenv("LIBGL_THROTTLE_REFRESH") != NULL)
-       ? VBLANK_FLAG_THROTTLE : 0;
+   GLuint  flags = VBLANK_FLAG_INTERVAL;
+   int vblank_mode;
+
+
+   if ( driCheckOption( optionCache, "vblank_mode", DRI_ENUM ) )
+      vblank_mode = driQueryOptioni( optionCache, "vblank_mode" );
+   else
+      vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
+
+   switch (vblank_mode) {
+   case DRI_CONF_VBLANK_NEVER:
+      flags = 0;
+      break;
+   case DRI_CONF_VBLANK_DEF_INTERVAL_0:
+      break;
+   case DRI_CONF_VBLANK_DEF_INTERVAL_1:
+      flags |= VBLANK_FLAG_THROTTLE;
+      break;
+   case DRI_CONF_VBLANK_ALWAYS_SYNC:
+      flags |= VBLANK_FLAG_SYNC;
+      break;
+   }
 
    return flags;
 }
@@ -209,7 +241,7 @@ GLuint driGetDefaultVBlankFlags( void )
  * the first time the \c drmWaitVBlank fails.  If \c drmWaitVBlank is
  * successful, \c vbl_seq will be set the sequence value in the reply.
  *
- * \param vbl      Pointer to drmVBlank packet desribing how to wait.
+ * \param vbl      Pointer to drmVBlank packet describing how to wait.
  * \param vbl_seq  Location to store the current refresh counter.
  * \param fd       File descriptor use to call into the DRM.
  * \return         Zero on success or -1 on failure.
@@ -227,8 +259,8 @@ static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd )
       if ( first_time ) {
         fprintf(stderr, 
                 "%s: drmWaitVBlank returned %d, IRQs don't seem to be"
-                " working correctly.\nTry running with LIBGL_THROTTLE_REFRESH"
-                " and LIBL_SYNC_REFRESH unset.\n", __FUNCTION__, ret);
+                " working correctly.\nTry adjusting the vblank_mode"
+                " configuration parameter.\n", __FUNCTION__, ret);
         first_time = GL_FALSE;
       }
 
@@ -240,32 +272,113 @@ static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd )
 }
 
 
+/****************************************************************************/
+/**
+ * Returns the default swap interval of the given drawable.
+ */
+
+static unsigned
+driGetDefaultVBlankInterval( const  __DRIdrawable *priv )
+{
+   if ( (priv->vblFlags & (VBLANK_FLAG_THROTTLE | VBLANK_FLAG_SYNC)) != 0 ) {
+      return 1;
+   }
+   else {
+      return 0;
+   }
+}
+
+
+/****************************************************************************/
+/**
+ * Sets the default swap interval when the drawable is first bound to a
+ * direct rendering context.
+ */
+
+void driDrawableInitVBlank( __DRIdrawable *priv )
+{
+   if ( priv->swap_interval == (unsigned)-1 &&
+       !( priv->vblFlags & VBLANK_FLAG_NO_IRQ ) ) {
+      /* Get current vertical blank sequence */
+      drmVBlank vbl;
+      vbl.request.type = DRM_VBLANK_RELATIVE;
+      if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
+        vbl.request.type |= DRM_VBLANK_SECONDARY;
+      vbl.request.sequence = 0;
+      do_wait( &vbl, &priv->vblSeq, priv->driScreenPriv->fd );
+      priv->vblank_base = priv->vblSeq;
+
+      priv->swap_interval = driGetDefaultVBlankInterval( priv );
+   }
+}
+
+
+/****************************************************************************/
+/**
+ * Returns the current swap interval of the given drawable.
+ */
+
+unsigned
+driGetVBlankInterval( const  __DRIdrawable *priv )
+{
+   if ( (priv->vblFlags & VBLANK_FLAG_INTERVAL) != 0 ) {
+      /* this must have been initialized when the drawable was first bound
+       * to a direct rendering context. */
+      assert ( priv->swap_interval != (unsigned)-1 );
+
+      return priv->swap_interval;
+   }
+   else 
+      return driGetDefaultVBlankInterval( priv );
+}
+
+
+/****************************************************************************/
+/**
+ * Returns the current vertical blank sequence number of the given drawable.
+ */
+
+void
+driGetCurrentVBlank( __DRIdrawable *priv )
+{
+   drmVBlank vbl;
+
+   vbl.request.type = DRM_VBLANK_RELATIVE;
+   if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
+      vbl.request.type |= DRM_VBLANK_SECONDARY;
+   }
+   vbl.request.sequence = 0;
+
+   (void) do_wait( &vbl, &priv->vblSeq, priv->driScreenPriv->fd );
+}
+
+
 /****************************************************************************/
 /**
  * Waits for the vertical blank for use with glXSwapBuffers.
  * 
- * \param vbl_seq  Vertical blank sequence number (MSC) after the last buffer
- *                 swap.  Updated after this wait.
- * \param flags    \c VBLANK_FLAG bits that control how long to wait.
  * \param missed_deadline  Set to \c GL_TRUE if the MSC after waiting is later
- *                 than the "target" based on \c flags.  The idea is that if
- *                 \c missed_deadline is set, then the application is not 
- *                 achieving its desired framerate.
+ *                 than the "target" based on \c priv->vblFlags.  The idea is
+ *                 that if \c missed_deadline is set, then the application is
+ *                 not achieving its desired framerate.
  * \return         Zero on success, -1 on error.
  */
 
 int
-driWaitForVBlank( const  __DRIdrawablePrivate *priv, GLuint * vbl_seq,
-                 GLuint flags, GLboolean * missed_deadline )
+driWaitForVBlank( __DRIdrawable *priv, GLboolean * missed_deadline )
 {
    drmVBlank vbl;
    unsigned   original_seq;
    unsigned   deadline;
    unsigned   interval;
-
+   unsigned   diff;
 
    *missed_deadline = GL_FALSE;
-   if ( (flags & VBLANK_FLAG_NO_IRQ) != 0 ) {
+   if ( (priv->vblFlags & (VBLANK_FLAG_INTERVAL |
+                          VBLANK_FLAG_THROTTLE |
+                          VBLANK_FLAG_SYNC)) == 0 ||
+       (priv->vblFlags & VBLANK_FLAG_NO_IRQ) != 0 ) {
       return 0;
    }
 
@@ -276,50 +389,46 @@ driWaitForVBlank( const  __DRIdrawablePrivate *priv, GLuint * vbl_seq,
     *
     * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
     * least one vertical blank since the last wait.  Since do_wait modifies
-    * vbl_seq, we have to save the original value of vbl_seq for the
+    * priv->vblSeq, we have to save the original value of priv->vblSeq for the
     * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
     */
 
-   original_seq = *vbl_seq;
+   original_seq = priv->vblSeq;
+   interval = driGetVBlankInterval(priv);
+   deadline = original_seq + interval;
 
-   vbl.request.sequence = ((flags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
    vbl.request.type = DRM_VBLANK_RELATIVE;
-      
-   if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
+   if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
+      vbl.request.type |= DRM_VBLANK_SECONDARY;
+   }
+   vbl.request.sequence = ((priv->vblFlags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
+
+   if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
       return -1;
    }
 
-       
-   vbl.request.type = DRM_VBLANK_ABSOLUTE;
+   diff = priv->vblSeq - deadline;
 
-   if ( (flags & VBLANK_FLAG_INTERVAL) != 0 ) {
-#ifndef _SOLO
-      interval = priv->pdraw->swap_interval;
-#else
-      interval = 0;
-#endif
-   }
-   else if ( (flags & VBLANK_FLAG_THROTTLE) != 0 ) {
-      interval = 1;
-   }
-   else {
-      interval = 0;
+   /* No need to wait again if we've already reached the target */
+   if (diff <= (1 << 23)) {
+      *missed_deadline = (priv->vblFlags & VBLANK_FLAG_SYNC) ? (diff > 0) :
+                        GL_TRUE;
+      return 0;
    }
 
+   /* Wait until the target vertical blank. */
+   vbl.request.type = DRM_VBLANK_ABSOLUTE;
+   if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
+      vbl.request.type |= DRM_VBLANK_SECONDARY;
+   }
+   vbl.request.sequence = deadline;
 
-   /* Wait until the next vertical blank.  If the interval is zero, then
-    * the deadline is one vertical blank after the previous wait.
-    */
-
-   vbl.request.sequence = original_seq + interval;
-   if ( *vbl_seq < vbl.request.sequence ) {
-      if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
-        return -1;
-      }
+   if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
+      return -1;
    }
 
-   deadline = original_seq + ((interval == 0) ? 1 : interval);
-   *missed_deadline = ( *vbl_seq > deadline );
+   diff = priv->vblSeq - deadline;
+   *missed_deadline = diff > 0 && diff <= (1 << 23);
 
    return 0;
 }