mesa: Implement mutex/locking around texture object reference counting.
authorBrian Paul <brian.paul@tungstengraphics.com>
Fri, 4 Jul 2008 16:29:15 +0000 (10:29 -0600)
committerBrian Paul <brian.paul@tungstengraphics.com>
Fri, 4 Jul 2008 16:29:15 +0000 (10:29 -0600)
Use new _mesa_reference_texobj() function for referencing/unreferencing
textures.  Add new assertions/tests to try to detect invalid usage of
deleted textures.

cherry-picked from master (9e01b915f1243a3f551cb795b7124bd1e52ca15f)

src/mesa/main/attrib.c
src/mesa/main/context.c
src/mesa/main/fbobject.c
src/mesa/main/framebuffer.c
src/mesa/main/mtypes.h
src/mesa/main/texobj.c
src/mesa/main/texobj.h
src/mesa/main/texstate.c

index 2e6bb76586e31b33693b2bfeb520709c98eec513..b990369a9e816f926c8bcbde58a1d301f9e70360 100644 (file)
@@ -361,6 +361,7 @@ _mesa_PushAttrib(GLbitfield mask)
         ctx->Texture.Unit[u].Current1DArray->RefCount++;
         ctx->Texture.Unit[u].Current2DArray->RefCount++;
       }
+
       attr = MALLOC_STRUCT( gl_texture_attrib );
       MEMCPY( attr, &ctx->Texture, sizeof(struct gl_texture_attrib) );
       /* copy state of the currently bound texture objects */
index 33f6d2c0d0cf68fc7dd20396ad0f599a3cb656bd..279880cf4019a5d75fbf9e59489ae8f134aff8d0 100644 (file)
@@ -500,19 +500,12 @@ alloc_shared_state( GLcontext *ctx )
    if (!ss->Default2DArray)
       goto cleanup;
 
-   /* Effectively bind the default textures to all texture units */
-   ss->Default1D->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->Default2D->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->Default3D->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->DefaultCubeMap->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->DefaultRect->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->Default1DArray->RefCount += MAX_TEXTURE_IMAGE_UNITS;
-   ss->Default2DArray->RefCount += MAX_TEXTURE_IMAGE_UNITS;
+   /* sanity check */
+   assert(ss->Default1D->RefCount == 1);
 
    _glthread_INIT_MUTEX(ss->TexMutex);
    ss->TextureStateStamp = 0;
 
-
 #if FEATURE_EXT_framebuffer_object
    ss->FrameBuffers = _mesa_NewHashTable();
    if (!ss->FrameBuffers)
@@ -522,10 +515,9 @@ alloc_shared_state( GLcontext *ctx )
       goto cleanup;
 #endif
 
-
    return GL_TRUE;
 
- cleanup:
+cleanup:
    /* Ran out of memory at some point.  Free everything and return NULL */
    if (ss->DisplayList)
       _mesa_DeleteHashTable(ss->DisplayList);
index 56a3131016fccad24be5a499d086368b78ce78ec..0ae69bdce7aa7569845e670c5c7327bf5fe02df9 100644 (file)
@@ -172,17 +172,12 @@ _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);
-      }
-      else {
+      if (ctx->Driver.FinishRenderTexture) {
          /* tell driver that we're done rendering to this texture. */
-         if (ctx->Driver.FinishRenderTexture) {
-            ctx->Driver.FinishRenderTexture(ctx, att);
-         }
+         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->Texture);
@@ -213,8 +208,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 */
index 96f1b30c9b2c7204cbbecfce1b78619d10836d85..dab449fc09a674b22198a8485479ed939cb9cae4 100644 (file)
@@ -38,6 +38,7 @@
 #include "fbobject.h"
 #include "framebuffer.h"
 #include "renderbuffer.h"
+#include "texobj.h"
 
 
 
@@ -192,17 +193,11 @@ _mesa_free_framebuffer_data(struct gl_framebuffer *fb)
          _mesa_reference_renderbuffer(&att->Renderbuffer, NULL);
       }
       if (att->Texture) {
-         /* render to texture */
-         att->Texture->RefCount--;
-         if (att->Texture->RefCount == 0) {
-            GET_CURRENT_CONTEXT(ctx);
-            if (ctx) {
-               ctx->Driver.DeleteTexture(ctx, att->Texture);
-            }
-         }
+         _mesa_reference_texobj(&att->Texture, NULL);
       }
+      ASSERT(!att->Renderbuffer);
+      ASSERT(!att->Texture);
       att->Type = GL_NONE;
-      att->Texture = NULL;
    }
 
    /* unbind _Depth/_StencilBuffer to decr ref counts */
index 00e7d5d395d6ff75e973d4ac0deda7eec077b86f..a38ec02852305b0e1686bed6ec672e551dc42093 100644 (file)
@@ -1404,6 +1404,7 @@ struct gl_texture_image
  */
 struct gl_texture_object
 {
+   _glthread_Mutex Mutex;      /**< for thread safety */
    GLint RefCount;             /**< reference count */
    GLuint Name;                        /**< the user-visible texture object ID */
    GLenum Target;               /**< GL_TEXTURE_1D, GL_TEXTURE_2D, etc. */
index 606e62d7a08bf0103f03be2e846d6d3209b4f2c9..b77a00dd15c4e3d649783a52aaeac3b219fa8ba5 100644 (file)
@@ -5,9 +5,9 @@
 
 /*
  * Mesa 3-D graphics library
- * Version:  6.5
+ * Version:  7.1
  *
- * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ * Copyright (C) 1999-2007  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"),
@@ -108,6 +108,7 @@ _mesa_initialize_texture_object( struct gl_texture_object *obj,
 
    _mesa_bzero(obj, sizeof(*obj));
    /* init the non-zero fields */
+   _glthread_INIT_MUTEX(obj->Mutex);
    obj->RefCount = 1;
    obj->Name = name;
    obj->Target = target;
@@ -155,6 +156,11 @@ _mesa_delete_texture_object( GLcontext *ctx, struct gl_texture_object *texObj )
 
    (void) ctx;
 
+   /* Set Target to an invalid value.  With some assertions elsewhere
+    * we can try to detect possible use of deleted textures.
+    */
+   texObj->Target = 0x99;
+
 #if FEATURE_colortable
    _mesa_free_colortable_data(&texObj->Palette);
 #endif
@@ -168,6 +174,9 @@ _mesa_delete_texture_object( GLcontext *ctx, struct gl_texture_object *texObj )
       }
    }
 
+   /* destroy the mutex -- it may have allocated memory (eg on bsd) */
+   _glthread_DESTROY_MUTEX(texObj->Mutex);
+
    /* free this object */
    _mesa_free(texObj);
 }
@@ -186,6 +195,7 @@ void
 _mesa_copy_texture_object( struct gl_texture_object *dest,
                            const struct gl_texture_object *src )
 {
+   dest->Target = src->Target;
    dest->Name = src->Name;
    dest->Priority = src->Priority;
    dest->BorderColor[0] = src->BorderColor[0];
@@ -217,6 +227,94 @@ _mesa_copy_texture_object( struct gl_texture_object *dest,
 }
 
 
+/**
+ * Check if the given texture object is valid by examining its Target field.
+ * For debugging only.
+ */
+static GLboolean
+valid_texture_object(const struct gl_texture_object *tex)
+{
+   switch (tex->Target) {
+   case 0:
+   case GL_TEXTURE_1D:
+   case GL_TEXTURE_2D:
+   case GL_TEXTURE_3D:
+   case GL_TEXTURE_CUBE_MAP_ARB:
+   case GL_TEXTURE_RECTANGLE_NV:
+   case GL_TEXTURE_1D_ARRAY_EXT:
+   case GL_TEXTURE_2D_ARRAY_EXT:
+      return GL_TRUE;
+   case 0x99:
+      _mesa_problem(NULL, "invalid reference to a deleted texture object");
+      return GL_FALSE;
+   default:
+      _mesa_problem(NULL, "invalid texture object Target value");
+      return GL_FALSE;
+   }
+}
+
+
+/**
+ * Reference (or unreference) a texture object.
+ * If '*ptr', decrement *ptr's refcount (and delete if it becomes zero).
+ * If 'tex' is non-null, increment its refcount.
+ */
+void
+_mesa_reference_texobj(struct gl_texture_object **ptr,
+                       struct gl_texture_object *tex)
+{
+   assert(ptr);
+   if (*ptr == tex) {
+      /* no change */
+      return;
+   }
+
+   if (*ptr) {
+      /* Unreference the old texture */
+      GLboolean deleteFlag = GL_FALSE;
+      struct gl_texture_object *oldTex = *ptr;
+
+      assert(valid_texture_object(oldTex));
+
+      _glthread_LOCK_MUTEX(oldTex->Mutex);
+      ASSERT(oldTex->RefCount > 0);
+      oldTex->RefCount--;
+
+      deleteFlag = (oldTex->RefCount == 0);
+      _glthread_UNLOCK_MUTEX(oldTex->Mutex);
+
+      if (deleteFlag) {
+         GET_CURRENT_CONTEXT(ctx);
+         if (ctx)
+            ctx->Driver.DeleteTexture(ctx, oldTex);
+         else
+            _mesa_problem(NULL, "Unable to delete texture, no context");
+      }
+
+      *ptr = NULL;
+   }
+   assert(!*ptr);
+
+   if (tex) {
+      /* reference new texture */
+      assert(valid_texture_object(tex));
+      _glthread_LOCK_MUTEX(tex->Mutex);
+      if (tex->RefCount == 0) {
+         /* this texture's being deleted (look just above) */
+         /* Not sure this can every really happen.  Warn if it does. */
+         _mesa_problem(NULL, "referencing deleted texture object");
+         *ptr = NULL;
+      }
+      else {
+         tex->RefCount++;
+         *ptr = tex;
+      }
+      _glthread_UNLOCK_MUTEX(tex->Mutex);
+   }
+}
+
+
+
 /**
  * Report why a texture object is incomplete.  
  *
@@ -613,8 +711,7 @@ unbind_texobj_from_fbo(GLcontext *ctx, struct gl_texture_object *texObj)
 
 /**
  * Check if the given texture object is bound to any texture image units and
- * unbind it if so.
- * XXX all RefCount accesses should be protected by a mutex.
+ * unbind it if so (revert to default textures).
  */
 static void
 unbind_texobj_from_texunits(GLcontext *ctx, struct gl_texture_object *texObj)
@@ -623,42 +720,26 @@ unbind_texobj_from_texunits(GLcontext *ctx, struct gl_texture_object *texObj)
 
    for (u = 0; u < MAX_TEXTURE_IMAGE_UNITS; u++) {
       struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
-      struct gl_texture_object **curr = NULL;
-
       if (texObj == unit->Current1D) {
-         curr = &unit->Current1D;
-         unit->Current1D = ctx->Shared->Default1D;
+         _mesa_reference_texobj(&unit->Current1D, ctx->Shared->Default1D);
       }
       else if (texObj == unit->Current2D) {
-         curr = &unit->Current2D;
-         unit->Current2D = ctx->Shared->Default2D;
+         _mesa_reference_texobj(&unit->Current2D, ctx->Shared->Default2D);
       }
       else if (texObj == unit->Current3D) {
-         curr = &unit->Current3D;
-         unit->Current3D = ctx->Shared->Default3D;
+         _mesa_reference_texobj(&unit->Current3D, ctx->Shared->Default3D);
       }
       else if (texObj == unit->CurrentCubeMap) {
-         curr = &unit->CurrentCubeMap;
-         unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap;
+         _mesa_reference_texobj(&unit->CurrentCubeMap, ctx->Shared->DefaultCubeMap);
       }
       else if (texObj == unit->CurrentRect) {
-         curr = &unit->CurrentRect;
-         unit->CurrentRect = ctx->Shared->DefaultRect;
+         _mesa_reference_texobj(&unit->CurrentRect, ctx->Shared->DefaultRect);
       }
       else if (texObj == unit->Current1DArray) {
-         curr = &unit->Current1DArray;
-         unit->CurrentRect = ctx->Shared->Default1DArray;
+         _mesa_reference_texobj(&unit->Current1DArray, ctx->Shared->Default1DArray);
       }
       else if (texObj == unit->Current2DArray) {
-         curr = &unit->Current1DArray;
-         unit->CurrentRect = ctx->Shared->Default2DArray;
-      }
-
-      if (curr) {
-         (*curr)->RefCount++;
-         texObj->RefCount--;
-         if (texObj == unit->_Current)
-            unit->_Current = *curr;
+         _mesa_reference_texobj(&unit->Current2DArray, ctx->Shared->Default2DArray);
       }
    }
 }
@@ -694,8 +775,6 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
             = _mesa_lookup_texture(ctx, textures[i]);
 
          if (delObj) {
-           GLboolean deleted;
-
            _mesa_lock_texture(ctx, delObj);
 
             /* Check if texture is bound to any framebuffer objects.
@@ -705,10 +784,12 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
             unbind_texobj_from_fbo(ctx, delObj);
 
             /* Check if this texture is currently bound to any texture units.
-             * If so, unbind it and decrement the reference count.
+             * If so, unbind it.
              */
             unbind_texobj_from_texunits(ctx, delObj);
 
+           _mesa_unlock_texture(ctx, delObj);
+
             ctx->NewState |= _NEW_TEXTURE;
 
             /* The texture _name_ is now free for re-use.
@@ -718,23 +799,10 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
             _mesa_HashRemove(ctx->Shared->TexObjects, delObj->Name);
             _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
 
-            /* The actual texture object will not be freed until it's no
-             * longer bound in any context.
-             * XXX all RefCount accesses should be protected by a mutex.
+            /* Unreference the texobj.  If refcount hits zero, the texture
+             * will be deleted.
              */
-            delObj->RefCount--;
-           deleted = (delObj->RefCount == 0);
-           _mesa_unlock_texture(ctx, delObj);
-
-           /* We know that refcount went to zero above, so this is
-            * the only pointer left to delObj, so we don't have to
-            * worry about locking any more:
-            */
-            if (deleted) {
-               ASSERT(delObj->Name != 0); /* Never delete default tex objs */
-               ASSERT(ctx->Driver.DeleteTexture);
-               (*ctx->Driver.DeleteTexture)(ctx, delObj);
-            }
+            _mesa_reference_texobj(&delObj, NULL);
          }
       }
    }
@@ -762,7 +830,6 @@ _mesa_BindTexture( GLenum target, GLuint texName )
    GET_CURRENT_CONTEXT(ctx);
    const GLuint unit = ctx->Texture.CurrentUnit;
    struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
-   struct gl_texture_object *oldTexObj;
    struct gl_texture_object *newTexObj = NULL;
    ASSERT_OUTSIDE_BEGIN_END(ctx);
 
@@ -770,62 +837,6 @@ _mesa_BindTexture( GLenum target, GLuint texName )
       _mesa_debug(ctx, "glBindTexture %s %d\n",
                   _mesa_lookup_enum_by_nr(target), (GLint) texName);
 
-   /*
-    * Get pointer to currently bound texture object (oldTexObj)
-    */
-   switch (target) {
-      case GL_TEXTURE_1D:
-         oldTexObj = texUnit->Current1D;
-         break;
-      case GL_TEXTURE_2D:
-         oldTexObj = texUnit->Current2D;
-         break;
-      case GL_TEXTURE_3D:
-         oldTexObj = texUnit->Current3D;
-         break;
-      case GL_TEXTURE_CUBE_MAP_ARB:
-         if (!ctx->Extensions.ARB_texture_cube_map) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
-            return;
-         }
-         oldTexObj = texUnit->CurrentCubeMap;
-         break;
-      case GL_TEXTURE_RECTANGLE_NV:
-         if (!ctx->Extensions.NV_texture_rectangle) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
-            return;
-         }
-         oldTexObj = texUnit->CurrentRect;
-         break;
-      case GL_TEXTURE_1D_ARRAY_EXT:
-         if (!ctx->Extensions.MESA_texture_array) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
-            return;
-         }
-         oldTexObj = texUnit->Current1DArray;
-         break;
-      case GL_TEXTURE_2D_ARRAY_EXT:
-         if (!ctx->Extensions.MESA_texture_array) {
-            _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
-            return;
-         }
-         oldTexObj = texUnit->Current2DArray;
-         break;
-      default:
-         _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
-         return;
-   }
-
-   if (oldTexObj->Name == texName) {
-      /* XXX this might be wrong.  If the texobj is in use by another
-       * context and a texobj parameter was changed, this might be our
-       * only chance to update this context's hardware state.
-       * Note that some applications re-bind the same texture a lot so we
-       * want to handle that case quickly.
-       */
-      return;   /* rebinding the same texture- no change */
-   }
-
    /*
     * Get pointer to new texture object (newTexObj)
     */
@@ -854,7 +865,8 @@ _mesa_BindTexture( GLenum target, GLuint texName )
             newTexObj = ctx->Shared->Default2DArray;
             break;
          default:
-            ; /* Bad targets are caught above */
+            _mesa_error(ctx, GL_INVALID_ENUM, "glBindTexture(target)");
+            return;
       }
    }
    else {
@@ -900,28 +912,30 @@ _mesa_BindTexture( GLenum target, GLuint texName )
       newTexObj->Target = target;
    }
 
-   /* XXX all RefCount accesses should be protected by a mutex. */
-   newTexObj->RefCount++;
+   assert(valid_texture_object(newTexObj));
 
-   /* do the actual binding, but first flush outstanding vertices:
-    */
+   /* flush before changing binding */
    FLUSH_VERTICES(ctx, _NEW_TEXTURE);
 
+   /* Do the actual binding.  The refcount on the previously bound
+    * texture object will be decremented.  It'll be deleted if the
+    * count hits zero.
+    */
    switch (target) {
       case GL_TEXTURE_1D:
-         texUnit->Current1D = newTexObj;
+         _mesa_reference_texobj(&texUnit->Current1D, newTexObj);
          break;
       case GL_TEXTURE_2D:
-         texUnit->Current2D = newTexObj;
+         _mesa_reference_texobj(&texUnit->Current2D, newTexObj);
          break;
       case GL_TEXTURE_3D:
-         texUnit->Current3D = newTexObj;
+         _mesa_reference_texobj(&texUnit->Current3D, newTexObj);
          break;
       case GL_TEXTURE_CUBE_MAP_ARB:
-         texUnit->CurrentCubeMap = newTexObj;
+         _mesa_reference_texobj(&texUnit->CurrentCubeMap, newTexObj);
          break;
       case GL_TEXTURE_RECTANGLE_NV:
-         texUnit->CurrentRect = newTexObj;
+         _mesa_reference_texobj(&texUnit->CurrentRect, newTexObj);
          break;
       case GL_TEXTURE_1D_ARRAY_EXT:
          texUnit->Current1DArray = newTexObj;
@@ -930,6 +944,7 @@ _mesa_BindTexture( GLenum target, GLuint texName )
          texUnit->Current2DArray = newTexObj;
          break;
       default:
+         /* Bad target should be caught above */
          _mesa_problem(ctx, "bad target in BindTexture");
          return;
    }
@@ -937,18 +952,6 @@ _mesa_BindTexture( GLenum target, GLuint texName )
    /* Pass BindTexture call to device driver */
    if (ctx->Driver.BindTexture)
       (*ctx->Driver.BindTexture)( ctx, target, newTexObj );
-
-   /* Decrement the reference count on the old texture and check if it's
-    * time to delete it.
-    */
-   /* XXX all RefCount accesses should be protected by a mutex. */
-   oldTexObj->RefCount--;
-   ASSERT(oldTexObj->RefCount >= 0);
-   if (oldTexObj->RefCount == 0) {
-      ASSERT(oldTexObj->Name != 0);
-      ASSERT(ctx->Driver.DeleteTexture);
-      (*ctx->Driver.DeleteTexture)( ctx, oldTexObj );
-   }
 }
 
 
index 2a2bde36017577e81465660e123e7905b2126c06..d5374c5d6c4fd1034bb97eb5b66e07753101f5b0 100644 (file)
@@ -57,6 +57,10 @@ extern void
 _mesa_copy_texture_object( struct gl_texture_object *dest,
                            const struct gl_texture_object *src );
 
+extern void
+_mesa_reference_texobj(struct gl_texture_object **ptr,
+                       struct gl_texture_object *tex);
+
 extern void
 _mesa_test_texobj_completeness( const GLcontext *ctx,
                                 struct gl_texture_object *obj );
index 421f9128493e2f095d54c330a8fe28a130fc6425..3bdb55257f81f1c0fd2df16bba813473e9f408a9 100644 (file)
@@ -62,31 +62,6 @@ static const struct gl_tex_env_combine_state default_combine_state = {
 };
 
 
-/**
- * Copy a texture binding.  Helper used by _mesa_copy_texture_state().
- */
-static void
-copy_texture_binding(const GLcontext *ctx,
-                     struct gl_texture_object **dst,
-                     struct gl_texture_object *src)
-{
-   /* only copy if names differ (per OpenGL SI) */
-   if ((*dst)->Name != src->Name) {
-      /* unbind/delete dest binding which we're changing */
-      (*dst)->RefCount--;
-      if ((*dst)->RefCount == 0) {
-         /* time to delete this texture object */
-         ASSERT((*dst)->Name != 0);
-         ASSERT(ctx->Driver.DeleteTexture);
-         /* XXX cast-away const, unfortunately */
-         (*ctx->Driver.DeleteTexture)((GLcontext *) ctx, *dst);
-      }
-      /* make new binding, incrementing ref count */
-      *dst = src;
-      src->RefCount++;
-   }
-}
-
 
 /**
  * Used by glXCopyContext to copy texture state from one context to another.
@@ -143,20 +118,20 @@ _mesa_copy_texture_state( const GLcontext *src, GLcontext *dst )
       /* copy texture object bindings, not contents of texture objects */
       _mesa_lock_context_textures(dst);
 
-      copy_texture_binding(src, &dst->Texture.Unit[i].Current1D,
-                           src->Texture.Unit[i].Current1D);
-      copy_texture_binding(src, &dst->Texture.Unit[i].Current2D,
-                           src->Texture.Unit[i].Current2D);
-      copy_texture_binding(src, &dst->Texture.Unit[i].Current3D,
-                           src->Texture.Unit[i].Current3D);
-      copy_texture_binding(src, &dst->Texture.Unit[i].CurrentCubeMap,
-                           src->Texture.Unit[i].CurrentCubeMap);
-      copy_texture_binding(src, &dst->Texture.Unit[i].CurrentRect,
-                           src->Texture.Unit[i].CurrentRect);
-      copy_texture_binding(src, &dst->Texture.Unit[i].Current1DArray,
-                           src->Texture.Unit[i].Current1DArray);
-      copy_texture_binding(src, &dst->Texture.Unit[i].Current2DArray,
-                           src->Texture.Unit[i].Current2DArray);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].Current1D,
+                              src->Texture.Unit[i].Current1D);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].Current2D,
+                              src->Texture.Unit[i].Current2D);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].Current3D,
+                              src->Texture.Unit[i].Current3D);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].CurrentCubeMap,
+                              src->Texture.Unit[i].CurrentCubeMap);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].CurrentRect,
+                              src->Texture.Unit[i].CurrentRect);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].Current1DArray,
+                              src->Texture.Unit[i].Current1DArray);
+       _mesa_reference_texobj(&dst->Texture.Unit[i].Current2DArray,
+                              src->Texture.Unit[i].Current2DArray);
 
       _mesa_unlock_context_textures(dst);
    }
@@ -727,6 +702,8 @@ alloc_proxy_textures( GLcontext *ctx )
    if (!ctx->Texture.Proxy2DArray)
       goto cleanup;
 
+   assert(ctx->Texture.Proxy1D->RefCount == 1);
+
    return GL_TRUE;
 
  cleanup:
@@ -786,13 +763,14 @@ init_texture_unit( GLcontext *ctx, GLuint unit )
    ASSIGN_4V( texUnit->EyePlaneR, 0.0, 0.0, 0.0, 0.0 );
    ASSIGN_4V( texUnit->EyePlaneQ, 0.0, 0.0, 0.0, 0.0 );
 
-   texUnit->Current1D = ctx->Shared->Default1D;
-   texUnit->Current2D = ctx->Shared->Default2D;
-   texUnit->Current3D = ctx->Shared->Default3D;
-   texUnit->CurrentCubeMap = ctx->Shared->DefaultCubeMap;
-   texUnit->CurrentRect = ctx->Shared->DefaultRect;
-   texUnit->Current1DArray = ctx->Shared->Default1DArray;
-   texUnit->Current2DArray = ctx->Shared->Default2DArray;
+   /* initialize current texture object ptrs to the shared default objects */
+   _mesa_reference_texobj(&texUnit->Current1D, ctx->Shared->Default1D);
+   _mesa_reference_texobj(&texUnit->Current2D, ctx->Shared->Default2D);
+   _mesa_reference_texobj(&texUnit->Current3D, ctx->Shared->Default3D);
+   _mesa_reference_texobj(&texUnit->CurrentCubeMap, ctx->Shared->DefaultCubeMap);
+   _mesa_reference_texobj(&texUnit->CurrentRect, ctx->Shared->DefaultRect);
+   _mesa_reference_texobj(&texUnit->Current1DArray, ctx->Shared->Default1DArray);
+   _mesa_reference_texobj(&texUnit->Current2DArray, ctx->Shared->Default2DArray);
 }
 
 
@@ -807,25 +785,22 @@ _mesa_init_texture(GLcontext *ctx)
    assert(MAX_TEXTURE_LEVELS >= MAX_3D_TEXTURE_LEVELS);
    assert(MAX_TEXTURE_LEVELS >= MAX_CUBE_TEXTURE_LEVELS);
 
-   /* Effectively bind the default textures to all texture units */
-   ctx->Shared->Default1D->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->Default2D->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->Default3D->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->DefaultCubeMap->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->DefaultRect->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->Default1DArray->RefCount += MAX_TEXTURE_UNITS;
-   ctx->Shared->Default2DArray->RefCount += MAX_TEXTURE_UNITS;
-
    /* Texture group */
    ctx->Texture.CurrentUnit = 0;      /* multitexture */
    ctx->Texture._EnabledUnits = 0;
-   for (i=0; i<MAX_TEXTURE_UNITS; i++)
-      init_texture_unit( ctx, i );
    ctx->Texture.SharedPalette = GL_FALSE;
 #if FEATURE_colortable
    _mesa_init_colortable(&ctx->Texture.Palette);
 #endif
 
+   for (i = 0; i < MAX_TEXTURE_UNITS; i++)
+      init_texture_unit( ctx, i );
+
+   /* After we're done initializing the context's texture state the default
+    * texture objects' refcounts should be at least MAX_TEXTURE_UNITS + 1.
+    */
+   assert(ctx->Shared->Default1D->RefCount >= MAX_TEXTURE_UNITS + 1);
+
    /* Allocate proxy textures */
    if (!alloc_proxy_textures( ctx ))
       return GL_FALSE;
@@ -840,6 +815,20 @@ _mesa_init_texture(GLcontext *ctx)
 void
 _mesa_free_texture_data(GLcontext *ctx)
 {
+   GLuint u;
+
+   /* unreference current textures */
+   for (u = 0; u < MAX_TEXTURE_IMAGE_UNITS; u++) {
+      struct gl_texture_unit *unit = ctx->Texture.Unit + u;
+      _mesa_reference_texobj(&unit->Current1D, NULL);
+      _mesa_reference_texobj(&unit->Current2D, NULL);
+      _mesa_reference_texobj(&unit->Current3D, NULL);
+      _mesa_reference_texobj(&unit->CurrentCubeMap, NULL);
+      _mesa_reference_texobj(&unit->CurrentRect, NULL);
+      _mesa_reference_texobj(&unit->Current1DArray, NULL);
+      _mesa_reference_texobj(&unit->Current2DArray, NULL);
+   }
+
    /* Free proxy texture objects */
    (ctx->Driver.DeleteTexture)(ctx,  ctx->Texture.Proxy1D );
    (ctx->Driver.DeleteTexture)(ctx,  ctx->Texture.Proxy2D );
@@ -849,6 +838,7 @@ _mesa_free_texture_data(GLcontext *ctx)
    (ctx->Driver.DeleteTexture)(ctx,  ctx->Texture.Proxy1DArray );
    (ctx->Driver.DeleteTexture)(ctx,  ctx->Texture.Proxy2DArray );
 
+
 #if FEATURE_colortable
    {
       GLuint i;