Merge branch 'r500-support'
[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 static unsigned int msc_to_vblank(__DRIdrawablePrivate * dPriv, int64_t msc)
39 {
40 return (unsigned int)(msc - dPriv->msc_base + dPriv->vblank_base);
41 }
42
43 static int64_t vblank_to_msc(__DRIdrawablePrivate * dPriv, unsigned int vblank)
44 {
45 return (int64_t)(vblank - dPriv->vblank_base + dPriv->msc_base);
46 }
47
48
49 /****************************************************************************/
50 /**
51 * Get the current MSC refresh counter.
52 *
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.
56 *
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.
61 *
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.
67 */
68 int driDrawableGetMSC32( __DRIscreenPrivate * priv,
69 __DRIdrawablePrivate * dPriv,
70 int64_t * count)
71 {
72 drmVBlank vbl;
73 int ret;
74
75 /* Don't wait for anything. Just get the current refresh count. */
76
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;
81
82 ret = drmWaitVBlank( priv->fd, &vbl );
83
84 if (dPriv) {
85 *count = vblank_to_msc(dPriv, vbl.reply.sequence);
86 } else {
87 /* Old driver (no knowledge of drawable MSC callback) */
88 *count = vbl.reply.sequence;
89 }
90
91 return ret;
92 }
93
94 /****************************************************************************/
95 /**
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.
102 *
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.
111 *
112 * The real solution is to provide an ioctl that uses a 64-bit count.
113 *
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
120 * reached.
121 * \param msc Buffer to hold MSC when done waiting.
122 *
123 * \return Zero on success or \c GLX_BAD_CONTEXT on failure.
124 */
125
126 int driWaitForMSC32( __DRIdrawablePrivate *priv,
127 int64_t target_msc, int64_t divisor, int64_t remainder,
128 int64_t * msc )
129 {
130 drmVBlank vbl;
131
132
133 if ( divisor != 0 ) {
134 unsigned int target = (unsigned int)target_msc;
135 unsigned int next = target;
136 unsigned int r;
137 int dont_wait = (target_msc == 0);
138
139 do {
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.
143 */
144 vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE :
145 DRM_VBLANK_ABSOLUTE;
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;
149
150 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
151 /* FIXME: This doesn't seem like the right thing to return here.
152 */
153 return GLX_BAD_CONTEXT;
154 }
155
156 *msc = vblank_to_msc(priv, vbl.reply.sequence);
157
158 dont_wait = 0;
159 if (target_msc != 0 && *msc == target)
160 break;
161
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.
168 */
169 r = (*msc % (unsigned int)divisor);
170 next = (*msc - r + (unsigned int)remainder);
171 if (next <= *msc) next += (unsigned int)divisor;
172
173 } while ( r != (unsigned int)remainder );
174 }
175 else {
176 /* If the \c divisor is zero, just wait until the MSC is greater
177 * than or equal to \c target_msc.
178 */
179
180 vbl.request.type = DRM_VBLANK_ABSOLUTE;
181 vbl.request.sequence = target_msc ? msc_to_vblank(priv, target_msc) : 0;
182
183 if ( priv->vblFlags & VBLANK_FLAG_SECONDARY )
184 vbl.request.type |= DRM_VBLANK_SECONDARY;
185
186 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
187 /* FIXME: This doesn't seem like the right thing to return here.
188 */
189 return GLX_BAD_CONTEXT;
190 }
191 }
192
193 *msc = vblank_to_msc(priv, vbl.reply.sequence);
194
195 if ( *msc < target_msc ) {
196 *msc += 0x0000000100000000LL;
197 }
198
199 return 0;
200 }
201
202
203 /****************************************************************************/
204 /**
205 * Gets a set of default vertical-blank-wait flags based on the internal GLX
206 * API version and several configuration options.
207 */
208
209 GLuint driGetDefaultVBlankFlags( const driOptionCache *optionCache )
210 {
211 GLuint flags = VBLANK_FLAG_INTERVAL;
212 int vblank_mode;
213
214
215 if ( driCheckOption( optionCache, "vblank_mode", DRI_ENUM ) )
216 vblank_mode = driQueryOptioni( optionCache, "vblank_mode" );
217 else
218 vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
219
220 switch (vblank_mode) {
221 case DRI_CONF_VBLANK_NEVER:
222 flags = 0;
223 break;
224 case DRI_CONF_VBLANK_DEF_INTERVAL_0:
225 break;
226 case DRI_CONF_VBLANK_DEF_INTERVAL_1:
227 flags |= VBLANK_FLAG_THROTTLE;
228 break;
229 case DRI_CONF_VBLANK_ALWAYS_SYNC:
230 flags |= VBLANK_FLAG_SYNC;
231 break;
232 }
233
234 return flags;
235 }
236
237
238 /****************************************************************************/
239 /**
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.
244 *
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.
249 */
250
251 static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd )
252 {
253 int ret;
254
255
256 ret = drmWaitVBlank( fd, vbl );
257 if ( ret != 0 ) {
258 static GLboolean first_time = GL_TRUE;
259
260 if ( first_time ) {
261 fprintf(stderr,
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;
266 }
267
268 return -1;
269 }
270
271 *vbl_seq = vbl->reply.sequence;
272 return 0;
273 }
274
275
276 /****************************************************************************/
277 /**
278 * Returns the default swap interval of the given drawable.
279 */
280
281 static unsigned
282 driGetDefaultVBlankInterval( const __DRIdrawablePrivate *priv )
283 {
284 if ( (priv->vblFlags & (VBLANK_FLAG_THROTTLE | VBLANK_FLAG_SYNC)) != 0 ) {
285 return 1;
286 }
287 else {
288 return 0;
289 }
290 }
291
292
293 /****************************************************************************/
294 /**
295 * Sets the default swap interval when the drawable is first bound to a
296 * direct rendering context.
297 */
298
299 void driDrawableInitVBlank( __DRIdrawablePrivate *priv )
300 {
301 if ( priv->swap_interval == (unsigned)-1 &&
302 !( priv->vblFlags & VBLANK_FLAG_NO_IRQ ) ) {
303 /* Get current vertical blank sequence */
304 drmVBlank vbl;
305
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;
312
313 priv->swap_interval = driGetDefaultVBlankInterval( priv );
314 }
315 }
316
317
318 /****************************************************************************/
319 /**
320 * Returns the current swap interval of the given drawable.
321 */
322
323 unsigned
324 driGetVBlankInterval( const __DRIdrawablePrivate *priv )
325 {
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 );
330
331 return priv->swap_interval;
332 }
333 else
334 return driGetDefaultVBlankInterval( priv );
335 }
336
337
338 /****************************************************************************/
339 /**
340 * Returns the current vertical blank sequence number of the given drawable.
341 */
342
343 void
344 driGetCurrentVBlank( __DRIdrawablePrivate *priv )
345 {
346 drmVBlank vbl;
347
348 vbl.request.type = DRM_VBLANK_RELATIVE;
349 if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
350 vbl.request.type |= DRM_VBLANK_SECONDARY;
351 }
352 vbl.request.sequence = 0;
353
354 (void) do_wait( &vbl, &priv->vblSeq, priv->driScreenPriv->fd );
355 }
356
357
358 /****************************************************************************/
359 /**
360 * Waits for the vertical blank for use with glXSwapBuffers.
361 *
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.
367 */
368
369 int
370 driWaitForVBlank( __DRIdrawablePrivate *priv, GLboolean * missed_deadline )
371 {
372 drmVBlank vbl;
373 unsigned original_seq;
374 unsigned deadline;
375 unsigned interval;
376 unsigned diff;
377
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 ) {
383 return 0;
384 }
385
386
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.
390 *
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.
395 */
396
397 original_seq = priv->vblSeq;
398 interval = driGetVBlankInterval(priv);
399 deadline = original_seq + interval;
400
401 vbl.request.type = DRM_VBLANK_RELATIVE;
402 if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) {
403 vbl.request.type |= DRM_VBLANK_SECONDARY;
404 }
405 vbl.request.sequence = ((priv->vblFlags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
406
407 if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
408 return -1;
409 }
410
411 diff = priv->vblSeq - deadline;
412
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) :
416 GL_TRUE;
417 return 0;
418 }
419
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;
424 }
425 vbl.request.sequence = deadline;
426
427 if ( do_wait( & vbl, &priv->vblSeq, priv->driScreenPriv->fd ) != 0 ) {
428 return -1;
429 }
430
431 diff = priv->vblSeq - deadline;
432 *missed_deadline = diff > 0 && diff <= (1 << 23);
433
434 return 0;
435 }