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