Android 图形架构 之六——深入分析draw()是如何工作的

Posted 薛瑄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 图形架构 之六——深入分析draw()是如何工作的相关的知识,希望对你有一定的参考价值。

前言

前面我们分析了 app进程中的 addView 的执行过程,其中涉及到了ViewRootImpl,WMS,Session,WindowState,Surface 等等,最后分析到performDraw() 函数,接下来,我们就继续来分析,带着两个问题,它是如何把数据写入生产者队列的,已经SurfaceFlinger 何时开始处理的?

Android 图形架构 之一 ——概述
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类
Android 图形架构 之五——深入分析addView所发生的的一切
Android 图形架构 之六——深入分析draw()是如何工作的
Android 图形架构 之七——Choreographer 源码分析
android图形架构 之八——硬件VSync、VSync-app、Vsync-sf

流程分析

本文要分析的主流程,如下图所示:

接下来就从performDraw 开始分析:

文件路径:frameworks/base/core/java/android/view/ViewRootImpl.java

代码一:
   private void performDraw() 
       ......
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try 
            draw(fullRedrawNeeded);
         finally 
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        
        ......
    

    private void draw(boolean fullRedrawNeeded) 

     .....
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) 
           return;
        
     ......
    
代码二:
    /**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) 

        // Draw with software renderer.

          .....
          //申请的图形缓冲区,并锁定,用于在上面写数据,代码三分析
         canvas = mSurface.lockCanvas(dirty);
         .....
         //调用系统的api(这里指Canvas 类的 ),向缓冲区写入数据,这里也会调用我们熟悉的onDraw 函数
         mView.draw(canvas);
         .....
         //解锁缓冲区,入队,并通知SurfaceFlinger 去消费,下面会分析
         surface.unlockCanvasAndPost(canvas);
         .....

        return true;
    

lockCanvas

来看看mSurface.lockCanvas(dirty);

位于 frameworks/base/core/java/android/view/Surface.java:

代码三:
    public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException 
        synchronized (mLock) 
            checkNotReleasedLocked();

            //调用Native代码
            //mCanvas 里面有SkBitmap,SkBitmap里面的数据 指向了缓冲区队列中 某一个BufferSlot 对应的 GraphicBuffer
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        
    

位于 frameworks/base/core/jni/android_view_Surface.cpp:

代码四:
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) 
     //获取java层的Surface保存的long型句柄
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

    ......

    Rect dirtyRect(Rect::EMPTY_RECT);
    Rect* dirtyRectPtr = NULL;
    //获取java层dirty Rect的位置大小信息
    if (dirtyRectObj) 
        dirtyRect.left   = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
        dirtyRect.top    = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
        dirtyRect.right  = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
        dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
        dirtyRectPtr = &dirtyRect;
    

    ANativeWindow_Buffer outBuffer;
    //调用Surface的lock方法,获取一个缓冲区,并赋值给outBuffer
    //代码五分析
    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
    ......
    //创建一个SkImageInfo ,用于下面创建SkBitmap 
    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                         convertPixelFormat(outBuffer.format),
                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
   // 使用outBuffer为SkBitmap变量赋值
    SkBitmap bitmap;
    //获取图形缓冲区每一行像素大小
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setInfo(info, bpr);
    if (outBuffer.width > 0 && outBuffer.height > 0) 
        //把outBuffer的数据,设置到bitmap中
        bitmap.setPixels(outBuffer.bits);
     else 
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    

    
    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    //把刚才创建的SkBitmap ,设置到Canvas 中,后续 就可以在这个bitmap上绘图了
    nativeCanvas->setBitmap(bitmap);

    if (dirtyRectPtr) 
       //剪裁出dirty区域大小
        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
    

    if (dirtyRectObj) 
        //将剪裁位置大小信息赋给java层Canvas对象
        env->SetIntField(dirtyRectObj, gRectClassInfo.left,   dirtyRect.left);
        env->SetIntField(dirtyRectObj, gRectClassInfo.top,    dirtyRect.top);
        env->SetIntField(dirtyRectObj, gRectClassInfo.right,  dirtyRect.right);
        env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
    

    // Create another reference to the surface and return it.  This reference
    // should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,
    // because the latter could be replaced while the surface is locked.
    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    return (jlong) lockedSurface.get();

接下来分析Surface的lock函数,它的功能是申请图形缓冲区,并锁定这块缓冲区,后面就可调用draw函数,来绘制了
函数位于frameworks/native/libs/gui/Surface.cpp:

代码五:
status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)

    if (mLockedBuffer != nullptr) 
        ALOGE("Surface::lock failed, already locked");
        return INVALID_OPERATION;
    

    if (!mConnectedToCpu) 
        // 调用connect函数, 连接cpu
        int err = Surface::connect(NATIVE_WINDOW_API_CPU);
        if (err) 
            return err;
        
        // we're intending to do software rendering from this point
        setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
    

    ANativeWindowBuffer* out;
    int fenceFd = -1;
    //调用dequeueBuffer函数,申请图形缓冲区,代码六会分析
    status_t err = dequeueBuffer(&out, &fenceFd);
    ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
    if (err == NO_ERROR) 
        //获取图形缓冲区区域大小,赋给后备缓冲区变量backBuffer
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        //该缓冲区的宽高,赋值给bounds
        const Rect bounds(backBuffer->width, backBuffer->height);

        Region newDirtyRegion;
        //是否在脏区域边界,newDirtyRegion表示新的脏区域
        if (inOutDirtyBounds) 
            //设置新的脏区域 
            newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
            //脏区域和 缓冲区区域 作 与运算,求交集
            newDirtyRegion.andSelf(bounds);
         else 
            //不是只刷新脏区域,则待刷新区域,是整个缓冲区的宽高
            newDirtyRegion.set(bounds);
        

        // figure out if we can copy the frontbuffer back
        //上一次绘制的信息保存在mPostedBuffer中,而这个mPostedBuffer则要在unLockAndPost函数中设置
        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        //比较 本次 和 上次的缓冲区,宽、高、格式是否一致
        const bool canCopyBack = (frontBuffer != nullptr &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);

        if (canCopyBack) 
            // copy the area that is invalid and not repainted this round
            // 获取到需要更新的脏区域
            const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
            if (!copyback.isEmpty()) 
                //把frontBuffer 数据拷贝到backBuffer,后续只用更新该脏区域的数据,提高了效率
                copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
            
         else 
            // if we can't copy-back anything, modify the user's dirty
            // region to make sure they redraw the whole buffer
            //如果两次缓冲区的大小,格式不一致,则设置脏区域为当前缓冲区的大小 ,后续更新整个缓冲区
            newDirtyRegion.set(bounds);
            mDirtyRegion.clear();
            Mutex::Autolock lock(mMutex);
            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) 
                mSlots[i].dirtyRegion.clear();
            
        


         // scope for the lock
            Mutex::Autolock lock(mMutex);
            //得到此次后备缓冲器的bufferSlot在mSlots里面的index
            int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
            if (backBufferSlot >= 0) 
                //将新的dirty赋给这个bufferslot
                Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
                mDirtyRegion.subtract(dirtyRegion);
                dirtyRegion = newDirtyRegion;
            
        
		//两个脏区域,或运算,相叠加
        mDirtyRegion.orSelf(newDirtyRegion);
        if (inOutDirtyBounds) 
            *inOutDirtyBounds = newDirtyRegion.getBounds();
        

        void* vaddr;
        //lock和unlock分别用来锁定和解锁一个指定的图形缓冲区,在访问一块图形缓冲区的时候,
        //例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突,
        //锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);

        ALOGW_IF(res, "failed locking buffer (handle = %p)",
                backBuffer->handle);

        if (res != 0) 
            err = INVALID_OPERATION;
         else 
            //mLockedBuffer 表示锁定的buffer,指向当前绘制的buffer backBuffer
            mLockedBuffer = backBuffer;
            //outBuffer 是要返回的数据,使用backBuffer 为它赋值
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            //指向一块缓冲区的起始地址,这块缓冲区被锁定了,接下来就可以在上面写入数据
            outBuffer->bits   = vaddr;
        
    
    return err;

注意在Surface 中也有一个结构体BufferSlot,代码如下,它和BufferQueueCore中的BufferSlot 类型有点差别。前者是在app进程中,后者是在SurfaceFlinger进程中

    struct BufferSlot 
        sp<GraphicBuffer> buffer;
        Region dirtyRegion;
    ;

接下来 调用了Surface::dequeueBuffer

代码六:
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) 
    ......

    FrameEventHistoryDelta frameTimestamps;
    //在生产者-消费者队列,申请图形缓冲区
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
                                                            reqFormat, reqUsage, &mBufferAge,
                                                            enableFrameTimestamps ? &frameTimestamps
                                                                                  : nullptr);

    .....
    if (buf < 0 || buf >= NUM_BUFFER_SLOTS) 
        ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
        android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
        return FAILED_TRANSACTION;
    

    Mutex::Autolock lock(mMutex);

    // Write this while holding the mutex
    mLastDequeueStartTime = startTime;
	//这里的mSlots,是Surface中的BufferSlot数组,其中buffer 类型是GraphicBuffer 
    sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);

    if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) 
        static FenceMonitor hwcReleaseThread("HWC release");
        hwcReleaseThread.queueFence(fence);
    
    //如果所有状态buffer的标志位是RELEASE_ALL_BUFFERS,表示个数大于64,就要释放所有超过64的buffer
    if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) 
        freeAllBuffers();
    

    if (enableFrameTimestamps) 
         mFrameEventHistory->applyDelta(frameTimestamps);
    
    //新分配的BufferSlot的状态是BUFFER_NEEDS_REALLOCATION标志,代表需要重新分配了GraphicBuffer
    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) 
        if (mReportRemovedBuffers && (gbuf != nullptr)) 
            mRemovedBuffers.push_back(gbuf);
        
        //由于申请内存的操作是在surfaceflinger进程中,当前在app进程,所以这是一个进程间通信
        //buf 存储的是BufferSlot在mSlot 的索引,gbuf 存储是GraphicBuffer
        //把buf 对应的BufferSlot 对应的GraphicBuffer,指针赋值给gbuf
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        .....
    

    ......
    //获取这个buffer对象的指针内容
    *buffer = gbuf.get();

    if (mSharedBufferMode && mAutoRefresh) 
        mSharedBufferSlot = buf;
        mSharedBufferHasBeenQueued = false;
     else if (mSharedBufferSlot == buf) 
        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
        mSharedBufferHasBeenQueued = false;
    

    return OK;

调用BufferQueueProducer的dequeueBuffer函数申请一块图形缓冲区,这个在文章 Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类 分析过了

这里分析一下BufferQueueProducer的requestBuffer,它是通过IPC Binder调用远程接口,(Binder 通信的过程就不分析了)直接看实现,位于frameworks/native/libs/gui/BufferQueueProducer.cpp:

在SurfaceFlinger 创建Layer 的时候,调用createBufferQueue,进而创建了BufferQueueProducer,这些操作是在SurfaceFlinger 进程中的 ,可阅读Android 图形架构 之三—— 创建Layer、Surface、SurfaceControlAndroid 图形架构 之四——图形缓冲区的申请和消费流程及核心类 来了解这些

该函数 是根据指定index取出mSlots中的slot中的buffer。

代码七:
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) 
    ATRACE_CALL();
    BQ_LOGV("requestBuffer: slot %d", slot);
    Mutex::Autolock lock(mCore->mMutex);

    if (mCore->mIsAbandoned) 
        BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
        return NO_INIT;
    
    //check index and state
    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) 
        BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
                slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
        return BAD_VALUE;
     else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) 
        BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
                "(state = %d)", slot, mSlots[slot].mBufferState);
        return BAD_VALUE;
    
    //注意,这里已经到了SurfaceFlinger进程中,此时的BufferSlot的Graphicbuffer 数据存储在变量 mGraphicBuffer
    //将slot对应的BufferSlot  mRequestBufferCalled 标志位置为true,并返回BufferSlot的Graphicbuffer
    mSlots[slot].mRequestBufferCalled = true;
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;

draw

接着回到代码二,mView.draw(canvas); ,调用Canvas 的api 在这块缓冲区上,进行数据的绘制。此处我们就省略了,下面看看SurfaceFlinger 如何使用这块缓冲区

unlockCanvasAndPost

接下来 在代码二中,调用了 surface.unlockCanvasAndPost(canvas);,主要作用就是把缓冲区入队,通知SurfaceFlinger进行渲染
代码路径:frameworks/base/core/java/android/view/Surface.java

代码八:
    public void unlockCanvasAndPost(Canvas canvas) 
        synchronized (mLock) 
            checkNotReleasedLocked();

            if (mHwuiContext != null) 
                mHwuiContext.unlockAndPost(canvas);
             else 
                unlockSwCanvasAndPost(canvas);
            
        
    

    private void unlockSwCanvasAndPost(Canvas canvas) 
       .以上是关于Android 图形架构 之六——深入分析draw()是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章

Android 图形架构 之七——Choreographer 源码分析

Android 图形架构 之七——Choreographer 源码分析

Android 图形架构 之二—— SurfaceFlinger 启动和连接

Android 图形架构 之二—— SurfaceFlinger 启动和连接

Android 图形架构 之五—— 深入分析addView所发生的的一切

Android 图形架构 之五—— 深入分析addView所发生的的一切