X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ffbobject.c;h=88449380229a284f124191e80a5247217fef2089;hb=b87a2cbec41e7fbf5514f0e6347eaa3ee1490f59;hp=6b6ebb78f75355a06ed3f2332e89814ee929daa0;hpb=f8a938814e8cdbf65153c277f257df8e22fae9c5;p=mesa.git diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index 6b6ebb78f75..88449380229 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -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,9 +311,21 @@ _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: @@ -389,7 +409,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 +451,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. @@ -530,13 +551,13 @@ _mesa_FramebufferRenderbuffer_sw(struct gl_context *ctx, 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 +567,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); } @@ -811,7 +832,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)) { @@ -868,8 +889,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 || @@ -958,6 +978,7 @@ _mesa_test_framebuffer_completeness(struct gl_context *ctx, 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 @@ -1078,13 +1099,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); @@ -1205,7 +1227,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; @@ -1218,7 +1240,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; @@ -1253,44 +1275,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. - */ - if (numImages != 0) { - fb->Width = minWidth; - fb->Height = minHeight; - } - - /* finally, update the visual info for the framebuffer */ - _mesa_update_framebuffer_visual(ctx, fb); + /* + * 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); } 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; @@ -1301,10 +1321,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; } @@ -1338,7 +1355,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 { @@ -1371,6 +1391,11 @@ _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) @@ -1393,7 +1418,7 @@ framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb, * 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)) { + if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader) { _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname); break; } @@ -1415,6 +1440,9 @@ framebuffer_parameteri(struct gl_context *ctx, struct gl_framebuffer *fb, _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname); } + + invalidate_framebuffer(fb); + ctx->NewState |= _NEW_BUFFERS; } void GLAPIENTRY @@ -1447,10 +1475,47 @@ _mesa_FramebufferParameteri(GLenum target, GLenum pname, GLint param) 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; @@ -1463,7 +1528,7 @@ get_framebuffer_parameteriv(struct gl_context *ctx, struct gl_framebuffer *fb, * 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)) { + if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader) { _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", func, pname); break; } @@ -1475,6 +1540,24 @@ get_framebuffer_parameteriv(struct gl_context *ctx, struct gl_framebuffer *fb, 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); @@ -1501,13 +1584,6 @@ _mesa_GetFramebufferParameteriv(GLenum target, GLenum pname, GLint *params) 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"); } @@ -1629,14 +1705,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++) { @@ -1644,19 +1717,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); @@ -1664,13 +1753,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, @@ -2006,6 +2111,63 @@ invalidate_rb(GLuint key, void *data, void *userData) /** sentinal value, see below */ #define NO_SAMPLES 1000 +void +_mesa_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei samples) +{ + const GLenum baseFormat = _mesa_base_fbo_format(ctx, internalFormat); + + assert(baseFormat != 0); + assert(width >= 0 && width <= (GLsizei) ctx->Const.MaxRenderbufferSize); + assert(height >= 0 && height <= (GLsizei) ctx->Const.MaxRenderbufferSize); + assert(samples != NO_SAMPLES); + if (samples != 0) { + assert(samples > 0); + assert(_mesa_check_sample_count(ctx, GL_RENDERBUFFER, + internalFormat, samples) == GL_NO_ERROR); + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (rb->InternalFormat == internalFormat && + rb->Width == (GLuint) width && + rb->Height == (GLuint) height && + rb->NumSamples == samples) { + /* no change in allocation needed */ + return; + } + + /* These MUST get set by the AllocStorage func */ + rb->Format = MESA_FORMAT_NONE; + rb->NumSamples = samples; + + /* Now allocate the storage */ + assert(rb->AllocStorage); + if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { + /* No error - check/set fields now */ + /* If rb->Format == MESA_FORMAT_NONE, the format is unsupported. */ + assert(rb->Width == (GLuint) width); + assert(rb->Height == (GLuint) height); + rb->InternalFormat = internalFormat; + rb->_BaseFormat = baseFormat; + assert(rb->_BaseFormat != 0); + } + else { + /* Probably ran out of memory - clear the fields */ + rb->Width = 0; + rb->Height = 0; + rb->Format = MESA_FORMAT_NONE; + rb->InternalFormat = GL_NONE; + rb->_BaseFormat = GL_NONE; + rb->NumSamples = 0; + } + + /* Invalidate the framebuffers the renderbuffer is attached in. */ + if (rb->AttachedAnytime) { + _mesa_HashWalk(ctx->Shared->FrameBuffers, invalidate_rb, rb); + } +} /** * Helper function used by renderbuffer_storage_direct() and @@ -2060,50 +2222,12 @@ renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, } if (sample_count_error != GL_NO_ERROR) { - _mesa_error(ctx, sample_count_error, "%s(samples)", func); + _mesa_error(ctx, sample_count_error, "%s(samples=%d)", func, samples); return; } } - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - if (rb->InternalFormat == internalFormat && - rb->Width == (GLuint) width && - rb->Height == (GLuint) height && - rb->NumSamples == samples) { - /* no change in allocation needed */ - return; - } - - /* These MUST get set by the AllocStorage func */ - rb->Format = MESA_FORMAT_NONE; - rb->NumSamples = samples; - - /* Now allocate the storage */ - assert(rb->AllocStorage); - if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { - /* No error - check/set fields now */ - /* If rb->Format == MESA_FORMAT_NONE, the format is unsupported. */ - assert(rb->Width == (GLuint) width); - assert(rb->Height == (GLuint) height); - rb->InternalFormat = internalFormat; - rb->_BaseFormat = baseFormat; - assert(rb->_BaseFormat != 0); - } - else { - /* Probably ran out of memory - clear the fields */ - rb->Width = 0; - rb->Height = 0; - rb->Format = MESA_FORMAT_NONE; - rb->InternalFormat = GL_NONE; - rb->_BaseFormat = GL_NONE; - rb->NumSamples = 0; - } - - /* Invalidate the framebuffers the renderbuffer is attached in. */ - if (rb->AttachedAnytime) { - _mesa_HashWalk(ctx->Shared->FrameBuffers, invalidate_rb, rb); - } + _mesa_renderbuffer_storage(ctx, rb, internalFormat, width, height, samples); } /** @@ -2451,7 +2575,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); @@ -2505,18 +2628,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. @@ -2553,7 +2681,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); } } @@ -2650,6 +2783,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++) { @@ -2659,6 +2794,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; } @@ -2666,10 +2802,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); } @@ -2794,7 +2930,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); } @@ -2808,9 +2956,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. */ @@ -2936,54 +3084,49 @@ 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) && - !_mesa_is_gles31(ctx); - break; - default: - err = true; - } + case GL_TEXTURE_1D_ARRAY: + err = dims != 1 || !ctx->Extensions.EXT_texture_array; break; - case 3: - if (textarget != GL_TEXTURE_3D) - err = true; + case GL_TEXTURE_2D: + err = dims != 2; break; - default: + case GL_TEXTURE_2D_ARRAY: + err = dims != 2 || !ctx->Extensions.EXT_texture_array || + (_mesa_is_gles(ctx) && ctx->Version < 30); + break; + 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) { @@ -3081,30 +3224,46 @@ 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_enum_to_string(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); @@ -3170,6 +3329,28 @@ _mesa_framebuffer_texture(struct gl_context *ctx, struct gl_framebuffer *fb, } +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); +} + + static void framebuffer_texture_with_dims(int dims, GLenum target, GLenum attachment, GLenum textarget, @@ -3189,7 +3370,7 @@ framebuffer_texture_with_dims(int dims, GLenum target, } /* 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) { @@ -3198,13 +3379,28 @@ framebuffer_texture_with_dims(int dims, GLenum target, if ((dims == 3) && !check_layer(ctx, texObj->Target, layer, caller)) return; + + if (!check_level(ctx, 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); } @@ -3217,6 +3413,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) @@ -3226,6 +3432,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, @@ -3236,91 +3452,140 @@ _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; - GLenum textarget = 0; + 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_enum_to_string(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)) + /* 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_layer(ctx, texObj->Target, layer, func)) + att = _mesa_get_and_validate_attachment(ctx, fb, attachment, func); + if (!att) return; + } - if (!check_level(ctx, texObj->Target, level, func)) - return; + 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; + } + + if (!no_error) { + if (!check_layered) { + if (!check_texture_target(ctx, texObj->Target, func)) + return; - if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + if (!check_layer(ctx, texObj->Target, layer, func)) + return; + } + + if (!check_level(ctx, texObj->Target, level, func)) + 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; } } - _mesa_framebuffer_texture(ctx, fb, attachment, texObj, textarget, level, - layer, GL_FALSE, func); + _mesa_framebuffer_texture(ctx, fb, attachment, att, texObj, textarget, + level, layer, layered); } - void GLAPIENTRY -_mesa_NamedFramebufferTextureLayer(GLuint framebuffer, GLenum attachment, - GLuint texture, GLint level, GLint layer) +_mesa_FramebufferTextureLayer_no_error(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; + frame_buffer_texture(0, target, attachment, texture, level, layer, + "glFramebufferTextureLayer", false, true, false); +} - const char *func = "glNamedFramebufferTextureLayer"; - /* Get the framebuffer object */ - fb = _mesa_lookup_framebuffer_err(ctx, framebuffer, func); - if (!fb) - return; +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); +} - /* 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; +void GLAPIENTRY +_mesa_NamedFramebufferTextureLayer_no_error(GLuint framebuffer, + GLenum attachment, + GLuint texture, GLint level, + GLint layer) +{ + frame_buffer_texture(framebuffer, 0, attachment, texture, level, layer, + "glNamedFramebufferTextureLayer", true, true, false); +} - if (!check_layer(ctx, texObj->Target, layer, func)) - return; - if (!check_level(ctx, texObj->Target, level, func)) - 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); +} - 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_FramebufferTexture_no_error(GLenum target, GLenum attachment, + GLuint texture, GLint level) +{ + frame_buffer_texture(0, target, attachment, texture, level, 0, + "glFramebufferTexture", false, true, true); } @@ -3328,42 +3593,16 @@ void GLAPIENTRY _mesa_FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level) { - GET_CURRENT_CONTEXT(ctx); - struct gl_framebuffer *fb; - struct gl_texture_object *texObj; - GLboolean layered = GL_FALSE; - - const char *func = "FramebufferTexture"; - - if (!_mesa_has_geometry_shaders(ctx)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "unsupported function (glFramebufferTexture) called"); - return; - } - - /* Get the framebuffer object */ - fb = get_framebuffer_target(ctx, target); - if (!fb) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture(invalid target %s)", - _mesa_enum_to_string(target)); - return; - } - - /* Get the texture object */ - if (!get_texture_for_framebuffer(ctx, texture, true, func, &texObj)) - return; - - if (texObj) { - if (!check_layered_texture_target(ctx, texObj->Target, func, &layered)) - return; - - if (!check_level(ctx, texObj->Target, level, func)) - return; - } + 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); } @@ -3371,39 +3610,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 = GL_FALSE; - - 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); } @@ -3411,10 +3619,30 @@ 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 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; + bool is_color_attachment; if (_mesa_is_winsys_fbo(fb)) { /* Can't attach new renderbuffers to a window system framebuffer */ @@ -3423,11 +3651,29 @@ _mesa_framebuffer_renderbuffer(struct gl_context *ctx, return; } - att = get_attachment(ctx, fb, attachment); + att = get_attachment(ctx, fb, attachment, &is_color_attachment); if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(invalid attachment %s)", func, - _mesa_enum_to_string(attachment)); + /* + * 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; } @@ -3442,18 +3688,9 @@ _mesa_framebuffer_renderbuffer(struct gl_context *ctx, } } - 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); + _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb); } - void GLAPIENTRY _mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, @@ -3489,8 +3726,8 @@ _mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment, rb = NULL; } - _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb, - "glFramebufferRenderbuffer"); + framebuffer_renderbuffer(ctx, fb, attachment, rb, + "glFramebufferRenderbuffer"); } @@ -3526,22 +3763,37 @@ _mesa_NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, rb = NULL; } - _mesa_framebuffer_renderbuffer(ctx, fb, attachment, rb, - "glNamedFramebufferRenderbuffer"); + 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) +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 @@ -3569,17 +3821,49 @@ _mesa_get_framebuffer_attachment_parameter(struct gl_context *ctx, _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_enum_to_string(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; } @@ -3598,8 +3882,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); @@ -3611,8 +3895,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) { @@ -3685,8 +3980,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_enum_to_string(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) { @@ -3759,10 +4059,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_enum_to_string(pname)); - } else if (att->Texture) { const struct gl_texture_image *texImage = _mesa_select_tex_image(att->Texture, att->Texture->Target, @@ -3780,7 +4076,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: @@ -3823,8 +4121,8 @@ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, return; } - _mesa_get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, - params, + get_framebuffer_attachment_parameter(ctx, buffer, attachment, pname, + params, "glGetFramebufferAttachmentParameteriv"); } @@ -3853,8 +4151,8 @@ _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"); } @@ -4121,7 +4419,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"); } @@ -4161,7 +4460,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"); }