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