【Android GUI】FramebufferNativeWindow与Surface

文章目录

  • 显示整体体系
  • FramebufferNativeWindow
    • FramebufferNativeWindow构造函数
  • dequeueBuffer
  • Surface
  • 总结
  • 参考

显示整体体系

native window为OpenGL与本地窗口系统之间搭建了桥梁。
在这里插入图片描述
这个窗口系统中,有两类本地窗口,nativewindow1是能直接显示在屏幕的,使用的是帧缓冲区;nativewindow2是从内存缓冲区分配的空间,通常需要通过其他方式(例如将其内容绘制到帧缓冲区)才能在屏幕上显示。

当系统中存在多个需要显示UI的应用程序时,一方面这种改进设计保证了它们都能获得一个“本地窗口”;另一方面这些“本地窗口”也都可以被有序地显示到终端屏幕上,因为SurfaceFlinger会收集所有程序的显示需求,对它们进行统一的图像混合操作,然后输出到自己的NativeWindow-1上。

应用程序也可以使用Skia等第三方图形库,只要它们和SurfaceFlinger之间的“协议”不变即可。
在这里插入图片描述

FramebufferNativeWindow

EGL需要本地窗口为OpenGL创造环境:

EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
NativeWindowTypewindow, const EGLint *attrib_list);

无论哪种本地窗口都需要和NativeWindowType保持一致,数据类型的定义:

/*frameworks/native/opengl/include/egl/Eglplatform.h*/
typedef EGLNativeWindowType  NativeWindowType;//注意这两种数据类型其实是一样的#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
 /* Win32 和 WinCE系统下的定义 */typedef HWND    EGLNativeWindowType;
#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian系统 */typedef void *EGLNativeWindowType;
#elif defined(__ANDROID__) || defined(ANDROID)/* Android系统 */
struct ANativeWindow;typedef struct ANativeWindow* EGLNativeWindowType;#elif defined(__unix__)/* UNIX系统 */typedef Window   EGLNativeWindowType;
#else
#error "Platform not recognized"
#endif

ANativeWindow定义:

/*system/core/include/system/Window.h*/
struct ANativeWindow
{const uint32_t flags; //与Surface或updater有关的属性
    const int   minSwapInterval;//所支持的最小交换间隔时间
    const int   maxSwapInterval;//所支持的最大交换间隔时间
    const float xdpi; //水平方向的密度,以dpi为单位
    const float ydpi;//垂直方向的密度,以dpi为单位
    intptr_t    oem[4];//为OEM定制驱动所保留的空间
    int (*setSwapInterval)(struct ANativeWindow* window, int interval);
    int (*dequeueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer** buffer, int* fenceFd);
    int (*queueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);
    int (*cancelBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);
    int (*query)(const struct ANativeWindow* window,int what, int* value);
    int (*perform)(struct ANativeWindow* window,int operation, ... );
    void* reserved_proc[2];
};

FramebufferNativeWindow构造函数

/*frameworks/native/libs/ui/FramebufferNativeWindow.cpp*/
FramebufferNativeWindow::FramebufferNativeWindow() 
    : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
    hw_module_t const* module;
    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {//加载模块
        int stride;
        int err;
        int i;
        err = framebuffer_open(module, &fbDev);
        err = gralloc_open(module, &grDev);//分别打开fb和grallocif(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
           fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){//根据fb设备属性获得buffer数
            mNumBuffers = fbDev->numFramebuffers;
        } else {
            mNumBuffers = MIN_NUM_FRAME_BUFFERS;//否则就采用最少的buffer数值,即2
        }
        mNumFreeBuffers = mNumBuffers;//可用的buffer个数,初始时是所有buffer可用
        mBufferHead = mNumBuffers-1;for (i = 0; i < mNumBuffers; i++) //给每个buffer初始化
        {
            buffers[i] = new NativeBuffer(fbDev->width, fbDev->height, fbDev->format,
                                  GRALLOC_USAGE _HW_FB);
        }//NativeBuffer是什么?

        for (i = 0; i < mNumBuffers; i++) //给每个buffer分配空间
        {
            err = grDev->alloc(grDev, fbDev->width, fbDev->height, fbDev->format,
                       GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);}
             /*为本地窗口赋属性值*/
        const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; 
        const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
        const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
        const_cast<int&>(ANativeWindow::minSwapInterval) =fbDev->minSwapInterval;
        const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval;
    } else {
        ALOGE("Couldn't get gralloc module");
    }
    /*以下代码段开始履行窗口“协议”*/
    ANativeWindow::setSwapInterval = setSwapInterval;
    ANativeWindow::dequeueBuffer = dequeueBuffer;
    ANativeWindow::queueBuffer = queueBuffer;
    ANativeWindow::query = query;
    ANativeWindow::perform = perform;
    /*下面这几个接口已经被废弃了,不过为了保持兼容性,暂时还是保留的*/
    ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED; 
}

重点关注的是FramebufferNativeWindow是如何分配buffer的。

成员变量mNumBuffers代表了FramebufferNativeWindow所管理的buffer总数。它取决于两个方面:首先从fb设备中取值,即numFramebuffers;否则就默认定义为MIN_NUM_FRAME_BUFFERS:

#define MIN_NUM_FRAME_BUFFERS  2
#define MAX_NUM_FRAME_BUFFERS  3

这代表双缓冲或者三缓冲机制。
FramebufferNativeWindow构造函数中的第一个for循环里先给各buffer创建相应的实例(new NativeBuffer),其中的属性值都来源于fbDev,如宽、高、格式等。紧随其后的就是调用Gralloc设备的alloc()方法:

err = grDev->alloc(grDev, fbDev->width, fbDev->height, fbDev->format,
GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);

所有申请到的缓冲区都需要由FramebufferNativeWindow中的全局变量buffers[MAX_NUM_FRAME_BUFFERS]来记录,每个数据元素是一个NativeBuffer:

class NativeBuffer : public ANativeObjectBase<ANativeWindowBuffer, 
       NativeBuffer,LightRefBase<NativeBuffer>>
{

NativeBuffer 继承自ANativeWindowBuffer:

typedef struct ANativeWindowBuffer
{int width; //宽
    int height;//高
    …
    buffer_handle_t handle;/*代表内存块的句柄,比如ashmem机制。*/} ANativeWindowBuffer_t;

一个本地窗口包含了很多属性值,如各种标志(flags)、横纵坐标的密度值等。这些数值都可以从fb设备中查询到,我们需要将它们赋予刚生成的FramebufferNativeWindow实例的属性。

FramebufferNativeWindow会将其对应的成员函数逐个填充到ANativeWindow的函数指针中:

ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;

这样的话OpenGL ES就通过ANativeWindow与本地窗口建立了联系。

dequeueBuffer

OpenGL ES就是通过这个方法来分配用于渲染的缓冲区的:

int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd)
{
    FramebufferNativeWindow* self = getSelf(window); /*Step1*/
    Mutex::Autolock _l(self->mutex);/*Step2*//*Step3. 计算mBufferHead */
    int index = self->mBufferHead++;
    if (self->mBufferHead >= self->mNumBuffers)
        self->mBufferHead = 0;//循环

    /*Step4. 如果当前没有可用缓冲区*/
    while (!self->mNumFreeBuffers) {
        self->mCondition.wait(self->mutex);
    }
    /*Step5. 如果有人释放了缓冲区*/
    self->mNumFreeBuffers--;
    self->mCurrentBufferIndex = index;
    *buffer = self->buffers[index].get();
    *fenceFd = -1;
    return 0;
}

Surface

应用程序端的本地窗口是Surface,和FramebufferNativeWindow一样,它必须继承AnativeWindow

class Surface
    : public ANativeObjectBase<ANativeWindow, Surface, RefBase>
/*frameworks/native/libs/gui/Surface.cpp*/
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer): mGraphicBufferPro duc  er(bufferProducer)
{/*给ANativeWindow中的函数指针赋值*/
    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;/*为各内部变量赋值,因为此时用户还没有发起申请,所以大部分变量的初始值是0*/
    mReqWidth = 0;
    mReqHeight = 0;
    …
    mDefaultWidth = 0;
    mDefaultHeight = 0;
    mUserWidth = 0;
    mUserHeight = 0;}

surface承担应用进程UI显示的责任。

Surface将通过mGraphicBufferProducer来获取buffer,而且这些缓冲区会被记录在mSlots数组中。

int Surface::dequeueBuffer(android_native_buffer_t** buffer,int *fenceFd) {…
    Mutex::Autolock lock(mMutex);
    int buf = -1;
    /*Step1. 宽高计算*/
    int reqW = mReqWidth ? mReqWidth : mUserWidth;
    int reqH = mReqHeight ? mReqHeight : mUserHeight;
    /*Step2. dequeueBuffer得到一个缓冲区*/
    sp<Fence> fence;
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
                           reqW, reqH, mReqFormat, mReqUsage);/*生产者发挥作用了*/ 
    …
sp<GraphicBuffer>&gbuf(mSlots[buf].buffer);/*注意buf是一个int值,
                                            代表的是mSlots数组序号*/if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);//申请空间}*buffer = gbuf.get();
}

摘自原书:

大致流程是:ViewRootImpl持有一个Java层的Surface对象(即mSurface),初始时是空的。后续ViewRootImpl将向WindowManagerService发起relayout请求,此时mSurface才被赋予真正有效的值。WindowManagerService会先让WindowStateAnimator生成一个SurfaceControl,然后通过Surface.copyFrom()函数将其复制到mSurface中。这个复制函数会通过native接口nativeCreateFrom SurfaceControl来生成本地Surface(C++)对象,具体是在android_view_Surface.cpp文件中。JNI函数nativeCreateFromSurfaceControl将从SurfaceControl中提取出Surface(C++),最终记录到Surface(Java)的成员变量中。这样,后期我们就可以从此变量中还原出底层的Surface对象了。

Surface由SurfaceControl管理,而后者又由SurfaceComposerClient创建。

/*frameworks/native/libs/gui/SurfaceComposerClient.cpp*/
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name,uint32_t w,
                                       uint32_t h,PixelFormat format,uint32_t flags)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        status_t err = mClient->createSurface(name, w, h, format, flags, &handle, &gbp);  
        //生成一个Surfaceif (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);//SurfaceControl是“本地”的对象
        }
    }
    return sur;
}

mClient是一个ISurfaceComposerClient的sp指针,通过它创建一个surface。

SurfaceControl对象并不是由ISurfaceComposerClient的createSurface直接生成的,这个函数的参数中包括了gbp,即前面所说的“buffer生产者”。

真正与SurfaceFlinger间有联系的应该就是gbp。

来看下ISurfaceComposerClient的服务端:

void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
    if (sm != 0) {
        sp<ISurfaceComposerClient> conn = sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

这里面与三个匿名Binder相关联,它们是由surfaceflinger提供的。surface flinger是注册在server manager上面的。

总结

FramebufferNativeWindow是为surfaceflinger服务的,由Gralloc提供。
surface虽然为应用程序服务的,但是本质上还是由surface flinger服务统一管理的。

参考

《深入理解Android内核设计思想》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/552799.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超平实版Pytorch CNN Conv2d

torch.nn.Conv2d 基本参数 in_channels (int) 输入的通道数量。比如一个2D的图片&#xff0c;由R、G、B三个通道的2D数据叠加。 out_channels (int) 输出的通道数量。 kernel_size (int or tuple) kernel&#xff08;也就是卷积核&#xff0c;也可…

selenium反反爬虫,隐藏selenium特征

一、stealth.min.js 使用 用selenium爬网页时&#xff0c;常常碰到被检测到selenium &#xff0c;会被服务器直接判定为非法访问&#xff0c;这个时候就可以用stealth.min.js 来隐藏selenium特征&#xff0c;达到绕过检测的目的 from selenium import webdriver from seleniu…

接口压力测试 jmeter--入门篇(一)

一 压力测试的目的 评估系统的能力识别系统的弱点&#xff1a;瓶颈/弱点检查系统的隐藏的问题检验系统的稳定性和可靠性 二 性能测试指标以及测算 【虚拟用户数】&#xff1a;线程用户【并发数】&#xff1a;指在某一时间&#xff0c;一定数量的虚拟用户同时对系统的某个功…

5.11 mybatis之returnInstanceForEmptyRow作用

文章目录 1. 当returnInstanceForEmptyRowtrue时2 当returnInstanceForEmptyRowfalse时 mybatis的settings配置中有个属性returnInstanceForEmptyRow&#xff0c;该属性新增于mybatis的3.4.2版本&#xff0c;低于此版本不可用。该属性的作用官方解释为&#xff1a;当返回行的所…

如何快速上手:springboot+mongodb

当使用 Java Spring Boot 与 MongoDB 时&#xff0c;可以使用 Spring Data MongoDB 来轻松地进行数据库操作。以下是一个简单的示例&#xff0c;演示如何在 Spring Boot 中使用 MongoDB 进行基本的 CRUD&#xff08;创建、读取、更新、删除&#xff09;操作。 Spring Data for …

代码随想录训练营day39

第九章 动态规划part02 1.LeetCode. 不同路径 1.1题目链接&#xff1a;62.不同路径 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;B站卡哥视频 1.2思路&#xff1a;采用动态规划算法 想要求dp[i][j]&#xff0c;只能有两个方向来推导出来&#xff0c;即dp[i - 1][…

论文解读:(CoOp)Learning to Prompt for Vision-Language Models

文章汇总 存在的问题 虽然训练类别通常具有文本形式&#xff0c;例如“金鱼”或“卫生纸”&#xff0c;但它们将被转换为离散标签&#xff0c;只是为了简化交叉熵损失的计算&#xff0c;从而使文本中的语义封装在很大程度上未被利用。这样的学习范式将视觉识别系统限制在闭集…

【QOpenGL实践】QOpenGLWidget

目录 一、说明 二、QOpenGLWidget 类对象 2.1 概要 2.1.1 函数功能 2.1. 2 三个虚函数 2.1.3 信号 2.2 详细说明 2.2.1 三个虚函数 2.2.2 绘画技巧 2.2.3 OpenGL 函数调用、标头和 QOpenGLFunctions 三、实现代码示例 3.1 最简模式 3.2 与 QGLWidget 的关系 3.3…

LeetCode———144—— 二叉树的前序遍历

目录 ​编辑 1.题目 2.解答 1.首先计算二叉树的节点个数&#xff1a; 2.以先序遍历&#xff08;Preorder Traversal&#xff09;的方式遍历一个二叉树&#xff0c;并将遍历到的节点的值存储在一个整数数组中 3.最终代码 1.题目 . - 力扣&#xff08;LeetCode&#xff09; 给…

123页|华为项目管理精华-成功的项目管理(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 华为项目管理精华 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT解决方案&#xff01;&a…

IDEA Tomcat localhost 日志和 catalina日志乱码(解决)

只需要修改 Tomcat 的 logging.properties D:\work\apache-tomcat-8.5.70-windows-x64\apache-tomcat-8.5.70\conf

SpringMVC 常用注解介绍

Spring MVC 常用注解介绍 文章目录 Spring MVC 常用注解介绍准备1. RequestMapping1.1 介绍2.2 注解使用 2. 请求参数2.1 传递单个参数2.2 传递多个参数2.3 传递对象2.4 传递数组 3. RequestParam3.1 注解使用3.2 传入集合 4. RequestBody5. PathVariable6. RequestPart7. Rest…

Since Maven 3.8.1 http repositories are blocked.

编译maven 项目时候报错提示下面信息&#xff1a; Since Maven 3.8.1 http repositories are blocked.Possible solutions: - Check that Maven settings.xml does not contain http repositories - Check that Maven pom files do not contain http repository http://XXXXXX:…

Mac电脑上有什么好玩的格斗游戏 《真人快打1》可以在苹果电脑上玩吗

你是不是喜欢玩格斗游戏&#xff1f;你是不是想在你的Mac电脑上体验一些刺激和激烈的对战&#xff1f;在这篇文章中&#xff0c;我们将介绍Mac电脑上有什么好玩的格斗游戏&#xff0c;以及《真人快打1》可以在苹果电脑上玩吗。 一、Mac电脑上有什么好玩的格斗游戏 格斗游戏是…

设计循环队列(队列oj)

1.设计循环队列 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。…

如何在原生项目中集成flutter

两个前提条件&#xff1a; 从flutter v1.17版本开始&#xff0c;flutter module仅支持AndroidX的应用在release模式下flutter仅支持一下架构&#xff1a;x84_64、armeabi-v7a、arm6f4-v8a,不支持mips和x86;所以引入flutter前需要在app/build.gradle下配置flutter支持的架构 a…

Jmeter 压测-Jprofiler定位接口相应时间长

1、环境准备 执行压测脚本&#xff0c;分析该接口tps很低&#xff0c;响应时间很长 高频接口在100ms以内&#xff0c;普通接口在200ms以内 2、JProfiler分析响应时间长的方法 ①JProfiler录制数据 压测脚本&#xff0c;执行1-3分钟即可 ②分析接口相应时间长的方法 通过Me…

全国产化无风扇嵌入式车载电脑在车队管理嵌入式车载行业应用

车队管理嵌入式车载行业应用 车队管理方案能有效解决车辆繁多管理困难问题&#xff0c;配合调度系统让命令更加精确有效执行。实时监控车辆状况、行驶路线和位置&#xff0c;指导驾驶员安全有序行驶&#xff0c;有效降低保险成本、事故概率以及轮胎和零部件的磨损与损坏。 方…

vue3第二十节(新增编译宏defineModel)

为什么会需要使用defineModel() 注意&#xff1a;defineModel() 需要在3.4及以上版本才可使用&#xff1b; 组件之间通讯&#xff0c;通过 props 和 emits 进行通讯,是单向数据流&#xff0c;比如&#xff1a;props是自上而下的&#xff08;父组件数据修改导致子组件更新&…

企业网站制作如何被百度收录

1、网站在百度中的整体评分 说俗点就是网站的权重&#xff0c;在优化过程中我们会见到很多网站出现秒收的情况&#xff0c;发布的文章几分钟就可以收录&#xff0c;这个通过SITE语法都可以去查询&#xff0c;那么这跟自己的网站权重以及内容更新习惯是有非常重要的关联。 我们…