[PATCH 4/4] DRI2: defer swap if relevant CRTC is in DMPS off state

Ilija Hadzic ilijahadzic at gmail.com
Fri Dec 14 18:09:13 PST 2012


From: Ilija Hadzic <ihadzic at research.bell-labs.com>

If drawable is displayed on a CRTC with a valid mode and
relevant CRTC is in DPMS off state, calculate the nominal
vblank period and defer the swap until the calculated time
elapses.

This patch fixes a bug that caused an application to render
at uncontrolled rate when CRTC goes into DPMS "off" state,
thus thrashing the GPU and CPU and likely offsetting the
power savings achieved by shutting off the display.

v2: Take into account target MSC and any time that may have
    passed between previous event and entering scheduling the
    current event.

Signed-off-by: Ilija Hadzic <ihadzic at research.bell-labs.com>
---
 src/radeon_dri2.c  | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/radeon_video.c |  3 +-
 src/radeon_video.h |  2 ++
 3 files changed, 95 insertions(+), 7 deletions(-)

diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
index a025e16..98ce8eb 100644
--- a/src/radeon_dri2.c
+++ b/src/radeon_dri2.c
@@ -31,6 +31,7 @@
 
 #include "radeon.h"
 #include "radeon_dri2.h"
+#include "radeon_video.h"
 
 #ifdef DRI2
 
@@ -1281,6 +1282,45 @@ void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
 }
 
 /*
+ * Subtract current time from the time of last recorded frame event.
+ * In (highly unlikely) case of an overflow, clip the result to max integer
+ */
+static int time_since_last_event(unsigned int tv_sec, unsigned int tv_usec)
+{
+    struct timeval now;
+    int64_t delta_t_usec, delta_t_msec;
+    int delta_t_sec;
+
+    gettimeofday(&now, NULL);
+    delta_t_sec = (int)now.tv_sec - (int)tv_sec;
+    delta_t_usec = (int64_t)now.tv_usec - (int64_t)tv_usec;
+    delta_t_usec += (int64_t)delta_t_sec * 1000000;
+    delta_t_msec = delta_t_usec / 1000;
+    if (delta_t_msec > INT_MAX)
+	delta_t_msec = INT_MAX;
+    if (delta_t_msec < -INT_MAX)
+	delta_t_msec = -INT_MAX;
+    return delta_t_msec;
+}
+
+/*
+ * Entry point into radeon_dri2_frame_event_handler when event is
+ * scheduled using timer facility instead of synchronizing on
+ * vblank (when CRTC's display is turned off or when for some reason
+ * scheduling using vblanks fails).
+ */
+static
+CARD32 radeon_dri2_deferred_swap(OsTimerPtr timer, CARD32 now, pointer data)
+{
+    struct timeval tv;
+
+    TimerFree(timer);
+    gettimeofday(&tv, NULL);
+    radeon_dri2_frame_event_handler(0, tv.tv_sec, tv.tv_usec, data);
+    return 0;
+}
+
+/*
  * ScheduleSwap is responsible for requesting a DRM vblank event for the
  * appropriate frame.
  *
@@ -1309,9 +1349,11 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
     ScreenPtr screen = draw->pScreen;
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     RADEONInfoPtr info = RADEONPTR(scrn);
-    xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw, FALSE);
+    xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw, TRUE);
+    drmmode_crtc_private_ptr drmmode_crtc;
     drmVBlank vbl;
     int ret, flip = 0;
+    int nominal_vblank_period, swap_delay;
     DRI2FrameEventPtr swap_info = NULL;
     enum DRI2FrameEventType swap_type = DRI2_SWAP;
     CARD64 current_msc;
@@ -1331,7 +1373,7 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
     radeon_dri2_ref_buffer(front);
     radeon_dri2_ref_buffer(back);
 
-    /* Drawable not displayed... just complete the swap */
+    /* either off-screen or CRTC not usable... just complete the swap */
     if (crtc == NULL)
         goto blit_fallback;
 
@@ -1355,6 +1397,45 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
         goto blit_fallback;
     }
 
+    /*
+     * Calculate the time to next swap event (we'll use it
+     * only if we can't use the real vblank event from CRTC).
+     */
+    drmmode_crtc = crtc->driver_private;
+    nominal_vblank_period = crtc->mode.HTotal * crtc->mode.VTotal;
+    nominal_vblank_period /= crtc->mode.Clock;
+    if (drmmode_crtc->last_event_valid) {
+	int delta_t, delta_msc;
+
+	delta_t = time_since_last_event(drmmode_crtc->last_event_tv_sec,
+					drmmode_crtc->last_event_tv_usec);
+	if (delta_t < 0) {
+	    xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		       "negative time since last event (bug?).\n");
+	    delta_t = 0;
+	}
+	if (*target_msc < drmmode_crtc->last_event_msc)
+	    delta_msc = 0;
+	else
+	    delta_msc = (int)(*target_msc - drmmode_crtc->last_event_msc);
+	if (delta_t >= nominal_vblank_period * delta_msc)
+	    swap_delay = 1;
+	else
+	    swap_delay = nominal_vblank_period * delta_msc - delta_t;
+    } else
+	swap_delay = 1;
+
+    /*
+     * CRTC is in DPMS off state, fallback to blit, but pace the
+     * application at the rate that roughly approximates the
+     * nominal frame rate of the relevant CRTC.
+     */
+    if (!radeon_crtc_is_enabled(crtc)) {
+	TimerSet(NULL, 0, swap_delay, radeon_dri2_deferred_swap, swap_info);
+	*target_msc = 0;
+	return TRUE;
+    }
+
     /* Get current count */
     vbl.request.type = DRM_VBLANK_RELATIVE;
     vbl.request.type |= populate_vbl_request_type(info, crtc);
@@ -1364,7 +1445,9 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
         xf86DrvMsg(scrn->scrnIndex, X_WARNING,
                 "first get vblank counter failed: %s\n",
                 strerror(errno));
-        goto blit_fallback;
+	TimerSet(NULL, 0, swap_delay, radeon_dri2_deferred_swap, swap_info);
+	*target_msc = 0;
+	return TRUE;
     }
 
     current_msc = vbl.reply.sequence;
@@ -1413,7 +1496,9 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
             xf86DrvMsg(scrn->scrnIndex, X_WARNING,
                     "divisor 0 get vblank counter failed: %s\n",
                     strerror(errno));
-            goto blit_fallback;
+	    TimerSet(NULL, 0, swap_delay, radeon_dri2_deferred_swap, swap_info);
+	    *target_msc = 0;
+            return TRUE;
         }
 
         *target_msc = vbl.reply.sequence + flip;
@@ -1458,7 +1543,9 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
         xf86DrvMsg(scrn->scrnIndex, X_WARNING,
                 "final get vblank counter failed: %s\n",
                 strerror(errno));
-        goto blit_fallback;
+	TimerSet(NULL, 0, swap_delay, radeon_dri2_deferred_swap, swap_info);
+	*target_msc = 0;
+	return TRUE;
     }
 
     /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
diff --git a/src/radeon_video.c b/src/radeon_video.c
index 63b4844..8999dd0 100644
--- a/src/radeon_video.c
+++ b/src/radeon_video.c
@@ -67,8 +67,7 @@ radeon_box_area(BoxPtr box)
     return (int) (box->x2 - box->x1) * (int) (box->y2 - box->y1);
 }
 
-static Bool
-radeon_crtc_is_enabled(xf86CrtcPtr crtc)
+Bool radeon_crtc_is_enabled(xf86CrtcPtr crtc)
 {
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     return drmmode_crtc->dpms_mode == DPMSModeOn;
diff --git a/src/radeon_video.h b/src/radeon_video.h
index f097f2f..e6068e8 100644
--- a/src/radeon_video.h
+++ b/src/radeon_video.h
@@ -100,4 +100,6 @@ RADEONCopyMungedData(ScrnInfoPtr pScrn,
 		     unsigned int srcPitch, unsigned int srcPitch2,
 		     unsigned int dstPitch, unsigned int h, unsigned int w);
 
+Bool radeon_crtc_is_enabled(xf86CrtcPtr crtc);
+
 #endif
-- 
1.7.12.4



More information about the xorg-driver-ati mailing list