Android graphics 學習-生產者、消費者、BufferQueue介紹
備註:博文根據Android5.1程式碼分析,為個人學習筆記。如有問題,歡迎指正。
博文是後續理解Camera preview,recording buffer流轉的基礎。要不然後續Camera buffer部分不好分析。這是這幾天看的筆記,也算是對BufferQueue有了新的認識,仍有許多地方需要探索。首先從整體上初步認識一下graphics元件(圖片來源於Android官方文件)。
上圖中大家見的最多的就是mediaPlayer和CameraPreview.buffer資料流,首先經過surface->GLConsumer->SurfaceFlinger->hardwareComposer,這樣一條線路,buffer資料就渲染到螢幕上了。這裡為什麼只提到Camera Preview 那麼Camera Recording,Caputre模式下buffer又如何流轉的呢。這裡先提出疑問?後續博文在一一解開。這裡大體上有個整體認識舉行。
一、BufferQueue介紹
BufferQueue是buffer流轉重要中轉站,理解它的工作機制是非常重要的,它的狀態改變經歷過下面幾個過程。只需要先了解一下buffer狀態和owner轉變過程。後面會結合例子來分析。
- 1.首先生產者dequeue過來一塊Buffer,此時該buffer的狀態為DEQUEUED,所有者為PRODUCER,生產者可以填充資料了。在沒有dequeue操作時,buffer的狀態為free,所有者為BUFFERQUEUE.
- 2.生產者填充完資料後,進行queue操作,此時buffer的狀態由DEQUEUED->QUEUED的轉變,buffer所有者也變成了BufferQueue了。
- 注意
- 注意
- 3 上面已經通知消費者去拿buffer了,這個時候消費者就進行acquire操作將buffer拿過來,此時buffer的狀態由QUEUED->ACQUIRED轉變,buffer的擁有者由BufferQueue變成Consumer.
- 4.當消費者已經消費了這塊buffer(已經合成,已經編碼等),就進行release操作釋放buffer,將buffer歸還給BufferQueue,buffer狀態由ACQUIRED變成FREE.buffer擁有者由Consumer變成BufferQueue.
下圖也是從Android官網上引用過來Android7.0的BufferQueue工作狀態圖(與Android5.1意思一樣,只是這個下過更好一點)。
原始碼路徑:frameworks/native/include/gui/BufferSlot.h
enum BufferState {
// FREE indicates that the buffer is available to be dequeued
// by the producer. The buffer may be in use by the consumer for
// a finite time, so the buffer must not be modified until the
// associated fence is signaled.
//
// The slot is "owned" by BufferQueue. It transitions to DEQUEUED
// when dequeueBuffer is called.
FREE = 0,
// DEQUEUED indicates that the buffer has been dequeued by the
// producer, but has not yet been queued or canceled. The
// producer may modify the buffer's contents as soon as the
// associated ready fence is signaled.
//
// The slot is "owned" by the producer. It can transition to
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
DEQUEUED = 1,
// QUEUED indicates that the buffer has been filled by the
// producer and queued for use by the consumer. The buffer
// contents may continue to be modified for a finite time, so
// the contents must not be accessed until the associated fence
// is signaled.
//
// The slot is "owned" by BufferQueue. It can transition to
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
// queued in asynchronous mode).
QUEUED = 2,
// ACQUIRED indicates that the buffer has been acquired by the
// consumer. As with QUEUED, the contents must not be accessed
// by the consumer until the fence is signaled.
//
// The slot is "owned" by the consumer. It transitions to FREE
// when releaseBuffer is called.
ACQUIRED = 3
};
雖然Android原始碼中的BufferQueue類中包含一個buffer監聽類定義和靜態建立BufferQueue物件的靜態方法。但是bufferQueue主要工作由類BufferQueueCore來完成,該類中由幾個變數非常值得我們瞭解一下(貼出來都是原始碼中的註釋)。如果想探索其它成員的話,還是擼擼原始碼吧。
- 1.mAllocator:這個是gralloc buffer分配物件,由SurfaceFlinger提供的,這個在建立消費者Client物件時,會根據例子介紹,先了解一下。
// mAllocator is the connection to SurfaceFlinger that is used to allocate
// new GraphicBuffer objects.
sp<IGraphicBufferAlloc> mAllocator;
- 2.mConsumerListener:這個是消費者監聽物件,用於當有幀可用時,bufferQueue會回撥該物件中的onFrameAvailable函式,來通知消費者有幀可用了,趕緊來取buffer.
// mConsumerListener is used to notify the connected consumer of
// asynchronous events that it may wish to react to. It is initially
// set to NULL and is written by consumerConnect and consumerDisconnect.
sp<IConsumerListener> mConsumerListener;
- 3.mSlots:為存放buffer的地方,它的數量為64,這裡先劇透一下一個surface物件對應一個mSlot物件,一個mSlot物件最多儲存64塊Buffer,即BufferQueue最多能運轉64塊buffer。這樣的話,每一個surface最多能申請64個buffer.但為了確保buffer能非同步進行,一般有2個reserver的。
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
// allocated for a slot when requestBuffer is called with that slot's index.
BufferQueueDefs::SlotsType mSlots;
//----------------------------------
// BufferQueue will keep track of at most this value of buffers.
// Attempts at runtime to increase the number of buffers past this
// will fail.
enum { NUM_BUFFER_SLOTS = 64 };//官方定義的是64個
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
- 4.mFrameCounter:表示已經入佇列和剛剛分配的buffer的數量。
// mFrameCounter is the free running counter, incremented on every
// successful queueBuffer call and buffer allocation.
uint64_t mFrameCounter;
- 5.createBufferQueue():建立bufferQueue介面。該靜態方法,內部根據surfaceFlinger傳過來的buffer分配物件allocator,然後依次建立BufferQueue,生產者,消費者,建立生產者和消費者過程中,將它們都捆綁到了同一個BufferQueue上。
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
"BufferQueue: outConsumer must not be NULL");
sp<BufferQueueCore> core(new BufferQueueCore(allocator));
LOG_ALWAYS_FATAL_IF(core == NULL,
"BufferQueue: failed to create BufferQueueCore");
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
LOG_ALWAYS_FATAL_IF(producer == NULL,
"BufferQueue: failed to create BufferQueueProducer");
//請先留意一下消費者物件類是BufferQueueConsumer,該類能夠實現程序間通訊
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
LOG_ALWAYS_FATAL_IF(consumer == NULL,
"BufferQueue: failed to create BufferQueueConsumer");
*outProducer = producer;//返回到client中
*outConsumer = consumer;
}
二、消費者Consumer介紹
消費者介面類主要實現了IGraphicBufferConsumer介面。它們的繼承引用關係如下所示:
雖然這裡定義IGraphicBufferConsumer消費者標準介面。但是在下面的事例中,沒跟蹤到建立代理物件的地方。這裡先不做深入理解吧。下面列出來一些比較熟悉的介面加上官方註釋。
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
EGLDisplay display, EGLSyncKHR fence,
const sp<Fence>& releaseFence) = 0;
// consumerConnect connects a consumer to the BufferQueue. Only one
// consumer may be connected, and when that consumer disconnects the
// BufferQueue is placed into the "abandoned" state, causing most
// interactions with the BufferQueue by the producer to fail.
// controlledByApp indicates whether the consumer is controlled by
// the application.
//
// consumer may not be NULL.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned
// * BAD_VALUE - a NULL consumer was provided
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
上面列出來的幾個介面,讓我頭痛的是consumerConnect()這個介面。當時在分析註冊幀監聽物件時,始終不確定Consumer到底呼叫的是哪一個consumerConnect()方法。一開始以為呼叫的是代理物件的consumerConnect()方法。但是在建立BufferQueue物件時,建立的是BufferQueueConsumer物件,該類繼承了BnGraphicBufferConsumer().到後來我才發現,其實BufferQueueConsumer類中已經實現了consumerConnect()方法。
void Layer::onFirstRef() {
// Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
mProducer = new MonitoredProducer(producer, mFlinger);
mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
//此處省略幾行程式碼,目前不是我們關心的。
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
updateTransformHint(hw);
}
客戶端在請求建立surface時,會在SurfaceFlinger服務中,建立一個Layer物件,而在第一次引用該layer物件時,就會呼叫onFirstRef函式。上面程式碼可以發現建立Layer物件同時建立了生產者,消費者,BufferQueue物件,其中將創建出來的IGraphicBufferConsumer consumer消費者物件傳給了SurfaceFlingerConsumer()建構函式。建立SurfaceFlingerConsumer物件有點曲折,分成下面幾步走。
- 1.由於SurfaceFlingerConsumer類繼承了ConsumerBase類,所以會回撥基類的建構函式。該建構函式中的bufferQueue輸入引數,就是我們在建立BufferQueue物件時,建立的BufferQueueConsumer物件,該類已經實現了consumerConnect方法,所以下面ConsumerBase建構函式中直接呼叫了該方法,註冊監聽物件到BufferQueue,這裡的註冊只是註冊一個軀殼,真正的監聽物件註冊是由上面的setContentsChangedListener方法實現。
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
mConsumer(bufferQueue) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
// reference once the ctor ends, as that would cause the refcount of 'this'
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
// that's what we create.
//以當前類物件為輸入引數,重新建立一個監聽物件。
wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mConsumer->setConsumerName(mName);
}
}
- 2.註冊幀可用通知物件
上面在ConsumerBase建構函式中,建立ProxyConsumerListener監聽物件時,將ConsumerBase物件傳入給ProxyConsumerListener,其實這裡面真正的監聽物件是通過ConsumerBase可以修改的。
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
bool controlledByApp) {
return connect(consumer, controlledByApp);//直接呼叫了connect,如下所示
}
//在原始檔中實現
status_t BufferQueueConsumer::connect(
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
ATRACE_CALL();
//此處省略一些錯誤檢查程式碼。
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("connect(C): BufferQueue has been abandoned");
return NO_INIT;
}
//將消費者監聽物件註冊到BufferQueue中,這裡可以看到,監聽物件賦給了BufferQueue的mConsumerListener成員。
mCore->mConsumerListener = consumerListener;
mCore->mConsumerControlledByApp = controlledByApp;
return NO_ERROR;
}
- 3.幀可用通知函式onFrameAvailable幹了什麼
由於在Layer時onFirstRef()函式中呼叫了setContentsChangedListener(this),其時Layer實現了FrameAvailableListener介面。下面就是在Layer實現的onFrameAvailable方法。
void Layer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.push_back(item);
}
android_atomic_inc(&mQueuedFrames);
mFlinger->signalLayerUpdate();//通知更新Layer.
}
- 4.何時觸發幀可用通知onFrameAvailable()回撥
當生產者進行queueBuffer操作時,會在生產者本地物件中回撥註冊到BufferQueue中的幀可用回撥。
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
//.......
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
//......
//由於生產者和消費者屬於同一個BufferQueue物件,那麼這裡取出來的監聽物件就是
//上面我們註冊進去的。
frameAvailableListener = mCore->mConsumerListener;//取到註冊到BufferQueue中的幀可用通知。
//......
if (frameAvailableListener != NULL) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
//......
}
三、生產者Producer介紹
生產者實現IGraphicBufferProducer的介面,在實際執行過程中,應用端存在代理物件,SurfaceFlinger端存在本地物件。生產者代理物件通過Binder通訊,不斷的dequeueBuffer和queueBuffer操作,本地物件同樣響應這些操作請求,這樣buffer就轉了起來了。
下面列舉出了和生產者有密切關係的幾個類。不同的是Surface類中包含了一個BpGraphicBufferProducer代理物件介面,它的DequeueBuffer和queueBuffer操作實際上是通過生產者代理物件實現的。
這裡介紹幾個非常重要的大咖,註釋是官方的,應該是最好的解釋了。
- 1.requestBuffer:字面意思就是請求buffer,留意一下它的引數,可以知道是請求序號為slot的buffer。如果返回值是無效的,必須在進行一次dequeueBuffer操作。
// requestBuffer returns the GraphicBuffer for slot N.
//
// In normal operation, this is called the first time slot N is returned
// by dequeueBuffer. It must be called again if dequeueBuffer returns
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
- 2.dequeueBuffer:重磅大咖,它的作用就是獲取下一塊可用buffer的slot索引index.
如果該index的buffer為NULL,則會自動申請buffer.其中輸出引數fence物件,用來同步該塊Buffer.Buffer的內容在fence訊號傳送之前是不能被重寫的。如果fence物件是Fence::NO_FENCE,則該塊buffer可以直接被重寫。
// dequeueBuffer gets the next buffer slot index for the producer to use.
// If a buffer slot is available then that slot index is written to the
// location pointed to by the buf argument and a status of OK is returned.
// If no slot is available then a status of -EBUSY is returned and buf is
// unmodified.
//
// The outFence parameter will be updated to hold the fence associated with
// the buffer. The contents of the buffer must not be overwritten until the
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
// written immediately.
//
// The width and height parameters must be no greater than the minimum of
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
// An error due to invalid dimensions might not be reported until
// updateTexImage() is called. If width and height are both zero, the
// default values specified by setDefaultBufferSize() are used instead.
//
// The pixel formats are enumerated in graphics.h, e.g.
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
// will be used.
//
// The usage argument specifies gralloc buffer usage flags. The values
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
// will be merged with the usage flags specified by setConsumerUsageBits.
//
// The return value may be a negative error value or a non-negative
// collection of flags. If the flags are set, the return values are
// valid, but additional actions must be performed.
//
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
// producer must discard cached GraphicBuffer references for the slot
// returned in buf.
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
// must discard cached GraphicBuffer references for all slots.
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
- 3.queueBuffer:當buffer填充完畢之後,生產者通過該介面將buffer歸還給BufferQueue,有些額外的資訊需要提供給bufferQueue,具體如下面介紹。尤其是時間戳必須提供BufferQueue.時間戳以納秒為單位,具有單調遞增屬性。這也就是在Camera preview過程中hal上傳的每一幀都包含時間戳資訊。
// queueBuffer returns a filled buffer to the BufferQueue.
//
// Additional data is provided in the QueueBufferInput struct. Notably,
// a timestamp must be provided for the buffer. The timestamp is in
// nanoseconds, and must be monotonically increasing. Its other semantics
// (zero point, etc) are producer-specific and should be documented by the
// producer.
//
// The caller may provide a fence that signals when all rendering
// operations have completed. Alternatively, NO_FENCE may be used,
// indicating that the buffer is ready immediately.
//
// Some values are returned in the output struct: the current settings
// for default width and height, the current transform hint, and the
// number of queued buffers.
virtual status_t queueBuffer(int slot,
const QueueBufferInput& input, QueueBufferOutput* output);
//需要提供的資料如下
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// crop - a crop rectangle that's used as a hint to the consumer
// scalingMode - a set of flags from NATIVE_WINDOW_SCALING_* in <window.h>
// transform - a set of flags from NATIVE_WINDOW_TRANSFORM_* in <window.h>
// async - if the buffer is queued in asynchronous mode
// fence - a fence that the consumer must wait on before reading the buffer,
// set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
// camera mode).
四,階段小結
這裡簡單畫了一張流程圖,來顯示生產者和消費者,bufferQueue如何工作的。圖中標號代表先後順序,其中特別的是queueBuffer和onFrameAvailable回撥是前後進行的,這裡就記作同一序號。
五、BufferQueue例項學習
下面是一個Android自帶的一個例項程式,不過本地變異要解決一些編譯錯誤問題,修改我也在程式碼後面貼出來了。在接下來的分析中,我們就隨著這個程式,簡單探索一下Bufferqueue是如何工作起來的。首先貼出完整的測試程式。
#include <cutils/memory.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <android/native_window.h>
#include <sys/time.h>
using namespace android;
//namespace android {
int main(int argc, char** argv)
{
// set up the thread-pool
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create a client to surfaceflinger
//過程1:我們詳細分析一下,它的過程。
sp<SurfaceComposerClient> client = new SurfaceComposerClient();
//過程2:下面我們會詳細分析,如果通過surfaceControl物件獲取surface的。
sp<SurfaceControl> surfaceControl = client->createSurface(String8("resize"),
160, 240, PIXEL_FORMAT_RGB_565, 0);
sp<Surface> surface = surfaceControl->getSurface();
//這裡像是開啟openGl的事務功能
SurfaceComposerClient::openGlobalTransaction();
//過程3:設定層數,見下面詳解
surfaceControl->setLayer(100000);
SurfaceComposerClient::closeGlobalTransaction();
ANativeWindow_Buffer outBuffer;
//過程4:dequeueBuffer 獲取buffer,見下面詳解
surface->lock(&outBuffer, NULL);
ALOGE("armwind,buffer_height:0x%x,buffer_width:0x%x,f:%d",outBuffer.height,outBuffer.width,outBuffer.format);
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
//這裡會設定160*240大小的紅色色塊。
android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
//過程5:queueBuffer buffer入buffer queue.
surface->unlockAndPost();
ALOGE("armwind,first sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
sleep(4);
surface->lock(&outBuffer, NULL);
//這裡會設定160*240大小的青黃色塊,不知道如何形容,請看下圖。下面的過程和上面的是一樣的,這裡就不介紹了。
android_memset16((uint16_t*)outBuffer.bits, 0x07E0, bpr*outBuffer.height);
surface->unlockAndPost();
ALOGE("armwind,second sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
sleep(4);
SurfaceComposerClient::openGlobalTransaction();
surfaceControl->setSize(2000, 1000);
SurfaceComposerClient::closeGlobalTransaction();
IPCThreadState::self()->joinThreadPool();
return 0;
}
//}
如果有程式碼的話,可以本地實驗一把。將編譯出來的可執行程式,push進自己開發板實驗看看。
- Makefile中存在的問題
diff --git a/Android.mk b/Android.mk
index 8a86970..e615e16 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,7 +8,8 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui \
- libgui
+ libgui \
+ libbinder
LOCAL_MODULE:= test-resize
- 原始碼中存在的問題
其中主要是沒有新增native_window.h標頭檔案和surface->lock
引數問題。其它都是本人新增log筆記。
diff --git a/resize.cpp b/resize.cpp
index 8b051e8..b4e22bc 100644
--- a/resize.cpp
+++ b/resize.cpp
@@ -24,10 +24,12 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <android/native_window.h>
+#include <sys/time.h>
using namespace android;
-namespace android {
+//namespace android {
int main(int argc, char** argv)
{
@@ -49,16 +51,21 @@ int main(int argc, char** argv)
ANativeWindow_Buffer outBuffer;
surface->lock(&outBuffer, NULL);
+ ALOGE("armwind,buffer_height:0x%x,buffer_width:0x%x,f:%d",outBuffer.height,outBuffer.width,outBuffer.format);
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
surface->unlockAndPost();
+ ALOGE("armwind,first sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
+ sleep(4);
- surface->lock(&outBuffer);
+ surface->lock(&outBuffer, NULL);
android_memset16((uint16_t*)outBuffer.bits, 0x07E0, bpr*outBuffer.height);
surface->unlockAndPost();
+ ALOGE("armwind,second sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
+ sleep(4);
SurfaceComposerClient::openGlobalTransaction();
- surfaceControl->setSize(320, 240);
+ surfaceControl->setSize(2000, 1000);
SurfaceComposerClient::closeGlobalTransaction();
@@ -66,3 +73,4 @@ int main(int argc, char** argv)
return 0;
}
+//}
mma命令編譯完成後,會在out/target/product/nanopi2/system/bin生成test-resize可執行程式(這裡我是nanopi2開發板)
E/ ( 1973): armwind,buffer_height:0xf0,buffer_width:0xa0,f:4
E/ ( 1973): armwind,first sleep 4s,addr:0xb6c80000,stride:160
E/ ( 1973): armwind,second sleep 4s,addr:0xb6c6d000,stride:160
效果圖如下,其實是有2個過程的,首先會顯示成紅色塊,4S後顯示成下面這種顏色,先用心去感受一下。
二、詳細分析
1.過程1分析-建立SurfaceComposerClient 代理物件
Composer是在ComposerService上又封裝了一下,Composer字面意思就是作曲家。這裡就是surface混合類。在過程1中,直接建立了surfaceComposerClient物件。建構函式中獲取Composer單例物件,可見一個程序只有一個composer物件。接下來在onFirstRef()做了很多。在進行一步步分析之前,先看一下類之間的關係,surfaceFlinger實現了ISurfaceComposer介面,而surfaceFlinger代理物件又儲存到ComposerService類中的mComposerService成員中。下面簡單畫了一個繼承和引用關係圖。
SurfaceComposerClient::SurfaceComposerClient()
: mStatus(NO_INIT), mComposer(Composer::getInstance())
{
}
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
sp<ISurfaceComposerClient> conn = sm->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
onFirstRef()函式是在我們使用sp智慧指標時,第一次引用該物件時會呼叫的函式。可以看到上面函式中主要是獲取surfaceflinger代理物件,然後通過該代理物件獲取一個BpSurfaceComposerClient代理物件,這裡隨著程式碼一步步跟進。
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
ComposerService& instance = ComposerService::getInstance();//這裡是一個單例,
Mutex::Autolock _l(instance.mLock);
if (instance.mComposerService == NULL) {
ComposerService::getInstance().connectLocked();//第一次建立會走這裡
assert(instance.mComposerService != NULL);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
//下面是composerService類的宣告,可以看到ComposerService繼承了單例類Singleton.
//那麼上面呼叫的connectLocked()方法,也是ComposerService.
class ComposerService : public Singleton<ComposerService>//注意這裡模板引數就是ComposerService自己。
//-------------------------------------------------------
void ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");//注意這裡的名字是"SurfaceFlinger"
//根據名字,從serviceManage查詢我們SurfaceFlinger服務。並將獲取到的代理物件儲存到mComposerService成員中
while (getService(name, &mComposerService) != NO_ERROR) {
usleep(250000);
}
assert(mComposerService != NULL);
//這裡還有一些死亡監聽物件類宣告和物件建立,這裡我們就不貼程式碼了。
}
經過上面的折騰,SurfaceFlinger的代理物件終於儲存到ComposerService::mComposerService成員中,詳細的註釋都新增到了程式碼中。這裡主要做的就是-通過單例介面getInstance(),獲取surfaceFlinger代理物件。
接下來SurfaceFlinger代理物件通過匿名binder通訊方式,獲取ISurfaceComposerClient代理物件。其實這裡和ComposerService類類似,將ISurfaceComposerClient物件儲存到成員mClient中。下面簡圖中的Client就是在createConnet過程中在SurfaceFlinger服務端new出來的。
virtual sp<ISurfaceComposerClient> createConnection()
{
uint32_t n;
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
上面是surfaceFlinger代理端實現的介面,將獲取到的binder物件轉換成ISurfaceComposerClient物件。下面是本地物件響應CREATE_CONNECTION訊息的地方。
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case CREATE_CONNECTION: {
//檢查SurfaceFlinger服務的名字是否正確。
CHECK_INTERFACE(ISurfaceComposer, data, reply);
//直接呼叫SurfaceFlinger實現的介面。
sp<IBinder> b = createConnection()->asBinder();
//該操作會在kernel生成一個Binder_node和Binder_ref物件,其中binder_ref就會傳送給應用程序端。
reply->writeStrongBinder(b);
return NO_ERROR;
//......
}
//surfaceFlinger中createConnection實現
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
sp<ISurfaceComposerClient> bclient;
//new了SurfaceFlinger端的Client,同時將surfaceFlinger物件傳進去了,這裡Client物件內部會回撥SurfaceFlinger的介面。
sp<Client> client(new Client(this));
status_t err = client->initCheck();
if (err == NO_ERROR) {
bclient = client;
}
//clinet程序實現了ISurfaceComposerClient介面,這裡返回到SurfaceFlinger本地物件中,寫給引用Clinet端。
return bclient;
}
上面與SurfaceFlinger建立連線後,會建立一個對應當前應用程序的Client程序。需要特別注意這裡,下面會介紹Client和Layer的關係。到目前為止,我們需要知道已經建立哪幾個物件。下圖中左側是應用程序端的物件,右側是在SurfaceFlinger服務中存在的物件。其中SurfaceComposerclient物件包含Composer(可以理解成surfaceFlinger代理物件)和ISurfaceComposerClient代理物件。
2.過程2分析-createSurface()建立surface.
- (1) SurfaceComposerClient->createSurface()
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);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);
}
}
return sur;
}
上面createSurface介面呼叫ISurfaceComposerClient代理物件createSurface的介面。注意這裡建立3個區域性變數sur,handle和gbp,其中sur是管理surface的類,handle可以理解成該buffer的id,gbp是buffer生產者代理物件。
- (2) createSurface代理端介面實現
原始碼:frameworks/native/libs/gui/ISurfaceComposerClient.cpp
virtual status_t createSurface(const String8& name, uint32_t w,
uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
data.writeString8(name);
data.writeInt32(w);
data.writeInt32(h);
data.writeInt32(format);
data.writeInt32(flags);
remote()->transact(CREATE_SURFACE, data, &reply);
*handle = reply.readStrongBinder();
*gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
return reply.readInt32();
}
上面代理端只是做了資料轉發。往下看
- (3) createSurface本地端介面實現
creteSurface的本地端介面還是呼叫了Client實現的CreteSurface介面.
原始碼路徑:frameworks/native/services/surfaceflinger/Client.cpp
status_t Client::createSurface(
const String8& name,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp)
{
/*
* createSurface must be called from the GL thread so that it can
* have access to the GL context.
*/
class MessageCreateLayer : public MessageBase {
SurfaceFlinger* flinger;
Client* client;
sp<IBinder>* handle;
sp<IGraphicBufferProducer>* gbp;
status_t result;
const String8& name;
uint32_t w, h;
PixelFormat format;
uint32_t flags;
public:
MessageCreateLayer(SurfaceFlinger* flinger,
const String8& name, Client* client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp)
: flinger(flinger), client(client),
handle(handle), gbp(gbp),
name(name), w(w), h(h), format(format), flags(flags) {
}
status_t getResult() const { return result; }
virtual bool handler() {
//最關鍵就是在這裡,建立CreateLayer.還記得我們前面Reszie程序中申請的surface大小嗎?
//w=160, h=240, format=PIXEL_FORMAT_RGB_565, flags=0
result = flinger->createLayer(name, client, w, h, format, flags,
handle, gbp);
return true;
}
};
//這裡定義了一個內部建立Layer的訊息類,將該訊息類物件傳送給SurfaceFlinger物件處理createLayer請求。
//注意這裡client物件this,就是上面createConnection中new出來的client,先留意一下。
sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
name, this, w, h, format, flags, handle, gbp);
mFlinger->postMessageSync(msg);//傳送給SurfaceFlinger物件處理。
return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}
本地端的createSurface內部定義了內部類MessageCreateLayer,而需要我們關心的是handler()實現。在handler中直接呼叫了SurfaceFlinger物件的CreateLayer介面來建立一個layer物件,由此可想而知,應用端的surface物件對應的就是layer物件。一個surface物件對應一個Layer物件,而一個Layer又對應多個buffer,Android5.1每一個Layer最多有64個mSlot。
status_t SurfaceFlinger::createLayer(
const String8& name,
const sp<Client>& client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
//記得這個時候w=160,h=240,format=PIXEL_FORMAT_RGB_565,flag=0
//省掉一些檢查寬高檢查程式碼----------------
status_t result = NO_ERROR;
sp<Layer> layer;
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { //flag = 0
case ISurfaceComposerClient::eFXSurfaceNormal:
//上面flag=0,直接走到這裡了。w=160,h=240,format=PIXEL_FORMAT_RGB_565,flag=0
result = createNormalLayer(client,
name, w, h, flags, format,
handle, gbp, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceDim:
//這個就是建立不需要顯示出來的Layer。
result = createDimLayer(client,
name, w, h, flags,
handle, gbp, &layer);
break;
default:
result = BAD_VALUE;
break;
}
if (result == NO_ERROR) {
addClientLayer(client, *handle, *gbp, layer);
setTransactionFlags(eTransactionNeeded);
}
return result;
}
該函式算主要做了下面幾件事情:
- 1.首先會檢查寬高是否合法,然後根據我們傳進來的flag,來建立需要顯示的surface還是不需要顯示的surface。
- 2.createNormalLayer()函式中建立Layer物件和handle物件。其中handle物件表示該Layer物件的控制代碼。
- 3.addClientLayer()最後將新建立的Layer物件和handle物件捆綁,此外將該layer物件和BufferProducer物件儲存到SurfaceFlinger相應的陣列中,便於後面查詢。
void SurfaceFlinger::addClientLayer(const sp<Client>& client,
const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc,
const sp<Layer>& lbc)
{
// attach this layer to the client
client->attachLayer(handle, lbc);
// add this layer to the current state list
Mutex::Autolock _l(mStateLock);
mCurrentState.layersSortedByZ.add(lbc);//儲存到SurfaceFlinger物件中
mGraphicBufferProducerList.add(gbc->asBinder());//記錄BufferProducer物件
}
到這裡應用Client端請求createSurface請求已經處理完了,請求處理過程中,又誕生了下面幾個物件。
- Layer:一個Layer對應一個surface物件。
- buffer_producer本地物件:該物件是在建立Layer物件同時建立的。建立該物件便於應用端獲取buffer,應用Client端有了buffer_producer代理物件後,就可以detachBuffer,queueBuffer了。
- buffer_consumer本地物件:該物件目前我的發現是由於buffer_consumer本地物件實現了FrameAvailableListener介面中的onFrameAvailable(),這樣的話,應用端拿到buffer_consumer代理物件後,當幀可用後就可以通知surfaceFliner去合成layer了。
status_t BnSurfaceComposerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case CREATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
String8 name = data.readString8();
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
PixelFormat format = data.readInt32();
uint32_t flags = data.readInt32();
sp<IBinder> handle;
sp<IGraphicBufferProducer> gbp;
status_t result = createSurface(name, w, h, format, flags,
&handle, &gbp);
//匿名binder寫入,會在kernel中生成對應的binder_node.
reply->writeStrongBinder(handle);
//匿名binder寫入,會在kernel中生成對應的binder_node.
reply->writeStrongBinder(gbp->asBinder());
reply->writeInt32(result);//寫入返回值
return NO_ERROR;
} break;
//.......
}
與此同時,應用端這樣獲取的。
remote()->transact(CREATE_SURFACE, data, &reply);
*handle = reply.readStrongBinder();
//這裡直接就轉換成IGraphicBufferProducer代理物件了。
*gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
return reply.readInt32();//返回結果
到這裡我們返回到SurfaceComposerClient物件的createSurface物件中
sp<SurfaceControl> SurfaceComposerClient::createSurface(.....)
{
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);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);//這裡根據生產者代理物件建立了SurfaceControl物件,最好這裡追追原始碼看看。
}
}
return sur;
}
可以發現上面獲取到handle物件和BufferProducer代理物件後,直接出入SurfaceControl建構函式中,獲取到了一個surface物件。
SurfaceControl::SurfaceControl(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbp)
: mClient(client), mHandle(handle), mGraphicBufferProducer(gbp)
{
}
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
// This surface is always consumed by SurfaceFlinger, so the
// producerControlledByApp value doesn't matter; using false.
//看到了吧,這裡直接new的,666666
mSurfaceData = new Surface(mGraphicBufferProducer, false);
}
return mSurfaceData;
}
上面可以看到getSurface介面,根據bufferProducer代理物件建立了surface物件。這樣有了mGraphicBufferProducer代理物件後,後續申請buffer就方便了。生產者代理物件,本地物件,Layer物件的關係如下圖所示。
4.過程3-設定層數
該過程是通過SurfaceControl物件設定當前surface所在層數,而SurfaceControl物件又通過SurfaceComposerClient代理物件來完成的。
status_t SurfaceControl::setLayer(int32_t layer) {
status_t err = validate();