2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
34 #include "framebuffer.h"
36 #include "renderbuffer.h"
44 * None of the GL_EXT_framebuffer_object functions are compiled into
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.
56 static struct gl_framebuffer DummyFramebuffer
;
57 static struct gl_renderbuffer DummyRenderbuffer
;
60 #define IS_CUBE_FACE(TARGET) \
61 ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
62 (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
66 * Helper routine for getting a gl_renderbuffer.
68 static struct gl_renderbuffer
*
69 lookup_renderbuffer(GLcontext
*ctx
, GLuint id
)
71 struct gl_renderbuffer
*rb
;
76 rb
= (struct gl_renderbuffer
*)
77 _mesa_HashLookup(ctx
->Shared
->RenderBuffers
, id
);
83 * Helper routine for getting a gl_framebuffer.
85 static struct gl_framebuffer
*
86 lookup_framebuffer(GLcontext
*ctx
, GLuint id
)
88 struct gl_framebuffer
*fb
;
93 fb
= (struct gl_framebuffer
*)
94 _mesa_HashLookup(ctx
->Shared
->FrameBuffers
, id
);
100 * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
101 * gl_renderbuffer_attachment object.
103 static struct gl_renderbuffer_attachment
*
104 get_attachment(GLcontext
*ctx
, struct gl_framebuffer
*fb
, GLenum attachment
)
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
) {
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
];
141 * Remove any texture or renderbuffer attached to the given attachment
142 * point. Update reference counts, etc.
145 _mesa_remove_attachment(GLcontext
*ctx
, struct gl_renderbuffer_attachment
*att
)
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
;
155 att
->Texture
->RefCount
--;
156 if (att
->Texture
->RefCount
== 0) {
157 ctx
->Driver
.DeleteTexture(ctx
, att
->Texture
);
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
);
168 att
->Renderbuffer
= NULL
;
171 att
->Complete
= GL_TRUE
;
176 * Bind a texture object to an attachment point.
177 * The previous binding, if any, will be removed first.
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
)
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
;
193 att
->CubeMapFace
= 0;
195 att
->Zoffset
= zoffset
;
196 att
->Complete
= GL_FALSE
;
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.
208 * Bind a renderbuffer to an attachment point.
209 * The previous binding, if any, will be removed first.
212 _mesa_set_renderbuffer_attachment(GLcontext
*ctx
,
213 struct gl_renderbuffer_attachment
*att
,
214 struct gl_renderbuffer
*rb
)
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
;
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
232 _mesa_framebuffer_renderbuffer(GLcontext
*ctx
,
233 struct gl_renderbuffer_attachment
*att
,
234 struct gl_renderbuffer
*rb
)
237 _mesa_set_renderbuffer_attachment(ctx
, att
, rb
);
240 _mesa_remove_attachment(ctx
, att
);
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.
252 test_attachment_completeness(const GLcontext
*ctx
, GLenum format
,
253 struct gl_renderbuffer_attachment
*att
)
255 assert(format
== GL_COLOR
|| format
== GL_DEPTH
|| format
== GL_STENCIL
);
257 /* assume complete */
258 att
->Complete
= GL_TRUE
;
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
;
266 att
->Complete
= GL_FALSE
;
270 texImage
= texObj
->Image
[att
->CubeMapFace
][att
->TextureLevel
];
272 att
->Complete
= GL_FALSE
;
275 if (texImage
->Width
< 1 || texImage
->Height
< 1) {
276 att
->Complete
= GL_FALSE
;
279 if (texObj
->Target
== GL_TEXTURE_3D
&& att
->Zoffset
>= texImage
->Depth
) {
280 att
->Complete
= GL_FALSE
;
284 if (format
== GL_COLOR
) {
285 if (texImage
->TexFormat
->BaseFormat
!= GL_RGB
&&
286 texImage
->TexFormat
->BaseFormat
!= GL_RGBA
) {
287 att
->Complete
= GL_FALSE
;
291 else if (format
== GL_DEPTH
) {
292 if (texImage
->TexFormat
->BaseFormat
!= GL_DEPTH_COMPONENT
) {
293 att
->Complete
= GL_FALSE
;
298 /* no such thing as stencil textures */
299 att
->Complete
= GL_FALSE
;
303 else if (att
->Type
== GL_RENDERBUFFER_EXT
) {
304 if (att
->Renderbuffer
->Width
< 1 || att
->Renderbuffer
->Height
< 1) {
305 att
->Complete
= GL_FALSE
;
308 if (format
== GL_COLOR
) {
309 if (att
->Renderbuffer
->_BaseFormat
!= GL_RGB
&&
310 att
->Renderbuffer
->_BaseFormat
!= GL_RGBA
) {
311 att
->Complete
= GL_FALSE
;
315 else if (format
== GL_DEPTH
) {
316 if (att
->Renderbuffer
->_BaseFormat
!= GL_DEPTH_COMPONENT
) {
317 att
->Complete
= GL_FALSE
;
322 assert(format
== GL_STENCIL
);
323 if (att
->Renderbuffer
->_BaseFormat
!= GL_STENCIL_INDEX
) {
324 att
->Complete
= GL_FALSE
;
330 ASSERT(att
->Type
== GL_NONE
);
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.
344 _mesa_test_framebuffer_completeness(GLcontext
*ctx
, struct gl_framebuffer
*fb
)
346 GLuint numImages
, width
= 0, height
= 0;
347 GLenum intFormat
= GL_NONE
;
351 assert(fb
->Name
!= 0);
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
;
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
;
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
;
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
;
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
;
392 if (f
!= GL_RGB
&& f
!= GL_RGBA
&& f
!= GL_DEPTH_COMPONENT
) {
393 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
;
397 else if (att
->Type
== GL_RENDERBUFFER_EXT
) {
398 w
= att
->Renderbuffer
->Width
;
399 h
= att
->Renderbuffer
->Height
;
400 f
= att
->Renderbuffer
->InternalFormat
;
404 assert(att
->Type
== GL_NONE
);
408 if (numImages
== 1) {
409 /* set required width, height and format */
416 /* check that width, height, format are same */
417 if (w
!= width
|| h
!= height
) {
418 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
;
421 if (intFormat
!= GL_NONE
&& f
!= intFormat
) {
422 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
;
428 /* Check that all DrawBuffers are present */
429 for (i
= 0; i
< ctx
->Const
.MaxDrawBuffers
; i
++) {
430 if (fb
->ColorDrawBuffer
[i
] != GL_NONE
) {
431 const struct gl_renderbuffer_attachment
*att
432 = get_attachment(ctx
, fb
, fb
->ColorDrawBuffer
[i
]);
434 if (att
->Type
== GL_NONE
) {
435 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT
;
441 /* Check that the ReadBuffer is present */
442 if (fb
->ColorReadBuffer
!= GL_NONE
) {
443 const struct gl_renderbuffer_attachment
*att
444 = get_attachment(ctx
, fb
, fb
->ColorReadBuffer
);
446 if (att
->Type
== GL_NONE
) {
447 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT
;
452 /* Check if any renderbuffer is attached more than once */
453 for (i
= 0; i
< BUFFER_COUNT
- 1; i
++) {
454 struct gl_renderbuffer
*rb_i
= fb
->Attachment
[i
].Renderbuffer
;
457 for (j
= i
+ 1; j
< BUFFER_COUNT
; j
++) {
458 struct gl_renderbuffer
*rb_j
= fb
->Attachment
[j
].Renderbuffer
;
460 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
;
468 if (numImages
== 0) {
469 fb
->_Status
= GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
;
474 * If we get here, the framebuffer is complete!
476 fb
->_Status
= GL_FRAMEBUFFER_COMPLETE_EXT
;
483 _mesa_IsRenderbufferEXT(GLuint renderbuffer
)
485 GET_CURRENT_CONTEXT(ctx
);
486 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, GL_FALSE
);
488 struct gl_renderbuffer
*rb
= lookup_renderbuffer(ctx
, renderbuffer
);
489 if (rb
!= NULL
&& rb
!= &DummyRenderbuffer
)
497 _mesa_BindRenderbufferEXT(GLenum target
, GLuint renderbuffer
)
499 struct gl_renderbuffer
*newRb
, *oldRb
;
500 GET_CURRENT_CONTEXT(ctx
);
502 ASSERT_OUTSIDE_BEGIN_END(ctx
);
504 if (target
!= GL_RENDERBUFFER_EXT
) {
505 _mesa_error(ctx
, GL_INVALID_ENUM
,
506 "glBindRenderbufferEXT(target)");
510 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
513 newRb
= lookup_renderbuffer(ctx
, renderbuffer
);
514 if (newRb
== &DummyRenderbuffer
) {
515 /* ID was reserved, but no real renderbuffer object made yet */
519 /* create new renderbuffer object */
520 newRb
= ctx
->Driver
.NewRenderbuffer(ctx
, renderbuffer
);
522 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glBindRenderbufferEXT");
525 ASSERT(newRb
->AllocStorage
);
526 _mesa_HashInsert(ctx
->Shared
->RenderBuffers
, renderbuffer
, newRb
);
534 oldRb
= ctx
->CurrentRenderbuffer
;
537 if (oldRb
->RefCount
== 0) {
538 oldRb
->Delete(oldRb
);
542 ASSERT(newRb
!= &DummyRenderbuffer
);
544 ctx
->CurrentRenderbuffer
= newRb
;
549 _mesa_DeleteRenderbuffersEXT(GLsizei n
, const GLuint
*renderbuffers
)
552 GET_CURRENT_CONTEXT(ctx
);
554 ASSERT_OUTSIDE_BEGIN_END(ctx
);
555 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
557 for (i
= 0; i
< n
; i
++) {
558 if (renderbuffers
[i
] > 0) {
559 struct gl_renderbuffer
*rb
;
560 rb
= lookup_renderbuffer(ctx
, renderbuffers
[i
]);
562 /* check if deleting currently bound renderbuffer object */
563 if (rb
== ctx
->CurrentRenderbuffer
) {
565 ASSERT(rb
->RefCount
>= 2);
566 _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT
, 0);
569 /* remove from hash table immediately, to free the ID */
570 _mesa_HashRemove(ctx
->Shared
->RenderBuffers
, renderbuffers
[i
]);
572 if (rb
!= &DummyRenderbuffer
) {
573 /* But the object will not be freed until it's no longer
574 * bound in any context.
577 if (rb
->RefCount
== 0) {
588 _mesa_GenRenderbuffersEXT(GLsizei n
, GLuint
*renderbuffers
)
590 GET_CURRENT_CONTEXT(ctx
);
594 ASSERT_OUTSIDE_BEGIN_END(ctx
);
597 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenRenderbuffersEXT(n)");
604 first
= _mesa_HashFindFreeKeyBlock(ctx
->Shared
->RenderBuffers
, n
);
606 for (i
= 0; i
< n
; i
++) {
607 GLuint name
= first
+ i
;
608 renderbuffers
[i
] = name
;
609 /* insert dummy placeholder into hash table */
610 _glthread_LOCK_MUTEX(ctx
->Shared
->Mutex
);
611 _mesa_HashInsert(ctx
->Shared
->RenderBuffers
, name
, &DummyRenderbuffer
);
612 _glthread_UNLOCK_MUTEX(ctx
->Shared
->Mutex
);
618 * Given an internal format token for a render buffer, return the
619 * corresponding base format.
620 * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
624 base_internal_format(GLcontext
*ctx
, GLenum internalFormat
)
626 switch (internalFormat
) {
645 case GL_STENCIL_INDEX
:
646 case GL_STENCIL_INDEX1_EXT
:
647 case GL_STENCIL_INDEX4_EXT
:
648 case GL_STENCIL_INDEX8_EXT
:
649 case GL_STENCIL_INDEX16_EXT
:
650 return GL_STENCIL_INDEX
;
651 case GL_DEPTH_COMPONENT
:
652 case GL_DEPTH_COMPONENT16
:
653 case GL_DEPTH_COMPONENT24
:
654 case GL_DEPTH_COMPONENT32
:
655 return GL_DEPTH_COMPONENT
;
656 /* XXX add floating point formats eventually */
664 _mesa_RenderbufferStorageEXT(GLenum target
, GLenum internalFormat
,
665 GLsizei width
, GLsizei height
)
667 struct gl_renderbuffer
*rb
;
669 GET_CURRENT_CONTEXT(ctx
);
671 ASSERT_OUTSIDE_BEGIN_END(ctx
);
673 if (target
!= GL_RENDERBUFFER_EXT
) {
674 _mesa_error(ctx
, GL_INVALID_ENUM
, "glRenderbufferStorageEXT(target)");
678 baseFormat
= base_internal_format(ctx
, internalFormat
);
679 if (baseFormat
== 0) {
680 _mesa_error(ctx
, GL_INVALID_ENUM
,
681 "glRenderbufferStorageEXT(internalFormat)");
685 if (width
< 1 || width
> ctx
->Const
.MaxRenderbufferSize
) {
686 _mesa_error(ctx
, GL_INVALID_VALUE
, "glRenderbufferStorageEXT(width)");
690 if (height
< 1 || height
> ctx
->Const
.MaxRenderbufferSize
) {
691 _mesa_error(ctx
, GL_INVALID_VALUE
, "glRenderbufferStorageEXT(height)");
695 rb
= ctx
->CurrentRenderbuffer
;
698 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glRenderbufferStorageEXT");
702 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
704 /* Now allocate the storage */
705 ASSERT(rb
->AllocStorage
);
706 if (rb
->AllocStorage(ctx
, rb
, internalFormat
, width
, height
)) {
707 /* No error - check/set fields now */
708 assert(rb
->Width
== width
);
709 assert(rb
->Height
== height
);
710 assert(rb
->InternalFormat
);
711 rb
->_BaseFormat
= baseFormat
;
714 /* Probably ran out of memory - clear the fields */
717 rb
->InternalFormat
= GL_NONE
;
718 rb
->_BaseFormat
= GL_NONE
;
722 test_framebuffer_completeness(ctx, fb);
724 /* XXX if this renderbuffer is attached anywhere, invalidate attachment
731 _mesa_GetRenderbufferParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
733 GET_CURRENT_CONTEXT(ctx
);
735 ASSERT_OUTSIDE_BEGIN_END(ctx
);
737 if (target
!= GL_RENDERBUFFER_EXT
) {
738 _mesa_error(ctx
, GL_INVALID_ENUM
,
739 "glGetRenderbufferParameterivEXT(target)");
743 if (!ctx
->CurrentRenderbuffer
) {
744 _mesa_error(ctx
, GL_INVALID_OPERATION
,
745 "glGetRenderbufferParameterivEXT");
749 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
752 case GL_RENDERBUFFER_WIDTH_EXT
:
753 *params
= ctx
->CurrentRenderbuffer
->Width
;
755 case GL_RENDERBUFFER_HEIGHT_EXT
:
756 *params
= ctx
->CurrentRenderbuffer
->Height
;
758 case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT
:
759 *params
= ctx
->CurrentRenderbuffer
->InternalFormat
;
761 case GL_RENDERBUFFER_RED_SIZE_EXT
:
762 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGB
||
763 ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGBA
) {
764 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[0];
770 case GL_RENDERBUFFER_GREEN_SIZE_EXT
:
771 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGB
||
772 ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGBA
) {
773 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[1];
779 case GL_RENDERBUFFER_BLUE_SIZE_EXT
:
780 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGB
||
781 ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGBA
) {
782 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[2];
788 case GL_RENDERBUFFER_ALPHA_SIZE_EXT
:
789 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGB
||
790 ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_RGBA
) {
791 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[3];
797 case GL_RENDERBUFFER_DEPTH_SIZE_EXT
:
798 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_DEPTH_COMPONENT
) {
799 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[0];
805 case GL_RENDERBUFFER_STENCIL_SIZE_EXT
:
806 if (ctx
->CurrentRenderbuffer
->_BaseFormat
== GL_STENCIL_INDEX
) {
807 *params
= ctx
->CurrentRenderbuffer
->ComponentSizes
[0];
815 _mesa_error(ctx
, GL_INVALID_ENUM
,
816 "glGetRenderbufferParameterivEXT(target)");
823 _mesa_IsFramebufferEXT(GLuint framebuffer
)
825 GET_CURRENT_CONTEXT(ctx
);
826 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, GL_FALSE
);
828 struct gl_framebuffer
*rb
= lookup_framebuffer(ctx
, framebuffer
);
829 if (rb
!= NULL
&& rb
!= &DummyFramebuffer
)
837 _mesa_BindFramebufferEXT(GLenum target
, GLuint framebuffer
)
839 struct gl_framebuffer
*newFb
, *newReadFb
, *oldFb
;
840 GET_CURRENT_CONTEXT(ctx
);
842 ASSERT_OUTSIDE_BEGIN_END(ctx
);
844 if (target
!= GL_FRAMEBUFFER_EXT
) {
845 _mesa_error(ctx
, GL_INVALID_ENUM
,
846 "glBindFramebufferEXT(target)");
850 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
853 /* Binding a user-created framebuffer object */
854 newFb
= lookup_framebuffer(ctx
, framebuffer
);
855 if (newFb
== &DummyFramebuffer
) {
856 /* ID was reserved, but no real framebuffer object made yet */
860 /* create new framebuffer object */
861 newFb
= ctx
->Driver
.NewFramebuffer(ctx
, framebuffer
);
863 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glBindFramebufferEXT");
866 _mesa_HashInsert(ctx
->Shared
->FrameBuffers
, framebuffer
, newFb
);
872 /* Binding the window system framebuffer (which was originally set
875 newFb
= ctx
->WinSysDrawBuffer
;
876 newReadFb
= ctx
->WinSysReadBuffer
;
879 oldFb
= ctx
->DrawBuffer
;
880 if (oldFb
&& oldFb
->Name
!= 0) {
882 if (oldFb
->RefCount
== 0) {
883 oldFb
->Delete(oldFb
);
887 ASSERT(newFb
!= &DummyFramebuffer
);
889 /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
890 ctx
->DrawBuffer
= newFb
;
891 ctx
->ReadBuffer
= newReadFb
;
896 _mesa_DeleteFramebuffersEXT(GLsizei n
, const GLuint
*framebuffers
)
899 GET_CURRENT_CONTEXT(ctx
);
901 ASSERT_OUTSIDE_BEGIN_END(ctx
);
902 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
904 for (i
= 0; i
< n
; i
++) {
905 if (framebuffers
[i
] > 0) {
906 struct gl_framebuffer
*fb
;
907 fb
= lookup_framebuffer(ctx
, framebuffers
[i
]);
909 ASSERT(fb
== &DummyFramebuffer
|| fb
->Name
== framebuffers
[i
]);
911 /* check if deleting currently bound framebuffer object */
912 if (fb
== ctx
->DrawBuffer
) {
914 ASSERT(fb
->RefCount
>= 2);
915 _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
918 /* remove from hash table immediately, to free the ID */
919 _mesa_HashRemove(ctx
->Shared
->FrameBuffers
, framebuffers
[i
]);
921 if (fb
!= &DummyFramebuffer
) {
922 /* But the object will not be freed until it's no longer
923 * bound in any context.
926 if (fb
->RefCount
== 0) {
937 _mesa_GenFramebuffersEXT(GLsizei n
, GLuint
*framebuffers
)
939 GET_CURRENT_CONTEXT(ctx
);
943 ASSERT_OUTSIDE_BEGIN_END(ctx
);
946 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenFramebuffersEXT(n)");
953 first
= _mesa_HashFindFreeKeyBlock(ctx
->Shared
->FrameBuffers
, n
);
955 for (i
= 0; i
< n
; i
++) {
956 GLuint name
= first
+ i
;
957 framebuffers
[i
] = name
;
958 /* insert dummy placeholder into hash table */
959 _glthread_LOCK_MUTEX(ctx
->Shared
->Mutex
);
960 _mesa_HashInsert(ctx
->Shared
->FrameBuffers
, name
, &DummyFramebuffer
);
961 _glthread_UNLOCK_MUTEX(ctx
->Shared
->Mutex
);
968 _mesa_CheckFramebufferStatusEXT(GLenum target
)
970 GET_CURRENT_CONTEXT(ctx
);
972 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, 0);
974 if (target
!= GL_FRAMEBUFFER_EXT
) {
975 _mesa_error(ctx
, GL_INVALID_ENUM
, "glCheckFramebufferStatus(target)");
976 return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
979 if (ctx
->DrawBuffer
->Name
== 0) {
980 /* The window system / default framebuffer is always complete */
981 return GL_FRAMEBUFFER_COMPLETE_EXT
;
984 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
986 _mesa_test_framebuffer_completeness(ctx
, ctx
->DrawBuffer
);
987 return ctx
->DrawBuffer
->_Status
;
993 * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
994 * \return GL_TRUE if any error, GL_FALSE otherwise
997 error_check_framebuffer_texture(GLcontext
*ctx
, GLuint dims
,
998 GLenum target
, GLenum attachment
,
999 GLenum textarget
, GLuint texture
, GLint level
)
1001 ASSERT(dims
>= 1 && dims
<= 3);
1003 if (target
!= GL_FRAMEBUFFER_EXT
) {
1004 _mesa_error(ctx
, GL_INVALID_ENUM
,
1005 "glFramebufferTexture%dDEXT(target)", dims
);
1009 /* check framebuffer binding */
1010 if (ctx
->DrawBuffer
->Name
== 0) {
1011 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1012 "glFramebufferTexture%dDEXT", dims
);
1016 /* only check textarget, level if texture ID is non-zero */
1018 if ((dims
== 1 && textarget
!= GL_TEXTURE_1D
) ||
1019 (dims
== 3 && textarget
!= GL_TEXTURE_3D
) ||
1020 (dims
== 2 && textarget
!= GL_TEXTURE_2D
&&
1021 textarget
!= GL_TEXTURE_RECTANGLE_ARB
&&
1022 !IS_CUBE_FACE(textarget
))) {
1023 _mesa_error(ctx
, GL_INVALID_VALUE
,
1024 "glFramebufferTexture%dDEXT(textarget)", dims
);
1028 if ((level
< 0) || level
>= _mesa_max_texture_levels(ctx
, textarget
)) {
1029 _mesa_error(ctx
, GL_INVALID_VALUE
,
1030 "glFramebufferTexture%dDEXT(level)", dims
);
1040 _mesa_FramebufferTexture1DEXT(GLenum target
, GLenum attachment
,
1041 GLenum textarget
, GLuint texture
, GLint level
)
1043 struct gl_renderbuffer_attachment
*att
;
1044 struct gl_texture_object
*texObj
;
1045 GET_CURRENT_CONTEXT(ctx
);
1047 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1049 if (error_check_framebuffer_texture(ctx
, 1, target
, attachment
,
1050 textarget
, texture
, level
))
1053 ASSERT(textarget
== GL_TEXTURE_1D
);
1055 att
= get_attachment(ctx
, ctx
->DrawBuffer
, attachment
);
1057 _mesa_error(ctx
, GL_INVALID_ENUM
,
1058 "glFramebufferTexture1DEXT(attachment)");
1062 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1065 texObj
= (struct gl_texture_object
*)
1066 _mesa_HashLookup(ctx
->Shared
->TexObjects
, texture
);
1068 _mesa_error(ctx
, GL_INVALID_VALUE
,
1069 "glFramebufferTexture1DEXT(texture)");
1072 if (texObj
->Target
!= textarget
) {
1073 _mesa_error(ctx
, GL_INVALID_OPERATION
, /* XXX correct error? */
1074 "glFramebufferTexture1DEXT(texture target)");
1079 /* remove texture attachment */
1082 ctx
->Driver
.RenderbufferTexture(ctx
, att
, texObj
, textarget
, level
, 0);
1087 _mesa_FramebufferTexture2DEXT(GLenum target
, GLenum attachment
,
1088 GLenum textarget
, GLuint texture
, GLint level
)
1090 struct gl_renderbuffer_attachment
*att
;
1091 struct gl_texture_object
*texObj
;
1092 GET_CURRENT_CONTEXT(ctx
);
1094 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1096 if (error_check_framebuffer_texture(ctx
, 2, target
, attachment
,
1097 textarget
, texture
, level
))
1100 ASSERT(textarget
== GL_TEXTURE_2D
||
1101 textarget
== GL_TEXTURE_RECTANGLE_ARB
||
1102 IS_CUBE_FACE(textarget
));
1104 att
= get_attachment(ctx
, ctx
->DrawBuffer
, attachment
);
1106 _mesa_error(ctx
, GL_INVALID_ENUM
,
1107 "glFramebufferTexture2DEXT(attachment)");
1111 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1114 texObj
= (struct gl_texture_object
*)
1115 _mesa_HashLookup(ctx
->Shared
->TexObjects
, texture
);
1117 _mesa_error(ctx
, GL_INVALID_VALUE
,
1118 "glFramebufferTexture2DEXT(texture)");
1121 if ((texObj
->Target
== GL_TEXTURE_2D
&& textarget
!= GL_TEXTURE_2D
) ||
1122 (texObj
->Target
== GL_TEXTURE_RECTANGLE_ARB
1123 && textarget
!= GL_TEXTURE_RECTANGLE_ARB
) ||
1124 (texObj
->Target
== GL_TEXTURE_CUBE_MAP
1125 && !IS_CUBE_FACE(textarget
))) {
1126 _mesa_error(ctx
, GL_INVALID_OPERATION
, /* XXX correct error? */
1127 "glFramebufferTexture2DEXT(texture target)");
1132 /* remove texture attachment */
1135 ctx
->Driver
.RenderbufferTexture(ctx
, att
, texObj
, textarget
, level
, 0);
1140 _mesa_FramebufferTexture3DEXT(GLenum target
, GLenum attachment
,
1141 GLenum textarget
, GLuint texture
,
1142 GLint level
, GLint zoffset
)
1144 struct gl_renderbuffer_attachment
*att
;
1145 struct gl_texture_object
*texObj
;
1146 GET_CURRENT_CONTEXT(ctx
);
1148 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1150 if (error_check_framebuffer_texture(ctx
, 3, target
, attachment
,
1151 textarget
, texture
, level
))
1154 ASSERT(textarget
== GL_TEXTURE_3D
);
1156 att
= get_attachment(ctx
, ctx
->DrawBuffer
, attachment
);
1158 _mesa_error(ctx
, GL_INVALID_ENUM
,
1159 "glFramebufferTexture1DEXT(attachment)");
1163 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1166 const GLint maxSize
= 1 << (ctx
->Const
.Max3DTextureLevels
- 1);
1167 texObj
= (struct gl_texture_object
*)
1168 _mesa_HashLookup(ctx
->Shared
->TexObjects
, texture
);
1170 _mesa_error(ctx
, GL_INVALID_VALUE
,
1171 "glFramebufferTexture3DEXT(texture)");
1174 if (texObj
->Target
!= textarget
) {
1175 _mesa_error(ctx
, GL_INVALID_OPERATION
, /* XXX correct error? */
1176 "glFramebufferTexture3DEXT(texture target)");
1179 if (zoffset
< 0 || zoffset
>= maxSize
) {
1180 _mesa_error(ctx
, GL_INVALID_VALUE
,
1181 "glFramebufferTexture3DEXT(zoffset)");
1186 /* remove texture attachment */
1189 ctx
->Driver
.RenderbufferTexture(ctx
, att
, texObj
, textarget
,
1195 _mesa_FramebufferRenderbufferEXT(GLenum target
, GLenum attachment
,
1196 GLenum renderbufferTarget
,
1197 GLuint renderbuffer
)
1199 struct gl_renderbuffer_attachment
*att
;
1200 struct gl_renderbuffer
*rb
;
1201 GET_CURRENT_CONTEXT(ctx
);
1203 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1205 if (target
!= GL_FRAMEBUFFER_EXT
) {
1206 _mesa_error(ctx
, GL_INVALID_ENUM
,
1207 "glFramebufferRenderbufferEXT(target)");
1211 if (renderbufferTarget
!= GL_RENDERBUFFER_EXT
) {
1212 _mesa_error(ctx
, GL_INVALID_ENUM
,
1213 "glFramebufferRenderbufferEXT(renderbufferTarget)");
1217 if (ctx
->DrawBuffer
->Name
== 0) {
1218 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glFramebufferRenderbufferEXT");
1222 att
= get_attachment(ctx
, ctx
->DrawBuffer
, attachment
);
1224 _mesa_error(ctx
, GL_INVALID_ENUM
,
1225 "glFramebufferRenderbufferEXT(attachment)");
1230 rb
= lookup_renderbuffer(ctx
, renderbuffer
);
1232 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1233 "glFramebufferRenderbufferEXT(renderbuffer)");
1238 /* remove renderbuffer attachment */
1242 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1244 assert(ctx
->Driver
.FramebufferRenderbuffer
);
1245 ctx
->Driver
.FramebufferRenderbuffer(ctx
, att
, rb
);
1250 _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target
, GLenum attachment
,
1251 GLenum pname
, GLint
*params
)
1253 const struct gl_renderbuffer_attachment
*att
;
1254 GET_CURRENT_CONTEXT(ctx
);
1256 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1258 if (target
!= GL_FRAMEBUFFER_EXT
) {
1259 _mesa_error(ctx
, GL_INVALID_ENUM
,
1260 "glGetFramebufferAttachmentParameterivEXT(target)");
1264 if (ctx
->DrawBuffer
->Name
== 0) {
1265 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1266 "glGetFramebufferAttachmentParameterivEXT");
1270 att
= get_attachment(ctx
, ctx
->DrawBuffer
, attachment
);
1272 _mesa_error(ctx
, GL_INVALID_ENUM
,
1273 "glGetFramebufferAttachmentParameterivEXT(attachment)");
1277 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1280 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT
:
1281 *params
= att
->Type
;
1283 case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT
:
1284 if (att
->Type
== GL_RENDERBUFFER_EXT
) {
1285 *params
= att
->Renderbuffer
->Name
;
1287 else if (att
->Type
== GL_TEXTURE
) {
1288 *params
= att
->Texture
->Name
;
1291 _mesa_error(ctx
, GL_INVALID_ENUM
,
1292 "glGetFramebufferAttachmentParameterivEXT(pname)");
1295 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT
:
1296 if (att
->Type
== GL_TEXTURE
) {
1297 *params
= att
->TextureLevel
;
1300 _mesa_error(ctx
, GL_INVALID_ENUM
,
1301 "glGetFramebufferAttachmentParameterivEXT(pname)");
1304 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT
:
1305 if (att
->Type
== GL_TEXTURE
) {
1306 *params
= GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ att
->CubeMapFace
;
1309 _mesa_error(ctx
, GL_INVALID_ENUM
,
1310 "glGetFramebufferAttachmentParameterivEXT(pname)");
1313 case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT
:
1314 if (att
->Type
== GL_TEXTURE
) {
1315 *params
= att
->Zoffset
;
1318 _mesa_error(ctx
, GL_INVALID_ENUM
,
1319 "glGetFramebufferAttachmentParameterivEXT(pname)");
1323 _mesa_error(ctx
, GL_INVALID_ENUM
,
1324 "glGetFramebufferAttachmentParameterivEXT(pname)");
1331 _mesa_GenerateMipmapEXT(GLenum target
)
1333 struct gl_texture_unit
*texUnit
;
1334 struct gl_texture_object
*texObj
;
1335 GET_CURRENT_CONTEXT(ctx
);
1337 ASSERT_OUTSIDE_BEGIN_END(ctx
);
1338 FLUSH_VERTICES(ctx
, _NEW_BUFFERS
);
1344 case GL_TEXTURE_CUBE_MAP
:
1345 /* OK, legal value */
1348 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGenerateMipmapEXT(target)");
1352 texUnit
= &ctx
->Texture
.Unit
[ctx
->Texture
.CurrentUnit
];
1353 texObj
= _mesa_select_tex_object(ctx
, texUnit
, target
);
1355 /* XXX this might not handle cube maps correctly */
1356 _mesa_generate_mipmap(ctx
, target
, texUnit
, texObj
);