android 图形系统加速学习系列 (二)

jopen 12年前
     <p><span style="font-family:'Microsoft YaHei';font-size:16px;">上一节介绍下android 2D&3D库加载的过程,节绍下软件实现的libagl库并重点介绍一下copybit 2D图形加速部分。<br /> <br /> <br /> 如果处理器只有2D硬件加速而没有3D硬件加速,则可以利用opengl中的libagl,实现封装在libagl里的copybit,因为相对3D API来说,这个模块的封装基本是做好的,只要去实现一个copybit HAL即可;<br /> 如果处理器2D/3D硬件加速均有,那么可以丢开 copybit,去实现openGL ES 2D/3D API 的加速功能。<br /> <br /> <br /> 【2D&3D配置】<br /> 上节已说过根据配置文件/system/lib/egl/egl.cfg决定加载软件、硬件加速库,加载相应的值赋值到如下数据结构中:<br /> #define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);<br /> #define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);<br /> <br /> <br /> struct egl_t {<br />     #include "EGL/egl_entries.in"<br /> };<br /> <br /> <br /> struct gl_hooks_t {<br />     struct gl_t {<br />         #include "entries.in"<br />     } gl;<br />     struct gl_ext_t {<br />         void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);<br />     } ext;<br /> };<br /> struct egl_connection_t<br /> {<br />     void *              dso;<br />     gl_hooks_t *        hooks[2];<br />     EGLint              major;<br />     EGLint              minor;<br />     egl_t               egl;<br /> };<br /> <br /> <br /> 那么对于上层调用者来说,如何使用OpenGLES硬件加速呢?在这里有个config配置的问题:<br /> 利用libs/EGL/egl.cpp::eglChooseConfig函数中的参数选择config<br /> enum {<br />     IMPL_HARDWARE = 0,<br />     IMPL_SOFTWARE,<br />     IMPL_NUM_IMPLEMENTATIONS<br /> };<br /> egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];<br /> 即调用顺序如下:<br /> gEGLImpl[IMPL_HARDWARE].egl.eglChooseConfig(...)<br /> gEGLImpl[IMPL_SOFTWARE].egl.eglChooseConfig(...)<br /> <br /> <br /> [2D硬件加速]<br />   frameworks/base/opengl/libagl/egl.cpp 文件中利用hardware/libhardware/hardware.c<br />   文件中定义的hw_get_module()函数,该函数判断获得的系统属性是否在variant_keys[]数组中定义<br />   通过load()函数加载相应的硬件模块;否则加载default硬件模块。<br /> <br /> <br />   libGLES_android.so为编译frameworks/base/opengl/libagl/目录而生成的,其专门有一个copybit.cpp文件对copybit模块进<br />   一步封装。libagl中通过在frameworks/base/opengl/libagl/Android.mk文件中定义:<br />   LIBAGL_USE_GRALLOC_COPYBITS := 1   默认是打开的<br />   来加载copybit模块;如果未定义LIBAGL_USE_GRALLOC_COPYBITS,则通过软件的方式而<br />   不使用copybit 模块来达到 2D 硬件加速。<br />   对于copybit函数调用及相关的流程介绍如下:<br />   1、使用libagl库中封装好的copybit函数使用方式<br />   libagl\state.cpp 中加载copybit hal动态库<br />   eglCreateContext [EGL初始化函数,创建上下文context] (frameworks\base\opengl\libagl\Egl.cpp)<br /> ogles_context_t *ogles_init(size_t extra)<br />  <span style="white-space:pre;"> </span>==><br />     ...<br /> #ifdef LIBAGL_USE_GRALLOC_COPYBITS<br />  hw_module_t const* module;<br />  if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {<br />      struct copybit_device_t* copyBits;<br />      if (copybit_open(module, ?Bits) == 0) {<br />          c->copybits.blitEngine = copyBits;<br />          {<br />              int minLim = copyBits->get(copyBits,<br />                      COPYBIT_MINIFICATION_LIMIT);<br />              if (minLim != -EINVAL && minLim > 0) {<br />                  c->copybits.minScale = (1 << 16) / minLim;<br />              }<br />          }<br />          {<br />              int magLim = copyBits->get(copyBits,<br />                      COPYBIT_MAGNIFICATION_LIMIT);<br />              if (magLim != -EINVAL && magLim > 0) {<br />                  c->copybits.maxScale = min(32*1024-1, magLim) << 16;<br />              }<br />          }<br />      }<br />  }<br /> #endif // LIBAGL_USE_GRALLOC_COPYBITS<br /> <br /> void ogles_uninit(ogles_context_t* c) <br /> ==><br /> #ifdef LIBAGL_USE_GRALLOC_COPYBITS<br />  if (c->copybits.blitEngine != NULL) {<br />      copybit_close((struct copybit_device_t*) c->copybits.blitEngine);<br />  }<br /> #endif // LIBAGL_USE_GRALLOC_COPYBITS<br />   如此后面使用blitEngine进行调用相关的成员函数即可<br /> c->copybits.blitEngine = copyBits;<br /> <br />   操作copybit:<br />   libagl\texture.cpp 文件<br />   void glDrawTexsvOES(const GLshort* coords) <br />    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);<br /> drawTexiOESWithCopybit(x, y, z, w, h, c)<br /> drawTexiOESWithCopybit_impl <br /> return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);<br /> <br /> <br />    libalg\array.cpp 文件<br />    static const arrays_prims_fct_t drawArraysPrims[] = {<br />       drawPrimitivesPoints,<br />       drawPrimitivesLines,<br />       drawPrimitivesLineLoop,<br />       drawPrimitivesLineStrip,<br />       drawPrimitivesTriangles,<br />       drawPrimitivesTriangleStrip,<br />       drawPrimitivesTriangleFan<br />    };<br />    void glDrawArrays(GLenum mode, GLint first, GLsizei count)<br /> drawArraysPrims[mode](c, first, count);<br /> drawPrimitivesTriangleFan(ogles_context_t* c, GLint first, GLsizei count)<br /> drawTriangleFanWithCopybit<br /> drawTriangleFanWithCopybit_impl<br />   以上两个函数都调用到了copybit hal模块了,但<span style="white-space:pre;"> </span>drawTexiOESWithCopybit_impl 只是对copybit进行<br />   简单的封装,而 drawTriangleFanWithCopybit_impl 对copybit函数进一步的封装,较复杂。<br />   <br />   这里单独介绍一下:<br />   libagl/copybit.cpp<br /> static bool copybit(GLint x, GLint y,<br />         GLint w, GLint h,<br />         EGLTextureObject* textureObject,<br />         const GLint* crop_rect,<br />         int transform,<br />         ogles_context_t* c)<br />   {<br />   <span style="white-space:pre;"> </span>...<br />   <span style="white-space:pre;"> </span>//1、确定Texture Env Mode是否正确<br />     switch (tev.env) {<br />     case GGL_REPLACE:<br />     <span style="white-space:pre;"> </span> ...<br />         break;<br />     case GGL_MODULATE:<br />         // only cases allowed is:<br />         // RGB  source, color={1,1,1,a} -> can be done with GL_REPLACE<br />         // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE<br />         ...<br />         break;<br />    default:<br />         // Incompatible texture environment.<br />         LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");<br />         return false;<br />     }<br />   <span style="white-space:pre;"> </span><br />   <span style="white-space:pre;"> </span>//2、将texture转换为copybit格式<br />     textureToCopyBitImage(&textureObject->surface, opFormat,textureObject->buffer, &src);<br /> <br /> //3、支持超出硬件显示支持能力进行缩放<br />     if (dsdx < maxScaleInv || dsdx > minScaleInv ||<br />         dtdy < maxScaleInv || dtdy > minScaleInv)<br />     {<br />         // The requested scale is out of the range the hardware<br />         // can support.<span style="white-space:pre;"> </span><br />         err = copybit->stretch(copybit,<br />                 &tmp_dst, &src, &tmp_rect, &srect, &tmp_it);<br /> }<br /> <br /> //4、是否有alpha值进行分别处理<br />     /* and now the alpha-plane hack. This handles the "Fade" case of a<br />      * texture with an alpha channel.<br />      */<br />     if (alphaPlaneWorkaround) { //如果有alpha值需要倒三次<br />     <span style="white-space:pre;"> </span>// first make a copy of the destination buffer  将数据从目的地址考出至临时地址<br />     <span style="white-space:pre;"> </span>err = copybit->stretch(copybit,&tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it);<br />     <span style="white-space:pre;"> </span><br />     <span style="white-space:pre;"> </span>// then proceed as usual, but without the alpha plane 从源地址复制至目的地址<br />     <span style="white-space:pre;"> </span>err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);<br />     <span style="white-space:pre;"> </span><br />     <span style="white-space:pre;"> </span>// finally copy back the destination on top with 1-alphaplane 从临时地址复制到目的地址,并带有alpha值<br />     <span style="white-space:pre;"> </span>err = copybit->stretch(copybit,&dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it);    <br />     }else{ //没有alpha,只需要做一次 从源地址复制至目的地址<br /> err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);    <br />     }<br />     <br />     对于alpha通道问题:<br />     这种情况属于整个图形区域采用相同的alpha值。 需要表现的效果为背景透明效果,前景明显<br />     可见。由此得出计算公式为“前景x(1-Alpha)+背景x Alpha”,<br />     需要三个步骤,移出背景,移入前景,带Alpha参数移入背景。<br />    <br /> 2、直接调用copybit hal 的blit进行调用的方式,名为“越狱”的调用方式<br /> libagl/egl.cpp<br /> egl_window_surface_v2_t::egl_window_surface_v2_t<br /> ==><br /> if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &pModule) == 0) {<br />         copybit_open(pModule, &blitengine);<br />     }<br />     <br />   egl_window_surface_v2_t::~egl_window_surface_v2_t()<br />   ==><br />   <span style="white-space:pre;"> </span>if (blitengine) {<br />         copybit_close(blitengine);<br />     }<br />     <br />   操作copybit:<br />   void egl_window_surface_v2_t::copyBlt<br />   ==><br />   <span style="white-space:pre;"> </span>copybit_device_t* const copybit = blitengine;<br />     if (copybit)  { //使用硬件2D加功能<br />     <span style="white-space:pre;"> </span>...<br />         copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);<br />         copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 255);<br />         copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);<br />         region_iterator it(clip);<br />         err = copybit->blit(copybit, &dimg, &simg, &it);<br />     }<br />     <br />     if (!copybit || err) { //使用软件实现blit功能,即利用memcpy实现<br />     <span style="white-space:pre;"> </span>...<br />     <br />         uint8_t const * const src_bits = (uint8_t const *)src_vaddr;<br />         uint8_t       * const dst_bits = (uint8_t       *)dst_vaddr;<br /> <br /> <br />         while (cur != end) {<br />             const Rect& r(*cur++);<br />             ssize_t w = r.right - r.left;<br />             ssize_t h = r.bottom - r.top;<br />             if (w <= 0 || h<=0) continue;<br />             size_t size = w * bpp;<br />             uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;<br />             uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;<br />             if (dbpr==sbpr && size==sbpr) {<br />                 size *= h;<br />                 h = 1;<br />             }<br />             do {<br />                 memcpy(d, s, size);<br />                 d += dbpr;<br />                 s += sbpr;<br />             } while (--h > 0);<br />         }    <br />     }<br /> <br /> 3、还有一个地方也会调用,这样子就不需要加载libagl而调用<br /> frameworks\base\libs\surfaceflinger\LayerBuffer.cpp<br /> 按照代码中注解,所以请谨慎使用。<br /> enum {<br />    /* FIXME: this only exists to work-around some issues with<br />     * the video and camera frameworks. don't implement unless<br />     * you know what you're doing.<br />     */<br />    GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER = 0x080000001,<br /> };<br />     gralloc_module_t const * module = LayerBuffer::getGrallocModule();<br />     if (module && module->perform) {<br />         int err = module->perform(module,<br />                 GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,<br />                 buffers.heap->heapID(), bufferSize,<br />                 offset, buffers.heap->base(),<br />                 &src.img.handle);<br /> <br /> <br />         // we can fail here is the passed buffer is purely software<br />         mSupportsCopybit = (err == NO_ERROR);<br />     }<br />     <br />     调用点:<br />     void LayerBuffer::onFirstRef()<br />     {<br />     <span style="white-space:pre;"> </span>...<br />    if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {<br />        copybit_open(module, &mBlitEngine);<br />    }    <span style="white-space:pre;"> </span><br />     }<br />     <br />     void LayerBuffer::BufferSource::onDraw(const Region& clip) const<br /> if (ourBuffer->supportsCopybit()) {<br /> ...<br />           copybit_device_t* copybit = mLayer.mBlitEngine;<br />           if (copybit && err != NO_ERROR) {<br />               // create our EGLImageKHR the first time<br />               err = initTempBuffer();<br />               if (err == NO_ERROR) {<br />                   // NOTE: Assume the buffer is allocated with the proper USAGE flags<br />                   const NativeBuffer& dst(mTempBuffer);<br />                   region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));<br />                   copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);<br />                   copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);<br />                   copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);<br />                   err = copybit->stretch(copybit, &dst.img, &src.img,<br />                           &dst.crop, &src.crop, &clip);<br />                   if (err != NO_ERROR) {<br />                       clearTempBufferImage();<br />                   }<br />               }<br />           }<span style="white-space:pre;"> </span><br /> }<br /> <br /> 以上的几个调用频率:<br /> drawTexiOES 99% 主要调用copybit模块的函数,即调用封装的libagl\copybit.cpp中接口函数<br />   egl_window_surface_v2_t::copyBlt 仅有几次调用<br />   LayerBuffer::BufferSource::onDraw 没有调用过,不保证以后不调用,到时调试Camera时就知道了<br /> <br /> <br /> 下面再介绍一下硬件copy hal接口的实现:<br /> 目前的copybit提供了如下的接口:<br /> //Set a copybit parameter.<br /> int (*set_parameter)(struct copybit_device_t *dev, int name, int value);<br /> <br /> //Get a static copybit information.<br /> int (*get)(struct copybit_device_t *dev, int name);<br /> <br />     /**<br />      * Execute the bit blit copy operation  最重要的一个函数<br />      *<br />      * @param dev from open<br />      * @param dst is the destination image<br />      * @param src is the source image<br />      * @param region the clip region<br />      *<br />      * @return 0 if successful<br />      */<br />     int (*blit)(struct copybit_device_t *dev,<br />                 struct copybit_image_t const *dst,<br />                 struct copybit_image_t const *src,<br />                 struct copybit_region_t const *region);<br /> <br /> <br /> <br /> //Execute the stretch bit blit copy operation,可由blit函数进行实现<br />     int (*stretch)(struct copybit_device_t *dev,<br />                    struct copybit_image_t const *dst,<br />                    struct copybit_image_t const *src,<br />                    struct copybit_rect_t const *dst_rect,<br />                    struct copybit_rect_t const *src_rect,<br />                    struct copybit_region_t const *region);<br /> <br /> 具体实现应该没有什么大问题,注意几个小点即可以了:<br /> 1、并不是所有硬件都支持这么多格式,而且android上层使用大端序,即RGBA8888对应于ARM的ABGR8888<br />        所以对于framebuffer.cpp(gralloc模块)及3D OpenGl库中颜色格式设定需要注意,否则会反掉。<br />        <br />     2、利用COPYBIT_PLANE_ALPHA(plane alpha)设定全局alpha值,而本身颜色中的alpha利用blit进行合成<br />        对于常说的SRC_OVER在上层libagl\copybit.cpp中进行了实现,下层只需要实现SRC_COPY情况即可。<br />     <br />     3、原始MSM做法针对copybit调用进行了优化:<br />     static int stretch_copybit(<br />         struct copybit_device_t *dev,<br />         struct copybit_image_t const *dst,<br />         struct copybit_image_t const *src,<br />         struct copybit_rect_t const *dst_rect,<br />         struct copybit_rect_t const *src_rect,<br />         struct copybit_region_t const *region) <br /> {<br /> ...<br />         const uint32_t maxCount = sizeof(list.req)/sizeof(list.req[0]);<br />         const struct copybit_rect_t bounds = { 0, 0, dst->w, dst->h };<br />         struct copybit_rect_t clip;<br />         list.count = 0;<br />         status = 0;<br />         <br />         // 通过一个while循环,积攒12个region,一次调用硬件驱动ioctl函数,将数据传入驱动,进行stretch操作<br />         while ((status == 0) && region->next(region, &clip)) {<br />             intersect(&clip, &bounds, &clip);<br />             mdp_blit_req* req = &list.req[list.count];<br />             set_infos(ctx, req);<br />             set_image(&req->dst, dst);<br />             set_image(&req->src, src);<br />             set_rects(ctx, req, dst_rect, src_rect, &clip);<br /> <br /> <br />             if (req->src_rect.w<=0 || req->src_rect.h<=0)<br />                 continue;<br /> <br /> <br />             if (req->dst_rect.w<=0 || req->dst_rect.h<=0)<br />                 continue;<br /> <br /> <br />             if (++list.count == maxCount) {<br />                 status = msm_copybit(ctx, &list);<br />                 list.count = 0;<br />             }<br />         }<br />         <br />         //没有next区域则直接调用硬件驱动ioctl函数进行输出<br />         if ((status == 0) && list.count) {<br />             status = msm_copybit(ctx, &list);<br />         }<span style="white-space:pre;"> </span><br /> }<br />     <br />     /** copy the bits */<br /> static int msm_copybit(struct copybit_context_t *dev, void const *list) <br /> {<br />    int err = ioctl(dev->mFD, MSMFB_BLIT,<br />                    (struct mdp_blit_req_list const*)list);<br /> <br /> <br /> }<br /> 利用ioctl进行用户层拷贝数据到内核层,这是会对效率有所影响。<br /> <br /> <br /> 【硬件2D&3D同时存在处理】</span></p>    <p><span style="font-family:'Microsoft YaHei';font-size:16px;">1、修改 surfaceflinger 中的 Android.mk,这个mk中用libGLES_android替换掉libEGL即可</span></p>    <span style="font-family:'Microsoft YaHei';font-size:16px;">2、在 frameworks/base/opengl/libagl/Android.mk 中定义:<br />     LIBAGL_USE_GRALLOC_COPYBITS := 1<br />     来加载copybit模块;如果未定义LIBAGL_USE_GRALLOC_COPYBITS,则通过软件的方式而不使用<br />     copybit 模块来达到2D硬件加速</span>