GLX直接渲染与间接渲染

GLX支持直接渲染与间接渲染两种模式,直接渲染需OpenGL应用程序直接访问GPU,间接渲染则将指令转发至X Window Server执行。文章记录了Mesa的GLX实现这两种渲染模式的相关源码,如single2.c、indirect_init.c等,详细解析待后续进行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

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];
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值