Android SurfaceFlinger服務(八) ----- 圖像的輸出

SurfaceFlinger合成後就進行圖像的輸出的工作。在圖像輸出時,存在硬件合成器與不存在的情況有些差別。軟件合成時用到圖像緩衝區生產者與消費者模型。首先來看看圖像緩衝區的初始化。

void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");

    ......

    // initialize our non-virtual displays
    for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
        // set-up the displays that are already connected
        if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
            // All non-virtual displays are currently considered secure.
            bool isSecure = true;
            createBuiltinDisplayLocked(type);
            wp<IBinder> token = mBuiltinDisplays[i];

            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());

            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            int32_t hwcId = allocateHwcDisplayId(type);
            sp<DisplayDevice> hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig());
            if (i > DisplayDevice::DISPLAY_PRIMARY) {
                // FIXME: currently we don't get blank/unblank requests
                // for displays other than the main display, so we always
                // assume a connected display is unblanked.
                ALOGD("marking display %zu as acquired/unblanked", i);
                hw->setPowerMode(HWC_POWER_MODE_NORMAL);
            }
            mDisplays.add(token, hw);
        }
    }

   ......
}
  • 調用BufferQueue::createBufferQueue創建圖像緩衝區,並得到其生產者和消費者接口
  • 利用上面得到的consumer消費者接口創建FramebufferSurface
  • 利用上面得到的producer生產者接口創建DisplayDevice
  • DisplayDevice軟件合成的圖像在FramebufferSurface中得以消費處理
DisplayDevice::DisplayDevice(
        const sp<SurfaceFlinger>& flinger,
        DisplayType type,
        int32_t hwcId,
        int format,
        bool isSecure,
        const wp<IBinder>& displayToken,
        const sp<DisplaySurface>& displaySurface,
        const sp<IGraphicBufferProducer>& producer,
        EGLConfig config)
    : lastCompositionHadVisibleLayers(false),
      mFlinger(flinger),
      mType(type), mHwcDisplayId(hwcId),
      mDisplayToken(displayToken),
      mDisplaySurface(displaySurface),
      mDisplay(EGL_NO_DISPLAY),
      mSurface(EGL_NO_SURFACE),
      mDisplayWidth(), mDisplayHeight(), mFormat(),
      mFlags(),
      mPageFlipCount(),
      mIsSecure(isSecure),
      mSecureLayerVisible(false),
      mLayerStack(NO_LAYER_STACK),
      mOrientation(),
      mPowerMode(HWC_POWER_MODE_OFF),
      mActiveConfig(0)
{
    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);
    eglQuerySurface(display, surface, EGL_WIDTH,  &mDisplayWidth);
    eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight);

    // Make sure that composition can never be stalled by a virtual display
    // consumer that isn't processing buffers fast enough. We have to do this
    // in two places:
    // * Here, in case the display is composed entirely by HWC.
    // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
    //   window's swap interval in eglMakeCurrent, so they'll override the
    //   interval we set here.
    if (mType >= DisplayDevice::DISPLAY_VIRTUAL)
        window->setSwapInterval(window, 0);

    mConfig = config;
    mDisplay = display;
    mSurface = surface;
    mFormat  = format;
    mPageFlipCount = 0;
    mViewport.makeInvalid();
    mFrame.makeInvalid();

    // virtual displays are always considered enabled
    mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
                  HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;

    // Name the display.  The name will be replaced shortly if the display
    // was created with createDisplay().
    switch (mType) {
        case DISPLAY_PRIMARY:
            mDisplayName = "Built-in Screen";
            break;
        case DISPLAY_EXTERNAL:
            mDisplayName = "HDMI Screen";
            break;
        default:
            mDisplayName = "Virtual Screen";    // e.g. Overlay #n
            break;
    }

    // initialize the display orientation transform.
    setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
}
  • 使用生產者接口初始化egl圖形庫
  • 軟件繪製圖層時就會調用EGL圖形庫,間接調用生產者接口

在SurfaceFlinger::doDisplayComposition函數中調用doComposeSurfaces函數合成圖層,然後調用hw->swapBuffers。對於沒有HWComper存在時,直接提交顯示。若存在HWComper時,將軟件合成的圖層交由HWComper處理。

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);         
            }
        }
    }  

    status_t result = mDisplaySurface->advanceFrame();      
    if (result != NO_ERROR) {
        ALOGE("[%s] failed pushing new frame to HWC: %d",   
                mDisplayName.string(), result);             
    }  
}
  • 在不存在HWComposer或存在HWComposer但使用gles合成時調用eglSwapBuffers提交gles合成的緩衝區
  • 其它情況對圖層合層不作處理

eglSwapBuffers提交gles合成的緩衝區後交由圖層消費者去處理,前文中提到消費者是FramebufferSurface。看看其處理函數:

void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
    sp<GraphicBuffer> buf;
    sp<Fence> 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);
    }
}
  • 調用mHwc.fbPost提交buffer到HWComposer函數
int HWComposer::fbPost(int32_t id,
        const sp<Fence>& acquireFence, const sp<GraphicBuffer>& 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函數將軟件合成的結果提交給硬件合成器去混合圖層輸出
  • 不存在硬件合成器時,直接調用mFbDev->post將軟件合成的結果提交到framebuffer進去輸出

在SurfaceFlinger中執行完doDisplayComposition進行圖層處理後會調用postFramebuffer提交圖層到HWComposer中。

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

    ......

    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時調用hwc.commit()提交圖層。
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();        
            mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW);
        }

        for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) {    
            DisplayData& disp(mDisplayData[i]);
            if (disp.outbufHandle) {                        
                mLists[i]->outbuf = disp.outbufHandle;      
                mLists[i]->outbufAcquireFenceFd =           
                        disp.outbufAcquireFence->dup();     
            }
        }

        err = mHwc->set(mHwc, mNumDisplays, mLists);

        for (size_t i=0 ; i<mNumDisplays ; i++) {
            DisplayData& disp(mDisplayData[i]);
            disp.lastDisplayFence = disp.lastRetireFence;
            disp.lastRetireFence = Fence::NO_FENCE;         
            if (disp.list) {
                if (disp.list->retireFenceFd != -1) {       
                    disp.lastRetireFence = new Fence(disp.list->retireFenceFd);  
                    disp.list->retireFenceFd = -1;          
                }
                disp.list->flags &= ~HWC_GEOMETRY_CHANGED;  
            }
        }
    }  
    return (status_t)err;
}
  • 調用mHwc->set使用硬件對層圖進行合成並輸出到顯示設備

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章