PS
咱們在前面的文章中就說過關於EGL的出現緣由以及其做用OpenGL 是一個跨平臺的API,而不一樣的操做系統(Windows,Android,IOS)各有本身的屏幕渲染實現。因此OpenGL定義了一箇中間接口層EGL(Embedded Graphics Library)標準,具體實現交給各個操做系統自己java
簡單來講EGL是一箇中間接口層,是一個規範,因爲OpenGL的跨平臺性,因此說這個規範顯得尤爲重要,無論各個操做系統如何蹦躂,都不能脫離我所定義的規範。c++
EGL定義的一個抽象的系統顯示類,用於操做設備窗口。緩存
EGL配置,如rgba位數jvm
渲染緩存,一塊內存空間,全部要渲染到屏幕上的圖像數據,都要先緩存在EGLSurface上。ide
OpenGL上下文,用於存儲OpenGL的繪製狀態信息、數據。函數
初始化EGL的過程能夠說是對上面幾個信息進行配置的過程oop
咱們在使用Java GLSurfaceView的時候其實只是自定義了Render,該Render實現了GLsurfaceView.Renderer接口,而後自定義的Render中的3個方法就會獲得回調,Android 系統其實幫我省掉了其中的不少步驟。因此咱們這裏來看一下完整流程
(1). 獲取顯示設備(對應於上面的EGLDisplay)this
/* * Get an EGL instance */ mEgl = (EGL10) EGLContext.getEGL(); /* * Get to the default display. */ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
(2). 初始化EGLspa
int[] version = new int[2]; //初始化屏幕 if(!mEgl.eglInitialize(mEglDisplay, version)) { throw new RuntimeException("eglInitialize failed"); }
(3). 選擇Config(用EGLConfig配置參數)操作系統
//這段代碼的做用是選擇EGL配置, 便可以本身先設定好一個你但願的EGL配置,好比說RGB三種顏色各佔幾位,你能夠隨便配,而EGL可能不能知足你全部的要求,因而它會返回一些與你的要求最接近的配置供你選擇。 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) { throw new IllegalArgumentException("eglChooseConfig#2 failed"); }
(4). 建立EGLContext
//從上一步EGL返回的配置列表中選擇一種配置,用來建立EGL Context。 egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, mEGLContextClientVersion != 0 ? attrib_list : null);
(5). 獲取EGLSurface
//建立一個窗口Surface,能夠當作屏幕所對應的內存 egl.eglCreateWindowSurface(display, config, nativeWindow, null)
PS
這裏的nativeWindow是GLSurfaceView的surfaceHolder
(6). 綁定渲染環境到當前線程
/* * Before we can issue GL commands, we need to make sure * the context is current and bound to a surface. */ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { /* * Could not make the context current, probably because the underlying * SurfaceView surface has been destroyed. */ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); return false; }
循環繪製
loop:{ //繪製中.... //(7).交換緩衝區 mEglHelper.swap(); } public int swap() { if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { return mEgl.eglGetError(); } return EGL10.EGL_SUCCESS; }
上面咱們介紹了EGL的一些基礎知識,接着咱們來看在GLSurfaceView/GLTextureView中EGL的具體實現,咱們來從源碼上剖析Android系統EGL及GL線程
咱們來看一下GLThread,GLThread也是從普通的Thread類繼承而來,理論上就是一個普通的線程,爲何它擁有OpenGL繪圖能力?繼續往下看,裏面最重要的部分就是guardedRun()方法
static class GLThread extends Thread { ... @Override public void run() { try { guardedRun(); } catch (InterruptedException e) { // fall thru and exit normally } finally { sGLThreadManager.threadExiting(this); } } }
讓咱們來看一下guardedRun()方法裏有什麼東西,guardedRun()裏大體作的事情:
private void guardedRun() throws InterruptedException { while(true){ //if ready to draw ... mEglHelper.start();//對應於上面完整流程中的(1)(2)(3)(4) ... mEglHelper.createSurface()//對應於上面完整流程中的(5)(6) ... 回調GLSurfaceView.Renderer的onSurfaceCreated(); ... 回調GLSurfaceView.Renderer的onSurfaceChanged(); ... 回調GLSurfaceView.Renderer的onDrawFrame(); ... mEglHelper.swap();//對應於上面完整流程中的(5)(7) } }
從上面咱們的分析得知GLSurfaceView 中的GLThread就是一個普通的線程,只不過它按照了OpenGL繪圖的完整流程正確地操做了下來,所以它有OpenGL的繪圖能力。那麼,若是咱們本身建立一個線程,也按這樣的操做方法,那咱們也能夠在本身建立的線程裏繪圖嗎?答案是確定的(這不正是EGL的接口意義),下面我會給出EGL在Native C/C++中的實現。
Android Native環境中並不存在現成的EGL環境,因此咱們在進行OpenGL的NDK開發時就必須本身實現EGL環境,那麼如何實現呢,咱們只須要參照GLSurfaceView中的GLThread的寫法就能實現Native中的EGL
PS
一下的內容可能須要你對C/C++以及NDK 有必定熟悉
gl_render.h
class GLRender { private: const char *TAG = "GLRender"; //OpenGL渲染狀態 enum STATE { NO_SURFACE, //沒有有效的surface FRESH_SURFACE, //持有一個爲初始化的新的surface RENDERING, //初始化完畢,能夠開始渲染 SURFACE_DESTROY, //surface銷燬 STOP //中止繪製 }; JNIEnv *m_env = NULL; //線程依附的jvm環境 JavaVM *m_jvm_for_thread = NULL; //Surface引用,必需要使用引用,不然沒法在線程中操做 jobject m_surface_ref = NULL; //本地屏幕 ANativeWindow *m_native_window = NULL; //EGL顯示錶面 EglSurface *m_egl_surface = NULL; int m_window_width = 0; int m_window_height = 0; // 繪製代理器 ImageRender *pImageRender; //OpenGL渲染狀態 STATE m_state = NO_SURFACE; // 初始化相關的方法 void InitRenderThread(); bool InitEGL(); void InitDspWindow(JNIEnv *env); // 建立/銷燬 Surface void CreateSurface(); void DestroySurface(); // 渲染方法 void Render(); void ReleaseSurface(); void ReleaseWindow(); // 渲染線程回調方法 static void sRenderThread(std::shared_ptr<GLRender> that); public: GLRender(JNIEnv *env); ~GLRender(); //外部傳入Surface void SetSurface(jobject surface); void Stop(); void SetBitmapRender(ImageRender *bitmapRender); // 釋放資源相關方法 void ReleaseRender(); ImageRender *GetImageRender(); };
gl_render.cpp
//構造函數 GLRender::GLRender(JNIEnv *env) { this->m_env = env; //獲取JVM虛擬機,爲建立線程做準備 env->GetJavaVM(&m_jvm_for_thread); InitRenderThread(); } //析構函數 GLRender::~GLRender() { delete m_egl_surface; } //初始化渲染線程 void GLRender::InitRenderThread() { // 使用智能指針,線程結束時,自動刪除本類指針 std::shared_ptr<GLRender> that(this); std::thread t(sRenderThread, that); t.detach(); } //線程回調函數 void GLRender::sRenderThread(std::shared_ptr<GLRender> that) { JNIEnv *env; //(1) 將線程附加到虛擬機,並獲取env if (that->m_jvm_for_thread->AttachCurrentThread(&env, NULL) != JNI_OK) { LOGE(that->TAG, "線程初始化異常"); return; } // (2) 初始化 EGL if (!that->InitEGL()) { //解除線程和jvm關聯 that->m_jvm_for_thread->DetachCurrentThread(); return; } //進入循環 while (true) { //根據OpenGL渲染狀態進入不一樣的處理 switch (that->m_state) { //刷新Surface,從外面設置Surface後m_state置爲該狀態,說明已經從外部(java層)得到Surface的對象了 case FRESH_SURFACE: LOGI(that->TAG, "Loop Render FRESH_SURFACE") // (3) 初始化Window that->InitDspWindow(env); // (4) 建立EglSurface that->CreateSurface(); // m_state置爲RENDERING狀態進入渲染 that->m_state = RENDERING; break; case RENDERING: LOGI(that->TAG, "Loop Render RENDERING") // (5) 渲染 that->Render(); break; case STOP: LOGI(that->TAG, "Loop Render STOP") //(6) 解除線程和jvm關聯 that->ReleaseRender(); that->m_jvm_for_thread->DetachCurrentThread(); return; case SURFACE_DESTROY: LOGI(that->TAG, "Loop Render SURFACE_DESTROY") //(7) 釋放資源 that->DestroySurface(); that->m_state = NO_SURFACE; break; case NO_SURFACE: default: break; } usleep(20000); } }
咱們定義的GLRender各個流程代碼裏已經標註了步驟,雖然代碼量比較多,可是咱們的c++ class分析也是跟java相似,
PS
上圖中的(3)(4)等步驟對應於代碼中的步驟註釋
本篇文章咱們介紹了EGL,以及分析了Java 中GLSurfaceView中的EGL實現,而後咱們試着參照Java端的代碼實現Native中的EGL環境,關於代碼以及流程圖中的細節,咱們下篇再來分析。