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硬件加速