AndroidQ 圖形系統(10)SurfaceView實現原理之surface創建和繪製原理

上一篇文章說了SurfaceView默認Z-order是小於主窗口的,爲了能夠顯示出來,需要以自身所佔矩形區域在主窗口設置透明區域,這是在SurfaceView的回調onAttachedToWindow中實現的,本篇接着看SurfaceView另一個回調onWindowVisibilityChanged

首先還是貼出上一篇分析的ViewRootImpl的performTraversals方法部分代碼:

private void performTraversals() {
	final View host = mView;
	......
	if (mFirst) {
		...
		host.dispatchAttachedToWindow(mAttachInfo, 0);
		...
	}
	...
	if (viewVisibilityChanged) {
		...
		host.dispatchWindowVisibilityChanged(viewVisibility);
		...
	}
	.....
	performMeasure();
	...
	performLayout();
	...
	performDraw();
	...
}

其實這裏調用host.dispatchWindowVisibilityChanged和前面host.dispatchAttachedToWindow流程都類似的,就是遍歷View樹,讓所有View都能執行到dispatchWindowVisibilityChanged,但我們一般不會重寫View的dispatchWindowVisibilityChanged方法的,而是重寫onWindowVisibilityChanged方法:
View.java

/**
     * Dispatch a window visibility change down the view hierarchy.
     * ViewGroups should override to route to their children.
     *
     * @param visibility The new visibility of the window.
     *
     * @see #onWindowVisibilityChanged(int)
     */
    public void dispatchWindowVisibilityChanged(@Visibility int visibility) {
        onWindowVisibilityChanged(visibility);
    }

所以這個遍歷流程我們就不看了,直接來到SurfaceView的具體實現中:

SurfaceView.onWindowVisibilityChanged

   @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mWindowVisibility = visibility == VISIBLE;
        updateRequestedVisibility();
        updateSurface();
    }

首先調用父類View的onWindowVisibilityChanged,接着將初始化mWindowVisibility的值,代表當前SurfaceView是否可見,接着updateRequestedVisibility更新mRequestedVisible的值,我們重點要看的是updateSurface方法,這個方法中會創建SurfaceView自己的surface

updateSurface

/** @hide */
    protected void updateSurface() {
        ...
        ViewRootImpl viewRoot = getViewRootImpl();
        ...

        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
         
        final float alpha = getFixedAlpha();
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean visibleChanged = mVisible != mRequestedVisible;
        final boolean alphaChanged = mSurfaceAlpha != alpha;
        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
                && mRequestedVisible;
        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
        boolean redrawNeeded = false;

        if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha
                && alphaChanged) || windowVisibleChanged) {
            getLocationInWindow(mLocation);

            ...

            try {
                final boolean visible = mVisible = mRequestedVisible;
                mWindowSpaceLeft = mLocation[0];
                mWindowSpaceTop = mLocation[1];
                mSurfaceWidth = myWidth;
                mSurfaceHeight = myHeight;
                mFormat = mRequestedFormat;
                mLastWindowVisibility = mWindowVisibility;

                mScreenRect.left = mWindowSpaceLeft;
                mScreenRect.top = mWindowSpaceTop;
                mScreenRect.right = mWindowSpaceLeft + getWidth();
                mScreenRect.bottom = mWindowSpaceTop + getHeight();
               
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);

                if (creating) {
                    viewRoot.createBoundsSurface(mSubLayer);
                    mSurfaceSession = new SurfaceSession();
                    mDeferredDestroySurfaceControl = mSurfaceControl;

                    updateOpaqueFlag();
                    final String name = "SurfaceView - " + viewRoot.getTitle().toString();

                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                        .setName(name)
                        .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                        .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                        .setFormat(mFormat)
                        .setParent(viewRoot.getSurfaceControl())
                        .setFlags(mSurfaceFlags)
                        .build();
                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
                        .setName("Background for -" + name)
                        .setOpaque(true)
                        .setColorLayer()
                        .setParent(mSurfaceControl)
                        .build();

                } else if (mSurfaceControl == null) {
                    return;
                }

                boolean realSizeChanged = false;

                mSurfaceLock.lock();
                try {
                    mDrawingStopped = !visible;

                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                            + "Cur surface: " + mSurface);

                    SurfaceControl.openTransaction();
                    try {
                        mSurfaceControl.setLayer(mSubLayer);

                        if (mViewVisibility) {
                            mSurfaceControl.show();
                        } else {
                            mSurfaceControl.hide();
                        }
                        updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
                        if (mUseAlpha) {
                            mSurfaceControl.setAlpha(alpha);
                            mSurfaceAlpha = alpha;
                        }

                        ...
                        if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
                                    0.0f, 0.0f,
                                    mScreenRect.height() / (float) mSurfaceHeight);
                            ...
                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                        }
                        mSurfaceControl.setCornerRadius(mCornerRadius);
                        if (sizeChanged && !creating) {
                            mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
                        }
                    } finally {
                        SurfaceControl.closeTransaction();
                    }

                    if (sizeChanged || creating) {
                        redrawNeeded = true;
                    }

                    mSurfaceFrame.left = 0;
                    mSurfaceFrame.top = 0;
                    if (translator == null) {
                        mSurfaceFrame.right = mSurfaceWidth;
                        mSurfaceFrame.bottom = mSurfaceHeight;
                    } else {
                        float appInvertedScale = translator.applicationInvertedScale;
                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
                    }

                    final int surfaceWidth = mSurfaceFrame.right;
                    final int surfaceHeight = mSurfaceFrame.bottom;
                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
                            || mLastSurfaceHeight != surfaceHeight;
                    mLastSurfaceWidth = surfaceWidth;
                    mLastSurfaceHeight = surfaceHeight;
                } finally {
                    mSurfaceLock.unlock();
                }

                try {
                    redrawNeeded |= visible && !mDrawFinished;

                    SurfaceHolder.Callback[] callbacks = null;

                    final boolean surfaceChanged = creating;
                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                            
                            if (mSurface.isValid()) {
                                mSurface.forceScopedDisconnect();
                            }
                        }
                    }

                    if (creating) {
                        mSurface.copyFrom(mSurfaceControl);
                    }

                    if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
                            < Build.VERSION_CODES.O) {
                        
                        mSurface.createFrom(mSurfaceControl);
                    }

                    if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;
                            
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }
                        if (redrawNeeded) {
                           
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }

                            mPendingReportDraws++;
                            viewRoot.drawPending();
                            SurfaceCallbackHelper sch =
                                    new SurfaceCallbackHelper(this::onDrawFinished);
                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (mSurfaceControl != null && !mSurfaceCreated) {
                        mSurface.release();
                        releaseSurfaces();
                    }
                }
            } catch (Exception ex) {
                Log.e(TAG, "Exception configuring surface", ex);
            }
           
        } else {
           
            final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
                    || mWindowSpaceTop != mLocation[1];
            final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
                    || getHeight() != mScreenRect.height();
            if (positionChanged || layoutSizeChanged) { // Only the position has changed
                mWindowSpaceLeft = mLocation[0];
                mWindowSpaceTop = mLocation[1];
                
                mLocation[0] = getWidth();
                mLocation[1] = getHeight();

                mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
                        mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);

                if (translator != null) {
                    translator.translateRectInAppWindowToScreen(mScreenRect);
                }

                if (mSurfaceControl == null) {
                    return;
                }

                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
                    try {
                        
                        setParentSpaceRectangle(mScreenRect, -1);
                    } catch (Exception ex) {
                        Log.e(TAG, "Exception configuring surface", ex);
                    }
                }
            }
        }
    }

刪了一些代碼,看着代碼挺多,其實很多代碼都是獲取SurfaceView的信息相關,寬高,透明度,座標等,還有很多是surface創建之後需要設置的信息,可以看到有很多setXXX方法,這個方法的核心邏輯還是創建SurfaceControlSurface,創建方式和APP一樣的,APP的SurfaceControlSurface是在ViewRootImpl的performTraversals方法中創建的,等下再說,先來看這個方法。

首先拿到SurfaceView寬高,可以通過外部調用setFixedSize設置,如果沒設置就拿SurfaceView測量到的,這裏提一下,如果SurfaceView的寬高測量模式爲EXACTLY,則setFixedSize不會生效,這是SurfaceView自定義的測量方法onMeasure中調用了View的resolveSizeAndState決定的,具體可以自己去看,很簡單,接着就是獲取SurfaceView一系列信息,透明度,格式是否改變,可見性是否改變等等,這些值作爲是否往下走的判斷依據。

getLocationInWindow獲取SurfaceView左上角座標,接着一系列賦值,SurfaceView座標啊,寬高,格式等,重點是SurfaceControl的創建,我在AndroidQ 圖形系統(1)Surface與SurfaceControl創建分析已經分析過APP的Surface與SurfaceControl創建過程,流程都一樣,總結一下:

  1. 首先應用進程會new一個java層SurfaceControl,什麼都沒做,然後傳遞到WMS進程,因爲SurfaceControl在AIDL中是out類型,所以在WMS進程賦值。
  2. WMS在創建java層SurfaceControl的同時通過nativeCreate方法到native層做一系列初始化。
  3. SurfaceComposerClientcreateSurfaceChecked函數中通過ISurfaceComposerClient的Bp端mClient向SurfaceFlinger進程請求創建Surface,即調用createSurface函數,而在SurfaceFlinger進程Surface對應的是Layer
  4. 在第一次創建Layer的子類BufferQueueLayer的過程中,即在BufferQueueLayer的onFirstRef函數中會創建生產者消費者模型架構。
  5. SurfaceFlinger進程的任務完成之後會直接new一個SurfaceControl,並將SurfaceFlinger進程創建的Layer引用和生產者保存到SurfaceControl中,最後將native層SurfaceControl指針保存到java層SurfaceControl。
  6. native層SurfaceControl創建好了之後就可以通過此對象創建native層的Surface對象,即調用mSurface.copyFrom(mSurfaceControl),最後將native層Surface指針保存到java層Surface,最終java層和native層的Surface和SurfaceControl都創建完畢。

SurfaceView中創建surface也是同樣的流程,大部分事情都是在native層做的,最重要的就是創建了SurfaceFlinger進程的生產者-消費者模式架構以及Layer。

SurfaceControl創建好了之後會調用很多setXXX方法,如setLayer,setAlpha,setPosition,setBufferSize等,這些信息最終都會設置到SurfaceFlinger的Layer中去,對於應用層來說其實就是SurfaceView的信息。

接着來看:

           if (creating) {
                        mSurface.copyFrom(mSurfaceControl);
                    }

通過copyFrom會到native層創建native層的Surface,並將指針保存在java層Surface中,這個方法執行完也宣告SurfaceView的Surface創建完畢。

再來看:

if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;

                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }

getSurfaceCallbacks會獲取所有添加的SurfaceHolder.Callback,遍歷他們調用surfaceCreated回調方法通知開發者SurfaceView的Surface創建完畢,我們可以拿到Surface進行繪製了。

再看下surfaceChanged回調的代碼:

if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "surfaceChanged -- format=" + mFormat
                                    + " w=" + myWidth + " h=" + myHeight);
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }

上面這幾個條件都會引起surfaceChanged

接着看surfaceDestroyed的回調,主要就是SurfaceView變得不可見時調用:

if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                    + "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                            
                            if (mSurface.isValid()) {
                                mSurface.forceScopedDisconnect();
                            }
                        }
                    }

到此SurfaceView自己的Surface創建流程以及SurfaceHolder.Callback回調時機已經清楚了。這些完成之後就會對SurfaceView進行測量,佈局與繪製了,雖然SurfaceView不會走onDraw流程,但是它重寫了View的dispatchDraw方法,所以還是會調dispatchDraw這個方法:

SurfaceView.dispatchDraw

@Override
    protected void dispatchDraw(Canvas canvas) {
        if (mDrawFinished && !isAboveParent()) {
            // draw() is not called when SKIP_DRAW is set
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                // punch a whole in the view-hierarchy below us
                clearSurfaceViewPort(canvas);
            }
        }
        super.dispatchDraw(canvas);
    }

    private void clearSurfaceViewPort(Canvas canvas) {
        if (mCornerRadius > 0f) {
            canvas.getClipBounds(mTmpRect);
        
            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
                    mCornerRadius, mCornerRadius, mRoundedViewportPaint);
        } else {
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
    }

這個方法最終其實是調用了canvas.drawColor給SurfaceView畫了一個黑色背景,這裏的canvas是APP的,並不是SurfaceView的。

接着我們要來看看SurfaceView的繪製原理,首先來看一段最簡單的使用SurfaceView繪製的代碼:

package com.example.surfaceviewdemo;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainActivity extends AppCompatActivity {
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private Paint mPaint;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSurfaceView = findViewById(R.id.surfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new MyHolderCallback());
        mPaint = new Paint();
    }
    class MyHolderCallback implements SurfaceHolder.Callback{

        @Override
        public void surfaceCreated(final SurfaceHolder holder) {
            Log.d("dongjiao","surfaceCreated....");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mPaint.setColor(Color.RED);
                    Canvas canvas = holder.lockCanvas();
                    canvas.drawLine(0,100,400,100,mPaint);
                    holder.unlockCanvasAndPost(canvas);
                }
            }).start();
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Log.d("dongjiao","surfaceChanged....");
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            Log.d("dongjiao","surfaceDestroyed....");
        }
    }
}

在子線程中繪製一條直線:
在這裏插入圖片描述
我們可以發現好像用SurfaceView進行繪製和用普通View繪製的區別不大,SurfaceView除了能夠在子線程繪製外其他都差不多,爲什麼在SurfaceView中需要自己手動獲取canvas,而普通View不需要,想一下普通的自定義View的onDraw方法中的canvas哪來的,來自ViewRootImpl中的drawSoftware方法:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
	......
	canvas = mSurface.lockCanvas(dirty);
	....
	mView.draw(canvas);
	....
	surface.unlockCanvasAndPost(canvas);
	.....
}

我們可以發現,普通View的canvas和SurfaceView的canvas獲取方式是一樣的,不一樣的只是Surface,普通View由系統幫我們獲取之後通過DecorView傳遞到View樹每一個View的onDraw方法中,因爲SurfaceView有自己的Surface,所以需要手動通過自己的Surface獲取自己單獨的canvas,我們接下來就來看看SurfaceView中這小小的一段繪製代碼到底做了什麼事。

繪製的代碼就只有三句:

               Canvas canvas = holder.lockCanvas();
               canvas.drawLine(0,100,400,100,mPaint);
               holder.unlockCanvasAndPost(canvas);

首先來看canvas的獲取:

holder.lockCanvas

@Override
        public Canvas lockCanvas() {
            return internalLockCanvas(null, false);
        }

接着會調用SurfaceHolder的internalLockCanvas方法:

SurfaceHolder.internalLockCanvas

private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
            mSurfaceLock.lock();

            Canvas c = null;
            if (!mDrawingStopped && mSurfaceControl != null) {
                try {
                    if (hardware) {
                        c = mSurface.lockHardwareCanvas();
                    } else {
                        c = mSurface.lockCanvas(dirty);
                    }
                } catch (Exception e) {
                   
                }
            }

            if (c != null) {
                mLastLockTime = SystemClock.uptimeMillis();
                return c;
            }

            ......
            mSurfaceLock.unlock();

            return null;
        }

我們這裏調用沒有使用硬件加速(使用CPU渲染)的繪製,所以走mSurface.lockCanvas,dirty傳遞的是null,接着調到Surface的lockCanvas方法

Surface.lockCanvas

public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mLockedObject != 0) {
                
                throw new IllegalArgumentException("Surface was already locked");
            }
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

mLockedObject是native層返回的Surface的指針,是根據mNativeObject重新創建的一個native層Surface,接着來看看nativeLockCanvas方法:

nativeLockCanvas

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
   ......
    Rect dirtyRect(Rect::EMPTY_RECT);
    Rect* dirtyRectPtr = NULL;

    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;
    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
    if (err < 0) {
        const char* const exception = (err == NO_MEMORY) ?
                OutOfResourcesException :
                "java/lang/IllegalArgumentException";
        jniThrowException(env, exception, NULL);
        return 0;
    }

    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                         convertPixelFormat(outBuffer.format),
                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);

    SkBitmap bitmap;
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setInfo(info, bpr);
    if (outBuffer.width > 0 && outBuffer.height > 0) {
        bitmap.setPixels(outBuffer.bits);
    } else {
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    }

    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(bitmap);

    if (dirtyRectPtr) {
        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
    }

    if (dirtyRectObj) {
        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();
}

首先如果我們調用lockCanvas時指定了繪製的髒區域(對於非髒區域就直接複製上一個緩衝區的),則將此區域大小賦值給dirtyRect,讓dirtyRectPtr指向此區域,這裏我們傳遞的是null所以不會走這一步,接下來就是最重要的步驟了surface->lock,傳遞了兩個參數,outBuffer類型爲ANativeWindow_Buffer,其實可以理解爲它就是代表GraphicBuffer,來看看ANativeWindow_Buffer結構體:

typedef struct ANativeWindow_Buffer {
    /// The number of pixels that are shown horizontally.
    int32_t width;

    /// The number of pixels that are shown vertically.
    int32_t height;

    /// The number of *pixels* that a line in the buffer takes in
    /// memory. This may be >= width.
    int32_t stride;

    /// The format of the buffer. One of AHardwareBuffer_Format.
    int32_t format;

    /// The actual bits.
    void* bits;

    /// Do not touch.
    uint32_t reserved[6];
} ANativeWindow_Buffer;

這個結構體中的寬高,格式等就是surface->lock申請到的GraphicBuffer的寬高,格式,最重要的是bits,它保存了GraphicBuffer的地址,GraphicBuffer中的數據比較大,在進程間傳遞是不現實的,所以傳遞的是句柄。

surface->lock主要目的就是向BufferQueue申請圖形緩衝區GraphicBuffer,首次調用時GraphicBuffer將被分配並初始化爲零,GraphicBuffer的內存分配使用的Gralloc,Gralloc中有兩個重要的輔助,一個負責GraphicBuffer內存分配的GraphicBufferAllocator,另一個負責GraphicBuffer內存映射的GraphicBufferMapper

Surface->lock

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
	......
	status_t err = dequeueBuffer(&out, &fenceFd);
	......
	const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        const bool canCopyBack = (frontBuffer != nullptr &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);
        .....
	
	sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
	......
	
	void* vaddr;
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);
       .......
                
	if (res != 0) {
            err = INVALID_OPERATION;
        } else {
            mLockedBuffer = backBuffer;
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            outBuffer->bits   = vaddr;
        }

}

可以看一下,Surface->lock核心就是上面這點代碼,這裏用到了雙重緩衝,我們申請的GraphicBuffer其實是backBuffer,還有一個frontBuffer正在顯示,當我們在backBuffer繪製好數據之後調用unlockAndPost提交時會交換兩個buffer,雙重buffer使畫面更加流暢。

dequeueBuffer函數最終會向BufferQueue申請GraphicBuffer,可參考AndroidQ 圖形系統(3)dequeueBuffer函數分析
申請的大致步驟是:優先獲取已經綁定了GraphicBuffer且狀態爲FREE的BufferSlot,如果沒有,再獲取沒有綁定GraphicBuffer且狀態爲FREE的BufferSlot,並設置flag爲BUFFER_NEEDS_REALLOCATION,然後會通過GrallocGraphicBuffer進行內存分配和映射。

接着dequeueBuffer申請到GraphicBuffer之後,通過lockAsync最終調到GraphicBufferMapper中得到GraphicBuffer的vaddr,最後給開始傳遞過來的ANativeWindow_Buffer outBuffer賦值,outBuffer就保存了GraphicBuffer的相關信息,並且有了vaddr之後就可以對GraphicBuffer進行操作了。

我們再回到nativeLockCanvassurface->lock之後的操作,創建了SkImageInfoSkBitmap,這兩個是Skia 2D圖形庫相關的東西,並且關聯上了ANativeWindow_Buffer,然後通過java層canvas得到native層canvas,並調用setBitmapSkBitmap設置到canvas中,所以我們View中使用canvas API時其實使用的是Skia庫畫在了SkBitmap上,最終到了GraphicBuffer,目前Android系統已經很少使用Skia繪圖了,更多的是OpenGL ES 或 Vulkan 。
再接着看Surface->lock最後一點代碼,因爲我們沒有指定髒區域,所以這部分代碼不會走,最後根據nativeObject得到的surface在拷貝一個lockedSurface返回給java層。

到此SurfaceHolder的lockCanvas函數已經分析完畢了,進行一下總結:

  1. SurfaceHolder的lockCanvas會通過java Surface的lockCanvas調到native層。
  2. 在native層最重要的一件事情就是通過native Surface的lock函數,最終通過dequeueBuffer函數向BufferQueue申請GraphicBuffer,首次申請GraphicBuffer是一件很複雜的事情,需要Gralloc模塊幫忙分配內存和映射內存。
  3. GraphicBuffer申請完成之後會創建Skia庫相關的SkImageInfoSkBitmapSkImageInfo的相關信息就是從申請到的GraphicBuffer中獲取的,通過setInfoGraphicBufferSkBitmap關聯,同時還將SkBitmap設置到Canvas中,這樣上層java使用Canvas API底層依靠的是Skia進行繪製。

再回到SurfaceView中的一小段繪製代碼:

               Canvas canvas = holder.lockCanvas();
               canvas.drawLine(0,100,400,100,mPaint);
               holder.unlockCanvasAndPost(canvas);

lockCanvas結束之後,使用Canvas APIdrawLine方法進行繪製,底層通過Skia完成繪製之後調用unlockCanvasAndPost將繪製結果提交,我們再來簡單看看unlockCanvasAndPost方法,這個方法會同樣是調到native層:

nativeUnlockCanvasAndPost

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    if (!isSurfaceValid(surface)) {
        return;
    }

    // detach the canvas from the surface
    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(SkBitmap());

    // unlock surface
    status_t err = surface->unlockAndPost();
    if (err < 0) {
        doThrowIAE(env);
    }
}

首先做一些收尾工作然後調用surface->unlockAndPost():

Surface->unlockAndPost

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函數,這個函數會將繪製好的圖形緩衝區放入BufferQueue中,然後通知SurfaceFlinger取,
AndroidQ 圖形系統(4)queueBuffer函數分析
,通知的過程就是請求一個SurfaceFlinger進程的Vsync,待收到Vsync之後就可以拿到圖形緩衝區進行合成了。

此函數最後mPostedBuffer = mLockedBuffer將backBuffer交換到前臺。

到此SurfaceView的繪製原理大致流程已經分析完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章