X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=2c01526d16791c36104cde26d375b7765d037bca;hb=b9b88516f8d3efc902696f1092519e298ceb7cdb;hp=d558d7f8723e3c42d6cdd3a5cb9dd269f038ee3b;hpb=9c1b41879aab2ff7386c547a2ccce7686c018cf5;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index d558d7f8723..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" @@ -41,7 +42,7 @@ #include "glformats.h" #include "hash.h" #include "macros.h" -#include "mfeatures.h" +#include "multisample.h" #include "mtypes.h" #include "renderbuffer.h" #include "state.h" @@ -49,10 +50,6 @@ #include "texobj.h" -/** Set this to 1 to debug/log glBlitFramebuffer() calls */ -#define DEBUG_BLIT 0 - - /** * Notes: * @@ -77,7 +74,7 @@ static struct gl_framebuffer IncompleteFramebuffer; static void -delete_dummy_renderbuffer(struct gl_renderbuffer *rb) +delete_dummy_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { /* no op */ } @@ -92,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; @@ -123,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. */ @@ -140,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 @@ -162,13 +201,12 @@ invalidate_framebuffer(struct gl_framebuffer *fb) 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 ctx->Extensions.EXT_framebuffer_blit && _mesa_is_desktop_gl(ctx) - ? ctx->DrawBuffer : NULL; + return have_fb_blit ? ctx->DrawBuffer : NULL; case GL_READ_FRAMEBUFFER: - return ctx->Extensions.EXT_framebuffer_blit && _mesa_is_desktop_gl(ctx) - ? ctx->ReadBuffer : NULL; + return have_fb_blit ? ctx->ReadBuffer : NULL; case GL_FRAMEBUFFER_EXT: return ctx->DrawBuffer; default: @@ -185,9 +223,9 @@ get_framebuffer_target(struct gl_context *ctx, GLenum target) * 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; @@ -216,13 +254,13 @@ _mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, */ i = attachment - GL_COLOR_ATTACHMENT0_EXT; if (i >= ctx->Const.MaxColorAttachments - || (i > 0 && ctx->API == API_OPENGLES)) { - return NULL; + || (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)) - return NULL; + if (!_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx)) + return NULL; /* fall-through */ case GL_DEPTH_ATTACHMENT_EXT: return &fb->Attachment[BUFFER_DEPTH]; @@ -244,6 +282,25 @@ _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, { 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: return &fb->Attachment[BUFFER_FRONT_LEFT]; @@ -298,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 (_mesa_get_attachment_teximage(att)) { - ctx->Driver.RenderTexture(ctx, fb, att); - } - - invalidate_framebuffer(fb); + _mesa_update_texture_renderbuffer(ctx, fb, att); } @@ -366,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); } @@ -385,33 +522,40 @@ _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); } @@ -441,7 +585,7 @@ _mesa_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) default: switch (rb->Format) { /* XXX This list is likely incomplete. */ - case MESA_FORMAT_RGB9_E5_FLOAT: + case MESA_FORMAT_R9G9B9E5_FLOAT: fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; return; default:; @@ -453,6 +597,32 @@ _mesa_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) } +/** + * 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; +} + + /** * For debug only. */ @@ -469,8 +639,16 @@ 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) { + 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); } @@ -491,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; @@ -501,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? */ @@ -552,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)) { @@ -576,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 { @@ -593,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; @@ -608,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) { @@ -630,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 { @@ -642,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 { @@ -657,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; } @@ -680,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(_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 @@ -697,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 attFormat; + mesa_format attFormat; + GLenum att_tex_target = GL_NONE; /* * XXX for ARB_fbo, only check color buffers that are named by @@ -711,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) { @@ -720,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 { @@ -729,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; } } @@ -737,8 +1012,8 @@ _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 = - _mesa_get_attachment_teximage(att); + 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); @@ -746,10 +1021,29 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, f = texImg->_BaseFormat; 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; } } @@ -761,24 +1055,48 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, f = att->Renderbuffer->InternalFormat; 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 (att->Renderbuffer && numSamples < 0) { - /* first buffer */ - numSamples = att->Renderbuffer->NumSamples; - } - /* check if integer color */ 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; } @@ -788,22 +1106,16 @@ _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 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) @@ -811,46 +1123,122 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, if (att->Type == GL_RENDERBUFFER && att->Renderbuffer->Format == MESA_FORMAT_NONE) { fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; - fbo_incomplete("unsupported renderbuffer format", i); + 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 (_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; } @@ -864,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; } @@ -897,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)"); @@ -920,127 +1328,357 @@ _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); } - -/** - * 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. - */ -static void -detach_renderbuffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - struct gl_renderbuffer *rb) +void GLAPIENTRY +_mesa_BindRenderbuffer(GLenum target, GLuint renderbuffer) { - GLuint i; - for (i = 0; i < BUFFER_COUNT; i++) { - if (fb->Attachment[i].Renderbuffer == rb) { - _mesa_remove_attachment(ctx, &fb->Attachment[i]); - } - } - invalidate_framebuffer(fb); + 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); +} + +/** + * 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 +framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum pname, GLint param, const char *func) +{ + 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); } + /* 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)) { - detach_renderbuffer(ctx, ctx->DrawBuffer, rb); + _mesa_detach_renderbuffer(ctx, ctx->DrawBuffer, rb); } if (_mesa_is_user_fbo(ctx->ReadBuffer) && ctx->ReadBuffer != ctx->DrawBuffer) { - detach_renderbuffer(ctx, ctx->ReadBuffer, rb); + _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); } @@ -1072,13 +1710,15 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_ALPHA8: case GL_ALPHA12: case GL_ALPHA16: - return ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; + 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->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; + 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: @@ -1086,139 +1726,174 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_LUMINANCE12_ALPHA4: case GL_LUMINANCE12_ALPHA12: case GL_LUMINANCE16_ALPHA16: - return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; + 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->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; + 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_RGB8: case GL_RGB10: case GL_RGB12: case GL_RGB16: + return _mesa_is_desktop_gl(ctx) ? GL_RGB : 0; case GL_SRGB8_EXT: - return GL_RGB; - case GL_RGBA: - case GL_RGBA2: + return _mesa_is_desktop_gl(ctx) ? GL_RGB : 0; case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: - case GL_RGB10_A2: + 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 GL_RGBA; + 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: + /* 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: return GL_DEPTH_COMPONENT; - case GL_DEPTH_STENCIL_EXT: - case GL_DEPTH24_STENCIL8_EXT: - if (ctx->Extensions.EXT_packed_depth_stencil) - return GL_DEPTH_STENCIL_EXT; - else - return 0; + 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: - if (ctx->Extensions.ARB_depth_buffer_float) - return GL_DEPTH_COMPONENT; - else - return 0; + return ctx->Version >= 30 + || (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_depth_buffer_float) + ? GL_DEPTH_COMPONENT : 0; case GL_DEPTH32F_STENCIL8: - if (ctx->Extensions.ARB_depth_buffer_float) - return GL_DEPTH_STENCIL; - else - return 0; + return ctx->Version >= 30 + || (ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_depth_buffer_float) + ? GL_DEPTH_STENCIL : 0; case GL_RED: - case GL_R8: case GL_R16: - return ctx->Extensions.ARB_texture_rg ? GL_RED : 0; + 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_RG8: case GL_RG16: - return ctx->Extensions.ARB_texture_rg ? GL_RG : 0; + 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 ctx->Extensions.EXT_texture_snorm ? GL_RED : 0; + 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 ctx->Extensions.EXT_texture_snorm ? GL_RG : 0; + 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 ctx->Extensions.EXT_texture_snorm ? GL_RGB : 0; + 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 ctx->Extensions.EXT_texture_snorm ? GL_RGBA : 0; + 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->Extensions.EXT_texture_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 ctx->Extensions.EXT_texture_snorm && - ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; + 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 ctx->Extensions.EXT_texture_snorm && - ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; + 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 ctx->Extensions.EXT_texture_snorm && - ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; + return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_snorm + ? GL_INTENSITY : 0; + case GL_R16F: case GL_R32F: - return ctx->Extensions.ARB_texture_rg && - ctx->Extensions.ARB_texture_float ? GL_RED : 0; + 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 ctx->Extensions.ARB_texture_rg && - ctx->Extensions.ARB_texture_float ? GL_RG : 0; + 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 ctx->Extensions.ARB_texture_float ? GL_RGB : 0; + return (_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_float) + ? GL_RGB : 0; case GL_RGBA16F: case GL_RGBA32F: - return ctx->Extensions.ARB_texture_float ? GL_RGBA : 0; + 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->Extensions.ARB_texture_float && + 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->Extensions.ARB_texture_float && + 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->Extensions.ARB_texture_float && + 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->Extensions.ARB_texture_float && + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.ARB_texture_float && ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; - case GL_RGB9_E5: - return ctx->Extensions.EXT_texture_shared_exponent ? GL_RGB : 0; case GL_R11F_G11F_B10F: - return ctx->Extensions.EXT_packed_float ? GL_RGB : 0; + 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: @@ -1226,8 +1901,9 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_RGBA8I_EXT: case GL_RGBA16I_EXT: case GL_RGBA32I_EXT: - return ctx->Version >= 30 || - ctx->Extensions.EXT_texture_integer ? GL_RGBA : 0; + 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: @@ -1235,18 +1911,18 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_RGB8I_EXT: case GL_RGB16I_EXT: case GL_RGB32I_EXT: - return ctx->Version >= 30 || - ctx->Extensions.EXT_texture_integer ? GL_RGB : 0; - + 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 || - (ctx->Extensions.ARB_texture_rg && - ctx->Extensions.EXT_texture_integer) ? GL_RED : 0; + 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: @@ -1254,9 +1930,10 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_RG16I: case GL_RG32UI: case GL_RG32I: - return ctx->Version >= 30 || - (ctx->Extensions.ARB_texture_rg && - ctx->Extensions.EXT_texture_integer) ? GL_RG : 0; + 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: @@ -1264,7 +1941,8 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_INTENSITY16UI_EXT: case GL_INTENSITY32I_EXT: case GL_INTENSITY32UI_EXT: - return ctx->Extensions.EXT_texture_integer && + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; case GL_LUMINANCE8I_EXT: @@ -1273,7 +1951,8 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_LUMINANCE16UI_EXT: case GL_LUMINANCE32I_EXT: case GL_LUMINANCE32UI_EXT: - return ctx->Extensions.EXT_texture_integer && + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; case GL_LUMINANCE_ALPHA8I_EXT: @@ -1282,7 +1961,8 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_LUMINANCE_ALPHA16UI_EXT: case GL_LUMINANCE_ALPHA32I_EXT: case GL_LUMINANCE_ALPHA32UI_EXT: - return ctx->Extensions.EXT_texture_integer && + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; case GL_ALPHA8I_EXT: @@ -1291,14 +1971,18 @@ _mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) case GL_ALPHA16UI_EXT: case GL_ALPHA32I_EXT: case GL_ALPHA32UI_EXT: - return ctx->Extensions.EXT_texture_integer && + return ctx->API == API_OPENGL_COMPAT && + ctx->Extensions.EXT_texture_integer && ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; case GL_RGB10_A2UI: - return ctx->Extensions.ARB_texture_rgb10_a2ui ? GL_RGBA : 0; + return (_mesa_is_desktop_gl(ctx) && + ctx->Extensions.ARB_texture_rgb10_a2ui) + || _mesa_is_gles3(ctx) ? GL_RGBA : 0; case GL_RGB565: - return ctx->Extensions.ARB_ES2_compatibility ? GL_RGB : 0; + return _mesa_is_gles(ctx) || ctx->Extensions.ARB_ES2_compatibility + ? GL_RGB : 0; default: return 0; } @@ -1333,59 +2017,21 @@ invalidate_rb(GLuint key, void *data, void *userData) /** 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(). - */ -static void -renderbuffer_storage(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei samples) +void +_mesa_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei samples) { - 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; - } - - if (width < 0 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(width)", func); - return; - } - - if (height < 0 || 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; - } - - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, "%s", func); - return; + 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); @@ -1403,7 +2049,7 @@ 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 */ /* If rb->Format == MESA_FORMAT_NONE, the format is unsupported. */ @@ -1429,51 +2075,186 @@ renderbuffer_storage(GLenum target, GLenum internalFormat, } } - -#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 for _mesa_GetRenderbufferParameterivEXT() and - * _mesa_GetFramebufferAttachmentParameterivEXT() - * We have to be careful to respect the base format. For example, if a - * renderbuffer/texture was created with internalFormat=GL_RGB but the + * 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_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) { if (_mesa_base_format_has_channel(baseFormat, pname)) return _mesa_get_format_bits(format, pname); @@ -1484,14 +2265,15 @@ get_component_bits(GLenum pname, GLenum baseFormat, gl_format format) 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"); } @@ -1500,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"); } @@ -1509,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: @@ -1521,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. */ @@ -1569,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); @@ -1605,14 +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 (_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; - if (att->Texture && _mesa_get_attachment_teximage(att)) { + if (att->Texture && att->Renderbuffer->TexImage + && driver_RenderTexture_is_safe(att)) { ctx->Driver.RenderTexture(ctx, fb, att); } } @@ -1627,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 (_mesa_is_winsys_fbo(fb)) - 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; @@ -1700,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; @@ -1725,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. @@ -1773,113 +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); +} - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - buffer = get_framebuffer_target(ctx, target); - if (!buffer) { - _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); - return 0; - } +GLenum +_mesa_check_framebuffer_status(struct gl_context *ctx, + struct gl_framebuffer *buffer) +{ + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); if (_mesa_is_winsys_fbo(buffer)) { - /* The window system / default framebuffer is always complete */ - return GL_FRAMEBUFFER_COMPLETE_EXT; + /* EGL_KHR_surfaceless_context allows the winsys FBO to be incomplete. */ + if (buffer != &IncompleteFramebuffer) { + return GL_FRAMEBUFFER_COMPLETE_EXT; + } else { + return GL_FRAMEBUFFER_UNDEFINED; + } } /* No need to flush here */ @@ -1892,6 +2754,67 @@ _mesa_CheckFramebufferStatusEXT(GLenum target) } +GLenum GLAPIENTRY +_mesa_CheckFramebufferStatus(GLenum target) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCheckFramebufferStatus(%s)\n", + _mesa_enum_to_string(target)); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glCheckFramebufferStatus(invalid target %s)", + _mesa_enum_to_string(target)); + return 0; + } + + return _mesa_check_framebuffer_status(ctx, fb); +} + + +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 @@ -1914,152 +2837,356 @@ reuse_framebuffer_texture_attachment(struct gl_framebuffer *fb, 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 glFramebufferTexture1D/2D/3DEXT() and - * glFramebufferTextureLayerEXT(). - * Note: glFramebufferTextureLayerEXT() has no textarget parameter so we'll - * get textarget=0 in that case. + * 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 void -framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, - GLenum attachment, GLenum textarget, GLuint texture, - GLint level, GLint zoffset) +static bool +get_texture_for_framebuffer(struct gl_context *ctx, GLuint texture, + bool layered, const char *caller, + struct gl_texture_object **texObj) { - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj = NULL; - struct gl_framebuffer *fb; - GLenum maxLevelsTarget; + *texObj = NULL; /* This will get returned if texture = 0. */ - ASSERT_OUTSIDE_BEGIN_END(ctx); + if (!texture) + return true; - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%sEXT(target=0x%x)", caller, target); - return; + *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; } - /* check framebuffer binding */ - if (_mesa_is_winsys_fbo(fb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT", caller); - return; - } + return true; +} - /* 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) { - /* If textarget == 0 it means we're being called by - * glFramebufferTextureLayer() and textarget is not used. - * The only legal texture types for that function are 3D and - * 1D/2D arrays textures. - */ - err = (texObj->Target != GL_TEXTURE_3D) && - (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) && - (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT); - } - else { - /* Make sure textarget is consistent with the texture's type */ - err = (texObj->Target == GL_TEXTURE_CUBE_MAP) - ? !_mesa_is_cube_face(textarget) - : (texObj->Target != textarget); - } - } - else { - /* can't render to a non-existant texture */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(non existant texture)", - caller); - return; - } - if (err) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(texture target mismatch)", - caller); - return; - } +/** + * 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; - 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; - } + 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 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; - } + 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, + "%s(invalid textarget %s)", + caller, _mesa_enum_to_string(textarget)); + return false; + } + + /* 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; + } - maxLevelsTarget = textarget ? textarget : texObj->Target; - if ((level < 0) || - (level >= _mesa_max_texture_levels(ctx, maxLevelsTarget))) { + if (target == GL_TEXTURE_3D) { + const GLuint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); + if (layer >= maxSize) { _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%sEXT(level)", caller); - return; + "%s(invalid layer %u)", caller, layer); + return false; + } + } + 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; } } + else if (target == GL_TEXTURE_CUBE_MAP) { + if (layer >= 6) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(layer %u >= 6)", caller, layer); + return false; + } + } + + return true; +} - att = _mesa_get_attachment(ctx, fb, attachment); + +/** + * 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) { 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 && - zoffset == 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); + 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 && + texObj == fb->Attachment[BUFFER_DEPTH].Texture && level == fb->Attachment[BUFFER_DEPTH].TextureLevel && _mesa_tex_target_to_face(textarget) == fb->Attachment[BUFFER_DEPTH].CubeMapFace && - zoffset == fb->Attachment[BUFFER_DEPTH].Zoffset) { - /* As above, but with depth and stencil transposed. */ - reuse_framebuffer_texture_attachment(fb, BUFFER_STENCIL, - BUFFER_DEPTH); + layer == fb->Attachment[BUFFER_DEPTH].Zoffset) { + /* As above, but with depth and stencil transposed. */ + reuse_framebuffer_texture_attachment(fb, BUFFER_STENCIL, + BUFFER_DEPTH); } else { - _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget, - level, zoffset); - 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_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 @@ -2073,231 +3200,416 @@ 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]); - _mesa_remove_attachment(ctx, &fb->Attachment[BUFFER_STENCIL]); + 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); } - -void GLAPIENTRY -_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) +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; + } - if (texture != 0) { - GLboolean error; + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, caller, &texObj)) + return; - switch (textarget) { - case GL_TEXTURE_1D: - error = GL_FALSE; - break; - case GL_TEXTURE_1D_ARRAY: - error = !ctx->Extensions.EXT_texture_array; - break; - default: - error = GL_TRUE; - } + if (texObj) { + if (!check_textarget(ctx, dims, texObj->Target, textarget, caller)) + return; - if (error) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture1DEXT(textarget=%s)", - _mesa_lookup_enum_by_nr(textarget)); + if ((dims == 3) && !check_layer(ctx, texObj->Target, layer, caller)) + return; + + if (!check_level(ctx, textarget, level, caller)) return; - } } - framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, - level, 0); + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, caller); } void GLAPIENTRY -_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) +_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_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) { - GLboolean error; + const char *func = "glFramebufferTextureLayer"; - switch (textarget) { - case GL_TEXTURE_2D: - error = GL_FALSE; - break; - case GL_TEXTURE_RECTANGLE: - error = !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: - error = !ctx->Extensions.ARB_texture_cube_map; - break; - case GL_TEXTURE_2D_ARRAY: - error = !ctx->Extensions.EXT_texture_array; - break; - default: - error = GL_TRUE; - } + /* Get the framebuffer object */ + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTextureLayer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } - if (error) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture2DEXT(textarget=%s)", - _mesa_lookup_enum_by_nr(textarget)); + /* 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_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_3D)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture3DEXT(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, "3D", target, attachment, textarget, texture, - level, zoffset); + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, func); } void GLAPIENTRY -_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLint layer) +_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; + + 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, + "glFramebufferTexture(invalid target %s)", + _mesa_enum_to_string(target)); + 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; - framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, - level, layer); + 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) +_mesa_NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, + GLuint texture, GLint level) { - struct gl_renderbuffer_attachment *att; - struct gl_framebuffer *fb; - struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLboolean layered = GL_FALSE; - ASSERT_OUTSIDE_BEGIN_END(ctx); + const char *func = "glNamedFramebufferTexture"; - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, "glFramebufferRenderbufferEXT(target)"); + if (!_mesa_has_geometry_shaders(ctx)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "unsupported function (glNamedFramebufferTexture) called"); return; } - if (renderbufferTarget != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(renderbufferTarget)"); + /* 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 +_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, "glFramebufferRenderbufferEXT"); + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(window-system framebuffer)", func); return; } - att = _mesa_get_attachment(ctx, fb, attachment); + att = get_attachment(ctx, fb, attachment); if (att == NULL) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(invalid attachment %s)", - _mesa_lookup_enum_by_nr(attachment)); + "%s(invalid attachment %s)", func, + _mesa_enum_to_string(attachment)); 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; - } - } - 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)"); + "%s(renderbuffer is not DEPTH_STENCIL format)", func); return; } } + _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb); +} - FLUSH_VERTICES(ctx, _NEW_BUFFERS); +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(ctx->Driver.FramebufferRenderbuffer); - ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } - /* 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); + if (renderbuffertarget != GL_RENDERBUFFER) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbuffer(renderbuffertarget is not " + "GL_RENDERBUFFER)"); + 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"); } void GLAPIENTRY -_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, - GLenum pname, GLint *params) +_mesa_NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) { - const struct gl_renderbuffer_attachment *att; - struct gl_framebuffer *buffer; - GLenum err; + struct gl_framebuffer *fb; + struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - /* The error differs in GL and GLES. */ - err = _mesa_is_desktop_gl(ctx) ? GL_INVALID_OPERATION : GL_INVALID_ENUM; + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glNamedFramebufferRenderbuffer"); + if (!fb) + return; - buffer = get_framebuffer_target(ctx, target); - if (!buffer) { + if (renderbuffertarget != GL_RENDERBUFFER) { _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); + "glNamedFramebufferRenderbuffer(renderbuffertarget is not " + "GL_RENDERBUFFER)"); return; } + if (renderbuffer) { + rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, + "glNamedFramebufferRenderbuffer"); + if (!rb) + return; + } + else { + /* remove renderbuffer attachment */ + rb = NULL; + } + + framebuffer_renderbuffer(ctx, fb, attachment, rb, + "glNamedFramebufferRenderbuffer"); +} + + +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; + GLenum err; + + /* 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: @@ -2309,34 +3621,72 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, * OES_framebuffer_object spec refers to the EXT_framebuffer_object * spec. */ - if (!_mesa_is_desktop_gl(ctx) || !ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameteriv(bound FBO = 0)"); - return; + 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; + } + + if (_mesa_is_gles3(ctx) && attachment != GL_BACK && + attachment != GL_DEPTH && attachment != GL_STENCIL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); + return; + } + + /* 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, + "%s(requesting GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME " + "when GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is " + "GL_FRAMEBUFFER_DEFAULT is not allowed)", caller); + return; } + /* 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) { + const struct gl_renderbuffer_attachment *depthAtt, *stencilAtt; + 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 */ - 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); + 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; } } @@ -2345,37 +3695,44 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: - *params = _mesa_is_winsys_fbo(buffer) + /* 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); - if (_mesa_is_desktop_gl(ctx)) { + if (_mesa_is_desktop_gl(ctx) || _mesa_is_gles3(ctx)) { *params = 0; } else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + 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, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + _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: @@ -2388,44 +3745,46 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, } } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + _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 { *params = 0; } } - else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } 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, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { if (ctx->Extensions.EXT_framebuffer_sRGB) { - *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format); + *params = + _mesa_get_format_color_encoding(att->Renderbuffer->Format); } else { /* According to ARB_framebuffer_sRGB, we should return LINEAR @@ -2435,22 +3794,40 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, } 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, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + _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_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_X24S8) { + else if (format == MESA_FORMAT_Z32_FLOAT_S8X24_UINT) { /* depends on the attachment parameter */ if (attachment == GL_STENCIL_ATTACHMENT) { *params = GL_INDEX; @@ -2470,17 +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, - "glGetFramebufferAttachmentParameterivEXT(pname)"); + _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, @@ -2495,514 +3873,443 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, att->Renderbuffer->Format); } else { - _mesa_problem(ctx, "glGetFramebufferAttachmentParameterivEXT:" - " invalid FBO attachment structure"); + _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_image *srcImage; - struct gl_texture_object *texObj; - GLboolean error; - 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: - error = GL_FALSE; - break; - case GL_TEXTURE_CUBE_MAP: - error = !ctx->Extensions.ARB_texture_cube_map; - break; - case GL_TEXTURE_1D_ARRAY: - case GL_TEXTURE_2D_ARRAY: - error = !ctx->Extensions.EXT_texture_array; - break; - default: - error = GL_TRUE; - } - - if (error) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target=%s)", - _mesa_lookup_enum_by_nr(target)); - return; - } - - texObj = _mesa_get_current_tex_object(ctx, target); - - if (texObj->BaseLevel >= texObj->MaxLevel) { - /* nothing to do */ + buffer = get_framebuffer_target(ctx, target); + if (!buffer) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameteriv(invalid target %s)", + _mesa_enum_to_string(target)); return; } - if (texObj->Target == GL_TEXTURE_CUBE_MAP && - !_mesa_cube_complete(texObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGenerateMipmap(incomplete cube map)"); - return; - } + _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, + "glGetFramebufferAttachmentParameteriv"); +} - _mesa_lock_texture(ctx, texObj); - srcImage = _mesa_select_tex_image(ctx, texObj, target, texObj->BaseLevel); - if (!srcImage) { - _mesa_unlock_texture(ctx, texObj); - return; - } +void GLAPIENTRY +_mesa_GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, + GLenum attachment, + GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *buffer; - 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"); } -/** - * Helper function for checking if the datatypes of color buffers are - * compatible for glBlitFramebuffer. From the 3.1 spec, page 198: - * - * "GL_INVALID_OPERATION is generated if mask contains GL_COLOR_BUFFER_BIT - * and any of the following conditions hold: - * - The read buffer contains fixed-point or floating-point values and any - * draw buffer contains neither fixed-point nor floating-point values. - * - The read buffer contains unsigned integer values and any draw buffer - * does not contain unsigned integer values. - * - The read buffer contains signed integer values and any draw buffer - * does not contain signed integer values." - */ -static GLboolean -compatible_color_datatypes(gl_format srcFormat, gl_format dstFormat) +void GLAPIENTRY +_mesa_NamedFramebufferParameteri(GLuint framebuffer, GLenum pname, + GLint param) { - GLenum srcType = _mesa_get_format_datatype(srcFormat); - GLenum dstType = _mesa_get_format_datatype(dstFormat); + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb = NULL; - if (srcType != GL_INT && srcType != GL_UNSIGNED_INT) { - assert(srcType == GL_UNSIGNED_NORMALIZED || - srcType == GL_SIGNED_NORMALIZED || - srcType == GL_FLOAT); - /* Boil any of those types down to GL_FLOAT */ - srcType = GL_FLOAT; + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteri(" + "ARB_framebuffer_no_attachments not implemented)"); + return; } - if (dstType != GL_INT && dstType != GL_UNSIGNED_INT) { - assert(dstType == GL_UNSIGNED_NORMALIZED || - dstType == GL_SIGNED_NORMALIZED || - dstType == GL_FLOAT); - /* Boil any of those types down to GL_FLOAT */ - dstType = GL_FLOAT; - } + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glNamedFramebufferParameteri"); - return srcType == dstType; + if (fb) { + framebuffer_parameteri(ctx, fb, pname, param, + "glNamedFramebufferParameteriv"); + } } -/** - * Return the equivalent non-generic internal format. - * This is useful for comparing whether two internal formats are semantically - * equivalent. - */ -static GLenum -get_nongeneric_internalformat(GLenum format) +void GLAPIENTRY +_mesa_GetNamedFramebufferParameteriv(GLuint framebuffer, GLenum pname, + GLint *param) { - switch (format) { - /* GL 1.1 formats. */ - case 4: - case GL_RGBA: - return GL_RGBA8; - - case 3: - case GL_RGB: - return GL_RGB8; - - case 2: - case GL_LUMINANCE_ALPHA: - return GL_LUMINANCE8_ALPHA8; - - case 1: - case GL_LUMINANCE: - return GL_LUMINANCE8; - - case GL_ALPHA: - return GL_ALPHA8; - - case GL_INTENSITY: - return GL_INTENSITY8; - - /* GL_ARB_texture_rg */ - case GL_RED: - return GL_R8; - - case GL_RG: - return GL_RG8; - - /* GL_EXT_texture_sRGB */ - case GL_SRGB: - return GL_SRGB8; - - case GL_SRGB_ALPHA: - return GL_SRGB8_ALPHA8; - - case GL_SLUMINANCE: - return GL_SLUMINANCE8; - - case GL_SLUMINANCE_ALPHA: - return GL_SLUMINANCE8_ALPHA8; - - /* GL_EXT_texture_snorm */ - case GL_RGBA_SNORM: - return GL_RGBA8_SNORM; - - case GL_RGB_SNORM: - return GL_RGB8_SNORM; - - case GL_RG_SNORM: - return GL_RG8_SNORM; - - case GL_RED_SNORM: - return GL_R8_SNORM; - - case GL_LUMINANCE_ALPHA_SNORM: - return GL_LUMINANCE8_ALPHA8_SNORM; - - case GL_LUMINANCE_SNORM: - return GL_LUMINANCE8_SNORM; + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; - case GL_ALPHA_SNORM: - return GL_ALPHA8_SNORM; + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteriv(" + "ARB_framebuffer_no_attachments not implemented)"); + return; + } - case GL_INTENSITY_SNORM: - return GL_INTENSITY8_SNORM; + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glGetNamedFramebufferParameteriv"); + } else { + fb = ctx->WinSysDrawBuffer; + } - default: - return format; + if (fb) { + get_framebuffer_parameteriv(ctx, fb, pname, param, + "glGetNamedFramebufferParameteriv"); } } -static GLboolean -compatible_resolve_formats(const struct gl_renderbuffer *colorReadRb, - const struct gl_renderbuffer *colorDrawRb) +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) { - /* The simple case where we know the backing formats are the same. + int i; + + /* 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 (colorReadRb->Format == colorDrawRb->Format) { - return GL_TRUE; + if (numAttachments < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(numAttachments < 0)", name); + return; + } + + if (width < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(width < 0)", name); + return; + } + + if (height < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(height < 0)", name); + return; } - /* The Mesa formats are different, so we must check whether the internal - * formats are compatible. + /* 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." * - * Under some circumstances, the user may request e.g. two GL_RGBA8 - * textures and get two entirely different Mesa formats like RGBA8888 and - * ARGB8888. Drivers behaving like that should be able to cope with - * non-matching formats by themselves, because it's not the user's fault. + * 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). */ - if (get_nongeneric_internalformat(colorReadRb->InternalFormat) == - get_nongeneric_internalformat(colorDrawRb->InternalFormat)) { - return GL_TRUE; + 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; + } + } } - return GL_FALSE; + /* We don't actually do anything for this yet. Just return after + * validating the parameters and generating the required errors. + */ + return; + +invalid_enum: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", name, + _mesa_enum_to_string(attachments[i])); + return; } -/** - * 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_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, + GLsizei width, GLsizei height) +{ + struct gl_framebuffer *fb; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateSubFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, - "glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)\n", - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, _mesa_lookup_enum_by_nr(filter)); + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + x, y, width, height, + "glInvalidateSubFramebuffer"); +} - if (ctx->NewState) { - _mesa_update_state(ctx); - } - readFb = ctx->ReadBuffer; - drawFb = ctx->DrawBuffer; +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); - if (!readFb || !drawFb) { - /* This will normally never happen but someday we may want to - * support MakeCurrent() with no drawables. - */ - return; + /* 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; - /* 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; - } + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + x, y, width, height, + "glInvalidateNamedFramebufferSubData"); +} - if (filter != GL_NEAREST && filter != GL_LINEAR) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)"); - return; - } - if (mask & ~legalMaskBits) { - _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); - return; - } +void GLAPIENTRY +_mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, + const GLenum *attachments) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); - /* 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)"); + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); return; } - /* get color read/draw renderbuffers */ - if (mask & GL_COLOR_BUFFER_BIT) { - colorReadRb = readFb->_ColorReadBuffer; - colorDrawRb = drawFb->_ColorDrawBuffers[0]; + /* 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"); +} - /* From the EXT_framebuffer_object spec: - * - * "If a buffer is specified in and does not exist in both - * the read and draw framebuffers, the corresponding bit is silently - * ignored." - */ - if ((colorReadRb == NULL) || (colorDrawRb == NULL)) { - colorReadRb = colorDrawRb = NULL; - mask &= ~GL_COLOR_BUFFER_BIT; - } - else if (!compatible_color_datatypes(colorReadRb->Format, - colorDrawRb->Format)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(color buffer datatypes mismatch)"); - return; - } - } - else { - colorReadRb = colorDrawRb = NULL; - } - if (mask & GL_STENCIL_BUFFER_BIT) { - struct gl_renderbuffer *readRb = - readFb->Attachment[BUFFER_STENCIL].Renderbuffer; - struct gl_renderbuffer *drawRb = - drawFb->Attachment[BUFFER_STENCIL].Renderbuffer; +void GLAPIENTRY +_mesa_InvalidateNamedFramebufferData(GLuint framebuffer, + GLsizei numAttachments, + const GLenum *attachments) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); - /* From the EXT_framebuffer_object spec: - * - * "If a buffer is specified in and does not exist in both - * the read and draw framebuffers, the corresponding bit is silently - * ignored." - */ - if ((readRb == NULL) || (drawRb == NULL)) { - mask &= ~GL_STENCIL_BUFFER_BIT; - } - else if (_mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS)) { - /* There is no need to check the stencil datatype here, because - * there is only one: GL_UNSIGNED_INT. - */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(stencil buffer size mismatch)"); + /* 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; - if (mask & GL_DEPTH_BUFFER_BIT) { - struct gl_renderbuffer *readRb = - readFb->Attachment[BUFFER_DEPTH].Renderbuffer; - struct gl_renderbuffer *drawRb = - drawFb->Attachment[BUFFER_DEPTH].Renderbuffer; - - /* From the EXT_framebuffer_object spec: - * - * "If a buffer is specified in and does not exist in both - * the read and draw framebuffers, the corresponding bit is silently - * ignored." - */ - if ((readRb == NULL) || (drawRb == NULL)) { - mask &= ~GL_DEPTH_BUFFER_BIT; - } - else if ((_mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS)) || - (_mesa_get_format_datatype(readRb->Format) != - _mesa_get_format_datatype(drawRb->Format))) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(depth buffer format mismatch)"); - return; - } - } + /* 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"); +} - if (readFb->Visual.samples > 0 && - drawFb->Visual.samples > 0 && - readFb->Visual.samples != drawFb->Visual.samples) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(mismatched samples)"); - 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; - } +void GLAPIENTRY +_mesa_DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, + const GLenum *attachments) +{ + struct gl_framebuffer *fb; + GLint i; - /* color formats must match */ - if (colorReadRb && - colorDrawRb && - !compatible_resolve_formats(colorReadRb, colorDrawRb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample pixel formats)"); - return; - } - } + GET_CURRENT_CONTEXT(ctx); - if (filter == GL_LINEAR && (mask & GL_COLOR_BUFFER_BIT)) { - /* 3.1 spec, page 199: - * "Calling BlitFramebuffer will result in an INVALID_OPERATION error - * if filter is LINEAR and read buffer contains integer data." - */ - GLenum type = _mesa_get_format_datatype(colorReadRb->Format); - if (type == GL_INT || type == GL_UNSIGNED_INT) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(integer color type)"); - return; - } + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glDiscardFramebufferEXT(target %s)", + _mesa_enum_to_string(target)); + return; } - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT"); + if (numAttachments < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glDiscardFramebufferEXT(numAttachments < 0)"); return; } - /* 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"); + 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 (!mask) { - return; - } - - ASSERT(ctx->Driver.BlitFramebuffer); - ctx->Driver.BlitFramebuffer(ctx, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); -} -#endif /* FEATURE_EXT_framebuffer_blit */ - - -#if FEATURE_ARB_geometry_shader4 -void GLAPIENTRY -_mesa_FramebufferTextureARB(GLenum target, GLenum attachment, - GLuint texture, GLint level) -{ - GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureARB " - "not implemented!"); -} + if (ctx->Driver.DiscardFramebuffer) + ctx->Driver.DiscardFramebuffer(ctx, target, numAttachments, attachments); + return; -void GLAPIENTRY -_mesa_FramebufferTextureFaceARB(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLenum face) -{ - GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureFaceARB " - "not implemented!"); +invalid_enum: + _mesa_error(ctx, GL_INVALID_ENUM, + "glDiscardFramebufferEXT(attachment %s)", + _mesa_enum_to_string(attachments[i])); } -#endif /* FEATURE_ARB_geometry_shader4 */