X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=faf3763b716dde4324c479bccc98b28ab6469a1b;hb=989edea40913de9347908488db8978eb2efaba63;hp=f41f464e3d1c18fcfea610eebefa5cd72072fa66;hpb=676d0accf5cc43e86057b14cfb8bba9316932582;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index f41f464e3d1..faf3763b716 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -1,8 +1,9 @@ /* * Mesa 3-D graphics library - * Version: 6.3 + * Version: 7.1 * - * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * Copyright (C) 1999-2009 VMware, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -24,17 +25,24 @@ /* + * GL_EXT/ARB_framebuffer_object extensions + * * Authors: * Brian Paul */ +#include "buffers.h" #include "context.h" #include "fbobject.h" #include "framebuffer.h" #include "hash.h" +#include "macros.h" +#include "mipmap.h" #include "renderbuffer.h" +#include "state.h" #include "teximage.h" +#include "texobj.h" #include "texstore.h" @@ -62,11 +70,32 @@ static struct gl_renderbuffer DummyRenderbuffer; (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) +static void +delete_dummy_renderbuffer(struct gl_renderbuffer *rb) +{ + /* no op */ +} + +static void +delete_dummy_framebuffer(struct gl_framebuffer *fb) +{ + /* no op */ +} + + +void +_mesa_init_fbobjects(GLcontext *ctx) +{ + DummyFramebuffer.Delete = delete_dummy_framebuffer; + DummyRenderbuffer.Delete = delete_dummy_renderbuffer; +} + + /** * Helper routine for getting a gl_renderbuffer. */ -static struct gl_renderbuffer * -lookup_renderbuffer(GLcontext *ctx, GLuint id) +struct gl_renderbuffer * +_mesa_lookup_renderbuffer(GLcontext *ctx, GLuint id) { struct gl_renderbuffer *rb; @@ -82,8 +111,8 @@ lookup_renderbuffer(GLcontext *ctx, GLuint id) /** * Helper routine for getting a gl_framebuffer. */ -static struct gl_framebuffer * -lookup_framebuffer(GLcontext *ctx, GLuint id) +struct gl_framebuffer * +_mesa_lookup_framebuffer(GLcontext *ctx, GLuint id) { struct gl_framebuffer *fb; @@ -100,8 +129,9 @@ lookup_framebuffer(GLcontext *ctx, GLuint id) * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding * gl_renderbuffer_attachment object. */ -static struct gl_renderbuffer_attachment * -get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, GLenum attachment) +struct gl_renderbuffer_attachment * +_mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, + GLenum attachment) { GLuint i; @@ -146,26 +176,17 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att) { if (att->Type == GL_TEXTURE) { ASSERT(att->Texture); - if (att->Renderbuffer) { - /* delete/remove the 'wrapper' renderbuffer */ - /* XXX do we really want to do this??? */ - att->Renderbuffer->Delete(att->Renderbuffer); - att->Renderbuffer = NULL; + if (ctx->Driver.FinishRenderTexture) { + /* tell driver we're done rendering to this texobj */ + ctx->Driver.FinishRenderTexture(ctx, att); } - att->Texture->RefCount--; - if (att->Texture->RefCount == 0) { - ctx->Driver.DeleteTexture(ctx, att->Texture); - } - att->Texture = NULL; + _mesa_reference_texobj(&att->Texture, NULL); /* unbind */ + ASSERT(!att->Texture); } - else if (att->Type == GL_RENDERBUFFER_EXT) { - ASSERT(att->Renderbuffer); + if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) { ASSERT(!att->Texture); - att->Renderbuffer->RefCount--; - if (att->Renderbuffer->RefCount == 0) { - att->Renderbuffer->Delete(att->Renderbuffer); - } - att->Renderbuffer = NULL; + _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); /* unbind */ + ASSERT(!att->Renderbuffer); } att->Type = GL_NONE; att->Complete = GL_TRUE; @@ -178,13 +199,24 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att) */ void _mesa_set_texture_attachment(GLcontext *ctx, + struct gl_framebuffer *fb, struct gl_renderbuffer_attachment *att, struct gl_texture_object *texObj, GLenum texTarget, GLuint level, GLuint zoffset) { - _mesa_remove_attachment(ctx, att); - att->Type = GL_TEXTURE; - att->Texture = texObj; + if (att->Texture == texObj) { + /* re-attaching same texture */ + ASSERT(att->Type == GL_TEXTURE); + } + else { + /* new attachment */ + _mesa_remove_attachment(ctx, att); + att->Type = GL_TEXTURE; + assert(!att->Texture); + _mesa_reference_texobj(&att->Texture, texObj); + } + + /* always update these fields */ att->TextureLevel = level; if (IS_CUBE_FACE(texTarget)) { att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X; @@ -195,12 +227,9 @@ _mesa_set_texture_attachment(GLcontext *ctx, att->Zoffset = zoffset; att->Complete = GL_FALSE; - texObj->RefCount++; - - /* XXX when we attach to a texture, we should probably set the - * att->Renderbuffer pointer to a "wrapper renderbuffer" which - * makes the texture image look like renderbuffer. - */ + if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) { + ctx->Driver.RenderTexture(ctx, fb, att); + } } @@ -213,32 +242,37 @@ _mesa_set_renderbuffer_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att, struct gl_renderbuffer *rb) { + /* XXX check if re-doing same attachment, exit early */ _mesa_remove_attachment(ctx, att); att->Type = GL_RENDERBUFFER_EXT; - att->Renderbuffer = rb; att->Texture = NULL; /* just to be safe */ att->Complete = GL_FALSE; - rb->RefCount++; + _mesa_reference_renderbuffer(&att->Renderbuffer, rb); } /** * Fallback for ctx->Driver.FramebufferRenderbuffer() - * Sets a framebuffer attachment to a particular renderbuffer. - * The framebuffer in question is ctx->DrawBuffer. - * \sa _mesa_renderbuffer_texture + * Attach a renderbuffer object to a framebuffer object. */ void -_mesa_framebuffer_renderbuffer(GLcontext *ctx, - struct gl_renderbuffer_attachment *att, - struct gl_renderbuffer *rb) +_mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb, + GLenum attachment, struct gl_renderbuffer *rb) { + struct gl_renderbuffer_attachment *att; + + _glthread_LOCK_MUTEX(fb->Mutex); + + att = _mesa_get_attachment(ctx, fb, attachment); + ASSERT(att); if (rb) { _mesa_set_renderbuffer_attachment(ctx, att, rb); } else { _mesa_remove_attachment(ctx, att); } + + _glthread_UNLOCK_MUTEX(fb->Mutex); } @@ -289,7 +323,14 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format, } } else if (format == GL_DEPTH) { - if (texImage->TexFormat->BaseFormat != GL_DEPTH_COMPONENT) { + if (texImage->TexFormat->BaseFormat == GL_DEPTH_COMPONENT) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + texImage->TexFormat->BaseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { att->Complete = GL_FALSE; return; } @@ -301,26 +342,48 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format, } } else if (att->Type == GL_RENDERBUFFER_EXT) { - if (att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) { + ASSERT(att->Renderbuffer); + if (!att->Renderbuffer->InternalFormat || + att->Renderbuffer->Width < 1 || + att->Renderbuffer->Height < 1) { att->Complete = GL_FALSE; return; } if (format == GL_COLOR) { if (att->Renderbuffer->_BaseFormat != GL_RGB && att->Renderbuffer->_BaseFormat != GL_RGBA) { + ASSERT(att->Renderbuffer->RedBits); + ASSERT(att->Renderbuffer->GreenBits); + ASSERT(att->Renderbuffer->BlueBits); att->Complete = GL_FALSE; return; } } else if (format == GL_DEPTH) { - if (att->Renderbuffer->_BaseFormat != GL_DEPTH_COMPONENT) { + ASSERT(att->Renderbuffer->DepthBits); + if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { att->Complete = GL_FALSE; return; } } else { assert(format == GL_STENCIL); - if (att->Renderbuffer->_BaseFormat != GL_STENCIL_INDEX) { + ASSERT(att->Renderbuffer->StencilBits); + if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { att->Complete = GL_FALSE; return; } @@ -334,6 +397,20 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format, } +/** + * Helpful for debugging + */ +static void +fbo_incomplete(const char *msg, int index) +{ + (void) msg; + (void) index; + /* + _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index); + */ +} + + /** * Test if the given framebuffer object is complete and update its * Status field with the results. @@ -343,10 +420,11 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format, void _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) { - GLuint numImages, width = 0, height = 0; - GLenum intFormat = GL_NONE; - GLuint w = 0, h = 0; + GLuint numImages; + GLenum intFormat = GL_NONE; /* color buffers' internal format */ + GLuint minWidth = ~0, minHeight = ~0, maxWidth = 0, maxHeight = 0; GLint i; + GLuint j; assert(fb->Name != 0); @@ -354,16 +432,23 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) fb->Width = 0; fb->Height = 0; - /* Start at -2 to more easily loop over all attachment points */ + /* Start at -2 to more easily loop over all attachment points. + * -2: depth buffer + * -1: stencil buffer + * >=0: color buffer + */ for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) { struct gl_renderbuffer_attachment *att; GLenum f; + /* check for attachment completeness + */ if (i == -2) { att = &fb->Attachment[BUFFER_DEPTH]; test_attachment_completeness(ctx, GL_DEPTH, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("depth attachment incomplete", -1); return; } } @@ -372,6 +457,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) test_attachment_completeness(ctx, GL_STENCIL, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("stencil attachment incomplete", -1); return; } } @@ -380,23 +466,34 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) test_attachment_completeness(ctx, GL_COLOR, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("color attachment incomplete", i); return; } } + /* get width, height, format of the renderbuffer/texture + */ if (att->Type == GL_TEXTURE) { - w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width; - h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height; - f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format; + const struct gl_texture_image *texImg + = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + minWidth = MIN2(minWidth, texImg->Width); + maxWidth = MAX2(maxWidth, texImg->Width); + minHeight = MIN2(minHeight, texImg->Height); + maxHeight = MAX2(maxHeight, texImg->Height); + f = texImg->_BaseFormat; numImages++; - if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) { + if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT + && f != GL_DEPTH_STENCIL_EXT) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; + fbo_incomplete("texture attachment incomplete", -1); return; } } else if (att->Type == GL_RENDERBUFFER_EXT) { - w = att->Renderbuffer->Width; - h = att->Renderbuffer->Height; + minWidth = MIN2(minWidth, att->Renderbuffer->Width); + maxWidth = MAX2(minWidth, att->Renderbuffer->Width); + minHeight = MIN2(minHeight, att->Renderbuffer->Height); + maxHeight = MAX2(minHeight, att->Renderbuffer->Height); f = att->Renderbuffer->InternalFormat; numImages++; } @@ -405,34 +502,41 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) continue; } + /* Error-check width, height, format + */ if (numImages == 1) { - /* set required width, height and format */ - width = w; - height = h; + /* save format */ if (i >= 0) intFormat = f; } else { - /* check that width, height, format are same */ - if (w != width || h != height) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; - return; - } - if (intFormat != GL_NONE && f != intFormat) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - return; + if (!ctx->Extensions.ARB_framebuffer_object) { + /* check that width, height, format are same */ + if (minWidth != maxWidth || minHeight != maxHeight) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; + fbo_incomplete("width or height mismatch", -1); + return; + } + /* check that all color buffer have same format */ + if (intFormat != GL_NONE && f != intFormat) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; + fbo_incomplete("format mismatch", -1); + return; + } } } } +#ifndef FEATURE_OES_framebuffer_object /* Check that all DrawBuffers are present */ - for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) { - if (fb->ColorDrawBuffer[i] != GL_NONE) { + for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) { + if (fb->ColorDrawBuffer[j] != GL_NONE) { const struct gl_renderbuffer_attachment *att - = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]); + = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[j]); assert(att); if (att->Type == GL_NONE) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT; + fbo_incomplete("missing drawbuffer", j); return; } } @@ -441,41 +545,31 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb) /* Check that the ReadBuffer is present */ if (fb->ColorReadBuffer != GL_NONE) { const struct gl_renderbuffer_attachment *att - = get_attachment(ctx, fb, fb->ColorReadBuffer); + = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer); assert(att); if (att->Type == GL_NONE) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT; + fbo_incomplete("missing readbuffer", -1); return; } } - - /* Check if any renderbuffer is attached more than once */ - for (i = 0; i < BUFFER_COUNT - 1; i++) { - struct gl_renderbuffer *rb_i = fb->Attachment[i].Renderbuffer; - if (rb_i) { - GLint j; - for (j = i + 1; j < BUFFER_COUNT; j++) { - struct gl_renderbuffer *rb_j = fb->Attachment[j].Renderbuffer; - if (rb_i == rb_j) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT; - return; - } - } - } - } - +#endif if (numImages == 0) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; + fbo_incomplete("no attachments", -1); return; } /* * If we get here, the framebuffer is complete! + * Note that if ARB_framebuffer_object is supported and the attached + * renderbuffers/textures are different sizes, the framebuffer width/height + * will be set to the smallest width/height. */ fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; - fb->Width = w; - fb->Height = h; + fb->Width = minWidth; + fb->Height = minHeight; } @@ -484,21 +578,22 @@ _mesa_IsRenderbufferEXT(GLuint renderbuffer) { GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - if (renderbuffer && lookup_renderbuffer(ctx, renderbuffer)) - return GL_TRUE; - else - return GL_FALSE; + if (renderbuffer) { + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (rb != NULL && rb != &DummyRenderbuffer) + return GL_TRUE; + } + return GL_FALSE; } void GLAPIENTRY _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) { - struct gl_renderbuffer *newRb, *oldRb; + struct gl_renderbuffer *newRb; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); if (target != GL_RENDERBUFFER_EXT) { _mesa_error(ctx, GL_INVALID_ENUM, @@ -506,8 +601,16 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) return; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + /* The above doesn't fully flush the drivers in the way that a + * glFlush does, but that is required here: + */ + if (ctx->Driver.Flush) + ctx->Driver.Flush(ctx); + + if (renderbuffer) { - newRb = lookup_renderbuffer(ctx, renderbuffer); + newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer); if (newRb == &DummyRenderbuffer) { /* ID was reserved, but no real renderbuffer object made yet */ newRb = NULL; @@ -521,24 +624,16 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) } ASSERT(newRb->AllocStorage); _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); + newRb->RefCount = 1; /* referenced by hash table */ } - newRb->RefCount++; } else { newRb = NULL; } - oldRb = ctx->CurrentRenderbuffer; - if (oldRb) { - oldRb->RefCount--; - if (oldRb->RefCount == 0) { - oldRb->Delete(oldRb); - } - } - ASSERT(newRb != &DummyRenderbuffer); - ctx->CurrentRenderbuffer = newRb; + _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb); } @@ -549,23 +644,29 @@ _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers) GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); for (i = 0; i < n; i++) { if (renderbuffers[i] > 0) { struct gl_renderbuffer *rb; - rb = lookup_renderbuffer(ctx, renderbuffers[i]); + rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]); if (rb) { - /* remove from hash table immediately, to free the ID */ + /* check if deleting currently bound renderbuffer object */ + if (rb == ctx->CurrentRenderbuffer) { + /* bind default */ + ASSERT(rb->RefCount >= 2); + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + } + + /* Remove from hash table immediately, to free the ID. + * But the object will not be freed until it's no longer + * referenced anywhere else. + */ _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]); if (rb != &DummyRenderbuffer) { - /* But the object will not be freed until it's no longer - * bound in any context. - */ - rb->RefCount--; - if (rb->RefCount == 0) { - rb->Delete(rb); - } + /* no longer referenced by hash table */ + _mesa_reference_renderbuffer(&rb, NULL); } } } @@ -606,11 +707,14 @@ _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) /** * Given an internal format token for a render buffer, return the * corresponding base format. + * This is very similar to _mesa_base_tex_format() but the set of valid + * internal formats is somewhat different. + * * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT - * or zero if error. + * GL_DEPTH_STENCIL_EXT or zero if error. */ -static GLenum -base_internal_format(GLcontext *ctx, GLenum internalFormat) +GLenum +_mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat) { switch (internalFormat) { case GL_RGB: @@ -642,6 +746,12 @@ base_internal_format(GLcontext *ctx, GLenum internalFormat) case GL_DEPTH_COMPONENT24: case GL_DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT; + case GL_DEPTH_STENCIL_EXT: + case GL_DEPTH24_STENCIL8_EXT: + if (ctx->Extensions.EXT_packed_depth_stencil) + return GL_DEPTH_STENCIL_EXT; + else + return 0; /* XXX add floating point formats eventually */ default: return 0; @@ -658,26 +768,25 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); if (target != GL_RENDERBUFFER_EXT) { _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)"); return; } - baseFormat = base_internal_format(ctx, internalFormat); + baseFormat = _mesa_base_fbo_format(ctx, internalFormat); if (baseFormat == 0) { _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(internalFormat)"); return; } - if (width < 1 || width > ctx->Const.MaxRenderbufferSize) { + if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)"); return; } - if (height < 1 || height > ctx->Const.MaxRenderbufferSize) { + if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { _mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)"); return; } @@ -689,13 +798,35 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, return; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (rb->InternalFormat == internalFormat && + rb->Width == (GLuint) width && + rb->Height == (GLuint) height) { + /* no change in allocation needed */ + return; + } + + /* These MUST get set by the AllocStorage func */ + rb->_ActualFormat = 0; + rb->RedBits = + rb->GreenBits = + rb->BlueBits = + rb->AlphaBits = + rb->IndexBits = + rb->DepthBits = + rb->StencilBits = 0; + /* Now allocate the storage */ ASSERT(rb->AllocStorage); if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { /* No error - check/set fields now */ - assert(rb->Width == width); - assert(rb->Height == height); - assert(rb->InternalFormat); + assert(rb->_ActualFormat); + assert(rb->Width == (GLuint) width); + assert(rb->Height == (GLuint) height); + assert(rb->RedBits || rb->GreenBits || rb->BlueBits || rb->AlphaBits || + rb->DepthBits || rb->StencilBits || rb->IndexBits); + rb->InternalFormat = internalFormat; rb->_BaseFormat = baseFormat; } else { @@ -703,7 +834,15 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, rb->Width = 0; rb->Height = 0; rb->InternalFormat = GL_NONE; + rb->_ActualFormat = GL_NONE; rb->_BaseFormat = GL_NONE; + rb->RedBits = + rb->GreenBits = + rb->BlueBits = + rb->AlphaBits = + rb->IndexBits = + rb->DepthBits = + rb->StencilBits = 0; } /* @@ -734,6 +873,8 @@ _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) return; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + switch (pname) { case GL_RENDERBUFFER_WIDTH_EXT: *params = ctx->CurrentRenderbuffer->Width; @@ -745,58 +886,23 @@ _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) *params = ctx->CurrentRenderbuffer->InternalFormat; return; case GL_RENDERBUFFER_RED_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB || - ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) { - *params = ctx->CurrentRenderbuffer->RedBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->RedBits; break; case GL_RENDERBUFFER_GREEN_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB || - ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) { - *params = ctx->CurrentRenderbuffer->GreenBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->GreenBits; break; case GL_RENDERBUFFER_BLUE_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB || - ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) { - *params = ctx->CurrentRenderbuffer->BlueBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->BlueBits; break; case GL_RENDERBUFFER_ALPHA_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB || - ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) { - *params = ctx->CurrentRenderbuffer->AlphaBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->AlphaBits; break; case GL_RENDERBUFFER_DEPTH_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) { - *params = ctx->CurrentRenderbuffer->DepthBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->DepthBits; break; case GL_RENDERBUFFER_STENCIL_SIZE_EXT: - if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX) { - *params = ctx->CurrentRenderbuffer->StencilBits; - } - else { - *params = 0; - } + *params = ctx->CurrentRenderbuffer->StencilBits; break; - default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetRenderbufferParameterivEXT(target)"); @@ -810,30 +916,103 @@ _mesa_IsFramebufferEXT(GLuint framebuffer) { GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - if (framebuffer && lookup_framebuffer(ctx, framebuffer)) - return GL_TRUE; - else - return GL_FALSE; + if (framebuffer) { + struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer); + if (rb != NULL && rb != &DummyFramebuffer) + return GL_TRUE; + } + return GL_FALSE; +} + + +static void +check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb) +{ + GLuint i; + ASSERT(ctx->Driver.RenderTexture); + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = fb->Attachment + i; + struct gl_texture_object *texObj = att->Texture; + if (texObj + && att->Texture->Image[att->CubeMapFace][att->TextureLevel]) { + ctx->Driver.RenderTexture(ctx, fb, att); + } + } +} + + +/** + * Examine all the framebuffer's attachments to see if any are textures. + * If so, call ctx->Driver.FinishRenderTexture() for each texture to + * notify the device driver that the texture image may have changed. + */ +static void +check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb) +{ + if (ctx->Driver.FinishRenderTexture) { + GLuint i; + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = fb->Attachment + i; + if (att->Texture && att->Renderbuffer) { + ctx->Driver.FinishRenderTexture(ctx, att); + } + } + } } void GLAPIENTRY _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) { - struct gl_framebuffer *newFb, *newReadFb, *oldFb; + struct gl_framebuffer *newFb, *newFbread; + GLboolean bindReadBuf, bindDrawBuf; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - if (target != GL_FRAMEBUFFER_EXT) { + if (!ctx->Extensions.EXT_framebuffer_object) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBindFramebufferEXT(unsupported)"); + return; + } + + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); + return; + } + bindDrawBuf = GL_TRUE; + bindReadBuf = GL_FALSE; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); + return; + } + bindDrawBuf = GL_FALSE; + bindReadBuf = GL_TRUE; + break; +#endif + case GL_FRAMEBUFFER_EXT: + bindDrawBuf = GL_TRUE; + bindReadBuf = GL_TRUE; + break; + default: _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); return; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (ctx->Driver.Flush) { + ctx->Driver.Flush(ctx); + } + if (framebuffer) { /* Binding a user-created framebuffer object */ - newFb = lookup_framebuffer(ctx, framebuffer); + newFb = _mesa_lookup_framebuffer(ctx, framebuffer); if (newFb == &DummyFramebuffer) { /* ID was reserved, but no real framebuffer object made yet */ newFb = NULL; @@ -847,30 +1026,43 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) } _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb); } - newFb->RefCount++; - newReadFb = newFb; + newFbread = newFb; } else { /* Binding the window system framebuffer (which was originally set * with MakeCurrent). */ newFb = ctx->WinSysDrawBuffer; - newReadFb = ctx->WinSysReadBuffer; + newFbread = ctx->WinSysReadBuffer; } - oldFb = ctx->DrawBuffer; - if (oldFb) { /* AND oldFb->Name != 0 */ - oldFb->RefCount--; - if (oldFb->RefCount == 0) { - oldFb->Delete(oldFb); - } + ASSERT(newFb); + ASSERT(newFb != &DummyFramebuffer); + + /* + * XXX check if re-binding same buffer and skip some of this code. + */ + + if (bindReadBuf) { + _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread); } - ASSERT(newFb != &DummyFramebuffer); + if (bindDrawBuf) { + /* check if old FB had any texture attachments */ + check_end_texture_render(ctx, ctx->DrawBuffer); - /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */ - ctx->DrawBuffer = newFb; - ctx->ReadBuffer = newReadFb; + /* check if time to delete this framebuffer */ + _mesa_reference_framebuffer(&ctx->DrawBuffer, newFb); + + if (newFb->Name != 0) { + /* check if newly bound framebuffer has any texture attachments */ + check_begin_texture_render(ctx, newFb); + } + } + + if (ctx->Driver.BindFramebuffer) { + ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread); + } } @@ -881,13 +1073,27 @@ _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + /* The above doesn't fully flush the drivers in the way that a + * glFlush does, but that is required here: + */ + if (ctx->Driver.Flush) + ctx->Driver.Flush(ctx); for (i = 0; i < n; i++) { if (framebuffers[i] > 0) { struct gl_framebuffer *fb; - fb = lookup_framebuffer(ctx, framebuffers[i]); + fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]); if (fb) { ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]); + + /* check if deleting currently bound framebuffer object */ + if (fb == ctx->DrawBuffer) { + /* bind default */ + ASSERT(fb->RefCount >= 2); + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + /* remove from hash table immediately, to free the ID */ _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]); @@ -895,10 +1101,7 @@ _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) /* But the object will not be freed until it's no longer * bound in any context. */ - fb->RefCount--; - if (fb->RefCount == 0) { - fb->Delete(fb); - } + _mesa_unreference_framebuffer(&fb); } } } @@ -940,116 +1143,174 @@ _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers) GLenum GLAPIENTRY _mesa_CheckFramebufferStatusEXT(GLenum target) { + struct gl_framebuffer *buffer; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - if (target != GL_FRAMEBUFFER_EXT) { + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); + return 0; + } + buffer = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); + return 0; + } + buffer = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + buffer = ctx->DrawBuffer; + break; + default: _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */ } - if (ctx->DrawBuffer->Name == 0) { + if (buffer->Name == 0) { /* The window system / default framebuffer is always complete */ return GL_FRAMEBUFFER_COMPLETE_EXT; } - _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer); - return ctx->DrawBuffer->_Status; + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + _mesa_test_framebuffer_completeness(ctx, buffer); + return buffer->_Status; } /** - * Do error checking common to glFramebufferTexture1D/2D/3DEXT. - * \return GL_TRUE if any error, GL_FALSE otherwise + * Common code called by glFramebufferTexture1D/2D/3DEXT(). */ -static GLboolean -error_check_framebuffer_texture(GLcontext *ctx, GLuint dims, - GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) +static void +framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target, + GLenum attachment, GLenum textarget, GLuint texture, + GLint level, GLint zoffset) { - ASSERT(dims >= 1 && dims <= 3); + struct gl_renderbuffer_attachment *att; + struct gl_texture_object *texObj = NULL; + struct gl_framebuffer *fb; + + ASSERT_OUTSIDE_BEGIN_END(ctx); if (target != GL_FRAMEBUFFER_EXT) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%dDEXT(target)", dims); - return GL_TRUE; + "glFramebufferTexture%sEXT(target)", caller); + return; } + fb = ctx->DrawBuffer; + ASSERT(fb); + /* check framebuffer binding */ - if (ctx->DrawBuffer->Name == 0) { + if (fb->Name == 0) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%dDEXT", dims); - return GL_TRUE; + "glFramebufferTexture%sEXT", caller); + return; } - /* only check textarget, level if texture ID is non-zero */ + + /* The textarget, level, and zoffset parameters are only validated if + * texture is non-zero. + */ if (texture) { - if ((dims == 1 && textarget != GL_TEXTURE_1D) || - (dims == 3 && textarget != GL_TEXTURE_3D) || - (dims == 2 && textarget != GL_TEXTURE_2D && - textarget != GL_TEXTURE_RECTANGLE_ARB && - !IS_CUBE_FACE(textarget))) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%dDEXT(textarget)", dims); - return GL_TRUE; + GLboolean err = GL_TRUE; + + texObj = _mesa_lookup_texture(ctx, texture); + if (texObj != NULL) { + if (textarget == 0) { + err = (texObj->Target != GL_TEXTURE_3D) && + (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) && + (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT); + } + else { + err = (texObj->Target == GL_TEXTURE_CUBE_MAP) + ? !IS_CUBE_FACE(textarget) + : (texObj->Target != textarget); + } + } + + if (err) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture%sEXT(texture target mismatch)", + caller); + return; + } + + if (texObj->Target == GL_TEXTURE_3D) { + const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); + if (zoffset < 0 || zoffset >= maxSize) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(zoffset)", caller); + return; + } + } + else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) || + (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT)) { + if (zoffset < 0 || zoffset >= ctx->Const.MaxArrayTextureLayers) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(layer)", caller); + return; + } } - if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%dDEXT(level)", dims); - return GL_TRUE; + + if ((level < 0) || + (level >= _mesa_max_texture_levels(ctx, texObj->Target))) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(level)", caller); + return; } } - return GL_FALSE; + att = _mesa_get_attachment(ctx, fb, attachment); + if (att == NULL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTexture%sEXT(attachment)", caller); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + /* The above doesn't fully flush the drivers in the way that a + * glFlush does, but that is required here: + */ + if (ctx->Driver.Flush) + ctx->Driver.Flush(ctx); + + _glthread_LOCK_MUTEX(fb->Mutex); + if (texObj) { + _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget, + level, zoffset); + } + else { + _mesa_remove_attachment(ctx, att); + } + _glthread_UNLOCK_MUTEX(fb->Mutex); } + void GLAPIENTRY _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */ - - if (error_check_framebuffer_texture(ctx, 1, target, attachment, - textarget, texture, level)) - return; - - ASSERT(textarget == GL_TEXTURE_1D); - - att = get_attachment(ctx, ctx->DrawBuffer, attachment); - if (att == NULL) { + if ((texture != 0) && (textarget != GL_TEXTURE_1D)) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture1DEXT(attachment)"); + "glFramebufferTexture1DEXT(textarget)"); return; } - if (texture) { - texObj = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texture); - if (!texObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture1DEXT(texture)"); - return; - } - if (texObj->Target != textarget) { - _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */ - "glFramebufferTexture1DEXT(texture target)"); - return; - } - } - else { - /* remove texture attachment */ - texObj = NULL; - } - ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0); + framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, + level, 0); } @@ -1057,51 +1318,19 @@ void GLAPIENTRY _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */ - - if (error_check_framebuffer_texture(ctx, 2, target, attachment, - textarget, texture, level)) - return; - - ASSERT(textarget == GL_TEXTURE_2D || - textarget == GL_TEXTURE_RECTANGLE_ARB || - IS_CUBE_FACE(textarget)); - - att = get_attachment(ctx, ctx->DrawBuffer, attachment); - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture2DEXT(attachment)"); + if ((texture != 0) && + (textarget != GL_TEXTURE_2D) && + (textarget != GL_TEXTURE_RECTANGLE_ARB) && + (!IS_CUBE_FACE(textarget))) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture2DEXT(textarget)"); return; } - if (texture) { - texObj = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texture); - if (!texObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture2DEXT(texture)"); - return; - } - if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) || - (texObj->Target == GL_TEXTURE_RECTANGLE_ARB - && textarget != GL_TEXTURE_RECTANGLE_ARB) || - (texObj->Target == GL_TEXTURE_CUBE_MAP - && !IS_CUBE_FACE(textarget))) { - _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */ - "glFramebufferTexture2DEXT(texture target)"); - return; - } - } - else { - /* remove texture attachment */ - texObj = NULL; - } - ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0); + framebuffer_texture(ctx, "2D", target, attachment, textarget, texture, + level, 0); } @@ -1110,52 +1339,27 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */ - - if (error_check_framebuffer_texture(ctx, 3, target, attachment, - textarget, texture, level)) - return; - - ASSERT(textarget == GL_TEXTURE_3D); - - att = get_attachment(ctx, ctx->DrawBuffer, attachment); - if (att == NULL) { + if ((texture != 0) && (textarget != GL_TEXTURE_3D)) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture1DEXT(attachment)"); + "glFramebufferTexture3DEXT(textarget)"); return; } - if (texture) { - const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); - texObj = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texture); - if (!texObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture3DEXT(texture)"); - return; - } - if (texObj->Target != textarget) { - _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */ - "glFramebufferTexture3DEXT(texture target)"); - return; - } - if (zoffset < 0 || zoffset >= maxSize) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture3DEXT(zoffset)"); - return; - } - } - else { - /* remove texture attachment */ - texObj = NULL; - } - ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, - level, zoffset); + framebuffer_texture(ctx, "3D", target, attachment, textarget, texture, + level, zoffset); +} + + +void GLAPIENTRY +_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer) +{ + GET_CURRENT_CONTEXT(ctx); + + framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, + level, layer); } @@ -1165,13 +1369,35 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLuint renderbuffer) { struct gl_renderbuffer_attachment *att; + struct gl_framebuffer *fb; struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - if (target != GL_FRAMEBUFFER_EXT) { + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(target)"); + return; + } + fb = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(target)"); + return; + } + fb = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + fb = ctx->DrawBuffer; + break; + default: _mesa_error(ctx, GL_INVALID_ENUM, "glFramebufferRenderbufferEXT(target)"); return; @@ -1183,13 +1409,13 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, return; } - if (ctx->DrawBuffer->Name == 0) { + if (fb->Name == 0) { /* Can't attach new renderbuffers to a window system framebuffer */ _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT"); return; } - att = get_attachment(ctx, ctx->DrawBuffer, attachment); + att = _mesa_get_attachment(ctx, fb, attachment); if (att == NULL) { _mesa_error(ctx, GL_INVALID_ENUM, "glFramebufferRenderbufferEXT(attachment)"); @@ -1197,7 +1423,7 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, } if (renderbuffer) { - rb = lookup_renderbuffer(ctx, renderbuffer); + rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); if (!rb) { _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT(renderbuffer)"); @@ -1209,10 +1435,20 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, rb = NULL; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + /* The above doesn't fully flush the drivers in the way that a + * glFlush does, but that is required here: + */ + if (ctx->Driver.Flush) + ctx->Driver.Flush(ctx); + assert(ctx->Driver.FramebufferRenderbuffer); - ctx->Driver.FramebufferRenderbuffer(ctx, att, rb); + ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); - _mesa_update_framebuffer_visual(ctx->DrawBuffer); + /* Some subsequent GL commands may depend on the framebuffer's visual + * after the binding is updated. Update visual info now. + */ + _mesa_update_framebuffer_visual(fb); } @@ -1221,29 +1457,59 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint *params) { const struct gl_renderbuffer_attachment *att; + struct gl_framebuffer *buffer; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - if (target != GL_FRAMEBUFFER_EXT) { + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(target)"); + return; + } + buffer = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(target)"); + return; + } + buffer = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + buffer = ctx->DrawBuffer; + break; + default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetFramebufferAttachmentParameterivEXT(target)"); return; } - if (ctx->DrawBuffer->Name == 0) { + if (buffer->Name == 0) { _mesa_error(ctx, GL_INVALID_OPERATION, "glGetFramebufferAttachmentParameterivEXT"); return; } - att = get_attachment(ctx, ctx->DrawBuffer, attachment); + att = _mesa_get_attachment(ctx, buffer, attachment); if (att == NULL) { _mesa_error(ctx, GL_INVALID_ENUM, "glGetFramebufferAttachmentParameterivEXT(attachment)"); return; } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + /* The above doesn't fully flush the drivers in the way that a + * glFlush does, but that is required here: + */ + if (ctx->Driver.Flush) + ctx->Driver.Flush(ctx); + switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: *params = att->Type; @@ -1271,7 +1537,12 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, return; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: if (att->Type == GL_TEXTURE) { - *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace; + if (att->Texture && att->Texture->Target == GL_TEXTURE_CUBE_MAP) { + *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace; + } + else { + *params = 0; + } } else { _mesa_error(ctx, GL_INVALID_ENUM, @@ -1280,7 +1551,12 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, return; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: if (att->Type == GL_TEXTURE) { - *params = att->Zoffset; + if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) { + *params = att->Zoffset; + } + else { + *params = 0; + } } else { _mesa_error(ctx, GL_INVALID_ENUM, @@ -1303,6 +1579,7 @@ _mesa_GenerateMipmapEXT(GLenum target) GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); switch (target) { case GL_TEXTURE_1D: @@ -1319,6 +1596,97 @@ _mesa_GenerateMipmapEXT(GLenum target) texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit]; texObj = _mesa_select_tex_object(ctx, texUnit, target); - /* XXX this might not handle cube maps correctly */ - _mesa_generate_mipmap(ctx, target, texUnit, texObj); + _mesa_lock_texture(ctx, texObj); + if (target == GL_TEXTURE_CUBE_MAP) { + int face; + + for (face = 0; face < 6; face++) + ctx->Driver.GenerateMipmap(ctx, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face, + texObj); + } else { + ctx->Driver.GenerateMipmap(ctx, target, texObj); + } + _mesa_unlock_texture(ctx, texObj); +} + + +#if FEATURE_EXT_framebuffer_blit +void GLAPIENTRY +_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (ctx->NewState) { + _mesa_update_state(ctx); + } + + if (!ctx->ReadBuffer) { + /* XXX */ + } + + /* check for complete framebuffers */ + if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT || + ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, + "glBlitFramebufferEXT(incomplete draw/read buffers)"); + return; + } + + if (filter != GL_NEAREST && filter != GL_LINEAR) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)"); + return; + } + + if (mask & ~(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT)) { + _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); + return; + } + + /* depth/stencil must be blitted with nearest filtering */ + if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) + && filter != GL_NEAREST) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter"); + return; + } + + if (mask & GL_STENCIL_BUFFER_BIT) { + struct gl_renderbuffer *readRb = ctx->ReadBuffer->_StencilBuffer; + struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_StencilBuffer; + if (readRb->StencilBits != drawRb->StencilBits) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(stencil buffer size mismatch"); + return; + } + } + + if (mask & GL_DEPTH_BUFFER_BIT) { + struct gl_renderbuffer *readRb = ctx->ReadBuffer->_DepthBuffer; + struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_DepthBuffer; + if (readRb->DepthBits != drawRb->DepthBits) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(depth buffer size mismatch"); + return; + } + } + + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT"); + return; + } + + ASSERT(ctx->Driver.BlitFramebuffer); + ctx->Driver.BlitFramebuffer(ctx, + srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + mask, filter); } +#endif /* FEATURE_EXT_framebuffer_blit */