现在的位置: 首页 > 综合 > 正文

android系统硬件OpenGL 3D 移植

2013年01月28日 ⁄ 综合 ⁄ 共 5250字 ⁄ 字号 评论关闭

通过前面两篇文章: 

http://blog.csdn.net/andyhuabing/article/details/7176049

http://blog.csdn.net/andyhuabing/article/details/7177757

大家应该对于OpenGL的框架层有个完整的理解了,而这一篇文章将是基本总结android
系统中对于OpenGL的处理流程,这也是移植完OpenGL1.0硬件3D到android系统的一个小小总结。

一、先来一个整体系统的框架调用结构图:

二、主要代码分布:

frameworks\base\opengl\include OpenGL标准本地库接口定义

frameworks\base\opengl\libagl  主要是软件3D库实现,即 libGLES_android.so 
frameworks\base\opengl\libs    主要libEGL实现以及libGLESv1_CM.so,libGLESv2.so

frameworks\base\opengl\java    所有java框架层的接口定义,基本上与c的openGL接口一致

frameworks\base\core\jni       OpenGL JNI 接口,主要是 gl10,gl11及gl20,当然还有EGL接口定义

在android系统中,上层使用的sdk java api接口定义与标准OpenGL接口基本上完全一致,这样
就对于java及c++工程师有利,基本上不需要进行大的修改,程序就可以移到java或者c++层运行。
避免了学习的成本及后期维护成本。

三、EGL & GL 接口介绍
EGL 是 OpenGL ES 和底层 Native 平台视窗系统之间的接口,EGL主要由Display,Context,Configuration构成
而GL则是本地视窗系统交互且平台无关的层,所以移植的重点就是 EGLxxx接口。

EGL是一组与平台相关的系列函数,所以也同时定义了一组平台相关的native数据类型

数据类型             值含义
EGLDisplay系统显示 ID 或句柄
EGLDisplay 系统显示 ID 或句柄
EGLSurface系统窗口或 frame buffer 句柄
EGLContextOpenGL ES 图形上下文

NativeDisplayType 平台显示数据类型,标识你所开发设备的物理屏幕
NativeWindowType  平台窗口数据类型,标识系统窗口
NativePixmapType  可以作为 Framebuffer 的系统图像(内存)数据类型,该类型只用于离屏渲染

重要概念备注:
EGLDisplay 是一个关联系统物理屏幕的通用数据类型。对于 PC 来说, Display 就是显示器的句柄
而在android系统设计为多个物理显示设备,不过目前只使用了一个物理显示屏幕。

获取 Display id值:
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS))
return null;

四、基本的OpenGL 编程步骤如下:
1. 获取Display。
Display代表显示器,在有些系统上可以有多个显示器,也就会有多个Display。获得Display要调用EGLboolean eglGetDisplay(NativeDisplay dpy),参数一般为 EGL_DEFAULT_DISPLAY 。该参数实际的意义是平台实现相关的,在X-Window下是XDisplay ID,在MS Windows下是Window DC。

2. 初始化egl。
调用 EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor),该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。

3. 选择Config。
所为Config实际指的是FrameBuffer的参数,在MS Windows下对应于PixelFormat,在X-Window下对应Visual。一般用EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list, EGLConfig * config, EGLint config_size, EGLint *num_config),其中attr_list是以EGL_NONE结束的参数数组,通常以id,value依次存放,对于个别标识性的属性可以只有
id,没有value。另一个办法是用EGLboolean eglGetConfigs(EGLDisplay dpy, EGLConfig * config, EGLint config_size, EGLint *num_config) 来获得所有config。这两个函数都会返回不多于config_size个Config,结果保存在config[]中,系统的总Config个数保存 在num_config中。可以利用eglGetConfig()中间两个参数为0来查询系统支持的Config总个数。

Config有众多的Attribute,这些Attribute决定FrameBuffer的格式和能力,通过eglGetConfigAttrib ()来读取,但不能修改。

4. 构造Surface。
Surface实际上就是一个FrameBuffer,通过 EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr) 来创建一个可实际显示的Surface。系统通常还支持另外两种Surface:PixmapSurface和PBufferSurface,这两种都不
是可显示的Surface,PixmapSurface是保存在系统内存中的位图,PBuffer则是保存在显存中的帧。

Surface也有一些attribute,基本上都可以故名思意, EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL,通过eglSurfaceAttrib()设置、eglQuerySurface()读取。

5. 创建Context。
OpenGL的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,这些状态作用于程序提交的顶点 坐标等图元从而形成帧缓冲内的像素。在OpenGL的编程接口中,Context就代表这个状态机,程序的主要工作就是向Context提供图元、设置状 态,偶尔也从Context里获取一些信息。
用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)来创建一个Context。

6. 绘制。
应用程序通过OpenGL API进行绘制,一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示。

五、硬件 OpenGL 3D 移植
本身android系统默认使用软件3D OpenGL库,而为了硬件加速则必须移植硬件OpenGL 3D库。
一般的移植步骤如下:
1、拿到平台相关的 eglxxx 及 glxxx 相关实现代码,而glXXX这部分代码基本是由GPU图形引擎公司实现
   的,基本上都是开源代码,比如 http://www.khronos.org/ 网站或者 http://www.vivantecorp.com/
   目前对于1.0可能采用直接调用模式,而2.0版本则采用Clinet/server架构进行实现,中间通过rpc通讯
   
2、深入分析eglXX相关接口,这里结合android系统重点分析这些接口实现及注意点   
在android中有两三大模块会调用到egl接口
1、开机动画
frameworks\base\cmds\bootanimation\BootAnimation.cpp
--> BootAnimation::readyToRun()
   
2、应用程序,通过 com_google_android_gles_jni_EGLImpl.cpp 中的JNI接口直接调用
 
3、SurfaceFlinger 图形合成进程调用,这一块是最复杂也是移植的核心部分所在
这块没有直接调用eglxx接口,而是经过了一层封装:
DisplayHardware.cpp --> DisplayHardware::init
frameworks/base/opengl/libs/egl/egl.cpp 中封装实现,这里最终都是调用 cnx->egl.eglxx()接口
 
eglGetDisplay(EGL_DEFAULT_DISPLAY) 返回显示屏幕id,这里需要注意平台的display创建及native句柄。
 
/* Create our main surface */
eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
这里需要注意 mNativeWindow.get() 给定的值低层是不可以使用的,这里一个sp指针对象值 
sp<FramebufferNativeWindow> mNativeWindow;
mNativeWindow = new FramebufferNativeWindow();
这里创建的是一个offscreen surface或者window,用于OpenGL将图形画到此画布上。
 
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
GL_MAX_TEXTURE_SIZE是获取纹理的宽高范围值,GL_MAX_VIEWPORT_DIMS获得视口的长和宽
而android系统只传入一个 GLint 值,这里会越界,好的平台会将高度值赋值给 mMaxTextureSize
有的平台则会直接死机,正确的应用传入一个 GLint mMaxViewportDims[2] 数组

eglMakeCurrent 将opengles环境设置为当前,这就是为了保存OpenGL状态机的上下文环境

EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
这是一个OpenGL上屏函数,系统中2D与3D使用同一个framebuffer,即显存surface用于上屏。
那么这个实现时注意,这里会将eglCreateWindowSurface创建的surface&window会blit或者flip
到显存上才可能显示。在我的系统中测试时,使用单缓冲时图图会闪烁,使用双缓冲可以解决此问题。


3、EGL接口实现,这块主要涉及到 内存,显示及缓冲区等接口实现

     由用户实现而由OpenGL内部调用,一般用于C/S这种架构,需要自行研究

/*
The client application, or default platform library must register valid versions of each of these 
interfaces before any EGL or GL functions are invoked, using the following functions provided by the 3D driver.
*/
typedef struct
{
   BEGL_MemoryInterface  *memInterface;     /* Memory interface which will called by the 3d driver */
   BEGL_HWInterface      *hwInterface;      /* Hardware interface which will be called by the driver */
   BEGL_DisplayInterface *displayInterface; /* Display interface which will be called by the driver */
  。。。。

} BEGL_DriverInterfaces;

4、其它,优化各个函数性能。

   比如同时使用硬件2D及3D加速功能   

总结: 如果对OpenGL的基本框架没有一个完整的了解,移植一个硬件OpenGL 3D库还是相当麻烦,并且调试中也会碰到各种问题。所幸经过几周的努力终于将其移植成功!呵呵,运气还不错。


抱歉!评论已关闭.