[PATCH] Add Option "TearFree"

Alex Deucher alexdeucher at gmail.com
Mon Apr 20 06:45:52 PDT 2015


On Mon, Apr 20, 2015 at 5:54 AM, Michel Dänzer <michel at daenzer.net> wrote:
> From: Michel Dänzer <michel.daenzer at amd.com>
>
> Avoids tearing by flipping between two scanout BOs per (non-rotated) CRTC
>
> Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>

Reviewed-by: Alex Deucher <alexander.deucher at amd.com>

> ---
>  man/radeon.man        |   7 +++
>  src/drmmode_display.c |  78 ++++++++++++++++-------------
>  src/drmmode_display.h |   5 +-
>  src/radeon.h          |   2 +
>  src/radeon_kms.c      | 136 ++++++++++++++++++++++++++++++++++++++------------
>  5 files changed, 158 insertions(+), 70 deletions(-)
>
> diff --git a/man/radeon.man b/man/radeon.man
> index 2703773..f0a6be1 100644
> --- a/man/radeon.man
> +++ b/man/radeon.man
> @@ -276,6 +276,13 @@ Enable DRI2 page flipping.  The default is
>  .B on.
>  Pageflipping is supported on all radeon hardware.
>  .TP
> +.BI "Option \*qTearFree\*q \*q" boolean \*q
> +Enable tearing prevention using the hardware page flipping mechanism. This
> +option currently doesn't have any effect for rotated CRTCs. It requires
> +allocating two separate scanout buffers for each non-rotated CRTC. Enabling
> +this option currently disables Option \*qEnablePageFlip\*q. The default is
> +.B off.
> +.TP
>  .BI "Option \*qAccelMethod\*q \*q" "string" \*q
>  Chooses between available acceleration architectures.  Valid values are
>  .B EXA
> diff --git a/src/drmmode_display.c b/src/drmmode_display.c
> index 1f22869..ce6cd80 100644
> --- a/src/drmmode_display.c
> +++ b/src/drmmode_display.c
> @@ -488,6 +488,10 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
>                 scanout->bo = NULL;
>         }
>
> +       if (scanout->damage) {
> +               DamageDestroy(scanout->damage);
> +               scanout->damage = NULL;
> +       }
>  }
>
>  void
> @@ -501,12 +505,9 @@ drmmode_scanout_free(ScrnInfoPtr scrn)
>                         xf86_config->crtc[c]->driver_private;
>
>                 drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
> -                                            &drmmode_crtc->scanout);
> -
> -               if (drmmode_crtc->scanout_damage) {
> -                       DamageDestroy(drmmode_crtc->scanout_damage);
> -                       drmmode_crtc->scanout_damage = NULL;
> -               }
> +                                            &drmmode_crtc->scanout[0]);
> +               drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
> +                                            &drmmode_crtc->scanout[1]);
>         }
>  }
>
> @@ -704,44 +705,49 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
>                         x = drmmode_crtc->prime_pixmap_x;
>                         y = 0;
>
> -                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout);
> +                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]);
> +                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]);
>                 } else
>  #endif
>                 if (drmmode_crtc->rotate.fb_id) {
>                         fb_id = drmmode_crtc->rotate.fb_id;
>                         x = y = 0;
>
> -                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout);
> -               } else if (info->shadow_primary) {
> -                       drmmode_crtc_scanout_create(crtc,
> -                                                   &drmmode_crtc->scanout,
> -                                                   NULL, mode->HDisplay,
> -                                                   mode->VDisplay);
> -
> -                       if (drmmode_crtc->scanout.pixmap) {
> -                               RegionPtr pRegion;
> -                               BoxPtr pBox;
> -
> -                               if (!drmmode_crtc->scanout_damage) {
> -                                       drmmode_crtc->scanout_damage =
> -                                               DamageCreate(radeon_screen_damage_report,
> -                                                            NULL, DamageReportRawRegion,
> -                                                            TRUE, pScreen, NULL);
> -                                       DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
> -                                                      drmmode_crtc->scanout_damage);
> +                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]);
> +                       drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]);
> +               } else if (info->tear_free || info->shadow_primary) {
> +                       for (i = 0; i < (info->tear_free ? 2 : 1); i++) {
> +                               drmmode_crtc_scanout_create(crtc,
> +                                                           &drmmode_crtc->scanout[i],
> +                                                           NULL, mode->HDisplay,
> +                                                           mode->VDisplay);
> +
> +                               if (drmmode_crtc->scanout[i].pixmap) {
> +                                       RegionPtr pRegion;
> +                                       BoxPtr pBox;
> +
> +                                       if (!drmmode_crtc->scanout[i].damage) {
> +                                               drmmode_crtc->scanout[i].damage =
> +                                                       DamageCreate(radeon_screen_damage_report,
> +                                                                    NULL, DamageReportRawRegion,
> +                                                                    TRUE, pScreen, NULL);
> +                                               DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
> +                                                              drmmode_crtc->scanout[i].damage);
> +                                       }
> +
> +                                       pRegion = DamageRegion(drmmode_crtc->scanout[i].damage);
> +                                       RegionUninit(pRegion);
> +                                       pRegion->data = NULL;
> +                                       pBox = RegionExtents(pRegion);
> +                                       pBox->x1 = min(pBox->x1, x);
> +                                       pBox->y1 = min(pBox->y1, y);
> +                                       pBox->x2 = max(pBox->x2, x + mode->HDisplay);
> +                                       pBox->y2 = max(pBox->y2, y + mode->VDisplay);
> +
> +                                       x = y = 0;
>                                 }
>
> -                               pRegion = DamageRegion(drmmode_crtc->scanout_damage);
> -                               RegionUninit(pRegion);
> -                               pRegion->data = NULL;
> -                               pBox = RegionExtents(pRegion);
> -                               pBox->x1 = min(pBox->x1, x);
> -                               pBox->y1 = min(pBox->y1, y);
> -                               pBox->x2 = max(pBox->x2, x + mode->HDisplay);
> -                               pBox->y2 = max(pBox->y2, y + mode->VDisplay);
> -
> -                               fb_id = drmmode_crtc->scanout.fb_id;
> -                               x = y = 0;
> +                               fb_id = drmmode_crtc->scanout[drmmode_crtc->scanout_id].fb_id;
>                         }
>                 }
>                 ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
> diff --git a/src/drmmode_display.h b/src/drmmode_display.h
> index 43a3a4a..1908b46 100644
> --- a/src/drmmode_display.h
> +++ b/src/drmmode_display.h
> @@ -75,6 +75,7 @@ typedef struct {
>  struct drmmode_scanout {
>      struct radeon_bo *bo;
>      PixmapPtr pixmap;
> +    DamagePtr damage;
>      unsigned fb_id;
>      int width, height;
>  };
> @@ -85,8 +86,8 @@ typedef struct {
>      int hw_id;
>      struct radeon_bo *cursor_bo;
>      struct drmmode_scanout rotate;
> -    struct drmmode_scanout scanout;
> -    DamagePtr scanout_damage;
> +    struct drmmode_scanout scanout[2];
> +    unsigned scanout_id;
>      Bool scanout_update_pending;
>      int dpms_mode;
>      CARD64 dpms_last_ust;
> diff --git a/src/radeon.h b/src/radeon.h
> index dbc1660..1c794ce 100644
> --- a/src/radeon.h
> +++ b/src/radeon.h
> @@ -152,6 +152,7 @@ typedef enum {
>      OPTION_DELETE_DP12,
>      OPTION_DRI3,
>      OPTION_SHADOW_PRIMARY,
> +    OPTION_TEAR_FREE,
>  } RADEONOpts;
>
>
> @@ -477,6 +478,7 @@ typedef struct {
>      Bool              accelOn;
>      Bool              use_glamor;
>      Bool              shadow_primary;
> +    Bool              tear_free;
>      Bool             exa_pixmaps;
>      Bool              exa_force_create;
>      XF86ModReqInfo    exaReq;
> diff --git a/src/radeon_kms.c b/src/radeon_kms.c
> index 64593ab..e18f85a 100644
> --- a/src/radeon_kms.c
> +++ b/src/radeon_kms.c
> @@ -78,6 +78,7 @@ const OptionInfoRec RADEONOptions_KMS[] = {
>      { OPTION_SWAPBUFFERS_WAIT,"SwapbuffersWait", OPTV_BOOLEAN, {0}, FALSE },
>      { OPTION_DELETE_DP12,    "DeleteUnusedDP12Displays", OPTV_BOOLEAN, {0}, FALSE},
>      { OPTION_DRI3,           "DRI3",             OPTV_BOOLEAN, {0}, FALSE },
> +    { OPTION_TEAR_FREE,      "TearFree",         OPTV_BOOLEAN, {0}, FALSE },
>      { -1,                    NULL,               OPTV_NONE,    {0}, FALSE }
>  };
>
> @@ -320,21 +321,11 @@ radeon_scanout_extents_intersect(BoxPtr extents, int x, int y, int w, int h)
>      return (extents->x1 < extents->x2 && extents->y1 < extents->y2);
>  }
>
> -static void
> -radeon_scanout_update_abort(ScrnInfoPtr scrn, void *event_data)
> -{
> -    xf86CrtcPtr xf86_crtc = event_data;
> -    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
> -
> -    drmmode_crtc->scanout_update_pending = FALSE;
> -}
> -
> -static void
> -radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
> -                             void *event_data)
> +static Bool
> +radeon_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id)
>  {
> -    xf86CrtcPtr xf86_crtc = event_data;
>      drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
> +    ScrnInfoPtr scrn;
>      DamagePtr pDamage;
>      RegionPtr pRegion;
>      DrawablePtr pDraw;
> @@ -344,26 +335,28 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
>      RADEONInfoPtr info;
>      Bool force;
>
> -    if (!drmmode_crtc->scanout.pixmap ||
> -       drmmode_crtc->dpms_mode != DPMSModeOn)
> -       goto out;
> +    if (drmmode_crtc->dpms_mode != DPMSModeOn ||
> +       !drmmode_crtc->scanout[scanout_id].pixmap)
> +       return FALSE;
>
> -    pDamage = drmmode_crtc->scanout_damage;
> +    pDamage = drmmode_crtc->scanout[scanout_id].damage;
>      if (!pDamage)
> -       goto out;
> +       return FALSE;
>
>      pRegion = DamageRegion(pDamage);
>      if (!RegionNotEmpty(pRegion))
> -       goto out;
> +       return FALSE;
>
> -    pDraw = &drmmode_crtc->scanout.pixmap->drawable;
> +    pDraw = &drmmode_crtc->scanout[scanout_id].pixmap->drawable;
>      extents = *RegionExtents(pRegion);
> +    RegionEmpty(pRegion);
>      if (!radeon_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
>                                           pDraw->width, pDraw->height))
> -       goto clear_damage;
> +       return FALSE;
>
>      pScreen = pDraw->pScreen;
>      gc = GetScratchGC(pDraw->depth, pScreen);
> +    scrn = xf86_crtc->scrn;
>      info = RADEONPTR(scrn);
>      force = info->accel_state->force;
>      info->accel_state->force = TRUE;
> @@ -380,14 +373,28 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
>
>      radeon_cs_flush_indirect(scrn);
>
> -clear_damage:
> -    RegionEmpty(pRegion);
> +    return TRUE;
> +}
> +
> +static void
> +radeon_scanout_update_abort(ScrnInfoPtr scrn, void *event_data)
> +{
> +    xf86CrtcPtr xf86_crtc = event_data;
> +    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
>
> -out:
>      drmmode_crtc->scanout_update_pending = FALSE;
>  }
>
>  static void
> +radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
> +                             void *event_data)
> +{
> +    radeon_scanout_do_update(event_data, 0);
> +
> +    radeon_scanout_update_abort(scrn, event_data);
> +}
> +
> +static void
>  radeon_scanout_update(xf86CrtcPtr xf86_crtc)
>  {
>      drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
> @@ -400,11 +407,11 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
>      BoxRec extents;
>
>      if (drmmode_crtc->scanout_update_pending ||
> -       !drmmode_crtc->scanout.pixmap ||
> +       !drmmode_crtc->scanout[0].pixmap ||
>         drmmode_crtc->dpms_mode != DPMSModeOn)
>         return;
>
> -    pDamage = drmmode_crtc->scanout_damage;
> +    pDamage = drmmode_crtc->scanout[0].damage;
>      if (!pDamage)
>         return;
>
> @@ -412,7 +419,7 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
>      if (!RegionNotEmpty(pRegion))
>         return;
>
> -    pDraw = &drmmode_crtc->scanout.pixmap->drawable;
> +    pDraw = &drmmode_crtc->scanout[0].pixmap->drawable;
>      extents = *RegionExtents(pRegion);
>      if (!radeon_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
>                                           pDraw->width, pDraw->height))
> @@ -445,6 +452,60 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
>      drmmode_crtc->scanout_update_pending = TRUE;
>  }
>
> +static void
> +radeon_scanout_flip_abort(ScrnInfoPtr scrn, void *event_data)
> +{
> +    drmmode_crtc_private_ptr drmmode_crtc = event_data;
> +
> +    drmmode_crtc->scanout_update_pending = FALSE;
> +}
> +
> +static void
> +radeon_scanout_flip_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, void *event_data)
> +{
> +    radeon_scanout_flip_abort(scrn, event_data);
> +}
> +
> +static void
> +radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
> +                   xf86CrtcPtr xf86_crtc)
> +{
> +    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
> +    ScrnInfoPtr scrn;
> +    struct radeon_drm_queue_entry *drm_queue_entry;
> +    unsigned scanout_id;
> +
> +    if (drmmode_crtc->scanout_update_pending)
> +       return;
> +
> +    scanout_id = drmmode_crtc->scanout_id ^ 1;
> +    if (!radeon_scanout_do_update(xf86_crtc, scanout_id))
> +       return;
> +
> +    scrn = xf86_crtc->scrn;
> +    drm_queue_entry = radeon_drm_queue_alloc(scrn, RADEON_DRM_QUEUE_CLIENT_DEFAULT,
> +                                            RADEON_DRM_QUEUE_ID_DEFAULT,
> +                                            drmmode_crtc,
> +                                            radeon_scanout_flip_handler,
> +                                            radeon_scanout_flip_abort);
> +    if (!drm_queue_entry) {
> +       xf86DrvMsg(scrn->scrnIndex, X_WARNING,
> +                  "Allocating DRM event queue entry failed.\n");
> +       return;
> +    }
> +
> +    if (drmModePageFlip(drmmode_crtc->drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
> +                       drmmode_crtc->scanout[scanout_id].fb_id,
> +                       DRM_MODE_PAGE_FLIP_EVENT, drm_queue_entry)) {
> +       xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed: %s\n",
> +                  strerror(errno));
> +       return;
> +    }
> +
> +    drmmode_crtc->scanout_id = scanout_id;
> +    drmmode_crtc->scanout_update_pending = TRUE;
> +}
> +
>  static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
>  {
>      SCREEN_PTR(arg);
> @@ -455,12 +516,16 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
>      (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
>      pScreen->BlockHandler = RADEONBlockHandler_KMS;
>
> -    if (info->shadow_primary) {
> +    if (info->tear_free || info->shadow_primary) {
>         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
>         int c;
>
> -       for (c = 0; c < xf86_config->num_crtc; c++)
> -           radeon_scanout_update(xf86_config->crtc[c]);
> +       for (c = 0; c < xf86_config->num_crtc; c++) {
> +           if (info->tear_free)
> +               radeon_scanout_flip(pScreen, info, xf86_config->crtc[c]);
> +           else
> +               radeon_scanout_update(xf86_config->crtc[c]);
> +       }
>      }
>
>      radeon_cs_flush_indirect(pScrn);
> @@ -1092,15 +1157,22 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
>      }
>  #endif
>
> +    info->tear_free = xf86ReturnOptValBool(info->Options, OPTION_TEAR_FREE,
> +                                          FALSE);
> +
> +    if (info->shadow_primary)
> +       xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "TearFree enabled\n");
> +
>      if (info->dri2.pKernelDRMVersion->version_minor >= 8) {
>         info->allowPageFlip = xf86ReturnOptValBool(info->Options,
>                                                    OPTION_PAGE_FLIP, TRUE);
>  #if USE_GLAMOR
> -       if (info->shadow_primary) {
> +       if (info->tear_free || info->shadow_primary) {
>             xf86DrvMsg(pScrn->scrnIndex,
>                        info->allowPageFlip ? X_WARNING : X_DEFAULT,
>                        "KMS Pageflipping: disabled%s\n",
> -                      info->allowPageFlip ? " because of ShadowPrimary" : "");
> +                      info->allowPageFlip ?
> +                      " because of ShadowPrimary/TearFree" : "");
>             info->allowPageFlip = FALSE;
>         } else
>  #endif
> --
> 2.1.4
>
> _______________________________________________
> xorg-driver-ati mailing list
> xorg-driver-ati at lists.x.org
> http://lists.x.org/mailman/listinfo/xorg-driver-ati


More information about the xorg-driver-ati mailing list