fbobject.c revision 91802fdf730451aaa0246f514f6778ffaef92c50
1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5
4 *
5 * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26/*
27 * Authors:
28 *   Brian Paul
29 */
30
31
32#include "context.h"
33#include "fbobject.h"
34#include "framebuffer.h"
35#include "hash.h"
36#include "renderbuffer.h"
37#include "teximage.h"
38#include "texstore.h"
39
40
41/**
42 * Notes:
43 *
44 * None of the GL_EXT_framebuffer_object functions are compiled into
45 * display lists.
46 */
47
48
49
50/*
51 * When glGenRender/FramebuffersEXT() is called we insert pointers to
52 * these placeholder objects into the hash table.
53 * Later, when the object ID is first bound, we replace the placeholder
54 * with the real frame/renderbuffer.
55 */
56static struct gl_framebuffer DummyFramebuffer;
57static struct gl_renderbuffer DummyRenderbuffer;
58
59
60#define IS_CUBE_FACE(TARGET) \
61   ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
62    (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
63
64
65/**
66 * Helper routine for getting a gl_renderbuffer.
67 */
68static struct gl_renderbuffer *
69lookup_renderbuffer(GLcontext *ctx, GLuint id)
70{
71   struct gl_renderbuffer *rb;
72
73   if (id == 0)
74      return NULL;
75
76   rb = (struct gl_renderbuffer *)
77      _mesa_HashLookup(ctx->Shared->RenderBuffers, id);
78   return rb;
79}
80
81
82/**
83 * Helper routine for getting a gl_framebuffer.
84 */
85static struct gl_framebuffer *
86lookup_framebuffer(GLcontext *ctx, GLuint id)
87{
88   struct gl_framebuffer *fb;
89
90   if (id == 0)
91      return NULL;
92
93   fb = (struct gl_framebuffer *)
94      _mesa_HashLookup(ctx->Shared->FrameBuffers, id);
95   return fb;
96}
97
98
99/**
100 * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
101 * gl_renderbuffer_attachment object.
102 */
103static struct gl_renderbuffer_attachment *
104get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, GLenum attachment)
105{
106   GLuint i;
107
108   switch (attachment) {
109   case GL_COLOR_ATTACHMENT0_EXT:
110   case GL_COLOR_ATTACHMENT1_EXT:
111   case GL_COLOR_ATTACHMENT2_EXT:
112   case GL_COLOR_ATTACHMENT3_EXT:
113   case GL_COLOR_ATTACHMENT4_EXT:
114   case GL_COLOR_ATTACHMENT5_EXT:
115   case GL_COLOR_ATTACHMENT6_EXT:
116   case GL_COLOR_ATTACHMENT7_EXT:
117   case GL_COLOR_ATTACHMENT8_EXT:
118   case GL_COLOR_ATTACHMENT9_EXT:
119   case GL_COLOR_ATTACHMENT10_EXT:
120   case GL_COLOR_ATTACHMENT11_EXT:
121   case GL_COLOR_ATTACHMENT12_EXT:
122   case GL_COLOR_ATTACHMENT13_EXT:
123   case GL_COLOR_ATTACHMENT14_EXT:
124   case GL_COLOR_ATTACHMENT15_EXT:
125      i = attachment - GL_COLOR_ATTACHMENT0_EXT;
126      if (i >= ctx->Const.MaxColorAttachments) {
127	 return NULL;
128      }
129      return &fb->Attachment[BUFFER_COLOR0 + i];
130   case GL_DEPTH_ATTACHMENT_EXT:
131      return &fb->Attachment[BUFFER_DEPTH];
132   case GL_STENCIL_ATTACHMENT_EXT:
133      return &fb->Attachment[BUFFER_STENCIL];
134   default:
135      return NULL;
136   }
137}
138
139
140/**
141 * Remove any texture or renderbuffer attached to the given attachment
142 * point.  Update reference counts, etc.
143 */
144void
145_mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
146{
147   if (att->Type == GL_TEXTURE) {
148      ASSERT(att->Texture);
149      if (att->Renderbuffer) {
150         /* delete/remove the 'wrapper' renderbuffer */
151         /* XXX do we really want to do this??? */
152         att->Renderbuffer->Delete(att->Renderbuffer);
153         att->Renderbuffer = NULL;
154      }
155      att->Texture->RefCount--;
156      if (att->Texture->RefCount == 0) {
157	 ctx->Driver.DeleteTexture(ctx, att->Texture);
158      }
159      att->Texture = NULL;
160   }
161   else if (att->Type == GL_RENDERBUFFER_EXT) {
162      ASSERT(att->Renderbuffer);
163      ASSERT(!att->Texture);
164      att->Renderbuffer->RefCount--;
165      if (att->Renderbuffer->RefCount == 0) {
166         att->Renderbuffer->Delete(att->Renderbuffer);
167      }
168      att->Renderbuffer = NULL;
169   }
170   att->Type = GL_NONE;
171   att->Complete = GL_TRUE;
172}
173
174
175/**
176 * Bind a texture object to an attachment point.
177 * The previous binding, if any, will be removed first.
178 */
179void
180_mesa_set_texture_attachment(GLcontext *ctx,
181                             struct gl_renderbuffer_attachment *att,
182                             struct gl_texture_object *texObj,
183                             GLenum texTarget, GLuint level, GLuint zoffset)
184{
185   _mesa_remove_attachment(ctx, att);
186   att->Type = GL_TEXTURE;
187   att->Texture = texObj;
188   att->TextureLevel = level;
189   if (IS_CUBE_FACE(texTarget)) {
190      att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
191   }
192   else {
193      att->CubeMapFace = 0;
194   }
195   att->Zoffset = zoffset;
196   att->Complete = GL_FALSE;
197
198   texObj->RefCount++;
199
200   /* XXX when we attach to a texture, we should probably set the
201    * att->Renderbuffer pointer to a "wrapper renderbuffer" which
202    * makes the texture image look like renderbuffer.
203    */
204}
205
206
207/**
208 * Bind a renderbuffer to an attachment point.
209 * The previous binding, if any, will be removed first.
210 */
211void
212_mesa_set_renderbuffer_attachment(GLcontext *ctx,
213                                  struct gl_renderbuffer_attachment *att,
214                                  struct gl_renderbuffer *rb)
215{
216   _mesa_remove_attachment(ctx, att);
217   att->Type = GL_RENDERBUFFER_EXT;
218   att->Renderbuffer = rb;
219   att->Texture = NULL; /* just to be safe */
220   att->Complete = GL_FALSE;
221   rb->RefCount++;
222}
223
224
225/**
226 * Fallback for ctx->Driver.FramebufferRenderbuffer()
227 * Sets a framebuffer attachment to a particular renderbuffer.
228 * The framebuffer in question is ctx->DrawBuffer.
229 * \sa _mesa_renderbuffer_texture
230 */
231void
232_mesa_framebuffer_renderbuffer(GLcontext *ctx,
233                               struct gl_renderbuffer_attachment *att,
234                               struct gl_renderbuffer *rb)
235{
236   if (rb) {
237      _mesa_set_renderbuffer_attachment(ctx, att, rb);
238   }
239   else {
240      _mesa_remove_attachment(ctx, att);
241   }
242}
243
244
245/**
246 * Test if an attachment point is complete and update its Complete field.
247 * \param format if GL_COLOR, this is a color attachment point,
248 *               if GL_DEPTH, this is a depth component attachment point,
249 *               if GL_STENCIL, this is a stencil component attachment point.
250 */
251static void
252test_attachment_completeness(const GLcontext *ctx, GLenum format,
253                             struct gl_renderbuffer_attachment *att)
254{
255   assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL);
256
257   /* assume complete */
258   att->Complete = GL_TRUE;
259
260   /* Look for reasons why the attachment might be incomplete */
261   if (att->Type == GL_TEXTURE) {
262      const struct gl_texture_object *texObj = att->Texture;
263      struct gl_texture_image *texImage;
264
265      if (!texObj) {
266         att->Complete = GL_FALSE;
267         return;
268      }
269
270      texImage = texObj->Image[att->CubeMapFace][att->TextureLevel];
271      if (!texImage) {
272         att->Complete = GL_FALSE;
273         return;
274      }
275      if (texImage->Width < 1 || texImage->Height < 1) {
276         att->Complete = GL_FALSE;
277         return;
278      }
279      if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) {
280         att->Complete = GL_FALSE;
281         return;
282      }
283
284      if (format == GL_COLOR) {
285         if (texImage->TexFormat->BaseFormat != GL_RGB &&
286             texImage->TexFormat->BaseFormat != GL_RGBA) {
287            att->Complete = GL_FALSE;
288            return;
289         }
290      }
291      else if (format == GL_DEPTH) {
292         if (texImage->TexFormat->BaseFormat == GL_DEPTH_COMPONENT) {
293            /* OK */
294         }
295         else if (ctx->Extensions.EXT_packed_depth_stencil &&
296                  att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
297            /* OK */
298         }
299         else {
300            att->Complete = GL_FALSE;
301            return;
302         }
303      }
304      else {
305         /* no such thing as stencil textures */
306         att->Complete = GL_FALSE;
307         return;
308      }
309   }
310   else if (att->Type == GL_RENDERBUFFER_EXT) {
311      if (att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) {
312         att->Complete = GL_FALSE;
313         return;
314      }
315      if (format == GL_COLOR) {
316         if (att->Renderbuffer->_BaseFormat != GL_RGB &&
317             att->Renderbuffer->_BaseFormat != GL_RGBA) {
318            att->Complete = GL_FALSE;
319            return;
320         }
321      }
322      else if (format == GL_DEPTH) {
323         if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
324            /* OK */
325         }
326         else if (ctx->Extensions.EXT_packed_depth_stencil &&
327                  att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
328            /* OK */
329         }
330         else {
331            att->Complete = GL_FALSE;
332            return;
333         }
334      }
335      else {
336         assert(format == GL_STENCIL);
337         if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
338            /* OK */
339         }
340         else if (ctx->Extensions.EXT_packed_depth_stencil &&
341                  att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
342            /* OK */
343         }
344         else {
345            att->Complete = GL_FALSE;
346            return;
347         }
348      }
349   }
350   else {
351      ASSERT(att->Type == GL_NONE);
352      /* complete */
353      return;
354   }
355}
356
357
358/**
359 * Test if the given framebuffer object is complete and update its
360 * Status field with the results.
361 * Also update the framebuffer's Width and Height fields if the
362 * framebuffer is complete.
363 */
364void
365_mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
366{
367   GLuint numImages, width = 0, height = 0;
368   GLenum intFormat = GL_NONE;
369   GLuint w = 0, h = 0;
370   GLint i;
371
372   assert(fb->Name != 0);
373
374   numImages = 0;
375   fb->Width = 0;
376   fb->Height = 0;
377
378   /* Start at -2 to more easily loop over all attachment points */
379   for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) {
380      struct gl_renderbuffer_attachment *att;
381      GLenum f;
382
383      if (i == -2) {
384         att = &fb->Attachment[BUFFER_DEPTH];
385         test_attachment_completeness(ctx, GL_DEPTH, att);
386         if (!att->Complete) {
387            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
388            return;
389         }
390      }
391      else if (i == -1) {
392         att = &fb->Attachment[BUFFER_STENCIL];
393         test_attachment_completeness(ctx, GL_STENCIL, att);
394         if (!att->Complete) {
395            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
396            return;
397         }
398      }
399      else {
400         att = &fb->Attachment[BUFFER_COLOR0 + i];
401         test_attachment_completeness(ctx, GL_COLOR, att);
402         if (!att->Complete) {
403            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
404            return;
405         }
406      }
407
408      if (att->Type == GL_TEXTURE) {
409         w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width;
410         h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height;
411         f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format;
412         numImages++;
413         if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) {
414            /* XXX need GL_DEPTH_STENCIL_EXT test? */
415            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
416            return;
417         }
418      }
419      else if (att->Type == GL_RENDERBUFFER_EXT) {
420         w = att->Renderbuffer->Width;
421         h = att->Renderbuffer->Height;
422         f = att->Renderbuffer->InternalFormat;
423         numImages++;
424      }
425      else {
426         assert(att->Type == GL_NONE);
427         continue;
428      }
429
430      if (numImages == 1) {
431         /* set required width, height and format */
432         width = w;
433         height = h;
434         if (i >= 0)
435            intFormat = f;
436      }
437      else {
438         /* check that width, height, format are same */
439         if (w != width || h != height) {
440            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
441            return;
442         }
443         if (intFormat != GL_NONE && f != intFormat) {
444            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
445            return;
446         }
447      }
448   }
449
450   /* Check that all DrawBuffers are present */
451   for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
452      if (fb->ColorDrawBuffer[i] != GL_NONE) {
453         const struct gl_renderbuffer_attachment *att
454            = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
455         assert(att);
456         if (att->Type == GL_NONE) {
457            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
458            return;
459         }
460      }
461   }
462
463   /* Check that the ReadBuffer is present */
464   if (fb->ColorReadBuffer != GL_NONE) {
465      const struct gl_renderbuffer_attachment *att
466         = get_attachment(ctx, fb, fb->ColorReadBuffer);
467      assert(att);
468      if (att->Type == GL_NONE) {
469         fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
470         return;
471      }
472   }
473
474   /* Check if any renderbuffer is attached more than once.
475    * Note that there's one exception: a GL_DEPTH_STENCIL renderbuffer can be
476    * bound to both the stencil and depth attachment points at the same time.
477    */
478   for (i = 0; i < BUFFER_COUNT - 1; i++) {
479      struct gl_renderbuffer *rb_i = fb->Attachment[i].Renderbuffer;
480      if (rb_i) {
481         GLint j;
482         for (j = i + 1; j < BUFFER_COUNT; j++) {
483            struct gl_renderbuffer *rb_j = fb->Attachment[j].Renderbuffer;
484            if (rb_i == rb_j && rb_i->_BaseFormat != GL_DEPTH_STENCIL_EXT) {
485               fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT;
486               return;
487            }
488         }
489      }
490   }
491
492
493   if (numImages == 0) {
494      fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
495      return;
496   }
497
498   /*
499    * If we get here, the framebuffer is complete!
500    */
501   fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT;
502   fb->Width = w;
503   fb->Height = h;
504}
505
506
507GLboolean GLAPIENTRY
508_mesa_IsRenderbufferEXT(GLuint renderbuffer)
509{
510   GET_CURRENT_CONTEXT(ctx);
511   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
512   if (renderbuffer) {
513      struct gl_renderbuffer *rb = lookup_renderbuffer(ctx, renderbuffer);
514      if (rb != NULL && rb != &DummyRenderbuffer)
515         return GL_TRUE;
516   }
517   return GL_FALSE;
518}
519
520
521void GLAPIENTRY
522_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
523{
524   struct gl_renderbuffer *newRb, *oldRb;
525   GET_CURRENT_CONTEXT(ctx);
526
527   ASSERT_OUTSIDE_BEGIN_END(ctx);
528   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
529
530   if (target != GL_RENDERBUFFER_EXT) {
531         _mesa_error(ctx, GL_INVALID_ENUM,
532                  "glBindRenderbufferEXT(target)");
533      return;
534   }
535
536   if (renderbuffer) {
537      newRb = lookup_renderbuffer(ctx, renderbuffer);
538      if (newRb == &DummyRenderbuffer) {
539         /* ID was reserved, but no real renderbuffer object made yet */
540         newRb = NULL;
541      }
542      if (!newRb) {
543	 /* create new renderbuffer object */
544	 newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
545	 if (!newRb) {
546	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
547	    return;
548	 }
549         ASSERT(newRb->AllocStorage);
550         _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
551      }
552      newRb->RefCount++;
553   }
554   else {
555      newRb = NULL;
556   }
557
558   oldRb = ctx->CurrentRenderbuffer;
559   if (oldRb) {
560      oldRb->RefCount--;
561      if (oldRb->RefCount == 0) {
562         oldRb->Delete(oldRb);
563      }
564   }
565
566   ASSERT(newRb != &DummyRenderbuffer);
567
568   ctx->CurrentRenderbuffer = newRb;
569}
570
571
572void GLAPIENTRY
573_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
574{
575   GLint i;
576   GET_CURRENT_CONTEXT(ctx);
577
578   ASSERT_OUTSIDE_BEGIN_END(ctx);
579
580   for (i = 0; i < n; i++) {
581      if (renderbuffers[i] > 0) {
582	 struct gl_renderbuffer *rb;
583	 rb = lookup_renderbuffer(ctx, renderbuffers[i]);
584	 if (rb) {
585            /* check if deleting currently bound renderbuffer object */
586            if (rb == ctx->CurrentRenderbuffer) {
587               /* bind default */
588               ASSERT(rb->RefCount >= 2);
589               _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
590            }
591
592	    /* remove from hash table immediately, to free the ID */
593	    _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
594
595            if (rb != &DummyRenderbuffer) {
596               /* But the object will not be freed until it's no longer
597                * bound in any context.
598                */
599               rb->RefCount--;
600               if (rb->RefCount == 0) {
601                  rb->Delete(rb);
602               }
603	    }
604	 }
605      }
606   }
607}
608
609
610void GLAPIENTRY
611_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
612{
613   GET_CURRENT_CONTEXT(ctx);
614   GLuint first;
615   GLint i;
616
617   ASSERT_OUTSIDE_BEGIN_END(ctx);
618
619   if (n < 0) {
620      _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
621      return;
622   }
623
624   if (!renderbuffers)
625      return;
626
627   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
628
629   for (i = 0; i < n; i++) {
630      GLuint name = first + i;
631      renderbuffers[i] = name;
632      /* insert dummy placeholder into hash table */
633      _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
634      _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
635      _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
636   }
637}
638
639
640/**
641 * Given an internal format token for a render buffer, return the
642 * corresponding base format.
643 * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
644 *  GL_DEPTH_STENCIL_EXT or zero if error.
645 */
646static GLenum
647base_internal_format(GLcontext *ctx, GLenum internalFormat)
648{
649   switch (internalFormat) {
650   case GL_RGB:
651   case GL_R3_G3_B2:
652   case GL_RGB4:
653   case GL_RGB5:
654   case GL_RGB8:
655   case GL_RGB10:
656   case GL_RGB12:
657   case GL_RGB16:
658      return GL_RGB;
659   case GL_RGBA:
660   case GL_RGBA2:
661   case GL_RGBA4:
662   case GL_RGB5_A1:
663   case GL_RGBA8:
664   case GL_RGB10_A2:
665   case GL_RGBA12:
666   case GL_RGBA16:
667      return GL_RGBA;
668   case GL_STENCIL_INDEX:
669   case GL_STENCIL_INDEX1_EXT:
670   case GL_STENCIL_INDEX4_EXT:
671   case GL_STENCIL_INDEX8_EXT:
672   case GL_STENCIL_INDEX16_EXT:
673      return GL_STENCIL_INDEX;
674   case GL_DEPTH_COMPONENT:
675   case GL_DEPTH_COMPONENT16:
676   case GL_DEPTH_COMPONENT24:
677   case GL_DEPTH_COMPONENT32:
678      return GL_DEPTH_COMPONENT;
679   case GL_DEPTH_STENCIL_EXT:
680   case GL_DEPTH24_STENCIL8_EXT:
681      if (ctx->Extensions.EXT_packed_depth_stencil)
682         return GL_DEPTH_STENCIL_EXT;
683      else
684         return 0;
685   /* XXX add floating point formats eventually */
686   default:
687      return 0;
688   }
689}
690
691
692void GLAPIENTRY
693_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
694                             GLsizei width, GLsizei height)
695{
696   struct gl_renderbuffer *rb;
697   GLenum baseFormat;
698   GET_CURRENT_CONTEXT(ctx);
699
700   ASSERT_OUTSIDE_BEGIN_END(ctx);
701   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
702
703   if (target != GL_RENDERBUFFER_EXT) {
704      _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
705      return;
706   }
707
708   baseFormat = base_internal_format(ctx, internalFormat);
709   if (baseFormat == 0) {
710      _mesa_error(ctx, GL_INVALID_ENUM,
711                  "glRenderbufferStorageEXT(internalFormat)");
712      return;
713   }
714
715   if (width < 1 || width > ctx->Const.MaxRenderbufferSize) {
716      _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
717      return;
718   }
719
720   if (height < 1 || height > ctx->Const.MaxRenderbufferSize) {
721      _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
722      return;
723   }
724
725   rb = ctx->CurrentRenderbuffer;
726
727   if (!rb) {
728      _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
729      return;
730   }
731
732   /* Now allocate the storage */
733   ASSERT(rb->AllocStorage);
734   if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
735      /* No error - check/set fields now */
736      assert(rb->Width == width);
737      assert(rb->Height == height);
738      assert(rb->InternalFormat);
739      rb->_BaseFormat = baseFormat;
740   }
741   else {
742      /* Probably ran out of memory - clear the fields */
743      rb->Width = 0;
744      rb->Height = 0;
745      rb->InternalFormat = GL_NONE;
746      rb->_BaseFormat = GL_NONE;
747   }
748
749   /*
750   test_framebuffer_completeness(ctx, fb);
751   */
752   /* XXX if this renderbuffer is attached anywhere, invalidate attachment
753    * points???
754    */
755}
756
757
758void GLAPIENTRY
759_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
760{
761   GET_CURRENT_CONTEXT(ctx);
762
763   ASSERT_OUTSIDE_BEGIN_END(ctx);
764
765   if (target != GL_RENDERBUFFER_EXT) {
766      _mesa_error(ctx, GL_INVALID_ENUM,
767                  "glGetRenderbufferParameterivEXT(target)");
768      return;
769   }
770
771   if (!ctx->CurrentRenderbuffer) {
772      _mesa_error(ctx, GL_INVALID_OPERATION,
773                  "glGetRenderbufferParameterivEXT");
774      return;
775   }
776
777   switch (pname) {
778   case GL_RENDERBUFFER_WIDTH_EXT:
779      *params = ctx->CurrentRenderbuffer->Width;
780      return;
781   case GL_RENDERBUFFER_HEIGHT_EXT:
782      *params = ctx->CurrentRenderbuffer->Height;
783      return;
784   case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
785      *params = ctx->CurrentRenderbuffer->InternalFormat;
786      return;
787   case GL_RENDERBUFFER_RED_SIZE_EXT:
788      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
789          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
790         *params = ctx->CurrentRenderbuffer->RedBits;
791      }
792      else {
793         *params = 0;
794      }
795      break;
796   case GL_RENDERBUFFER_GREEN_SIZE_EXT:
797      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
798          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
799         *params = ctx->CurrentRenderbuffer->GreenBits;
800      }
801      else {
802         *params = 0;
803      }
804      break;
805   case GL_RENDERBUFFER_BLUE_SIZE_EXT:
806      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
807          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
808         *params = ctx->CurrentRenderbuffer->BlueBits;
809      }
810      else {
811         *params = 0;
812      }
813      break;
814   case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
815      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
816          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
817         *params = ctx->CurrentRenderbuffer->AlphaBits;
818      }
819      else {
820         *params = 0;
821      }
822      break;
823   case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
824      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT ||
825          ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
826         *params = ctx->CurrentRenderbuffer->DepthBits;
827      }
828      else {
829         *params = 0;
830      }
831      break;
832   case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
833      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX ||
834          ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
835         *params = ctx->CurrentRenderbuffer->StencilBits;
836      }
837      else {
838         *params = 0;
839      }
840      break;
841
842   default:
843      _mesa_error(ctx, GL_INVALID_ENUM,
844                  "glGetRenderbufferParameterivEXT(target)");
845      return;
846   }
847}
848
849
850GLboolean GLAPIENTRY
851_mesa_IsFramebufferEXT(GLuint framebuffer)
852{
853   GET_CURRENT_CONTEXT(ctx);
854   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
855   if (framebuffer) {
856      struct gl_framebuffer *rb = lookup_framebuffer(ctx, framebuffer);
857      if (rb != NULL && rb != &DummyFramebuffer)
858         return GL_TRUE;
859   }
860   return GL_FALSE;
861}
862
863
864void GLAPIENTRY
865_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
866{
867   struct gl_framebuffer *newFb, *newReadFb, *oldFb;
868   GET_CURRENT_CONTEXT(ctx);
869
870   ASSERT_OUTSIDE_BEGIN_END(ctx);
871   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
872
873   if (target != GL_FRAMEBUFFER_EXT) {
874      _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
875      return;
876   }
877
878   if (framebuffer) {
879      /* Binding a user-created framebuffer object */
880      newFb = lookup_framebuffer(ctx, framebuffer);
881      if (newFb == &DummyFramebuffer) {
882         /* ID was reserved, but no real framebuffer object made yet */
883         newFb = NULL;
884      }
885      if (!newFb) {
886	 /* create new framebuffer object */
887	 newFb = ctx->Driver.NewFramebuffer(ctx, framebuffer);
888	 if (!newFb) {
889	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
890	    return;
891	 }
892         _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
893      }
894      newFb->RefCount++;
895      newReadFb = newFb;
896   }
897   else {
898      /* Binding the window system framebuffer (which was originally set
899       * with MakeCurrent).
900       */
901      newFb = ctx->WinSysDrawBuffer;
902      newReadFb = ctx->WinSysReadBuffer;
903   }
904
905   oldFb = ctx->DrawBuffer;
906   if (oldFb && oldFb->Name != 0) {
907      oldFb->RefCount--;
908      if (oldFb->RefCount == 0) {
909         oldFb->Delete(oldFb);
910      }
911   }
912
913   ASSERT(newFb != &DummyFramebuffer);
914
915   /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
916   ctx->DrawBuffer = newFb;
917   ctx->ReadBuffer = newReadFb;
918}
919
920
921void GLAPIENTRY
922_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
923{
924   GLint i;
925   GET_CURRENT_CONTEXT(ctx);
926
927   ASSERT_OUTSIDE_BEGIN_END(ctx);
928
929   for (i = 0; i < n; i++) {
930      if (framebuffers[i] > 0) {
931	 struct gl_framebuffer *fb;
932	 fb = lookup_framebuffer(ctx, framebuffers[i]);
933	 if (fb) {
934            ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
935
936            /* check if deleting currently bound framebuffer object */
937            if (fb == ctx->DrawBuffer) {
938               /* bind default */
939               ASSERT(fb->RefCount >= 2);
940               _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
941            }
942
943	    /* remove from hash table immediately, to free the ID */
944	    _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
945
946            if (fb != &DummyFramebuffer) {
947               /* But the object will not be freed until it's no longer
948                * bound in any context.
949                */
950               fb->RefCount--;
951               if (fb->RefCount == 0) {
952                  fb->Delete(fb);
953               }
954	    }
955	 }
956      }
957   }
958}
959
960
961void GLAPIENTRY
962_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
963{
964   GET_CURRENT_CONTEXT(ctx);
965   GLuint first;
966   GLint i;
967
968   ASSERT_OUTSIDE_BEGIN_END(ctx);
969
970   if (n < 0) {
971      _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
972      return;
973   }
974
975   if (!framebuffers)
976      return;
977
978   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
979
980   for (i = 0; i < n; i++) {
981      GLuint name = first + i;
982      framebuffers[i] = name;
983      /* insert dummy placeholder into hash table */
984      _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
985      _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
986      _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
987   }
988}
989
990
991
992GLenum GLAPIENTRY
993_mesa_CheckFramebufferStatusEXT(GLenum target)
994{
995   GET_CURRENT_CONTEXT(ctx);
996
997   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
998
999   if (target != GL_FRAMEBUFFER_EXT) {
1000      _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1001      return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
1002   }
1003
1004   if (ctx->DrawBuffer->Name == 0) {
1005      /* The window system / default framebuffer is always complete */
1006      return GL_FRAMEBUFFER_COMPLETE_EXT;
1007   }
1008
1009   _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
1010   return ctx->DrawBuffer->_Status;
1011}
1012
1013
1014
1015/**
1016 * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
1017 * \return GL_TRUE if any error, GL_FALSE otherwise
1018 */
1019static GLboolean
1020error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
1021                                GLenum target, GLenum attachment,
1022                                GLenum textarget, GLuint texture, GLint level)
1023{
1024   ASSERT(dims >= 1 && dims <= 3);
1025
1026   if (target != GL_FRAMEBUFFER_EXT) {
1027      _mesa_error(ctx, GL_INVALID_ENUM,
1028                  "glFramebufferTexture%dDEXT(target)", dims);
1029      return GL_TRUE;
1030   }
1031
1032   /* check framebuffer binding */
1033   if (ctx->DrawBuffer->Name == 0) {
1034      _mesa_error(ctx, GL_INVALID_OPERATION,
1035                  "glFramebufferTexture%dDEXT", dims);
1036      return GL_TRUE;
1037   }
1038
1039   /* only check textarget, level if texture ID is non-zero */
1040   if (texture) {
1041      if ((dims == 1 && textarget != GL_TEXTURE_1D) ||
1042	  (dims == 3 && textarget != GL_TEXTURE_3D) ||
1043	  (dims == 2 && textarget != GL_TEXTURE_2D &&
1044	   textarget != GL_TEXTURE_RECTANGLE_ARB &&
1045	   !IS_CUBE_FACE(textarget))) {
1046	 _mesa_error(ctx, GL_INVALID_VALUE,
1047		     "glFramebufferTexture%dDEXT(textarget)", dims);
1048	 return GL_TRUE;
1049      }
1050
1051      if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
1052	 _mesa_error(ctx, GL_INVALID_VALUE,
1053		     "glFramebufferTexture%dDEXT(level)", dims);
1054	 return GL_TRUE;
1055      }
1056   }
1057
1058   return GL_FALSE;
1059}
1060
1061
1062void GLAPIENTRY
1063_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1064                              GLenum textarget, GLuint texture, GLint level)
1065{
1066   struct gl_renderbuffer_attachment *att;
1067   struct gl_texture_object *texObj;
1068   GET_CURRENT_CONTEXT(ctx);
1069
1070   ASSERT_OUTSIDE_BEGIN_END(ctx);
1071   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1072
1073   if (error_check_framebuffer_texture(ctx, 1, target, attachment,
1074				       textarget, texture, level))
1075      return;
1076
1077   ASSERT(textarget == GL_TEXTURE_1D);
1078
1079   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1080   if (att == NULL) {
1081      _mesa_error(ctx, GL_INVALID_ENUM,
1082		  "glFramebufferTexture1DEXT(attachment)");
1083      return;
1084   }
1085
1086   if (texture) {
1087      texObj = (struct gl_texture_object *)
1088	 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1089      if (!texObj) {
1090	 _mesa_error(ctx, GL_INVALID_VALUE,
1091		     "glFramebufferTexture1DEXT(texture)");
1092	 return;
1093      }
1094      if (texObj->Target != textarget) {
1095	 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1096		     "glFramebufferTexture1DEXT(texture target)");
1097	 return;
1098      }
1099   }
1100   else {
1101      /* remove texture attachment */
1102      texObj = NULL;
1103   }
1104   ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1105
1106   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1107}
1108
1109
1110void GLAPIENTRY
1111_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1112                              GLenum textarget, GLuint texture, GLint level)
1113{
1114   struct gl_renderbuffer_attachment *att;
1115   struct gl_texture_object *texObj;
1116   GET_CURRENT_CONTEXT(ctx);
1117
1118   ASSERT_OUTSIDE_BEGIN_END(ctx);
1119   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1120
1121   if (error_check_framebuffer_texture(ctx, 2, target, attachment,
1122				       textarget, texture, level))
1123      return;
1124
1125   ASSERT(textarget == GL_TEXTURE_2D ||
1126	  textarget == GL_TEXTURE_RECTANGLE_ARB ||
1127	  IS_CUBE_FACE(textarget));
1128
1129   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1130   if (att == NULL) {
1131      _mesa_error(ctx, GL_INVALID_ENUM,
1132		  "glFramebufferTexture2DEXT(attachment)");
1133      return;
1134   }
1135
1136   if (texture) {
1137      texObj = (struct gl_texture_object *)
1138	 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1139      if (!texObj) {
1140	 _mesa_error(ctx, GL_INVALID_VALUE,
1141		     "glFramebufferTexture2DEXT(texture)");
1142	 return;
1143      }
1144      if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
1145	  (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
1146	   && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
1147	  (texObj->Target == GL_TEXTURE_CUBE_MAP
1148	   && !IS_CUBE_FACE(textarget))) {
1149	 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1150		     "glFramebufferTexture2DEXT(texture target)");
1151	 return;
1152      }
1153   }
1154   else {
1155      /* remove texture attachment */
1156      texObj = NULL;
1157   }
1158   ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1159   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1160}
1161
1162
1163void GLAPIENTRY
1164_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1165                              GLenum textarget, GLuint texture,
1166                              GLint level, GLint zoffset)
1167{
1168   struct gl_renderbuffer_attachment *att;
1169   struct gl_texture_object *texObj;
1170   GET_CURRENT_CONTEXT(ctx);
1171
1172   ASSERT_OUTSIDE_BEGIN_END(ctx);
1173   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1174
1175   if (error_check_framebuffer_texture(ctx, 3, target, attachment,
1176				       textarget, texture, level))
1177      return;
1178
1179   ASSERT(textarget == GL_TEXTURE_3D);
1180
1181   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1182   if (att == NULL) {
1183      _mesa_error(ctx, GL_INVALID_ENUM,
1184		  "glFramebufferTexture1DEXT(attachment)");
1185      return;
1186   }
1187
1188   if (texture) {
1189      const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1190      texObj = (struct gl_texture_object *)
1191	 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1192      if (!texObj) {
1193	 _mesa_error(ctx, GL_INVALID_VALUE,
1194		     "glFramebufferTexture3DEXT(texture)");
1195	 return;
1196      }
1197      if (texObj->Target != textarget) {
1198	 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1199		     "glFramebufferTexture3DEXT(texture target)");
1200	 return;
1201      }
1202      if (zoffset < 0 || zoffset >= maxSize) {
1203	 _mesa_error(ctx, GL_INVALID_VALUE,
1204		     "glFramebufferTexture3DEXT(zoffset)");
1205	 return;
1206      }
1207   }
1208   else {
1209      /* remove texture attachment */
1210      texObj = NULL;
1211   }
1212   ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
1213                                   level, zoffset);
1214   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1215}
1216
1217
1218void GLAPIENTRY
1219_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1220                                 GLenum renderbufferTarget,
1221                                 GLuint renderbuffer)
1222{
1223   struct gl_renderbuffer_attachment *att;
1224   struct gl_renderbuffer *rb;
1225   GET_CURRENT_CONTEXT(ctx);
1226
1227   ASSERT_OUTSIDE_BEGIN_END(ctx);
1228   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1229
1230   if (target != GL_FRAMEBUFFER_EXT) {
1231      _mesa_error(ctx, GL_INVALID_ENUM,
1232                  "glFramebufferRenderbufferEXT(target)");
1233      return;
1234   }
1235
1236   if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1237      _mesa_error(ctx, GL_INVALID_ENUM,
1238                  "glFramebufferRenderbufferEXT(renderbufferTarget)");
1239      return;
1240   }
1241
1242   if (ctx->DrawBuffer->Name == 0) {
1243      /* Can't attach new renderbuffers to a window system framebuffer */
1244      _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1245      return;
1246   }
1247
1248   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1249   if (att == NULL) {
1250      _mesa_error(ctx, GL_INVALID_ENUM,
1251                 "glFramebufferRenderbufferEXT(attachment)");
1252      return;
1253   }
1254
1255   if (renderbuffer) {
1256      rb = lookup_renderbuffer(ctx, renderbuffer);
1257      if (!rb) {
1258	 _mesa_error(ctx, GL_INVALID_OPERATION,
1259		     "glFramebufferRenderbufferEXT(renderbuffer)");
1260	 return;
1261      }
1262   }
1263   else {
1264      /* remove renderbuffer attachment */
1265      rb = NULL;
1266   }
1267
1268   assert(ctx->Driver.FramebufferRenderbuffer);
1269   ctx->Driver.FramebufferRenderbuffer(ctx, att, rb);
1270
1271   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1272}
1273
1274
1275void GLAPIENTRY
1276_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1277                                             GLenum pname, GLint *params)
1278{
1279   const struct gl_renderbuffer_attachment *att;
1280   GET_CURRENT_CONTEXT(ctx);
1281
1282   ASSERT_OUTSIDE_BEGIN_END(ctx);
1283
1284   if (target != GL_FRAMEBUFFER_EXT) {
1285      _mesa_error(ctx, GL_INVALID_ENUM,
1286                  "glGetFramebufferAttachmentParameterivEXT(target)");
1287      return;
1288   }
1289
1290   if (ctx->DrawBuffer->Name == 0) {
1291      _mesa_error(ctx, GL_INVALID_OPERATION,
1292                  "glGetFramebufferAttachmentParameterivEXT");
1293      return;
1294   }
1295
1296   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1297   if (att == NULL) {
1298      _mesa_error(ctx, GL_INVALID_ENUM,
1299                  "glGetFramebufferAttachmentParameterivEXT(attachment)");
1300      return;
1301   }
1302
1303   switch (pname) {
1304   case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1305      *params = att->Type;
1306      return;
1307   case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1308      if (att->Type == GL_RENDERBUFFER_EXT) {
1309	 *params = att->Renderbuffer->Name;
1310      }
1311      else if (att->Type == GL_TEXTURE) {
1312	 *params = att->Texture->Name;
1313      }
1314      else {
1315	 _mesa_error(ctx, GL_INVALID_ENUM,
1316		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1317      }
1318      return;
1319   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1320      if (att->Type == GL_TEXTURE) {
1321	 *params = att->TextureLevel;
1322      }
1323      else {
1324	 _mesa_error(ctx, GL_INVALID_ENUM,
1325		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1326      }
1327      return;
1328   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1329      if (att->Type == GL_TEXTURE) {
1330	 *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1331      }
1332      else {
1333	 _mesa_error(ctx, GL_INVALID_ENUM,
1334		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1335      }
1336      return;
1337   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1338      if (att->Type == GL_TEXTURE) {
1339	 *params = att->Zoffset;
1340      }
1341      else {
1342	 _mesa_error(ctx, GL_INVALID_ENUM,
1343		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1344      }
1345      return;
1346   default:
1347      _mesa_error(ctx, GL_INVALID_ENUM,
1348                  "glGetFramebufferAttachmentParameterivEXT(pname)");
1349      return;
1350   }
1351}
1352
1353
1354void GLAPIENTRY
1355_mesa_GenerateMipmapEXT(GLenum target)
1356{
1357   struct gl_texture_unit *texUnit;
1358   struct gl_texture_object *texObj;
1359   GET_CURRENT_CONTEXT(ctx);
1360
1361   ASSERT_OUTSIDE_BEGIN_END(ctx);
1362
1363   switch (target) {
1364   case GL_TEXTURE_1D:
1365   case GL_TEXTURE_2D:
1366   case GL_TEXTURE_3D:
1367   case GL_TEXTURE_CUBE_MAP:
1368      /* OK, legal value */
1369      break;
1370   default:
1371      _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1372      return;
1373   }
1374
1375   texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1376   texObj = _mesa_select_tex_object(ctx, texUnit, target);
1377
1378   /* XXX this might not handle cube maps correctly */
1379   _mesa_generate_mipmap(ctx, target, texUnit, texObj);
1380}
1381