/*
* Mesa 3-D graphics library
- * Version: 6.5
+ * Version: 6.5.1
*
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
*
*/
+#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;
else {
/* tell driver that we're done rendering to this texture. */
if (ctx->Driver.FinishRenderTexture) {
- ctx->Driver.FinishRenderTexture(ctx, att->Texture,
- att->CubeMapFace,
- att->TextureLevel);
+ ctx->Driver.FinishRenderTexture(ctx, att);
}
}
att->Texture = NULL;
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)
}
att->Zoffset = zoffset;
att->Complete = GL_FALSE;
+
+ 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);
}
{
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);
}
/* OK */
}
else if (ctx->Extensions.EXT_packed_depth_stencil &&
- att->Renderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
+ texImage->TexFormat->BaseFormat == GL_DEPTH_STENCIL_EXT) {
/* OK */
}
else {
}
}
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) {
+ ASSERT(att->Renderbuffer->DepthBits);
if (att->Renderbuffer->_BaseFormat == GL_DEPTH_COMPONENT) {
/* OK */
}
}
else {
assert(format == GL_STENCIL);
+ ASSERT(att->Renderbuffer->StencilBits);
if (att->Renderbuffer->_BaseFormat == GL_STENCIL_INDEX) {
/* OK */
}
}
+/**
+ * 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;
}
}
h = texImg->Height;
f = texImg->_BaseFormat;
numImages++;
- if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) {
- /* XXX need GL_DEPTH_STENCIL_EXT test? */
+ 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
- = _mesa_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;
}
}
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.
- * Note that there's one exception: a GL_DEPTH_STENCIL renderbuffer can be
- * bound to both the stencil and depth attachment points at the same time.
- */
- 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 && rb_i->_BaseFormat != GL_DEPTH_STENCIL_EXT) {
- 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) {
- struct gl_renderbuffer *rb = 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)
{
- struct gl_renderbuffer *newRb, *oldRb;
+ struct gl_renderbuffer *newRb;
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);
+
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);
}
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) {
/* check if deleting currently bound renderbuffer object */
if (rb == ctx->CurrentRenderbuffer) {
_mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
}
- /* 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]);
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
* 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:
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;
}
FLUSH_VERTICES(ctx, _NEW_BUFFERS);
if (rb->InternalFormat == internalFormat &&
- rb->Width == width &&
- rb->Height == height) {
+ 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 =
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
if (framebuffer) {
- struct gl_framebuffer *rb = lookup_framebuffer(ctx, framebuffer);
+ struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer);
if (rb != NULL && rb != &DummyFramebuffer)
return GL_TRUE;
}
}
+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_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+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;
- struct gl_texture_object *texObj = att->Texture;
- if (texObj) {
- ctx->Driver.FinishRenderTexture(ctx, texObj, att->CubeMapFace,
- att->TextureLevel);
+ if (att->Texture && att->Renderbuffer) {
+ ctx->Driver.FinishRenderTexture(ctx, att);
}
}
}
void GLAPIENTRY
_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
{
- struct gl_framebuffer *newFb, *oldFb;
+ struct gl_framebuffer *newFb, *newFbread;
GLboolean bindReadBuf, bindDrawBuf;
GET_CURRENT_CONTEXT(ctx);
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:
}
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);
}
- if (bindReadBuf)
- newFb->RefCount++;
- if (bindDrawBuf)
- newFb->RefCount++;
+ newFbread = newFb;
}
else {
/* Binding the window system framebuffer (which was originally set
* with MakeCurrent).
*/
newFb = ctx->WinSysDrawBuffer;
+ newFbread = ctx->WinSysReadBuffer;
}
+ 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) {
- oldFb = ctx->ReadBuffer;
- if (oldFb && oldFb->Name != 0) {
- oldFb->RefCount--;
- if (oldFb->RefCount == 0) {
- oldFb->Delete(oldFb);
- }
+ _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread);
+ if (!newFbread->Name) {
+ _mesa_readbuffer_update_fields(ctx, ctx->Pixel.ReadBuffer);
}
- ctx->ReadBuffer = newFb;
}
if (bindDrawBuf) {
- oldFb = ctx->DrawBuffer;
- if (oldFb && oldFb->Name != 0) {
- /* check if old FB had any texture attachments */
- if (ctx->Driver.FinishRenderTexture) {
- check_texture_render(ctx, oldFb);
- }
- /* check if time to delete this framebuffer */
- oldFb->RefCount--;
- if (oldFb->RefCount == 0) {
- oldFb->Delete(oldFb);
+ /* 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);
}
- ctx->DrawBuffer = newFb;
+ }
+
+ if (ctx->Driver.BindFramebuffer) {
+ ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread);
}
}
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]);
/* 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);
}
}
}
/**
- * 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 ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
- _mesa_error(ctx, GL_INVALID_VALUE,
- "glFramebufferTexture%dDEXT(level)", dims);
- return GL_TRUE;
+ if (err) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glFramebufferTexture%sEXT(texture target mismatch)",
+ caller);
+ return;
}
- }
-
- return GL_FALSE;
-}
-
-
-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);
+ 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 (error_check_framebuffer_texture(ctx, 1, target, attachment,
- textarget, texture, level))
- return;
- ASSERT(textarget == GL_TEXTURE_1D);
+ if ((level < 0) ||
+ (level >= _mesa_max_texture_levels(ctx, texObj->Target))) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glFramebufferTexture%sEXT(level)", caller);
+ return;
+ }
+ }
- /* XXX read blit */
- att = _mesa_get_attachment(ctx, ctx->DrawBuffer, attachment);
+ att = _mesa_get_attachment(ctx, fb, attachment);
if (att == NULL) {
_mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture1DEXT(attachment)");
+ "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);
- 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;
- }
+ _glthread_LOCK_MUTEX(fb->Mutex);
+ if (texObj) {
+ _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget,
+ level, zoffset);
}
else {
- /* remove texture attachment */
- texObj = NULL;
+ _mesa_remove_attachment(ctx, att);
}
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
+ _glthread_UNLOCK_MUTEX(fb->Mutex);
}
+
void GLAPIENTRY
-_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
+_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);
-
- 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 = _mesa_get_attachment(ctx, ctx->DrawBuffer, attachment);
- if (att == NULL) {
+ if ((texture != 0) && (textarget != GL_TEXTURE_1D)) {
_mesa_error(ctx, GL_INVALID_ENUM,
- "glFramebufferTexture2DEXT(attachment)");
+ "glFramebufferTexture1DEXT(textarget)");
return;
}
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
+ level, 0);
+}
- 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;
+
+void GLAPIENTRY
+_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
+ GLenum textarget, GLuint texture, GLint level)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ 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;
}
- 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);
-
- if (error_check_framebuffer_texture(ctx, 3, target, attachment,
- textarget, texture, level))
- return;
-
- ASSERT(textarget == GL_TEXTURE_3D);
-
- att = _mesa_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;
}
- FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+ framebuffer_texture(ctx, "3D", target, attachment, textarget, texture,
+ level, zoffset);
+}
- 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);
+
+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);
}
}
if (renderbuffer) {
- rb = lookup_renderbuffer(ctx, renderbuffer);
+ rb = _mesa_lookup_renderbuffer(ctx, renderbuffer);
if (!rb) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glFramebufferRenderbufferEXT(renderbuffer)");
}
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, fb, attachment, rb);
}
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:
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);
}