Rework the GL_READ_BUFFER, GL_DRAW_BUFFER state repairs that Roland previously did.
[mesa.git] / src / mesa / main / fbobject.c
index f7e870b49cd9335d3974f98931be4ffd4190aff4..5cd18d02872a52ffadf09768d2b98ff916c08970 100644 (file)
@@ -29,6 +29,7 @@
  */
 
 
+#include "buffers.h"
 #include "context.h"
 #include "fbobject.h"
 #include "framebuffer.h"
@@ -150,26 +151,18 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
 {
    if (att->Type == GL_TEXTURE) {
       ASSERT(att->Texture);
-      att->Texture->RefCount--;
-      if (att->Texture->RefCount == 0) {
-        ctx->Driver.DeleteTexture(ctx, att->Texture);
+      if (ctx->Driver.FinishRenderTexture) {
+         /* tell driver we're done rendering to this texobj */
+         ctx->Driver.FinishRenderTexture(ctx, att);
       }
-      else {
-         /* tell driver that we're done rendering to this texture. */
-         if (ctx->Driver.FinishRenderTexture) {
-            ctx->Driver.FinishRenderTexture(ctx, att);
-         }
-      }
-      att->Texture = NULL;
+      _mesa_reference_texobj(&att->Texture, NULL); /* unbind */
+      ASSERT(!att->Texture);
    }
    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); /* unbind */
+      ASSERT(!att->Renderbuffer);
    }
    att->Type = GL_NONE;
    att->Complete = GL_TRUE;
@@ -195,8 +188,8 @@ _mesa_set_texture_attachment(GLcontext *ctx,
       /* new attachment */
       _mesa_remove_attachment(ctx, att);
       att->Type = GL_TEXTURE;
-      att->Texture = texObj;
-      texObj->RefCount++;
+      assert(!att->Texture);
+      _mesa_reference_texobj(&att->Texture, texObj);
    }
 
    /* always update these fields */
@@ -228,10 +221,9 @@ _mesa_set_renderbuffer_attachment(GLcontext *ctx,
    /* 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);
 }
 
 
@@ -246,12 +238,9 @@ _mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
    struct gl_renderbuffer_attachment *att;
 
    _glthread_LOCK_MUTEX(fb->Mutex);
-   if (rb)
-      _glthread_LOCK_MUTEX(rb->Mutex);
 
    att = _mesa_get_attachment(ctx, fb, attachment);
    ASSERT(att);
-
    if (rb) {
       _mesa_set_renderbuffer_attachment(ctx, att, rb);
    }
@@ -259,8 +248,6 @@ _mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
       _mesa_remove_attachment(ctx, att);
    }
 
-   if (rb)
-      _glthread_UNLOCK_MUTEX(rb->Mutex);
    _glthread_UNLOCK_MUTEX(fb->Mutex);
 }
 
@@ -559,7 +546,7 @@ _mesa_IsRenderbufferEXT(GLuint renderbuffer)
 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);
@@ -593,21 +580,16 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
         }
          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) {
-      _mesa_unreference_renderbuffer(&oldRb);
-   }
-
    ASSERT(newRb != &DummyRenderbuffer);
 
-   ctx->CurrentRenderbuffer = newRb;
+   _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb);
 }
 
 
@@ -632,14 +614,15 @@ _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
                _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.
-                */
-               _mesa_unreference_renderbuffer(&rb);
+               /* no longer referenced by hash table */
+               _mesa_reference_renderbuffer(&rb, NULL);
            }
         }
       }
@@ -938,7 +921,7 @@ check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
 void GLAPIENTRY
 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
 {
-   struct gl_framebuffer *newFb;
+   struct gl_framebuffer *newFb, *newFbread;
    GLboolean bindReadBuf, bindDrawBuf;
    GET_CURRENT_CONTEXT(ctx);
 
@@ -979,9 +962,11 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
    }
 
    FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (ctx->Driver.Flush) {  
       ctx->Driver.Flush(ctx);
    }
+
    if (framebuffer) {
       /* Binding a user-created framebuffer object */
       newFb = _mesa_lookup_framebuffer(ctx, framebuffer);
@@ -998,12 +983,14 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
         }
          _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
       }
+      newFbread = newFb;
    }
    else {
       /* Binding the window system framebuffer (which was originally set
        * with MakeCurrent).
        */
       newFb = ctx->WinSysDrawBuffer;
+      newFbread = ctx->WinSysReadBuffer;
    }
 
    ASSERT(newFb);
@@ -1013,23 +1000,48 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
     * XXX check if re-binding same buffer and skip some of this code.
     */
 
+#if 000
+   /* 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!!! */
+#endif
    if (bindReadBuf) {
-      _mesa_reference_framebuffer(&ctx->ReadBuffer, newFb);
+      _mesa_reference_framebuffer(&ctx->ReadBuffer, newFbread);
+#if 000
+      if (!newFbread->Name) {
+         _mesa_readbuffer_update_fields(ctx, ctx->Pixel.ReadBuffer);
+      }
+#endif
    }
 
    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 != 0) {
          /* check if newly bound framebuffer has any texture attachments */
          check_begin_texture_render(ctx, newFb);
       }
+      else {
+         /* XXX try to remove this: */
+#if 000
+         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);
+#endif
+      }
    }
 
    if (ctx->Driver.BindFramebuffer) {
-      ctx->Driver.BindFramebuffer(ctx, target, newFb);
+      ctx->Driver.BindFramebuffer(ctx, target, newFb, newFbread);
    }
 }
 
@@ -1158,20 +1170,19 @@ _mesa_CheckFramebufferStatusEXT(GLenum target)
  * Common code called by glFramebufferTexture1D/2D/3DEXT().
  */
 static void
-framebuffer_texture(GLuint dims, GLenum target, GLenum attachment,
-                    GLenum textarget, GLuint texture,
+framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target, 
+                    GLenum attachment, GLenum textarget, GLuint texture,
                     GLint level, GLint zoffset)
 {
    struct gl_renderbuffer_attachment *att;
    struct gl_texture_object *texObj = NULL;
    struct gl_framebuffer *fb;
-   GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    if (target != GL_FRAMEBUFFER_EXT) {
       _mesa_error(ctx, GL_INVALID_ENUM,
-                  "glFramebufferTexture%dDEXT(target)", dims);
+                  "glFramebufferTexture%sEXT(target)", caller);
       return;
    }
 
@@ -1181,83 +1192,68 @@ framebuffer_texture(GLuint dims, GLenum target, GLenum attachment,
    /* check framebuffer binding */
    if (fb->Name == 0) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glFramebufferTexture%dDEXT", dims);
+                  "glFramebufferTexture%sEXT", caller);
       return;
    }
 
+
+   /* The textarget, level, and zoffset parameters are only validated if
+    * texture is non-zero.
+    */
    if (texture) {
-      texObj = _mesa_lookup_texture(ctx, texture);
-   }
+      GLboolean err = GL_TRUE;
 
-   /* Check dimension-dependent things */
-   switch (dims) {
-   case 1:
-      if (textarget != GL_TEXTURE_1D) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glFramebufferTexture1DEXT(textarget)");
-         return;
-      }
-      if (texObj && texObj->Target != GL_TEXTURE_1D) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glFramebufferTexture1DEXT(texture target mismatch)");
-         return;
-      }
-      break;
-   case 2:
-      if (textarget != GL_TEXTURE_2D &&
-          textarget != GL_TEXTURE_RECTANGLE_ARB &&
-          !IS_CUBE_FACE(textarget)) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glFramebufferTexture2DEXT(textarget)");
-         return;
-      }
-      if (texObj) {
-         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,
-                        "glFramebufferTexture1DEXT(texture target mismatch)");
-            return;
+      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);
          }
       }
-      break;
-   case 3:
-      if (textarget != GL_TEXTURE_3D) {
-         _mesa_error(ctx, GL_INVALID_ENUM,
-                     "glFramebufferTexture3DEXT(textarget)");
-         return;
-      }
-      if (texObj && texObj->Target != GL_TEXTURE_3D) {
+
+      if (err) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glFramebufferTexture3DEXT(texture target mismatch)");
+                     "glFramebufferTexture%sEXT(texture target mismatch)",
+                     caller);
          return;
       }
-      {
+
+      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,
-                        "glFramebufferTexture3DEXT(zoffset)");
+                        "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;
          }
       }
-      break;
-   default:
-      _mesa_problem(ctx, "Unexpected dims in error_check_framebuffer_texture");
-      return;
-   }
 
-   if ((level < 0) || level >= _mesa_max_texture_levels(ctx, textarget)) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glFramebufferTexture%dDEXT(level)", dims);
-      return;
+
+      if ((level < 0) || 
+          (level >= _mesa_max_texture_levels(ctx, texObj->Target))) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glFramebufferTexture%sEXT(level)", caller);
+         return;
+      }
    }
 
    att = _mesa_get_attachment(ctx, fb, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
-                 "glFramebufferTexture%dDEXT(attachment)", dims);
+                  "glFramebufferTexture%sEXT(attachment)", caller);
       return;
    }
 
@@ -1285,9 +1281,16 @@ void GLAPIENTRY
 _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
                               GLenum textarget, GLuint texture, GLint level)
 {
-   const GLint zoffset = 0;
-   framebuffer_texture(1, target, attachment, textarget, texture,
-                       level, zoffset);
+   GET_CURRENT_CONTEXT(ctx);
+
+   if ((texture != 0) && (textarget != GL_TEXTURE_1D)) {
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glFramebufferTexture1DEXT(textarget)");
+      return;
+   }
+
+   framebuffer_texture(ctx, "1D", target, attachment, textarget, texture,
+                       level, 0);
 }
 
 
@@ -1295,9 +1298,19 @@ void GLAPIENTRY
 _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
                               GLenum textarget, GLuint texture, GLint level)
 {
-   const GLint zoffset = 0;
-   framebuffer_texture(2, target, attachment, textarget, texture,
-                       level, zoffset);
+   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;
+   }
+
+   framebuffer_texture(ctx, "2D", target, attachment, textarget, texture,
+                       level, 0);
 }
 
 
@@ -1306,11 +1319,29 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
                               GLenum textarget, GLuint texture,
                               GLint level, GLint zoffset)
 {
-   framebuffer_texture(3, target, attachment, textarget, texture,
+   GET_CURRENT_CONTEXT(ctx);
+
+   if ((texture != 0) && (textarget != GL_TEXTURE_3D)) {
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glFramebufferTexture3DEXT(textarget)");
+      return;
+   }
+
+   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);
+}
+
 
 void GLAPIENTRY
 _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,