Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / mesa / drivers / dri / common / vblank.c
1 /* -*- mode: c; c-basic-offset: 3 -*- */
2 /*
3  * (c) Copyright IBM Corporation 2002
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * on the rights to use, copy, modify, merge, publish, distribute, sub
10  * license, and/or sell copies of the Software, and to permit persons to whom
11  * the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
20  * VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23  * USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * Authors:
26  *    Ian Romanick <idr@us.ibm.com>
27  */
28
29 #include "main/glheader.h"
30 #include "xf86drm.h"
31 #include "main/mtypes.h"
32 #include "main/macros.h"
33 #include "main/dd.h"
34 #include "vblank.h"
35 #include "xmlpool.h"
36
37 static unsigned int msc_to_vblank(__DRIdrawable * dPriv, int64_t msc)
38 {
39    return (unsigned int)(msc - dPriv->msc_base + dPriv->vblank_base);
40 }
41
42 static int64_t vblank_to_msc(__DRIdrawable * dPriv, unsigned int vblank)
43 {
44    return (int64_t)(vblank - dPriv->vblank_base + dPriv->msc_base);
45 }
46
47
48 /****************************************************************************/
49 /**
50  * Get the current MSC refresh counter.
51  *
52  * Stores the 64-bit count of vertical refreshes since some (arbitrary)
53  * point in time in \c count.  Unless the value wraps around, which it
54  * may, it will never decrease for a given drawable.
55  *
56  * \warning This function is called from \c glXGetVideoSyncSGI, which expects
57  * a \c count of type \c unsigned (32-bit), and \c glXGetSyncValuesOML, which 
58  * expects a \c count of type \c int64_t (signed 64-bit).  The kernel ioctl 
59  * currently always returns a \c sequence of type \c unsigned.
60  *
61  * \param priv   Pointer to the DRI screen private struct.
62  * \param dPriv  Pointer to the DRI drawable private struct
63  * \param count  Storage to hold MSC counter.
64  * \return       Zero is returned on success.  A negative errno value
65  *               is returned on failure.
66  */
67 int driDrawableGetMSC32( __DRIscreen * priv,
68                          __DRIdrawable * dPriv,
69                          int64_t * count)
70 {
71    drmVBlank vbl;
72    int ret;
73
74    /* Don't wait for anything.  Just get the current refresh count. */
75
76    vbl.request.type = DRM_VBLANK_RELATIVE;
77    vbl.request.sequence = 0;
78    if ( dPriv && dPriv->vblFlags & VBLANK_FLAG_SECONDARY )
79       vbl.request.type |= DRM_VBLANK_SECONDARY;
80
81    ret = drmWaitVBlank( priv->fd, &vbl );
82
83    if (dPriv) {
84       *count = vblank_to_msc(dPriv, vbl.reply.sequence);
85    } else {
86       /* Old driver (no knowledge of drawable MSC callback) */
87       *count = vbl.reply.sequence;
88    }
89
90    return ret;
91 }
92
93 /****************************************************************************/
94 /**
95  * Wait for a specified refresh count.  This implements most of the
96  * functionality of \c glXWaitForMscOML from the GLX_OML_sync_control spec.
97  * Waits for the \c target_msc refresh.  If that has already passed, it
98  * waits until \f$(MSC \bmod divisor)\f$ is equal to \c remainder.  If 
99  * \c target_msc is 0, use the behavior of glXWaitVideoSyncSGI(), which
100  * omits the initial check against a target MSC value.
101  * 
102  * This function is actually something of a hack.  The problem is that, at
103  * the time of this writing, none of the existing DRM modules support an
104  * ioctl that returns a 64-bit count (at least not on 32-bit platforms).
105  * However, this function exists to support a GLX function that requires
106  * the use of 64-bit counts.  As such, there is a little bit of ugly
107  * hackery at the end of this function to make the 32-bit count act like
108  * a 64-bit count.  There are still some cases where this will break, but
109  * I believe it catches the most common cases.
110  *
111  * The real solution is to provide an ioctl that uses a 64-bit count.
112  *
113  * \param dpy         Pointer to the \c Display.
114  * \param priv        Pointer to the DRI drawable private.
115  * \param target_msc  Desired refresh count to wait for.  A value of 0
116  *                    means to use the glXWaitVideoSyncSGI() behavior.
117  * \param divisor     MSC divisor if \c target_msc is already reached.
118  * \param remainder   Desired MSC remainder if \c target_msc is already
119  *                    reached.
120  * \param msc         Buffer to hold MSC when done waiting.
121  *
122  * \return            Zero on success or \c GLX_BAD_CONTEXT on failure.
123  */
124
125 int driWaitForMSC32( __DRIdrawable *priv,
126                      int64_t target_msc, int64_t divisor, int64_t remainder,
127                      int64_t * msc )
128 {
129    drmVBlank vbl;
130
131
132    if ( divisor != 0 ) {
133       int64_t next = target_msc;
134       int64_t r;
135       int dont_wait = (target_msc == 0);
136
137       do {
138          /* dont_wait means we're using the glXWaitVideoSyncSGI() behavior.
139           * The first time around, just get the current count and proceed 
140           * to the test for (MSC % divisor) == remainder.
141           */
142          vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE :
143                                         DRM_VBLANK_ABSOLUTE;
144          vbl.request.sequence = next ? msc_to_vblank(priv, next) : 0;
145          if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
146             vbl.request.type |= DRM_VBLANK_SECONDARY;
147
148          if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
149             /* FIXME: This doesn't seem like the right thing to return here.
150              */
151             return GLX_BAD_CONTEXT;
152          }
153
154          *msc = vblank_to_msc(priv, vbl.reply.sequence);
155
156          if (!dont_wait && *msc == next)
157             break;
158          dont_wait = 0;
159
160          /* Assuming the wait-done test fails, the next refresh to wait for
161           * will be one that satisfies (MSC % divisor) == remainder.  The
162           * value (MSC - (MSC % divisor) + remainder) is the refresh value 
163           * closest to the current value that would satisfy the equation.  
164           * If this refresh has already happened, we add divisor to obtain 
165           * the next refresh after the current one that will satisfy it.
166           */
167          r = ((uint64_t)*msc % divisor);
168          next = (*msc - r + remainder);
169          if (next <= *msc)
170             next += divisor;
171
172       } while (r != remainder);
173    }
174    else {
175       /* If the \c divisor is zero, just wait until the MSC is greater
176        * than or equal to \c target_msc.
177        */
178
179       vbl.request.type = DRM_VBLANK_ABSOLUTE;
180       vbl.request.sequence = target_msc ? msc_to_vblank(priv, target_msc) : 0;
181
182       if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
183          vbl.request.type |= DRM_VBLANK_SECONDARY;
184
185       if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
186          /* FIXME: This doesn't seem like the right thing to return here.
187           */
188          return GLX_BAD_CONTEXT;
189       }
190    }
191
192    *msc = vblank_to_msc(priv, vbl.reply.sequence);
193
194    if ( *msc < target_msc ) {
195       *msc += 0x0000000100000000LL;
196    }
197
198    return 0;
199 }
200
201
202 /****************************************************************************/
203 /**
204  * Gets a set of default vertical-blank-wait flags based on the internal GLX
205  * API version and several configuration options.
206  */
207
208 GLuint driGetDefaultVBlankFlags( const driOptionCache *optionCache )
209 {
210    GLuint  flags = VBLANK_FLAG_INTERVAL;
211    int vblank_mode;
212
213
214    if ( driCheckOption( optionCache, "vblank_mode", DRI_ENUM ) )
215       vblank_mode = driQueryOptioni( optionCache, "vblank_mode" );
216    else
217       vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
218
219    switch (vblank_mode) {
220    case DRI_CONF_VBLANK_NEVER:
221       flags = 0;
222       break;
223    case DRI_CONF_VBLANK_DEF_INTERVAL_0:
224       break;
225    case DRI_CONF_VBLANK_DEF_INTERVAL_1:
226       flags |= VBLANK_FLAG_THROTTLE;
227       break;
228    case DRI_CONF_VBLANK_ALWAYS_SYNC:
229       flags |= VBLANK_FLAG_SYNC;
230       break;
231    }
232
233    return flags;
234 }
235
236
237 /****************************************************************************/
238 /**
239  * Wrapper to call \c drmWaitVBlank.  The main purpose of this function is to
240  * wrap the error message logging.  The error message should only be logged
241  * the first time the \c drmWaitVBlank fails.  If \c drmWaitVBlank is
242  * successful, \c vbl_seq will be set the sequence value in the reply.
243  *
244  * \param vbl      Pointer to drmVBlank packet describing how to wait.
245  * \param vbl_seq  Location to store the current refresh counter.
246  * \param fd       File descriptor use to call into the DRM.
247  * \return         Zero on success or -1 on failure.
248  */
249
250 static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd )
251 {
252    int   ret;
253
254
255    ret = drmWaitVBlank( fd, vbl );
256    if ( ret != 0 ) {
257       static GLboolean first_time = GL_TRUE;
258
259       if ( first_time ) {
260          fprintf(stderr, 
261                  "%s: drmWaitVBlank returned %d, IRQs don't seem to be"
262                  " working correctly.\nTry adjusting the vblank_mode"
263                  " configuration parameter.\n", __FUNCTION__, ret);
264          first_time = GL_FALSE;
265       }
266
267       return -1;
268    }
269
270    *vbl_seq = vbl->reply.sequence;
271    return 0;
272 }
273
274
275 /****************************************************************************/
276 /**
277  * Returns the default swap interval of the given drawable.
278  */
279
280 static unsigned
281 driGetDefaultVBlankInterval( const  __DRIdrawable *priv )
282 {
283    if ( (priv->vblFlags & (VBLANK_FLAG_THROTTLE | VBLANK_FLAG_SYNC)) != 0 ) {
284       return 1;
285    }
286    else {
287       return 0;
288    }
289 }
290
291
292 /****************************************************************************/
293 /**
294  * Sets the default swap interval when the drawable is first bound to a
295  * direct rendering context.
296  */
297
298 void driDrawableInitVBlank( __DRIdrawable *priv )
299 {
300    if ( priv->swap_interval == (unsigned)-1 &&
301         !( priv->vblFlags & VBLANK_FLAG_NO_IRQ ) ) {
302       /* Get current vertical blank sequence */
303       drmVBlank vbl;
304  
305       vbl.request.type = DRM_VBLANK_RELATIVE;
306       if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
307          vbl.request.type |= DRM_VBLANK_SECONDARY;
308       vbl.request.sequence = 0;
309       do_wait( &vbl, &priv->vblSeq, priv->driScreenPriv->fd );
310       priv->vblank_base = priv->vblSeq;
311
312       priv->swap_interval = driGetDefaultVBlankInterval( priv );
313    }
314 }
315
316
317 /****************************************************************************/
318 /**
319  * Returns the current swap interval of the given drawable.
320  */
321
322 unsigned
323 driGetVBlankInterval( const  __DRIdrawable *priv )
324 {
325    if ( (priv->vblFlags & VBLANK_FLAG_INTERVAL) != 0 ) {
326       /* this must have been initialized when the drawable was first bound
327        * to a direct rendering context. */
328       assert ( priv->swap_interval != (unsigned)-1 );
329
330       return priv->swap_interval;
331    }
332    else 
333       return driGetDefaultVBlankInterval( priv );
334 }
335
336
337 /****************************************************************************/
338 /**
339  * Returns the current vertical blank sequence number of the given drawable.
340  */
341
342 void
343 driGetCurrentVBlank( __DRIdrawable *priv )
344 {
345    drmVBlank vbl;
346
347    vbl.request.type = DRM_VBLANK_RELATIVE;
348    if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
349       vbl.request.type |= DRM_VBLANK_SECONDARY;
350    }
351    vbl.request.sequence = 0;
352
353    (void) do_wait( &vbl, &priv->vblSeq, priv->driScreenPriv->fd );
354 }
355
356
357 /****************************************************************************/
358 /**
359  * Waits for the vertical blank for use with glXSwapBuffers.
360  * 
361  * \param missed_deadline  Set to \c GL_TRUE if the MSC after waiting is later
362  *                 than the "target" based on \c priv->vblFlags.  The idea is
363  *                 that if \c missed_deadline is set, then the application is
364  *                 not achieving its desired framerate.
365  * \return         Zero on success, -1 on error.
366  */
367
368 int
369 driWaitForVBlank( __DRIdrawable *priv, GLboolean * missed_deadline )
370 {
371    drmVBlank vbl;
372    unsigned   original_seq;
373    unsigned   deadline;
374    unsigned   interval;
375    unsigned   diff;
376
377    *missed_deadline = GL_FALSE;
378    if ( (priv->vblFlags & (VBLANK_FLAG_INTERVAL |
379                            VBLANK_FLAG_THROTTLE |
380                            VBLANK_FLAG_SYNC)) == 0 ||
381         (priv->vblFlags & VBLANK_FLAG_NO_IRQ) != 0 ) {
382       return 0;
383    }
384
385
386    /* VBLANK_FLAG_SYNC means to wait for at least one vertical blank.  If
387     * that flag is not set, do a fake wait for zero vertical blanking
388     * periods so that we can get the current MSC.
389     *
390     * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
391     * least one vertical blank since the last wait.  Since do_wait modifies
392     * priv->vblSeq, we have to save the original value of priv->vblSeq for the
393     * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
394     */
395
396    original_seq = priv->vblSeq;
397    interval = driGetVBlankInterval(priv);
398    deadline = original_seq + interval;
399
400    vbl.request.type = DRM_VBLANK_RELATIVE;
401    if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
402       vbl.request.type |= DRM_VBLANK_SECONDARY;
403    }
404    vbl.request.sequence = ((priv->vblFlags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
405
406    if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
407       return -1;
408    }
409
410    diff = priv->vblSeq - deadline;
411
412    /* No need to wait again if we've already reached the target */
413    if (diff <= (1 << 23)) {
414       *missed_deadline = (priv->vblFlags & VBLANK_FLAG_SYNC) ? (diff > 0) :
415                          GL_TRUE;
416       return 0;
417    }
418
419    /* Wait until the target vertical blank. */
420    vbl.request.type = DRM_VBLANK_ABSOLUTE;
421    if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
422       vbl.request.type |= DRM_VBLANK_SECONDARY;
423    }
424    vbl.request.sequence = deadline;
425
426    if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
427       return -1;
428    }
429
430    diff = priv->vblSeq - deadline;
431    *missed_deadline = diff > 0 && diff <= (1 << 23);
432
433    return 0;
434 }