EGL简介以及窗口初始化

屏幕上可见的帧缓冲区由一个像素数据的二维数组表示。直接在可显示缓冲区上更新像素由一个严重的问题——用户在部分更新帧缓冲区时看到伪像或者闪烁的现象

为了解决这个问题,引入了双缓冲区。

OpenGLES应用程序中,这种活动通过EGL函数eglSwapBuffers控制:

eglSwapBuffers(exContext->eglDisplay, esContext->eglSurface);

EGL提供以下机制:

与设备的原生窗口系统通信

查询绘图表面的可用类型和配置

创建绘图表面

在OpenGLES3.0和其它图形渲染API(如桌面OpenGL和OpenVG——硬件加速矢量图形的跨平台API,活着窗口系统的原生绘图命令)之间同步渲染

管理纹理贴图等渲染资源

与窗口系统通信

Apple提供自己的EGL API的iOS 实现,称为EAGL

因为每个窗口系统都有不同语义,所以EGL提供基本的不透明类型——EGLDisplay,该类封装了所有系统相关性,用于和原生窗口系统接口交互。

任何使用EGL的应用程序必须执行的第一个操作是创建和初始化与本地EGL显示的连接。

一、初始化EGL

EGLConfig config;

EGLint majorVersion;

EGLint minorVersion;

EGLint contextAttribs[] = {

EGL_CONTEXT_CLIENT_VERSION,        3,        EGL_NONE

};

// 获取宽和高

esContext->width = ANativeWindow_getWidth(esContext->eglNativeWindow);esContext->height = ANativeWindow_getHeight(esContext->eglNativeWindow);

// 打开与EGL显示服务器的连接,默认为EGL_DEFAULT_DISPLAY

esContext->eglDisplay = eglGetDisplay(esContext->eglNativeDisplay);

// 如果显示连接不可用,则返回EGL_NO_DISPLAY标志,此时无法创建窗口

if (esContext->eglDisplay == EGL_NO_DISPLAY) {

return GL_FALSE;

}

// 初始化egl并回传版本号

if (!eglInitialize(esContext->eglDisplay, &majorVersion, &minorVersion)) {

return GL_FALSE;

}

成功打开连接后,需要初始化EGL,这通过调用如下函数完成:

EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);

dpy —— 指定EGL显示连接

major —— 返回EGL的主版本号

minor —— 返回EGL的副版本号

二、确定可用表面配置

一旦初始化了EGL,就可以确定可用渲染表面的类型和配置,有两种方法:

查询每个表面配置,找出最好的选择

指定一组需求,让EGL推荐最佳配置

通常使用第二种方法更简单,而且最有可能得到匹配。

任何一种情况下,EGL将返回一个EGLConfig,这是包含有关特定表面及其特性(每个颜色变量分量的位数、与EGLConfig相关的深度缓冲区(如果存在))的EGL内部数据结构的标识符。

EGLint numConfigs = 0;

EGLint attribList[] = {

EGL_RED_SIZE, 5,

EGL_GREEN_SIZE, 6,

EGL_BLUE_SIZE, 5,

EGL_ALPHA_SIZE, (flags & ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE,

EGL_DEPTH_SIZE, (flags & ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE,

EGL_STENCIL_SIZE, (flags & ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE,

EGL_SAMPLE_BUFFERS, (flags & ES_WINDOW_MULTISAMPLE) ? 1 : 0,

EGL_RENDERABLE_TYPE, getContextRenderableType(esContext->eglDisplay),

EGL_NONE

};

// 选择Config

if (!eglChooseConfig(esContext->eglDisplay, attribList, &config, 1, &numConfigs)) {

return GL_FALSE;

}

if (numConfigs < 1) {

return GL_FALSE;

}

三、查询EGLConfig属性

EGLConfig包含关于EGL启用的表面的所有信息。可用颜色、与配置相关的其它缓冲区(深度和模板缓冲区等)、表面类型和其它特性

// Android需要获取EGL_NATIVE_VISUAL_ID的值并将其放如ANativeWindow_setBuffersGeometry函数中

EGLint format = 0;

eglGetConfigAttrib(esContext->eglDisplay, config, EGL_NATIVE_VISUAL_ID, &format);

ANativeWindow_setBuffersGeometry(esContext->eglNativeWindow, 0, 0, format);

四、创建屏幕上的渲染区域:EGL窗口

EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy,

EGLConfig config,

EGLNativeWindowType win,

const EGLint *attrib_list);

该函数用于创建一个窗口,以我们得到的原生显示管理器的连接和前一步得到的EGLConfig为参数。它需要原生窗口系统事先创建一个窗口。

创建窗口示例:

// 创建一个EGL窗口表面示例

EGLint list[] = {

EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE

};

EGLSurface window = eglCreateWindowSurface(esContext->eglDisplay, config,

esContext->eglNativeWindow, list);

if (window == EGL_NO_SURFACE) {

switch (eglGetError()) {

case EGL_BAD_MATCH:

break;

case EGL_BAD_CONFIG:

break;

case EGL_BAD_NATIVE_WINDOW:

break;

case EGL_BAD_ALLOC:

break;

}

}

上面的代码是创建一个绘图场景,但是我们还必须完成两个步骤,才能成功地用OpenGLES绘图。然而窗口不是唯一可用的渲染表面,也可以使用屏幕外渲染区域,也就是离屏渲染。

屏幕外渲染区域(离屏渲染):EGL Pbuffer

OpenGLES3.0除了可以进行屏幕渲染,也可以在不可见的屏幕外渲染,也就是在像素缓冲区上进行渲染,支持硬件加速。Pbuffer最常用于纹理贴图。

创建Pbuffer 和 创建EGL窗口非常相似,只有少数微笑的不同。

创建Pbuffer需要和窗口一样找到EGLConfig,并作一些修改:需要扩增EGL_SURFACE_TYPE的值,使其包含EGL_PBUFFER_BIT。

创建EGL像素缓冲区示例如下:

EGLint bufferList[] = {

EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,

EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,

EGL_RED_SIZE, 5,

EGL_GREEN_SIZE, 6,

EGL_BLUE_SIZE, 5,

EGL_DEPTH_SIZE, 1,

EGL_NONE

};

const EGLint MaxConfigs = 10;

EGLConfig configs[MaxConfigs];

EGLint numConfigs;

if (!eglChooseConfig(esContext->eglDisplay, bufferList, configs, MaxConfigs, &numConfigs)) {

return GL_FALSE;

}

EGLSurface pbuffer;

EGLint attribufferList[] = {

EGL_WIDTH, 512,

EGL_HEIGHT, 512,

EGL_LARGEST_PBUFFER, EGL_TRUE,

EGL_NONE

};

pbuffer = eglCreatePbufferSurface(esContext->eglDisplay, config, attribufferList);

if (pbuffer == EGL_NO_SURFACE) {

switch (eglGetError()) {

case EGL_BAD_ALLOC:

break;

case EGL_BAD_CONFIG:

break;

case EGL_BAD_PARAMETER:

break;

case EGL_BAD_MATCH:

break;

}

}

// 检查pbuffer 分配的内存大小

EGLint width;

EGLint height;

if (!eglQuerySurface(esContext->eglDisplay, pbuffer, EGL_WIDTH, &width)

|| !eglQuerySurface(esContext->eglDisplay, pbuffer, EGL_HEIGHT, &height)) {

// 不能查询 surface的信息

return GL_FALSE;

}

五、创建渲染上下文

渲染上下文是OpenGLES3.0的内部数据结构,包含操作所需要的所有状态信息。它包含了对顶点着色器和片元着色器数据的引用。

// 5.创建上下文

esContext->eglContext = eglCreateContext(esContext->eglDisplay, config,

EGL_NO_CONTEXT, contextAttribs);

// 判断上下文是否创建成功

if (esContext->eglContext == EGL_NO_CONTEXT) {

return GL_FALSE;

}

// 根据之前的配置构建上下文

if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface, esContext->eglSurface, esContext->eglContext)) {

return GL_FALSE;

}

六、指定某个EGLContext为当前上下文

// 指定某个EGLContext为当前上下文

if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface,

esContext->eglSurface, esContext->eglContext)) {

return GL_FALSE;

}

同步渲染

多个图形API在单个窗口中的渲染,此时需要使用同步渲染,需要应用程序允许多个库渲染到共享窗口。

如果不止使用OpenGLES3.0渲染,则不能简单地调用glFinish来抱着个所有渲染已经发生,你可以调用:

EGLBoolean eglWaitClient()

延迟客户端的执行,直到某个API的所有渲染完成,成功返回EGL_TRUE,失败时返回EGL_FALSE并发送EGL_BAD_CURRENT_SURFACE错误

同样,如果你需要保证原生窗口系统的渲染完成,则调用如下函数:

EGLBoolean eglWaitNative(EGLint engine)

engine,指定渲染程序等待渲染完成

你可能感兴趣的:(EGL简介以及窗口初始化)