X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=2c01526d16791c36104cde26d375b7765d037bca;hb=b9b88516f8d3efc902696f1092519e298ceb7cdb;hp=f31d8b36db8ffbde680bc3753636b7bbe2cbcac6;hpb=652901e95b4ed406293d0e1fabee857c054119b1;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index f31d8b36db8..2c01526d167 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -1,6 +1,5 @@ /* * Mesa 3-D graphics library - * Version: 7.1 * * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. * Copyright (C) 1999-2009 VMware, Inc. All Rights Reserved. @@ -18,9 +17,10 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. */ @@ -31,6 +31,7 @@ * Brian Paul */ +#include #include "buffers.h" #include "context.h" @@ -38,9 +39,10 @@ #include "fbobject.h" #include "formats.h" #include "framebuffer.h" +#include "glformats.h" #include "hash.h" #include "macros.h" -#include "mfeatures.h" +#include "multisample.h" #include "mtypes.h" #include "renderbuffer.h" #include "state.h" @@ -48,13 +50,6 @@ #include "texobj.h" -/** Set this to 1 to help debug FBO incompleteness problems */ -#define DEBUG_FBO 0 - -/** Set this to 1 to debug/log glBlitFramebuffer() calls */ -#define DEBUG_BLIT 0 - - /** * Notes: * @@ -78,13 +73,8 @@ static struct gl_renderbuffer DummyRenderbuffer; static struct gl_framebuffer IncompleteFramebuffer; -#define IS_CUBE_FACE(TARGET) \ - ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \ - (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) - - static void -delete_dummy_renderbuffer(struct gl_renderbuffer *rb) +delete_dummy_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { /* no op */ } @@ -99,9 +89,9 @@ delete_dummy_framebuffer(struct gl_framebuffer *fb) void _mesa_init_fbobjects(struct gl_context *ctx) { - _glthread_INIT_MUTEX(DummyFramebuffer.Mutex); - _glthread_INIT_MUTEX(DummyRenderbuffer.Mutex); - _glthread_INIT_MUTEX(IncompleteFramebuffer.Mutex); + mtx_init(&DummyFramebuffer.Mutex, mtx_plain); + mtx_init(&DummyRenderbuffer.Mutex, mtx_plain); + mtx_init(&IncompleteFramebuffer.Mutex, mtx_plain); DummyFramebuffer.Delete = delete_dummy_framebuffer; DummyRenderbuffer.Delete = delete_dummy_renderbuffer; IncompleteFramebuffer.Delete = delete_dummy_framebuffer; @@ -130,6 +120,27 @@ _mesa_lookup_renderbuffer(struct gl_context *ctx, GLuint id) } +/** + * A convenience function for direct state access that throws + * GL_INVALID_OPERATION if the renderbuffer doesn't exist. + */ +struct gl_renderbuffer * +_mesa_lookup_renderbuffer_err(struct gl_context *ctx, GLuint id, + const char *func) +{ + struct gl_renderbuffer *rb; + + rb = _mesa_lookup_renderbuffer(ctx, id); + if (!rb || rb == &DummyRenderbuffer) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(non-existent renderbuffer %u)", func, id); + return NULL; + } + + return rb; +} + + /** * Helper routine for getting a gl_framebuffer. */ @@ -147,6 +158,27 @@ _mesa_lookup_framebuffer(struct gl_context *ctx, GLuint id) } +/** + * A convenience function for direct state access that throws + * GL_INVALID_OPERATION if the framebuffer doesn't exist. + */ +struct gl_framebuffer * +_mesa_lookup_framebuffer_err(struct gl_context *ctx, GLuint id, + const char *func) +{ + struct gl_framebuffer *fb; + + fb = _mesa_lookup_framebuffer(ctx, id); + if (!fb || fb == &DummyFramebuffer) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(non-existent framebuffer %u)", func, id); + return NULL; + } + + return fb; +} + + /** * Mark the given framebuffer as invalid. This will force the * test for framebuffer completeness to be done before the framebuffer @@ -159,6 +191,30 @@ invalidate_framebuffer(struct gl_framebuffer *fb) } +/** + * Return the gl_framebuffer object which corresponds to the given + * framebuffer target, such as GL_DRAW_FRAMEBUFFER. + * Check support for GL_EXT_framebuffer_blit to determine if certain + * targets are legal. + * \return gl_framebuffer pointer or NULL if target is illegal + */ +static struct gl_framebuffer * +get_framebuffer_target(struct gl_context *ctx, GLenum target) +{ + bool have_fb_blit = _mesa_is_gles3(ctx) || _mesa_is_desktop_gl(ctx); + switch (target) { + case GL_DRAW_FRAMEBUFFER: + return have_fb_blit ? ctx->DrawBuffer : NULL; + case GL_READ_FRAMEBUFFER: + return have_fb_blit ? ctx->ReadBuffer : NULL; + case GL_FRAMEBUFFER_EXT: + return ctx->DrawBuffer; + default: + return NULL; + } +} + + /** * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding * gl_renderbuffer_attachment object. @@ -167,13 +223,13 @@ invalidate_framebuffer(struct gl_framebuffer *fb) * If \p attachment is GL_DEPTH_STENCIL_ATTACHMENT, return a pointer to * the depth buffer attachment point. */ -struct gl_renderbuffer_attachment * -_mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, - GLenum attachment) +static struct gl_renderbuffer_attachment * +get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment) { GLuint i; - assert(fb->Name > 0); + assert(_mesa_is_user_fbo(fb)); switch (attachment) { case GL_COLOR_ATTACHMENT0_EXT: @@ -192,19 +248,22 @@ _mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, case GL_COLOR_ATTACHMENT13_EXT: case GL_COLOR_ATTACHMENT14_EXT: case GL_COLOR_ATTACHMENT15_EXT: + /* Only OpenGL ES 1.x forbids color attachments other than + * GL_COLOR_ATTACHMENT0. For all other APIs the limit set by the + * hardware is used. + */ i = attachment - GL_COLOR_ATTACHMENT0_EXT; - if (i >= ctx->Const.MaxColorAttachments) { - return NULL; + if (i >= ctx->Const.MaxColorAttachments + || (i > 0 && ctx->API == API_OPENGLES)) { + return NULL; } return &fb->Attachment[BUFFER_COLOR0 + i]; case GL_DEPTH_STENCIL_ATTACHMENT: + if (!_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx)) + return NULL; /* fall-through */ - case GL_DEPTH_BUFFER: - /* fall-through / new in GL 3.0 */ case GL_DEPTH_ATTACHMENT_EXT: return &fb->Attachment[BUFFER_DEPTH]; - case GL_STENCIL_BUFFER: - /* fall-through / new in GL 3.0 */ case GL_STENCIL_ATTACHMENT_EXT: return &fb->Attachment[BUFFER_STENCIL]; default: @@ -221,7 +280,26 @@ static struct gl_renderbuffer_attachment * _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, GLenum attachment) { - assert(fb->Name == 0); + assert(_mesa_is_winsys_fbo(fb)); + + if (_mesa_is_gles3(ctx)) { + assert(attachment == GL_BACK || + attachment == GL_DEPTH || + attachment == GL_STENCIL); + switch (attachment) { + case GL_BACK: + /* Since there is no stereo rendering in ES 3.0, only return the + * LEFT bits. + */ + if (ctx->DrawBuffer->Visual.doubleBufferMode) + return &fb->Attachment[BUFFER_BACK_LEFT]; + return &fb->Attachment[BUFFER_FRONT_LEFT]; + case GL_DEPTH: + return &fb->Attachment[BUFFER_DEPTH]; + case GL_STENCIL: + return &fb->Attachment[BUFFER_STENCIL]; + } + } switch (attachment) { case GL_FRONT_LEFT: @@ -237,13 +315,34 @@ _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, return &fb->Attachment[BUFFER_AUX0]; } return NULL; - case GL_DEPTH_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_DEPTH_ATTACHMENT_EXT: + + /* Page 336 (page 352 of the PDF) of the OpenGL 3.0 spec says: + * + * "If the default framebuffer is bound to target, then attachment must + * be one of FRONT LEFT, FRONT RIGHT, BACK LEFT, BACK RIGHT, or AUXi, + * identifying a color buffer; DEPTH, identifying the depth buffer; or + * STENCIL, identifying the stencil buffer." + * + * Revision #34 of the ARB_framebuffer_object spec has essentially the same + * language. However, revision #33 of the ARB_framebuffer_object spec + * says: + * + * "If the default framebuffer is bound to , then + * must be one of FRONT_LEFT, FRONT_RIGHT, BACK_LEFT, BACK_RIGHT, AUXi, + * DEPTH_BUFFER, or STENCIL_BUFFER, identifying a color buffer, the + * depth buffer, or the stencil buffer, and may be + * FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE or + * FRAMEBUFFER_ATTACHMENT_OBJECT_NAME." + * + * The enum values for DEPTH_BUFFER and STENCIL_BUFFER have been removed + * from glext.h, so shipping apps should not use those values. + * + * Note that neither EXT_framebuffer_object nor OES_framebuffer_object + * support queries of the window system FBO. + */ + case GL_DEPTH: return &fb->Attachment[BUFFER_DEPTH]; - case GL_STENCIL_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_STENCIL_ATTACHMENT_EXT: + case GL_STENCIL: return &fb->Attachment[BUFFER_STENCIL]; default: return NULL; @@ -256,67 +355,146 @@ _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, * Remove any texture or renderbuffer attached to the given attachment * point. Update reference counts, etc. */ -void -_mesa_remove_attachment(struct gl_context *ctx, - struct gl_renderbuffer_attachment *att) +static void +remove_attachment(struct gl_context *ctx, + struct gl_renderbuffer_attachment *att) { + struct gl_renderbuffer *rb = att->Renderbuffer; + + /* tell driver that we're done rendering to this texture. */ + if (rb && rb->NeedsFinishRenderTexture) + ctx->Driver.FinishRenderTexture(ctx, rb); + if (att->Type == GL_TEXTURE) { - ASSERT(att->Texture); - if (ctx->Driver.FinishRenderTexture) { - /* tell driver that we're done rendering to this texture. */ - ctx->Driver.FinishRenderTexture(ctx, att); - } + assert(att->Texture); _mesa_reference_texobj(&att->Texture, NULL); /* unbind */ - ASSERT(!att->Texture); + assert(!att->Texture); } if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) { - ASSERT(!att->Texture); + assert(!att->Texture); _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); /* unbind */ - ASSERT(!att->Renderbuffer); + assert(!att->Renderbuffer); } att->Type = GL_NONE; att->Complete = GL_TRUE; } +/** + * Verify a couple error conditions that will lead to an incomplete FBO and + * may cause problems for the driver's RenderTexture path. + */ +static bool +driver_RenderTexture_is_safe(const struct gl_renderbuffer_attachment *att) +{ + const struct gl_texture_image *const texImage = + att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + + if (!texImage || + texImage->Width == 0 || texImage->Height == 0 || texImage->Depth == 0) + return false; + + if ((texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY + && att->Zoffset >= texImage->Height) + || (texImage->TexObject->Target != GL_TEXTURE_1D_ARRAY + && att->Zoffset >= texImage->Depth)) + return false; + + return true; +} + +/** + * Create a renderbuffer which will be set up by the driver to wrap the + * texture image slice. + * + * By using a gl_renderbuffer (like user-allocated renderbuffers), drivers get + * to share most of their framebuffer rendering code between winsys, + * renderbuffer, and texture attachments. + * + * The allocated renderbuffer uses a non-zero Name so that drivers can check + * it for determining vertical orientation, but we use ~0 to make it fairly + * unambiguous with actual user (non-texture) renderbuffers. + */ +void +_mesa_update_texture_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + struct gl_renderbuffer_attachment *att) +{ + struct gl_texture_image *texImage; + struct gl_renderbuffer *rb; + + texImage = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + + rb = att->Renderbuffer; + if (!rb) { + rb = ctx->Driver.NewRenderbuffer(ctx, ~0); + if (!rb) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glFramebufferTexture()"); + return; + } + _mesa_reference_renderbuffer(&att->Renderbuffer, rb); + + /* This can't get called on a texture renderbuffer, so set it to NULL + * for clarity compared to user renderbuffers. + */ + rb->AllocStorage = NULL; + + rb->NeedsFinishRenderTexture = ctx->Driver.FinishRenderTexture != NULL; + } + + if (!texImage) + return; + + rb->_BaseFormat = texImage->_BaseFormat; + rb->Format = texImage->TexFormat; + rb->InternalFormat = texImage->InternalFormat; + rb->Width = texImage->Width2; + rb->Height = texImage->Height2; + rb->Depth = texImage->Depth2; + rb->NumSamples = texImage->NumSamples; + rb->TexImage = texImage; + + if (driver_RenderTexture_is_safe(att)) + ctx->Driver.RenderTexture(ctx, fb, att); +} /** * Bind a texture object to an attachment point. * The previous binding, if any, will be removed first. */ -void -_mesa_set_texture_attachment(struct gl_context *ctx, - struct gl_framebuffer *fb, - struct gl_renderbuffer_attachment *att, - struct gl_texture_object *texObj, - GLenum texTarget, GLuint level, GLuint zoffset) +static void +set_texture_attachment(struct gl_context *ctx, + struct gl_framebuffer *fb, + struct gl_renderbuffer_attachment *att, + struct gl_texture_object *texObj, + GLenum texTarget, GLuint level, GLuint layer, + GLboolean layered) { + struct gl_renderbuffer *rb = att->Renderbuffer; + + if (rb && rb->NeedsFinishRenderTexture) + ctx->Driver.FinishRenderTexture(ctx, rb); + if (att->Texture == texObj) { /* re-attaching same texture */ - ASSERT(att->Type == GL_TEXTURE); - if (ctx->Driver.FinishRenderTexture) - ctx->Driver.FinishRenderTexture(ctx, att); + assert(att->Type == GL_TEXTURE); } else { /* new attachment */ - if (ctx->Driver.FinishRenderTexture && att->Texture) - ctx->Driver.FinishRenderTexture(ctx, att); - _mesa_remove_attachment(ctx, att); + remove_attachment(ctx, att); att->Type = GL_TEXTURE; assert(!att->Texture); _mesa_reference_texobj(&att->Texture, texObj); } + invalidate_framebuffer(fb); /* always update these fields */ att->TextureLevel = level; att->CubeMapFace = _mesa_tex_target_to_face(texTarget); - att->Zoffset = zoffset; + att->Zoffset = layer; + att->Layered = layered; att->Complete = GL_FALSE; - if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) { - ctx->Driver.RenderTexture(ctx, fb, att); - } - - invalidate_framebuffer(fb); + _mesa_update_texture_renderbuffer(ctx, fb, att); } @@ -324,15 +502,16 @@ _mesa_set_texture_attachment(struct gl_context *ctx, * Bind a renderbuffer to an attachment point. * The previous binding, if any, will be removed first. */ -void -_mesa_set_renderbuffer_attachment(struct gl_context *ctx, - struct gl_renderbuffer_attachment *att, - struct gl_renderbuffer *rb) +static void +set_renderbuffer_attachment(struct gl_context *ctx, + struct gl_renderbuffer_attachment *att, + struct gl_renderbuffer *rb) { /* XXX check if re-doing same attachment, exit early */ - _mesa_remove_attachment(ctx, att); + remove_attachment(ctx, att); att->Type = GL_RENDERBUFFER_EXT; att->Texture = NULL; /* just to be safe */ + att->Layered = GL_FALSE; att->Complete = GL_FALSE; _mesa_reference_renderbuffer(&att->Renderbuffer, rb); } @@ -343,32 +522,104 @@ _mesa_set_renderbuffer_attachment(struct gl_context *ctx, * Attach a renderbuffer object to a framebuffer object. */ void -_mesa_framebuffer_renderbuffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - GLenum attachment, struct gl_renderbuffer *rb) +_mesa_FramebufferRenderbuffer_sw(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLenum attachment, + struct gl_renderbuffer *rb) { struct gl_renderbuffer_attachment *att; - _glthread_LOCK_MUTEX(fb->Mutex); + mtx_lock(&fb->Mutex); - att = _mesa_get_attachment(ctx, fb, attachment); - ASSERT(att); + att = get_attachment(ctx, fb, attachment); + assert(att); if (rb) { - _mesa_set_renderbuffer_attachment(ctx, att, rb); + set_renderbuffer_attachment(ctx, att, rb); if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { /* do stencil attachment here (depth already done above) */ - att = _mesa_get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); + att = get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); assert(att); - _mesa_set_renderbuffer_attachment(ctx, att, rb); + set_renderbuffer_attachment(ctx, att, rb); } + rb->AttachedAnytime = GL_TRUE; } else { - _mesa_remove_attachment(ctx, att); + remove_attachment(ctx, att); + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + /* detach stencil (depth was detached above) */ + att = get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); + assert(att); + remove_attachment(ctx, att); + } } invalidate_framebuffer(fb); - _glthread_UNLOCK_MUTEX(fb->Mutex); + mtx_unlock(&fb->Mutex); +} + + +/** + * Fallback for ctx->Driver.ValidateFramebuffer() + * Check if the renderbuffer's formats are supported by the software + * renderer. + * Drivers should probably override this. + */ +void +_mesa_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + gl_buffer_index buf; + for (buf = 0; buf < BUFFER_COUNT; buf++) { + const struct gl_renderbuffer *rb = fb->Attachment[buf].Renderbuffer; + if (rb) { + switch (rb->_BaseFormat) { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_RED: + case GL_RG: + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; + return; + + default: + switch (rb->Format) { + /* XXX This list is likely incomplete. */ + case MESA_FORMAT_R9G9B9E5_FLOAT: + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; + return; + default:; + /* render buffer format is supported by software rendering */ + } + } + } + } +} + + +/** + * Return true if the framebuffer has a combined depth/stencil + * renderbuffer attached. + */ +GLboolean +_mesa_has_depthstencil_combined(const struct gl_framebuffer *fb) +{ + const struct gl_renderbuffer_attachment *depth = + &fb->Attachment[BUFFER_DEPTH]; + const struct gl_renderbuffer_attachment *stencil = + &fb->Attachment[BUFFER_STENCIL]; + + if (depth->Type == stencil->Type) { + if (depth->Type == GL_RENDERBUFFER_EXT && + depth->Renderbuffer == stencil->Renderbuffer) + return GL_TRUE; + + if (depth->Type == GL_TEXTURE && + depth->Texture == stencil->Texture) + return GL_TRUE; + } + + return GL_FALSE; } @@ -378,11 +629,9 @@ _mesa_framebuffer_renderbuffer(struct gl_context *ctx, static void att_incomplete(const char *msg) { -#if DEBUG_FBO - _mesa_debug(NULL, "attachment incomplete: %s\n", msg); -#else - (void) msg; -#endif + if (MESA_DEBUG_FLAGS & DEBUG_INCOMPLETE_FBO) { + _mesa_debug(NULL, "attachment incomplete: %s\n", msg); + } } @@ -390,14 +639,19 @@ att_incomplete(const char *msg) * For debug only. */ static void -fbo_incomplete(const char *msg, int index) +fbo_incomplete(struct gl_context *ctx, const char *msg, int index) { -#if DEBUG_FBO - _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index); -#else - (void) msg; - (void) index; -#endif + static GLuint msg_id; + + _mesa_gl_debug(ctx, &msg_id, + MESA_DEBUG_SOURCE_API, + MESA_DEBUG_TYPE_OTHER, + MESA_DEBUG_SEVERITY_MEDIUM, + "FBO incomplete: %s [%d]\n", msg, index); + + if (MESA_DEBUG_FLAGS & DEBUG_INCOMPLETE_FBO) { + _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index); + } } @@ -415,7 +669,8 @@ _mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat) case GL_LUMINANCE_ALPHA: case GL_INTENSITY: case GL_ALPHA: - return ctx->Extensions.ARB_framebuffer_object; + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_framebuffer_object; case GL_RED: case GL_RG: return ctx->Extensions.ARB_texture_rg; @@ -425,6 +680,52 @@ _mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat) } +/** + * Is the given base format a legal format for a color renderbuffer? + */ +static GLboolean +is_format_color_renderable(const struct gl_context *ctx, mesa_format format, + GLenum internalFormat) +{ + const GLenum baseFormat = + _mesa_get_format_base_format(format); + GLboolean valid; + + valid = _mesa_is_legal_color_format(ctx, baseFormat); + if (!valid || _mesa_is_desktop_gl(ctx)) { + return valid; + } + + /* Reject additional cases for GLES */ + switch (internalFormat) { + case GL_RGBA8_SNORM: + case GL_RGB32F: + case GL_RGB32I: + case GL_RGB32UI: + case GL_RGB16F: + case GL_RGB16I: + case GL_RGB16UI: + case GL_RGB8_SNORM: + case GL_RGB8I: + case GL_RGB8UI: + case GL_SRGB8: + case GL_RGB9_E5: + case GL_RG8_SNORM: + case GL_R8_SNORM: + return GL_FALSE; + default: + break; + } + + if (format == MESA_FORMAT_B10G10R10A2_UNORM && + internalFormat != GL_RGB10_A2) { + return GL_FALSE; + } + + return GL_TRUE; +} + + /** * Is the given base format a legal format for a depth/stencil renderbuffer? */ @@ -476,18 +777,42 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, } if (texImage->Width < 1 || texImage->Height < 1) { att_incomplete("teximage width/height=0"); - printf("texobj = %u\n", texObj->Name); - printf("level = %d\n", att->TextureLevel); att->Complete = GL_FALSE; return; } - if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) { - att_incomplete("bad z offset"); - att->Complete = GL_FALSE; - return; + + switch (texObj->Target) { + case GL_TEXTURE_3D: + if (att->Zoffset >= texImage->Depth) { + att_incomplete("bad z offset"); + att->Complete = GL_FALSE; + return; + } + break; + case GL_TEXTURE_1D_ARRAY: + if (att->Zoffset >= texImage->Height) { + att_incomplete("bad 1D-array layer"); + att->Complete = GL_FALSE; + return; + } + break; + case GL_TEXTURE_2D_ARRAY: + if (att->Zoffset >= texImage->Depth) { + att_incomplete("bad 2D-array layer"); + att->Complete = GL_FALSE; + return; + } + break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + if (att->Zoffset >= texImage->Depth) { + att_incomplete("bad cube-array layer"); + att->Complete = GL_FALSE; + return; + } + break; } - baseFormat = _mesa_get_format_base_format(texImage->TexFormat); + baseFormat = texImage->_BaseFormat; if (format == GL_COLOR) { if (!_mesa_is_legal_color_format(ctx, baseFormat)) { @@ -500,14 +825,25 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, att->Complete = GL_FALSE; return; } + + /* OES_texture_float allows creation and use of floating point + * textures with GL_FLOAT, GL_HALF_FLOAT but it does not allow + * these textures to be used as a render target, this is done via + * GL_EXT_color_buffer(_half)_float with set of new sized types. + */ + if (_mesa_is_gles(ctx) && (texImage->TexObject->_IsFloat || + texImage->TexObject->_IsHalfFloat)) { + att_incomplete("bad internal format"); + att->Complete = GL_FALSE; + return; + } } else if (format == GL_DEPTH) { if (baseFormat == GL_DEPTH_COMPONENT) { /* OK */ } - else if (ctx->Extensions.EXT_packed_depth_stencil && - ctx->Extensions.ARB_depth_texture && - baseFormat == GL_DEPTH_STENCIL_EXT) { + else if (ctx->Extensions.ARB_depth_texture && + baseFormat == GL_DEPTH_STENCIL) { /* OK */ } else { @@ -517,13 +853,14 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, } } else { - ASSERT(format == GL_STENCIL); - if (ctx->Extensions.EXT_packed_depth_stencil && - ctx->Extensions.ARB_depth_texture && - baseFormat == GL_DEPTH_STENCIL_EXT) { + assert(format == GL_STENCIL); + if (ctx->Extensions.ARB_depth_texture && + baseFormat == GL_DEPTH_STENCIL) { /* OK */ - } - else { + } else if (ctx->Extensions.ARB_texture_stencil8 && + baseFormat == GL_STENCIL_INDEX) { + /* OK */ + } else { /* no such thing as stencil-only textures */ att_incomplete("illegal stencil texture"); att->Complete = GL_FALSE; @@ -532,10 +869,9 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, } } else if (att->Type == GL_RENDERBUFFER_EXT) { - const GLenum baseFormat = - _mesa_get_format_base_format(att->Renderbuffer->Format); + const GLenum baseFormat = att->Renderbuffer->_BaseFormat; - ASSERT(att->Renderbuffer); + assert(att->Renderbuffer); if (!att->Renderbuffer->InternalFormat || att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) { @@ -554,8 +890,7 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, if (baseFormat == GL_DEPTH_COMPONENT) { /* OK */ } - else if (ctx->Extensions.EXT_packed_depth_stencil && - baseFormat == GL_DEPTH_STENCIL_EXT) { + else if (baseFormat == GL_DEPTH_STENCIL) { /* OK */ } else { @@ -566,11 +901,8 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, } else { assert(format == GL_STENCIL); - if (baseFormat == GL_STENCIL_INDEX) { - /* OK */ - } - else if (ctx->Extensions.EXT_packed_depth_stencil && - baseFormat == GL_DEPTH_STENCIL_EXT) { + if (baseFormat == GL_STENCIL_INDEX || + baseFormat == GL_DEPTH_STENCIL) { /* OK */ } else { @@ -581,7 +913,7 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, } } else { - ASSERT(att->Type == GL_NONE); + assert(att->Type == GL_NONE); /* complete */ return; } @@ -604,14 +936,28 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, GLenum intFormat = GL_NONE; /* color buffers' internal format */ GLuint minWidth = ~0, minHeight = ~0, maxWidth = 0, maxHeight = 0; GLint numSamples = -1; + GLint fixedSampleLocations = -1; GLint i; GLuint j; + /* Covers max_layer_count, is_layered, and layer_tex_target */ + bool layer_info_valid = false; + GLuint max_layer_count = 0, att_layer_count; + bool is_layered = false; + GLenum layer_tex_target = 0; + bool has_depth_attachment = false; + bool has_stencil_attachment = false; - assert(fb->Name != 0); + assert(_mesa_is_user_fbo(fb)); + + /* we're changing framebuffer fields here */ + FLUSH_VERTICES(ctx, _NEW_BUFFERS); numImages = 0; fb->Width = 0; fb->Height = 0; + fb->_AllColorBuffersFixedPoint = GL_TRUE; + fb->_HasSNormOrFloatColorBuffer = GL_FALSE; + fb->_HasAttachments = true; /* Start at -2 to more easily loop over all attachment points. * -2: depth buffer @@ -621,7 +967,8 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) { struct gl_renderbuffer_attachment *att; GLenum f; - gl_format mesaFormat; + mesa_format attFormat; + GLenum att_tex_target = GL_NONE; /* * XXX for ARB_fbo, only check color buffers that are named by @@ -635,8 +982,10 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, test_attachment_completeness(ctx, GL_DEPTH, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("depth attachment incomplete", -1); + fbo_incomplete(ctx, "depth attachment incomplete", -1); return; + } else if (att->Type != GL_NONE) { + has_depth_attachment = true; } } else if (i == -1) { @@ -644,8 +993,10 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, test_attachment_completeness(ctx, GL_STENCIL, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("stencil attachment incomplete", -1); + fbo_incomplete(ctx, "stencil attachment incomplete", -1); return; + } else if (att->Type != GL_NONE) { + has_stencil_attachment = true; } } else { @@ -653,7 +1004,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, test_attachment_completeness(ctx, GL_COLOR, att); if (!att->Complete) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("color attachment incomplete", i); + fbo_incomplete(ctx, "color attachment incomplete", i); return; } } @@ -661,19 +1012,38 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, /* get width, height, format of the renderbuffer/texture */ if (att->Type == GL_TEXTURE) { - const struct gl_texture_image *texImg - = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + const struct gl_texture_image *texImg = att->Renderbuffer->TexImage; + att_tex_target = att->Texture->Target; minWidth = MIN2(minWidth, texImg->Width); maxWidth = MAX2(maxWidth, texImg->Width); minHeight = MIN2(minHeight, texImg->Height); maxHeight = MAX2(maxHeight, texImg->Height); f = texImg->_BaseFormat; - mesaFormat = texImg->TexFormat; + attFormat = texImg->TexFormat; numImages++; - if (!_mesa_is_legal_color_format(ctx, f) && - !is_legal_depth_format(ctx, f)) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - fbo_incomplete("texture attachment incomplete", -1); + + if (!is_format_color_renderable(ctx, attFormat, + texImg->InternalFormat) && + !is_legal_depth_format(ctx, f) && + f != GL_STENCIL_INDEX) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + fbo_incomplete(ctx, "texture attachment incomplete", -1); + return; + } + + if (numSamples < 0) + numSamples = texImg->NumSamples; + else if (numSamples != texImg->NumSamples) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + fbo_incomplete(ctx, "inconsistent sample count", -1); + return; + } + + if (fixedSampleLocations < 0) + fixedSampleLocations = texImg->FixedSampleLocations; + else if (fixedSampleLocations != texImg->FixedSampleLocations) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + fbo_incomplete(ctx, "inconsistent fixed sample locations", -1); return; } } @@ -683,26 +1053,50 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, minHeight = MIN2(minHeight, att->Renderbuffer->Height); maxHeight = MAX2(minHeight, att->Renderbuffer->Height); f = att->Renderbuffer->InternalFormat; - mesaFormat = att->Renderbuffer->Format; + attFormat = att->Renderbuffer->Format; numImages++; + + if (numSamples < 0) + numSamples = att->Renderbuffer->NumSamples; + else if (numSamples != att->Renderbuffer->NumSamples) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + fbo_incomplete(ctx, "inconsistent sample count", -1); + return; + } + + /* RENDERBUFFER has fixedSampleLocations implicitly true */ + if (fixedSampleLocations < 0) + fixedSampleLocations = GL_TRUE; + else if (fixedSampleLocations != GL_TRUE) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + fbo_incomplete(ctx, "inconsistent fixed sample locations", -1); + return; + } } else { assert(att->Type == GL_NONE); continue; } - if (numSamples < 0) { - /* first buffer */ - numSamples = att->Renderbuffer->NumSamples; - } - /* check if integer color */ - fb->_IntegerColor = _mesa_is_format_integer_color(mesaFormat); + fb->_IntegerColor = _mesa_is_format_integer_color(attFormat); - /* Error-check width, height, format, samples - */ + /* Update _AllColorBuffersFixedPoint and _HasSNormOrFloatColorBuffer. */ + if (i >= 0) { + GLenum type = _mesa_get_format_datatype(attFormat); + + fb->_AllColorBuffersFixedPoint = + fb->_AllColorBuffersFixedPoint && + (type == GL_UNSIGNED_NORMALIZED || type == GL_SIGNED_NORMALIZED); + + fb->_HasSNormOrFloatColorBuffer = + fb->_HasSNormOrFloatColorBuffer || + type == GL_SIGNED_NORMALIZED || type == GL_FLOAT; + } + + /* Error-check width, height, format */ if (numImages == 1) { - /* save format, num samples */ + /* save format */ if (i >= 0) { intFormat = f; } @@ -712,61 +1106,139 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, /* 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); + fbo_incomplete(ctx, "width or height mismatch", -1); return; } - /* check that all color buffer have same format */ + /* check that all color buffers are the same format */ if (intFormat != GL_NONE && f != intFormat) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - fbo_incomplete("format mismatch", -1); + fbo_incomplete(ctx, "format mismatch", -1); return; } } - if (att->Renderbuffer && - att->Renderbuffer->NumSamples != numSamples) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; - fbo_incomplete("inconsistant number of samples", i); - return; - } + } + + /* Check that the format is valid. (MESA_FORMAT_NONE means unsupported) + */ + if (att->Type == GL_RENDERBUFFER && + att->Renderbuffer->Format == MESA_FORMAT_NONE) { + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; + fbo_incomplete(ctx, "unsupported renderbuffer format", i); + return; + } + + /* Check that layered rendering is consistent. */ + if (att->Layered) { + if (att_tex_target == GL_TEXTURE_CUBE_MAP) + att_layer_count = 6; + else if (att_tex_target == GL_TEXTURE_1D_ARRAY) + att_layer_count = att->Renderbuffer->Height; + else + att_layer_count = att->Renderbuffer->Depth; + } else { + att_layer_count = 0; + } + if (!layer_info_valid) { + is_layered = att->Layered; + max_layer_count = att_layer_count; + layer_tex_target = att_tex_target; + layer_info_valid = true; + } else if (max_layer_count > 0 && layer_tex_target != att_tex_target) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS; + fbo_incomplete(ctx, "layered framebuffer has mismatched targets", i); + return; + } else if (is_layered != att->Layered) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS; + fbo_incomplete(ctx, + "framebuffer attachment layer mode is inconsistent", + i); + return; + } else if (att_layer_count > max_layer_count) { + max_layer_count = att_layer_count; + } + + /* + * The extension GL_ARB_framebuffer_no_attachments places additional + * requirement on each attachment. Those additional requirements are + * tighter that those of previous versions of GL. In interest of better + * compatibility, we will not enforce these restrictions. For the record + * those additional restrictions are quoted below: + * + * "The width and height of image are greater than zero and less than or + * equal to the values of the implementation-dependent limits + * MAX_FRAMEBUFFER_WIDTH and MAX_FRAMEBUFFER_HEIGHT, respectively." + * + * "If is a three-dimensional texture or a one- or two-dimensional + * array texture and the attachment is layered, the depth or layer count + * of the texture is less than or equal to the implementation-dependent + * limit MAX_FRAMEBUFFER_LAYERS." + * + * "If image has multiple samples, its sample count is less than or equal + * to the value of the implementation-dependent limit + * MAX_FRAMEBUFFER_SAMPLES." + * + * The same requirements are also in place for GL 4.5, + * Section 9.4.1 "Framebuffer Attachment Completeness", pg 310-311 + */ + } + + fb->MaxNumLayers = max_layer_count; + + if (numImages == 0) { + fb->_HasAttachments = false; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; + fbo_incomplete(ctx, "no attachments", -1); + return; + } + if (fb->DefaultGeometry.Width == 0 || fb->DefaultGeometry.Height == 0) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; + fbo_incomplete(ctx, "no attachments and default width or height is 0", -1); + return; } } -#if FEATURE_GL - if (ctx->API == API_OPENGL) { + if (_mesa_is_desktop_gl(ctx) && !ctx->Extensions.ARB_ES2_compatibility) { /* Check that all DrawBuffers are present */ for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) { - if (fb->ColorDrawBuffer[j] != GL_NONE) { - const struct gl_renderbuffer_attachment *att - = _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; - } - } + if (fb->ColorDrawBuffer[j] != GL_NONE) { + const struct gl_renderbuffer_attachment *att + = get_attachment(ctx, fb, fb->ColorDrawBuffer[j]); + assert(att); + if (att->Type == GL_NONE) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT; + fbo_incomplete(ctx, "missing drawbuffer", j); + return; + } + } } /* Check that the ReadBuffer is present */ if (fb->ColorReadBuffer != GL_NONE) { - const struct gl_renderbuffer_attachment *att - = _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; - } + const struct gl_renderbuffer_attachment *att + = get_attachment(ctx, fb, fb->ColorReadBuffer); + assert(att); + if (att->Type == GL_NONE) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT; + fbo_incomplete(ctx, "missing readbuffer", -1); + return; + } } } -#else - (void) j; -#endif - if (numImages == 0) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; - fbo_incomplete("no attachments", -1); + /* The OpenGL ES3 spec, in chapter 9.4. FRAMEBUFFER COMPLETENESS, says: + * + * "Depth and stencil attachments, if present, are the same image." + * + * This restriction is not present in the OpenGL ES2 spec. + */ + if (_mesa_is_gles3(ctx) && + has_stencil_attachment && has_depth_attachment && + !_mesa_has_depthstencil_combined(fb)) { + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; + fbo_incomplete(ctx, "Depth and stencil attachments must be the same image", -1); return; } @@ -780,32 +1252,34 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, if (ctx->Driver.ValidateFramebuffer) { ctx->Driver.ValidateFramebuffer(ctx, fb); if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - fbo_incomplete("driver marked FBO as incomplete", -1); + fbo_incomplete(ctx, "driver marked FBO as incomplete", -1); + return; } } - if (fb->_Status == GL_FRAMEBUFFER_COMPLETE_EXT) { - /* - * 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. - */ + /* + * 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. + */ + if (numImages != 0) { fb->Width = minWidth; fb->Height = minHeight; - - /* finally, update the visual info for the framebuffer */ - _mesa_update_framebuffer_visual(ctx, fb); } + + /* finally, update the visual info for the framebuffer */ + _mesa_update_framebuffer_visual(ctx, fb); } GLboolean GLAPIENTRY -_mesa_IsRenderbufferEXT(GLuint renderbuffer) +_mesa_IsRenderbuffer(GLuint renderbuffer) { GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); if (renderbuffer) { - struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + struct gl_renderbuffer *rb = + _mesa_lookup_renderbuffer(ctx, renderbuffer); if (rb != NULL && rb != &DummyRenderbuffer) return GL_TRUE; } @@ -813,13 +1287,31 @@ _mesa_IsRenderbufferEXT(GLuint renderbuffer) } -void GLAPIENTRY -_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) +static struct gl_renderbuffer * +allocate_renderbuffer_locked(struct gl_context *ctx, GLuint renderbuffer, + const char *func) { struct gl_renderbuffer *newRb; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); + /* create new renderbuffer object */ + newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer); + if (!newRb) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); + return NULL; + } + assert(newRb->AllocStorage); + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, renderbuffer, newRb); + newRb->RefCount = 1; /* referenced by hash table */ + + return newRb; +} + + +static void +bind_renderbuffer(GLenum target, GLuint renderbuffer, bool allow_user_names) +{ + struct gl_renderbuffer *newRb; + GET_CURRENT_CONTEXT(ctx); if (target != GL_RENDERBUFFER_EXT) { _mesa_error(ctx, GL_INVALID_ENUM, "glBindRenderbufferEXT(target)"); @@ -836,240 +1328,718 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) /* ID was reserved, but no real renderbuffer object made yet */ newRb = NULL; } - else if (!newRb && ctx->Extensions.ARB_framebuffer_object) { + else if (!newRb && !allow_user_names) { /* All RB IDs must be Gen'd */ _mesa_error(ctx, GL_INVALID_OPERATION, "glBindRenderbuffer(buffer)"); return; } if (!newRb) { - /* create new renderbuffer object */ - newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer); - if (!newRb) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT"); - return; - } - ASSERT(newRb->AllocStorage); - _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); - newRb->RefCount = 1; /* referenced by hash table */ + _mesa_HashLockMutex(ctx->Shared->RenderBuffers); + newRb = allocate_renderbuffer_locked(ctx, renderbuffer, + "glBindRenderbufferEXT"); + _mesa_HashUnlockMutex(ctx->Shared->RenderBuffers); } } else { newRb = NULL; } - ASSERT(newRb != &DummyRenderbuffer); + assert(newRb != &DummyRenderbuffer); _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb); } +void GLAPIENTRY +_mesa_BindRenderbuffer(GLenum target, GLuint renderbuffer) +{ + GET_CURRENT_CONTEXT(ctx); + + /* OpenGL ES glBindRenderbuffer and glBindRenderbufferOES use this same + * entry point, but they allow the use of user-generated names. + */ + bind_renderbuffer(target, renderbuffer, _mesa_is_gles(ctx)); +} + +void GLAPIENTRY +_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) +{ + /* This function should not be in the dispatch table for core profile / + * OpenGL 3.1, so execution should never get here in those cases -- no + * need for an explicit test. + */ + bind_renderbuffer(target, renderbuffer, true); +} /** - * If the given renderbuffer is anywhere attached to the framebuffer, detach - * the renderbuffer. - * This is used when a renderbuffer object is deleted. - * The spec calls for unbinding. + * ARB_framebuffer_no_attachment - Application passes requested param's + * here. NOTE: NumSamples requested need not be _NumSamples which is + * what the hw supports. */ static void -detach_renderbuffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - struct gl_renderbuffer *rb) +framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum pname, GLint param, const char *func) { - GLuint i; - for (i = 0; i < BUFFER_COUNT; i++) { - if (fb->Attachment[i].Renderbuffer == rb) { - _mesa_remove_attachment(ctx, &fb->Attachment[i]); + switch (pname) { + case GL_FRAMEBUFFER_DEFAULT_WIDTH: + if (param < 0 || param > ctx->Const.MaxFramebufferWidth) + _mesa_error(ctx, GL_INVALID_VALUE, "%s", func); + else + fb->DefaultGeometry.Width = param; + break; + case GL_FRAMEBUFFER_DEFAULT_HEIGHT: + if (param < 0 || param > ctx->Const.MaxFramebufferHeight) + _mesa_error(ctx, GL_INVALID_VALUE, "%s", func); + else + fb->DefaultGeometry.Height = param; + break; + case GL_FRAMEBUFFER_DEFAULT_LAYERS: + /* + * According to the OpenGL ES 3.1 specification section 9.2.1, the + * GL_FRAMEBUFFER_DEFAULT_LAYERS parameter name is not supported. + */ + if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname); + break; } + if (param < 0 || param > ctx->Const.MaxFramebufferLayers) + _mesa_error(ctx, GL_INVALID_VALUE, "%s", func); + else + fb->DefaultGeometry.Layers = param; + break; + case GL_FRAMEBUFFER_DEFAULT_SAMPLES: + if (param < 0 || param > ctx->Const.MaxFramebufferSamples) + _mesa_error(ctx, GL_INVALID_VALUE, "%s", func); + else + fb->DefaultGeometry.NumSamples = param; + break; + case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS: + fb->DefaultGeometry.FixedSampleLocations = param; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(pname=0x%x)", func, pname); } + invalidate_framebuffer(fb); + ctx->NewState |= _NEW_BUFFERS; +} + +void GLAPIENTRY +_mesa_FramebufferParameteri(GLenum target, GLenum pname, GLint param) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferParameteriv not supported " + "(ARB_framebuffer_no_attachments not implemented)"); + return; + } + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferParameteri(target=0x%x)", target); + return; + } + + /* check framebuffer binding */ + if (_mesa_is_winsys_fbo(fb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferParameteri"); + return; + } + + framebuffer_parameteri(ctx, fb, pname, param, "glFramebufferParameteri"); +} + +static void +get_framebuffer_parameteriv(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum pname, GLint *params, const char *func) +{ + switch (pname) { + case GL_FRAMEBUFFER_DEFAULT_WIDTH: + *params = fb->DefaultGeometry.Width; + break; + case GL_FRAMEBUFFER_DEFAULT_HEIGHT: + *params = fb->DefaultGeometry.Height; + break; + case GL_FRAMEBUFFER_DEFAULT_LAYERS: + /* + * According to the OpenGL ES 3.1 specification section 9.2.3, the + * GL_FRAMEBUFFER_LAYERS parameter name is not supported. + */ + if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname); + break; + } + *params = fb->DefaultGeometry.Layers; + break; + case GL_FRAMEBUFFER_DEFAULT_SAMPLES: + *params = fb->DefaultGeometry.NumSamples; + break; + case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS: + *params = fb->DefaultGeometry.FixedSampleLocations; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(pname=0x%x)", func, pname); + } +} + +void GLAPIENTRY +_mesa_GetFramebufferParameteriv(GLenum target, GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetFramebufferParameteriv not supported " + "(ARB_framebuffer_no_attachments not implemented)"); + return; + } + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferParameteriv(target=0x%x)", target); + return; + } + + /* check framebuffer binding */ + if (_mesa_is_winsys_fbo(fb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetFramebufferParameteriv"); + return; + } + + get_framebuffer_parameteriv(ctx, fb, pname, params, + "glGetFramebufferParameteriv"); +} + + +/** + * Remove the specified renderbuffer or texture from any attachment point in + * the framebuffer. + * + * \returns + * \c true if the renderbuffer was detached from an attachment point. \c + * false otherwise. + */ +bool +_mesa_detach_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + const void *att) +{ + unsigned i; + bool progress = false; + + for (i = 0; i < BUFFER_COUNT; i++) { + if (fb->Attachment[i].Texture == att + || fb->Attachment[i].Renderbuffer == att) { + remove_attachment(ctx, &fb->Attachment[i]); + progress = true; + } + } + + /* Section 4.4.4 (Framebuffer Completeness), subsection "Whole Framebuffer + * Completeness," of the OpenGL 3.1 spec says: + * + * "Performing any of the following actions may change whether the + * framebuffer is considered complete or incomplete: + * + * ... + * + * - Deleting, with DeleteTextures or DeleteRenderbuffers, an object + * containing an image that is attached to a framebuffer object + * that is bound to the framebuffer." + */ + if (progress) + invalidate_framebuffer(fb); + + return progress; } void GLAPIENTRY -_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers) +_mesa_DeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) { GLint i; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteRenderbuffers(n < 0)"); + return; + } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); for (i = 0; i < n; i++) { if (renderbuffers[i] > 0) { - struct gl_renderbuffer *rb; - rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]); - if (rb) { + struct gl_renderbuffer *rb; + rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]); + if (rb) { /* check if deleting currently bound renderbuffer object */ if (rb == ctx->CurrentRenderbuffer) { /* bind default */ - ASSERT(rb->RefCount >= 2); - _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + assert(rb->RefCount >= 2); + _mesa_BindRenderbuffer(GL_RENDERBUFFER_EXT, 0); } - if (ctx->DrawBuffer->Name) { - detach_renderbuffer(ctx, ctx->DrawBuffer, rb); + /* Section 4.4.2 (Attaching Images to Framebuffer Objects), + * subsection "Attaching Renderbuffer Images to a Framebuffer," + * of the OpenGL 3.1 spec says: + * + * "If a renderbuffer object is deleted while its image is + * attached to one or more attachment points in the currently + * bound framebuffer, then it is as if FramebufferRenderbuffer + * had been called, with a renderbuffer of 0, for each + * attachment point to which this image was attached in the + * currently bound framebuffer. In other words, this + * renderbuffer image is first detached from all attachment + * points in the currently bound framebuffer. Note that the + * renderbuffer image is specifically not detached from any + * non-bound framebuffers. Detaching the image from any + * non-bound framebuffers is the responsibility of the + * application. + */ + if (_mesa_is_user_fbo(ctx->DrawBuffer)) { + _mesa_detach_renderbuffer(ctx, ctx->DrawBuffer, rb); } - if (ctx->ReadBuffer->Name && ctx->ReadBuffer != ctx->DrawBuffer) { - detach_renderbuffer(ctx, ctx->ReadBuffer, rb); + if (_mesa_is_user_fbo(ctx->ReadBuffer) + && ctx->ReadBuffer != ctx->DrawBuffer) { + _mesa_detach_renderbuffer(ctx, ctx->ReadBuffer, rb); } - /* Remove from hash table immediately, to free the ID. + /* 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]); + _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]); if (rb != &DummyRenderbuffer) { /* no longer referenced by hash table */ _mesa_reference_renderbuffer(&rb, NULL); - } - } + } + } } } } - -void GLAPIENTRY -_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) +static void +create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, + bool dsa) { - GET_CURRENT_CONTEXT(ctx); + const char *func = dsa ? "glCreateRenderbuffers" : "glGenRenderbuffers"; GLuint first; GLint i; - ASSERT_OUTSIDE_BEGIN_END(ctx); - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)"); + _mesa_error(ctx, GL_INVALID_VALUE, "%s(n<0)", func); return; } if (!renderbuffers) return; + _mesa_HashLockMutex(ctx->Shared->RenderBuffers); + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n); for (i = 0; i < n; i++) { GLuint name = first + i; renderbuffers[i] = name; - /* insert dummy placeholder into hash table */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer); - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + + if (dsa) { + allocate_renderbuffer_locked(ctx, name, func); + } else { + /* insert a dummy renderbuffer into the hash table */ + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, name, + &DummyRenderbuffer); + } } + + _mesa_HashUnlockMutex(ctx->Shared->RenderBuffers); +} + + +void GLAPIENTRY +_mesa_GenRenderbuffers(GLsizei n, GLuint *renderbuffers) +{ + GET_CURRENT_CONTEXT(ctx); + create_render_buffers(ctx, n, renderbuffers, false); +} + + +void GLAPIENTRY +_mesa_CreateRenderbuffers(GLsizei n, GLuint *renderbuffers) +{ + GET_CURRENT_CONTEXT(ctx); + create_render_buffers(ctx, n, renderbuffers, true); } /** - * Given an internal format token for a renderbuffer, return the - * corresponding base format. + * Given an internal format token for a render buffer, return the + * corresponding base format (one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, + * GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL_EXT, GL_ALPHA, GL_LUMINANCE, + * GL_LUMINANCE_ALPHA, GL_INTENSITY, etc). + * + * This is similar to _mesa_base_tex_format() but the set of valid + * internal formats is different. + * + * Note that even if a format is determined to be legal here, validation + * of the FBO may fail if the format is not supported by the driver/GPU. + * + * \param internalFormat as passed to glRenderbufferStorage() + * \return the base internal format, or 0 if internalFormat is illegal */ GLenum _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) { - GLenum baseFormat; - + /* + * Notes: some formats such as alpha, luminance, etc. were added + * with GL_ARB_framebuffer_object. + */ switch (internalFormat) { - case GL_RGBA16_SNORM: - /* This is used internally by Mesa for accum buffers. */ + case GL_ALPHA: + case GL_ALPHA4: + case GL_ALPHA8: + case GL_ALPHA12: + case GL_ALPHA16: + return (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_framebuffer_object) ? GL_ALPHA : 0; + case GL_LUMINANCE: + case GL_LUMINANCE4: + case GL_LUMINANCE8: + case GL_LUMINANCE12: + case GL_LUMINANCE16: + return (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_framebuffer_object) ? GL_LUMINANCE : 0; + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + return (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_framebuffer_object) ? GL_LUMINANCE_ALPHA : 0; + case GL_INTENSITY: + case GL_INTENSITY4: + case GL_INTENSITY8: + case GL_INTENSITY12: + case GL_INTENSITY16: + return (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_framebuffer_object) ? GL_INTENSITY : 0; + case GL_RGB8: + return GL_RGB; + case GL_RGB: + case GL_R3_G3_B2: + case GL_RGB4: + case GL_RGB5: + case GL_RGB10: + case GL_RGB12: + case GL_RGB16: + return _mesa_is_desktop_gl(ctx) ? GL_RGB : 0; + case GL_SRGB8_EXT: + return _mesa_is_desktop_gl(ctx) ? GL_RGB : 0; + case GL_RGBA4: + case GL_RGB5_A1: + case GL_RGBA8: return GL_RGBA; + case GL_RGBA: + case GL_RGBA2: + case GL_RGBA12: + case GL_RGBA16: + return _mesa_is_desktop_gl(ctx) ? GL_RGBA : 0; + case GL_RGB10_A2: + case GL_SRGB8_ALPHA8_EXT: + return _mesa_is_desktop_gl(ctx) || _mesa_is_gles3(ctx) ? GL_RGBA : 0; case GL_STENCIL_INDEX: case GL_STENCIL_INDEX1_EXT: case GL_STENCIL_INDEX4_EXT: - case GL_STENCIL_INDEX8_EXT: case GL_STENCIL_INDEX16_EXT: - /* This is not a valid texture internalFormat, but valid for - * renderbuffers. + /* There are extensions for GL_STENCIL_INDEX1 and GL_STENCIL_INDEX4 in + * OpenGL ES, but Mesa does not currently support them. */ + return _mesa_is_desktop_gl(ctx) ? GL_STENCIL_INDEX : 0; + case GL_STENCIL_INDEX8_EXT: return GL_STENCIL_INDEX; case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT32: + return _mesa_is_desktop_gl(ctx) ? GL_DEPTH_COMPONENT : 0; case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT24: - case GL_DEPTH_COMPONENT32: - /* This is an override of _mesa_base_tex_format's check that - * ARB_depth_texture is present. We allow depth RBs without it. - */ return GL_DEPTH_COMPONENT; - } + case GL_DEPTH_STENCIL: + return _mesa_is_desktop_gl(ctx) ? GL_DEPTH_STENCIL : 0; + case GL_DEPTH24_STENCIL8: + return GL_DEPTH_STENCIL; + case GL_DEPTH_COMPONENT32F: + return ctx->Version >= 30 + || (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_depth_buffer_float) + ? GL_DEPTH_COMPONENT : 0; + case GL_DEPTH32F_STENCIL8: + return ctx->Version >= 30 + || (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_depth_buffer_float) + ? GL_DEPTH_STENCIL : 0; + case GL_RED: + case GL_R16: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_rg + ? GL_RED : 0; + case GL_R8: + return ctx->API != API_OPENGLES && ctx->Extensions.ARB_texture_rg + ? GL_RED : 0; + case GL_RG: + case GL_RG16: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_rg + ? GL_RG : 0; + case GL_RG8: + return ctx->API != API_OPENGLES && ctx->Extensions.ARB_texture_rg + ? GL_RG : 0; + /* signed normalized texture formats */ + case GL_RED_SNORM: + case GL_R8_SNORM: + case GL_R16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_RED : 0; + case GL_RG_SNORM: + case GL_RG8_SNORM: + case GL_RG16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_RG : 0; + case GL_RGB_SNORM: + case GL_RGB8_SNORM: + case GL_RGB16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_RGB : 0; + case GL_RGBA_SNORM: + case GL_RGBA8_SNORM: + case GL_RGBA16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_RGBA : 0; + case GL_ALPHA_SNORM: + case GL_ALPHA8_SNORM: + case GL_ALPHA16_SNORM: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_snorm && + ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; + case GL_LUMINANCE_SNORM: + case GL_LUMINANCE8_SNORM: + case GL_LUMINANCE16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_LUMINANCE : 0; + case GL_LUMINANCE_ALPHA_SNORM: + case GL_LUMINANCE8_ALPHA8_SNORM: + case GL_LUMINANCE16_ALPHA16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_LUMINANCE_ALPHA : 0; + case GL_INTENSITY_SNORM: + case GL_INTENSITY8_SNORM: + case GL_INTENSITY16_SNORM: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_INTENSITY : 0; + + case GL_R16F: + case GL_R32F: + return ((_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rg && + ctx->Extensions.ARB_texture_float) || + _mesa_is_gles3(ctx) /* EXT_color_buffer_float */ ) + ? GL_RED : 0; + case GL_RG16F: + case GL_RG32F: + return ((_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rg && + ctx->Extensions.ARB_texture_float) || + _mesa_is_gles3(ctx) /* EXT_color_buffer_float */ ) + ? GL_RG : 0; + case GL_RGB16F: + case GL_RGB32F: + return (_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_float) + ? GL_RGB : 0; + case GL_RGBA16F: + case GL_RGBA32F: + return ((_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_float) || + _mesa_is_gles3(ctx) /* EXT_color_buffer_float */ ) + ? GL_RGBA : 0; + case GL_ALPHA16F_ARB: + case GL_ALPHA32F_ARB: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_texture_float && + ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; + case GL_LUMINANCE16F_ARB: + case GL_LUMINANCE32F_ARB: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_texture_float && + ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; + case GL_LUMINANCE_ALPHA16F_ARB: + case GL_LUMINANCE_ALPHA32F_ARB: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_texture_float && + ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; + case GL_INTENSITY16F_ARB: + case GL_INTENSITY32F_ARB: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_texture_float && + ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; + case GL_R11F_G11F_B10F: + return ((_mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_packed_float) || + _mesa_is_gles3(ctx) /* EXT_color_buffer_float */ ) + ? GL_RGB : 0; + + case GL_RGBA8UI_EXT: + case GL_RGBA16UI_EXT: + case GL_RGBA32UI_EXT: + case GL_RGBA8I_EXT: + case GL_RGBA16I_EXT: + case GL_RGBA32I_EXT: + return ctx->Version >= 30 + || (_mesa_is_desktop_gl(ctx) && + ctx->Extensions.EXT_texture_integer) ? GL_RGBA : 0; + + case GL_RGB8UI_EXT: + case GL_RGB16UI_EXT: + case GL_RGB32UI_EXT: + case GL_RGB8I_EXT: + case GL_RGB16I_EXT: + case GL_RGB32I_EXT: + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_integer + ? GL_RGB : 0; + case GL_R8UI: + case GL_R8I: + case GL_R16UI: + case GL_R16I: + case GL_R32UI: + case GL_R32I: + return ctx->Version >= 30 + || (_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rg && + ctx->Extensions.EXT_texture_integer) ? GL_RED : 0; + + case GL_RG8UI: + case GL_RG8I: + case GL_RG16UI: + case GL_RG16I: + case GL_RG32UI: + case GL_RG32I: + return ctx->Version >= 30 + || (_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rg && + ctx->Extensions.EXT_texture_integer) ? GL_RG : 0; + + case GL_INTENSITY8I_EXT: + case GL_INTENSITY8UI_EXT: + case GL_INTENSITY16I_EXT: + case GL_INTENSITY16UI_EXT: + case GL_INTENSITY32I_EXT: + case GL_INTENSITY32UI_EXT: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && + ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; + + case GL_LUMINANCE8I_EXT: + case GL_LUMINANCE8UI_EXT: + case GL_LUMINANCE16I_EXT: + case GL_LUMINANCE16UI_EXT: + case GL_LUMINANCE32I_EXT: + case GL_LUMINANCE32UI_EXT: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && + ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; + + case GL_LUMINANCE_ALPHA8I_EXT: + case GL_LUMINANCE_ALPHA8UI_EXT: + case GL_LUMINANCE_ALPHA16I_EXT: + case GL_LUMINANCE_ALPHA16UI_EXT: + case GL_LUMINANCE_ALPHA32I_EXT: + case GL_LUMINANCE_ALPHA32UI_EXT: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && + ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; + + case GL_ALPHA8I_EXT: + case GL_ALPHA8UI_EXT: + case GL_ALPHA16I_EXT: + case GL_ALPHA16UI_EXT: + case GL_ALPHA32I_EXT: + case GL_ALPHA32UI_EXT: + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && + ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; + + case GL_RGB10_A2UI: + return (_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rgb10_a2ui) + || _mesa_is_gles3(ctx) ? GL_RGBA : 0; - baseFormat = _mesa_base_tex_format(ctx, internalFormat); - if (baseFormat < 0) + case GL_RGB565: + return _mesa_is_gles(ctx) || ctx->Extensions.ARB_ES2_compatibility + ? GL_RGB : 0; + default: return 0; - - return baseFormat; + } } -/** sentinal value, see below */ -#define NO_SAMPLES 1000 - - /** - * Helper function used by _mesa_RenderbufferStorageEXT() and - * _mesa_RenderbufferStorageMultisample(). - * samples will be NO_SAMPLES if called by _mesa_RenderbufferStorageEXT(). + * Invalidate a renderbuffer attachment. Called from _mesa_HashWalk(). */ static void -renderbuffer_storage(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei samples) +invalidate_rb(GLuint key, void *data, void *userData) { - const char *func = samples == NO_SAMPLES ? - "glRenderbufferStorage" : "RenderbufferStorageMultisample"; - struct gl_renderbuffer *rb; - GLenum baseFormat; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (target != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", func); - return; - } - - baseFormat = _mesa_base_fbo_format(ctx, internalFormat); - if (baseFormat == 0) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat)", func); - return; - } + struct gl_framebuffer *fb = (struct gl_framebuffer *) data; + struct gl_renderbuffer *rb = (struct gl_renderbuffer *) userData; - if (baseFormat != GL_DEPTH_COMPONENT && - baseFormat != GL_STENCIL_INDEX && - baseFormat != GL_DEPTH_STENCIL && - !_mesa_is_legal_color_format(ctx, baseFormat)) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat)", func); - return; - } - - if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(width)", func); - return; + /* If this is a user-created FBO */ + if (_mesa_is_user_fbo(fb)) { + GLuint i; + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = fb->Attachment + i; + if (att->Type == GL_RENDERBUFFER && + att->Renderbuffer == rb) { + /* Mark fb status as indeterminate to force re-validation */ + fb->_Status = 0; + return; + } + } } +} - if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(height)", func); - return; - } - if (samples == NO_SAMPLES) { - /* NumSamples == 0 indicates non-multisampling */ - samples = 0; - } - else if (samples > (GLsizei) ctx->Const.MaxSamples) { - /* note: driver may choose to use more samples than what's requested */ - _mesa_error(ctx, GL_INVALID_VALUE, "%s(samples)", func); - return; - } +/** sentinal value, see below */ +#define NO_SAMPLES 1000 - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, "%s", func); - return; +void +_mesa_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei samples) +{ + const GLenum baseFormat = _mesa_base_fbo_format(ctx, internalFormat); + + assert(baseFormat != 0); + assert(width >= 0 && width <= (GLsizei) ctx->Const.MaxRenderbufferSize); + assert(height >= 0 && height <= (GLsizei) ctx->Const.MaxRenderbufferSize); + assert(samples != NO_SAMPLES); + if (samples != 0) { + assert(samples > 0); + assert(_mesa_check_sample_count(ctx, GL_RENDERBUFFER, + internalFormat, samples) == GL_NO_ERROR); } FLUSH_VERTICES(ctx, _NEW_BUFFERS); if (rb->InternalFormat == internalFormat && rb->Width == (GLuint) width && - rb->Height == (GLuint) height) { + rb->Height == (GLuint) height && + rb->NumSamples == samples) { /* no change in allocation needed */ return; } @@ -1079,10 +2049,10 @@ renderbuffer_storage(GLenum target, GLenum internalFormat, rb->NumSamples = samples; /* Now allocate the storage */ - ASSERT(rb->AllocStorage); + assert(rb->AllocStorage); if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { /* No error - check/set fields now */ - assert(rb->Format != MESA_FORMAT_NONE); + /* If rb->Format == MESA_FORMAT_NONE, the format is unsupported. */ assert(rb->Width == (GLuint) width); assert(rb->Height == (GLuint) height); rb->InternalFormat = internalFormat; @@ -1099,115 +2069,211 @@ renderbuffer_storage(GLenum target, GLenum internalFormat, rb->NumSamples = 0; } - /* - test_framebuffer_completeness(ctx, fb); - */ - /* XXX if this renderbuffer is attached anywhere, invalidate attachment - * points??? - */ + /* Invalidate the framebuffers the renderbuffer is attached in. */ + if (rb->AttachedAnytime) { + _mesa_HashWalk(ctx->Shared->FrameBuffers, invalidate_rb, rb); + } } - -#if FEATURE_OES_EGL_image -void GLAPIENTRY -_mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) +/** + * Helper function used by renderbuffer_storage_direct() and + * renderbuffer_storage_target(). + * samples will be NO_SAMPLES if called by a non-multisample function. + */ +static void +renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei samples, const char *func) { - struct gl_renderbuffer *rb; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); + GLenum baseFormat; + GLenum sample_count_error; - if (!ctx->Extensions.OES_EGL_image) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glEGLImageTargetRenderbufferStorageOES(unsupported)"); + baseFormat = _mesa_base_fbo_format(ctx, internalFormat); + if (baseFormat == 0) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat=%s)", + func, _mesa_enum_to_string(internalFormat)); return; } - if (target != GL_RENDERBUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, - "EGLImageTargetRenderbufferStorageOES"); + if (width < 0 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid width %d)", func, + width); return; } - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "EGLImageTargetRenderbufferStorageOES"); + if (height < 0 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid height %d)", func, + height); return; } - FLUSH_VERTICES(ctx, _NEW_BUFFERS); + if (samples == NO_SAMPLES) { + /* NumSamples == 0 indicates non-multisampling */ + samples = 0; + } + else { + /* check the sample count; + * note: driver may choose to use more samples than what's requested + */ + sample_count_error = _mesa_check_sample_count(ctx, GL_RENDERBUFFER, + internalFormat, samples); - ctx->Driver.EGLImageTargetRenderbufferStorage(ctx, rb, image); -} -#endif + /* Section 2.5 (GL Errors) of OpenGL 3.0 specification, page 16: + * + * "If a negative number is provided where an argument of type sizei or + * sizeiptr is specified, the error INVALID VALUE is generated." + */ + if (samples < 0) { + sample_count_error = GL_INVALID_VALUE; + } + + if (sample_count_error != GL_NO_ERROR) { + _mesa_error(ctx, sample_count_error, "%s(samples=%d)", func, samples); + return; + } + } + + _mesa_renderbuffer_storage(ctx, rb, internalFormat, width, height, samples); +} + +/** + * Helper function used by _mesa_NamedRenderbufferStorage*(). + * samples will be NO_SAMPLES if called by a non-multisample function. + */ +static void +renderbuffer_storage_named(GLuint renderbuffer, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei samples, + const char *func) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_API) { + if (samples == NO_SAMPLES) + _mesa_debug(ctx, "%s(%u, %s, %d, %d)\n", + func, renderbuffer, + _mesa_enum_to_string(internalFormat), + width, height); + else + _mesa_debug(ctx, "%s(%u, %s, %d, %d, %d)\n", + func, renderbuffer, + _mesa_enum_to_string(internalFormat), + width, height, samples); + } + + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (!rb || rb == &DummyRenderbuffer) { + /* ID was reserved, but no real renderbuffer object made yet */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid renderbuffer %u)", + func, renderbuffer); + return; + } + + renderbuffer_storage(ctx, rb, internalFormat, width, height, samples, func); +} + +/** + * Helper function used by _mesa_RenderbufferStorage() and + * _mesa_RenderbufferStorageMultisample(). + * samples will be NO_SAMPLES if called by _mesa_RenderbufferStorage(). + */ +static void +renderbuffer_storage_target(GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei samples, + const char *func) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_API) { + if (samples == NO_SAMPLES) + _mesa_debug(ctx, "%s(%s, %s, %d, %d)\n", + func, + _mesa_enum_to_string(target), + _mesa_enum_to_string(internalFormat), + width, height); + else + _mesa_debug(ctx, "%s(%s, %s, %d, %d, %d)\n", + func, + _mesa_enum_to_string(target), + _mesa_enum_to_string(internalFormat), + width, height, samples); + } + + if (target != GL_RENDERBUFFER_EXT) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", func); + return; + } + + if (!ctx->CurrentRenderbuffer) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(no renderbuffer bound)", + func); + return; + } + + renderbuffer_storage(ctx, ctx->CurrentRenderbuffer, internalFormat, width, + height, samples, func); +} + + +void GLAPIENTRY +_mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) +{ + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + + if (!ctx->Extensions.OES_EGL_image) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glEGLImageTargetRenderbufferStorageOES(unsupported)"); + return; + } + + if (target != GL_RENDERBUFFER) { + _mesa_error(ctx, GL_INVALID_ENUM, + "EGLImageTargetRenderbufferStorageOES"); + return; + } + + rb = ctx->CurrentRenderbuffer; + if (!rb) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "EGLImageTargetRenderbufferStorageOES"); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + ctx->Driver.EGLImageTargetRenderbufferStorage(ctx, rb, image); +} /** - * Helper function for _mesa_GetRenderbufferParameterivEXT() and - * _mesa_GetFramebufferAttachmentParameterivEXT() + * Helper function for _mesa_GetRenderbufferParameteriv() and + * _mesa_GetFramebufferAttachmentParameteriv() * We have to be careful to respect the base format. For example, if a * renderbuffer/texture was created with internalFormat=GL_RGB but the * driver actually chose a GL_RGBA format, when the user queries ALPHA_SIZE * we need to return zero. */ static GLint -get_component_bits(GLenum pname, GLenum baseFormat, gl_format format) +get_component_bits(GLenum pname, GLenum baseFormat, mesa_format format) { - switch (pname) { - case GL_RENDERBUFFER_RED_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA || - baseFormat == GL_RG || baseFormat == GL_RED) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_GREEN_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA || baseFormat == GL_RG) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_BLUE_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_ALPHA_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: - if (baseFormat == GL_RGBA || baseFormat == GL_ALPHA || - baseFormat == GL_LUMINANCE_ALPHA) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_DEPTH_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: - if (baseFormat == GL_DEPTH_COMPONENT || baseFormat == GL_DEPTH_STENCIL) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_STENCIL_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - if (baseFormat == GL_STENCIL_INDEX || baseFormat == GL_DEPTH_STENCIL) - return _mesa_get_format_bits(format, pname); - else - return 0; - default: + if (_mesa_base_format_has_channel(baseFormat, pname)) + return _mesa_get_format_bits(format, pname); + else return 0; - } } void GLAPIENTRY -_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, +_mesa_RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height) { /* GL_ARB_fbo says calling this function is equivalent to calling * glRenderbufferStorageMultisample() with samples=0. We pass in * a token value here just for error reporting purposes. */ - renderbuffer_storage(target, internalFormat, width, height, NO_SAMPLES); + renderbuffer_storage_target(target, internalFormat, width, height, + NO_SAMPLES, "glRenderbufferStorage"); } @@ -1216,7 +2282,8 @@ _mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height) { - renderbuffer_storage(target, internalFormat, width, height, samples); + renderbuffer_storage_target(target, internalFormat, width, height, + samples, "glRenderbufferStorageMultisample"); } @@ -1225,7 +2292,7 @@ _mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples, */ void GLAPIENTRY _es_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height) + GLsizei width, GLsizei height) { switch (internalFormat) { case GL_RGB565: @@ -1237,31 +2304,38 @@ _es_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, break; } - renderbuffer_storage(target, internalFormat, width, height, 0); + renderbuffer_storage_target(target, internalFormat, width, height, 0, + "glRenderbufferStorageEXT"); } - void GLAPIENTRY -_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) +_mesa_NamedRenderbufferStorage(GLuint renderbuffer, GLenum internalformat, + GLsizei width, GLsizei height) { - struct gl_renderbuffer *rb; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); + /* GL_ARB_fbo says calling this function is equivalent to calling + * glRenderbufferStorageMultisample() with samples=0. We pass in + * a token value here just for error reporting purposes. + */ + renderbuffer_storage_named(renderbuffer, internalformat, width, height, + NO_SAMPLES, "glNamedRenderbufferStorage"); +} - if (target != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetRenderbufferParameterivEXT(target)"); - return; - } +void GLAPIENTRY +_mesa_NamedRenderbufferStorageMultisample(GLuint renderbuffer, GLsizei samples, + GLenum internalformat, + GLsizei width, GLsizei height) +{ + renderbuffer_storage_named(renderbuffer, internalformat, width, height, + samples, + "glNamedRenderbufferStorageMultisample"); +} - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetRenderbufferParameterivEXT"); - return; - } +static void +get_render_buffer_parameteriv(struct gl_context *ctx, + struct gl_renderbuffer *rb, GLenum pname, + GLint *params, const char *func) +{ /* No need to flush here since we're just quering state which is * not effected by rendering. */ @@ -1285,21 +2359,63 @@ _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) *params = get_component_bits(pname, rb->_BaseFormat, rb->Format); break; case GL_RENDERBUFFER_SAMPLES: - if (ctx->Extensions.ARB_framebuffer_object) { + if ((_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_framebuffer_object) + || _mesa_is_gles3(ctx)) { *params = rb->NumSamples; break; } /* fallthrough */ default: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid pname=%s)", func, + _mesa_enum_to_string(pname)); + return; + } +} + + +void GLAPIENTRY +_mesa_GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + + if (target != GL_RENDERBUFFER_EXT) { _mesa_error(ctx, GL_INVALID_ENUM, "glGetRenderbufferParameterivEXT(target)"); return; } + + if (!ctx->CurrentRenderbuffer) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetRenderbufferParameterivEXT" + "(no renderbuffer bound)"); + return; + } + + get_render_buffer_parameteriv(ctx, ctx->CurrentRenderbuffer, pname, + params, "glGetRenderbufferParameteriv"); +} + + +void GLAPIENTRY +_mesa_GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, + GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (!rb || rb == &DummyRenderbuffer) { + /* ID was reserved, but no real renderbuffer object made yet */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetNamedRenderbufferParameteriv" + "(invalid renderbuffer %i)", renderbuffer); + return; + } + + get_render_buffer_parameteriv(ctx, rb, pname, params, + "glGetNamedRenderbufferParameteriv"); } GLboolean GLAPIENTRY -_mesa_IsFramebufferEXT(GLuint framebuffer) +_mesa_IsFramebuffer(GLuint framebuffer) { GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); @@ -1321,16 +2437,15 @@ static void check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) { GLuint i; - ASSERT(ctx->Driver.RenderTexture); + assert(ctx->Driver.RenderTexture); - if (fb->Name == 0) + if (_mesa_is_winsys_fbo(fb)) return; /* can't render to texture with winsys framebuffers */ for (i = 0; i < BUFFER_COUNT; i++) { struct gl_renderbuffer_attachment *att = fb->Attachment + i; - struct gl_texture_object *texObj = att->Texture; - if (texObj - && texObj->Image[att->CubeMapFace][att->TextureLevel]) { + if (att->Texture && att->Renderbuffer->TexImage + && driver_RenderTexture_is_safe(att)) { ctx->Driver.RenderTexture(ctx, fb, att); } } @@ -1345,63 +2460,39 @@ check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) static void check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) { - if (fb->Name == 0) - return; /* can't render to texture with winsys framebuffers */ + /* Skip if we know NeedsFinishRenderTexture won't be set. */ + if (_mesa_is_winsys_fbo(fb) && !ctx->Driver.BindRenderbufferTexImage) + return; 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); + struct gl_renderbuffer *rb = att->Renderbuffer; + if (rb && rb->NeedsFinishRenderTexture) { + ctx->Driver.FinishRenderTexture(ctx, rb); } } } } -void GLAPIENTRY -_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) +static void +bind_framebuffer(GLenum target, GLuint framebuffer, bool allow_user_names) { struct gl_framebuffer *newDrawFb, *newReadFb; - struct gl_framebuffer *oldDrawFb, *oldReadFb; GLboolean bindReadBuf, bindDrawBuf; GET_CURRENT_CONTEXT(ctx); -#ifdef DEBUG - if (ctx->Extensions.ARB_framebuffer_object) { - ASSERT(ctx->Extensions.EXT_framebuffer_object); - ASSERT(ctx->Extensions.EXT_framebuffer_blit); - } -#endif - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - 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; @@ -1418,19 +2509,19 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) /* ID was reserved, but no real framebuffer object made yet */ newDrawFb = NULL; } - else if (!newDrawFb && ctx->Extensions.ARB_framebuffer_object) { + else if (!newDrawFb && !allow_user_names) { /* All FBO IDs must be Gen'd */ _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFramebuffer(buffer)"); return; } if (!newDrawFb) { - /* create new framebuffer object */ - newDrawFb = ctx->Driver.NewFramebuffer(ctx, framebuffer); - if (!newDrawFb) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT"); - return; - } + /* create new framebuffer object */ + newDrawFb = ctx->Driver.NewFramebuffer(ctx, framebuffer); + if (!newDrawFb) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT"); + return; + } _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newDrawFb); } newReadFb = newDrawFb; @@ -1443,18 +2534,23 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) newReadFb = ctx->WinSysReadBuffer; } - ASSERT(newDrawFb); - ASSERT(newDrawFb != &DummyFramebuffer); + _mesa_bind_framebuffers(ctx, + bindDrawBuf ? newDrawFb : ctx->DrawBuffer, + bindReadBuf ? newReadFb : ctx->ReadBuffer); +} - /* save pointers to current/old framebuffers */ - oldDrawFb = ctx->DrawBuffer; - oldReadFb = ctx->ReadBuffer; +void +_mesa_bind_framebuffers(struct gl_context *ctx, + struct gl_framebuffer *newDrawFb, + struct gl_framebuffer *newReadFb) +{ + struct gl_framebuffer *const oldDrawFb = ctx->DrawBuffer; + struct gl_framebuffer *const oldReadFb = ctx->ReadBuffer; + const bool bindDrawBuf = oldDrawFb != newDrawFb; + const bool bindReadBuf = oldReadFb != newReadFb; - /* check if really changing bindings */ - if (oldDrawFb == newDrawFb) - bindDrawBuf = GL_FALSE; - if (oldReadFb == newReadFb) - bindReadBuf = GL_FALSE; + assert(newDrawFb); + assert(newDrawFb != &DummyFramebuffer); /* * OK, now bind the new Draw/Read framebuffers, if they're changing. @@ -1480,11 +2576,8 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) if (bindDrawBuf) { FLUSH_VERTICES(ctx, _NEW_BUFFERS); - /* check if old read/draw buffers were render-to-texture */ - if (!bindReadBuf) - check_end_texture_render(ctx, oldReadFb); - - if (oldDrawFb != oldReadFb) + /* check if old framebuffer had any texture attachments */ + if (oldDrawFb) check_end_texture_render(ctx, oldDrawFb); /* check if newly bound framebuffer has any texture attachments */ @@ -1494,132 +2587,161 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) } if ((bindDrawBuf || bindReadBuf) && ctx->Driver.BindFramebuffer) { - ctx->Driver.BindFramebuffer(ctx, target, newDrawFb, newReadFb); + /* The few classic drivers that actually hook this function really only + * want to know if the draw framebuffer changed. + */ + ctx->Driver.BindFramebuffer(ctx, + bindDrawBuf ? GL_FRAMEBUFFER : GL_READ_FRAMEBUFFER, + newDrawFb, newReadFb); } } +void GLAPIENTRY +_mesa_BindFramebuffer(GLenum target, GLuint framebuffer) +{ + GET_CURRENT_CONTEXT(ctx); + + /* OpenGL ES glBindFramebuffer and glBindFramebufferOES use this same entry + * point, but they allow the use of user-generated names. + */ + bind_framebuffer(target, framebuffer, _mesa_is_gles(ctx)); +} + + +void GLAPIENTRY +_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) +{ + /* This function should not be in the dispatch table for core profile / + * OpenGL 3.1, so execution should never get here in those cases -- no + * need for an explicit test. + */ + bind_framebuffer(target, framebuffer, true); +} + void GLAPIENTRY -_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) +_mesa_DeleteFramebuffers(GLsizei n, const GLuint *framebuffers) { GLint i; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteFramebuffers(n < 0)"); + return; + } + FLUSH_VERTICES(ctx, _NEW_BUFFERS); for (i = 0; i < n; i++) { if (framebuffers[i] > 0) { - struct gl_framebuffer *fb; - fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]); - if (fb) { - ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]); + struct gl_framebuffer *fb; + fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]); + if (fb) { + assert(fb == &DummyFramebuffer || fb->Name == framebuffers[i]); /* check if deleting currently bound framebuffer object */ - if (ctx->Extensions.EXT_framebuffer_blit) { - /* separate draw/read binding points */ - if (fb == ctx->DrawBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); - } - if (fb == ctx->ReadBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); - } + if (fb == ctx->DrawBuffer) { + /* bind default */ + assert(fb->RefCount >= 2); + _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } - else { - /* only one binding point for read/draw buffers */ - if (fb == ctx->DrawBuffer || fb == ctx->ReadBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - } + if (fb == ctx->ReadBuffer) { + /* bind default */ + assert(fb->RefCount >= 2); + _mesa_BindFramebuffer(GL_READ_FRAMEBUFFER, 0); } - /* remove from hash table immediately, to free the ID */ - _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]); + /* remove from hash table immediately, to free the ID */ + _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]); if (fb != &DummyFramebuffer) { /* But the object will not be freed until it's no longer * bound in any context. */ _mesa_reference_framebuffer(&fb, NULL); - } - } + } + } } } } -void GLAPIENTRY -_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers) +/** + * This is the implementation for glGenFramebuffers and glCreateFramebuffers. + * It is not exposed to the rest of Mesa to encourage the use of + * nameless buffers in driver internals. + */ +static void +create_framebuffers(GLsizei n, GLuint *framebuffers, bool dsa) { GET_CURRENT_CONTEXT(ctx); GLuint first; GLint i; + struct gl_framebuffer *fb; - ASSERT_OUTSIDE_BEGIN_END(ctx); + const char *func = dsa ? "glCreateFramebuffers" : "glGenFramebuffers"; if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)"); + _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); return; } if (!framebuffers) return; + _mesa_HashLockMutex(ctx->Shared->FrameBuffers); + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n); for (i = 0; i < n; i++) { GLuint name = first + i; framebuffers[i] = name; - /* insert dummy placeholder into hash table */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer); - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + + if (dsa) { + fb = ctx->Driver.NewFramebuffer(ctx, framebuffers[i]); + if (!fb) { + _mesa_HashUnlockMutex(ctx->Shared->FrameBuffers); + _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); + return; + } + } + else + fb = &DummyFramebuffer; + + _mesa_HashInsertLocked(ctx->Shared->FrameBuffers, name, fb); } + + _mesa_HashUnlockMutex(ctx->Shared->FrameBuffers); } +void GLAPIENTRY +_mesa_GenFramebuffers(GLsizei n, GLuint *framebuffers) +{ + create_framebuffers(n, framebuffers, false); +} -GLenum GLAPIENTRY -_mesa_CheckFramebufferStatusEXT(GLenum target) + +void GLAPIENTRY +_mesa_CreateFramebuffers(GLsizei n, GLuint *framebuffers) { - struct gl_framebuffer *buffer; - GET_CURRENT_CONTEXT(ctx); + create_framebuffers(n, framebuffers, true); +} + +GLenum +_mesa_check_framebuffer_status(struct gl_context *ctx, + struct gl_framebuffer *buffer) +{ ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - 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; + if (_mesa_is_winsys_fbo(buffer)) { + /* EGL_KHR_surfaceless_context allows the winsys FBO to be incomplete. */ + if (buffer != &IncompleteFramebuffer) { + return GL_FRAMEBUFFER_COMPLETE_EXT; + } else { + return GL_FRAMEBUFFER_UNDEFINED; } - 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 (buffer->Name == 0) { - /* The window system / default framebuffer is always complete */ - return GL_FRAMEBUFFER_COMPLETE_EXT; } /* No need to flush here */ @@ -1632,126 +2754,441 @@ _mesa_CheckFramebufferStatusEXT(GLenum target) } - -/** - * Common code called by glFramebufferTexture1D/2D/3DEXT(). - */ -static void -framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, - GLenum attachment, GLenum textarget, GLuint texture, - GLint level, GLint zoffset) +GLenum GLAPIENTRY +_mesa_CheckFramebufferStatus(GLenum target) { - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj = NULL; struct gl_framebuffer *fb; - GLboolean error = GL_FALSE; - - ASSERT_OUTSIDE_BEGIN_END(ctx); + GET_CURRENT_CONTEXT(ctx); - switch (target) { - case GL_READ_FRAMEBUFFER_EXT: - error = !ctx->Extensions.EXT_framebuffer_blit; - fb = ctx->ReadBuffer; - break; - case GL_DRAW_FRAMEBUFFER_EXT: - error = !ctx->Extensions.EXT_framebuffer_blit; - /* fall-through */ - case GL_FRAMEBUFFER_EXT: - fb = ctx->DrawBuffer; - break; - default: - error = GL_TRUE; - } + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCheckFramebufferStatus(%s)\n", + _mesa_enum_to_string(target)); - if (error) { + fb = get_framebuffer_target(ctx, target); + if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%sEXT(target=0x%x)", caller, target); - return; + "glCheckFramebufferStatus(invalid target %s)", + _mesa_enum_to_string(target)); + return 0; } - ASSERT(fb); - - /* check framebuffer binding */ - if (fb->Name == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT", caller); - return; - } + return _mesa_check_framebuffer_status(ctx, fb); +} - /* The textarget, level, and zoffset parameters are only validated if - * texture is non-zero. - */ - if (texture) { - GLboolean err = GL_TRUE; - - texObj = _mesa_lookup_texture(ctx, texture); - if (texObj != NULL) { - if (textarget == 0) { - /* XXX what's the purpose of this? */ - 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); - } +GLenum GLAPIENTRY +_mesa_CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + /* Validate the target (for conformance's sake) and grab a reference to the + * default framebuffer in case framebuffer = 0. + * Section 9.4 Framebuffer Completeness of the OpenGL 4.5 core spec + * (30.10.2014, PDF page 336) says: + * "If framebuffer is zero, then the status of the default read or + * draw framebuffer (as determined by target) is returned." + */ + switch (target) { + case GL_DRAW_FRAMEBUFFER: + case GL_FRAMEBUFFER: + fb = ctx->WinSysDrawBuffer; + break; + case GL_READ_FRAMEBUFFER: + fb = ctx->WinSysReadBuffer; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glCheckNamedFramebufferStatus(invalid target %s)", + _mesa_enum_to_string(target)); + return 0; + } + + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glCheckNamedFramebufferStatus"); + if (!fb) + return 0; + } + + return _mesa_check_framebuffer_status(ctx, fb); +} + + +/** + * Replicate the src attachment point. Used by framebuffer_texture() when + * the same texture is attached at GL_DEPTH_ATTACHMENT and + * GL_STENCIL_ATTACHMENT. + */ +static void +reuse_framebuffer_texture_attachment(struct gl_framebuffer *fb, + gl_buffer_index dst, + gl_buffer_index src) +{ + struct gl_renderbuffer_attachment *dst_att = &fb->Attachment[dst]; + struct gl_renderbuffer_attachment *src_att = &fb->Attachment[src]; + + assert(src_att->Texture != NULL); + assert(src_att->Renderbuffer != NULL); + + _mesa_reference_texobj(&dst_att->Texture, src_att->Texture); + _mesa_reference_renderbuffer(&dst_att->Renderbuffer, src_att->Renderbuffer); + dst_att->Type = src_att->Type; + dst_att->Complete = src_att->Complete; + dst_att->TextureLevel = src_att->TextureLevel; + dst_att->Zoffset = src_att->Zoffset; + dst_att->Layered = src_att->Layered; +} + + +/** + * Common code called by gl*FramebufferTexture*() to retrieve the correct + * texture object pointer. + * + * \param texObj where the pointer to the texture object is returned. Note + * that a successful call may return texObj = NULL. + * + * \return true if no errors, false if errors + */ +static bool +get_texture_for_framebuffer(struct gl_context *ctx, GLuint texture, + bool layered, const char *caller, + struct gl_texture_object **texObj) +{ + *texObj = NULL; /* This will get returned if texture = 0. */ + + if (!texture) + return true; + + *texObj = _mesa_lookup_texture(ctx, texture); + if (*texObj == NULL || (*texObj)->Target == 0) { + /* Can't render to a non-existent texture object. + * + * The OpenGL 4.5 core spec (02.02.2015) in Section 9.2 Binding and + * Managing Framebuffer Objects specifies a different error + * depending upon the calling function (PDF pages 325-328). + * *FramebufferTexture (where layered = GL_TRUE) throws invalid + * value, while the other commands throw invalid operation (where + * layered = GL_FALSE). + */ + const GLenum error = layered ? GL_INVALID_VALUE : + GL_INVALID_OPERATION; + _mesa_error(ctx, error, + "%s(non-existent texture %u)", caller, texture); + return false; + } + + return true; +} + + +/** + * Common code called by gl*FramebufferTexture() to verify the texture target + * and decide whether or not the attachment should truly be considered + * layered. + * + * \param layered true if attachment should be considered layered, false if + * not + * + * \return true if no errors, false if errors + */ +static bool +check_layered_texture_target(struct gl_context *ctx, GLenum target, + const char *caller, GLboolean *layered) +{ + *layered = GL_TRUE; + + switch (target) { + case GL_TEXTURE_3D: + case GL_TEXTURE_1D_ARRAY_EXT: + case GL_TEXTURE_2D_ARRAY_EXT: + case GL_TEXTURE_CUBE_MAP: + case GL_TEXTURE_CUBE_MAP_ARRAY: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + return true; + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + case GL_TEXTURE_RECTANGLE: + case GL_TEXTURE_2D_MULTISAMPLE: + /* These texture types are valid to pass to + * glFramebufferTexture(), but since they aren't layered, it + * is equivalent to calling glFramebufferTexture{1D,2D}(). + */ + *layered = GL_FALSE; + return true; + } + + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid texture target %s)", caller, + _mesa_enum_to_string(target)); + return false; +} + + +/** + * Common code called by gl*FramebufferTextureLayer() to verify the texture + * target. + * + * \return true if no errors, false if errors + */ +static bool +check_texture_target(struct gl_context *ctx, GLenum target, + const char *caller) +{ + /* We're being called by glFramebufferTextureLayer(). + * The only legal texture types for that function are 3D, + * cube-map, and 1D/2D/cube-map array textures. + * + * We don't need to check for GL_ARB_texture_cube_map_array because the + * application wouldn't have been able to create a texture with a + * GL_TEXTURE_CUBE_MAP_ARRAY target if the extension were not enabled. + */ + switch (target) { + case GL_TEXTURE_3D: + case GL_TEXTURE_1D_ARRAY: + case GL_TEXTURE_2D_ARRAY: + case GL_TEXTURE_CUBE_MAP_ARRAY: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + return true; + case GL_TEXTURE_CUBE_MAP: + /* We don't need to check the extension (GL_ARB_direct_state_access) or + * GL version (4.5) for GL_TEXTURE_CUBE_MAP because DSA is always + * enabled in core profile. This can be called from + * _mesa_FramebufferTextureLayer in compatibility profile (OpenGL 3.0), + * so we do have to check the profile. + */ + return ctx->API == API_OPENGL_CORE; + } + + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid texture target %s)", caller, + _mesa_enum_to_string(target)); + return false; +} + + +/** + * Common code called by glFramebufferTexture*D() to verify the texture + * target. + * + * \return true if no errors, false if errors + */ +static bool +check_textarget(struct gl_context *ctx, int dims, GLenum target, + GLenum textarget, const char *caller) +{ + bool err = false; + + switch (dims) { + case 1: + switch (textarget) { + case GL_TEXTURE_1D: + break; + case GL_TEXTURE_1D_ARRAY: + err = !ctx->Extensions.EXT_texture_array; + break; + default: + err = true; } - else { - /* can't render to a non-existant texture */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(non existant texture)", - caller); - return; + break; + case 2: + switch (textarget) { + case GL_TEXTURE_2D: + break; + case GL_TEXTURE_RECTANGLE: + err = _mesa_is_gles(ctx) + || !ctx->Extensions.NV_texture_rectangle; + break; + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + err = !ctx->Extensions.ARB_texture_cube_map; + break; + case GL_TEXTURE_2D_ARRAY: + err = (_mesa_is_gles(ctx) && ctx->Version < 30) + || !ctx->Extensions.EXT_texture_array; + break; + case GL_TEXTURE_2D_MULTISAMPLE: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + err = (_mesa_is_gles(ctx) || + !ctx->Extensions.ARB_texture_multisample) && + !_mesa_is_gles31(ctx); + break; + default: + err = true; } + break; + case 3: + if (textarget != GL_TEXTURE_3D) + err = true; + break; + default: + err = true; + } - if (err) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(texture target mismatch)", - caller); - return; - } + if (err) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid textarget %s)", + caller, _mesa_enum_to_string(textarget)); + return false; + } - 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; - } + /* Make sure textarget is consistent with the texture's type */ + err = (target == GL_TEXTURE_CUBE_MAP) ? + !_mesa_is_cube_face(textarget): (target != textarget); + + if (err) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(mismatched texture target)", caller); + return false; + } + + return true; +} + + +/** + * Common code called by gl*FramebufferTextureLayer() and + * glFramebufferTexture3D() to validate the layer. + * + * \return true if no errors, false if errors + */ +static bool +check_layer(struct gl_context *ctx, GLenum target, GLint layer, + const char *caller) +{ + /* Page 306 (page 328 of the PDF) of the OpenGL 4.5 (Core Profile) + * spec says: + * + * "An INVALID_VALUE error is generated if texture is non-zero + * and layer is negative." + */ + if (layer < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(layer %u < 0)", caller, layer); + return false; + } + + if (target == GL_TEXTURE_3D) { + const GLuint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); + if (layer >= maxSize) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(invalid layer %u)", caller, layer); + return false; } - 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; - } + } + else if ((target == GL_TEXTURE_1D_ARRAY) || + (target == GL_TEXTURE_2D_ARRAY) || + (target == GL_TEXTURE_CUBE_MAP_ARRAY) || + (target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) { + if (layer >= ctx->Const.MaxArrayTextureLayers) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(layer %u >= GL_MAX_ARRAY_TEXTURE_LAYERS)", + caller, layer); + return false; } - - if ((level < 0) || - (level >= _mesa_max_texture_levels(ctx, texObj->Target))) { + } + else if (target == GL_TEXTURE_CUBE_MAP) { + if (layer >= 6) { _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%sEXT(level)", caller); - return; + "%s(layer %u >= 6)", caller, layer); + return false; } } - att = _mesa_get_attachment(ctx, fb, attachment); + return true; +} + + +/** + * Common code called by all gl*FramebufferTexture*() entry points to verify + * the level. + * + * \return true if no errors, false if errors + */ +static bool +check_level(struct gl_context *ctx, GLenum target, GLint level, + const char *caller) +{ + if ((level < 0) || + (level >= _mesa_max_texture_levels(ctx, target))) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(invalid level %d)", caller, level); + return false; + } + + return true; +} + + +void +_mesa_framebuffer_texture(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment, + struct gl_texture_object *texObj, GLenum textarget, + GLint level, GLuint layer, GLboolean layered, + const char *caller) +{ + struct gl_renderbuffer_attachment *att; + + /* The window-system framebuffer object is immutable */ + if (_mesa_is_winsys_fbo(fb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(window-system framebuffer)", + caller); + return; + } + + /* Not a hash lookup, so we can afford to get the attachment here. */ + att = get_attachment(ctx, fb, attachment); if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%sEXT(attachment)", caller); + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); return; } FLUSH_VERTICES(ctx, _NEW_BUFFERS); - _glthread_LOCK_MUTEX(fb->Mutex); + mtx_lock(&fb->Mutex); if (texObj) { - _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget, - level, zoffset); + if (attachment == GL_DEPTH_ATTACHMENT && + texObj == fb->Attachment[BUFFER_STENCIL].Texture && + level == fb->Attachment[BUFFER_STENCIL].TextureLevel && + _mesa_tex_target_to_face(textarget) == + fb->Attachment[BUFFER_STENCIL].CubeMapFace && + layer == fb->Attachment[BUFFER_STENCIL].Zoffset) { + /* The texture object is already attached to the stencil attachment + * point. Don't create a new renderbuffer; just reuse the stencil + * attachment's. This is required to prevent a GL error in + * glGetFramebufferAttachmentParameteriv(GL_DEPTH_STENCIL). + */ + reuse_framebuffer_texture_attachment(fb, BUFFER_DEPTH, + BUFFER_STENCIL); + } else if (attachment == GL_STENCIL_ATTACHMENT && + texObj == fb->Attachment[BUFFER_DEPTH].Texture && + level == fb->Attachment[BUFFER_DEPTH].TextureLevel && + _mesa_tex_target_to_face(textarget) == + fb->Attachment[BUFFER_DEPTH].CubeMapFace && + layer == fb->Attachment[BUFFER_DEPTH].Zoffset) { + /* As above, but with depth and stencil transposed. */ + reuse_framebuffer_texture_attachment(fb, BUFFER_STENCIL, + BUFFER_DEPTH); + } else { + set_texture_attachment(ctx, fb, att, texObj, textarget, + level, layer, layered); + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + /* Above we created a new renderbuffer and attached it to the + * depth attachment point. Now attach it to the stencil attachment + * point too. + */ + assert(att == &fb->Attachment[BUFFER_DEPTH]); + reuse_framebuffer_texture_attachment(fb,BUFFER_STENCIL, + BUFFER_DEPTH); + } + } + /* Set the render-to-texture flag. We'll check this flag in * glTexImage() and friends to determine if we need to revalidate * any FBOs that might be rendering into this texture. @@ -1763,250 +3200,493 @@ framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, texObj->_RenderToTexture = GL_TRUE; } else { - _mesa_remove_attachment(ctx, att); + remove_attachment(ctx, att); + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + assert(att == &fb->Attachment[BUFFER_DEPTH]); + remove_attachment(ctx, &fb->Attachment[BUFFER_STENCIL]); + } } invalidate_framebuffer(fb); - _glthread_UNLOCK_MUTEX(fb->Mutex); + mtx_unlock(&fb->Mutex); } +static void +framebuffer_texture_with_dims(int dims, GLenum target, + GLenum attachment, GLenum textarget, + GLuint texture, GLint level, GLint layer, + const char *caller) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + + /* Get the framebuffer object */ + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid target %s)", caller, + _mesa_enum_to_string(target)); + return; + } + + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, caller, &texObj)) + return; + + if (texObj) { + if (!check_textarget(ctx, dims, texObj->Target, textarget, caller)) + return; + + if ((dims == 3) && !check_layer(ctx, texObj->Target, layer, caller)) + return; + + if (!check_level(ctx, textarget, level, caller)) + return; + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, caller); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture1D(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level) +{ + framebuffer_texture_with_dims(1, target, attachment, textarget, texture, + level, 0, "glFramebufferTexture1D"); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture2D(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level) +{ + framebuffer_texture_with_dims(2, target, attachment, textarget, texture, + level, 0, "glFramebufferTexture2D"); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture3D(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint layer) +{ + framebuffer_texture_with_dims(3, target, attachment, textarget, texture, + level, layer, "glFramebufferTexture3D"); +} + void GLAPIENTRY -_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) +_mesa_FramebufferTextureLayer(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer) { GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLenum textarget = 0; - if ((texture != 0) && (textarget != GL_TEXTURE_1D)) { + const char *func = "glFramebufferTextureLayer"; + + /* Get the framebuffer object */ + fb = get_framebuffer_target(ctx, target); + if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture1DEXT(textarget)"); + "glFramebufferTextureLayer(invalid target %s)", + _mesa_enum_to_string(target)); return; } - framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, - level, 0); + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, func, &texObj)) + return; + + if (texObj) { + if (!check_texture_target(ctx, texObj->Target, func)) + return; + + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + + if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + assert(layer >= 0 && layer < 6); + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + layer = 0; + } + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, func); } void GLAPIENTRY -_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) +_mesa_NamedFramebufferTextureLayer(GLuint framebuffer, GLenum attachment, + GLuint texture, GLint level, GLint layer) { GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLenum textarget = 0; - if ((texture != 0) && - (textarget != GL_TEXTURE_2D) && - (textarget != GL_TEXTURE_RECTANGLE_ARB) && - (!IS_CUBE_FACE(textarget))) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture2DEXT(textarget=0x%x)", textarget); + const char *func = "glNamedFramebufferTextureLayer"; + + /* Get the framebuffer object */ + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); + if (!fb) return; + + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, func, &texObj)) + return; + + if (texObj) { + if (!check_texture_target(ctx, texObj->Target, func)) + return; + + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + + if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + assert(layer >= 0 && layer < 6); + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + layer = 0; + } } - framebuffer_texture(ctx, "2D", target, attachment, textarget, texture, - level, 0); + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, func); } void GLAPIENTRY -_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level, GLint zoffset) +_mesa_FramebufferTexture(GLenum target, GLenum attachment, + GLuint texture, GLint level) { GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLboolean layered = GL_FALSE; - if ((texture != 0) && (textarget != GL_TEXTURE_3D)) { + const char *func = "FramebufferTexture"; + + if (!_mesa_has_geometry_shaders(ctx)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "unsupported function (glFramebufferTexture) called"); + return; + } + + /* Get the framebuffer object */ + fb = get_framebuffer_target(ctx, target); + if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture3DEXT(textarget)"); + "glFramebufferTexture(invalid target %s)", + _mesa_enum_to_string(target)); return; } - framebuffer_texture(ctx, "3D", target, attachment, textarget, texture, - level, zoffset); + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, true, func, &texObj)) + return; + + if (texObj) { + if (!check_layered_texture_target(ctx, texObj->Target, func, &layered)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, + 0, layered, func); } void GLAPIENTRY -_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLint layer) +_mesa_NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, + GLuint texture, GLint level) { GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLboolean layered = GL_FALSE; - framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, - level, layer); + const char *func = "glNamedFramebufferTexture"; + + if (!_mesa_has_geometry_shaders(ctx)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "unsupported function (glNamedFramebufferTexture) called"); + return; + } + + /* Get the framebuffer object */ + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); + if (!fb) + return; + + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, true, func, &texObj)) + return; + + if (texObj) { + if (!check_layered_texture_target(ctx, texObj->Target, func, + &layered)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, + 0, layered, func); } -void GLAPIENTRY -_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, - GLenum renderbufferTarget, - GLuint renderbuffer) +void +_mesa_framebuffer_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLenum attachment, + struct gl_renderbuffer *rb) +{ + assert(!_mesa_is_winsys_fbo(fb)); + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + assert(ctx->Driver.FramebufferRenderbuffer); + ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); + + /* Some subsequent GL commands may depend on the framebuffer's visual + * after the binding is updated. Update visual info now. + */ + _mesa_update_framebuffer_visual(ctx, fb); +} + +static void +framebuffer_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLenum attachment, + struct gl_renderbuffer *rb, + const char *func) { struct gl_renderbuffer_attachment *att; + + if (_mesa_is_winsys_fbo(fb)) { + /* Can't attach new renderbuffers to a window system framebuffer */ + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(window-system framebuffer)", func); + return; + } + + att = get_attachment(ctx, fb, attachment); + if (att == NULL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(invalid attachment %s)", func, + _mesa_enum_to_string(attachment)); + return; + } + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT && + rb && rb->Format != MESA_FORMAT_NONE) { + /* make sure the renderbuffer is a depth/stencil format */ + const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); + if (baseFormat != GL_DEPTH_STENCIL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(renderbuffer is not DEPTH_STENCIL format)", func); + return; + } + } + + _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb); +} + +void GLAPIENTRY +_mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ struct gl_framebuffer *fb; struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbuffer(invalid target %s)", + _mesa_enum_to_string(target)); + 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, - "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: + if (renderbuffertarget != GL_RENDERBUFFER) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(target)"); + "glFramebufferRenderbuffer(renderbuffertarget is not " + "GL_RENDERBUFFER)"); return; } - if (renderbufferTarget != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(renderbufferTarget)"); - return; - } + if (renderbuffer) { + rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, + "glFramebufferRenderbuffer"); + if (!rb) + return; + } + else { + /* remove renderbuffer attachment */ + rb = NULL; + } + + framebuffer_renderbuffer(ctx, fb, attachment, rb, + "glFramebufferRenderbuffer"); +} - if (fb->Name == 0) { - /* Can't attach new renderbuffers to a window system framebuffer */ - _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT"); + +void GLAPIENTRY +_mesa_NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + struct gl_framebuffer *fb; + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glNamedFramebufferRenderbuffer"); + if (!fb) return; - } - att = _mesa_get_attachment(ctx, fb, attachment); - if (att == NULL) { + if (renderbuffertarget != GL_RENDERBUFFER) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(invalid attachment %s)", - _mesa_lookup_enum_by_nr(attachment)); + "glNamedFramebufferRenderbuffer(renderbuffertarget is not " + "GL_RENDERBUFFER)"); return; } if (renderbuffer) { - rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferRenderbufferEXT(non-existant" - " renderbuffer %u)", renderbuffer); - return; - } - else if (rb == &DummyRenderbuffer) { - /* This is what NVIDIA does */ - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferRenderbufferEXT(renderbuffer %u)", - renderbuffer); - return; - } + rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, + "glNamedFramebufferRenderbuffer"); + if (!rb) + return; } else { /* remove renderbuffer attachment */ rb = NULL; } - if (attachment == GL_DEPTH_STENCIL_ATTACHMENT && - rb && rb->Format != MESA_FORMAT_NONE) { - /* make sure the renderbuffer is a depth/stencil format */ - const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); - if (baseFormat != GL_DEPTH_STENCIL) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferRenderbufferEXT(renderbuffer" - " is not DEPTH_STENCIL format)"); - return; - } - } - - - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - assert(ctx->Driver.FramebufferRenderbuffer); - ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); - - /* Some subsequent GL commands may depend on the framebuffer's visual - * after the binding is updated. Update visual info now. - */ - _mesa_update_framebuffer_visual(ctx, fb); + framebuffer_renderbuffer(ctx, fb, attachment, rb, + "glNamedFramebufferRenderbuffer"); } -void GLAPIENTRY -_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, - GLenum pname, GLint *params) +void +_mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, + struct gl_framebuffer *buffer, + GLenum attachment, GLenum pname, + GLint *params, const char *caller) { const struct gl_renderbuffer_attachment *att; - struct gl_framebuffer *buffer; - GET_CURRENT_CONTEXT(ctx); + GLenum err; - ASSERT_OUTSIDE_BEGIN_END(ctx); + /* The error code for an attachment type of GL_NONE differs between APIs. + * + * From the ES 2.0.25 specification, page 127: + * "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then + * querying any other pname will generate INVALID_ENUM." + * + * From the OpenGL 3.0 specification, page 337, or identically, + * the OpenGL ES 3.0.4 specification, page 240: + * + * "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no + * framebuffer is bound to target. In this case querying pname + * FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other + * queries will generate an INVALID_OPERATION error." + */ + err = ctx->API == API_OPENGLES2 && ctx->Version < 30 ? + GL_INVALID_ENUM : GL_INVALID_OPERATION; + + if (_mesa_is_winsys_fbo(buffer)) { + /* Page 126 (page 136 of the PDF) of the OpenGL ES 2.0.25 spec + * says: + * + * "If the framebuffer currently bound to target is zero, then + * INVALID_OPERATION is generated." + * + * The EXT_framebuffer_object spec has the same wording, and the + * OES_framebuffer_object spec refers to the EXT_framebuffer_object + * spec. + */ + if ((!_mesa_is_desktop_gl(ctx) || + !ctx->Extensions.ARB_framebuffer_object) + && !_mesa_is_gles3(ctx)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(window-system framebuffer)", caller); + return; + } - switch (target) { -#if FEATURE_EXT_framebuffer_blit - case GL_DRAW_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { + if (_mesa_is_gles3(ctx) && attachment != GL_BACK && + attachment != GL_DEPTH && attachment != GL_STENCIL) { _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); + "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); return; } - buffer = ctx->DrawBuffer; - break; - case GL_READ_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { + + /* The specs are not clear about how to handle + * GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME with the default framebuffer, + * but dEQP-GLES3 expects an INVALID_ENUM error. This has also been + * discussed in: + * + * https://cvs.khronos.org/bugzilla/show_bug.cgi?id=12928#c1 + * and https://bugs.freedesktop.org/show_bug.cgi?id=31947 + */ + if (pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); + "%s(requesting GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME " + "when GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is " + "GL_FRAMEBUFFER_DEFAULT is not allowed)", caller); 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 (buffer->Name == 0) { /* the default / window-system FBO */ att = _mesa_get_fb0_attachment(ctx, buffer, attachment); } else { /* user-created framebuffer FBO */ - att = _mesa_get_attachment(ctx, buffer, attachment); + att = get_attachment(ctx, buffer, attachment); } if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(attachment)"); + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); return; } if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { - /* the depth and stencil attachments must point to the same buffer */ const struct gl_renderbuffer_attachment *depthAtt, *stencilAtt; - depthAtt = _mesa_get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT); - stencilAtt = _mesa_get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); + if (pname == GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) { + /* This behavior is first specified in OpenGL 4.4 specification. + * + * From the OpenGL 4.4 spec page 275: + * "This query cannot be performed for a combined depth+stencil + * attachment, since it does not have a single format." + */ + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" + " is invalid for depth+stencil attachment)", caller); + return; + } + /* the depth and stencil attachments must point to the same buffer */ + depthAtt = get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT); + stencilAtt = get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); if (depthAtt->Renderbuffer != stencilAtt->Renderbuffer) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameterivEXT(DEPTH/STENCIL" - " attachments differ)"); + "%s(DEPTH/STENCIL attachments differ)", caller); return; } } @@ -2015,27 +3695,44 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: - *params = buffer->Name == 0 ? GL_FRAMEBUFFER_DEFAULT : att->Type; + /* From the OpenGL spec, 9.2. Binding and Managing Framebuffer Objects: + * + * "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then + * either no framebuffer is bound to target; or the default framebuffer + * is bound, attachment is DEPTH or STENCIL, and the number of depth or + * stencil bits, respectively, is zero." + */ + *params = (_mesa_is_winsys_fbo(buffer) && + ((attachment != GL_DEPTH && attachment != GL_STENCIL) || + (att->Type != GL_NONE))) + ? GL_FRAMEBUFFER_DEFAULT : att->Type; return; case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: if (att->Type == GL_RENDERBUFFER_EXT) { - *params = att->Renderbuffer->Name; + *params = att->Renderbuffer->Name; } else if (att->Type == GL_TEXTURE) { - *params = att->Texture->Name; + *params = att->Texture->Name; } else { assert(att->Type == GL_NONE); - *params = 0; + if (_mesa_is_desktop_gl(ctx) || _mesa_is_gles3(ctx)) { + *params = 0; + } else { + goto invalid_pname_enum; + } } return; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: if (att->Type == GL_TEXTURE) { - *params = att->TextureLevel; + *params = att->TextureLevel; + } + else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + goto invalid_pname_enum; } return; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: @@ -2047,14 +3744,23 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, *params = 0; } } + else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); + } else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + goto invalid_pname_enum; } return; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: - if (att->Type == GL_TEXTURE) { - if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) { + if (ctx->API == API_OPENGLES) { + goto invalid_pname_enum; + } else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); + } else if (att->Type == GL_TEXTURE) { + if (att->Texture && (att->Texture->Target == GL_TEXTURE_3D || + att->Texture->Target == GL_TEXTURE_2D_ARRAY)) { *params = att->Zoffset; } else { @@ -2062,31 +3768,74 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, } } else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + goto invalid_pname_enum; } return; case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + if ((!_mesa_is_desktop_gl(ctx) || + !ctx->Extensions.ARB_framebuffer_object) + && !_mesa_is_gles3(ctx)) { + goto invalid_pname_enum; + } + else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { - *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format); + if (ctx->Extensions.EXT_framebuffer_sRGB) { + *params = + _mesa_get_format_color_encoding(att->Renderbuffer->Format); + } + else { + /* According to ARB_framebuffer_sRGB, we should return LINEAR + * if the sRGB conversion is unsupported. */ + *params = GL_LINEAR; + } } return; case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - return; + if ((ctx->API != API_OPENGL_COMPAT || + !ctx->Extensions.ARB_framebuffer_object) + && ctx->API != API_OPENGL_CORE + && !_mesa_is_gles3(ctx)) { + goto invalid_pname_enum; + } + else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { - gl_format format = att->Renderbuffer->Format; - if (format == MESA_FORMAT_CI8 || format == MESA_FORMAT_S8) { + mesa_format format = att->Renderbuffer->Format; + + /* Page 235 (page 247 of the PDF) in section 6.1.13 of the OpenGL ES + * 3.0.1 spec says: + * + * "If pname is FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE.... If + * attachment is DEPTH_STENCIL_ATTACHMENT the query will fail and + * generate an INVALID_OPERATION error. + */ + if (_mesa_is_gles3(ctx) && + attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(cannot query " + "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE of " + "GL_DEPTH_STENCIL_ATTACHMENT)", caller); + return; + } + + if (format == MESA_FORMAT_S_UINT8) { /* special cases */ *params = GL_INDEX; } + else if (format == MESA_FORMAT_Z32_FLOAT_S8X24_UINT) { + /* depends on the attachment parameter */ + if (attachment == GL_STENCIL_ATTACHMENT) { + *params = GL_INDEX; + } + else { + *params = GL_FLOAT; + } + } else { *params = _mesa_get_format_datatype(format); } @@ -2098,13 +3847,18 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + if ((!_mesa_is_desktop_gl(ctx) || + !ctx->Extensions.ARB_framebuffer_object) + && !_mesa_is_gles3(ctx)) { + goto invalid_pname_enum; + } + else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else if (att->Texture) { const struct gl_texture_image *texImage = - _mesa_select_tex_image(ctx, att->Texture, att->Texture->Target, + _mesa_select_tex_image(att->Texture, att->Texture->Target, att->TextureLevel); if (texImage) { *params = get_component_bits(pname, texImage->_BaseFormat, @@ -2119,276 +3873,443 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, att->Renderbuffer->Format); } else { - *params = 0; + _mesa_problem(ctx, "%s: invalid FBO attachment structure", caller); } return; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + case GL_FRAMEBUFFER_ATTACHMENT_LAYERED: + if (!_mesa_has_geometry_shaders(ctx)) { + goto invalid_pname_enum; + } else if (att->Type == GL_TEXTURE) { + *params = att->Layered; + } else if (att->Type == GL_NONE) { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); + } else { + goto invalid_pname_enum; + } return; + default: + goto invalid_pname_enum; } + + return; + +invalid_pname_enum: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); + return; } void GLAPIENTRY -_mesa_GenerateMipmapEXT(GLenum target) +_mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, + GLenum pname, GLint *params) { - struct gl_texture_object *texObj; GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *buffer; - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - switch (target) { - case GL_TEXTURE_1D: - case GL_TEXTURE_2D: - case GL_TEXTURE_3D: - case GL_TEXTURE_CUBE_MAP: - /* OK, legal value */ - break; - default: - /* XXX need to implement GL_TEXTURE_1D_ARRAY and GL_TEXTURE_2D_ARRAY */ - _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)"); + buffer = get_framebuffer_target(ctx, target); + if (!buffer) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameteriv(invalid target %s)", + _mesa_enum_to_string(target)); return; } - texObj = _mesa_get_current_tex_object(ctx, target); + _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, + "glGetFramebufferAttachmentParameteriv"); +} - if (texObj->BaseLevel >= texObj->MaxLevel) { - /* nothing to do */ - return; - } - if (texObj->Target == GL_TEXTURE_CUBE_MAP && - !_mesa_cube_complete(texObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGenerateMipmap(incomplete cube map)"); - return; - } +void GLAPIENTRY +_mesa_GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, + GLenum attachment, + GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *buffer; - _mesa_lock_texture(ctx, texObj); - if (target == GL_TEXTURE_CUBE_MAP) { - GLuint face; - for (face = 0; face < 6; face++) - ctx->Driver.GenerateMipmap(ctx, - GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face, - texObj); + if (framebuffer) { + buffer = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glGetNamedFramebufferAttachmentParameteriv"); + if (!buffer) + return; } else { - ctx->Driver.GenerateMipmap(ctx, target, texObj); + /* + * Section 9.2 Binding and Managing Framebuffer Objects of the OpenGL + * 4.5 core spec (30.10.2014, PDF page 314): + * "If framebuffer is zero, then the default draw framebuffer is + * queried." + */ + buffer = ctx->WinSysDrawBuffer; } - _mesa_unlock_texture(ctx, texObj); -} - - -#if FEATURE_EXT_framebuffer_blit -static const struct gl_renderbuffer_attachment * -find_attachment(const struct gl_framebuffer *fb, const struct gl_renderbuffer *rb) -{ - GLuint i; - for (i = 0; i < Elements(fb->Attachment); i++) { - if (fb->Attachment[i].Renderbuffer == rb) - return &fb->Attachment[i]; - } - return NULL; + _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, + "glGetNamedFramebufferAttachmentParameteriv"); } - -/** - * Blit rectangular region, optionally from one framebuffer to another. - * - * Note, if the src buffer is multisampled and the dest is not, this is - * when the samples must be resolved to a single color. - */ void GLAPIENTRY -_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter) -{ - const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | - GL_DEPTH_BUFFER_BIT | - GL_STENCIL_BUFFER_BIT); - const struct gl_framebuffer *readFb, *drawFb; - const struct gl_renderbuffer *colorReadRb, *colorDrawRb; +_mesa_NamedFramebufferParameteri(GLuint framebuffer, GLenum pname, + GLint param) +{ GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb = NULL; - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteri(" + "ARB_framebuffer_no_attachments not implemented)"); + return; + } - if (ctx->NewState) { - _mesa_update_state(ctx); + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glNamedFramebufferParameteri"); + + if (fb) { + framebuffer_parameteri(ctx, fb, pname, param, + "glNamedFramebufferParameteriv"); } +} - readFb = ctx->ReadBuffer; - drawFb = ctx->DrawBuffer; - if (!readFb || !drawFb) { - /* This will normally never happen but someday we may want to - * support MakeCurrent() with no drawables. - */ +void GLAPIENTRY +_mesa_GetNamedFramebufferParameteriv(GLuint framebuffer, GLenum pname, + GLint *param) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteriv(" + "ARB_framebuffer_no_attachments not implemented)"); return; } - /* check for complete framebuffers */ - if (drawFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT || - readFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, - "glBlitFramebufferEXT(incomplete draw/read buffers)"); - return; + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glGetNamedFramebufferParameteriv"); + } else { + fb = ctx->WinSysDrawBuffer; } - if (filter != GL_NEAREST && filter != GL_LINEAR) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)"); - return; + if (fb) { + get_framebuffer_parameteriv(ctx, fb, pname, param, + "glGetNamedFramebufferParameteriv"); } +} + + +static void +invalidate_framebuffer_storage(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, + GLsizei width, GLsizei height, const char *name) +{ + int i; - if (mask & ~legalMaskBits) { - _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); + /* Section 17.4 Whole Framebuffer Operations of the OpenGL 4.5 Core + * Spec (2.2.2015, PDF page 522) says: + * "An INVALID_VALUE error is generated if numAttachments, width, or + * height is negative." + */ + if (numAttachments < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(numAttachments < 0)", name); 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"); + if (width < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(width < 0)", name); return; } - /* get color read/draw renderbuffers */ - if (mask & GL_COLOR_BUFFER_BIT) { - colorReadRb = readFb->_ColorReadBuffer; - colorDrawRb = drawFb->_ColorDrawBuffers[0]; - } - else { - colorReadRb = colorDrawRb = NULL; + if (height < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(height < 0)", name); + return; } - if (mask & GL_STENCIL_BUFFER_BIT) { - struct gl_renderbuffer *readRb = readFb->_StencilBuffer; - struct gl_renderbuffer *drawRb = drawFb->_StencilBuffer; - if (!readRb || - !drawRb || - _mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(stencil buffer size mismatch"); - return; + /* The GL_ARB_invalidate_subdata spec says: + * + * "If an attachment is specified that does not exist in the + * framebuffer bound to , it is ignored." + * + * It also says: + * + * "If contains COLOR_ATTACHMENTm and m is greater than + * or equal to the value of MAX_COLOR_ATTACHMENTS, then the error + * INVALID_OPERATION is generated." + * + * No mention is made of GL_AUXi being out of range. Therefore, we allow + * any enum that can be allowed by the API (OpenGL ES 3.0 has a different + * set of retrictions). + */ + for (i = 0; i < numAttachments; i++) { + if (_mesa_is_winsys_fbo(fb)) { + switch (attachments[i]) { + case GL_ACCUM: + case GL_AUX0: + case GL_AUX1: + case GL_AUX2: + case GL_AUX3: + /* Accumulation buffers and auxilary buffers were removed in + * OpenGL 3.1, and they never existed in OpenGL ES. + */ + if (ctx->API != API_OPENGL_COMPAT) + goto invalid_enum; + break; + case GL_COLOR: + case GL_DEPTH: + case GL_STENCIL: + break; + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + if (!_mesa_is_desktop_gl(ctx)) + goto invalid_enum; + break; + default: + goto invalid_enum; + } + } else { + switch (attachments[i]) { + case GL_DEPTH_ATTACHMENT: + case GL_STENCIL_ATTACHMENT: + break; + case GL_DEPTH_STENCIL_ATTACHMENT: + /* GL_DEPTH_STENCIL_ATTACHMENT is a valid attachment point only + * in desktop and ES 3.0 profiles. Note that OES_packed_depth_stencil + * extension does not make this attachment point valid on ES 2.0. + */ + if (_mesa_is_desktop_gl(ctx) || _mesa_is_gles3(ctx)) + break; + /* fallthrough */ + case GL_COLOR_ATTACHMENT0: + case GL_COLOR_ATTACHMENT1: + case GL_COLOR_ATTACHMENT2: + case GL_COLOR_ATTACHMENT3: + case GL_COLOR_ATTACHMENT4: + case GL_COLOR_ATTACHMENT5: + case GL_COLOR_ATTACHMENT6: + case GL_COLOR_ATTACHMENT7: + case GL_COLOR_ATTACHMENT8: + case GL_COLOR_ATTACHMENT9: + case GL_COLOR_ATTACHMENT10: + case GL_COLOR_ATTACHMENT11: + case GL_COLOR_ATTACHMENT12: + case GL_COLOR_ATTACHMENT13: + case GL_COLOR_ATTACHMENT14: + case GL_COLOR_ATTACHMENT15: { + unsigned k = attachments[i] - GL_COLOR_ATTACHMENT0; + if (k >= ctx->Const.MaxColorAttachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(attachment >= max. color attachments)", name); + return; + } + break; + } + default: + goto invalid_enum; + } } } - if (mask & GL_DEPTH_BUFFER_BIT) { - struct gl_renderbuffer *readRb = readFb->_DepthBuffer; - struct gl_renderbuffer *drawRb = drawFb->_DepthBuffer; - if (!readRb || - !drawRb || - _mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(depth buffer size mismatch"); - return; - } - } + /* We don't actually do anything for this yet. Just return after + * validating the parameters and generating the required errors. + */ + return; - if (readFb->Visual.samples > 0 && - drawFb->Visual.samples > 0 && - readFb->Visual.samples != drawFb->Visual.samples) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(mismatched samples"); +invalid_enum: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", name, + _mesa_enum_to_string(attachments[i])); + return; +} + + +void GLAPIENTRY +_mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, + GLsizei width, GLsizei height) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateSubFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); return; } - /* extra checks for multisample copies... */ - if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { - /* src and dest region sizes must be the same */ - if (srcX1 - srcX0 != dstX1 - dstX0 || - srcY1 - srcY0 != dstY1 - dstY0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample region sizes"); - return; - } + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + x, y, width, height, + "glInvalidateSubFramebuffer"); +} - /* color formats must match */ - if (colorReadRb && - colorDrawRb && - colorReadRb->Format != colorDrawRb->Format) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample pixel formats"); + +void GLAPIENTRY +_mesa_InvalidateNamedFramebufferSubData(GLuint framebuffer, + GLsizei numAttachments, + const GLenum *attachments, + GLint x, GLint y, + GLsizei width, GLsizei height) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + /* The OpenGL 4.5 core spec (02.02.2015) says (in Section 17.4 Whole + * Framebuffer Operations, PDF page 522): "If framebuffer is zero, the + * default draw framebuffer is affected." + */ + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glInvalidateNamedFramebufferSubData"); + if (!fb) return; - } } + else + fb = ctx->WinSysDrawBuffer; - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT"); - return; - } + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + x, y, width, height, + "glInvalidateNamedFramebufferSubData"); +} - /* Debug code */ - if (DEBUG_BLIT) { - printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d," - " 0x%x, 0x%x)\n", - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); - if (colorReadRb) { - const struct gl_renderbuffer_attachment *att; - att = find_attachment(readFb, colorReadRb); - printf(" Src FBO %u RB %u (%dx%d) ", - readFb->Name, colorReadRb->Name, - colorReadRb->Width, colorReadRb->Height); - if (att && att->Texture) { - printf("Tex %u tgt 0x%x level %u face %u", - att->Texture->Name, - att->Texture->Target, - att->TextureLevel, - att->CubeMapFace); - } - printf("\n"); - - att = find_attachment(drawFb, colorDrawRb); - printf(" Dst FBO %u RB %u (%dx%d) ", - drawFb->Name, colorDrawRb->Name, - colorDrawRb->Width, colorDrawRb->Height); - if (att && att->Texture) { - printf("Tex %u tgt 0x%x level %u face %u", - att->Texture->Name, - att->Texture->Target, - att->TextureLevel, - att->CubeMapFace); - } - printf("\n"); - } +void GLAPIENTRY +_mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, + const GLenum *attachments) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; } - ASSERT(ctx->Driver.BlitFramebuffer); - ctx->Driver.BlitFramebuffer(ctx, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); + /* The GL_ARB_invalidate_subdata spec says: + * + * "The command + * + * void InvalidateFramebuffer(enum target, + * sizei numAttachments, + * const enum *attachments); + * + * is equivalent to the command InvalidateSubFramebuffer with , , + * , equal to 0, 0, , + * respectively." + */ + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + 0, 0, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, + "glInvalidateFramebuffer"); } -#endif /* FEATURE_EXT_framebuffer_blit */ -#if FEATURE_ARB_geometry_shader4 + void GLAPIENTRY -_mesa_FramebufferTextureARB(GLenum target, GLenum attachment, - GLuint texture, GLint level) +_mesa_InvalidateNamedFramebufferData(GLuint framebuffer, + GLsizei numAttachments, + const GLenum *attachments) { + struct gl_framebuffer *fb; GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureARB " - "not implemented!"); + + /* The OpenGL 4.5 core spec (02.02.2015) says (in Section 17.4 Whole + * Framebuffer Operations, PDF page 522): "If framebuffer is zero, the + * default draw framebuffer is affected." + */ + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glInvalidateNamedFramebufferData"); + if (!fb) + return; + } + else + fb = ctx->WinSysDrawBuffer; + + /* The GL_ARB_invalidate_subdata spec says: + * + * "The command + * + * void InvalidateFramebuffer(enum target, + * sizei numAttachments, + * const enum *attachments); + * + * is equivalent to the command InvalidateSubFramebuffer with , , + * , equal to 0, 0, , + * respectively." + */ + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + 0, 0, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, + "glInvalidateNamedFramebufferData"); } + void GLAPIENTRY -_mesa_FramebufferTextureFaceARB(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLenum face) +_mesa_DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, + const GLenum *attachments) { + struct gl_framebuffer *fb; + GLint i; + GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureFaceARB " - "not implemented!"); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glDiscardFramebufferEXT(target %s)", + _mesa_enum_to_string(target)); + return; + } + + if (numAttachments < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glDiscardFramebufferEXT(numAttachments < 0)"); + return; + } + + for (i = 0; i < numAttachments; i++) { + switch (attachments[i]) { + case GL_COLOR: + case GL_DEPTH: + case GL_STENCIL: + if (_mesa_is_user_fbo(fb)) + goto invalid_enum; + break; + case GL_COLOR_ATTACHMENT0: + case GL_DEPTH_ATTACHMENT: + case GL_STENCIL_ATTACHMENT: + if (_mesa_is_winsys_fbo(fb)) + goto invalid_enum; + break; + default: + goto invalid_enum; + } + } + + if (ctx->Driver.DiscardFramebuffer) + ctx->Driver.DiscardFramebuffer(ctx, target, numAttachments, attachments); + + return; + +invalid_enum: + _mesa_error(ctx, GL_INVALID_ENUM, + "glDiscardFramebufferEXT(attachment %s)", + _mesa_enum_to_string(attachments[i])); } -#endif /* FEATURE_ARB_geometry_shader4 */