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