上一节介绍下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硬件加速