/*
* 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.
*/
#include "glformats.h"
#include "hash.h"
#include "macros.h"
-#include "mfeatures.h"
+#include "multisample.h"
#include "mtypes.h"
#include "renderbuffer.h"
#include "state.h"
_mesa_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);
- }
_mesa_reference_texobj(&att->Texture, NULL); /* unbind */
ASSERT(!att->Texture);
}
att->Complete = GL_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];
+ if (!texImage)
+ return;
+
+ 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;
+ }
+
+ rb->_BaseFormat = texImage->_BaseFormat;
+ rb->Format = texImage->TexFormat;
+ rb->InternalFormat = texImage->InternalFormat;
+ rb->Width = texImage->Width2;
+ rb->Height = texImage->Height2;
+ rb->NumSamples = texImage->NumSamples;
+ rb->TexImage = texImage;
+
+ ctx->Driver.RenderTexture(ctx, fb, att);
+}
/**
* Bind a texture object to an attachment point.
struct gl_framebuffer *fb,
struct gl_renderbuffer_attachment *att,
struct gl_texture_object *texObj,
- GLenum texTarget, GLuint level, GLuint zoffset)
+ GLenum texTarget, GLuint level, GLuint zoffset,
+ 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);
}
else {
/* new attachment */
- if (ctx->Driver.FinishRenderTexture && att->Texture)
- ctx->Driver.FinishRenderTexture(ctx, att);
_mesa_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->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);
}
* 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_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);
}
GLint fixedSampleLocations = -1;
GLint i;
GLuint j;
+ bool layer_count_valid = false;
+ GLuint layer_count = 0, att_layer_count;
assert(_mesa_is_user_fbo(fb));
numImages = 0;
fb->Width = 0;
fb->Height = 0;
+ fb->_AllColorBuffersFixedPoint = GL_TRUE;
+ fb->_HasSNormOrFloatColorBuffer = GL_FALSE;
/* Start at -2 to more easily loop over all attachment points.
* -2: depth buffer
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;
}
}
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;
}
}
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;
minWidth = MIN2(minWidth, texImg->Width);
maxWidth = MAX2(maxWidth, texImg->Width);
minHeight = MIN2(minHeight, texImg->Height);
if (!is_format_color_renderable(ctx, attFormat, texImg->InternalFormat) &&
!is_legal_depth_format(ctx, f)) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
- fbo_incomplete("texture attachment incomplete", -1);
+ fbo_incomplete(ctx, "texture attachment incomplete", -1);
return;
}
numSamples = texImg->NumSamples;
else if (numSamples != texImg->NumSamples) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
- fbo_incomplete("inconsistent sample count", -1);
+ fbo_incomplete(ctx, "inconsistent sample count", -1);
return;
}
fixedSampleLocations = texImg->FixedSampleLocations;
else if (fixedSampleLocations != texImg->FixedSampleLocations) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
- fbo_incomplete("inconsistent fixed sample locations", -1);
+ fbo_incomplete(ctx, "inconsistent fixed sample locations", -1);
return;
}
}
numSamples = att->Renderbuffer->NumSamples;
else if (numSamples != att->Renderbuffer->NumSamples) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
- fbo_incomplete("inconsistent sample count", -1);
+ fbo_incomplete(ctx, "inconsistent sample count", -1);
return;
}
fixedSampleLocations = GL_TRUE;
else if (fixedSampleLocations != GL_TRUE) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
- fbo_incomplete("inconsistent fixed sample locations", -1);
+ fbo_incomplete(ctx, "inconsistent fixed sample locations", -1);
return;
}
}
/* check if integer color */
fb->_IntegerColor = _mesa_is_format_integer_color(attFormat);
+ /* 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 */
/* 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->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. */
+ att_layer_count = att->Layered ? att->Renderbuffer->Depth : 0;
+ if (!layer_count_valid) {
+ layer_count = att_layer_count;
+ layer_count_valid = true;
+ } else if (layer_count != att_layer_count) {
+ if (layer_count == 0 || att_layer_count == 0) {
+ fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS;
+ fbo_incomplete(ctx, "framebuffer attachment layer mode is inconsistent", i);
+ } else {
+ fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB;
+ fbo_incomplete(ctx, "framebuffer attachment layer count is inconsistent", i);
+ }
return;
}
}
+ fb->Layered = layer_count > 0;
+
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++) {
assert(att);
if (att->Type == GL_NONE) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
- fbo_incomplete("missing drawbuffer", j);
+ fbo_incomplete(ctx, "missing drawbuffer", j);
return;
}
}
assert(att);
if (att->Type == GL_NONE) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
- fbo_incomplete("missing readbuffer", -1);
+ fbo_incomplete(ctx, "missing readbuffer", -1);
return;
}
}
if (numImages == 0) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
- fbo_incomplete("no attachments", -1);
+ fbo_incomplete(ctx, "no attachments", -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);
}
}
}
+void GLAPIENTRY
+_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
+{
+ _mesa_BindRenderbuffer(target, renderbuffer);
+}
+
+
/**
* If the given renderbuffer is anywhere attached to the framebuffer, detach
* the renderbuffer.
"glRenderbufferStorage" : "glRenderbufferStorageMultisample";
struct gl_renderbuffer *rb;
GLenum baseFormat;
+ GLenum sample_count_error;
GET_CURRENT_CONTEXT(ctx);
if (MESA_VERBOSE & VERBOSE_API) {
/* 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;
+ else {
+ /* check the sample count;
+ * note: driver may choose to use more samples than what's requested
+ */
+ sample_count_error = _mesa_check_sample_count(ctx, target,
+ internalFormat, samples);
+ if (sample_count_error != GL_NO_ERROR) {
+ _mesa_error(ctx, sample_count_error, "%s(samples)", func);
+ return;
+ }
}
rb = ctx->CurrentRenderbuffer;
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) {
ctx->Driver.RenderTexture(ctx, fb, att);
}
}
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);
}
}
}
#ifdef DEBUG
if (ctx->Extensions.ARB_framebuffer_object) {
- ASSERT(ctx->Extensions.EXT_framebuffer_object);
ASSERT(ctx->Extensions.EXT_framebuffer_blit);
}
#endif
- if (!ctx->Extensions.EXT_framebuffer_object) {
- _mesa_error(ctx, GL_INVALID_OPERATION,
- "glBindFramebufferEXT(unsupported)");
- return;
- }
-
switch (target) {
case GL_DRAW_FRAMEBUFFER_EXT:
if (!ctx->Extensions.EXT_framebuffer_blit) {
}
}
+void GLAPIENTRY
+_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
+{
+ _mesa_BindFramebuffer(target, framebuffer);
+}
+
+
void GLAPIENTRY
_mesa_DeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
}
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 */
static void
framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target,
GLenum attachment, GLenum textarget, GLuint texture,
- GLint level, GLint zoffset)
+ GLint level, GLint zoffset, GLboolean layered)
{
struct gl_renderbuffer_attachment *att;
struct gl_texture_object *texObj = NULL;
BUFFER_DEPTH);
} else {
_mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
- level, zoffset);
+ level, zoffset, 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
}
framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
- level, 0);
+ level, 0, GL_FALSE);
}
}
framebuffer_texture(ctx, "2D", target, attachment, textarget, texture,
- level, 0);
+ level, 0, GL_FALSE);
}
}
framebuffer_texture(ctx, "3D", target, attachment, textarget, texture,
- level, zoffset);
+ level, zoffset, GL_FALSE);
}
GET_CURRENT_CONTEXT(ctx);
framebuffer_texture(ctx, "Layer", target, attachment, 0, texture,
- level, layer);
+ level, layer, GL_FALSE);
+}
+
+
+void GLAPIENTRY
+_mesa_FramebufferTexture(GLenum target, GLenum attachment,
+ GLuint texture, GLint level)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (ctx->Version >= 32 || ctx->Extensions.ARB_geometry_shader4) {
+ framebuffer_texture(ctx, "Layer", target, attachment, 0, texture,
+ level, 0, GL_TRUE);
+ } else {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "unsupported function (glFramebufferTexture) called");
+ }
}
GET_CURRENT_CONTEXT(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ FLUSH_VERTICES(ctx, 0);
switch (target) {
case GL_TEXTURE_1D:
return GL_FALSE;
}
+static GLboolean
+is_valid_blit_filter(const struct gl_context *ctx, GLenum filter)
+{
+ switch (filter) {
+ case GL_NEAREST:
+ case GL_LINEAR:
+ return true;
+ case GL_SCALED_RESOLVE_FASTEST_EXT:
+ case GL_SCALED_RESOLVE_NICEST_EXT:
+ return ctx->Extensions.EXT_framebuffer_multisample_blit_scaled;
+ default:
+ return false;
+ }
+}
/**
* Blit rectangular region, optionally from one framebuffer to another.
const struct gl_framebuffer *readFb, *drawFb;
GET_CURRENT_CONTEXT(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ FLUSH_VERTICES(ctx, 0);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx,
return;
}
- if (filter != GL_NEAREST && filter != GL_LINEAR) {
- _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)");
+ if (!is_valid_blit_filter(ctx, filter)) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(%s)",
+ _mesa_lookup_enum_by_nr(filter));
+ return;
+ }
+
+ if ((filter == GL_SCALED_RESOLVE_FASTEST_EXT ||
+ filter == GL_SCALED_RESOLVE_NICEST_EXT) &&
+ (readFb->Visual.samples == 0 || drawFb->Visual.samples > 0)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT(%s)",
+ _mesa_lookup_enum_by_nr(filter));
return;
}
}
}
}
- if (filter == GL_LINEAR) {
- /* 3.1 spec, page 199:
+ if (filter != GL_NEAREST) {
+ /* From EXT_framebuffer_multisample_blit_scaled specification:
* "Calling BlitFramebuffer will result in an INVALID_OPERATION error
- * if filter is LINEAR and read buffer contains integer data."
+ * if filter is not NEAREST and read buffer contains integer data."
*/
GLenum type = _mesa_get_format_datatype(colorReadRb->Format);
if (type == GL_INT || type == GL_UNSIGNED_INT) {
}
/* extra checks for multisample copies... */
- if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) {
+ if ((readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) &&
+ (filter == GL_NEAREST || filter == GL_LINEAR)) {
/* src and dest region sizes must be the same */
if (abs(srcX1 - srcX0) != abs(dstX1 - dstX0) ||
abs(srcY1 - srcY0) != abs(dstY1 - dstY0)) {