Modern OpenGL ES: ndk编程——画一个三角形之创建EGL窗口

上一节我们 知道了如何用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窗口
     由于,android_app 有一个userData,可以用来接受 用户自己定义的对象,如 

     
void android_main(struct android_app *app)
{
  ....
  winContext   wContext;  // 自己定义的窗口上下文结构体,里面包含了创建窗口的所有信息
 app->userData = &wContext;

  ....
}
这样 ,我们在 生命周期方法里,就能够对 我们定义的窗口上下文进行初始化,并创建窗口,如
void onAppCmd(struct android_app *app, int_32 md)
{
     winContext *wContext = (winContext) app->userData;

switch(cmd)
{
   ...
   case  APP_CMD_INIT_WINDOW:
     wContext->eglNativeDisplay = EGL_DEFAULT_DISPLAY;
    wContext->eglNativeWindow = app->window;
 ....
  //创建窗口
}
}



     在上面1-5 的分析中,可以看出,创建一个 EGL窗口,
         首先需要 EGLDisplay去获取本地系统的一个连接
         其次,要选择Surface的配置,并确定该配置
         然后,根据 android_app 提供的本地window,以及EGLDisplay 去创建一个Surface,并制定Surface的渲染模式。
         最后, 指定Surface的 Context。

     
     在上面1-5 的分析中,可以看出,创建一个 EGL窗口,
         首先需要 EGLDisplay去获取本地系统的一个连接
         其次,要选择Surface的配置,并确定该配置
         然后,根据 android_app 提供的本地window,以及EGLDisplay 去创建一个Surface,并制定Surface的渲染模式。
         最后, 指定Surface的 Context。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章