Merge commit 'origin/master' into gallium-0.2
[profile/ivi/mesa.git] / src / mesa / main / fbobject.c
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  */
60 static struct gl_framebuffer DummyFramebuffer;
61 static 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
69 static void
70 delete_dummy_renderbuffer(struct gl_renderbuffer *rb)
71 {
72    /* no op */
73 }
74
75 static void
76 delete_dummy_framebuffer(struct gl_framebuffer *fb)
77 {
78    /* no op */
79 }
80
81
82 void
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  */
93 struct 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  */
110 struct 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  */
128 struct 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  */
170 void
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 that we're done rendering to this texture. */
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  */
196 void
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  */
236 void
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  */
254 void
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  */
281 static void
282 test_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                   texImage->TexFormat->BaseFormat == GL_DEPTH_STENCIL_EXT) {
327             /* OK */
328          }
329          else {
330             att->Complete = GL_FALSE;
331             return;
332          }
333       }
334       else {
335          /* no such thing as stencil textures */
336          att->Complete = GL_FALSE;
337          return;
338       }
339    }
340    else if (att->Type == GL_RENDERBUFFER_EXT) {
341       ASSERT(att->Renderbuffer);
342       if (!att->Renderbuffer->InternalFormat ||
343           att->Renderbuffer->Width < 1 ||
344           att->Renderbuffer->Height < 1) {
345          att->Complete = GL_FALSE;
346          return;
347       }
348       if (format == GL_COLOR) {
349          if (att->Renderbuffer->_BaseFormat != GL_RGB &&
350              att->Renderbuffer->_BaseFormat != GL_RGBA) {
351             ASSERT(att->Renderbuffer->RedBits);
352             ASSERT(att->Renderbuffer->GreenBits);
353             ASSERT(att->Renderbuffer->BlueBits);
354             att->Complete = GL_FALSE;
355             return;
356          }
357       }
358       else if (format == GL_DEPTH) {
359          ASSERT(att->Renderbuffer->DepthBits);
360          if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
361             /* OK */
362          }
363          else if (ctx->Extensions.EXT_packed_depth_stencil &&
364                   att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
365             /* OK */
366          }
367          else {
368             att->Complete = GL_FALSE;
369             return;
370          }
371       }
372       else {
373          assert(format == GL_STENCIL);
374          ASSERT(att->Renderbuffer->StencilBits);
375          if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
376             /* OK */
377          }
378          else if (ctx->Extensions.EXT_packed_depth_stencil &&
379                   att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
380             /* OK */
381          }
382          else {
383             att->Complete = GL_FALSE;
384             return;
385          }
386       }
387    }
388    else {
389       ASSERT(att->Type == GL_NONE);
390       /* complete */
391       return;
392    }
393 }
394
395
396 /**
397  * Helpful for debugging
398  */
399 static void
400 fbo_incomplete(const char *msg, int index)
401 {
402    (void) msg;
403    (void) index;
404    /*
405    _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index);
406    */
407 }
408
409
410 /**
411  * Test if the given framebuffer object is complete and update its
412  * Status field with the results.
413  * Also update the framebuffer's Width and Height fields if the
414  * framebuffer is complete.
415  */
416 void
417 _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
418 {
419    GLuint numImages, width = 0, height = 0;
420    GLenum intFormat = GL_NONE;
421    GLuint w = 0, h = 0;
422    GLint i;
423    GLuint j;
424
425    assert(fb->Name != 0);
426
427    numImages = 0;
428    fb->Width = 0;
429    fb->Height = 0;
430
431    /* Start at -2 to more easily loop over all attachment points */
432    for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) {
433       struct gl_renderbuffer_attachment *att;
434       GLenum f;
435
436       if (i == -2) {
437          att = &fb->Attachment[BUFFER_DEPTH];
438          test_attachment_completeness(ctx, GL_DEPTH, att);
439          if (!att->Complete) {
440             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
441             fbo_incomplete("depth attachment incomplete", -1);
442             return;
443          }
444       }
445       else if (i == -1) {
446          att = &fb->Attachment[BUFFER_STENCIL];
447          test_attachment_completeness(ctx, GL_STENCIL, att);
448          if (!att->Complete) {
449             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
450             fbo_incomplete("stencil attachment incomplete", -1);
451             return;
452          }
453       }
454       else {
455          att = &fb->Attachment[BUFFER_COLOR0 + i];
456          test_attachment_completeness(ctx, GL_COLOR, att);
457          if (!att->Complete) {
458             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
459             fbo_incomplete("color attachment incomplete", i);
460             return;
461          }
462       }
463
464       if (att->Type == GL_TEXTURE) {
465          const struct gl_texture_image *texImg
466             = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
467          w = texImg->Width;
468          h = texImg->Height;
469          f = texImg->_BaseFormat;
470          numImages++;
471          if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT
472              && f != GL_DEPTH_STENCIL_EXT) {
473             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
474             fbo_incomplete("texture attachment incomplete", -1);
475             return;
476          }
477       }
478       else if (att->Type == GL_RENDERBUFFER_EXT) {
479          w = att->Renderbuffer->Width;
480          h = att->Renderbuffer->Height;
481          f = att->Renderbuffer->InternalFormat;
482          numImages++;
483       }
484       else {
485          assert(att->Type == GL_NONE);
486          continue;
487       }
488
489       if (numImages == 1) {
490          /* set required width, height and format */
491          width = w;
492          height = h;
493          if (i >= 0)
494             intFormat = f;
495       }
496       else {
497          /* check that width, height, format are same */
498          if (w != width || h != height) {
499             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
500             fbo_incomplete("width or height mismatch", -1);
501             return;
502          }
503          if (intFormat != GL_NONE && f != intFormat) {
504             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
505             fbo_incomplete("format mismatch", -1);
506             return;
507          }
508       }
509    }
510
511 #ifndef FEATURE_OES_framebuffer_object
512    /* Check that all DrawBuffers are present */
513    for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) {
514       if (fb->ColorDrawBuffer[j] != GL_NONE) {
515          const struct gl_renderbuffer_attachment *att
516             = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[j]);
517          assert(att);
518          if (att->Type == GL_NONE) {
519             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
520             fbo_incomplete("missing drawbuffer", j);
521             return;
522          }
523       }
524    }
525
526    /* Check that the ReadBuffer is present */
527    if (fb->ColorReadBuffer != GL_NONE) {
528       const struct gl_renderbuffer_attachment *att
529          = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer);
530       assert(att);
531       if (att->Type == GL_NONE) {
532          fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
533             fbo_incomplete("missing readbuffer", -1);
534          return;
535       }
536    }
537 #endif
538
539    if (numImages == 0) {
540       fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
541       fbo_incomplete("no attachments", -1);
542       return;
543    }
544
545    /*
546     * If we get here, the framebuffer is complete!
547     */
548    fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT;
549    fb->Width = w;
550    fb->Height = h;
551 }
552
553
554 GLboolean GLAPIENTRY
555 _mesa_IsRenderbufferEXT(GLuint renderbuffer)
556 {
557    GET_CURRENT_CONTEXT(ctx);
558    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
559    if (renderbuffer) {
560       struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
561       if (rb != NULL && rb != &DummyRenderbuffer)
562          return GL_TRUE;
563    }
564    return GL_FALSE;
565 }
566
567
568 void GLAPIENTRY
569 _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
570 {
571    struct gl_renderbuffer *newRb;
572    GET_CURRENT_CONTEXT(ctx);
573
574    ASSERT_OUTSIDE_BEGIN_END(ctx);
575
576    if (target != GL_RENDERBUFFER_EXT) {
577          _mesa_error(ctx, GL_INVALID_ENUM,
578                   "glBindRenderbufferEXT(target)");
579       return;
580    }
581
582    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
583    /* The above doesn't fully flush the drivers in the way that a
584     * glFlush does, but that is required here:
585     */
586    if (ctx->Driver.Flush)
587       ctx->Driver.Flush(ctx);
588
589
590    if (renderbuffer) {
591       newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
592       if (newRb == &DummyRenderbuffer) {
593          /* ID was reserved, but no real renderbuffer object made yet */
594          newRb = NULL;
595       }
596       if (!newRb) {
597          /* create new renderbuffer object */
598          newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
599          if (!newRb) {
600             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
601             return;
602          }
603          ASSERT(newRb->AllocStorage);
604          _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
605          newRb->RefCount = 1; /* referenced by hash table */
606       }
607    }
608    else {
609       newRb = NULL;
610    }
611
612    ASSERT(newRb != &DummyRenderbuffer);
613
614    _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb);
615 }
616
617
618 void GLAPIENTRY
619 _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
620 {
621    GLint i;
622    GET_CURRENT_CONTEXT(ctx);
623
624    ASSERT_OUTSIDE_BEGIN_END(ctx);
625    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
626
627    for (i = 0; i < n; i++) {
628       if (renderbuffers[i] > 0) {
629          struct gl_renderbuffer *rb;
630          rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]);
631          if (rb) {
632             /* check if deleting currently bound renderbuffer object */
633             if (rb == ctx->CurrentRenderbuffer) {
634                /* bind default */
635                ASSERT(rb->RefCount >= 2);
636                _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
637             }
638
639             /* Remove from hash table immediately, to free the ID.
640              * But the object will not be freed until it's no longer
641              * referenced anywhere else.
642              */
643             _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
644
645             if (rb != &DummyRenderbuffer) {
646                /* no longer referenced by hash table */
647                _mesa_reference_renderbuffer(&rb, NULL);
648             }
649          }
650       }
651    }
652 }
653
654
655 void GLAPIENTRY
656 _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
657 {
658    GET_CURRENT_CONTEXT(ctx);
659    GLuint first;
660    GLint i;
661
662    ASSERT_OUTSIDE_BEGIN_END(ctx);
663
664    if (n < 0) {
665       _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
666       return;
667    }
668
669    if (!renderbuffers)
670       return;
671
672    first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
673
674    for (i = 0; i < n; i++) {
675       GLuint name = first + i;
676       renderbuffers[i] = name;
677       /* insert dummy placeholder into hash table */
678       _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
679       _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
680       _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
681    }
682 }
683
684
685 /**
686  * Given an internal format token for a render buffer, return the
687  * corresponding base format.
688  * This is very similar to _mesa_base_tex_format() but the set of valid
689  * internal formats is somewhat different.
690  *
691  * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
692  *  GL_DEPTH_STENCIL_EXT or zero if error.
693  */
694 GLenum
695 _mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
696 {
697    switch (internalFormat) {
698    case GL_RGB:
699    case GL_R3_G3_B2:
700    case GL_RGB4:
701    case GL_RGB5:
702    case GL_RGB8:
703    case GL_RGB10:
704    case GL_RGB12:
705    case GL_RGB16:
706       return GL_RGB;
707    case GL_RGBA:
708    case GL_RGBA2:
709    case GL_RGBA4:
710    case GL_RGB5_A1:
711    case GL_RGBA8:
712    case GL_RGB10_A2:
713    case GL_RGBA12:
714    case GL_RGBA16:
715       return GL_RGBA;
716    case GL_STENCIL_INDEX:
717    case GL_STENCIL_INDEX1_EXT:
718    case GL_STENCIL_INDEX4_EXT:
719    case GL_STENCIL_INDEX8_EXT:
720    case GL_STENCIL_INDEX16_EXT:
721       return GL_STENCIL_INDEX;
722    case GL_DEPTH_COMPONENT:
723    case GL_DEPTH_COMPONENT16:
724    case GL_DEPTH_COMPONENT24:
725    case GL_DEPTH_COMPONENT32:
726       return GL_DEPTH_COMPONENT;
727    case GL_DEPTH_STENCIL_EXT:
728    case GL_DEPTH24_STENCIL8_EXT:
729       if (ctx->Extensions.EXT_packed_depth_stencil)
730          return GL_DEPTH_STENCIL_EXT;
731       else
732          return 0;
733    /* XXX add floating point formats eventually */
734    default:
735       return 0;
736    }
737 }
738
739
740 void GLAPIENTRY
741 _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
742                              GLsizei width, GLsizei height)
743 {
744    struct gl_renderbuffer *rb;
745    GLenum baseFormat;
746    GET_CURRENT_CONTEXT(ctx);
747
748    ASSERT_OUTSIDE_BEGIN_END(ctx);
749
750    if (target != GL_RENDERBUFFER_EXT) {
751       _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
752       return;
753    }
754
755    baseFormat = _mesa_base_fbo_format(ctx, internalFormat);
756    if (baseFormat == 0) {
757       _mesa_error(ctx, GL_INVALID_ENUM,
758                   "glRenderbufferStorageEXT(internalFormat)");
759       return;
760    }
761
762    if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) {
763       _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
764       return;
765    }
766
767    if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) {
768       _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
769       return;
770    }
771
772    rb = ctx->CurrentRenderbuffer;
773
774    if (!rb) {
775       _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
776       return;
777    }
778
779    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
780
781    if (rb->InternalFormat == internalFormat &&
782        rb->Width == (GLuint) width &&
783        rb->Height == (GLuint) height) {
784       /* no change in allocation needed */
785       return;
786    }
787
788    /* These MUST get set by the AllocStorage func */
789    rb->_ActualFormat = 0;
790    rb->RedBits =
791    rb->GreenBits =
792    rb->BlueBits =
793    rb->AlphaBits =
794    rb->IndexBits =
795    rb->DepthBits =
796    rb->StencilBits = 0;
797
798    /* Now allocate the storage */
799    ASSERT(rb->AllocStorage);
800    if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
801       /* No error - check/set fields now */
802       assert(rb->_ActualFormat);
803       assert(rb->Width == (GLuint) width);
804       assert(rb->Height == (GLuint) height);
805       assert(rb->RedBits || rb->GreenBits || rb->BlueBits || rb->AlphaBits ||
806              rb->DepthBits || rb->StencilBits || rb->IndexBits);
807       rb->InternalFormat = internalFormat;
808       rb->_BaseFormat = baseFormat;
809    }
810    else {
811       /* Probably ran out of memory - clear the fields */
812       rb->Width = 0;
813       rb->Height = 0;
814       rb->InternalFormat = GL_NONE;
815       rb->_ActualFormat = GL_NONE;
816       rb->_BaseFormat = GL_NONE;
817       rb->RedBits =
818       rb->GreenBits =
819       rb->BlueBits =
820       rb->AlphaBits =
821       rb->IndexBits =
822       rb->DepthBits =
823       rb->StencilBits = 0;
824    }
825
826    /*
827    test_framebuffer_completeness(ctx, fb);
828    */
829    /* XXX if this renderbuffer is attached anywhere, invalidate attachment
830     * points???
831     */
832 }
833
834
835 void GLAPIENTRY
836 _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
837 {
838    GET_CURRENT_CONTEXT(ctx);
839
840    ASSERT_OUTSIDE_BEGIN_END(ctx);
841
842    if (target != GL_RENDERBUFFER_EXT) {
843       _mesa_error(ctx, GL_INVALID_ENUM,
844                   "glGetRenderbufferParameterivEXT(target)");
845       return;
846    }
847
848    if (!ctx->CurrentRenderbuffer) {
849       _mesa_error(ctx, GL_INVALID_OPERATION,
850                   "glGetRenderbufferParameterivEXT");
851       return;
852    }
853
854    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
855
856    switch (pname) {
857    case GL_RENDERBUFFER_WIDTH_EXT:
858       *params = ctx->CurrentRenderbuffer->Width;
859       return;
860    case GL_RENDERBUFFER_HEIGHT_EXT:
861       *params = ctx->CurrentRenderbuffer->Height;
862       return;
863    case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
864       *params = ctx->CurrentRenderbuffer->InternalFormat;
865       return;
866    case GL_RENDERBUFFER_RED_SIZE_EXT:
867       *params = ctx->CurrentRenderbuffer->RedBits;
868       break;
869    case GL_RENDERBUFFER_GREEN_SIZE_EXT:
870       *params = ctx->CurrentRenderbuffer->GreenBits;
871       break;
872    case GL_RENDERBUFFER_BLUE_SIZE_EXT:
873       *params = ctx->CurrentRenderbuffer->BlueBits;
874       break;
875    case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
876       *params = ctx->CurrentRenderbuffer->AlphaBits;
877       break;
878    case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
879       *params = ctx->CurrentRenderbuffer->DepthBits;
880       break;
881    case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
882       *params = ctx->CurrentRenderbuffer->StencilBits;
883       break;
884    default:
885       _mesa_error(ctx, GL_INVALID_ENUM,
886                   "glGetRenderbufferParameterivEXT(target)");
887       return;
888    }
889 }
890
891
892 GLboolean GLAPIENTRY
893 _mesa_IsFramebufferEXT(GLuint framebuffer)
894 {
895    GET_CURRENT_CONTEXT(ctx);
896    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
897    if (framebuffer) {
898       struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer);
899       if (rb != NULL && rb != &DummyFramebuffer)
900          return GL_TRUE;
901    }
902    return GL_FALSE;
903 }
904
905
906 static void
907 check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
908 {
909    GLuint i;
910    ASSERT(ctx->Driver.RenderTexture);
911    for (i = 0; i < BUFFER_COUNT; i++) {
912       struct gl_renderbuffer_attachment *att = fb->Attachment + i;
913       struct gl_texture_object *texObj = att->Texture;
914       if (texObj
915           && att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
916          ctx->Driver.RenderTexture(ctx, fb, att);
917       }
918    }
919 }
920
921
922 /**
923  * Examine all the framebuffer's attachments to see if any are textures.
924  * If so, call ctx->Driver.FinishRenderTexture() for each texture to
925  * notify the device driver that the texture image may have changed.
926  */
927 static void
928 check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
929 {
930    if (ctx->Driver.FinishRenderTexture) {
931       GLuint i;
932       for (i = 0; i < BUFFER_COUNT; i++) {
933          struct gl_renderbuffer_attachment *att = fb->Attachment + i;
934          if (att->Texture && att->Renderbuffer) {
935             ctx->Driver.FinishRenderTexture(ctx, att);
936          }
937       }
938    }
939 }
940
941
942 void GLAPIENTRY
943 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
944 {
945    struct gl_framebuffer *newFb, *newFbread;
946    GLboolean bindReadBuf, bindDrawBuf;
947    GET_CURRENT_CONTEXT(ctx);
948
949    ASSERT_OUTSIDE_BEGIN_END(ctx);
950
951    if (!ctx->Extensions.EXT_framebuffer_object) {
952       _mesa_error(ctx, GL_INVALID_OPERATION,
953                   "glBindFramebufferEXT(unsupported)");
954       return;
955    }
956
957    switch (target) {
958 #if FEATURE_EXT_framebuffer_blit
959    case GL_DRAW_FRAMEBUFFER_EXT:
960       if (!ctx->Extensions.EXT_framebuffer_blit) {
961          _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
962          return;
963       }
964       bindDrawBuf = GL_TRUE;
965       bindReadBuf = GL_FALSE;
966       break;
967    case GL_READ_FRAMEBUFFER_EXT:
968       if (!ctx->Extensions.EXT_framebuffer_blit) {
969          _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
970          return;
971       }
972       bindDrawBuf = GL_FALSE;
973       bindReadBuf = GL_TRUE;
974       break;
975 #endif
976    case GL_FRAMEBUFFER_EXT:
977       bindDrawBuf = GL_TRUE;
978       bindReadBuf = GL_TRUE;
979       break;
980    default:
981       _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
982       return;
983    }
984
985    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
986
987    if (ctx->Driver.Flush) {  
988       ctx->Driver.Flush(ctx);
989    }
990
991    if (framebuffer) {
992       /* Binding a user-created framebuffer object */
993       newFb = _mesa_lookup_framebuffer(ctx, framebuffer);
994       if (newFb == &DummyFramebuffer) {
995          /* ID was reserved, but no real framebuffer object made yet */
996          newFb = NULL;
997       }
998       if (!newFb) {
999          /* create new framebuffer object */
1000          newFb = ctx->Driver.NewFramebuffer(ctx, framebuffer);
1001          if (!newFb) {
1002             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
1003             return;
1004          }
1005          _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
1006       }
1007       newFbread = newFb;
1008    }
1009    else {
1010       /* Binding the window system framebuffer (which was originally set
1011        * with MakeCurrent).
1012        */
1013       newFb = ctx->WinSysDrawBuffer;
1014       newFbread = ctx->WinSysReadBuffer;
1015    }
1016
1017    ASSERT(newFb);
1018    ASSERT(newFb != &DummyFramebuffer);
1019
1020    /*
1021     * XXX check if re-binding same buffer and skip some of this code.
1022     */
1023
1024    if (bindReadBuf) {
1025       _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread);
1026    }
1027
1028    if (bindDrawBuf) {
1029       /* check if old FB had any texture attachments */
1030       check_end_texture_render(ctx, ctx->DrawBuffer);
1031
1032       /* check if time to delete this framebuffer */
1033       _mesa_reference_framebuffer(&ctx->DrawBuffer, newFb);
1034
1035       if (newFb->Name != 0) {
1036          /* check if newly bound framebuffer has any texture attachments */
1037          check_begin_texture_render(ctx, newFb);
1038       }
1039    }
1040
1041    if (ctx->Driver.BindFramebuffer) {
1042       ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread);
1043    }
1044 }
1045
1046
1047 void GLAPIENTRY
1048 _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
1049 {
1050    GLint i;
1051    GET_CURRENT_CONTEXT(ctx);
1052
1053    ASSERT_OUTSIDE_BEGIN_END(ctx);
1054    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1055    /* The above doesn't fully flush the drivers in the way that a
1056     * glFlush does, but that is required here:
1057     */
1058    if (ctx->Driver.Flush)
1059       ctx->Driver.Flush(ctx);
1060
1061    for (i = 0; i < n; i++) {
1062       if (framebuffers[i] > 0) {
1063          struct gl_framebuffer *fb;
1064          fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]);
1065          if (fb) {
1066             ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
1067
1068             /* check if deleting currently bound framebuffer object */
1069             if (fb == ctx->DrawBuffer) {
1070                /* bind default */
1071                ASSERT(fb->RefCount >= 2);
1072                _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1073             }
1074
1075             /* remove from hash table immediately, to free the ID */
1076             _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
1077
1078             if (fb != &DummyFramebuffer) {
1079                /* But the object will not be freed until it's no longer
1080                 * bound in any context.
1081                 */
1082                _mesa_unreference_framebuffer(&fb);
1083             }
1084          }
1085       }
1086    }
1087 }
1088
1089
1090 void GLAPIENTRY
1091 _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
1092 {
1093    GET_CURRENT_CONTEXT(ctx);
1094    GLuint first;
1095    GLint i;
1096
1097    ASSERT_OUTSIDE_BEGIN_END(ctx);
1098
1099    if (n < 0) {
1100       _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
1101       return;
1102    }
1103
1104    if (!framebuffers)
1105       return;
1106
1107    first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
1108
1109    for (i = 0; i < n; i++) {
1110       GLuint name = first + i;
1111       framebuffers[i] = name;
1112       /* insert dummy placeholder into hash table */
1113       _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
1114       _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
1115       _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1116    }
1117 }
1118
1119
1120
1121 GLenum GLAPIENTRY
1122 _mesa_CheckFramebufferStatusEXT(GLenum target)
1123 {
1124    struct gl_framebuffer *buffer;
1125    GET_CURRENT_CONTEXT(ctx);
1126
1127    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
1128
1129    switch (target) {
1130 #if FEATURE_EXT_framebuffer_blit
1131    case GL_DRAW_FRAMEBUFFER_EXT:
1132       if (!ctx->Extensions.EXT_framebuffer_blit) {
1133          _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1134          return 0;
1135       }
1136       buffer = ctx->DrawBuffer;
1137       break;
1138    case GL_READ_FRAMEBUFFER_EXT:
1139       if (!ctx->Extensions.EXT_framebuffer_blit) {
1140          _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1141          return 0;
1142       }
1143       buffer = ctx->ReadBuffer;
1144       break;
1145 #endif
1146    case GL_FRAMEBUFFER_EXT:
1147       buffer = ctx->DrawBuffer;
1148       break;
1149    default:
1150       _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1151       return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
1152    }
1153
1154    if (buffer->Name == 0) {
1155       /* The window system / default framebuffer is always complete */
1156       return GL_FRAMEBUFFER_COMPLETE_EXT;
1157    }
1158
1159    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1160
1161    _mesa_test_framebuffer_completeness(ctx, buffer);
1162    return buffer->_Status;
1163 }
1164
1165
1166
1167 /**
1168  * Common code called by glFramebufferTexture1D/2D/3DEXT().
1169  */
1170 static void
1171 framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target, 
1172                     GLenum attachment, GLenum textarget, GLuint texture,
1173                     GLint level, GLint zoffset)
1174 {
1175    struct gl_renderbuffer_attachment *att;
1176    struct gl_texture_object *texObj = NULL;
1177    struct gl_framebuffer *fb;
1178
1179    ASSERT_OUTSIDE_BEGIN_END(ctx);
1180
1181    if (target != GL_FRAMEBUFFER_EXT) {
1182       _mesa_error(ctx, GL_INVALID_ENUM,
1183                   "glFramebufferTexture%sEXT(target)", caller);
1184       return;
1185    }
1186
1187    fb = ctx->DrawBuffer;
1188    ASSERT(fb);
1189
1190    /* check framebuffer binding */
1191    if (fb->Name == 0) {
1192       _mesa_error(ctx, GL_INVALID_OPERATION,
1193                   "glFramebufferTexture%sEXT", caller);
1194       return;
1195    }
1196
1197
1198    /* The textarget, level, and zoffset parameters are only validated if
1199     * texture is non-zero.
1200     */
1201    if (texture) {
1202       GLboolean err = GL_TRUE;
1203
1204       texObj = _mesa_lookup_texture(ctx, texture);
1205       if (texObj != NULL) {
1206          if (textarget == 0) {
1207             err = (texObj->Target != GL_TEXTURE_3D) &&
1208                 (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) &&
1209                 (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT);
1210          }
1211          else {
1212             err = (texObj->Target == GL_TEXTURE_CUBE_MAP)
1213                 ? !IS_CUBE_FACE(textarget)
1214                 : (texObj->Target != textarget);
1215          }
1216       }
1217
1218       if (err) {
1219          _mesa_error(ctx, GL_INVALID_OPERATION,
1220                      "glFramebufferTexture%sEXT(texture target mismatch)",
1221                      caller);
1222          return;
1223       }
1224
1225       if (texObj->Target == GL_TEXTURE_3D) {
1226          const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1227          if (zoffset < 0 || zoffset >= maxSize) {
1228             _mesa_error(ctx, GL_INVALID_VALUE,
1229                         "glFramebufferTexture%sEXT(zoffset)", caller);
1230             return;
1231          }
1232       }
1233       else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) ||
1234                (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT)) {
1235          if (zoffset < 0 || zoffset >= ctx->Const.MaxArrayTextureLayers) {
1236             _mesa_error(ctx, GL_INVALID_VALUE,
1237                         "glFramebufferTexture%sEXT(layer)", caller);
1238             return;
1239          }
1240       }
1241
1242
1243       if ((level < 0) || 
1244           (level >= _mesa_max_texture_levels(ctx, texObj->Target))) {
1245          _mesa_error(ctx, GL_INVALID_VALUE,
1246                      "glFramebufferTexture%sEXT(level)", caller);
1247          return;
1248       }
1249    }
1250
1251    att = _mesa_get_attachment(ctx, fb, attachment);
1252    if (att == NULL) {
1253       _mesa_error(ctx, GL_INVALID_ENUM,
1254                   "glFramebufferTexture%sEXT(attachment)", caller);
1255       return;
1256    }
1257
1258    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1259    /* The above doesn't fully flush the drivers in the way that a
1260     * glFlush does, but that is required here:
1261     */
1262    if (ctx->Driver.Flush)
1263       ctx->Driver.Flush(ctx);
1264
1265    _glthread_LOCK_MUTEX(fb->Mutex);
1266    if (texObj) {
1267       _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
1268                                    level, zoffset);
1269    }
1270    else {
1271       _mesa_remove_attachment(ctx, att);
1272    }
1273    _glthread_UNLOCK_MUTEX(fb->Mutex);
1274 }
1275
1276
1277
1278 void GLAPIENTRY
1279 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1280                               GLenum textarget, GLuint texture, GLint level)
1281 {
1282    GET_CURRENT_CONTEXT(ctx);
1283
1284    if ((texture != 0) && (textarget != GL_TEXTURE_1D)) {
1285       _mesa_error(ctx, GL_INVALID_ENUM,
1286                   "glFramebufferTexture1DEXT(textarget)");
1287       return;
1288    }
1289
1290    framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
1291                        level, 0);
1292 }
1293
1294
1295 void GLAPIENTRY
1296 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1297                               GLenum textarget, GLuint texture, GLint level)
1298 {
1299    GET_CURRENT_CONTEXT(ctx);
1300
1301    if ((texture != 0) &&
1302        (textarget != GL_TEXTURE_2D) &&
1303        (textarget != GL_TEXTURE_RECTANGLE_ARB) &&
1304        (!IS_CUBE_FACE(textarget))) {
1305       _mesa_error(ctx, GL_INVALID_OPERATION,
1306                   "glFramebufferTexture2DEXT(textarget)");
1307       return;
1308    }
1309
1310    framebuffer_texture(ctx, "2D", target, attachment, textarget, texture,
1311                        level, 0);
1312 }
1313
1314
1315 void GLAPIENTRY
1316 _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1317                               GLenum textarget, GLuint texture,
1318                               GLint level, GLint zoffset)
1319 {
1320    GET_CURRENT_CONTEXT(ctx);
1321
1322    if ((texture != 0) && (textarget != GL_TEXTURE_3D)) {
1323       _mesa_error(ctx, GL_INVALID_ENUM,
1324                   "glFramebufferTexture3DEXT(textarget)");
1325       return;
1326    }
1327
1328    framebuffer_texture(ctx, "3D", target, attachment, textarget, texture,
1329                        level, zoffset);
1330 }
1331
1332
1333 void GLAPIENTRY
1334 _mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment,
1335                                  GLuint texture, GLint level, GLint layer)
1336 {
1337    GET_CURRENT_CONTEXT(ctx);
1338
1339    framebuffer_texture(ctx, "Layer", target, attachment, 0, texture,
1340                        level, layer);
1341 }
1342
1343
1344 void GLAPIENTRY
1345 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1346                                  GLenum renderbufferTarget,
1347                                  GLuint renderbuffer)
1348 {
1349    struct gl_renderbuffer_attachment *att;
1350    struct gl_framebuffer *fb;
1351    struct gl_renderbuffer *rb;
1352    GET_CURRENT_CONTEXT(ctx);
1353
1354    ASSERT_OUTSIDE_BEGIN_END(ctx);
1355
1356    switch (target) {
1357 #if FEATURE_EXT_framebuffer_blit
1358    case GL_DRAW_FRAMEBUFFER_EXT:
1359       if (!ctx->Extensions.EXT_framebuffer_blit) {
1360          _mesa_error(ctx, GL_INVALID_ENUM,
1361                      "glFramebufferRenderbufferEXT(target)");
1362          return;
1363       }
1364       fb = ctx->DrawBuffer;
1365       break;
1366    case GL_READ_FRAMEBUFFER_EXT:
1367       if (!ctx->Extensions.EXT_framebuffer_blit) {
1368          _mesa_error(ctx, GL_INVALID_ENUM,
1369                      "glFramebufferRenderbufferEXT(target)");
1370          return;
1371       }
1372       fb = ctx->ReadBuffer;
1373       break;
1374 #endif
1375    case GL_FRAMEBUFFER_EXT:
1376       fb = ctx->DrawBuffer;
1377       break;
1378    default:
1379       _mesa_error(ctx, GL_INVALID_ENUM,
1380                   "glFramebufferRenderbufferEXT(target)");
1381       return;
1382    }
1383
1384    if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1385       _mesa_error(ctx, GL_INVALID_ENUM,
1386                   "glFramebufferRenderbufferEXT(renderbufferTarget)");
1387       return;
1388    }
1389
1390    if (fb->Name == 0) {
1391       /* Can't attach new renderbuffers to a window system framebuffer */
1392       _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1393       return;
1394    }
1395
1396    att = _mesa_get_attachment(ctx, fb, attachment);
1397    if (att == NULL) {
1398       _mesa_error(ctx, GL_INVALID_ENUM,
1399                  "glFramebufferRenderbufferEXT(attachment)");
1400       return;
1401    }
1402
1403    if (renderbuffer) {
1404       rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
1405       if (!rb) {
1406          _mesa_error(ctx, GL_INVALID_OPERATION,
1407                      "glFramebufferRenderbufferEXT(renderbuffer)");
1408          return;
1409       }
1410    }
1411    else {
1412       /* remove renderbuffer attachment */
1413       rb = NULL;
1414    }
1415
1416    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1417    /* The above doesn't fully flush the drivers in the way that a
1418     * glFlush does, but that is required here:
1419     */
1420    if (ctx->Driver.Flush)
1421       ctx->Driver.Flush(ctx);
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
1433 void 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    /* The above doesn't fully flush the drivers in the way that a
1486     * glFlush does, but that is required here:
1487     */
1488    if (ctx->Driver.Flush)
1489       ctx->Driver.Flush(ctx);
1490
1491    switch (pname) {
1492    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1493       *params = att->Type;
1494       return;
1495    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1496       if (att->Type == GL_RENDERBUFFER_EXT) {
1497          *params = att->Renderbuffer->Name;
1498       }
1499       else if (att->Type == GL_TEXTURE) {
1500          *params = att->Texture->Name;
1501       }
1502       else {
1503          _mesa_error(ctx, GL_INVALID_ENUM,
1504                      "glGetFramebufferAttachmentParameterivEXT(pname)");
1505       }
1506       return;
1507    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1508       if (att->Type == GL_TEXTURE) {
1509          *params = att->TextureLevel;
1510       }
1511       else {
1512          _mesa_error(ctx, GL_INVALID_ENUM,
1513                      "glGetFramebufferAttachmentParameterivEXT(pname)");
1514       }
1515       return;
1516    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1517       if (att->Type == GL_TEXTURE) {
1518          if (att->Texture && att->Texture->Target == GL_TEXTURE_CUBE_MAP) {
1519             *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1520          }
1521          else {
1522             *params = 0;
1523          }
1524       }
1525       else {
1526          _mesa_error(ctx, GL_INVALID_ENUM,
1527                      "glGetFramebufferAttachmentParameterivEXT(pname)");
1528       }
1529       return;
1530    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1531       if (att->Type == GL_TEXTURE) {
1532          if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) {
1533             *params = att->Zoffset;
1534          }
1535          else {
1536             *params = 0;
1537          }
1538       }
1539       else {
1540          _mesa_error(ctx, GL_INVALID_ENUM,
1541                      "glGetFramebufferAttachmentParameterivEXT(pname)");
1542       }
1543       return;
1544    default:
1545       _mesa_error(ctx, GL_INVALID_ENUM,
1546                   "glGetFramebufferAttachmentParameterivEXT(pname)");
1547       return;
1548    }
1549 }
1550
1551
1552 void GLAPIENTRY
1553 _mesa_GenerateMipmapEXT(GLenum target)
1554 {
1555    struct gl_texture_unit *texUnit;
1556    struct gl_texture_object *texObj;
1557    GET_CURRENT_CONTEXT(ctx);
1558
1559    ASSERT_OUTSIDE_BEGIN_END(ctx);
1560    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1561
1562    switch (target) {
1563    case GL_TEXTURE_1D:
1564    case GL_TEXTURE_2D:
1565    case GL_TEXTURE_3D:
1566    case GL_TEXTURE_CUBE_MAP:
1567       /* OK, legal value */
1568       break;
1569    default:
1570       _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1571       return;
1572    }
1573
1574    texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1575    texObj = _mesa_select_tex_object(ctx, texUnit, target);
1576
1577    _mesa_lock_texture(ctx, texObj);
1578    if (target == GL_TEXTURE_CUBE_MAP) {
1579       int face;
1580
1581       for (face = 0; face < 6; face++)
1582          ctx->Driver.GenerateMipmap(ctx,
1583                                     GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face,
1584                                     texObj);
1585    } else {
1586       ctx->Driver.GenerateMipmap(ctx, target, texObj);
1587    }
1588    _mesa_unlock_texture(ctx, texObj);
1589 }
1590
1591
1592 #if FEATURE_EXT_framebuffer_blit
1593 void GLAPIENTRY
1594 _mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
1595                          GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
1596                          GLbitfield mask, GLenum filter)
1597 {
1598    GET_CURRENT_CONTEXT(ctx);
1599
1600    ASSERT_OUTSIDE_BEGIN_END(ctx);
1601    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1602
1603    if (ctx->NewState) {
1604       _mesa_update_state(ctx);
1605    }
1606
1607    if (!ctx->ReadBuffer) {
1608       /* XXX */
1609    }
1610
1611    /* check for complete framebuffers */
1612    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
1613        ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1614       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
1615                   "glBlitFramebufferEXT(incomplete draw/read buffers)");
1616       return;
1617    }
1618
1619    if (filter != GL_NEAREST && filter != GL_LINEAR) {
1620       _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)");
1621       return;
1622    }
1623
1624    if (mask & ~(GL_COLOR_BUFFER_BIT |
1625                 GL_DEPTH_BUFFER_BIT |
1626                 GL_STENCIL_BUFFER_BIT)) {
1627       _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)");
1628       return;
1629    }
1630
1631    /* depth/stencil must be blitted with nearest filtering */
1632    if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
1633         && filter != GL_NEAREST) {
1634       _mesa_error(ctx, GL_INVALID_OPERATION,
1635              "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter");
1636       return;
1637    }
1638
1639    if (mask & GL_STENCIL_BUFFER_BIT) {
1640       struct gl_renderbuffer *readRb = ctx->ReadBuffer->_StencilBuffer;
1641       struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_StencilBuffer;
1642       if (readRb->StencilBits != drawRb->StencilBits) {
1643          _mesa_error(ctx, GL_INVALID_OPERATION,
1644                      "glBlitFramebufferEXT(stencil buffer size mismatch");
1645          return;
1646       }
1647    }
1648
1649    if (mask & GL_DEPTH_BUFFER_BIT) {
1650       struct gl_renderbuffer *readRb = ctx->ReadBuffer->_DepthBuffer;
1651       struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_DepthBuffer;
1652       if (readRb->DepthBits != drawRb->DepthBits) {
1653          _mesa_error(ctx, GL_INVALID_OPERATION,
1654                      "glBlitFramebufferEXT(depth buffer size mismatch");
1655          return;
1656       }
1657    }
1658
1659    if (!ctx->Extensions.EXT_framebuffer_blit) {
1660       _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT");
1661       return;
1662    }
1663
1664    ASSERT(ctx->Driver.BlitFramebuffer);
1665    ctx->Driver.BlitFramebuffer(ctx,
1666                                srcX0, srcY0, srcX1, srcY1,
1667                                dstX0, dstY0, dstX1, dstY1,
1668                                mask, filter);
1669 }
1670 #endif /* FEATURE_EXT_framebuffer_blit */