转载请把头部出处链接和尾部二维码一起转载,本文出自:http://blog.csdn.net/hejjunlin/article/details/65935584
Agenda:
- EGL是什么?
- EGL数据类型
- EGL在Android中应用
- EGL的工作流程
- GLSurfaceView与EGL区别
- 简单Demo
EGL是什么?
EGL? is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system.
It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs.
那么什么是EGL?EGL是OpenGL ES和底层的native window system之间的接口,承上启下。
在Android上,EGL完善了OpenGL ES。利用类似eglCreateWindowSurface的EGL函数可以创建surface 用来render ,有了这个surface你就能往这个surface中利用OpenGL ES函数去画图了。OpenGL ES 本质上是一个图形渲染管线的状态机,而 EGL 则是用于监控这些状态以及维护 Frame buffer 和其他渲染 Surface 的外部层。下图是一个3d游戏典型的 EGL 系统布局图。
EGL数据类型
数据类型 | 值 |
---|---|
EGLBoolean | EGL_TRUE =1, EGL_FALSE=0 |
EGLint | int 数据类型 |
EGLDisplay | 系统显示 ID 或句柄 |
EGLConfig | Surface 的 EGL 配置 |
EGLSurface | 系统窗口或 frame buffer 句柄 |
EGLContext | OpenGL ES 图形上下文 |
NativeDisplayType | Native 系统显示类型 |
NativeWindowType | Native 系统窗口缓存类型 |
NativePixmapType | Native 系统 frame buffer |
EGL在Android中应用
下面是开机动画BootAnimation中的实现,首先是创建本地环境,
status_t BootAnimation::readyToRun() {
// 创建SurfaceControl
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
// 设置layerstack
control->setLayer(0x40000000);
SurfaceComposerClient::closeGlobalTransaction();
//获取Surface
sp<Surface> s = control->getSurface();
// initialize opengl and egl
const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_NONE
};
EGLint w, h, dummy;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
//调用eglGetDisplay
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); // 第一步
eglInitialize(display, 0, 0); // 第二步
eglChooseConfig(display, attribs, &config, 1, &numConfigs); // 第三步
//调用eglCreateWindowSurface将Surface s转换为本地窗口,
surface = eglCreateWindowSurface(display, config, s.get(), NULL); // 第四步
context = eglCreateContext(display, config, NULL, NULL); // 第五步
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
//eglMakeCurrent后生成的surface就可以利用opengl画图了
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
return NO_INIT;
return NO_ERROR;
}
egl创建好后,调用gl相关命令去画图,注意eglSwapBuffers(mDisplay, mSurface) 函数是非常重要的一个函数,会去触发queueBuffer和dequeueBuffer,图画就一帧一帧的画出来了。
bool BootAnimation::android()
{
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
// clear screen
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
//调用eglSwapBuffers会去触发queuebuffer,dequeuebuffer,
//queuebuffer将画好的buffer交给surfaceflinger处理,
//dequeuebuffer新创建一个buffer用来画图
eglSwapBuffers(mDisplay, mSurface); // 第六步
glEnable(GL_TEXTURE_2D);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
const GLint xc = (mWidth - mAndroid[0].w) / 2;
const GLint yc = (mHeight - mAndroid[0].h) / 2;
const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
updateRect.height());
// Blend state
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
const nsecs_t startTime = systemTime();
do {
nsecs_t now = systemTime();
double time = now - startTime;
float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
GLint x = xc - offset;
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);
glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
if (res == EGL_FALSE)
break;
// 12fps: don't animate too fast to preserve CPU
const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
if (sleepTime > 0)
usleep(sleepTime);
checkExit();
} while (!exitPending());
glDeleteTextures(1, &mAndroid[0].name);
glDeleteTextures(1, &mAndroid[1].name);
return false;
}
EGL的工作流程
以上开机动画,可分如下几个阶段:
-
1、 获取Display。
Display代表显示。获得Display要调用EGLboolean eglGetDisplay(NativeDisplay dpy),参数一般为 EGL_DEFAULT_DISPLAY 。对应开机动画就是如下代码://调用eglGetDisplay
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); -
2、 初始化egl。
调用 EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor),该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。对应开机动画就是如下代码:eglInitialize(display, 0, 0);
-
3、 选择Config。
所为Config实际指的是FrameBuffer的参数。一般用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 ()来读取,但不能修改。对应开机动画就是如下代码:eglChooseConfig(display, attribs, &config, 1, &numConfigs);
-
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()读取。对应开机动画就是如下代码:surface = eglCreateWindowSurface(display, config, s.get(), NULL);
-
5、 创建Context。
OpenGL的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,这些状态作用于程序提交的顶点 坐标等图元从而形成帧缓冲内的像素。在OpenGL的编程接口中,Context就代表这个状态机,程序的主要工作就是向Context提供图元、设置状态,偶尔也从Context里获取一些信息。
用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)来创建一个Context。对应开机动画就是如下代码:context = eglCreateContext(display, config, NULL, NULL);
-
6、 绘制。
应用程序通过OpenGL API进行绘制,一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示。对应开机动画就是如下代码:eglSwapBuffers(mDisplay, mSurface); /
EGL 官网详细讲述了Surface、Display、Context 概念。对应链接:
简单地说
(1)Display 是图形显示设备(显示屏)的抽象表示。大部分EGL函数都要带一个 Display 作为参数
(2)Context 是 OpenGL 状态机。Context 与 Surface 可以是一对一、多对一、一对多的关系
(3)Surface 是绘图缓冲,可以是 window、pbuffer、pixmap 三种类型之一
EGL 工作流程为:
(1)初始化
(2)配置
(3)创建Surface(绑定到平台Windowing系统)
(4)绑定Surface与Context
(5)Main Loop:渲染(OpenGL),交换离线缓冲(offline buffer)与显示缓冲
(6)释放资源
GLSurfaceView与EGL区别
- GLSurfaceView隐藏了EGL操作及渲染线程的细节,并提供了生命周期回调方法。
- EGL可以控制渲染循环,例如:可以没法控制帧速(fps),GLSurfaceView不能
简单Demo:
第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。