该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录
说明:
在SurfaceFlinger中理解BufferQueue 相关内容对于图形渲染和显示非常重要:
总之,理解这些概念是 Android 图形渲染和显示的关键,尤其是在处理图形性能和显示质量时。接下来根据上面的基本概念我们主要研究以下内容:
研究BufferQueue,主要涉及的几个关键文件如下:
本文主要针对BufferQueue的原理进行解读,关于流程的解读会在后面篇章中进行详细说明。
BufferQueue的状态定义在文件AOSP/frameworks/native/libs/gui/BufferSlot.cpp中, 结构体 struct BufferSlot中定义为BufferState,实现如下:
enum BufferState {
FREE = 0,
DEQUEUED = 1,
QUEUED = 2,
ACQUIRED = 3
};
这段代码定义了一个名为BufferState的枚举类型,用于表示图形缓冲区的不同状态。这些状态通常在 Android 的 SurfaceFlinger 系统中使用,用于协调图形数据的生产者和消费者之间的操作。每个状态解释如下:
这些状态用于确保生产者和消费者之间对图形缓冲区的访问是协调的,以防止竞态条件和不一致性。这是 Android 图形系统中重要的一部分,用于实现高性能的图形渲染和显示。
应用端为生产者,执行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 可以在三种不同的模式下运行:
为了执行这项工作的大部分环节,SurfaceFlinger 就像另一个 OpenGL ES 客户端一样工作。例如,当 SurfaceFlinger 正在积极地将一个缓冲区或两个缓冲区合成到第三个缓冲区中时,它使用的是 OpenGL ES。
接下来我们专注于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 mProducer;
sp 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 producer;
sp consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
sp 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 serviceManager = defaultServiceManager();
sp binderProducer =
serviceManager->getService(PRODUCER_NAME);
mProducer = interface_cast(binderProducer);
EXPECT_TRUE(mProducer != NULL);
sp binderConsumer =
serviceManager->getService(CONSUMER_NAME);
mConsumer = interface_cast(binderConsumer);
EXPECT_TRUE(mConsumer != NULL);
sp 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;
sp 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(&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(&dataOut)));
ASSERT_EQ(*dataOut, 0x12345678);
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
}
//...
} // namespace android
从这里也可以看到生产者和消费者模式在代码中的实际使用case,先是生产者执行了dequeuebuffer操作->requestbuffer操作->对buffer进行填充渲染;消费者执行acquire操作,之后验证缓冲区内容是否和之前填充的内容一致。而在实际的android源码中,BufferQueue的生产者在一个进程中,完成渲染和填充后,通过binder通信通知消费者的进程获取Buffer并通过HWC去合成绘制到屏幕上。
在 Android 的 BufferQueue 中,Fence(也称为同步信号或屏障)用于确保图形缓冲区的正确同步和顺序处理。Fence主要用于表示某个操作的完成状态或某个资源的可用性状态。在图形缓冲区的上下文中,Fence 主要用于跟踪图形数据的生产和消费的状态。例如,一个Fence可以表示图形缓冲区的填充已经完成,或者表示图形缓冲区的内容已经准备好用于显示。Fence的应用如下:
@1 Fence在生产者与消费者之间的同步:
这种同步机制有助于防止竞态条件和不一致性,确保图形数据的正确传递和显示。
@2 Fence 的等待和触发:
@3 时间戳和缓冲区的正确顺序:
总之,Fence 在 Android 的 BufferQueue 中用于确保图形数据的正确同步、顺序处理和及时呈现。它是异步图形处理中的重要工具,有助于防止竞态条件和确保图形帧的正确显示。在实际开发中,开发者通常会与 Fence 交互,以确保图形数据的正确传递和显示。
在SurfaceFlinger中,BufferSlot 类中的定义了2个fence相关成员变量:mEglFence 和 mFence ,两者都是用于同步和管理图形缓冲区状态的属性,但它们在具体的分工上有一些不同: