必讀:
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,
四、小結