• Android 12(S) 图像显示系统 - 简单聊聊 SurfaceView 与 BufferQueue的关联(十三)



    必读:

    Android 12(S) 图像显示系统 - 开篇


     

    一、前言


    前面的文章中,讲解的内容基本都是从我们提供的一个 native demo Android 12(S) 图形显示系统 - 示例应用(二)  来谈起的。实际中,我们更多的是基于 application framework 的 java api 来进行开发工作。从事音视频等工作比较常用的就是 SurfaceView了。SurfaceView 其优秀的特性让其广泛的应用在 Android 的视频、游戏、摄像头等开发领域,这篇文章我们就简单聊一聊 SurfaceView,看看它是如何同我们前面分析的图形显示逻辑关联起来的。

     

    二、涉及的组件及源码位置


    Android framework 层提供了丰富的图形显示的高级别组件,本文中涉及的组件我们列于下面:

     

    可以看到这些高级别组件都有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,

     

    四、小结

     

     

  • 相关阅读:
    韦东山老师 RTOS 入门课程(二)理解任务的创建,切换过程
    【学习方法论】学习的三种境界、三种习惯、三个要点,三个心态
    Devkit代码迁移工具——smartdenovo源码迁移
    Zookeeper分布式锁的概念及原理
    k8s最小镜像
    python 多进程windows报错 linux不报错 TypeError: cannot pickle ‘_thread.lock‘ object
    OS>>信号的产生,信号的保存,信号的捕捉
    解决 Element-ui中 表格(Table)使用 v-if 条件控制列显隐时数据展示错乱的问题
    博客园商业化之路-众包平台:继续召集早期合作开发者
    主数据管理平台产品功能组成架构
  • 原文地址:https://www.cnblogs.com/roger-yu/p/16041250.html