BufferQueue研究
我们在工作的过程中,肯定听过分析卡顿或者冻屏问题的时候,定位到APP卡在dequeueBuffer方法里面,或者也听身边的同事老说3Buffer等信息。所以3Buffer是什么鬼?
什么是BufferQueue?
搞Android,你一定知道Graphic Buffer和 Buffer Queue, 你的笔记中肯定也有下面这张Graphic Buffer的状态迁移图。
系统中有两类Buffer Queue,如下图所示:
Layer背后的Buffer Queue
第一类,也是最为大家所熟知的,就是Layer背后的BufferQueue,用来连接App与SurfaceFlinger。App为Producer端,而 SurfaceFlinger 为 Consumer 端。
App 绘制时,先从 Buffer Queue 中 dequeue(调用 Producer 的 dequeueBuffer()函数)出来一块图形缓冲,绘制完成后,再把绘制好的图形缓冲 queue(调用 Producer 的 queueBuffer()函数)到 Buffer Queue 中,并通知 SurfaceFlinger来消费。SurfaceFlinger 收到通知后,从 Buffer Queue 中 acquire 一块绘制过的 Buffer,然后进行合成处理:要么进行 GPU合成,要么交给 HWC 去合成。
合成完成之后,这个块 Buffer 就恢复自由身,会被返回到 Buffer Queue 中(调用 Consumer 的 releaseBuffer()函数),以备下一次使用。
但是在Android S代码上面,谷歌对SurfaceFlinger的代码进行了重构,从个人理解是为了减少SF的负责,Android S开始强制App端创建BufferQueue,也就是强制Client端分配Buffer。
在Android S的代码中引入了一个BLASTBufferQueue.java(后面简称BBQ)这个类,ViewRootImpl.java在调用relayoutWindow函数的时候,会创建BBQ这个对象。
Surface getOrCreateBLASTSurface() {if (!mSurfaceControl.isValid()) {return null;}Surface ret = null;if (mBlastBufferQueue == null) {mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y,mWindowAttributes.format);// We only return the Surface the first time, as otherwise// it hasn't changed and there is no need to update.ret = mBlastBufferQueue.createSurface();} else {mBlastBufferQueue.update(mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y,mWindowAttributes.format);}return ret;
}
在BBQ对象初初始化的时候,会调用nativeCreate方法,BBQ对象会在构造方法中传入SurfaceControl对象,而这样就会和SurfaceFlinger创建了一个连接通道。SurfaceControl.java封装了很多Client调用的binder接口,而服务端是SurfaceFlinger。
通过nativeCreate本地方法,通过JNI(android_graphics_BLASTBufferQueue.cpp)的nativeCreate方法,创建了native层的BBQ。
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,jlong width, jlong height, jint format) {String8 str8;if (jName) {const jchar* str16 = env->GetStringCritical(jName, nullptr);if (str16) {str8 = String8(reinterpret_cast<const char16_t*>(str16), env->GetStringLength(jName));env->ReleaseStringCritical(jName, str16);str16 = nullptr;}}std::string name = str8.string();sp<BLASTBufferQueue> queue =new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,height, format);queue->incStrong((void*)nativeCreate);return reinterpret_cast<jlong>(queue.get());
}
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,int width, int height, int32_t format): mSurfaceControl(surface),mSize(width, height),mRequestedSize(mSize),mFormat(format),mNextTransaction(nullptr) {createBufferQueue(&mProducer, &mConsumer);// since the adapter is in the client process, set dequeue timeout// explicitly so that dequeueBuffer will blockmProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());// safe default, most producers are expected to override thismProducer->setMaxDequeuedBufferCount(2);mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,GraphicBuffer::USAGE_HW_COMPOSER |GraphicBuffer::USAGE_HW_TEXTURE,1, false);static int32_t id = 0;mName = name + "#" + std::to_string(id);auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(id);id++;mBufferItemConsumer->setName(String8(consumerName.c_str()));mBufferItemConsumer->setFrameAvailableListener(this);mBufferItemConsumer->setBufferFreedListener(this);mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));mBufferItemConsumer->setBlastBufferQueue(this);ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);mTransformHint = mSurfaceControl->getTransformHint();mBufferItemConsumer->setTransformHint(mTransformHint);SurfaceComposerClient::Transaction().setFlags(surface, layer_state_t::eEnableBackpressure,layer_state_t::eEnableBackpressure).setApplyToken(mApplyToken).apply();mNumAcquired = 0;mNumFrameAvailable = 0;BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width,height, format, mTransformHint);
}
从上面的代码中,createBufferQueue创建了BufferQueue,同时也创建了Graphic Buffer的生产者和消费者。其中有个代码mProducer -> setMaxDequeuedBufferCount(2),这个就和3Buffer有关系了,我们先整理下Buffer的运转过程,如图所示:
App的RenderThread 调用 Producer.dequeueBuffer()在BufferQueue中拿到一个空闲的Buffer。
App的RenderThread调用Producer.queueBuffer将绘制好的 Buffer 入列。注意,此时入列的 Buffer 可能还未绘制完成,即 GPU 可能还在进行绘制工作。
最终调用到 Procuder 的 Bn 端,即 SurfaceFliner 进程里的某个 Binder 线程里。在 Bn 端,会通过调用SurfaceFlinger的SetTransactionState方法,把当前的带有Buffer信息的State保存到一个TransactionQueue队列中。
当带有Buffer信息的Layer信息保存到队列中, 这个动作称作“上帧”。所以我么可以在 systrace 上看到该Layer待消费的 Buffer 数目+1。
而 Buffer Queue 的消费者就是 SurfaceFlinger,所以在下一个 Vsync信号到来后,在 SurfaceFlinger 的 handleMessageInvalidate()函数中,调用 acquireBuffer()去取 Buffer,取走之后,BufferQueue 中待消费的 Buffer 便减少一个。
因为有上帧,所以要重新进行合成,SurfaceFlinger 调用onMessageRefresh()函数去做合成,一般是 HWC 合成,直接把 Buffer 交给 HWC。合成完成后,在 postComposition()里,会调用binder接口进行通讯。
App端的binder收到消息后调用releaseBuffer()释放 Buffer,如 systrace 所示,这里释放的是上一帧的 Buffer。
上面图中7个步骤就是一个buffer详细的转运过程。
DisplayDevice 背后的Buffer Queue
第二类Buffer Queue是GPU合成特有的,一般在游戏APP渲染过程中会遇到,这个Buffer Queue隐藏在DisplayDevice之后,是在SurfaceFlinger为每个接入系统的显示屏创建DisplayDevice实例时创建的。
执行在SurfaceFlinger::processDisplayAdded函数中。
void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,const DisplayDeviceState& state) {......sp<compositionengine::DisplaySurface> displaySurface;sp<IGraphicBufferProducer> producer;sp<IGraphicBufferProducer> bqProducer;sp<IGraphicBufferConsumer> bqConsumer;getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);......
}
这个函数是为DisplaySurface创建BufferQueue, createBufferQueue函数是指向BufferQueue::createBufferQueue,传入的第三个参数 consumerIsSurfaceFlinger 为false,表示BufferQueue的消费者不是SurfaceFlinger。
void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,const DisplayDeviceState& state) {......if (state.isVirtual()) {const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());LOG_FATAL_IF(!displayId);auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,bqProducer, bqConsumer, state.displayName);displaySurface = surface;producer = std::move(surface);} else {ALOGE_IF(state.surface != nullptr,"adding a supported display, but rendering ""surface is provided (%p), ignoring it",state.surface.get());const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());LOG_FATAL_IF(!displayId);displaySurface =sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,state.physical->activeMode->getSize(),ui::Size(maxGraphicsWidth, maxGraphicsHeight));producer = bqProducer;}......
}
除了虚拟盘,主屏或者外屏采用FrameBufferSurface,继承自ConsumerBase,把BufferQueueConsumer封装到FrameBufferSurface里面。
Buffer共享
Buffer分配
Buffer同步:fence
后面三个点的内容还在整理中,等整理完毕,再同步到这章内容中。
相关文章:
BufferQueue研究
我们在工作的过程中,肯定听过分析卡顿或者冻屏问题的时候,定位到APP卡在dequeueBuffer方法里面,或者也听身边的同事老说3Buffer等信息。所以3Buffer是什么鬼?什么是BufferQueue?搞Android,你一定知道Graphic Buffer和…...
【计组笔记08】计算机组成与原理之IO设备系统(输入、输出设备、外存储器)
这篇文章,主要介绍计算机组成与原理之IO设备系统(输入、输出设备、外存储器)。 目录 一、IO设备系统 1.1、IO系统的演变 (1)早期阶段 (2)接口模块和DMA阶段...
使用Vue实现数据可视化大屏功能(一)
导语 现在在很多的工程项目中,都有有关于数据大屏相关的监控内容,这里我们就来看一下如何用Vue来搭建一个数据可视化大屏应用。 创建项目 使用WebStorm工具创建一个Vue的项目。如下图所示,配置好vue的脚手架工具和nodejs的运行环境&#…...
华为OD机试真题Python实现【整数对最小和】真题+解题思路+代码(20222023)
整数对最小和 题目 给定两个整数数组 array1 array2 数组元素按升序排列 假设从array1 array2中分别取出一个元素可构成一对元素 现在需要取出K个元素 并对取出的所有元素求和 计算和的最小值 注意: 两对元素如果对应于array1 array2中的两个下标均相同,则视为同一个元素 �…...
2023年绿色建筑国际会议(ICoGB 2023)
2023年绿色建筑国际会议(ICoGB 2023) 重要信息 会议网址:www.icogb.org 会议时间:2023年5月19-21日 召开地点:斯德哥尔摩 截稿时间:2023年4月1日 录用通知:投稿后2周内 收录检索ÿ…...
【力扣1653】使字符串平衡的最少删除次数
给你一个字符串 s ,它仅包含字符 a 和 b 。你可以删除 s 中任意数目的字符,使得 s 平衡 。当不存在下标对 (i,j) 满足 i < j ,且 s[i] b 的同时 s[j] a ,此时认为 s 是 平衡 的。请你返回使 s 平衡 的 最少 删除次数。…...
链表的中间结点与链表的倒数第k个结点(精美图示详解哦)
全文目录引言链表的中间结点题目描述与思路实现链表的倒数第k个结点题目描述与思路实现总结引言 在上一篇文章中,介绍了反转链表 我们利用了链表是逻辑连续的特点,逆置了链表的逻辑连接顺序,从而实现反转链表: 戳我查看反转链表详…...
防静电监控仪可以检测现场设备是否和实际大地接触
随着电子产品集成化度越来越高,对于电子产品装配来说,静电的危害严重影响到产品的质量、成品率和可靠性, 必须对用于电子产品装配的净化间进行系统防静电措施,将生产过程中的静电危害程度降至最低。近年来电子企业对ESD的危害的深入认识&…...
计算机网络第八版——第二章课后题答案(超详细)
第二章 该答案为博主在网络上整理,排版不易,希望大家多多点赞支持。后续将会持续更新(可以给博主点个关注~ 第一章 答案 【2-01】物理层要解决哪些问题?物理层的主要特点是什么? 解答:物理层考虑的是怎…...
2023年3月全国DAMA-CDGA/CDGP数据管理认证火热报名中...
弘博创新是DAMA中国授权的数据治理人才培养基地,贴合市场需求定制教学体系,采用行业资深名师授课,理论与实践案例相结合,快速全面提升个人/企业数据治理专业知识与实践经验,通过考试还能获得数据专业领域证书。 DAMA认…...
查询与进程调度(CFS)相关信息
目录 查询与进程相关的调度信息 查看CFS调度信息 CPU相关的信息 CFS就绪队列的总运行时间 实时队列与deadline调度的相关信息 所有进程相关的信息 查询与进程相关的调度信息 进程的nice值,优先级,调度策略,vruntime等信息。在proc目录下…...
07对MVC的理解
MVC是一种设计模式,用于将应用程序的不同方面分离开来,以便更容易地管理和维护应用程序。MVC代表模型-视图-控制器,它将应用程序分为三个主要组件:模型(Model):负责管理应用程序的数据和业务逻辑…...
WebSocket与Socket、TCP、HTTP的关系
目录:1、名词解析;2、WebSocket简介与原理;3、WebSocket和Http的关系和异同点;4、WebSocket与Socket的区别;5、Socket和TCP/IP;6、一个应用程序的通信链路;1、基础名词解析:…...
音频基础知识简述 esp-sr 上手指南
此篇博客先对音频基础知识进行简要叙述,然后帮助读者入门 esp-sr SDK。 1 音频的基本概念 1.1 声音的本质 声音的本质是波在介质中的传播现象,声波的本质是一种波,是一种物理量。 两者不一样,声音是一种抽象的,是声…...
Flex弹性布局一文通【最全Flex教学】
文章目录一.Flex布局1.1 传统布局和flex布局1.1.1 传统布局1.1.2 flex弹性布局1.2 flex初步体验1.3 布局原理二.常见Flex属性2.1 常见父项属性2.2 flex-direction主轴的方向2.3 justify-content设置主轴上的子元素排列方式2.4 设置子元素是否flex-wrap换行2.5 align-itmes设置侧…...
Navicat使用教程
Navicat:一个可以对别人的数据库进行操作的软件(需要与如mysql等数据库配套使用) 1. 下载mysql MySQL :: Download MySQL Community Server (Archived Versions) 下载上面那个版本 下载下来是个压缩包,解压 2.配置mysql (1)在…...
35岁测试人该何去何从?10年工作经验的我,只不过是一年的工作经验用了10年......
如果到了这个年龄,还是初级测试,或者只会一些简单的自动化测试,那么真的是不好干了。 35的年龄,企业对员工是有另一层面的考量。 简单来说,就是年龄上去了,能力也要上去,要么是技术专家&#…...
SpringBoot 项目中集成 Prometheus 和 Grafana
项目上线后,除了能保障正常运行以外,也需要服务运行的各个指标进行监控,例如 服务器CPU、内存使用占比,Full GC 执行时间等,针对一些指标出现异常,可以加入一些报警机制能及时反馈给开发运维。这样…...
红队APT——反朔源流量加密CSMSF证书指纹C2项目CDN域前置
目录 0x01 背景交代 0x02 常见红蓝对抗中红队面临问题 0x03 蓝队发现处置情况...
Linux环境下实现并详细分析c/cpp线程池(附源码)
一、线程池原理 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 线程池是一种多线程处理形式,处理过程中将任务添加到…...
移动web(三)
her~~llo,我是你们的好朋友Lyle,是名梦想成为计算机大佬的男人! 博客是为了记录自我的学习历程,加强记忆方便复习,如有不足之处还望多多包涵!非常欢迎大家的批评指正。 媒体查询 目标:能够根据…...
macbook怎么运行exe文件 mac打开exe文件的三大方法
exe文件是Windows系统的可执行文件,虽然Mac系统上无法直接打开exe文件,但是你可以在Mac电脑上安装双系统或者虚拟机来实现mac电脑上运行exe文件。除了这两种方法之外,你还可以在Mac电脑上使用类虚拟机软件打开exe文件,这三种方法各…...
GoldenGate(OGG)高可用XAG部署
前言: 本文档主要描述通过Oracle Grid Infrastructure Agents (XAG)基于Oracle RAC实现GoldenGate(OGG)软件高可用的实施操作 环境信息: 源端 目标端 节点一IP 节点二IP 192.168.1.84 192.168.1.86 节点一IP 节点二IP 192.168.1.200 192.168.1.210 VIP 192.…...
如何使用Docker容器部署O2OA(翱途)开发平台与OnlyOffice的集成版本?
O2OA(翱途)开发平台[下称O2OA平台或者O2OA]默认可以和OnlyOffice进行集成来实现在线文档编辑以及流程集成。开发者可以直接安装O2OA官网的OnlyOfficeO2Server的Docker版本用于体验。本文将详细介绍如何安装O2OA OnlyOffice的Docker版本。OnlyOffice Docs Sever可以单独安装,O2…...
springboot复习(黑马)(持续更新)
学习目标基于SpringBoot框架的程序开发步骤熟练使用SpringBoot配置信息修改服务器配置基于SpringBoot的完成SSM整合项目开发一、SpringBoot简介1. 入门案例问题导入SpringMVC的HelloWord程序大家还记得吗?SpringBoot是由Pivotal团队提供的全新框架,其设计…...
K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示
K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RCHX711称重模块1.2、STM32F103C8T6HX711称重模块五、基础知识学习与相关资料下载六、视频效果展…...
单例模式之饿汉式
目录 1 单例模式的程序结构 2 饿汉式单例模式的实现 3 饿汉式线程安全 4 防止反射破坏单例 5 总结 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。所谓单例就是在系统中只有一个该类的实例,并且提供一个访问该实例的全局…...
软件测试培训三个月,找到工作了11K,面试总结分享给大家
功能方面:问的最多的就是测试流程,测试计划包含哪些内容,公司人员配置,有bug开发认为不是 bug怎么处理,怎样才算是好的用例,测试用例设计方法(等价类,边界值等概念方法)&…...
Hbase备份与恢复工具Snapshot的基本概念与工作原理
数据库都有相对完善的备份与恢复功能。备份与恢复功能是数据库在数据意外丢失、损坏下的最后一根救命稻草。数据库定期备份、定期演练恢复是当下很多重要业务都在慢慢接受的最佳实践,也是数据库管理者推荐的一种管理规范。HBase数据库最核心的备份与恢复工具——Sna…...
RTOS中事件集的实现原理以及实用应用
事件集的原理 RTOS中事件集的实现原理是通过位掩码来实现的。事件集是一种用于在任务之间传递信号的机制。在RTOS中,事件集通常是一个32位的二进制位向量。每个位都代表一个特定的事件,例如信号、标志、定时器等。 当一个任务等待一个或多个事件时&…...
iis7搭建网站织梦/淘宝指数网址
一、选择交换机的主要技能指标 交换机:交换机(Switch)意为“开关”是一种用于电(光)信号转发的网络设备。它可以为接入交换机的任意两个网络节点提供独享的电信号通路。最常见的交换机是以太网交换机。其他常见的还有电…...
网站开发还是做数据库开发/接单平台app
介绍通常情况下,在主机和VNX存储系统物理连接正常后,新安装的主机都要到存储系统上完成注册。注册过程中会将主机IP地址、主机名称和操作系统信息发送到存储系统,以方便存储系统对主机的日后管理。目前有三种常见主机注册方法:手动…...
wordpress wp2pcs sy/关键词怎么做快速的有排名
Kotlin中的序列一、前言二、filter、map、flatMap、Sequence一、前言 Kotlin里面的集合式api和Java类似,但也有区别,Kotlin里面加入了可变和不可变的特性,例如可变集合MutableList,不可变的则是List,这部分的功能主要…...
php网站开发师条件/重庆人力资源和社会保障网官网
Akaxin是一款开源免费的私有聊天软件,可以部署在任意服务器上,搭建自己的聊天服务器,供自己与朋友、用户使用。githubhttps://github.com/daychat/openzaly特性:单聊、群聊(含文字、图片、语音等)端到端的加密消息(服务端不可解密…...
服务器搭建网站能ping t/抖音seo招商
题目链接 数据过大,需要用字符串处理数据。 再一位一位进行除法,注意余数。 #include <stdio.h> int main(){char a[1001];int h[1001];int b, c, d,e,f,g;scanf("%s %d", a, &b);d 0;for (c 0; a[c] ! \0; c) {d ((a[c] - 48) …...
做网站合肥/成人厨师短期培训班
requestAnimationFrame window.requestAnimationFrame() 方法跟 setTimeout 类似,都是推迟某个函数的执行。不同之处在于,setTimeout 必须指定推迟的时间; window.requestAnimationFrame() 则是推迟到浏览器下一次重流时执行,执行完才会进行…...