X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=d23916d1ad72318baa85bb42b66db1149e96a4f4;hb=ca721b3d894a49d7342f5aa053ed132017e9352a;hp=dc1e1a67321bf3509b0d4475805b8d02673bee5d;hpb=d890fc710f6d3472ea3837e074fafc100d41e01f;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index dc1e1a67321..d23916d1ad7 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -89,9 +89,9 @@ delete_dummy_framebuffer(struct gl_framebuffer *fb) void _mesa_init_fbobjects(struct gl_context *ctx) { - mtx_init(&DummyFramebuffer.Mutex, mtx_plain); - mtx_init(&DummyRenderbuffer.Mutex, mtx_plain); - mtx_init(&IncompleteFramebuffer.Mutex, mtx_plain); + simple_mtx_init(&DummyFramebuffer.Mutex, mtx_plain); + simple_mtx_init(&DummyRenderbuffer.Mutex, mtx_plain); + simple_mtx_init(&IncompleteFramebuffer.Mutex, mtx_plain); DummyFramebuffer.Delete = delete_dummy_framebuffer; DummyRenderbuffer.Delete = delete_dummy_renderbuffer; IncompleteFramebuffer.Delete = delete_dummy_framebuffer; @@ -222,15 +222,21 @@ get_framebuffer_target(struct gl_context *ctx, GLenum target) * default / window-system FB object. * If \p attachment is GL_DEPTH_STENCIL_ATTACHMENT, return a pointer to * the depth buffer attachment point. + * Returns if the attachment is a GL_COLOR_ATTACHMENTm_EXT on + * is_color_attachment, because several callers would return different errors + * if they don't find the attachment. */ static struct gl_renderbuffer_attachment * get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, - GLenum attachment) + GLenum attachment, bool *is_color_attachment) { GLuint i; assert(_mesa_is_user_fbo(fb)); + if (is_color_attachment) + *is_color_attachment = false; + switch (attachment) { case GL_COLOR_ATTACHMENT0_EXT: case GL_COLOR_ATTACHMENT1_EXT: @@ -248,6 +254,8 @@ get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, case GL_COLOR_ATTACHMENT13_EXT: case GL_COLOR_ATTACHMENT14_EXT: case GL_COLOR_ATTACHMENT15_EXT: + if (is_color_attachment) + *is_color_attachment = true; /* Only OpenGL ES 1.x forbids color attachments other than * GL_COLOR_ATTACHMENT0. For all other APIs the limit set by the * hardware is used. @@ -277,8 +285,8 @@ get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, * window-system framebuffer (not user-created framebuffer objects). */ static struct gl_renderbuffer_attachment * -_mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, - GLenum attachment) +get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment) { assert(_mesa_is_winsys_fbo(fb)); @@ -295,7 +303,7 @@ _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, return &fb->Attachment[BUFFER_BACK_LEFT]; return &fb->Attachment[BUFFER_FRONT_LEFT]; case GL_DEPTH: - return &fb->Attachment[BUFFER_DEPTH]; + return &fb->Attachment[BUFFER_DEPTH]; case GL_STENCIL: return &fb->Attachment[BUFFER_STENCIL]; } @@ -303,13 +311,34 @@ _mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, switch (attachment) { case GL_FRONT_LEFT: - return &fb->Attachment[BUFFER_FRONT_LEFT]; + /* Front buffers can be allocated on the first use, but + * glGetFramebufferAttachmentParameteriv must work even if that + * allocation hasn't happened yet. In such case, use the back buffer, + * which should be the same. + */ + if (fb->Attachment[BUFFER_FRONT_LEFT].Type == GL_NONE) + return &fb->Attachment[BUFFER_BACK_LEFT]; + else + return &fb->Attachment[BUFFER_FRONT_LEFT]; case GL_FRONT_RIGHT: - return &fb->Attachment[BUFFER_FRONT_RIGHT]; + /* Same as above. */ + if (fb->Attachment[BUFFER_FRONT_RIGHT].Type == GL_NONE) + return &fb->Attachment[BUFFER_BACK_RIGHT]; + else + return &fb->Attachment[BUFFER_FRONT_RIGHT]; case GL_BACK_LEFT: return &fb->Attachment[BUFFER_BACK_LEFT]; case GL_BACK_RIGHT: return &fb->Attachment[BUFFER_BACK_RIGHT]; + case GL_BACK: + /* The ARB_ES3_1_compatibility spec says: + * + * "Since this command can only query a single framebuffer + * attachment, BACK is equivalent to BACK_LEFT." + */ + if (ctx->Extensions.ARB_ES3_1_compatibility) + return &fb->Attachment[BUFFER_BACK_LEFT]; + return NULL; case GL_AUX0: if (fb->Visual.numAuxBuffers == 1) { return &fb->Attachment[BUFFER_AUX0]; @@ -389,7 +418,8 @@ 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->Width == 0 || texImage->Height == 0 || texImage->Depth == 0) + if (!texImage || + texImage->Width == 0 || texImage->Height == 0 || texImage->Depth == 0) return false; if ((texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY @@ -430,7 +460,7 @@ _mesa_update_texture_renderbuffer(struct gl_context *ctx, _mesa_error(ctx, GL_OUT_OF_MEMORY, "glFramebufferTexture()"); return; } - _mesa_reference_renderbuffer(&att->Renderbuffer, rb); + att->Renderbuffer = rb; /* This can't get called on a texture renderbuffer, so set it to NULL * for clarity compared to user renderbuffers. @@ -528,15 +558,15 @@ _mesa_FramebufferRenderbuffer_sw(struct gl_context *ctx, { struct gl_renderbuffer_attachment *att; - mtx_lock(&fb->Mutex); + simple_mtx_lock(&fb->Mutex); - att = get_attachment(ctx, fb, attachment); + att = get_attachment(ctx, fb, attachment, NULL); assert(att); if (rb) { set_renderbuffer_attachment(ctx, att, rb); if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { /* do stencil attachment here (depth already done above) */ - att = get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); + att = get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT, NULL); assert(att); set_renderbuffer_attachment(ctx, att, rb); } @@ -546,7 +576,7 @@ _mesa_FramebufferRenderbuffer_sw(struct gl_context *ctx, 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); + att = get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT, NULL); assert(att); remove_attachment(ctx, att); } @@ -554,7 +584,7 @@ _mesa_FramebufferRenderbuffer_sw(struct gl_context *ctx, invalidate_framebuffer(fb); - mtx_unlock(&fb->Mutex); + simple_mtx_unlock(&fb->Mutex); } @@ -708,6 +738,7 @@ is_format_color_renderable(const struct gl_context *ctx, mesa_format format, case GL_RGB8I: case GL_RGB8UI: case GL_SRGB8: + case GL_RGB10: case GL_RGB9_E5: case GL_RG8_SNORM: case GL_R8_SNORM: @@ -716,8 +747,11 @@ is_format_color_renderable(const struct gl_context *ctx, mesa_format format, break; } - if (format == MESA_FORMAT_B10G10R10A2_UNORM && - internalFormat != GL_RGB10_A2) { + if (internalFormat != GL_RGB10_A2 && + (format == MESA_FORMAT_B10G10R10A2_UNORM || + format == MESA_FORMAT_B10G10R10X2_UNORM || + format == MESA_FORMAT_R10G10B10A2_UNORM || + format == MESA_FORMAT_R10G10B10X2_UNORM)) { return GL_FALSE; } @@ -759,7 +793,7 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, /* Look for reasons why the attachment might be incomplete */ if (att->Type == GL_TEXTURE) { const struct gl_texture_object *texObj = att->Texture; - struct gl_texture_image *texImage; + const struct gl_texture_image *texImage; GLenum baseFormat; if (!texObj) { @@ -811,7 +845,7 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, break; } - baseFormat = _mesa_get_format_base_format(texImage->TexFormat); + baseFormat = texImage->_BaseFormat; if (format == GL_COLOR) { if (!_mesa_is_legal_color_format(ctx, baseFormat)) { @@ -830,8 +864,7 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, * 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)) { + if (_mesa_is_gles(ctx) && (texObj->_IsFloat || texObj->_IsHalfFloat)) { att_incomplete("bad internal format"); att->Complete = GL_FALSE; return; @@ -868,8 +901,7 @@ 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); if (!att->Renderbuffer->InternalFormat || @@ -957,6 +989,8 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, fb->Height = 0; fb->_AllColorBuffersFixedPoint = GL_TRUE; fb->_HasSNormOrFloatColorBuffer = GL_FALSE; + fb->_HasAttachments = true; + fb->_IntegerBuffers = 0; /* Start at -2 to more easily loop over all attachment points. * -2: depth buffer @@ -1077,13 +1111,14 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, continue; } - /* check if integer color */ - fb->_IntegerColor = _mesa_is_format_integer_color(attFormat); - - /* Update _AllColorBuffersFixedPoint and _HasSNormOrFloatColorBuffer. */ + /* Update flags describing color buffer datatypes */ if (i >= 0) { GLenum type = _mesa_get_format_datatype(attFormat); + /* check if integer color */ + if (_mesa_is_format_integer_color(attFormat)) + fb->_IntegerBuffers |= (1 << i); + fb->_AllColorBuffersFixedPoint = fb->_AllColorBuffersFixedPoint && (type == GL_UNSIGNED_NORMALIZED || type == GL_SIGNED_NORMALIZED); @@ -1155,14 +1190,48 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, } 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->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; - fbo_incomplete(ctx, "no attachments", -1); - return; + 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 (_mesa_is_desktop_gl(ctx) && !ctx->Extensions.ARB_ES2_compatibility) { @@ -1170,7 +1239,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) { if (fb->ColorDrawBuffer[j] != GL_NONE) { const struct gl_renderbuffer_attachment *att - = get_attachment(ctx, fb, fb->ColorDrawBuffer[j]); + = get_attachment(ctx, fb, fb->ColorDrawBuffer[j], NULL); assert(att); if (att->Type == GL_NONE) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT; @@ -1183,7 +1252,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, /* Check that the ReadBuffer is present */ if (fb->ColorReadBuffer != GL_NONE) { const struct gl_renderbuffer_attachment *att - = get_attachment(ctx, fb, fb->ColorReadBuffer); + = get_attachment(ctx, fb, fb->ColorReadBuffer, NULL); assert(att); if (att->Type == GL_NONE) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT; @@ -1218,42 +1287,42 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, ctx->Driver.ValidateFramebuffer(ctx, fb); if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { 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_IsRenderbuffer(GLuint renderbuffer) { + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - if (renderbuffer) { - struct gl_renderbuffer *rb = - _mesa_lookup_renderbuffer(ctx, renderbuffer); - if (rb != NULL && rb != &DummyRenderbuffer) - return GL_TRUE; - } - return GL_FALSE; + + rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + return rb != NULL && rb != &DummyRenderbuffer; } static struct gl_renderbuffer * -allocate_renderbuffer(struct gl_context *ctx, GLuint renderbuffer, - const char *func) +allocate_renderbuffer_locked(struct gl_context *ctx, GLuint renderbuffer, + const char *func) { struct gl_renderbuffer *newRb; @@ -1264,10 +1333,7 @@ allocate_renderbuffer(struct gl_context *ctx, GLuint renderbuffer, return NULL; } assert(newRb->AllocStorage); - mtx_lock(&ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); - newRb->RefCount = 1; /* referenced by hash table */ - mtx_unlock(&ctx->Shared->Mutex); + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, renderbuffer, newRb); return newRb; } @@ -1301,7 +1367,10 @@ bind_renderbuffer(GLenum target, GLuint renderbuffer, bool allow_user_names) } if (!newRb) { - newRb = allocate_renderbuffer(ctx, renderbuffer, "glBindRenderbufferEXT"); + _mesa_HashLockMutex(ctx->Shared->RenderBuffers); + newRb = allocate_renderbuffer_locked(ctx, renderbuffer, + "glBindRenderbufferEXT"); + _mesa_HashUnlockMutex(ctx->Shared->RenderBuffers); } } else { @@ -1334,6 +1403,203 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) 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 bool +_pname_valid_for_default_framebuffer(struct gl_context *ctx, + GLenum pname) +{ + if (!_mesa_is_desktop_gl(ctx)) + return false; + + switch (pname) { + case GL_DOUBLEBUFFER: + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: + case GL_IMPLEMENTATION_COLOR_READ_TYPE: + case GL_SAMPLES: + case GL_SAMPLE_BUFFERS: + case GL_STEREO: + return true; + default: + return false; + } +} + +static void +get_framebuffer_parameteriv(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum pname, GLint *params, const char *func) +{ + /* From OpenGL 4.5 spec, section 9.2.3 "Framebuffer Object Queries: + * + * "An INVALID_OPERATION error is generated by GetFramebufferParameteriv + * if the default framebuffer is bound to target and pname is not one + * of the accepted values from table 23.73, other than + * SAMPLE_POSITION." + * + * For OpenGL ES, using default framebuffer still raises INVALID_OPERATION + * for any pname. + */ + if (_mesa_is_winsys_fbo(fb) && + !_pname_valid_for_default_framebuffer(ctx, pname)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid pname=0x%x for default framebuffer)", func, pname); + return; + } + + 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; + case GL_DOUBLEBUFFER: + *params = fb->Visual.doubleBufferMode; + break; + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: + *params = _mesa_get_color_read_format(ctx, fb, func); + break; + case GL_IMPLEMENTATION_COLOR_READ_TYPE: + *params = _mesa_get_color_read_type(ctx, fb, func); + break; + case GL_SAMPLES: + *params = _mesa_geometric_samples(fb); + break; + case GL_SAMPLE_BUFFERS: + *params = _mesa_geometric_samples(fb) > 0; + break; + case GL_STEREO: + *params = fb->Visual.stereoMode; + 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; + } + + get_framebuffer_parameteriv(ctx, fb, pname, params, + "glGetFramebufferParameteriv"); +} + /** * Remove the specified renderbuffer or texture from any attachment point in @@ -1451,14 +1717,11 @@ create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, GLuint first; GLint i; - if (n < 0) { - _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++) { @@ -1466,19 +1729,35 @@ create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, renderbuffers[i] = name; if (dsa) { - allocate_renderbuffer(ctx, name, func); + allocate_renderbuffer_locked(ctx, name, func); } else { /* insert a dummy renderbuffer into the hash table */ - mtx_lock(&ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer); - mtx_unlock(&ctx->Shared->Mutex); + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, name, + &DummyRenderbuffer); } } + + _mesa_HashUnlockMutex(ctx->Shared->RenderBuffers); +} + + +static void +create_render_buffers_err(struct gl_context *ctx, GLsizei n, + GLuint *renderbuffers, bool dsa) +{ + const char *func = dsa ? "glCreateRenderbuffers" : "glGenRenderbuffers"; + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(n<0)", func); + return; + } + + create_render_buffers(ctx, n, renderbuffers, dsa); } void GLAPIENTRY -_mesa_GenRenderbuffers(GLsizei n, GLuint *renderbuffers) +_mesa_GenRenderbuffers_no_error(GLsizei n, GLuint *renderbuffers) { GET_CURRENT_CONTEXT(ctx); create_render_buffers(ctx, n, renderbuffers, false); @@ -1486,13 +1765,29 @@ _mesa_GenRenderbuffers(GLsizei n, GLuint *renderbuffers) void GLAPIENTRY -_mesa_CreateRenderbuffers(GLsizei n, GLuint *renderbuffers) +_mesa_GenRenderbuffers(GLsizei n, GLuint *renderbuffers) +{ + GET_CURRENT_CONTEXT(ctx); + create_render_buffers_err(ctx, n, renderbuffers, false); +} + + +void GLAPIENTRY +_mesa_CreateRenderbuffers_no_error(GLsizei n, GLuint *renderbuffers) { GET_CURRENT_CONTEXT(ctx); create_render_buffers(ctx, n, renderbuffers, true); } +void GLAPIENTRY +_mesa_CreateRenderbuffers(GLsizei n, GLuint *renderbuffers) +{ + GET_CURRENT_CONTEXT(ctx); + create_render_buffers_err(ctx, n, renderbuffers, true); +} + + /** * Given an internal format token for a render buffer, return the * corresponding base format (one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, @@ -1509,7 +1804,7 @@ _mesa_CreateRenderbuffers(GLsizei n, GLuint *renderbuffers) * \return the base internal format, or 0 if internalFormat is illegal */ GLenum -_mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) +_mesa_base_fbo_format(const struct gl_context *ctx, GLenum internalFormat) { /* * Notes: some formats such as alpha, luminance, etc. were added @@ -1828,53 +2123,21 @@ invalidate_rb(GLuint key, void *data, void *userData) /** sentinal value, see below */ #define NO_SAMPLES 1000 - -/** - * 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) +void +_mesa_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei samples) { - GLenum baseFormat; - GLenum sample_count_error; - - baseFormat = _mesa_base_fbo_format(ctx, internalFormat); - if (baseFormat == 0) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat=%s)", - func, _mesa_lookup_enum_by_nr(internalFormat)); - return; - } - - if (width < 0 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid width %d)", func, - width); - return; - } - - if (height < 0 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid height %d)", func, - height); - return; - } + const GLenum baseFormat = _mesa_base_fbo_format(ctx, internalFormat); - 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); - if (sample_count_error != GL_NO_ERROR) { - _mesa_error(ctx, sample_count_error, "%s(samples)", func); - return; - } + 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); @@ -1919,34 +2182,95 @@ renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, } /** - * Helper function used by _mesa_NamedRenderbufferStorage*(). + * 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_named(GLuint renderbuffer, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei samples, - const char *func) +renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + 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_lookup_enum_by_nr(internalFormat), - width, height); - else - _mesa_debug(ctx, "%s(%u, %s, %d, %d, %d)\n", - func, renderbuffer, - _mesa_lookup_enum_by_nr(internalFormat), - width, height, samples); - } + GLenum baseFormat; + GLenum sample_count_error; - 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); + 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 (width < 0 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid width %d)", func, + width); + return; + } + + if (height < 0 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid height %d)", func, + height); + return; + } + + 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); + + /* Section 2.5 (GL Errors) of OpenGL 3.0 specification, page 16: + * + * "If a negative number is provided where an argument of type sizei or + * sizeiptr is specified, the error INVALID VALUE is generated." + */ + if (samples < 0) { + sample_count_error = GL_INVALID_VALUE; + } + + if (sample_count_error != GL_NO_ERROR) { + _mesa_error(ctx, sample_count_error, "%s(samples=%d)", func, samples); + return; + } + } + + _mesa_renderbuffer_storage(ctx, rb, internalFormat, width, height, samples); +} + +/** + * Helper function used by _mesa_NamedRenderbufferStorage*(). + * samples will be NO_SAMPLES if called by a non-multisample function. + */ +static void +renderbuffer_storage_named(GLuint renderbuffer, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei samples, + const char *func) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_API) { + if (samples == NO_SAMPLES) + _mesa_debug(ctx, "%s(%u, %s, %d, %d)\n", + func, renderbuffer, + _mesa_enum_to_string(internalFormat), + width, height); + else + _mesa_debug(ctx, "%s(%u, %s, %d, %d, %d)\n", + func, renderbuffer, + _mesa_enum_to_string(internalFormat), + width, height, samples); + } + + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (!rb || rb == &DummyRenderbuffer) { + /* ID was reserved, but no real renderbuffer object made yet */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid renderbuffer %u)", + func, renderbuffer); return; } @@ -1969,14 +2293,14 @@ renderbuffer_storage_target(GLenum target, GLenum internalFormat, if (samples == NO_SAMPLES) _mesa_debug(ctx, "%s(%s, %s, %d, %d)\n", func, - _mesa_lookup_enum_by_nr(target), - _mesa_lookup_enum_by_nr(internalFormat), + _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_lookup_enum_by_nr(target), - _mesa_lookup_enum_by_nr(internalFormat), + _mesa_enum_to_string(target), + _mesa_enum_to_string(internalFormat), width, height, samples); } @@ -2149,7 +2473,7 @@ get_render_buffer_parameteriv(struct gl_context *ctx, /* fallthrough */ default: _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid pname=%s)", func, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); return; } } @@ -2263,7 +2587,6 @@ 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); @@ -2317,18 +2640,23 @@ bind_framebuffer(GLenum target, GLuint framebuffer, bool allow_user_names) 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. @@ -2365,7 +2693,12 @@ bind_framebuffer(GLenum target, GLuint framebuffer, bool allow_user_names) } 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); } } @@ -2462,6 +2795,8 @@ create_framebuffers(GLsizei n, GLuint *framebuffers, bool dsa) if (!framebuffers) return; + _mesa_HashLockMutex(ctx->Shared->FrameBuffers); + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n); for (i = 0; i < n; i++) { @@ -2471,6 +2806,7 @@ create_framebuffers(GLsizei n, GLuint *framebuffers, bool dsa) 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; } @@ -2478,10 +2814,10 @@ create_framebuffers(GLsizei n, GLuint *framebuffers, bool dsa) else fb = &DummyFramebuffer; - mtx_lock(&ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->FrameBuffers, name, fb); - mtx_unlock(&ctx->Shared->Mutex); + _mesa_HashInsertLocked(ctx->Shared->FrameBuffers, name, fb); } + + _mesa_HashUnlockMutex(ctx->Shared->FrameBuffers); } @@ -2524,6 +2860,16 @@ _mesa_check_framebuffer_status(struct gl_context *ctx, } +GLenum GLAPIENTRY +_mesa_CheckFramebufferStatus_no_error(GLenum target) +{ + GET_CURRENT_CONTEXT(ctx); + + struct gl_framebuffer *fb = get_framebuffer_target(ctx, target); + return _mesa_check_framebuffer_status(ctx, fb); +} + + GLenum GLAPIENTRY _mesa_CheckFramebufferStatus(GLenum target) { @@ -2532,13 +2878,13 @@ _mesa_CheckFramebufferStatus(GLenum target) if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(ctx, "glCheckFramebufferStatus(%s)\n", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); fb = get_framebuffer_target(ctx, target); if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return 0; } @@ -2570,7 +2916,7 @@ _mesa_CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target) default: _mesa_error(ctx, GL_INVALID_ENUM, "glCheckNamedFramebufferStatus(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return 0; } @@ -2606,7 +2952,19 @@ reuse_framebuffer_texture_attachment(struct gl_framebuffer *fb, dst_att->Type = src_att->Type; dst_att->Complete = src_att->Complete; dst_att->TextureLevel = src_att->TextureLevel; + dst_att->CubeMapFace = src_att->CubeMapFace; dst_att->Zoffset = src_att->Zoffset; + dst_att->Layered = src_att->Layered; +} + + +static struct gl_texture_object * +get_texture_for_framebuffer(struct gl_context *ctx, GLuint texture) +{ + if (!texture) + return NULL; + + return _mesa_lookup_texture(ctx, texture); } @@ -2620,9 +2978,9 @@ reuse_framebuffer_texture_attachment(struct gl_framebuffer *fb, * \return true if no errors, false if errors */ static bool -get_texture_for_framebuffer(struct gl_context *ctx, GLuint texture, - bool layered, const char *caller, - struct gl_texture_object **texObj) +get_texture_for_framebuffer_err(struct gl_context *ctx, GLuint texture, + bool layered, const char *caller, + struct gl_texture_object **texObj) { *texObj = NULL; /* This will get returned if texture = 0. */ @@ -2689,7 +3047,7 @@ check_layered_texture_target(struct gl_context *ctx, GLenum target, _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid texture target %s)", caller, - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return false; } @@ -2704,9 +3062,13 @@ static bool check_texture_target(struct gl_context *ctx, GLenum target, const char *caller) { - /* 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. + /* 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: @@ -2715,11 +3077,19 @@ check_texture_target(struct gl_context *ctx, GLenum target, 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_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return false; } @@ -2736,59 +3106,55 @@ check_textarget(struct gl_context *ctx, int dims, GLenum target, { 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; - } + switch (textarget) { + case GL_TEXTURE_1D: + err = dims != 1; 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; - break; - default: - err = true; - } + case GL_TEXTURE_1D_ARRAY: + err = dims != 1 || !ctx->Extensions.EXT_texture_array; + break; + case GL_TEXTURE_2D: + err = dims != 2; break; - case 3: - if (textarget != GL_TEXTURE_3D) - err = true; + case GL_TEXTURE_2D_ARRAY: + err = dims != 2 || !ctx->Extensions.EXT_texture_array || + (_mesa_is_gles(ctx) && ctx->Version < 30); break; - default: + case GL_TEXTURE_2D_MULTISAMPLE: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + err = dims != 2 || + !ctx->Extensions.ARB_texture_multisample || + (_mesa_is_gles(ctx) && ctx->Version < 31); + break; + case GL_TEXTURE_RECTANGLE: + err = dims != 2 || _mesa_is_gles(ctx) || + !ctx->Extensions.NV_texture_rectangle; + break; + case GL_TEXTURE_CUBE_MAP: + case GL_TEXTURE_CUBE_MAP_ARRAY: err = true; + 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 = dims != 2 || !ctx->Extensions.ARB_texture_cube_map; + break; + case GL_TEXTURE_3D: + err = dims != 3; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(unknown textarget 0x%x)", caller, textarget); + return false; } if (err) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid textarget %s)", - caller, _mesa_lookup_enum_by_nr(textarget)); + caller, _mesa_enum_to_string(textarget)); return false; } @@ -2823,8 +3189,7 @@ check_layer(struct gl_context *ctx, GLenum target, GLint layer, * and layer is negative." */ if (layer < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "%s(layer %u < 0)", caller, layer); + _mesa_error(ctx, GL_INVALID_VALUE, "%s(layer %d < 0)", caller, layer); return false; } @@ -2847,6 +3212,13 @@ check_layer(struct gl_context *ctx, GLenum target, GLint 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; } @@ -2859,11 +3231,19 @@ check_layer(struct gl_context *ctx, GLenum target, GLint layer, * \return true if no errors, false if errors */ static bool -check_level(struct gl_context *ctx, GLenum target, GLint level, - const char *caller) +check_level(struct gl_context *ctx, struct gl_texture_object *texObj, + GLenum target, GLint level, const char *caller) { - if ((level < 0) || - (level >= _mesa_max_texture_levels(ctx, target))) { + /* Section 9.2.8 of the OpenGL 4.6 specification says: + * + * "If texture refers to an immutable-format texture, level must be + * greater than or equal to zero and smaller than the value of + * TEXTURE_VIEW_NUM_LEVELS for texture." + */ + const int max_levels = texObj->Immutable ? texObj->ImmutableLevels : + _mesa_max_texture_levels(ctx, target); + + if (level < 0 || level >= max_levels) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid level %d)", caller, level); return false; @@ -2873,33 +3253,49 @@ check_level(struct gl_context *ctx, GLenum target, GLint level, } -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 * +_mesa_get_and_validate_attachment(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLenum attachment, 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; + return NULL; } /* Not a hash lookup, so we can afford to get the attachment here. */ - att = get_attachment(ctx, fb, attachment); + bool is_color_attachment; + struct gl_renderbuffer_attachment *att = + get_attachment(ctx, fb, attachment, &is_color_attachment); if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, - _mesa_lookup_enum_by_nr(attachment)); - return; + if (is_color_attachment) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid color attachment %s)", caller, + _mesa_enum_to_string(attachment)); + } else { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); + } + return NULL; } + return att; +} + + +void +_mesa_framebuffer_texture(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment, + struct gl_renderbuffer_attachment *att, + struct gl_texture_object *texObj, GLenum textarget, + GLint level, GLuint layer, GLboolean layered) +{ FLUSH_VERTICES(ctx, _NEW_BUFFERS); - mtx_lock(&fb->Mutex); + simple_mtx_lock(&fb->Mutex); if (texObj) { if (attachment == GL_DEPTH_ATTACHMENT && texObj == fb->Attachment[BUFFER_STENCIL].Texture && @@ -2958,7 +3354,29 @@ _mesa_framebuffer_texture(struct gl_context *ctx, struct gl_framebuffer *fb, invalidate_framebuffer(fb); - mtx_unlock(&fb->Mutex); + simple_mtx_unlock(&fb->Mutex); +} + + +static void +framebuffer_texture_with_dims_no_error(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint layer) +{ + GET_CURRENT_CONTEXT(ctx); + + /* Get the framebuffer object */ + struct gl_framebuffer *fb = get_framebuffer_target(ctx, target); + + /* Get the texture object */ + struct gl_texture_object *texObj = + get_texture_for_framebuffer(ctx, texture); + + struct gl_renderbuffer_attachment *att = + get_attachment(ctx, fb, attachment, NULL); + + _mesa_framebuffer_texture(ctx, fb, attachment, att, texObj, textarget, + level, layer, GL_FALSE); } @@ -2976,12 +3394,12 @@ framebuffer_texture_with_dims(int dims, GLenum target, fb = get_framebuffer_target(ctx, target); if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid target %s)", caller, - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } /* Get the texture object */ - if (!get_texture_for_framebuffer(ctx, texture, false, caller, &texObj)) + if (!get_texture_for_framebuffer_err(ctx, texture, false, caller, &texObj)) return; if (texObj) { @@ -2990,13 +3408,28 @@ framebuffer_texture_with_dims(int dims, GLenum target, if ((dims == 3) && !check_layer(ctx, texObj->Target, layer, caller)) return; + + if (!check_level(ctx, texObj, textarget, level, caller)) + return; } - if (!check_level(ctx, textarget, level, caller)) + struct gl_renderbuffer_attachment *att = + _mesa_get_and_validate_attachment(ctx, fb, attachment, caller); + if (!att) return; - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, - layer, GL_FALSE, caller); + _mesa_framebuffer_texture(ctx, fb, attachment, att, texObj, textarget, + level, layer, GL_FALSE); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture1D_no_error(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level) +{ + framebuffer_texture_with_dims_no_error(target, attachment, textarget, + texture, level, 0); } @@ -3009,6 +3442,16 @@ _mesa_FramebufferTexture1D(GLenum target, GLenum attachment, } +void GLAPIENTRY +_mesa_FramebufferTexture2D_no_error(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level) +{ + framebuffer_texture_with_dims_no_error(target, attachment, textarget, + texture, level, 0); +} + + void GLAPIENTRY _mesa_FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) @@ -3018,6 +3461,16 @@ _mesa_FramebufferTexture2D(GLenum target, GLenum attachment, } +void GLAPIENTRY +_mesa_FramebufferTexture3D_no_error(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint layer) +{ + framebuffer_texture_with_dims_no_error(target, attachment, textarget, + texture, level, layer); +} + + void GLAPIENTRY _mesa_FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, @@ -3028,120 +3481,157 @@ _mesa_FramebufferTexture3D(GLenum target, GLenum attachment, } -void GLAPIENTRY -_mesa_FramebufferTextureLayer(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLint layer) +static ALWAYS_INLINE void +frame_buffer_texture(GLuint framebuffer, GLenum target, + GLenum attachment, GLuint texture, + GLint level, GLint layer, const char *func, + bool dsa, bool no_error, bool check_layered) { GET_CURRENT_CONTEXT(ctx); - struct gl_framebuffer *fb; - struct gl_texture_object *texObj; + GLboolean layered = GL_FALSE; - const char *func = "glFramebufferTextureLayer"; + if (!no_error && check_layered) { + if (!_mesa_has_geometry_shaders(ctx)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "unsupported function (%s) called", func); + return; + } + } /* Get the framebuffer object */ - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTextureLayer(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); - return; + struct gl_framebuffer *fb; + if (no_error) { + if (dsa) { + fb = _mesa_lookup_framebuffer(ctx, framebuffer); + } else { + fb = get_framebuffer_target(ctx, target); + } + } else { + if (dsa) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); + if (!fb) + return; + } else { + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid target %s)", + func, _mesa_enum_to_string(target)); + 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)) + /* Get the texture object and framebuffer attachment*/ + struct gl_renderbuffer_attachment *att; + struct gl_texture_object *texObj; + if (no_error) { + texObj = get_texture_for_framebuffer(ctx, texture); + att = get_attachment(ctx, fb, attachment, NULL); + } else { + if (!get_texture_for_framebuffer_err(ctx, texture, check_layered, func, + &texObj)) return; - if (!check_level(ctx, texObj->Target, level, func)) + att = _mesa_get_and_validate_attachment(ctx, fb, attachment, func); + if (!att) return; } - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, - layer, GL_FALSE, func); -} - + GLenum textarget = 0; + if (texObj) { + if (check_layered) { + /* We do this regardless of no_error because this sets layered */ + if (!check_layered_texture_target(ctx, texObj->Target, func, + &layered)) + return; + } -void GLAPIENTRY -_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; + if (!no_error) { + if (!check_layered) { + if (!check_texture_target(ctx, texObj->Target, func)) + return; - const char *func = "glNamedFramebufferTextureLayer"; + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + } - /* Get the framebuffer object */ - fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); - if (!fb) - return; + if (!check_level(ctx, texObj, texObj->Target, level, func)) + return; + } - /* Get the texture object */ - if (!get_texture_for_framebuffer(ctx, texture, false, func, &texObj)) - return; + if (!check_layered && texObj->Target == GL_TEXTURE_CUBE_MAP) { + assert(layer >= 0 && layer < 6); + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + layer = 0; + } + } - if (texObj) { - if (!check_texture_target(ctx, texObj->Target, func)) - return; + _mesa_framebuffer_texture(ctx, fb, attachment, att, texObj, textarget, + level, layer, layered); +} - if (!check_layer(ctx, texObj->Target, layer, func)) - return; +void GLAPIENTRY +_mesa_FramebufferTextureLayer_no_error(GLenum target, GLenum attachment, + GLuint texture, GLint level, + GLint layer) +{ + frame_buffer_texture(0, target, attachment, texture, level, layer, + "glFramebufferTextureLayer", false, true, false); +} - if (!check_level(ctx, texObj->Target, level, func)) - return; - } - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, - layer, GL_FALSE, func); +void GLAPIENTRY +_mesa_FramebufferTextureLayer(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer) +{ + frame_buffer_texture(0, target, attachment, texture, level, layer, + "glFramebufferTextureLayer", false, false, false); } void GLAPIENTRY -_mesa_FramebufferTexture(GLenum target, GLenum attachment, - GLuint texture, GLint level) +_mesa_NamedFramebufferTextureLayer_no_error(GLuint framebuffer, + GLenum attachment, + GLuint texture, GLint level, + GLint layer) { - GET_CURRENT_CONTEXT(ctx); - struct gl_framebuffer *fb; - struct gl_texture_object *texObj; - GLboolean layered; + frame_buffer_texture(framebuffer, 0, attachment, texture, level, layer, + "glNamedFramebufferTextureLayer", true, true, false); +} - const char *func = "FramebufferTexture"; - if (!_mesa_has_geometry_shaders(ctx)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "unsupported function (glFramebufferTexture) called"); - return; - } +void GLAPIENTRY +_mesa_NamedFramebufferTextureLayer(GLuint framebuffer, GLenum attachment, + GLuint texture, GLint level, GLint layer) +{ + frame_buffer_texture(framebuffer, 0, attachment, texture, level, layer, + "glNamedFramebufferTextureLayer", true, false, false); +} - /* Get the framebuffer object */ - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); - return; - } - /* Get the texture object */ - if (!get_texture_for_framebuffer(ctx, texture, true, func, &texObj)) - return; +void GLAPIENTRY +_mesa_FramebufferTexture_no_error(GLenum target, GLenum attachment, + GLuint texture, GLint level) +{ + frame_buffer_texture(0, target, attachment, texture, level, 0, + "glFramebufferTexture", false, true, true); +} - if (texObj) { - if (!check_layered_texture_target(ctx, texObj->Target, func, &layered)) - return; - if (!check_level(ctx, texObj->Target, level, func)) - return; - } +void GLAPIENTRY +_mesa_FramebufferTexture(GLenum target, GLenum attachment, + GLuint texture, GLint level) +{ + frame_buffer_texture(0, target, attachment, texture, level, 0, + "glFramebufferTexture", false, false, true); +} - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, - 0, layered, func); +void GLAPIENTRY +_mesa_NamedFramebufferTexture_no_error(GLuint framebuffer, GLenum attachment, + GLuint texture, GLint level) +{ + frame_buffer_texture(framebuffer, 0, attachment, texture, level, 0, + "glNamedFramebufferTexture", true, true, true); } @@ -3149,39 +3639,8 @@ void GLAPIENTRY _mesa_NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) { - GET_CURRENT_CONTEXT(ctx); - struct gl_framebuffer *fb; - struct gl_texture_object *texObj; - GLboolean layered; - - const char *func = "glNamedFramebufferTexture"; - - if (!_mesa_has_geometry_shaders(ctx)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "unsupported function (glNamedFramebufferTexture) called"); - return; - } - - /* Get the framebuffer object */ - fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); - if (!fb) - return; - - /* Get the texture object */ - if (!get_texture_for_framebuffer(ctx, texture, true, func, &texObj)) - return; - - if (texObj) { - if (!check_layered_texture_target(ctx, texObj->Target, func, - &layered)) - return; - - if (!check_level(ctx, texObj->Target, level, func)) - return; - } - - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, - 0, layered, func); + frame_buffer_texture(framebuffer, 0, attachment, texture, level, 0, + "glNamedFramebufferTexture", true, false, true); } @@ -3189,48 +3648,129 @@ void _mesa_framebuffer_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, GLenum attachment, - struct gl_renderbuffer *rb, - const char *func) + 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 ALWAYS_INLINE void +framebuffer_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer, const char *func, bool no_error) { struct gl_renderbuffer_attachment *att; + struct gl_renderbuffer *rb; + bool is_color_attachment; - if (_mesa_is_winsys_fbo(fb)) { - /* Can't attach new renderbuffers to a window system framebuffer */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "%s(window-system framebuffer)", func); + if (!no_error && renderbuffertarget != GL_RENDERBUFFER) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(renderbuffertarget is not GL_RENDERBUFFER)", func); return; } - att = get_attachment(ctx, fb, attachment); - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(invalid attachment %s)", func, - _mesa_lookup_enum_by_nr(attachment)); - return; + if (renderbuffer) { + if (!no_error) { + rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, func); + if (!rb) + return; + } else { + rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + } + } 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) { + if (!no_error) { + if (_mesa_is_winsys_fbo(fb)) { + /* Can't attach new renderbuffers to a window system framebuffer */ _mesa_error(ctx, GL_INVALID_OPERATION, - "%s(renderbuffer is not DEPTH_STENCIL format)", func); + "%s(window-system framebuffer)", func); + return; + } + + att = get_attachment(ctx, fb, attachment, &is_color_attachment); + if (att == NULL) { + /* + * From OpenGL 4.5 spec, section 9.2.7 "Attaching Renderbuffer Images + * to a Framebuffer": + * + * "An INVALID_OPERATION error is generated if attachment is + * COLOR_- ATTACHMENTm where m is greater than or equal to the + * value of MAX_COLOR_- ATTACHMENTS ." + * + * If we are at this point, is because the attachment is not valid, so + * if is_color_attachment is true, is because of the previous reason. + */ + if (is_color_attachment) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid color attachment %s)", func, + _mesa_enum_to_string(attachment)); + } else { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(invalid attachment %s)", func, + _mesa_enum_to_string(attachment)); + } + return; } + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT && + rb && rb->Format != MESA_FORMAT_NONE) { + /* make sure the renderbuffer is a depth/stencil format */ + const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); + if (baseFormat != GL_DEPTH_STENCIL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(renderbuffer is not DEPTH_STENCIL format)", func); + return; + } + } } - FLUSH_VERTICES(ctx, _NEW_BUFFERS); + _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb); +} - assert(ctx->Driver.FramebufferRenderbuffer); - ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); +static void +framebuffer_renderbuffer_error(struct gl_context *ctx, + struct gl_framebuffer *fb, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer, const char *func) +{ + framebuffer_renderbuffer(ctx, fb, attachment, renderbuffertarget, + renderbuffer, func, false); +} - /* 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_no_error(struct gl_context *ctx, + struct gl_framebuffer *fb, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer, const char *func) +{ + framebuffer_renderbuffer(ctx, fb, attachment, renderbuffertarget, + renderbuffer, func, true); } +void GLAPIENTRY +_mesa_FramebufferRenderbuffer_no_error(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + GET_CURRENT_CONTEXT(ctx); + + struct gl_framebuffer *fb = get_framebuffer_target(ctx, target); + framebuffer_renderbuffer_no_error(ctx, fb, attachment, renderbuffertarget, + renderbuffer, "glFramebufferRenderbuffer"); +} void GLAPIENTRY _mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, @@ -3238,86 +3778,79 @@ _mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, GLuint renderbuffer) { struct gl_framebuffer *fb; - struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); fb = get_framebuffer_target(ctx, target); if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "glFramebufferRenderbuffer(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } - if (renderbuffertarget != GL_RENDERBUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbuffer(renderbuffertarget is not " - "GL_RENDERBUFFER)"); - return; - } + framebuffer_renderbuffer_error(ctx, fb, attachment, renderbuffertarget, + renderbuffer, "glFramebufferRenderbuffer"); +} - if (renderbuffer) { - rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, - "glFramebufferRenderbuffer"); - if (!rb) - return; - } - else { - /* remove renderbuffer attachment */ - rb = NULL; - } +void GLAPIENTRY +_mesa_NamedFramebufferRenderbuffer_no_error(GLuint framebuffer, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + GET_CURRENT_CONTEXT(ctx); - _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb, - "glFramebufferRenderbuffer"); + struct gl_framebuffer *fb = _mesa_lookup_framebuffer(ctx, framebuffer); + framebuffer_renderbuffer_no_error(ctx, fb, attachment, renderbuffertarget, + renderbuffer, + "glNamedFramebufferRenderbuffer"); } - void GLAPIENTRY _mesa_NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { struct gl_framebuffer *fb; - struct gl_renderbuffer *rb; GET_CURRENT_CONTEXT(ctx); fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, "glNamedFramebufferRenderbuffer"); - - if (renderbuffertarget != GL_RENDERBUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glNamedFramebufferRenderbuffer(renderbuffertarget is not " - "GL_RENDERBUFFER)"); + if (!fb) return; - } - - if (renderbuffer) { - rb = _mesa_lookup_renderbuffer_err(ctx, renderbuffer, - "glNamedFramebufferRenderbuffer"); - if (!rb) - return; - } - else { - /* remove renderbuffer attachment */ - rb = NULL; - } - _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb, + framebuffer_renderbuffer_error(ctx, fb, attachment, renderbuffertarget, + renderbuffer, "glNamedFramebufferRenderbuffer"); } -void -_mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, - struct gl_framebuffer *buffer, - GLenum attachment, GLenum pname, - GLint *params, const char *caller) +static void +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; + bool is_color_attachment = false; GLenum err; - /* The error differs in GL and GLES. */ - err = _mesa_is_desktop_gl(ctx) ? GL_INVALID_OPERATION : GL_INVALID_ENUM; + /* 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 @@ -3342,20 +3875,52 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, attachment != GL_DEPTH && attachment != GL_STENCIL) { _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, - _mesa_lookup_enum_by_nr(attachment)); + _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); + att = get_fb0_attachment(ctx, buffer, attachment); } else { /* user-created framebuffer FBO */ - att = get_attachment(ctx, buffer, attachment); + att = get_attachment(ctx, buffer, attachment, &is_color_attachment); } if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, - _mesa_lookup_enum_by_nr(attachment)); + /* + * From OpenGL 4.5 spec, section 9.2.3 "Framebuffer Object Queries": + * + * "An INVALID_OPERATION error is generated if a framebuffer object + * is bound to target and attachment is COLOR_ATTACHMENTm where m is + * greater than or equal to the value of MAX_COLOR_ATTACHMENTS." + * + * If we are at this point, is because the attachment is not valid, so + * if is_color_attachment is true, is because of the previous reason. + */ + if (is_color_attachment) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid color attachment %s)", + caller, _mesa_enum_to_string(attachment)); + } else { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); + } return; } @@ -3374,8 +3939,8 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, return; } /* the depth and stencil attachments must point to the same buffer */ - depthAtt = get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT); - stencilAtt = get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); + depthAtt = get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT, NULL); + stencilAtt = get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT, NULL); if (depthAtt->Renderbuffer != stencilAtt->Renderbuffer) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(DEPTH/STENCIL attachments differ)", caller); @@ -3387,8 +3952,19 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, switch (pname) { case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: - *params = _mesa_is_winsys_fbo(buffer) - ? GL_FRAMEBUFFER_DEFAULT : att->Type; + /* From the OpenGL spec, 9.2. Binding and Managing Framebuffer Objects: + * + * "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then + * either no framebuffer is bound to target; or the default framebuffer + * is bound, attachment is DEPTH or STENCIL, and the number of depth or + * stencil bits, respectively, is zero." + * + * Note that we don't need explicit checks on DEPTH and STENCIL, because + * on the case the spec is pointing, att->Type is already NONE, so we + * just need to check att->Type. + */ + *params = (_mesa_is_winsys_fbo(buffer) && att->Type != GL_NONE) ? + GL_FRAMEBUFFER_DEFAULT : att->Type; return; case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: if (att->Type == GL_RENDERBUFFER_EXT) { @@ -3412,7 +3988,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, } else if (att->Type == GL_NONE) { _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; @@ -3429,7 +4005,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, } else if (att->Type == GL_NONE) { _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; @@ -3440,7 +4016,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, goto invalid_pname_enum; } else if (att->Type == GL_NONE) { _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _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)) { @@ -3461,8 +4037,13 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, goto invalid_pname_enum; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + if (_mesa_is_winsys_fbo(buffer) && + (attachment == GL_DEPTH || attachment == GL_STENCIL)) { + *params = GL_LINEAR; + } else { + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); + } } else { if (ctx->Extensions.EXT_framebuffer_sRGB) { @@ -3485,7 +4066,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, } else if (att->Type == GL_NONE) { _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); } else { mesa_format format = att->Renderbuffer->Format; @@ -3535,10 +4116,6 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, && !_mesa_is_gles3(ctx)) { goto invalid_pname_enum; } - else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); - } else if (att->Texture) { const struct gl_texture_image *texImage = _mesa_select_tex_image(att->Texture, att->Texture->Target, @@ -3556,7 +4133,9 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, att->Renderbuffer->Format); } else { - _mesa_problem(ctx, "%s: invalid FBO attachment structure", caller); + assert(att->Type == GL_NONE); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } return; case GL_FRAMEBUFFER_ATTACHMENT_LAYERED: @@ -3566,7 +4145,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, *params = att->Layered; } else if (att->Type == GL_NONE) { _mesa_error(ctx, err, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; } @@ -3579,7 +4158,7 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, invalid_pname_enum: _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid pname %s)", caller, - _mesa_lookup_enum_by_nr(pname)); + _mesa_enum_to_string(pname)); return; } @@ -3595,12 +4174,12 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, if (!buffer) { _mesa_error(ctx, GL_INVALID_ENUM, "glGetFramebufferAttachmentParameteriv(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } - _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, - params, + get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, "glGetFramebufferAttachmentParameteriv"); } @@ -3629,12 +4208,64 @@ _mesa_GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, buffer = ctx->WinSysDrawBuffer; } - _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, - params, + get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, "glGetNamedFramebufferAttachmentParameteriv"); } +void GLAPIENTRY +_mesa_NamedFramebufferParameteri(GLuint framebuffer, GLenum pname, + GLint param) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb = NULL; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteri(" + "ARB_framebuffer_no_attachments not implemented)"); + return; + } + + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glNamedFramebufferParameteri"); + + if (fb) { + framebuffer_parameteri(ctx, fb, pname, param, + "glNamedFramebufferParameteriv"); + } +} + + +void GLAPIENTRY +_mesa_GetNamedFramebufferParameteriv(GLuint framebuffer, GLenum pname, + GLint *param) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + + if (!ctx->Extensions.ARB_framebuffer_no_attachments) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glNamedFramebufferParameteriv(" + "ARB_framebuffer_no_attachments not implemented)"); + return; + } + + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glGetNamedFramebufferParameteriv"); + } else { + fb = ctx->WinSysDrawBuffer; + } + + if (fb) { + get_framebuffer_parameteriv(ctx, fb, pname, param, + "glGetNamedFramebufferParameteriv"); + } +} + + static void invalidate_framebuffer_storage(struct gl_context *ctx, struct gl_framebuffer *fb, @@ -3760,11 +4391,20 @@ invalidate_framebuffer_storage(struct gl_context *ctx, invalid_enum: _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", name, - _mesa_lookup_enum_by_nr(attachments[i])); + _mesa_enum_to_string(attachments[i])); return; } +void GLAPIENTRY +_mesa_InvalidateSubFramebuffer_no_error(GLenum target, GLsizei numAttachments, + const GLenum *attachments, GLint x, + GLint y, GLsizei width, GLsizei height) +{ + /* no-op */ +} + + void GLAPIENTRY _mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, @@ -3777,7 +4417,7 @@ _mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "glInvalidateSubFramebuffer(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } @@ -3816,6 +4456,14 @@ _mesa_InvalidateNamedFramebufferSubData(GLuint framebuffer, } +void GLAPIENTRY +_mesa_InvalidateFramebuffer_no_error(GLenum target, GLsizei numAttachments, + const GLenum *attachments) +{ + /* no-op */ +} + + void GLAPIENTRY _mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments) @@ -3827,7 +4475,7 @@ _mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "glInvalidateFramebuffer(invalid target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } @@ -3845,7 +4493,8 @@ _mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, */ invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, 0, 0, - MAX_VIEWPORT_WIDTH, MAX_VIEWPORT_HEIGHT, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, "glInvalidateFramebuffer"); } @@ -3885,7 +4534,8 @@ _mesa_InvalidateNamedFramebufferData(GLuint framebuffer, */ invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, 0, 0, - MAX_VIEWPORT_WIDTH, MAX_VIEWPORT_HEIGHT, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, "glInvalidateNamedFramebufferData"); } @@ -3903,7 +4553,7 @@ _mesa_DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, if (!fb) { _mesa_error(ctx, GL_INVALID_ENUM, "glDiscardFramebufferEXT(target %s)", - _mesa_lookup_enum_by_nr(target)); + _mesa_enum_to_string(target)); return; } @@ -3940,5 +4590,5 @@ _mesa_DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, invalid_enum: _mesa_error(ctx, GL_INVALID_ENUM, "glDiscardFramebufferEXT(attachment %s)", - _mesa_lookup_enum_by_nr(attachments[i])); + _mesa_enum_to_string(attachments[i])); }