用來描述Android應用程序的UI元數據的SharedClient
是如何創(chuàng)建的茅诱?
實際在近期版本的Android 源碼中邑闺,已經移除了SharedClient
季俩,只存在于早期版本中栅组,甚至4.4中雀瓢,該類都已經不復存在。
//http://aosp.opersys.com/xref/android-2.3_r1/xref/frameworks/base/include/private/surfaceflinger/SharedBufferStack.h#127
class SharedClient
{
public:
SharedClient();
~SharedClient();
status_t validate(size_t token) const;
private:
friend class SharedBufferBase;
friend class SharedBufferClient;
friend class SharedBufferServer;
// FIXME: this should be replaced by a lock-less primitive
Mutex lock;
Condition cv;
SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];
};
SharedClient
在早期版本中玉掸,是做什么的呢刃麸?
Android
應用和SurfaceFlinger
服務是不同的進程,它們通過binder通訊司浪。但是binder有一個問題泊业,無法傳遞大數據(限制在1M-8k,即1016K)断傲,一個Android應用存在多個窗口脱吱,數據量比較大,因此使用Binder的話不合適认罩。于是選擇了使用Android匿名共享內存方案箱蝠。
在每一個Android
應用與SurfaceFlinger
之間的連接上,加上一塊用來傳遞UI元數據的匿名共享內存垦垂,這塊區(qū)域被包裝成SharedClient
宦搬。
可以看出,
SharedClient
是用來解決Android
應用與SurfaceFlinger
服務之間的數據傳輸問題的劫拗。
所以這個問題也可以換個說法间校,SharedClient
被廢棄后,Android
系統(tǒng)是如何在Android
應用和SurfaceFlinger
服務之間傳輸數據的页慷?是如何通訊的呢憔足?
帶著這個問題接著來分析一下SurfaceFlinger
源碼胁附。
官方文檔中有張圖。
涉及
Metadata (元數據)滓彰, WindowManager控妻, SurfaceFlinger,Buffer Data 揭绑, surface.cpp弓候,GLConsumer.cpp ,IGraphicBufferProducer
等他匪。
出處:http://www.reibang.com/p/f96ab6646ae3
App到SurfaceFlinger
應用首先通過Surface的接口向SurfaceFlinger申請一塊buffer, 需要留意的是Surface剛創(chuàng)建時是不會有buffer被alloc出來的菇存,只有應用第一次要繪制畫面時dequeueBuffer會讓SurfaceFlinger去alloc buffer, 在應用側會通過importBuffer把這塊內存映射到應用的進程空間來。
之后App通過dequeueBuffer拿到畫布邦蜜, 通過queueBuffer來提交繪制好的數據依鸥。
APP繪畫的畫布是由SurfaceFlinger提供的,而畫布是一塊共享內存畦徘,APP向SurfaceFlinger申請到畫布毕籽,是將共享內存的地址映射到自身進程空間。 App負責在畫布上作畫井辆,畫完的作品提交給SurfaceFlinger, 這個提交操作并不是把內存復制一份給SurfaceFlinger溶握,而是把共享內存的控制權交還給SurfaceFlinger杯缺, SurfaceFlinger把拿來的多個應用的共享內存再送給HWC Service去合成, HWC Service把合成的數據交給DRM去輸出完成app畫面顯示到屏幕上的過程睡榆。為了更有效地利用時間這樣的共享內存不止一份萍肆,可能有兩份或三份,即常說的double buffering, triple buffering.
那么我們就需要設計一個機制可以管理buffer的控制權胀屿,這個就是BufferQueue.
本篇文章的分析塘揣,我先略過Surface相關細節(jié),重點看數據是如何從App到SurfaceFlinger的宿崭。
所以亲铡,要想知道數據是如何傳輸的,關鍵點是理解BufferQueue
葡兑。
BufferQueue
數據不是通過copy從應用傳遞到SurfaceFlinger
奖蔓,而是通過句柄傳遞。先記住這句話讹堤,后面會說道吆鹤。
BufferQueues
是 Android
圖形組件之間的粘合劑。它們是一對隊列洲守,可以調解緩沖區(qū)從生產方到消耗方的固定周期疑务。一旦生產方移交其緩沖區(qū)沾凄,SurfaceFlinger
便會負責將所有內容合成到顯示部分。
有關 BufferQueue
通信過程知允,請參見下圖撒蟀。
這個圖結合前面官方文檔的描述,可簡單總結出以下幾點:
1.
SurfaceFlinger
創(chuàng)建并擁有 BufferQueue
數據結構2.應用需要緩沖區(qū)時廊镜,它會通過調用
dequeueBuffer()
從BufferQueue
請求一個可用的緩沖區(qū)3.應用繪制UI,填充緩沖區(qū),并通過調用
queueBuffer()
將緩沖區(qū)返回到隊列
-
SurfaceFlinger
通過調用acquireBuffer()
獲取該緩沖區(qū)并使用該緩沖區(qū)的內容
那么這些是怎么做到的呢牙肝?
出處:http://www.reibang.com/p/730dd558c269
SharedBufferStack中的緩沖區(qū)只是用來描述UI元數據的,這意味著它們不包含真正的UI數據嗤朴。真正的UI數據保存在GraphicBuffer中配椭,后面我們再描述GaphicBuffer。因此雹姊,為了完整地描述一個UI股缸,SharedBufferStack中的每一個已經使用了的緩沖區(qū)都對應有一個GraphicBuffer,用來描述真正的UI數據吱雏。當SurfaceFlinger服務緩制Buffer-1和Buffer-2的時候敦姻,就會找到與它們所對應的GraphicBuffer,這樣就可以將對應的UI繪制出來了歧杏。
當Android應用程序需要更新一個Surface的時候镰惦,它就會找到與它所對應的SharedBufferStack,并且從它的空閑緩沖區(qū)列表的尾部取出一個空閑的Buffer犬绒。我們假設這個取出來的空閑Buffer的編號為index旺入。接下來Android應用程序就請求SurfaceFlinger服務為這個編號為index的Buffer分配一個圖形緩沖區(qū)GraphicBuffer。
早期的源碼中凯力,存在SharedBufferStack茵瘾,現在SharedBufferStack已經沒有了。
那么現在咐鹤,應用繪制UI所需的內存空間是如何分配的拗秘?
出處:http://www.reibang.com/p/3c61375cc15b
在BufferQueue的設計中,一個buffer的狀態(tài)有以下幾種:
FREE :表示該buffer可以給到應用程序祈惶,由應用程序來繪畫
DEQUEUED:表示該buffer的控制權已經給到應用程序側雕旨,這個狀態(tài)下應用程序可以在上面繪畫了
QUEUED: 表示該buffer已經由應用程序繪畫完成,buffer的控制權已經回到SurfaceFlinger手上了
ACQUIRED:表示該buffer已經交由HWC Service去合成了行瑞,這時控制權已給到HWC Service了
Buffer的初始狀態(tài)為FREE, 當生產者通過dequeueBuffer來申請buffer成功時奸腺,buffer狀態(tài)變?yōu)榱薉EQUEUED狀態(tài), 應用畫圖完成后通過queueBuffer把buffer狀態(tài)改到QUEUED狀態(tài)血久, 當SurfaceFlinger通過acquireBuffer操作把buffer拿去給HWC Service合成突照, 這時buffer狀態(tài)變?yōu)锳CQUIRED狀態(tài),合成完成后通過releaseBuffer把buffer狀態(tài)重新改為FREE狀態(tài)氧吐。 狀態(tài)切換如下圖所示:
BufferSlot
每一個應用程序的圖層在SurfaceFlinger里稱為一個Layer讹蘑, 而每個Layer都擁有一個獨立的BufferQueue, 每個BufferQueue都有多個Buffer,Android 系統(tǒng)上目前支持每個Layer最多64個buffer, 這個最大值被定義在frameworks/native/gui/BufferQueueDefs.h末盔, 每個buffer用一個結構體BufferSlot來代表。
每個BufferSlot里主要有如下重要成員:
struct BufferSlot{
......
BufferState mBufferState;//代表當前Buffer的狀態(tài) FREE/DEQUEUED/QUEUED/ACQUIRED
....
sp<GraphicBuffer> mGraphicBuffer;//代表了真正的buffer的存儲空間
......
uint64_t mFrameNumber;//表示這個slot被queued的編號座慰,在應用調dequeueBuffer申請slot時會參考該值
......
sp<Fence> mFence;//在Fence一章再來看它的作用
.....
}
SurfaceFlinger
不僅是Consumer
陨舱,創(chuàng)建并擁有 BufferQueue數據結構,而且還提供了Buffer
空間——GraphicBuffer
(GraphicBuffer
就是緩沖區(qū),真正的buffer的存儲空間)版仔。
了解了上面的這些基本知識后游盲,再來看看調用dequeueBuffer
后,如何分配緩存區(qū)蛮粮,以及BufferQueue
的基本流程益缎。
1. dequeueBuffer
從應用通過dequeueBuffer來申請buffer這里開始看代碼。應用在第一次dequeueBuffer時會先調用requestBuffer:
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/gui/Surface.cpp
sp<IGraphicBufferProducer> mGraphicBufferProducer;
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/gui/Surface.cpp
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,
const sp<IBinder>& surfaceControlHandle)
: mGraphicBufferProducer(bufferProducer),
//mGraphicBufferProducer 初始化= bufferProducer
....
}
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
....
// 這里嘗試去dequeueBuffer,因為這時SurfaceFlinger對應Layer的slot還沒有分配buffer,這時SurfaceFlinger會回復的flag會有BUFFER_NEEDS_REALLOCATION
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
dqInput.height, dqInput.format,
dqInput.usage, &mBufferAge,
dqInput.getTimestamps ?
&frameTimestamps : nullptr);
....
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
...
//這里檢查到dequeueBuffer返回的結果里帶有BUFFER_NEEDS_REALLOCATION標志就會發(fā)出一次requestBuffer
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
...
}
....
}
調用mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,dqInput.height, dqInput.format, dqInput.usage, &mBufferAge,dqInput.getTimestamps ? &frameTimestamps : nullptr);
時發(fā)現分配出來的slot沒有GraphicBuffer然想, 這時會去申請對應的buffer:
//frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))//檢查是否已分配了GraphicBuffer
{
......
returnFlags |= BUFFER_NEEDS_REALLOCATION;//發(fā)現需要分配buffer,置個標記
}
......
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
......
//新創(chuàng)建一個新的GraphicBuffer給到對應的slot
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
......
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;//把GraphicBuffer給到對應的slot
......
}
......
return returnFlags;//注意在應用第一次請求buffer, dequeueBuffer返回時對應的GraphicBuffer已經創(chuàng)建完成并給到了對應的slot上莺奔,但返回給應用的flags里還是帶有BUFFER_NEEDS_REALLOCATION標記的
}
調用BufferQueueProducer::dequeueBuffer
的時候,雖然創(chuàng)建了GraphicBuffer
变泄,但是應用側并沒有與擁有可用的緩沖區(qū)令哟,即GraphicBuffer
,只是新創(chuàng)建一個新的GraphicBuffer
給到對應的slot
妨蛹。
然后走到mGraphicBufferProducer->requestBuffer(buf, &gbuf)
屏富。
// frameworks/native/libs/gui/IGraphicBufferProducer.cpp
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
...
virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
bool nonNull = reply.readInt32();
if (nonNull) {
*buf = new GraphicBuffer();
result = reply.read(**buf);
if(result != NO_ERROR) {
(*buf).clear();
return result;
}
}
result = reply.readInt32();
return result;
}
然后從bp走到bn端。
// frameworks/native/libs/gui/IGraphicBufferProducer.cpp
status_t BnGraphicBufferProducer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != nullptr);
if (buffer != nullptr) {
reply->write(*buffer);
}
reply->writeInt32(result);
return NO_ERROR;
}
...
}
return BBinder::onTransact(code, data, reply, flags);
}
應用側收到帶有BUFFER_NEEDS_REALLOCATION標記的返回結果后就會調BufferQueueProducer::requestBuffer
來獲取對應buffer的信息:
//frameworks/native/libs/gui/include/gui/BufferQueueProducer.h
sp<BufferQueueCore> mCore;
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
BufferQueueDefs::SlotsType& mSlots;
// frameworks/native/libs/gui/BufferQueueProducer.cpp
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
bool consumerIsSurfaceFlinger) :
mCore(core),
mSlots(core->mSlots),
...{}
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
std::lock_guard<std::mutex> lock(mCore->mMutex);
...
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
這段調用mGraphicBufferProducer->requestBuffer(buf, &gbuf)
后發(fā)生的binder
通訊創(chuàng)建GraphicBuffer
的過程比較難以理解蛙卤,與一般的binder通訊不同役听, 要想理解這一部分通訊是如何創(chuàng)建GraphicBuffer
,需要對dma-buf
有一定的理解表窘。
關于dmf_buf 機制可以查看這篇:dma-buf 由淺入深(一) —— 最簡單的 dma-buf 驅動程序
,
概念
dma-buf 的出現就是為了解決各個驅動之間 buffer 共享的問題甜滨,因此它本質上是 buffer 與 file 的結合乐严,即 dma-buf 既是塊物理 buffer,又是個 linux file衣摩。buffer 是內容昂验,file 是媒介,只有通過 file 這個媒介才能實現同一 buffer 在不同驅動之間的流轉艾扮。
一個典型的 dma-buf 應用框圖如下:
通常既琴,我們將分配 buffer 的模塊稱為 exporter,將使用該 buffer 的模塊稱為 importer 或 user泡嘴。但在本系列文章中甫恩,importer 特指內核空間的使用者,user 特指用戶空間的使用者酌予。
2. GraphicBuffer的創(chuàng)建
為了弄清楚mGraphicBufferProducer->requestBuffer(buf, &gbuf)
到底是如何給應用側提供GraphicBuffer
磺箕。下面來看一下 GraphicBuffer
類奖慌。
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/include/ui/GraphicBuffer.h
class GraphicBufferMapper;
class GraphicBuffer
: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,
public Flattenable<GraphicBuffer>
GraphicBuffer繼承于ANativeWindowBuffer和Flattenable,前者是在ANativeWindow中的圖元緩沖松靡,后者是Binder 傳輸時候的Parcel封裝IBinder简僧。
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/GraphicBuffer.cpp
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
{
width =
height =
stride =
format =
usage_deprecated = 0;
usage = 0;
layerCount = 0;
handle = nullptr;
}
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName)
{
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
inUsage, &handle, &outStride, mId,
std::move(requestorName));
...
}
return err;
}
在初始化中有一個十分核心的類GraphicBufferAllocator,圖元申請器雕欺。這個類真正在一個GraphicBuffer的殼內岛马,通過allocate真正生成一個核心內存塊。接著會調用GraphicBufferMapper. getTransportSize在Mapper中記錄大小屠列。請注意啦逆,allocate方法中有一個十分核心的參數handle。他是來自ANativeWindowBuffer:
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/nativebase/include/nativebase/nativebase.h#96
const native_handle_t* handle;
// system/core/libcutils/include/cutils/native_handle.h中定義了該結構體
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/system/core/libcutils/include/cutils/native_handle.h#34
typedef struct native_handle
{
int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; /* number of ints at &data[numFds] */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#endif
int data[0]; /* numFds + numInts ints */
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} native_handle_t;
native_handle_t實際上是的GraphicBuffer的句柄脸哀。
這里的句柄蹦浦,就是官方文檔中緩沖區(qū)始終通過句柄進行傳遞。
3. GraphicBufferAllocator
下面來看看GraphicBufferAllocator 初始化
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/GraphicBufferAllocator.cpp
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator )
Mutex GraphicBufferAllocator::sLock;
KeyedVector<buffer_handle_t,
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
// 按照版本由高到低的順序加載 gralloc allocator, 成功則退出
mAllocator = std::make_unique<const Gralloc4Allocator>(
reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
// 創(chuàng)建 Gralloc4Allocator
...
}
ANDROID_SINGLETON_STATIC_INSTANCE這個宏實際上就是一個單例,在構造函數中做了2件事情:
1.初始化GraphicBufferMapper
2.創(chuàng)建Gralloc4Allocator
3.1 GraphicBufferMapper
下面我們來看GraphicBufferMapper 初始化做了什么撞蜂。
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/GraphicBufferMapper.cpp
GraphicBufferMapper::GraphicBufferMapper() {
mMapper = std::make_unique<const Gralloc4Mapper>();
...
}
Gralloc4Mapper 的初始化
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/Gralloc4.cpp#90
Gralloc4Mapper::Gralloc4Mapper() {
mMapper = IMapper::getService();
...
}
本質上是溝通了Hal層的hwServiceManager之后盲镶,獲取IMapper的服務。
最后我們關注IMapper.hal蝌诡,看看這個hal層開放了什么方法給上層,看幾個重點函數溉贿。
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/hardware/interfaces/graphics/mapper/4.0/IMapper.hal#23
interface IMapper {
// BufferDescriptorInfo用于描述圖形buffer的屬性(寬、高浦旱、格式...)
struct BufferDescriptorInfo {
string name; // buffer的名字宇色,用于debugging/tracing
uint32_t width; // width說明了分配的buffer中有多少個像素列,但它并不表示相鄰行的同一列元素的偏移量,區(qū)別stride颁湖。
uint32_t height; // height說明了分配的buffer中有多少像素行
uint32_t layerCount; // 分配的緩沖區(qū)中的圖像層數
PixelFormat format; // 像素格式 (參見/frameworks/native/libs/ui/include/ui/PixelFormat.h中的定義)
bitfield<BufferUsage> usage;buffer使用方式的標志位(參見/frameworks/native/libs/ui/include/ui/GraphicBuffer.h的定義)宣蠕。
uint64_t reservedSize; // 與緩沖區(qū)關聯(lián)的保留區(qū)域的大小(字節(jié))甥捺。
};
/**
* 創(chuàng)建一個 buffer descriptor抢蚀,這個descriptor可以用于IAllocator分配buffer
* 主要完成兩個工作:
* 1. 檢查參數的合法性(設備是否支持);
* 2. 把BufferDescriptorInfo這個結構體變量進行重新的包裝镰禾,本質就是轉化為byte stream皿曲,這樣可以傳遞給IAllocator
*/
createDescriptor(BufferDescriptorInfo description)
generates (Error error,
BufferDescriptor descriptor);
/**
* 把raw buffer handle轉為imported buffer handle,這樣就可以在調用進程中使用了
* 當其他進程分配的GraphicBuffer傳遞到當前進程后吴侦,需要通過該方法映射到當前進程屋休,為后續(xù)的lock做好準備
*/
importBuffer(handle rawHandle) generates (Error error, pointer buffer);
/**
* importBuffer()返回的buffer handle不再使用后必須調用freeBuffer()釋放
*/
freeBuffer(pointer buffer) generates (Error error);
/**
* 已指定的CPU usage 鎖定緩沖區(qū)的指定區(qū)域accessRegion。lock之后就可以對buffer進行讀寫了
*/
lock(pointer buffer,
uint64_t cpuUsage,
Rect accessRegion,
handle acquireFence)
generates (Error error,
pointer data);
/**
* 解鎖緩沖區(qū)以指示對緩沖區(qū)的所有CPU訪問都已完成
*/
unlock(pointer buffer) generates (Error error, handle releaseFence);
/**
* 根據給定的MetadataType獲取對應的buffer metadata
*/
get(pointer buffer, MetadataType metadataType)
generates (Error error,
vec<uint8_t> metadata);
/**
* 設置給定的MetadataType對應的buffer metadata
*/
set(pointer buffer, MetadataType metadataType, vec<uint8_t> metadata)
generates (Error error);
};
1.importBuffer 生成可用的Buffer
2.freeBuffer 釋放Buffer
3.lock 上鎖buffer
4.unlock 解鎖鎖buffer
IMapper::getService()我們可以理解為完成了 buffer handle 所指向的圖形緩存到運行進程的映射备韧。訪問 buffer 數據一般遵循這樣的流程:
importBuffer -> lock -> 讀寫GraphicBuffer-> unlock -> freeBuffer劫樟。
3.2 Gralloc4Allocator
Gralloc4Allocator構造函數中會去創(chuàng)建一個 Gralloc4Allocator 對象,并且傳遞一個 Gralloc4Mapper 對象作為參數:
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/Gralloc4.cpp
Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
ALOGW("allocator 4.x is not supported");
return;
}
}
Gralloc4Allocator 的構造函數中去獲取 gralloc-allocator hal service,這是一個 binderized hal service毅哗。
這里我們先暫時理解為:透過 GraphicBufferAllocator & Gralloc4Allocator 我們就可以使用 gralloc-alloctor hal 的功能了听怕。
GraphicBuffer類圖如下所示:
-
GraphicBuffer
:對應gralloc
分配的圖形Buffer
(也可能是普通內存,具體要看gralloc實現)虑绵,它繼承ANativeWindowBuffer
結構體尿瞭,核心成員是指向圖形緩存的句柄(native_handle_t * handle
),并且圖形Buffer
本身是多進程共享的翅睛,跨進程傳輸的是GraphicBuffer
的關鍵屬性声搁,這樣在使用進程可以重建GraphicBuffer
,同時指向同一塊圖形Buffer
捕发。 -
GraphicBufferAllocator
:向下對接gralloc allocator HAL
服務疏旨,是進程內單例,負責分配進程間共享的圖形Buffer扎酷。 -
GraphicBufferMapper
:向下對接gralloc mapper HAL
服務檐涝,是進程內單例,負責把GraphicBufferAllocator
分配的GraphicBuffer
映射到當前進程空間法挨。
GraphicBufferAllocator
分配內存的具體實現在HAL層谁榜,GraphicBufferMapper
映射GraphicBuffer
到當前進程空間的具體實現也一樣,暫不做深入凡纳。
現在來看GraphicBuffer是怎么做到跨進程共享的窃植。
3.3 關于GraphicBuffer的跨進程共享
在圖形系統(tǒng)中,生產者和最終的消費者往往不在同一個進程中荐糜,所以 GraphicBuffer 需要跨進程傳遞巷怜,以實現數據共享。我們先用一張流程圖來概況:
首先暴氏,生產進程通過GraphicBuffer::flatten把ANativeWindowBuffer關鍵屬性保存在兩個數組中:buffer和fds,其實就是 Binder 數據傳輸前的序列化處理延塑;
其次,跨進程傳輸buffer和fds答渔,這里一般就是 Binder IPC 跨進程通信页畦;
然后,消費進程通過GraphicBuffer::unflatten在自己的進程中重建ANativeWindowBuffer研儒,關鍵是重建ANativeWindowBuffer.handle這個結構體成員,相當于把生產進程的GraphicBuffer映射到了消費進程独令;
最后端朵,遵循 importBuffer->lock->讀寫GraphicBuffer->unlock->freeBuffer 的基本流程操作GraphicBuffer淹朋。
下面跟著代碼一步步說明這個流程残揉。
GraphicBuffer的數據太大了,沒有辦法進行Binder通信燕鸽,那么他為什么可以辦到binder返回呢招狸?我們調用IGraphicBufferProducer
的requestBuffer
,走到BnGraphicBufferProducer::onTransact
敬拓,最后會走到BufferQueueProducer::requestBuffer
邻薯。
// frameworks/native/libs/gui/IGraphicBufferProducer.cpp
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
...
virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
...
bool nonNull = reply.readInt32();
if (nonNull) {
*buf = new GraphicBuffer();
result = reply.read(**buf);
if(result != NO_ERROR) {
(*buf).clear();
return result;
}
}
result = reply.readInt32();
return result;
}
}
// frameworks/native/libs/gui/IGraphicBufferProducer.cpp
status_t BnGraphicBufferProducer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != nullptr);
if (buffer != nullptr) {
reply->write(*buffer);
}
reply->writeInt32(result);
return NO_ERROR;
}
...
}
return BBinder::onTransact(code, data, reply, flags);
}
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
...
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
從requestBuffer
,到BufferQueueProducer::requestBuffer
,這就是一個flatten-> unflatten ->importBuffer->lock->讀寫GraphicBuffer
的過程乘凸。
這個binder流程應該是SF端寫fd厕诡,AP端讀出fd',binder驅動幫助轉換了這個fd营勤。
具體怎么做的呢灵嫌,先來看看Parcel的reply.read和reply.write。
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::read(FlattenableHelperInterface& val) const
{
...
err = val.unflatten(buf, len, fds, fd_count);
...
}
status_t Parcel::write(const FlattenableHelperInterface& val)
{
...
err = val.flatten(buf, len, fds, fd_count);
...
}
requestBuffer---->BnGraphicBufferProducer::onTransact----> reply->write(*buffer)---->Parcel::write---->val.flatten(buf, len, fds, fd_count)---->GraphicBuffer::flatten
葛作。
val
是調用 BufferQueueProducer::requestBuffer
生成的GraphicBuffer
寿羞。
// frameworks/native/libs/ui/GraphicBuffer.cpp
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
...
int32_t* buf = static_cast<int32_t*>(buffer);
buf[0] = 'GB01';
buf[1] = width;
buf[2] = height;
...
if (handle) {
...
memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int));
memcpy(buf + 13, handle->data + handle->numFds,
static_cast<size_t>(mTransportNumInts) * sizeof(int));
}
...
}
memcpy
是內存拷貝函數,這里把handle->data
中的中復制到fds
中赂蠢。
void *memcpy(void *dest, const void *src, size_t n);
從源src所指的內存地址的起始位置開始拷貝n個字節(jié)到目標dest所指的內存地址的起始位置中绪穆。memcpy可以復制任意內容,例如字符數組虱岂、整型玖院、結構體、類等量瓜。
這里的handle
就是ANativeWindowBuffer::handle
司恳。調用initWithHandle
的時候會初始化。
// frameworks/native/libs/ui/GraphicBuffer.cpp
GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method,
uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride)
: GraphicBuffer() {
mInitCheck = initWithHandle(inHandle, method, inWidth, inHeight, inFormat, inLayerCount,
inUsage, inStride);
}
ANativeWindowBuffer::handle = inHandle;
native_handle_t是上層抽象的可以在進程間傳遞的數據結構绍傲,對private_handle_t的抽象包裝扔傅。
numFds=1表示有一個文件句柄:fd
numInts= 8表示后面跟了8個INT型的數據:magic,flags,size,offset,base,lockState,writeOwner,pid;
服務進程將創(chuàng)建的GraphicBuffer對象的成員變量handle寫回到請求創(chuàng)建圖形緩沖區(qū)的客戶進程,這時客戶進程通過以下方式就可以讀取服務進程返回的關于創(chuàng)建圖形buffer的信息數據烫饼。
服務端返回replay后,應用側通過reply.read(**buf)讀native_handle_t
數據猎塞。
// frameworks/native/libs/gui/IGraphicBufferProducer.cpp
virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
...
if (nonNull) {
//新建GraphicBuffer
*buf = new GraphicBuffer();
result = reply.read(**buf);
if(result != NO_ERROR) {
(*buf).clear();
return result;
}
}
...
}
reply.read(**buf)---->Parcel::read---->val.unflatten(buf, len, fds, fd_count)---->GraphicBuffer::unflatten
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/ui/GraphicBuffer.cpp#101
status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count) {
...
native_handle* h =
native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
...
memcpy(h->data, fds, numFds * sizeof(int));
memcpy(h->data + numFds, buf + flattenWordCount, numInts * sizeof(int));
handle = h;
...
if (handle != nullptr) {
buffer_handle_t importedHandle;
status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
...
native_handle_close(handle);
native_handle_delete(const_cast<native_handle_t*>(handle));
handle = importedHandle;
mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
}
...
}
關注2個重點函數:native_handle_create
和importBuffer
。
調用native_handle_create
在應用側進程構造一個native_handle
對象杠纵。
// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/system/core/libcutils/native_handle.cpp#38
native_handle_t* native_handle_create(int numFds, int numInts) {
if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
numInts > NATIVE_HANDLE_MAX_INTS) {
errno = EINVAL;
return NULL;
}
size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));
if (h) {
h->version = sizeof(native_handle_t);
h->numFds = numFds;
h->numInts = numInts;
}
return h;
}
importBuffer
的調用在HAL層荠耽,這里看看其接口。
// IMapper.hal
/**
* 把raw buffer handle轉為imported buffer handle比藻,這樣就可以在調用進程中使用了
* 當其他進程分配的GraphicBuffer傳遞到當前進程后铝量,需要通過該方法映射到當前進程,為后續(xù)的lock做好準備
*/
importBuffer(handle rawHandle) generates (Error error, pointer buffer);
Android早期版本調用的是mBufferMapper.registerBuffer银亲,作用是將創(chuàng)建的圖形緩沖區(qū)映射到客戶進程的地址空間來慢叨,這樣客戶端進程就可以直接在圖形buffer映射的地址空間繪圖。
客戶端進程讀取到服務進程創(chuàng)建的圖形buffer的描述信息native_handle后务蝠,通過GraphicBufferMapper對象mBufferMapper的registerBuffer函數將創(chuàng)建的圖形buffer映射到客戶端進程地址空間拍谐。
前面調用GraphicBuffer::flatten
,會走到memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int));
,把handle->data
復制到fds
中,這里調用 GraphicBuffer::unflatten
之后轩拨,走到memcpy(h->data, fds, numFds * sizeof(int));
,把fds
復制到handle->data
中践瓷。
這樣就把整個handle
拷貝過來了,接著調用importBuffer亡蓉,把handle轉化從hidl_handle轉化為可用的private_handle_t晕翠。
這樣就將Private_native_t中的數據:magic,flags,size,offset,base,lockState,writeOwner,pid復制到了客戶端進程。服務端(SurfaceFlinger)分配了一段內存作為Surface的作圖緩沖區(qū)寸宵,客戶端怎樣在這個作圖緩沖區(qū)上繪圖呢崖面?兩個進程間如何共享內存,這就需要GraphicBufferMapper將分配的圖形緩沖區(qū)映射到客戶端進程地址空間梯影。對于共享緩沖區(qū)巫员,他們操作同一物理地址的內存塊。
其實這里還有一個很重要的點ION
沒有深入甲棍。
出處:http://www.reibang.com/p/3bfc0053d254
一般來說:圖元的繪制分為如下幾個步驟:
1.dequeueBuffer 獲取一個圖元的插槽位置简识,或者生產一個圖元。其實在IGrraphicBufferProducer通過flattern進行一次句柄GraphicBuffer拷貝感猛,依次為依據找到底層的共享內存七扰。
2.lock 綁定圖元共享內存地址,最后通過句柄在GrallocImportedBufferPool中找到在SF進程申請好的內存地址
3.queueBuffer 把圖元放入mActiveBuffer中陪白,并且從新計算dequeue和acquire的數量颈走,同時把GrapicBuffer放到mQueue進行消費,最后調用frameAvilable回調通知消費者咱士。
4.unlock 解鎖圖元 揭開共享內存的映射立由。
到這里面涉及到了幾個fd的轉化,先不用太關注序厉,知道是通過ion申請一段共享內存锐膜,通過fd的方式告訴App進程可以映射到同一段物理內存。
更高一點的版本ION也被換掉了弛房。
在 Android 12 中道盏,GKI 2.0 將 ION 分配器替換為 DMA-BUF。
4.queueBuffer
客戶端/應用通過調用dequeueBuffer獲取到一個可用的buffer后文捶,就可以往這個buffer中填充數據了荷逞。填充好數據后,就要把這個buffer再返還給BufferQueue粹排,調用的方法是queueBuffer颅围。
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
sp<Fence> acquireFence;
bool getFrameTimestamps = false;
// 保存Surface傳遞過來的input里面封裝的buffer信息
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
if (acquireFence == nullptr) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
break;
default:
BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
return BAD_VALUE;
}
// 回調接口,用于通知consumer
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
// BufferQueue是否被棄用
if (mCore->mIsAbandoned) {
BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
// BufferQueue是否沒有連的producer
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("queueBuffer: BufferQueue has no connected producer");
return NO_INIT;
}
// BufferSlot對應的slot序號是否合法恨搓,狀態(tài)是否為DEQUEUE
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) { // 是否調用了requestBuffer 函數
BQ_LOGE("queueBuffer: slot %d was queued without requesting "
"a buffer", slot);
return BAD_VALUE;
}
// If shared buffer mode has just been enabled, cache the slot of the
// first buffer that is queued and mark it as the shared buffer.
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
BufferQueueCore::INVALID_BUFFER_SLOT) {
mCore->mSharedBufferSlot = slot;
mSlots[slot].mBufferState.mShared = true;
}
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s",
slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace,
hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom,
transform,
BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
// 當前queue的具體GraphicBuffer
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
// 根據當前的GraphicBufferd的寬高創(chuàng)建矩形區(qū)域
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
// 創(chuàng)建裁剪區(qū)域
Rect croppedRect(Rect::EMPTY_RECT);
// 裁剪區(qū)域 賦值為crop和bufferRect相交部分
crop.intersect(bufferRect, &croppedRect);
if (croppedRect != crop) {
BQ_LOGE("queueBuffer: crop rect is not contained within the "
"buffer in slot %d", slot);
return BAD_VALUE;
}
// Override UNKNOWN dataspace with consumer default
if (dataSpace == HAL_DATASPACE_UNKNOWN) {
dataSpace = mCore->mDefaultBufferDataSpace;
}
mSlots[slot].mFence = acquireFence;
// 改變入隊的BufferSlot的狀態(tài)為QUEUED
mSlots[slot].mBufferState.queue();
// Increment the frame counter and store a local version of it
// for use outside the lock on mCore->mMutex.
++mCore->mFrameCounter;
currentFrameNumber = mCore->mFrameCounter;
mSlots[slot].mFrameNumber = currentFrameNumber;
// 把BufferSlot中的信息封裝為BufferItem,后續(xù)會把這個BufferItem加入到隊列中
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
item.mCrop = crop;
item.mTransform = transform &
~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
item.mHdrMetadata = hdrMetadata;
item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
item.mFence = acquireFence;
item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
(mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
(mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
item.mSurfaceDamage = surfaceDamage;
item.mQueuedBuffer = true;
item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
item.mApi = mCore->mConnectedApi;
mStickyTransform = stickyTransform;
// Cache the shared buffer data so that the BufferItem can be recreated.
if (mCore->mSharedBufferMode) {
mCore->mSharedBufferCache.crop = crop;
mCore->mSharedBufferCache.transform = transform;
mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(
scalingMode);
mCore->mSharedBufferCache.dataspace = dataSpace;
}
output->bufferReplaced = false;
if (mCore->mQueue.empty()) {
// 如果mQueue隊列為空,則直接push進入這個mQueue斧抱,不用考慮阻塞
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
mCore->mQueue.push_back(item);
//取出BufferQueueCore的回調接口常拓,下面調用這個接口的onFrameAvailable函數來通知Consumer
frameAvailableListener = mCore->mConsumerListener;
} else {
// When the queue is not empty, we need to look at the last buffer
// in the queue to see if we need to replace it
const BufferItem& last = mCore->mQueue.itemAt(
mCore->mQueue.size() - 1);
if (last.mIsDroppable) {
// 判斷最后一個BufferItem是否可以丟棄
if (!last.mIsStale) {
mSlots[last.mSlot].mBufferState.freeQueued();
// After leaving shared buffer mode, the shared buffer will
// still be around. Mark it as no longer shared if this
// operation causes it to be free.
if (!mCore->mSharedBufferMode &&
mSlots[last.mSlot].mBufferState.isFree()) {
mSlots[last.mSlot].mBufferState.mShared = false;
}
// Don't put the shared buffer on the free list.
if (!mSlots[last.mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(last.mSlot);
mCore->mFreeBuffers.push_back(last.mSlot);
output->bufferReplaced = true;
}
}
// Make sure to merge the damage rect from the frame we're about
// to drop into the new frame's damage rect.
if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT ||
item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) {
item.mSurfaceDamage = Region::INVALID_REGION;
} else {
item.mSurfaceDamage |= last.mSurfaceDamage;
}
// 用當前BufferItem,替換了隊列最后一個BufferItem
// Overwrite the droppable buffer with the incoming one
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
//取出回調接口辉浦,因為是替換弄抬,所以后續(xù)調用接口的函數 onFrameReplaced
frameReplacedListener = mCore->mConsumerListener;
} else {
// 直接push進入這個mQueue
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
}
}
// 表示 buffer已經queued,此時入隊完成
mCore->mBufferHasBeenQueued = true;
// mDequeueCondition是C++條件變量用作等待/喚醒宪郊,這里調用notify_all會喚醒調用了wait的線程
mCore->mDequeueCondition.notify_all();
mCore->mLastQueuedSlot = slot;
//output 參數掂恕,會在Surface中繼續(xù)使用
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
VALIDATE_CONSISTENCY();
} // Autolock scope
// It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
// it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
// there will be no Binder call
if (!mConsumerIsSurfaceFlinger) {
item.mGraphicBuffer.clear();
}
// Update and get FrameEventHistory.
nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
NewFrameEventsEntry newFrameEventsEntry = {
currentFrameNumber,
postedTime,
requestedPresentTimestamp,
std::move(acquireFenceTime)
};
addAndGetFrameTimestamps(&newFrameEventsEntry,
getFrameTimestamps ? &output->frameTimestamps : nullptr);
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
int connectedApi;
sp<Fence> lastQueuedFence;
{ // scope for the lock
std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(lock);
}
//通知consumer,此處調用接口的不同弛槐,是有上面懊亡,是否替換最后一個BufferItem 決定的
if (frameAvailableListener != nullptr) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != nullptr) {
frameReplacedListener->onFrameReplaced(item);
}
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
mCallbackCondition.notify_all();
}
// Wait without lock held
if (connectedApi == NATIVE_WINDOW_API_EGL) {
// Waiting here allows for two full buffers to be queued but not a
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
lastQueuedFence->waitForever("Throttling EGL Production");
}
return NO_ERROR;
}
queueBuffer 的流程主要做了這兩件事情:
1.將對應 BufferSlot 狀態(tài)設置成 QUEUED
2.創(chuàng)建 BufferItem 對象,并將 GraphicBuffer 的數據復制給 BufferItem乎串,并入隊到 BufferQueueCore 的 mQueue 隊列中店枣,這樣可以方便圖像消費者直接按先進先出的順序從 mQueue 隊列取出 GraphicBuffer 使用
生產者寫完數據,把buffer還給buffer queue后叹誉,會通知消費者來使用鸯两。
5.acquireBuffer 和 releaseBuffer
BufferQueueConsumer作為消費者的一個代表元素通過 acquireBuffer 來獲取圖像緩沖區(qū),通過 releaseBuffer 來釋放該緩沖區(qū)长豁。
5.1 acquireBuffer
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent, uint64_t maxFrameNumber) {
ATRACE_CALL();
int numDroppedBuffers = 0;
sp<IProducerListener> listener;
{
std::unique_lock<std::mutex> lock(mCore->mMutex);
// Check that the consumer doesn't currently have the maximum number of
// buffers acquired. We allow the max buffer count to be exceeded by one
// buffer so that the consumer can successfully set up the newly acquired
// buffer before releasing the old one.
// 檢查acquire的buffer的數量是否超出了限制
int numAcquiredBuffers = 0;
for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
}
const bool acquireNonDroppableBuffer = mCore->mAllowExtraAcquire &&
numAcquiredBuffers == mCore->mMaxAcquiredBufferCount + 1;
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1 &&
!acquireNonDroppableBuffer) {
BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
return INVALID_OPERATION;
}
bool sharedBufferAvailable = mCore->mSharedBufferMode &&
mCore->mAutoRefresh && mCore->mSharedBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT;
// In asynchronous mode the list is guaranteed to be one buffer deep,
// while in synchronous mode we use the oldest buffer.
// 檢查BufferQueueCore中的mQueue隊列是否為空
if (mCore->mQueue.empty() && !sharedBufferAvailable) {
return NO_BUFFER_AVAILABLE;
}
// 獲取BufferQueueCore中的mQueue隊列的迭代器
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
// If expectedPresent is specified, we may not want to return a buffer yet.
// If it's specified and there's more than one buffer queued, we may want
// to drop a buffer.
// Skip this if we're in shared buffer mode and the queue is empty,
// since in that case we'll just return the shared buffer.
if (expectedPresent != 0 && !mCore->mQueue.empty()) {
// expectedPresent表示期望這個buffer什么時候顯示到屏幕上钧唐。
// 如果buffer的期望顯示時間小于expectedPresent,我們會acquire and return這個buffer
// 如果我們不想顯示它直到expectedPresent之后匠襟,可以返回PRESENT_LATER
// The 'expectedPresent' argument indicates when the buffer is expected
// to be presented on-screen. If the buffer's desired present time is
// earlier (less) than expectedPresent -- meaning it will be displayed
// on time or possibly late if we show it as soon as possible -- we
// acquire and return it. If we don't want to display it until after the
// expectedPresent time, we return PRESENT_LATER without acquiring it.
//
// 安全起見钝侠,如果expectedPresent超過了buffer的期望顯示時間1秒,我們會推遲acquire
// To be safe, we don't defer acquisition if expectedPresent is more
// than one second in the future beyond the desired present time
// (i.e., we'd be holding the buffer for a long time).
//
// NOTE: Code assumes monotonic time values from the system clock
// are positive.
// 檢查是否需要丟棄一些幀,主要是判斷timestamps & expectedPresent
// Start by checking to see if we can drop frames. We skip this check if
// the timestamps are being auto-generated by Surface. If the app isn't
// generating timestamps explicitly, it probably doesn't want frames to
// be discarded based on them.
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
const BufferItem& bufferItem(mCore->mQueue[1]);
// If dropping entry[0] would leave us with a buffer that the
// consumer is not yet ready for, don't drop it.
if (maxFrameNumber && bufferItem.mFrameNumber > maxFrameNumber) {
break;
}
// If entry[1] is timely, drop entry[0] (and repeat). We apply an
// additional criterion here: we only drop the earlier buffer if our
// desiredPresent falls within +/- 1 second of the expected present.
// Otherwise, bogus desiredPresent times (e.g., 0 or a small
// relative timestamp), which normally mean "ignore the timestamp
// and acquire immediately", would cause us to drop frames.
//
// We may want to add an additional criterion: don't drop the
// earlier buffer if entry[1]'s fence hasn't signaled yet.
nsecs_t desiredPresent = bufferItem.mTimestamp;
// desiredPresent比expectedPresent小了1 second多宅此,或desiredPresent大于expectedPresent
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresent) {
// This buffer is set to display in the near future, or
// desiredPresent is garbage. Either way we don't want to drop
// the previous buffer just to get this on the screen sooner.
BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
PRId64 " (%" PRId64 ") now=%" PRId64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
break;
}
BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
" size=%zu",
desiredPresent, expectedPresent, mCore->mQueue.size());
// 處理要drop的buffer
if (!front->mIsStale) {
// Front buffer is still in mSlots, so mark the slot as free
// 對應的BufferSlot設置為FREE狀態(tài)
mSlots[front->mSlot].mBufferState.freeQueued();
// After leaving shared buffer mode, the shared buffer will
// still be around. Mark it as no longer shared if this
// operation causes it to be free.
if (!mCore->mSharedBufferMode &&
mSlots[front->mSlot].mBufferState.isFree()) {
mSlots[front->mSlot].mBufferState.mShared = false;
}
// mActiveBuffers :綁定了GraphicBuffer且狀態(tài)為非FREE的BufferSlot集合;
// mFreeBuffers :綁定了GraphicBuffer且狀態(tài)為FREE的BufferSlot集合机错;
// Don't put the shared buffer on the free list
if (!mSlots[front->mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(front->mSlot); // 從mActiveBuffers刪除
mCore->mFreeBuffers.push_back(front->mSlot);// 添加進mFreeBuffers
}
if (mCore->mBufferReleasedCbEnabled) {
listener = mCore->mConnectedProducerListener; // 設置生產者的監(jiān)聽器
}
++numDroppedBuffers; // 計數加1,記錄drop了幾個buffer
}
mCore->mQueue.erase(front);// 從mQueue中刪除
front = mCore->mQueue.begin();// 重置front父腕,進入下一次while循環(huán)
}
// See if the front buffer is ready to be acquired
nsecs_t desiredPresent = front->mTimestamp;
bool bufferIsDue = desiredPresent <= expectedPresent ||
desiredPresent > expectedPresent + MAX_REASONABLE_NSEC;
bool consumerIsReady = maxFrameNumber > 0 ?
front->mFrameNumber <= maxFrameNumber : true;
if (!bufferIsDue || !consumerIsReady) {
BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
" (%" PRId64 ") now=%" PRId64 " frame=%" PRIu64
" consumer=%" PRIu64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC),
front->mFrameNumber, maxFrameNumber);
ATRACE_NAME("PRESENT_LATER");
return PRESENT_LATER;
}
BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
"(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
}
// 走到這里就說明:該丟棄的已經都丟棄了弱匪,余下的就可以拿去顯示了。
int slot = BufferQueueCore::INVALID_BUFFER_SLOT;
if (sharedBufferAvailable && mCore->mQueue.empty()) {
// make sure the buffer has finished allocating before acquiring it
// 共享Buffer模式下處理
mCore->waitWhileAllocatingLocked(lock);
slot = mCore->mSharedBufferSlot;
// Recreate the BufferItem for the shared buffer from the data that
// was cached when it was last queued.
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
outBuffer->mScalingMode = mCore->mSharedBufferCache.scalingMode;
outBuffer->mDataSpace = mCore->mSharedBufferCache.dataspace;
outBuffer->mFrameNumber = mCore->mFrameCounter;
outBuffer->mSlot = slot;
outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
outBuffer->mTransformToDisplayInverse =
(mCore->mSharedBufferCache.transform &
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
outBuffer->mSurfaceDamage = Region::INVALID_REGION;
outBuffer->mQueuedBuffer = false;
outBuffer->mIsStale = false;
outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
mCore->mAutoRefresh;
} else if (acquireNonDroppableBuffer && front->mIsDroppable) {
BQ_LOGV("acquireBuffer: front buffer is not droppable");
return NO_BUFFER_AVAILABLE;
} else {
// 從front獲取對應的slot index
slot = front->mSlot;
*outBuffer = *front;
}
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
if (!outBuffer->mIsStale) {
mSlots[slot].mAcquireCalled = true;
// Don't decrease the queue count if the BufferItem wasn't
// previously in the queue. This happens in shared buffer mode when
// the queue is empty and the BufferItem is created above.
if (mCore->mQueue.empty()) {
mSlots[slot].mBufferState.acquireNotInQueue();
} else {
// 將BufferState狀態(tài)改為acquire
mSlots[slot].mBufferState.acquire();
}
mSlots[slot].mFence = Fence::NO_FENCE;
}
// If the buffer has previously been acquired by the consumer, set
// mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
// on the consumer side
if (outBuffer->mAcquireCalled) {
outBuffer->mGraphicBuffer = nullptr;
}
//將該Buffer從mQueue中移除
mCore->mQueue.erase(front);
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
mCore->mDequeueCondition.notify_all();
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
VALIDATE_CONSISTENCY();
}
// 回調璧亮,通知生產者
if (listener != nullptr) {
for (int i = 0; i < numDroppedBuffers; ++i) {
listener->onBufferReleased();
}
}
return NO_ERROR;
}
主要就是這幾件事情:
1.判斷 BufferQueueCore 中的 mQueue 是否為空萧诫,mQueue 就是前面 BufferQueueProducer 調用 queueBuffer 函數時,將緩沖區(qū)入隊的容器枝嘶;
2.取出對應的 BufferSlot(會有一些判斷規(guī)則帘饶,舍棄一些buffer);
3.將 BufferState 改為 acquire 狀態(tài)群扶;
4.將該 Buffer 從 mQueue 中移除及刻;
5.2 releaseBuffer
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
EGLSyncKHR eglFence) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
releaseFence == nullptr) {
BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
releaseFence.get());
return BAD_VALUE;
}
sp<IProducerListener> listener;
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
// Ignore this for the shared buffer where the frame number can easily
// get out of sync due to the buffer being queued and acquired at the
// same time.
if (frameNumber != mSlots[slot].mFrameNumber &&
!mSlots[slot].mBufferState.isShared()) {
return STALE_BUFFER_SLOT;
}
if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
"but its state was %s", slot,
mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
mSlots[slot].mFence = releaseFence;
mSlots[slot].mBufferState.release();//置為FREE狀態(tài)
// After leaving shared buffer mode, the shared buffer will
// still be around. Mark it as no longer shared if this
// operation causes it to be free.
if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
mSlots[slot].mBufferState.mShared = false;
}
// Don't put the shared buffer on the free list.
if (!mSlots[slot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(slot);// 從mActiveBuffers中刪除
mCore->mFreeBuffers.push_back(slot);//加入到mFreeBuffers中
}
if (mCore->mBufferReleasedCbEnabled) {
listener = mCore->mConnectedProducerListener; // 設置listener
}
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
// 喚醒等待的線程
mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
} // Autolock scope
// Call back without lock held
if (listener != nullptr) {
listener->onBufferReleased(); //通知producer
}
return NO_ERROR;
}
至此镀裤,BufferQueue
的大致流程已經看完〗煞梗回到一開始的問題暑劝,APP
和SurfaceFlinger
如何傳輸數據?
關于這個問題颗搂,我覺得這里總結的蠻好的担猛。
出處:http://www.reibang.com/p/f96ab6646ae3
APP繪畫的畫布是由SurfaceFlinger提供的,而畫布是一塊共享內存丢氢,APP向SurfaceFlinger申請到畫布傅联,是將共享內存的地址映射到自身進程空間。 App負責在畫布上作畫疚察,畫完的作品提交給SurfaceFlinger蒸走, 這個提交操作并不是把內存復制一份給SurfaceFlinger,而是把共享內存的控制權交還給SurfaceFlinger稍浆。
最后载碌,給出一張BufferQueue
的流程圖,復盤一下:
更多關于BufferQueue
源碼方面細節(jié)衅枫,可以參考這篇
Android 12(S) 圖像顯示系統(tǒng) - 開篇
參考鏈接:
Android圖形系統(tǒng)(八)-app與SurfaceFlinger共享UI元數據過程
SurfaceFlinger 原理分析
SurfaceFlinger中的SharedClient
《深入理解android內核設計思想》
Android畫面顯示流程分析(2)
Android畫面顯示流程分析(3)
顯示框架之app與SurfaceFlinger通信
android Gui系統(tǒng)之SurfaceFlinger(2)---BufferQueue
Android 重學系列 GraphicBuffer的誕生
Android 重學系列 圖元的消費
Android graphics(二) bufferqueue
[Android禪修之路] 解讀 GraphicBuffer 開篇
[Android禪修之路] 解讀 GraphicBuffer 之 Ion 驅動層
Android P 圖形顯示系統(tǒng)(十) BufferQueue(一)
AndroidQ 圖形系統(tǒng)(7)GraphicBuffer內存分配與Gralloc
android graphic(8)—surface申請GraphicBuffer過程
surfaceflinger分析
Android P 圖形顯示系統(tǒng)(十一) BufferQueue(二)
Android graphics(二) bufferqueue
Android 12(S) 圖像顯示系統(tǒng) - 解讀Gralloc架構及GraphicBuffer創(chuàng)建/傳遞/釋放(十四)
Android GraphicBuffer分配過程