implement the 'completeness' tests
[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 "hash.h"
35 #include "teximage.h"
36 #include "texstore.h"
37
38
39 /*
40 * When glGenRender/FramebuffersEXT() is called we insert pointers to
41 * these placeholder objects into the hash table.
42 * Later, when the object ID is first bound, we replace the placeholder
43 * with the real frame/renderbuffer.
44 */
45 static struct gl_frame_buffer_object DummyFramebuffer;
46 static struct gl_render_buffer_object DummyRenderbuffer;
47
48
49 #define IS_CUBE_FACE(TARGET) \
50 ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
51 (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
52
53
54 /**
55 * Helper routine for getting a gl_render_buffer_object.
56 */
57 static struct gl_render_buffer_object *
58 lookup_renderbuffer(GLcontext *ctx, GLuint id)
59 {
60 struct gl_render_buffer_object *rb;
61
62 if (id == 0)
63 return NULL;
64
65 rb = (struct gl_render_buffer_object *)
66 _mesa_HashLookup(ctx->Shared->RenderBuffers, id);
67 return rb;
68 }
69
70
71 /**
72 * Helper routine for getting a gl_frame_buffer_object.
73 */
74 static struct gl_frame_buffer_object *
75 lookup_framebuffer(GLcontext *ctx, GLuint id)
76 {
77 struct gl_frame_buffer_object *fb;
78
79 if (id == 0)
80 return NULL;
81
82 fb = (struct gl_frame_buffer_object *)
83 _mesa_HashLookup(ctx->Shared->FrameBuffers, id);
84 return fb;
85 }
86
87
88 /**
89 * Allocate a new gl_render_buffer_object.
90 * XXX make this a device driver function.
91 */
92 static struct gl_render_buffer_object *
93 new_renderbuffer(GLcontext *ctx, GLuint name)
94 {
95 struct gl_render_buffer_object *rb = CALLOC_STRUCT(gl_render_buffer_object);
96 if (rb) {
97 rb->Name = name;
98 rb->RefCount = 1;
99 /* other fields are zero */
100 }
101 return rb;
102 }
103
104
105 /**
106 * Allocate a new gl_frame_buffer_object.
107 * XXX make this a device driver function.
108 */
109 static struct gl_frame_buffer_object *
110 new_framebuffer(GLcontext *ctx, GLuint name)
111 {
112 struct gl_frame_buffer_object *fb = CALLOC_STRUCT(gl_frame_buffer_object);
113 if (fb) {
114 fb->Name = name;
115 fb->RefCount = 1;
116 }
117 return fb;
118 }
119
120
121 static struct gl_render_buffer_attachment *
122 get_attachment(GLcontext *ctx, GLenum attachment)
123 {
124 GLuint i;
125
126 switch (attachment) {
127 case GL_COLOR_ATTACHMENT0_EXT:
128 case GL_COLOR_ATTACHMENT1_EXT:
129 case GL_COLOR_ATTACHMENT2_EXT:
130 case GL_COLOR_ATTACHMENT3_EXT:
131 case GL_COLOR_ATTACHMENT4_EXT:
132 case GL_COLOR_ATTACHMENT5_EXT:
133 case GL_COLOR_ATTACHMENT6_EXT:
134 case GL_COLOR_ATTACHMENT7_EXT:
135 case GL_COLOR_ATTACHMENT8_EXT:
136 case GL_COLOR_ATTACHMENT9_EXT:
137 case GL_COLOR_ATTACHMENT10_EXT:
138 case GL_COLOR_ATTACHMENT11_EXT:
139 case GL_COLOR_ATTACHMENT12_EXT:
140 case GL_COLOR_ATTACHMENT13_EXT:
141 case GL_COLOR_ATTACHMENT14_EXT:
142 case GL_COLOR_ATTACHMENT15_EXT:
143 i = attachment - GL_COLOR_ATTACHMENT0_EXT;
144 if (i >= ctx->Const.MaxColorAttachments) {
145 return NULL;
146 }
147 return &ctx->CurrentFramebuffer->ColorAttachment[i];
148 case GL_DEPTH_ATTACHMENT_EXT:
149 return &ctx->CurrentFramebuffer->DepthAttachment;
150 case GL_STENCIL_ATTACHMENT_EXT:
151 return &ctx->CurrentFramebuffer->StencilAttachment;
152 default:
153 return NULL;
154 }
155 }
156
157
158 static void
159 remove_attachment(GLcontext *ctx, struct gl_render_buffer_attachment *att)
160 {
161 if (att->Type == GL_TEXTURE) {
162 ASSERT(att->Texture);
163 ASSERT(!att->Renderbuffer);
164 att->Texture->RefCount--;
165 if (att->Texture->RefCount == 0) {
166 ctx->Driver.DeleteTexture(ctx, att->Texture);
167 }
168 att->Texture = NULL;
169 }
170 else if (att->Type == GL_RENDERBUFFER_EXT) {
171 ASSERT(att->Renderbuffer);
172 ASSERT(!att->Texture);
173 att->Renderbuffer->RefCount--;
174 if (att->Renderbuffer->RefCount == 0) {
175 _mesa_free(att->Renderbuffer); /* XXX driver free */
176 }
177 att->Renderbuffer = NULL;
178 }
179 att->Type = GL_NONE;
180 att->Complete = GL_TRUE;
181 }
182
183
184 static void
185 set_texture_attachment(GLcontext *ctx,
186 struct gl_render_buffer_attachment *att,
187 struct gl_texture_object *texObj,
188 GLenum texTarget, GLuint level, GLuint zoffset)
189 {
190 remove_attachment(ctx, att);
191 att->Type = GL_TEXTURE;
192 att->Texture = texObj;
193 att->TextureLevel = level;
194 if (IS_CUBE_FACE(texTarget)) {
195 att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
196 }
197 else {
198 att->CubeMapFace = 0;
199 }
200 att->Zoffset = zoffset;
201 att->Complete = GL_FALSE;
202 texObj->RefCount++;
203 }
204
205
206 static void
207 set_renderbuffer_attachment(GLcontext *ctx,
208 struct gl_render_buffer_attachment *att,
209 struct gl_render_buffer_object *rb)
210 {
211 remove_attachment(ctx, att);
212 att->Type = GL_RENDERBUFFER_EXT;
213 att->Renderbuffer = rb;
214 att->Complete = GL_FALSE;
215 rb->RefCount++;
216 }
217
218
219 /**
220 * Test if an attachment point is complete and update its Complete field.
221 * \param format if GL_COLOR, this is a color attachment point,
222 * if GL_DEPTH, this is a depth component attachment point,
223 * if GL_STENCIL, this is a stencil component attachment point.
224 */
225 static void
226 test_attachment_completeness(const GLcontext *ctx, GLenum format,
227 struct gl_render_buffer_attachment *att)
228 {
229 assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL);
230
231 /* assume complete */
232 att->Complete = GL_TRUE;
233
234 if (att->Type == GL_NONE)
235 return; /* complete */
236
237 /* Look for reasons why the attachment might be incomplete */
238 if (att->Type == GL_TEXTURE) {
239 struct gl_texture_object *texObj = att->Texture;
240 struct gl_texture_image *texImage;
241
242 assert(texObj);
243
244 texImage = texObj->Image[att->CubeMapFace][att->TextureLevel];
245 if (!texImage) {
246 att->Complete = GL_FALSE;
247 return;
248 }
249 if (texImage->Width < 1 || texImage->Height < 1) {
250 att->Complete = GL_FALSE;
251 return;
252 }
253 if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) {
254 att->Complete = GL_FALSE;
255 return;
256 }
257
258 if (format == GL_COLOR) {
259 if (texImage->Format != GL_RGB && texImage->Format != GL_RGBA) {
260 att->Complete = GL_FALSE;
261 return;
262 }
263 }
264 else if (format == GL_DEPTH) {
265 if (texImage->Format != GL_DEPTH_COMPONENT) {
266 att->Complete = GL_FALSE;
267 return;
268 }
269 }
270 else {
271 /* no such thing as stencil textures */
272 att->Complete = GL_FALSE;
273 return;
274 }
275 }
276 else {
277 assert(att->Type == GL_RENDERBUFFER_EXT);
278
279 if (att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) {
280 att->Complete = GL_FALSE;
281 return;
282 }
283 if (format == GL_COLOR) {
284 if (att->Renderbuffer->_BaseFormat != GL_RGB &&
285 att->Renderbuffer->_BaseFormat != GL_RGBA) {
286 att->Complete = GL_FALSE;
287 return;
288 }
289 }
290 else if (format == GL_DEPTH) {
291 if (att->Renderbuffer->_BaseFormat != GL_DEPTH_COMPONENT) {
292 att->Complete = GL_FALSE;
293 return;
294 }
295 }
296 else {
297 assert(format == GL_STENCIL);
298 if (att->Renderbuffer->_BaseFormat != GL_STENCIL_INDEX) {
299 att->Complete = GL_FALSE;
300 return;
301 }
302 }
303 }
304 }
305
306
307 /**
308 * Test if the given framebuffer object is complete and update its
309 * Status field with the results.
310 */
311 static void
312 test_framebuffer_completeness(GLcontext *ctx,
313 struct gl_frame_buffer_object *fb)
314 {
315 GLint i;
316 GLuint numImages, width, height;
317 GLenum intFormat;
318
319 /* Set to COMPLETE status, then try to find reasons for being incomplete */
320 fb->Status = GL_FRAMEBUFFER_COMPLETE_EXT;
321
322 numImages = 0;
323
324 /* Start at -2 to more easily loop over all attachment points */
325 for (i = -2; i < ctx->Const.MaxColorAttachments; i++) {
326 struct gl_render_buffer_attachment *att;
327 GLuint w, h;
328 GLenum f;
329
330 if (i == -2) {
331 att = &fb->DepthAttachment;
332 test_attachment_completeness(ctx, GL_DEPTH, att);
333 if (!att->Complete) {
334 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
335 return;
336 }
337 }
338 else if (i == -1) {
339 att = &fb->StencilAttachment;
340 test_attachment_completeness(ctx, GL_STENCIL, att);
341 if (!att->Complete) {
342 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
343 return;
344 }
345 }
346 else {
347 att = &fb->ColorAttachment[i];
348 test_attachment_completeness(ctx, GL_COLOR, att);
349 if (!att->Complete) {
350 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
351 return;
352 }
353 }
354
355 if (att->Type == GL_TEXTURE) {
356 w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width;
357 h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height;
358 f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format;
359 numImages++;
360 }
361 else if (att->Type == GL_RENDERBUFFER_EXT) {
362 w = att->Renderbuffer->Width;
363 h = att->Renderbuffer->Height;
364 f = att->Renderbuffer->InternalFormat;
365 numImages++;
366 }
367 else {
368 assert(att->Type == GL_NONE);
369 continue;
370 }
371
372 if (numImages == 1) {
373 /* set required width, height and format */
374 width = w;
375 height = h;
376 if (i >= 0)
377 intFormat = f;
378 }
379 else {
380 /* check that width, height, format are same */
381 if (w != width || h != height) {
382 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
383 return;
384 }
385 if (i >= 0 && f != intFormat) {
386 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
387 return;
388 }
389
390 }
391 }
392
393 /* Check that all DrawBuffers are present */
394 for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
395 if (fb->DrawBuffer[i] != GL_NONE) {
396 struct gl_render_buffer_attachment *att
397 = get_attachment(ctx, fb->DrawBuffer[i]);
398 if (att->Type == GL_NONE) {
399 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
400 return;
401 }
402 }
403 }
404
405 /* Check that the ReadBuffer is present */
406 if (fb->ReadBuffer != GL_NONE) {
407 struct gl_render_buffer_attachment *att
408 = get_attachment(ctx, fb->ReadBuffer);
409 if (att->Type == GL_NONE) {
410 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
411 return;
412 }
413 }
414
415 if (numImages == 0) {
416 fb->Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
417 return;
418 }
419 }
420
421
422
423 GLboolean GLAPIENTRY
424 _mesa_IsRenderbufferEXT(GLuint renderbuffer)
425 {
426 const struct gl_render_buffer_object *rb;
427 GET_CURRENT_CONTEXT(ctx);
428
429 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
430
431 rb = lookup_renderbuffer(ctx, renderbuffer);
432 return rb ? GL_TRUE : GL_FALSE;
433 }
434
435
436 void GLAPIENTRY
437 _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
438 {
439 struct gl_render_buffer_object *newRb, *oldRb;
440 GET_CURRENT_CONTEXT(ctx);
441
442 ASSERT_OUTSIDE_BEGIN_END(ctx);
443
444 if (target != GL_RENDERBUFFER_EXT) {
445 _mesa_error(ctx, GL_INVALID_ENUM,
446 "glBindRenderbufferEXT(target)");
447 return;
448 }
449
450 if (renderbuffer) {
451 newRb = lookup_renderbuffer(ctx, renderbuffer);
452 if (newRb == &DummyRenderbuffer) {
453 /* ID was reserved, but no real renderbuffer object made yet */
454 newRb = NULL;
455 }
456 if (!newRb) {
457 /* create new renderbuffer object */
458 newRb = new_renderbuffer(ctx, renderbuffer);
459 if (!newRb) {
460 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT");
461 return;
462 }
463 _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
464 }
465 newRb->RefCount++;
466 }
467 else {
468 newRb = NULL;
469 }
470
471 oldRb = ctx->CurrentRenderbuffer;
472 if (oldRb) {
473 oldRb->RefCount--;
474 if (oldRb->RefCount == 0) {
475 _mesa_free(oldRb); /* XXX device driver function */
476 }
477 }
478
479 ASSERT(newRb != &DummyRenderbuffer);
480
481 ctx->CurrentRenderbuffer = newRb;
482 }
483
484
485 void GLAPIENTRY
486 _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
487 {
488 GLint i;
489 GET_CURRENT_CONTEXT(ctx);
490
491 ASSERT_OUTSIDE_BEGIN_END(ctx);
492
493 for (i = 0; i < n; i++) {
494 if (renderbuffers[i]) {
495 struct gl_render_buffer_object *rb;
496 rb = lookup_renderbuffer(ctx, renderbuffers[i]);
497 if (rb) {
498 /* remove from hash table immediately, to free the ID */
499 _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]);
500
501 if (rb != &DummyRenderbuffer) {
502 /* But the object will not be freed until it's no longer
503 * bound in any context.
504 */
505 rb->RefCount--;
506 if (rb->RefCount == 0) {
507 _mesa_free(rb); /* XXX call device driver function */
508 }
509 }
510 }
511 }
512 }
513 }
514
515
516 void GLAPIENTRY
517 _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
518 {
519 GET_CURRENT_CONTEXT(ctx);
520 GLuint first;
521 GLint i;
522
523 ASSERT_OUTSIDE_BEGIN_END(ctx);
524
525 if (n < 0) {
526 _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)");
527 return;
528 }
529
530 if (!renderbuffers)
531 return;
532
533 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n);
534
535 for (i = 0; i < n; i++) {
536 GLuint name = first + i;
537 renderbuffers[i] = name;
538 /* insert dummy placeholder into hash table */
539 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
540 _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer);
541 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
542 }
543 }
544
545
546 static GLenum
547 base_internal_format(GLcontext *ctx, GLenum internalFormat)
548 {
549 switch (internalFormat) {
550 case GL_RGB:
551 case GL_R3_G3_B2:
552 case GL_RGB4:
553 case GL_RGB5:
554 case GL_RGB8:
555 case GL_RGB10:
556 case GL_RGB12:
557 case GL_RGB16:
558 return GL_RGB;
559 case GL_RGBA:
560 case GL_RGBA2:
561 case GL_RGBA4:
562 case GL_RGB5_A1:
563 case GL_RGBA8:
564 case GL_RGB10_A2:
565 case GL_RGBA12:
566 case GL_RGBA16:
567 return GL_RGBA;
568 case GL_STENCIL_INDEX:
569 case GL_STENCIL_INDEX1_EXT:
570 case GL_STENCIL_INDEX4_EXT:
571 case GL_STENCIL_INDEX8_EXT:
572 case GL_STENCIL_INDEX16_EXT:
573 return GL_STENCIL_INDEX;
574 case GL_DEPTH_COMPONENT:
575 case GL_DEPTH_COMPONENT16_SGIX:
576 case GL_DEPTH_COMPONENT24_SGIX:
577 case GL_DEPTH_COMPONENT32_SGIX:
578 return GL_DEPTH_COMPONENT;
579 default:
580 return 0;
581 }
582 }
583
584
585 void GLAPIENTRY
586 _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
587 GLsizei width, GLsizei height)
588 {
589 GLenum baseFormat;
590 GET_CURRENT_CONTEXT(ctx);
591
592 ASSERT_OUTSIDE_BEGIN_END(ctx);
593
594 if (target != GL_RENDERBUFFER_EXT) {
595 _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
596 return;
597 }
598
599 baseFormat = base_internal_format(ctx, internalFormat);
600 if (baseFormat == 0) {
601 _mesa_error(ctx, GL_INVALID_ENUM,
602 "glRenderbufferStorageEXT(internalFormat)");
603 return;
604 }
605
606 if (width < 1 || width > ctx->Const.MaxRenderbufferSize) {
607 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
608 return;
609 }
610
611 if (height < 1 || height > ctx->Const.MaxRenderbufferSize) {
612 _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
613 return;
614 }
615
616 /* XXX this check isn't in the spec, but seems necessary */
617 if (!ctx->CurrentRenderbuffer) {
618 _mesa_error(ctx, GL_INVALID_OPERATION, "glRenderbufferStorageEXT");
619 return;
620 }
621
622 if (ctx->CurrentRenderbuffer->Data) {
623 /* XXX device driver free */
624 _mesa_free(ctx->CurrentRenderbuffer->Data);
625 }
626
627 /* XXX device driver allocate, fix size */
628 ctx->CurrentRenderbuffer->Data = _mesa_malloc(width * height * 4);
629 if (ctx->CurrentRenderbuffer->Data == NULL) {
630 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glRenderbufferStorageEXT");
631 return;
632 }
633 ctx->CurrentRenderbuffer->InternalFormat = internalFormat;
634 ctx->CurrentRenderbuffer->Width = width;
635 ctx->CurrentRenderbuffer->Height = height;
636 ctx->CurrentRenderbuffer->_BaseFormat = baseFormat;
637 }
638
639
640 void GLAPIENTRY
641 _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
642 {
643 GET_CURRENT_CONTEXT(ctx);
644
645 ASSERT_OUTSIDE_BEGIN_END(ctx);
646
647 if (target != GL_RENDERBUFFER_EXT) {
648 _mesa_error(ctx, GL_INVALID_ENUM,
649 "glGetRenderbufferParameterivEXT(target)");
650 return;
651 }
652
653 if (!ctx->CurrentRenderbuffer) {
654 _mesa_error(ctx, GL_INVALID_OPERATION,
655 "glGetRenderbufferParameterivEXT");
656 return;
657 }
658
659 switch (pname) {
660 case GL_RENDERBUFFER_WIDTH_EXT:
661 *params = ctx->CurrentRenderbuffer->Width;
662 return;
663 case GL_RENDERBUFFER_HEIGHT_EXT:
664 *params = ctx->CurrentRenderbuffer->Height;
665 return;
666 case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT:
667 *params = ctx->CurrentRenderbuffer->InternalFormat;
668 return;
669 default:
670 _mesa_error(ctx, GL_INVALID_ENUM,
671 "glGetRenderbufferParameterivEXT(target)");
672 return;
673 }
674 }
675
676
677 GLboolean GLAPIENTRY
678 _mesa_IsFramebufferEXT(GLuint framebuffer)
679 {
680 const struct gl_frame_buffer_object *fb;
681 GET_CURRENT_CONTEXT(ctx);
682
683 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
684
685 fb = lookup_framebuffer(ctx, framebuffer);
686 return fb ? GL_TRUE : GL_FALSE;
687 }
688
689
690 void GLAPIENTRY
691 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
692 {
693 struct gl_frame_buffer_object *newFb, *oldFb;
694 GET_CURRENT_CONTEXT(ctx);
695
696 ASSERT_OUTSIDE_BEGIN_END(ctx);
697
698 if (target != GL_FRAMEBUFFER_EXT) {
699 _mesa_error(ctx, GL_INVALID_ENUM,
700 "glBindFramebufferEXT(target)");
701 return;
702 }
703
704 if (framebuffer) {
705 newFb = lookup_framebuffer(ctx, framebuffer);
706 if (newFb == &DummyFramebuffer) {
707 /* ID was reserved, but no real framebuffer object made yet */
708 newFb = NULL;
709 }
710 if (!newFb) {
711 /* create new framebuffer object */
712 newFb = new_framebuffer(ctx, framebuffer);
713 if (!newFb) {
714 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT");
715 return;
716 }
717 _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
718 }
719 newFb->RefCount++;
720 }
721 else {
722 newFb = NULL;
723 }
724
725 oldFb = ctx->CurrentFramebuffer;
726 if (oldFb) {
727 oldFb->RefCount--;
728 if (oldFb->RefCount == 0) {
729 _mesa_free(oldFb); /* XXX device driver function */
730 }
731 }
732
733 ASSERT(newFb != &DummyFramebuffer);
734
735 ctx->CurrentFramebuffer = newFb;
736 }
737
738
739 void GLAPIENTRY
740 _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
741 {
742 GLint i;
743 GET_CURRENT_CONTEXT(ctx);
744
745 ASSERT_OUTSIDE_BEGIN_END(ctx);
746
747 for (i = 0; i < n; i++) {
748 if (framebuffers[i]) {
749 struct gl_frame_buffer_object *fb;
750 fb = lookup_framebuffer(ctx, framebuffers[i]);
751 if (fb) {
752 /* remove from hash table immediately, to free the ID */
753 _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
754
755 if (fb != &DummyFramebuffer) {
756 /* But the object will not be freed until it's no longer
757 * bound in any context.
758 */
759 fb->RefCount--;
760 if (fb->RefCount == 0) {
761 _mesa_free(fb); /* XXX call device driver function */
762 }
763 }
764 }
765 }
766 }
767 }
768
769
770 void GLAPIENTRY
771 _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
772 {
773 GET_CURRENT_CONTEXT(ctx);
774 GLuint first;
775 GLint i;
776
777 ASSERT_OUTSIDE_BEGIN_END(ctx);
778
779 if (n < 0) {
780 _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)");
781 return;
782 }
783
784 if (!framebuffers)
785 return;
786
787 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n);
788
789 for (i = 0; i < n; i++) {
790 GLuint name = first + i;
791 framebuffers[i] = name;
792 /* insert dummy placeholder into hash table */
793 _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
794 _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer);
795 _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
796 }
797 }
798
799
800
801 GLenum GLAPIENTRY
802 _mesa_CheckFramebufferStatusEXT(GLenum target)
803 {
804 GET_CURRENT_CONTEXT(ctx);
805
806 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FRAMEBUFFER_STATUS_ERROR_EXT);
807
808 if (target != GL_FRAMEBUFFER_EXT) {
809 _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
810 return GL_FRAMEBUFFER_STATUS_ERROR_EXT;
811 }
812
813 if (!ctx->CurrentFramebuffer) {
814 /* The window system / default framebuffer is always complete */
815 return GL_FRAMEBUFFER_COMPLETE_EXT;
816 }
817
818 test_framebuffer_completeness(ctx, ctx->CurrentFramebuffer);
819 return ctx->CurrentFramebuffer->Status;
820 }
821
822
823
824 /**
825 * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
826 * \return GL_TRUE if any error, GL_FALSE otherwise
827 */
828 static GLboolean
829 error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
830 GLenum target, GLenum attachment,
831 GLenum textarget, GLuint texture, GLint level)
832 {
833 ASSERT(dims >= 1 && dims <= 3);
834
835 if (target != GL_FRAMEBUFFER_EXT) {
836 _mesa_error(ctx, GL_INVALID_ENUM,
837 "glFramebufferTexture%dDEXT(target)", dims);
838 return GL_TRUE;
839 }
840
841 if (ctx->CurrentFramebuffer == NULL) {
842 _mesa_error(ctx, GL_INVALID_OPERATION,
843 "glFramebufferTexture%dDEXT", dims);
844 return GL_TRUE;
845 }
846
847 /* only check textarget, level if texture ID is non-zero*/
848 if (texture) {
849 if ((dims == 1 && textarget != GL_TEXTURE_1D) ||
850 (dims == 3 && textarget != GL_TEXTURE_3D) ||
851 (dims == 2 && textarget != GL_TEXTURE_2D &&
852 textarget != GL_TEXTURE_RECTANGLE_ARB &&
853 !IS_CUBE_FACE(textarget))) {
854 _mesa_error(ctx, GL_INVALID_VALUE,
855 "glFramebufferTexture%dDEXT(textarget)", dims);
856 return GL_TRUE;
857 }
858
859 if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
860 _mesa_error(ctx, GL_INVALID_VALUE,
861 "glFramebufferTexture%dDEXT(level)", dims);
862 return GL_TRUE;
863 }
864 }
865
866 return GL_FALSE;
867 }
868
869
870 void GLAPIENTRY
871 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
872 GLenum textarget, GLuint texture, GLint level)
873 {
874 struct gl_render_buffer_attachment *att;
875 GET_CURRENT_CONTEXT(ctx);
876
877 ASSERT_OUTSIDE_BEGIN_END(ctx);
878
879 if (error_check_framebuffer_texture(ctx, 1, target, attachment,
880 textarget, texture, level))
881 return;
882
883 ASSERT(textarget == GL_TEXTURE_1D);
884
885 att = get_attachment(ctx, attachment);
886 if (att == NULL) {
887 _mesa_error(ctx, GL_INVALID_ENUM,
888 "glFramebufferTexture1DEXT(attachment)");
889 return;
890 }
891
892 if (texture) {
893 struct gl_texture_object *texObj = (struct gl_texture_object *)
894 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
895 if (!texObj) {
896 _mesa_error(ctx, GL_INVALID_VALUE,
897 "glFramebufferTexture1DEXT(texture)");
898 return;
899 }
900 if (texObj->Target != textarget) {
901 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
902 "glFramebufferTexture1DEXT(texture target)");
903 return;
904 }
905 set_texture_attachment(ctx, att, texObj, textarget, level, 0);
906 }
907 else {
908 remove_attachment(ctx, att);
909 }
910
911 /* XXX call a driver function to signal new attachment? */
912 }
913
914
915 void GLAPIENTRY
916 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
917 GLenum textarget, GLuint texture, GLint level)
918 {
919 struct gl_render_buffer_attachment *att;
920 GET_CURRENT_CONTEXT(ctx);
921
922 ASSERT_OUTSIDE_BEGIN_END(ctx);
923
924 if (error_check_framebuffer_texture(ctx, 2, target, attachment,
925 textarget, texture, level))
926 return;
927
928 ASSERT(textarget == GL_TEXTURE_2D ||
929 textarget == GL_TEXTURE_RECTANGLE_ARB ||
930 IS_CUBE_FACE(textarget));
931
932 att = get_attachment(ctx, attachment);
933 if (att == NULL) {
934 _mesa_error(ctx, GL_INVALID_ENUM,
935 "glFramebufferTexture2DEXT(attachment)");
936 return;
937 }
938
939 if (texture) {
940 struct gl_texture_object *texObj = (struct gl_texture_object *)
941 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
942 if (!texObj) {
943 _mesa_error(ctx, GL_INVALID_VALUE,
944 "glFramebufferTexture2DEXT(texture)");
945 return;
946 }
947 if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
948 (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
949 && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
950 (texObj->Target == GL_TEXTURE_CUBE_MAP
951 && !IS_CUBE_FACE(textarget))) {
952 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
953 "glFramebufferTexture2DEXT(texture target)");
954 return;
955 }
956 set_texture_attachment(ctx, att, texObj, textarget, level, 0);
957 }
958 else {
959 remove_attachment(ctx, att);
960 }
961
962 }
963
964
965 void GLAPIENTRY
966 _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
967 GLenum textarget, GLuint texture,
968 GLint level, GLint zoffset)
969 {
970 struct gl_render_buffer_attachment *att;
971 GET_CURRENT_CONTEXT(ctx);
972
973 ASSERT_OUTSIDE_BEGIN_END(ctx);
974
975 if (error_check_framebuffer_texture(ctx, 3, target, attachment,
976 textarget, texture, level))
977 return;
978
979 ASSERT(textarget == GL_TEXTURE_3D);
980
981 att = get_attachment(ctx, attachment);
982 if (att == NULL) {
983 _mesa_error(ctx, GL_INVALID_ENUM,
984 "glFramebufferTexture1DEXT(attachment)");
985 return;
986 }
987
988 if (texture) {
989 struct gl_texture_object *texObj = (struct gl_texture_object *)
990 _mesa_HashLookup(ctx->Shared->TexObjects, texture);
991 if (!texObj) {
992 _mesa_error(ctx, GL_INVALID_VALUE,
993 "glFramebufferTexture3DEXT(texture)");
994 return;
995 }
996 if (texObj->Target != textarget) {
997 _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
998 "glFramebufferTexture3DEXT(texture target)");
999 return;
1000 }
1001 if (zoffset >= texObj->Image[0][level]->Depth) {
1002 _mesa_error(ctx, GL_INVALID_VALUE,
1003 "glFramebufferTexture3DEXT(zoffset)");
1004 return;
1005 }
1006 set_texture_attachment(ctx, att, texObj, textarget, level, zoffset);
1007 }
1008 else {
1009 remove_attachment(ctx, att);
1010 }
1011 }
1012
1013
1014 void GLAPIENTRY
1015 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
1016 GLenum renderbufferTarget,
1017 GLuint renderbuffer)
1018 {
1019 struct gl_render_buffer_attachment *att;
1020 GET_CURRENT_CONTEXT(ctx);
1021
1022 ASSERT_OUTSIDE_BEGIN_END(ctx);
1023
1024 if (target != GL_FRAMEBUFFER_EXT) {
1025 _mesa_error(ctx, GL_INVALID_ENUM,
1026 "glFramebufferRenderbufferEXT(target)");
1027 return;
1028 }
1029
1030 if (renderbufferTarget != GL_RENDERBUFFER_EXT) {
1031 _mesa_error(ctx, GL_INVALID_ENUM,
1032 "glFramebufferRenderbufferEXT(renderbufferTarget)");
1033 return;
1034 }
1035
1036 if (ctx->CurrentFramebuffer == NULL) {
1037 _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
1038 return;
1039 }
1040
1041 att = get_attachment(ctx, attachment);
1042 if (att == NULL) {
1043 _mesa_error(ctx, GL_INVALID_ENUM,
1044 "glFramebufferRenderbufferEXT(attachment)");
1045 return;
1046 }
1047
1048 if (renderbuffer) {
1049 struct gl_render_buffer_object *rb;
1050 rb = lookup_renderbuffer(ctx, renderbuffer);
1051 if (!rb) {
1052 _mesa_error(ctx, GL_INVALID_VALUE,
1053 "glFramebufferRenderbufferEXT(renderbuffer)");
1054 return;
1055 }
1056 set_renderbuffer_attachment(ctx, att, rb);
1057 }
1058 else {
1059 remove_attachment(ctx, att);
1060 }
1061 }
1062
1063
1064 void GLAPIENTRY
1065 _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
1066 GLenum pname, GLint *params)
1067 {
1068 const struct gl_render_buffer_attachment *att;
1069 GET_CURRENT_CONTEXT(ctx);
1070
1071 ASSERT_OUTSIDE_BEGIN_END(ctx);
1072
1073 if (target != GL_FRAMEBUFFER_EXT) {
1074 _mesa_error(ctx, GL_INVALID_ENUM,
1075 "glGetFramebufferAttachmentParameterivEXT(target)");
1076 return;
1077 }
1078
1079 if (ctx->CurrentFramebuffer == NULL) {
1080 _mesa_error(ctx, GL_INVALID_OPERATION,
1081 "glGetFramebufferAttachmentParameterivEXT");
1082 return;
1083 }
1084
1085 att = get_attachment(ctx, attachment);
1086 if (att == NULL) {
1087 _mesa_error(ctx, GL_INVALID_ENUM,
1088 "glGetFramebufferAttachmentParameterivEXT(attachment)");
1089 return;
1090 }
1091
1092 switch (pname) {
1093 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
1094 *params = att->Type;
1095 return;
1096 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
1097 if (att->Type == GL_RENDERBUFFER_EXT) {
1098 *params = att->Renderbuffer->Name;
1099 }
1100 else if (att->Type == GL_TEXTURE) {
1101 *params = att->Texture->Name;
1102 }
1103 else {
1104 _mesa_error(ctx, GL_INVALID_ENUM,
1105 "glGetFramebufferAttachmentParameterivEXT(pname)");
1106 }
1107 return;
1108 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
1109 if (att->Type == GL_TEXTURE) {
1110 *params = att->TextureLevel;
1111 }
1112 else {
1113 _mesa_error(ctx, GL_INVALID_ENUM,
1114 "glGetFramebufferAttachmentParameterivEXT(pname)");
1115 }
1116 return;
1117 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT:
1118 if (att->Type == GL_TEXTURE) {
1119 *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace;
1120 }
1121 else {
1122 _mesa_error(ctx, GL_INVALID_ENUM,
1123 "glGetFramebufferAttachmentParameterivEXT(pname)");
1124 }
1125 return;
1126 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT:
1127 if (att->Type == GL_TEXTURE) {
1128 *params = att->Zoffset;
1129 }
1130 else {
1131 _mesa_error(ctx, GL_INVALID_ENUM,
1132 "glGetFramebufferAttachmentParameterivEXT(pname)");
1133 }
1134 return;
1135 default:
1136 _mesa_error(ctx, GL_INVALID_ENUM,
1137 "glGetFramebufferAttachmentParameterivEXT(pname)");
1138 return;
1139 }
1140 }
1141
1142
1143 void GLAPIENTRY
1144 _mesa_GenerateMipmapEXT(GLenum target)
1145 {
1146 struct gl_texture_unit *texUnit;
1147 struct gl_texture_object *texObj;
1148 GET_CURRENT_CONTEXT(ctx);
1149
1150 ASSERT_OUTSIDE_BEGIN_END(ctx);
1151
1152 switch (target) {
1153 case GL_TEXTURE_1D:
1154 case GL_TEXTURE_2D:
1155 case GL_TEXTURE_3D:
1156 case GL_TEXTURE_CUBE_MAP:
1157 break;
1158 default:
1159 _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
1160 return;
1161 }
1162
1163 texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
1164 texObj = _mesa_select_tex_object(ctx, texUnit, target);
1165
1166 /* XXX this might not handle cube maps correctly */
1167 _mesa_generate_mipmap(ctx, target, texUnit, texObj);
1168 }