2012年4月6日 星期五

Android SurfaceFlinger Part 1 - 啟動流程

SurfaceFlinger啟動方法有兩種

  • 方法一: 隨著zygote啟動(Default)

       啟動的流程是 main@SystemServer.java ->system_init@com_android_server_SystemServer.cpp
        ->system_init@system_init.cpp
       在system_init的function裡寫著

    property_get("system_init.startsurfaceflinger", propBuf, "1");
        if (strcmp(propBuf, "1") == 0) {
        // Start the SurfaceFlinger
        SurfaceFlinger::instantiate();
    }
     ...
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

        因此,假設系統中不存在"system_init.startsensorservice"就會執行SurfaceFlinger::instantiate();定義    在BinderService.h 最終執行
 sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
        將surfaceflinger加入系統的service.


  • 方法二:直接執行surfaceflinger

       我們可以在例如init.rc裡面直接去啟動surfaceflinger,程式碼位置是main_surfaceflinger.cpp 程式碼的內容是

  int main(int argc, char** argv) {
      SurfaceFlinger::publishAndJoinThreadPool();
      return 0;
  }
     SurfaceFlinger定義在SurfaceFlinger.h,而他是繼承BinderService 定義在BinderService.h 因此,我們可以在  BinderService.h看到 publishAndJoinThreadPool()的實做
    sp proc(ProcessState::self());
    sp sm(defaultServiceManager());
    sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

兩種方法都是利用
 sm->addService(String16(SERVICE::getServiceName()), new SERVICE());   
將surfaceflinger加入service並利用
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
進入surfaceflinger裡的
SurfaceFlinger::readyToRun()
定義在Thread::_threadLoop@Threads.cpp

...
self->mStatus = self->readyToRun();
...
readyToRun()會做幾件事:

  • 建立GraphicPlane
  • GraphicPlane& plane(graphicPlane(dpy));
    DisplayHardware* const hw = new DisplayHardware(this, dpy);
    plane.setDisplayHardware(hw);

    在建立DisplayHardware的物件時init()@DisplayHareware,會new 一個FramebufferNativeWindow 的object FramebufferNativeWindow()@FramebufferNativeWindow 然後分別建立fbdev framebuffer_open()@gralloc.h及grdev gralloc_open()@gralloc.h 最後都會透過gralloc_device_open()@gralloc.cpp來建立,
    如果是framebuffer的話,會呼叫fb_device_open()@framebuffer.cpp 在裡面要注意的是他透過mapFrameBuffer()->mapFrameBufferLocked()嘗試開啟
    char const * const device_template[] = {
        "/dev/graphics/fb%u",
        "/dev/fb%u",
        0 };
    
    與kernel做溝通. 如果是graphic的話就要看平台實做了。


  • 初始化EGL
    • 取得Display資訊
    • EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      eglInitialize(display, NULL, NULL);
      eglGetConfigs(display, NULL, 0, &numConfigs);
      err = selectConfigForPixelFormat(display, attribs, format, &config);
      

    • 建立main surface by call eglCreateWindowSurface()
    • surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
      if (mFlags & PARTIAL_UPDATES) {
          // if we have partial updates, we definitely don't need to
          // preserve the backbuffer, which may be costly.
          eglSurfaceAttrib(display, surface,
                      EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
      }
      
    • 取得LCD 的density
    •     if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
              if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
                  LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
                  strcpy(property, "160");
              }
          } else {
              /* for the emulator case, reset the dpi values too */
              mDpiX = mDpiY = atoi(property);
          }
          mDensity = atoi(property) * (1.0f/160.0f);
      
    • 建立的Context
    • context = eglCreateContext(display, config, NULL, contextAttributes);
      
      context存放著當前的 顏色,texture 等等的資訊
    • 暫時 unBind context
    • eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

  • 初始化hw composer
  • mHwc = new HWComposer(mFlinger);
    if (mHwc->initCheck() == NO_ERROR) {
        mHwc->setFrameBuffer(mDisplay, mSurface);
    }
    
  • Bind context
  • 最後回到readyToRun()透過hw.makeCurrent();來bind context接著就可以開始畫圖了
  • 播放動畫
  • property_set("ctl.start", "bootanim");
在readyToRun()跑完之後,就會進入SurfaceFlinger::threadLoop()在下一篇會討論