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