Surface Flinger 의 연속~

========================================================================================================
출처 : http://blog.daum.net/baramjin/16010960


Surface Flinger의 초기화가 끝나면 사실 쓰레드가 하나 돌면서 Event를 처리하고 2-3가지의 일을 해주는 것이 주된 업무다. (맞나? 쓰레드 하는 일이 다 그렇지...)

 

SurfaceFlinger의 threadLoop()를 보면 다음과 같다.

 

bool SurfaceFlinger::threadLoop()
{
    waitForEvent();

    // check for transactions
    if (UNLIKELY(mConsoleSignals)) {
        handleConsoleEvents();
    }

 

    if (LIKELY(mTransactionCount == 0)) {
        // if we're in a global transaction, don't do anything.
        const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
        uint32_t transactionFlags = getTransactionFlags(mask);
        if (LIKELY(transactionFlags)) {
            handleTransaction(transactionFlags);
        }
    }

 

    // post surfaces (if needed)
    handlePageFlip();

 

    const DisplayHardware& hw(graphicPlane(0).displayHardware());
    if (LIKELY(hw.canDraw())) {
        // repaint the framebuffer (if needed)

        handleRepaint();

 

        // release the clients before we flip ('cause flip might block)
        unlockClients();
        executeScheduledBroadcasts();

 

        // sample the cpu gauge
        if (UNLIKELY(mDebugCpu)) {
            handleDebugCpu();
        }

 

        postFramebuffer();
    } else {
        // pretend we did the post
        unlockClients();
        executeScheduledBroadcasts();
        usleep(16667); // 60 fps period
    }
    return true;
}

 

handleRepaint()는 flags에 따라서 업데이트할 방법이나 영역을 결정하고 모든 surface를 조합한다(이게 결국 Surface Flinger의 역활이지...)

 

void SurfaceFlinger::handleRepaint()
{
    // set the frame buffer
    const DisplayHardware& hw(graphicPlane(0).displayHardware());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

 

    if (UNLIKELY(mDebugRegion)) {
        debugFlashRegions();
    }

 

    // compute the invalid region
    mInvalidRegion.orSelf(mDirtyRegion);

 

    uint32_t flags = hw.getFlags();
    if (flags & DisplayHardware::BUFFER_PRESERVED) {
        // here we assume DisplayHardware::flip()'s  implementation
        // performs the copy-back optimization.
    } else {
        if (flags & DisplayHardware::UPDATE_ON_DEMAND) {
            // we need to fully redraw the part that will be updated
            mDirtyRegion.set(mInvalidRegion.bounds());
        } else {
            // we need to redraw everything
            mDirtyRegion.set(hw.bounds());
            mInvalidRegion = mDirtyRegion;
        }

    }

 

    // compose all surfaces
    composeSurfaces(mDirtyRegion);

 

    // clear the dirty regions
    mDirtyRegion.clear();
}

 

void SurfaceFlinger::composeSurfaces(const Region& dirty)
{
    if (UNLIKELY(!mWormholeRegion.isEmpty())) {
        // should never happen unless the window manager has a bug
        // draw something...
        drawWormhole();
    }
    const SurfaceFlinger& flinger(*this);
    const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
    const size_t count = drawingLayers.size();
    LayerBase const* const* const layers = drawingLayers.array();
    for (size_t i=0 ; i<count ; ++i) {
        LayerBase const * const layer = layers[i];
        const Region& visibleRegion(layer->visibleRegionScreen);
        if (!visibleRegion.isEmpty())  {
            const Region clip(dirty.intersect(visibleRegion));
            if (!clip.isEmpty()) {
                layer->draw(clip);
            }
        }
    }
}

 

코드를 보면 조건에 따라 Update할 영역이 일부인지 전체인지를 결정하고 결국 composerSurfaces()를 호출하여 surface들을 조합하게 된다. composerSurfaces()는 Layer에서 보여지는 영역을 잘라오고 이를 draw 한다. (음 이는 Layer도 또 분석하라는 말인가?)

 

다시 threadLoop()를 가면 handleRepaint()에 의해서 그려질 내용은 정리되었고 이를 실제 FrameBuffer로 내보내야 한다. 이를 하는 것이 postFrameBuffer()이다.

 

void SurfaceFlinger::postFramebuffer()
{
    const bool skip = mOrientationAnimation->run();
    if (UNLIKELY(skip)) {
        return;
    }

 

    if (!mInvalidRegion.isEmpty()) {
        const DisplayHardware& hw(graphicPlane(0).displayHardware());

        if (UNLIKELY(mDebugFps)) {
            debugShowFPS();
        }

 

        hw.flip(mInvalidRegion);

 

        mInvalidRegion.clear();

 

        if (Layer::deletedTextures.size()) {
            glDeleteTextures(
                    Layer::deletedTextures.size(),
                    Layer::deletedTextures.array());
            Layer::deletedTextures.clear();
        }
    }
}

 

일단 처음에 보면 Animation이 돌고 있으면 바로 return하게 된다. 이는 BootAnmation을 통해서 나중에 좀 더 생각해 보고 일단 주된 동작은

graphicPlane으로부터 처음 초기화에서 만들어 놓은 DisplayHardware의 포인터를 얻고 DisplayHardware의 flip을 호출해서 버퍼을 swap 시켜주게 된다.

 

handleRepaint()와 postFrameBuffer()를 보면 Surface Flinger는 업데이트할 영역 관리를 두개의 영역 변수로 관리한다. 하나는 mDirtyRegion으로 handleRepaint()에서 계산해서 필요한 일을 한 후에 mInvalidRegion에 저장하고 mDirtyRegion은 클리어 된다. postFrameBuffer()는 mInvalidRegion이 있는 경우에만 더블 버퍼 스왑을 하고 역시 mInvalidRegion은 클리어 한다.

 

void DisplayHardware::flip(const Region& dirty) const
{
    checkGLErrors();

 

    EGLDisplay dpy = mDisplay;
    EGLSurface surface = mSurface;

 

    Region newDirty(dirty);
    newDirty.andSelf(Rect(mWidth, mHeight));

 

    if (mFlags & BUFFER_PRESERVED) {
        const Region copyback(mDirty.subtract(newDirty));
        mDirty = newDirty;
        mDisplaySurface->copyFrontToBack(copyback);
    }

 

    if (mFlags & SWAP_RECTANGLE_EXTENSION) {
        const Rect& b(newDirty.bounds());
        mDisplaySurface->setSwapRectangle(
                b.left, b.top, b.width(), b.height());

    }

 

    eglSwapBuffers(dpy, surface);
    checkEGLErrors("eglSwapBuffers");

}

 

 사실 코드를 보면 eglSwapBuffers만 부르기 때문에 어떻게 더블 버퍼를 관리하는지는 다시 EGL의 몫으로 넘겨졌다. 또한 copyFrontToBack등을 통해서 기존에 그려진 내용을 저장해 두는 과정 등이 있는데 이 부분이 왜 필요한지, 또 성능에 영향은 없는지도 고민되는 사항이다.

 

모 그런 부분은 일단 알아서 다 잘 되겠지 하는 생각 또는 나중에 시간나면 분석하는 것 둘 중에 하나의 일일 것이다.


http://blog.daum.net/baramjin/16010960


'Platform > Android' 카테고리의 다른 글

[Android] Boot Animation  (0) 2010.03.18
[Android] SGL  (0) 2010.03.18
[Android] Surface Flinger와 초기화 과정  (0) 2010.03.18
[Android] EGL 이란?  (0) 2010.03.18
[Android] Hello Android 출력하기  (0) 2010.03.09

+ Recent posts