X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=2c01526d16791c36104cde26d375b7765d037bca;hb=b9b88516f8d3efc902696f1092519e298ceb7cdb;hp=377f9152995a010314aa56328322be19c22d6d92;hpb=245e5c4813eb30e32bd61a08ae32ee278b09d1cf;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index 377f9152995..2c01526d167 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -120,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. */ @@ -137,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 @@ -347,7 +389,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 @@ -423,7 +466,7 @@ 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, + GLenum texTarget, GLuint level, GLuint layer, GLboolean layered) { struct gl_renderbuffer *rb = att->Renderbuffer; @@ -447,7 +490,7 @@ set_texture_attachment(struct gl_context *ctx, /* 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; @@ -479,9 +522,10 @@ 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; @@ -768,7 +812,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)) { @@ -813,8 +857,10 @@ test_attachment_completeness(const struct gl_context *ctx, GLenum format, 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; @@ -823,8 +869,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 || @@ -912,6 +957,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, 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 @@ -978,7 +1024,8 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, if (!is_format_color_renderable(ctx, attFormat, texImg->InternalFormat) && - !is_legal_depth_format(ctx, f)) { + !is_legal_depth_format(ctx, f) && + f != GL_STENCIL_INDEX) { fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; fbo_incomplete(ctx, "texture attachment incomplete", -1); return; @@ -1109,14 +1156,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) { @@ -1172,21 +1253,22 @@ _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); } @@ -1206,8 +1288,8 @@ _mesa_IsRenderbuffer(GLuint renderbuffer) 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; @@ -1218,7 +1300,7 @@ allocate_renderbuffer(struct gl_context *ctx, GLuint renderbuffer, return NULL; } assert(newRb->AllocStorage); - _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, renderbuffer, newRb); newRb->RefCount = 1; /* referenced by hash table */ return newRb; @@ -1253,7 +1335,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 { @@ -1286,6 +1371,155 @@ _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 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 @@ -1400,7 +1634,6 @@ create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, bool dsa) { const char *func = dsa ? "glCreateRenderbuffers" : "glGenRenderbuffers"; - struct gl_renderbuffer *obj; GLuint first; GLint i; @@ -1412,6 +1645,8 @@ create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, if (!renderbuffers) return; + _mesa_HashLockMutex(ctx->Shared->RenderBuffers); + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n); for (i = 0; i < n; i++) { @@ -1419,16 +1654,15 @@ create_render_buffers(struct gl_context *ctx, GLsizei n, GLuint *renderbuffers, renderbuffers[i] = name; if (dsa) { - obj = allocate_renderbuffer(ctx, name, func); + allocate_renderbuffer_locked(ctx, name, func); } else { - obj = &DummyRenderbuffer; - - /* insert the object into the hash table */ - mtx_lock(&ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->RenderBuffers, name, obj); - mtx_unlock(&ctx->Shared->Mutex); + /* insert a dummy renderbuffer into the hash table */ + _mesa_HashInsertLocked(ctx->Shared->RenderBuffers, name, + &DummyRenderbuffer); } } + + _mesa_HashUnlockMutex(ctx->Shared->RenderBuffers); } @@ -1783,80 +2017,21 @@ invalidate_rb(GLuint key, void *data, void *userData) /** sentinal value, see below */ #define NO_SAMPLES 1000 - -/** - * Helper function used by _mesa_RenderbufferStorage() and - * _mesa_RenderbufferStorageMultisample(). - * samples will be NO_SAMPLES if called by _mesa_RenderbufferStorage(). - */ -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" : "glRenderbufferStorageMultisample"; - struct gl_renderbuffer *rb; - GLenum baseFormat; - GLenum sample_count_error; - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_API) { - 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), - 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), - width, height, samples); - } - - 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=%s)", - func, _mesa_lookup_enum_by_nr(internalFormat)); - 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 { - /* check the sample count; - * note: driver may choose to use more samples than what's requested - */ - sample_count_error = _mesa_check_sample_count(ctx, target, - internalFormat, samples); - if (sample_count_error != GL_NO_ERROR) { - _mesa_error(ctx, sample_count_error, "%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); @@ -1900,43 +2075,181 @@ renderbuffer_storage(GLenum target, GLenum internalFormat, } } - -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); + 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); -} + /* 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_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 + * 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. */ @@ -1959,7 +2272,8 @@ _mesa_RenderbufferStorage(GLenum target, GLenum internalFormat, * 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"); } @@ -1968,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"); } @@ -1989,7 +2304,30 @@ _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_NamedRenderbufferStorage(GLuint renderbuffer, 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_named(renderbuffer, internalformat, width, height, + NO_SAMPLES, "glNamedRenderbufferStorage"); +} + +void GLAPIENTRY +_mesa_NamedRenderbufferStorageMultisample(GLuint renderbuffer, GLsizei samples, + GLenum internalformat, + GLsizei width, GLsizei height) +{ + renderbuffer_storage_named(renderbuffer, internalformat, width, height, + samples, + "glNamedRenderbufferStorageMultisample"); } @@ -2029,7 +2367,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; } } @@ -2143,7 +2481,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); @@ -2197,18 +2534,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. @@ -2245,7 +2587,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); } } @@ -2319,51 +2666,74 @@ _mesa_DeleteFramebuffers(GLsizei n, const GLuint *framebuffers) } -void GLAPIENTRY -_mesa_GenFramebuffers(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; + + 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 */ - mtx_lock(&ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer); - mtx_unlock(&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); } -GLenum GLAPIENTRY -_mesa_CheckFramebufferStatus(GLenum target) +void GLAPIENTRY +_mesa_GenFramebuffers(GLsizei n, GLuint *framebuffers) { - struct gl_framebuffer *buffer; - GET_CURRENT_CONTEXT(ctx); + create_framebuffers(n, framebuffers, false); +} - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glCheckFramebufferStatus(%s)\n", - _mesa_lookup_enum_by_nr(target)); +void GLAPIENTRY +_mesa_CreateFramebuffers(GLsizei n, GLuint *framebuffers) +{ + create_framebuffers(n, framebuffers, true); +} - 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)) { /* EGL_KHR_surfaceless_context allows the winsys FBO to be incomplete. */ @@ -2384,6 +2754,67 @@ _mesa_CheckFramebufferStatus(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 @@ -2406,161 +2837,327 @@ 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/3D() and - * glFramebufferTextureLayer(). + * Common code called by gl*FramebufferTexture*() to retrieve the correct + * texture object pointer. * - * \param textarget is the textarget that was passed to the - * glFramebufferTexture...() function, or 0 if the corresponding function - * doesn't have a textarget parameter. + * \param texObj where the pointer to the texture object is returned. Note + * that a successful call may return texObj = NULL. * - * \param layered is true if this function was called from - * glFramebufferTexture(), false otherwise. + * \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, GLuint zoffset, GLboolean layered) +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. */ - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%s(target=0x%x)", caller, target); - return; - } + if (!texture) + return true; - /* check framebuffer binding */ - if (_mesa_is_winsys_fbo(fb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%s", caller); - 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; } - /* 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 (layered) { - /* We're being called by glFramebufferTexture() and textarget - * is not used. - */ - switch (texObj->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: - err = false; - break; - 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}(). - */ - err = false; - layered = false; - textarget = texObj->Target; - break; - default: - err = true; - break; - } - } else { - /* 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) && - (texObj->Target != GL_TEXTURE_CUBE_MAP_ARRAY) && - (texObj->Target != GL_TEXTURE_2D_MULTISAMPLE_ARRAY); - } - } - 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%s(non existant texture)", - caller); - return; - } + return true; +} - if (err) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%s(texture target mismatch)", - caller); - return; - } - if (texObj->Target == GL_TEXTURE_3D) { - const GLuint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); - if (zoffset >= maxSize) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%s(zoffset)", caller); - return; - } - } - else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) || - (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT) || - (texObj->Target == GL_TEXTURE_CUBE_MAP_ARRAY) || - (texObj->Target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) { - if (zoffset >= ctx->Const.MaxArrayTextureLayers) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%s(layer)", 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; - maxLevelsTarget = textarget ? textarget : texObj->Target; - if ((level < 0) || - (level >= _mesa_max_texture_levels(ctx, maxLevelsTarget))) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%s(level)", 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; } - att = get_attachment(ctx, fb, attachment); - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%s(attachment)", caller); - return; + _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; } - FLUSH_VERTICES(ctx, _NEW_BUFFERS); + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid texture target %s)", caller, + _mesa_enum_to_string(target)); + return false; +} - mtx_lock(&fb->Mutex); - if (texObj) { - if (attachment == GL_DEPTH_ATTACHMENT && + +/** + * 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; + } + 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; + } + + if (target == GL_TEXTURE_3D) { + const GLuint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); + if (layer >= maxSize) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(invalid layer %u)", caller, layer); + return false; + } + } + else if ((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; +} + + +/** + * 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, "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + 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) { + 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 @@ -2573,13 +3170,14 @@ framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, level == fb->Attachment[BUFFER_DEPTH].TextureLevel && _mesa_tex_target_to_face(textarget) == fb->Attachment[BUFFER_DEPTH].CubeMapFace && - zoffset == fb->Attachment[BUFFER_DEPTH].Zoffset) { + layer == fb->Attachment[BUFFER_DEPTH].Zoffset) { /* As above, but with depth and stencil transposed. */ reuse_framebuffer_texture_attachment(fb, BUFFER_STENCIL, BUFFER_DEPTH); } else { set_texture_attachment(ctx, fb, att, texObj, textarget, - level, zoffset, layered); + 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 @@ -2615,116 +3213,157 @@ framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, } -void GLAPIENTRY -_mesa_FramebufferTexture1D(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; - if (texture != 0) { - GLboolean error; + /* 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; + } - 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; - } + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, caller, &texObj)) + return; - if (error) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture1D(textarget=%s)", - _mesa_lookup_enum_by_nr(textarget)); + if (texObj) { + if (!check_textarget(ctx, dims, texObj->Target, textarget, caller)) + return; + + if ((dims == 3) && !check_layer(ctx, texObj->Target, layer, caller)) + return; + + if (!check_level(ctx, textarget, level, caller)) return; - } } - framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, - level, 0, GL_FALSE); + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, caller); } void GLAPIENTRY -_mesa_FramebufferTexture2D(GLenum target, GLenum attachment, +_mesa_FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - GET_CURRENT_CONTEXT(ctx); - - if (texture != 0) { - GLboolean error; - - switch (textarget) { - case GL_TEXTURE_2D: - error = GL_FALSE; - break; - case GL_TEXTURE_RECTANGLE: - error = _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: - error = !ctx->Extensions.ARB_texture_cube_map; - break; - case GL_TEXTURE_2D_ARRAY: - error = (_mesa_is_gles(ctx) && ctx->Version < 30) - || !ctx->Extensions.EXT_texture_array; - break; - case GL_TEXTURE_2D_MULTISAMPLE: - case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: - error = _mesa_is_gles(ctx) - || !ctx->Extensions.ARB_texture_multisample; - break; - default: - error = GL_TRUE; - } + framebuffer_texture_with_dims(1, target, attachment, textarget, texture, + level, 0, "glFramebufferTexture1D"); +} - if (error) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture2D(textarget=%s)", - _mesa_lookup_enum_by_nr(textarget)); - return; - } - } - framebuffer_texture(ctx, "2D", target, attachment, textarget, texture, - level, 0, GL_FALSE); +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 zoffset) + 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) && (textarget != GL_TEXTURE_3D)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture3D(textarget)"); + const char *func = "glFramebufferTextureLayer"; + + /* 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; } - framebuffer_texture(ctx, "3D", target, attachment, textarget, texture, - level, zoffset, GL_FALSE); + /* Get the texture object */ + if (!get_texture_for_framebuffer(ctx, texture, false, func, &texObj)) + return; + + if (texObj) { + if (!check_texture_target(ctx, texObj->Target, func)) + return; + + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + + if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + assert(layer >= 0 && layer < 6); + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + layer = 0; + } + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, func); } void GLAPIENTRY -_mesa_FramebufferTextureLayer(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLint layer) +_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; + + 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; - framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, - level, layer, GL_FALSE); + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + + if (!check_level(ctx, texObj->Target, level, func)) + return; + + if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + assert(layer >= 0 && layer < 6); + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + layer = 0; + } + } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, + layer, GL_FALSE, func); } @@ -2733,117 +3372,244 @@ _mesa_FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level) { GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *fb; + struct gl_texture_object *texObj; + GLboolean layered = GL_FALSE; - if (_mesa_has_geometry_shaders(ctx)) { - framebuffer_texture(ctx, "", target, attachment, 0, texture, - level, 0, GL_TRUE); - } else { + 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; + + if (!check_level(ctx, texObj->Target, level, func)) + return; } + + _mesa_framebuffer_texture(ctx, fb, attachment, texObj, 0, level, + 0, layered, func); } void GLAPIENTRY -_mesa_FramebufferRenderbuffer(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; - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbuffer(target)"); + const char *func = "glNamedFramebufferTexture"; + + 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, - "glFramebufferRenderbuffer(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, "glFramebufferRenderbuffer"); + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(window-system framebuffer)", func); return; } att = get_attachment(ctx, fb, attachment); if (att == NULL) { _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbuffer(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, - "glFramebufferRenderbuffer(non-existant" - " renderbuffer %u)", renderbuffer); - return; - } - else if (rb == &DummyRenderbuffer) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferRenderbuffer(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, - "glFramebufferRenderbuffer(renderbuffer" - " is not DEPTH_STENCIL format)"); + "%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); +void GLAPIENTRY +_mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + struct gl_framebuffer *fb; + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); - /* 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); + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } + + 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_GetFramebufferAttachmentParameteriv(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); - /* 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, - "glGetFramebufferAttachmentParameteriv(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: @@ -2859,16 +3625,34 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, !ctx->Extensions.ARB_framebuffer_object) && !_mesa_is_gles3(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameteriv(bound FBO = 0)"); + "%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, - "glGetFramebufferAttachmentParameteriv(attachment)"); + "%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); } @@ -2878,8 +3662,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, } if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameteriv(attachment)"); + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", caller, + _mesa_enum_to_string(attachment)); return; } @@ -2893,9 +3677,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, * attachment, since it does not have a single format." */ _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameteriv(" - "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" - " is invalid for depth+stencil attachment)"); + "%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 */ @@ -2903,8 +3686,7 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, stencilAtt = get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); if (depthAtt->Renderbuffer != stencilAtt->Renderbuffer) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameteriv(DEPTH/STENCIL" - " attachments differ)"); + "%s(DEPTH/STENCIL attachments differ)", caller); return; } } @@ -2913,7 +3695,16 @@ _mesa_GetFramebufferAttachmentParameteriv(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: @@ -2937,8 +3728,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, *params = att->TextureLevel; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; @@ -2954,8 +3745,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, } } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; @@ -2965,8 +3756,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, if (ctx->API == API_OPENGLES) { goto invalid_pname_enum; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _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)) { @@ -2987,8 +3778,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, goto invalid_pname_enum; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { if (ctx->Extensions.EXT_framebuffer_sRGB) { @@ -3010,8 +3801,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, goto invalid_pname_enum; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { mesa_format format = att->Renderbuffer->Format; @@ -3026,9 +3817,9 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, if (_mesa_is_gles3(ctx) && attachment == GL_DEPTH_STENCIL_ATTACHMENT) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameteriv(cannot query " + "%s(cannot query " "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE of " - "GL_DEPTH_STENCIL_ATTACHMENT"); + "GL_DEPTH_STENCIL_ATTACHMENT)", caller); return; } @@ -3062,8 +3853,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, goto invalid_pname_enum; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(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 = @@ -3082,8 +3873,7 @@ _mesa_GetFramebufferAttachmentParameteriv(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; case GL_FRAMEBUFFER_ATTACHMENT_LAYERED: @@ -3092,8 +3882,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, } else if (att->Type == GL_TEXTURE) { *params = att->Layered; } else if (att->Type == GL_NONE) { - _mesa_error(ctx, err, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, err, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); } else { goto invalid_pname_enum; } @@ -3105,30 +3895,144 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, return; invalid_pname_enum: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameteriv(pname)"); + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid pname %s)", caller, + _mesa_enum_to_string(pname)); return; } +void GLAPIENTRY +_mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, + GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *buffer; + + buffer = get_framebuffer_target(ctx, target); + if (!buffer) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameteriv(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } + + _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, + "glGetFramebufferAttachmentParameteriv"); +} + + +void GLAPIENTRY +_mesa_GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, + GLenum attachment, + GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_framebuffer *buffer; + + if (framebuffer) { + buffer = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glGetNamedFramebufferAttachmentParameteriv"); + if (!buffer) + return; + } + else { + /* + * 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_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(GLenum target, GLsizei numAttachments, +invalidate_framebuffer_storage(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height, const char *name) { int i; - struct gl_framebuffer *fb; - GET_CURRENT_CONTEXT(ctx); - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", name); + /* Section 17.4 Whole Framebuffer Operations of the OpenGL 4.5 Core + * Spec (2.2.2015, PDF page 522) says: + * "An INVALID_VALUE error is generated if numAttachments, width, or + * height is negative." + */ + if (numAttachments < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(numAttachments < 0)", name); return; } - if (numAttachments < 0) { + if (width < 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "%s(numAttachments < 0)", name); + "%s(width < 0)", name); + return; + } + + if (height < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(height < 0)", name); return; } @@ -3224,7 +4128,8 @@ invalidate_framebuffer_storage(GLenum target, GLsizei numAttachments, return; invalid_enum: - _mesa_error(ctx, GL_INVALID_ENUM, "%s(attachment)", name); + _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid attachment %s)", name, + _mesa_enum_to_string(attachments[i])); return; } @@ -3234,16 +4139,67 @@ _mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) { - invalidate_framebuffer_storage(target, numAttachments, attachments, + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateSubFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } + + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, x, y, width, height, "glInvalidateSubFramebuffer"); } +void GLAPIENTRY +_mesa_InvalidateNamedFramebufferSubData(GLuint framebuffer, + GLsizei numAttachments, + const GLenum *attachments, + GLint x, GLint y, + GLsizei width, GLsizei height) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + /* The OpenGL 4.5 core spec (02.02.2015) says (in Section 17.4 Whole + * Framebuffer Operations, PDF page 522): "If framebuffer is zero, the + * default draw framebuffer is affected." + */ + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glInvalidateNamedFramebufferSubData"); + if (!fb) + return; + } + else + fb = ctx->WinSysDrawBuffer; + + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + x, y, width, height, + "glInvalidateNamedFramebufferSubData"); +} + + void GLAPIENTRY _mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments) { + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + fb = get_framebuffer_target(ctx, target); + if (!fb) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glInvalidateFramebuffer(invalid target %s)", + _mesa_enum_to_string(target)); + return; + } + /* The GL_ARB_invalidate_subdata spec says: * * "The command @@ -3256,13 +4212,55 @@ _mesa_InvalidateFramebuffer(GLenum target, GLsizei numAttachments, * , equal to 0, 0, , * respectively." */ - invalidate_framebuffer_storage(target, numAttachments, attachments, + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, 0, 0, - MAX_VIEWPORT_WIDTH, MAX_VIEWPORT_HEIGHT, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, "glInvalidateFramebuffer"); } +void GLAPIENTRY +_mesa_InvalidateNamedFramebufferData(GLuint framebuffer, + GLsizei numAttachments, + const GLenum *attachments) +{ + struct gl_framebuffer *fb; + GET_CURRENT_CONTEXT(ctx); + + /* The OpenGL 4.5 core spec (02.02.2015) says (in Section 17.4 Whole + * Framebuffer Operations, PDF page 522): "If framebuffer is zero, the + * default draw framebuffer is affected." + */ + if (framebuffer) { + fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, + "glInvalidateNamedFramebufferData"); + if (!fb) + return; + } + else + fb = ctx->WinSysDrawBuffer; + + /* The GL_ARB_invalidate_subdata spec says: + * + * "The command + * + * void InvalidateFramebuffer(enum target, + * sizei numAttachments, + * const enum *attachments); + * + * is equivalent to the command InvalidateSubFramebuffer with , , + * , equal to 0, 0, , + * respectively." + */ + invalidate_framebuffer_storage(ctx, fb, numAttachments, attachments, + 0, 0, + ctx->Const.MaxViewportWidth, + ctx->Const.MaxViewportHeight, + "glInvalidateNamedFramebufferData"); +} + + void GLAPIENTRY _mesa_DiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) @@ -3276,7 +4274,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; } @@ -3313,5 +4311,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])); }