GUI系统之SurfaceFlinger(4)opengl es本地窗口SurfaceTextureClient

本文主要是介绍GUI系统之SurfaceFlinger(4)opengl es本地窗口SurfaceTextureClient,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章都是通过阅读源码分析出来的,还在不断完善与改进中,其中难免有些地方理解得不对,欢迎大家批评指正。
转载请注明:From LXS. http://blog.csdn.net/uiop78uiop78/





1.1.1 SurfaceTextureClient

针对应用程序端的本地窗口是SurfaceTextureClient,和FramebufferNativeWindow一样,它必须继承ANativeWindow:

class SurfaceTextureClient

    : publicANativeObjectBase<ANativeWindow, SurfaceTextureClient,RefBase>

 

这个本地窗口当然也需要实现ANativeWindow所制定的“协议”,我们的重点是关注它与前面的FramebufferNativeWindow有什么不同。SurfaceTextureClient的构造函数只是简单地调用了init函数,后者则对ANativeWindow::dequeueBuffer等函数指针及内部变量赋了初值。由于整个函数的功能很简单,我们只摘录其中的一部分:

/*frameworks/native/libs/gui/SurfaceTextureClient.cpp*/

void SurfaceTextureClient::init() {

    /*给ANativeWindow中的函数指针赋值*/

   ANativeWindow::setSwapInterval  =hook_setSwapInterval;

    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;

    …

    /*为各内部变量赋值,因为此时用户还没有真正发起申请,所以基本是0*/

    mReqWidth = 0;

    mReqHeight = 0;

    …

    mDefaultWidth = 0;

    mDefaultHeight = 0;

    mUserWidth = 0;

    mUserHeight = 0;…

}

SurfaceTextureClient是面向Android系统中所有UI应用程序的,也就是说它承担着单个应用进程中的UI显示需求。基于这点考虑,可以推测出它的内部实现至少会有以下几点:

Ø  提供给上层(主要是java层)绘制图像的“画板”

前面说过,这个本地窗口分配的内存应该不是来自于帧缓冲区,那么具体是由谁分配的,又是如何管理的呢?

Ø  它与SurfaceFlinger间是如何分工的

显然SurfaceFlinger需要收集系统中所有应用程序绘制的图像数据,然后集中显示到物理屏幕上。在这个过程中,SurfaceTextureClient扮演了什么样的角色呢?

 

我们先来解释下这个类中的一些重要的成员变量,如下表所示:

表格 11‑4 SurfaceTextureClient部分成员变量一览

成员变量

说明

sp<ISurfaceTexture> mSurfaceTexture

这个变量是SurfaceTextureClient的核心,很多“协议”就是通过它实现的,后面会有详细讲解

BufferSlot mSlots[NUM_BUFFER_SLOTS]

从名称上可以看出,这是Client内部用于存储buffer的地方,容量NUM_BUFFER_SLOTS最多达32个。BufferSlot类内部又由一个GraphicBuffer和一个dirtyRegion组成,当有用户dequeueBuffer时就会分配真正的空间

uint32_t  mReqWidth

SurfaceTextureClient中有多组相似的宽高变量,它们之间是有区别的。这里的宽和高是指下一次dequeue时将会申请的尺寸,初始值都是1

uint32_t  mReqHeight

uint32_t mReqFormat

和上面的两变量类似,这是指下次dequeue时将会申请的buffer的像素格式,初始值是PIXEL_FORMAT_RGBA_8888

uint32_t mReqUsage

指下次dequeue时将会指定的usage类型

Rect mCrop

Crop表示“修剪”,这个变量将在下次queue时用于修剪缓冲区,可以调用setCrop来设置具体的值

int mScalingMode

同样,这个变量将用于下次queue时对缓冲区进行scale,可以调用setScalingMode来设置具体的值

uint32_t mTransform

用于下次queue时的图形翻转等操作(Transform)

uint32_t mDefaultWidth

默认情况下的缓冲区宽高值

uint32_t mDefaultHeight

uint32_t mUserWidth

如果不为零的话,就是应用层指定的值,将会覆盖前面的mDefaultWidth/ mDefaultHeight

uint32_t mUserHeight

sp<GraphicBuffer>           mLockedBuffer

这三个值需要锁的保护,接下来还会有分析

sp<GraphicBuffer>           mPostedBuffer

Region mDirtyRegion

从这些内部变量的描述中,我们可以大概了解到两点:SurfaceTextureClient中将通过mSurfaceTexture来获得buffer,而且这些缓冲区会被记录在mSlots数组中。接下来就来分析其中的实现细节。

前面SurfaceTextureClient构造函数里我们看到ANativeWindow中的函数指针赋予的是各种以hook开头的函数,这些函数内部又直接调用了SurfaceTextureClient中真正的实现,比如hook_dequeueBuffer对应的是dequeueBuffer。这就好像是“钩子”一样,所以称之为hook。

int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t**buffer) {…

    Mutex::Autolocklock(mMutex);

int buf = -1;

/*Step1. 宽高计算*/

    int reqW = mReqWidth ?mReqWidth : mUserWidth;

int reqH = mReqHeight ? mReqHeight :mUserHeight;

/*Step2. dequeueBuffer得到一个缓冲区*/

    status_t result =mSurfaceTexture->dequeueBuffer(&buf, reqW, reqH,mReqFormat, mReqUsage);

    …

   sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);//注意buf只是一个int值,代表的是mSlots数组序号

/*Step3. requestBuffer*/

    if ((result &ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {

        result =mSurfaceTexture->requestBuffer(buf, &gbuf);

        …

    }

    *buffer = gbuf.get();

    return OK;

}

Step1@ SurfaceTextureClient::dequeueBuffer。用于UI绘制的图形缓冲区一定有宽高属性,具体的值由mReqWidth/mReqHeight或者mUserWidth/mUserHeight决定,其中前者的优先级比后者高

Step2@ SurfaceTextureClient::dequeueBuffer。可以看到,真正执行dequeueBuffer操作的确实是mSurfaceTexture(ISurfaceTexture)。这个变量的赋值有两个来源:作为SurfaceTextureClient的构造函数参数传入,然后间接调用setISurfaceTexture来设置的;或者SurfaceTextureClient的子类通过直接调用setISurfaceTexture来生成。

在应用进程环境中,属于后面一种情况。

具体流程就是:当Java层的Surface进行init时,实际上执行的函数是Surface_init@android_view_Surface.cpp。这个JNI函数将进一步调用SurfaceComposerClient::createSurface生成一个SurfaceControl,后者是用于管理Surface的类。它将在SurfaceControl::getSurface时生成一个Surface实例,在构造时通过SurfaceControl:: getSurfaceTexture来获得一个ISurfaceTexture。而Surface类实际上又继承自SurfaceTextureClient,所以它可以调用setISurfaceTexture。

由于Android源码有多处称为Surface的地方,取名极其混乱,我们下面通过一张完整的流程图来帮助大家把这些关系理顺:


图 11‑10 ISurfaceTexture创建流程

 

从这个图中可以看到,ISurfaceTexture是由ISurface::getSurfaceTexture生成的,而ISurface则是由SurfaceFlinger生成的。在这一过程中,总共使用到了三个匿名binderserver,它们所提供的接口整理如下表:

表格 11‑5 与Surface相关的三个匿名binder

匿名Binder

提供的接口

ISurfaceComposerClient

sp<ISurface> createSurface(…);

status_t  destroySurface(SurfaceID sid);

ISurface

sp<ISurfaceTexture> getSurfaceTexture(…);

ISurfaceTexture

status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);

status_t setBufferCount(int bufferCount);

status_t dequeueBuffer(…);

status_t queueBuffer(…);

void cancelBuffer(int slot);

int query(int what, int* value);

status_t setSynchronousMode(bool enabled);

status_t connect(int api, QueueBufferOutput* output);

status_t disconnect(int api);

由此可见,这三个匿名binder是一环扣一环的,也就是说我们访问的顺序只能是ISurfaceComposerClientàISurfaceàISurfaceTexture。当然,第一个匿名binder就一定是需要由一个实名binder来提供,它就是SurfaceFlinger,而SurfaceFlinger则是在ServiceManager中“注册在案”的。具体是在SurfaceComposerClient::onFirstRef()这个函数中,通过向ServiceManager查询名称为“SurfaceFlinger”的binder server来获得的。不过和其它常见binder server不同的是,SurfaceFlinger虽然在ServiceManager中注册的名称为“SurfaceFlinger”,但它在server端实现的binder接口却是ISurfaceComposer,因而SurfaceComposerClient得到的其实是ISurfaceComposer,这点大家要特别注意,否则可能会搞乱。

//我们可以从SurfaceFlinger的继承关系中看出这一区别,如下代码片断

class SurfaceFlinger :

        publicBinderService<SurfaceFlinger>, //在ServiceManager中注册为“SurfaceFlinger”

        public BnSurfaceComposer, //实现的接口却叫ISurfaceComposer,不知道为什么要这么设计。。。

绕了一大圈后,我们接着分析前面dequeueBuffer函数的实现。很显然SurfaceTextureClient只是一个中介,它间接调用mSurfaceTexture也就是ISurfaceTexture的服务。那么ISurfaceTexture在Server端又是由谁来完成的呢?


图 11‑11 ISurfaceTexture的本地端实现

 

因为这里面牵扯到很多新的类,我们先不做过多解释,到后面BufferQueue小节再详细分析其中的依赖关系。

当mSurfaceTexture->dequeueBuffer返回后,buf变量就是mSlots[]数组中可用的成员序号。接下来就要通过这个序号来获取真正的buffer地址,即mSlots[buf].buffer。

 

Step3@ SurfaceTextureClient::dequeueBuffer。假如返回值result中的标志包含了BUFFER_NEEDS_REALLOCATION,说明BufferQueue为这个Slot重新分配了空间,具体细节请参见下一个小节。此时我们还需要另外调用requestBuffer来确定gbuf的值,这其中又牵涉到很多东西,我们放在下面小节统一解释原因。

通过这两个小节,我们学习了显示系统中两个重要的本地窗口,即FramebufferNativewindow和SurfaceTextureClient。第一个窗口是专门为SurfaceFlinger服务的,它由Gralloc提供支持,相对逻辑上很好理解。而SurfaceTextureClient则是为应用程序服务的,同时它从本质上还是由SurfaceFlinger服务统一管理的,因而涉及到很多跨进程的通信细节。这个小节我们只是简单地勾勒出其中的框架,接下去就要分几个方面来做完整的分析了。

Ø  BufferQueue

为应用程序服务的本地窗口SurfaceTextureClient在server端的实现是BufferQueue。我们将详细解析BufferQueue的内部实现,并结合应用程序端的使用流程来理解清楚它们之间的关系。

 

Ø  Buffer、Consumer、Producer是“生产者-消费者”模型中的三个参与对象,如何协调好它们的工作是应用程序能否正常显示UI的关键。在接下来内容的安排上,我们先讲解Buffer(BufferQueue)与Producer(应用程序)间的交互,然后再专门切入Consumer(SurfaceFlinger)做详细分析


这篇关于GUI系统之SurfaceFlinger(4)opengl es本地窗口SurfaceTextureClient的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/926414

相关文章

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

在VSCode中本地运行DeepSeek的流程步骤

《在VSCode中本地运行DeepSeek的流程步骤》本文详细介绍了如何在本地VSCode中安装和配置Ollama和CodeGPT,以使用DeepSeek进行AI编码辅助,无需依赖云服务,需要的朋友可... 目录步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT安装Ollama下载Olla

Linux系统之authconfig命令的使用解读

《Linux系统之authconfig命令的使用解读》authconfig是一个用于配置Linux系统身份验证和账户管理设置的命令行工具,主要用于RedHat系列的Linux发行版,它提供了一系列选项... 目录linux authconfig命令的使用基本语法常用选项示例总结Linux authconfi

Nginx配置系统服务&设置环境变量方式

《Nginx配置系统服务&设置环境变量方式》本文介绍了如何将Nginx配置为系统服务并设置环境变量,以便更方便地对Nginx进行操作,通过配置系统服务,可以使用系统命令来启动、停止或重新加载Nginx... 目录1.Nginx操作问题2.配置系统服android务3.设置环境变量总结1.Nginx操作问题

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

JAVA集成本地部署的DeepSeek的图文教程

《JAVA集成本地部署的DeepSeek的图文教程》本文主要介绍了JAVA集成本地部署的DeepSeek的图文教程,包含配置环境变量及下载DeepSeek-R1模型并启动,具有一定的参考价值,感兴趣的... 目录一、下载部署DeepSeek1.下载ollama2.下载DeepSeek-R1模型并启动 二、J

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创