nouveau: unbreak nv40
[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_dereference_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_dereference_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, *oldFb;
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 _glthread_LOCK_MUTEX(newFb->Mutex);
1002 if (bindReadBuf)
1003 newFb->RefCount++;
1004 if (bindDrawBuf)
1005 newFb->RefCount++;
1006 _glthread_UNLOCK_MUTEX(newFb->Mutex);
1007 }
1008 else {
1009 /* Binding the window system framebuffer (which was originally set
1010 * with MakeCurrent).
1011 */
1012 newFb = ctx->WinSysDrawBuffer;
1013 }
1014
1015 ASSERT(newFb);
1016 ASSERT(newFb != &DummyFramebuffer);
1017
1018 /*
1019 * XXX check if re-binding same buffer and skip some of this code.
1020 */
1021
1022 if (bindReadBuf) {
1023 oldFb = ctx->ReadBuffer;
1024 if (oldFb && oldFb->Name != 0) {
1025 _mesa_dereference_framebuffer(&oldFb);
1026 }
1027 ctx->ReadBuffer = newFb;
1028 }
1029
1030 if (bindDrawBuf) {
1031 oldFb = ctx->DrawBuffer;
1032 if (oldFb && oldFb->Name != 0) {
1033 /* check if old FB had any texture attachments */
1034 check_end_texture_render(ctx, oldFb);
1035 /* check if time to delete this framebuffer */
1036 _mesa_dereference_framebuffer(&oldFb);
1037 }
1038 ctx->DrawBuffer = newFb;
1039 if (newFb->Name != 0) {
1040 /* check if newly bound framebuffer has any texture attachments */
1041 check_begin_texture_render(ctx, newFb);
1042 }
1043 }
1044
1045 if (ctx->Driver.BindFramebuffer) {
1046 ctx->Driver.BindFramebuffer(ctx, target, newFb);
1047 }
1048 }
1049
1050
1051 void GLAPIENTRY
1052 _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
1053 {
1054 GLint i;
1055 GET_CURRENT_CONTEXT(ctx);
1056
1057 ASSERT_OUTSIDE_BEGIN_END(ctx);
1058 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1059 /* The above doesn't fully flush the drivers in the way that a
1060 * glFlush does, but that is required here:
1061 */
1062 if (ctx->Driver.Flush)
1063 ctx->Driver.Flush(ctx);
1064
1065 for (i = 0; i < n; i++) {
1066 if (framebuffers[i] > 0) {
1067 struct gl_framebuffer *fb;
1068 fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]);
1069 if (fb) {
1070 ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
1071
1072 /* check if deleting currently bound framebuffer object */
1073 if (fb == ctx->DrawBuffer) {
1074 /* bind default */
1075 ASSERT(fb->RefCount >= 2);
1076 _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1077 }
1078
1079 /* remove from hash table immediately, to free the ID */
1080 _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
1081
1082 if (fb != &DummyFramebuffer) {
1083 /* But the object will not be freed until it's no longer
1084 * bound in any context.
1085 */
1086 _mesa_dereference_framebuffer(&fb);
1087 }
1088 }
1089 }
1090 }
1091 }
1092
1093
1094 void GLAPIENTRY
1095 _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
1096 {
1097 GET_CURRENT_CONTEXT(ctx);
1098 GLuint first;
1099 GLint i;
1100
1101 ASSERT_OUTSIDE_BEGIN_END(ctx);
1102
1103 if (n < 0) {
1104 _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
1105 return;
1106 }
1107
1108 if (!framebuffers)
1109 return;
1110
1111 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
1112
1113 for (i = 0; i < n; i++) {
1114 GLuint name = first + i;
1115 framebuffers[i] = name;
1116 /* insert dummy placeholder into hash table */
1117 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
1118 _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
1119 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
1120 }
1121 }
1122
1123
1124
1125 GLenum GLAPIENTRY
1126 _mesa_CheckFramebufferStatusEXT(GLenum target)
1127 {
1128 struct gl_framebuffer *buffer;
1129 GET_CURRENT_CONTEXT(ctx);
1130
1131 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
1132
1133 switch (target) {
1134 #if FEATURE_EXT_framebuffer_blit
1135 case GL_DRAW_FRAMEBUFFER_EXT:
1136 if (!ctx->Extensions.EXT_framebuffer_blit) {
1137 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1138 return 0;
1139 }
1140 buffer = ctx->DrawBuffer;
1141 break;
1142 case GL_READ_FRAMEBUFFER_EXT:
1143 if (!ctx->Extensions.EXT_framebuffer_blit) {
1144 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1145 return 0;
1146 }
1147 buffer = ctx->ReadBuffer;
1148 break;
1149 #endif
1150 case GL_FRAMEBUFFER_EXT:
1151 buffer = ctx->DrawBuffer;
1152 break;
1153 default:
1154 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
1155 return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
1156 }
1157
1158 if (buffer->Name == 0) {
1159 /* The window system / default framebuffer is always complete */
1160 return GL_FRAMEBUFFER_COMPLETE_EXT;
1161 }
1162
1163 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1164
1165 _mesa_test_framebuffer_completeness(ctx, buffer);
1166 return buffer->_Status;
1167 }
1168
1169
1170
1171 /**
1172 * Common code called by glFramebufferTexture1D/2D/3DEXT().
1173 */
1174 static void
1175 framebuffer_texture(GLuint dims, GLenum target, GLenum attachment,
1176 GLenum textarget, GLuint texture,
1177 GLint level, GLint zoffset)
1178 {
1179 struct gl_renderbuffer_attachment *att;
1180 struct gl_texture_object *texObj = NULL;
1181 struct gl_framebuffer *fb;
1182 GET_CURRENT_CONTEXT(ctx);
1183
1184 ASSERT_OUTSIDE_BEGIN_END(ctx);
1185
1186 if (target != GL_FRAMEBUFFER_EXT) {
1187 _mesa_error(ctx, GL_INVALID_ENUM,
1188 "glFramebufferTexture%dDEXT(target)", dims);
1189 return;
1190 }
1191
1192 fb = ctx->DrawBuffer;
1193 ASSERT(fb);
1194
1195 /* check framebuffer binding */
1196 if (fb->Name == 0) {
1197 _mesa_error(ctx, GL_INVALID_OPERATION,
1198 "glFramebufferTexture%dDEXT", dims);
1199 return;
1200 }
1201
1202 if (texture) {
1203 texObj = _mesa_lookup_texture(ctx, texture);
1204 }
1205
1206 /* Check dimension-dependent things */
1207 switch (dims) {
1208 case 1:
1209 if (textarget != GL_TEXTURE_1D) {
1210 _mesa_error(ctx, GL_INVALID_ENUM,
1211 "glFramebufferTexture1DEXT(textarget)");
1212 return;
1213 }
1214 if (texObj && texObj->Target != GL_TEXTURE_1D) {
1215 _mesa_error(ctx, GL_INVALID_OPERATION,
1216 "glFramebufferTexture1DEXT(texture target mismatch)");
1217 return;
1218 }
1219 break;
1220 case 2:
1221 if (textarget != GL_TEXTURE_2D &&
1222 textarget != GL_TEXTURE_RECTANGLE_ARB &&
1223 !IS_CUBE_FACE(textarget)) {
1224 _mesa_error(ctx, GL_INVALID_ENUM,
1225 "glFramebufferTexture2DEXT(textarget)");
1226 return;
1227 }
1228 if (texObj) {
1229 if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
1230 (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
1231 && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
1232 (texObj->Target == GL_TEXTURE_CUBE_MAP
1233 && !IS_CUBE_FACE(textarget))) {
1234 _mesa_error(ctx, GL_INVALID_OPERATION,
1235 "glFramebufferTexture1DEXT(texture target mismatch)");
1236 return;
1237 }
1238 }
1239 break;
1240 case 3:
1241 if (textarget != GL_TEXTURE_3D) {
1242 _mesa_error(ctx, GL_INVALID_ENUM,
1243 "glFramebufferTexture3DEXT(textarget)");
1244 return;
1245 }
1246 if (texObj && texObj->Target != GL_TEXTURE_3D) {
1247 _mesa_error(ctx, GL_INVALID_OPERATION,
1248 "glFramebufferTexture3DEXT(texture target mismatch)");
1249 return;
1250 }
1251 {
1252 const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1253 if (zoffset < 0 || zoffset >= maxSize) {
1254 _mesa_error(ctx, GL_INVALID_VALUE,
1255 "glFramebufferTexture3DEXT(zoffset)");
1256 return;
1257 }
1258 }
1259 break;
1260 default:
1261 _mesa_problem(ctx, "Unexpected dims in error_check_framebuffer_texture");
1262 return;
1263 }
1264
1265 if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
1266 _mesa_error(ctx, GL_INVALID_VALUE,
1267 "glFramebufferTexture%dDEXT(level)", dims);
1268 return;
1269 }
1270
1271 att = _mesa_get_attachment(ctx, fb, attachment);
1272 if (att == NULL) {
1273 _mesa_error(ctx, GL_INVALID_ENUM,
1274 "glFramebufferTexture%dDEXT(attachment)", dims);
1275 return;
1276 }
1277
1278 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1279 /* The above doesn't fully flush the drivers in the way that a
1280 * glFlush does, but that is required here:
1281 */
1282 if (ctx->Driver.Flush)
1283 ctx->Driver.Flush(ctx);
1284
1285 _glthread_LOCK_MUTEX(fb->Mutex);
1286 if (texObj) {
1287 _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
1288 level, zoffset);
1289 }
1290 else {
1291 _mesa_remove_attachment(ctx, att);
1292 }
1293 _glthread_UNLOCK_MUTEX(fb->Mutex);
1294 }
1295
1296
1297
1298 void GLAPIENTRY
1299 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1300 GLenum textarget, GLuint texture, GLint level)
1301 {
1302 const GLint zoffset = 0;
1303 framebuffer_texture(1, target, attachment, textarget, texture,
1304 level, zoffset);
1305 }
1306
1307
1308 void GLAPIENTRY
1309 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1310 GLenum textarget, GLuint texture, GLint level)
1311 {
1312 const GLint zoffset = 0;
1313 framebuffer_texture(2, target, attachment, textarget, texture,
1314 level, zoffset);
1315 }
1316
1317
1318 void GLAPIENTRY
1319 _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1320 GLenum textarget, GLuint texture,
1321 GLint level, GLint zoffset)
1322 {
1323 framebuffer_texture(3, target, attachment, textarget, texture,
1324 level, zoffset);
1325 }
1326
1327
1328
1329 void GLAPIENTRY
1330 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1331 GLenum renderbufferTarget,
1332 GLuint renderbuffer)
1333 {
1334 struct gl_renderbuffer_attachment *att;
1335 struct gl_framebuffer *fb;
1336 struct gl_renderbuffer *rb;
1337 GET_CURRENT_CONTEXT(ctx);
1338
1339 ASSERT_OUTSIDE_BEGIN_END(ctx);
1340
1341 switch (target) {
1342 #if FEATURE_EXT_framebuffer_blit
1343 case GL_DRAW_FRAMEBUFFER_EXT:
1344 if (!ctx->Extensions.EXT_framebuffer_blit) {
1345 _mesa_error(ctx, GL_INVALID_ENUM,
1346 "glFramebufferRenderbufferEXT(target)");
1347 return;
1348 }
1349 fb = ctx->DrawBuffer;
1350 break;
1351 case GL_READ_FRAMEBUFFER_EXT:
1352 if (!ctx->Extensions.EXT_framebuffer_blit) {
1353 _mesa_error(ctx, GL_INVALID_ENUM,
1354 "glFramebufferRenderbufferEXT(target)");
1355 return;
1356 }
1357 fb = ctx->ReadBuffer;
1358 break;
1359 #endif
1360 case GL_FRAMEBUFFER_EXT:
1361 fb = ctx->DrawBuffer;
1362 break;
1363 default:
1364 _mesa_error(ctx, GL_INVALID_ENUM,
1365 "glFramebufferRenderbufferEXT(target)");
1366 return;
1367 }
1368
1369 if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1370 _mesa_error(ctx, GL_INVALID_ENUM,
1371 "glFramebufferRenderbufferEXT(renderbufferTarget)");
1372 return;
1373 }
1374
1375 if (fb->Name == 0) {
1376 /* Can't attach new renderbuffers to a window system framebuffer */
1377 _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1378 return;
1379 }
1380
1381 att = _mesa_get_attachment(ctx, fb, attachment);
1382 if (att == NULL) {
1383 _mesa_error(ctx, GL_INVALID_ENUM,
1384 "glFramebufferRenderbufferEXT(attachment)");
1385 return;
1386 }
1387
1388 if (renderbuffer) {
1389 rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
1390 if (!rb) {
1391 _mesa_error(ctx, GL_INVALID_OPERATION,
1392 "glFramebufferRenderbufferEXT(renderbuffer)");
1393 return;
1394 }
1395 }
1396 else {
1397 /* remove renderbuffer attachment */
1398 rb = NULL;
1399 }
1400
1401 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1402 /* The above doesn't fully flush the drivers in the way that a
1403 * glFlush does, but that is required here:
1404 */
1405 if (ctx->Driver.Flush)
1406 ctx->Driver.Flush(ctx);
1407
1408 assert(ctx->Driver.FramebufferRenderbuffer);
1409 ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb);
1410
1411 /* Some subsequent GL commands may depend on the framebuffer's visual
1412 * after the binding is updated. Update visual info now.
1413 */
1414 _mesa_update_framebuffer_visual(fb);
1415 }
1416
1417
1418 void GLAPIENTRY
1419 _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1420 GLenum pname, GLint *params)
1421 {
1422 const struct gl_renderbuffer_attachment *att;
1423 struct gl_framebuffer *buffer;
1424 GET_CURRENT_CONTEXT(ctx);
1425
1426 ASSERT_OUTSIDE_BEGIN_END(ctx);
1427
1428 switch (target) {
1429 #if FEATURE_EXT_framebuffer_blit
1430 case GL_DRAW_FRAMEBUFFER_EXT:
1431 if (!ctx->Extensions.EXT_framebuffer_blit) {
1432 _mesa_error(ctx, GL_INVALID_ENUM,
1433 "glGetFramebufferAttachmentParameterivEXT(target)");
1434 return;
1435 }
1436 buffer = ctx->DrawBuffer;
1437 break;
1438 case GL_READ_FRAMEBUFFER_EXT:
1439 if (!ctx->Extensions.EXT_framebuffer_blit) {
1440 _mesa_error(ctx, GL_INVALID_ENUM,
1441 "glGetFramebufferAttachmentParameterivEXT(target)");
1442 return;
1443 }
1444 buffer = ctx->ReadBuffer;
1445 break;
1446 #endif
1447 case GL_FRAMEBUFFER_EXT:
1448 buffer = ctx->DrawBuffer;
1449 break;
1450 default:
1451 _mesa_error(ctx, GL_INVALID_ENUM,
1452 "glGetFramebufferAttachmentParameterivEXT(target)");
1453 return;
1454 }
1455
1456 if (buffer->Name == 0) {
1457 _mesa_error(ctx, GL_INVALID_OPERATION,
1458 "glGetFramebufferAttachmentParameterivEXT");
1459 return;
1460 }
1461
1462 att = _mesa_get_attachment(ctx, buffer, attachment);
1463 if (att == NULL) {
1464 _mesa_error(ctx, GL_INVALID_ENUM,
1465 "glGetFramebufferAttachmentParameterivEXT(attachment)");
1466 return;
1467 }
1468
1469 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1470 /* The above doesn't fully flush the drivers in the way that a
1471 * glFlush does, but that is required here:
1472 */
1473 if (ctx->Driver.Flush)
1474 ctx->Driver.Flush(ctx);
1475
1476 switch (pname) {
1477 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1478 *params = att->Type;
1479 return;
1480 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1481 if (att->Type == GL_RENDERBUFFER_EXT) {
1482 *params = att->Renderbuffer->Name;
1483 }
1484 else if (att->Type == GL_TEXTURE) {
1485 *params = att->Texture->Name;
1486 }
1487 else {
1488 _mesa_error(ctx, GL_INVALID_ENUM,
1489 "glGetFramebufferAttachmentParameterivEXT(pname)");
1490 }
1491 return;
1492 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1493 if (att->Type == GL_TEXTURE) {
1494 *params = att->TextureLevel;
1495 }
1496 else {
1497 _mesa_error(ctx, GL_INVALID_ENUM,
1498 "glGetFramebufferAttachmentParameterivEXT(pname)");
1499 }
1500 return;
1501 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1502 if (att->Type == GL_TEXTURE) {
1503 *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1504 }
1505 else {
1506 _mesa_error(ctx, GL_INVALID_ENUM,
1507 "glGetFramebufferAttachmentParameterivEXT(pname)");
1508 }
1509 return;
1510 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1511 if (att->Type == GL_TEXTURE) {
1512 *params = att->Zoffset;
1513 }
1514 else {
1515 _mesa_error(ctx, GL_INVALID_ENUM,
1516 "glGetFramebufferAttachmentParameterivEXT(pname)");
1517 }
1518 return;
1519 default:
1520 _mesa_error(ctx, GL_INVALID_ENUM,
1521 "glGetFramebufferAttachmentParameterivEXT(pname)");
1522 return;
1523 }
1524 }
1525
1526
1527 void GLAPIENTRY
1528 _mesa_GenerateMipmapEXT(GLenum target)
1529 {
1530 struct gl_texture_unit *texUnit;
1531 struct gl_texture_object *texObj;
1532 GET_CURRENT_CONTEXT(ctx);
1533
1534 ASSERT_OUTSIDE_BEGIN_END(ctx);
1535 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1536
1537 switch (target) {
1538 case GL_TEXTURE_1D:
1539 case GL_TEXTURE_2D:
1540 case GL_TEXTURE_3D:
1541 case GL_TEXTURE_CUBE_MAP:
1542 /* OK, legal value */
1543 break;
1544 default:
1545 _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1546 return;
1547 }
1548
1549 texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1550 texObj = _mesa_select_tex_object(ctx, texUnit, target);
1551
1552 /* XXX this might not handle cube maps correctly */
1553 _mesa_lock_texture(ctx, texObj);
1554 _mesa_generate_mipmap(ctx, target, texUnit, texObj);
1555 _mesa_unlock_texture(ctx, texObj);
1556 }
1557
1558
1559 #if FEATURE_EXT_framebuffer_blit
1560 void GLAPIENTRY
1561 _mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
1562 GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
1563 GLbitfield mask, GLenum filter)
1564 {
1565 GET_CURRENT_CONTEXT(ctx);
1566
1567 ASSERT_OUTSIDE_BEGIN_END(ctx);
1568 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1569
1570 if (ctx->NewState) {
1571 _mesa_update_state(ctx);
1572 }
1573
1574 if (!ctx->ReadBuffer) {
1575 /* XXX */
1576 }
1577
1578 /* check for complete framebuffers */
1579 if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
1580 ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1581 _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
1582 "glBlitFramebufferEXT(incomplete draw/read buffers)");
1583 return;
1584 }
1585
1586 if (filter != GL_NEAREST && filter != GL_LINEAR) {
1587 _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)");
1588 return;
1589 }
1590
1591 if (mask & ~(GL_COLOR_BUFFER_BIT |
1592 GL_DEPTH_BUFFER_BIT |
1593 GL_STENCIL_BUFFER_BIT)) {
1594 _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)");
1595 return;
1596 }
1597
1598 /* depth/stencil must be blitted with nearest filtering */
1599 if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
1600 && filter != GL_NEAREST) {
1601 _mesa_error(ctx, GL_INVALID_OPERATION,
1602 "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter");
1603 return;
1604 }
1605
1606 if (mask & GL_STENCIL_BUFFER_BIT) {
1607 struct gl_renderbuffer *readRb = ctx->ReadBuffer->_StencilBuffer;
1608 struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_StencilBuffer;
1609 if (readRb->StencilBits != drawRb->StencilBits) {
1610 _mesa_error(ctx, GL_INVALID_OPERATION,
1611 "glBlitFramebufferEXT(stencil buffer size mismatch");
1612 return;
1613 }
1614 }
1615
1616 if (mask & GL_DEPTH_BUFFER_BIT) {
1617 struct gl_renderbuffer *readRb = ctx->ReadBuffer->_DepthBuffer;
1618 struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_DepthBuffer;
1619 if (readRb->DepthBits != drawRb->DepthBits) {
1620 _mesa_error(ctx, GL_INVALID_OPERATION,
1621 "glBlitFramebufferEXT(depth buffer size mismatch");
1622 return;
1623 }
1624 }
1625
1626 if (!ctx->Extensions.EXT_framebuffer_blit) {
1627 _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT");
1628 return;
1629 }
1630
1631 ASSERT(ctx->Driver.BlitFramebuffer);
1632 ctx->Driver.BlitFramebuffer(ctx,
1633 srcX0, srcY0, srcX1, srcY1,
1634 dstX0, dstY0, dstX1, dstY1,
1635 mask, filter);
1636 }
1637 #endif /* FEATURE_EXT_framebuffer_blit */