Xorg屏幕旋轉實現方式

正常顯示流程

  Xorg無旋轉時顯示流程如下圖所示:
正常流程圖
  FB Root爲屏幕對應的幀存,屏幕顯示的最終效果渲染到該幀存中。CRTC負責讀取FB Root的數據,並按照用戶設置的分辨率時序參數進行輸出。VGA或者DVI作爲output,將CRTC的輸出進行A/D轉換或者編碼後送給顯示器顯示,對FB Root的更新會實時顯示到屏幕上。

旋轉顯示流程

  Xorg帶旋轉時顯示流程如下圖所示:
旋轉流程圖
  旋轉模式下,屏幕顯示內容先渲染到FB Root中,然後由Xorg Block Handler(關於Xorg Handler的介紹可以參考另一篇文章:Xorg Handler簡介)將FB Root的數據旋轉後存入FB Rotate中,CRTC綁定到FB Rotate進行顯示。很明顯這種情況下,比正常顯示多了一個幀存和無休止的旋轉操作,而且對FB Root的更新不會實時顯示到屏幕上,必須等旋轉操作完成後才能顯示出最終效果。

驅動對旋轉的支持

  驅動要支持旋轉功能必須要實現以下4個xf86CrtcFuncsRec結構體成員函數:

  • shadow_allocate: 負責分配FB Rotate幀存;
  • shadow_create: Xorg無法直接操作幀存,該函數創建一個Xorg可操作的Pixmap對象,並將FB Rotate綁定到其中;
  • shadow_destroy: 銷燬FB Rotate和對應的Pixmap;
  • set_mode_major: 初始化旋轉參數、安裝用於旋轉的Block Handler;配置CRTC分辨率,並綁定到FB Rotate;配置output並綁定到CRTC;

xf86CrtcRotate函數

  set_mode_major函數中要實現的初始化旋轉參數和安裝旋轉Block Handler由Xorg xf86CrtcRotate函數實現。

  該函數先根據屏幕寬高、旋轉方向、輸入矩陣等參數計算出旋轉相關的參數:

Bool
xf86CrtcRotate(xf86CrtcPtr crtc)
{
    ScrnInfoPtr pScrn = crtc->scrn;
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
    PictTransform crtc_to_fb;
    struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
    xFixed *new_params = NULL;
    int new_nparams = 0;
    PictFilterPtr new_filter = NULL;
    int new_width = 0;
    int new_height = 0;
    RRTransformPtr transform = NULL;
    Bool damage = FALSE;

    if (pScreen->isGPU)
        return TRUE;
    if (crtc->transformPresent)
        transform = &crtc->transform;

    if (!RRTransformCompute(crtc->x, crtc->y,
                            crtc->mode.HDisplay, crtc->mode.VDisplay,
                            crtc->rotation,
                            transform,
                            &crtc_to_fb,
                            &f_crtc_to_fb,
                            &f_fb_to_crtc) &&
        xf86CrtcFitsScreen(crtc, &f_crtc_to_fb)) {
        /*
         * If the untranslated transformation is the identity,
         * disable the shadow buffer
         */
        xf86RotateDestroy(crtc);
        crtc->transform_in_use = FALSE;
        free(new_params);
        new_params = NULL;
        new_nparams = 0;
        new_filter = NULL;
        new_width = 0;
        new_height = 0;
    }
    else {

  接着調用驅動的shadow_allocate函數分配FB Rotate幀存:

/* Allocate memory for rotation */
if (old_width != width || old_height != height) {
    if (shadow || shadowData) {
        crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
        crtc->rotatedPixmap = NULL;
        crtc->rotatedData = NULL;
    }
    shadowData = crtc->funcs->shadow_allocate(crtc, width, height);
    if (!shadowData)
        goto bail1;
    crtc->rotatedData = shadowData;
    /* shadow will be damaged in xf86RotatePrepare */
}

  最後將xf86RotateBlockHandler註冊成爲Screen的Block Handler用於完成旋轉:

/* Wrap block handler */
if (!xf86_config->BlockHandler) {
    xf86_config->BlockHandler = pScreen->BlockHandler;
    pScreen->BlockHandler = xf86RotateBlockHandler;
}

xf86RotateBlockHandler函數

static void
xf86RotateBlockHandler(ScreenPtr pScreen,
                       void *pTimeout, void *pReadmask)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);

    /* Unwrap before redisplay in case the software
     * cursor layer wants to add its block handler to the
     * chain
     */
    pScreen->BlockHandler = xf86_config->BlockHandler;

    xf86RotateRedisplay(pScreen);

    (*pScreen->BlockHandler) (pScreen, pTimeout, pReadmask);

    /* Re-wrap if we still need this hook */
    if (xf86_config->rotation_damage != NULL) {
        xf86_config->BlockHandler = pScreen->BlockHandler;
        pScreen->BlockHandler = xf86RotateBlockHandler;
    } else
        xf86_config->BlockHandler = NULL;
}

  xf86RotateBlockHandler先調用xf86RotateRedisplay完成旋轉功能,再調用Screen原始的Block Handler,最後判斷是否還處於旋轉狀態,來決定是否繼續註冊爲Screen Block Handler完成後續旋轉操作。

xf86RotateCrtcRedisplay函數

  xf86RotateBlockHandler調用xf86RotateRedisplay函數進行旋轉,xf86RotateRedisplay函數完成一些準備工作後,最後會調用xf86RotateCrtcRedisplay函數完成旋轉操作。

static void
xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
{
    ScrnInfoPtr scrn = crtc->scrn;
    ScreenPtr screen = scrn->pScreen;
    WindowPtr root = screen->root;
    PixmapPtr dst_pixmap = crtc->rotatedPixmap;
    PictFormatPtr format = PictureWindowFormat(screen->root);
    int error;
    PicturePtr src, dst;
    int n = RegionNumRects(region);
    BoxPtr b = RegionRects(region);
    XID include_inferiors = IncludeInferiors;

    if (crtc->driverIsPerformingTransform)
        return;

    src = CreatePicture(None,
                        &root->drawable,
                        format,
                        CPSubwindowMode,
                        &include_inferiors, serverClient, &error);
    if (!src)
        return;

    dst = CreatePicture(None,
                        &dst_pixmap->drawable,
                        format, 0L, NULL, serverClient, &error);
    if (!dst)
        return;

    error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
    if (error)
        return;
    if (crtc->transform_in_use && crtc->filter)
        SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);

    if (crtc->shadowClear) {
        CompositePicture(PictOpSrc,
                         src, NULL, dst,
                         0, 0, 0, 0, 0, 0,
                         crtc->mode.HDisplay, crtc->mode.VDisplay);
        crtc->shadowClear = FALSE;
    }
    else {
        while (n--) {
            BoxRec dst_box;

            dst_box = *b;
            dst_box.x1 -= crtc->filter_width >> 1;
            dst_box.x2 += crtc->filter_width >> 1;
            dst_box.y1 -= crtc->filter_height >> 1;
            dst_box.y2 += crtc->filter_height >> 1;
            pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &dst_box);
            CompositePicture(PictOpSrc,
                             src, NULL, dst,
                             dst_box.x1, dst_box.y1, 0, 0, dst_box.x1,
                             dst_box.y1, dst_box.x2 - dst_box.x1,
                             dst_box.y2 - dst_box.y1);
            b++;
        }
    }
    FreePicture(src, None);
    FreePicture(dst, None);
}

  xf86RotateCrtcRedisplay函數會使用FB Root創建一個源Picture,並設置源的transform和filter屬性。使用FB Rotate創建一個目標Picture,最後調用CompositePicture將源融合到目標上去。如果硬件支持這種類型的操作,將會由驅動的Composite函數完成該操作,如果硬件不支持,將會由軟件完成。

總結

  • 驅動要支持旋轉操作,必須要實現上述的4個xf86CrtcFuncsRec結構體成員函數;
  • 旋轉操作實際由源到目標的Composite操作完成,源帶transform和filter屬性;
  • 旋轉操作是持續不斷進行的;
  • 如果硬件不支持這種類型的Composite,將會由軟件實現,性能影響較大;

公衆號二維碼

發佈了6 篇原創文章 · 獲贊 10 · 訪問量 3008
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章