1 /* -*- mode: c; c-basic-offset: 3 -*- */
3 * (c) Copyright IBM Corporation 2002
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:
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
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.
26 * Ian Romanick <idr@us.ibm.com>
38 static unsigned int msc_to_vblank(__DRIdrawablePrivate
* dPriv
, int64_t msc
)
40 return (unsigned int)(msc
- dPriv
->msc_base
+ dPriv
->vblank_base
);
43 static int64_t vblank_to_msc(__DRIdrawablePrivate
* dPriv
, unsigned int vblank
)
45 return (int64_t)(vblank
- dPriv
->vblank_base
+ dPriv
->msc_base
);
49 /****************************************************************************/
51 * Get the current MSC refresh counter.
53 * Stores the 64-bit count of vertical refreshes since some (arbitrary)
54 * point in time in \c count. Unless the value wraps around, which it
55 * may, it will never decrease for a given drawable.
57 * \warning This function is called from \c glXGetVideoSyncSGI, which expects
58 * a \c count of type \c unsigned (32-bit), and \c glXGetSyncValuesOML, which
59 * expects a \c count of type \c int64_t (signed 64-bit). The kernel ioctl
60 * currently always returns a \c sequence of type \c unsigned.
62 * \param priv Pointer to the DRI screen private struct.
63 * \param dPriv Pointer to the DRI drawable private struct
64 * \param count Storage to hold MSC counter.
65 * \return Zero is returned on success. A negative errno value
66 * is returned on failure.
68 int driDrawableGetMSC32( __DRIscreenPrivate
* priv
,
69 __DRIdrawablePrivate
* dPriv
,
75 /* Don't wait for anything. Just get the current refresh count. */
77 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
78 vbl
.request
.sequence
= 0;
79 if ( dPriv
&& dPriv
->vblFlags
& VBLANK_FLAG_SECONDARY
)
80 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
82 ret
= drmWaitVBlank( priv
->fd
, &vbl
);
85 *count
= vblank_to_msc(dPriv
, vbl
.reply
.sequence
);
87 /* Old driver (no knowledge of drawable MSC callback) */
88 *count
= vbl
.reply
.sequence
;
94 /****************************************************************************/
96 * Wait for a specified refresh count. This implements most of the
97 * functionality of \c glXWaitForMscOML from the GLX_OML_sync_control spec.
98 * Waits for the \c target_msc refresh. If that has already passed, it
99 * waits until \f$(MSC \bmod divisor)\f$ is equal to \c remainder. If
100 * \c target_msc is 0, use the behavior of glXWaitVideoSyncSGI(), which
101 * omits the initial check against a target MSC value.
103 * This function is actually something of a hack. The problem is that, at
104 * the time of this writing, none of the existing DRM modules support an
105 * ioctl that returns a 64-bit count (at least not on 32-bit platforms).
106 * However, this function exists to support a GLX function that requires
107 * the use of 64-bit counts. As such, there is a little bit of ugly
108 * hackery at the end of this function to make the 32-bit count act like
109 * a 64-bit count. There are still some cases where this will break, but
110 * I believe it catches the most common cases.
112 * The real solution is to provide an ioctl that uses a 64-bit count.
114 * \param dpy Pointer to the \c Display.
115 * \param priv Pointer to the DRI drawable private.
116 * \param target_msc Desired refresh count to wait for. A value of 0
117 * means to use the glXWaitVideoSyncSGI() behavior.
118 * \param divisor MSC divisor if \c target_msc is already reached.
119 * \param remainder Desired MSC remainder if \c target_msc is already
121 * \param msc Buffer to hold MSC when done waiting.
123 * \return Zero on success or \c GLX_BAD_CONTEXT on failure.
126 int driWaitForMSC32( __DRIdrawablePrivate
*priv
,
127 int64_t target_msc
, int64_t divisor
, int64_t remainder
,
133 if ( divisor
!= 0 ) {
134 unsigned int target
= (unsigned int)target_msc
;
135 unsigned int next
= target
;
137 int dont_wait
= (target_msc
== 0);
140 /* dont_wait means we're using the glXWaitVideoSyncSGI() behavior.
141 * The first time around, just get the current count and proceed
142 * to the test for (MSC % divisor) == remainder.
144 vbl
.request
.type
= dont_wait
? DRM_VBLANK_RELATIVE
:
146 vbl
.request
.sequence
= next
? msc_to_vblank(priv
, next
) : 0;
147 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
)
148 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
150 if ( drmWaitVBlank( priv
->driScreenPriv
->fd
, &vbl
) != 0 ) {
151 /* FIXME: This doesn't seem like the right thing to return here.
153 return GLX_BAD_CONTEXT
;
156 *msc
= vblank_to_msc(priv
, vbl
.reply
.sequence
);
159 if (target_msc
!= 0 && *msc
== target
)
162 /* Assuming the wait-done test fails, the next refresh to wait for
163 * will be one that satisfies (MSC % divisor) == remainder. The
164 * value (MSC - (MSC % divisor) + remainder) is the refresh value
165 * closest to the current value that would satisfy the equation.
166 * If this refresh has already happened, we add divisor to obtain
167 * the next refresh after the current one that will satisfy it.
169 r
= (*msc
% (unsigned int)divisor
);
170 next
= (*msc
- r
+ (unsigned int)remainder
);
171 if (next
<= *msc
) next
+= (unsigned int)divisor
;
173 } while ( r
!= (unsigned int)remainder
);
176 /* If the \c divisor is zero, just wait until the MSC is greater
177 * than or equal to \c target_msc.
180 vbl
.request
.type
= DRM_VBLANK_ABSOLUTE
;
181 vbl
.request
.sequence
= target_msc
? msc_to_vblank(priv
, target_msc
) : 0;
183 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
)
184 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
186 if ( drmWaitVBlank( priv
->driScreenPriv
->fd
, &vbl
) != 0 ) {
187 /* FIXME: This doesn't seem like the right thing to return here.
189 return GLX_BAD_CONTEXT
;
193 *msc
= vblank_to_msc(priv
, vbl
.reply
.sequence
);
195 if ( *msc
< target_msc
) {
196 *msc
+= 0x0000000100000000LL
;
203 /****************************************************************************/
205 * Gets a set of default vertical-blank-wait flags based on the internal GLX
206 * API version and several configuration options.
209 GLuint
driGetDefaultVBlankFlags( const driOptionCache
*optionCache
)
211 GLuint flags
= VBLANK_FLAG_INTERVAL
;
215 if ( driCheckOption( optionCache
, "vblank_mode", DRI_ENUM
) )
216 vblank_mode
= driQueryOptioni( optionCache
, "vblank_mode" );
218 vblank_mode
= DRI_CONF_VBLANK_DEF_INTERVAL_1
;
220 switch (vblank_mode
) {
221 case DRI_CONF_VBLANK_NEVER
:
224 case DRI_CONF_VBLANK_DEF_INTERVAL_0
:
226 case DRI_CONF_VBLANK_DEF_INTERVAL_1
:
227 flags
|= VBLANK_FLAG_THROTTLE
;
229 case DRI_CONF_VBLANK_ALWAYS_SYNC
:
230 flags
|= VBLANK_FLAG_SYNC
;
238 /****************************************************************************/
240 * Wrapper to call \c drmWaitVBlank. The main purpose of this function is to
241 * wrap the error message logging. The error message should only be logged
242 * the first time the \c drmWaitVBlank fails. If \c drmWaitVBlank is
243 * successful, \c vbl_seq will be set the sequence value in the reply.
245 * \param vbl Pointer to drmVBlank packet desribing how to wait.
246 * \param vbl_seq Location to store the current refresh counter.
247 * \param fd File descriptor use to call into the DRM.
248 * \return Zero on success or -1 on failure.
251 static int do_wait( drmVBlank
* vbl
, GLuint
* vbl_seq
, int fd
)
256 ret
= drmWaitVBlank( fd
, vbl
);
258 static GLboolean first_time
= GL_TRUE
;
262 "%s: drmWaitVBlank returned %d, IRQs don't seem to be"
263 " working correctly.\nTry adjusting the vblank_mode"
264 " configuration parameter.\n", __FUNCTION__
, ret
);
265 first_time
= GL_FALSE
;
271 *vbl_seq
= vbl
->reply
.sequence
;
276 /****************************************************************************/
278 * Returns the default swap interval of the given drawable.
282 driGetDefaultVBlankInterval( const __DRIdrawablePrivate
*priv
)
284 if ( (priv
->vblFlags
& (VBLANK_FLAG_THROTTLE
| VBLANK_FLAG_SYNC
)) != 0 ) {
293 /****************************************************************************/
295 * Sets the default swap interval when the drawable is first bound to a
296 * direct rendering context.
299 void driDrawableInitVBlank( __DRIdrawablePrivate
*priv
)
301 if ( priv
->swap_interval
== (unsigned)-1 &&
302 !( priv
->vblFlags
& VBLANK_FLAG_NO_IRQ
) ) {
303 /* Get current vertical blank sequence */
306 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
307 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
)
308 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
309 vbl
.request
.sequence
= 0;
310 do_wait( &vbl
, &priv
->vblSeq
, priv
->driScreenPriv
->fd
);
311 priv
->vblank_base
= priv
->vblSeq
;
313 priv
->swap_interval
= driGetDefaultVBlankInterval( priv
);
318 /****************************************************************************/
320 * Returns the current swap interval of the given drawable.
324 driGetVBlankInterval( const __DRIdrawablePrivate
*priv
)
326 if ( (priv
->vblFlags
& VBLANK_FLAG_INTERVAL
) != 0 ) {
327 /* this must have been initialized when the drawable was first bound
328 * to a direct rendering context. */
329 assert ( priv
->swap_interval
!= (unsigned)-1 );
331 return priv
->swap_interval
;
334 return driGetDefaultVBlankInterval( priv
);
338 /****************************************************************************/
340 * Returns the current vertical blank sequence number of the given drawable.
344 driGetCurrentVBlank( __DRIdrawablePrivate
*priv
)
348 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
349 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
) {
350 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
352 vbl
.request
.sequence
= 0;
354 (void) do_wait( &vbl
, &priv
->vblSeq
, priv
->driScreenPriv
->fd
);
358 /****************************************************************************/
360 * Waits for the vertical blank for use with glXSwapBuffers.
362 * \param missed_deadline Set to \c GL_TRUE if the MSC after waiting is later
363 * than the "target" based on \c priv->vblFlags. The idea is
364 * that if \c missed_deadline is set, then the application is
365 * not achieving its desired framerate.
366 * \return Zero on success, -1 on error.
370 driWaitForVBlank( __DRIdrawablePrivate
*priv
, GLboolean
* missed_deadline
)
373 unsigned original_seq
;
378 *missed_deadline
= GL_FALSE
;
379 if ( (priv
->vblFlags
& (VBLANK_FLAG_INTERVAL
|
380 VBLANK_FLAG_THROTTLE
|
381 VBLANK_FLAG_SYNC
)) == 0 ||
382 (priv
->vblFlags
& VBLANK_FLAG_NO_IRQ
) != 0 ) {
387 /* VBLANK_FLAG_SYNC means to wait for at least one vertical blank. If
388 * that flag is not set, do a fake wait for zero vertical blanking
389 * periods so that we can get the current MSC.
391 * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
392 * least one vertical blank since the last wait. Since do_wait modifies
393 * priv->vblSeq, we have to save the original value of priv->vblSeq for the
394 * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
397 original_seq
= priv
->vblSeq
;
398 interval
= driGetVBlankInterval(priv
);
399 deadline
= original_seq
+ interval
;
401 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
402 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
) {
403 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
405 vbl
.request
.sequence
= ((priv
->vblFlags
& VBLANK_FLAG_SYNC
) != 0) ? 1 : 0;
407 if ( do_wait( & vbl
, &priv
->vblSeq
, priv
->driScreenPriv
->fd
) != 0 ) {
411 diff
= priv
->vblSeq
- deadline
;
413 /* No need to wait again if we've already reached the target */
414 if (diff
<= (1 << 23)) {
415 *missed_deadline
= (priv
->vblFlags
& VBLANK_FLAG_SYNC
) ? (diff
> 0) :
420 /* Wait until the target vertical blank. */
421 vbl
.request
.type
= DRM_VBLANK_ABSOLUTE
;
422 if ( priv
->vblFlags
& VBLANK_FLAG_SECONDARY
) {
423 vbl
.request
.type
|= DRM_VBLANK_SECONDARY
;
425 vbl
.request
.sequence
= deadline
;
427 if ( do_wait( & vbl
, &priv
->vblSeq
, priv
->driScreenPriv
->fd
) != 0 ) {
431 diff
= priv
->vblSeq
- deadline
;
432 *missed_deadline
= diff
> 0 && diff
<= (1 << 23);