Name

    ARB_vertex_buffer_object

Name Strings

    GL_ARB_vertex_buffer_object
    GLX_ARB_vertex_buffer_object

Contributors

    Ben Ashbaugh
    Bob Beretta
    Pat Brown
    Cass Everitt
    Mandar Godse
    James Jones
    John Kessenich
    Dale Kirkland
    Jon Leech
    Bill Licea-Kane
    Barthold Lichtenbelt
    Bimal Poddar
    Thomas Roell
    Ian Romanick
    Jeremy Sandmel
    Jon Paul Schelter
    John Stauffer
    Nick Triantos
    Daniel Vogel

Contact

    Rick Hammerstone, AMD (rick.hammerstone 'at' amd.com)
    Matt Craighead, NVIDIA Corporation (mcraighead 'at' nvidia.com)
    Kurt Akeley, NVIDIA Corporation (kakeley 'at' nvidia.com)

Notice

    Copyright (c) 2003-2013 The Khronos Group Inc. Copyright terms at
        http://www.khronos.org/registry/speccopyright.html

Specification Update Policy

    Khronos-approved extension specifications are updated in response to
    issues and bugs prioritized by the Khronos OpenGL Working Group. For
    extensions which have been promoted to a core Specification, fixes will
    first appear in the latest version of that core Specification, and will
    eventually be backported to the extension document. This policy is
    described in more detail at
        https://www.khronos.org/registry/OpenGL/docs/update_policy.php

IP Status

    None.

Status

    Complete. Approved by ARB on February 12, 2003.

Version

    Last Modified Date: October 25, 2010
    Revision: 0.99.6

Number

    ARB Extension #28

Dependencies

    Written based on the wording of the OpenGL 1.4 specification.

    GL_ARB_vertex_blend affects the definition of this extension.

    GL_ARB_vertex_program affects the definition of this extension.

    GL_EXT_vertex_shader affects the definition of this extension.

    GLX_ARB_create_context affects the behavior of this extension.

Overview

    This extension defines an interface that allows various types of data
    (especially vertex array data) to be cached in high-performance
    graphics memory on the server, thereby increasing the rate of data
    transfers.

    Chunks of data are encapsulated within "buffer objects", which
    conceptually are nothing more than arrays of bytes, just like any
    chunk of memory.  An API is provided whereby applications can read
    from or write to buffers, either via the GL itself (glBufferData,
    glBufferSubData, glGetBufferSubData) or via a pointer to the memory.

    The latter technique is known as "mapping" a buffer.  When an
    application maps a buffer, it is given a pointer to the memory.  When
    the application finishes reading from or writing to the memory, it is
    required to "unmap" the buffer before it is once again permitted to
    use that buffer as a GL data source or sink.  Mapping often allows
    applications to eliminate an extra data copy otherwise required to
    access the buffer, thereby enhancing performance.  In addition,
    requiring that applications unmap the buffer to use it as a data
    source or sink ensures that certain classes of latent synchronization
    bugs cannot occur.

    Although this extension only defines hooks for buffer objects to be
    used with OpenGL's vertex array APIs, the API defined in this
    extension permits buffer objects to be used as either data sources or
    sinks for any GL command that takes a pointer as an argument.
    Normally, in the absence of this extension, a pointer passed into the
    GL is simply a pointer to the user's data.  This extension defines
    a mechanism whereby this pointer is used not as a pointer to the data
    itself, but as an offset into a currently bound buffer object.  The
    buffer object ID zero is reserved, and when buffer object zero is
    bound to a given target, the commands affected by that buffer binding
    behave normally.  When a nonzero buffer ID is bound, then the pointer
    represents an offset.

    In the case of vertex arrays, this extension defines not merely one
    binding for all attributes, but a separate binding for each
    individual attribute.  As a result, applications can source their
    attributes from multiple buffers.  An application might, for example,
    have a model with constant texture coordinates and variable geometry.
    The texture coordinates might be retrieved from a buffer object with
    the usage mode "STATIC_DRAW", indicating to the GL that the
    application does not expect to update the contents of the buffer
    frequently or even at all, while the vertices might be retrieved from
    a buffer object with the usage mode "STREAM_DRAW", indicating that
    the vertices will be updated on a regular basis.

    In addition, a binding is defined by which applications can source
    index data (as used by DrawElements, DrawRangeElements, and
    MultiDrawElements) from a buffer object.  On some platforms, this
    enables very large models to be rendered with no more than a few
    small commands to the graphics device.

    It is expected that a future extension will allow sourcing pixel data
    from and writing pixel data to a buffer object.

Issues

    What should this extension be called?

        RESOLVED: By unanimous consent among the working group members,
        the name was chosen to be "ARB_vertex_buffer_object".  A large
        number of other names were considered throughout the lifetime of
        the proposal, especially "vertex_array_object" (originally),
        "buffer_object" (later on), and "memory_object" (near the end),
        but the name "vertex_buffer_object" was ultimately chosen.

        In particular, this name emphasizes not only that we have created
        a new type of object that encapsulates arbitrary data (buffer
        objects), but also, in particular, that these objects are used in
        this extension to source vertex data.  The name also is
        intentionally similar to "vertex buffers", although it should be
        emphasized that there is no such thing as a "vertex buffer" in
        the terminology of this extension.  The term "buffer object" is
        the correct noun.

    How is this extension different from ATI_vertex_array_object plus
    ATI_map_object_buffer?

        The following summarizes the major differences.
        - VAOs renamed to "buffer objects", to signify that they can be
          used for more than just vertex data.  Other renaming and API
          changes to try to better match OpenGL conventions.
        - The standard GL pointer APIs have been overloaded to be able to
          refer to offsets within these buffers, rather than adding new
          entry points.
        - The usage modes permitted for buffers have been augmented
          significantly, to reflect a broader class of application
          behaviors.
        - A new entry point allows reading back the contents of a buffer
          object.

    How is this extension different from NV_vertex_array_range?

        The following summarizes the major differences.
        - Applications are no longer responsible for memory management
          and synchronization.
        - Applications may still access high-performance memory, but
          this is optional, and such access is more restricted.
        - Buffer changes (glBindBufferARB) are generally expected to
          be very lightweight, rather than extremely heavyweight
          (glVertexArrayRangeNV).
        - A platform-specific allocator such as wgl/glXAllocateMemoryNV
          is no longer required.

    How does this extension relate to NV_pixel_data_range?

        A future extension could be created based on the framework
        created here that would support analogous functionality to that
        provided by NV_pixel_data_range.  Presumably, this extension
        would require little more than two new targets for BindBuffer,
        named (say) UNPACK_PIXELS and PACK_PIXELS.  The lists of commands
        affected by these bindings could easily be taken verbatim out of
        the NV_pixel_data_range specification.

    Should this extension include support for allowing vertex indices
    to be stored in buffer objects?

        RESOLVED: YES.  It is easily and cleanly added with just the
        addition of a binding point for the index buffer object.  Since
        our approach of overloading pointers works for any pointer in GL,
        no additional APIs need be defined, unlike in the various
        *_element_array extensions.

        Note that it is expected that implementations may have different
        memory type requirements for efficient storage of indices and
        vertices.  For example, some systems may prefer indices in AGP
        memory and vertices in video memory, or vice versa; or, on
        systems where DMA of index data is not supported, index data must
        be stored in (cacheable) system memory for acceptable
        performance.  As a result, applications are strongly urged to
        put their models' vertex and index data in separate buffers, to
        assist drivers in choosing the most efficient locations.

    Should the layout of an array store be defined at array store
    creation time?

        RESOLVED: NO.  This could provide better performance if the
        client specifies a data type that the hardware doesn't support,
        but this isn't a performance path anyways, and it adds a
        cumbersome interface on top of the extension.

    Should there be some sort of scheme for allowing applications to
    stream vertex data efficiently?

        RESOLVED: YES.  Applications that generate their data on the
        fly end up doing an extra data copy unless they are given a
        pointer into memory that the graphics hardware can DMA from.  The
        performance win from doing this can be significant.

    Should the client be able to retrieve a pointer to a buffer object?

        RESOLVED: YES.  This solves the previous problem.  Since GL
        vertex array formats are already user-visible, this does not
        suffer from the sorts of formatting issues that would arise if
        the GL allowed applications to retrieve pointers to texture
        objects or to the framebuffer.  Synchronization can be a concern,
        but proper usage of this extension will minimize its overhead.

    Should this extension sit on top of the existing vertex array
    implementation, instead of introducing a new set of API calls?

        RESOLVED: YES.  This simplifies the API, and separating out the
        buffer binding from the offset/stride within the buffer leads to
        an elegant "BindBufferARB" command that can be used for other
        parts of GL like the pixel path.

    Should buffer object state overlap with existing vertex array pointer
    state, or should there be new drawing commands, e.g.,
    DrawArrayObject?

        RESOLVED: OVERLAP.  The exponential growth in drawing commands
        is problematic.  Even without this, there is already
        ArrayElement, DrawArrays, DrawElements, DrawRangeElements,
        MultiDrawArrays, and MultiDrawElements.

    Does the buffer binding state push/pop?

        RESOLVED: YES.  It pushes/pops on the client with the rest of
        the vertex array state.  Some revisions of the ATI VAO spec
        listed a push/pop attrib "objbuf", but no new bit was defined;
        all this has been moved into the standard "vertex-array" bit.

        Note that both the user-controlled binding ARRAY_BUFFER_ARB
        binding point and the per-array bindings push and pop.

        Note that additional binding points, such as ones for pixel or
        texture transfers, would not be part of the vertex array state,
        and thus would likely push and pop as part of the pixel store
        (client) state when they are defined.

    How is the decision whether to use the array pointer as an offset or
    as a real pointer made?

        RESOLVED: When the default buffer object (object zero) is
        bound, all pointers behave as real pointers.  When any other
        object is bound, all pointers are treated as offsets.
        Conceptually, one can imagine that buffer object zero is a buffer
        object sitting at base NULL and with an extent large enough that
        it covers all of the system's virtual address space.

        Note that this approach essentially requires that binding points
        be client (not server) state.

    Can buffer objects be shared between contexts in the same way that
    display lists are?

        RESOLVED: YES.  All potentially large OpenGL objects, such as
        display lists and textures, can be shared, and this is an
        important capability.  Note, however, that sharing requires that
        buffer objects be server (not client) state, since it is not
        possible to share client state.

    Should buffer objects be client state or server state?

        RESOLVED: Server state.  Arguments for client state include:

          - Buffer data are stored in client-side format, making server
            storage complex when client and server endianness differ.
          - Vertex arrays are client state.

        These arguments are outweighed by the significant advantages
        of server state, including:

          - Server state can be shared between contexts, and this is
            expected to be an important capability (sharing of texture
            objects is very common).
          - In the case of indirect rendering, performance may be
            very significantly greater for data stored on the server
            side of the wire.

    How is synchronization enforced when buffer objects are shared by
    multiple OpenGL contexts?

        RESOLVED: It is generally the clients' responsibility to
        synchronize modifications made to shared buffer objects.  GL
        implementations will make some effort to avoid deletion of in-use
        buffer objects, but may not be able to ensure this handling.

    What happens if a currently bound buffer object is deleted?

        RESOLVED: Orphan.  To avoid chasing invalid pointers OpenGL
        implementations will attempt to defer the deletion of any buffer
        object until that object is not bound by any client in the share
        list.  It should be possible to implement this behavior
        efficiently in the direct rendering case, but the implementation
        may be difficult/impossible in the indirect rendering case.
        Since synchronization during sharing is a client responsibility,
        this behavior is acceptable.

    Should there be a way to query the data in a buffer object?

        RESOLVED: YES.  Almost all objects in OpenGL are fully
        queriable, and since these objects are simply byte arrays, there
        does not seem to be any reason to do things otherwise here.  The
        primary exceptions to GL queriability are cases where such
        functionality would be extremely burdensome to provide, as is the
        case with display lists.

    Do buffer objects survive screen resolution changes, etc.?

        RESOLVED: YES.  This is not mentioned in the spec, so by
        default they behave just like other OpenGL state, like texture
        objects -- the data is unmodified by external events like
        modeswitches, switching the system into standby or hibernate
        mode, etc.

    What happens to a mapped buffer when a screen resolution change or
    other such window-system-specific system event occurs?

        RESOLVED: The buffer's contents may become undefined.  The
        application will then be notified at Unmap time that the buffer's
        contents have been destroyed.  However, for the remaining
        duration of the map, the pointer returned from Map must continue
        to point to valid memory, in order to ensure that the application
        cannot crash if it continues to read or write after the system
        event has been handled.

    What happens to the pointer returned by MapBufferARB after a call to
    UnmapBufferARB?

        RESOLVED: The pointer becomes totally invalid.  Note that
        drivers are free to move the underlying buffer or even unmap the
        memory, leaving the virtual addresses in question pointing at
        nothing.  Such flexibility is necessary to enable efficient
        implementations on systems with no virtual memory; with limited
        control over virtual memory from graphics drivers; or where
        virtual address space is at a premium.

    Are any of these commands allowed inside Begin/End?

        RESOLVED: NO, with the possible exception of BindBuffer, which
        should not be used inside a Begin/End but will have undefined
        error behavior, like most vertex array commands.

    What happens when an attempt is made to access data outside the
    bounds of the buffer object with a command that dereferences the
    arrays?

        RESOLVED: ALLOW PROGRAM TERMINATION.  In the event of a
        software fallback, bounds checking can become impractical.  Since
        applications don't know the actual address of the buffer object
        and only provide an offset, they can't ever guarantee that
        out-of-bounds offsets will fall on valid memory.  So it's hard to
        do any better than this.

        Of course, such an event should not be able to bring down the
        system, only terminate the program.

    What type should <offset> and <size> arguments use?

        RESOLVED: We define new types that will work well on 64-bit
        systems, analogous to C's "intptr_t".  The new type "GLintptrARB"
        should be used in place of GLint whenever it is expected that
        values might exceed 2 billion.  The new type "GLsizeiptrARB"
        should be used in place of GLsizei whenever it is expected
        that counts might exceed 2 billion.  Both types are defined as
        signed integers large enough to contain any pointer value.  As a
        result, they naturally scale to larger numbers of bits on systems
        with 64-bit or even larger pointers.

        The offsets introduced in this extension are typed GLintptrARB,
        consistent with other GL parameters that must be non-negative,
        but are arithmetic in nature (not uint), and are not sizes; for
        example, the xoffset argument to TexSubImage*D is of type GLint.
        Buffer sizes are typed GLsizeiptrARB.

        The idea of making these types unsigned was considered, but was
        ultimately rejected on the grounds that supporting buffers larger
        than 2 GB was not deemed important on 32-bit systems.

    Should buffer maps be client or server state?

        RESOLVED: Server.  If a buffer is being shared by multiple
        clients, it will also be desirable to share the mappings of that
        buffer.  In cases where the mapping cannot shared (for example,
        in the case of indirect rendering) queries of the map pointer by
        clients other than the one that created the map will return a
        null pointer.

    Should "Unmap" be treated as one word or two?

        RESOLVED: One word.

    Should "usage" be a parameter to BufferDataARB, or specified
    separately using a parameter specification command, e.g.,
    BufferParameteriARB?

        RESOLVED: Parameter to BufferDataARB.  It is desirable for the
        implementation to know the usage when the buffer is initialized,
        so including it in the initialization command makes sense.  This
        avoids manpage notes such as "Please specify the usage before you
        initialize the buffer".

    Should it be possible to change the usage of an initialized buffer?

        RESOLVED: NO.  Unless it is shown that this flexibility is
        necessary, it will be easier for implementations to be efficient
        if usage cannot be changed.  (Except by re-initializing the
        buffer completely.)

    Should we allow for the possibility of multiple simultaneous maps for
    a single buffer?

        RESOLVED: NO.  If multiple maps are allowed, the mapping
        semantics become very difficult to understand and to specify.
        It is also unclear that there are any benefits to offering such
        functionality.  Therefore, only one map per buffer is permitted.

        Note: the limit of one map per buffer eliminates any need for
        "sub-region" mapping.  The single map always maps the entire
        data store of the buffer.

    Should it be an error to render from a currently mapped buffer?

        RESOLVED: YES.  Making this an error rather than undefined makes
        the API much more bulletproof.

    Should it be possible for the application to query the "viability" of
    the data store of a buffer?

        RESOLVED: NO.  UnmapBuffer can return FALSE to indicate this, but
        there is no additional query to check whether the data has been
        lost.  In general, most/all GL state is queriable, unless there
        is a compelling reason otherwise.  However, on examination, it
        appears that there are several compelling reasons otherwise in
        this case.  In particular, the default for this state variable is
        non-obvious (is the data "valid" when no data has been specified
        yet?), and it's unclear when it should be reset (BufferData only?
        BufferSubData?  A successful UnmapBuffer?).  After these issues
        came to light, the query was removed from the spec.

    What should the error behavior of BufferDataARB and MapBufferARB be?

        RESOLVED: BufferDataARB returns no value and sets OUT_OF_MEMORY
        if the buffer could not be created, whereas MapBufferARB returns
        NULL and also sets OUT_OF_MEMORY if the buffer could not be
        mapped.

    Should UnmapBufferARB return a boolean indicating data integrity?

        RESOLVED: YES, since the Unmap is precisely the point at which
        the buffer can no longer be lost.

    How is unaligned data handled?

        RESOLVED: All client restrictions on data alignment must be met,
        and in addition to that, all offsets must be multiples of the
        size of the underlying data type.  So, for example, float data in
        a buffer object must have an offset that is (typically) a
        multiple of 4.  This should make the server implementation
        easier, since this additional rule will guarantee that no
        alignment checking is required on most platforms.

    Should MapBufferARB return the pointer to the map, or should there be
    a separate call to ask for the pointer?

        RESOLVED: BOTH.  For convenience, MapBufferARB returns a pointer
        or NULL in the event of failure; but since most/all GL state is
        queriable, you can also query the pointer at a later point in
        time.  If the buffer is not mapped, querying the pointer should
        return NULL.

    Should there be one binding point for all arrays or several binding
    points, one for each array?

        RESOLVED: One binding point for all arrays.  Index data uses a
        separate target.

    Should there be a PRESERVE/DISCARD option on BufferSubDataARB?  On
    MapBufferARB?

        RESOLVED: NO, NO.  ATI_vertex_array_object had this option for
        UpdateObjectBufferATI, which is the equivalent of
        BufferSubDataARB, but it's unclear whether this has any utility.
        There might be some utility for MapBufferARB, but forcing the
        user to call BufferDataARB again with a NULL data pointer has
        some advantages of its own, such as forcing the user to respecify
        the size.

    Should there be an option for MapBufferARB that guarantees
    nonvolatile memory?

        RESOLVED: NO.  On systems where volatile memory spaces are a
        concern, there is little or no way to supply nonvolatile memory
        without crippling performance badly.  In some cases, it might
        not even be possible to implement Map except by returning system
        memory.  Systems that do not have problems with volatility are,
        of course, welcome to return TRUE from UnmapBufferARB all the
        time.  If applications want the ease of use that results from not
        having to check for lost data, they can still use BufferDataARB
        and BufferSubDataARB, so the burden is not too great.

    What new usages do we need to add?

        RESOLVED.  We have defined a 3x3 matrix of usages.  The
        pixel-related terms draw, read, and copy are used to distinguish
        between three basic data paths: application to GL (draw), GL to
        application (read), and GL to GL (copy).  The terms stream,
        static, and dynamic are used to identify three data access
        patterns: specify once and use once or perhaps only a few times
        (stream), specify once and use many times (static), and specify
        and use repeatedly (dynamic).

        Note that the "copy" and "read" usage token values will become
        meaningful only when pixel transfer capability is added to
        buffer objects by a (presumed) subsequent extension.

        Note that the data paths "draw", "read", and "copy" are analogous
        in both name and meaning to the GL commands DrawPixels,
        ReadPixels, and CopyPixels, respectively.

    Is it legal C to use pointers as offsets?

        We haven't come to any definitive conclusion about this.  The
        proposal is to convert to pointer as:

            pointer = (char *)NULL + offset;

        And convert back to offset as:

            offset = (char *)pointer - (char *)NULL;

        Varying opinions have been expressed as to whether this is legal,
        although no one could provide an example of a real system where
        any problems would occur.

    Should we add new Offset commands, e.g., VertexOffset, if the pointer
    approach has some compatibility concerns?

        RESOLVED: NO.  The working group voted that the existing pointer-
        as-offset approach is acceptable.

    Which commands are compiled into display lists?

        RESOLVED: None of the commands in this extension are compiled
        into display lists.  The reasoning is that the server may not
        have up-to-date buffer bindings, since BindBuffer is a client
        command.

        Just as without this extension, vertex data is dereferenced
        when ArrayElement, etc. are compiled into a display list.

    Should there be a new command "DiscardAndMapBuffer" that is
    equivalent to BufferDataARB with NULL pointer followed by
    MapBufferARB?

        RESOLVED: NO, no one has come up with a clearly superior proposal
        that everyone can agree on.

     Are any GL commands disallowed when at least one buffer object is
     mapped?

        RESOLVED: NO.  In general, applications may use whatever GL
        commands they wish when a buffer is mapped.  However, several
        other restrictions on the application do apply: the application
        must not attempt to source data out of, or sink data into, a
        currently mapped buffer.  Furthermore, the application may not
        use the pointer returned by Map as an argument to a GL command.
        (Note that this last restriction is unlikely to be enforced in
        practice, but it violates reasonable expectations about how the
        extension should be used, and it doesn't seem to be a very
        interesting usage model anyhow.  Maps are for the user, not for
        the GL.)

        More restrictive rules were considered (for example, "after
        calling MapBuffer, all GL commands except for UnmapBuffer produce
        errors"), but this was considered far too restrictive.  The
        expectation is that an application might map a buffer and start
        filling it in a different thread, but continue to render in its
        main thread (using a different buffer or no buffer at all).  So
        no commands are disallowed simply because a buffer happens to be
        mapped.

    Should the usage and data arguments to BufferDataARB be swapped?

        RESOLVED: NO.  This would be more consistent with other things in
        GL if they were swapped, but no one seems to care.  If we had
        caught this earlier, maybe, but it's just too late.

    How does MultiDrawElements work?

        The language gets a little confusing, but I believe it is quite
        clearly specified in the end.  The argument <indices> to
        MultiDrawElements, which is of type "const void **", is an
        honest-to-goodness pointer to regular old system memory, no
        matter whether a buffer is bound or not.  That memory in turn
        consists of an array of <primcount> pointers.  If no buffer is
        bound, each of those <primcount> pointers is a regular pointer.
        If a buffer is bound, each of those <primcount> pointers is a
        fake pointer that represents an offset in the buffer object.

        If you wanted to put the array of <primcount> offsets in a buffer
        object, you'd have to define a new extension with a new target.

    When is the binding between a buffer object and a specific vertex array
    (e.g., VERTEX_ARRAY_BUFFER_BINDING_ARB) established?

        The array's buffer binding is set when the array pointer is specified.
        Using the vertex array as an example, this is when VertexPointer is
        called.  At that time, the current array buffer binding is used for
        the vertex array.  The current array buffer binding is set by calling
        BindBufferARB with a <target> of ARRAY_BUFFER_ARB.  Changing the
        current array buffer binding does not affect the bindings used by
        already established arrays.

          BindBufferARB(ARRAY_BUFFER_ARB, 1);
          VertexPointer(...);   // vertex array data points to buffer 1
          BindBufferARB(ARRAY_BUFFER_ARB, 2);
          // vertex array data still points to buffer 1

    What happens when a single ArrayElement call within a large sequence of
    ArrayElement calls specifies an element that is outside the range of the
    bound buffer objects?

        UNRESOLVED.  The three suggestions from the ARB meeting are to either
        ignore just that ArrayElement call, set an error bit and ignore all
        ArrayElement calls until End, or treat the ArrayElement call as
        ArrayElement(0).

    How should EnableClientState and DisableClientState be handled when using
    indirect rendering?

        RESOLVED:  EnableVertexAttribArray and DisableVertexAttribArray
        commands are used to inform the server of new enable/disable state.

    When using indirect rendering, how is DrawElements handled when the
    element array is in a buffer object but one or more of the enabled arrays
    are not?

        RESOLVED:  There are two commands that can be used to implement
        DrawElements and related calls.  If all of the data resides on the
        server, the element pointer is set by using a BindBufferToArray
        command with array set to ELEMENT_ARRAY_ATI.  This command is
        followed by the appropriate EnableVertexAttribArray and
        DisableVertexAttribArray calls, making sure to enable the element
        array, and either a DrawElements call or a DrawRangeElements call.

        If any arrays reside on the client, including the element array,
        the sequence is essentially the same except the DrawRangeElements
        protocol must be used.  If the element array resides on the
        server, the client must issue a GetElementRange command to determine
        the range of array data (and the values for 'start' and 'end') that
        must be sent to the server.

    Is a "provoke" flag needed in the ArrayElement command protocol to switch
    between the case where the VERTEX_ARRAY is client-side vs. server-side?

        NO.  The server will know whether or not the ArrayElement command
        will provoke a vertex because it knows whether or not a buffer object
        is bound to VERTEX_ARRAY.

    How does the server know which arrays with buffer objects bound are
    enabled in ArrayElement?

        RESOLVED:  The array bindings are configured using BindBuffer and
        BindBufferToArray commands.  The arrays are enabled and disabled
        using EnableVertexAttribArray and DisableVertexAttribArray.

    Can't the server-side array bindings just be sent in a the ARRAY_INFO
    when DrawArrays is called?

        NO.  The only way to do that would be to transmit the offset, type,
        size, stride, etc. parameters and the buffer ID.  However, the buffer
        may have been deleted in the meantime.  The binding information must
        be sent when the pointer function (e.g., VertexPointer) is called.

    What about byte-ordering?  The format of the data, and therefore what
    byte-swapping may need to be done, is not know when the data is
    uploaded to the server.  In fact, since it is legal (though probably
    nonsensical) to have the same bytes in the buffer be used as multiple
    datatypes, a single byte-ordering may not exist.  What happens when
    two clients with different byte ordering share one buffer object?  Is
    it valid to not expose the extension if the byte-ordering of the
    client and server do not match?

        RESOLVED:  It is the client's responsibility to convert buffer data
        to and from the server's byte order.  Since only the client knows
        the correct format of the data, and there may be multiple clients
        with different byte orderings sharing a single buffer object, it is
        unreasonable to ask the GL to handle buffer object byte-swapping.
        To avoid errors caused by naive clients attempting to use buffer
        objects without performing the appropriate byte swapping, clients
        must opt in to buffer object support at context creation time using
        the GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB context attrib.
        If this attribute is not specified and the byte ordering of the client
        and server differ, the VBO extension must not be exposed and the
        maximum context version that can be reported is 1.4.

    Should Enable/DisableVertexAttribArray and VertexAttribPointer handle
    both indexed and legacy arrays?

        RESOLVED: Yes.  Send GL_NONE for <array> when referring to
        indexed arrays.  For all other values of <array> except
        GL_TEXTURE??, <index> is ignored.  For GL_TEXTURE??, see next
        issue.

    How is the client-side state client active texture, needed by
    glTexCoordPointer, communicated to the server?

        RESOLVED: Send the active texture index using the <index>
        parameter normally used for indexed arrays.  The value of
        <index> will be an offset from GL_TEXTURE0.

        OTHER OPTIONS CONSIDERED: Add a separate parameter to
        VertexAttribPointer rather than alias the index parameter.
        Only advantage is slightly improved clarity.

        Add ClientActiveTexture protocol.  This would cause problems
        because the active texture is client state.  If two GLX processes
        were maintaining their own client state for one server-side
        context, it would be hard to reliably keep the active texture
        state in sync between the client and server.  It would need to be
        tracked per client on the server.

    How are Push/PopClientAttrib handled?

        RESOLVED: Specify protocol for these commands.  Some state
        affected by these commands needs to be duplicated in the server
        now, so these operations need to be duplicated there as well.

        OTHER OPTIONS CONSIDERED: Break the commands down into the
        individual commands required to perform the operation and send the
        appropriate protocol for those commands.  This is insufficient
        because it could cause deleted buffer objects to be destroyed by
        the server while they are still in use by a non-current entry in
        the client's attribute stack.

    How are integer arrays differentiated from floating point or fixed
    point arrays?

        RESOLVED: Added an "is_integer" boolean field to ARRAY_INFO and
        VertexAttribPointer.

    Is separate protocol needed for MapBuffer/UnmapBuffer?

        RESOLVED: Yes.  Buffers can only be mapped once.  Buffers need
        not be bound for the duration of their mapping, and whether a
        buffer is mapped or not is server state, so to properly track
        this state and generate related errors when there are multiple
        clients sharing the same buffer object, protocol must be sent
        to the server to duplicate the state there.

    Should the MapBuffer/UnmapBuffer protocol handle transferring of
    the buffer data when needed, or should implementations transfer the
    data using the BufferSubData and GetBufferSubData protocol?

        RESOLVED: Using the BufferSubData and GetBufferSubData protocol.
        This simplifies the Map and Unmap protocol and allows
        implementations to break up large data transfers into chunks.
        For example, the buffer data may be larger than the maximum size
        of a GLX single command.  Sending the data as part of the
        UnmapBuffer protocol would fail, but sending it in one or more
        BufferSubData commands would still be possible.  Implementations
        should take care to retreive the data, if needed, before mapping
        and send it back after unmapping.  This does not introduce race
        conditions because it is already up to the application to ensure
        proper mutexing of buffer object operations is done, and the
        protocol will still all happen within one application-visible GL
        command.

    Should client array data sent to the server in DrawArraysNew and
    DrawRangeElements be aligned?  Should individual arrays be separately
    aligned?  Should the arrays be sent be sent in order of largest
    element type to smallest?

        RESOLVED: No.  The client shall send the data as one contiguous
        array of bytes.  The server shall be responsible for aligning
        the individual entries as they are extracted if such alignment
        is needed.

    EXT_vertex_array allows enabling/disabling vertex arrays with
    Enable/Disable.  This state needs to be intercepted by the client
    to properly manage buffer object state.  Should protocol for Enable
    and Disable still be sent when these enums are used, or should the
    EnableClientState/DisableClientState protocol be sent instead?

        RESOLVED: Send the EnableClientState/DisableClientState protocol.
        The server may need to take additional actions for these special
        Enable/Disable enums.  Since the client already needs to
        intercept and handle them specially, keep the protocol separate
        for the server's benefit.

New Procedures and Functions

    void BindBufferARB(enum target, uint buffer);
    void DeleteBuffersARB(sizei n, const uint *buffers);
    void GenBuffersARB(sizei n, uint *buffers);
    boolean IsBufferARB(uint buffer);

    void BufferDataARB(enum target, sizeiptrARB size, const void *data,
                       enum usage);
    void BufferSubDataARB(enum target, intptrARB offset, sizeiptrARB size,
                          const void *data);
    void GetBufferSubDataARB(enum target, intptrARB offset,
                             sizeiptrARB size, void *data);

    void *MapBufferARB(enum target, enum access);
    boolean UnmapBufferARB(enum target);

    void GetBufferParameterivARB(enum target, enum pname, int *params);
    void GetBufferPointervARB(enum target, enum pname, void **params);

New Tokens

    Accepted as an attribute name in the <attrib_list> parameter of
    glXCreateContextAttribsARB:

        GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB 0x2095

    Accepted by the <target> parameters of BindBufferARB, BufferDataARB,
    BufferSubDataARB, MapBufferARB, UnmapBufferARB,
    GetBufferSubDataARB, GetBufferParameterivARB, and
    GetBufferPointervARB:

        ARRAY_BUFFER_ARB                             0x8892
        ELEMENT_ARRAY_BUFFER_ARB                     0x8893

    Accepted by the <pname> parameter of GetBooleanv, GetIntegerv,
    GetFloatv, and GetDoublev:

        ARRAY_BUFFER_BINDING_ARB                     0x8894
        ELEMENT_ARRAY_BUFFER_BINDING_ARB             0x8895
        VERTEX_ARRAY_BUFFER_BINDING_ARB              0x8896
        NORMAL_ARRAY_BUFFER_BINDING_ARB              0x8897
        COLOR_ARRAY_BUFFER_BINDING_ARB               0x8898
        INDEX_ARRAY_BUFFER_BINDING_ARB               0x8899
        TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB       0x889A
        EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB           0x889B
        SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB     0x889C
        FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB      0x889D
        WEIGHT_ARRAY_BUFFER_BINDING_ARB              0x889E

    Accepted by the <pname> parameter of GetVertexAttribivARB:

        VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB       0x889F

    Accepted by the <usage> parameter of BufferDataARB:

        STREAM_DRAW_ARB                              0x88E0
        STREAM_READ_ARB                              0x88E1
        STREAM_COPY_ARB                              0x88E2
        STATIC_DRAW_ARB                              0x88E4
        STATIC_READ_ARB                              0x88E5
        STATIC_COPY_ARB                              0x88E6
        DYNAMIC_DRAW_ARB                             0x88E8
        DYNAMIC_READ_ARB                             0x88E9
        DYNAMIC_COPY_ARB                             0x88EA

    Accepted by the <access> parameter of MapBufferARB:

        READ_ONLY_ARB                                0x88B8
        WRITE_ONLY_ARB                               0x88B9
        READ_WRITE_ARB                               0x88BA

    Accepted by the <pname> parameter of GetBufferParameterivARB:

        BUFFER_SIZE_ARB                              0x8764
        BUFFER_USAGE_ARB                             0x8765
        BUFFER_ACCESS_ARB                            0x88BB
        BUFFER_MAPPED_ARB                            0x88BC

    Accepted by the <pname> parameter of GetBufferPointervARB:

        BUFFER_MAP_POINTER_ARB                       0x88BD


Additions to Chapter 2 of the 1.4 Specification (OpenGL Operation)

    Add to Table 2.2:

       "GL Type        Minimum       Description
                       Bit Width
        -----------------------------------------------------------------
        intptrARB      <ptrbits>     signed 2's complement binary integer
        sizeiptrARB    <ptrbits>     Non-negative binary integer size"

    Add to the paragraph under Table 2.2:

    "<ptrbits> is the number of bits required to represent a pointer
    type; in other words, types intptrARB and sizeiptrARB must be
    sufficiently large as to store any address."

    Add a new section "Buffer Objects" between sections 2.8 and 2.9:

    "2.8A  Buffer Objects
     --------------------

    The vertex data arrays described in section 2.8 are stored in client
    memory.  It is sometimes desirable to store frequently used client
    data, such as vertex array data, in high-performance server memory.
    GL buffer objects provide a mechanism that clients can use to
    allocate, initialize, and render from such memory.

    The name space for buffer objects is the unsigned integers, with zero
    reserved for the GL.  A buffer object is created by binding an unused
    name to ARRAY_BUFFER_ARB.  The binding is effected by calling

        void BindBufferARB(enum target, uint buffer);

    with <target> set to ARRAY_BUFFER_ARB and <buffer> set to the unused
    name.  The resulting buffer object is a new state vector, initialized
    with a zero-sized memory buffer, and comprising the state values
    listed in Table BufObj1.

        Name                   Type         Initial Value   Legal Values
        ----                   ----         -------------   ------------
        BUFFER_SIZE_ARB        integer      0               any non-negative
                                                            integer
        BUFFER_USAGE_ARB       enum         STATIC_DRAW_ARB STREAM_DRAW_ARB,
                                                            STREAM_READ_ARB,
                                                            STREAM_COPY_ARB,
                                                            STATIC_DRAW_ARB,
                                                            STATIC_READ_ARB,
                                                            STATIC_COPY_ARB,
                                                            DYNAMIC_DRAW_ARB,
                                                            DYNAMIC_READ_ARB,
                                                            DYNAMIC_COPY_ARB
        BUFFER_ACCESS_ARB      enum         READ_WRITE_ARB  READ_ONLY_ARB,
                                                            WRITE_ONLY_ARB,
                                                            READ_WRITE_ARB
        BUFFER_MAPPED_ARB      boolean      FALSE           TRUE, FALSE
        BUFFER_MAP_POINTER_ARB void*        NULL            address

        Table BufObj1: Buffer object parameters and their values.

    BindBufferARB may also be used to bind an existing buffer object.
    If the bind is successful no change is made to the state of the
    newly bound buffer object, and any previous binding to <target> is
    broken.

    While a buffer object is bound, GL operations on the target to which
    it is bound affect the bound buffer object, and queries of the target
    to which a buffer object is bound return state from the bound object.

    In the initial state the GL-reserved name zero is bound to
    ARRAY_BUFFER_ARB.  There is no buffer object corresponding to the
    name zero, so client attempts to modify or query buffer object state
    for the target ARRAY_BUFFER_ARB while zero is bound will generate
    GL errors.

    Buffer objects are deleted by calling

        void DeleteBuffersARB(sizei n, const uint *buffers);

    <buffers> contains <n> names of buffer objects to be deleted.  After
    a buffer object is deleted it has no contents, and its name is again
    unused.  Unused names in <buffers> are silently ignored, as is the
    value zero.

    The command

        void GenBuffersARB(sizei n, uint *buffers);

    returns <n> previously unused buffer object names in <buffers>.
    These names are marked as used, for the purposes of GenBuffersARB
    only, but they acquire buffer state only when they are first bound,
    just as if they were unused.

    While a buffer object is bound, any GL operations on that object
    affect any other bindings of that object.  If a buffer object is
    deleted while it is bound, all bindings to that object in the current
    context (i.e. in the thread that called DeleteBuffers) are reset to
    bindings to buffer zero.  Bindings to that buffer in other contexts
    and other threads are not affected, but attempting to use a deleted
    buffer in another thread produces undefined results, including but
    not limited to possible GL errors and rendering corruption.  Using a
    deleted buffer in another context or thread may not, however, result
    in program termination.

    The data store of a buffer object is created and initialized by
    calling

        void BufferDataARB(enum target, sizeiptrARB size,
                           const void *data, enum usage);

    with <target> set to ARRAY_BUFFER_ARB, <size> set to the size of the
    data store in basic machine units, and <data> pointing to the
    source data in client memory.  If <data> is non-null, then the source
    data is copied to the buffer object's data store.  If <data> is null,
    then the contents of the buffer object's data store are undefined.

    <usage> is specified as one of nine enumerated values, indicating
    the expected application usage pattern of the data store.  The
    values are:

        STREAM_DRAW_ARB    The data store contents will be specified once
                           by the application, and used at most a few
                           times as the source of a GL (drawing) command.
        STREAM_READ_ARB    The data store contents will be specified once
                           by reading data from the GL, and queried at
                           most a few times by the application.
        STREAM_COPY_ARB    The data store contents will be specified once
                           by reading data from the GL, and used at most
                           a few times as the source of a GL (drawing)
                           command.
        STATIC_DRAW_ARB    The data store contents will be specified once
                           by the application, and used many times as the
                           source for GL (drawing) commands.
        STATIC_READ_ARB    The data store contents will be specified once
                           by reading data from the GL, and queried many
                           times by the application.
        STATIC_COPY_ARB    The data store contents will be specified once
                           by reading data from the GL, and used many
                           times as the source for GL (drawing) commands.
        DYNAMIC_DRAW_ARB   The data store contents will be respecified
                           repeatedly by the application, and used many
                           times as the source for GL (drawing) commands.
        DYNAMIC_READ_ARB   The data store contents will be respecified
                           repeatedly by reading data from the GL, and
                           queried many times by the application.
        DYNAMIC_COPY_ARB   The data store contents will be respecified
                           repeatedly by reading data from the GL, and
                           used many times as the source for GL (drawing)
                           commands.

    <usage> is provided as a performance hint only.  The specified usage
    value does not constrain the actual usage pattern of the data store.

    BufferDataARB deletes any existing data store, and sets the values of
    the buffer object's state variables to:

        Name                   Value
        ----                   -----
        BUFFER_SIZE_ARB        <size>
        BUFFER_USAGE_ARB       <usage>
        BUFFER_ACCESS_ARB      READ_WRITE_ARB
        BUFFER_MAPPED_ARB      FALSE
        BUFFER_MAP_POINTER_ARB NULL

    Clients must align data elements consistent with the requirements
    of the client platform, with an additional base-level requirement
    that an offset within a buffer to a datum comprising N basic machine
    units be a multiple of N.

    If the GL is unable to create a data store of the requested size,
    the error OUT_OF_MEMORY is generated.

    To modify some or all of the data contained in a buffer object's data
    store, the client may use the command

        void BufferSubDataARB(enum target, intptrARB offset,
                              sizeiptrARB size, const void *data);

    with <target> set to ARRAY_BUFFER_ARB.  <offset> and <size> indicate
    the range of data in the buffer object that is to be replaced, in
    terms of basic machine units.  <data> specifies a region of client
    memory <size> basic machine units in length, containing the data that
    replace the specified buffer range.  An error is generated if
    <offset> or <size> is less than zero, or if <offset> + <size> is
    greater than the value of BUFFER_SIZE_ARB.

    The entire data store of a buffer object can be mapped into the
    client's address space by calling

        void *MapBufferARB(enum target, enum access);

    with <target> set to ARRAY_BUFFER_ARB.  If the GL is able to map the
    buffer object's data store into the client's address space,
    MapBufferARB returns the pointer value to the data store.  Otherwise
    MapBufferARB returns NULL, and the error OUT_OF_MEMORY is generated.
    <access> is specified as one of READ_ONLY_ARB, WRITE_ONLY_ARB, or
    READ_WRITE_ARB, indicating the operations that the client may perform
    on the data store through the pointer while the data store is mapped.

    MapBufferARB sets the following buffer object state values:

        Name                   Value
        ----                   -----
        BUFFER_ACCESS_ARB      <access>
        BUFFER_MAPPED_ARB      TRUE
        BUFFER_MAP_POINTER_ARB pointer to the data store

    It is an INVALID_OPERATION error to map a buffer data store that is
    in the mapped state.

    Non-null pointers returned by MapBufferARB may be used by the client
    to modify and query buffer object data, consistent with the access
    rules of the mapping, while the mapping remains valid.  No GL error
    is generated if the pointer is used to attempt to modify a
    READ_ONLY_ARB data store, or to attempt to read from a WRITE_ONLY_ARB
    data store, but operation may be slow and system errors (possibly
    including program termination) may result.  Pointer values returned
    by MapBufferARB may not be passed as parameter values to GL commands.
    For example, they may not be used to specify array pointers, or to
    specify or query pixel or texture image data; such actions produce
    undefined results, although implementations may not check for such
    behavior for performance reasons.

    It is an INVALID_OPERATION error to call BufferSubDataARB to modify
    the data store of a mapped buffer.

    Mappings to the data stores of buffer objects may have nonstandard
    performance characteristics.  For example, such mappings may be
    marked as uncacheable regions of memory, and in such cases reading
    from them may be very slow.  To ensure optimal performance, the
    client should use the mapping in a fashion consistent with the values
    of BUFFER_USAGE_ARB and BUFFER_ACCESS_ARB.  Using a mapping in a
    fashion inconsistent with these values is liable to be multiple
    orders of magnitude slower than using normal memory.

    After the client has specified the contents of a mapped data store,
    and before the data in that store are dereferenced by any GL commands,
    the mapping must be relinquished by calling

        boolean UnmapBufferARB(enum target);

    with <target> set to ARRAY_BUFFER_ARB.  Unmapping a mapped buffer
    object invalidates the pointers to its data store and sets the
    object's BUFFER_MAPPED_ARB state to FALSE and its
    BUFFER_MAP_POINTER_ARB state to NULL.

    UnmapBufferARB returns TRUE unless data values in the buffer's data
    store have become corrupted during the period that the buffer was
    mapped.  Such corruption can be the result of a screen resolution
    change or other window-system-dependent event that causes system
    heaps such as those for high-performance graphics memory to be
    discarded.  GL implementations must guarantee that such corruption
    can occur only during the periods that a buffer's data store is
    mapped.  If such corruption has occurred, UnmapBufferARB returns
    FALSE, and the contents of the buffer's data store become undefined.

    It is an INVALID_OPERATION error to explicitly unmap a buffer data
    store that is in the unmapped state.  Unmapping that occurs as a side
    effect of buffer deletion or reinitialization is not an error,
    however."


    2.8A.1 Vertex Arrays in Buffer Objects
    --------------------------------------

    Blocks of vertex array data may be stored in buffer objects with the
    same format and layout options supported for client-side vertex
    arrays.  However, it is expected that GL implementations will (at
    minimum) be optimized for data with all components represented as
    floats, as well as for color data with components represented as
    either floats or unsigned bytes.

    A buffer object binding point is added to the client state associated
    with each vertex array type.  The client does not directly specify
    the bindings to with these new binding points.  Instead, the commands
    that specify the locations and organizations of vertex arrays
    copy the buffer object name that is bound to ARRAY_BUFFER_ARB to the
    binding point corresponding to the vertex array of the type being
    specified.  For example, the NormalPointer command copies the value
    of ARRAY_BUFFER_BINDING_ARB (the queriable name of the buffer binding
    corresponding to the target ARRAY_BUFFER_ARB) to the client state
    variable NORMAL_ARRAY_BUFFER_BINDING_ARB.

    If EXT_vertex_shader is defined, then the command
    VariantArrayEXT(uint id, ...) copies the value of
    ARRAY_BUFFER_BINDING_ARB to the buffer object binding point
    corresponding to variant array <id>.

    If ARB_vertex_program is defined, then the command
    VertexAttribPointerARB(int attrib, ...) copies the value of
    ARRAY_BUFFER_BINDING_ARB to the buffer object binding point
    corresponding to vertex attrib array <attrib>.

    If ARB_vertex_blend is defined, then the command WeightPointerARB
    copies the value of ARRAY_BUFFER_BINDING_ARB to
    WEIGHT_ARRAY_BUFFER_BINDING_ARB.

    Rendering commands ArrayElement, DrawArrays, DrawElements,
    DrawRangeElements, MultiDrawArrays, and MultiDrawElements operate as
    previously defined, except that data for enabled vertex, variant, and
    attrib arrays are sourced from buffers if the array's buffer binding
    is non-zero.  When an array is sourced from a buffer object, the
    pointer value of that array is used to compute an offset, in basic
    machine units, into the data store of the buffer object.  This offset
    is computed by subtracting a null pointer from the pointer value,
    where both pointers are treated as pointers to basic machine units.

    It is acceptable for vertex, variant, or attrib arrays to be sourced
    from any combination of client memory and various buffer objects
    during a single rendering operation.

    It is an INVALID_OPERATION error to source data from a buffer object
    that is currently mapped.


    2.8B.1 Array Indices in Buffer Objects
    ----------------------------------------------

    Blocks of array indices may be stored in buffer objects with the
    same format options that are supported for client-side index arrays.
    Initially zero is bound to ELEMENT_ARRAY_BUFFER_ARB, indicating that
    DrawElements and DrawRangeElements are to source their indices from
    arrays passed as their <indices> parameters, and that
    MultiDrawElements is to source its indices from the array of pointers
    to arrays passed in as its <indices> parameter.

    A buffer object is bound to ELEMENT_ARRAY_BUFFER_ARB by calling

        void BindBufferARB(enum target, uint buffer);

    with <target> set to ELEMENT_ARRAY_BUFFER_ARB, and <buffer> set to
    the name of the buffer object.  If no corresponding buffer object
    exists, one is initialized as defined in Section 2.8A.

    The commands BufferDataARB, BufferSubDataARB, MapBufferARB, and
    UnmapBufferARB may all be used with <target> set to
    ELEMENT_ARRAY_BUFFER_ARB.  In such event, these commands operate in
    the same fashion as described in section 2.8A, but on the buffer
    currently bound to the ELEMENT_ARRAY_BUFFER_ARB target.

    While a non-zero buffer object name is bound to
    ELEMENT_ARRAY_BUFFER_ARB, DrawElements and DrawRangeElements source
    their indices from that buffer object, using their <indices>
    parameters as offsets into the buffer object in the same fashion as
    described in section 2.8A1.  MultiDrawElements also sources its
    indices from that buffer object, using its <indices> parameter as a
    pointer to an array of pointers that represent offsets into the
    buffer object.

    Buffer objects created by binding an unused name to ARRAY_BUFFER_ARB
    and to ELEMENT_ARRAY_BUFFER_ARB are formally equivalent, but the GL
    may make different choices about storage implementation based on
    the initial binding.  In some cases performance will be optimized
    by storing indices and array data in separate buffer objects, and by
    creating those buffer objects with the corresponding binding points."


Additions to Chapter 3 of the 1.4 Specification (Rasterization)

    None


Additions to Chapter 4 of the 1.4 Specification (Per-Fragment
Operations and the Frame Buffer)

    None


Additions to Chapter 5 of the 1.4 Specification (Special Functions)

    Added to section 5.4, as part of the discussion of what commands
    are compiled into display lists:

    "Commands that are used to create, manage, and query buffer objects
    are not included in display lists, but are executed immediately.
    These commands are BindBufferARB, DeleteBuffersARB, GenBuffersARB,
    IsBufferARB, BufferDataARB, BufferSubDataARB, MapBufferARB,
    UnmapBufferARB, GetBufferParameterivARB, GetBufferSubDataARB,
    and GetBufferPointervARB.

    GL commands that source data from buffer objects dereference the
    buffer object data in question at display list compile time, rather
    than encoding the buffer ID and buffer offset into the display list.
    Only GL commands that are executed immediately, rather than being
    compiled into a display list, are permitted to use a buffer object as
    a data sink."


Additions to Chapter 6 of the 1.4 Specification (State and State
Requests)

    Added to section 6.1 in a subsection titled Buffer Object Queries:

    "The command

        boolean IsBufferARB(uint buffer);

    returns TRUE if <buffer> is the name of a buffer object. If
    <buffer> is zero, or if <buffer> is a non-zero value that is not
    the name of a buffer object, IsBufferARB return FALSE.

    The command

        void GetBufferSubDataARB(enum target, intptrARB offset,
                                 sizeiptrARB size, void *data);

    queries the data contents of a buffer object.  <target> is
    ARRAY_BUFFER_ARB.  <offset> and <size> indicate the range of data
    in the buffer object that is to be queried, in terms of basic machine
    units.  <data> specifies a region of client memory, <size> basic
    machine units in length, into which the data is to be retrieved.

    An error is generated if GetBufferSubDataARB is executed for a buffer
    object that is currently mapped.

    While the data store of a buffer object is mapped, the pointer to
    the data store can be queried by calling

        void GetBufferPointervARB(enum target, enum pname, void **params);

    with <target> set to ARRAY_BUFFER_ARB and <pname> set to
    BUFFER_MAP_POINTER_ARB.  The single buffer map pointer is returned
    in <params>.  GetBufferPointervARB returns the NULL pointer value if
    the buffer's data store is not currently mapped, or if the requesting
    client did not map the buffer object's data store, and the
    implementation is unable to support mappings on multiple clients."

    Added to the list of queries in section 6.1.3, Enumerated Queries:

        "void GetBufferParameterivARB(enum target, enum pname, int *params);"


Errors

    INVALID_ENUM is generated if the <target> parameter of BindBufferARB,
    BufferDataARB, BufferSubDataARB, MapBufferARB, UnmapBufferARB,
    GetBufferSubDataARB, GetBufferParameterivARB, or GetBufferPointervARB
    is not ARRAY_BUFFER_ARB or ELEMENT_ARRAY_BUFFER_ARB.

    INVALID_VALUE is generated if the <n> parameter of DeleteBuffersARB or
    GenBuffersARB is negative.

    INVALID_VALUE is generated if the <size> parameter of BufferDataARB,
    BufferSubDataARB, or GetBufferSubDataARB is negative.

    INVALID_OPERATION is generated if BufferDataARB, BufferSubDataARB,
    MapBufferARB, UnmapBufferARB, GetBufferSubDataARB,
    GetBufferParameterivARB, or GetBufferPointervARB is executed while
    zero is bound to the <target> parameter.

    OUT_OF_MEMORY may be generated if the data store of a buffer object
    cannot be allocated because the <size> argument of BufferDataARB is
    too large.

    OUT_OF_MEMORY may be generated when MapBufferARB is called if the
    data store of the buffer object in question cannot be mapped.  This
    may occur for a variety of system-specific reasons, such as the
    absence of sufficient remaining virtual memory.

    INVALID_ENUM is generated if the <usage> parameter of BufferDataARB is
    not STREAM_DRAW_ARB, STREAM_READ_ARB, STREAM_COPY_ARB, STATIC_DRAW_ARB,
    STATIC_READ_ARB, STATIC_COPY_ARB, DYNAMIC_DRAW_ARB, DYNAMIC_READ_ARB,
    or DYNAMIC_COPY_ARB.

    INVALID_VALUE is generated if the <offset> parameter to BufferSubDataARB
    or GetBufferSubDataARB is negative.

    INVALID_VALUE is generated if the <offset> and <size> parameters of
    BufferSubDataARB or GetBufferSubDataARB define a region of memory that
    extends beyond that allocated by BufferDataARB.

    INVALID_OPERATION is generated if MapBufferARB is executed for a
    buffer that is already mapped.

    INVALID_OPERATION is generated if UnmapBufferARB is executed for a
    buffer that is not currently mapped.

    INVALID_ENUM is generated if the <access> parameter of MapBufferARB
    is not READ_ONLY_ARB, WRITE_ONLY_ARB, or READ_WRITE_ARB.

    INVALID_ENUM is generated if the <pname> parameter of
    GetBufferParameterivARB is not BUFFER_SIZE_ARB, BUFFER_USAGE_ARB,
    BUFFER_ACCESS_ARB, or BUFFER_MAPPED_ARB.

    INVALID_ENUM is generated if the <pname> parameter of
    GetBufferPointervARB is not BUFFER_MAP_POINTER_ARB.

    INVALID_OPERATION may be generated if any of the commands
    defined in this extension is executed between the execution of Begin
    and the corresponding execution of End.

    INVALID_OPERATION is generated if a buffer object that is currently
    mapped is used as a source of GL render data, or as a destination of
    GL query data.

    INVALID_OPERATION is generated if BufferSubDataARB is used to modify
    the data store contents of a mapped buffer, or if GetBufferSubDataARB
    is used to query to data store contents of a mapped buffer.


New State

(table 6.7, Vertex Array Data, p. 222)

    Get Value                                 Type      Get Command              Initial Value   Sec     Attribute
    ---------                                 ----      -----------              -------------   ---     ---------
    ARRAY_BUFFER_BINDING_ARB                  Z+        GetIntegerv              0               2.8A    vertex-array
    VERTEX_ARRAY_BUFFER_BINDING_ARB           Z+        GetIntegerv              0               2.8A    vertex-array
    NORMAL_ARRAY_BUFFER_BINDING_ARB           Z+        GetIntegerv              0               2.8A    vertex-array
    COLOR_ARRAY_BUFFER_BINDING_ARB            Z+        GetIntegerv              0               2.8A    vertex-array
    INDEX_ARRAY_BUFFER_BINDING_ARB            Z+        GetIntegerv              0               2.8A    vertex-array
    TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB    Z+        GetIntegerv              0               2.8A    vertex-array
    EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB        Z+        GetIntegerv              0               2.8A    vertex-array
    SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB  Z+        GetIntegerv              0               2.8A    vertex-array
    FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB   Z+        GetIntegerv              0               2.8A    vertex-array
    WEIGHT_ARRAY_BUFFER_BINDING_ARB           Z+        GetIntegerv              0               2.8A    vertex-array
    ELEMENT_ARRAY_BUFFER_BINDING_ARB          Z+        GetIntegerv              0               2.8A.2  vertex-array

    VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB    16+ x Z+  GetVertexAttribivARB     0               2.8A    vertex-array

    XXX need to add buffer state for variant arrays

(new table for buffer objects)

    Get Value                   Type    Get Command                  Initial Value   Sec     Attribute
    ---------                   ----    -----------                  -------------   ---     ---------
    (buffer data)               BMU     GetBufferSubDataARB                          2.8A    none
    BUFFER_SIZE_ARB             Z+      GetBufferParameterivARB      0               2.8A    none
    BUFFER_USAGE_ARB            Z9      GetBufferParameterivARB      STATIC_DRAW_ARB 2.8A    none
    BUFFER_ACCESS_ARB           Z3      GetBufferParameterivARB      READ_WRITE_ARB  2.8A    none
    BUFFER_MAPPED_ARB           B       GetBufferParameterivARB      FALSE           2.8A    none
    BUFFER_MAP_POINTER_ARB      Y       GetBufferPointervARB         NULL            2.8A    none


New Implementation Dependent State

    (none)

Additions to the GLX Specification

    Add new section "2.X Buffer Objects" between sections "2.5 Texture Objects"
    and "2.6 Aligning Multiple Drawables".

    "Unformatted data may be stored in the OpenGL server using named buffer
    objects.  The storage associated with a buffer object may be used as the
    input or output of various commands by binding the buffer to a target
    associated with the command.  For example, if a buffer object is bound to
    the GL_ARRAY_BUFFER_ARB target before calling glVertexPointer vertex
    data will be sourced from the buffer object's storage.

    "Buffer objects may be shared by rendering contexts, as long as the server
    portion of the contexts share the same address space.  (Like display lists
    and texture objects, buffer objects are part of the server context state.)
    OpenGL makes no attempt to synchronize access to buffer objects.  If a
    buffer object is bound to more than one context, then it is up to the
    programmer to ensure that the contents of the object are not being changed
    via one context while another context is using the buffer object for
    rendering.  The results of changing a buffer object while another context
    is using it are undefined.

    "A buffer object is considered to be bound to a context if it is either
    currently bound to a particular target (e.g., most recently selected for a
    target via glBindBufferARB) or if some portion of the buffer will be
    accessed by a GL operation.  For example, a buffer object is considered
    bound to a context if the most recent call of glVertexPointer was made
    while the buffer object was bound to the GL_ARRAY_BUFFER_ARB target.

    "As a result, any client-state that attaches the contents of a buffer object
    to the GL state must be duplicated on the server.  At a minimum the
    following client-state must be replicated on the server:

        - All vertex array size, type, stride and pointer information.
        - All vertex array enables.

    "All modifications to shared context state as a result of executing
    glBindBufferARB are atomic.  Also, a buffer object will not be deleted
    until it is no longer bound to any rendering context."

    If GLX_ARB_create_context is present, add the following between
    paragraphs 8 and 9 of section 3.3.7 "Rendering Contexts":

    "The attribute name GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB
    specifies whether the application wishes to use buffer objects even
    if the byte ordering of the client and server do not match.  If this
    attribute is True the client is responsible for byte swapping any data
    it stores in or reads from buffer objects.  If this attribute is False
    and the client and server have different byte orderings, the server
    will not expose any extensions that require buffer object support and
    the maximum values allowed for GLX_CONTEXT_MAJOR_VERSION_ARB and
    GLX_CONTEXT_MINOR_VERSION_ARB are 1 and 4 respectively.  The default
    value for GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB is False."

    Add to the following paragraph to the end of section 4.3:

    "Because the GL views buffer objects as unformatted data, the above byte
    swapping rules do not apply to them.  Data to be stored in buffer objects
    must be byte swapped by the application to match the server's byte ordering
    before the data is presented to the GL."

GLX Protocol

    The following rendering commands are sent to the server as part of a
    glXRender request:

        BindBufferARB
            2           12              rendering command length
            2           290             rendering command opcode
            4           ENUM            target
            4           CARD32          buffer

        VertexAttribPointer
            2           36              rendering command length
            2           291             rendering command opcode
            8           CARD64          offset
            4           ENUM            array
            4           CARD32          index
            4           ENUM            type
            4           CARD32          size
            4           CARD32          stride
            1           BOOL            is_integer
            1           BOOL            normalized
            2                           unused

        EnableVertexAttribArray
            2           12              rendering command length
            2           292             rendering command opcode
            4           ENUM            array
            4           CARD32          index

        DisableVertexAttribArray
            2           12              rendering command length
            2           293             rendering command opcode
            4           ENUM            array
            4           CARD32          index

        ArrayElement
            2           8               rendering command length
            2           294             rendering command opcode
            4           CARD32          index

        DrawElements
            2           24               rendering command length
            2           295              rendering command opcode
            8           CARD64           indices_offset
            4           ENUM             mode (GL_POINTS, etc.)
            4           CARD32           count
            4           CARD32           type

        PushClientAttrib
            2           8                rendering command length
            2           296              rendering command opcode
            4           CARD32           mask

        PopClientAttrib
            2           4                rendering command length
            2           297              rendering command opcode

    The following rendering commands are sent to the server as part of a
    glXRender request, or as a glXRenderLarge request.

        BufferDataARB
            2           24+n+p          rendering command length
            2           298             rendering command opcode
            8           INT64           size
            4           ENUM            target
            4           ENUM            usage
            1           BOOL            null_data
            3                           unused
            n           LISTofBYTE      data
            p                           unused, p=pad(n)

        If the command is encoded in a glXRenderLarge request, the command
        opcode and command length fields are expanded to 4 bytes each.

            4           28+n+p          rendering command length
            4           298             rendering command opcode

        BufferSubDataARB
            2           24+n+p          rendering command length
            2           299             rendering command opcode
            8           CARD64          offset
            8           CARD64          size
            4           ENUM            target
            n           LISTofBYTE      data, where n = size
            p                           unused, p=pad(n)

        If the command is encoded in a glXRenderLarge request, the command
        opcode and command length fields are expanded to 4 bytes each.

            4           28+n+p          rendering command length
            4           299             rendering command opcode

        DrawArraysNew
            2           20+(20*m)+n+p    rendering command length
            2           300              rendering command opcode
            4           ENUM             mode (GL_POINTS, etc.)
            4           CARD32           first
            4           CARD32           count
            4           CARD32           m
            20*m        LISTofARRAY_INFO client_array_info
            n           LISTofBYTE       client_arrays
            p                            unused, p=pad(n)

        If the command is encoded in a glXRenderLarge request, the command
        opcode and command length fields are expanded to 4 bytes each.

            4           24+(20*m)+n+p   rendering command length
            4           300             rendering command opcode

        Where <m> is the number of enabled vertex arrays that do not have
        buffer objects bound to them, <n> is <count> * <s>, and <s> is the
        sum of all client array element sizes, as defined below.

        ARRAY_INFO
            4           ENUM             data type
                        0x1400 i=1       GL_BYTE
                        0x1401 i=1       GL_UNSIGNED_BYTE
                        0x1402 i=2       GL_SHORT
                        0x1403 i=2       GL_UNSIGNED_SHORT
                        0x1404 i=4       GL_INT
                        0x1405 i=4       GL_UNSIGNED_INT
                        0x1406 i=4       GL_FLOAT
                        0x140A i=8       GL_DOUBLE
                        0x140B i=2       GL_HALF_FLOAT
            4           CARD32           j (number of values in array element)
            4           ENUM             array type
                        0x8074 j=2/3/4   VERTEX_ARRAY
                        0x8075 j=3       NORMAL_ARRAY
                        0x8076 j=3/4     COLOR_ARRAY
                        0x8077 j=1       INDEX_ARRAY
                        0x8078 j=1/2/3/4 TEXTURE_COORD_ARRAY
                        0x8079 j=1       EDGE_FLAG_ARRAY
                        0x8457 j=1       FOG_COORD_ARRAY
                        0x845E j=3       SECONDARY_COLOR_ARRAY
                        0x0000 j=1/2/3/4 VERTEX_ATTRIB_ARRAY
                        0x86AD j>=1      WEIGHT_ARRAY_ARB
                        0x8844 j>=1      MATRIX_INDEX_ARRAY_ARB
                        0x850C j=1       VERTEX_WEIGHT_ARRAY_EXT
                        0x8768 j=1       ELEMENT_ARRAY_ATI
            4           CARD32           index
            1           BOOL             normalized
            1           BOOL             is_integer
            2                            unused

        For each client array, the client array element size is <i>*<j>.
        The <index> field is the generic attribute array index when
        <array type> is VERTEX_ATTRIB_ARRAY and the client active texture
        unit's offset from GL_TEXTURE0 when <array_type> is
        <TEXTURE_COORD_ARRAY>.  For all other values of <array type>,
        <index> must be 0.

        The DrawArraysNew protocol differs from the DrawArrays protocol
        defined in EXT_vertex_array in the following ways:

          1) The ARRAY_INFO structure has been expanded to include
             <index>, <normalized>, and <is_integer>.

          2) ARRAY_INFO and associated array data are only sent for arrays
             that do not have buffer objects bound to them.

          3) The data for each array is sent contiguously rather than
             interleaved with the other arrays.

          4) The array data is tightly packed.  Rather than containing
             padding after each array, a single pad field is added on the
             end.

        DrawRangeElements
            2           36+(20*m)+n+p    rendering command length
            2           301              rendering command opcode
            8           CARD64           indices_offset
            4           ENUM             mode (GL_POINTS, etc.)
            4           CARD32           start
            4           CARD32           end
            4           CARD32           count
            4           CARD32           type
            4           CARD32           m
            20*m        LISTofARRAY_INFO client_array_info
            n           LISTofBYTE       client_arrays
            p                            unused, p=pad(n)

        If the command is encoded in a glXRenderLarge request, the command
        opcode and command length fields are expanded to 4 bytes each.

            4           40+(20*m)+n+p    rendering command length
            4           301              rendering command opcode

        Where <n> is defined as it is for DrawArraysNew.

    The remaining commands are non-rendering commands.  These commands are
    sent separately (i.e., not as part of a glXRender or glXRenderLarge
    request), using glx single requests:

        DeleteBuffersARB
            1           CARD8           opcode (X assigned)
            1           186             GLX opcode
            2           3+n             request length
            4           GLX_CONTEXT_TAG context tag
            4           CARD32          n
            n*4         LISTofCARD32    ids

        GenBuffersARB
            1           CARD8           opcode (X assigned)
            1           187             GLX opcode
            2           3               request length
            4           GLX_CONTEXT_TAG context tag
            4           CARD32          n
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           n               reply length
            24                          unused
            n*4         LISTofCARD32    buffers

        IsBufferARB
            1           CARD8           opcode (X assigned)
            1           188             GLX opcode
            2           3               request length
            4           GLX_CONTEXT_TAG context tag
            4           CARD32          id
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           0               reply length
            4           BOOL32          return value
            20                          unused

        GetBufferSubDataARB
            1           CARD8           opcode (X assigned)
            1           189             GLX opcode
            2           7               request length
            4           GLX_CONTEXT_TAG context tag
            8           CARD64          offset
            8           CARD64          size
            4           ENUM            target
          =>
            1           1               reply
            1           1               unused
            2           CARD16          sequence number
            4           m               reply length, m = (n + p) / 4
            4                           unused
            4           CARD32          n
            16                          unused
            n           LISTofBYTE      buffer subdata
            p                           unused, p=pad(n)

        GetBufferParameterivARB
            1           CARD8           opcode (X assigned)
            1           190             GLX opcode
            2           4               request length
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           ENUM            pname
          =>
            1           1               reply
            1           1               unused
            2           CARD16          sequence number
            4           m               reply length, m = (n == 1 ? 0 : n)
            4                           unused
            4           CARD32          n (number of parameter components)

            if (n == 1) this follows:

            4           CARD32          params
            12                          unused

            otherwise this follows:

            16                          unused
            4*n         LISTofCARD32    params

            Note that n may be 0, indicating that a GL error occurred.

        GetElementRange
            1           CARD8           opcode (X assigned)
            1           191             GLX opcode
            2           6               request length
            4           GLX_CONTEXT_TAG context tag
            8           CARD64          offset
            4           ENUM            type
            4           INT32           count
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           0               reply length
            8                           unused
            4           CARD32          lowest element
            4           CARD32          highest element
            8                           unused

         GetBufferPointerv
            1           CARD8           opcode (X assigned)
            1           192             GLX opcode
            2           4               request length
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m = (n==1 ? 0 : n*2)
            4                           unused
            4           CARD32          n

            if (n=1) this follows:

            8           CARD64          params
            8                           unused

            otherwise this follows:

            16                          unused
            n*8         LISTofCARD64    params

            Note that n may be 0, indicating that a GL error occurred.

         MapBuffer
            1           CARD8           opcode (X assigned)
            1           193             GLX opcode
            2           4               request length
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           ENUM            access
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           0               reply length
            4                           unused
            4           CARD32          0 on error, otherwise 1.
            8           CARD64          mapping address
            8                           unused

         UnmapBuffer
            1           CARD8           opcode (X assigned)
            1           194             GLX opcode
            2           3               request length
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           0               reply length
            4           CARD32          return value
           20                           unused

Usage Examples

    These examples illustrate various usages.  In all cases a rendering
    loop is included, and array parameters are initialized inside the
    loop as would be required if multiple array rendering operations
    were performed in the loops.  (Though only one operation is shown.)

    Convenient macro definition for specifying buffer offsets:

        #define BUFFER_OFFSET(i) ((char *)NULL + (i))

    Traditional vertex arrays:

        // Create system memory buffer
        data = malloc(320);

        // Fill system memory buffer
        ...

        // Frame rendering loop
        while (...) {

            // Define arrays
            VertexPointer(4, FLOAT, 0, data);
            ColorPointer(4, UNSIGNED_BYTE, 0, data+256);

            // Enable arrays
            EnableClientState(VERTEX_ARRAY);
            EnableClientState(COLOR_ARRAY);

            // Draw arrays
            DrawArrays(TRIANGLE_STRIP, 0, 16);

            // Disable arrays
            DisableClientState(VERTEX_ARRAY);
            DisableClientState(COLOR_ARRAY);

            // Other rendering commands
            ...

        }

        // Free system memory buffer
        free(data);


    Vertex arrays using a buffer object:

        // Create system memory buffer
        data = malloc(320);

        // Fill system memory buffer
        ...

        // Create buffer object
        BindBufferARB(ARRAY_BUFFER_ARB, 1);

        // Initialize data store of buffer object
        BufferDataARB(ARRAY_BUFFER_ARB, 320, data, STATIC_DRAW_ARB);

        // Free system memory buffer
        free(data);

        // Frame rendering loop
        while (...) {

            // Define arrays
            BindBufferARB(ARRAY_BUFFER_ARB, 1);
            VertexPointer(4, FLOAT, 0, BUFFER_OFFSET(0));
            ColorPointer(4, UNSIGNED_BYTE, 0, BUFFER_OFFSET(256));

            // Enable arrays
            EnableClientState(VERTEX_ARRAY);
            EnableClientState(COLOR_ARRAY);

            // Draw arrays
            DrawArrays(TRIANGLE_STRIP, 0, 16);

            // Disable arrays
            DisableClientState(VERTEX_ARRAY);
            DisableClientState(COLOR_ARRAY);

            // Other rendering commands
            ...

        }

        // Delete buffer object
        int buffer[1] = {1};
        DeleteBuffersARB(1, buffer);


    Code that works with and without buffer objects:

        // Create system memory buffer
        data = malloc(320);

        // Fill system memory buffer
        ...

        // Initialize buffer object, and null the data pointer
#ifdef USE_BUFFER_OBJECTS
        BindBufferARB(ARRAY_BUFFER_ARB, 1);
        BufferDataARB(ARRAY_BUFFER_ARB, 320, data, STATIC_DRAW_ARB);
        free(data);
        data = NULL;
#endif

        // Frame rendering loop
        while (...) {

            // Define arrays
#ifdef USE_BUFFER_OBJECTS
            BindBufferARB(ARRAY_BUFFER_ARB, 1);
#endif
            VertexPointer(4, FLOAT, 0, data);
            ColorPointer(4, UNSIGNED_BYTE, 0, data+256);

            // Enable arrays
            EnableClientState(VERTEX_ARRAY);
            EnableClientState(COLOR_ARRAY);

            // Draw arrays
            DrawArrays(TRIANGLE_STRIP, 0, 16);

            // Disable arrays
            DisableClientState(VERTEX_ARRAY);
            DisableClientState(COLOR_ARRAY);

            // Other rendering commands
            ...

        }

        // Delete buffer object
#ifdef USE_BUFFER_OBJECTS
        int buffer[1] = {1};
        DeleteBuffersARB(1, buffer);
#else
        // Free system memory buffer
        free(data);
#endif


    Vertex arrays using a mapped buffer object:

        // Frame rendering loop
        while (...) {

            // Define arrays (and create buffer object in first pass)
            BindBufferARB(ARRAY_BUFFER_ARB, 1);
            VertexPointer(4, FLOAT, 0, BUFFER_OFFSET(0));
            ColorPointer(4, UNSIGNED_BYTE, 0, BUFFER_OFFSET(256));

            // Enable arrays
            EnableClientState(VERTEX_ARRAY);
            EnableClientState(COLOR_ARRAY);

            // Initialize data store of buffer object
            BufferDataARB(ARRAY_BUFFER_ARB, 320, NULL, STREAM_DRAW_ARB);

            // Map the buffer object
            float *p = MapBufferARB(ARRAY_BUFFER_ARB, WRITE_ONLY);

            // Compute and store data in mapped buffer object
            ...

            // Unmap buffer object and draw arrays
            if (UnmapBufferARB(ARRAY_BUFFER_ARB)) {
                DrawArrays(TRIANGLE_STRIP, 0, 16);
            }

            // Disable arrays
            DisableClientState(VERTEX_ARRAY);
            DisableClientState(COLOR_ARRAY);

            // Other rendering commands
            ...

        }

        // Delete buffer object
        int buffer[1] = {1};
        DeleteBuffersARB(1, buffer);


    Vertex arrays using a mapped buffer object for array data and an
    unmapped buffer object for indices:

        // Create system memory buffer for indices
        indexdata = malloc(400);

        // Fill system memory buffer with 100 indices
        ...

        // Create index buffer object
        BindBufferARB(ELEMENT_ARRAY_BUFFER_ARB, 2);
        BufferDataARB(ELEMENT_ARRAY_BUFFER_ARB, 400, indexdata,
                STATIC_DRAW_ARB);

        // Free system memory buffer
        free(indexdata);

        // Frame rendering loop
        while (...) {

            // Define arrays (and create buffer object in first pass)
            BindBufferARB(ARRAY_BUFFER_ARB, 1);
            VertexPointer(4, FLOAT, 0, BUFFER_OFFSET(0));
            ColorPointer(4, UNSIGNED_BYTE, 0, BUFFER_OFFSET(256));
            BindBufferARB(ELEMENT_ARRAY_BUFFER_ARB, 2);

            // Enable arrays
            EnableClientState(VERTEX_ARRAY);
            EnableClientState(COLOR_ARRAY);

            // Initialize data store of buffer object
            BufferDataARB(ARRAY_BUFFER_ARB, 320, NULL, STREAM_DRAW_ARB);

            // Map the buffer object
            float *p = MapBufferARB(ARRAY_BUFFER_ARB, WRITE_ONLY);

            // Compute and store data in mapped buffer object
            ...

            // Unmap buffer object and draw arrays
            if (UnmapBufferARB(ARRAY_BUFFER_ARB)) {
                DrawElements(TRIANGLE_STRIP, 100, UNSIGNED_INT,
                             BUFFER_OFFSET(0));
            }

            // Disable arrays
            DisableClientState(VERTEX_ARRAY);
            DisableClientState(COLOR_ARRAY);

            // Other rendering commands
            ...

        }

        // Delete buffer objects
        int buffers[2] = {1, 2};
        DeleteBuffersARB(1, buffers);


    Mapping multiple buffers simultaneously:

        // Map buffers
        BindBuffer(ARRAY_BUFFER_ARB, 1);
        float *a = MapBuffer(ARRAY_BUFFER_ARB, WRITE_ONLY);
        BindBuffer(ARRAY_BUFFER_ARB, 2);
        float *b = MapBuffer(ARRAY_BUFFER_ARB, WRITE_ONLY);

        // Fill buffers
        ...

        // Unmap buffers
        BindBuffer(ARRAY_BUFFER_ARB, 1);
        if (!UnmapBufferARB(ARRAY_BUFFER_ARB)) {
            // Handle error case
        }
        BindBuffer(ARRAY_BUFFER_ARB, 2);
        if (!UnmapBufferARB(ARRAY_BUFFER_ARB)) {
            // Handle error case
        }

    Revision  Date         Author  Changes
    --------  -----------  ------  ---------------------------------------
    0.99.7    18-Jan-2012  mgodse  Fixed typos.

    0.99.6    25-Oct-2010  jrj     Fixed 'context tag' location in
                                   GetBufferSubDataARB GLX protocol.

    0.99.5    14-Apr-2010  Jon Leech Added ARB suffix to GLX token.

    0.99.4    07-Apr-2010  jrj     Added the GLX_ARB_vertex_buffer_object
                                   string to identify GLX implementations
                                   that support the new context attribute.

    0.99.3    10-Feb-2010  jrj     Fixed GetBufferSubData reply layout and
                                   cleaned up the field descriptions.
                                   Added GetBufferPointerv protocol.
                                   Added an issue describing why the
                                   MapBuffer and UnmapBuffer protocol do
                                   not transmit buffer data.

    0.99.2    02-Feb-2010  jrj     Removed <n> field from DrawArraysNew
                                   and DrawRangeElements.  Its value can
                                   be computed from <m> and the data in
                                   <client_array_info>.
                                   Added more language describing
                                   DrawArraysNew fields and differences
                                   from the existing DrawArrays protocol.

    0.99.1    15-Jan-2010  jrj     Added more issues.
                                   Use 64 bits for offsets and sizes where
                                   appropriate.
                                   Added is_integer to ARRAY_INFO and
                                   VertexAttribPointer
                                   Added PushClientAttrib and
                                   PopClientAttrib protocol.
                                   Added MapBuffer/UnmapBuffer protocol.
                                   Defined VERTEX_ATTRIB_ARRAY = GL_NONE =
                                   0.
                                   Added padding to DrawArraysNew and
                                   DrawRangeElements.
                                   Changed resolution to the byte ordering
                                   issue.

    0.99      16-May-2008  idr     Added "Additions to the GLX Specification"
                                   Minor updates to protocol encoding
    0.98      01-Sep-2006  idr     Resolved the byte-ordering issue.
    0.97      16-Mar-2005  idr     Added 'null_data' field to BufferData.
                                   This works like the 'null_image' in
                                   TexImage3D.
                                   Removed the 'type' field from
                                   DrawRangeElements and DrawElements.
                                   Added the 'count' field to GetElementRange.
    0.96      09-Mar-2005  idr     Modified the EnableBufferObject and
                                   DisableBufferObject commands.
                                   Changed the parameter ordering and the
                                   client-side data specification for
                                   DrawArraysNew.
                                   Replaced the existing DrawElements-type
                                   commands with DrawRangeElements and
                                   DrawElements.
                                   Added the GetElementRange query.
                                   Added a proposed resolution for the
                                   EnableClientState issue.
    0.95      04-Mar-2005  idr     Fixed the size of BindBufferToArray.
                                   Added several issues.
    0.94      09-Dec-2004  idr     Typo corrections.
                                   Fixed the sizes of several LISTofBYTE and
                                   BOOL elements.
                                   Removed the 'provoke' field from the
                                   ArrayElement command.
                                   Added commands to support DrawElements.
    0.93      08-Dec-2004  idr     Added several issues.
    0.92      27-Oct-2004  idr     Initial pass at GLX protocol.
