indirect_vertex_array.c   [plain text]


/*
 * (C) Copyright IBM Corporation 2004, 2005
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * IBM,
 * AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <inttypes.h>
#include <assert.h>
#include <string.h>

#include "glxclient.h"
#include "indirect.h"
#include <GL/glxproto.h>
#include "glxextensions.h"
#include "indirect_vertex_array.h"
#include "indirect_vertex_array_priv.h"

#define __GLX_PAD(n) (((n)+3) & ~3)

/**
 * \file indirect_vertex_array.c
 * Implement GLX protocol for vertex arrays and vertex buffer objects.
 *
 * The most important function in this fill is \c fill_array_info_cache.
 * The \c array_state_vector contains a cache of the ARRAY_INFO data sent
 * in the DrawArrays protocol.  Certain operations, such as enabling or
 * disabling an array, can invalidate this cache.  \c fill_array_info_cache
 * fills-in this data.  Additionally, it examines the enabled state and
 * other factors to determine what "version" of DrawArrays protocoal can be
 * used.
 *
 * Current, only two versions of DrawArrays protocol are implemented.  The
 * first version is the "none" protocol.  This is the fallback when the
 * server does not support GL 1.1 / EXT_vertex_arrays.  It is implemented
 * by sending batches of immediate mode commands that are equivalent to the
 * DrawArrays protocol.
 *
 * The other protocol that is currently implemented is the "old" protocol.
 * This is the GL 1.1 DrawArrays protocol.  The only difference between GL
 * 1.1 and EXT_vertex_arrays is the opcode used for the DrawArrays command.
 * This protocol is called "old" because the ARB is in the process of
 * defining a new protocol, which will probably be called wither "new" or
 * "vbo", to support multiple texture coordinate arrays, generic attributes,
 * and vertex buffer objects.
 *
 * \author Ian Romanick <ian.d.romanick@intel.com>
 */

static void emit_DrawArrays_none(GLenum mode, GLint first, GLsizei count);
static void emit_DrawArrays_old(GLenum mode, GLint first, GLsizei count);

static void emit_DrawElements_none(GLenum mode, GLsizei count, GLenum type,
                                   const GLvoid * indices);
static void emit_DrawElements_old(GLenum mode, GLsizei count, GLenum type,
                                  const GLvoid * indices);


static GLubyte *emit_element_none(GLubyte * dst,
                                  const struct array_state_vector *arrays,
                                  unsigned index);
static GLubyte *emit_element_old(GLubyte * dst,
                                 const struct array_state_vector *arrays,
                                 unsigned index);
static struct array_state *get_array_entry(const struct array_state_vector
                                           *arrays, GLenum key,
                                           unsigned index);
static void fill_array_info_cache(struct array_state_vector *arrays);
static GLboolean validate_mode(__GLXcontext * gc, GLenum mode);
static GLboolean validate_count(__GLXcontext * gc, GLsizei count);
static GLboolean validate_type(__GLXcontext * gc, GLenum type);


/**
 * Table of sizes, in bytes, of a GL types.  All of the type enums are be in
 * the range 0x1400 - 0x140F.  That includes types added by extensions (i.e.,
 * \c GL_HALF_FLOAT_NV).  This elements of this table correspond to the
 * type enums masked with 0x0f.
 *
 * \notes
 * \c GL_HALF_FLOAT_NV is not included.  Neither are \c GL_2_BYTES,
 * \c GL_3_BYTES, or \c GL_4_BYTES.
 */
const GLuint __glXTypeSize_table[16] = {
   1, 1, 2, 2, 4, 4, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0
};


/**
 * Free the per-context array state that was allocated with
 * __glXInitVertexArrayState().
 */
void
__glXFreeVertexArrayState(__GLXcontext * gc)
{
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;

   if (arrays) {
      if (arrays->stack) {
         free(arrays->stack);
         arrays->stack = NULL;
      }
      if (arrays->arrays) {
         free(arrays->arrays);
         arrays->arrays = NULL;
      }
      free(arrays);
      state->array_state = NULL;
   }
}


/**
 * Initialize vertex array state of a GLX context.
 *
 * \param gc  GLX context whose vertex array state is to be initialized.
 *
 * \warning
 * This function may only be called after __GLXcontext::gl_extension_bits,
 * __GLXcontext::server_minor, and __GLXcontext::server_major have been
 * initialized.  These values are used to determine what vertex arrays are
 * supported.
 *
 * \bug
 * Return values from malloc are not properly tested.
 */
void
__glXInitVertexArrayState(__GLXcontext * gc)
{
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays;

   unsigned array_count;
   int texture_units = 1, vertex_program_attribs = 0;
   unsigned i, j;

   GLboolean got_fog = GL_FALSE;
   GLboolean got_secondary_color = GL_FALSE;


   arrays = calloc(1, sizeof(struct array_state_vector));
   state->array_state = arrays;

   arrays->old_DrawArrays_possible = !state->NoDrawArraysProtocol;
   arrays->new_DrawArrays_possible = GL_FALSE;
   arrays->DrawArrays = NULL;

   arrays->active_texture_unit = 0;


   /* Determine how many arrays are actually needed.  Only arrays that
    * are supported by the server are create.  For example, if the server
    * supports only 2 texture units, then only 2 texture coordinate arrays
    * are created.
    *
    * At the very least, GL_VERTEX_ARRAY, GL_NORMAL_ARRAY,
    * GL_COLOR_ARRAY, GL_INDEX_ARRAY, GL_TEXTURE_COORD_ARRAY, and
    * GL_EDGE_FLAG_ARRAY are supported.
    */

   array_count = 5;

   if (__glExtensionBitIsEnabled(gc, GL_EXT_fog_coord_bit)
       || (gc->server_major > 1) || (gc->server_minor >= 4)) {
      got_fog = GL_TRUE;
      array_count++;
   }

   if (__glExtensionBitIsEnabled(gc, GL_EXT_secondary_color_bit)
       || (gc->server_major > 1) || (gc->server_minor >= 4)) {
      got_secondary_color = GL_TRUE;
      array_count++;
   }

   if (__glExtensionBitIsEnabled(gc, GL_ARB_multitexture_bit)
       || (gc->server_major > 1) || (gc->server_minor >= 3)) {
      __indirect_glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texture_units);
   }

   if (__glExtensionBitIsEnabled(gc, GL_ARB_vertex_program_bit)) {
      __indirect_glGetProgramivARB(GL_VERTEX_PROGRAM_ARB,
                                   GL_MAX_PROGRAM_ATTRIBS_ARB,
                                   &vertex_program_attribs);
   }

   arrays->num_texture_units = texture_units;
   arrays->num_vertex_program_attribs = vertex_program_attribs;
   array_count += texture_units + vertex_program_attribs;
   arrays->num_arrays = array_count;
   arrays->arrays = calloc(array_count, sizeof(struct array_state));

   arrays->arrays[0].data_type = GL_FLOAT;
   arrays->arrays[0].count = 3;
   arrays->arrays[0].key = GL_NORMAL_ARRAY;
   arrays->arrays[0].normalized = GL_TRUE;
   arrays->arrays[0].old_DrawArrays_possible = GL_TRUE;

   arrays->arrays[1].data_type = GL_FLOAT;
   arrays->arrays[1].count = 4;
   arrays->arrays[1].key = GL_COLOR_ARRAY;
   arrays->arrays[1].normalized = GL_TRUE;
   arrays->arrays[1].old_DrawArrays_possible = GL_TRUE;

   arrays->arrays[2].data_type = GL_FLOAT;
   arrays->arrays[2].count = 1;
   arrays->arrays[2].key = GL_INDEX_ARRAY;
   arrays->arrays[2].old_DrawArrays_possible = GL_TRUE;

   arrays->arrays[3].data_type = GL_UNSIGNED_BYTE;
   arrays->arrays[3].count = 1;
   arrays->arrays[3].key = GL_EDGE_FLAG_ARRAY;
   arrays->arrays[3].old_DrawArrays_possible = GL_TRUE;

   for (i = 0; i < texture_units; i++) {
      arrays->arrays[4 + i].data_type = GL_FLOAT;
      arrays->arrays[4 + i].count = 4;
      arrays->arrays[4 + i].key = GL_TEXTURE_COORD_ARRAY;

      arrays->arrays[4 + i].old_DrawArrays_possible = (i == 0);
      arrays->arrays[4 + i].index = i;

      arrays->arrays[4 + i].header[1] = i + GL_TEXTURE0;
   }

   i = 4 + texture_units;

   if (got_fog) {
      arrays->arrays[i].data_type = GL_FLOAT;
      arrays->arrays[i].count = 1;
      arrays->arrays[i].key = GL_FOG_COORDINATE_ARRAY;
      arrays->arrays[i].old_DrawArrays_possible = GL_TRUE;
      i++;
   }

   if (got_secondary_color) {
      arrays->arrays[i].data_type = GL_FLOAT;
      arrays->arrays[i].count = 3;
      arrays->arrays[i].key = GL_SECONDARY_COLOR_ARRAY;
      arrays->arrays[i].old_DrawArrays_possible = GL_TRUE;
      arrays->arrays[i].normalized = GL_TRUE;
      i++;
   }


   for (j = 0; j < vertex_program_attribs; j++) {
      const unsigned idx = (vertex_program_attribs - (j + 1));


      arrays->arrays[idx + i].data_type = GL_FLOAT;
      arrays->arrays[idx + i].count = 4;
      arrays->arrays[idx + i].key = GL_VERTEX_ATTRIB_ARRAY_POINTER;

      arrays->arrays[idx + i].old_DrawArrays_possible = 0;
      arrays->arrays[idx + i].index = idx;

      arrays->arrays[idx + i].header[1] = idx;
   }

   i += vertex_program_attribs;


   /* Vertex array *must* be last becuase of the way that
    * emit_DrawArrays_none works.
    */

   arrays->arrays[i].data_type = GL_FLOAT;
   arrays->arrays[i].count = 4;
   arrays->arrays[i].key = GL_VERTEX_ARRAY;
   arrays->arrays[i].old_DrawArrays_possible = GL_TRUE;

   assert((i + 1) == arrays->num_arrays);

   arrays->stack_index = 0;
   arrays->stack = malloc(sizeof(struct array_stack_state)
                          * arrays->num_arrays
                          * __GL_CLIENT_ATTRIB_STACK_DEPTH);
}


/**
 * Calculate the size of a single vertex for the "none" protocol.  This is
 * essentially the size of all the immediate-mode commands required to
 * implement the enabled vertex arrays.
 */
static size_t
calculate_single_vertex_size_none(const struct array_state_vector *arrays)
{
   size_t single_vertex_size = 0;
   unsigned i;


   for (i = 0; i < arrays->num_arrays; i++) {
      if (arrays->arrays[i].enabled) {
         single_vertex_size += ((uint16_t *) arrays->arrays[i].header)[0];
      }
   }

   return single_vertex_size;
}


/**
 * Emit a single element using non-DrawArrays protocol.
 */
GLubyte *
emit_element_none(GLubyte * dst,
                  const struct array_state_vector * arrays, unsigned index)
{
   unsigned i;


   for (i = 0; i < arrays->num_arrays; i++) {
      if (arrays->arrays[i].enabled) {
         const size_t offset = index * arrays->arrays[i].true_stride;

         /* The generic attributes can have more data than is in the
          * elements.  This is because a vertex array can be a 2 element,
          * normalized, unsigned short, but the "closest" immediate mode
          * protocol is for a 4Nus.  Since the sizes are small, the
          * performance impact on modern processors should be negligible.
          */
         (void) memset(dst, 0, ((uint16_t *) arrays->arrays[i].header)[0]);

         (void) memcpy(dst, arrays->arrays[i].header,
                       arrays->arrays[i].header_size);

         dst += arrays->arrays[i].header_size;

         (void) memcpy(dst, ((GLubyte *) arrays->arrays[i].data) + offset,
                       arrays->arrays[i].element_size);

         dst += __GLX_PAD(arrays->arrays[i].element_size);
      }
   }

   return dst;
}


/**
 * Emit a single element using "old" DrawArrays protocol from
 * EXT_vertex_arrays / OpenGL 1.1.
 */
GLubyte *
emit_element_old(GLubyte * dst,
                 const struct array_state_vector * arrays, unsigned index)
{
   unsigned i;


   for (i = 0; i < arrays->num_arrays; i++) {
      if (arrays->arrays[i].enabled) {
         const size_t offset = index * arrays->arrays[i].true_stride;

         (void) memcpy(dst, ((GLubyte *) arrays->arrays[i].data) + offset,
                       arrays->arrays[i].element_size);

         dst += __GLX_PAD(arrays->arrays[i].element_size);
      }
   }

   return dst;
}


struct array_state *
get_array_entry(const struct array_state_vector *arrays,
                GLenum key, unsigned index)
{
   unsigned i;

   for (i = 0; i < arrays->num_arrays; i++) {
      if ((arrays->arrays[i].key == key)
          && (arrays->arrays[i].index == index)) {
         return &arrays->arrays[i];
      }
   }

   return NULL;
}


static GLboolean
allocate_array_info_cache(struct array_state_vector *arrays,
                          size_t required_size)
{
#define MAX_HEADER_SIZE 20
   if (arrays->array_info_cache_buffer_size < required_size) {
      GLubyte *temp = realloc(arrays->array_info_cache_base,
                              required_size + MAX_HEADER_SIZE);

      if (temp == NULL) {
         return GL_FALSE;
      }

      arrays->array_info_cache_base = temp;
      arrays->array_info_cache = temp + MAX_HEADER_SIZE;
      arrays->array_info_cache_buffer_size = required_size;
   }

   arrays->array_info_cache_size = required_size;
   return GL_TRUE;
}


/**
 */
void
fill_array_info_cache(struct array_state_vector *arrays)
{
   GLboolean old_DrawArrays_possible;
   unsigned i;


   /* Determine how many arrays are enabled.
    */

   arrays->enabled_client_array_count = 0;
   old_DrawArrays_possible = arrays->old_DrawArrays_possible;
   for (i = 0; i < arrays->num_arrays; i++) {
      if (arrays->arrays[i].enabled) {
         arrays->enabled_client_array_count++;
         old_DrawArrays_possible &= arrays->arrays[i].old_DrawArrays_possible;
      }
   }

   if (arrays->new_DrawArrays_possible) {
      assert(!arrays->new_DrawArrays_possible);
   }
   else if (old_DrawArrays_possible) {
      const size_t required_size = arrays->enabled_client_array_count * 12;
      uint32_t *info;


      if (!allocate_array_info_cache(arrays, required_size)) {
         return;
      }


      info = (uint32_t *) arrays->array_info_cache;
      for (i = 0; i < arrays->num_arrays; i++) {
         if (arrays->arrays[i].enabled) {
            *(info++) = arrays->arrays[i].data_type;
            *(info++) = arrays->arrays[i].count;
            *(info++) = arrays->arrays[i].key;
         }
      }

      arrays->DrawArrays = emit_DrawArrays_old;
      arrays->DrawElements = emit_DrawElements_old;
   }
   else {
      arrays->DrawArrays = emit_DrawArrays_none;
      arrays->DrawElements = emit_DrawElements_none;
   }

   arrays->array_info_cache_valid = GL_TRUE;
}


/**
 * Emit a \c glDrawArrays command using the "none" protocol.  That is,
 * emit immediate-mode commands that are equivalent to the requiested
 * \c glDrawArrays command.  This is used with servers that don't support
 * the OpenGL 1.1 / EXT_vertex_arrays DrawArrays protocol or in cases where
 * vertex state is enabled that is not compatible with that protocol.
 */
void
emit_DrawArrays_none(GLenum mode, GLint first, GLsizei count)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;

   size_t single_vertex_size;
   GLubyte *pc;
   unsigned i;
   static const uint16_t begin_cmd[2] = { 8, X_GLrop_Begin };
   static const uint16_t end_cmd[2] = { 4, X_GLrop_End };


   single_vertex_size = calculate_single_vertex_size_none(arrays);

   pc = gc->pc;

   (void) memcpy(pc, begin_cmd, 4);
   *(int *) (pc + 4) = mode;

   pc += 8;

   for (i = 0; i < count; i++) {
      if ((pc + single_vertex_size) >= gc->bufEnd) {
         pc = __glXFlushRenderBuffer(gc, pc);
      }

      pc = emit_element_none(pc, arrays, first + i);
   }

   if ((pc + 4) >= gc->bufEnd) {
      pc = __glXFlushRenderBuffer(gc, pc);
   }

   (void) memcpy(pc, end_cmd, 4);
   pc += 4;

   gc->pc = pc;
   if (gc->pc > gc->limit) {
      (void) __glXFlushRenderBuffer(gc, gc->pc);
   }
}


/**
 * Emit the header data for the GL 1.1 / EXT_vertex_arrays DrawArrays
 * protocol.
 *
 * \param gc                    GLX context.
 * \param arrays                Array state.
 * \param elements_per_request  Location to store the number of elements that
 *                              can fit in a single Render / RenderLarge
 *                              command.
 * \param total_request         Total number of requests for a RenderLarge
 *                              command.  If a Render command is used, this
 *                              will be zero.
 * \param mode                  Drawing mode.
 * \param count                 Number of vertices.
 *
 * \returns
 * A pointer to the buffer for array data.
 */
static GLubyte *
emit_DrawArrays_header_old(__GLXcontext * gc,
                           struct array_state_vector *arrays,
                           size_t * elements_per_request,
                           unsigned int *total_requests,
                           GLenum mode, GLsizei count)
{
   size_t command_size;
   size_t single_vertex_size;
   const unsigned header_size = 16;
   unsigned i;
   GLubyte *pc;


   /* Determine the size of the whole command.  This includes the header,
    * the ARRAY_INFO data and the array data.  Once this size is calculated,
    * it will be known whether a Render or RenderLarge command is needed.
    */

   single_vertex_size = 0;
   for (i = 0; i < arrays->num_arrays; i++) {
      if (arrays->arrays[i].enabled) {
         single_vertex_size += __GLX_PAD(arrays->arrays[i].element_size);
      }
   }

   command_size = arrays->array_info_cache_size + header_size
      + (single_vertex_size * count);


   /* Write the header for either a Render command or a RenderLarge
    * command.  After the header is written, write the ARRAY_INFO data.
    */

   if (command_size > gc->maxSmallRenderCommandSize) {
      /* maxSize is the maximum amount of data can be stuffed into a single
       * packet.  sz_xGLXRenderReq is added because bufSize is the maximum
       * packet size minus sz_xGLXRenderReq.
       */
      const size_t maxSize = (gc->bufSize + sz_xGLXRenderReq)
         - sz_xGLXRenderLargeReq;
      unsigned vertex_requests;


      /* Calculate the number of data packets that will be required to send
       * the whole command.  To do this, the number of verticies that
       * will fit in a single buffer must be calculated.
       *
       * The important value here is elements_per_request.  This is the
       * number of complete array elements that will fit in a single
       * buffer.  There may be some wasted space at the end of the buffer,
       * but splitting elements across buffer boundries would be painful.
       */

      elements_per_request[0] = maxSize / single_vertex_size;

      vertex_requests = (count + elements_per_request[0] - 1)
         / elements_per_request[0];

      *total_requests = vertex_requests + 1;


      __glXFlushRenderBuffer(gc, gc->pc);

      command_size += 4;

      pc = ((GLubyte *) arrays->array_info_cache) - (header_size + 4);
      *(uint32_t *) (pc + 0) = command_size;
      *(uint32_t *) (pc + 4) = X_GLrop_DrawArrays;
      *(uint32_t *) (pc + 8) = count;
      *(uint32_t *) (pc + 12) = arrays->enabled_client_array_count;
      *(uint32_t *) (pc + 16) = mode;

      __glXSendLargeChunk(gc, 1, *total_requests, pc,
                          header_size + 4 + arrays->array_info_cache_size);

      pc = gc->pc;
   }
   else {
      if ((gc->pc + command_size) >= gc->bufEnd) {
         (void) __glXFlushRenderBuffer(gc, gc->pc);
      }

      pc = gc->pc;
      *(uint16_t *) (pc + 0) = command_size;
      *(uint16_t *) (pc + 2) = X_GLrop_DrawArrays;
      *(uint32_t *) (pc + 4) = count;
      *(uint32_t *) (pc + 8) = arrays->enabled_client_array_count;
      *(uint32_t *) (pc + 12) = mode;

      pc += header_size;

      (void) memcpy(pc, arrays->array_info_cache,
                    arrays->array_info_cache_size);
      pc += arrays->array_info_cache_size;

      *elements_per_request = count;
      *total_requests = 0;
   }


   return pc;
}


/**
 */
void
emit_DrawArrays_old(GLenum mode, GLint first, GLsizei count)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;

   GLubyte *pc;
   size_t elements_per_request;
   unsigned total_requests = 0;
   unsigned i;
   size_t total_sent = 0;


   pc = emit_DrawArrays_header_old(gc, arrays, &elements_per_request,
                                   &total_requests, mode, count);


   /* Write the arrays.
    */

   if (total_requests == 0) {
      assert(elements_per_request >= count);

      for (i = 0; i < count; i++) {
         pc = emit_element_old(pc, arrays, i + first);
      }

      assert(pc <= gc->bufEnd);

      gc->pc = pc;
      if (gc->pc > gc->limit) {
         (void) __glXFlushRenderBuffer(gc, gc->pc);
      }
   }
   else {
      unsigned req;


      for (req = 2; req <= total_requests; req++) {
         if (count < elements_per_request) {
            elements_per_request = count;
         }

         pc = gc->pc;
         for (i = 0; i < elements_per_request; i++) {
            pc = emit_element_old(pc, arrays, i + first);
         }

         first += elements_per_request;

         total_sent += (size_t) (pc - gc->pc);
         __glXSendLargeChunk(gc, req, total_requests, gc->pc, pc - gc->pc);

         count -= elements_per_request;
      }
   }
}


void
emit_DrawElements_none(GLenum mode, GLsizei count, GLenum type,
                       const GLvoid * indices)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   static const uint16_t begin_cmd[2] = { 8, X_GLrop_Begin };
   static const uint16_t end_cmd[2] = { 4, X_GLrop_End };

   GLubyte *pc;
   size_t single_vertex_size;
   unsigned i;


   single_vertex_size = calculate_single_vertex_size_none(arrays);


   if ((gc->pc + single_vertex_size) >= gc->bufEnd) {
      gc->pc = __glXFlushRenderBuffer(gc, gc->pc);
   }

   pc = gc->pc;

   (void) memcpy(pc, begin_cmd, 4);
   *(int *) (pc + 4) = mode;

   pc += 8;

   for (i = 0; i < count; i++) {
      unsigned index = 0;

      if ((pc + single_vertex_size) >= gc->bufEnd) {
         pc = __glXFlushRenderBuffer(gc, pc);
      }

      switch (type) {
      case GL_UNSIGNED_INT:
         index = (unsigned) (((GLuint *) indices)[i]);
         break;
      case GL_UNSIGNED_SHORT:
         index = (unsigned) (((GLushort *) indices)[i]);
         break;
      case GL_UNSIGNED_BYTE:
         index = (unsigned) (((GLubyte *) indices)[i]);
         break;
      }
      pc = emit_element_none(pc, arrays, index);
   }

   if ((pc + 4) >= gc->bufEnd) {
      pc = __glXFlushRenderBuffer(gc, pc);
   }

   (void) memcpy(pc, end_cmd, 4);
   pc += 4;

   gc->pc = pc;
   if (gc->pc > gc->limit) {
      (void) __glXFlushRenderBuffer(gc, gc->pc);
   }
}


/**
 */
void
emit_DrawElements_old(GLenum mode, GLsizei count, GLenum type,
                      const GLvoid * indices)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;

   GLubyte *pc;
   size_t elements_per_request;
   unsigned total_requests = 0;
   unsigned i;
   unsigned req;
   unsigned req_element = 0;


   pc = emit_DrawArrays_header_old(gc, arrays, &elements_per_request,
                                   &total_requests, mode, count);


   /* Write the arrays.
    */

   req = 2;
   while (count > 0) {
      if (count < elements_per_request) {
         elements_per_request = count;
      }

      switch (type) {
      case GL_UNSIGNED_INT:{
            const GLuint *ui_ptr = (const GLuint *) indices + req_element;

            for (i = 0; i < elements_per_request; i++) {
               const GLint index = (GLint) * (ui_ptr++);
               pc = emit_element_old(pc, arrays, index);
            }
            break;
         }
      case GL_UNSIGNED_SHORT:{
            const GLushort *us_ptr = (const GLushort *) indices + req_element;

            for (i = 0; i < elements_per_request; i++) {
               const GLint index = (GLint) * (us_ptr++);
               pc = emit_element_old(pc, arrays, index);
            }
            break;
         }
      case GL_UNSIGNED_BYTE:{
            const GLubyte *ub_ptr = (const GLubyte *) indices + req_element;

            for (i = 0; i < elements_per_request; i++) {
               const GLint index = (GLint) * (ub_ptr++);
               pc = emit_element_old(pc, arrays, index);
            }
            break;
         }
      }

      if (total_requests != 0) {
         __glXSendLargeChunk(gc, req, total_requests, gc->pc, pc - gc->pc);
         pc = gc->pc;
         req++;
      }

      count -= elements_per_request;
      req_element += elements_per_request;
   }


   assert((total_requests == 0) || ((req - 1) == total_requests));

   if (total_requests == 0) {
      assert(pc <= gc->bufEnd);

      gc->pc = pc;
      if (gc->pc > gc->limit) {
         (void) __glXFlushRenderBuffer(gc, gc->pc);
      }
   }
}


/**
 * Validate that the \c mode parameter to \c glDrawArrays, et. al. is valid.
 * If it is not valid, then an error code is set in the GLX context.
 *
 * \returns
 * \c GL_TRUE if the argument is valid, \c GL_FALSE if is not.
 */
static GLboolean
validate_mode(__GLXcontext * gc, GLenum mode)
{
   switch (mode) {
   case GL_POINTS:
   case GL_LINE_STRIP:
   case GL_LINE_LOOP:
   case GL_LINES:
   case GL_TRIANGLE_STRIP:
   case GL_TRIANGLE_FAN:
   case GL_TRIANGLES:
   case GL_QUAD_STRIP:
   case GL_QUADS:
   case GL_POLYGON:
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return GL_FALSE;
   }

   return GL_TRUE;
}


/**
 * Validate that the \c count parameter to \c glDrawArrays, et. al. is valid.
 * A value less than zero is invalid and will result in \c GL_INVALID_VALUE
 * being set.  A value of zero will not result in an error being set, but
 * will result in \c GL_FALSE being returned.
 *
 * \returns
 * \c GL_TRUE if the argument is valid, \c GL_FALSE if it is not.
 */
static GLboolean
validate_count(__GLXcontext * gc, GLsizei count)
{
   if (count < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
   }

   return (count > 0);
}


/**
 * Validate that the \c type parameter to \c glDrawElements, et. al. is
 * valid.  Only \c GL_UNSIGNED_BYTE, \c GL_UNSIGNED_SHORT, and
 * \c GL_UNSIGNED_INT are valid.
 *
 * \returns
 * \c GL_TRUE if the argument is valid, \c GL_FALSE if it is not.
 */
static GLboolean
validate_type(__GLXcontext * gc, GLenum type)
{
   switch (type) {
   case GL_UNSIGNED_INT:
   case GL_UNSIGNED_SHORT:
   case GL_UNSIGNED_BYTE:
      return GL_TRUE;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return GL_FALSE;
   }
}


void
__indirect_glDrawArrays(GLenum mode, GLint first, GLsizei count)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;


   if (validate_mode(gc, mode) && validate_count(gc, count)) {
      if (!arrays->array_info_cache_valid) {
         fill_array_info_cache(arrays);
      }

      arrays->DrawArrays(mode, first, count);
   }
}


void
__indirect_glArrayElement(GLint index)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;

   size_t single_vertex_size;


   single_vertex_size = calculate_single_vertex_size_none(arrays);

   if ((gc->pc + single_vertex_size) >= gc->bufEnd) {
      gc->pc = __glXFlushRenderBuffer(gc, gc->pc);
   }

   gc->pc = emit_element_none(gc->pc, arrays, index);

   if (gc->pc > gc->limit) {
      (void) __glXFlushRenderBuffer(gc, gc->pc);
   }
}


void
__indirect_glDrawElements(GLenum mode, GLsizei count, GLenum type,
                          const GLvoid * indices)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;


   if (validate_mode(gc, mode) && validate_count(gc, count)
       && validate_type(gc, type)) {
      if (!arrays->array_info_cache_valid) {
         fill_array_info_cache(arrays);
      }

      arrays->DrawElements(mode, count, type, indices);
   }
}


void
__indirect_glDrawRangeElements(GLenum mode, GLuint start, GLuint end,
                               GLsizei count, GLenum type,
                               const GLvoid * indices)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;


   if (validate_mode(gc, mode) && validate_count(gc, count)
       && validate_type(gc, type)) {
      if (end < start) {
         __glXSetError(gc, GL_INVALID_VALUE);
         return;
      }

      if (!arrays->array_info_cache_valid) {
         fill_array_info_cache(arrays);
      }

      arrays->DrawElements(mode, count, type, indices);
   }
}


void
__indirect_glMultiDrawArraysEXT(GLenum mode, GLint * first, GLsizei * count,
                                GLsizei primcount)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   GLsizei i;


   if (validate_mode(gc, mode)) {
      if (!arrays->array_info_cache_valid) {
         fill_array_info_cache(arrays);
      }

      for (i = 0; i < primcount; i++) {
         if (validate_count(gc, count[i])) {
            arrays->DrawArrays(mode, first[i], count[i]);
         }
      }
   }
}


void
__indirect_glMultiDrawElementsEXT(GLenum mode, const GLsizei * count,
                                  GLenum type, const GLvoid ** indices,
                                  GLsizei primcount)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   const __GLXattribute *state =
      (const __GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   GLsizei i;


   if (validate_mode(gc, mode) && validate_type(gc, type)) {
      if (!arrays->array_info_cache_valid) {
         fill_array_info_cache(arrays);
      }

      for (i = 0; i < primcount; i++) {
         if (validate_count(gc, count[i])) {
            arrays->DrawElements(mode, count[i], type, indices[i]);
         }
      }
   }
}


#define COMMON_ARRAY_DATA_INIT(a, PTR, TYPE, STRIDE, COUNT, NORMALIZED, HDR_SIZE, OPCODE) \
  do {                                                                  \
    (a)->data = PTR;                                                    \
    (a)->data_type = TYPE;                                              \
    (a)->user_stride = STRIDE;                                          \
    (a)->count = COUNT;                                                 \
    (a)->normalized = NORMALIZED;                                       \
                                                                        \
    (a)->element_size = __glXTypeSize( TYPE ) * COUNT;                  \
    (a)->true_stride = (STRIDE == 0)                                    \
      ? (a)->element_size : STRIDE;                                     \
                                                                        \
    (a)->header_size = HDR_SIZE;                                        \
    ((uint16_t *) (a)->header)[0] = __GLX_PAD((a)->header_size + (a)->element_size); \
    ((uint16_t *) (a)->header)[1] = OPCODE;                             \
  } while(0)


void
__indirect_glVertexPointer(GLint size, GLenum type, GLsizei stride,
                           const GLvoid * pointer)
{
   static const uint16_t short_ops[5] = {
      0, 0, X_GLrop_Vertex2sv, X_GLrop_Vertex3sv, X_GLrop_Vertex4sv
   };
   static const uint16_t int_ops[5] = {
      0, 0, X_GLrop_Vertex2iv, X_GLrop_Vertex3iv, X_GLrop_Vertex4iv
   };
   static const uint16_t float_ops[5] = {
      0, 0, X_GLrop_Vertex2fv, X_GLrop_Vertex3fv, X_GLrop_Vertex4fv
   };
   static const uint16_t double_ops[5] = {
      0, 0, X_GLrop_Vertex2dv, X_GLrop_Vertex3dv, X_GLrop_Vertex4dv
   };
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (size < 2 || size > 4 || stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_SHORT:
      opcode = short_ops[size];
      break;
   case GL_INT:
      opcode = int_ops[size];
      break;
   case GL_FLOAT:
      opcode = float_ops[size];
      break;
   case GL_DOUBLE:
      opcode = double_ops[size];
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_VERTEX_ARRAY, 0);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, size, GL_FALSE, 4,
                          opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glNormalPointer(GLenum type, GLsizei stride,
                           const GLvoid * pointer)
{
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_BYTE:
      opcode = X_GLrop_Normal3bv;
      break;
   case GL_SHORT:
      opcode = X_GLrop_Normal3sv;
      break;
   case GL_INT:
      opcode = X_GLrop_Normal3iv;
      break;
   case GL_FLOAT:
      opcode = X_GLrop_Normal3fv;
      break;
   case GL_DOUBLE:
      opcode = X_GLrop_Normal3dv;
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_NORMAL_ARRAY, 0);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, 3, GL_TRUE, 4, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glColorPointer(GLint size, GLenum type, GLsizei stride,
                          const GLvoid * pointer)
{
   static const uint16_t byte_ops[5] = {
      0, 0, 0, X_GLrop_Color3bv, X_GLrop_Color4bv
   };
   static const uint16_t ubyte_ops[5] = {
      0, 0, 0, X_GLrop_Color3ubv, X_GLrop_Color4ubv
   };
   static const uint16_t short_ops[5] = {
      0, 0, 0, X_GLrop_Color3sv, X_GLrop_Color4sv
   };
   static const uint16_t ushort_ops[5] = {
      0, 0, 0, X_GLrop_Color3usv, X_GLrop_Color4usv
   };
   static const uint16_t int_ops[5] = {
      0, 0, 0, X_GLrop_Color3iv, X_GLrop_Color4iv
   };
   static const uint16_t uint_ops[5] = {
      0, 0, 0, X_GLrop_Color3uiv, X_GLrop_Color4uiv
   };
   static const uint16_t float_ops[5] = {
      0, 0, 0, X_GLrop_Color3fv, X_GLrop_Color4fv
   };
   static const uint16_t double_ops[5] = {
      0, 0, 0, X_GLrop_Color3dv, X_GLrop_Color4dv
   };
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (size < 3 || size > 4 || stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_BYTE:
      opcode = byte_ops[size];
      break;
   case GL_UNSIGNED_BYTE:
      opcode = ubyte_ops[size];
      break;
   case GL_SHORT:
      opcode = short_ops[size];
      break;
   case GL_UNSIGNED_SHORT:
      opcode = ushort_ops[size];
      break;
   case GL_INT:
      opcode = int_ops[size];
      break;
   case GL_UNSIGNED_INT:
      opcode = uint_ops[size];
      break;
   case GL_FLOAT:
      opcode = float_ops[size];
      break;
   case GL_DOUBLE:
      opcode = double_ops[size];
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_COLOR_ARRAY, 0);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, size, GL_TRUE, 4, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glIndexPointer(GLenum type, GLsizei stride, const GLvoid * pointer)
{
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_UNSIGNED_BYTE:
      opcode = X_GLrop_Indexubv;
      break;
   case GL_SHORT:
      opcode = X_GLrop_Indexsv;
      break;
   case GL_INT:
      opcode = X_GLrop_Indexiv;
      break;
   case GL_FLOAT:
      opcode = X_GLrop_Indexfv;
      break;
   case GL_DOUBLE:
      opcode = X_GLrop_Indexdv;
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_INDEX_ARRAY, 0);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, 1, GL_FALSE, 4, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glEdgeFlagPointer(GLsizei stride, const GLvoid * pointer)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }


   a = get_array_entry(arrays, GL_EDGE_FLAG_ARRAY, 0);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, GL_UNSIGNED_BYTE, stride, 1, GL_FALSE,
                          4, X_GLrop_EdgeFlagv);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glTexCoordPointer(GLint size, GLenum type, GLsizei stride,
                             const GLvoid * pointer)
{
   static const uint16_t short_ops[5] = {
      0, X_GLrop_TexCoord1sv, X_GLrop_TexCoord2sv, X_GLrop_TexCoord3sv,
      X_GLrop_TexCoord4sv
   };
   static const uint16_t int_ops[5] = {
      0, X_GLrop_TexCoord1iv, X_GLrop_TexCoord2iv, X_GLrop_TexCoord3iv,
      X_GLrop_TexCoord4iv
   };
   static const uint16_t float_ops[5] = {
      0, X_GLrop_TexCoord1dv, X_GLrop_TexCoord2fv, X_GLrop_TexCoord3fv,
      X_GLrop_TexCoord4fv
   };
   static const uint16_t double_ops[5] = {
      0, X_GLrop_TexCoord1dv, X_GLrop_TexCoord2dv, X_GLrop_TexCoord3dv,
      X_GLrop_TexCoord4dv
   };

   static const uint16_t mshort_ops[5] = {
      0, X_GLrop_MultiTexCoord1svARB, X_GLrop_MultiTexCoord2svARB,
      X_GLrop_MultiTexCoord3svARB, X_GLrop_MultiTexCoord4svARB
   };
   static const uint16_t mint_ops[5] = {
      0, X_GLrop_MultiTexCoord1ivARB, X_GLrop_MultiTexCoord2ivARB,
      X_GLrop_MultiTexCoord3ivARB, X_GLrop_MultiTexCoord4ivARB
   };
   static const uint16_t mfloat_ops[5] = {
      0, X_GLrop_MultiTexCoord1dvARB, X_GLrop_MultiTexCoord2fvARB,
      X_GLrop_MultiTexCoord3fvARB, X_GLrop_MultiTexCoord4fvARB
   };
   static const uint16_t mdouble_ops[5] = {
      0, X_GLrop_MultiTexCoord1dvARB, X_GLrop_MultiTexCoord2dvARB,
      X_GLrop_MultiTexCoord3dvARB, X_GLrop_MultiTexCoord4dvARB
   };

   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;
   unsigned header_size;
   unsigned index;


   if (size < 1 || size > 4 || stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   index = arrays->active_texture_unit;
   if (index == 0) {
      switch (type) {
      case GL_SHORT:
         opcode = short_ops[size];
         break;
      case GL_INT:
         opcode = int_ops[size];
         break;
      case GL_FLOAT:
         opcode = float_ops[size];
         break;
      case GL_DOUBLE:
         opcode = double_ops[size];
         break;
      default:
         __glXSetError(gc, GL_INVALID_ENUM);
         return;
      }

      header_size = 4;
   }
   else {
      switch (type) {
      case GL_SHORT:
         opcode = mshort_ops[size];
         break;
      case GL_INT:
         opcode = mint_ops[size];
         break;
      case GL_FLOAT:
         opcode = mfloat_ops[size];
         break;
      case GL_DOUBLE:
         opcode = mdouble_ops[size];
         break;
      default:
         __glXSetError(gc, GL_INVALID_ENUM);
         return;
      }

      header_size = 8;
   }

   a = get_array_entry(arrays, GL_TEXTURE_COORD_ARRAY, index);
   assert(a != NULL);
   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, size, GL_FALSE,
                          header_size, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glSecondaryColorPointerEXT(GLint size, GLenum type, GLsizei stride,
                                      const GLvoid * pointer)
{
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (size != 3 || stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_BYTE:
      opcode = 4126;
      break;
   case GL_UNSIGNED_BYTE:
      opcode = 4131;
      break;
   case GL_SHORT:
      opcode = 4127;
      break;
   case GL_UNSIGNED_SHORT:
      opcode = 4132;
      break;
   case GL_INT:
      opcode = 4128;
      break;
   case GL_UNSIGNED_INT:
      opcode = 4133;
      break;
   case GL_FLOAT:
      opcode = 4129;
      break;
   case GL_DOUBLE:
      opcode = 4130;
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_SECONDARY_COLOR_ARRAY, 0);
   if (a == NULL) {
      __glXSetError(gc, GL_INVALID_OPERATION);
      return;
   }

   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, size, GL_TRUE, 4, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glFogCoordPointerEXT(GLenum type, GLsizei stride,
                                const GLvoid * pointer)
{
   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   if (stride < 0) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   switch (type) {
   case GL_FLOAT:
      opcode = 4124;
      break;
   case GL_DOUBLE:
      opcode = 4125;
      break;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   a = get_array_entry(arrays, GL_FOG_COORD_ARRAY, 0);
   if (a == NULL) {
      __glXSetError(gc, GL_INVALID_OPERATION);
      return;
   }

   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, 1, GL_FALSE, 4, opcode);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


void
__indirect_glVertexAttribPointerARB(GLuint index, GLint size,
                                    GLenum type, GLboolean normalized,
                                    GLsizei stride, const GLvoid * pointer)
{
   static const uint16_t short_ops[5] = { 0, 4189, 4190, 4191, 4192 };
   static const uint16_t float_ops[5] = { 0, 4193, 4194, 4195, 4196 };
   static const uint16_t double_ops[5] = { 0, 4197, 4198, 4199, 4200 };

   uint16_t opcode;
   __GLXcontext *gc = __glXGetCurrentContext();
   __GLXattribute *state = (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;
   unsigned true_immediate_count;
   unsigned true_immediate_size;


   if ((size < 1) || (size > 4) || (stride < 0)
       || (index > arrays->num_vertex_program_attribs)) {
      __glXSetError(gc, GL_INVALID_VALUE);
      return;
   }

   if (normalized && (type != GL_FLOAT) && (type != GL_DOUBLE)) {
      switch (type) {
      case GL_BYTE:
         opcode = X_GLrop_VertexAttrib4NbvARB;
         break;
      case GL_UNSIGNED_BYTE:
         opcode = X_GLrop_VertexAttrib4NubvARB;
         break;
      case GL_SHORT:
         opcode = X_GLrop_VertexAttrib4NsvARB;
         break;
      case GL_UNSIGNED_SHORT:
         opcode = X_GLrop_VertexAttrib4NusvARB;
         break;
      case GL_INT:
         opcode = X_GLrop_VertexAttrib4NivARB;
         break;
      case GL_UNSIGNED_INT:
         opcode = X_GLrop_VertexAttrib4NuivARB;
         break;
      default:
         __glXSetError(gc, GL_INVALID_ENUM);
         return;
      }

      true_immediate_count = 4;
   }
   else {
      true_immediate_count = size;

      switch (type) {
      case GL_BYTE:
         opcode = X_GLrop_VertexAttrib4bvARB;
         true_immediate_count = 4;
         break;
      case GL_UNSIGNED_BYTE:
         opcode = X_GLrop_VertexAttrib4ubvARB;
         true_immediate_count = 4;
         break;
      case GL_SHORT:
         opcode = short_ops[size];
         break;
      case GL_UNSIGNED_SHORT:
         opcode = X_GLrop_VertexAttrib4usvARB;
         true_immediate_count = 4;
         break;
      case GL_INT:
         opcode = X_GLrop_VertexAttrib4ivARB;
         true_immediate_count = 4;
         break;
      case GL_UNSIGNED_INT:
         opcode = X_GLrop_VertexAttrib4uivARB;
         true_immediate_count = 4;
         break;
      case GL_FLOAT:
         opcode = float_ops[size];
         break;
      case GL_DOUBLE:
         opcode = double_ops[size];
         break;
      default:
         __glXSetError(gc, GL_INVALID_ENUM);
         return;
      }
   }

   a = get_array_entry(arrays, GL_VERTEX_ATTRIB_ARRAY_POINTER, index);
   if (a == NULL) {
      __glXSetError(gc, GL_INVALID_OPERATION);
      return;
   }

   COMMON_ARRAY_DATA_INIT(a, pointer, type, stride, size, normalized, 8,
                          opcode);

   true_immediate_size = __glXTypeSize(type) * true_immediate_count;
   ((uint16_t *) (a)->header)[0] = __GLX_PAD(a->header_size
                                             + true_immediate_size);

   if (a->enabled) {
      arrays->array_info_cache_valid = GL_FALSE;
   }
}


/**
 * I don't have 100% confidence that this is correct.  The different rules
 * about whether or not generic vertex attributes alias "classic" vertex
 * attributes (i.e., attrib1 ?= primary color) between ARB_vertex_program,
 * ARB_vertex_shader, and NV_vertex_program are a bit confusing.  My
 * feeling is that the client-side doesn't have to worry about it.  The
 * client just sends all the data to the server and lets the server deal
 * with it.
 */
void
__indirect_glVertexAttribPointerNV(GLuint index, GLint size,
                                   GLenum type, GLsizei stride,
                                   const GLvoid * pointer)
{
   __GLXcontext *gc = __glXGetCurrentContext();
   GLboolean normalized = GL_FALSE;


   switch (type) {
   case GL_UNSIGNED_BYTE:
      if (size != 4) {
         __glXSetError(gc, GL_INVALID_VALUE);
         return;
      }
      normalized = GL_TRUE;

   case GL_SHORT:
   case GL_FLOAT:
   case GL_DOUBLE:
      __indirect_glVertexAttribPointerARB(index, size, type,
                                          normalized, stride, pointer);
      return;
   default:
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }
}


void
__indirect_glClientActiveTextureARB(GLenum texture)
{
   __GLXcontext *const gc = __glXGetCurrentContext();
   __GLXattribute *const state =
      (__GLXattribute *) (gc->client_state_private);
   struct array_state_vector *const arrays = state->array_state;
   const GLint unit = (GLint) texture - GL_TEXTURE0;


   if ((unit < 0) || (unit >= arrays->num_texture_units)) {
      __glXSetError(gc, GL_INVALID_ENUM);
      return;
   }

   arrays->active_texture_unit = unit;
}


/**
 * Modify the enable state for the selected array
 */
GLboolean
__glXSetArrayEnable(__GLXattribute * state, GLenum key, unsigned index,
                    GLboolean enable)
{
   struct array_state_vector *arrays = state->array_state;
   struct array_state *a;


   /* Texture coordinate arrays have an implict index set when the
    * application calls glClientActiveTexture.
    */
   if (key == GL_TEXTURE_COORD_ARRAY) {
      index = arrays->active_texture_unit;
   }

   a = get_array_entry(arrays, key, index);

   if ((a != NULL) && (a->enabled != enable)) {
      a->enabled = enable;
      arrays->array_info_cache_valid = GL_FALSE;
   }

   return (a != NULL);
}


void
__glXArrayDisableAll(__GLXattribute * state)
{
   struct array_state_vector *arrays = state->array_state;
   unsigned i;


   for (i = 0; i < arrays->num_arrays; i++) {
      arrays->arrays[i].enabled = GL_FALSE;
   }

   arrays->array_info_cache_valid = GL_FALSE;
}


/**
 */
GLboolean
__glXGetArrayEnable(const __GLXattribute * const state,
                    GLenum key, unsigned index, GLintptr * dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);

   if (a != NULL) {
      *dest = (GLintptr) a->enabled;
   }

   return (a != NULL);
}


/**
 */
GLboolean
__glXGetArrayType(const __GLXattribute * const state,
                  GLenum key, unsigned index, GLintptr * dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);

   if (a != NULL) {
      *dest = (GLintptr) a->data_type;
   }

   return (a != NULL);
}


/**
 */
GLboolean
__glXGetArraySize(const __GLXattribute * const state,
                  GLenum key, unsigned index, GLintptr * dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);

   if (a != NULL) {
      *dest = (GLintptr) a->count;
   }

   return (a != NULL);
}


/**
 */
GLboolean
__glXGetArrayStride(const __GLXattribute * const state,
                    GLenum key, unsigned index, GLintptr * dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);

   if (a != NULL) {
      *dest = (GLintptr) a->user_stride;
   }

   return (a != NULL);
}


/**
 */
GLboolean
__glXGetArrayPointer(const __GLXattribute * const state,
                     GLenum key, unsigned index, void **dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);


   if (a != NULL) {
      *dest = (void *) (a->data);
   }

   return (a != NULL);
}


/**
 */
GLboolean
__glXGetArrayNormalized(const __GLXattribute * const state,
                        GLenum key, unsigned index, GLintptr * dest)
{
   const struct array_state_vector *arrays = state->array_state;
   const struct array_state *a =
      get_array_entry((struct array_state_vector *) arrays,
                      key, index);


   if (a != NULL) {
      *dest = (GLintptr) a->normalized;
   }

   return (a != NULL);
}


/**
 */
GLuint
__glXGetActiveTextureUnit(const __GLXattribute * const state)
{
   return state->array_state->active_texture_unit;
}


void
__glXPushArrayState(__GLXattribute * state)
{
   struct array_state_vector *arrays = state->array_state;
   struct array_stack_state *stack =
      &arrays->stack[(arrays->stack_index * arrays->num_arrays)];
   unsigned i;

   /* XXX are we pushing _all_ the necessary fields? */
   for (i = 0; i < arrays->num_arrays; i++) {
      stack[i].data = arrays->arrays[i].data;
      stack[i].data_type = arrays->arrays[i].data_type;
      stack[i].user_stride = arrays->arrays[i].user_stride;
      stack[i].count = arrays->arrays[i].count;
      stack[i].key = arrays->arrays[i].key;
      stack[i].index = arrays->arrays[i].index;
      stack[i].enabled = arrays->arrays[i].enabled;
   }

   arrays->active_texture_unit_stack[arrays->stack_index] =
      arrays->active_texture_unit;

   arrays->stack_index++;
}


void
__glXPopArrayState(__GLXattribute * state)
{
   struct array_state_vector *arrays = state->array_state;
   struct array_stack_state *stack;
   unsigned i;


   arrays->stack_index--;
   stack = &arrays->stack[(arrays->stack_index * arrays->num_arrays)];

   for (i = 0; i < arrays->num_arrays; i++) {
      switch (stack[i].key) {
      case GL_NORMAL_ARRAY:
         __indirect_glNormalPointer(stack[i].data_type,
                                    stack[i].user_stride, stack[i].data);
         break;
      case GL_COLOR_ARRAY:
         __indirect_glColorPointer(stack[i].count,
                                   stack[i].data_type,
                                   stack[i].user_stride, stack[i].data);
         break;
      case GL_INDEX_ARRAY:
         __indirect_glIndexPointer(stack[i].data_type,
                                   stack[i].user_stride, stack[i].data);
         break;
      case GL_EDGE_FLAG_ARRAY:
         __indirect_glEdgeFlagPointer(stack[i].user_stride, stack[i].data);
         break;
      case GL_TEXTURE_COORD_ARRAY:
         arrays->active_texture_unit = stack[i].index;
         __indirect_glTexCoordPointer(stack[i].count,
                                      stack[i].data_type,
                                      stack[i].user_stride, stack[i].data);
         break;
      case GL_SECONDARY_COLOR_ARRAY:
         __indirect_glSecondaryColorPointerEXT(stack[i].count,
                                               stack[i].data_type,
                                               stack[i].user_stride,
                                               stack[i].data);
         break;
      case GL_FOG_COORDINATE_ARRAY:
         __indirect_glFogCoordPointerEXT(stack[i].data_type,
                                         stack[i].user_stride, stack[i].data);
         break;

      }

      __glXSetArrayEnable(state, stack[i].key, stack[i].index,
                          stack[i].enabled);
   }

   arrays->active_texture_unit =
      arrays->active_texture_unit_stack[arrays->stack_index];
}