EGLContext: eglMakeCurrent詳解

1. 前言

在完成EGL的初始化之後,需要通過eglMakeCurrent()函數來將當前的上下文切換,這樣opengl的函數才能啓動作用。

boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)

該接口將申請到的display,draw(surface)和 context進行了綁定。也就是說,在context下的OpenGLAPI指令將draw(surface)作爲其渲染最終目的地。而display作爲draw(surface)的前端顯示。調用後,當前線程使用的EGLContex爲context。

那麼爲什麼調用了該函數之後,OpenGL ES的api函數就可以用了呢?
因爲我們沒有GPU驅動的代碼,所以,我們可以從libagl中看出一點端倪。
查看這部分代碼,建議將如下幾部分的sourcecode導入到sourceinsight中。

frameworks/native/opengl/libagl
frameworks/base/cmds/bootanimation

2.libGLES_android

在Android的架構設計中,使用模擬GPU的話,加載的庫應該爲libGLES_android.so。而從Android的編譯腳本中,可以知道libGLES_android.so的sourcecode的路徑爲:

N5_5.1/frameworks/native/opengl/libagl。

Pixelflinger是Android系統中爲OpenGL ES引擎提供的一套軟件渲染器(renderer)。Pixelflinger作爲軟件渲染器,爲OpenGL ES引擎提供了一系列基礎繪圖功能。這些功能包括定義各種顏色格式像素位置、畫點畫線、繪製矩形及三角形、填充紋理等等。由於OpenGL ES相當於一個狀態機,配置OpenGL ES狀態的函數也均由Pixelflinger提供。

Pixelflinger的源代碼位於system/core/libpixelflinger。
頭文件位於system/core/include/libpixelflinger和system/core/include/private/pixelflinger。

3.egl_context

Context是什麼東西呢?我們先來看看下面的幾個結構體。

egl_context_t

egl_context_t類定義在frameworks/native/opengl/libs/egl/egl_object.h中。

egl_context_t

ogles_context_t

先看看ogles_context_t結構體,定義在frameworks/native/opengl/libagl/context.h中。
ogles_context_t

context_t

context_t結構體定義在system/core/include/private/pixelflinger/ggl_context.h中。
context_t

context_t結構體,用來存儲OpenGL ES狀態及數據上下文。context_t結構體中包含GGLContext、state_t、shade_t、iterators_t、generated_vars_t等結構體。
GGLContext結構體中包含了Pixelflinger庫可提供給上層調用的一些函數接口,包括畫點畫線、繪製多邊形、配置OpenGL ES狀態機等函數接口。
state_t結構體用來描述OpenGL ES狀態機的狀態,包含多個子結構體,每個子結構體均負責處理一部分OpenGL ES功能。包括存儲貼圖及紋理、裁剪操作、光柵操作、混合操作、景深操作、霧化操作等功能。

這幾個結構體之間的關係如圖:
context_t

4. eglCreateContext

eglCreateContext實現在framework/native/opengl/libagl/egl.cpp中。該函數主要完成對egl_context_t結構體的初始化。同時調用ogles_init完成對應ogles_context_t結構體的初始化。

EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
                            EGLContext /*share_list*/, const EGLint* /*attrib_list*/)
{
    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
    ogles_context_t* gl = ogles_init(sizeof(egl_context_t));
    if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
    
    egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base);
    c->flags = egl_context_t::NEVER_CURRENT;
    c->dpy = dpy;
    c->config = config;
    c->read = 0;
    c->draw = 0;
    return (EGLContext)gl;
}

ogles_context_t *ogles_init(size_t extra)
{
    // 申請空間,空間大小爲egl_context_t & ogles_context_t + 32,爲了32位對齊。
    void* const base = malloc(extra + sizeof(ogles_context_t) + 32);
    if (!base) return 0;

    // 將指針C指向ogles_context_t,32位對齊
    ogles_context_t *c =
            (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL);
memset(c, 0, sizeof(ogles_context_t));
   // 調用ggl_init_context初始化context_t
    ggl_init_context(&(c->rasterizer));

    // XXX: this should be passed as an argument
    sp<EGLSurfaceManager> smgr(new EGLSurfaceManager());
    c->surfaceManager = smgr.get();
    c->surfaceManager->incStrong(c);

    sp<EGLBufferObjectManager> bomgr(new EGLBufferObjectManager());
    c->bufferObjectManager = bomgr.get();
    c->bufferObjectManager->incStrong(c);

    ogles_init_array(c);
    ogles_init_matrix(c);
    ogles_init_vertex(c);
    ogles_init_light(c);
    ogles_init_texture(c); // 初始化紋理相關的狀態。
    
    c->rasterizer.base = base;
    c->point.size = TRI_ONE;
    c->line.width = TRI_ONE;

    // in OpenGL, writing to the depth buffer is enabled by default.
    c->rasterizer.procs.depthMask(c, 1);

    // OpenGL enables dithering by default
    c->rasterizer.procs.enable(c, GL_DITHER);
    return c;
}


void ggl_init_context(context_t* c)
{
    memset(c, 0, sizeof(context_t));
    ggl_init_procs(c);// 將state_t結構體的函數指針與Pixelflinger.cpp中的函數掛鉤。
    ggl_init_trap(c);// 配置OpenGL ES狀態機的狀態。
    ggl_init_scanline(c); // 主要將scanline函數與context_t的接口掛鉤。
    ggl_init_texture(c); // 初始化紋理相關的狀態。
    ggl_init_picker(c); // 空函數。
    ggl_init_raster(c); // 將Raster.cpp中的函數與state_t結構體的函數指針掛鉤。
    c->formats = gglGetPixelFormatTable();
    c->state.blend.src = GGL_ONE;
    c->state.blend.dst = GGL_ZERO;
    c->state.blend.src_alpha = GGL_ONE;
    c->state.blend.dst_alpha = GGL_ZERO;
    c->state.mask.color = 0xF;
    c->state.mask.depth = 0;
    c->state.mask.stencil = 0xFFFFFFFF;
    c->state.logic_op.opcode = GGL_COPY;
    c->state.alpha_test.func = GGL_ALWAYS;
    c->state.depth_test.func = GGL_LESS;
    c->state.depth_test.clearValue = FIXED_ONE;
    c->shade.w0 = FIXED_ONE;
    memcpy(c->ditherMatrix, gDitherMatrix, sizeof(gDitherMatrix));
}

在eglCreateContext函數中,首先會調用ogles_init爲ogles_context_t & egl_context_t申請地址空間,並完成結構體中成員的賦值,然後將ogles_context_t->rasterizer->base指向elg_context_t。最後,返回執行ogles_context_t結構體的指針gl。

執行完成eglCreateContext之後,結構圖如下:

5. eglMakeCurrent

eglMakeCurrent將申請到的display,draw(surface)和 context進行了綁定。也就是說,在context下的OpenGLAPI指令將draw(surface)作爲其渲染最終目的地。而display作爲draw(surface)的前端顯示。調用後,當前線程使用的EGLContex爲context。

先看看在bootanimation.cpp中對於eglMakeCurrent的調用:

   if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
        return NO_INIT;

接下來在libagl中看看glMakeCurrent的實現。

EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
                            EGLSurface read, EGLContext ctx)
{
    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
    if (draw) {
        egl_surface_t* s = (egl_surface_t*)draw;
        if (!s->isValid())
            return setError(EGL_BAD_SURFACE, EGL_FALSE);
        if (s->dpy != dpy)
            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
        // TODO: check that draw is compatible with the context
    }
    if (read && read!=draw) {
        egl_surface_t* s = (egl_surface_t*)read;
        if (!s->isValid())
            return setError(EGL_BAD_SURFACE, EGL_FALSE);
        if (s->dpy != dpy)
            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
        // TODO: check that read is compatible with the context
    }
    EGLContext current_ctx = EGL_NO_CONTEXT;
    if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT))
        return setError(EGL_BAD_MATCH, EGL_FALSE);

    if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT))
        return setError(EGL_BAD_MATCH, EGL_FALSE);

    if (ctx == EGL_NO_CONTEXT) {
        // if we're detaching, we need the current context
        current_ctx = (EGLContext)getGlThreadSpecific();
    } else {
        egl_context_t* c = egl_context_t::context(ctx);
        egl_surface_t* d = (egl_surface_t*)draw;
        egl_surface_t* r = (egl_surface_t*)read;
        if ((d && d->ctx && d->ctx != ctx) ||
            (r && r->ctx && r->ctx != ctx)) {
            // one of the surface is bound to a context in another thread
            return setError(EGL_BAD_ACCESS, EGL_FALSE);
        }
    }
// 調用makeCurrent,將gl和當前的進程進行綁定。
    ogles_context_t* gl = (ogles_context_t*)ctx;
    if (makeCurrent(gl) == 0) {
        if (ctx) {
            egl_context_t* c = egl_context_t::context(ctx);
            egl_surface_t* d = (egl_surface_t*)draw;
            egl_surface_t* r = (egl_surface_t*)read;

            if (c->draw) {// 斷開當前draw surface的綁定
                egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
                s->disconnect();
                s->ctx = EGL_NO_CONTEXT;
                if (s->zombie)
                    delete s;
            }
            if (c->read) {
                // FIXME: unlock/disconnect the read surface too 
            }
            // 將draw & read 綁定到當前的上下文。
            c->draw = draw;
            c->read = read;

            if (c->flags & egl_context_t::NEVER_CURRENT) {
                c->flags &= ~egl_context_t::NEVER_CURRENT;
                GLint w = 0;
                GLint h = 0;
                if (draw) {
                    w = d->getWidth();
                    h = d->getHeight();
                }
                ogles_surfaceport(gl, 0, 0);
                ogles_viewport(gl, 0, 0, w, h);
                ogles_scissor(gl, 0, 0, w, h);
            }
            if (d) {
                if (d->connect() == EGL_FALSE) {
                    return EGL_FALSE;
                }
                d->ctx = ctx;
                d->bindDrawSurface(gl);
            }
            if (r) {
                // FIXME: lock/connect the read surface too 
                r->ctx = ctx;
                r->bindReadSurface(gl);
            }
        } else {//取消綁定
            // if surfaces were bound to the context bound to this thread
            // mark then as unbound.
            if (current_ctx) {
                egl_context_t* c = egl_context_t::context(current_ctx);
                egl_surface_t* d = (egl_surface_t*)c->draw;
                egl_surface_t* r = (egl_surface_t*)c->read;
                if (d) {
                    c->draw = 0;
                    d->disconnect();
                    d->ctx = EGL_NO_CONTEXT;
                    if (d->zombie)
                        delete d;
                }
                if (r) {
                    c->read = 0;
                    r->ctx = EGL_NO_CONTEXT;
                    // FIXME: unlock/disconnect the read surface too 
                }
            }
        }
        return EGL_TRUE;
    }
    return setError(EGL_BAD_ACCESS, EGL_FALSE);
}

這裏面將gl和當前的進程綁定是通過函數makecurrent來實現的。

static int makeCurrent(ogles_context_t* gl)
{
// 取得當前GlThread的context信息。
    ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
    if (gl) {
        egl_context_t* c = egl_context_t::context(gl);
        if (c->flags & egl_context_t::IS_CURRENT) { // 標記是當前的context,但實際卻不是,出錯。
            if (current != gl) {
                // it is an error to set a context current, if it's already
                // current to another thread
                return -1;
            }
        } else { // 執行glFlush,然後將當前的context標記爲非當前context
            if (current) {
                // mark the current context as not current, and flus
                glFlush();
                egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
            }
        }
        if (!(c->flags & egl_context_t::IS_CURRENT)) { // 然後將context和thread進行綁定,同時將context flag設置爲curren
            // The context is not current, make it current!
            setGlThreadSpecific(gl);
            c->flags |= egl_context_t::IS_CURRENT;
        }
    } else { // gl爲空,就將當前context flags設置爲非當前,然後將當前thread的context設置爲0
        if (current) 
            // mark the current context as not current, and flush
            glFlush();
            egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
        }
        // this thread has no context attached to it
        setGlThreadSpecific(0);
    }
    return 0;
}

我們來看看context和GlThread的設置及取得操作實現。這兩個函數是內聯函數,定義在context.h中。

#ifdef HAVE_ANDROID_OS
    // We have a dedicated TLS slot in bionic
    inline void setGlThreadSpecific(ogles_context_t *value) {
        __get_tls()[TLS_SLOT_OPENGL] = value;
    }
    inline ogles_context_t* getGlThreadSpecific() {
        return static_cast<ogles_context_t*>(__get_tls()[TLS_SLOT_OPENGL]);
    }
#else
    extern pthread_key_t gGLKey;
    inline void setGlThreadSpecific(ogles_context_t *value) {
        pthread_setspecific(gGLKey, value);
    }
    inline ogles_context_t* getGlThreadSpecific() {
        return static_cast<ogles_context_t*>(pthread_getspecific(gGLKey));
    }
#endif

從上面的代碼分析可以知道,eglMakeCurrent會將第四個參數context設置到GLThread的__get_tls()[TLS_SLOT_OPENGL]中,從而實現context和GLThread之間的聯繫,同時,會將第二個參數draw & 第三個參數read賦值給context的成員變量,實現了context 和surface的綁定。經過了eglMakeCurrent之後,整個結構關係如下:

6.glclear

接下來,我們來看一下一個簡單的opengl es的api實現。比如glclear

void glClear(GLbitfield mask) {

    ogles_context_t* c = ogles_context_t::get();

    c->rasterizer.procs.clear(c, mask);

}

可以在ogles_context_t結構體中看到get()函數的實現。

    static inline ogles_context_t* get() {

        return getGlThreadSpecific();

    }

明白了,當glclear別調用的時候,首先通過getGlThreadSpecific()獲取到當前GlThread的context信息。而這個context就是在eglMakeCurrent的時候和GLThread綁定的context。

所以,GLThread可以看成是一箇中間媒介,實現context的傳遞。

這樣就回到了爲什麼調用eglMakeCurrent完成GlThread上下文的設置之後,opengl es的api就可以起作用咯。


作者:happy19850920
來源:CSDN
原文:https://blog.csdn.net/happy19850920/article/details/50673005
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章