New files - split off runtime assembly functions from
[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 }
393 else if (att->Type == GL_RENDERBUFFER_EXT) {
394 w = att->Renderbuffer->Width;
395 h = att->Renderbuffer->Height;
396 f = att->Renderbuffer->InternalFormat;
397 numImages++;
398 }
399 else {
400 assert(att->Type == GL_NONE);
401 continue;
402 }
403
404 if (numImages == 1) {
405 /* set required width, height and format */
406 width = w;
407 height = h;
408 if (i >= 0)
409 intFormat = f;
410 }
411 else {
412 /* check that width, height, format are same */
413 if (w != width || h != height) {
414 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
415 return;
416 }
417 if (intFormat != GL_NONE && f != intFormat) {
418 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
419 return;
420 }
421 }
422 }
423
424 /* Check that all DrawBuffers are present */
425 for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
426 if (fb->ColorDrawBuffer[i] != GL_NONE) {
427 const struct gl_renderbuffer_attachment *att
428 = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
429 assert(att);
430 if (att->Type == GL_NONE) {
431 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
432 return;
433 }
434 }
435 }
436
437 /* Check that the ReadBuffer is present */
438 if (fb->ColorReadBuffer != GL_NONE) {
439 const struct gl_renderbuffer_attachment *att
440 = get_attachment(ctx, fb, fb->ColorReadBuffer);
441 assert(att);
442 if (att->Type == GL_NONE) {
443 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
444 return;
445 }
446 }
447
448 /* Check if any renderbuffer is attached more than once */
449 for (i = 0; i < BUFFER_COUNT - 1; i++) {
450 struct gl_renderbuffer *rb_i = fb->Attachment[i].Renderbuffer;
451 if (rb_i) {
452 GLint j;
453 for (j = i + 1; j < BUFFER_COUNT; j++) {
454 struct gl_renderbuffer *rb_j = fb->Attachment[j].Renderbuffer;
455 if (rb_i == rb_j) {
456 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT;
457 return;
458 }
459 }
460 }
461 }
462
463
464 if (numImages == 0) {
465 fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
466 return;
467 }
468
469 /*
470 * If we get here, the framebuffer is complete!
471 */
472 fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT;
473 fb->Width = w;
474 fb->Height = h;
475 }
476
477
478 GLboolean GLAPIENTRY
479 _mesa_IsRenderbufferEXT(GLuint renderbuffer)
480 {
481 GET_CURRENT_CONTEXT(ctx);
482 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
483 if (renderbuffer && lookup_renderbuffer(ctx, renderbuffer))
484 return GL_TRUE;
485 else
486 return GL_FALSE;
487 }
488
489
490 void GLAPIENTRY
491 _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
492 {
493 struct gl_renderbuffer *newRb, *oldRb;
494 GET_CURRENT_CONTEXT(ctx);
495
496 ASSERT_OUTSIDE_BEGIN_END(ctx);
497 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
498
499 if (target != GL_RENDERBUFFER_EXT) {
500 _mesa_error(ctx, GL_INVALID_ENUM,
501 "glBindRenderbufferEXT(target)");
502 return;
503 }
504
505 if (renderbuffer) {
506 newRb = lookup_renderbuffer(ctx, renderbuffer);
507 if (newRb == &DummyRenderbuffer) {
508 /* ID was reserved, but no real renderbuffer object made yet */
509 newRb = NULL;
510 }
511 if (!newRb) {
512 /* create new renderbuffer object */
513 newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer);
514 if (!newRb) {
515 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
516 return;
517 }
518 ASSERT(newRb->AllocStorage);
519 _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
520 }
521 newRb->RefCount++;
522 }
523 else {
524 newRb = NULL;
525 }
526
527 oldRb = ctx->CurrentRenderbuffer;
528 if (oldRb) {
529 oldRb->RefCount--;
530 if (oldRb->RefCount == 0) {
531 oldRb->Delete(oldRb);
532 }
533 }
534
535 ASSERT(newRb != &DummyRenderbuffer);
536
537 ctx->CurrentRenderbuffer = newRb;
538 }
539
540
541 void GLAPIENTRY
542 _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
543 {
544 GLint i;
545 GET_CURRENT_CONTEXT(ctx);
546
547 ASSERT_OUTSIDE_BEGIN_END(ctx);
548
549 for (i = 0; i < n; i++) {
550 if (renderbuffers[i] > 0) {
551 struct gl_renderbuffer *rb;
552 rb = lookup_renderbuffer(ctx, renderbuffers[i]);
553 if (rb) {
554 /* remove from hash table immediately, to free the ID */
555 _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
556
557 if (rb != &DummyRenderbuffer) {
558 /* But the object will not be freed until it's no longer
559 * bound in any context.
560 */
561 rb->RefCount--;
562 if (rb->RefCount == 0) {
563 rb->Delete(rb);
564 }
565 }
566 }
567 }
568 }
569 }
570
571
572 void GLAPIENTRY
573 _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
574 {
575 GET_CURRENT_CONTEXT(ctx);
576 GLuint first;
577 GLint i;
578
579 ASSERT_OUTSIDE_BEGIN_END(ctx);
580
581 if (n < 0) {
582 _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
583 return;
584 }
585
586 if (!renderbuffers)
587 return;
588
589 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
590
591 for (i = 0; i < n; i++) {
592 GLuint name = first + i;
593 renderbuffers[i] = name;
594 /* insert dummy placeholder into hash table */
595 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
596 _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
597 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
598 }
599 }
600
601
602 /**
603 * Given an internal format token for a render buffer, return the
604 * corresponding base format.
605 * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
606 * or zero if error.
607 */
608 static GLenum
609 base_internal_format(GLcontext *ctx, GLenum internalFormat)
610 {
611 switch (internalFormat) {
612 case GL_RGB:
613 case GL_R3_G3_B2:
614 case GL_RGB4:
615 case GL_RGB5:
616 case GL_RGB8:
617 case GL_RGB10:
618 case GL_RGB12:
619 case GL_RGB16:
620 return GL_RGB;
621 case GL_RGBA:
622 case GL_RGBA2:
623 case GL_RGBA4:
624 case GL_RGB5_A1:
625 case GL_RGBA8:
626 case GL_RGB10_A2:
627 case GL_RGBA12:
628 case GL_RGBA16:
629 return GL_RGBA;
630 case GL_STENCIL_INDEX:
631 case GL_STENCIL_INDEX1_EXT:
632 case GL_STENCIL_INDEX4_EXT:
633 case GL_STENCIL_INDEX8_EXT:
634 case GL_STENCIL_INDEX16_EXT:
635 return GL_STENCIL_INDEX;
636 case GL_DEPTH_COMPONENT:
637 case GL_DEPTH_COMPONENT16:
638 case GL_DEPTH_COMPONENT24:
639 case GL_DEPTH_COMPONENT32:
640 return GL_DEPTH_COMPONENT;
641 /* XXX add floating point formats eventually */
642 default:
643 return 0;
644 }
645 }
646
647
648 void GLAPIENTRY
649 _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
650 GLsizei width, GLsizei height)
651 {
652 struct gl_renderbuffer *rb;
653 GLenum baseFormat;
654 GET_CURRENT_CONTEXT(ctx);
655
656 ASSERT_OUTSIDE_BEGIN_END(ctx);
657 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
658
659 if (target != GL_RENDERBUFFER_EXT) {
660 _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
661 return;
662 }
663
664 baseFormat = base_internal_format(ctx, internalFormat);
665 if (baseFormat == 0) {
666 _mesa_error(ctx, GL_INVALID_ENUM,
667 "glRenderbufferStorageEXT(internalFormat)");
668 return;
669 }
670
671 if (width < 1 || width > ctx->Const.MaxRenderbufferSize) {
672 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
673 return;
674 }
675
676 if (height < 1 || height > ctx->Const.MaxRenderbufferSize) {
677 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
678 return;
679 }
680
681 rb = ctx->CurrentRenderbuffer;
682
683 if (!rb) {
684 _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
685 return;
686 }
687
688 /* Now allocate the storage */
689 ASSERT(rb->AllocStorage);
690 if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
691 /* No error - check/set fields now */
692 assert(rb->Width == width);
693 assert(rb->Height == height);
694 assert(rb->InternalFormat);
695 rb->_BaseFormat = baseFormat;
696 }
697 else {
698 /* Probably ran out of memory - clear the fields */
699 rb->Width = 0;
700 rb->Height = 0;
701 rb->InternalFormat = GL_NONE;
702 rb->_BaseFormat = GL_NONE;
703 }
704
705 /*
706 test_framebuffer_completeness(ctx, fb);
707 */
708 /* XXX if this renderbuffer is attached anywhere, invalidate attachment
709 * points???
710 */
711 }
712
713
714 void GLAPIENTRY
715 _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
716 {
717 GET_CURRENT_CONTEXT(ctx);
718
719 ASSERT_OUTSIDE_BEGIN_END(ctx);
720
721 if (target != GL_RENDERBUFFER_EXT) {
722 _mesa_error(ctx, GL_INVALID_ENUM,
723 "glGetRenderbufferParameterivEXT(target)");
724 return;
725 }
726
727 if (!ctx->CurrentRenderbuffer) {
728 _mesa_error(ctx, GL_INVALID_OPERATION,
729 "glGetRenderbufferParameterivEXT");
730 return;
731 }
732
733 switch (pname) {
734 case GL_RENDERBUFFER_WIDTH_EXT:
735 *params = ctx->CurrentRenderbuffer->Width;
736 return;
737 case GL_RENDERBUFFER_HEIGHT_EXT:
738 *params = ctx->CurrentRenderbuffer->Height;
739 return;
740 case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
741 *params = ctx->CurrentRenderbuffer->InternalFormat;
742 return;
743 case GL_RENDERBUFFER_RED_SIZE_EXT:
744 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
745 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
746 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
747 }
748 else {
749 *params = 0;
750 }
751 break;
752 case GL_RENDERBUFFER_GREEN_SIZE_EXT:
753 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
754 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
755 *params = ctx->CurrentRenderbuffer->ComponentSizes[1];
756 }
757 else {
758 *params = 0;
759 }
760 break;
761 case GL_RENDERBUFFER_BLUE_SIZE_EXT:
762 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
763 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
764 *params = ctx->CurrentRenderbuffer->ComponentSizes[2];
765 }
766 else {
767 *params = 0;
768 }
769 break;
770 case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
771 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
772 ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
773 *params = ctx->CurrentRenderbuffer->ComponentSizes[3];
774 }
775 else {
776 *params = 0;
777 }
778 break;
779 case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
780 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
781 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
782 }
783 else {
784 *params = 0;
785 }
786 break;
787 case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
788 if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
789 *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
790 }
791 else {
792 *params = 0;
793 }
794 break;
795
796 default:
797 _mesa_error(ctx, GL_INVALID_ENUM,
798 "glGetRenderbufferParameterivEXT(target)");
799 return;
800 }
801 }
802
803
804 GLboolean GLAPIENTRY
805 _mesa_IsFramebufferEXT(GLuint framebuffer)
806 {
807 GET_CURRENT_CONTEXT(ctx);
808 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
809 if (framebuffer && lookup_framebuffer(ctx, framebuffer))
810 return GL_TRUE;
811 else
812 return GL_FALSE;
813 }
814
815
816 void GLAPIENTRY
817 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
818 {
819 struct gl_framebuffer *newFb, *newReadFb, *oldFb;
820 GET_CURRENT_CONTEXT(ctx);
821
822 ASSERT_OUTSIDE_BEGIN_END(ctx);
823 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
824
825 if (target != GL_FRAMEBUFFER_EXT) {
826 _mesa_error(ctx, GL_INVALID_ENUM,
827 "glBindFramebufferEXT(target)");
828 return;
829 }
830
831 if (framebuffer) {
832 /* Binding a user-created framebuffer object */
833 newFb = lookup_framebuffer(ctx, framebuffer);
834 if (newFb == &DummyFramebuffer) {
835 /* ID was reserved, but no real framebuffer object made yet */
836 newFb = NULL;
837 }
838 if (!newFb) {
839 /* create new framebuffer object */
840 newFb = ctx->Driver.NewFramebuffer(ctx, framebuffer);
841 if (!newFb) {
842 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
843 return;
844 }
845 _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
846 }
847 newFb->RefCount++;
848 newReadFb = newFb;
849 }
850 else {
851 /* Binding the window system framebuffer (which was originally set
852 * with MakeCurrent).
853 */
854 newFb = ctx->WinSysDrawBuffer;
855 newReadFb = ctx->WinSysReadBuffer;
856 }
857
858 oldFb = ctx->DrawBuffer;
859 if (oldFb) { /* AND oldFb->Name != 0 */
860 oldFb->RefCount--;
861 if (oldFb->RefCount == 0) {
862 oldFb->Delete(oldFb);
863 }
864 }
865
866 ASSERT(newFb != &DummyFramebuffer);
867
868 /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
869 ctx->DrawBuffer = newFb;
870 ctx->ReadBuffer = newReadFb;
871 }
872
873
874 void GLAPIENTRY
875 _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
876 {
877 GLint i;
878 GET_CURRENT_CONTEXT(ctx);
879
880 ASSERT_OUTSIDE_BEGIN_END(ctx);
881
882 for (i = 0; i < n; i++) {
883 if (framebuffers[i] > 0) {
884 struct gl_framebuffer *fb;
885 fb = lookup_framebuffer(ctx, framebuffers[i]);
886 if (fb) {
887 ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
888 /* remove from hash table immediately, to free the ID */
889 _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
890
891 if (fb != &DummyFramebuffer) {
892 /* But the object will not be freed until it's no longer
893 * bound in any context.
894 */
895 fb->RefCount--;
896 if (fb->RefCount == 0) {
897 fb->Delete(fb);
898 }
899 }
900 }
901 }
902 }
903 }
904
905
906 void GLAPIENTRY
907 _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
908 {
909 GET_CURRENT_CONTEXT(ctx);
910 GLuint first;
911 GLint i;
912
913 ASSERT_OUTSIDE_BEGIN_END(ctx);
914
915 if (n < 0) {
916 _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
917 return;
918 }
919
920 if (!framebuffers)
921 return;
922
923 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
924
925 for (i = 0; i < n; i++) {
926 GLuint name = first + i;
927 framebuffers[i] = name;
928 /* insert dummy placeholder into hash table */
929 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
930 _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
931 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
932 }
933 }
934
935
936
937 GLenum GLAPIENTRY
938 _mesa_CheckFramebufferStatusEXT(GLenum target)
939 {
940 GET_CURRENT_CONTEXT(ctx);
941
942 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
943
944 if (target != GL_FRAMEBUFFER_EXT) {
945 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
946 return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
947 }
948
949 if (ctx->DrawBuffer->Name == 0) {
950 /* The window system / default framebuffer is always complete */
951 return GL_FRAMEBUFFER_COMPLETE_EXT;
952 }
953
954 _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
955 return ctx->DrawBuffer->_Status;
956 }
957
958
959
960 /**
961 * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
962 * \return GL_TRUE if any error, GL_FALSE otherwise
963 */
964 static GLboolean
965 error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
966 GLenum target, GLenum attachment,
967 GLenum textarget, GLuint texture, GLint level)
968 {
969 ASSERT(dims >= 1 && dims <= 3);
970
971 if (target != GL_FRAMEBUFFER_EXT) {
972 _mesa_error(ctx, GL_INVALID_ENUM,
973 "glFramebufferTexture%dDEXT(target)", dims);
974 return GL_TRUE;
975 }
976
977 /* check framebuffer binding */
978 if (ctx->DrawBuffer->Name == 0) {
979 _mesa_error(ctx, GL_INVALID_OPERATION,
980 "glFramebufferTexture%dDEXT", dims);
981 return GL_TRUE;
982 }
983
984 /* only check textarget, level if texture ID is non-zero */
985 if (texture) {
986 if ((dims == 1 && textarget != GL_TEXTURE_1D) ||
987 (dims == 3 && textarget != GL_TEXTURE_3D) ||
988 (dims == 2 && textarget != GL_TEXTURE_2D &&
989 textarget != GL_TEXTURE_RECTANGLE_ARB &&
990 !IS_CUBE_FACE(textarget))) {
991 _mesa_error(ctx, GL_INVALID_VALUE,
992 "glFramebufferTexture%dDEXT(textarget)", dims);
993 return GL_TRUE;
994 }
995
996 if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
997 _mesa_error(ctx, GL_INVALID_VALUE,
998 "glFramebufferTexture%dDEXT(level)", dims);
999 return GL_TRUE;
1000 }
1001 }
1002
1003 return GL_FALSE;
1004 }
1005
1006
1007 void GLAPIENTRY
1008 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
1009 GLenum textarget, GLuint texture, GLint level)
1010 {
1011 struct gl_renderbuffer_attachment *att;
1012 struct gl_texture_object *texObj;
1013 GET_CURRENT_CONTEXT(ctx);
1014
1015 ASSERT_OUTSIDE_BEGIN_END(ctx);
1016 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1017
1018 if (error_check_framebuffer_texture(ctx, 1, target, attachment,
1019 textarget, texture, level))
1020 return;
1021
1022 ASSERT(textarget == GL_TEXTURE_1D);
1023
1024 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1025 if (att == NULL) {
1026 _mesa_error(ctx, GL_INVALID_ENUM,
1027 "glFramebufferTexture1DEXT(attachment)");
1028 return;
1029 }
1030
1031 if (texture) {
1032 texObj = (struct gl_texture_object *)
1033 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1034 if (!texObj) {
1035 _mesa_error(ctx, GL_INVALID_VALUE,
1036 "glFramebufferTexture1DEXT(texture)");
1037 return;
1038 }
1039 if (texObj->Target != textarget) {
1040 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1041 "glFramebufferTexture1DEXT(texture target)");
1042 return;
1043 }
1044 }
1045 else {
1046 /* remove texture attachment */
1047 texObj = NULL;
1048 }
1049 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1050 }
1051
1052
1053 void GLAPIENTRY
1054 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
1055 GLenum textarget, GLuint texture, GLint level)
1056 {
1057 struct gl_renderbuffer_attachment *att;
1058 struct gl_texture_object *texObj;
1059 GET_CURRENT_CONTEXT(ctx);
1060
1061 ASSERT_OUTSIDE_BEGIN_END(ctx);
1062 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1063
1064 if (error_check_framebuffer_texture(ctx, 2, target, attachment,
1065 textarget, texture, level))
1066 return;
1067
1068 ASSERT(textarget == GL_TEXTURE_2D ||
1069 textarget == GL_TEXTURE_RECTANGLE_ARB ||
1070 IS_CUBE_FACE(textarget));
1071
1072 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1073 if (att == NULL) {
1074 _mesa_error(ctx, GL_INVALID_ENUM,
1075 "glFramebufferTexture2DEXT(attachment)");
1076 return;
1077 }
1078
1079 if (texture) {
1080 texObj = (struct gl_texture_object *)
1081 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1082 if (!texObj) {
1083 _mesa_error(ctx, GL_INVALID_VALUE,
1084 "glFramebufferTexture2DEXT(texture)");
1085 return;
1086 }
1087 if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
1088 (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
1089 && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
1090 (texObj->Target == GL_TEXTURE_CUBE_MAP
1091 && !IS_CUBE_FACE(textarget))) {
1092 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1093 "glFramebufferTexture2DEXT(texture target)");
1094 return;
1095 }
1096 }
1097 else {
1098 /* remove texture attachment */
1099 texObj = NULL;
1100 }
1101 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
1102 }
1103
1104
1105 void GLAPIENTRY
1106 _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
1107 GLenum textarget, GLuint texture,
1108 GLint level, GLint zoffset)
1109 {
1110 struct gl_renderbuffer_attachment *att;
1111 struct gl_texture_object *texObj;
1112 GET_CURRENT_CONTEXT(ctx);
1113
1114 ASSERT_OUTSIDE_BEGIN_END(ctx);
1115 FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
1116
1117 if (error_check_framebuffer_texture(ctx, 3, target, attachment,
1118 textarget, texture, level))
1119 return;
1120
1121 ASSERT(textarget == GL_TEXTURE_3D);
1122
1123 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1124 if (att == NULL) {
1125 _mesa_error(ctx, GL_INVALID_ENUM,
1126 "glFramebufferTexture1DEXT(attachment)");
1127 return;
1128 }
1129
1130 if (texture) {
1131 const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
1132 texObj = (struct gl_texture_object *)
1133 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
1134 if (!texObj) {
1135 _mesa_error(ctx, GL_INVALID_VALUE,
1136 "glFramebufferTexture3DEXT(texture)");
1137 return;
1138 }
1139 if (texObj->Target != textarget) {
1140 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
1141 "glFramebufferTexture3DEXT(texture target)");
1142 return;
1143 }
1144 if (zoffset < 0 || zoffset >= maxSize) {
1145 _mesa_error(ctx, GL_INVALID_VALUE,
1146 "glFramebufferTexture3DEXT(zoffset)");
1147 return;
1148 }
1149 }
1150 else {
1151 /* remove texture attachment */
1152 texObj = NULL;
1153 }
1154 ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
1155 level, zoffset);
1156 }
1157
1158
1159 void GLAPIENTRY
1160 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1161 GLenum renderbufferTarget,
1162 GLuint renderbuffer)
1163 {
1164 struct gl_renderbuffer_attachment *att;
1165 struct gl_renderbuffer *rb;
1166 GET_CURRENT_CONTEXT(ctx);
1167
1168 ASSERT_OUTSIDE_BEGIN_END(ctx);
1169 FLUSH_VERTICES(ctx, _NEW_BUFFERS);
1170
1171 if (target != GL_FRAMEBUFFER_EXT) {
1172 _mesa_error(ctx, GL_INVALID_ENUM,
1173 "glFramebufferRenderbufferEXT(target)");
1174 return;
1175 }
1176
1177 if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1178 _mesa_error(ctx, GL_INVALID_ENUM,
1179 "glFramebufferRenderbufferEXT(renderbufferTarget)");
1180 return;
1181 }
1182
1183 if (ctx->DrawBuffer->Name == 0) {
1184 _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1185 return;
1186 }
1187
1188 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1189 if (att == NULL) {
1190 _mesa_error(ctx, GL_INVALID_ENUM,
1191 "glFramebufferRenderbufferEXT(attachment)");
1192 return;
1193 }
1194
1195 if (renderbuffer) {
1196 rb = lookup_renderbuffer(ctx, renderbuffer);
1197 if (!rb) {
1198 _mesa_error(ctx, GL_INVALID_OPERATION,
1199 "glFramebufferRenderbufferEXT(renderbuffer)");
1200 return;
1201 }
1202 }
1203 else {
1204 /* remove renderbuffer attachment */
1205 rb = NULL;
1206 }
1207
1208 assert(ctx->Driver.FramebufferRenderbuffer);
1209 ctx->Driver.FramebufferRenderbuffer(ctx, att, rb);
1210
1211 _mesa_update_framebuffer_visual(ctx->DrawBuffer);
1212 }
1213
1214
1215 void GLAPIENTRY
1216 _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1217 GLenum pname, GLint *params)
1218 {
1219 const struct gl_renderbuffer_attachment *att;
1220 GET_CURRENT_CONTEXT(ctx);
1221
1222 ASSERT_OUTSIDE_BEGIN_END(ctx);
1223
1224 if (target != GL_FRAMEBUFFER_EXT) {
1225 _mesa_error(ctx, GL_INVALID_ENUM,
1226 "glGetFramebufferAttachmentParameterivEXT(target)");
1227 return;
1228 }
1229
1230 if (ctx->DrawBuffer->Name == 0) {
1231 _mesa_error(ctx, GL_INVALID_OPERATION,
1232 "glGetFramebufferAttachmentParameterivEXT");
1233 return;
1234 }
1235
1236 att = get_attachment(ctx, ctx->DrawBuffer, attachment);
1237 if (att == NULL) {
1238 _mesa_error(ctx, GL_INVALID_ENUM,
1239 "glGetFramebufferAttachmentParameterivEXT(attachment)");
1240 return;
1241 }
1242
1243 switch (pname) {
1244 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1245 *params = att->Type;
1246 return;
1247 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1248 if (att->Type == GL_RENDERBUFFER_EXT) {
1249 *params = att->Renderbuffer->Name;
1250 }
1251 else if (att->Type == GL_TEXTURE) {
1252 *params = att->Texture->Name;
1253 }
1254 else {
1255 _mesa_error(ctx, GL_INVALID_ENUM,
1256 "glGetFramebufferAttachmentParameterivEXT(pname)");
1257 }
1258 return;
1259 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1260 if (att->Type == GL_TEXTURE) {
1261 *params = att->TextureLevel;
1262 }
1263 else {
1264 _mesa_error(ctx, GL_INVALID_ENUM,
1265 "glGetFramebufferAttachmentParameterivEXT(pname)");
1266 }
1267 return;
1268 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1269 if (att->Type == GL_TEXTURE) {
1270 *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1271 }
1272 else {
1273 _mesa_error(ctx, GL_INVALID_ENUM,
1274 "glGetFramebufferAttachmentParameterivEXT(pname)");
1275 }
1276 return;
1277 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1278 if (att->Type == GL_TEXTURE) {
1279 *params = att->Zoffset;
1280 }
1281 else {
1282 _mesa_error(ctx, GL_INVALID_ENUM,
1283 "glGetFramebufferAttachmentParameterivEXT(pname)");
1284 }
1285 return;
1286 default:
1287 _mesa_error(ctx, GL_INVALID_ENUM,
1288 "glGetFramebufferAttachmentParameterivEXT(pname)");
1289 return;
1290 }
1291 }
1292
1293
1294 void GLAPIENTRY
1295 _mesa_GenerateMipmapEXT(GLenum target)
1296 {
1297 struct gl_texture_unit *texUnit;
1298 struct gl_texture_object *texObj;
1299 GET_CURRENT_CONTEXT(ctx);
1300
1301 ASSERT_OUTSIDE_BEGIN_END(ctx);
1302
1303 switch (target) {
1304 case GL_TEXTURE_1D:
1305 case GL_TEXTURE_2D:
1306 case GL_TEXTURE_3D:
1307 case GL_TEXTURE_CUBE_MAP:
1308 /* OK, legal value */
1309 break;
1310 default:
1311 _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1312 return;
1313 }
1314
1315 texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1316 texObj = _mesa_select_tex_object(ctx, texUnit, target);
1317
1318 /* XXX this might not handle cube maps correctly */
1319 _mesa_generate_mipmap(ctx, target, texUnit, texObj);
1320 }