拋開Nvidia閉源驅動,mesa可以說是linux系統標配的OpenGL框架實現。稱之爲框架實現,是因爲mesa並不是一個獨立應用,它編譯的結果是一堆.so動態庫。對應用程序而言,mesa提供了實現所有OpenGL和GLX API的libGL.so動態庫。對顯卡驅動而言,mesa提供了一套將應用層API(glXXX())與驅動實現動態結合的機制,具體可瞭解(DRI/DRM, Gallium3D)。mesa的靈活之處在於,不管底層用哪個廠家的驅動,上層libGL.so實現其實是一樣的,即代碼完全複用。顯卡驅動只需要實現特定的接口。
OpenGL API Dispatch在mesa官網上有講解:https://www.mesa3d.org/dispatch.html
對應的代碼如下,集中在 src/mapi 目錄:
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指針和當前上下文:
#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函數連接起來。
/**
* 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指針的讀寫問題,以及針對特定平臺的優化。
/* 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()