文章目录
前言
GLX支持直接渲染(Direct Rendering)与间接渲染(Indirect Rendering)两种模式,直接渲染模式需要OpenGL应用程序能够直接访问GPU(可以简单地认为OpenGL应用程序与GPU需要在同一台电脑上),而间接渲染则需要将OpenGL指令转发至X Window Server,由X Window Server负责执行真正的OpenGL指令。
所以直接渲染和间接渲染的OpenGL函数应该是两套执行流程,那么Mesa的GLX是如何实现的呢?本篇文章先做一些简单的代码记录,详细解析容后道来。
源码解析
src/glx/single2.c
void
__indirect_glGetFloatv(GLenum val, GLfloat * f)
{
const GLenum origVal = val;
__GLX_SINGLE_DECLARE_VARIABLES();
xGLXSingleReply reply;
val = RemapTransposeEnum(val);
__GLX_SINGLE_LOAD_VARIABLES();
__GLX_SINGLE_BEGIN(X_GLsop_GetFloatv, 4); // 发送请求
__GLX_SINGLE_PUT_LONG(0, val);
__GLX_SINGLE_READ_XREPLY(); // 读取响应,相当于实现了一个RPC
__GLX_SINGLE_GET_SIZE(compsize);
if (compsize == 0) {
/*
** Error occurred; don't modify user's buffer.
*/
}
else {
GLintptr data;
/*
** We still needed to send the request to the server in order to
** find out whether it was legal to make a query (it's illegal,
** for example, to call a query between glBegin() and glEnd()).
*/
if (get_client_data(gc, val, &data)) {
*f = (GLfloat) data;
}
else {
/*
** Not a local value, so use what we got from the server.
*/
if (compsize == 1) {
__GLX_SINGLE_GET_FLOAT(f);
}
else {
__GLX_SINGLE_GET_FLOAT_ARRAY(f, compsize);
if (val != origVal) {
/* matrix transpose */
TransposeMatrixf(f);
}
}
}
}
__GLX_SINGLE_END();
}
build/src/mapi/glapi/gen/indirect_init.c
/**
* Create and initialize a new GL dispatch table. The table is initialized
* with GLX indirect rendering protocol functions.
*/
struct _glapi_table * __glXNewIndirectAPI( void )
{
_glapi_proc *table;
unsigned entries;
unsigned i;
int o;
entries = _glapi_get_dispatch_table_size();
table = malloc(entries * sizeof(_glapi_proc));
if (table == NULL)
return NULL;
/* first, set all entries to point to no-op functions */
for (i = 0; i < entries; i++) {
table[i] = (_glapi_proc) NoOp;
}
/* now, initialize the entries we understand */
/* 1.0 */
table[213] = (_glapi_proc) __indirect_glAccum;
// 省略
table[262] = (_glapi_proc) __indirect_glGetFloatv;
// 省略
return (struct _glapi_table *) table;
}
src/glx/indirect_glx.c
static int
indirect_bind_context(struct glx_context *gc, struct glx_context *old,
GLXDrawable draw, GLXDrawable read)
{
GLXContextTag tag;
Display *dpy = gc->psc->dpy;
Bool sent;
if (old != &dummyContext && !old->isDirect && old->psc->dpy == dpy) {
tag = old->currentContextTag;
old->currentContextTag = 0;
} else {
tag = 0;
}
sent = SendMakeCurrentRequest(dpy, gc->xid, tag, draw, read,
&gc->currentContextTag);
if (sent) {
if (!IndirectAPI)
IndirectAPI = __glXNewIndirectAPI(); // 死亡凝视
_glapi_set_dispatch(IndirectAPI); // 在src/mapi/mapi_glapi.c中定义
/* The indirect vertex array state must to be initialised after we
* have setup the context, as it needs to query server attributes.
*
* At the point this is called gc->currentDpy is not initialized
* nor is the thread's current context actually set. Hence the
* cleverness before the GetString calls.
*/
__GLXattribute *state = gc->client_state_private;
if (state && state->array_state == NULL) {
gc->currentDpy = gc->psc->dpy;
__glXSetCurrentContext(gc);
__indirect_glGetString(GL_EXTENSIONS);
__indirect_glGetString(GL_VERSION);
__glXInitVertexArrayState(gc);
}
}
return !sent;
}
static const struct glx_context_vtable indirect_context_vtable = {
.destroy = indirect_destroy_context,
.bind = indirect_bind_context, // 死亡凝视
.unbind = indirect_unbind_context,
.wait_gl = indirect_wait_gl,
.wait_x = indirect_wait_x,
.use_x_font = indirect_use_x_font,
.bind_tex_image = indirect_bind_tex_image,
.release_tex_image = indirect_release_tex_image,
.get_proc_address = NULL,
};
/**
* \todo Eliminate \c __glXInitVertexArrayState. Replace it with a new
* function called \c __glXAllocateClientState that allocates the memory and
* does all the initialization (including the pixel pack / unpack).
*
* \note
* This function is \b not the place to validate the context creation
* parameters. It is just the allocator for the \c glx_context.
*/
_X_HIDDEN struct glx_context *
indirect_create_context(struct glx_screen *psc,
struct glx_config *mode,
struct glx_context *shareList, int renderType)
{
struct glx_context *gc;
int bufSize;
CARD8 opcode;
__GLXattribute *state;
opcode = __glXSetupForCommand(psc->dpy);
if (!opcode) {
return NULL;
}
/* Allocate our context record */
gc = calloc(1, sizeof *gc);
if (!gc) {
/* Out of memory */
return NULL;
}
glx_context_init(gc, psc, mode);
gc->isDirect = GL_FALSE;
gc->vtable = &indirect_context_vtable; // 死亡凝视
state = calloc(1, sizeof(struct __GLXattributeRec));
gc->renderType = renderType;
// 省略
}
src/glx/glxclient.h
这又是一个重要的数据结构
/**
* GLX state that needs to be kept on the client. One of these records
* exist for each context that has been made current by this client.
*/
struct glx_context
{
/**
* \name Drawing command buffer.
*
* Drawing commands are packed into this buffer before being sent as a
* single GLX protocol request. The buffer is sent when it overflows or
* is flushed by \c __glXFlushRenderBuffer. \c pc is the next location
* in the buffer to be filled. \c limit is described above in the buffer
* slop discussion.
*
* Commands that require large amounts of data to be transfered will
* also use this buffer to hold a header that describes the large
* command.
*
* These must be the first 6 fields since they are static initialized
* in the dummy context in glxext.c
*/
/*@{ */
GLubyte *buf;
GLubyte *pc;
GLubyte *limit;
GLubyte *bufEnd;
GLint bufSize;
/*@} */
const struct glx_context_vtable *vtable;
/**
* The XID of this rendering context. When the context is created a
* new XID is allocated. This is set to None when the context is
* destroyed but is still current to some thread. In this case the
* context will be freed on next MakeCurrent.
*/
XID xid;
/**
* The XID of the \c shareList context.
*/
XID share_xid;
/**
* Screen number.
*/
GLint screen;
struct glx_screen *psc;
/**
* \c GL_TRUE if the context was created with ImportContext, which
* means the server-side context was created by another X client.
*/
GLboolean imported;
/**
* The context tag returned by MakeCurrent when this context is made
* current. This tag is used to identify the context that a thread has
* current so that proper server context management can be done. It is
* used for all context specific commands (i.e., \c Render, \c RenderLarge,
* \c WaitX, \c WaitGL, \c UseXFont, and \c MakeCurrent (for the old
* context)).
*/
GLXContextTag currentContextTag;
/**
* \name Rendering mode
*
* The rendering mode is kept on the client as well as the server.
* When \c glRenderMode is called, the buffer associated with the
* previous rendering mode (feedback or select) is filled.
*/
/*@{ */
GLenum renderMode;
GLfloat *feedbackBuf;
GLuint *selectBuf;
/*@} */
/**
* Client side attribs.
*/
__GLXattributeMachine attributes;
/**
* Client side error code. This is set when client side gl API
* routines need to set an error because of a bad enumerant or
* running out of memory, etc.
*/
GLenum error;
/**
* Whether this context does direct rendering.
*/
Bool isDirect;
#if defined(GLX_DIRECT_RENDERING) && defined(GLX_USE_APPLEGL)
void *driContext;
#endif
/**
* \c dpy of current display for this context. Will be \c NULL if not
* current to any display, or if this is the "dummy context".
*/
Display *currentDpy;
/**
* The current drawable for this context. Will be None if this
* context is not current to any drawable. currentReadable is below.
*/
GLXDrawable currentDrawable;
/**
* \name GL Constant Strings
*
* Constant strings that describe the server implementation
* These pertain to GL attributes, not to be confused with
* GLX versioning attributes.
*/
/*@{ */
GLubyte *vendor;
GLubyte *renderer;
GLubyte *version;
GLubyte *extensions;
/*@} */
/**
* Maximum small render command size. This is the smaller of 64k and
* the size of the above buffer.
*/
GLint maxSmallRenderCommandSize;
/**
* Major opcode for the extension. Copied here so a lookup isn't
* needed.
*/
GLint majorOpcode;
/**
* Pointer to the config used to create this context.
*/
struct glx_config *config;
/**
* The current read-drawable for this context. Will be None if this
* context is not current to any drawable.
*
* \since Internal API version 20030606.
*/
GLXDrawable currentReadable;
/**
* Pointer to client-state data that is private to libGL. This is only
* used for indirect rendering contexts.
*
* No internal API version change was made for this change. Client-side
* drivers should NEVER use this data or even care that it exists.
*/
void *client_state_private;
/**
* Stored value for \c glXQueryContext attribute \c GLX_RENDER_TYPE.
*/
int renderType;
/**
* \name Raw server GL version
*
* True core GL version supported by the server. This is the raw value
* returned by the server, and it may not reflect what is actually
* supported (or reported) by the client-side library.
*/
/*@{ */
int server_major; /**< Major version number. */
int server_minor; /**< Minor version number. */
/*@} */
/**
* Number of threads we're currently current in.
*/
unsigned long thread_refcount;
/**
* GLX_ARB_create_context_no_error setting for this context.
* This needs to be kept here to enforce shared context rules.
*/
Bool noError;
char gl_extension_bits[__GL_EXT_BYTES];
};