上一节我们 知道了如何用ndk 来创建一个 Activity
这一节,我们来创建 GL 的窗口
上一节已经知道, Khronos Group 为我们提供了 EGL API 来帮助我们创建 窗口,同步窗口绘制,管理窗口渲染。
1 与窗口系统通信
EGL在OpenGL ES 和 本地窗口系统之间 提供了一个 "glue"层。 在EGL 决定那种类型的绘制surface可以用之前,它先开启了与窗口系统通信的通道。
1.1 EGLDisplay
因为,每个窗口系统都有其不同的实现机制,因此EGL提供了一种封装了与本地窗口系统交互的系统库的 类型: EGLDisplay。
使用EGL的应用程序所做的第一步就是创建和初始化与本地EGL display的连接。
EGLDisplay
eglGetDisplay(EGLNativeDisplayType displayId)
参数:
displayId: 定义了 display的连接, 默认为 EGL_DEFAULT_DISPLAY
它用来匹配本地窗口系统display的类
返回值: 如果失败, 它会返回EGL_NO_DISPLAY
1.2 初始化EGL
一旦获取了正确的链接, EGL 需要初始化
EGLBoolean
eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion)
参数:
display:
majorVersion: 定义了实现EGL的最大版本
minorVersion: 定义了实现EGL的最小版本
功能: 该函数初始化了EGL内部的数据结构,并返回其最大最小版本号。
返回值: 如果EGL初始化失败,会返回EGL_FALSE,并返回EGL_BAD_DISPLAY,提示一个无效的EGLDisplay, 和返回 EGL_NOT_INITIALIZED 提示EGL没有初始化
2 确定可用的Surfae配置
一旦初始化了EGL, 我们就可以决定渲染Surface的类型和配置。 有两种方式:
确定每个Surface的配置,并找出最佳选择
自己定义一组配置,然后让EGL推荐最佳配置
在多数情况下,第二种比较简单实现, 而且基本与第一种效果一样。 不管哪种方法,EGL都会返回一个 EGLConfig。 EGLConfig是 EGL内部数据结构的标识, 该数据结构包含了特定Surface的信息和特性: 颜色分量的量化程度(bit数), 深度buffer。
2.1 让EGL 选择配置
EGLBoolean
eglChooseConfig(EGLDisplay display, const EGLint *attribList, EGLConfig *configs, EGLint maxReturnConfigs, EGLint *numConfigs)
参数:
display:
attribList: 定义了与 configs匹配的一组属性
configs: 返回的一组符合要求的配置
maxReturnConfigs: 返回最大的配置数目
numConfigs: 返回返回配置的数目
返回值:
EGL_TRUE
EGL_FALSE:
EGL_BAD_ATTRIBUTE: 如果attribList里有不符合要求的。
功能:一旦eglChooseConfig返回成功, 会返回一组符合要求的配置
2.2 确定配置
EGLConfig 包含了关于 EGL获得的Surface的所有信息。
可获取的颜色表示范围, depth buffers, stencil buffers, multisample buffers等。
在选择可用的Surface配置后,需要确定配置
EGLBoolean
eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value)
参数:
display: 定义EGL display连接
config: 定义了确认的配置
attribute: 返回的特定属性
value: 返回的特定的值
返回值:
EGL_TRUE
EGL_FALSE
EGL_BAD_ATTRIBUTE: 如果attribute不是有效属性
3 创建渲染区域: EGL Window
一旦我们确定了与渲染相合适的 EGLConfig 后,我们就可以创建窗口了。
EGLSurface
eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType window, const EGLint *attribList)
参数:
display:
config:
window: 指定一个 本地窗口
attribuList, 指定一组窗口属性, 也许是NULL. 是Surface的属性,以EGL_RENDER_BUFFER开头, 以 EGL_NONE 结尾
中间设置的参数有: EGL_SIGLE_BUFFER, EGL_BACK_BUFFER, 默认是 EGL_BACK_BUFFER
说明:
它的第三个参数 就是上一节中 我们创建的Android窗口
EGL_SIGLE_BUFFER 和 EGL_BACK_BUFFER代表了两种渲染的方式。 前者是直接渲染到显示的buffer里, 后者是 先渲染到BACK_BUFFER,然后前后buffer交换。
返回值:
EGL_BAD_MATCH: EGLConfig 与 native window不匹配
EGL_BAD_CONFIG:EGLConfig不被系统支持
EGL_BAD_NATIVE_WINDOW: 当创建native window 无效
EGL_BAD_ALLOCAT: eglCreateWindowSurface 不能为EGL window分配资源
4 创建渲染的 Context
一个渲染的Context是 OpenGL ES3.0的一个数据结构,包含了所有要操作的 状态信息。
如, 它包含了 vertex/fragment shader以及顶点数据的所有引用。
EGLContext
eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint *attribList)
参数:
display:
config,
sharedContext: 运行多个 EGL Context共享某种类型的数据,如shader program和 texture map。 如果没有共享的:EGL_NO_CONTEXT
attribList: 指定EGL Context的版本
5 使EGLContext 称为当前Context
这很重要。 我们可能会创建多个 Context, 因此需要当前渲染的Surface绑定一个 Context。
EGLBoolean
eglMakeCurrent(EGLDisplay display , EGLSurface draw, EGLSurface read, EGLContext context)
参数:
display:
draw: 指定现实的Surface
read: 指定渲染到的Surface
context: 指定了Surface 依附的Context
______________________________________________________________________________
实现EGL 创建窗口
按照上一帖,
void android_main(strut android_app *app)不仅是 我们程序的入口,而且app 还为我们提供了 本地的window。
struct android_app中 有一个 用于 处理app命令的回掉函数:
static void onAppCmd(struct android_app *app, int32_t cmd)
其中,cmd有几种类型: APP_CMD_SAVE_STATE, APP_CMD_INIT_WINDOW, APP_CMD_TERM_WINDOW, APP_CMD_GAINED_FOCUS, APP_CMD_LOST_FOCUS。
这几种类型 代表了 窗口的生命周期。 当窗口启动时 会执行 APP_CAM_INIT_WINDOW, APP_CMD_GAINED_FOCUS; 当窗口销毁时会有 APP_CMD_LOST_FOCUS, APP_CMD_TERM_WINDOW
在本地窗口处于 APP_CMD_INIT_WINDOW的时候,我们可以创建EGL窗口