EGL详解

eglMakeCurrent()详解

在完成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中。

l frameworks/native/opengl/libagl

l frameworks/base/cmds/bootanimation

2.6.1 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。

2.6.2 context

Context是什么东西呢?我们先来看看下面的几个结构体。

l egl_context_t

egl_context_t类定义在frameworks/native/opengl/libs/egl/egl_object.h中。

 

l Ogles_context_t

先看看ogles_context_t结构体,定义在frameworks/native/opengl/libagl/context.h中。

l Context_t

Context_t结构体定义在system/core/include/private/pixelflinger/ggl_context.h中。

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功能。包括存储贴图及纹理、裁剪操作、光栅操作、混合操作、景深操作、雾化操作等功能。

 

这几个结构体之间的关系如图:

 

2.6.3 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之后,结构图如下:

 

2.6.4 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 flush

                glFlush();

                egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;

            }

        }

        if (!(c->flags & egl_context_t::IS_CURRENT)) { // 然后将context和thread进行绑定,同时将context flag设置为current

            // 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之后,整个结构关系如下:

 

2.6.5 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就可以起作用咯。

 

猜你喜欢

转载自blog.csdn.net/pkx1993/article/details/81631585