Android之窗口系統
要點
1.Android窗口系統通過C-S架構和一套Buffer循環機制實現,在保證安全穩定的前提下基本上做到了極致性能(無大塊內存拷貝,IPC通信內容最少)。
2.SurfaceFlinger創建Layer,將其中的BufferQueueProducer作爲IGraphicBufferProducer傳給應用側的Surface,因而構成窗口。
3.Surface是皮,BufferQueue是肉,通過這樣的皮肉關係構建了Buffer循環機制。Buffer循環機制不僅用於窗口系統,也用於視頻播放解碼流,相機拍照數據流等(Camera2.0架構)。
注:代碼基於Android 5.0
窗口系統接口
作爲一個應用,繪圖是自由的,基本上也是平臺無關的。但如果要把繪圖的結果顯示出來,就必須依賴平臺提供的窗口系統。好比我們寫篇文章出來容易,要投到雜誌上發表,就必須按雜誌社的格式,並通過審校等一堆流程,不然每個人都隨便發,肯定亂套了。
Android的窗口系統設計
Android爲應用層提供的窗口接口爲ANativeWindow。這個接口可用來調整配置參數,獲取圖形內存並送還觸發顯示。應用層是Buffer的生產者。
對於非GPU繪圖的應用,通過這個類去獲取圖形內存(dequeueBuffer),並在繪製完成之後送還(queueBuffer),讓顯示系統在合適的時機顯示。
對於GPU繪圖(嚴格來說,是使用EGL標準)的應用,在創建OpenGL上下文時將ANativeWindow的指針傳入,GPU的驅動會在合適的時候完成獲取內存和送還的操作。應用層只需要調用eglSwapBuffers換緩存即可。
Android的Windows接口
對應用層開放的windows接口定義在
system/core/include/system/windows.h
主要接口函數如下:
struct ANativeWindow
{
/*......*/
int (*setSwapInterval)(struct ANativeWindow* window, int interval);//設置Buffer失效期限,當應用生產Buffer快於消費者(一般是顯示系統)的消費時,這個參數決定是否丟棄之前沒來得及消費的Buffer。
int (*query)(const struct ANativeWindow* window, int what, int* value);//查詢參數
int (*perform)(struct ANativeWindow* window,int operation, ... );//設置參數,其中也包括做連接
int (*dequeueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer, int* fenceFd);//從隊列中取出一塊Buffer用於生產(一般是圖形渲染),若是第一次調用,會觸發Buffer的申請
int (*queueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd);//將生產完成的Buffer送還
int (*cancelBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd);//釋放Buffer隊列中的Buffer,一般是disconnect時調用
};
用於perform和query接口的一些宏定義如下:
/*用於perform和query接口的宏*/
enum {
NATIVE_WINDOW_SET_USAGE = 0,
NATIVE_WINDOW_CONNECT = 1, /* deprecated */
NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
NATIVE_WINDOW_SET_CROP = 3, /* private */
NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
NATIVE_WINDOW_LOCK = 11, /* private */
NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
NATIVE_WINDOW_API_CONNECT = 13, /* private */
NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19
};
窗口的創建與使用
Surface
自Android 4.2之後,FramebufferNativeWindow被廢棄,所有窗口均繼承Surface。Surface本身是一種ANativeWindow。
class Surface
: public ANativeObjectBase<ANativeWindow, Surface, RefBase>/*Surface繼承於ANativeWindow*/
{
public:
Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
/*......*/
private:
// ANativeWindow hooks
/*這幾個hook函數對應於 window.h 中的接口函數*/
static int hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd);
static int hook_perform(ANativeWindow* window, int operation, ...);
static int hook_query(const ANativeWindow* window, int what, int* value);
static int hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer);
static int hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
/*.......*/
public:
/*這兩個函數主要是CPU繪圖時調用,除了獲取,映射Buffer之外,額外將非髒區域從上一塊Buffer拷貝到本塊Buffer,以便渲染時只繪製髒區域*/
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
/*.......*/
private:
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
};
/*Buffer生產者,Surface*/
sp<IGraphicBufferProducer> mGraphicBufferProducer;
BufferSlot mSlots[NUM_BUFFER_SLOTS];
/*通過native_window_set_buffers_dimensions改變,用於在合適的時機改變GraphicBuffer的長寬*/
uint32_t mReqWidth;
uint32_t mReqHeight;
/*需求的GraphicBuffer格式,用於動態調整*/
PixelFormat mReqFormat;
/*需求Usage說明,影響GraphicBuffer的flags*/
uint32_t mReqUsage;
/*時間戳,用於判斷Buffer是否過期*/
int64_t mTimestamp;
/*Buffer的使用範圍*/
Rect mCrop;
/*Buffer後續變換需求,只包括水平、垂直翻轉,90度旋轉*/
uint32_t mTransformHint;
/*BufferProducer是否由使用Surface的App控制,這個主要是用來決定dequeueBuffer時是否堵塞*/
bool mProducerControlledByApp;
/*......*/
};
}; // namespace android
窗口的創建
用於圖形顯示的窗口,是由SurfaceFlinger進程負責創建的:
爲應用層創建Surface(窗口)時,SurfaceFlinger同步創建一個Layer,並將Layer的生產者關聯到Surface上。這樣,應用側便可以通過Surface申請Buffer,作爲生產者渲染圖像,送顯由Layer中的消費者負責。
Buffer循環機制
對於按一定幀率刷新的窗口系統,每一次渲染只有很有限的時間,頻繁地申請/釋放圖形內存是不可接受的。Android的做法是維護一個Buffer隊列,按生產者——消費者模式循環利用。這個Buffer就是上一章所述的GraphicBuffer。
Buffer的狀態
Buffer隊列池中持有固定數量的Buffer(由setBufferCount函數決定,一般是3塊),每個Buffer有四種狀態,決定其是否可以被生產者/消費者訪問。
Buffer的生產
序號化
由於Layer位於SurfaceFlinger進程中,GraphicBuffer是在SurfaceFlinger進程中創建的。應用層作爲生產者,使用時需要將其映射到自己的進程空間。在每次申請Buffer時都做一次映射很不明智。
很容易想到的一個方法是在應用層建一個GraphicBuffer隊列,和SurfaceFlinger中的Buffer隊列對應地映射起來,每次申請和返還時,以序號代替真實的GraphicBuffer傳替。Android也正是這麼做的。
dequeueBuffer
Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd)
這接口用於申請內存進行生產(渲染)。
於Surface側,它獲取Buffer的序列號,然後檢查是否已經同步過,若沒有同步過調用requestBuffer同步(映射共享內存)。
於BufferQueue側,它檢查是否有 FREE 標誌的Buffer,如果沒有,根據 mProducerControlledByApp 標誌決定返回錯誤碼或者等待。
fenceFd是BufferQueue返回給Surface的,生產者有義務去等fence。
queueBuffer
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd)
於Surface側,它將Buffer送還給BufferQueue,但這不一定表示生產者用完了,消費者獲取到Buffer之後仍有義務等fence。
於BufferQueue側,接收到Buffer,將其放到一個可用隊列中,修改狀態,並且觸發監聽器的onFrameAvailable函數。
Buffer的消費
acquireBuffer
消費者獲取buffer,由於在queueBuffer時,會將Buffer放到一個隊列中,這時便從那個隊列去取。
releaseBuffer
消費者釋放buffer。