Remove CVS keywords.
[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
29 #include "glheader.h"
30 #include "xf86drm.h"
31 #include "mtypes.h"
32 #include "macros.h"
33 #include "dd.h"
34 #include "vblank.h"
35 #include "xmlpool.h"
36
37
38 /****************************************************************************/
39 /**
40 * Get the current MSC refresh counter.
41 *
42 * Stores the 64-bit count of vertical refreshes since some (arbitrary)
43 * point in time in \c count. Unless the value wraps around, which it
44 * may, it will never decrease.
45 *
46 * \warning This function is called from \c glXGetVideoSyncSGI, which expects
47 * a \c count of type \c unsigned (32-bit), and \c glXGetSyncValuesOML, which
48 * expects a \c count of type \c int64_t (signed 64-bit). The kernel ioctl
49 * currently always returns a \c sequence of type \c unsigned.
50 *
51 * \param priv Pointer to the DRI screen private struct.
52 * \param count Storage to hold MSC counter.
53 * \return Zero is returned on success. A negative errno value
54 * is returned on failure.
55 */
56 int driGetMSC32( __DRIscreenPrivate * priv, int64_t * count )
57 {
58 drmVBlank vbl;
59 int ret;
60
61 /* Don't wait for anything. Just get the current refresh count. */
62
63 vbl.request.type = DRM_VBLANK_RELATIVE;
64 vbl.request.sequence = 0;
65
66 ret = drmWaitVBlank( priv->fd, &vbl );
67 *count = (int64_t)vbl.reply.sequence;
68
69 return ret;
70 }
71
72
73 /****************************************************************************/
74 /**
75 * Wait for a specified refresh count. This implements most of the
76 * functionality of \c glXWaitForMscOML from the GLX_OML_sync_control spec.
77 * Waits for the \c target_msc refresh. If that has already passed, it
78 * waits until \f$(MSC \bmod divisor)\f$ is equal to \c remainder. If
79 * \c target_msc is 0, use the behavior of glXWaitVideoSyncSGI(), which
80 * omits the initial check against a target MSC value.
81 *
82 * This function is actually something of a hack. The problem is that, at
83 * the time of this writing, none of the existing DRM modules support an
84 * ioctl that returns a 64-bit count (at least not on 32-bit platforms).
85 * However, this function exists to support a GLX function that requires
86 * the use of 64-bit counts. As such, there is a little bit of ugly
87 * hackery at the end of this function to make the 32-bit count act like
88 * a 64-bit count. There are still some cases where this will break, but
89 * I believe it catches the most common cases.
90 *
91 * The real solution is to provide an ioctl that uses a 64-bit count.
92 *
93 * \param dpy Pointer to the \c Display.
94 * \param priv Pointer to the DRI drawable private.
95 * \param target_msc Desired refresh count to wait for. A value of 0
96 * means to use the glXWaitVideoSyncSGI() behavior.
97 * \param divisor MSC divisor if \c target_msc is already reached.
98 * \param remainder Desired MSC remainder if \c target_msc is already
99 * reached.
100 * \param msc Buffer to hold MSC when done waiting.
101 *
102 * \return Zero on success or \c GLX_BAD_CONTEXT on failure.
103 */
104
105 int driWaitForMSC32( __DRIdrawablePrivate *priv,
106 int64_t target_msc, int64_t divisor, int64_t remainder,
107 int64_t * msc )
108 {
109 drmVBlank vbl;
110
111
112 if ( divisor != 0 ) {
113 unsigned int target = (unsigned int)target_msc;
114 unsigned int next = target;
115 unsigned int r;
116 int dont_wait = (target_msc == 0);
117
118 do {
119 /* dont_wait means we're using the glXWaitVideoSyncSGI() behavior.
120 * The first time around, just get the current count and proceed
121 * to the test for (MSC % divisor) == remainder.
122 */
123 vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE :
124 DRM_VBLANK_ABSOLUTE;
125 vbl.request.sequence = next;
126
127 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
128 /* FIXME: This doesn't seem like the right thing to return here.
129 */
130 return GLX_BAD_CONTEXT;
131 }
132
133 dont_wait = 0;
134 if (target_msc != 0 && vbl.reply.sequence == target)
135 break;
136
137 /* Assuming the wait-done test fails, the next refresh to wait for
138 * will be one that satisfies (MSC % divisor) == remainder. The
139 * value (MSC - (MSC % divisor) + remainder) is the refresh value
140 * closest to the current value that would satisfy the equation.
141 * If this refresh has already happened, we add divisor to obtain
142 * the next refresh after the current one that will satisfy it.
143 */
144 r = (vbl.reply.sequence % (unsigned int)divisor);
145 next = (vbl.reply.sequence - r + (unsigned int)remainder);
146 if (next <= vbl.reply.sequence) next += (unsigned int)divisor;
147
148 } while ( r != (unsigned int)remainder );
149 }
150 else {
151 /* If the \c divisor is zero, just wait until the MSC is greater
152 * than or equal to \c target_msc.
153 */
154
155 vbl.request.type = DRM_VBLANK_ABSOLUTE;
156 vbl.request.sequence = target_msc;
157
158 if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) {
159 /* FIXME: This doesn't seem like the right thing to return here.
160 */
161 return GLX_BAD_CONTEXT;
162 }
163 }
164
165 *msc = (target_msc & 0xffffffff00000000LL);
166 *msc |= vbl.reply.sequence;
167 if ( *msc < target_msc ) {
168 *msc += 0x0000000100000000LL;
169 }
170
171 return 0;
172 }
173
174
175 /****************************************************************************/
176 /**
177 * Gets a set of default vertical-blank-wait flags based on the internal GLX
178 * API version and several configuration options.
179 */
180
181 GLuint driGetDefaultVBlankFlags( const driOptionCache *optionCache )
182 {
183 GLuint flags = VBLANK_FLAG_INTERVAL;
184 int vblank_mode;
185
186
187 if ( driCheckOption( optionCache, "vblank_mode", DRI_ENUM ) )
188 vblank_mode = driQueryOptioni( optionCache, "vblank_mode" );
189 else
190 vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
191
192 switch (vblank_mode) {
193 case DRI_CONF_VBLANK_NEVER:
194 flags = 0;
195 break;
196 case DRI_CONF_VBLANK_DEF_INTERVAL_0:
197 break;
198 case DRI_CONF_VBLANK_DEF_INTERVAL_1:
199 flags |= VBLANK_FLAG_THROTTLE;
200 break;
201 case DRI_CONF_VBLANK_ALWAYS_SYNC:
202 flags |= VBLANK_FLAG_SYNC;
203 break;
204 }
205
206 return flags;
207 }
208
209
210 /****************************************************************************/
211 /**
212 * Wrapper to call \c drmWaitVBlank. The main purpose of this function is to
213 * wrap the error message logging. The error message should only be logged
214 * the first time the \c drmWaitVBlank fails. If \c drmWaitVBlank is
215 * successful, \c vbl_seq will be set the sequence value in the reply.
216 *
217 * \param vbl Pointer to drmVBlank packet desribing how to wait.
218 * \param vbl_seq Location to store the current refresh counter.
219 * \param fd File descriptor use to call into the DRM.
220 * \return Zero on success or -1 on failure.
221 */
222
223 static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd )
224 {
225 int ret;
226
227
228 ret = drmWaitVBlank( fd, vbl );
229 if ( ret != 0 ) {
230 static GLboolean first_time = GL_TRUE;
231
232 if ( first_time ) {
233 fprintf(stderr,
234 "%s: drmWaitVBlank returned %d, IRQs don't seem to be"
235 " working correctly.\nTry running with LIBGL_THROTTLE_REFRESH"
236 " and LIBL_SYNC_REFRESH unset.\n", __FUNCTION__, ret);
237 first_time = GL_FALSE;
238 }
239
240 return -1;
241 }
242
243 *vbl_seq = vbl->reply.sequence;
244 return 0;
245 }
246
247
248 /****************************************************************************/
249 /**
250 * Sets the default swap interval when the drawable is first bound to a
251 * direct rendering context.
252 */
253
254 void driDrawableInitVBlank( __DRIdrawablePrivate *priv, GLuint flags,
255 GLuint *vbl_seq )
256 {
257 if ( priv->pdraw->swap_interval == (unsigned)-1 ) {
258 /* Get current vertical blank sequence */
259 drmVBlank vbl = { .request={ .type = DRM_VBLANK_RELATIVE, .sequence = 0 } };
260 do_wait( &vbl, vbl_seq, priv->driScreenPriv->fd );
261
262 priv->pdraw->swap_interval = (flags & (VBLANK_FLAG_THROTTLE |
263 VBLANK_FLAG_SYNC)) != 0 ? 1 : 0;
264 }
265 }
266
267
268 /****************************************************************************/
269 /**
270 * Returns the current swap interval of the given drawable.
271 */
272
273 unsigned
274 driGetVBlankInterval( const __DRIdrawablePrivate *priv, GLuint flags )
275 {
276 if ( (flags & VBLANK_FLAG_INTERVAL) != 0 ) {
277 /* this must have been initialized when the drawable was first bound
278 * to a direct rendering context. */
279 assert ( priv->pdraw->swap_interval != (unsigned)-1 );
280
281 return priv->pdraw->swap_interval;
282 }
283 else if ( (flags & (VBLANK_FLAG_THROTTLE | VBLANK_FLAG_SYNC)) != 0 ) {
284 return 1;
285 }
286 else {
287 return 0;
288 }
289 }
290
291
292 /****************************************************************************/
293 /**
294 * Returns the current vertical blank sequence number of the given drawable.
295 */
296
297 void
298 driGetCurrentVBlank( const __DRIdrawablePrivate *priv, GLuint flags,
299 GLuint *vbl_seq )
300 {
301 drmVBlank vbl;
302
303 vbl.request.type = DRM_VBLANK_RELATIVE;
304 if ( flags & VBLANK_FLAG_SECONDARY ) {
305 vbl.request.type |= DRM_VBLANK_SECONDARY;
306 }
307 vbl.request.sequence = 0;
308
309 (void) do_wait( &vbl, vbl_seq, priv->driScreenPriv->fd );
310 }
311
312
313 /****************************************************************************/
314 /**
315 * Waits for the vertical blank for use with glXSwapBuffers.
316 *
317 * \param vbl_seq Vertical blank sequence number (MSC) after the last buffer
318 * swap. Updated after this wait.
319 * \param flags \c VBLANK_FLAG bits that control how long to wait.
320 * \param missed_deadline Set to \c GL_TRUE if the MSC after waiting is later
321 * than the "target" based on \c flags. The idea is that if
322 * \c missed_deadline is set, then the application is not
323 * achieving its desired framerate.
324 * \return Zero on success, -1 on error.
325 */
326
327 int
328 driWaitForVBlank( const __DRIdrawablePrivate *priv, GLuint * vbl_seq,
329 GLuint flags, GLboolean * missed_deadline )
330 {
331 drmVBlank vbl;
332 unsigned original_seq;
333 unsigned deadline;
334 unsigned interval;
335 unsigned diff;
336
337 *missed_deadline = GL_FALSE;
338 if ( (flags & (VBLANK_FLAG_INTERVAL |
339 VBLANK_FLAG_THROTTLE |
340 VBLANK_FLAG_SYNC)) == 0 ||
341 (flags & VBLANK_FLAG_NO_IRQ) != 0 ) {
342 return 0;
343 }
344
345
346 /* VBLANK_FLAG_SYNC means to wait for at least one vertical blank. If
347 * that flag is not set, do a fake wait for zero vertical blanking
348 * periods so that we can get the current MSC.
349 *
350 * VBLANK_FLAG_INTERVAL and VBLANK_FLAG_THROTTLE mean to wait for at
351 * least one vertical blank since the last wait. Since do_wait modifies
352 * vbl_seq, we have to save the original value of vbl_seq for the
353 * VBLANK_FLAG_INTERVAL / VBLANK_FLAG_THROTTLE calculation later.
354 */
355
356 original_seq = *vbl_seq;
357 interval = driGetVBlankInterval(priv, flags);
358 deadline = original_seq + interval;
359
360 vbl.request.type = DRM_VBLANK_RELATIVE;
361 if ( flags & VBLANK_FLAG_SECONDARY ) {
362 vbl.request.type |= DRM_VBLANK_SECONDARY;
363 }
364 vbl.request.sequence = ((flags & VBLANK_FLAG_SYNC) != 0) ? 1 : 0;
365
366 if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
367 return -1;
368 }
369
370 diff = *vbl_seq - deadline;
371
372 /* No need to wait again if we've already reached the target */
373 if (diff <= (1 << 23)) {
374 *missed_deadline = (flags & VBLANK_FLAG_SYNC) ? (diff > 0) : GL_TRUE;
375 return 0;
376 }
377
378 /* Wait until the target vertical blank. */
379 vbl.request.type = DRM_VBLANK_ABSOLUTE;
380 if ( flags & VBLANK_FLAG_SECONDARY ) {
381 vbl.request.type |= DRM_VBLANK_SECONDARY;
382 }
383 vbl.request.sequence = deadline;
384
385 if ( do_wait( & vbl, vbl_seq, priv->driScreenPriv->fd ) != 0 ) {
386 return -1;
387 }
388
389 diff = *vbl_seq - deadline;
390 *missed_deadline = diff > 0 && diff <= (1 << 23);
391
392 return 0;
393 }