前言
經(jīng)過上一篇對(duì)OpenGL es的解析先匪,我們引出了在eglSwapBuffer時(shí)候會(huì)調(diào)用會(huì)調(diào)用兩個(gè)關(guān)鍵的方法:
- 1.Surface::dequeueBuffer
- 2.Surface::queueBuffer
從上一篇openGL es分析可以得出种吸,每一次當(dāng)我們繪制完一次圖元之后,surface做為生產(chǎn)者一方會(huì)在一個(gè)循環(huán)中一般依次完成如下內(nèi)容:
- 1.dequeueBuffer 獲取一個(gè)圖元的插槽位置呀非,或者生產(chǎn)一個(gè)圖元
- 2.lock 鎖定圖元
- 3.queueBuffer 把圖元放入緩沖隊(duì)列中
- 4.unlock 解鎖圖元
對(duì)于生產(chǎn)者來說關(guān)鍵的是這四個(gè)步驟坚俗。不過openGL es把整個(gè)過程顛倒,每一次繪制上一幀岸裙,對(duì)于更加好理解猖败,我把整個(gè)過程設(shè)置回Android常用的方式。我們分別來研究這幾個(gè)函數(shù)做了什么降允。
遇到什么問題恩闻,歡迎來本文進(jìn)行討論http://www.reibang.com/p/3bfc0053d254
正文
首先我們先不去深究細(xì)節(jié),先對(duì)整個(gè)流程的源碼流程有一個(gè)大體印象剧董。因?yàn)閳D元的誕生不清楚幢尚,也看不懂其他原理。
egl lock 鎖定圖元
文件:/frameworks/native/opengl/libagl/egl.cpp
status_t egl_window_surface_v2_t::lock(
ANativeWindowBuffer* buf, int usage, void** vaddr)
{
auto& mapper = GraphicBufferMapper::get();
return mapper.lock(buf->handle, usage,
android::Rect(buf->width, buf->height), vaddr);
}
在lock函數(shù)實(shí)際上是把ANativeWindowBuffer的handle傳進(jìn)去進(jìn)行鎖定送滞,同時(shí)傳入了一個(gè)vaddr的地址瘤泪,這個(gè)地址是做什么的呢今布?其實(shí)就是共享buffer中的圖元存儲(chǔ)的地址僧须。
實(shí)際上上在lock的時(shí)候泡徙,并不是直接把buffer傳下去掏击,而是傳遞一個(gè)handle层扶,一個(gè)ANativeWindowBuffer的句柄瓜饥。
dequeueBuffer 獲取一個(gè)圖元的插槽位置嗤堰,或者生產(chǎn)一個(gè)圖元
文件:/frameworks/native/libs/gui/Surface.cpp
先介紹Surface的核心對(duì)象之一mSlot园爷,這個(gè)對(duì)象是數(shù)組BufferSlot:
struct BufferSlot {
BufferSlot()
: mGraphicBuffer(nullptr),
mEglDisplay(EGL_NO_DISPLAY),
mBufferState(),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mFence(Fence::NO_FENCE),
mAcquireCalled(false),
mNeedsReallocation(false) {
}
sp<GraphicBuffer> mGraphicBuffer;
EGLDisplay mEglDisplay;
BufferState mBufferState;
bool mRequestBufferCalled;
uint64_t mFrameNumber;
EGLSyncKHR mEglFence;
sp<Fence> mFence;
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
bool mNeedsReallocation;
};
在這里面保存著幾個(gè)很重要對(duì)象:
- 1.GraphicBuffer 圖元緩沖對(duì)象
- 2.mEglDisplay opengl es的屏幕對(duì)象宠蚂,實(shí)際上就是egl_display_t
- 3.BufferState 圖元狀態(tài)
- 4.EGLSyncKHR opengl es的同步柵
- 5.Fence 同步柵
在這里先介紹一個(gè)重要的概念,每一個(gè)GraphicBuffer圖元在不同的流程會(huì)分為5個(gè)狀態(tài)都會(huì)在BufferState記錄狀態(tài):
- 1.free 圖元是自由的等待dequeue使用
- 2.dequeue SF中緩沖隊(duì)列的插槽對(duì)應(yīng)index的圖元要么找到一個(gè)free的圖元童社,要么就申請(qǐng)一個(gè)出來求厕。
- 3.queue 從dequeue出來的圖元,經(jīng)過queueBuffer進(jìn)入到SF進(jìn)行刷新界面時(shí)候讀取出來進(jìn)行渲染
- 4.acquire 當(dāng)SF的圖元消費(fèi)者進(jìn)行消費(fèi)之后,將會(huì)把這個(gè)狀態(tài)設(shè)置為Acquire
- 4.share 共享圖元 這種模式比較特殊呀癣,這種模式下能夠和其他三個(gè)模式并存美浦,只是這種模式下只會(huì)整個(gè)Layer進(jìn)行繪制使用同一個(gè)圖元繪制。
根據(jù)這些狀態(tài)项栏,在SF中對(duì)應(yīng)的計(jì)數(shù)個(gè)數(shù)不一樣浦辨,這些計(jì)數(shù)影響著SF是否需要調(diào)整整個(gè)mSlot的使用策略。
圖元狀態(tài) | mShared | mDequeueCount | mQueueCount | mAcquireCount |
---|---|---|---|---|
FREE | false | 0 | 0 | 0 |
DEQUEUED | false | 1 | 0 | 0 |
QUEUED | false | 0 | 1 | 0 |
ACQUIRED | false | 0 | 0 | 1 |
SHARED | true | any | any | any |
- 1.mShared 代表該圖元是共享的
- 2.mDequeueCount 有多少圖元是否出隊(duì)沼沈,被應(yīng)用程序正在處理
- 3.mQueueCount 有多少圖元已經(jīng)入隊(duì)流酬,正在等待被消費(fèi)者消費(fèi)
- 4.mAcquireCount 有多少圖元正在被消費(fèi)。
因此當(dāng)我們需要進(jìn)行調(diào)整列另,需要對(duì)mDequeueCount+mAcquireCount加入調(diào)整計(jì)算芽腾,這樣才能知道一共有多少圖元在緩沖隊(duì)伍之外,才能正確的計(jì)算访递,是否應(yīng)該調(diào)整BufferQueue.mSlot的策略晦嵌。在圖元緩沖隊(duì)列初始化那一章中,能看到會(huì)計(jì)算mMaxAcquiredBufferCount和mMaxDequeuedBufferCount的數(shù)量拷姿,來控制每一個(gè)Layer的圖元生產(chǎn)者的是否需要調(diào)整slot為新的GraphicBuffer騰出位置惭载。
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
uint32_t reqWidth;
uint32_t reqHeight;
PixelFormat reqFormat;
uint64_t reqUsage;
bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
reqWidth = mReqWidth ? mReqWidth : mUserWidth;
reqHeight = mReqHeight ? mReqHeight : mUserHeight;
reqFormat = mReqFormat;
reqUsage = mReqUsage;
enableFrameTimestamps = mEnableFrameTimestamps;
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
if (gbuf != NULL) {
*buffer = gbuf.get();
*fenceFd = -1;
return OK;
}
}
} // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
int buf = -1;
sp<Fence> fence;
nsecs_t startTime = systemTime();
FrameEventHistoryDelta frameTimestamps;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps ? &frameTimestamps
: nullptr);
mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
...
return result;
}
if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
...
return FAILED_TRANSACTION;
}
Mutex::Autolock lock(mMutex);
// Write this while holding the mutex
mLastDequeueStartTime = startTime;
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
...
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if (enableFrameTimestamps) {
mFrameEventHistory->applyDelta(frameTimestamps);
}
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
if (mReportRemovedBuffers && (gbuf != nullptr)) {
mRemovedBuffers.push_back(gbuf);
}
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
...
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
...
}
} else {
*fenceFd = -1;
}
*buffer = gbuf.get();
if (mSharedBufferMode && mAutoRefresh) {
mSharedBufferSlot = buf;
mSharedBufferHasBeenQueued = false;
} else if (mSharedBufferSlot == buf) {
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
}
return OK;
}
流程如下:
- 1.如果是共享圖元模式,則只會(huì)獲取mSharedBufferSlot記錄的在共享圖元在mShared的位置响巢,直接返回對(duì)的index描滔。
- 2.調(diào)用SF的dequeueBuffer方法,在SF嘗試的獲取圖元對(duì)應(yīng)的位置踪古。
- 3.如果返回的result超出了NUM_BUFFER_SLOTS含长,則返回一場。判斷返回的命令決定是否釋放在客戶端中的mSlot
- 4.如果判斷到BUFFER_NEEDS_REALLOCATION需要重新申請(qǐng)圖元伏穆,則調(diào)用requestBuffer拘泞,拿到新申請(qǐng)圖元的保存到客戶端進(jìn)程。
讓我們重點(diǎn)關(guān)注SF的dequeueBuffer枕扫。
BufferQueueProducer dequeueBuffer
文件:/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) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
if (mCore->mIsAbandoned) {
return NO_INIT;
}
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
return NO_INIT;
}
} // Autolock scope
if ((width && !height) || (!width && height)) {
return BAD_VALUE;
}
status_t returnFlags = NO_ERROR;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
if (format == 0) {
format = mCore->mDefaultBufferFormat;
}
// Enable the usage bits the consumer requested
usage |= mCore->mConsumerUsageBits;
const bool useDefaultSize = !width && !height;
if (useDefaultSize) {
width = mCore->mDefaultWidth;
height = mCore->mDefaultHeight;
}
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
&found);
if (status != NO_ERROR) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
return -EBUSY;
}
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (!mCore->mAllowAllocation) {
if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
if (mCore->mSharedBufferSlot == found) {
return BAD_VALUE;
}
mCore->mFreeSlots.insert(found);
mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot == found &&
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
return BAD_VALUE;
}
if (mCore->mSharedBufferSlot != found) {
mCore->mActiveBuffers.insert(found);
}
*outSlot = found;
ATRACE_BUFFER_INDEX(found);
attachedByConsumer = mSlots[found].mNeedsReallocation;
mSlots[found].mNeedsReallocation = false;
mSlots[found].mBufferState.dequeue();
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
mCore->mIsAllocating = true;
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
// We add 1 because that will be the frame number when this buffer
// is queued
mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
}
if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
...
}
eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
// Don't return a fence in shared buffer mode, except for the first
// frame.
*outFence = (mCore->mSharedBufferMode &&
mCore->mSharedBufferSlot == found) ?
Fence::NO_FENCE : mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
BufferQueueCore::INVALID_BUFFER_SLOT) {
mCore->mSharedBufferSlot = found;
mSlots[found].mBufferState.mShared = true;
}
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
}
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
return error;
}
if (mCore->mIsAbandoned) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
return NO_INIT;
}
VALIDATE_CONSISTENCY();
} // Autolock scope
}
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
1000000000);
if (result == EGL_FALSE) {
BQ_LOGE("dequeueBuffer: error %#x waiting for fence",
eglGetError());
} else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
BQ_LOGE("dequeueBuffer: timeout waiting for fence");
}
eglDestroySyncKHR(eglDisplay, eglFence);
}
...
if (outBufferAge) {
*outBufferAge = mCore->mBufferAge;
}
addAndGetFrameTimestamps(nullptr, outTimestamps);
return returnFlags;
}
- waitWhileAllocatingLocked 如果其他線程進(jìn)入這個(gè)方法陪腌,會(huì)等待BufferQueueProducer幫其他應(yīng)用進(jìn)程申請(qǐng)完之后才能繼續(xù)走下去。
- waitForFreeSlotThenRelock 不斷的查找BufferQueueCore的Slot中空閑的插槽位置烟瞧,并且取出BufferSlot(在SF中也有一個(gè)BufferSlot對(duì)應(yīng)記錄)中的GraphicBuffer的index(found)诗鸭,判斷當(dāng)前這個(gè)圖元是否寬高,像素格式是否和請(qǐng)求一樣参滴,不一樣則需要重新請(qǐng)求强岸,則在返回碼添加BUFFER_NEEDS_REALLOCATION。
- 先把found的index添加到mActiveBuffer 區(qū)間砾赔,標(biāo)示為活躍狀態(tài),并且設(shè)置為dequeue狀態(tài)蝌箍。接著如果找到的GraphicBuffer是空的青灼,或者需要重新申請(qǐng),則把設(shè)置到BufferSlot的參數(shù)全部初始化十绑。
- 發(fā)現(xiàn)打開了BUFFER_NEEDS_REALLOCATION標(biāo)志位聚至,就在SF中申請(qǐng)一個(gè)新的GraphicBuffer,接著調(diào)用GraphicBuffer的initCheck進(jìn)行校驗(yàn)本橙。拿到found的下標(biāo)扳躬,把新的GraphicBuffer 加入到mSlot。調(diào)用clearBufferSlotLocked甚亭,初始化clearBufferSlotLocked和里面的參數(shù)贷币。
- addAndGetFrameTimestamps 這個(gè)方法在dequeuebuffer沒有意義。
在這個(gè)過程中其實(shí)很簡單亏狰,就是找到合適的空位役纹,添加到活躍區(qū)間,設(shè)置標(biāo)志位暇唾,最后發(fā)現(xiàn)為空則會(huì)新生成一個(gè)促脉,最后返回的是mSlot對(duì)應(yīng)位置的下標(biāo)。而不會(huì)直接返回一個(gè)完整的GraphicBuffer策州,因?yàn)橐粋€(gè)圖元太大了瘸味,根本不可能通過Binder進(jìn)行通信。
我們來看看waitForFreeSlotThenRelock是怎么從mSlot找到合適位置的圖元插槽够挂。
BufferQueueProducer waitForFreeSlotThenRelock
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
int* found) const {
auto callerString = (caller == FreeSlotCaller::Dequeue) ?
"dequeueBuffer" : "attachBuffer";
bool tryAgain = true;
while (tryAgain) {
if (mCore->mIsAbandoned) {
return NO_INIT;
}
int dequeuedCount = 0;
int acquiredCount = 0;
for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isDequeued()) {
++dequeuedCount;
}
if (mSlots[s].mBufferState.isAcquired()) {
++acquiredCount;
}
}
if (mCore->mBufferHasBeenQueued &&
dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
...
return INVALID_OPERATION;
}
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
...
const int maxBufferCount = mCore->getMaxBufferCountLocked();
bool tooManyBuffers = mCore->mQueue.size()
> static_cast<size_t>(maxBufferCount);
if (tooManyBuffers) {
...
} else {
...
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = mCore->mSharedBufferSlot;
} else {
if (caller == FreeSlotCaller::Dequeue) {
// If we're calling this from dequeue, prefer free buffers
int slot = getFreeBufferLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else if (mCore->mAllowAllocation) {
*found = getFreeSlotLocked();
}
} else {
// If we're calling this from attach, prefer free slots
int slot = getFreeSlotLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else {
*found = getFreeBufferLocked();
}
}
}
}
tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
tooManyBuffers;
if (tryAgain) {
if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
if (mDequeueTimeout >= 0) {
status_t result = mCore->mDequeueCondition.waitRelative(
mCore->mMutex, mDequeueTimeout);
if (result == TIMED_OUT) {
return result;
}
} else {
mCore->mDequeueCondition.wait(mCore->mMutex);
}
}
} // while (tryAgain)
return NO_ERROR;
}
- 1.每一次進(jìn)入這個(gè)方法旁仿,都會(huì)先循環(huán)檢測mActiveBuffers中所有所有保存的GraphicBuffer對(duì)象的狀態(tài),計(jì)算已經(jīng)出隊(duì)dequeue和消費(fèi)acquire的數(shù)目孽糖,并且做校驗(yàn)dequeue是否大于mMaxDequeuedBufferCount數(shù)目枯冈,超出了就不能計(jì)算。還要校驗(yàn)整個(gè)Queue大小是否已經(jīng)比maxBufferCount的限制办悟。
- 2.如果判斷在dequeue執(zhí)行狀態(tài)尘奏,則會(huì)從先從mFreeBuffer找,找不到再從mFreeSlots中查找病蛉。
- 3.最后喚醒其他要出隊(duì)到應(yīng)用的線程炫加。
BufferQueueProducer requestBuffer
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
...
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
在這個(gè)過程中,很簡單铡恕,直接返回一個(gè)GraphicBuffer對(duì)象琢感。不是說GraphicBuffer很大丢间,Binder沒有辦法傳輸嗎探熔?為什么這里又能返回到app進(jìn)程呢?稍后解析烘挫。這里就能Surface就記錄了對(duì)應(yīng)index的GraphicBuffer诀艰。
Surface queueBuffer
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
if (fenceFd >= 0) {
close(fenceFd);
}
return i;
}
if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
if (fenceFd >= 0) {
close(fenceFd);
}
return OK;
}
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop(Rect::EMPTY_RECT);
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform,
mEnableFrameTimestamps);
input.setHdrMetadata(mHdrMetadata);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
} else {
int width = buffer->width;
int height = buffer->height;
bool rotated90 = (mTransform ^ mStickyTransform) &
NATIVE_WINDOW_TRANSFORM_ROT_90;
if (rotated90) {
std::swap(width, height);
}
Region flippedRegion;
for (auto rect : mDirtyRegion) {
int left = rect.left;
int right = rect.right;
int top = height - rect.bottom; // Flip from OpenGL convention
int bottom = height - rect.top; // Flip from OpenGL convention
switch (mTransform ^ mStickyTransform) {
case NATIVE_WINDOW_TRANSFORM_ROT_90: {
// Rotate 270 degrees
Rect flippedRect{top, width - right, bottom, width - left};
flippedRegion.orSelf(flippedRect);
break;
}
case NATIVE_WINDOW_TRANSFORM_ROT_180: {
// Rotate 180 degrees
Rect flippedRect{width - right, height - bottom,
width - left, height - top};
flippedRegion.orSelf(flippedRect);
break;
}
case NATIVE_WINDOW_TRANSFORM_ROT_270: {
// Rotate 90 degrees
Rect flippedRect{height - bottom, left,
height - top, right};
flippedRegion.orSelf(flippedRect);
break;
}
default: {
Rect flippedRect{left, top, right, bottom};
flippedRegion.orSelf(flippedRect);
break;
}
}
}
input.setSurfaceDamage(flippedRegion);
}
nsecs_t now = systemTime();
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
mLastQueueDuration = systemTime() - now;
...
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
std::make_shared<FenceTime>(std::move(fence)));
...
mFrameEventHistory->updateSignalTimes();
}
mLastFrameNumber = mNextFrameNumber;
mDefaultWidth = output.width;
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
mTransformHint = output.transformHint;
}
mConsumerRunningBehind = (output.numPendingBuffers >= 2);
if (!mConnectedToCpu) {
// Clear surface damage back to full-buffer
mDirtyRegion = Region::INVALID_REGION;
}
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
mSharedBufferHasBeenQueued = true;
}
mQueueBufferCondition.broadcast();
return err;
}
流程如下:
- 1.獲取當(dāng)前的時(shí)間戳柬甥,等著作為參數(shù)傳出去。通過getSlotFromBufferLocked檢測GraphicBuffer有沒有保存在Surface的mSlots中其垄,沒有就不能進(jìn)行下一步苛蒲。
- 2.設(shè)置所有的必須的參數(shù)給QueueBufferInput。如crop裁剪區(qū)域绿满,fence同步柵臂外。如果打開了硬件加速,則會(huì)設(shè)置Surface的Image喇颁。如果發(fā)現(xiàn)旋轉(zhuǎn)角度漏健,則會(huì)旋轉(zhuǎn)像素區(qū)域,最后進(jìn)行合并橘霎。最后設(shè)置到input蔫浆。
- 3.調(diào)用BufferQueueProducer的queueBuffer,把圖元對(duì)應(yīng)的下標(biāo)index設(shè)置進(jìn)BufferQueueProducer中處理姐叁。
- 4.更新mFrameEventHistory數(shù)據(jù)瓦盛。
- 5.設(shè)置framebuffer,記錄前后兩幀在對(duì)應(yīng)index外潜;記錄入隊(duì)消耗的時(shí)間等輔助參數(shù)原环。
BufferQueueProducer queueBuffer
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
...
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;
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
...
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:
...
return BAD_VALUE;
}
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
...
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
BufferQueueCore::INVALID_BUFFER_SLOT) {
mCore->mSharedBufferSlot = slot;
mSlots[slot].mBufferState.mShared = true;
}
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
Rect croppedRect(Rect::EMPTY_RECT);
crop.intersect(bufferRect, &croppedRect);
if (dataSpace == HAL_DATASPACE_UNKNOWN) {
dataSpace = mCore->mDefaultBufferDataSpace;
}
mSlots[slot].mFence = acquireFence;
mSlots[slot].mBufferState.queue();
++mCore->mFrameCounter;
currentFrameNumber = mCore->mFrameCounter;
mSlots[slot].mFrameNumber = currentFrameNumber;
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 ||
mCore->mDequeueBufferCannotBlock ||
(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()) {
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
} else {
const BufferItem& last = mCore->mQueue.itemAt(
mCore->mQueue.size() - 1);
if (last.mIsDroppable) {
if (!last.mIsStale) {
mSlots[last.mSlot].mBufferState.freeQueued();
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;
}
}
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
frameReplacedListener = mCore->mConsumerListener;
} else {
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
}
}
mCore->mBufferHasBeenQueued = true;
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = 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()));
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
VALIDATE_CONSISTENCY();
} // Autolock scope
if (!mConsumerIsSurfaceFlinger) {
item.mGraphicBuffer.clear();
}
item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
int connectedApi;
sp<Fence> lastQueuedFence;
{ // scope for the lock
Mutex::Autolock lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(mCallbackMutex);
}
if (frameAvailableListener != NULL) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
mCallbackCondition.broadcast();
}
// Wait without lock held
if (connectedApi == NATIVE_WINDOW_API_EGL) {
lastQueuedFence->waitForever("Throttling EGL Production");
}
// 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);
return NO_ERROR;
}
核心有四個(gè):
- 1.拿到當(dāng)前需要入隊(duì)的圖元,并且把QueueBufferInput設(shè)置的參數(shù)全部取出橡卤,設(shè)置到BufferItem中扮念,接著設(shè)置到BufferQueueCore的mQueue中,mQueue為如下類型:
typedef Vector<BufferItem> Fifo;
其實(shí)在這個(gè)階段判斷mQueue如果為空碧库,直接加到mQueue的末尾柜与。不為空,需要判斷最后一個(gè)圖元是否已經(jīng)不需要顯示了嵌灰,如果是共享模式的圖元弄匕,則關(guān)閉。不是沽瞭,則會(huì)從Active區(qū)域移除迁匠,放到Free區(qū)域中,并且代替mQueue最后一個(gè)圖元驹溃。否則還是放到mQueue末尾城丧。
2.設(shè)置QueueBufferOutput參數(shù),能看到nextframebuffer豌鹤,就是BufferQueueCore中包含者幀數(shù)+1亡哄。
3.調(diào)用frameAvailableListener回調(diào)。這個(gè)回調(diào)在緩沖隊(duì)列初始化有專門介紹過布疙。是通知消費(fèi)者可以進(jìn)行消費(fèi)圖元的回調(diào)蚊惯。
- addAndGetFrameTimestamps 更新當(dāng)前幀時(shí)間戳愿卸。
addAndGetFrameTimestamps
nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
NewFrameEventsEntry newFrameEventsEntry = {
currentFrameNumber,
postedTime,
requestedPresentTimestamp,
std::move(acquireFenceTime)
};
addAndGetFrameTimestamps(&newFrameEventsEntry,
getFrameTimestamps ? &output->frameTimestamps : nullptr);
- 1.currentFrameNumber 每當(dāng)進(jìn)入queueBuffer,就會(huì)自動(dòng)添加一次mFrameCounter截型,這個(gè)參數(shù)代表這是當(dāng)前Surface誕生以來第幾幀趴荸。
- 2.postedTime 完成queue時(shí)候的時(shí)間。
- 3.requestedPresentTimestamp 應(yīng)用端調(diào)用Binder通信時(shí)候的時(shí)刻宦焦。
- 4.acquireFenceTime 一個(gè)同步柵
void BufferQueueProducer::addAndGetFrameTimestamps(
const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
if (newTimestamps == nullptr && outDelta == nullptr) {
return;
}
sp<IConsumerListener> listener;
{
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != NULL) {
listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
}
此時(shí)就是回調(diào)到消費(fèi)者中的監(jiān)聽回調(diào)发钝,具體做了什么之后再說。
unlock 解鎖圖元
文件:/frameworks/native/opengl/libagl/egl.cpp
status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf)
{
if (!buf) return BAD_VALUE;
auto& mapper = GraphicBufferMapper::get();
return mapper.unlock(buf->handle);
}
能看到GraphicBufferMapper調(diào)用以ANativeWindowBuffer的handle為線索unlock解鎖圖元映射波闹。
小結(jié)
在整個(gè)流程中笼平,我們能夠看到生產(chǎn)者生產(chǎn)涉及到的主要角色如下:
- 1.GraphicBufferMapper
- 2.BufferQueueProducer
- 3.Surface(ANativeWindow)
- 4.GraphicBuffer(ANativeWindowBuffer)
Surface是面向應(yīng)用客戶端的圖元生產(chǎn)者,BufferQueueProducer是面向SF服務(wù)端的圖元生產(chǎn)者舔痪。其核心涉及實(shí)際是查找mSlot中有沒有空閑的位置寓调,讓圖元占用。但是真正進(jìn)行消費(fèi)的時(shí)候锄码,需要設(shè)置到BufferItem的Vector中夺英。
但是思考過沒有,一個(gè)圖元代表一幀的數(shù)據(jù)滋捶。一個(gè)屏幕常見的占用的內(nèi)存108019204 早就超過了應(yīng)用傳輸Binder的極限1040k.那么系統(tǒng)是怎么規(guī)避這個(gè)問題呢痛悯?
我們從dequeue步驟中能看到,每一次dequeue之后先回返回一個(gè)mSlot的下標(biāo)重窟,即使在這個(gè)步驟已經(jīng)new了一個(gè)GraphicBuffer载萌,他也不會(huì)返回GraphicBuffer。但是到了requestBuffer就能GraphicBuffer對(duì)象巡扇。為什么這么設(shè)計(jì)扭仁?就算是返回了GraphicBuffer對(duì)象,Binder會(huì)因?yàn)檫@個(gè)對(duì)象占用太大而報(bào)錯(cuò)厅翔。
系統(tǒng)是怎么辦到的乖坠?而且在OpenGL es中eglSwapBuffers中,把framebuffer_t和ANativeWindowBuffer的bit屬性關(guān)聯(lián)起來刀闷,ANativeWindowBuffer又是怎么在跨進(jìn)程通信初始化bit字段的熊泵?
接下來讓我們專門來解析GraphicBuffer類。
GraphicBuffer 初始化
文件:/frameworks/native/include/ui/GraphicBuffer.h
先來看看其繼承關(guān)系:
class GraphicBuffer
: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,
public Flattenable<GraphicBuffer>
GraphicBuffer繼承于ANativeWindowBuffer和Flattenable甸昏,前者是在ANativeWindow中的圖元緩沖顽分,后者是Binder 傳輸時(shí)候的Parcel封裝IBinder。但是這里里面的flattern和unflattern方法被重寫了為自己的保存所有參數(shù)的方法施蜜。我們稍后再看卒蘸。
文件:/frameworks/native/libs/ui/GraphicBuffer.cpp
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName)
: GraphicBuffer()
{
mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
usage, std::move(requestorName));
}
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));
if (err == NO_ERROR) {
mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
format = inFormat;
layerCount = inLayerCount;
usage = inUsage;
usage_deprecated = int(usage);
stride = static_cast<int>(outStride);
}
return err;
}
在初始化中有一個(gè)十分核心的類GraphicBufferAllocator,圖元申請(qǐng)器花墩。這個(gè)類真正在一個(gè)GraphicBuffer的殼內(nèi)悬秉,通過allocate真正生成一個(gè)核心內(nèi)存塊。接著會(huì)調(diào)用GraphicBufferMapper. getTransportSize在Mapper中記錄大小冰蘑。請(qǐng)注意和泌,allocate方法中有一個(gè)十分核心的參數(shù)handle。他是來自ANativeWindowBuffer:
文件:/frameworks/native/libs/nativebase/include/nativebase/nativebase.h
const native_handle_t* handle;
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實(shí)際上是的GraphicBuffer的句柄祠肥。
讓我們依次看看GraphicBufferAllocator和GraphicBufferMapper都做了什么武氓。
GraphicBufferAllocator 初始化
文件: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()),
mAllocator(std::make_unique<Gralloc2::Allocator>(
mMapper.getGrallocMapper()))
{
}
ANDROID_SINGLETON_STATIC_INSTANCE這個(gè)宏實(shí)際上就是一個(gè)單例:
template <typename TYPE>
class ANDROID_API Singleton
{
public:
static TYPE& getInstance() {
Mutex::Autolock _l(sLock);
TYPE* instance = sInstance;
if (instance == 0) {
instance = new TYPE();
sInstance = instance;
}
return *instance;
}
static bool hasInstance() {
Mutex::Autolock _l(sLock);
return sInstance != 0;
}
protected:
~Singleton() { }
Singleton() { }
private:
Singleton(const Singleton&);
Singleton& operator = (const Singleton&);
static Mutex sLock;
static TYPE* sInstance;
};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
template<> ::android::Mutex \
(::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); /* NOLINT */ \
template class ::android::Singleton< TYPE >;
其實(shí)很簡單有一個(gè)靜態(tài)方法,上鎖后獲取一個(gè)靜態(tài)實(shí)例仇箱。
在構(gòu)造函數(shù)中實(shí)例化了兩個(gè)十分核心對(duì)象:
- 1.GraphicBufferMapper
- 2.Gralloc2::Allocator 從GraphicBufferMapper獲圖元生成器
GraphicBufferMapper 初始化
文件:/frameworks/native/libs/ui/GraphicBufferMapper.cpp
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
GraphicBufferMapper::GraphicBufferMapper()
: mMapper(std::make_unique<const Gralloc2::Mapper>())
{
}
初始化核心對(duì)象Gralloc2::Mapper县恕。
其實(shí)Gralloc2::Mapper和Gralloc2::Allocator兩者都是對(duì)應(yīng)Hal層的對(duì)象。我們依次看看兩者的初始化剂桥。
Gralloc2::Mapper 的初始化
Mapper::Mapper()
{
mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
...
// IMapper 2.1 is optional
mMapperV2_1 = IMapper::castFrom(mMapper);
}
能看到本質(zhì)上是溝通了Hal層的hwServiceManager之后忠烛,獲取IMapper的服務(wù)。
之前在SurfaceFlinger的HAL層初始化有詳細(xì)的介紹权逗,這里就不多贅述美尸。這里就直接擺出關(guān)鍵幾個(gè)數(shù)據(jù)結(jié)構(gòu)。
Gralloc2::Mapper HAL層的數(shù)據(jù)結(jié)構(gòu)介紹
從我之前幾篇文章能夠知道斟薇,一般來說HAL需要hw_module_t的結(jié)構(gòu)體作為核心师坎。本文繼續(xù)以msm8960為基準(zhǔn),Gralloc2對(duì)應(yīng)的Hal層全部都是passthrough 直通模式堪滨,我們看看這個(gè)數(shù)據(jù)結(jié)構(gòu)
文件:/hardware/qcom/display/msm8960/libgralloc/gralloc.cpp
struct private_module_t HAL_MODULE_INFO_SYM = {
.base = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = GRALLOC_MODULE_API_VERSION_0_2,
.hal_api_version = 0,
.id = GRALLOC_HARDWARE_MODULE_ID,
.name = "Graphics Memory Allocator Module",
.author = "The Android Open Source Project",
.methods = &gralloc_module_methods,
.dso = 0,
},
.registerBuffer = gralloc_register_buffer,
.unregisterBuffer = gralloc_unregister_buffer,
.lock = gralloc_lock,
.unlock = gralloc_unlock,
.perform = gralloc_perform,
.lock_ycbcr = gralloc_lock_ycbcr,
},
.framebuffer = 0,
.fbFormat = 0,
.flags = 0,
.numBuffers = 0,
.bufferMask = 0,
.lock = PTHREAD_MUTEX_INITIALIZER,
.currentBuffer = 0,
};
能看到這里面gralloc對(duì)應(yīng)的結(jié)構(gòu)體胯陋,module_api_version 代表gralloc hal結(jié)構(gòu)體的版本。此時(shí)版本為0.2袱箱。以及注冊(cè)了gralloc_register_buffer遏乔,gralloc_unregister_buffer,gralloc_lock发笔,gralloc_unlock按灶,gralloc_perform,gralloc_lock_ycbcr的方法指針.
對(duì)應(yīng)hw_module_t的包裝類是Gralloc0HalImpl 。gralloc有點(diǎn)特殊筐咧,沒有包裝成hw_device_t鸯旁,而是直接操作hw_module_t。
位置如下:/hardware/interfaces/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc0Hal.h
最后我們關(guān)注IMapper.hal量蕊,看看這個(gè)hal層開放了什么方法給上層:
interface IMapper {
...
@entry
@callflow(next="*")
createDescriptor(BufferDescriptorInfo descriptorInfo)
generates (Error error,
BufferDescriptor descriptor);
@entry
@callflow(next="*")
importBuffer(handle rawHandle) generates (Error error, pointer buffer);
@exit
@callflow(next="*")
freeBuffer(pointer buffer) generates (Error error);
@callflow(next="unlock")
lock(pointer buffer,
bitfield<BufferUsage> cpuUsage,
Rect accessRegion,
handle acquireFence)
generates (Error error,
pointer data);
@callflow(next="unlock")
lockYCbCr(pointer buffer,
bitfield<BufferUsage> cpuUsage,
Rect accessRegion,
handle acquireFence)
generates (Error error,
YCbCrLayout layout);
@callflow(next="*")
unlock(pointer buffer)
generates (Error error,
handle releaseFence);
};
一共四個(gè)方法:
- 1.importBuffer 生成可用的Buffer
- 2.freeBuffer 釋放Buffer
- 3.lock 上鎖buffer
- 4.lockYCbCr 上鎖一個(gè)ycbcr的像素格式的buffer
- 4.unlock 解鎖鎖buffer
最后包裝在這個(gè)HalImpl的類之上铺罢,在包裝一層GrallocMapper給上層:
template <typename T>
class GrallocMapper : public T {
protected:
void* addImportedBuffer(native_handle_t* bufferHandle) override {
return GrallocImportedBufferPool::getInstance().add(bufferHandle);
}
native_handle_t* removeImportedBuffer(void* buffer) override {
return GrallocImportedBufferPool::getInstance().remove(buffer);
}
const native_handle_t* getImportedBuffer(void* buffer) const override {
return GrallocImportedBufferPool::getInstance().get(buffer);
}
};
GrallocMapper是繼承與MapperImpl,位置如下残炮。
/hardware/interfaces/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
Gralloc2::Allocator HAL層的數(shù)據(jù)結(jié)構(gòu)介紹
其實(shí)對(duì)于Allocator來說,其實(shí)對(duì)應(yīng)的hw_module_t和IMapper是一致的韭赘。只不過不同的hal對(duì)象開發(fā)的api不同邀层。對(duì)于Allocator他只關(guān)注如何申請(qǐng)內(nèi)存出來坡氯。
先來看看對(duì)應(yīng)的hal文件:
文件:/hardware/interfaces/graphics/allocator/2.0/IAllocator.hal
interface IAllocator {
@entry
@exit
@callflow(next="*")
dumpDebugInfo() generates (string debugInfo);
@entry
@exit
@callflow(next="*")
allocate(BufferDescriptor descriptor, uint32_t count)
generates (Error error,
uint32_t stride,
vec<handle> buffers);
};
其實(shí)只有一個(gè)allocate的方法底哥,進(jìn)行內(nèi)存申請(qǐng)五嫂。不過在IAllocator的包裝類Gralloc0HalImpl在調(diào)用initMoudle初始化hw_module_t的時(shí)候,調(diào)用了如下方法:
文件:/hardware/interfaces/graphics/allocator/2.0/utils/passthrough/include/allocator-passthrough/2.0/Gralloc0Hal.h
bool initWithModule(const hw_module_t* module) {
int result = gralloc_open(module, &mDevice);
if (result) {
mDevice = nullptr;
return false;
}
return true;
}
這里調(diào)用了gralloc_open袖牙,對(duì)hw_module_t進(jìn)行初始化侧巨,生成一個(gè)hw_device_t.
文件:/hardware/qcom/display/msm8960/libgralloc/gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
const private_module_t* m = reinterpret_cast<const private_module_t*>(
module);
gpu_context_t *dev;
IAllocController* alloc_ctrl = IAllocController::getInstance();
dev = new gpu_context_t(m, alloc_ctrl);
*device = &dev->common;
status = 0;
} else {
status = fb_device_open(module, name, device);
}
return status;
}
在gralloc初始化hw_device_t時(shí)候,會(huì)判斷hw_module_t中的name字段鞭达。是否打開gralloc服務(wù)司忱。打開就使用gpu_context_t包裝gralloc申請(qǐng)服務(wù)返回給上層,不打開則使用老的方式framebuffer畴蹭,通信fb驅(qū)動(dòng)坦仍。
能看到上面hw_module_t.name字段就是GRALLOC_HARDWARE_GPU0。其實(shí)絕大部分都是在使用gralloc服務(wù)叨襟。framebuffer的服務(wù)將不會(huì)仔細(xì)聊繁扎,我們把重點(diǎn)放到gralloc服務(wù)。
再來看看gpu_context_t:
文件:/hardware/qcom/display/msm8960/libgralloc/gpu.cpp
gpu_context_t::gpu_context_t(const private_module_t* module,
IAllocController* alloc_ctrl ) :
mAllocCtrl(alloc_ctrl)
{
// Zero out the alloc_device_t
memset(static_cast<alloc_device_t*>(this), 0, sizeof(alloc_device_t));
// Initialize the procs
common.tag = HARDWARE_DEVICE_TAG;
common.version = 0;
common.module = const_cast<hw_module_t*>(&module->base.common);
common.close = gralloc_close;
alloc = gralloc_alloc;
#ifdef QCOM_BSP
allocSize = gralloc_alloc_size;
#endif
free = gralloc_free;
}
到了這一步糊闽,才真正的給整個(gè)hal的allocate方法賦予意義锻离。
先來看看IAllocController 這個(gè)單例初始化。
IAllocController 初始化
文件:/hardware/qcom/display/msm8960/libgralloc/alloc_controller.cpp
IAllocController* IAllocController::sController = NULL;
IAllocController* IAllocController::getInstance(void)
{
if(sController == NULL) {
sController = new IonController();
}
return sController;
}
//-------------- IonController-----------------------//
IonController::IonController()
{
mIonAlloc = new IonAlloc();
}
IAllocController中間包裝了一個(gè)IonAlloc墓怀。IonController中包裝一個(gè)ion驅(qū)動(dòng)控制器IonAlloc汽纠。IonAlloc實(shí)際上繼承IMemAlloc。
最后對(duì)應(yīng)著HAL向外暴露的對(duì)象為AllocatorImpl
小結(jié)
涉及到的對(duì)象有點(diǎn)多傀履,我們還是畫一個(gè)UML圖來梳理一遍整個(gè)GraphicAllactor的Hal層初始化虱朵。
記住這幅圖就能對(duì)整個(gè)GraphicBuffer的體系的設(shè)計(jì)了然于胸。
GraphicBuffer Hal層的生成原理
在GraphicBuffer中其核心方法為:
- 1.GraphicBufferAllocator. allocate钓账。
- 2.GraphicBufferMapper. getTransportSize
讓我們依次解析這兩個(gè)方法碴犬。
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride,
uint64_t /*graphicBufferId*/, std::string requestorName)
{
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
// allowed from an API stand-point allocate a 1x1 buffer instead.
if (!width || !height)
width = height = 1;
// Ensure that layerCount is valid.
if (layerCount < 1)
layerCount = 1;
Gralloc2::IMapper::BufferDescriptorInfo info = {};
info.width = width;
info.height = height;
info.layerCount = layerCount;
info.format = static_cast<Gralloc2::PixelFormat>(format);
info.usage = usage;
Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
if (error == Gralloc2::Error::NONE) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint32_t bpp = bytesPerPixel(format);
alloc_rec_t rec;
rec.width = width;
rec.height = height;
rec.stride = *stride;
rec.format = format;
rec.layerCount = layerCount;
rec.usage = usage;
rec.size = static_cast<size_t>(height * (*stride) * bpp);
rec.requestorName = std::move(requestorName);
list.add(*handle, rec);
return NO_ERROR;
} else {
...
return NO_MEMORY;
}
}
核心方法就是把buffer_handle_t句柄作為參數(shù)調(diào)用mAllocator的allocate方法,接著獲得真正申請(qǐng)出來的參數(shù)保存到sAllocList中梆暮。之后刪除就能通過handle找到對(duì)應(yīng)的參數(shù)銷毀服协。
Gralloc2::Allocator allocate
Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
uint32_t* outStride, buffer_handle_t* outBufferHandles) const
{
BufferDescriptor descriptor;
Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
if (error == Error::NONE) {
error = allocate(descriptor, count, outStride, outBufferHandles);
}
return error;
}
Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
uint32_t* outStride, buffer_handle_t* outBufferHandle) const
{
return allocate(descriptorInfo, 1, outStride, outBufferHandle);
}
先使用了Gralloc2::Mapper創(chuàng)建了BufferDescriptor對(duì)象。很簡單就不去看啦粹,就是在Hal層初始化了BufferDescriptor對(duì)象偿荷。
Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
uint32_t* outStride, buffer_handle_t* outBufferHandles) const
{
Error error;
auto ret = mAllocator->allocate(descriptor, count,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
error = tmpError;
if (tmpError != Error::NONE) {
return;
}
// import buffers
for (uint32_t i = 0; i < count; i++) {
error = mMapper.importBuffer(tmpBuffers[i],
&outBufferHandles[i]);
if (error != Error::NONE) {
for (uint32_t j = 0; j < i; j++) {
mMapper.freeBuffer(outBufferHandles[j]);
outBufferHandles[j] = nullptr;
}
return;
}
}
*outStride = tmpStride;
});
// make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
hardware::IPCThreadState::self()->flushCommands();
return (ret.isOk()) ? error : kTransactionError;
}
能看到這里就會(huì)調(diào)用了Hal層對(duì)應(yīng)AllocatorImpl的申請(qǐng)多個(gè)圖元方法。能看到在申請(qǐng)完內(nèi)存之后唠椭,將會(huì)調(diào)用GraphicBufferMapper的importBuffer方法跳纳,讓這段句柄對(duì)應(yīng)的內(nèi)存變得可用。
AllocatorImpl allocate
文件:/hardware/interfaces/graphics/allocator/2.0/utils/hal/include/allocator-hal/2.0/Allocator.h
Return<void> allocate(const BufferDescriptor& descriptor, uint32_t count,
IAllocator::allocate_cb hidl_cb) override {
uint32_t stride;
std::vector<const native_handle_t*> buffers;
Error error = mHal->allocateBuffers(descriptor, count, &stride, &buffers);
if (error != Error::NONE) {
hidl_cb(error, 0, hidl_vec<hidl_handle>());
return Void();
}
hidl_vec<hidl_handle> hidlBuffers(buffers.cbegin(), buffers.cend());
hidl_cb(Error::NONE, stride, hidlBuffers);
// free the local handles
mHal->freeBuffers(buffers);
return Void();
}
此時(shí)還是一樣調(diào)用mHal的allocateBuffers的方法贪嫂,并且使用hidl_handle包裝native_handle_t寺庄。換句話說,實(shí)際上我們?cè)谏蠈覩raphicBuffer拿到的handle是hidl_handle對(duì)象。
Gralloc0HalImpl::allocateBuffers
文件:/hardware/interfaces/graphics/allocator/2.0/utils/passthrough/include/allocator-passthrough/2.0/Gralloc0Hal.h
Error allocateBuffers(const BufferDescriptor& descriptor, uint32_t count, uint32_t* outStride,
std::vector<const native_handle_t*>* outBuffers) override {
mapper::V2_0::IMapper::BufferDescriptorInfo descriptorInfo;
if (!grallocDecodeBufferDescriptor(descriptor, &descriptorInfo)) {
return Error::BAD_DESCRIPTOR;
}
Error error = Error::NONE;
uint32_t stride = 0;
std::vector<const native_handle_t*> buffers;
buffers.reserve(count);
// allocate the buffers
for (uint32_t i = 0; i < count; i++) {
const native_handle_t* tmpBuffer;
uint32_t tmpStride;
error = allocateOneBuffer(descriptorInfo, &tmpBuffer, &tmpStride);
if (error != Error::NONE) {
break;
}
buffers.push_back(tmpBuffer);
if (stride == 0) {
stride = tmpStride;
} else if (stride != tmpStride) {
// non-uniform strides
error = Error::UNSUPPORTED;
break;
}
}
...
*outStride = stride;
*outBuffers = std::move(buffers);
return Error::NONE;
}
核心的方法是allocateOneBuffer申請(qǐng)一個(gè)圖元內(nèi)存斗塘。我們直接看對(duì)應(yīng)的gralloc的實(shí)現(xiàn)赢织。
Error allocateOneBuffer(const mapper::V2_0::IMapper::BufferDescriptorInfo& info,
const native_handle_t** outBuffer, uint32_t* outStride) {
if (info.layerCount > 1 || (info.usage >> 32) != 0) {
return Error::BAD_VALUE;
}
const native_handle_t* buffer = nullptr;
int stride = 0;
int result = mDevice->alloc(mDevice, info.width, info.height, static_cast<int>(info.format),
info.usage, &buffer, &stride);
switch (result) {
case 0:
*outBuffer = buffer;
*outStride = stride;
return Error::NONE;
case -EINVAL:
return Error::BAD_VALUE;
default:
return Error::NO_RESOURCES;
}
}
gpu_context_t::gralloc_alloc
文件:/hardware/qcom/display/msm8960/libgralloc/gpu.cpp
int gpu_context_t::gralloc_alloc(alloc_device_t* dev, int w, int h, int format,
int usage, buffer_handle_t* pHandle,
int* pStride)
{
if (!dev) {
return -EINVAL;
}
gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev);
return gpu->alloc_impl(w, h, format, usage, pHandle, pStride, 0);
}
gpu_context_t::alloc_impl
int gpu_context_t::alloc_impl(int w, int h, int format, int usage,
buffer_handle_t* pHandle, int* pStride,
size_t bufferSize) {
if (!pHandle || !pStride)
return -EINVAL;
size_t size;
int alignedw, alignedh;
int grallocFormat = format;
int bufferType;
//If input format is HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED then based on
//the usage bits, gralloc assigns a format.
...
getGrallocInformationFromFormat(grallocFormat, &bufferType);
size = getBufferSizeAndDimensions(w, h, grallocFormat, alignedw, alignedh);
if ((ssize_t)size <= 0)
return -EINVAL;
size = (bufferSize >= size)? bufferSize : size;
if ((usage & GRALLOC_USAGE_EXTERNAL_DISP) ||
(usage & GRALLOC_USAGE_PROTECTED)) {
bufferType = BUFFER_TYPE_VIDEO;
}
bool useFbMem = false;
char property[PROPERTY_VALUE_MAX];
if((usage & GRALLOC_USAGE_HW_FB) &&
(property_get("debug.gralloc.map_fb_memory", property, NULL) > 0) &&
(!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
(!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
useFbMem = true;
}
int err = 0;
if(useFbMem) {
err = gralloc_alloc_framebuffer(size, usage, pHandle);
} else {
err = gralloc_alloc_buffer(size, usage, pHandle, bufferType,
grallocFormat, alignedw, alignedh);
}
if (err < 0) {
return err;
}
*pStride = alignedw;
return 0;
}
getBufferSizeAndDimensions計(jì)算不同規(guī)格像素需要占用的內(nèi)存大小。接著檢測debug.gralloc.map_fb_memory是否在全局變量中設(shè)置開馍盟,打開了gralloc服務(wù)也會(huì)強(qiáng)制使用fb驅(qū)動(dòng)于置。
我們先不管fb驅(qū)動(dòng)的邏輯,先看gralloc邏輯朽合。調(diào)用gralloc_alloc_buffer。
gralloc_alloc_buffer
int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage,
buffer_handle_t* pHandle, int bufferType,
int format, int width, int height)
{
int err = 0;
int flags = 0;
size = roundUpToPageSize(size);
alloc_data data;
data.offset = 0;
data.fd = -1;
data.base = 0;
if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)
data.align = 8192;
else
data.align = getpagesize();
if ((qdutils::MDPVersion::getInstance().getMDPVersion() >= \
qdutils::MDSS_V5) && (usage & GRALLOC_USAGE_PROTECTED)) {
data.align = ALIGN(data.align, SZ_1M);
size = ALIGN(size, data.align);
}
data.size = size;
data.pHandle = (unsigned int) pHandle;
err = mAllocCtrl->allocate(data, usage);
if (!err) {
/* allocate memory for enhancement data */
alloc_data eData;
eData.fd = -1;
eData.base = 0;
eData.offset = 0;
eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));
eData.pHandle = data.pHandle;
eData.align = getpagesize();
int eDataUsage = GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP;
int eDataErr = mAllocCtrl->allocate(eData, eDataUsage);
...
flags |= data.allocType;
int eBaseAddr = int(eData.base) + eData.offset;
private_handle_t *hnd = new private_handle_t(data.fd, size, flags,
bufferType, format, width, height, eData.fd, eData.offset,
eBaseAddr);
hnd->offset = data.offset;
hnd->base = int(data.base) + data.offset;
hnd->gpuaddr = 0;
*pHandle = hnd;
}
return err;
}
alloc_data中保存handle句柄饱狂,讓IonController處理曹步。當(dāng)IonController.allocate申請(qǐng)完內(nèi)存后,alloc_data記錄記錄映射的共享內(nèi)存的起點(diǎn)位置和長度休讳,以及對(duì)應(yīng)的fd讲婚。這個(gè)fd很重要,fd究竟是指什么驅(qū)動(dòng)呢俊柔?我們往下看筹麸。
IonController allocate
文件:/hardware/qcom/display/msm8960/libgralloc/alloc_controller.cpp
int IonController::allocate(alloc_data& data, int usage)
{
int ionFlags = 0;
int ret;
data.uncached = useUncached(usage);
data.allocType = 0;
if(usage & GRALLOC_USAGE_PRIVATE_UI_CONTIG_HEAP)
ionFlags |= ION_HEAP(ION_SF_HEAP_ID);
if(usage & GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP)
ionFlags |= ION_HEAP(ION_SYSTEM_HEAP_ID);
if(usage & GRALLOC_USAGE_PRIVATE_IOMMU_HEAP)
ionFlags |= ION_HEAP(ION_IOMMU_HEAP_ID);
//MM Heap is exclusively a secure heap.
if(usage & GRALLOC_USAGE_PRIVATE_MM_HEAP) {
if(usage & GRALLOC_USAGE_PROTECTED) {
ionFlags |= ION_HEAP(ION_CP_MM_HEAP_ID);
ionFlags |= ION_SECURE;
}
else {
ionFlags |= ION_HEAP(ION_IOMMU_HEAP_ID);
}
}
if(usage & GRALLOC_USAGE_PRIVATE_CAMERA_HEAP)
ionFlags |= ION_HEAP(ION_CAMERA_HEAP_ID);
if(usage & GRALLOC_USAGE_PROTECTED)
data.allocType |= private_handle_t::PRIV_FLAGS_SECURE_BUFFER;
if(!ionFlags)
ionFlags = ION_HEAP(ION_SF_HEAP_ID) | ION_HEAP(ION_IOMMU_HEAP_ID);
data.flags = ionFlags;
ret = mIonAlloc->alloc_buffer(data);
// Fallback
if(ret < 0 && canFallback(usage,
(ionFlags & ION_SYSTEM_HEAP_ID)))
{
ALOGW("Falling back to system heap");
data.flags = ION_HEAP(ION_SYSTEM_HEAP_ID);
ret = mIonAlloc->alloc_buffer(data);
}
if(ret >= 0 ) {
data.allocType |= private_handle_t::PRIV_FLAGS_USES_ION;
}
return ret;
}
IonAlloc alloc_buffer
文件:/hardware/qcom/display/msm8960/libgralloc/ionalloc.cpp
#define ION_DEVICE "/dev/ion"
int IonAlloc::alloc_buffer(alloc_data& data)
{
Locker::Autolock _l(mLock);
int err = 0;
struct ion_handle_data handle_data;
struct ion_fd_data fd_data;
struct ion_allocation_data ionAllocData;
void *base = 0;
ionAllocData.len = data.size;
ionAllocData.align = data.align;
ionAllocData.heap_id_mask = data.flags & ~ION_SECURE;
ionAllocData.flags = data.uncached ? 0 : ION_FLAG_CACHED;
if (data.flags & ION_SECURE)
ionAllocData.flags |= ION_SECURE;
err = open_device();
if (err)
return err;
if(ioctl(mIonFd, ION_IOC_ALLOC, &ionAllocData)) {
err = -errno;
return err;
}
fd_data.handle = ionAllocData.handle;
handle_data.handle = ionAllocData.handle;
if(ioctl(mIonFd, ION_IOC_MAP, &fd_data)) {
err = -errno;
ioctl(mIonFd, ION_IOC_FREE, &handle_data);
return err;
}
if(!(data.flags & ION_SECURE)) {
base = mmap(0, ionAllocData.len, PROT_READ|PROT_WRITE,
MAP_SHARED, fd_data.fd, 0);
if(base == MAP_FAILED) {
err = -errno;
ioctl(mIonFd, ION_IOC_FREE, &handle_data);
return err;
}
memset(base, 0, ionAllocData.len);
// Clean cache after memset
clean_buffer(base, data.size, data.offset, fd_data.fd,
CACHE_CLEAN_AND_INVALIDATE);
}
data.base = base;
data.fd = fd_data.fd;
ioctl(mIonFd, ION_IOC_FREE, &handle_data);
return 0;
}
IonController通過format預(yù)計(jì)完需要申請(qǐng)的圖元內(nèi)存大小,就調(diào)用IonAlloc的allocate雏婶。在這個(gè)方法中做的事情核心有三件:
- 1.打開/dev/ion 驅(qū)動(dòng)物赶,在驅(qū)動(dòng)中創(chuàng)建一個(gè)ion_client對(duì)象
- 2.調(diào)用ioctl 傳入ION_IOC_ALLOC ,在底層創(chuàng)建一個(gè)ion_buffer區(qū)域留晚,并和ion_handle_data綁定酵紫。
- 3.ioctl 傳入ION_IOC_MAP 進(jìn)行ion_allocation_data的內(nèi)存和里面fd文件句柄進(jìn)行綁定,也即是有了一個(gè)匿名文件错维。
- 4.如果不是安全模式奖地,mmap 映射一段共享的地址在base中,并且讓虛擬內(nèi)存和ion管理的page綁定赋焕。
GraphicBufferMapper importBuffer
文件:/frameworks/native/libs/ui/GraphicBufferMapper.cpp
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
uint32_t width, uint32_t height, uint32_t layerCount,
PixelFormat format, uint64_t usage, uint32_t stride,
buffer_handle_t* outHandle)
{
ATRACE_CALL();
buffer_handle_t bufferHandle;
Gralloc2::Error error = mMapper->importBuffer(
hardware::hidl_handle(rawHandle), &bufferHandle);
....
Gralloc2::IMapper::BufferDescriptorInfo info = {};
info.width = width;
info.height = height;
info.layerCount = layerCount;
info.format = static_cast<Gralloc2::PixelFormat>(format);
info.usage = usage;
error = mMapper->validateBufferSize(bufferHandle, info, stride);
...
*outHandle = bufferHandle;
return NO_ERROR;
}
能上面的allocate方法只能此時(shí)的句柄是hidl_handle参歹,而hidl_handle成為rawHandle,這個(gè)句柄還不能使用隆判。需要經(jīng)過importBuffer經(jīng)過轉(zhuǎn)化才能使用犬庇。最后需要校驗(yàn)buffer的大小
MapperImpl importBuffer
Return<void> importBuffer(const hidl_handle& rawHandle,
IMapper::importBuffer_cb hidl_cb) override {
...
native_handle_t* bufferHandle = nullptr;
Error error = mHal->importBuffer(rawHandle.getNativeHandle(), &bufferHandle);
if (error != Error::NONE) {
hidl_cb(error, nullptr);
return Void();
}
void* buffer = addImportedBuffer(bufferHandle);
if (!buffer) {
mHal->freeBuffer(bufferHandle);
hidl_cb(Error::NO_RESOURCES, nullptr);
return Void();
}
hidl_cb(error, buffer);
return Void();
}
在MapperImpl中,把importBuffer分成2步驟侨嘀。
- 1.Gralloc0HalImpl importBuffer
- 2.import 處理完Buffer后械筛,addImportedBuffer把每一個(gè)Handle都添加到GrallocImportedBufferPool一個(gè)緩存池中,緩存起來飒炎。
Gralloc0HalImpl importBuffer
文件:/hardware/interfaces/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc0Hal.h
Error importBuffer(const native_handle_t* rawHandle,
native_handle_t** outBufferHandle) override {
native_handle_t* bufferHandle = native_handle_clone(rawHandle);
...
if (mModule->registerBuffer(mModule, bufferHandle)) {
...
return Error::BAD_BUFFER;
}
*outBufferHandle = bufferHandle;
return Error::NONE;
}
此時(shí)importBuffer對(duì)應(yīng)的方法就是registerBuffer埋哟。在mMapper中,是直接操作hw_module_t。
hw_module_t
文件:/hardware/qcom/display/msm8960/libgralloc/mapper.cpp
int gralloc_register_buffer(gralloc_module_t const* module,
buffer_handle_t handle)
{
...
private_handle_t* hnd = (private_handle_t*)handle;
hnd->base = 0;
hnd->base_metadata = 0;
int err = gralloc_map(module, handle);
if (err) {
return err;
}
return 0;
}
此時(shí)才會(huì)調(diào)用gralloc_map赤赊,真正進(jìn)行mmap進(jìn)行映射闯狱。
static int gralloc_map(gralloc_module_t const* module,
buffer_handle_t handle)
{
private_handle_t* hnd = (private_handle_t*)handle;
void *mappedAddress;
if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) &&
!(hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER)) {
size_t size = hnd->size;
IMemAlloc* memalloc = getAllocator(hnd->flags) ;
int err = memalloc->map_buffer(&mappedAddress, size,
hnd->offset, hnd->fd);
if(err || mappedAddress == MAP_FAILED) {
hnd->base = 0;
return -errno;
}
hnd->base = intptr_t(mappedAddress) + hnd->offset;
mappedAddress = MAP_FAILED;
size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));
err = memalloc->map_buffer(&mappedAddress, size,
hnd->offset_metadata, hnd->fd_metadata);
if(err || mappedAddress == MAP_FAILED) {
hnd->base_metadata = 0;
return -errno;
}
hnd->base_metadata = intptr_t(mappedAddress) + hnd->offset_metadata;
}
return 0;
}
此時(shí)會(huì)調(diào)用ionalloc的map_buffer,為hnd的mappedAddress進(jìn)行映射抛计。映射了2段內(nèi)存哄孤。一段是從mappedAddress到hnd->offset的,另一段是重新申請(qǐng)的mappedAddress到offset_metadata吹截。記住這兩個(gè)base瘦陈。換句話說,只有此時(shí)才給handle中的base賦值共享內(nèi)存的地址波俄。
GraphicBufferMapper lock
類似的流程晨逝,我們直接看核心方法:
int gralloc_lock(gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr)
{
private_handle_t* hnd = (private_handle_t*)handle;
int err = gralloc_map_and_invalidate(module, handle, usage, l, t, w, h);
if(!err)
*vaddr = (void*)hnd->base;
return err;
}
static int gralloc_map_and_invalidate (gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h)
{
if (private_handle_t::validate(handle) < 0)
return -EINVAL;
int err = 0;
private_handle_t* hnd = (private_handle_t*)handle;
if (usage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
if (hnd->base == 0) {
// we need to map for real
pthread_mutex_t* const lock = &sMapLock;
pthread_mutex_lock(lock);
err = gralloc_map(module, handle);
pthread_mutex_unlock(lock);
}
IMemAlloc* memalloc = getAllocator(hnd->flags) ;
err = memalloc->clean_buffer((void*)hnd->base,
hnd->size, hnd->offset, hnd->fd,
CACHE_INVALIDATE);
if ((usage & GRALLOC_USAGE_SW_WRITE_MASK) &&
!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
hnd->flags |= private_handle_t::PRIV_FLAGS_NEEDS_FLUSH;
}
} else {
hnd->flags |= private_handle_t::PRIV_FLAGS_DO_NOT_FLUSH;
}
return err;
}
能看到此時(shí)lock核心的思想,調(diào)用gralloc_map_and_invalidate懦铺,檢測private_handle_t中的base的字段有沒有進(jìn)行映射過捉貌,沒有就進(jìn)行一次映射。接著就初始化這一段內(nèi)存的資源冬念,最后讓句柄中hnd->base共享地址的資源賦值給addr趁窃,這個(gè)地址是什么?其實(shí)就是ANativeWindowBuffer中的bits字段急前。這樣private_handle_t中的base就和ANativeWindowBuffer的bits關(guān)聯(lián)起來醒陆。
此時(shí)才能正常的操作整個(gè)圖元。
GraphicBuffer 的跨進(jìn)程通信
前文說過GraphicBuffer的數(shù)據(jù)太大了裆针,沒有辦法進(jìn)行Binder通信统求,那么他為什么可以辦到binder返回呢?我們先去圖元生產(chǎn)者的基類IGraphicBufferProducer 的遠(yuǎn)程端:
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
explicit BpGraphicBufferProducer(const sp<IBinder>& impl)
: BpInterface<IGraphicBufferProducer>(impl)
{
}
~BpGraphicBufferProducer() override;
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;
}
其實(shí)就在App進(jìn)程中new了一個(gè)GraphicBuffer對(duì)象据块,但是這個(gè)對(duì)象展示不會(huì)去ion申請(qǐng)內(nèi)存码邻。而是調(diào)用了read的方法,繼續(xù)解壓縮reply返回的數(shù)據(jù)包另假。因?yàn)镚raphicBuffer是一個(gè)Flatten對(duì)象像屋,因此會(huì)走到GraphicBuffer的unflatten方法。
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
int const* buf = static_cast<int const*>(buffer);
...
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;
} else {
...
}
mId = static_cast<uint64_t>(buf[7]) << 32;
mId |= static_cast<uint32_t>(buf[8]);
mGenerationNumber = static_cast<uint32_t>(buf[9]);
mOwner = ownHandle;
if (handle != 0) {
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);
}
buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
size -= sizeNeeded;
fds += numFds;
count -= numFds;
return NO_ERROR;
}
此時(shí)就把整個(gè)handle拷貝過來了边篮,接著調(diào)用importBuffer己莺,把handle轉(zhuǎn)化從hidl_handle轉(zhuǎn)化為可用的private_handle_t。記住此時(shí)一般是deqeue方法中調(diào)用戈轿,此時(shí)還沒有l(wèi)ock凌受,因此還沒有和底層共享內(nèi)存關(guān)聯(lián)。
但是最重要的SF進(jìn)程和App進(jìn)程之間同一個(gè)handle的GraphicBuffer沒有進(jìn)行關(guān)聯(lián)思杯,我們來看看MapperImpl的lock方法:
文件:/hardware/interfaces/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
Return<void> lock(void* buffer, uint64_t cpuUsage, const V2_0::IMapper::Rect& accessRegion,
const hidl_handle& acquireFence, IMapper::lock_cb hidl_cb) override {
const native_handle_t* bufferHandle = getImportedBuffer(buffer);
if (!bufferHandle) {
hidl_cb(Error::BAD_BUFFER, nullptr);
return Void();
}
base::unique_fd fenceFd;
Error error = getFenceFd(acquireFence, &fenceFd);
if (error != Error::NONE) {
hidl_cb(error, nullptr);
return Void();
}
void* data = nullptr;
error = mHal->lock(bufferHandle, cpuUsage, accessRegion, std::move(fenceFd), &data);
hidl_cb(error, data);
return Void();
}
在進(jìn)行mmap共享內(nèi)存綁定之前胜蛉,會(huì)通過getImportedBuffer查找已經(jīng)在GrallocImportedBufferPool緩存下來的圖元數(shù)據(jù)的句柄native_handle_t挠进,這樣App進(jìn)程就找到了對(duì)應(yīng)SF進(jìn)程中GraphicBuffer的共享內(nèi)存。
總結(jié)
老規(guī)矩一幅圖總結(jié):
一般來說:圖元的繪制分為如下幾個(gè)步驟:
- 1.dequeueBuffer 獲取一個(gè)圖元的插槽位置誊册,或者生產(chǎn)一個(gè)圖元领突。其實(shí)在IGrraphicBufferProducer通過flattern進(jìn)行一次句柄GraphicBuffer拷貝,依次為依據(jù)找到底層的共享內(nèi)存案怯。
- 2.lock 綁定圖元共享內(nèi)存地址君旦,最后通過句柄在GrallocImportedBufferPool中找到在SF進(jìn)程申請(qǐng)好的內(nèi)存地址
- 3.queueBuffer 把圖元放入mActiveBuffer中,并且從新計(jì)算dequeue和acquire的數(shù)量嘲碱,同時(shí)把GrapicBuffer放到mQueue進(jìn)行消費(fèi)金砍,最后調(diào)用frameAvilable回調(diào)通知消費(fèi)者。
- 4.unlock 解鎖圖元 揭開共享內(nèi)存的映射麦锯。
到這里面涉及到了幾個(gè)fd的轉(zhuǎn)化恕稠,先不用太關(guān)注,知道是通過ion申請(qǐng)一段共享內(nèi)存离咐,通過fd的方式告訴App進(jìn)程可以映射到同一段物理內(nèi)存谱俭。
到這里奉件,我們就能看到了整個(gè)gralloc的服務(wù)宵蛀,在申請(qǐng)內(nèi)存時(shí)候,會(huì)把主要的工作交給內(nèi)核驅(qū)動(dòng)ion县貌。這個(gè)驅(qū)動(dòng)究竟是怎么回事呢术陶?在Android 4.4的時(shí)候還是在ashmem,申請(qǐng)共享內(nèi)存煤痕,那么ion是怎么設(shè)計(jì)的梧宫,下一篇將會(huì)為你揭曉。