include sched.h to get sched_yield() prototype
[mesa.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 /* $XFree86:$ */
29
30 #include "glheader.h"
31 #include "xf86drm.h"
32 #include "mtypes.h"
33 #include "macros.h"
34 #include "dd.h"
35 #include "vblank.h"
36 #include "xmlpool.h"
37
38
39 /****************************************************************************/
40 /**
41 * Get the current MSC refresh counter.
42 *
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.
46 *
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.
51 *
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.
56 */
57 int driGetMSC32( __DRIscreenPrivate * priv, int64_t * count )
58 {
59 drmVBlank vbl;
60 int ret;
61
62 /* Don't wait for anything. Just get the current refresh count. */
63
64 vbl.request.type = DRM_VBLANK_RELATIVE;
65 vbl.request.sequence = 0;
66
67 ret = drmWaitVBlank( priv->fd, &vbl );
68 *count = (int64_t)vbl.reply.sequence;
69
70 return ret;
71 }
72
73
74 /****************************************************************************/
75 /**
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.
82 *
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.
91 *
92 * The real solution is to provide an ioctl that uses a 64-bit count.
93 *
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
100 * reached.
101 * \param msc Buffer to hold MSC when done waiting.
102 *
103 * \return Zero on success or \c GLX_BAD_CONTEXT on failure.
104 */
105
106 int driWaitForMSC32( __DRIdrawablePrivate *priv,
107 int64_t target_msc, int64_t divisor, int64_t remainder,
108 int64_t * msc )
109 {
110 drmVBlank vbl;
111
112
113 if ( divisor != 0 ) {
114 unsigned int target = (unsigned int)target_msc;
115 unsigned int next = target;
116 unsigned int r;
117 int dont_wait = (target_msc == 0);
118
119 do {
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.
123 */
124 vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE :
125 DRM_VBLANK_ABSOLUTE;
126 vbl.request.sequence = next;
127
128 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
129 /* FIXME: This doesn't seem like the right thing to return here.
130 */
131 #ifndef _SOLO
132 return GLX_BAD_CONTEXT;
133 #else
134 return -1;
135 #endif
136 }
137
138 dont_wait = 0;
139 if (target_msc != 0 && vbl.reply.sequence == target)
140 break;
141
142 /* Assuming the wait-done test fails, the next refresh to wait for
143 * will be one that satisfies (MSC % divisor) == remainder. The
144 * value (MSC - (MSC % divisor) + remainder) is the refresh value
145 * closest to the current value that would satisfy the equation.
146 * If this refresh has already happened, we add divisor to obtain
147 * the next refresh after the current one that will satisfy it.
148 */
149 r = (vbl.reply.sequence % (unsigned int)divisor);
150 next = (vbl.reply.sequence - r + (unsigned int)remainder);
151 if (next <= vbl.reply.sequence) next += (unsigned int)divisor;
152
153 } while ( r != (unsigned int)remainder );
154 }
155 else {
156 /* If the \c divisor is zero, just wait until the MSC is greater
157 * than or equal to \c target_msc.
158 */
159
160 vbl.request.type = DRM_VBLANK_ABSOLUTE;
161 vbl.request.sequence = target_msc;
162
163 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
164 /* FIXME: This doesn't seem like the right thing to return here.
165 */
166 #ifndef _SOLO
167 return GLX_BAD_CONTEXT;
168 #else
169 return -1;
170 #endif
171 }
172 }
173
174 *msc = (target_msc & 0xffffffff00000000LL);
175 *msc |= vbl.reply.sequence;
176 if ( *msc < target_msc ) {
177 *msc += 0x0000000100000000LL;
178 }
179
180 return 0;
181 }
182
183
184 /****************************************************************************/
185 /**
186 * Gets a set of default vertical-blank-wait flags based on the internal GLX
187 * API version and several configuration options.
188 */
189
190 GLuint driGetDefaultVBlankFlags( const driOptionCache *optionCache )
191 {
192 GLuint flags = 0;
193 int vblank_mode;
194
195 flags |= (driCompareGLXAPIVersion( 20030317 ) >= 0)
196 ? VBLANK_FLAG_INTERVAL : 0;
197
198 if ( driCheckOption( optionCache, "vblank_mode", DRI_ENUM ) )
199 vblank_mode = driQueryOptioni( optionCache, "vblank_mode" );
200 else
201 vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
202
203 switch (vblank_mode) {
204 case DRI_CONF_VBLANK_NEVER:
205 flags = 0;
206 break;
207 case DRI_CONF_VBLANK_DEF_INTERVAL_0:
208 break;
209 case DRI_CONF_VBLANK_DEF_INTERVAL_1:
210 flags |= VBLANK_FLAG_THROTTLE;
211 break;
212 case DRI_CONF_VBLANK_ALWAYS_SYNC:
213 flags |= VBLANK_FLAG_SYNC;
214 break;
215 }
216
217 return flags;
218 }
219
220
221 /****************************************************************************/
222 /**
223 * Sets the default swap interval when the drawable is first bound to a
224 * direct rendering context.
225 */
226
227 void driDrawableInitVBlank( __DRIdrawablePrivate *priv, GLuint flags )
228 {
229 #ifndef _SOLO
230 if ( priv->pdraw->swap_interval == (unsigned)-1 ) {
231 priv->pdraw->swap_interval = (flags & VBLANK_FLAG_THROTTLE) != 0 ? 1 : 0;
232 }
233 #endif
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 desribing 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 running with LIBGL_THROTTLE_REFRESH"
263 " and LIBL_SYNC_REFRESH unset.\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 * Waits for the vertical blank for use with glXSwapBuffers.
278 *
279 * \param vbl_seq Vertical blank sequence number (MSC) after the last buffer
280 * swap. Updated after this wait.
281 * \param flags \c VBLANK_FLAG bits that control how long to wait.
282 * \param missed_deadline Set to \c GL_TRUE if the MSC after waiting is later
283 * than the "target" based on \c flags. The idea is that if
284 * \c missed_deadline is set, then the application is not
285 * achieving its desired framerate.
286 * \return Zero on success, -1 on error.
287 */
288
289 int
290 driWaitForVBlank( const __DRIdrawablePrivate *priv, GLuint * vbl_seq,
291 GLuint flags, GLboolean * missed_deadline )
292 {
293 drmVBlank vbl;
294 unsigned original_seq;
295 unsigned deadline;
296 unsigned interval;
297
298
299 *missed_deadline = GL_FALSE;
300 if ( (flags & (VBLANK_FLAG_INTERVAL |
301 VBLANK_FLAG_THROTTLE |
302 VBLANK_FLAG_SYNC)) == 0 ||
303 (flags & VBLANK_FLAG_NO_IRQ) != 0 ) {
304 return 0;
305 }
306
307
308 /* VBLANK_FLAG_SYNC means to wait for at least one vertical blank. If
309 * that flag is not set, do a fake wait for zero vertical blanking
310 * periods so that we can get the current MSC.
311 *
312 * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
313 * least one vertical blank since the last wait. Since do_wait modifies
314 * vbl_seq, we have to save the original value of vbl_seq for the
315 * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
316 */
317
318 original_seq = *vbl_seq;
319
320 vbl.request.sequence = ((flags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
321 vbl.request.type = DRM_VBLANK_RELATIVE;
322
323 if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
324 return -1;
325 }
326
327
328 vbl.request.type = DRM_VBLANK_ABSOLUTE;
329
330 if ( (flags & VBLANK_FLAG_INTERVAL) != 0 ) {
331 #ifndef _SOLO
332 interval = priv->pdraw->swap_interval;
333 #else
334 interval = 0;
335 #endif
336 /* this must have been initialized when the drawable was first bound
337 * to a direct rendering context. */
338 assert ( interval != (unsigned)-1 );
339 }
340 else if ( (flags & VBLANK_FLAG_THROTTLE) != 0 ) {
341 interval = 1;
342 }
343 else {
344 interval = 0;
345 }
346
347
348 /* Wait until the next vertical blank. If the interval is zero, then
349 * the deadline is one vertical blank after the previous wait.
350 */
351
352 vbl.request.sequence = original_seq + interval;
353 if ( *vbl_seq < vbl.request.sequence ) {
354 if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
355 return -1;
356 }
357 }
358
359 deadline = original_seq + ((interval == 0) ? 1 : interval);
360 *missed_deadline = ( *vbl_seq > deadline );
361
362 return 0;
363 }