上一節我們 知道了如何用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窗口