抛开Nvidia闭源驱动,mesa能够说是linux系统标配的OpenGL框架实现。称之为框架实现,是由于mesa并非一个独立应用,它编译的结果是一堆.so动态库。对应用程序而言,mesa提供了实现全部OpenGL和GLX API的libGL.so动态库。对显卡驱动而言,mesa提供了一套将应用层API(glXXX())与驱动实现动态结合的机制,具体可了解(DRI/DRM, Gallium3D)。mesa的灵活之处在于,无论底层用哪一个厂家的驱动,上层libGL.so实现实际上是同样的,即代码彻底复用。显卡驱动只须要实现特定的接口。html
OpenGL API Dispatch在mesa官网上有讲解:https://www.mesa3d.org/dispatch.htmllinux
对应的代码以下,集中在 src/mapi 目录:api
mesa-19.0.5/src - mapi - mapi_glapi.c: _glapi_set_dispatch() will be called by mesa state tracker. - u_current.c: u_current_set_table() Set the global or per-thread dispatch table pointer. - es1api/glapi_mapi_tmp.h: The implementations of all OpenGL ESv1 functions. - es2api/glapi_mapi_tmp.h: The implementations of all OpenGL ESv2 functions. - glapi: OpenGL API dispatch layer, it is responsible for dispatching all the gl*() functions. - glapi_mapi_tmp.h: The implementations of all OpenGL functions. - glapi.h:Define GET_DISPATCH(). - glapitable.h: Define struct _glapi_table. - glapitemp.h: This file is a template which generates the OpenGL API entry point functions. - glapi_dispatch.c: This file generates all the gl* function entrypoints through #include "glapi/glapitemp.h". - glprocs.h: This file is only included by glapi.c and is used for the GetProcAddress() function. - glapi_getproc.c: Code for implementing glXGetProcAddress()
全部OpenGL声明和定义在 glapi_mapi_tmp.h ,格式都很统一。
声明:多线程
GLAPI void APIENTRY GLAPI_PREFIX(NewList)(GLuint list, GLenum mode);
实现:框架
GLAPI void APIENTRY GLAPI_PREFIX(NewList)(GLuint list, GLenum mode) { /* 获取当前上下文使用的struct _glapi_table指针 */ const struct _glapi_table *_tbl = entry_current_get(); /* 从struct _glapi_table指针索引到目标函数指针 */ mapi_func _func = ((const mapi_func *) _tbl)[0]; /* 调用 */ ((void (APIENTRY *)(GLuint list, GLenum mode)) _func)(list, mode); }
结构体struct _glapi_table的定义在 src/mapi/glapi/glapitable.h :
这个结构体包含了mesa支持的全部GL函数。GLAPIENTRYP宏是指针标识符*,即_glapi_table每个数据成员是一个函数指针。函数
struct _glapi_table { void (GLAPIENTRYP NewList)(GLuint list, GLenum mode); /* 0 */ void (GLAPIENTRYP EndList)(void); /* 1 */ void (GLAPIENTRYP CallList)(GLuint list); /* 2 */ void (GLAPIENTRYP CallLists)(GLsizei n, GLenum type, const GLvoid * lists); /* 3 */ void (GLAPIENTRYP DeleteLists)(GLuint list, GLsizei range); /* 4 */ GLuint (GLAPIENTRYP GenLists)(GLsizei range); /* 5 */ ... }
glapi.h定义了宏用于获取当前的GL Dispatch指针和当前上下文:oop
#define GET_DISPATCH() \ (likely(_glapi_Dispatch) ? _glapi_Dispatch : _glapi_get_dispatch()) #define GET_CURRENT_CONTEXT(C) struct gl_context *C = (struct gl_context *) \ (likely(_glapi_Context) ? _glapi_Context : _glapi_get_context())
mapi_glapi.c提供了函数对当前GL Dispatch指针赋值。mesa在建立或切换上下文时会调用_glapi_set_dispatch()修改该指针:优化
/* It will be called in src/mesa/main code */ void _glapi_set_dispatch(struct _glapi_table *dispatch) { u_current_set_table((const struct _glapi_table *) dispatch); } #define u_current_table_glapi_Dispatch #define u_current_context _glapi_Context /** * Set the global or per-thread dispatch table pointer. * If the dispatch parameter is NULL we'll plug in the no-op dispatch * table (__glapi_noop_table). */ void u_current_set_table(const struct _glapi_table *tbl) { u_current_init(); stub_init_once(); if (!tbl) tbl = (const struct _glapi_table *) table_noop_array; #if defined(GLX_USE_TLS) u_current_table = (struct _glapi_table *) tbl; #else tss_set(u_current_table_tsd, (void *) tbl); u_current_table = (ThreadSafe) ? NULL : (void *) tbl; #endif }
在mesa建立GL上下文时,会调用_mesa_initialize_exec_table()初始化struct _glapi_table *exec,从而将struct _glapi_table结构体的gl函数指针与mesa函数链接起来。ui
/** * Initialize a context's exec table with pointers to Mesa's supported GL functions. * * * This function depends on ctx->Version. * * \param ctx GL context to which \c exec belongs. */ void _mesa_initialize_exec_table(struct gl_context *ctx) { struct _glapi_table *exec; exec = ctx->Exec; assert(exec != NULL); assert(ctx->Version > 0); _mesa_initialize_exec_dispatch(ctx, exec); if (!_mesa_is_no_error_enabled(ctx) && (_mesa_is_desktop_gl(ctx) || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))) { SET_BeginTransformFeedback(exec, _mesa_BeginTransformFeedback); SET_BindBufferRange(exec, _mesa_BindBufferRange); SET_BindFragDataLocation(exec, _mesa_BindFragDataLocation); SET_BindFragDataLocationIndexed(exec, _mesa_BindFragDataLocationIndexed); SET_BindSampler(exec, _mesa_BindSampler); ... } ... }
src/mapi目录下还有部分代码是处理多上下文和多线程环境下GL Dispatch指针的读写问题,以及针对特定平台的优化。spa
/* GL API Call Trace */ glDrawArrays(...); // src/mapi/glapi/glapi_mapi_tmp.h ((struct _glapi_table *disp)[310])(...); // Mesa GL API Dispatch Layer _mesa_DrawArrays(...); // src/mesa/main/draw.c. More info: _mesa_initialize_exec_dispatch() (struct gl_context *)ctx->Driver.Draw(...); // src/mesa/main/draw.c. Binding Mesa API and State Tracker API: st_init_draw_functions() st_draw_vbo(...); //src/mesa/state_tracker/st_draw.c (struct pipe_context *)pipe->draw_vbo(...); //Call into the GPU driver virgl_draw_vbo(...); // e.g. VirtioGPU. Binging through virgl_context_create()/virgl_create_screen()/virgl_drm_screen_create()