Render Buffer Interface Code This is a quick and dirty explanation of how interfacing with the GLX/X protocol buffers works in the glx client code. This is intended for people who need to write or modify code that sends and receives requests to a GLX server, via these buffers. Examples would be debugging current protocol errors, and adding new protocol requests, such as extensions. Most basic protocol requests are auto-generated from the libGL.tcl specification. However, any request that needs a reply or sends varying amount of data (esp. pixel data) is hand coded. Although it wouldn't be entirely difficult to auto-generate all of this code, it would require some changes to how the spec file is parsed and code is auto-generated. The specification for how GLX protocol packets are defined is contained in the GLX protocol spec, available from ftp://sgigate.sgi.com, in the /pub/opengl/doc/ directory, in the file specs.tar.Z. This includes the OpenGL and GLU specs as well. More information about the X Protocol spec (upon which the GLX protocol is based) is available from the X tree itself, in xc/doc/hardcopy/XProtocol/. Put simply, creating a GLX packet is two main steps: requesting a buffer, and filling that buffer. The new interface code simplifies all of the details down to this. No longer do you need to worry about regular or large requests when adding a new function call. Requesting a Buffer A buffer is requested with the following macro: __GLX_GET_RENDER_BUFFER(buffer, opcode, request_size, extra_data_size) buffer should be a simple char * pointer, that will be set to point into the allocated packet, to the next available byte. opcode should be the opcode of the request. It will be placed into the packet for you. request_size is the size of the request packet itself. This is how much space will be allocated by the macro. extra_data_size is for potentially large requests. While the non-varying request size should be given above, this is the variable data size (for example, a texture size). For a regular request, this should be 0. Filling a Buffer A buffer is filled with macros of the form: __GLX_PUT_(buffer, data) examples are: __GLX_PUT_int(), __GLX_PUT_enum(), __GLX_PUT_float(), etc. buffer is the current buffer pointer, passed to and set by __GLX_GET_RENDER_BUFFER above. data is the data to be inserted into the buffer. The data will be placed into the buffer, and the buffer pointer will be appropriately incremented. Example of Use. Here is an example of how the above are used in a simple request: __glx_Vertex3f( GLfloat x, GLfloat y, GLfloat z) { char* buffer=NULL; __GLX_GET_RENDER_BUFFER(buffer, 70, 16, 0); __GLX_PUT_float(buffer, x); __GLX_PUT_float(buffer, y); __GLX_PUT_float(buffer, z); } Large Requests. There a few more details for sending data specific to potentially large requests. Specifically, there are two more macros to make life easier: __GLX_PUT_PIXEL_DATA_ARGUMENTS Pixel data requests (such as DrawPixels, TexImage, etc.) pass more information than is included in the function call. Specifically, they pass information about how the pixel data is stored. This is because a client may do some unpacking of pixel data, or may rely on the server to do the unpacking. This information is intended to communicate to the server what the client expects the server to do. In this specific GLX implementation, much unpacking is done on the client side. This means that some of this information to the server is always the same, since the client has already done the work for the server. Using this macro hides these details when implementing a new function call as well as simplifies making the packet. __GLX_PUT_buffer(buffer, data, width, height, format, type, size) This is used to send the extra data, similarly to the above macros to send simple data types. This macro completely hides any unpacking that is performed on the data, as well as sending the data on multiple packets in the case of a large render request. The arguements are fairly straightforward, buffer is, as above, a pointer into the current packet. data is a pointer to the data to be sent. width, height, format, type are the basic arguements to the function. size is the size of the data, as calculated by GLX_image_size. Note that the image size could be calculated inside of the macro, based on the data passed to the macro (format, type, etc). This was not done, as your function needs to pass this size to the initial GET_RENDER_BUFFER macro anyways, so this allows you to reuse that calculation. Example of Use: Here's a sample from pixel.c: void __glx_DrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid*pixels) { char* buffer = NULL; int s=GLX_image_size(width, height, format, type); __GLX_GET_RENDER_BUFFER(buffer, 173, 40, s); __GLX_PUT_PIXEL_DATA_ARGUMENTS; __GLX_PUT_sizei(buffer, width); __GLX_PUT_sizei(buffer, height); __GLX_PUT_enum(buffer, format); __GLX_PUT_enum(buffer, type); __GLX_PUT_buffer(buffer, pixels, width, height, format, type, s); } NOTE about 'unused' bytes in packet: These *must* be filled (most likely with 0, but it doesn't matter) like so: __GLX_PUT_byte(buffer, 0) since buffer needs to be incremented past the empty location. If this is not done, the packet will be created wrong, and the library will not work properly. Terence Ripperda ripperda@sgi.com