android 圖形系統加速學習系列 (二)

jopen 12年前發布 | 28K 次閱讀 Android Android開發 移動開發

上一節介紹下android 2D&3D庫加載的過程,節紹下軟件實現的libagl庫并重點介紹一下copybit 2D圖形加速部分。


如果處理器只有2D硬件加速而沒有3D硬件加速,則可以利用opengl中的libagl,實現封裝在libagl里的copybit,因為相對3D API來說,這個模塊的封裝基本是做好的,只要去實現一個copybit HAL即可;
如果處理器2D/3D硬件加速均有,那么可以丟開 copybit,去實現openGL ES 2D/3D API 的加速功能。


【2D&3D配置】
上節已說過根據配置文件/system/lib/egl/egl.cfg決定加載軟件、硬件加速庫,加載相應的值賦值到如下數據結構中:
#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
#define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);


struct egl_t {
    #include "EGL/egl_entries.in"
};


struct gl_hooks_t {
    struct gl_t {
        #include "entries.in"
    } gl;
    struct gl_ext_t {
        void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);
    } ext;
};
struct egl_connection_t
{
    void *              dso;
    gl_hooks_t *        hooks[2];
    EGLint              major;
    EGLint              minor;
    egl_t               egl;
};


那么對于上層調用者來說,如何使用OpenGLES硬件加速呢?在這里有個config配置的問題:
利用libs/EGL/egl.cpp::eglChooseConfig函數中的參數選擇config
enum {
    IMPL_HARDWARE = 0,
    IMPL_SOFTWARE,
    IMPL_NUM_IMPLEMENTATIONS
};
egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
即調用順序如下:
gEGLImpl[IMPL_HARDWARE].egl.eglChooseConfig(...)
gEGLImpl[IMPL_SOFTWARE].egl.eglChooseConfig(...)


[2D硬件加速]
  frameworks/base/opengl/libagl/egl.cpp 文件中利用hardware/libhardware/hardware.c
  文件中定義的hw_get_module()函數,該函數判斷獲得的系統屬性是否在variant_keys[]數組中定義
  通過load()函數加載相應的硬件模塊;否則加載default硬件模塊。


  libGLES_android.so為編譯frameworks/base/opengl/libagl/目錄而生成的,其專門有一個copybit.cpp文件對copybit模塊進
  一步封裝。libagl中通過在frameworks/base/opengl/libagl/Android.mk文件中定義:
  LIBAGL_USE_GRALLOC_COPYBITS := 1   默認是打開的
  來加載copybit模塊;如果未定義LIBAGL_USE_GRALLOC_COPYBITS,則通過軟件的方式而
  不使用copybit 模塊來達到 2D 硬件加速。
  對于copybit函數調用及相關的流程介紹如下:
  1、使用libagl庫中封裝好的copybit函數使用方式
  libagl\state.cpp 中加載copybit hal動態庫
  eglCreateContext [EGL初始化函數,創建上下文context] (frameworks\base\opengl\libagl\Egl.cpp)
ogles_context_t *ogles_init(size_t extra)
  ==>
    ...
#ifdef LIBAGL_USE_GRALLOC_COPYBITS
 hw_module_t const* module;
 if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
     struct copybit_device_t* copyBits;
     if (copybit_open(module, ?Bits) == 0) {
         c->copybits.blitEngine = copyBits;
         {
             int minLim = copyBits->get(copyBits,
                     COPYBIT_MINIFICATION_LIMIT);
             if (minLim != -EINVAL && minLim > 0) {
                 c->copybits.minScale = (1 << 16) / minLim;
             }
         }
         {
             int magLim = copyBits->get(copyBits,
                     COPYBIT_MAGNIFICATION_LIMIT);
             if (magLim != -EINVAL && magLim > 0) {
                 c->copybits.maxScale = min(32*1024-1, magLim) << 16;
             }
         }
     }
 }
#endif // LIBAGL_USE_GRALLOC_COPYBITS

void ogles_uninit(ogles_context_t* c) 
==>
#ifdef LIBAGL_USE_GRALLOC_COPYBITS
 if (c->copybits.blitEngine != NULL) {
     copybit_close((struct copybit_device_t*) c->copybits.blitEngine);
 }
#endif // LIBAGL_USE_GRALLOC_COPYBITS
  如此后面使用blitEngine進行調用相關的成員函數即可
c->copybits.blitEngine = copyBits;

  操作copybit:
  libagl\texture.cpp 文件
  void glDrawTexsvOES(const GLshort* coords) 
   drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
drawTexiOESWithCopybit(x, y, z, w, h, c)
drawTexiOESWithCopybit_impl 
return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);


   libalg\array.cpp 文件
   static const arrays_prims_fct_t drawArraysPrims[] = {
      drawPrimitivesPoints,
      drawPrimitivesLines,
      drawPrimitivesLineLoop,
      drawPrimitivesLineStrip,
      drawPrimitivesTriangles,
      drawPrimitivesTriangleStrip,
      drawPrimitivesTriangleFan
   };
   void glDrawArrays(GLenum mode, GLint first, GLsizei count)
drawArraysPrims[mode](c, first, count);
drawPrimitivesTriangleFan(ogles_context_t* c, GLint first, GLsizei count)
drawTriangleFanWithCopybit
drawTriangleFanWithCopybit_impl
  以上兩個函數都調用到了copybit hal模塊了,但 drawTexiOESWithCopybit_impl 只是對copybit進行
  簡單的封裝,而 drawTriangleFanWithCopybit_impl 對copybit函數進一步的封裝,較復雜。
  
  這里單獨介紹一下:
  libagl/copybit.cpp
static bool copybit(GLint x, GLint y,
        GLint w, GLint h,
        EGLTextureObject* textureObject,
        const GLint* crop_rect,
        int transform,
        ogles_context_t* c)
  {
   ...
   //1、確定Texture Env Mode是否正確
    switch (tev.env) {
    case GGL_REPLACE:
      ...
        break;
    case GGL_MODULATE:
        // only cases allowed is:
        // RGB  source, color={1,1,1,a} -> can be done with GL_REPLACE
        // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE
        ...
        break;
   default:
        // Incompatible texture environment.
        LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");
        return false;
    }
  
   //2、將texture轉換為copybit格式
    textureToCopyBitImage(&textureObject->surface, opFormat,textureObject->buffer, &src);

//3、支持超出硬件顯示支持能力進行縮放
    if (dsdx < maxScaleInv || dsdx > minScaleInv ||
        dtdy < maxScaleInv || dtdy > minScaleInv)
    {
        // The requested scale is out of the range the hardware
        // can support.
        err = copybit->stretch(copybit,
                &tmp_dst, &src, &tmp_rect, &srect, &tmp_it);
}

//4、是否有alpha值進行分別處理
    /* and now the alpha-plane hack. This handles the "Fade" case of a
     * texture with an alpha channel.
     */
    if (alphaPlaneWorkaround) { //如果有alpha值需要倒三次
     // first make a copy of the destination buffer  將數據從目的地址考出至臨時地址
     err = copybit->stretch(copybit,&tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it);
    
     // then proceed as usual, but without the alpha plane 從源地址復制至目的地址
     err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
    
     // finally copy back the destination on top with 1-alphaplane 從臨時地址復制到目的地址,并帶有alpha值
     err = copybit->stretch(copybit,&dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it);    
    }else{ //沒有alpha,只需要做一次 從源地址復制至目的地址
err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);    
    }
    
    對于alpha通道問題:
    這種情況屬于整個圖形區域采用相同的alpha值。 需要表現的效果為背景透明效果,前景明顯
    可見。由此得出計算公式為“前景x(1-Alpha)+背景x Alpha”,
    需要三個步驟,移出背景,移入前景,帶Alpha參數移入背景。
   
2、直接調用copybit hal 的blit進行調用的方式,名為“越獄”的調用方式
libagl/egl.cpp
egl_window_surface_v2_t::egl_window_surface_v2_t
==>
if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &pModule) == 0) {
        copybit_open(pModule, &blitengine);
    }
    
  egl_window_surface_v2_t::~egl_window_surface_v2_t()
  ==>
   if (blitengine) {
        copybit_close(blitengine);
    }
    
  操作copybit:
  void egl_window_surface_v2_t::copyBlt
  ==>
   copybit_device_t* const copybit = blitengine;
    if (copybit)  { //使用硬件2D加功能
     ...
        copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
        copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 255);
        copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
        region_iterator it(clip);
        err = copybit->blit(copybit, &dimg, &simg, &it);
    }
    
    if (!copybit || err) { //使用軟件實現blit功能,即利用memcpy實現
     ...
    
        uint8_t const * const src_bits = (uint8_t const *)src_vaddr;
        uint8_t       * const dst_bits = (uint8_t       *)dst_vaddr;


        while (cur != end) {
            const Rect& r(*cur++);
            ssize_t w = r.right - r.left;
            ssize_t h = r.bottom - r.top;
            if (w <= 0 || h<=0) continue;
            size_t size = w * bpp;
            uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
            uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
            if (dbpr==sbpr && size==sbpr) {
                size *= h;
                h = 1;
            }
            do {
                memcpy(d, s, size);
                d += dbpr;
                s += sbpr;
            } while (--h > 0);
        }    
    }

3、還有一個地方也會調用,這樣子就不需要加載libagl而調用
frameworks\base\libs\surfaceflinger\LayerBuffer.cpp
按照代碼中注解,所以請謹慎使用。
enum {
   /* FIXME: this only exists to work-around some issues with
    * the video and camera frameworks. don't implement unless
    * you know what you're doing.
    */
   GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER = 0x080000001,
};
    gralloc_module_t const * module = LayerBuffer::getGrallocModule();
    if (module && module->perform) {
        int err = module->perform(module,
                GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,
                buffers.heap->heapID(), bufferSize,
                offset, buffers.heap->base(),
                &src.img.handle);


        // we can fail here is the passed buffer is purely software
        mSupportsCopybit = (err == NO_ERROR);
    }
    
    調用點:
    void LayerBuffer::onFirstRef()
    {
     ...
   if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
       copybit_open(module, &mBlitEngine);
   }    
    }
    
    void LayerBuffer::BufferSource::onDraw(const Region& clip) const
if (ourBuffer->supportsCopybit()) {
...
          copybit_device_t* copybit = mLayer.mBlitEngine;
          if (copybit && err != NO_ERROR) {
              // create our EGLImageKHR the first time
              err = initTempBuffer();
              if (err == NO_ERROR) {
                  // NOTE: Assume the buffer is allocated with the proper USAGE flags
                  const NativeBuffer& dst(mTempBuffer);
                  region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
                  copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
                  copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
                  copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
                  err = copybit->stretch(copybit, &dst.img, &src.img,
                          &dst.crop, &src.crop, &clip);
                  if (err != NO_ERROR) {
                      clearTempBufferImage();
                  }
              }
          }
}

以上的幾個調用頻率:
drawTexiOES 99% 主要調用copybit模塊的函數,即調用封裝的libagl\copybit.cpp中接口函數
  egl_window_surface_v2_t::copyBlt 僅有幾次調用
  LayerBuffer::BufferSource::onDraw 沒有調用過,不保證以后不調用,到時調試Camera時就知道了


下面再介紹一下硬件copy hal接口的實現:
目前的copybit提供了如下的接口:
//Set a copybit parameter.
int (*set_parameter)(struct copybit_device_t *dev, int name, int value);

//Get a static copybit information.
int (*get)(struct copybit_device_t *dev, int name);

    /**
     * Execute the bit blit copy operation  最重要的一個函數
     *
     * @param dev from open
     * @param dst is the destination image
     * @param src is the source image
     * @param region the clip region
     *
     * @return 0 if successful
     */
    int (*blit)(struct copybit_device_t *dev,
                struct copybit_image_t const *dst,
                struct copybit_image_t const *src,
                struct copybit_region_t const *region);



//Execute the stretch bit blit copy operation,可由blit函數進行實現
    int (*stretch)(struct copybit_device_t *dev,
                   struct copybit_image_t const *dst,
                   struct copybit_image_t const *src,
                   struct copybit_rect_t const *dst_rect,
                   struct copybit_rect_t const *src_rect,
                   struct copybit_region_t const *region);

具體實現應該沒有什么大問題,注意幾個小點即可以了:
1、并不是所有硬件都支持這么多格式,而且android上層使用大端序,即RGBA8888對應于ARM的ABGR8888
       所以對于framebuffer.cpp(gralloc模塊)及3D OpenGl庫中顏色格式設定需要注意,否則會反掉。
       
    2、利用COPYBIT_PLANE_ALPHA(plane alpha)設定全局alpha值,而本身顏色中的alpha利用blit進行合成
       對于常說的SRC_OVER在上層libagl\copybit.cpp中進行了實現,下層只需要實現SRC_COPY情況即可。
    
    3、原始MSM做法針對copybit調用進行了優化:
    static int stretch_copybit(
        struct copybit_device_t *dev,
        struct copybit_image_t const *dst,
        struct copybit_image_t const *src,
        struct copybit_rect_t const *dst_rect,
        struct copybit_rect_t const *src_rect,
        struct copybit_region_t const *region) 
{
...
        const uint32_t maxCount = sizeof(list.req)/sizeof(list.req[0]);
        const struct copybit_rect_t bounds = { 0, 0, dst->w, dst->h };
        struct copybit_rect_t clip;
        list.count = 0;
        status = 0;
        
        // 通過一個while循環,積攢12個region,一次調用硬件驅動ioctl函數,將數據傳入驅動,進行stretch操作
        while ((status == 0) && region->next(region, &clip)) {
            intersect(&clip, &bounds, &clip);
            mdp_blit_req* req = &list.req[list.count];
            set_infos(ctx, req);
            set_image(&req->dst, dst);
            set_image(&req->src, src);
            set_rects(ctx, req, dst_rect, src_rect, &clip);


            if (req->src_rect.w<=0 || req->src_rect.h<=0)
                continue;


            if (req->dst_rect.w<=0 || req->dst_rect.h<=0)
                continue;


            if (++list.count == maxCount) {
                status = msm_copybit(ctx, &list);
                list.count = 0;
            }
        }
        
        //沒有next區域則直接調用硬件驅動ioctl函數進行輸出
        if ((status == 0) && list.count) {
            status = msm_copybit(ctx, &list);
        }
}
    
    /** copy the bits */
static int msm_copybit(struct copybit_context_t *dev, void const *list) 
{
   int err = ioctl(dev->mFD, MSMFB_BLIT,
                   (struct mdp_blit_req_list const*)list);


}
利用ioctl進行用戶層拷貝數據到內核層,這是會對效率有所影響。


【硬件2D&3D同時存在處理】

1、修改 surfaceflinger 中的 Android.mk,這個mk中用libGLES_android替換掉libEGL即可

2、在 frameworks/base/opengl/libagl/Android.mk 中定義:
    LIBAGL_USE_GRALLOC_COPYBITS := 1
    來加載copybit模塊;如果未定義LIBAGL_USE_GRALLOC_COPYBITS,則通過軟件的方式而不使用
    copybit 模塊來達到2D硬件加速

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!