Graphics Batching

If you extend X to add more poly graphics primitives, you may be able to take advantage of facilities in the library to allow back-to-back single calls to be transformed into poly requests. This may dramatically improve performance of programs that are not written using poly requests. A pointer to an xReq(), called last_req in the display structure, is the last request being processed. By checking that the last request type, drawable, gc, and other options are the same as the new one and that there is enough space left in the buffer, you may be able to just extend the previous graphics request by extending the length field of the request and appending the data to the buffer. This can improve performance by five times or more in naive programs. For example, here is the source for the XDrawPoint() stub. (Writing extension stubs is discussed in the "Writing Extension Stubs".)
#include <X11/Xlibint.h>

/* precompute the maximum size of batching request allowed */

static int size = sizeof(xPolyPointReq) + EPERBATCH * sizeof(xPoint);

XDrawPoint(dpy, d, gc, x, y)
    register Display *dpy;
    Drawable d;
    GC gc;
    int x, y; /* INT16 */
{
    xPoint *point;
    LockDisplay(dpy);
    FlushGC(dpy, gc);
    {
    register xPolyPointReq *req = (xPolyPointReq *) dpy->last_req;
    /* if same as previous request, with same drawable, batch requests */
    if (
          (req->reqType == X_PolyPoint)
       && (req->drawable == d)
       && (req->gc == gc->gid)
       && (req->coordMode == CoordModeOrigin)
       && ((dpy->bufptr + sizeof (xPoint)) <= dpy->bufmax)
       && (((char *)dpy->bufptr - (char *)req) < size) ) {
         point = (xPoint *) dpy->bufptr;
         req->length += sizeof (xPoint) >> 2;
         dpy->bufptr += sizeof (xPoint);
         }

    else {
        GetReqExtra(PolyPoint, 4, req); /* 1 point = 4 bytes */
        req->drawable = d;
        req->gc = gc->gid;
        req->coordMode = CoordModeOrigin;
        point = (xPoint *) (req + 1);
        }
    point->x = x;
    point->y = y;
    }
    UnlockDisplay(dpy);
    SyncHandle();
}

To keep clients from generating very long requests that may monopolize the server, there is a symbol defined in X11/Xlibint.h of EPERBATCH on the number of requests batched. Most of the performance benefit occurs in the first few merged requests. Note that FlushGC() is called before picking up the value of last_req, because it may modify this field.

Next: Writing Extension Stubs

Christophe Tronche