Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android系統教程 >> 安卓省電與加速 >> Android應用程序UI硬件加速渲染環境初始化過程分析

Android應用程序UI硬件加速渲染環境初始化過程分析

編輯:安卓省電與加速

在Android應用程序中,我們是通過Canvas API來繪制UI元素的。在硬件加速渲染環境中,這些Canvas API調用最終會轉化為Open GL API調用(轉化過程對應用程序來說是透明的)。由於Open GL API調用要求發生在Open GL環境中,因此在每當有新的Activity窗口啟動時,系統都會為其初始化好Open GL環境。這篇文章就詳細分析這個Open GL環境的初始化過程。

 

Open GL環境也稱為Open GL渲染上下文。一個Open GL渲染上下文只能與一個線程關聯。在一個Open GL渲染上下文創建的Open GL對象一般來說只能在關聯的Open GL線程中操作。這樣就可以避免發生多線程並發訪問發生的沖突問題。這與大多數的UI架構限制UI操作只能發生在UI線程的原理是差不多的。

在Android 5.0之前,Android應用程序的主線程同時也是一個Open GL線程。但是從Android 5.0之後,Android應用程序的Open GL線程就獨立出來了,稱為Render Thread,如圖1所示:

/

圖1 Android應用程序Main Thread和Render Thread

Render Thread有一個Task Queue,Main Thread通過一個代理對象Render Proxy向這個Task Queue發送一個drawFrame命令,從而驅使Render Thread執行一次渲染操作。因此,Android應用程序UI硬件加速渲染環境的初始化過程任務之一就是要創建一個Render Thread。

一個Android應用程序可能存在多個Activity組件。在Android系統中,每一個Activity組件都是一個獨立渲染的窗口。由於一個Android應用程序只有一個Render Thread,因此當Main Thread向Render Thread發出渲染命令時,Render Thread要知道當前要渲染的窗口是什麼。從這個角度看,Android應用程序UI硬件加速渲染環境的初始化過程任務之二就是要告訴Render Thread當前要渲染的窗口是什麼。

一旦Render Thread知道了當前要渲染的窗口,它就將可以將該窗口綁定到Open GL渲染上下文中去,從而使得後面的渲染操作都是針對被綁定的窗口的,如圖2所示:

/

圖2 綁定窗口到Open GL渲染上下文中

Java層的Activity窗口到了Open GL這一層,被抽象為一個ANativeWindow。將它綁定到Open GL渲染上下文之後,就可以通過eglSwapBuffer函數向SurfaceFlinger服務Dequeue和Queue Graphic Buffer。其中,Dequeue Graphic Buffer是為了在上面進行繪制UI,而Queue Graphic Buffer是為了將繪制好的UI交給Surface Flinger合成和顯示。

接下來,我們就結合源代碼分析Android應用程序UI硬件加速渲染環境的初始化過程,主要的關注點就是創建Render Thread的過程和綁定窗口到Render Thread的過程。

從前面Android應用程序窗口(Activity)的視圖對象(View)的創建過程分析一文可以知道,Activity組件在創建的過程中,也就是在其生命周期函數onCreate的調用過程中,一般會通過調用另外一個成員函數setContentView創建和初始化關聯的窗口視圖,最後通過調用ViewRoot類的成員函數setView完成這一過程。上述文章分析的源碼是Android 2.3版本的。到了Android 4.0之後,ViewRoot類的名字改成了ViewRootImpl,它們的作用仍然一樣的。

Android應用程序UI硬件加速渲染環境的初始化過程是在ViewRootImpl類的成員函數setView開始,如下所示:

 

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ......

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......

                if (view instanceof RootViewSurfaceTaker) {
                    mSurfaceHolderCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                    if (mSurfaceHolderCallback != null) {
                        mSurfaceHolder = new TakenSurfaceHolder();
                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                    }
                }

                ......

                // If the application owns the surface, don't enable hardware acceleration
                if (mSurfaceHolder == null) {
                    enableHardwareAcceleration(attrs);
                }

                ......
            }
        }
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

 

參數view描述的是當前正在創建的Activity窗口的頂級視圖。如果它實現了RootViewSurfaceTaker接口,並且通過該接口的成員函數willYouTakeTheSurface提供了一個SurfaceHolder.Callback2接口,那麼就表明應用程序想自己接管對窗口的一切渲染操作。這樣創建出來的Activity窗口就類似於一個SurfaceView一樣,完全由應用程序自己來控制它的渲染。

基本上我們是不會將一個Activity窗口當作一個SurfaceView來使用的,因此在ViewRootImpl類的成員變量mSurfaceHolder將保持為null值,這樣就會導致ViewRootImpl類的成員函數enableHardwareAcceleration被調用為判斷是否需要為當前創建的Activity窗口啟用硬件加速渲染。

ViewRootImpl類的成員函數enableHardwareAcceleration的實現如下所示:

 

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ......

    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
        mAttachInfo.mHardwareAccelerated = false;
        mAttachInfo.mHardwareAccelerationRequested = false;

        ......

        // Try to enable hardware acceleration if requested
        final boolean hardwareAccelerated =
                (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;

        if (hardwareAccelerated) {
            if (!HardwareRenderer.isAvailable()) {
                return;
            }

            // Persistent processes (including the system) should not do
            // accelerated rendering on low-end devices.  In that case,
            // sRendererDisabled will be set.  In addition, the system process
            // itself should never do accelerated rendering.  In that case, both
            // sRendererDisabled and sSystemRendererDisabled are set.  When
            // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
            // can be used by code on the system process to escape that and enable
            // HW accelerated drawing.  (This is basically for the lock screen.)

            final boolean fakeHwAccelerated = (attrs.privateFlags &
                    WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
            final boolean forceHwAccelerated = (attrs.privateFlags &
                    WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;

            if (fakeHwAccelerated) {
                // This is exclusively for the preview windows the window manager
                // shows for launching applications, so they will look more like
                // the app being launched.
                mAttachInfo.mHardwareAccelerationRequested = true;
            } else if (!HardwareRenderer.sRendererDisabled
                    || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
                .......

                mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
                if (mAttachInfo.mHardwareRenderer != null) {
                    .......
                    mAttachInfo.mHardwareAccelerated =
                            mAttachInfo.mHardwareAccelerationRequested = true;
                }
            }
        }
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

 

雖然硬件加速渲染是個好東西,但是也不是每一個需要繪制UI的進程都必需的。這樣做是考慮到兩個因素。第一個因素是並不是所有的Canvas API都可以被GPU支持。如果應用程序使用到了這些API,那麼就需要禁用硬件加速渲染。第二個因素是支持硬件加速渲染的代價是增加了內存開銷。例如,只是硬件加速渲染環境初始化這一操作,就要花掉8M的內存。因此,對於兩類進程就不是很適合使用硬件加速渲染。

第一類進程是Persistent進程。Persistent進程是一種常駐進程,它們的優先級別很高,即使在內存緊張的時候也不會被AMS殺掉。對於低內存設備,這類進程是不適合使用硬件加速渲染的。在這種情況下,它們會將HardwareRenderer類的靜態成員變量sRendererDisabled設置為true,表明要禁用硬件加速渲染。這裡順便提一下,一個應用程序進程可以在AndroidManifest.xml文件將Application標簽的persistent屬性設置為true來將自己設置為Persistent進程,不過只有系統級別的應用設置才有效。類似的進程有Phone、Bluetooth和Nfc等應用。

第二類進程是System進程。System進程有很多線程是需要顯示UI的。這些UI一般都是比較簡單的,並且System進程也像Persistent進程一樣,在內存緊張時是無法殺掉的,因此它們完全沒有必要通過硬件加速來渲染。於是,System進程就會將HardwareRenderer類的靜態成員變量sRendererDisabled和sSystemRendererDisabled都會被設置為true,表示它要禁用硬件加速渲染。

對於System進程,有兩種UI需要特殊處理。第一種UI是Starting Window。當一個Activity啟動時,如果它的宿主進程還沒有創建,那麼在等待其宿主進程創建的過程中,System進程就會根據該Activity窗口設置的Theme顯示一個Starting Window,也稱為Preview Window。由於System進程是禁用了硬件加速渲染的,因此Starting Window是通過軟件方式渲染的。但是為了使得Starting Window的渲染更像它對應的Activity窗口,我們將用來描述Starting Window屬性的一個AttachInfo對象的成員變量mHardwareAccelerationRequested的值設置為true。這將會使得Starting Window的view_state_accelerated屬性設置為true。該屬性一旦被設置為true,將會使得Starting Window的另一屬性colorBackgroundCacheHint被忽略。屬性colorBackgroundCacheHint被忽略之後,Starting Window在繪制的過程中將不會被緩存。使用了硬件加速渲染的Activity窗口在渲染的過程中也是不會被緩存的。這就使得它們的渲染行為保持一致。Starting Window的這一特性是通過將參數attrs指向的一個WindowManager.LayoutParams對象的成員變量privateFlags的位WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED設置為1來描述的。

第二種UI是鎖屏界面。鎖屏界面是一個例如,它允許使用硬件加速渲染。但是System進程又表明了它要禁用硬件加速渲染,這時候就通過將參數attrs指向的一個WindowManager.LayoutParams對象的成員變量privateFlags的位WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED設置為1來強調鎖屏界面不受System進程禁用硬件加速的限制。

除了上面提到的兩類進程以及一些特殊的UI,其余的就根據Activity窗口自己是否請求了硬件加速渲染而決定是否要為其開啟硬件加速。在默認情況下,Activity窗口是請求硬件加速渲染的,也就是參數attrs指向的一個WindowManager.LayoutParams對象的成員變量flags的位WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED是被設置為1的。不過即便如此,也是要設備本身支持硬件加速渲染才行。判斷設備是否設置硬件加速渲染可以通過調用HardwareRenderer類的靜態成員函數isAvailable來獲得。

最後,如果當前創建的窗口支持硬件加速渲染,那麼就會調用HardwareRenderer類的靜態成員函數create創建一個HardwareRenderer對象,並且保存在與該窗口關聯的一個AttachInfo對象的成員變量的成員變量mHardwareRenderer對象。這個HardwareRenderer對象以後將負責執行窗口硬件加速渲染的相關操作。

HardwareRenderer類的靜態成員函數create的實現如下所示:

 

public abstract class HardwareRenderer {
    ......

    static HardwareRenderer create(Context context, boolean translucent) {
        HardwareRenderer renderer = null;
        if (GLES20Canvas.isAvailable()) {
            renderer = new ThreadedRenderer(context, translucent);
        }
        return renderer;
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/HardwareRenderer.java。

 

從這裡就可以看到,在設備支持Open GL ES 2.0的情況下,HardwareRenderer類的靜態成員函數create創建的實際上是一個ThreadedRenderer對象。該ThreadedRenderer對象是從HardwareRenderer類繼承下來的。

接下來我們就繼續分析ThreadedRenderer對象的創建過程,如下所示:

 

public class ThreadedRenderer extends HardwareRenderer {
    ......

    private long mNativeProxy;
    ......
    private RenderNode mRootNode;
    ......

    ThreadedRenderer(Context context, boolean translucent) {
        ......

        long rootNodePtr = nCreateRootRenderNode();
        mRootNode = RenderNode.adopt(rootNodePtr);
        ......
        mNativeProxy = nCreateProxy(translucent, rootNodePtr);

        AtlasInitializer.sInstance.init(context, mNativeProxy);

        ......
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java。

 

在創建ThreadedRenderer對象的過程中,最主要的是做了三件事情:

1. 調用ThreadedRenderer類的成員函數nCreateRootRenderNode在Native層創建了一個Render Node,並且通過Java層的RenderNode類的靜態成員函數adopt將其封裝在一個Java層的Render Node中。這個Render Node即為窗口的Root Render Node。

2. 調用ThreadedRenderer類的成員函數nCreateProxy在Native層創建了一個Render Proxy對象。該Render Proxy對象以後將負責從Main Thread向Render Thread發送命令。

3. 調用AtlasInitializer類的成員函數init初始化一個系統預加載資源的地圖集。通過這個地圖集,可以優化資源的內存使用。

關於系統預加載資源地圖集,我們在下一篇文章中再詳細分析,這裡我們主要關注窗口的Root Render Node以及在Main Thread線程中使用的Render Proxy對象的創建過程。

窗口的Root Render Node是通過調用ThreadedRenderer類的成員函數nCreateRootRenderNode創建的。這是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_createRootRenderNode實現,如下所示:

 

static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
    RootRenderNode* node = new RootRenderNode(env);
    node->incStrong(0);
    node->setName(RootRenderNode);
    return reinterpret_cast(node);
}
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。

 

從這裡就可以看出,窗口在Native層的Root Render Node實際上是一個RootRenderNode對象。

窗口在Main Thread線程中使用的Render Proxy對象是通過調用ThreadedRenderer類的成員函數nCreateProxy創建的。這是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_createProxy實現,如下所示:

static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
        jboolean translucent, jlong rootRenderNodePtr) {
    RootRenderNode* rootRenderNode = reinterpret_cast(rootRenderNodePtr);
    ContextFactoryImpl factory(rootRenderNode);
    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
}
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。

 

參數rootRenderNodePtr指向前面創建的RootRenderNode對象。有了這個RootRenderNode對象之後,函數android_view_ThreadedRenderer_createProxy就創建了一個RenderProxy對象。

RenderProxy對象的創建過程如下所示:

 

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance())
        , mContext(0) {
    SETUP_TASK(createContext);
    args->translucent = translucent;
    args->rootRenderNode = rootRenderNode;
    args->thread = &mRenderThread;
    args->contextFactory = contextFactory;
    mContext = (CanvasContext*) postAndWait(task);
    mDrawFrameTask.setContext(&mRenderThread, mContext);
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。

 

RenderProxy類有三個重要的成員變量mRenderThread、mContext和mDrawFrameTask,它們的類型分別為RenderThread、CanvasContext和DrawFrameTask。其中,mRenderThread描述的就是Render Thread,mContext描述的是一個畫布上下文,mDrawFrameTask描述的是一個用來執行渲染任務的Task。接下來我們就重點分析這三個成員變量的初始化過程。

RenderProxy類的成員變量mRenderThread指向的Render Thread是通過調用RenderThread類的靜態成員函數getInstance獲得的。從名字我們就可以看出,RenderThread類的靜態成員函數getInstance返回的是一個RenderThread單例。也就是說,在一個Android應用程序進程中,只有一個Render Thread存在。

為了更好地了解Render Thread是如何運行的,我們繼續分析Render Thread的創建過程,如下所示:

 

RenderThread::RenderThread() : Thread(true), Singleton()
        ...... {
    mFrameCallbackTask = new DispatchFrameCallbacks(this);
    mLooper = new Looper(false);
    run(RenderThread);
}

這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

RenderThread類的成員變量mFrameCallbackTask指向一個DispatchFrameCallbacks對象,用來描述一個幀繪制任務。下面描述Render Thread的運行模型時,我們再詳細分析。

RenderThread類的成員變量mLooper指向一個Looper對象,Render Thread通過它來創建一個消息驅動運行模型,類似於Main Thread的消息驅動運行模型。關於Looper的實現,可以參考前面Android應用程序消息處理機制(Looper、Handler)分析一文。

RenderThread類是從Thread類繼承下來的,當我們調用它的成員函數run的時候,就會創建一個新的線程。這個新的線程的入口點函數為RenderThread類的成員函數threadLoop,它的實現如下所示:

 

bool RenderThread::threadLoop() {
    .......
    initThreadLocals();

    int timeoutMillis = -1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
        ......

        nsecs_t nextWakeup;
        // Process our queue, if we have anything
        while (RenderTask* task = nextTask(&nextWakeup)) {
            task->run();
            // task may have deleted itself, do not reference it again
        }
        if (nextWakeup == LLONG_MAX) {
            timeoutMillis = -1;
        } else {
            nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
            if (timeoutMillis < 0) {
                timeoutMillis = 0;
            }
        }

        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
            drainDisplayEventQueue(true);
            mFrameCallbacks.insert(
                    mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
            mPendingRegistrationFrameCallbacks.clear();
            requestVsync();
        }
    }

    return false;
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

這裡我們就可以看到Render Thread的運行模型:

1. 空閒的時候,Render Thread就睡眠在成員變量mLooper指向的一個Looper對象的成員函數pollOnce中。

2. 當其它線程需要調度Render Thread,就會向它的任務隊列增加一個任務,然後喚醒Render Thread進行處理。Render Thread通過成員函數nextTask獲得需要處理的任務,並且調用它的成員函數run進行處理。

RenderThread類的成員函數nextTask的實現如下所示:

 

RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
    AutoMutex _lock(mLock);
    RenderTask* next = mQueue.peek();
    if (!next) {
        mNextWakeup = LLONG_MAX;
    } else {
        mNextWakeup = next->mRunAt;
        // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
        if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
            next = mQueue.next();
        } else {
            next = 0;
        }
    }
    if (nextWakeup) {
        *nextWakeup = mNextWakeup;
    }
    return next;
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

RenderThread類的成員變量mQueue描述的是一個Task Queue。每一個Task都是用一個RenderTask對象來描述。同時,RenderTask類有一個成員變量mRunAt,用來表明Task的執行時間。這樣,保存在Task Queue的Task就可以按照執行時間從先到後的順序排序。於是,RenderThread類的成員函數nextTask通過判斷排在隊列頭的Task的執行時間是否小於等於當前時間,就可以知道當前是否有Task需要執行。如果有Task需要執行的話,就將它返回給調用者。

RenderThread類的成員函數nextTask除了返回下一個要執行的Task之外,還會通過參數nextWakeup返回下一個要執行的Task的執行時間。這個時間同時也會記錄在RenderThread類的成員變量mNextWakeup中。注意,下一個要執行的Task可能是馬上就要執行的,也有可能是由於執行時間還未到而不能執行的Task。返回這個時間的意義是使得Render Thread可以准確計算下一次需要進入睡眠狀態的時間。這個計算可以回過頭去看前面分析的RenderThread類的成員函數threadLoop。

注意,如果沒有下一個任務可以執行,那麼RenderThread類的成員函數nextTask通過參數nextWakeup返回的值為LLONG_MAX,表示Render Thread接下來無限期進入睡眠狀態,直到被其它線程喚醒為止。

RenderThread類提供了queue、queueAtFront和queueDelayed三個成員函數向Task Queue增加一個Task,它們的實現如下所示:

 

void RenderThread::queue(RenderTask* task) {
    AutoMutex _lock(mLock);
    mQueue.queue(task);
    if (mNextWakeup && task->mRunAt < mNextWakeup) {
        mNextWakeup = 0;
        mLooper->wake();
    }
}

void RenderThread::queueAtFront(RenderTask* task) {
    AutoMutex _lock(mLock);
    mQueue.queueAtFront(task);
    mLooper->wake();
}

void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
    queue(task);
}
這三個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

其中, RenderThread類的成員函數queue按照執行時間將參數task描述的Task排列在Task Queue中,並且如果該Task的執行時間小於之前記錄的下一個要執行任務的執行時間,就會馬上喚醒Render Thread來處理;RenderThread類的成員函數queue將參數task描述的Task排列在Task Queue的頭部,並且馬上喚醒Render Thread來處理;RenderThread類的成員函數queueDelayed指定參數task描述的Task的執行時間為當前時間之後的delayMs毫秒。

理解了Render Thread的Task Queue之後,回到RenderThread類的成員函數threadLoop中,我們再來看Render Thread在進入無限循環之前調用的RenderThread類的成員函數initThreadLocals,它的實現如下所示:

 

void RenderThread::initThreadLocals() {
    initializeDisplayEventReceiver();
    mEglManager = new EglManager(*this);
    mRenderState = new RenderState();
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

RenderThread類的成員函數initThreadLocals首先調用另外一個成員函數initializeDisplayEventReceiver創建和初始化一個DisplayEventReceiver對象,用來接收Vsync信號。接著又會分別創建一個EglManager對象和一個RenderState對象,並且保存在成員變量mEglManager和mRenderState中。前者用在初始化Open GL渲染上下文需要用到,而後者用來記錄Render Thread當前的一些渲染狀態。

接下來我們主要關注DisplayEventReceiver對象的創建和初始化過程,即RenderThread類的成員函數initializeDisplayEventReceiver的實現,如下所示:

 

void RenderThread::initializeDisplayEventReceiver() {
    ......
    mDisplayEventReceiver = new DisplayEventReceiver();
    ......

    // Register the FD
    mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
            Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

創建的DisplayEventReceiver對象關聯的文件描述符被注冊到了Render Thread的消息循環中。這意味著屏幕產生Vsync信號時,SurfaceFlinger服務(Vsync信號由SurfaceFlinger服務進行管理和分發)會通過上述文件描述符號喚醒Render Thread。這時候Render Thread就會調用RenderThread類的靜態成員函數displayEventReceiverCallback。

RenderThread類的靜態成員函數displayEventReceiverCallback的實現如下所示:

 

int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
    ......

    reinterpret_cast(data)->drainDisplayEventQueue();

    return 1; // keep the callback
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

RenderThread類的靜態成員函數displayEventReceiverCallback調用RenderThread類的成員函數drainDisplayEventQueue來處理Vsync信號,後者的實現如下所示:

 

void RenderThread::drainDisplayEventQueue(bool skipCallbacks) {
    ......
    nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
    if (vsyncEvent > 0) {
        mVsyncRequested = false;
        mTimeLord.vsyncReceived(vsyncEvent);
        if (!skipCallbacks && !mFrameCallbackTaskPending) {
            ......
            mFrameCallbackTaskPending = true;
            queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
        }
    }
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

RenderThread類的靜態成員函數displayEventReceiverCallback首先調用另外一個成員函數latestVsyncEvent獲得最新發出的Vsync信號的時間。如果這個時間值大於0,那麼就表明這是一個有效的Vsync信號。在這種情況下,就將RenderThread類的成員變量mVsyncRequested設置為true,表示上次發出的Vsync信號接收請求已經獲得。

最後,如果參數skipCallbacks的值等於false,那麼就表示需要將RenderThread類的成員變量mFrameCallbackTask指向的一個類型為DispatchFrameCallbacks的Task添加到Render Thread的Task Queue去處理。但是這時候如果RenderThread類的成員變量mFrameCallbackTaskPending的值也等於true,就表示該Task已經添加到Render Thread的Task Queue去了,因此就不再用重復添加。

RenderThread類的成員變量mFrameCallbackTask描述的Task是用來做什麼的呢?原來就是用來顯示動畫的。當Java層注冊一個動畫類型的Render Node到Render Thread時,一個類型為IFrameCallback的回調接口就會通過RenderThread類的成員函數postFrameCallback注冊到Render Thread的一個Pending Registration Frame Callbacks列表中,如下所示:

 

void RenderThread::postFrameCallback(IFrameCallback* callback) {
    mPendingRegistrationFrameCallbacks.insert(callback);
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

Render Thread的Pending Registration Frame Callbacks列表由RenderThread類的成員變量mPendingRegistrationFrameCallbacks描述。

當Pending Registration Frame Callbacks列表不為空時,每次Vsync信號到來時,Render Thread都會通過RenderThread類的成員變量mFrameCallbackTask描述的一個Task來執行它,這樣就相當於是將動畫的每一幀都同步到Vsync信號來顯示。這也是為什麼RenderThread類的成員函數drainDisplayEventQueue每次被調用要檢查是否需要將成員變量mFrameCallbackTask描述的一個Task添加到Render Thread的Task Queue的原因。

當RenderThread類的成員變量mFrameCallbackTask描述的Task的類型為DispatchFrameCallbacks,當它被調度執行時,它的成員函數run就會被調用,如下所示:

 

class DispatchFrameCallbacks : public RenderTask {
private:
    RenderThread* mRenderThread;
public:
    DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}

    virtual void run() {
        mRenderThread->dispatchFrameCallbacks();
    }
};
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

DispatchFrameCallbacks類的成員函數run調用了RenderThread類的成員函數dispatchFrameCallbacks來執行注冊到Pending Registration Frame Callbacks列表中的IFrameCallback回調接口,如下所示:

 

void RenderThread::dispatchFrameCallbacks() {
    ATRACE_CALL();
    mFrameCallbackTaskPending = false;

    std::set<iframecallback*> callbacks;
    mFrameCallbacks.swap(callbacks);

    for (std::set<iframecallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
        (*it)->doFrame();
    }
}</iframecallback*></iframecallback*>
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。

 

我們回過頭來看前面分析的RenderThread類的成員函數threadLoop,每當Render Thread被喚醒時,它都會檢查Pending Registration Frame Callbacks列表是否不為空。如果不為空,那麼就會將保存在裡面的IFrameCallback回調接口轉移至由RenderThread類的成員變量mFrameCallbacks描述的另外一個IFrameCallback回調接口列表中,並且調用RenderThread類的另外一個成員函數requestVsync請求SurfaceFlinger服務在下一個Vsync信號到來時通知Render Thread,以便Render Thread可以執行剛才被轉移的IFrameCallback回調接口。

現在既然下一個Vsync信號已經到來,因此RenderThread類的成員函數dispatchFrameCallbacks就執行所有轉移至保存在成員變量mFrameCallbacks描述的IFrameCallback回調接口列表中的IFrameCallback接口,即調用它們的成員函數doFrame。

總結來說,Render Thread在運行時主要是做以下兩件事情:

1. 執行Task Queue的任務,這些Task一般就是由Main Thread發送過來的,例如,Main Thread通過發送一個Draw Frame Task給Render Thread的Task Queue中,請求Render Thread渲染窗口的下一幀。

2. 執行Pending Registration Frame Callbacks列表的IFrameCallback回調接口。每一個IFrameCallback回調接口代表的是一個動畫幀,這些動畫幀被同步到Vsync信號到來由Render Thread自動執行。具體來說,就是每當Vsync信號到來時,就將一個類型為DispatchFrameCallbacks的Task添加到Render Thread的Task Queue去等待調度。一旦該Task被調度,就可以在Render Thread中執行注冊在Pending Registration Frame Callbacks列表中的IFrameCallback回調接口了。

在後面的文章中,我們還會繼續分析上述這兩件事情的詳細執行過程。

了解了Render Thread的創建過程之後,回到RenderProxy類的構造函數中,接下來我們繼續分析它的成員變量mContext的初始化過程,也就是畫布上下文的初始化過程。這是通過向Render Thread發送一個createContext命令來完成的。為了方便描述,我們將相關的代碼列出來,如下所示:

 

#define ARGS(method) method ## Args

#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)

#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) 
    typedef struct { 
        a1; a2; a3; a4; a5; a6; a7; a8; 
    } ARGS(name); 
    static void* Bridge_ ## name(ARGS(name)* args)

#define SETUP_TASK(method) 
    .......
    MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); 
    ARGS(method) *args = (ARGS(method) *) task->payload()


CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
        RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    return new CanvasContext(*args->thread, args->translucent,
            args->rootRenderNode, args->contextFactory);
}

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        ...... {
    SETUP_TASK(createContext);
    args->translucent = translucent;
    args->rootRenderNode = rootRenderNode;
    args->thread = &mRenderThread;
    args->contextFactory = contextFactory;
    mContext = (CanvasContext*) postAndWait(task);
    ......
}
這些代碼片斷定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。

 

這是一個典型的Main Thread通過Render Proxy向Render Thread請求執行一個命令的流程,在後面的文章中,碰到類型的代碼時,我們就將忽略中間的封裝環節,直接分析命令的執行過程。

我們首先看宏SETUP_TASK,它需要一個函數作為參數。這個函數通過CREATE_BRIDGEX來聲明,其中X是一個數字,數字的大小就等於函數需要的參數的個數。例如,通過CREATE_BRIDGE4聲明的函數有4個參數。在上面的代碼段中,我們通過CREATE_BRIDGE4宏聲明了一個createContext函數。

宏SETUP_TASK的作用創建一個類型MethodInvokeRenderTask的Task。這個Task關聯有一個由CREATE_BRIDGEX宏聲明的函數。例如,SETUP_TASK(createContext)創建的MethodInvokeRenderTask關聯的函數是由CREATE_BRIDGE4聲明的函數createContext。這個Task最終會通過RenderProxy類的成員函數postAndWait添加到Render Thread的Task Queue中,如下所示:

 

void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
    void* retval;
    task->setReturnPtr(&retval);
    SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
    AutoMutex _lock(mSyncMutex);
    mRenderThread.queue(&syncTask);
    mSyncCondition.wait(mSyncMutex);
    return retval;
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。

 

RenderProxy類的成員函數postAndWait除了會將參數task描述MethodInvokeRenderTask描述的Task添加到Render Thread的Task Queue之外,還會等待該Task執行完成之後才返回。也就是說,這是一個從Main Thread到Render Thread的同步調用過程。

當MethodInvokeRenderTask被執行時,它所關聯的函數就會被調用。例如,在我們這個情景中,通過CREATE_BRIDGE4聲明的函數createContext就會被執行。注意,這時候函數createContext是在Render Thread中執行的,它主要就是創建一個CanvasContext對象,用來描述Render Thread的畫布上下文。這個畫布上下文接下來在Render Thread中綁定窗口時會用到,到時候我們就可以更清楚地看到它的作用。

回到RenderProxy類的構造函數中,接下來我們繼續分析它的成員變量mDrawFrameTask的初始化過程。RenderProxy類的成員變量mDrawFrameTask描述的是一個Draw Frame Task,Main Thread每次都是通過它來向Render Thread發出渲染下一幀的命令的。

對Draw Frame Task的初始化很簡單,主要是將前面已經獲得的RenderThread對象和CanvasContext對象保存在它內部,以便以後它可以直接使用相關的功能,這是通過調用DrawFrameTask類的成員函數setContext實現的,如下所示:

 

void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
    mRenderThread = thread;
    mContext = context;
}
這個函數定義在文件frameworks/base/libs/hwui$ vi renderthread/DrawFrameTask.cpp中。

 

這樣,一個RenderProxy對象的創建過程就分析完成了,從中我們也看到Render Thread的創建過程和運行模型,以及Render Proxy與Render Thread的交互模型,總結來說:

1. RenderProxy內部有一個成員變量mRenderThread,它指向的是一個RenderThread對象,通過它可以向Render Thread線程發送命令。

2. RenderProxy內部有一個成員變量mContext,它指向的是一個CanvasContext對象,Render Thread的渲染工作就是通過它來完成的。

3. RenderProxy內部有一個成員變量mDrawFrameTask,它指向的是一個DrawFrameTask對象,Main Thread通過它向ender Thread線程發送渲染下一幀的命令。

接下來我們繼續分析Android應用程序UI硬件加速渲染環境初始化的另一個主要任務--綁定窗口到Render Thread中。

從前面Android應用程序窗口(Activity)的測量(Measure)、布局(Layout)和繪制(Draw)過程分析這篇文章可以知道,Activity窗口的繪制流程是在ViewRoot(Impl)類的成員函數performTraversals發起的。在繪制之前,首先要獲得一個Surface。這個Surface描述的就是一個窗口。因此,一旦獲得了對應的Surface,就需要將它綁定到Render Thread中,如下所示:

 

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ......

    private void performTraversals() {
        ......

        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null) {
            ......

            try {
                ......

                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                ......

                if (!hadSurface) {
                    if (mSurface.isValid()) {
                        ......
                     
                        if (mAttachInfo.mHardwareRenderer != null) {
                            try {
                                hwInitialized = mAttachInfo.mHardwareRenderer.initialize(
                                        mSurface);
                            } catch (OutOfResourcesException e) {
                                ......
                            }
                        }
                    }
                } 

                ......
            } catch (RemoteException e) {
            }

            ......
        }

        if (!cancelDraw && !newSurface) {
            if (!skipDraw || mReportNextDraw) {
                ......

               performDraw();
            }
        } 

        ......
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

 

當前Activity窗口對應的Surface是通過調用ViewRootImpl類的成員函數relayoutWindow向WindowManagerService服務請求創建和返回的,並且保存在ViewRootImpl類的成員變量mSurface中。如果這個Surface是新創建的,那麼就會調用ViewRootImpl類的成員變量mAttachInfo指向的一個AttachInfo對象的成員變量mHardwareRenderer描述的一個HardwareRenderer對象的成員函數initialize將它綁定到Render Thread中。最後,如果需要繪制當前的Activity窗口,那會調用ViewRootImpl類的另外一個成員函數performDraw進行繪制。

這裡我們只關注綁定窗口對應的Surface到Render Thread的過程。從前面的分析可以知道,ViewRootImpl類的成員變量mAttachInfo指向的一個AttachInfo對象的成員變量mHardwareRenderer保存的實際上是一個ThreadedRenderer對象,它的成員函數initialize的實現如下所示:

 

public class ThreadedRenderer extends HardwareRenderer {
    ......

    @Override
    boolean initialize(Surface surface) throws OutOfResourcesException {
        mInitialized = true;
        ......
        boolean status = nInitialize(mNativeProxy, surface);
        ......
        return status;
    }

    ......
}
這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

 

ThreadedRenderer類的成員函數initialize首先將成員變量mInitialized的值設置為true,表明它接下來已經綁定過Surface到Render Thread了,接著再調用另外一個成員函數nInitialize執行真正的綁定工作。

ThreadedRenderer類的成員函數nInitialize是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_initialize實現,如下所示:

 

static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jobject jsurface) {
    RenderProxy* proxy = reinterpret_cast(proxyPtr);
    sp window = android_view_Surface_getNativeWindow(env, jsurface);
    return proxy->initialize(window);
}
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。

 

參數proxyPtr描述的就是之前所創建的一個RenderProxy對象,而參數jsurface描述的是要綁定給Render Thread的Java層的Surface。前面提到,Java層的Surface在Native層對應的是一個ANativeWindow。我們可以通過函數android_view_Surface_getNativeWindow來獲得一個Java層的Surface在Native層對應的ANativeWindow。

接下來,就可以通過RenderProxy類的成員函數initialize將前面獲得的ANativeWindow綁定到Render Thread中,如下所示:

 

CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
    return (void*) args->context->initialize(args->window);
}

bool RenderProxy::initialize(const sp& window) {
    SETUP_TASK(initialize);
    args->context = mContext;
    args->window = window.get();
    return (bool) postAndWait(task);
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。

 

從前面的分析可以知道,RenderProxy類的成員函數initialize向Render Thread的Task Queue發送了一個Task。當這個Task在Render Thread中執行時,由宏CREATE_BRIDGE2聲明的函數initialize就會被執行。

在由宏CREATE_BRIDGE2聲明的函數initialize中,參數context指向的是RenderProxy類的成員變量mContext指向的一個CanvasContext對象,而參數window指向的ANativeWindow就是要綁定到Render Thread的ANativeWindow。

由宏CREATE_BRIDGE2聲明的函數initialize通過調用參數context指向的CanvasContext對象的成員函數initialize來綁定參數window指向的ANativeWindow,如下所示:

 

bool CanvasContext::initialize(ANativeWindow* window) {
    setSurface(window);
    if (mCanvas) return false;
    mCanvas = new OpenGLRenderer(mRenderThread.renderState());
    ......
    return true;
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。

 

CanvasContext類的成員函數initialize通過調用另外一個成員函數setSurface來綁定參數window描述的ANativeWindow到Render Thread中。綁定完成之後,如果CanvasContext類的成員變量mCanvas等於NULL,那麼就說明負責執行Open GL渲染命令的一個OpenGLRenderer對象還沒創建。在這種情況下,就創建一個OpenGLRenderer對象,並且保存在CanvasContext類的成員變量mCanvas中,以便後面可以用來執行Open GL相關操作。

CanvasContext類的成員函數setSurface的實現如下所示:

 

void CanvasContext::setSurface(ANativeWindow* window) {
    mNativeWindow = window;

    if (mEglSurface != EGL_NO_SURFACE) {
        mEglManager.destroySurface(mEglSurface);
        mEglSurface = EGL_NO_SURFACE;
    }

    if (window) {
        mEglSurface = mEglManager.createSurface(window);
    }

    if (mEglSurface != EGL_NO_SURFACE) {
        ......
        mHaveNewSurface = true;
        makeCurrent();
    } 

    ......
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。

 

每一個Open GL渲染上下文都需要關聯有一個EGL Surface。這個EGL Surface描述的是一個繪圖表面,它封裝的實際上是一個ANativeWindow。有了這個EGL Surface之後,我們在執行Open GL命令的時候,才能確定這些命令是作用在哪個窗口上。

CanvasContext類的成員變量mEglManager實際上是指向前面我們分析RenderThread類的成員函數initThreadLocals時創建的一個EglManager對象。通過調用這個EglManager對象的成員函數createSurface就可以將參數window描述的ANativeWindow封裝成一個EGL Surface。

EGL Surface創建成功之後,就可以調用CanvasContext類的成員函數makeCurrent將它綁定到Render Thread的Open GL渲染上下文來,如下所示:

 

void CanvasContext::makeCurrent() {
    // TODO: Figure out why this workaround is needed, see b/13913604
    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
}
這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。

 

從這裡就可以看到,將一個EGL Surface綁定到Render Thread的Open GL渲染上下文中是通過CanvasContext類的成員變量mEglManager指向的一個EglManager對象的成員函數makeCurrent來完成的。實際上就是通過EGL函數建立了從Open GL到底層OS圖形系統的橋梁。這一點應該怎麼理解呢?Open GL是一套與OS無關的規范,不過當它在一個具體的OS實現時,仍然是需要與OS的圖形系統打交道的。例如,Open GL需要從底層的OS圖形系統中獲得圖形緩沖區來保存渲染結果,並且也需要將渲染好的圖形緩沖區交給底層的OS圖形系統來顯示到設備屏幕去。Open GL與底層的OS圖形系統的這些交互通道都是通過EGL函數來建立的。

至此,將當前窗口綁定到Render Thread的過程就分析完成了,整個Android應用程序UI硬件加速渲染環境的初始化過程也分析完成了。前面在分析ThreadedRenderer對象的創建過程時提到了系統預加載資源地圖集的概念,在繼續分析Android應用程序UI硬件加速渲染的Display List構建和渲染過程之前,我們有必要先分析這個地圖集的概念,因為這是Display List在渲染時涉及到的一個重要優化,敬請關注! 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved