fbobject.c revision c7037ccd
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.1
4 *
5 * Copyright (C) 1999-2008  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 "buffers.h"
33#include "context.h"
34#include "fbobject.h"
35#include "framebuffer.h"
36#include "hash.h"
37#include "mipmap.h"
38#include "renderbuffer.h"
39#include "state.h"
40#include "teximage.h"
41#include "texobj.h"
42#include "texstore.h"
43
44
45/**
46 * Notes:
47 *
48 * None of the GL_EXT_framebuffer_object functions are compiled into
49 * display lists.
50 */
51
52
53
54/*
55 * When glGenRender/FramebuffersEXT() is called we insert pointers to
56 * these placeholder objects into the hash table.
57 * Later, when the object ID is first bound, we replace the placeholder
58 * with the real frame/renderbuffer.
59 */
60static struct gl_framebuffer DummyFramebuffer;
61static struct gl_renderbuffer DummyRenderbuffer;
62
63
64#define IS_CUBE_FACE(TARGET) \
65   ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
66    (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
67
68
69static void
70delete_dummy_renderbuffer(struct gl_renderbuffer *rb)
71{
72   /* no op */
73}
74
75static void
76delete_dummy_framebuffer(struct gl_framebuffer *fb)
77{
78   /* no op */
79}
80
81
82void
83_mesa_init_fbobjects(GLcontext *ctx)
84{
85   DummyFramebuffer.Delete = delete_dummy_framebuffer;
86   DummyRenderbuffer.Delete = delete_dummy_renderbuffer;
87}
88
89
90/**
91 * Helper routine for getting a gl_renderbuffer.
92 */
93struct gl_renderbuffer *
94_mesa_lookup_renderbuffer(GLcontext *ctx, GLuint id)
95{
96   struct gl_renderbuffer *rb;
97
98   if (id == 0)
99      return NULL;
100
101   rb = (struct gl_renderbuffer *)
102      _mesa_HashLookup(ctx->Shared->RenderBuffers, id);
103   return rb;
104}
105
106
107/**
108 * Helper routine for getting a gl_framebuffer.
109 */
110struct gl_framebuffer *
111_mesa_lookup_framebuffer(GLcontext *ctx, GLuint id)
112{
113   struct gl_framebuffer *fb;
114
115   if (id == 0)
116      return NULL;
117
118   fb = (struct gl_framebuffer *)
119      _mesa_HashLookup(ctx->Shared->FrameBuffers, id);
120   return fb;
121}
122
123
124/**
125 * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
126 * gl_renderbuffer_attachment object.
127 */
128struct gl_renderbuffer_attachment *
129_mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
130                     GLenum attachment)
131{
132   GLuint i;
133
134   switch (attachment) {
135   case GL_COLOR_ATTACHMENT0_EXT:
136   case GL_COLOR_ATTACHMENT1_EXT:
137   case GL_COLOR_ATTACHMENT2_EXT:
138   case GL_COLOR_ATTACHMENT3_EXT:
139   case GL_COLOR_ATTACHMENT4_EXT:
140   case GL_COLOR_ATTACHMENT5_EXT:
141   case GL_COLOR_ATTACHMENT6_EXT:
142   case GL_COLOR_ATTACHMENT7_EXT:
143   case GL_COLOR_ATTACHMENT8_EXT:
144   case GL_COLOR_ATTACHMENT9_EXT:
145   case GL_COLOR_ATTACHMENT10_EXT:
146   case GL_COLOR_ATTACHMENT11_EXT:
147   case GL_COLOR_ATTACHMENT12_EXT:
148   case GL_COLOR_ATTACHMENT13_EXT:
149   case GL_COLOR_ATTACHMENT14_EXT:
150   case GL_COLOR_ATTACHMENT15_EXT:
151      i = attachment - GL_COLOR_ATTACHMENT0_EXT;
152      if (i >= ctx->Const.MaxColorAttachments) {
153	 return NULL;
154      }
155      return &fb->Attachment[BUFFER_COLOR0 + i];
156   case GL_DEPTH_ATTACHMENT_EXT:
157      return &fb->Attachment[BUFFER_DEPTH];
158   case GL_STENCIL_ATTACHMENT_EXT:
159      return &fb->Attachment[BUFFER_STENCIL];
160   default:
161      return NULL;
162   }
163}
164
165
166/**
167 * Remove any texture or renderbuffer attached to the given attachment
168 * point.  Update reference counts, etc.
169 */
170void
171_mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
172{
173   if (att->Type == GL_TEXTURE) {
174      ASSERT(att->Texture);
175      if (ctx->Driver.FinishRenderTexture) {
176         /* tell driver we're done rendering to this texobj */
177         ctx->Driver.FinishRenderTexture(ctx, att);
178      }
179      _mesa_reference_texobj(&att->Texture, NULL); /* unbind */
180      ASSERT(!att->Texture);
181   }
182   if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) {
183      ASSERT(!att->Texture);
184      _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); /* unbind */
185      ASSERT(!att->Renderbuffer);
186   }
187   att->Type = GL_NONE;
188   att->Complete = GL_TRUE;
189}
190
191
192/**
193 * Bind a texture object to an attachment point.
194 * The previous binding, if any, will be removed first.
195 */
196void
197_mesa_set_texture_attachment(GLcontext *ctx,
198                             struct gl_framebuffer *fb,
199                             struct gl_renderbuffer_attachment *att,
200                             struct gl_texture_object *texObj,
201                             GLenum texTarget, GLuint level, GLuint zoffset)
202{
203   if (att->Texture == texObj) {
204      /* re-attaching same texture */
205      ASSERT(att->Type == GL_TEXTURE);
206   }
207   else {
208      /* new attachment */
209      _mesa_remove_attachment(ctx, att);
210      att->Type = GL_TEXTURE;
211      assert(!att->Texture);
212      _mesa_reference_texobj(&att->Texture, texObj);
213   }
214
215   /* always update these fields */
216   att->TextureLevel = level;
217   if (IS_CUBE_FACE(texTarget)) {
218      att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
219   }
220   else {
221      att->CubeMapFace = 0;
222   }
223   att->Zoffset = zoffset;
224   att->Complete = GL_FALSE;
225
226   if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
227      ctx->Driver.RenderTexture(ctx, fb, att);
228   }
229}
230
231
232/**
233 * Bind a renderbuffer to an attachment point.
234 * The previous binding, if any, will be removed first.
235 */
236void
237_mesa_set_renderbuffer_attachment(GLcontext *ctx,
238                                  struct gl_renderbuffer_attachment *att,
239                                  struct gl_renderbuffer *rb)
240{
241   /* XXX check if re-doing same attachment, exit early */
242   _mesa_remove_attachment(ctx, att);
243   att->Type = GL_RENDERBUFFER_EXT;
244   att->Texture = NULL; /* just to be safe */
245   att->Complete = GL_FALSE;
246   _mesa_reference_renderbuffer(&att->Renderbuffer, rb);
247}
248
249
250/**
251 * Fallback for ctx->Driver.FramebufferRenderbuffer()
252 * Attach a renderbuffer object to a framebuffer object.
253 */
254void
255_mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
256                               GLenum attachment, struct gl_renderbuffer *rb)
257{
258   struct gl_renderbuffer_attachment *att;
259
260   _glthread_LOCK_MUTEX(fb->Mutex);
261
262   att = _mesa_get_attachment(ctx, fb, attachment);
263   ASSERT(att);
264   if (rb) {
265      _mesa_set_renderbuffer_attachment(ctx, att, rb);
266   }
267   else {
268      _mesa_remove_attachment(ctx, att);
269   }
270
271   _glthread_UNLOCK_MUTEX(fb->Mutex);
272}
273
274
275/**
276 * Test if an attachment point is complete and update its Complete field.
277 * \param format if GL_COLOR, this is a color attachment point,
278 *               if GL_DEPTH, this is a depth component attachment point,
279 *               if GL_STENCIL, this is a stencil component attachment point.
280 */
281static void
282test_attachment_completeness(const GLcontext *ctx, GLenum format,
283                             struct gl_renderbuffer_attachment *att)
284{
285   assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL);
286
287   /* assume complete */
288   att->Complete = GL_TRUE;
289
290   /* Look for reasons why the attachment might be incomplete */
291   if (att->Type == GL_TEXTURE) {
292      const struct gl_texture_object *texObj = att->Texture;
293      struct gl_texture_image *texImage;
294
295      if (!texObj) {
296         att->Complete = GL_FALSE;
297         return;
298      }
299
300      texImage = texObj->Image[att->CubeMapFace][att->TextureLevel];
301      if (!texImage) {
302         att->Complete = GL_FALSE;
303         return;
304      }
305      if (texImage->Width < 1 || texImage->Height < 1) {
306         att->Complete = GL_FALSE;
307         return;
308      }
309      if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) {
310         att->Complete = GL_FALSE;
311         return;
312      }
313
314      if (format == GL_COLOR) {
315         if (texImage->TexFormat->BaseFormat != GL_RGB &&
316             texImage->TexFormat->BaseFormat != GL_RGBA) {
317            att->Complete = GL_FALSE;
318            return;
319         }
320      }
321      else if (format == GL_DEPTH) {
322         if (texImage->TexFormat->BaseFormat == GL_DEPTH_COMPONENT) {
323            /* OK */
324         }
325         else if (ctx->Extensions.EXT_packed_depth_stencil &&
326                  ctx->Extensions.ARB_depth_texture &&
327                  texImage->TexFormat->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         ASSERT(att->Renderbuffer->StencilBits);
338         if (ctx->Extensions.EXT_packed_depth_stencil &&
339             ctx->Extensions.ARB_depth_texture &&
340             att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
341            /* OK */
342         }
343         else {
344            /* no such thing as stencil-only textures */
345            att->Complete = GL_FALSE;
346            return;
347         }
348      }
349   }
350   else if (att->Type == GL_RENDERBUFFER_EXT) {
351      ASSERT(att->Renderbuffer);
352      if (!att->Renderbuffer->InternalFormat ||
353          att->Renderbuffer->Width < 1 ||
354          att->Renderbuffer->Height < 1) {
355         att->Complete = GL_FALSE;
356         return;
357      }
358      if (format == GL_COLOR) {
359         if (att->Renderbuffer->_BaseFormat != GL_RGB &&
360             att->Renderbuffer->_BaseFormat != GL_RGBA) {
361            ASSERT(att->Renderbuffer->RedBits);
362            ASSERT(att->Renderbuffer->GreenBits);
363            ASSERT(att->Renderbuffer->BlueBits);
364            att->Complete = GL_FALSE;
365            return;
366         }
367      }
368      else if (format == GL_DEPTH) {
369         ASSERT(att->Renderbuffer->DepthBits);
370         if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
371            /* OK */
372         }
373         else if (ctx->Extensions.EXT_packed_depth_stencil &&
374                  att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
375            /* OK */
376         }
377         else {
378            att->Complete = GL_FALSE;
379            return;
380         }
381      }
382      else {
383         assert(format == GL_STENCIL);
384         ASSERT(att->Renderbuffer->StencilBits);
385         if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
386            /* OK */
387         }
388         else if (ctx->Extensions.EXT_packed_depth_stencil &&
389                  att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
390            /* OK */
391         }
392         else {
393            att->Complete = GL_FALSE;
394            return;
395         }
396      }
397   }
398   else {
399      ASSERT(att->Type == GL_NONE);
400      /* complete */
401      return;
402   }
403}
404
405
406/**
407 * Helpful for debugging
408 */
409static void
410fbo_incomplete(const char *msg, int index)
411{
412   (void) msg;
413   (void) index;
414   /*
415   _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index);
416   */
417}
418
419
420/**
421 * Test if the given framebuffer object is complete and update its
422 * Status field with the results.
423 * Also update the framebuffer's Width and Height fields if the
424 * framebuffer is complete.
425 */
426void
427_mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
428{
429   GLuint numImages, width = 0, height = 0;
430   GLenum intFormat = GL_NONE;
431   GLuint w = 0, h = 0;
432   GLint i;
433   GLuint j;
434
435   assert(fb->Name != 0);
436
437   numImages = 0;
438   fb->Width = 0;
439   fb->Height = 0;
440
441   /* Start at -2 to more easily loop over all attachment points */
442   for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) {
443      struct gl_renderbuffer_attachment *att;
444      GLenum f;
445
446      if (i == -2) {
447         att = &fb->Attachment[BUFFER_DEPTH];
448         test_attachment_completeness(ctx, GL_DEPTH, att);
449         if (!att->Complete) {
450            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
451            fbo_incomplete("depth attachment incomplete", -1);
452            return;
453         }
454      }
455      else if (i == -1) {
456         att = &fb->Attachment[BUFFER_STENCIL];
457         test_attachment_completeness(ctx, GL_STENCIL, att);
458         if (!att->Complete) {
459            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
460            fbo_incomplete("stencil attachment incomplete", -1);
461            return;
462         }
463      }
464      else {
465         att = &fb->Attachment[BUFFER_COLOR0 + i];
466         test_attachment_completeness(ctx, GL_COLOR, att);
467         if (!att->Complete) {
468            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
469            fbo_incomplete("color attachment incomplete", i);
470            return;
471         }
472      }
473
474      if (att->Type == GL_TEXTURE) {
475         const struct gl_texture_image *texImg
476            = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
477         w = texImg->Width;
478         h = texImg->Height;
479         f = texImg->_BaseFormat;
480         numImages++;
481         if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT
482             && f != GL_DEPTH_STENCIL_EXT) {
483            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
484            fbo_incomplete("texture attachment incomplete", -1);
485            return;
486         }
487      }
488      else if (att->Type == GL_RENDERBUFFER_EXT) {
489         w = att->Renderbuffer->Width;
490         h = att->Renderbuffer->Height;
491         f = att->Renderbuffer->InternalFormat;
492         numImages++;
493      }
494      else {
495         assert(att->Type == GL_NONE);
496         continue;
497      }
498
499      if (numImages == 1) {
500         /* set required width, height and format */
501         width = w;
502         height = h;
503         if (i >= 0)
504            intFormat = f;
505      }
506      else {
507         /* check that width, height, format are same */
508         if (w != width || h != height) {
509            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
510            fbo_incomplete("width or height mismatch", -1);
511            return;
512         }
513         if (intFormat != GL_NONE && f != intFormat) {
514            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
515            fbo_incomplete("format mismatch", -1);
516            return;
517         }
518      }
519   }
520
521#ifndef FEATURE_OES_framebuffer_object
522   /* Check that all DrawBuffers are present */
523   for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) {
524      if (fb->ColorDrawBuffer[j] != GL_NONE) {
525         const struct gl_renderbuffer_attachment *att
526            = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[j]);
527         assert(att);
528         if (att->Type == GL_NONE) {
529            fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
530            fbo_incomplete("missing drawbuffer", j);
531            return;
532         }
533      }
534   }
535
536   /* Check that the ReadBuffer is present */
537   if (fb->ColorReadBuffer != GL_NONE) {
538      const struct gl_renderbuffer_attachment *att
539         = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer);
540      assert(att);
541      if (att->Type == GL_NONE) {
542         fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
543            fbo_incomplete("missing readbuffer", -1);
544         return;
545      }
546   }
547#endif
548
549   if (numImages == 0) {
550      fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
551      fbo_incomplete("no attachments", -1);
552      return;
553   }
554
555   /*
556    * If we get here, the framebuffer is complete!
557    */
558   fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT;
559   fb->Width = w;
560   fb->Height = h;
561}
562
563
564GLboolean GLAPIENTRY
565_mesa_IsRenderbufferEXT(GLuint renderbuffer)
566{
567   GET_CURRENT_CONTEXT(ctx);
568   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
569   if (renderbuffer) {
570      struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
571      if (rb != NULL && rb != &DummyRenderbuffer)
572         return GL_TRUE;
573   }
574   return GL_FALSE;
575}
576
577
578void GLAPIENTRY
579_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
580{
581   struct gl_renderbuffer *newRb;
582   GET_CURRENT_CONTEXT(ctx);
583
584   ASSERT_OUTSIDE_BEGIN_END(ctx);
585
586   if (target != GL_RENDERBUFFER_EXT) {
587         _mesa_error(ctx, GL_INVALID_ENUM,
588                  "glBindRenderbufferEXT(target)");
589      return;
590   }
591
592   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
593   /* The above doesn't fully flush the drivers in the way that a
594    * glFlush does, but that is required here:
595    */
596   if (ctx->Driver.Flush)
597      ctx->Driver.Flush(ctx);
598
599
600   if (renderbuffer) {
601      newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
602      if (newRb == &DummyRenderbuffer) {
603         /* ID was reserved, but no real renderbuffer object made yet */
604         newRb = NULL;
605      }
606      if (!newRb) {
607	 /* create new renderbuffer object */
608	 newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
609	 if (!newRb) {
610	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
611	    return;
612	 }
613         ASSERT(newRb->AllocStorage);
614         _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
615         newRb->RefCount = 1; /* referenced by hash table */
616      }
617   }
618   else {
619      newRb = NULL;
620   }
621
622   ASSERT(newRb != &DummyRenderbuffer);
623
624   _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb);
625}
626
627
628void GLAPIENTRY
629_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
630{
631   GLint i;
632   GET_CURRENT_CONTEXT(ctx);
633
634   ASSERT_OUTSIDE_BEGIN_END(ctx);
635   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
636
637   for (i = 0; i < n; i++) {
638      if (renderbuffers[i] > 0) {
639	 struct gl_renderbuffer *rb;
640	 rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]);
641	 if (rb) {
642            /* check if deleting currently bound renderbuffer object */
643            if (rb == ctx->CurrentRenderbuffer) {
644               /* bind default */
645               ASSERT(rb->RefCount >= 2);
646               _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
647            }
648
649	    /* Remove from hash table immediately, to free the ID.
650             * But the object will not be freed until it's no longer
651             * referenced anywhere else.
652             */
653	    _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
654
655            if (rb != &DummyRenderbuffer) {
656               /* no longer referenced by hash table */
657               _mesa_reference_renderbuffer(&rb, NULL);
658	    }
659	 }
660      }
661   }
662}
663
664
665void GLAPIENTRY
666_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
667{
668   GET_CURRENT_CONTEXT(ctx);
669   GLuint first;
670   GLint i;
671
672   ASSERT_OUTSIDE_BEGIN_END(ctx);
673
674   if (n < 0) {
675      _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
676      return;
677   }
678
679   if (!renderbuffers)
680      return;
681
682   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
683
684   for (i = 0; i < n; i++) {
685      GLuint name = first + i;
686      renderbuffers[i] = name;
687      /* insert dummy placeholder into hash table */
688      _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
689      _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
690      _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
691   }
692}
693
694
695/**
696 * Given an internal format token for a render buffer, return the
697 * corresponding base format.
698 * This is very similar to _mesa_base_tex_format() but the set of valid
699 * internal formats is somewhat different.
700 *
701 * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
702 *  GL_DEPTH_STENCIL_EXT or zero if error.
703 */
704GLenum
705_mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
706{
707   switch (internalFormat) {
708   case GL_RGB:
709   case GL_R3_G3_B2:
710   case GL_RGB4:
711   case GL_RGB5:
712   case GL_RGB8:
713   case GL_RGB10:
714   case GL_RGB12:
715   case GL_RGB16:
716      return GL_RGB;
717   case GL_RGBA:
718   case GL_RGBA2:
719   case GL_RGBA4:
720   case GL_RGB5_A1:
721   case GL_RGBA8:
722   case GL_RGB10_A2:
723   case GL_RGBA12:
724   case GL_RGBA16:
725      return GL_RGBA;
726   case GL_STENCIL_INDEX:
727   case GL_STENCIL_INDEX1_EXT:
728   case GL_STENCIL_INDEX4_EXT:
729   case GL_STENCIL_INDEX8_EXT:
730   case GL_STENCIL_INDEX16_EXT:
731      return GL_STENCIL_INDEX;
732   case GL_DEPTH_COMPONENT:
733   case GL_DEPTH_COMPONENT16:
734   case GL_DEPTH_COMPONENT24:
735   case GL_DEPTH_COMPONENT32:
736      return GL_DEPTH_COMPONENT;
737   case GL_DEPTH_STENCIL_EXT:
738   case GL_DEPTH24_STENCIL8_EXT:
739      if (ctx->Extensions.EXT_packed_depth_stencil)
740         return GL_DEPTH_STENCIL_EXT;
741      else
742         return 0;
743   /* XXX add floating point formats eventually */
744   default:
745      return 0;
746   }
747}
748
749
750void GLAPIENTRY
751_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
752                             GLsizei width, GLsizei height)
753{
754   struct gl_renderbuffer *rb;
755   GLenum baseFormat;
756   GET_CURRENT_CONTEXT(ctx);
757
758   ASSERT_OUTSIDE_BEGIN_END(ctx);
759
760   if (target != GL_RENDERBUFFER_EXT) {
761      _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
762      return;
763   }
764
765   baseFormat = _mesa_base_fbo_format(ctx, internalFormat);
766   if (baseFormat == 0) {
767      _mesa_error(ctx, GL_INVALID_ENUM,
768                  "glRenderbufferStorageEXT(internalFormat)");
769      return;
770   }
771
772   if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) {
773      _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
774      return;
775   }
776
777   if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) {
778      _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
779      return;
780   }
781
782   rb = ctx->CurrentRenderbuffer;
783
784   if (!rb) {
785      _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
786      return;
787   }
788
789   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
790
791   if (rb->InternalFormat == internalFormat &&
792       rb->Width == (GLuint) width &&
793       rb->Height == (GLuint) height) {
794      /* no change in allocation needed */
795      return;
796   }
797
798   /* These MUST get set by the AllocStorage func */
799   rb->_ActualFormat = 0;
800   rb->RedBits =
801   rb->GreenBits =
802   rb->BlueBits =
803   rb->AlphaBits =
804   rb->IndexBits =
805   rb->DepthBits =
806   rb->StencilBits = 0;
807
808   /* Now allocate the storage */
809   ASSERT(rb->AllocStorage);
810   if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
811      /* No error - check/set fields now */
812      assert(rb->_ActualFormat);
813      assert(rb->Width == (GLuint) width);
814      assert(rb->Height == (GLuint) height);
815      assert(rb->RedBits || rb->GreenBits || rb->BlueBits || rb->AlphaBits ||
816             rb->DepthBits || rb->StencilBits || rb->IndexBits);
817      rb->InternalFormat = internalFormat;
818      rb->_BaseFormat = baseFormat;
819   }
820   else {
821      /* Probably ran out of memory - clear the fields */
822      rb->Width = 0;
823      rb->Height = 0;
824      rb->InternalFormat = GL_NONE;
825      rb->_ActualFormat = GL_NONE;
826      rb->_BaseFormat = GL_NONE;
827      rb->RedBits =
828      rb->GreenBits =
829      rb->BlueBits =
830      rb->AlphaBits =
831      rb->IndexBits =
832      rb->DepthBits =
833      rb->StencilBits = 0;
834   }
835
836   /*
837   test_framebuffer_completeness(ctx, fb);
838   */
839   /* XXX if this renderbuffer is attached anywhere, invalidate attachment
840    * points???
841    */
842}
843
844
845void GLAPIENTRY
846_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
847{
848   GET_CURRENT_CONTEXT(ctx);
849
850   ASSERT_OUTSIDE_BEGIN_END(ctx);
851
852   if (target != GL_RENDERBUFFER_EXT) {
853      _mesa_error(ctx, GL_INVALID_ENUM,
854                  "glGetRenderbufferParameterivEXT(target)");
855      return;
856   }
857
858   if (!ctx->CurrentRenderbuffer) {
859      _mesa_error(ctx, GL_INVALID_OPERATION,
860                  "glGetRenderbufferParameterivEXT");
861      return;
862   }
863
864   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
865
866   switch (pname) {
867   case GL_RENDERBUFFER_WIDTH_EXT:
868      *params = ctx->CurrentRenderbuffer->Width;
869      return;
870   case GL_RENDERBUFFER_HEIGHT_EXT:
871      *params = ctx->CurrentRenderbuffer->Height;
872      return;
873   case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
874      *params = ctx->CurrentRenderbuffer->InternalFormat;
875      return;
876   case GL_RENDERBUFFER_RED_SIZE_EXT:
877      *params = ctx->CurrentRenderbuffer->RedBits;
878      break;
879   case GL_RENDERBUFFER_GREEN_SIZE_EXT:
880      *params = ctx->CurrentRenderbuffer->GreenBits;
881      break;
882   case GL_RENDERBUFFER_BLUE_SIZE_EXT:
883      *params = ctx->CurrentRenderbuffer->BlueBits;
884      break;
885   case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
886      *params = ctx->CurrentRenderbuffer->AlphaBits;
887      break;
888   case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
889      *params = ctx->CurrentRenderbuffer->DepthBits;
890      break;
891   case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
892      *params = ctx->CurrentRenderbuffer->StencilBits;
893      break;
894   default:
895      _mesa_error(ctx, GL_INVALID_ENUM,
896                  "glGetRenderbufferParameterivEXT(target)");
897      return;
898   }
899}
900
901
902GLboolean GLAPIENTRY
903_mesa_IsFramebufferEXT(GLuint framebuffer)
904{
905   GET_CURRENT_CONTEXT(ctx);
906   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
907   if (framebuffer) {
908      struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer);
909      if (rb != NULL && rb != &DummyFramebuffer)
910         return GL_TRUE;
911   }
912   return GL_FALSE;
913}
914
915
916static void
917check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
918{
919   GLuint i;
920   ASSERT(ctx->Driver.RenderTexture);
921   for (i = 0; i < BUFFER_COUNT; i++) {
922      struct gl_renderbuffer_attachment *att = fb->Attachment + i;
923      struct gl_texture_object *texObj = att->Texture;
924      if (texObj
925          && att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
926         ctx->Driver.RenderTexture(ctx, fb, att);
927      }
928   }
929}
930
931
932/**
933 * Examine all the framebuffer's attachments to see if any are textures.
934 * If so, call ctx->Driver.FinishRenderTexture() for each texture to
935 * notify the device driver that the texture image may have changed.
936 */
937static void
938check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
939{
940   if (ctx->Driver.FinishRenderTexture) {
941      GLuint i;
942      for (i = 0; i < BUFFER_COUNT; i++) {
943         struct gl_renderbuffer_attachment *att = fb->Attachment + i;
944         if (att->Texture && att->Renderbuffer) {
945            ctx->Driver.FinishRenderTexture(ctx, att);
946         }
947      }
948   }
949}
950
951
952void GLAPIENTRY
953_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
954{
955   struct gl_framebuffer *newFb, *newFbread;
956   GLboolean bindReadBuf, bindDrawBuf;
957   GET_CURRENT_CONTEXT(ctx);
958
959   ASSERT_OUTSIDE_BEGIN_END(ctx);
960
961   if (!ctx->Extensions.EXT_framebuffer_object) {
962      _mesa_error(ctx, GL_INVALID_OPERATION,
963                  "glBindFramebufferEXT(unsupported)");
964      return;
965   }
966
967   switch (target) {
968#if FEATURE_EXT_framebuffer_blit
969   case GL_DRAW_FRAMEBUFFER_EXT:
970      if (!ctx->Extensions.EXT_framebuffer_blit) {
971         _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
972         return;
973      }
974      bindDrawBuf = GL_TRUE;
975      bindReadBuf = GL_FALSE;
976      break;
977   case GL_READ_FRAMEBUFFER_EXT:
978      if (!ctx->Extensions.EXT_framebuffer_blit) {
979         _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
980         return;
981      }
982      bindDrawBuf = GL_FALSE;
983      bindReadBuf = GL_TRUE;
984      break;
985#endif
986   case GL_FRAMEBUFFER_EXT:
987      bindDrawBuf = GL_TRUE;
988      bindReadBuf = GL_TRUE;
989      break;
990   default:
991      _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
992      return;
993   }
994
995   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
996
997   if (ctx->Driver.Flush) {
998      ctx->Driver.Flush(ctx);
999   }
1000
1001   if (framebuffer) {
1002      /* Binding a user-created framebuffer object */
1003      newFb = _mesa_lookup_framebuffer(ctx, framebuffer);
1004      if (newFb == &DummyFramebuffer) {
1005         /* ID was reserved, but no real framebuffer object made yet */
1006         newFb = NULL;
1007      }
1008      if (!newFb) {
1009	 /* create new framebuffer object */
1010	 newFb = ctx->Driver.NewFramebuffer(ctx, framebuffer);
1011	 if (!newFb) {
1012	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
1013	    return;
1014	 }
1015         _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
1016      }
1017      newFbread = newFb;
1018   }
1019   else {
1020      /* Binding the window system framebuffer (which was originally set
1021       * with MakeCurrent).
1022       */
1023      newFb = ctx->WinSysDrawBuffer;
1024      newFbread = ctx->WinSysReadBuffer;
1025   }
1026
1027   ASSERT(newFb);
1028   ASSERT(newFb != &DummyFramebuffer);
1029
1030   /*
1031    * XXX check if re-binding same buffer and skip some of this code.
1032    */
1033
1034   if (bindReadBuf) {
1035      _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread);
1036   }
1037
1038   if (bindDrawBuf) {
1039      /* check if old FB had any texture attachments */
1040      check_end_texture_render(ctx, ctx->DrawBuffer);
1041
1042      /* check if time to delete this framebuffer */
1043      _mesa_reference_framebuffer(&ctx->DrawBuffer, newFb);
1044
1045      if (newFb->Name != 0) {
1046         /* check if newly bound framebuffer has any texture attachments */
1047         check_begin_texture_render(ctx, newFb);
1048      }
1049   }
1050
1051   if (ctx->Driver.BindFramebuffer) {
1052      ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread);
1053   }
1054}
1055
1056
1057void GLAPIENTRY
1058_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
1059{
1060   GLint i;
1061   GET_CURRENT_CONTEXT(ctx);
1062
1063   ASSERT_OUTSIDE_BEGIN_END(ctx);
1064   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1065   /* The above doesn't fully flush the drivers in the way that a
1066    * glFlush does, but that is required here:
1067    */
1068   if (ctx->Driver.Flush)
1069      ctx->Driver.Flush(ctx);
1070
1071   for (i = 0; i < n; i++) {
1072      if (framebuffers[i] > 0) {
1073	 struct gl_framebuffer *fb;
1074	 fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]);
1075	 if (fb) {
1076            ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
1077
1078            /* check if deleting currently bound framebuffer object */
1079            if (fb == ctx->DrawBuffer) {
1080               /* bind default */
1081               ASSERT(fb->RefCount >= 2);
1082               _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1083            }
1084
1085	    /* remove from hash table immediately, to free the ID */
1086	    _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
1087
1088            if (fb != &DummyFramebuffer) {
1089               /* But the object will not be freed until it's no longer
1090                * bound in any context.
1091                */
1092               _mesa_unreference_framebuffer(&fb);
1093	    }
1094	 }
1095      }
1096   }
1097}
1098
1099
1100void GLAPIENTRY
1101_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
1102{
1103   GET_CURRENT_CONTEXT(ctx);
1104   GLuint first;
1105   GLint i;
1106
1107   ASSERT_OUTSIDE_BEGIN_END(ctx);
1108
1109   if (n < 0) {
1110      _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
1111      return;
1112   }
1113
1114   if (!framebuffers)
1115      return;
1116
1117   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
1118
1119   for (i = 0; i < n; i++) {
1120      GLuint name = first + i;
1121      framebuffers[i] = name;
1122      /* insert dummy placeholder into hash table */
1123      _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
1124      _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
1125      _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1126   }
1127}
1128
1129
1130
1131GLenum GLAPIENTRY
1132_mesa_CheckFramebufferStatusEXT(GLenum target)
1133{
1134   struct gl_framebuffer *buffer;
1135   GET_CURRENT_CONTEXT(ctx);
1136
1137   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
1138
1139   switch (target) {
1140#if FEATURE_EXT_framebuffer_blit
1141   case GL_DRAW_FRAMEBUFFER_EXT:
1142      if (!ctx->Extensions.EXT_framebuffer_blit) {
1143         _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1144         return 0;
1145      }
1146      buffer = ctx->DrawBuffer;
1147      break;
1148   case GL_READ_FRAMEBUFFER_EXT:
1149      if (!ctx->Extensions.EXT_framebuffer_blit) {
1150         _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1151         return 0;
1152      }
1153      buffer = ctx->ReadBuffer;
1154      break;
1155#endif
1156   case GL_FRAMEBUFFER_EXT:
1157      buffer = ctx->DrawBuffer;
1158      break;
1159   default:
1160      _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1161      return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
1162   }
1163
1164   if (buffer->Name == 0) {
1165      /* The window system / default framebuffer is always complete */
1166      return GL_FRAMEBUFFER_COMPLETE_EXT;
1167   }
1168
1169   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1170
1171   _mesa_test_framebuffer_completeness(ctx, buffer);
1172   return buffer->_Status;
1173}
1174
1175
1176
1177/**
1178 * Common code called by glFramebufferTexture1D/2D/3DEXT().
1179 */
1180static void
1181framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target,
1182                    GLenum attachment, GLenum textarget, GLuint texture,
1183                    GLint level, GLint zoffset)
1184{
1185   struct gl_renderbuffer_attachment *att;
1186   struct gl_texture_object *texObj = NULL;
1187   struct gl_framebuffer *fb;
1188
1189   ASSERT_OUTSIDE_BEGIN_END(ctx);
1190
1191   if (target != GL_FRAMEBUFFER_EXT) {
1192      _mesa_error(ctx, GL_INVALID_ENUM,
1193                  "glFramebufferTexture%sEXT(target)", caller);
1194      return;
1195   }
1196
1197   fb = ctx->DrawBuffer;
1198   ASSERT(fb);
1199
1200   /* check framebuffer binding */
1201   if (fb->Name == 0) {
1202      _mesa_error(ctx, GL_INVALID_OPERATION,
1203                  "glFramebufferTexture%sEXT", caller);
1204      return;
1205   }
1206
1207
1208   /* The textarget, level, and zoffset parameters are only validated if
1209    * texture is non-zero.
1210    */
1211   if (texture) {
1212      GLboolean err = GL_TRUE;
1213
1214      texObj = _mesa_lookup_texture(ctx, texture);
1215      if (texObj != NULL) {
1216         if (textarget == 0) {
1217            err = (texObj->Target != GL_TEXTURE_3D) &&
1218                (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) &&
1219                (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT);
1220         }
1221         else {
1222            err = (texObj->Target == GL_TEXTURE_CUBE_MAP)
1223                ? !IS_CUBE_FACE(textarget)
1224                : (texObj->Target != textarget);
1225         }
1226      }
1227
1228      if (err) {
1229         _mesa_error(ctx, GL_INVALID_OPERATION,
1230                     "glFramebufferTexture%sEXT(texture target mismatch)",
1231                     caller);
1232         return;
1233      }
1234
1235      if (texObj->Target == GL_TEXTURE_3D) {
1236         const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1237         if (zoffset < 0 || zoffset >= maxSize) {
1238            _mesa_error(ctx, GL_INVALID_VALUE,
1239                        "glFramebufferTexture%sEXT(zoffset)", caller);
1240            return;
1241         }
1242      }
1243      else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) ||
1244               (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT)) {
1245         if (zoffset < 0 || zoffset >= ctx->Const.MaxArrayTextureLayers) {
1246            _mesa_error(ctx, GL_INVALID_VALUE,
1247                        "glFramebufferTexture%sEXT(layer)", caller);
1248            return;
1249         }
1250      }
1251
1252
1253      if ((level < 0) ||
1254          (level >= _mesa_max_texture_levels(ctx, texObj->Target))) {
1255         _mesa_error(ctx, GL_INVALID_VALUE,
1256                     "glFramebufferTexture%sEXT(level)", caller);
1257         return;
1258      }
1259   }
1260
1261   att = _mesa_get_attachment(ctx, fb, attachment);
1262   if (att == NULL) {
1263      _mesa_error(ctx, GL_INVALID_ENUM,
1264                  "glFramebufferTexture%sEXT(attachment)", caller);
1265      return;
1266   }
1267
1268   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1269   /* The above doesn't fully flush the drivers in the way that a
1270    * glFlush does, but that is required here:
1271    */
1272   if (ctx->Driver.Flush)
1273      ctx->Driver.Flush(ctx);
1274
1275   _glthread_LOCK_MUTEX(fb->Mutex);
1276   if (texObj) {
1277      _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
1278                                   level, zoffset);
1279   }
1280   else {
1281      _mesa_remove_attachment(ctx, att);
1282   }
1283   _glthread_UNLOCK_MUTEX(fb->Mutex);
1284}
1285
1286
1287
1288void GLAPIENTRY
1289_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1290                              GLenum textarget, GLuint texture, GLint level)
1291{
1292   GET_CURRENT_CONTEXT(ctx);
1293
1294   if ((texture != 0) && (textarget != GL_TEXTURE_1D)) {
1295      _mesa_error(ctx, GL_INVALID_ENUM,
1296                  "glFramebufferTexture1DEXT(textarget)");
1297      return;
1298   }
1299
1300   framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
1301                       level, 0);
1302}
1303
1304
1305void GLAPIENTRY
1306_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1307                              GLenum textarget, GLuint texture, GLint level)
1308{
1309   GET_CURRENT_CONTEXT(ctx);
1310
1311   if ((texture != 0) &&
1312       (textarget != GL_TEXTURE_2D) &&
1313       (textarget != GL_TEXTURE_RECTANGLE_ARB) &&
1314       (!IS_CUBE_FACE(textarget))) {
1315      _mesa_error(ctx, GL_INVALID_OPERATION,
1316                  "glFramebufferTexture2DEXT(textarget)");
1317      return;
1318   }
1319
1320   framebuffer_texture(ctx, "2D", target, attachment, textarget, texture,
1321                       level, 0);
1322}
1323
1324
1325void GLAPIENTRY
1326_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1327                              GLenum textarget, GLuint texture,
1328                              GLint level, GLint zoffset)
1329{
1330   GET_CURRENT_CONTEXT(ctx);
1331
1332   if ((texture != 0) && (textarget != GL_TEXTURE_3D)) {
1333      _mesa_error(ctx, GL_INVALID_ENUM,
1334                  "glFramebufferTexture3DEXT(textarget)");
1335      return;
1336   }
1337
1338   framebuffer_texture(ctx, "3D", target, attachment, textarget, texture,
1339                       level, zoffset);
1340}
1341
1342
1343void GLAPIENTRY
1344_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment,
1345                                 GLuint texture, GLint level, GLint layer)
1346{
1347   GET_CURRENT_CONTEXT(ctx);
1348
1349   framebuffer_texture(ctx, "Layer", target, attachment, 0, texture,
1350                       level, layer);
1351}
1352
1353
1354void GLAPIENTRY
1355_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1356                                 GLenum renderbufferTarget,
1357                                 GLuint renderbuffer)
1358{
1359   struct gl_renderbuffer_attachment *att;
1360   struct gl_framebuffer *fb;
1361   struct gl_renderbuffer *rb;
1362   GET_CURRENT_CONTEXT(ctx);
1363
1364   ASSERT_OUTSIDE_BEGIN_END(ctx);
1365
1366   switch (target) {
1367#if FEATURE_EXT_framebuffer_blit
1368   case GL_DRAW_FRAMEBUFFER_EXT:
1369      if (!ctx->Extensions.EXT_framebuffer_blit) {
1370         _mesa_error(ctx, GL_INVALID_ENUM,
1371                     "glFramebufferRenderbufferEXT(target)");
1372         return;
1373      }
1374      fb = ctx->DrawBuffer;
1375      break;
1376   case GL_READ_FRAMEBUFFER_EXT:
1377      if (!ctx->Extensions.EXT_framebuffer_blit) {
1378         _mesa_error(ctx, GL_INVALID_ENUM,
1379                     "glFramebufferRenderbufferEXT(target)");
1380         return;
1381      }
1382      fb = ctx->ReadBuffer;
1383      break;
1384#endif
1385   case GL_FRAMEBUFFER_EXT:
1386      fb = ctx->DrawBuffer;
1387      break;
1388   default:
1389      _mesa_error(ctx, GL_INVALID_ENUM,
1390                  "glFramebufferRenderbufferEXT(target)");
1391      return;
1392   }
1393
1394   if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1395      _mesa_error(ctx, GL_INVALID_ENUM,
1396                  "glFramebufferRenderbufferEXT(renderbufferTarget)");
1397      return;
1398   }
1399
1400   if (fb->Name == 0) {
1401      /* Can't attach new renderbuffers to a window system framebuffer */
1402      _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1403      return;
1404   }
1405
1406   att = _mesa_get_attachment(ctx, fb, attachment);
1407   if (att == NULL) {
1408      _mesa_error(ctx, GL_INVALID_ENUM,
1409                 "glFramebufferRenderbufferEXT(attachment)");
1410      return;
1411   }
1412
1413   if (renderbuffer) {
1414      rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
1415      if (!rb) {
1416	 _mesa_error(ctx, GL_INVALID_OPERATION,
1417		     "glFramebufferRenderbufferEXT(renderbuffer)");
1418	 return;
1419      }
1420   }
1421   else {
1422      /* remove renderbuffer attachment */
1423      rb = NULL;
1424   }
1425
1426   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1427   /* The above doesn't fully flush the drivers in the way that a
1428    * glFlush does, but that is required here:
1429    */
1430   if (ctx->Driver.Flush)
1431      ctx->Driver.Flush(ctx);
1432
1433   assert(ctx->Driver.FramebufferRenderbuffer);
1434   ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb);
1435
1436   /* Some subsequent GL commands may depend on the framebuffer's visual
1437    * after the binding is updated.  Update visual info now.
1438    */
1439   _mesa_update_framebuffer_visual(fb);
1440}
1441
1442
1443void GLAPIENTRY
1444_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1445                                             GLenum pname, GLint *params)
1446{
1447   const struct gl_renderbuffer_attachment *att;
1448   struct gl_framebuffer *buffer;
1449   GET_CURRENT_CONTEXT(ctx);
1450
1451   ASSERT_OUTSIDE_BEGIN_END(ctx);
1452
1453   switch (target) {
1454#if FEATURE_EXT_framebuffer_blit
1455   case GL_DRAW_FRAMEBUFFER_EXT:
1456      if (!ctx->Extensions.EXT_framebuffer_blit) {
1457         _mesa_error(ctx, GL_INVALID_ENUM,
1458                     "glGetFramebufferAttachmentParameterivEXT(target)");
1459         return;
1460      }
1461      buffer = ctx->DrawBuffer;
1462      break;
1463   case GL_READ_FRAMEBUFFER_EXT:
1464      if (!ctx->Extensions.EXT_framebuffer_blit) {
1465         _mesa_error(ctx, GL_INVALID_ENUM,
1466                     "glGetFramebufferAttachmentParameterivEXT(target)");
1467         return;
1468      }
1469      buffer = ctx->ReadBuffer;
1470      break;
1471#endif
1472   case GL_FRAMEBUFFER_EXT:
1473      buffer = ctx->DrawBuffer;
1474      break;
1475   default:
1476      _mesa_error(ctx, GL_INVALID_ENUM,
1477                  "glGetFramebufferAttachmentParameterivEXT(target)");
1478      return;
1479   }
1480
1481   if (buffer->Name == 0) {
1482      _mesa_error(ctx, GL_INVALID_OPERATION,
1483                  "glGetFramebufferAttachmentParameterivEXT");
1484      return;
1485   }
1486
1487   att = _mesa_get_attachment(ctx, buffer, attachment);
1488   if (att == NULL) {
1489      _mesa_error(ctx, GL_INVALID_ENUM,
1490                  "glGetFramebufferAttachmentParameterivEXT(attachment)");
1491      return;
1492   }
1493
1494   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1495   /* The above doesn't fully flush the drivers in the way that a
1496    * glFlush does, but that is required here:
1497    */
1498   if (ctx->Driver.Flush)
1499      ctx->Driver.Flush(ctx);
1500
1501   switch (pname) {
1502   case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1503      *params = att->Type;
1504      return;
1505   case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1506      if (att->Type == GL_RENDERBUFFER_EXT) {
1507	 *params = att->Renderbuffer->Name;
1508      }
1509      else if (att->Type == GL_TEXTURE) {
1510	 *params = att->Texture->Name;
1511      }
1512      else {
1513	 _mesa_error(ctx, GL_INVALID_ENUM,
1514		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1515      }
1516      return;
1517   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1518      if (att->Type == GL_TEXTURE) {
1519	 *params = att->TextureLevel;
1520      }
1521      else {
1522	 _mesa_error(ctx, GL_INVALID_ENUM,
1523		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1524      }
1525      return;
1526   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1527      if (att->Type == GL_TEXTURE) {
1528         if (att->Texture && att->Texture->Target == GL_TEXTURE_CUBE_MAP) {
1529            *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1530         }
1531         else {
1532            *params = 0;
1533         }
1534      }
1535      else {
1536	 _mesa_error(ctx, GL_INVALID_ENUM,
1537		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1538      }
1539      return;
1540   case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1541      if (att->Type == GL_TEXTURE) {
1542         if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) {
1543            *params = att->Zoffset;
1544         }
1545         else {
1546            *params = 0;
1547         }
1548      }
1549      else {
1550	 _mesa_error(ctx, GL_INVALID_ENUM,
1551		     "glGetFramebufferAttachmentParameterivEXT(pname)");
1552      }
1553      return;
1554   default:
1555      _mesa_error(ctx, GL_INVALID_ENUM,
1556                  "glGetFramebufferAttachmentParameterivEXT(pname)");
1557      return;
1558   }
1559}
1560
1561
1562void GLAPIENTRY
1563_mesa_GenerateMipmapEXT(GLenum target)
1564{
1565   struct gl_texture_unit *texUnit;
1566   struct gl_texture_object *texObj;
1567   GET_CURRENT_CONTEXT(ctx);
1568
1569   ASSERT_OUTSIDE_BEGIN_END(ctx);
1570   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1571
1572   switch (target) {
1573   case GL_TEXTURE_1D:
1574   case GL_TEXTURE_2D:
1575   case GL_TEXTURE_3D:
1576   case GL_TEXTURE_CUBE_MAP:
1577      /* OK, legal value */
1578      break;
1579   default:
1580      _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1581      return;
1582   }
1583
1584   texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1585   texObj = _mesa_select_tex_object(ctx, texUnit, target);
1586
1587   _mesa_lock_texture(ctx, texObj);
1588   if (target == GL_TEXTURE_CUBE_MAP) {
1589      int face;
1590
1591      for (face = 0; face < 6; face++)
1592	 ctx->Driver.GenerateMipmap(ctx,
1593				    GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face,
1594				    texObj);
1595   } else {
1596      ctx->Driver.GenerateMipmap(ctx, target, texObj);
1597   }
1598   _mesa_unlock_texture(ctx, texObj);
1599}
1600
1601
1602#if FEATURE_EXT_framebuffer_blit
1603void GLAPIENTRY
1604_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
1605                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
1606                         GLbitfield mask, GLenum filter)
1607{
1608   GET_CURRENT_CONTEXT(ctx);
1609
1610   ASSERT_OUTSIDE_BEGIN_END(ctx);
1611   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1612
1613   if (ctx->NewState) {
1614      _mesa_update_state(ctx);
1615   }
1616
1617   if (!ctx->ReadBuffer) {
1618      /* XXX */
1619   }
1620
1621   /* check for complete framebuffers */
1622   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
1623       ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1624      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
1625                  "glBlitFramebufferEXT(incomplete draw/read buffers)");
1626      return;
1627   }
1628
1629   if (filter != GL_NEAREST && filter != GL_LINEAR) {
1630      _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)");
1631      return;
1632   }
1633
1634   if (mask & ~(GL_COLOR_BUFFER_BIT |
1635                GL_DEPTH_BUFFER_BIT |
1636                GL_STENCIL_BUFFER_BIT)) {
1637      _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)");
1638      return;
1639   }
1640
1641   /* depth/stencil must be blitted with nearest filtering */
1642   if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
1643        && filter != GL_NEAREST) {
1644      _mesa_error(ctx, GL_INVALID_OPERATION,
1645             "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter");
1646      return;
1647   }
1648
1649   if (mask & GL_STENCIL_BUFFER_BIT) {
1650      struct gl_renderbuffer *readRb = ctx->ReadBuffer->_StencilBuffer;
1651      struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_StencilBuffer;
1652      if (readRb->StencilBits != drawRb->StencilBits) {
1653         _mesa_error(ctx, GL_INVALID_OPERATION,
1654                     "glBlitFramebufferEXT(stencil buffer size mismatch");
1655         return;
1656      }
1657   }
1658
1659   if (mask & GL_DEPTH_BUFFER_BIT) {
1660      struct gl_renderbuffer *readRb = ctx->ReadBuffer->_DepthBuffer;
1661      struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_DepthBuffer;
1662      if (readRb->DepthBits != drawRb->DepthBits) {
1663         _mesa_error(ctx, GL_INVALID_OPERATION,
1664                     "glBlitFramebufferEXT(depth buffer size mismatch");
1665         return;
1666      }
1667   }
1668
1669   if (!ctx->Extensions.EXT_framebuffer_blit) {
1670      _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT");
1671      return;
1672   }
1673
1674   ASSERT(ctx->Driver.BlitFramebuffer);
1675   ctx->Driver.BlitFramebuffer(ctx,
1676                               srcX0, srcY0, srcX1, srcY1,
1677                               dstX0, dstY0, dstX1, dstY1,
1678                               mask, filter);
1679}
1680#endif /* FEATURE_EXT_framebuffer_blit */
1681