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