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