android 图形系统加速学习系列 (二)
jopen
13年前
<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>