bufferobj.c revision cdc920a0
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.6
4 *
5 * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
6 * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27/**
28 * \file bufferobj.c
29 * \brief Functions for the GL_ARB_vertex/pixel_buffer_object extensions.
30 * \author Brian Paul, Ian Romanick
31 */
32
33
34#include "glheader.h"
35#include "hash.h"
36#include "imports.h"
37#include "image.h"
38#include "context.h"
39#include "bufferobj.h"
40#include "fbobject.h"
41#include "texobj.h"
42
43
44/* Debug flags */
45/*#define VBO_DEBUG*/
46/*#define BOUNDS_CHECK*/
47
48
49#ifdef FEATURE_OES_mapbuffer
50#define DEFAULT_ACCESS GL_MAP_WRITE_BIT
51#else
52#define DEFAULT_ACCESS (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)
53#endif
54
55
56/**
57 * Return pointer to address of a buffer object target.
58 * \param ctx  the GL context
59 * \param target  the buffer object target to be retrieved.
60 * \return   pointer to pointer to the buffer object bound to \c target in the
61 *           specified context or \c NULL if \c target is invalid.
62 */
63static INLINE struct gl_buffer_object **
64get_buffer_target(GLcontext *ctx, GLenum target)
65{
66   switch (target) {
67   case GL_ARRAY_BUFFER_ARB:
68      return &ctx->Array.ArrayBufferObj;
69   case GL_ELEMENT_ARRAY_BUFFER_ARB:
70      return &ctx->Array.ElementArrayBufferObj;
71   case GL_PIXEL_PACK_BUFFER_EXT:
72      return &ctx->Pack.BufferObj;
73   case GL_PIXEL_UNPACK_BUFFER_EXT:
74      return &ctx->Unpack.BufferObj;
75   case GL_COPY_READ_BUFFER:
76      if (ctx->Extensions.ARB_copy_buffer) {
77         return &ctx->CopyReadBuffer;
78      }
79      break;
80   case GL_COPY_WRITE_BUFFER:
81      if (ctx->Extensions.ARB_copy_buffer) {
82         return &ctx->CopyWriteBuffer;
83      }
84      break;
85   default:
86      return NULL;
87   }
88   return NULL;
89}
90
91
92/**
93 * Get the buffer object bound to the specified target in a GL context.
94 * \param ctx  the GL context
95 * \param target  the buffer object target to be retrieved.
96 * \return   pointer to the buffer object bound to \c target in the
97 *           specified context or \c NULL if \c target is invalid.
98 */
99static INLINE struct gl_buffer_object *
100get_buffer(GLcontext *ctx, GLenum target)
101{
102   struct gl_buffer_object **bufObj = get_buffer_target(ctx, target);
103   if (bufObj)
104      return *bufObj;
105   return NULL;
106}
107
108
109/**
110 * Convert a GLbitfield describing the mapped buffer access flags
111 * into one of GL_READ_WRITE, GL_READ_ONLY, or GL_WRITE_ONLY.
112 */
113static GLenum
114simplified_access_mode(GLbitfield access)
115{
116   const GLbitfield rwFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
117   if ((access & rwFlags) == rwFlags)
118      return GL_READ_WRITE;
119   if ((access & GL_MAP_READ_BIT) == GL_MAP_READ_BIT)
120      return GL_READ_ONLY;
121   if ((access & GL_MAP_WRITE_BIT) == GL_MAP_WRITE_BIT)
122      return GL_WRITE_ONLY;
123   return GL_READ_WRITE; /* this should never happen, but no big deal */
124}
125
126
127/**
128 * Tests the subdata range parameters and sets the GL error code for
129 * \c glBufferSubDataARB and \c glGetBufferSubDataARB.
130 *
131 * \param ctx     GL context.
132 * \param target  Buffer object target on which to operate.
133 * \param offset  Offset of the first byte of the subdata range.
134 * \param size    Size, in bytes, of the subdata range.
135 * \param caller  Name of calling function for recording errors.
136 * \return   A pointer to the buffer object bound to \c target in the
137 *           specified context or \c NULL if any of the parameter or state
138 *           conditions for \c glBufferSubDataARB or \c glGetBufferSubDataARB
139 *           are invalid.
140 *
141 * \sa glBufferSubDataARB, glGetBufferSubDataARB
142 */
143static struct gl_buffer_object *
144buffer_object_subdata_range_good( GLcontext * ctx, GLenum target,
145                                  GLintptrARB offset, GLsizeiptrARB size,
146                                  const char *caller )
147{
148   struct gl_buffer_object *bufObj;
149
150   if (size < 0) {
151      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size < 0)", caller);
152      return NULL;
153   }
154
155   if (offset < 0) {
156      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset < 0)", caller);
157      return NULL;
158   }
159
160   bufObj = get_buffer(ctx, target);
161   if (!bufObj) {
162      _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", caller);
163      return NULL;
164   }
165   if (!_mesa_is_bufferobj(bufObj)) {
166      _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
167      return NULL;
168   }
169   if (offset + size > bufObj->Size) {
170      _mesa_error(ctx, GL_INVALID_VALUE,
171		  "%s(size + offset > buffer size)", caller);
172      return NULL;
173   }
174   if (_mesa_bufferobj_mapped(bufObj)) {
175      /* Buffer is currently mapped */
176      _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
177      return NULL;
178   }
179
180   return bufObj;
181}
182
183
184/**
185 * Allocate and initialize a new buffer object.
186 *
187 * Default callback for the \c dd_function_table::NewBufferObject() hook.
188 */
189static struct gl_buffer_object *
190_mesa_new_buffer_object( GLcontext *ctx, GLuint name, GLenum target )
191{
192   struct gl_buffer_object *obj;
193
194   (void) ctx;
195
196   obj = MALLOC_STRUCT(gl_buffer_object);
197   _mesa_initialize_buffer_object(obj, name, target);
198   return obj;
199}
200
201
202/**
203 * Delete a buffer object.
204 *
205 * Default callback for the \c dd_function_table::DeleteBuffer() hook.
206 */
207static void
208_mesa_delete_buffer_object( GLcontext *ctx, struct gl_buffer_object *bufObj )
209{
210   (void) ctx;
211
212   if (bufObj->Data)
213      free(bufObj->Data);
214
215   /* assign strange values here to help w/ debugging */
216   bufObj->RefCount = -1000;
217   bufObj->Name = ~0;
218
219   _glthread_DESTROY_MUTEX(bufObj->Mutex);
220   free(bufObj);
221}
222
223
224
225/**
226 * Set ptr to bufObj w/ reference counting.
227 */
228void
229_mesa_reference_buffer_object(GLcontext *ctx,
230                              struct gl_buffer_object **ptr,
231                              struct gl_buffer_object *bufObj)
232{
233   if (*ptr == bufObj)
234      return;
235
236   if (*ptr) {
237      /* Unreference the old buffer */
238      GLboolean deleteFlag = GL_FALSE;
239      struct gl_buffer_object *oldObj = *ptr;
240
241      _glthread_LOCK_MUTEX(oldObj->Mutex);
242      ASSERT(oldObj->RefCount > 0);
243      oldObj->RefCount--;
244#if 0
245      printf("BufferObj %p %d DECR to %d\n",
246             (void *) oldObj, oldObj->Name, oldObj->RefCount);
247#endif
248      deleteFlag = (oldObj->RefCount == 0);
249      _glthread_UNLOCK_MUTEX(oldObj->Mutex);
250
251      if (deleteFlag) {
252
253         /* some sanity checking: don't delete a buffer still in use */
254#if 0
255         /* unfortunately, these tests are invalid during context tear-down */
256	 ASSERT(ctx->Array.ArrayBufferObj != bufObj);
257	 ASSERT(ctx->Array.ElementArrayBufferObj != bufObj);
258	 ASSERT(ctx->Array.ArrayObj->Vertex.BufferObj != bufObj);
259#endif
260
261	 ASSERT(ctx->Driver.DeleteBuffer);
262         ctx->Driver.DeleteBuffer(ctx, oldObj);
263      }
264
265      *ptr = NULL;
266   }
267   ASSERT(!*ptr);
268
269   if (bufObj) {
270      /* reference new buffer */
271      _glthread_LOCK_MUTEX(bufObj->Mutex);
272      if (bufObj->RefCount == 0) {
273         /* this buffer's being deleted (look just above) */
274         /* Not sure this can every really happen.  Warn if it does. */
275         _mesa_problem(NULL, "referencing deleted buffer object");
276         *ptr = NULL;
277      }
278      else {
279         bufObj->RefCount++;
280#if 0
281         printf("BufferObj %p %d INCR to %d\n",
282                (void *) bufObj, bufObj->Name, bufObj->RefCount);
283#endif
284         *ptr = bufObj;
285      }
286      _glthread_UNLOCK_MUTEX(bufObj->Mutex);
287   }
288}
289
290
291/**
292 * Initialize a buffer object to default values.
293 */
294void
295_mesa_initialize_buffer_object( struct gl_buffer_object *obj,
296				GLuint name, GLenum target )
297{
298   (void) target;
299
300   memset(obj, 0, sizeof(struct gl_buffer_object));
301   _glthread_INIT_MUTEX(obj->Mutex);
302   obj->RefCount = 1;
303   obj->Name = name;
304   obj->Usage = GL_STATIC_DRAW_ARB;
305   obj->AccessFlags = DEFAULT_ACCESS;
306}
307
308
309/**
310 * Allocate space for and store data in a buffer object.  Any data that was
311 * previously stored in the buffer object is lost.  If \c data is \c NULL,
312 * memory will be allocated, but no copy will occur.
313 *
314 * This is the default callback for \c dd_function_table::BufferData()
315 * Note that all GL error checking will have been done already.
316 *
317 * \param ctx     GL context.
318 * \param target  Buffer object target on which to operate.
319 * \param size    Size, in bytes, of the new data store.
320 * \param data    Pointer to the data to store in the buffer object.  This
321 *                pointer may be \c NULL.
322 * \param usage   Hints about how the data will be used.
323 * \param bufObj  Object to be used.
324 *
325 * \return GL_TRUE for success, GL_FALSE for failure
326 * \sa glBufferDataARB, dd_function_table::BufferData.
327 */
328static GLboolean
329_mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
330		   const GLvoid * data, GLenum usage,
331		   struct gl_buffer_object * bufObj )
332{
333   void * new_data;
334
335   (void) ctx; (void) target;
336
337   new_data = _mesa_realloc( bufObj->Data, bufObj->Size, size );
338   if (new_data) {
339      bufObj->Data = (GLubyte *) new_data;
340      bufObj->Size = size;
341      bufObj->Usage = usage;
342
343      if (data) {
344	 memcpy( bufObj->Data, data, size );
345      }
346
347      return GL_TRUE;
348   }
349   else {
350      return GL_FALSE;
351   }
352}
353
354
355/**
356 * Replace data in a subrange of buffer object.  If the data range
357 * specified by \c size + \c offset extends beyond the end of the buffer or
358 * if \c data is \c NULL, no copy is performed.
359 *
360 * This is the default callback for \c dd_function_table::BufferSubData()
361 * Note that all GL error checking will have been done already.
362 *
363 * \param ctx     GL context.
364 * \param target  Buffer object target on which to operate.
365 * \param offset  Offset of the first byte to be modified.
366 * \param size    Size, in bytes, of the data range.
367 * \param data    Pointer to the data to store in the buffer object.
368 * \param bufObj  Object to be used.
369 *
370 * \sa glBufferSubDataARB, dd_function_table::BufferSubData.
371 */
372static void
373_mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
374		      GLsizeiptrARB size, const GLvoid * data,
375		      struct gl_buffer_object * bufObj )
376{
377   (void) ctx; (void) target;
378
379   /* this should have been caught in _mesa_BufferSubData() */
380   ASSERT(size + offset <= bufObj->Size);
381
382   if (bufObj->Data) {
383      memcpy( (GLubyte *) bufObj->Data + offset, data, size );
384   }
385}
386
387
388/**
389 * Retrieve data from a subrange of buffer object.  If the data range
390 * specified by \c size + \c offset extends beyond the end of the buffer or
391 * if \c data is \c NULL, no copy is performed.
392 *
393 * This is the default callback for \c dd_function_table::GetBufferSubData()
394 * Note that all GL error checking will have been done already.
395 *
396 * \param ctx     GL context.
397 * \param target  Buffer object target on which to operate.
398 * \param offset  Offset of the first byte to be fetched.
399 * \param size    Size, in bytes, of the data range.
400 * \param data    Destination for data
401 * \param bufObj  Object to be used.
402 *
403 * \sa glBufferGetSubDataARB, dd_function_table::GetBufferSubData.
404 */
405static void
406_mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
407			  GLsizeiptrARB size, GLvoid * data,
408			  struct gl_buffer_object * bufObj )
409{
410   (void) ctx; (void) target;
411
412   if (bufObj->Data && ((GLsizeiptrARB) (size + offset) <= bufObj->Size)) {
413      memcpy( data, (GLubyte *) bufObj->Data + offset, size );
414   }
415}
416
417
418/**
419 * Default callback for \c dd_function_tabel::MapBuffer().
420 *
421 * The function parameters will have been already tested for errors.
422 *
423 * \param ctx     GL context.
424 * \param target  Buffer object target on which to operate.
425 * \param access  Information about how the buffer will be accessed.
426 * \param bufObj  Object to be mapped.
427 * \return  A pointer to the object's internal data store that can be accessed
428 *          by the processor
429 *
430 * \sa glMapBufferARB, dd_function_table::MapBuffer
431 */
432static void *
433_mesa_buffer_map( GLcontext *ctx, GLenum target, GLenum access,
434		  struct gl_buffer_object *bufObj )
435{
436   (void) ctx;
437   (void) target;
438   (void) access;
439   /* Just return a direct pointer to the data */
440   if (_mesa_bufferobj_mapped(bufObj)) {
441      /* already mapped! */
442      return NULL;
443   }
444   bufObj->Pointer = bufObj->Data;
445   bufObj->Length = bufObj->Size;
446   bufObj->Offset = 0;
447   return bufObj->Pointer;
448}
449
450
451/**
452 * Default fallback for \c dd_function_table::MapBufferRange().
453 * Called via glMapBufferRange().
454 */
455static void *
456_mesa_buffer_map_range( GLcontext *ctx, GLenum target, GLintptr offset,
457                        GLsizeiptr length, GLbitfield access,
458                        struct gl_buffer_object *bufObj )
459{
460   (void) ctx;
461   (void) target;
462   assert(!_mesa_bufferobj_mapped(bufObj));
463   /* Just return a direct pointer to the data */
464   bufObj->Pointer = bufObj->Data + offset;
465   bufObj->Length = length;
466   bufObj->Offset = offset;
467   bufObj->AccessFlags = access;
468   return bufObj->Pointer;
469}
470
471
472/**
473 * Default fallback for \c dd_function_table::FlushMappedBufferRange().
474 * Called via glFlushMappedBufferRange().
475 */
476static void
477_mesa_buffer_flush_mapped_range( GLcontext *ctx, GLenum target,
478                                 GLintptr offset, GLsizeiptr length,
479                                 struct gl_buffer_object *obj )
480{
481   (void) ctx;
482   (void) target;
483   (void) offset;
484   (void) length;
485   (void) obj;
486   /* no-op */
487}
488
489
490/**
491 * Default callback for \c dd_function_table::MapBuffer().
492 *
493 * The input parameters will have been already tested for errors.
494 *
495 * \sa glUnmapBufferARB, dd_function_table::UnmapBuffer
496 */
497static GLboolean
498_mesa_buffer_unmap( GLcontext *ctx, GLenum target,
499                    struct gl_buffer_object *bufObj )
500{
501   (void) ctx;
502   (void) target;
503   /* XXX we might assert here that bufObj->Pointer is non-null */
504   bufObj->Pointer = NULL;
505   bufObj->Length = 0;
506   bufObj->Offset = 0;
507   bufObj->AccessFlags = 0x0;
508   return GL_TRUE;
509}
510
511
512/**
513 * Default fallback for \c dd_function_table::CopyBufferSubData().
514 * Called via glCopyBuffserSubData().
515 */
516static void
517_mesa_copy_buffer_subdata(GLcontext *ctx,
518                          struct gl_buffer_object *src,
519                          struct gl_buffer_object *dst,
520                          GLintptr readOffset, GLintptr writeOffset,
521                          GLsizeiptr size)
522{
523   GLubyte *srcPtr, *dstPtr;
524
525   /* buffer should not already be mapped */
526   assert(!_mesa_bufferobj_mapped(src));
527   assert(!_mesa_bufferobj_mapped(dst));
528
529   srcPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_READ_BUFFER,
530                                              GL_READ_ONLY, src);
531   dstPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_WRITE_BUFFER,
532                                              GL_WRITE_ONLY, dst);
533
534   if (srcPtr && dstPtr)
535      memcpy(dstPtr + writeOffset, srcPtr + readOffset, size);
536
537   ctx->Driver.UnmapBuffer(ctx, GL_COPY_READ_BUFFER, src);
538   ctx->Driver.UnmapBuffer(ctx, GL_COPY_WRITE_BUFFER, dst);
539}
540
541
542
543/**
544 * Initialize the state associated with buffer objects
545 */
546void
547_mesa_init_buffer_objects( GLcontext *ctx )
548{
549   _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj,
550                                 ctx->Shared->NullBufferObj);
551   _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj,
552                                 ctx->Shared->NullBufferObj);
553
554   _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer,
555                                 ctx->Shared->NullBufferObj);
556   _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer,
557                                 ctx->Shared->NullBufferObj);
558}
559
560
561void
562_mesa_free_buffer_objects( GLcontext *ctx )
563{
564   _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, NULL);
565   _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, NULL);
566
567   _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, NULL);
568   _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, NULL);
569}
570
571
572/**
573 * Bind the specified target to buffer for the specified context.
574 * Called by glBindBuffer() and other functions.
575 */
576static void
577bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
578{
579   struct gl_buffer_object *oldBufObj;
580   struct gl_buffer_object *newBufObj = NULL;
581   struct gl_buffer_object **bindTarget = NULL;
582
583   bindTarget = get_buffer_target(ctx, target);
584   if (!bindTarget) {
585      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)");
586      return;
587   }
588
589   /* Get pointer to old buffer object (to be unbound) */
590   oldBufObj = *bindTarget;
591   if (oldBufObj && oldBufObj->Name == buffer)
592      return;   /* rebinding the same buffer object- no change */
593
594   /*
595    * Get pointer to new buffer object (newBufObj)
596    */
597   if (buffer == 0) {
598      /* The spec says there's not a buffer object named 0, but we use
599       * one internally because it simplifies things.
600       */
601      newBufObj = ctx->Shared->NullBufferObj;
602   }
603   else {
604      /* non-default buffer object */
605      newBufObj = _mesa_lookup_bufferobj(ctx, buffer);
606      if (!newBufObj) {
607         /* if this is a new buffer object id, allocate a buffer object now */
608         ASSERT(ctx->Driver.NewBufferObject);
609         newBufObj = ctx->Driver.NewBufferObject(ctx, buffer, target);
610         if (!newBufObj) {
611            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB");
612            return;
613         }
614         _mesa_HashInsert(ctx->Shared->BufferObjects, buffer, newBufObj);
615      }
616   }
617
618   /* bind new buffer */
619   _mesa_reference_buffer_object(ctx, bindTarget, newBufObj);
620
621   /* Pass BindBuffer call to device driver */
622   if (ctx->Driver.BindBuffer)
623      ctx->Driver.BindBuffer( ctx, target, newBufObj );
624}
625
626
627/**
628 * Update the default buffer objects in the given context to reference those
629 * specified in the shared state and release those referencing the old
630 * shared state.
631 */
632void
633_mesa_update_default_objects_buffer_objects(GLcontext *ctx)
634{
635   /* Bind the NullBufferObj to remove references to those
636    * in the shared context hash table.
637    */
638   bind_buffer_object( ctx, GL_ARRAY_BUFFER_ARB, 0);
639   bind_buffer_object( ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
640   bind_buffer_object( ctx, GL_PIXEL_PACK_BUFFER_ARB, 0);
641   bind_buffer_object( ctx, GL_PIXEL_UNPACK_BUFFER_ARB, 0);
642}
643
644
645/**
646 * When we're about to read pixel data out of a PBO (via glDrawPixels,
647 * glTexImage, etc) or write data into a PBO (via glReadPixels,
648 * glGetTexImage, etc) we call this function to check that we're not
649 * going to read out of bounds.
650 *
651 * XXX This would also be a convenient time to check that the PBO isn't
652 * currently mapped.  Whoever calls this function should check for that.
653 * Remember, we can't use a PBO when it's mapped!
654 *
655 * If we're not using a PBO, this is a no-op.
656 *
657 * \param width  width of image to read/write
658 * \param height  height of image to read/write
659 * \param depth  depth of image to read/write
660 * \param format  format of image to read/write
661 * \param type  datatype of image to read/write
662 * \param ptr  the user-provided pointer/offset
663 * \return GL_TRUE if the PBO access is OK, GL_FALSE if the access would
664 *         go out of bounds.
665 */
666GLboolean
667_mesa_validate_pbo_access(GLuint dimensions,
668                          const struct gl_pixelstore_attrib *pack,
669                          GLsizei width, GLsizei height, GLsizei depth,
670                          GLenum format, GLenum type, const GLvoid *ptr)
671{
672   GLvoid *start, *end;
673   const GLubyte *sizeAddr; /* buffer size, cast to a pointer */
674
675   if (!_mesa_is_bufferobj(pack->BufferObj))
676      return GL_TRUE;  /* no PBO, OK */
677
678   if (pack->BufferObj->Size == 0)
679      /* no buffer! */
680      return GL_FALSE;
681
682   /* get address of first pixel we'll read */
683   start = _mesa_image_address(dimensions, pack, ptr, width, height,
684                               format, type, 0, 0, 0);
685
686   /* get address just past the last pixel we'll read */
687   end =  _mesa_image_address(dimensions, pack, ptr, width, height,
688                              format, type, depth-1, height-1, width);
689
690
691   sizeAddr = ((const GLubyte *) 0) + pack->BufferObj->Size;
692
693   if ((const GLubyte *) start > sizeAddr) {
694      /* This will catch negative values / wrap-around */
695      return GL_FALSE;
696   }
697   if ((const GLubyte *) end > sizeAddr) {
698      /* Image read goes beyond end of buffer */
699      return GL_FALSE;
700   }
701
702   /* OK! */
703   return GL_TRUE;
704}
705
706
707/**
708 * For commands that read from a PBO (glDrawPixels, glTexImage,
709 * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
710 * and return the pointer into the PBO.  If we're not reading from a
711 * PBO, return \p src as-is.
712 * If non-null return, must call _mesa_unmap_pbo_source() when done.
713 *
714 * \return NULL if error, else pointer to start of data
715 */
716const GLvoid *
717_mesa_map_pbo_source(GLcontext *ctx,
718                     const struct gl_pixelstore_attrib *unpack,
719                     const GLvoid *src)
720{
721   const GLubyte *buf;
722
723   if (_mesa_is_bufferobj(unpack->BufferObj)) {
724      /* unpack from PBO */
725      buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
726                                              GL_READ_ONLY_ARB,
727                                              unpack->BufferObj);
728      if (!buf)
729         return NULL;
730
731      buf = ADD_POINTERS(buf, src);
732   }
733   else {
734      /* unpack from normal memory */
735      buf = src;
736   }
737
738   return buf;
739}
740
741
742/**
743 * Combine PBO-read validation and mapping.
744 * If any GL errors are detected, they'll be recorded and NULL returned.
745 * \sa _mesa_validate_pbo_access
746 * \sa _mesa_map_pbo_source
747 * A call to this function should have a matching call to
748 * _mesa_unmap_pbo_source().
749 */
750const GLvoid *
751_mesa_map_validate_pbo_source(GLcontext *ctx,
752                              GLuint dimensions,
753                              const struct gl_pixelstore_attrib *unpack,
754                              GLsizei width, GLsizei height, GLsizei depth,
755                              GLenum format, GLenum type, const GLvoid *ptr,
756                              const char *where)
757{
758   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
759
760   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
761      /* non-PBO access: no validation to be done */
762      return ptr;
763   }
764
765   if (!_mesa_validate_pbo_access(dimensions, unpack,
766                                  width, height, depth, format, type, ptr)) {
767      _mesa_error(ctx, GL_INVALID_OPERATION,
768                  "%s(out of bounds PBO access)", where);
769      return NULL;
770   }
771
772   if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
773      /* buffer is already mapped - that's an error */
774      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
775      return NULL;
776   }
777
778   ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
779   return ptr;
780}
781
782
783/**
784 * Counterpart to _mesa_map_pbo_source()
785 */
786void
787_mesa_unmap_pbo_source(GLcontext *ctx,
788                       const struct gl_pixelstore_attrib *unpack)
789{
790   ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
791   if (_mesa_is_bufferobj(unpack->BufferObj)) {
792      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
793                              unpack->BufferObj);
794   }
795}
796
797
798/**
799 * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
800 * if we're writing to a PBO, map it write-only and return the pointer
801 * into the PBO.  If we're not writing to a PBO, return \p dst as-is.
802 * If non-null return, must call _mesa_unmap_pbo_dest() when done.
803 *
804 * \return NULL if error, else pointer to start of data
805 */
806void *
807_mesa_map_pbo_dest(GLcontext *ctx,
808                   const struct gl_pixelstore_attrib *pack,
809                   GLvoid *dest)
810{
811   void *buf;
812
813   if (_mesa_is_bufferobj(pack->BufferObj)) {
814      /* pack into PBO */
815      buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
816                                              GL_WRITE_ONLY_ARB,
817                                              pack->BufferObj);
818      if (!buf)
819         return NULL;
820
821      buf = ADD_POINTERS(buf, dest);
822   }
823   else {
824      /* pack to normal memory */
825      buf = dest;
826   }
827
828   return buf;
829}
830
831
832/**
833 * Combine PBO-write validation and mapping.
834 * If any GL errors are detected, they'll be recorded and NULL returned.
835 * \sa _mesa_validate_pbo_access
836 * \sa _mesa_map_pbo_dest
837 * A call to this function should have a matching call to
838 * _mesa_unmap_pbo_dest().
839 */
840GLvoid *
841_mesa_map_validate_pbo_dest(GLcontext *ctx,
842                            GLuint dimensions,
843                            const struct gl_pixelstore_attrib *unpack,
844                            GLsizei width, GLsizei height, GLsizei depth,
845                            GLenum format, GLenum type, GLvoid *ptr,
846                            const char *where)
847{
848   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
849
850   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
851      /* non-PBO access: no validation to be done */
852      return ptr;
853   }
854
855   if (!_mesa_validate_pbo_access(dimensions, unpack,
856                                  width, height, depth, format, type, ptr)) {
857      _mesa_error(ctx, GL_INVALID_OPERATION,
858                  "%s(out of bounds PBO access)", where);
859      return NULL;
860   }
861
862   if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
863      /* buffer is already mapped - that's an error */
864      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
865      return NULL;
866   }
867
868   ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
869   return ptr;
870}
871
872
873/**
874 * Counterpart to _mesa_map_pbo_dest()
875 */
876void
877_mesa_unmap_pbo_dest(GLcontext *ctx,
878                     const struct gl_pixelstore_attrib *pack)
879{
880   ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
881   if (_mesa_is_bufferobj(pack->BufferObj)) {
882      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, pack->BufferObj);
883   }
884}
885
886
887
888/**
889 * Return the gl_buffer_object for the given ID.
890 * Always return NULL for ID 0.
891 */
892struct gl_buffer_object *
893_mesa_lookup_bufferobj(GLcontext *ctx, GLuint buffer)
894{
895   if (buffer == 0)
896      return NULL;
897   else
898      return (struct gl_buffer_object *)
899         _mesa_HashLookup(ctx->Shared->BufferObjects, buffer);
900}
901
902
903/**
904 * If *ptr points to obj, set ptr = the Null/default buffer object.
905 * This is a helper for buffer object deletion.
906 * The GL spec says that deleting a buffer object causes it to get
907 * unbound from all arrays in the current context.
908 */
909static void
910unbind(GLcontext *ctx,
911       struct gl_buffer_object **ptr,
912       struct gl_buffer_object *obj)
913{
914   if (*ptr == obj) {
915      _mesa_reference_buffer_object(ctx, ptr, ctx->Shared->NullBufferObj);
916   }
917}
918
919
920/**
921 * Plug default/fallback buffer object functions into the device
922 * driver hooks.
923 */
924void
925_mesa_init_buffer_object_functions(struct dd_function_table *driver)
926{
927   /* GL_ARB_vertex/pixel_buffer_object */
928   driver->NewBufferObject = _mesa_new_buffer_object;
929   driver->DeleteBuffer = _mesa_delete_buffer_object;
930   driver->BindBuffer = NULL;
931   driver->BufferData = _mesa_buffer_data;
932   driver->BufferSubData = _mesa_buffer_subdata;
933   driver->GetBufferSubData = _mesa_buffer_get_subdata;
934   driver->MapBuffer = _mesa_buffer_map;
935   driver->UnmapBuffer = _mesa_buffer_unmap;
936
937   /* GL_ARB_map_buffer_range */
938   driver->MapBufferRange = _mesa_buffer_map_range;
939   driver->FlushMappedBufferRange = _mesa_buffer_flush_mapped_range;
940
941   /* GL_ARB_copy_buffer */
942   driver->CopyBufferSubData = _mesa_copy_buffer_subdata;
943}
944
945
946
947/**********************************************************************/
948/* API Functions                                                      */
949/**********************************************************************/
950
951void GLAPIENTRY
952_mesa_BindBufferARB(GLenum target, GLuint buffer)
953{
954   GET_CURRENT_CONTEXT(ctx);
955   ASSERT_OUTSIDE_BEGIN_END(ctx);
956
957   bind_buffer_object(ctx, target, buffer);
958}
959
960
961/**
962 * Delete a set of buffer objects.
963 *
964 * \param n      Number of buffer objects to delete.
965 * \param ids    Array of \c n buffer object IDs.
966 */
967void GLAPIENTRY
968_mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
969{
970   GET_CURRENT_CONTEXT(ctx);
971   GLsizei i;
972   ASSERT_OUTSIDE_BEGIN_END(ctx);
973
974   if (n < 0) {
975      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteBuffersARB(n)");
976      return;
977   }
978
979   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
980
981   for (i = 0; i < n; i++) {
982      struct gl_buffer_object *bufObj = _mesa_lookup_bufferobj(ctx, ids[i]);
983      if (bufObj) {
984         struct gl_array_object *arrayObj = ctx->Array.ArrayObj;
985         GLuint j;
986
987         ASSERT(bufObj->Name == ids[i]);
988
989         if (_mesa_bufferobj_mapped(bufObj)) {
990            /* if mapped, unmap it now */
991            ctx->Driver.UnmapBuffer(ctx, 0, bufObj);
992            bufObj->AccessFlags = DEFAULT_ACCESS;
993            bufObj->Pointer = NULL;
994         }
995
996         /* unbind any vertex pointers bound to this buffer */
997         unbind(ctx, &arrayObj->Vertex.BufferObj, bufObj);
998         unbind(ctx, &arrayObj->Weight.BufferObj, bufObj);
999         unbind(ctx, &arrayObj->Normal.BufferObj, bufObj);
1000         unbind(ctx, &arrayObj->Color.BufferObj, bufObj);
1001         unbind(ctx, &arrayObj->SecondaryColor.BufferObj, bufObj);
1002         unbind(ctx, &arrayObj->FogCoord.BufferObj, bufObj);
1003         unbind(ctx, &arrayObj->Index.BufferObj, bufObj);
1004         unbind(ctx, &arrayObj->EdgeFlag.BufferObj, bufObj);
1005         for (j = 0; j < Elements(arrayObj->TexCoord); j++) {
1006            unbind(ctx, &arrayObj->TexCoord[j].BufferObj, bufObj);
1007         }
1008         for (j = 0; j < Elements(arrayObj->VertexAttrib); j++) {
1009            unbind(ctx, &arrayObj->VertexAttrib[j].BufferObj, bufObj);
1010         }
1011
1012         if (ctx->Array.ArrayBufferObj == bufObj) {
1013            _mesa_BindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
1014         }
1015         if (ctx->Array.ElementArrayBufferObj == bufObj) {
1016            _mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
1017         }
1018
1019         /* unbind any pixel pack/unpack pointers bound to this buffer */
1020         if (ctx->Pack.BufferObj == bufObj) {
1021            _mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 );
1022         }
1023         if (ctx->Unpack.BufferObj == bufObj) {
1024            _mesa_BindBufferARB( GL_PIXEL_UNPACK_BUFFER_EXT, 0 );
1025         }
1026
1027         /* The ID is immediately freed for re-use */
1028         _mesa_HashRemove(ctx->Shared->BufferObjects, bufObj->Name);
1029         _mesa_reference_buffer_object(ctx, &bufObj, NULL);
1030      }
1031   }
1032
1033   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1034}
1035
1036
1037/**
1038 * Generate a set of unique buffer object IDs and store them in \c buffer.
1039 *
1040 * \param n       Number of IDs to generate.
1041 * \param buffer  Array of \c n locations to store the IDs.
1042 */
1043void GLAPIENTRY
1044_mesa_GenBuffersARB(GLsizei n, GLuint *buffer)
1045{
1046   GET_CURRENT_CONTEXT(ctx);
1047   GLuint first;
1048   GLint i;
1049   ASSERT_OUTSIDE_BEGIN_END(ctx);
1050
1051   if (n < 0) {
1052      _mesa_error(ctx, GL_INVALID_VALUE, "glGenBuffersARB");
1053      return;
1054   }
1055
1056   if (!buffer) {
1057      return;
1058   }
1059
1060   /*
1061    * This must be atomic (generation and allocation of buffer object IDs)
1062    */
1063   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
1064
1065   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->BufferObjects, n);
1066
1067   /* Allocate new, empty buffer objects and return identifiers */
1068   for (i = 0; i < n; i++) {
1069      struct gl_buffer_object *bufObj;
1070      GLuint name = first + i;
1071      GLenum target = 0;
1072      bufObj = ctx->Driver.NewBufferObject( ctx, name, target );
1073      if (!bufObj) {
1074         _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1075         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenBuffersARB");
1076         return;
1077      }
1078      _mesa_HashInsert(ctx->Shared->BufferObjects, first + i, bufObj);
1079      buffer[i] = first + i;
1080   }
1081
1082   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1083}
1084
1085
1086/**
1087 * Determine if ID is the name of a buffer object.
1088 *
1089 * \param id  ID of the potential buffer object.
1090 * \return  \c GL_TRUE if \c id is the name of a buffer object,
1091 *          \c GL_FALSE otherwise.
1092 */
1093GLboolean GLAPIENTRY
1094_mesa_IsBufferARB(GLuint id)
1095{
1096   struct gl_buffer_object *bufObj;
1097   GET_CURRENT_CONTEXT(ctx);
1098   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1099
1100   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
1101   bufObj = _mesa_lookup_bufferobj(ctx, id);
1102   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1103
1104   return bufObj ? GL_TRUE : GL_FALSE;
1105}
1106
1107
1108void GLAPIENTRY
1109_mesa_BufferDataARB(GLenum target, GLsizeiptrARB size,
1110                    const GLvoid * data, GLenum usage)
1111{
1112   GET_CURRENT_CONTEXT(ctx);
1113   struct gl_buffer_object *bufObj;
1114   ASSERT_OUTSIDE_BEGIN_END(ctx);
1115
1116   if (size < 0) {
1117      _mesa_error(ctx, GL_INVALID_VALUE, "glBufferDataARB(size < 0)");
1118      return;
1119   }
1120
1121   switch (usage) {
1122   case GL_STREAM_DRAW_ARB:
1123   case GL_STREAM_READ_ARB:
1124   case GL_STREAM_COPY_ARB:
1125   case GL_STATIC_DRAW_ARB:
1126   case GL_STATIC_READ_ARB:
1127   case GL_STATIC_COPY_ARB:
1128   case GL_DYNAMIC_DRAW_ARB:
1129   case GL_DYNAMIC_READ_ARB:
1130   case GL_DYNAMIC_COPY_ARB:
1131      /* OK */
1132      break;
1133   default:
1134      _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)");
1135      return;
1136   }
1137
1138   bufObj = get_buffer(ctx, target);
1139   if (!bufObj) {
1140      _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(target)" );
1141      return;
1142   }
1143   if (!_mesa_is_bufferobj(bufObj)) {
1144      _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer 0)" );
1145      return;
1146   }
1147
1148   if (_mesa_bufferobj_mapped(bufObj)) {
1149      /* Unmap the existing buffer.  We'll replace it now.  Not an error. */
1150      ctx->Driver.UnmapBuffer(ctx, target, bufObj);
1151      bufObj->AccessFlags = DEFAULT_ACCESS;
1152      ASSERT(bufObj->Pointer == NULL);
1153   }
1154
1155   FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT);
1156
1157   bufObj->Written = GL_TRUE;
1158
1159#ifdef VBO_DEBUG
1160   printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n",
1161                bufObj->Name, size, data, usage);
1162#endif
1163
1164#ifdef BOUNDS_CHECK
1165   size += 100;
1166#endif
1167
1168   ASSERT(ctx->Driver.BufferData);
1169   if (!ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj )) {
1170      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB()");
1171   }
1172}
1173
1174
1175void GLAPIENTRY
1176_mesa_BufferSubDataARB(GLenum target, GLintptrARB offset,
1177                       GLsizeiptrARB size, const GLvoid * data)
1178{
1179   GET_CURRENT_CONTEXT(ctx);
1180   struct gl_buffer_object *bufObj;
1181   ASSERT_OUTSIDE_BEGIN_END(ctx);
1182
1183   bufObj = buffer_object_subdata_range_good( ctx, target, offset, size,
1184                                              "glBufferSubDataARB" );
1185   if (!bufObj) {
1186      /* error already recorded */
1187      return;
1188   }
1189
1190   bufObj->Written = GL_TRUE;
1191
1192   ASSERT(ctx->Driver.BufferSubData);
1193   ctx->Driver.BufferSubData( ctx, target, offset, size, data, bufObj );
1194}
1195
1196
1197void GLAPIENTRY
1198_mesa_GetBufferSubDataARB(GLenum target, GLintptrARB offset,
1199                          GLsizeiptrARB size, void * data)
1200{
1201   GET_CURRENT_CONTEXT(ctx);
1202   struct gl_buffer_object *bufObj;
1203   ASSERT_OUTSIDE_BEGIN_END(ctx);
1204
1205   bufObj = buffer_object_subdata_range_good( ctx, target, offset, size,
1206                                              "glGetBufferSubDataARB" );
1207   if (!bufObj) {
1208      /* error already recorded */
1209      return;
1210   }
1211
1212   ASSERT(ctx->Driver.GetBufferSubData);
1213   ctx->Driver.GetBufferSubData( ctx, target, offset, size, data, bufObj );
1214}
1215
1216
1217void * GLAPIENTRY
1218_mesa_MapBufferARB(GLenum target, GLenum access)
1219{
1220   GET_CURRENT_CONTEXT(ctx);
1221   struct gl_buffer_object * bufObj;
1222   GLbitfield accessFlags;
1223   void *map;
1224
1225   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL);
1226
1227   switch (access) {
1228   case GL_READ_ONLY_ARB:
1229      accessFlags = GL_MAP_READ_BIT;
1230      break;
1231   case GL_WRITE_ONLY_ARB:
1232      accessFlags = GL_MAP_WRITE_BIT;
1233      break;
1234   case GL_READ_WRITE_ARB:
1235      accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
1236      break;
1237   default:
1238      _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)");
1239      return NULL;
1240   }
1241
1242   bufObj = get_buffer(ctx, target);
1243   if (!bufObj) {
1244      _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(target)" );
1245      return NULL;
1246   }
1247   if (!_mesa_is_bufferobj(bufObj)) {
1248      _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(buffer 0)" );
1249      return NULL;
1250   }
1251   if (_mesa_bufferobj_mapped(bufObj)) {
1252      _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(already mapped)");
1253      return NULL;
1254   }
1255
1256   ASSERT(ctx->Driver.MapBuffer);
1257   map = ctx->Driver.MapBuffer( ctx, target, access, bufObj );
1258   if (!map) {
1259      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)");
1260      return NULL;
1261   }
1262   else {
1263      /* The driver callback should have set these fields.
1264       * This is important because other modules (like VBO) might call
1265       * the driver function directly.
1266       */
1267      ASSERT(bufObj->Pointer == map);
1268      ASSERT(bufObj->Length == bufObj->Size);
1269      ASSERT(bufObj->Offset == 0);
1270      bufObj->AccessFlags = accessFlags;
1271   }
1272
1273   if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB)
1274      bufObj->Written = GL_TRUE;
1275
1276#ifdef VBO_DEBUG
1277   printf("glMapBufferARB(%u, sz %ld, access 0x%x)\n",
1278	  bufObj->Name, bufObj->Size, access);
1279   if (access == GL_WRITE_ONLY_ARB) {
1280      GLuint i;
1281      GLubyte *b = (GLubyte *) bufObj->Pointer;
1282      for (i = 0; i < bufObj->Size; i++)
1283         b[i] = i & 0xff;
1284   }
1285#endif
1286
1287#ifdef BOUNDS_CHECK
1288   {
1289      GLubyte *buf = (GLubyte *) bufObj->Pointer;
1290      GLuint i;
1291      /* buffer is 100 bytes larger than requested, fill with magic value */
1292      for (i = 0; i < 100; i++) {
1293         buf[bufObj->Size - i - 1] = 123;
1294      }
1295   }
1296#endif
1297
1298   return bufObj->Pointer;
1299}
1300
1301
1302GLboolean GLAPIENTRY
1303_mesa_UnmapBufferARB(GLenum target)
1304{
1305   GET_CURRENT_CONTEXT(ctx);
1306   struct gl_buffer_object *bufObj;
1307   GLboolean status = GL_TRUE;
1308   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1309
1310   bufObj = get_buffer(ctx, target);
1311   if (!bufObj) {
1312      _mesa_error(ctx, GL_INVALID_ENUM, "glUnmapBufferARB(target)" );
1313      return GL_FALSE;
1314   }
1315   if (!_mesa_is_bufferobj(bufObj)) {
1316      _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB" );
1317      return GL_FALSE;
1318   }
1319   if (!_mesa_bufferobj_mapped(bufObj)) {
1320      _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB");
1321      return GL_FALSE;
1322   }
1323
1324#ifdef BOUNDS_CHECK
1325   if (bufObj->Access != GL_READ_ONLY_ARB) {
1326      GLubyte *buf = (GLubyte *) bufObj->Pointer;
1327      GLuint i;
1328      /* check that last 100 bytes are still = magic value */
1329      for (i = 0; i < 100; i++) {
1330         GLuint pos = bufObj->Size - i - 1;
1331         if (buf[pos] != 123) {
1332            _mesa_warning(ctx, "Out of bounds buffer object write detected"
1333                          " at position %d (value = %u)\n",
1334                          pos, buf[pos]);
1335         }
1336      }
1337   }
1338#endif
1339
1340#ifdef VBO_DEBUG
1341   if (bufObj->AccessFlags & GL_MAP_WRITE_BIT) {
1342      GLuint i, unchanged = 0;
1343      GLubyte *b = (GLubyte *) bufObj->Pointer;
1344      GLint pos = -1;
1345      /* check which bytes changed */
1346      for (i = 0; i < bufObj->Size - 1; i++) {
1347         if (b[i] == (i & 0xff) && b[i+1] == ((i+1) & 0xff)) {
1348            unchanged++;
1349            if (pos == -1)
1350               pos = i;
1351         }
1352      }
1353      if (unchanged) {
1354         printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n",
1355                      bufObj->Name, unchanged, bufObj->Size, pos);
1356      }
1357   }
1358#endif
1359
1360   status = ctx->Driver.UnmapBuffer( ctx, target, bufObj );
1361   bufObj->AccessFlags = DEFAULT_ACCESS;
1362   ASSERT(bufObj->Pointer == NULL);
1363   ASSERT(bufObj->Offset == 0);
1364   ASSERT(bufObj->Length == 0);
1365
1366   return status;
1367}
1368
1369
1370void GLAPIENTRY
1371_mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
1372{
1373   GET_CURRENT_CONTEXT(ctx);
1374   struct gl_buffer_object *bufObj;
1375   ASSERT_OUTSIDE_BEGIN_END(ctx);
1376
1377   bufObj = get_buffer(ctx, target);
1378   if (!bufObj) {
1379      _mesa_error(ctx, GL_INVALID_ENUM, "GetBufferParameterivARB(target)" );
1380      return;
1381   }
1382   if (!_mesa_is_bufferobj(bufObj)) {
1383      _mesa_error(ctx, GL_INVALID_OPERATION, "GetBufferParameterivARB" );
1384      return;
1385   }
1386
1387   switch (pname) {
1388   case GL_BUFFER_SIZE_ARB:
1389      *params = (GLint) bufObj->Size;
1390      break;
1391   case GL_BUFFER_USAGE_ARB:
1392      *params = bufObj->Usage;
1393      break;
1394   case GL_BUFFER_ACCESS_ARB:
1395      *params = simplified_access_mode(bufObj->AccessFlags);
1396      break;
1397   case GL_BUFFER_MAPPED_ARB:
1398      *params = _mesa_bufferobj_mapped(bufObj);
1399      break;
1400   default:
1401      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname)");
1402      return;
1403   }
1404}
1405
1406
1407/**
1408 * New in GL 3.2
1409 * This is pretty much a duplicate of GetBufferParameteriv() but the
1410 * GL_BUFFER_SIZE_ARB attribute will be 64-bits on a 64-bit system.
1411 */
1412void GLAPIENTRY
1413_mesa_GetBufferParameteri64v(GLenum target, GLenum pname, GLint64 *params)
1414{
1415   GET_CURRENT_CONTEXT(ctx);
1416   struct gl_buffer_object *bufObj;
1417   ASSERT_OUTSIDE_BEGIN_END(ctx);
1418
1419   bufObj = get_buffer(ctx, target);
1420   if (!bufObj) {
1421      _mesa_error(ctx, GL_INVALID_ENUM, "GetBufferParameteri64v(target)" );
1422      return;
1423   }
1424   if (!_mesa_is_bufferobj(bufObj)) {
1425      _mesa_error(ctx, GL_INVALID_OPERATION, "GetBufferParameteri64v" );
1426      return;
1427   }
1428
1429   switch (pname) {
1430   case GL_BUFFER_SIZE_ARB:
1431      *params = bufObj->Size;
1432      break;
1433   case GL_BUFFER_USAGE_ARB:
1434      *params = bufObj->Usage;
1435      break;
1436   case GL_BUFFER_ACCESS_ARB:
1437      *params = simplified_access_mode(bufObj->AccessFlags);
1438      break;
1439   case GL_BUFFER_MAPPED_ARB:
1440      *params = _mesa_bufferobj_mapped(bufObj);
1441      break;
1442   default:
1443      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(pname)");
1444      return;
1445   }
1446}
1447
1448
1449void GLAPIENTRY
1450_mesa_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
1451{
1452   GET_CURRENT_CONTEXT(ctx);
1453   struct gl_buffer_object * bufObj;
1454   ASSERT_OUTSIDE_BEGIN_END(ctx);
1455
1456   if (pname != GL_BUFFER_MAP_POINTER_ARB) {
1457      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(pname)");
1458      return;
1459   }
1460
1461   bufObj = get_buffer(ctx, target);
1462   if (!bufObj) {
1463      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(target)" );
1464      return;
1465   }
1466   if (!_mesa_is_bufferobj(bufObj)) {
1467      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferPointervARB" );
1468      return;
1469   }
1470
1471   *params = bufObj->Pointer;
1472}
1473
1474
1475void GLAPIENTRY
1476_mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
1477                        GLintptr readOffset, GLintptr writeOffset,
1478                        GLsizeiptr size)
1479{
1480   GET_CURRENT_CONTEXT(ctx);
1481   struct gl_buffer_object *src, *dst;
1482   ASSERT_OUTSIDE_BEGIN_END(ctx);
1483
1484   src = get_buffer(ctx, readTarget);
1485   if (!src || !_mesa_is_bufferobj(src)) {
1486      _mesa_error(ctx, GL_INVALID_ENUM,
1487                  "glCopyBuffserSubData(readTarget = 0x%x)", readTarget);
1488      return;
1489   }
1490
1491   dst = get_buffer(ctx, writeTarget);
1492   if (!dst || !_mesa_is_bufferobj(dst)) {
1493      _mesa_error(ctx, GL_INVALID_ENUM,
1494                  "glCopyBuffserSubData(writeTarget = 0x%x)", writeTarget);
1495      return;
1496   }
1497
1498   if (_mesa_bufferobj_mapped(src)) {
1499      _mesa_error(ctx, GL_INVALID_OPERATION,
1500                  "glCopyBuffserSubData(readBuffer is mapped)");
1501      return;
1502   }
1503
1504   if (_mesa_bufferobj_mapped(dst)) {
1505      _mesa_error(ctx, GL_INVALID_OPERATION,
1506                  "glCopyBuffserSubData(writeBuffer is mapped)");
1507      return;
1508   }
1509
1510   if (readOffset < 0) {
1511      _mesa_error(ctx, GL_INVALID_VALUE,
1512                  "glCopyBuffserSubData(readOffset = %d)", readOffset);
1513      return;
1514   }
1515
1516   if (writeOffset < 0) {
1517      _mesa_error(ctx, GL_INVALID_VALUE,
1518                  "glCopyBuffserSubData(writeOffset = %d)", writeOffset);
1519      return;
1520   }
1521
1522   if (readOffset + size > src->Size) {
1523      _mesa_error(ctx, GL_INVALID_VALUE,
1524                  "glCopyBuffserSubData(readOffset + size = %d)",
1525                  readOffset, size);
1526      return;
1527   }
1528
1529   if (writeOffset + size > dst->Size) {
1530      _mesa_error(ctx, GL_INVALID_VALUE,
1531                  "glCopyBuffserSubData(writeOffset + size = %d)",
1532                  writeOffset, size);
1533      return;
1534   }
1535
1536   if (src == dst) {
1537      if (readOffset + size <= writeOffset) {
1538         /* OK */
1539      }
1540      else if (writeOffset + size <= readOffset) {
1541         /* OK */
1542      }
1543      else {
1544         /* overlapping src/dst is illegal */
1545         _mesa_error(ctx, GL_INVALID_VALUE,
1546                     "glCopyBuffserSubData(overlapping src/dst)");
1547         return;
1548      }
1549   }
1550
1551   ctx->Driver.CopyBufferSubData(ctx, src, dst, readOffset, writeOffset, size);
1552}
1553
1554
1555/**
1556 * See GL_ARB_map_buffer_range spec
1557 */
1558void * GLAPIENTRY
1559_mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
1560                     GLbitfield access)
1561{
1562   GET_CURRENT_CONTEXT(ctx);
1563   struct gl_buffer_object *bufObj;
1564   void *map;
1565
1566   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL);
1567
1568   if (!ctx->Extensions.ARB_map_buffer_range) {
1569      _mesa_error(ctx, GL_INVALID_OPERATION,
1570                  "glMapBufferRange(extension not supported)");
1571      return NULL;
1572   }
1573
1574   if (offset < 0) {
1575      _mesa_error(ctx, GL_INVALID_VALUE,
1576                  "glMapBufferRange(offset = %ld)", offset);
1577      return NULL;
1578   }
1579
1580   if (length < 0) {
1581      _mesa_error(ctx, GL_INVALID_VALUE,
1582                  "glMapBufferRange(length = %ld)", length);
1583      return NULL;
1584   }
1585
1586   if ((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0) {
1587      _mesa_error(ctx, GL_INVALID_OPERATION,
1588                  "glMapBufferRange(access indicates neither read or write)");
1589      return NULL;
1590   }
1591
1592   if (access & GL_MAP_READ_BIT) {
1593      if ((access & GL_MAP_INVALIDATE_RANGE_BIT) ||
1594          (access & GL_MAP_INVALIDATE_BUFFER_BIT) ||
1595          (access & GL_MAP_UNSYNCHRONIZED_BIT)) {
1596         _mesa_error(ctx, GL_INVALID_OPERATION,
1597                     "glMapBufferRange(invalid access flags)");
1598         return NULL;
1599      }
1600   }
1601
1602   if ((access & GL_MAP_FLUSH_EXPLICIT_BIT) &&
1603       ((access & GL_MAP_WRITE_BIT) == 0)) {
1604      _mesa_error(ctx, GL_INVALID_OPERATION,
1605                  "glMapBufferRange(invalid access flags)");
1606      return NULL;
1607   }
1608
1609   bufObj = get_buffer(ctx, target);
1610   if (!bufObj || !_mesa_is_bufferobj(bufObj)) {
1611      _mesa_error(ctx, GL_INVALID_ENUM,
1612                  "glMapBufferRange(target = 0x%x)", target);
1613      return NULL;
1614   }
1615
1616   if (offset + length > bufObj->Size) {
1617      _mesa_error(ctx, GL_INVALID_VALUE,
1618                  "glMapBufferRange(offset + length > size)");
1619      return NULL;
1620   }
1621
1622   if (_mesa_bufferobj_mapped(bufObj)) {
1623      _mesa_error(ctx, GL_INVALID_OPERATION,
1624                  "glMapBufferRange(buffer already mapped)");
1625      return NULL;
1626   }
1627
1628   ASSERT(ctx->Driver.MapBufferRange);
1629   map = ctx->Driver.MapBufferRange(ctx, target, offset, length,
1630                                    access, bufObj);
1631   if (!map) {
1632      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)");
1633   }
1634   else {
1635      /* The driver callback should have set all these fields.
1636       * This is important because other modules (like VBO) might call
1637       * the driver function directly.
1638       */
1639      ASSERT(bufObj->Pointer == map);
1640      ASSERT(bufObj->Length == length);
1641      ASSERT(bufObj->Offset == offset);
1642      ASSERT(bufObj->AccessFlags == access);
1643   }
1644
1645   return map;
1646}
1647
1648
1649/**
1650 * See GL_ARB_map_buffer_range spec
1651 */
1652void GLAPIENTRY
1653_mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
1654{
1655   GET_CURRENT_CONTEXT(ctx);
1656   struct gl_buffer_object *bufObj;
1657   ASSERT_OUTSIDE_BEGIN_END(ctx);
1658
1659   if (!ctx->Extensions.ARB_map_buffer_range) {
1660      _mesa_error(ctx, GL_INVALID_OPERATION,
1661                  "glMapBufferRange(extension not supported)");
1662      return;
1663   }
1664
1665   if (offset < 0) {
1666      _mesa_error(ctx, GL_INVALID_VALUE,
1667                  "glMapBufferRange(offset = %ld)", offset);
1668      return;
1669   }
1670
1671   if (length < 0) {
1672      _mesa_error(ctx, GL_INVALID_VALUE,
1673                  "glMapBufferRange(length = %ld)", length);
1674      return;
1675   }
1676
1677   bufObj = get_buffer(ctx, target);
1678   if (!bufObj) {
1679      _mesa_error(ctx, GL_INVALID_ENUM,
1680                  "glMapBufferRange(target = 0x%x)", target);
1681      return;
1682   }
1683
1684   if (!_mesa_is_bufferobj(bufObj)) {
1685      _mesa_error(ctx, GL_INVALID_OPERATION,
1686                  "glMapBufferRange(current buffer is 0)");
1687      return;
1688   }
1689
1690   if (!_mesa_bufferobj_mapped(bufObj)) {
1691      /* buffer is not mapped */
1692      _mesa_error(ctx, GL_INVALID_OPERATION,
1693                  "glMapBufferRange(buffer is not mapped)");
1694      return;
1695   }
1696
1697   if ((bufObj->AccessFlags & GL_MAP_FLUSH_EXPLICIT_BIT) == 0) {
1698      _mesa_error(ctx, GL_INVALID_OPERATION,
1699                  "glMapBufferRange(GL_MAP_FLUSH_EXPLICIT_BIT not set)");
1700      return;
1701   }
1702
1703   if (offset + length > bufObj->Length) {
1704      _mesa_error(ctx, GL_INVALID_VALUE,
1705             "glMapBufferRange(offset %ld + length %ld > mapped length %ld)",
1706             offset, length, bufObj->Length);
1707      return;
1708   }
1709
1710   ASSERT(bufObj->AccessFlags & GL_MAP_WRITE_BIT);
1711
1712   if (ctx->Driver.FlushMappedBufferRange)
1713      ctx->Driver.FlushMappedBufferRange(ctx, target, offset, length, bufObj);
1714}
1715
1716
1717#if FEATURE_APPLE_object_purgeable
1718static GLenum
1719_mesa_BufferObjectPurgeable(GLcontext *ctx, GLuint name, GLenum option)
1720{
1721   struct gl_buffer_object *bufObj;
1722   GLenum retval;
1723
1724   bufObj = _mesa_lookup_bufferobj(ctx, name);
1725   if (!bufObj) {
1726      _mesa_error(ctx, GL_INVALID_VALUE,
1727                  "glObjectPurgeable(name = 0x%x)", name);
1728      return 0;
1729   }
1730   if (!_mesa_is_bufferobj(bufObj)) {
1731      _mesa_error(ctx, GL_INVALID_OPERATION, "glObjectPurgeable(buffer 0)" );
1732      return 0;
1733   }
1734
1735   if (bufObj->Purgeable) {
1736      _mesa_error(ctx, GL_INVALID_OPERATION,
1737                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
1738      return GL_VOLATILE_APPLE;
1739   }
1740
1741   bufObj->Purgeable = GL_TRUE;
1742
1743   retval = GL_VOLATILE_APPLE;
1744   if (ctx->Driver.BufferObjectPurgeable)
1745      retval = ctx->Driver.BufferObjectPurgeable(ctx, bufObj, option);
1746
1747   return retval;
1748}
1749
1750
1751static GLenum
1752_mesa_RenderObjectPurgeable(GLcontext *ctx, GLuint name, GLenum option)
1753{
1754   struct gl_renderbuffer *bufObj;
1755   GLenum retval;
1756
1757   bufObj = _mesa_lookup_renderbuffer(ctx, name);
1758   if (!bufObj) {
1759      _mesa_error(ctx, GL_INVALID_VALUE,
1760                  "glObjectUnpurgeable(name = 0x%x)", name);
1761      return 0;
1762   }
1763
1764   if (bufObj->Purgeable) {
1765      _mesa_error(ctx, GL_INVALID_OPERATION,
1766                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
1767      return GL_VOLATILE_APPLE;
1768   }
1769
1770   bufObj->Purgeable = GL_TRUE;
1771
1772   retval = GL_VOLATILE_APPLE;
1773   if (ctx->Driver.RenderObjectPurgeable)
1774      retval = ctx->Driver.RenderObjectPurgeable(ctx, bufObj, option);
1775
1776   return retval;
1777}
1778
1779
1780static GLenum
1781_mesa_TextureObjectPurgeable(GLcontext *ctx, GLuint name, GLenum option)
1782{
1783   struct gl_texture_object *bufObj;
1784   GLenum retval;
1785
1786   bufObj = _mesa_lookup_texture(ctx, name);
1787   if (!bufObj) {
1788      _mesa_error(ctx, GL_INVALID_VALUE,
1789                  "glObjectPurgeable(name = 0x%x)", name);
1790      return 0;
1791   }
1792
1793   if (bufObj->Purgeable) {
1794      _mesa_error(ctx, GL_INVALID_OPERATION,
1795                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
1796      return GL_VOLATILE_APPLE;
1797   }
1798
1799   bufObj->Purgeable = GL_TRUE;
1800
1801   retval = GL_VOLATILE_APPLE;
1802   if (ctx->Driver.TextureObjectPurgeable)
1803      retval = ctx->Driver.TextureObjectPurgeable(ctx, bufObj, option);
1804
1805   return retval;
1806}
1807
1808
1809GLenum GLAPIENTRY
1810_mesa_ObjectPurgeableAPPLE(GLenum objectType, GLuint name, GLenum option)
1811{
1812   GLenum retval;
1813
1814   GET_CURRENT_CONTEXT(ctx);
1815   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
1816
1817   if (name == 0) {
1818      _mesa_error(ctx, GL_INVALID_VALUE,
1819                  "glObjectPurgeable(name = 0x%x)", name);
1820      return 0;
1821   }
1822
1823   switch (option) {
1824   case GL_VOLATILE_APPLE:
1825   case GL_RELEASED_APPLE:
1826      /* legal */
1827      break;
1828   default:
1829      _mesa_error(ctx, GL_INVALID_ENUM,
1830                  "glObjectPurgeable(name = 0x%x) invalid option: %d",
1831                  name, option);
1832      return 0;
1833   }
1834
1835   switch (objectType) {
1836   case GL_TEXTURE:
1837      retval = _mesa_TextureObjectPurgeable (ctx, name, option);
1838      break;
1839   case GL_RENDERBUFFER_EXT:
1840      retval = _mesa_RenderObjectPurgeable (ctx, name, option);
1841      break;
1842   case GL_BUFFER_OBJECT_APPLE:
1843      retval = _mesa_BufferObjectPurgeable (ctx, name, option);
1844      break;
1845   default:
1846      _mesa_error(ctx, GL_INVALID_ENUM,
1847                  "glObjectPurgeable(name = 0x%x) invalid type: %d",
1848                  name, objectType);
1849      return 0;
1850   }
1851
1852   /* In strict conformance to the spec, we must only return VOLATILE when
1853    * when passed the VOLATILE option. Madness.
1854    *
1855    * XXX First fix the spec, then fix me.
1856    */
1857   return option == GL_VOLATILE_APPLE ? GL_VOLATILE_APPLE : retval;
1858}
1859
1860
1861static GLenum
1862_mesa_BufferObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option)
1863{
1864   struct gl_buffer_object *bufObj;
1865   GLenum retval;
1866
1867   bufObj = _mesa_lookup_bufferobj(ctx, name);
1868   if (!bufObj) {
1869      _mesa_error(ctx, GL_INVALID_VALUE,
1870                  "glObjectUnpurgeable(name = 0x%x)", name);
1871      return 0;
1872   }
1873
1874   if (! bufObj->Purgeable) {
1875      _mesa_error(ctx, GL_INVALID_OPERATION,
1876                  "glObjectUnpurgeable(name = 0x%x) object is "
1877                  " already \"unpurged\"", name);
1878      return 0;
1879   }
1880
1881   bufObj->Purgeable = GL_FALSE;
1882
1883   retval = GL_RETAINED_APPLE;
1884   if (ctx->Driver.BufferObjectUnpurgeable)
1885      retval = ctx->Driver.BufferObjectUnpurgeable(ctx, bufObj, option);
1886
1887   return retval;
1888}
1889
1890
1891static GLenum
1892_mesa_RenderObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option)
1893{
1894   struct gl_renderbuffer *bufObj;
1895   GLenum retval;
1896
1897   bufObj = _mesa_lookup_renderbuffer(ctx, name);
1898   if (!bufObj) {
1899      _mesa_error(ctx, GL_INVALID_VALUE,
1900                  "glObjectUnpurgeable(name = 0x%x)", name);
1901      return 0;
1902   }
1903
1904   if (! bufObj->Purgeable) {
1905      _mesa_error(ctx, GL_INVALID_OPERATION,
1906                  "glObjectUnpurgeable(name = 0x%x) object is "
1907                  " already \"unpurged\"", name);
1908      return 0;
1909   }
1910
1911   bufObj->Purgeable = GL_FALSE;
1912
1913   retval = GL_RETAINED_APPLE;
1914   if (ctx->Driver.RenderObjectUnpurgeable)
1915      retval = ctx->Driver.RenderObjectUnpurgeable(ctx, bufObj, option);
1916
1917   return option;
1918}
1919
1920
1921static GLenum
1922_mesa_TextureObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option)
1923{
1924   struct gl_texture_object *bufObj;
1925   GLenum retval;
1926
1927   bufObj = _mesa_lookup_texture(ctx, name);
1928   if (!bufObj) {
1929      _mesa_error(ctx, GL_INVALID_VALUE,
1930                  "glObjectUnpurgeable(name = 0x%x)", name);
1931      return 0;
1932   }
1933
1934   if (! bufObj->Purgeable) {
1935      _mesa_error(ctx, GL_INVALID_OPERATION,
1936                  "glObjectUnpurgeable(name = 0x%x) object is"
1937                  " already \"unpurged\"", name);
1938      return 0;
1939   }
1940
1941   bufObj->Purgeable = GL_FALSE;
1942
1943   retval = GL_RETAINED_APPLE;
1944   if (ctx->Driver.TextureObjectUnpurgeable)
1945      retval = ctx->Driver.TextureObjectUnpurgeable(ctx, bufObj, option);
1946
1947   return retval;
1948}
1949
1950
1951GLenum GLAPIENTRY
1952_mesa_ObjectUnpurgeableAPPLE(GLenum objectType, GLuint name, GLenum option)
1953{
1954   GET_CURRENT_CONTEXT(ctx);
1955   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
1956
1957   if (name == 0) {
1958      _mesa_error(ctx, GL_INVALID_VALUE,
1959                  "glObjectUnpurgeable(name = 0x%x)", name);
1960      return 0;
1961   }
1962
1963   switch (option) {
1964   case GL_RETAINED_APPLE:
1965   case GL_UNDEFINED_APPLE:
1966      /* legal */
1967      break;
1968   default:
1969      _mesa_error(ctx, GL_INVALID_ENUM,
1970                  "glObjectUnpurgeable(name = 0x%x) invalid option: %d",
1971                  name, option);
1972      return 0;
1973   }
1974
1975   switch (objectType) {
1976   case GL_BUFFER_OBJECT_APPLE:
1977      return _mesa_BufferObjectUnpurgeable(ctx, name, option);
1978   case GL_TEXTURE:
1979      return _mesa_TextureObjectUnpurgeable(ctx, name, option);
1980   case GL_RENDERBUFFER_EXT:
1981      return _mesa_RenderObjectUnpurgeable(ctx, name, option);
1982   default:
1983      _mesa_error(ctx, GL_INVALID_ENUM,
1984                  "glObjectUnpurgeable(name = 0x%x) invalid type: %d",
1985                  name, objectType);
1986      return 0;
1987   }
1988}
1989
1990
1991static void
1992_mesa_GetBufferObjectParameterivAPPLE(GLcontext *ctx, GLuint name,
1993                                      GLenum pname, GLint* params)
1994{
1995   struct gl_buffer_object *bufObj;
1996
1997   bufObj = _mesa_lookup_bufferobj(ctx, name);
1998   if (!bufObj) {
1999      _mesa_error(ctx, GL_INVALID_VALUE,
2000                  "glGetObjectParameteriv(name = 0x%x) invalid object", name);
2001      return;
2002   }
2003
2004   switch (pname) {
2005   case GL_PURGEABLE_APPLE:
2006      *params = bufObj->Purgeable;
2007      break;
2008   default:
2009      _mesa_error(ctx, GL_INVALID_ENUM,
2010                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
2011                  name, pname);
2012      break;
2013   }
2014}
2015
2016
2017static void
2018_mesa_GetRenderObjectParameterivAPPLE(GLcontext *ctx, GLuint name,
2019                                      GLenum pname, GLint* params)
2020{
2021   struct gl_renderbuffer *bufObj;
2022
2023   bufObj = _mesa_lookup_renderbuffer(ctx, name);
2024   if (!bufObj) {
2025      _mesa_error(ctx, GL_INVALID_VALUE,
2026                  "glObjectUnpurgeable(name = 0x%x)", name);
2027      return;
2028   }
2029
2030   switch (pname) {
2031   case GL_PURGEABLE_APPLE:
2032      *params = bufObj->Purgeable;
2033      break;
2034   default:
2035      _mesa_error(ctx, GL_INVALID_ENUM,
2036                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
2037                  name, pname);
2038      break;
2039   }
2040}
2041
2042
2043static void
2044_mesa_GetTextureObjectParameterivAPPLE(GLcontext *ctx, GLuint name,
2045                                       GLenum pname, GLint* params)
2046{
2047   struct gl_texture_object *bufObj;
2048
2049   bufObj = _mesa_lookup_texture(ctx, name);
2050   if (!bufObj) {
2051      _mesa_error(ctx, GL_INVALID_VALUE,
2052                  "glObjectUnpurgeable(name = 0x%x)", name);
2053      return;
2054   }
2055
2056   switch (pname) {
2057   case GL_PURGEABLE_APPLE:
2058      *params = bufObj->Purgeable;
2059      break;
2060   default:
2061      _mesa_error(ctx, GL_INVALID_ENUM,
2062                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
2063                  name, pname);
2064      break;
2065   }
2066}
2067
2068
2069void GLAPIENTRY
2070_mesa_GetObjectParameterivAPPLE(GLenum objectType, GLuint name, GLenum pname,
2071                                GLint* params)
2072{
2073   GET_CURRENT_CONTEXT(ctx);
2074
2075   if (name == 0) {
2076      _mesa_error(ctx, GL_INVALID_VALUE,
2077                  "glGetObjectParameteriv(name = 0x%x)", name);
2078      return;
2079   }
2080
2081   switch (objectType) {
2082   case GL_TEXTURE:
2083      _mesa_GetTextureObjectParameterivAPPLE (ctx, name, pname, params);
2084      break;
2085   case GL_BUFFER_OBJECT_APPLE:
2086      _mesa_GetBufferObjectParameterivAPPLE (ctx, name, pname, params);
2087      break;
2088   case GL_RENDERBUFFER_EXT:
2089      _mesa_GetRenderObjectParameterivAPPLE (ctx, name, pname, params);
2090      break;
2091   default:
2092      _mesa_error(ctx, GL_INVALID_ENUM,
2093                  "glGetObjectParameteriv(name = 0x%x) invalid type: %d",
2094                  name, objectType);
2095   }
2096}
2097
2098#endif /* FEATURE_APPLE_object_purgeable */
2099