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>
39 /****************************************************************************/
41 * Get the current MSC refresh counter.
43 * Stores the 64-bit count of vertical refreshes since some (arbitrary)
44 * point in time in \c count. Unless the value wraps around, which it
45 * may, it will never decrease.
47 * \warning This function is called from \c glXGetVideoSyncSGI, which expects
48 * a \c count of type \c unsigned (32-bit), and \c glXGetSyncValuesOML, which
49 * expects a \c count of type \c int64_t (signed 64-bit). The kernel ioctl
50 * currently always returns a \c sequence of type \c unsigned.
52 * \param priv Pointer to the DRI screen private struct.
53 * \param count Storage to hold MSC counter.
54 * \return Zero is returned on success. A negative errno value
55 * is returned on failure.
57 int driGetMSC32( __DRIscreenPrivate
* priv
, int64_t * count
)
62 /* Don't wait for anything. Just get the current refresh count. */
64 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
65 vbl
.request
.sequence
= 0;
67 ret
= drmWaitVBlank( priv
->fd
, &vbl
);
68 *count
= (int64_t)vbl
.reply
.sequence
;
74 /****************************************************************************/
76 * Wait for a specified refresh count. This implements most of the
77 * functionality of \c glXWaitForMscOML from the GLX_OML_sync_control spec.
78 * Waits for the \c target_msc refresh. If that has already passed, it
79 * waits until \f$(MSC \bmod divisor)\f$ is equal to \c remainder. If
80 * \c target_msc is 0, use the behavior of glXWaitVideoSyncSGI(), which
81 * omits the initial check against a target MSC value.
83 * This function is actually something of a hack. The problem is that, at
84 * the time of this writing, none of the existing DRM modules support an
85 * ioctl that returns a 64-bit count (at least not on 32-bit platforms).
86 * However, this function exists to support a GLX function that requires
87 * the use of 64-bit counts. As such, there is a little bit of ugly
88 * hackery at the end of this function to make the 32-bit count act like
89 * a 64-bit count. There are still some cases where this will break, but
90 * I believe it catches the most common cases.
92 * The real solution is to provide an ioctl that uses a 64-bit count.
94 * \param dpy Pointer to the \c Display.
95 * \param priv Pointer to the DRI drawable private.
96 * \param target_msc Desired refresh count to wait for. A value of 0
97 * means to use the glXWaitVideoSyncSGI() behavior.
98 * \param divisor MSC divisor if \c target_msc is already reached.
99 * \param remainder Desired MSC remainder if \c target_msc is already
101 * \param msc Buffer to hold MSC when done waiting.
103 * \return Zero on success or \c GLX_BAD_CONTEXT on failure.
106 int driWaitForMSC32( __DRIdrawablePrivate
*priv
,
107 int64_t target_msc
, int64_t divisor
, int64_t remainder
,
113 if ( divisor
!= 0 ) {
114 unsigned int target
= (unsigned int)target_msc
;
115 unsigned int next
= target
;
117 int dont_wait
= (target_msc
== 0);
120 /* dont_wait means we're using the glXWaitVideoSyncSGI() behavior.
121 * The first time around, just get the current count and proceed
122 * to the test for (MSC % divisor) == remainder.
124 vbl
.request
.type
= dont_wait
? DRM_VBLANK_RELATIVE
:
126 vbl
.request
.sequence
= next
;
128 if ( drmWaitVBlank( priv
->driScreenPriv
->fd
, &vbl
) != 0 ) {
129 /* FIXME: This doesn't seem like the right thing to return here.
131 return GLX_BAD_CONTEXT
;
135 if (target_msc
!= 0 && vbl
.reply
.sequence
== target
)
138 /* Assuming the wait-done test fails, the next refresh to wait for
139 * will be one that satisfies (MSC % divisor) == remainder. The
140 * value (MSC - (MSC % divisor) + remainder) is the refresh value
141 * closest to the current value that would satisfy the equation.
142 * If this refresh has already happened, we add divisor to obtain
143 * the next refresh after the current one that will satisfy it.
145 r
= (vbl
.reply
.sequence
% (unsigned int)divisor
);
146 next
= (vbl
.reply
.sequence
- r
+ (unsigned int)remainder
);
147 if (next
<= vbl
.reply
.sequence
) next
+= (unsigned int)divisor
;
149 } while ( r
!= (unsigned int)remainder
);
152 /* If the \c divisor is zero, just wait until the MSC is greater
153 * than or equal to \c target_msc.
156 vbl
.request
.type
= DRM_VBLANK_ABSOLUTE
;
157 vbl
.request
.sequence
= target_msc
;
159 if ( drmWaitVBlank( priv
->driScreenPriv
->fd
, &vbl
) != 0 ) {
160 /* FIXME: This doesn't seem like the right thing to return here.
162 return GLX_BAD_CONTEXT
;
166 *msc
= (target_msc
& 0xffffffff00000000LL
);
167 *msc
|= vbl
.reply
.sequence
;
168 if ( *msc
< target_msc
) {
169 *msc
+= 0x0000000100000000LL
;
176 /****************************************************************************/
178 * Gets a set of default vertical-blank-wait flags based on the internal GLX
179 * API version and several configuration options.
182 GLuint
driGetDefaultVBlankFlags( const driOptionCache
*optionCache
)
187 flags
|= (driCompareGLXAPIVersion( 20030317 ) >= 0)
188 ? VBLANK_FLAG_INTERVAL
: 0;
190 if ( driCheckOption( optionCache
, "vblank_mode", DRI_ENUM
) )
191 vblank_mode
= driQueryOptioni( optionCache
, "vblank_mode" );
193 vblank_mode
= DRI_CONF_VBLANK_DEF_INTERVAL_1
;
195 switch (vblank_mode
) {
196 case DRI_CONF_VBLANK_NEVER
:
199 case DRI_CONF_VBLANK_DEF_INTERVAL_0
:
201 case DRI_CONF_VBLANK_DEF_INTERVAL_1
:
202 flags
|= VBLANK_FLAG_THROTTLE
;
204 case DRI_CONF_VBLANK_ALWAYS_SYNC
:
205 flags
|= VBLANK_FLAG_SYNC
;
213 /****************************************************************************/
215 * Sets the default swap interval when the drawable is first bound to a
216 * direct rendering context.
219 void driDrawableInitVBlank( __DRIdrawablePrivate
*priv
, GLuint flags
)
221 if ( priv
->pdraw
->swap_interval
== (unsigned)-1 ) {
222 priv
->pdraw
->swap_interval
= (flags
& VBLANK_FLAG_THROTTLE
) != 0 ? 1 : 0;
227 /****************************************************************************/
229 * Wrapper to call \c drmWaitVBlank. The main purpose of this function is to
230 * wrap the error message logging. The error message should only be logged
231 * the first time the \c drmWaitVBlank fails. If \c drmWaitVBlank is
232 * successful, \c vbl_seq will be set the sequence value in the reply.
234 * \param vbl Pointer to drmVBlank packet desribing how to wait.
235 * \param vbl_seq Location to store the current refresh counter.
236 * \param fd File descriptor use to call into the DRM.
237 * \return Zero on success or -1 on failure.
240 static int do_wait( drmVBlank
* vbl
, GLuint
* vbl_seq
, int fd
)
245 ret
= drmWaitVBlank( fd
, vbl
);
247 static GLboolean first_time
= GL_TRUE
;
251 "%s: drmWaitVBlank returned %d, IRQs don't seem to be"
252 " working correctly.\nTry running with LIBGL_THROTTLE_REFRESH"
253 " and LIBL_SYNC_REFRESH unset.\n", __FUNCTION__
, ret
);
254 first_time
= GL_FALSE
;
260 *vbl_seq
= vbl
->reply
.sequence
;
265 /****************************************************************************/
267 * Waits for the vertical blank for use with glXSwapBuffers.
269 * \param vbl_seq Vertical blank sequence number (MSC) after the last buffer
270 * swap. Updated after this wait.
271 * \param flags \c VBLANK_FLAG bits that control how long to wait.
272 * \param missed_deadline Set to \c GL_TRUE if the MSC after waiting is later
273 * than the "target" based on \c flags. The idea is that if
274 * \c missed_deadline is set, then the application is not
275 * achieving its desired framerate.
276 * \return Zero on success, -1 on error.
280 driWaitForVBlank( const __DRIdrawablePrivate
*priv
, GLuint
* vbl_seq
,
281 GLuint flags
, GLboolean
* missed_deadline
)
284 unsigned original_seq
;
289 *missed_deadline
= GL_FALSE
;
290 if ( (flags
& (VBLANK_FLAG_INTERVAL
|
291 VBLANK_FLAG_THROTTLE
|
292 VBLANK_FLAG_SYNC
)) == 0 ||
293 (flags
& VBLANK_FLAG_NO_IRQ
) != 0 ) {
298 /* VBLANK_FLAG_SYNC means to wait for at least one vertical blank. If
299 * that flag is not set, do a fake wait for zero vertical blanking
300 * periods so that we can get the current MSC.
302 * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
303 * least one vertical blank since the last wait. Since do_wait modifies
304 * vbl_seq, we have to save the original value of vbl_seq for the
305 * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
308 original_seq
= *vbl_seq
;
310 vbl
.request
.sequence
= ((flags
& VBLANK_FLAG_SYNC
) != 0) ? 1 : 0;
311 vbl
.request
.type
= DRM_VBLANK_RELATIVE
;
313 if ( do_wait( & vbl
, vbl_seq
, priv
->driScreenPriv
->fd
) != 0 ) {
318 vbl
.request
.type
= DRM_VBLANK_ABSOLUTE
;
320 if ( (flags
& VBLANK_FLAG_INTERVAL
) != 0 ) {
321 interval
= priv
->pdraw
->swap_interval
;
322 /* this must have been initialized when the drawable was first bound
323 * to a direct rendering context. */
324 assert ( interval
!= (unsigned)-1 );
326 else if ( (flags
& VBLANK_FLAG_THROTTLE
) != 0 ) {
334 /* Wait until the next vertical blank. If the interval is zero, then
335 * the deadline is one vertical blank after the previous wait.
338 vbl
.request
.sequence
= original_seq
+ interval
;
339 if ( *vbl_seq
< vbl
.request
.sequence
) {
340 if ( do_wait( & vbl
, vbl_seq
, priv
->driScreenPriv
->fd
) != 0 ) {
345 deadline
= original_seq
+ ((interval
== 0) ? 1 : interval
);
346 *missed_deadline
= ( *vbl_seq
> deadline
);