Android 12(S) 图形显示系统 - 简单聊聊 SurfaceView 与 BufferQueue的关联(十三)
必读:
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,
四、小结
Android 12(S) 图形显示系统 - 简单聊聊 SurfaceView 与 BufferQueue的关联(十三)的更多相关文章
- Android 12(S) 图形显示系统 - 基本概念(一)
1 前言 Android图形系统是系统框架中一个非常重要的子系统,与其它子系统一样,Android 框架提供了各种用于 2D 和 3D 图形渲染的 API供开发者使用来创建绚丽多彩的应用APP.图形渲 ...
- Android 12(S) 图形显示系统 - 示例应用(二)
1 前言 为了更深刻的理解Android图形系统抽象的概念和BufferQueue的工作机制,这篇文章我们将从Native Level入手,基于Android图形系统API写作一个简单的图形处理小程序 ...
- Android 12(S) 图形显示系统 - 应用建立和SurfaceFlinger的沟通桥梁(三)
1 前言 上一篇文章中我们已经创建了一个Native示例应用,从使用者的角度了解了图形显示系统API的基本使用,从这篇文章开始我们将基于这个示例应用深入图形显示系统API的内部实现逻辑,分析运作流程. ...
- Android 12(S) 图形显示系统 - SurfaceFlinger的启动和消息队列处理机制(四)
1 前言 SurfaceFlinger作为Android图形显示系统处理逻辑的核心单元,我们有必要去了解其是如何启动,初始化及进行消息处理的.这篇文章我们就来简单分析SurfaceFlinger这个B ...
- Android 12(S) 图形显示系统 - createSurface的流程(五)
题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...
- Android 12(S) 图形显示系统 - 初识ANativeWindow/Surface/SurfaceControl(七)
题外话 "行百里者半九十",是说步行一百里路,走过九十里,只能算是走了一半.因为步行越接近目的地,走起来越困难.借指凡事到了接近成功,往往是最吃力.最艰难的时段.劝人做事贵在坚持, ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(九)
题外话 Covid-19疫情的强烈反弹,小区里检测出了无症状感染者.小区封闭管理,我也不得不居家办公了.既然这么大把的时间可以光明正大的宅家里,自然要好好利用,八个字 == 努力工作,好好学习 一.前 ...
- Android 12(S) 图形显示系统 - 解读Gralloc架构及GraphicBuffer创建/传递/释放(十四)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 在前面的文章中,已经出现过 GraphicBuffer 的身影,GraphicBuffer 是Android图形显示系统中的一个重 ...
- Android 12(S) 图形显示系统 - BufferQueue/BLASTBufferQueue之初识(六)
题外话 你有没有听见,心里有一声咆哮,那一声咆哮,它好像在说:我就是要从后面追上去! 写文章真的好痛苦,特别是自己对这方面的知识也一知半解就更加痛苦了.这已经是这个系列的第六篇了,很多次都想放弃了,但 ...
随机推荐
- Solution -「LOCAL」ZB 平衡树
\(\mathcal{Description}\) OurOJ. 维护一列二元组 \((a,b)\),给定初始 \(n\) 个元素,接下来 \(m\) 次操作: 在某个位置插入一个二元组: 翻 ...
- python写的百度图片爬虫
学了一下python正则表达式,写一个百度图片爬虫玩玩. 当技术遇上心术不正的人,就成我这样的2B青年了. python3.6开发.程序已经打包好,下载地址: http://pan.baidu.com ...
- 第一次接触数据库(SQLite)
第一次接触,学了创建列表 + 行的删除 + 内容的更改 + 删除列表 第一次接触要知道一些基本知识 NULL(SQL) = Nnoe(python) #空值 INTEGER = int #整数 R ...
- ACM训练,大理石在哪儿?
int p = lower_bound(a, a+num, x) - a; //在已排序数组a中查找大于或等于x的第一个位置 lower_bound( )返回的是一个迭代器,-a相当于减去a的首地址, ...
- bower install 报错fatal: unable to access 'https://github.com/angular/bower-angular-touch.git/'类错误解决方法
bower install时出现很多unable to access 'https://github.com/angular/bower-angular-touch.git/'类似的错误, 方法一:( ...
- MySQL第五讲
内容回顾 单表操作 """ 1.配置文件先统一设置成utf8 \s 2.无论你怎么改都没有生效 你的机器上不止一个mysql文件 C有一个 D有一个 3.百度搜索 sho ...
- 开发并发布依赖包,作为工具包供别人npm install
1.初识包及 npm 包的概念: nodejs 中的第三方模块叫包 不同于 nodejs 中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用 nodejs 中的包都是免费开源 ...
- C# NPOI导出数据到Excel
1 public void Export() 2 { 3 //创建工作簿对象 4 IWorkbook workbook = new XSSFWorkbook(); 5 6 ExportStatisti ...
- [源码解析] NVIDIA HugeCTR,GPU 版本参数服务器 --(9)--- Local hash表
[源码解析] NVIDIA HugeCTR,GPU 版本参数服务器 --(9)--- Local hash表 目录 [源码解析] NVIDIA HugeCTR,GPU 版本参数服务器 --(9)--- ...
- git合并分支代码的方法
1.先提交本地代码,防止被拉取其他分支的代码污染(self为自己的分支 other为想要拉取的分支) git add . git commit -m '备注信息' git push origin se ...