Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android6.0 圖像合成過程詳解(二) doComposition函數

Android6.0 圖像合成過程詳解(二) doComposition函數

編輯:關於Android編程

上篇博客分析到setUpHWComposer函數,這裡我們繼續分析圖像合成的過程從doComposition函數開始,以及在這過程中解答一些上篇博客提出的疑問。

一、doComposition合成圖層

doComposition這個函數就是合成所有層的圖像

void SurfaceFlinger::doComposition() {
    ATRACE_CALL();
    const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
    for (size_t dpy=0 ; dpy& hw(mDisplays[dpy]);
        if (hw->isDisplayOn()) {
            // transform the dirty region into this screen's coordinate space
            const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));

            // repaint the framebuffer (if needed)
            doDisplayComposition(hw, dirtyRegion);

            hw->dirtyRegion.clear();
            hw->flip(hw->swapRegion);
            hw->swapRegion.clear();
        }
        // inform the h/w that we're done compositing
        hw->compositionComplete();
    }
    postFramebuffer();
}

上面函數遍歷所有的DisplayDevice然後調用doDisplayComposition函數。然後我們再看看doDisplayComposition函數

void SurfaceFlinger::doDisplayComposition(const sp& hw,
        const Region& inDirtyRegion)
{
    bool isHwcDisplay = hw->getHwcDisplayId() >= 0;
    if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
        return;
    }

    Region dirtyRegion(inDirtyRegion);

    //swapRegion設置為需要更新的區域
    hw->swapRegion.orSelf(dirtyRegion);

    uint32_t flags = hw->getFlags();//獲得顯示設備支持的更新方式標志
    if (flags & DisplayDevice::SWAP_RECTANGLE) {//支持矩陣更新        
        dirtyRegion.set(hw->swapRegion.bounds());
    } else {
        if (flags & DisplayDevice::PARTIAL_UPDATES) {//支持部分更新
            dirtyRegion.set(hw->swapRegion.bounds());
        } else {
            //將更新區域調整為整個窗口大小
            dirtyRegion.set(hw->bounds());
            hw->swapRegion = dirtyRegion;
        }
    }

    if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
        if (!doComposeSurfaces(hw, dirtyRegion)) return;//合成
    } else {
        RenderEngine& engine(getRenderEngine());
        mat4 colorMatrix = mColorMatrix;
        if (mDaltonize) {
            colorMatrix = colorMatrix * mDaltonizer();
        }
        mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
        doComposeSurfaces(hw, dirtyRegion);//合成
        engine.setupColorTransform(oldMatrix);
    }

    // update the swap region and clear the dirty region
    hw->swapRegion.orSelf(dirtyRegion);

    // swap buffers (presentation)
    hw->swapBuffers(getHwComposer());//使用egl將egl中的合成好的圖像,輸出到DisplayDevice的mSurface中
}

這個函數設置下需要更新的區域,後面調用doComposeSurfaces函數來合成圖層,調用完doComposeSurfaces函數後,如果需要egl合成圖像話,在這個函數中合成好。而最後調用swapBuffers只是將egl合成好的圖像輸出到DisplayDevice的mSurface中。

我們再來看看doComposeSurfaces函數,我們先來看一開始的代碼,先判斷是否有egl合成,然後再看是否有hwc合成(硬件合成)

 

bool SurfaceFlinger::doComposeSurfaces(const sp& hw, const Region& dirty)
{
    RenderEngine& engine(getRenderEngine());
    const int32_t id = hw->getHwcDisplayId();
    HWComposer& hwc(getHwComposer());
    HWComposer::LayerListIterator cur = hwc.begin(id);
    const HWComposer::LayerListIterator end = hwc.end(id);

    bool hasGlesComposition = hwc.hasGlesComposition(id);
    if (hasGlesComposition) {//是否有egl合成
        if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
            ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
                  hw->getDisplayName().string());
            eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) {
              ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
            }
            return false;
        }

        // Never touch the framebuffer if we don't have any framebuffer layers
        const bool hasHwcComposition = hwc.hasHwcComposition(id);
        if (hasHwcComposition) {//是否有hwc合成
            // when using overlays, we assume a fully transparent framebuffer
            // NOTE: we could reduce how much we need to clear, for instance
            // remove where there are opaque FB layers. however, on some
            // GPUs doing a "clean slate" clear might be more efficient.
            // We'll revisit later if needed.
            engine.clearWithColor(0, 0, 0, 0);
        } else {
            // we start with the whole screen area
            const Region bounds(hw->getBounds());

            // we remove the scissor part
            // we're left with the letterbox region
            // (common case is that letterbox ends-up being empty)
            const Region letterbox(bounds.subtract(hw->getScissor()));

            // compute the area to clear
            Region region(hw->undefinedRegion.merge(letterbox));

            // but limit it to the dirty region
            region.andSelf(dirty);

            // screen is already cleared here
            if (!region.isEmpty()) {
                // can happen with SurfaceView
                drawWormhole(hw, region);
            }
        }

        if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) {
            // just to be on the safe side, we don't set the
            // scissor on the main display. It should never be needed
            // anyways (though in theory it could since the API allows it).
            const Rect& bounds(hw->getBounds());
            const Rect& scissor(hw->getScissor());
            if (scissor != bounds) {
                // scissor doesn't match the screen's dimensions, so we
                // need to clear everything outside of it and enable
                // the GL scissor so we don't draw anything where we shouldn't

                // enable scissor for this frame
                const uint32_t height = hw->getHeight();
                engine.setScissor(scissor.left, height - scissor.bottom,
                        scissor.getWidth(), scissor.getHeight());
            }
        }
    }
......

 

我們來看hasGlesComposition函數和hasHwcComposition函數,就是看其對應的DisplayData中是否有hasFbComp和hasOvComp。

 

bool HWComposer::hasGlesComposition(int32_t id) const {
    if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
        return true;
    return mDisplayData[id].hasFbComp;
}
bool HWComposer::hasHwcComposition(int32_t id) const {
    if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
        return false;
    return mDisplayData[id].hasOvComp;
}
而這兩個值是在prepare中調用Hwc的prepare函數之後賦值的

 

status_t HWComposer::prepare() {
   ......
   int err = mHwc->prepare(mHwc, mNumDisplays, mLists);
    ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err));

    if (err == NO_ERROR) {
        // here we're just making sure that "skip" layers are set
        // to HWC_FRAMEBUFFER and we're also counting how many layers
        // we have of each type.
        //
        // If there are no window layers, we treat the display has having FB
        // composition, because SurfaceFlinger will use GLES to draw the
        // wormhole region.
        for (size_t i=0 ; inumHwLayers ; i++) {
                    hwc_layer_1_t& l = disp.list->hwLayers[i];

                    //ALOGD("prepare: %d, type=%d, handle=%p",
                    //        i, l.compositionType, l.handle);

                    if (l.flags & HWC_SKIP_LAYER) {
                        l.compositionType = HWC_FRAMEBUFFER;
                    }
                    if (l.compositionType == HWC_FRAMEBUFFER) {
                        disp.hasFbComp = true;//只要有一個layer是HWC_FRAMEBUFFER
                    }
                    if (l.compositionType == HWC_OVERLAY) {
                        disp.hasOvComp = true;//有一個layer是HWC_OVERLAY
                    }
                    if (l.compositionType == HWC_CURSOR_OVERLAY) {
                        disp.hasOvComp = true;//有一個layer是HWC_CURSOR_OVERLAY
                    }
                }
                if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {//layer的數量 有framebufferTarget為1 沒有為0
                    disp.hasFbComp = true;
                }
            } else {
                disp.hasFbComp = true;//沒有list
            }
        }
    }
    return (status_t)err;
}

我們繼續看doComposeSurfaces函數,下面這個函數當cur!=end代表起碼有兩個以上圖層,然後遍歷圖層,當layer是HWC_FRAMEBUFFER代表是需要egl合成的,而HWC_FRAMEBUFFER_TARGET是egl合成後使用的直接就跳了,HWC_CURSOR_OVERLAY和HWC_OVERLAY是用HWC模塊(硬件合成)的,也就不用調用Layer的draw方法。而如果圖層只要1個或者沒有,那麼直接使用egl合成。

    HWComposer::LayerListIterator cur = hwc.begin(id);
    const HWComposer::LayerListIterator end = hwc.end(id);
    ......

    const Vector< sp >& layers(hw->getVisibleLayersSortedByZ());
    const size_t count = layers.size();
    const Transform& tr = hw->getTransform();
    if (cur != end) { //代表起碼有兩個以上圖層
        // we're using h/w composer
        for (size_t i=0 ; i& layer(layers[i]);
            const Region clip(dirty.intersect(tr.transform(layer->visibleRegion)));
            if (!clip.isEmpty()) {
                switch (cur->getCompositionType()) {
                    case HWC_CURSOR_OVERLAY:
                    case HWC_OVERLAY: {
                        const Layer::State& state(layer->getDrawingState());
                        if ((cur->getHints() & HWC_HINT_CLEAR_FB)
                                && i
                                && layer->isOpaque(state) && (state.alpha == 0xFF)
                                && hasGlesComposition) {
                            // never clear the very first layer since we're
                            // guaranteed the FB is already cleared
                            layer->clearWithOpenGL(hw, clip);
                        }
                        break;
                    }
                    case HWC_FRAMEBUFFER: {
                        layer->draw(hw, clip);//只有是HWC_FRAMEBUFFER才會調用Layer的draw合成
                        break;
                    }
                    case HWC_FRAMEBUFFER_TARGET: {
                        // this should not happen as the iterator shouldn't
                        // let us get there.
                        ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i);
                        break;
                    }
                }
            }
            layer->setAcquireFence(hw, *cur);
        }
    } else {
        // we're not using h/w composer
        for (size_t i=0 ; i& layer(layers[i]);
            const Region clip(dirty.intersect(
                    tr.transform(layer->visibleRegion)));
            if (!clip.isEmpty()) {
                layer->draw(hw, clip);
            }
        }
    }

    // disable scissor at the end of the frame
    engine.disableScissor();
    return true;
}

Layer的draw我們就不看了主要是使用egl合成紋理,但是有一點疑問,我們從來沒有把layer中的mActiveBuffer放到egl中去,那麼egl又是怎麼合成各個layer的呢,我想肯定客戶進程在繪制各個layer的時候,也是用egl繪制的,所有後面合成的時候egl有各個layer的buffer。


後面我們再來看下DisplayDevice::swapBuffers函數,是使用eglSwapBuffers來把egl合成的數據放到mSurface中去。

 

void DisplayDevice::swapBuffers(HWComposer& hwc) const {
    // We need to call eglSwapBuffers() if:
    //  (1) we don't have a hardware composer, or
    //  (2) we did GLES composition this frame, and either
    //    (a) we have framebuffer target support (not present on legacy
    //        devices, where HWComposer::commit() handles things); or
    //    (b) this is a virtual display
    if (hwc.initCheck() != NO_ERROR ||
            (hwc.hasGlesComposition(mHwcDisplayId) &&
             (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) {
        EGLBoolean success = eglSwapBuffers(mDisplay, mSurface);
        if (!success) {
            EGLint error = eglGetError();
            if (error == EGL_CONTEXT_LOST ||
                    mType == DisplayDevice::DISPLAY_PRIMARY) {
                LOG_ALWAYS_FATAL("eglSwapBuffers(%p, %p) failed with 0x%08x",
                        mDisplay, mSurface, error);
            } else {
                ALOGE("eglSwapBuffers(%p, %p) failed with 0x%08x",
                        mDisplay, mSurface, error);
            }
        }
    }
    else if(hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL)
    {
        EGLBoolean success = eglSwapBuffersVIV(mDisplay, mSurface);
        if (!success) {
            EGLint error = eglGetError();
            ALOGE("eglSwapBuffersVIV(%p, %p) failed with 0x%08x",
                        mDisplay, mSurface, error);
        }
    }

    status_t result = mDisplaySurface->advanceFrame();
    if (result != NO_ERROR) {
        ALOGE("[%s] failed pushing new frame to HWC: %d",
                mDisplayName.string(), result);
    }
}

 


二、FramebufferSurface收到egl合成數據

之前分析DisplayDevice時候,還分析了FramebufferSurface,我們這裡再來看下。

在SurfaceFlinger.cpp中的init函數,在創建DisplayDevice之前,我們先調用createBufferQueue來創建了一個buffer的生產者和消費者,然後把消費者放入了FramebufferSurface,生產者放入了DisplayDevice中。

          sp producer;
            sp consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());

            sp<framebuffersurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            int32_t hwcId = allocateHwcDisplayId(type);
            sp hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig());</framebuffersurface>

我們先來看生產者,下面是DisplayDevice的構造函數,生產者作為參數直接新建了一個Surface,然後把這個Surface作為參數調用eglCreateWindowSurface返回的就是mSurface,之前我們分析最後egl合成的數據時調用eglSwapBuffers並且把數據放到mSurface,這樣最後肯定就到消費者(FramebufferSurface)去了。

    mNativeWindow = new Surface(producer, false);
    ANativeWindow* const window = mNativeWindow.get();

    /*
     * Create our display's surface
     */

    EGLSurface surface;
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (config == EGL_NO_CONFIG) {
        config = RenderEngine::chooseEglConfig(display, format);
    }
    surface = eglCreateWindowSurface(display, config, window, NULL);

最後到消費者那端的onFrameAvailable,也就是FramebufferSurface的onFrameAvailable中,我們現在來分析下這個過程,也就解答了一個onFrameAvailable的疑惑。


FramebufferSurface的父類是ConsumerBase類,我們來看其構造函數。先是構造了mConsumer,這裡其實就是BufferQueueConsumer類,後面調用了其consumerConnect方法。

ConsumerBase::ConsumerBase(const sp& bufferQueue, bool controlledByApp) :
        mAbandoned(false),
        mConsumer(bufferQueue) {
    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());

    wp listener = static_cast(this);
    sp proxy = new BufferQueue::ProxyConsumerListener(listener);

    status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
    if (err != NO_ERROR) {
        CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
                strerror(-err), err);
    } else {
        mConsumer->setConsumerName(mName);
    }
}

我們來看下BufferQueueConsumer類的consumerConnect方法,就是調用了connect方法。

    virtual status_t consumerConnect(const sp& consumer,
            bool controlledByApp) {
        return connect(consumer, controlledByApp);
    }

這個方法中將mCore->mConsumerListener = consumerListener,這個mCore就是BufferQueueCore類。我們再從ConsumerBase的構造函數看這個consumerListener參數其實就是FrameBufferSurface對象本身。

status_t BufferQueueConsumer::connect(
        const sp& consumerListener, bool controlledByApp) {
    ATRACE_CALL();

    if (consumerListener == NULL) {
        BQ_LOGE("connect(C): consumerListener may not be NULL");
        return BAD_VALUE;
    }

    BQ_LOGV("connect(C): controlledByApp=%s",
            controlledByApp ? "true" : "false");

    Mutex::Autolock lock(mCore->mMutex);

    if (mCore->mIsAbandoned) {
        BQ_LOGE("connect(C): BufferQueue has been abandoned");
        return NO_INIT;
    }

    mCore->mConsumerListener = consumerListener;//設置回調
    mCore->mConsumerControlledByApp = controlledByApp;

    return NO_ERROR;
}

 

我們再看BufferQueueProducer::queueBuffer函數,這個函數應該是生產者已經使用好buffer了,這個使用會調用如下代碼這個listener就是BufferQueueCore的mConsumerListener,傳輸的數據時BufferItem。再傳之前把BufferItem的mGraphicBuffer清了,因為消費者可以自己獲取buffer,不用通過BufferItem傳。

    item.mGraphicBuffer.clear();
    item.mSlot = BufferItem::INVALID_BUFFER_SLOT;

    // Call back without the main BufferQueue lock held, but with the callback
    // lock held so we can ensure that callbacks occur in order
    {
        Mutex::Autolock lock(mCallbackMutex);
        while (callbackTicket != mCurrentCallbackTicket) {
            mCallbackCondition.wait(mCallbackMutex);
        }

        if (frameAvailableListener != NULL) {
            frameAvailableListener->onFrameAvailable(item);
        } else if (frameReplacedListener != NULL) {
            frameReplacedListener->onFrameReplaced(item);
        }

        ++mCurrentCallbackTicket;
        mCallbackCondition.broadcast();
    }

這樣就要FramebufferSurface的onFrameAvailable函數中去了,我們來看下這個函數。

 

void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
    sp buf;
    sp acquireFence;
    status_t err = nextBuffer(buf, acquireFence);
    if (err != NO_ERROR) {
        ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
                strerror(-err), err);
        return;
    }
    err = mHwc.fbPost(mDisplayType, acquireFence, buf);
    if (err != NO_ERROR) {
        ALOGE("error posting framebuffer: %d", err);
    }
}
這個函數先用nextBuffer獲取數據,然後調用了HWComposer的fbPost函數。我們先來看下nextBuffer函數,這個函數主要通過acquireBufferLocked獲取BufferItem,其中的mBuf就是buffer了。
status_t FramebufferSurface::nextBuffer(sp& outBuffer, sp& outFence) {
    Mutex::Autolock lock(mMutex);

    BufferItem item;
    status_t err = acquireBufferLocked(&item, 0);
    if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
        outBuffer = mCurrentBuffer;
        return NO_ERROR;
    } else if (err != NO_ERROR) {
        ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
        return err;
    }

    if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
        item.mBuf != mCurrentBufferSlot) {
        // Release the previous buffer.
        err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
        if (err < NO_ERROR) {
            ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
            return err;
        }
    }
    mCurrentBufferSlot = item.mBuf;
    mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
    outFence = item.mFence;
    outBuffer = mCurrentBuffer;
    return NO_ERROR;
}
而這個acquireBufferLocked還是用mConsumer的acquireBuffer來獲取BufferItem。mConsumer就是BufferQueueConsumer類。
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
    status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
    if (err != NO_ERROR) {
        return err;
    }

    if (item->mGraphicBuffer != NULL) {
        mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
    }

    mSlots[item->mBuf].mFrameNumber = item->mFrameNumber;
    mSlots[item->mBuf].mFence = item->mFence;

    CB_LOGV("acquireBufferLocked: -> slot=%d/%" PRIu64,
            item->mBuf, item->mFrameNumber);

    return OK;
}

回到FramebufferSurface的onFrameAvailable中這樣獲取了buffer之後,調用了HWComposer的fbPost方法。

 



三、egl合成數據在HWComposer的處理

繼上面調用fbPost方法,我們來看下,這裡是調用了setFramebufferTarget方法。

int HWComposer::fbPost(int32_t id,
        const sp& acquireFence, const sp& buffer) {
    if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
        return setFramebufferTarget(id, acquireFence, buffer);
    } else {
        acquireFence->waitForever("HWComposer::fbPost");
        return mFbDev->post(mFbDev, buffer->handle);
    }
}

我們來看下setFramebufferTarget方法,這裡就是把該設備的DisplayData數據中的framebufferTarget填充,主要是其handle數據,這裡就是egl合成好的數據buffer。

也就是最終egl合成好的數據放在DisplayData的framebufferTarget變量的handle中。

status_t HWComposer::setFramebufferTarget(int32_t id,
        const sp& acquireFence, const sp& buf) {
    if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
        return BAD_INDEX;
    }
    DisplayData& disp(mDisplayData[id]);
    if (!disp.framebufferTarget) {
        // this should never happen, but apparently eglCreateWindowSurface()
        // triggers a Surface::queueBuffer()  on some
        // devices (!?) -- log and ignore.
        ALOGE("HWComposer: framebufferTarget is null");
        return NO_ERROR;
    }

    int acquireFenceFd = -1;
    if (acquireFence->isValid()) {
        acquireFenceFd = acquireFence->dup();
    }

    // ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd);
    disp.fbTargetHandle = buf->handle;//egl合成好的數據
    disp.framebufferTarget->handle = disp.fbTargetHandle;//egl合成好的數據,最終是放在這裡
    disp.framebufferTarget->acquireFenceFd = acquireFenceFd;
    return NO_ERROR;
}

 


四、硬件模塊合成

這樣就剩最後一步了,把不管是普通layer的數據,還是egl合成好的數據發送到硬件模塊合成了,最後就到顯示設備了。

繼第一節分析的doComposition函數最後會調用postFramebuffer函數,我們再來分析下這個函數,這個函數主要是調用了HWComposer的commit函數。

 

void SurfaceFlinger::postFramebuffer()
{
    ATRACE_CALL();

    const nsecs_t now = systemTime();
    mDebugInSwapBuffers = now;

    HWComposer& hwc(getHwComposer());
    if (hwc.initCheck() == NO_ERROR) {
        if (!hwc.supportsFramebufferTarget()) {
            // EGL spec says:
            //   "surface must be bound to the calling thread's current context,
            //    for the current rendering API."
            getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
        }
        hwc.commit();
    }
......
我們來看下HWComposer的commit函數,這個函數就是先設置了egl的那個設備的surface和display,然後處理虛擬設備的outbuf等,最後調用了硬件模塊合成到顯示設備上。
status_t HWComposer::commit() {
    int err = NO_ERROR;
    if (mHwc) {
        if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // On version 1.0, the OpenGL ES target surface is communicated
            // by the (dpy, sur) fields and we are guaranteed to have only
            // a single display.
            mLists[0]->dpy = eglGetCurrentDisplay();//設置下egl相關變量
            mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW);
        }

        for (size_t i=VIRTUAL_DISPLAY_ID_BASE; ioutbuf = disp.outbufHandle;
                mLists[i]->outbufAcquireFenceFd =
                        disp.outbufAcquireFence->dup();
            }
        }

        err = mHwc->set(mHwc, mNumDisplays, mLists);//調用硬件模塊合成
......

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved