/*
* Mesa 3-D graphics library
- * Version: 6.3
+ * Version: 6.5.1
*
- * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
+ * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*/
+#include "buffers.h"
#include "context.h"
#include "fbobject.h"
#include "framebuffer.h"
#include "hash.h"
+#include "mipmap.h"
#include "renderbuffer.h"
+#include "state.h"
#include "teximage.h"
+#include "texobj.h"
#include "texstore.h"
/**
* Helper routine for getting a gl_renderbuffer.
*/
-static struct gl_renderbuffer *
-lookup_renderbuffer(GLcontext *ctx, GLuint id)
+struct gl_renderbuffer *
+_mesa_lookup_renderbuffer(GLcontext *ctx, GLuint id)
{
struct gl_renderbuffer *rb;
/**
* Helper routine for getting a gl_framebuffer.
*/
-static struct gl_framebuffer *
-lookup_framebuffer(GLcontext *ctx, GLuint id)
+struct gl_framebuffer *
+_mesa_lookup_framebuffer(GLcontext *ctx, GLuint id)
{
struct gl_framebuffer *fb;
* Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
* gl_renderbuffer_attachment object.
*/
-static struct gl_renderbuffer_attachment *
-get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, GLenum attachment)
+struct gl_renderbuffer_attachment *
+_mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
+ GLenum attachment)
{
GLuint i;
{
if (att->Type == GL_TEXTURE) {
ASSERT(att->Texture);
- if (att->Renderbuffer) {
- /* delete/remove the 'wrapper' renderbuffer */
- /* XXX do we really want to do this??? */
- att->Renderbuffer->Delete(att->Renderbuffer);
- att->Renderbuffer = NULL;
- }
att->Texture->RefCount--;
if (att->Texture->RefCount == 0) {
ctx->Driver.DeleteTexture(ctx, att->Texture);
}
+ else {
+ /* tell driver that we're done rendering to this texture. */
+ if (ctx->Driver.FinishRenderTexture) {
+ ctx->Driver.FinishRenderTexture(ctx, att);
+ }
+ }
att->Texture = NULL;
}
- else if (att->Type == GL_RENDERBUFFER_EXT) {
+ if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) {
ASSERT(att->Renderbuffer);
ASSERT(!att->Texture);
- att->Renderbuffer->RefCount--;
- if (att->Renderbuffer->RefCount == 0) {
- att->Renderbuffer->Delete(att->Renderbuffer);
- }
- att->Renderbuffer = NULL;
+ _mesa_reference_renderbuffer(&att->Renderbuffer, NULL);
}
att->Type = GL_NONE;
att->Complete = GL_TRUE;
*/
void
_mesa_set_texture_attachment(GLcontext *ctx,
+ struct gl_framebuffer *fb,
struct gl_renderbuffer_attachment *att,
struct gl_texture_object *texObj,
GLenum texTarget, GLuint level, GLuint zoffset)
{
- _mesa_remove_attachment(ctx, att);
- att->Type = GL_TEXTURE;
- att->Texture = texObj;
+ if (att->Texture == texObj) {
+ /* re-attaching same texture */
+ ASSERT(att->Type == GL_TEXTURE);
+ }
+ else {
+ /* new attachment */
+ _mesa_remove_attachment(ctx, att);
+ att->Type = GL_TEXTURE;
+ att->Texture = texObj;
+ texObj->RefCount++;
+ }
+
+ /* always update these fields */
att->TextureLevel = level;
if (IS_CUBE_FACE(texTarget)) {
att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
att->Zoffset = zoffset;
att->Complete = GL_FALSE;
- texObj->RefCount++;
-
- /* XXX when we attach to a texture, we should probably set the
- * att->Renderbuffer pointer to a "wrapper renderbuffer" which
- * makes the texture image look like renderbuffer.
- */
+ if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
+ ctx->Driver.RenderTexture(ctx, fb, att);
+ }
}
struct gl_renderbuffer_attachment *att,
struct gl_renderbuffer *rb)
{
+ /* XXX check if re-doing same attachment, exit early */
_mesa_remove_attachment(ctx, att);
att->Type = GL_RENDERBUFFER_EXT;
- att->Renderbuffer = rb;
att->Texture = NULL; /* just to be safe */
att->Complete = GL_FALSE;
- rb->RefCount++;
+ _mesa_reference_renderbuffer(&att->Renderbuffer, rb);
}
/**
* Fallback for ctx->Driver.FramebufferRenderbuffer()
- * Sets a framebuffer attachment to a particular renderbuffer.
- * The framebuffer in question is ctx->DrawBuffer.
- * \sa _mesa_renderbuffer_texture
+ * Attach a renderbuffer object to a framebuffer object.
*/
void
-_mesa_framebuffer_renderbuffer(GLcontext *ctx,
- struct gl_renderbuffer_attachment *att,
- struct gl_renderbuffer *rb)
+_mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
+ GLenum attachment, struct gl_renderbuffer *rb)
{
+ struct gl_renderbuffer_attachment *att;
+
+ _glthread_LOCK_MUTEX(fb->Mutex);
+
+ att = _mesa_get_attachment(ctx, fb, attachment);
+ ASSERT(att);
if (rb) {
_mesa_set_renderbuffer_attachment(ctx, att, rb);
}
else {
_mesa_remove_attachment(ctx, att);
}
+
+ _glthread_UNLOCK_MUTEX(fb->Mutex);
}
}
}
else if (format == GL_DEPTH) {
- if (texImage->TexFormat->BaseFormat != GL_DEPTH_COMPONENT) {
+ if (texImage->TexFormat->BaseFormat == GL_DEPTH_COMPONENT) {
+ /* OK */
+ }
+ else if (ctx->Extensions.EXT_packed_depth_stencil &&
+ texImage->TexFormat->BaseFormat == GL_DEPTH_STENCIL_EXT) {
+ /* OK */
+ }
+ else {
att->Complete = GL_FALSE;
return;
}
}
}
else if (att->Type == GL_RENDERBUFFER_EXT) {
- if (att->Renderbuffer->Width < 1 || att->Renderbuffer->Height < 1) {
+ ASSERT(att->Renderbuffer);
+ if (!att->Renderbuffer->InternalFormat ||
+ att->Renderbuffer->Width < 1 ||
+ att->Renderbuffer->Height < 1) {
att->Complete = GL_FALSE;
return;
}
if (format == GL_COLOR) {
if (att->Renderbuffer->_BaseFormat != GL_RGB &&
att->Renderbuffer->_BaseFormat != GL_RGBA) {
+ ASSERT(att->Renderbuffer->RedBits);
+ ASSERT(att->Renderbuffer->GreenBits);
+ ASSERT(att->Renderbuffer->BlueBits);
att->Complete = GL_FALSE;
return;
}
}
else if (format == GL_DEPTH) {
- if (att->Renderbuffer->_BaseFormat != GL_DEPTH_COMPONENT) {
+ ASSERT(att->Renderbuffer->DepthBits);
+ if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
+ /* OK */
+ }
+ else if (ctx->Extensions.EXT_packed_depth_stencil &&
+ att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
+ /* OK */
+ }
+ else {
att->Complete = GL_FALSE;
return;
}
}
else {
assert(format == GL_STENCIL);
- if (att->Renderbuffer->_BaseFormat != GL_STENCIL_INDEX) {
+ ASSERT(att->Renderbuffer->StencilBits);
+ if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
+ /* OK */
+ }
+ else if (ctx->Extensions.EXT_packed_depth_stencil &&
+ att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
+ /* OK */
+ }
+ else {
att->Complete = GL_FALSE;
return;
}
}
+/**
+ * Helpful for debugging
+ */
+static void
+fbo_incomplete(const char *msg, int index)
+{
+ (void) msg;
+ (void) index;
+ /*
+ _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index);
+ */
+}
+
+
/**
* Test if the given framebuffer object is complete and update its
* Status field with the results.
GLenum intFormat = GL_NONE;
GLuint w = 0, h = 0;
GLint i;
+ GLuint j;
assert(fb->Name != 0);
test_attachment_completeness(ctx, GL_DEPTH, att);
if (!att->Complete) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
+ fbo_incomplete("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);
return;
}
}
test_attachment_completeness(ctx, GL_COLOR, att);
if (!att->Complete) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
+ fbo_incomplete("color attachment incomplete", i);
return;
}
}
if (att->Type == GL_TEXTURE) {
- w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width;
- h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height;
- f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format;
+ const struct gl_texture_image *texImg
+ = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
+ w = texImg->Width;
+ h = texImg->Height;
+ f = texImg->_BaseFormat;
numImages++;
- if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) {
+ if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT
+ && f != GL_DEPTH_STENCIL_EXT) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
+ fbo_incomplete("texture attachment incomplete", -1);
return;
}
}
/* check that width, height, format are same */
if (w != width || h != height) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
+ fbo_incomplete("width or height mismatch", -1);
return;
}
if (intFormat != GL_NONE && f != intFormat) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
+ fbo_incomplete("format mismatch", -1);
return;
}
}
}
/* Check that all DrawBuffers are present */
- for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
- if (fb->ColorDrawBuffer[i] != GL_NONE) {
+ for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) {
+ if (fb->ColorDrawBuffer[j] != GL_NONE) {
const struct gl_renderbuffer_attachment *att
- = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
+ = _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;
}
}
/* Check that the ReadBuffer is present */
if (fb->ColorReadBuffer != GL_NONE) {
const struct gl_renderbuffer_attachment *att
- = get_attachment(ctx, fb, fb->ColorReadBuffer);
+ = _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;
}
}
- /* Check if any renderbuffer is attached more than once */
- for (i = 0; i < BUFFER_COUNT - 1; i++) {
- struct gl_renderbuffer *rb_i = fb->Attachment[i].Renderbuffer;
- if (rb_i) {
- GLint j;
- for (j = i + 1; j < BUFFER_COUNT; j++) {
- struct gl_renderbuffer *rb_j = fb->Attachment[j].Renderbuffer;
- if (rb_i == rb_j) {
- fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT;
- return;
- }
- }
- }
- }
-
-
if (numImages == 0) {
fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT;
+ fbo_incomplete("no attachments", -1);
return;
}
{
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
- if (renderbuffer && lookup_renderbuffer(ctx, renderbuffer))
- return GL_TRUE;
- else
- return GL_FALSE;
+ if (renderbuffer) {
+ struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
+ if (rb != NULL && rb != &DummyRenderbuffer)
+ return GL_TRUE;
+ }
+ return GL_FALSE;
}
void GLAPIENTRY
_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
{
- struct gl_renderbuffer *newRb, *oldRb;
+ struct gl_renderbuffer *newRb;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
if (target != GL_RENDERBUFFER_EXT) {
_mesa_error(ctx, GL_INVALID_ENUM,
return;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ /* The above doesn't fully flush the drivers in the way that a
+ * glFlush does, but that is required here:
+ */
+ if (ctx->Driver.Flush)
+ ctx->Driver.Flush(ctx);
+
+
if (renderbuffer) {
- newRb = lookup_renderbuffer(ctx, renderbuffer);
+ newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
if (newRb == &DummyRenderbuffer) {
/* ID was reserved, but no real renderbuffer object made yet */
newRb = NULL;
}
ASSERT(newRb->AllocStorage);
_mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb);
+ newRb->RefCount = 1; /* referenced by hash table */
}
- newRb->RefCount++;
}
else {
newRb = NULL;
}
- oldRb = ctx->CurrentRenderbuffer;
- if (oldRb) {
- oldRb->RefCount--;
- if (oldRb->RefCount == 0) {
- oldRb->Delete(oldRb);
- }
- }
-
ASSERT(newRb != &DummyRenderbuffer);
- ctx->CurrentRenderbuffer = newRb;
+ _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb);
}
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
for (i = 0; i < n; i++) {
if (renderbuffers[i] > 0) {
struct gl_renderbuffer *rb;
- rb = lookup_renderbuffer(ctx, renderbuffers[i]);
+ rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]);
if (rb) {
- /* remove from hash table immediately, to free the ID */
+ /* check if deleting currently bound renderbuffer object */
+ if (rb == ctx->CurrentRenderbuffer) {
+ /* bind default */
+ ASSERT(rb->RefCount >= 2);
+ _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+ }
+
+ /* 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]);
if (rb != &DummyRenderbuffer) {
- /* But the object will not be freed until it's no longer
- * bound in any context.
- */
- rb->RefCount--;
- if (rb->RefCount == 0) {
- rb->Delete(rb);
- }
+ /* no longer referenced by hash table */
+ _mesa_reference_renderbuffer(&rb, NULL);
}
}
}
/**
* Given an internal format token for a render buffer, return the
* corresponding base format.
+ * This is very similar to _mesa_base_tex_format() but the set of valid
+ * internal formats is somewhat different.
+ *
* \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
- * or zero if error.
+ * GL_DEPTH_STENCIL_EXT or zero if error.
*/
-static GLenum
-base_internal_format(GLcontext *ctx, GLenum internalFormat)
+GLenum
+_mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
{
switch (internalFormat) {
case GL_RGB:
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;
/* XXX add floating point formats eventually */
default:
return 0;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
if (target != GL_RENDERBUFFER_EXT) {
_mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
return;
}
- baseFormat = base_internal_format(ctx, internalFormat);
+ baseFormat = _mesa_base_fbo_format(ctx, internalFormat);
if (baseFormat == 0) {
_mesa_error(ctx, GL_INVALID_ENUM,
"glRenderbufferStorageEXT(internalFormat)");
return;
}
- if (width < 1 || width > ctx->Const.MaxRenderbufferSize) {
+ if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) {
_mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(width)");
return;
}
- if (height < 1 || height > ctx->Const.MaxRenderbufferSize) {
+ if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) {
_mesa_error(ctx, GL_INVALID_VALUE, "glRenderbufferStorageEXT(height)");
return;
}
return;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+ if (rb->InternalFormat == internalFormat &&
+ rb->Width == (GLuint) width &&
+ rb->Height == (GLuint) height) {
+ /* no change in allocation needed */
+ return;
+ }
+
+ /* These MUST get set by the AllocStorage func */
+ rb->_ActualFormat = 0;
+ rb->RedBits =
+ rb->GreenBits =
+ rb->BlueBits =
+ rb->AlphaBits =
+ rb->IndexBits =
+ rb->DepthBits =
+ rb->StencilBits = 0;
+
/* Now allocate the storage */
ASSERT(rb->AllocStorage);
if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
/* No error - check/set fields now */
- assert(rb->Width == width);
- assert(rb->Height == height);
- assert(rb->InternalFormat);
+ assert(rb->_ActualFormat);
+ assert(rb->Width == (GLuint) width);
+ assert(rb->Height == (GLuint) height);
+ assert(rb->RedBits || rb->GreenBits || rb->BlueBits || rb->AlphaBits ||
+ rb->DepthBits || rb->StencilBits || rb->IndexBits);
+ rb->InternalFormat = internalFormat;
rb->_BaseFormat = baseFormat;
}
else {
rb->Width = 0;
rb->Height = 0;
rb->InternalFormat = GL_NONE;
+ rb->_ActualFormat = GL_NONE;
rb->_BaseFormat = GL_NONE;
+ rb->RedBits =
+ rb->GreenBits =
+ rb->BlueBits =
+ rb->AlphaBits =
+ rb->IndexBits =
+ rb->DepthBits =
+ rb->StencilBits = 0;
}
/*
return;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
switch (pname) {
case GL_RENDERBUFFER_WIDTH_EXT:
*params = ctx->CurrentRenderbuffer->Width;
*params = ctx->CurrentRenderbuffer->InternalFormat;
return;
case GL_RENDERBUFFER_RED_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
- ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->RedBits;
break;
case GL_RENDERBUFFER_GREEN_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
- ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[1];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->GreenBits;
break;
case GL_RENDERBUFFER_BLUE_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
- ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[2];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->BlueBits;
break;
case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
- ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[3];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->AlphaBits;
break;
case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->DepthBits;
break;
case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
- if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
- *params = ctx->CurrentRenderbuffer->ComponentSizes[0];
- }
- else {
- *params = 0;
- }
+ *params = ctx->CurrentRenderbuffer->StencilBits;
break;
-
default:
_mesa_error(ctx, GL_INVALID_ENUM,
"glGetRenderbufferParameterivEXT(target)");
{
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
- if (framebuffer && lookup_framebuffer(ctx, framebuffer))
- return GL_TRUE;
- else
- return GL_FALSE;
+ if (framebuffer) {
+ struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer);
+ if (rb != NULL && rb != &DummyFramebuffer)
+ return GL_TRUE;
+ }
+ return GL_FALSE;
+}
+
+
+static void
+check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+{
+ GLuint i;
+ ASSERT(ctx->Driver.RenderTexture);
+ for (i = 0; i < BUFFER_COUNT; i++) {
+ struct gl_renderbuffer_attachment *att = fb->Attachment + i;
+ struct gl_texture_object *texObj = att->Texture;
+ if (texObj
+ && att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
+ ctx->Driver.RenderTexture(ctx, fb, att);
+ }
+ }
+}
+
+
+/**
+ * Examine all the framebuffer's attachments to see if any are textures.
+ * If so, call ctx->Driver.FinishRenderTexture() for each texture to
+ * notify the device driver that the texture image may have changed.
+ */
+static void
+check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+{
+ 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);
+ }
+ }
+ }
}
void GLAPIENTRY
_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
{
- struct gl_framebuffer *newFb, *newReadFb, *oldFb;
+ struct gl_framebuffer *newFb, *newFbread;
+ GLboolean bindReadBuf, bindDrawBuf;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
- if (target != GL_FRAMEBUFFER_EXT) {
+ 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;
+ break;
+ default:
_mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
return;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ if (ctx->Driver.Flush) {
+ ctx->Driver.Flush(ctx);
+ }
if (framebuffer) {
/* Binding a user-created framebuffer object */
- newFb = lookup_framebuffer(ctx, framebuffer);
+ newFb = _mesa_lookup_framebuffer(ctx, framebuffer);
if (newFb == &DummyFramebuffer) {
/* ID was reserved, but no real framebuffer object made yet */
newFb = NULL;
}
_mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
}
- newFb->RefCount++;
- newReadFb = newFb;
+ newFbread = newFb;
}
else {
/* Binding the window system framebuffer (which was originally set
* with MakeCurrent).
*/
newFb = ctx->WinSysDrawBuffer;
- newReadFb = ctx->WinSysReadBuffer;
+ newFbread = ctx->WinSysReadBuffer;
}
- oldFb = ctx->DrawBuffer;
- if (oldFb) { /* AND oldFb->Name != 0 */
- oldFb->RefCount--;
- if (oldFb->RefCount == 0) {
- oldFb->Delete(oldFb);
+ ASSERT(newFb);
+ ASSERT(newFb != &DummyFramebuffer);
+
+ /*
+ * XXX check if re-binding same buffer and skip some of this code.
+ */
+
+ /* for window-framebuffers, re-initialize the fbo values, as they
+ could be wrong (makecurrent with a new drawable while still a fbo
+ was bound will lead to default init fbo values).
+ note that therefore the context ReadBuffer/DrawBuffer values are not
+ valid while fbo's are bound!!! */
+ if (bindReadBuf) {
+ _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread);
+ if (!newFbread->Name) {
+ _mesa_readbuffer_update_fields(ctx, ctx->Pixel.ReadBuffer);
+ }
+ }
+
+ if (bindDrawBuf) {
+ /* check if old FB had any texture attachments */
+ check_end_texture_render(ctx, ctx->DrawBuffer);
+ /* check if time to delete this framebuffer */
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, newFb);
+ if (!newFb->Name) {
+ GLuint i;
+ GLenum buffers[MAX_DRAW_BUFFERS];
+ for(i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
+ buffers[i] = ctx->Color.DrawBuffer[i];
+ }
+ _mesa_drawbuffers(ctx, ctx->Const.MaxDrawBuffers, buffers, NULL);
+ }
+ else {
+ /* check if newly bound framebuffer has any texture attachments */
+ check_begin_texture_render(ctx, newFb);
}
}
- ASSERT(newFb != &DummyFramebuffer);
-
- /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
- ctx->DrawBuffer = newFb;
- ctx->ReadBuffer = newReadFb;
+ if (ctx->Driver.BindFramebuffer) {
+ ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread);
+ }
}
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ /* The above doesn't fully flush the drivers in the way that a
+ * glFlush does, but that is required here:
+ */
+ if (ctx->Driver.Flush)
+ ctx->Driver.Flush(ctx);
for (i = 0; i < n; i++) {
if (framebuffers[i] > 0) {
struct gl_framebuffer *fb;
- fb = lookup_framebuffer(ctx, framebuffers[i]);
+ fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]);
if (fb) {
ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]);
+
+ /* check if deleting currently bound framebuffer object */
+ if (fb == ctx->DrawBuffer) {
+ /* bind default */
+ ASSERT(fb->RefCount >= 2);
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ }
+
/* remove from hash table immediately, to free the ID */
_mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]);
/* But the object will not be freed until it's no longer
* bound in any context.
*/
- fb->RefCount--;
- if (fb->RefCount == 0) {
- fb->Delete(fb);
- }
+ _mesa_unreference_framebuffer(&fb);
}
}
}
GLenum GLAPIENTRY
_mesa_CheckFramebufferStatusEXT(GLenum target)
{
+ struct gl_framebuffer *buffer;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
- if (target != GL_FRAMEBUFFER_EXT) {
+ switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+ case GL_DRAW_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
+ return 0;
+ }
+ buffer = ctx->DrawBuffer;
+ break;
+ case GL_READ_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
+ return 0;
+ }
+ buffer = ctx->ReadBuffer;
+ break;
+#endif
+ case GL_FRAMEBUFFER_EXT:
+ buffer = ctx->DrawBuffer;
+ break;
+ default:
_mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
}
- if (ctx->DrawBuffer->Name == 0) {
+ if (buffer->Name == 0) {
/* The window system / default framebuffer is always complete */
return GL_FRAMEBUFFER_COMPLETE_EXT;
}
- _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
- return ctx->DrawBuffer->_Status;
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+ _mesa_test_framebuffer_completeness(ctx, buffer);
+ return buffer->_Status;
}
/**
- * Do error checking common to glFramebufferTexture1D/2D/3DEXT.
- * \return GL_TRUE if any error, GL_FALSE otherwise
+ * Common code called by glFramebufferTexture1D/2D/3DEXT().
*/
-static GLboolean
-error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
- GLenum target, GLenum attachment,
- GLenum textarget, GLuint texture, GLint level)
+static void
+framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target,
+ GLenum attachment, GLenum textarget, GLuint texture,
+ GLint level, GLint zoffset)
{
- ASSERT(dims >= 1 && dims <= 3);
+ struct gl_renderbuffer_attachment *att;
+ struct gl_texture_object *texObj = NULL;
+ struct gl_framebuffer *fb;
+
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
if (target != GL_FRAMEBUFFER_EXT) {
_mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture%dDEXT(target)", dims);
- return GL_TRUE;
+ "glFramebufferTexture%sEXT(target)", caller);
+ return;
}
+ fb = ctx->DrawBuffer;
+ ASSERT(fb);
+
/* check framebuffer binding */
- if (ctx->DrawBuffer->Name == 0) {
+ if (fb->Name == 0) {
_mesa_error(ctx, GL_INVALID_OPERATION,
- "glFramebufferTexture%dDEXT", dims);
- return GL_TRUE;
+ "glFramebufferTexture%sEXT", caller);
+ return;
}
- /* only check textarget, level if texture ID is non-zero */
+
+ /* The textarget, level, and zoffset parameters are only validated if
+ * texture is non-zero.
+ */
if (texture) {
- if ((dims == 1 && textarget != GL_TEXTURE_1D) ||
- (dims == 3 && textarget != GL_TEXTURE_3D) ||
- (dims == 2 && textarget != GL_TEXTURE_2D &&
- textarget != GL_TEXTURE_RECTANGLE_ARB &&
- !IS_CUBE_FACE(textarget))) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture%dDEXT(textarget)", dims);
- return GL_TRUE;
+ GLboolean err = GL_TRUE;
+
+ texObj = _mesa_lookup_texture(ctx, texture);
+ if (texObj != NULL) {
+ if (textarget == 0) {
+ err = (texObj->Target != GL_TEXTURE_3D) &&
+ (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) &&
+ (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT);
+ }
+ else {
+ err = (texObj->Target == GL_TEXTURE_CUBE_MAP)
+ ? !IS_CUBE_FACE(textarget)
+ : (texObj->Target != textarget);
+ }
+ }
+
+ if (err) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glFramebufferTexture%sEXT(texture target mismatch)",
+ caller);
+ return;
}
- if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture%dDEXT(level)", dims);
- return 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;
+ }
+ }
+ 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;
+ }
+ }
+
+
+ if ((level < 0) ||
+ (level >= _mesa_max_texture_levels(ctx, texObj->Target))) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glFramebufferTexture%sEXT(level)", caller);
+ return;
}
}
- return GL_FALSE;
+ att = _mesa_get_attachment(ctx, fb, attachment);
+ if (att == NULL) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glFramebufferTexture%sEXT(attachment)", caller);
+ return;
+ }
+
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ /* The above doesn't fully flush the drivers in the way that a
+ * glFlush does, but that is required here:
+ */
+ if (ctx->Driver.Flush)
+ ctx->Driver.Flush(ctx);
+
+ _glthread_LOCK_MUTEX(fb->Mutex);
+ if (texObj) {
+ _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
+ level, zoffset);
+ }
+ else {
+ _mesa_remove_attachment(ctx, att);
+ }
+ _glthread_UNLOCK_MUTEX(fb->Mutex);
}
+
void GLAPIENTRY
_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
GLenum textarget, GLuint texture, GLint level)
{
- struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
-
- if (error_check_framebuffer_texture(ctx, 1, target, attachment,
- textarget, texture, level))
- return;
-
- ASSERT(textarget == GL_TEXTURE_1D);
-
- att = get_attachment(ctx, ctx->DrawBuffer, attachment);
- if (att == NULL) {
+ if ((texture != 0) && (textarget != GL_TEXTURE_1D)) {
_mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture1DEXT(attachment)");
+ "glFramebufferTexture1DEXT(textarget)");
return;
}
- if (texture) {
- texObj = (struct gl_texture_object *)
- _mesa_HashLookup(ctx->Shared->TexObjects, texture);
- if (!texObj) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture1DEXT(texture)");
- return;
- }
- if (texObj->Target != textarget) {
- _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
- "glFramebufferTexture1DEXT(texture target)");
- return;
- }
- }
- else {
- /* remove texture attachment */
- texObj = NULL;
- }
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
+ framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
+ level, 0);
}
_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
GLenum textarget, GLuint texture, GLint level)
{
- struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
-
- if (error_check_framebuffer_texture(ctx, 2, target, attachment,
- textarget, texture, level))
- return;
-
- ASSERT(textarget == GL_TEXTURE_2D ||
- textarget == GL_TEXTURE_RECTANGLE_ARB ||
- IS_CUBE_FACE(textarget));
-
- att = get_attachment(ctx, ctx->DrawBuffer, attachment);
- if (att == NULL) {
- _mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture2DEXT(attachment)");
+ if ((texture != 0) &&
+ (textarget != GL_TEXTURE_2D) &&
+ (textarget != GL_TEXTURE_RECTANGLE_ARB) &&
+ (!IS_CUBE_FACE(textarget))) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glFramebufferTexture2DEXT(textarget)");
return;
}
- if (texture) {
- texObj = (struct gl_texture_object *)
- _mesa_HashLookup(ctx->Shared->TexObjects, texture);
- if (!texObj) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture2DEXT(texture)");
- return;
- }
- if ((texObj->Target == GL_TEXTURE_2D && textarget != GL_TEXTURE_2D) ||
- (texObj->Target == GL_TEXTURE_RECTANGLE_ARB
- && textarget != GL_TEXTURE_RECTANGLE_ARB) ||
- (texObj->Target == GL_TEXTURE_CUBE_MAP
- && !IS_CUBE_FACE(textarget))) {
- _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
- "glFramebufferTexture2DEXT(texture target)");
- return;
- }
- }
- else {
- /* remove texture attachment */
- texObj = NULL;
- }
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
+ framebuffer_texture(ctx, "2D", target, attachment, textarget, texture,
+ level, 0);
}
GLenum textarget, GLuint texture,
GLint level, GLint zoffset)
{
- struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
-
- if (error_check_framebuffer_texture(ctx, 3, target, attachment,
- textarget, texture, level))
- return;
-
- ASSERT(textarget == GL_TEXTURE_3D);
-
- att = get_attachment(ctx, ctx->DrawBuffer, attachment);
- if (att == NULL) {
+ if ((texture != 0) && (textarget != GL_TEXTURE_3D)) {
_mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture1DEXT(attachment)");
+ "glFramebufferTexture3DEXT(textarget)");
return;
}
- if (texture) {
- const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
- texObj = (struct gl_texture_object *)
- _mesa_HashLookup(ctx->Shared->TexObjects, texture);
- if (!texObj) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture3DEXT(texture)");
- return;
- }
- if (texObj->Target != textarget) {
- _mesa_error(ctx, GL_INVALID_OPERATION, /* XXX correct error? */
- "glFramebufferTexture3DEXT(texture target)");
- return;
- }
- if (zoffset < 0 || zoffset >= maxSize) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture3DEXT(zoffset)");
- return;
- }
- }
- else {
- /* remove texture attachment */
- texObj = NULL;
- }
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
- level, zoffset);
+ framebuffer_texture(ctx, "3D", target, attachment, textarget, texture,
+ level, zoffset);
+}
+
+
+void GLAPIENTRY
+_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLint layer)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ framebuffer_texture(ctx, "Layer", target, attachment, 0, texture,
+ level, layer);
}
GLuint renderbuffer)
{
struct gl_renderbuffer_attachment *att;
+ struct gl_framebuffer *fb;
struct gl_renderbuffer *rb;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
- if (target != GL_FRAMEBUFFER_EXT) {
+ switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+ case GL_DRAW_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glFramebufferRenderbufferEXT(target)");
+ return;
+ }
+ fb = ctx->DrawBuffer;
+ break;
+ case GL_READ_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glFramebufferRenderbufferEXT(target)");
+ return;
+ }
+ fb = ctx->ReadBuffer;
+ break;
+#endif
+ case GL_FRAMEBUFFER_EXT:
+ fb = ctx->DrawBuffer;
+ break;
+ default:
_mesa_error(ctx, GL_INVALID_ENUM,
"glFramebufferRenderbufferEXT(target)");
return;
return;
}
- if (ctx->DrawBuffer->Name == 0) {
+ if (fb->Name == 0) {
/* Can't attach new renderbuffers to a window system framebuffer */
_mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
return;
}
- att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+ att = _mesa_get_attachment(ctx, fb, attachment);
if (att == NULL) {
_mesa_error(ctx, GL_INVALID_ENUM,
"glFramebufferRenderbufferEXT(attachment)");
}
if (renderbuffer) {
- rb = lookup_renderbuffer(ctx, renderbuffer);
+ rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
if (!rb) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glFramebufferRenderbufferEXT(renderbuffer)");
rb = NULL;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ /* The above doesn't fully flush the drivers in the way that a
+ * glFlush does, but that is required here:
+ */
+ if (ctx->Driver.Flush)
+ ctx->Driver.Flush(ctx);
+
assert(ctx->Driver.FramebufferRenderbuffer);
- ctx->Driver.FramebufferRenderbuffer(ctx, att, rb);
+ ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb);
- _mesa_update_framebuffer_visual(ctx->DrawBuffer);
+ /* Some subsequent GL commands may depend on the framebuffer's visual
+ * after the binding is updated. Update visual info now.
+ */
+ _mesa_update_framebuffer_visual(fb);
}
GLenum pname, GLint *params)
{
const struct gl_renderbuffer_attachment *att;
+ struct gl_framebuffer *buffer;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- if (target != GL_FRAMEBUFFER_EXT) {
+ switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+ case GL_DRAW_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glGetFramebufferAttachmentParameterivEXT(target)");
+ return;
+ }
+ buffer = ctx->DrawBuffer;
+ break;
+ case GL_READ_FRAMEBUFFER_EXT:
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glGetFramebufferAttachmentParameterivEXT(target)");
+ return;
+ }
+ buffer = ctx->ReadBuffer;
+ break;
+#endif
+ case GL_FRAMEBUFFER_EXT:
+ buffer = ctx->DrawBuffer;
+ break;
+ default:
_mesa_error(ctx, GL_INVALID_ENUM,
"glGetFramebufferAttachmentParameterivEXT(target)");
return;
}
- if (ctx->DrawBuffer->Name == 0) {
+ if (buffer->Name == 0) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glGetFramebufferAttachmentParameterivEXT");
return;
}
- att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+ att = _mesa_get_attachment(ctx, buffer, attachment);
if (att == NULL) {
_mesa_error(ctx, GL_INVALID_ENUM,
"glGetFramebufferAttachmentParameterivEXT(attachment)");
return;
}
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ /* The above doesn't fully flush the drivers in the way that a
+ * glFlush does, but that is required here:
+ */
+ if (ctx->Driver.Flush)
+ ctx->Driver.Flush(ctx);
+
switch (pname) {
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
*params = att->Type;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
switch (target) {
case GL_TEXTURE_1D:
texObj = _mesa_select_tex_object(ctx, texUnit, target);
/* XXX this might not handle cube maps correctly */
- _mesa_generate_mipmap(ctx, target, texUnit, texObj);
+ _mesa_lock_texture(ctx, texObj);
+ ctx->Driver.GenerateMipmap(ctx, target, texObj);
+ _mesa_unlock_texture(ctx, texObj);
+}
+
+
+#if FEATURE_EXT_framebuffer_blit
+void GLAPIENTRY
+_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+ FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+ if (ctx->NewState) {
+ _mesa_update_state(ctx);
+ }
+
+ if (!ctx->ReadBuffer) {
+ /* XXX */
+ }
+
+ /* check for complete framebuffers */
+ if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
+ ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+ "glBlitFramebufferEXT(incomplete draw/read buffers)");
+ return;
+ }
+
+ if (filter != GL_NEAREST && filter != GL_LINEAR) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)");
+ return;
+ }
+
+ if (mask & ~(GL_COLOR_BUFFER_BIT |
+ GL_DEPTH_BUFFER_BIT |
+ GL_STENCIL_BUFFER_BIT)) {
+ _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)");
+ return;
+ }
+
+ /* 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");
+ return;
+ }
+
+ if (mask & GL_STENCIL_BUFFER_BIT) {
+ struct gl_renderbuffer *readRb = ctx->ReadBuffer->_StencilBuffer;
+ struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_StencilBuffer;
+ if (readRb->StencilBits != drawRb->StencilBits) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBlitFramebufferEXT(stencil buffer size mismatch");
+ return;
+ }
+ }
+
+ if (mask & GL_DEPTH_BUFFER_BIT) {
+ struct gl_renderbuffer *readRb = ctx->ReadBuffer->_DepthBuffer;
+ struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_DepthBuffer;
+ if (readRb->DepthBits != drawRb->DepthBits) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBlitFramebufferEXT(depth buffer size mismatch");
+ return;
+ }
+ }
+
+ if (!ctx->Extensions.EXT_framebuffer_blit) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT");
+ return;
+ }
+
+ ASSERT(ctx->Driver.BlitFramebuffer);
+ ctx->Driver.BlitFramebuffer(ctx,
+ srcX0, srcY0, srcX1, srcY1,
+ dstX0, dstY0, dstX1, dstY1,
+ mask, filter);
}
+#endif /* FEATURE_EXT_framebuffer_blit */