[PATCH xf86-video-intel 2/2] Fix handling of target_msc, divisor, remainder in I830DRI2ScheduleSwap()
Mario Kleiner
mario.kleiner at tuebingen.mpg.de
Sun Feb 21 09:45:48 PST 2010
The current code in I830DRI2ScheduleSwap() only schedules the
correct vblank events for the case divisor == 0, i.e., the
simple glXSwapBuffers() case.
In a glXSwapBuffersMscOML() request, divisor can be > 0, which would go
wrong.
This modified code should handle target_msc, divisor, remainder and
the different cases defined in the OML_sync_control extension
correctly for the divisor > 0 case.
It also tries to make sure that the effective framecount of swap satisfies
all constraints, taking the 1 frame delay in pageflipping mode and possible
delays in blitting/exchange mode due to DRM_VBLANK_NEXTONMISS into account.
The swap_interval logic in the X-Servers DRI2SwapBuffers() call expects
the returned swap_target from the DDX to be reasonably accurate, otherwise
implementation of swap_interval for the glXSwapBuffers() as defined in the
SGI_swap_interval extension may become unreliable.
For non-pageflipped mode, the returned swap_target is always correct due to the
adjustments done by drmWaitVBlank(), as DRM_VBLANK_NEXTONMISS is set.
In pageflipped mode, DRM_VBLANK_NEXTONMISS can't be used without
severe impact on performance, so the code in I830DRI2ScheduleSwap()
must make manual adjustments to the returned vbl.reply.sequence number.
This patch adds the needed adjustments.
Signed-off-by: Mario Kleiner <mario.kleiner at tuebingen.mpg.de>
---
src/i830_dri.c | 110 +++++++++++++++++++++++++++++++++++--------------------
1 files changed, 70 insertions(+), 40 deletions(-)
diff --git a/src/i830_dri.c b/src/i830_dri.c
index d5e085a..f3cd739 100644
--- a/src/i830_dri.c
+++ b/src/i830_dri.c
@@ -586,6 +586,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
int ret, pipe = I830DRI2DrawablePipe(draw), flip = 0;
DRI2FrameEventPtr swap_info;
enum DRI2FrameEventType swap_type = DRI2_SWAP;
+ CARD64 current_msc;
swap_info = xcalloc(1, sizeof(DRI2FrameEventRec));
@@ -629,6 +630,8 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
return FALSE;
}
+ current_msc = vbl.reply.sequence;
+
/* Flips need to be submitted one frame before */
if (DRI2CanFlip(draw) && !intel->shadow_present &&
intel->use_pageflipping) {
@@ -638,55 +641,81 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
swap_info->type = swap_type;
+ /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
+ * Do it early, so handling of different timing constraints
+ * for divisor, remainder and msc vs. target_msc works.
+ */
+ if (*target_msc > 0)
+ *target_msc -= flip;
+
/*
- * If divisor is zero, we just need to make sure target_msc passes
- * before waking up the client.
+ * If divisor is zero, or current_msc is smaller than target_msc,
+ * we just need to make sure target_msc passes before initiating the swap.
*/
- if (divisor == 0) {
- vbl.request.type = DRM_VBLANK_NEXTONMISS |
- DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
- if (pipe > 0)
- vbl.request.type |= DRM_VBLANK_SECONDARY;
-
- vbl.request.sequence = *target_msc;
- vbl.request.sequence -= flip;
- vbl.request.signal = (unsigned long)swap_info;
- ret = drmWaitVBlank(intel->drmSubFD, &vbl);
- if (ret) {
- xf86DrvMsg(scrn->scrnIndex, X_WARNING,
- "divisor 0 get vblank counter failed: %s\n",
- strerror(errno));
- return FALSE;
- }
-
- *target_msc = vbl.reply.sequence;
- swap_info->frame = *target_msc;
-
- return TRUE;
+ if (divisor == 0 || current_msc < *target_msc) {
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ /* If non-pageflipping, but blitting/exchanging, we need to use
+ * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later on.
+ */
+ if (flip == 0)
+ vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+ if (pipe > 0)
+ vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+ /* If target_msc already reached or passed, set it to
+ * current_msc to ensure we return a reasonable value back
+ * to the caller. This makes swap_interval logic more robust.
+ */
+ if (current_msc >= *target_msc)
+ *target_msc = current_msc;
+
+ vbl.request.sequence = *target_msc;
+ vbl.request.signal = (unsigned long)swap_info;
+ ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "divisor 0 get vblank counter failed: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+
+ /* Adjust returned value for 1 frame pageflip offset if flip > 0 */
+ *target_msc = vbl.reply.sequence + flip;
+ swap_info->frame = *target_msc;
+
+ return TRUE;
}
-
+
/*
* If we get here, target_msc has already passed or we don't have one,
- * so we queue an event that will satisfy the divisor/remainderequation.
+ * and we need to queue an event that will satisfy the divisor/remainder
+ * equation.
*/
- if ((vbl.reply.sequence % divisor) == remainder)
- return FALSE;
-
+
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ if (flip == 0)
+ vbl.request.type |= DRM_VBLANK_NEXTONMISS;
if (pipe > 0)
- vbl.request.type |= DRM_VBLANK_SECONDARY;
+ vbl.request.type |= DRM_VBLANK_SECONDARY;
+ vbl.request.sequence = current_msc - (current_msc % divisor) + remainder;
+
/*
- * If we have no remainder, and the test above failed, it means we've
- * passed the last point where seq % divisor == remainder, so we need
- * to wait for the next time that will happen.
+ * If calculated deadline vbl.request.sequence is smaller than or equal to
+ * current_msc, it means we've passed the last point when effective
+ * onset frame seq could satisfy seq % divisor == remainder,
+ * so we need to wait for the next time that this will happen.
+ *
+ * This comparison takes the 1 frame swap delay in pageflipping mode into
+ * account, as well as a potential DRM_VBLANK_NEXTONMISS delay if we are
+ * blitting/exchanging instead of flipping.
*/
- if (!remainder)
- vbl.request.sequence += divisor;
-
- vbl.request.sequence = vbl.reply.sequence -
- (vbl.reply.sequence % divisor) + remainder;
+ if (vbl.request.sequence <= current_msc)
+ vbl.request.sequence += divisor;
+
+ /* Account for 1 frame extra pageflip delay if flip > 0 */
vbl.request.sequence -= flip;
+
vbl.request.signal = (unsigned long)swap_info;
ret = drmWaitVBlank(intel->drmSubFD, &vbl);
if (ret) {
@@ -695,10 +724,11 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
strerror(errno));
return FALSE;
}
-
- *target_msc = vbl.reply.sequence;
+
+ /* Adjust returned value for 1 frame pageflip offset if flip > 0 */
+ *target_msc = vbl.reply.sequence + flip;
swap_info->frame = *target_msc;
-
+
return TRUE;
}
--
1.6.6
More information about the xorg-devel
mailing list