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