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

相关文章

Nginx搭建前端本地预览环境的完整步骤教学

《Nginx搭建前端本地预览环境的完整步骤教学》这篇文章主要为大家详细介绍了Nginx搭建前端本地预览环境的完整步骤教学,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录项目目录结构核心配置文件:nginx.conf脚本化操作:nginx.shnpm 脚本集成总结:对前端的意义很多

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

Oracle数据库在windows系统上重启步骤

《Oracle数据库在windows系统上重启步骤》有时候在服务中重启了oracle之后,数据库并不能正常访问,下面:本文主要介绍Oracle数据库在windows系统上重启的相关资料,文中通过代... oracle数据库在Windows上重启的方法我这里是使用oracle自带的sqlplus工具实现的方

Git打标签从本地创建到远端推送的详细流程

《Git打标签从本地创建到远端推送的详细流程》在软件开发中,Git标签(Tag)是为发布版本、标记里程碑量身定制的“快照锚点”,它能永久记录项目历史中的关键节点,然而,仅创建本地标签往往不够,如何将其... 目录一、标签的两种“形态”二、本地创建与查看1. 打附注标http://www.chinasem.cn

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

Java实现本地缓存的四种方法实现与对比

《Java实现本地缓存的四种方法实现与对比》本地缓存的优点就是速度非常快,没有网络消耗,本地缓存比如caffine,guavacache这些都是比较常用的,下面我们来看看这四种缓存的具体实现吧... 目录1、HashMap2、Guava Cache3、Caffeine4、Encache本地缓存比如 caff