Android 12(S) 圖形顯示系統 - 簡單聊聊 SurfaceView 與 BufferQueue的關聯(十三)

二的次方發表於2022-03-25

必讀:

Android 12(S) 圖形顯示系統 - 開篇


 

一、前言


前面的文章中,講解的內容基本都是從我們提供的一個 native demo Android 12(S) 圖形顯示系統 - 示例應用(二)  來談起的。實際中,我們更多的是基於 application framework 的 java api 來進行開發工作。從事音視訊等工作比較常用的就是 SurfaceView了。SurfaceView 其優秀的特性讓其廣泛的應用在 Android 的視訊、遊戲、攝像頭等開發領域,這篇文章我們就簡單聊一聊 SurfaceView,看看它是如何同我們前面分析的圖形顯示邏輯關聯起來的。

 

二、涉及的元件及原始碼位置


Android framework 層提供了豐富的圖形顯示的高階別元件,本文中涉及的元件我們列於下面:

元件 原始碼位置

SurfaceView

Java /frameworks/base/core/java/android/view/SurfaceView.java
JNI

SurfaceControl

Java /frameworks/base/core/java/android/view/SurfaceControl.java
JNI /frameworks/base/core/jni/android_view_SurfaceControl.cpp

Surface

Java /frameworks/base/core/java/android/view/Surface.java
JNI /frameworks/base/core/jni/android_view_Surface.cpp

BLASTBufferQueue

Java /frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java
JNI /frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp

 

可以看到這些高階別元件都有JNI串接到native code。從元件的名字也可以看出,這些元件肯定和我們前面文章中講到的native層的Surface/SurfaceControl/BLASTBufferQueue 冥冥中有某些聯絡。

 

三、SurfaceView和BufferQueue的關聯


關於SurfaceView如何建立的,我們不做講解,感興趣的可以去網路上搜尋相關資訊即可。比如這篇文章:Android SurfaceView原理分析

我們直接進入我們關注的內容:java層的 SurfaceView 是如何與 native 層的 buffer queue 關聯並協同工作的呢?

 

應用建立SurfaceView後,會呼叫到到updateSurface方法,我們看一下這個方法中主要做了哪些工作:

protected void updateSurface() {
    // 中間省略的基本都是些引數的初始化或狀態的獲取與判斷
    if (判斷在 建立、格式發生變化、size變化等情況下要做什麼) {
        // 中間省略一些 設定 寬、高、格式等資訊的程式碼
        if (creating) {
            final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
            // mUseBlastAdapter 預設是 true
            if (mUseBlastAdapter) {
                // 第一次需要建立時呼叫到 createBlastSurfaceControls
                createBlastSurfaceControls(viewRoot, name);
            } else {
                // 如果不使用BlastAdapter則呼叫 createSurfaceControls
                mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot, name);
            }
         }

        ...
        copySurface(creating /* surfaceControlCreated */, sizeChanged);
        ...
        // 後面省略 callback 的邏輯
}

 

第一次建立時呼叫到createBlastSurfaceControls,在createBlastSurfaceControls中可以看到會去建立3個SurfaceControl,請看主要程式碼:

private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name) {
    if (mSurfaceControl == null) {
        mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) // 建立SurfaceControl - 1
                .setName(name)
                .setLocalOwnerView(this)
                .setParent(viewRoot.getBoundsLayer())
                .setCallsite("SurfaceView.updateSurface")
                .setContainerLayer()
                .build();
    }

    if (mBlastSurfaceControl == null) {
        mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)// 建立SurfaceControl - 2
                .setName(name + "(BLAST)")
                .setLocalOwnerView(this)
                .setParent(mSurfaceControl)
                .setFlags(mSurfaceFlags)
                .setHidden(false)
                .setBLASTLayer()
                .setCallsite("SurfaceView.updateSurface")
                .build();
    } else {
        // update blast layer
        mTmpTransaction
                .setOpaque(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                .setSecure(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.SECURE) != 0)
                .show(mBlastSurfaceControl)
                .apply();
    }

    if (mBackgroundControl == null) {
        mBackgroundControl = createBackgroundControl(name);// 建立SurfaceControl - 3
    }

    // Always recreate the IGBP for compatibility. This can be optimized in the future but
    // the behavior change will need to be gated by SDK version.
    if (mBlastBufferQueue != null) {
        mBlastBufferQueue.destroy();
    }
    mTransformHint = viewRoot.getSurfaceTransformHint();
    mBlastSurfaceControl.setTransformHint(mTransformHint); 
        
    // 建立 BLASTBufferQueue
    mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
            mSurfaceHeight, mFormat);
}

 

在createBlastSurfaceControls中可以看到會去建立3個SurfaceControl:

mSurfaceControl : 名字是 "SurfaceView[" + viewRoot.getTitle().toString() + "]" 

mBlastSurfaceControl : 名字是 "SurfaceView[" + viewRoot.getTitle().toString() + "]" + "(BLAST)" 

mBackgroundControl : 名字是 "Background for " + "SurfaceView[" + viewRoot.getTitle().toString() + "]"

 

而且三者之間的關係:mSurfaceControl 是 mBlastSurfaceControl 和 mBackgroundControl 的 parent

mSurfaceControl.setParent(viewRoot.getBoundsLayer()) 

mBlastSurfaceControl.setParent(mSurfaceControl) 

mBackgroundControl.setParent(mSurfaceControl)

 

最後還有一句最重要的建立 BLASTBufferQueue:

mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
          mSurfaceHeight, mFormat);

 

我們分別看看建立 SurfaceControl 和 建立 BLASTBufferQueue 都具體做了什麼?

建立 SurfaceControl 做了什麼?

先看看建構函式:

private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
    SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
    String callsite)
            throws OutOfResourcesException, IllegalArgumentException {

    mNativeObject = nativeCreate(session, name, w, h, format, flags,
                parent != null ? parent.mNativeObject : 0, metaParcel);
}

建構函式中除了記錄處理一些基本資訊外,最重要的就是呼叫了 nativeCreate 方法,這個方法會走到JNI中去:

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {

    sp<SurfaceComposerClient> client; // 獲取SurfaceComposerClient,建立和SurfaceFlinger的通訊
    if (sessionObj != NULL) {
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;

    sp<IBinder> parentHandle; // 是否有parent layer
    if (parent != nullptr) {
        parentHandle = parent->getHandle();
    }
    // 建立surface & layer
    status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
                                                flags, parentHandle, std::move(metadata));

    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get()); // 將surface的地址、指標返回給 java object 儲存
}

上述的程式碼流程是不是很熟悉,nativeCreate中做的事情類似我們前面文章中分析過的。

Android 12(S) 圖形顯示系統 - 應用建立和SurfaceFlinger的溝通橋樑(三)

Android 12(S) 圖形顯示系統 - createSurface的流程(五)

JNI層中呼叫SurfaceFlinger建立surface,就是建立一個native SurfaceControl物件,然後把這個物件的地址返回給 java surfacecontrol 物件,儲存到它的成員 mNativeObject 中。這樣子 Java 層的 SurfaceControl 物件就和 native 層的 SurfaceControl 物件關聯在了一起,在java層對SurfaceControl 進行操作本質就是經JNI操作native 層的 SurfaceControl 物件。


本文作者@二的次方  2022-03-25 釋出於部落格園


建立 BLASTBufferQueue 做了什麼?

同樣先看建構函式

    public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
            @PixelFormat.Format int format) {
        mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format);
    }

也是去呼叫了 nativeCreate,在JNI層去建立了 native BLASTBufferQueue物件

static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
                          jlong width, jlong height, jint format) {
    std::string name = str8.string();
    sp<BLASTBufferQueue> queue =  // 建立native BLASTBufferQueue物件
            new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,
                                 height, format);
    queue->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(queue.get());// 返回BLASTBufferQueue地址給java object儲存
}

同樣也是把native BLASTBufferQueue物件的地址返回給 java object,儲存到 mNativeObject。這樣子 Java 層的 BLASTBufferQueue就和 native 層的 BLASTBufferQueue物件關聯在了一起,在java層對 BLASTBufferQueue 進行操作本質就是經JNI操作native 層的 BLASTBufferQueue 物件。

 

BLASTBufferQueue中有createSurface的方法

/**
 * @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
 */
public Surface createSurface() {
    return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
}

/**
 * @return a new Surface instance from the IGraphicsBufferProducer of the adapter and
 * the SurfaceControl handle.
 */
public Surface createSurfaceWithHandle() {
    return nativeGetSurface(mNativeObject, true /* includeSurfaceControlHandle */);
}

呼叫到了JNI

static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
                                jboolean includeSurfaceControlHandle) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    return android_view_Surface_createFromSurface(env,
                                                  queue->getSurface(includeSurfaceControlHandle));
}

queue->getSurface()方法在Android 12(S) 圖形顯示系統 - BufferQueue/BLASTBufferQueue之初識(六)中分析過,可以自行檢視。

android_view_Surface_createFromSurface 就是建立了一個 java Surface object。並且也把 native surface object的地址儲存在了 java Surface object的成員mNativeObject中。

 

在SurfaceView的updateSurface最後還呼叫了一個方法 copySurface,這個是幹什麼的?

private void copySurface(boolean surfaceControlCreated, boolean bufferSizeChanged) {
    if (surfaceControlCreated) {
        if (mUseBlastAdapter) {
            mSurface.copyFrom(mBlastBufferQueue);
        } else {
            mSurface.copyFrom(mSurfaceControl);
        }
    }
    ...
}

程式碼很簡單,接著往下走到Surface::copyFrom,只看blast的情況

public void copyFrom(BLASTBufferQueue queue) {
    long blastBufferQueuePtr = queue.mNativeObject; // 獲取BLASTBufferQueue的native物件的地址
    long newNativeObject = nativeGetFromBlastBufferQueue(mNativeObject, blastBufferQueuePtr); // 去建立一個 native Surface
    updateNativeObject(newNativeObject);
}

又又又來到了JNI的層面 nativeGetFromBlastBufferQueue

static jlong nativeGetFromBlastBufferQueue(JNIEnv* env, jclass clazz, jlong nativeObject,
                                           jlong blastBufferQueueNativeObj) {
    Surface* self(reinterpret_cast<Surface*>(nativeObject));
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(blastBufferQueueNativeObj);
    const sp<IGraphicBufferProducer>& bufferProducer = queue->getIGraphicBufferProducer();
    // If the underlying IGBP's are the same, we don't need to do anything.
    if (self != nullptr &&
        IInterface::asBinder(self->getIGraphicBufferProducer()) ==
                IInterface::asBinder(bufferProducer)) {
        return nativeObject;
    }

    sp<Surface> surface = queue->getSurface(true /* includeSurfaceControlHandle */);  // 建立native surface
    if (surface != NULL) {
        surface->incStrong(&sRefBaseOwner);
    }

    return reinterpret_cast<jlong>(surface.get());
}

是不是還有是去使用 BLASTBufferQueue::getSurface 去建立 native surface,而且最後把新建立的native surface的地址儲存在了java Surface object的成員mNativeObject中。

 


 

通過上面的簡單介紹,差不多可以理清了java層各個元件是如何與native層的元件關聯在一起的了。正式這種關聯的存在java層的 SurfaceView 是與 native 層的 buffer queue 就可以協同工作了

簡單總結一張圖:

 


講到這裡一直有個疑問,SurfaceView中為什麼要建立三個SurfaceControl物件?

這三個SurfaceControl分別對應到SurfaceFlinger中的不同layer,在繪圖時發揮不同作用。

♦ mSurfaceControl : Container Layer

♦ mBlastSurfaceControl : Buffer State Layer

♦ mBackgroundControl : Effect Layer

mBlastSurfaceControl才是真正去建立BufferQueue,提供生產者、消費者去繪製圖形用的。

 


 

我們可以執行 dumpsys SurfaceFlinger來觀察資訊:這是一個使用SurfaceView進行播放的例子,可以觀察各個layer的name 和 parent 資訊,是不是和我們分析的一致。

// background layer
+ EffectLayer (Background for SurfaceView[com.demoplayer/com.demoplayer.MainActivity]#0) uid=10063
  Region SurfaceDamageRegion (this=0 count=0)
      layerStack=   0, z=-2147483648, pos=(0,0), size=(  -1,  -1), crop=[  0,   0,  -1,  -1], cornerRadius=0.000000, isProtected=0, isTrustedOverlay=0, isOpaque=1, invalidate=0, dataspace=Default, defaultPixelFormat=Unknown/None, backgroundBlurRadius=0, color=(0.000,0.000,0.000,1.000), flags=0x00000002, tr=[0.00, 0.00][0.00, 0.00]
      parent=SurfaceView[com.demoplayer/com.demoplayer.MainActivity]#0
      zOrderRelativeOf=com.demoplayer/com.demoplayer.MainActivity#0
      activeBuffer=[   0x   0:   0,Unknown/None], tr=[0.00, 0.00][0.00, 0.00] queued-frames=0, mRefreshPending=0, metadata={}, cornerRadiusCrop=[0.00, 0.00, 0.00, 0.00],  shadowRadius=0.000, 

// container layer
+ ContainerLayer (SurfaceView[com.demoplayer/com.demoplayer.MainActivity]#0) uid=10063
  Region SurfaceDamageRegion (this=0 count=0)
      layerStack=   0, z=       -2, pos=(0,0), size=(  -1,  -1), crop=[  0,   0, 1920, 1080], cornerRadius=0.000000, isProtected=0, isTrustedOverlay=0, isOpaque=0, invalidate=1, dataspace=Default, defaultPixelFormat=Unknown/None, backgroundBlurRadius=0, color=(0.000,0.000,0.000,1.000), flags=0x00000000, tr=[0.00, 0.00][0.00, 0.00]
      parent=Bounds for - com.demoplayer/com.demoplayer.MainActivity#0
      zOrderRelativeOf=com.demoplayer/com.demoplayer.MainActivity#0
      activeBuffer=[   0x   0:   0,Unknown/None], tr=[0.00, 0.00][0.00, 0.00] queued-frames=0, mRefreshPending=0, metadata={}, cornerRadiusCrop=[0.00, 0.00, 0.00, 0.00],  shadowRadius=0.000, 

// buffer state layer 
+ BufferStateLayer (SurfaceView[com.demoplayer/com.demoplayer.MainActivity](BLAST)#0) uid=10063
  Region SurfaceDamageRegion (this=0 count=0)
      layerStack=   0, z=        0, pos=(0,0), size=(3840,2160), crop=[  0,   0,  -1,  -1], cornerRadius=0.000000, isProtected=0, isTrustedOverlay=0, isOpaque=1, invalidate=0, dataspace=BT2020 SMPTE 2084 Limited range, defaultPixelFormat=Unknown 0x000077, backgroundBlurRadius=0, color=(0.000,0.000,0.000,1.000), flags=0x00000102, tr=[0.50, 0.00][0.00, 0.50]
      parent=SurfaceView[com.demoplayer/com.demoplayer.MainActivity]#0
      zOrderRelativeOf=none
      activeBuffer=[3840x2160:3840,Unknown 0x000077], tr=[0.00, 0.00][0.00, 0.00] queued-frames=0, mRefreshPending=0, metadata={dequeueTime:129821039996}, cornerRadiusCrop=[0.00, 0.00, 0.00, 0.00],  shadowRadius=0.000,

 

四、小結

 

 

相關文章