原文出處——>[Android應用程序UI硬件加速渲染的Display List渲染過程分析](http://blog.csdn.net/luoshengyang/article/details/46281499)
在硬件加速渲染環境中,Android應用程序窗口的UI渲染是分兩步進行的。第一步是構建Display List,發生在應用程序進程的Main Thread中;第二步是渲染Display List,發生在應用程序進程的Render Thread中。Display List的渲染不是簡單地執行繪制命令,而是包含了一系列優化操作,例如繪制命令的合并執行。本文就詳細分析Display List的渲染過程。
從前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文可以知道,Android應用程序窗口的Root Render Node的Display List,包含了Android應用程序窗口所有的繪制命令,因此我們只要對Root Render Node的Display List進行渲染,就可以得到整個Android應用程序窗口的UI。
Android應用程序窗口的Display List的構建是通過Display List Renderer進行的,而渲染是通過Open GL Renderer進行的,如圖1所示:
:-: 
圖1 Android應用程序窗口的Display List的渲染示意圖
從圖1可以知道,Open GL Renderer只作用在Android應用程序窗口的Root Render Node的Display List上,這是因為Root Render Node的Display List包含了Android應用程序窗口所有的繪制命令。
Android應用程序窗口的Display List的渲染是由Render Thread執行的,不過是由Main Thread通知Render Thread執行的,如圖2所示:
:-: 
圖2 Main Thread向Render Thread發起渲染命令
從圖2可以知道。Main Thread通過向Render Thread的TaskQueue添加一個drawFrame任務來通知Render Thread渲染Android應用程序窗口的UI。
從前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文還可以知道,Android應用程序窗口的Display List構建完成之后,Main Thread就馬上向Render Thread發出渲染命令,如下所示:
~~~
public class ThreadedRenderer extends HardwareRenderer {
......
@Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
......
updateRootDisplayList(view, callbacks);
......
if (attachInfo.mPendingAnimatingRenderNodes != null) {
final int count = attachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
registerAnimatingRenderNode(
attachInfo.mPendingAnimatingRenderNodes.get(i));
}
attachInfo.mPendingAnimatingRenderNodes.clear();
// We don't need this anymore as subsequent calls to
// ViewRootImpl#attachRenderNodeAnimator will go directly to us.
attachInfo.mPendingAnimatingRenderNodes = null;
}
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
recordDuration, view.getResources().getDisplayMetrics().density);
if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
ThreadedRenderer類的成員函數draw主要執行三個操作:
1. 調用成員函數updateRootDisplayList構建或者更新應用程序窗口的Root Render Node的Display List。
2. 調用成員函數registerAnimationRenderNode注冊應用程序窗口動畫相關的Render Node。
3. 調用成員函數nSyncAndDrawFrame渲染應用程序窗口的Root Render Node的Display List。
其中,第一個操作在前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文已經分析,第二個操作在接下來的一篇文章中分析,這篇文章主要關注第三個操作,即應用程序窗口的Root Render Node的Display List的渲染過程,即ThreadedRenderer類的成員函數nSyncAndDrawFrame的實現。
ThreadedRenderer類的成員函數nSyncAndDrawFrame是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_syncAndDrawFrame實現,如下所示:
~~~
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
}
~~~
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
參數proxyPtr描述的是一個RenderProxy對象,這里調用它的成員函數syncAndDrawFrame渲染應用程序窗口的Display List。
RenderProxy類的成員函數syncAndDrawFrame的實現如下所示:
~~~
int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
float density) {
mDrawFrameTask.setDensity(density);
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。
RenderProxy類的成員變量mDrawFrameTask指向的是一個DrawFrameTask對象。在前面Android應用程序UI硬件加速渲染環境初始化過程分析一文提到,這個DrawFrameTask對象描述的是一個用來執行渲染任務的Task,這里調用它的成員函數drawFrame渲染應用程序窗口的下一幀,也就是應用程序窗口的Display List。
DrawFrameTask的成員函數drawFrame的實現如下所示:
~~~
int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
......
mSyncResult = kSync_OK;
......
postAndWait();
......
return mSyncResult;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。
DrawFrameTask的成員函數drawFrame最主要的操作就是調用另外一個成員函數postAndWait往Render Thread的Task Queue拋一個消息,并且進入睡眠狀態,等待Render Thread在合適的時候喚醒。
DrawFrameTask的成員函數postAndWait的實現如下所示:
~~~
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。
由于DrawFrameTask類描述的就是一個可以添加到Render Thread的Task Queue的Task,因此DrawFrameTask的成員函數postAndWait就將當前正在處理的DrawFrameTask對象添加到由成員變量mRenderThread描述的Render Thread的Task Queue,并且在另外一個成員變量mSignal描述的一個條件變量上進行等待。
從前面Android應用程序UI硬件加速渲染環境初始化過程分析一文可以知道,添加到Render Thread的Task Queue的Task被處理時,它的成員函數run就會被調用,因此接下來DrawFrameTask類的成員函數run就會被調用,它的實現如下所示:
~~~
void DrawFrameTask::run() {
......
bool canUnblockUiThread;
bool canDrawThisFrame;
{
TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
// Grab a copy of everything we need
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
unblockUiThread();
}
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
}
if (!canUnblockUiThread) {
unblockUiThread();
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。
要理解這個函數首先要理解應用程序進程的Main Thread和Render Thread是如何協作的。從前面的分析可以知道,Main Thread請求Render Thread執行Draw Frame Task的時候,不能馬上返回,而是進入等待狀態。等到Render Thread從Main Thread同步完繪制所需要的信息之后,Main Thread才會被喚醒。
那么,Render Thread要從Main Thread同步什么信息呢?原來,Main Thread和Render Thread都各自維護了一份應用程序窗口視圖信息。各自維護了一份應用程序窗口視圖信息的目的,就是為了可以互不干擾,進而實現最大程度的并行。其中,Render Thread維護的應用程序窗口視圖信息是來自于Main Thread的。因此,當Main Thread維護的應用程序窗口信息發生了變化時,就需要同步到Render Thread去。
應用程序窗口的視圖信息包含圖1所示的各個Render Node的Display List、Property以及Display List引用的Bitmap。在RenderNode類中,有六個成員變量是與Display List和Property相關的,如下所示:
~~~
class RenderNode : public VirtualLightRefBase {
public:
......
ANDROID_API void setStagingDisplayList(DisplayListData* newData);
......
const RenderProperties& stagingProperties() {
return mStagingProperties;
}
......
private:
......
uint32_t mDirtyPropertyFields;
RenderProperties mProperties;
RenderProperties mStagingProperties;
bool mNeedsDisplayListDataSync;
// WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
DisplayListData* mDisplayListData;
DisplayListData* mStagingDisplayListData;
......
};
~~~
這個類定義在文件frameworks/base/libs/hwui/RenderNode.h中。
其中,成員變量mStagingProperties描述的Render Properties和成員變量mStagingDisplayListData描述的Display List Data由Main Thread維護,而成員變量mProperties描述的Render Properties和成員變量mDisplayListData描述的Display List Data由Render Thread維護。
這一點可以從前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文看出。當Main Thread構建完成應用程序窗口的Display List之后,就會調用RenderNode類的成員函數setStagingDisplayList將其設置到Root Render Node的成員變量mStagingDisplayListData中去。而當應用程序窗口某一個View的Property發生變化時,就會調用RenderNode類的成員函數mutateStagingProperties獲得成員變量mStagingProperties描述的Render Properties,進而修改相應的Property。
當Main Thread維護的Render Properties發生變化時,成員變量mDirtyPropertyFields的值就不等于0,其中不等于0的位就表示是哪一個具體的Property發生了變化,而當Main Thread維護的Display List Data發生變化時,成員變量mNeedsDisplayListDataSync的值就等于true,表示要從Main Thread同步到Render Thread。
另外,在前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文分析將一個Bitmap繪制命令轉化為一個DrawBitmapOp記錄在Display List時,Bitmap會被增加一個引用,如下所示:
~~~
status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。
參數bitmap描述的SkBitmap通過調用DisplayListRenderer類的成員函數refBitmap進行使用,它的實現如下所示:
~~~
class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
......
inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
......
mDisplayListData->bitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListRenderer.h中。
DisplayListRenderer類的成員函數refBitmap在增加參數bitmap描述的一個SkBitmap的引用計數之前,會將它保存在成員變量mDisplayListData指向的一個DisplayListData對象的成員變量bitmapResources描述的一個Vector中。
上述情況是針對調用GLES20Canvas類的以下成員函數drawBitmap繪制一個Bitmap時發生的情況:
~~~
class GLES20Canvas extends HardwareCanvas {
......
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/GLES20Canvas.java。
我們還可以調用GLES20Canvas類的另外一個重載版本的成員函數drawBitmap繪制一個Bitmap,如下所示:
~~~
class GLES20Canvas extends HardwareCanvas {
......
@Override
public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
int width, int height, boolean hasAlpha, Paint paint) {
......
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, colors, offset, stride, x, y,
width, height, hasAlpha, nativePaint);
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/GLES20Canvas.java。
GLES20Canvas類這個重載版本的成員函數drawBitmap通過一個int數組來指定要繪制的Bitmap。這個int數組是由應用程序自己管理的,并且會被封裝成一個SkBitmap,最終由DisplayListRenderer類的成員函數drawBitmapData將該Bitmap繪制命令封裝成一個DrawBitmapDataOp記錄在Display List中,如下所示:
~~~
status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmapData(bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。
DisplayListRenderer類的成員函數drawBitmapData通過另外一個成員函數refBitmapData來增加參數bitmap描述的SkBitmap的引用,如下所示:
~~~
class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
......
inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
mDisplayListData->ownedBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。
與前面分析的DisplayListRenderer類的成員函數refBitmap不同,DisplayListRenderer類的成員函數refBitmapData將參數bitmap描述的SkBitmap保存在成員變量mDisplayListData指向的一個DisplayListData對象的成員變量ownedBitmapResources描述的一個Vector中。這是由于前者引用的SkBitmap使用的底層存儲是由應用程序提供和管理的,而后者引用的SkBitmap使用的底層存儲是在SkBitmap內部創建和管理的。這個區別在接下來分析Bitmap的同步過程時會進一步得到體現。
Display List引用的Bitmap的同步方式與Display List和Render Property的同步方式有所不同。在同步Bitmap的時候,Bitmap將作為一個Open GL紋理上傳到GPU去被Render Thread使用。
有了這些背景知識之后 ,再回到DrawFrameTask類的成員函數run中,它的執行邏輯如下所示:
1. 調用成員函數syncFrameState將應用程序窗口的Display List、Render Property以及Display List引用的Bitmap等信息從Main Thread同步到Render Thread中。注意,在這個同步過程中,Main Thread是處于等待狀態的。
2. 如果成員函數syncFrameState能順利地完成信息同步,那么它的返回值canUnblockUiThread就會等于true,表示在Render Thread渲染應用程序窗口的下一幀之前,就可以喚醒Main Thread了。否則的話,就要等到Render Thread渲染應用程序窗口的下一幀之后,才能喚醒Main Thread。喚醒Render Thread是通過調用成員函數unblockUiThread來完成的,如下所示:
~~~
void DrawFrameTask::unblockUiThread() {
AutoMutex _lock(mLock);
mSignal.signal();
}
~~~
這個函數定義在frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。
前面Main Thread就剛好是等待在DrawFrameTask類的成員變量mSignal描述的一個條件變量之上的,所以現在Render Thread就通過這個條件變量來喚醒它。
3. 調用成員變量mContext描述的一個CanvasContext對象的成員函數draw渲染應用程序窗口的Display List,不過前提是當前幀能夠進行繪制。什么時候當前幀不能夠進行繪制呢?我們知道,應用程序進程繪制好一個窗口之后,得到的圖形緩沖區要交給Surface Flinger進行合成,最后才能顯示在屏幕上。Surface Flinger為每一個窗口都維護了一個圖形緩沖區隊列。當這個隊列等待合成的圖形緩沖區的個數大于等于2時,就表明Surface Flinger太忙了。因此這時候就最好不再向它提交圖形緩沖區,這就意味著應用程序窗口的當前幀不能繪制了,也就是會被丟棄。
注意,Render Thread渲染應用程序窗口的Display List的時候,Main Thread有可能是處于等待狀態,也有可能不是處于等待狀態。這取決于前面的信息同步結果。信息同步結果是通過一個TreeInfo來描述的。當Main Thread不是處于等待狀態時,它就可以馬上處理其它事情了,例如構建應用程序窗口下一幀時使用的Display List。這樣就可以做到Render Thread在繪制應用程序窗口的當前幀的同時,Main Thread可以并行地去構建應用程序窗口的下一幀的Display List。這一點也是Android 5.0引進Render Thread的好處所在。
接下來,我們就先分析應用程序窗口繪制信息的同步過程,即DrawFrameTask類的成員函數syncFrameState的實現,接著再分析應用程序窗口的Display List的渲染過程,即CanvasContext類的成員函數draw的實現。
DrawFrameTask類的成員函數syncFrameState的實現如下所示:
~~~
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
......
Caches::getInstance().textureCache.resetMarkInUse();
for (size_t i = 0; i < mLayers.size(); i++) {
mContext->processLayerUpdate(mLayers[i].get());
}
......
mContext->prepareTree(info);
if (info.out.hasAnimations) {
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
}
}
// If prepareTextures is false, we ran out of texture cache space
return info.prepareTextures;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。
應用程序進程有一個Caches單例。這個Caches單例有一個成員變量textureCache,它指向的是一個TextureCache對象。這個TextureCache對象用來緩存應用程序窗口在渲染過程中用過的Open GL紋理。在同步應用程序窗口繪制信息之前,DrawFrameTask類的成員函數syncFrameState首先調用這個TextureCache對象的成員函數resetMarkInUse將緩存的Open GL紋理標記為未使用狀態。
在前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文提到,當TextureView有更新時,Native層會將一個與它關聯的DeferredLayerUpdater對象保存在DrawFrameTask類的成員變量mLayers描述的一個vector中。也就是說,保存在這個vector中的DeferredLayerUpdater對象,都是需要進一步處理的。需要做的處理就是從與TextureView關聯的SurfaceTexture中讀出下一個可用的圖形緩沖區,并且將該圖形緩沖區封裝成一個Open GL紋理。這是通過調用DrawFrameTask類的成員變量mContext指向的一個CanvasContext對象的成員函數processLayerUpdate來實現的。
CanvasContext類的成員函數processLayerUpdate的實現如下所示:
~~~
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
bool success = layerUpdater->apply();
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
CanvasContext類的成員函數processLayerUpdate主要是調用參數layerUpdater描述的一個DeferredLayerUpdater對象的成員函數apply讀出下一個可用的圖形緩沖區,并且將該圖形緩沖區封裝成一個Open GL紋理,以便后面可以對它進行渲染。
DeferredLayerUpdater類的成員函數apply的實現如下所示:
~~~
bool DeferredLayerUpdater::apply() {
bool success = true;
......
if (mSurfaceTexture.get()) {
......
if (mUpdateTexImage) {
mUpdateTexImage = false;
doUpdateTexImage();
}
......
}
return success;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredLayerUpdater.cpp中。
DeferredLayerUpdater類的成員變量mSurfaceTexture指向的一個是GLConsumer對象。這個GLConsumer對象用來描述與當前正在處理的DeferredLayerUpdater對象關聯的TextureView對象所使用的一個SurfaceTexture對象的讀端。也就是說,通過這個GLConsumer對象可以將關聯的TextureView對象的下一個可用的圖形緩沖區讀取出來。
從前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文可以知道,當一個TextureView有可用的圖形緩沖區時,與它關聯的DeferredLayerUpdater對象的成員變量mUpdateTexImage值會被設置為true。這時候如果當前正在處理的DeferredLayerUpdater對象的成員變量mSurfaceTexture指向了一個GLConsumer對象,那么現在就是時候去讀取可用的圖形緩沖區了。這是通過調用DeferredLayerUpdater類的成員函數doUpdateTexImage來實現的。
DeferredLayerUpdater類的成員函數doUpdateTexImage的實現如下所示:
~~~
void DeferredLayerUpdater::doUpdateTexImage() {
if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
......
GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight,
!mBlend, forceFilter, renderTarget, transform);
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredLayerUpdater.cpp中。
DeferredLayerUpdater類的成員函數doUpdateTexImage調用成員變量mSurfaceTexture指向的一個GLConsumer對象的成員函數updateTexImage讀出可用的圖形緩沖區,并且將該圖形緩沖區封裝成一個Open GL紋理。這個Open GL紋理可以通過調用上述的GLConsumer對象的成員函數getCurrentTextureTarget獲得了。
接下來DeferredLayerUpdater類的成員函數doUpdateTexImage調用LayerRenderer類的靜態成員函數updateTextureLayer將獲得的Open GL紋理關聯給成員變量mLayer描述的一個Layer對象。
LayerRenderer類的靜態成員函數updateTextureLayer的實現如下所示:
~~~
void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform) {
if (layer) {
......
if (renderTarget != layer->getRenderTarget()) {
layer->setRenderTarget(renderTarget);
......
}
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/LayerRenderer.cpp中。
LayerRenderer類的靜態成員函數updateTextureLayer主要就是將參數renderTarget描述的Open GL紋理設置給參數layer描述的Layer對象。這是通過調用Layer類的成員函數setRenderTarget實現的。一個Layer對象關聯了Open GL紋理之后,以后就可以進行渲染了。
這一步執行完成之后,如果應用程序窗口存在需要更新的TextureView,那么這些TextureView就更新完畢,也就是這些TextureView下一個可用的圖形緩沖區已經被讀出,并且封裝成了Open GL紋理。回到前面分析的DrawFrameTask類的成員函數syncFrameState中,接下來要做的事情是將Main Thread維護的Display List等信息同步到Render Thread中。這是通過調用DrawFrameTask類的成員變量mContext指向的一個CanvasContext對象的成員函數prepareTree實現的。
CanvasContext對象的成員函數prepareTree執行完畢之后,會通過參數info描述的一個TreeInfo對象返回一些同步結果:
1. 當這個TreeInfo對象的成員變量out指向的一個Out對象的成員變量hasAnimations等于true時,表示應用程序窗口存在未完成的動畫。如果這些未完成的動畫至少存在一個是非異步動畫時,上述Out對象的成員變量requiresUiRedraw的值就會被設置為true。這時候DrawFrameTask類的成員變量mSyncResult的kSync_UIRedrawRequired位就會被設置為1。所謂非異步動畫,就是那些在執行過程可以停止的動畫。這個停止執行的邏輯是由Main Thread執行的,例如,Main Thread可以響應用戶輸入停止執行一個非異步動畫。從前面分析可以知道,DrawFrameTask類的成員變量mSyncResult的值最后將會返回給Java層的ThreadedRenderer類的成員函數draw。ThreadedRenderer類的成員函數draw一旦發現該值的kSync_UIRedrawRequired位被設置為1,那么就會向Main Thread的消息隊列發送一個INVALIDATE消息,以便在處理這個INVALIDATE消息的時候,可以響應停止執行非異步動畫的請求。
2. 當這個TreeInfo對象的成員變量prepareTextures的值等于true時,表示應用程序窗口的Display List引用到的Bitmap均已作為Open GL紋理上傳到了GPU。這意味著應用程序窗口的Display List引用到的Bitmap已全部同步完成。在這種情況下,Render Thread在渲染下一幀之前,就可以喚醒Main Thread。另一方面,如果上述TreeInfo對象的成員變量prepareTextures的值等于false,就意味著應用程序窗口的Display List引用到的某些Bitmap不能成功地作為Open GL紋理上傳到GPU,這時候Render Thread在渲染下一幀之后,才可以喚醒Main Thread,防止這些未能作為Open GL紋理上傳到GPU的Bitmap一邊被Render Thread渲染,一邊又被Main Thread修改。那么什么時候應用程序窗口的Display List引用到的Bitmap會不能成功地作為Open GL紋理上傳到GPU呢?一個應用程序進程可以創建的Open GL紋理是有大小限制的,如果超出這個限制,那么就會導至某些Bitmap不能作為Open GL紋理上傳到GPU。
接下來,我們就繼續分析CanvasContext類的成員函數prepareTree的實現,以便可以了解應用程序窗口的Display List等信息的同步過程,如下所示:
~~~
void CanvasContext::prepareTree(TreeInfo& info) {
......
info.renderer = mCanvas;
......
mRootRenderNode->prepareTree(info);
......
int runningBehind = 0;
// TODO: This query is moderately expensive, investigate adding some sort
// of fast-path based off when we last called eglSwapBuffers() as well as
// last vsync time. Or something.
mNativeWindow->query(mNativeWindow.get(),
NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
info.out.canDrawThisFrame = !runningBehind;
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
if (!info.out.requiresUiRedraw) {
// If animationsNeedsRedraw is set don't bother posting for an RT anim
// as we will just end up fighting the UI thread.
mRenderThread.postFrameCallback(this);
}
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
CanvasContext類的成員變量mRootRenderNode指向的一個RenderNode對象描述的是應用程序窗口的Root Render Node,這里通過調用它的成員函數prepareTree開始對應用程序窗口的各個View的Display List進行同步。
在這個同步的過程中,如果某些View設置了動畫,并且這些動還未執行完成,那么參數info指向的TreeInfo對象的成員變量hasAnimations的值就會等于true。這時候如果應用程序窗口的下一幀不可以渲染,即上述TreeInfo對象的成員變量canDrawThisFrame的值等于false,并且所有View設置的動畫都是非異步的,即上述TreeInfo對象的成員變量requiresUiRedraw的值等于false,那么就需要解決一個問題,那些未執行完成的動畫如何繼續執行下去?因為等到當應用程序窗口的下一幀可以渲染時,這些未完成的動畫還是需要繼續執行的。
我們知道,當TreeInfo對象的成員變量requiresUiRedraw的值等于true時,Main Thread會自動發起渲染應用程序窗口的Display List的命令。在這個命令的執行過程中,未完成的動畫是可以繼續執行的。但是當TreeInfo對象的成員變量requiresUiRedraw的值等于false時,Main Thread不會自動發起渲染應用程序窗口的Display List的命令,這時候就需要向Render Thread注冊一個IFrameCallback接口,這是通過調用CanvasContext類的成員變量mRenderThread指向的一個RenderThread對象的成員函數postFrameCallback實現的
從前面Android應用程序UI硬件加速渲染環境初始化過程分析一文可以知道,注冊到Render Thread的IFrameCallback接口在下一個Vsync信號到來時,它的成員函數doFrame會被調用,這時候就可以執行渲染應用程序窗口的下一幀了。在渲染的過程中,就可以繼續執行那些未完成的動畫了。
CanvasContext類的成員變量mNativeWindow描述的就是當前綁定的應用程序窗口,通過調用它的成員函數query,并且將第二個參數設置為NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND,可以查詢到它提交了多少個圖形緩沖區還未被處理。如果這些已提交了但是還沒有被處理的圖形緩沖區大于等于2,輸出參數runningBehind就會等于true,這表明Surface Flinger太忙了,這時候應用程序窗口就應該丟棄當前幀,因此就將參數info指向的TreeInfo對象的成員變量canDrawThisFrame的值設置為false。
接下來我們繼續分析RenderNode類的成員函數prepareTree的實現,以便可以了解對應用程序窗口的各個View的Display List的同步過程,如下所示:
~~~
void RenderNode::prepareTree(TreeInfo& info) {
......
prepareTreeImpl(info);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
RenderNode類的成員函數prepareTree調用另外一個成員函數prepareTreeImpl來同步當前正在處理的Render Node的Display List等信息,后者的實現如下所示:
~~~
void RenderNode::prepareTreeImpl(TreeInfo& info) {
......
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingPropertiesChanges(info);
}
uint32_t animatorDirtyMask = 0;
if (CC_LIKELY(info.runAnimations)) {
animatorDirtyMask = mAnimatorManager.animate(info);
}
......
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
prepareSubTree(info, mDisplayListData);
pushLayerUpdate(info);
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
從前面分析的DrawFrameTask類的成員函數run可以知道,參數info指向的TreeInfo對象的成員變量mode的值等于TreeInfo::MODE_FULL,這意味著RenderNode類的成員函數prepareTreeImpl執行在同步模式中,這時候它將會執行以下五個操作:
1. 調用成員函數pushStagingPropertiesChanges同步當前正在處理的Render Node的Property。
2. 在參數info指向的TreeInfo對象的成員變量runAnitmations的值等于true的前提下,調用成員變量mAnimatorManager指向的一個AnimatorManager對象的成員函數animate執行動畫相關的操作。
3. 調用成員函數pushStagingDisplayListChanges同步當前正在處理的Render Node的Display List。
4. 調用成員函數prepareSubTree同步當前正在處理的Render Node的Display List引用的Bitmap,以及當前正在處理的Render Node的子Render Node的Display List等信息。
5. 調用成員函數pushLayerUpdate檢查當前正在處理的Render Node是否設置了Layer。如果設置了的話,就對這些Layer進行處理。
其中,第2個操作是與動畫顯示相關的,我們在接下來的一篇文章再詳細分析。
與第1個操作相關的函數是RenderNode類的成員函數pushStagingPropertiesChanges,它的實現如下所示:
~~~
void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
......
if (mDirtyPropertyFields) {
mDirtyPropertyFields = 0;
......
mProperties = mStagingProperties;
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
前面提到,當RenderNode類的成員變量mDirtyPropertyFields的值不等于0時,就表明Main Thread維護的Render Node的Property發生了變化,因此就需要將它同步到Render Thread去,也就是將成員變量mStagingProperties描述的RenderProperties對象轉移到成員變量mProperties去。
與第3個操作相關的函數是RenderNode類的成員函數pushStagingDisplayListChanges,它的實現如下所示:
~~~
void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
if (mNeedsDisplayListDataSync) {
mNeedsDisplayListDataSync = false;
......
deleteDisplayListData();
mDisplayListData = mStagingDisplayListData;
mStagingDisplayListData = NULL;
if (mDisplayListData) {
for (size_t i = 0; i < mDisplayListData->functors.size(); i++) {
(*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, NULL);
}
}
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
前面提到,當RenderNode類的成員變量mNeedsDisplayListDataSync的值等于true時,就表明Main Thread維護的Render Node的Display List發生了變化,因此就需要將它同步到Render Thread去,也就是將成員變量mStagingDisplayListData描述的DisplayListData對象轉移到成員變量mDisplayListData去。
在將成員變量mStagingDisplayListData描述的DisplayListData對象轉移到成員變量mDisplayListData去之前,首先會調用成員函數deleteDisplayListData刪除成員變量mDisplayListData原先描述的DisplayListData對象。
記錄在Display List Data的繪制命令除了是一些普通的DrawOp之外,還可能是一些函數指針,這些函數指針保存在Display List Data的成員變量functors描述的一個Vector中。這些函數指針是做什么用的呢?有些繪制命令很復雜,是不能通過一個簡單的DrawOp來描述的,例如它是由一系列簡單的繪制命令以復雜方式組合在一起形成的。對于這些復雜的繪制命令,我們就可以通過一個函數指針來描述。當Render Thread渲染應用程序窗口的Display List遇到這些函數指針時,就會調用這些函數指針指向的函數,這樣這些函數就可以在其內部實現復雜的繪制命令,或者說是完成自定義的繪制命令。
這些函數指針在應用程序窗口的Display List的渲染過程中,會被調用兩次。第一次調用時,第一個參數指定為DrawGlInfo::kModeSync,表示這時候它可以執行一些同步相關的操作。第二次調用時,第二個參數指定為DrawGlInfo::kModeDraw,表示這時候可以執行一些與渲染相關的操作。
此外,我們還可以通過Java層的ThreadedRenderer類的靜態成員函數invokeFunctor將一個函數指定在Render Thread執行。例如,我們希望在應用程序進程中執行一些Open GL相關的操作時,就可以將這些操作封裝在一個函數中,并且將該函數的地址封裝成一個Task發送到Render Thread的Task Queue中。當這個Task被Render Thread處理的時候,封裝在這個Task里面的函數就會被執行。這時候傳遞給這些函數的第一個參數就為DrawGlInfo::kModeProcess或者DrawGlInfo::kModeProcessNoContext。其中,DrawGlInfo::kModeProcess表示Render Thread已經初好了Open GL環境,而DrawGlInfo::kModeProcessNoContext表示Render Thread還沒有初始化Open GL環境。
將函數指針記錄Display List中交給Main Thread和Render Thread執行以及將函數指針封裝成Task交給Render Thread執行的設計主要是為了實現WebView功能的。Android系統從4.4開始,通過Chromium來實現WebView的功能。Chromium有一套非常復雜的網頁渲染機制,當它通過WebView嵌入在應用程序進程執行時,就會需要利用Render Thread可以執行Open GL操作的能力來完成它自己的功能。由這些網頁渲染操作很復雜,因此就最好是通過函數來描述,這樣就產生了能夠將函數指定在Render Thread執行的需求。以后如果有機會分析WebView然Chromium版實現,我們就會看到這一套機制是如何運行的。
回到RenderNode類的成員函數prepareTree中,與第4個操作相關的函數是RenderNode類的成員函數prepareSubTree,它的實現如下所示:
~~~
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
......
if (subtree->ownedBitmapResources.size()) {
info.prepareTextures = false;
}
for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
DrawRenderNodeOp* op = subtree->children()[i];
RenderNode* childNode = op->mRenderNode;
......
childNode->prepareTreeImpl(info);
.....
}
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
前面提到,Display List引用的Bitmap保存在它的成員變量ownedBitmapResources和bitmapResources的兩個Vector中。其中,保存在Display List的成員變量ownedBitmapResources中的Bitmap的底層儲存是由應用程序提供和管理的。這意味著很難維護該底層儲存在Main Thread和Render Thread的一致性。例如,有可能應用程序自行修改了該底層儲存的內容,但是又沒有通知Render Thread進行同步。因此,當存在這樣的Bitmap時,就不允許Render Thread在渲染完成應用程序窗口的一幀之前喚醒Main Thread,就是為了防止Main Thread會修改上述Bitmap的底層儲存。為了達到這個目的,這時候就需要將參數info指向的一個TreeInfo對象的成員變量prepareTextures的值設置為false。
另一方面,保存在Display List的成員變量bitmapResources中的Bitmap的底層儲存不是由應用程序提供和管理的,因此就能夠保證它不會被隨意修改而又不通知Render Thread進行同步。對于這些Bitmap,就可以將它們作為Open GL紋理上傳到GPU去。這就相當于是將Bitmap從Main Thread同步到Render Thread中,因為Render Thread就通過已經上傳到GPU的Open GL紋理來使用這些Bitmap。能夠執行這樣的同步操作的前提是Display List的成員變量ownedBitmapResources描述的Vector為空。因為當Display List的成員變量ownedBitmapResources描述的Vector不為空時,Main Thread和Render Thread在渲染應用程序窗口的一幀時是完全同步的,因此就沒有必要將Bitmap從Main Thread同步到Render Thread去。
此外,對于保存在Display List的成員變量bitmapResources中的Bitmap,由于內存大小的限制,因此就不是所有的這些Bitmap都是能夠作為Open GL紋理上傳到GPU去的。一旦某一個Bitmap不能作為Open GL紋理上傳到GPU去,那么也是需要完全同步Main Thread和Render Thread渲染應用程序窗口的一幀的。這時候就需要將參數info指向的一個TreeInfo對象的成員變量prepareTextures的值設置為false。
同步完成當前正在處理的Render Node的Display List引用的Bitmap之后,接下來RenderNode類的成員函數prepareSubTree就調用前面分析過的成員函數prepareTreeImpl來同步當前在處理的Render Node的子Render Node的Display List、Property和Display List引用的Bitmap等信息。這個過程是一直歸遞執行下去,直到應用程序窗口視圖結構中的每一個Render Node的isplay List、Property和Display List引用的Bitmap等信息都從Main Thread同步到Render Thread為止。
上面提到的將Bitmap封裝成Open GL紋理上傳到GPU是通過調用TextureCache類的成員函數prefetchAndMarkInUse來實現的,如下所示:
~~~
bool TextureCache::prefetchAndMarkInUse(const SkBitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap);
if (texture) {
texture->isInUse = true;
}
return texture;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/TextureCache.cpp中。
TextureCache類的成員函數prefetchAndMarkInUse調用另外一個成員函數getCachedTexture將參數bitmap描述的Bitmap封裝成Open Gl紋理上傳到GPU中。如果能夠上傳成功,那么就可以獲得一個Texture對象。TextureCache類的成員函數prefetchAndMarkInUse在將這個Texture對象返回給調用者之前,需要將它的成員變量isInUse設置為true,表示該Texture對象正在使用當中。
TextureCache類的成員函數getCachedTexture的實現如下所示:
~~~
Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
Texture* texture = mCache.get(bitmap->pixelRef());
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
return NULL;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
bool canCache = size < mMaxSize;
// Don't even try to cache a bitmap that's bigger than the cache
while (canCache && mSize + size > mMaxSize) {
Texture* oldest = mCache.peekOldestValue();
if (oldest && !oldest->isInUse) {
mCache.removeOldest();
} else {
canCache = false;
}
}
if (canCache) {
texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
mSize += size;
......
mCache.put(bitmap->pixelRef(), texture);
}
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
// TODO: Re-adjust the cache size if the bitmap's dimensions have changed
generateTexture(bitmap, texture, true);
}
return texture;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/TextureCache.cpp中。
每一個Bitmap作為Open GL紋理上傳到GPU后,都會為其創建一個Texture對象。這些Texture對象就保存在TextureCache類通過成員變量mCache指向的一個LruCache中。因此,當不能夠在該LruCache中找到參數bitmap描述的Bitmap對應的Texture對象時,就說明該Bitmap還未作為Open GL紋理上傳到過GPU中,因此接下來就需要將它作為Open GL紋理上傳到GPU去。
但是參數bitmap描述的Bitmap卻不一定能夠成功作為Open GL紋理上傳到GPU去,有兩個原因:
1. Bitmap太大,超出預先設定的最大Open GL紋理的大小。這種情況通過調用TextureCache類的成員函數canMakeTextureFromBitmap進行判斷。
2. 已經作為Open GL紋理上傳到GPU的Bitmap太多,超出預先設定的最多可以上傳到GPU的大小。
在第2種情況下,這時候TextureCache類的成員函數getCachedTexture會嘗試刪掉那些最早上傳到GPU的現在還不處于使用狀態的Open GL紋理,直到能滿足將參數bitmap描述的Bitmap作為Open GL紋理上傳到GPU為止。
一旦確定能夠將參數bitmap描述的Bitmap作為Open GL紋理上傳到GPU,那么就會調用TextureCache類的成員函數generateTexture執行具體的操作,并且創建為其創建一個Texture對象保存在成員變量mCache指向的一個LruCache中。
另一方面,如果參數bitmap描述的Bitmap之前已經作為Open GL紋理上傳到過GPU中,由于現在它的內容可能已經發生了變化,因此也需要調用TextureCache類的成員函數generateTexture執行重新上傳的操作。
回到RenderNode類的成員函數prepareTree中,與第5個操作相關的函數是RenderNode類的成員函數pushLayerUpdate,它的實現如下所示:
~~~
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().layerProperties().type();
......
if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
LayerRenderer::destroyLayer(mLayer);
mLayer = NULL;
}
return;
}
......
if (!mLayer) {
mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
......
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
......
}
......
}
SkRect dirty;
info.damageAccumulator->peekAtDirty(&dirty);
......
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
......
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
}
......
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
在分析RenderNode類的成員函數pushLayerUpdate的實現之前,我們首先要理解什么情況下一個Render Node會被設置為一個Layer。
當一個View的類型被設置為LAYER_TYPE_HARDWARE時,如果它的成員函數buildLayer被調用,那么與它關聯的Render Node就會被設置為一個Layer。這意味著該View將會作為一個FBO(Frame Buffer Object)進行渲染。這樣做主要是為了更流暢地顯示一個View的動畫。這一點我們在前面Android應用程序UI硬件加速渲染技術簡要介紹和學習計劃一文中曾經提到。
有了這個背景知識之后,我們就可以分析RenderNode類的成員函數pushLayerUpdate的實現了。
RenderNode類的成員函數pushLayerUpdate首先是判斷當前正在處理的Render Node的Layer Type是否為kLayerTypeRenderLayer,也就是判斷與它關聯的View的類型是否設置為LAYER_TYPE_HARDWARE。如果不是,那么就不用往下執行了,因為這種情況當前正在處理的Render Node不可能設置為一個Layer。
另一方面,如果當前正在處理的Render Node的Display List還沒有創建或者是空的,那么RenderNode類的成員函數pushLayerUpdate也不用往下執行了,因為這種情況當前正在處理的Render Node是無需要渲染的。判斷當前正在處理的Render Node的Display List有沒有創建或者是不是空的,可以通過調用RenderNode類的成員函數isRenderable來實現。
在上述兩種情況下,RenderNode類的成員函數pushLayerUpdate在返回之前,會判斷一下之前是否已經為當前正在處理的Render Node創建過Layer。如果創建過,那么就會調用LayerRenderer類的靜態成員函數destroyLayer來銷毀該Layer。
接下來就是當前正在處理的Render Node需要設置為一個Layer的情況了。如果當前正在處理的Render Node還沒有設置過Layer,也就是它的成員變量mLayer的值等于NULL,那么就調用LayerRenderer類的靜態成員函數createRenderLayer為它設置一個,也就是創建一個Layer對象,并且保存在它的成員變量mLayer中。
另一方面,如果當前正在處理的Render Node之前已經設置過Layer,但是該Layer的大小與當前正在處理的Render Node的大小不一致,那么就需要調用LayerRenderer類的靜態成員函數resizeLayer調整廖Layer的大小。
再接下來是計算當前正在處理的Render Node是否在應用程序窗口當前要更新的臟區域中。如果在的話,那么就需要調用與它關聯的Layer對象的成員函數updateDeferred來標記與它關聯的Layer對象是需要進行更新處理的。
Layer類的成員函數updateDeferred的實現如下所示:
~~~
void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) {
requireRenderer();
this->renderNode = renderNode;
const Rect r(left, top, right, bottom);
dirtyRect.unionWith(r);
deferredUpdateScheduled = true;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/Layer.cpp中。
Layer類的成員函數updateDeferred首先是調用另外一個成員函數requireRenderer檢查當前正在處理的Layer對象是否已經創建有一個LayerRenderer對象了。這個LayerRenderer對象就是負責渲染當前正在處理的Layer對象的。如果還沒有創建,那么就需要創建。如下所示:
~~~
void Layer::requireRenderer() {
if (!renderer) {
renderer = new LayerRenderer(renderState, this);
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/Layer.cpp中。
Layer類的成員函數updateDeferred接下來再記錄當前正在處理的Layer對象關聯的Render Node,并且更新它的臟區域,最后將成員變量deferredUpdateScheduled設置為true,表示當前正在處理的Layer對象后面還需要執行真正的更新操作,而這里只是記錄了相關的更新狀態信息而已。
這一步執行完成后,回到RenderNode類的成員函數pushLayerUpdate中,這時候成員變量mLayer指向的Layer對象的成員變量deferredUpdateScheduled的值是等于true的,并且參數info指向的一個TreeInfo對象的成員變量renderer的值不為空,它指向了一個OpenGLRenderer對象,因此接下來就會調用該OpenGLRenderer對象的成員函數pushLayerUpdate來將成員變量mLayer指向的Layer對象記錄在內部的一個待更新的Layer列表中,如下所示:
~~~
void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
if (layer) {
......
for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
if (mLayerUpdates.itemAt(i) == layer) {
return;
}
}
mLayerUpdates.push_back(layer);
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
OpenGLRenderer類將需要進行更新處理的Layer對象保存在成員變量mLayerUpdates描述的一個Vector中,保存在這個Vector中的Layer對象在渲染應用程序窗口的Display List的時候,就是需要進行更新處理的。
這一步執行完成之后,應用程序窗口的Display List等信息就從Main Thread同步到Render Thread了,回到DrawFrameTask類的成員函數run中,接下來就可以調用CanvasContext類的成員函數draw渲染應用程序窗口的Display List了。
CanvasContext類的成員函數draw的實現如下所示:
~~~
void CanvasContext::draw() {
......
SkRect dirty;
mDamageAccumulator.finish(&dirty);
......
status_t status;
if (!dirty.isEmpty()) {
status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom, mOpaque);
} else {
status = mCanvas->prepare(mOpaque);
}
Rect outBounds;
status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
......
mCanvas->finish();
......
if (status & DrawGlInfo::kStatusDrew) {
swapBuffers();
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
CanvasContext類的成員函數draw的執行過程如下所示:
1. 獲得應用程序窗口要更新的臟區域之后,調用成員變量mCanvas指向的一個OpenGLRenderer對象的成員函數prepareDirty或者prepare執行一些初始化工作,取決于臟區域是不是空的。
2, 調用成員變量mCanvas指向的一個OpenGLRenderer對象的成員函數drawRenderNode渲染成員變量mRootRenderNode描述的應用程序窗口的Root Render Node的Display List。
3. 調用成員變量mCanvas指向的一個OpenGLRenderer對象的成員函數finish執行一些清理工作。在這一步中,如果開啟了OverDraw,那么還會在應用程序窗口的上面繪制一些描述OverDraw的顏色塊。
4. 調用另外一個成員函數swapBuffers將前面已經繪制好的圖形緩沖區提交給Surface Flinger合成和顯示。
在上述四個步驟中,最重要的是第1步和第2步,因此接下來我們就分別對它們進行分析。
我們假設第1步得到的應用程序窗口要更新的臟區域不為空,因此這一步執行的就是OpenGLRenderer類的成員函數prepareDirty,它的實現如下所示:
~~~
status_t OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
setupFrameState(left, top, right, bottom, opaque);
......
if (currentSnapshot()->fbo == 0) {
......
updateLayers();
} else {
return startFrame();
}
return DrawGlInfo::kStatusDone;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
OpenGLRenderer類的成員函數prepareDirty首先是調用另外一個成員函數setupFrameState設置幀狀態,它的實現如下所示:
~~~
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
......
initializeSaveStack(left, top, right, bottom, mLightCenter);
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
OpenGLRenderer類的成員函數setupFrameState最主要的操作是調用另外一個成員函數initializeSaveStack初始化一個Save Stack。
OpenGLRenderer類的成員函數initializeSaveStack是從父類StatefulBaseRenderer繼承下來的,它的實現如下所示:
void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
~~~
float clipRight, float clipBottom, const Vector3& lightCenter) {
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = getTargetFbo();
mSnapshot->setRelativeLightCenter(lightCenter);
mSaveCount = 1;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/StatefulBaseRenderer.cpp中。
StatefulBaseRenderer類內部維護有一個Save Stack。這個Save Stack由一系列的Snapshot組成,其中最頂端的Snapshot,也就是當前使用的Snapshot,保存成員變量mSnapshot中。每一個Snapshot都是用來描述當前的一個渲染狀態,例如偏移位置、裁剪區間、燈光位置等。
Snapshot有一個重要的成員變量fbo。當它的值大于0的時候,就表示要將UI渲染在一個FBO上。涉及到渲染UI的Renderer有兩個,一個是LayerRenderer,另外一個是OpenGLRenderer。從前面的分析可以知道,LayerRenderer主要負責用來渲染類型為LAYER_TYPE_HARDWARE的View。這些View將會渲染在一個FBO上。OpenGLRenderer負責渲染應用程序窗口的Display List。這個Display List是直接渲染在Frame Buffer上的,也就是直接渲染在從Surface Flinger請求回來的圖形緩沖區上。
LayerRenderer類繼承于OpenGLRenderer類,OpenGLRenderer類又繼承于StatefulBaseRenderer類。StatefulBaseRenderer類的成員函數getTargetFbo是一個虛函數,LayerRenderer類和OpenGLRenderer類都重寫了它。
其中,OpenGLRenderer類的成員函數getTargetFbo的實現如下所示:
~~~
class OpenGLRenderer : public StatefulBaseRenderer {
......
virtual GLuint getTargetFbo() const {
return 0;
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.h。
從這里就可以看到,OpenGLRenderer類的成員函數getTargetFbo的返回值總是0,也就是說,OpenGLRenderer類總是直接將UI渲染在Frame Buffer上。
LayerRenderer類的成員函數getTargetFbo的實現如下所示:
~~~
GLuint LayerRenderer::getTargetFbo() const {
return mLayer->getFbo();
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/LayerRenderer.cpp。
LayerRenderer類的成員變量mLayer描述的是一個Layer對象。這個Layer對象關聯有一個FBO對象,可以通過調用它的成員函數getFbo獲得。獲得FBO被LayerRenderer類的成員函數getTargetFbo返回給調用者。
回到前面分析的StatefulBaseRenderer類的成員函數initializeSaveStack中,從前面的調用過程可以知道,當前正在處理的是一個OpenGLRenderer對象,因此,成員變量mSnapshot指向的一個Snapshot對象的成員變量fbo的值等于0。
StatefulBaseRenderer類的成員函數initializeSaveStack執行完成后,回到OpenGLRenderer類的成員函數prepareDirty中,它調用另外一個成員函數currentSnapshot獲得的就是父類StatefulBaseRenderer的成員變量mSnapshot描述的Snapshot對象。這個Snapshot對象的成員變量fbo的值是等于0的,因此接下來就會繼續調用OpenGLRenderer類的成員函數updateLayers更新那些待更析的Layer對象。
另一方面,如果當前正在處理的是一個LayerRenderer對象,那么OpenGLRenderer類的成員函數prepareDirty調用的是另外一個成員函數startFrame執行一些Open GL初始化工作,例如設置View Port等基本操作。
由于當前正在處理的是一個OpenGLRenderer對象,因此我們接下來繼續分析OpenGLRenderer類的成員函數updateLayers的實現,如下所示:
~~~
void OpenGLRenderer::updateLayers() {
......
int count = mLayerUpdates.size();
if (count > 0) {
......
for (int i = 0; i < count; i++) {
Layer* layer = mLayerUpdates.itemAt(i);
updateLayer(layer, false);
......
}
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
前面提到,OpenGLRenderer類的成員變量mLayerUpdates描述的一個Vector保存的都是那些需要更新的Layer對象。每一個Layer對象的更新是通過調用OpenGLRenderer類的另外一個成員函數updateLayer實現的。
OpenGLRenderer類的成員函數updateLayer的實現如下所示:
~~~
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (layer->deferredUpdateScheduled && layer->renderer
&& layer->renderNode.get() && layer->renderNode->isRenderable()) {
......
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
layer->render(*this);
} else {
layer->defer(*this);
}
......
return true;
}
return false;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
從前面的分析可以知道,保存在OpenGLRenderer類的成員變量mLayerUpdates描述的一個Vector中的Layer對象,它的成員變量deferredUpdateScheduled的值是等于true的。當這些Layer對象設置有自己的Renderer,以及關聯有Render Node,并且這個Render Node是可渲染的時候,就會調用它們的成員函數render進行直接更新,或者調用它們的成員函數defer進行延遲更新。
當參數inFrame的值等于true,或者OpenGLRenderer類的成員變量mCaches指向的一個Caches對象的成員變量drawDeferDisabled的值等于true時,就會調用Layer類的成員函數render進行直接更新。其中,Caches類的成員變量drawDeferDisabled用來描述是否要對Open GL操作進行合并。當它的值等于true時,就表示不要合并;否則就表示需要合并。關于Open GL操作的合并,我們在前面Android應用程序UI硬件加速渲染的預加載資源地圖集服務(Asset Atlas Service)分析一文中有提到。
我們假設Open GL操作需要進行合并,即OpenGLRenderer類的成員變量mCaches指向的一個Caches對象的成員變量drawDeferDisabled等于false。從前面的調用過程可以知道,參數inFrame的值也是等于false,因此接下來OpenGLRenderer類的成員函數updateLayer就會調用Layer類的成員函數defer對參數layer描述的一個Layer對象進行更新。
Layer類的成員函數defer的實現如下所示:
~~~
void Layer::defer(const OpenGLRenderer& rootRenderer) {
......
delete deferredList;
deferredList = new DeferredDisplayList(dirtyRect);
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
......
renderNode->computeOrdering();
renderNode->defer(deferredState, 0);
deferredUpdateScheduled = false;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/Layer.cpp中。
Layer類的成員函數defer的主要工作是創建一個DeferredDisplayList對象,保存在成員變量deferredList中,然后再將該DeferredDisplayList對象封裝成一個DeferStateStruct對象中。同時被封裝成這個DeferStateStruct對象還有Layer類的成員變量renderer描述的一個LayerRenderer對象。
Layer類的成員變量renderNode描述的是當前正在處理的Layer對象所關聯的Render Node。Layer類的成員函數defer接下來就分別調用這個Render Node的成員函數computeOrdering和defer。其中,調用Render Node的成員函數defer的時候,會將前面創建的DeferStateStruct對象作為參數傳遞進去。
調用一個Render Node的成員函數computeOrdering,是為了找出那些需要投影到它的Background進行渲染的子Render Node。這些子Render Node稱為Projected Node,如下所示:
:-: 
圖3 Projection Nodes
Projection Node的解釋可以參考前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文,RenderNode類的成員函數computeOrdering的實現我們也留給讀者自己去分析。最終如果一個Rende Node具有Projected Node,那么這些Projected Node就會保存在它的成員變量mProjectedNodes中。
回到前面分析的Layer類的成員函數defer中,接下來它要調用的是RenderNode類的成員函數defer,它的實現如下所示:
~~~
void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
DeferOperationHandler handler(deferStruct, level);
issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
RenderNode類的成員函數defer調用另外一個成員函數issueOperations對當前正在處理的Render Node的Display List的繪制命令進行處理,具體的處理是由第二個參數指定的一個DeferOperationHandler對象的操作符重載函數()執行的,如下所示:
~~~
class DeferOperationHandler {
public:
DeferOperationHandler(DeferStateStruct& deferStruct, int level)
: mDeferStruct(deferStruct), mLevel(level) {}
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
}
......
private:
DeferStateStruct& mDeferStruct;
const int mLevel;
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
參數operation指向的就是當前正在處理的Render Node的Display List的一個繪制命令,這里調用它的成員函數defer執行我們在前面Android應用程序UI硬件加速渲染的預加載資源地圖集服務(Asset Atlas Service)分析一文提到的繪制命令合并操作。
接下來,我們首先分析RenderNode類的成員函數issueOperations,然后再分析一個典型的DisplayListOp的成員函數defer的實現。
RenderNode類的成員函數issueOperations的實現如下所示:
~~~
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
......
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
......
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
......
if (drawLayer) {
handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
renderer.getSaveCount() - 1, properties().getClipToBounds());
} else {
......
for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
buildZSortedChildList(chunk, zTranslatedNodes);
issueOperationsOf3dChildren(kNegativeZChildren,
initialTransform, zTranslatedNodes, renderer, handler);
for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
......
handler(op, saveCountOffset, properties().getClipToBounds());
if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && opIndex == projectionReceiveIndex)) {
issueOperationsOfProjectedChildren(renderer, handler);
}
}
issueOperationsOf3dChildren(kPositiveZChildren,
initialTransform, zTranslatedNodes, renderer, handler);
}
}
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
RenderNode類的成員函數issueOperations執行的操作就是對當前正在處理的Render Node的Display List的繪制命令進行重排。為什么需要重排呢?在前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文中,我們分析Display List的結構,如圖4所示:
:-: 
圖4 Display List
Display List的繪制命令以Chunk為單位進行保存。每一個Chunk通過begin op index和end op index描述的一系列Display List Op對應的就是一個Render Node包含繪制命令。此外,每一個Chunk還通過begin child index和end child index描述的一系列Draw Render Node Op對應的就是一個Render Node的子Render Node相關的繪制命令。這些子Render Node的Z軸位置相對父Render Node有可能是負的,也有可能是正的。對于Z軸位置為負的子Render Node的繪制命令,它們應該先于父Render Node的繪制命令執行。而對于Z軸位置為正的子Render Node的繪制命令,它們應該后于父Render Node的繪制命令執行。因此,每一個Chunk描述的繪制命令的排列順序就如下所示:
1. Z軸位置為負的子Render Node的繪制命令。
2. 父Render Node的繪制命令。
3. Z軸位置為正的子Render Node的繪制命令。
此外,如果一個Render Node的的某一個Display List Op恰好是一個圖3所示的Projection Receiver,那么還需要Render Node的所有Projected Node的繪制命令排列在該Projection Receiver的后面。
如果一個Render Node設置了Layer,那么就意味著這個Render Node的所有繪制命令都是作為一個整體進行執行的。也就是說,對于設置了Layer的Render Node,我們首先需要將它的Display List的所有繪制命令合成一個整體的繪制命令,目的就是為了得到一個FBO,然后渲染這個FBO就可以得一個Render Node的UI。
對于設置了Layer的Render Node來說,它的成員函數defer會被調用兩次。第一次調用的時候,就是為了將它的Display List的所有繪制命令合成一個FBO。第二次調用的時候,就是為了將合成后的FBO渲染到應用程序窗口的UI上。
這時候RenderNode類的成員函數defer屬于第一次執行。那么RenderNode類的成員函數issueOperations是如何區分它是被第一次調用的成員函數defer調用,還是第二次調用的成員函數defer調用呢?主要是通過比較參數renderer描述的OpenGLRender對象和成員變量mLayer指向的一個Layer對象的成員變量renderer描述折一個OpenGLRender對象來區分。如果這兩個OpenGLRenderer對象是同一個,就意味著是被第一次調用的成員函數defer調用;否則的話,就是被第二次調用的成員函數defer調用。
當RenderNode類的成員函數issueOperations是被第二次調用的成員函數defer調用的時候,該Render Node的Display List的所有繪制命令已經被合成在一個FBO里面,并且這個FBO是由它所關聯的Layer對象維護的,因此這時候只需要將該Layer對象封裝成一個DrawLayerOp交給參數handler描述的一個DeferOperationHandler對象處理即可。
我們再確認一下現在RenderNode類的成員函數issueOperations是被第一次調用的成員函數defer調用。它的參數renderer指向的一個OpenGLRenderer對象是從Layer類的成員函數defer傳遞進行的,而Layer類的成員函數defer傳遞進行的這個OpenGLRenderer對象就正好是與Render Node關聯的Layer對象的成員變量renderer描述折一個OpenGLRender對象,因此它們就是相同的。從前面的分析可以知道,這個OpenGLRenderer對象的實際類型是LayerRenderer。
后面我們會看到,當Render Node的成員函數issueOperations是被第二次調用的成員函數defer調用的時候,它的參數renderer指向的一個OpenGLRenderer對象的實際類型就是OpenGLRenderer,它與當前正在處理的Render Node關聯的Layer對象的成員變量描述折一個OpenGLRender對象不可能是相同的,因為后者的實際類型是LayerRenderer。
接下來我們就繼續分析RenderNode類的成員函數issueOperations是被第一次調用的成員函數defer調用時的執行情況,這時候得到的本地變量drawLayer的值為false。
RenderNode類的成員函數issueOperations首先是判斷當前正在處理的Render Node的占據的屏幕位置在應用程序窗口的當前幀中是否是可見的。如果不可見,那么得到的本地變量quickRejected的值就等于true。在這種情況下就不用做任何事情。
當本地變量quickRejected的值就等于false,并且本地變量drawLayer的值也等于false的時候,RenderNode類的成員函數issueOperations就對當前正在處理的Render Node的Display List的所有繪制命令按照我們上面描述的規則進行排序。
RenderNode類的成員函數issueOperations通過一個for循環對當前正在處理的Render Node的Display List的繪制命令按Chunk進行處理。對于每一個Chunk:
1. 調用成員函數buildZSortedChildList對其子Render Node相關的Draw Render Node Op按照Z軸位置從小到大的順序排列在本地變量zTranslatedNodes描述的一個Vector中。
2. 調用成員函數issueOperationsOf3dChildren將Z軸位置為負的子Render Node相關的Draw Render Node Op交給參數handler描述的一個DeferOperationHandler對象處理。
3. 通過一個for循環依次將當前正在處理的Render Node相關的Display List Op交給參數handler描述的一個DeferOperationHandler對象處理。如果其中的某一個Display List Op是一個Projection Receiver,那么就繼續調用成員函數issueOperationsOfProjectedChildren將當前正在處理的Render Node的Projected Node交給參數handler描述的一個DeferOperationHandler對象處理。
4. 調用成員函數issueOperationsOf3dChildren將Z軸位置為正的子Render Node相關的Draw Render Node Op交給參數handler描述的一個DeferOperationHandler對象處理。
接下來我們繼續分析RenderNode類的成員函數issueOperationsOf3dChildren和issueOperationsOfProjectedChildren的實現。
RenderNode類的成員函數issueOperationsOf3dChildren的實現如下所示:
~~~
template <class T>
void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
OpenGLRenderer& renderer, T& handler) {
const int size = zTranslatedNodes.size();
......
const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
size_t drawIndex, shadowIndex, endIndex;
if (mode == kNegativeZChildren) {
drawIndex = 0;
endIndex = nonNegativeIndex;
shadowIndex = endIndex; // draw no shadows
} else {
drawIndex = nonNegativeIndex;
endIndex = size;
shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
}
......
while (shadowIndex < endIndex || drawIndex < endIndex) {
......
DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
......
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
childOp->mSkipInOrderDraw = true;
......
drawIndex++;
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
RenderNode類的成員函數issueOperationsOf3dChildren既用來處理Z軸位置為負的子Render Node相關的Draw Render Node Op,也用來處理Z軸位置為正的子Render Node相關的Draw Render Node Op,因此它就需要根據參數mode以及參數zTranslatedNodes描述的一個Vector中Z軸位置為非負的子Render Node相關的Draw Render Node Op的索引nonNegativeIndex來確定當前需要處理的子Render Node相關的Draw Render Node Op。
由于參數zTranslatedNodes描述的一個Vector中的Draw Render Node Op是按照它們對應的子Render Node的Z軸位置由小到大的順序排列的,因此如果參數mode的值等于kNegativeZChildren,那么當前需要處理的Draw Render Node Op在參數zTranslatedNodes描述的一個Vector中的索引范圍就為[0, nonNegativeIndex) ]。另一方面,如果參數mode的值等于kPositiveZChildren,,那么當前需要處理的Draw Render Node Op在參數zTranslatedNodes描述的一個Vector中的索引范圍就為[nonNegativeIndex, size)],其中,size為參數zTranslatedNodes描述的一個Vector的大小。
確定了要處理的Draw Render Node Op在參數zTranslatedNodes描述的一個Vector的范圍之后,就可以通過一個while循環對它們進行處理了,處理的方式就將它們交給參數handler描述的一個DeferOperationHandler對象。
在將要處理的Draw Render Node Op交給參數handler描述的一個DeferOperationHandler對象處理之前,有一個小Hack,這些Draw Render Node Op的成員變量mSkipInOrderDraw的值設置為false,處理完成之后再恢復為true。這樣做的目的是為了當前正在處理的Render Node以相同的方式遞歸處理其子Render Node的Display List的繪制命令。我們在后面將會看到這一點。
我們再來看RenderNode類的成員函數issueOperationsOfProjectedChildren的實現,如下所示:
~~~
template <class T>
void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
......
// draw projected nodes
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
DrawRenderNodeOp* childOp = mProjectedNodes[i];
......
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
childOp->mSkipInOrderDraw = true;
......
}
......
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/RenderNode.cpp中。
RenderNode類的成員函數issueOperationsOfProjectedChildren主要就是將成員變量mProjectedNodes描述的一個Vector中的所有Draw Render Node Op都交給參數handler描述的一個DeferOperationHandler對象處理。其中,RenderNode類的成員變量mProjectedNodes描述的一個Vector應該包含哪些Projected Node就是在Layer類的成員函數defer中調用當前正在處理的Render Node的成員函數computeOrdering來計算得到的。
同樣,在將要處理的Draw Render Node Op交給參數handler描述的一個DeferOperationHandler對象處理之前,這些Draw Render Node Op的成員變量mSkipInOrderDraw的值設置為false,處理完成之后再恢復為true。這樣做的目的是為了當前正在處理的Render Node以相同的方式遞歸處理它的Projected Node的Display List的繪制命令。我們在后面將會看到這一點。
這一步執行完成之后,回到RenderNode類的成員函數issueOperations中,現在當前正在處理的Render Node的Display List的所有繪制命令都按照我們前面描述的順序交給參數handler描述的一個DeferOperationHandler對象處理了,也就是調用該DeferOperationHandler對象的操作符重載函數()進行處理。以一個類型為DrawOp的Display List Op為例,DeferOperationHandler對象的操作符重載函數()會調用它的成員函數defer進行處理。
DrawOp類的成員函數defer的實現如下所示:
~~~
class DrawOp : public DisplayListOp {
......
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
......
deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListOp.h中。
DrawOp類的成員函數defer調用了參數deferStruct描述的一個DeferStateStruct對象的成員變量mDeferredList指向的一個DeferredDisplayList對象的成員函數addDrawOp檢查當前正在處理的一個DrawOp是否可以與其它DrawOp進行合并,它的實現如下所示:
~~~
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
/* 1: op calculates local bounds */
DeferredDisplayState* const state = createState();
if (op->getLocalBounds(state->mBounds)) {
if (state->mBounds.isEmpty()) {
.......
return;
}
} else {
state->mBounds.setEmpty();
}
/* 2: renderer calculates global bounds + stores state */
if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) {
......
return; // quick rejected
}
/* 3: ask op for defer info, given renderer state */
DeferInfo deferInfo;
op->onDefer(renderer, deferInfo, *state);
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
deferInfo.mergeable &= !recordingComplexClip();
deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
discardDrawingBatches(mBatches.size() - 1);
......
}
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
return;
}
// find the latest batch of the new op's type, and try to merge the new op into it
DrawBatch* targetBatch = NULL;
// insertion point of a new batch, will hopefully be immediately after similar batch
// (eventually, should be similar shader)
int insertBatchIndex = mBatches.size();
if (!mBatches.isEmpty()) {
if (state->mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
......
return;
}
if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = NULL;
}
}
} else {
// join with similar, non-merging batch
targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
if (targetBatch || deferInfo.mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still interate to find similar batch to insert after
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
DrawBatch* overBatch = (DrawBatch*)mBatches[i];
if (overBatch == targetBatch) break;
// TODO: also consider shader shared between batch types
if (deferInfo.batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
if (!targetBatch) break; // found insert position, quit
}
if (overBatch->intersects(state->mBounds)) {
// NOTE: it may be possible to optimize for special cases where two operations
// of the same batch/paint could swap order, such as with a non-mergeable
// (clipped) and a mergeable text operation
targetBatch = NULL;
......
break;
}
}
}
}
if (!targetBatch) {
if (deferInfo.mergeable) {
targetBatch = new MergingDrawBatch(deferInfo,
renderer.getViewportWidth(), renderer.getViewportHeight());
mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
} else {
targetBatch = new DrawBatch(deferInfo);
mBatchLookup[deferInfo.batchId] = targetBatch;
}
......
mBatches.insertAt(targetBatch, insertBatchIndex);
}
targetBatch->add(op, state, deferInfo.opaqueOverBounds);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredDisplayList.cpp中。
在分析DeferredDisplayList類的成員函數addDrawOp的實現之前,我們首先要了解它的三個成員變量mBatches、mBatchLookup和mMergingBatches,如下所示:
~~~
class DeferredDisplayList {
friend class DeferStateStruct; // used to give access to allocator
public:
......
enum OpBatchId {
kOpBatch_None = 0, // Don't batch
kOpBatch_Bitmap,
kOpBatch_Patch,
kOpBatch_AlphaVertices,
kOpBatch_Vertices,
kOpBatch_AlphaMaskTexture,
kOpBatch_Text,
kOpBatch_ColorText,
kOpBatch_Count, // Add other batch ids before this
};
......
private:
......
Vector<Batch*> mBatches;
// Maps batch ids to the most recent *non-merging* batch of that id
Batch* mBatchLookup[kOpBatch_Count];
......
/**
* Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
* collide, which avoids the need to resolve mergeid collisions.
*/
TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
......
};
~~~
這三個成員變量定義在文件frameworks/base/libs/hwui/DeferredDisplayList.h中。
可以批量進行處理的繪制命令,也就是DrawOp,放在同一個Batch中,這些Batch按照繪制先后順序保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中。注意,這里說的批量處理,有兩種含義。第一種含義是在同一個Batch中的每一個DrawOp都是單獨執行的,不過它們是按順序執行的。第二種含義是在在同一個Batch中的所有DrawOp都是一次性執行的。其中,第二種含義才稱為合并執行。
兩個DrawOp可以合并執行的必要條件是它們具有相同的Batch ID和Merge ID。注意,這不是充分條件。也就是說,具有相同Batch ID和Merge ID的兩個Draw Op不一定能夠合并執行。例如,當它們重疊,或者在它們之間存在另外的DrawOp與它們重疊。這些都會造成兩個具有相同Batch ID和Merge ID的Draw Op不能合并執行。
對于具有相同Batch ID但是不同的Merge ID的兩個Draw Op,我們希望它們將放在相鄰的位置,因為Batch ID描述的是一種繪制類型。這些繪制類型由枚舉類型OpBatchId定義。這樣GPU在執行這些Draw Op時,在內部就不需要進行狀態切換,這樣可以提高效率。當然,也并不是所有具有相同Batch ID的DrawOp都能夠放在相鄰的位置,因為它們之間可能存在其它的Draw Op與它們重疊。
基于以上的分析,當給出一個DrawOp時,我們希望:
1. 在DeferredDisplayList類的成員變量mBatches描述的一個Vector中快速找可以與它進行合并執行的DrawOp所在的Batch。這時候就需要用到DeferredDisplayList類的成員變量mMergingBatches描述的是一個TinyHashMap數組了。這個數組的大小為kOpBatch_Count,這意味著每一個Batch ID在這個數組中都有一個TinyHashMap。因此,給出一個DrawOp,我們根據它的Batch ID就可以快速得到一個TinyHashMap。有了這個TinyHashMap,我們再以給出的Draw Op的Merge ID作為鍵值,快速找到一個Batch。接著再根據其它條件判斷給出的DrawOp與在找到的Batch中已經存在的DrawOp是否能夠合并。如果能夠合并,就將給出的DrawOp添加到找到的Batch去就行了。
2. 如果不能在DeferredDisplayList類的成員變量mBatches描述的一個Vector中可以讓它合并的Batch時,我們希望可以快速找到另外一個Batch,這個Batch的所有DrawOp都是依次地單獨執行。這時候就需要用到DeferredDisplayList類的成員變量mBatchLookup描述的一個Batch數組了。這個數組的大小同樣為kOpBatch_Count,這也意味著每一個Batch ID在這個數組中都有一個Batch。因此,給出一個DrawOp,我們根據它的Batch ID就可以快速得到一個Batch。接著再根據其它條件判斷給出的DrawOp與在找到的Batch中已經存在的DrawOp是否能夠合并。如果能夠合并,就將給出的DrawOp添加到找到的Batch去就行了。
3. 如果通過上面的兩個方法還是不能找到一個Batch,那么就需要創建一個新的Batch來存放給出的Draw Op。但是我們希望可以將這個新創建的Batch放在與它具有相同Batch ID的Batch相鄰的位置上。
了解了DeferredDisplayList類的三個成員變量mBatches、mBatchLookup和mMergingBatches的作用之后,我們再來看另外一個結構體DeferInfo,如下所示:
~~~
struct DeferInfo {
public:
DeferInfo() :
batchId(DeferredDisplayList::kOpBatch_None),
mergeId((mergeid_t) -1),
mergeable(false),
opaqueOverBounds(false) {
};
int batchId;
mergeid_t mergeId;
bool mergeable;
bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below
};
~~~
這個結構體定義在文件frameworks/base/libs/hwui/DeferredDisplayList.h中。
結構體DeferInfo有四個成員變量,分別是:
1. batchId:描述一個DrawOp的Batch ID。
2. mergeId:描述一個DrawOp的Merge ID。
3. mergeable:描述一個DrawOp是否具有與其它DrawOp進行合并的條件,最終能不能合并還要取決于其它條件。
4. opaqueOverBounds:描述的一個DrawOp是不是不透明繪制。如果是的話,就會可能覆蓋在它前面的DrawOp,但是最終能不能覆蓋同樣還要取決于其它條件。
每一個DrawOp都定義有一個成員函數onDefer,用來設置一個DeferInfo結構體的各個成員變量,以便調用者可以知道它的Batch ID和Merge ID,以及它的合并和覆蓋繪制信息。具體的例子可以參考前面Android應用程序UI硬件加速渲染的預加載資源地圖集服務(Asset Atlas Service)分析一文。
有了上面這些知識之后,我們就開始分析上面列出的DrawOp類的成員函數defer的代碼。為了描述分便,我們分段來閱讀:
~~~
/* 1: op calculates local bounds */
DeferredDisplayState* const state = createState();
if (op->getLocalBounds(state->mBounds)) {
if (state->mBounds.isEmpty()) {
.......
return;
}
} else {
state->mBounds.setEmpty();
}
~~~
這段代碼是獲得參數op描述的DrawOp的繪制區域,保存在本地變量state指向的一個DeferDisplayState結構體的成員變量mBounds中。通過調用這個DrawOp的成員函數getLocalBounds可以獲得它的繪制區域。
如果這個DrawOp設置了一個空區域,那么就不會對它進行處理了。另一方面,如果這個DrawOp沒有設置繪制區載,調用它的成員函數getLocalBounds得到的返回值為false,這時候會將本地變量const_state指向的一個DeferDisplayState結構體的成員變量mBounds描述的區域設置為空,但是其實想表達的意思是未設置繪制區域。
~~~
/* 2: renderer calculates global bounds + stores state */
if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) {
......
return; // quick rejected
}
~~~
這段代碼調用參數renderer描述的一個OpenGLRender對象的成員函數storeDisplayState設置參數op描述的DrawOp的裁剪區域。如果參數op描述的DrawOp描述的繪制區域與當前的裁剪區域沒有交集,那么就說明該DrawOp是不可見的,因此就不用對它進行繪制了,于是就不用往下處理了。
~~~
/* 3: ask op for defer info, given renderer state */
DeferInfo deferInfo;
op->onDefer(renderer, deferInfo, *state);
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
deferInfo.mergeable &= !recordingComplexClip();
deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
~~~
這段代碼調用參數op描述的DrawOp獲得一個初始好的DeferInfo結構體,也就是獲得參數op描述的DrawOp的Batch ID和Merge ID,以及合并和覆蓋繪制信息。
如果參數op描述的DrawOp表明自己可以與其它具有相同Batch ID和Merge ID的DrawOp合并,但是如果當前的裁剪區域是一個復雜的裁剪區域,也就是由一系列正則的矩形組合形成的復雜區域,那么就會禁止op描述的DrawOp與其它具有相同Batch ID和Merge ID的DrawOp合并。
同樣,如果參數op描述的DrawOp表明自己的繪制會覆蓋前面的DrawOp,但是如果當前的裁剪區域是一個復雜的裁剪區域,或者當前是繪制在一個Layer上,那么就會禁止op描述的DrawOp覆蓋前面的DrawOp。
復雜的裁剪區域會導致具有相同Batch ID和Merge ID的DrawOp不能正確地合并,同時也會導致不透明的DrawOp不能正確地覆蓋前面的DrawOp。另外,如果參數op描述的DrawOp是繪制在一個Layer之上,也就是在它之前有一個saveLayer操作,該操作會創建一個Layer,那么后面會有一個對應的restore/restoreToCount操作。當執行restore/restoreToCount操作的時候,前面繪制出來的Layer會被合并在前一個Layer或者Frame Buffer之上。這個合并的操作導致參數op描述的DrawOp不能直接就覆蓋前面的DrawOp,也就是丟棄前面的DrawOp。
~~~
if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
discardDrawingBatches(mBatches.size() - 1);
......
}
~~~
這段代碼綜合判斷參數op描述的DrawOp是否能夠覆蓋排在前面的DrawOp。如果以下條件都能滿足,那么參數op描述的DrawOp是否能夠覆蓋排在前面的DrawOp:
1. 當前設置了禁止過度繪制,即DeferredDisplayList類的成員變量mAvoidOverdraw的值等于true。在啟用過度繪制的情況下,即使是被覆蓋的區域,也要進行繪制。這樣才能將看到過度繪制。
2. 在參數op描述的DrawOp之前,已經存在其它的DrawOp,也就是DeferredDisplayList類的成員變量mBatches描述的一個Vector不為空,這樣才有DrawOp被覆蓋。
3. 參數op描述的DrawOp明確設置有繪制區域。如果參數op描述的DrawOp沒有設置繪制區域,那么本地變量state指向的一個state指向的一個DeferDisplayState結構體的成員變量mClipSideFlags的值會被設置為kClipSide_ConservativeFull。未設置繪制區域的DrawOp,我們就不能明確地知道它會不會覆蓋之前的DrawOp。
4. 參數op描述的DrawOp表明自己是不透明繪制,即本地變量deferInfo描述的一個DeferInfo結構體的成員變量opaqueOverBounds的值等于ture。
5. 參數op描述的DrawOp的繪制區域包含了之前的DrawOp合并起來的繪制區域。
這些排在前面的DrawOp就保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中。如果能夠覆蓋,那么就可以丟棄它們,實際上就是調用DeferredDisplayList類的成員函數discardDrawingBatches清空上述Vector。
~~~
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
return;
}
~~~
如果參數renderer描述的一個OpenGLRenderer表明自己禁止重新排序它的DrawOp,也就是禁止執行DrawOp的合并操作,這時候就會直接為參數op描述的DrawOp創建一個Batch,并且保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中。這意味著每一個DrawOp都會有獨立保存一個Batch中,這樣就可以避免出現合并操作。
~~~
// find the latest batch of the new op's type, and try to merge the new op into it
DrawBatch* targetBatch = NULL;
// insertion point of a new batch, will hopefully be immediately after similar batch
// (eventually, should be similar shader)
int insertBatchIndex = mBatches.size();
if (!mBatches.isEmpty()) {
if (state->mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
......
return;
}
~~~
這段代碼判斷在參籹op描述的DrawOp之前,是否已經存在其它的DrawOp。如果存在,但是參籹op描述的DrawOp又沒有設置繪制區域,那么即使前面的DrawOp能夠與它進行合并,那么也是禁止的。這時候就單獨為它創建一個Batch,并且保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中。
~~~
if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = NULL;
}
}
} else {
// join with similar, non-merging batch
targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
~~~
如果參數op描述的DrawOp表明自己可以與其它具有相同Batch ID和Merge ID的DrawOp進行合并,那么這段代碼就按照我們前面描述的,通過DeferredDisplayList類的成員變量mMergingBatches描述的一個TinyHashMap數組,快速找到一個具有相同Batch ID和Merge ID的Batch。如果能找到這樣的Batch,還需要調用這個Batch的成員函數canMergeWith判斷已經存在該Batch的DrawOp是否能夠真的與參數op描述的DrawOp進行合并。例如,對于Batch ID等于kOpBatch_Text的兩個文字繪制DrawOp,如果文字的顏色不一樣,那么這兩個DrawOp合并。
如果參數op描述的DrawOp表明自己不可以與其它DrawOp進行合并,那么這段代碼也是按照我們前面描述的,通過DeferredDisplayList類的成員變量mBatchLookup描述的一個Batch數組,找到一個與它具有相同的Batch ID的Batch,以便將參數op描述的DrawOp加入到這個Batch去進行依次的獨立繪制。
~~~
if (targetBatch || deferInfo.mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still interate to find similar batch to insert after
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
DrawBatch* overBatch = (DrawBatch*)mBatches[i];
if (overBatch == targetBatch) break;
// TODO: also consider shader shared between batch types
if (deferInfo.batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
if (!targetBatch) break; // found insert position, quit
}
if (overBatch->intersects(state->mBounds)) {
// NOTE: it may be possible to optimize for special cases where two operations
// of the same batch/paint could swap order, such as with a non-mergeable
// (clipped) and a mergeable text operation
targetBatch = NULL;
......
break;
}
}
}
~~~
這段代碼判斷參數op描述的DrawOp是否真的能加入到前面找到的Batch去,主要就是判斷參數op描述的DrawOp與找到的Batch里面的DrawOp之間,是否存在其它的DrawOp與它重疊。如果存在,那么就不能夠將參數op描述的DrawOp是否真的能加入到前面找到的Batch去了。這意味著要為參數op描述的DrawOp創建一個獨立的Batch。這個Batch也是按照我們前面描述的,盡可能放在前面與它具有相同Batch ID的Batch的相鄰位置。這個位置就通過設置本地變量insertBatchIndex的值得到。
~~~
if (!targetBatch) {
if (deferInfo.mergeable) {
targetBatch = new MergingDrawBatch(deferInfo,
renderer.getViewportWidth(), renderer.getViewportHeight());
mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
} else {
targetBatch = new DrawBatch(deferInfo);
mBatchLookup[deferInfo.batchId] = targetBatch;
}
......
mBatches.insertAt(targetBatch, insertBatchIndex);
}
targetBatch->add(op, state, deferInfo.opaqueOverBounds);
~~~
這段代碼判斷本地變量targetBatch的值。如果等于NULL,那么就表明前面不能在DeferredDisplayList類的成員變量mBatches描述的一個Vector中找到一個能夠用來保存參數op描述的DrawOp的Batch。這時候就需要為參數op描述的DrawOp創建一個Batch了。這個Batch的具體類型要么是MergingDrawBatch,要么是DrawBatch,取決于參數op描述的DrawOp是否表明自己是可合并的,即本地變量deferInfo描述的一個DeferInfo結構體的成員變量mergeable的值是否為true。
如果參數op描述的DrawOp表明自己是可合并的,那么就為它創建一個MergingDrawBatch,并且保存在DeferredDisplayList類的成員變量mMergingBatches描述的一個TinyHashMap數組中,使得它后面的與它具有相同Batch ID和Merge ID的DrawOp能夠快速找到它。
如果參數op描述的DrawOp表明自己是不可以合并的,那么就為它創建一個DrawBatch,并且保存在DeferredDisplayList類的成員變量mBatchLookup描述的一個Batch數組中,以便它后面的與它具有相同Batch ID的DrawOp能夠快速找到它。
這意味著保存在同一個MergingDrawBatch的DrawOp,在渲染的時候是可以進合并繪制的,而保存在同一個rawBatch的DrawOp,在渲染的時候是可以連續地進行獨立繪制的。
最后,新創建的Batch就根據前面得到的本地變量insertBatchIndex的值保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中,使得該Batch盡可能地與它具有同的Batch ID的Batch放在一起。
另一方面,如果本地變量targetBatch的值不等于NULL,那么就表明前面找到了一個Batch,這個Batch可以用來保存參數op描述的DrawOp。
這樣,當DeferredDisplayList類的成員addDrawOp執行完成之后,當前正在處理的所有DrawOp都經過合并等處理了,并且處理后得到的DrawOp以Batch為單位保存在DeferredDisplayList類的成員變量mBatches描述的一個Vector中。
上面描述的是一個普通的DrawOp的成員函數defer被調用時所執行的繪制命令重排和合并操作。還有另外一種特殊的Display List Op,即DrawRenderNodeOp。從前面的分析可以知道,當一個Render Node包含有子Render Node時,它的Display List包含有一個對應的DrawRenderNodeOp。此外,當一個Render Node具有Projected Node時,每一個Projected Node都有一個對應的DrawRenderNodeOp保存該Render Node的成員變量mProjectedNodes描述的一個Vector。所有的這些DrawRenderNodeOp也像DrawOp一樣,會被DeferOperationHandler類的操作符重載函數()調用它們的成員函數defer。
DrawRenderNodeOp類的成員函數defer的實現如下所示:
~~~
class DrawRenderNodeOp : public DrawBoundedOp {
......
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
mRenderNode->defer(deferStruct, level + 1);
}
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListOp.h中。
DrawRenderNodeOp類的成員變量mRenderNode描述的是當前正在處理的DrawRenderNodeOp所關聯的一個Render Node。當這個Render Node的Display List不為空時,就表示這個Render Node的Display List的繪制命令需要執行重排和合并操作。
此外,DrawRenderNodeOp類還有另外一個成員變量mSkipInOrderDraw。當它的值等于true時,就表示當前正在處理的DrawRenderNodeOp所關聯的Render Node要跳過順序繪制。這是什么意思呢?其實這是針對我們前面提到的Ripple Drawable的。我們知道,Ripple Drawable有可能不是按照它們在視圖結構的順序繪制的,因為它們有可能會被投影到最近一個父Render Node的Backround去繪制。這樣當它們對應的Render Node在順序繪制中就應該跳過處理。
在我們這個情景中,這里的DrawRenderNodeOp類的成員函數defer并不是在順序繪制過程中被調用的,而是在重排和合并一個Render Node的Display List的繪制命令的過程中調用的,也就是在前面分析的RenderNode類的成員函數issueOperationsOf3dChildren和issueOperationsOfProjectedChildren中調用的。這兩個成員函數需要強制DrawRenderNodeOp類的成員函數defer重排和合并當前正在處理的DrawRenderNodeOp所關聯的一個Render Node的Display List的繪制命令,因此就會強制當前正在處理的DrawRenderNodeOp的成員變量mSkipInOrderDraw設置為false。
這樣,當一個DrawRenderNodeOp的成員變量mSkipInOrderDraw的值為false,并且它關聯的Render Node的Display List不為空,這個Render Node的成員函數defer就會被調用。這意味著通過DrawRenderNodeOp類的成員函數defer,一個Render Node及其所有的子Render Node和Projected Node的Display List的繪制命令都會得到歸遞重排和合并處理。
這一步執行完成之后,回到CanvasContext類的成員函數draw中,這時候所有設置了Layer的Render Node的Display List包含的Display List Op都已經得到了重排和合并等處理,接下來要做的事情就是調用OpenGLRenderer類的成員函數drawRenderNode渲染應用程序窗口的Root Render Node的Display List。
OpenGLRenderer類的成員函數drawRenderNode的實現如下所示:
~~~
status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (renderNode && renderNode->isRenderable()) {
// compute 3d ordering
renderNode->computeOrdering();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
renderNode->replay(replayStruct, 0);
return status | replayStruct.mDrawGlStatus;
}
bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
flushLayers();
status = startFrame();
return deferredList.flush(*this, dirty) | status;
}
// Even if there is no drawing command(Ex: invisible),
// it still needs startFrame to clear buffer and start tiling.
return startFrame();
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp。
參數renderNode描述的是應用程序窗口的Root Render Node,如果它的值不等于NULL,并且它是可渲染的,即調用它的成員函數isRenderable的返回值為true,那么接下來就開始渲染的它的Display List。
在渲染應用程序窗口的Root Render Node之前,OpenGLRenderer類的成員函數drawRenderNode首先調用它的成員函數computeOrdering計算它的Projected Node。這一步與前面LayerRenderer類渲染設置了Layer的Render Node的Display List的過程是一樣的,都是為重排那些Projected Node,使得它們的渲染順序位于要投影到的Render Node的后面。
OpenGLRenderer類的成員函數drawRenderNode接下來判斷當前是否禁止重排應用程序窗口的Root Render Node的Display List的繪制命令,也就是不允許對這些繪制命令進行合并。如果是禁止的話,那么OpenGLRenderer類的成員變量mCaches指向的一個Caches對象的成員變量drawDeferDisabled的值就會等于true。在這種情況下,就會跳過應用程序窗口的Root Render Node的Display List的繪制命令的重排階段,而直接對它們進行執行。這是通過調用RenderNode類的成員函數replay實現的。
如果當前不禁止重排應用程序窗口的Root Render Node的Display List的繪制命令,那么OpenGLRenderer類的成員函數drawRenderNode接下來做的事情就是調用前面分析過的RenderNode類的成員函數defer對應用程序窗口的Root Render Node及其子Render Node和Projected Node的的Display List的繪制命令進行合并操作。合并后得到的繪制命令,也就是DrawOp,就以Batch為單位保存在本地變量deferredList描述的一個DeferredDisplayList對象的成員變量mBatches描述的一個Vector中。
這里有一點需要注意的是,在調用RenderNode類的成員函數defer合并應用程序窗口的Root Render Node的Display List的繪制命令的時候,傳遞進去的DeferStateStruct結構體封裝的Renderer是一個OpenGLRenderer。這意味著如果應用程序窗口的Root Render Node包含了一個設置了Layer的子RenderNode,那么當調用到RenderNode類的成員函數issueOperations遞歸處理該子RenderNode時候,這個子RenderNode就直接以一個DrawLayerOp進行繪制。這是由于這時候這個子RenderNode的成員變量renderer指向的OpenGLRenderer對象的實際類型是LayerRenderer,而參數renderer指向OpenGLRenderer對象的實際類型就是OpenGLRenderer。這兩個OpenGLRenderer對象的不相等,就使得本地變量drawLayer的值等于true,于是該子RenderNode的繪制命令就被封裝為一個DrawLayerOp。這樣做是合理的,因為這個子RenderNode的Display List的繪制命令之前已經被重排和合并過了。
重排和合并完成應用程序窗口的Root Render Node及其子Render Node和Projected Node的Display List的繪制命令之后,本來就可以執行它們了。但是在執行它們之前,還有一件事情需要做,就是先執行那些設置了Layer的子Render Node的繪制命令,以便得到一個對應的FBO。這些FBO就代表了那些設置了Layer的子Render Node的UI。這一步是通過調用OpenGLRenderer類的成員函數flush來完成的。
OpenGLRenderer類的成員函數flush的實現如下所示:
~~~
void OpenGLRenderer::flushLayers() {
int count = mLayerUpdates.size();
if (count > 0) {
......
// Note: it is very important to update the layers in order
for (int i = 0; i < count; i++) {
......
Layer* layer = mLayerUpdates.itemAt(i);
layer->flush();
......
}
......
mRenderState.bindFramebuffer(getTargetFbo());
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp。
從前面的分析可以知道,OpenGLRenderer類的成員變量mLayerUpdates描述的一個Vector里面存放的都是設置了Layer的Render Node關聯的Layer,并且這些Render Noder的Display List的繪制命令都是已經經過了重排和合并等操作的。
對于保存在上述Vector中的每一個Layer,OpenGLRenderer類的成員函數flushLayers都會調用它的成員函數flush,目的就是執行這些Layer關聯的Render Node的Display List經過重排和合并后的繪制命令。
Layer類的成員函數flush的實現如下所示:
~~~
void Layer::flush() {
// renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
if (deferredList && renderer) {
......
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
deferredList->flush(*renderer, dirtyRect);
......
}
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/Layer.cpp中。
從前面的分析可以知道,這時候正在處理的Layer對象的成員變量renderer和deferredList的值均不等于NULL,它們分別指向了一個LayerRenderer對象和一個DeferredDisplayList對象,因此Layer類的成員函數flush接下來就分別調用了這兩個對象的成員函數prepareDirty和flush。
LayerRenderer類的成員函數prepareDirty的實現如下所示:
~~~
status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
bool opaque) {
......
renderState().bindFramebuffer(mLayer->getFbo());
......
return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/LayerRenderer.cpp中。
LayerRenderer類的成員函數prepareDirty做了一件很重要的事情,就是在從成員變量mLayer指向的一個Layer對象獲得一個FBO,并且將該FBO設置當前Open GL環境的渲染對象,這意味著后續的Open GL繪制命令都是將UI渲染在該FBO上。
LayerRenderer類的成員函數prepareDirty最后還調用了父類OpenGLRenderer的成員函數prepareDirty。前面我們在分析OpenGLRenderer類的成員函數prepareDirty的時候提到,如果當前正在處理的一個LayerRenderer對象,那么它所做的事情是調用OpenGLRenderer類的另外一個成員函數startFrame。OpenGLRenderer類的成員函數startFrame僅僅是負責執行一些諸如清理顏色繪沖區等基本操作。當然,這里清理的是從成員變量mLayer指向的一個Layer對象獲得一個FBO的顏色繪沖區。
這一步執行完成之后,回到Layer類的成員函數flush中,它接下來調用DeferredDisplayList類的成員函數flush,目的是為了將當前正在處理的Layer關聯的Render Node的Display List渲染在上述的FBO上。
DeferredDisplayList類的成員函數flush的實現如下所示:
~~~
status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
......
status |= replayBatchList(mBatches, renderer, dirty);
......
return status;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredDisplayList.cpp中。
前面提到,DeferredDisplayList類的成員變量mBatches描述的一個Vector存放的就是一個設置了Layer的Render Node的Display List經過重排和合并后的繪制命令,這些繪制命令通過DeferredDisplayList類的另外一個成員函數replayBatchList執行。
DeferredDisplayList類的成員函數replayBatchList的實現如下所示:
~~~
static status_t replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < batchList.size(); i++) {
if (batchList[i]) {
status |= batchList[i]->replay(renderer, dirty, i);
}
}
......
return status;
}
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredDisplayList.cpp中。
DeferredDisplayList類的成員函數replayBatchList依次調用參數batchList描述的一個Vector中的每一個Batch對象的成員函數replay。從前面分析的DeferredDisplayList類的成員函數addDrawOp可以知道,參數batchList描述的一個Vector中的每一個Batch對象的實際類型要么是DrawBatch,要么是MergingDrawBatch,因此我們接下來就繼續分析DrawBatch類和MergingDrawBatch類的成員函數replay的實現。
DrawBatch類的成員函數replay的實現如下所示:
~~~
class DrawBatch : public Batch {
public:
......
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
......
status_t status = DrawGlInfo::kStatusDone;
......
for (unsigned int i = 0; i < mOps.size(); i++) {
DrawOp* op = mOps[i].op;
......
status |= op->applyDraw(renderer, dirty);
.....
}
return status;
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredDisplayList.cpp中。
DrawBatch類的成員函數replay依次調用存放在成員變量mOps描述的一個Vector中的每一個DrawOp的成員函數applyDraw,以便這些DrawOp可以轉化為Open GL繪制命令進行執行。
以一個具體的DrawRectOp為例,它的成員函數applyDraw的實現如下所示:
~~~
class DrawRectOp : public DrawStrokableOp {
public:
......
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListOp.h中。
DrawRectOp類的成員函數applyDraw調用了參數renderer描述的一個OpenGLRenderer對象的成員函數drawRect來渲染當前正在處理的一個DrawRectOp。參數renderer描述的一個OpenGLRenderer對象的實際類型為LayerRenderer,不過LayerRenderer類的成員函數drawRect是從父類OpenGLRenderer繼承下來的。因此,當前正在處理的一個DrawRectOp最終是通過OpenGLRenderer類的成員函數drawRect轉化Open GL繪制命令進行執行的。這一點我們就留給讀者自己去分析了。
還有一種特殊的DrawOp,即DrawRenderNodeOp,當它們的成員函數applyDraw被調用時,它所做的工作實際上遞歸地將它的子Render Node或者Projected Node的Display List包含的DrawOp轉化為Open GL命令來執行,它的實現如下所示:
~~~
class DrawRenderNodeOp : public DrawBoundedOp {
......
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) {
if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
mRenderNode->replay(replayStruct, level + 1);
}
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListOp.h中。
這一點與前面我們分析的DrawRenderNodeOp類的成員函數applyDraw的邏輯是類似的,因此我們就不再詳述。
接下來我們再來看MergingDrawBatch類的成員函數replay的實現,如下所示:
~~~
class MergingDrawBatch : public DrawBatch {
public:
......
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
......
DrawOp* op = mOps[0].op;
......
status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
......
return status;
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DeferredDisplayList.cpp中。
MergingDrawBatch類的成員函數replay只調用了保存在成員變量mOps描述的一個Vector中的第一個DrawOp的成員函數multiDraw,但是會將其余的DrawOp作為參數傳遞給它。
以一個具體的DrawPatchOp為例,它的成員函數multiDraw的實現如下所示:
~~~
class DrawPatchOp : public DrawBoundedOp {
public:
......
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
const Vector<OpStatePair>& ops, const Rect& bounds) {
const DeferredDisplayState& firstState = *(ops[0].state);
renderer.restoreDisplayState(firstState, true); // restore all but the clip
// Batches will usually contain a small number of items so it's
// worth performing a first iteration to count the exact number
// of vertices we need in the new mesh
uint32_t totalVertices = 0;
for (unsigned int i = 0; i < ops.size(); i++) {
totalVertices += ((DrawPatchOp*) ops[i].op)->getMesh(renderer)->verticesCount;
}
const bool hasLayer = renderer.hasLayer();
uint32_t indexCount = 0;
TextureVertex vertices[totalVertices];
TextureVertex* vertex = &vertices[0];
// Create a mesh that contains the transformed vertices for all the
// 9-patch objects that are part of the batch. Note that onDefer()
// enforces ops drawn by this function to have a pure translate or
// identity matrix
for (unsigned int i = 0; i < ops.size(); i++) {
DrawPatchOp* patchOp = (DrawPatchOp*) ops[i].op;
const DeferredDisplayState* state = ops[i].state;
const Patch* opMesh = patchOp->getMesh(renderer);
uint32_t vertexCount = opMesh->verticesCount;
if (vertexCount == 0) continue;
// We use the bounds to know where to translate our vertices
// Using patchOp->state.mBounds wouldn't work because these
// bounds are clipped
const float tx = (int) floorf(state->mMatrix.getTranslateX() +
patchOp->mLocalBounds.left + 0.5f);
const float ty = (int) floorf(state->mMatrix.getTranslateY() +
patchOp->mLocalBounds.top + 0.5f);
// Copy & transform all the vertices for the current operation
TextureVertex* opVertices = opMesh->vertices;
for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
TextureVertex::set(vertex++,
opVertices->x + tx, opVertices->y + ty,
opVertices->u, opVertices->v);
}
// Dirty the current layer if possible. When the 9-patch does not
// contain empty quads we can take a shortcut and simply set the
// dirty rect to the object's bounds.
if (hasLayer) {
if (!opMesh->hasEmptyQuads) {
renderer.dirtyLayer(tx, ty,
tx + patchOp->mLocalBounds.getWidth(),
ty + patchOp->mLocalBounds.getHeight());
} else {
const size_t count = opMesh->quads.size();
for (size_t i = 0; i < count; i++) {
const Rect& quadBounds = opMesh->quads[i];
const float x = tx + quadBounds.left;
const float y = ty + quadBounds.top;
renderer.dirtyLayer(x, y,
x + quadBounds.getWidth(), y + quadBounds.getHeight());
}
}
}
indexCount += opMesh->indexCount;
}
return renderer.drawPatches(mBitmap, getAtlasEntry(),
&vertices[0], indexCount, getPaint(renderer));
}
......
};
~~~
這個函數定義在文件frameworks/base/libs/hwui/DisplayListOp.h中。
在前面Android應用程序UI硬件加速渲染的預加載資源地圖集服務(Asset Atlas Service)分析一文中,我們有分析過DrawPatchOp類的成員函數multiDraw的實現,它所做的事情就是首先計算出當前正在處理的DrawPatchOp和參數ops描述的DrawPatchOp的紋理坐標,并且將這些紋理坐標保存一個數組中傳遞給參數renderer描述的一個OpenGLRenderer對象的成員函數drawPatches,使得后者可以一次性地將N個DrawPatchOp合并在一起轉化為Open GL繪制命令執行。這之所以是可行的,是因為這些DrawPatchOp是以紋理方式進行渲染的,它們使用的是同一個紋理。
這一步執行完成之后,回到OpenGLRenderer類的成員函數flushLayers中,這時候所有設置了Layer的Render Noder及其子Render Node和Projected Node的Display List均已渲染到了自己的FBO之上,接下來就要將這些FBO以及其它沒有設置Layer的Render Node的Display List渲染在Frame Buffer上,也就是渲染在從Surface Flinger請求回來的一個圖形緩沖區之上。由于前面每調用一個Layer對象的成員函數flush的時候,都會將一個FBO設置為當前的渲染對象,而接下來的渲染對象是Frame Buffer,因此就需要調用成員變量mRenderState描述的一個RenderState對象的成員函數bindFramebuffer將Frame Buffer設置為當前的渲染對象。前面提到,OpenGLRenderer類的成員函數getTargetFbo的返回值等于0,當我們將一個值為0的FBO設置為當前的渲染對象時,起到的效果實際上解除前面設置的值為非0的FBO作為當前的渲染對象,并且將當前的渲染對象還原回Frame Buffer的效果。
OpenGLRenderer類的成員函數flushLayers執行完成后,回到OpenGLRenderer類的成員函數drawRenderNode中,這時候可以渲染應用程序窗口的Root Render Node的Display List了。在渲染之前,同樣是先調用OpenGLRenderer類的成員函數startFrame執行一些諸如清理顏色繪沖區等基本操作。注意,這里清理的是Frame Buffer的顏色繪沖區。這時候應用程序窗口的Root Render Node及其子Render Node和Projected Node的Display List經過重排和合并后的繪制命令就存放在本地變量deferredList描述的一個DeferredDisplayList的成員變量mBatches描述的一個Vector中,因此OpenGLRenderer類的成員函數drawRenderNode就可以調用前面已經分析過的DeferredDisplayList類的成員函數flush來執行它們。這里同樣是需要注意,這些繪制命令的執行是作用在Frame Buffer之上的。
至此,應用程序窗口的Display List的渲染過程就分析完成了。整個過程比較復雜,但是總結來說,核心邏輯就是:
1. 將Main Thread維護的Display List同步到Render Thread維護的Display List去。這個同步過程由Render Thread執行,但是Main Thread會被阻塞住。
2. 如果能夠完全地將Main Thread維護的Display List同步到Render Thread維護的Display List去,那么Main Thread就會被喚醒,此后Main Thread和Render Thread就互不干擾,各自操作各自內部維護的Display List;否則的話,Main Thread就會繼續阻塞,直到Render Thread完成應用程序窗口當前幀的渲染為止。
3. Render Thread在渲染應用程序窗口的Root Render Node的Display List之前,首先將那些設置了Layer的子Render Node的Display List渲染在各自的一個FBO上,接下來再一起將這些FBO以及那些沒有設置Layer的子Render Node的Display List一起渲染在Frame Buffer之上,也就是渲染在從Surface Flinger請求回來的一個圖形緩沖區上。這個圖形緩沖區最終會被提交給Surface Flinger合并以及顯示在屏幕上。
第2步能夠完全將Main Thread維護的Display List同步到Render Thread維護的Display List去很關鍵,它使得Main Thread和Render Thread可以并行執行,這意味著Render Thread在渲染應用程序窗口當前幀的Display List的同時,Main Thread可以去準備應用程序窗口下一幀的Display List,這樣就使得應用程序窗口的UI更流暢。
Android 5.0引入Render Thread的作用除了可以獲得上面描述的效果之外,還可以使得應用程序窗口動畫顯示更加流暢。在接下來的一篇文章中,我們就繼續分析在硬件加速渲染的環境下,應用程序窗口的動畫顯示框架,敬請關注!
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析