Android:显示系统总结

显示系统

Hardware Composer是什么?

硬件图层混合器,把不同的view layer混合起来。
参考:https://blog.csdn.net/zhjali123/article/details/81455060

SurfaceFlinger是什么?

是一个数据缓存区的管理者,用于接收来自于多个源。管理的数据缓存区叫做layer。
大多数应用于任何时间在屏幕上具有三个 layers:屏幕顶部的状态栏,底部或侧面的导航栏,以及应用程序 UI。

SurfaceFlinger与windowManager是什么关系?

WM将手机的window元数据(包括屏幕,z-order等)信息发送给SurfaceFlinger,因此SurfaceFlinger 能使用这些信息来合成surfaces,并输出到显示设备.
参考:http://gityuan.com/2017/02/05/graphic_arch/

SurfaceFlinger,windowManager,Hardware Compose是什么关系

windowManager请求SurfaceFlinger,SurfaceFlinger请求Hardware Compose。

应用开发者如何构造图形

可以使用Canvas 或 OpenGL的api。
参考:https://source.android.com/devices/graphics/index.html

Surface是什么?

在 Android 平台上创建的每个窗口都由 Surface 提供支持。

Surface什么时候创建?

在ViewRootImpl的relayoutWindow的时候创建。
https://zhuanlan.zhihu.com/p/30535788

从应用到GPU整个数据流是如何的?

image.png

左侧的对象是生成图形缓冲区的渲染器,如主屏幕、状态栏和系统界面。SurfaceFlinger 是合成器,而硬件混合渲染器是制作器。
参考:https://source.android.com/devices/graphics/index.html

BufferQueue是什么?

image.png

BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。包含三种同步,非同步,舍弃三种工作模式。

应用如何和SurfaceFlinger建立连接?

通过SurfaceComposerClient类来连接。进行Binder通信。
注意,不是直接使用SurfaceFlinger的Binder。

mSession = new SurfaceComposerClient();
sp<SurfaceControl> control = session()->createSurface(
            String8("BootAnimation"), dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

应用端new一个SurfaceComposerClient,然后使用这个类去创建一个SurfaceControl去操作Surface。

下面是SurfaceComposerClient的初始化函数。

void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
    if (sm != 0) {
        auto rootProducer = mParent.promote();
        sp<ISurfaceComposerClient> conn;
        conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) :
                sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

ISurfaceComposer实际是SurfaceFlinger在本地的代理。
/SDM670_Master_8_1/android/frameworks/native/libs/gui/include/gui/ISurfaceComposer.h
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> 服务端的类,一般叫BnXXX,继承于BnInterface

/SDM670_Master_8_1/android/frameworks/native/libs/gui/ISurfaceComposer.cpp
class BpSurfaceComposer : public BpInterface<ISurfaceComposer> 客户端代理的类,一般叫BpXXX,继承于BpInterface

ISurfaceComposerClient也是远端的一个代理,应用的操作都是通过ISurfaceComposerClient来操作的。

SurfaceControl类是干什么的?

java上层操控Surface的一个类。

应用是怎么申请缓冲区的?

app与surfaceflinger间如何共享内存

通过GraphicBufferMapper,通过序列化的方式构造。
通过mmap同一个文件来实现内存共享。
参考:https://www.cnblogs.com/gdk-0078/p/5165242.html
http://www.360doc.com/content/14/0911/15/10366845_408677796.shtml

Gralloc模块干什么的?

Android系统在硬件抽象层中提供了一个Gralloc模块,封装了对帧缓冲区的所有访问操作。

SharedBufferStack过时了吗?

过时了!!

open gl es在android的窗口较什么名字?

FramebufferNativeWindow
https://blog.csdn.net/STN_LCD/article/details/52680679

layer的消费者和生产者是怎么来的?

在layer的构造方法中

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer, true); //创建了一个buffer queue
    mProducer = new MonitoredProducer(producer, mFlinger, this);
    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName, this);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setContentsChangedListener(this);
    mSurfaceFlingerConsumer->setName(mName);

    if (mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }

    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

layer 是什么时候创建的?

SurfaceFlinger.cpp
的createNormalLayer方法中创建了一个layer

status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }

    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }

    ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
    return err;
}

Layer创建流程是怎样的?

从客户端binder通信SurfaceFlinger,让SurfaceFlinger帮忙建一个surface。
SurfaceComposerClient::createSurface ->
Client::createSurface ->
flinger->createLayer ->
outLayer = new Layer(this, client, name, w, h, flags)

Surface的创建流程是怎样的?

在创建Surface的过程中,会在SurfaceFlinger端创建一个Layer。
流程是

WindowManagerService.relayoutWindow -> 
WindowManagerService.createSurfaceControl -> 
 winAnimator.createSurfaceLocked ->
 surfaceController.getSurface(outSurface) ->
 outSurface.copyFrom(mSurfaceControl) ->  
Surface.nativeGetFromSurfaceControl(surfaceControlPtr) -> 
ctrl->getSurface() -> 
new Surface(mGraphicBufferProducer, false);

这个Surface是在wms中的,那应用的呢??应用是这样binder调用

 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,
                mPendingMergedConfiguration, mSurface);

参考https://www.jianshu.com/p/64e5c866b4ae
是通过parcel的方式,在应用端自己new了一个。
在android_view_Surface.cpp函数中,通过binder返回的graphicBufferProducer构建一个Surface。

    if (surfaceShim.graphicBufferProducer != nullptr) {
        // we have a new IGraphicBufferProducer, create a new Surface for it
        sur = new Surface(surfaceShim.graphicBufferProducer, true);
        // and keep a reference before passing to java
        sur->incStrong(&sRefBaseOwner);
    }

获取buffer的流程是怎样的?

RenderThread::initThreadLocals -> EglManager::createSurface -> egl.cpp.eglCreateWindowSurface -> egl.cpp.createWindowSurface
surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
static_cast<ANativeWindow*>(window));

egl_window_surface_v2_t 的connect()函数会dequeuebuffer

RenderThread什么时候启动??

在viewrootimpl中的开启硬件加速时,会创建ThreadRenderer,进而创建RenderProxy代理,该代理会获取RenderThread的单例,创建单例的时候就会启动这个渲染线程。

应用是如何addview的?

WindowManager wm = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
para.type = WindowManager.LayoutParams.TYPE_PHONE;
...
(6.0以上需要动态检查是否被授予了SYSTEM_ALERT_WINDOW权限)
...
wm.addView(view, para);

可以看到
SystemServiceRegistry.java

      registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});

所有是使用WindowManagerImpl来addview的。
参考:https://blog.csdn.net/maximus_chan/article/details/40784367
那么Activity第一个view是从哪里来的?
在ActivityThread中的handleResumeActivity函数中,将decor add进去了。

                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    }

那应用如何开启一次绘制了?

addview,添加一个decorview-> viewrootimple.travlasl -> relayout获取一个surface->performdraw调用RenderThread去绘制。
继续查看ThreadedRenderer.draw函数。这个是属于硬件加速的东西。
最终是构建一个display list。
绘制一个display list会调用Surface的debuffer申请内存,然后内存在应用和surfaceflinger共享。

参考,https://blog.csdn.net/luoshengyang/article/details/46281499
https://www.jianshu.com/p/ccd5da85cf9e

光栅化是什么?

屏幕是一个个光栅组成的。画图就是确定哪些光栅要填充内容。这个有gpu完成。

VSYNC虚拟化是什么?

参考https://windrunnerlihuan.com/2017/05/25/Android-SurfaceFlinger-%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E4%BA%94-VSync-%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/

app如何申请vsync,流程是怎样的?

ViewRootImpl.java
mChoreographer = Choreographer.getInstance();

mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

Choreographer.java
Choreographer.scheduleVsyncLocked

mDisplayEventReceiver.scheduleVsync

DisplayEventDispatcher.cpp
 mReceiver.requestNextVsync

DisplayEventReceiver.cpp
requestNextVsync()
mEventConnection->requestNextVsync();

EventThread.cpp
EventThread::Connection::requestNextVsync
mFlinger.resyncWithRateLimit();
enableVSyncLocked
 mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
 mVSyncSource->setVSyncEnabled(true);

SurfaceFlinger.cpp
mDispSync->addEventListener
DispSyncSource.onDispSyncEvent 被回调

vsync的相位差是如何计算的?

参考:https://www.jianshu.com/p/d3e4b1805c92

addView会触发一次绘制吗?

会的,addView会触发。

frameworks\base\core\java\android\app\ActivityThread.java#handleResumeActivity()

frameworks\base\core\java\android\view\WindowManagerImpl.java#addView()

frameworks\base\core\java\android\view\ViewRoot#setView()

frameworks\base\core\java\android\view\ViewRoot#requestLayout()

frameworks\base\core\java\android\view\ViewRoot#scheduleTraversals()

参考:https://blog.csdn.net/user11223344abc/article/details/81168087

Vsync与动画的关系

需要绘制完一帧之后,再次申请vsync!!
参考:https://blog.csdn.net/jiangnan2222/article/details/82619796

多重缓冲技术

多重缓冲是什么技术呢?我们先来说双重缓冲。在Linux上,通常使用FrameBuffer来做显示输出。双重缓冲会创建一个FrontBuffer和一个BackBuffer,顾名思义,FrontBuffer是当前显示的页面,BackBuffer是下一个要显示的画面。
参考:https://www.cnblogs.com/frrj/archive/2018/07/30/brief-info-of-android-display.html
详细请看源码
Layer.cpp

Fence机制是什么?

Fence是一种同步机制,在[Android](javascript:void())里主要用于图形系统中GraphicBuffer的同步。那它和已有同步机制相比有什么特点呢?它主要被用来处理跨硬件的情况,尤其是CPU,GPU和HWC之间的同步,另外它还可以用于多个时间点之间的同步。
参考:http://www.voidcn.com/article/p-beszdqzb-u.html

可以用system trace来描述整个绘制过程吗?

首先VSYNC-app跳变,app开始绘制。


VSYNC-app跳变

Choreographer#doFrame 开始绘制


image.png

分别处理input ,animation ,traversal事件,分别对应Choreographer.doCallbacks不同类型,这份trace的traversal事件最耗时,draw对应ViewRootImpl.performDraw(),Record View#draw()对应ThreadedRenderer.updateRootDisplayList,更新根的DisplayList。


input,animate,travel

ThreadedRenderer.updateRootDisplayList会唤醒沉睡的RenderThread。ThreadedRenderer.nSyncAndDrawFrame 会调用native方法,通过RenderProxy跟RenderThread打交道。
创建一个DrawFrameTask,丢到RenderThread的任务列表中。

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);
}

RenderThread

SurfaceFlinger在vsync消息来时会处理三个主要的步骤handleMessageTransaction,handleMessageInvalidate,handleMessageRefresh


image.png
void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            bool frameMissed = !mHadClientComposition &&
                    mPreviousPresentFence != Fence::NO_FENCE &&
                    (mPreviousPresentFence->getSignalTime() ==
                            Fence::SIGNAL_TIME_PENDING);
            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
            if (mPropagateBackpressure && frameMissed) {
                signalLayerUpdate();
                break;
            }

            // Now that we're going to make it to the handleMessageTransaction()
            // call below it's safe to call updateVrFlinger(), which will
            // potentially trigger a display handoff.
            updateVrFlinger();

            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;
        }
    }
}

SurfaceControl的show方法是如何和应用启动显示的Surface相关联的?

其实是通过动画的方式,会调用到show方法。

"android.display@6431" prio=5 runnable
  java.lang.Thread.State: RUNNABLE
      at android.view.SurfaceControl.show(SurfaceControl.java:387)
      at com.android.server.wm.WindowStateAnimator.showSurfaceRobustlyLocked(WindowStateAnimator.java:1784)
      at com.android.server.wm.WindowStateAnimator.prepareSurfaceLocked(WindowStateAnimator.java:1522)
      at com.android.server.wm.WindowAnimator.animateLocked(WindowAnimator.java:684)
      at com.android.server.wm.WindowAnimator.-wrap0(WindowAnimator.java:-1)
      at com.android.server.wm.WindowAnimator$1.doFrame(WindowAnimator.java:123)
      - locked <0x19e0> (a java.util.HashMap)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:856)
      at android.view.Choreographer.doCallbacks(Choreographer.java:670)
      at android.view.Choreographer.doFrame(Choreographer.java:603)

WMS是通过什么来操作Surface的呢?

WMS中每一个窗口对应一个windowstate,每个windowstate有一个windowstateanimator,里面有surfacecontrol,来操作surface。

LayerStack是什么?Layer的Z轴如何确定??

Layer放置在LayerStack。
Z轴通过SurfaceFlinger的变量layersSortedByZ来确定。
参考https://blog.csdn.net/scpcaicai/article/details/51338411

SurfaceControl的show是做了什么操作?

实际是需要搭配openSurfaceTransaction和closeSurfaceTransaction,这个会进行一个binder通信,通知到SurfaceFlinger。

       mService.openSurfaceTransaction();
        try {
            mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
            mSurfaceController.setLayerStackInTransaction(getLayerStack());
            mSurfaceController.setLayer(mAnimLayer);
        } finally {
            mService.closeSurfaceTransaction();
        }

流程大概是:
SurfaceControl::setFlags -》SurfaceComposerClient::setFlags -》Composer:setFlags
注意composer是单例的。
最终设置了一个mComposerStates状态,binder通信把这个状态传到SurfaceFlinger。

void Composer::closeGlobalTransactionImpl(bool synchronous) {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
…….
        transaction = mComposerStates;
        mComposerStates.clear();

…...
   sm->setTransactionState(transaction, displayTransaction, flags);
}

这个过程会申请一次vsync吗??看看会不会调用requestNextVsync

QueueBuffer后会请求一次Vsync吗?

会。
流程如下
BufferQueueProducer::queueBuffer

IConsumerListener.h
frameAvailableListener->onFrameAvailable(item)

Layer::onFrameAvailable
SurfaceFlinger::signalLayerUpdate
MessageQueue::invalidate
DisplayEventConnection::requestNextVsync

QueueBuffer是怎么调用的?

软件绘制:
ViewRootImpl.drawSoftware方法会lockcanvas和unlockcanvasandpost,分别对应dequeuebuffer和queuebuffer

硬件加速:
硬件渲染者的实例是 ThreadedRenderer
HardwareRenderer.java
static HardwareRenderer create(Context context, boolean translucent) {
HardwareRenderer renderer = null;
if (DisplayListCanvas.isAvailable()) {
renderer = new ThreadedRenderer(context, translucent);
}
return renderer;
}
在ViewRootImpl的draw方法中调用 ThreadedRenderer的draw方法进行硬件绘制。下面是调用顺序。
mAttachInfo.mHardwareRenderer.draw
android_view_ThreadedRenderer.nSyncAndDrawFrame
RenderProxy.syncAndDrawFrame
DrawFrameTask.drawFrame
CanvasContext::draw
EglManager.eglSwapBuffers
egl_window_surface_v2_t::swapBuffers
nativeWindow->queueBuffer

默认构造函数的对象实例方法

参考http://www.cnblogs.com/gklovexixi/p/5814626.html

硬件加速的环境CanvasContext是怎么创建的?

在RenderProxy的构造函数中:

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance())
        , mContext(nullptr) {
    SETUP_TASK(createContext);
    args->translucent = translucent;
    args->rootRenderNode = rootRenderNode;
    args->thread = &mRenderThread;
    args->contextFactory = contextFactory;
    mContext = (CanvasContext*) postAndWait(task);  //这里赋值
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}

SETUP_TASK是什么东西呢?
看看下面的宏定义

#define ARGS(method) method ## Args

#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,)
#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,)
#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,)
#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,)
#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_assert(std::is_trivially_destructible<ARGS(name)>::value, \
            "Error, ARGS must be trivially destructible!"); \
    static void* Bridge_ ## name(ARGS(name)* args)

#define SETUP_TASK(method) \
    LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
        "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
                METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(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 CanvasContext::create(*args->thread, args->translucent,
            args->rootRenderNode, args->contextFactory);
}

将CREATE_BRIDGE4翻译过来就是

第一步:
CREATE_BRIDGE(createContext, thread,translucent,rootRenderNode,contextFactory,,,,)
第二步
    typedef struct { \
        createContext, thread,translucent,rootRenderNode,contextFactory,,,,
    } createContextArgs; \
    static void* Bridge_createContext(createContextArgs* args)

将SETUP_TASK翻译过来就是,##是连接的意思

MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_createContext);
createContextArgs *args = (createContextArgs *) task->payload()

task->payload返回args,新建了一个task,然后在构造函数赋值args,运行这个task。

Layer的名字是从哪里来的?

在relayoutwindow的时候会创建一个WindowSurfaceController,Title就是layer的名字了。
mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags, this, windowType, ownerUid);

Layer的Layer::doTransaction做了什么??

参考http://www.360doc.com/content/14/0329/22/10366845_364800019.shtml

多少个周期才显示画面

https://blog.csdn.net/prike/article/details/52175191

绘制的Pipline是什么?

参考:https://segmentfault.com/a/1190000017099186?utm_source=tag-newest
指使用的绘制api,包括skiagl,opengl等

RenderPipelineType Properties::getRenderPipelineType() {
    if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
        return sRenderPipelineType;
    }
    char prop[PROPERTY_VALUE_MAX];
    property_get(PROPERTY_RENDERER, prop, "skiagl");
    if (!strcmp(prop, "skiagl")) {
        ALOGD("Skia GL Pipeline");
        sRenderPipelineType = RenderPipelineType::SkiaGL;
    } else if (!strcmp(prop, "skiavk")) {
        ALOGD("Skia Vulkan Pipeline");
        sRenderPipelineType = RenderPipelineType::SkiaVulkan;
    } else {  //"opengl"
        ALOGD("HWUI GL Pipeline");
        sRenderPipelineType = RenderPipelineType::OpenGL;
    }

    #ifdef VENDOR_EDIT
    //Xiaori.Yuan@MM.Display.Service.Feature, 2018/12/17, Add for douyin power
    String8 processName("");
    getProcessName(getpid(), processName);
    if(needChangeToOpenGL(processName)){
        ALOGD("%s: HWUI GL Pipeline",processName.string());
        sRenderPipelineType = RenderPipelineType::OpenGL;
    }
    #endif /* VENDOR_EDIT */

    return sRenderPipelineType;
}

如何导出一个应用的绘制信息?

dumpsys gfxinfo com.opera.browser
Applications Graphics Acceleration Info:
Uptime: 11571274 Realtime: 18973286

** Graphics info for pid 14773 [com.opera.browser] **

Stats since: 11541453130610ns
Total frames rendered: 536
Janky frames: 85 (15.86%)
50th percentile: 5ms
90th percentile: 23ms
95th percentile: 61ms
99th percentile: 150ms
Number Missed Vsync: 33
Number High input latency: 280
Number Slow UI thread: 28
Number Slow bitmap uploads: 1
Number Slow issue draw commands: 11
Number Frame deadline missed: 43
HISTOGRAM: 5ms=328 6ms=15 7ms=11 8ms=13 9ms=12 10ms=15 11ms=9 12ms=11 13ms=10 14ms=10 15ms=11 16ms=11 17ms=5 18ms=5 19ms=8 20ms=3 21ms=4 22ms=1 23ms=4 24ms=0 25ms=1 26ms=1 27ms=0 28ms=0 29ms=1 30ms=1 31ms=1 32ms=3 34ms=1 36ms=2 38ms=3 40ms=0 42ms=3 44ms=1 46ms=0 48ms=1 53ms=2 57ms=2 61ms=3 65ms=2 69ms=1 73ms=0 77ms=1 81ms=2 85ms=2 89ms=0 93ms=0 97ms=0 101ms=1 105ms=0 109ms=0 113ms=0 117ms=1 121ms=0 125ms=1 129ms=0 133ms=1 150ms=8 200ms=1 250ms=1 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=1 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=1 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
Font Cache (CPU):
  Size: 504.68 kB
  Glyph Count: 43
CPU Caches:
GPU Caches:
  Other:
    Buffer Object: 99.00 KB (3 entries)
  Image:
    Surface: 2.21 MB (31 entries)
  Scratch:
    Buffer Object: 64.00 KB (2 entries)
    Surface: 71.11 MB (39 entries)
Other Caches:
                         Current / Maximum
  VectorDrawableAtlas    0.00 kB /   0.00 KB (entries = 0)
  Layers Total           0.00 KB (numLayers = 0)
Total GPU memory usage:
  77053016 bytes, 73.48 MB (23.22 MB is purgeable)


Pipeline=Skia (Vulkan)

Layout Cache Info:
  Usage: 413/5000 entries
  Hit ratio: 5161/5574 (0.925906)
Profile data in ms:

        com.opera.browser/com.opera.android.BrowserActivity/android.view.ViewRootImpl@40f3c9e (visibility=0)
View hierarchy:

  com.opera.browser/com.opera.android.BrowserActivity/android.view.ViewRootImpl@40f3c9e
  224 views, 201.06 kB of display lists


Total ViewRootImpl: 1
Total Views:        224
Total DisplayList:  201.06 kB

android如何调用skia的api的??

绘制是通过类SKCanvas.cpp来实现的。
生成的库是libskia

view的构造函数会新建一个RenderNode,RenderNode会新建一个DisplaylistCanvas,Canvas的绘制就是要用到skia的接口啊!!

参考:https://blog.csdn.net/jxt1234and2010/article/details/42572559
https://blog.csdn.net/wind_hzx/article/details/20307093
https://blog.csdn.net/kc58236582/article/details/52879698

如何将displaylist转换为opengl的命令?

android o之前通过方法:
DeferredDisplayList::flush
android O之后参考
https://www.jianshu.com/p/abfaea892611

绘制的指令的记录在哪里的?

绘制的指令记录在RecordingCanvas中,这个东西是在创建DisplayListCanvas的过程中创建的。
具体是android_view_DisplayListCanvas.cpp的android_view_DisplayListCanvas_createDisplayListCanvas函数中。
CanvasState用来记录绘制的状态。
CanvasState的变量mSnapshot 表示当前的快照,用来记录当前绘制的坐标系


image.png

参考 https://blog.csdn.net/jinzhuojun/article/details/54234354

绘制指令保存的chunk是什么东东?

chunk里面保存着绘制的指令,按照子view来区分,通过插入栅栏来隔开不同的chunk。
参考:https://www.kancloud.cn/alex_wsc/androids/473792

image.png

一个页面绘制的起点是什么?

是Root Render Node,这个相当与一个树的起点,从这个起点可以变量整棵树,进而绘制了一个完整的画面。
Root Render Node在ThreadRender的构造函数中创建。对于一个Actvity,这个Root RenderNode就是decord view啦。

从RootRenderNode的角度看,绘制流程是怎样的?

流程如下:

ViewRootImpl.java
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);

ThreadedRenderer.java
draw()
 updateRootDisplayList()
canvas.drawRenderNode(view.updateDisplayListIfDirty());

EGLSurface和Surface有什么关系?

Surface是android端的东西,EGLSurface是Egl系统的一个页面。

对应EGLSurface,有EGLContext,代表EGL绘制的上下文。

这个上下文在RenderProxy的构造函数中创建

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance())
        , mContext(nullptr) {
    SETUP_TASK(createContext);
    args->translucent = translucent;
    args->rootRenderNode = rootRenderNode;
    args->thread = &mRenderThread;
    args->contextFactory = contextFactory;
    mContext = (CanvasContext*) postAndWait(task);
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}

可以看到这个上下文指定了渲染的线程RenderThread和渲染的根结点RootRenderNode。
并且在他的init函数会绑定android的Surface

CREATE_BRIDGE2(initialize, CanvasContext* context, Surface* surface) {
    args->context->initialize(args->surface);
    return nullptr;
}

参考https://blog.csdn.net/happy19850920/article/details/50773875
https://blog.csdn.net/luoshengyang/article/details/45769759

https://blog.csdn.net/c_z_w/article/details/86527847

GlSurfaceView的流程是怎样的?

参考frameworks/native/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java

可以将GlsurfaceView直接用过setContentview来设置进去。
GlSurfaceView里面只需要设置一个Render渲染器就可以。

class ClearGLSurfaceView extends GLSurfaceView {
    public ClearGLSurfaceView(Context context) {
        super(context);
        mRenderer = new ClearRenderer();
        setRenderer(mRenderer);
    }

    ClearRenderer mRenderer;
}

而所有的绘制的回调函数都在Render中实现。例如

   public interface Renderer {
        void onSurfaceCreated(GL10 gl, EGLConfig config);

        void onSurfaceChanged(GL10 gl, int width, int height);

        void onDrawFrame(GL10 gl);
    }

那如何触发开始渲染的呢?
我们知道Surface是独立渲染的,渲染的入口就是setRenderer方法。这个方法会启动一个GlThread渲染线程开始渲染。
渲染的流程:

1.获取显示设备
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

2.选择config
mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);

3.创建EglContext
mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);

4.创建EglSurface
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
mEglDisplay, mEglConfig, view.getHolder());

5.设置渲染环境到当前线程
mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)

6.开始渲染

TextureView

在UI重绘函数performTranversals()中,作为View hierachy的一分子,TextureView的draw()函数被调用,其中便会相继调用applyUpdate()和HardwareLayer的updateSurfaceTexture()函数

参考https://blog.csdn.net/m475664483/article/details/52998445
https://www.jianshu.com/p/a14955e52216
https://www.jianshu.com/p/4e2916889f27

Android View系統的位置信息是如何的?

protected int mLeft;   view左边界距离父View左边的距离
protected int mRight; view右边界距离父View左边的距离
protected int mTop;view上边界距离父View上边的距离
protected int mBottom;view下边界距离父View上边的距离
protected int mScrollX;
protected int mScrollY;

见下图,参考https://blog.csdn.net/hehe26/article/details/53286027

image.png

位置相关函数: 参考:https://blog.csdn.net/cadi2011/article/details/71440491
getLocationInWindow

一个控件在其父窗口中的坐标位置,源码如下,原理就是不断地获取父view的mLeft,然后叠加在一起,就是最后一个控件在其父窗口中的左坐标位置。右左标同理。

    public void getLocationInWindow(@Size(2) int[] outLocation) {
        if (outLocation == null || outLocation.length < 2) {
            throw new IllegalArgumentException("outLocation must be an array of two integers");
        }

        outLocation[0] = 0;
        outLocation[1] = 0;

        transformFromViewToWindowSpace(outLocation); 
    }

    /** @hide */
    public void transformFromViewToWindowSpace(@Size(2) int[] inOutLocation) {
        if (inOutLocation == null || inOutLocation.length < 2) {
            throw new IllegalArgumentException("inOutLocation must be an array of two integers");
        }

        if (mAttachInfo == null) {
            // When the view is not attached to a window, this method does not make sense
            inOutLocation[0] = inOutLocation[1] = 0;
            return;
        }

        float position[] = mAttachInfo.mTmpTransformLocation;
        position[0] = inOutLocation[0];
        position[1] = inOutLocation[1];

        if (!hasIdentityMatrix()) {
            getMatrix().mapPoints(position);
        }

        position[0] += mLeft;
        position[1] += mTop;

        ViewParent viewParent = mParent;
        while (viewParent instanceof View) {   //关键点,遍历父view
            final View view = (View) viewParent;

            position[0] -= view.mScrollX;
            position[1] -= view.mScrollY;

            if (!view.hasIdentityMatrix()) {
                view.getMatrix().mapPoints(position);
            }

            position[0] += view.mLeft;
            position[1] += view.mTop;

            viewParent = view.mParent;
         }

        if (viewParent instanceof ViewRootImpl) {
            // *cough*
            final ViewRootImpl vr = (ViewRootImpl) viewParent;
            position[1] -= vr.mCurScrollY;
        }

        inOutLocation[0] = Math.round(position[0]);
        inOutLocation[1] = Math.round(position[1]);
    }

getLocationOnScreen

一个控件在其整个屏幕上的坐标位置,源码如下,意思是靠mAttachInfo来获取

    public int[] getLocationOnScreen() {
        int[] location = new int[2];
        getLocationOnScreen(location);
        return location;
    }

    /**
     * <p>Computes the coordinates of this view on the screen. The argument
     * must be an array of two integers. After the method returns, the array
     * contains the x and y location in that order.</p>
     *
     * @param outLocation an array of two integers in which to hold the coordinates
     */
    public void getLocationOnScreen(@Size(2) int[] outLocation) {
        getLocationInWindow(outLocation);

        final AttachInfo info = mAttachInfo;
        if (info != null) {
            outLocation[0] += info.mWindowLeft;
            outLocation[1] += info.mWindowTop;
        }
    }

getLocationInWindow是以B为原点的C的坐标,而getLocationOnScreen以A为原点。


image.png

window与view的区别

一个Activity就是一个window,这里window里面装着decordview,decordview是根view。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容