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