Android SurfaceFlinger导读(04)理解BufferQueue

2023-10-07 08:53

本文主要是介绍Android SurfaceFlinger导读(04)理解BufferQueue,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录

说明:

  • 关于导读:导读部分主要是方便初学者理解SurfaceFlinger代码中的机制,为后面分析代码打下一个更好的基础,这样就可以把更多的精力放在surfaceFlinger的业务逻辑分析上。
  • 关于代码分支:以下代码分析均在android5.1.1_r3分支上 目录frameworks/native/services/surfaceflinger为root目录

在SurfaceFlinger中理解BufferQueue 相关内容对于图形渲染和显示非常重要:

  • BufferQueue 的作用:BufferQueue 是 SurfaceFlinger 的一部分,用于协调生产者(通常是应用程序或图形渲染引擎)和消费者(通常是显示系统)之间的图形数据传递。它允许生产者将渲染好的图形数据排队以供消费者显示,并确保在异步图形处理过程中进行同步和缓冲区的管理。
  • 图形缓冲区(Buffers):BufferQueue 管理图形缓冲区,这些缓冲区可以包含图像数据、帧数据或其他图形数据。了解如何创建、填充、排队和释放这些缓冲区是非常重要的,因为它们是图形渲染和显示的核心。
  • BufferQueue 的状态:BufferQueue 可以处于不同的状态,如 FREE、DEQUEUED、QUEUED 和 ACQUIRED。这些状态用于表示图形缓冲区的可用性和所有权,以确保生产者和消费者之间的正确同步和数据传递。
  • BufferQueueProducer 和 BufferQueueConsumer:生产者通过 BufferQueueProducer 接口与 BufferQueue 交互,而消费者通过 BufferQueueConsumer 接口与之交互。了解这两个接口以及它们的用法对于理解SurfaceFlinger 如何工作至关重要。
  • 同步和时间戳:了解如何使用同步信号(fence)以及如何处理时间戳(timestamp)对于确保图形帧在正确时间显示非常重要。这些功能帮助确保图形帧的正确顺序和时间戳。
  • BufferQueue 的线程处理:SurfaceFlinger 使用线程来管理和处理图形缓冲区。理解这些线程的角色和工作方式对于理解图形渲染和显示的异步性质非常关键。
  • 硬件加速和图形合成:SurfaceFlinger 可以与硬件加速图形合成引擎一起工作,以实现高性能的图形渲染。了解如何与硬件合成引擎交互以及如何配置硬件加速也很重要。

总之,理解这些概念是 Android 图形渲染和显示的关键,尤其是在处理图形性能和显示质量时。接下来根据上面的基本概念我们主要研究以下内容:

  • BufferQueue的4种状态详细解读。
  • BufferQueue的生产者消费者模型。
  • BufferQueue中的fence同步。

研究BufferQueue,主要涉及的几个关键文件如下:

  • AOSP/frameworks/native/libs/gui/BufferQueueProducer.cpp
  • AOSP/frameworks/native/libs/gui/BufferQueueCore.cpp
  • AOSP/frameworks/native/libs/gui/BufferQueueConsumer.cpp
  • AOSP/frameworks/native/libs/gui/BufferQueue.cpp
  • AOSP/frameworks/native/libs/gui/BufferSlot.cpp
  • AOSP/frameworks/native/include/gui/BufferSlot.h
  • AOSP/frameworks/native/include/gui/BufferQueueProducer.h
  • AOSP/frameworks/native/include/gui/BufferQueueCore.h
  • AOSP/frameworks/native/include/gui/BufferQueueConsumer.h
  • AOSP/frameworks/native/include/gui/BufferQueue.h
  • AOSP/frameworks/native/include/gui/BufferQueueDefs.h

本文主要针对BufferQueue的原理进行解读,关于流程的解读会在后面篇章中进行详细说明。

1 BufferQueue的4种状态解读

BufferQueue的状态定义在文件AOSP/frameworks/native/libs/gui/BufferSlot.cpp中, 结构体 struct BufferSlot中定义为BufferState,实现如下:

enum BufferState {FREE = 0,DEQUEUED = 1,QUEUED = 2,ACQUIRED = 3};

这段代码定义了一个名为BufferState的枚举类型,用于表示图形缓冲区的不同状态。这些状态通常在 Android 的 SurfaceFlinger 系统中使用,用于协调图形数据的生产者和消费者之间的操作。每个状态解释如下:

  • FREE(自由):表示缓冲区是可供生产者(dequeue)使用的状态。在此状态下,缓冲区可能在有限的时间内由消费者使用,因此在相关联的"ready fence"(准备就绪标志)被触发之前,不得修改缓冲区。这个状态的槽位(slot)由BufferQueue拥有,当调用dequeueBuffer时,它会转换为DEQUEUED状态。
  • DEQUEUED(已出队):表示生产者已经从队列中取出了缓冲区,但尚未将其重新排入队列或取消。在此状态下,生产者可以在相关的"ready fence"被触发后立即修改缓冲区内容。这个状态的槽位仍然由生产者拥有,可以通过queueBuffer转换为QUEUED状态,或通过cancelBuffer转换回FREE状态。
  • QUEUED(已排队):表示生产者已经填充了缓冲区并将其排队以供消费者使用。在此状态下,缓冲区内容可能在有限时间内继续被修改,因此在相关的"ready fence"被触发之前,不得访问缓冲区内容。这个状态的槽位由BufferQueue拥有,可以通过acquireBuffer转换为ACQUIRED状态,或者在异步模式下通过排队另一个缓冲区转换回FREE状态。
  • ACQUIRED(已获取):表示消费者已经获取了缓冲区。与QUEUED状态一样,消费者在相关的"ready fence"被触发之前不得访问缓冲区内容。这个状态的槽位由消费者拥有,当调用releaseBuffer时,它会转换回FREE状态。

这些状态用于确保生产者和消费者之间对图形缓冲区的访问是协调的,以防止竞态条件和不一致性。这是 Android 图形系统中重要的一部分,用于实现高性能的图形渲染和显示。

2 BufferQueue的生产者/消费者模型

2.1 生产者-消费者流程

应用端为生产者,执行DequeueBuffer操作,然后填充Buffer,完成后执行QueueBuffer操作;SurfaceFlinger为消费者,执行AcquireBuffer,接下来调用HWC来合成绘制Buffer。完成后执行ReleaseBuffer。整体流程如下图所示:

同时,这里BufferQueue的状态变化为:FREE->DEQUEUED->QUEUED->ACQUIRED->FREE。

BufferQueue的通信过程如下(以下部分源自官网翻译)

BufferQueues 是 Android 图形组件之间的粘合剂。它们是一对队列,可以调解缓冲区从生产方到消耗方的固定周期。一旦生产方移交其缓冲区,SurfaceFlinger 便会负责将所有内容合成到显示部分。

BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。图像生产方的一些示例包括由相机 HAL 或 OpenGL ES 游戏生成的相机预览。图像消耗方的一些示例包括 SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。

其中,BufferQueue 可以在三种不同的模式下运行:

  • 类同步模式 - 默认情况下,BufferQueue 在类同步模式下运行,在该模式下,从生产方进入的每个缓冲区都在消耗方那退出。在此模式下不会舍弃任何缓冲区。如果生产方速度太快,创建缓冲区的速度比消耗缓冲区的速度更快,它将阻塞并等待可用的缓冲区。
  • 非阻塞模式 - BufferQueue 还可以在非阻塞模式下运行,在此类情况下,它会生成错误,而不是等待缓冲区。在此模式下也不会舍弃缓冲区。这有助于避免可能不了解图形框架的复杂依赖项的应用软件出现潜在死锁现象。
  • 舍弃模式 - 最后,BufferQueue 可以配置为丢弃旧缓冲区,而不是生成错误或进行等待。例如,如果对纹理视图执行 GL 渲染并尽快绘制,则必须丢弃缓冲区。

为了执行这项工作的大部分环节,SurfaceFlinger 就像另一个 OpenGL ES 客户端一样工作。例如,当 SurfaceFlinger 正在积极地将一个缓冲区或两个缓冲区合成到第三个缓冲区中时,它使用的是 OpenGL ES。

2.2 测试源码解读,深入理解生产者-消费者模式

接下来我们专注于BufferQueue代码是如何使用的,先看原生代码中给出的测试程序。在文件AOSP/frameworks/native/libs/gui/tests/BufferQueue_test.cpp,这里抽取了其中给一个有代表性的测试用例,对应代码如下:


class BufferQueueTest : public ::testing::Test {public:
protected:BufferQueueTest() {const ::testing::TestInfo* const testInfo =::testing::UnitTest::GetInstance()->current_test_info();ALOGV("Begin test: %s.%s", testInfo->test_case_name(),testInfo->name());}~BufferQueueTest() {const ::testing::TestInfo* const testInfo =::testing::UnitTest::GetInstance()->current_test_info();ALOGV("End test:   %s.%s", testInfo->test_case_name(),testInfo->name());}void GetMinUndequeuedBufferCount(int* bufferCount) {ASSERT_TRUE(bufferCount != NULL);ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,bufferCount));ASSERT_GE(*bufferCount, 0);}void createBufferQueue() {BufferQueue::createBufferQueue(&mProducer, &mConsumer);}sp<IGraphicBufferProducer> mProducer;sp<IGraphicBufferConsumer> mConsumer;
};struct DummyConsumer : public BnConsumerListener {virtual void onFrameAvailable() {}virtual void onBuffersReleased() {}virtual void onSidebandStreamChanged() {}
};// XXX: Tests that fork a process to hold the BufferQueue must run before tests
// that use a local BufferQueue, or else Binder will get unhappy
TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {const String16 PRODUCER_NAME = String16("BQTestProducer");const String16 CONSUMER_NAME = String16("BQTestConsumer");pid_t forkPid = fork();ASSERT_NE(forkPid, -1);if (forkPid == 0) {//子进程sp<IGraphicBufferProducer> producer;sp<IGraphicBufferConsumer> consumer;BufferQueue::createBufferQueue(&producer, &consumer);sp<IServiceManager> serviceManager = defaultServiceManager();serviceManager->addService(PRODUCER_NAME, producer->asBinder());serviceManager->addService(CONSUMER_NAME, consumer->asBinder());ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();LOG_ALWAYS_FATAL("Shouldn't be here");}//父进程sp<IServiceManager> serviceManager = defaultServiceManager();sp<IBinder> binderProducer =serviceManager->getService(PRODUCER_NAME);mProducer = interface_cast<IGraphicBufferProducer>(binderProducer);EXPECT_TRUE(mProducer != NULL);sp<IBinder> binderConsumer =serviceManager->getService(CONSUMER_NAME);mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);EXPECT_TRUE(mConsumer != NULL);sp<DummyConsumer> dc(new DummyConsumer);ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));IGraphicBufferProducer::QueueBufferOutput output;ASSERT_EQ(OK,mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));int slot;sp<Fence> fence;sp<GraphicBuffer> buffer;//生产者 dequeueBuffer操作ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,GRALLOC_USAGE_SW_WRITE_OFTEN));//生产者 requestBuffer操作ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));//生产者填充Buffer缓冲区uint32_t* dataIn;ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,reinterpret_cast<void**>(&dataIn)));*dataIn = 0x12345678;ASSERT_EQ(OK, buffer->unlock());IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1),NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE);//生产者 queueBuffer操作ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));IGraphicBufferConsumer::BufferItem item;//消费者 acquireBuffer操作ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));//消费者 缓冲区数据验证操作,看上屏的Buffer内容和之前渲染的Buffer内容是否一致uint32_t* dataOut;ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,reinterpret_cast<void**>(&dataOut)));ASSERT_EQ(*dataOut, 0x12345678);ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
}
//...
} // namespace android

从这里也可以看到生产者和消费者模式在代码中的实际使用case,先是生产者执行了dequeuebuffer操作->requestbuffer操作->对buffer进行填充渲染;消费者执行acquire操作,之后验证缓冲区内容是否和之前填充的内容一致。而在实际的android源码中,BufferQueue的生产者在一个进程中,完成渲染和填充后,通过binder通信通知消费者的进程获取Buffer并通过HWC去合成绘制到屏幕上。

3 BufferQueue的Fence机制

3.1 理解Fence机制

在 Android 的 BufferQueue 中,Fence(也称为同步信号或屏障)用于确保图形缓冲区的正确同步和顺序处理。Fence主要用于表示某个操作的完成状态或某个资源的可用性状态。在图形缓冲区的上下文中,Fence 主要用于跟踪图形数据的生产和消费的状态。例如,一个Fence可以表示图形缓冲区的填充已经完成,或者表示图形缓冲区的内容已经准备好用于显示。Fence的应用如下:

@1 Fence在生产者与消费者之间的同步:

  • 在异步图形处理中,生产者(例如应用程序或图形渲染引擎)通常会填充图形缓冲区,并使用 Fence 来表示缓冲区是否已经填充完毕。
  • 消费者(例如显示系统)使用 Fence 来等待缓冲区的就绪状态,确保不会在缓冲区尚未填充完毕时访问其内容。

这种同步机制有助于防止竞态条件和不一致性,确保图形数据的正确传递和显示。

@2 Fence 的等待和触发:

  •  消费者可以使用 Fence 来等待某个操作的完成,通过等待操作的 Fence 变为“已触发”状态。
  •  生产者可以触发 Fence,表示相关操作已经完成,Fence 变为“已触发”状态,从而通知消费者可以安全地使用相关资源或数据。
  • Fence 通常与系统的同步机制一起使用,例如 sync_wait(等待 Fence 触发)和 sync_merge(合并多个 Fence)等函数。

@3 时间戳和缓冲区的正确顺序:

  • Fence 通常伴随着时间戳一起使用,以确保图形缓冲区的正确顺序。时间戳可以用于指示每个图形帧的呈现时间。
  • 使用时间戳和 Fence,消费者可以按照正确的顺序呈现图形帧,从而实现平滑的图形显示。

总之,Fence 在 Android 的 BufferQueue 中用于确保图形数据的正确同步、顺序处理和及时呈现。它是异步图形处理中的重要工具,有助于防止竞态条件和确保图形帧的正确显示。在实际开发中,开发者通常会与 Fence 交互,以确保图形数据的正确传递和显示。

3.2 BufferSlot中的mEglFence和mFence 解读

在SurfaceFlinger中,BufferSlot 类中的定义了2个fence相关成员变量:mEglFence 和 mFence ,两者都是用于同步和管理图形缓冲区状态的属性,但它们在具体的分工上有一些不同:

  • mEglFence:mEglFence是一个 EGL同步对象。它用于表示 GPU是否已经完成了与图形缓冲区相关的渲染操作。当 GPU渲染操作完成时,它会信号通知到EGLFence。EglFence主要用于同步GPU的图形渲染操作,确保 GPU已经完成了渲染,以确保渲染的完整性。
  • mFence:mFence用于SurfaceFlinger内部的图形合成和显示操作。它用于表示CPU和GPU之间的同步,确保图形缓冲区中的图形数据在合成和显示之前正确准备和完成。mFence通常用于 SurfaceFlinger的图形合成和显示管道,以确保图形数据的正确顺序和同步。

这篇关于Android SurfaceFlinger导读(04)理解BufferQueue的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是