要想解析Mesa GL Dispatch的原理,首先需要对mesa进行编译
__glXInitialize --> driCreateDisplay --> driCreateScreen
--> driOpenDriver
--> loader_open_driver
\--> loader_get_extensions_name
--> __driDriverGetExtensions_##drivername
\--> globalDriverAPI = &galliumdrm_driver_api
\--> dri2_init_screen
\--> dri_init_screen_helper
\--> st_gl_api_create
\–> return st_gl_api
\–> .create_context = st_api_create_context
\–> st_create_context
\–> st_create_context_priv
\–> _mesa_initialize_dispatch_tables
\–> _mesa_initialize_exec_table
// 省略
#define GLAPI_PREFIX(func) gl##func
// 省略
GLAPI void APIENTRY GLAPI_PREFIX(NewList)(GLuint list, GLenum mode);
// 省略
GLAPI void APIENTRY GLAPI_PREFIX(NewList)(GLuint list, GLenum mode)
{
const struct _glapi_table *_tbl = entry_current_get();
mapi_func _func = ((const mapi_func *) _tbl)[0];
((void (APIENTRY *)(GLuint list, GLenum mode)) _func)(list, mode);
}
// 省略
void
_mesa_initialize_exec_table(struct gl_context *ctx)
{
struct _glapi_table *exec;
exec = ctx->Exec;
// 省略
SET_NewList(exec, _mesa_NewList);
/**
* Begin a new display list.
*/
void GLAPIENTRY
_mesa_NewList(GLuint name, GLenum mode)
{
GET_CURRENT_CONTEXT(ctx);
FLUSH_CURRENT(ctx, 0); /* must be called before assert */
ASSERT_OUTSIDE_BEGIN_END(ctx);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glNewList %u %s\n", name,
_mesa_enum_to_string(mode));
if (name == 0) {
_mesa_error(ctx, GL_INVALID_VALUE, "glNewList");
return;
}
if (mode != GL_COMPILE && mode != GL_COMPILE_AND_EXECUTE) {
_mesa_error(ctx, GL_INVALID_ENUM, "glNewList");
return;
}
if (ctx->ListState.CurrentList) {
/* already compiling a display list */
_mesa_error(ctx, GL_INVALID_OPERATION, "glNewList");
return;
}
ctx->CompileFlag = GL_TRUE;
ctx->ExecuteFlag = (mode == GL_COMPILE_AND_EXECUTE);
/* Reset accumulated list state */
invalidate_saved_current_state( ctx );
/* Allocate new display list */
ctx->ListState.CurrentList = make_list(name, BLOCK_SIZE);
ctx->ListState.CurrentBlock = ctx->ListState.CurrentList->Head;
ctx->ListState.CurrentPos = 0;
vbo_save_NewList(ctx, name, mode);
ctx->CurrentServerDispatch = ctx->Save;
_glapi_set_dispatch(ctx->CurrentServerDispatch);
if (ctx->MarshalExec == NULL) {
ctx->CurrentClientDispatch = ctx->CurrentServerDispatch;
}
}
// 省略
/**
* \file main/dispatch.h
* Macros for handling GL dispatch tables.
*
* For each known GL function, there are 3 macros in this file. The first
* macro is named CALL_FuncName and is used to call that GL function using
* the specified dispatch table. The other 2 macros, called GET_FuncName
* can SET_FuncName, are used to get and set the dispatch pointer for the
* named function in the specified dispatch table.
*/
#define CALL_by_offset(disp, cast, offset, parameters) \
(*(cast (GET_by_offset(disp, offset)))) parameters
#define GET_by_offset(disp, offset) \
(offset >= 0) ? (((_glapi_proc *)(disp))[offset]) : NULL
#define SET_by_offset(disp, offset, fn) \
do { \
if ( (offset) < 0 ) { \
/* fprintf( stderr, "[%s:%u] SET_by_offset(%p, %d, %s)!\n", */ \
/* __func__, __LINE__, disp, offset, # fn); */ \
/* abort(); */ \
} \
else { \
( (_glapi_proc *) (disp) )[offset] = (_glapi_proc) fn; \
} \
} while(0)
// 省略
/* total number of offsets below */
#define _gloffset_COUNT 1607
#define _gloffset_NewList 0
#define _gloffset_EndList 1
// 省略
static inline void SET_NewList(struct _glapi_table *disp, void (GLAPIENTRYP fn)(GLuint, GLenum)) {
SET_by_offset(disp, _gloffset_NewList, fn);
}
// 省略
struct gl_context
{
/** State possibly shared with other contexts in the address space */
struct gl_shared_state *Shared;
/** \name API function pointer tables */
/*@{*/
gl_api API;
/**
* The current dispatch table for non-displaylist-saving execution, either
* BeginEnd or OutsideBeginEnd
*/
struct _glapi_table *Exec;
// 省略
}
void
_mesa_initialize_dispatch_tables(struct gl_context *ctx)
{
/* Do the code-generated setup of the exec table in api_exec.c. */
_mesa_initialize_exec_table(ctx);
if (ctx->Save)
_mesa_initialize_save_table(ctx);
}
static struct st_context *
st_create_context_priv(struct gl_context *ctx, struct pipe_context *pipe,
const struct st_config_options *options, bool no_error)
{
// 省略
_mesa_override_extensions(ctx);
_mesa_compute_version(ctx);
if (ctx->Version == 0) {
/* This can happen when a core profile was requested, but the driver
* does not support some features of GL 3.1 or later.
*/
st_destroy_context_priv(st, false);
return NULL;
}
_mesa_initialize_dispatch_tables(ctx);
_mesa_initialize_vbo_vtxfmt(ctx);
// 省略
}
struct st_context *
st_create_context(gl_api api, struct pipe_context *pipe,
const struct gl_config *visual,
struct st_context *share,
const struct st_config_options *options,
bool no_error)
{
struct gl_context *ctx;
struct gl_context *shareCtx = share ? share->ctx : NULL;
struct dd_function_table funcs;
struct st_context *st;
util_cpu_detect();
memset(&funcs, 0, sizeof(funcs));
st_init_driver_functions(pipe->screen, &funcs);
ctx = calloc(1, sizeof(struct gl_context));
if (!ctx)
return NULL;
if (!_mesa_initialize_context(ctx, api, visual, shareCtx, &funcs)) {
free(ctx);
return NULL;
}
st_debug_init();
if (pipe->screen->get_disk_shader_cache)
ctx->Cache = pipe->screen->get_disk_shader_cache(pipe->screen);
/* XXX: need a capability bit in gallium to query if the pipe
* driver prefers DP4 or MUL/MAD for vertex transformation.
*/
if (debug_get_option_mesa_mvp_dp4())
ctx->Const.ShaderCompilerOptions[MESA_SHADER_VERTEX].OptimizeForAOS = GL_TRUE;
st = st_create_context_priv(ctx, pipe, options, no_error);
if (!st) {
_mesa_destroy_context(ctx);
}
return st;
}
static struct st_context_iface *
st_api_create_context(struct st_api *stapi, struct st_manager *smapi,
const struct st_context_attribs *attribs,
enum st_context_error *error,
struct st_context_iface *shared_stctxi)
{
// 省略
st = st_create_context(api, pipe, mode_ptr, shared_ctx,
// 省略 &attribs->options, no_error);
}
static const struct st_api st_gl_api = {
.name = "Mesa " PACKAGE_VERSION,
.api = ST_API_OPENGL,
.profile_mask = ST_PROFILE_DEFAULT_MASK |
ST_PROFILE_OPENGL_CORE_MASK |
ST_PROFILE_OPENGL_ES1_MASK |
ST_PROFILE_OPENGL_ES2_MASK |
0,
.feature_mask = ST_API_FEATURE_MS_VISUALS_MASK,
.destroy = st_api_destroy,
.query_versions = st_api_query_versions,
.create_context = st_api_create_context,
.make_current = st_api_make_current,
.get_current = st_api_get_current,
.destroy_drawable = st_api_destroy_drawable,
};
struct st_api *
st_gl_api_create(void)
{
return (struct st_api *) &st_gl_api;
}
const __DRIconfig **
dri_init_screen_helper(struct dri_screen *screen,
struct pipe_screen *pscreen)
{
screen->base.screen = pscreen;
screen->base.get_egl_image = dri_get_egl_image;
screen->base.get_param = dri_get_param;
screen->base.set_background_context = dri_set_background_context;
screen->st_api = st_gl_api_create();
if (!screen->st_api)
return NULL;
if(pscreen->get_param(pscreen, PIPE_CAP_NPOT_TEXTURES))
screen->target = PIPE_TEXTURE_2D;
else
screen->target = PIPE_TEXTURE_RECT;
dri_postprocessing_init(screen);
screen->st_api->query_versions(screen->st_api, &screen->base,
&screen->options,
&screen->sPriv->max_gl_core_version,
&screen->sPriv->max_gl_compat_version,
&screen->sPriv->max_gl_es1_version,
&screen->sPriv->max_gl_es2_version);
return dri_fill_in_modes(screen);
}
/**
* DRI driver virtual function table.
*
* DRI versions differ in their implementation of init_screen and swap_buffers.
*/
const struct __DriverAPIRec galliumdrm_driver_api = {
.InitScreen = dri2_init_screen,
.DestroyScreen = dri_destroy_screen,
.CreateContext = dri_create_context,
.DestroyContext = dri_destroy_context,
.CreateBuffer = dri2_create_buffer,
.DestroyBuffer = dri_destroy_buffer,
.MakeCurrent = dri_make_current,
.UnbindContext = dri_unbind_context,
.AllocateBuffer = dri2_allocate_buffer,
.ReleaseBuffer = dri2_release_buffer,
};
/**
* This is the driver specific part of the createNewScreen entry point.
*
* Returns the struct gl_config supported by this driver.
*/
static const __DRIconfig **
dri2_init_screen(__DRIscreen * sPriv)
{
const __DRIconfig **configs;
struct dri_screen *screen;
struct pipe_screen *pscreen = NULL;
screen = CALLOC_STRUCT(dri_screen);
if (!screen)
return NULL;
screen->sPriv = sPriv;
screen->fd = sPriv->fd;
(void) mtx_init(&screen->opencl_func_mutex, mtx_plain);
sPriv->driverPrivate = (void *)screen;
if (pipe_loader_drm_probe_fd(&screen->dev, screen->fd)) {
dri_init_options(screen);
pscreen = pipe_loader_create_screen(screen->dev);
}
if (!pscreen)
goto release_pipe;
screen->throttle = pscreen->get_param(pscreen, PIPE_CAP_THROTTLE);
if (pscreen->resource_create_with_modifiers)
dri2ImageExtension.createImageWithModifiers =
dri2_create_image_with_modifiers;
if (pscreen->get_param(pscreen, PIPE_CAP_DMABUF)) {
uint64_t cap;
if (drmGetCap(sPriv->fd, DRM_CAP_PRIME, &cap) == 0 &&
(cap & DRM_PRIME_CAP_IMPORT)) {
dri2ImageExtension.createImageFromFds = dri2_from_fds;
dri2ImageExtension.createImageFromDmaBufs = dri2_from_dma_bufs;
dri2ImageExtension.createImageFromDmaBufs2 = dri2_from_dma_bufs2;
if (pscreen->query_dmabuf_modifiers) {
dri2ImageExtension.queryDmaBufFormats = dri2_query_dma_buf_formats;
dri2ImageExtension.queryDmaBufModifiers =
dri2_query_dma_buf_modifiers;
dri2ImageExtension.queryDmaBufFormatModifierAttribs =
dri2_query_dma_buf_format_modifier_attribs;
}
}
}
if (pscreen->set_damage_region)
dri2BufferDamageExtension.set_damage_region = dri2_set_damage_region;
if (pscreen->get_param(pscreen, PIPE_CAP_DEVICE_RESET_STATUS_QUERY)) {
sPriv->extensions = dri_robust_screen_extensions;
screen->has_reset_status_query = true;
}
else
sPriv->extensions = dri_screen_extensions;
configs = dri_init_screen_helper(screen, pscreen); // 死亡凝视
if (!configs)
goto destroy_screen;
screen->can_share_buffer = true;
screen->auto_fake_front = dri_with_format(sPriv);
screen->broken_invalidate = !sPriv->dri2.useInvalidate;
screen->lookup_egl_image = dri2_lookup_egl_image;
return configs;
destroy_screen:
dri_destroy_screen_helper(screen);
release_pipe:
if (screen->dev)
pipe_loader_release(&screen->dev, 1);
FREE(screen);
return NULL;
}
#define DEFINE_LOADER_DRM_ENTRYPOINT(drivername) \
const __DRIextension **__driDriverGetExtensions_##drivername(void); \
PUBLIC const __DRIextension **__driDriverGetExtensions_##drivername(void) \
{ \
globalDriverAPI = &galliumdrm_driver_api; \
return galliumdrm_driver_extensions; \
}
#if defined(GALLIUM_VC4)
DEFINE_LOADER_DRM_ENTRYPOINT(vc4)
#endif
char *
loader_get_extensions_name(const char *driver_name)
{
char *name = NULL;
if (asprintf(&name, "%s_%s", __DRI_DRIVER_GET_EXTENSIONS, driver_name) < 0)
return NULL;
const size_t len = strlen(name);
for (size_t i = 0; i < len; i++) {
if (name[i] == '-')
name[i] = '_';
}
return name;
}
/**
* Opens a DRI driver using its driver name, returning the __DRIextension
* entrypoints.
*
* \param driverName - a name like "i965", "radeon", "nouveau", etc.
* \param out_driver - Address where the dlopen() return value will be stored.
* \param search_path_vars - NULL-terminated list of env vars that can be used
* to override the DEFAULT_DRIVER_DIR search path.
*/
const struct __DRIextensionRec **
loader_open_driver(const char *driver_name,
void **out_driver_handle,
const char **search_path_vars)
{
char path[PATH_MAX], *search_paths, *next, *end;
char *get_extensions_name;
const struct __DRIextensionRec **extensions = NULL;
const struct __DRIextensionRec **(*get_extensions)(void);
search_paths = NULL;
if (geteuid() == getuid() && search_path_vars) {
for (int i = 0; search_path_vars[i] != NULL; i++) {
search_paths = getenv(search_path_vars[i]);
if (search_paths)
break;
}
}
if (search_paths == NULL)
search_paths = DEFAULT_DRIVER_DIR;
void *driver = NULL;
end = search_paths + strlen(search_paths);
for (char *p = search_paths; p < end; p = next + 1) {
int len;
next = strchr(p, ':');
if (next == NULL)
next = end;
len = next - p;
#if USE_ELF_TLS
snprintf(path, sizeof(path), "%.*s/tls/%s_dri.so", len, p, driver_name);
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
#endif
if (driver == NULL) {
snprintf(path, sizeof(path), "%.*s/%s_dri.so", len, p, driver_name);
driver = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
if (driver == NULL)
log_(_LOADER_DEBUG, "MESA-LOADER: failed to open %s: %s\n",
path, dlerror());
}
/* not need continue to loop all paths once the driver is found */
if (driver != NULL)
break;
}
if (driver == NULL) {
log_(_LOADER_WARNING, "MESA-LOADER: failed to open %s (search paths %s)\n",
driver_name, search_paths);
*out_driver_handle = NULL;
return NULL;
}
log_(_LOADER_DEBUG, "MESA-LOADER: dlopen(%s)\n", path);
get_extensions_name = loader_get_extensions_name(driver_name);
if (get_extensions_name) {
get_extensions = dlsym(driver, get_extensions_name);
if (get_extensions) {
extensions = get_extensions();
} else {
log_(_LOADER_DEBUG, "MESA-LOADER: driver does not expose %s(): %s\n",
get_extensions_name, dlerror());
}
free(get_extensions_name);
}
if (!extensions)
extensions = dlsym(driver, __DRI_DRIVER_EXTENSIONS);
if (extensions == NULL) {
log_(_LOADER_WARNING,
"MESA-LOADER: driver exports no extensions (%s)\n", dlerror());
dlclose(driver);
}
*out_driver_handle = driver;
return extensions;
}
/**
* Try to \c dlopen the named driver.
*
* This function adds the "_dri.so" suffix to the driver name and searches the
* directories specified by the \c LIBGL_DRIVERS_PATH environment variable in
* order to find the driver.
*
* \param driverName - a name like "i965", "radeon", "nouveau", etc.
* \param out_driver_handle - Address to return the resulting dlopen() handle.
*
* \returns
* The __DRIextension entrypoint table for the driver, or \c NULL if driver
* file not found.
*/
_X_HIDDEN const __DRIextension **
driOpenDriver(const char *driverName, void **out_driver_handle)
{
void *glhandle;
/* Attempt to make sure libGL symbols will be visible to the driver */
glhandle = dlopen(GL_LIB_NAME, RTLD_NOW | RTLD_GLOBAL);
static const char *search_path_vars[] = {
"LIBGL_DRIVERS_PATH",
"LIBGL_DRIVERS_DIR", /* deprecated */
NULL
};
const __DRIextension **extensions =
loader_open_driver(driverName, out_driver_handle, search_path_vars);
if (glhandle)
dlclose(glhandle);
return extensions;
}
static char *
get_driver_config(const char *driverName)
{
void *handle;
char *config = NULL;
const __DRIextension **extensions = driOpenDriver(driverName, &handle);
if (extensions) {
for (int i = 0; extensions[i]; i++) {
if (strcmp(extensions[i]->name, __DRI_CONFIG_OPTIONS) != 0)
continue;
__DRIconfigOptionsExtension *ext =
(__DRIconfigOptionsExtension *)extensions[i];
if (ext->base.version >= 2)
config = ext->getXml(driverName);
else
config = strdup(ext->xml);
break;
}
}
if (!config) {
/* Fall back to the old method */
config = dlsym(handle, "__driConfigOptions");
if (config)
config = strdup(config);
}
dlclose(handle);
return config;
}
/*
* Exported function for obtai
* ning a driver's option list (UTF-8 encoded XML).
*
* The returned char pointer points directly into the driver. Therefore
* it should be treated as a constant.
*
* If the driver was not found or does not support configuration NULL is
* returned.
*/
_GLX_PUBLIC const char *
glXGetDriverConfig(const char *driverName)
{
struct driver_config_entry *e;
pthread_mutex_lock(&driver_config_mutex);
for (e = driver_config_cache; e; e = e->next) {
if (strcmp(e->driverName, driverName) == 0)
goto out;
}
e = malloc(sizeof(*e));
if (!e)
goto out;
e->config = get_driver_config(driverName);
e->driverName = strdup(driverName);
if (!e->config || !e->driverName) {
free(e->config);
free(e->driverName);
free(e);
e = NULL;
goto out;
}
e->next = driver_config_cache;
driver_config_cache = e;
if (!e->next)
atexit(clear_driver_config_cache);
out:
pthread_mutex_unlock(&driver_config_mutex);
return e ? e->config : NULL;
}
static struct glx_screen *
driCreateScreen(int screen, struct glx_display *priv)
{
struct dri_display *pdp;
__GLXDRIscreen *psp;
const __DRIextension **extensions;
struct dri_screen *psc;
char *driverName;
int i;
psc = calloc(1, sizeof *psc);
if (psc == NULL)
return NULL;
if (!glx_screen_init(&psc->base, screen, priv)) {
free(psc);
return NULL;
}
if (!driGetDriverName(priv->dpy, screen, &driverName)) {
goto cleanup;
}
extensions = driOpenDriver(driverName, &psc->driver); // 死亡凝视
if (extensions == NULL) {
ErrorMessageF("driver exports no extensions (%s)\n", dlerror());
goto cleanup;
}
for (i = 0; extensions[i]; i++) {
if (strcmp(extensions[i]->name, __DRI_CORE) == 0)
psc->core = (__DRIcoreExtension *) extensions[i];
if (strcmp(extensions[i]->name, __DRI_LEGACY) == 0)
psc->legacy = (__DRIlegacyExtension *) extensions[i];
}
if (psc->core == NULL || psc->legacy == NULL)
goto cleanup;
pdp = (struct dri_display *) priv->driDisplay;
psc->driScreen =
CallCreateNewScreen(psc->base.dpy, screen, psc, pdp);
if (psc->driScreen == NULL)
goto cleanup;
extensions = psc->core->getExtensions(psc->driScreen);
driBindExtensions(psc, extensions);
psc->base.vtable = &dri_screen_vtable;
psp = &psc->vtable;
psc->base.driScreen = psp;
if (psc->driCopySubBuffer)
psp->copySubBuffer = driCopySubBuffer;
psp->destroyScreen = driDestroyScreen;
psp->createDrawable = driCreateDrawable;
psp->swapBuffers = driSwapBuffers;
psp->setSwapInterval = driSetSwapInterval;
psp->getSwapInterval = driGetSwapInterval;
free(driverName);
return &psc->base;
cleanup:
CriticalErrorMessageF("failed to load driver: %s\n", driverName);
free(driverName);
if (psc->driver)
dlclose(psc->driver);
glx_screen_cleanup(&psc->base);
free(psc);
return NULL;
}
/*
* Allocate, initialize and return a __DRIdisplayPrivate object.
* This is called from __glXInitialize() when we are given a new
* display pointer.
*/
_X_HIDDEN __GLXDRIdisplay *
driCreateDisplay(Display * dpy)
{
struct dri_display *pdpyp;
int eventBase, errorBase;
int major, minor, patch;
if (!XF86DRIQueryExtension(dpy, &eventBase, &errorBase)) {
return NULL;
}
if (!XF86DRIQueryVersion(dpy, &major, &minor, &patch)) {
return NULL;
}
pdpyp = malloc(sizeof *pdpyp);
if (!pdpyp) {
return NULL;
}
pdpyp->driMajor = major;
pdpyp->driMinor = minor;
pdpyp->driPatch = patch;
pdpyp->base.destroyDisplay = driDestroyDisplay;
pdpyp->base.createScreen = driCreateScreen; // 死亡凝视
return &pdpyp->base;
}
/*
** Initialize the client side extension code.
*/
_X_HIDDEN struct glx_display *
__glXInitialize(Display * dpy)
{
struct glx_display *dpyPriv, *d;
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
Bool glx_direct, glx_accel;
#endif
int i;
_XLockMutex(_Xglobal_lock);
for (dpyPriv = glx_displays; dpyPriv; dpyPriv = dpyPriv->next) {
if (dpyPriv->dpy == dpy) {
_XUnlockMutex(_Xglobal_lock);
return dpyPriv;
}
}
/* Drop the lock while we create the display private. */
_XUnlockMutex(_Xglobal_lock);
dpyPriv = calloc(1, sizeof *dpyPriv);
if (!dpyPriv)
return NULL;
dpyPriv->codes = XInitExtension(dpy, __glXExtensionName);
if (!dpyPriv->codes) {
free(dpyPriv);
return NULL;
}
dpyPriv->dpy = dpy;
dpyPriv->majorOpcode = dpyPriv->codes->major_opcode;
dpyPriv->serverGLXvendor = 0x0;
dpyPriv->serverGLXversion = 0x0;
/* See if the versions are compatible. This GLX implementation does not
* work with servers that only support GLX 1.0.
*/
if (!QueryVersion(dpy, dpyPriv->majorOpcode,
&dpyPriv->majorVersion, &dpyPriv->minorVersion)
|| (dpyPriv->majorVersion == 1 && dpyPriv->minorVersion < 1)) {
free(dpyPriv);
return NULL;
}
for (i = 0; i < __GLX_NUMBER_EVENTS; i++) {
XESetWireToEvent(dpy, dpyPriv->codes->first_event + i, __glXWireToEvent);
XESetEventToWire(dpy, dpyPriv->codes->first_event + i, __glXEventToWire);
}
XESetCloseDisplay(dpy, dpyPriv->codes->extension, __glXCloseDisplay);
XESetErrorString (dpy, dpyPriv->codes->extension, __glXErrorString);
dpyPriv->glXDrawHash = __glxHashCreate();
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
glx_direct = !env_var_as_boolean("LIBGL_ALWAYS_INDIRECT", false);
glx_accel = !env_var_as_boolean("LIBGL_ALWAYS_SOFTWARE", false);
dpyPriv->drawHash = __glxHashCreate();
#ifndef GLX_USE_APPLEGL
/* Set the logger before the *CreateDisplay functions. */
loader_set_logger(dri_message);
#endif
/*
** Initialize the direct rendering per display data and functions.
** Note: This _must_ be done before calling any other DRI routines
** (e.g., those called in AllocAndFetchScreenConfigs).
*/
#if defined(GLX_USE_DRM)
if (glx_direct && glx_accel) {
#if defined(HAVE_DRI3)
if (!env_var_as_boolean("LIBGL_DRI3_DISABLE", false))
dpyPriv->dri3Display = dri3_create_display(dpy);
#endif /* HAVE_DRI3 */
dpyPriv->dri2Display = dri2CreateDisplay(dpy);
dpyPriv->driDisplay = driCreateDisplay(dpy); // 死亡凝视
}
#endif /* GLX_USE_DRM */
if (glx_direct)
dpyPriv->driswDisplay = driswCreateDisplay(dpy);
#endif /* GLX_DIRECT_RENDERING && !GLX_USE_APPLEGL */
#ifdef GLX_USE_APPLEGL
if (!applegl_create_display(dpyPriv)) {
free(dpyPriv);
return NULL;
}
#endif
#ifdef GLX_USE_WINDOWSGL
if (glx_direct && glx_accel)
dpyPriv->windowsdriDisplay = driwindowsCreateDisplay(dpy);
#endif
if (!AllocAndFetchScreenConfigs(dpy, dpyPriv)) {
free(dpyPriv);
return NULL;
}
__glX_send_client_info(dpyPriv);
/* Grab the lock again and add the dispay private, unless somebody
* beat us to initializing on this display in the meantime. */
_XLockMutex(_Xglobal_lock);
for (d = glx_displays; d; d = d->next) {
if (d->dpy == dpy) {
_XUnlockMutex(_Xglobal_lock);
glx_display_free(dpyPriv);
return d;
}
}
dpyPriv->next = glx_displays;
glx_displays = dpyPriv;
_XUnlockMutex(_Xglobal_lock);
return dpyPriv;
}
static __GLXDRIdrawable *
dri2CreateDrawable(struct glx_screen *base, XID xDrawable,
GLXDrawable drawable, struct glx_config *config_base)
{
struct dri2_drawable *pdraw;
struct dri2_screen *psc = (struct dri2_screen *) base;
__GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
struct glx_display *dpyPriv;
struct dri2_display *pdp;
GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
dpyPriv = __glXInitialize(psc->base.dpy); // 死亡凝视
if (dpyPriv == NULL)
return NULL;
pdraw = calloc(1, sizeof(*pdraw));
if (!pdraw)
return NULL;
pdraw->base.destroyDrawable = dri2DestroyDrawable;
pdraw->base.xDrawable = xDrawable;
pdraw->base.drawable = drawable;
pdraw->base.psc = &psc->base;
pdraw->bufferCount = 0;
pdraw->swap_interval = 1; /* default may be overridden below */
pdraw->have_back = 0;
if (psc->config)
psc->config->configQueryi(psc->driScreen,
"vblank_mode", &vblank_mode);
switch (vblank_mode) {
case DRI_CONF_VBLANK_NEVER:
case DRI_CONF_VBLANK_DEF_INTERVAL_0:
pdraw->swap_interval = 0;
break;
case DRI_CONF_VBLANK_DEF_INTERVAL_1:
case DRI_CONF_VBLANK_ALWAYS_SYNC:
default:
pdraw->swap_interval = 1;
break;
}
DRI2CreateDrawable(psc->base.dpy, xDrawable);
pdp = (struct dri2_display *)dpyPriv->dri2Display;
/* Create a new drawable */
pdraw->driDrawable =
(*psc->dri2->createNewDrawable) (psc->driScreen,
config->driConfig, pdraw);
if (!pdraw->driDrawable) {
DRI2DestroyDrawable(psc->base.dpy, xDrawable);
free(pdraw);
return NULL;
}
if (__glxHashInsert(pdp->dri2Hash, xDrawable, pdraw)) {
(*psc->core->destroyDrawable) (pdraw->driDrawable);
DRI2DestroyDrawable(psc->base.dpy, xDrawable);
free(pdraw);
return None;
}
/*
* Make sure server has the same swap interval we do for the new
* drawable.
*/
if (psc->vtable.setSwapInterval)
psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval);
return &pdraw->base;
}
Driver loading and binding
When libGL.so initializes itself (via the __glXInitialize function) a
call is made to driCreateDisplay(). This function uses DRI facilities
to determine the driver file appropriate for each screen on the local
display. Each screen's driver is then opened with dlopen() and asked
for its __driCreateScreen() function. The pointers to the __driCreateScreen()
functions are kept in an array, indexed by screen number, in the
__DRIdisplayRec struct.
When a driver's __driCreateScreen() function is called, it must initialize
a __DRIscreenRec struct. This struct acts as the root of a tree of
function pointers which are called to create and destroy contexts and
drawables and perform all the operations needed by the GLX interface.
See the xc/lib/GL/glx/glxclient.h file for details.