Android 重學(xué)系列 GraphicBuffer的誕生

前言

經(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;
}
    1. waitWhileAllocatingLocked 如果其他線程進(jìn)入這個(gè)方法陪腌,會(huì)等待BufferQueueProducer幫其他應(yīng)用進(jìn)程申請(qǐng)完之后才能繼續(xù)走下去。
    1. waitForFreeSlotThenRelock 不斷的查找BufferQueueCore的Slot中空閑的插槽位置烟瞧,并且取出BufferSlot(在SF中也有一個(gè)BufferSlot對(duì)應(yīng)記錄)中的GraphicBuffer的index(found)诗鸭,判斷當(dāng)前這個(gè)圖元是否寬高,像素格式是否和請(qǐng)求一樣参滴,不一樣則需要重新請(qǐng)求强岸,則在返回碼添加BUFFER_NEEDS_REALLOCATION。
    1. 先把found的index添加到mActiveBuffer 區(qū)間砾赔,標(biāo)示為活躍狀態(tài),并且設(shè)置為dequeue狀態(tài)蝌箍。接著如果找到的GraphicBuffer是空的青灼,或者需要重新申請(qǐng),則把設(shè)置到BufferSlot的參數(shù)全部初始化十绑。
    1. 發(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ù)贷币。
    1. 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)蚊惯。

    1. 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層初始化虱朵。


GraphicBuffer生成體系.png

記住這幅圖就能對(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é):


GraphicBuffer誕生到可使用.png

一般來說:圖元的繪制分為如下幾個(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ì)為你揭曉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摆碉,一起剝皮案震驚了整個(gè)濱河市塘匣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巷帝,老刑警劉巖忌卤,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異楞泼,居然都是意外死亡驰徊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門堕阔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棍厂,“玉大人,你說我怎么就攤上這事超陆∥” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長例驹。 經(jīng)常有香客問我捐韩,道長,這世上最難降的妖魔是什么鹃锈? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任荤胁,我火速辦了婚禮,結(jié)果婚禮上屎债,老公的妹妹穿的比我還像新娘仅政。我一直安慰自己,他們只是感情好盆驹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布圆丹。 她就那樣靜靜地躺著,像睡著了一般躯喇。 火紅的嫁衣襯著肌膚如雪辫封。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天廉丽,我揣著相機(jī)與錄音倦微,去河邊找鬼。 笑死正压,一個(gè)胖子當(dāng)著我的面吹牛欣福,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播焦履,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拓劝,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了嘉裤?” 一聲冷哼從身側(cè)響起郑临,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屑宠,沒想到半個(gè)月后厢洞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侨把,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年犀变,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秋柄。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡获枝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骇笔,到底是詐尸還是另有隱情省店,我是刑警寧澤嚣崭,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站懦傍,受9級(jí)特大地震影響雹舀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粗俱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一说榆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寸认,春花似錦签财、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灸叼,卻和暖如春神汹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背古今。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工屁魏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沧卢。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓蚁堤,卻偏偏與公主長得像醉者,于是被迫代替她去往敵國和親但狭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354