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