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