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