zoukankan      html  css  js  c++  java
  • Android之Surface绘制原理

    一、Surface的Buffer是从哪里来的?

      源码:frameworks/base/core/java/android/view/ViewRootImpl.java

      View触发绘制是通过requestLayout()函数或者setLayoutParms()函数:

       performTravsersals()函数实现:

    private void performTraversals() {
        ……
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        performLayout(lp, mWidth, mHeight);
        performDraw();
        ……
    }

       perfomrDraw()函数调用draw()函数开始绘制:

    private void performDraw() {
        ……
        boolean canUseAsync = draw(fullRedrawNeeded);
        ……
    }

      ViewRootImpl.draw()函数实现:

    private boolean draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        ……
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) {
            return false;
        }
        ……
        return useAsyncReport;
    }

      drawSoftware()软件绘制,默认是软件绘制。

      drawSoftware()函数软件绘制流程:

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    
        // Draw with software renderer.
        final Canvas canvas;
        ……
        canvas = mSurface.lockCanvas(dirty);
        ……
        mView.draw(canvas);
        ……
        surface.unlockCanvasAndPost(canvas);
    
    }

      获取:通过lockCanvas函数获取Canvas对象,

      绘制:再通过mView.draw(canvas)函数向在canvas上绘制,

      提交:最后通过surface.unlockCanvasAndPost(canvas)函数提交Canvas。

      通过lockCanvas()函数获取Canvas对象,lockCanvas()函数如何获取Canvas对象。

      lockCanvas()函数实现:

    /**
     * Gets a {@link Canvas} for drawing into this surface.
     *
     * After drawing into the provided {@link Canvas}, the caller must
     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
     *
     * @param inOutDirty A rectangle that represents the dirty region that the caller wants
     * to redraw.  This function may choose to expand the dirty rectangle if for example
     * the surface has been resized or if the previous contents of the surface were
     * not available.  The caller must redraw the entire dirty region as represented
     * by the contents of the inOutDirty rectangle upon return from this function.
     * The caller may also pass <code>null</code> instead, in the case where the
     * entire surface should be redrawn.
     * @return A canvas for drawing into the surface.
     *
     * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
     * @throws OutOfResourcesException If the canvas cannot be locked.
     */
    public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mLockedObject != 0) {
                // Ideally, nativeLockCanvas() would throw in this situation and prevent the
                // double-lock, but that won't happen if mNativeObject was updated.  We can't
                // abandon the old mLockedObject because it might still be in use, so instead
                // we just refuse to re-lock the Surface.
                throw new IllegalArgumentException("Surface was already locked");
            }
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

      通过Native层android_view_Surface.cpp的nativeLockCanvas(mNativeObject, mCanvas, inOutDirty)函数获取,mNativeOjbect参数是Java层的Surface在Native层对应的Surface对象的指针。mCanvas是Surface的变量,在lockCanvas()函数调用时mCanvas是空的。

      在调用nativeLockCanvas()函数后mCanvas就有值了,最后返回mCanvas对象。

    static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
            jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
        // (1)
        sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
        ……
        // (2)
        ANativeWindow_Buffer buffer;
        status_t err = surface->lock(&buffer, dirtyRectPtr);
        ……
        // (3)
        graphics::Canvas canvas(env, canvasObj);
        canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
        ……
        // (4)
        // 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();
    }

      (1) 获取Native层的Surface对象。

      (2) 获取Native层的Surface对象的Buffer。

      (3) 将Buffer设置给Canvas,这里Canvas就有一个Buffer了。在每次都申请一个新的Buffer给Canvas对象。

      (4) 向Java层返回Native的Surface对象,这里返回的是一个Long型数据,这个Long型数据是Surface指针。

      获取Buffer实现,surface -> lock(&buffer, ),这里传入Buffer地址:

      源码:frameworks/native/libs/gui/Surface.cpp

    status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
    {
        ……
        // (1)
        ANativeWindowBuffer* out;
        status_t err = dequeueBuffer(&out, &fenceFd);
        ……
        // (2)
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        ……
        // (3)
        void* vaddr;
        status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd);
        ……
        // (4)
        mLockedBuffer = backBuffer;
        // (5)
        outBuffer->bits   = vaddr;
        ……
    }

      (1) 获取dequeueBuffer()函数在SurfaceFlinger的Buffer队列中获取Buffer。

      (2) 创建GraphicBuffer对象backBuffer。在SharedBufferStack中有双缓冲机制,分别为FontBuffer和BackBuffer。

      • FontBuffer:代表当前将显示在屏幕的Buffer数据。属于前台Buffer。
      • BackBuffer:代表绘制的Buffer数据,是准备渲染的数据Buffer。属于后台Buffer。

      (3) 锁定Buffer,并将Buffer地址返回,将返回的Buffer地址给Canvas的Buffer。

      (4) 切换Buffer,将后台BackBuffer切换到前台,交给mLockedBuffer。FontBuffer的变量就是mLockedBuffer。

      (5) 将vaddr赋值给outBuffer->bits,bits最后赋值给Canvas的Buffer,就是BkBitmap,作为Canvas的缓冲区。

      dequeteBuffer()是如何获取Buffer的,dequeteBuffer()函数实现:

    int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
        ……
        int buf = -1;
        // (1)
        status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
                                                                reqFormat, reqUsage, &mBufferAge,
                                                                enableFrameTimestamps ? &frameTimestamps
                                                                                      : nullptr);
        ……
        // (2)
        sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
        ……
        // (3)
        if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
            if (mReportRemovedBuffers && (gbuf != nullptr)) {
                mRemovedBuffers.push_back(gbuf);
            }
            result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
            if (result != NO_ERROR) {
                ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
                mGraphicBufferProducer->cancelBuffer(buf, fence);
                return result;
            }
        }
        ……
        // (4)
        *buffer = gbuf.get();
    }

      (1) 通过mGaphicBufferProducer->dequeteBuffer()函数在远端的Buffer slots中获得一个空闲的Buffer,返回远端Buffer地址指针。

      (2) 通过gbp从本地Buffer Slots里获取Buffer,在(1)中从远端,在(2)中从本地,这里涉及远端Buffer queue与本地Buffer queue同步问题。

      (3) 负责本地Buffer与远端Buffer同步,远端返回的Buffer的result是BUFFER_NEEDS_REALLOCATION或者本地的gbp是null,通过gbp的requestBuffer()获取新的远端Buffer指针地址。mGaphicBufferProducer->requestBuffer()函数。

      (4) 获取Buffer。

    二、Surface的Buffer是如何提交的?

       通过surface.unlockCanvasAndPost(canvas)向远端提交更新的Buffer,unlockCanvasAndPost()函数实现:

    /**
     * Posts the new contents of the {@link Canvas} to the surface and
     * releases the {@link Canvas}.
     *
     * @param canvas The canvas previously obtained from {@link #lockCanvas}.
     */
    public void unlockCanvasAndPost(Canvas canvas) {
        synchronized (mLock) {
            unlockSwCanvasAndPost(canvas);
        }
    }
    
    private void unlockSwCanvasAndPost(Canvas canvas) {
        try {
            nativeUnlockCanvasAndPost(mLockedObject, canvas);
        } finally {
            nativeRelease(mLockedObject);
            mLockedObject = 0;
        }
    }

      最后调用到Native层的nativeUnlockCanvasAndPost(mLockedObject, canvas)。

      Native层,nativeUnlockCanvasAndPost()函数实现:

    static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
            jlong nativeObject, jobject canvasObj) {
        // (1)
        sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    
        // (2)
        // detach the canvas from the surface
        graphics::Canvas canvas(env, canvasObj);
        canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
    
        // (3)
        // unlock surface
        status_t err = surface->unlockAndPost();
    }

      (1) 获取对应Java层的Native层的Surface对象。

      (2) 获取对应Java层的Native层的Canvas对象。

      (3) 将本地Buffer更新到远端的Buffer queue中。

      Native层更新远端Buffer queue,surface->unlockAndPost()函数实现:

      源码:frameworks/native/libs/gui/Surface.cpp

    status_t Surface::unlockAndPost()
    {
        if (mLockedBuffer == nullptr) {
            ALOGE("Surface::unlockAndPost failed, no locked buffer");
            return INVALID_OPERATION;
        }
    
        int fd = -1;
        status_t err = mLockedBuffer->unlockAsync(&fd);
        ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
    
        err = queueBuffer(mLockedBuffer.get(), fd);
        ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
                mLockedBuffer->handle, strerror(-err));
    
        mPostedBuffer = mLockedBuffer;
        mLockedBuffer = nullptr;
        return err;
    }

       通过函数queueBuffer(mLockedBuffer.get(), )函数实现更新:

    int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
        ……
        // (1)
        int i = getSlotFromBufferLocked(buffer);
        ……
        // (2)
        status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
        ……
        return err;
    }

      (1) 获取Buffer的index。

      (2) 通过mGraphicBufferProducer->queueBuffer(i, )函数,将本地的Buffer同步到远端Buffer queue中。

     三、总结

  • 相关阅读:
    kuryr环境搭建
    使用docker搭建kafka环境
    记一次解决curl https证书问题
    一篇精彩的洗地文
    个人知识管理利器wiz
    Markdown写作
    用Bottle开发web程序(二)
    用Bottle开发web程序(一)
    Magnum Kubernetes源码分析(二)
    DVWA(九):File Upload 全等级文件上传
  • 原文地址:https://www.cnblogs.com/naray/p/15363076.html
Copyright © 2011-2022 走看看