Android Graphics Framework

作者: xiabodan | 来源:发表于2017-11-22 17:17 被阅读531次

    Android系统图形框架

    Android系统图形框架由下往上主要的包括HAL(HWComposer和Gralloc两个moudle),SurfaceFlinger(BufferQueue的消费者),WindowManagerService(窗口管理者),View(BufferQueue的生产者)四大模块。

    • HAL: 包括HWComposer和Gralloc两个moudle,Android N上由SurfaceFlinger打开,因此在同一进程。 gralloc 用于BufferQueue的内存分配,同时也有fb的显示接口,HWComposer作为合成SurfaceFlinger里面的Layer,并显示(通过gralloc的post函数)
    • SurfaceFlinger也叫LayerFlinger,作为Layer的管理着,同是也是BufferQueue的消费者,当每个Layer的生产者draw完完整的一帧时,会通知SurfaceFlinger,通知的方式采用BufferQueue。
    • WindowManagerService: 作为Window的管理者,掌管着计算窗口大小,窗口切换等任务,同时也会将相应的参数设置给SurfaceFlinger,比如Window的在z-order,和窗口的大小。
    • View: 作为BufferQueue的生产者,没当执行lockCanvas->draw->unlockCanvas,之后会存入一帧数据进入BufferQueue中。
      图片.png

    1、低级别组件

    • BufferQueue 和 gralloc。
      BufferQueue 将可生成图形数据缓冲区的组件(生产者)连接到接受数据以便进行显示或进一步处理的组件(消费者)。通过供应商专用 HAL 接口实现的 gralloc 内存分配器将用于执行缓冲区分配任务
    • SurfaceFlinger、Hardware Composer 和虚拟显示屏。
      SurfaceFlinger 接受来自多个源的数据缓冲区,然后将它们进行合成并发送到显示屏。Hardware Composer HAL (HWC) 确定使用可用硬件合成缓冲区的最有效的方法,虚拟显示屏使合成输出可在系统内使用(录制屏幕或通过网络发送屏幕)。
    • Surface、Canvas 和 SurfaceHolder。
      Surface 可生成一个通常由 SurfaceFlinger 使用的缓冲区队列。当渲染到 Surface 上时,结果最终将出现在传送给消费者的缓冲区中。Canvas API 提供一种软件实现方法(支持硬件加速),用于直接在 Surface 上绘图(OpenGL ES 的低级别替代方案)。与视图有关的任何内容均涉及到 SurfaceHolder,其 API 可用于获取和设置 Surface 参数(如大小和格式)。
    • EGLSurface 和 OpenGL ES。
      OpenGL ES (GLES) 定义了用于与 EGL 结合使用的图形渲染 API。EGI 是一个规定如何通过操作系统创建和访问窗口的库(要绘制纹理多边形,请使用 GLES 调用;要将渲染放到屏幕上,请使用 EGL 调用)。此页还介绍了 ANativeWindow,它是 Java Surface 类的 C/C++ 等价类,用于通过原生代码创建 EGL 窗口表面。
    • Vulkan。
      Vulkan 是一种用于高性能 3D 图形的低开销、跨平台 API。与 OpenGL ES 一样,Vulkan 提供用于在应用中创建高质量实时图形的工具。Vulkan 的优势包括降低 CPU 开销以及支持 SPIR-V 二进制中间语言。

    1.1 Gralloc

    gralloc 用于BufferQueue的内存分配,同时也有fb的显示接口

    • gralloc_alloc:用于分配一个显示显示缓冲区,分配好只有会返回缓冲区的handle(缓冲区buffer不会拷贝,所有的操作都是传handle)
    static int gralloc_alloc_buffer(alloc_device_t* dev,
            size_t size, int /*usage*/, buffer_handle_t* pHandle)
    {
        int err = 0;
        int fd = -1;
        size = roundUpToPageSize(size);
    
        fd = ashmem_create_region("gralloc-buffer", size);  // 创建匿名共享内存
        if (fd < 0) {
            ALOGE("couldn't create ashmem (%s)", strerror(-errno));
            err = -errno;
        }
        if (err == 0) {
            private_handle_t* hnd = new private_handle_t(fd, size, 0);
            gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
                    dev->common.module);
            err = mapBuffer(module, hnd); // 同时将创建好的内存映射进入当前进程,返回handle
            if (err == 0) {
                *pHandle = hnd;
            }
        }
        return err;
    }
    
    • gralloc_free :释放缓冲区内存
    • fb_post :更新fb显示

    1.2 BufferQueue

    • BufferQueue 类是 Android 中所有图形处理操作的核心。它的作用很简单:将生成图形数据缓冲区的一方(生产方)连接到接受数据以进行显示或进一步处理的一方(消耗方)。几乎所有在系统中移动图形数据缓冲区的内容都依赖于 BufferQueue。
    • BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。图像生产方的一些示例包括由相机 HAL 或 OpenGL ES 游戏生成的相机预览。图像消耗方的一些示例包括 SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。
    • BufferQueue 永远不会复制缓冲区内容(移动如此多的数据是非常低效的操作)。相反,缓冲区始终通过句柄进行传递。
    • 生产方和消耗方可以存在于不同的进程中。目前,消耗方始终创建和拥有数据结构。在旧版本的 Android 中,只有生产方才进行 Binder 处理(即生产方可能在远程进程中,但消耗方必须存在于创建队列的进程中)。Android 4.4 和更高版本已发展为更常规的实现。


      图片.png

    1.3 BufferQueue生产者与消费者分析

    当应用add一个window到WMS时,WMS会调用SurfaceFlinger为这个window创建一个BufferQueue,BufferQueue的消费者留在了SurfaceFlinger中,而生产者会返回到View中,View中采用Canvas或者GL绘图,用于消费者

        // ViewRootImpl.java
        private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                boolean insetsPending) throws RemoteException {
            int relayoutResult = mWindowSession.relayout(
                    mWindow, mSeq, params,
                    (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                    (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                    viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                    mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                    mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
                    mSurface);            
        }
    

    其中的
    mWindow即每个应用窗口所创建的window,和ActivityRecord、DecorView一一对应
    mSurface即为当前这个Layer的BufferQueue生产者

    void Layer::onFirstRef() {
        // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
        sp<IGraphicBufferProducer> producer;
        sp<IGraphicBufferConsumer> consumer;
        BufferQueue::createBufferQueue(&producer, &consumer);
        mProducer = new MonitoredProducer(producer, mFlinger);
        mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
                this);
        mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
        mSurfaceFlingerConsumer->setContentsChangedListener(this);
        mSurfaceFlingerConsumer->setName(mName);
    
    #ifdef TARGET_DISABLE_TRIPLE_BUFFERING
    #warning "disabling triple buffering"
    #else
        mProducer->setMaxDequeuedBufferCount(2);
    #endif
    
        const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
        updateTransformHint(hw);
    }
    

    createBufferQueue即为创建BufferQueue的地方,同时通过setContentsChangedListener将this注册到消费者中去,当生产者mSurface有新的帧生成后,会通知到Layer中的onFrameAvailable

    // Layer.cpp
    void Layer::onFrameAvailable(const BufferItem& item) {
        // Add this buffer from our internal queue tracker
        { // Autolock scope
            Mutex::Autolock lock(mQueueItemLock);
    
            mQueueItems.push_back(item);
            android_atomic_inc(&mQueuedFrames);
    
            // Wake up any pending callbacks
            mLastFrameNumberReceived = item.mFrameNumber;
            mQueueItemCondition.broadcast();
        }
    
        mFlinger->signalLayerUpdate();
    }
    

    2、 SurfaceFlinger

    • 管理Layer的创建与销毁,同时单个Layer又要接收来自WMS的设置参数(setLayer, setPosition , setSize setAlpha, createLayer, removeLayer等)
    • Layer的可见区域计算,合成Layer
    • 渲染合成图像到fb
    • 管理VSYNC信号
    • 创建BufferQueue用于SurfaceFlinger的缓冲区


      图片.png

    2.1 创建BufferQueue用于SurfaceFlinger中的缓冲区(非Layer中的BufferQueue)

    void SurfaceFlinger::init() {
        for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {    
                wp<IBinder> token = mBuiltinDisplays[i];
    
                sp<IGraphicBufferProducer> producer;
                sp<IGraphicBufferConsumer> consumer;
                BufferQueue::createBufferQueue(&producer, &consumer,
                        new GraphicBufferAlloc());  // 创建BufferQueue
    
                sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                        consumer);  // 消费者放入FramebufferSurface
                int32_t hwcId = allocateHwcDisplayId(type);
                sp<DisplayDevice> hw = new DisplayDevice(this,
                        type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                        fbs, producer,
                        mRenderEngine->getEGLConfig()); // 生产者是DisplayDevice,本质是合成只有的图像
    
                if (i > DisplayDevice::DISPLAY_PRIMARY) {
                    ALOGD("marking display %zu as acquired/unblanked", i);
                    hw->setPowerMode(HWC_POWER_MODE_NORMAL);
                }
                mDisplays.add(token, hw);    
        }
    }
    

    2.2 Layer更新

    前面讲BufferQueue时,每当Layer中有新的buffer准备好了,一定会通知相应Layer的onFrameAvailable,从而通过signalLayerUpdate通知SurfaceFlinger,这就是开始处理Layer的入口

    void SurfaceFlinger::signalLayerUpdate() {
        mEventQueue.invalidate();
    }
    
    void MessageQueue::invalidate() {
        mEvents->requestNextVsync();
    }
    

    但是onMessageReceived并不会立即收到消息去执行handleMessageTransaction、handleMessageRefresh,而是等到下一个VSYNC信号到来

    void SurfaceFlinger::onMessageReceived(int32_t what) {
        ATRACE_CALL();
        switch (what) {
            case MessageQueue::INVALIDATE: {
                bool refreshNeeded = handleMessageTransaction();
                refreshNeeded |= handleMessageInvalidate();
                refreshNeeded |= mRepaintEverything;
                if (refreshNeeded) {
                    // Signal a refresh if a transaction modified the window state,
                    // a new buffer was latched, or if HWC has requested a full
                    // repaint
                    signalRefresh();
                }
                break;
            }
            case MessageQueue::REFRESH: {
                handleMessageRefresh();
                break;
            }
        }
    }
    

    2.3 VSYNC信号

    • SurfaceFlinger负责在收到的硬件VSYNC信号,通知EventThread,EventThread存放着所有注册过的Connection,只要有Connection存在,就会触发相应的事件,Looper中会响应事件并回调


      图片.png
    • Choreographer的VSYNC信号和SurfaceFlinger接收VSYNC信号原理一样,都是通过注册一个Connection监听事件


      图片.png

    2.4 Layer可见性计算与合成显示

    void SurfaceFlinger::handleMessageRefresh() {
        ATRACE_CALL();
    
        nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
    
        preComposition(); //
        rebuildLayerStacks();  //  计算可见区域,主要的依据就是z-order大的覆盖有重叠区域z-order小的
        setUpHWComposer();
        doDebugFlashRegions();
        doComposition();  // 合成图像,并调用swapBuffers,这样消费者FramebufferSurface就会被回调onFrameAvailable
        postComposition(refreshStartTime);
    }
    

    消费者FramebufferSurface收到onFrameAvailable后就会将新的图像post到fb(通过gralloc的post函数)

    // Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
    void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
        sp<GraphicBuffer> buf;
        sp<Fence> acquireFence;
        status_t err = nextBuffer(buf, acquireFence);
        if (err != NO_ERROR) {
            return;
        }
        err = mHwc.fbPost(mDisplayType, acquireFence, buf);  // 调用gralloc的post
    }
    

    3、WindowManagerService框架

    图片.png

    职责

    • 计算窗口大小

    • 计算窗口Z轴位置

    • 管理输入法窗口

    • 管理壁纸窗口

    • 执行窗口切换

    3.1 addWindow

    在Activity的启动中当执行到performLaunchActivity

        // ActivityThread.java
        final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ActivityClientRecord r = mActivities.get(token);
            r = performResumeActivity(token, clearHide, reason);
            if (r != null) {
                final Activity a = r.activity;
                boolean willBeVisible = !a.mStartedActivity;
                if (r.window == null && !a.mFinished && willBeVisible) {
                    r.window = r.activity.getWindow(); //  Window指的是PhoneWindow,在performLaunchActivity中attch函数中创建
                    View decor = r.window.getDecorView();  // decor是在onCreate中用户主动调用setContentView时创建
                    decor.setVisibility(View.INVISIBLE);
                    ViewManager wm = a.getWindowManager();  // 实质是ViewManagerImpl
                    WindowManager.LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor; 
                    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  //APP的type都是TYPE_BASE_APPLICATION
                    l.softInputMode |= forwardBit;
                    if (r.mPreserveWindow) {
                        a.mWindowAdded = true;
                        r.mPreserveWindow = false;
                        // Normally the ViewRoot sets up callbacks with the Activity
                        // in addView->ViewRootImpl#setView. If we are instead reusing
                        // the decor view we have to notify the view root that the
                        // callbacks may have changed.
                        ViewRootImpl impl = decor.getViewRootImpl();
                        if (impl != null) {
                            impl.notifyChildRebuilt();
                        }
                    }
                    if (a.mVisibleFromClient && !a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);   // 向WindowManagerGlobal中添加当前Activity的DecorView
                    }
        }
    

    decor是在onCreate中用户主动调用setContentView时创建
    Window指的是PhoneWindow,在performLaunchActivity中attch函数中创建
    ActivityClientRecord是在App进程中的和AMS中的ActivityRecord一一对应的对象

    WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } else {
            }
    
            ViewRootImpl root;
            View panelParentView = null;
    
            synchronized (mLock) {
                // Start watching for system property changes.
                if (mSystemPropertyUpdater == null) {
                    mSystemPropertyUpdater = new Runnable() {
                        @Override public void run() {
                            synchronized (mLock) {
                                for (int i = mRoots.size() - 1; i >= 0; --i) {
                                    mRoots.get(i).loadSystemProperties();
                                }
                            }
                        }
                    };
                    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
                }
    
                int index = findViewLocked(view, false);
                if (index >= 0) {
                    if (mDyingViews.contains(view)) {
                        // Don't wait for MSG_DIE to make it's way through root's queue.
                        mRoots.get(index).doDie();
                    } else {
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    }
                    // The previous removeView() had not completed executing. Now it has.
                }
    
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            }
            root.setView(view, wparams, panelParentView);
        }
    

    可以看出WindowManagerGlobal是管理view(DecorView),ViewRootImpl,参数的管家,他们都是一对一的关系

    ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } 
    }
    

    开始向WMS添加当前mWindow(W),mWindow继承于IWindow.Stub,也是个binder的对象,WMS可以通过这个binder回调ViewRootImpl

        WindowManagerService.java
        public int addWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                InputChannel outInputChannel) {
        
                boolean addToken = false;
                WindowToken token = mTokenMap.get(attrs.token);
                AppWindowToken atoken = null;
                boolean addToastWindowRequiresToken = false;
                ...
                WindowState win = new WindowState(this, session, client, token,
                        attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
                        mPolicy.adjustWindowParamsLw(win.mAttrs);   // new 一个新的WindowState
                win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
    
                res = mPolicy.prepareAddWindowLw(win, attrs);
                if (res != WindowManagerGlobal.ADD_OKAY) {
                    return res;
                }
    
                final boolean openInputChannels = (outInputChannel != null
                        && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
                if  (openInputChannels) {
                    // 创建一个Input的双向进程间管道,并将read端的fd通过outInputChannel返回,用于ViewRootImpl中创建Input的监听Receiver,管道的write端registerInputChannel注册进入了InputDispatcher,然后加入了InputDispatcher线程的Looper,用于发送事件信号
                    // 这就成功的建立了InputDispatcher与当前应用ViewRootImpl进程间双向通讯
                    win.openInputChannel(outInputChannel);                    
                }
                        
                if (addToken) {
                    mTokenMap.put(attrs.token, token);
                }
                win.attach();
                mWindowMap.put(client.asBinder(), win); // 将创建的WindowState存入WMS
                
                if (focusChanged) {
                    mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);   // 检查焦点窗口是否发生变化,如果发生了变化就要通知InputDispatcher更新焦点窗口
                }
                mInputMonitor.updateInputWindowsLw(false /*force*/);  // 将WMS中所有的InputWindowHandles都更新到InputDispatcher中去,方便InputDispatcher根据触摸坐标寻找相应的window
    

    3.2 relayoutWindow

    
    ViewRootImpl.java
    
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    scheduleTraversals()中利用Choreographer向SurfaceFlinger注册一个vsync信号接收回调,收到回调后会执行performTraversals(),这个函数是对view进行measure,layout,draw的核心函数,当执行performTraversals时发生以下条件事会调用WMS的relayoutWindow,通知WMS window发生的变化

    • 第一次performTraversals调用的时候
    • 当window需要resize(改变尺寸)的时候
    • view可见性发生变化时
    • 其他一些条件
    ViewRootImpl.java
    private void performTraversals() {
            if (mFirst || windowShouldResize || insetsChanged ||
                    viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            }
    }
    

    3.3 SurfaceFlinger Z order顺序确定

    openGlobalTransaction --> setLayer --> closeGlobalTransaction -->closeGlobalTransactionImpl --> setTransactionState --> setClientStateLocked --> mCurrentState.layersSortedByZ.add
    layersSortedByZ是一个LayerVector的结构,继承于SortedVector,在add的同时会用二分法则排序插入
    Z order 的值越大显示越靠前,但是最终显示的判定还要结合layer的可见区域Region visibleRegion来显示,一般情况可见区域只有Navgation Bar + Status Bar + Activity,有些软件播放视频会全屏,那么Navgation Bar + Status Bar 也就不可见了

    
        SurfaceComposerClient::openGlobalTransaction();
        control->setLayer(0x40000000);
        SurfaceComposerClient::closeGlobalTransaction();
    

    4、 窗口(Window)的结构

    图片.png

    ViewRootImpl是一个虚拟根View,用来控制窗口的渲染,以及用来与WindowManagerService、SurfaceFlinger通信

    DecorView是窗口的真正根View,用户在Activity的onCreate中setContentView中创建

    ContentView描述窗口的主题风格

    4.1 ViewRootImpl

    Window的虚拟根View,一个Aactivity对应一个DecorView,对应一个ActivityClientRecord,对应一个ViewRootImpl,ViewRootImpl的作用:

    • 与WMS交互:将window(W:继承于IWindow.Stub)加入到WMS(addToDispaly)

    • 与SurfaceFlinger交互: 因为ViewRootImpl中layout,measure,draw是Surface的的生产者,因此当draw完成以后SurfaceFlinger回取出相应的buffer进行合成显示

    • 注册一个InputEventReceiver :基于WMS返回的InputChannel创建一个Callback,用于监听输入事件mInputEventReceiver一旦被创建,就已经在监听输入事件了

    • performTraversals() : 执行画图三个重要操作measure,layout,draw,主要是在scheduleTraversals()中用Choreographer注册系统刷新下一帧回调。

    • Choreographer:当window有任何改变的时候就会利用Choreographer注册一个vsync信号回调,比如尺寸,参数变化,可见性变化,范围改变,保证显示实时更新

    5、参考

    SurfaceFlinger Z order顺序确定

    相关文章

      网友评论

        本文标题:Android Graphics Framework

        本文链接:https://www.haomeiwen.com/subject/uxdovxtx.html