<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                原文出處——>[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所示: :-: ![](https://box.kancloud.cn/bd41bd0bddf122f429d4f9272eb0f86e_631x335.png) 圖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所示: :-: ![](https://box.kancloud.cn/9a9945f33fdae1a48b49e6df037c9c4d_296x195.png) 圖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,如下所示: :-: ![](https://box.kancloud.cn/93ec2d9f6e50b22f8f65fdb77e7254e5_767x411.png) 圖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所示: :-: ![](https://box.kancloud.cn/f1a3cf602def39eb9c73cac4f00644a4_666x308.png) 圖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的作用除了可以獲得上面描述的效果之外,還可以使得應用程序窗口動畫顯示更加流暢。在接下來的一篇文章中,我們就繼續分析在硬件加速渲染的環境下,應用程序窗口的動畫顯示框架,敬請關注!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看