正常顯示流程
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,將會由軟件實現,性能影響較大;