remove ^M chars, disable shading language extensions
[mesa.git] / src / mesa / main / fbobject.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.3
4 *
5 * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /*
27 * Authors:
28 * Brian Paul
29 */
30
31
32 #include "context.h"
33 #include "fbobject.h"
34 #include "framebuffer.h"
35 #include "hash.h"
36 #include "renderbuffer.h"
37 #include "teximage.h"
38 #include "texstore.h"
39
40
41 /**
42 * Notes:
43 *
44 * None of the GL_EXT_framebuffer_object functions are compiled into
45 * display lists.
46 */
47
48
49
50 /*
51 * When glGenRender/FramebuffersEXT() is called we insert pointers to
52 * these placeholder objects into the hash table.
53 * Later, when the object ID is first bound, we replace the placeholder
54 * with the real frame/renderbuffer.
55 */
56 static struct gl_framebuffer DummyFramebuffer;
57 static struct gl_renderbuffer DummyRenderbuffer;
58
59
60 #define IS_CUBE_FACE(TARGET) \
61 ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
62 (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
63
64
65 /**
66 * Helper routine for getting a gl_renderbuffer.
67 */
68 static struct gl_renderbuffer *
69 lookup_renderbuffer(GLcontext *ctx, GLuint id)
70 {
71 struct gl_renderbuffer *rb;
72
73 if (id == 0)
74 return NULL;
75
76 rb = (struct gl_renderbuffer *)
77 _mesa_HashLookup(ctx->Shared->RenderBuffers, id);
78 return rb;
79 }
80
81
82 /**
83 * Helper routine for getting a gl_framebuffer.
84 */
85 static struct gl_framebuffer *
86 lookup_framebuffer(GLcontext *ctx, GLuint id)
87 {
88 struct gl_framebuffer *fb;
89
90 if (id == 0)
91 return NULL;
92
93 fb = (struct gl_framebuffer *)
94 _mesa_HashLookup(ctx->Shared->FrameBuffers, id);
95 return fb;
96 }
97
98
99 /**
100 * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
101 * gl_renderbuffer_attachment object.
102 */
103 static struct gl_renderbuffer_attachment *
104 get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, GLenum attachment)
105 {
106 GLuint i;
107
108 switch (attachment) {
109 case GL_COLOR_ATTACHMENT0_EXT:
110 case GL_COLOR_ATTACHMENT1_EXT:
111 case GL_COLOR_ATTACHMENT2_EXT:
112 case GL_COLOR_ATTACHMENT3_EXT:
113 case GL_COLOR_ATTACHMENT4_EXT:
114 case GL_COLOR_ATTACHMENT5_EXT:
115 case GL_COLOR_ATTACHMENT6_EXT:
116 case GL_COLOR_ATTACHMENT7_EXT:
117 case GL_COLOR_ATTACHMENT8_EXT:
118 case GL_COLOR_ATTACHMENT9_EXT:
119 case GL_COLOR_ATTACHMENT10_EXT:
120 case GL_COLOR_ATTACHMENT11_EXT:
121 case GL_COLOR_ATTACHMENT12_EXT:
122 case GL_COLOR_ATTACHMENT13_EXT:
123 case GL_COLOR_ATTACHMENT14_EXT:
124 case GL_COLOR_ATTACHMENT15_EXT:
125 i = attachment - GL_COLOR_ATTACHMENT0_EXT;
126 if (i >= ctx->Const.MaxColorAttachments) {
127 return NULL;
128 }
129 return &fb->Attachment[BUFFER_COLOR0 + i];
130 case GL_DEPTH_ATTACHMENT_EXT:
131 return &fb->Attachment[BUFFER_DEPTH];
132 case GL_STENCIL_ATTACHMENT_EXT:
133 return &fb->Attachment[BUFFER_STENCIL];
134 default:
135 return NULL;
136 }
137 }
138
139
140 /**
141 * Remove any texture or renderbuffer attached to the given attachment
142 * point. Update reference counts, etc.
143 */
144 void
145 _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
146 {
147 if (att->Type == GL_TEXTURE) {
148 ASSERT(att->Texture);
149 if (att->Renderbuffer) {
150 /* delete/remove the 'wrapper' renderbuffer */
151 /* XXX do we really want to do this??? */
152 att->Renderbuffer->Delete(att->Renderbuffer);
153 att->Renderbuffer = NULL;
154 }
155 att->Texture->RefCount--;
156 if (att->Texture->RefCount == 0) {
157 ctx->Driver.DeleteTexture(ctx, att->Texture);
158 }
159 att->Texture = NULL;
160 }
161 else if (att->Type == GL_RENDERBUFFER_EXT) {
162 ASSERT(att->Renderbuffer);
163 ASSERT(!att->Texture);
164 att->Renderbuffer->RefCount--;
165 if (att->Renderbuffer->RefCount == 0) {
166 att->Renderbuffer->Delete(att->Renderbuffer);
167 }
168 att->Renderbuffer = NULL;
169 }
170 att->Type = GL_NONE;
171 att->Complete = GL_TRUE;
172 }
173
174
175 /**
176 * Bind a texture object to an attachment point.
177 * The previous binding, if any, will be removed first.
178 */
179 void
180 _mesa_set_texture_attachment(GLcontext *ctx,
181 struct gl_renderbuffer_attachment *att,
182 struct gl_texture_object *texObj,
183 GLenum texTarget, GLuint level, GLuint zoffset)
184 {
185 _mesa_remove_attachment(ctx, att);
186 att->Type = GL_TEXTURE;
187 att->Texture = texObj;
188 att->TextureLevel = level;
189 if (IS_CUBE_FACE(texTarget)) {
190 att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
191 }
192 else {
193 att->CubeMapFace = 0;
194 }
195 att->Zoffset = zoffset;
196 att->Complete = GL_FALSE;
197
198 texObj->RefCount++;
199
200 /* XXX when we attach to a texture, we should probably set the
201 * att->Renderbuffer pointer to a "wrapper renderbuffer" which
202 * makes the texture image look like renderbuffer.
203 */
204 }
205
206
207 /**
208 * Bind a renderbuffer to an attachment point.
209 * The previous binding, if any, will be removed first.
210 */
211 void
212 _mesa_set_renderbuffer_attachment(GLcontext *ctx,
213 struct gl_renderbuffer_attachment *att,
214 struct gl_renderbuffer *rb)
215 {
216 _mesa_remove_attachment(ctx, att);
217 att->Type = GL_RENDERBUFFER_EXT;
218 att->Renderbuffer = rb;
219 att->Texture = NULL; /* just to be safe */
220 att->Complete = GL_FALSE;
221 rb->RefCount++;
222 }
223
224
225 /**
226 * Fallback for ctx->Driver.FramebufferRenderbuffer()
227 * Sets a framebuffer attachment to a particular renderbuffer.
228 * The framebuffer in question is ctx->DrawBuffer.
229 * \sa _mesa_renderbuffer_texture
230 */
231 void
232 _mesa_framebuffer_renderbuffer(GLcontext *ctx,
233 struct gl_renderbuffer_attachment *att,
234 struct gl_renderbuffer *rb)
235 {
236 if (rb) {
237 _mesa_set_renderbuffer_attachment(ctx, att, rb);
238 }
239 else {
240 _mesa_remove_attachment(ctx, att);
241 }
242 }
243
244
245 /**
246 * Test if an attachment point is complete and update its Complete field.
247 * \param format if GL_COLOR, this is a color attachment point,
248 * if GL_DEPTH, this is a depth component attachment point,
249 * if GL_STENCIL, this is a stencil component attachment point.
250 */
251 static void
252 test_attachment_completeness(const GLcontext *ctx, GLenum format,
253 struct gl_renderbuffer_attachment *att)
254 {
255 assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL);
256
257 /* assume complete */
258 att->Complete = GL_TRUE;
259
260 /* Look for reasons why the attachment might be incomplete */
261 if (att->Type == GL_TEXTURE) {
262 const struct gl_texture_object *texObj = att->Texture;
263 struct gl_texture_image *texImage;
264
265 if (!texObj) {
266 att->Complete = GL_FALSE;
267 return;
268 }
269
270 texImage = texObj->Image[att->CubeMapFace][att->TextureLevel];
271 if (!texImage) {
272 att->Complete = GL_FALSE;
273 return;
274 }
275 if (texImage->Width < 1 || texImage->Height < 1) {
276 att->Complete = GL_FALSE;
277 return;
278 }
279 if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) {
280 att->Complete = GL_FALSE;
281 return;
282 }
283
284 if (format == GL_COLOR) {
285 if (texImage->TexFormat->BaseFormat != GL_RGB &&
286 texImage->TexFormat->BaseFormat != GL_RGBA) {
287 att->Complete = GL_FALSE;
288 return;
289 }
290 }
291 else if (format == GL_DEPTH) {
292 if (texImage->TexFormat->BaseFormat != GL_DEPTH_COMPONENT) {
293 att->Complete = GL_FALSE;
294 return;
295 }
296 }
297 else {
298 /* no such thing as stencil textures */
299 att->Complete = GL_FALSE;
300 return;
301 }
302 }
303 else if (att->Type == GL_RENDERBUFFER_EXT) {
304 if (att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) {
305 att->Complete = GL_FALSE;
306 return;
307 }
308 if (format == GL_COLOR) {
309 if (att->Renderbuffer->_BaseFormat != GL_RGB &&
310 att->Renderbuffer->_BaseFormat != GL_RGBA) {
311 att->Complete = GL_FALSE;
312 return;
313 }
314 }
315 else if (format == GL_DEPTH) {
316 if (att->Renderbuffer->_BaseFormat != GL_DEPTH_COMPONENT) {
317 att->Complete = GL_FALSE;
318 return;
319 }
320 }
321 else {
322 assert(format == GL_STENCIL);
323 if (att->Renderbuffer->_BaseFormat != GL_STENCIL_INDEX) {
324 att->Complete = GL_FALSE;
325 return;
326 }
327 }
328 }
329 else {
330 ASSERT(att->Type == GL_NONE);
331 /* complete */
332 return;
333 }
334 }
335
336
337 /**
338 * Test if the given framebuffer object is complete and update its
339 * Status field with the results.
340 * Also update the framebuffer's Width and Height fields if the
341 * framebuffer is complete.
342 */
343 void
344 _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
345 {
346 GLuint numImages, width = 0, height = 0;
347 GLenum intFormat = GL_NONE;
348 GLuint w = 0, h = 0;
349 GLint i;
350
351 assert(fb->Name != 0);
352
353 numImages = 0;
354 fb->Width = 0;
355 fb->Height = 0;
356
357 /* Start at -2 to more easily loop over all attachment points */
358 for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) {
359 struct gl_renderbuffer_attachment *att;
360 GLenum f;
361
362 if (i == -2) {
363 att = &fb->Attachment[BUFFER_DEPTH];
364 test_attachment_completeness(ctx, GL_DEPTH, att);
365 if (!att->Complete) {
366 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
367 return;
368 }
369 }
370 else if (i == -1) {
371 att = &fb->Attachment[BUFFER_STENCIL];
372 test_attachment_completeness(ctx, GL_STENCIL, att);
373 if (!att->Complete) {
374 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
375 return;
376 }
377 }
378 else {
379 att = &fb->Attachment[BUFFER_COLOR0 + i];
380 test_attachment_completeness(ctx, GL_COLOR, att);
381 if (!att->Complete) {
382 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
383 return;
384 }
385 }
386
387 if (att->Type == GL_TEXTURE) {
388 w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width;
389 h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height;
390 f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format;
391 numImages++;
392 if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) {
393 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
394 return;
395 }
396 }
397 else if (att->Type == GL_RENDERBUFFER_EXT) {
398 w = att->Renderbuffer->Width;
399 h = att->Renderbuffer->Height;
400 f = att->Renderbuffer->InternalFormat;
401 numImages++;
402 }
403 else {
404 assert(att->Type == GL_NONE);
405 continue;
406 }
407
408 if (numImages == 1) {
409 /* set required width, height and format */
410 width = w;
411 height = h;
412 if (i >= 0)
413 intFormat = f;
414 }
415 else {
416 /* check that width, height, format are same */
417 if (w != width || h != height) {
418 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
419 return;
420 }
421 if (intFormat != GL_NONE && f != intFormat) {
422 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
423 return;
424 }
425 }
426 }
427
428 /* Check that all DrawBuffers are present */
429 for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
430 if (fb->ColorDrawBuffer[i] != GL_NONE) {
431 const struct gl_renderbuffer_attachment *att
432 = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
433 assert(att);
434 if (att->Type == GL_NONE) {
435 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
436 return;
437 }
438 }
439 }
440
441 /* Check that the ReadBuffer is present */
442 if (fb->ColorReadBuffer != GL_NONE) {
443 const struct gl_renderbuffer_attachment *att
444 = get_attachment(ctx, fb, fb->ColorReadBuffer);
445 assert(att);
446 if (att->Type == GL_NONE) {
447 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
448 return;
449 }
450 }
451
452 /* Check if any renderbuffer is attached more than once */
453 for (i = 0; i < BUFFER_COUNT - 1; i++) {
454 struct gl_renderbuffer *rb_i = fb->Attachment[i].Renderbuffer;
455 if (rb_i) {
456 GLint j;
457 for (j = i + 1; j < BUFFER_COUNT; j++) {
458 struct gl_renderbuffer *rb_j = fb->Attachment[j].Renderbuffer;
459 if (rb_i == rb_j) {
460 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT;
461 return;
462 }
463 }
464 }
465 }
466
467
468 if (numImages == 0) {
469 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
470 return;
471 }
472
473 /*
474 * If we get here, the framebuffer is complete!
475 */
476 fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT;
477 fb->Width = w;
478 fb->Height = h;
479 }
480
481
482 GLboolean GLAPIENTRY
483 _mesa_IsRenderbufferEXT(GLuint renderbuffer)
484 {
485 GET_CURRENT_CONTEXT(ctx);
486 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
487 if (renderbuffer && lookup_renderbuffer(ctx, renderbuffer))
488 return GL_TRUE;
489 else
490 return GL_FALSE;
491 }
492
493
494 void GLAPIENTRY
495 _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
496 {
497 struct gl_renderbuffer *newRb, *oldRb;
498 GET_CURRENT_CONTEXT(ctx);
499
500 ASSERT_OUTSIDE_BEGIN_END(ctx);
501 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
502
503 if (target != GL_RENDERBUFFER_EXT) {
504 _mesa_error(ctx, GL_INVALID_ENUM,
505 "glBindRenderbufferEXT(target)");
506 return;
507 }
508
509 if (renderbuffer) {
510 newRb = lookup_renderbuffer(ctx, renderbuffer);
511 if (newRb == &DummyRenderbuffer) {
512 /* ID was reserved, but no real renderbuffer object made yet */
513 newRb = NULL;
514 }
515 if (!newRb) {
516 /* create new renderbuffer object */
517 newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
518 if (!newRb) {
519 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
520 return;
521 }
522 ASSERT(newRb->AllocStorage);
523 _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
524 }
525 newRb->RefCount++;
526 }
527 else {
528 newRb = NULL;
529 }
530
531 oldRb = ctx->CurrentRenderbuffer;
532 if (oldRb) {
533 oldRb->RefCount--;
534 if (oldRb->RefCount == 0) {
535 oldRb->Delete(oldRb);
536 }
537 }
538
539 ASSERT(newRb != &DummyRenderbuffer);
540
541 ctx->CurrentRenderbuffer = newRb;
542 }
543
544
545 void GLAPIENTRY
546 _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
547 {
548 GLint i;
549 GET_CURRENT_CONTEXT(ctx);
550
551 ASSERT_OUTSIDE_BEGIN_END(ctx);
552
553 for (i = 0; i < n; i++) {
554 if (renderbuffers[i] > 0) {
555 struct gl_renderbuffer *rb;
556 rb = lookup_renderbuffer(ctx, renderbuffers[i]);
557 if (rb) {
558 /* remove from hash table immediately, to free the ID */
559 _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
560
561 if (rb != &DummyRenderbuffer) {
562 /* But the object will not be freed until it's no longer
563 * bound in any context.
564 */
565 rb->RefCount--;
566 if (rb->RefCount == 0) {
567 rb->Delete(rb);
568 }
569 }
570 }
571 }
572 }
573 }
574
575
576 void GLAPIENTRY
577 _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
578 {
579 GET_CURRENT_CONTEXT(ctx);
580 GLuint first;
581 GLint i;
582
583 ASSERT_OUTSIDE_BEGIN_END(ctx);
584
585 if (n < 0) {
586 _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
587 return;
588 }
589
590 if (!renderbuffers)
591 return;
592
593 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
594
595 for (i = 0; i < n; i++) {
596 GLuint name = first + i;
597 renderbuffers[i] = name;
598 /* insert dummy placeholder into hash table */
599 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
600 _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
601 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
602 }
603 }
604
605
606 /**
607 * Given an internal format token for a render buffer, return the
608 * corresponding base format.
609 * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
610 * or zero if error.
611 */
612 static GLenum
613 base_internal_format(GLcontext *ctx, GLenum internalFormat)
614 {
615 switch (internalFormat) {
616 case GL_RGB:
617 case GL_R3_G3_B2:
618 case GL_RGB4:
619 case GL_RGB5:
620 case GL_RGB8:
621 case GL_RGB10:
622 case GL_RGB12:
623 case GL_RGB16:
624 return GL_RGB;
625 case GL_RGBA:
626 case GL_RGBA2:
627 case GL_RGBA4:
628 case GL_RGB5_A1:
629 case GL_RGBA8:
630 case GL_RGB10_A2:
631 case GL_RGBA12:
632 case GL_RGBA16:
633 return GL_RGBA;
634 case GL_STENCIL_INDEX:
635 case GL_STENCIL_INDEX1_EXT:
636 case GL_STENCIL_INDEX4_EXT:
637 case GL_STENCIL_INDEX8_EXT:
638 case GL_STENCIL_INDEX16_EXT:
639 return GL_STENCIL_INDEX;
640 case GL_DEPTH_COMPONENT:
641 case GL_DEPTH_COMPONENT16:
642 case GL_DEPTH_COMPONENT24:
643 case GL_DEPTH_COMPONENT32:
644 return GL_DEPTH_COMPONENT;
645 /* XXX add floating point formats eventually */
646 default:
647 return 0;
648 }
649 }
650
651
652 void GLAPIENTRY
653 _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
654 GLsizei width, GLsizei height)
655 {
656 struct gl_renderbuffer *rb;
657 GLenum baseFormat;
658 GET_CURRENT_CONTEXT(ctx);
659
660 ASSERT_OUTSIDE_BEGIN_END(ctx);
661 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
662
663 if (target != GL_RENDERBUFFER_EXT) {
664 _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
665 return;
666 }
667
668 baseFormat = base_internal_format(ctx, internalFormat);
669 if (baseFormat == 0) {
670 _mesa_error(ctx, GL_INVALID_ENUM,
671 "glRenderbufferStorageEXT(internalFormat)");
672 return;
673 }
674
675 if (width < 1 || width > ctx->Const.MaxRenderbufferSize) {
676 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
677 return;
678 }
679
680 if (height < 1 || height > ctx->Const.MaxRenderbufferSize) {
681 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
682 return;
683 }
684
685 rb = ctx->CurrentRenderbuffer;
686
687 if (!rb) {
688 _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
689 return;
690 }
691
692 /* Now allocate the storage */
693 ASSERT(rb->AllocStorage);
694 if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
695 /* No error - check/set fields now */
696 assert(rb->Width == width);
697 assert(rb->Height == height);
698 assert(rb->InternalFormat);
699 rb->_BaseFormat = baseFormat;
700 }
701 else {
702 /* Probably ran out of memory - clear the fields */
703 rb->Width = 0;
704 rb->Height = 0;
705 rb->InternalFormat = GL_NONE;
706 rb->_BaseFormat = GL_NONE;
707 }
708
709 /*
710 test_framebuffer_completeness(ctx, fb);
711 */
712 /* XXX if this renderbuffer is attached anywhere, invalidate attachment
713 * points???
714 */
715 }
716
717
718 void GLAPIENTRY
719 _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
720 {
721 GET_CURRENT_CONTEXT(ctx);
722
723 ASSERT_OUTSIDE_BEGIN_END(ctx);
724
725 if (target != GL_RENDERBUFFER_EXT) {
726 _mesa_error(ctx, GL_INVALID_ENUM,
727 "glGetRenderbufferParameterivEXT(target)");
728 return;
729 }
730
731 if (!ctx->CurrentRenderbuffer) {
732 _mesa_error(ctx, GL_INVALID_OPERATION,
733 "glGetRenderbufferParameterivEXT");
734 return;
735 }
736
737 switch (pname) {
738 case GL_RENDERBUFFER_WIDTH_EXT:
739 *params = ctx->CurrentRenderbuffer->Width;
740 return;
741 case GL_RENDERBUFFER_HEIGHT_EXT:
742 *params = ctx->CurrentRenderbuffer->Height;
743 return;
744 case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
745 *params = ctx->CurrentRenderbuffer->InternalFormat;
746 return;
747 case GL_RENDERBUFFER_RED_SIZE_EXT:
748 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
749 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
750 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
751 }
752 else {
753 *params = 0;
754 }
755 break;
756 case GL_RENDERBUFFER_GREEN_SIZE_EXT:
757 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
758 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
759 *params = ctx->CurrentRenderbuffer->ComponentSizes[1];
760 }
761 else {
762 *params = 0;
763 }
764 break;
765 case GL_RENDERBUFFER_BLUE_SIZE_EXT:
766 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
767 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
768 *params = ctx->CurrentRenderbuffer->ComponentSizes[2];
769 }
770 else {
771 *params = 0;
772 }
773 break;
774 case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
775 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
776 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
777 *params = ctx->CurrentRenderbuffer->ComponentSizes[3];
778 }
779 else {
780 *params = 0;
781 }
782 break;
783 case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
784 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
785 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
786 }
787 else {
788 *params = 0;
789 }
790 break;
791 case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
792 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
793 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
794 }
795 else {
796 *params = 0;
797 }
798 break;
799
800 default:
801 _mesa_error(ctx, GL_INVALID_ENUM,
802 "glGetRenderbufferParameterivEXT(target)");
803 return;
804 }
805 }
806
807
808 GLboolean GLAPIENTRY
809 _mesa_IsFramebufferEXT(GLuint framebuffer)
810 {
811 GET_CURRENT_CONTEXT(ctx);
812 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
813 if (framebuffer && lookup_framebuffer(ctx, framebuffer))
814 return GL_TRUE;
815 else
816 return GL_FALSE;
817 }
818
819
820 void GLAPIENTRY
821 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
822 {
823 struct gl_framebuffer *newFb, *newReadFb, *oldFb;
824 GET_CURRENT_CONTEXT(ctx);
825
826 ASSERT_OUTSIDE_BEGIN_END(ctx);
827 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
828
829 if (target != GL_FRAMEBUFFER_EXT) {
830 _mesa_error(ctx, GL_INVALID_ENUM,
831 "glBindFramebufferEXT(target)");
832 return;
833 }
834
835 if (framebuffer) {
836 /* Binding a user-created framebuffer object */
837 newFb = lookup_framebuffer(ctx, framebuffer);
838 if (newFb == &DummyFramebuffer) {
839 /* ID was reserved, but no real framebuffer object made yet */
840 newFb = NULL;
841 }
842 if (!newFb) {
843 /* create new framebuffer object */
844 newFb = ctx->Driver.NewFramebuffer(ctx, framebuffer);
845 if (!newFb) {
846 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
847 return;
848 }
849 _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
850 }
851 newFb->RefCount++;
852 newReadFb = newFb;
853 }
854 else {
855 /* Binding the window system framebuffer (which was originally set
856 * with MakeCurrent).
857 */
858 newFb = ctx->WinSysDrawBuffer;
859 newReadFb = ctx->WinSysReadBuffer;
860 }
861
862 oldFb = ctx->DrawBuffer;
863 if (oldFb) { /* AND oldFb->Name != 0 */
864 oldFb->RefCount--;
865 if (oldFb->RefCount == 0) {
866 oldFb->Delete(oldFb);
867 }
868 }
869
870 ASSERT(newFb != &DummyFramebuffer);
871
872 /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
873 ctx->DrawBuffer = newFb;
874 ctx->ReadBuffer = newReadFb;
875 }
876
877
878 void GLAPIENTRY
879 _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
880 {
881 GLint i;
882 GET_CURRENT_CONTEXT(ctx);
883
884 ASSERT_OUTSIDE_BEGIN_END(ctx);
885
886 for (i = 0; i < n; i++) {
887 if (framebuffers[i] > 0) {
888 struct gl_framebuffer *fb;
889 fb = lookup_framebuffer(ctx, framebuffers[i]);
890 if (fb) {
891 ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
892 /* remove from hash table immediately, to free the ID */
893 _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
894
895 if (fb != &DummyFramebuffer) {
896 /* But the object will not be freed until it's no longer
897 * bound in any context.
898 */
899 fb->RefCount--;
900 if (fb->RefCount == 0) {
901 fb->Delete(fb);
902 }
903 }
904 }
905 }
906 }
907 }
908
909
910 void GLAPIENTRY
911 _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
912 {
913 GET_CURRENT_CONTEXT(ctx);
914 GLuint first;
915 GLint i;
916
917 ASSERT_OUTSIDE_BEGIN_END(ctx);
918
919 if (n < 0) {
920 _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
921 return;
922 }
923
924 if (!framebuffers)
925 return;
926
927 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
928
929 for (i = 0; i < n; i++) {
930 GLuint name = first + i;
931 framebuffers[i] = name;
932 /* insert dummy placeholder into hash table */
933 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
934 _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
935 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
936 }
937 }
938
939
940
941 GLenum GLAPIENTRY
942 _mesa_CheckFramebufferStatusEXT(GLenum target)
943 {
944 GET_CURRENT_CONTEXT(ctx);
945
946 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
947
948 if (target != GL_FRAMEBUFFER_EXT) {
949 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
950 return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
951 }
952
953 if (ctx->DrawBuffer->Name == 0) {
954 /* The window system / default framebuffer is always complete */
955 return GL_FRAMEBUFFER_COMPLETE_EXT;
956 }
957
958 _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
959 return ctx->DrawBuffer->_Status;
960 }
961
962
963
964 /**
965 * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
966 * \return GL_TRUE if any error, GL_FALSE otherwise
967 */
968 static GLboolean
969 error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
970 GLenum target, GLenum attachment,
971 GLenum textarget, GLuint texture, GLint level)
972 {
973 ASSERT(dims >= 1 && dims <= 3);
974
975 if (target != GL_FRAMEBUFFER_EXT) {
976 _mesa_error(ctx, GL_INVALID_ENUM,
977 "glFramebufferTexture%dDEXT(target)", dims);
978 return GL_TRUE;
979 }
980
981 /* check framebuffer binding */
982 if (ctx->DrawBuffer->Name == 0) {
983 _mesa_error(ctx, GL_INVALID_OPERATION,
984 "glFramebufferTexture%dDEXT", dims);
985 return GL_TRUE;
986 }
987
988 /* only check textarget, level if texture ID is non-zero */
989 if (texture) {
990 if ((dims == 1 && textarget != GL_TEXTURE_1D) ||
991 (dims == 3 && textarget != GL_TEXTURE_3D) ||
992 (dims == 2 && textarget != GL_TEXTURE_2D &&
993 textarget != GL_TEXTURE_RECTANGLE_ARB &&
994 !IS_CUBE_FACE(textarget))) {
995 _mesa_error(ctx, GL_INVALID_VALUE,
996 "glFramebufferTexture%dDEXT(textarget)", dims);
997 return GL_TRUE;
998 }
999
1000 if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
1001 _mesa_error(ctx, GL_INVALID_VALUE,
1002 "glFramebufferTexture%dDEXT(level)", dims);
1003 return GL_TRUE;
1004 }
1005 }
1006
1007 return GL_FALSE;
1008 }
1009
1010
1011 void GLAPIENTRY
1012 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1013 GLenum textarget, GLuint texture, GLint level)
1014 {
1015 struct gl_renderbuffer_attachment *att;
1016 struct gl_texture_object *texObj;
1017 GET_CURRENT_CONTEXT(ctx);
1018
1019 ASSERT_OUTSIDE_BEGIN_END(ctx);
1020 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1021
1022 if (error_check_framebuffer_texture(ctx, 1, target, attachment,
1023 textarget, texture, level))
1024 return;
1025
1026 ASSERT(textarget == GL_TEXTURE_1D);
1027
1028 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1029 if (att == NULL) {
1030 _mesa_error(ctx, GL_INVALID_ENUM,
1031 "glFramebufferTexture1DEXT(attachment)");
1032 return;
1033 }
1034
1035 if (texture) {
1036 texObj = (struct gl_texture_object *)
1037 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1038 if (!texObj) {
1039 _mesa_error(ctx, GL_INVALID_VALUE,
1040 "glFramebufferTexture1DEXT(texture)");
1041 return;
1042 }
1043 if (texObj->Target != textarget) {
1044 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1045 "glFramebufferTexture1DEXT(texture target)");
1046 return;
1047 }
1048 }
1049 else {
1050 /* remove texture attachment */
1051 texObj = NULL;
1052 }
1053 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1054 }
1055
1056
1057 void GLAPIENTRY
1058 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1059 GLenum textarget, GLuint texture, GLint level)
1060 {
1061 struct gl_renderbuffer_attachment *att;
1062 struct gl_texture_object *texObj;
1063 GET_CURRENT_CONTEXT(ctx);
1064
1065 ASSERT_OUTSIDE_BEGIN_END(ctx);
1066 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1067
1068 if (error_check_framebuffer_texture(ctx, 2, target, attachment,
1069 textarget, texture, level))
1070 return;
1071
1072 ASSERT(textarget == GL_TEXTURE_2D ||
1073 textarget == GL_TEXTURE_RECTANGLE_ARB ||
1074 IS_CUBE_FACE(textarget));
1075
1076 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1077 if (att == NULL) {
1078 _mesa_error(ctx, GL_INVALID_ENUM,
1079 "glFramebufferTexture2DEXT(attachment)");
1080 return;
1081 }
1082
1083 if (texture) {
1084 texObj = (struct gl_texture_object *)
1085 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1086 if (!texObj) {
1087 _mesa_error(ctx, GL_INVALID_VALUE,
1088 "glFramebufferTexture2DEXT(texture)");
1089 return;
1090 }
1091 if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
1092 (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
1093 && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
1094 (texObj->Target == GL_TEXTURE_CUBE_MAP
1095 && !IS_CUBE_FACE(textarget))) {
1096 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1097 "glFramebufferTexture2DEXT(texture target)");
1098 return;
1099 }
1100 }
1101 else {
1102 /* remove texture attachment */
1103 texObj = NULL;
1104 }
1105 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1106 }
1107
1108
1109 void GLAPIENTRY
1110 _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1111 GLenum textarget, GLuint texture,
1112 GLint level, GLint zoffset)
1113 {
1114 struct gl_renderbuffer_attachment *att;
1115 struct gl_texture_object *texObj;
1116 GET_CURRENT_CONTEXT(ctx);
1117
1118 ASSERT_OUTSIDE_BEGIN_END(ctx);
1119 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1120
1121 if (error_check_framebuffer_texture(ctx, 3, target, attachment,
1122 textarget, texture, level))
1123 return;
1124
1125 ASSERT(textarget == GL_TEXTURE_3D);
1126
1127 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1128 if (att == NULL) {
1129 _mesa_error(ctx, GL_INVALID_ENUM,
1130 "glFramebufferTexture1DEXT(attachment)");
1131 return;
1132 }
1133
1134 if (texture) {
1135 const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1136 texObj = (struct gl_texture_object *)
1137 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1138 if (!texObj) {
1139 _mesa_error(ctx, GL_INVALID_VALUE,
1140 "glFramebufferTexture3DEXT(texture)");
1141 return;
1142 }
1143 if (texObj->Target != textarget) {
1144 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1145 "glFramebufferTexture3DEXT(texture target)");
1146 return;
1147 }
1148 if (zoffset < 0 || zoffset >= maxSize) {
1149 _mesa_error(ctx, GL_INVALID_VALUE,
1150 "glFramebufferTexture3DEXT(zoffset)");
1151 return;
1152 }
1153 }
1154 else {
1155 /* remove texture attachment */
1156 texObj = NULL;
1157 }
1158 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
1159 level, zoffset);
1160 }
1161
1162
1163 void GLAPIENTRY
1164 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1165 GLenum renderbufferTarget,
1166 GLuint renderbuffer)
1167 {
1168 struct gl_renderbuffer_attachment *att;
1169 struct gl_renderbuffer *rb;
1170 GET_CURRENT_CONTEXT(ctx);
1171
1172 ASSERT_OUTSIDE_BEGIN_END(ctx);
1173 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1174
1175 if (target != GL_FRAMEBUFFER_EXT) {
1176 _mesa_error(ctx, GL_INVALID_ENUM,
1177 "glFramebufferRenderbufferEXT(target)");
1178 return;
1179 }
1180
1181 if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1182 _mesa_error(ctx, GL_INVALID_ENUM,
1183 "glFramebufferRenderbufferEXT(renderbufferTarget)");
1184 return;
1185 }
1186
1187 if (ctx->DrawBuffer->Name == 0) {
1188 _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1189 return;
1190 }
1191
1192 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1193 if (att == NULL) {
1194 _mesa_error(ctx, GL_INVALID_ENUM,
1195 "glFramebufferRenderbufferEXT(attachment)");
1196 return;
1197 }
1198
1199 if (renderbuffer) {
1200 rb = lookup_renderbuffer(ctx, renderbuffer);
1201 if (!rb) {
1202 _mesa_error(ctx, GL_INVALID_OPERATION,
1203 "glFramebufferRenderbufferEXT(renderbuffer)");
1204 return;
1205 }
1206 }
1207 else {
1208 /* remove renderbuffer attachment */
1209 rb = NULL;
1210 }
1211
1212 assert(ctx->Driver.FramebufferRenderbuffer);
1213 ctx->Driver.FramebufferRenderbuffer(ctx, att, rb);
1214
1215 _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1216 }
1217
1218
1219 void GLAPIENTRY
1220 _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1221 GLenum pname, GLint *params)
1222 {
1223 const struct gl_renderbuffer_attachment *att;
1224 GET_CURRENT_CONTEXT(ctx);
1225
1226 ASSERT_OUTSIDE_BEGIN_END(ctx);
1227
1228 if (target != GL_FRAMEBUFFER_EXT) {
1229 _mesa_error(ctx, GL_INVALID_ENUM,
1230 "glGetFramebufferAttachmentParameterivEXT(target)");
1231 return;
1232 }
1233
1234 if (ctx->DrawBuffer->Name == 0) {
1235 _mesa_error(ctx, GL_INVALID_OPERATION,
1236 "glGetFramebufferAttachmentParameterivEXT");
1237 return;
1238 }
1239
1240 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1241 if (att == NULL) {
1242 _mesa_error(ctx, GL_INVALID_ENUM,
1243 "glGetFramebufferAttachmentParameterivEXT(attachment)");
1244 return;
1245 }
1246
1247 switch (pname) {
1248 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1249 *params = att->Type;
1250 return;
1251 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1252 if (att->Type == GL_RENDERBUFFER_EXT) {
1253 *params = att->Renderbuffer->Name;
1254 }
1255 else if (att->Type == GL_TEXTURE) {
1256 *params = att->Texture->Name;
1257 }
1258 else {
1259 _mesa_error(ctx, GL_INVALID_ENUM,
1260 "glGetFramebufferAttachmentParameterivEXT(pname)");
1261 }
1262 return;
1263 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1264 if (att->Type == GL_TEXTURE) {
1265 *params = att->TextureLevel;
1266 }
1267 else {
1268 _mesa_error(ctx, GL_INVALID_ENUM,
1269 "glGetFramebufferAttachmentParameterivEXT(pname)");
1270 }
1271 return;
1272 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1273 if (att->Type == GL_TEXTURE) {
1274 *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1275 }
1276 else {
1277 _mesa_error(ctx, GL_INVALID_ENUM,
1278 "glGetFramebufferAttachmentParameterivEXT(pname)");
1279 }
1280 return;
1281 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1282 if (att->Type == GL_TEXTURE) {
1283 *params = att->Zoffset;
1284 }
1285 else {
1286 _mesa_error(ctx, GL_INVALID_ENUM,
1287 "glGetFramebufferAttachmentParameterivEXT(pname)");
1288 }
1289 return;
1290 default:
1291 _mesa_error(ctx, GL_INVALID_ENUM,
1292 "glGetFramebufferAttachmentParameterivEXT(pname)");
1293 return;
1294 }
1295 }
1296
1297
1298 void GLAPIENTRY
1299 _mesa_GenerateMipmapEXT(GLenum target)
1300 {
1301 struct gl_texture_unit *texUnit;
1302 struct gl_texture_object *texObj;
1303 GET_CURRENT_CONTEXT(ctx);
1304
1305 ASSERT_OUTSIDE_BEGIN_END(ctx);
1306
1307 switch (target) {
1308 case GL_TEXTURE_1D:
1309 case GL_TEXTURE_2D:
1310 case GL_TEXTURE_3D:
1311 case GL_TEXTURE_CUBE_MAP:
1312 /* OK, legal value */
1313 break;
1314 default:
1315 _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1316 return;
1317 }
1318
1319 texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1320 texObj = _mesa_select_tex_object(ctx, texUnit, target);
1321
1322 /* XXX this might not handle cube maps correctly */
1323 _mesa_generate_mipmap(ctx, target, texUnit, texObj);
1324 }